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