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