1 ---------------------------------------------------------------------------
2 -- @author Damien Leone <damien.leone@gmail.com>
3 -- @author Julien Danjou <julien@danjou.info>
4 -- @copyright 2008 Damien Leone, Julien Danjou
5 -- @release @AWESOME_VERSION@
6 ---------------------------------------------------------------------------
8 -- Grab environment we need
15 local button
= require("awful.button")
21 keygrabber
= keygrabber
23 local util
= require("awful.util")
24 local tags
= require("awful.tag")
25 local awbeautiful
= require("beautiful")
26 local tonumber = tonumber
28 --- Menu module for awful
34 local function load_theme(custom
)
38 beautiful
= awbeautiful
.get()
40 theme
.fg_focus
= custom
.fg_focus
or beautiful
.menu_fg_focus
or beautiful
.fg_focus
41 theme
.bg_focus
= custom
.bg_focus
or beautiful
.menu_bg_focus
or beautiful
.bg_focus
42 theme
.fg_normal
= custom
.fg_normal
or beautiful
.menu_fg_normal
or beautiful
.fg_normal
43 theme
.bg_normal
= custom
.bg_normal
or beautiful
.menu_bg_normal
or beautiful
.bg_normal
45 theme
.submenu_icon
= custom
.submenu_icon
or beautiful
.menu_submenu_icon
47 theme
.menu_height
= custom
.height
or beautiful
.menu_height
or 16
48 theme
.menu_width
= custom
.width
or beautiful
.menu_width
or 100
50 theme
.border
= custom
.border_color
or beautiful
.menu_border_color
or beautiful
.border_normal
51 theme
.border_width
= custom
.border_width
or beautiful
.menu_border_width
or beautiful
.border_width
56 local function item_leave(menu
, num
)
58 menu
.items
[num
].wibox
.fg
= menu
.theme
.fg_normal
59 menu
.items
[num
].wibox
.bg
= menu
.theme
.bg_normal
63 --- Hide a menu popup.
64 -- @param menu The menu to hide.
66 -- Remove items from screen
67 for i
= 1, #menu
.items
do
69 menu
.items
[i
].wibox
.screen
= nil
71 if menu
.active_child
then
72 menu
.active_child
:hide()
73 menu
.active_child
= nil
77 if cur_menu
== menu
then
78 cur_menu
= cur_menu
.parent
80 if not cur_menu
and menu
.keygrabber
then
81 capi
.keygrabber
.stop()
85 -- Get the elder parent so for example when you kill
86 -- it, it will destroy the whole family.
87 local function get_parents(menu
)
89 return get_parents(menu
.parent
)
94 local function exec(menu
, num
, mouse_event
)
95 local cmd
= menu
.items
[num
].cmd
96 if type(cmd
) == "table" then
100 if not menu
.child
[num
] then
101 menu
.child
[num
] = new({ items
= cmd
}, menu
, num
)
104 if menu
.active_child
then
105 menu
.active_child
:hide()
106 menu
.active_child
= nil
108 menu
.active_child
= menu
.child
[num
]
109 menu
.active_child
:show()
110 elseif type(cmd
) == "string" then
111 get_parents(menu
):hide()
113 elseif type(cmd
) == "function" then
114 get_parents(menu
):hide()
119 local function item_enter(menu
, num
, mouse_event
)
120 if menu
.sel
== num
then
123 item_leave(menu
, menu
.sel
)
126 menu
.items
[num
].wibox
.fg
= menu
.theme
.fg_focus
127 menu
.items
[num
].wibox
.bg
= menu
.theme
.bg_focus
131 if menu
.auto_expand
and mouse_event
then
132 if menu
.active_child
then
133 menu
.active_child
:hide()
134 menu
.active_child
= nil
137 if type(menu
.items
[num
].cmd
) == "table" then
143 local function grabber(mod, key
, event
)
144 if event
== "release" then
148 local sel
= cur_menu
.sel
or 0
149 if key
== menu_keys
.up
then
150 local sel_new
= sel
-1 < 1 and #cur_menu
.items
or sel
-1
151 item_enter(cur_menu
, sel_new
)
152 elseif key
== menu_keys
.down
then
153 local sel_new
= sel
+1 > #cur_menu
.items
and 1 or sel
+1
154 item_enter(cur_menu
, sel_new
)
155 elseif sel
> 0 and key
== menu_keys
.exec
then
157 elseif key
== menu_keys
.back
then
159 elseif key
== menu_keys
.close
then
160 get_parents(cur_menu
):hide()
166 local function add_item(data
, num
, item_info
)
168 position
= "floating",
169 fg
= data
.theme
.fg_normal
,
170 bg
= data
.theme
.bg_normal
,
171 border_color
= data
.theme
.border
,
172 border_width
= data
.theme
.border_width
176 local bindings
= util
.table.join(
177 button({}, 1, function () item_enter(data
, num
); exec(data
, num
) end),
178 button({}, 3, function () hide(data
) end)
181 -- Create the item label widget
182 local label
= widget({ type = "textbox", align
= "left" })
183 label
.text
= item_info
[1]
184 label
:margin({ left
= data
.h
+ 2 })
185 -- Set icon if needed
187 local icon
= type(item_info
[3]) == "string" and image(item_info
[3]) or item_info
[3]
188 if icon
.width
> tonumber(data
.h
) or icon
.height
> tonumber(data
.h
) then
190 if ((data
.h
/icon
.height
) * icon
.width
) > tonumber(data
.h
) then
191 width
, height
= data
.h
, (tonumber(data
.h
) / icon
.width
) * icon
.height
193 width
, height
= (tonumber(data
.h
) / icon
.height
) * icon
.width
, data
.h
195 icon
= icon
:crop_and_scale(0, 0, icon
.width
, icon
.height
, width
, height
)
197 label
.bg_image
= icon
200 item
:buttons(bindings
)
202 function label
.mouse_enter() item_enter(data
, num
, true) end
203 function item
.mouse_enter() item_enter(data
, num
, true) end
205 -- Create the submenu icon widget
207 if type(item_info
[2]) == "table" then
208 submenu
= widget({ type = "imagebox", align
= "right" })
209 submenu
.image
= data
.theme
.submenu_icon
and image(data
.theme
.submenu_icon
)
210 submenu
:buttons(bindings
)
212 function submenu
.mouse_enter() item_enter(data
, num
, true) end
215 -- Add widgets to the wibox
216 item
.widgets
= { label
, submenu
}
220 return { wibox
= item
, cmd
= item_info
[2] }
223 --- Build a popup menu with running clients and shows it.
224 -- @param menu Menu table, see new() function for more informations
226 function clients(menu
)
227 local cls
= capi
.client
.get()
229 for k
, c
in pairs(cls
) do
230 cls_t
[#cls_t
+ 1] = { util
.escape(c
.name
) or "",
232 if not c
:isvisible() then
233 tags
.viewmore(c
:tags(), c
.screen
)
235 capi
.client
.focus
= c
251 local function set_coords(menu
, screen_idx
)
252 local s_geometry
= capi
.screen
[screen_idx
].workarea
253 local screen_w
= s_geometry
.x
+ s_geometry
.width
254 local screen_h
= s_geometry
.y
+ s_geometry
.height
256 local i_h
= menu
.h
- menu
.theme
.border_width
257 local m_h
= (i_h
* #menu
.items
) + menu
.theme
.border_width
260 menu
.w
= menu
.parent
.w
261 menu
.h
= menu
.parent
.h
263 local p_w
= i_h
* (menu
.num
- 1)
264 local m_w
= menu
.w
- menu
.theme
.border_width
266 menu
.y
= menu
.parent
.y
+ p_w
+ m_h
> screen_h
and screen_h
- m_h
or menu
.parent
.y
+ p_w
267 menu
.x
= menu
.parent
.x
+ m_w
*2 > screen_w
and menu
.parent
.x
- m_w
or menu
.parent
.x
+ m_w
269 local m_coords
= capi
.mouse
.coords()
272 menu
.y
= m_coords
.y
+1 < s_geometry
.y
and s_geometry
.y
or m_coords
.y
+1
273 menu
.x
= m_coords
.x
+1 < s_geometry
.x
and s_geometry
.x
or m_coords
.x
+1
275 menu
.y
= menu
.y
+ m_h
> screen_h
and screen_h
- m_h
or menu
.y
276 menu
.x
= menu
.x
+ m_w
> screen_w
and screen_w
- m_w
or menu
.x
281 -- @param menu The menu to show.
282 -- @param keygrabber A boolean enabling or not the keyboard navigation.
283 function show(menu
, keygrabber
)
284 local screen_index
= capi
.mouse
.screen
285 set_coords(menu
, screen_index
)
286 for num
, item
in pairs(menu
.items
) do
287 local wibox
= item
.wibox
292 y
= menu
.y
+ (num
- 1) * (menu
.h
- menu
.theme
.border_width
)
294 wibox
.screen
= screen_index
298 menu
.keygrabber
= menu
.parent
.keygrabber
299 elseif keygrabber
~= nil then
300 menu
.keygrabber
= keygrabber
302 menu
.keygrabber
= false
305 if not cur_menu
and menu
.keygrabber
then
306 capi
.keygrabber
.run(grabber
)
311 --- Toggle menu visibility.
312 -- @param menu The menu to show if it's hidden, or to hide if it's shown.
313 -- @param keygrabber A boolean enabling or not the keyboard navigation.
314 function toggle(menu
, keygrabber
)
315 if menu
.items
[1] and menu
.items
[1].wibox
.screen
then
318 show(menu
, keygrabber
)
322 --- Set key bindings for menu navigation.
323 -- @param keys Table containing the following keys: up, down, exec, back, close. If a key is missing the default key binding will be used, defaults are respectively: "Up", "Down", "Return", "Left", "Escape".
324 function setkeys(keys
)
325 menu_keys
.up
= keys
and keys
.up
or "Up"
326 menu_keys
.down
= keys
and keys
.down
or "Down"
327 menu_keys
.exec
= keys
and keys
.exec
or "Return"
328 menu_keys
.back
= keys
and keys
.back
or "Left"
329 menu_keys
.close
= keys
and keys
.close
or "Escape"
332 --- Open a menu popup.
333 -- @param menu Table containing the menu informations. Key items: Table containing the displayed items, each element is a tab containing: item name, tiggered action, submenu table or function, item icon (optional). Keys [fg|bg]_[focus|normal], border, border_width, submenu_icon, height and width override the default display for your menu, each of them are optional. Key auto_expand controls the submenu auto expand behaviour by setting it to true (default) or false.
334 -- @param parent Specify the parent menu if we want to open a submenu, this value should never be set by the user.
335 -- @param num Specify the parent's clicked item number if we want to open a submenu, this value should never be set by the user.
336 function new(menu
, parent
, num
)
337 -- Create a table to store our menu informations
340 if not menu_keys
.up
then
346 data
.theme
= parent
and parent
.theme
or load_theme(menu
)
350 data
.auto_expand
= parent
.auto_expand
351 elseif menu
.auto_expand
~= nil then
352 data
.auto_expand
= menu
.auto_expand
354 data
.auto_expand
= true
356 data
.h
= parent
and parent
.h
or data
.theme
.menu_height
357 data
.w
= parent
and parent
.w
or data
.theme
.menu_width
360 for k
, v
in pairs(menu
.items
) do
361 table.insert(data
.items
, add_item(data
, k
, v
))