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