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.
14 #include <libtu/obj.h>
15 #include <libtu/objp.h>
16 #include <libtu/minmax.h>
17 #include <libtu/map.h>
18 #include <libmainloop/defer.h>
32 #include "frame-pointer.h"
33 #include "frame-draw.h"
41 #include "framedpholder.h"
45 extern bool frame_set_background(WFrame *frame, bool set_always);
46 extern void frame_initialise_gr(WFrame *frame);
48 static bool frame_initialise_titles(WFrame *frame);
49 static void frame_free_titles(WFrame *frame);
51 static void frame_add_mode_bindmaps(WFrame *frame);
54 WHook *frame_managed_changed_hook=NULL;
56 #define IS_FLOATING_MODE(FRAME) \
57 ((FRAME)->mode==FRAME_MODE_FLOATING || (FRAME)->mode==FRAME_MODE_TRANSIENT)
58 #define FORWARD_CWIN_RQGEOM(FRAME) IS_FLOATING_MODE(FRAME)
59 #define USE_MINMAX(FRAME) IS_FLOATING_MODE(FRAME)
60 #define DEST_EMPTY(FRAME) IS_FLOATING_MODE(FRAME)
63 /*{{{ Destroy/create frame */
66 bool frame_init(WFrame *frame, WWindow *parent, const WFitParams *fp,
76 frame->tab_dragged_idx=-1;
81 frame->tr_mode=GR_TRANSPARENCY_DEFAULT;
83 frame->bar_brush=NULL;
86 frame->bar_max_width_q=1.0;
88 if(!mplex_init((WMPlex*)frame, parent, fp))
91 frame_initialise_gr(frame);
92 frame_initialise_titles(frame);
94 region_add_bindmap((WRegion*)frame, ioncore_frame_bindmap);
95 region_add_bindmap((WRegion*)frame, ioncore_mplex_bindmap);
97 frame_add_mode_bindmaps(frame);
99 mplex_managed_geom((WMPlex*)frame, &mg);
102 frame->flags|=FRAME_SHADED;
104 ((WRegion*)frame)->flags|=REGION_PLEASE_WARP;
110 WFrame *create_frame(WWindow *parent, const WFitParams *fp, WFrameMode mode)
112 CREATEOBJ_IMPL(WFrame, frame, (p, parent, fp, mode));
116 void frame_deinit(WFrame *frame)
118 frame_free_titles(frame);
119 frame_release_brushes(frame);
120 mplex_deinit((WMPlex*)frame);
127 /*{{{ Mode switching */
130 static void frame_add_mode_bindmaps(WFrame *frame)
132 WFrameMode mode=frame->mode;
134 if(mode==FRAME_MODE_FLOATING){
135 region_add_bindmap((WRegion*)frame, ioncore_mplex_toplevel_bindmap);
136 region_add_bindmap((WRegion*)frame, ioncore_frame_toplevel_bindmap);
137 region_add_bindmap((WRegion*)frame, ioncore_frame_floating_bindmap);
138 }else if(mode==FRAME_MODE_TRANSIENT){
139 region_add_bindmap((WRegion*)frame, ioncore_frame_transient_bindmap);
141 /* mode==FRAME_MODE_TILED || mode==FRAME_MODE_TILED_ALT || mode==FRAME_MODE_UNKNOWN */
142 region_add_bindmap((WRegion*)frame, ioncore_mplex_toplevel_bindmap);
143 region_add_bindmap((WRegion*)frame, ioncore_frame_toplevel_bindmap);
144 region_add_bindmap((WRegion*)frame, ioncore_frame_tiled_bindmap);
149 void frame_set_mode(WFrame *frame, WFrameMode mode)
151 if(frame->mode==mode)
154 frame_clear_shape(frame);
156 frame_release_brushes(frame);
158 region_remove_bindmap((WRegion*)frame, ioncore_mplex_toplevel_bindmap);
159 region_remove_bindmap((WRegion*)frame, ioncore_frame_toplevel_bindmap);
160 region_remove_bindmap((WRegion*)frame, ioncore_frame_tiled_bindmap);
161 region_remove_bindmap((WRegion*)frame, ioncore_frame_floating_bindmap);
162 region_remove_bindmap((WRegion*)frame, ioncore_frame_transient_bindmap);
166 frame_add_mode_bindmaps(frame);
168 frame_initialise_gr(frame);
170 mplex_fit_managed(&frame->mplex);
171 frame_recalc_bar(frame);
172 frame_set_background(frame, TRUE);
176 WFrameMode frame_mode(WFrame *frame)
182 StringIntMap frame_modes[]={
183 {"unknown", FRAME_MODE_UNKNOWN},
184 {"tiled", FRAME_MODE_TILED},
185 {"tiled-alt", FRAME_MODE_TILED_ALT},
186 {"floating", FRAME_MODE_FLOATING},
187 {"transient", FRAME_MODE_TRANSIENT},
196 EXTL_EXPORT_AS(WFrame, mode)
197 const char *frame_mode_extl(WFrame *frame)
199 return stringintmap_key(frame_modes, frame->mode, NULL);
206 EXTL_EXPORT_AS(WFrame, set_mode)
207 bool frame_set_mode_extl(WFrame *frame, const char *modestr)
212 idx=stringintmap_ndx(frame_modes, modestr);
216 frame_set_mode(frame, frame_modes[idx].value);
228 int frame_tab_at_x(WFrame *frame, int x)
233 frame_bar_geom(frame, &bg);
235 if(x>=bg.x+bg.w || x<bg.x)
240 for(tab=0; tab<FRAME_MCOUNT(frame); tab++){
241 tx+=frame_nth_tab_w(frame, tab);
250 int frame_nth_tab_x(WFrame *frame, int n)
256 x+=frame_nth_tab_w(frame, i);
262 static int frame_nth_tab_w_iw(WFrame *frame, int n, bool inner)
265 GrBorderWidths bdw=GR_BORDER_WIDTHS_INIT;
266 int m=FRAME_MCOUNT(frame);
269 frame_bar_geom(frame, &bg);
274 if(frame->bar_brush!=NULL)
275 grbrush_get_border_widths(frame->bar_brush, &bdw);
278 w=bg.w-bdw.left-bdw.right-(bdw.tb_ileft+bdw.tb_iright+bdw.spacing)*(m-1);
283 /* Get n:th tab's portion of free area */
284 w=(((n+1)*w)/m-(n*w)/m);
286 /* Add n:th tab's borders back */
288 w+=(n==0 ? bdw.left : bdw.tb_ileft);
289 w+=(n==m-1 ? bdw.right : bdw.tb_iright+bdw.spacing);
296 int frame_nth_tab_w(WFrame *frame, int n)
298 return frame_nth_tab_w_iw(frame, n, FALSE);
302 int frame_nth_tab_iw(WFrame *frame, int n)
304 return frame_nth_tab_w_iw(frame, n, TRUE);
309 void frame_update_attr_nth(WFrame *frame, int i)
313 if(i<0 || i>=frame->titles_n)
316 frame_update_attr(frame, i, mplex_mx_nth((WMPlex*)frame, i));
320 static void frame_update_attrs(WFrame *frame)
326 FRAME_MX_FOR_ALL(sub, frame, tmp){
327 frame_update_attr(frame, i, sub);
333 static void frame_free_titles(WFrame *frame)
337 if(frame->titles!=NULL){
338 for(i=0; i<frame->titles_n; i++){
339 if(frame->titles[i].text)
340 free(frame->titles[i].text);
341 gr_stylespec_unalloc(&frame->titles[i].attr);
350 static void do_init_title(WFrame *frame, int i, WRegion *sub)
352 frame->titles[i].text=NULL;
353 frame->titles[i].iw=frame_nth_tab_iw(frame, i);
355 gr_stylespec_init(&frame->titles[i].attr);
357 frame_update_attr(frame, i, sub);
361 static bool frame_initialise_titles(WFrame *frame)
363 int i, n=FRAME_MCOUNT(frame);
365 frame_free_titles(frame);
370 frame->titles=ALLOC_N(GrTextElem, n);
371 if(frame->titles==NULL)
375 if(FRAME_MCOUNT(frame)==0){
376 do_init_title(frame, 0, NULL);
381 FRAME_MX_FOR_ALL(sub, frame, tmp){
382 do_init_title(frame, i, sub);
387 frame_recalc_bar(frame);
396 /*{{{ Resize and reparent */
399 bool frame_fitrep(WFrame *frame, WWindow *par, const WFitParams *fp)
401 WRectangle old_geom, mg;
402 bool wchg=(REGION_GEOM(frame).w!=fp->g.w);
403 bool hchg=(REGION_GEOM(frame).h!=fp->g.h);
405 old_geom=REGION_GEOM(frame);
407 window_do_fitrep(&(frame->mplex.win), par, &(fp->g));
409 mplex_managed_geom((WMPlex*)frame, &mg);
413 frame->flags|=(FRAME_SHADED|FRAME_SAVED_VERT);
414 frame->saved_y=old_geom.y;
415 frame->saved_h=old_geom.h;
417 frame->flags&=~FRAME_SHADED;
419 frame->flags&=~FRAME_MAXED_VERT;
424 frame->flags|=(FRAME_MIN_HORIZ|FRAME_SAVED_HORIZ);
425 frame->saved_x=old_geom.x;
426 frame->saved_w=old_geom.w;
428 frame->flags&=~FRAME_MIN_HORIZ;
430 frame->flags&=~FRAME_MAXED_HORIZ;
434 mplex_fit_managed((WMPlex*)frame);
435 mplex_size_changed((WMPlex*)frame, wchg, hchg);
442 void frame_size_hints(WFrame *frame, WSizeHints *hints_ret)
449 mplex_managed_geom((WMPlex*)frame, &subgeom);
451 woff=maxof(REGION_GEOM(frame).w-subgeom.w, 0);
452 hoff=maxof(REGION_GEOM(frame).h-subgeom.h, 0);
454 if(FRAME_CURRENT(frame)!=NULL){
455 region_size_hints(FRAME_CURRENT(frame), hints_ret);
456 if(!USE_MINMAX(frame)){
457 hints_ret->max_set=0;
458 hints_ret->min_set=0;
459 hints_ret->base_set=0;
460 hints_ret->aspect_set=0;
461 hints_ret->no_constrain=FALSE;
462 /*hints_ret->no_constrain=TRUE;*/
465 sizehints_clear(hints_ret);
468 FRAME_MX_FOR_ALL(sub, frame, tmp){
469 sizehints_adjust_for(hints_ret, sub);
472 if(!hints_ret->base_set){
473 hints_ret->base_width=0;
474 hints_ret->base_height=0;
475 hints_ret->base_set=TRUE;
478 if(!hints_ret->min_set){
479 hints_ret->min_width=0;
480 hints_ret->min_height=0;
481 hints_ret->min_set=TRUE;
484 hints_ret->base_width+=woff;
485 hints_ret->base_height+=hoff;
486 hints_ret->max_width+=woff;
487 hints_ret->max_height+=hoff;
488 hints_ret->min_width+=woff;
489 hints_ret->min_height+=hoff;
491 if(frame->barmode==FRAME_BAR_SHAPED){
492 int f=frame->flags&(FRAME_SHADED|FRAME_SHADED_TOGGLE);
494 if(f==FRAME_SHADED || f==FRAME_SHADED_TOGGLE){
495 hints_ret->min_height=frame->bar_h;
496 hints_ret->max_height=frame->bar_h;
497 hints_ret->base_height=frame->bar_h;
498 if(!hints_ret->max_set){
499 hints_ret->max_width=INT_MAX;
500 hints_ret->max_set=TRUE;
513 void frame_inactivated(WFrame *frame)
515 window_draw((WWindow*)frame, FALSE);
519 void frame_activated(WFrame *frame)
521 window_draw((WWindow*)frame, FALSE);
525 void frame_quasiactivation(WFrame *frame, WRegion *reg, bool act)
529 was=(frame->quasiactive_count>0);
531 frame->quasiactive_count=maxof(0, frame->quasiactive_count
534 is=(frame->quasiactive_count>0);
536 if(was!=is && !REGION_IS_ACTIVE(frame))
537 window_draw((WWindow*)frame, FALSE);
541 static bool actinact(WRegion *reg, bool act)
543 WPHolder *returnph=region_get_return(reg);
546 if(returnph==NULL || pholder_stale(returnph))
549 frame=OBJ_CAST(pholder_target(returnph), WFrame);
552 /* Ok, reg has return placeholder set to a frame:
553 * do quasiactivation/inactivation
555 frame_quasiactivation(frame, reg, act);
562 static bool activated(WRegion *reg)
564 return actinact(reg, TRUE);
568 static bool inactivated(WRegion *reg)
570 return actinact(reg, FALSE);
574 void ioncore_frame_quasiactivation_notify(WRegion *reg,
577 if(how==ioncore_g.notifies.activated ||
578 how==ioncore_g.notifies.pseudoactivated){
580 }else if(how==ioncore_g.notifies.inactivated ||
581 how==ioncore_g.notifies.pseudoinactivated){
583 }else if(how==ioncore_g.notifies.set_return){
584 if(REGION_IS_ACTIVE(reg) || REGION_IS_PSEUDOACTIVE(reg))
586 }else if(how==ioncore_g.notifies.unset_return){
587 if(REGION_IS_ACTIVE(reg) || REGION_IS_PSEUDOACTIVE(reg))
596 /*{{{ Client window rqgeom */
599 static void frame_managed_rqgeom_absolute(WFrame *frame, WRegion *sub,
600 const WRQGeomParams *rq,
603 if(!FORWARD_CWIN_RQGEOM(frame)){
604 region_managed_rqgeom_absolute_default((WRegion*)frame, sub,
607 WRQGeomParams rq2=RQGEOMPARAMS_INIT;
608 int gravity=ForgetGravity;
613 rq2.flags=rq->flags&(REGION_RQGEOM_WEAK_ALL
614 |REGION_RQGEOM_TRYONLY
615 |REGION_RQGEOM_ABSOLUTE);
617 if(rq->flags®ION_RQGEOM_GRAVITY)
620 mplex_managed_geom(&frame->mplex, &off);
623 off.w=REGION_GEOM(frame).w-off.w;
624 off.h=REGION_GEOM(frame).h-off.h;
626 rq2.geom.w=maxof(rq2.geom.w+off.w, 0);
627 rq2.geom.h=maxof(rq2.geom.h+off.h, 0);
629 /*region_size_hints_correct((WRegion*)frame, &(geom.w), &(geom.h), TRUE);*/
631 /* If WEAK_? is set, then geom.(x|y) is root-relative as it was not
632 * requested by the client and clientwin_handle_configure_request has
633 * no better guess. Otherwise the coordinates are those requested by
634 * the client (modulo borders/gravity) and we interpret them to be
635 * root-relative coordinates for this frame modulo gravity.
637 if(rq->flags®ION_RQGEOM_WEAK_X)
640 rq2.geom.x+=xgravity_deltax(gravity, -off.x, off.x+off.w);
642 if(rq->flags®ION_RQGEOM_WEAK_Y)
645 rq2.geom.y+=xgravity_deltay(gravity, -off.y, off.y+off.h);
647 region_rqgeom((WRegion*)frame, &rq2, geomret);
662 /*{{{ Frame recreate pholder stuff */
665 static WFramedPHolder *frame_make_recreate_pholder(WFrame *frame)
669 WFramedParam fparam=FRAMEDPARAM_INIT;
671 ph=region_make_return_pholder((WRegion*)frame);
676 fparam.mode=frame->mode;
678 fph=create_framedpholder(ph, &fparam);
681 destroy_obj((Obj*)ph);
689 static void mplex_flatten_phs(WMPlex *mplex)
694 FOR_ALL_NODES_ON_LLIST(node, mplex->mx_list, tmp){
695 WMPlexPHolder *last=(mplex->mx_phs==NULL ? NULL : mplex->mx_phs->prev);
696 mplex_move_phs(mplex, node, last, NULL);
701 static void frame_modify_pholders(WFrame *frame)
704 WMPlexPHolder *phs, *ph;
706 mplex_flatten_phs(&frame->mplex);
708 if(frame->mplex.mx_phs==NULL)
711 fph=frame_make_recreate_pholder(frame);
716 phs=frame->mplex.mx_phs;
717 frame->mplex.mx_phs=NULL;
719 phs->recreate_pholder=fph;
721 for(ph=phs; ph!=NULL; ph=ph->next)
722 watch_reset(&ph->mplex_watch);
732 bool frame_set_shaded(WFrame *frame, int sp)
734 bool set=(frame->flags&FRAME_SHADED);
735 bool nset=libtu_do_setparam(sp, set);
736 WRQGeomParams rq=RQGEOMPARAMS_INIT;
743 rq.flags=REGION_RQGEOM_H_ONLY;
744 rq.geom=REGION_GEOM(frame);
747 if(!(frame->flags&FRAME_SAVED_VERT))
749 rq.geom.h=frame->saved_h;
751 if(frame->barmode==FRAME_BAR_NONE){
753 }else if(frame->barmode==FRAME_BAR_SHAPED){
754 rq.geom.h=frame->bar_h;
758 frame_border_inner_geom(frame, &tmp);
760 rq.geom.h=rq.geom.h-tmp.h;
764 frame->flags|=FRAME_SHADED_TOGGLE;
766 region_rqgeom((WRegion*)frame, &rq, NULL);
768 frame->flags&=~FRAME_SHADED_TOGGLE;
770 return (frame->flags&FRAME_SHADED);
775 * Set shading state according to the parameter \var{how}
776 * (set/unset/toggle). Resulting state is returned, which may not be
777 * what was requested.
779 EXTL_EXPORT_AS(WFrame, set_shaded)
780 bool frame_set_shaded_extl(WFrame *frame, const char *how)
782 return frame_set_shaded(frame, libtu_string_to_setparam(how));
787 * Is \var{frame} shaded?
791 bool frame_is_shaded(WFrame *frame)
793 return ((frame->flags&FRAME_SHADED)!=0);
797 bool frame_set_numbers(WFrame *frame, int sp)
799 bool set=frame->flags&FRAME_SHOW_NUMBERS;
800 bool nset=libtu_do_setparam(sp, set);
803 frame->flags^=FRAME_SHOW_NUMBERS;
804 frame_recalc_bar(frame);
805 frame_draw_bar(frame, TRUE);
808 return frame->flags&FRAME_SHOW_NUMBERS;
813 * Control whether tabs show numbers (set/unset/toggle).
814 * Resulting state is returned, which may not be what was
817 EXTL_EXPORT_AS(WFrame, set_numbers)
818 bool frame_set_numbers_extl(WFrame *frame, const char *how)
820 return frame_set_numbers(frame, libtu_string_to_setparam(how));
825 * Does \var{frame} show numbers for tabs?
827 bool frame_is_numbers(WFrame *frame)
829 return frame->flags&FRAME_SHOW_NUMBERS;
833 void frame_managed_notify(WFrame *frame, WRegion *sub, WRegionNotify how)
835 frame_update_attrs(frame);
836 frame_recalc_bar(frame);
837 frame_draw_bar(frame, FALSE);
841 static void frame_size_changed_default(WFrame *frame,
842 bool wchg, bool hchg)
844 int bar_w=frame->bar_w;
847 frame_recalc_bar(frame);
849 if(frame->barmode==FRAME_BAR_SHAPED &&
850 ((!wchg && hchg) || (wchg && bar_w==frame->bar_w))){
851 frame_set_shape(frame);
856 static void frame_managed_changed(WFrame *frame, int mode, bool sw,
861 if(mode!=MPLEX_CHANGE_SWITCHONLY)
862 frame_initialise_titles(frame);
864 frame_update_attrs(frame);
867 need_draw=!frame_set_background(frame, FALSE);
870 frame_draw_bar(frame, mode!=MPLEX_CHANGE_SWITCHONLY);
872 mplex_call_changed_hook((WMPlex*)frame,
873 frame_managed_changed_hook,
878 #define EMPTY_AND_SHOULD_BE_DESTROYED(FRAME) \
879 (DEST_EMPTY(frame) && FRAME_MCOUNT(FRAME)==0 && \
880 !OBJ_IS_BEING_DESTROYED(frame))
883 static void frame_destroy_empty(WFrame *frame)
885 if(EMPTY_AND_SHOULD_BE_DESTROYED(frame)){
886 frame_modify_pholders(frame);
887 destroy_obj((Obj*)frame);
892 void frame_managed_remove(WFrame *frame, WRegion *reg)
894 mplex_managed_remove((WMPlex*)frame, reg);
895 if(EMPTY_AND_SHOULD_BE_DESTROYED(frame)){
896 mainloop_defer_action((Obj*)frame,
897 (WDeferredAction*)frame_destroy_empty);
902 int frame_default_index(WFrame *frame)
904 return ioncore_g.frame_default_index;
914 ExtlTab frame_get_configuration(WFrame *frame)
916 ExtlTab tab=mplex_get_configuration(&frame->mplex);
918 extl_table_sets_i(tab, "mode", frame->mode);
920 if(frame->flags&FRAME_SAVED_VERT){
921 extl_table_sets_i(tab, "saved_y", frame->saved_y);
922 extl_table_sets_i(tab, "saved_h", frame->saved_h);
925 if(frame->flags&FRAME_SAVED_HORIZ){
926 extl_table_sets_i(tab, "saved_x", frame->saved_x);
927 extl_table_sets_i(tab, "saved_w", frame->saved_w);
935 void frame_do_load(WFrame *frame, ExtlTab tab)
940 if(extl_table_gets_i(tab, "saved_x", &p) &&
941 extl_table_gets_i(tab, "saved_w", &s)){
944 frame->flags|=FRAME_SAVED_HORIZ;
947 if(extl_table_gets_i(tab, "saved_y", &p) &&
948 extl_table_gets_i(tab, "saved_h", &s)){
951 frame->flags|=FRAME_SAVED_VERT;
954 mplex_load_contents(&frame->mplex, tab);
958 WRegion *frame_load(WWindow *par, const WFitParams *fp, ExtlTab tab)
960 int mode=FRAME_MODE_UNKNOWN;
963 if(!extl_table_gets_i(tab, "mode", &mode)){
964 #warning "TODO: Remove backwards compatibility hack"
966 if(extl_table_gets_s(tab, "frame_style", &style)){
967 if(strcmp(style, "frame-tiled")==0)
968 mode=FRAME_MODE_TILED;
969 else if(strcmp(style, "frame-floating")==0)
970 mode=FRAME_MODE_FLOATING;
971 else if(strcmp(style, "frame-transientcontainer")==0)
972 mode=FRAME_MODE_TRANSIENT;
977 frame=create_frame(par, fp, mode);
980 frame_do_load(frame, tab);
982 return (WRegion*)frame;
989 /*{{{ Dynfuntab and class info */
992 static DynFunTab frame_dynfuntab[]={
993 {region_size_hints, frame_size_hints},
995 {mplex_managed_changed, frame_managed_changed},
996 {mplex_size_changed, frame_size_changed_default},
997 {region_managed_notify, frame_managed_notify},
999 {region_activated, frame_activated},
1000 {region_inactivated, frame_inactivated},
1002 {(DynFun*)window_press, (DynFun*)frame_press},
1004 {(DynFun*)region_get_configuration,
1005 (DynFun*)frame_get_configuration},
1010 {mplex_managed_geom,
1011 frame_managed_geom},
1016 {(DynFun*)region_fitrep,
1017 (DynFun*)frame_fitrep},
1019 {region_managed_rqgeom_absolute,
1020 frame_managed_rqgeom_absolute},
1022 {region_managed_remove, frame_managed_remove},
1024 {(DynFun*)mplex_default_index,
1025 (DynFun*)frame_default_index},
1032 IMPLCLASS(WFrame, WMPlex, frame_deinit, frame_dynfuntab);