Bug correcton in links module.
[berthome:berthome.git] / bowl.lua
1 -- -*- coding: utf-8 -*-
2 --------------------------------------------------------------------------------
3 -- @author Nicolas Berthier <nberthier@gmail.com>
4 -- @copyright 2010 Nicolas Berthier
5 --------------------------------------------------------------------------------
6 -- 
7 -- Bowls are kind of helpers that can be drawn (at the bottom --- for now) of an
8 -- area, and displaying the current key prefix. It is inspired by emacs'
9 -- behavior, that prints prefix keys in the minibuffer after a certain time.
10 --
11 -- I call it `bowl' as a reference to the bowl that one might have at home,
12 -- where one puts its actual keys... A more serious name would be `hint' or
13 -- `tooltip' (but they do not fit well for this usage).
14 --
15 -- Example usage: see `rc.lua' file.
16 --
17 --------------------------------------------------------------------------------
18
19 --{{{ Grab environment (mostly aliases)
20 local setmetatable = setmetatable
21 local ipairs = ipairs
22 local type = type
23 local pairs = pairs
24 local string = string
25 local print = print
26 local error = error
27
28 local capi = capi
29 local client = client
30 local awesome = awesome
31 local root = root
32 local timer = timer
33
34 local infoline = require ("infoline")
35 --}}}
36
37 module ("bowl")
38
39 -- Privata data: we use weak keys in order to allow collection of private data
40 -- if keys (clients) are collected (i.e., no longer used, after having been
41 -- killed for instance)
42 local data = setmetatable ({}, { __mode = 'k' })
43
44 --{{{ Default values
45
46 --- Default modifier filter
47 local modfilter = {
48     ["Mod1"]    = "M",
49     ["Mod4"]    = "S",
50     ["Control"] = "C",
51     ["Shift"]   = string.upper,
52 }
53
54 -- Timers configuration
55 local use_timers = true
56 local timeout = 2.0
57
58 --}}}
59
60 --{{{ Keychain pretty-printing
61
62 function mod_to_string (mods, k)
63     local ret, k = "", k
64     for _, mod in ipairs (mods) do
65         if modfilter[mod] then
66             local t = type (modfilter[mod])
67             if t == "function" then
68                 k = modfilter[mod](k)
69             elseif t == "string" then
70                 ret = ret .. modfilter[mod] .. "-"
71             else
72                 error ("Invalid modifier key filter: got a " .. t)
73             end
74         else
75             ret = ret .. mod .. "-"
76         end
77     end
78     return ret, k
79 end
80
81 function ks_to_string (m, k)
82     local m, k = mod_to_string (m, k)
83     return m .. k
84 end
85
86 --}}}
87
88 --{{{ Timer management
89
90 local function delete_timer_maybe (d)
91     if d.timer then             -- stop and remove the timer
92         d.timer:remove_signal ("timeout", d.timer_function)
93         d.timer:stop ()
94         d.timer = nil
95         d.timer_expired = true
96     end
97 end
98
99 local function delayed_call_maybe (d, f)
100     if use_timers then
101         if not d.timer_expired and not d.timer then
102             -- create and start the timer
103             d.timer = timer ({ timeout = timeout })
104             d.timer_function = function () f (); delete_timer_maybe (d) end
105             d.timer:add_signal ("timeout", d.timer_function)
106             d.timer:start ()
107             d.timer_expired = false
108         elseif not d.timer_expired then
109             -- restart the timer...
110
111             -- XXX: What is the actual semantics of the call to `start' (ie,
112             -- does it restart the timer with the initial timeout)?
113             d.timer:stop ()
114             d.timer.timeout = timeout -- reset timeout
115             d.timer:start ()
116         end
117     else                        -- timers disabled
118         f ()                    -- call the given function directly
119     end
120 end
121
122 --}}}
123
124 --{{{ Infoline management
125
126 function dispose (w)
127     local d = data[w]
128     if d.bowl then          -- if bowl was enabled... (should always be true...)
129         infoline.dispose (d.bowl)
130         d.bowl = nil
131     end
132     delete_timer_maybe (d)
133     data[w] = nil
134 end
135
136 function append (w, m, k)
137     local d = data[w]
138     local pretty_ks = ks_to_string (m, k) .. " "
139     infoline.set_text (d.bowl, infoline.get_text (d.bowl) .. pretty_ks)
140
141     local function enable_bowl ()
142         -- XXX: is there a possible bad interleaving that could make
143         -- this function execute while the bowl has already been
144         -- disposed of? in which case the condition should be checked
145         -- first...
146
147         -- if d.bowl then
148         infoline.attach (d.bowl, w)
149         -- end
150     end
151
152     delayed_call_maybe (d, enable_bowl)
153 end
154
155 function create (w)
156     -- XXX: Note the prefix text could be customizable...
157     data[w] = { bowl = infoline.new (" ") }
158 end
159
160 --}}}
161
162
163 --- Initializes the bowl module, with given properties; should be called before
164 --- ANY other function of this module.
165 -- Configurations fields include:
166 --
167 -- `use_timers', `timeout': A boolean defining whether bowls drawing should be
168 -- delayed, along with a number being this time shift, in seconds (Default
169 -- values are `true' and `2').
170 --
171 -- `modfilter': A table associating modifiers (Mod1, Mod4, Control, Shift, etc.)
172 -- with either a string (in this case it will replace the modifier when printed
173 -- in heplers) or functions (in this case the key string will be repaced by a
174 -- call to this function with the key string as parameter). Default value is:
175 -- { ["Mod1"] = "M", ["Mod4"] = "S", ["Control"] = "C", ["Shift"] =
176 -- string.upper }
177 --
178 -- @param c The table of properties.
179 function init (c)
180     local c = c or { }
181     modfilter = c.modfilter and c.modfilter or modfilter
182     if c.use_timers ~= nil then use_timers = c.use_timers end
183     if use_timers then
184         timeout = c.timeout ~= nil and c.timeout or timeout
185     end
186 end
187
188 --- Setup signal listeners, that trigger appropriate functions for a default
189 --- behavior.
190 function default_setup ()
191     local function to_root (f) return function (...) f (root, ...) end end
192     client.add_signal ("keychain::enter", create)
193     client.add_signal ("keychain::append", append)
194     client.add_signal ("keychain::leave", dispose)
195     awesome.add_signal ("keychain::enter", to_root (create))
196     awesome.add_signal ("keychain::append", to_root (append))
197     awesome.add_signal ("keychain::leave", to_root (dispose))
198 end
199
200 -- Local variables:
201 -- indent-tabs-mode: nil
202 -- fill-column: 80
203 -- lua-indent-level: 4
204 -- End:
205 -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80