Released version 3-2015061300
[notion.git] / ioncore / mplex.c
blob7b7cb1a5aec7793a9dadddf68a4b0278f4d4327e
1 /*
2 * ion/ioncore/mplex.c
4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
7 */
9 #include <limits.h>
10 #include <string.h>
12 #include <libtu/objp.h>
13 #include <libtu/minmax.h>
14 #include <libtu/rb.h>
15 #include <libextl/extl.h>
16 #include <libmainloop/defer.h>
18 #include "names.h"
19 #include "common.h"
20 #include "window.h"
21 #include "global.h"
22 #include "rootwin.h"
23 #include "focus.h"
24 #include "event.h"
25 #include "attach.h"
26 #include "manage.h"
27 #include "resize.h"
28 #include "tags.h"
29 #include "sizehint.h"
30 #include "extlconv.h"
31 #include "frame-pointer.h"
32 #include "bindmaps.h"
33 #include "regbind.h"
34 #include "saveload.h"
35 #include "xwindow.h"
36 #include "mplexpholder.h"
37 #include "grouppholder.h"
38 #include "llist.h"
39 #include "names.h"
40 #include "sizepolicy.h"
41 #include "stacking.h"
42 #include "group.h"
43 #include "navi.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 \
50 && ((ST)->reg==NULL \
51 || (ST)->reg->flags&REGION_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);
89 /*}}}*/
92 /*{{{ Destroy/create mplex */
95 bool mplex_do_init(WMPlex *mplex, WWindow *parent,
96 const WFitParams *fp, Window win, const char *name)
98 mplex->flags=0;
100 mplex->mx_list=NULL;
101 mplex->mx_current=NULL;
102 mplex->misc_phs=NULL;
103 mplex->mx_count=0;
105 mplex->mgd=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))
112 return FALSE;
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);
125 return TRUE;
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)
143 WMPlexIterTmp tmp;
144 WRegion *reg;
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);
161 /*}}}*/
164 /*{{{ Node lookup etc. */
167 WStacking *mplex_find_stacking(WMPlex *mplex, WRegion *reg)
169 WStacking *st;
171 /* Some routines that call us expect us to this check. */
172 if(reg==NULL || REGION_MANAGER(reg)!=(WRegion*)mplex)
173 return NULL;
175 st=ioncore_find_stacking(reg);
177 assert(st==NULL || st->mgr_prev!=NULL);
179 return st;
183 WStacking *mplex_current_node(WMPlex *mplex)
185 WStacking *st=NULL;
186 WRegion *reg;
188 reg=REGION_ACTIVE_SUB(mplex);
189 reg=region_managed_within((WRegion*)mplex, reg);
190 if(reg!=NULL)
191 st=mplex_find_stacking(mplex, reg);
193 if(st!=NULL)
194 return st;
195 else
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);
207 /*}}}*/
210 /*{{{ Exclusive list management and exports */
212 /*EXTL_DOC
213 * Returns the number of objects on the mutually exclusive list of \var{mplex}.
215 EXTL_SAFE
216 EXTL_EXPORT_MEMBER
217 int mplex_mx_count(WMPlex *mplex)
219 return mplex->mx_count;
223 /*EXTL_DOC
224 * Returns the managed object currently active within the mutually exclusive
225 * list of \var{mplex}.
227 EXTL_SAFE
228 EXTL_EXPORT_MEMBER
229 WRegion *mplex_mx_current(WMPlex *mplex)
231 WLListNode *lnode=mplex->mx_current;
232 return (lnode==NULL ? NULL : lnode->st->reg);
236 /*EXTL_DOC
237 * Returns the \var{n}:th object on the mutually exclusive
238 * list of \var{mplex}.
240 EXTL_SAFE
241 EXTL_EXPORT_MEMBER
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);
249 /*EXTL_DOC
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.
256 EXTL_SAFE
257 EXTL_EXPORT_MEMBER
258 bool mplex_mx_i(WMPlex *mplex, ExtlFn iterfn)
260 WLListIterTmp tmp;
261 llist_iter_init(&tmp, mplex->mx_list);
263 return extl_iter_objlist_(iterfn, (ObjIterator*)llist_iter_regions, &tmp);
267 /*EXTL_DOC
268 * Iterate over managed regions of \var{mplex} until \var{iterfn} returns
269 * \code{false}.
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.
274 EXTL_SAFE
275 EXTL_EXPORT_MEMBER
276 bool mplex_managed_i(WMPlex *mplex, ExtlFn iterfn)
278 WMPlexIterTmp tmp;
279 mplex_iter_init(&tmp, mplex);
281 return extl_iter_objlist_(iterfn, (ObjIterator*)mplex_iter, &tmp);
285 /*EXTL_DOC
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}
289 * $-1$ & Last. \\
290 * $-2$ & After \fnref{WMPlex.mx_current}. \\
291 * \end{tabularx}
293 EXTL_EXPORT_MEMBER
294 void mplex_set_index(WMPlex *mplex, WRegion *reg, int index)
296 WLListNode *lnode, *after;
297 WStacking *node;
299 node=mplex_find_stacking(mplex, reg);
301 if(node==NULL)
302 return;
304 lnode=node->lnode;
306 if(lnode==NULL){
307 lnode=ALLOC(WLListNode);
308 if(lnode==NULL)
309 return;
310 lnode->next=NULL;
311 lnode->prev=NULL;
312 lnode->phs=NULL;
313 lnode->st=node;
314 node->lnode=lnode;
315 mplex->mx_count++;
316 }else{
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);
327 /*EXTL_DOC
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,
330 * -1 is returned.
332 EXTL_SAFE
333 EXTL_EXPORT_MEMBER
334 int mplex_get_index(WMPlex *mplex, WRegion *reg)
336 WLListIterTmp tmp;
337 WLListNode *lnode;
338 int index=0;
340 FOR_ALL_NODES_ON_LLIST(lnode, mplex->mx_list, tmp){
341 if(reg==lnode->st->reg)
342 return index;
343 index++;
346 return -1;
350 /*EXTL_DOC
351 * Move \var{r} ``right'' within objects managed by \var{mplex} on list 1.
353 EXTL_EXPORT_MEMBER
354 void mplex_inc_index(WMPlex *mplex, WRegion *r)
356 if(r==NULL)
357 r=mplex_mx_current(mplex);
358 if(r!=NULL)
359 mplex_set_index(mplex, r, mplex_get_index(mplex, r)+1);
363 /*EXTL_DOC
364 * Move \var{r} ``left'' within objects managed by \var{mplex} on list 1.
366 EXTL_EXPORT_MEMBER
367 void mplex_dec_index(WMPlex *mplex, WRegion *r)
369 if(r==NULL)
370 r=mplex_mx_current(mplex);
371 if(r!=NULL)
372 mplex_set_index(mplex, r, mplex_get_index(mplex, r)-1);
376 /*}}}*/
379 /*{{{ Mapping */
382 static void mplex_map_mgd(WMPlex *mplex)
384 WMPlexIterTmp tmp;
385 WStacking *node;
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)
396 WMPlexIterTmp tmp;
397 WStacking *node;
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);
429 /*}}}*/
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))
441 return FALSE;
443 if(wchg || hchg){
444 mplex_fit_managed(mplex);
445 mplex_size_changed(mplex, wchg, hchg);
448 return TRUE;
452 void mplex_do_fit_managed(WMPlex *mplex, WFitParams *fp)
454 WMPlexIterTmp tmp;
455 WStacking *node;
456 WFitParams fp2;
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){
470 fp2=*fp;
471 sizepolicy(&node->szplcy, node->reg, NULL, 0, &fp2);
472 region_fitrep(node->reg, NULL, &fp2);
478 void mplex_fit_managed(WMPlex *mplex)
480 WFitParams fp;
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,
491 WRectangle *geomret)
493 WFitParams fp;
494 WStacking *node;
496 node=mplex_find_stacking(mplex, sub);
498 assert(node!=NULL);
500 fp.mode=0;
501 mplex_managed_geom(mplex, &fp.g);
503 sizepolicy(&node->szplcy, sub, &rq->geom, rq->flags, &fp);
505 if(geomret!=NULL)
506 *geomret=fp.g;
508 if(!(rq->flags&REGION_RQGEOM_TRYONLY))
509 region_fitrep(sub, NULL, &fp);
513 void mplex_set_szplcy(WMPlex *mplex, WRegion *sub, WSizePolicy szplcy)
515 WStacking *node;
517 node=mplex_find_stacking(mplex, sub);
519 if(node!=NULL)
520 node->szplcy=szplcy;
524 WSizePolicy mplex_get_szplcy(WMPlex *mplex, WRegion *sub)
526 WStacking *node;
528 node=mplex_find_stacking(mplex, sub);
530 return (node==NULL ? SIZEPOLICY_DEFAULT : node->szplcy);
534 /*}}}*/
537 /*{{{ Focus */
540 typedef struct{
541 WMPlex *mplex;
542 WStacking *to_try;
543 WStacking *group_st;
544 PtrList **hidelist;
545 bool try_hard;
546 } FiltData;
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);
559 return (reg==NULL
560 ? NULL
561 : (reg==st->reg
562 ? 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_;
574 WStacking *stw;
576 if(st->reg==NULL || !REGION_IS_MAPPED(st->reg))
577 return FALSE;
579 if(!data->hidelist
580 || (data->to_try==NULL && data->group_st==NULL)
581 || st->level<STACKING_LEVEL_MODAL1){
582 return TRUE;
585 /* Ok, modal node in the way. Let's see if it is pseudomodal
586 * and can be hidden.
589 stw=stacking_within(data->mplex, st);
591 /* This should not happen */
592 if(stw==NULL || stw->reg==NULL)
593 return FALSE;
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)
599 return TRUE;
601 if(IS_PSEUDOMODAL(stw)){
602 /* Don't insert multiple times. */
603 return !ptrlist_reinsert_first(data->hidelist, stw);
606 return TRUE;
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,
620 WStacking *to_try,
621 WStacking *group_st,
622 PtrList **hidelist)
624 WStackingFilter *fi=mapped_pseudomodal_include_filt;
625 WStackingFilter *fa=mgr_pseudomodal_approve_filt;
626 WStacking *stacking=mplex_get_stacking(mplex);
627 FiltData data;
628 WStacking *st;
630 if(stacking==NULL)
631 return NULL;
633 if(to_try!=NULL && (to_try->reg==NULL || !REGION_IS_MAPPED(to_try->reg)))
634 to_try=NULL;
636 data.mplex=mplex;
637 data.to_try=to_try;
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);
646 return st;
650 static WStacking *mplex_do_to_focus_on(WMPlex *mplex, WStacking *node,
651 WStacking *to_try,
652 PtrList **hidelist, bool *within)
654 WGroup *grp=OBJ_CAST(node->reg, WGroup);
655 WStacking *st;
657 if(grp!=NULL){
658 if(to_try==NULL)
659 to_try=grp->current_managed;
660 /* Only will return stuff within 'node' */
661 st=mplex_find_to_focus(mplex, to_try, node, hidelist);
662 if(st!=NULL){
663 if(within!=NULL)
664 *within=TRUE;
665 return st;
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))
675 st=node;
677 if(st==node && within!=NULL)
678 *within=TRUE;
680 return st;
684 static WStacking *maybe_focusable(WRegion *reg)
686 if(reg==NULL || !REGION_IS_MAPPED(reg))
687 return NULL;
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)
707 WStacking *foc=NULL;
708 WRegion *reg=NULL;
710 foc=maybe_focusable(REGION_ACTIVE_SUB(mplex));
712 if(foc==NULL){
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);
716 if(foc!=NULL)
717 break;
721 if(foc!=NULL){
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);
726 }else{
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);
737 if(st==NULL){
738 st=(mplex->mx_current!=NULL
739 ? mplex->mx_current->st
740 : NULL);
743 if(st!=NULL){
744 region_do_set_focus(st->reg, warp);
745 return;
749 window_do_set_focus((WWindow*)mplex, warp);
753 static void mplex_refocus(WMPlex *mplex, WStacking *node, bool warp)
755 bool within=FALSE;
756 WStacking *foc=NULL;
758 if(node!=NULL)
759 foc=mplex_do_to_focus_on(mplex, node, NULL, NULL, &within);
761 if(foc==NULL || !within)
762 foc=mplex_to_focus(mplex);
764 if(foc!=NULL)
765 region_maybewarp(foc->reg, warp);
769 /*}}}*/
772 /*{{{ Switch */
775 static void mplex_do_remanage_stdisp(WMPlex *mplex, WRegion *sub)
777 WRegion *stdisp=(WRegion*)(mplex->stdispwatch.obj);
779 /* Move stdisp */
780 if(sub!=NULL && CAN_MANAGE_STDISP(sub)){
781 if(stdisp!=NULL){
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));
791 }else{
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
802 : NULL));
806 static void mplex_do_node_display(WMPlex *mplex, WStacking *node,
807 bool call_changed)
809 WRegion *sub=node->reg;
810 WLListNode *mxc=mplex->mx_current;
811 WFitParams fp;
813 if(!STACKING_IS_HIDDEN(node))
814 return;
816 if(node->lnode!=NULL && node->lnode!=mxc)
817 mplex_do_remanage_stdisp(mplex, sub);
819 node->hidden=FALSE;
821 if(SUBS_MAY_BE_MAPPED(mplex))
822 region_map(sub);
823 else
824 region_unmap(sub);
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){
835 if(mxc!=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;
846 /* Ugly hack:
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);
855 if(grp!=NULL){
856 WRegion *bottom=group_bottom(grp);
857 if(bottom!=NULL){
858 region_managed_rqorder((WRegion*)grp, bottom,
859 REGION_ORDER_BACK);
864 if(call_changed)
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&REGION_GOTO_ENTERWINDOW);
875 PtrList *hidelist=NULL;
876 PtrList **hidelistp=(ew ? NULL : &hidelist);
877 WStacking *foc;
878 /*bool within=FALSE;*/
880 if(sub==NULL && node==NULL)
881 return FALSE;
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))
888 return FALSE;
890 foc=mplex_do_to_focus_on(mplex, node, sub, hidelistp, NULL /*&within*/);
892 if(foc!=NULL){
893 while(hidelist!=NULL){
894 WStacking *st=(WStacking*)ptrlist_take_first(&hidelist);
895 st->hidden=TRUE;
896 region_unmap(st->reg);
899 if(ioncore_g.autoraise &&
900 !(flags&REGION_GOTO_ENTERWINDOW) &&
901 foc->level>STACKING_LEVEL_BOTTOM){
902 WStacking **stackingp=mplex_get_stackingp(mplex);
903 stacking_restack(stackingp, foc, None, NULL, NULL, FALSE);
906 res->reg=foc->reg;
907 res->flags=flags;
909 return (foc==sub || (sub==NULL && foc==node));
910 }else{
911 return FALSE;
916 bool mplex_managed_prepare_focus(WMPlex *mplex, WRegion *disp,
917 int flags, WPrepareFocusResult *res)
919 WStacking *node=mplex_find_stacking(mplex, disp);
921 if(node==NULL)
922 return FALSE;
923 else
924 return mplex_do_prepare_focus(mplex, node, NULL, flags, res);
928 /*}}}*/
931 /*{{{ Switch exports */
934 static void do_switch(WMPlex *mplex, WLListNode *lnode)
936 WStacking *node=(lnode!=NULL ? lnode->st : NULL);
938 if(node!=NULL){
939 bool mcf=region_may_control_focus((WRegion*)mplex);
941 mplex_do_node_display(mplex, node, TRUE);
943 if(mcf)
944 mplex_refocus(mplex, node, TRUE);
949 /*EXTL_DOC
950 * Have \var{mplex} display the \var{n}:th object managed by it.
952 EXTL_EXPORT_MEMBER
953 void mplex_switch_nth(WMPlex *mplex, uint n)
955 do_switch(mplex, llist_nth_node(mplex->mx_list, n));
959 /*EXTL_DOC
960 * Have \var{mplex} display next (wrt. currently selected) object managed
961 * by it.
963 EXTL_EXPORT_MEMBER
964 void mplex_switch_next(WMPlex *mplex)
966 do_switch(mplex, LIST_NEXT_WRAP(mplex->mx_list, mplex->mx_current,
967 next, prev));
971 /*EXTL_DOC
972 * Have \var{mplex} display previous (wrt. currently selected) object
973 * managed by it.
975 EXTL_EXPORT_MEMBER
976 void mplex_switch_prev(WMPlex *mplex)
978 do_switch(mplex, LIST_PREV_WRAP(mplex->mx_list, mplex->mx_current,
979 next, prev));
982 /*EXTL_DOC
983 * Have \var{mplex} display the given child window already added to the mplex
985 EXTL_EXPORT_MEMBER
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;
998 if(node==NULL)
999 return FALSE;
1001 hidden=STACKING_IS_HIDDEN(node);
1002 nhidden=libtu_do_setparam(sp, hidden);
1004 if(!hidden && nhidden){
1005 node->hidden=TRUE;
1007 if(REGION_IS_MAPPED(mplex) && !MPLEX_MGD_UNVIEWABLE(mplex))
1008 region_unmap(reg);
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);
1022 /*EXTL_DOC
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));
1035 /*EXTL_DOC
1036 * Is \var{reg} on within \var{mplex} and hidden?
1038 EXTL_SAFE
1039 EXTL_EXPORT_MEMBER
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));
1048 /*}}}*/
1051 /*{{{ Navigation */
1054 static WStacking *mplex_nxt(WMPlex *mplex, WStacking *st, bool wrap)
1056 return (st->mgr_next!=NULL
1057 ? st->mgr_next
1058 : (wrap ? mplex->mgd : NULL));
1062 static WStacking *mplex_prv(WMPlex *mplex, WStacking *st, bool wrap)
1064 return (st!=mplex->mgd
1065 ? st->mgr_prev
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;
1078 uint min_level=0;
1080 stacking=mplex_get_stacking(mplex);
1082 if(stacking!=NULL)
1083 min_level=stacking_min_level_mapped(stacking);
1085 st=sti;
1086 while(1){
1087 st=fn(mplex, st, wrap);
1089 if(st==NULL || (st==sti && !sti_ok))
1090 break;
1092 if(!st->hidden){
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)
1097 return res;
1098 }else{
1099 if(st->level>=min_level && !PASSIVE(st))
1100 return region_navi_cont((WRegion*)mplex, st->reg, data);
1104 if(st==sti)
1105 break;
1108 return NULL;
1112 WRegion *mplex_navi_first(WMPlex *mplex, WRegionNavi nh,
1113 WRegionNaviData *data)
1115 WStacking *lst=mplex->mgd;
1116 WRegion *res=NULL;
1118 if(lst!=NULL){
1119 if(nh==REGION_NAVI_ANY){
1120 /* ? */
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);
1126 }else{
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)
1138 WStacking *st;
1139 WRegion *res;
1141 if(rel!=NULL){
1142 st=mplex_find_stacking(mplex, rel);
1143 if(st==NULL)
1144 return NULL;
1145 }else if(mplex->mx_current!=NULL){
1146 st=mplex->mx_current->st;
1147 }else{
1148 return mplex_navi_first(mplex, nh, data);
1151 if(nh==REGION_NAVI_ANY){
1152 /* ? */
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);
1158 }else{
1159 res=do_navi(mplex, st, mplex_prv, data, FALSE, FALSE);
1162 return region_navi_cont((WRegion*)mplex, res, data);
1166 /*}}}*/
1169 /*{{{ Stacking */
1172 bool mplex_managed_rqorder(WMPlex *mplex, WRegion *reg, WRegionOrder order)
1174 WStacking **stackingp=mplex_get_stackingp(mplex);
1175 WStacking *st;
1177 if(stackingp==NULL || *stackingp==NULL)
1178 return FALSE;
1180 st=mplex_find_stacking(mplex, reg);
1182 if(st==NULL)
1183 return FALSE;
1185 stacking_restack(stackingp, st, None, NULL, NULL,
1186 (order!=REGION_ORDER_FRONT));
1188 return TRUE;
1192 /*}}}*/
1195 /*{{{ Attach */
1198 static bool mplex_stack(WMPlex *mplex, WStacking *st)
1200 WStacking *tmp=NULL;
1201 WStacking **stackingp=mplex_get_stackingp(mplex);
1203 if(stackingp==NULL)
1204 return FALSE;
1206 LINK_ITEM_FIRST(tmp, st, next, prev);
1207 stacking_weave(stackingp, &tmp, FALSE);
1208 assert(tmp==NULL);
1210 return TRUE;
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;
1234 WSizePolicy szplcy;
1235 uint level;
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)
1243 ? param->szplcy
1244 : (param->flags&MPLEX_ATTACH_UNNUMBERED
1245 ? SIZEPOLICY_FULL_BOUNDS
1246 : SIZEPOLICY_FULL_EXACT));
1248 level=(param->flags&MPLEX_ATTACH_LEVEL
1249 ? param->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
1256 || !mx_was_empty));
1258 sw=(!hidden && (param->flags&MPLEX_ATTACH_SWITCHTO
1259 || (param->flags&MPLEX_ATTACH_UNNUMBERED
1260 ? FALSE
1261 : (mplex_current_node(mplex)==NULL))));
1263 hidden=(hidden || (!sw && !(param->flags&MPLEX_ATTACH_UNNUMBERED)));
1265 node=create_stacking();
1267 if(node==NULL)
1268 return FALSE;
1270 if(!(param->flags&MPLEX_ATTACH_UNNUMBERED)){
1271 lnode=ALLOC(WLListNode);
1272 if(lnode==NULL){
1273 stacking_free(node);
1274 return FALSE;
1276 lnode->next=NULL;
1277 lnode->prev=NULL;
1278 lnode->phs=NULL;
1279 lnode->st=node;
1280 node->lnode=lnode;
1283 if(!stacking_assoc(node, reg)){
1284 if(lnode!=NULL){
1285 node->lnode=NULL;
1286 free(lnode);
1288 stacking_free(node);
1289 return FALSE;
1292 node->hidden=TRUE;
1293 node->szplcy=szplcy;
1294 node->level=level;
1295 node->pseudomodal=(param->flags&MPLEX_ATTACH_PSEUDOMODAL ? 1 : 0);
1297 if(lnode!=NULL){
1298 WMPlexPHolder *ph2, *phn, *php;
1300 llist_link_after(&(mplex->mx_list),
1301 (ph!=NULL ? ph->after : NULL),
1302 lnode);
1303 mplex->mx_count++;
1306 /* Move placeholders after new node */
1307 for(php=NULL, ph2=ph; ph2!=NULL; php=ph2, ph2=phn){
1308 phn=ph2->next;
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)){
1324 WFitParams fp;
1326 fp.mode=0;
1327 mplex_managed_geom(mplex, &(fp.g));
1329 sizepolicy(&node->szplcy, reg,
1330 (param->flags&MPLEX_ATTACH_GEOM ? &(param->geom) : NULL),
1331 0, &fp);
1333 if(rectangle_compare(&fp.g, &REGION_GEOM(reg))!=RECTANGLE_SAME)
1334 region_fitrep(reg, NULL, &fp);
1337 if(!hidden)
1338 mplex_do_node_display(mplex, node, FALSE);
1339 else
1340 region_unmap(reg);
1342 if(mcf){
1343 if(sw){
1344 mplex_refocus(mplex, node, FALSE);
1345 }else if(!hidden &&
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);
1353 }else if(!hidden){
1354 region_pointer_focus_hack(reg);
1356 }else if(!hidden){
1357 region_pointer_focus_hack(reg);
1360 if(lnode!=NULL)
1361 mplex_managed_changed(mplex, MPLEX_CHANGE_ADD, sw, reg);
1363 return TRUE;
1367 static void mplex_attach_fp(WMPlex *mplex, const WMPlexAttachParams *param,
1368 WFitParams *fp)
1370 if(param->flags&MPLEX_ATTACH_GEOM)
1371 fp->g=param->geom;
1372 else
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)
1382 WFitParams fp;
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,
1389 (void*)ph, data);
1393 WRegion *mplex_do_attach(WMPlex *mplex, WMPlexAttachParams *param,
1394 WRegionAttachData *data)
1396 WMPlexPHolder *ph;
1397 WRegion *reg;
1399 ph=create_mplexpholder(mplex, NULL, param);
1401 if(ph==NULL)
1402 return NULL;
1404 reg=mplex_do_attach_pholder(mplex, ph, data);
1406 destroy_obj((Obj*)ph);
1408 return reg;
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;
1418 data.u.n.fn=fn;
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| \
1427 MPLEX_ATTACH_INDEX)
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;
1438 data.u.reg=reg;
1440 return mplex_do_attach(mplex, &param, &data);
1444 static void get_params(WMPlex *mplex, ExtlTab tab, int mask,
1445 WMPlexAttachParams *par)
1447 int tmp;
1448 int ok=~mask;
1450 if(ok&MPLEX_ATTACH_LEVEL){
1451 if(extl_table_gets_i(tab, "level", &tmp)){
1452 if(tmp>=0){
1453 par->flags|=MPLEX_ATTACH_LEVEL;
1454 par->level=tmp;
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;
1486 par->szplcy=tmp;
1490 if(extl_table_gets_rectangle(tab, "geom", &par->geom))
1491 par->flags|=MPLEX_ATTACH_GEOM&ok;
1495 /*EXTL_DOC
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}.
1500 EXTL_EXPORT_MEMBER
1501 WRegion *mplex_attach(WMPlex *mplex, WRegion *reg, ExtlTab param)
1503 WMPlexAttachParams par=MPLEXATTACHPARAMS_INIT;
1504 WRegionAttachData data;
1506 if(reg==NULL)
1507 return NULL;
1509 get_params(mplex, param, 0, &par);
1511 data.type=REGION_ATTACH_REPARENT;
1512 data.u.reg=reg;
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;
1526 data.u.tab=param;
1528 return mplex_do_attach(mplex, par, &data);
1532 /*EXTL_DOC
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. \\
1557 * \end{tabularx}
1559 * In addition parameters to the region to be created are passed in this
1560 * same table.
1562 EXTL_EXPORT_MEMBER
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,
1572 WRegion *dropped)
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)){
1578 int rx, ry;
1579 region_rootpos(curr, &rx, &ry);
1580 if(rectangle_contains(&REGION_GEOM(curr), x-rx, y-ry)){
1581 if(region_handle_drop(curr, x, y, dropped))
1582 return TRUE;
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;
1595 WPHolder *ph=NULL;
1596 WMPlexPHolder *mph;
1598 /* Check current */ {
1599 WStacking *cur=mplex_current_node(mplex);
1601 if(cur!=NULL){
1602 ph=region_prepare_manage(cur->reg, cwin, param, cpriority);
1603 if(ph!=NULL)
1604 return ph;
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);
1610 if(ph!=NULL)
1611 return ph;
1615 if(!MANAGE_PRIORITY_OK(priority, MANAGE_PRIORITY_NORMAL))
1616 return NULL;
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);
1624 if(mph!=NULL){
1625 WGroupPHolder *gph;
1626 WGroupAttachParams gp=GROUPATTACHPARAMS_INIT;
1628 gp.switchto_set=1;
1629 gp.switchto=1;
1630 gp.bottom=1;
1632 gph=create_grouppholder(NULL, NULL, &gp);
1634 if(gph!=NULL){
1635 gph->recreate_pholder=(WPHolder*)mph;
1636 return (WPHolder*)gph;
1640 return (WPHolder*)mph;
1644 /*}}}*/
1647 /*{{{ Remove */
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);
1658 if(stdisp!=NULL){
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);
1668 if(node==NULL)
1669 return;
1671 hadfocus=(mplex_current_node(mplex)==node);
1673 if(node->lnode!=NULL){
1674 if(mplex->mx_current==node->lnode){
1675 WLListNode *lnext;
1677 mplex->mx_current=NULL;
1678 lnext=LIST_PREV(mplex->mx_list, node->lnode, next, prev);
1679 if(lnext==NULL){
1680 lnext=LIST_NEXT(mplex->mx_list, node->lnode, next, prev);
1681 if(lnext==node->lnode)
1682 lnext=NULL;
1684 if(lnext!=NULL)
1685 next=lnext->st;
1688 mplex_move_phs_before(mplex, node->lnode);
1689 llist_unlink(&(mplex->mx_list), node->lnode);
1690 mplex->mx_count--;
1692 free(node->lnode);
1693 node->lnode=NULL;
1694 mx=TRUE;
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))
1707 return;
1709 if(next!=NULL)
1710 mplex_do_node_display(mplex, next, FALSE);
1712 if(hadfocus && mcf)
1713 mplex_refocus(mplex, next, FALSE);
1715 if(mx)
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);
1729 /*}}}*/
1732 /*{{{ Rescue */
1735 bool mplex_rescue_clientwins(WMPlex *mplex, WRescueInfo *info)
1737 bool ret1, ret2;
1738 WMPlexIterTmp tmp;
1739 WLListIterTmp ltmp;
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,
1757 &tmp);
1759 ret2=region_rescue_child_clientwins((WRegion*)mplex, info);
1761 return (ret1 && ret2);
1765 /*}}}*/
1768 /*{{{ Status display support */
1771 bool mplex_set_stdisp(WMPlex *mplex, WRegion *reg,
1772 const WMPlexSTDispInfo *din)
1774 WRegion *oldstdisp=(WRegion*)(mplex->stdispwatch.obj);
1775 WRegion *mgr=NULL;
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))
1785 mgr=NULL;
1788 if(din!=NULL)
1789 mplex->stdispinfo=*din;
1791 if(reg==NULL){
1792 watch_reset(&(mplex->stdispwatch));
1794 if(mgr!=NULL){
1795 region_unmanage_stdisp(mgr, TRUE, FALSE);
1796 if(oldstdisp!=NULL)
1797 region_detach_manager(oldstdisp);
1799 }else{
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);
1808 return TRUE;
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},
1824 {NULL, 0}
1828 static bool do_attach_stdisp(WRegion *UNUSED(mplex), WRegion *UNUSED(reg), void *UNUSED(unused))
1830 /* We do not actually manage the stdisp. */
1831 return TRUE;
1835 /*EXTL_DOC
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
1839 * recognised:
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,
1853 * removed. \\
1854 * \end{tabularx}
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;
1861 char *s;
1863 if(extl_table_gets_s(t, "pos", &s)){
1864 din.pos=stringintmap_value(pos_map, s, -1);
1865 if(din.pos<0){
1866 warn(TR("Invalid position setting."));
1867 return NULL;
1871 extl_table_gets_b(t, "fullsize", &(din.fullsize));
1873 s=NULL;
1874 extl_table_gets_s(t, "action", &s);
1876 if(s==NULL || strcmp(s, "replace")==0){
1877 WRegionAttachData data;
1878 WFitParams fp;
1880 fp.g.x=0;
1881 fp.g.y=0;
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
1887 * if there's one.
1889 extl_table_gets_rectangle(t, "geom", &(fp.g));
1891 data.type=REGION_ATTACH_LOAD;
1892 data.u.tab=t;
1894 stdisp=region_attach_helper((WRegion*)mplex,
1895 (WWindow*)mplex, &fp,
1896 do_attach_stdisp, NULL,
1897 &data);
1899 if(stdisp==NULL)
1900 return 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."));
1906 return FALSE;
1909 if(!mplex_set_stdisp(mplex, stdisp, &din)){
1910 destroy_obj((Obj*)stdisp);
1911 return NULL;
1914 return stdisp;
1918 static ExtlTab mplex_do_get_stdisp_extl(WMPlex *mplex, bool fullconfig)
1920 WRegion *reg=(WRegion*)mplex->stdispwatch.obj;
1921 ExtlTab t;
1923 if(reg==NULL)
1924 return extl_table_none();
1926 if(fullconfig){
1927 t=region_get_configuration(reg);
1928 extl_table_sets_rectangle(t, "geom", &REGION_GEOM(reg));
1929 }else{
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);
1939 return t;
1943 /*EXTL_DOC
1944 * Get status display information. See \fnref{WMPlex.get_stdisp} for
1945 * information on the fields.
1947 EXTL_SAFE
1948 EXTL_EXPORT_AS(WMPlex, get_stdisp)
1949 ExtlTab mplex_get_stdisp_extl(WMPlex *mplex)
1951 return mplex_do_get_stdisp_extl(mplex, FALSE);
1955 /*}}}*/
1958 /*{{{ Dynfuns */
1961 void mplex_managed_geom_default(const WMPlex *mplex, WRectangle *geom)
1963 geom->x=0;
1964 geom->y=0;
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));
1992 return idx;
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));
2011 /*}}}*/
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)
2022 return "reorder";
2023 else if(mode==MPLEX_CHANGE_ADD)
2024 return "add";
2025 else if(mode==MPLEX_CHANGE_REMOVE)
2026 return "remove";
2027 return NULL;
2031 static bool mrsh_chg(ExtlFn fn, WMPlexChangedParams *p)
2033 ExtlTab t=extl_create_table();
2034 bool ret;
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);
2041 extl_protect(NULL);
2042 ret=extl_call(fn, "t", NULL, t);
2043 extl_unprotect(NULL);
2045 extl_unref_table(t);
2047 return ret;
2051 void mplex_call_changed_hook(WMPlex *mplex, WHook *hook,
2052 int mode, bool sw, WRegion *reg)
2054 WMPlexChangedParams p;
2056 p.reg=mplex;
2057 p.mode=mode;
2058 p.sw=sw;
2059 p.sub=reg;
2061 hook_call_p(hook, &p, (WHookMarshallExtl*)mrsh_chg);
2065 /*}}} */
2068 /*{{{ Save/load */
2071 static void save_node(WMPlex *mplex, ExtlTab subs, int *n,
2072 WStacking *node, bool unnumbered)
2074 ExtlTab st, g;
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(&REGION_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);
2091 if(unnumbered)
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*/;
2103 WMPlexIterTmp tmp;
2104 WLListIterTmp ltmp;
2105 WLListNode *lnode;
2106 WStacking *node;
2107 int n=0;
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);
2132 return tab;
2136 void mplex_load_contents(WMPlex *mplex, ExtlTab tab)
2138 ExtlTab substab, subtab;
2139 int n, i;
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;
2152 WFitParams fp;
2153 WPHolder *ph;
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);
2163 if(ph!=NULL){
2164 region_attach_load_helper((WRegion*)mplex, (WWindow*)mplex, &fp,
2165 (WRegionDoAttachFn*)mplex_do_attach_final,
2166 (void*)ph, subtab, &ph);
2168 if(ph!=NULL)
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);
2183 if(mplex!=NULL)
2184 mplex_load_contents(mplex, tab);
2185 return (WRegion*)mplex;
2189 /*}}}*/
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},
2250 END_DYNFUNTAB
2254 EXTL_EXPORT
2255 IMPLCLASS(WMPlex, WWindow, mplex_deinit, mplex_dynfuntab);
2258 /*}}}*/