--------------------------------------------------------------------------- -- @author Damien Leone <damien.leone@gmail.com> -- @copyright 2008 Damien Leone -- @release @AWESOME_VERSION@ --------------------------------------------------------------------------- -- Grab environment we need local pairs = pairs local table = table local type = type local wibox = wibox local image = image local string = string local widget = widget local button = button local capi = { screen = screen, mouse = mouse, client = client } local util = require("awful.util") local tags = require("awful.tag") local awbeautiful = require("awful.beautiful") --- Menu module for awful module("awful.menu") -- Table containing all currently opened menus local menus = {} local function load_theme(custom) local theme = {} local beautiful beautiful = awbeautiful.get() 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 theme.submenu_icon = custom.submenu_icon or beautiful.menu_submenu_icon or "@AWESOME_ICON_PATH@/submenu.png" theme.menu_height = custom.height or beautiful.menu_height or 15 theme.menu_width = custom.width or beautiful.menu_width or 100 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 return theme end local function mouse_enter(w, theme) w.fg = theme.fg_focus w.bg = theme.bg_focus end local function mouse_leave(w, theme) w.fg = theme.fg_normal w.bg = theme.bg_normal end local function destroy(data) if data then -- Remove items from screen for i = 1, #data.items do data.items[i].screen = nil end -- Clean memory, dirty but I'm lazy for i = 1, #data.items do table.remove(data.items) end destroy(data.child) -- Remove menu from menus table menus[data.id] = nil end end local function get_parents(data) local p_data = data -- Get the elder parent so when you kill -- it he will destroy the whole family while p_data.parent do p_data = p_data.parent end return p_data end local function exec(data, action, num) if type(action[2]) == "table" then destroy(data.child) data.child = new({ id=action[1], items=action[2] }, data, num) elseif type(action[2]) == "string" then destroy(get_parents(data)) util.spawn(action[2]) elseif type(action[2]) == "function" then destroy(get_parents(data)) action[2]() end end local function add_item(data, num, item_info) local item = wibox({ name = data.id .. "item" .. num, position = "floating", fg = data.theme.fg_normal, bg = data.theme.bg_normal, border_color = data.theme.border, border_width = data.theme.border_width }) -- Create bindings local bindings = { button({}, 1, function () exec(data, item_info, num) end), button({}, 3, function () destroy(data) end) } -- Create the item icon widget local icon = nil if item_info[3] then icon = widget({ type = "imagebox", name = "icon", align = "left" }) if type(item_info[3]) == "string" then icon.image = image(item_info[3]) else icon.image = item_info[3] end else icon = widget({ type = "textbox", name = "icon", align = "left" }) icon.width = data.h end icon:buttons(bindings) function icon.mouse_enter() mouse_enter(item, data.theme) end function icon.mouse_leave() mouse_leave(item, data.theme) end -- Create the item label widget local label = widget({ type = "textbox", name = data.id .. "label" .. num, align = "flex" }) label.text = string.format(" %s", item_info[1]) label:buttons(bindings) function label.mouse_enter() mouse_enter(item, data.theme) end function label.mouse_leave() mouse_leave(item, data.theme) end -- Create the submenu icon widget local submenu = nil if type(item_info[2]) == "table" then submenu = widget({ type = "imagebox", name = "submenu", align = "right" }) submenu.image = image(data.theme.submenu_icon) submenu:buttons(bindings) function submenu.mouse_enter() mouse_enter(item, data.theme) end function submenu.mouse_leave() mouse_leave(item, data.theme) end end -- Add widgets to the wibox item.widgets = { icon, label, submenu } item:geometry({ width = data.w, height = data.h, x = data.x, y = data.y + (num - 1)*(data.h + data.theme.border_width) }) item.ontop = true item.screen = data.screen return item end --- Open a popup menu with running clients. -- @param menu Menu table, see new() function for more informations function clients(menu) local cls = capi.client.get() local cls_t = {} for k, c in pairs(cls) do cls_t[#cls_t + 1] = { util.escape(c.name) or "", function () if not c:isvisible() then tags.viewmore(c:tags(), c.screen) end capi.client.focus = c end, c.icon } end if not menu then menu = {} end menu.id="Clients" menu.items=cls_t return new(menu) end local function set_coords(data) local m_coords = capi.mouse.coords() local s_geometry = capi.screen[data.screen].workarea local screen_w = s_geometry.x + s_geometry.width local screen_h = s_geometry.y + s_geometry.height if data.parent then data.w = data.parent.w data.h = data.parent.h local p_w = (data.h + data.theme.border_width) * (data.num - 1) local m_h = data.theme.border_width + (data.h + data.theme.border_width)*data.nb_items local m_w = data.w + data.theme.border_width data.y = data.parent.y + p_w + m_h > screen_h and screen_h - m_h or data.parent.y + p_w data.x = data.parent.x + data.w*2 + data.theme.border_width > screen_w and data.parent.x - m_w or data.parent.x + m_w else data.y = m_coords.y < s_geometry.y and s_geometry.y or m_coords.y data.x = m_coords.x < s_geometry.x and s_geometry.x or m_coords.x local m_h = data.theme.border_width + (data.h + data.theme.border_width)*data.nb_items local m_w = data.w + data.theme.border_width*2 data.y = data.y + m_h > screen_h and screen_h - m_h or data.y data.x = data.x + m_w > screen_w and screen_w - m_w or data.x end end --- Open a menu popup. -- @param menu Table containing the menu informations. Element id: string naming your menu, only one menu with the same id can be displayed on screen at the same time. Element items: Table containing the displayed items, each element is a tab containing: item name, tiggered action, submenu table or function, item icon (optional). Elements [fg|bg]_[focus|normal], border, border_width, submenu_icon, height and width override the default display for your menu, each of them are optional -- @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. function new(menu, parent, num) -- Close the menu if it was already opened if menus[menu.id] then destroy(menus[menu.id]) end -- Create a table to store our menu informations local data = {} data.id = menu.id data.screen = capi.mouse.screen data.items = {} data.child = nil data.nb_items = #menu.items data.num = num and num or 1 data.theme = parent and parent.theme or load_theme(menu) data.parent = parent and parent or nil data.h = parent and parent.h or data.theme.menu_height data.w = parent and parent.w or data.theme.menu_width set_coords(data) -- Create items for k, v in pairs(menu.items) do table.insert(data.items, add_item(data, k, v)) end -- Add menu to menus table menus[menu.id] = data return data end --- Close a menu popup. -- @param id The name of the menu to close function close(id) destroy(menus[id]) end