]> git.decadent.org.uk Git - ion3.git/blob - mod_tiling/split.c
Imported Upstream version 20090110
[ion3.git] / mod_tiling / split.c
1 /*
2  * ion/mod_tiling/split.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2009. 
5  *
6  * See the included file LICENSE for details.
7  */
8
9 #include <limits.h>
10 #include <string.h>
11 #include <X11/Xmd.h>
12
13 #include <libtu/minmax.h>
14 #include <libtu/rb.h>
15 #include <libtu/objp.h>
16 #include <ioncore/common.h>
17 #include <ioncore/focus.h>
18 #include <ioncore/global.h>
19 #include <ioncore/window.h>
20 #include <ioncore/resize.h>
21 #include <ioncore/attach.h>
22 #include <ioncore/manage.h>
23 #include <ioncore/extlconv.h>
24 #include <ioncore/rectangle.h>
25 #include <ioncore/saveload.h>
26 #include <ioncore/names.h>
27 #include "tiling.h"
28 #include "split.h"
29 #include "split-stdisp.h"
30
31
32 static Rb_node split_of_map=NULL;
33
34
35 /*{{{ Geometry helper functions */
36
37
38 int split_size(WSplit *split, int dir)
39 {
40     return (dir==SPLIT_HORIZONTAL ? split->geom.w : split->geom.h);
41 }
42
43 int split_other_size(WSplit *split, int dir)
44 {
45     return (dir==SPLIT_VERTICAL ? split->geom.w : split->geom.h);
46 }
47
48 int split_pos(WSplit *split, int dir)
49 {
50     return (dir==SPLIT_HORIZONTAL ? split->geom.x : split->geom.y);
51 }
52
53 int split_other_pos(WSplit *split, int dir)
54 {
55     return (dir==SPLIT_VERTICAL ? split->geom.x : split->geom.y);
56 }
57
58
59 static int reg_calcresize(WRegion *reg, int dir, int nsize)
60 {
61     int tmp;
62     
63     if(dir==SPLIT_HORIZONTAL)
64         tmp=region_min_w(reg);
65     else
66         tmp=region_min_h(reg);
67     
68     return (nsize<tmp ? tmp : nsize);
69 }
70
71
72 /* No, these are not even supposed to be proper/consistent 
73  * Z \cup {\infty, -\infty} calculation rules. 
74  */
75
76 static int infadd(int x, int y)
77 {
78     if(x==INT_MAX || y==INT_MAX)
79         return INT_MAX;
80     else
81         return x+y;
82 }
83
84
85 static int infsub(int x, int y)
86 {
87     if(x==INT_MAX)
88         return INT_MAX;
89     else if(y==INT_MAX)
90         return 0;
91     else
92         return x-y;
93 }
94
95
96 /* Negative "unused space" means no SPLIT_UNUSED under a node, while
97  * zero unused space means there's a zero-sized SPLIT_UNUSED under the
98  * node.
99  */
100 static int unusedadd(int x, int y)
101 {
102     if(x<0 && y<0)
103         return -1;
104     return maxof(x, 0)+maxof(y, 0);
105 }
106
107
108 static void bound(int *what, int min, int max)
109 {
110     if(*what<min)
111         *what=min;
112     else if(*what>max)
113         *what=max;
114 }
115
116
117 /*}}}*/
118
119
120 /*{{{ Functions to get and set a region's containing node */
121
122
123 #define node_of_reg splittree_node_of
124
125 WSplitRegion *splittree_node_of(WRegion *reg)
126 {
127     Rb_node node=NULL;
128     int found=0;
129     
130     /*assert(REGION_MANAGER_CHK(reg, WTiling)!=NULL);*/
131     
132     if(split_of_map!=NULL){
133         node=rb_find_pkey_n(split_of_map, reg, &found);
134         if(found)
135             return (WSplitRegion*)(node->v.val);
136     }
137     
138     return NULL;
139 }
140
141
142 #define set_node_of_reg splittree_set_node_of
143
144
145 bool splittree_set_node_of(WRegion *reg, WSplitRegion *split)
146 {
147     Rb_node node=NULL;
148     int found;
149     
150     /*assert(REGION_MANAGER_CHK(reg, WTiling)!=NULL);*/
151     
152     if(split_of_map==NULL){
153         if(split==NULL)
154             return TRUE;
155         split_of_map=make_rb();
156         if(split_of_map==NULL)
157             return FALSE;
158     }
159     
160     node=rb_find_pkey_n(split_of_map, reg, &found);
161     if(found)
162         rb_delete_node(node);
163     
164     return (rb_insertp(split_of_map, reg, split)!=NULL);
165 }
166
167
168 /*}}}*/
169
170
171 /*{{{ Primn */
172
173
174 WPrimn primn_invert(WPrimn primn)
175 {
176     return (primn==PRIMN_TL
177             ? PRIMN_BR
178             : (primn==PRIMN_BR
179                ? PRIMN_TL
180                : primn));
181 }
182
183
184 WPrimn primn_none2any(WPrimn primn)
185 {
186     return (primn==PRIMN_NONE ? PRIMN_ANY : primn);
187 }
188
189
190 /*}}}*/
191
192
193 /*{{{ Create */
194
195
196 bool split_init(WSplit *split, const WRectangle *geom)
197 {
198     split->parent=NULL;
199     split->ws_if_root=NULL;
200     split->geom=*geom;
201     split->min_w=0;
202     split->min_h=0;
203     split->max_w=INT_MAX;
204     split->max_h=INT_MAX;
205     split->unused_w=-1;
206     split->unused_h=-1;
207     return TRUE;
208 }
209
210 bool splitinner_init(WSplitInner *split, const WRectangle *geom)
211 {
212     return split_init(&(split->split), geom);
213 }
214
215
216 bool splitsplit_init(WSplitSplit *split, const WRectangle *geom, int dir)
217 {
218     splitinner_init(&(split->isplit), geom);
219     split->dir=dir;
220     split->tl=NULL;
221     split->br=NULL;
222     split->current=SPLIT_CURRENT_TL;
223     return TRUE;
224 }
225
226
227 bool splitregion_init(WSplitRegion *split, const WRectangle *geom, 
228                       WRegion *reg)
229 {
230     split_init(&(split->split), geom);
231     split->reg=reg;
232     if(reg!=NULL)
233         set_node_of_reg(reg, split);
234     return TRUE;
235 }
236
237
238 bool splitst_init(WSplitST *split, const WRectangle *geom, WRegion *reg)
239 {
240     splitregion_init(&(split->regnode), geom, reg);
241     split->orientation=REGION_ORIENTATION_HORIZONTAL;
242     split->corner=MPLEX_STDISP_BL;
243     return TRUE;
244 }
245
246
247 WSplitSplit *create_splitsplit(const WRectangle *geom, int dir)
248 {
249     CREATEOBJ_IMPL(WSplitSplit, splitsplit, (p, geom, dir));
250 }
251
252
253 WSplitRegion *create_splitregion(const WRectangle *geom, WRegion *reg)
254 {
255     CREATEOBJ_IMPL(WSplitRegion, splitregion, (p, geom, reg));
256 }
257
258
259 WSplitST *create_splitst(const WRectangle *geom, WRegion *reg)
260 {
261     CREATEOBJ_IMPL(WSplitST, splitst, (p, geom, reg));
262 }
263
264
265 /*}}}*/
266
267
268 /*{{{ Deinit */
269
270
271 void split_deinit(WSplit *split)
272 {
273     assert(split->parent==NULL);
274 }
275
276
277 void splitinner_deinit(WSplitInner *split)
278 {
279     split_deinit(&(split->split));
280 }
281
282
283 void splitsplit_deinit(WSplitSplit *split)
284 {
285     if(split->tl!=NULL){
286         split->tl->parent=NULL;
287         destroy_obj((Obj*)(split->tl));
288     }
289     if(split->br!=NULL){
290         split->br->parent=NULL;
291         destroy_obj((Obj*)(split->br));
292     }
293     
294     splitinner_deinit(&(split->isplit));
295 }
296     
297
298 void splitregion_deinit(WSplitRegion *split)
299 {
300     if(split->reg!=NULL){
301         set_node_of_reg(split->reg, NULL);
302         split->reg=NULL;
303     }
304     
305     split_deinit(&(split->split));
306 }
307
308
309 void splitst_deinit(WSplitST *split)
310 {
311     splitregion_deinit(&(split->regnode));
312 }
313
314
315 /*}}}*/
316
317
318 /*{{{ Size bounds management */
319
320
321 static void splitregion_update_bounds(WSplitRegion *node, bool recursive)
322 {
323     WSizeHints hints;
324     WSplit *snode=(WSplit*)node;
325     
326     assert(node->reg!=NULL);
327     
328     region_size_hints(node->reg, &hints);
329     
330     snode->min_w=maxof(1, hints.min_set ? hints.min_width : 1);
331     snode->max_w=INT_MAX;
332     snode->unused_w=-1;
333
334     snode->min_h=maxof(1, hints.min_set ? hints.min_height : 1);
335     snode->max_h=INT_MAX;
336     snode->unused_h=-1;
337 }
338
339
340 static void splitst_update_bounds(WSplitST *node, bool rec)
341 {
342     WSplit *snode=(WSplit*)node;
343
344     if(node->regnode.reg==NULL){
345         snode->min_w=CF_STDISP_MIN_SZ;
346         snode->min_h=CF_STDISP_MIN_SZ;
347         snode->max_w=CF_STDISP_MIN_SZ;
348         snode->max_h=CF_STDISP_MIN_SZ;
349     }else{
350         WSizeHints hints;
351         region_size_hints(node->regnode.reg, &hints);
352         snode->min_w=maxof(1, hints.min_set ? hints.min_width : 1);
353         snode->max_w=maxof(snode->min_w, hints.min_width);
354         snode->min_h=maxof(1, hints.min_set ? hints.min_height : 1);
355         snode->max_h=maxof(snode->min_h, hints.min_height);
356     }
357
358     snode->unused_w=-1;
359     snode->unused_h=-1;
360     
361     if(node->orientation==REGION_ORIENTATION_HORIZONTAL){
362         snode->min_w=CF_STDISP_MIN_SZ;
363         snode->max_w=INT_MAX;
364     }else{
365         snode->min_h=CF_STDISP_MIN_SZ;
366         snode->max_h=INT_MAX;
367     }
368 }
369
370
371 static void splitsplit_update_bounds(WSplitSplit *split, bool recursive)
372 {
373     WSplit *tl, *br;
374     WSplit *node=(WSplit*)split;
375
376     assert(split->tl!=NULL && split->br!=NULL);
377     
378     tl=split->tl;
379     br=split->br;
380     
381     if(recursive){
382         split_update_bounds(tl, TRUE);
383         split_update_bounds(br, TRUE);
384     }
385     
386     if(split->dir==SPLIT_HORIZONTAL){
387         node->max_w=infadd(tl->max_w, br->max_w);
388         node->min_w=infadd(tl->min_w, br->min_w);
389         node->unused_w=unusedadd(tl->unused_w, br->unused_w);
390         node->min_h=maxof(tl->min_h, br->min_h);
391         node->max_h=maxof(minof(tl->max_h, br->max_h), node->min_h);
392         node->unused_h=minof(tl->unused_h, br->unused_h);
393     }else{
394         node->max_h=infadd(tl->max_h, br->max_h);
395         node->min_h=infadd(tl->min_h, br->min_h);
396         node->unused_h=unusedadd(tl->unused_h, br->unused_h);
397         node->min_w=maxof(tl->min_w, br->min_w);
398         node->max_w=maxof(minof(tl->max_w, br->max_w), node->min_w);
399         node->unused_w=minof(tl->unused_w, br->unused_w);
400     }
401 }
402
403
404 void split_update_bounds(WSplit *node, bool recursive)
405 {
406     CALL_DYN(split_update_bounds, node, (node, recursive));
407 }
408
409
410 void splitsplit_update_geom_from_children(WSplitSplit *node)
411 {
412     WSplit *split=(WSplit*)node;
413     
414     if(node->dir==SPLIT_VERTICAL){
415         ((WSplit*)node)->geom.h=node->tl->geom.h+node->br->geom.h;
416         ((WSplit*)node)->geom.y=node->tl->geom.y;
417     }else if(node->dir==SPLIT_HORIZONTAL){
418         ((WSplit*)node)->geom.w=node->tl->geom.w+node->br->geom.w;
419         ((WSplit*)node)->geom.x=node->tl->geom.x;
420     }
421 }
422
423
424 /*}}}*/
425
426
427 /*{{{ Status display handling helper functions. */
428
429
430 static WSplitST *saw_stdisp=NULL;
431
432
433 void splittree_begin_resize()
434 {
435     saw_stdisp=NULL;
436 }
437
438
439 void splittree_end_resize()
440 {
441     if(saw_stdisp!=NULL){
442         split_regularise_stdisp(saw_stdisp);
443         saw_stdisp=NULL;
444     }
445 }
446
447
448 static void splittree_scan_stdisp_rootward_(WSplitInner *node_)
449 {
450     WSplitSplit *node=OBJ_CAST(node_, WSplitSplit);
451     
452     if(node!=NULL){
453         if(OBJ_IS(node->tl, WSplitST)){
454             saw_stdisp=(WSplitST*)(node->tl);
455             return;
456         }else if(OBJ_IS(node->br, WSplitST)){
457             saw_stdisp=(WSplitST*)(node->br);
458             return;
459         }
460     }
461     
462     if(node_->split.parent!=NULL)
463         splittree_scan_stdisp_rootward_(node_->split.parent);
464 }
465
466
467 void splittree_scan_stdisp_rootward(WSplit *node)
468 {
469     if(node->parent!=NULL)
470         splittree_scan_stdisp_rootward_(node->parent);
471 }
472
473
474 static WSplitST *splittree_scan_stdisp(WSplit *node_, bool set_saw)
475 {
476     WSplitST *r=NULL;
477     WSplitSplit *node=OBJ_CAST(node_, WSplitSplit);
478     
479     if(node==NULL)
480         return NULL;
481     
482     r=OBJ_CAST(node->tl, WSplitST);
483     if(r==NULL)
484         r=OBJ_CAST(node->br, WSplitST);
485     
486     if(r!=NULL){
487         if(set_saw)
488             saw_stdisp=r;
489         return r;
490     }
491     
492     r=splittree_scan_stdisp(node->tl, set_saw);
493     if(r==NULL)
494         r=splittree_scan_stdisp(node->br, set_saw);
495     
496     return r;
497 }
498
499
500 static bool stdisp_immediate_child(WSplitSplit *node)
501 {
502     return (node!=NULL && (OBJ_IS(node->tl, WSplitST) ||
503                            OBJ_IS(node->br, WSplitST)));
504 }
505
506
507 static WSplit *dodge_stdisp(WSplit *node, bool keep_within)
508 {
509     WSplitST *stdisp;
510     WSplitSplit *stdispp;
511     
512     stdisp=splittree_scan_stdisp(node, TRUE);
513     
514     if(stdisp==NULL)
515         return node;
516     
517     stdispp=OBJ_CAST(((WSplit*)stdisp)->parent, WSplitSplit);
518     
519     if(stdispp==NULL)
520         return node;
521     
522     if((WSplit*)stdispp==node){
523         /* Node itself immediately contains stdisp. Due to the way
524          * try_unsink works, stdisp this will not change, so another
525          * node must be used, if we want to fully dodge stdisp.
526          */
527         return (keep_within
528                 ? node
529                 : (stdispp->tl==(WSplit*)stdisp 
530                    ? stdispp->br 
531                    : stdispp->tl));
532     }
533     
534     do{
535         if(!split_try_unsink_stdisp(stdispp, FALSE, TRUE)){
536             warn(TR("Unable to move the status display out of way."));
537             return NULL;
538         }
539     }while(stdispp->tl!=node && stdispp->br!=node);
540     
541     return node;
542 }
543
544
545 /*}}}*/
546
547
548 /*{{{ Low-level resize code; from root to leaf */
549
550
551 static void split_do_resize_default(WSplit *node, const WRectangle *ng, 
552                                     WPrimn hprimn, WPrimn vprimn, 
553                                     bool transpose)
554 {
555     node->geom=*ng;
556 }
557
558
559 static void splitregion_do_resize(WSplitRegion *node, const WRectangle *ng, 
560                                   WPrimn hprimn, WPrimn vprimn, 
561                                   bool transpose)
562 {
563     assert(node->reg!=NULL);
564     region_fit(node->reg, ng, REGION_FIT_EXACT);
565     split_update_bounds(&(node->split), FALSE);
566     node->split.geom=*ng;
567 }
568
569
570 static void splitst_do_resize(WSplitST *node, const WRectangle *ng, 
571                               WPrimn hprimn, WPrimn vprimn, 
572                               bool transpose)
573 {
574     saw_stdisp=node;
575
576     if(node->regnode.reg==NULL){
577         ((WSplit*)node)->geom=*ng;
578     }else{
579         splitregion_do_resize(&(node->regnode), ng, hprimn, vprimn, 
580                                transpose);
581     }
582 }
583
584
585 static int other_dir(int dir)
586 {
587     return (dir==SPLIT_VERTICAL ? SPLIT_HORIZONTAL : SPLIT_VERTICAL);
588 }
589
590
591 static void adjust_sizes(int *tls_, int *brs_, int nsize, int sz, 
592                          int tlmin, int brmin, int tlmax, int brmax,
593                          int primn)
594 {
595     int tls=*tls_;
596     int brs=*brs_;
597     
598     if(primn==PRIMN_TL){
599         tls=tls+nsize-sz;
600         bound(&tls, tlmin, tlmax);
601         brs=nsize-tls;
602         bound(&brs, brmin, brmax);
603         tls=nsize-brs;
604         bound(&tls, tlmin, tlmax);
605     }else if(primn==PRIMN_BR){
606         brs=brs+nsize-sz;
607         bound(&brs, brmin, brmax);
608         tls=nsize-brs;
609         bound(&tls, tlmin, tlmax);
610         brs=nsize-tls;
611         bound(&brs, brmin, brmax);
612     }else{ /* && PRIMN_ANY */
613         tls=tls*nsize/sz;
614         bound(&tls, tlmin, tlmax);
615         brs=nsize-tls;
616         bound(&brs, brmin, brmax);
617         tls=nsize-brs;
618         bound(&tls, tlmin, tlmax);
619     }
620     
621     *tls_=tls;
622     *brs_=brs;
623 }
624
625
626 static void get_minmaxunused(WSplit *node, int dir, 
627                              int *min, int *max, int *unused)
628 {
629     if(dir==SPLIT_VERTICAL){
630         *min=node->min_h;
631         *max=maxof(*min, node->max_h);
632         *unused=minof(node->unused_h, node->geom.h);
633     }else{
634         *min=node->min_w;
635         *max=maxof(*min, node->max_w);
636         *unused=minof(node->unused_w, node->geom.w);
637     }
638 }
639
640
641 void splitsplit_do_resize(WSplitSplit *node, const WRectangle *ng, 
642                           WPrimn hprimn, WPrimn vprimn, bool transpose)
643 {
644     assert(ng->w>=0 && ng->h>=0);
645     assert(node->tl!=NULL && node->br!=NULL);
646     assert(!transpose || (hprimn==PRIMN_ANY && vprimn==PRIMN_ANY));
647     
648     {
649         WSplit *tl=node->tl, *br=node->br;
650         int tls=split_size((WSplit*)tl, node->dir);
651         int brs=split_size((WSplit*)br, node->dir);
652         int sz=tls+brs;
653         /* Status display can not be transposed. */
654         int dir=((transpose && !stdisp_immediate_child(node))
655                  ? other_dir(node->dir)
656                  : node->dir);
657         int nsize=(dir==SPLIT_VERTICAL ? ng->h : ng->w);
658         int primn=(dir==SPLIT_VERTICAL ? vprimn : hprimn);
659         int tlmin, tlmax, tlunused, tlused;
660         int brmin, brmax, brunused, brused;
661         WRectangle tlg=*ng, brg=*ng;
662         
663         get_minmaxunused(tl, dir, &tlmin, &tlmax, &tlunused);
664         get_minmaxunused(br, dir, &brmin, &brmax, &brunused);
665         
666         tlused=maxof(0, tls-maxof(0, tlunused));
667         brused=maxof(0, brs-maxof(0, brunused));
668         /* tlmin,  brmin >= 1 => (tls>=tlmin, brs>=brmin => sz>0) */
669         
670         if(sz>2){
671             if(primn==PRIMN_ANY && (tlunused>=0 || brunused>=0)){
672                 if(nsize<=tlused+brused){
673                     /* Need to shrink a tangible node */
674                     adjust_sizes(&tls, &brs, nsize, sz,
675                                  tlmin, brmin, tlused, brused, primn);
676                 }else{
677                     /* Just expand or shrink unused space */
678                     adjust_sizes(&tls, &brs, nsize, sz,
679                                  tlused, brused, 
680                                  (tlunused<0 ? tlused : tlmax),
681                                  (brunused<0 ? brused : brmax), primn);
682                 }
683                 
684             }else{
685                 adjust_sizes(&tls, &brs, nsize, sz, 
686                              tlmin, brmin, tlmax, brmax, primn);
687             }
688         }
689         
690         if(tls+brs!=nsize){
691             /* Bad fit; just size proportionally. */
692             if(sz<=2){
693                 tls=nsize/2;
694                 brs=nsize-tls;
695             }else{
696                 tls=split_size(tl, node->dir)*nsize/sz;
697                 brs=nsize-tls;
698             }
699         }
700         
701         if(dir==SPLIT_VERTICAL){
702             tlg.h=tls;
703             brg.y+=tls;
704             brg.h=brs;
705         }else{
706             tlg.w=tls;
707             brg.x+=tls;
708             brg.w=brs;
709         }
710         
711         split_do_resize(tl, &tlg, hprimn, vprimn, transpose);
712         split_do_resize(br, &brg, hprimn, vprimn, transpose);
713         
714         node->dir=dir;
715         ((WSplit*)node)->geom=*ng;
716         split_update_bounds((WSplit*)node, FALSE);
717     }
718 }
719
720
721 void split_do_resize(WSplit *node, const WRectangle *ng, 
722                      WPrimn hprimn, WPrimn vprimn, bool transpose)
723 {
724     CALL_DYN(split_do_resize, node, (node, ng, hprimn, vprimn, transpose));
725 }
726
727
728 void split_resize(WSplit *node, const WRectangle *ng, 
729                   WPrimn hprimn, WPrimn vprimn)
730 {
731     split_update_bounds(node, TRUE);
732     splittree_begin_resize();
733     split_do_resize(node, ng, hprimn, vprimn, FALSE);
734     splittree_end_resize();
735 }
736
737
738 /*}}}*/
739
740
741 /*{{{ Low-level resize code; request towards root */
742
743
744 static void flexibility(WSplit *node, int dir, int *shrink, int *stretch)
745 {
746     if(dir==SPLIT_VERTICAL){
747         *shrink=maxof(0, node->geom.h-node->min_h);
748         if(OBJ_IS(node, WSplitST))
749             *stretch=maxof(0, node->max_h-node->geom.h);
750         else
751             *stretch=INT_MAX;
752     }else{
753         *shrink=maxof(0, node->geom.w-node->min_w);
754         if(OBJ_IS(node, WSplitST))
755             *stretch=maxof(0, node->max_w-node->geom.w);
756         else
757             *stretch=INT_MAX;
758     }
759 }
760
761
762 static void calc_amount(int *amount, int rs, WSplit *other, int dir)
763 {
764     int shrink, stretch;
765     
766     flexibility(other, dir, &shrink, &stretch);
767
768     if(rs>0)
769         *amount=minof(rs, shrink);
770     else if(rs<0)
771         *amount=-minof(-rs, stretch);
772     else
773         *amount=0;
774 }
775
776
777
778 static void splitsplit_do_rqsize(WSplitSplit *p, WSplit *node, 
779                                  RootwardAmount *ha, RootwardAmount *va, 
780                                  WRectangle *rg, bool tryonly)
781 {
782     WPrimn hprimn=PRIMN_ANY, vprimn=PRIMN_ANY;
783     WRectangle og, pg, ng;
784     RootwardAmount *ca;
785     WSplit *other;
786     WPrimn thisnode;
787     int amount;
788     
789     assert(!ha->any || ha->tl==0);
790     assert(!va->any || va->tl==0);
791     assert(p->tl==node || p->br==node);
792     
793     if(p->tl==node){
794         other=p->br;
795         thisnode=PRIMN_TL;
796     }else{
797         other=p->tl;
798         thisnode=PRIMN_BR;
799     }
800
801     ca=(p->dir==SPLIT_VERTICAL ? va : ha);
802
803     if(thisnode==PRIMN_TL || ca->any){
804         calc_amount(&amount, ca->br, other, p->dir);
805         ca->br-=amount;
806     }else/*if(thisnode==PRIMN_BR)*/{
807         calc_amount(&amount, ca->tl, other, p->dir);
808         ca->tl-=amount;
809     }
810     
811     if(((WSplit*)p)->parent==NULL /*|| 
812        (ha->tl==0 && ha->br==0 && va->tl==0 && va->br==0)*/){
813         if(((WSplit*)p)->ws_if_root!=NULL)
814             pg=REGION_GEOM((WTiling*)(((WSplit*)p)->ws_if_root));
815         else
816             pg=((WSplit*)p)->geom;
817     }else{
818         splitinner_do_rqsize(((WSplit*)p)->parent, (WSplit*)p, ha, va,
819                              &pg, tryonly);
820     }
821     
822     assert(pg.w>=0 && pg.h>=0);
823
824     og=pg;
825     ng=pg;
826
827     if(p->dir==SPLIT_VERTICAL){
828         ng.h=maxof(0, node->geom.h+amount);
829         og.h=maxof(0, other->geom.h-amount);
830         adjust_sizes(&(ng.h), &(og.h), pg.h, ng.h+og.h,
831                      node->min_h, other->min_h, node->max_h, other->max_h, 
832                      PRIMN_TL /* node is passed as tl param */);
833         if(thisnode==PRIMN_TL)
834             og.y=pg.y+pg.h-og.h;
835         else
836             ng.y=pg.y+pg.h-ng.h;
837         vprimn=thisnode;
838     }else{
839         ng.w=maxof(0, node->geom.w+amount);
840         og.w=maxof(0, other->geom.w-amount);
841         adjust_sizes(&(ng.w), &(og.w), pg.w, ng.w+og.w,
842                      node->min_w, other->min_w, node->max_w, other->max_w, 
843                      PRIMN_TL /* node is passed as tl param */);
844         if(thisnode==PRIMN_TL)
845             og.x=pg.x+pg.w-og.w;
846         else
847             ng.x=pg.x+pg.w-ng.w;
848         hprimn=thisnode;
849     }
850     
851     if(!tryonly){
852         /* Entä jos 'other' on stdisp? */
853         split_do_resize(other, &og, hprimn, vprimn, FALSE);
854         
855         ((WSplit*)p)->geom=pg;
856     }
857     
858     *rg=ng;
859 }
860
861
862 void splitinner_do_rqsize(WSplitInner *p, WSplit *node, 
863                           RootwardAmount *ha, RootwardAmount *va, 
864                           WRectangle *rg, bool tryonly)
865 {
866     CALL_DYN(splitinner_do_rqsize, p, (p, node, ha, va, rg, tryonly));
867 }
868
869
870 static void initra(RootwardAmount *ra, int p, int s, int op, int os, 
871                    bool any)
872 {
873     ra->any=any;
874     ra->tl=op-p;
875     ra->br=(p+s)-(op+os);
876     if(any){
877         ra->br+=ra->tl;
878         ra->tl=0;
879     }
880 }
881
882
883 void split_do_rqgeom_(WSplit *node, const WRectangle *ng, 
884                       bool hany, bool vany, WRectangle *rg, 
885                       bool tryonly)
886 {
887     RootwardAmount ha, va;
888
889     if(node->parent==NULL){
890         if(node->ws_if_root!=NULL)
891             *rg=REGION_GEOM((WTiling*)(node->ws_if_root));
892         else
893             *rg=*ng;
894     }else{
895         initra(&ha, ng->x, ng->w, node->geom.x, node->geom.w, hany);
896         initra(&va, ng->y, ng->h, node->geom.y, node->geom.h, vany);
897     
898         splitinner_do_rqsize(node->parent, node, &ha, &va, rg, tryonly);
899     }
900 }
901
902
903 /*}}}*/
904
905
906 /*{{{ Resize interface */
907
908
909 static void bnd(int *pos, int *sz, int opos, int osz, int minsz, int maxsz)
910 {
911     int ud=abs(*pos-opos);
912     int dd=abs((*pos+*sz)-(opos+osz));
913     int szrq=*sz;
914     
915     if(ud+dd!=0){
916         bound(sz, minsz, maxsz);
917         *pos+=(szrq-*sz)*ud/(ud+dd);
918     }
919 }
920
921
922 WSplit *split_find_root(WSplit *split)
923 {
924     if(split->parent==NULL)
925         return split;
926     return split_find_root((WSplit*)split->parent);
927 }
928
929
930 void splittree_rqgeom(WSplit *sub, int flags, const WRectangle *geom_, 
931                       WRectangle *geomret)
932 {
933     bool hany=flags&REGION_RQGEOM_WEAK_X;
934     bool vany=flags&REGION_RQGEOM_WEAK_Y;
935     bool tryonly=flags&REGION_RQGEOM_TRYONLY;
936     WRectangle geom=*geom_;
937     WRectangle retg;
938     WSplit *root=split_find_root(sub);
939     
940     if(geomret==NULL)
941         geomret=&retg;
942
943     split_update_bounds(root, TRUE);
944     
945     if(OBJ_IS(sub, WSplitST)){
946         WSplitST *sub_as_stdisp=(WSplitST*)sub;
947         
948         if(flags&REGION_RQGEOM_TRYONLY){
949             warn(TR("REGION_RQGEOM_TRYONLY unsupported for status display."));
950             *geomret=sub->geom;
951             return;
952         }
953         split_regularise_stdisp(sub_as_stdisp);
954         geom=sub->geom;
955         if(sub_as_stdisp->orientation==REGION_ORIENTATION_HORIZONTAL){
956             if(geom_->h==geom.h)
957                 return;
958             geom.h=geom_->h;
959         }else{
960             if(geom_->w==geom.w)
961                 return;
962             geom.w=geom_->w;
963         }
964         split_update_bounds(root, TRUE);
965     }
966
967     /* Handle internal size bounds */
968     bnd(&(geom.x), &(geom.w), sub->geom.x, sub->geom.w, 
969         sub->min_w, sub->max_w);
970     bnd(&(geom.y), &(geom.h), sub->geom.y, sub->geom.h, 
971         sub->min_h, sub->max_h);
972
973     /* Check if we should resize to both tl and br */
974     
975     if(hany){
976         geom.w+=sub->geom.x-geom.x;
977         geom.x=sub->geom.x;
978     }
979
980     if(vany){
981         geom.h+=sub->geom.y-geom.y;
982         geom.y=sub->geom.y;
983     }
984     
985     splittree_begin_resize();
986     
987     split_do_rqgeom_(sub, &geom, hany, vany, geomret, tryonly);
988     
989     if(!tryonly){
990         split_do_resize(sub, geomret, hany, vany, FALSE);
991         splittree_end_resize();
992         *geomret=sub->geom;
993     }else{
994         saw_stdisp=NULL;
995     }
996 }
997
998
999 /*EXTL_DOC
1000  * Attempt to resize and/or move the split tree starting at \var{node}.
1001  * Behaviour and the \var{g} parameter are as for \fnref{WRegion.rqgeom} 
1002  * operating on \var{node} (if it were a \type{WRegion}).
1003  */
1004 EXTL_EXPORT_MEMBER
1005 ExtlTab split_rqgeom(WSplit *node, ExtlTab g)
1006 {
1007     WRectangle geom, ogeom;
1008     int flags=REGION_RQGEOM_WEAK_ALL;
1009         
1010     geom=node->geom;
1011     ogeom=geom;
1012
1013     if(extl_table_gets_i(g, "x", &(geom.x)))
1014         flags&=~REGION_RQGEOM_WEAK_X;
1015     if(extl_table_gets_i(g, "y", &(geom.y)))
1016         flags&=~REGION_RQGEOM_WEAK_Y;
1017     if(extl_table_gets_i(g, "w", &(geom.w)))
1018         flags&=~REGION_RQGEOM_WEAK_W;
1019     if(extl_table_gets_i(g, "h", &(geom.h)))
1020         flags&=~REGION_RQGEOM_WEAK_H;
1021     
1022     geom.w=maxof(1, geom.w);
1023     geom.h=maxof(1, geom.h);
1024
1025     splittree_rqgeom(node, flags, &geom, &ogeom);
1026     
1027     return extl_table_from_rectangle(&ogeom);
1028     
1029 err:
1030     warn(TR("Invalid node."));
1031     return extl_table_none();
1032 }
1033
1034
1035 /*}}}*/
1036
1037
1038 /*{{{ Split */
1039
1040
1041 void splittree_changeroot(WSplit *root, WSplit *node)
1042 {
1043     WTiling *ws=(WTiling*)(root->ws_if_root);
1044     
1045     assert(ws!=NULL);
1046     assert(ws->split_tree==root);
1047     root->ws_if_root=NULL;
1048     ws->split_tree=node;
1049     if(node!=NULL){
1050         node->ws_if_root=ws;
1051         node->parent=NULL;
1052     }
1053 }
1054
1055
1056 static void splitsplit_replace(WSplitSplit *split, WSplit *child,
1057                                WSplit *what)
1058 {
1059     assert(split->tl==child || split->br==child);
1060     
1061     if(split->tl==child)
1062         split->tl=what;
1063     else
1064         split->br=what;
1065
1066     child->parent=NULL;
1067     
1068     what->parent=(WSplitInner*)split;
1069     what->ws_if_root=NULL; /* May not be needed. */
1070 }
1071
1072
1073 void splitinner_replace(WSplitInner *split, WSplit *child, WSplit *what)
1074 {
1075     CALL_DYN(splitinner_replace, split, (split, child, what));
1076 }
1077
1078
1079 WSplitRegion *splittree_split(WSplit *node, int dir, WPrimn primn,
1080                               int minsize, WRegionSimpleCreateFn *fn, 
1081                               WWindow *parent)
1082 {
1083     int objmin, objmax;
1084     int s, sn, so, pos;
1085     WSplitSplit *nsplit;
1086     WSplitRegion *nnode;
1087     WSplitInner *psplit;
1088     WRegion *nreg;
1089     WFitParams fp;
1090     WRectangle ng, rg;
1091     
1092     assert(node!=NULL && parent!=NULL);
1093     
1094     splittree_begin_resize();
1095     
1096     node=dodge_stdisp(node, FALSE);
1097     
1098     if(node==NULL)
1099         return NULL;
1100     
1101     if(OBJ_IS(node, WSplitST)){
1102         warn(TR("Splitting the status display is not allowed."));
1103         return NULL;
1104     }
1105
1106     if(primn!=PRIMN_TL && primn!=PRIMN_BR)
1107         primn=PRIMN_BR;
1108     if(dir!=SPLIT_HORIZONTAL && dir!=SPLIT_VERTICAL)
1109         dir=SPLIT_VERTICAL;
1110
1111     split_update_bounds(split_find_root(node), TRUE);
1112     objmin=(dir==SPLIT_VERTICAL ? node->min_h : node->min_w);
1113
1114     s=split_size(node, dir);
1115     sn=maxof(minsize, s/2);
1116     so=maxof(objmin, s-sn);
1117
1118     if(sn+so!=s){
1119         int rs;
1120         ng=node->geom;
1121         if(dir==SPLIT_VERTICAL)
1122             ng.h=sn+so;
1123         else
1124             ng.w=sn+so;
1125         split_do_rqgeom_(node, &ng, TRUE, TRUE, &rg, TRUE);
1126         rs=(dir==SPLIT_VERTICAL ? rg.h : rg.w);
1127         if(rs<minsize+objmin){
1128             warn(TR("Unable to split: not enough free space."));
1129             return NULL;
1130         }
1131         split_do_rqgeom_(node, &ng, TRUE, TRUE, &rg, FALSE);
1132         rs=(dir==SPLIT_VERTICAL ? rg.h : rg.w);
1133         if(minsize>rs/2){
1134             sn=minsize;
1135             so=rs-sn;
1136         }else{
1137             so=maxof(rs/2, objmin);
1138             sn=rs-so;
1139         }
1140     }else{
1141         rg=node->geom;
1142         splittree_scan_stdisp_rootward(node);
1143     }
1144
1145     /* Create split and new window
1146      */
1147     fp.mode=REGION_FIT_EXACT;
1148     fp.g=rg;
1149     
1150     nsplit=create_splitsplit(&(fp.g), dir);
1151     
1152     if(nsplit==NULL)
1153         return NULL;
1154
1155     if(dir==SPLIT_VERTICAL){
1156         if(primn==PRIMN_BR)
1157             fp.g.y+=so;
1158         fp.g.h=sn;
1159     }else{
1160         if(primn==PRIMN_BR)
1161             fp.g.x+=so;
1162         fp.g.w=sn;
1163     }
1164     
1165     nreg=fn(parent, &fp);
1166     
1167     if(nreg==NULL){
1168         destroy_obj((Obj*)nsplit);
1169         return NULL;
1170     }
1171
1172     nnode=create_splitregion(&(fp.g), nreg);
1173     if(nnode==NULL){
1174         destroy_obj((Obj*)nreg);
1175         destroy_obj((Obj*)nsplit);
1176         return NULL;
1177     }
1178     
1179     /* Now that everything's ok, resize and move original node.
1180      */    
1181     ng=rg;
1182     if(dir==SPLIT_VERTICAL){
1183         ng.h=so;
1184         if(primn==PRIMN_TL)
1185             ng.y+=sn;
1186     }else{
1187         ng.w=so;
1188         if(primn==PRIMN_TL)
1189             ng.x+=sn;
1190     }
1191
1192     split_do_resize(node, &ng, 
1193                     (dir==SPLIT_HORIZONTAL ? primn : PRIMN_ANY),
1194                     (dir==SPLIT_VERTICAL ? primn : PRIMN_ANY),
1195                     FALSE);
1196
1197     /* Set up split structure
1198      */
1199     psplit=node->parent;
1200     
1201     if(psplit!=NULL)
1202         splitinner_replace(psplit, node, (WSplit*)nsplit);
1203     else
1204         splittree_changeroot(node, (WSplit*)nsplit);
1205         
1206     node->parent=(WSplitInner*)nsplit;
1207     ((WSplit*)nnode)->parent=(WSplitInner*)nsplit;
1208     
1209     if(primn==PRIMN_BR){
1210         nsplit->tl=node;
1211         nsplit->br=(WSplit*)nnode;
1212         nsplit->current=SPLIT_CURRENT_TL;
1213     }else{
1214         nsplit->tl=(WSplit*)nnode;
1215         nsplit->br=node;
1216         nsplit->current=SPLIT_CURRENT_BR;
1217     }
1218     
1219     splittree_end_resize();
1220     
1221     return nnode;
1222 }
1223
1224
1225 /*}}}*/
1226
1227
1228 /*{{{ Remove */
1229
1230
1231 static void splitsplit_remove(WSplitSplit *node, WSplit *child, 
1232                               bool reclaim_space)
1233 {
1234     static int nstdisp=0;
1235     WSplitInner *parent;
1236     WSplit *other;
1237     int hprimn=PRIMN_ANY, vprimn=PRIMN_ANY;
1238     
1239     assert(node->tl==child || node->br==child);
1240     
1241     if(node->tl==child){
1242         other=node->br;
1243         if(node->dir==SPLIT_VERTICAL)
1244             vprimn=PRIMN_TL;
1245         else
1246             hprimn=PRIMN_TL;
1247     }else{
1248         other=node->tl;
1249         if(node->dir==SPLIT_VERTICAL)
1250             vprimn=PRIMN_BR;
1251         else
1252             hprimn=PRIMN_BR;
1253     }
1254     
1255     assert(other!=NULL);
1256
1257     if(nstdisp==0 && reclaim_space && OBJ_IS(other, WSplitST)){
1258         /* Try to move stdisp out of the way. */
1259         split_try_unsink_stdisp(node, FALSE, TRUE);
1260         assert(child->parent!=NULL);
1261         nstdisp++;
1262         splitinner_remove(child->parent, child, reclaim_space);
1263         nstdisp--;
1264         return;
1265     }
1266
1267     parent=((WSplit*)node)->parent;
1268     
1269     if(parent!=NULL)
1270         splitinner_replace(parent, (WSplit*)node, other);
1271     else
1272         splittree_changeroot((WSplit*)node, other);
1273     
1274     if(reclaim_space)
1275         split_resize(other, &(((WSplit*)node)->geom), hprimn, vprimn);
1276     
1277     child->parent=NULL;
1278     
1279     node->tl=NULL;
1280     node->br=NULL;
1281     ((WSplit*)node)->parent=NULL;
1282     destroy_obj((Obj*)node);
1283 }
1284
1285
1286 void splitinner_remove(WSplitInner *node, WSplit *child, bool reclaim_space)
1287 {
1288     CALL_DYN(splitinner_remove, node, (node, child, reclaim_space));
1289 }
1290
1291
1292 void splittree_remove(WSplit *node, bool reclaim_space)
1293 {
1294     if(node->parent!=NULL)
1295         splitinner_remove(node->parent, node, reclaim_space);
1296     else if(node->ws_if_root!=NULL)
1297         splittree_changeroot(node, NULL);
1298     
1299     destroy_obj((Obj*)node);
1300 }
1301
1302
1303 /*}}}*/
1304
1305
1306 /*{{{ Tree traversal */
1307
1308
1309 static bool defaultfilter(WSplit *node)
1310 {
1311     return (OBJ_IS(node, WSplitRegion) && 
1312             ((WSplitRegion*)node)->reg!=NULL);
1313 }
1314
1315
1316 static WSplit *split_current_todir_default(WSplit *node, 
1317                                            WPrimn hprimn, WPrimn vprimn,
1318                                            WSplitFilter *filter)
1319 {
1320     if(filter==NULL)
1321         filter=defaultfilter;
1322
1323     return (filter(node) ? node : NULL);
1324 }
1325
1326
1327 static WSplit *splitsplit_current_todir(WSplitSplit *node, 
1328                                         WPrimn hprimn, WPrimn vprimn,
1329                                         WSplitFilter *filter)
1330 {
1331     WPrimn primn=(node->dir==SPLIT_HORIZONTAL ? hprimn : vprimn);
1332     WSplit *first, *second, *ret;
1333     
1334     if(primn==PRIMN_TL ||
1335        (primn==PRIMN_ANY && node->current==SPLIT_CURRENT_TL)){
1336         first=node->tl;
1337         second=node->br;
1338     }else if(primn==PRIMN_BR ||
1339        (primn==PRIMN_ANY && node->current==SPLIT_CURRENT_BR)){
1340         first=node->br;
1341         second=node->tl;
1342     }else{
1343         return NULL;
1344     }
1345         
1346     ret=split_current_todir(first, hprimn, vprimn, filter);
1347     if(ret==NULL)
1348         ret=split_current_todir(second, hprimn, vprimn, filter);
1349     if(ret==NULL && filter!=NULL){
1350         if(filter((WSplit*)node))
1351             ret=(WSplit*)node;
1352     }
1353         
1354     return ret;
1355 }
1356
1357
1358 WSplit *split_current_todir(WSplit *node, WPrimn hprimn, WPrimn vprimn,
1359                             WSplitFilter *filter)
1360 {
1361     WSplit *ret=NULL;
1362     CALL_DYN_RET(ret, WSplit*, split_current_todir, node, 
1363                  (node, hprimn, vprimn, filter));
1364     return ret;
1365 }
1366
1367
1368 /* Note: both hprimn and vprimn are inverted when descending.  Therefore
1369  * one should be either PRIMN_NONE or PRIMN_ANY for sensible geometric 
1370  * navigation. (Both are PRIMN_TL or PRIMN_BR for pseudo-linear 
1371  * next/previous navigation.)
1372  */
1373 WSplit *splitsplit_nextto(WSplitSplit *node, WSplit *child,
1374                           WPrimn hprimn, WPrimn vprimn, 
1375                           WSplitFilter *filter)
1376 {
1377     WPrimn primn=(node->dir==SPLIT_HORIZONTAL ? hprimn : vprimn);
1378     WSplit *split=NULL, *nnode=NULL;
1379     
1380     if(node->tl==child && (primn==PRIMN_BR || primn==PRIMN_ANY))
1381         split=node->br;
1382     else if(node->br==child && (primn==PRIMN_TL || primn==PRIMN_ANY))
1383         split=node->tl;
1384     
1385     if(split!=NULL){
1386         nnode=split_current_todir(split, 
1387                                   primn_none2any(primn_invert(hprimn)),
1388                                   primn_none2any(primn_invert(vprimn)),
1389                                   filter);
1390     }
1391     
1392     if(nnode==NULL)
1393         nnode=split_nextto((WSplit*)node, hprimn, vprimn, filter);
1394         
1395     return nnode;
1396 }
1397
1398
1399 WSplit *splitinner_nextto(WSplitInner *node, WSplit *child,
1400                           WPrimn hprimn, WPrimn vprimn, 
1401                           WSplitFilter *filter)
1402 {
1403     WSplit *ret=NULL;
1404     CALL_DYN_RET(ret, WSplit*, splitinner_nextto, node, 
1405                  (node, child, hprimn, vprimn, filter));
1406     return ret;
1407 }
1408
1409
1410 WSplit *split_nextto(WSplit *node, WPrimn hprimn, WPrimn vprimn, 
1411                      WSplitFilter *filter)
1412 {
1413     while(node->parent!=NULL){
1414         WSplit *ret=splitinner_nextto(node->parent, node, 
1415                                       hprimn, vprimn, filter);
1416         if(ret!=NULL)
1417             return ret;
1418         node=(WSplit*)node->parent;
1419     }
1420     return NULL;
1421 }
1422
1423
1424 void splitinner_mark_current_default(WSplitInner *split, WSplit *child)
1425 {
1426     if(((WSplit*)split)->parent!=NULL)
1427         splitinner_mark_current(((WSplit*)split)->parent, (WSplit*)split);
1428 }
1429
1430
1431 void splitsplit_mark_current(WSplitSplit *split, WSplit *child)
1432 {
1433     assert(child==split->tl || child==split->br);
1434     
1435     split->current=(split->tl==child ? SPLIT_CURRENT_TL : SPLIT_CURRENT_BR);
1436     
1437     splitinner_mark_current_default(&(split->isplit), child);
1438 }
1439
1440
1441 void splitinner_mark_current(WSplitInner *split, WSplit *child)
1442 {
1443     CALL_DYN(splitinner_mark_current, split, (split, child));
1444 }
1445
1446
1447 static void splitsplit_forall(WSplitSplit *node, WSplitFn *fn)
1448 {
1449     fn(node->tl);
1450     fn(node->br);
1451 }
1452
1453
1454 void splitinner_forall(WSplitInner *node, WSplitFn *fn)
1455 {
1456     CALL_DYN(splitinner_forall, node, (node, fn));
1457 }
1458
1459
1460 static WSplit *splitsplit_current(WSplitSplit *split)
1461 {
1462     return (split->current==SPLIT_CURRENT_TL ? split->tl : split->br);
1463 }
1464
1465
1466 /*EXTL_DOC
1467  * Returns the most previously active child node of \var{split}.
1468  */
1469 EXTL_SAFE
1470 EXTL_EXPORT_MEMBER
1471 WSplit *splitinner_current(WSplitInner *node)
1472 {
1473     WSplit *ret=NULL;
1474     CALL_DYN_RET(ret, WSplit*, splitinner_current, node, (node));
1475     return ret;
1476 }
1477     
1478
1479 /*}}}*/
1480
1481
1482 /*{{{ X window handling */
1483
1484
1485 static void splitregion_stacking(WSplitRegion *split, 
1486                                  Window *bottomret, Window *topret)
1487 {
1488     *bottomret=None;
1489     *topret=None;
1490     if(split->reg!=NULL)
1491         region_stacking(split->reg, bottomret, topret);
1492 }
1493
1494
1495 void splitsplit_stacking(WSplitSplit *split, 
1496                          Window *bottomret, Window *topret)
1497 {
1498     Window tlb=None, tlt=None;
1499     Window brb=None, brt=None;
1500     
1501     split_stacking(split->tl, &tlb, &tlt);
1502     split_stacking(split->br, &brb, &brt);
1503     
1504     /* To make sure that this condition holds is left to the workspace
1505      * code to do after a split tree has been loaded or modified.
1506      */
1507     if(split->current==SPLIT_CURRENT_TL){
1508         *topret=(tlt!=None ? tlt : brt);
1509         *bottomret=(brb!=None ? brb : tlb);
1510     }else{
1511         *topret=(brt!=None ? brt : tlt);
1512         *bottomret=(tlb!=None ? tlb : brb);
1513     }
1514 }
1515
1516 void split_stacking(WSplit *split, Window *bottomret, Window *topret)
1517 {
1518     *bottomret=None;
1519     *topret=None;
1520     {
1521         CALL_DYN(split_stacking, split, (split, bottomret, topret));
1522     }
1523 }
1524
1525
1526 static void splitregion_restack(WSplitRegion *split, Window other, int mode)
1527 {
1528     if(split->reg!=NULL)
1529         region_restack(split->reg, other, mode);
1530 }
1531
1532 void splitsplit_restack(WSplitSplit *split, Window other, int mode)
1533 {
1534     Window bottom=None, top=None;
1535     WSplit *first, *second;
1536     
1537     if(split->current==SPLIT_CURRENT_TL){
1538         first=split->br;
1539         second=split->tl;
1540     }else{
1541         first=split->tl;
1542         second=split->br;
1543     }
1544     
1545     split_restack(first, other, mode);
1546     split_stacking(first, &bottom, &top);
1547     if(top!=None){
1548         other=top;
1549         mode=Above;
1550     }
1551     split_restack(second, other, mode);
1552 }
1553
1554 void split_restack(WSplit *split, Window other, int mode)
1555 {
1556     CALL_DYN(split_restack, split, (split, other, mode));
1557 }
1558
1559
1560 static void splitregion_map(WSplitRegion *split)
1561 {
1562     if(split->reg!=NULL)
1563         region_map(split->reg);
1564 }
1565
1566 static void splitinner_map(WSplitInner *split)
1567 {
1568     splitinner_forall(split, split_map);
1569 }
1570
1571 void split_map(WSplit *split)
1572 {
1573     CALL_DYN(split_map, split, (split));
1574 }
1575
1576
1577 static void splitregion_unmap(WSplitRegion *split)
1578 {
1579     if(split->reg!=NULL)
1580         region_unmap(split->reg);
1581 }
1582
1583 static void splitinner_unmap(WSplitInner *split)
1584 {
1585     splitinner_forall(split, split_unmap);
1586 }
1587
1588 void split_unmap(WSplit *split)
1589 {
1590     CALL_DYN(split_unmap, split, (split));
1591 }
1592
1593
1594 static void splitregion_reparent(WSplitRegion *split, WWindow *wwin)
1595 {
1596     if(split->reg!=NULL){
1597         WRectangle g=split->split.geom;
1598         region_reparent(split->reg, wwin, &g, REGION_FIT_EXACT);
1599     }
1600 }
1601
1602
1603 static void splitsplit_reparent(WSplitSplit *split, WWindow *wwin)
1604 {
1605     if(split->current==SPLIT_CURRENT_TL){
1606         split_reparent(split->br, wwin);
1607         split_reparent(split->tl, wwin);
1608     }else{
1609         split_reparent(split->tl, wwin);
1610         split_reparent(split->br, wwin);
1611     }
1612 }
1613
1614
1615 void split_reparent(WSplit *split, WWindow *wwin)
1616 {
1617     CALL_DYN(split_reparent, split, (split, wwin));
1618 }
1619
1620
1621 /*}}}*/
1622
1623
1624 /*{{{ Transpose, flip, rotate */
1625
1626
1627 void splitsplit_flip_default(WSplitSplit *split)
1628 {
1629     WRectangle tlng, brng;
1630     WRectangle *sg=&((WSplit*)split)->geom;
1631     WSplit *tmp;
1632     
1633     assert(split->tl!=NULL && split->br!=NULL);
1634     
1635     split_update_bounds((WSplit*)split, TRUE);
1636
1637     tlng=split->tl->geom;
1638     brng=split->br->geom;
1639     
1640     if(split->dir==SPLIT_HORIZONTAL){
1641         brng.x=sg->x;
1642         tlng.x=sg->x+sg->w-tlng.w;
1643     }else{
1644         brng.y=sg->y;
1645         tlng.y=sg->y+sg->h-tlng.h;
1646     }
1647     
1648     tmp=split->tl;
1649     split->tl=split->br;
1650     split->br=tmp;
1651     split->current=(split->current==SPLIT_CURRENT_TL
1652                     ? SPLIT_CURRENT_BR
1653                     : SPLIT_CURRENT_TL);
1654
1655     split_do_resize(split->tl, &brng, PRIMN_ANY, PRIMN_ANY, FALSE);
1656     split_do_resize(split->br, &tlng, PRIMN_ANY, PRIMN_ANY, FALSE);
1657 }
1658
1659
1660 static void splitsplit_flip_(WSplitSplit *split)
1661 {
1662     CALL_DYN(splitsplit_flip, split, (split));
1663 }
1664
1665
1666 /*EXTL_DOC
1667  * Flip contents of \var{split}.
1668  */
1669 EXTL_EXPORT_MEMBER
1670 void splitsplit_flip(WSplitSplit *split)
1671 {
1672     splittree_begin_resize();
1673
1674     split=OBJ_CAST(dodge_stdisp((WSplit*)split, FALSE), WSplitSplit);
1675     
1676     if(split==NULL)
1677         return;
1678     
1679     splitsplit_flip_(split);
1680     
1681     splittree_end_resize();
1682 }
1683
1684 typedef enum{
1685     FLIP_VERTICAL,
1686     FLIP_HORIZONTAL,
1687     FLIP_NONE,
1688     FLIP_ANY
1689 } FlipDir;
1690
1691
1692 static FlipDir flipdir=FLIP_VERTICAL;
1693
1694
1695 static void do_flip(WSplit *split)
1696 {
1697     WSplitSplit *ss=OBJ_CAST(split, WSplitSplit);
1698     
1699     if(ss!=NULL){
1700         if((flipdir==FLIP_ANY
1701             || (ss->dir==SPLIT_VERTICAL && flipdir==FLIP_VERTICAL)
1702             || (ss->dir==SPLIT_HORIZONTAL && flipdir==FLIP_HORIZONTAL)) 
1703            && !OBJ_IS(ss->tl, WSplitST) 
1704            && !OBJ_IS(ss->br, WSplitST)){
1705             splitsplit_flip_(ss);
1706         }
1707     }
1708     
1709     if(OBJ_IS(ss, WSplitInner))
1710         splitinner_forall((WSplitInner*)ss, do_flip);
1711 }
1712
1713     
1714 static void splittree_flip_dir(WSplit *splittree, FlipDir dir)
1715 {
1716     /* todo stdisp outta way */
1717     if(OBJ_IS(splittree, WSplitInner)){
1718         flipdir=dir;
1719         splitinner_forall((WSplitInner*)splittree, do_flip);
1720     }
1721 }
1722
1723
1724 static bool split_fliptrans_to(WSplit *node, const WRectangle *geom, 
1725                               bool trans, FlipDir flip)
1726 {
1727     WRectangle rg;
1728     WSplit *node2;
1729     
1730     splittree_begin_resize();
1731     
1732     /* split_do_resize can do things right if 'node' has stdisp as child, 
1733      * but otherwise transpose will put the stdisp in a bad split
1734      * configuration if it is contained within 'node', so we must
1735      * first move it and its fixed parent split below node. For correct
1736      * geometry calculation we move it immediately below node, and
1737      * resize stdisp's fixed parent node instead.
1738      */
1739     node2=dodge_stdisp(node, TRUE);
1740     
1741     if(node==NULL || node2!=node)
1742         return FALSE;
1743     
1744     split_update_bounds(node, TRUE);
1745     
1746     split_do_rqgeom_(node, geom, PRIMN_ANY, PRIMN_ANY, &rg, FALSE);
1747     
1748     split_do_resize(node, &rg, PRIMN_ANY, PRIMN_ANY, trans);
1749     
1750     if(flip!=FLIP_NONE)
1751         splittree_flip_dir(node, flip);
1752
1753     splittree_end_resize();
1754     
1755     return TRUE;
1756 }
1757
1758
1759 bool split_transpose_to(WSplit *node, const WRectangle *geom)
1760 {
1761     return split_fliptrans_to(node, geom, TRUE, FLIP_ANY);
1762 }
1763
1764
1765 /*EXTL_DOC
1766  * Transpose contents of \var{node}. 
1767  */
1768 EXTL_EXPORT_MEMBER
1769 void split_transpose(WSplit *node)
1770 {
1771     WRectangle g=node->geom;
1772     
1773     split_transpose_to(node, &g);
1774 }
1775
1776
1777 bool split_rotate_to(WSplit *node, const WRectangle *geom, int rotation)
1778 {
1779     FlipDir flip=FLIP_NONE;
1780     bool trans=FALSE;
1781     
1782     if(rotation==SCREEN_ROTATION_90){
1783         flip=FLIP_HORIZONTAL;
1784         trans=TRUE;
1785     }else if(rotation==SCREEN_ROTATION_180){
1786         flip=FLIP_ANY;
1787     }else if(rotation==SCREEN_ROTATION_270){
1788         flip=FLIP_VERTICAL;
1789         trans=TRUE;
1790     }
1791
1792     return split_fliptrans_to(node, geom, trans, flip);
1793 }
1794
1795 /*}}}*/
1796
1797
1798 /*{{{ Exports */
1799
1800
1801 /*EXTL_DOC
1802  * Return parent split for \var{split}.
1803  */
1804 EXTL_SAFE
1805 EXTL_EXPORT_MEMBER
1806 WSplitInner *split_parent(WSplit *split)
1807 {
1808     return split->parent;
1809 }
1810
1811
1812 /*EXTL_DOC
1813  * Returns the area of workspace used by the regions under \var{split}.
1814  */
1815 EXTL_SAFE
1816 EXTL_EXPORT_MEMBER
1817 ExtlTab split_geom(WSplit *split)
1818 {
1819     return extl_table_from_rectangle(&(split->geom));
1820 }
1821
1822
1823 /*EXTL_DOC
1824  * Returns the top or left child node of \var{split} depending
1825  * on the direction of the split.
1826  */
1827 EXTL_SAFE
1828 EXTL_EXPORT_MEMBER
1829 WSplit *splitsplit_tl(WSplitSplit *split)
1830 {
1831     return split->tl;
1832 }
1833
1834
1835 /*EXTL_DOC
1836  * Returns the bottom or right child node of \var{split} depending
1837  * on the direction of the split.
1838  */
1839 EXTL_SAFE
1840 EXTL_EXPORT_MEMBER
1841 WSplit *splitsplit_br(WSplitSplit *split)
1842 {
1843     return split->br;
1844 }
1845
1846 /*EXTL_DOC
1847  * Returns the direction of \var{split}; either \codestr{vertical} or
1848  * \codestr{horizontal}.
1849  */
1850 EXTL_SAFE
1851 EXTL_EXPORT_MEMBER
1852 const char *splitsplit_dir(WSplitSplit *split)
1853 {
1854     return (split->dir==SPLIT_VERTICAL ? "vertical" : "horizontal");
1855 }
1856
1857
1858 /*EXTL_DOC
1859  * Returns the region contained in \var{node}.
1860  */
1861 EXTL_SAFE
1862 EXTL_EXPORT_MEMBER
1863 WRegion *splitregion_reg(WSplitRegion *node)
1864 {
1865     return node->reg;
1866 }
1867     
1868
1869 /*}}}*/
1870
1871
1872 /*{{{ Save support */
1873
1874
1875 ExtlTab split_base_config(WSplit *node)
1876 {
1877     ExtlTab t=extl_create_table();
1878     extl_table_sets_s(t, "type", OBJ_TYPESTR(node));
1879     return t;
1880 }
1881
1882
1883 static bool splitregion_get_config(WSplitRegion *node, ExtlTab *ret)
1884 {
1885     ExtlTab rt, t;
1886     
1887     if(node->reg==NULL)
1888         return FALSE;
1889     
1890     if(!region_supports_save(node->reg)){
1891         warn(TR("Unable to get configuration for %s."), 
1892              region_name(node->reg));
1893         return FALSE;
1894     }
1895     
1896     rt=region_get_configuration(node->reg);
1897     t=split_base_config(&(node->split));
1898     extl_table_sets_t(t, "regparams", rt);
1899     extl_unref_table(rt);
1900     *ret=t;
1901     
1902     return TRUE;
1903 }
1904
1905
1906 static bool splitst_get_config(WSplitST *node, ExtlTab *ret)
1907 {
1908     *ret=split_base_config((WSplit*)node);
1909     return TRUE;
1910 }
1911
1912
1913 static bool splitsplit_get_config(WSplitSplit *node, ExtlTab *ret)
1914 {
1915     ExtlTab tab, tltab, brtab;
1916     int tls, brs;
1917     
1918     if(!split_get_config(node->tl, &tltab))
1919         return split_get_config(node->br, ret);
1920     
1921     if(!split_get_config(node->br, &brtab)){
1922         *ret=tltab;
1923         return TRUE;
1924     }
1925
1926     tab=split_base_config((WSplit*)node);
1927
1928     tls=split_size(node->tl, node->dir);
1929     brs=split_size(node->br, node->dir);
1930         
1931     extl_table_sets_s(tab, "dir", (node->dir==SPLIT_VERTICAL
1932                                    ? "vertical" : "horizontal"));
1933
1934     extl_table_sets_i(tab, "tls", tls);
1935     extl_table_sets_t(tab, "tl", tltab);
1936     extl_unref_table(tltab);
1937
1938     extl_table_sets_i(tab, "brs", brs);
1939     extl_table_sets_t(tab, "br", brtab);
1940     extl_unref_table(brtab);
1941         
1942     *ret=tab;
1943     
1944     return TRUE;
1945 }
1946
1947
1948 bool split_get_config(WSplit *node, ExtlTab *tabret)
1949 {
1950     bool ret=FALSE;
1951     CALL_DYN_RET(ret, bool, split_get_config, node, (node, tabret));
1952     return ret;
1953 }
1954
1955
1956 /*}}}*/
1957
1958
1959 /*{{{ The classes */
1960
1961
1962 static DynFunTab split_dynfuntab[]={
1963     {split_do_resize, split_do_resize_default},
1964     {(DynFun*)split_current_todir, (DynFun*)split_current_todir_default},
1965     END_DYNFUNTAB,
1966 };
1967
1968 static DynFunTab splitinner_dynfuntab[]={
1969     {splitinner_mark_current, splitinner_mark_current_default},
1970     {split_map, splitinner_map},
1971     {split_unmap, splitinner_unmap},
1972     END_DYNFUNTAB,
1973 };
1974
1975 static DynFunTab splitsplit_dynfuntab[]={
1976     {split_update_bounds, splitsplit_update_bounds},
1977     {split_do_resize, splitsplit_do_resize},
1978     {splitinner_do_rqsize, splitsplit_do_rqsize},
1979     {splitinner_replace, splitsplit_replace},
1980     {splitinner_remove, splitsplit_remove},
1981     {(DynFun*)split_current_todir, (DynFun*)splitsplit_current_todir},
1982     {(DynFun*)splitinner_current, (DynFun*)splitsplit_current},
1983     {(DynFun*)splitinner_nextto, (DynFun*)splitsplit_nextto},
1984     {splitinner_mark_current, splitsplit_mark_current},
1985     {(DynFun*)split_get_config, (DynFun*)splitsplit_get_config},
1986     {splitinner_forall, splitsplit_forall},
1987     {split_restack, splitsplit_restack},
1988     {split_stacking, splitsplit_stacking},
1989     {split_reparent, splitsplit_reparent},
1990     {splitsplit_flip, splitsplit_flip_default},
1991     END_DYNFUNTAB,
1992 };
1993
1994 static DynFunTab splitregion_dynfuntab[]={
1995     {split_update_bounds, splitregion_update_bounds},
1996     {split_do_resize, splitregion_do_resize},
1997     {(DynFun*)split_get_config, (DynFun*)splitregion_get_config},
1998     {split_map, splitregion_map},
1999     {split_unmap, splitregion_unmap},
2000     {split_restack, splitregion_restack},
2001     {split_stacking, splitregion_stacking},
2002     {split_reparent, splitregion_reparent},
2003     END_DYNFUNTAB,
2004 };
2005
2006 static DynFunTab splitst_dynfuntab[]={
2007     {split_update_bounds, splitst_update_bounds},
2008     {split_do_resize, splitst_do_resize},
2009     {(DynFun*)split_get_config, (DynFun*)splitst_get_config},
2010     END_DYNFUNTAB,
2011 };
2012
2013
2014 EXTL_EXPORT
2015 IMPLCLASS(WSplit, Obj, split_deinit, split_dynfuntab);
2016
2017 EXTL_EXPORT
2018 IMPLCLASS(WSplitInner, WSplit, splitinner_deinit, splitinner_dynfuntab);
2019
2020 EXTL_EXPORT
2021 IMPLCLASS(WSplitSplit, WSplitInner, splitsplit_deinit, splitsplit_dynfuntab);
2022
2023 EXTL_EXPORT
2024 IMPLCLASS(WSplitRegion, WSplit, splitregion_deinit, splitregion_dynfuntab);
2025
2026 EXTL_EXPORT
2027 IMPLCLASS(WSplitST, WSplitRegion, splitst_deinit, splitst_dynfuntab);
2028
2029
2030 /*}}}*/
2031