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_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
);
61 : window_get_stacking(par
));
65 WStacking
**group_get_stackingp(WGroup
*ws
)
67 WWindow
*par
=REGION_PARENT(ws
);
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
;
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
)
129 WStacking
*unweaved
=NULL
;
130 int xdiff
=0, ydiff
=0;
135 oldpar
=REGION_PARENT(ws
);
138 if(fp
->mode
®ION_FIT_WHATEVER
)
140 REGION_GEOM(ws
)=fp
->g
;
142 if(!region_same_rootwin((WRegion
*)ws
, (WRegion
*)par
))
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
®ION_FIT_WHATEVER
)){
167 REGION_GEOM(ws
).w
=fp
->g
.w
;
168 REGION_GEOM(ws
).h
=fp
->g
.h
;
172 unweaved
=stacking_unweave(&oldpar
->stacking
, wsfilt
, (void*)ws
);
175 FOR_ALL_NODES_IN_GROUP(ws
, st
, tmp
){
181 g
=REGION_GEOM(st
->reg
);
185 if(fp
->mode
®ION_FIT_WHATEVER
){
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
);
199 stacking_weave(&par
->stacking
, &unweaved
, FALSE
);
205 static void group_map(WGroup
*ws
)
210 REGION_MARK_MAPPED(ws
);
211 XMapWindow(ioncore_g
.dpy
, ws
->dummywin
);
213 FOR_ALL_MANAGED_BY_GROUP(ws
, reg
, tmp
){
219 static void group_unmap(WGroup
*ws
)
224 REGION_MARK_UNMAPPED(ws
);
225 XUnmapWindow(ioncore_g
.dpy
, ws
->dummywin
);
227 FOR_ALL_MANAGED_BY_GROUP(ws
, reg
, tmp
){
233 static WStacking
*find_to_focus(WGroup
*ws
, WStacking
*st
, bool group_only
)
235 WStacking
*stacking
=group_get_stacking(ws
);
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
);
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
);
266 WStacking
*node
=mplex_find_stacking(mplex
, (WRegion
*)ws
);
271 return mplex_do_prepare_focus(mplex
, node
, st
,
274 if(!region_prepare_focus((WRegion
*)ws
, flags
, res
))
277 st
=find_to_focus(ws
, st
, FALSE
);
282 if(ioncore_g
.autoraise
&&
283 !(flags
®ION_GOTO_ENTERWINDOW
) &&
284 st
->level
>STACKING_LEVEL_BOTTOM
){
285 WStacking
**stackingp
=group_get_stackingp(ws
);
286 stacking_restack(stackingp
, st
, None
, NULL
, NULL
, FALSE
);
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
);
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
;
317 next_st
=stacking_unstack(REGION_PARENT(ws
), st
);
318 UNLINK_ITEM(ws
->managed_list
, st
, mgr_next
, mgr_prev
);
319 stacking_unassoc(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
);
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
);
353 /*{{{ Create/destroy */
356 bool group_init(WGroup
*ws
, WWindow
*par
, const WFitParams
*fp
, const char *name
)
360 ws
->current_managed
=NULL
;
361 ws
->managed_stdisp
=NULL
;
363 ws
->managed_list
=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
)
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
,
385 ((WRegion
*)ws
)->flags
|=REGION_GRAB_ON_PARENT
;
387 region_add_bindmap((WRegion
*)ws
, ioncore_group_bindmap
);
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
)
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
);
420 grouppholder_do_unlink(ws
->phs
);
422 region_deinit(&ws
->reg
);
426 bool group_rescue_clientwins(WGroup
*ws
, WRescueInfo
*info
)
430 group_iter_init_nostdisp(&tmp
, ws
);
432 return region_rescue_some_clientwins((WRegion
*)ws
, info
,
433 (WRegionIterator
*)group_iter
,
438 WPHolder
*group_get_rescue_pholder_for(WGroup
*ws
,
441 WGroupAttachParams ap
=GROUPATTACHPARAMS_INIT
;
442 WFramedParam fp
=FRAMEDPARAM_INIT
;
446 ap
.geom
=REGION_GEOM(forwhat
);
450 if(REGION_PARENT(forwhat
)==REGION_PARENT(ws
)){
451 ap
.geom
.x
-=REGION_GEOM(ws
).x
;
452 ap
.geom
.y
-=REGION_GEOM(ws
).y
;
454 ap
.geom_weak
=REGION_RQGEOM_WEAK_X
|REGION_RQGEOM_WEAK_Y
;
459 WFrame *frame=OBJ_CAST(forwhat, WFrame);
464 ph
=(WPHolder
*)create_grouppholder(ws
, NULL
, &ap
);
466 return pholder_either((WPHolder
*)create_framedpholder(ph
, &fp
), ph
);
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
;
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
));
497 (st
==NULL
|| HAS_DYN(st
->reg
, region_manage_stdisp
))){
498 group_remanage_stdisp(grp
);
501 group_bottom_set(grp
);
507 * Sets the `bottom' of \var{ws}. The region \var{reg} must already
508 * be managed by \var{ws}, unless \code{nil}.
511 bool group_set_bottom(WGroup
*ws
, WRegion
*reg
)
516 st
=group_find_stacking(ws
, reg
);
522 group_do_set_bottom(ws
, st
);
529 * Returns the `bottom' of \var{ws}.
533 WRegion
*group_bottom(WGroup
*ws
)
535 return (ws
->bottom
!=NULL
? ws
->bottom
->reg
: NULL
);
545 WStacking
*group_do_add_managed(WGroup
*ws
, WRegion
*reg
, int level
,
549 CALL_DYN_RET(st
, WStacking
*, group_do_add_managed
, ws
,
550 (ws
, reg
, level
, szplcy
));
555 WStacking
*group_do_add_managed_default(WGroup
*ws
, WRegion
*reg
, int level
,
558 WStacking
*st
=NULL
, *tmp
=NULL
;
559 WStacking
**stackingp
=group_get_stackingp(ws
);
565 st
=create_stacking();
570 if(!stacking_assoc(st
, reg
)){
575 frame
=OBJ_CAST(reg
, WFrame
);
577 if(framemode_unalt(frame_mode(frame
))==FRAME_MODE_TILED
)
578 frame_set_mode(frame
, FRAME_MODE_FLOATING
);
584 LINK_ITEM_FIRST(tmp
, st
, next
, prev
);
585 stacking_weave(stackingp
, &tmp
, FALSE
);
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
))
598 static void geom_group_to_parent(WGroup
*ws
, const WRectangle
*g
,
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
,
618 const WGroupAttachParams
*param
)
620 WStacking
*st
, *stabove
=NULL
;
629 if(param
->stack_above
!=NULL
)
630 stabove
=group_find_stacking(ws
, param
->stack_above
);
636 : STACKING_LEVEL_NORMAL
));
639 szplcy
=(param
->szplcy_set
642 ? SIZEPOLICY_FULL_EXACT
643 : SIZEPOLICY_VISIBILITY_CONSTRAINED
));
645 if(!param
->whatever
){
646 weak
=(param
->geom_weak_set
650 : REGION_RQGEOM_WEAK_ALL
));
653 geom_group_to_parent(ws
, ¶m
->geom
, &g
);
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
, ®ION_GEOM(reg
))!=RECTANGLE_SAME
)
685 region_fitrep(reg
, NULL
, &fp
);
689 st
=group_do_add_managed(ws
, reg
, level
, szplcy
);
698 group_do_set_bottom(ws
, st
);
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
));
706 if(region_may_control_focus((WRegion
*)ws
))
707 region_set_focus(st
->reg
);
709 ws
->current_managed
=st
;
710 }else if(region_is_fully_mapped(reg
)){
711 region_pointer_focus_hack(reg
);
718 static void group_attach_fp(WGroup
*ws
, const WGroupAttachParams
*param
,
722 geom_group_to_parent(ws
, ¶m
->geom
, &fp
->g
);
723 fp
->mode
=REGION_FIT_EXACT
;
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
)
737 if(ws
->bottom
!=NULL
&& param
->bottom
){
738 warn(TR("'bottom' already set."));
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
)
766 if(extl_table_gets_t(tab
, sub
, &s
)){
767 groupattachparams_get(par
, s
, NULL
);
773 if(extl_table_is_bool_set(tab
, "bottom")){
774 par
->level
=STACKING_LEVEL_BOTTOM
;
779 if(extl_table_gets_i(tab
, "level", &tmp
)){
786 if(!par
->level_set
&& extl_table_is_bool_set(tab
, "modal")){
787 par
->level
=STACKING_LEVEL_MODAL1
;
791 if(extl_table_gets_b(tab
, "switchto", &tmpb
)){
792 par
->switchto
=(tmpb
!=0);
796 if(extl_table_gets_i(tab
, "sizepolicy", &tmp
)){
799 }else if(extl_table_gets_s(tab
, "sizepolicy", &tmps
)){
800 if(string2sizepolicy(tmps
, &par
->szplcy
))
805 if(extl_table_gets_t(tab
, "geom", &g
)){
808 if(extl_table_gets_i(g
, "x", &(par
->geom
.x
)))
810 if(extl_table_gets_i(g
, "y", &(par
->geom
.y
)))
812 if(extl_table_gets_i(g
, "w", &(par
->geom
.w
)))
814 if(extl_table_gets_i(g
, "h", &(par
->geom
.h
)))
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);
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}.
837 WRegion
*group_attach(WGroup
*ws
, WRegion
*reg
, ExtlTab param
)
839 WGroupAttachParams par
=GROUPATTACHPARAMS_INIT
;
840 WRegionAttachData data
;
845 groupattachparams_get(&par
, param
, NULL
);
847 data
.type
=REGION_ATTACH_REPARENT
;
850 return group_do_attach(ws
, &par
, &data
);
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}. \\
870 * In addition parameters to the region to be created are passed in this
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
;
884 return group_do_attach(ws
, &par
, &data
);
891 /*{{{ Status display support */
894 static int stdisp_szplcy(const WMPlexSTDispInfo
*di
, WRegion
*stdisp
)
897 int policy
=0, gravity
=0;
900 if(region_orientation(stdisp
)==REGION_ORIENTATION_VERTICAL
){
901 if(pos
==MPLEX_STDISP_TL
|| pos
==MPLEX_STDISP_BL
)
902 policy
=SIZEPOLICY_STRETCH_LEFT
;
904 policy
=SIZEPOLICY_STRETCH_RIGHT
;
906 if(pos
==MPLEX_STDISP_TL
|| pos
==MPLEX_STDISP_TR
)
907 policy
=SIZEPOLICY_STRETCH_TOP
;
909 policy
=SIZEPOLICY_STRETCH_BOTTOM
;
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
)
933 WRegion
*b
=(ws
->bottom
==NULL
? NULL
: ws
->bottom
->reg
);
935 /* Check if 'bottom' wants to manage the stdisp. */
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
)
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
)
951 ws
->managed_stdisp
->szplcy
=szplcy
;
953 region_detach_manager(stdisp
);
954 ws
->managed_stdisp
=group_do_add_managed(ws
, stdisp
,
955 STACKING_LEVEL_ON_TOP
,
959 stdisp
->flags
|=REGION_SKIP_FOCUS
;
961 fp
.g
=REGION_GEOM(ws
);
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
);
975 mplex
->mx_current
!=NULL
&&
976 mplex
->mx_current
->st
->reg
==(WRegion
*)ws
){
977 mplex_remanage_stdisp(mplex
);
985 /*{{{ Geometry requests */
988 void group_managed_rqgeom(WGroup
*ws
, WRegion
*reg
,
989 const WRQGeomParams
*rq
,
995 st
=group_find_stacking(ws
, reg
);
999 fp
.mode
=REGION_FIT_EXACT
;
1001 fp
.g
=REGION_GEOM(ws
);
1003 sizepolicy(&st
->szplcy
, reg
, &rq
->geom
, rq
->flags
, &fp
);
1009 if(!(rq
->flags
®ION_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
®ION_RQGEOM_TRYONLY
) && geomret
!=NULL
)
1021 *geomret
=REGION_GEOM(sub
);
1023 WRQGeomParams rq2
=*rq
;
1024 rq2
.flags
&=~REGION_RQGEOM_ABSOLUTE
;
1026 region_managed_rqgeom((WRegion
*)grp
, sub
, &rq2
, geomret
);
1037 static WStacking
*nxt(WGroup
*ws
, WStacking
*st
, bool wrap
)
1039 return (st
->mgr_next
!=NULL
1041 : (wrap
? ws
->managed_list
: NULL
));
1045 static WStacking
*prv(WGroup
*ws
, WStacking
*st
, bool wrap
)
1047 return (st
!=ws
->managed_list
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
®ION_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
;
1071 stacking
=group_get_stacking(ws
);
1074 min_level
=stacking_min_level_mapped(stacking
);
1078 st
=fn(ws
, st
, wrap
);
1080 if(st
==NULL
|| st
==sti
)
1083 if(focusable(ws
, st
, min_level
))
1087 if(sti_ok
&& focusable(ws
, sti
, min_level
))
1094 static WStacking
*group_do_navi_first(WGroup
*ws
, WRegionNavi nh
)
1096 WStacking
*lst
=ws
->managed_list
;
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
);
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
)
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
);
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
);
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
);
1170 void group_restack(WGroup
*ws
, Window other
, int mode
)
1174 win
=region_xwindow((WRegion
*)ws
);
1176 xwindow_restack(win
, other
, mode
);
1183 WStacking
*group_find_stacking(WGroup
*ws
, WRegion
*r
)
1185 if(r
==NULL
|| REGION_MANAGER(r
)!=(WRegion
*)ws
)
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
);
1198 if(REGION_MANAGER(r
)==(WRegion
*)ws
)
1200 st
=group_find_stacking(ws
, r
);
1203 r
=REGION_MANAGER(r
);
1210 bool group_managed_rqorder(WGroup
*grp
, WRegion
*reg
, WRegionOrder order
)
1212 WStacking
**stackingp
=group_get_stackingp(grp
);
1215 if(stackingp
==NULL
|| *stackingp
==NULL
)
1218 st
=group_find_stacking(grp
, reg
);
1223 stacking_restack(stackingp
, st
, None
, NULL
, NULL
,
1224 (order
!=REGION_ORDER_FRONT
));
1237 * Iterate over managed regions of \var{ws} until \var{iterfn} returns
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.
1245 bool group_managed_i(WGroup
*ws
, ExtlFn iterfn
)
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
);
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
;
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);
1291 * Returns the group of \var{reg}, if \var{reg} is its bottom,
1292 * and \var{reg} itself otherwise.
1295 WRegion
*region_groupleader_of(WRegion
*reg
)
1297 WGroup
*grp
=REGION_MANAGER_CHK(reg
, WGroup
);
1299 return ((grp
!=NULL
&& group_bottom(grp
)==reg
)
1311 ExtlTab
group_get_configuration(WGroup
*ws
)
1313 ExtlTab tab
, mgds
, subtab
, g
;
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
){
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
);
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
);
1360 void group_do_load(WGroup
*ws
, ExtlTab tab
)
1362 ExtlTab substab
, subtab
;
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
;
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
);
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
)
1398 /* Generic initial name - to be overwritten later. */
1399 ws
=create_group(par
, fp
, "Notion GroupCW or GroupWS");
1404 group_do_load(ws
, tab
);
1406 return (WRegion
*)ws
;
1413 /*{{{ Dynamic function table and class implementation */
1416 static DynFunTab group_dynfuntab
[]={
1417 {(DynFun
*)region_fitrep
,
1418 (DynFun
*)group_fitrep
},
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
},
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
},
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
},
1488 IMPLCLASS(WGroup
, WRegion
, group_deinit
, group_dynfuntab
);