Remove/mark some unused functions and parameters
[notion.git] / ioncore / group.c
blob2c693b4b61778566ca5157e18ceb3ddb62b8bf3f
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_remanage_stdisp(WGroup *ws);
49 static void group_do_set_bottom(WGroup *grp, WStacking *st);
52 /*{{{ Stacking list stuff */
55 WStacking *group_get_stacking(WGroup *ws)
57 WWindow *par=REGION_PARENT(ws);
59 return (par==NULL
60 ? NULL
61 : window_get_stacking(par));
65 WStacking **group_get_stackingp(WGroup *ws)
67 WWindow *par=REGION_PARENT(ws);
69 return (par==NULL
70 ? NULL
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;
114 /*}}}*/
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)
128 WGroupIterTmp tmp;
129 WStacking *unweaved=NULL;
130 int xdiff=0, ydiff=0;
131 WStacking *st;
132 WWindow *oldpar;
133 WRectangle g;
135 oldpar=REGION_PARENT(ws);
137 if(par==NULL){
138 if(fp->mode&REGION_FIT_WHATEVER)
139 return TRUE;
140 REGION_GEOM(ws)=fp->g;
141 }else{
142 if(!region_same_rootwin((WRegion*)ws, (WRegion*)par))
143 return FALSE;
145 if(ws->managed_stdisp!=NULL && ws->managed_stdisp->reg!=NULL)
146 region_detach_manager(ws->managed_stdisp->reg);
147 else if(ws->bottom!=NULL && ws->bottom->reg!=NULL &&
148 HAS_DYN(ws->bottom->reg, region_unmanage_stdisp)){
149 /* Usually the stdisp will not be managed by the group itself, but
150 * rather by the WTiling managed by the group, see
151 * group_manage_stdisp. */
152 region_unmanage_stdisp(ws->bottom->reg, TRUE, TRUE);
155 assert(ws->managed_stdisp==NULL);
157 xdiff=fp->g.x-REGION_GEOM(ws).x;
158 ydiff=fp->g.y-REGION_GEOM(ws).y;
160 region_unset_parent((WRegion*)ws);
161 XReparentWindow(ioncore_g.dpy, ws->dummywin, par->win, -1, -1);
162 region_set_parent((WRegion*)ws, par);
164 REGION_GEOM(ws).x=fp->g.x;
165 REGION_GEOM(ws).y=fp->g.y;
166 if(!(fp->mode&REGION_FIT_WHATEVER)){
167 REGION_GEOM(ws).w=fp->g.w;
168 REGION_GEOM(ws).h=fp->g.h;
171 if(oldpar!=NULL)
172 unweaved=stacking_unweave(&oldpar->stacking, wsfilt, (void*)ws);
175 FOR_ALL_NODES_IN_GROUP(ws, st, tmp){
176 WFitParams fp2=*fp;
178 if(st->reg==NULL)
179 continue;
181 g=REGION_GEOM(st->reg);
182 g.x+=xdiff;
183 g.y+=ydiff;
185 if(fp->mode&REGION_FIT_WHATEVER){
186 fp2.g=g;
187 }else{
188 fp2.g=REGION_GEOM(ws);
189 sizepolicy(&st->szplcy, st->reg, &g, REGION_RQGEOM_WEAK_ALL, &fp2);
192 if(!region_fitrep(st->reg, par, &fp2)){
193 warn(TR("Error reparenting %s."), region_name(st->reg));
194 region_detach_manager(st->reg);
198 if(unweaved!=NULL)
199 stacking_weave(&par->stacking, &unweaved, FALSE);
201 return TRUE;
205 static void group_map(WGroup *ws)
207 WRegion *reg;
208 WGroupIterTmp tmp;
210 REGION_MARK_MAPPED(ws);
211 XMapWindow(ioncore_g.dpy, ws->dummywin);
213 FOR_ALL_MANAGED_BY_GROUP(ws, reg, tmp){
214 region_map(reg);
219 static void group_unmap(WGroup *ws)
221 WRegion *reg;
222 WGroupIterTmp tmp;
224 REGION_MARK_UNMAPPED(ws);
225 XUnmapWindow(ioncore_g.dpy, ws->dummywin);
227 FOR_ALL_MANAGED_BY_GROUP(ws, reg, tmp){
228 region_unmap(reg);
233 static WStacking *find_to_focus(WGroup *ws, WStacking *st, bool group_only)
235 WStacking *stacking=group_get_stacking(ws);
237 if(stacking==NULL)
238 return st;
240 return stacking_find_to_focus_mapped(stacking, st,
241 (group_only ? (WRegion*)ws : NULL));
245 static void group_do_set_focus(WGroup *ws, bool warp)
247 WStacking *st=find_to_focus(ws, ws->current_managed, FALSE);
249 if(st!=NULL && st->reg!=NULL)
250 region_do_set_focus(st->reg, warp);
251 else
252 region_finalise_focusing((WRegion*)ws, ws->dummywin, warp, CurrentTime, TRUE);
256 static bool group_managed_prepare_focus(WGroup *ws, WRegion *reg,
257 int flags, WPrepareFocusResult *res)
259 WMPlex *mplex=OBJ_CAST(REGION_MANAGER(ws), WMPlex);
260 WStacking *st=group_find_stacking(ws, reg);
262 if(st==NULL)
263 return FALSE;
265 if(mplex!=NULL){
266 WStacking *node=mplex_find_stacking(mplex, (WRegion*)ws);
268 if(node==NULL)
269 return FALSE;
271 return mplex_do_prepare_focus(mplex, node, st,
272 flags, res);
273 }else{
274 if(!region_prepare_focus((WRegion*)ws, flags, res))
275 return FALSE;
277 st=find_to_focus(ws, st, FALSE);
279 if(st==NULL)
280 return FALSE;
282 if(ioncore_g.autoraise &&
283 !(flags&REGION_GOTO_ENTERWINDOW) &&
284 st->level>STACKING_LEVEL_BOTTOM){
285 WStacking **stackingp=group_get_stackingp(ws);
286 stacking_restack(stackingp, st, None, NULL, NULL, FALSE);
289 res->reg=st->reg;
290 res->flags=flags;
292 return (res->reg==reg);
297 void group_managed_remove(WGroup *ws, WRegion *reg)
299 bool mcf=region_may_control_focus((WRegion*)ws);
300 WStacking *st, *next_st=NULL;
301 bool was_current=FALSE;
303 st=group_find_stacking(ws, reg);
305 if(st!=NULL){
306 if(st==ws->bottom)
307 group_do_set_bottom(ws, NULL);
309 if(st==ws->managed_stdisp)
310 ws->managed_stdisp=NULL;
312 if(st==ws->current_managed){
313 ws->current_managed=NULL;
314 was_current=TRUE;
317 next_st=stacking_unstack(REGION_PARENT(ws), st);
318 UNLINK_ITEM(ws->managed_list, st, mgr_next, mgr_prev);
319 stacking_unassoc(st);
320 stacking_free(st);
323 region_unset_manager(reg, (WRegion*)ws);
325 if(!OBJ_IS_BEING_DESTROYED(ws) && was_current){
326 /* This may still potentially cause problems when focus
327 * change is pending. Perhaps we should use region_await_focus,
328 * if it is pointing to our child (and region_may_control_focus
329 * fail if it is pointing somewhere else).
331 WStacking *stf=find_to_focus(ws, next_st, TRUE);
332 if(stf!=NULL && mcf){
333 region_maybewarp_now(stf->reg, FALSE);
334 }else{
335 ws->current_managed=stf;
341 void group_managed_notify(WGroup *ws, WRegion *reg, WRegionNotify how)
343 if(how==ioncore_g.notifies.activated ||
344 how==ioncore_g.notifies.pseudoactivated){
345 ws->current_managed=group_find_stacking(ws, reg);
350 /*}}}*/
353 /*{{{ Create/destroy */
356 bool group_init(WGroup *ws, WWindow *par, const WFitParams *fp, const char *name)
358 const char *p[1];
360 ws->current_managed=NULL;
361 ws->managed_stdisp=NULL;
362 ws->bottom=NULL;
363 ws->managed_list=NULL;
364 ws->phs=NULL;
366 ws->dummywin=XCreateWindow(ioncore_g.dpy, par->win,
367 fp->g.x, fp->g.y, 1, 1, 0,
368 CopyFromParent, InputOnly,
369 CopyFromParent, 0, NULL);
370 if(ws->dummywin==None)
371 return FALSE;
373 p[0] = name;
374 xwindow_set_text_property(ws->dummywin, XA_WM_NAME, p, 1);
376 region_init(&ws->reg, par, fp);
377 region_register(&ws->reg);
379 XSelectInput(ioncore_g.dpy, ws->dummywin,
380 FocusChangeMask|KeyPressMask|KeyReleaseMask|
381 ButtonPressMask|ButtonReleaseMask);
382 XSaveContext(ioncore_g.dpy, ws->dummywin, ioncore_g.win_context,
383 (XPointer)ws);
385 ((WRegion*)ws)->flags|=REGION_GRAB_ON_PARENT;
387 region_add_bindmap((WRegion*)ws, ioncore_group_bindmap);
389 return TRUE;
393 WGroup *create_group(WWindow *par, const WFitParams *fp, const char *name)
395 CREATEOBJ_IMPL(WGroup, group, (p, par, fp, name));
399 void group_deinit(WGroup *ws)
401 WGroupIterTmp tmp;
402 WRegion *reg;
404 if(ws->managed_stdisp!=NULL && ws->managed_stdisp->reg!=NULL){
405 group_managed_remove(ws, ws->managed_stdisp->reg);
406 assert(ws->managed_stdisp==NULL);
409 FOR_ALL_MANAGED_BY_GROUP(ws, reg, tmp){
410 destroy_obj((Obj*)reg);
413 assert(ws->managed_list==NULL);
415 XDeleteContext(ioncore_g.dpy, ws->dummywin, ioncore_g.win_context);
416 XDestroyWindow(ioncore_g.dpy, ws->dummywin);
417 ws->dummywin=None;
419 while(ws->phs!=NULL)
420 grouppholder_do_unlink(ws->phs);
422 region_deinit(&ws->reg);
426 bool group_rescue_clientwins(WGroup *ws, WRescueInfo *info)
428 WGroupIterTmp tmp;
430 group_iter_init_nostdisp(&tmp, ws);
432 return region_rescue_some_clientwins((WRegion*)ws, info,
433 (WRegionIterator*)group_iter,
434 &tmp);
438 WPHolder *group_get_rescue_pholder_for(WGroup *ws,
439 WRegion *forwhat)
441 WGroupAttachParams ap=GROUPATTACHPARAMS_INIT;
442 WFramedParam fp=FRAMEDPARAM_INIT;
443 WPHolder *ph;
445 ap.geom_set=TRUE;
446 ap.geom=REGION_GEOM(forwhat);
448 ap.geom_weak_set=1;
450 if(REGION_PARENT(forwhat)==REGION_PARENT(ws)){
451 ap.geom.x-=REGION_GEOM(ws).x;
452 ap.geom.y-=REGION_GEOM(ws).y;
453 }else{
454 ap.geom_weak=REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y;
457 /* frame mode */
459 WFrame *frame=OBJ_CAST(forwhat, WFrame);
460 if(frame!=NULL)
461 fp.mode=frame->mode;
464 ph=(WPHolder*)create_grouppholder(ws, NULL, &ap);
466 return pholder_either((WPHolder*)create_framedpholder(ph, &fp), ph);
471 /*}}}*/
474 /*{{{ Bottom */
477 void group_bottom_set(WGroup *grp)
479 CALL_DYN(group_bottom_set, grp, (grp));
483 static void group_do_set_bottom(WGroup *grp, WStacking *st)
485 WStacking *was=grp->bottom;
486 WStacking *std=grp->managed_stdisp;
488 grp->bottom=st;
490 if(!OBJ_IS_BEING_DESTROYED(grp)){
491 bool noremanage=((was==st) ||
492 (was==NULL && std==NULL) ||
493 (st!=NULL && st==std) ||
494 (st==NULL && was==std));
496 if(!noremanage &&
497 (st==NULL || HAS_DYN(st->reg, region_manage_stdisp))){
498 group_remanage_stdisp(grp);
501 group_bottom_set(grp);
506 /*EXTL_DOC
507 * Sets the `bottom' of \var{ws}. The region \var{reg} must already
508 * be managed by \var{ws}, unless \code{nil}.
510 EXTL_EXPORT_MEMBER
511 bool group_set_bottom(WGroup *ws, WRegion *reg)
513 WStacking *st=NULL;
515 if(reg!=NULL){
516 st=group_find_stacking(ws, reg);
518 if(st==NULL)
519 return FALSE;
522 group_do_set_bottom(ws, st);
524 return TRUE;
528 /*EXTL_DOC
529 * Returns the `bottom' of \var{ws}.
531 EXTL_SAFE
532 EXTL_EXPORT_MEMBER
533 WRegion *group_bottom(WGroup *ws)
535 return (ws->bottom!=NULL ? ws->bottom->reg : NULL);
539 /*}}}*/
542 /*{{{ Attach */
545 WStacking *group_do_add_managed(WGroup *ws, WRegion *reg, int level,
546 WSizePolicy szplcy)
548 WStacking *st=NULL;
549 CALL_DYN_RET(st, WStacking*, group_do_add_managed, ws,
550 (ws, reg, level, szplcy));
551 return st;
555 WStacking *group_do_add_managed_default(WGroup *ws, WRegion *reg, int level,
556 WSizePolicy szplcy)
558 WStacking *st=NULL, *tmp=NULL;
559 WStacking **stackingp=group_get_stackingp(ws);
560 WFrame *frame;
562 if(stackingp==NULL)
563 return NULL;
565 st=create_stacking();
567 if(st==NULL)
568 return NULL;
570 if(!stacking_assoc(st, reg)){
571 stacking_free(st);
572 return NULL;
575 frame=OBJ_CAST(reg, WFrame);
576 if(frame!=NULL){
577 if(framemode_unalt(frame_mode(frame))==FRAME_MODE_TILED)
578 frame_set_mode(frame, FRAME_MODE_FLOATING);
581 st->level=level;
582 st->szplcy=szplcy;
584 LINK_ITEM_FIRST(tmp, st, next, prev);
585 stacking_weave(stackingp, &tmp, FALSE);
586 assert(tmp==NULL);
588 LINK_ITEM(ws->managed_list, st, mgr_next, mgr_prev);
589 region_set_manager(reg, (WRegion*)ws);
591 if(region_is_fully_mapped((WRegion*)ws))
592 region_map(reg);
594 return st;
598 static void geom_group_to_parent(WGroup *ws, const WRectangle *g,
599 WRectangle *wg)
601 wg->x=g->x+REGION_GEOM(ws).x;
602 wg->y=g->y+REGION_GEOM(ws).y;
603 wg->w=maxof(1, g->w);
604 wg->h=maxof(1, g->h);
608 static int group_must_focus(WGroup *ws, WStacking *st)
610 WStacking *stacking=group_get_stacking(ws);
612 return (stacking!=NULL && stacking_must_focus(stacking, st));
616 bool group_do_attach_final(WGroup *ws,
617 WRegion *reg,
618 const WGroupAttachParams *param)
620 WStacking *st, *stabove=NULL;
621 WSizePolicy szplcy;
622 WFitParams fp;
623 WRectangle g;
624 uint level;
625 int weak;
626 bool sw;
628 /* Stacking */
629 if(param->stack_above!=NULL)
630 stabove=group_find_stacking(ws, param->stack_above);
632 level=(stabove!=NULL
633 ? stabove->level
634 : (param->level_set
635 ? param->level
636 : STACKING_LEVEL_NORMAL));
638 /* Fit */
639 szplcy=(param->szplcy_set
640 ? param->szplcy
641 : (param->bottom
642 ? SIZEPOLICY_FULL_EXACT
643 : SIZEPOLICY_VISIBILITY_CONSTRAINED));
645 if(!param->whatever){
646 weak=(param->geom_weak_set
647 ? param->geom_weak
648 : (param->geom_set
650 : REGION_RQGEOM_WEAK_ALL));
652 if(param->geom_set)
653 geom_group_to_parent(ws, &param->geom, &g);
654 else
655 g=REGION_GEOM(reg);
657 /* If the requested geometry does not overlap the workspaces's geometry,
658 * position request is never honoured.
660 if((g.x+g.w<=REGION_GEOM(ws).x) ||
661 (g.x>=REGION_GEOM(ws).x+REGION_GEOM(ws).w)){
662 weak|=REGION_RQGEOM_WEAK_X;
665 if((g.y+g.h<=REGION_GEOM(ws).y) ||
666 (g.y>=REGION_GEOM(ws).y+REGION_GEOM(ws).h)){
667 weak|=REGION_RQGEOM_WEAK_Y;
670 if(weak&(REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y) &&
671 (szplcy==SIZEPOLICY_UNCONSTRAINED ||
672 szplcy==SIZEPOLICY_VISIBILITY_CONSTRAINED ||
673 szplcy==SIZEPOLICY_FREE ||
674 szplcy==SIZEPOLICY_FREE_GLUE /* without flags */)){
675 /* TODO: use 'weak'? */
676 group_calc_placement(ws, level, &g);
679 fp.g=REGION_GEOM(ws);
680 fp.mode=REGION_FIT_EXACT;
682 sizepolicy(&szplcy, reg, &g, weak, &fp);
684 if(rectangle_compare(&fp.g, &REGION_GEOM(reg))!=RECTANGLE_SAME)
685 region_fitrep(reg, NULL, &fp);
688 /* Add */
689 st=group_do_add_managed(ws, reg, level, szplcy);
691 if(st==NULL)
692 return FALSE;
694 if(stabove!=NULL)
695 st->above=stabove;
697 if(param->bottom)
698 group_do_set_bottom(ws, st);
700 /* Focus */
701 sw=((param->switchto_set ? param->switchto : ioncore_g.switchto_new)
702 ? st==find_to_focus(ws, st, FALSE)
703 : group_must_focus(ws, st));
705 if(sw){
706 if(region_may_control_focus((WRegion*)ws))
707 region_set_focus(st->reg);
708 else
709 ws->current_managed=st;
710 }else if(region_is_fully_mapped(reg)){
711 region_pointer_focus_hack(reg);
714 return TRUE;
718 static void group_attach_fp(WGroup *ws, const WGroupAttachParams *param,
719 WFitParams *fp)
721 if(param->geom_set){
722 geom_group_to_parent(ws, &param->geom, &fp->g);
723 fp->mode=REGION_FIT_EXACT;
724 }else{
725 fp->g=REGION_GEOM(ws);
726 fp->mode=REGION_FIT_BOUNDS|REGION_FIT_WHATEVER;
731 WRegion *group_do_attach(WGroup *ws,
732 /*const*/ WGroupAttachParams *param,
733 WRegionAttachData *data)
735 WFitParams fp;
737 if(ws->bottom!=NULL && param->bottom){
738 warn(TR("'bottom' already set."));
739 return NULL;
742 group_attach_fp(ws, param, &fp);
744 return region_attach_helper((WRegion*) ws, REGION_PARENT(ws), &fp,
745 (WRegionDoAttachFn*)group_do_attach_final,
746 /*(const WRegionAttachParams*)*/param, data);
747 /* ^^^^ doesn't seem to work. */
751 void groupattachparams_get(WGroupAttachParams *par, ExtlTab tab, const char *sub)
753 int tmp;
754 bool tmpb;
755 char *tmps;
756 ExtlTab g;
758 par->switchto_set=0;
759 par->level_set=0;
760 par->szplcy_set=0;
761 par->geom_set=0;
762 par->bottom=0;
764 if(sub){
765 ExtlTab s;
766 if(extl_table_gets_t(tab, sub, &s)){
767 groupattachparams_get(par, s, NULL);
768 extl_unref_table(s);
770 return;
773 if(extl_table_is_bool_set(tab, "bottom")){
774 par->level=STACKING_LEVEL_BOTTOM;
775 par->level_set=1;
776 par->bottom=1;
779 if(extl_table_gets_i(tab, "level", &tmp)){
780 if(tmp>=0){
781 par->level_set=1;
782 par->level=tmp;
786 if(!par->level_set && extl_table_is_bool_set(tab, "modal")){
787 par->level=STACKING_LEVEL_MODAL1;
788 par->level_set=1;
791 if(extl_table_gets_b(tab, "switchto", &tmpb)){
792 par->switchto=(tmpb!=0);
793 par->switchto_set=1;
796 if(extl_table_gets_i(tab, "sizepolicy", &tmp)){
797 par->szplcy_set=1;
798 par->szplcy=tmp;
799 }else if(extl_table_gets_s(tab, "sizepolicy", &tmps)){
800 if(string2sizepolicy(tmps, &par->szplcy))
801 par->szplcy_set=1;
802 free(tmps);
805 if(extl_table_gets_t(tab, "geom", &g)){
806 int n=0;
808 if(extl_table_gets_i(g, "x", &(par->geom.x)))
809 n++;
810 if(extl_table_gets_i(g, "y", &(par->geom.y)))
811 n++;
812 if(extl_table_gets_i(g, "w", &(par->geom.w)))
813 n++;
814 if(extl_table_gets_i(g, "h", &(par->geom.h)))
815 n++;
817 if(n==4)
818 par->geom_set=1;
820 extl_unref_table(g);
823 if(extl_table_gets_b(tab, "auto_placement", &tmpb)){
824 par->geom_weak_set=1;
825 par->geom_weak=(tmpb ? REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y : 0);
831 /*EXTL_DOC
832 * Attach and reparent existing region \var{reg} to \var{ws}.
833 * The table \var{param} may contain the fields \var{index} and
834 * \var{switchto} that are interpreted as for \fnref{WMPlex.attach_new}.
836 EXTL_EXPORT_MEMBER
837 WRegion *group_attach(WGroup *ws, WRegion *reg, ExtlTab param)
839 WGroupAttachParams par=GROUPATTACHPARAMS_INIT;
840 WRegionAttachData data;
842 if(reg==NULL)
843 return NULL;
845 groupattachparams_get(&par, param, NULL);
847 data.type=REGION_ATTACH_REPARENT;
848 data.u.reg=reg;
850 return group_do_attach(ws, &par, &data);
854 /*EXTL_DOC
855 * Create a new region to be managed by \var{ws}. At least the following
856 * fields in \var{param} are understood:
858 * \begin{tabularx}{\linewidth}{lX}
859 * \tabhead{Field & Description}
860 * \var{type} & (string) Class of the object to be created. Mandatory. \\
861 * \var{name} & (string) Name of the object to be created. \\
862 * \var{switchto} & (boolean) Should the region be switched to? \\
863 * \var{level} & (integer) Stacking level; default is 1. \\
864 * \var{modal} & (boolean) Make object modal; ignored if level is set. \\
865 * \var{sizepolicy} & (string) Size policy; see Section \ref{sec:sizepolicies}. \\
866 * \var{bottom} & (boolean) Mark the attached region as the
867 * ``bottom'' of \var{ws}. \\
868 * \end{tabularx}
870 * In addition parameters to the region to be created are passed in this
871 * same table.
873 EXTL_EXPORT_MEMBER
874 WRegion *group_attach_new(WGroup *ws, ExtlTab param)
876 WGroupAttachParams par=GROUPATTACHPARAMS_INIT;
877 WRegionAttachData data;
879 groupattachparams_get(&par, param, NULL);
881 data.type=REGION_ATTACH_LOAD;
882 data.u.tab=param;
884 return group_do_attach(ws, &par, &data);
888 /*}}}*/
891 /*{{{ Status display support */
894 static int stdisp_szplcy(const WMPlexSTDispInfo *di, WRegion *stdisp)
896 int pos=di->pos;
897 int policy=0, gravity=0;
899 if(di->fullsize){
900 if(region_orientation(stdisp)==REGION_ORIENTATION_VERTICAL){
901 if(pos==MPLEX_STDISP_TL || pos==MPLEX_STDISP_BL)
902 policy=SIZEPOLICY_STRETCH_LEFT;
903 else
904 policy=SIZEPOLICY_STRETCH_RIGHT;
905 }else{
906 if(pos==MPLEX_STDISP_TL || pos==MPLEX_STDISP_TR)
907 policy=SIZEPOLICY_STRETCH_TOP;
908 else
909 policy=SIZEPOLICY_STRETCH_BOTTOM;
911 }else{
912 policy=SIZEPOLICY_GRAVITY;
915 if(pos==MPLEX_STDISP_TL)
916 gravity=SIZEPOLICY_VERT_TOP|SIZEPOLICY_HORIZ_LEFT;
917 else if(pos==MPLEX_STDISP_BL)
918 gravity=SIZEPOLICY_VERT_BOTTOM|SIZEPOLICY_HORIZ_LEFT;
919 else if(pos==MPLEX_STDISP_TR)
920 gravity=SIZEPOLICY_VERT_TOP|SIZEPOLICY_HORIZ_RIGHT;
921 else /*if(pos=MPLEX_STDISP_BR)*/
922 gravity=SIZEPOLICY_VERT_BOTTOM|SIZEPOLICY_HORIZ_RIGHT;
924 return (policy|gravity);
928 void group_manage_stdisp(WGroup *ws, WRegion *stdisp,
929 const WMPlexSTDispInfo *di)
931 WFitParams fp;
932 uint szplcy;
933 WRegion *b=(ws->bottom==NULL ? NULL : ws->bottom->reg);
935 /* Check if 'bottom' wants to manage the stdisp. */
936 if(b!=NULL
937 && !OBJ_IS_BEING_DESTROYED(b)
938 && HAS_DYN(b, region_manage_stdisp)){
939 region_manage_stdisp(b, stdisp, di);
940 if(REGION_MANAGER(stdisp)==b)
941 return;
944 /* No. */
946 szplcy=stdisp_szplcy(di, stdisp)|SIZEPOLICY_SHRUNK;
948 if(ws->managed_stdisp!=NULL && ws->managed_stdisp->reg==stdisp){
949 if(ws->managed_stdisp->szplcy==szplcy)
950 return;
951 ws->managed_stdisp->szplcy=szplcy;
952 }else{
953 region_detach_manager(stdisp);
954 ws->managed_stdisp=group_do_add_managed(ws, stdisp,
955 STACKING_LEVEL_ON_TOP,
956 szplcy);
959 stdisp->flags|=REGION_SKIP_FOCUS;
961 fp.g=REGION_GEOM(ws);
962 fp.mode=0;
964 sizepolicy(&ws->managed_stdisp->szplcy, stdisp, NULL, 0, &fp);
966 region_fitrep(stdisp, NULL, &fp);
970 static void group_remanage_stdisp(WGroup *ws)
972 WMPlex *mplex=OBJ_CAST(REGION_MANAGER(ws), WMPlex);
974 if(mplex!=NULL &&
975 mplex->mx_current!=NULL &&
976 mplex->mx_current->st->reg==(WRegion*)ws){
977 mplex_remanage_stdisp(mplex);
982 /*}}}*/
985 /*{{{ Geometry requests */
988 void group_managed_rqgeom(WGroup *ws, WRegion *reg,
989 const WRQGeomParams *rq,
990 WRectangle *geomret)
992 WFitParams fp;
993 WStacking *st;
995 st=group_find_stacking(ws, reg);
997 if(st==NULL){
998 fp.g=rq->geom;
999 fp.mode=REGION_FIT_EXACT;
1000 }else{
1001 fp.g=REGION_GEOM(ws);
1002 fp.mode=0;
1003 sizepolicy(&st->szplcy, reg, &rq->geom, rq->flags, &fp);
1006 if(geomret!=NULL)
1007 *geomret=fp.g;
1009 if(!(rq->flags&REGION_RQGEOM_TRYONLY))
1010 region_fitrep(reg, NULL, &fp);
1014 void group_managed_rqgeom_absolute(WGroup *grp, WRegion *sub,
1015 const WRQGeomParams *rq,
1016 WRectangle *geomret)
1018 if(grp->bottom!=NULL && grp->bottom->reg==sub){
1019 region_rqgeom((WRegion*)grp, rq, geomret);
1020 if(!(rq->flags&REGION_RQGEOM_TRYONLY) && geomret!=NULL)
1021 *geomret=REGION_GEOM(sub);
1022 }else{
1023 WRQGeomParams rq2=*rq;
1024 rq2.flags&=~REGION_RQGEOM_ABSOLUTE;
1026 region_managed_rqgeom((WRegion*)grp, sub, &rq2, geomret);
1031 /*}}}*/
1034 /*{{{ Navigation */
1037 static WStacking *nxt(WGroup *ws, WStacking *st, bool wrap)
1039 return (st->mgr_next!=NULL
1040 ? st->mgr_next
1041 : (wrap ? ws->managed_list : NULL));
1045 static WStacking *prv(WGroup *ws, WStacking *st, bool wrap)
1047 return (st!=ws->managed_list
1048 ? st->mgr_prev
1049 : (wrap ? st->mgr_prev : NULL));
1053 typedef WStacking *NxtFn(WGroup *ws, WStacking *st, bool wrap);
1056 static bool focusable(WGroup *ws, WStacking *st, uint min_level)
1058 return (st->reg!=NULL
1059 && REGION_IS_MAPPED(st->reg)
1060 && !(st->reg->flags&REGION_SKIP_FOCUS)
1061 && st->level>=min_level);
1065 static WStacking *do_get_next(WGroup *ws, WStacking *sti,
1066 NxtFn *fn, bool wrap, bool sti_ok)
1068 WStacking *st, *stacking;
1069 uint min_level=0;
1071 stacking=group_get_stacking(ws);
1073 if(stacking!=NULL)
1074 min_level=stacking_min_level_mapped(stacking);
1076 st=sti;
1077 while(1){
1078 st=fn(ws, st, wrap);
1080 if(st==NULL || st==sti)
1081 break;
1083 if(focusable(ws, st, min_level))
1084 return st;
1087 if(sti_ok && focusable(ws, sti, min_level))
1088 return sti;
1090 return NULL;
1094 static WStacking *group_do_navi_first(WGroup *ws, WRegionNavi nh)
1096 WStacking *lst=ws->managed_list;
1098 if(lst==NULL)
1099 return NULL;
1101 if(nh==REGION_NAVI_ANY &&
1102 ws->current_managed!=NULL &&
1103 ws->current_managed->reg!=NULL){
1104 return ws->current_managed;
1107 if(nh==REGION_NAVI_ANY || nh==REGION_NAVI_END ||
1108 nh==REGION_NAVI_BOTTOM || nh==REGION_NAVI_RIGHT){
1109 return do_get_next(ws, lst, prv, TRUE, TRUE);
1110 }else{
1111 return do_get_next(ws, lst->mgr_prev, nxt, TRUE, TRUE);
1116 static WRegion *group_navi_first(WGroup *ws, WRegionNavi nh,
1117 WRegionNaviData *data)
1119 WStacking *st=group_do_navi_first(ws, nh);
1121 return region_navi_cont(&ws->reg, (st!=NULL ? st->reg : NULL), data);
1125 static WStacking *group_do_navi_next(WGroup *ws, WStacking *st,
1126 WRegionNavi nh, bool wrap)
1128 if(st==NULL)
1129 return group_do_navi_first(ws, nh);
1131 if(nh==REGION_NAVI_ANY || nh==REGION_NAVI_END ||
1132 nh==REGION_NAVI_BOTTOM || nh==REGION_NAVI_RIGHT){
1133 return do_get_next(ws, st, nxt, wrap, FALSE);
1134 }else{
1135 return do_get_next(ws, st, prv, wrap, FALSE);
1139 static WRegion *group_navi_next(WGroup *ws, WRegion *reg,
1140 WRegionNavi nh, WRegionNaviData *data)
1142 WStacking *st=group_find_stacking(ws, reg);
1144 st=group_do_navi_next(ws, st, nh, FALSE);
1146 return region_navi_cont(&ws->reg, (st!=NULL ? st->reg : NULL), data);
1150 /*}}}*/
1153 /*{{{ Stacking */
1157 * Note: Managed objects are considered to be stacked separately from the
1158 * group, slightly violating expectations.
1161 void group_stacking(WGroup *ws, Window *bottomret, Window *topret)
1163 Window win=region_xwindow((WRegion*)ws);
1165 *bottomret=win;
1166 *topret=win;
1170 void group_restack(WGroup *ws, Window other, int mode)
1172 Window win;
1174 win=region_xwindow((WRegion*)ws);
1175 if(win!=None){
1176 xwindow_restack(win, other, mode);
1177 other=win;
1178 mode=Above;
1183 WStacking *group_find_stacking(WGroup *ws, WRegion *r)
1185 if(r==NULL || REGION_MANAGER(r)!=(WRegion*)ws)
1186 return NULL;
1188 return ioncore_find_stacking(r);
1192 static WStacking *find_stacking_if_not_on_ws(WGroup *ws, Window w)
1194 WRegion *r=xwindow_region_of(w);
1195 WStacking *st=NULL;
1197 while(r!=NULL){
1198 if(REGION_MANAGER(r)==(WRegion*)ws)
1199 break;
1200 st=group_find_stacking(ws, r);
1201 if(st!=NULL)
1202 break;
1203 r=REGION_MANAGER(r);
1206 return st;
1210 bool group_managed_rqorder(WGroup *grp, WRegion *reg, WRegionOrder order)
1212 WStacking **stackingp=group_get_stackingp(grp);
1213 WStacking *st;
1215 if(stackingp==NULL || *stackingp==NULL)
1216 return FALSE;
1218 st=group_find_stacking(grp, reg);
1220 if(st==NULL)
1221 return FALSE;
1223 stacking_restack(stackingp, st, None, NULL, NULL,
1224 (order!=REGION_ORDER_FRONT));
1226 return TRUE;
1230 /*}}}*/
1233 /*{{{ Misc. */
1236 /*EXTL_DOC
1237 * Iterate over managed regions of \var{ws} until \var{iterfn} returns
1238 * \code{false}.
1239 * The function is called in protected mode.
1240 * This routine returns \code{true} if it reaches the end of list
1241 * without this happening.
1243 EXTL_SAFE
1244 EXTL_EXPORT_MEMBER
1245 bool group_managed_i(WGroup *ws, ExtlFn iterfn)
1247 WGroupIterTmp tmp;
1248 group_iter_init(&tmp, ws);
1250 return extl_iter_objlist_(iterfn, (ObjIterator*)group_iter, &tmp);
1254 WRegion* group_current(WGroup *ws)
1256 return (ws->current_managed!=NULL ? ws->current_managed->reg : NULL);
1260 void group_size_hints(WGroup *ws, WSizeHints *hints_ret)
1262 if(ws->bottom==NULL || ws->bottom->reg==NULL){
1263 sizehints_clear(hints_ret);
1264 }else{
1265 region_size_hints(ws->bottom->reg, hints_ret);
1266 hints_ret->no_constrain=TRUE;
1271 Window group_xwindow(const WGroup *ws)
1273 return ws->dummywin;
1277 /*EXTL_DOC
1278 * Returns the group of \var{reg}, if it is managed by one,
1279 * and \var{reg} itself otherwise.
1281 /*EXTL_EXPORT_MEMBER
1282 WRegion *region_group_of(WRegion *reg)
1284 WRegion *mgr=REGION_MANAGER(reg);
1286 return (OBJ_IS(mgr, WGroup) ? mgr : reg);
1290 /*EXTL_DOC
1291 * Returns the group of \var{reg}, if \var{reg} is its bottom,
1292 * and \var{reg} itself otherwise.
1294 EXTL_EXPORT_MEMBER
1295 WRegion *region_groupleader_of(WRegion *reg)
1297 WGroup *grp=REGION_MANAGER_CHK(reg, WGroup);
1299 return ((grp!=NULL && group_bottom(grp)==reg)
1300 ? (WRegion*)grp
1301 : reg);
1305 /*}}}*/
1308 /*{{{ Save/load */
1311 ExtlTab group_get_configuration(WGroup *ws)
1313 ExtlTab tab, mgds, subtab, g;
1314 WStacking *st;
1315 WGroupIterTmp tmp;
1316 int n=0;
1317 WRectangle tmpg;
1319 tab=region_get_base_configuration((WRegion*)ws);
1321 mgds=extl_create_table();
1323 extl_table_sets_t(tab, "managed", mgds);
1325 /* TODO: stacking order messed up */
1327 FOR_ALL_NODES_IN_GROUP(ws, st, tmp){
1328 if(st->reg==NULL)
1329 continue;
1331 subtab=region_get_configuration(st->reg);
1333 if(subtab!=extl_table_none()){
1334 extl_table_sets_s(subtab, "sizepolicy",
1335 sizepolicy2string(st->szplcy));
1336 extl_table_sets_i(subtab, "level", st->level);
1338 tmpg=REGION_GEOM(st->reg);
1339 tmpg.x-=REGION_GEOM(ws).x;
1340 tmpg.y-=REGION_GEOM(ws).y;
1342 g=extl_table_from_rectangle(&tmpg);
1343 extl_table_sets_t(subtab, "geom", g);
1344 extl_unref_table(g);
1346 if(ws->bottom==st)
1347 extl_table_sets_b(subtab, "bottom", TRUE);
1349 extl_table_seti_t(mgds, ++n, subtab);
1350 extl_unref_table(subtab);
1354 extl_unref_table(mgds);
1356 return tab;
1360 void group_do_load(WGroup *ws, ExtlTab tab)
1362 ExtlTab substab, subtab;
1363 int i, n;
1365 if(extl_table_gets_t(tab, "managed", &substab)){
1366 n=extl_table_get_n(substab);
1367 for(i=1; i<=n; i++){
1368 if(extl_table_geti_t(substab, i, &subtab)){
1369 WGroupAttachParams par=GROUPATTACHPARAMS_INIT;
1370 WFitParams fp;
1371 WPHolder *ph;
1373 groupattachparams_get(&par, subtab, NULL);
1374 group_attach_fp(ws, &par, &fp);
1376 ph=(WPHolder*)create_grouppholder(ws, NULL, &par);
1378 region_attach_load_helper((WRegion*)ws, REGION_PARENT(ws), &fp,
1379 (WRegionDoAttachFn*)group_do_attach_final,
1380 (void*)&par, subtab, &ph);
1382 if(ph!=NULL)
1383 destroy_obj((Obj*)ph);
1385 extl_unref_table(subtab);
1389 extl_unref_table(substab);
1394 WRegion *group_loaj(WWindow *par, const WFitParams *fp, ExtlTab tab)
1396 WGroup *ws;
1398 /* Generic initial name - to be overwritten later. */
1399 ws=create_group(par, fp, "Notion GroupCW or GroupWS");
1401 if(ws==NULL)
1402 return NULL;
1404 group_do_load(ws, tab);
1406 return (WRegion*)ws;
1410 /*}}}*/
1413 /*{{{ Dynamic function table and class implementation */
1416 static DynFunTab group_dynfuntab[]={
1417 {(DynFun*)region_fitrep,
1418 (DynFun*)group_fitrep},
1420 {region_map,
1421 group_map},
1423 {region_unmap,
1424 group_unmap},
1426 {(DynFun*)region_managed_prepare_focus,
1427 (DynFun*)group_managed_prepare_focus},
1429 {region_do_set_focus,
1430 group_do_set_focus},
1432 {region_managed_notify,
1433 group_managed_notify},
1435 {region_managed_remove,
1436 group_managed_remove},
1438 {(DynFun*)region_get_configuration,
1439 (DynFun*)group_get_configuration},
1441 {(DynFun*)region_current,
1442 (DynFun*)group_current},
1444 {(DynFun*)region_rescue_clientwins,
1445 (DynFun*)group_rescue_clientwins},
1447 {region_restack,
1448 group_restack},
1450 {region_stacking,
1451 group_stacking},
1453 {(DynFun*)region_managed_get_pholder,
1454 (DynFun*)group_managed_get_pholder},
1456 {region_managed_rqgeom,
1457 group_managed_rqgeom},
1459 {region_managed_rqgeom_absolute,
1460 group_managed_rqgeom_absolute},
1462 {(DynFun*)group_do_add_managed,
1463 (DynFun*)group_do_add_managed_default},
1465 {region_size_hints,
1466 group_size_hints},
1468 {(DynFun*)region_xwindow,
1469 (DynFun*)group_xwindow},
1471 {(DynFun*)region_navi_first,
1472 (DynFun*)group_navi_first},
1474 {(DynFun*)region_navi_next,
1475 (DynFun*)group_navi_next},
1477 {(DynFun*)region_managed_rqorder,
1478 (DynFun*)group_managed_rqorder},
1480 {(DynFun*)region_get_rescue_pholder_for,
1481 (DynFun*)group_get_rescue_pholder_for},
1483 END_DYNFUNTAB
1487 EXTL_EXPORT
1488 IMPLCLASS(WGroup, WRegion, group_deinit, group_dynfuntab);
1491 /*}}}*/