Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: ee43d9c01d
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 432 lines (384 sloc) 16.048 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
---------------------------------------------------------------------------
-- @author Julien Danjou <julien@danjou.info>
-- @copyright 2008 Julien Danjou
-- @release @AWESOME_VERSION@
---------------------------------------------------------------------------

-- Grab environment we need
local math = math
local image = image
local pairs = pairs
local type = type
local setmetatable = setmetatable
local type = type
local capi =
{
    awesome = awesome,
    wibox = wibox,
    widget = widget,
    client = client,
}
local abutton = require("awful.button")
local beautiful = require("beautiful")
local util = require("awful.util")
local widget = require("awful.widget")
local mouse = require("awful.mouse")
local client = require("awful.client")
local layout = require("awful.widget.layout")

--- Titlebar module for awful
module("tbar")

-- Privata data
local data = setmetatable({}, { __mode = 'k' })

-- Predeclaration for buttons
local button_groups

local function button_callback_focus_raise_move(w, t)
    capi.client.focus = t.client
    t.client:raise()
    mouse.client.move(t.client)
end

local function button_callback_move(w, t)
    return mouse.client.move(t.client)
end

local function button_callback_resize(w, t)
    return mouse.client.resize(t.client)
end

--- Create a standard titlebar.
-- @param c The client.
-- @param args Arguments.
-- modkey: the modkey used for the bindings.
-- fg: the foreground color.
-- bg: the background color.
-- fg_focus: the foreground color for focused window.
-- fg_focus: the background color for focused window.
-- width: the titlebar width
function add(c, args)
    if not c or (c.type ~= "normal" and c.type ~= "dialog") then return end
    if not args then args = {} end
    if not args.height then args.height = capi.awesome.font_height * 1.5 end
    local theme = beautiful.get()
    if not args.widget then customwidget = {} else customwidget = args.widget end
    -- Store colors
    data[c] = {}
    data[c].fg = args.fg or theme.titlebar_fg_normal or theme.fg_normal
    data[c].bg = args.bg or theme.titlebar_bg_normal or theme.bg_normal
    data[c].fg_focus = args.fg_focus or theme.titlebar_fg_focus or theme.fg_focus
    data[c].bg_focus = args.bg_focus or theme.titlebar_bg_focus or theme.bg_focus
    data[c].width = args.width
    data[c].font = args.font or theme.titlebar_font or theme.font

    local tb = capi.wibox(args)

    local title = capi.widget({ type = "textbox" })
    if c.name then
        title.text = "<span font_desc='" .. data[c].font .. "'> " ..
                     util.escape(c.name) .. " </span>"
    end
    layout.margins[title] = { top = 1 }


    -- Redirect relevant events to the client the titlebar belongs to
    local bts = util.table.join(
        abutton({ }, 1, button_callback_focus_raise_move),
        abutton({ args.modkey }, 1, button_callback_move),
        abutton({ args.modkey }, 3, button_callback_resize))
    title:buttons(bts)

    -- for each button group, call create for the client.
    -- if a button set is created add the set to the
    -- data[c].button_sets for late updates and add the
    -- individual buttons to the array part of the widget
    -- list
    local widget_list = {
        layout = layout.horizontal.rightleft
    }
    local iw = 1
    local is = 1
    data[c].button_sets = {}
    for i = 1, #button_groups do
        local set = button_groups[i].create(c, args.modkey, theme)
        if (set) then
            data[c].button_sets[is] = set
            is = is + 1
            for n,b in pairs(set) do
                widget_list[iw] = b
                iw = iw + 1
            end
        end
    end

    local icon = capi.widget({ type = "imagebox" })
    local focused_icon_image = image(theme.titlebar_icon_focus)
    local normal_icon_image = image(theme.titlebar_icon_normal)

    data[c].update_icon = function(c)
        if capi.client.focus == c then
            icon.image = focused_icon_image
        else
            icon.image = normal_icon_image
        end
    end

    tb.widgets = {
        {
            icon,
            layout = layout.horizontal.leftright
        },
        {
            widget_list,
            customwidget,
            layout = layout.horizontal.rightleft
        },
        title,
        layout = layout.horizontal.leftright
    }

    c.titlebar = tb

    c:add_signal("property::icon", update)
    c:add_signal("property::name", update)
    c:add_signal("property::sticky", update)
    c:add_signal("property::floating", update)
    c:add_signal("property::ontop", update)
    c:add_signal("property::maximized_vertical", update)
    c:add_signal("property::maximized_horizontal", update)
    update(c)
end

--- Update a titlebar. This should be called in some hooks.
-- @param c The client to update.
-- @param prop The property name which has changed.
function update(c)
     if c.titlebar and data[c] then
        data[c].update_icon(c)

        local widgets = c.titlebar.widgets
        if widgets[3] then
            widgets[3].text = "<span font_desc='" .. data[c].font ..
            "'> ".. util.escape(c.name or "<unknown>") .. " </span>"
        end

        if capi.client.focus == c then
            c.titlebar.fg = data[c].fg_focus
            c.titlebar.bg = data[c].bg_focus
        else
            c.titlebar.fg = data[c].fg
            c.titlebar.bg = data[c].bg
        end

        -- iterated of all registered button_sets and update
        local sets = data[c].button_sets
        for i = 1, #sets do
            sets[i].update(c,prop)
        end
    end
end

--- Remove a titlebar from a client.
-- @param c The client.
function remove(c)
    c.titlebar = nil
    data[c] = nil
end

-- Create a new button for the toolbar
-- @param c The client of the titlebar
-- @param name The base name of the button (i.e. close)
-- @param modkey ... you know that one, don't you?
-- @param theme The theme from beautifull. Used to get the image paths
-- @param state The state the button is associated to. Containse path the action and info about the image
local function button_new(c, name, modkey, theme, state)
    local bts = abutton({ }, 1, nil, state.action)

    -- get the image path from the theme. Only return a button if we find an image
    local img
    img = "titlebar_" .. name .. "_button_" .. state.img
    img = theme[img]
    if not img then return end
    img = image(img)
    if not img then return end

    -- now create the button
    local bname = name .. "_" .. state.idx
    local button = widget.button({ image = img })
    if not button then return end
    local rbts = button:buttons()

    for k, v in pairs(rbts) do
        bts[#bts + 1] = v
    end

    button:buttons(bts)
    button.visible = false
    return button
end

-- Update the buttons in a button group
-- @param s The button group to update
-- @param c The client of the titlebar
-- @param p The property that has changed
local function button_group_update(s,c,p)
    -- hide the currently active button, get the new state and show the new button
    local n = s.select_state(c,p)
    if n == nil then return end
    if (s.active ~= nil) then s.active.visible = false end
    s.active = s.buttons[n]
    s.active.visible = true
end

-- Create all buttons in a group
-- @param c The client of the titlebar
-- @param group The button group to create the buttons for
-- @param modkey ...
-- @param theme Theme for the image paths
local function button_group_create(c, group, modkey, theme )
    local s = {}
    s.name = group.name
    s.select_state = group.select_state
    s.buttons = {
        layout = layout.horizontal.rightleft
    }
    for n,state in pairs(group.states) do
        s.buttons[n] = button_new(c, s.name, modkey, theme, state)
        if (s.buttons[n] == nil) then return end
        for a,v in pairs(group.attributes) do
            s.buttons[n][a] = v
        end
    end
    function s.update(c,p) button_group_update(s,c,p) end
    return s
end

-- Builds a new button group
-- @param name The base name for the buttons in the group (i.e. "close")
-- @param attrs Common attributes for the buttons (i.e. {align = "right")
-- @param sfn State select function.
-- @param args The states of the button
local function button_group(name, attrs, sfn, ...)
    local s = {}
    s.name = name
    s.select_state = sfn
    s.attributes = attrs
    s.states = {}

    for i, state in pairs({...}) do
        s.states[state.idx] = state
    end

    function s.create(c,modkey, theme) return button_group_create(c,s,modkey, theme) end
    return s
end

-- Select a state for a client based on an attribute of the client and whether it has focus
-- @param c The client of the titlebar
-- @param p The property that has changed
-- @param a The property to check
local function select_state(c,p,a)
    if (c == nil) then return "n/i" end
    if capi.client.focus == c then
        if c[a] then
            return "f/a"
        else
            return "f/i"
        end
    else
        if c[a] then
            return "n/a"
        else
            return "n/i"
        end
    end
end

-- Select a state for a client based on whether it's floating or not
-- @param c The client of the titlebar
-- @param p The property that has changed
local function select_state_floating(c,p)
    if not c then return end
    if capi.client.focus == c then
        if client.floating.get(c) then
            return "f/a"
        end
        return "f/i"
    end
    if client.floating.get(c) then
        return "n/a"
    end
    return "n/i"
end

-- Select a state for a client based on whether it's maximized or not
-- @param c The client of the titlebar
-- @param p The property that has changed
local function select_state_maximized(c,p)
    if (c == nil) then return "n/i" end
    if capi.client.focus == c then
        if c.maximized_horizontal or c.maximized_vertical then
            return "f/a"
        else
            return "f/i"
        end
    else
        if c.maximized_horizontal or c.maximized_vertical then
            return "n/a"
        else
            return "n/i"
        end
    end
end

-- Select a state for a client based on whether it has focus or not
-- @param c The client of the titlebar
-- @param p The property that has changed
local function select_state_focus(c,p)
    if c and capi.client.focus == c then
        return "f"
    end
    return "n"
end

-- These are the predefined button groups
-- A short explanation using 'close_buttons' as an example:
-- "close" : name of the button, the images for this button are taken from the
-- theme variables titlebar_close_button_...
-- { align ... : attributes of all the buttons
-- select_state_focus : This function returns a short string used to describe
-- the state. In this case either "n" or "f" depending on
-- the focus state of the client. These strings can be
-- choosen freely but the< must match one of the idx fuekds
-- of the states below
-- { idx = "n" ... : This is the state of the button for the 'unfocussed'
-- (normal) state. The idx = "n" parameter connects this
-- button to the return value of the 'select_state_focus'
-- function. The img = "normal" parameter is used to
-- determine its image. In this case the iamge is taken from
-- the theme variable "titlebar_close_button_normal".
-- Finally the last parameter is the action for mouse
-- button 1

local ontop_buttons = button_group("ontop",
                                   { align = "right" },
                                   function(c,p) return select_state(c, p, "ontop") end,
                                   { idx = "n/i", img = "normal_inactive",
                                     action = function(w, t) t.client.ontop = true end },
                                   { idx = "f/i", img = "focus_inactive",
                                     action = function(w, t) t.client.ontop = true end },
                                   { idx = "n/a", img = "normal_active",
                                     action = function(w, t) t.client.ontop = false end },
                                   { idx = "f/a", img = "focus_active",
                                     action = function(w, t) t.client.ontop = false end })

local sticky_buttons = button_group("sticky",
                                    { align = "right" },
                                    function(c,p) return select_state(c,p,"sticky") end,
                                    { idx = "n/i", img = "normal_inactive",
                                      action = function(w, t) t.client.sticky = true end },
                                    { idx = "f/i", img = "focus_inactive",
                                      action = function(w, t) t.client.sticky = true end },
                                    { idx = "n/a", img = "normal_active",
                                      action = function(w, t) t.client.sticky = false end },
                                    { idx = "f/a", img = "focus_active",
                                      action = function(w, t) t.client.sticky = false end })

local maximized_buttons = button_group("maximized",
                                     { align = "right" },
                                     select_state_maximized,
                                     { idx = "n/i", img = "normal_inactive",
                                       action = function(w, t) t.client.maximized_horizontal = true
                                                               t.client.maximized_vertical = true end },
                                     { idx = "f/i", img = "focus_inactive",
                                       action = function(w, t) t.client.maximized_horizontal = true
                                                               t.client.maximized_vertical = true end },
                                     { idx = "n/a", img = "normal_active",
                                       action = function(w, t) t.client.maximized_horizontal = false
                                                               t.client.maximized_vertical = false end },
                                     { idx = "f/a", img = "focus_active",
                                       action = function(w, t) t.client.maximized_horizontal = false
                                                               t.client.maximized_vertical = false end })

local close_buttons = button_group("close",
                                   { align = "left" },
                                   select_state_focus,
                                   { idx = "n", img = "normal",
                                     action = function (w, t) t.client:kill() end },
                                   { idx = "f", img = "focus",
                                     action = function (w, t) t.client:kill() end })

local function floating_update(w, t)
    client.floating.toggle(t.client)
end

local floating_buttons = button_group("floating",
                                     { align = "right"},
                                     select_state_floating,
                                     { idx = "n/i", img = "normal_inactive", action = floating_update },
                                     { idx = "f/i", img = "focus_inactive", action = floating_update },
                                     { idx = "n/a", img = "normal_active", action = floating_update },
                                     { idx = "f/a", img = "focus_active", action = floating_update })

button_groups = { close_buttons, }

-- Register standards hooks
capi.client.add_signal("focus", update)
capi.client.add_signal("unfocus", update)

-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
Something went wrong with that request. Please try again.