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