Better error message
[notion/jeffpc.git] / ioncore / group.c
blob6188e4dd1badb2969277ced9c68768f5a6d7026b
1 /*
2 * ion/ioncore/group.c
4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
7 */
9 #include <string.h>
11 #include <X11/Xatom.h>
13 #include <libtu/minmax.h>
14 #include <libtu/objp.h>
15 #include <libmainloop/defer.h>
17 #include <ioncore/property.h>
19 #include "common.h"
20 #include "rootwin.h"
21 #include "focus.h"
22 #include "global.h"
23 #include "region.h"
24 #include "manage.h"
25 #include "screen.h"
26 #include "names.h"
27 #include "saveload.h"
28 #include "attach.h"
29 #include "regbind.h"
30 #include "extlconv.h"
31 #include "xwindow.h"
32 #include "resize.h"
33 #include "stacking.h"
34 #include "sizepolicy.h"
35 #include "bindmaps.h"
36 #include "navi.h"
37 #include "sizehint.h"
38 #include "llist.h"
39 #include "mplex.h"
40 #include "group.h"
41 #include "grouppholder.h"
42 #include "frame.h"
43 #include "float-placement.h"
44 #include "return.h"
47 static void group_place_stdisp(WGroup *ws, WWindow *parent,
48 int pos, WRegion *stdisp);
50 static void group_remanage_stdisp(WGroup *ws);
52 static void group_do_set_bottom(WGroup *grp, WStacking *st);
55 /*{{{ Stacking list stuff */
58 WStacking *group_get_stacking(WGroup *ws)
60 WWindow *par=REGION_PARENT(ws);
62 return (par==NULL
63 ? NULL
64 : window_get_stacking(par));
68 WStacking **group_get_stackingp(WGroup *ws)
70 WWindow *par=REGION_PARENT(ws);
72 return (par==NULL
73 ? NULL
74 : window_get_stackingp(par));
78 static bool wsfilt(WStacking *st, void *ws)
80 return (st->reg!=NULL && REGION_MANAGER(st->reg)==(WRegion*)ws);
84 static bool wsfilt_nostdisp(WStacking *st, void *ws)
86 return (wsfilt(st, ws) && ((WGroup*)ws)->managed_stdisp!=st);
90 void group_iter_init(WGroupIterTmp *tmp, WGroup *ws)
92 stacking_iter_mgr_init(tmp, ws->managed_list, NULL, ws);
96 void group_iter_init_nostdisp(WGroupIterTmp *tmp, WGroup *ws)
98 stacking_iter_mgr_init(tmp, ws->managed_list, wsfilt_nostdisp, ws);
102 WRegion *group_iter(WGroupIterTmp *tmp)
104 return stacking_iter_mgr(tmp);
108 WStacking *group_iter_nodes(WGroupIterTmp *tmp)
110 return stacking_iter_mgr_nodes(tmp);
114 WGroupIterTmp group_iter_default_tmp;
117 /*}}}*/
120 /*{{{ region dynfun implementations */
123 static void group_fit(WGroup *ws, const WRectangle *geom)
125 REGION_GEOM(ws)=*geom;
129 bool group_fitrep(WGroup *ws, WWindow *par, const WFitParams *fp)
131 WGroupIterTmp tmp;
132 WStacking *unweaved=NULL;
133 int xdiff=0, ydiff=0;
134 WStacking *st;
135 WWindow *oldpar;
136 WRectangle g;
138 oldpar=REGION_PARENT(ws);
140 if(par==NULL){
141 if(fp->mode&REGION_FIT_WHATEVER)
142 return TRUE;
143 REGION_GEOM(ws)=fp->g;
144 }else{
145 if(!region_same_rootwin((WRegion*)ws, (WRegion*)par))
146 return FALSE;
148 if(ws->managed_stdisp!=NULL && ws->managed_stdisp->reg!=NULL)
149 region_detach_manager(ws->managed_stdisp->reg);
150 else if(ws->bottom!=NULL && ws->bottom->reg!=NULL &&
151 HAS_DYN(ws->bottom->reg, region_unmanage_stdisp)){
152 /* Usually the stdisp will not be managed by the group itself, but
153 * rather by the WTiling managed by the group, see
154 * group_manage_stdisp. */
155 region_unmanage_stdisp(ws->bottom->reg, TRUE, TRUE);
158 assert(ws->managed_stdisp==NULL);
160 xdiff=fp->g.x-REGION_GEOM(ws).x;
161 ydiff=fp->g.y-REGION_GEOM(ws).y;
163 region_unset_parent((WRegion*)ws);
164 XReparentWindow(ioncore_g.dpy, ws->dummywin, par->win, -1, -1);
165 region_set_parent((WRegion*)ws, par);
167 REGION_GEOM(ws).x=fp->g.x;
168 REGION_GEOM(ws).y=fp->g.y;
169 if(!(fp->mode&REGION_FIT_WHATEVER)){
170 REGION_GEOM(ws).w=fp->g.w;
171 REGION_GEOM(ws).h=fp->g.h;
174 if(oldpar!=NULL)
175 unweaved=stacking_unweave(&oldpar->stacking, wsfilt, (void*)ws);
178 FOR_ALL_NODES_IN_GROUP(ws, st, tmp){
179 WFitParams fp2=*fp;
181 if(st->reg==NULL)
182 continue;
184 g=REGION_GEOM(st->reg);
185 g.x+=xdiff;
186 g.y+=ydiff;
188 if(fp->mode&REGION_FIT_WHATEVER){
189 fp2.g=g;
190 }else{
191 fp2.g=REGION_GEOM(ws);
192 sizepolicy(&st->szplcy, st->reg, &g, REGION_RQGEOM_WEAK_ALL, &fp2);
195 if(!region_fitrep(st->reg, par, &fp2)){
196 warn(TR("Error reparenting %s."), region_name(st->reg));
197 region_detach_manager(st->reg);
201 if(unweaved!=NULL)
202 stacking_weave(&par->stacking, &unweaved, FALSE);
204 return TRUE;
208 static void group_map(WGroup *ws)
210 WRegion *reg;
211 WGroupIterTmp tmp;
213 REGION_MARK_MAPPED(ws);
214 XMapWindow(ioncore_g.dpy, ws->dummywin);
216 FOR_ALL_MANAGED_BY_GROUP(ws, reg, tmp){
217 region_map(reg);
222 static void group_unmap(WGroup *ws)
224 WRegion *reg;
225 WGroupIterTmp tmp;
227 REGION_MARK_UNMAPPED(ws);
228 XUnmapWindow(ioncore_g.dpy, ws->dummywin);
230 FOR_ALL_MANAGED_BY_GROUP(ws, reg, tmp){
231 region_unmap(reg);
236 static WStacking *find_to_focus(WGroup *ws, WStacking *st, bool group_only)
238 WStacking *stacking=group_get_stacking(ws);
240 if(stacking==NULL)
241 return st;
243 return stacking_find_to_focus_mapped(stacking, st,
244 (group_only ? (WRegion*)ws : NULL));
248 static void group_do_set_focus(WGroup *ws, bool warp)
250 WStacking *st=find_to_focus(ws, ws->current_managed, FALSE);
252 if(st!=NULL && st->reg!=NULL)
253 region_do_set_focus(st->reg, warp);
254 else
255 region_finalise_focusing((WRegion*)ws, ws->dummywin, warp, CurrentTime, TRUE);
259 static bool group_managed_prepare_focus(WGroup *ws, WRegion *reg,
260 int flags, WPrepareFocusResult *res)
262 WMPlex *mplex=OBJ_CAST(REGION_MANAGER(ws), WMPlex);
263 WStacking *st=group_find_stacking(ws, reg);
265 if(st==NULL)
266 return FALSE;
268 if(mplex!=NULL){
269 WStacking *node=mplex_find_stacking(mplex, (WRegion*)ws);
271 if(node==NULL)
272 return FALSE;
274 return mplex_do_prepare_focus(mplex, node, st,
275 flags, res);
276 }else{
277 if(!region_prepare_focus((WRegion*)ws, flags, res))
278 return FALSE;
280 st=find_to_focus(ws, st, FALSE);
282 if(st==NULL)
283 return FALSE;
285 if(ioncore_g.autoraise &&
286 !(flags&REGION_GOTO_ENTERWINDOW) &&
287 st->level>STACKING_LEVEL_BOTTOM){
288 WStacking **stackingp=group_get_stackingp(ws);
289 stacking_restack(stackingp, st, None, NULL, NULL, FALSE);
292 res->reg=st->reg;
293 res->flags=flags;
295 return (res->reg==reg);
300 void group_managed_remove(WGroup *ws, WRegion *reg)
302 bool mcf=region_may_control_focus((WRegion*)ws);
303 WStacking *st, *next_st=NULL;
304 bool was_stdisp=FALSE, was_bottom=FALSE;
305 bool was_current=FALSE;
307 st=group_find_stacking(ws, reg);
309 if(st!=NULL){
310 if(st==ws->bottom){
311 was_bottom=TRUE;
312 group_do_set_bottom(ws, NULL);
315 if(st==ws->managed_stdisp){
316 ws->managed_stdisp=NULL;
317 was_stdisp=TRUE;
320 if(st==ws->current_managed){
321 ws->current_managed=NULL;
322 was_current=TRUE;
325 next_st=stacking_unstack(REGION_PARENT(ws), st);
326 UNLINK_ITEM(ws->managed_list, st, mgr_next, mgr_prev);
327 stacking_unassoc(st);
328 stacking_free(st);
331 region_unset_manager(reg, (WRegion*)ws);
333 if(!OBJ_IS_BEING_DESTROYED(ws) && was_current){
334 /* This may still potentially cause problems when focus
335 * change is pending. Perhaps we should use region_await_focus,
336 * if it is pointing to our child (and region_may_control_focus
337 * fail if it is pointing somewhere else).
339 WStacking *stf=find_to_focus(ws, next_st, TRUE);
340 if(stf!=NULL && mcf){
341 region_maybewarp_now(stf->reg, FALSE);
342 }else{
343 ws->current_managed=stf;
349 void group_managed_notify(WGroup *ws, WRegion *reg, WRegionNotify how)
351 if(how==ioncore_g.notifies.activated ||
352 how==ioncore_g.notifies.pseudoactivated){
353 ws->current_managed=group_find_stacking(ws, reg);
358 /*}}}*/
361 /*{{{ Create/destroy */
364 bool group_init(WGroup *ws, WWindow *par, const WFitParams *fp, const char *name)
366 const char *p[1];
368 ws->current_managed=NULL;
369 ws->managed_stdisp=NULL;
370 ws->bottom=NULL;
371 ws->managed_list=NULL;
372 ws->phs=NULL;
374 ws->dummywin=XCreateWindow(ioncore_g.dpy, par->win,
375 fp->g.x, fp->g.y, 1, 1, 0,
376 CopyFromParent, InputOnly,
377 CopyFromParent, 0, NULL);
378 if(ws->dummywin==None)
379 return FALSE;
381 p[0] = name;
382 xwindow_set_text_property(ws->dummywin, XA_WM_NAME, p, 1);
384 region_init(&ws->reg, par, fp);
385 region_register(&ws->reg);
387 XSelectInput(ioncore_g.dpy, ws->dummywin,
388 FocusChangeMask|KeyPressMask|KeyReleaseMask|
389 ButtonPressMask|ButtonReleaseMask);
390 XSaveContext(ioncore_g.dpy, ws->dummywin, ioncore_g.win_context,
391 (XPointer)ws);
393 ((WRegion*)ws)->flags|=REGION_GRAB_ON_PARENT;
395 region_add_bindmap((WRegion*)ws, ioncore_group_bindmap);
397 return TRUE;
401 WGroup *create_group(WWindow *par, const WFitParams *fp, const char *name)
403 CREATEOBJ_IMPL(WGroup, group, (p, par, fp, name));
407 void group_deinit(WGroup *ws)
409 WGroupIterTmp tmp;
410 WRegion *reg;
412 if(ws->managed_stdisp!=NULL && ws->managed_stdisp->reg!=NULL){
413 group_managed_remove(ws, ws->managed_stdisp->reg);
414 assert(ws->managed_stdisp==NULL);
417 FOR_ALL_MANAGED_BY_GROUP(ws, reg, tmp){
418 destroy_obj((Obj*)reg);
421 assert(ws->managed_list==NULL);
423 XDeleteContext(ioncore_g.dpy, ws->dummywin, ioncore_g.win_context);
424 XDestroyWindow(ioncore_g.dpy, ws->dummywin);
425 ws->dummywin=None;
427 while(ws->phs!=NULL)
428 grouppholder_do_unlink(ws->phs);
430 region_deinit(&ws->reg);
434 bool group_rescue_clientwins(WGroup *ws, WRescueInfo *info)
436 WGroupIterTmp tmp;
438 group_iter_init_nostdisp(&tmp, ws);
440 return region_rescue_some_clientwins((WRegion*)ws, info,
441 (WRegionIterator*)group_iter,
442 &tmp);
446 WPHolder *group_get_rescue_pholder_for(WGroup *ws,
447 WRegion *forwhat)
449 WGroupAttachParams ap=GROUPATTACHPARAMS_INIT;
450 WFramedParam fp=FRAMEDPARAM_INIT;
451 WPHolder *ph;
453 ap.geom_set=TRUE;
454 ap.geom=REGION_GEOM(forwhat);
456 ap.geom_weak_set=1;
458 if(REGION_PARENT(forwhat)==REGION_PARENT(ws)){
459 ap.geom.x-=REGION_GEOM(ws).x;
460 ap.geom.y-=REGION_GEOM(ws).y;
461 }else{
462 ap.geom_weak=REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y;
465 /* frame mode */
467 WFrame *frame=OBJ_CAST(forwhat, WFrame);
468 if(frame!=NULL)
469 fp.mode=frame->mode;
472 ph=(WPHolder*)create_grouppholder(ws, NULL, &ap);
474 return pholder_either((WPHolder*)create_framedpholder(ph, &fp), ph);
479 /*}}}*/
482 /*{{{ Bottom */
485 void group_bottom_set(WGroup *grp)
487 CALL_DYN(group_bottom_set, grp, (grp));
491 static void group_do_set_bottom(WGroup *grp, WStacking *st)
493 WStacking *was=grp->bottom;
494 WStacking *std=grp->managed_stdisp;
496 grp->bottom=st;
498 if(!OBJ_IS_BEING_DESTROYED(grp)){
499 bool noremanage=((was==st) ||
500 (was==NULL && std==NULL) ||
501 (st!=NULL && st==std) ||
502 (st==NULL && was==std));
504 if(!noremanage &&
505 (st==NULL || HAS_DYN(st->reg, region_manage_stdisp))){
506 group_remanage_stdisp(grp);
509 group_bottom_set(grp);
514 /*EXTL_DOC
515 * Sets the `bottom' of \var{ws}. The region \var{reg} must already
516 * be managed by \var{ws}, unless \code{nil}.
518 EXTL_EXPORT_MEMBER
519 bool group_set_bottom(WGroup *ws, WRegion *reg)
521 WStacking *st=NULL;
523 if(reg!=NULL){
524 st=group_find_stacking(ws, reg);
526 if(st==NULL)
527 return FALSE;
530 group_do_set_bottom(ws, st);
532 return TRUE;
536 /*EXTL_DOC
537 * Returns the `bottom' of \var{ws}.
539 EXTL_SAFE
540 EXTL_EXPORT_MEMBER
541 WRegion *group_bottom(WGroup *ws)
543 return (ws->bottom!=NULL ? ws->bottom->reg : NULL);
547 /*}}}*/
550 /*{{{ Attach */
553 WStacking *group_do_add_managed(WGroup *ws, WRegion *reg, int level,
554 WSizePolicy szplcy)
556 WStacking *st=NULL;
557 CALL_DYN_RET(st, WStacking*, group_do_add_managed, ws,
558 (ws, reg, level, szplcy));
559 return st;
563 WStacking *group_do_add_managed_default(WGroup *ws, WRegion *reg, int level,
564 WSizePolicy szplcy)
566 WStacking *st=NULL, *tmp=NULL;
567 Window bottom=None, top=None;
568 WStacking **stackingp=group_get_stackingp(ws);
569 WFrame *frame;
571 if(stackingp==NULL)
572 return NULL;
574 st=create_stacking();
576 if(st==NULL)
577 return NULL;
579 if(!stacking_assoc(st, reg)){
580 stacking_free(st);
581 return NULL;
584 frame=OBJ_CAST(reg, WFrame);
585 if(frame!=NULL){
586 if(framemode_unalt(frame_mode(frame))==FRAME_MODE_TILED)
587 frame_set_mode(frame, FRAME_MODE_FLOATING);
590 st->level=level;
591 st->szplcy=szplcy;
593 LINK_ITEM_FIRST(tmp, st, next, prev);
594 stacking_weave(stackingp, &tmp, FALSE);
595 assert(tmp==NULL);
597 LINK_ITEM(ws->managed_list, st, mgr_next, mgr_prev);
598 region_set_manager(reg, (WRegion*)ws);
600 if(region_is_fully_mapped((WRegion*)ws))
601 region_map(reg);
603 return st;
607 static void geom_group_to_parent(WGroup *ws, const WRectangle *g,
608 WRectangle *wg)
610 wg->x=g->x+REGION_GEOM(ws).x;
611 wg->y=g->y+REGION_GEOM(ws).y;
612 wg->w=maxof(1, g->w);
613 wg->h=maxof(1, g->h);
617 static int group_must_focus(WGroup *ws, WStacking *st)
619 WStacking *stacking=group_get_stacking(ws);
621 return (stacking!=NULL && stacking_must_focus(stacking, st));
625 bool group_do_attach_final(WGroup *ws,
626 WRegion *reg,
627 const WGroupAttachParams *param)
629 WStacking *st, *stabove=NULL;
630 WSizePolicy szplcy;
631 WFitParams fp;
632 WRectangle g;
633 uint level;
634 int weak;
635 bool sw;
637 /* Stacking */
638 if(param->stack_above!=NULL)
639 stabove=group_find_stacking(ws, param->stack_above);
641 level=(stabove!=NULL
642 ? stabove->level
643 : (param->level_set
644 ? param->level
645 : STACKING_LEVEL_NORMAL));
647 /* Fit */
648 szplcy=(param->szplcy_set
649 ? param->szplcy
650 : (param->bottom
651 ? SIZEPOLICY_FULL_EXACT
652 : SIZEPOLICY_VISIBILITY_CONSTRAINED));
654 if(!param->whatever){
655 weak=(param->geom_weak_set
656 ? param->geom_weak
657 : (param->geom_set
659 : REGION_RQGEOM_WEAK_ALL));
661 if(param->geom_set)
662 geom_group_to_parent(ws, &param->geom, &g);
663 else
664 g=REGION_GEOM(reg);
666 /* If the requested geometry does not overlap the workspaces's geometry,
667 * position request is never honoured.
669 if((g.x+g.w<=REGION_GEOM(ws).x) ||
670 (g.x>=REGION_GEOM(ws).x+REGION_GEOM(ws).w)){
671 weak|=REGION_RQGEOM_WEAK_X;
674 if((g.y+g.h<=REGION_GEOM(ws).y) ||
675 (g.y>=REGION_GEOM(ws).y+REGION_GEOM(ws).h)){
676 weak|=REGION_RQGEOM_WEAK_Y;
679 if(weak&(REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y) &&
680 (szplcy==SIZEPOLICY_UNCONSTRAINED ||
681 szplcy==SIZEPOLICY_VISIBILITY_CONSTRAINED ||
682 szplcy==SIZEPOLICY_FREE ||
683 szplcy==SIZEPOLICY_FREE_GLUE /* without flags */)){
684 /* TODO: use 'weak'? */
685 group_calc_placement(ws, level, &g);
688 fp.g=REGION_GEOM(ws);
689 fp.mode=REGION_FIT_EXACT;
691 sizepolicy(&szplcy, reg, &g, weak, &fp);
693 if(rectangle_compare(&fp.g, &REGION_GEOM(reg))!=RECTANGLE_SAME)
694 region_fitrep(reg, NULL, &fp);
697 /* Add */
698 st=group_do_add_managed(ws, reg, level, szplcy);
700 if(st==NULL)
701 return FALSE;
703 if(stabove!=NULL)
704 st->above=stabove;
706 if(param->bottom)
707 group_do_set_bottom(ws, st);
709 /* Focus */
710 sw=((param->switchto_set ? param->switchto : ioncore_g.switchto_new)
711 ? st==find_to_focus(ws, st, FALSE)
712 : group_must_focus(ws, st));
714 if(sw){
715 if(region_may_control_focus((WRegion*)ws))
716 region_set_focus(st->reg);
717 else
718 ws->current_managed=st;
719 }else if(region_is_fully_mapped(reg)){
720 region_pointer_focus_hack(reg);
723 return TRUE;
727 static void group_attach_fp(WGroup *ws, const WGroupAttachParams *param,
728 WFitParams *fp)
730 if(param->geom_set){
731 geom_group_to_parent(ws, &param->geom, &fp->g);
732 fp->mode=REGION_FIT_EXACT;
733 }else{
734 fp->g=REGION_GEOM(ws);
735 fp->mode=REGION_FIT_BOUNDS|REGION_FIT_WHATEVER;
740 WRegion *group_do_attach(WGroup *ws,
741 /*const*/ WGroupAttachParams *param,
742 WRegionAttachData *data)
744 WFitParams fp;
745 WRegion *reg;
747 if(ws->bottom!=NULL && param->bottom){
748 warn(TR("'bottom' already set."));
749 return NULL;
752 group_attach_fp(ws, param, &fp);
754 return region_attach_helper((WRegion*) ws, REGION_PARENT(ws), &fp,
755 (WRegionDoAttachFn*)group_do_attach_final,
756 /*(const WRegionAttachParams*)*/param, data);
757 /* ^^^^ doesn't seem to work. */
761 void groupattachparams_get(WGroupAttachParams *par, ExtlTab tab, const char *sub)
763 int tmp;
764 bool tmpb;
765 char *tmps;
766 ExtlTab g;
768 par->switchto_set=0;
769 par->level_set=0;
770 par->szplcy_set=0;
771 par->geom_set=0;
772 par->bottom=0;
774 if(sub){
775 ExtlTab s;
776 if(extl_table_gets_t(tab, sub, &s)){
777 groupattachparams_get(par, s, NULL);
778 extl_unref_table(s);
780 return;
783 if(extl_table_is_bool_set(tab, "bottom")){
784 par->level=STACKING_LEVEL_BOTTOM;
785 par->level_set=1;
786 par->bottom=1;
789 if(extl_table_gets_i(tab, "level", &tmp)){
790 if(tmp>=0){
791 par->level_set=1;
792 par->level=tmp;
796 if(!par->level_set && extl_table_is_bool_set(tab, "modal")){
797 par->level=STACKING_LEVEL_MODAL1;
798 par->level_set=1;
801 if(extl_table_gets_b(tab, "switchto", &tmpb)){
802 par->switchto=(tmpb!=0);
803 par->switchto_set=1;
806 if(extl_table_gets_i(tab, "sizepolicy", &tmp)){
807 par->szplcy_set=1;
808 par->szplcy=tmp;
809 }else if(extl_table_gets_s(tab, "sizepolicy", &tmps)){
810 if(string2sizepolicy(tmps, &par->szplcy))
811 par->szplcy_set=1;
812 free(tmps);
815 if(extl_table_gets_t(tab, "geom", &g)){
816 int n=0;
818 if(extl_table_gets_i(g, "x", &(par->geom.x)))
819 n++;
820 if(extl_table_gets_i(g, "y", &(par->geom.y)))
821 n++;
822 if(extl_table_gets_i(g, "w", &(par->geom.w)))
823 n++;
824 if(extl_table_gets_i(g, "h", &(par->geom.h)))
825 n++;
827 if(n==4)
828 par->geom_set=1;
830 extl_unref_table(g);
833 if(extl_table_gets_b(tab, "auto_placement", &tmpb)){
834 par->geom_weak_set=1;
835 par->geom_weak=(tmpb ? REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y : 0);
841 /*EXTL_DOC
842 * Attach and reparent existing region \var{reg} to \var{ws}.
843 * The table \var{param} may contain the fields \var{index} and
844 * \var{switchto} that are interpreted as for \fnref{WMPlex.attach_new}.
846 EXTL_EXPORT_MEMBER
847 WRegion *group_attach(WGroup *ws, WRegion *reg, ExtlTab param)
849 WGroupAttachParams par=GROUPATTACHPARAMS_INIT;
850 WRegionAttachData data;
852 if(reg==NULL)
853 return NULL;
855 groupattachparams_get(&par, param, NULL);
857 data.type=REGION_ATTACH_REPARENT;
858 data.u.reg=reg;
860 return group_do_attach(ws, &par, &data);
864 /*EXTL_DOC
865 * Create a new region to be managed by \var{ws}. At least the following
866 * fields in \var{param} are understood:
868 * \begin{tabularx}{\linewidth}{lX}
869 * \tabhead{Field & Description}
870 * \var{type} & (string) Class of the object to be created. Mandatory. \\
871 * \var{name} & (string) Name of the object to be created. \\
872 * \var{switchto} & (boolean) Should the region be switched to? \\
873 * \var{level} & (integer) Stacking level; default is 1. \\
874 * \var{modal} & (boolean) Make object modal; ignored if level is set. \\
875 * \var{sizepolicy} & (string) Size policy; see Section \ref{sec:sizepolicies}. \\
876 * \var{bottom} & (boolean) Mark the attached region as the
877 * ``bottom'' of \var{ws}. \\
878 * \end{tabularx}
880 * In addition parameters to the region to be created are passed in this
881 * same table.
883 EXTL_EXPORT_MEMBER
884 WRegion *group_attach_new(WGroup *ws, ExtlTab param)
886 WGroupAttachParams par=GROUPATTACHPARAMS_INIT;
887 WRegionAttachData data;
889 groupattachparams_get(&par, param, NULL);
891 data.type=REGION_ATTACH_LOAD;
892 data.u.tab=param;
894 return group_do_attach(ws, &par, &data);
898 /*}}}*/
901 /*{{{ Status display support */
904 static int stdisp_szplcy(const WMPlexSTDispInfo *di, WRegion *stdisp)
906 int pos=di->pos;
907 int policy=0, gravity=0;
909 if(di->fullsize){
910 if(region_orientation(stdisp)==REGION_ORIENTATION_VERTICAL){
911 if(pos==MPLEX_STDISP_TL || pos==MPLEX_STDISP_BL)
912 policy=SIZEPOLICY_STRETCH_LEFT;
913 else
914 policy=SIZEPOLICY_STRETCH_RIGHT;
915 }else{
916 if(pos==MPLEX_STDISP_TL || pos==MPLEX_STDISP_TR)
917 policy=SIZEPOLICY_STRETCH_TOP;
918 else
919 policy=SIZEPOLICY_STRETCH_BOTTOM;
921 }else{
922 policy=SIZEPOLICY_GRAVITY;
925 if(pos==MPLEX_STDISP_TL)
926 gravity=SIZEPOLICY_VERT_TOP|SIZEPOLICY_HORIZ_LEFT;
927 else if(pos==MPLEX_STDISP_BL)
928 gravity=SIZEPOLICY_VERT_BOTTOM|SIZEPOLICY_HORIZ_LEFT;
929 else if(pos==MPLEX_STDISP_TR)
930 gravity=SIZEPOLICY_VERT_TOP|SIZEPOLICY_HORIZ_RIGHT;
931 else /*if(pos=MPLEX_STDISP_BR)*/
932 gravity=SIZEPOLICY_VERT_BOTTOM|SIZEPOLICY_HORIZ_RIGHT;
934 return (policy|gravity);
938 void group_manage_stdisp(WGroup *ws, WRegion *stdisp,
939 const WMPlexSTDispInfo *di)
941 WFitParams fp;
942 uint szplcy;
943 WRegion *b=(ws->bottom==NULL ? NULL : ws->bottom->reg);
945 /* Check if 'bottom' wants to manage the stdisp. */
946 if(b!=NULL
947 && !OBJ_IS_BEING_DESTROYED(b)
948 && HAS_DYN(b, region_manage_stdisp)){
949 region_manage_stdisp(b, stdisp, di);
950 if(REGION_MANAGER(stdisp)==b)
951 return;
954 /* No. */
956 szplcy=stdisp_szplcy(di, stdisp)|SIZEPOLICY_SHRUNK;
958 if(ws->managed_stdisp!=NULL && ws->managed_stdisp->reg==stdisp){
959 if(ws->managed_stdisp->szplcy==szplcy)
960 return;
961 ws->managed_stdisp->szplcy=szplcy;
962 }else{
963 region_detach_manager(stdisp);
964 ws->managed_stdisp=group_do_add_managed(ws, stdisp,
965 STACKING_LEVEL_ON_TOP,
966 szplcy);
969 stdisp->flags|=REGION_SKIP_FOCUS;
971 fp.g=REGION_GEOM(ws);
972 fp.mode=0;
974 sizepolicy(&ws->managed_stdisp->szplcy, stdisp, NULL, 0, &fp);
976 region_fitrep(stdisp, NULL, &fp);
980 static void group_remanage_stdisp(WGroup *ws)
982 WMPlex *mplex=OBJ_CAST(REGION_MANAGER(ws), WMPlex);
984 if(mplex!=NULL &&
985 mplex->mx_current!=NULL &&
986 mplex->mx_current->st->reg==(WRegion*)ws){
987 mplex_remanage_stdisp(mplex);
992 /*}}}*/
995 /*{{{ Geometry requests */
998 void group_managed_rqgeom(WGroup *ws, WRegion *reg,
999 const WRQGeomParams *rq,
1000 WRectangle *geomret)
1002 WFitParams fp;
1003 WStacking *st;
1005 st=group_find_stacking(ws, reg);
1007 if(st==NULL){
1008 fp.g=rq->geom;
1009 fp.mode=REGION_FIT_EXACT;
1010 }else{
1011 fp.g=REGION_GEOM(ws);
1012 fp.mode=0;
1013 sizepolicy(&st->szplcy, reg, &rq->geom, rq->flags, &fp);
1016 if(geomret!=NULL)
1017 *geomret=fp.g;
1019 if(!(rq->flags&REGION_RQGEOM_TRYONLY))
1020 region_fitrep(reg, NULL, &fp);
1024 void group_managed_rqgeom_absolute(WGroup *grp, WRegion *sub,
1025 const WRQGeomParams *rq,
1026 WRectangle *geomret)
1028 if(grp->bottom!=NULL && grp->bottom->reg==sub){
1029 region_rqgeom((WRegion*)grp, rq, geomret);
1030 if(!(rq->flags&REGION_RQGEOM_TRYONLY) && geomret!=NULL)
1031 *geomret=REGION_GEOM(sub);
1032 }else{
1033 WRQGeomParams rq2=*rq;
1034 rq2.flags&=~REGION_RQGEOM_ABSOLUTE;
1036 region_managed_rqgeom((WRegion*)grp, sub, &rq2, geomret);
1041 /*}}}*/
1044 /*{{{ Navigation */
1047 static WStacking *nxt(WGroup *ws, WStacking *st, bool wrap)
1049 return (st->mgr_next!=NULL
1050 ? st->mgr_next
1051 : (wrap ? ws->managed_list : NULL));
1055 static WStacking *prv(WGroup *ws, WStacking *st, bool wrap)
1057 return (st!=ws->managed_list
1058 ? st->mgr_prev
1059 : (wrap ? st->mgr_prev : NULL));
1063 typedef WStacking *NxtFn(WGroup *ws, WStacking *st, bool wrap);
1066 static bool focusable(WGroup *ws, WStacking *st, uint min_level)
1068 return (st->reg!=NULL
1069 && REGION_IS_MAPPED(st->reg)
1070 && !(st->reg->flags&REGION_SKIP_FOCUS)
1071 && st->level>=min_level);
1075 static WStacking *do_get_next(WGroup *ws, WStacking *sti,
1076 NxtFn *fn, bool wrap, bool sti_ok)
1078 WStacking *st, *stacking;
1079 uint min_level=0;
1081 stacking=group_get_stacking(ws);
1083 if(stacking!=NULL)
1084 min_level=stacking_min_level_mapped(stacking);
1086 st=sti;
1087 while(1){
1088 st=fn(ws, st, wrap);
1090 if(st==NULL || st==sti)
1091 break;
1093 if(focusable(ws, st, min_level))
1094 return st;
1097 if(sti_ok && focusable(ws, sti, min_level))
1098 return sti;
1100 return NULL;
1104 static WStacking *group_do_navi_first(WGroup *ws, WRegionNavi nh)
1106 WStacking *lst=ws->managed_list;
1108 if(lst==NULL)
1109 return NULL;
1111 if(nh==REGION_NAVI_ANY &&
1112 ws->current_managed!=NULL &&
1113 ws->current_managed->reg!=NULL){
1114 return ws->current_managed;
1117 if(nh==REGION_NAVI_ANY || nh==REGION_NAVI_END ||
1118 nh==REGION_NAVI_BOTTOM || nh==REGION_NAVI_RIGHT){
1119 return do_get_next(ws, lst, prv, TRUE, TRUE);
1120 }else{
1121 return do_get_next(ws, lst->mgr_prev, nxt, TRUE, TRUE);
1126 static WRegion *group_navi_first(WGroup *ws, WRegionNavi nh,
1127 WRegionNaviData *data)
1129 WStacking *st=group_do_navi_first(ws, nh);
1131 return region_navi_cont(&ws->reg, (st!=NULL ? st->reg : NULL), data);
1135 static WStacking *group_do_navi_next(WGroup *ws, WStacking *st,
1136 WRegionNavi nh, bool wrap)
1138 if(st==NULL)
1139 return group_do_navi_first(ws, nh);
1141 if(nh==REGION_NAVI_ANY || nh==REGION_NAVI_END ||
1142 nh==REGION_NAVI_BOTTOM || nh==REGION_NAVI_RIGHT){
1143 return do_get_next(ws, st, nxt, wrap, FALSE);
1144 }else{
1145 return do_get_next(ws, st, prv, wrap, FALSE);
1149 static WRegion *group_navi_next(WGroup *ws, WRegion *reg,
1150 WRegionNavi nh, WRegionNaviData *data)
1152 WStacking *st=group_find_stacking(ws, reg);
1154 st=group_do_navi_next(ws, st, nh, FALSE);
1156 return region_navi_cont(&ws->reg, (st!=NULL ? st->reg : NULL), data);
1160 /*}}}*/
1163 /*{{{ Stacking */
1167 * Note: Managed objects are considered to be stacked separately from the
1168 * group, slightly violating expectations.
1171 void group_stacking(WGroup *ws, Window *bottomret, Window *topret)
1173 Window win=region_xwindow((WRegion*)ws);
1175 *bottomret=win;
1176 *topret=win;
1180 void group_restack(WGroup *ws, Window other, int mode)
1182 Window win;
1184 win=region_xwindow((WRegion*)ws);
1185 if(win!=None){
1186 xwindow_restack(win, other, mode);
1187 other=win;
1188 mode=Above;
1193 WStacking *group_find_stacking(WGroup *ws, WRegion *r)
1195 if(r==NULL || REGION_MANAGER(r)!=(WRegion*)ws)
1196 return NULL;
1198 return ioncore_find_stacking(r);
1202 static WStacking *find_stacking_if_not_on_ws(WGroup *ws, Window w)
1204 WRegion *r=xwindow_region_of(w);
1205 WStacking *st=NULL;
1207 while(r!=NULL){
1208 if(REGION_MANAGER(r)==(WRegion*)ws)
1209 break;
1210 st=group_find_stacking(ws, r);
1211 if(st!=NULL)
1212 break;
1213 r=REGION_MANAGER(r);
1216 return st;
1220 bool group_managed_rqorder(WGroup *grp, WRegion *reg, WRegionOrder order)
1222 WStacking **stackingp=group_get_stackingp(grp);
1223 WStacking *st;
1225 if(stackingp==NULL || *stackingp==NULL)
1226 return FALSE;
1228 st=group_find_stacking(grp, reg);
1230 if(st==NULL)
1231 return FALSE;
1233 stacking_restack(stackingp, st, None, NULL, NULL,
1234 (order!=REGION_ORDER_FRONT));
1236 return TRUE;
1240 /*}}}*/
1243 /*{{{ Misc. */
1246 /*EXTL_DOC
1247 * Iterate over managed regions of \var{ws} until \var{iterfn} returns
1248 * \code{false}.
1249 * The function is called in protected mode.
1250 * This routine returns \code{true} if it reaches the end of list
1251 * without this happening.
1253 EXTL_SAFE
1254 EXTL_EXPORT_MEMBER
1255 bool group_managed_i(WGroup *ws, ExtlFn iterfn)
1257 WGroupIterTmp tmp;
1258 group_iter_init(&tmp, ws);
1260 return extl_iter_objlist_(iterfn, (ObjIterator*)group_iter, &tmp);
1264 WRegion* group_current(WGroup *ws)
1266 return (ws->current_managed!=NULL ? ws->current_managed->reg : NULL);
1270 void group_size_hints(WGroup *ws, WSizeHints *hints_ret)
1272 if(ws->bottom==NULL || ws->bottom->reg==NULL){
1273 sizehints_clear(hints_ret);
1274 }else{
1275 region_size_hints(ws->bottom->reg, hints_ret);
1276 hints_ret->no_constrain=TRUE;
1281 Window group_xwindow(const WGroup *ws)
1283 return ws->dummywin;
1287 /*EXTL_DOC
1288 * Returns the group of \var{reg}, if it is managed by one,
1289 * and \var{reg} itself otherwise.
1291 /*EXTL_EXPORT_MEMBER
1292 WRegion *region_group_of(WRegion *reg)
1294 WRegion *mgr=REGION_MANAGER(reg);
1296 return (OBJ_IS(mgr, WGroup) ? mgr : reg);
1300 /*EXTL_DOC
1301 * Returns the group of \var{reg}, if \var{reg} is its bottom,
1302 * and \var{reg} itself otherwise.
1304 EXTL_EXPORT_MEMBER
1305 WRegion *region_groupleader_of(WRegion *reg)
1307 WGroup *grp=REGION_MANAGER_CHK(reg, WGroup);
1309 return ((grp!=NULL && group_bottom(grp)==reg)
1310 ? (WRegion*)grp
1311 : reg);
1315 /*}}}*/
1318 /*{{{ Save/load */
1321 ExtlTab group_get_configuration(WGroup *ws)
1323 ExtlTab tab, mgds, subtab, g;
1324 WStacking *st;
1325 WGroupIterTmp tmp;
1326 WMPlex *par;
1327 int n=0;
1328 WRectangle tmpg;
1330 tab=region_get_base_configuration((WRegion*)ws);
1332 mgds=extl_create_table();
1334 extl_table_sets_t(tab, "managed", mgds);
1336 /* TODO: stacking order messed up */
1338 FOR_ALL_NODES_IN_GROUP(ws, st, tmp){
1339 if(st->reg==NULL)
1340 continue;
1342 subtab=region_get_configuration(st->reg);
1344 if(subtab!=extl_table_none()){
1345 extl_table_sets_s(subtab, "sizepolicy",
1346 sizepolicy2string(st->szplcy));
1347 extl_table_sets_i(subtab, "level", st->level);
1349 tmpg=REGION_GEOM(st->reg);
1350 tmpg.x-=REGION_GEOM(ws).x;
1351 tmpg.y-=REGION_GEOM(ws).y;
1353 g=extl_table_from_rectangle(&tmpg);
1354 extl_table_sets_t(subtab, "geom", g);
1355 extl_unref_table(g);
1357 if(ws->bottom==st)
1358 extl_table_sets_b(subtab, "bottom", TRUE);
1360 extl_table_seti_t(mgds, ++n, subtab);
1361 extl_unref_table(subtab);
1365 extl_unref_table(mgds);
1367 return tab;
1371 void group_do_load(WGroup *ws, ExtlTab tab)
1373 ExtlTab substab, subtab;
1374 int i, n;
1376 if(extl_table_gets_t(tab, "managed", &substab)){
1377 n=extl_table_get_n(substab);
1378 for(i=1; i<=n; i++){
1379 if(extl_table_geti_t(substab, i, &subtab)){
1380 WGroupAttachParams par=GROUPATTACHPARAMS_INIT;
1381 WRegionAttachData data;
1382 WFitParams fp;
1383 WPHolder *ph;
1385 groupattachparams_get(&par, subtab, NULL);
1386 group_attach_fp(ws, &par, &fp);
1388 ph=(WPHolder*)create_grouppholder(ws, NULL, &par);
1390 region_attach_load_helper((WRegion*)ws, REGION_PARENT(ws), &fp,
1391 (WRegionDoAttachFn*)group_do_attach_final,
1392 (void*)&par, subtab, &ph);
1394 if(ph!=NULL)
1395 destroy_obj((Obj*)ph);
1397 extl_unref_table(subtab);
1401 extl_unref_table(substab);
1406 WRegion *group_loaj(WWindow *par, const WFitParams *fp, ExtlTab tab)
1408 WGroup *ws;
1410 /* Generic initial name - to be overwritten later. */
1411 ws=create_group(par, fp, "Notion GroupCW or GroupWS");
1413 if(ws==NULL)
1414 return NULL;
1416 group_do_load(ws, tab);
1418 return (WRegion*)ws;
1422 /*}}}*/
1425 /*{{{ Dynamic function table and class implementation */
1428 static DynFunTab group_dynfuntab[]={
1429 {(DynFun*)region_fitrep,
1430 (DynFun*)group_fitrep},
1432 {region_map,
1433 group_map},
1435 {region_unmap,
1436 group_unmap},
1438 {(DynFun*)region_managed_prepare_focus,
1439 (DynFun*)group_managed_prepare_focus},
1441 {region_do_set_focus,
1442 group_do_set_focus},
1444 {region_managed_notify,
1445 group_managed_notify},
1447 {region_managed_remove,
1448 group_managed_remove},
1450 {(DynFun*)region_get_configuration,
1451 (DynFun*)group_get_configuration},
1453 {(DynFun*)region_current,
1454 (DynFun*)group_current},
1456 {(DynFun*)region_rescue_clientwins,
1457 (DynFun*)group_rescue_clientwins},
1459 {region_restack,
1460 group_restack},
1462 {region_stacking,
1463 group_stacking},
1465 {(DynFun*)region_managed_get_pholder,
1466 (DynFun*)group_managed_get_pholder},
1468 {region_managed_rqgeom,
1469 group_managed_rqgeom},
1471 {region_managed_rqgeom_absolute,
1472 group_managed_rqgeom_absolute},
1474 {(DynFun*)group_do_add_managed,
1475 (DynFun*)group_do_add_managed_default},
1477 {region_size_hints,
1478 group_size_hints},
1480 {(DynFun*)region_xwindow,
1481 (DynFun*)group_xwindow},
1483 {(DynFun*)region_navi_first,
1484 (DynFun*)group_navi_first},
1486 {(DynFun*)region_navi_next,
1487 (DynFun*)group_navi_next},
1489 {(DynFun*)region_managed_rqorder,
1490 (DynFun*)group_managed_rqorder},
1492 {(DynFun*)region_get_rescue_pholder_for,
1493 (DynFun*)group_get_rescue_pholder_for},
1495 END_DYNFUNTAB
1499 EXTL_EXPORT
1500 IMPLCLASS(WGroup, WRegion, group_deinit, group_dynfuntab);
1503 /*}}}*/