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
)
458 if(!MPLEX_MGD_UNVIEWABLE(mplex
) && (fp
->g
.w
<=1 || fp
->g
.h
<=1)){
459 mplex
->flags
|=MPLEX_MANAGED_UNVIEWABLE
;
460 if(REGION_IS_MAPPED(mplex
))
461 mplex_unmap_mgd(mplex
);
462 }else if(MPLEX_MGD_UNVIEWABLE(mplex
) && !(fp
->g
.w
<=1 || fp
->g
.h
<=1)){
463 mplex
->flags
&=~MPLEX_MANAGED_UNVIEWABLE
;
464 if(REGION_IS_MAPPED(mplex
))
465 mplex_map_mgd(mplex
);
468 if(!MPLEX_MGD_UNVIEWABLE(mplex
)){
469 FOR_ALL_NODES_IN_MPLEX(mplex
, node
, tmp
){
471 sizepolicy(&node
->szplcy
, node
->reg
, NULL
, 0, &fp2
);
472 region_fitrep(node
->reg
, NULL
, &fp2
);
478 void mplex_fit_managed(WMPlex
*mplex
)
482 fp
.mode
=REGION_FIT_EXACT
;
483 mplex_managed_geom(mplex
, &(fp
.g
));
485 mplex_do_fit_managed(mplex
, &fp
);
489 static void mplex_managed_rqgeom(WMPlex
*mplex
, WRegion
*sub
,
490 const WRQGeomParams
*rq
,
496 node
=mplex_find_stacking(mplex
, sub
);
501 mplex_managed_geom(mplex
, &fp
.g
);
503 sizepolicy(&node
->szplcy
, sub
, &rq
->geom
, rq
->flags
, &fp
);
508 if(!(rq
->flags
®ION_RQGEOM_TRYONLY
))
509 region_fitrep(sub
, NULL
, &fp
);
513 void mplex_set_szplcy(WMPlex
*mplex
, WRegion
*sub
, WSizePolicy szplcy
)
517 node
=mplex_find_stacking(mplex
, sub
);
524 WSizePolicy
mplex_get_szplcy(WMPlex
*mplex
, WRegion
*sub
)
528 node
=mplex_find_stacking(mplex
, sub
);
530 return (node
==NULL
? SIZEPOLICY_DEFAULT
: node
->szplcy
);
549 static WRegion
*manager_within(WMPlex
*mplex
, WStacking
*st
)
551 return region_managed_within((WRegion
*)mplex
, st
->reg
);
555 static WStacking
*stacking_within(WMPlex
*mplex
, WStacking
*st
)
557 WRegion
*reg
=manager_within(mplex
, st
);
563 : ioncore_find_stacking(reg
)));
567 /* Mutually exclusive regions can't be pseudomodal */
568 #define IS_PSEUDOMODAL(ST) ((ST)->lnode==NULL && (ST)->pseudomodal)
571 static bool mapped_pseudomodal_include_filt(WStacking
*st
, void *data_
)
573 FiltData
*data
=(FiltData
*)data_
;
576 if(st
->reg
==NULL
|| !REGION_IS_MAPPED(st
->reg
))
580 || (data
->to_try
==NULL
&& data
->group_st
==NULL
)
581 || st
->level
<STACKING_LEVEL_MODAL1
){
585 /* Ok, modal node in the way. Let's see if it is pseudomodal
589 stw
=stacking_within(data
->mplex
, st
);
591 /* This should not happen */
592 if(stw
==NULL
|| stw
->reg
==NULL
)
595 /* The node is within the same group, so it can not be hidden.
596 * Latter case should not happen.
598 if(stw
==data
->group_st
|| stw
==data
->to_try
)
601 if(IS_PSEUDOMODAL(stw
)){
602 /* Don't insert multiple times. */
603 return !ptrlist_reinsert_first(data
->hidelist
, stw
);
610 static bool mgr_pseudomodal_approve_filt(WStacking
*st
, void *data_
)
612 FiltData
*data
=(FiltData
*)data_
;
614 return (data
->group_st
==NULL
|| st
==data
->group_st
||
615 manager_within(data
->mplex
, st
)==data
->group_st
->reg
);
619 WStacking
*mplex_find_to_focus(WMPlex
*mplex
,
624 WStackingFilter
*fi
=mapped_pseudomodal_include_filt
;
625 WStackingFilter
*fa
=mgr_pseudomodal_approve_filt
;
626 WStacking
*stacking
=mplex_get_stacking(mplex
);
633 if(to_try
!=NULL
&& (to_try
->reg
==NULL
|| !REGION_IS_MAPPED(to_try
->reg
)))
638 data
.group_st
=group_st
;
639 data
.hidelist
=hidelist
;
641 st
=stacking_find_to_focus(stacking
, to_try
, fi
, fa
, &data
);
643 if(st
==NULL
&& hidelist
!=NULL
)
644 ptrlist_clear(hidelist
);
650 static WStacking
*mplex_do_to_focus_on(WMPlex
*mplex
, WStacking
*node
,
652 PtrList
**hidelist
, bool *within
)
654 WGroup
*grp
=OBJ_CAST(node
->reg
, WGroup
);
659 to_try
=grp
->current_managed
;
660 /* Only will return stuff within 'node' */
661 st
=mplex_find_to_focus(mplex
, to_try
, node
, hidelist
);
669 st
=mplex_find_to_focus(mplex
, node
, NULL
, hidelist
);
671 /* If 'node' points to a group, it isn't actually on the stacking list.
672 * Give it the focus, if there's nothing "proper" that could be focussed.
674 if(st
==NULL
&& grp
!=NULL
&& REGION_IS_MAPPED(grp
))
677 if(st
==node
&& within
!=NULL
)
684 static WStacking
*maybe_focusable(WRegion
*reg
)
686 if(reg
==NULL
|| !REGION_IS_MAPPED(reg
))
689 return ioncore_find_stacking(reg
);
693 static WStacking
*has_stacking_within(WMPlex
*mplex
, WRegion
*reg
)
695 while(reg
!=NULL
&& REGION_MANAGER(reg
)!=(WRegion
*)mplex
)
696 reg
=REGION_MANAGER(reg
);
698 return maybe_focusable(reg
);
702 /* 1. Try keep focus in REGION_ACTIVE_SUB.
703 * 2. Choose something else, attempting previous in focus history.
705 static WStacking
*mplex_to_focus(WMPlex
*mplex
)
710 foc
=maybe_focusable(REGION_ACTIVE_SUB(mplex
));
713 /* Search focus history if no specific attempt set.*/
714 for(reg
=ioncore_g
.focuslist
; reg
!=NULL
; reg
=reg
->active_next
){
715 foc
=has_stacking_within(mplex
, reg
);
722 /* In the history search case, 'foc' might point to a group,
723 * since we don't properly try to find a stacking within it...
725 return mplex_do_to_focus_on(mplex
, foc
, NULL
, NULL
, NULL
);
727 return mplex_find_to_focus(mplex
, NULL
, NULL
, NULL
);
732 void mplex_do_set_focus(WMPlex
*mplex
, bool warp
)
734 if(!MPLEX_MGD_UNVIEWABLE(mplex
)){
735 WStacking
*st
=mplex_to_focus(mplex
);
738 st
=(mplex
->mx_current
!=NULL
739 ? mplex
->mx_current
->st
744 region_do_set_focus(st
->reg
, warp
);
749 window_do_set_focus((WWindow
*)mplex
, warp
);
753 static void mplex_refocus(WMPlex
*mplex
, WStacking
*node
, bool warp
)
759 foc
=mplex_do_to_focus_on(mplex
, node
, NULL
, NULL
, &within
);
761 if(foc
==NULL
|| !within
)
762 foc
=mplex_to_focus(mplex
);
765 region_maybewarp(foc
->reg
, warp
);
775 static void mplex_do_remanage_stdisp(WMPlex
*mplex
, WRegion
*sub
)
777 WRegion
*stdisp
=(WRegion
*)(mplex
->stdispwatch
.obj
);
780 if(sub
!=NULL
&& CAN_MANAGE_STDISP(sub
)){
782 WRegion
*omgr
=REGION_MANAGER(stdisp
);
783 if(omgr
!=sub
&& omgr
!=NULL
){
784 if(CAN_MANAGE_STDISP(omgr
))
785 region_unmanage_stdisp(omgr
, FALSE
, FALSE
);
786 region_detach_manager(stdisp
);
789 region_manage_stdisp(sub
, stdisp
,
790 &(mplex
->stdispinfo
));
792 region_unmanage_stdisp(sub
, TRUE
, FALSE
);
798 void mplex_remanage_stdisp(WMPlex
*mplex
)
800 mplex_do_remanage_stdisp(mplex
, (mplex
->mx_current
!=NULL
801 ? mplex
->mx_current
->st
->reg
806 static void mplex_do_node_display(WMPlex
*mplex
, WStacking
*node
,
809 WRegion
*sub
=node
->reg
;
810 WLListNode
*mxc
=mplex
->mx_current
;
813 if(!STACKING_IS_HIDDEN(node
))
816 if(node
->lnode
!=NULL
&& node
->lnode
!=mxc
)
817 mplex_do_remanage_stdisp(mplex
, sub
);
821 if(SUBS_MAY_BE_MAPPED(mplex
))
826 /* the mplex might have been resized while this window was invisible,
827 * and the client window might have had lazy resizing enabled.
829 fp
.mode
=REGION_FIT_EXACT
;
830 mplex_managed_geom(mplex
, &(fp
.g
));
831 sizepolicy(&node
->szplcy
, node
->reg
, NULL
, 0, &fp
);
832 region_fitrep(node
->reg
, NULL
, &fp
);
834 if(node
->lnode
!=NULL
){
836 /* Hide current mx region. We do it after mapping the
837 * new one to avoid flicker.
839 if(REGION_IS_MAPPED(mplex
))
840 region_unmap(mxc
->st
->reg
);
841 mxc
->st
->hidden
=TRUE
;
844 mplex
->mx_current
=node
->lnode
;
847 * Many programs will get upset if the visible, although only
848 * such, client window is not the lowest window in the mplex.
849 * xprop/xwininfo will return the information for the lowest
850 * window. 'netscape -remote' will not work at all if there are
851 * no visible netscape windows.
854 WGroup
*grp
=(WGroup
*)OBJ_CAST(sub
, WGroupCW
);
856 WRegion
*bottom
=group_bottom(grp
);
858 region_managed_rqorder((WRegion
*)grp
, bottom
,
865 mplex_managed_changed(mplex
, MPLEX_CHANGE_SWITCHONLY
, TRUE
, sub
);
870 bool mplex_do_prepare_focus(WMPlex
*mplex
, WStacking
*node
,
871 WStacking
*sub
, int flags
,
872 WPrepareFocusResult
*res
)
874 bool ew
=(flags
®ION_GOTO_ENTERWINDOW
);
875 PtrList
*hidelist
=NULL
;
876 PtrList
**hidelistp
=(ew
? NULL
: &hidelist
);
878 /*bool within=FALSE;*/
880 if(sub
==NULL
&& node
==NULL
)
883 /* Display the node in any case */
884 if(node
!=NULL
&& !ew
)
885 mplex_do_node_display(mplex
, node
, TRUE
);
887 if(!region_prepare_focus((WRegion
*)mplex
, flags
, res
))
890 foc
=mplex_do_to_focus_on(mplex
, node
, sub
, hidelistp
, NULL
/*&within*/);
893 while(hidelist
!=NULL
){
894 WStacking
*st
=(WStacking
*)ptrlist_take_first(&hidelist
);
896 region_unmap(st
->reg
);
899 if(ioncore_g
.autoraise
&&
900 !(flags
®ION_GOTO_ENTERWINDOW
) &&
901 foc
->level
>STACKING_LEVEL_BOTTOM
){
902 WStacking
**stackingp
=mplex_get_stackingp(mplex
);
903 stacking_restack(stackingp
, foc
, None
, NULL
, NULL
, FALSE
);
909 return (foc
==sub
|| (sub
==NULL
&& foc
==node
));
916 bool mplex_managed_prepare_focus(WMPlex
*mplex
, WRegion
*disp
,
917 int flags
, WPrepareFocusResult
*res
)
919 WStacking
*node
=mplex_find_stacking(mplex
, disp
);
924 return mplex_do_prepare_focus(mplex
, node
, NULL
, flags
, res
);
931 /*{{{ Switch exports */
934 static void do_switch(WMPlex
*mplex
, WLListNode
*lnode
)
936 WStacking
*node
=(lnode
!=NULL
? lnode
->st
: NULL
);
939 bool mcf
=region_may_control_focus((WRegion
*)mplex
);
941 mplex_do_node_display(mplex
, node
, TRUE
);
944 mplex_refocus(mplex
, node
, TRUE
);
950 * Have \var{mplex} display the \var{n}:th object managed by it.
953 void mplex_switch_nth(WMPlex
*mplex
, uint n
)
955 do_switch(mplex
, llist_nth_node(mplex
->mx_list
, n
));
960 * Have \var{mplex} display next (wrt. currently selected) object managed
964 void mplex_switch_next(WMPlex
*mplex
)
966 do_switch(mplex
, LIST_NEXT_WRAP(mplex
->mx_list
, mplex
->mx_current
,
972 * Have \var{mplex} display previous (wrt. currently selected) object
976 void mplex_switch_prev(WMPlex
*mplex
)
978 do_switch(mplex
, LIST_PREV_WRAP(mplex
->mx_list
, mplex
->mx_current
,
983 * Have \var{mplex} display the given child window already added to the mplex
986 void mplex_switch_to(WMPlex
*mplex
, WRegion
*reg
)
988 WPrepareFocusResult result
;
989 mplex_managed_prepare_focus(mplex
, reg
->manager
, 0, &result
);
992 bool mplex_set_hidden(WMPlex
*mplex
, WRegion
*reg
, int sp
)
994 bool mcf
=region_may_control_focus((WRegion
*)mplex
);
995 WStacking
*node
=mplex_find_stacking(mplex
, reg
);
996 bool hidden
, nhidden
;
1001 hidden
=STACKING_IS_HIDDEN(node
);
1002 nhidden
=libtu_do_setparam(sp
, hidden
);
1004 if(!hidden
&& nhidden
){
1007 if(REGION_IS_MAPPED(mplex
) && !MPLEX_MGD_UNVIEWABLE(mplex
))
1010 /* lnode -> switch next? */
1011 }else if(hidden
&& !nhidden
){
1012 mplex_do_node_display(mplex
, node
, TRUE
);
1015 if(mcf
&& !PASSIVE(node
))
1016 mplex_refocus(mplex
, (nhidden
? NULL
: node
), TRUE
);
1018 return STACKING_IS_HIDDEN(node
);
1023 * Set the visibility of the region \var{reg} on \var{mplex}
1024 * as specified with the parameter \var{how}
1025 * (one of \codestr{set}, \codestr{unset}, or \codestr{toggle}).
1026 * The resulting state is returned.
1028 EXTL_EXPORT_AS(WMPlex
, set_hidden
)
1029 bool mplex_set_hidden_extl(WMPlex
*mplex
, WRegion
*reg
, const char *how
)
1031 return mplex_set_hidden(mplex
, reg
, libtu_string_to_setparam(how
));
1036 * Is \var{reg} on within \var{mplex} and hidden?
1040 bool mplex_is_hidden(WMPlex
*mplex
, WRegion
*reg
)
1042 WStacking
*node
=mplex_find_stacking(mplex
, reg
);
1044 return (node
!=NULL
&& STACKING_IS_HIDDEN(node
));
1054 static WStacking
*mplex_nxt(WMPlex
*mplex
, WStacking
*st
, bool wrap
)
1056 return (st
->mgr_next
!=NULL
1058 : (wrap
? mplex
->mgd
: NULL
));
1062 static WStacking
*mplex_prv(WMPlex
*mplex
, WStacking
*st
, bool wrap
)
1064 return (st
!=mplex
->mgd
1066 : (wrap
? st
->mgr_prev
: NULL
));
1070 typedef WStacking
*NxtFn(WMPlex
*mplex
, WStacking
*st
, bool wrap
);
1073 static WRegion
*do_navi(WMPlex
*mplex
, WStacking
*sti
,
1074 NxtFn
*fn
, WRegionNaviData
*data
,
1075 bool sti_ok
, bool wrap
)
1077 WStacking
*st
, *stacking
;
1080 stacking
=mplex_get_stacking(mplex
);
1083 min_level
=stacking_min_level_mapped(stacking
);
1087 st
=fn(mplex
, st
, wrap
);
1089 if(st
==NULL
|| (st
==sti
&& !sti_ok
))
1093 if(OBJ_IS(st
->reg
, WGroup
)){
1094 /* WGroup navigation code should respect modal stuff. */
1095 WRegion
*res
=region_navi_cont((WRegion
*)mplex
, st
->reg
, data
);
1096 if(res
!=NULL
&& res
!=st
->reg
)
1099 if(st
->level
>=min_level
&& !PASSIVE(st
))
1100 return region_navi_cont((WRegion
*)mplex
, st
->reg
, data
);
1112 WRegion
*mplex_navi_first(WMPlex
*mplex
, WRegionNavi nh
,
1113 WRegionNaviData
*data
)
1115 WStacking
*lst
=mplex
->mgd
;
1119 if(nh
==REGION_NAVI_ANY
){
1123 if(nh
==REGION_NAVI_ANY
|| nh
==REGION_NAVI_END
||
1124 nh
==REGION_NAVI_BOTTOM
|| nh
==REGION_NAVI_RIGHT
){
1125 res
=do_navi(mplex
, lst
, mplex_prv
, data
, TRUE
, TRUE
);
1127 res
=do_navi(mplex
, lst
->mgr_prev
, mplex_nxt
, data
, TRUE
, TRUE
);
1131 return region_navi_cont((WRegion
*)mplex
, res
, data
);
1135 WRegion
*mplex_navi_next(WMPlex
*mplex
, WRegion
*rel
, WRegionNavi nh
,
1136 WRegionNaviData
*data
)
1142 st
=mplex_find_stacking(mplex
, rel
);
1145 }else if(mplex
->mx_current
!=NULL
){
1146 st
=mplex
->mx_current
->st
;
1148 return mplex_navi_first(mplex
, nh
, data
);
1151 if(nh
==REGION_NAVI_ANY
){
1155 if(nh
==REGION_NAVI_ANY
|| nh
==REGION_NAVI_END
||
1156 nh
==REGION_NAVI_BOTTOM
|| nh
==REGION_NAVI_RIGHT
){
1157 res
=do_navi(mplex
, st
, mplex_nxt
, data
, FALSE
, FALSE
);
1159 res
=do_navi(mplex
, st
, mplex_prv
, data
, FALSE
, FALSE
);
1162 return region_navi_cont((WRegion
*)mplex
, res
, data
);
1172 bool mplex_managed_rqorder(WMPlex
*mplex
, WRegion
*reg
, WRegionOrder order
)
1174 WStacking
**stackingp
=mplex_get_stackingp(mplex
);
1177 if(stackingp
==NULL
|| *stackingp
==NULL
)
1180 st
=mplex_find_stacking(mplex
, reg
);
1185 stacking_restack(stackingp
, st
, None
, NULL
, NULL
,
1186 (order
!=REGION_ORDER_FRONT
));
1198 static bool mplex_stack(WMPlex
*mplex
, WStacking
*st
)
1200 WStacking
*tmp
=NULL
;
1201 WStacking
**stackingp
=mplex_get_stackingp(mplex
);
1206 LINK_ITEM_FIRST(tmp
, st
, next
, prev
);
1207 stacking_weave(stackingp
, &tmp
, FALSE
);
1214 static void mplex_unstack(WMPlex
*mplex
, WStacking
*st
)
1216 /*WStacking *stacking;*/
1218 /*stacking=mplex_get_stacking(mplex);*/
1220 stacking_unstack(&mplex
->win
, st
);
1224 /* WMPlexWPHolder is used for position marking in order to allow
1225 * WLListNodes be safely removed in the attach handler hnd, that
1226 * could remove something this mplex is managing.
1228 bool mplex_do_attach_final(WMPlex
*mplex
, WRegion
*reg
, WMPlexPHolder
*ph
)
1230 WStacking
*node
=NULL
;
1231 WLListNode
*lnode
=NULL
;
1232 WMPlexAttachParams
*param
=&ph
->param
;
1233 bool mx_was_empty
, sw
, mcf
, hidden
;
1237 mcf
=region_may_control_focus((WRegion
*)mplex
);
1239 mx_was_empty
=(mplex
->mx_list
==NULL
);
1241 szplcy
=((param
->flags
&MPLEX_ATTACH_SIZEPOLICY
&&
1242 param
->szplcy
!=SIZEPOLICY_DEFAULT
)
1244 : (param
->flags
&MPLEX_ATTACH_UNNUMBERED
1245 ? SIZEPOLICY_FULL_BOUNDS
1246 : SIZEPOLICY_FULL_EXACT
));
1248 level
=(param
->flags
&MPLEX_ATTACH_LEVEL
1250 : (param
->flags
&MPLEX_ATTACH_UNNUMBERED
1251 ? STACKING_LEVEL_NORMAL
1252 : STACKING_LEVEL_BOTTOM
));
1254 hidden
=(param
->flags
&MPLEX_ATTACH_HIDDEN
1255 && (param
->flags
&MPLEX_ATTACH_UNNUMBERED
1258 sw
=(!hidden
&& (param
->flags
&MPLEX_ATTACH_SWITCHTO
1259 || (param
->flags
&MPLEX_ATTACH_UNNUMBERED
1261 : (mplex_current_node(mplex
)==NULL
))));
1263 hidden
=(hidden
|| (!sw
&& !(param
->flags
&MPLEX_ATTACH_UNNUMBERED
)));
1265 node
=create_stacking();
1270 if(!(param
->flags
&MPLEX_ATTACH_UNNUMBERED
)){
1271 lnode
=ALLOC(WLListNode
);
1273 stacking_free(node
);
1283 if(!stacking_assoc(node
, reg
)){
1288 stacking_free(node
);
1293 node
->szplcy
=szplcy
;
1295 node
->pseudomodal
=(param
->flags
&MPLEX_ATTACH_PSEUDOMODAL
? 1 : 0);
1298 WMPlexPHolder
*ph2
, *phn
, *php
;
1300 llist_link_after(&(mplex
->mx_list
),
1301 (ph
!=NULL
? ph
->after
: NULL
),
1306 /* Move placeholders after new node */
1307 for(php
=NULL
, ph2
=ph
; ph2
!=NULL
; php
=ph2
, ph2
=phn
){
1309 mplexpholder_move(ph2
, mplex
, php
, lnode
);
1313 LINK_ITEM(mplex
->mgd
, node
, mgr_next
, mgr_prev
);
1315 if(!OBJ_IS(reg
, WGroup
))
1316 mplex_stack(mplex
, node
);
1318 region_set_manager(reg
, (WRegion
*)mplex
);
1320 if(param
->flags
&MPLEX_ATTACH_PASSIVE
)
1321 reg
->flags
|=REGION_SKIP_FOCUS
;
1323 if(!(param
->flags
&MPLEX_ATTACH_WHATEVER
)){
1327 mplex_managed_geom(mplex
, &(fp
.g
));
1329 sizepolicy(&node
->szplcy
, reg
,
1330 (param
->flags
&MPLEX_ATTACH_GEOM
? &(param
->geom
) : NULL
),
1333 if(rectangle_compare(&fp
.g
, ®ION_GEOM(reg
))!=RECTANGLE_SAME
)
1334 region_fitrep(reg
, NULL
, &fp
);
1338 mplex_do_node_display(mplex
, node
, FALSE
);
1344 mplex_refocus(mplex
, node
, FALSE
);
1346 (level
>=STACKING_LEVEL_MODAL1
|| OBJ_IS(reg
, WGroup
))){
1347 /* New modal regions may require focusing, so try to
1348 * give focus back to currently active object.
1349 * (There seems to be some problem with uncontained
1350 * client windows still..)
1352 mplex_refocus(mplex
, NULL
, FALSE
);
1354 region_pointer_focus_hack(reg
);
1357 region_pointer_focus_hack(reg
);
1361 mplex_managed_changed(mplex
, MPLEX_CHANGE_ADD
, sw
, reg
);
1367 static void mplex_attach_fp(WMPlex
*mplex
, const WMPlexAttachParams
*param
,
1370 if(param
->flags
&MPLEX_ATTACH_GEOM
)
1373 mplex_managed_geom(mplex
, &(fp
->g
));
1375 fp
->mode
=REGION_FIT_WHATEVER
|REGION_FIT_BOUNDS
;
1379 WRegion
*mplex_do_attach_pholder(WMPlex
*mplex
, WMPlexPHolder
*ph
,
1380 WRegionAttachData
*data
)
1384 mplex_attach_fp(mplex
, &ph
->param
, &fp
);
1386 return region_attach_helper((WRegion
*)mplex
,
1387 (WWindow
*)mplex
, &fp
,
1388 (WRegionDoAttachFn
*)mplex_do_attach_final
,
1393 WRegion
*mplex_do_attach(WMPlex
*mplex
, WMPlexAttachParams
*param
,
1394 WRegionAttachData
*data
)
1399 ph
=create_mplexpholder(mplex
, NULL
, param
);
1404 reg
=mplex_do_attach_pholder(mplex
, ph
, data
);
1406 destroy_obj((Obj
*)ph
);
1412 WRegion
*mplex_do_attach_new(WMPlex
*mplex
, WMPlexAttachParams
*param
,
1413 WRegionCreateFn
*fn
, void *fn_param
)
1415 WRegionAttachData data
;
1417 data
.type
=REGION_ATTACH_NEW
;
1419 data
.u
.n
.param
=fn_param
;
1421 return mplex_do_attach(mplex
, param
, &data
);
1425 #define MPLEX_ATTACH_SET_FLAGS (MPLEX_ATTACH_GEOM| \
1426 MPLEX_ATTACH_SIZEPOLICY| \
1430 WRegion
*mplex_attach_simple(WMPlex
*mplex
, WRegion
*reg
, int flags
)
1432 WMPlexAttachParams param
;
1433 WRegionAttachData data
;
1435 param
.flags
=flags
&~MPLEX_ATTACH_SET_FLAGS
;
1437 data
.type
=REGION_ATTACH_REPARENT
;
1440 return mplex_do_attach(mplex
, ¶m
, &data
);
1444 static void get_params(WMPlex
*mplex
, ExtlTab tab
, int mask
,
1445 WMPlexAttachParams
*par
)
1450 if(ok
&MPLEX_ATTACH_LEVEL
){
1451 if(extl_table_gets_i(tab
, "level", &tmp
)){
1453 par
->flags
|=MPLEX_ATTACH_LEVEL
;
1458 if(extl_table_is_bool_set(tab
, "modal"))
1459 par
->level
=maxof(par
->level
, STACKING_LEVEL_MODAL1
);
1462 if(extl_table_is_bool_set(tab
, "unnumbered"))
1463 par
->flags
|=MPLEX_ATTACH_UNNUMBERED
&ok
;
1465 if(extl_table_is_bool_set(tab
, "switchto"))
1466 par
->flags
|=MPLEX_ATTACH_SWITCHTO
&ok
;
1468 if(extl_table_is_bool_set(tab
, "hidden"))
1469 par
->flags
|=MPLEX_ATTACH_HIDDEN
&ok
;
1471 if(extl_table_is_bool_set(tab
, "passive"))
1472 par
->flags
|=MPLEX_ATTACH_PASSIVE
&ok
;
1474 if(extl_table_is_bool_set(tab
, "pseudomodal"))
1475 par
->flags
|=MPLEX_ATTACH_PSEUDOMODAL
&ok
;
1477 if(extl_table_gets_i(tab
, "index", &(par
->index
)))
1478 par
->flags
|=MPLEX_ATTACH_INDEX
&ok
;
1480 if(ok
&MPLEX_ATTACH_SIZEPOLICY
){
1481 if(extl_table_gets_sizepolicy(tab
, "sizepolicy", &par
->szplcy
)){
1482 par
->flags
|=MPLEX_ATTACH_SIZEPOLICY
;
1483 }else if(extl_table_gets_i(tab
, "sizepolicy", &tmp
)){
1484 /* Backwards compat. numeric version */
1485 par
->flags
|=MPLEX_ATTACH_SIZEPOLICY
;
1490 if(extl_table_gets_rectangle(tab
, "geom", &par
->geom
))
1491 par
->flags
|=MPLEX_ATTACH_GEOM
&ok
;
1496 * Attach and reparent existing region \var{reg} to \var{mplex}.
1497 * The table \var{param} may contain the fields \var{index} and
1498 * \var{switchto} that are interpreted as for \fnref{WMPlex.attach_new}.
1501 WRegion
*mplex_attach(WMPlex
*mplex
, WRegion
*reg
, ExtlTab param
)
1503 WMPlexAttachParams par
=MPLEXATTACHPARAMS_INIT
;
1504 WRegionAttachData data
;
1509 get_params(mplex
, param
, 0, &par
);
1511 data
.type
=REGION_ATTACH_REPARENT
;
1514 return mplex_do_attach(mplex
, &par
, &data
);
1518 WRegion
*mplex_attach_new_(WMPlex
*mplex
, WMPlexAttachParams
*par
,
1519 int mask
, ExtlTab param
)
1521 WRegionAttachData data
;
1523 get_params(mplex
, param
, mask
, par
);
1525 data
.type
=REGION_ATTACH_LOAD
;
1528 return mplex_do_attach(mplex
, par
, &data
);
1533 * Create a new region to be managed by \var{mplex}. At least the following
1534 * fields in \var{param} are understood (all but \var{type} are optional).
1536 * \begin{tabularx}{\linewidth}{lX}
1537 * \tabhead{Field & Description}
1538 * \var{type} & (string) Class name (a string) of the object to be created. \\
1539 * \var{name} & (string) Name of the object to be created (a string). \\
1540 * \var{switchto} & (boolean) Should the region be switched to (boolean)? \\
1541 * \var{unnumbered} & (boolean) Do not put on the numbered mutually
1542 * exclusive list. \\
1543 * \var{index} & (integer) Index on this list, same as for
1544 * \fnref{WMPlex.set_index}. \\
1545 * \var{level} & (integer) Stacking level. \\
1546 * \var{modal} & (boolean) Shortcut for modal stacking level. \\
1547 * \var{hidden} & (boolean) Attach hidden, if not prevented
1548 * by e.g. the mutually exclusive list being empty.
1549 * This option overrides \var{switchto}. \\
1550 * \var{passive} & (boolean) Skip in certain focusing operations. \\
1551 * \var{pseudomodal} & (boolean) The attached region is ``pseudomodal''
1552 * if the stacking level dictates it to be modal.
1553 * This means that the region may be hidden to display
1554 * regions with lesser stacking levels. \\
1555 * \var{sizepolicy} & (string) Size policy; see Section \ref{sec:sizepolicies}. \\
1556 * \var{geom} & (table) Geometry specification. \\
1559 * In addition parameters to the region to be created are passed in this
1563 WRegion
*mplex_attach_new(WMPlex
*mplex
, ExtlTab param
)
1565 WMPlexAttachParams par
=MPLEXATTACHPARAMS_INIT
;
1567 return mplex_attach_new_(mplex
, &par
, 0, param
);
1571 static bool mplex_handle_drop(WMPlex
*mplex
, int x
, int y
,
1574 WRegion
*curr
=mplex_mx_current(mplex
);
1576 /* This code should handle dropping tabs on floating workspaces. */
1577 if(curr
&& HAS_DYN(curr
, region_handle_drop
)){
1579 region_rootpos(curr
, &rx
, &ry
);
1580 if(rectangle_contains(®ION_GEOM(curr
), x
-rx
, y
-ry
)){
1581 if(region_handle_drop(curr
, x
, y
, dropped
))
1586 return (NULL
!=mplex_attach_simple(mplex
, dropped
, MPLEX_ATTACH_SWITCHTO
));
1590 WPHolder
*mplex_prepare_manage(WMPlex
*mplex
, const WClientWin
*cwin
,
1591 const WManageParams
*param
, int priority
)
1593 int cpriority
=MANAGE_PRIORITY_SUB(priority
, MANAGE_PRIORITY_NORMAL
);
1594 WMPlexAttachParams ap
;
1598 /* Check current */ {
1599 WStacking
*cur
=mplex_current_node(mplex
);
1602 ph
=region_prepare_manage(cur
->reg
, cwin
, param
, cpriority
);
1607 if(mplex
->mx_current
!=NULL
&& mplex
->mx_current
->st
!=cur
){
1608 ph
=region_prepare_manage(mplex
->mx_current
->st
->reg
,
1609 cwin
, param
, cpriority
);
1615 if(!MANAGE_PRIORITY_OK(priority
, MANAGE_PRIORITY_NORMAL
))
1618 ap
.flags
=((param
->switchto
? MPLEX_ATTACH_SWITCHTO
: 0)
1619 |MPLEX_ATTACH_SIZEPOLICY
);
1620 ap
.szplcy
=SIZEPOLICY_FULL_EXACT
;
1622 mph
=create_mplexpholder(mplex
, NULL
, &ap
);
1626 WGroupAttachParams gp
=GROUPATTACHPARAMS_INIT
;
1632 gph
=create_grouppholder(NULL
, NULL
, &gp
);
1635 gph
->recreate_pholder
=(WPHolder
*)mph
;
1636 return (WPHolder
*)gph
;
1640 return (WPHolder
*)mph
;
1650 void mplex_managed_remove(WMPlex
*mplex
, WRegion
*sub
)
1652 bool mx
=FALSE
, hadfocus
=FALSE
, mcf
;
1653 WRegion
*stdisp
=(WRegion
*)(mplex
->stdispwatch
.obj
);
1654 WStacking
*node
, *next
=NULL
;
1656 mcf
=region_may_control_focus((WRegion
*)mplex
);
1659 if(CAN_MANAGE_STDISP(sub
) &&
1660 region_managed_within((WRegion
*)mplex
, stdisp
)==sub
){
1661 region_unmanage_stdisp(sub
, TRUE
, TRUE
);
1662 region_detach_manager(stdisp
);
1666 node
=mplex_find_stacking(mplex
, sub
);
1671 hadfocus
=(mplex_current_node(mplex
)==node
);
1673 if(node
->lnode
!=NULL
){
1674 if(mplex
->mx_current
==node
->lnode
){
1677 mplex
->mx_current
=NULL
;
1678 lnext
=LIST_PREV(mplex
->mx_list
, node
->lnode
, next
, prev
);
1680 lnext
=LIST_NEXT(mplex
->mx_list
, node
->lnode
, next
, prev
);
1681 if(lnext
==node
->lnode
)
1688 mplex_move_phs_before(mplex
, node
->lnode
);
1689 llist_unlink(&(mplex
->mx_list
), node
->lnode
);
1697 UNLINK_ITEM(mplex
->mgd
, node
, mgr_next
, mgr_prev
);
1699 mplex_unstack(mplex
, node
);
1701 stacking_unassoc(node
);
1702 stacking_free(node
);
1704 region_unset_manager(sub
, (WRegion
*)mplex
);
1706 if(OBJ_IS_BEING_DESTROYED(mplex
))
1710 mplex_do_node_display(mplex
, next
, FALSE
);
1713 mplex_refocus(mplex
, next
, FALSE
);
1716 mplex_managed_changed(mplex
, MPLEX_CHANGE_REMOVE
, next
!=NULL
, sub
);
1720 void mplex_child_removed(WMPlex
*mplex
, WRegion
*sub
)
1722 if(sub
!=NULL
&& sub
==(WRegion
*)(mplex
->stdispwatch
.obj
)){
1723 watch_reset(&(mplex
->stdispwatch
));
1724 mplex_set_stdisp(mplex
, NULL
, NULL
);
1735 bool mplex_rescue_clientwins(WMPlex
*mplex
, WRescueInfo
*info
)
1740 WLListNode
*lnode
, *was_current
=mplex
->mx_current
;
1743 /* First all mx stuff to move them nicely to another mplex (when that
1744 * is the case), switching to the current region in the target if
1745 * allowed by ph_flags_mask region_rescue.
1747 FOR_ALL_NODES_ON_LLIST(lnode
, mplex
->mx_list
, ltmp
){
1748 int sw
=(lnode
==was_current
? PHOLDER_ATTACH_SWITCHTO
: 0);
1749 region_do_rescue_this(lnode
->st
->reg
, info
, sw
);
1752 /* Then the rest (possibly retrying failed mx stuff).
1754 mplex_iter_init(&tmp
, mplex
);
1755 ret1
=region_rescue_some_clientwins((WRegion
*)mplex
, info
,
1756 (WRegionIterator
*)mplex_iter
,
1759 ret2
=region_rescue_child_clientwins((WRegion
*)mplex
, info
);
1761 return (ret1
&& ret2
);
1768 /*{{{ Status display support */
1771 bool mplex_set_stdisp(WMPlex
*mplex
, WRegion
*reg
,
1772 const WMPlexSTDispInfo
*din
)
1774 WRegion
*oldstdisp
=(WRegion
*)(mplex
->stdispwatch
.obj
);
1777 assert(reg
==NULL
|| (reg
==oldstdisp
) ||
1778 (REGION_MANAGER(reg
)==NULL
&&
1779 REGION_PARENT(reg
)==(WWindow
*)mplex
));
1781 if(oldstdisp
!=NULL
){
1782 mgr
=region_managed_within((WRegion
*)mplex
, oldstdisp
);
1784 if(!CAN_MANAGE_STDISP(mgr
))
1789 mplex
->stdispinfo
=*din
;
1792 watch_reset(&(mplex
->stdispwatch
));
1795 region_unmanage_stdisp(mgr
, TRUE
, FALSE
);
1797 region_detach_manager(oldstdisp
);
1800 watch_setup(&(mplex
->stdispwatch
), (Obj
*)reg
, NULL
);
1802 mplex_remanage_stdisp(mplex
);
1805 if(oldstdisp
!=NULL
&& oldstdisp
!=reg
)
1806 mainloop_defer_destroy((Obj
*)oldstdisp
);
1812 void mplex_get_stdisp(WMPlex
*mplex
, WRegion
**reg
, WMPlexSTDispInfo
*di
)
1814 *di
=mplex
->stdispinfo
;
1815 *reg
=(WRegion
*)mplex
->stdispwatch
.obj
;
1819 static StringIntMap pos_map
[]={
1820 {"tl", MPLEX_STDISP_TL
},
1821 {"tr", MPLEX_STDISP_TR
},
1822 {"bl", MPLEX_STDISP_BL
},
1823 {"br", MPLEX_STDISP_BR
},
1828 static bool do_attach_stdisp(WRegion
*UNUSED(mplex
), WRegion
*UNUSED(reg
), void *UNUSED(unused
))
1830 /* We do not actually manage the stdisp. */
1836 * Set/create status display for \var{mplex}. Table is a standard
1837 * description of the object to be created (as passed to e.g.
1838 * \fnref{WMPlex.attach_new}). In addition, the following fields are
1841 * \begin{tabularx}{\linewidth}{lX}
1842 * \tabhead{Field & Description}
1843 * \var{pos} & (string) The corner of the screen to place the status
1844 * display in: one of \codestr{tl}, \codestr{tr}, \codestr{bl}
1845 * or \codestr{br}. \\
1846 * \var{fullsize} & (boolean) Waste all available space. \\
1847 * \var{action} & (string) If this field is set to \codestr{keep},
1848 * \var{pos} and \var{fullsize} are changed for the existing
1849 * status display. If this field is set to \codestr{remove},
1850 * the existing status display is removed. If this
1851 * field is not set or is set to \codestr{replace}, a
1852 * new status display is created and the old, if any,
1856 EXTL_EXPORT_AS(WMPlex
, set_stdisp
)
1857 WRegion
*mplex_set_stdisp_extl(WMPlex
*mplex
, ExtlTab t
)
1859 WRegion
*stdisp
=NULL
;
1860 WMPlexSTDispInfo din
=mplex
->stdispinfo
;
1863 if(extl_table_gets_s(t
, "pos", &s
)){
1864 din
.pos
=stringintmap_value(pos_map
, s
, -1);
1866 warn(TR("Invalid position setting."));
1871 extl_table_gets_b(t
, "fullsize", &(din
.fullsize
));
1874 extl_table_gets_s(t
, "action", &s
);
1876 if(s
==NULL
|| strcmp(s
, "replace")==0){
1877 WRegionAttachData data
;
1882 fp
.g
.w
=REGION_GEOM(mplex
).w
;
1883 fp
.g
.h
=REGION_GEOM(mplex
).h
;
1884 fp
.mode
=REGION_FIT_BOUNDS
|REGION_FIT_WHATEVER
;
1886 /* Full mplex size is stupid so use saved geometry initially
1889 extl_table_gets_rectangle(t
, "geom", &(fp
.g
));
1891 data
.type
=REGION_ATTACH_LOAD
;
1894 stdisp
=region_attach_helper((WRegion
*)mplex
,
1895 (WWindow
*)mplex
, &fp
,
1896 do_attach_stdisp
, NULL
,
1902 }else if(strcmp(s
, "keep")==0){
1903 stdisp
=(WRegion
*)(mplex
->stdispwatch
.obj
);
1904 }else if(strcmp(s
, "remove")!=0){
1905 warn(TR("Invalid action setting."));
1909 if(!mplex_set_stdisp(mplex
, stdisp
, &din
)){
1910 destroy_obj((Obj
*)stdisp
);
1918 static ExtlTab
mplex_do_get_stdisp_extl(WMPlex
*mplex
, bool fullconfig
)
1920 WRegion
*reg
=(WRegion
*)mplex
->stdispwatch
.obj
;
1924 return extl_table_none();
1927 t
=region_get_configuration(reg
);
1928 extl_table_sets_rectangle(t
, "geom", ®ION_GEOM(reg
));
1930 t
=extl_create_table();
1931 extl_table_sets_o(t
, "reg", (Obj
*)reg
);
1934 if(t
!=extl_table_none()){
1935 WMPlexSTDispInfo
*di
=&(mplex
->stdispinfo
);
1936 extl_table_sets_s(t
, "pos", stringintmap_key(pos_map
, di
->pos
, NULL
));
1937 extl_table_sets_b(t
, "fullsize", di
->fullsize
);
1944 * Get status display information. See \fnref{WMPlex.get_stdisp} for
1945 * information on the fields.
1948 EXTL_EXPORT_AS(WMPlex
, get_stdisp
)
1949 ExtlTab
mplex_get_stdisp_extl(WMPlex
*mplex
)
1951 return mplex_do_get_stdisp_extl(mplex
, FALSE
);
1961 void mplex_managed_geom_default(const WMPlex
*mplex
, WRectangle
*geom
)
1965 geom
->w
=REGION_GEOM(mplex
).w
;
1966 geom
->h
=REGION_GEOM(mplex
).h
;
1970 void mplex_managed_geom(const WMPlex
*mplex
, WRectangle
*geom
)
1972 CALL_DYN(mplex_managed_geom
, mplex
, (mplex
, geom
));
1976 void mplex_size_changed(WMPlex
*mplex
, bool wchg
, bool hchg
)
1978 CALL_DYN(mplex_size_changed
, mplex
, (mplex
, wchg
, hchg
));
1982 void mplex_managed_changed(WMPlex
*mplex
, int mode
, bool sw
, WRegion
*mgd
)
1984 CALL_DYN(mplex_managed_changed
, mplex
, (mplex
, mode
, sw
, mgd
));
1988 int mplex_default_index(WMPlex
*mplex
)
1990 int idx
=LLIST_INDEX_LAST
;
1991 CALL_DYN_RET(idx
, int, mplex_default_index
, mplex
, (mplex
));
1996 /* For regions managing stdisps */
1998 void region_manage_stdisp(WRegion
*reg
, WRegion
*stdisp
,
1999 const WMPlexSTDispInfo
*info
)
2001 CALL_DYN(region_manage_stdisp
, reg
, (reg
, stdisp
, info
));
2005 void region_unmanage_stdisp(WRegion
*reg
, bool permanent
, bool nofocus
)
2007 CALL_DYN(region_unmanage_stdisp
, reg
, (reg
, permanent
, nofocus
));
2014 /*{{{ Changed hook helper */
2017 static const char *mode2str(int mode
)
2019 if(mode
==MPLEX_CHANGE_SWITCHONLY
)
2020 return "switchonly";
2021 else if(mode
==MPLEX_CHANGE_REORDER
)
2023 else if(mode
==MPLEX_CHANGE_ADD
)
2025 else if(mode
==MPLEX_CHANGE_REMOVE
)
2031 static bool mrsh_chg(ExtlFn fn
, WMPlexChangedParams
*p
)
2033 ExtlTab t
=extl_create_table();
2036 extl_table_sets_o(t
, "reg", (Obj
*)p
->reg
);
2037 extl_table_sets_s(t
, "mode", mode2str(p
->mode
));
2038 extl_table_sets_b(t
, "sw", p
->sw
);
2039 extl_table_sets_o(t
, "sub", (Obj
*)p
->sub
);
2042 ret
=extl_call(fn
, "t", NULL
, t
);
2043 extl_unprotect(NULL
);
2045 extl_unref_table(t
);
2051 void mplex_call_changed_hook(WMPlex
*mplex
, WHook
*hook
,
2052 int mode
, bool sw
, WRegion
*reg
)
2054 WMPlexChangedParams p
;
2061 hook_call_p(hook
, &p
, (WHookMarshallExtl
*)mrsh_chg
);
2071 static void save_node(WMPlex
*mplex
, ExtlTab subs
, int *n
,
2072 WStacking
*node
, bool unnumbered
)
2076 st
=region_get_configuration(node
->reg
);
2078 if(st
!=extl_table_none()){
2079 if(mplex
->mx_current
!=NULL
&& node
==mplex
->mx_current
->st
)
2080 extl_table_sets_b(st
, "switchto", TRUE
);
2081 extl_table_sets_s(st
, "sizepolicy",
2082 sizepolicy2string(node
->szplcy
));
2083 extl_table_sets_i(st
, "level", node
->level
);
2084 g
=extl_table_from_rectangle(®ION_GEOM(node
->reg
));
2085 extl_table_sets_t(st
, "geom", g
);
2086 extl_unref_table(g
);
2087 if(STACKING_IS_HIDDEN(node
))
2088 extl_table_sets_b(st
, "hidden", TRUE
);
2089 if(STACKING_IS_PSEUDOMODAL(node
))
2090 extl_table_sets_b(st
, "pseudomodal", TRUE
);
2092 extl_table_sets_b(st
, "unnumbered", TRUE
);
2094 extl_table_seti_t(subs
, ++(*n
), st
);
2095 extl_unref_table(st
);
2100 ExtlTab
mplex_get_configuration(WMPlex
*mplex
)
2102 ExtlTab tab
, subs
/*, stdisptab*/;
2109 tab
=region_get_base_configuration((WRegion
*)mplex
);
2111 subs
=extl_create_table();
2112 extl_table_sets_t(tab
, "managed", subs
);
2114 /* First the numbered/mutually exclusive nodes */
2115 FOR_ALL_NODES_ON_LLIST(lnode
, mplex
->mx_list
, ltmp
){
2116 save_node(mplex
, subs
, &n
, lnode
->st
, FALSE
);
2119 FOR_ALL_NODES_IN_MPLEX(mplex
, node
, tmp
){
2120 if(node
->lnode
==NULL
)
2121 save_node(mplex
, subs
, &n
, node
, TRUE
);
2124 extl_unref_table(subs
);
2126 /*stdisptab=mplex_do_get_stdisp_extl(mplex, TRUE);
2127 if(stdisptab!=extl_table_none()){
2128 extl_table_sets_t(tab, "stdisp", stdisptab);
2129 extl_unref_table(stdisptab);
2136 void mplex_load_contents(WMPlex
*mplex
, ExtlTab tab
)
2138 ExtlTab substab
, subtab
;
2141 /*if(extl_table_gets_t(tab, "stdisp", &subtab)){
2142 mplex_set_stdisp_extl(mplex, subtab);
2143 extl_unref_table(subtab);
2146 if(extl_table_gets_t(tab
, "managed", &substab
) ||
2147 extl_table_gets_t(tab
, "subs", &substab
)){
2148 n
=extl_table_get_n(substab
);
2149 for(i
=1; i
<=n
; i
++){
2150 if(extl_table_geti_t(substab
, i
, &subtab
)){
2151 WMPlexAttachParams par
=MPLEXATTACHPARAMS_INIT
;
2155 get_params(mplex
, subtab
, 0, &par
);
2156 mplex_attach_fp(mplex
, &par
, &fp
);
2158 par
.flags
|=MPLEX_ATTACH_INDEX
;
2159 par
.index
=LLIST_INDEX_LAST
;
2161 ph
=(WPHolder
*)create_mplexpholder(mplex
, NULL
, &par
);
2164 region_attach_load_helper((WRegion
*)mplex
, (WWindow
*)mplex
, &fp
,
2165 (WRegionDoAttachFn
*)mplex_do_attach_final
,
2166 (void*)ph
, subtab
, &ph
);
2169 destroy_obj((Obj
*)ph
);
2172 extl_unref_table(subtab
);
2175 extl_unref_table(substab
);
2180 WRegion
*mplex_load(WWindow
*par
, const WFitParams
*fp
, ExtlTab tab
, const char *name
)
2182 WMPlex
*mplex
=create_mplex(par
, fp
, name
);
2184 mplex_load_contents(mplex
, tab
);
2185 return (WRegion
*)mplex
;
2192 /*{{{ Dynfuntab and class info */
2195 static DynFunTab mplex_dynfuntab
[]={
2196 {region_do_set_focus
,
2197 mplex_do_set_focus
},
2199 {region_managed_remove
,
2200 mplex_managed_remove
},
2202 {region_managed_rqgeom
,
2203 mplex_managed_rqgeom
},
2205 {(DynFun
*)region_managed_prepare_focus
,
2206 (DynFun
*)mplex_managed_prepare_focus
},
2208 {(DynFun
*)region_handle_drop
,
2209 (DynFun
*)mplex_handle_drop
},
2211 {region_map
, mplex_map
},
2212 {region_unmap
, mplex_unmap
},
2214 {(DynFun
*)region_prepare_manage
,
2215 (DynFun
*)mplex_prepare_manage
},
2217 {(DynFun
*)region_current
,
2218 (DynFun
*)mplex_current
},
2220 {(DynFun
*)region_rescue_clientwins
,
2221 (DynFun
*)mplex_rescue_clientwins
},
2223 {(DynFun
*)region_get_configuration
,
2224 (DynFun
*)mplex_get_configuration
},
2226 {mplex_managed_geom
,
2227 mplex_managed_geom_default
},
2229 {(DynFun
*)region_fitrep
,
2230 (DynFun
*)mplex_fitrep
},
2232 {region_child_removed
,
2233 mplex_child_removed
},
2235 {(DynFun
*)region_managed_get_pholder
,
2236 (DynFun
*)mplex_managed_get_pholder
},
2238 {(DynFun
*)region_get_rescue_pholder_for
,
2239 (DynFun
*)mplex_get_rescue_pholder_for
},
2241 {(DynFun
*)region_navi_first
,
2242 (DynFun
*)mplex_navi_first
},
2244 {(DynFun
*)region_navi_next
,
2245 (DynFun
*)mplex_navi_next
},
2247 {(DynFun
*)region_managed_rqorder
,
2248 (DynFun
*)mplex_managed_rqorder
},
2255 IMPLCLASS(WMPlex
, WWindow
, mplex_deinit
, mplex_dynfuntab
);