]> git.decadent.org.uk Git - ion3.git/blob - mod_statusbar/statusbar.c
[svn-upgrade] Integrating new upstream version, ion3 (20070720)
[ion3.git] / mod_statusbar / statusbar.c
1 /*
2  * ion/mod_statusbar/statusbar.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2007. 
5  *
6  * See the included file LICENSE for details.
7  */
8
9 #include <string.h>
10 #include <limits.h>
11
12 #include <libtu/objp.h>
13 #include <libtu/minmax.h>
14 #include <libtu/ptrlist.h>
15 #include <libtu/misc.h>
16 #include <ioncore/common.h>
17 #include <ioncore/global.h>
18 #include <ioncore/window.h>
19 #include <ioncore/binding.h>
20 #include <ioncore/regbind.h>
21 #include <ioncore/event.h>
22 #include <ioncore/resize.h>
23 #include <ioncore/gr.h>
24 #include <ioncore/gr-util.h>
25 #include <ioncore/names.h>
26 #include <ioncore/strings.h>
27 #include <ioncore/basicpholder.h>
28 #include <ioncore/sizehint.h>
29
30 #include "statusbar.h"
31 #include "main.h"
32 #include "draw.h"
33
34
35 static void statusbar_set_elems(WStatusBar *sb, ExtlTab t);
36 static void statusbar_free_elems(WStatusBar *sb);
37 static void statusbar_update_natural_size(WStatusBar *p);
38 static void statusbar_arrange_systray(WStatusBar *p);
39 static int statusbar_systray_x(WStatusBar *p);
40 static void statusbar_rearrange(WStatusBar *sb, bool rs);
41 static void do_calc_systray_w(WStatusBar *p, WSBElem *el);
42 static void statusbar_calc_systray_w(WStatusBar *p);
43
44 static WStatusBar *statusbars=NULL;
45
46
47 /*{{{ Init/deinit */
48
49
50 bool statusbar_init(WStatusBar *p, WWindow *parent, const WFitParams *fp)
51 {
52     if(!window_init(&(p->wwin), parent, fp))
53         return FALSE;
54
55     p->brush=NULL;
56     p->elems=NULL;
57     p->nelems=0;
58     p->natural_w=1;
59     p->natural_h=1;
60     p->filleridx=-1;
61     p->sb_next=NULL;
62     p->sb_prev=NULL;
63     p->traywins=NULL;
64     p->systray_enabled=TRUE;
65     
66     statusbar_updategr(p);
67
68     if(p->brush==NULL){
69         window_deinit(&(p->wwin));
70         return FALSE;
71     }
72     
73     window_select_input(&(p->wwin), IONCORE_EVENTMASK_NORMAL);
74     
75     region_register((WRegion*)p);
76
77     region_add_bindmap((WRegion*)p, mod_statusbar_statusbar_bindmap);
78     
79     LINK_ITEM(statusbars, p, sb_next, sb_prev);
80     
81     return TRUE;
82 }
83
84
85
86 WStatusBar *create_statusbar(WWindow *parent, const WFitParams *fp)
87 {
88     CREATEOBJ_IMPL(WStatusBar, statusbar, (p, parent, fp));
89 }
90
91
92 void statusbar_deinit(WStatusBar *p)
93 {
94     UNLINK_ITEM(statusbars, p, sb_next, sb_prev);
95
96     statusbar_free_elems(p);
97     
98     if(p->brush!=NULL){
99         grbrush_release(p->brush);
100         p->brush=NULL;
101     }
102     
103     window_deinit(&(p->wwin));
104 }
105
106
107 /*}}}*/
108
109
110 /*{{{ Content stuff */
111
112
113 static void init_sbelem(WSBElem *el)
114 {
115     el->type=WSBELEM_NONE;
116     el->text_w=0;
117     el->text=NULL;
118     el->max_w=0;
119     el->tmpl=NULL;
120     el->meter=STRINGID_NONE;
121     el->attr=STRINGID_NONE;
122     el->stretch=0;
123     el->align=WSBELEM_ALIGN_CENTER;
124     el->zeropad=0;
125     el->x=0;
126     el->traywins=NULL;
127 }
128
129
130 static bool gets_stringstore(ExtlTab t, const char *str, StringId *id)
131 {
132     char *s;
133     
134     if(extl_table_gets_s(t, str, &s)){
135         *id=stringstore_alloc(s);
136         free(s);
137         return (*id!=STRINGID_NONE);
138     }
139     
140     return FALSE;
141 }
142
143     
144 static WSBElem *get_sbelems(ExtlTab t, int *nret, int *filleridxret)
145 {
146     int i, n=extl_table_get_n(t);
147     WSBElem *el;
148     int systrayidx=-1;
149     
150     *nret=0;
151     *filleridxret=-1;
152     
153     if(n<=0)
154         return NULL;
155     
156     el=ALLOC_N(WSBElem, n); 
157     
158     if(el==NULL)
159         return NULL;
160     
161     for(i=0; i<n; i++){
162         ExtlTab tt;
163         
164         init_sbelem(&el[i]);
165
166         if(extl_table_geti_t(t, i+1, &tt)){
167             if(extl_table_gets_i(tt, "type", &(el[i].type))){
168                 if(el[i].type==WSBELEM_TEXT || el[i].type==WSBELEM_STRETCH){
169                     extl_table_gets_s(tt, "text", &(el[i].text));
170                 }else if(el[i].type==WSBELEM_METER){
171                     gets_stringstore(tt, "meter", &(el[i].meter));
172                     extl_table_gets_s(tt, "tmpl", &(el[i].tmpl));
173                     extl_table_gets_i(tt, "align", &(el[i].align));
174                     extl_table_gets_i(tt, "zeropad", &(el[i].zeropad));
175                     el[i].zeropad=maxof(el[i].zeropad, 0);
176                 }else if(el[i].type==WSBELEM_SYSTRAY){
177                     const char *tmp;
178                     
179                     gets_stringstore(tt, "meter", &(el[i].meter));
180                     extl_table_gets_i(tt, "align", &(el[i].align));
181                     
182                     tmp=stringstore_get(el[i].meter);
183                     
184                     if(tmp==NULL || strcmp(tmp, "systray")==0)
185                         systrayidx=i;
186                 }else if(el[i].type==WSBELEM_FILLER){
187                     *filleridxret=i;
188                 }
189             }
190             extl_unref_table(tt);
191         }
192     }
193     
194     if(systrayidx==-1){
195         WSBElem *el2=REALLOC_N(el, WSBElem, n, n+1);
196         if(el2!=NULL){
197             el=el2;
198             init_sbelem(&el[n]);
199             el[n].type=WSBELEM_SYSTRAY;
200             n++;
201         }
202     }
203     
204     *nret=n;
205     
206     return el;
207 }
208     
209
210 static void free_sbelems(WSBElem *el, int n)
211 {
212     int i;
213     
214     for(i=0; i<n; i++){
215         if(el[i].text!=NULL)
216             free(el[i].text);
217         if(el[i].tmpl!=NULL)
218             free(el[i].tmpl);
219         if(el[i].meter!=STRINGID_NONE)
220             stringstore_free(el[i].meter);
221         if(el[i].attr!=STRINGID_NONE)
222             stringstore_free(el[i].attr);
223         if(el[i].traywins!=NULL)
224             ptrlist_clear(&el[i].traywins);
225     }
226     
227     free(el);
228 }
229
230
231 static void statusbar_set_elems(WStatusBar *sb, ExtlTab t)
232 {
233     statusbar_free_elems(sb);
234     
235     sb->elems=get_sbelems(t, &(sb->nelems), &(sb->filleridx));
236 }
237
238
239 static void statusbar_free_elems(WStatusBar *sb)
240 {
241     if(sb->elems!=NULL){
242         free_sbelems(sb->elems, sb->nelems);
243         sb->elems=NULL;
244         sb->nelems=0;
245         sb->filleridx=-1;
246     }
247 }
248
249
250 /*}}}*/
251
252
253
254 /*{{{ Size stuff */
255
256
257 static void statusbar_resize(WStatusBar *p)
258 {
259     WRQGeomParams rq=RQGEOMPARAMS_INIT;
260     
261     rq.flags=REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y;
262     
263     rq.geom.w=p->natural_w;
264     rq.geom.h=p->natural_h;
265     rq.geom.x=REGION_GEOM(p).x;
266     rq.geom.y=REGION_GEOM(p).y;
267
268     if(rectangle_compare(&rq.geom, &REGION_GEOM(p))!=RECTANGLE_SAME)
269         region_rqgeom((WRegion*)p, &rq, NULL);
270 }
271
272
273 static void calc_elem_w(WStatusBar *p, WSBElem *el, GrBrush *brush)
274 {
275     const char *str;
276
277     if(el->type==WSBELEM_SYSTRAY){
278         do_calc_systray_w(p, el);
279         return;
280     }
281     
282     if(brush==NULL){
283         el->text_w=0;
284         return;
285     }
286     
287     if(el->type==WSBELEM_METER){
288         str=(el->text!=NULL ? el->text : STATUSBAR_NX_STR);
289         el->text_w=grbrush_get_text_width(brush, str, strlen(str));
290         str=el->tmpl;
291         el->max_w=maxof((str!=NULL
292                          ? grbrush_get_text_width(brush, str, strlen(str))
293                          : 0),
294                         el->text_w);
295     }else{
296         str=el->text;
297         el->text_w=(str!=NULL
298                     ? grbrush_get_text_width(brush, str, strlen(str))
299                     : 0);
300         el->max_w=el->text_w;
301     }
302 }
303
304
305 static void statusbar_calc_widths(WStatusBar *sb)
306 {
307     int i;
308     
309     for(i=0; i<sb->nelems; i++)
310         calc_elem_w(sb, &(sb->elems[i]), sb->brush);
311 }
312
313
314 static void statusbar_do_update_natural_size(WStatusBar *p)
315 {
316     GrBorderWidths bdw;
317     GrFontExtents fnte;
318     WRegion *reg;
319     PtrListIterTmp tmp;
320     int totw=0, stmh=0;
321     int i;
322
323     if(p->brush==NULL){
324         bdw.left=0; bdw.right=0;
325         bdw.top=0; bdw.bottom=0;
326         fnte.max_height=4;
327     }else{
328         grbrush_get_border_widths(p->brush, &bdw);
329         grbrush_get_font_extents(p->brush, &fnte);
330     }
331     
332     for(i=0; i<p->nelems; i++)
333         totw+=p->elems[i].max_w;
334         
335     FOR_ALL_ON_PTRLIST(WRegion*, reg, p->traywins, tmp){
336         stmh=maxof(stmh, REGION_GEOM(reg).h);
337     }
338     
339     p->natural_w=bdw.left+totw+bdw.right;
340     p->natural_h=maxof(stmh, fnte.max_height)+bdw.top+bdw.bottom;
341 }
342
343
344 void statusbar_size_hints(WStatusBar *p, WSizeHints *h)
345 {
346     h->min_set=TRUE;
347     h->min_width=p->natural_w;
348     h->min_height=p->natural_h;
349     
350     h->max_set=TRUE;
351     h->max_width=INT_MAX;/*p->natural_w;*/
352     h->max_height=p->natural_h;
353 }
354
355
356 /*}}}*/
357
358
359 /*{{{ Systray */
360
361
362 static WSBElem *statusbar_associate_systray(WStatusBar *sb, WRegion *reg)
363 {
364     WClientWin *cwin=OBJ_CAST(reg, WClientWin);
365     WSBElem *el=NULL, *fbel=NULL;
366     char *name=NULL;
367     int i;
368     
369     if(cwin!=NULL)
370         extl_table_gets_s(cwin->proptab, "statusbar", &name);
371     
372     for(i=0; i<sb->nelems; i++){
373         const char *meter;
374         
375         if(sb->elems[i].type!=WSBELEM_SYSTRAY)
376             continue;
377         
378         meter=stringstore_get(sb->elems[i].meter);
379         
380         if(meter==NULL){
381             fbel=&sb->elems[i];
382             continue;
383         }
384         if(name!=NULL && strcmp(meter, name)==0){
385             el=&sb->elems[i];
386             break;
387         }
388         if(strcmp(meter, "systray")==0)
389             fbel=&sb->elems[i];
390     }
391     
392     if(name!=NULL)
393         free(name);
394         
395     if(el==NULL)
396         el=fbel;
397     
398     if(el==NULL)
399         return NULL;
400     
401     ptrlist_insert_last(&el->traywins, (Obj*)reg);
402     
403     return el;
404 }
405
406
407 static WSBElem *statusbar_unassociate_systray(WStatusBar *sb, WRegion *reg)
408 {
409     int i;
410     
411     for(i=0; i<sb->nelems; i++){
412         if(ptrlist_remove(&(sb->elems[i].traywins), (Obj*)reg))
413             return &sb->elems[i];
414     }
415     
416     return NULL;
417 }
418
419     
420
421 static void do_calc_systray_w(WStatusBar *p, WSBElem *el)
422 {
423     WRegion *reg;
424     PtrListIterTmp tmp;
425     int padding=0;
426     int w=-padding;
427     
428     FOR_ALL_ON_PTRLIST(WRegion*, reg, el->traywins, tmp){
429         w=w+REGION_GEOM(reg).w+padding;
430     }
431     
432     el->text_w=maxof(0, w);
433     el->max_w=el->text_w; /* for now */
434 }
435
436
437 static void statusbar_calc_systray_w(WStatusBar *p)
438 {
439     int i;
440     
441     for(i=0; i<p->nelems; i++){
442         if(p->elems[i].type==WSBELEM_SYSTRAY)
443             do_calc_systray_w(p, &p->elems[i]);
444     }
445 }
446
447
448 static void statusbar_arrange_systray(WStatusBar *p)
449 {
450     WRegion *reg;
451     PtrListIterTmp tmp;
452     GrBorderWidths bdw;
453     int padding=0, ymiddle;
454     int i, x;
455     
456     if(p->brush!=NULL){
457         grbrush_get_border_widths(p->brush, &bdw);
458     }else{
459         bdw.top=0;
460         bdw.bottom=0;
461     }
462     
463     ymiddle=bdw.top+(REGION_GEOM(p).h-bdw.top-bdw.bottom)/2;
464     
465     for(i=0; i<p->nelems; i++){
466         WSBElem *el=&p->elems[i];
467         if(el->type!=WSBELEM_SYSTRAY)
468             continue;
469         x=el->x;
470         FOR_ALL_ON_PTRLIST(WRegion*, reg, el->traywins, tmp){
471             WRectangle g=REGION_GEOM(reg);
472             g.x=x;
473             g.y=ymiddle-g.h/2;
474             region_fit(reg, &g, REGION_FIT_EXACT);
475             x=x+g.w+padding;
476         }
477     }
478 }
479
480
481 static void systray_adjust_size(WRegion *reg, WRectangle *g)
482 {
483     g->h=CF_STATUSBAR_SYSTRAY_HEIGHT;
484     
485     region_size_hints_correct(reg, &g->w, &g->h, TRUE);
486 }
487
488
489
490 static WRegion *statusbar_do_attach_final(WStatusBar *sb,
491                                           WRegion *reg,
492                                           void *unused)
493 {
494     WFitParams fp;
495     WSBElem *el;
496     
497     if(!ptrlist_insert_last(&sb->traywins, (Obj*)reg))
498         return NULL;
499     
500     el=statusbar_associate_systray(sb, reg);
501     if(el==NULL){
502         ptrlist_remove(&sb->traywins, (Obj*)reg);
503         return NULL;
504     }
505
506     fp.g=REGION_GEOM(reg);
507     fp.mode=REGION_FIT_EXACT;
508     systray_adjust_size(reg, &fp.g);
509     
510     region_fitrep(reg, NULL, &fp);
511     
512     do_calc_systray_w(sb, el);
513
514     region_set_manager(reg, (WRegion*)sb);
515     
516     statusbar_rearrange(sb, TRUE);
517     
518     if(REGION_IS_MAPPED(sb))
519         region_map(reg);
520     
521     return reg;
522 }
523
524
525 static WRegion *statusbar_do_attach(WStatusBar *sb, WRegionAttachData *data)
526 {
527     WFitParams fp;
528     
529     fp.g.x=0;
530     fp.g.y=0;
531     fp.g.h=CF_STATUSBAR_SYSTRAY_HEIGHT;
532     fp.g.w=CF_STATUSBAR_SYSTRAY_HEIGHT;
533     fp.mode=REGION_FIT_WHATEVER|REGION_FIT_BOUNDS;
534     
535     return region_attach_helper((WRegion*)sb, (WWindow*)sb, &fp,
536                                 (WRegionDoAttachFn*)statusbar_do_attach_final, 
537                                 NULL, data);
538 }
539
540
541 static WRegion *statusbar_attach_ph(WStatusBar *sb, int flags,
542                                     WRegionAttachData *data)
543 {
544     return statusbar_do_attach(sb, data);
545 }
546
547
548 static WPHolder *statusbar_prepare_manage(WStatusBar *sb, 
549                                           const WClientWin *cwin,
550                                           const WManageParams *param,
551                                           int priority)
552 {
553     if(!MANAGE_PRIORITY_OK(priority, MANAGE_PRIORITY_LOW))
554         return NULL;
555     
556     return (WPHolder*)create_basicpholder((WRegion*)sb, 
557                                           ((WBasicPHolderHandler*)
558                                            statusbar_attach_ph));
559 }
560
561
562 static void statusbar_managed_remove(WStatusBar *sb, WRegion *reg)
563 {
564     WSBElem *el;
565         
566     ptrlist_remove(&sb->traywins, (Obj*)reg);
567     
568     el=statusbar_unassociate_systray(sb, reg);
569     
570     region_unset_manager(reg, (WRegion*)sb);
571
572     if(el!=NULL && ioncore_g.opmode!=IONCORE_OPMODE_DEINIT){
573         do_calc_systray_w(sb, el);
574         statusbar_rearrange(sb, TRUE);
575     }
576 }
577
578
579 static void statusbar_managed_rqgeom(WStatusBar *sb, WRegion *reg, 
580                                      const WRQGeomParams *rq,
581                                      WRectangle *geomret)
582 {
583     WRectangle g;
584     
585     g.x=REGION_GEOM(reg).x;
586     g.y=REGION_GEOM(reg).y;
587     g.w=rq->geom.w;
588     g.h=rq->geom.h;
589
590     systray_adjust_size(reg, &g);
591
592     if(rq->flags&REGION_RQGEOM_TRYONLY){
593         if(geomret!=NULL)
594             *geomret=g;
595         return;
596     }
597     
598     region_fit(reg, &g, REGION_FIT_EXACT);
599     
600     statusbar_calc_systray_w(sb);
601     statusbar_rearrange(sb, TRUE);
602     
603     if(geomret!=NULL)
604         *geomret=REGION_GEOM(reg);
605     
606 }
607
608
609 void statusbar_map(WStatusBar *sb)
610 {
611     WRegion *reg;
612     PtrListIterTmp tmp;
613     
614     window_map((WWindow*)sb);
615     
616     FOR_ALL_ON_PTRLIST(WRegion*, reg, sb->traywins, tmp)
617         region_map(reg);
618 }
619
620
621 void statusbar_unmap(WStatusBar *sb)
622 {
623     WRegion *reg;
624     PtrListIterTmp tmp;
625     
626     window_unmap((WWindow*)sb);
627     
628     FOR_ALL_ON_PTRLIST(WRegion*, reg, sb->traywins, tmp)
629         region_unmap(reg);
630 }
631
632
633 bool statusbar_fitrep(WStatusBar *sb, WWindow *par, const WFitParams *fp)
634 {
635     bool wchg=(REGION_GEOM(sb).w!=fp->g.w);
636     bool hchg=(REGION_GEOM(sb).h!=fp->g.h);
637     
638     window_do_fitrep(&(sb->wwin), par, &(fp->g));
639     
640     if(wchg || hchg){
641         statusbar_calculate_xs(sb);
642         statusbar_arrange_systray(sb);
643         statusbar_draw(sb, TRUE);
644     }
645     
646     return TRUE;
647 }
648
649
650 WPHolder *statusbar_prepare_manage_transient(WStatusBar *sb, 
651                                              const WClientWin *cwin,
652                                              const WManageParams *param,
653                                              int unused)
654 {
655     WRegion *mgr=REGION_MANAGER(sb);
656     
657     if(mgr==NULL)
658         mgr=(WRegion*)region_screen_of((WRegion*)sb);
659     
660     if(mgr!=NULL)
661         return region_prepare_manage(mgr, cwin, param, 
662                                      MANAGE_PRIORITY_NONE);
663     else
664         return NULL;
665 }
666
667     
668
669 /*}}}*/
670
671
672 /*{{{ Exports */
673
674
675 static ExtlFn parse_template_fn;
676 static bool parse_template_fn_set=FALSE;
677
678
679 EXTL_EXPORT
680 void mod_statusbar__set_template_parser(ExtlFn fn)
681 {
682     if(parse_template_fn_set)
683         extl_unref_fn(parse_template_fn);
684     parse_template_fn=extl_ref_fn(fn);
685     parse_template_fn_set=TRUE;
686 }
687
688
689 /*EXTL_DOC
690  * Set statusbar template.
691  */
692 EXTL_EXPORT_MEMBER
693 void statusbar_set_template(WStatusBar *sb, const char *tmpl)
694 {
695     ExtlTab t=extl_table_none();
696     bool ok=FALSE;
697     
698     if(parse_template_fn_set){
699         extl_protect(NULL);
700         ok=extl_call(parse_template_fn, "s", "t", tmpl, &t);
701         extl_unprotect(NULL);
702     }
703
704     if(ok)
705         statusbar_set_template_table(sb, t);
706 }
707
708
709 /*EXTL_DOC
710  * Set statusbar template as table.
711  */
712 EXTL_EXPORT_MEMBER
713 void statusbar_set_template_table(WStatusBar *sb, ExtlTab t)
714 {
715     WRegion *reg;
716     PtrListIterTmp tmp;
717     
718     statusbar_set_elems(sb, t);
719
720     FOR_ALL_ON_PTRLIST(WRegion*, reg, sb->traywins, tmp){
721         statusbar_associate_systray(sb, reg);
722     }
723     
724     statusbar_calc_widths(sb);
725     statusbar_rearrange(sb, FALSE);
726 }
727
728
729 /*EXTL_DOC
730  * Get statusbar template as table.
731  */
732 EXTL_EXPORT_MEMBER
733 ExtlTab statusbar_get_template_table(WStatusBar *sb)
734 {
735     int count = sb->nelems;
736     int i;
737
738     ExtlTab t = extl_create_table();
739
740     for(i=0; i<count; i++){
741         ExtlTab tt = extl_create_table();
742         
743         extl_table_sets_i(tt, "type", sb->elems[i].type);
744         extl_table_sets_s(tt, "text", sb->elems[i].text);
745         extl_table_sets_s(tt, "meter", stringstore_get(sb->elems[i].meter));
746         extl_table_sets_s(tt, "tmpl", sb->elems[i].tmpl);
747         extl_table_sets_i(tt, "align", sb->elems[i].align);
748         extl_table_sets_i(tt, "zeropad", sb->elems[i].zeropad);
749
750         extl_table_seti_t(t, (i+1), tt);
751         extl_unref_table(tt);
752     }
753
754     return t;
755 }
756
757
758 static void reset_stretch(WStatusBar *sb)
759 {
760     int i;
761     
762     for(i=0; i<sb->nelems; i++)
763         sb->elems[i].stretch=0;
764 }
765
766
767 static void positive_stretch(WStatusBar *sb)
768 {
769     int i;
770     
771     for(i=0; i<sb->nelems; i++)
772         sb->elems[i].stretch=maxof(0, sb->elems[i].stretch);
773 }
774
775
776 static void spread_stretch(WStatusBar *sb)
777 {
778     int i, j, k;
779     int diff;
780     WSBElem *el, *lel, *rel;
781     const char *str;
782     
783     for(i=0; i<sb->nelems; i++){
784         el=&(sb->elems[i]);
785
786         if(el->type!=WSBELEM_METER && el->type!=WSBELEM_SYSTRAY)
787             continue;
788         
789         diff=el->max_w-el->text_w;
790         
791         lel=NULL;
792         rel=NULL;
793         
794         if(el->align!=WSBELEM_ALIGN_RIGHT){
795             for(j=i+1; j<sb->nelems; j++){
796                 if(sb->elems[j].type==WSBELEM_STRETCH){
797                     rel=&(sb->elems[j]);
798                     break;
799                 }
800             }
801         }
802         
803         if(el->align!=WSBELEM_ALIGN_LEFT){
804             for(k=i-1; k>=0; k--){
805                 if(sb->elems[k].type==WSBELEM_STRETCH){
806                     lel=&(sb->elems[k]);
807                     break;
808                 }
809             }
810         }
811         
812         if(rel!=NULL && lel!=NULL){
813             int l=diff/2;
814             int r=diff-l;
815             lel->stretch+=l;
816             rel->stretch+=r;
817         }else if(lel!=NULL){
818             lel->stretch+=diff;
819         }else if(rel!=NULL){
820             rel->stretch+=diff;
821         }
822     }
823 }
824
825
826 static void statusbar_rearrange(WStatusBar *sb, bool rs)
827 {
828     if(rs){
829         int onw=sb->natural_w;
830         int onh=sb->natural_h;
831         
832         statusbar_do_update_natural_size(sb);
833         
834         if(    (sb->natural_h>onh && REGION_GEOM(sb).h>=onh)
835             || (sb->natural_h<onh && REGION_GEOM(sb).h<=onh)
836             || (sb->natural_w>onw && REGION_GEOM(sb).w>=onw)
837             || (sb->natural_w<onw && REGION_GEOM(sb).w<=onw)){
838             
839             statusbar_resize(sb);
840         }
841     }
842
843     reset_stretch(sb);
844     spread_stretch(sb);
845     positive_stretch(sb);
846     statusbar_calculate_xs(sb);
847     
848     if(rs)
849         statusbar_arrange_systray(sb);
850 }
851
852
853
854 /*EXTL_DOC
855  * Set statusbar template.
856  */
857 EXTL_EXPORT_MEMBER
858 void statusbar_update(WStatusBar *sb, ExtlTab t)
859 {
860     int i;
861     WSBElem *el;
862     bool grow=FALSE;
863     
864     if(sb->brush==NULL)
865         return;
866     
867     for(i=0; i<sb->nelems; i++){
868         const char *meter;
869         
870         el=&(sb->elems[i]);
871         
872         if(el->type!=WSBELEM_METER)
873             continue;
874         
875         if(el->text!=NULL){
876             free(el->text);
877             el->text=NULL;
878         }
879
880         if(el->attr!=GRATTR_NONE){
881             stringstore_free(el->attr);
882             el->attr=GRATTR_NONE;
883         }
884         
885         meter=stringstore_get(el->meter);
886         
887         if(meter!=NULL){
888             const char *str;
889             char *attrnm;
890             
891             extl_table_gets_s(t, meter, &(el->text));
892             
893             if(el->text==NULL){
894                 str=STATUSBAR_NX_STR;
895             }else{
896                 /* Zero-pad */
897                 int l=strlen(el->text);
898                 int ml=str_len(el->text);
899                 int diff=el->zeropad-ml;
900                 if(diff>0){
901                     char *tmp=ALLOC_N(char, l+diff+1);
902                     if(tmp!=NULL){
903                         memset(tmp, '0', diff);
904                         memcpy(tmp+diff, el->text, l+1);
905                         free(el->text);
906                         el->text=tmp;
907                     }
908                 }
909                 str=el->text;
910             }
911             
912             if(el->tmpl!=NULL && el->text!=NULL){
913                 char *tmp=grbrush_make_label(sb->brush, el->text, el->max_w);
914                 if(tmp!=NULL){
915                     free(el->text);
916                     el->text=tmp;
917                     str=tmp;
918                 }
919             }
920
921             el->text_w=grbrush_get_text_width(sb->brush, str, strlen(str));
922             
923             if(el->text_w>el->max_w && el->tmpl==NULL){
924                 el->max_w=el->text_w;
925                 grow=TRUE;
926             }
927             
928             attrnm=scat(meter, "_hint");
929             if(attrnm!=NULL){
930                 char *s;
931                 if(extl_table_gets_s(t, attrnm, &s)){
932                     el->attr=stringstore_alloc(s);
933                     free(s);
934                 }
935                 free(attrnm);
936             }
937         }
938     }
939
940     statusbar_rearrange(sb, grow);
941     
942     window_draw((WWindow*)sb, FALSE);
943 }
944
945
946 /*}}}*/
947
948
949 /*{{{ Updategr */
950
951
952 void statusbar_updategr(WStatusBar *p)
953 {
954     GrBrush *nbrush;
955     
956     nbrush=gr_get_brush(p->wwin.win, region_rootwin_of((WRegion*)p),
957                         "stdisp-statusbar");
958     if(nbrush==NULL)
959         return;
960     
961     if(p->brush!=NULL)
962         grbrush_release(p->brush);
963     
964     p->brush=nbrush;
965
966     statusbar_calc_widths(p);
967     statusbar_rearrange(p, TRUE);
968     
969     window_draw(&(p->wwin), TRUE);
970 }
971
972
973 /*}}}*/
974
975
976 /*{{{ Misc */
977
978
979 int statusbar_orientation(WStatusBar *sb)
980 {
981     return REGION_ORIENTATION_HORIZONTAL;
982 }
983
984
985 /*EXTL_DOC
986  * Returns a list of all statusbars.
987  */
988 EXTL_EXPORT
989 ExtlTab mod_statusbar_statusbars()
990 {
991     ExtlTab t=extl_create_table();
992     WStatusBar *sb;
993     int i=1;
994     
995     for(sb=statusbars; sb!=NULL; sb=sb->sb_next){
996         extl_table_seti_o(t, i, (Obj*)sb);
997         i++;
998     }
999     
1000     return t;
1001 }
1002
1003
1004 WStatusBar *mod_statusbar_find_suitable(WClientWin *cwin,
1005                                         const WManageParams *param)
1006 {
1007     WStatusBar *sb;
1008
1009     for(sb=statusbars; sb!=NULL; sb=sb->sb_next){
1010         /*if(!sb->is_auto)
1011             continue;*/
1012         if(!sb->systray_enabled)
1013             continue;
1014         if(!region_same_rootwin((WRegion*)sb, (WRegion*)cwin))
1015             continue;
1016         break;
1017     }
1018
1019     return sb;
1020 }
1021
1022
1023 bool statusbar_set_systray(WStatusBar *sb, int sp)
1024 {
1025     bool set=sb->systray_enabled;
1026     bool nset=libtu_do_setparam(sp, set);
1027     
1028     sb->systray_enabled=nset;
1029     
1030     return nset;
1031 }
1032
1033
1034 /*EXTL_DOC
1035  * Enable or disable use of \var{sb} as systray.
1036  * The parameter \var{how} can be one of 
1037  * \codestr{set}, \codestr{unset}, or \codestr{toggle}.
1038  * Resulting state is returned.
1039  */
1040 EXTL_EXPORT_AS(WStatusBar, set_systray)
1041 bool statusbar_set_systray_extl(WStatusBar *sb, const char *how)
1042 {
1043     return statusbar_set_systray(sb, libtu_string_to_setparam(how));
1044 }
1045
1046
1047 /*EXTL_DOC
1048  * Is \var{sb} used as a systray?
1049  */
1050 EXTL_EXPORT_AS(WStatusBar, is_systray)
1051 bool statusbar_is_systray_extl(WStatusBar *sb)
1052 {
1053     return sb->systray_enabled;
1054 }
1055
1056
1057 /*}}}*/
1058
1059
1060 /*{{{ Load */
1061
1062
1063 WRegion *statusbar_load(WWindow *par, const WFitParams *fp, ExtlTab tab)
1064 {
1065     WStatusBar *sb=create_statusbar(par, fp);
1066
1067     if(sb!=NULL){
1068         char *tmpl=NULL;
1069         ExtlTab t=extl_table_none();
1070         if(extl_table_gets_s(tab, "template", &tmpl)){
1071             statusbar_set_template(sb, tmpl);
1072             free(tmpl);
1073         }else if(extl_table_gets_t(tab, "template_table", &t)){
1074             statusbar_set_template_table(sb, t);
1075             extl_unref_table(t);
1076         }else{
1077             const char *tmpl=TR("[ %date || load: %load ] %filler%systray");
1078             statusbar_set_template(sb, tmpl);
1079         }
1080         
1081         extl_table_gets_b(tab, "systray", &sb->systray_enabled);
1082     }
1083     
1084     return (WRegion*)sb;
1085 }
1086
1087
1088 /*}}}*/
1089
1090
1091 /*{{{ Dynamic function table and class implementation */
1092
1093
1094 static DynFunTab statusbar_dynfuntab[]={
1095     {window_draw, statusbar_draw},
1096     {region_updategr, statusbar_updategr},
1097     {region_size_hints, statusbar_size_hints},
1098     {(DynFun*)region_orientation, (DynFun*)statusbar_orientation},
1099
1100     {region_managed_rqgeom, statusbar_managed_rqgeom},
1101     {(DynFun*)region_prepare_manage, (DynFun*)statusbar_prepare_manage},
1102     {region_managed_remove, statusbar_managed_remove},
1103     
1104     {(DynFun*)region_prepare_manage_transient, 
1105      (DynFun*)statusbar_prepare_manage_transient},
1106     
1107     {region_map, statusbar_map},
1108     {region_unmap, statusbar_unmap},
1109     
1110     {(DynFun*)region_fitrep,
1111      (DynFun*)statusbar_fitrep},
1112     
1113     END_DYNFUNTAB
1114 };
1115
1116
1117 EXTL_EXPORT
1118 IMPLCLASS(WStatusBar, WWindow, statusbar_deinit, statusbar_dynfuntab);
1119
1120     
1121 /*}}}*/
1122