]> git.decadent.org.uk Git - ion3.git/blob - ioncore/ioncore_bindings.lua
[svn-upgrade] Integrating new upstream version, ion3 (20070506)
[ion3.git] / ioncore / ioncore_bindings.lua
1 --
2 -- ion/share/ioncore-bindings.lua
3 -- 
4 -- Copyright (c) Tuomo Valkonen 2004-2007.
5 --
6 -- See the included file LICENSE for details.
7 --
8
9 local ioncore=_G.ioncore
10
11 local warn=ioncore.warn
12
13 local compiled2str=setmetatable({}, {__mode="k"})
14
15 --DOC
16 -- Compile string \var{cmd} into a bindable function. Within \var{cmd}, the
17 -- variable ''\var{_}'' (underscore) can be used to refer to the object 
18 -- that was selecting for the bound action and chosen to handle it.
19 -- The  variable ''\var{_sub}'' refers to a ''currently active'' sub-object 
20 -- of \var{_}, or a sub-object where the action loading to the binding 
21 -- being called actually occured.
22 -- 
23 -- The string \var{guard}  maybe set to pose limits on \code{_sub}. Currently 
24 -- supported guards are \code{_sub:non-nil} and \code{_sub:WFoobar}, where 
25 -- \type{WFoobar} is a class.
26 function ioncore.compile_cmd(cmd, guard)
27     local guardcode=""
28     local gfn=nil
29     
30     if guard then
31         local st, en, condition=string.find(guard, "^_sub:([%w-_]+)$")
32         local sub='_sub'
33         if not condition then
34             st, en, condition=string.find(guard, "^_chld:([%w-_]+)$")
35             if condition then
36                 sub='_chld'
37             end
38         end
39         
40         if not condition then
41             ioncore.warn_traced(TR("Invalid guard %s.", guard))
42         elseif condition=="non-nil" then
43             guardcode='if not '..sub..' then return end; '
44         else
45             guardcode='if not obj_is('..sub..', "'..condition..'") then return end; '
46         end
47         
48         local gfncode="return function(_, _sub, _chld) "..guardcode.." return true end"
49         local gfn, gerr=loadstring(gfncode, guardcode)
50         if not gfn then
51             ioncore.warn_traced(TR("Error compiling guard: %s", gerr))
52         end
53         gfn=gfn()
54     end
55
56     local function guarded(gfn, fn)
57         if not gfn then
58             return fn
59         else
60             return function(_, _sub, _chld) 
61                 if gfn(_, _sub, _chld) then 
62                     cmd(_, _sub, _chld) 
63                 end
64             end
65         end
66     end
67     
68     if type(cmd)=="string" then
69         local fncode=("return function(_, _sub, _chld) local d = "
70                        ..cmd.." end")
71         local fn, err=loadstring(fncode, cmd)
72         if not fn then
73             ioncore.warn_traced(TR("Error in command string: ")..err)
74             return
75         end
76         compiled2str[fn]=cmd
77         return guarded(gfn, fn())
78     elseif type(cmd)=="function" then
79         return guarded(gfn, cmd)
80     end
81
82     ioncore.warn_traced(TR("Invalid command"))
83 end
84
85
86 local function putcmd(cmd, guard, tab)
87     local func
88     if cmd then
89         func=ioncore.compile_cmd(cmd, guard)
90         if type(func)~="function" then
91             return
92         end
93     end
94     
95     tab.func=func
96     tab.cmdstr=cmd
97     tab.guard=guard
98     
99     return tab
100 end
101
102 --DOC
103 -- Used to enter documentation among bindings so that other programs
104 -- can read it. Does nothing.
105 function ioncore.bdoc(text)
106     return {action = "doc", text = text}
107 end
108
109 --DOC
110 -- Returns a function that creates a submap binding description table.
111 -- When the key press action \var{keyspec} occurs, Ioncore will wait for
112 -- a further key presse and act according to the submap.
113 -- For details, see section \ref{sec:bindings}.
114 function ioncore.submap(kcb_, list)
115     if not list then
116         return function(lst)
117                    return submap(kcb_, lst)
118                end
119     end
120     return {action = "kpress", kcb = kcb_, submap = list}
121 end
122
123 --DOC
124 -- Creates a binding description table for the action of pressing a key given 
125 -- by \var{keyspec} (with possible modifiers) to the function \var{func}.
126 -- For more information on bindings, see section \ref{sec:bindings}.
127 function ioncore.kpress(keyspec, cmd, guard)
128     return putcmd(cmd, guard, {action = "kpress", kcb = keyspec})
129 end
130
131 --DOC
132 -- This is similar to \fnref{kpress} but after calling \var{cmd}, 
133 -- Ioncore waits for all modifiers to be released before processing
134 -- any further actions.
135 -- For more information on bindings, see section \ref{sec:bindings}.
136 function ioncore.kpress_wait(keyspec, cmd, guard)
137     return putcmd(cmd, guard, {action = "kpress_wait", kcb = keyspec})
138 end
139
140 --DOC
141 -- Submap enter event for bindings.
142 function ioncore.submap_enter(cmd, guard)
143     return putcmd(cmd, guard, {action = "submap_enter"})
144 end
145
146 --DOC
147 -- Submap modifier release event for bindings.
148 function ioncore.submap_wait(cmd, guard)
149     return putcmd(cmd, guard, {action = "submap_wait"})
150 end
151
152 -- DOC
153 -- Submap leave event for bindings.
154 --function ioncore.submap_leave(cmd, guard)
155 --    return putcmd(cmd, guard, {action = "submap_leave"})
156 --end
157
158 local function mact(act_, kcb_, cmd, guard)
159     local st, en, kcb2_, area_=string.find(kcb_, "([^@]*)@(.*)")
160     return putcmd(cmd, guard, {
161         action = act_,
162         kcb = (kcb2_ or kcb_),
163         area = area_,
164     })
165 end
166
167 --DOC
168 -- Creates a binding description table for the action of clicking a mouse 
169 -- button while possible modifier keys are pressed,
170 -- both given by \var{buttonspec}, to the function \var{func}.
171 -- For more information, see section \ref{sec:bindings}.
172 function ioncore.mclick(buttonspec, cmd, guard)
173     return mact("mclick", buttonspec, cmd, guard)
174 end
175
176 --DOC
177 -- Similar to \fnref{mclick} but for double-click.
178 -- Also see section \ref{sec:bindings}.
179 function ioncore.mdblclick(buttonspec, cmd, guard)
180     return mact("mdblclick", buttonspec, cmd, guard)
181 end
182
183 --DOC
184 -- Similar to \fnref{mclick} but for just pressing the mouse button.
185 -- Also see section \ref{sec:bindings}.
186 function ioncore.mpress(buttonspec, cmd, guard)
187     return mact("mpress", buttonspec, cmd, guard)
188 end
189
190 --DOC
191 -- Creates a binding description table for the action of moving the mouse
192 -- (or other pointing device) while the button given by \var{buttonspec}
193 -- is held pressed and the modifiers given by \var{buttonspec} were pressed
194 -- when the button was initially pressed.
195 -- Also see section \ref{sec:bindings}.
196 function ioncore.mdrag(buttonspec, cmd, guard)
197     return mact("mdrag", buttonspec, cmd, guard)
198 end
199
200 --DOC
201 -- Define bindings for context \var{context}. Here \var{binding} is
202 -- a table composed of entries created with \fnref{ioncore.kpress}, 
203 -- etc.; see section \ref{sec:bindings} for details.
204 function ioncore.defbindings(context, bindings)
205     local function filterdoc(b)
206         local t={}
207         for k, v in ipairs(b) do
208             local v2=v
209             if v2.submap then
210                 v2=table.copy(v)
211                 v2.submap=filterdoc(v2.submap)
212             end
213             if v2.action~="doc" then
214                 table.insert(t, v2)
215             end
216         end
217         return t
218     end
219     return ioncore.do_defbindings(context, filterdoc(bindings))
220 end
221
222 local function bindings_get_cmds(map)
223     for k, v in pairs(map) do
224         if v.func then
225             v.cmd=compiled2str[v.func]
226         end
227         if v.submap then
228             bindings_get_cmds(v.submap)
229         end
230     end
231 end
232
233 --DOC
234 -- Get a table of all bindings.
235 function ioncore.getbindings(maybe_context)
236     local bindings=ioncore.do_getbindings()
237     if maybe_context then
238         bindings_get_cmds(bindings[maybe_context])
239         return bindings[maybe_context]
240     else
241         for k, v in pairs(bindings) do
242             bindings_get_cmds(v)
243         end
244         return bindings
245     end
246 end
247
248