]> git.decadent.org.uk Git - ion3.git/blob - ioncore/ioncore_menudb.lua
[svn-inject] Installing original source of ion3
[ion3.git] / ioncore / ioncore_menudb.lua
1 --
2 -- ion/ioncore/ioncore_menudb.lua -- Routines for defining menus.
3 -- 
4 -- Copyright (c) Tuomo Valkonen 2004-2006.
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
15 -- Table to hold defined menus.
16 local menus={}
17
18
19 -- Menu construction {{{
20
21 --DOC
22 -- Define a new menu with \var{name} being the menu's name and \var{tab} 
23 -- being a table of menu entries. If \var{tab.append} is set, the entries 
24 -- are appended to previously-defined ones, if possible.
25 function ioncore.defmenu(name, tab)
26     if menus[name] and type(tab)=="table" and tab.append then
27         if type(menus[name])~="table" then
28             ioncore.warn(TR("Unable to append to non-table menu"))
29             return
30         else
31             table.append(menus[name], tab)
32         end
33     else
34         menus[name]=tab
35     end
36 end
37
38 --DOC
39 -- Returns a menu defined with \fnref{ioncore.defmenu}.
40 function ioncore.getmenu(name)
41     return menus[name]
42 end
43
44 --DOC
45 -- Define context menu for context \var{ctx}, \var{tab} being a table 
46 -- of menu entries.
47 function ioncore.defctxmenu(ctx, ...)
48     local tab, add
49     if #arg>1 and type(arg[1])=="string" then
50         tab=arg[2]
51         tab.label=ioncore.gettext(arg[1])
52     else
53         tab=arg[1]
54     end
55     ioncore.defmenu("ctxmenu-"..ctx, tab)
56 end
57
58 --DOC
59 -- Returns a context menu defined with \fnref{ioncore.defctxmenu}.
60 function ioncore.getctxmenu(name)
61     return menus["ctxmenu-"..name]
62 end
63
64
65 function ioncore.evalmenu(menu, args)
66     if type(menu)=="string" then
67         return ioncore.evalmenu(menus[menu], args)
68     elseif type(menu)=="function" then
69         if args then
70             return menu(unpack(args))
71         else
72             return menu()
73         end
74     elseif type(menu)=="table" then
75         return menu
76     end
77 end
78
79
80 --DOC
81 -- Use this function to define normal menu entries. The string \var{name} 
82 -- is the string shown in the visual representation of menu, and the
83 -- parameter \var{cmd} and \var{guard} are similar to those of
84 -- \fnref{ioncore.defbindings}.
85 function ioncore.menuentry(name, cmd, guard)
86     local fn, gfn=ioncore.compile_cmd(cmd, guard)
87     if fn then
88         return {name=ioncore.gettext(name), func=fn, guard_func=gfn}
89     end
90 end
91
92 --DOC
93 -- Use this function to define menu entries for submenus. The parameter
94 -- \fnref{sub_or_name} is either a table of menu entries or the name
95 -- of an already defined menu. The initial menu entry to highlight can be
96 -- specified by \var{options.initial} as either an integer starting from 1, 
97 -- or a  function that returns such a number. Another option supported is
98 -- \var{options.noautoexpand} that will cause \fnref{mod_query.query_menu}
99 -- to not automatically expand this submenu.
100 function ioncore.submenu(name, sub_or_name, options)
101     if not options then
102         options={}
103     end
104     return {
105         name=ioncore.gettext(name),
106         submenu_fn=function()
107                        return ioncore.evalmenu(sub_or_name)
108                    end,
109         initial=options.initial,
110         noautoexpand=options.noautoexpand,
111     }
112 end
113
114
115 -- }}}
116
117
118 -- Workspace and window lists {{{
119
120 local function makelist(list)
121     local function mkentry(tgt)
122         return menuentry(tgt:name(), function() tgt:goto() end)
123     end
124     local entries=table.map(mkentry, list)
125     table.sort(entries, function(a, b) return a.name < b.name end)
126     return entries
127 end
128
129 function menus.windowlist()
130     return makelist(ioncore.clientwin_list())
131 end
132
133 function menus.workspacelist()
134     return makelist(ioncore.region_list("WGenWS"))
135 end
136
137 -- }}}
138
139
140 -- Style menu {{{
141
142
143 local function mplex_of(reg)
144     while reg and not obj_is(reg, "WMPlex") do
145         reg=reg:parent()
146     end
147     return reg
148 end
149
150 local function selectstyle(look, where)
151     dopath(look)
152
153     local fname=ioncore.get_savefile('look')
154
155     local function writeit()
156         local f, err=io.open(fname, 'w')
157         if not f then
158             mod_query.message(where, err)
159         else
160             f:write(string.format('dopath("%s")\n', look))
161             f:close()
162         end
163     end
164
165     if not mod_query then
166         if fname then
167             writeit()
168         end
169         return
170     end
171     
172     where=mplex_of(where)
173     if not where then 
174         return 
175     end
176     
177     if not fname then
178         query_message(where, TR("Cannot save selection."))
179         return
180     end
181     
182     mod_query.query_yesno(where, TR("Save look selection in %s?", fname),
183                           writeit)
184 end
185
186 local function receive_styles(str)
187     local data=""
188     
189     while str do
190         data=data .. str
191         if string.len(data)>ioncore.RESULT_DATA_LIMIT then
192             error(TR("Too much result data"))
193         end
194         str=coroutine.yield()
195     end
196     
197     local found={}
198     local styles={}
199     local stylemenu={}
200     
201     for look in string.gfind(data, "(look[-_][^\n]*)%.lua\n") do
202         if not found[look] then
203             found[look]=true
204             table.insert(styles, look)
205         end
206     end
207     
208     table.sort(styles)
209     
210     for _, look in ipairs(styles) do
211         local look_=look
212         table.insert(stylemenu, menuentry(look,  
213                                           function(where)
214                                               selectstyle(look_, where)
215                                           end))
216     end
217     
218     table.insert(stylemenu, menuentry(TR("Refresh list"),
219                                       ioncore.refresh_stylelist))
220     
221     menus.stylemenu=stylemenu
222 end
223
224
225 --DOC
226 -- Refresh list of known style files.
227 function ioncore.refresh_stylelist()
228     local cmd=ioncore.lookup_script("ion-completefile")
229     if cmd then
230         local path=ioncore.get_paths().searchpath
231         local function mkarg(s)
232             if s=="" then
233                 return ""
234             else
235                 return (" "..string.shell_safe(s).."/look_"..
236                         " "..string.shell_safe(s).."/look-")
237             end
238             return ""
239         end
240
241         cmd=cmd..string.gsub(path..":", "([^:]*):", mkarg)
242         
243         ioncore.popen_bgread(cmd, coroutine.wrap(receive_styles))
244     end
245 end
246
247 -- }}}
248
249
250 -- Context menu {{{
251
252
253 local function classes(reg)
254     local function classes_(t)
255         if t.__parentclass then
256             classes_(t.__parentclass)
257         end
258         coroutine.yield(t.__typename)
259     end
260     return coroutine.wrap(function() classes_(reg) end)
261 end
262
263
264 local function modeparts(mode)
265     if not mode then
266         return function() return end
267     end
268     
269     local f, s, v=string.gmatch(mode, "(%-?[^-]+)");
270     
271     local function nxt(_, m)
272         v = f(s, v)
273         return (v and (m .. v))
274     end
275     
276     return nxt, nil, ""
277 end
278
279
280 local function get_ctxmenu(reg, sub, is_par)
281     local m={}
282     
283     local function cp(m2)
284         local m3={}
285         for k, v in ipairs(m2) do
286             local v2=table.copy(v)
287             
288             if v2.func then
289                 local ofunc=v2.func
290                 v2.func=function() return ofunc(reg, sub) end
291             end
292             
293             if v2.submenu_fn then
294                 local ofn=v2.submenu_fn
295                 v2.submenu_fn=function() return cp(ofn()) end
296             end
297             
298             m3[k]=v2
299         end
300         m3.label=m2.label
301         return m3
302     end
303     
304     local function add_ctxmenu(m2, use_label)
305         if m2 then
306             if is_par then
307                 m2=cp(m2)
308             end
309
310             m=table.icat(m, m2)
311             m.label=(use_label and m2.label) or m.label
312         end
313     end
314     
315     local mgr=reg:manager()
316     local mgrname=(mgr and mgr:name()) or nil
317     local mode=(reg.mode and reg:mode())
318
319     for s in classes(reg) do
320         local nm="ctxmenu-"..s
321         add_ctxmenu(ioncore.evalmenu(nm), true)
322         for m in modeparts(mode) do
323             add_ctxmenu(ioncore.evalmenu(nm.."."..m), false)
324         end
325         if mgrname then
326             add_ctxmenu(ioncore.evalmenu(nm.."@"..mgrname), false)
327         end
328     end
329     return m
330 end
331
332 function menus.ctxmenu(reg, sub)
333     local m=get_ctxmenu(reg, sub, false);
334     
335     sub=reg
336     reg=reg:manager()
337     
338     while reg do
339         local mm = get_ctxmenu(reg, sub, true)
340         if #mm>0 then
341             local nm=mm.label or obj_typename(reg)
342             table.insert(m, ioncore.submenu(nm, mm))
343         end
344         sub=reg
345         reg=reg:manager()
346     end
347     
348     return m
349 end
350
351 -- }}}
352
353 ioncore.refresh_stylelist()
354