Much simpler release procedure due to recent improvements in project setup
[notion/jeffpc.git] / mod_statusbar / mod_statusbar.lua
blobdb91e27c487304507ee63d17fd901ec826fddad6
1 --
2 -- ion/mod_statusbar/mod_statusbar.lua
3 --
4 -- Copyright (c) Tuomo Valkonen 2004-2009.
5 --
6 -- See the included file LICENSE for details.
7 --
9 -- This is a slight abuse of the package.loaded variable perhaps, but
10 -- library-like packages should handle checking if they're loaded instead of
11 -- confusing the user with require/include differences.
12 if package.loaded["mod_statusbar"] then return end
14 if not ioncore.load_module("mod_statusbar") then
15 return
16 end
18 local mod_statusbar=_G["mod_statusbar"]
19 assert(mod_statusbar)
22 -- Meter list {{{
24 local meters={}
26 --DOC
27 -- Inform of a value.
28 function mod_statusbar.inform(name, value)
29 meters[name]=value
30 end
32 -- }}}
35 -- Template processing {{{
37 local function process_template(template, meter_f, text_f, stretch_f)
38 local st, en, b, c, r, p, tmp
40 while template~="" do
41 -- Find '%something'
42 st, en, b, r=string.find(template, '^(.-)%%(.*)')
44 if not b then
45 -- Not found
46 text_f(template)
47 break
48 else
49 if b~="" then
50 -- Add preciding text as normal text element
51 text_f(b)
52 end
53 template=r
55 -- Check for '% ' and '%%'
56 st, en, c, r=string.find(template, '^([ %%])(.*)')
58 if c then
59 if c==' ' then
60 stretch_f(c)
61 else
62 text_f(c)
63 end
64 template=r
65 else
66 -- Extract [alignment][zero padding]<meter name>
67 local pat='([<|>]?)(0*[0-9]*)([a-zA-Z0-9_]+)'
68 -- First within {...}
69 st, en, c, p, b, r=string.find(template, '^{'..pat..'}(.*)')
70 if not st then
71 -- And then without
72 st, en, c, p, b, r=string.find(template, '^'..pat..'(.*)')
73 end
74 if b then
75 meter_f(b, c, tonumber(p))
76 template=r
77 end
78 end
79 end
80 end
81 end
85 function mod_statusbar.template_to_table(template)
86 local res={}
87 local m=meters --set_date(stng, meters)
88 local aligns={["<"]=0, ["|"]=1, [">"]=2}
90 process_template(template,
91 -- meter
92 function(s, c, p)
93 if s=="filler" then
94 table.insert(res, {type=4})
95 elseif (string.find(s, "^systray$") or
96 string.find(s, "^systray_")) then
97 table.insert(res, {
98 type=5,
99 meter=s,
100 align=aligns[c],
102 else
103 table.insert(res, {
104 type=2,
105 meter=s,
106 align=aligns[c],
107 tmpl=meters[s.."_template"],
108 zeropad=p,
111 end,
112 -- text
113 function(t)
114 table.insert(res, {
115 type=1,
116 text=t,
118 end,
119 -- stretch
120 function(t)
121 table.insert(res, {
122 type=3,
123 text=t,
125 end)
126 return res
130 mod_statusbar._set_template_parser(mod_statusbar.template_to_table)
133 -- }}}
135 -- Update {{{
137 --DOC
138 -- Update statusbar contents. To be called after series
139 -- of \fnref{mod_statusbar.inform} calls.
140 function mod_statusbar.update(update_templates)
141 for _, sb in pairs(mod_statusbar.statusbars()) do
142 if update_templates then
143 local t=sb:get_template_table()
144 for _, v in pairs(t) do
145 if v.meter then
146 v.tmpl=meters[v.meter.."_template"]
149 sb:set_template_table(t)
151 sb:update(meters)
155 -- }}}
158 -- ion-statusd support {{{
160 local statusd_pid=0
162 function mod_statusbar.rcv_statusd(str)
163 local data=""
164 local updatenw=false
165 local updated=false
167 local function doline(i)
168 if i=="." then
169 mod_statusbar.update(updatenw)
170 updated=true
171 else
172 local _, _, m, v=string.find(i, "^([^:]+):%s*(.*)")
173 if m and v then
174 mod_statusbar.inform(m, v)
175 updatenw=updatenw or string.find(m, "_template")
178 return ""
181 while str do
182 updated=false
183 data=string.gsub(data..str, "([^\n]*)\n", doline)
184 str=coroutine.yield(updated)
187 ioncore.warn(TR("ion-statusd quit."))
188 statusd_pid=0
189 meters={}
190 mod_statusbar.update(updatenw)
194 function mod_statusbar.get_modules()
195 local mods={}
196 local specials={["filler"]=true, ["systray"]=true}
198 for _, sb in pairs(mod_statusbar.statusbars()) do
199 for _, item in pairs(sb:get_template_table()) do
200 if item.type==2 and not specials[item.meter] then
201 local _, _, m=string.find(item.meter, "^([^_]*)");
202 if m and m~="" then
203 mods[m]=true
209 return mods
213 function mod_statusbar.cfg_statusd(cfg)
214 if date_format_backcompat_kludge then
215 if not cfg.date then
216 cfg=table.copy(cfg, false)
217 cfg.date={date_format=date_format_backcompat_kludge}
218 elseif not cfg.date.date_format then
219 cfg=table.copy(cfg, true)
220 cfg.date.date_format=date_format_backcompat_kludge
224 --TODO: don't construct file name twice.
225 ioncore.write_savefile("cfg_statusd", cfg)
226 return ioncore.get_savefile("cfg_statusd")
230 function mod_statusbar.rcv_statusd_err(str)
231 if str then
232 io.stderr:write(str)
236 --DOC
237 -- Load modules and launch \file{ion-statusd} with configuration
238 -- table \var{cfg}. The options for each \file{ion-statusd} monitor
239 -- script should be contained in the corresponding sub-table of \var{cfg}.
240 function mod_statusbar.launch_statusd(cfg)
241 if statusd_pid>0 then
242 return
245 -- Launch tried, don't do it automatically after reading
246 -- configuration.
247 mod_statusbar.no_autolaunch=true
249 local mods=mod_statusbar.get_modules()
251 -- Load modules
252 for m in pairs(mods) do
253 if dopath("statusbar_"..m, true) then
254 mods[m]=nil
258 -- Lookup ion-statusd
259 local statusd_script="ion-statusd"
260 local statusd=ioncore.lookup_script(statusd_script)
261 if not statusd then
262 ioncore.warn(TR("Could not find %s", statusd_script))
263 return
266 local statusd_errors
267 local function initrcverr(str)
268 statusd_errors=(statusd_errors or "")..str
271 local cfg=mod_statusbar.cfg_statusd(cfg or {})
272 local params=""
273 for k in pairs(mods) do params=params.." -m "..k end
274 local cmd=statusd.." -c "..cfg..params
276 local rcv=coroutine.wrap(mod_statusbar.rcv_statusd)
277 local rcverr=mod_statusbar.rcv_statusd_err
279 statusd_pid=mod_statusbar._launch_statusd(cmd,
280 rcv, initrcverr,
281 rcv, rcverr)
283 if statusd_errors then
284 warn(TR("Errors starting ion-statusd:\n")..statusd_errors)
287 if statusd_pid<=0 then
288 warn(TR("Failed to start ion-statusd."))
289 end
292 --}}}
295 -- Initialisation and default settings {{{
297 --DOC
298 -- Create a statusbar. The possible parameters in the
299 -- table \var{param} are:
301 -- \begin{tabularx}{\linewidth}{llX}
302 -- Variable & Type & Description \\
303 -- \var{template} & string & The template; see
304 -- Section \ref{sec:statusbar}. \\
305 -- \var{pos} & string & Position: \codestr{tl}, \codestr{tr},
306 -- \codestr{bl} or \codestr{br}
307 -- (for the obvious combinations of
308 -- top/left/bottom/right). \\
309 -- \var{screen} & integer & Screen number to create the statusbar on. \\
310 -- \var{fullsize} & boolean & If set, the statusbar will waste
311 -- space instead of adapting to layout. \\
312 -- \var{systray} & boolaen & Swallow (KDE protocol) systray icons. \\
313 -- \end{tabularx}
315 function mod_statusbar.create(param)
316 local scr=ioncore.find_screen_id(param.screen or 0)
317 if not scr then
318 error(TR("Screen not found."))
321 if not param.force then
322 local stdisp=scr:get_stdisp()
323 if stdisp and stdisp.reg then
324 error(TR("Screen already has an stdisp. Refusing to create a "..
325 "statusbar."))
329 local sb=scr:set_stdisp({
330 type="WStatusBar",
331 pos=(param.pos or "bl"),
332 fullsize=param.fullsize,
333 name="*statusbar*",
334 template=param.template,
335 template_table=param.template_table,
336 systray=param.systray,
339 if not sb then
340 error(TR("Failed to create statusbar."))
343 return sb
346 -- }}}
349 -- Mark ourselves loaded.
350 package.loaded["mod_statusbar"]=true
353 -- Load user configuration file
354 dopath('cfg_statusbar', true)
356 -- Launch statusd if the user didn't launch it.
357 if not mod_statusbar.no_autolaunch then
358 mod_statusbar.launch_statusd()