]> git.decadent.org.uk Git - ion3.git/blob - ioncore/mplex.c
[svn-upgrade] Integrating new upstream version, ion3 (20070203)
[ion3.git] / ioncore / mplex.c
1 /*
2  * ion/ioncore/mplex.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2007. 
5  *
6  * Ion is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation; either version 2.1 of the License, or
9  * (at your option) any later version.
10  */
11
12 #include <limits.h>
13 #include <string.h>
14
15 #include <libtu/objp.h>
16 #include <libtu/minmax.h>
17 #include <libtu/rb.h>
18 #include <libextl/extl.h>
19 #include <libmainloop/defer.h>
20
21 #include "common.h"
22 #include "window.h"
23 #include "global.h"
24 #include "rootwin.h"
25 #include "focus.h"
26 #include "event.h"
27 #include "attach.h"
28 #include "manage.h"
29 #include "resize.h"
30 #include "tags.h"
31 #include "sizehint.h"
32 #include "extlconv.h"
33 #include "frame-pointer.h"
34 #include "bindmaps.h"
35 #include "regbind.h"
36 #include "saveload.h"
37 #include "xwindow.h"
38 #include "mplexpholder.h"
39 #include "llist.h"
40 #include "names.h"
41 #include "sizepolicy.h"
42 #include "stacking.h"
43 #include "group.h"
44 #include "navi.h"
45 #include "groupedpholder.h"
46
47
48 #define SUBS_MAY_BE_MAPPED(MPLEX) \
49     (REGION_IS_MAPPED(MPLEX) && !MPLEX_MGD_UNVIEWABLE(MPLEX))
50
51 #define PASSIVE(ST) ((ST)->level<=STACKING_LEVEL_MODAL1          \
52                      && ((ST)->reg==NULL                         \
53                          || (ST)->reg->flags&REGION_SKIP_FOCUS))
54
55 #define CAN_MANAGE_STDISP(REG) HAS_DYN(REG, region_manage_stdisp)
56
57
58 /*{{{ Stacking list stuff */
59
60
61 WStacking *mplex_get_stacking(WMPlex *mplex)
62 {
63     return window_get_stacking(&mplex->win);
64 }
65
66
67 WStacking **mplex_get_stackingp(WMPlex *mplex)
68 {
69     return window_get_stackingp(&mplex->win);
70 }
71
72
73 void mplex_iter_init(WMPlexIterTmp *tmp, WMPlex *mplex)
74 {
75     stacking_iter_mgr_init(tmp, mplex->mgd, NULL, mplex);
76 }
77
78
79 WRegion *mplex_iter(WMPlexIterTmp *tmp)
80 {
81     return stacking_iter_mgr(tmp);
82 }
83
84
85 WStacking *mplex_iter_nodes(WMPlexIterTmp *tmp)
86 {
87     return stacking_iter_mgr_nodes(tmp);
88 }
89
90
91 /*}}}*/
92
93
94 /*{{{ Destroy/create mplex */
95
96
97 bool mplex_do_init(WMPlex *mplex, WWindow *parent, 
98                    const WFitParams *fp, Window win)
99 {
100     mplex->flags=0;
101     
102     mplex->mx_list=NULL;
103     mplex->mx_current=NULL;
104     mplex->mx_phs=NULL;
105     mplex->mx_count=0;
106     
107     mplex->mgd=NULL;
108     
109     watch_init(&(mplex->stdispwatch));
110     mplex->stdispinfo.pos=MPLEX_STDISP_BL;
111     mplex->stdispinfo.fullsize=FALSE;
112     
113     if(!window_do_init((WWindow*)mplex, parent, fp, win))
114         return FALSE;
115     
116     mplex->win.region.flags|=REGION_BINDINGS_ARE_GRABBED;
117     
118     window_select_input(&(mplex->win), IONCORE_EVENTMASK_CWINMGR);
119     
120     region_register((WRegion*)mplex);
121     
122     /* Call this to set MPLEX_MANAGED_UNVIEWABLE if necessary. */
123     mplex_fit_managed(mplex);
124     
125     return TRUE;
126 }
127
128
129 bool mplex_init(WMPlex *mplex, WWindow *parent, const WFitParams *fp)
130 {
131     return mplex_do_init(mplex, parent, fp, None);
132 }
133
134
135 WMPlex *create_mplex(WWindow *parent, const WFitParams *fp)
136 {
137     CREATEOBJ_IMPL(WMPlex, mplex, (p, parent, fp));
138 }
139
140
141 void mplex_deinit(WMPlex *mplex)
142 {
143     WMPlexIterTmp tmp;
144     WRegion *reg;
145     
146     FOR_ALL_MANAGED_BY_MPLEX(mplex, reg, tmp){
147         destroy_obj((Obj*)reg);
148     }
149     
150     assert(mplex->mgd==NULL);
151     assert(mplex->mx_list==NULL);
152
153     while(mplex->mx_phs!=NULL){
154         assert(mplexpholder_move(mplex->mx_phs, NULL, NULL, NULL));
155     }
156     
157     window_deinit((WWindow*)mplex);
158 }
159
160
161 bool mplex_may_destroy(WMPlex *mplex)
162 {
163     if(mplex->mgd!=NULL){
164         warn(TR("Refusing to destroy - not empty."));
165         return FALSE;
166     }
167     
168     return TRUE;
169 }
170
171
172 /*}}}*/
173
174
175 /*{{{ Node lookup etc. */
176
177
178 WStacking *mplex_find_stacking(WMPlex *mplex, WRegion *reg)
179 {
180     WStacking *st;
181     
182     /* Some routines that call us expect us to this check. */
183     if(reg==NULL || REGION_MANAGER(reg)!=(WRegion*)mplex)
184         return NULL;
185     
186     st=ioncore_find_stacking(reg);
187
188     assert(st==NULL || st->mgr_prev!=NULL);
189     
190     return st;
191 }
192
193
194 WStacking *mplex_current_node(WMPlex *mplex)
195 {
196     WStacking *st=NULL;
197     WRegion *reg;
198     
199     reg=REGION_ACTIVE_SUB(mplex);
200     reg=region_managed_within((WRegion*)mplex, reg);
201     if(reg!=NULL)
202         st=mplex_find_stacking(mplex, reg);
203
204     if(st!=NULL)
205         return st;
206     else
207         return (mplex->mx_current!=NULL ? mplex->mx_current->st : NULL);
208 }
209
210
211 WRegion *mplex_current(WMPlex *mplex)
212 {
213     WStacking *node=mplex_current_node(mplex);
214     return (node==NULL ? NULL : node->reg);
215 }
216
217
218 /*}}}*/
219
220
221 /*{{{ Exclusive list management and exports */
222
223 /*EXTL_DOC
224  * Returns the number of objects on the mutually exclusive list of \var{mplex}.
225  */
226 EXTL_SAFE
227 EXTL_EXPORT_MEMBER
228 int mplex_mx_count(WMPlex *mplex)
229 {
230     return mplex->mx_count;
231 }
232
233
234 /*EXTL_DOC
235  * Returns the managed object currently active within the mutually exclusive
236  * list of \var{mplex}.
237  */
238 EXTL_SAFE
239 EXTL_EXPORT_MEMBER
240 WRegion *mplex_mx_current(WMPlex *mplex)
241 {
242     WLListNode *lnode=mplex->mx_current;
243     return (lnode==NULL ? NULL : lnode->st->reg);
244 }
245
246
247 /*EXTL_DOC
248  * Returns the \var{n}:th object managed by \var{mplex} on the
249  * \var{l}:th layer.
250  */
251 EXTL_SAFE
252 EXTL_EXPORT_MEMBER
253 WRegion *mplex_mx_nth(WMPlex *mplex, uint n)
254 {
255     WLListNode *lnode=llist_nth_node(mplex->mx_list, n);
256     return (lnode==NULL ? NULL : lnode->st->reg);
257 }
258
259
260 /*EXTL_DOC
261  * Iterate over numbered/mutually exclusive region list of \var{mplex} 
262  * until \var{iterfn} returns \code{false}.
263  * The function itself returns \code{true} if it reaches the end of list
264  * without this happening.
265  */
266 EXTL_SAFE
267 EXTL_EXPORT_MEMBER
268 bool mplex_mx_i(WMPlex *mplex, ExtlFn iterfn)
269 {
270     WLListIterTmp tmp;
271     llist_iter_init(&tmp, mplex->mx_list);
272     
273     return extl_iter_objlist_(iterfn, (ObjIterator*)llist_iter_regions, &tmp);
274 }
275
276
277 /*EXTL_DOC
278  * Iterate over managed regions of \var{mplex} until \var{iterfn} returns
279  * \code{false}.
280  * The function itself returns \code{true} if it reaches the end of list
281  * without this happening.
282  */
283 EXTL_SAFE
284 EXTL_EXPORT_MEMBER
285 bool mplex_managed_i(WMPlex *mplex, ExtlFn iterfn)
286 {
287     WMPlexIterTmp tmp;
288     mplex_iter_init(&tmp, mplex);
289     
290     return extl_iter_objlist_(iterfn, (ObjIterator*)mplex_iter, &tmp);
291 }
292
293
294 /*EXTL_DOC
295  * Set index of \var{reg} within the multiplexer to \var{index} within 
296  * the mutually exclusive list. Special values for \var{index} are:
297  * \begin{tabularx}{\linewidth}{lX}
298  *   $-1$ & Last. \\
299  *   $-2$ & After \fnref{WMPlex.mx_current}. \\
300  * \end{tabularx}
301  */
302 EXTL_EXPORT_MEMBER
303 void mplex_set_index(WMPlex *mplex, WRegion *reg, int index)
304 {
305     WLListNode *lnode, *after;
306     WStacking *node;
307     
308     node=mplex_find_stacking(mplex, reg);
309
310     if(node==NULL)
311         return;
312     
313     lnode=node->lnode;
314     
315     if(lnode==NULL){
316         lnode=ALLOC(WLListNode);
317         if(lnode==NULL)
318             return;
319         lnode->next=NULL;
320         lnode->prev=NULL;
321         lnode->phs=NULL;
322         lnode->st=node;
323         node->lnode=lnode;
324         mplex->mx_count++;
325     }else{
326         mplex_move_phs_before(mplex, lnode);
327         llist_unlink(&(mplex->mx_list), lnode);
328     }
329     
330     /* TODO: Support remove? */
331     
332     after=llist_index_to_after(mplex->mx_list, mplex->mx_current, index);
333     llist_link_after(&(mplex->mx_list), after, lnode);
334     mplex_managed_changed(mplex, MPLEX_CHANGE_REORDER, FALSE, reg);
335 }
336
337
338 /*EXTL_DOC
339  * Get index of \var{reg} within the multiplexer on list 1. The first region 
340  * managed by \var{mplex} has index zero. If \var{reg} is not managed by 
341  * \var{mplex}, -1 is returned.
342  */
343 EXTL_SAFE
344 EXTL_EXPORT_MEMBER
345 int mplex_get_index(WMPlex *mplex, WRegion *reg)
346 {
347     WLListIterTmp tmp;
348     WLListNode *lnode;
349     int index=0;
350     
351     FOR_ALL_NODES_ON_LLIST(lnode, mplex->mx_list, tmp){
352         if(reg==lnode->st->reg)
353             return index;
354         index++;
355     }
356     
357     return -1;
358 }
359
360
361 /*EXTL_DOC
362  * Move \var{r} ''right'' within objects managed by \var{mplex} on list 1.
363  */
364 EXTL_EXPORT_MEMBER
365 void mplex_inc_index(WMPlex *mplex, WRegion *r)
366 {
367     if(r==NULL)
368         r=mplex_mx_current(mplex);
369     if(r!=NULL)
370         mplex_set_index(mplex, r, mplex_get_index(mplex, r)+1);
371 }
372
373
374 /*EXTL_DOC
375  * Move \var{r} ''right'' within objects managed by \var{mplex} on list 1.
376  */
377 EXTL_EXPORT_MEMBER
378 void mplex_dec_index(WMPlex *mplex, WRegion *r)
379 {
380     if(r==NULL)
381         r=mplex_mx_current(mplex);
382     if(r!=NULL)
383         mplex_set_index(mplex, r, mplex_get_index(mplex, r)-1);
384 }
385
386
387 /*}}}*/
388
389
390 /*{{{ Mapping */
391
392
393 static void mplex_map_mgd(WMPlex *mplex)
394 {
395     WMPlexIterTmp tmp;
396     WStacking *node;
397
398     FOR_ALL_NODES_IN_MPLEX(mplex, node, tmp){
399         if(!STACKING_IS_HIDDEN(node))
400             region_map(node->reg);
401     }
402 }
403
404
405 static void mplex_unmap_mgd(WMPlex *mplex)
406 {
407     WMPlexIterTmp tmp;
408     WStacking *node;
409
410     FOR_ALL_NODES_IN_MPLEX(mplex, node, tmp){
411         if(!STACKING_IS_HIDDEN(node))
412             region_unmap(node->reg);
413     }
414 }
415
416
417
418 void mplex_map(WMPlex *mplex)
419 {
420     window_map((WWindow*)mplex);
421     /* A lame requirement of the ICCCM is that client windows should be
422      * unmapped if the parent is unmapped.
423      */
424     if(!MPLEX_MGD_UNVIEWABLE(mplex))
425         mplex_map_mgd(mplex);
426 }
427
428
429 void mplex_unmap(WMPlex *mplex)
430 {
431     window_unmap((WWindow*)mplex);
432     /* A lame requirement of the ICCCM is that client windows should be
433      * unmapped if the parent is unmapped.
434      */
435     if(!MPLEX_MGD_UNVIEWABLE(mplex))
436         mplex_unmap_mgd(mplex);
437 }
438
439
440 /*}}}*/
441
442
443 /*{{{ Resize and reparent */
444
445
446 bool mplex_fitrep(WMPlex *mplex, WWindow *par, const WFitParams *fp)
447 {
448     bool wchg=(REGION_GEOM(mplex).w!=fp->g.w);
449     bool hchg=(REGION_GEOM(mplex).h!=fp->g.h);
450     
451     window_do_fitrep(&(mplex->win), par, &(fp->g));
452     
453     if(wchg || hchg){
454         mplex_fit_managed(mplex);
455         mplex_size_changed(mplex, wchg, hchg);
456     }
457     
458     return TRUE;
459 }
460
461
462 void mplex_do_fit_managed(WMPlex *mplex, WFitParams *fp)
463 {
464     WRectangle geom;
465     WMPlexIterTmp tmp;
466     WStacking *node;
467     WFitParams fp2;
468     
469     if(!MPLEX_MGD_UNVIEWABLE(mplex) && (fp->g.w<=1 || fp->g.h<=1)){
470         mplex->flags|=MPLEX_MANAGED_UNVIEWABLE;
471         if(REGION_IS_MAPPED(mplex))
472             mplex_unmap_mgd(mplex);
473     }else if(MPLEX_MGD_UNVIEWABLE(mplex) && !(fp->g.w<=1 || fp->g.h<=1)){
474         mplex->flags&=~MPLEX_MANAGED_UNVIEWABLE;
475         if(REGION_IS_MAPPED(mplex))
476             mplex_map_mgd(mplex);
477     }
478     
479     if(!MPLEX_MGD_UNVIEWABLE(mplex)){
480         FOR_ALL_NODES_IN_MPLEX(mplex, node, tmp){
481             fp2=*fp;
482             sizepolicy(&node->szplcy, node->reg, NULL, 0, &fp2);
483             region_fitrep(node->reg, NULL, &fp2);
484         }
485     }
486 }
487
488
489 void mplex_fit_managed(WMPlex *mplex)
490 {
491     WFitParams fp;
492     
493     fp.mode=REGION_FIT_EXACT;
494     mplex_managed_geom(mplex, &(fp.g));
495
496     mplex_do_fit_managed(mplex, &fp);
497 }
498
499
500 static void mplex_managed_rqgeom(WMPlex *mplex, WRegion *sub,
501                                  const WRQGeomParams *rq,
502                                  WRectangle *geomret)
503 {
504     WRectangle rg;
505     WFitParams fp;
506     WStacking *node;
507
508     node=mplex_find_stacking(mplex, sub);
509     
510     assert(node!=NULL);
511
512     mplex_managed_geom(mplex, &fp.g);
513
514     sizepolicy(&node->szplcy, sub, &rq->geom, rq->flags, &fp);
515     
516     if(geomret!=NULL)
517         *geomret=fp.g;
518     
519     if(!(rq->flags&REGION_RQGEOM_TRYONLY))
520         region_fitrep(sub, NULL, &fp);
521 }
522
523
524 /*}}}*/
525
526
527 /*{{{ Focus  */
528
529
530 static WRegion *mplex_do_to_focus(WMPlex *mplex, WStacking *to_try)
531 {
532     WStacking *stacking=mplex_get_stacking(mplex);
533     WStacking *st=NULL;
534     
535     if(stacking==NULL)
536         return NULL;
537
538     if(to_try!=NULL && (to_try->reg==NULL || !REGION_IS_MAPPED(to_try->reg)))
539         to_try=NULL;
540
541     st=stacking_find_to_focus_mapped(stacking, to_try, NULL);
542     
543     if(st!=NULL)
544         return st->reg;
545     else if(mplex->mx_current!=NULL)
546         return mplex->mx_current->st->reg;
547     else
548         return NULL;
549 }
550
551
552 WRegion *mplex_to_focus(WMPlex *mplex)
553 {
554     WRegion *reg=REGION_ACTIVE_SUB(mplex);
555     WStacking *to_try=NULL;
556     
557     if(reg!=NULL)
558         to_try=ioncore_find_stacking(reg);
559
560     return mplex_do_to_focus(mplex, to_try);
561 }
562
563
564 static WRegion *mplex_do_to_focus_on(WMPlex *mplex, WStacking *node,
565                                      WStacking *to_try)
566 {
567     WStacking *stacking=mplex_get_stacking(mplex);
568     WStacking *st=NULL;
569     
570     if(stacking==NULL)
571         return NULL;
572
573     if(to_try!=NULL && (to_try->reg==NULL || !REGION_IS_MAPPED(to_try->reg)))
574         to_try=NULL;
575     
576     st=stacking_find_to_focus_mapped(stacking, to_try, node->reg);
577     
578     return (st!=NULL ? st->reg : NULL);
579 }
580
581
582 static WRegion *mplex_to_focus_on(WMPlex *mplex, WStacking *node,
583                                   WStacking *to_try)
584 {
585     WRegion *reg;
586     WGroup *grp=OBJ_CAST(node->reg, WGroup);
587     
588     if(grp!=NULL){
589         if(to_try==NULL)
590             to_try=grp->current_managed;
591         reg=mplex_do_to_focus_on(mplex, node, to_try);
592         if(reg!=NULL || to_try!=NULL)
593             return reg;
594         /* We don't know whether something is blocking focus here,
595          * or if there was nothing to focus (as node->reg itself
596          * isn't on the stacking list).
597          */
598     }
599     
600     reg=mplex_do_to_focus(mplex, node);
601     return (reg==node->reg ? reg : NULL);
602 }
603
604
605 void mplex_do_set_focus(WMPlex *mplex, bool warp)
606 {
607     if(!MPLEX_MGD_UNVIEWABLE(mplex)){
608         WRegion *reg=mplex_to_focus(mplex);
609         
610         if(reg!=NULL){
611             region_do_set_focus(reg, warp);
612             return;
613         }
614     }
615     
616     window_do_set_focus((WWindow*)mplex, warp);
617 }
618
619
620 /*}}}*/
621
622
623 /*{{{ Switch */
624
625
626 static void mplex_do_remanage_stdisp(WMPlex *mplex, WRegion *sub)
627 {
628     WRegion *stdisp=(WRegion*)(mplex->stdispwatch.obj);
629
630     /* Move stdisp */
631     if(sub!=NULL && CAN_MANAGE_STDISP(sub)){
632         if(stdisp!=NULL){
633             WRegion *mgrw=region_managed_within((WRegion*)mplex, stdisp);
634             if(mgrw!=sub){
635                 WRegion *mgr=REGION_MANAGER(stdisp);
636                 if(mgr!=NULL){
637                     if(CAN_MANAGE_STDISP(mgr))
638                         region_unmanage_stdisp(mgr, FALSE, FALSE);
639                     region_detach_manager(stdisp);
640                 }
641                 
642                 region_manage_stdisp(sub, stdisp, 
643                                      &(mplex->stdispinfo));
644             }
645         }else{
646             region_unmanage_stdisp(sub, TRUE, FALSE);
647         }
648     }/*else if(stdisp!=NULL){
649         region_detach_manager(stdisp);
650         region_unmap(stdisp);
651     }*/
652 }
653
654
655 void mplex_remanage_stdisp(WMPlex *mplex)
656 {
657     mplex_do_remanage_stdisp(mplex, (mplex->mx_current!=NULL
658                                      ? mplex->mx_current->st->reg
659                                      : NULL));
660 }
661
662
663 static void mplex_do_node_display(WMPlex *mplex, WStacking *node,
664                                   bool call_changed)
665 {
666     WRegion *sub=node->reg;
667     WLListNode *mxc=mplex->mx_current;
668
669     if(!STACKING_IS_HIDDEN(node))
670         return;
671     
672     if(node->lnode!=NULL && node->lnode!=mxc)
673         mplex_do_remanage_stdisp(mplex, sub);
674
675     node->hidden=FALSE;
676     
677     if(SUBS_MAY_BE_MAPPED(mplex))
678         region_map(sub);
679     else
680         region_unmap(sub);
681     
682     if(node->lnode!=NULL){
683         if(mxc!=NULL){
684             /* Hide current mx region. We do it after mapping the
685              * new one to avoid flicker.
686              */
687             if(REGION_IS_MAPPED(mplex))
688                 region_unmap(mxc->st->reg);
689             mxc->st->hidden=TRUE;
690         }
691         
692         mplex->mx_current=node->lnode;
693         
694         /* Ugly hack:
695          * Many programs will get upset if the visible, although only 
696          * such, client window is not the lowest window in the mplex. 
697          * xprop/xwininfo will return the information for the lowest 
698          * window. 'netscape -remote' will not work at all if there are
699          * no visible netscape windows.
700          */
701         {
702             #warning "TODO: less ugly hack"
703             WGroup *grp=(WGroup*)OBJ_CAST(sub, WGroupCW);
704             if(grp!=NULL && grp->bottom!=NULL){
705                 region_managed_rqorder((WRegion*)grp, grp->bottom->reg, 
706                                        REGION_ORDER_BACK);
707             }
708         }
709         
710         if(call_changed)
711             mplex_managed_changed(mplex, MPLEX_CHANGE_SWITCHONLY, TRUE, sub);
712     }
713 }
714
715
716 static bool mplex_refocus(WMPlex *mplex, WStacking *node, bool warp)
717 {
718     WRegion *foc=NULL;
719     bool ret=TRUE;
720     
721     if(node!=NULL){
722         foc=mplex_to_focus_on(mplex, node, NULL);
723         ret=(foc!=NULL);
724     }
725         
726     if(foc==NULL){
727         ret=FALSE;
728         foc=mplex_to_focus(mplex);
729     }
730     
731     if(foc!=NULL /* && !REGION_IS_ACTIVE(foc) */ )
732         region_maybewarp(foc, warp);
733     
734     return ret;
735 }
736
737
738 bool mplex_do_prepare_focus(WMPlex *mplex, WStacking *node,
739                             WStacking *sub, int flags, 
740                             WPrepareFocusResult *res)
741 {
742     WRegion *foc;
743     
744     if(sub==NULL && node==NULL)
745         return FALSE;
746     
747     /* Display the node in any case */
748     if(node!=NULL && !(flags&REGION_GOTO_ENTERWINDOW))
749         mplex_do_node_display(mplex, node, TRUE);
750     
751     if(!region_prepare_focus((WRegion*)mplex, flags, res))
752         return FALSE;
753
754     if(node!=NULL)
755         foc=mplex_to_focus_on(mplex, node, sub);
756     else
757         foc=mplex_do_to_focus(mplex, sub);
758
759     if(foc!=NULL){
760         res->reg=foc;
761         res->flags=flags;
762         
763         if(sub==NULL)
764             return (foc==node->reg);
765         else
766             return (foc==sub->reg);
767     }else{
768         return FALSE;
769     }
770 }
771
772
773 bool mplex_managed_prepare_focus(WMPlex *mplex, WRegion *disp, 
774                                  int flags, WPrepareFocusResult *res)
775 {
776     WStacking *node=mplex_find_stacking(mplex, disp);
777     
778     if(node==NULL)
779         return FALSE;
780     else
781         return mplex_do_prepare_focus(mplex, node, NULL, flags, res);
782 }
783
784
785 /*}}}*/
786
787
788 /*{{{ Switch exports */
789
790
791 static void do_switch(WMPlex *mplex, WLListNode *lnode)
792 {
793     WStacking *node=(lnode!=NULL ? lnode->st : NULL);
794     
795     if(node!=NULL){
796         bool mcf=region_may_control_focus((WRegion*)mplex);
797     
798         mplex_do_node_display(mplex, node, TRUE);
799         
800         if(mcf)
801             mplex_refocus(mplex, node, TRUE);
802     }
803 }
804
805
806 /*EXTL_DOC
807  * Have \var{mplex} display the \var{n}:th object managed by it.
808  */
809 EXTL_EXPORT_MEMBER
810 void mplex_switch_nth(WMPlex *mplex, uint n)
811 {
812     do_switch(mplex, llist_nth_node(mplex->mx_list, n));
813 }
814
815
816 /*EXTL_DOC
817  * Have \var{mplex} display next (wrt. currently selected) object managed 
818  * by it.
819  */
820 EXTL_EXPORT_MEMBER
821 void mplex_switch_next(WMPlex *mplex)
822 {
823     do_switch(mplex, LIST_NEXT_WRAP(mplex->mx_list, mplex->mx_current, 
824                                     next, prev));
825 }
826
827
828 /*EXTL_DOC
829  * Have \var{mplex} display previous (wrt. currently selected) object
830  * managed by it.
831  */
832 EXTL_EXPORT_MEMBER
833 void mplex_switch_prev(WMPlex *mplex)
834 {
835     do_switch(mplex, LIST_PREV_WRAP(mplex->mx_list, mplex->mx_current,
836                                     next, prev));
837 }
838
839
840 bool mplex_set_hidden(WMPlex *mplex, WRegion *reg, int sp)
841 {
842     bool mcf=region_may_control_focus((WRegion*)mplex);
843     WStacking *node=mplex_find_stacking(mplex, reg);
844     bool hidden, nhidden;
845     
846     if(node==NULL)
847         return FALSE;
848     
849     hidden=STACKING_IS_HIDDEN(node);
850     nhidden=libtu_do_setparam(sp, hidden);
851     
852     if(!hidden && nhidden){
853         node->hidden=TRUE;
854         
855         if(REGION_IS_MAPPED(mplex) && !MPLEX_MGD_UNVIEWABLE(mplex))
856             region_unmap(reg);
857         
858         /* lnode -> switch next? */
859     }else if(hidden && !nhidden){
860         mplex_do_node_display(mplex, node, TRUE);
861     }
862     
863     if(mcf && !PASSIVE(node))
864         mplex_refocus(mplex, (nhidden ? NULL : node), TRUE);
865     
866     return STACKING_IS_HIDDEN(node);
867 }
868
869
870 /*EXTL_DOC
871  * Set the visibility of the region \var{reg} on \var{mplex}
872  * as specified with the parameter \var{how} (set/unset/toggle).
873  * The resulting state is returned.
874  */
875 EXTL_EXPORT_AS(WMPlex, set_hidden)
876 bool mplex_set_hidden_extl(WMPlex *mplex, WRegion *reg, const char *how)
877 {
878     return mplex_set_hidden(mplex, reg, libtu_string_to_setparam(how));
879 }
880
881
882 /*EXTL_DOC
883  * Is \var{reg} on within \var{mplex} and hidden?
884  */
885 EXTL_SAFE
886 EXTL_EXPORT_MEMBER
887 bool mplex_is_hidden(WMPlex *mplex, WRegion *reg)
888 {
889     WStacking *node=mplex_find_stacking(mplex, reg);
890     
891     return (node!=NULL && STACKING_IS_HIDDEN(node));
892 }
893
894
895 /*}}}*/
896
897
898 /*{{{ Navigation */
899
900
901 static WStacking *mplex_nxt(WMPlex *mplex, WStacking *st, bool wrap)
902 {
903     return (st->mgr_next!=NULL 
904             ? st->mgr_next 
905             : (wrap ? mplex->mgd : NULL));
906 }
907
908
909 static WStacking *mplex_prv(WMPlex *mplex, WStacking *st, bool wrap)
910 {
911     return (st!=mplex->mgd
912             ? st->mgr_prev
913             : (wrap ? st->mgr_prev : NULL));
914 }
915
916
917 typedef WStacking *NxtFn(WMPlex *mplex, WStacking *st, bool wrap);
918
919
920 static WRegion *do_navi(WMPlex *mplex, WStacking *sti, 
921                         NxtFn *fn, WRegionNaviData *data, 
922                         bool sti_ok, bool wrap)
923 {
924     WStacking *st, *stacking;
925     uint min_level=0;
926     
927     stacking=mplex_get_stacking(mplex);
928     
929     if(stacking!=NULL)
930         min_level=stacking_min_level_mapped(stacking);
931
932     st=sti;
933     while(1){
934         st=fn(mplex, st, wrap); 
935         
936         if(st==NULL || (st==sti && !sti_ok))
937             break;
938         
939         if(!st->hidden){
940             if(OBJ_IS(st->reg, WGroup)){
941                 /* WGroup navigation code should respect modal stuff. */
942                 WRegion *res=region_navi_cont((WRegion*)mplex, st->reg, data);
943                 if(res!=NULL){
944                     if(res!=st->reg){
945                         return res;
946                     }else{
947                         #warning "TODO: What to do?"
948                     }
949                 }
950             }else{
951                 if(st->level>=min_level && !PASSIVE(st))
952                     return region_navi_cont((WRegion*)mplex, st->reg, data);
953             }
954         }
955                 
956         if(st==sti)
957             break;
958     }
959     
960     return NULL;
961 }
962                         
963
964 WRegion *mplex_navi_first(WMPlex *mplex, WRegionNavi nh,
965                           WRegionNaviData *data)
966 {
967     WStacking *lst=mplex->mgd;
968     WRegion *res=NULL;
969     
970     if(lst!=NULL){
971         if(nh==REGION_NAVI_ANY){
972             /* ? */
973         }
974     
975         if(nh==REGION_NAVI_ANY || nh==REGION_NAVI_END || 
976            nh==REGION_NAVI_BOTTOM || nh==REGION_NAVI_RIGHT){
977             res=do_navi(mplex, lst, mplex_prv, data, TRUE, TRUE);
978         }else{
979             res=do_navi(mplex, lst->mgr_prev, mplex_nxt, data, TRUE, TRUE);
980         }
981     }
982     
983     return region_navi_cont((WRegion*)mplex, res, data);
984 }
985
986     
987 WRegion *mplex_navi_next(WMPlex *mplex, WRegion *rel, WRegionNavi nh,
988                          WRegionNaviData *data)
989 {
990     WStacking *st;
991     WRegion *res;
992     
993     if(rel!=NULL){
994         st=mplex_find_stacking(mplex, rel);
995         if(st==NULL)
996             return NULL;
997     }else if(mplex->mx_current!=NULL){
998         st=mplex->mx_current->st;
999     }else{
1000         return mplex_navi_first(mplex, nh, data);
1001     }
1002     
1003     if(nh==REGION_NAVI_ANY){
1004         /* ? */
1005     }
1006     
1007     if(nh==REGION_NAVI_ANY || nh==REGION_NAVI_END || 
1008        nh==REGION_NAVI_BOTTOM || nh==REGION_NAVI_RIGHT){
1009         res=do_navi(mplex, st, mplex_nxt, data, FALSE, FALSE);
1010     }else{
1011         res=do_navi(mplex, st, mplex_prv, data, FALSE, FALSE);
1012     }
1013     
1014     return region_navi_cont((WRegion*)mplex, res, data);
1015 }
1016
1017
1018 /*}}}*/
1019
1020
1021 /*{{{ Stacking */
1022
1023
1024 bool mplex_managed_rqorder(WMPlex *mplex, WRegion *reg, WRegionOrder order)
1025 {
1026     WStacking **stackingp=mplex_get_stackingp(mplex);
1027     WStacking *st;
1028
1029     if(stackingp==NULL || *stackingp==NULL)
1030         return FALSE;
1031
1032     st=mplex_find_stacking(mplex, reg);
1033     
1034     if(st==NULL)
1035         return FALSE;
1036     
1037     stacking_restack(stackingp, st, None, NULL, NULL,
1038                      (order!=REGION_ORDER_FRONT));
1039     
1040     return TRUE;
1041 }
1042
1043
1044 /*}}}*/
1045
1046
1047 /*{{{ Attach */
1048
1049
1050 static bool mplex_stack(WMPlex *mplex, WStacking *st)
1051 {
1052     WStacking *tmp=NULL;
1053     Window bottom=None, top=None;
1054     WStacking **stackingp=mplex_get_stackingp(mplex);
1055     
1056     if(stackingp==NULL)
1057         return FALSE;
1058
1059     LINK_ITEM_FIRST(tmp, st, next, prev);
1060     stacking_weave(stackingp, &tmp, FALSE);
1061     assert(tmp==NULL);
1062     
1063     return TRUE;
1064 }
1065
1066
1067 static void mplex_unstack(WMPlex *mplex, WStacking *st)
1068 {
1069     WStacking *stacking;
1070     
1071     stacking=mplex_get_stacking(mplex);
1072     
1073     stacking_unstack(&mplex->win, st);
1074 }
1075
1076
1077 /* WMPlexWPHolder is used for position marking in order to allow
1078  * WLListNodes be safely removed in the attach handler hnd, that
1079  * could remove something this mplex is managing.
1080  */
1081 bool mplex_do_attach_final(WMPlex *mplex, WRegion *reg, WMPlexPHolder *ph)
1082 {
1083     WStacking *node=NULL;
1084     WLListNode *lnode=NULL;
1085     WMPlexAttachParams *param=&ph->param;
1086     bool mx_was_empty, sw, modal, mcf, hidden;
1087     uint level;
1088     
1089     mcf=region_may_control_focus((WRegion*)mplex);
1090     
1091     mx_was_empty=(mplex->mx_list==NULL);
1092     
1093     modal=(param->flags&MPLEX_ATTACH_LEVEL
1094            ? param->level>=STACKING_LEVEL_MODAL1
1095            : param->flags&MPLEX_ATTACH_MODAL);
1096     
1097     level=(param->flags&MPLEX_ATTACH_LEVEL
1098            ? param->level
1099            : (param->flags&MPLEX_ATTACH_MODAL
1100               ? STACKING_LEVEL_MODAL1
1101               : (param->flags&MPLEX_ATTACH_UNNUMBERED
1102                  ? STACKING_LEVEL_NORMAL
1103                  : STACKING_LEVEL_BOTTOM)));
1104     
1105     hidden=(param->flags&MPLEX_ATTACH_HIDDEN
1106             && (param->flags&MPLEX_ATTACH_UNNUMBERED
1107                 || !mx_was_empty));
1108     
1109     sw=(!hidden && (param->flags&MPLEX_ATTACH_SWITCHTO 
1110                     || (param->flags&MPLEX_ATTACH_UNNUMBERED
1111                         ? modal
1112                         : (mplex_current_node(mplex)==NULL))));
1113     
1114     hidden=(hidden || (!sw && !(param->flags&MPLEX_ATTACH_UNNUMBERED)));
1115     
1116     
1117     node=create_stacking();
1118     
1119     if(node==NULL)
1120         return FALSE;
1121     
1122     if(!(param->flags&MPLEX_ATTACH_UNNUMBERED)){
1123         lnode=ALLOC(WLListNode);
1124         if(lnode==NULL){
1125             stacking_free(node);
1126             return FALSE;
1127         }
1128         lnode->next=NULL;
1129         lnode->prev=NULL;
1130         lnode->phs=NULL;
1131         lnode->st=node;
1132         node->lnode=lnode;
1133     }
1134     
1135     if(!stacking_assoc(node, reg)){
1136         if(lnode!=NULL){
1137             node->lnode=NULL;
1138             free(lnode);
1139         }
1140         stacking_free(node);
1141         return FALSE;
1142     }
1143
1144     node->hidden=TRUE;
1145     node->szplcy=param->szplcy;
1146     node->level=level;
1147     
1148     if(lnode!=NULL){
1149         llist_link_after(&(mplex->mx_list), 
1150                          (ph!=NULL ? ph->after : NULL), 
1151                          lnode);
1152         mplex->mx_count++;
1153         
1154         /* Move following placeholders after new node */
1155         while(ph->next!=NULL)
1156             mplexpholder_move(ph->next, mplex, NULL, lnode);
1157     }
1158     
1159     LINK_ITEM(mplex->mgd, node, mgr_next, mgr_prev);
1160
1161     if(!OBJ_IS(reg, WGroup))
1162         mplex_stack(mplex, node);
1163     
1164     region_set_manager(reg, (WRegion*)mplex);
1165     
1166     if(!hidden)
1167         mplex_do_node_display(mplex, node, FALSE);
1168     else
1169         region_unmap(reg);
1170     
1171     if(sw && mcf)
1172         mplex_refocus(mplex, node, FALSE);
1173     
1174     if(lnode!=NULL)
1175         mplex_managed_changed(mplex, MPLEX_CHANGE_ADD, sw, reg);
1176     
1177     return TRUE;
1178 }
1179
1180
1181 WRegion *mplex_do_attach_pholder(WMPlex *mplex, WMPlexPHolder *ph,
1182                                  WRegionAttachData *data)
1183 {
1184     WMPlexAttachParams *param=&(ph->param);
1185     WSizePolicy szplcy=param->szplcy;
1186     WFitParams fp;
1187     WRegion *reg;
1188     
1189     param->szplcy=(param->flags&MPLEX_ATTACH_SIZEPOLICY &&
1190                    param->szplcy!=SIZEPOLICY_DEFAULT
1191                    ? param->szplcy
1192                    : (param->flags&MPLEX_ATTACH_UNNUMBERED
1193                       ? SIZEPOLICY_FULL_BOUNDS
1194                       : SIZEPOLICY_FULL_EXACT));
1195     
1196     mplex_managed_geom(mplex, &(fp.g));
1197     
1198     sizepolicy(&param->szplcy, NULL, 
1199                (param->flags&MPLEX_ATTACH_GEOM 
1200                 ? &(param->geom)
1201                 : NULL),
1202                0, &fp);
1203     
1204     if(param->flags&MPLEX_ATTACH_WHATEVER)
1205         fp.mode|=REGION_FIT_WHATEVER;
1206     
1207     reg=region_attach_helper((WRegion*)mplex, 
1208                              (WWindow*)mplex, &fp,
1209                              (WRegionDoAttachFn*)mplex_do_attach_final,
1210                              (void*)ph, data);
1211     
1212     /* restore */
1213     ph->param.szplcy=szplcy;
1214     
1215     return reg;
1216 }
1217
1218
1219 WRegion *mplex_do_attach(WMPlex *mplex, WMPlexAttachParams *param,
1220                          WRegionAttachData *data)
1221 {
1222     WMPlexPHolder *ph;
1223     WRegion *reg;
1224     
1225     ph=create_mplexpholder(mplex, NULL, param);
1226     
1227     if(ph==NULL)
1228         return NULL;
1229
1230     reg=mplex_do_attach_pholder(mplex, ph, data);
1231     
1232     destroy_obj((Obj*)ph);
1233     
1234     return reg;
1235 }
1236
1237
1238 WRegion *mplex_do_attach_new(WMPlex *mplex, WMPlexAttachParams *param,
1239                              WRegionCreateFn *fn, void *fn_param)
1240 {
1241     WRegionAttachData data;
1242     
1243     data.type=REGION_ATTACH_NEW;
1244     data.u.n.fn=fn;
1245     data.u.n.param=fn_param;
1246     
1247     return mplex_do_attach(mplex, param, &data);
1248 }
1249
1250
1251 #define MPLEX_ATTACH_SET_FLAGS (MPLEX_ATTACH_GEOM|       \
1252                                 MPLEX_ATTACH_SIZEPOLICY| \
1253                                 MPLEX_ATTACH_INDEX)
1254
1255
1256 WRegion *mplex_attach_simple(WMPlex *mplex, WRegion *reg, int flags)
1257 {
1258     WMPlexAttachParams param;
1259     WRegionAttachData data;
1260     
1261     param.flags=flags&~MPLEX_ATTACH_SET_FLAGS;
1262     
1263     data.type=REGION_ATTACH_REPARENT;
1264     data.u.reg=reg;
1265     
1266     return mplex_do_attach(mplex, &param, &data);
1267 }
1268
1269
1270 static void get_params(WMPlex *mplex, ExtlTab tab, int mask,
1271                        WMPlexAttachParams *par)
1272 {
1273     int layer=1;
1274     int tmp;
1275     int ok=~mask;
1276     
1277     if(mask==0 && extl_table_gets_i(tab, "layer", &tmp)){
1278         /* backwards compatibility. No need to consider masked cases */
1279         if(tmp==2){
1280             par->flags|=MPLEX_ATTACH_UNNUMBERED;
1281             if(!extl_table_is_bool_set(tab, "passive"))
1282                 par->flags|=MPLEX_ATTACH_MODAL;
1283         }
1284     }
1285
1286     if(extl_table_gets_i(tab, "level", &tmp)){
1287         if(tmp>=0 && ok&MPLEX_ATTACH_LEVEL){
1288             par->flags|=MPLEX_ATTACH_LEVEL;
1289             par->level=tmp;
1290         }
1291     }
1292     
1293     if(extl_table_is_bool_set(tab, "modal"))
1294         par->flags|=MPLEX_ATTACH_MODAL&ok;
1295
1296     if(extl_table_is_bool_set(tab, "unnumbered"))
1297         par->flags|=MPLEX_ATTACH_UNNUMBERED&ok;
1298     
1299     if(extl_table_is_bool_set(tab, "switchto"))
1300         par->flags|=MPLEX_ATTACH_SWITCHTO&ok;
1301
1302     if(extl_table_is_bool_set(tab, "hidden"))
1303         par->flags|=MPLEX_ATTACH_HIDDEN&ok;
1304
1305     if(extl_table_gets_i(tab, "index", &(par->index)))
1306         par->flags|=MPLEX_ATTACH_INDEX&ok;
1307
1308     if(extl_table_gets_i(tab, "sizepolicy", &tmp)){
1309         if(ok&MPLEX_ATTACH_SIZEPOLICY){
1310             par->flags|=MPLEX_ATTACH_SIZEPOLICY;
1311             par->szplcy=tmp;
1312         }
1313     }
1314     
1315     if(extl_table_gets_rectangle(tab, "geom", &par->geom))
1316         par->flags|=MPLEX_ATTACH_GEOM&ok;
1317 }
1318
1319
1320 /*EXTL_DOC
1321  * Attach and reparent existing region \var{reg} to \var{mplex}.
1322  * The table \var{param} may contain the fields \var{index} and
1323  * \var{switchto} that are interpreted as for \fnref{WMPlex.attach_new}.
1324  */
1325 EXTL_EXPORT_MEMBER
1326 WRegion *mplex_attach(WMPlex *mplex, WRegion *reg, ExtlTab param)
1327 {
1328     WMPlexAttachParams par=MPLEXATTACHPARAMS_INIT;
1329     WRegionAttachData data;
1330
1331     if(reg==NULL)
1332         return NULL;
1333     
1334     get_params(mplex, param, 0, &par);
1335     
1336     data.type=REGION_ATTACH_REPARENT;
1337     data.u.reg=reg;
1338     
1339     return mplex_do_attach(mplex, &par, &data);
1340 }
1341
1342
1343 WRegion *mplex_attach_new_(WMPlex *mplex, WMPlexAttachParams *par, 
1344                            int mask, ExtlTab param)
1345 {
1346     WRegionAttachData data;
1347     
1348     get_params(mplex, param, mask, par);
1349     
1350     data.type=REGION_ATTACH_LOAD;
1351     data.u.tab=param;
1352     
1353     return mplex_do_attach(mplex, par, &data);
1354 }
1355
1356
1357 /*EXTL_DOC
1358  * Create a new region to be managed by \var{mplex}. At least the following
1359  * fields in \var{param} are understood (all but \var{type} are optional).
1360  * 
1361  * \begin{tabularx}{\linewidth}{lX}
1362  *  \tabhead{Field & Description}
1363  *  \var{type} & (string) Class name (a string) of the object to be created. \\
1364  *  \var{name} & (string) Name of the object to be created (a string). \\
1365  *  \var{switchto} & (boolean) Should the region be switched to (boolean)? \\
1366  *  \var{unnumbered} & (boolean) Do not put on the numbered mutually 
1367  *                     exclusive list. \\
1368  *  \var{index} & (integer) Index on this list, same as for 
1369  *                \fnref{WMPlex.set_index}. \\
1370  *  \var{level} & (integer) Stacking level. \\
1371  *  \var{modal} & (boolean) Shortcut for modal stacking level. \\
1372  *  \var{hidden} & (boolean) Attach hidden, if not prevented
1373  *                  by e.g. the mutually exclusive list being empty.
1374  *                  This option overrides \var{switchto}. \\
1375  *  \var{sizepolicy} & (integer) Size policy.
1376  *                     (TODO: document them somewhere.) \\
1377  *  \var{geom} & (table) Geometry specification. \\
1378  * \end{tabularx}
1379  * 
1380  * In addition parameters to the region to be created are passed in this 
1381  * same table.
1382  */
1383 EXTL_EXPORT_MEMBER
1384 WRegion *mplex_attach_new(WMPlex *mplex, ExtlTab param)
1385 {
1386     WMPlexAttachParams par=MPLEXATTACHPARAMS_INIT;
1387     
1388     return mplex_attach_new_(mplex, &par, 0, param);
1389 }
1390
1391
1392 /*EXTL_DOC
1393  * Attach all tagged regions to \var{mplex}.
1394  */
1395 EXTL_EXPORT_MEMBER
1396 void mplex_attach_tagged(WMPlex *mplex)
1397 {
1398     WRegion *reg;
1399     int flags=MPLEX_ATTACH_SWITCHTO;
1400     
1401     while((reg=ioncore_tagged_take_first())!=NULL){
1402         mplex_attach_simple(mplex, reg, flags);
1403         /*flags=0;*/
1404     }
1405 }
1406
1407
1408 static bool mplex_handle_drop(WMPlex *mplex, int x, int y,
1409                               WRegion *dropped)
1410 {
1411     WRegion *curr=mplex_mx_current(mplex);
1412     
1413     /* This code should handle dropping tabs on floating workspaces. */
1414     if(curr && HAS_DYN(curr, region_handle_drop)){
1415         int rx, ry;
1416         region_rootpos(curr, &rx, &ry);
1417         if(rectangle_contains(&REGION_GEOM(curr), x-rx, y-ry)){
1418             if(region_handle_drop(curr, x, y, dropped))
1419                 return TRUE;
1420         }
1421     }
1422     
1423     return (NULL!=mplex_attach_simple(mplex, dropped, MPLEX_ATTACH_SWITCHTO));
1424 }
1425
1426
1427 WPHolder *mplex_prepare_manage(WMPlex *mplex, const WClientWin *cwin,
1428                                const WManageParams *param, int redir)
1429 {
1430     WMPlexAttachParams ap;
1431     WPHolder *ph=NULL;
1432     WMPlexPHolder *mph;
1433     WLListNode *after;
1434     
1435     if(redir==MANAGE_REDIR_STRICT_YES || redir==MANAGE_REDIR_PREFER_YES){
1436         WStacking *cur=mplex_current_node(mplex);
1437         
1438         if(cur!=NULL){
1439             ph=region_prepare_manage(cur->reg, cwin, param,
1440                                      MANAGE_REDIR_PREFER_YES);
1441             if(ph!=NULL)
1442                 return ph;
1443         }
1444         
1445         if(mplex->mx_current!=NULL && mplex->mx_current->st!=cur){
1446             ph=region_prepare_manage(mplex->mx_current->st->reg, 
1447                                      cwin, param, 
1448                                      MANAGE_REDIR_PREFER_YES);
1449             if(ph!=NULL)
1450                 return ph;
1451         }
1452     }
1453     
1454     if(redir==MANAGE_REDIR_STRICT_YES)
1455         return NULL;
1456     
1457     ap.flags=((param->switchto ? MPLEX_ATTACH_SWITCHTO : 0)
1458               |MPLEX_ATTACH_SIZEPOLICY);
1459     ap.szplcy=SIZEPOLICY_FULL_BOUNDS;
1460     
1461     mph=create_mplexpholder(mplex, NULL, &ap);
1462     
1463     if(mph!=NULL){
1464         WGroupedPHolder *gph=create_groupedpholder((WPHolder*)mph);
1465         if(gph!=NULL)
1466             return (WPHolder*)gph;
1467     }
1468     
1469     return (WPHolder*)mph;
1470 }
1471
1472
1473 /*}}}*/
1474
1475
1476 /*{{{ Remove */
1477
1478
1479 void mplex_managed_remove(WMPlex *mplex, WRegion *sub)
1480 {
1481     bool mx=FALSE, hadfocus=FALSE, mcf;
1482     WRegion *stdisp=(WRegion*)(mplex->stdispwatch.obj);
1483     WStacking *node, *next=NULL;
1484  
1485     mcf=region_may_control_focus((WRegion*)mplex);
1486     
1487     if(stdisp!=NULL){
1488         if(CAN_MANAGE_STDISP(sub) && 
1489            region_managed_within((WRegion*)mplex, stdisp)==sub){
1490             region_unmanage_stdisp(sub, TRUE, TRUE);
1491             region_detach_manager(stdisp);
1492         }
1493     }
1494     
1495     node=mplex_find_stacking(mplex, sub);
1496     
1497     if(node==NULL)
1498         return;
1499     
1500     hadfocus=(mplex_current_node(mplex)==node);
1501     
1502     if(node->lnode!=NULL){
1503         if(mplex->mx_current==node->lnode){
1504             WLListNode *lnext;
1505             
1506             mplex->mx_current=NULL;
1507             lnext=LIST_PREV(mplex->mx_list, node->lnode, next, prev);
1508             if(lnext==NULL){
1509                 lnext=LIST_NEXT(mplex->mx_list, node->lnode, next, prev);
1510                 if(lnext==node->lnode)
1511                     lnext=NULL;
1512             }
1513             if(lnext!=NULL)
1514                 next=lnext->st;
1515         }
1516         
1517         mplex_move_phs_before(mplex, node->lnode);
1518         llist_unlink(&(mplex->mx_list), node->lnode);
1519         mplex->mx_count--;
1520
1521         free(node->lnode);
1522         node->lnode=NULL;
1523         mx=TRUE;
1524     }
1525     
1526     UNLINK_ITEM(mplex->mgd, node, mgr_next, mgr_prev);
1527     
1528     mplex_unstack(mplex, node);
1529     
1530     stacking_unassoc(node);
1531     stacking_free(node);
1532
1533     region_unset_manager(sub, (WRegion*)mplex);
1534     
1535     if(OBJ_IS_BEING_DESTROYED(mplex))
1536         return;
1537     
1538     if(next!=NULL)
1539         mplex_do_node_display(mplex, next, FALSE);
1540
1541     if(hadfocus && mcf)
1542         mplex_refocus(mplex, next, FALSE);
1543     
1544     if(mx)
1545         mplex_managed_changed(mplex, MPLEX_CHANGE_REMOVE, next!=NULL, sub);
1546 }
1547
1548
1549 bool mplex_rescue_clientwins(WMPlex *mplex, WPHolder *ph)
1550 {
1551     bool ret1, ret2;
1552     WMPlexIterTmp tmp;
1553     
1554     mplex_iter_init(&tmp, mplex);
1555     ret1=region_rescue_some_clientwins((WRegion*)mplex, ph,
1556                                        (WRegionIterator*)mplex_iter,
1557                                        &tmp);
1558     
1559     ret2=region_rescue_child_clientwins((WRegion*)mplex, ph);
1560     
1561     return (ret1 && ret2);
1562 }
1563
1564
1565
1566 void mplex_child_removed(WMPlex *mplex, WRegion *sub)
1567 {
1568     if(sub!=NULL && sub==(WRegion*)(mplex->stdispwatch.obj)){
1569         watch_reset(&(mplex->stdispwatch));
1570         mplex_set_stdisp(mplex, NULL, NULL);
1571     }
1572 }
1573
1574
1575 /*}}}*/
1576
1577
1578 /*{{{ Status display support */
1579
1580 #ifndef offsetof
1581 # define offsetof(T,F) ((size_t)((char*)&((T*)0L)->F-(char*)0L))
1582 #endif
1583
1584 #define STRUCTOF(T, F, FADDR) \
1585         ((T*)((char*)(FADDR)-offsetof(T, F)))
1586
1587
1588 static void stdisp_watch_handler(Watch *watch, Obj *obj)
1589 {
1590     /*WMPlex *mplex=STRUCTOF(WMPlex, stdispinfo, 
1591      STRUCTOF(WMPlexSTDispInfo, regwatch, watch));
1592      WMPlexSTDispInfo *di=&(mplex->stdispinfo);
1593      WGenWS *ws=OBJ_CAST(REGION_MANAGER(obj), WGenWS);
1594      * 
1595      if(ioncore_g.opmode!=IONCORE_OPMODE_DEINIT && ws!=NULL)
1596      genws_unmanage_stdisp(ws, TRUE, FALSE);*/
1597 }
1598
1599
1600 bool mplex_set_stdisp(WMPlex *mplex, WRegion *reg, 
1601                       const WMPlexSTDispInfo *din)
1602 {
1603     WRegion *oldstdisp=(WRegion*)(mplex->stdispwatch.obj);
1604     WRegion *mgr=NULL;
1605     
1606     assert(reg==NULL || (reg==oldstdisp) ||
1607            (REGION_MANAGER(reg)==NULL && 
1608             REGION_PARENT(reg)==(WWindow*)mplex));
1609     
1610     if(oldstdisp!=NULL){
1611         mgr=region_managed_within((WRegion*)mplex, oldstdisp);
1612         
1613         if(!CAN_MANAGE_STDISP(mgr))
1614             mgr=NULL;
1615     }
1616     
1617     if(din!=NULL)
1618         mplex->stdispinfo=*din;
1619     
1620     if(reg==NULL){
1621         watch_reset(&(mplex->stdispwatch));
1622         
1623         if(mgr!=NULL){
1624             region_unmanage_stdisp(mgr, TRUE, FALSE);
1625             if(oldstdisp!=NULL)
1626                 region_detach_manager(oldstdisp);
1627         }
1628     }else{
1629         watch_setup(&(mplex->stdispwatch), (Obj*)reg, stdisp_watch_handler);
1630         
1631         mplex_remanage_stdisp(mplex);
1632     }
1633     
1634     if(oldstdisp!=NULL && oldstdisp!=reg)
1635         region_dispose((WRegion*)oldstdisp, FALSE);
1636     
1637     return TRUE;
1638 }
1639
1640
1641 void mplex_get_stdisp(WMPlex *mplex, WRegion **reg, WMPlexSTDispInfo *di)
1642 {
1643     *di=mplex->stdispinfo;
1644     *reg=(WRegion*)mplex->stdispwatch.obj;
1645 }
1646
1647
1648 static StringIntMap pos_map[]={
1649     {"tl", MPLEX_STDISP_TL},
1650     {"tr", MPLEX_STDISP_TR},
1651     {"bl", MPLEX_STDISP_BL},
1652     {"br", MPLEX_STDISP_BR},
1653     {NULL, 0}
1654 };
1655
1656
1657 static bool do_attach_stdisp(WRegion *mplex, WRegion *reg, void *unused)
1658 {
1659     /* We do not actually manage the stdisp. */
1660     return TRUE;
1661 }
1662
1663
1664 /*EXTL_DOC
1665  * Set/create status display for \var{mplex}. Table is a standard
1666  * description of the object to be created (as passed to e.g. 
1667  * \fnref{WMPlex.attach_new}). In addition, the following fields are
1668  * recognised:
1669  * 
1670  * \begin{tabularx}{\linewidth}{lX}
1671  *   \tabhead{Field & Description}
1672  *   \var{pos} & The corner of the screen to place the status display
1673  *               in. One of \code{tl}, \code{tr}, \var{bl} or \var{br}. \\
1674  *   \var{action} & If this field is set to \code{keep}, \var{corner}
1675  *                  and \var{orientation} are changed for the existing
1676  *                  status display. If this field is set to \var{remove},
1677  *                  the existing status display is removed. If this
1678  *                  field is not set or is set to \code{replace}, a 
1679  *                  new status display is created and the old, if any,
1680  *                  removed. \\
1681  * \end{tabularx}
1682  */
1683 EXTL_EXPORT_AS(WMPlex, set_stdisp)
1684 WRegion *mplex_set_stdisp_extl(WMPlex *mplex, ExtlTab t)
1685 {
1686     WRegion *stdisp=NULL;
1687     WMPlexSTDispInfo din=mplex->stdispinfo;
1688     char *s;
1689     
1690     if(extl_table_gets_s(t, "pos", &s)){
1691         din.pos=stringintmap_value(pos_map, s, -1);
1692         if(din.pos<0){
1693             warn(TR("Invalid position setting."));
1694             return NULL;
1695         }
1696     }
1697     
1698     extl_table_gets_b(t, "fullsize", &(din.fullsize));
1699     
1700     s=NULL;
1701     extl_table_gets_s(t, "action", &s);
1702     
1703     if(s==NULL || strcmp(s, "replace")==0){
1704         WRegionAttachData data;
1705         WFitParams fp;
1706         int o2;
1707         
1708         fp.g.x=0;
1709         fp.g.y=0;
1710         fp.g.w=REGION_GEOM(mplex).w;
1711         fp.g.h=REGION_GEOM(mplex).h;
1712         fp.mode=REGION_FIT_BOUNDS|REGION_FIT_WHATEVER;
1713         
1714         /* Full mplex size is stupid so use saved geometry initially
1715          * if there's one.
1716          */
1717         extl_table_gets_rectangle(t, "geom", &(fp.g));
1718         
1719         data.type=REGION_ATTACH_LOAD;
1720         data.u.tab=t;
1721             
1722         stdisp=region_attach_helper((WRegion*)mplex, 
1723                                     (WWindow*)mplex, &fp,
1724                                     do_attach_stdisp, NULL,
1725                                     &data);
1726         
1727         if(stdisp==NULL)
1728             return NULL;
1729         
1730     }else if(strcmp(s, "keep")==0){
1731         stdisp=(WRegion*)(mplex->stdispwatch.obj);
1732     }else if(strcmp(s, "remove")!=0){
1733         warn(TR("Invalid action setting."));
1734         return FALSE;
1735     }
1736     
1737     if(!mplex_set_stdisp(mplex, stdisp, &din)){
1738         destroy_obj((Obj*)stdisp);
1739         return NULL;
1740     }
1741     
1742     return stdisp;
1743 }
1744
1745
1746 static ExtlTab mplex_do_get_stdisp_extl(WMPlex *mplex, bool fullconfig)
1747 {
1748     WRegion *reg=(WRegion*)mplex->stdispwatch.obj;
1749     ExtlTab t;
1750     
1751     if(reg==NULL)
1752         return extl_table_none();
1753     
1754     if(fullconfig){
1755         t=region_get_configuration(reg);
1756         extl_table_sets_rectangle(t, "geom", &REGION_GEOM(reg));
1757     }else{
1758         t=extl_create_table();
1759         extl_table_sets_o(t, "reg", (Obj*)reg);
1760     }
1761     
1762     if(t!=extl_table_none()){
1763         WMPlexSTDispInfo *di=&(mplex->stdispinfo);
1764         extl_table_sets_s(t, "pos", stringintmap_key(pos_map, di->pos, NULL));
1765         extl_table_sets_b(t, "fullsize", di->fullsize);
1766     }
1767     return t;
1768 }
1769
1770
1771 /*EXTL_DOC
1772  * Get status display information. See \fnref{WMPlex.get_stdisp} for
1773  * information on the fields.
1774  */
1775 EXTL_SAFE
1776 EXTL_EXPORT_AS(WMPlex, get_stdisp)
1777 ExtlTab mplex_get_stdisp_extl(WMPlex *mplex)
1778 {
1779     return mplex_do_get_stdisp_extl(mplex, FALSE);
1780 }
1781
1782
1783 /*}}}*/
1784
1785
1786 /*{{{ Dynfuns */
1787
1788
1789 void mplex_managed_geom_default(const WMPlex *mplex, WRectangle *geom)
1790 {
1791     geom->x=0;
1792     geom->y=0;
1793     geom->w=REGION_GEOM(mplex).w;
1794     geom->h=REGION_GEOM(mplex).h;
1795 }
1796
1797
1798 void mplex_managed_geom(const WMPlex *mplex, WRectangle *geom)
1799 {
1800     CALL_DYN(mplex_managed_geom, mplex, (mplex, geom));
1801 }
1802
1803
1804 void mplex_size_changed(WMPlex *mplex, bool wchg, bool hchg)
1805 {
1806     CALL_DYN(mplex_size_changed, mplex, (mplex, wchg, hchg));
1807 }
1808
1809
1810 void mplex_managed_changed(WMPlex *mplex, int mode, bool sw, WRegion *mgd)
1811 {
1812     CALL_DYN(mplex_managed_changed, mplex, (mplex, mode, sw, mgd));
1813 }
1814
1815
1816 int mplex_default_index(WMPlex *mplex)
1817 {
1818     int idx=LLIST_INDEX_LAST;
1819     CALL_DYN_RET(idx, int, mplex_default_index, mplex, (mplex));
1820     return idx;
1821 }
1822
1823
1824 /* For regions managing stdisps */
1825
1826 void region_manage_stdisp(WRegion *reg, WRegion *stdisp, 
1827                           const WMPlexSTDispInfo *info)
1828 {
1829     CALL_DYN(region_manage_stdisp, reg, (reg, stdisp, info));
1830 }
1831
1832
1833 void region_unmanage_stdisp(WRegion *reg, bool permanent, bool nofocus)
1834 {
1835     CALL_DYN(region_unmanage_stdisp, reg, (reg, permanent, nofocus));
1836 }
1837
1838
1839 /*}}}*/
1840
1841
1842 /*{{{ Changed hook helper */
1843
1844
1845 static const char *mode2str(int mode)
1846 {
1847     if(mode==MPLEX_CHANGE_SWITCHONLY)
1848         return "switchonly";
1849     else if(mode==MPLEX_CHANGE_REORDER)
1850         return "reorder";
1851     else if(mode==MPLEX_CHANGE_ADD)
1852         return "add";
1853     else if(mode==MPLEX_CHANGE_REMOVE)
1854         return "remove";
1855     return NULL;
1856 }
1857     
1858
1859 static bool mrsh_chg(ExtlFn fn, WMPlexChangedParams *p)
1860 {
1861     ExtlTab t=extl_create_table();
1862     bool ret;
1863     
1864     extl_table_sets_o(t, "reg", (Obj*)p->reg);
1865     extl_table_sets_s(t, "mode", mode2str(p->mode));
1866     extl_table_sets_b(t, "sw", p->sw);
1867     extl_table_sets_o(t, "sub", (Obj*)p->sub);
1868     
1869     extl_protect(NULL);
1870     ret=extl_call(fn, "t", NULL, t);
1871     extl_unprotect(NULL);
1872     
1873     extl_unref_table(t);
1874     
1875     return ret;
1876 }
1877
1878
1879 void mplex_call_changed_hook(WMPlex *mplex, WHook *hook, 
1880                              int mode, bool sw, WRegion *reg)
1881 {
1882     WMPlexChangedParams p;
1883     
1884     p.reg=mplex;
1885     p.mode=mode;
1886     p.sw=sw;
1887     p.sub=reg;
1888
1889     hook_call_p(hook, &p, (WHookMarshallExtl*)mrsh_chg);
1890 }
1891
1892
1893 /*}}} */
1894
1895
1896 /*{{{ Save/load */
1897
1898
1899 static void save_node(WMPlex *mplex, ExtlTab subs, int *n, 
1900                       WStacking *node, bool unnumbered)
1901 {
1902     ExtlTab st, g;
1903     
1904     st=region_get_configuration(node->reg);
1905     
1906     if(st!=extl_table_none()){
1907         /*"TODO: better switchto saving? */
1908         if(mplex->mx_current!=NULL && node==mplex->mx_current->st)
1909             extl_table_sets_b(st, "switchto", TRUE);
1910         extl_table_sets_i(st, "sizepolicy", node->szplcy);
1911         extl_table_sets_i(st, "level", node->level);
1912         g=extl_table_from_rectangle(&REGION_GEOM(node->reg));
1913         extl_table_sets_t(st, "geom", g);
1914         extl_unref_table(g);
1915         if(STACKING_IS_HIDDEN(node))
1916             extl_table_sets_b(st, "hidden", TRUE);
1917         if(unnumbered)
1918             extl_table_sets_b(st, "unnumbered", TRUE);
1919         
1920         extl_table_seti_t(subs, ++(*n), st);
1921         extl_unref_table(st);
1922     }
1923 }
1924
1925
1926 ExtlTab mplex_get_configuration(WMPlex *mplex)
1927 {
1928     ExtlTab tab, subs, stdisptab;
1929     WMPlexIterTmp tmp;
1930     WLListIterTmp ltmp;
1931     WLListNode *lnode;
1932     WStacking *node;
1933     int n=0;
1934     
1935     tab=region_get_base_configuration((WRegion*)mplex);
1936     
1937     subs=extl_create_table();
1938     extl_table_sets_t(tab, "managed", subs);
1939     
1940     /* First the numbered/mutually exclusive nodes */
1941     FOR_ALL_NODES_ON_LLIST(lnode, mplex->mx_list, ltmp){
1942         save_node(mplex, subs, &n, lnode->st, FALSE);
1943     }
1944
1945     FOR_ALL_NODES_IN_MPLEX(mplex, node, tmp){
1946         if(node->lnode==NULL)
1947             save_node(mplex, subs, &n, node, TRUE);
1948     }
1949
1950     extl_unref_table(subs);
1951     
1952     /*stdisptab=mplex_do_get_stdisp_extl(mplex, TRUE);
1953     if(stdisptab!=extl_table_none()){
1954         extl_table_sets_t(tab, "stdisp", stdisptab);
1955         extl_unref_table(stdisptab);
1956     }*/
1957     
1958     return tab;
1959 }
1960
1961
1962 static WMPlex *tmp_mplex=NULL;
1963 static WMPlexAttachParams *tmp_par=NULL;
1964
1965 static WPHolder *pholder_callback()
1966 {
1967     assert(tmp_mplex!=NULL);
1968     return (WPHolder*)create_mplexpholder(tmp_mplex, NULL, tmp_par);
1969 }
1970
1971
1972 void mplex_load_contents(WMPlex *mplex, ExtlTab tab)
1973 {
1974     ExtlTab substab, subtab;
1975     int n, i;
1976
1977     /*if(extl_table_gets_t(tab, "stdisp", &subtab)){
1978         mplex_set_stdisp_extl(mplex, subtab);
1979         extl_unref_table(subtab);
1980     }*/
1981     
1982     if(extl_table_gets_t(tab, "managed", &substab) ||
1983        extl_table_gets_t(tab, "subs", &substab)){
1984         n=extl_table_get_n(substab);
1985         for(i=1; i<=n; i++){
1986             if(extl_table_geti_t(substab, i, &subtab)){
1987                 /*mplex_attach_new(mplex, subtab);*/
1988                 WMPlexAttachParams par=MPLEXATTACHPARAMS_INIT;
1989                 WRegionAttachData data;
1990                 char *tmp=NULL;
1991                 
1992                 get_params(mplex, subtab, 0, &par);
1993                 
1994                 par.flags|=MPLEX_ATTACH_INDEX;
1995                 par.index=LLIST_INDEX_LAST;
1996                 
1997                 tmp_par=&par;
1998                 tmp_mplex=mplex;
1999                 
2000                 data.type=REGION_ATTACH_LOAD;
2001                 data.u.tab=subtab;
2002                 
2003                 ioncore_set_sm_pholder_callback(pholder_callback);
2004     
2005                 mplex_do_attach(mplex, &par, &data);
2006
2007                 tmp_mplex=NULL;
2008                 
2009                 extl_unref_table(subtab);
2010             }
2011         }
2012         extl_unref_table(substab);
2013     }
2014 }
2015
2016
2017 WRegion *mplex_load(WWindow *par, const WFitParams *fp, ExtlTab tab)
2018 {
2019     WMPlex *mplex=create_mplex(par, fp);
2020     if(mplex!=NULL)
2021         mplex_load_contents(mplex, tab);
2022     return (WRegion*)mplex;
2023 }
2024
2025
2026 /*}}}*/
2027
2028
2029 /*{{{ Dynfuntab and class info */
2030
2031
2032 static DynFunTab mplex_dynfuntab[]={
2033     {region_do_set_focus, 
2034      mplex_do_set_focus},
2035     
2036     {region_managed_remove, 
2037      mplex_managed_remove},
2038     
2039     {region_managed_rqgeom,
2040      mplex_managed_rqgeom},
2041     
2042     {(DynFun*)region_managed_prepare_focus,
2043      (DynFun*)mplex_managed_prepare_focus},
2044     
2045     {(DynFun*)region_handle_drop, 
2046      (DynFun*)mplex_handle_drop},
2047     
2048     {region_map, mplex_map},
2049     {region_unmap, mplex_unmap},
2050     
2051     {(DynFun*)region_prepare_manage,
2052      (DynFun*)mplex_prepare_manage},
2053     
2054     {(DynFun*)region_current,
2055      (DynFun*)mplex_current},
2056     
2057     {(DynFun*)region_rescue_clientwins,
2058      (DynFun*)mplex_rescue_clientwins},
2059     
2060     {(DynFun*)region_get_configuration,
2061      (DynFun*)mplex_get_configuration},
2062     
2063     {mplex_managed_geom, 
2064      mplex_managed_geom_default},
2065     
2066     {(DynFun*)region_fitrep,
2067      (DynFun*)mplex_fitrep},
2068     
2069     {region_child_removed,
2070      mplex_child_removed},
2071     
2072     {(DynFun*)region_managed_get_pholder,
2073      (DynFun*)mplex_managed_get_pholder},
2074
2075     {(DynFun*)region_get_rescue_pholder_for,
2076      (DynFun*)mplex_get_rescue_pholder_for},
2077
2078     {(DynFun*)region_may_destroy,
2079      (DynFun*)mplex_may_destroy},
2080     
2081     {(DynFun*)region_navi_first,
2082      (DynFun*)mplex_navi_first},
2083     
2084     {(DynFun*)region_navi_next,
2085      (DynFun*)mplex_navi_next},
2086     
2087     {(DynFun*)region_managed_rqorder,
2088      (DynFun*)mplex_managed_rqorder},
2089     
2090     END_DYNFUNTAB
2091 };
2092
2093
2094 EXTL_EXPORT
2095 IMPLCLASS(WMPlex, WWindow, mplex_deinit, mplex_dynfuntab);
2096
2097
2098 /*}}}*/
2099