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/minmax.h>
15 #include <libtu/objp.h>
16 #include <libmainloop/defer.h>
33 #include "sizepolicy.h"
40 #include "grouppholder.h"
42 #include "float-placement.h"
46 static void group_place_stdisp(WGroup *ws, WWindow *parent,
47 int pos, WRegion *stdisp);
49 static void group_remanage_stdisp(WGroup *ws);
52 /*{{{ Stacking list stuff */
55 WStacking *group_get_stacking(WGroup *ws)
57 WWindow *par=REGION_PARENT(ws);
61 : window_get_stacking(par));
65 WStacking **group_get_stackingp(WGroup *ws)
67 WWindow *par=REGION_PARENT(ws);
71 : window_get_stackingp(par));
75 static bool wsfilt(WStacking *st, void *ws)
77 return (st->reg!=NULL && REGION_MANAGER(st->reg)==(WRegion*)ws);
81 static bool wsfilt_nostdisp(WStacking *st, void *ws)
83 return (wsfilt(st, ws) && ((WGroup*)ws)->managed_stdisp!=st);
87 void group_iter_init(WGroupIterTmp *tmp, WGroup *ws)
89 stacking_iter_mgr_init(tmp, ws->managed_list, NULL, ws);
93 void group_iter_init_nostdisp(WGroupIterTmp *tmp, WGroup *ws)
95 stacking_iter_mgr_init(tmp, ws->managed_list, wsfilt_nostdisp, ws);
99 WRegion *group_iter(WGroupIterTmp *tmp)
101 return stacking_iter_mgr(tmp);
105 WStacking *group_iter_nodes(WGroupIterTmp *tmp)
107 return stacking_iter_mgr_nodes(tmp);
111 WGroupIterTmp group_iter_default_tmp;
117 /*{{{ region dynfun implementations */
120 static void group_fit(WGroup *ws, const WRectangle *geom)
122 REGION_GEOM(ws)=*geom;
126 bool group_fitrep(WGroup *ws, WWindow *par, const WFitParams *fp)
129 WStacking *unweaved=NULL;
130 int xdiff=0, ydiff=0;
135 oldpar=REGION_PARENT(ws);
138 if(fp->mode®ION_FIT_WHATEVER)
140 REGION_GEOM(ws)=fp->g;
142 if(!region_same_rootwin((WRegion*)ws, (WRegion*)par))
145 if(ws->managed_stdisp!=NULL && ws->managed_stdisp->reg!=NULL)
146 region_detach_manager(ws->managed_stdisp->reg);
148 assert(ws->managed_stdisp==NULL);
150 xdiff=fp->g.x-REGION_GEOM(ws).x;
151 ydiff=fp->g.y-REGION_GEOM(ws).y;
153 region_unset_parent((WRegion*)ws);
154 XReparentWindow(ioncore_g.dpy, ws->dummywin, par->win, -1, -1);
155 region_set_parent((WRegion*)ws, par);
157 REGION_GEOM(ws).x=fp->g.x;
158 REGION_GEOM(ws).y=fp->g.y;
159 if(!(fp->mode®ION_FIT_WHATEVER)){
160 REGION_GEOM(ws).w=fp->g.w;
161 REGION_GEOM(ws).h=fp->g.h;
165 unweaved=stacking_unweave(&oldpar->stacking, wsfilt, (void*)ws);
168 FOR_ALL_NODES_IN_GROUP(ws, st, tmp){
174 g=REGION_GEOM(st->reg);
178 if(fp->mode®ION_FIT_WHATEVER){
181 fp2.g=REGION_GEOM(ws);
182 sizepolicy(&st->szplcy, st->reg, &g, REGION_RQGEOM_WEAK_ALL, &fp2);
185 if(!region_fitrep(st->reg, par, &fp2)){
186 warn(TR("Error reparenting %s."), region_name(st->reg));
187 region_detach_manager(st->reg);
192 stacking_weave(&par->stacking, &unweaved, FALSE);
198 static void group_map(WGroup *ws)
203 REGION_MARK_MAPPED(ws);
204 XMapWindow(ioncore_g.dpy, ws->dummywin);
206 FOR_ALL_MANAGED_BY_GROUP(ws, reg, tmp){
212 static void group_unmap(WGroup *ws)
217 REGION_MARK_UNMAPPED(ws);
218 XUnmapWindow(ioncore_g.dpy, ws->dummywin);
220 FOR_ALL_MANAGED_BY_GROUP(ws, reg, tmp){
226 static WStacking *find_to_focus(WGroup *ws, WStacking *st, bool group_only)
228 WStacking *stacking=group_get_stacking(ws);
233 return stacking_find_to_focus_mapped(stacking, st,
234 (group_only ? (WRegion*)ws : NULL));
238 static bool group_refocus_(WGroup *ws, WStacking *st)
240 if(st!=ws->current_managed && st->reg!=NULL){
241 if(region_may_control_focus((WRegion*)ws))
242 region_set_focus(st->reg);
244 ws->current_managed=st;
252 static void group_do_set_focus(WGroup *ws, bool warp)
254 WStacking *st=ws->current_managed;
256 if(st==NULL || st->reg==NULL)
257 st=find_to_focus(ws, NULL, TRUE);
259 if(st!=NULL && st->reg!=NULL)
260 region_do_set_focus(st->reg, warp);
262 region_finalise_focusing((WRegion*)ws, ws->dummywin, warp);
266 static bool group_managed_prepare_focus(WGroup *ws, WRegion *reg,
267 int flags, WPrepareFocusResult *res)
269 WMPlex *mplex=OBJ_CAST(REGION_MANAGER(ws), WMPlex);
270 WStacking *st=group_find_stacking(ws, reg);
276 WStacking *node=mplex_find_stacking(mplex, (WRegion*)ws);
281 return mplex_do_prepare_focus(mplex, node, st,
284 if(!region_prepare_focus((WRegion*)ws, flags, res))
287 st=find_to_focus(ws, st, FALSE);
292 if(ioncore_g.autoraise &&
293 !(flags®ION_GOTO_ENTERWINDOW) &&
294 st->level>STACKING_LEVEL_BOTTOM){
295 WStacking **stackingp=group_get_stackingp(ws);
296 stacking_restack(stackingp, st, None, NULL, NULL, FALSE);
302 return (res->reg==reg);
307 void group_managed_remove(WGroup *ws, WRegion *reg)
309 bool mcf=region_may_control_focus((WRegion*)ws);
310 WStacking *st, *next_st=NULL;
311 bool was_stdisp=FALSE, was_bottom=FALSE;
312 bool was_current=FALSE;
314 st=group_find_stacking(ws, reg);
317 next_st=stacking_unstack(REGION_PARENT(ws), st);
319 UNLINK_ITEM(ws->managed_list, st, mgr_next, mgr_prev);
321 if(st==ws->managed_stdisp){
322 ws->managed_stdisp=NULL;
331 if(st==ws->current_managed){
332 ws->current_managed=NULL;
336 stacking_unassoc(st);
340 region_unset_manager(reg, (WRegion*)ws);
342 if(!OBJ_IS_BEING_DESTROYED(ws)){
343 if(was_bottom && !was_stdisp && ws->managed_stdisp==NULL){
344 /* We should probably be managing any stdisp, that 'bottom'
347 group_remanage_stdisp(ws);
351 /* This may still potentially cause problems when focus
352 * change is pending. Perhaps we should use region_await_focus,
353 * if it is pointing to our child (and region_may_control_focus
354 * fail if it is pointing somewhere else).
356 WStacking *stf=find_to_focus(ws, next_st, TRUE);
357 if(stf!=NULL && mcf){
358 region_maybewarp_now(stf->reg, FALSE);
360 ws->current_managed=stf;
367 void group_managed_notify(WGroup *ws, WRegion *reg, WRegionNotify how)
369 if(how==ioncore_g.notifies.activated ||
370 how==ioncore_g.notifies.pseudoactivated){
371 ws->current_managed=group_find_stacking(ws, reg);
379 /*{{{ Create/destroy */
382 bool group_init(WGroup *ws, WWindow *par, const WFitParams *fp)
384 ws->current_managed=NULL;
385 ws->managed_stdisp=NULL;
387 ws->managed_list=NULL;
389 ws->dummywin=XCreateWindow(ioncore_g.dpy, par->win,
390 fp->g.x, fp->g.y, 1, 1, 0,
391 CopyFromParent, InputOnly,
392 CopyFromParent, 0, NULL);
393 if(ws->dummywin==None)
396 region_init(&ws->reg, par, fp);
397 region_register(&ws->reg);
399 XSelectInput(ioncore_g.dpy, ws->dummywin,
400 FocusChangeMask|KeyPressMask|KeyReleaseMask|
401 ButtonPressMask|ButtonReleaseMask);
402 XSaveContext(ioncore_g.dpy, ws->dummywin, ioncore_g.win_context,
405 ((WRegion*)ws)->flags|=REGION_GRAB_ON_PARENT;
407 region_add_bindmap((WRegion*)ws, ioncore_group_bindmap);
413 WGroup *create_group(WWindow *par, const WFitParams *fp)
415 CREATEOBJ_IMPL(WGroup, group, (p, par, fp));
419 void group_deinit(WGroup *ws)
424 if(ws->managed_stdisp!=NULL && ws->managed_stdisp->reg!=NULL){
425 group_managed_remove(ws, ws->managed_stdisp->reg);
426 assert(ws->managed_stdisp==NULL);
429 FOR_ALL_MANAGED_BY_GROUP(ws, reg, tmp){
430 destroy_obj((Obj*)reg);
433 assert(ws->managed_list==NULL);
435 XDeleteContext(ioncore_g.dpy, ws->dummywin, ioncore_g.win_context);
436 XDestroyWindow(ioncore_g.dpy, ws->dummywin);
439 region_deinit(&ws->reg);
444 bool group_rescue_clientwins(WGroup *ws, WRescueInfo *info)
448 group_iter_init_nostdisp(&tmp, ws);
450 return region_rescue_some_clientwins((WRegion*)ws, info,
451 (WRegionIterator*)group_iter,
456 static bool group_empty_for_bottom_stdisp(WGroup *ws)
461 FOR_ALL_NODES_IN_GROUP(ws, st, tmp){
462 if(st!=ws->bottom && st!=ws->managed_stdisp)
470 static WRegion *group_managed_disposeroot(WGroup *ws, WRegion *reg)
472 if(group_bottom(ws)==reg){
473 if(group_empty_for_bottom_stdisp(ws))
474 return region_disposeroot((WRegion*)ws);
487 static void group_do_set_bottom(WGroup *grp, WStacking *st)
489 WStacking *was=grp->bottom;
494 if(st==NULL || HAS_DYN(st->reg, region_manage_stdisp))
495 group_remanage_stdisp(grp);
502 * Sets the 'bottom' of \var{ws}. The region \var{reg} must already
503 * be managed by \var{ws}, unless \code{nil}.
506 bool group_set_bottom(WGroup *ws, WRegion *reg)
511 st=group_find_stacking(ws, reg);
517 group_do_set_bottom(ws, st);
524 * Returns the 'bottom' of \var{ws}.
528 WRegion *group_bottom(WGroup *ws)
530 return (ws->bottom!=NULL ? ws->bottom->reg : NULL);
540 WStacking *group_do_add_managed(WGroup *ws, WRegion *reg, int level,
544 CALL_DYN_RET(st, WStacking*, group_do_add_managed, ws,
545 (ws, reg, level, szplcy));
550 WStacking *group_do_add_managed_default(WGroup *ws, WRegion *reg, int level,
553 WStacking *st=NULL, *tmp=NULL;
554 Window bottom=None, top=None;
555 WStacking **stackingp=group_get_stackingp(ws);
561 st=create_stacking();
566 if(!stacking_assoc(st, reg)){
571 frame=OBJ_CAST(reg, WFrame);
573 WFrameMode m=frame_mode(frame);
574 if(m!=FRAME_MODE_FLOATING && m!=FRAME_MODE_TRANSIENT)
575 frame_set_mode(frame, FRAME_MODE_FLOATING);
581 LINK_ITEM_FIRST(tmp, st, next, prev);
582 stacking_weave(stackingp, &tmp, FALSE);
585 LINK_ITEM(ws->managed_list, st, mgr_next, mgr_prev);
586 region_set_manager(reg, (WRegion*)ws);
588 if(region_is_fully_mapped((WRegion*)ws))
595 static void geom_group_to_parent(WGroup *ws, const WRectangle *g,
598 wg->x=g->x+REGION_GEOM(ws).x;
599 wg->y=g->y+REGION_GEOM(ws).y;
600 wg->w=maxof(1, g->w);
601 wg->h=maxof(1, g->h);
605 bool group_do_attach_final(WGroup *ws,
607 const WGroupAttachParams *param)
609 WStacking *st, *stabove=NULL;
618 if(param->stack_above!=NULL)
619 stabove=group_find_stacking(ws, param->stack_above);
625 : STACKING_LEVEL_NORMAL));
628 szplcy=(param->szplcy_set
631 ? SIZEPOLICY_FULL_EXACT
632 : SIZEPOLICY_UNCONSTRAINED));
634 weak=(param->geom_weak_set
638 : REGION_RQGEOM_WEAK_ALL));
641 geom_group_to_parent(ws, ¶m->geom, &g);
645 /* If the requested geometry does not overlap the workspaces's geometry,
646 * position request is never honoured.
648 if((g.x+g.w<=REGION_GEOM(ws).x) ||
649 (g.x>=REGION_GEOM(ws).x+REGION_GEOM(ws).w)){
650 weak|=REGION_RQGEOM_WEAK_X;
653 if((g.y+g.h<=REGION_GEOM(ws).y) ||
654 (g.y>=REGION_GEOM(ws).y+REGION_GEOM(ws).h)){
655 weak|=REGION_RQGEOM_WEAK_Y;
658 if(weak&(REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y) &&
659 (szplcy==SIZEPOLICY_UNCONSTRAINED ||
660 szplcy==SIZEPOLICY_FREE ||
661 szplcy==SIZEPOLICY_FREE_GLUE /* without flags */)){
662 /* TODO: use 'weak'? */
663 group_calc_placement(ws, level, &g);
666 fp.g=REGION_GEOM(ws);
667 fp.mode=REGION_FIT_EXACT;
669 sizepolicy(&szplcy, reg, &g, weak, &fp);
671 if(rectangle_compare(&fp.g, ®ION_GEOM(reg))!=RECTANGLE_SAME)
672 region_fitrep(reg, NULL, &fp);
675 st=group_do_add_managed(ws, reg, level, szplcy);
684 group_do_set_bottom(ws, st);
687 sw=(param->switchto_set ? param->switchto : ioncore_g.switchto_new);
689 if(sw || st->level>=STACKING_LEVEL_MODAL1){
690 WStacking *stf=find_to_focus(ws, st, FALSE);
693 /* Ok, the new region can be focused */
694 group_refocus_(ws, stf);
702 WRegion *group_do_attach(WGroup *ws,
703 /*const*/ WGroupAttachParams *param,
704 WRegionAttachData *data)
710 if(ws->bottom!=NULL && param->bottom){
711 warn(TR("'bottom' already set."));
715 par=REGION_PARENT(ws);
719 geom_group_to_parent(ws, ¶m->geom, &fp.g);
720 fp.mode=REGION_FIT_EXACT;
722 fp.g=REGION_GEOM(ws);
723 fp.mode=REGION_FIT_BOUNDS|REGION_FIT_WHATEVER;
726 return region_attach_helper((WRegion*) ws, par, &fp,
727 (WRegionDoAttachFn*)group_do_attach_final,
728 /*(const WRegionAttachParams*)*/param, data);
729 /* ^^^^ doesn't seem to work. */
733 static void get_params(WGroup *ws, ExtlTab tab, WGroupAttachParams *par)
745 if(extl_table_gets_i(tab, "level", &tmp)){
747 par->level_set=STACKING_LEVEL_NORMAL;
752 if(extl_table_is_bool_set(tab, "bottom")){
753 par->level=STACKING_LEVEL_BOTTOM;
758 if(!par->level_set && extl_table_is_bool_set(tab, "modal")){
759 par->level=STACKING_LEVEL_MODAL1;
763 if(extl_table_is_bool_set(tab, "switchto"))
766 if(extl_table_gets_i(tab, "sizepolicy", &tmp)){
769 }else if(extl_table_gets_s(tab, "sizepolicy", &tmps)){
770 if(string2sizepolicy(tmps, &par->szplcy))
775 if(extl_table_gets_t(tab, "geom", &g)){
778 if(extl_table_gets_i(g, "x", &(par->geom.x)))
780 if(extl_table_gets_i(g, "y", &(par->geom.y)))
782 if(extl_table_gets_i(g, "w", &(par->geom.w)))
784 if(extl_table_gets_i(g, "h", &(par->geom.h)))
797 * Attach and reparent existing region \var{reg} to \var{ws}.
798 * The table \var{param} may contain the fields \var{index} and
799 * \var{switchto} that are interpreted as for \fnref{WMPlex.attach_new}.
802 WRegion *group_attach(WGroup *ws, WRegion *reg, ExtlTab param)
804 WGroupAttachParams par=GROUPATTACHPARAMS_INIT;
805 WRegionAttachData data;
810 get_params(ws, param, &par);
812 data.type=REGION_ATTACH_REPARENT;
815 return group_do_attach(ws, &par, &data);
820 * Create a new region to be managed by \var{ws}. At least the following
821 * fields in \var{param} are understood:
823 * \begin{tabularx}{\linewidth}{lX}
824 * \tabhead{Field & Description}
825 * \var{type} & Class name (a string) of the object to be created. Mandatory. \\
826 * \var{name} & Name of the object to be created (a string). Optional. \\
827 * \var{switchto} & Should the region be switched to (boolean)? Optional. \\
828 * \var{level} & Stacking level; default is 1. \\
829 * \var{modal} & Make object modal; ignored if level is set. \\
830 * \var{sizepolicy} & Size policy. \\
833 * In addition parameters to the region to be created are passed in this
837 WRegion *group_attach_new(WGroup *ws, ExtlTab param)
839 WGroupAttachParams par=GROUPATTACHPARAMS_INIT;
840 WRegionAttachData data;
842 get_params(ws, param, &par);
844 data.type=REGION_ATTACH_LOAD;
847 return group_do_attach(ws, &par, &data);
854 /*{{{ Status display support */
857 static int stdisp_szplcy(const WMPlexSTDispInfo *di, WRegion *stdisp)
862 if(region_orientation(stdisp)==REGION_ORIENTATION_VERTICAL){
863 if(pos==MPLEX_STDISP_TL || pos==MPLEX_STDISP_BL)
864 return SIZEPOLICY_STRETCH_LEFT;
866 return SIZEPOLICY_STRETCH_RIGHT;
868 if(pos==MPLEX_STDISP_TL || pos==MPLEX_STDISP_TR)
869 return SIZEPOLICY_STRETCH_TOP;
871 return SIZEPOLICY_STRETCH_BOTTOM;
874 if(pos==MPLEX_STDISP_TL)
875 return SIZEPOLICY_GRAVITY_NORTHWEST;
876 else if(pos==MPLEX_STDISP_BL)
877 return SIZEPOLICY_GRAVITY_SOUTHWEST;
878 else if(pos==MPLEX_STDISP_TR)
879 return SIZEPOLICY_GRAVITY_NORTHEAST;
880 else /*if(pos=MPLEX_STDISP_BR)*/
881 return SIZEPOLICY_GRAVITY_SOUTHEAST;
886 void group_manage_stdisp(WGroup *ws, WRegion *stdisp,
887 const WMPlexSTDispInfo *di)
891 WRegion *b=(ws->bottom==NULL ? NULL : ws->bottom->reg);
893 /* Check if 'bottom' wants to manage the stdisp. */
895 && !OBJ_IS_BEING_DESTROYED(b)
896 && HAS_DYN(b, region_manage_stdisp)){
897 region_manage_stdisp(b, stdisp, di);
898 if(REGION_MANAGER(stdisp)==b)
904 szplcy=stdisp_szplcy(di, stdisp)|SIZEPOLICY_SHRUNK;
906 if(ws->managed_stdisp!=NULL && ws->managed_stdisp->reg==stdisp){
907 if(ws->managed_stdisp->szplcy==szplcy)
909 ws->managed_stdisp->szplcy=szplcy;
911 region_detach_manager(stdisp);
912 ws->managed_stdisp=group_do_add_managed(ws, stdisp,
913 STACKING_LEVEL_ON_TOP,
917 fp.g=REGION_GEOM(ws);
918 sizepolicy(&ws->managed_stdisp->szplcy, stdisp, NULL, 0, &fp);
920 region_fitrep(stdisp, NULL, &fp);
924 static void group_remanage_stdisp(WGroup *ws)
926 WMPlex *mplex=OBJ_CAST(REGION_MANAGER(ws), WMPlex);
929 mplex->mx_current!=NULL &&
930 mplex->mx_current->st->reg==(WRegion*)ws){
931 mplex_remanage_stdisp(mplex);
939 /*{{{ Geometry requests */
942 void group_managed_rqgeom(WGroup *ws, WRegion *reg,
943 const WRQGeomParams *rq,
949 st=group_find_stacking(ws, reg);
953 fp.mode=REGION_FIT_EXACT;
955 fp.g=REGION_GEOM(ws);
956 sizepolicy(&st->szplcy, reg, &rq->geom, rq->flags, &fp);
962 if(!(rq->flags®ION_RQGEOM_TRYONLY))
963 region_fitrep(reg, NULL, &fp);
967 void group_managed_rqgeom_absolute(WGroup *grp, WRegion *sub,
968 const WRQGeomParams *rq,
971 if(grp->bottom!=NULL && grp->bottom->reg==sub){
972 region_rqgeom((WRegion*)grp, rq, geomret);
973 if(!(rq->flags®ION_RQGEOM_TRYONLY) && geomret!=NULL)
974 *geomret=REGION_GEOM(sub);
976 WRQGeomParams rq2=*rq;
977 rq2.flags&=~REGION_RQGEOM_ABSOLUTE;
979 region_managed_rqgeom((WRegion*)grp, sub, &rq2, geomret);
990 static WStacking *nxt(WGroup *ws, WStacking *st, bool wrap)
992 return (st->mgr_next!=NULL
994 : (wrap ? ws->managed_list : NULL));
998 static WStacking *prv(WGroup *ws, WStacking *st, bool wrap)
1000 return (st!=ws->managed_list
1002 : (wrap ? st->mgr_prev : NULL));
1006 typedef WStacking *NxtFn(WGroup *ws, WStacking *st, bool wrap);
1009 static bool mapped_filt(WStacking *st, void *unused)
1011 return (st->reg!=NULL && REGION_IS_MAPPED(st->reg));
1015 static bool focusable(WGroup *ws, WStacking *st, uint min_level)
1017 return (st->reg!=NULL
1018 && REGION_IS_MAPPED(st->reg)
1019 && !(st->reg->flags®ION_SKIP_FOCUS)
1020 && st->level>=min_level);
1024 static WStacking *do_get_next(WGroup *ws, WStacking *sti,
1025 NxtFn *fn, bool wrap, bool sti_ok)
1027 WStacking *st, *stacking;
1030 stacking=group_get_stacking(ws);
1033 min_level=stacking_min_level(stacking, mapped_filt, NULL);
1037 st=fn(ws, st, wrap);
1039 if(st==NULL || st==sti)
1042 if(focusable(ws, st, min_level))
1046 if(sti_ok && focusable(ws, sti, min_level))
1053 static WStacking *group_do_navi_first(WGroup *ws, WRegionNavi nh)
1055 WStacking *lst=ws->managed_list;
1060 if(nh==REGION_NAVI_ANY &&
1061 ws->current_managed!=NULL &&
1062 ws->current_managed->reg!=NULL){
1063 return ws->current_managed;
1066 if(nh==REGION_NAVI_ANY || nh==REGION_NAVI_END ||
1067 nh==REGION_NAVI_BOTTOM || nh==REGION_NAVI_RIGHT){
1068 return do_get_next(ws, lst, prv, TRUE, TRUE);
1070 return do_get_next(ws, lst->mgr_prev, nxt, TRUE, TRUE);
1075 static WRegion *group_navi_first(WGroup *ws, WRegionNavi nh,
1076 WRegionNaviData *data)
1078 WStacking *st=group_do_navi_first(ws, nh);
1080 return region_navi_cont(&ws->reg, (st!=NULL ? st->reg : NULL), data);
1084 static WStacking *group_do_navi_next(WGroup *ws, WStacking *st,
1085 WRegionNavi nh, bool wrap)
1088 return group_do_navi_first(ws, nh);
1090 if(nh==REGION_NAVI_ANY || nh==REGION_NAVI_END ||
1091 nh==REGION_NAVI_BOTTOM || nh==REGION_NAVI_RIGHT){
1092 return do_get_next(ws, st, nxt, wrap, FALSE);
1094 return do_get_next(ws, st, prv, wrap, FALSE);
1098 static WRegion *group_navi_next(WGroup *ws, WRegion *reg,
1099 WRegionNavi nh, WRegionNaviData *data)
1101 WStacking *st=group_find_stacking(ws, reg);
1103 st=group_do_navi_next(ws, st, nh, FALSE);
1105 return region_navi_cont(&ws->reg, (st!=NULL ? st->reg : NULL), data);
1116 * Note: Managed objects are considered to be stacked separately from the
1117 * group, slightly violating expectations.
1120 void group_stacking(WGroup *ws, Window *bottomret, Window *topret)
1122 Window win=region_xwindow((WRegion*)ws);
1129 void group_restack(WGroup *ws, Window other, int mode)
1133 win=region_xwindow((WRegion*)ws);
1135 xwindow_restack(win, other, mode);
1142 WStacking *group_find_stacking(WGroup *ws, WRegion *r)
1144 if(r==NULL || REGION_MANAGER(r)!=(WRegion*)ws)
1147 return ioncore_find_stacking(r);
1151 static WStacking *find_stacking_if_not_on_ws(WGroup *ws, Window w)
1153 WRegion *r=xwindow_region_of(w);
1157 if(REGION_MANAGER(r)==(WRegion*)ws)
1159 st=group_find_stacking(ws, r);
1162 r=REGION_MANAGER(r);
1169 bool group_managed_rqorder(WGroup *grp, WRegion *reg, WRegionOrder order)
1171 WStacking **stackingp=group_get_stackingp(grp);
1174 if(stackingp==NULL || *stackingp==NULL)
1177 st=group_find_stacking(grp, reg);
1182 stacking_restack(stackingp, st, None, NULL, NULL,
1183 (order!=REGION_ORDER_FRONT));
1196 * Iterate over managed regions of \var{ws} until \var{iterfn} returns
1198 * The function itself returns \code{true} if it reaches the end of list
1199 * without this happening.
1203 bool group_managed_i(WGroup *ws, ExtlFn iterfn)
1206 group_iter_init(&tmp, ws);
1208 return extl_iter_objlist_(iterfn, (ObjIterator*)group_iter, &tmp);
1212 WRegion* group_current(WGroup *ws)
1214 return (ws->current_managed!=NULL ? ws->current_managed->reg : NULL);
1218 void group_size_hints(WGroup *ws, WSizeHints *hints_ret)
1220 if(ws->bottom==NULL || ws->bottom->reg==NULL){
1221 sizehints_clear(hints_ret);
1223 region_size_hints(ws->bottom->reg, hints_ret);
1224 hints_ret->no_constrain=TRUE;
1229 Window group_xwindow(const WGroup *ws)
1231 return ws->dummywin;
1236 * Returns the group of \var{reg}, if it is managed by one,
1237 * and \var{reg} itself otherwise.
1239 /*EXTL_EXPORT_MEMBER
1240 WRegion *region_group_of(WRegion *reg)
1242 WRegion *mgr=REGION_MANAGER(reg);
1244 return (OBJ_IS(mgr, WGroup) ? mgr : reg);
1249 * Returns the group of \var{reg}, if \var{reg} is its bottom,
1250 * and \var{reg} itself otherwise.
1253 WRegion *region_groupleader_of(WRegion *reg)
1255 WGroup *grp=REGION_MANAGER_CHK(reg, WGroup);
1257 return ((grp!=NULL && group_bottom(grp)==reg)
1269 static ExtlTab group_get_configuration(WGroup *ws)
1271 ExtlTab tab, mgds, subtab, g;
1278 tab=region_get_base_configuration((WRegion*)ws);
1280 mgds=extl_create_table();
1282 extl_table_sets_t(tab, "managed", mgds);
1284 /* TODO: stacking order messed up */
1286 FOR_ALL_NODES_IN_GROUP(ws, st, tmp){
1290 subtab=region_get_configuration(st->reg);
1292 if(subtab!=extl_table_none()){
1293 extl_table_sets_i(subtab, "sizepolicy", st->szplcy);
1294 extl_table_sets_i(subtab, "level", st->level);
1296 tmpg=REGION_GEOM(st->reg);
1297 tmpg.x-=REGION_GEOM(ws).x;
1298 tmpg.y-=REGION_GEOM(ws).y;
1300 g=extl_table_from_rectangle(&tmpg);
1301 extl_table_sets_t(subtab, "geom", g);
1302 extl_unref_table(g);
1305 extl_table_sets_b(subtab, "bottom", TRUE);
1307 extl_table_seti_t(mgds, ++n, subtab);
1308 extl_unref_table(subtab);
1312 extl_unref_table(mgds);
1318 void group_do_load(WGroup *ws, ExtlTab tab)
1320 ExtlTab substab, subtab;
1323 if(extl_table_gets_t(tab, "managed", &substab)){
1324 n=extl_table_get_n(substab);
1325 for(i=1; i<=n; i++){
1326 if(extl_table_geti_t(substab, i, &subtab)){
1327 group_attach_new(ws, subtab);
1328 extl_unref_table(subtab);
1332 extl_unref_table(substab);
1337 WRegion *group_load(WWindow *par, const WFitParams *fp, ExtlTab tab)
1341 ws=create_group(par, fp);
1346 group_do_load(ws, tab);
1348 return (WRegion*)ws;
1355 /*{{{ Dynamic function table and class implementation */
1358 static DynFunTab group_dynfuntab[]={
1359 {(DynFun*)region_fitrep,
1360 (DynFun*)group_fitrep},
1368 {(DynFun*)region_managed_prepare_focus,
1369 (DynFun*)group_managed_prepare_focus},
1371 {region_do_set_focus,
1372 group_do_set_focus},
1374 {region_managed_notify,
1375 group_managed_notify},
1377 {region_managed_remove,
1378 group_managed_remove},
1380 {(DynFun*)region_get_configuration,
1381 (DynFun*)group_get_configuration},
1383 {(DynFun*)region_managed_disposeroot,
1384 (DynFun*)group_managed_disposeroot},
1386 {(DynFun*)region_current,
1387 (DynFun*)group_current},
1389 {(DynFun*)region_rescue_clientwins,
1390 (DynFun*)group_rescue_clientwins},
1398 {(DynFun*)region_managed_get_pholder,
1399 (DynFun*)group_managed_get_pholder},
1401 {region_managed_rqgeom,
1402 group_managed_rqgeom},
1404 {region_managed_rqgeom_absolute,
1405 group_managed_rqgeom_absolute},
1407 {(DynFun*)group_do_add_managed,
1408 (DynFun*)group_do_add_managed_default},
1413 {(DynFun*)region_xwindow,
1414 (DynFun*)group_xwindow},
1416 {(DynFun*)region_navi_first,
1417 (DynFun*)group_navi_first},
1419 {(DynFun*)region_navi_next,
1420 (DynFun*)group_navi_next},
1422 {(DynFun*)region_managed_rqorder,
1423 (DynFun*)group_managed_rqorder},
1430 IMPLCLASS(WGroup, WRegion, group_deinit, group_dynfuntab);