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