4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
12 #include <libtu/objp.h>
13 #include <libtu/minmax.h>
15 #include <libextl/extl.h>
16 #include <libmainloop/defer.h>
31 #include "frame-pointer.h"
36 #include "mplexpholder.h"
37 #include "grouppholder.h"
40 #include "sizepolicy.h"
46 #define SUBS_MAY_BE_MAPPED(MPLEX) \
47 (REGION_IS_MAPPED(MPLEX) && !MPLEX_MGD_UNVIEWABLE(MPLEX))
49 #define PASSIVE(ST) ((ST)->level<=STACKING_LEVEL_MODAL1 \
51 || (ST)->reg->flags®ION_SKIP_FOCUS))
53 #define CAN_MANAGE_STDISP(REG) HAS_DYN(REG, region_manage_stdisp)
56 /*{{{ Stacking list stuff */
59 WStacking
*mplex_get_stacking(WMPlex
*mplex
)
61 return window_get_stacking(&mplex
->win
);
65 WStacking
**mplex_get_stackingp(WMPlex
*mplex
)
67 return window_get_stackingp(&mplex
->win
);
71 void mplex_iter_init(WMPlexIterTmp
*tmp
, WMPlex
*mplex
)
73 stacking_iter_mgr_init(tmp
, mplex
->mgd
, NULL
, mplex
);
77 WRegion
*mplex_iter(WMPlexIterTmp
*tmp
)
79 return stacking_iter_mgr(tmp
);
83 WStacking
*mplex_iter_nodes(WMPlexIterTmp
*tmp
)
85 return stacking_iter_mgr_nodes(tmp
);
92 /*{{{ Destroy/create mplex */
95 bool mplex_do_init(WMPlex
*mplex
, WWindow
*parent
,
96 const WFitParams
*fp
, Window win
, const char *name
)
101 mplex
->mx_current
=NULL
;
102 mplex
->misc_phs
=NULL
;
107 watch_init(&(mplex
->stdispwatch
));
108 mplex
->stdispinfo
.pos
=MPLEX_STDISP_BL
;
109 mplex
->stdispinfo
.fullsize
=FALSE
;
111 if(!window_do_init((WWindow
*)mplex
, parent
, fp
, win
, name
))
114 mplex
->win
.region
.flags
|=REGION_BINDINGS_ARE_GRABBED
;
116 window_select_input(&(mplex
->win
), IONCORE_EVENTMASK_CWINMGR
);
118 region_register((WRegion
*)mplex
);
120 region_set_name((WRegion
*)mplex
, name
);
122 /* Call this to set MPLEX_MANAGED_UNVIEWABLE if necessary. */
123 mplex_fit_managed(mplex
);
129 bool mplex_init(WMPlex
*mplex
, WWindow
*parent
, const WFitParams
*fp
, const char *name
)
131 return mplex_do_init(mplex
, parent
, fp
, None
, name
);
135 WMPlex
*create_mplex(WWindow
*parent
, const WFitParams
*fp
, const char *name
)
137 CREATEOBJ_IMPL(WMPlex
, mplex
, (p
, parent
, fp
, name
));
141 void mplex_deinit(WMPlex
*mplex
)
146 FOR_ALL_MANAGED_BY_MPLEX(mplex
, reg
, tmp
){
147 destroy_obj((Obj
*)reg
);
150 assert(mplex
->mgd
==NULL
);
151 assert(mplex
->mx_list
==NULL
);
153 while(mplex
->misc_phs
!=NULL
){
154 assert(mplexpholder_move(mplex
->misc_phs
, NULL
, NULL
, NULL
));
157 window_deinit((WWindow
*)mplex
);
164 /*{{{ Node lookup etc. */
167 WStacking
*mplex_find_stacking(WMPlex
*mplex
, WRegion
*reg
)
171 /* Some routines that call us expect us to this check. */
172 if(reg
==NULL
|| REGION_MANAGER(reg
)!=(WRegion
*)mplex
)
175 st
=ioncore_find_stacking(reg
);
177 assert(st
==NULL
|| st
->mgr_prev
!=NULL
);
183 WStacking
*mplex_current_node(WMPlex
*mplex
)
188 reg
=REGION_ACTIVE_SUB(mplex
);
189 reg
=region_managed_within((WRegion
*)mplex
, reg
);
191 st
=mplex_find_stacking(mplex
, reg
);
196 return (mplex
->mx_current
!=NULL
? mplex
->mx_current
->st
: NULL
);
200 WRegion
*mplex_current(WMPlex
*mplex
)
202 WStacking
*node
=mplex_current_node(mplex
);
203 return (node
==NULL
? NULL
: node
->reg
);
210 /*{{{ Exclusive list management and exports */
213 * Returns the number of objects on the mutually exclusive list of \var{mplex}.
217 int mplex_mx_count(WMPlex
*mplex
)
219 return mplex
->mx_count
;
224 * Returns the managed object currently active within the mutually exclusive
225 * list of \var{mplex}.
229 WRegion
*mplex_mx_current(WMPlex
*mplex
)
231 WLListNode
*lnode
=mplex
->mx_current
;
232 return (lnode
==NULL
? NULL
: lnode
->st
->reg
);
237 * Returns the \var{n}:th object on the mutually exclusive
238 * list of \var{mplex}.
242 WRegion
*mplex_mx_nth(WMPlex
*mplex
, uint n
)
244 WLListNode
*lnode
=llist_nth_node(mplex
->mx_list
, n
);
245 return (lnode
==NULL
? NULL
: lnode
->st
->reg
);
250 * Iterate over numbered/mutually exclusive region list of \var{mplex}
251 * until \var{iterfn} returns \code{false}.
252 * The function is called in protected mode.
253 * This routine returns \code{true} if it reaches the end of list
254 * without this happening.
258 bool mplex_mx_i(WMPlex
*mplex
, ExtlFn iterfn
)
261 llist_iter_init(&tmp
, mplex
->mx_list
);
263 return extl_iter_objlist_(iterfn
, (ObjIterator
*)llist_iter_regions
, &tmp
);
268 * Iterate over managed regions of \var{mplex} until \var{iterfn} returns
270 * The function is called in protected mode.
271 * This routine returns \code{true} if it reaches the end of list
272 * without this happening.
276 bool mplex_managed_i(WMPlex
*mplex
, ExtlFn iterfn
)
279 mplex_iter_init(&tmp
, mplex
);
281 return extl_iter_objlist_(iterfn
, (ObjIterator
*)mplex_iter
, &tmp
);
286 * Set index of \var{reg} to \var{index} within the mutually exclusive
287 * list of \var{mplex}. Special values for \var{index} are:
288 * \begin{tabularx}{\linewidth}{lX}
290 * $-2$ & After \fnref{WMPlex.mx_current}. \\
294 void mplex_set_index(WMPlex
*mplex
, WRegion
*reg
, int index
)
296 WLListNode
*lnode
, *after
;
299 node
=mplex_find_stacking(mplex
, reg
);
307 lnode
=ALLOC(WLListNode
);
317 mplex_move_phs_before(mplex
, lnode
);
318 llist_unlink(&(mplex
->mx_list
), lnode
);
321 after
=llist_index_to_after(mplex
->mx_list
, mplex
->mx_current
, index
);
322 llist_link_after(&(mplex
->mx_list
), after
, lnode
);
323 mplex_managed_changed(mplex
, MPLEX_CHANGE_REORDER
, FALSE
, reg
);
328 * Get index of \var{reg} on the mutually exclusive list of \var{mplex}.
329 * The indices begin from zero.. If \var{reg} is not on the list,
334 int mplex_get_index(WMPlex
*mplex
, WRegion
*reg
)
340 FOR_ALL_NODES_ON_LLIST(lnode
, mplex
->mx_list
, tmp
){
341 if(reg
==lnode
->st
->reg
)
351 * Move \var{r} ``right'' within objects managed by \var{mplex} on list 1.
354 void mplex_inc_index(WMPlex
*mplex
, WRegion
*r
)
357 r
=mplex_mx_current(mplex
);
359 mplex_set_index(mplex
, r
, mplex_get_index(mplex
, r
)+1);
364 * Move \var{r} ``left'' within objects managed by \var{mplex} on list 1.
367 void mplex_dec_index(WMPlex
*mplex
, WRegion
*r
)
370 r
=mplex_mx_current(mplex
);
372 mplex_set_index(mplex
, r
, mplex_get_index(mplex
, r
)-1);
382 static void mplex_map_mgd(WMPlex
*mplex
)
387 FOR_ALL_NODES_IN_MPLEX(mplex
, node
, tmp
){
388 if(!STACKING_IS_HIDDEN(node
))
389 region_map(node
->reg
);
394 static void mplex_unmap_mgd(WMPlex
*mplex
)
399 FOR_ALL_NODES_IN_MPLEX(mplex
, node
, tmp
){
400 if(!STACKING_IS_HIDDEN(node
))
401 region_unmap(node
->reg
);
407 void mplex_map(WMPlex
*mplex
)
409 window_map((WWindow
*)mplex
);
410 /* A lame requirement of the ICCCM is that client windows should be
411 * unmapped if the parent is unmapped.
413 if(!MPLEX_MGD_UNVIEWABLE(mplex
))
414 mplex_map_mgd(mplex
);
418 void mplex_unmap(WMPlex
*mplex
)
420 window_unmap((WWindow
*)mplex
);
421 /* A lame requirement of the ICCCM is that client windows should be
422 * unmapped if the parent is unmapped.
424 if(!MPLEX_MGD_UNVIEWABLE(mplex
))
425 mplex_unmap_mgd(mplex
);
432 /*{{{ Resize and reparent */
435 bool mplex_fitrep(WMPlex
*mplex
, WWindow
*par
, const WFitParams
*fp
)
437 bool wchg
=(REGION_GEOM(mplex
).w
!=fp
->g
.w
);
438 bool hchg
=(REGION_GEOM(mplex
).h
!=fp
->g
.h
);
440 if(!window_fitrep(&(mplex
->win
), par
, fp
))
444 mplex_fit_managed(mplex
);
445 mplex_size_changed(mplex
, wchg
, hchg
);
452 void mplex_do_fit_managed(WMPlex
*mplex
, WFitParams
*fp
)
459 if(!MPLEX_MGD_UNVIEWABLE(mplex
) && (fp
->g
.w
<=1 || fp
->g
.h
<=1)){
460 mplex
->flags
|=MPLEX_MANAGED_UNVIEWABLE
;
461 if(REGION_IS_MAPPED(mplex
))
462 mplex_unmap_mgd(mplex
);
463 }else if(MPLEX_MGD_UNVIEWABLE(mplex
) && !(fp
->g
.w
<=1 || fp
->g
.h
<=1)){
464 mplex
->flags
&=~MPLEX_MANAGED_UNVIEWABLE
;
465 if(REGION_IS_MAPPED(mplex
))
466 mplex_map_mgd(mplex
);
469 if(!MPLEX_MGD_UNVIEWABLE(mplex
)){
470 FOR_ALL_NODES_IN_MPLEX(mplex
, node
, tmp
){
472 sizepolicy(&node
->szplcy
, node
->reg
, NULL
, 0, &fp2
);
473 region_fitrep(node
->reg
, NULL
, &fp2
);
479 void mplex_fit_managed(WMPlex
*mplex
)
483 fp
.mode
=REGION_FIT_EXACT
;
484 mplex_managed_geom(mplex
, &(fp
.g
));
486 mplex_do_fit_managed(mplex
, &fp
);
490 static void mplex_managed_rqgeom(WMPlex
*mplex
, WRegion
*sub
,
491 const WRQGeomParams
*rq
,
498 node
=mplex_find_stacking(mplex
, sub
);
503 mplex_managed_geom(mplex
, &fp
.g
);
505 sizepolicy(&node
->szplcy
, sub
, &rq
->geom
, rq
->flags
, &fp
);
510 if(!(rq
->flags
®ION_RQGEOM_TRYONLY
))
511 region_fitrep(sub
, NULL
, &fp
);
515 void mplex_set_szplcy(WMPlex
*mplex
, WRegion
*sub
, WSizePolicy szplcy
)
519 node
=mplex_find_stacking(mplex
, sub
);
526 WSizePolicy
mplex_get_szplcy(WMPlex
*mplex
, WRegion
*sub
)
530 node
=mplex_find_stacking(mplex
, sub
);
532 return (node
==NULL
? SIZEPOLICY_DEFAULT
: node
->szplcy
);
551 static WRegion
*manager_within(WMPlex
*mplex
, WStacking
*st
)
553 return region_managed_within((WRegion
*)mplex
, st
->reg
);
557 static WStacking
*stacking_within(WMPlex
*mplex
, WStacking
*st
)
559 WRegion
*reg
=manager_within(mplex
, st
);
565 : ioncore_find_stacking(reg
)));
569 /* Mutually exclusive regions can't be pseudomodal */
570 #define IS_PSEUDOMODAL(ST) ((ST)->lnode==NULL && (ST)->pseudomodal)
573 static bool mapped_pseudomodal_include_filt(WStacking
*st
, void *data_
)
575 FiltData
*data
=(FiltData
*)data_
;
578 if(st
->reg
==NULL
|| !REGION_IS_MAPPED(st
->reg
))
582 || (data
->to_try
==NULL
&& data
->group_st
==NULL
)
583 || st
->level
<STACKING_LEVEL_MODAL1
){
587 /* Ok, modal node in the way. Let's see if it is pseudomodal
591 stw
=stacking_within(data
->mplex
, st
);
593 /* This should not happen */
594 if(stw
==NULL
|| stw
->reg
==NULL
)
597 /* The node is within the same group, so it can not be hidden.
598 * Latter case should not happen.
600 if(stw
==data
->group_st
|| stw
==data
->to_try
)
603 if(IS_PSEUDOMODAL(stw
)){
604 /* Don't insert multiple times. */
605 return !ptrlist_reinsert_first(data
->hidelist
, stw
);
612 static bool mgr_pseudomodal_approve_filt(WStacking
*st
, void *data_
)
614 FiltData
*data
=(FiltData
*)data_
;
616 return (data
->group_st
==NULL
|| st
==data
->group_st
||
617 manager_within(data
->mplex
, st
)==data
->group_st
->reg
);
621 WStacking
*mplex_find_to_focus(WMPlex
*mplex
,
626 WStackingFilter
*fi
=mapped_pseudomodal_include_filt
;
627 WStackingFilter
*fa
=mgr_pseudomodal_approve_filt
;
628 WStacking
*stacking
=mplex_get_stacking(mplex
);
635 if(to_try
!=NULL
&& (to_try
->reg
==NULL
|| !REGION_IS_MAPPED(to_try
->reg
)))
640 data
.group_st
=group_st
;
641 data
.hidelist
=hidelist
;
643 st
=stacking_find_to_focus(stacking
, to_try
, fi
, fa
, &data
);
645 if(st
==NULL
&& hidelist
!=NULL
)
646 ptrlist_clear(hidelist
);
652 static WStacking
*mplex_do_to_focus_on(WMPlex
*mplex
, WStacking
*node
,
654 PtrList
**hidelist
, bool *within
)
656 WGroup
*grp
=OBJ_CAST(node
->reg
, WGroup
);
661 to_try
=grp
->current_managed
;
662 /* Only will return stuff within 'node' */
663 st
=mplex_find_to_focus(mplex
, to_try
, node
, hidelist
);
671 st
=mplex_find_to_focus(mplex
, node
, NULL
, hidelist
);
673 /* If 'node' points to a group, it isn't actually on the stacking list.
674 * Give it the focus, if there's nothing "proper" that could be focussed.
676 if(st
==NULL
&& grp
!=NULL
&& REGION_IS_MAPPED(grp
))
679 if(st
==node
&& within
!=NULL
)
686 static WStacking
*maybe_focusable(WRegion
*reg
)
688 if(reg
==NULL
|| !REGION_IS_MAPPED(reg
))
691 return ioncore_find_stacking(reg
);
695 static WStacking
*has_stacking_within(WMPlex
*mplex
, WRegion
*reg
)
697 while(reg
!=NULL
&& REGION_MANAGER(reg
)!=(WRegion
*)mplex
)
698 reg
=REGION_MANAGER(reg
);
700 return maybe_focusable(reg
);
704 /* 1. Try keep focus in REGION_ACTIVE_SUB.
705 * 2. Choose something else, attempting previous in focus history.
707 static WStacking
*mplex_to_focus(WMPlex
*mplex
)
709 WStacking
*foc
=NULL
, *fallback
=NULL
;
712 foc
=maybe_focusable(REGION_ACTIVE_SUB(mplex
));
715 /* Search focus history if no specific attempt set.*/
716 for(reg
=ioncore_g
.focus_current
; reg
!=NULL
; reg
=reg
->active_next
){
717 foc
=has_stacking_within(mplex
, reg
);
724 /* In the history search case, 'foc' might point to a group,
725 * since we don't properly try to find a stacking within it...
727 return mplex_do_to_focus_on(mplex
, foc
, NULL
, NULL
, NULL
);
729 return mplex_find_to_focus(mplex
, NULL
, NULL
, NULL
);
734 void mplex_do_set_focus(WMPlex
*mplex
, bool warp
)
736 if(!MPLEX_MGD_UNVIEWABLE(mplex
)){
737 WStacking
*st
=mplex_to_focus(mplex
);
740 st
=(mplex
->mx_current
!=NULL
741 ? mplex
->mx_current
->st
746 region_do_set_focus(st
->reg
, warp
);
751 window_do_set_focus((WWindow
*)mplex
, warp
);
755 static void mplex_refocus(WMPlex
*mplex
, WStacking
*node
, bool warp
)
761 foc
=mplex_do_to_focus_on(mplex
, node
, NULL
, NULL
, &within
);
763 if(foc
==NULL
|| !within
)
764 foc
=mplex_to_focus(mplex
);
767 region_maybewarp(foc
->reg
, warp
);
777 static void mplex_do_remanage_stdisp(WMPlex
*mplex
, WRegion
*sub
)
779 WRegion
*stdisp
=(WRegion
*)(mplex
->stdispwatch
.obj
);
782 if(sub
!=NULL
&& CAN_MANAGE_STDISP(sub
)){
784 WRegion
*omgr
=REGION_MANAGER(stdisp
);
785 if(omgr
!=sub
&& omgr
!=NULL
){
786 if(CAN_MANAGE_STDISP(omgr
))
787 region_unmanage_stdisp(omgr
, FALSE
, FALSE
);
788 region_detach_manager(stdisp
);
791 region_manage_stdisp(sub
, stdisp
,
792 &(mplex
->stdispinfo
));
794 region_unmanage_stdisp(sub
, TRUE
, FALSE
);
800 void mplex_remanage_stdisp(WMPlex
*mplex
)
802 mplex_do_remanage_stdisp(mplex
, (mplex
->mx_current
!=NULL
803 ? mplex
->mx_current
->st
->reg
808 static void mplex_do_node_display(WMPlex
*mplex
, WStacking
*node
,
811 WRegion
*sub
=node
->reg
;
812 WLListNode
*mxc
=mplex
->mx_current
;
815 if(!STACKING_IS_HIDDEN(node
))
818 if(node
->lnode
!=NULL
&& node
->lnode
!=mxc
)
819 mplex_do_remanage_stdisp(mplex
, sub
);
823 if(SUBS_MAY_BE_MAPPED(mplex
))
828 /* the mplex might have been resized while this window was invisible,
829 * and the client window might have had lazy resizing enabled.
831 fp
.mode
=REGION_FIT_EXACT
;
832 mplex_managed_geom(mplex
, &(fp
.g
));
833 sizepolicy(&node
->szplcy
, node
->reg
, NULL
, 0, &fp
);
834 region_fitrep(node
->reg
, NULL
, &fp
);
836 if(node
->lnode
!=NULL
){
838 /* Hide current mx region. We do it after mapping the
839 * new one to avoid flicker.
841 if(REGION_IS_MAPPED(mplex
))
842 region_unmap(mxc
->st
->reg
);
843 mxc
->st
->hidden
=TRUE
;
846 mplex
->mx_current
=node
->lnode
;
849 * Many programs will get upset if the visible, although only
850 * such, client window is not the lowest window in the mplex.
851 * xprop/xwininfo will return the information for the lowest
852 * window. 'netscape -remote' will not work at all if there are
853 * no visible netscape windows.
856 WGroup
*grp
=(WGroup
*)OBJ_CAST(sub
, WGroupCW
);
858 WRegion
*bottom
=group_bottom(grp
);
860 region_managed_rqorder((WRegion
*)grp
, bottom
,
867 mplex_managed_changed(mplex
, MPLEX_CHANGE_SWITCHONLY
, TRUE
, sub
);
872 bool mplex_do_prepare_focus(WMPlex
*mplex
, WStacking
*node
,
873 WStacking
*sub
, int flags
,
874 WPrepareFocusResult
*res
)
876 bool ew
=(flags
®ION_GOTO_ENTERWINDOW
);
877 PtrList
*hidelist
=NULL
;
878 PtrList
**hidelistp
=(ew
? NULL
: &hidelist
);
880 /*bool within=FALSE;*/
882 if(sub
==NULL
&& node
==NULL
)
885 /* Display the node in any case */
886 if(node
!=NULL
&& !ew
)
887 mplex_do_node_display(mplex
, node
, TRUE
);
889 if(!region_prepare_focus((WRegion
*)mplex
, flags
, res
))
892 foc
=mplex_do_to_focus_on(mplex
, node
, sub
, hidelistp
, NULL
/*&within*/);
895 while(hidelist
!=NULL
){
896 WStacking
*st
=(WStacking
*)ptrlist_take_first(&hidelist
);
898 region_unmap(st
->reg
);
901 if(ioncore_g
.autoraise
&&
902 !(flags
®ION_GOTO_ENTERWINDOW
) &&
903 foc
->level
>STACKING_LEVEL_BOTTOM
){
904 WStacking
**stackingp
=mplex_get_stackingp(mplex
);
905 stacking_restack(stackingp
, foc
, None
, NULL
, NULL
, FALSE
);
911 return (foc
==sub
|| (sub
==NULL
&& foc
==node
));
918 bool mplex_managed_prepare_focus(WMPlex
*mplex
, WRegion
*disp
,
919 int flags
, WPrepareFocusResult
*res
)
921 WStacking
*node
=mplex_find_stacking(mplex
, disp
);
926 return mplex_do_prepare_focus(mplex
, node
, NULL
, flags
, res
);
933 /*{{{ Switch exports */
936 static void do_switch(WMPlex
*mplex
, WLListNode
*lnode
)
938 WStacking
*node
=(lnode
!=NULL
? lnode
->st
: NULL
);
941 bool mcf
=region_may_control_focus((WRegion
*)mplex
);
943 mplex_do_node_display(mplex
, node
, TRUE
);
946 mplex_refocus(mplex
, node
, TRUE
);
952 * Have \var{mplex} display the \var{n}:th object managed by it.
955 void mplex_switch_nth(WMPlex
*mplex
, uint n
)
957 do_switch(mplex
, llist_nth_node(mplex
->mx_list
, n
));
962 * Have \var{mplex} display next (wrt. currently selected) object managed
966 void mplex_switch_next(WMPlex
*mplex
)
968 do_switch(mplex
, LIST_NEXT_WRAP(mplex
->mx_list
, mplex
->mx_current
,
974 * Have \var{mplex} display previous (wrt. currently selected) object
978 void mplex_switch_prev(WMPlex
*mplex
)
980 do_switch(mplex
, LIST_PREV_WRAP(mplex
->mx_list
, mplex
->mx_current
,
985 * Have \var{mplex} display the given child window already added to the mplex
988 void mplex_switch_to(WMPlex
*mplex
, WRegion
*reg
)
990 WPrepareFocusResult result
;
991 mplex_managed_prepare_focus(mplex
, reg
->manager
, 0, &result
);
994 bool mplex_set_hidden(WMPlex
*mplex
, WRegion
*reg
, int sp
)
996 bool mcf
=region_may_control_focus((WRegion
*)mplex
);
997 WStacking
*node
=mplex_find_stacking(mplex
, reg
);
998 bool hidden
, nhidden
;
1003 hidden
=STACKING_IS_HIDDEN(node
);
1004 nhidden
=libtu_do_setparam(sp
, hidden
);
1006 if(!hidden
&& nhidden
){
1009 if(REGION_IS_MAPPED(mplex
) && !MPLEX_MGD_UNVIEWABLE(mplex
))
1012 /* lnode -> switch next? */
1013 }else if(hidden
&& !nhidden
){
1014 mplex_do_node_display(mplex
, node
, TRUE
);
1017 if(mcf
&& !PASSIVE(node
))
1018 mplex_refocus(mplex
, (nhidden
? NULL
: node
), TRUE
);
1020 return STACKING_IS_HIDDEN(node
);
1025 * Set the visibility of the region \var{reg} on \var{mplex}
1026 * as specified with the parameter \var{how}
1027 * (one of \codestr{set}, \codestr{unset}, or \codestr{toggle}).
1028 * The resulting state is returned.
1030 EXTL_EXPORT_AS(WMPlex
, set_hidden
)
1031 bool mplex_set_hidden_extl(WMPlex
*mplex
, WRegion
*reg
, const char *how
)
1033 return mplex_set_hidden(mplex
, reg
, libtu_string_to_setparam(how
));
1038 * Is \var{reg} on within \var{mplex} and hidden?
1042 bool mplex_is_hidden(WMPlex
*mplex
, WRegion
*reg
)
1044 WStacking
*node
=mplex_find_stacking(mplex
, reg
);
1046 return (node
!=NULL
&& STACKING_IS_HIDDEN(node
));
1056 static WStacking
*mplex_nxt(WMPlex
*mplex
, WStacking
*st
, bool wrap
)
1058 return (st
->mgr_next
!=NULL
1060 : (wrap
? mplex
->mgd
: NULL
));
1064 static WStacking
*mplex_prv(WMPlex
*mplex
, WStacking
*st
, bool wrap
)
1066 return (st
!=mplex
->mgd
1068 : (wrap
? st
->mgr_prev
: NULL
));
1072 typedef WStacking
*NxtFn(WMPlex
*mplex
, WStacking
*st
, bool wrap
);
1075 static WRegion
*do_navi(WMPlex
*mplex
, WStacking
*sti
,
1076 NxtFn
*fn
, WRegionNaviData
*data
,
1077 bool sti_ok
, bool wrap
)
1079 WStacking
*st
, *stacking
;
1082 stacking
=mplex_get_stacking(mplex
);
1085 min_level
=stacking_min_level_mapped(stacking
);
1089 st
=fn(mplex
, st
, wrap
);
1091 if(st
==NULL
|| (st
==sti
&& !sti_ok
))
1095 if(OBJ_IS(st
->reg
, WGroup
)){
1096 /* WGroup navigation code should respect modal stuff. */
1097 WRegion
*res
=region_navi_cont((WRegion
*)mplex
, st
->reg
, data
);
1098 if(res
!=NULL
&& res
!=st
->reg
)
1101 if(st
->level
>=min_level
&& !PASSIVE(st
))
1102 return region_navi_cont((WRegion
*)mplex
, st
->reg
, data
);
1114 WRegion
*mplex_navi_first(WMPlex
*mplex
, WRegionNavi nh
,
1115 WRegionNaviData
*data
)
1117 WStacking
*lst
=mplex
->mgd
;
1121 if(nh
==REGION_NAVI_ANY
){
1125 if(nh
==REGION_NAVI_ANY
|| nh
==REGION_NAVI_END
||
1126 nh
==REGION_NAVI_BOTTOM
|| nh
==REGION_NAVI_RIGHT
){
1127 res
=do_navi(mplex
, lst
, mplex_prv
, data
, TRUE
, TRUE
);
1129 res
=do_navi(mplex
, lst
->mgr_prev
, mplex_nxt
, data
, TRUE
, TRUE
);
1133 return region_navi_cont((WRegion
*)mplex
, res
, data
);
1137 WRegion
*mplex_navi_next(WMPlex
*mplex
, WRegion
*rel
, WRegionNavi nh
,
1138 WRegionNaviData
*data
)
1144 st
=mplex_find_stacking(mplex
, rel
);
1147 }else if(mplex
->mx_current
!=NULL
){
1148 st
=mplex
->mx_current
->st
;
1150 return mplex_navi_first(mplex
, nh
, data
);
1153 if(nh
==REGION_NAVI_ANY
){
1157 if(nh
==REGION_NAVI_ANY
|| nh
==REGION_NAVI_END
||
1158 nh
==REGION_NAVI_BOTTOM
|| nh
==REGION_NAVI_RIGHT
){
1159 res
=do_navi(mplex
, st
, mplex_nxt
, data
, FALSE
, FALSE
);
1161 res
=do_navi(mplex
, st
, mplex_prv
, data
, FALSE
, FALSE
);
1164 return region_navi_cont((WRegion
*)mplex
, res
, data
);
1174 bool mplex_managed_rqorder(WMPlex
*mplex
, WRegion
*reg
, WRegionOrder order
)
1176 WStacking
**stackingp
=mplex_get_stackingp(mplex
);
1179 if(stackingp
==NULL
|| *stackingp
==NULL
)
1182 st
=mplex_find_stacking(mplex
, reg
);
1187 stacking_restack(stackingp
, st
, None
, NULL
, NULL
,
1188 (order
!=REGION_ORDER_FRONT
));
1200 static bool mplex_stack(WMPlex
*mplex
, WStacking
*st
)
1202 WStacking
*tmp
=NULL
;
1203 WStacking
**stackingp
=mplex_get_stackingp(mplex
);
1208 LINK_ITEM_FIRST(tmp
, st
, next
, prev
);
1209 stacking_weave(stackingp
, &tmp
, FALSE
);
1216 static void mplex_unstack(WMPlex
*mplex
, WStacking
*st
)
1218 WStacking
*stacking
;
1220 stacking
=mplex_get_stacking(mplex
);
1222 stacking_unstack(&mplex
->win
, st
);
1226 /* WMPlexWPHolder is used for position marking in order to allow
1227 * WLListNodes be safely removed in the attach handler hnd, that
1228 * could remove something this mplex is managing.
1230 bool mplex_do_attach_final(WMPlex
*mplex
, WRegion
*reg
, WMPlexPHolder
*ph
)
1232 WStacking
*node
=NULL
;
1233 WLListNode
*lnode
=NULL
;
1234 WMPlexAttachParams
*param
=&ph
->param
;
1235 bool mx_was_empty
, sw
, modal
, mcf
, hidden
;
1239 mcf
=region_may_control_focus((WRegion
*)mplex
);
1241 mx_was_empty
=(mplex
->mx_list
==NULL
);
1243 szplcy
=((param
->flags
&MPLEX_ATTACH_SIZEPOLICY
&&
1244 param
->szplcy
!=SIZEPOLICY_DEFAULT
)
1246 : (param
->flags
&MPLEX_ATTACH_UNNUMBERED
1247 ? SIZEPOLICY_FULL_BOUNDS
1248 : SIZEPOLICY_FULL_EXACT
));
1250 modal
=(param
->flags
&MPLEX_ATTACH_LEVEL
1251 && param
->level
>=STACKING_LEVEL_MODAL1
);
1253 level
=(param
->flags
&MPLEX_ATTACH_LEVEL
1255 : (param
->flags
&MPLEX_ATTACH_UNNUMBERED
1256 ? STACKING_LEVEL_NORMAL
1257 : STACKING_LEVEL_BOTTOM
));
1259 hidden
=(param
->flags
&MPLEX_ATTACH_HIDDEN
1260 && (param
->flags
&MPLEX_ATTACH_UNNUMBERED
1263 sw
=(!hidden
&& (param
->flags
&MPLEX_ATTACH_SWITCHTO
1264 || (param
->flags
&MPLEX_ATTACH_UNNUMBERED
1266 : (mplex_current_node(mplex
)==NULL
))));
1268 hidden
=(hidden
|| (!sw
&& !(param
->flags
&MPLEX_ATTACH_UNNUMBERED
)));
1270 node
=create_stacking();
1275 if(!(param
->flags
&MPLEX_ATTACH_UNNUMBERED
)){
1276 lnode
=ALLOC(WLListNode
);
1278 stacking_free(node
);
1288 if(!stacking_assoc(node
, reg
)){
1293 stacking_free(node
);
1298 node
->szplcy
=szplcy
;
1300 node
->pseudomodal
=(param
->flags
&MPLEX_ATTACH_PSEUDOMODAL
? 1 : 0);
1303 WMPlexPHolder
*ph2
, *phn
, *php
;
1305 llist_link_after(&(mplex
->mx_list
),
1306 (ph
!=NULL
? ph
->after
: NULL
),
1311 /* Move placeholders after new node */
1312 for(php
=NULL
, ph2
=ph
; ph2
!=NULL
; php
=ph2
, ph2
=phn
){
1314 mplexpholder_move(ph2
, mplex
, php
, lnode
);
1318 LINK_ITEM(mplex
->mgd
, node
, mgr_next
, mgr_prev
);
1320 if(!OBJ_IS(reg
, WGroup
))
1321 mplex_stack(mplex
, node
);
1323 region_set_manager(reg
, (WRegion
*)mplex
);
1325 if(param
->flags
&MPLEX_ATTACH_PASSIVE
)
1326 reg
->flags
|=REGION_SKIP_FOCUS
;
1328 if(!(param
->flags
&MPLEX_ATTACH_WHATEVER
)){
1332 mplex_managed_geom(mplex
, &(fp
.g
));
1334 sizepolicy(&node
->szplcy
, reg
,
1335 (param
->flags
&MPLEX_ATTACH_GEOM
? &(param
->geom
) : NULL
),
1338 if(rectangle_compare(&fp
.g
, ®ION_GEOM(reg
))!=RECTANGLE_SAME
)
1339 region_fitrep(reg
, NULL
, &fp
);
1343 mplex_do_node_display(mplex
, node
, FALSE
);
1349 mplex_refocus(mplex
, node
, FALSE
);
1351 (level
>=STACKING_LEVEL_MODAL1
|| OBJ_IS(reg
, WGroup
))){
1352 /* New modal regions may require focusing, so try to
1353 * give focus back to currently active object.
1354 * (There seems to be some problem with uncontained
1355 * client windows still..)
1357 mplex_refocus(mplex
, NULL
, FALSE
);
1359 region_pointer_focus_hack(reg
);
1362 region_pointer_focus_hack(reg
);
1366 mplex_managed_changed(mplex
, MPLEX_CHANGE_ADD
, sw
, reg
);
1372 static void mplex_attach_fp(WMPlex
*mplex
, const WMPlexAttachParams
*param
,
1375 if(param
->flags
&MPLEX_ATTACH_GEOM
)
1378 mplex_managed_geom(mplex
, &(fp
->g
));
1380 fp
->mode
=REGION_FIT_WHATEVER
|REGION_FIT_BOUNDS
;
1384 WRegion
*mplex_do_attach_pholder(WMPlex
*mplex
, WMPlexPHolder
*ph
,
1385 WRegionAttachData
*data
)
1389 mplex_attach_fp(mplex
, &ph
->param
, &fp
);
1391 return region_attach_helper((WRegion
*)mplex
,
1392 (WWindow
*)mplex
, &fp
,
1393 (WRegionDoAttachFn
*)mplex_do_attach_final
,
1398 WRegion
*mplex_do_attach(WMPlex
*mplex
, WMPlexAttachParams
*param
,
1399 WRegionAttachData
*data
)
1404 ph
=create_mplexpholder(mplex
, NULL
, param
);
1409 reg
=mplex_do_attach_pholder(mplex
, ph
, data
);
1411 destroy_obj((Obj
*)ph
);
1417 WRegion
*mplex_do_attach_new(WMPlex
*mplex
, WMPlexAttachParams
*param
,
1418 WRegionCreateFn
*fn
, void *fn_param
)
1420 WRegionAttachData data
;
1422 data
.type
=REGION_ATTACH_NEW
;
1424 data
.u
.n
.param
=fn_param
;
1426 return mplex_do_attach(mplex
, param
, &data
);
1430 #define MPLEX_ATTACH_SET_FLAGS (MPLEX_ATTACH_GEOM| \
1431 MPLEX_ATTACH_SIZEPOLICY| \
1435 WRegion
*mplex_attach_simple(WMPlex
*mplex
, WRegion
*reg
, int flags
)
1437 WMPlexAttachParams param
;
1438 WRegionAttachData data
;
1440 param
.flags
=flags
&~MPLEX_ATTACH_SET_FLAGS
;
1442 data
.type
=REGION_ATTACH_REPARENT
;
1445 return mplex_do_attach(mplex
, ¶m
, &data
);
1449 static void get_params(WMPlex
*mplex
, ExtlTab tab
, int mask
,
1450 WMPlexAttachParams
*par
)
1456 if(ok
&MPLEX_ATTACH_LEVEL
){
1457 if(extl_table_gets_i(tab
, "level", &tmp
)){
1459 par
->flags
|=MPLEX_ATTACH_LEVEL
;
1464 if(extl_table_is_bool_set(tab
, "modal"))
1465 par
->level
=maxof(par
->level
, STACKING_LEVEL_MODAL1
);
1468 if(extl_table_is_bool_set(tab
, "unnumbered"))
1469 par
->flags
|=MPLEX_ATTACH_UNNUMBERED
&ok
;
1471 if(extl_table_is_bool_set(tab
, "switchto"))
1472 par
->flags
|=MPLEX_ATTACH_SWITCHTO
&ok
;
1474 if(extl_table_is_bool_set(tab
, "hidden"))
1475 par
->flags
|=MPLEX_ATTACH_HIDDEN
&ok
;
1477 if(extl_table_is_bool_set(tab
, "passive"))
1478 par
->flags
|=MPLEX_ATTACH_PASSIVE
&ok
;
1480 if(extl_table_is_bool_set(tab
, "pseudomodal"))
1481 par
->flags
|=MPLEX_ATTACH_PSEUDOMODAL
&ok
;
1483 if(extl_table_gets_i(tab
, "index", &(par
->index
)))
1484 par
->flags
|=MPLEX_ATTACH_INDEX
&ok
;
1486 if(ok
&MPLEX_ATTACH_SIZEPOLICY
){
1487 if(extl_table_gets_sizepolicy(tab
, "sizepolicy", &par
->szplcy
)){
1488 par
->flags
|=MPLEX_ATTACH_SIZEPOLICY
;
1489 }else if(extl_table_gets_i(tab
, "sizepolicy", &tmp
)){
1490 /* Backwards compat. numeric version */
1491 par
->flags
|=MPLEX_ATTACH_SIZEPOLICY
;
1496 if(extl_table_gets_rectangle(tab
, "geom", &par
->geom
))
1497 par
->flags
|=MPLEX_ATTACH_GEOM
&ok
;
1502 * Attach and reparent existing region \var{reg} to \var{mplex}.
1503 * The table \var{param} may contain the fields \var{index} and
1504 * \var{switchto} that are interpreted as for \fnref{WMPlex.attach_new}.
1507 WRegion
*mplex_attach(WMPlex
*mplex
, WRegion
*reg
, ExtlTab param
)
1509 WMPlexAttachParams par
=MPLEXATTACHPARAMS_INIT
;
1510 WRegionAttachData data
;
1515 get_params(mplex
, param
, 0, &par
);
1517 data
.type
=REGION_ATTACH_REPARENT
;
1520 return mplex_do_attach(mplex
, &par
, &data
);
1524 WRegion
*mplex_attach_new_(WMPlex
*mplex
, WMPlexAttachParams
*par
,
1525 int mask
, ExtlTab param
)
1527 WRegionAttachData data
;
1529 get_params(mplex
, param
, mask
, par
);
1531 data
.type
=REGION_ATTACH_LOAD
;
1534 return mplex_do_attach(mplex
, par
, &data
);
1539 * Create a new region to be managed by \var{mplex}. At least the following
1540 * fields in \var{param} are understood (all but \var{type} are optional).
1542 * \begin{tabularx}{\linewidth}{lX}
1543 * \tabhead{Field & Description}
1544 * \var{type} & (string) Class name (a string) of the object to be created. \\
1545 * \var{name} & (string) Name of the object to be created (a string). \\
1546 * \var{switchto} & (boolean) Should the region be switched to (boolean)? \\
1547 * \var{unnumbered} & (boolean) Do not put on the numbered mutually
1548 * exclusive list. \\
1549 * \var{index} & (integer) Index on this list, same as for
1550 * \fnref{WMPlex.set_index}. \\
1551 * \var{level} & (integer) Stacking level. \\
1552 * \var{modal} & (boolean) Shortcut for modal stacking level. \\
1553 * \var{hidden} & (boolean) Attach hidden, if not prevented
1554 * by e.g. the mutually exclusive list being empty.
1555 * This option overrides \var{switchto}. \\
1556 * \var{passive} & (boolean) Skip in certain focusing operations. \\
1557 * \var{pseudomodal} & (boolean) The attached region is ``pseudomodal''
1558 * if the stacking level dictates it to be modal.
1559 * This means that the region may be hidden to display
1560 * regions with lesser stacking levels. \\
1561 * \var{sizepolicy} & (string) Size policy; see Section \ref{sec:sizepolicies}. \\
1562 * \var{geom} & (table) Geometry specification. \\
1565 * In addition parameters to the region to be created are passed in this
1569 WRegion
*mplex_attach_new(WMPlex
*mplex
, ExtlTab param
)
1571 WMPlexAttachParams par
=MPLEXATTACHPARAMS_INIT
;
1573 return mplex_attach_new_(mplex
, &par
, 0, param
);
1577 static bool mplex_handle_drop(WMPlex
*mplex
, int x
, int y
,
1580 WRegion
*curr
=mplex_mx_current(mplex
);
1582 /* This code should handle dropping tabs on floating workspaces. */
1583 if(curr
&& HAS_DYN(curr
, region_handle_drop
)){
1585 region_rootpos(curr
, &rx
, &ry
);
1586 if(rectangle_contains(®ION_GEOM(curr
), x
-rx
, y
-ry
)){
1587 if(region_handle_drop(curr
, x
, y
, dropped
))
1592 return (NULL
!=mplex_attach_simple(mplex
, dropped
, MPLEX_ATTACH_SWITCHTO
));
1596 WPHolder
*mplex_prepare_manage(WMPlex
*mplex
, const WClientWin
*cwin
,
1597 const WManageParams
*param
, int priority
)
1599 int cpriority
=MANAGE_PRIORITY_SUB(priority
, MANAGE_PRIORITY_NORMAL
);
1600 WMPlexAttachParams ap
;
1605 /* Check current */ {
1606 WStacking
*cur
=mplex_current_node(mplex
);
1609 ph
=region_prepare_manage(cur
->reg
, cwin
, param
, cpriority
);
1614 if(mplex
->mx_current
!=NULL
&& mplex
->mx_current
->st
!=cur
){
1615 ph
=region_prepare_manage(mplex
->mx_current
->st
->reg
,
1616 cwin
, param
, cpriority
);
1622 if(!MANAGE_PRIORITY_OK(priority
, MANAGE_PRIORITY_NORMAL
))
1625 ap
.flags
=((param
->switchto
? MPLEX_ATTACH_SWITCHTO
: 0)
1626 |MPLEX_ATTACH_SIZEPOLICY
);
1627 ap
.szplcy
=SIZEPOLICY_FULL_EXACT
;
1629 mph
=create_mplexpholder(mplex
, NULL
, &ap
);
1633 WGroupAttachParams gp
=GROUPATTACHPARAMS_INIT
;
1639 gph
=create_grouppholder(NULL
, NULL
, &gp
);
1642 gph
->recreate_pholder
=(WPHolder
*)mph
;
1643 return (WPHolder
*)gph
;
1647 return (WPHolder
*)mph
;
1657 void mplex_managed_remove(WMPlex
*mplex
, WRegion
*sub
)
1659 bool mx
=FALSE
, hadfocus
=FALSE
, mcf
;
1660 WRegion
*stdisp
=(WRegion
*)(mplex
->stdispwatch
.obj
);
1661 WStacking
*node
, *next
=NULL
;
1663 mcf
=region_may_control_focus((WRegion
*)mplex
);
1666 if(CAN_MANAGE_STDISP(sub
) &&
1667 region_managed_within((WRegion
*)mplex
, stdisp
)==sub
){
1668 region_unmanage_stdisp(sub
, TRUE
, TRUE
);
1669 region_detach_manager(stdisp
);
1673 node
=mplex_find_stacking(mplex
, sub
);
1678 hadfocus
=(mplex_current_node(mplex
)==node
);
1680 if(node
->lnode
!=NULL
){
1681 if(mplex
->mx_current
==node
->lnode
){
1684 mplex
->mx_current
=NULL
;
1685 lnext
=LIST_PREV(mplex
->mx_list
, node
->lnode
, next
, prev
);
1687 lnext
=LIST_NEXT(mplex
->mx_list
, node
->lnode
, next
, prev
);
1688 if(lnext
==node
->lnode
)
1695 mplex_move_phs_before(mplex
, node
->lnode
);
1696 llist_unlink(&(mplex
->mx_list
), node
->lnode
);
1704 UNLINK_ITEM(mplex
->mgd
, node
, mgr_next
, mgr_prev
);
1706 mplex_unstack(mplex
, node
);
1708 stacking_unassoc(node
);
1709 stacking_free(node
);
1711 region_unset_manager(sub
, (WRegion
*)mplex
);
1713 if(OBJ_IS_BEING_DESTROYED(mplex
))
1717 mplex_do_node_display(mplex
, next
, FALSE
);
1720 mplex_refocus(mplex
, next
, FALSE
);
1723 mplex_managed_changed(mplex
, MPLEX_CHANGE_REMOVE
, next
!=NULL
, sub
);
1727 void mplex_child_removed(WMPlex
*mplex
, WRegion
*sub
)
1729 if(sub
!=NULL
&& sub
==(WRegion
*)(mplex
->stdispwatch
.obj
)){
1730 watch_reset(&(mplex
->stdispwatch
));
1731 mplex_set_stdisp(mplex
, NULL
, NULL
);
1742 bool mplex_rescue_clientwins(WMPlex
*mplex
, WRescueInfo
*info
)
1747 WLListNode
*lnode
, *was_current
=mplex
->mx_current
;
1750 /* First all mx stuff to move them nicely to another mplex (when that
1751 * is the case), switching to the current region in the target if
1752 * allowed by ph_flags_mask region_rescue.
1754 FOR_ALL_NODES_ON_LLIST(lnode
, mplex
->mx_list
, ltmp
){
1755 int sw
=(lnode
==was_current
? PHOLDER_ATTACH_SWITCHTO
: 0);
1756 region_do_rescue_this(lnode
->st
->reg
, info
, sw
);
1759 /* Then the rest (possibly retrying failed mx stuff).
1761 mplex_iter_init(&tmp
, mplex
);
1762 ret1
=region_rescue_some_clientwins((WRegion
*)mplex
, info
,
1763 (WRegionIterator
*)mplex_iter
,
1766 ret2
=region_rescue_child_clientwins((WRegion
*)mplex
, info
);
1768 return (ret1
&& ret2
);
1775 /*{{{ Status display support */
1778 bool mplex_set_stdisp(WMPlex
*mplex
, WRegion
*reg
,
1779 const WMPlexSTDispInfo
*din
)
1781 WRegion
*oldstdisp
=(WRegion
*)(mplex
->stdispwatch
.obj
);
1784 assert(reg
==NULL
|| (reg
==oldstdisp
) ||
1785 (REGION_MANAGER(reg
)==NULL
&&
1786 REGION_PARENT(reg
)==(WWindow
*)mplex
));
1788 if(oldstdisp
!=NULL
){
1789 mgr
=region_managed_within((WRegion
*)mplex
, oldstdisp
);
1791 if(!CAN_MANAGE_STDISP(mgr
))
1796 mplex
->stdispinfo
=*din
;
1799 watch_reset(&(mplex
->stdispwatch
));
1802 region_unmanage_stdisp(mgr
, TRUE
, FALSE
);
1804 region_detach_manager(oldstdisp
);
1807 watch_setup(&(mplex
->stdispwatch
), (Obj
*)reg
, NULL
);
1809 mplex_remanage_stdisp(mplex
);
1812 if(oldstdisp
!=NULL
&& oldstdisp
!=reg
)
1813 mainloop_defer_destroy((Obj
*)oldstdisp
);
1819 void mplex_get_stdisp(WMPlex
*mplex
, WRegion
**reg
, WMPlexSTDispInfo
*di
)
1821 *di
=mplex
->stdispinfo
;
1822 *reg
=(WRegion
*)mplex
->stdispwatch
.obj
;
1826 static StringIntMap pos_map
[]={
1827 {"tl", MPLEX_STDISP_TL
},
1828 {"tr", MPLEX_STDISP_TR
},
1829 {"bl", MPLEX_STDISP_BL
},
1830 {"br", MPLEX_STDISP_BR
},
1835 static bool do_attach_stdisp(WRegion
*mplex
, WRegion
*reg
, void *unused
)
1837 /* We do not actually manage the stdisp. */
1843 * Set/create status display for \var{mplex}. Table is a standard
1844 * description of the object to be created (as passed to e.g.
1845 * \fnref{WMPlex.attach_new}). In addition, the following fields are
1848 * \begin{tabularx}{\linewidth}{lX}
1849 * \tabhead{Field & Description}
1850 * \var{pos} & (string) The corner of the screen to place the status
1851 * display in: one of \codestr{tl}, \codestr{tr}, \codestr{bl}
1852 * or \codestr{br}. \\
1853 * \var{fullsize} & (boolean) Waste all available space. \\
1854 * \var{action} & (string) If this field is set to \codestr{keep},
1855 * \var{pos} and \var{fullsize} are changed for the existing
1856 * status display. If this field is set to \codestr{remove},
1857 * the existing status display is removed. If this
1858 * field is not set or is set to \codestr{replace}, a
1859 * new status display is created and the old, if any,
1863 EXTL_EXPORT_AS(WMPlex
, set_stdisp
)
1864 WRegion
*mplex_set_stdisp_extl(WMPlex
*mplex
, ExtlTab t
)
1866 WRegion
*stdisp
=NULL
;
1867 WMPlexSTDispInfo din
=mplex
->stdispinfo
;
1870 if(extl_table_gets_s(t
, "pos", &s
)){
1871 din
.pos
=stringintmap_value(pos_map
, s
, -1);
1873 warn(TR("Invalid position setting."));
1878 extl_table_gets_b(t
, "fullsize", &(din
.fullsize
));
1881 extl_table_gets_s(t
, "action", &s
);
1883 if(s
==NULL
|| strcmp(s
, "replace")==0){
1884 WRegionAttachData data
;
1890 fp
.g
.w
=REGION_GEOM(mplex
).w
;
1891 fp
.g
.h
=REGION_GEOM(mplex
).h
;
1892 fp
.mode
=REGION_FIT_BOUNDS
|REGION_FIT_WHATEVER
;
1894 /* Full mplex size is stupid so use saved geometry initially
1897 extl_table_gets_rectangle(t
, "geom", &(fp
.g
));
1899 data
.type
=REGION_ATTACH_LOAD
;
1902 stdisp
=region_attach_helper((WRegion
*)mplex
,
1903 (WWindow
*)mplex
, &fp
,
1904 do_attach_stdisp
, NULL
,
1910 }else if(strcmp(s
, "keep")==0){
1911 stdisp
=(WRegion
*)(mplex
->stdispwatch
.obj
);
1912 }else if(strcmp(s
, "remove")!=0){
1913 warn(TR("Invalid action setting."));
1917 if(!mplex_set_stdisp(mplex
, stdisp
, &din
)){
1918 destroy_obj((Obj
*)stdisp
);
1926 static ExtlTab
mplex_do_get_stdisp_extl(WMPlex
*mplex
, bool fullconfig
)
1928 WRegion
*reg
=(WRegion
*)mplex
->stdispwatch
.obj
;
1932 return extl_table_none();
1935 t
=region_get_configuration(reg
);
1936 extl_table_sets_rectangle(t
, "geom", ®ION_GEOM(reg
));
1938 t
=extl_create_table();
1939 extl_table_sets_o(t
, "reg", (Obj
*)reg
);
1942 if(t
!=extl_table_none()){
1943 WMPlexSTDispInfo
*di
=&(mplex
->stdispinfo
);
1944 extl_table_sets_s(t
, "pos", stringintmap_key(pos_map
, di
->pos
, NULL
));
1945 extl_table_sets_b(t
, "fullsize", di
->fullsize
);
1952 * Get status display information. See \fnref{WMPlex.get_stdisp} for
1953 * information on the fields.
1956 EXTL_EXPORT_AS(WMPlex
, get_stdisp
)
1957 ExtlTab
mplex_get_stdisp_extl(WMPlex
*mplex
)
1959 return mplex_do_get_stdisp_extl(mplex
, FALSE
);
1969 void mplex_managed_geom_default(const WMPlex
*mplex
, WRectangle
*geom
)
1973 geom
->w
=REGION_GEOM(mplex
).w
;
1974 geom
->h
=REGION_GEOM(mplex
).h
;
1978 void mplex_managed_geom(const WMPlex
*mplex
, WRectangle
*geom
)
1980 CALL_DYN(mplex_managed_geom
, mplex
, (mplex
, geom
));
1984 void mplex_size_changed(WMPlex
*mplex
, bool wchg
, bool hchg
)
1986 CALL_DYN(mplex_size_changed
, mplex
, (mplex
, wchg
, hchg
));
1990 void mplex_managed_changed(WMPlex
*mplex
, int mode
, bool sw
, WRegion
*mgd
)
1992 CALL_DYN(mplex_managed_changed
, mplex
, (mplex
, mode
, sw
, mgd
));
1996 int mplex_default_index(WMPlex
*mplex
)
1998 int idx
=LLIST_INDEX_LAST
;
1999 CALL_DYN_RET(idx
, int, mplex_default_index
, mplex
, (mplex
));
2004 /* For regions managing stdisps */
2006 void region_manage_stdisp(WRegion
*reg
, WRegion
*stdisp
,
2007 const WMPlexSTDispInfo
*info
)
2009 CALL_DYN(region_manage_stdisp
, reg
, (reg
, stdisp
, info
));
2013 void region_unmanage_stdisp(WRegion
*reg
, bool permanent
, bool nofocus
)
2015 CALL_DYN(region_unmanage_stdisp
, reg
, (reg
, permanent
, nofocus
));
2022 /*{{{ Changed hook helper */
2025 static const char *mode2str(int mode
)
2027 if(mode
==MPLEX_CHANGE_SWITCHONLY
)
2028 return "switchonly";
2029 else if(mode
==MPLEX_CHANGE_REORDER
)
2031 else if(mode
==MPLEX_CHANGE_ADD
)
2033 else if(mode
==MPLEX_CHANGE_REMOVE
)
2039 static bool mrsh_chg(ExtlFn fn
, WMPlexChangedParams
*p
)
2041 ExtlTab t
=extl_create_table();
2044 extl_table_sets_o(t
, "reg", (Obj
*)p
->reg
);
2045 extl_table_sets_s(t
, "mode", mode2str(p
->mode
));
2046 extl_table_sets_b(t
, "sw", p
->sw
);
2047 extl_table_sets_o(t
, "sub", (Obj
*)p
->sub
);
2050 ret
=extl_call(fn
, "t", NULL
, t
);
2051 extl_unprotect(NULL
);
2053 extl_unref_table(t
);
2059 void mplex_call_changed_hook(WMPlex
*mplex
, WHook
*hook
,
2060 int mode
, bool sw
, WRegion
*reg
)
2062 WMPlexChangedParams p
;
2069 hook_call_p(hook
, &p
, (WHookMarshallExtl
*)mrsh_chg
);
2079 static void save_node(WMPlex
*mplex
, ExtlTab subs
, int *n
,
2080 WStacking
*node
, bool unnumbered
)
2084 st
=region_get_configuration(node
->reg
);
2086 if(st
!=extl_table_none()){
2087 if(mplex
->mx_current
!=NULL
&& node
==mplex
->mx_current
->st
)
2088 extl_table_sets_b(st
, "switchto", TRUE
);
2089 extl_table_sets_s(st
, "sizepolicy",
2090 sizepolicy2string(node
->szplcy
));
2091 extl_table_sets_i(st
, "level", node
->level
);
2092 g
=extl_table_from_rectangle(®ION_GEOM(node
->reg
));
2093 extl_table_sets_t(st
, "geom", g
);
2094 extl_unref_table(g
);
2095 if(STACKING_IS_HIDDEN(node
))
2096 extl_table_sets_b(st
, "hidden", TRUE
);
2097 if(STACKING_IS_PSEUDOMODAL(node
))
2098 extl_table_sets_b(st
, "pseudomodal", TRUE
);
2100 extl_table_sets_b(st
, "unnumbered", TRUE
);
2102 extl_table_seti_t(subs
, ++(*n
), st
);
2103 extl_unref_table(st
);
2108 ExtlTab
mplex_get_configuration(WMPlex
*mplex
)
2110 ExtlTab tab
, subs
, stdisptab
;
2117 tab
=region_get_base_configuration((WRegion
*)mplex
);
2119 subs
=extl_create_table();
2120 extl_table_sets_t(tab
, "managed", subs
);
2122 /* First the numbered/mutually exclusive nodes */
2123 FOR_ALL_NODES_ON_LLIST(lnode
, mplex
->mx_list
, ltmp
){
2124 save_node(mplex
, subs
, &n
, lnode
->st
, FALSE
);
2127 FOR_ALL_NODES_IN_MPLEX(mplex
, node
, tmp
){
2128 if(node
->lnode
==NULL
)
2129 save_node(mplex
, subs
, &n
, node
, TRUE
);
2132 extl_unref_table(subs
);
2134 /*stdisptab=mplex_do_get_stdisp_extl(mplex, TRUE);
2135 if(stdisptab!=extl_table_none()){
2136 extl_table_sets_t(tab, "stdisp", stdisptab);
2137 extl_unref_table(stdisptab);
2144 void mplex_load_contents(WMPlex
*mplex
, ExtlTab tab
)
2146 ExtlTab substab
, subtab
;
2149 /*if(extl_table_gets_t(tab, "stdisp", &subtab)){
2150 mplex_set_stdisp_extl(mplex, subtab);
2151 extl_unref_table(subtab);
2154 if(extl_table_gets_t(tab
, "managed", &substab
) ||
2155 extl_table_gets_t(tab
, "subs", &substab
)){
2156 n
=extl_table_get_n(substab
);
2157 for(i
=1; i
<=n
; i
++){
2158 if(extl_table_geti_t(substab
, i
, &subtab
)){
2159 WMPlexAttachParams par
=MPLEXATTACHPARAMS_INIT
;
2163 get_params(mplex
, subtab
, 0, &par
);
2164 mplex_attach_fp(mplex
, &par
, &fp
);
2166 par
.flags
|=MPLEX_ATTACH_INDEX
;
2167 par
.index
=LLIST_INDEX_LAST
;
2169 ph
=(WPHolder
*)create_mplexpholder(mplex
, NULL
, &par
);
2172 region_attach_load_helper((WRegion
*)mplex
, (WWindow
*)mplex
, &fp
,
2173 (WRegionDoAttachFn
*)mplex_do_attach_final
,
2174 (void*)ph
, subtab
, &ph
);
2177 destroy_obj((Obj
*)ph
);
2180 extl_unref_table(subtab
);
2183 extl_unref_table(substab
);
2188 WRegion
*mplex_load(WWindow
*par
, const WFitParams
*fp
, ExtlTab tab
, const char *name
)
2190 WMPlex
*mplex
=create_mplex(par
, fp
, name
);
2192 mplex_load_contents(mplex
, tab
);
2193 return (WRegion
*)mplex
;
2200 /*{{{ Dynfuntab and class info */
2203 static DynFunTab mplex_dynfuntab
[]={
2204 {region_do_set_focus
,
2205 mplex_do_set_focus
},
2207 {region_managed_remove
,
2208 mplex_managed_remove
},
2210 {region_managed_rqgeom
,
2211 mplex_managed_rqgeom
},
2213 {(DynFun
*)region_managed_prepare_focus
,
2214 (DynFun
*)mplex_managed_prepare_focus
},
2216 {(DynFun
*)region_handle_drop
,
2217 (DynFun
*)mplex_handle_drop
},
2219 {region_map
, mplex_map
},
2220 {region_unmap
, mplex_unmap
},
2222 {(DynFun
*)region_prepare_manage
,
2223 (DynFun
*)mplex_prepare_manage
},
2225 {(DynFun
*)region_current
,
2226 (DynFun
*)mplex_current
},
2228 {(DynFun
*)region_rescue_clientwins
,
2229 (DynFun
*)mplex_rescue_clientwins
},
2231 {(DynFun
*)region_get_configuration
,
2232 (DynFun
*)mplex_get_configuration
},
2234 {mplex_managed_geom
,
2235 mplex_managed_geom_default
},
2237 {(DynFun
*)region_fitrep
,
2238 (DynFun
*)mplex_fitrep
},
2240 {region_child_removed
,
2241 mplex_child_removed
},
2243 {(DynFun
*)region_managed_get_pholder
,
2244 (DynFun
*)mplex_managed_get_pholder
},
2246 {(DynFun
*)region_get_rescue_pholder_for
,
2247 (DynFun
*)mplex_get_rescue_pholder_for
},
2249 {(DynFun
*)region_navi_first
,
2250 (DynFun
*)mplex_navi_first
},
2252 {(DynFun
*)region_navi_next
,
2253 (DynFun
*)mplex_navi_next
},
2255 {(DynFun
*)region_managed_rqorder
,
2256 (DynFun
*)mplex_managed_rqorder
},
2263 IMPLCLASS(WMPlex
, WWindow
, mplex_deinit
, mplex_dynfuntab
);