-- -*- coding: utf-8 -*-
--------------------------------------------------------------------------------
-- @author Nicolas Berthier &lt;nberthier@gmail.com&gt;
-- @copyright 2010 Nicolas Berthier
--------------------------------------------------------------------------------
--
-- This is a module for defining infolines in awesome. An infoline is a wibox
-- attached to a region (typically, a client or the root window).
--
-- Remarks:
--
-- - It has not been tested with multiple screens yet.
--
-- Example usage: (TODO --- read the comments for now, there are not many
-- functions)
--
--------------------------------------------------------------------------------

--{{{ Grab environment (mostly aliases)
local setmetatable = setmetatable
local ipairs = ipairs
local type = type
local pairs = pairs
local string = string
local print = print
local error = error
local io = io

local client = client
local awesome = awesome
local wibox = wibox
local widget = widget
local root  = root
local screen  = screen
local mouse  = mouse
--}}}

module ("infoline")

-- Privata data: we use weak keys in order to allow collection of private data
-- if keys (clients) are collected (i.e., no longer used, after having been
-- killed for instance).
-- 
-- XXX: For now, we have at most one infoline per client, but it could be
-- interesting to create several types of infolines (identified by indexes to be
-- allocated by this module), and associated to, e.g., different configuration
-- flags and positioning routine...
local data = setmetatable ({}, { __mode = 'k' })

--{{{ Infoline positioning

-- XXX: this is a hack that positions an infoline at the bottom of a given area.
local function setup_position (wb, geom)
    local b = wb:geometry ()
    b.x = geom.x
    b.width = geom.width
    b.y = geom.y + geom.height - awesome.font_height
    b.height = awesome.font_height
    wb:geometry (b)
end

--}}}

--{{{ Configurations:

-- When true, this flag indicates that an infoline is hidden if its attached
-- client loses its focus. Otherwise, it remains always visible.
follow_focus = true

--}}}

--{{{ Infoline updates

function get_text (il) return il.wb.widgets[1].text end
function set_text (il, text) il.wb.widgets[1].text = text end

-- Forces a refresh of the given infoline.
function update (il)
    local wb = il.wb
    local c = il.cli

    if il.enabled then
        -- XXX: Note this could be much better if we had some sort of root and
        -- client interface unification: the following involves a priori useless
        -- code duplication...
        if c == root then
            wb.screen = mouse.screen -- XXX: is this the behavior we need?
            wb.visible = true
            setup_position (wb, screen[mouse.screen].workarea)
        else
            if c:isvisible () and (not follow_focus or client.focus == c) then
                wb.screen = c.screen
                wb.visible = true
                setup_position (wb, c:geometry ())
            else                    -- do we have to hide it?
                wb.visible = false
            end
        end
    elseif wb.visible then      --otherwise we need to hide it.
        wb.visible = false
    end
end

local function update_from_client (c)
    -- Note that we may not have an infoline for this client, hence the
    -- conditional...
    if data[c] then update (data[c]) end
end

-- Force execution of the above function on client state modification.
client.add_signal ("focus", update_from_client)
client.add_signal ("unfocus", update_from_client)
client.add_signal ("unmanage", update_from_client)

--}}}

--{{{ Infoline management

--- Creates a new infoline, with the given initial text. Note it is not visible
--- by default, and not attached to any client.
function new (text)
    local il = {
        wb = wibox ({
                        ontop = true, -- XXX: setting a depth when attaching to
                                      --a client would be much better
                        widgets = {
                            widget ({ type = "textbox", align="left" })
                        },
                    })
    }
    -- these will remain false until the infoline is attached to a client.
    il.wb.visible = false
    il.enabled = false
    set_text (il, text or "")
    return il
end

-- Attached infolines will react to the following client-related signals, and
-- automatically setup their position according to the client's geometry.
local csignals = { "property::geometry", "property::minimized",
    "property::visible", "property::focus", "property::screen", }

-- Attaches an infoline to a client. Note the infoline becomes visible at that
-- time, if the client is currently visible (and if it has focus, when
-- `follow_focus' holds).
function attach (il, c)
    data[c] = il
    il.cli = c
    il.enabled = true
    update (il)
    if c ~= root then
        -- subscribe to client-related signals
        for _, s in ipairs (csignals) do
            c:add_signal (s, update_from_client)
        end
    end
end

--- Detach the given infoline from its client, if any.
function dispose (il)
    local c = il.cli
    if c then -- note c can be nil here, if the given infoline has not been
              --attached to any client...
        il.enabled = false
        update (il)         -- a shortcut here would be: `il.wb.visible = false'
        data[c] = nil
        if c ~= root then
            -- unsubscribe from client-related signals
            for _, s in ipairs (csignals) do
                c:remove_signal (s, update_from_client)
            end
        end
    end
end

--}}}

-- Local variables:
-- indent-tabs-mode: nil
-- fill-column: 80
-- lua-indent-level: 4
-- End:
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
