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/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"
44 extern bool frame_set_background(WFrame *frame, bool set_always);
45 extern void frame_initialise_gr(WFrame *frame);
47 static bool frame_initialise_titles(WFrame *frame);
48 static void frame_free_titles(WFrame *frame);
50 static void frame_add_mode_bindmaps(WFrame *frame);
53 WHook *frame_managed_changed_hook=NULL;
55 #define IS_FLOATING_MODE(FRAME) \
56 ((FRAME)->mode==FRAME_MODE_FLOATING || (FRAME)->mode==FRAME_MODE_TRANSIENT)
57 #define FORWARD_CWIN_RQGEOM(FRAME) IS_FLOATING_MODE(FRAME)
58 #define USE_MINMAX(FRAME) IS_FLOATING_MODE(FRAME)
59 #define DEST_EMPTY(FRAME) IS_FLOATING_MODE(FRAME)
62 /*{{{ Destroy/create frame */
65 bool frame_init(WFrame *frame, WWindow *parent, const WFitParams *fp,
75 frame->tab_dragged_idx=-1;
80 frame->tr_mode=GR_TRANSPARENCY_DEFAULT;
82 frame->bar_brush=NULL;
85 frame->bar_max_width_q=1.0;
87 if(!mplex_init((WMPlex*)frame, parent, fp))
90 frame_initialise_gr(frame);
91 frame_initialise_titles(frame);
93 region_add_bindmap((WRegion*)frame, ioncore_frame_bindmap);
94 region_add_bindmap((WRegion*)frame, ioncore_frame_toplevel_bindmap);
96 frame_add_mode_bindmaps(frame);
98 mplex_managed_geom((WMPlex*)frame, &mg);
101 frame->flags|=FRAME_SHADED;
103 ((WRegion*)frame)->flags|=REGION_PLEASE_WARP;
109 WFrame *create_frame(WWindow *parent, const WFitParams *fp, WFrameMode mode)
111 CREATEOBJ_IMPL(WFrame, frame, (p, parent, fp, mode));
115 void frame_deinit(WFrame *frame)
117 frame_free_titles(frame);
118 frame_release_brushes(frame);
119 mplex_deinit((WMPlex*)frame);
126 /*{{{ Mode switching */
129 static void frame_add_mode_bindmaps(WFrame *frame)
131 WFrameMode mode=frame->mode;
133 if(mode==FRAME_MODE_TILED || mode==FRAME_MODE_TILED_ALT){
134 region_add_bindmap((WRegion*)frame, ioncore_frame_toplevel_bindmap);
135 region_add_bindmap((WRegion*)frame, ioncore_frame_tiled_bindmap);
136 }else if(mode==FRAME_MODE_FLOATING){
137 region_add_bindmap((WRegion*)frame, ioncore_frame_toplevel_bindmap);
138 region_add_bindmap((WRegion*)frame, ioncore_frame_floating_bindmap);
139 }else if(mode==FRAME_MODE_TRANSIENT){
140 region_add_bindmap((WRegion*)frame, ioncore_frame_transient_bindmap);
145 void frame_set_mode(WFrame *frame, WFrameMode mode)
147 if(frame->mode==mode)
150 frame_clear_shape(frame);
152 frame_release_brushes(frame);
154 region_remove_bindmap((WRegion*)frame, ioncore_frame_toplevel_bindmap);
155 region_remove_bindmap((WRegion*)frame, ioncore_frame_tiled_bindmap);
156 region_remove_bindmap((WRegion*)frame, ioncore_frame_floating_bindmap);
157 region_remove_bindmap((WRegion*)frame, ioncore_frame_transient_bindmap);
161 frame_add_mode_bindmaps(frame);
163 frame_initialise_gr(frame);
165 mplex_fit_managed(&frame->mplex);
166 frame_recalc_bar(frame);
167 frame_set_background(frame, TRUE);
171 WFrameMode frame_mode(WFrame *frame)
177 StringIntMap frame_modes[]={
178 {"tiled", FRAME_MODE_TILED},
179 {"tiled-alt", FRAME_MODE_TILED_ALT},
180 {"floating", FRAME_MODE_FLOATING},
181 {"transient", FRAME_MODE_TRANSIENT},
189 EXTL_EXPORT_AS(WFrame, mode)
190 const char *frame_mode_extl(WFrame *frame)
192 return stringintmap_key(frame_modes, frame->mode, NULL);
199 EXTL_EXPORT_AS(WFrame, set_mode)
200 bool frame_set_mode_extl(WFrame *frame, const char *modestr)
205 idx=stringintmap_ndx(frame_modes, modestr);
209 frame_set_mode(frame, frame_modes[idx].value);
221 int frame_tab_at_x(WFrame *frame, int x)
226 frame_bar_geom(frame, &bg);
228 if(x>=bg.x+bg.w || x<bg.x)
233 for(tab=0; tab<FRAME_MCOUNT(frame); tab++){
234 tx+=frame_nth_tab_w(frame, tab);
243 int frame_nth_tab_x(WFrame *frame, int n)
249 x+=frame_nth_tab_w(frame, i);
255 static int frame_nth_tab_w_iw(WFrame *frame, int n, bool inner)
258 GrBorderWidths bdw=GR_BORDER_WIDTHS_INIT;
259 int m=FRAME_MCOUNT(frame);
262 frame_bar_geom(frame, &bg);
267 if(frame->bar_brush!=NULL)
268 grbrush_get_border_widths(frame->bar_brush, &bdw);
271 w=bg.w-bdw.left-bdw.right-(bdw.tb_ileft+bdw.tb_iright+bdw.spacing)*(m-1);
276 /* Get n:th tab's portion of free area */
277 w=(((n+1)*w)/m-(n*w)/m);
279 /* Add n:th tab's borders back */
281 w+=(n==0 ? bdw.left : bdw.tb_ileft);
282 w+=(n==m-1 ? bdw.right : bdw.tb_iright+bdw.spacing);
289 int frame_nth_tab_w(WFrame *frame, int n)
291 return frame_nth_tab_w_iw(frame, n, FALSE);
295 int frame_nth_tab_iw(WFrame *frame, int n)
297 return frame_nth_tab_w_iw(frame, n, TRUE);
302 static void update_attr(WFrame *frame, int i, WRegion *reg)
305 static char *attrs[]={
306 "unselected-not_tagged-not_dragged-no_activity",
307 "selected-not_tagged-not_dragged-no_activity",
308 "unselected-tagged-not_dragged-no_activity",
309 "selected-tagged-not_dragged-no_activity",
310 "unselected-not_tagged-dragged-no_activity",
311 "selected-not_tagged-dragged-no_activity",
312 "unselected-tagged-dragged-no_activity",
313 "selected-tagged-dragged-no_activity",
314 "unselected-not_tagged-not_dragged-activity",
315 "selected-not_tagged-not_dragged-activity",
316 "unselected-tagged-not_dragged-activity",
317 "selected-tagged-not_dragged-activity",
318 "unselected-not_tagged-dragged-activity",
319 "selected-not_tagged-dragged-activity",
320 "unselected-tagged-dragged-activity",
321 "selected-tagged-dragged-activity"
324 if(i>=frame->titles_n){
325 /* Might happen when deinitialising */
329 if(reg==FRAME_CURRENT(frame))
331 if(reg!=NULL && reg->flags®ION_TAGGED)
333 if(i==frame->tab_dragged_idx)
335 if(reg!=NULL && region_is_activity_r(reg))
338 frame->titles[i].attr=attrs[flags];
342 void frame_update_attr_nth(WFrame *frame, int i)
346 if(i<0 || i>=frame->titles_n)
349 update_attr(frame, i, mplex_mx_nth((WMPlex*)frame, i));
353 static void update_attrs(WFrame *frame)
359 FRAME_MX_FOR_ALL(sub, frame, tmp){
360 update_attr(frame, i, sub);
366 static void frame_free_titles(WFrame *frame)
370 if(frame->titles!=NULL){
371 for(i=0; i<frame->titles_n; i++){
372 if(frame->titles[i].text)
373 free(frame->titles[i].text);
382 static void do_init_title(WFrame *frame, int i, WRegion *sub)
384 frame->titles[i].text=NULL;
385 frame->titles[i].iw=frame_nth_tab_iw(frame, i);
386 update_attr(frame, i, sub);
390 static bool frame_initialise_titles(WFrame *frame)
392 int i, n=FRAME_MCOUNT(frame);
394 frame_free_titles(frame);
399 frame->titles=ALLOC_N(GrTextElem, n);
400 if(frame->titles==NULL)
404 if(FRAME_MCOUNT(frame)==0){
405 do_init_title(frame, 0, NULL);
410 FRAME_MX_FOR_ALL(sub, frame, tmp){
411 do_init_title(frame, i, sub);
416 frame_recalc_bar(frame);
425 /*{{{ Resize and reparent */
428 bool frame_fitrep(WFrame *frame, WWindow *par, const WFitParams *fp)
430 WRectangle old_geom, mg;
431 bool wchg=(REGION_GEOM(frame).w!=fp->g.w);
432 bool hchg=(REGION_GEOM(frame).h!=fp->g.h);
434 old_geom=REGION_GEOM(frame);
436 window_do_fitrep(&(frame->mplex.win), par, &(fp->g));
438 mplex_managed_geom((WMPlex*)frame, &mg);
442 frame->flags|=(FRAME_SHADED|FRAME_SAVED_VERT);
443 frame->saved_y=old_geom.y;
444 frame->saved_h=old_geom.h;
446 frame->flags&=~FRAME_SHADED;
448 frame->flags&=~FRAME_MAXED_VERT;
453 frame->flags|=(FRAME_MIN_HORIZ|FRAME_SAVED_HORIZ);
454 frame->saved_x=old_geom.x;
455 frame->saved_w=old_geom.w;
457 frame->flags&=~FRAME_MIN_HORIZ;
459 frame->flags&=~FRAME_MAXED_HORIZ;
463 mplex_fit_managed((WMPlex*)frame);
464 mplex_size_changed((WMPlex*)frame, wchg, hchg);
471 void frame_size_hints(WFrame *frame, WSizeHints *hints_ret)
478 mplex_managed_geom((WMPlex*)frame, &subgeom);
480 woff=maxof(REGION_GEOM(frame).w-subgeom.w, 0);
481 hoff=maxof(REGION_GEOM(frame).h-subgeom.h, 0);
483 if(FRAME_CURRENT(frame)!=NULL){
484 region_size_hints(FRAME_CURRENT(frame), hints_ret);
485 if(!USE_MINMAX(frame)){
486 hints_ret->max_set=0;
487 hints_ret->min_set=0;
488 hints_ret->base_set=0;
489 hints_ret->aspect_set=0;
490 hints_ret->no_constrain=FALSE;
491 /*hints_ret->no_constrain=TRUE;*/
494 sizehints_clear(hints_ret);
497 FRAME_MX_FOR_ALL(sub, frame, tmp){
498 sizehints_adjust_for(hints_ret, sub);
501 if(!hints_ret->base_set){
502 hints_ret->base_width=0;
503 hints_ret->base_height=0;
504 hints_ret->base_set=TRUE;
507 if(!hints_ret->min_set){
508 hints_ret->min_width=0;
509 hints_ret->min_height=0;
510 hints_ret->min_set=TRUE;
513 hints_ret->base_width+=woff;
514 hints_ret->base_height+=hoff;
515 hints_ret->max_width+=woff;
516 hints_ret->max_height+=hoff;
517 hints_ret->min_width+=woff;
518 hints_ret->min_height+=hoff;
520 if(frame->barmode==FRAME_BAR_SHAPED){
521 int f=frame->flags&(FRAME_SHADED|FRAME_SHADED_TOGGLE);
523 if(f==FRAME_SHADED || f==FRAME_SHADED_TOGGLE){
524 hints_ret->min_height=frame->bar_h;
525 hints_ret->max_height=frame->bar_h;
526 hints_ret->base_height=frame->bar_h;
527 if(!hints_ret->max_set){
528 hints_ret->max_width=INT_MAX;
529 hints_ret->max_set=TRUE;
542 void frame_inactivated(WFrame *frame)
544 window_draw((WWindow*)frame, FALSE);
548 void frame_activated(WFrame *frame)
550 window_draw((WWindow*)frame, FALSE);
557 /*{{{ Client window rqgeom */
560 static void frame_managed_rqgeom_absolute(WFrame *frame, WRegion *sub,
561 const WRQGeomParams *rq,
564 if(!FORWARD_CWIN_RQGEOM(frame)){
565 region_managed_rqgeom_absolute_default((WRegion*)frame, sub,
568 WRQGeomParams rq2=RQGEOMPARAMS_INIT;
569 int gravity=ForgetGravity;
574 rq2.flags=rq->flags&(REGION_RQGEOM_WEAK_ALL
575 |REGION_RQGEOM_TRYONLY
576 |REGION_RQGEOM_ABSOLUTE);
578 if(rq->flags®ION_RQGEOM_GRAVITY)
581 mplex_managed_geom(&frame->mplex, &off);
584 off.w=REGION_GEOM(frame).w-off.w;
585 off.h=REGION_GEOM(frame).h-off.h;
587 rq2.geom.w=maxof(rq2.geom.w+off.w, 0);
588 rq2.geom.h=maxof(rq2.geom.h+off.h, 0);
590 /*region_size_hints_correct((WRegion*)frame, &(geom.w), &(geom.h), TRUE);*/
592 /* If WEAK_? is set, then geom.(x|y) is root-relative as it was not
593 * requested by the client and clientwin_handle_configure_request has
594 * no better guess. Otherwise the coordinates are those requested by
595 * the client (modulo borders/gravity) and we interpret them to be
596 * root-relative coordinates for this frame modulo gravity.
598 if(rq->flags®ION_RQGEOM_WEAK_X)
601 rq2.geom.x+=xgravity_deltax(gravity, -off.x, off.x+off.w);
603 if(rq->flags®ION_RQGEOM_WEAK_Y)
606 rq2.geom.y+=xgravity_deltay(gravity, -off.y, off.y+off.h);
608 region_rqgeom((WRegion*)frame, &rq2, geomret);
626 bool frame_set_shaded(WFrame *frame, int sp)
628 bool set=(frame->flags&FRAME_SHADED);
629 bool nset=libtu_do_setparam(sp, set);
630 WRQGeomParams rq=RQGEOMPARAMS_INIT;
637 rq.flags=REGION_RQGEOM_H_ONLY;
638 rq.geom=REGION_GEOM(frame);
641 if(!(frame->flags&FRAME_SAVED_VERT))
643 rq.geom.h=frame->saved_h;
645 if(frame->barmode==FRAME_BAR_NONE){
647 }else if(frame->barmode==FRAME_BAR_SHAPED){
648 rq.geom.h=frame->bar_h;
652 frame_border_inner_geom(frame, &tmp);
654 rq.geom.h=rq.geom.h-tmp.h;
658 frame->flags|=FRAME_SHADED_TOGGLE;
660 region_rqgeom((WRegion*)frame, &rq, NULL);
662 frame->flags&=~FRAME_SHADED_TOGGLE;
664 return (frame->flags&FRAME_SHADED);
669 * Set shading state according to the parameter \var{how}
670 * (set/unset/toggle). Resulting state is returned, which may not be
671 * what was requested.
673 EXTL_EXPORT_AS(WFrame, set_shaded)
674 bool frame_set_shaded_extl(WFrame *frame, const char *how)
676 return frame_set_shaded(frame, libtu_string_to_setparam(how));
681 * Is \var{frame} shaded?
685 bool frame_is_shaded(WFrame *frame)
687 return ((frame->flags&FRAME_SHADED)!=0);
691 bool frame_set_numbers(WFrame *frame, int sp)
693 bool set=frame->flags&FRAME_SHOW_NUMBERS;
694 bool nset=libtu_do_setparam(sp, set);
697 frame->flags^=FRAME_SHOW_NUMBERS;
698 frame_recalc_bar(frame);
699 frame_draw_bar(frame, TRUE);
702 return frame->flags&FRAME_SHOW_NUMBERS;
707 * Control whether tabs show numbers (set/unset/toggle).
708 * Resulting state is returned, which may not be what was
711 EXTL_EXPORT_AS(WFrame, set_numbers)
712 bool frame_set_numbers_extl(WFrame *frame, const char *how)
714 return frame_set_numbers(frame, libtu_string_to_setparam(how));
719 * Does \var{frame} show numbers for tabs?
721 bool frame_is_numbers(WFrame *frame)
723 return frame->flags&FRAME_SHOW_NUMBERS;
727 void frame_managed_notify(WFrame *frame, WRegion *sub, const char *how)
730 frame_recalc_bar(frame);
731 frame_draw_bar(frame, FALSE);
735 static void frame_size_changed_default(WFrame *frame,
736 bool wchg, bool hchg)
738 int bar_w=frame->bar_w;
741 frame_recalc_bar(frame);
743 if(frame->barmode==FRAME_BAR_SHAPED &&
744 ((!wchg && hchg) || (wchg && bar_w==frame->bar_w))){
745 frame_set_shape(frame);
750 static void frame_managed_changed(WFrame *frame, int mode, bool sw,
755 if(mode!=MPLEX_CHANGE_SWITCHONLY)
756 frame_initialise_titles(frame);
761 need_draw=!frame_set_background(frame, FALSE);
764 frame_draw_bar(frame, mode!=MPLEX_CHANGE_SWITCHONLY);
766 mplex_call_changed_hook((WMPlex*)frame,
767 frame_managed_changed_hook,
772 #define EMPTY_AND_SHOULD_BE_DESTROYED(FRAME) \
773 (DEST_EMPTY(frame) && FRAME_MCOUNT(FRAME)==0 && \
774 !OBJ_IS_BEING_DESTROYED(frame))
777 static void frame_destroy_empty(WFrame *frame)
779 if(EMPTY_AND_SHOULD_BE_DESTROYED(frame))
780 destroy_obj((Obj*)frame);
784 void frame_managed_remove(WFrame *frame, WRegion *reg)
786 mplex_managed_remove((WMPlex*)frame, reg);
787 if(EMPTY_AND_SHOULD_BE_DESTROYED(frame)){
788 mainloop_defer_action((Obj*)frame,
789 (WDeferredAction*)frame_destroy_empty);
794 int frame_default_index(WFrame *frame)
796 return ioncore_g.frame_default_index;
806 ExtlTab frame_get_configuration(WFrame *frame)
808 ExtlTab tab=mplex_get_configuration(&frame->mplex);
810 extl_table_sets_i(tab, "mode", frame->mode);
812 if(frame->flags&FRAME_SAVED_VERT){
813 extl_table_sets_i(tab, "saved_y", frame->saved_y);
814 extl_table_sets_i(tab, "saved_h", frame->saved_h);
817 if(frame->flags&FRAME_SAVED_HORIZ){
818 extl_table_sets_i(tab, "saved_x", frame->saved_x);
819 extl_table_sets_i(tab, "saved_w", frame->saved_w);
827 void frame_do_load(WFrame *frame, ExtlTab tab)
832 if(extl_table_gets_i(tab, "saved_x", &p) &&
833 extl_table_gets_i(tab, "saved_w", &s)){
836 frame->flags|=FRAME_SAVED_HORIZ;
839 if(extl_table_gets_i(tab, "saved_y", &p) &&
840 extl_table_gets_i(tab, "saved_h", &s)){
843 frame->flags|=FRAME_SAVED_VERT;
846 mplex_load_contents(&frame->mplex, tab);
850 WRegion *frame_load(WWindow *par, const WFitParams *fp, ExtlTab tab)
852 int mode=FRAME_MODE_UNKNOWN;
855 if(!extl_table_gets_i(tab, "mode", &mode)){
856 #warning "TODO: Remove backwards compatibility hack"
858 if(extl_table_gets_s(tab, "frame_style", &style)){
859 if(strcmp(style, "frame-tiled")==0)
860 mode=FRAME_MODE_TILED;
861 else if(strcmp(style, "frame-floating")==0)
862 mode=FRAME_MODE_FLOATING;
863 else if(strcmp(style, "frame-transientcontainer")==0)
864 mode=FRAME_MODE_TRANSIENT;
869 frame=create_frame(par, fp, mode);
872 frame_do_load(frame, tab);
874 return (WRegion*)frame;
881 /*{{{ Dynfuntab and class info */
884 static DynFunTab frame_dynfuntab[]={
885 {region_size_hints, frame_size_hints},
887 {mplex_managed_changed, frame_managed_changed},
888 {mplex_size_changed, frame_size_changed_default},
889 {region_managed_notify, frame_managed_notify},
891 {region_activated, frame_activated},
892 {region_inactivated, frame_inactivated},
894 {(DynFun*)window_press, (DynFun*)frame_press},
896 {(DynFun*)region_get_configuration,
897 (DynFun*)frame_get_configuration},
908 {(DynFun*)region_fitrep,
909 (DynFun*)frame_fitrep},
911 {region_managed_rqgeom_absolute,
912 frame_managed_rqgeom_absolute},
914 {region_managed_remove, frame_managed_remove},
916 {(DynFun*)mplex_default_index,
917 (DynFun*)frame_default_index},
924 IMPLCLASS(WFrame, WMPlex, frame_deinit, frame_dynfuntab);