]> git.decadent.org.uk Git - ion3.git/blob - mod_tiling/split.c
[svn-upgrade] Integrating new upstream version, ion3 (20070506)
[ion3.git] / mod_tiling / split.c
1 /*
2  * ion/mod_tiling/split.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2007. 
5  *
6  * See the included file LICENSE for details.
7  */
8
9 #include <limits.h>
10 #include <string.h>
11 #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 WSplitSplit *splittree_scan_stdisp_parent(WSplit *node_, bool set_saw)
475 {
476     WSplitSplit *r, *node=OBJ_CAST(node_, WSplitSplit);
477     
478     if(node==NULL)
479         return NULL;
480     
481     if(OBJ_IS(node->tl, WSplitST)){
482         if(set_saw)
483             saw_stdisp=(WSplitST*)node->tl;
484         return node;
485     }
486
487     if(OBJ_IS(node->br, WSplitST)){
488         if(set_saw)
489             saw_stdisp=(WSplitST*)node->br;
490         return node;
491     }
492
493     r=splittree_scan_stdisp_parent(node->tl, set_saw);
494     if(r==NULL)
495         r=splittree_scan_stdisp_parent(node->br, set_saw);
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 *move_stdisp_out_of_way(WSplit *node)
508 {
509     WSplitSplit *stdispp;
510     
511     if(!OBJ_IS(node, WSplitSplit))
512         return node;
513     
514     stdispp=splittree_scan_stdisp_parent(node, TRUE);
515         
516     if(stdispp==NULL)
517         return node;
518         
519     while(stdispp->tl!=node && stdispp->br!=node){
520         if(!split_try_unsink_stdisp(stdispp, FALSE, TRUE)){
521             warn(TR("Unable to move the status display out of way."));
522             return NULL;
523         }
524     }
525     
526     return (WSplit*)stdispp;
527 }
528
529
530 /*}}}*/
531
532
533 /*{{{ Low-level resize code; from root to leaf */
534
535
536 static void split_do_resize_default(WSplit *node, const WRectangle *ng, 
537                                     WPrimn hprimn, WPrimn vprimn, 
538                                     bool transpose)
539 {
540     node->geom=*ng;
541 }
542
543
544 static void splitregion_do_resize(WSplitRegion *node, const WRectangle *ng, 
545                                   WPrimn hprimn, WPrimn vprimn, 
546                                   bool transpose)
547 {
548     assert(node->reg!=NULL);
549     region_fit(node->reg, ng, REGION_FIT_EXACT);
550     split_update_bounds(&(node->split), FALSE);
551     node->split.geom=*ng;
552 }
553
554
555 static void splitst_do_resize(WSplitST *node, const WRectangle *ng, 
556                               WPrimn hprimn, WPrimn vprimn, 
557                               bool transpose)
558 {
559     saw_stdisp=node;
560
561     if(node->regnode.reg==NULL){
562         ((WSplit*)node)->geom=*ng;
563     }else{
564         splitregion_do_resize(&(node->regnode), ng, hprimn, vprimn, 
565                                transpose);
566     }
567 }
568
569
570 static int other_dir(int dir)
571 {
572     return (dir==SPLIT_VERTICAL ? SPLIT_HORIZONTAL : SPLIT_VERTICAL);
573 }
574
575
576 static void adjust_sizes(int *tls_, int *brs_, int nsize, int sz, 
577                          int tlmin, int brmin, int tlmax, int brmax,
578                          int primn)
579 {
580     int tls=*tls_;
581     int brs=*brs_;
582     
583     if(primn==PRIMN_TL){
584         tls=tls+nsize-sz;
585         bound(&tls, tlmin, tlmax);
586         brs=nsize-tls;
587         bound(&brs, brmin, brmax);
588         tls=nsize-brs;
589         bound(&tls, tlmin, tlmax);
590     }else if(primn==PRIMN_BR){
591         brs=brs+nsize-sz;
592         bound(&brs, brmin, brmax);
593         tls=nsize-brs;
594         bound(&tls, tlmin, tlmax);
595         brs=nsize-tls;
596         bound(&brs, brmin, brmax);
597     }else{ /* && PRIMN_ANY */
598         tls=tls*nsize/sz;
599         bound(&tls, tlmin, tlmax);
600         brs=nsize-tls;
601         bound(&brs, brmin, brmax);
602         tls=nsize-brs;
603         bound(&tls, tlmin, tlmax);
604     }
605     
606     *tls_=tls;
607     *brs_=brs;
608 }
609
610
611 static void get_minmaxunused(WSplit *node, int dir, 
612                              int *min, int *max, int *unused)
613 {
614     if(dir==SPLIT_VERTICAL){
615         *min=node->min_h;
616         *max=maxof(*min, node->max_h);
617         *unused=minof(node->unused_h, node->geom.h);
618     }else{
619         *min=node->min_w;
620         *max=maxof(*min, node->max_w);
621         *unused=minof(node->unused_w, node->geom.w);
622     }
623 }
624
625
626 void splitsplit_do_resize(WSplitSplit *node, const WRectangle *ng, 
627                           WPrimn hprimn, WPrimn vprimn, bool transpose)
628 {
629     assert(ng->w>=0 && ng->h>=0);
630     assert(node->tl!=NULL && node->br!=NULL);
631     assert(!transpose || (hprimn==PRIMN_ANY && vprimn==PRIMN_ANY));
632     
633     {
634         WSplit *tl=node->tl, *br=node->br;
635         int tls=split_size((WSplit*)tl, node->dir);
636         int brs=split_size((WSplit*)br, node->dir);
637         int sz=tls+brs;
638         /* Status display can not be transposed. */
639         int dir=((transpose && !stdisp_immediate_child(node))
640                  ? other_dir(node->dir)
641                  : node->dir);
642         int nsize=(dir==SPLIT_VERTICAL ? ng->h : ng->w);
643         int primn=(dir==SPLIT_VERTICAL ? vprimn : hprimn);
644         int tlmin, tlmax, tlunused, tlused;
645         int brmin, brmax, brunused, brused;
646         WRectangle tlg=*ng, brg=*ng;
647         
648         get_minmaxunused(tl, dir, &tlmin, &tlmax, &tlunused);
649         get_minmaxunused(br, dir, &brmin, &brmax, &brunused);
650         
651         tlused=maxof(0, tls-maxof(0, tlunused));
652         brused=maxof(0, brs-maxof(0, brunused));
653         /* tlmin,  brmin >= 1 => (tls>=tlmin, brs>=brmin => sz>0) */
654         
655         if(sz>2){
656             if(primn==PRIMN_ANY && (tlunused>=0 || brunused>=0)){
657                 if(nsize<=tlused+brused){
658                     /* Need to shrink a tangible node */
659                     adjust_sizes(&tls, &brs, nsize, sz,
660                                  tlmin, brmin, tlused, brused, primn);
661                 }else{
662                     /* Just expand or shrink unused space */
663                     adjust_sizes(&tls, &brs, nsize, sz,
664                                  tlused, brused, 
665                                  (tlunused<0 ? tlused : tlmax),
666                                  (brunused<0 ? brused : brmax), primn);
667                 }
668                 
669             }else{
670                 adjust_sizes(&tls, &brs, nsize, sz, 
671                              tlmin, brmin, tlmax, brmax, primn);
672             }
673         }
674         
675         if(tls+brs!=nsize){
676             /* Bad fit; just size proportionally. */
677             if(sz<=2){
678                 tls=nsize/2;
679                 brs=nsize-tls;
680             }else{
681                 tls=split_size(tl, node->dir)*nsize/sz;
682                 brs=nsize-tls;
683             }
684         }
685         
686         if(dir==SPLIT_VERTICAL){
687             tlg.h=tls;
688             brg.y+=tls;
689             brg.h=brs;
690         }else{
691             tlg.w=tls;
692             brg.x+=tls;
693             brg.w=brs;
694         }
695         
696         split_do_resize(tl, &tlg, hprimn, vprimn, transpose);
697         split_do_resize(br, &brg, hprimn, vprimn, transpose);
698         
699         node->dir=dir;
700         ((WSplit*)node)->geom=*ng;
701         split_update_bounds((WSplit*)node, FALSE);
702     }
703 }
704
705
706 void split_do_resize(WSplit *node, const WRectangle *ng, 
707                      WPrimn hprimn, WPrimn vprimn, bool transpose)
708 {
709     CALL_DYN(split_do_resize, node, (node, ng, hprimn, vprimn, transpose));
710 }
711
712
713 void split_resize(WSplit *node, const WRectangle *ng, 
714                   WPrimn hprimn, WPrimn vprimn)
715 {
716     split_update_bounds(node, TRUE);
717     splittree_begin_resize();
718     split_do_resize(node, ng, hprimn, vprimn, FALSE);
719     splittree_end_resize();
720 }
721
722
723 /*}}}*/
724
725
726 /*{{{ Low-level resize code; request towards root */
727
728
729 static void flexibility(WSplit *node, int dir, int *shrink, int *stretch)
730 {
731     if(dir==SPLIT_VERTICAL){
732         *shrink=maxof(0, node->geom.h-node->min_h);
733         if(OBJ_IS(node, WSplitST))
734             *stretch=maxof(0, node->max_h-node->geom.h);
735         else
736             *stretch=INT_MAX;
737     }else{
738         *shrink=maxof(0, node->geom.w-node->min_w);
739         if(OBJ_IS(node, WSplitST))
740             *stretch=maxof(0, node->max_w-node->geom.w);
741         else
742             *stretch=INT_MAX;
743     }
744 }
745
746
747 static void calc_amount(int *amount, int rs, WSplit *other, int dir)
748 {
749     int shrink, stretch;
750     
751     flexibility(other, dir, &shrink, &stretch);
752
753     if(rs>0)
754         *amount=minof(rs, shrink);
755     else if(rs<0)
756         *amount=-minof(-rs, stretch);
757     else
758         *amount=0;
759 }
760
761
762
763 static void splitsplit_do_rqsize(WSplitSplit *p, WSplit *node, 
764                                  RootwardAmount *ha, RootwardAmount *va, 
765                                  WRectangle *rg, bool tryonly)
766 {
767     WPrimn hprimn=PRIMN_ANY, vprimn=PRIMN_ANY;
768     WRectangle og, pg, ng;
769     RootwardAmount *ca;
770     WSplit *other;
771     WPrimn thisnode;
772     int amount;
773     
774     assert(!ha->any || ha->tl==0);
775     assert(!va->any || va->tl==0);
776     assert(p->tl==node || p->br==node);
777     
778     if(p->tl==node){
779         other=p->br;
780         thisnode=PRIMN_TL;
781     }else{
782         other=p->tl;
783         thisnode=PRIMN_BR;
784     }
785
786     ca=(p->dir==SPLIT_VERTICAL ? va : ha);
787
788     if(thisnode==PRIMN_TL || ca->any){
789         calc_amount(&amount, ca->br, other, p->dir);
790         ca->br-=amount;
791     }else/*if(thisnode==PRIMN_BR)*/{
792         calc_amount(&amount, ca->tl, other, p->dir);
793         ca->tl-=amount;
794     }
795     
796     if(((WSplit*)p)->parent==NULL /*|| 
797        (ha->tl==0 && ha->br==0 && va->tl==0 && va->br==0)*/){
798         if(((WSplit*)p)->ws_if_root!=NULL)
799             pg=REGION_GEOM((WTiling*)(((WSplit*)p)->ws_if_root));
800         else
801             pg=((WSplit*)p)->geom;
802     }else{
803         splitinner_do_rqsize(((WSplit*)p)->parent, (WSplit*)p, ha, va,
804                              &pg, tryonly);
805     }
806     
807     assert(pg.w>=0 && pg.h>=0);
808
809     og=pg;
810     ng=pg;
811
812     if(p->dir==SPLIT_VERTICAL){
813         ng.h=maxof(0, node->geom.h+amount);
814         og.h=maxof(0, other->geom.h-amount);
815         adjust_sizes(&(ng.h), &(og.h), pg.h, ng.h+og.h,
816                      node->min_h, other->min_h, node->max_h, other->max_h, 
817                      PRIMN_TL /* node is passed as tl param */);
818         if(thisnode==PRIMN_TL)
819             og.y=pg.y+pg.h-og.h;
820         else
821             ng.y=pg.y+pg.h-ng.h;
822         vprimn=thisnode;
823     }else{
824         ng.w=maxof(0, node->geom.w+amount);
825         og.w=maxof(0, other->geom.w-amount);
826         adjust_sizes(&(ng.w), &(og.w), pg.w, ng.w+og.w,
827                      node->min_w, other->min_w, node->max_w, other->max_w, 
828                      PRIMN_TL /* node is passed as tl param */);
829         if(thisnode==PRIMN_TL)
830             og.x=pg.x+pg.w-og.w;
831         else
832             ng.x=pg.x+pg.w-ng.w;
833         hprimn=thisnode;
834     }
835     
836     if(!tryonly){
837         /* Entä jos 'other' on stdisp? */
838         split_do_resize(other, &og, hprimn, vprimn, FALSE);
839         
840         ((WSplit*)p)->geom=pg;
841     }
842     
843     *rg=ng;
844 }
845
846
847 void splitinner_do_rqsize(WSplitInner *p, WSplit *node, 
848                           RootwardAmount *ha, RootwardAmount *va, 
849                           WRectangle *rg, bool tryonly)
850 {
851     CALL_DYN(splitinner_do_rqsize, p, (p, node, ha, va, rg, tryonly));
852 }
853
854
855 static void initra(RootwardAmount *ra, int p, int s, int op, int os, 
856                    bool any)
857 {
858     ra->any=any;
859     ra->tl=op-p;
860     ra->br=(p+s)-(op+os);
861     if(any){
862         ra->br+=ra->tl;
863         ra->tl=0;
864     }
865 }
866
867
868 void split_do_rqgeom_(WSplit *node, const WRectangle *ng, 
869                       bool hany, bool vany, WRectangle *rg, 
870                       bool tryonly)
871 {
872     RootwardAmount ha, va;
873
874     if(node->parent==NULL){
875         if(node->ws_if_root!=NULL)
876             *rg=REGION_GEOM((WTiling*)(node->ws_if_root));
877         else
878             *rg=*ng;
879     }else{
880         initra(&ha, ng->x, ng->w, node->geom.x, node->geom.w, hany);
881         initra(&va, ng->y, ng->h, node->geom.y, node->geom.h, vany);
882     
883         splitinner_do_rqsize(node->parent, node, &ha, &va, rg, tryonly);
884     }
885 }
886
887
888 /*}}}*/
889
890
891 /*{{{ Resize interface */
892
893
894 static void bnd(int *pos, int *sz, int opos, int osz, int minsz, int maxsz)
895 {
896     int ud=abs(*pos-opos);
897     int dd=abs((*pos+*sz)-(opos+osz));
898     int szrq=*sz;
899     
900     if(ud+dd!=0){
901         bound(sz, minsz, maxsz);
902         *pos+=(szrq-*sz)*ud/(ud+dd);
903     }
904 }
905
906
907 WSplit *split_find_root(WSplit *split)
908 {
909     if(split->parent==NULL)
910         return split;
911     return split_find_root((WSplit*)split->parent);
912 }
913
914
915 void splittree_rqgeom(WSplit *sub, int flags, const WRectangle *geom_, 
916                       WRectangle *geomret)
917 {
918     bool hany=flags&REGION_RQGEOM_WEAK_X;
919     bool vany=flags&REGION_RQGEOM_WEAK_Y;
920     bool tryonly=flags&REGION_RQGEOM_TRYONLY;
921     WRectangle geom=*geom_;
922     WRectangle retg;
923     WSplit *root=split_find_root(sub);
924     
925     if(geomret==NULL)
926         geomret=&retg;
927
928     split_update_bounds(root, TRUE);
929     
930     if(OBJ_IS(sub, WSplitST)){
931         WSplitST *sub_as_stdisp=(WSplitST*)sub;
932         
933         if(flags&REGION_RQGEOM_TRYONLY){
934             warn(TR("REGION_RQGEOM_TRYONLY unsupported for status display."));
935             *geomret=sub->geom;
936             return;
937         }
938         split_regularise_stdisp(sub_as_stdisp);
939         geom=sub->geom;
940         if(sub_as_stdisp->orientation==REGION_ORIENTATION_HORIZONTAL){
941             if(geom_->h==geom.h)
942                 return;
943             geom.h=geom_->h;
944         }else{
945             if(geom_->w==geom.w)
946                 return;
947             geom.w=geom_->w;
948         }
949         split_update_bounds(root, TRUE);
950     }
951
952     /* Handle internal size bounds */
953     bnd(&(geom.x), &(geom.w), sub->geom.x, sub->geom.w, 
954         sub->min_w, sub->max_w);
955     bnd(&(geom.y), &(geom.h), sub->geom.y, sub->geom.h, 
956         sub->min_h, sub->max_h);
957
958     /* Check if we should resize to both tl and br */
959     
960     if(hany){
961         geom.w+=sub->geom.x-geom.x;
962         geom.x=sub->geom.x;
963     }
964
965     if(vany){
966         geom.h+=sub->geom.y-geom.y;
967         geom.y=sub->geom.y;
968     }
969     
970     splittree_begin_resize();
971     
972     split_do_rqgeom_(sub, &geom, hany, vany, geomret, tryonly);
973     
974     if(!tryonly){
975         split_do_resize(sub, geomret, hany, vany, FALSE);
976         splittree_end_resize();
977         *geomret=sub->geom;
978     }else{
979         saw_stdisp=NULL;
980     }
981 }
982
983
984 /*EXTL_DOC
985  * Attempt to resize and/or move the split tree starting at \var{node}.
986  * Behaviour and the \var{g} parameter are as for \fnref{WRegion.rqgeom} 
987  * operating on \var{node} (if it were a \type{WRegion}).
988  */
989 EXTL_EXPORT_MEMBER
990 ExtlTab split_rqgeom(WSplit *node, ExtlTab g)
991 {
992     WRectangle geom, ogeom;
993     int flags=REGION_RQGEOM_WEAK_ALL;
994         
995     geom=node->geom;
996     ogeom=geom;
997
998     if(extl_table_gets_i(g, "x", &(geom.x)))
999         flags&=~REGION_RQGEOM_WEAK_X;
1000     if(extl_table_gets_i(g, "y", &(geom.y)))
1001         flags&=~REGION_RQGEOM_WEAK_Y;
1002     if(extl_table_gets_i(g, "w", &(geom.w)))
1003         flags&=~REGION_RQGEOM_WEAK_W;
1004     if(extl_table_gets_i(g, "h", &(geom.h)))
1005         flags&=~REGION_RQGEOM_WEAK_H;
1006     
1007     geom.w=maxof(1, geom.w);
1008     geom.h=maxof(1, geom.h);
1009
1010     splittree_rqgeom(node, flags, &geom, &ogeom);
1011     
1012     return extl_table_from_rectangle(&ogeom);
1013     
1014 err:
1015     warn(TR("Invalid node."));
1016     return extl_table_none();
1017 }
1018
1019
1020 /*}}}*/
1021
1022
1023 /*{{{ Split */
1024
1025
1026 void splittree_changeroot(WSplit *root, WSplit *node)
1027 {
1028     WTiling *ws=(WTiling*)(root->ws_if_root);
1029     
1030     assert(ws!=NULL);
1031     assert(ws->split_tree==root);
1032     root->ws_if_root=NULL;
1033     ws->split_tree=node;
1034     if(node!=NULL){
1035         node->ws_if_root=ws;
1036         node->parent=NULL;
1037     }
1038 }
1039
1040
1041 static void splitsplit_replace(WSplitSplit *split, WSplit *child,
1042                                WSplit *what)
1043 {
1044     assert(split->tl==child || split->br==child);
1045     
1046     if(split->tl==child)
1047         split->tl=what;
1048     else
1049         split->br=what;
1050
1051     child->parent=NULL;
1052     
1053     what->parent=(WSplitInner*)split;
1054     what->ws_if_root=NULL; /* May not be needed. */
1055 }
1056
1057
1058 void splitinner_replace(WSplitInner *split, WSplit *child, WSplit *what)
1059 {
1060     CALL_DYN(splitinner_replace, split, (split, child, what));
1061 }
1062
1063
1064 WSplitRegion *splittree_split(WSplit *node, int dir, WPrimn primn,
1065                               int minsize, WRegionSimpleCreateFn *fn, 
1066                               WWindow *parent)
1067 {
1068     int objmin, objmax;
1069     int s, sn, so, pos;
1070     WSplitSplit *nsplit;
1071     WSplitRegion *nnode;
1072     WSplitInner *psplit;
1073     WRegion *nreg;
1074     WFitParams fp;
1075     WRectangle ng, rg;
1076     
1077     assert(node!=NULL && parent!=NULL);
1078     
1079     if(OBJ_IS(node, WSplitST)){
1080         warn(TR("Splitting the status display is not allowed."));
1081         return NULL;
1082     }
1083
1084     splittree_begin_resize();
1085     
1086     if(!move_stdisp_out_of_way(node))
1087         return NULL;
1088
1089     if(primn!=PRIMN_TL && primn!=PRIMN_BR)
1090         primn=PRIMN_BR;
1091     if(dir!=SPLIT_HORIZONTAL && dir!=SPLIT_VERTICAL)
1092         dir=SPLIT_VERTICAL;
1093
1094     split_update_bounds(split_find_root(node), TRUE);
1095     objmin=(dir==SPLIT_VERTICAL ? node->min_h : node->min_w);
1096
1097     s=split_size(node, dir);
1098     sn=maxof(minsize, s/2);
1099     so=maxof(objmin, s-sn);
1100
1101     if(sn+so!=s){
1102         int rs;
1103         ng=node->geom;
1104         if(dir==SPLIT_VERTICAL)
1105             ng.h=sn+so;
1106         else
1107             ng.w=sn+so;
1108         split_do_rqgeom_(node, &ng, TRUE, TRUE, &rg, TRUE);
1109         rs=(dir==SPLIT_VERTICAL ? rg.h : rg.w);
1110         if(rs<minsize+objmin){
1111             warn(TR("Unable to split: not enough free space."));
1112             return NULL;
1113         }
1114         split_do_rqgeom_(node, &ng, TRUE, TRUE, &rg, FALSE);
1115         rs=(dir==SPLIT_VERTICAL ? rg.h : rg.w);
1116         if(minsize>rs/2){
1117             sn=minsize;
1118             so=rs-sn;
1119         }else{
1120             so=maxof(rs/2, objmin);
1121             sn=rs-so;
1122         }
1123     }else{
1124         rg=node->geom;
1125         splittree_scan_stdisp_rootward(node);
1126     }
1127
1128     /* Create split and new window
1129      */
1130     fp.mode=REGION_FIT_EXACT;
1131     fp.g=rg;
1132     
1133     nsplit=create_splitsplit(&(fp.g), dir);
1134     
1135     if(nsplit==NULL)
1136         return NULL;
1137
1138     if(dir==SPLIT_VERTICAL){
1139         if(primn==PRIMN_BR)
1140             fp.g.y+=so;
1141         fp.g.h=sn;
1142     }else{
1143         if(primn==PRIMN_BR)
1144             fp.g.x+=so;
1145         fp.g.w=sn;
1146     }
1147     
1148     nreg=fn(parent, &fp);
1149     
1150     if(nreg==NULL){
1151         destroy_obj((Obj*)nsplit);
1152         return NULL;
1153     }
1154
1155     nnode=create_splitregion(&(fp.g), nreg);
1156     if(nnode==NULL){
1157         destroy_obj((Obj*)nreg);
1158         destroy_obj((Obj*)nsplit);
1159         return NULL;
1160     }
1161     
1162     /* Now that everything's ok, resize and move original node.
1163      */    
1164     ng=rg;
1165     if(dir==SPLIT_VERTICAL){
1166         ng.h=so;
1167         if(primn==PRIMN_TL)
1168             ng.y+=sn;
1169     }else{
1170         ng.w=so;
1171         if(primn==PRIMN_TL)
1172             ng.x+=sn;
1173     }
1174
1175     split_do_resize(node, &ng, 
1176                     (dir==SPLIT_HORIZONTAL ? primn : PRIMN_ANY),
1177                     (dir==SPLIT_VERTICAL ? primn : PRIMN_ANY),
1178                     FALSE);
1179
1180     /* Set up split structure
1181      */
1182     psplit=node->parent;
1183     
1184     if(psplit!=NULL)
1185         splitinner_replace(psplit, node, (WSplit*)nsplit);
1186     else
1187         splittree_changeroot(node, (WSplit*)nsplit);
1188         
1189     node->parent=(WSplitInner*)nsplit;
1190     ((WSplit*)nnode)->parent=(WSplitInner*)nsplit;
1191     
1192     if(primn==PRIMN_BR){
1193         nsplit->tl=node;
1194         nsplit->br=(WSplit*)nnode;
1195         nsplit->current=SPLIT_CURRENT_TL;
1196     }else{
1197         nsplit->tl=(WSplit*)nnode;
1198         nsplit->br=node;
1199         nsplit->current=SPLIT_CURRENT_BR;
1200     }
1201     
1202     splittree_end_resize();
1203     
1204     return nnode;
1205 }
1206
1207
1208 /*}}}*/
1209
1210
1211 /*{{{ Remove */
1212
1213
1214 static void splitsplit_remove(WSplitSplit *node, WSplit *child, 
1215                               bool reclaim_space)
1216 {
1217     static int nstdisp=0;
1218     WSplitInner *parent;
1219     WSplit *other;
1220     
1221     assert(node->tl==child || node->br==child);
1222     
1223     if(node->tl==child)
1224         other=node->br;
1225     else
1226         other=node->tl;
1227     
1228     assert(other!=NULL);
1229
1230     if(nstdisp==0 && reclaim_space && OBJ_IS(other, WSplitST)){
1231         /* Try to move stdisp out of the way. */
1232         split_try_unsink_stdisp(node, FALSE, TRUE);
1233         assert(child->parent!=NULL);
1234         nstdisp++;
1235         splitinner_remove(child->parent, child, reclaim_space);
1236         nstdisp--;
1237         return;
1238     }
1239
1240     parent=((WSplit*)node)->parent;
1241     
1242     if(parent!=NULL)
1243         splitinner_replace(parent, (WSplit*)node, other);
1244     else
1245         splittree_changeroot((WSplit*)node, other);
1246     
1247     if(reclaim_space)
1248         split_resize(other, &(((WSplit*)node)->geom), PRIMN_ANY, PRIMN_ANY);
1249     
1250     child->parent=NULL;
1251     
1252     node->tl=NULL;
1253     node->br=NULL;
1254     ((WSplit*)node)->parent=NULL;
1255     destroy_obj((Obj*)node);
1256 }
1257
1258
1259 void splitinner_remove(WSplitInner *node, WSplit *child, bool reclaim_space)
1260 {
1261     CALL_DYN(splitinner_remove, node, (node, child, reclaim_space));
1262 }
1263
1264
1265 void splittree_remove(WSplit *node, bool reclaim_space)
1266 {
1267     if(node->parent!=NULL)
1268         splitinner_remove(node->parent, node, reclaim_space);
1269     else if(node->ws_if_root!=NULL)
1270         splittree_changeroot(node, NULL);
1271     
1272     destroy_obj((Obj*)node);
1273 }
1274
1275
1276 /*}}}*/
1277
1278
1279 /*{{{ Tree traversal */
1280
1281
1282 static bool defaultfilter(WSplit *node)
1283 {
1284     return (OBJ_IS(node, WSplitRegion) && 
1285             ((WSplitRegion*)node)->reg!=NULL);
1286 }
1287
1288
1289 static WSplit *split_current_todir_default(WSplit *node, 
1290                                            WPrimn hprimn, WPrimn vprimn,
1291                                            WSplitFilter *filter)
1292 {
1293     if(filter==NULL)
1294         filter=defaultfilter;
1295
1296     return (filter(node) ? node : NULL);
1297 }
1298
1299
1300 static WSplit *splitsplit_current_todir(WSplitSplit *node, 
1301                                         WPrimn hprimn, WPrimn vprimn,
1302                                         WSplitFilter *filter)
1303 {
1304     WPrimn primn=(node->dir==SPLIT_HORIZONTAL ? hprimn : vprimn);
1305     WSplit *first, *second, *ret;
1306     
1307     if(primn==PRIMN_TL ||
1308        (primn==PRIMN_ANY && node->current==SPLIT_CURRENT_TL)){
1309         first=node->tl;
1310         second=node->br;
1311     }else if(primn==PRIMN_BR ||
1312        (primn==PRIMN_ANY && node->current==SPLIT_CURRENT_BR)){
1313         first=node->br;
1314         second=node->tl;
1315     }else{
1316         return NULL;
1317     }
1318         
1319     ret=split_current_todir(first, hprimn, vprimn, filter);
1320     if(ret==NULL)
1321         ret=split_current_todir(second, hprimn, vprimn, filter);
1322     if(ret==NULL && filter!=NULL){
1323         if(filter((WSplit*)node))
1324             ret=(WSplit*)node;
1325     }
1326         
1327     return ret;
1328 }
1329
1330
1331 WSplit *split_current_todir(WSplit *node, WPrimn hprimn, WPrimn vprimn,
1332                             WSplitFilter *filter)
1333 {
1334     WSplit *ret=NULL;
1335     CALL_DYN_RET(ret, WSplit*, split_current_todir, node, 
1336                  (node, hprimn, vprimn, filter));
1337     return ret;
1338 }
1339
1340
1341 /* Note: both hprimn and vprimn are inverted when descending.  Therefore
1342  * one should be either PRIMN_NONE or PRIMN_ANY for sensible geometric 
1343  * navigation. (Both are PRIMN_TL or PRIMN_BR for pseudo-linear 
1344  * next/previous navigation.)
1345  */
1346 WSplit *splitsplit_nextto(WSplitSplit *node, WSplit *child,
1347                           WPrimn hprimn, WPrimn vprimn, 
1348                           WSplitFilter *filter)
1349 {
1350     WPrimn primn=(node->dir==SPLIT_HORIZONTAL ? hprimn : vprimn);
1351     WSplit *split=NULL, *nnode=NULL;
1352     
1353     if(node->tl==child && (primn==PRIMN_BR || primn==PRIMN_ANY))
1354         split=node->br;
1355     else if(node->br==child && (primn==PRIMN_TL || primn==PRIMN_ANY))
1356         split=node->tl;
1357     
1358     if(split!=NULL){
1359         nnode=split_current_todir(split, 
1360                                   primn_none2any(primn_invert(hprimn)),
1361                                   primn_none2any(primn_invert(vprimn)),
1362                                   filter);
1363     }
1364     
1365     if(nnode==NULL)
1366         nnode=split_nextto((WSplit*)node, hprimn, vprimn, filter);
1367         
1368     return nnode;
1369 }
1370
1371
1372 WSplit *splitinner_nextto(WSplitInner *node, WSplit *child,
1373                           WPrimn hprimn, WPrimn vprimn, 
1374                           WSplitFilter *filter)
1375 {
1376     WSplit *ret=NULL;
1377     CALL_DYN_RET(ret, WSplit*, splitinner_nextto, node, 
1378                  (node, child, hprimn, vprimn, filter));
1379     return ret;
1380 }
1381
1382
1383 WSplit *split_nextto(WSplit *node, WPrimn hprimn, WPrimn vprimn, 
1384                      WSplitFilter *filter)
1385 {
1386     while(node->parent!=NULL){
1387         WSplit *ret=splitinner_nextto(node->parent, node, 
1388                                       hprimn, vprimn, filter);
1389         if(ret!=NULL)
1390             return ret;
1391         node=(WSplit*)node->parent;
1392     }
1393     return NULL;
1394 }
1395
1396
1397 void splitinner_mark_current_default(WSplitInner *split, WSplit *child)
1398 {
1399     if(((WSplit*)split)->parent!=NULL)
1400         splitinner_mark_current(((WSplit*)split)->parent, (WSplit*)split);
1401 }
1402
1403
1404 void splitsplit_mark_current(WSplitSplit *split, WSplit *child)
1405 {
1406     assert(child==split->tl || child==split->br);
1407     
1408     split->current=(split->tl==child ? SPLIT_CURRENT_TL : SPLIT_CURRENT_BR);
1409     
1410     splitinner_mark_current_default(&(split->isplit), child);
1411 }
1412
1413
1414 void splitinner_mark_current(WSplitInner *split, WSplit *child)
1415 {
1416     CALL_DYN(splitinner_mark_current, split, (split, child));
1417 }
1418
1419
1420 static void splitsplit_forall(WSplitSplit *node, WSplitFn *fn)
1421 {
1422     fn(node->tl);
1423     fn(node->br);
1424 }
1425
1426
1427 void splitinner_forall(WSplitInner *node, WSplitFn *fn)
1428 {
1429     CALL_DYN(splitinner_forall, node, (node, fn));
1430 }
1431
1432
1433 static WSplit *splitsplit_current(WSplitSplit *split)
1434 {
1435     return (split->current==SPLIT_CURRENT_TL ? split->tl : split->br);
1436 }
1437
1438
1439 /*EXTL_DOC
1440  * Returns the most previously active child node of \var{split}.
1441  */
1442 EXTL_SAFE
1443 EXTL_EXPORT_MEMBER
1444 WSplit *splitinner_current(WSplitInner *node)
1445 {
1446     WSplit *ret=NULL;
1447     CALL_DYN_RET(ret, WSplit*, splitinner_current, node, (node));
1448     return ret;
1449 }
1450     
1451
1452 /*}}}*/
1453
1454
1455 /*{{{ X window handling */
1456
1457
1458 static void splitregion_stacking(WSplitRegion *split, 
1459                                  Window *bottomret, Window *topret)
1460 {
1461     *bottomret=None;
1462     *topret=None;
1463     if(split->reg!=NULL)
1464         region_stacking(split->reg, bottomret, topret);
1465 }
1466
1467
1468 void splitsplit_stacking(WSplitSplit *split, 
1469                          Window *bottomret, Window *topret)
1470 {
1471     Window tlb=None, tlt=None;
1472     Window brb=None, brt=None;
1473     
1474     split_stacking(split->tl, &tlb, &tlt);
1475     split_stacking(split->br, &brb, &brt);
1476     
1477     /* To make sure that this condition holds is left to the workspace
1478      * code to do after a split tree has been loaded or modified.
1479      */
1480     if(split->current==SPLIT_CURRENT_TL){
1481         *topret=(tlt!=None ? tlt : brt);
1482         *bottomret=(brb!=None ? brb : tlb);
1483     }else{
1484         *topret=(brt!=None ? brt : tlt);
1485         *bottomret=(tlb!=None ? tlb : brb);
1486     }
1487 }
1488
1489 void split_stacking(WSplit *split, Window *bottomret, Window *topret)
1490 {
1491     *bottomret=None;
1492     *topret=None;
1493     {
1494         CALL_DYN(split_stacking, split, (split, bottomret, topret));
1495     }
1496 }
1497
1498
1499 static void splitregion_restack(WSplitRegion *split, Window other, int mode)
1500 {
1501     if(split->reg!=NULL)
1502         region_restack(split->reg, other, mode);
1503 }
1504
1505 void splitsplit_restack(WSplitSplit *split, Window other, int mode)
1506 {
1507     Window bottom=None, top=None;
1508     WSplit *first, *second;
1509     
1510     if(split->current==SPLIT_CURRENT_TL){
1511         first=split->br;
1512         second=split->tl;
1513     }else{
1514         first=split->tl;
1515         second=split->br;
1516     }
1517     
1518     split_restack(first, other, mode);
1519     split_stacking(first, &bottom, &top);
1520     if(top!=None){
1521         other=top;
1522         mode=Above;
1523     }
1524     split_restack(second, other, mode);
1525 }
1526
1527 void split_restack(WSplit *split, Window other, int mode)
1528 {
1529     CALL_DYN(split_restack, split, (split, other, mode));
1530 }
1531
1532
1533 static void splitregion_map(WSplitRegion *split)
1534 {
1535     if(split->reg!=NULL)
1536         region_map(split->reg);
1537 }
1538
1539 static void splitinner_map(WSplitInner *split)
1540 {
1541     splitinner_forall(split, split_map);
1542 }
1543
1544 void split_map(WSplit *split)
1545 {
1546     CALL_DYN(split_map, split, (split));
1547 }
1548
1549
1550 static void splitregion_unmap(WSplitRegion *split)
1551 {
1552     if(split->reg!=NULL)
1553         region_unmap(split->reg);
1554 }
1555
1556 static void splitinner_unmap(WSplitInner *split)
1557 {
1558     splitinner_forall(split, split_unmap);
1559 }
1560
1561 void split_unmap(WSplit *split)
1562 {
1563     CALL_DYN(split_unmap, split, (split));
1564 }
1565
1566
1567 static void splitregion_reparent(WSplitRegion *split, WWindow *wwin)
1568 {
1569     if(split->reg!=NULL){
1570         WRectangle g=split->split.geom;
1571         region_reparent(split->reg, wwin, &g, REGION_FIT_EXACT);
1572     }
1573 }
1574
1575
1576 static void splitsplit_reparent(WSplitSplit *split, WWindow *wwin)
1577 {
1578     if(split->current==SPLIT_CURRENT_TL){
1579         split_reparent(split->br, wwin);
1580         split_reparent(split->tl, wwin);
1581     }else{
1582         split_reparent(split->tl, wwin);
1583         split_reparent(split->br, wwin);
1584     }
1585 }
1586
1587
1588 void split_reparent(WSplit *split, WWindow *wwin)
1589 {
1590     CALL_DYN(split_reparent, split, (split, wwin));
1591 }
1592
1593
1594 /*}}}*/
1595
1596
1597 /*{{{ Transpose, flip, rotate */
1598
1599
1600 void splitsplit_flip_default(WSplitSplit *split)
1601 {
1602     WRectangle tlng, brng;
1603     WRectangle *sg=&((WSplit*)split)->geom;
1604     WSplit *tmp;
1605     
1606     assert(split->tl!=NULL && split->br!=NULL);
1607     
1608     split_update_bounds((WSplit*)split, TRUE);
1609
1610     tlng=split->tl->geom;
1611     brng=split->br->geom;
1612     
1613     if(split->dir==SPLIT_HORIZONTAL){
1614         brng.x=sg->x;
1615         tlng.x=sg->x+sg->w-tlng.w;
1616     }else{
1617         brng.y=sg->y;
1618         tlng.y=sg->y+sg->h-tlng.h;
1619     }
1620     
1621     tmp=split->tl;
1622     split->tl=split->br;
1623     split->br=tmp;
1624     split->current=(split->current==SPLIT_CURRENT_TL
1625                     ? SPLIT_CURRENT_BR
1626                     : SPLIT_CURRENT_TL);
1627
1628     split_do_resize(split->tl, &brng, PRIMN_ANY, PRIMN_ANY, FALSE);
1629     split_do_resize(split->br, &tlng, PRIMN_ANY, PRIMN_ANY, FALSE);
1630 }
1631
1632
1633 static void splitsplit_flip_(WSplitSplit *split)
1634 {
1635     CALL_DYN(splitsplit_flip, split, (split));
1636 }
1637
1638
1639 /*EXTL_DOC
1640  * Flip contents of \var{node}.
1641  */
1642 EXTL_EXPORT_MEMBER
1643 void splitsplit_flip(WSplitSplit *split)
1644 {
1645     splittree_begin_resize();
1646
1647     if(!move_stdisp_out_of_way((WSplit*)split))
1648         return;
1649     
1650     splitsplit_flip_(split);
1651     
1652     splittree_end_resize();
1653 }
1654
1655 typedef enum{
1656     FLIP_VERTICAL,
1657     FLIP_HORIZONTAL,
1658     FLIP_NONE,
1659     FLIP_ANY
1660 } FlipDir;
1661
1662
1663 static FlipDir flipdir=FLIP_VERTICAL;
1664
1665
1666 static void do_flip(WSplit *split)
1667 {
1668     WSplitSplit *ss=OBJ_CAST(split, WSplitSplit);
1669     
1670     if(ss!=NULL){
1671         if((flipdir==FLIP_ANY
1672             || (ss->dir==SPLIT_VERTICAL && flipdir==FLIP_VERTICAL)
1673             || (ss->dir==SPLIT_HORIZONTAL && flipdir==FLIP_HORIZONTAL)) 
1674            && !OBJ_IS(ss->tl, WSplitST) 
1675            && !OBJ_IS(ss->br, WSplitST)){
1676             splitsplit_flip_(ss);
1677         }
1678     }
1679     
1680     if(OBJ_IS(ss, WSplitInner))
1681         splitinner_forall((WSplitInner*)ss, do_flip);
1682 }
1683
1684     
1685 static void splittree_flip_dir(WSplit *splittree, FlipDir dir)
1686 {
1687     /* todo stdisp outta way */
1688     if(OBJ_IS(splittree, WSplitInner)){
1689         flipdir=dir;
1690         splitinner_forall((WSplitInner*)splittree, do_flip);
1691     }
1692 }
1693
1694
1695 static bool split_fliptrans_to(WSplit *node, const WRectangle *geom, 
1696                               bool trans, FlipDir flip)
1697 {
1698     WRectangle rg;
1699     WSplit *node2;
1700     
1701     splittree_begin_resize();
1702     
1703     /* split_do_resize can do things right if 'node' has stdisp as child, 
1704      * but otherwise transpose will put the stdisp in a bad split
1705      * configuration if it is contained within 'node', so we must
1706      * first move it and its fixed parent split below node. For correct
1707      * geometry calculation we move it immediately below node, and
1708      * resize stdisp's fixed parent node instead.
1709      */
1710     node2=move_stdisp_out_of_way(node);
1711     
1712     if(node2==NULL)
1713         return FALSE;
1714     
1715     split_update_bounds(node2, TRUE);
1716     
1717     split_do_rqgeom_(node2, geom, PRIMN_ANY, PRIMN_ANY, &rg, FALSE);
1718     
1719     split_do_resize(node2, &rg, PRIMN_ANY, PRIMN_ANY, trans);
1720     
1721     if(flip!=FLIP_NONE)
1722         splittree_flip_dir(node2, flip);
1723
1724     splittree_end_resize();
1725     
1726     return TRUE;
1727 }
1728
1729
1730 bool split_transpose_to(WSplit *node, const WRectangle *geom)
1731 {
1732     return split_fliptrans_to(node, geom, TRUE, FLIP_ANY);
1733 }
1734
1735
1736 /*EXTL_DOC
1737  * Transpose contents of \var{node}. 
1738  */
1739 EXTL_EXPORT_MEMBER
1740 void split_transpose(WSplit *node)
1741 {
1742     WRectangle g=node->geom;
1743     
1744     split_transpose_to(node, &g);
1745 }
1746
1747
1748 bool split_rotate_to(WSplit *node, const WRectangle *geom, int rotation)
1749 {
1750     FlipDir flip=FLIP_NONE;
1751     bool trans=FALSE;
1752     
1753     if(rotation==SCREEN_ROTATION_90){
1754         flip=FLIP_HORIZONTAL;
1755         trans=TRUE;
1756     }else if(rotation==SCREEN_ROTATION_180){
1757         flip=FLIP_ANY;
1758     }else if(rotation==SCREEN_ROTATION_270){
1759         flip=FLIP_VERTICAL;
1760         trans=TRUE;
1761     }
1762
1763     return split_fliptrans_to(node, geom, trans, flip);
1764 }
1765
1766 /*}}}*/
1767
1768
1769 /*{{{ Exports */
1770
1771
1772 /*EXTL_DOC
1773  * Return parent split for \var{split}.
1774  */
1775 EXTL_SAFE
1776 EXTL_EXPORT_MEMBER
1777 WSplitInner *split_parent(WSplit *split)
1778 {
1779     return split->parent;
1780 }
1781
1782
1783 /*EXTL_DOC
1784  * Returns the area of workspace used by the regions under \var{split}.
1785  */
1786 EXTL_SAFE
1787 EXTL_EXPORT_MEMBER
1788 ExtlTab split_geom(WSplit *split)
1789 {
1790     return extl_table_from_rectangle(&(split->geom));
1791 }
1792
1793
1794 /*EXTL_DOC
1795  * Returns the top or left child node of \var{split} depending
1796  * on the direction of the split.
1797  */
1798 EXTL_SAFE
1799 EXTL_EXPORT_MEMBER
1800 WSplit *splitsplit_tl(WSplitSplit *split)
1801 {
1802     return split->tl;
1803 }
1804
1805
1806 /*EXTL_DOC
1807  * Returns the bottom or right child node of \var{split} depending
1808  * on the direction of the split.
1809  */
1810 EXTL_SAFE
1811 EXTL_EXPORT_MEMBER
1812 WSplit *splitsplit_br(WSplitSplit *split)
1813 {
1814     return split->br;
1815 }
1816
1817 /*EXTL_DOC
1818  * Returns the direction of \var{split}; either \codestr{vertical} or
1819  * \codestr{horizontal}.
1820  */
1821 EXTL_SAFE
1822 EXTL_EXPORT_MEMBER
1823 const char *splitsplit_dir(WSplitSplit *split)
1824 {
1825     return (split->dir==SPLIT_VERTICAL ? "vertical" : "horizontal");
1826 }
1827
1828
1829 /*EXTL_DOC
1830  * Returns the region contained in \var{node}.
1831  */
1832 EXTL_SAFE
1833 EXTL_EXPORT_MEMBER
1834 WRegion *splitregion_reg(WSplitRegion *node)
1835 {
1836     return node->reg;
1837 }
1838     
1839
1840 /*}}}*/
1841
1842
1843 /*{{{ Save support */
1844
1845
1846 ExtlTab split_base_config(WSplit *node)
1847 {
1848     ExtlTab t=extl_create_table();
1849     extl_table_sets_s(t, "type", OBJ_TYPESTR(node));
1850     return t;
1851 }
1852
1853
1854 static bool splitregion_get_config(WSplitRegion *node, ExtlTab *ret)
1855 {
1856     ExtlTab rt, t;
1857     
1858     if(node->reg==NULL)
1859         return FALSE;
1860     
1861     if(!region_supports_save(node->reg)){
1862         warn(TR("Unable to get configuration for %s."), 
1863              region_name(node->reg));
1864         return FALSE;
1865     }
1866     
1867     rt=region_get_configuration(node->reg);
1868     t=split_base_config(&(node->split));
1869     extl_table_sets_t(t, "regparams", rt);
1870     extl_unref_table(rt);
1871     *ret=t;
1872     
1873     return TRUE;
1874 }
1875
1876
1877 static bool splitst_get_config(WSplitST *node, ExtlTab *ret)
1878 {
1879     *ret=split_base_config((WSplit*)node);
1880     return TRUE;
1881 }
1882
1883
1884 static bool splitsplit_get_config(WSplitSplit *node, ExtlTab *ret)
1885 {
1886     ExtlTab tab, tltab, brtab;
1887     int tls, brs;
1888     
1889     if(!split_get_config(node->tl, &tltab))
1890         return split_get_config(node->br, ret);
1891     
1892     if(!split_get_config(node->br, &brtab)){
1893         *ret=tltab;
1894         return TRUE;
1895     }
1896
1897     tab=split_base_config((WSplit*)node);
1898
1899     tls=split_size(node->tl, node->dir);
1900     brs=split_size(node->br, node->dir);
1901         
1902     extl_table_sets_s(tab, "dir", (node->dir==SPLIT_VERTICAL
1903                                    ? "vertical" : "horizontal"));
1904
1905     extl_table_sets_i(tab, "tls", tls);
1906     extl_table_sets_t(tab, "tl", tltab);
1907     extl_unref_table(tltab);
1908
1909     extl_table_sets_i(tab, "brs", brs);
1910     extl_table_sets_t(tab, "br", brtab);
1911     extl_unref_table(brtab);
1912         
1913     *ret=tab;
1914     
1915     return TRUE;
1916 }
1917
1918
1919 bool split_get_config(WSplit *node, ExtlTab *tabret)
1920 {
1921     bool ret=FALSE;
1922     CALL_DYN_RET(ret, bool, split_get_config, node, (node, tabret));
1923     return ret;
1924 }
1925
1926
1927 /*}}}*/
1928
1929
1930 /*{{{ The classes */
1931
1932
1933 static DynFunTab split_dynfuntab[]={
1934     {split_do_resize, split_do_resize_default},
1935     {(DynFun*)split_current_todir, (DynFun*)split_current_todir_default},
1936     END_DYNFUNTAB,
1937 };
1938
1939 static DynFunTab splitinner_dynfuntab[]={
1940     {splitinner_mark_current, splitinner_mark_current_default},
1941     {split_map, splitinner_map},
1942     {split_unmap, splitinner_unmap},
1943     END_DYNFUNTAB,
1944 };
1945
1946 static DynFunTab splitsplit_dynfuntab[]={
1947     {split_update_bounds, splitsplit_update_bounds},
1948     {split_do_resize, splitsplit_do_resize},
1949     {splitinner_do_rqsize, splitsplit_do_rqsize},
1950     {splitinner_replace, splitsplit_replace},
1951     {splitinner_remove, splitsplit_remove},
1952     {(DynFun*)split_current_todir, (DynFun*)splitsplit_current_todir},
1953     {(DynFun*)splitinner_current, (DynFun*)splitsplit_current},
1954     {(DynFun*)splitinner_nextto, (DynFun*)splitsplit_nextto},
1955     {splitinner_mark_current, splitsplit_mark_current},
1956     {(DynFun*)split_get_config, (DynFun*)splitsplit_get_config},
1957     {splitinner_forall, splitsplit_forall},
1958     {split_restack, splitsplit_restack},
1959     {split_stacking, splitsplit_stacking},
1960     {split_reparent, splitsplit_reparent},
1961     {splitsplit_flip, splitsplit_flip_default},
1962     END_DYNFUNTAB,
1963 };
1964
1965 static DynFunTab splitregion_dynfuntab[]={
1966     {split_update_bounds, splitregion_update_bounds},
1967     {split_do_resize, splitregion_do_resize},
1968     {(DynFun*)split_get_config, (DynFun*)splitregion_get_config},
1969     {split_map, splitregion_map},
1970     {split_unmap, splitregion_unmap},
1971     {split_restack, splitregion_restack},
1972     {split_stacking, splitregion_stacking},
1973     {split_reparent, splitregion_reparent},
1974     END_DYNFUNTAB,
1975 };
1976
1977 static DynFunTab splitst_dynfuntab[]={
1978     {split_update_bounds, splitst_update_bounds},
1979     {split_do_resize, splitst_do_resize},
1980     {(DynFun*)split_get_config, (DynFun*)splitst_get_config},
1981     END_DYNFUNTAB,
1982 };
1983
1984
1985 EXTL_EXPORT
1986 IMPLCLASS(WSplit, Obj, split_deinit, split_dynfuntab);
1987
1988 EXTL_EXPORT
1989 IMPLCLASS(WSplitInner, WSplit, splitinner_deinit, splitinner_dynfuntab);
1990
1991 EXTL_EXPORT
1992 IMPLCLASS(WSplitSplit, WSplitInner, splitsplit_deinit, splitsplit_dynfuntab);
1993
1994 EXTL_EXPORT
1995 IMPLCLASS(WSplitRegion, WSplit, splitregion_deinit, splitregion_dynfuntab);
1996
1997 EXTL_EXPORT
1998 IMPLCLASS(WSplitST, WSplitRegion, splitst_deinit, splitst_dynfuntab);
1999
2000
2001 /*}}}*/
2002