2 * ion/mod_statusbar/statusbar.c
4 * Copyright (c) Tuomo Valkonen 1999-2007.
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.
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>
33 #include "statusbar.h"
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);
47 static WStatusBar *statusbars=NULL;
53 bool statusbar_init(WStatusBar *p, WWindow *parent, const WFitParams *fp)
55 if(!window_init(&(p->wwin), parent, fp))
67 p->systray_enabled=TRUE;
69 statusbar_updategr(p);
72 window_deinit(&(p->wwin));
76 window_select_input(&(p->wwin), IONCORE_EVENTMASK_NORMAL);
78 region_register((WRegion*)p);
80 region_add_bindmap((WRegion*)p, mod_statusbar_statusbar_bindmap);
82 ((WRegion*)p)->flags|=REGION_SKIP_FOCUS;
84 LINK_ITEM(statusbars, p, sb_next, sb_prev);
91 WStatusBar *create_statusbar(WWindow *parent, const WFitParams *fp)
93 CREATEOBJ_IMPL(WStatusBar, statusbar, (p, parent, fp));
97 void statusbar_deinit(WStatusBar *p)
99 UNLINK_ITEM(statusbars, p, sb_next, sb_prev);
101 statusbar_free_elems(p);
104 grbrush_release(p->brush);
108 window_deinit(&(p->wwin));
115 /*{{{ Content stuff */
118 static void init_sbelem(WSBElem *el)
120 el->type=WSBELEM_NONE;
125 el->meter=STRINGID_NONE;
126 el->attr=STRINGID_NONE;
128 el->align=WSBELEM_ALIGN_CENTER;
135 static bool gets_stringstore(ExtlTab t, const char *str, StringId *id)
139 if(extl_table_gets_s(t, str, &s)){
140 *id=stringstore_alloc(s);
142 return (*id!=STRINGID_NONE);
149 static WSBElem *get_sbelems(ExtlTab t, int *nret, int *filleridxret)
151 int i, n=extl_table_get_n(t);
161 el=ALLOC_N(WSBElem, n);
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){
184 gets_stringstore(tt, "meter", &(el[i].meter));
185 extl_table_gets_i(tt, "align", &(el[i].align));
187 tmp=stringstore_get(el[i].meter);
189 if(tmp==NULL || strcmp(tmp, "systray")==0)
191 }else if(el[i].type==WSBELEM_FILLER){
195 extl_unref_table(tt);
200 WSBElem *el2=REALLOC_N(el, WSBElem, n, n+1);
204 el[n].type=WSBELEM_SYSTRAY;
215 static void free_sbelems(WSBElem *el, int n)
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);
236 static void statusbar_set_elems(WStatusBar *sb, ExtlTab t)
238 statusbar_free_elems(sb);
240 sb->elems=get_sbelems(t, &(sb->nelems), &(sb->filleridx));
244 static void statusbar_free_elems(WStatusBar *sb)
247 free_sbelems(sb->elems, sb->nelems);
262 static void statusbar_resize(WStatusBar *p)
264 WRQGeomParams rq=RQGEOMPARAMS_INIT;
266 rq.flags=REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y;
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;
273 if(rectangle_compare(&rq.geom, ®ION_GEOM(p))!=RECTANGLE_SAME)
274 region_rqgeom((WRegion*)p, &rq, NULL);
278 static void calc_elem_w(WStatusBar *p, WSBElem *el, GrBrush *brush)
282 if(el->type==WSBELEM_SYSTRAY){
283 do_calc_systray_w(p, el);
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));
296 el->max_w=maxof((str!=NULL
297 ? grbrush_get_text_width(brush, str, strlen(str))
302 el->text_w=(str!=NULL
303 ? grbrush_get_text_width(brush, str, strlen(str))
305 el->max_w=el->text_w;
310 static void statusbar_calc_widths(WStatusBar *sb)
314 for(i=0; i<sb->nelems; i++)
315 calc_elem_w(sb, &(sb->elems[i]), sb->brush);
319 static void statusbar_do_update_natural_size(WStatusBar *p)
329 bdw.left=0; bdw.right=0;
330 bdw.top=0; bdw.bottom=0;
333 grbrush_get_border_widths(p->brush, &bdw);
334 grbrush_get_font_extents(p->brush, &fnte);
337 for(i=0; i<p->nelems; i++)
338 totw+=p->elems[i].max_w;
340 FOR_ALL_ON_PTRLIST(WRegion*, reg, p->traywins, tmp){
341 stmh=maxof(stmh, REGION_GEOM(reg).h);
344 p->natural_w=bdw.left+totw+bdw.right;
345 p->natural_h=maxof(stmh, fnte.max_height)+bdw.top+bdw.bottom;
349 void statusbar_size_hints(WStatusBar *p, WSizeHints *h)
352 h->min_width=p->natural_w;
353 h->min_height=p->natural_h;
356 h->max_width=INT_MAX;/*p->natural_w;*/
357 h->max_height=p->natural_h;
367 static WSBElem *statusbar_associate_systray(WStatusBar *sb, WRegion *reg)
369 WClientWin *cwin=OBJ_CAST(reg, WClientWin);
370 WSBElem *el=NULL, *fbel=NULL;
375 extl_table_gets_s(cwin->proptab, "statusbar", &name);
377 for(i=0; i<sb->nelems; i++){
380 if(sb->elems[i].type!=WSBELEM_SYSTRAY)
383 meter=stringstore_get(sb->elems[i].meter);
389 if(name!=NULL && strcmp(meter, name)==0){
393 if(strcmp(meter, "systray")==0)
406 ptrlist_insert_last(&el->traywins, (Obj*)reg);
412 static WSBElem *statusbar_unassociate_systray(WStatusBar *sb, WRegion *reg)
416 for(i=0; i<sb->nelems; i++){
417 if(ptrlist_remove(&(sb->elems[i].traywins), (Obj*)reg))
418 return &sb->elems[i];
426 static void do_calc_systray_w(WStatusBar *p, WSBElem *el)
433 FOR_ALL_ON_PTRLIST(WRegion*, reg, el->traywins, tmp){
434 w=w+REGION_GEOM(reg).w+padding;
437 el->text_w=maxof(0, w);
438 el->max_w=el->text_w; /* for now */
442 static void statusbar_calc_systray_w(WStatusBar *p)
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]);
453 static void statusbar_arrange_systray(WStatusBar *p)
458 int padding=0, ymiddle;
462 grbrush_get_border_widths(p->brush, &bdw);
468 ymiddle=bdw.top+(REGION_GEOM(p).h-bdw.top-bdw.bottom)/2;
470 for(i=0; i<p->nelems; i++){
471 WSBElem *el=&p->elems[i];
472 if(el->type!=WSBELEM_SYSTRAY)
475 FOR_ALL_ON_PTRLIST(WRegion*, reg, el->traywins, tmp){
476 WRectangle g=REGION_GEOM(reg);
479 region_fit(reg, &g, REGION_FIT_EXACT);
486 static void systray_adjust_size(WRegion *reg, WRectangle *g)
488 g->h=CF_STATUSBAR_SYSTRAY_HEIGHT;
490 region_size_hints_correct(reg, &g->w, &g->h, TRUE);
495 static WRegion *statusbar_do_attach_final(WStatusBar *sb,
502 if(!ptrlist_insert_last(&sb->traywins, (Obj*)reg))
505 el=statusbar_associate_systray(sb, reg);
507 ptrlist_remove(&sb->traywins, (Obj*)reg);
511 fp.g=REGION_GEOM(reg);
512 fp.mode=REGION_FIT_EXACT;
513 systray_adjust_size(reg, &fp.g);
515 region_fitrep(reg, NULL, &fp);
517 do_calc_systray_w(sb, el);
519 region_set_manager(reg, (WRegion*)sb);
521 statusbar_rearrange(sb, TRUE);
523 if(REGION_IS_MAPPED(sb))
530 static WRegion *statusbar_do_attach(WStatusBar *sb, WRegionAttachData *data)
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;
540 return region_attach_helper((WRegion*)sb, (WWindow*)sb, &fp,
541 (WRegionDoAttachFn*)statusbar_do_attach_final,
546 static WRegion *statusbar_attach_ph(WStatusBar *sb, int flags,
547 WRegionAttachData *data)
549 return statusbar_do_attach(sb, data);
553 static WPHolder *statusbar_prepare_manage(WStatusBar *sb,
554 const WClientWin *cwin,
555 const WManageParams *param,
558 if(redir==MANAGE_REDIR_STRICT_YES)
561 return (WPHolder*)create_basicpholder((WRegion*)sb,
562 ((WBasicPHolderHandler*)
563 statusbar_attach_ph));
567 static void statusbar_managed_remove(WStatusBar *sb, WRegion *reg)
571 ptrlist_remove(&sb->traywins, (Obj*)reg);
573 el=statusbar_unassociate_systray(sb, reg);
575 region_unset_manager(reg, (WRegion*)sb);
577 if(el!=NULL && ioncore_g.opmode!=IONCORE_OPMODE_DEINIT){
578 do_calc_systray_w(sb, el);
579 statusbar_rearrange(sb, TRUE);
584 static void statusbar_managed_rqgeom(WStatusBar *sb, WRegion *reg,
585 const WRQGeomParams *rq,
590 g.x=REGION_GEOM(reg).x;
591 g.y=REGION_GEOM(reg).y;
595 systray_adjust_size(reg, &g);
597 if(rq->flags®ION_RQGEOM_TRYONLY){
603 region_fit(reg, &g, REGION_FIT_EXACT);
605 statusbar_calc_systray_w(sb);
606 statusbar_rearrange(sb, TRUE);
609 *geomret=REGION_GEOM(reg);
614 void statusbar_map(WStatusBar *sb)
619 window_map((WWindow*)sb);
621 FOR_ALL_ON_PTRLIST(WRegion*, reg, sb->traywins, tmp)
626 void statusbar_unmap(WStatusBar *sb)
631 window_unmap((WWindow*)sb);
633 FOR_ALL_ON_PTRLIST(WRegion*, reg, sb->traywins, tmp)
638 bool statusbar_fitrep(WStatusBar *sb, WWindow *par, const WFitParams *fp)
640 bool wchg=(REGION_GEOM(sb).w!=fp->g.w);
641 bool hchg=(REGION_GEOM(sb).h!=fp->g.h);
643 window_do_fitrep(&(sb->wwin), par, &(fp->g));
646 statusbar_calculate_xs(sb);
647 statusbar_arrange_systray(sb);
648 statusbar_draw(sb, TRUE);
655 WPHolder *statusbar_prepare_manage_transient(WStatusBar *sb,
656 const WClientWin *cwin,
657 const WManageParams *param,
660 WRegion *mgr=REGION_MANAGER(sb);
663 mgr=(WRegion*)region_screen_of((WRegion*)sb);
666 return region_prepare_manage(mgr, cwin, param,
667 MANAGE_REDIR_PREFER_NO);
680 static ExtlFn parse_template_fn;
681 static bool parse_template_fn_set=FALSE;
685 void mod_statusbar__set_template_parser(ExtlFn fn)
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;
695 * Set statusbar template.
698 void statusbar_set_template(WStatusBar *sb, const char *tmpl)
700 ExtlTab t=extl_table_none();
703 if(parse_template_fn_set){
705 ok=extl_call(parse_template_fn, "s", "t", tmpl, &t);
706 extl_unprotect(NULL);
710 statusbar_set_template_table(sb, t);
715 * Set statusbar template as table.
718 void statusbar_set_template_table(WStatusBar *sb, ExtlTab t)
723 statusbar_set_elems(sb, t);
725 FOR_ALL_ON_PTRLIST(WRegion*, reg, sb->traywins, tmp){
726 statusbar_associate_systray(sb, reg);
729 statusbar_calc_widths(sb);
730 statusbar_rearrange(sb, FALSE);
735 * Get statusbar template as table.
738 ExtlTab statusbar_get_template_table(WStatusBar *sb)
740 int count = sb->nelems;
743 ExtlTab t = extl_create_table();
745 for(i=0; i<count; i++){
746 ExtlTab tt = extl_create_table();
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);
755 extl_table_seti_t(t, (i+1), tt);
756 extl_unref_table(tt);
763 static void reset_stretch(WStatusBar *sb)
767 for(i=0; i<sb->nelems; i++)
768 sb->elems[i].stretch=0;
772 static void positive_stretch(WStatusBar *sb)
776 for(i=0; i<sb->nelems; i++)
777 sb->elems[i].stretch=maxof(0, sb->elems[i].stretch);
781 static void spread_stretch(WStatusBar *sb)
785 WSBElem *el, *lel, *rel;
788 for(i=0; i<sb->nelems; i++){
791 if(el->type!=WSBELEM_METER && el->type!=WSBELEM_SYSTRAY)
794 diff=el->max_w-el->text_w;
799 if(el->align!=WSBELEM_ALIGN_RIGHT){
800 for(j=i+1; j<sb->nelems; j++){
801 if(sb->elems[j].type==WSBELEM_STRETCH){
808 if(el->align!=WSBELEM_ALIGN_LEFT){
809 for(k=i-1; k>=0; k--){
810 if(sb->elems[k].type==WSBELEM_STRETCH){
817 if(rel!=NULL && lel!=NULL){
831 static void statusbar_rearrange(WStatusBar *sb, bool rs)
834 int onw=sb->natural_w;
835 int onh=sb->natural_h;
837 statusbar_do_update_natural_size(sb);
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)){
844 statusbar_resize(sb);
850 positive_stretch(sb);
851 statusbar_calculate_xs(sb);
854 statusbar_arrange_systray(sb);
860 * Set statusbar template.
863 void statusbar_update(WStatusBar *sb, ExtlTab t)
872 for(i=0; i<sb->nelems; i++){
877 if(el->type!=WSBELEM_METER)
885 if(el->attr!=GRATTR_NONE){
886 stringstore_free(el->attr);
887 el->attr=GRATTR_NONE;
890 meter=stringstore_get(el->meter);
896 extl_table_gets_s(t, meter, &(el->text));
899 str=STATUSBAR_NX_STR;
902 int l=strlen(el->text);
903 int ml=str_len(el->text);
904 int diff=el->zeropad-ml;
906 char *tmp=ALLOC_N(char, l+diff+1);
908 memset(tmp, '0', diff);
909 memcpy(tmp+diff, el->text, l+1);
917 if(el->tmpl!=NULL && el->text!=NULL){
918 char *tmp=grbrush_make_label(sb->brush, el->text, el->max_w);
926 el->text_w=grbrush_get_text_width(sb->brush, str, strlen(str));
928 if(el->text_w>el->max_w && el->tmpl==NULL){
929 el->max_w=el->text_w;
933 attrnm=scat(meter, "_hint");
936 if(extl_table_gets_s(t, attrnm, &s)){
937 el->attr=stringstore_alloc(s);
945 statusbar_rearrange(sb, grow);
947 window_draw((WWindow*)sb, FALSE);
957 void statusbar_updategr(WStatusBar *p)
961 nbrush=gr_get_brush(p->wwin.win, region_rootwin_of((WRegion*)p),
967 grbrush_release(p->brush);
971 statusbar_calc_widths(p);
972 statusbar_rearrange(p, TRUE);
974 window_draw(&(p->wwin), TRUE);
984 int statusbar_orientation(WStatusBar *sb)
986 return REGION_ORIENTATION_HORIZONTAL;
991 * Returns a list of all statusbars.
994 ExtlTab mod_statusbar_statusbars()
996 ExtlTab t=extl_create_table();
1000 for(sb=statusbars; sb!=NULL; sb=sb->sb_next){
1001 extl_table_seti_o(t, i, (Obj*)sb);
1009 WStatusBar *mod_statusbar_find_suitable(WClientWin *cwin,
1010 const WManageParams *param)
1014 for(sb=statusbars; sb!=NULL; sb=sb->sb_next){
1017 if(!sb->systray_enabled)
1019 if(!region_same_rootwin((WRegion*)sb, (WRegion*)cwin))
1028 bool statusbar_set_systray(WStatusBar *sb, int sp)
1030 bool set=sb->systray_enabled;
1031 bool nset=libtu_do_setparam(sp, set);
1033 sb->systray_enabled=nset;
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.
1044 EXTL_EXPORT_AS(WStatusBar, set_systray)
1045 bool statusbar_set_systray_extl(WStatusBar *sb, const char *how)
1047 return statusbar_set_systray(sb, libtu_string_to_setparam(how));
1052 * Is \var{sb} used as a systray?
1055 bool statusbar_is_systray_extl(WStatusBar *sb)
1057 return sb->systray_enabled;
1067 WRegion *statusbar_load(WWindow *par, const WFitParams *fp, ExtlTab tab)
1069 WStatusBar *sb=create_statusbar(par, fp);
1073 ExtlTab t=extl_table_none();
1074 if(extl_table_gets_s(tab, "template", &tmpl)){
1075 statusbar_set_template(sb, tmpl);
1077 }else if(extl_table_gets_t(tab, "template_table", &t)){
1078 statusbar_set_template_table(sb, t);
1079 extl_unref_table(t);
1081 const char *tmpl=TR("[ %date || load: %load ] %filler%systray");
1082 statusbar_set_template(sb, tmpl);
1085 extl_table_gets_b(tab, "systray", &sb->systray_enabled);
1088 return (WRegion*)sb;
1095 /*{{{ Dynamic function table and class implementation */
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},
1104 {region_managed_rqgeom, statusbar_managed_rqgeom},
1105 {(DynFun*)region_prepare_manage, (DynFun*)statusbar_prepare_manage},
1106 {region_managed_remove, statusbar_managed_remove},
1108 {(DynFun*)region_prepare_manage_transient,
1109 (DynFun*)statusbar_prepare_manage_transient},
1111 {region_map, statusbar_map},
1112 {region_unmap, statusbar_unmap},
1114 {(DynFun*)region_fitrep,
1115 (DynFun*)statusbar_fitrep},
1122 IMPLCLASS(WStatusBar, WWindow, statusbar_deinit, statusbar_dynfuntab);