-- -*- coding: utf-8 -*-
--------------------------------------------------------------------------------
-- @author Nicolas Berthier &lt;nberthier@gmail.com&gt;
-- @copyright 2010 Nicolas Berthier
--------------------------------------------------------------------------------
-- 
-- Bowls are kind of helpers that can be drawn (at the bottom --- for now) of an
-- area, and displaying the current key prefix. It is inspired by emacs'
-- behavior, that prints prefix keys in the minibuffer after a certain time.
--
-- I call it `bowl' as a reference to the bowl that one might have at home,
-- where one puts its actual keys... A more serious name would be `hint' or
-- `tooltip' (but they do not fit well for this usage).
--
-- Example usage: see `rc.lua' file.
--
--------------------------------------------------------------------------------

--{{{ 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 capi = capi
local client = client
local awesome = awesome
local root = root
local timer = timer

local infoline = require ("infoline")
--}}}

module ("bowl")

-- 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)
local data = setmetatable ({}, { __mode = 'k' })

--{{{ Default values

--- Default modifier filter
local modfilter = {
    ["Mod1"]    = "M",
    ["Mod4"]    = "S",
    ["Control"] = "C",
    ["Shift"]   = string.upper,
}

-- Timers configuration
local use_timers = true
local timeout = 2.0

--}}}

--{{{ Keychain pretty-printing

function mod_to_string (mods, k)
    local ret, k = "", k
    for _, mod in ipairs (mods) do
        if modfilter[mod] then
            local t = type (modfilter[mod])
            if t == "function" then
                k = modfilter[mod](k)
            elseif t == "string" then
                ret = ret .. modfilter[mod] .. "-"
            else
                error ("Invalid modifier key filter: got a " .. t)
            end
        else
            ret = ret .. mod .. "-"
        end
    end
    return ret, k
end

function ks_to_string (m, k)
    local m, k = mod_to_string (m, k)
    return m .. k
end

--}}}

--{{{ Timer management

local function delete_timer_maybe (d)
    if d.timer then             -- stop and remove the timer
        d.timer:remove_signal ("timeout", d.timer_function)
        d.timer:stop ()
        d.timer = nil
        d.timer_expired = true
    end
end

local function delayed_call_maybe (d, f)
    if use_timers then
        if not d.timer_expired and not d.timer then
            -- create and start the timer
            d.timer = timer ({ timeout = timeout })
            d.timer_function = function () f (); delete_timer_maybe (d) end
            d.timer:add_signal ("timeout", d.timer_function)
            d.timer:start ()
            d.timer_expired = false
        elseif not d.timer_expired then
            -- restart the timer...

            -- XXX: What is the actual semantics of the call to `start' (ie,
            -- does it restart the timer with the initial timeout)?
            d.timer:stop ()
            d.timer.timeout = timeout -- reset timeout
            d.timer:start ()
        end
    else                        -- timers disabled
        f ()                    -- call the given function directly
    end
end

--}}}

--{{{ Infoline management

function dispose (w)
    local d = data[w]
    if d.bowl then          -- if bowl was enabled... (should always be true...)
        infoline.dispose (d.bowl)
        d.bowl = nil
    end
    delete_timer_maybe (d)
    data[w] = nil
end

function append (w, m, k)
    local d = data[w]
    local pretty_ks = ks_to_string (m, k) .. " "
    infoline.set_text (d.bowl, infoline.get_text (d.bowl) .. pretty_ks)

    local function enable_bowl ()
        -- XXX: is there a possible bad interleaving that could make
        -- this function execute while the bowl has already been
        -- disposed of? in which case the condition should be checked
        -- first...

        -- if d.bowl then
        infoline.attach (d.bowl, w)
        -- end
    end

    delayed_call_maybe (d, enable_bowl)
end

function create (w)
    -- XXX: Note the prefix text could be customizable...
    data[w] = { bowl = infoline.new (" ") }
end

--}}}


--- Initializes the bowl module, with given properties; should be called before
--- ANY other function of this module.
-- Configurations fields include:
--
-- `use_timers', `timeout': A boolean defining whether bowls drawing should be
-- delayed, along with a number being this time shift, in seconds (Default
-- values are `true' and `2').
--
-- `modfilter': A table associating modifiers (Mod1, Mod4, Control, Shift, etc.)
-- with either a string (in this case it will replace the modifier when printed
-- in heplers) or functions (in this case the key string will be repaced by a
-- call to this function with the key string as parameter). Default value is:
-- { ["Mod1"] = "M", ["Mod4"] = "S", ["Control"] = "C", ["Shift"] =
-- string.upper }
--
-- @param c The table of properties.
function init (c)
    local c = c or { }
    modfilter = c.modfilter and c.modfilter or modfilter
    if c.use_timers ~= nil then use_timers = c.use_timers end
    if use_timers then
        timeout = c.timeout ~= nil and c.timeout or timeout
    end
end

--- Setup signal listeners, that trigger appropriate functions for a default
--- behavior.
function default_setup ()
    local function to_root (f) return function (...) f (root, ...) end end
    client.add_signal ("keychain::enter", create)
    client.add_signal ("keychain::append", append)
    client.add_signal ("keychain::leave", dispose)
    awesome.add_signal ("keychain::enter", to_root (create))
    awesome.add_signal ("keychain::append", to_root (append))
    awesome.add_signal ("keychain::leave", to_root (dispose))
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
