2 * ion/mod_statusbar/statusbar.c
4 * Copyright (c) Tuomo Valkonen 1999-2007.
6 * See the included file LICENSE for details.
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>
30 #include "statusbar.h"
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);
44 static WStatusBar *statusbars=NULL;
50 bool statusbar_init(WStatusBar *p, WWindow *parent, const WFitParams *fp)
52 if(!window_init(&(p->wwin), parent, fp))
64 p->systray_enabled=TRUE;
66 statusbar_updategr(p);
69 window_deinit(&(p->wwin));
73 window_select_input(&(p->wwin), IONCORE_EVENTMASK_NORMAL);
75 region_register((WRegion*)p);
77 region_add_bindmap((WRegion*)p, mod_statusbar_statusbar_bindmap);
79 ((WRegion*)p)->flags|=REGION_SKIP_FOCUS;
81 LINK_ITEM(statusbars, p, sb_next, sb_prev);
88 WStatusBar *create_statusbar(WWindow *parent, const WFitParams *fp)
90 CREATEOBJ_IMPL(WStatusBar, statusbar, (p, parent, fp));
94 void statusbar_deinit(WStatusBar *p)
96 UNLINK_ITEM(statusbars, p, sb_next, sb_prev);
98 statusbar_free_elems(p);
101 grbrush_release(p->brush);
105 window_deinit(&(p->wwin));
112 /*{{{ Content stuff */
115 static void init_sbelem(WSBElem *el)
117 el->type=WSBELEM_NONE;
122 el->meter=STRINGID_NONE;
123 el->attr=STRINGID_NONE;
125 el->align=WSBELEM_ALIGN_CENTER;
132 static bool gets_stringstore(ExtlTab t, const char *str, StringId *id)
136 if(extl_table_gets_s(t, str, &s)){
137 *id=stringstore_alloc(s);
139 return (*id!=STRINGID_NONE);
146 static WSBElem *get_sbelems(ExtlTab t, int *nret, int *filleridxret)
148 int i, n=extl_table_get_n(t);
158 el=ALLOC_N(WSBElem, n);
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){
181 gets_stringstore(tt, "meter", &(el[i].meter));
182 extl_table_gets_i(tt, "align", &(el[i].align));
184 tmp=stringstore_get(el[i].meter);
186 if(tmp==NULL || strcmp(tmp, "systray")==0)
188 }else if(el[i].type==WSBELEM_FILLER){
192 extl_unref_table(tt);
197 WSBElem *el2=REALLOC_N(el, WSBElem, n, n+1);
201 el[n].type=WSBELEM_SYSTRAY;
212 static void free_sbelems(WSBElem *el, int n)
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);
233 static void statusbar_set_elems(WStatusBar *sb, ExtlTab t)
235 statusbar_free_elems(sb);
237 sb->elems=get_sbelems(t, &(sb->nelems), &(sb->filleridx));
241 static void statusbar_free_elems(WStatusBar *sb)
244 free_sbelems(sb->elems, sb->nelems);
259 static void statusbar_resize(WStatusBar *p)
261 WRQGeomParams rq=RQGEOMPARAMS_INIT;
263 rq.flags=REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y;
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;
270 if(rectangle_compare(&rq.geom, ®ION_GEOM(p))!=RECTANGLE_SAME)
271 region_rqgeom((WRegion*)p, &rq, NULL);
275 static void calc_elem_w(WStatusBar *p, WSBElem *el, GrBrush *brush)
279 if(el->type==WSBELEM_SYSTRAY){
280 do_calc_systray_w(p, el);
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));
293 el->max_w=maxof((str!=NULL
294 ? grbrush_get_text_width(brush, str, strlen(str))
299 el->text_w=(str!=NULL
300 ? grbrush_get_text_width(brush, str, strlen(str))
302 el->max_w=el->text_w;
307 static void statusbar_calc_widths(WStatusBar *sb)
311 for(i=0; i<sb->nelems; i++)
312 calc_elem_w(sb, &(sb->elems[i]), sb->brush);
316 static void statusbar_do_update_natural_size(WStatusBar *p)
326 bdw.left=0; bdw.right=0;
327 bdw.top=0; bdw.bottom=0;
330 grbrush_get_border_widths(p->brush, &bdw);
331 grbrush_get_font_extents(p->brush, &fnte);
334 for(i=0; i<p->nelems; i++)
335 totw+=p->elems[i].max_w;
337 FOR_ALL_ON_PTRLIST(WRegion*, reg, p->traywins, tmp){
338 stmh=maxof(stmh, REGION_GEOM(reg).h);
341 p->natural_w=bdw.left+totw+bdw.right;
342 p->natural_h=maxof(stmh, fnte.max_height)+bdw.top+bdw.bottom;
346 void statusbar_size_hints(WStatusBar *p, WSizeHints *h)
349 h->min_width=p->natural_w;
350 h->min_height=p->natural_h;
353 h->max_width=INT_MAX;/*p->natural_w;*/
354 h->max_height=p->natural_h;
364 static WSBElem *statusbar_associate_systray(WStatusBar *sb, WRegion *reg)
366 WClientWin *cwin=OBJ_CAST(reg, WClientWin);
367 WSBElem *el=NULL, *fbel=NULL;
372 extl_table_gets_s(cwin->proptab, "statusbar", &name);
374 for(i=0; i<sb->nelems; i++){
377 if(sb->elems[i].type!=WSBELEM_SYSTRAY)
380 meter=stringstore_get(sb->elems[i].meter);
386 if(name!=NULL && strcmp(meter, name)==0){
390 if(strcmp(meter, "systray")==0)
403 ptrlist_insert_last(&el->traywins, (Obj*)reg);
409 static WSBElem *statusbar_unassociate_systray(WStatusBar *sb, WRegion *reg)
413 for(i=0; i<sb->nelems; i++){
414 if(ptrlist_remove(&(sb->elems[i].traywins), (Obj*)reg))
415 return &sb->elems[i];
423 static void do_calc_systray_w(WStatusBar *p, WSBElem *el)
430 FOR_ALL_ON_PTRLIST(WRegion*, reg, el->traywins, tmp){
431 w=w+REGION_GEOM(reg).w+padding;
434 el->text_w=maxof(0, w);
435 el->max_w=el->text_w; /* for now */
439 static void statusbar_calc_systray_w(WStatusBar *p)
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]);
450 static void statusbar_arrange_systray(WStatusBar *p)
455 int padding=0, ymiddle;
459 grbrush_get_border_widths(p->brush, &bdw);
465 ymiddle=bdw.top+(REGION_GEOM(p).h-bdw.top-bdw.bottom)/2;
467 for(i=0; i<p->nelems; i++){
468 WSBElem *el=&p->elems[i];
469 if(el->type!=WSBELEM_SYSTRAY)
472 FOR_ALL_ON_PTRLIST(WRegion*, reg, el->traywins, tmp){
473 WRectangle g=REGION_GEOM(reg);
476 region_fit(reg, &g, REGION_FIT_EXACT);
483 static void systray_adjust_size(WRegion *reg, WRectangle *g)
485 g->h=CF_STATUSBAR_SYSTRAY_HEIGHT;
487 region_size_hints_correct(reg, &g->w, &g->h, TRUE);
492 static WRegion *statusbar_do_attach_final(WStatusBar *sb,
499 if(!ptrlist_insert_last(&sb->traywins, (Obj*)reg))
502 el=statusbar_associate_systray(sb, reg);
504 ptrlist_remove(&sb->traywins, (Obj*)reg);
508 fp.g=REGION_GEOM(reg);
509 fp.mode=REGION_FIT_EXACT;
510 systray_adjust_size(reg, &fp.g);
512 region_fitrep(reg, NULL, &fp);
514 do_calc_systray_w(sb, el);
516 region_set_manager(reg, (WRegion*)sb);
518 statusbar_rearrange(sb, TRUE);
520 if(REGION_IS_MAPPED(sb))
527 static WRegion *statusbar_do_attach(WStatusBar *sb, WRegionAttachData *data)
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;
537 return region_attach_helper((WRegion*)sb, (WWindow*)sb, &fp,
538 (WRegionDoAttachFn*)statusbar_do_attach_final,
543 static WRegion *statusbar_attach_ph(WStatusBar *sb, int flags,
544 WRegionAttachData *data)
546 return statusbar_do_attach(sb, data);
550 static WPHolder *statusbar_prepare_manage(WStatusBar *sb,
551 const WClientWin *cwin,
552 const WManageParams *param,
555 if(!MANAGE_PRIORITY_OK(priority, MANAGE_PRIORITY_LOW))
558 return (WPHolder*)create_basicpholder((WRegion*)sb,
559 ((WBasicPHolderHandler*)
560 statusbar_attach_ph));
564 static void statusbar_managed_remove(WStatusBar *sb, WRegion *reg)
568 ptrlist_remove(&sb->traywins, (Obj*)reg);
570 el=statusbar_unassociate_systray(sb, reg);
572 region_unset_manager(reg, (WRegion*)sb);
574 if(el!=NULL && ioncore_g.opmode!=IONCORE_OPMODE_DEINIT){
575 do_calc_systray_w(sb, el);
576 statusbar_rearrange(sb, TRUE);
581 static void statusbar_managed_rqgeom(WStatusBar *sb, WRegion *reg,
582 const WRQGeomParams *rq,
587 g.x=REGION_GEOM(reg).x;
588 g.y=REGION_GEOM(reg).y;
592 systray_adjust_size(reg, &g);
594 if(rq->flags®ION_RQGEOM_TRYONLY){
600 region_fit(reg, &g, REGION_FIT_EXACT);
602 statusbar_calc_systray_w(sb);
603 statusbar_rearrange(sb, TRUE);
606 *geomret=REGION_GEOM(reg);
611 void statusbar_map(WStatusBar *sb)
616 window_map((WWindow*)sb);
618 FOR_ALL_ON_PTRLIST(WRegion*, reg, sb->traywins, tmp)
623 void statusbar_unmap(WStatusBar *sb)
628 window_unmap((WWindow*)sb);
630 FOR_ALL_ON_PTRLIST(WRegion*, reg, sb->traywins, tmp)
635 bool statusbar_fitrep(WStatusBar *sb, WWindow *par, const WFitParams *fp)
637 bool wchg=(REGION_GEOM(sb).w!=fp->g.w);
638 bool hchg=(REGION_GEOM(sb).h!=fp->g.h);
640 window_do_fitrep(&(sb->wwin), par, &(fp->g));
643 statusbar_calculate_xs(sb);
644 statusbar_arrange_systray(sb);
645 statusbar_draw(sb, TRUE);
652 WPHolder *statusbar_prepare_manage_transient(WStatusBar *sb,
653 const WClientWin *cwin,
654 const WManageParams *param,
657 WRegion *mgr=REGION_MANAGER(sb);
660 mgr=(WRegion*)region_screen_of((WRegion*)sb);
663 return region_prepare_manage(mgr, cwin, param,
664 MANAGE_PRIORITY_NONE);
677 static ExtlFn parse_template_fn;
678 static bool parse_template_fn_set=FALSE;
682 void mod_statusbar__set_template_parser(ExtlFn fn)
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;
692 * Set statusbar template.
695 void statusbar_set_template(WStatusBar *sb, const char *tmpl)
697 ExtlTab t=extl_table_none();
700 if(parse_template_fn_set){
702 ok=extl_call(parse_template_fn, "s", "t", tmpl, &t);
703 extl_unprotect(NULL);
707 statusbar_set_template_table(sb, t);
712 * Set statusbar template as table.
715 void statusbar_set_template_table(WStatusBar *sb, ExtlTab t)
720 statusbar_set_elems(sb, t);
722 FOR_ALL_ON_PTRLIST(WRegion*, reg, sb->traywins, tmp){
723 statusbar_associate_systray(sb, reg);
726 statusbar_calc_widths(sb);
727 statusbar_rearrange(sb, FALSE);
732 * Get statusbar template as table.
735 ExtlTab statusbar_get_template_table(WStatusBar *sb)
737 int count = sb->nelems;
740 ExtlTab t = extl_create_table();
742 for(i=0; i<count; i++){
743 ExtlTab tt = extl_create_table();
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);
752 extl_table_seti_t(t, (i+1), tt);
753 extl_unref_table(tt);
760 static void reset_stretch(WStatusBar *sb)
764 for(i=0; i<sb->nelems; i++)
765 sb->elems[i].stretch=0;
769 static void positive_stretch(WStatusBar *sb)
773 for(i=0; i<sb->nelems; i++)
774 sb->elems[i].stretch=maxof(0, sb->elems[i].stretch);
778 static void spread_stretch(WStatusBar *sb)
782 WSBElem *el, *lel, *rel;
785 for(i=0; i<sb->nelems; i++){
788 if(el->type!=WSBELEM_METER && el->type!=WSBELEM_SYSTRAY)
791 diff=el->max_w-el->text_w;
796 if(el->align!=WSBELEM_ALIGN_RIGHT){
797 for(j=i+1; j<sb->nelems; j++){
798 if(sb->elems[j].type==WSBELEM_STRETCH){
805 if(el->align!=WSBELEM_ALIGN_LEFT){
806 for(k=i-1; k>=0; k--){
807 if(sb->elems[k].type==WSBELEM_STRETCH){
814 if(rel!=NULL && lel!=NULL){
828 static void statusbar_rearrange(WStatusBar *sb, bool rs)
831 int onw=sb->natural_w;
832 int onh=sb->natural_h;
834 statusbar_do_update_natural_size(sb);
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)){
841 statusbar_resize(sb);
847 positive_stretch(sb);
848 statusbar_calculate_xs(sb);
851 statusbar_arrange_systray(sb);
857 * Set statusbar template.
860 void statusbar_update(WStatusBar *sb, ExtlTab t)
869 for(i=0; i<sb->nelems; i++){
874 if(el->type!=WSBELEM_METER)
882 if(el->attr!=GRATTR_NONE){
883 stringstore_free(el->attr);
884 el->attr=GRATTR_NONE;
887 meter=stringstore_get(el->meter);
893 extl_table_gets_s(t, meter, &(el->text));
896 str=STATUSBAR_NX_STR;
899 int l=strlen(el->text);
900 int ml=str_len(el->text);
901 int diff=el->zeropad-ml;
903 char *tmp=ALLOC_N(char, l+diff+1);
905 memset(tmp, '0', diff);
906 memcpy(tmp+diff, el->text, l+1);
914 if(el->tmpl!=NULL && el->text!=NULL){
915 char *tmp=grbrush_make_label(sb->brush, el->text, el->max_w);
923 el->text_w=grbrush_get_text_width(sb->brush, str, strlen(str));
925 if(el->text_w>el->max_w && el->tmpl==NULL){
926 el->max_w=el->text_w;
930 attrnm=scat(meter, "_hint");
933 if(extl_table_gets_s(t, attrnm, &s)){
934 el->attr=stringstore_alloc(s);
942 statusbar_rearrange(sb, grow);
944 window_draw((WWindow*)sb, FALSE);
954 void statusbar_updategr(WStatusBar *p)
958 nbrush=gr_get_brush(p->wwin.win, region_rootwin_of((WRegion*)p),
964 grbrush_release(p->brush);
968 statusbar_calc_widths(p);
969 statusbar_rearrange(p, TRUE);
971 window_draw(&(p->wwin), TRUE);
981 int statusbar_orientation(WStatusBar *sb)
983 return REGION_ORIENTATION_HORIZONTAL;
988 * Returns a list of all statusbars.
991 ExtlTab mod_statusbar_statusbars()
993 ExtlTab t=extl_create_table();
997 for(sb=statusbars; sb!=NULL; sb=sb->sb_next){
998 extl_table_seti_o(t, i, (Obj*)sb);
1006 WStatusBar *mod_statusbar_find_suitable(WClientWin *cwin,
1007 const WManageParams *param)
1011 for(sb=statusbars; sb!=NULL; sb=sb->sb_next){
1014 if(!sb->systray_enabled)
1016 if(!region_same_rootwin((WRegion*)sb, (WRegion*)cwin))
1025 bool statusbar_set_systray(WStatusBar *sb, int sp)
1027 bool set=sb->systray_enabled;
1028 bool nset=libtu_do_setparam(sp, set);
1030 sb->systray_enabled=nset;
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.
1042 EXTL_EXPORT_AS(WStatusBar, set_systray)
1043 bool statusbar_set_systray_extl(WStatusBar *sb, const char *how)
1045 return statusbar_set_systray(sb, libtu_string_to_setparam(how));
1050 * Is \var{sb} used as a systray?
1052 EXTL_EXPORT_AS(WStatusBar, is_systray)
1053 bool statusbar_is_systray_extl(WStatusBar *sb)
1055 return sb->systray_enabled;
1065 WRegion *statusbar_load(WWindow *par, const WFitParams *fp, ExtlTab tab)
1067 WStatusBar *sb=create_statusbar(par, fp);
1071 ExtlTab t=extl_table_none();
1072 if(extl_table_gets_s(tab, "template", &tmpl)){
1073 statusbar_set_template(sb, tmpl);
1075 }else if(extl_table_gets_t(tab, "template_table", &t)){
1076 statusbar_set_template_table(sb, t);
1077 extl_unref_table(t);
1079 const char *tmpl=TR("[ %date || load: %load ] %filler%systray");
1080 statusbar_set_template(sb, tmpl);
1083 extl_table_gets_b(tab, "systray", &sb->systray_enabled);
1086 return (WRegion*)sb;
1093 /*{{{ Dynamic function table and class implementation */
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},
1102 {region_managed_rqgeom, statusbar_managed_rqgeom},
1103 {(DynFun*)region_prepare_manage, (DynFun*)statusbar_prepare_manage},
1104 {region_managed_remove, statusbar_managed_remove},
1106 {(DynFun*)region_prepare_manage_transient,
1107 (DynFun*)statusbar_prepare_manage_transient},
1109 {region_map, statusbar_map},
1110 {region_unmap, statusbar_unmap},
1112 {(DynFun*)region_fitrep,
1113 (DynFun*)statusbar_fitrep},
1120 IMPLCLASS(WStatusBar, WWindow, statusbar_deinit, statusbar_dynfuntab);