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