]> git.decadent.org.uk Git - ion3.git/blob - ioncore/ioncore_bindings.lua
Imported Upstream version 20090110
[ion3.git] / ioncore / ioncore_bindings.lua
1 --
2 -- ion/share/ioncore-bindings.lua
3 -- 
4 -- Copyright (c) Tuomo Valkonen 2004-2009.
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 gerr
50         gfn, gerr=loadstring(gfncode, guardcode)
51         if not gfn then
52             ioncore.warn_traced(TR("Error compiling guard: %s", gerr))
53         end
54         gfn=gfn()
55     end
56
57     local function guarded(gfn, fn)
58         if not gfn then
59             return fn
60         else
61             return function(_, _sub, _chld) 
62                 if gfn(_, _sub, _chld) then 
63                     fn(_, _sub, _chld) 
64                 end
65             end
66         end
67     end
68     
69     if type(cmd)=="string" then
70         local fncode=("return function(_, _sub, _chld) local d = "
71                        ..cmd.." end")
72         local fn, err=loadstring(fncode, cmd)
73         if not fn then
74             ioncore.warn_traced(TR("Error in command string: ")..err)
75             return
76         end
77         compiled2str[fn]=cmd
78         return guarded(gfn, fn())
79     elseif type(cmd)=="function" then
80         return guarded(gfn, cmd)
81     end
82
83     ioncore.warn_traced(TR("Invalid command"))
84 end
85
86
87 local function putcmd(cmd, guard, tab)
88     local func
89     if cmd then
90         func=ioncore.compile_cmd(cmd, guard)
91         if type(func)~="function" then
92             return
93         end
94     end
95     
96     tab.func=func
97     tab.cmdstr=cmd
98     tab.guard=guard
99     
100     return tab
101 end
102
103 --DOC
104 -- Used to enter documentation among bindings so that other programs
105 -- can read it. Does nothing.
106 function ioncore.bdoc(text)
107     return {action = "doc", text = text}
108 end
109
110 --DOC
111 -- Returns a function that creates a submap binding description table.
112 -- When the key press action \var{keyspec} occurs, Ioncore will wait for
113 -- a further key presse and act according to the submap.
114 -- For details, see Section \ref{sec:bindings}.
115 function ioncore.submap(keyspec, list)
116     if not list then
117         return function(lst)
118                    return submap(keyspec, lst)
119                end
120     end
121     return {action = "kpress", kcb = keyspec, submap = list}
122 end
123
124 --DOC
125 -- Creates a binding description table for the action of pressing a key given 
126 -- by \var{keyspec} (with possible modifiers) to the function \var{cmd}.
127 -- The \var{guard} controls when the binding can be called.
128 -- For more informationp see Section \ref{sec:bindings}.
129 function ioncore.kpress(keyspec, cmd, guard)
130     return putcmd(cmd, guard, {action = "kpress", kcb = keyspec})
131 end
132
133 --DOC
134 -- This is similar to \fnref{ioncore.kpress} but after calling \var{cmd}, 
135 -- Ioncore waits for all modifiers to be released before processing
136 -- any further actions.
137 -- For more information on bindings, see Section \ref{sec:bindings}.
138 function ioncore.kpress_wait(keyspec, cmd, guard)
139     return putcmd(cmd, guard, {action = "kpress_wait", kcb = keyspec})
140 end
141
142 --DOC
143 -- Submap enter event for bindings.
144 function ioncore.submap_enter(cmd, guard)
145     return putcmd(cmd, guard, {action = "submap_enter"})
146 end
147
148 --DOC
149 -- Submap modifier release event for bindings.
150 function ioncore.submap_wait(cmd, guard)
151     return putcmd(cmd, guard, {action = "submap_wait"})
152 end
153
154 -- DOC
155 -- Submap leave event for bindings.
156 --function ioncore.submap_leave(cmd, guard)
157 --    return putcmd(cmd, guard, {action = "submap_leave"})
158 --end
159
160 local function mact(act_, kcb_, cmd, guard)
161     local st, en, kcb2_, area_=string.find(kcb_, "([^@]*)@(.*)")
162     return putcmd(cmd, guard, {
163         action = act_,
164         kcb = (kcb2_ or kcb_),
165         area = area_,
166     })
167 end
168
169 --DOC
170 -- Creates a binding description table for the action of clicking a mouse 
171 -- button while possible modifier keys are pressed,
172 -- both given by \var{buttonspec}, to the function \var{cmd}.
173 -- For more information, see Section \ref{sec:bindings}.
174 function ioncore.mclick(buttonspec, cmd, guard)
175     return mact("mclick", buttonspec, cmd, guard)
176 end
177
178 --DOC
179 -- Similar to \fnref{ioncore.mclick} but for double-click.
180 -- Also see Section \ref{sec:bindings}.
181 function ioncore.mdblclick(buttonspec, cmd, guard)
182     return mact("mdblclick", buttonspec, cmd, guard)
183 end
184
185 --DOC
186 -- Similar to \fnref{ioncore.mclick} but for just pressing the mouse button.
187 -- Also see Section \ref{sec:bindings}.
188 function ioncore.mpress(buttonspec, cmd, guard)
189     return mact("mpress", buttonspec, cmd, guard)
190 end
191
192 --DOC
193 -- Creates a binding description table for the action of moving the mouse
194 -- (or other pointing device) while the button given by \var{buttonspec}
195 -- is held pressed and the modifiers given by \var{buttonspec} were pressed
196 -- when the button was initially pressed.
197 -- Also see section \ref{sec:bindings}.
198 function ioncore.mdrag(buttonspec, cmd, guard)
199     return mact("mdrag", buttonspec, cmd, guard)
200 end
201
202 --DOC
203 -- Define bindings for context \var{context}. Here \var{binding} is
204 -- a table composed of entries created with \fnref{ioncore.kpress}, 
205 -- etc.; see Section \ref{sec:bindings} for details.
206 function ioncore.defbindings(context, bindings)
207     local function filterdoc(b)
208         local t={}
209         for k, v in ipairs(b) do
210             local v2=v
211             if v2.submap then
212                 v2=table.copy(v)
213                 v2.submap=filterdoc(v2.submap)
214             end
215             if v2.action~="doc" then
216                 table.insert(t, v2)
217             end
218         end
219         return t
220     end
221     return ioncore.do_defbindings(context, filterdoc(bindings))
222 end
223
224 local function bindings_get_cmds(map)
225     for k, v in pairs(map) do
226         if v.func then
227             v.cmd=compiled2str[v.func]
228         end
229         if v.submap then
230             bindings_get_cmds(v.submap)
231         end
232     end
233 end
234
235 --DOC
236 -- Get a table of all bindings.
237 function ioncore.getbindings(maybe_context)
238     local bindings=ioncore.do_getbindings()
239     if maybe_context then
240         bindings_get_cmds(bindings[maybe_context])
241         return bindings[maybe_context]
242     else
243         for k, v in pairs(bindings) do
244             bindings_get_cmds(v)
245         end
246         return bindings
247     end
248 end
249
250