2 -- ion/ioncore/ioncore_menudb.lua -- Routines for defining menus.
4 -- Copyright (c) Tuomo Valkonen 2004-2007.
6 -- See the included file LICENSE for details.
9 local ioncore=_G.ioncore
12 -- Table to hold defined menus.
16 -- Menu construction {{{
19 -- Define a new menu with \var{name} being the menu's name and \var{tab}
20 -- being a table of menu entries. If \var{tab.append} is set, the entries
21 -- are appended to previously-defined ones, if possible.
22 function ioncore.defmenu(name, tab)
23 if menus[name] and type(tab)=="table" and tab.append then
24 if type(menus[name])~="table" then
25 ioncore.warn(TR("Unable to append to non-table menu"))
28 for k, v in ipairs(tab) do
29 table.insert(menus[name], v)
38 -- Returns a menu defined with \fnref{ioncore.defmenu}.
39 function ioncore.getmenu(name)
44 -- Define context menu for context \var{ctx}, \var{tab} being a table
46 function ioncore.defctxmenu(ctx, ...)
48 if #arg>1 and type(arg[1])=="string" then
50 tab.label=ioncore.gettext(arg[1])
54 ioncore.defmenu("ctxmenu-"..ctx, tab)
58 -- Returns a context menu defined with \fnref{ioncore.defctxmenu}.
59 function ioncore.getctxmenu(name)
60 return menus["ctxmenu-"..name]
63 function ioncore.evalmenu(menu, ...)
64 if type(menu)=="string" then
65 return ioncore.evalmenu(menus[menu], ...)
66 elseif type(menu)=="function" then
68 elseif type(menu)=="table" then
74 -- Use this function to define normal menu entries. The string \var{name}
75 -- is the string shown in the visual representation of menu. The
76 -- parameter \var{cmd} and \var{guard_or_opts} (when string) are similar
77 -- to those of \fnref{ioncore.defbindings}. If \var{guard_or_opts} is
78 -- a table, it may contains the \var{guard} field, and the \var{priority}
79 -- field, for controlling positioning of entries in context menus.
80 -- (The default priority is 1 for most entries, and -1 for auto-generated
82 function ioncore.menuentry(name, cmd, guard_or_opts)
86 if type(guard_or_opts)=="string" then
88 elseif type(guard_or_opts)=="table" then
93 local fn, gfn=ioncore.compile_cmd(cmd, guard)
96 name=ioncore.gettext(name),
105 -- Use this function to define menu entries for submenus. The parameter
106 -- \fnref{sub_or_name} is either a table of menu entries or the name
107 -- of an already defined menu. The initial menu entry to highlight can be
108 -- specified by \var{options.initial} as either an integer starting from 1,
109 -- or a function that returns such a number. Another option supported is
110 -- \var{options.noautoexpand} that will cause \fnref{mod_query.query_menu}
111 -- to not automatically expand this submenu.
112 function ioncore.submenu(name, sub_or_name, options)
113 return table.append({
114 name=ioncore.gettext(name),
115 submenu_fn=function()
116 return ioncore.evalmenu (sub_or_name)
125 -- Workspace and window lists {{{
127 local function addto(list)
128 return function(tgt, attr)
129 local e=menuentry(tgt:name(), function() tgt:goto() end)
131 table.insert(list, e)
136 local function sort(entries)
137 table.sort(entries, function(a, b) return a.name < b.name end)
141 function menus.windowlist()
143 ioncore.clientwin_i(addto(entries))
147 function menus.workspacelist()
149 local iter_=addto(entries)
151 local function iter(obj)
152 return (not obj_is(obj, "WGroupWS")
156 ioncore.region_i(iter)
161 local function focuslist(do_act)
164 local iter_=addto(entries)
166 local function iter(obj, attr)
167 if obj_is(obj, "WClientWin") then
174 local function iter_act(obj)
175 return iter(obj, "activity")
178 local function iter_foc(obj)
179 return (seen[obj] or iter(obj))
183 -- Windows with activity first
184 ioncore.activity_i(iter_act)
187 -- The ones that have been focused in their lifetime
188 ioncore.focushistory_i(iter_foc)
191 ioncore.clientwin_i(iter_foc)
196 menus.focuslist=function() return focuslist(true) end
197 menus.focuslist_=function() return focuslist(false) end
205 local function mplex_of(reg)
206 while reg and not obj_is(reg, "WMPlex") do
212 local function selectstyle(look, where)
215 local fname=ioncore.get_savefile('look')
217 local function writeit()
218 local f, err=io.open(fname, 'w')
220 mod_query.message(where, err)
222 f:write(string.format('dopath("%s")\n', look))
227 if not mod_query then
234 where=mplex_of(where)
240 query_message(where, TR("Cannot save selection."))
244 mod_query.query_yesno(where, TR("Save look selection in %s?", fname),
248 local function receive_styles(str)
253 if string.len(data)>ioncore.RESULT_DATA_LIMIT then
254 error(TR("Too much result data"))
256 str=coroutine.yield()
263 for look in string.gfind(data, "(look[-_][^\n]*)%.lua\n") do
264 if not found[look] then
266 table.insert(styles, look)
272 for _, look in ipairs(styles) do
274 table.insert(stylemenu, menuentry(look,
276 selectstyle(look_, where)
280 table.insert(stylemenu, menuentry(TR("Refresh list"),
281 ioncore.refresh_stylelist))
283 menus.stylemenu=stylemenu
288 -- Refresh list of known style files.
289 function ioncore.refresh_stylelist()
290 local cmd=ioncore.lookup_script("ion-completefile")
292 local path=ioncore.get_paths().searchpath
293 local function mkarg(s)
297 return (" "..string.shell_safe(s).."/look_"..
298 " "..string.shell_safe(s).."/look-")
303 cmd=cmd..string.gsub(path..":", "([^:]*):", mkarg)
305 ioncore.popen_bgread(cmd, coroutine.wrap(receive_styles))
315 local function classes(reg)
316 local function classes_(t)
317 if t.__parentclass then
318 classes_(t.__parentclass)
320 coroutine.yield(t.__typename)
322 return coroutine.wrap(function() classes_(reg) end)
326 local function modeparts(mode)
328 return function() return end
331 local f, s, v=string.gmatch(mode, "(%-?[^-]+)");
333 local function nxt(_, m)
335 return (v and (m .. v))
342 local function get_ctxmenu(reg, sub)
345 local function cp(m2)
347 for k, v in ipairs(m2) do
348 local v2=table.copy(v)
352 v2.func=function() return ofunc(reg, sub) end
355 if v2.submenu_fn then
356 local ofn=v2.submenu_fn
357 v2.submenu_fn=function() return cp(ofn()) end
366 local function add_ctxmenu(m2, use_label)
368 m=table.icat(m, cp(m2))
369 m.label=(use_label and m2.label) or m.label
373 local mgr=reg:manager()
374 local mgrname=(mgr and mgr:name()) or nil
375 local mode=(reg.mode and reg:mode())
377 for s in classes(reg) do
378 local nm="ctxmenu-"..s
379 add_ctxmenu(ioncore.evalmenu(nm), true)
380 for m in modeparts(mode) do
381 add_ctxmenu(ioncore.evalmenu(nm.."."..m), false)
384 add_ctxmenu(ioncore.evalmenu(nm.."@"..mgrname), false)
391 local function sortmenu(m)
394 for _, e in ipairs(m) do
395 e.priority=(e.priority or 1)+v
399 table.sort(m, function(e1, e2) return e1.priority > e2.priority end)
405 function menus.ctxmenu(reg, sub)
408 if obj_is(sub, "WGroup") then
409 sub=(sub:bottom() or sub)
412 -- First, stuff between reg (inclusive) and sub_or_chld (inclusive)
413 -- at the top level in the menu.
415 while r and s~=reg do
416 local mm=get_ctxmenu(r, s)
417 m=((m and table.icat(mm, m)) or mm)
424 -- Then stuff below reg (exclusive) as submenus
426 local mm = get_ctxmenu(r, s)
428 local nm=mm.label or obj_typename(reg)
429 local tmp=ioncore.submenu(nm, sortmenu(mm), {priority=-1})
441 ioncore.refresh_stylelist()