4 * Copyright (c) Tuomo Valkonen 1999-2008.
6 * See the included file LICENSE for details.
11 #include <libtu/minmax.h>
12 #include <libtu/objp.h>
13 #include <libmainloop/defer.h>
30 #include "sizepolicy.h"
37 #include "grouppholder.h"
39 #include "float-placement.h"
43 static void group_place_stdisp(WGroup *ws, WWindow *parent,
44 int pos, WRegion *stdisp);
46 static void group_remanage_stdisp(WGroup *ws);
48 static void group_do_set_bottom(WGroup *grp, WStacking *st);
51 /*{{{ Stacking list stuff */
54 WStacking *group_get_stacking(WGroup *ws)
56 WWindow *par=REGION_PARENT(ws);
60 : window_get_stacking(par));
64 WStacking **group_get_stackingp(WGroup *ws)
66 WWindow *par=REGION_PARENT(ws);
70 : window_get_stackingp(par));
74 static bool wsfilt(WStacking *st, void *ws)
76 return (st->reg!=NULL && REGION_MANAGER(st->reg)==(WRegion*)ws);
80 static bool wsfilt_nostdisp(WStacking *st, void *ws)
82 return (wsfilt(st, ws) && ((WGroup*)ws)->managed_stdisp!=st);
86 void group_iter_init(WGroupIterTmp *tmp, WGroup *ws)
88 stacking_iter_mgr_init(tmp, ws->managed_list, NULL, ws);
92 void group_iter_init_nostdisp(WGroupIterTmp *tmp, WGroup *ws)
94 stacking_iter_mgr_init(tmp, ws->managed_list, wsfilt_nostdisp, ws);
98 WRegion *group_iter(WGroupIterTmp *tmp)
100 return stacking_iter_mgr(tmp);
104 WStacking *group_iter_nodes(WGroupIterTmp *tmp)
106 return stacking_iter_mgr_nodes(tmp);
110 WGroupIterTmp group_iter_default_tmp;
116 /*{{{ region dynfun implementations */
119 static void group_fit(WGroup *ws, const WRectangle *geom)
121 REGION_GEOM(ws)=*geom;
125 bool group_fitrep(WGroup *ws, WWindow *par, const WFitParams *fp)
128 WStacking *unweaved=NULL;
129 int xdiff=0, ydiff=0;
134 oldpar=REGION_PARENT(ws);
137 if(fp->mode®ION_FIT_WHATEVER)
139 REGION_GEOM(ws)=fp->g;
141 if(!region_same_rootwin((WRegion*)ws, (WRegion*)par))
144 if(ws->managed_stdisp!=NULL && ws->managed_stdisp->reg!=NULL)
145 region_detach_manager(ws->managed_stdisp->reg);
147 assert(ws->managed_stdisp==NULL);
149 xdiff=fp->g.x-REGION_GEOM(ws).x;
150 ydiff=fp->g.y-REGION_GEOM(ws).y;
152 region_unset_parent((WRegion*)ws);
153 XReparentWindow(ioncore_g.dpy, ws->dummywin, par->win, -1, -1);
154 region_set_parent((WRegion*)ws, par);
156 REGION_GEOM(ws).x=fp->g.x;
157 REGION_GEOM(ws).y=fp->g.y;
158 if(!(fp->mode®ION_FIT_WHATEVER)){
159 REGION_GEOM(ws).w=fp->g.w;
160 REGION_GEOM(ws).h=fp->g.h;
164 unweaved=stacking_unweave(&oldpar->stacking, wsfilt, (void*)ws);
167 FOR_ALL_NODES_IN_GROUP(ws, st, tmp){
173 g=REGION_GEOM(st->reg);
177 if(fp->mode®ION_FIT_WHATEVER){
180 fp2.g=REGION_GEOM(ws);
181 sizepolicy(&st->szplcy, st->reg, &g, REGION_RQGEOM_WEAK_ALL, &fp2);
184 if(!region_fitrep(st->reg, par, &fp2)){
185 warn(TR("Error reparenting %s."), region_name(st->reg));
186 region_detach_manager(st->reg);
191 stacking_weave(&par->stacking, &unweaved, FALSE);
197 static void group_map(WGroup *ws)
202 REGION_MARK_MAPPED(ws);
203 XMapWindow(ioncore_g.dpy, ws->dummywin);
205 FOR_ALL_MANAGED_BY_GROUP(ws, reg, tmp){
211 static void group_unmap(WGroup *ws)
216 REGION_MARK_UNMAPPED(ws);
217 XUnmapWindow(ioncore_g.dpy, ws->dummywin);
219 FOR_ALL_MANAGED_BY_GROUP(ws, reg, tmp){
225 static WStacking *find_to_focus(WGroup *ws, WStacking *st, bool group_only)
227 WStacking *stacking=group_get_stacking(ws);
232 return stacking_find_to_focus_mapped(stacking, st,
233 (group_only ? (WRegion*)ws : NULL));
237 static void group_do_set_focus(WGroup *ws, bool warp)
239 WStacking *st=find_to_focus(ws, ws->current_managed, FALSE);
241 if(st!=NULL && st->reg!=NULL)
242 region_do_set_focus(st->reg, warp);
244 region_finalise_focusing((WRegion*)ws, ws->dummywin, warp);
248 static bool group_managed_prepare_focus(WGroup *ws, WRegion *reg,
249 int flags, WPrepareFocusResult *res)
251 WMPlex *mplex=OBJ_CAST(REGION_MANAGER(ws), WMPlex);
252 WStacking *st=group_find_stacking(ws, reg);
258 WStacking *node=mplex_find_stacking(mplex, (WRegion*)ws);
263 return mplex_do_prepare_focus(mplex, node, st,
266 if(!region_prepare_focus((WRegion*)ws, flags, res))
269 st=find_to_focus(ws, st, FALSE);
274 if(ioncore_g.autoraise &&
275 !(flags®ION_GOTO_ENTERWINDOW) &&
276 st->level>STACKING_LEVEL_BOTTOM){
277 WStacking **stackingp=group_get_stackingp(ws);
278 stacking_restack(stackingp, st, None, NULL, NULL, FALSE);
284 return (res->reg==reg);
289 void group_managed_remove(WGroup *ws, WRegion *reg)
291 bool mcf=region_may_control_focus((WRegion*)ws);
292 WStacking *st, *next_st=NULL;
293 bool was_stdisp=FALSE, was_bottom=FALSE;
294 bool was_current=FALSE;
296 st=group_find_stacking(ws, reg);
301 group_do_set_bottom(ws, NULL);
304 if(st==ws->managed_stdisp){
305 ws->managed_stdisp=NULL;
309 if(st==ws->current_managed){
310 ws->current_managed=NULL;
314 next_st=stacking_unstack(REGION_PARENT(ws), st);
315 UNLINK_ITEM(ws->managed_list, st, mgr_next, mgr_prev);
316 stacking_unassoc(st);
320 region_unset_manager(reg, (WRegion*)ws);
322 if(!OBJ_IS_BEING_DESTROYED(ws) && was_current){
323 /* This may still potentially cause problems when focus
324 * change is pending. Perhaps we should use region_await_focus,
325 * if it is pointing to our child (and region_may_control_focus
326 * fail if it is pointing somewhere else).
328 WStacking *stf=find_to_focus(ws, next_st, TRUE);
329 if(stf!=NULL && mcf){
330 region_maybewarp_now(stf->reg, FALSE);
332 ws->current_managed=stf;
338 void group_managed_notify(WGroup *ws, WRegion *reg, WRegionNotify how)
340 if(how==ioncore_g.notifies.activated ||
341 how==ioncore_g.notifies.pseudoactivated){
342 ws->current_managed=group_find_stacking(ws, reg);
350 /*{{{ Create/destroy */
353 bool group_init(WGroup *ws, WWindow *par, const WFitParams *fp)
355 ws->current_managed=NULL;
356 ws->managed_stdisp=NULL;
358 ws->managed_list=NULL;
361 ws->dummywin=XCreateWindow(ioncore_g.dpy, par->win,
362 fp->g.x, fp->g.y, 1, 1, 0,
363 CopyFromParent, InputOnly,
364 CopyFromParent, 0, NULL);
365 if(ws->dummywin==None)
368 region_init(&ws->reg, par, fp);
369 region_register(&ws->reg);
371 XSelectInput(ioncore_g.dpy, ws->dummywin,
372 FocusChangeMask|KeyPressMask|KeyReleaseMask|
373 ButtonPressMask|ButtonReleaseMask);
374 XSaveContext(ioncore_g.dpy, ws->dummywin, ioncore_g.win_context,
377 ((WRegion*)ws)->flags|=REGION_GRAB_ON_PARENT;
379 region_add_bindmap((WRegion*)ws, ioncore_group_bindmap);
385 WGroup *create_group(WWindow *par, const WFitParams *fp)
387 CREATEOBJ_IMPL(WGroup, group, (p, par, fp));
391 void group_deinit(WGroup *ws)
396 if(ws->managed_stdisp!=NULL && ws->managed_stdisp->reg!=NULL){
397 group_managed_remove(ws, ws->managed_stdisp->reg);
398 assert(ws->managed_stdisp==NULL);
401 FOR_ALL_MANAGED_BY_GROUP(ws, reg, tmp){
402 destroy_obj((Obj*)reg);
405 assert(ws->managed_list==NULL);
407 XDeleteContext(ioncore_g.dpy, ws->dummywin, ioncore_g.win_context);
408 XDestroyWindow(ioncore_g.dpy, ws->dummywin);
412 grouppholder_do_unlink(ws->phs);
414 region_deinit(&ws->reg);
418 bool group_rescue_clientwins(WGroup *ws, WRescueInfo *info)
422 group_iter_init_nostdisp(&tmp, ws);
424 return region_rescue_some_clientwins((WRegion*)ws, info,
425 (WRegionIterator*)group_iter,
430 WPHolder *group_get_rescue_pholder_for(WGroup *ws,
433 WGroupAttachParams ap=GROUPATTACHPARAMS_INIT;
434 WFramedParam fp=FRAMEDPARAM_INIT;
438 ap.geom=REGION_GEOM(forwhat);
442 if(REGION_PARENT(forwhat)==REGION_PARENT(ws)){
443 ap.geom.x-=REGION_GEOM(ws).x;
444 ap.geom.y-=REGION_GEOM(ws).y;
446 ap.geom_weak=REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y;
451 WFrame *frame=OBJ_CAST(forwhat, WFrame);
456 ph=(WPHolder*)create_grouppholder(ws, NULL, &ap);
458 return pholder_either((WPHolder*)create_framedpholder(ph, &fp), ph);
469 void group_bottom_set(WGroup *grp)
471 CALL_DYN(group_bottom_set, grp, (grp));
475 static void group_do_set_bottom(WGroup *grp, WStacking *st)
477 WStacking *was=grp->bottom;
478 WStacking *std=grp->managed_stdisp;
482 if(!OBJ_IS_BEING_DESTROYED(grp)){
483 bool noremanage=((was==st) ||
484 (was==NULL && std==NULL) ||
485 (st!=NULL && st==std) ||
486 (st==NULL && was==std));
489 (st==NULL || HAS_DYN(st->reg, region_manage_stdisp))){
490 group_remanage_stdisp(grp);
493 group_bottom_set(grp);
499 * Sets the `bottom' of \var{ws}. The region \var{reg} must already
500 * be managed by \var{ws}, unless \code{nil}.
503 bool group_set_bottom(WGroup *ws, WRegion *reg)
508 st=group_find_stacking(ws, reg);
514 group_do_set_bottom(ws, st);
521 * Returns the `bottom' of \var{ws}.
525 WRegion *group_bottom(WGroup *ws)
527 return (ws->bottom!=NULL ? ws->bottom->reg : NULL);
537 WStacking *group_do_add_managed(WGroup *ws, WRegion *reg, int level,
541 CALL_DYN_RET(st, WStacking*, group_do_add_managed, ws,
542 (ws, reg, level, szplcy));
547 WStacking *group_do_add_managed_default(WGroup *ws, WRegion *reg, int level,
550 WStacking *st=NULL, *tmp=NULL;
551 Window bottom=None, top=None;
552 WStacking **stackingp=group_get_stackingp(ws);
558 st=create_stacking();
563 if(!stacking_assoc(st, reg)){
568 frame=OBJ_CAST(reg, WFrame);
570 if(framemode_unalt(frame_mode(frame))==FRAME_MODE_TILED)
571 frame_set_mode(frame, FRAME_MODE_FLOATING);
577 LINK_ITEM_FIRST(tmp, st, next, prev);
578 stacking_weave(stackingp, &tmp, FALSE);
581 LINK_ITEM(ws->managed_list, st, mgr_next, mgr_prev);
582 region_set_manager(reg, (WRegion*)ws);
584 if(region_is_fully_mapped((WRegion*)ws))
591 static void geom_group_to_parent(WGroup *ws, const WRectangle *g,
594 wg->x=g->x+REGION_GEOM(ws).x;
595 wg->y=g->y+REGION_GEOM(ws).y;
596 wg->w=maxof(1, g->w);
597 wg->h=maxof(1, g->h);
601 static int group_must_focus(WGroup *ws, WStacking *st)
603 WStacking *stacking=group_get_stacking(ws);
605 return (stacking!=NULL && stacking_must_focus(stacking, st));
609 bool group_do_attach_final(WGroup *ws,
611 const WGroupAttachParams *param)
613 WStacking *st, *stabove=NULL;
622 if(param->stack_above!=NULL)
623 stabove=group_find_stacking(ws, param->stack_above);
629 : STACKING_LEVEL_NORMAL));
632 szplcy=(param->szplcy_set
635 ? SIZEPOLICY_FULL_EXACT
636 : SIZEPOLICY_VISIBILITY_CONSTRAINED));
638 if(!param->whatever){
639 weak=(param->geom_weak_set
643 : REGION_RQGEOM_WEAK_ALL));
646 geom_group_to_parent(ws, ¶m->geom, &g);
650 /* If the requested geometry does not overlap the workspaces's geometry,
651 * position request is never honoured.
653 if((g.x+g.w<=REGION_GEOM(ws).x) ||
654 (g.x>=REGION_GEOM(ws).x+REGION_GEOM(ws).w)){
655 weak|=REGION_RQGEOM_WEAK_X;
658 if((g.y+g.h<=REGION_GEOM(ws).y) ||
659 (g.y>=REGION_GEOM(ws).y+REGION_GEOM(ws).h)){
660 weak|=REGION_RQGEOM_WEAK_Y;
663 if(weak&(REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y) &&
664 (szplcy==SIZEPOLICY_UNCONSTRAINED ||
665 szplcy==SIZEPOLICY_VISIBILITY_CONSTRAINED ||
666 szplcy==SIZEPOLICY_FREE ||
667 szplcy==SIZEPOLICY_FREE_GLUE /* without flags */)){
668 /* TODO: use 'weak'? */
669 group_calc_placement(ws, level, &g);
672 fp.g=REGION_GEOM(ws);
673 fp.mode=REGION_FIT_EXACT;
675 sizepolicy(&szplcy, reg, &g, weak, &fp);
677 if(rectangle_compare(&fp.g, ®ION_GEOM(reg))!=RECTANGLE_SAME)
678 region_fitrep(reg, NULL, &fp);
682 st=group_do_add_managed(ws, reg, level, szplcy);
691 group_do_set_bottom(ws, st);
694 sw=((param->switchto_set ? param->switchto : ioncore_g.switchto_new)
695 ? st==find_to_focus(ws, st, FALSE)
696 : group_must_focus(ws, st));
699 if(region_may_control_focus((WRegion*)ws))
700 region_set_focus(st->reg);
702 ws->current_managed=st;
703 }else if(region_is_fully_mapped(reg)){
704 region_pointer_focus_hack(reg);
711 static void group_attach_fp(WGroup *ws, const WGroupAttachParams *param,
715 geom_group_to_parent(ws, ¶m->geom, &fp->g);
716 fp->mode=REGION_FIT_EXACT;
718 fp->g=REGION_GEOM(ws);
719 fp->mode=REGION_FIT_BOUNDS|REGION_FIT_WHATEVER;
724 WRegion *group_do_attach(WGroup *ws,
725 /*const*/ WGroupAttachParams *param,
726 WRegionAttachData *data)
731 if(ws->bottom!=NULL && param->bottom){
732 warn(TR("'bottom' already set."));
736 group_attach_fp(ws, param, &fp);
738 return region_attach_helper((WRegion*) ws, REGION_PARENT(ws), &fp,
739 (WRegionDoAttachFn*)group_do_attach_final,
740 /*(const WRegionAttachParams*)*/param, data);
741 /* ^^^^ doesn't seem to work. */
745 void group_get_attach_params(WGroup *ws, ExtlTab tab,
746 WGroupAttachParams *par)
759 if(extl_table_is_bool_set(tab, "bottom")){
760 par->level=STACKING_LEVEL_BOTTOM;
765 if(extl_table_gets_i(tab, "level", &tmp)){
772 if(!par->level_set && extl_table_is_bool_set(tab, "modal")){
773 par->level=STACKING_LEVEL_MODAL1;
777 if(extl_table_gets_b(tab, "switchto", &tmpb)){
778 par->switchto=(tmpb!=0);
782 if(extl_table_gets_i(tab, "sizepolicy", &tmp)){
785 }else if(extl_table_gets_s(tab, "sizepolicy", &tmps)){
786 if(string2sizepolicy(tmps, &par->szplcy))
791 if(extl_table_gets_t(tab, "geom", &g)){
794 if(extl_table_gets_i(g, "x", &(par->geom.x)))
796 if(extl_table_gets_i(g, "y", &(par->geom.y)))
798 if(extl_table_gets_i(g, "w", &(par->geom.w)))
800 if(extl_table_gets_i(g, "h", &(par->geom.h)))
813 * Attach and reparent existing region \var{reg} to \var{ws}.
814 * The table \var{param} may contain the fields \var{index} and
815 * \var{switchto} that are interpreted as for \fnref{WMPlex.attach_new}.
818 WRegion *group_attach(WGroup *ws, WRegion *reg, ExtlTab param)
820 WGroupAttachParams par=GROUPATTACHPARAMS_INIT;
821 WRegionAttachData data;
826 group_get_attach_params(ws, param, &par);
828 data.type=REGION_ATTACH_REPARENT;
831 return group_do_attach(ws, &par, &data);
836 * Create a new region to be managed by \var{ws}. At least the following
837 * fields in \var{param} are understood:
839 * \begin{tabularx}{\linewidth}{lX}
840 * \tabhead{Field & Description}
841 * \var{type} & (string) Class of the object to be created. Mandatory. \\
842 * \var{name} & (string) Name of the object to be created. \\
843 * \var{switchto} & (boolean) Should the region be switched to? \\
844 * \var{level} & (integer) Stacking level; default is 1. \\
845 * \var{modal} & (boolean) Make object modal; ignored if level is set. \\
846 * \var{sizepolicy} & (string) Size policy; see Section \ref{sec:sizepolicies}. \\
847 * \var{bottom} & (boolean) Mark the attached region as the
848 * ``bottom'' of \var{ws}. \\
851 * In addition parameters to the region to be created are passed in this
855 WRegion *group_attach_new(WGroup *ws, ExtlTab param)
857 WGroupAttachParams par=GROUPATTACHPARAMS_INIT;
858 WRegionAttachData data;
860 group_get_attach_params(ws, param, &par);
862 data.type=REGION_ATTACH_LOAD;
865 return group_do_attach(ws, &par, &data);
872 /*{{{ Status display support */
875 static int stdisp_szplcy(const WMPlexSTDispInfo *di, WRegion *stdisp)
878 int policy=0, gravity=0;
881 if(region_orientation(stdisp)==REGION_ORIENTATION_VERTICAL){
882 if(pos==MPLEX_STDISP_TL || pos==MPLEX_STDISP_BL)
883 policy=SIZEPOLICY_STRETCH_LEFT;
885 policy=SIZEPOLICY_STRETCH_RIGHT;
887 if(pos==MPLEX_STDISP_TL || pos==MPLEX_STDISP_TR)
888 policy=SIZEPOLICY_STRETCH_TOP;
890 policy=SIZEPOLICY_STRETCH_BOTTOM;
893 policy=SIZEPOLICY_GRAVITY;
896 if(pos==MPLEX_STDISP_TL)
897 gravity=SIZEPOLICY_VERT_TOP|SIZEPOLICY_HORIZ_LEFT;
898 else if(pos==MPLEX_STDISP_BL)
899 gravity=SIZEPOLICY_VERT_BOTTOM|SIZEPOLICY_HORIZ_LEFT;
900 else if(pos==MPLEX_STDISP_TR)
901 gravity=SIZEPOLICY_VERT_TOP|SIZEPOLICY_HORIZ_RIGHT;
902 else /*if(pos=MPLEX_STDISP_BR)*/
903 gravity=SIZEPOLICY_VERT_BOTTOM|SIZEPOLICY_HORIZ_RIGHT;
905 return (policy|gravity);
909 void group_manage_stdisp(WGroup *ws, WRegion *stdisp,
910 const WMPlexSTDispInfo *di)
914 WRegion *b=(ws->bottom==NULL ? NULL : ws->bottom->reg);
916 /* Check if 'bottom' wants to manage the stdisp. */
918 && !OBJ_IS_BEING_DESTROYED(b)
919 && HAS_DYN(b, region_manage_stdisp)){
920 region_manage_stdisp(b, stdisp, di);
921 if(REGION_MANAGER(stdisp)==b)
927 szplcy=stdisp_szplcy(di, stdisp)|SIZEPOLICY_SHRUNK;
929 if(ws->managed_stdisp!=NULL && ws->managed_stdisp->reg==stdisp){
930 if(ws->managed_stdisp->szplcy==szplcy)
932 ws->managed_stdisp->szplcy=szplcy;
934 region_detach_manager(stdisp);
935 ws->managed_stdisp=group_do_add_managed(ws, stdisp,
936 STACKING_LEVEL_ON_TOP,
940 stdisp->flags|=REGION_SKIP_FOCUS;
942 fp.g=REGION_GEOM(ws);
945 sizepolicy(&ws->managed_stdisp->szplcy, stdisp, NULL, 0, &fp);
947 region_fitrep(stdisp, NULL, &fp);
951 static void group_remanage_stdisp(WGroup *ws)
953 WMPlex *mplex=OBJ_CAST(REGION_MANAGER(ws), WMPlex);
956 mplex->mx_current!=NULL &&
957 mplex->mx_current->st->reg==(WRegion*)ws){
958 mplex_remanage_stdisp(mplex);
966 /*{{{ Geometry requests */
969 void group_managed_rqgeom(WGroup *ws, WRegion *reg,
970 const WRQGeomParams *rq,
976 st=group_find_stacking(ws, reg);
980 fp.mode=REGION_FIT_EXACT;
982 fp.g=REGION_GEOM(ws);
984 sizepolicy(&st->szplcy, reg, &rq->geom, rq->flags, &fp);
990 if(!(rq->flags®ION_RQGEOM_TRYONLY))
991 region_fitrep(reg, NULL, &fp);
995 void group_managed_rqgeom_absolute(WGroup *grp, WRegion *sub,
996 const WRQGeomParams *rq,
999 if(grp->bottom!=NULL && grp->bottom->reg==sub){
1000 region_rqgeom((WRegion*)grp, rq, geomret);
1001 if(!(rq->flags®ION_RQGEOM_TRYONLY) && geomret!=NULL)
1002 *geomret=REGION_GEOM(sub);
1004 WRQGeomParams rq2=*rq;
1005 rq2.flags&=~REGION_RQGEOM_ABSOLUTE;
1007 region_managed_rqgeom((WRegion*)grp, sub, &rq2, geomret);
1018 static WStacking *nxt(WGroup *ws, WStacking *st, bool wrap)
1020 return (st->mgr_next!=NULL
1022 : (wrap ? ws->managed_list : NULL));
1026 static WStacking *prv(WGroup *ws, WStacking *st, bool wrap)
1028 return (st!=ws->managed_list
1030 : (wrap ? st->mgr_prev : NULL));
1034 typedef WStacking *NxtFn(WGroup *ws, WStacking *st, bool wrap);
1037 static bool focusable(WGroup *ws, WStacking *st, uint min_level)
1039 return (st->reg!=NULL
1040 && REGION_IS_MAPPED(st->reg)
1041 && !(st->reg->flags®ION_SKIP_FOCUS)
1042 && st->level>=min_level);
1046 static WStacking *do_get_next(WGroup *ws, WStacking *sti,
1047 NxtFn *fn, bool wrap, bool sti_ok)
1049 WStacking *st, *stacking;
1052 stacking=group_get_stacking(ws);
1055 min_level=stacking_min_level_mapped(stacking);
1059 st=fn(ws, st, wrap);
1061 if(st==NULL || st==sti)
1064 if(focusable(ws, st, min_level))
1068 if(sti_ok && focusable(ws, sti, min_level))
1075 static WStacking *group_do_navi_first(WGroup *ws, WRegionNavi nh)
1077 WStacking *lst=ws->managed_list;
1082 if(nh==REGION_NAVI_ANY &&
1083 ws->current_managed!=NULL &&
1084 ws->current_managed->reg!=NULL){
1085 return ws->current_managed;
1088 if(nh==REGION_NAVI_ANY || nh==REGION_NAVI_END ||
1089 nh==REGION_NAVI_BOTTOM || nh==REGION_NAVI_RIGHT){
1090 return do_get_next(ws, lst, prv, TRUE, TRUE);
1092 return do_get_next(ws, lst->mgr_prev, nxt, TRUE, TRUE);
1097 static WRegion *group_navi_first(WGroup *ws, WRegionNavi nh,
1098 WRegionNaviData *data)
1100 WStacking *st=group_do_navi_first(ws, nh);
1102 return region_navi_cont(&ws->reg, (st!=NULL ? st->reg : NULL), data);
1106 static WStacking *group_do_navi_next(WGroup *ws, WStacking *st,
1107 WRegionNavi nh, bool wrap)
1110 return group_do_navi_first(ws, nh);
1112 if(nh==REGION_NAVI_ANY || nh==REGION_NAVI_END ||
1113 nh==REGION_NAVI_BOTTOM || nh==REGION_NAVI_RIGHT){
1114 return do_get_next(ws, st, nxt, wrap, FALSE);
1116 return do_get_next(ws, st, prv, wrap, FALSE);
1120 static WRegion *group_navi_next(WGroup *ws, WRegion *reg,
1121 WRegionNavi nh, WRegionNaviData *data)
1123 WStacking *st=group_find_stacking(ws, reg);
1125 st=group_do_navi_next(ws, st, nh, FALSE);
1127 return region_navi_cont(&ws->reg, (st!=NULL ? st->reg : NULL), data);
1138 * Note: Managed objects are considered to be stacked separately from the
1139 * group, slightly violating expectations.
1142 void group_stacking(WGroup *ws, Window *bottomret, Window *topret)
1144 Window win=region_xwindow((WRegion*)ws);
1151 void group_restack(WGroup *ws, Window other, int mode)
1155 win=region_xwindow((WRegion*)ws);
1157 xwindow_restack(win, other, mode);
1164 WStacking *group_find_stacking(WGroup *ws, WRegion *r)
1166 if(r==NULL || REGION_MANAGER(r)!=(WRegion*)ws)
1169 return ioncore_find_stacking(r);
1173 static WStacking *find_stacking_if_not_on_ws(WGroup *ws, Window w)
1175 WRegion *r=xwindow_region_of(w);
1179 if(REGION_MANAGER(r)==(WRegion*)ws)
1181 st=group_find_stacking(ws, r);
1184 r=REGION_MANAGER(r);
1191 bool group_managed_rqorder(WGroup *grp, WRegion *reg, WRegionOrder order)
1193 WStacking **stackingp=group_get_stackingp(grp);
1196 if(stackingp==NULL || *stackingp==NULL)
1199 st=group_find_stacking(grp, reg);
1204 stacking_restack(stackingp, st, None, NULL, NULL,
1205 (order!=REGION_ORDER_FRONT));
1218 * Iterate over managed regions of \var{ws} until \var{iterfn} returns
1220 * The function is called in protected mode.
1221 * This routine returns \code{true} if it reaches the end of list
1222 * without this happening.
1226 bool group_managed_i(WGroup *ws, ExtlFn iterfn)
1229 group_iter_init(&tmp, ws);
1231 return extl_iter_objlist_(iterfn, (ObjIterator*)group_iter, &tmp);
1235 WRegion* group_current(WGroup *ws)
1237 return (ws->current_managed!=NULL ? ws->current_managed->reg : NULL);
1241 void group_size_hints(WGroup *ws, WSizeHints *hints_ret)
1243 if(ws->bottom==NULL || ws->bottom->reg==NULL){
1244 sizehints_clear(hints_ret);
1246 region_size_hints(ws->bottom->reg, hints_ret);
1247 hints_ret->no_constrain=TRUE;
1252 Window group_xwindow(const WGroup *ws)
1254 return ws->dummywin;
1259 * Returns the group of \var{reg}, if it is managed by one,
1260 * and \var{reg} itself otherwise.
1262 /*EXTL_EXPORT_MEMBER
1263 WRegion *region_group_of(WRegion *reg)
1265 WRegion *mgr=REGION_MANAGER(reg);
1267 return (OBJ_IS(mgr, WGroup) ? mgr : reg);
1272 * Returns the group of \var{reg}, if \var{reg} is its bottom,
1273 * and \var{reg} itself otherwise.
1276 WRegion *region_groupleader_of(WRegion *reg)
1278 WGroup *grp=REGION_MANAGER_CHK(reg, WGroup);
1280 return ((grp!=NULL && group_bottom(grp)==reg)
1292 static ExtlTab group_get_configuration(WGroup *ws)
1294 ExtlTab tab, mgds, subtab, g;
1301 tab=region_get_base_configuration((WRegion*)ws);
1303 mgds=extl_create_table();
1305 extl_table_sets_t(tab, "managed", mgds);
1307 /* TODO: stacking order messed up */
1309 FOR_ALL_NODES_IN_GROUP(ws, st, tmp){
1313 subtab=region_get_configuration(st->reg);
1315 if(subtab!=extl_table_none()){
1316 extl_table_sets_s(subtab, "sizepolicy",
1317 sizepolicy2string(st->szplcy));
1318 extl_table_sets_i(subtab, "level", st->level);
1320 tmpg=REGION_GEOM(st->reg);
1321 tmpg.x-=REGION_GEOM(ws).x;
1322 tmpg.y-=REGION_GEOM(ws).y;
1324 g=extl_table_from_rectangle(&tmpg);
1325 extl_table_sets_t(subtab, "geom", g);
1326 extl_unref_table(g);
1329 extl_table_sets_b(subtab, "bottom", TRUE);
1331 extl_table_seti_t(mgds, ++n, subtab);
1332 extl_unref_table(subtab);
1336 extl_unref_table(mgds);
1342 void group_do_load(WGroup *ws, ExtlTab tab)
1344 ExtlTab substab, subtab;
1347 if(extl_table_gets_t(tab, "managed", &substab)){
1348 n=extl_table_get_n(substab);
1349 for(i=1; i<=n; i++){
1350 if(extl_table_geti_t(substab, i, &subtab)){
1351 WGroupAttachParams par=GROUPATTACHPARAMS_INIT;
1352 WRegionAttachData data;
1356 group_get_attach_params(ws, subtab, &par);
1357 group_attach_fp(ws, &par, &fp);
1359 ph=(WPHolder*)create_grouppholder(ws, NULL, &par);
1361 region_attach_load_helper((WRegion*)ws, REGION_PARENT(ws), &fp,
1362 (WRegionDoAttachFn*)group_do_attach_final,
1363 (void*)&par, subtab, &ph);
1366 destroy_obj((Obj*)ph);
1368 extl_unref_table(subtab);
1372 extl_unref_table(substab);
1377 WRegion *group_load(WWindow *par, const WFitParams *fp, ExtlTab tab)
1381 ws=create_group(par, fp);
1386 group_do_load(ws, tab);
1388 return (WRegion*)ws;
1395 /*{{{ Dynamic function table and class implementation */
1398 static DynFunTab group_dynfuntab[]={
1399 {(DynFun*)region_fitrep,
1400 (DynFun*)group_fitrep},
1408 {(DynFun*)region_managed_prepare_focus,
1409 (DynFun*)group_managed_prepare_focus},
1411 {region_do_set_focus,
1412 group_do_set_focus},
1414 {region_managed_notify,
1415 group_managed_notify},
1417 {region_managed_remove,
1418 group_managed_remove},
1420 {(DynFun*)region_get_configuration,
1421 (DynFun*)group_get_configuration},
1423 {(DynFun*)region_current,
1424 (DynFun*)group_current},
1426 {(DynFun*)region_rescue_clientwins,
1427 (DynFun*)group_rescue_clientwins},
1435 {(DynFun*)region_managed_get_pholder,
1436 (DynFun*)group_managed_get_pholder},
1438 {region_managed_rqgeom,
1439 group_managed_rqgeom},
1441 {region_managed_rqgeom_absolute,
1442 group_managed_rqgeom_absolute},
1444 {(DynFun*)group_do_add_managed,
1445 (DynFun*)group_do_add_managed_default},
1450 {(DynFun*)region_xwindow,
1451 (DynFun*)group_xwindow},
1453 {(DynFun*)region_navi_first,
1454 (DynFun*)group_navi_first},
1456 {(DynFun*)region_navi_next,
1457 (DynFun*)group_navi_next},
1459 {(DynFun*)region_managed_rqorder,
1460 (DynFun*)group_managed_rqorder},
1462 {(DynFun*)region_get_rescue_pholder_for,
1463 (DynFun*)group_get_rescue_pholder_for},
1470 IMPLCLASS(WGroup, WRegion, group_deinit, group_dynfuntab);