2008-10-22 14:22:48 +02:00
---------------------------------------------------------------------------
-- @author Damien Leone <damien.leone@gmail.com>
2008-11-07 15:27:35 +01:00
-- @author Julien Danjou <julien@danjou.info>
-- @copyright 2008 Damien Leone, Julien Danjou
2008-10-22 14:22:48 +02:00
-- @release @AWESOME_VERSION@
---------------------------------------------------------------------------
-- Grab environment we need
2008-10-23 11:19:46 +02:00
local pairs = pairs
2008-10-22 14:22:48 +02:00
local table = table
local type = type
2009-07-15 15:57:31 +02:00
local setmetatable = setmetatable
2008-10-22 14:22:48 +02:00
local wibox = wibox
local image = image
local widget = widget
2009-04-17 18:08:52 +02:00
local button = require ( " awful.button " )
2009-03-04 19:42:25 +01:00
local capi =
{
screen = screen ,
mouse = mouse ,
2009-03-09 14:14:01 +01:00
client = client ,
keygrabber = keygrabber
2009-03-04 19:42:25 +01:00
}
2008-10-22 14:22:48 +02:00
local util = require ( " awful.util " )
2008-10-23 11:19:46 +02:00
local tags = require ( " awful.tag " )
2009-03-10 20:31:41 +01:00
local layout = require ( " awful.widget.layout " )
2008-11-13 11:53:41 +01:00
local awbeautiful = require ( " beautiful " )
2009-05-05 17:32:53 +02:00
local awibox = require ( " awful.wibox " )
2008-12-18 18:02:54 +01:00
local tonumber = tonumber
2008-10-22 14:22:48 +02:00
--- Menu module for awful
module ( " awful.menu " )
2009-03-09 14:14:01 +01:00
local cur_menu
2009-03-09 14:22:13 +01:00
local menu_keys = { }
2009-03-09 14:14:01 +01:00
2008-10-24 22:31:14 +02:00
local function load_theme ( custom )
local theme = { }
2008-10-22 14:22:48 +02:00
local beautiful
2008-10-24 22:31:14 +02:00
2008-10-22 14:22:48 +02:00
beautiful = awbeautiful.get ( )
2008-10-24 22:31:14 +02:00
theme.fg_focus = custom.fg_focus or beautiful.menu_fg_focus or beautiful.fg_focus
theme.bg_focus = custom.bg_focus or beautiful.menu_bg_focus or beautiful.bg_focus
theme.fg_normal = custom.fg_normal or beautiful.menu_fg_normal or beautiful.fg_normal
theme.bg_normal = custom.bg_normal or beautiful.menu_bg_normal or beautiful.bg_normal
2008-11-10 11:57:20 +01:00
theme.submenu_icon = custom.submenu_icon or beautiful.menu_submenu_icon
2008-10-22 14:22:48 +02:00
2008-12-18 18:10:52 +01:00
theme.menu_height = custom.height or beautiful.menu_height or 16
2008-10-24 22:31:14 +02:00
theme.menu_width = custom.width or beautiful.menu_width or 100
2008-10-22 14:22:48 +02:00
2008-10-24 22:31:14 +02:00
theme.border = custom.border_color or beautiful.menu_border_color or beautiful.border_normal
theme.border_width = custom.border_width or beautiful.menu_border_width or beautiful.border_width
2008-10-22 14:22:48 +02:00
2008-10-24 22:31:14 +02:00
return theme
2008-10-22 14:22:48 +02:00
end
2009-03-04 19:42:25 +01:00
local function item_leave ( menu , num )
if num > 0 then
menu.items [ num ] . wibox.fg = menu.theme . fg_normal
menu.items [ num ] . wibox.bg = menu.theme . bg_normal
end
end
2008-11-07 15:27:35 +01:00
--- Hide a menu popup.
-- @param menu The menu to hide.
function hide ( menu )
2008-11-10 19:09:20 +01:00
-- Remove items from screen
for i = 1 , # menu.items do
2009-05-06 23:46:39 +02:00
item_leave ( menu , i )
2009-02-23 12:04:27 +01:00
menu.items [ i ] . wibox.screen = nil
2008-11-10 19:09:20 +01:00
end
if menu.active_child then
menu.active_child : hide ( )
2009-03-04 19:42:25 +01:00
menu.active_child = nil
2008-10-22 14:22:48 +02:00
end
2009-03-04 19:42:25 +01:00
menu.sel = nil
2009-03-09 14:14:01 +01:00
if cur_menu == menu then
cur_menu = cur_menu.parent
end
if not cur_menu and menu.keygrabber then
capi.keygrabber . stop ( )
end
2008-10-22 14:22:48 +02:00
end
2009-01-08 12:25:27 +01:00
-- Get the elder parent so for example when you kill
2008-11-07 15:27:35 +01:00
-- it, it will destroy the whole family.
2009-03-04 19:42:25 +01:00
local function get_parents ( menu )
if menu.parent then
return get_parents ( menu.parent )
2008-10-22 14:22:48 +02:00
end
2009-03-04 19:42:25 +01:00
return menu
2008-10-22 14:22:48 +02:00
end
2009-03-04 19:42:25 +01:00
local function exec ( menu , num , mouse_event )
2009-04-30 02:12:23 +02:00
local cmd = menu.items [ num ] . cmd
2009-02-23 12:04:27 +01:00
if type ( cmd ) == " table " then
2009-03-30 23:45:29 +02:00
if # cmd == 0 then
return
end
2009-03-04 19:42:25 +01:00
if not menu.child [ num ] then
menu.child [ num ] = new ( { items = cmd } , menu , num )
2008-11-10 19:09:20 +01:00
end
2009-03-04 19:42:25 +01:00
if menu.active_child then
menu.active_child : hide ( )
menu.active_child = nil
2008-10-30 12:46:49 +01:00
end
2009-03-04 19:42:25 +01:00
menu.active_child = menu.child [ num ]
menu.active_child : show ( )
2009-02-23 12:04:27 +01:00
elseif type ( cmd ) == " string " then
2009-03-04 19:42:25 +01:00
get_parents ( menu ) : hide ( )
2009-02-23 12:04:27 +01:00
util.spawn ( cmd )
elseif type ( cmd ) == " function " then
2009-03-04 19:42:25 +01:00
get_parents ( menu ) : hide ( )
2009-02-23 12:04:27 +01:00
cmd ( )
2008-10-22 14:22:48 +02:00
end
end
2009-03-04 19:42:25 +01:00
local function item_enter ( menu , num , mouse_event )
if menu.sel == num then
return
elseif menu.sel then
item_leave ( menu , menu.sel )
end
2009-02-22 16:01:39 +01:00
2009-03-04 19:42:25 +01:00
menu.items [ num ] . wibox.fg = menu.theme . fg_focus
menu.items [ num ] . wibox.bg = menu.theme . bg_focus
menu.sel = num
2009-03-09 14:14:01 +01:00
cur_menu = menu
2009-03-04 19:42:25 +01:00
if menu.auto_expand and mouse_event then
if menu.active_child then
menu.active_child : hide ( )
menu.active_child = nil
2009-02-22 16:01:39 +01:00
end
2009-03-04 19:42:25 +01:00
if type ( menu.items [ num ] . cmd ) == " table " then
exec ( menu , num )
2009-02-22 16:01:39 +01:00
end
end
2009-02-23 12:04:27 +01:00
end
2009-03-09 14:14:01 +01:00
local function grabber ( mod , key , event )
if event == " release " then
return true
end
local sel = cur_menu.sel or 0
2009-03-09 14:22:13 +01:00
if key == menu_keys.up then
2009-03-09 14:14:01 +01:00
local sel_new = sel - 1 < 1 and # cur_menu.items or sel - 1
item_enter ( cur_menu , sel_new )
2009-03-09 14:22:13 +01:00
elseif key == menu_keys.down then
2009-03-09 14:14:01 +01:00
local sel_new = sel + 1 > # cur_menu.items and 1 or sel + 1
item_enter ( cur_menu , sel_new )
2009-03-09 14:22:13 +01:00
elseif sel > 0 and key == menu_keys.exec then
2009-03-09 14:14:01 +01:00
exec ( cur_menu , sel )
2009-03-09 14:22:13 +01:00
elseif key == menu_keys.back then
2009-03-09 14:14:01 +01:00
cur_menu : hide ( )
2009-03-09 14:22:13 +01:00
elseif key == menu_keys.close then
2009-03-09 14:14:01 +01:00
get_parents ( cur_menu ) : hide ( )
end
return true
end
2008-10-22 14:22:48 +02:00
local function add_item ( data , num , item_info )
local item = wibox ( {
position = " floating " ,
2008-10-24 22:31:14 +02:00
fg = data.theme . fg_normal ,
bg = data.theme . bg_normal ,
border_color = data.theme . border ,
border_width = data.theme . border_width
2008-10-22 14:22:48 +02:00
} )
-- Create bindings
2009-04-17 18:08:52 +02:00
local bindings = util.table . join (
2009-03-04 19:42:25 +01:00
button ( { } , 1 , function ( ) item_enter ( data , num ) ; exec ( data , num ) end ) ,
2008-11-07 15:27:35 +01:00
button ( { } , 3 , function ( ) hide ( data ) end )
2009-04-17 18:08:52 +02:00
)
2008-10-22 14:22:48 +02:00
2008-12-18 18:02:54 +01:00
-- Create the item label widget
2009-03-10 20:31:41 +01:00
local label = widget ( { type = " textbox " } )
2008-12-18 18:02:54 +01:00
label.text = item_info [ 1 ]
label : margin ( { left = data.h + 2 } )
-- Set icon if needed
2008-11-07 15:27:35 +01:00
if item_info [ 3 ] then
2008-12-18 18:02:54 +01:00
local icon = type ( item_info [ 3 ] ) == " string " and image ( item_info [ 3 ] ) or item_info [ 3 ]
if icon.width > tonumber ( data.h ) or icon.height > tonumber ( data.h ) then
local width , height
if ( ( data.h / icon.height ) * icon.width ) > tonumber ( data.h ) then
width , height = data.h , ( tonumber ( data.h ) / icon.width ) * icon.height
else
width , height = ( tonumber ( data.h ) / icon.height ) * icon.width , data.h
end
icon = icon : crop_and_scale ( 0 , 0 , icon.width , icon.height , width , height )
2008-10-23 11:19:46 +02:00
end
2008-12-18 18:02:54 +01:00
label.bg_image = icon
2008-10-22 14:22:48 +02:00
end
2009-08-14 16:46:35 +02:00
item : buttons ( bindings )
2008-10-22 14:22:48 +02:00
2009-03-04 19:42:25 +01:00
function label . mouse_enter ( ) item_enter ( data , num , true ) end
function item . mouse_enter ( ) item_enter ( data , num , true ) end
2009-02-13 21:20:23 +01:00
2008-10-22 14:22:48 +02:00
-- Create the submenu icon widget
2008-11-07 15:27:35 +01:00
local submenu
2008-10-22 14:22:48 +02:00
if type ( item_info [ 2 ] ) == " table " then
2009-03-10 20:31:41 +01:00
submenu = widget ( { type = " imagebox " } )
2008-11-10 11:57:20 +01:00
submenu.image = data.theme . submenu_icon and image ( data.theme . submenu_icon )
2009-08-14 16:46:35 +02:00
submenu : buttons ( bindings )
2008-10-22 14:22:48 +02:00
2009-03-04 19:42:25 +01:00
function submenu . mouse_enter ( ) item_enter ( data , num , true ) end
2008-10-22 14:22:48 +02:00
end
-- Add widgets to the wibox
2009-03-10 20:31:41 +01:00
item.widgets = {
label ,
submenu ,
layout = layout.horizontal . flex
}
2008-10-22 14:22:48 +02:00
item.ontop = true
2009-02-23 12:04:27 +01:00
return { wibox = item , cmd = item_info [ 2 ] }
2008-10-22 14:22:48 +02:00
end
2008-11-07 15:27:35 +01:00
--- Build a popup menu with running clients and shows it.
2008-10-24 22:31:14 +02:00
-- @param menu Menu table, see new() function for more informations
2008-11-07 15:27:35 +01:00
-- @return The menu.
2008-10-24 22:31:14 +02:00
function clients ( menu )
2008-10-23 11:19:46 +02:00
local cls = capi.client . get ( )
local cls_t = { }
for k , c in pairs ( cls ) do
2008-10-27 12:05:34 +01:00
cls_t [ # cls_t + 1 ] = { util.escape ( c.name ) or " " ,
2008-10-23 11:19:46 +02:00
function ( )
if not c : isvisible ( ) then
tags.viewmore ( c : tags ( ) , c.screen )
end
capi.client . focus = c
end ,
c.icon }
end
2008-10-24 22:31:14 +02:00
if not menu then
menu = { }
end
2008-11-07 15:27:35 +01:00
menu.items = cls_t
2008-10-24 22:31:14 +02:00
2009-04-30 02:12:23 +02:00
local m = new ( menu )
2008-11-07 15:27:35 +01:00
m : show ( )
return m
2008-10-23 11:19:46 +02:00
end
2008-11-07 15:27:35 +01:00
local function set_coords ( menu , screen_idx )
2009-05-05 17:32:53 +02:00
local s_geometry = awibox.get_workarea ( screen_idx )
2008-10-24 11:04:02 +02:00
local screen_w = s_geometry.x + s_geometry.width
2008-10-24 22:31:14 +02:00
local screen_h = s_geometry.y + s_geometry.height
2008-10-24 11:04:02 +02:00
2009-03-10 20:31:41 +01:00
local i_h = menu.h + menu.theme . border_width
2009-02-22 15:48:23 +01:00
local m_h = ( i_h * # menu.items ) + menu.theme . border_width
2008-11-07 15:27:35 +01:00
if menu.parent then
menu.w = menu.parent . w
menu.h = menu.parent . h
2009-02-22 15:48:23 +01:00
local p_w = i_h * ( menu.num - 1 )
local m_w = menu.w - menu.theme . border_width
2008-11-07 15:27:35 +01:00
menu.y = menu.parent . y + p_w + m_h > screen_h and screen_h - m_h or menu.parent . y + p_w
2009-02-22 15:48:23 +01:00
menu.x = menu.parent . x + m_w * 2 > screen_w and menu.parent . x - m_w or menu.parent . x + m_w
2008-10-24 09:10:42 +02:00
else
2008-11-07 15:27:35 +01:00
local m_coords = capi.mouse . coords ( )
2009-02-22 15:48:23 +01:00
local m_w = menu.w
2008-11-07 15:27:35 +01:00
2009-03-09 14:14:01 +01:00
menu.y = m_coords.y + 1 < s_geometry.y and s_geometry.y or m_coords.y + 1
menu.x = m_coords.x + 1 < s_geometry.x and s_geometry.x or m_coords.x + 1
2008-11-07 15:27:35 +01:00
menu.y = menu.y + m_h > screen_h and screen_h - m_h or menu.y
menu.x = menu.x + m_w > screen_w and screen_w - m_w or menu.x
end
end
--- Show a menu.
-- @param menu The menu to show.
2009-03-09 14:14:01 +01:00
-- @param keygrabber A boolean enabling or not the keyboard navigation.
function show ( menu , keygrabber )
2008-11-07 15:27:35 +01:00
local screen_index = capi.mouse . screen
set_coords ( menu , screen_index )
for num , item in pairs ( menu.items ) do
2009-02-23 12:04:27 +01:00
local wibox = item.wibox
wibox : geometry ( {
2008-11-07 15:27:35 +01:00
width = menu.w ,
height = menu.h ,
x = menu.x ,
2009-03-10 20:31:41 +01:00
y = menu.y + ( num - 1 ) * ( menu.h + menu.theme . border_width )
2008-11-07 15:27:35 +01:00
} )
2009-02-23 12:04:27 +01:00
wibox.screen = screen_index
2008-11-07 15:27:35 +01:00
end
2009-03-09 14:14:01 +01:00
if menu.parent then
2009-03-09 14:22:13 +01:00
menu.keygrabber = menu.parent . keygrabber
2009-03-09 14:14:01 +01:00
elseif keygrabber ~= nil then
menu.keygrabber = keygrabber
else
menu.keygrabber = false
end
if not cur_menu and menu.keygrabber then
capi.keygrabber . run ( grabber )
end
cur_menu = menu
2008-11-07 15:27:35 +01:00
end
2008-10-24 09:10:42 +02:00
2008-11-07 15:27:35 +01:00
--- Toggle menu visibility.
-- @param menu The menu to show if it's hidden, or to hide if it's shown.
2009-03-09 14:14:01 +01:00
-- @param keygrabber A boolean enabling or not the keyboard navigation.
function toggle ( menu , keygrabber )
2009-02-23 12:04:27 +01:00
if menu.items [ 1 ] and menu.items [ 1 ] . wibox.screen then
2008-11-07 15:27:35 +01:00
hide ( menu )
else
2009-03-09 14:14:01 +01:00
show ( menu , keygrabber )
2008-10-24 09:10:42 +02:00
end
end
2009-03-09 14:22:13 +01:00
--- Set key bindings for menu navigation.
-- @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".
function setkeys ( keys )
menu_keys.up = keys and keys.up or " Up "
menu_keys.down = keys and keys.down or " Down "
menu_keys.exec = keys and keys.exec or " Return "
menu_keys.back = keys and keys.back or " Left "
menu_keys.close = keys and keys.close or " Escape "
end
2008-10-23 11:19:46 +02:00
--- Open a menu popup.
2009-02-22 16:01:39 +01:00
-- @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.
2008-10-23 11:19:46 +02:00
-- @param parent Specify the parent menu if we want to open a submenu, this value should never be set by the user.
-- @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.
2008-10-23 15:11:13 +02:00
function new ( menu , parent , num )
2008-10-22 14:22:48 +02:00
-- Create a table to store our menu informations
local data = { }
2008-10-30 12:46:49 +01:00
2009-03-09 14:22:13 +01:00
if not menu_keys.up then
2009-03-30 23:46:11 +02:00
setkeys ( menu.keys )
2009-03-09 14:22:13 +01:00
end
2008-10-22 14:22:48 +02:00
data.items = { }
2008-11-07 15:27:35 +01:00
data.num = num or 1
2008-10-24 22:31:14 +02:00
data.theme = parent and parent.theme or load_theme ( menu )
2008-11-07 15:27:35 +01:00
data.parent = parent
2008-11-10 19:09:20 +01:00
data.child = { }
2009-02-23 14:19:49 +01:00
if parent then
data.auto_expand = parent.auto_expand
2009-02-23 14:12:28 +01:00
elseif menu.auto_expand ~= nil then
data.auto_expand = menu.auto_expand
else
data.auto_expand = true
end
2008-10-24 22:31:14 +02:00
data.h = parent and parent.h or data.theme . menu_height
data.w = parent and parent.w or data.theme . menu_width
2008-10-22 14:22:48 +02:00
-- Create items
2008-10-23 15:11:13 +02:00
for k , v in pairs ( menu.items ) do
2008-10-23 11:19:46 +02:00
table.insert ( data.items , add_item ( data , k , v ) )
2008-10-22 14:22:48 +02:00
end
2008-11-07 15:27:35 +01:00
-- Set methods
data.hide = hide
data.show = show
data.toggle = toggle
2008-10-22 14:22:48 +02:00
return data
2009-04-17 18:08:52 +02:00
end
2009-07-15 15:57:31 +02:00
setmetatable ( _M , { __call = function ( _ , ... ) return new ( ... ) end } )
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80