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