2 * ion/mod_statusbar/statusbar.c
4 * Copyright (c) Tuomo Valkonen 1999-2006.
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.
14 #include <libtu/objp.h>
15 #include <libtu/minmax.h>
16 #include <libtu/ptrlist.h>
17 #include <libtu/misc.h>
18 #include <ioncore/common.h>
19 #include <ioncore/global.h>
20 #include <ioncore/window.h>
21 #include <ioncore/binding.h>
22 #include <ioncore/regbind.h>
23 #include <ioncore/event.h>
24 #include <ioncore/resize.h>
25 #include <ioncore/gr.h>
26 #include <ioncore/names.h>
27 #include <ioncore/strings.h>
28 #include <ioncore/basicpholder.h>
29 #include <ioncore/sizehint.h>
31 #include "statusbar.h"
36 static void statusbar_set_elems(WStatusBar *sb, ExtlTab t);
37 static void statusbar_free_elems(WStatusBar *sb);
38 static void statusbar_update_natural_size(WStatusBar *p);
39 static void statusbar_arrange_systray(WStatusBar *p);
40 static int statusbar_systray_x(WStatusBar *p);
41 static void statusbar_rearrange(WStatusBar *sb, bool rs);
42 static void do_calc_systray_w(WStatusBar *p, WSBElem *el);
43 static void statusbar_calc_systray_w(WStatusBar *p);
46 static WStatusBar *statusbars=NULL;
52 bool statusbar_init(WStatusBar *p, WWindow *parent, const WFitParams *fp)
54 if(!window_init(&(p->wwin), parent, fp))
66 p->systray_enabled=TRUE;
68 statusbar_updategr(p);
71 window_deinit(&(p->wwin));
75 window_select_input(&(p->wwin), IONCORE_EVENTMASK_NORMAL);
77 region_register((WRegion*)p);
79 region_add_bindmap((WRegion*)p, mod_statusbar_statusbar_bindmap);
81 ((WRegion*)p)->flags|=REGION_SKIP_FOCUS;
83 LINK_ITEM(statusbars, p, sb_next, sb_prev);
90 WStatusBar *create_statusbar(WWindow *parent, const WFitParams *fp)
92 CREATEOBJ_IMPL(WStatusBar, statusbar, (p, parent, fp));
96 void statusbar_deinit(WStatusBar *p)
98 UNLINK_ITEM(statusbars, p, sb_next, sb_prev);
100 statusbar_free_elems(p);
103 grbrush_release(p->brush);
107 window_deinit(&(p->wwin));
114 /*{{{ Content stuff */
116 static void init_sbelem(WSBElem *el)
118 el->type=WSBELEM_NONE;
126 el->align=WSBELEM_ALIGN_CENTER;
133 static WSBElem *get_sbelems(ExtlTab t, int *nret, int *filleridxret)
135 int i, n=extl_table_get_n(t);
145 el=ALLOC_N(WSBElem, n);
155 if(extl_table_geti_t(t, i+1, &tt)){
156 if(extl_table_gets_i(tt, "type", &(el[i].type))){
157 if(el[i].type==WSBELEM_TEXT || el[i].type==WSBELEM_STRETCH){
158 extl_table_gets_s(tt, "text", &(el[i].text));
159 }else if(el[i].type==WSBELEM_METER){
160 extl_table_gets_s(tt, "meter", &(el[i].meter));
161 extl_table_gets_s(tt, "tmpl", &(el[i].tmpl));
162 extl_table_gets_i(tt, "align", &(el[i].align));
163 extl_table_gets_i(tt, "zeropad", &(el[i].zeropad));
164 el[i].zeropad=maxof(el[i].zeropad, 0);
165 }else if(el[i].type==WSBELEM_SYSTRAY){
166 extl_table_gets_s(tt, "meter", &(el[i].meter));
167 extl_table_gets_i(tt, "align", &(el[i].align));
168 if(el[i].meter==NULL || strcmp(el[i].meter, "systray")==0)
170 }else if(el[i].type==WSBELEM_FILLER){
174 extl_unref_table(tt);
179 WSBElem *el2=REALLOC_N(el, WSBElem, n, n+1);
183 el[n].type=WSBELEM_SYSTRAY;
194 static void free_sbelems(WSBElem *el, int n)
201 if(el[i].meter!=NULL)
207 if(el[i].traywins!=NULL)
208 ptrlist_clear(&el[i].traywins);
215 static void statusbar_set_elems(WStatusBar *sb, ExtlTab t)
217 statusbar_free_elems(sb);
219 sb->elems=get_sbelems(t, &(sb->nelems), &(sb->filleridx));
223 static void statusbar_free_elems(WStatusBar *sb)
226 free_sbelems(sb->elems, sb->nelems);
241 static void statusbar_resize(WStatusBar *p)
243 WRQGeomParams rq=RQGEOMPARAMS_INIT;
245 rq.flags=REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y;
247 rq.geom.w=p->natural_w;
248 rq.geom.h=p->natural_h;
249 rq.geom.x=REGION_GEOM(p).x;
250 rq.geom.y=REGION_GEOM(p).y;
252 if(rectangle_compare(&rq.geom, ®ION_GEOM(p))!=RECTANGLE_SAME)
253 region_rqgeom((WRegion*)p, &rq, NULL);
257 static void calc_elem_w(WStatusBar *p, WSBElem *el, GrBrush *brush)
261 if(el->type==WSBELEM_SYSTRAY){
262 do_calc_systray_w(p, el);
271 if(el->type==WSBELEM_METER){
272 str=(el->text!=NULL ? el->text : STATUSBAR_NX_STR);
273 el->text_w=grbrush_get_text_width(brush, str, strlen(str));
275 el->max_w=maxof((str!=NULL
276 ? grbrush_get_text_width(brush, str, strlen(str))
281 el->text_w=(str!=NULL
282 ? grbrush_get_text_width(brush, str, strlen(str))
284 el->max_w=el->text_w;
289 static void statusbar_calc_widths(WStatusBar *sb)
293 for(i=0; i<sb->nelems; i++)
294 calc_elem_w(sb, &(sb->elems[i]), sb->brush);
298 static void statusbar_do_update_natural_size(WStatusBar *p)
308 bdw.left=0; bdw.right=0;
309 bdw.top=0; bdw.bottom=0;
312 grbrush_get_border_widths(p->brush, &bdw);
313 grbrush_get_font_extents(p->brush, &fnte);
316 for(i=0; i<p->nelems; i++)
317 totw+=p->elems[i].max_w;
319 FOR_ALL_ON_PTRLIST(WRegion*, reg, p->traywins, tmp){
320 stmh=maxof(stmh, REGION_GEOM(reg).h);
323 p->natural_w=bdw.left+totw+bdw.right;
324 p->natural_h=maxof(stmh, fnte.max_height)+bdw.top+bdw.bottom;
328 void statusbar_size_hints(WStatusBar *p, WSizeHints *h)
331 h->min_width=p->natural_w;
332 h->min_height=p->natural_h;
335 h->max_width=p->natural_w;
336 h->max_height=p->natural_h;
346 static WSBElem *statusbar_associate_systray(WStatusBar *sb, WRegion *reg)
348 WClientWin *cwin=OBJ_CAST(reg, WClientWin);
349 WSBElem *el=NULL, *fbel=NULL;
354 extl_table_gets_s(cwin->proptab, "statusbar", &name);
356 for(i=0; i<sb->nelems; i++){
357 if(sb->elems[i].type!=WSBELEM_SYSTRAY)
359 if(sb->elems[i].meter==NULL){
363 if(name!=NULL && strcmp(sb->elems[i].meter, name)==0){
367 if(strcmp(sb->elems[i].meter, "systray")==0)
380 ptrlist_insert_last(&el->traywins, (Obj*)reg);
386 static WSBElem *statusbar_unassociate_systray(WStatusBar *sb, WRegion *reg)
390 for(i=0; i<sb->nelems; i++){
391 if(ptrlist_remove(&(sb->elems[i].traywins), (Obj*)reg))
392 return &sb->elems[i];
400 static void do_calc_systray_w(WStatusBar *p, WSBElem *el)
407 FOR_ALL_ON_PTRLIST(WRegion*, reg, el->traywins, tmp){
408 w=w+REGION_GEOM(reg).w+padding;
411 el->text_w=maxof(0, w);
412 el->max_w=el->text_w; /* for now */
416 static void statusbar_calc_systray_w(WStatusBar *p)
420 for(i=0; i<p->nelems; i++){
421 if(p->elems[i].type==WSBELEM_SYSTRAY)
422 do_calc_systray_w(p, &p->elems[i]);
427 static void statusbar_arrange_systray(WStatusBar *p)
432 int padding=0, ymiddle;
436 grbrush_get_border_widths(p->brush, &bdw);
442 ymiddle=bdw.top+(REGION_GEOM(p).h-bdw.top-bdw.bottom)/2;
444 for(i=0; i<p->nelems; i++){
445 WSBElem *el=&p->elems[i];
446 if(el->type!=WSBELEM_SYSTRAY)
449 FOR_ALL_ON_PTRLIST(WRegion*, reg, el->traywins, tmp){
450 WRectangle g=REGION_GEOM(reg);
453 region_fit(reg, &g, REGION_FIT_EXACT);
460 static void systray_adjust_size(WRegion *reg, WRectangle *g)
462 g->h=CF_STATUSBAR_SYSTRAY_HEIGHT;
464 region_size_hints_correct(reg, &g->w, &g->h, TRUE);
469 static WRegion *statusbar_do_attach_final(WStatusBar *sb,
476 if(!ptrlist_insert_last(&sb->traywins, (Obj*)reg))
479 el=statusbar_associate_systray(sb, reg);
481 ptrlist_remove(&sb->traywins, (Obj*)reg);
485 fp.g=REGION_GEOM(reg);
486 fp.mode=REGION_FIT_EXACT;
487 systray_adjust_size(reg, &fp.g);
489 region_fitrep(reg, NULL, &fp);
491 do_calc_systray_w(sb, el);
493 region_set_manager(reg, (WRegion*)sb);
495 statusbar_rearrange(sb, TRUE);
497 if(REGION_IS_MAPPED(sb))
504 static WRegion *statusbar_do_attach(WStatusBar *sb, WRegionAttachData *data)
510 fp.g.h=CF_STATUSBAR_SYSTRAY_HEIGHT;
511 fp.g.w=CF_STATUSBAR_SYSTRAY_HEIGHT;
512 fp.mode=REGION_FIT_WHATEVER|REGION_FIT_BOUNDS;
514 return region_attach_helper((WRegion*)sb, (WWindow*)sb, &fp,
515 (WRegionDoAttachFn*)statusbar_do_attach_final,
520 static WRegion *statusbar_attach_ph(WStatusBar *sb, int flags,
521 WRegionAttachData *data)
523 return statusbar_do_attach(sb, data);
527 static WPHolder *statusbar_prepare_manage(WStatusBar *sb,
528 const WClientWin *cwin,
529 const WManageParams *param,
532 if(redir==MANAGE_REDIR_STRICT_YES)
535 return (WPHolder*)create_basicpholder((WRegion*)sb,
536 ((WBasicPHolderHandler*)
537 statusbar_attach_ph));
541 static void statusbar_managed_remove(WStatusBar *sb, WRegion *reg)
545 ptrlist_remove(&sb->traywins, (Obj*)reg);
547 el=statusbar_unassociate_systray(sb, reg);
549 region_unset_manager(reg, (WRegion*)sb);
551 if(el!=NULL && ioncore_g.opmode!=IONCORE_OPMODE_DEINIT){
552 do_calc_systray_w(sb, el);
553 statusbar_rearrange(sb, TRUE);
558 static void statusbar_managed_rqgeom(WStatusBar *sb, WRegion *reg,
559 const WRQGeomParams *rq,
564 g.x=REGION_GEOM(reg).x;
565 g.y=REGION_GEOM(reg).y;
569 systray_adjust_size(reg, &g);
571 if(rq->flags®ION_RQGEOM_TRYONLY){
577 region_fit(reg, &g, REGION_FIT_EXACT);
579 statusbar_calc_systray_w(sb);
580 statusbar_rearrange(sb, TRUE);
583 *geomret=REGION_GEOM(reg);
588 void statusbar_map(WStatusBar *sb)
593 window_map((WWindow*)sb);
595 FOR_ALL_ON_PTRLIST(WRegion*, reg, sb->traywins, tmp)
600 void statusbar_unmap(WStatusBar *sb)
605 window_unmap((WWindow*)sb);
607 FOR_ALL_ON_PTRLIST(WRegion*, reg, sb->traywins, tmp)
612 bool statusbar_fitrep(WStatusBar *sb, WWindow *par, const WFitParams *fp)
614 bool wchg=(REGION_GEOM(sb).w!=fp->g.w);
615 bool hchg=(REGION_GEOM(sb).h!=fp->g.h);
617 window_do_fitrep(&(sb->wwin), par, &(fp->g));
620 statusbar_calculate_xs(sb);
621 statusbar_arrange_systray(sb);
622 statusbar_draw(sb, TRUE);
629 WPHolder *statusbar_prepare_manage_transient(WStatusBar *sb,
630 const WClientWin *cwin,
631 const WManageParams *param,
634 WRegion *mgr=REGION_MANAGER(sb);
637 mgr=(WRegion*)region_screen_of((WRegion*)sb);
640 return region_prepare_manage(mgr, cwin, param,
641 MANAGE_REDIR_PREFER_NO);
654 static ExtlFn parse_template_fn;
655 static bool parse_template_fn_set=FALSE;
659 void mod_statusbar__set_template_parser(ExtlFn fn)
661 if(parse_template_fn_set)
662 extl_unref_fn(parse_template_fn);
663 parse_template_fn=extl_ref_fn(fn);
664 parse_template_fn_set=TRUE;
669 * Set statusbar template.
672 void statusbar_set_template(WStatusBar *sb, const char *tmpl)
674 ExtlTab t=extl_table_none();
677 if(parse_template_fn_set){
679 ok=extl_call(parse_template_fn, "s", "t", tmpl, &t);
680 extl_unprotect(NULL);
684 statusbar_set_template_table(sb, t);
689 * Set statusbar template as table.
692 void statusbar_set_template_table(WStatusBar *sb, ExtlTab t)
697 statusbar_set_elems(sb, t);
699 FOR_ALL_ON_PTRLIST(WRegion*, reg, sb->traywins, tmp){
700 statusbar_associate_systray(sb, reg);
703 statusbar_calc_widths(sb);
704 statusbar_rearrange(sb, FALSE);
709 * Get statusbar template as table.
712 ExtlTab statusbar_get_template_table(WStatusBar *sb)
714 int count = sb->nelems;
717 ExtlTab t = extl_create_table();
719 for(i=0; i<count; i++){
720 ExtlTab tt = extl_create_table();
722 extl_table_sets_i(tt, "type", sb->elems[i].type);
723 extl_table_sets_s(tt, "text", sb->elems[i].text);
724 extl_table_sets_s(tt, "meter", sb->elems[i].meter);
725 extl_table_sets_s(tt, "tmpl", sb->elems[i].tmpl);
726 extl_table_sets_i(tt, "align", sb->elems[i].align);
727 extl_table_sets_i(tt, "zeropad", sb->elems[i].zeropad);
729 extl_table_seti_t(t, (i+1), tt);
730 extl_unref_table(tt);
737 static void reset_stretch(WStatusBar *sb)
741 for(i=0; i<sb->nelems; i++)
742 sb->elems[i].stretch=0;
746 static void positive_stretch(WStatusBar *sb)
750 for(i=0; i<sb->nelems; i++)
751 sb->elems[i].stretch=maxof(0, sb->elems[i].stretch);
755 static void spread_stretch(WStatusBar *sb)
759 WSBElem *el, *lel, *rel;
762 for(i=0; i<sb->nelems; i++){
765 if(el->type!=WSBELEM_METER && el->type!=WSBELEM_SYSTRAY)
768 diff=el->max_w-el->text_w;
773 if(el->align!=WSBELEM_ALIGN_RIGHT){
774 for(j=i+1; j<sb->nelems; j++){
775 if(sb->elems[j].type==WSBELEM_STRETCH){
782 if(el->align!=WSBELEM_ALIGN_LEFT){
783 for(k=i-1; k>=0; k--){
784 if(sb->elems[k].type==WSBELEM_STRETCH){
791 if(rel!=NULL && lel!=NULL){
805 static void statusbar_rearrange(WStatusBar *sb, bool rs)
808 int onw=sb->natural_w;
809 int onh=sb->natural_h;
811 statusbar_do_update_natural_size(sb);
813 if( (sb->natural_h>onh && REGION_GEOM(sb).h>=onh)
814 || (sb->natural_h<onh && REGION_GEOM(sb).h<=onh)
815 || (sb->natural_w>onw && REGION_GEOM(sb).w>=onw)
816 || (sb->natural_w<onw && REGION_GEOM(sb).w<=onw)){
818 statusbar_resize(sb);
824 positive_stretch(sb);
825 statusbar_calculate_xs(sb);
828 statusbar_arrange_systray(sb);
834 * Set statusbar template.
837 void statusbar_update(WStatusBar *sb, ExtlTab t)
846 for(i=0; i<sb->nelems; i++){
849 if(el->type!=WSBELEM_METER)
866 extl_table_gets_s(t, el->meter, &(el->text));
869 str=STATUSBAR_NX_STR;
872 int l=strlen(el->text);
873 int ml=str_len(el->text);
874 int diff=el->zeropad-ml;
876 char *tmp=ALLOC_N(char, l+diff+1);
878 memset(tmp, '0', diff);
879 memcpy(tmp+diff, el->text, l+1);
887 if(el->tmpl!=NULL && el->text!=NULL){
888 char *tmp=grbrush_make_label(sb->brush, el->text, el->max_w);
896 el->text_w=grbrush_get_text_width(sb->brush, str, strlen(str));
898 if(el->text_w>el->max_w && el->tmpl==NULL){
899 el->max_w=el->text_w;
903 attrnm=scat(el->meter, "_hint");
905 extl_table_gets_s(t, attrnm, &(el->attr));
911 statusbar_rearrange(sb, grow);
913 window_draw((WWindow*)sb, FALSE);
923 void statusbar_updategr(WStatusBar *p)
927 nbrush=gr_get_brush(p->wwin.win, region_rootwin_of((WRegion*)p),
933 grbrush_release(p->brush);
937 statusbar_calc_widths(p);
938 statusbar_rearrange(p, TRUE);
940 window_draw(&(p->wwin), TRUE);
950 int statusbar_orientation(WStatusBar *sb)
952 return REGION_ORIENTATION_HORIZONTAL;
957 * Returns a list of all statusbars.
960 ExtlTab mod_statusbar_statusbars()
962 ExtlTab t=extl_create_table();
966 for(sb=statusbars; sb!=NULL; sb=sb->sb_next){
967 extl_table_seti_o(t, i, (Obj*)sb);
975 WStatusBar *mod_statusbar_find_suitable(WClientWin *cwin,
976 const WManageParams *param)
980 for(sb=statusbars; sb!=NULL; sb=sb->sb_next){
983 if(!sb->systray_enabled)
985 if(!region_same_rootwin((WRegion*)sb, (WRegion*)cwin))
994 bool statusbar_set_systray(WStatusBar *sb, int sp)
996 bool set=sb->systray_enabled;
997 bool nset=libtu_do_setparam(sp, set);
999 sb->systray_enabled=nset;
1006 * Enable or disable use of \var{sb} as systray.
1007 * The parameter \var{how} can be one of (set/unset/toggle).
1008 * Resulting state is returned.
1010 EXTL_EXPORT_AS(WStatusBar, set_systray)
1011 bool statusbar_set_systray_extl(WStatusBar *sb, const char *how)
1013 return statusbar_set_systray(sb, libtu_string_to_setparam(how));
1018 * Is \var{sb} used as a systray?
1021 bool statusbar_is_systray_extl(WStatusBar *sb)
1023 return sb->systray_enabled;
1033 WRegion *statusbar_load(WWindow *par, const WFitParams *fp, ExtlTab tab)
1035 WStatusBar *sb=create_statusbar(par, fp);
1039 ExtlTab t=extl_table_none();
1040 if(extl_table_gets_s(tab, "template", &tmpl)){
1041 statusbar_set_template(sb, tmpl);
1043 }else if(extl_table_gets_t(tab, "template_table", &t)){
1044 statusbar_set_template_table(sb, t);
1045 extl_unref_table(t);
1047 const char *tmpl=TR("[ %date || load: %load ] %filler%systray");
1048 statusbar_set_template(sb, tmpl);
1051 extl_table_gets_b(tab, "systray", &sb->systray_enabled);
1054 return (WRegion*)sb;
1061 /*{{{ Dynamic function table and class implementation */
1064 static DynFunTab statusbar_dynfuntab[]={
1065 {window_draw, statusbar_draw},
1066 {region_updategr, statusbar_updategr},
1067 {region_size_hints, statusbar_size_hints},
1068 {(DynFun*)region_orientation, (DynFun*)statusbar_orientation},
1070 {region_managed_rqgeom, statusbar_managed_rqgeom},
1071 {(DynFun*)region_prepare_manage, (DynFun*)statusbar_prepare_manage},
1072 {region_managed_remove, statusbar_managed_remove},
1074 {(DynFun*)region_prepare_manage_transient,
1075 (DynFun*)statusbar_prepare_manage_transient},
1077 {region_map, statusbar_map},
1078 {region_unmap, statusbar_unmap},
1080 {(DynFun*)region_fitrep,
1081 (DynFun*)statusbar_fitrep},
1088 IMPLCLASS(WStatusBar, WWindow, statusbar_deinit, statusbar_dynfuntab);