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