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