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