4 * Copyright (c) Tuomo Valkonen 1999-2007.
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>
30 #include "frame-pointer.h"
35 #include "mplexpholder.h"
38 #include "sizepolicy.h"
42 #include "groupedpholder.h"
45 #define SUBS_MAY_BE_MAPPED(MPLEX) \
46 (REGION_IS_MAPPED(MPLEX) && !MPLEX_MGD_UNVIEWABLE(MPLEX))
48 #define PASSIVE(ST) ((ST)->level<=STACKING_LEVEL_MODAL1 \
50 || (ST)->reg->flags®ION_SKIP_FOCUS))
52 #define CAN_MANAGE_STDISP(REG) HAS_DYN(REG, region_manage_stdisp)
55 /*{{{ Stacking list stuff */
58 WStacking *mplex_get_stacking(WMPlex *mplex)
60 return window_get_stacking(&mplex->win);
64 WStacking **mplex_get_stackingp(WMPlex *mplex)
66 return window_get_stackingp(&mplex->win);
70 void mplex_iter_init(WMPlexIterTmp *tmp, WMPlex *mplex)
72 stacking_iter_mgr_init(tmp, mplex->mgd, NULL, mplex);
76 WRegion *mplex_iter(WMPlexIterTmp *tmp)
78 return stacking_iter_mgr(tmp);
82 WStacking *mplex_iter_nodes(WMPlexIterTmp *tmp)
84 return stacking_iter_mgr_nodes(tmp);
91 /*{{{ Destroy/create mplex */
94 bool mplex_do_init(WMPlex *mplex, WWindow *parent,
95 const WFitParams *fp, Window win)
100 mplex->mx_current=NULL;
106 watch_init(&(mplex->stdispwatch));
107 mplex->stdispinfo.pos=MPLEX_STDISP_BL;
108 mplex->stdispinfo.fullsize=FALSE;
110 if(!window_do_init((WWindow*)mplex, parent, fp, win))
113 mplex->win.region.flags|=REGION_BINDINGS_ARE_GRABBED;
115 window_select_input(&(mplex->win), IONCORE_EVENTMASK_CWINMGR);
117 region_register((WRegion*)mplex);
119 /* Call this to set MPLEX_MANAGED_UNVIEWABLE if necessary. */
120 mplex_fit_managed(mplex);
126 bool mplex_init(WMPlex *mplex, WWindow *parent, const WFitParams *fp)
128 return mplex_do_init(mplex, parent, fp, None);
132 WMPlex *create_mplex(WWindow *parent, const WFitParams *fp)
134 CREATEOBJ_IMPL(WMPlex, mplex, (p, parent, fp));
138 void mplex_deinit(WMPlex *mplex)
143 FOR_ALL_MANAGED_BY_MPLEX(mplex, reg, tmp){
144 destroy_obj((Obj*)reg);
147 assert(mplex->mgd==NULL);
148 assert(mplex->mx_list==NULL);
150 while(mplex->mx_phs!=NULL){
151 assert(mplexpholder_move(mplex->mx_phs, NULL, NULL, NULL));
154 window_deinit((WWindow*)mplex);
161 /*{{{ Node lookup etc. */
164 WStacking *mplex_find_stacking(WMPlex *mplex, WRegion *reg)
168 /* Some routines that call us expect us to this check. */
169 if(reg==NULL || REGION_MANAGER(reg)!=(WRegion*)mplex)
172 st=ioncore_find_stacking(reg);
174 assert(st==NULL || st->mgr_prev!=NULL);
180 WStacking *mplex_current_node(WMPlex *mplex)
185 reg=REGION_ACTIVE_SUB(mplex);
186 reg=region_managed_within((WRegion*)mplex, reg);
188 st=mplex_find_stacking(mplex, reg);
193 return (mplex->mx_current!=NULL ? mplex->mx_current->st : NULL);
197 WRegion *mplex_current(WMPlex *mplex)
199 WStacking *node=mplex_current_node(mplex);
200 return (node==NULL ? NULL : node->reg);
207 /*{{{ Exclusive list management and exports */
210 * Returns the number of objects on the mutually exclusive list of \var{mplex}.
214 int mplex_mx_count(WMPlex *mplex)
216 return mplex->mx_count;
221 * Returns the managed object currently active within the mutually exclusive
222 * list of \var{mplex}.
226 WRegion *mplex_mx_current(WMPlex *mplex)
228 WLListNode *lnode=mplex->mx_current;
229 return (lnode==NULL ? NULL : lnode->st->reg);
234 * Returns the \var{n}:th object on the mutually exclusive
235 * list of \var{mplex}.
239 WRegion *mplex_mx_nth(WMPlex *mplex, uint n)
241 WLListNode *lnode=llist_nth_node(mplex->mx_list, n);
242 return (lnode==NULL ? NULL : lnode->st->reg);
247 * Iterate over numbered/mutually exclusive region list of \var{mplex}
248 * until \var{iterfn} returns \code{false}.
249 * The function itself returns \code{true} if it reaches the end of list
250 * without this happening.
254 bool mplex_mx_i(WMPlex *mplex, ExtlFn iterfn)
257 llist_iter_init(&tmp, mplex->mx_list);
259 return extl_iter_objlist_(iterfn, (ObjIterator*)llist_iter_regions, &tmp);
264 * Iterate over managed regions of \var{mplex} until \var{iterfn} returns
266 * The function itself returns \code{true} if it reaches the end of list
267 * without this happening.
271 bool mplex_managed_i(WMPlex *mplex, ExtlFn iterfn)
274 mplex_iter_init(&tmp, mplex);
276 return extl_iter_objlist_(iterfn, (ObjIterator*)mplex_iter, &tmp);
281 * Set index of \var{reg} to \var{index} within the mutually exclusive
282 * list of \var{mplex}. Special values for \var{index} are:
283 * \begin{tabularx}{\linewidth}{lX}
285 * $-2$ & After \fnref{WMPlex.mx_current}. \\
289 void mplex_set_index(WMPlex *mplex, WRegion *reg, int index)
291 WLListNode *lnode, *after;
294 node=mplex_find_stacking(mplex, reg);
302 lnode=ALLOC(WLListNode);
312 mplex_move_phs_before(mplex, lnode);
313 llist_unlink(&(mplex->mx_list), lnode);
316 after=llist_index_to_after(mplex->mx_list, mplex->mx_current, index);
317 llist_link_after(&(mplex->mx_list), after, lnode);
318 mplex_managed_changed(mplex, MPLEX_CHANGE_REORDER, FALSE, reg);
323 * Get index of \var{reg} on the mutually exclusive list of \var{mplex}.
324 * The indices begin from zero.. If \var{reg} is not on the list,
329 int mplex_get_index(WMPlex *mplex, WRegion *reg)
335 FOR_ALL_NODES_ON_LLIST(lnode, mplex->mx_list, tmp){
336 if(reg==lnode->st->reg)
346 * Move \var{r} ``right'' within objects managed by \var{mplex} on list 1.
349 void mplex_inc_index(WMPlex *mplex, WRegion *r)
352 r=mplex_mx_current(mplex);
354 mplex_set_index(mplex, r, mplex_get_index(mplex, r)+1);
359 * Move \var{r} ``left'' within objects managed by \var{mplex} on list 1.
362 void mplex_dec_index(WMPlex *mplex, WRegion *r)
365 r=mplex_mx_current(mplex);
367 mplex_set_index(mplex, r, mplex_get_index(mplex, r)-1);
377 static void mplex_map_mgd(WMPlex *mplex)
382 FOR_ALL_NODES_IN_MPLEX(mplex, node, tmp){
383 if(!STACKING_IS_HIDDEN(node))
384 region_map(node->reg);
389 static void mplex_unmap_mgd(WMPlex *mplex)
394 FOR_ALL_NODES_IN_MPLEX(mplex, node, tmp){
395 if(!STACKING_IS_HIDDEN(node))
396 region_unmap(node->reg);
402 void mplex_map(WMPlex *mplex)
404 window_map((WWindow*)mplex);
405 /* A lame requirement of the ICCCM is that client windows should be
406 * unmapped if the parent is unmapped.
408 if(!MPLEX_MGD_UNVIEWABLE(mplex))
409 mplex_map_mgd(mplex);
413 void mplex_unmap(WMPlex *mplex)
415 window_unmap((WWindow*)mplex);
416 /* A lame requirement of the ICCCM is that client windows should be
417 * unmapped if the parent is unmapped.
419 if(!MPLEX_MGD_UNVIEWABLE(mplex))
420 mplex_unmap_mgd(mplex);
427 /*{{{ Resize and reparent */
430 bool mplex_fitrep(WMPlex *mplex, WWindow *par, const WFitParams *fp)
432 bool wchg=(REGION_GEOM(mplex).w!=fp->g.w);
433 bool hchg=(REGION_GEOM(mplex).h!=fp->g.h);
435 window_do_fitrep(&(mplex->win), par, &(fp->g));
438 mplex_fit_managed(mplex);
439 mplex_size_changed(mplex, wchg, hchg);
446 void mplex_do_fit_managed(WMPlex *mplex, WFitParams *fp)
453 if(!MPLEX_MGD_UNVIEWABLE(mplex) && (fp->g.w<=1 || fp->g.h<=1)){
454 mplex->flags|=MPLEX_MANAGED_UNVIEWABLE;
455 if(REGION_IS_MAPPED(mplex))
456 mplex_unmap_mgd(mplex);
457 }else if(MPLEX_MGD_UNVIEWABLE(mplex) && !(fp->g.w<=1 || fp->g.h<=1)){
458 mplex->flags&=~MPLEX_MANAGED_UNVIEWABLE;
459 if(REGION_IS_MAPPED(mplex))
460 mplex_map_mgd(mplex);
463 if(!MPLEX_MGD_UNVIEWABLE(mplex)){
464 FOR_ALL_NODES_IN_MPLEX(mplex, node, tmp){
466 sizepolicy(&node->szplcy, node->reg, NULL, 0, &fp2);
467 region_fitrep(node->reg, NULL, &fp2);
473 void mplex_fit_managed(WMPlex *mplex)
477 fp.mode=REGION_FIT_EXACT;
478 mplex_managed_geom(mplex, &(fp.g));
480 mplex_do_fit_managed(mplex, &fp);
484 static void mplex_managed_rqgeom(WMPlex *mplex, WRegion *sub,
485 const WRQGeomParams *rq,
492 node=mplex_find_stacking(mplex, sub);
497 mplex_managed_geom(mplex, &fp.g);
499 sizepolicy(&node->szplcy, sub, &rq->geom, rq->flags, &fp);
504 if(!(rq->flags®ION_RQGEOM_TRYONLY))
505 region_fitrep(sub, NULL, &fp);
509 void mplex_set_szplcy(WMPlex *mplex, WRegion *sub, WSizePolicy szplcy)
513 node=mplex_find_stacking(mplex, sub);
520 WSizePolicy mplex_get_szplcy(WMPlex *mplex, WRegion *sub)
524 node=mplex_find_stacking(mplex, sub);
526 return (node==NULL ? SIZEPOLICY_DEFAULT : node->szplcy);
545 static WRegion *manager_within(WMPlex *mplex, WStacking *st)
547 return region_managed_within((WRegion*)mplex, st->reg);
551 static WStacking *stacking_within(WMPlex *mplex, WStacking *st)
553 WRegion *reg=manager_within(mplex, st);
559 : ioncore_find_stacking(reg)));
563 /* Mutually exclusive regions can't be pseudomodal */
564 #define IS_PSEUDOMODAL(ST) ((ST)->lnode==NULL && (ST)->pseudomodal)
567 static bool mapped_pseudomodal_include_filt(WStacking *st, void *data_)
569 FiltData *data=(FiltData*)data_;
572 if(st->reg==NULL || !REGION_IS_MAPPED(st->reg))
576 || (data->to_try==NULL && data->group_st==NULL)
577 || st->level<STACKING_LEVEL_MODAL1){
581 /* Ok, modal node in the way. Let's see if it is pseudomodal
585 stw=stacking_within(data->mplex, st);
587 /* This should not happen */
588 if(stw==NULL || stw->reg==NULL)
591 /* The node is within the same group, so it can not be hidden.
592 * Latter case should not happen.
594 if(stw==data->group_st || stw==data->to_try)
597 if(IS_PSEUDOMODAL(stw)){
598 /* Don't insert multiple times. */
599 return !ptrlist_reinsert_first(data->hidelist, stw);
606 static bool mgr_pseudomodal_approve_filt(WStacking *st, void *data_)
608 FiltData *data=(FiltData*)data_;
610 return (data->group_st==NULL || st==data->group_st ||
611 manager_within(data->mplex, st)==data->group_st->reg);
615 WStacking *mplex_find_to_focus(WMPlex *mplex,
620 WStackingFilter *fi=mapped_pseudomodal_include_filt;
621 WStackingFilter *fa=mgr_pseudomodal_approve_filt;
622 WStacking *stacking=mplex_get_stacking(mplex);
629 if(to_try!=NULL && (to_try->reg==NULL || !REGION_IS_MAPPED(to_try->reg)))
634 data.group_st=group_st;
635 data.hidelist=hidelist;
637 st=stacking_find_to_focus(stacking, to_try, fi, fa, &data);
639 if(st==NULL && hidelist!=NULL)
640 ptrlist_clear(hidelist);
646 static WStacking *mplex_do_to_focus(WMPlex *mplex, WStacking *to_try,
649 return mplex_find_to_focus(mplex, to_try, NULL, hidelist);
653 static WStacking *mplex_do_to_focus_on(WMPlex *mplex, WStacking *node,
655 PtrList **hidelist, bool *within)
657 WGroup *grp=OBJ_CAST(node->reg, WGroup);
662 to_try=grp->current_managed;
663 /* Only will return stuff within 'node' */
664 st=mplex_find_to_focus(mplex, to_try, node, hidelist);
672 st=mplex_do_to_focus(mplex, node, hidelist);
674 if(st==node && within!=NULL)
681 static WStacking *maybe_focusable(WRegion *reg)
683 if(reg==NULL || !REGION_IS_MAPPED(reg))
686 return ioncore_find_stacking(reg);
690 static WStacking *has_stacking_within(WMPlex *mplex, WRegion *reg)
692 while(reg!=NULL && REGION_MANAGER(reg)!=(WRegion*)mplex)
693 reg=REGION_MANAGER(reg);
695 return maybe_focusable(reg);
699 static WStacking *mplex_to_focus(WMPlex *mplex)
701 WStacking *to_try=NULL;
705 to_try=maybe_focusable(REGION_ACTIVE_SUB(mplex));
708 /* Search focus history */
709 for(reg=ioncore_g.focus_current; reg!=NULL; reg=reg->active_next){
710 to_try=has_stacking_within(mplex, reg);
716 st=mplex_do_to_focus(mplex, to_try, NULL);
720 : (mplex->mx_current!=NULL
721 ? mplex->mx_current->st
726 void mplex_do_set_focus(WMPlex *mplex, bool warp)
728 if(!MPLEX_MGD_UNVIEWABLE(mplex)){
729 WStacking *st=mplex_to_focus(mplex);
732 region_do_set_focus(st->reg, warp);
737 window_do_set_focus((WWindow*)mplex, warp);
747 static void mplex_do_remanage_stdisp(WMPlex *mplex, WRegion *sub)
749 WRegion *stdisp=(WRegion*)(mplex->stdispwatch.obj);
752 if(sub!=NULL && CAN_MANAGE_STDISP(sub)){
754 WRegion *omgr=REGION_MANAGER(stdisp);
755 if(omgr!=sub && omgr!=NULL){
756 if(CAN_MANAGE_STDISP(omgr))
757 region_unmanage_stdisp(omgr, FALSE, FALSE);
758 region_detach_manager(stdisp);
761 region_manage_stdisp(sub, stdisp,
762 &(mplex->stdispinfo));
764 region_unmanage_stdisp(sub, TRUE, FALSE);
770 void mplex_remanage_stdisp(WMPlex *mplex)
772 mplex_do_remanage_stdisp(mplex, (mplex->mx_current!=NULL
773 ? mplex->mx_current->st->reg
778 static void mplex_do_node_display(WMPlex *mplex, WStacking *node,
781 WRegion *sub=node->reg;
782 WLListNode *mxc=mplex->mx_current;
784 if(!STACKING_IS_HIDDEN(node))
787 if(node->lnode!=NULL && node->lnode!=mxc)
788 mplex_do_remanage_stdisp(mplex, sub);
792 if(SUBS_MAY_BE_MAPPED(mplex))
797 if(node->lnode!=NULL){
799 /* Hide current mx region. We do it after mapping the
800 * new one to avoid flicker.
802 if(REGION_IS_MAPPED(mplex))
803 region_unmap(mxc->st->reg);
804 mxc->st->hidden=TRUE;
807 mplex->mx_current=node->lnode;
810 * Many programs will get upset if the visible, although only
811 * such, client window is not the lowest window in the mplex.
812 * xprop/xwininfo will return the information for the lowest
813 * window. 'netscape -remote' will not work at all if there are
814 * no visible netscape windows.
817 WGroup *grp=(WGroup*)OBJ_CAST(sub, WGroupCW);
819 WRegion *bottom=group_bottom(grp);
821 region_managed_rqorder((WRegion*)grp, bottom,
828 mplex_managed_changed(mplex, MPLEX_CHANGE_SWITCHONLY, TRUE, sub);
833 static bool mplex_refocus(WMPlex *mplex, WStacking *node, bool warp)
839 foc=mplex_do_to_focus_on(mplex, node, NULL, NULL, &within);
841 if(foc==NULL || !within)
842 foc=mplex_to_focus(mplex);
845 region_maybewarp(foc->reg, warp);
851 bool mplex_do_prepare_focus(WMPlex *mplex, WStacking *node,
852 WStacking *sub, int flags,
853 WPrepareFocusResult *res)
855 bool ew=(flags®ION_GOTO_ENTERWINDOW);
856 PtrList *hidelist=NULL;
857 PtrList **hidelistp=(ew ? NULL : &hidelist);
859 /*bool within=FALSE;*/
861 if(sub==NULL && node==NULL)
864 /* Display the node in any case */
865 if(node!=NULL && !ew)
866 mplex_do_node_display(mplex, node, TRUE);
868 if(!region_prepare_focus((WRegion*)mplex, flags, res))
871 foc=mplex_do_to_focus_on(mplex, node, sub, hidelistp, NULL /*&within*/);
874 while(hidelist!=NULL){
875 WStacking *st=(WStacking*)ptrlist_take_first(&hidelist);
877 region_unmap(st->reg);
880 if(ioncore_g.autoraise &&
881 !(flags®ION_GOTO_ENTERWINDOW) &&
882 foc->level>STACKING_LEVEL_BOTTOM){
883 WStacking **stackingp=mplex_get_stackingp(mplex);
884 stacking_restack(stackingp, foc, None, NULL, NULL, FALSE);
890 return (foc==sub || (sub==NULL && foc==node));
897 bool mplex_managed_prepare_focus(WMPlex *mplex, WRegion *disp,
898 int flags, WPrepareFocusResult *res)
900 WStacking *node=mplex_find_stacking(mplex, disp);
905 return mplex_do_prepare_focus(mplex, node, NULL, flags, res);
912 /*{{{ Switch exports */
915 static void do_switch(WMPlex *mplex, WLListNode *lnode)
917 WStacking *node=(lnode!=NULL ? lnode->st : NULL);
920 bool mcf=region_may_control_focus((WRegion*)mplex);
922 mplex_do_node_display(mplex, node, TRUE);
925 mplex_refocus(mplex, node, TRUE);
931 * Have \var{mplex} display the \var{n}:th object managed by it.
934 void mplex_switch_nth(WMPlex *mplex, uint n)
936 do_switch(mplex, llist_nth_node(mplex->mx_list, n));
941 * Have \var{mplex} display next (wrt. currently selected) object managed
945 void mplex_switch_next(WMPlex *mplex)
947 do_switch(mplex, LIST_NEXT_WRAP(mplex->mx_list, mplex->mx_current,
953 * Have \var{mplex} display previous (wrt. currently selected) object
957 void mplex_switch_prev(WMPlex *mplex)
959 do_switch(mplex, LIST_PREV_WRAP(mplex->mx_list, mplex->mx_current,
964 bool mplex_set_hidden(WMPlex *mplex, WRegion *reg, int sp)
966 bool mcf=region_may_control_focus((WRegion*)mplex);
967 WStacking *node=mplex_find_stacking(mplex, reg);
968 bool hidden, nhidden;
973 hidden=STACKING_IS_HIDDEN(node);
974 nhidden=libtu_do_setparam(sp, hidden);
976 if(!hidden && nhidden){
979 if(REGION_IS_MAPPED(mplex) && !MPLEX_MGD_UNVIEWABLE(mplex))
982 /* lnode -> switch next? */
983 }else if(hidden && !nhidden){
984 mplex_do_node_display(mplex, node, TRUE);
987 if(mcf && !PASSIVE(node))
988 mplex_refocus(mplex, (nhidden ? NULL : node), TRUE);
990 return STACKING_IS_HIDDEN(node);
995 * Set the visibility of the region \var{reg} on \var{mplex}
996 * as specified with the parameter \var{how}
997 * (one of \codestr{set}, \codestr{unset}, or \codestr{toggle}).
998 * The resulting state is returned.
1000 EXTL_EXPORT_AS(WMPlex, set_hidden)
1001 bool mplex_set_hidden_extl(WMPlex *mplex, WRegion *reg, const char *how)
1003 return mplex_set_hidden(mplex, reg, libtu_string_to_setparam(how));
1008 * Is \var{reg} on within \var{mplex} and hidden?
1012 bool mplex_is_hidden(WMPlex *mplex, WRegion *reg)
1014 WStacking *node=mplex_find_stacking(mplex, reg);
1016 return (node!=NULL && STACKING_IS_HIDDEN(node));
1026 static WStacking *mplex_nxt(WMPlex *mplex, WStacking *st, bool wrap)
1028 return (st->mgr_next!=NULL
1030 : (wrap ? mplex->mgd : NULL));
1034 static WStacking *mplex_prv(WMPlex *mplex, WStacking *st, bool wrap)
1036 return (st!=mplex->mgd
1038 : (wrap ? st->mgr_prev : NULL));
1042 typedef WStacking *NxtFn(WMPlex *mplex, WStacking *st, bool wrap);
1045 static WRegion *do_navi(WMPlex *mplex, WStacking *sti,
1046 NxtFn *fn, WRegionNaviData *data,
1047 bool sti_ok, bool wrap)
1049 WStacking *st, *stacking;
1052 stacking=mplex_get_stacking(mplex);
1055 min_level=stacking_min_level_mapped(stacking);
1059 st=fn(mplex, st, wrap);
1061 if(st==NULL || (st==sti && !sti_ok))
1065 if(OBJ_IS(st->reg, WGroup)){
1066 /* WGroup navigation code should respect modal stuff. */
1067 WRegion *res=region_navi_cont((WRegion*)mplex, st->reg, data);
1068 if(res!=NULL && res!=st->reg)
1071 if(st->level>=min_level && !PASSIVE(st))
1072 return region_navi_cont((WRegion*)mplex, st->reg, data);
1084 WRegion *mplex_navi_first(WMPlex *mplex, WRegionNavi nh,
1085 WRegionNaviData *data)
1087 WStacking *lst=mplex->mgd;
1091 if(nh==REGION_NAVI_ANY){
1095 if(nh==REGION_NAVI_ANY || nh==REGION_NAVI_END ||
1096 nh==REGION_NAVI_BOTTOM || nh==REGION_NAVI_RIGHT){
1097 res=do_navi(mplex, lst, mplex_prv, data, TRUE, TRUE);
1099 res=do_navi(mplex, lst->mgr_prev, mplex_nxt, data, TRUE, TRUE);
1103 return region_navi_cont((WRegion*)mplex, res, data);
1107 WRegion *mplex_navi_next(WMPlex *mplex, WRegion *rel, WRegionNavi nh,
1108 WRegionNaviData *data)
1114 st=mplex_find_stacking(mplex, rel);
1117 }else if(mplex->mx_current!=NULL){
1118 st=mplex->mx_current->st;
1120 return mplex_navi_first(mplex, nh, data);
1123 if(nh==REGION_NAVI_ANY){
1127 if(nh==REGION_NAVI_ANY || nh==REGION_NAVI_END ||
1128 nh==REGION_NAVI_BOTTOM || nh==REGION_NAVI_RIGHT){
1129 res=do_navi(mplex, st, mplex_nxt, data, FALSE, FALSE);
1131 res=do_navi(mplex, st, mplex_prv, data, FALSE, FALSE);
1134 return region_navi_cont((WRegion*)mplex, res, data);
1144 bool mplex_managed_rqorder(WMPlex *mplex, WRegion *reg, WRegionOrder order)
1146 WStacking **stackingp=mplex_get_stackingp(mplex);
1149 if(stackingp==NULL || *stackingp==NULL)
1152 st=mplex_find_stacking(mplex, reg);
1157 stacking_restack(stackingp, st, None, NULL, NULL,
1158 (order!=REGION_ORDER_FRONT));
1170 static bool mplex_stack(WMPlex *mplex, WStacking *st)
1172 WStacking *tmp=NULL;
1173 WStacking **stackingp=mplex_get_stackingp(mplex);
1178 LINK_ITEM_FIRST(tmp, st, next, prev);
1179 stacking_weave(stackingp, &tmp, FALSE);
1186 static void mplex_unstack(WMPlex *mplex, WStacking *st)
1188 WStacking *stacking;
1190 stacking=mplex_get_stacking(mplex);
1192 stacking_unstack(&mplex->win, st);
1196 /* WMPlexWPHolder is used for position marking in order to allow
1197 * WLListNodes be safely removed in the attach handler hnd, that
1198 * could remove something this mplex is managing.
1200 bool mplex_do_attach_final(WMPlex *mplex, WRegion *reg, WMPlexPHolder *ph)
1202 WStacking *node=NULL;
1203 WLListNode *lnode=NULL;
1204 WMPlexAttachParams *param=&ph->param;
1205 bool mx_was_empty, sw, modal, mcf, hidden;
1209 mcf=region_may_control_focus((WRegion*)mplex);
1211 mx_was_empty=(mplex->mx_list==NULL);
1213 szplcy=((param->flags&MPLEX_ATTACH_SIZEPOLICY &&
1214 param->szplcy!=SIZEPOLICY_DEFAULT)
1216 : (param->flags&MPLEX_ATTACH_UNNUMBERED
1217 ? SIZEPOLICY_FULL_BOUNDS
1218 : SIZEPOLICY_FULL_EXACT));
1220 modal=(param->flags&MPLEX_ATTACH_LEVEL
1221 && param->level>=STACKING_LEVEL_MODAL1);
1223 level=(param->flags&MPLEX_ATTACH_LEVEL
1225 : (param->flags&MPLEX_ATTACH_UNNUMBERED
1226 ? STACKING_LEVEL_NORMAL
1227 : STACKING_LEVEL_BOTTOM));
1229 hidden=(param->flags&MPLEX_ATTACH_HIDDEN
1230 && (param->flags&MPLEX_ATTACH_UNNUMBERED
1233 sw=(!hidden && (param->flags&MPLEX_ATTACH_SWITCHTO
1234 || (param->flags&MPLEX_ATTACH_UNNUMBERED
1236 : (mplex_current_node(mplex)==NULL))));
1238 hidden=(hidden || (!sw && !(param->flags&MPLEX_ATTACH_UNNUMBERED)));
1240 node=create_stacking();
1245 if(!(param->flags&MPLEX_ATTACH_UNNUMBERED)){
1246 lnode=ALLOC(WLListNode);
1248 stacking_free(node);
1258 if(!stacking_assoc(node, reg)){
1263 stacking_free(node);
1268 node->szplcy=szplcy;
1270 node->pseudomodal=(param->flags&MPLEX_ATTACH_PSEUDOMODAL ? 1 : 0);
1273 WMPlexPHolder *ph2, *phn, *php;
1275 llist_link_after(&(mplex->mx_list),
1276 (ph!=NULL ? ph->after : NULL),
1281 /* Move placeholders after new node */
1282 for(php=NULL, ph2=ph; ph2!=NULL; php=ph2, ph2=phn){
1284 mplexpholder_move(ph2, mplex, php, lnode);
1288 LINK_ITEM(mplex->mgd, node, mgr_next, mgr_prev);
1290 if(!OBJ_IS(reg, WGroup))
1291 mplex_stack(mplex, node);
1293 region_set_manager(reg, (WRegion*)mplex);
1295 if(param->flags&MPLEX_ATTACH_PASSIVE)
1296 reg->flags|=REGION_SKIP_FOCUS;
1298 if(!(param->flags&MPLEX_ATTACH_WHATEVER)){
1302 mplex_managed_geom(mplex, &(fp.g));
1304 sizepolicy(&node->szplcy, reg,
1305 (param->flags&MPLEX_ATTACH_GEOM ? &(param->geom) : NULL),
1308 if(rectangle_compare(&fp.g, ®ION_GEOM(reg))!=RECTANGLE_SAME)
1309 region_fitrep(reg, NULL, &fp);
1313 mplex_do_node_display(mplex, node, FALSE);
1319 mplex_refocus(mplex, node, FALSE);
1321 (level>=STACKING_LEVEL_MODAL1 || OBJ_IS(reg, WGroup))){
1322 /* New modal regions may require focusing, so try to
1323 * give focus back to currently active object.
1324 * (There seems to be some problem with uncontained
1325 * client windows still..)
1327 mplex_refocus(mplex, NULL, FALSE);
1329 region_pointer_focus_hack(reg);
1332 region_pointer_focus_hack(reg);
1336 mplex_managed_changed(mplex, MPLEX_CHANGE_ADD, sw, reg);
1342 WRegion *mplex_do_attach_pholder(WMPlex *mplex, WMPlexPHolder *ph,
1343 WRegionAttachData *data)
1345 WMPlexAttachParams *param=&(ph->param);
1348 if(param->flags&MPLEX_ATTACH_GEOM)
1351 mplex_managed_geom(mplex, &(fp.g));
1353 fp.mode=REGION_FIT_WHATEVER|REGION_FIT_BOUNDS;
1355 return region_attach_helper((WRegion*)mplex,
1356 (WWindow*)mplex, &fp,
1357 (WRegionDoAttachFn*)mplex_do_attach_final,
1362 WRegion *mplex_do_attach(WMPlex *mplex, WMPlexAttachParams *param,
1363 WRegionAttachData *data)
1368 ph=create_mplexpholder(mplex, NULL, param);
1373 reg=mplex_do_attach_pholder(mplex, ph, data);
1375 destroy_obj((Obj*)ph);
1381 WRegion *mplex_do_attach_new(WMPlex *mplex, WMPlexAttachParams *param,
1382 WRegionCreateFn *fn, void *fn_param)
1384 WRegionAttachData data;
1386 data.type=REGION_ATTACH_NEW;
1388 data.u.n.param=fn_param;
1390 return mplex_do_attach(mplex, param, &data);
1394 #define MPLEX_ATTACH_SET_FLAGS (MPLEX_ATTACH_GEOM| \
1395 MPLEX_ATTACH_SIZEPOLICY| \
1399 WRegion *mplex_attach_simple(WMPlex *mplex, WRegion *reg, int flags)
1401 WMPlexAttachParams param;
1402 WRegionAttachData data;
1404 param.flags=flags&~MPLEX_ATTACH_SET_FLAGS;
1406 data.type=REGION_ATTACH_REPARENT;
1409 return mplex_do_attach(mplex, ¶m, &data);
1413 static void get_params(WMPlex *mplex, ExtlTab tab, int mask,
1414 WMPlexAttachParams *par)
1420 if(ok&MPLEX_ATTACH_LEVEL){
1421 if(extl_table_gets_i(tab, "level", &tmp)){
1423 par->flags|=MPLEX_ATTACH_LEVEL;
1428 if(extl_table_is_bool_set(tab, "modal"))
1429 par->level=maxof(par->level, STACKING_LEVEL_MODAL1);
1432 if(extl_table_is_bool_set(tab, "unnumbered"))
1433 par->flags|=MPLEX_ATTACH_UNNUMBERED&ok;
1435 if(extl_table_is_bool_set(tab, "switchto"))
1436 par->flags|=MPLEX_ATTACH_SWITCHTO&ok;
1438 if(extl_table_is_bool_set(tab, "hidden"))
1439 par->flags|=MPLEX_ATTACH_HIDDEN&ok;
1441 if(extl_table_is_bool_set(tab, "passive"))
1442 par->flags|=MPLEX_ATTACH_PASSIVE&ok;
1444 if(extl_table_is_bool_set(tab, "pseudomodal"))
1445 par->flags|=MPLEX_ATTACH_PSEUDOMODAL&ok;
1447 if(extl_table_gets_i(tab, "index", &(par->index)))
1448 par->flags|=MPLEX_ATTACH_INDEX&ok;
1450 if(ok&MPLEX_ATTACH_SIZEPOLICY){
1451 if(extl_table_gets_s(tab, "sizepolicy", &tmpstr)){
1453 if(string2sizepolicy(tmpstr, &tmpp)){
1454 par->flags|=MPLEX_ATTACH_SIZEPOLICY;
1458 }else if(extl_table_gets_i(tab, "sizepolicy", &tmp)){
1459 /* Backwards compat. numeric version */
1460 par->flags|=MPLEX_ATTACH_SIZEPOLICY;
1465 if(extl_table_gets_rectangle(tab, "geom", &par->geom))
1466 par->flags|=MPLEX_ATTACH_GEOM&ok;
1471 * Attach and reparent existing region \var{reg} to \var{mplex}.
1472 * The table \var{param} may contain the fields \var{index} and
1473 * \var{switchto} that are interpreted as for \fnref{WMPlex.attach_new}.
1476 WRegion *mplex_attach(WMPlex *mplex, WRegion *reg, ExtlTab param)
1478 WMPlexAttachParams par=MPLEXATTACHPARAMS_INIT;
1479 WRegionAttachData data;
1484 get_params(mplex, param, 0, &par);
1486 data.type=REGION_ATTACH_REPARENT;
1489 return mplex_do_attach(mplex, &par, &data);
1493 WRegion *mplex_attach_new_(WMPlex *mplex, WMPlexAttachParams *par,
1494 int mask, ExtlTab param)
1496 WRegionAttachData data;
1498 get_params(mplex, param, mask, par);
1500 data.type=REGION_ATTACH_LOAD;
1503 return mplex_do_attach(mplex, par, &data);
1508 * Create a new region to be managed by \var{mplex}. At least the following
1509 * fields in \var{param} are understood (all but \var{type} are optional).
1511 * \begin{tabularx}{\linewidth}{lX}
1512 * \tabhead{Field & Description}
1513 * \var{type} & (string) Class name (a string) of the object to be created. \\
1514 * \var{name} & (string) Name of the object to be created (a string). \\
1515 * \var{switchto} & (boolean) Should the region be switched to (boolean)? \\
1516 * \var{unnumbered} & (boolean) Do not put on the numbered mutually
1517 * exclusive list. \\
1518 * \var{index} & (integer) Index on this list, same as for
1519 * \fnref{WMPlex.set_index}. \\
1520 * \var{level} & (integer) Stacking level. \\
1521 * \var{modal} & (boolean) Shortcut for modal stacking level. \\
1522 * \var{hidden} & (boolean) Attach hidden, if not prevented
1523 * by e.g. the mutually exclusive list being empty.
1524 * This option overrides \var{switchto}. \\
1525 * \var{passive} & (boolean) Skip in certain focusing operations. \\
1526 * \var{pseudomodal} & (boolean) The attached region is ``pseudomodal''
1527 * if the stacking level dictates it to be modal.
1528 * This means that the region may be hidden to display
1529 * regions with lesser stacking levels. \\
1530 * \var{sizepolicy} & (string) Size policy; see Section \ref{sec:sizepolicies}. \\
1531 * \var{geom} & (table) Geometry specification. \\
1534 * In addition parameters to the region to be created are passed in this
1538 WRegion *mplex_attach_new(WMPlex *mplex, ExtlTab param)
1540 WMPlexAttachParams par=MPLEXATTACHPARAMS_INIT;
1542 return mplex_attach_new_(mplex, &par, 0, param);
1546 static bool mplex_handle_drop(WMPlex *mplex, int x, int y,
1549 WRegion *curr=mplex_mx_current(mplex);
1551 /* This code should handle dropping tabs on floating workspaces. */
1552 if(curr && HAS_DYN(curr, region_handle_drop)){
1554 region_rootpos(curr, &rx, &ry);
1555 if(rectangle_contains(®ION_GEOM(curr), x-rx, y-ry)){
1556 if(region_handle_drop(curr, x, y, dropped))
1561 return (NULL!=mplex_attach_simple(mplex, dropped, MPLEX_ATTACH_SWITCHTO));
1565 WPHolder *mplex_prepare_manage(WMPlex *mplex, const WClientWin *cwin,
1566 const WManageParams *param, int priority)
1568 int cpriority=MANAGE_PRIORITY_SUB(priority, MANAGE_PRIORITY_NORMAL);
1569 WMPlexAttachParams ap;
1574 /* Check current */ {
1575 WStacking *cur=mplex_current_node(mplex);
1578 ph=region_prepare_manage(cur->reg, cwin, param, cpriority);
1583 if(mplex->mx_current!=NULL && mplex->mx_current->st!=cur){
1584 ph=region_prepare_manage(mplex->mx_current->st->reg,
1585 cwin, param, cpriority);
1591 if(!MANAGE_PRIORITY_OK(priority, MANAGE_PRIORITY_NORMAL))
1594 ap.flags=((param->switchto ? MPLEX_ATTACH_SWITCHTO : 0)
1595 |MPLEX_ATTACH_SIZEPOLICY);
1596 ap.szplcy=SIZEPOLICY_FULL_EXACT;
1598 mph=create_mplexpholder(mplex, NULL, &ap);
1601 WGroupedPHolder *gph=create_groupedpholder((WPHolder*)mph);
1603 return (WPHolder*)gph;
1606 return (WPHolder*)mph;
1616 void mplex_managed_remove(WMPlex *mplex, WRegion *sub)
1618 bool mx=FALSE, hadfocus=FALSE, mcf;
1619 WRegion *stdisp=(WRegion*)(mplex->stdispwatch.obj);
1620 WStacking *node, *next=NULL;
1622 mcf=region_may_control_focus((WRegion*)mplex);
1625 if(CAN_MANAGE_STDISP(sub) &&
1626 region_managed_within((WRegion*)mplex, stdisp)==sub){
1627 region_unmanage_stdisp(sub, TRUE, TRUE);
1628 region_detach_manager(stdisp);
1632 node=mplex_find_stacking(mplex, sub);
1637 hadfocus=(mplex_current_node(mplex)==node);
1639 if(node->lnode!=NULL){
1640 if(mplex->mx_current==node->lnode){
1643 mplex->mx_current=NULL;
1644 lnext=LIST_PREV(mplex->mx_list, node->lnode, next, prev);
1646 lnext=LIST_NEXT(mplex->mx_list, node->lnode, next, prev);
1647 if(lnext==node->lnode)
1654 mplex_move_phs_before(mplex, node->lnode);
1655 llist_unlink(&(mplex->mx_list), node->lnode);
1663 UNLINK_ITEM(mplex->mgd, node, mgr_next, mgr_prev);
1665 mplex_unstack(mplex, node);
1667 stacking_unassoc(node);
1668 stacking_free(node);
1670 region_unset_manager(sub, (WRegion*)mplex);
1672 if(OBJ_IS_BEING_DESTROYED(mplex))
1676 mplex_do_node_display(mplex, next, FALSE);
1679 mplex_refocus(mplex, next, FALSE);
1682 mplex_managed_changed(mplex, MPLEX_CHANGE_REMOVE, next!=NULL, sub);
1686 bool mplex_rescue_clientwins(WMPlex *mplex, WRescueInfo *info)
1691 WLListNode *lnode, *was_current=mplex->mx_current;
1693 /* First all mx stuff to move them nicely to another mplex (when that
1694 * is the case), switching to the current region in the target if
1695 * allowed by ph_flags_mask region_rescue.
1697 FOR_ALL_NODES_ON_LLIST(lnode, mplex->mx_list, ltmp){
1698 int sw=(lnode==was_current ? PHOLDER_ATTACH_SWITCHTO : 0);
1699 region_do_rescue_this(lnode->st->reg, info, sw);
1702 /* Then the rest (possibly retrying failed mx stuff).
1704 mplex_iter_init(&tmp, mplex);
1705 ret1=region_rescue_some_clientwins((WRegion*)mplex, info,
1706 (WRegionIterator*)mplex_iter,
1709 ret2=region_rescue_child_clientwins((WRegion*)mplex, info);
1711 return (ret1 && ret2);
1716 void mplex_child_removed(WMPlex *mplex, WRegion *sub)
1718 if(sub!=NULL && sub==(WRegion*)(mplex->stdispwatch.obj)){
1719 watch_reset(&(mplex->stdispwatch));
1720 mplex_set_stdisp(mplex, NULL, NULL);
1728 /*{{{ Status display support */
1731 # define offsetof(T,F) ((size_t)((char*)&((T*)0L)->F-(char*)0L))
1734 #define STRUCTOF(T, F, FADDR) \
1735 ((T*)((char*)(FADDR)-offsetof(T, F)))
1738 static void stdisp_watch_handler(Watch *watch, Obj *obj)
1740 /*WMPlex *mplex=STRUCTOF(WMPlex, stdispinfo,
1741 STRUCTOF(WMPlexSTDispInfo, regwatch, watch));
1742 WMPlexSTDispInfo *di=&(mplex->stdispinfo);
1743 WGenWS *ws=OBJ_CAST(REGION_MANAGER(obj), WGenWS);
1745 if(ioncore_g.opmode!=IONCORE_OPMODE_DEINIT && ws!=NULL)
1746 genws_unmanage_stdisp(ws, TRUE, FALSE);*/
1750 bool mplex_set_stdisp(WMPlex *mplex, WRegion *reg,
1751 const WMPlexSTDispInfo *din)
1753 WRegion *oldstdisp=(WRegion*)(mplex->stdispwatch.obj);
1756 assert(reg==NULL || (reg==oldstdisp) ||
1757 (REGION_MANAGER(reg)==NULL &&
1758 REGION_PARENT(reg)==(WWindow*)mplex));
1760 if(oldstdisp!=NULL){
1761 mgr=region_managed_within((WRegion*)mplex, oldstdisp);
1763 if(!CAN_MANAGE_STDISP(mgr))
1768 mplex->stdispinfo=*din;
1771 watch_reset(&(mplex->stdispwatch));
1774 region_unmanage_stdisp(mgr, TRUE, FALSE);
1776 region_detach_manager(oldstdisp);
1779 watch_setup(&(mplex->stdispwatch), (Obj*)reg, stdisp_watch_handler);
1781 mplex_remanage_stdisp(mplex);
1784 if(oldstdisp!=NULL && oldstdisp!=reg)
1785 mainloop_defer_destroy((Obj*)oldstdisp);
1791 void mplex_get_stdisp(WMPlex *mplex, WRegion **reg, WMPlexSTDispInfo *di)
1793 *di=mplex->stdispinfo;
1794 *reg=(WRegion*)mplex->stdispwatch.obj;
1798 static StringIntMap pos_map[]={
1799 {"tl", MPLEX_STDISP_TL},
1800 {"tr", MPLEX_STDISP_TR},
1801 {"bl", MPLEX_STDISP_BL},
1802 {"br", MPLEX_STDISP_BR},
1807 static bool do_attach_stdisp(WRegion *mplex, WRegion *reg, void *unused)
1809 /* We do not actually manage the stdisp. */
1815 * Set/create status display for \var{mplex}. Table is a standard
1816 * description of the object to be created (as passed to e.g.
1817 * \fnref{WMPlex.attach_new}). In addition, the following fields are
1820 * \begin{tabularx}{\linewidth}{lX}
1821 * \tabhead{Field & Description}
1822 * \var{pos} & (string) The corner of the screen to place the status
1823 * display in: one of \codestr{tl}, \codestr{tr}, \codestr{bl}
1824 * or \codestr{br}. \\
1825 * \var{fullsize} & (boolean) Waste all available space. \\
1826 * \var{action} & (string) If this field is set to \codestr{keep},
1827 * \var{pos} and \var{fullsize} are changed for the existing
1828 * status display. If this field is set to \codestr{remove},
1829 * the existing status display is removed. If this
1830 * field is not set or is set to \codestr{replace}, a
1831 * new status display is created and the old, if any,
1835 EXTL_EXPORT_AS(WMPlex, set_stdisp)
1836 WRegion *mplex_set_stdisp_extl(WMPlex *mplex, ExtlTab t)
1838 WRegion *stdisp=NULL;
1839 WMPlexSTDispInfo din=mplex->stdispinfo;
1842 if(extl_table_gets_s(t, "pos", &s)){
1843 din.pos=stringintmap_value(pos_map, s, -1);
1845 warn(TR("Invalid position setting."));
1850 extl_table_gets_b(t, "fullsize", &(din.fullsize));
1853 extl_table_gets_s(t, "action", &s);
1855 if(s==NULL || strcmp(s, "replace")==0){
1856 WRegionAttachData data;
1862 fp.g.w=REGION_GEOM(mplex).w;
1863 fp.g.h=REGION_GEOM(mplex).h;
1864 fp.mode=REGION_FIT_BOUNDS|REGION_FIT_WHATEVER;
1866 /* Full mplex size is stupid so use saved geometry initially
1869 extl_table_gets_rectangle(t, "geom", &(fp.g));
1871 data.type=REGION_ATTACH_LOAD;
1874 stdisp=region_attach_helper((WRegion*)mplex,
1875 (WWindow*)mplex, &fp,
1876 do_attach_stdisp, NULL,
1882 }else if(strcmp(s, "keep")==0){
1883 stdisp=(WRegion*)(mplex->stdispwatch.obj);
1884 }else if(strcmp(s, "remove")!=0){
1885 warn(TR("Invalid action setting."));
1889 if(!mplex_set_stdisp(mplex, stdisp, &din)){
1890 destroy_obj((Obj*)stdisp);
1898 static ExtlTab mplex_do_get_stdisp_extl(WMPlex *mplex, bool fullconfig)
1900 WRegion *reg=(WRegion*)mplex->stdispwatch.obj;
1904 return extl_table_none();
1907 t=region_get_configuration(reg);
1908 extl_table_sets_rectangle(t, "geom", ®ION_GEOM(reg));
1910 t=extl_create_table();
1911 extl_table_sets_o(t, "reg", (Obj*)reg);
1914 if(t!=extl_table_none()){
1915 WMPlexSTDispInfo *di=&(mplex->stdispinfo);
1916 extl_table_sets_s(t, "pos", stringintmap_key(pos_map, di->pos, NULL));
1917 extl_table_sets_b(t, "fullsize", di->fullsize);
1924 * Get status display information. See \fnref{WMPlex.get_stdisp} for
1925 * information on the fields.
1928 EXTL_EXPORT_AS(WMPlex, get_stdisp)
1929 ExtlTab mplex_get_stdisp_extl(WMPlex *mplex)
1931 return mplex_do_get_stdisp_extl(mplex, FALSE);
1941 void mplex_managed_geom_default(const WMPlex *mplex, WRectangle *geom)
1945 geom->w=REGION_GEOM(mplex).w;
1946 geom->h=REGION_GEOM(mplex).h;
1950 void mplex_managed_geom(const WMPlex *mplex, WRectangle *geom)
1952 CALL_DYN(mplex_managed_geom, mplex, (mplex, geom));
1956 void mplex_size_changed(WMPlex *mplex, bool wchg, bool hchg)
1958 CALL_DYN(mplex_size_changed, mplex, (mplex, wchg, hchg));
1962 void mplex_managed_changed(WMPlex *mplex, int mode, bool sw, WRegion *mgd)
1964 CALL_DYN(mplex_managed_changed, mplex, (mplex, mode, sw, mgd));
1968 int mplex_default_index(WMPlex *mplex)
1970 int idx=LLIST_INDEX_LAST;
1971 CALL_DYN_RET(idx, int, mplex_default_index, mplex, (mplex));
1976 /* For regions managing stdisps */
1978 void region_manage_stdisp(WRegion *reg, WRegion *stdisp,
1979 const WMPlexSTDispInfo *info)
1981 CALL_DYN(region_manage_stdisp, reg, (reg, stdisp, info));
1985 void region_unmanage_stdisp(WRegion *reg, bool permanent, bool nofocus)
1987 CALL_DYN(region_unmanage_stdisp, reg, (reg, permanent, nofocus));
1994 /*{{{ Changed hook helper */
1997 static const char *mode2str(int mode)
1999 if(mode==MPLEX_CHANGE_SWITCHONLY)
2000 return "switchonly";
2001 else if(mode==MPLEX_CHANGE_REORDER)
2003 else if(mode==MPLEX_CHANGE_ADD)
2005 else if(mode==MPLEX_CHANGE_REMOVE)
2011 static bool mrsh_chg(ExtlFn fn, WMPlexChangedParams *p)
2013 ExtlTab t=extl_create_table();
2016 extl_table_sets_o(t, "reg", (Obj*)p->reg);
2017 extl_table_sets_s(t, "mode", mode2str(p->mode));
2018 extl_table_sets_b(t, "sw", p->sw);
2019 extl_table_sets_o(t, "sub", (Obj*)p->sub);
2022 ret=extl_call(fn, "t", NULL, t);
2023 extl_unprotect(NULL);
2025 extl_unref_table(t);
2031 void mplex_call_changed_hook(WMPlex *mplex, WHook *hook,
2032 int mode, bool sw, WRegion *reg)
2034 WMPlexChangedParams p;
2041 hook_call_p(hook, &p, (WHookMarshallExtl*)mrsh_chg);
2051 static void save_node(WMPlex *mplex, ExtlTab subs, int *n,
2052 WStacking *node, bool unnumbered)
2056 st=region_get_configuration(node->reg);
2058 if(st!=extl_table_none()){
2059 if(mplex->mx_current!=NULL && node==mplex->mx_current->st)
2060 extl_table_sets_b(st, "switchto", TRUE);
2061 extl_table_sets_s(st, "sizepolicy",
2062 sizepolicy2string(node->szplcy));
2063 extl_table_sets_i(st, "level", node->level);
2064 g=extl_table_from_rectangle(®ION_GEOM(node->reg));
2065 extl_table_sets_t(st, "geom", g);
2066 extl_unref_table(g);
2067 if(STACKING_IS_HIDDEN(node))
2068 extl_table_sets_b(st, "hidden", TRUE);
2069 if(STACKING_IS_PSEUDOMODAL(node))
2070 extl_table_sets_b(st, "pseudomodal", TRUE);
2072 extl_table_sets_b(st, "unnumbered", TRUE);
2074 extl_table_seti_t(subs, ++(*n), st);
2075 extl_unref_table(st);
2080 ExtlTab mplex_get_configuration(WMPlex *mplex)
2082 ExtlTab tab, subs, stdisptab;
2089 tab=region_get_base_configuration((WRegion*)mplex);
2091 subs=extl_create_table();
2092 extl_table_sets_t(tab, "managed", subs);
2094 /* First the numbered/mutually exclusive nodes */
2095 FOR_ALL_NODES_ON_LLIST(lnode, mplex->mx_list, ltmp){
2096 save_node(mplex, subs, &n, lnode->st, FALSE);
2099 FOR_ALL_NODES_IN_MPLEX(mplex, node, tmp){
2100 if(node->lnode==NULL)
2101 save_node(mplex, subs, &n, node, TRUE);
2104 extl_unref_table(subs);
2106 /*stdisptab=mplex_do_get_stdisp_extl(mplex, TRUE);
2107 if(stdisptab!=extl_table_none()){
2108 extl_table_sets_t(tab, "stdisp", stdisptab);
2109 extl_unref_table(stdisptab);
2116 static WMPlex *tmp_mplex=NULL;
2117 static WMPlexAttachParams *tmp_par=NULL;
2119 static WPHolder *pholder_callback()
2121 assert(tmp_mplex!=NULL);
2122 return (WPHolder*)create_mplexpholder(tmp_mplex, NULL, tmp_par);
2126 void mplex_load_contents(WMPlex *mplex, ExtlTab tab)
2128 ExtlTab substab, subtab;
2131 /*if(extl_table_gets_t(tab, "stdisp", &subtab)){
2132 mplex_set_stdisp_extl(mplex, subtab);
2133 extl_unref_table(subtab);
2136 if(extl_table_gets_t(tab, "managed", &substab) ||
2137 extl_table_gets_t(tab, "subs", &substab)){
2138 n=extl_table_get_n(substab);
2139 for(i=1; i<=n; i++){
2140 if(extl_table_geti_t(substab, i, &subtab)){
2141 /*mplex_attach_new(mplex, subtab);*/
2142 WMPlexAttachParams par=MPLEXATTACHPARAMS_INIT;
2143 WRegionAttachData data;
2146 get_params(mplex, subtab, 0, &par);
2148 par.flags|=MPLEX_ATTACH_INDEX;
2149 par.index=LLIST_INDEX_LAST;
2154 data.type=REGION_ATTACH_LOAD;
2157 ioncore_set_sm_pholder_callback(pholder_callback);
2159 mplex_do_attach(mplex, &par, &data);
2163 extl_unref_table(subtab);
2166 extl_unref_table(substab);
2171 WRegion *mplex_load(WWindow *par, const WFitParams *fp, ExtlTab tab)
2173 WMPlex *mplex=create_mplex(par, fp);
2175 mplex_load_contents(mplex, tab);
2176 return (WRegion*)mplex;
2183 /*{{{ Dynfuntab and class info */
2186 static DynFunTab mplex_dynfuntab[]={
2187 {region_do_set_focus,
2188 mplex_do_set_focus},
2190 {region_managed_remove,
2191 mplex_managed_remove},
2193 {region_managed_rqgeom,
2194 mplex_managed_rqgeom},
2196 {(DynFun*)region_managed_prepare_focus,
2197 (DynFun*)mplex_managed_prepare_focus},
2199 {(DynFun*)region_handle_drop,
2200 (DynFun*)mplex_handle_drop},
2202 {region_map, mplex_map},
2203 {region_unmap, mplex_unmap},
2205 {(DynFun*)region_prepare_manage,
2206 (DynFun*)mplex_prepare_manage},
2208 {(DynFun*)region_current,
2209 (DynFun*)mplex_current},
2211 {(DynFun*)region_rescue_clientwins,
2212 (DynFun*)mplex_rescue_clientwins},
2214 {(DynFun*)region_get_configuration,
2215 (DynFun*)mplex_get_configuration},
2217 {mplex_managed_geom,
2218 mplex_managed_geom_default},
2220 {(DynFun*)region_fitrep,
2221 (DynFun*)mplex_fitrep},
2223 {region_child_removed,
2224 mplex_child_removed},
2226 {(DynFun*)region_managed_get_pholder,
2227 (DynFun*)mplex_managed_get_pholder},
2229 {(DynFun*)region_get_rescue_pholder_for,
2230 (DynFun*)mplex_get_rescue_pholder_for},
2232 {(DynFun*)region_navi_first,
2233 (DynFun*)mplex_navi_first},
2235 {(DynFun*)region_navi_next,
2236 (DynFun*)mplex_navi_next},
2238 {(DynFun*)region_managed_rqorder,
2239 (DynFun*)mplex_managed_rqorder},
2246 IMPLCLASS(WMPlex, WWindow, mplex_deinit, mplex_dynfuntab);