2 * ion/mod_tiling/split.c
4 * Copyright (c) Tuomo Valkonen 1999-2007.
6 * See the included file LICENSE for details.
13 #include <libtu/minmax.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>
29 #include "split-stdisp.h"
32 static Rb_node split_of_map=NULL;
35 /*{{{ Geometry helper functions */
38 int split_size(WSplit *split, int dir)
40 return (dir==SPLIT_HORIZONTAL ? split->geom.w : split->geom.h);
43 int split_other_size(WSplit *split, int dir)
45 return (dir==SPLIT_VERTICAL ? split->geom.w : split->geom.h);
48 int split_pos(WSplit *split, int dir)
50 return (dir==SPLIT_HORIZONTAL ? split->geom.x : split->geom.y);
53 int split_other_pos(WSplit *split, int dir)
55 return (dir==SPLIT_VERTICAL ? split->geom.x : split->geom.y);
59 static int reg_calcresize(WRegion *reg, int dir, int nsize)
63 if(dir==SPLIT_HORIZONTAL)
64 tmp=region_min_w(reg);
66 tmp=region_min_h(reg);
68 return (nsize<tmp ? tmp : nsize);
72 /* No, these are not even supposed to be proper/consistent
73 * Z \cup {\infty, -\infty} calculation rules.
76 static int infadd(int x, int y)
78 if(x==INT_MAX || y==INT_MAX)
85 static int infsub(int x, int y)
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
100 static int unusedadd(int x, int y)
104 return maxof(x, 0)+maxof(y, 0);
108 static void bound(int *what, int min, int max)
120 /*{{{ Functions to get and set a region's containing node */
123 #define node_of_reg splittree_node_of
125 WSplitRegion *splittree_node_of(WRegion *reg)
130 /*assert(REGION_MANAGER_CHK(reg, WTiling)!=NULL);*/
132 if(split_of_map!=NULL){
133 node=rb_find_pkey_n(split_of_map, reg, &found);
135 return (WSplitRegion*)(node->v.val);
142 #define set_node_of_reg splittree_set_node_of
145 bool splittree_set_node_of(WRegion *reg, WSplitRegion *split)
150 /*assert(REGION_MANAGER_CHK(reg, WTiling)!=NULL);*/
152 if(split_of_map==NULL){
155 split_of_map=make_rb();
156 if(split_of_map==NULL)
160 node=rb_find_pkey_n(split_of_map, reg, &found);
162 rb_delete_node(node);
164 return (rb_insertp(split_of_map, reg, split)!=NULL);
174 WPrimn primn_invert(WPrimn primn)
176 return (primn==PRIMN_TL
184 WPrimn primn_none2any(WPrimn primn)
186 return (primn==PRIMN_NONE ? PRIMN_ANY : primn);
196 bool split_init(WSplit *split, const WRectangle *geom)
199 split->ws_if_root=NULL;
203 split->max_w=INT_MAX;
204 split->max_h=INT_MAX;
210 bool splitinner_init(WSplitInner *split, const WRectangle *geom)
212 return split_init(&(split->split), geom);
216 bool splitsplit_init(WSplitSplit *split, const WRectangle *geom, int dir)
218 splitinner_init(&(split->isplit), geom);
222 split->current=SPLIT_CURRENT_TL;
227 bool splitregion_init(WSplitRegion *split, const WRectangle *geom,
230 split_init(&(split->split), geom);
233 set_node_of_reg(reg, split);
238 bool splitst_init(WSplitST *split, const WRectangle *geom, WRegion *reg)
240 splitregion_init(&(split->regnode), geom, reg);
241 split->orientation=REGION_ORIENTATION_HORIZONTAL;
242 split->corner=MPLEX_STDISP_BL;
247 WSplitSplit *create_splitsplit(const WRectangle *geom, int dir)
249 CREATEOBJ_IMPL(WSplitSplit, splitsplit, (p, geom, dir));
253 WSplitRegion *create_splitregion(const WRectangle *geom, WRegion *reg)
255 CREATEOBJ_IMPL(WSplitRegion, splitregion, (p, geom, reg));
259 WSplitST *create_splitst(const WRectangle *geom, WRegion *reg)
261 CREATEOBJ_IMPL(WSplitST, splitst, (p, geom, reg));
271 void split_deinit(WSplit *split)
273 assert(split->parent==NULL);
277 void splitinner_deinit(WSplitInner *split)
279 split_deinit(&(split->split));
283 void splitsplit_deinit(WSplitSplit *split)
286 split->tl->parent=NULL;
287 destroy_obj((Obj*)(split->tl));
290 split->br->parent=NULL;
291 destroy_obj((Obj*)(split->br));
294 splitinner_deinit(&(split->isplit));
298 void splitregion_deinit(WSplitRegion *split)
300 if(split->reg!=NULL){
301 set_node_of_reg(split->reg, NULL);
305 split_deinit(&(split->split));
309 void splitst_deinit(WSplitST *split)
311 splitregion_deinit(&(split->regnode));
318 /*{{{ Size bounds management */
321 static void splitregion_update_bounds(WSplitRegion *node, bool recursive)
324 WSplit *snode=(WSplit*)node;
326 assert(node->reg!=NULL);
328 region_size_hints(node->reg, &hints);
330 snode->min_w=maxof(1, hints.min_set ? hints.min_width : 1);
331 snode->max_w=INT_MAX;
334 snode->min_h=maxof(1, hints.min_set ? hints.min_height : 1);
335 snode->max_h=INT_MAX;
340 static void splitst_update_bounds(WSplitST *node, bool rec)
342 WSplit *snode=(WSplit*)node;
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;
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);
361 if(node->orientation==REGION_ORIENTATION_HORIZONTAL){
362 snode->min_w=CF_STDISP_MIN_SZ;
363 snode->max_w=INT_MAX;
365 snode->min_h=CF_STDISP_MIN_SZ;
366 snode->max_h=INT_MAX;
371 static void splitsplit_update_bounds(WSplitSplit *split, bool recursive)
374 WSplit *node=(WSplit*)split;
376 assert(split->tl!=NULL && split->br!=NULL);
382 split_update_bounds(tl, TRUE);
383 split_update_bounds(br, TRUE);
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);
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);
404 void split_update_bounds(WSplit *node, bool recursive)
406 CALL_DYN(split_update_bounds, node, (node, recursive));
410 void splitsplit_update_geom_from_children(WSplitSplit *node)
412 WSplit *split=(WSplit*)node;
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;
427 /*{{{ Status display handling helper functions. */
430 static WSplitST *saw_stdisp=NULL;
433 void splittree_begin_resize()
439 void splittree_end_resize()
441 if(saw_stdisp!=NULL){
442 split_regularise_stdisp(saw_stdisp);
448 static void splittree_scan_stdisp_rootward_(WSplitInner *node_)
450 WSplitSplit *node=OBJ_CAST(node_, WSplitSplit);
453 if(OBJ_IS(node->tl, WSplitST)){
454 saw_stdisp=(WSplitST*)(node->tl);
456 }else if(OBJ_IS(node->br, WSplitST)){
457 saw_stdisp=(WSplitST*)(node->br);
462 if(node_->split.parent!=NULL)
463 splittree_scan_stdisp_rootward_(node_->split.parent);
467 void splittree_scan_stdisp_rootward(WSplit *node)
469 if(node->parent!=NULL)
470 splittree_scan_stdisp_rootward_(node->parent);
474 static WSplitST *splittree_scan_stdisp(WSplit *node_, bool set_saw)
477 WSplitSplit *node=OBJ_CAST(node_, WSplitSplit);
482 r=OBJ_CAST(node->tl, WSplitST);
484 r=OBJ_CAST(node->br, WSplitST);
492 r=splittree_scan_stdisp(node->tl, set_saw);
494 r=splittree_scan_stdisp(node->br, set_saw);
500 static bool stdisp_immediate_child(WSplitSplit *node)
502 return (node!=NULL && (OBJ_IS(node->tl, WSplitST) ||
503 OBJ_IS(node->br, WSplitST)));
507 static WSplit *dodge_stdisp(WSplit *node, bool keep_within)
510 WSplitSplit *stdispp;
512 stdisp=splittree_scan_stdisp(node, TRUE);
517 stdispp=OBJ_CAST(((WSplit*)stdisp)->parent, WSplitSplit);
522 if((WSplit*)stdispp==node){
523 /* Node itself immediately contains stdisp. Due to the way
524 * try_unsink works, stdisp this will not change, so another
525 * node must be used, if we want to fully dodge stdisp.
529 : (stdispp->tl==(WSplit*)stdisp
535 if(!split_try_unsink_stdisp(stdispp, FALSE, TRUE)){
536 warn(TR("Unable to move the status display out of way."));
539 }while(stdispp->tl!=node && stdispp->br!=node);
548 /*{{{ Low-level resize code; from root to leaf */
551 static void split_do_resize_default(WSplit *node, const WRectangle *ng,
552 WPrimn hprimn, WPrimn vprimn,
559 static void splitregion_do_resize(WSplitRegion *node, const WRectangle *ng,
560 WPrimn hprimn, WPrimn vprimn,
563 assert(node->reg!=NULL);
564 region_fit(node->reg, ng, REGION_FIT_EXACT);
565 split_update_bounds(&(node->split), FALSE);
566 node->split.geom=*ng;
570 static void splitst_do_resize(WSplitST *node, const WRectangle *ng,
571 WPrimn hprimn, WPrimn vprimn,
576 if(node->regnode.reg==NULL){
577 ((WSplit*)node)->geom=*ng;
579 splitregion_do_resize(&(node->regnode), ng, hprimn, vprimn,
585 static int other_dir(int dir)
587 return (dir==SPLIT_VERTICAL ? SPLIT_HORIZONTAL : SPLIT_VERTICAL);
591 static void adjust_sizes(int *tls_, int *brs_, int nsize, int sz,
592 int tlmin, int brmin, int tlmax, int brmax,
600 bound(&tls, tlmin, tlmax);
602 bound(&brs, brmin, brmax);
604 bound(&tls, tlmin, tlmax);
605 }else if(primn==PRIMN_BR){
607 bound(&brs, brmin, brmax);
609 bound(&tls, tlmin, tlmax);
611 bound(&brs, brmin, brmax);
612 }else{ /* && PRIMN_ANY */
614 bound(&tls, tlmin, tlmax);
616 bound(&brs, brmin, brmax);
618 bound(&tls, tlmin, tlmax);
626 static void get_minmaxunused(WSplit *node, int dir,
627 int *min, int *max, int *unused)
629 if(dir==SPLIT_VERTICAL){
631 *max=maxof(*min, node->max_h);
632 *unused=minof(node->unused_h, node->geom.h);
635 *max=maxof(*min, node->max_w);
636 *unused=minof(node->unused_w, node->geom.w);
641 void splitsplit_do_resize(WSplitSplit *node, const WRectangle *ng,
642 WPrimn hprimn, WPrimn vprimn, bool transpose)
644 assert(ng->w>=0 && ng->h>=0);
645 assert(node->tl!=NULL && node->br!=NULL);
646 assert(!transpose || (hprimn==PRIMN_ANY && vprimn==PRIMN_ANY));
649 WSplit *tl=node->tl, *br=node->br;
650 int tls=split_size((WSplit*)tl, node->dir);
651 int brs=split_size((WSplit*)br, node->dir);
653 /* Status display can not be transposed. */
654 int dir=((transpose && !stdisp_immediate_child(node))
655 ? other_dir(node->dir)
657 int nsize=(dir==SPLIT_VERTICAL ? ng->h : ng->w);
658 int primn=(dir==SPLIT_VERTICAL ? vprimn : hprimn);
659 int tlmin, tlmax, tlunused, tlused;
660 int brmin, brmax, brunused, brused;
661 WRectangle tlg=*ng, brg=*ng;
663 get_minmaxunused(tl, dir, &tlmin, &tlmax, &tlunused);
664 get_minmaxunused(br, dir, &brmin, &brmax, &brunused);
666 tlused=maxof(0, tls-maxof(0, tlunused));
667 brused=maxof(0, brs-maxof(0, brunused));
668 /* tlmin, brmin >= 1 => (tls>=tlmin, brs>=brmin => sz>0) */
671 if(primn==PRIMN_ANY && (tlunused>=0 || brunused>=0)){
672 if(nsize<=tlused+brused){
673 /* Need to shrink a tangible node */
674 adjust_sizes(&tls, &brs, nsize, sz,
675 tlmin, brmin, tlused, brused, primn);
677 /* Just expand or shrink unused space */
678 adjust_sizes(&tls, &brs, nsize, sz,
680 (tlunused<0 ? tlused : tlmax),
681 (brunused<0 ? brused : brmax), primn);
685 adjust_sizes(&tls, &brs, nsize, sz,
686 tlmin, brmin, tlmax, brmax, primn);
691 /* Bad fit; just size proportionally. */
696 tls=split_size(tl, node->dir)*nsize/sz;
701 if(dir==SPLIT_VERTICAL){
711 split_do_resize(tl, &tlg, hprimn, vprimn, transpose);
712 split_do_resize(br, &brg, hprimn, vprimn, transpose);
715 ((WSplit*)node)->geom=*ng;
716 split_update_bounds((WSplit*)node, FALSE);
721 void split_do_resize(WSplit *node, const WRectangle *ng,
722 WPrimn hprimn, WPrimn vprimn, bool transpose)
724 CALL_DYN(split_do_resize, node, (node, ng, hprimn, vprimn, transpose));
728 void split_resize(WSplit *node, const WRectangle *ng,
729 WPrimn hprimn, WPrimn vprimn)
731 split_update_bounds(node, TRUE);
732 splittree_begin_resize();
733 split_do_resize(node, ng, hprimn, vprimn, FALSE);
734 splittree_end_resize();
741 /*{{{ Low-level resize code; request towards root */
744 static void flexibility(WSplit *node, int dir, int *shrink, int *stretch)
746 if(dir==SPLIT_VERTICAL){
747 *shrink=maxof(0, node->geom.h-node->min_h);
748 if(OBJ_IS(node, WSplitST))
749 *stretch=maxof(0, node->max_h-node->geom.h);
753 *shrink=maxof(0, node->geom.w-node->min_w);
754 if(OBJ_IS(node, WSplitST))
755 *stretch=maxof(0, node->max_w-node->geom.w);
762 static void calc_amount(int *amount, int rs, WSplit *other, int dir)
766 flexibility(other, dir, &shrink, &stretch);
769 *amount=minof(rs, shrink);
771 *amount=-minof(-rs, stretch);
778 static void splitsplit_do_rqsize(WSplitSplit *p, WSplit *node,
779 RootwardAmount *ha, RootwardAmount *va,
780 WRectangle *rg, bool tryonly)
782 WPrimn hprimn=PRIMN_ANY, vprimn=PRIMN_ANY;
783 WRectangle og, pg, ng;
789 assert(!ha->any || ha->tl==0);
790 assert(!va->any || va->tl==0);
791 assert(p->tl==node || p->br==node);
801 ca=(p->dir==SPLIT_VERTICAL ? va : ha);
803 if(thisnode==PRIMN_TL || ca->any){
804 calc_amount(&amount, ca->br, other, p->dir);
806 }else/*if(thisnode==PRIMN_BR)*/{
807 calc_amount(&amount, ca->tl, other, p->dir);
811 if(((WSplit*)p)->parent==NULL /*||
812 (ha->tl==0 && ha->br==0 && va->tl==0 && va->br==0)*/){
813 if(((WSplit*)p)->ws_if_root!=NULL)
814 pg=REGION_GEOM((WTiling*)(((WSplit*)p)->ws_if_root));
816 pg=((WSplit*)p)->geom;
818 splitinner_do_rqsize(((WSplit*)p)->parent, (WSplit*)p, ha, va,
822 assert(pg.w>=0 && pg.h>=0);
827 if(p->dir==SPLIT_VERTICAL){
828 ng.h=maxof(0, node->geom.h+amount);
829 og.h=maxof(0, other->geom.h-amount);
830 adjust_sizes(&(ng.h), &(og.h), pg.h, ng.h+og.h,
831 node->min_h, other->min_h, node->max_h, other->max_h,
832 PRIMN_TL /* node is passed as tl param */);
833 if(thisnode==PRIMN_TL)
839 ng.w=maxof(0, node->geom.w+amount);
840 og.w=maxof(0, other->geom.w-amount);
841 adjust_sizes(&(ng.w), &(og.w), pg.w, ng.w+og.w,
842 node->min_w, other->min_w, node->max_w, other->max_w,
843 PRIMN_TL /* node is passed as tl param */);
844 if(thisnode==PRIMN_TL)
852 /* Entä jos 'other' on stdisp? */
853 split_do_resize(other, &og, hprimn, vprimn, FALSE);
855 ((WSplit*)p)->geom=pg;
862 void splitinner_do_rqsize(WSplitInner *p, WSplit *node,
863 RootwardAmount *ha, RootwardAmount *va,
864 WRectangle *rg, bool tryonly)
866 CALL_DYN(splitinner_do_rqsize, p, (p, node, ha, va, rg, tryonly));
870 static void initra(RootwardAmount *ra, int p, int s, int op, int os,
875 ra->br=(p+s)-(op+os);
883 void split_do_rqgeom_(WSplit *node, const WRectangle *ng,
884 bool hany, bool vany, WRectangle *rg,
887 RootwardAmount ha, va;
889 if(node->parent==NULL){
890 if(node->ws_if_root!=NULL)
891 *rg=REGION_GEOM((WTiling*)(node->ws_if_root));
895 initra(&ha, ng->x, ng->w, node->geom.x, node->geom.w, hany);
896 initra(&va, ng->y, ng->h, node->geom.y, node->geom.h, vany);
898 splitinner_do_rqsize(node->parent, node, &ha, &va, rg, tryonly);
906 /*{{{ Resize interface */
909 static void bnd(int *pos, int *sz, int opos, int osz, int minsz, int maxsz)
911 int ud=abs(*pos-opos);
912 int dd=abs((*pos+*sz)-(opos+osz));
916 bound(sz, minsz, maxsz);
917 *pos+=(szrq-*sz)*ud/(ud+dd);
922 WSplit *split_find_root(WSplit *split)
924 if(split->parent==NULL)
926 return split_find_root((WSplit*)split->parent);
930 void splittree_rqgeom(WSplit *sub, int flags, const WRectangle *geom_,
933 bool hany=flags®ION_RQGEOM_WEAK_X;
934 bool vany=flags®ION_RQGEOM_WEAK_Y;
935 bool tryonly=flags®ION_RQGEOM_TRYONLY;
936 WRectangle geom=*geom_;
938 WSplit *root=split_find_root(sub);
943 split_update_bounds(root, TRUE);
945 if(OBJ_IS(sub, WSplitST)){
946 WSplitST *sub_as_stdisp=(WSplitST*)sub;
948 if(flags®ION_RQGEOM_TRYONLY){
949 warn(TR("REGION_RQGEOM_TRYONLY unsupported for status display."));
953 split_regularise_stdisp(sub_as_stdisp);
955 if(sub_as_stdisp->orientation==REGION_ORIENTATION_HORIZONTAL){
964 split_update_bounds(root, TRUE);
967 /* Handle internal size bounds */
968 bnd(&(geom.x), &(geom.w), sub->geom.x, sub->geom.w,
969 sub->min_w, sub->max_w);
970 bnd(&(geom.y), &(geom.h), sub->geom.y, sub->geom.h,
971 sub->min_h, sub->max_h);
973 /* Check if we should resize to both tl and br */
976 geom.w+=sub->geom.x-geom.x;
981 geom.h+=sub->geom.y-geom.y;
985 splittree_begin_resize();
987 split_do_rqgeom_(sub, &geom, hany, vany, geomret, tryonly);
990 split_do_resize(sub, geomret, hany, vany, FALSE);
991 splittree_end_resize();
1000 * Attempt to resize and/or move the split tree starting at \var{node}.
1001 * Behaviour and the \var{g} parameter are as for \fnref{WRegion.rqgeom}
1002 * operating on \var{node} (if it were a \type{WRegion}).
1005 ExtlTab split_rqgeom(WSplit *node, ExtlTab g)
1007 WRectangle geom, ogeom;
1008 int flags=REGION_RQGEOM_WEAK_ALL;
1013 if(extl_table_gets_i(g, "x", &(geom.x)))
1014 flags&=~REGION_RQGEOM_WEAK_X;
1015 if(extl_table_gets_i(g, "y", &(geom.y)))
1016 flags&=~REGION_RQGEOM_WEAK_Y;
1017 if(extl_table_gets_i(g, "w", &(geom.w)))
1018 flags&=~REGION_RQGEOM_WEAK_W;
1019 if(extl_table_gets_i(g, "h", &(geom.h)))
1020 flags&=~REGION_RQGEOM_WEAK_H;
1022 geom.w=maxof(1, geom.w);
1023 geom.h=maxof(1, geom.h);
1025 splittree_rqgeom(node, flags, &geom, &ogeom);
1027 return extl_table_from_rectangle(&ogeom);
1030 warn(TR("Invalid node."));
1031 return extl_table_none();
1041 void splittree_changeroot(WSplit *root, WSplit *node)
1043 WTiling *ws=(WTiling*)(root->ws_if_root);
1046 assert(ws->split_tree==root);
1047 root->ws_if_root=NULL;
1048 ws->split_tree=node;
1050 node->ws_if_root=ws;
1056 static void splitsplit_replace(WSplitSplit *split, WSplit *child,
1059 assert(split->tl==child || split->br==child);
1061 if(split->tl==child)
1068 what->parent=(WSplitInner*)split;
1069 what->ws_if_root=NULL; /* May not be needed. */
1073 void splitinner_replace(WSplitInner *split, WSplit *child, WSplit *what)
1075 CALL_DYN(splitinner_replace, split, (split, child, what));
1079 WSplitRegion *splittree_split(WSplit *node, int dir, WPrimn primn,
1080 int minsize, WRegionSimpleCreateFn *fn,
1085 WSplitSplit *nsplit;
1086 WSplitRegion *nnode;
1087 WSplitInner *psplit;
1092 assert(node!=NULL && parent!=NULL);
1094 splittree_begin_resize();
1096 node=dodge_stdisp(node, FALSE);
1101 if(OBJ_IS(node, WSplitST)){
1102 warn(TR("Splitting the status display is not allowed."));
1106 if(primn!=PRIMN_TL && primn!=PRIMN_BR)
1108 if(dir!=SPLIT_HORIZONTAL && dir!=SPLIT_VERTICAL)
1111 split_update_bounds(split_find_root(node), TRUE);
1112 objmin=(dir==SPLIT_VERTICAL ? node->min_h : node->min_w);
1114 s=split_size(node, dir);
1115 sn=maxof(minsize, s/2);
1116 so=maxof(objmin, s-sn);
1121 if(dir==SPLIT_VERTICAL)
1125 split_do_rqgeom_(node, &ng, TRUE, TRUE, &rg, TRUE);
1126 rs=(dir==SPLIT_VERTICAL ? rg.h : rg.w);
1127 if(rs<minsize+objmin){
1128 warn(TR("Unable to split: not enough free space."));
1131 split_do_rqgeom_(node, &ng, TRUE, TRUE, &rg, FALSE);
1132 rs=(dir==SPLIT_VERTICAL ? rg.h : rg.w);
1137 so=maxof(rs/2, objmin);
1142 splittree_scan_stdisp_rootward(node);
1145 /* Create split and new window
1147 fp.mode=REGION_FIT_EXACT;
1150 nsplit=create_splitsplit(&(fp.g), dir);
1155 if(dir==SPLIT_VERTICAL){
1165 nreg=fn(parent, &fp);
1168 destroy_obj((Obj*)nsplit);
1172 nnode=create_splitregion(&(fp.g), nreg);
1174 destroy_obj((Obj*)nreg);
1175 destroy_obj((Obj*)nsplit);
1179 /* Now that everything's ok, resize and move original node.
1182 if(dir==SPLIT_VERTICAL){
1192 split_do_resize(node, &ng,
1193 (dir==SPLIT_HORIZONTAL ? primn : PRIMN_ANY),
1194 (dir==SPLIT_VERTICAL ? primn : PRIMN_ANY),
1197 /* Set up split structure
1199 psplit=node->parent;
1202 splitinner_replace(psplit, node, (WSplit*)nsplit);
1204 splittree_changeroot(node, (WSplit*)nsplit);
1206 node->parent=(WSplitInner*)nsplit;
1207 ((WSplit*)nnode)->parent=(WSplitInner*)nsplit;
1209 if(primn==PRIMN_BR){
1211 nsplit->br=(WSplit*)nnode;
1212 nsplit->current=SPLIT_CURRENT_TL;
1214 nsplit->tl=(WSplit*)nnode;
1216 nsplit->current=SPLIT_CURRENT_BR;
1219 splittree_end_resize();
1231 static void splitsplit_remove(WSplitSplit *node, WSplit *child,
1234 static int nstdisp=0;
1235 WSplitInner *parent;
1238 assert(node->tl==child || node->br==child);
1245 assert(other!=NULL);
1247 if(nstdisp==0 && reclaim_space && OBJ_IS(other, WSplitST)){
1248 /* Try to move stdisp out of the way. */
1249 split_try_unsink_stdisp(node, FALSE, TRUE);
1250 assert(child->parent!=NULL);
1252 splitinner_remove(child->parent, child, reclaim_space);
1257 parent=((WSplit*)node)->parent;
1260 splitinner_replace(parent, (WSplit*)node, other);
1262 splittree_changeroot((WSplit*)node, other);
1265 split_resize(other, &(((WSplit*)node)->geom), PRIMN_ANY, PRIMN_ANY);
1271 ((WSplit*)node)->parent=NULL;
1272 destroy_obj((Obj*)node);
1276 void splitinner_remove(WSplitInner *node, WSplit *child, bool reclaim_space)
1278 CALL_DYN(splitinner_remove, node, (node, child, reclaim_space));
1282 void splittree_remove(WSplit *node, bool reclaim_space)
1284 if(node->parent!=NULL)
1285 splitinner_remove(node->parent, node, reclaim_space);
1286 else if(node->ws_if_root!=NULL)
1287 splittree_changeroot(node, NULL);
1289 destroy_obj((Obj*)node);
1296 /*{{{ Tree traversal */
1299 static bool defaultfilter(WSplit *node)
1301 return (OBJ_IS(node, WSplitRegion) &&
1302 ((WSplitRegion*)node)->reg!=NULL);
1306 static WSplit *split_current_todir_default(WSplit *node,
1307 WPrimn hprimn, WPrimn vprimn,
1308 WSplitFilter *filter)
1311 filter=defaultfilter;
1313 return (filter(node) ? node : NULL);
1317 static WSplit *splitsplit_current_todir(WSplitSplit *node,
1318 WPrimn hprimn, WPrimn vprimn,
1319 WSplitFilter *filter)
1321 WPrimn primn=(node->dir==SPLIT_HORIZONTAL ? hprimn : vprimn);
1322 WSplit *first, *second, *ret;
1324 if(primn==PRIMN_TL ||
1325 (primn==PRIMN_ANY && node->current==SPLIT_CURRENT_TL)){
1328 }else if(primn==PRIMN_BR ||
1329 (primn==PRIMN_ANY && node->current==SPLIT_CURRENT_BR)){
1336 ret=split_current_todir(first, hprimn, vprimn, filter);
1338 ret=split_current_todir(second, hprimn, vprimn, filter);
1339 if(ret==NULL && filter!=NULL){
1340 if(filter((WSplit*)node))
1348 WSplit *split_current_todir(WSplit *node, WPrimn hprimn, WPrimn vprimn,
1349 WSplitFilter *filter)
1352 CALL_DYN_RET(ret, WSplit*, split_current_todir, node,
1353 (node, hprimn, vprimn, filter));
1358 /* Note: both hprimn and vprimn are inverted when descending. Therefore
1359 * one should be either PRIMN_NONE or PRIMN_ANY for sensible geometric
1360 * navigation. (Both are PRIMN_TL or PRIMN_BR for pseudo-linear
1361 * next/previous navigation.)
1363 WSplit *splitsplit_nextto(WSplitSplit *node, WSplit *child,
1364 WPrimn hprimn, WPrimn vprimn,
1365 WSplitFilter *filter)
1367 WPrimn primn=(node->dir==SPLIT_HORIZONTAL ? hprimn : vprimn);
1368 WSplit *split=NULL, *nnode=NULL;
1370 if(node->tl==child && (primn==PRIMN_BR || primn==PRIMN_ANY))
1372 else if(node->br==child && (primn==PRIMN_TL || primn==PRIMN_ANY))
1376 nnode=split_current_todir(split,
1377 primn_none2any(primn_invert(hprimn)),
1378 primn_none2any(primn_invert(vprimn)),
1383 nnode=split_nextto((WSplit*)node, hprimn, vprimn, filter);
1389 WSplit *splitinner_nextto(WSplitInner *node, WSplit *child,
1390 WPrimn hprimn, WPrimn vprimn,
1391 WSplitFilter *filter)
1394 CALL_DYN_RET(ret, WSplit*, splitinner_nextto, node,
1395 (node, child, hprimn, vprimn, filter));
1400 WSplit *split_nextto(WSplit *node, WPrimn hprimn, WPrimn vprimn,
1401 WSplitFilter *filter)
1403 while(node->parent!=NULL){
1404 WSplit *ret=splitinner_nextto(node->parent, node,
1405 hprimn, vprimn, filter);
1408 node=(WSplit*)node->parent;
1414 void splitinner_mark_current_default(WSplitInner *split, WSplit *child)
1416 if(((WSplit*)split)->parent!=NULL)
1417 splitinner_mark_current(((WSplit*)split)->parent, (WSplit*)split);
1421 void splitsplit_mark_current(WSplitSplit *split, WSplit *child)
1423 assert(child==split->tl || child==split->br);
1425 split->current=(split->tl==child ? SPLIT_CURRENT_TL : SPLIT_CURRENT_BR);
1427 splitinner_mark_current_default(&(split->isplit), child);
1431 void splitinner_mark_current(WSplitInner *split, WSplit *child)
1433 CALL_DYN(splitinner_mark_current, split, (split, child));
1437 static void splitsplit_forall(WSplitSplit *node, WSplitFn *fn)
1444 void splitinner_forall(WSplitInner *node, WSplitFn *fn)
1446 CALL_DYN(splitinner_forall, node, (node, fn));
1450 static WSplit *splitsplit_current(WSplitSplit *split)
1452 return (split->current==SPLIT_CURRENT_TL ? split->tl : split->br);
1457 * Returns the most previously active child node of \var{split}.
1461 WSplit *splitinner_current(WSplitInner *node)
1464 CALL_DYN_RET(ret, WSplit*, splitinner_current, node, (node));
1472 /*{{{ X window handling */
1475 static void splitregion_stacking(WSplitRegion *split,
1476 Window *bottomret, Window *topret)
1480 if(split->reg!=NULL)
1481 region_stacking(split->reg, bottomret, topret);
1485 void splitsplit_stacking(WSplitSplit *split,
1486 Window *bottomret, Window *topret)
1488 Window tlb=None, tlt=None;
1489 Window brb=None, brt=None;
1491 split_stacking(split->tl, &tlb, &tlt);
1492 split_stacking(split->br, &brb, &brt);
1494 /* To make sure that this condition holds is left to the workspace
1495 * code to do after a split tree has been loaded or modified.
1497 if(split->current==SPLIT_CURRENT_TL){
1498 *topret=(tlt!=None ? tlt : brt);
1499 *bottomret=(brb!=None ? brb : tlb);
1501 *topret=(brt!=None ? brt : tlt);
1502 *bottomret=(tlb!=None ? tlb : brb);
1506 void split_stacking(WSplit *split, Window *bottomret, Window *topret)
1511 CALL_DYN(split_stacking, split, (split, bottomret, topret));
1516 static void splitregion_restack(WSplitRegion *split, Window other, int mode)
1518 if(split->reg!=NULL)
1519 region_restack(split->reg, other, mode);
1522 void splitsplit_restack(WSplitSplit *split, Window other, int mode)
1524 Window bottom=None, top=None;
1525 WSplit *first, *second;
1527 if(split->current==SPLIT_CURRENT_TL){
1535 split_restack(first, other, mode);
1536 split_stacking(first, &bottom, &top);
1541 split_restack(second, other, mode);
1544 void split_restack(WSplit *split, Window other, int mode)
1546 CALL_DYN(split_restack, split, (split, other, mode));
1550 static void splitregion_map(WSplitRegion *split)
1552 if(split->reg!=NULL)
1553 region_map(split->reg);
1556 static void splitinner_map(WSplitInner *split)
1558 splitinner_forall(split, split_map);
1561 void split_map(WSplit *split)
1563 CALL_DYN(split_map, split, (split));
1567 static void splitregion_unmap(WSplitRegion *split)
1569 if(split->reg!=NULL)
1570 region_unmap(split->reg);
1573 static void splitinner_unmap(WSplitInner *split)
1575 splitinner_forall(split, split_unmap);
1578 void split_unmap(WSplit *split)
1580 CALL_DYN(split_unmap, split, (split));
1584 static void splitregion_reparent(WSplitRegion *split, WWindow *wwin)
1586 if(split->reg!=NULL){
1587 WRectangle g=split->split.geom;
1588 region_reparent(split->reg, wwin, &g, REGION_FIT_EXACT);
1593 static void splitsplit_reparent(WSplitSplit *split, WWindow *wwin)
1595 if(split->current==SPLIT_CURRENT_TL){
1596 split_reparent(split->br, wwin);
1597 split_reparent(split->tl, wwin);
1599 split_reparent(split->tl, wwin);
1600 split_reparent(split->br, wwin);
1605 void split_reparent(WSplit *split, WWindow *wwin)
1607 CALL_DYN(split_reparent, split, (split, wwin));
1614 /*{{{ Transpose, flip, rotate */
1617 void splitsplit_flip_default(WSplitSplit *split)
1619 WRectangle tlng, brng;
1620 WRectangle *sg=&((WSplit*)split)->geom;
1623 assert(split->tl!=NULL && split->br!=NULL);
1625 split_update_bounds((WSplit*)split, TRUE);
1627 tlng=split->tl->geom;
1628 brng=split->br->geom;
1630 if(split->dir==SPLIT_HORIZONTAL){
1632 tlng.x=sg->x+sg->w-tlng.w;
1635 tlng.y=sg->y+sg->h-tlng.h;
1639 split->tl=split->br;
1641 split->current=(split->current==SPLIT_CURRENT_TL
1643 : SPLIT_CURRENT_TL);
1645 split_do_resize(split->tl, &brng, PRIMN_ANY, PRIMN_ANY, FALSE);
1646 split_do_resize(split->br, &tlng, PRIMN_ANY, PRIMN_ANY, FALSE);
1650 static void splitsplit_flip_(WSplitSplit *split)
1652 CALL_DYN(splitsplit_flip, split, (split));
1657 * Flip contents of \var{split}.
1660 void splitsplit_flip(WSplitSplit *split)
1662 splittree_begin_resize();
1664 split=OBJ_CAST(dodge_stdisp((WSplit*)split, FALSE), WSplitSplit);
1669 splitsplit_flip_(split);
1671 splittree_end_resize();
1682 static FlipDir flipdir=FLIP_VERTICAL;
1685 static void do_flip(WSplit *split)
1687 WSplitSplit *ss=OBJ_CAST(split, WSplitSplit);
1690 if((flipdir==FLIP_ANY
1691 || (ss->dir==SPLIT_VERTICAL && flipdir==FLIP_VERTICAL)
1692 || (ss->dir==SPLIT_HORIZONTAL && flipdir==FLIP_HORIZONTAL))
1693 && !OBJ_IS(ss->tl, WSplitST)
1694 && !OBJ_IS(ss->br, WSplitST)){
1695 splitsplit_flip_(ss);
1699 if(OBJ_IS(ss, WSplitInner))
1700 splitinner_forall((WSplitInner*)ss, do_flip);
1704 static void splittree_flip_dir(WSplit *splittree, FlipDir dir)
1706 /* todo stdisp outta way */
1707 if(OBJ_IS(splittree, WSplitInner)){
1709 splitinner_forall((WSplitInner*)splittree, do_flip);
1714 static bool split_fliptrans_to(WSplit *node, const WRectangle *geom,
1715 bool trans, FlipDir flip)
1720 splittree_begin_resize();
1722 /* split_do_resize can do things right if 'node' has stdisp as child,
1723 * but otherwise transpose will put the stdisp in a bad split
1724 * configuration if it is contained within 'node', so we must
1725 * first move it and its fixed parent split below node. For correct
1726 * geometry calculation we move it immediately below node, and
1727 * resize stdisp's fixed parent node instead.
1729 node2=dodge_stdisp(node, TRUE);
1731 if(node==NULL || node2!=node)
1734 split_update_bounds(node, TRUE);
1736 split_do_rqgeom_(node, geom, PRIMN_ANY, PRIMN_ANY, &rg, FALSE);
1738 split_do_resize(node, &rg, PRIMN_ANY, PRIMN_ANY, trans);
1741 splittree_flip_dir(node, flip);
1743 splittree_end_resize();
1749 bool split_transpose_to(WSplit *node, const WRectangle *geom)
1751 return split_fliptrans_to(node, geom, TRUE, FLIP_ANY);
1756 * Transpose contents of \var{node}.
1759 void split_transpose(WSplit *node)
1761 WRectangle g=node->geom;
1763 split_transpose_to(node, &g);
1767 bool split_rotate_to(WSplit *node, const WRectangle *geom, int rotation)
1769 FlipDir flip=FLIP_NONE;
1772 if(rotation==SCREEN_ROTATION_90){
1773 flip=FLIP_HORIZONTAL;
1775 }else if(rotation==SCREEN_ROTATION_180){
1777 }else if(rotation==SCREEN_ROTATION_270){
1782 return split_fliptrans_to(node, geom, trans, flip);
1792 * Return parent split for \var{split}.
1796 WSplitInner *split_parent(WSplit *split)
1798 return split->parent;
1803 * Returns the area of workspace used by the regions under \var{split}.
1807 ExtlTab split_geom(WSplit *split)
1809 return extl_table_from_rectangle(&(split->geom));
1814 * Returns the top or left child node of \var{split} depending
1815 * on the direction of the split.
1819 WSplit *splitsplit_tl(WSplitSplit *split)
1826 * Returns the bottom or right child node of \var{split} depending
1827 * on the direction of the split.
1831 WSplit *splitsplit_br(WSplitSplit *split)
1837 * Returns the direction of \var{split}; either \codestr{vertical} or
1838 * \codestr{horizontal}.
1842 const char *splitsplit_dir(WSplitSplit *split)
1844 return (split->dir==SPLIT_VERTICAL ? "vertical" : "horizontal");
1849 * Returns the region contained in \var{node}.
1853 WRegion *splitregion_reg(WSplitRegion *node)
1862 /*{{{ Save support */
1865 ExtlTab split_base_config(WSplit *node)
1867 ExtlTab t=extl_create_table();
1868 extl_table_sets_s(t, "type", OBJ_TYPESTR(node));
1873 static bool splitregion_get_config(WSplitRegion *node, ExtlTab *ret)
1880 if(!region_supports_save(node->reg)){
1881 warn(TR("Unable to get configuration for %s."),
1882 region_name(node->reg));
1886 rt=region_get_configuration(node->reg);
1887 t=split_base_config(&(node->split));
1888 extl_table_sets_t(t, "regparams", rt);
1889 extl_unref_table(rt);
1896 static bool splitst_get_config(WSplitST *node, ExtlTab *ret)
1898 *ret=split_base_config((WSplit*)node);
1903 static bool splitsplit_get_config(WSplitSplit *node, ExtlTab *ret)
1905 ExtlTab tab, tltab, brtab;
1908 if(!split_get_config(node->tl, &tltab))
1909 return split_get_config(node->br, ret);
1911 if(!split_get_config(node->br, &brtab)){
1916 tab=split_base_config((WSplit*)node);
1918 tls=split_size(node->tl, node->dir);
1919 brs=split_size(node->br, node->dir);
1921 extl_table_sets_s(tab, "dir", (node->dir==SPLIT_VERTICAL
1922 ? "vertical" : "horizontal"));
1924 extl_table_sets_i(tab, "tls", tls);
1925 extl_table_sets_t(tab, "tl", tltab);
1926 extl_unref_table(tltab);
1928 extl_table_sets_i(tab, "brs", brs);
1929 extl_table_sets_t(tab, "br", brtab);
1930 extl_unref_table(brtab);
1938 bool split_get_config(WSplit *node, ExtlTab *tabret)
1941 CALL_DYN_RET(ret, bool, split_get_config, node, (node, tabret));
1949 /*{{{ The classes */
1952 static DynFunTab split_dynfuntab[]={
1953 {split_do_resize, split_do_resize_default},
1954 {(DynFun*)split_current_todir, (DynFun*)split_current_todir_default},
1958 static DynFunTab splitinner_dynfuntab[]={
1959 {splitinner_mark_current, splitinner_mark_current_default},
1960 {split_map, splitinner_map},
1961 {split_unmap, splitinner_unmap},
1965 static DynFunTab splitsplit_dynfuntab[]={
1966 {split_update_bounds, splitsplit_update_bounds},
1967 {split_do_resize, splitsplit_do_resize},
1968 {splitinner_do_rqsize, splitsplit_do_rqsize},
1969 {splitinner_replace, splitsplit_replace},
1970 {splitinner_remove, splitsplit_remove},
1971 {(DynFun*)split_current_todir, (DynFun*)splitsplit_current_todir},
1972 {(DynFun*)splitinner_current, (DynFun*)splitsplit_current},
1973 {(DynFun*)splitinner_nextto, (DynFun*)splitsplit_nextto},
1974 {splitinner_mark_current, splitsplit_mark_current},
1975 {(DynFun*)split_get_config, (DynFun*)splitsplit_get_config},
1976 {splitinner_forall, splitsplit_forall},
1977 {split_restack, splitsplit_restack},
1978 {split_stacking, splitsplit_stacking},
1979 {split_reparent, splitsplit_reparent},
1980 {splitsplit_flip, splitsplit_flip_default},
1984 static DynFunTab splitregion_dynfuntab[]={
1985 {split_update_bounds, splitregion_update_bounds},
1986 {split_do_resize, splitregion_do_resize},
1987 {(DynFun*)split_get_config, (DynFun*)splitregion_get_config},
1988 {split_map, splitregion_map},
1989 {split_unmap, splitregion_unmap},
1990 {split_restack, splitregion_restack},
1991 {split_stacking, splitregion_stacking},
1992 {split_reparent, splitregion_reparent},
1996 static DynFunTab splitst_dynfuntab[]={
1997 {split_update_bounds, splitst_update_bounds},
1998 {split_do_resize, splitst_do_resize},
1999 {(DynFun*)split_get_config, (DynFun*)splitst_get_config},
2005 IMPLCLASS(WSplit, Obj, split_deinit, split_dynfuntab);
2008 IMPLCLASS(WSplitInner, WSplit, splitinner_deinit, splitinner_dynfuntab);
2011 IMPLCLASS(WSplitSplit, WSplitInner, splitsplit_deinit, splitsplit_dynfuntab);
2014 IMPLCLASS(WSplitRegion, WSplit, splitregion_deinit, splitregion_dynfuntab);
2017 IMPLCLASS(WSplitST, WSplitRegion, splitst_deinit, splitst_dynfuntab);