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