4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
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>
34 #include "sizepolicy.h"
41 #include "grouppholder.h"
43 #include "float-placement.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
);
64 : window_get_stacking(par
));
68 WStacking
**group_get_stackingp(WGroup
*ws
)
70 WWindow
*par
=REGION_PARENT(ws
);
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
;
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
)
132 WStacking
*unweaved
=NULL
;
133 int xdiff
=0, ydiff
=0;
138 oldpar
=REGION_PARENT(ws
);
141 if(fp
->mode
®ION_FIT_WHATEVER
)
143 REGION_GEOM(ws
)=fp
->g
;
145 if(!region_same_rootwin((WRegion
*)ws
, (WRegion
*)par
))
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
®ION_FIT_WHATEVER
)){
170 REGION_GEOM(ws
).w
=fp
->g
.w
;
171 REGION_GEOM(ws
).h
=fp
->g
.h
;
175 unweaved
=stacking_unweave(&oldpar
->stacking
, wsfilt
, (void*)ws
);
178 FOR_ALL_NODES_IN_GROUP(ws
, st
, tmp
){
184 g
=REGION_GEOM(st
->reg
);
188 if(fp
->mode
®ION_FIT_WHATEVER
){
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
);
202 stacking_weave(&par
->stacking
, &unweaved
, FALSE
);
208 static void group_map(WGroup
*ws
)
213 REGION_MARK_MAPPED(ws
);
214 XMapWindow(ioncore_g
.dpy
, ws
->dummywin
);
216 FOR_ALL_MANAGED_BY_GROUP(ws
, reg
, tmp
){
222 static void group_unmap(WGroup
*ws
)
227 REGION_MARK_UNMAPPED(ws
);
228 XUnmapWindow(ioncore_g
.dpy
, ws
->dummywin
);
230 FOR_ALL_MANAGED_BY_GROUP(ws
, reg
, tmp
){
236 static WStacking
*find_to_focus(WGroup
*ws
, WStacking
*st
, bool group_only
)
238 WStacking
*stacking
=group_get_stacking(ws
);
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
);
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
);
269 WStacking
*node
=mplex_find_stacking(mplex
, (WRegion
*)ws
);
274 return mplex_do_prepare_focus(mplex
, node
, st
,
277 if(!region_prepare_focus((WRegion
*)ws
, flags
, res
))
280 st
=find_to_focus(ws
, st
, FALSE
);
285 if(ioncore_g
.autoraise
&&
286 !(flags
®ION_GOTO_ENTERWINDOW
) &&
287 st
->level
>STACKING_LEVEL_BOTTOM
){
288 WStacking
**stackingp
=group_get_stackingp(ws
);
289 stacking_restack(stackingp
, st
, None
, NULL
, NULL
, FALSE
);
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
);
312 group_do_set_bottom(ws
, NULL
);
315 if(st
==ws
->managed_stdisp
){
316 ws
->managed_stdisp
=NULL
;
320 if(st
==ws
->current_managed
){
321 ws
->current_managed
=NULL
;
325 next_st
=stacking_unstack(REGION_PARENT(ws
), st
);
326 UNLINK_ITEM(ws
->managed_list
, st
, mgr_next
, mgr_prev
);
327 stacking_unassoc(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
);
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
);
361 /*{{{ Create/destroy */
364 bool group_init(WGroup
*ws
, WWindow
*par
, const WFitParams
*fp
, const char *name
)
368 ws
->current_managed
=NULL
;
369 ws
->managed_stdisp
=NULL
;
371 ws
->managed_list
=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
)
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
,
393 ((WRegion
*)ws
)->flags
|=REGION_GRAB_ON_PARENT
;
395 region_add_bindmap((WRegion
*)ws
, ioncore_group_bindmap
);
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
)
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
);
428 grouppholder_do_unlink(ws
->phs
);
430 region_deinit(&ws
->reg
);
434 bool group_rescue_clientwins(WGroup
*ws
, WRescueInfo
*info
)
438 group_iter_init_nostdisp(&tmp
, ws
);
440 return region_rescue_some_clientwins((WRegion
*)ws
, info
,
441 (WRegionIterator
*)group_iter
,
446 WPHolder
*group_get_rescue_pholder_for(WGroup
*ws
,
449 WGroupAttachParams ap
=GROUPATTACHPARAMS_INIT
;
450 WFramedParam fp
=FRAMEDPARAM_INIT
;
454 ap
.geom
=REGION_GEOM(forwhat
);
458 if(REGION_PARENT(forwhat
)==REGION_PARENT(ws
)){
459 ap
.geom
.x
-=REGION_GEOM(ws
).x
;
460 ap
.geom
.y
-=REGION_GEOM(ws
).y
;
462 ap
.geom_weak
=REGION_RQGEOM_WEAK_X
|REGION_RQGEOM_WEAK_Y
;
467 WFrame *frame=OBJ_CAST(forwhat, WFrame);
472 ph
=(WPHolder
*)create_grouppholder(ws
, NULL
, &ap
);
474 return pholder_either((WPHolder
*)create_framedpholder(ph
, &fp
), ph
);
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
;
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
));
505 (st
==NULL
|| HAS_DYN(st
->reg
, region_manage_stdisp
))){
506 group_remanage_stdisp(grp
);
509 group_bottom_set(grp
);
515 * Sets the `bottom' of \var{ws}. The region \var{reg} must already
516 * be managed by \var{ws}, unless \code{nil}.
519 bool group_set_bottom(WGroup
*ws
, WRegion
*reg
)
524 st
=group_find_stacking(ws
, reg
);
530 group_do_set_bottom(ws
, st
);
537 * Returns the `bottom' of \var{ws}.
541 WRegion
*group_bottom(WGroup
*ws
)
543 return (ws
->bottom
!=NULL
? ws
->bottom
->reg
: NULL
);
553 WStacking
*group_do_add_managed(WGroup
*ws
, WRegion
*reg
, int level
,
557 CALL_DYN_RET(st
, WStacking
*, group_do_add_managed
, ws
,
558 (ws
, reg
, level
, szplcy
));
563 WStacking
*group_do_add_managed_default(WGroup
*ws
, WRegion
*reg
, int level
,
566 WStacking
*st
=NULL
, *tmp
=NULL
;
567 Window bottom
=None
, top
=None
;
568 WStacking
**stackingp
=group_get_stackingp(ws
);
574 st
=create_stacking();
579 if(!stacking_assoc(st
, reg
)){
584 frame
=OBJ_CAST(reg
, WFrame
);
586 if(framemode_unalt(frame_mode(frame
))==FRAME_MODE_TILED
)
587 frame_set_mode(frame
, FRAME_MODE_FLOATING
);
593 LINK_ITEM_FIRST(tmp
, st
, next
, prev
);
594 stacking_weave(stackingp
, &tmp
, FALSE
);
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
))
607 static void geom_group_to_parent(WGroup
*ws
, const WRectangle
*g
,
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
,
627 const WGroupAttachParams
*param
)
629 WStacking
*st
, *stabove
=NULL
;
638 if(param
->stack_above
!=NULL
)
639 stabove
=group_find_stacking(ws
, param
->stack_above
);
645 : STACKING_LEVEL_NORMAL
));
648 szplcy
=(param
->szplcy_set
651 ? SIZEPOLICY_FULL_EXACT
652 : SIZEPOLICY_VISIBILITY_CONSTRAINED
));
654 if(!param
->whatever
){
655 weak
=(param
->geom_weak_set
659 : REGION_RQGEOM_WEAK_ALL
));
662 geom_group_to_parent(ws
, ¶m
->geom
, &g
);
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
, ®ION_GEOM(reg
))!=RECTANGLE_SAME
)
694 region_fitrep(reg
, NULL
, &fp
);
698 st
=group_do_add_managed(ws
, reg
, level
, szplcy
);
707 group_do_set_bottom(ws
, st
);
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
));
715 if(region_may_control_focus((WRegion
*)ws
))
716 region_set_focus(st
->reg
);
718 ws
->current_managed
=st
;
719 }else if(region_is_fully_mapped(reg
)){
720 region_pointer_focus_hack(reg
);
727 static void group_attach_fp(WGroup
*ws
, const WGroupAttachParams
*param
,
731 geom_group_to_parent(ws
, ¶m
->geom
, &fp
->g
);
732 fp
->mode
=REGION_FIT_EXACT
;
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
)
747 if(ws
->bottom
!=NULL
&& param
->bottom
){
748 warn(TR("'bottom' already set."));
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
)
776 if(extl_table_gets_t(tab
, sub
, &s
)){
777 groupattachparams_get(par
, s
, NULL
);
783 if(extl_table_is_bool_set(tab
, "bottom")){
784 par
->level
=STACKING_LEVEL_BOTTOM
;
789 if(extl_table_gets_i(tab
, "level", &tmp
)){
796 if(!par
->level_set
&& extl_table_is_bool_set(tab
, "modal")){
797 par
->level
=STACKING_LEVEL_MODAL1
;
801 if(extl_table_gets_b(tab
, "switchto", &tmpb
)){
802 par
->switchto
=(tmpb
!=0);
806 if(extl_table_gets_i(tab
, "sizepolicy", &tmp
)){
809 }else if(extl_table_gets_s(tab
, "sizepolicy", &tmps
)){
810 if(string2sizepolicy(tmps
, &par
->szplcy
))
815 if(extl_table_gets_t(tab
, "geom", &g
)){
818 if(extl_table_gets_i(g
, "x", &(par
->geom
.x
)))
820 if(extl_table_gets_i(g
, "y", &(par
->geom
.y
)))
822 if(extl_table_gets_i(g
, "w", &(par
->geom
.w
)))
824 if(extl_table_gets_i(g
, "h", &(par
->geom
.h
)))
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);
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}.
847 WRegion
*group_attach(WGroup
*ws
, WRegion
*reg
, ExtlTab param
)
849 WGroupAttachParams par
=GROUPATTACHPARAMS_INIT
;
850 WRegionAttachData data
;
855 groupattachparams_get(&par
, param
, NULL
);
857 data
.type
=REGION_ATTACH_REPARENT
;
860 return group_do_attach(ws
, &par
, &data
);
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}. \\
880 * In addition parameters to the region to be created are passed in this
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
;
894 return group_do_attach(ws
, &par
, &data
);
901 /*{{{ Status display support */
904 static int stdisp_szplcy(const WMPlexSTDispInfo
*di
, WRegion
*stdisp
)
907 int policy
=0, gravity
=0;
910 if(region_orientation(stdisp
)==REGION_ORIENTATION_VERTICAL
){
911 if(pos
==MPLEX_STDISP_TL
|| pos
==MPLEX_STDISP_BL
)
912 policy
=SIZEPOLICY_STRETCH_LEFT
;
914 policy
=SIZEPOLICY_STRETCH_RIGHT
;
916 if(pos
==MPLEX_STDISP_TL
|| pos
==MPLEX_STDISP_TR
)
917 policy
=SIZEPOLICY_STRETCH_TOP
;
919 policy
=SIZEPOLICY_STRETCH_BOTTOM
;
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
)
943 WRegion
*b
=(ws
->bottom
==NULL
? NULL
: ws
->bottom
->reg
);
945 /* Check if 'bottom' wants to manage the stdisp. */
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
)
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
)
961 ws
->managed_stdisp
->szplcy
=szplcy
;
963 region_detach_manager(stdisp
);
964 ws
->managed_stdisp
=group_do_add_managed(ws
, stdisp
,
965 STACKING_LEVEL_ON_TOP
,
969 stdisp
->flags
|=REGION_SKIP_FOCUS
;
971 fp
.g
=REGION_GEOM(ws
);
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
);
985 mplex
->mx_current
!=NULL
&&
986 mplex
->mx_current
->st
->reg
==(WRegion
*)ws
){
987 mplex_remanage_stdisp(mplex
);
995 /*{{{ Geometry requests */
998 void group_managed_rqgeom(WGroup
*ws
, WRegion
*reg
,
999 const WRQGeomParams
*rq
,
1000 WRectangle
*geomret
)
1005 st
=group_find_stacking(ws
, reg
);
1009 fp
.mode
=REGION_FIT_EXACT
;
1011 fp
.g
=REGION_GEOM(ws
);
1013 sizepolicy(&st
->szplcy
, reg
, &rq
->geom
, rq
->flags
, &fp
);
1019 if(!(rq
->flags
®ION_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
®ION_RQGEOM_TRYONLY
) && geomret
!=NULL
)
1031 *geomret
=REGION_GEOM(sub
);
1033 WRQGeomParams rq2
=*rq
;
1034 rq2
.flags
&=~REGION_RQGEOM_ABSOLUTE
;
1036 region_managed_rqgeom((WRegion
*)grp
, sub
, &rq2
, geomret
);
1047 static WStacking
*nxt(WGroup
*ws
, WStacking
*st
, bool wrap
)
1049 return (st
->mgr_next
!=NULL
1051 : (wrap
? ws
->managed_list
: NULL
));
1055 static WStacking
*prv(WGroup
*ws
, WStacking
*st
, bool wrap
)
1057 return (st
!=ws
->managed_list
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
®ION_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
;
1081 stacking
=group_get_stacking(ws
);
1084 min_level
=stacking_min_level_mapped(stacking
);
1088 st
=fn(ws
, st
, wrap
);
1090 if(st
==NULL
|| st
==sti
)
1093 if(focusable(ws
, st
, min_level
))
1097 if(sti_ok
&& focusable(ws
, sti
, min_level
))
1104 static WStacking
*group_do_navi_first(WGroup
*ws
, WRegionNavi nh
)
1106 WStacking
*lst
=ws
->managed_list
;
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
);
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
)
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
);
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
);
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
);
1180 void group_restack(WGroup
*ws
, Window other
, int mode
)
1184 win
=region_xwindow((WRegion
*)ws
);
1186 xwindow_restack(win
, other
, mode
);
1193 WStacking
*group_find_stacking(WGroup
*ws
, WRegion
*r
)
1195 if(r
==NULL
|| REGION_MANAGER(r
)!=(WRegion
*)ws
)
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
);
1208 if(REGION_MANAGER(r
)==(WRegion
*)ws
)
1210 st
=group_find_stacking(ws
, r
);
1213 r
=REGION_MANAGER(r
);
1220 bool group_managed_rqorder(WGroup
*grp
, WRegion
*reg
, WRegionOrder order
)
1222 WStacking
**stackingp
=group_get_stackingp(grp
);
1225 if(stackingp
==NULL
|| *stackingp
==NULL
)
1228 st
=group_find_stacking(grp
, reg
);
1233 stacking_restack(stackingp
, st
, None
, NULL
, NULL
,
1234 (order
!=REGION_ORDER_FRONT
));
1247 * Iterate over managed regions of \var{ws} until \var{iterfn} returns
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.
1255 bool group_managed_i(WGroup
*ws
, ExtlFn iterfn
)
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
);
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
;
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);
1301 * Returns the group of \var{reg}, if \var{reg} is its bottom,
1302 * and \var{reg} itself otherwise.
1305 WRegion
*region_groupleader_of(WRegion
*reg
)
1307 WGroup
*grp
=REGION_MANAGER_CHK(reg
, WGroup
);
1309 return ((grp
!=NULL
&& group_bottom(grp
)==reg
)
1321 ExtlTab
group_get_configuration(WGroup
*ws
)
1323 ExtlTab tab
, mgds
, subtab
, g
;
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
){
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
);
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
);
1371 void group_do_load(WGroup
*ws
, ExtlTab tab
)
1373 ExtlTab substab
, subtab
;
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
;
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
);
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
)
1410 /* Generic initial name - to be overwritten later. */
1411 ws
=create_group(par
, fp
, "Notion GroupCW or GroupWS");
1416 group_do_load(ws
, tab
);
1418 return (WRegion
*)ws
;
1425 /*{{{ Dynamic function table and class implementation */
1428 static DynFunTab group_dynfuntab
[]={
1429 {(DynFun
*)region_fitrep
,
1430 (DynFun
*)group_fitrep
},
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
},
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
},
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
},
1500 IMPLCLASS(WGroup
, WRegion
, group_deinit
, group_dynfuntab
);