]> git.decadent.org.uk Git - ion3.git/blob - mod_tiling/tiling.c
[svn-upgrade] Integrating new upstream version, ion3 (20070506)
[ion3.git] / mod_tiling / tiling.c
1 /*
2  * ion/mod_tiling/tiling.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2007. 
5  *
6  * See the included file LICENSE for details.
7  */
8
9 #include <string.h>
10
11 #include <libtu/objp.h>
12 #include <libtu/minmax.h>
13 #include <libtu/ptrlist.h>
14 #include <libmainloop/defer.h>
15 #include <libmainloop/signal.h>
16
17 #include <ioncore/common.h>
18 #include <ioncore/rootwin.h>
19 #include <ioncore/focus.h>
20 #include <ioncore/global.h>
21 #include <ioncore/region.h>
22 #include <ioncore/manage.h>
23 #include <ioncore/screen.h>
24 #include <ioncore/names.h>
25 #include <ioncore/saveload.h>
26 #include <ioncore/attach.h>
27 #include <ioncore/resize.h>
28 #include <libextl/extl.h>
29 #include <ioncore/regbind.h>
30 #include <ioncore/extlconv.h>
31 #include <ioncore/xwindow.h>
32 #include <ioncore/navi.h>
33 #include "placement.h"
34 #include "tiling.h"
35 #include "split.h"
36 #include "splitfloat.h"
37 #include "split-stdisp.h"
38 #include "main.h"
39
40
41
42 static WTilingIterTmp tiling_iter_default_tmp;
43
44
45 /*{{{ Some helper routines */
46
47
48 static WSplitRegion *get_node_check(WTiling *ws, WRegion *reg)
49 {
50     WSplitRegion *node;
51
52     if(reg==NULL)
53         return NULL;
54     
55     node=splittree_node_of(reg);
56     
57     if(node==NULL || REGION_MANAGER(reg)!=(WRegion*)ws)
58         return NULL;
59     
60     return node;
61 }
62
63
64 static bool check_node(WTiling *ws, WSplit *split)
65 {
66     if(split->parent)
67         return check_node(ws, (WSplit*)split->parent);
68     
69     if((split->ws_if_root!=(void*)ws)){
70         warn(TR("Split not on workspace."));
71         return FALSE;
72     }
73     return TRUE;
74 }
75
76
77 /*}}}*/
78
79
80 /*{{{ Dynfun implementations */
81
82
83 static void reparent_mgd(WRegion *sub, WWindow *par)
84 {
85     WFitParams subfp;
86     subfp.g=REGION_GEOM(sub);
87     subfp.mode=REGION_FIT_EXACT;
88     if(!region_fitrep(sub, par, &subfp)){
89         warn(TR("Error reparenting %s."), region_name(sub));
90         region_detach_manager(sub);
91     }
92 }
93
94
95 bool tiling_fitrep(WTiling *ws, WWindow *par, const WFitParams *fp)
96 {
97     WTilingIterTmp tmp;
98     bool ok=FALSE;
99     
100     if(par!=NULL){
101         if(!region_same_rootwin((WRegion*)ws, (WRegion*)par))
102             return FALSE;
103     
104         region_unset_parent((WRegion*)ws);
105         
106         XReparentWindow(ioncore_g.dpy, ws->dummywin, 
107                         par->win, fp->g.x, fp->g.y);
108         
109         region_set_parent((WRegion*)ws, par);
110     
111         if(ws->split_tree!=NULL)
112             split_reparent(ws->split_tree, par);
113     }
114     
115     REGION_GEOM(ws)=fp->g;
116     
117     if(ws->split_tree!=NULL){
118         bool done=FALSE;
119         if(fp->mode&REGION_FIT_ROTATE)
120             ok=split_rotate_to(ws->split_tree, &(fp->g), fp->rotation);
121         if(!ok)
122             split_resize(ws->split_tree, &(fp->g), PRIMN_ANY, PRIMN_ANY);
123     }
124     
125     return TRUE;
126 }
127
128
129 void tiling_managed_rqgeom(WTiling *ws, WRegion *mgd, 
130                            const WRQGeomParams *rq,
131                            WRectangle *geomret)
132 {
133     WSplitRegion *node=get_node_check(ws, mgd);
134     if(node!=NULL && ws->split_tree!=NULL)
135         splittree_rqgeom((WSplit*)node, rq->flags, &rq->geom, geomret);
136 }
137
138
139 void tiling_map(WTiling *ws)
140 {
141     REGION_MARK_MAPPED(ws);
142     XMapWindow(ioncore_g.dpy, ws->dummywin);
143     
144     if(ws->split_tree!=NULL)
145         split_map(ws->split_tree);
146 }
147
148
149 void tiling_unmap(WTiling *ws)
150 {
151     REGION_MARK_UNMAPPED(ws);
152     XUnmapWindow(ioncore_g.dpy, ws->dummywin);
153
154     if(ws->split_tree!=NULL)
155         split_unmap(ws->split_tree);
156 }
157
158
159 void tiling_fallback_focus(WTiling *ws, bool warp)
160 {
161     region_finalise_focusing((WRegion*)ws, ws->dummywin, warp);
162 }
163
164
165 void tiling_do_set_focus(WTiling *ws, bool warp)
166 {
167     WRegion *sub=tiling_current(ws);
168     
169     if(sub==NULL){
170         tiling_fallback_focus(ws, warp);
171         return;
172     }
173
174     region_do_set_focus(sub, warp);
175 }
176
177
178 static WTimer *restack_timer=NULL;
179
180
181 static void restack_handler(WTimer *tmr, Obj *obj)
182 {
183     if(obj!=NULL){
184         WTiling *ws=(WTiling*)obj;
185         split_restack(ws->split_tree, ws->dummywin, Above);
186     }
187 }
188
189
190 bool tiling_managed_prepare_focus(WTiling *ws, WRegion *reg, 
191                                  int flags, WPrepareFocusResult *res)
192 {
193     WSplitRegion *node; 
194
195     if(!region_prepare_focus((WRegion*)ws, flags, res))
196         return FALSE;
197     
198     node=get_node_check(ws, reg);
199     
200     if(node!=NULL && node->split.parent!=NULL)
201         splitinner_mark_current(node->split.parent, &(node->split));
202         
203     /* WSplitSplit uses activity based stacking as required on WAutoWS,
204      * so we must restack here.
205      */
206     if(ws->split_tree!=NULL){
207         int rd=mod_tiling_raise_delay;
208         bool use_timer=rd>0 && flags&REGION_GOTO_ENTERWINDOW;
209         
210         if(use_timer){
211             if(restack_timer!=NULL){
212                 Obj *obj=restack_timer->objwatch.obj;
213                 if(obj!=(Obj*)ws){
214                     timer_reset(restack_timer);
215                     restack_handler(restack_timer, obj);
216                 }
217             }else{
218                 restack_timer=create_timer();
219             }
220         }
221         
222         if(use_timer && restack_timer!=NULL){
223             timer_set(restack_timer, rd, restack_handler, (Obj*)ws);
224         }else{
225             split_restack(ws->split_tree, ws->dummywin, Above);
226         }
227     }
228
229     res->reg=reg;
230     res->flags=flags;
231     return TRUE;
232 }
233
234
235
236 void tiling_restack(WTiling *ws, Window other, int mode)
237 {
238     xwindow_restack(ws->dummywin, other, mode);
239     if(ws->split_tree!=NULL)
240         split_restack(ws->split_tree, ws->dummywin, Above);
241 }
242
243
244 void tiling_stacking(WTiling *ws, Window *bottomret, Window *topret)
245 {
246     Window sbottom=None, stop=None;
247     
248     if(ws->split_tree!=None)
249         split_stacking(ws->split_tree, &sbottom, &stop);
250     
251     *bottomret=ws->dummywin;
252     *topret=(stop!=None ? stop : ws->dummywin);
253 }
254
255
256 Window tiling_xwindow(const WTiling *ws)
257 {
258     return ws->dummywin;
259 }
260
261
262 /*}}}*/
263
264
265 /*{{{ Status display support code */
266
267
268 static bool regnodefilter(WSplit *split)
269 {
270     return OBJ_IS(split, WSplitRegion);
271 }
272
273
274 void tiling_unmanage_stdisp(WTiling *ws, bool permanent, bool nofocus)
275 {
276     WSplitRegion *tofocus=NULL;
277     bool setfocus=FALSE;
278     WRegion *od;
279     
280     if(ws->stdispnode==NULL)
281         return;
282     
283     od=ws->stdispnode->regnode.reg;
284     
285     if(od!=NULL){
286         if(!nofocus && REGION_IS_ACTIVE(od) && 
287            region_may_control_focus((WRegion*)ws)){
288             setfocus=TRUE;
289             tofocus=(WSplitRegion*)split_nextto((WSplit*)(ws->stdispnode), 
290                                                 PRIMN_ANY, PRIMN_ANY,
291                                                 regnodefilter);
292         }
293         /* Reset node_of info here so tiling_managed_remove will not
294          * remove the node.
295          */
296         splittree_set_node_of(od, NULL);
297         tiling_do_managed_remove(ws, od);
298     }
299     
300     if(permanent){
301         WSplit *node=(WSplit*)ws->stdispnode;
302         ws->stdispnode=NULL;
303         splittree_remove(node, TRUE);
304     }
305     
306     if(setfocus){
307         if(tofocus!=NULL)
308             region_set_focus(tofocus->reg);
309         else
310             tiling_fallback_focus(ws, FALSE);
311     }
312 }
313
314
315 static void tiling_create_stdispnode(WTiling *ws, WRegion *stdisp, 
316                                     int corner, int orientation, 
317                                     bool fullsize)
318 {
319     int flags=REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y;
320     WRectangle *wg=&REGION_GEOM(ws), dg;
321     WSplitST *stdispnode;
322     WSplitSplit *split;
323
324     assert(ws->split_tree!=NULL);
325     
326     if(orientation==REGION_ORIENTATION_HORIZONTAL){
327         dg.x=wg->x;
328         dg.w=wg->w;
329         dg.h=0;
330         dg.y=((corner==MPLEX_STDISP_BL || corner==MPLEX_STDISP_BR)
331               ? wg->y+wg->h
332               : 0);
333     }else{
334         dg.y=wg->y;
335         dg.h=wg->h;
336         dg.w=0;
337         dg.x=((corner==MPLEX_STDISP_TR || corner==MPLEX_STDISP_BR)
338               ? wg->x+wg->w
339               : 0);
340     }
341             
342     stdispnode=create_splitst(&dg, stdisp);
343     
344     if(stdispnode==NULL){
345         warn(TR("Unable to create a node for status display."));
346         return;
347     }
348     
349     stdispnode->corner=corner;
350     stdispnode->orientation=orientation;
351     stdispnode->fullsize=fullsize;
352     
353     split=create_splitsplit(wg, (orientation==REGION_ORIENTATION_HORIZONTAL 
354                                  ? SPLIT_VERTICAL
355                                  : SPLIT_HORIZONTAL));
356
357     if(split==NULL){
358         warn(TR("Unable to create new split for status display."));
359         stdispnode->regnode.reg=NULL;
360         destroy_obj((Obj*)stdispnode);
361         return;
362     }
363
364     /* Set up new split tree */
365     ((WSplit*)stdispnode)->parent=(WSplitInner*)split;
366     ws->split_tree->parent=(WSplitInner*)split;
367     ws->split_tree->ws_if_root=NULL;
368     
369     if((orientation==REGION_ORIENTATION_HORIZONTAL && 
370         (corner==MPLEX_STDISP_BL || corner==MPLEX_STDISP_BR)) ||
371        (orientation==REGION_ORIENTATION_VERTICAL && 
372         (corner==MPLEX_STDISP_TR || corner==MPLEX_STDISP_BR))){
373         split->tl=ws->split_tree;
374         split->br=(WSplit*)stdispnode;
375         split->current=SPLIT_CURRENT_TL;
376     }else{
377         split->tl=(WSplit*)stdispnode;
378         split->br=ws->split_tree;
379         split->current=SPLIT_CURRENT_BR;
380     }
381
382     ws->split_tree=(WSplit*)split;
383     ((WSplit*)split)->ws_if_root=ws;
384     ws->stdispnode=stdispnode;
385 }
386
387
388 void tiling_manage_stdisp(WTiling *ws, WRegion *stdisp, 
389                          const WMPlexSTDispInfo *di)
390 {
391     bool mcf=region_may_control_focus((WRegion*)ws);
392     int flags=REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y;
393     int orientation=region_orientation(stdisp);
394     bool act=FALSE;
395     WRectangle dg, *stdg;
396     
397     if(orientation!=REGION_ORIENTATION_VERTICAL /*&&
398        orientation!=REGION_ORIENTATION_HORIZONTAL*/){
399         orientation=REGION_ORIENTATION_HORIZONTAL;
400     }
401     
402     if(ws->stdispnode==NULL || ws->stdispnode->regnode.reg!=stdisp)
403         region_detach_manager(stdisp);
404
405     /* Remove old stdisp if corner and orientation don't match.
406      */
407     if(ws->stdispnode!=NULL && (di->pos!=ws->stdispnode->corner ||
408                                 orientation!=ws->stdispnode->orientation)){
409         tiling_unmanage_stdisp(ws, TRUE, TRUE);
410     }
411
412     if(ws->stdispnode==NULL){
413         tiling_create_stdispnode(ws, stdisp, di->pos, orientation, 
414                                 di->fullsize);
415         if(ws->stdispnode==NULL)
416             return;
417     }else{
418         WRegion *od=ws->stdispnode->regnode.reg;
419         if(od!=NULL){
420             act=REGION_IS_ACTIVE(od);
421             splittree_set_node_of(od, NULL);
422             tiling_managed_remove(ws, od);
423             assert(ws->stdispnode->regnode.reg==NULL);
424         }
425         
426         ws->stdispnode->fullsize=di->fullsize;
427         ws->stdispnode->regnode.reg=stdisp;
428         splittree_set_node_of(stdisp, &(ws->stdispnode->regnode));
429     }
430     
431     if(!tiling_managed_add(ws, stdisp)){
432         tiling_unmanage_stdisp(ws, TRUE, TRUE);
433         return;
434     }
435     
436     dg=((WSplit*)(ws->stdispnode))->geom;
437     
438     dg.h=stdisp_recommended_h(ws->stdispnode);
439     dg.w=stdisp_recommended_w(ws->stdispnode);
440     
441     splittree_rqgeom((WSplit*)(ws->stdispnode), flags, &dg, FALSE);
442     
443     stdg=&(((WSplit*)ws->stdispnode)->geom);
444     
445     if(stdisp->geom.x!=stdg->x || stdisp->geom.y!=stdg->y ||
446        stdisp->geom.w!=stdg->w || stdisp->geom.h!=stdg->h){
447         region_fit(stdisp, stdg, REGION_FIT_EXACT);
448     }
449
450     /* Restack to ensure the split tree is stacked in the expected order. */
451     if(ws->split_tree!=NULL)
452         split_restack(ws->split_tree, ws->dummywin, Above);
453     
454     if(mcf && act)
455         region_set_focus(stdisp);
456 }
457
458
459 /*}}}*/
460
461
462 /*{{{ Create/destroy */
463
464
465 bool tiling_managed_add_default(WTiling *ws, WRegion *reg)
466 {
467     Window bottom=None, top=None;
468     WFrame *frame;
469     
470     if(TILING_STDISP_OF(ws)!=reg){
471         if(!ptrlist_insert_last(&(ws->managed_list), reg))
472             return FALSE;
473     }
474     
475     region_set_manager(reg, (WRegion*)ws);
476     
477     frame=OBJ_CAST(reg, WFrame);
478     if(frame!=NULL){
479         WFrameMode mode=frame_mode(frame);
480         if(mode!=FRAME_MODE_TILED && mode!=FRAME_MODE_TILED_ALT)
481             frame_set_mode(frame, FRAME_MODE_TILED);
482     }
483     
484     if(REGION_IS_MAPPED(ws))
485         region_map(reg);
486     
487     if(region_may_control_focus((WRegion*)ws)){
488         WRegion *curr=tiling_current(ws);
489         if(curr==NULL || !REGION_IS_ACTIVE(curr))
490             region_warp(reg);
491     }
492     
493     return TRUE;
494 }
495
496
497 bool tiling_managed_add(WTiling *ws, WRegion *reg)
498 {
499     bool ret=FALSE;
500     CALL_DYN_RET(ret, bool, tiling_managed_add, ws, (ws, reg));
501     return ret;
502 }
503
504
505 bool tiling_do_attach_initial(WTiling *ws, WRegion *reg)
506 {
507     assert(ws->split_tree==NULL);
508     
509     ws->split_tree=(WSplit*)create_splitregion(&REGION_GEOM(reg), reg);
510     if(ws->split_tree==NULL)
511         return FALSE;
512     
513     ws->split_tree->ws_if_root=ws;
514     
515     if(!tiling_managed_add(ws, reg)){
516         destroy_obj((Obj*)ws->split_tree);
517         ws->split_tree=NULL;
518         return FALSE;
519     }
520     
521     return TRUE;
522 }
523                                       
524
525 static WRegion *create_frame_tiling(WWindow *parent, const WFitParams *fp)
526 {
527     return (WRegion*)create_frame(parent, fp, FRAME_MODE_TILED);
528 }
529
530
531 bool tiling_init(WTiling *ws, WWindow *parent, const WFitParams *fp, 
532                 WRegionSimpleCreateFn *create_frame_fn, bool ci)
533 {
534     ws->split_tree=NULL;
535     ws->create_frame_fn=(create_frame_fn 
536                          ? create_frame_fn
537                          : create_frame_tiling);
538     ws->stdispnode=NULL;
539     ws->managed_list=NULL;
540     ws->batchop=FALSE;
541     
542     ws->dummywin=XCreateWindow(ioncore_g.dpy, parent->win,
543                                 fp->g.x, fp->g.y, 1, 1, 0,
544                                 CopyFromParent, InputOnly,
545                                 CopyFromParent, 0, NULL);
546     if(ws->dummywin==None)
547         return FALSE;
548
549     region_init(&(ws->reg), parent, fp);
550
551     ws->reg.flags|=(REGION_GRAB_ON_PARENT|
552                     REGION_PLEASE_WARP);
553     
554     if(ci){
555         WRegionAttachData data;
556         WRegion *res;
557         
558         data.type=REGION_ATTACH_NEW;
559         data.u.n.fn=(WRegionCreateFn*)ws->create_frame_fn;
560         data.u.n.param=NULL;
561     
562         res=region_attach_helper((WRegion*)ws, parent, fp,
563                                  (WRegionDoAttachFn*)tiling_do_attach_initial, 
564                                  NULL, &data);
565
566         if(res==NULL){
567             XDestroyWindow(ioncore_g.dpy, ws->dummywin);
568             return FALSE;
569         }
570     }
571     
572     XSelectInput(ioncore_g.dpy, ws->dummywin,
573                  FocusChangeMask|KeyPressMask|KeyReleaseMask|
574                  ButtonPressMask|ButtonReleaseMask);
575     XSaveContext(ioncore_g.dpy, ws->dummywin, ioncore_g.win_context,
576                  (XPointer)ws);
577     
578     region_register(&(ws->reg));
579     region_add_bindmap((WRegion*)ws, mod_tiling_tiling_bindmap);
580     
581     return TRUE;
582 }
583
584
585 WTiling *create_tiling(WWindow *parent, const WFitParams *fp, 
586                      WRegionSimpleCreateFn *create_frame_fn, bool ci)
587 {
588     CREATEOBJ_IMPL(WTiling, tiling, (p, parent, fp, create_frame_fn, ci));
589 }
590
591
592 WTiling *create_tiling_simple(WWindow *parent, const WFitParams *fp)
593 {
594     return create_tiling(parent, fp, NULL, TRUE);
595 }
596
597
598 void tiling_deinit(WTiling *ws)
599 {
600     WRegion *reg;
601     WTilingIterTmp tmp;
602     WMPlex *remanage_mplex=NULL;
603     
604     tiling_unmanage_stdisp(ws, FALSE, TRUE);
605
606     FOR_ALL_MANAGED_BY_TILING(reg, ws, tmp){
607         destroy_obj((Obj*)reg);
608     }
609
610     FOR_ALL_MANAGED_BY_TILING(reg, ws, tmp){
611         assert(FALSE);
612     }
613     
614     if(ws->split_tree!=NULL)
615         destroy_obj((Obj*)(ws->split_tree));
616
617     XDeleteContext(ioncore_g.dpy, ws->dummywin, ioncore_g.win_context);
618     XDestroyWindow(ioncore_g.dpy, ws->dummywin);
619     ws->dummywin=None;
620
621     region_deinit(&(ws->reg));
622 }
623
624
625 WRegion *tiling_managed_disposeroot(WTiling *ws, WRegion *reg)
626 {
627     WTilingIterTmp tmp;
628     WRegion *mgd;
629     
630     if(ws->batchop)
631         return reg;
632     
633     FOR_ALL_MANAGED_BY_TILING(mgd, ws, tmp){
634         if(mgd!=TILING_STDISP_OF(ws) && mgd!=reg)
635             return reg;
636     }
637     
638     return region_disposeroot((WRegion*)ws);
639 }
640
641
642 bool tiling_rescue_clientwins(WTiling *ws, WRescueInfo *info)
643 {
644     WTilingIterTmp tmp;
645     
646     ptrlist_iter_init(&tmp, ws->managed_list);
647     
648     return region_rescue_some_clientwins((WRegion*)ws, info,
649                                          (WRegionIterator*)ptrlist_iter, 
650                                          &tmp);
651 }
652
653
654 void tiling_do_managed_remove(WTiling *ws, WRegion *reg)
655 {
656     if(TILING_STDISP_OF(ws)==reg){
657         ws->stdispnode->regnode.reg=NULL;
658     }else{
659         ptrlist_remove(&(ws->managed_list), reg);
660     }
661
662     region_unset_manager(reg, (WRegion*)ws);
663 }
664
665
666 static bool nostdispfilter(WSplit *node)
667 {
668     return (OBJ_IS(node, WSplitRegion) && !OBJ_IS(node, WSplitST));
669 }
670
671
672 void tiling_managed_remove(WTiling *ws, WRegion *reg)
673 {
674     bool act=REGION_IS_ACTIVE(reg);
675     bool mcf=region_may_control_focus((WRegion*)ws);
676     WSplitRegion *node=get_node_check(ws, reg);
677     bool norestore=(OBJ_IS_BEING_DESTROYED(ws) || ws->batchop);
678     WRegion *other=NULL;
679
680     if(!norestore)
681         other=tiling_do_navi_next(ws, reg, REGION_NAVI_ANY, TRUE, FALSE);
682     
683     tiling_do_managed_remove(ws, reg);
684
685     if(node==(WSplitRegion*)(ws->stdispnode))
686         ws->stdispnode=NULL;
687     
688     if(node!=NULL){
689         bool reused=FALSE;
690         
691         if(other==NULL && !norestore){
692             WWindow *par=REGION_PARENT(ws);
693             WFitParams fp;
694             
695             assert(par!=NULL);
696             
697             fp.g=node->split.geom;
698             fp.mode=REGION_FIT_EXACT;
699             
700             other=(ws->create_frame_fn)(par, &fp);
701             
702             if(other!=NULL){
703                 node->reg=other;
704                 tiling_managed_add(ws, other);
705                 reused=TRUE;
706             }else{
707                 warn(TR("Tiling in useless state."));
708             }
709         }
710         
711         if(!reused)
712             splittree_remove((WSplit*)node, (!norestore && other!=NULL));
713     }
714     
715     if(!norestore && other!=NULL && act && mcf)
716         region_warp(other);
717 }
718
719
720 static bool mplexfilter(WSplit *node)
721 {
722     WSplitRegion *regnode=OBJ_CAST(node, WSplitRegion);
723     
724     return (regnode!=NULL && regnode->reg!=NULL &&
725             OBJ_IS(regnode->reg, WMPlex));
726 }
727
728
729 static WPHolder *find_ph_result=NULL;
730 static WRegion *find_ph_param=NULL;
731
732
733 static bool find_ph(WSplit *split)
734 {
735     WSplitRegion *sr=OBJ_CAST(split, WSplitRegion);
736
737     assert(find_ph_result==NULL);
738     
739     if(sr==NULL || sr->reg==NULL)
740         return FALSE;
741     
742     find_ph_result=region_get_rescue_pholder_for(sr->reg, find_ph_param);
743
744     return (find_ph_result!=NULL);
745 }
746
747
748 WPHolder *tiling_get_rescue_pholder_for(WTiling *ws, WRegion *mgd)
749 {
750     WSplit *node=(WSplit*)get_node_check(ws, mgd);
751     WPHolder *ph;
752     
753     find_ph_result=NULL;
754     find_ph_param=mgd;
755     
756     if(node==NULL){
757         if(ws->split_tree!=NULL){
758             split_current_todir(ws->split_tree, PRIMN_ANY, PRIMN_ANY, 
759                                 find_ph);
760         }
761     }else{
762         while(node!=NULL){
763             split_nextto(node, PRIMN_ANY, PRIMN_ANY, find_ph);
764             if(find_ph_result!=NULL)
765                 break;
766             node=(WSplit*)node->parent;
767         }
768     }
769     
770     ph=find_ph_result;
771     find_ph_result=NULL;
772     find_ph_param=NULL;
773      
774     return ph;
775 }
776
777
778 /*}}}*/
779
780
781 /*{{{ Navigation */
782
783
784 static void navi_to_primn(WRegionNavi nh, WPrimn *hprimn, WPrimn *vprimn, 
785                           WPrimn choice)
786 {
787     /* choice should be PRIMN_ANY or PRIMN_NONE */
788     
789     switch(nh){
790     case REGION_NAVI_BEG:
791         *vprimn=PRIMN_TL;
792         *hprimn=PRIMN_TL;
793         break;
794         
795     case REGION_NAVI_END:
796         *vprimn=PRIMN_BR;
797         *hprimn=PRIMN_BR;
798         break;
799         
800     case REGION_NAVI_LEFT:
801         *hprimn=PRIMN_TL;
802         *vprimn=choice;
803         break;
804         
805     case REGION_NAVI_RIGHT:
806         *hprimn=PRIMN_BR;
807         *vprimn=choice;
808         break;
809         
810     case REGION_NAVI_TOP:
811         *hprimn=choice;
812         *vprimn=PRIMN_TL;
813         break;
814         
815     case REGION_NAVI_BOTTOM:
816         *hprimn=choice;
817         *vprimn=PRIMN_BR;
818         break;
819         
820     default:
821     case REGION_NAVI_ANY:
822         *hprimn=PRIMN_ANY;
823         *vprimn=PRIMN_ANY;
824         break;
825     }
826 }
827
828
829 static WRegion *node_reg(WSplit *node)
830 {
831     WSplitRegion *rnode=OBJ_CAST(node, WSplitRegion);
832     return (rnode!=NULL ? rnode->reg : NULL);
833 }
834
835     
836 WRegion *tiling_do_navi_next(WTiling *ws, WRegion *reg, 
837                              WRegionNavi nh, bool nowrap,
838                              bool any)
839 {
840     WSplitFilter *filter=(any ? NULL : nostdispfilter);
841     WPrimn hprimn, vprimn;
842     WRegion *nxt=NULL;
843
844     navi_to_primn(nh, &hprimn, &vprimn, PRIMN_NONE);
845     
846     if(reg==NULL)
847         reg=tiling_current(ws);
848     
849     if(reg!=NULL){
850         WSplitRegion *node=get_node_check(ws, reg);
851         if(node!=NULL){
852             nxt=node_reg(split_nextto((WSplit*)node, hprimn, vprimn, 
853                                       filter));
854         }
855     }
856     
857     if(nxt==NULL && !nowrap){
858         nxt=node_reg(split_current_todir(ws->split_tree, 
859                                          primn_none2any(primn_invert(hprimn)),
860                                          primn_none2any(primn_invert(vprimn)),
861                                          filter));
862     }
863     
864     return nxt;
865 }
866
867
868 WRegion *tiling_do_navi_first(WTiling *ws, WRegionNavi nh, bool any)
869 {
870     WSplitFilter *filter=(any ? NULL : nostdispfilter);
871     WPrimn hprimn, vprimn;
872     
873     navi_to_primn(nh, &hprimn, &vprimn, PRIMN_ANY);
874
875     return node_reg(split_current_todir(ws->split_tree, 
876                                         hprimn, vprimn, filter));
877 }
878
879
880 WRegion *tiling_navi_next(WTiling *ws, WRegion *reg, 
881                           WRegionNavi nh, WRegionNaviData *data)
882 {
883     WRegion *nxt=tiling_do_navi_next(ws, reg, nh, TRUE, FALSE);
884     
885     return region_navi_cont(&ws->reg, nxt, data);
886 }
887
888
889 WRegion *tiling_navi_first(WTiling *ws, WRegionNavi nh,
890                            WRegionNaviData *data)
891 {
892     WRegion *reg=tiling_do_navi_first(ws, nh, FALSE);
893     
894     return region_navi_cont(&ws->reg, reg, data);
895 }
896
897
898 /*}}}*/
899
900
901 /*{{{ Split/unsplit */
902
903
904 static bool get_split_dir_primn(const char *str, int *dir, int *primn)
905 {
906     WPrimn hprimn, vprimn;
907     WRegionNavi nh;
908
909     if(!ioncore_string_to_navi(str, &nh))
910         return FALSE;
911     
912     navi_to_primn(nh, &hprimn, &vprimn, PRIMN_NONE);
913     
914     if(hprimn==PRIMN_NONE){
915         *dir=SPLIT_VERTICAL;
916         *primn=vprimn;
917     }else if(vprimn==PRIMN_NONE){
918         *dir=SPLIT_HORIZONTAL;
919         *primn=hprimn;
920     }else{
921         warn(TR("Invalid direction"));
922         return FALSE;
923     }
924     
925     return TRUE;
926 }
927
928
929 static bool get_split_dir_primn_float(const char *str, int *dir, int *primn,
930                                       bool *floating)
931 {
932     if(strncmp(str, "floating:", 9)==0){
933         *floating=TRUE;
934         return get_split_dir_primn(str+9, dir, primn);
935     }else{
936         *floating=FALSE;
937         return get_split_dir_primn(str, dir, primn);
938     }
939 }
940
941
942 #define SPLIT_MINS 16 /* totally arbitrary */
943
944
945 static WFrame *tiling_do_split(WTiling *ws, WSplit *node, 
946                               const char *dirstr, int minw, int minh)
947 {
948     int dir, primn, mins;
949     bool floating=FALSE;
950     WFrame *newframe;
951     WSplitRegion *nnode;
952     
953     if(node==NULL || ws->split_tree==NULL){
954         warn(TR("Invalid node."));
955         return NULL;
956     }
957     
958     if(!get_split_dir_primn_float(dirstr, &dir, &primn, &floating))
959         return NULL;
960     
961     mins=(dir==SPLIT_VERTICAL ? minh : minw);
962
963     if(!floating){
964         nnode=splittree_split(node, dir, primn, mins, 
965                               ws->create_frame_fn, 
966                               REGION_PARENT(ws));
967     }else{
968         nnode=splittree_split_floating(node, dir, primn, mins,
969                                        ws->create_frame_fn, ws);
970     }
971     
972     if(nnode==NULL){
973         warn(TR("Unable to split."));
974         return NULL;
975     }
976
977     /* We must restack here to ensure the split tree is stacked in the
978      * expected order.
979      */
980     if(ws->split_tree!=NULL)
981         split_restack(ws->split_tree, ws->dummywin, Above);
982
983     newframe=OBJ_CAST(nnode->reg, WFrame);
984     assert(newframe!=NULL);
985
986     if(!tiling_managed_add(ws, nnode->reg)){
987         nnode->reg=NULL;
988         destroy_obj((Obj*)nnode);
989         destroy_obj((Obj*)newframe);
990         return NULL;
991     }
992
993     /* Restack */
994     if(ws->split_tree!=NULL)
995         split_restack(ws->split_tree, ws->dummywin, Above);
996     
997     return newframe;
998 }
999
1000
1001 /*EXTL_DOC
1002  * Create a new frame on \var{ws} \codestr{above}, \codestr{below}
1003  * \codestr{left} of, or \codestr{right} of \var{node} as indicated
1004  *  by \var{dirstr}. If \var{dirstr} is  prefixed with 
1005  * \codestr{floating:} a floating split is created.
1006  */
1007 EXTL_EXPORT_MEMBER
1008 WFrame *tiling_split(WTiling *ws, WSplit *node, const char *dirstr)
1009 {
1010     if(!check_node(ws, node))
1011         return NULL;
1012     
1013     return tiling_do_split(ws, node, dirstr,
1014                           SPLIT_MINS, SPLIT_MINS);
1015 }
1016
1017
1018 /*EXTL_DOC
1019  * Same as \fnref{WTiling.split} at the root of the split tree.
1020  */
1021 EXTL_EXPORT_MEMBER
1022 WFrame *tiling_split_top(WTiling *ws, const char *dirstr)
1023 {
1024     return tiling_do_split(ws, ws->split_tree, dirstr, 
1025                           SPLIT_MINS, SPLIT_MINS);
1026 }
1027
1028
1029 /*EXTL_DOC
1030  * Split \var{frame} creating a new frame to direction \var{dirstr}
1031  * (one of \codestr{left}, \codestr{right}, \codestr{top} or 
1032  * \codestr{bottom}) of \var{frame}.
1033  * If \var{attach_current} is set, the region currently displayed in
1034  * \var{frame}, if any, is moved to thenew frame.
1035  * If \var{dirstr} is prefixed with \codestr{floating:}, a floating
1036  * split is created.
1037  */
1038 EXTL_EXPORT_MEMBER
1039 WFrame *tiling_split_at(WTiling *ws, WFrame *frame, const char *dirstr, 
1040                        bool attach_current)
1041 {
1042     WRegion *curr;
1043     WSplitRegion *node;
1044     WFrame *newframe;
1045     
1046     if(frame==NULL)
1047         return NULL;
1048     
1049     node=get_node_check(ws, (WRegion*)frame);
1050
1051     newframe=tiling_do_split(ws, (WSplit*)node, dirstr, 
1052                             region_min_w((WRegion*)frame),
1053                             region_min_h((WRegion*)frame));
1054
1055     if(newframe==NULL)
1056         return NULL;
1057
1058     curr=mplex_mx_current(&(frame->mplex));
1059     
1060     if(attach_current && curr!=NULL)
1061         mplex_attach_simple(&(newframe->mplex), curr, MPLEX_ATTACH_SWITCHTO);
1062     
1063     if(region_may_control_focus((WRegion*)frame))
1064         region_goto((WRegion*)newframe);
1065
1066     return newframe;
1067 }
1068
1069
1070 void do_unsplit(WRegion *reg)
1071 {
1072     WTiling *ws=REGION_MANAGER_CHK(reg, WTiling);
1073     WPHolder *ph;
1074     bool res;
1075     
1076     if(ws==NULL)
1077         return;
1078     
1079     ph=region_get_rescue_pholder_for((WRegion*)ws, reg);
1080     
1081     if(ph==NULL){
1082         res=!region_rescue_needed(reg);
1083     }else{
1084         res=region_rescue(reg, ph);
1085         destroy_obj((Obj*)ph);
1086     }
1087     
1088     if(!res){
1089         warn(TR("Unable to unsplit: Could not move client windows "
1090                 "elsewhere within the tiling."));
1091         return;
1092     }
1093     
1094     destroy_obj((Obj*)reg);
1095 }
1096
1097
1098 /*EXTL_DOC
1099  * Try to relocate regions managed by \var{reg} to another frame
1100  * and, if possible, destroy it.
1101  */
1102 EXTL_EXPORT_MEMBER
1103 void tiling_unsplit_at(WTiling *ws, WRegion *reg)
1104 {
1105     if(reg==NULL || REGION_MANAGER(reg)!=(WRegion*)ws)
1106         return;
1107         
1108     mainloop_defer_action((Obj*)reg, (WDeferredAction*)do_unsplit);
1109 }
1110
1111
1112 /*}}}*/
1113
1114
1115 /*{{{ Navigation etc. exports */
1116
1117
1118 WRegion *tiling_current(WTiling *ws)
1119 {
1120     WSplitRegion *node=NULL;
1121     if(ws->split_tree!=NULL){
1122         node=(WSplitRegion*)split_current_todir(ws->split_tree, 
1123                                                 PRIMN_ANY, PRIMN_ANY, NULL);
1124     }
1125     return (node ? node->reg : NULL);
1126 }
1127
1128
1129 /*EXTL_DOC
1130  * Iterate over managed regions of \var{ws} until \var{iterfn} returns
1131  * \code{false}.
1132  * The function itself returns \code{true} if it reaches the end of list
1133  * without this happening.
1134  */
1135 EXTL_SAFE
1136 EXTL_EXPORT_MEMBER
1137 bool tiling_managed_i(WTiling *ws, ExtlFn iterfn)
1138 {
1139     PtrListIterTmp tmp;
1140     
1141     ptrlist_iter_init(&tmp, ws->managed_list);
1142     
1143     return extl_iter_objlist_(iterfn, (ObjIterator*)ptrlist_iter, &tmp);
1144 }
1145
1146
1147 /*EXTL_DOC
1148  * Returns the root of the split tree.
1149  */
1150 EXTL_SAFE
1151 EXTL_EXPORT_MEMBER
1152 WSplit *tiling_split_tree(WTiling *ws)
1153 {
1154     return ws->split_tree;
1155 }
1156
1157
1158 /*EXTL_DOC
1159  * Return the most previously active region next to \var{reg} in
1160  * direction \var{dirstr} (\codestr{left}, \codestr{right}, \codestr{up},
1161  * or \codestr{down}). The region \var{reg}
1162  * must be managed by \var{ws}. If \var{any} is not set, the status display
1163  * is not considered.
1164  */
1165 EXTL_SAFE
1166 EXTL_EXPORT_MEMBER
1167 WRegion *tiling_nextto(WTiling *ws, WRegion *reg, const char *dirstr,
1168                        bool any)
1169 {
1170     WRegionNavi nh;
1171     
1172     if(!ioncore_string_to_navi(dirstr, &nh))
1173         return NULL;
1174     
1175     return tiling_do_navi_next(ws, reg, nh, FALSE, any);
1176 }
1177
1178
1179 /*EXTL_DOC
1180  * Return the most previously active region on \var{ws} with no
1181  * other regions next to it in  direction \var{dirstr} 
1182  * (\codestr{left}, \codestr{right}, \codestr{up}, or \codestr{down}). 
1183  * If \var{any} is not set, the status display is not considered.
1184  */
1185 EXTL_SAFE
1186 EXTL_EXPORT_MEMBER
1187 WRegion *tiling_farthest(WTiling *ws, const char *dirstr, bool any)
1188 {
1189     WRegionNavi nh;
1190     
1191     if(!ioncore_string_to_navi(dirstr, &nh))
1192         return NULL;
1193     
1194     return tiling_do_navi_first(ws, nh, any);
1195 }
1196
1197
1198 /*EXTL_DOC
1199  * For region \var{reg} managed by \var{ws} return the \type{WSplit}
1200  * a leaf of which \var{reg} is.
1201  */
1202 EXTL_SAFE
1203 EXTL_EXPORT_MEMBER
1204 WSplitRegion *tiling_node_of(WTiling *ws, WRegion *reg)
1205 {
1206     if(reg==NULL){
1207         warn(TR("Nil parameter."));
1208         return NULL;
1209     }
1210     
1211     if(REGION_MANAGER(reg)!=(WRegion*)ws){
1212         warn(TR("Manager doesn't match."));
1213         return NULL;
1214     }
1215     
1216     return splittree_node_of(reg);
1217 }
1218
1219
1220 /*}}}*/
1221
1222
1223 /*{{{ Flip and transpose */
1224
1225
1226 static WSplitSplit *get_at_split(WTiling *ws, WRegion *reg)
1227 {
1228     WSplit *node;
1229     WSplitSplit *split;
1230     
1231     if(reg==NULL){
1232         split=OBJ_CAST(ws->split_tree, WSplitSplit);
1233         if(split==NULL)
1234             return NULL;
1235         else if(split->br==(WSplit*)ws->stdispnode)
1236             return OBJ_CAST(split->tl, WSplitSplit);
1237         else if(split->tl==(WSplit*)ws->stdispnode)
1238             return OBJ_CAST(split->br, WSplitSplit);
1239         else
1240             return split;
1241     }
1242     
1243     node=(WSplit*)get_node_check(ws, reg);
1244     
1245     if(node==NULL)
1246         return NULL;
1247     
1248     if(node==(WSplit*)ws->stdispnode){
1249         warn(TR("The status display is not a valid parameter for "
1250                 "this routine."));
1251         return NULL;
1252     }
1253         
1254     split=OBJ_CAST(node->parent, WSplitSplit);
1255     
1256     if(split!=NULL && (split->tl==(WSplit*)ws->stdispnode ||
1257                        split->br==(WSplit*)ws->stdispnode)){
1258         split=OBJ_CAST(((WSplit*)split)->parent, WSplitSplit);
1259     }
1260        
1261     return split;
1262 }
1263
1264
1265 /*EXTL_DOC
1266  * Flip \var{ws} at \var{reg} or root if nil.
1267  */
1268 EXTL_EXPORT_MEMBER
1269 bool iowns_flip_at(WTiling *ws, WRegion *reg)
1270 {
1271     WSplitSplit *split=get_at_split(ws, reg);
1272     
1273     if(split==NULL){
1274         return FALSE;
1275     }else{
1276         splitsplit_flip(split);
1277         return TRUE;
1278     }
1279 }
1280
1281
1282 /*EXTL_DOC
1283  * Transpose \var{ws} at \var{reg} or root if nil.
1284  */
1285 EXTL_EXPORT_MEMBER
1286 bool iowns_transpose_at(WTiling *ws, WRegion *reg)
1287 {
1288     WSplitSplit *split=get_at_split(ws, reg);
1289     
1290     if(split==NULL){
1291         return FALSE;
1292     }else{
1293         split_transpose((WSplit*)split);
1294         return TRUE;
1295     }
1296 }
1297
1298
1299 /*}}}*/
1300
1301
1302 /*{{{ Floating toggle */
1303
1304
1305 static void replace(WSplitSplit *split, WSplitSplit *nsplit)
1306 {
1307     WSplitInner *psplit=split->isplit.split.parent;
1308
1309     nsplit->tl=split->tl;
1310     split->tl=NULL;
1311     nsplit->tl->parent=(WSplitInner*)nsplit;
1312     
1313     nsplit->br=split->br;
1314     split->br=NULL;
1315     nsplit->br->parent=(WSplitInner*)nsplit;
1316     
1317     if(psplit!=NULL){
1318         splitinner_replace((WSplitInner*)psplit, (WSplit*)split, 
1319                            (WSplit*)nsplit);
1320     }else{
1321         splittree_changeroot((WSplit*)split, (WSplit*)nsplit);
1322     }
1323 }
1324
1325
1326 WSplitSplit *tiling_set_floating(WTiling *ws, WSplitSplit *split, int sp)
1327 {
1328     bool set=OBJ_IS(split, WSplitFloat);
1329     bool nset=libtu_do_setparam(sp, set);
1330     const WRectangle *g=&((WSplit*)split)->geom;
1331     WSplitSplit *ns;
1332     
1333     if(!XOR(nset, set))
1334         return split;
1335     
1336     if(nset){
1337         ns=(WSplitSplit*)create_splitfloat(g, ws, split->dir);
1338     }else{
1339         if(OBJ_IS(split->tl, WSplitST) || OBJ_IS(split->br, WSplitST)){
1340             warn(TR("Refusing to float split directly containing the "
1341                     "status display."));
1342             return NULL;
1343         }
1344         ns=create_splitsplit(g, split->dir);
1345     }
1346
1347     if(ns!=NULL){
1348         replace(split, ns);
1349         split_resize((WSplit*)ns, g, PRIMN_ANY, PRIMN_ANY);
1350         mainloop_defer_destroy((Obj*)split);
1351     }
1352     
1353     return ns;
1354 }
1355
1356
1357 /*EXTL_DOC
1358  * Toggle floating of a split's sides at \var{split} as indicated by the 
1359  * parameter \var{how} (\codestr{set}, \codestr{unset}, or \codestr{toggle}).
1360  * A split of the appropriate is returned, if there was a change.
1361  */
1362 EXTL_EXPORT_AS(WTiling, set_floating)
1363 WSplitSplit *tiling_set_floating_extl(WTiling *ws, WSplitSplit *split, 
1364                                      const char *how)
1365 {
1366     if(!check_node(ws, (WSplit*)split))
1367         return NULL;
1368     return tiling_set_floating(ws, split, libtu_string_to_setparam(how));
1369 }
1370
1371
1372 /*EXTL_DOC
1373  * Toggle floating of the sides of a split containin \var{reg} as indicated 
1374  * by the parameters \var{how} (\codestr{set}, \codestr{unset}, or 
1375  * \codestr{toggle}) and \var{dirstr} (\codestr{left}, \codestr{right}, 
1376  * \codestr{up}, or \codestr{down}). The new status is returned 
1377  * (and \code{false} also on error).
1378  */
1379 EXTL_EXPORT_AS(WTiling, set_floating_at)
1380 bool tiling_set_floating_at_extl(WTiling *ws, WRegion *reg, const char *how,
1381                                 const char *dirstr)
1382 {
1383     WPrimn hprimn=PRIMN_ANY, vprimn=PRIMN_ANY;
1384     WSplitSplit *split, *nsplit;
1385     WSplit *node;
1386     
1387     node=(WSplit*)get_node_check(ws, reg);
1388     if(node==NULL)
1389         return FALSE;
1390     
1391     
1392     if(dirstr!=NULL){
1393         WRegionNavi nh;
1394         
1395         if(!ioncore_string_to_navi(dirstr, &nh))
1396             return FALSE;
1397     
1398         navi_to_primn(nh, &hprimn, &vprimn, PRIMN_NONE);
1399     }
1400
1401     while(TRUE){
1402         split=OBJ_CAST(node->parent, WSplitSplit);
1403         if(split==NULL){
1404             warn(TR("No suitable split here."));
1405             return FALSE;
1406         }
1407
1408         if(!OBJ_IS(split->tl, WSplitST) && !OBJ_IS(split->br, WSplitST)){
1409             WPrimn tmp=(split->dir==SPLIT_VERTICAL ? vprimn : hprimn);
1410             if(tmp==PRIMN_ANY 
1411                || (node==split->tl && tmp==PRIMN_BR)
1412                || (node==split->br && tmp==PRIMN_TL)){
1413                 break;
1414             }
1415         }
1416         
1417         node=(WSplit*)split;
1418     }
1419     
1420     nsplit=tiling_set_floating(ws, split, libtu_string_to_setparam(how));
1421     
1422     return OBJ_IS((Obj*)(nsplit==NULL ? split : nsplit), WSplitFloat);
1423 }
1424
1425
1426 /*}}}*/
1427
1428
1429 /*{{{ Save */
1430
1431
1432 ExtlTab tiling_get_configuration(WTiling *ws)
1433 {
1434     ExtlTab tab, split_tree=extl_table_none();
1435     
1436     tab=region_get_base_configuration((WRegion*)ws);
1437     
1438     if(ws->split_tree!=NULL){
1439         if(!split_get_config(ws->split_tree, &split_tree))
1440             warn(TR("Could not get split tree."));
1441     }
1442     
1443     extl_table_sets_t(tab, "split_tree", split_tree);
1444     extl_unref_table(split_tree);
1445     
1446     return tab;
1447 }
1448
1449
1450 /*}}}*/
1451
1452
1453 /*{{{ Load */
1454
1455
1456 WSplit *load_splitst(WTiling *ws, const WRectangle *geom, ExtlTab tab)
1457 {
1458     WSplitST *st;
1459
1460     if(ws->stdispnode!=NULL){
1461         warn(TR("Workspace already has a status display node."));
1462         return NULL;
1463     }
1464
1465     st=create_splitst(geom, NULL);
1466     ws->stdispnode=st;
1467     return (WSplit*)st;
1468 }
1469
1470
1471 static bool do_attach(WTiling *ws, WRegion *reg, void *p)
1472 {
1473     WSplitRegion *node=create_splitregion(&REGION_GEOM(reg), reg);
1474     
1475     if(node==NULL)
1476         return FALSE;
1477             
1478     if(!tiling_managed_add(ws, reg)){
1479         node->reg=NULL;
1480         destroy_obj((Obj*)node);
1481         return FALSE;
1482     }
1483     
1484     *(WSplitRegion**)p=node;
1485     
1486     return TRUE;
1487 }
1488
1489
1490 WSplit *load_splitregion(WTiling *ws, const WRectangle *geom, ExtlTab tab)
1491 {
1492     WWindow *par=REGION_PARENT(ws);
1493     WRegionAttachData data;
1494     WSplit *node=NULL;
1495     WFitParams fp;
1496     ExtlTab rt;
1497     
1498     if(!extl_table_gets_t(tab, "regparams", &rt)){
1499         warn(TR("Missing region parameters."));
1500         return NULL;
1501     }
1502     
1503     data.type=REGION_ATTACH_LOAD;
1504     data.u.tab=rt;
1505     
1506     assert(par!=NULL);
1507     fp.g=*geom;
1508     fp.mode=REGION_FIT_EXACT;
1509     
1510     region_attach_helper((WRegion*)ws, par, &fp, 
1511                          (WRegionDoAttachFn*)do_attach, &node, &data);
1512
1513     extl_unref_table(rt);
1514     
1515     return node;
1516 }
1517
1518
1519 #define MINS 1
1520
1521 WSplit *load_splitsplit(WTiling *ws, const WRectangle *geom, ExtlTab tab)
1522 {
1523     WSplit *tl=NULL, *br=NULL;
1524     WSplitSplit *split;
1525     char *dir_str;
1526     int dir, brs, tls;
1527     ExtlTab subtab;
1528     WRectangle geom2;
1529     int set=0;
1530
1531     set+=(extl_table_gets_i(tab, "tls", &tls)==TRUE);
1532     set+=(extl_table_gets_i(tab, "brs", &brs)==TRUE);
1533     set+=(extl_table_gets_s(tab, "dir", &dir_str)==TRUE);
1534     
1535     if(set!=3)
1536         return NULL;
1537     
1538     if(strcmp(dir_str, "vertical")==0){
1539         dir=SPLIT_VERTICAL;
1540     }else if(strcmp(dir_str, "horizontal")==0){
1541         dir=SPLIT_HORIZONTAL;
1542     }else{
1543         warn(TR("Invalid direction."));
1544         free(dir_str);
1545         return NULL;
1546     }
1547     free(dir_str);
1548
1549     split=create_splitsplit(geom, dir);
1550     if(split==NULL)
1551         return NULL;
1552
1553     tls=maxof(tls, MINS);
1554     brs=maxof(brs, MINS);
1555         
1556     geom2=*geom;
1557     if(dir==SPLIT_HORIZONTAL){
1558         tls=maxof(0, geom->w)*tls/(tls+brs);
1559         geom2.w=tls;
1560     }else{
1561         tls=maxof(0, geom->h)*tls/(tls+brs);
1562         geom2.h=tls;
1563     }
1564     
1565     if(extl_table_gets_t(tab, "tl", &subtab)){
1566         tl=tiling_load_node(ws, &geom2, subtab);
1567         extl_unref_table(subtab);
1568     }
1569
1570     geom2=*geom;
1571     if(dir==SPLIT_HORIZONTAL){
1572         geom2.w-=tls;
1573         geom2.x+=tls;
1574     }else{
1575         geom2.h-=tls;
1576         geom2.y+=tls;
1577     }
1578             
1579     if(extl_table_gets_t(tab, "br", &subtab)){
1580         br=tiling_load_node(ws, &geom2, subtab);
1581         extl_unref_table(subtab);
1582     }
1583     
1584     if(tl==NULL || br==NULL){
1585         /* PRIMN_TL/BR instead of ANY because of stdisp. */
1586         destroy_obj((Obj*)split);
1587         if(tl!=NULL){
1588             split_do_resize(tl, geom, PRIMN_BR, PRIMN_BR, FALSE);
1589             return tl;
1590         }
1591         if(br!=NULL){
1592             split_do_resize(br, geom, PRIMN_TL, PRIMN_TL, FALSE);
1593             return br;
1594         }
1595         return NULL;
1596     }
1597     
1598     tl->parent=(WSplitInner*)split;
1599     br->parent=(WSplitInner*)split;
1600
1601     /*split->tmpsize=tls;*/
1602     split->tl=tl;
1603     split->br=br;
1604     
1605     return (WSplit*)split;
1606 }
1607
1608
1609 WSplit *tiling_load_node_default(WTiling *ws, const WRectangle *geom, 
1610                                 ExtlTab tab)
1611 {
1612     char *typestr=NULL;
1613     WSplit *node=NULL;
1614
1615     extl_table_gets_s(tab, "type", &typestr);
1616     
1617     if(typestr==NULL){
1618         warn(TR("No split type given."));
1619         return NULL;
1620     }
1621     
1622     if(strcmp(typestr, "WSplitRegion")==0)
1623         node=load_splitregion(ws, geom, tab);
1624     else if(strcmp(typestr, "WSplitSplit")==0)
1625         node=load_splitsplit(ws, geom, tab);
1626     else if(strcmp(typestr, "WSplitFloat")==0)
1627         node=load_splitfloat(ws, geom, tab);
1628     else if(strcmp(typestr, "WSplitST")==0)
1629         node=NULL;/*load_splitst(ws, geom, tab);*/
1630     else
1631         warn(TR("Unknown split type."));
1632     
1633     free(typestr);
1634     
1635     return node;
1636 }
1637
1638
1639 WSplit *tiling_load_node(WTiling *ws, const WRectangle *geom, ExtlTab tab)
1640 {
1641     WSplit *ret=NULL;
1642     CALL_DYN_RET(ret, WSplit*, tiling_load_node, ws, (ws, geom, tab));
1643     return ret;
1644 }
1645
1646
1647
1648 WRegion *tiling_load(WWindow *par, const WFitParams *fp, ExtlTab tab)
1649 {
1650     WTiling *ws;
1651     ExtlTab treetab;
1652     bool ci=TRUE;
1653
1654     if(extl_table_gets_t(tab, "split_tree", &treetab))
1655         ci=FALSE;
1656     
1657     ws=create_tiling(par, fp, NULL, ci);
1658     
1659     if(ws==NULL){
1660         if(!ci)
1661             extl_unref_table(treetab);
1662         return NULL;
1663     }
1664
1665     if(!ci){
1666         ws->split_tree=tiling_load_node(ws, &REGION_GEOM(ws), treetab);
1667         extl_unref_table(treetab);
1668     }
1669     
1670     if(ws->split_tree==NULL){
1671         warn(TR("The workspace is empty."));
1672         destroy_obj((Obj*)ws);
1673         return NULL;
1674     }
1675     
1676     ws->split_tree->ws_if_root=ws;
1677     split_restack(ws->split_tree, ws->dummywin, Above);
1678     
1679     return (WRegion*)ws;
1680 }
1681
1682
1683 /*}}}*/
1684
1685
1686 /*{{{ Dynamic function table and class implementation */
1687
1688
1689 static DynFunTab tiling_dynfuntab[]={
1690     {region_map, 
1691      tiling_map},
1692     
1693     {region_unmap, 
1694      tiling_unmap},
1695     
1696     {region_do_set_focus, 
1697      tiling_do_set_focus},
1698     
1699     {(DynFun*)region_fitrep,
1700      (DynFun*)tiling_fitrep},
1701     
1702     {region_managed_rqgeom, 
1703      tiling_managed_rqgeom},
1704     
1705     {region_managed_remove, 
1706      tiling_managed_remove},
1707     
1708     {(DynFun*)region_managed_prepare_focus,
1709      (DynFun*)tiling_managed_prepare_focus},
1710     
1711     {(DynFun*)region_prepare_manage, 
1712      (DynFun*)tiling_prepare_manage},
1713     
1714     {(DynFun*)region_rescue_clientwins,
1715      (DynFun*)tiling_rescue_clientwins},
1716
1717     {(DynFun*)region_get_rescue_pholder_for,
1718      (DynFun*)tiling_get_rescue_pholder_for},
1719     
1720     {(DynFun*)region_get_configuration,
1721      (DynFun*)tiling_get_configuration},
1722
1723     {(DynFun*)region_managed_disposeroot,
1724      (DynFun*)tiling_managed_disposeroot},
1725
1726     {(DynFun*)region_current,
1727      (DynFun*)tiling_current},
1728
1729     {(DynFun*)tiling_managed_add,
1730      (DynFun*)tiling_managed_add_default},
1731     
1732     {region_manage_stdisp,
1733      tiling_manage_stdisp},
1734
1735     {region_unmanage_stdisp,
1736      tiling_unmanage_stdisp},
1737     
1738     {(DynFun*)tiling_load_node,
1739      (DynFun*)tiling_load_node_default},
1740             
1741     {region_restack,
1742      tiling_restack},
1743
1744     {region_stacking,
1745      tiling_stacking},
1746     
1747     {(DynFun*)region_navi_first,
1748      (DynFun*)tiling_navi_first},
1749     
1750     {(DynFun*)region_navi_next,
1751      (DynFun*)tiling_navi_next},
1752     
1753     {(DynFun*)region_xwindow,
1754      (DynFun*)tiling_xwindow},
1755     
1756     END_DYNFUNTAB
1757 };
1758
1759
1760 EXTL_EXPORT
1761 IMPLCLASS(WTiling, WRegion, tiling_deinit, tiling_dynfuntab);
1762
1763     
1764 /*}}}*/
1765