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/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"
45 static void group_place_stdisp(WGroup *ws, WWindow *parent,
46 int pos, WRegion *stdisp);
50 /*{{{ Stacking list stuff */
53 WStacking *group_get_stacking(WGroup *ws)
55 WWindow *par=REGION_PARENT(ws);
59 : window_get_stacking(par));
63 WStacking **group_get_stackingp(WGroup *ws)
65 WWindow *par=REGION_PARENT(ws);
69 : window_get_stackingp(par));
73 static bool wsfilt(WStacking *st, void *ws)
75 return (st->reg!=NULL && REGION_MANAGER(st->reg)==(WRegion*)ws);
79 static bool wsfilt_nostdisp(WStacking *st, void *ws)
81 return (wsfilt(st, ws) && ((WGroup*)ws)->managed_stdisp!=st);
85 void group_iter_init(WGroupIterTmp *tmp, WGroup *ws)
87 stacking_iter_mgr_init(tmp, ws->managed_list, NULL, ws);
91 void group_iter_init_nostdisp(WGroupIterTmp *tmp, WGroup *ws)
93 stacking_iter_mgr_init(tmp, ws->managed_list, wsfilt_nostdisp, ws);
97 WRegion *group_iter(WGroupIterTmp *tmp)
99 return stacking_iter_mgr(tmp);
103 WStacking *group_iter_nodes(WGroupIterTmp *tmp)
105 return stacking_iter_mgr_nodes(tmp);
109 WGroupIterTmp group_iter_default_tmp;
115 /*{{{ region dynfun implementations */
118 static void group_fit(WGroup *ws, const WRectangle *geom)
120 REGION_GEOM(ws)=*geom;
124 bool group_fitrep(WGroup *ws, WWindow *par, const WFitParams *fp)
127 WStacking *unweaved=NULL;
128 int xdiff=0, ydiff=0;
133 oldpar=REGION_PARENT(ws);
136 if(fp->mode®ION_FIT_WHATEVER)
138 REGION_GEOM(ws)=fp->g;
140 if(!region_same_rootwin((WRegion*)ws, (WRegion*)par))
143 if(ws->managed_stdisp!=NULL && ws->managed_stdisp->reg!=NULL)
144 region_detach_manager(ws->managed_stdisp->reg);
146 assert(ws->managed_stdisp==NULL);
148 xdiff=fp->g.x-REGION_GEOM(ws).x;
149 ydiff=fp->g.y-REGION_GEOM(ws).y;
151 region_unset_parent((WRegion*)ws);
152 XReparentWindow(ioncore_g.dpy, ws->dummywin, par->win, -1, -1);
153 region_set_parent((WRegion*)ws, par);
155 REGION_GEOM(ws).x=fp->g.x;
156 REGION_GEOM(ws).y=fp->g.y;
157 if(!(fp->mode®ION_FIT_WHATEVER)){
158 REGION_GEOM(ws).w=fp->g.w;
159 REGION_GEOM(ws).h=fp->g.h;
163 unweaved=stacking_unweave(&oldpar->stacking, wsfilt, (void*)ws);
166 FOR_ALL_NODES_IN_GROUP(ws, st, tmp){
172 g=REGION_GEOM(st->reg);
176 if(fp->mode®ION_FIT_WHATEVER){
179 fp2.g=REGION_GEOM(ws);
180 sizepolicy(&st->szplcy, st->reg, &g, REGION_RQGEOM_WEAK_ALL, &fp2);
183 if(!region_fitrep(st->reg, par, &fp2)){
184 warn(TR("Error reparenting %s."), region_name(st->reg));
185 region_detach_manager(st->reg);
190 stacking_weave(&par->stacking, &unweaved, FALSE);
196 static void group_map(WGroup *ws)
201 REGION_MARK_MAPPED(ws);
202 XMapWindow(ioncore_g.dpy, ws->dummywin);
204 FOR_ALL_MANAGED_BY_GROUP(ws, reg, tmp){
210 static void group_unmap(WGroup *ws)
215 REGION_MARK_UNMAPPED(ws);
216 XUnmapWindow(ioncore_g.dpy, ws->dummywin);
218 FOR_ALL_MANAGED_BY_GROUP(ws, reg, tmp){
224 static WStacking *find_to_focus(WGroup *ws, WStacking *st, bool group_only)
226 WStacking *stacking=group_get_stacking(ws);
231 return stacking_find_to_focus_mapped(stacking, st,
232 (group_only ? (WRegion*)ws : NULL));
236 static bool group_refocus_(WGroup *ws, WStacking *st)
238 if(st!=ws->current_managed && st->reg!=NULL){
239 if(region_may_control_focus((WRegion*)ws))
240 region_set_focus(st->reg);
242 ws->current_managed=st;
250 static void group_do_set_focus(WGroup *ws, bool warp)
252 WStacking *st=ws->current_managed;
254 if(st==NULL || st->reg==NULL)
255 st=find_to_focus(ws, NULL, TRUE);
257 if(st!=NULL && st->reg!=NULL)
258 region_do_set_focus(st->reg, warp);
260 region_finalise_focusing((WRegion*)ws, ws->dummywin, warp);
264 static bool group_managed_prepare_focus(WGroup *ws, WRegion *reg,
265 int flags, WPrepareFocusResult *res)
267 WMPlex *mplex=OBJ_CAST(REGION_MANAGER(ws), WMPlex);
268 WStacking *st=group_find_stacking(ws, reg);
274 WStacking *node=mplex_find_stacking(mplex, (WRegion*)ws);
279 return mplex_do_prepare_focus(mplex, node, st,
284 if(!region_prepare_focus((WRegion*)ws, flags, res))
287 stacking=group_get_stacking(ws);
288 st=find_to_focus(ws, st, FALSE);
290 #warning "TODO: raise in some cases (not enter-window)?"
298 return (res->reg==reg);
303 static bool group_essentially_empty(WGroup *ws)
308 FOR_ALL_NODES_IN_GROUP(ws, st, tmp){
309 if(st!=ws->managed_stdisp)
317 void group_managed_remove(WGroup *ws, WRegion *reg)
319 bool mcf=region_may_control_focus((WRegion*)ws);
320 bool ds=OBJ_IS_BEING_DESTROYED(ws);
321 WStacking *st, *next_st=NULL;
322 bool was_stdisp=FALSE, was_bottom=FALSE;
326 st=group_find_stacking(ws, reg);
329 next_st=stacking_unstack(REGION_PARENT(ws), st);
331 UNLINK_ITEM(ws->managed_list, st, mgr_next, mgr_prev);
333 if(st==ws->managed_stdisp){
334 ws->managed_stdisp=NULL;
341 if(ws->bottom_last_close && group_essentially_empty(ws))
345 if(st==ws->current_managed){
347 ws->current_managed=NULL;
350 stacking_unassoc(st);
354 region_unset_manager(reg, (WRegion*)ws);
357 if(was_bottom && !was_stdisp && ws->managed_stdisp==NULL){
358 /* We should probably be managing any stdisp, that 'bottom'
361 WMPlex *mplex=OBJ_CAST(REGION_MANAGER(ws), WMPlex);
364 && mplex->mx_current!=NULL
365 && mplex->mx_current->st->reg==(WRegion*)ws){
366 mplex_remanage_stdisp(mplex);
371 WStacking *stf=find_to_focus(ws, next_st, TRUE);
373 region_warp(stf->reg);
375 }else if(dest && !ds){
376 mainloop_defer_destroy((Obj*)ws);
381 static void group_managed_activated(WGroup *ws, WRegion *reg)
383 ws->current_managed=group_find_stacking(ws, reg);
390 /*{{{ Create/destroy */
393 bool group_init(WGroup *ws, WWindow *par, const WFitParams *fp)
395 ws->current_managed=NULL;
396 ws->managed_stdisp=NULL;
398 ws->managed_list=NULL;
400 ws->dummywin=XCreateWindow(ioncore_g.dpy, par->win,
401 fp->g.x, fp->g.y, 1, 1, 0,
402 CopyFromParent, InputOnly,
403 CopyFromParent, 0, NULL);
404 if(ws->dummywin==None)
407 region_init(&ws->reg, par, fp);
408 region_register(&ws->reg);
410 XSelectInput(ioncore_g.dpy, ws->dummywin,
411 FocusChangeMask|KeyPressMask|KeyReleaseMask|
412 ButtonPressMask|ButtonReleaseMask);
413 XSaveContext(ioncore_g.dpy, ws->dummywin, ioncore_g.win_context,
416 ((WRegion*)ws)->flags|=REGION_GRAB_ON_PARENT;
418 region_add_bindmap((WRegion*)ws, ioncore_group_bindmap);
424 WGroup *create_group(WWindow *par, const WFitParams *fp)
426 CREATEOBJ_IMPL(WGroup, group, (p, par, fp));
430 void group_deinit(WGroup *ws)
435 if(ws->managed_stdisp!=NULL && ws->managed_stdisp->reg!=NULL){
436 group_managed_remove(ws, ws->managed_stdisp->reg);
437 assert(ws->managed_stdisp==NULL);
440 FOR_ALL_MANAGED_BY_GROUP(ws, reg, tmp){
441 destroy_obj((Obj*)reg);
444 assert(ws->managed_list==NULL);
446 XDeleteContext(ioncore_g.dpy, ws->dummywin, ioncore_g.win_context);
447 XDestroyWindow(ioncore_g.dpy, ws->dummywin);
450 region_deinit(&ws->reg);
455 bool group_rescue_clientwins(WGroup *ws, WPHolder *ph)
459 group_iter_init_nostdisp(&tmp, ws);
461 return region_rescue_some_clientwins((WRegion*)ws, ph,
462 (WRegionIterator*)group_iter,
467 bool group_may_destroy(WGroup *ws)
469 bool ret=group_essentially_empty(ws);
471 warn(TR("Workspace not empty - refusing to destroy."));
476 static bool group_managed_may_destroy(WGroup *ws, WRegion *reg)
488 WStacking *group_do_add_managed(WGroup *ws, WRegion *reg, int level,
492 CALL_DYN_RET(st, WStacking*, group_do_add_managed, ws,
493 (ws, reg, level, szplcy));
498 WStacking *group_do_add_managed_default(WGroup *ws, WRegion *reg, int level,
501 WStacking *st=NULL, *tmp=NULL;
502 Window bottom=None, top=None;
503 WStacking **stackingp=group_get_stackingp(ws);
509 st=create_stacking();
514 if(!stacking_assoc(st, reg)){
519 frame=OBJ_CAST(reg, WFrame);
521 WFrameMode m=frame_mode(frame);
522 if(m!=FRAME_MODE_FLOATING && m!=FRAME_MODE_TRANSIENT)
523 frame_set_mode(frame, FRAME_MODE_FLOATING);
529 LINK_ITEM_FIRST(tmp, st, next, prev);
530 stacking_weave(stackingp, &tmp, FALSE);
533 LINK_ITEM(ws->managed_list, st, mgr_next, mgr_prev);
534 region_set_manager(reg, (WRegion*)ws);
536 if(region_is_fully_mapped((WRegion*)ws))
543 static void geom_group_to_parent(WGroup *ws, const WRectangle *g,
546 wg->x=g->x+REGION_GEOM(ws).x;
547 wg->y=g->y+REGION_GEOM(ws).y;
548 wg->w=maxof(1, g->w);
549 wg->h=maxof(1, g->h);
553 bool group_do_attach_final(WGroup *ws,
555 const WGroupAttachParams *param)
557 WStacking *st, *stabove=NULL;
566 szplcy=(param->szplcy_set
569 ? SIZEPOLICY_FULL_EXACT
570 : SIZEPOLICY_UNCONSTRAINED));
572 weak=(param->geom_weak_set
576 : REGION_RQGEOM_WEAK_ALL));
579 geom_group_to_parent(ws, ¶m->geom, &g);
583 /* If the requested geometry does not overlap the workspaces's geometry,
584 * position request is never honoured.
586 if((g.x+g.w<=REGION_GEOM(ws).x) ||
587 (g.y+g.h<=REGION_GEOM(ws).y) ||
588 (g.x>=REGION_GEOM(ws).x+REGION_GEOM(ws).w) ||
589 (g.y>=REGION_GEOM(ws).y+REGION_GEOM(ws).h)){
590 weak|=REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_X;
593 if((weak&(REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y))
594 ==(REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y) &&
595 (szplcy==SIZEPOLICY_UNCONSTRAINED ||
596 szplcy==SIZEPOLICY_FREE ||
597 szplcy==SIZEPOLICY_FREE_GLUE /* without flags */)){
598 group_calc_placement(ws, &g);
601 fp.g=REGION_GEOM(ws);
602 fp.mode=REGION_FIT_EXACT;
604 sizepolicy(&szplcy, reg, &g, weak, &fp);
606 if(rectangle_compare(&fp.g, ®ION_GEOM(reg))!=RECTANGLE_SAME)
607 region_fitrep(reg, NULL, &fp);
610 if(param->stack_above!=NULL)
611 stabove=group_find_stacking(ws, param->stack_above);
617 : STACKING_LEVEL_NORMAL));
619 st=group_do_add_managed(ws, reg, level, szplcy);
631 if(HAS_DYN(reg, region_manage_stdisp) && ws->managed_stdisp!=NULL){
632 WMPlex *mplex=OBJ_CAST(REGION_MANAGER(ws), WMPlex);
633 if(mplex!=NULL){ /* should always hold */
635 WRegion *stdisp=NULL;
636 mplex_get_stdisp(mplex, &stdisp, &di);
638 assert(stdisp==ws->managed_stdisp->reg);
639 /* WARNING! Calls back to group code (managed_remove). */
640 region_manage_stdisp(reg, stdisp, &di);
646 sw=(param->switchto_set ? param->switchto : ioncore_g.switchto_new);
648 if(sw || st->level>=STACKING_LEVEL_MODAL1){
649 WStacking *stf=find_to_focus(ws, st, FALSE);
652 /* Ok, the new region can be focused */
653 group_refocus_(ws, stf);
661 WRegion *group_do_attach(WGroup *ws,
662 /*const*/ WGroupAttachParams *param,
663 WRegionAttachData *data)
669 if(ws->bottom!=NULL && param->bottom){
670 warn(TR("'bottom' already set."));
674 par=REGION_PARENT(ws);
678 geom_group_to_parent(ws, ¶m->geom, &fp.g);
679 fp.mode=REGION_FIT_EXACT;
681 fp.g=REGION_GEOM(ws);
682 fp.mode=REGION_FIT_BOUNDS|REGION_FIT_WHATEVER;
685 return region_attach_helper((WRegion*) ws, par, &fp,
686 (WRegionDoAttachFn*)group_do_attach_final,
687 /*(const WRegionAttachParams*)*/param, data);
688 /* ^^^^ doesn't seem to work. */
692 static void get_params(WGroup *ws, ExtlTab tab, WGroupAttachParams *par)
704 if(extl_table_gets_i(tab, "level", &tmp)){
706 par->level_set=STACKING_LEVEL_NORMAL;
711 if(extl_table_is_bool_set(tab, "bottom")){
712 par->level=STACKING_LEVEL_BOTTOM;
717 if(!par->level_set && extl_table_is_bool_set(tab, "modal")){
718 par->level=STACKING_LEVEL_MODAL1;
722 if(extl_table_is_bool_set(tab, "switchto"))
725 if(extl_table_gets_i(tab, "sizepolicy", &tmp)){
728 }else if(extl_table_gets_s(tab, "sizepolicy", &tmps)){
729 if(string2sizepolicy(tmps, &par->szplcy))
734 if(extl_table_gets_t(tab, "geom", &g)){
737 if(extl_table_gets_i(g, "x", &(par->geom.x)))
739 if(extl_table_gets_i(g, "y", &(par->geom.y)))
741 if(extl_table_gets_i(g, "w", &(par->geom.w)))
743 if(extl_table_gets_i(g, "h", &(par->geom.h)))
756 * Attach and reparent existing region \var{reg} to \var{ws}.
757 * The table \var{param} may contain the fields \var{index} and
758 * \var{switchto} that are interpreted as for \fnref{WMPlex.attach_new}.
761 WRegion *group_attach(WGroup *ws, WRegion *reg, ExtlTab param)
763 WGroupAttachParams par=GROUPATTACHPARAMS_INIT;
764 WRegionAttachData data;
769 get_params(ws, param, &par);
771 data.type=REGION_ATTACH_REPARENT;
774 return group_do_attach(ws, &par, &data);
779 * Create a new region to be managed by \var{ws}. At least the following
780 * fields in \var{param} are understood:
782 * \begin{tabularx}{\linewidth}{lX}
783 * \tabhead{Field & Description}
784 * \var{type} & Class name (a string) of the object to be created. Mandatory. \\
785 * \var{name} & Name of the object to be created (a string). Optional. \\
786 * \var{switchto} & Should the region be switched to (boolean)? Optional. \\
787 * \var{level} & Stacking level; default is 1. \\
788 * \var{modal} & Make object modal; ignored if level is set. \\
789 * \var{sizepolicy} & Size policy. \\
792 * In addition parameters to the region to be created are passed in this
796 WRegion *group_attach_new(WGroup *ws, ExtlTab param)
798 WGroupAttachParams par=GROUPATTACHPARAMS_INIT;
799 WRegionAttachData data;
801 get_params(ws, param, &par);
803 data.type=REGION_ATTACH_LOAD;
806 return group_do_attach(ws, &par, &data);
813 /*{{{ Status display support */
816 static int stdisp_szplcy(const WMPlexSTDispInfo *di, WRegion *stdisp)
821 if(region_orientation(stdisp)==REGION_ORIENTATION_VERTICAL){
822 if(pos==MPLEX_STDISP_TL || pos==MPLEX_STDISP_BL)
823 return SIZEPOLICY_STRETCH_LEFT;
825 return SIZEPOLICY_STRETCH_RIGHT;
827 if(pos==MPLEX_STDISP_TL || pos==MPLEX_STDISP_TR)
828 return SIZEPOLICY_STRETCH_TOP;
830 return SIZEPOLICY_STRETCH_BOTTOM;
833 if(pos==MPLEX_STDISP_TL)
834 return SIZEPOLICY_GRAVITY_NORTHWEST;
835 else if(pos==MPLEX_STDISP_BL)
836 return SIZEPOLICY_GRAVITY_SOUTHWEST;
837 else if(pos==MPLEX_STDISP_TR)
838 return SIZEPOLICY_GRAVITY_NORTHEAST;
839 else /*if(pos=MPLEX_STDISP_BR)*/
840 return SIZEPOLICY_GRAVITY_SOUTHEAST;
845 void group_manage_stdisp(WGroup *ws, WRegion *stdisp,
846 const WMPlexSTDispInfo *di)
850 WRegion *b=(ws->bottom==NULL ? NULL : ws->bottom->reg);
852 /* Check if 'bottom' wants to manage the stdisp. */
854 && !OBJ_IS_BEING_DESTROYED(b)
855 && HAS_DYN(b, region_manage_stdisp)){
856 region_manage_stdisp(b, stdisp, di);
857 if(REGION_MANAGER(stdisp)==b)
863 szplcy=stdisp_szplcy(di, stdisp)|SIZEPOLICY_SHRUNK;
865 if(ws->managed_stdisp!=NULL && ws->managed_stdisp->reg==stdisp){
866 if(ws->managed_stdisp->szplcy==szplcy)
868 ws->managed_stdisp->szplcy=szplcy;
870 region_detach_manager(stdisp);
871 ws->managed_stdisp=group_do_add_managed(ws, stdisp,
872 STACKING_LEVEL_ON_TOP,
876 fp.g=REGION_GEOM(ws);
877 sizepolicy(&ws->managed_stdisp->szplcy, stdisp, NULL, 0, &fp);
879 region_fitrep(stdisp, NULL, &fp);
883 void group_managed_rqgeom(WGroup *ws, WRegion *reg,
884 const WRQGeomParams *rq,
890 st=group_find_stacking(ws, reg);
894 fp.mode=REGION_FIT_EXACT;
896 fp.g=REGION_GEOM(ws);
897 sizepolicy(&st->szplcy, reg, &rq->geom, rq->flags, &fp);
903 if(!(rq->flags®ION_RQGEOM_TRYONLY))
904 region_fitrep(reg, NULL, &fp);
908 void group_managed_rqgeom_absolute(WGroup *grp, WRegion *sub,
909 const WRQGeomParams *rq,
912 if(grp->bottom!=NULL && grp->bottom->reg==sub){
913 region_rqgeom((WRegion*)grp, rq, geomret);
914 if(!(rq->flags®ION_RQGEOM_TRYONLY) && geomret!=NULL)
915 *geomret=REGION_GEOM(sub);
917 WRQGeomParams rq2=*rq;
918 rq2.flags&=~REGION_RQGEOM_ABSOLUTE;
920 region_managed_rqgeom((WRegion*)grp, sub, &rq2, geomret);
931 static WStacking *nxt(WGroup *ws, WStacking *st, bool wrap)
933 return (st->mgr_next!=NULL
935 : (wrap ? ws->managed_list : NULL));
939 static WStacking *prv(WGroup *ws, WStacking *st, bool wrap)
941 return (st!=ws->managed_list
943 : (wrap ? st->mgr_prev : NULL));
947 typedef WStacking *NxtFn(WGroup *ws, WStacking *st, bool wrap);
950 static bool mapped_filt(WStacking *st, void *unused)
952 return (st->reg!=NULL && REGION_IS_MAPPED(st->reg));
956 static bool focusable(WGroup *ws, WStacking *st, uint min_level)
958 return (st->reg!=NULL
959 && REGION_IS_MAPPED(st->reg)
960 && !(st->reg->flags®ION_SKIP_FOCUS)
961 && st->level>=min_level);
965 static WStacking *do_get_next(WGroup *ws, WStacking *sti,
966 NxtFn *fn, bool wrap, bool sti_ok)
968 WStacking *st, *stacking;
971 stacking=group_get_stacking(ws);
974 min_level=stacking_min_level(stacking, mapped_filt, NULL);
980 if(st==NULL || st==sti)
983 if(focusable(ws, st, min_level))
987 if(sti_ok && focusable(ws, sti, min_level))
994 static WStacking *group_do_navi_first(WGroup *ws, WRegionNavi nh)
996 WStacking *lst=ws->managed_list;
1001 if(nh==REGION_NAVI_ANY &&
1002 ws->current_managed!=NULL &&
1003 ws->current_managed->reg!=NULL){
1004 return ws->current_managed;
1007 if(nh==REGION_NAVI_ANY || nh==REGION_NAVI_END ||
1008 nh==REGION_NAVI_BOTTOM || nh==REGION_NAVI_RIGHT){
1009 return do_get_next(ws, lst, prv, TRUE, TRUE);
1011 return do_get_next(ws, lst->mgr_prev, nxt, TRUE, TRUE);
1016 static WRegion *group_navi_first(WGroup *ws, WRegionNavi nh,
1017 WRegionNaviData *data)
1019 WStacking *st=group_do_navi_first(ws, nh);
1021 return region_navi_cont(&ws->reg, (st!=NULL ? st->reg : NULL), data);
1025 static WStacking *group_do_navi_next(WGroup *ws, WStacking *st,
1026 WRegionNavi nh, bool wrap)
1029 return group_do_navi_first(ws, nh);
1031 if(nh==REGION_NAVI_ANY || nh==REGION_NAVI_END ||
1032 nh==REGION_NAVI_BOTTOM || nh==REGION_NAVI_RIGHT){
1033 return do_get_next(ws, st, nxt, wrap, FALSE);
1035 return do_get_next(ws, st, prv, wrap, FALSE);
1039 static WRegion *group_navi_next(WGroup *ws, WRegion *reg,
1040 WRegionNavi nh, WRegionNaviData *data)
1042 WStacking *st=group_find_stacking(ws, reg);
1044 st=group_do_navi_next(ws, st, nh, FALSE);
1046 return region_navi_cont(&ws->reg, (st!=NULL ? st->reg : NULL), data);
1057 * Note: Managed objects are considered to be stacked separately from the
1058 * group, slightly violating expectations.
1061 void group_stacking(WGroup *ws, Window *bottomret, Window *topret)
1063 Window win=region_xwindow((WRegion*)ws);
1070 void group_restack(WGroup *ws, Window other, int mode)
1074 win=region_xwindow((WRegion*)ws);
1076 xwindow_restack(win, other, mode);
1083 WStacking *group_find_stacking(WGroup *ws, WRegion *r)
1087 if(r==NULL || REGION_MANAGER(r)!=(WRegion*)ws)
1090 return ioncore_find_stacking(r);
1094 static WStacking *find_stacking_if_not_on_ws(WGroup *ws, Window w)
1096 WRegion *r=xwindow_region_of(w);
1100 if(REGION_MANAGER(r)==(WRegion*)ws)
1102 st=group_find_stacking(ws, r);
1105 r=REGION_MANAGER(r);
1112 bool group_managed_rqorder(WGroup *grp, WRegion *reg, WRegionOrder order)
1114 WStacking **stackingp=group_get_stackingp(grp);
1117 if(stackingp==NULL || *stackingp==NULL)
1120 st=group_find_stacking(grp, reg);
1125 stacking_restack(stackingp, st, None, NULL, NULL,
1126 (order!=REGION_ORDER_FRONT));
1139 * Returns the 'bottom' of \var{ws}.
1142 WRegion *group_bottom(WGroup *ws)
1144 return (ws->bottom!=NULL ? ws->bottom->reg : NULL);
1149 * Returns a list of regions managed by the workspace (frames, mostly).
1153 ExtlTab group_managed_list(WGroup *ws)
1156 group_iter_init(&tmp, ws);
1158 return extl_obj_iterable_to_table((ObjIterator*)group_iter, &tmp);
1162 WRegion* group_current(WGroup *ws)
1164 return (ws->current_managed!=NULL ? ws->current_managed->reg : NULL);
1168 void group_size_hints(WGroup *ws, WSizeHints *hints_ret)
1170 if(ws->bottom==NULL || ws->bottom->reg==NULL){
1171 sizehints_clear(hints_ret);
1173 region_size_hints(ws->bottom->reg, hints_ret);
1174 hints_ret->no_constrain=TRUE;
1179 Window group_xwindow(const WGroup *ws)
1181 return ws->dummywin;
1191 static ExtlTab group_get_configuration(WGroup *ws)
1193 ExtlTab tab, mgds, subtab, g;
1200 tab=region_get_base_configuration((WRegion*)ws);
1202 mgds=extl_create_table();
1204 extl_table_sets_t(tab, "managed", mgds);
1206 /* TODO: stacking order messed up */
1208 FOR_ALL_NODES_IN_GROUP(ws, st, tmp){
1212 subtab=region_get_configuration(st->reg);
1214 if(subtab!=extl_table_none()){
1215 extl_table_sets_i(subtab, "sizepolicy", st->szplcy);
1216 extl_table_sets_i(subtab, "level", st->level);
1218 tmpg=REGION_GEOM(st->reg);
1219 tmpg.x-=REGION_GEOM(ws).x;
1220 tmpg.y-=REGION_GEOM(ws).y;
1222 g=extl_table_from_rectangle(&tmpg);
1223 extl_table_sets_t(subtab, "geom", g);
1224 extl_unref_table(g);
1227 extl_table_sets_b(subtab, "bottom", TRUE);
1229 extl_table_seti_t(mgds, ++n, subtab);
1230 extl_unref_table(subtab);
1234 extl_unref_table(mgds);
1240 void group_do_load(WGroup *ws, ExtlTab tab)
1242 ExtlTab substab, subtab;
1245 if(extl_table_gets_t(tab, "managed", &substab)){
1246 n=extl_table_get_n(substab);
1247 for(i=1; i<=n; i++){
1248 if(extl_table_geti_t(substab, i, &subtab)){
1249 group_attach_new(ws, subtab);
1250 extl_unref_table(subtab);
1254 extl_unref_table(substab);
1257 ws->bottom_last_close=extl_table_is_bool_set(tab, "bottom_last_close");
1261 WRegion *group_load(WWindow *par, const WFitParams *fp, ExtlTab tab)
1265 ws=create_group(par, fp);
1270 group_do_load(ws, tab);
1272 return (WRegion*)ws;
1279 /*{{{ Dynamic function table and class implementation */
1282 static DynFunTab group_dynfuntab[]={
1283 {(DynFun*)region_fitrep,
1284 (DynFun*)group_fitrep},
1292 {(DynFun*)region_managed_prepare_focus,
1293 (DynFun*)group_managed_prepare_focus},
1295 {region_do_set_focus,
1296 group_do_set_focus},
1298 {region_managed_activated,
1299 group_managed_activated},
1301 {region_managed_remove,
1302 group_managed_remove},
1304 {(DynFun*)region_get_configuration,
1305 (DynFun*)group_get_configuration},
1307 {(DynFun*)region_may_destroy,
1308 (DynFun*)group_may_destroy},
1310 {(DynFun*)region_managed_may_destroy,
1311 (DynFun*)group_managed_may_destroy},
1313 {(DynFun*)region_current,
1314 (DynFun*)group_current},
1316 {(DynFun*)region_rescue_clientwins,
1317 (DynFun*)group_rescue_clientwins},
1325 {(DynFun*)region_managed_get_pholder,
1326 (DynFun*)group_managed_get_pholder},
1328 {region_managed_rqgeom,
1329 group_managed_rqgeom},
1331 {region_managed_rqgeom_absolute,
1332 group_managed_rqgeom_absolute},
1334 {(DynFun*)group_do_add_managed,
1335 (DynFun*)group_do_add_managed_default},
1340 {(DynFun*)region_xwindow,
1341 (DynFun*)group_xwindow},
1343 {(DynFun*)region_navi_first,
1344 (DynFun*)group_navi_first},
1346 {(DynFun*)region_navi_next,
1347 (DynFun*)group_navi_next},
1349 {(DynFun*)region_managed_rqorder,
1350 (DynFun*)group_managed_rqorder},
1357 IMPLCLASS(WGroup, WRegion, group_deinit, group_dynfuntab);