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
);
151 assert(ws
->managed_stdisp
==NULL
);
153 xdiff
=fp
->g
.x
-REGION_GEOM(ws
).x
;
154 ydiff
=fp
->g
.y
-REGION_GEOM(ws
).y
;
156 region_unset_parent((WRegion
*)ws
);
157 XReparentWindow(ioncore_g
.dpy
, ws
->dummywin
, par
->win
, -1, -1);
158 region_set_parent((WRegion
*)ws
, par
);
160 REGION_GEOM(ws
).x
=fp
->g
.x
;
161 REGION_GEOM(ws
).y
=fp
->g
.y
;
162 if(!(fp
->mode
®ION_FIT_WHATEVER
)){
163 REGION_GEOM(ws
).w
=fp
->g
.w
;
164 REGION_GEOM(ws
).h
=fp
->g
.h
;
168 unweaved
=stacking_unweave(&oldpar
->stacking
, wsfilt
, (void*)ws
);
171 FOR_ALL_NODES_IN_GROUP(ws
, st
, tmp
){
177 g
=REGION_GEOM(st
->reg
);
181 if(fp
->mode
®ION_FIT_WHATEVER
){
184 fp2
.g
=REGION_GEOM(ws
);
185 sizepolicy(&st
->szplcy
, st
->reg
, &g
, REGION_RQGEOM_WEAK_ALL
, &fp2
);
188 if(!region_fitrep(st
->reg
, par
, &fp2
)){
189 warn(TR("Error reparenting %s."), region_name(st
->reg
));
190 region_detach_manager(st
->reg
);
195 stacking_weave(&par
->stacking
, &unweaved
, FALSE
);
201 static void group_map(WGroup
*ws
)
206 REGION_MARK_MAPPED(ws
);
207 XMapWindow(ioncore_g
.dpy
, ws
->dummywin
);
209 FOR_ALL_MANAGED_BY_GROUP(ws
, reg
, tmp
){
215 static void group_unmap(WGroup
*ws
)
220 REGION_MARK_UNMAPPED(ws
);
221 XUnmapWindow(ioncore_g
.dpy
, ws
->dummywin
);
223 FOR_ALL_MANAGED_BY_GROUP(ws
, reg
, tmp
){
229 static WStacking
*find_to_focus(WGroup
*ws
, WStacking
*st
, bool group_only
)
231 WStacking
*stacking
=group_get_stacking(ws
);
236 return stacking_find_to_focus_mapped(stacking
, st
,
237 (group_only
? (WRegion
*)ws
: NULL
));
241 static void group_do_set_focus(WGroup
*ws
, bool warp
)
243 WStacking
*st
=find_to_focus(ws
, ws
->current_managed
, FALSE
);
245 if(st
!=NULL
&& st
->reg
!=NULL
)
246 region_do_set_focus(st
->reg
, warp
);
248 region_finalise_focusing((WRegion
*)ws
, ws
->dummywin
, warp
, CurrentTime
);
252 static bool group_managed_prepare_focus(WGroup
*ws
, WRegion
*reg
,
253 int flags
, WPrepareFocusResult
*res
)
255 WMPlex
*mplex
=OBJ_CAST(REGION_MANAGER(ws
), WMPlex
);
256 WStacking
*st
=group_find_stacking(ws
, reg
);
262 WStacking
*node
=mplex_find_stacking(mplex
, (WRegion
*)ws
);
267 return mplex_do_prepare_focus(mplex
, node
, st
,
270 if(!region_prepare_focus((WRegion
*)ws
, flags
, res
))
273 st
=find_to_focus(ws
, st
, FALSE
);
278 if(ioncore_g
.autoraise
&&
279 !(flags
®ION_GOTO_ENTERWINDOW
) &&
280 st
->level
>STACKING_LEVEL_BOTTOM
){
281 WStacking
**stackingp
=group_get_stackingp(ws
);
282 stacking_restack(stackingp
, st
, None
, NULL
, NULL
, FALSE
);
288 return (res
->reg
==reg
);
293 void group_managed_remove(WGroup
*ws
, WRegion
*reg
)
295 bool mcf
=region_may_control_focus((WRegion
*)ws
);
296 WStacking
*st
, *next_st
=NULL
;
297 bool was_stdisp
=FALSE
, was_bottom
=FALSE
;
298 bool was_current
=FALSE
;
300 st
=group_find_stacking(ws
, reg
);
305 group_do_set_bottom(ws
, NULL
);
308 if(st
==ws
->managed_stdisp
){
309 ws
->managed_stdisp
=NULL
;
313 if(st
==ws
->current_managed
){
314 ws
->current_managed
=NULL
;
318 next_st
=stacking_unstack(REGION_PARENT(ws
), st
);
319 UNLINK_ITEM(ws
->managed_list
, st
, mgr_next
, mgr_prev
);
320 stacking_unassoc(st
);
324 region_unset_manager(reg
, (WRegion
*)ws
);
326 if(!OBJ_IS_BEING_DESTROYED(ws
) && was_current
){
327 /* This may still potentially cause problems when focus
328 * change is pending. Perhaps we should use region_await_focus,
329 * if it is pointing to our child (and region_may_control_focus
330 * fail if it is pointing somewhere else).
332 WStacking
*stf
=find_to_focus(ws
, next_st
, TRUE
);
333 if(stf
!=NULL
&& mcf
){
334 region_maybewarp_now(stf
->reg
, FALSE
);
336 ws
->current_managed
=stf
;
342 void group_managed_notify(WGroup
*ws
, WRegion
*reg
, WRegionNotify how
)
344 if(how
==ioncore_g
.notifies
.activated
||
345 how
==ioncore_g
.notifies
.pseudoactivated
){
346 ws
->current_managed
=group_find_stacking(ws
, reg
);
354 /*{{{ Create/destroy */
357 bool group_init(WGroup
*ws
, WWindow
*par
, const WFitParams
*fp
)
361 ws
->current_managed
=NULL
;
362 ws
->managed_stdisp
=NULL
;
364 ws
->managed_list
=NULL
;
367 ws
->dummywin
=XCreateWindow(ioncore_g
.dpy
, par
->win
,
368 fp
->g
.x
, fp
->g
.y
, 1, 1, 0,
369 CopyFromParent
, InputOnly
,
370 CopyFromParent
, 0, NULL
);
371 if(ws
->dummywin
==None
)
375 xwindow_set_text_property(ws
->dummywin
, XA_WM_NAME
, p
, 1);
377 region_init(&ws
->reg
, par
, fp
);
378 region_register(&ws
->reg
);
380 XSelectInput(ioncore_g
.dpy
, ws
->dummywin
,
381 FocusChangeMask
|KeyPressMask
|KeyReleaseMask
|
382 ButtonPressMask
|ButtonReleaseMask
);
383 XSaveContext(ioncore_g
.dpy
, ws
->dummywin
, ioncore_g
.win_context
,
386 ((WRegion
*)ws
)->flags
|=REGION_GRAB_ON_PARENT
;
388 region_add_bindmap((WRegion
*)ws
, ioncore_group_bindmap
);
394 WGroup
*create_group(WWindow
*par
, const WFitParams
*fp
)
396 CREATEOBJ_IMPL(WGroup
, group
, (p
, par
, fp
));
400 void group_deinit(WGroup
*ws
)
405 if(ws
->managed_stdisp
!=NULL
&& ws
->managed_stdisp
->reg
!=NULL
){
406 group_managed_remove(ws
, ws
->managed_stdisp
->reg
);
407 assert(ws
->managed_stdisp
==NULL
);
410 FOR_ALL_MANAGED_BY_GROUP(ws
, reg
, tmp
){
411 destroy_obj((Obj
*)reg
);
414 assert(ws
->managed_list
==NULL
);
416 XDeleteContext(ioncore_g
.dpy
, ws
->dummywin
, ioncore_g
.win_context
);
417 XDestroyWindow(ioncore_g
.dpy
, ws
->dummywin
);
421 grouppholder_do_unlink(ws
->phs
);
423 region_deinit(&ws
->reg
);
427 bool group_rescue_clientwins(WGroup
*ws
, WRescueInfo
*info
)
431 group_iter_init_nostdisp(&tmp
, ws
);
433 return region_rescue_some_clientwins((WRegion
*)ws
, info
,
434 (WRegionIterator
*)group_iter
,
439 WPHolder
*group_get_rescue_pholder_for(WGroup
*ws
,
442 WGroupAttachParams ap
=GROUPATTACHPARAMS_INIT
;
443 WFramedParam fp
=FRAMEDPARAM_INIT
;
447 ap
.geom
=REGION_GEOM(forwhat
);
451 if(REGION_PARENT(forwhat
)==REGION_PARENT(ws
)){
452 ap
.geom
.x
-=REGION_GEOM(ws
).x
;
453 ap
.geom
.y
-=REGION_GEOM(ws
).y
;
455 ap
.geom_weak
=REGION_RQGEOM_WEAK_X
|REGION_RQGEOM_WEAK_Y
;
460 WFrame *frame=OBJ_CAST(forwhat, WFrame);
465 ph
=(WPHolder
*)create_grouppholder(ws
, NULL
, &ap
);
467 return pholder_either((WPHolder
*)create_framedpholder(ph
, &fp
), ph
);
478 void group_bottom_set(WGroup
*grp
)
480 CALL_DYN(group_bottom_set
, grp
, (grp
));
484 static void group_do_set_bottom(WGroup
*grp
, WStacking
*st
)
486 WStacking
*was
=grp
->bottom
;
487 WStacking
*std
=grp
->managed_stdisp
;
491 if(!OBJ_IS_BEING_DESTROYED(grp
)){
492 bool noremanage
=((was
==st
) ||
493 (was
==NULL
&& std
==NULL
) ||
494 (st
!=NULL
&& st
==std
) ||
495 (st
==NULL
&& was
==std
));
498 (st
==NULL
|| HAS_DYN(st
->reg
, region_manage_stdisp
))){
499 group_remanage_stdisp(grp
);
502 group_bottom_set(grp
);
508 * Sets the `bottom' of \var{ws}. The region \var{reg} must already
509 * be managed by \var{ws}, unless \code{nil}.
512 bool group_set_bottom(WGroup
*ws
, WRegion
*reg
)
517 st
=group_find_stacking(ws
, reg
);
523 group_do_set_bottom(ws
, st
);
530 * Returns the `bottom' of \var{ws}.
534 WRegion
*group_bottom(WGroup
*ws
)
536 return (ws
->bottom
!=NULL
? ws
->bottom
->reg
: NULL
);
546 WStacking
*group_do_add_managed(WGroup
*ws
, WRegion
*reg
, int level
,
550 CALL_DYN_RET(st
, WStacking
*, group_do_add_managed
, ws
,
551 (ws
, reg
, level
, szplcy
));
556 WStacking
*group_do_add_managed_default(WGroup
*ws
, WRegion
*reg
, int level
,
559 WStacking
*st
=NULL
, *tmp
=NULL
;
560 Window bottom
=None
, top
=None
;
561 WStacking
**stackingp
=group_get_stackingp(ws
);
567 st
=create_stacking();
572 if(!stacking_assoc(st
, reg
)){
577 frame
=OBJ_CAST(reg
, WFrame
);
579 if(framemode_unalt(frame_mode(frame
))==FRAME_MODE_TILED
)
580 frame_set_mode(frame
, FRAME_MODE_FLOATING
);
586 LINK_ITEM_FIRST(tmp
, st
, next
, prev
);
587 stacking_weave(stackingp
, &tmp
, FALSE
);
590 LINK_ITEM(ws
->managed_list
, st
, mgr_next
, mgr_prev
);
591 region_set_manager(reg
, (WRegion
*)ws
);
593 if(region_is_fully_mapped((WRegion
*)ws
))
600 static void geom_group_to_parent(WGroup
*ws
, const WRectangle
*g
,
603 wg
->x
=g
->x
+REGION_GEOM(ws
).x
;
604 wg
->y
=g
->y
+REGION_GEOM(ws
).y
;
605 wg
->w
=maxof(1, g
->w
);
606 wg
->h
=maxof(1, g
->h
);
610 static int group_must_focus(WGroup
*ws
, WStacking
*st
)
612 WStacking
*stacking
=group_get_stacking(ws
);
614 return (stacking
!=NULL
&& stacking_must_focus(stacking
, st
));
618 bool group_do_attach_final(WGroup
*ws
,
620 const WGroupAttachParams
*param
)
622 WStacking
*st
, *stabove
=NULL
;
631 if(param
->stack_above
!=NULL
)
632 stabove
=group_find_stacking(ws
, param
->stack_above
);
638 : STACKING_LEVEL_NORMAL
));
641 szplcy
=(param
->szplcy_set
644 ? SIZEPOLICY_FULL_EXACT
645 : SIZEPOLICY_VISIBILITY_CONSTRAINED
));
647 if(!param
->whatever
){
648 weak
=(param
->geom_weak_set
652 : REGION_RQGEOM_WEAK_ALL
));
655 geom_group_to_parent(ws
, ¶m
->geom
, &g
);
659 /* If the requested geometry does not overlap the workspaces's geometry,
660 * position request is never honoured.
662 if((g
.x
+g
.w
<=REGION_GEOM(ws
).x
) ||
663 (g
.x
>=REGION_GEOM(ws
).x
+REGION_GEOM(ws
).w
)){
664 weak
|=REGION_RQGEOM_WEAK_X
;
667 if((g
.y
+g
.h
<=REGION_GEOM(ws
).y
) ||
668 (g
.y
>=REGION_GEOM(ws
).y
+REGION_GEOM(ws
).h
)){
669 weak
|=REGION_RQGEOM_WEAK_Y
;
672 if(weak
&(REGION_RQGEOM_WEAK_X
|REGION_RQGEOM_WEAK_Y
) &&
673 (szplcy
==SIZEPOLICY_UNCONSTRAINED
||
674 szplcy
==SIZEPOLICY_VISIBILITY_CONSTRAINED
||
675 szplcy
==SIZEPOLICY_FREE
||
676 szplcy
==SIZEPOLICY_FREE_GLUE
/* without flags */)){
677 /* TODO: use 'weak'? */
678 group_calc_placement(ws
, level
, &g
);
681 fp
.g
=REGION_GEOM(ws
);
682 fp
.mode
=REGION_FIT_EXACT
;
684 sizepolicy(&szplcy
, reg
, &g
, weak
, &fp
);
686 if(rectangle_compare(&fp
.g
, ®ION_GEOM(reg
))!=RECTANGLE_SAME
)
687 region_fitrep(reg
, NULL
, &fp
);
691 st
=group_do_add_managed(ws
, reg
, level
, szplcy
);
700 group_do_set_bottom(ws
, st
);
703 sw
=((param
->switchto_set
? param
->switchto
: ioncore_g
.switchto_new
)
704 ? st
==find_to_focus(ws
, st
, FALSE
)
705 : group_must_focus(ws
, st
));
708 if(region_may_control_focus((WRegion
*)ws
))
709 region_set_focus(st
->reg
);
711 ws
->current_managed
=st
;
712 }else if(region_is_fully_mapped(reg
)){
713 region_pointer_focus_hack(reg
);
720 static void group_attach_fp(WGroup
*ws
, const WGroupAttachParams
*param
,
724 geom_group_to_parent(ws
, ¶m
->geom
, &fp
->g
);
725 fp
->mode
=REGION_FIT_EXACT
;
727 fp
->g
=REGION_GEOM(ws
);
728 fp
->mode
=REGION_FIT_BOUNDS
|REGION_FIT_WHATEVER
;
733 WRegion
*group_do_attach(WGroup
*ws
,
734 /*const*/ WGroupAttachParams
*param
,
735 WRegionAttachData
*data
)
740 if(ws
->bottom
!=NULL
&& param
->bottom
){
741 warn(TR("'bottom' already set."));
745 group_attach_fp(ws
, param
, &fp
);
747 return region_attach_helper((WRegion
*) ws
, REGION_PARENT(ws
), &fp
,
748 (WRegionDoAttachFn
*)group_do_attach_final
,
749 /*(const WRegionAttachParams*)*/param
, data
);
750 /* ^^^^ doesn't seem to work. */
754 void groupattachparams_get(WGroupAttachParams
*par
, ExtlTab tab
, const char *sub
)
769 if(extl_table_gets_t(tab
, sub
, &s
)){
770 groupattachparams_get(par
, s
, NULL
);
776 if(extl_table_is_bool_set(tab
, "bottom")){
777 par
->level
=STACKING_LEVEL_BOTTOM
;
782 if(extl_table_gets_i(tab
, "level", &tmp
)){
789 if(!par
->level_set
&& extl_table_is_bool_set(tab
, "modal")){
790 par
->level
=STACKING_LEVEL_MODAL1
;
794 if(extl_table_gets_b(tab
, "switchto", &tmpb
)){
795 par
->switchto
=(tmpb
!=0);
799 if(extl_table_gets_i(tab
, "sizepolicy", &tmp
)){
802 }else if(extl_table_gets_s(tab
, "sizepolicy", &tmps
)){
803 if(string2sizepolicy(tmps
, &par
->szplcy
))
808 if(extl_table_gets_t(tab
, "geom", &g
)){
811 if(extl_table_gets_i(g
, "x", &(par
->geom
.x
)))
813 if(extl_table_gets_i(g
, "y", &(par
->geom
.y
)))
815 if(extl_table_gets_i(g
, "w", &(par
->geom
.w
)))
817 if(extl_table_gets_i(g
, "h", &(par
->geom
.h
)))
826 if(extl_table_gets_b(tab
, "auto_placement", &tmpb
)){
827 par
->geom_weak_set
=1;
828 par
->geom_weak
=(tmpb
? REGION_RQGEOM_WEAK_X
|REGION_RQGEOM_WEAK_Y
: 0);
835 * Attach and reparent existing region \var{reg} to \var{ws}.
836 * The table \var{param} may contain the fields \var{index} and
837 * \var{switchto} that are interpreted as for \fnref{WMPlex.attach_new}.
840 WRegion
*group_attach(WGroup
*ws
, WRegion
*reg
, ExtlTab param
)
842 WGroupAttachParams par
=GROUPATTACHPARAMS_INIT
;
843 WRegionAttachData data
;
848 groupattachparams_get(&par
, param
, NULL
);
850 data
.type
=REGION_ATTACH_REPARENT
;
853 return group_do_attach(ws
, &par
, &data
);
858 * Create a new region to be managed by \var{ws}. At least the following
859 * fields in \var{param} are understood:
861 * \begin{tabularx}{\linewidth}{lX}
862 * \tabhead{Field & Description}
863 * \var{type} & (string) Class of the object to be created. Mandatory. \\
864 * \var{name} & (string) Name of the object to be created. \\
865 * \var{switchto} & (boolean) Should the region be switched to? \\
866 * \var{level} & (integer) Stacking level; default is 1. \\
867 * \var{modal} & (boolean) Make object modal; ignored if level is set. \\
868 * \var{sizepolicy} & (string) Size policy; see Section \ref{sec:sizepolicies}. \\
869 * \var{bottom} & (boolean) Mark the attached region as the
870 * ``bottom'' of \var{ws}. \\
873 * In addition parameters to the region to be created are passed in this
877 WRegion
*group_attach_new(WGroup
*ws
, ExtlTab param
)
879 WGroupAttachParams par
=GROUPATTACHPARAMS_INIT
;
880 WRegionAttachData data
;
882 groupattachparams_get(&par
, param
, NULL
);
884 data
.type
=REGION_ATTACH_LOAD
;
887 return group_do_attach(ws
, &par
, &data
);
894 /*{{{ Status display support */
897 static int stdisp_szplcy(const WMPlexSTDispInfo
*di
, WRegion
*stdisp
)
900 int policy
=0, gravity
=0;
903 if(region_orientation(stdisp
)==REGION_ORIENTATION_VERTICAL
){
904 if(pos
==MPLEX_STDISP_TL
|| pos
==MPLEX_STDISP_BL
)
905 policy
=SIZEPOLICY_STRETCH_LEFT
;
907 policy
=SIZEPOLICY_STRETCH_RIGHT
;
909 if(pos
==MPLEX_STDISP_TL
|| pos
==MPLEX_STDISP_TR
)
910 policy
=SIZEPOLICY_STRETCH_TOP
;
912 policy
=SIZEPOLICY_STRETCH_BOTTOM
;
915 policy
=SIZEPOLICY_GRAVITY
;
918 if(pos
==MPLEX_STDISP_TL
)
919 gravity
=SIZEPOLICY_VERT_TOP
|SIZEPOLICY_HORIZ_LEFT
;
920 else if(pos
==MPLEX_STDISP_BL
)
921 gravity
=SIZEPOLICY_VERT_BOTTOM
|SIZEPOLICY_HORIZ_LEFT
;
922 else if(pos
==MPLEX_STDISP_TR
)
923 gravity
=SIZEPOLICY_VERT_TOP
|SIZEPOLICY_HORIZ_RIGHT
;
924 else /*if(pos=MPLEX_STDISP_BR)*/
925 gravity
=SIZEPOLICY_VERT_BOTTOM
|SIZEPOLICY_HORIZ_RIGHT
;
927 return (policy
|gravity
);
931 void group_manage_stdisp(WGroup
*ws
, WRegion
*stdisp
,
932 const WMPlexSTDispInfo
*di
)
936 WRegion
*b
=(ws
->bottom
==NULL
? NULL
: ws
->bottom
->reg
);
938 /* Check if 'bottom' wants to manage the stdisp. */
940 && !OBJ_IS_BEING_DESTROYED(b
)
941 && HAS_DYN(b
, region_manage_stdisp
)){
942 region_manage_stdisp(b
, stdisp
, di
);
943 if(REGION_MANAGER(stdisp
)==b
)
949 szplcy
=stdisp_szplcy(di
, stdisp
)|SIZEPOLICY_SHRUNK
;
951 if(ws
->managed_stdisp
!=NULL
&& ws
->managed_stdisp
->reg
==stdisp
){
952 if(ws
->managed_stdisp
->szplcy
==szplcy
)
954 ws
->managed_stdisp
->szplcy
=szplcy
;
956 region_detach_manager(stdisp
);
957 ws
->managed_stdisp
=group_do_add_managed(ws
, stdisp
,
958 STACKING_LEVEL_ON_TOP
,
962 stdisp
->flags
|=REGION_SKIP_FOCUS
;
964 fp
.g
=REGION_GEOM(ws
);
967 sizepolicy(&ws
->managed_stdisp
->szplcy
, stdisp
, NULL
, 0, &fp
);
969 region_fitrep(stdisp
, NULL
, &fp
);
973 static void group_remanage_stdisp(WGroup
*ws
)
975 WMPlex
*mplex
=OBJ_CAST(REGION_MANAGER(ws
), WMPlex
);
978 mplex
->mx_current
!=NULL
&&
979 mplex
->mx_current
->st
->reg
==(WRegion
*)ws
){
980 mplex_remanage_stdisp(mplex
);
988 /*{{{ Geometry requests */
991 void group_managed_rqgeom(WGroup
*ws
, WRegion
*reg
,
992 const WRQGeomParams
*rq
,
998 st
=group_find_stacking(ws
, reg
);
1002 fp
.mode
=REGION_FIT_EXACT
;
1004 fp
.g
=REGION_GEOM(ws
);
1006 sizepolicy(&st
->szplcy
, reg
, &rq
->geom
, rq
->flags
, &fp
);
1012 if(!(rq
->flags
®ION_RQGEOM_TRYONLY
))
1013 region_fitrep(reg
, NULL
, &fp
);
1017 void group_managed_rqgeom_absolute(WGroup
*grp
, WRegion
*sub
,
1018 const WRQGeomParams
*rq
,
1019 WRectangle
*geomret
)
1021 if(grp
->bottom
!=NULL
&& grp
->bottom
->reg
==sub
){
1022 region_rqgeom((WRegion
*)grp
, rq
, geomret
);
1023 if(!(rq
->flags
®ION_RQGEOM_TRYONLY
) && geomret
!=NULL
)
1024 *geomret
=REGION_GEOM(sub
);
1026 WRQGeomParams rq2
=*rq
;
1027 rq2
.flags
&=~REGION_RQGEOM_ABSOLUTE
;
1029 region_managed_rqgeom((WRegion
*)grp
, sub
, &rq2
, geomret
);
1040 static WStacking
*nxt(WGroup
*ws
, WStacking
*st
, bool wrap
)
1042 return (st
->mgr_next
!=NULL
1044 : (wrap
? ws
->managed_list
: NULL
));
1048 static WStacking
*prv(WGroup
*ws
, WStacking
*st
, bool wrap
)
1050 return (st
!=ws
->managed_list
1052 : (wrap
? st
->mgr_prev
: NULL
));
1056 typedef WStacking
*NxtFn(WGroup
*ws
, WStacking
*st
, bool wrap
);
1059 static bool focusable(WGroup
*ws
, WStacking
*st
, uint min_level
)
1061 return (st
->reg
!=NULL
1062 && REGION_IS_MAPPED(st
->reg
)
1063 && !(st
->reg
->flags
®ION_SKIP_FOCUS
)
1064 && st
->level
>=min_level
);
1068 static WStacking
*do_get_next(WGroup
*ws
, WStacking
*sti
,
1069 NxtFn
*fn
, bool wrap
, bool sti_ok
)
1071 WStacking
*st
, *stacking
;
1074 stacking
=group_get_stacking(ws
);
1077 min_level
=stacking_min_level_mapped(stacking
);
1081 st
=fn(ws
, st
, wrap
);
1083 if(st
==NULL
|| st
==sti
)
1086 if(focusable(ws
, st
, min_level
))
1090 if(sti_ok
&& focusable(ws
, sti
, min_level
))
1097 static WStacking
*group_do_navi_first(WGroup
*ws
, WRegionNavi nh
)
1099 WStacking
*lst
=ws
->managed_list
;
1104 if(nh
==REGION_NAVI_ANY
&&
1105 ws
->current_managed
!=NULL
&&
1106 ws
->current_managed
->reg
!=NULL
){
1107 return ws
->current_managed
;
1110 if(nh
==REGION_NAVI_ANY
|| nh
==REGION_NAVI_END
||
1111 nh
==REGION_NAVI_BOTTOM
|| nh
==REGION_NAVI_RIGHT
){
1112 return do_get_next(ws
, lst
, prv
, TRUE
, TRUE
);
1114 return do_get_next(ws
, lst
->mgr_prev
, nxt
, TRUE
, TRUE
);
1119 static WRegion
*group_navi_first(WGroup
*ws
, WRegionNavi nh
,
1120 WRegionNaviData
*data
)
1122 WStacking
*st
=group_do_navi_first(ws
, nh
);
1124 return region_navi_cont(&ws
->reg
, (st
!=NULL
? st
->reg
: NULL
), data
);
1128 static WStacking
*group_do_navi_next(WGroup
*ws
, WStacking
*st
,
1129 WRegionNavi nh
, bool wrap
)
1132 return group_do_navi_first(ws
, nh
);
1134 if(nh
==REGION_NAVI_ANY
|| nh
==REGION_NAVI_END
||
1135 nh
==REGION_NAVI_BOTTOM
|| nh
==REGION_NAVI_RIGHT
){
1136 return do_get_next(ws
, st
, nxt
, wrap
, FALSE
);
1138 return do_get_next(ws
, st
, prv
, wrap
, FALSE
);
1142 static WRegion
*group_navi_next(WGroup
*ws
, WRegion
*reg
,
1143 WRegionNavi nh
, WRegionNaviData
*data
)
1145 WStacking
*st
=group_find_stacking(ws
, reg
);
1147 st
=group_do_navi_next(ws
, st
, nh
, FALSE
);
1149 return region_navi_cont(&ws
->reg
, (st
!=NULL
? st
->reg
: NULL
), data
);
1160 * Note: Managed objects are considered to be stacked separately from the
1161 * group, slightly violating expectations.
1164 void group_stacking(WGroup
*ws
, Window
*bottomret
, Window
*topret
)
1166 Window win
=region_xwindow((WRegion
*)ws
);
1173 void group_restack(WGroup
*ws
, Window other
, int mode
)
1177 win
=region_xwindow((WRegion
*)ws
);
1179 xwindow_restack(win
, other
, mode
);
1186 WStacking
*group_find_stacking(WGroup
*ws
, WRegion
*r
)
1188 if(r
==NULL
|| REGION_MANAGER(r
)!=(WRegion
*)ws
)
1191 return ioncore_find_stacking(r
);
1195 static WStacking
*find_stacking_if_not_on_ws(WGroup
*ws
, Window w
)
1197 WRegion
*r
=xwindow_region_of(w
);
1201 if(REGION_MANAGER(r
)==(WRegion
*)ws
)
1203 st
=group_find_stacking(ws
, r
);
1206 r
=REGION_MANAGER(r
);
1213 bool group_managed_rqorder(WGroup
*grp
, WRegion
*reg
, WRegionOrder order
)
1215 WStacking
**stackingp
=group_get_stackingp(grp
);
1218 if(stackingp
==NULL
|| *stackingp
==NULL
)
1221 st
=group_find_stacking(grp
, reg
);
1226 stacking_restack(stackingp
, st
, None
, NULL
, NULL
,
1227 (order
!=REGION_ORDER_FRONT
));
1240 * Iterate over managed regions of \var{ws} until \var{iterfn} returns
1242 * The function is called in protected mode.
1243 * This routine returns \code{true} if it reaches the end of list
1244 * without this happening.
1248 bool group_managed_i(WGroup
*ws
, ExtlFn iterfn
)
1251 group_iter_init(&tmp
, ws
);
1253 return extl_iter_objlist_(iterfn
, (ObjIterator
*)group_iter
, &tmp
);
1257 WRegion
* group_current(WGroup
*ws
)
1259 return (ws
->current_managed
!=NULL
? ws
->current_managed
->reg
: NULL
);
1263 void group_size_hints(WGroup
*ws
, WSizeHints
*hints_ret
)
1265 if(ws
->bottom
==NULL
|| ws
->bottom
->reg
==NULL
){
1266 sizehints_clear(hints_ret
);
1268 region_size_hints(ws
->bottom
->reg
, hints_ret
);
1269 hints_ret
->no_constrain
=TRUE
;
1274 Window
group_xwindow(const WGroup
*ws
)
1276 return ws
->dummywin
;
1281 * Returns the group of \var{reg}, if it is managed by one,
1282 * and \var{reg} itself otherwise.
1284 /*EXTL_EXPORT_MEMBER
1285 WRegion *region_group_of(WRegion *reg)
1287 WRegion *mgr=REGION_MANAGER(reg);
1289 return (OBJ_IS(mgr, WGroup) ? mgr : reg);
1294 * Returns the group of \var{reg}, if \var{reg} is its bottom,
1295 * and \var{reg} itself otherwise.
1298 WRegion
*region_groupleader_of(WRegion
*reg
)
1300 WGroup
*grp
=REGION_MANAGER_CHK(reg
, WGroup
);
1302 return ((grp
!=NULL
&& group_bottom(grp
)==reg
)
1314 static ExtlTab
group_get_configuration(WGroup
*ws
)
1316 ExtlTab tab
, mgds
, subtab
, g
;
1323 tab
=region_get_base_configuration((WRegion
*)ws
);
1325 mgds
=extl_create_table();
1327 extl_table_sets_t(tab
, "managed", mgds
);
1329 /* TODO: stacking order messed up */
1331 FOR_ALL_NODES_IN_GROUP(ws
, st
, tmp
){
1335 subtab
=region_get_configuration(st
->reg
);
1337 if(subtab
!=extl_table_none()){
1338 extl_table_sets_s(subtab
, "sizepolicy",
1339 sizepolicy2string(st
->szplcy
));
1340 extl_table_sets_i(subtab
, "level", st
->level
);
1342 tmpg
=REGION_GEOM(st
->reg
);
1343 tmpg
.x
-=REGION_GEOM(ws
).x
;
1344 tmpg
.y
-=REGION_GEOM(ws
).y
;
1346 g
=extl_table_from_rectangle(&tmpg
);
1347 extl_table_sets_t(subtab
, "geom", g
);
1348 extl_unref_table(g
);
1351 extl_table_sets_b(subtab
, "bottom", TRUE
);
1353 extl_table_seti_t(mgds
, ++n
, subtab
);
1354 extl_unref_table(subtab
);
1358 extl_unref_table(mgds
);
1364 void group_do_load(WGroup
*ws
, ExtlTab tab
)
1366 ExtlTab substab
, subtab
;
1369 if(extl_table_gets_t(tab
, "managed", &substab
)){
1370 n
=extl_table_get_n(substab
);
1371 for(i
=1; i
<=n
; i
++){
1372 if(extl_table_geti_t(substab
, i
, &subtab
)){
1373 WGroupAttachParams par
=GROUPATTACHPARAMS_INIT
;
1374 WRegionAttachData data
;
1378 groupattachparams_get(&par
, subtab
, NULL
);
1379 group_attach_fp(ws
, &par
, &fp
);
1381 ph
=(WPHolder
*)create_grouppholder(ws
, NULL
, &par
);
1383 region_attach_load_helper((WRegion
*)ws
, REGION_PARENT(ws
), &fp
,
1384 (WRegionDoAttachFn
*)group_do_attach_final
,
1385 (void*)&par
, subtab
, &ph
);
1388 destroy_obj((Obj
*)ph
);
1390 extl_unref_table(subtab
);
1394 extl_unref_table(substab
);
1399 WRegion
*group_load(WWindow
*par
, const WFitParams
*fp
, ExtlTab tab
)
1403 ws
=create_group(par
, fp
);
1408 group_do_load(ws
, tab
);
1410 return (WRegion
*)ws
;
1417 /*{{{ Dynamic function table and class implementation */
1420 static DynFunTab group_dynfuntab
[]={
1421 {(DynFun
*)region_fitrep
,
1422 (DynFun
*)group_fitrep
},
1430 {(DynFun
*)region_managed_prepare_focus
,
1431 (DynFun
*)group_managed_prepare_focus
},
1433 {region_do_set_focus
,
1434 group_do_set_focus
},
1436 {region_managed_notify
,
1437 group_managed_notify
},
1439 {region_managed_remove
,
1440 group_managed_remove
},
1442 {(DynFun
*)region_get_configuration
,
1443 (DynFun
*)group_get_configuration
},
1445 {(DynFun
*)region_current
,
1446 (DynFun
*)group_current
},
1448 {(DynFun
*)region_rescue_clientwins
,
1449 (DynFun
*)group_rescue_clientwins
},
1457 {(DynFun
*)region_managed_get_pholder
,
1458 (DynFun
*)group_managed_get_pholder
},
1460 {region_managed_rqgeom
,
1461 group_managed_rqgeom
},
1463 {region_managed_rqgeom_absolute
,
1464 group_managed_rqgeom_absolute
},
1466 {(DynFun
*)group_do_add_managed
,
1467 (DynFun
*)group_do_add_managed_default
},
1472 {(DynFun
*)region_xwindow
,
1473 (DynFun
*)group_xwindow
},
1475 {(DynFun
*)region_navi_first
,
1476 (DynFun
*)group_navi_first
},
1478 {(DynFun
*)region_navi_next
,
1479 (DynFun
*)group_navi_next
},
1481 {(DynFun
*)region_managed_rqorder
,
1482 (DynFun
*)group_managed_rqorder
},
1484 {(DynFun
*)region_get_rescue_pholder_for
,
1485 (DynFun
*)group_get_rescue_pholder_for
},
1492 IMPLCLASS(WGroup
, WRegion
, group_deinit
, group_dynfuntab
);