Compile warning
[notion/jeffpc.git] / ioncore / mplex.c
blobb6b806b7ad91709bb09c5064aab8cc723cca68ad
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 WRectangle geom;
455 WMPlexIterTmp tmp;
456 WStacking *node;
457 WFitParams fp2;
459 if(!MPLEX_MGD_UNVIEWABLE(mplex) && (fp->g.w<=1 || fp->g.h<=1)){
460 mplex->flags|=MPLEX_MANAGED_UNVIEWABLE;
461 if(REGION_IS_MAPPED(mplex))
462 mplex_unmap_mgd(mplex);
463 }else if(MPLEX_MGD_UNVIEWABLE(mplex) && !(fp->g.w<=1 || fp->g.h<=1)){
464 mplex->flags&=~MPLEX_MANAGED_UNVIEWABLE;
465 if(REGION_IS_MAPPED(mplex))
466 mplex_map_mgd(mplex);
469 if(!MPLEX_MGD_UNVIEWABLE(mplex)){
470 FOR_ALL_NODES_IN_MPLEX(mplex, node, tmp){
471 fp2=*fp;
472 sizepolicy(&node->szplcy, node->reg, NULL, 0, &fp2);
473 region_fitrep(node->reg, NULL, &fp2);
479 void mplex_fit_managed(WMPlex *mplex)
481 WFitParams fp;
483 fp.mode=REGION_FIT_EXACT;
484 mplex_managed_geom(mplex, &(fp.g));
486 mplex_do_fit_managed(mplex, &fp);
490 static void mplex_managed_rqgeom(WMPlex *mplex, WRegion *sub,
491 const WRQGeomParams *rq,
492 WRectangle *geomret)
494 WRectangle rg;
495 WFitParams fp;
496 WStacking *node;
498 node=mplex_find_stacking(mplex, sub);
500 assert(node!=NULL);
502 fp.mode=0;
503 mplex_managed_geom(mplex, &fp.g);
505 sizepolicy(&node->szplcy, sub, &rq->geom, rq->flags, &fp);
507 if(geomret!=NULL)
508 *geomret=fp.g;
510 if(!(rq->flags&REGION_RQGEOM_TRYONLY))
511 region_fitrep(sub, NULL, &fp);
515 void mplex_set_szplcy(WMPlex *mplex, WRegion *sub, WSizePolicy szplcy)
517 WStacking *node;
519 node=mplex_find_stacking(mplex, sub);
521 if(node!=NULL)
522 node->szplcy=szplcy;
526 WSizePolicy mplex_get_szplcy(WMPlex *mplex, WRegion *sub)
528 WStacking *node;
530 node=mplex_find_stacking(mplex, sub);
532 return (node==NULL ? SIZEPOLICY_DEFAULT : node->szplcy);
536 /*}}}*/
539 /*{{{ Focus */
542 typedef struct{
543 WMPlex *mplex;
544 WStacking *to_try;
545 WStacking *group_st;
546 PtrList **hidelist;
547 bool try_hard;
548 } FiltData;
551 static WRegion *manager_within(WMPlex *mplex, WStacking *st)
553 return region_managed_within((WRegion*)mplex, st->reg);
557 static WStacking *stacking_within(WMPlex *mplex, WStacking *st)
559 WRegion *reg=manager_within(mplex, st);
561 return (reg==NULL
562 ? NULL
563 : (reg==st->reg
564 ? st
565 : ioncore_find_stacking(reg)));
569 /* Mutually exclusive regions can't be pseudomodal */
570 #define IS_PSEUDOMODAL(ST) ((ST)->lnode==NULL && (ST)->pseudomodal)
573 static bool mapped_pseudomodal_include_filt(WStacking *st, void *data_)
575 FiltData *data=(FiltData*)data_;
576 WStacking *stw;
578 if(st->reg==NULL || !REGION_IS_MAPPED(st->reg))
579 return FALSE;
581 if(!data->hidelist
582 || (data->to_try==NULL && data->group_st==NULL)
583 || st->level<STACKING_LEVEL_MODAL1){
584 return TRUE;
587 /* Ok, modal node in the way. Let's see if it is pseudomodal
588 * and can be hidden.
591 stw=stacking_within(data->mplex, st);
593 /* This should not happen */
594 if(stw==NULL || stw->reg==NULL)
595 return FALSE;
597 /* The node is within the same group, so it can not be hidden.
598 * Latter case should not happen.
600 if(stw==data->group_st || stw==data->to_try)
601 return TRUE;
603 if(IS_PSEUDOMODAL(stw)){
604 /* Don't insert multiple times. */
605 return !ptrlist_reinsert_first(data->hidelist, stw);
608 return TRUE;
612 static bool mgr_pseudomodal_approve_filt(WStacking *st, void *data_)
614 FiltData *data=(FiltData*)data_;
616 return (data->group_st==NULL || st==data->group_st ||
617 manager_within(data->mplex, st)==data->group_st->reg);
621 WStacking *mplex_find_to_focus(WMPlex *mplex,
622 WStacking *to_try,
623 WStacking *group_st,
624 PtrList **hidelist)
626 WStackingFilter *fi=mapped_pseudomodal_include_filt;
627 WStackingFilter *fa=mgr_pseudomodal_approve_filt;
628 WStacking *stacking=mplex_get_stacking(mplex);
629 FiltData data;
630 WStacking *st;
632 if(stacking==NULL)
633 return NULL;
635 if(to_try!=NULL && (to_try->reg==NULL || !REGION_IS_MAPPED(to_try->reg)))
636 to_try=NULL;
638 data.mplex=mplex;
639 data.to_try=to_try;
640 data.group_st=group_st;
641 data.hidelist=hidelist;
643 st=stacking_find_to_focus(stacking, to_try, fi, fa, &data);
645 if(st==NULL && hidelist!=NULL)
646 ptrlist_clear(hidelist);
648 return st;
652 static WStacking *mplex_do_to_focus_on(WMPlex *mplex, WStacking *node,
653 WStacking *to_try,
654 PtrList **hidelist, bool *within)
656 WGroup *grp=OBJ_CAST(node->reg, WGroup);
657 WStacking *st;
659 if(grp!=NULL){
660 if(to_try==NULL)
661 to_try=grp->current_managed;
662 /* Only will return stuff within 'node' */
663 st=mplex_find_to_focus(mplex, to_try, node, hidelist);
664 if(st!=NULL){
665 if(within!=NULL)
666 *within=TRUE;
667 return st;
671 st=mplex_find_to_focus(mplex, node, NULL, hidelist);
673 /* If 'node' points to a group, it isn't actually on the stacking list.
674 * Give it the focus, if there's nothing "proper" that could be focussed.
676 if(st==NULL && grp!=NULL && REGION_IS_MAPPED(grp))
677 st=node;
679 if(st==node && within!=NULL)
680 *within=TRUE;
682 return st;
686 static WStacking *maybe_focusable(WRegion *reg)
688 if(reg==NULL || !REGION_IS_MAPPED(reg))
689 return NULL;
691 return ioncore_find_stacking(reg);
695 static WStacking *has_stacking_within(WMPlex *mplex, WRegion *reg)
697 while(reg!=NULL && REGION_MANAGER(reg)!=(WRegion*)mplex)
698 reg=REGION_MANAGER(reg);
700 return maybe_focusable(reg);
704 /* 1. Try keep focus in REGION_ACTIVE_SUB.
705 * 2. Choose something else, attempting previous in focus history.
707 static WStacking *mplex_to_focus(WMPlex *mplex)
709 WStacking *foc=NULL, *fallback=NULL;
710 WRegion *reg=NULL;
712 foc=maybe_focusable(REGION_ACTIVE_SUB(mplex));
714 if(foc==NULL){
715 /* Search focus history if no specific attempt set.*/
716 for(reg=ioncore_g.focus_current; reg!=NULL; reg=reg->active_next){
717 foc=has_stacking_within(mplex, reg);
718 if(foc!=NULL)
719 break;
723 if(foc!=NULL){
724 /* In the history search case, 'foc' might point to a group,
725 * since we don't properly try to find a stacking within it...
727 return mplex_do_to_focus_on(mplex, foc, NULL, NULL, NULL);
728 }else{
729 return mplex_find_to_focus(mplex, NULL, NULL, NULL);
734 void mplex_do_set_focus(WMPlex *mplex, bool warp)
736 if(!MPLEX_MGD_UNVIEWABLE(mplex)){
737 WStacking *st=mplex_to_focus(mplex);
739 if(st==NULL){
740 st=(mplex->mx_current!=NULL
741 ? mplex->mx_current->st
742 : NULL);
745 if(st!=NULL){
746 region_do_set_focus(st->reg, warp);
747 return;
751 window_do_set_focus((WWindow*)mplex, warp);
755 static void mplex_refocus(WMPlex *mplex, WStacking *node, bool warp)
757 bool within=FALSE;
758 WStacking *foc=NULL;
760 if(node!=NULL)
761 foc=mplex_do_to_focus_on(mplex, node, NULL, NULL, &within);
763 if(foc==NULL || !within)
764 foc=mplex_to_focus(mplex);
766 if(foc!=NULL)
767 region_maybewarp(foc->reg, warp);
771 /*}}}*/
774 /*{{{ Switch */
777 static void mplex_do_remanage_stdisp(WMPlex *mplex, WRegion *sub)
779 WRegion *stdisp=(WRegion*)(mplex->stdispwatch.obj);
781 /* Move stdisp */
782 if(sub!=NULL && CAN_MANAGE_STDISP(sub)){
783 if(stdisp!=NULL){
784 WRegion *omgr=REGION_MANAGER(stdisp);
785 if(omgr!=sub && omgr!=NULL){
786 if(CAN_MANAGE_STDISP(omgr))
787 region_unmanage_stdisp(omgr, FALSE, FALSE);
788 region_detach_manager(stdisp);
791 region_manage_stdisp(sub, stdisp,
792 &(mplex->stdispinfo));
793 }else{
794 region_unmanage_stdisp(sub, TRUE, FALSE);
800 void mplex_remanage_stdisp(WMPlex *mplex)
802 mplex_do_remanage_stdisp(mplex, (mplex->mx_current!=NULL
803 ? mplex->mx_current->st->reg
804 : NULL));
808 static void mplex_do_node_display(WMPlex *mplex, WStacking *node,
809 bool call_changed)
811 WRegion *sub=node->reg;
812 WLListNode *mxc=mplex->mx_current;
813 WFitParams fp;
815 if(!STACKING_IS_HIDDEN(node))
816 return;
818 if(node->lnode!=NULL && node->lnode!=mxc)
819 mplex_do_remanage_stdisp(mplex, sub);
821 node->hidden=FALSE;
823 if(SUBS_MAY_BE_MAPPED(mplex))
824 region_map(sub);
825 else
826 region_unmap(sub);
828 /* the mplex might have been resized while this window was invisible,
829 * and the client window might have had lazy resizing enabled.
831 fp.mode=REGION_FIT_EXACT;
832 mplex_managed_geom(mplex, &(fp.g));
833 sizepolicy(&node->szplcy, node->reg, NULL, 0, &fp);
834 region_fitrep(node->reg, NULL, &fp);
836 if(node->lnode!=NULL){
837 if(mxc!=NULL){
838 /* Hide current mx region. We do it after mapping the
839 * new one to avoid flicker.
841 if(REGION_IS_MAPPED(mplex))
842 region_unmap(mxc->st->reg);
843 mxc->st->hidden=TRUE;
846 mplex->mx_current=node->lnode;
848 /* Ugly hack:
849 * Many programs will get upset if the visible, although only
850 * such, client window is not the lowest window in the mplex.
851 * xprop/xwininfo will return the information for the lowest
852 * window. 'netscape -remote' will not work at all if there are
853 * no visible netscape windows.
856 WGroup *grp=(WGroup*)OBJ_CAST(sub, WGroupCW);
857 if(grp!=NULL){
858 WRegion *bottom=group_bottom(grp);
859 if(bottom!=NULL){
860 region_managed_rqorder((WRegion*)grp, bottom,
861 REGION_ORDER_BACK);
866 if(call_changed)
867 mplex_managed_changed(mplex, MPLEX_CHANGE_SWITCHONLY, TRUE, sub);
872 bool mplex_do_prepare_focus(WMPlex *mplex, WStacking *node,
873 WStacking *sub, int flags,
874 WPrepareFocusResult *res)
876 bool ew=(flags&REGION_GOTO_ENTERWINDOW);
877 PtrList *hidelist=NULL;
878 PtrList **hidelistp=(ew ? NULL : &hidelist);
879 WStacking *foc;
880 /*bool within=FALSE;*/
882 if(sub==NULL && node==NULL)
883 return FALSE;
885 /* Display the node in any case */
886 if(node!=NULL && !ew)
887 mplex_do_node_display(mplex, node, TRUE);
889 if(!region_prepare_focus((WRegion*)mplex, flags, res))
890 return FALSE;
892 foc=mplex_do_to_focus_on(mplex, node, sub, hidelistp, NULL /*&within*/);
894 if(foc!=NULL){
895 while(hidelist!=NULL){
896 WStacking *st=(WStacking*)ptrlist_take_first(&hidelist);
897 st->hidden=TRUE;
898 region_unmap(st->reg);
901 if(ioncore_g.autoraise &&
902 !(flags&REGION_GOTO_ENTERWINDOW) &&
903 foc->level>STACKING_LEVEL_BOTTOM){
904 WStacking **stackingp=mplex_get_stackingp(mplex);
905 stacking_restack(stackingp, foc, None, NULL, NULL, FALSE);
908 res->reg=foc->reg;
909 res->flags=flags;
911 return (foc==sub || (sub==NULL && foc==node));
912 }else{
913 return FALSE;
918 bool mplex_managed_prepare_focus(WMPlex *mplex, WRegion *disp,
919 int flags, WPrepareFocusResult *res)
921 WStacking *node=mplex_find_stacking(mplex, disp);
923 if(node==NULL)
924 return FALSE;
925 else
926 return mplex_do_prepare_focus(mplex, node, NULL, flags, res);
930 /*}}}*/
933 /*{{{ Switch exports */
936 static void do_switch(WMPlex *mplex, WLListNode *lnode)
938 WStacking *node=(lnode!=NULL ? lnode->st : NULL);
940 if(node!=NULL){
941 bool mcf=region_may_control_focus((WRegion*)mplex);
943 mplex_do_node_display(mplex, node, TRUE);
945 if(mcf)
946 mplex_refocus(mplex, node, TRUE);
951 /*EXTL_DOC
952 * Have \var{mplex} display the \var{n}:th object managed by it.
954 EXTL_EXPORT_MEMBER
955 void mplex_switch_nth(WMPlex *mplex, uint n)
957 do_switch(mplex, llist_nth_node(mplex->mx_list, n));
961 /*EXTL_DOC
962 * Have \var{mplex} display next (wrt. currently selected) object managed
963 * by it.
965 EXTL_EXPORT_MEMBER
966 void mplex_switch_next(WMPlex *mplex)
968 do_switch(mplex, LIST_NEXT_WRAP(mplex->mx_list, mplex->mx_current,
969 next, prev));
973 /*EXTL_DOC
974 * Have \var{mplex} display previous (wrt. currently selected) object
975 * managed by it.
977 EXTL_EXPORT_MEMBER
978 void mplex_switch_prev(WMPlex *mplex)
980 do_switch(mplex, LIST_PREV_WRAP(mplex->mx_list, mplex->mx_current,
981 next, prev));
984 /*EXTL_DOC
985 * Have \var{mplex} display the given child window already added to the mplex
987 EXTL_EXPORT_MEMBER
988 void mplex_switch_to(WMPlex *mplex, WRegion *reg)
990 WPrepareFocusResult result;
991 mplex_managed_prepare_focus(mplex, reg->manager, 0, &result);
994 bool mplex_set_hidden(WMPlex *mplex, WRegion *reg, int sp)
996 bool mcf=region_may_control_focus((WRegion*)mplex);
997 WStacking *node=mplex_find_stacking(mplex, reg);
998 bool hidden, nhidden;
1000 if(node==NULL)
1001 return FALSE;
1003 hidden=STACKING_IS_HIDDEN(node);
1004 nhidden=libtu_do_setparam(sp, hidden);
1006 if(!hidden && nhidden){
1007 node->hidden=TRUE;
1009 if(REGION_IS_MAPPED(mplex) && !MPLEX_MGD_UNVIEWABLE(mplex))
1010 region_unmap(reg);
1012 /* lnode -> switch next? */
1013 }else if(hidden && !nhidden){
1014 mplex_do_node_display(mplex, node, TRUE);
1017 if(mcf && !PASSIVE(node))
1018 mplex_refocus(mplex, (nhidden ? NULL : node), TRUE);
1020 return STACKING_IS_HIDDEN(node);
1024 /*EXTL_DOC
1025 * Set the visibility of the region \var{reg} on \var{mplex}
1026 * as specified with the parameter \var{how}
1027 * (one of \codestr{set}, \codestr{unset}, or \codestr{toggle}).
1028 * The resulting state is returned.
1030 EXTL_EXPORT_AS(WMPlex, set_hidden)
1031 bool mplex_set_hidden_extl(WMPlex *mplex, WRegion *reg, const char *how)
1033 return mplex_set_hidden(mplex, reg, libtu_string_to_setparam(how));
1037 /*EXTL_DOC
1038 * Is \var{reg} on within \var{mplex} and hidden?
1040 EXTL_SAFE
1041 EXTL_EXPORT_MEMBER
1042 bool mplex_is_hidden(WMPlex *mplex, WRegion *reg)
1044 WStacking *node=mplex_find_stacking(mplex, reg);
1046 return (node!=NULL && STACKING_IS_HIDDEN(node));
1050 /*}}}*/
1053 /*{{{ Navigation */
1056 static WStacking *mplex_nxt(WMPlex *mplex, WStacking *st, bool wrap)
1058 return (st->mgr_next!=NULL
1059 ? st->mgr_next
1060 : (wrap ? mplex->mgd : NULL));
1064 static WStacking *mplex_prv(WMPlex *mplex, WStacking *st, bool wrap)
1066 return (st!=mplex->mgd
1067 ? st->mgr_prev
1068 : (wrap ? st->mgr_prev : NULL));
1072 typedef WStacking *NxtFn(WMPlex *mplex, WStacking *st, bool wrap);
1075 static WRegion *do_navi(WMPlex *mplex, WStacking *sti,
1076 NxtFn *fn, WRegionNaviData *data,
1077 bool sti_ok, bool wrap)
1079 WStacking *st, *stacking;
1080 uint min_level=0;
1082 stacking=mplex_get_stacking(mplex);
1084 if(stacking!=NULL)
1085 min_level=stacking_min_level_mapped(stacking);
1087 st=sti;
1088 while(1){
1089 st=fn(mplex, st, wrap);
1091 if(st==NULL || (st==sti && !sti_ok))
1092 break;
1094 if(!st->hidden){
1095 if(OBJ_IS(st->reg, WGroup)){
1096 /* WGroup navigation code should respect modal stuff. */
1097 WRegion *res=region_navi_cont((WRegion*)mplex, st->reg, data);
1098 if(res!=NULL && res!=st->reg)
1099 return res;
1100 }else{
1101 if(st->level>=min_level && !PASSIVE(st))
1102 return region_navi_cont((WRegion*)mplex, st->reg, data);
1106 if(st==sti)
1107 break;
1110 return NULL;
1114 WRegion *mplex_navi_first(WMPlex *mplex, WRegionNavi nh,
1115 WRegionNaviData *data)
1117 WStacking *lst=mplex->mgd;
1118 WRegion *res=NULL;
1120 if(lst!=NULL){
1121 if(nh==REGION_NAVI_ANY){
1122 /* ? */
1125 if(nh==REGION_NAVI_ANY || nh==REGION_NAVI_END ||
1126 nh==REGION_NAVI_BOTTOM || nh==REGION_NAVI_RIGHT){
1127 res=do_navi(mplex, lst, mplex_prv, data, TRUE, TRUE);
1128 }else{
1129 res=do_navi(mplex, lst->mgr_prev, mplex_nxt, data, TRUE, TRUE);
1133 return region_navi_cont((WRegion*)mplex, res, data);
1137 WRegion *mplex_navi_next(WMPlex *mplex, WRegion *rel, WRegionNavi nh,
1138 WRegionNaviData *data)
1140 WStacking *st;
1141 WRegion *res;
1143 if(rel!=NULL){
1144 st=mplex_find_stacking(mplex, rel);
1145 if(st==NULL)
1146 return NULL;
1147 }else if(mplex->mx_current!=NULL){
1148 st=mplex->mx_current->st;
1149 }else{
1150 return mplex_navi_first(mplex, nh, data);
1153 if(nh==REGION_NAVI_ANY){
1154 /* ? */
1157 if(nh==REGION_NAVI_ANY || nh==REGION_NAVI_END ||
1158 nh==REGION_NAVI_BOTTOM || nh==REGION_NAVI_RIGHT){
1159 res=do_navi(mplex, st, mplex_nxt, data, FALSE, FALSE);
1160 }else{
1161 res=do_navi(mplex, st, mplex_prv, data, FALSE, FALSE);
1164 return region_navi_cont((WRegion*)mplex, res, data);
1168 /*}}}*/
1171 /*{{{ Stacking */
1174 bool mplex_managed_rqorder(WMPlex *mplex, WRegion *reg, WRegionOrder order)
1176 WStacking **stackingp=mplex_get_stackingp(mplex);
1177 WStacking *st;
1179 if(stackingp==NULL || *stackingp==NULL)
1180 return FALSE;
1182 st=mplex_find_stacking(mplex, reg);
1184 if(st==NULL)
1185 return FALSE;
1187 stacking_restack(stackingp, st, None, NULL, NULL,
1188 (order!=REGION_ORDER_FRONT));
1190 return TRUE;
1194 /*}}}*/
1197 /*{{{ Attach */
1200 static bool mplex_stack(WMPlex *mplex, WStacking *st)
1202 WStacking *tmp=NULL;
1203 WStacking **stackingp=mplex_get_stackingp(mplex);
1205 if(stackingp==NULL)
1206 return FALSE;
1208 LINK_ITEM_FIRST(tmp, st, next, prev);
1209 stacking_weave(stackingp, &tmp, FALSE);
1210 assert(tmp==NULL);
1212 return TRUE;
1216 static void mplex_unstack(WMPlex *mplex, WStacking *st)
1218 WStacking *stacking;
1220 stacking=mplex_get_stacking(mplex);
1222 stacking_unstack(&mplex->win, st);
1226 /* WMPlexWPHolder is used for position marking in order to allow
1227 * WLListNodes be safely removed in the attach handler hnd, that
1228 * could remove something this mplex is managing.
1230 bool mplex_do_attach_final(WMPlex *mplex, WRegion *reg, WMPlexPHolder *ph)
1232 WStacking *node=NULL;
1233 WLListNode *lnode=NULL;
1234 WMPlexAttachParams *param=&ph->param;
1235 bool mx_was_empty, sw, modal, mcf, hidden;
1236 WSizePolicy szplcy;
1237 uint level;
1239 mcf=region_may_control_focus((WRegion*)mplex);
1241 mx_was_empty=(mplex->mx_list==NULL);
1243 szplcy=((param->flags&MPLEX_ATTACH_SIZEPOLICY &&
1244 param->szplcy!=SIZEPOLICY_DEFAULT)
1245 ? param->szplcy
1246 : (param->flags&MPLEX_ATTACH_UNNUMBERED
1247 ? SIZEPOLICY_FULL_BOUNDS
1248 : SIZEPOLICY_FULL_EXACT));
1250 modal=(param->flags&MPLEX_ATTACH_LEVEL
1251 && param->level>=STACKING_LEVEL_MODAL1);
1253 level=(param->flags&MPLEX_ATTACH_LEVEL
1254 ? param->level
1255 : (param->flags&MPLEX_ATTACH_UNNUMBERED
1256 ? STACKING_LEVEL_NORMAL
1257 : STACKING_LEVEL_BOTTOM));
1259 hidden=(param->flags&MPLEX_ATTACH_HIDDEN
1260 && (param->flags&MPLEX_ATTACH_UNNUMBERED
1261 || !mx_was_empty));
1263 sw=(!hidden && (param->flags&MPLEX_ATTACH_SWITCHTO
1264 || (param->flags&MPLEX_ATTACH_UNNUMBERED
1265 ? FALSE
1266 : (mplex_current_node(mplex)==NULL))));
1268 hidden=(hidden || (!sw && !(param->flags&MPLEX_ATTACH_UNNUMBERED)));
1270 node=create_stacking();
1272 if(node==NULL)
1273 return FALSE;
1275 if(!(param->flags&MPLEX_ATTACH_UNNUMBERED)){
1276 lnode=ALLOC(WLListNode);
1277 if(lnode==NULL){
1278 stacking_free(node);
1279 return FALSE;
1281 lnode->next=NULL;
1282 lnode->prev=NULL;
1283 lnode->phs=NULL;
1284 lnode->st=node;
1285 node->lnode=lnode;
1288 if(!stacking_assoc(node, reg)){
1289 if(lnode!=NULL){
1290 node->lnode=NULL;
1291 free(lnode);
1293 stacking_free(node);
1294 return FALSE;
1297 node->hidden=TRUE;
1298 node->szplcy=szplcy;
1299 node->level=level;
1300 node->pseudomodal=(param->flags&MPLEX_ATTACH_PSEUDOMODAL ? 1 : 0);
1302 if(lnode!=NULL){
1303 WMPlexPHolder *ph2, *phn, *php;
1305 llist_link_after(&(mplex->mx_list),
1306 (ph!=NULL ? ph->after : NULL),
1307 lnode);
1308 mplex->mx_count++;
1311 /* Move placeholders after new node */
1312 for(php=NULL, ph2=ph; ph2!=NULL; php=ph2, ph2=phn){
1313 phn=ph2->next;
1314 mplexpholder_move(ph2, mplex, php, lnode);
1318 LINK_ITEM(mplex->mgd, node, mgr_next, mgr_prev);
1320 if(!OBJ_IS(reg, WGroup))
1321 mplex_stack(mplex, node);
1323 region_set_manager(reg, (WRegion*)mplex);
1325 if(param->flags&MPLEX_ATTACH_PASSIVE)
1326 reg->flags|=REGION_SKIP_FOCUS;
1328 if(!(param->flags&MPLEX_ATTACH_WHATEVER)){
1329 WFitParams fp;
1331 fp.mode=0;
1332 mplex_managed_geom(mplex, &(fp.g));
1334 sizepolicy(&node->szplcy, reg,
1335 (param->flags&MPLEX_ATTACH_GEOM ? &(param->geom) : NULL),
1336 0, &fp);
1338 if(rectangle_compare(&fp.g, &REGION_GEOM(reg))!=RECTANGLE_SAME)
1339 region_fitrep(reg, NULL, &fp);
1342 if(!hidden)
1343 mplex_do_node_display(mplex, node, FALSE);
1344 else
1345 region_unmap(reg);
1347 if(mcf){
1348 if(sw){
1349 mplex_refocus(mplex, node, FALSE);
1350 }else if(!hidden &&
1351 (level>=STACKING_LEVEL_MODAL1 || OBJ_IS(reg, WGroup))){
1352 /* New modal regions may require focusing, so try to
1353 * give focus back to currently active object.
1354 * (There seems to be some problem with uncontained
1355 * client windows still..)
1357 mplex_refocus(mplex, NULL, FALSE);
1358 }else if(!hidden){
1359 region_pointer_focus_hack(reg);
1361 }else if(!hidden){
1362 region_pointer_focus_hack(reg);
1365 if(lnode!=NULL)
1366 mplex_managed_changed(mplex, MPLEX_CHANGE_ADD, sw, reg);
1368 return TRUE;
1372 static void mplex_attach_fp(WMPlex *mplex, const WMPlexAttachParams *param,
1373 WFitParams *fp)
1375 if(param->flags&MPLEX_ATTACH_GEOM)
1376 fp->g=param->geom;
1377 else
1378 mplex_managed_geom(mplex, &(fp->g));
1380 fp->mode=REGION_FIT_WHATEVER|REGION_FIT_BOUNDS;
1384 WRegion *mplex_do_attach_pholder(WMPlex *mplex, WMPlexPHolder *ph,
1385 WRegionAttachData *data)
1387 WFitParams fp;
1389 mplex_attach_fp(mplex, &ph->param, &fp);
1391 return region_attach_helper((WRegion*)mplex,
1392 (WWindow*)mplex, &fp,
1393 (WRegionDoAttachFn*)mplex_do_attach_final,
1394 (void*)ph, data);
1398 WRegion *mplex_do_attach(WMPlex *mplex, WMPlexAttachParams *param,
1399 WRegionAttachData *data)
1401 WMPlexPHolder *ph;
1402 WRegion *reg;
1404 ph=create_mplexpholder(mplex, NULL, param);
1406 if(ph==NULL)
1407 return NULL;
1409 reg=mplex_do_attach_pholder(mplex, ph, data);
1411 destroy_obj((Obj*)ph);
1413 return reg;
1417 WRegion *mplex_do_attach_new(WMPlex *mplex, WMPlexAttachParams *param,
1418 WRegionCreateFn *fn, void *fn_param)
1420 WRegionAttachData data;
1422 data.type=REGION_ATTACH_NEW;
1423 data.u.n.fn=fn;
1424 data.u.n.param=fn_param;
1426 return mplex_do_attach(mplex, param, &data);
1430 #define MPLEX_ATTACH_SET_FLAGS (MPLEX_ATTACH_GEOM| \
1431 MPLEX_ATTACH_SIZEPOLICY| \
1432 MPLEX_ATTACH_INDEX)
1435 WRegion *mplex_attach_simple(WMPlex *mplex, WRegion *reg, int flags)
1437 WMPlexAttachParams param;
1438 WRegionAttachData data;
1440 param.flags=flags&~MPLEX_ATTACH_SET_FLAGS;
1442 data.type=REGION_ATTACH_REPARENT;
1443 data.u.reg=reg;
1445 return mplex_do_attach(mplex, &param, &data);
1449 static void get_params(WMPlex *mplex, ExtlTab tab, int mask,
1450 WMPlexAttachParams *par)
1452 int tmp;
1453 char *tmpstr;
1454 int ok=~mask;
1456 if(ok&MPLEX_ATTACH_LEVEL){
1457 if(extl_table_gets_i(tab, "level", &tmp)){
1458 if(tmp>=0){
1459 par->flags|=MPLEX_ATTACH_LEVEL;
1460 par->level=tmp;
1464 if(extl_table_is_bool_set(tab, "modal"))
1465 par->level=maxof(par->level, STACKING_LEVEL_MODAL1);
1468 if(extl_table_is_bool_set(tab, "unnumbered"))
1469 par->flags|=MPLEX_ATTACH_UNNUMBERED&ok;
1471 if(extl_table_is_bool_set(tab, "switchto"))
1472 par->flags|=MPLEX_ATTACH_SWITCHTO&ok;
1474 if(extl_table_is_bool_set(tab, "hidden"))
1475 par->flags|=MPLEX_ATTACH_HIDDEN&ok;
1477 if(extl_table_is_bool_set(tab, "passive"))
1478 par->flags|=MPLEX_ATTACH_PASSIVE&ok;
1480 if(extl_table_is_bool_set(tab, "pseudomodal"))
1481 par->flags|=MPLEX_ATTACH_PSEUDOMODAL&ok;
1483 if(extl_table_gets_i(tab, "index", &(par->index)))
1484 par->flags|=MPLEX_ATTACH_INDEX&ok;
1486 if(ok&MPLEX_ATTACH_SIZEPOLICY){
1487 if(extl_table_gets_sizepolicy(tab, "sizepolicy", &par->szplcy)){
1488 par->flags|=MPLEX_ATTACH_SIZEPOLICY;
1489 }else if(extl_table_gets_i(tab, "sizepolicy", &tmp)){
1490 /* Backwards compat. numeric version */
1491 par->flags|=MPLEX_ATTACH_SIZEPOLICY;
1492 par->szplcy=tmp;
1496 if(extl_table_gets_rectangle(tab, "geom", &par->geom))
1497 par->flags|=MPLEX_ATTACH_GEOM&ok;
1501 /*EXTL_DOC
1502 * Attach and reparent existing region \var{reg} to \var{mplex}.
1503 * The table \var{param} may contain the fields \var{index} and
1504 * \var{switchto} that are interpreted as for \fnref{WMPlex.attach_new}.
1506 EXTL_EXPORT_MEMBER
1507 WRegion *mplex_attach(WMPlex *mplex, WRegion *reg, ExtlTab param)
1509 WMPlexAttachParams par=MPLEXATTACHPARAMS_INIT;
1510 WRegionAttachData data;
1512 if(reg==NULL)
1513 return NULL;
1515 get_params(mplex, param, 0, &par);
1517 data.type=REGION_ATTACH_REPARENT;
1518 data.u.reg=reg;
1520 return mplex_do_attach(mplex, &par, &data);
1524 WRegion *mplex_attach_new_(WMPlex *mplex, WMPlexAttachParams *par,
1525 int mask, ExtlTab param)
1527 WRegionAttachData data;
1529 get_params(mplex, param, mask, par);
1531 data.type=REGION_ATTACH_LOAD;
1532 data.u.tab=param;
1534 return mplex_do_attach(mplex, par, &data);
1538 /*EXTL_DOC
1539 * Create a new region to be managed by \var{mplex}. At least the following
1540 * fields in \var{param} are understood (all but \var{type} are optional).
1542 * \begin{tabularx}{\linewidth}{lX}
1543 * \tabhead{Field & Description}
1544 * \var{type} & (string) Class name (a string) of the object to be created. \\
1545 * \var{name} & (string) Name of the object to be created (a string). \\
1546 * \var{switchto} & (boolean) Should the region be switched to (boolean)? \\
1547 * \var{unnumbered} & (boolean) Do not put on the numbered mutually
1548 * exclusive list. \\
1549 * \var{index} & (integer) Index on this list, same as for
1550 * \fnref{WMPlex.set_index}. \\
1551 * \var{level} & (integer) Stacking level. \\
1552 * \var{modal} & (boolean) Shortcut for modal stacking level. \\
1553 * \var{hidden} & (boolean) Attach hidden, if not prevented
1554 * by e.g. the mutually exclusive list being empty.
1555 * This option overrides \var{switchto}. \\
1556 * \var{passive} & (boolean) Skip in certain focusing operations. \\
1557 * \var{pseudomodal} & (boolean) The attached region is ``pseudomodal''
1558 * if the stacking level dictates it to be modal.
1559 * This means that the region may be hidden to display
1560 * regions with lesser stacking levels. \\
1561 * \var{sizepolicy} & (string) Size policy; see Section \ref{sec:sizepolicies}. \\
1562 * \var{geom} & (table) Geometry specification. \\
1563 * \end{tabularx}
1565 * In addition parameters to the region to be created are passed in this
1566 * same table.
1568 EXTL_EXPORT_MEMBER
1569 WRegion *mplex_attach_new(WMPlex *mplex, ExtlTab param)
1571 WMPlexAttachParams par=MPLEXATTACHPARAMS_INIT;
1573 return mplex_attach_new_(mplex, &par, 0, param);
1577 static bool mplex_handle_drop(WMPlex *mplex, int x, int y,
1578 WRegion *dropped)
1580 WRegion *curr=mplex_mx_current(mplex);
1582 /* This code should handle dropping tabs on floating workspaces. */
1583 if(curr && HAS_DYN(curr, region_handle_drop)){
1584 int rx, ry;
1585 region_rootpos(curr, &rx, &ry);
1586 if(rectangle_contains(&REGION_GEOM(curr), x-rx, y-ry)){
1587 if(region_handle_drop(curr, x, y, dropped))
1588 return TRUE;
1592 return (NULL!=mplex_attach_simple(mplex, dropped, MPLEX_ATTACH_SWITCHTO));
1596 WPHolder *mplex_prepare_manage(WMPlex *mplex, const WClientWin *cwin,
1597 const WManageParams *param, int priority)
1599 int cpriority=MANAGE_PRIORITY_SUB(priority, MANAGE_PRIORITY_NORMAL);
1600 WMPlexAttachParams ap;
1601 WPHolder *ph=NULL;
1602 WMPlexPHolder *mph;
1603 WLListNode *after;
1605 /* Check current */ {
1606 WStacking *cur=mplex_current_node(mplex);
1608 if(cur!=NULL){
1609 ph=region_prepare_manage(cur->reg, cwin, param, cpriority);
1610 if(ph!=NULL)
1611 return ph;
1614 if(mplex->mx_current!=NULL && mplex->mx_current->st!=cur){
1615 ph=region_prepare_manage(mplex->mx_current->st->reg,
1616 cwin, param, cpriority);
1617 if(ph!=NULL)
1618 return ph;
1622 if(!MANAGE_PRIORITY_OK(priority, MANAGE_PRIORITY_NORMAL))
1623 return NULL;
1625 ap.flags=((param->switchto ? MPLEX_ATTACH_SWITCHTO : 0)
1626 |MPLEX_ATTACH_SIZEPOLICY);
1627 ap.szplcy=SIZEPOLICY_FULL_EXACT;
1629 mph=create_mplexpholder(mplex, NULL, &ap);
1631 if(mph!=NULL){
1632 WGroupPHolder *gph;
1633 WGroupAttachParams gp=GROUPATTACHPARAMS_INIT;
1635 gp.switchto_set=1;
1636 gp.switchto=1;
1637 gp.bottom=1;
1639 gph=create_grouppholder(NULL, NULL, &gp);
1641 if(gph!=NULL){
1642 gph->recreate_pholder=(WPHolder*)mph;
1643 return (WPHolder*)gph;
1647 return (WPHolder*)mph;
1651 /*}}}*/
1654 /*{{{ Remove */
1657 void mplex_managed_remove(WMPlex *mplex, WRegion *sub)
1659 bool mx=FALSE, hadfocus=FALSE, mcf;
1660 WRegion *stdisp=(WRegion*)(mplex->stdispwatch.obj);
1661 WStacking *node, *next=NULL;
1663 mcf=region_may_control_focus((WRegion*)mplex);
1665 if(stdisp!=NULL){
1666 if(CAN_MANAGE_STDISP(sub) &&
1667 region_managed_within((WRegion*)mplex, stdisp)==sub){
1668 region_unmanage_stdisp(sub, TRUE, TRUE);
1669 region_detach_manager(stdisp);
1673 node=mplex_find_stacking(mplex, sub);
1675 if(node==NULL)
1676 return;
1678 hadfocus=(mplex_current_node(mplex)==node);
1680 if(node->lnode!=NULL){
1681 if(mplex->mx_current==node->lnode){
1682 WLListNode *lnext;
1684 mplex->mx_current=NULL;
1685 lnext=LIST_PREV(mplex->mx_list, node->lnode, next, prev);
1686 if(lnext==NULL){
1687 lnext=LIST_NEXT(mplex->mx_list, node->lnode, next, prev);
1688 if(lnext==node->lnode)
1689 lnext=NULL;
1691 if(lnext!=NULL)
1692 next=lnext->st;
1695 mplex_move_phs_before(mplex, node->lnode);
1696 llist_unlink(&(mplex->mx_list), node->lnode);
1697 mplex->mx_count--;
1699 free(node->lnode);
1700 node->lnode=NULL;
1701 mx=TRUE;
1704 UNLINK_ITEM(mplex->mgd, node, mgr_next, mgr_prev);
1706 mplex_unstack(mplex, node);
1708 stacking_unassoc(node);
1709 stacking_free(node);
1711 region_unset_manager(sub, (WRegion*)mplex);
1713 if(OBJ_IS_BEING_DESTROYED(mplex))
1714 return;
1716 if(next!=NULL)
1717 mplex_do_node_display(mplex, next, FALSE);
1719 if(hadfocus && mcf)
1720 mplex_refocus(mplex, next, FALSE);
1722 if(mx)
1723 mplex_managed_changed(mplex, MPLEX_CHANGE_REMOVE, next!=NULL, sub);
1727 void mplex_child_removed(WMPlex *mplex, WRegion *sub)
1729 if(sub!=NULL && sub==(WRegion*)(mplex->stdispwatch.obj)){
1730 watch_reset(&(mplex->stdispwatch));
1731 mplex_set_stdisp(mplex, NULL, NULL);
1736 /*}}}*/
1739 /*{{{ Rescue */
1742 bool mplex_rescue_clientwins(WMPlex *mplex, WRescueInfo *info)
1744 bool ret1, ret2;
1745 WMPlexIterTmp tmp;
1746 WLListIterTmp ltmp;
1747 WLListNode *lnode, *was_current=mplex->mx_current;
1750 /* First all mx stuff to move them nicely to another mplex (when that
1751 * is the case), switching to the current region in the target if
1752 * allowed by ph_flags_mask region_rescue.
1754 FOR_ALL_NODES_ON_LLIST(lnode, mplex->mx_list, ltmp){
1755 int sw=(lnode==was_current ? PHOLDER_ATTACH_SWITCHTO : 0);
1756 region_do_rescue_this(lnode->st->reg, info, sw);
1759 /* Then the rest (possibly retrying failed mx stuff).
1761 mplex_iter_init(&tmp, mplex);
1762 ret1=region_rescue_some_clientwins((WRegion*)mplex, info,
1763 (WRegionIterator*)mplex_iter,
1764 &tmp);
1766 ret2=region_rescue_child_clientwins((WRegion*)mplex, info);
1768 return (ret1 && ret2);
1772 /*}}}*/
1775 /*{{{ Status display support */
1778 bool mplex_set_stdisp(WMPlex *mplex, WRegion *reg,
1779 const WMPlexSTDispInfo *din)
1781 WRegion *oldstdisp=(WRegion*)(mplex->stdispwatch.obj);
1782 WRegion *mgr=NULL;
1784 assert(reg==NULL || (reg==oldstdisp) ||
1785 (REGION_MANAGER(reg)==NULL &&
1786 REGION_PARENT(reg)==(WWindow*)mplex));
1788 if(oldstdisp!=NULL){
1789 mgr=region_managed_within((WRegion*)mplex, oldstdisp);
1791 if(!CAN_MANAGE_STDISP(mgr))
1792 mgr=NULL;
1795 if(din!=NULL)
1796 mplex->stdispinfo=*din;
1798 if(reg==NULL){
1799 watch_reset(&(mplex->stdispwatch));
1801 if(mgr!=NULL){
1802 region_unmanage_stdisp(mgr, TRUE, FALSE);
1803 if(oldstdisp!=NULL)
1804 region_detach_manager(oldstdisp);
1806 }else{
1807 watch_setup(&(mplex->stdispwatch), (Obj*)reg, NULL);
1809 mplex_remanage_stdisp(mplex);
1812 if(oldstdisp!=NULL && oldstdisp!=reg)
1813 mainloop_defer_destroy((Obj*)oldstdisp);
1815 return TRUE;
1819 void mplex_get_stdisp(WMPlex *mplex, WRegion **reg, WMPlexSTDispInfo *di)
1821 *di=mplex->stdispinfo;
1822 *reg=(WRegion*)mplex->stdispwatch.obj;
1826 static StringIntMap pos_map[]={
1827 {"tl", MPLEX_STDISP_TL},
1828 {"tr", MPLEX_STDISP_TR},
1829 {"bl", MPLEX_STDISP_BL},
1830 {"br", MPLEX_STDISP_BR},
1831 {NULL, 0}
1835 static bool do_attach_stdisp(WRegion *mplex, WRegion *reg, void *unused)
1837 /* We do not actually manage the stdisp. */
1838 return TRUE;
1842 /*EXTL_DOC
1843 * Set/create status display for \var{mplex}. Table is a standard
1844 * description of the object to be created (as passed to e.g.
1845 * \fnref{WMPlex.attach_new}). In addition, the following fields are
1846 * recognised:
1848 * \begin{tabularx}{\linewidth}{lX}
1849 * \tabhead{Field & Description}
1850 * \var{pos} & (string) The corner of the screen to place the status
1851 * display in: one of \codestr{tl}, \codestr{tr}, \codestr{bl}
1852 * or \codestr{br}. \\
1853 * \var{fullsize} & (boolean) Waste all available space. \\
1854 * \var{action} & (string) If this field is set to \codestr{keep},
1855 * \var{pos} and \var{fullsize} are changed for the existing
1856 * status display. If this field is set to \codestr{remove},
1857 * the existing status display is removed. If this
1858 * field is not set or is set to \codestr{replace}, a
1859 * new status display is created and the old, if any,
1860 * removed. \\
1861 * \end{tabularx}
1863 EXTL_EXPORT_AS(WMPlex, set_stdisp)
1864 WRegion *mplex_set_stdisp_extl(WMPlex *mplex, ExtlTab t)
1866 WRegion *stdisp=NULL;
1867 WMPlexSTDispInfo din=mplex->stdispinfo;
1868 char *s;
1870 if(extl_table_gets_s(t, "pos", &s)){
1871 din.pos=stringintmap_value(pos_map, s, -1);
1872 if(din.pos<0){
1873 warn(TR("Invalid position setting."));
1874 return NULL;
1878 extl_table_gets_b(t, "fullsize", &(din.fullsize));
1880 s=NULL;
1881 extl_table_gets_s(t, "action", &s);
1883 if(s==NULL || strcmp(s, "replace")==0){
1884 WRegionAttachData data;
1885 WFitParams fp;
1886 int o2;
1888 fp.g.x=0;
1889 fp.g.y=0;
1890 fp.g.w=REGION_GEOM(mplex).w;
1891 fp.g.h=REGION_GEOM(mplex).h;
1892 fp.mode=REGION_FIT_BOUNDS|REGION_FIT_WHATEVER;
1894 /* Full mplex size is stupid so use saved geometry initially
1895 * if there's one.
1897 extl_table_gets_rectangle(t, "geom", &(fp.g));
1899 data.type=REGION_ATTACH_LOAD;
1900 data.u.tab=t;
1902 stdisp=region_attach_helper((WRegion*)mplex,
1903 (WWindow*)mplex, &fp,
1904 do_attach_stdisp, NULL,
1905 &data);
1907 if(stdisp==NULL)
1908 return NULL;
1910 }else if(strcmp(s, "keep")==0){
1911 stdisp=(WRegion*)(mplex->stdispwatch.obj);
1912 }else if(strcmp(s, "remove")!=0){
1913 warn(TR("Invalid action setting."));
1914 return FALSE;
1917 if(!mplex_set_stdisp(mplex, stdisp, &din)){
1918 destroy_obj((Obj*)stdisp);
1919 return NULL;
1922 return stdisp;
1926 static ExtlTab mplex_do_get_stdisp_extl(WMPlex *mplex, bool fullconfig)
1928 WRegion *reg=(WRegion*)mplex->stdispwatch.obj;
1929 ExtlTab t;
1931 if(reg==NULL)
1932 return extl_table_none();
1934 if(fullconfig){
1935 t=region_get_configuration(reg);
1936 extl_table_sets_rectangle(t, "geom", &REGION_GEOM(reg));
1937 }else{
1938 t=extl_create_table();
1939 extl_table_sets_o(t, "reg", (Obj*)reg);
1942 if(t!=extl_table_none()){
1943 WMPlexSTDispInfo *di=&(mplex->stdispinfo);
1944 extl_table_sets_s(t, "pos", stringintmap_key(pos_map, di->pos, NULL));
1945 extl_table_sets_b(t, "fullsize", di->fullsize);
1947 return t;
1951 /*EXTL_DOC
1952 * Get status display information. See \fnref{WMPlex.get_stdisp} for
1953 * information on the fields.
1955 EXTL_SAFE
1956 EXTL_EXPORT_AS(WMPlex, get_stdisp)
1957 ExtlTab mplex_get_stdisp_extl(WMPlex *mplex)
1959 return mplex_do_get_stdisp_extl(mplex, FALSE);
1963 /*}}}*/
1966 /*{{{ Dynfuns */
1969 void mplex_managed_geom_default(const WMPlex *mplex, WRectangle *geom)
1971 geom->x=0;
1972 geom->y=0;
1973 geom->w=REGION_GEOM(mplex).w;
1974 geom->h=REGION_GEOM(mplex).h;
1978 void mplex_managed_geom(const WMPlex *mplex, WRectangle *geom)
1980 CALL_DYN(mplex_managed_geom, mplex, (mplex, geom));
1984 void mplex_size_changed(WMPlex *mplex, bool wchg, bool hchg)
1986 CALL_DYN(mplex_size_changed, mplex, (mplex, wchg, hchg));
1990 void mplex_managed_changed(WMPlex *mplex, int mode, bool sw, WRegion *mgd)
1992 CALL_DYN(mplex_managed_changed, mplex, (mplex, mode, sw, mgd));
1996 int mplex_default_index(WMPlex *mplex)
1998 int idx=LLIST_INDEX_LAST;
1999 CALL_DYN_RET(idx, int, mplex_default_index, mplex, (mplex));
2000 return idx;
2004 /* For regions managing stdisps */
2006 void region_manage_stdisp(WRegion *reg, WRegion *stdisp,
2007 const WMPlexSTDispInfo *info)
2009 CALL_DYN(region_manage_stdisp, reg, (reg, stdisp, info));
2013 void region_unmanage_stdisp(WRegion *reg, bool permanent, bool nofocus)
2015 CALL_DYN(region_unmanage_stdisp, reg, (reg, permanent, nofocus));
2019 /*}}}*/
2022 /*{{{ Changed hook helper */
2025 static const char *mode2str(int mode)
2027 if(mode==MPLEX_CHANGE_SWITCHONLY)
2028 return "switchonly";
2029 else if(mode==MPLEX_CHANGE_REORDER)
2030 return "reorder";
2031 else if(mode==MPLEX_CHANGE_ADD)
2032 return "add";
2033 else if(mode==MPLEX_CHANGE_REMOVE)
2034 return "remove";
2035 return NULL;
2039 static bool mrsh_chg(ExtlFn fn, WMPlexChangedParams *p)
2041 ExtlTab t=extl_create_table();
2042 bool ret;
2044 extl_table_sets_o(t, "reg", (Obj*)p->reg);
2045 extl_table_sets_s(t, "mode", mode2str(p->mode));
2046 extl_table_sets_b(t, "sw", p->sw);
2047 extl_table_sets_o(t, "sub", (Obj*)p->sub);
2049 extl_protect(NULL);
2050 ret=extl_call(fn, "t", NULL, t);
2051 extl_unprotect(NULL);
2053 extl_unref_table(t);
2055 return ret;
2059 void mplex_call_changed_hook(WMPlex *mplex, WHook *hook,
2060 int mode, bool sw, WRegion *reg)
2062 WMPlexChangedParams p;
2064 p.reg=mplex;
2065 p.mode=mode;
2066 p.sw=sw;
2067 p.sub=reg;
2069 hook_call_p(hook, &p, (WHookMarshallExtl*)mrsh_chg);
2073 /*}}} */
2076 /*{{{ Save/load */
2079 static void save_node(WMPlex *mplex, ExtlTab subs, int *n,
2080 WStacking *node, bool unnumbered)
2082 ExtlTab st, g;
2084 st=region_get_configuration(node->reg);
2086 if(st!=extl_table_none()){
2087 if(mplex->mx_current!=NULL && node==mplex->mx_current->st)
2088 extl_table_sets_b(st, "switchto", TRUE);
2089 extl_table_sets_s(st, "sizepolicy",
2090 sizepolicy2string(node->szplcy));
2091 extl_table_sets_i(st, "level", node->level);
2092 g=extl_table_from_rectangle(&REGION_GEOM(node->reg));
2093 extl_table_sets_t(st, "geom", g);
2094 extl_unref_table(g);
2095 if(STACKING_IS_HIDDEN(node))
2096 extl_table_sets_b(st, "hidden", TRUE);
2097 if(STACKING_IS_PSEUDOMODAL(node))
2098 extl_table_sets_b(st, "pseudomodal", TRUE);
2099 if(unnumbered)
2100 extl_table_sets_b(st, "unnumbered", TRUE);
2102 extl_table_seti_t(subs, ++(*n), st);
2103 extl_unref_table(st);
2108 ExtlTab mplex_get_configuration(WMPlex *mplex)
2110 ExtlTab tab, subs, stdisptab;
2111 WMPlexIterTmp tmp;
2112 WLListIterTmp ltmp;
2113 WLListNode *lnode;
2114 WStacking *node;
2115 int n=0;
2117 tab=region_get_base_configuration((WRegion*)mplex);
2119 subs=extl_create_table();
2120 extl_table_sets_t(tab, "managed", subs);
2122 /* First the numbered/mutually exclusive nodes */
2123 FOR_ALL_NODES_ON_LLIST(lnode, mplex->mx_list, ltmp){
2124 save_node(mplex, subs, &n, lnode->st, FALSE);
2127 FOR_ALL_NODES_IN_MPLEX(mplex, node, tmp){
2128 if(node->lnode==NULL)
2129 save_node(mplex, subs, &n, node, TRUE);
2132 extl_unref_table(subs);
2134 /*stdisptab=mplex_do_get_stdisp_extl(mplex, TRUE);
2135 if(stdisptab!=extl_table_none()){
2136 extl_table_sets_t(tab, "stdisp", stdisptab);
2137 extl_unref_table(stdisptab);
2140 return tab;
2144 void mplex_load_contents(WMPlex *mplex, ExtlTab tab)
2146 ExtlTab substab, subtab;
2147 int n, i;
2149 /*if(extl_table_gets_t(tab, "stdisp", &subtab)){
2150 mplex_set_stdisp_extl(mplex, subtab);
2151 extl_unref_table(subtab);
2154 if(extl_table_gets_t(tab, "managed", &substab) ||
2155 extl_table_gets_t(tab, "subs", &substab)){
2156 n=extl_table_get_n(substab);
2157 for(i=1; i<=n; i++){
2158 if(extl_table_geti_t(substab, i, &subtab)){
2159 WMPlexAttachParams par=MPLEXATTACHPARAMS_INIT;
2160 WFitParams fp;
2161 WPHolder *ph;
2163 get_params(mplex, subtab, 0, &par);
2164 mplex_attach_fp(mplex, &par, &fp);
2166 par.flags|=MPLEX_ATTACH_INDEX;
2167 par.index=LLIST_INDEX_LAST;
2169 ph=(WPHolder*)create_mplexpholder(mplex, NULL, &par);
2171 if(ph!=NULL){
2172 region_attach_load_helper((WRegion*)mplex, (WWindow*)mplex, &fp,
2173 (WRegionDoAttachFn*)mplex_do_attach_final,
2174 (void*)ph, subtab, &ph);
2176 if(ph!=NULL)
2177 destroy_obj((Obj*)ph);
2180 extl_unref_table(subtab);
2183 extl_unref_table(substab);
2188 WRegion *mplex_load(WWindow *par, const WFitParams *fp, ExtlTab tab, const char *name)
2190 WMPlex *mplex=create_mplex(par, fp, name);
2191 if(mplex!=NULL)
2192 mplex_load_contents(mplex, tab);
2193 return (WRegion*)mplex;
2197 /*}}}*/
2200 /*{{{ Dynfuntab and class info */
2203 static DynFunTab mplex_dynfuntab[]={
2204 {region_do_set_focus,
2205 mplex_do_set_focus},
2207 {region_managed_remove,
2208 mplex_managed_remove},
2210 {region_managed_rqgeom,
2211 mplex_managed_rqgeom},
2213 {(DynFun*)region_managed_prepare_focus,
2214 (DynFun*)mplex_managed_prepare_focus},
2216 {(DynFun*)region_handle_drop,
2217 (DynFun*)mplex_handle_drop},
2219 {region_map, mplex_map},
2220 {region_unmap, mplex_unmap},
2222 {(DynFun*)region_prepare_manage,
2223 (DynFun*)mplex_prepare_manage},
2225 {(DynFun*)region_current,
2226 (DynFun*)mplex_current},
2228 {(DynFun*)region_rescue_clientwins,
2229 (DynFun*)mplex_rescue_clientwins},
2231 {(DynFun*)region_get_configuration,
2232 (DynFun*)mplex_get_configuration},
2234 {mplex_managed_geom,
2235 mplex_managed_geom_default},
2237 {(DynFun*)region_fitrep,
2238 (DynFun*)mplex_fitrep},
2240 {region_child_removed,
2241 mplex_child_removed},
2243 {(DynFun*)region_managed_get_pholder,
2244 (DynFun*)mplex_managed_get_pholder},
2246 {(DynFun*)region_get_rescue_pholder_for,
2247 (DynFun*)mplex_get_rescue_pholder_for},
2249 {(DynFun*)region_navi_first,
2250 (DynFun*)mplex_navi_first},
2252 {(DynFun*)region_navi_next,
2253 (DynFun*)mplex_navi_next},
2255 {(DynFun*)region_managed_rqorder,
2256 (DynFun*)mplex_managed_rqorder},
2258 END_DYNFUNTAB
2262 EXTL_EXPORT
2263 IMPLCLASS(WMPlex, WWindow, mplex_deinit, mplex_dynfuntab);
2266 /*}}}*/