]> git.decadent.org.uk Git - ion3.git/blob - mod_statusbar/mod_statusbar.lua
[svn-upgrade] Integrating new upstream version, ion3 (20070203)
[ion3.git] / mod_statusbar / mod_statusbar.lua
1 --
2 -- ion/mod_statusbar/mod_statusbar.lua
3 -- 
4 -- Copyright (c) Tuomo Valkonen 2004-2007.
5 --
6 -- Ion is free software; you can redistribute it and/or modify it under
7 -- the terms of the GNU Lesser General Public License as published by
8 -- the Free Software Foundation; either version 2.1 of the License, or
9 -- (at your option) any later version.
10 --
11
12 -- This is a slight abuse of the package.loaded variable perhaps, but
13 -- library-like packages should handle checking if they're loaded instead of
14 -- confusing the user with require/include differences.
15 if package.loaded["mod_statusbar"] then return end
16
17 if not ioncore.load_module("mod_statusbar") then
18     return
19 end
20
21 local mod_statusbar=_G["mod_statusbar"]
22 assert(mod_statusbar)
23
24
25 -- Meter list {{{
26
27 local meters={}
28
29 --DOC
30 -- Inform of a value.
31 function mod_statusbar.inform(name, value)
32     meters[name]=value
33 end
34
35 -- }}}
36
37
38 -- Template processing {{{
39
40 local function process_template(template, meter_f, text_f, stretch_f)
41     local st, en, b, c, r, p, tmp
42     
43     while template~="" do
44         -- Find '%something'
45         st, en, b, r=string.find(template, '^(.-)%%(.*)')
46     
47         if not b then
48             -- Not found
49             text_f(template)
50             break
51         else
52             if b~="" then
53                 -- Add preciding text as normal text element
54                 text_f(b)
55             end
56             template=r
57
58             -- Check for '% ' and '%%'
59             st, en, c, r=string.find(template, '^([ %%])(.*)')
60
61             if c then
62                 if c==' ' then
63                     stretch_f(c)
64                 else
65                     text_f(c)
66                 end
67                 template=r
68             else 
69                 -- Extract [alignment][zero padding]<meter name>
70                 local pat='([<|>]?)(0*[0-9]*)([a-zA-Z0-9_]+)'
71                 -- First within {...}
72                 st, en, c, p, b, r=string.find(template, '^{'..pat..'}(.*)')
73                 if not st then
74                     -- And then without
75                     st, en, c, p, b, r=string.find(template, '^'..pat..'(.*)')
76                 end
77                 if b then
78                     meter_f(b, c, tonumber(p))
79                     template=r
80                 end
81             end
82         end
83     end
84 end
85
86
87
88 function mod_statusbar.template_to_table(template)
89     local res={}
90     local m=meters --set_date(stng, meters)
91     local aligns={["<"]=0, ["|"]=1, [">"]=2}
92     
93     process_template(template,
94                      -- meter
95                      function(s, c, p)
96                          if s=="filler" then
97                              table.insert(res, {type=4})
98                          elseif (string.find(s, "^systray$") or
99                                  string.find(s, "^systray_")) then
100                              table.insert(res, {
101                                  type=5,
102                                  meter=s,
103                                  align=aligns[c],
104                              })
105                          else
106                              table.insert(res, {
107                                  type=2,
108                                  meter=s,
109                                  align=aligns[c],
110                                  tmpl=meters[s.."_template"],
111                                  zeropad=p,
112                              })
113                          end
114                      end,
115                      -- text
116                      function(t)
117                          table.insert(res, {
118                              type=1,
119                              text=t,
120                          })
121                      end,
122                      -- stretch
123                      function(t)
124                          table.insert(res, {
125                              type=3,
126                              text=t,
127                          })
128                      end)
129     return res
130 end
131
132
133 mod_statusbar._set_template_parser(mod_statusbar.template_to_table)
134
135
136 -- }}}
137
138 -- Update {{{
139
140 --DOC
141 -- Update statusbar contents. To be called after series
142 -- of \fnref{mod_statusbar.inform} calls.
143 function mod_statusbar.update(update_templates)
144     for _, sb in pairs(mod_statusbar.statusbars()) do
145         if update_templates then
146             local t=sb:get_template_table()
147             for _, v in pairs(t) do
148                 if v.meter then
149                     v.tmpl=meters[v.meter.."_template"]
150                 end
151             end
152             sb:set_template_table(t)
153         end
154         sb:update(meters)
155     end
156 end
157
158 -- }}}
159
160
161 -- ion-statusd support {{{
162
163 local statusd_pid=0
164
165 function mod_statusbar.rcv_statusd(str)
166     local data=""
167     local updatenw=false
168     local updated=false
169
170     local function doline(i)
171         if i=="." then
172             mod_statusbar.update(updatenw)
173             updated=true
174         else
175             local _, _, m, v=string.find(i, "^([^:]+):%s*(.*)")
176             if m and v then
177                 mod_statusbar.inform(m, v)
178                 updatenw=updatenw or string.find(m, "_template")
179             end
180         end
181         return ""
182     end
183     
184     while str do
185         updated=false
186         data=string.gsub(data..str, "([^\n]*)\n", doline)
187         str=coroutine.yield(updated)
188     end
189     
190     ioncore.warn(TR("ion-statusd quit."))
191     statusd_pid=0
192     meters={}
193     mod_statusbar.update(updatenw)
194 end
195
196
197 local function get_modules()
198     local mods={}
199     local specials={["filler"]=true, ["systray"]=true}
200     
201     for _, sb in pairs(mod_statusbar.statusbars()) do
202         for _, item in pairs(sb:get_template_table()) do
203             if item.type==2 and not specials[item.meter] then
204                 local _, _, m=string.find(item.meter, "^([^_]*)");
205                 if m and m~="" then
206                     mods[m]=true
207                 end
208             end
209         end
210     end
211     
212     return mods
213 end
214
215
216 function mod_statusbar.cfg_statusd(cfg)
217     if date_format_backcompat_kludge then
218         if not cfg.date then
219             cfg=table.copy(cfg, false)
220             cfg.date={date_format=date_format_backcompat_kludge}
221         elseif not cfg.date.date_format then
222             cfg=table.copy(cfg, true)
223             cfg.date.date_format=date_format_backcompat_kludge
224         end
225     end
226
227     --TODO: don't construct file name twice.
228     ioncore.write_savefile("cfg_statusd", cfg)
229     return ioncore.get_savefile("cfg_statusd")
230 end
231
232
233 function mod_statusbar.rcv_statusd_err(str)
234     if str then
235         io.stderr:write(str)
236     end
237 end
238
239
240 --DOC
241 -- Load modules and launch ion-statusd with configuration table \var{cfg}.
242 function mod_statusbar.launch_statusd(cfg)
243     if statusd_pid>0 then
244         return
245     end
246     
247     local mods=get_modules()
248     
249     -- Load modules
250     for m in pairs(mods) do
251         if dopath("statusbar_"..m, true) then
252             mods[m]=nil
253         end
254     end
255
256     -- Lookup ion-statusd
257     local statusd=ioncore.lookup_script("ion-statusd")
258     if not statusd then
259         ioncore.warn(TR("Could not find %s", script))
260         return
261     end
262
263     local statusd_errors
264     local function initrcverr(str)
265         statusd_errors=(statusd_errors or "")..str
266     end
267
268     local cfg=mod_statusbar.cfg_statusd(cfg or {})
269     local params=""
270     table.foreach(mods, function(k) params=params.." -m "..k end)
271     local cmd=statusd.." -q -c "..cfg..params
272     
273     local rcv=coroutine.wrap(mod_statusbar.rcv_statusd)
274     local rcverr=mod_statusbar.rcv_statusd_err
275     
276     statusd_pid=mod_statusbar._launch_statusd(cmd, 
277                                               rcv, initrcverr, 
278                                               rcv, rcverr)
279
280     if statusd_errors then
281         warn(TR("Errors starting ion-statusd:\n")..statusd_errors)
282     end
283
284     if statusd_pid<=0 then
285         warn(TR("Failed to start ion-statusd."))
286     end                                              
287 end
288
289 --}}}
290
291
292 -- Initialisation and default settings {{{
293
294 --DOC
295 -- Create a statusbar.
296 function mod_statusbar.create(param)
297     local scr=ioncore.find_screen_id(param.screen or 0)
298     if not scr then
299         error(TR("Screen not found."))
300     end
301     
302     if not param.force then
303         local stdisp=scr:get_stdisp()
304         if stdisp and stdisp.reg then
305             error(TR("Screen already has an stdisp. Refusing to create a "..
306                      "statusbar."))
307         end
308     end
309     
310     local sb=scr:set_stdisp({
311         type="WStatusBar", 
312         pos=(param.pos or "bl"),
313         fullsize=param.fullsize,
314         name="*statusbar*",
315         template=param.template,
316         template_table=param.template_table,
317         systray=param.systray,
318     })
319     
320     if not sb then
321         error(TR("Failed to create statusbar."))
322     end
323     
324     return sb
325 end
326
327 -- }}}
328
329
330 -- Mark ourselves loaded.
331 package.loaded["mod_statusbar"]=true
332
333
334 -- Load user configuration file
335 dopath('cfg_statusbar', true)
336
337 -- Launch statusd if the user didn't launch it.
338 if statusd_pid<=0 then
339     mod_statusbar.launch_statusd()
340 end