2 -- ion/ioncore/ioncore_menudb.lua -- Routines for defining menus.
4 -- Copyright (c) Tuomo Valkonen 2004-2009.
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
, ...)
49 if a2
and type(a1
)=="string" then
51 tab
.label
=ioncore
.gettext(a1
)
55 ioncore
.defmenu("ctxmenu-"..ctx
, tab
)
59 -- Returns a context menu defined with \fnref{ioncore.defctxmenu}.
60 function ioncore
.getctxmenu(name
)
61 return menus
["ctxmenu-"..name
]
64 function ioncore
.evalmenu(menu
, ...)
65 if type(menu
)=="string" then
66 return ioncore
.evalmenu(menus
[menu
], ...)
67 elseif type(menu
)=="function" then
69 elseif type(menu
)=="table" then
75 -- Use this function to define normal menu entries. The string \var{name}
76 -- is the string shown in the visual representation of menu. The
77 -- parameter \var{cmd} and \var{guard_or_opts} (when string) are similar
78 -- to those of \fnref{ioncore.defbindings}. If \var{guard_or_opts} is
79 -- a table, it may contains the \var{guard} field, and the \var{priority}
80 -- field, for controlling positioning of entries in context menus.
81 -- (The default priority is 1 for most entries, and -1 for auto-generated
83 function ioncore
.menuentry(name
, cmd
, guard_or_opts
)
87 if type(guard_or_opts
)=="string" then
89 elseif type(guard_or_opts
)=="table" then
94 local fn
, gfn
=ioncore
.compile_cmd(cmd
, guard
)
97 name
=ioncore
.gettext(name
),
106 -- Use this function to define menu entries for submenus. The parameter
107 -- \fnref{sub_or_name} is either a table of menu entries or the name
108 -- of an already defined menu. The initial menu entry to highlight can be
109 -- specified by \var{options.initial} as either an integer starting from 1,
110 -- or a function that returns such a number. Another option supported is
111 -- \var{options.noautoexpand} that will cause \fnref{mod_query.query_menu}
112 -- to not automatically expand this submenu.
113 function ioncore
.submenu(name
, sub_or_name
, options
)
114 return table.append({
115 name
=ioncore
.gettext(name
),
116 submenu_fn
=function()
117 return ioncore
.evalmenu (sub_or_name
)
126 -- Workspace and window lists {{{
128 local function addto(list
)
129 return function(tgt
, attr
)
130 local e
=menuentry(tgt
:name(), function() tgt
:display() end)
132 table.insert(list
, e
)
137 local function sort(entries
)
138 table.sort(entries
, function(a
, b
) return a
.name
< b
.name
end)
142 function menus
.windowlist()
144 ioncore
.clientwin_i(addto(entries
))
148 function menus
.workspacelist()
150 local iter_
=addto(entries
)
152 local function iter(obj
)
153 return (not obj_is(obj
, "WGroupWS")
157 ioncore
.region_i(iter
)
162 local function focuslist(do_act
)
165 local iter_
=addto(entries
)
167 local function iter(obj
, attr
)
168 if obj_is(obj
, "WClientWin") then
175 local function iter_act(obj
)
176 return iter(obj
, "activity")
179 local function iter_foc(obj
)
180 return (seen
[obj
] or iter(obj
))
184 -- Windows with activity first
185 ioncore
.activity_i(iter_act
)
188 -- The ones that have been focused in their lifetime
189 ioncore
.focushistory_i(iter_foc
)
192 ioncore
.clientwin_i(iter_foc
)
197 menus
.focuslist
=function() return focuslist(true) end
198 menus
.focuslist_
=function() return focuslist(false) end
206 local function mplex_of(reg
)
207 while reg
and not obj_is(reg
, "WMPlex") do
213 local function selectstyle(look
, where
)
216 local fname
=ioncore
.get_savefile('look')
218 local function writeit()
219 local f
, err
=io
.open(fname
, 'w')
221 mod_query
.message(where
, err
)
223 f
:write(string.format('dopath("%s")\n', look
))
228 if not mod_query
then
235 where
=mplex_of(where
)
241 query_message(where
, TR("Cannot save selection."))
245 mod_query
.query_yesno(where
, TR("Save look selection in %s?", fname
),
249 local function receive_styles(str
)
254 if string.len(data
)>ioncore
.RESULT_DATA_LIMIT
then
255 error(TR("Too much result data"))
257 str
=coroutine
.yield()
264 for look
in string.gmatch(data
, "(look[-_][^\n]*)%.lua\n") do
265 if not found
[look
] then
267 table.insert(styles
, look
)
273 for _
, look
in ipairs(styles
) do
275 table.insert(stylemenu
, menuentry(look
,
277 selectstyle(look_
, where
)
281 table.insert(stylemenu
, menuentry(TR("Refresh list"),
282 ioncore
.refresh_stylelist
))
284 menus
.stylemenu
=stylemenu
289 -- Refresh list of known style files.
290 function ioncore
.refresh_stylelist()
291 local cmd
=ioncore
.lookup_script("ion-completefile")
293 local path
=ioncore
.get_paths().searchpath
294 local function mkarg(s
)
298 return (" "..string.shell_safe(s
).."/look_"..
299 " "..string.shell_safe(s
).."/look-")
304 cmd
=cmd
..string.gsub(path
..":", "([^:]*):", mkarg
)
306 ioncore
.popen_bgread(cmd
, coroutine
.wrap(receive_styles
))
316 local function classes(reg
)
317 local function classes_(t
)
318 if t
.__parentclass
then
319 classes_(t
.__parentclass
)
321 coroutine
.yield(t
.__typename
)
323 return coroutine
.wrap(function() classes_(reg
) end)
327 local function modeparts(mode
)
329 return function() return end
332 local f
, s
, v
=string.gmatch(mode
, "(%-?[^-]+)");
334 local function nxt(_
, m
)
336 return (v
and (m
.. v
))
343 local function get_ctxmenu(reg
, sub
)
346 local function cp(m2
)
348 for k
, v
in ipairs(m2
) do
349 local v2
=table.copy(v
)
353 v2
.func
=function() return ofunc(reg
, sub
) end
356 if v2
.submenu_fn
then
357 local ofn
=v2
.submenu_fn
358 v2
.submenu_fn
=function() return cp(ofn()) end
367 local function add_ctxmenu(m2
, use_label
)
369 m
=table.icat(m
, cp(m2
))
370 m
.label
=(use_label
and m2
.label
) or m
.label
374 local mgr
=reg
:manager()
375 local mgrname
=(mgr
and mgr
:name()) or nil
376 local mode
=(reg
.mode
and reg
:mode())
378 for s
in classes(reg
) do
379 local nm
="ctxmenu-"..s
380 add_ctxmenu(ioncore
.evalmenu(nm
), true)
381 for m
in modeparts(mode
) do
382 add_ctxmenu(ioncore
.evalmenu(nm
.."."..m
), false)
385 add_ctxmenu(ioncore
.evalmenu(nm
.."@"..mgrname
), false)
392 local function sortmenu(m
)
395 for _
, e
in ipairs(m
) do
396 e
.priority
=(e
.priority
or 1)+v
400 table.sort(m
, function(e1
, e2
) return e1
.priority
> e2
.priority
end)
406 function menus
.ctxmenu(reg
, sub
)
409 if obj_is(sub
, "WGroup") then
410 sub
=(sub
:bottom() or sub
)
413 -- First, stuff between reg (inclusive) and sub_or_chld (inclusive)
414 -- at the top level in the menu.
416 while r
and s
~=reg
do
417 local mm
=get_ctxmenu(r
, s
)
418 m
=((m
and table.icat(mm
, m
)) or mm
)
425 -- Then stuff below reg (exclusive) as submenus
427 local mm
= get_ctxmenu(r
, s
)
429 local nm
=mm
.label
or obj_typename(reg
)
430 local tmp
=ioncore
.submenu(nm
, sortmenu(mm
), {priority
=-1})
442 ioncore
.refresh_stylelist()