2 * ion/mod_tiling/split.c
4 * Copyright (c) Tuomo Valkonen 1999-2009.
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 if(node
->dir
==SPLIT_VERTICAL
){
413 ((WSplit
*)node
)->geom
.h
=node
->tl
->geom
.h
+node
->br
->geom
.h
;
414 ((WSplit
*)node
)->geom
.y
=node
->tl
->geom
.y
;
415 }else if(node
->dir
==SPLIT_HORIZONTAL
){
416 ((WSplit
*)node
)->geom
.w
=node
->tl
->geom
.w
+node
->br
->geom
.w
;
417 ((WSplit
*)node
)->geom
.x
=node
->tl
->geom
.x
;
425 /*{{{ Status display handling helper functions. */
428 static WSplitST
*saw_stdisp
=NULL
;
431 void splittree_begin_resize()
437 void splittree_end_resize()
439 if(saw_stdisp
!=NULL
){
440 split_regularise_stdisp(saw_stdisp
);
446 static void splittree_scan_stdisp_rootward_(WSplitInner
*node_
)
448 WSplitSplit
*node
=OBJ_CAST(node_
, WSplitSplit
);
451 if(OBJ_IS(node
->tl
, WSplitST
)){
452 saw_stdisp
=(WSplitST
*)(node
->tl
);
454 }else if(OBJ_IS(node
->br
, WSplitST
)){
455 saw_stdisp
=(WSplitST
*)(node
->br
);
460 if(node_
->split
.parent
!=NULL
)
461 splittree_scan_stdisp_rootward_(node_
->split
.parent
);
465 void splittree_scan_stdisp_rootward(WSplit
*node
)
467 if(node
->parent
!=NULL
)
468 splittree_scan_stdisp_rootward_(node
->parent
);
472 static WSplitST
*splittree_scan_stdisp(WSplit
*node_
, bool set_saw
)
475 WSplitSplit
*node
=OBJ_CAST(node_
, WSplitSplit
);
480 r
=OBJ_CAST(node
->tl
, WSplitST
);
482 r
=OBJ_CAST(node
->br
, WSplitST
);
490 r
=splittree_scan_stdisp(node
->tl
, set_saw
);
492 r
=splittree_scan_stdisp(node
->br
, set_saw
);
498 static bool stdisp_immediate_child(WSplitSplit
*node
)
500 return (node
!=NULL
&& (OBJ_IS(node
->tl
, WSplitST
) ||
501 OBJ_IS(node
->br
, WSplitST
)));
505 static WSplit
*dodge_stdisp(WSplit
*node
, bool keep_within
)
508 WSplitSplit
*stdispp
;
510 stdisp
=splittree_scan_stdisp(node
, TRUE
);
515 stdispp
=OBJ_CAST(((WSplit
*)stdisp
)->parent
, WSplitSplit
);
520 if((WSplit
*)stdispp
==node
){
521 /* Node itself immediately contains stdisp. Due to the way
522 * try_unsink works, stdisp this will not change, so another
523 * node must be used, if we want to fully dodge stdisp.
527 : (stdispp
->tl
==(WSplit
*)stdisp
533 if(!split_try_unsink_stdisp(stdispp
, FALSE
, TRUE
)){
534 warn(TR("Unable to move the status display out of way."));
537 }while(stdispp
->tl
!=node
&& stdispp
->br
!=node
);
546 /*{{{ Low-level resize code; from root to leaf */
549 static void split_do_resize_default(WSplit
*node
, const WRectangle
*ng
,
550 WPrimn hprimn
, WPrimn vprimn
,
557 static void splitregion_do_resize(WSplitRegion
*node
, const WRectangle
*ng
,
558 WPrimn hprimn
, WPrimn vprimn
,
561 assert(node
->reg
!=NULL
);
562 region_fit(node
->reg
, ng
, REGION_FIT_EXACT
);
563 split_update_bounds(&(node
->split
), FALSE
);
564 node
->split
.geom
=*ng
;
568 static void splitst_do_resize(WSplitST
*node
, const WRectangle
*ng
,
569 WPrimn hprimn
, WPrimn vprimn
,
574 if(node
->regnode
.reg
==NULL
){
575 ((WSplit
*)node
)->geom
=*ng
;
577 splitregion_do_resize(&(node
->regnode
), ng
, hprimn
, vprimn
,
583 static int other_dir(int dir
)
585 return (dir
==SPLIT_VERTICAL
? SPLIT_HORIZONTAL
: SPLIT_VERTICAL
);
589 static void adjust_sizes(int *tls_
, int *brs_
, int nsize
, int sz
,
590 int tlmin
, int brmin
, int tlmax
, int brmax
,
598 bound(&tls
, tlmin
, tlmax
);
600 bound(&brs
, brmin
, brmax
);
602 bound(&tls
, tlmin
, tlmax
);
603 }else if(primn
==PRIMN_BR
){
605 bound(&brs
, brmin
, brmax
);
607 bound(&tls
, tlmin
, tlmax
);
609 bound(&brs
, brmin
, brmax
);
610 }else{ /* && PRIMN_ANY */
612 bound(&tls
, tlmin
, tlmax
);
614 bound(&brs
, brmin
, brmax
);
616 bound(&tls
, tlmin
, tlmax
);
624 static void get_minmaxunused(WSplit
*node
, int dir
,
625 int *min
, int *max
, int *unused
)
627 if(dir
==SPLIT_VERTICAL
){
629 *max
=maxof(*min
, node
->max_h
);
630 *unused
=minof(node
->unused_h
, node
->geom
.h
);
633 *max
=maxof(*min
, node
->max_w
);
634 *unused
=minof(node
->unused_w
, node
->geom
.w
);
639 void splitsplit_do_resize(WSplitSplit
*node
, const WRectangle
*ng
,
640 WPrimn hprimn
, WPrimn vprimn
, bool transpose
)
642 assert(ng
->w
>=0 && ng
->h
>=0);
643 assert(node
->tl
!=NULL
&& node
->br
!=NULL
);
644 assert(!transpose
|| (hprimn
==PRIMN_ANY
&& vprimn
==PRIMN_ANY
));
647 WSplit
*tl
=node
->tl
, *br
=node
->br
;
648 int tls
=split_size((WSplit
*)tl
, node
->dir
);
649 int brs
=split_size((WSplit
*)br
, node
->dir
);
651 /* Status display can not be transposed. */
652 int dir
=((transpose
&& !stdisp_immediate_child(node
))
653 ? other_dir(node
->dir
)
655 int nsize
=(dir
==SPLIT_VERTICAL
? ng
->h
: ng
->w
);
656 int primn
=(dir
==SPLIT_VERTICAL
? vprimn
: hprimn
);
657 int tlmin
, tlmax
, tlunused
, tlused
;
658 int brmin
, brmax
, brunused
, brused
;
659 WRectangle tlg
=*ng
, brg
=*ng
;
661 get_minmaxunused(tl
, dir
, &tlmin
, &tlmax
, &tlunused
);
662 get_minmaxunused(br
, dir
, &brmin
, &brmax
, &brunused
);
664 tlused
=maxof(0, tls
-maxof(0, tlunused
));
665 brused
=maxof(0, brs
-maxof(0, brunused
));
666 /* tlmin, brmin >= 1 => (tls>=tlmin, brs>=brmin => sz>0) */
669 if(primn
==PRIMN_ANY
&& (tlunused
>=0 || brunused
>=0)){
670 if(nsize
<=tlused
+brused
){
671 /* Need to shrink a tangible node */
672 adjust_sizes(&tls
, &brs
, nsize
, sz
,
673 tlmin
, brmin
, tlused
, brused
, primn
);
675 /* Just expand or shrink unused space */
676 adjust_sizes(&tls
, &brs
, nsize
, sz
,
678 (tlunused
<0 ? tlused
: tlmax
),
679 (brunused
<0 ? brused
: brmax
), primn
);
683 adjust_sizes(&tls
, &brs
, nsize
, sz
,
684 tlmin
, brmin
, tlmax
, brmax
, primn
);
689 /* Bad fit; just size proportionally. */
694 tls
=split_size(tl
, node
->dir
)*nsize
/sz
;
699 if(dir
==SPLIT_VERTICAL
){
709 split_do_resize(tl
, &tlg
, hprimn
, vprimn
, transpose
);
710 split_do_resize(br
, &brg
, hprimn
, vprimn
, transpose
);
713 ((WSplit
*)node
)->geom
=*ng
;
714 split_update_bounds((WSplit
*)node
, FALSE
);
719 void split_do_resize(WSplit
*node
, const WRectangle
*ng
,
720 WPrimn hprimn
, WPrimn vprimn
, bool transpose
)
722 CALL_DYN(split_do_resize
, node
, (node
, ng
, hprimn
, vprimn
, transpose
));
726 void split_resize(WSplit
*node
, const WRectangle
*ng
,
727 WPrimn hprimn
, WPrimn vprimn
)
729 split_update_bounds(node
, TRUE
);
730 splittree_begin_resize();
731 split_do_resize(node
, ng
, hprimn
, vprimn
, FALSE
);
732 splittree_end_resize();
739 /*{{{ Save, restore and verify code for maximization */
742 bool splits_are_related(WSplit
*p
, WSplit
*node
)
749 ? splits_are_related(p
, (WSplit
*)node
->parent
)
754 WSplit
*maxparentdir_rel(WSplit
*p
, WSplit
*node
, int dir
)
756 /* Descending from p, try to determine the first split of type dir between
757 * p and node, while ignoring a potential stdisp. */
758 if(OBJ_IS(p
, WSplitSplit
)){
759 WSplitSplit
*sp
=(WSplitSplit
*)p
;
760 assert(sp
->tl
!=NULL
&& sp
->br
!=NULL
);
761 assert(splits_are_related(sp
->tl
, node
) ||
762 splits_are_related(sp
->br
, node
));
764 if(OBJ_IS(sp
->tl
, WSplitST
))
765 return maxparentdir_rel(sp
->br
, node
, dir
);
766 if(OBJ_IS(sp
->br
, WSplitST
))
767 return maxparentdir_rel(sp
->tl
, node
, dir
);
771 splits_are_related(sp
->tl
, node
)
772 ? maxparentdir_rel(sp
->tl
, node
, dir
)
773 : maxparentdir_rel(sp
->br
, node
, dir
);
781 WSplit
*maxparent(WSplit
*node
)
783 WSplit
*p
=(WSplit
*)node
->parent
;
784 return p
==NULL
? node
: maxparent(p
);
788 WSplit
*maxparentdir(WSplit
*node
, int dir
)
790 return maxparentdir_rel(maxparent(node
), node
, dir
);
793 int *wh(WRectangle
*geom
, int orientation
)
795 return orientation
==REGION_ORIENTATION_HORIZONTAL
? &geom
->w
: &geom
->h
;
798 int *xy(WRectangle
*geom
, int orientation
)
800 return orientation
==REGION_ORIENTATION_HORIZONTAL
? &geom
->x
: &geom
->y
;
803 bool is_lt(int orientation
, int corner
)
805 /* Read as "is_left" or "is_top", depending on the orientation. */
807 orientation
==REGION_ORIENTATION_HORIZONTAL
808 ? corner
==MPLEX_STDISP_TL
|| corner
==MPLEX_STDISP_BL
809 : corner
==MPLEX_STDISP_TL
|| corner
==MPLEX_STDISP_TR
;
812 int flip_orientation(int orientation
)
815 orientation
==REGION_ORIENTATION_HORIZONTAL
816 ? REGION_ORIENTATION_VERTICAL
817 : REGION_ORIENTATION_HORIZONTAL
;
820 WRectangle
stdisp_recommended_geom(WSplitST
*st
, WRectangle wsg
)
822 /* wsg holds the geometry of the workspace that st is on. */
823 WRectangle stg
=REGION_GEOM(st
->regnode
.reg
);
824 int ori
=st
->orientation
;
825 stg
.w
=stdisp_recommended_w(st
);
826 stg
.h
=stdisp_recommended_h(st
);
828 if(!is_lt(ori
, st
->corner
))
829 *xy(&stg
, ori
)=*wh(&wsg
, ori
)-*wh(&stg
, ori
);
834 bool geom_overlaps_stgeom_xy(WRectangle geom
, WSplitST
*st
, WRectangle stg
)
845 int ori
=st
->orientation
;
848 is_lt(ori
, st
->corner
)
849 ? *xy(&geom
, ori
)<*wh(&stg
, ori
)
850 : *xy(&geom
, ori
)+*wh(&geom
, ori
)>*xy(&stg
, ori
);
853 bool geom_aligned_stdisp(WRectangle geom
, WSplitST
*st
)
861 * ----- ------ ----- |geom| -----
862 * |stg| |stg| ------ |stg|
866 WRectangle stg
=REGION_GEOM(st
->regnode
.reg
);
867 int ori
=flip_orientation(st
->orientation
);
870 is_lt(ori
, st
->corner
)
871 ? *xy(&geom
, ori
)==*wh(&stg
, ori
)
872 : *xy(&geom
, ori
)+*wh(&geom
, ori
)==*xy(&stg
, ori
);
875 void grow_by_stdisp_wh(WRectangle
*geom
, WSplitST
*st
)
882 * ----- ------ ----- |geom|
887 WRectangle stg
=REGION_GEOM(st
->regnode
.reg
);
888 int ori
=flip_orientation(st
->orientation
);
890 if(is_lt(ori
, st
->corner
))
892 *wh(geom
, ori
)+=*wh(&stg
, ori
);
895 bool frame_neighbors_stdisp(WFrame
*frame
, WSplitST
*st
)
898 geom_overlaps_stgeom_xy(REGION_GEOM(frame
), st
, REGION_GEOM(st
)) &&
899 geom_aligned_stdisp(REGION_GEOM(frame
), st
);
902 bool geom_clashes_stdisp(WRectangle geom
, WSplitST
*st
)
904 WRectangle stg
=REGION_GEOM(st
->regnode
.reg
);
905 int ori
=flip_orientation(st
->orientation
);
907 is_lt(ori
, st
->corner
)
909 : *xy(&geom
, ori
)+*wh(&geom
, ori
)==*xy(&stg
, ori
)+*wh(&stg
, ori
);
912 bool is_same_dir(int dir
, int ori
)
915 (dir
==SPLIT_HORIZONTAL
&& ori
==REGION_ORIENTATION_HORIZONTAL
) ||
916 (dir
==SPLIT_VERTICAL
&& ori
==REGION_ORIENTATION_VERTICAL
);
919 bool is_maxed(WFrame
*frame
, int dir
)
922 dir
==SPLIT_HORIZONTAL
923 ? frame
->flags
&FRAME_MAXED_HORIZ
&& frame
->flags
&FRAME_SAVED_HORIZ
924 : frame
->flags
&FRAME_MAXED_VERT
&& frame
->flags
&FRAME_SAVED_VERT
;
927 bool update_geom_from_stdisp(WFrame
*frame
, WRectangle
*ng
, int dir
)
929 WRegion
*ws
=REGION_MANAGER(frame
);
936 if(!OBJ_IS(ws
, WTiling
) || ((WTiling
*)ws
)->stdispnode
==NULL
)
939 st
=((WTiling
*)ws
)->stdispnode
;
941 if(st
->fullsize
|| !frame_neighbors_stdisp(frame
, st
))
944 rstg
=stdisp_recommended_geom(st
, ws
->geom
);
946 if(is_same_dir(dir
, st
->orientation
) &&
947 !geom_overlaps_stgeom_xy(*ng
, st
, rstg
))
949 grow_by_stdisp_wh(ng
, st
);
950 if(is_maxed(frame
, other_dir(dir
)) &&
951 geom_aligned_stdisp(frame
->saved_geom
, st
))
953 grow_by_stdisp_wh(&frame
->saved_geom
, st
);
958 if(!is_same_dir(dir
, st
->orientation
) &&
959 geom_clashes_stdisp(frame
->saved_geom
, st
))
961 stg
=REGION_GEOM(st
->regnode
.reg
);
962 ori
=flip_orientation(st
->orientation
);
963 if(is_lt(ori
, st
->corner
))
964 *xy(ng
, ori
)+=*wh(&stg
, ori
);
965 /* We've checked that this makes sense when verifying the saved layout. */
966 *wh(ng
, ori
)-=*wh(&stg
, ori
);
972 bool splitregion_do_restore(WSplitRegion
*node
, int dir
)
975 WRectangle geom
=((WSplit
*)node
)->geom
;
980 if(!OBJ_IS(node
->reg
, WFrame
))
983 frame
=(WFrame
*)node
->reg
;
984 if(dir
==SPLIT_HORIZONTAL
){
985 geom
.x
=frame
->saved_geom
.x
;
986 geom
.w
=frame
->saved_geom
.w
;
988 geom
.y
=frame
->saved_geom
.y
;
989 geom
.h
=frame
->saved_geom
.h
;
993 dir
==SPLIT_HORIZONTAL
994 ? frame
->flags
&FRAME_MAXED_VERT
995 : frame
->flags
&FRAME_MAXED_HORIZ
;
998 ret
=update_geom_from_stdisp(frame
, &geom
, dir
);
1000 /* Tell the region the correct geometry to avoid redrawing it again when
1001 * the stdisp is resized by split_regularise_stdisp. Some clients (notably
1002 * ncurses based ones) don't seem to react well to being resized multiple
1003 * times within a short amount of time and don't refresh themselves
1005 region_fit(node
->reg
, &geom
, REGION_FIT_EXACT
);
1007 split_update_bounds(&(node
->split
), FALSE
);
1009 /* Keep the old geometry for the WSplit. Otherwise the tiling would be
1010 * inconsistent, by for example having horizontal WSplitSplit's whose
1011 * children have different heights. The call to split_regularise_stdisp
1012 * below will take care of correcting the geometry of the WSplit and it
1013 * behaves badly when the tiling is inconsistent. */
1014 node
->split
.geom
=ret
? fakegeom
: geom
;
1016 frame
->flags
|=other_max
;
1020 bool splitst_do_restore(WSplit
*node
, int dir
)
1025 bool splitsplit_do_restore(WSplitSplit
*node
, int dir
)
1027 bool ret1
, ret2
, ret
=FALSE
;
1028 WSplit
*snode
=(WSplit
*)node
;
1035 assert(node
->tl
!=NULL
&& node
->br
!=NULL
);
1037 if(stdisp_immediate_child(node
)){
1038 if(OBJ_IS(node
->tl
, WSplitST
)){
1039 st
=(WSplitST
*)node
->tl
;
1042 st
=(WSplitST
*)node
->br
;
1045 stg
=((WSplit
*)st
)->geom
;
1046 split_do_restore(other
, dir
);
1048 if(node
->dir
==SPLIT_HORIZONTAL
){
1055 if(rectangle_compare(&stg
, &((WSplit
*)st
)->geom
)){
1056 splitst_do_resize(st
, &stg
, PRIMN_ANY
, PRIMN_ANY
, FALSE
);
1060 /* Avoid short-circuit evaluation. */
1061 ret1
=split_do_restore(node
->tl
, dir
);
1062 ret2
=split_do_restore(node
->br
, dir
);
1066 snode
->geom
.x
=node
->tl
->geom
.x
;
1067 snode
->geom
.y
=node
->tl
->geom
.y
;
1068 if(node
->dir
==SPLIT_HORIZONTAL
){
1069 snode
->geom
.w
=node
->tl
->geom
.w
+node
->br
->geom
.w
;
1070 snode
->geom
.h
=node
->tl
->geom
.h
;
1072 if(node
->dir
==SPLIT_VERTICAL
){
1073 snode
->geom
.w
=node
->tl
->geom
.w
;
1074 snode
->geom
.h
=node
->tl
->geom
.h
+node
->br
->geom
.h
;
1080 bool split_do_restore(WSplit
*node
, int dir
)
1083 CALL_DYN_RET(ret
, bool, split_do_restore
, node
, (node
, dir
));
1088 void splitregion_do_maxhelper(WSplitRegion
*node
, int dir
, int action
)
1091 if(!OBJ_IS(node
->reg
, WFrame
))
1093 frame
=(WFrame
*)node
->reg
;
1096 frame
->flags
|=FRAME_KEEP_FLAGS
;
1097 if(dir
==HORIZONTAL
){
1098 frame
->flags
|=(FRAME_MAXED_HORIZ
|FRAME_SAVED_HORIZ
);
1099 frame
->saved_geom
.x
=REGION_GEOM(frame
).x
;
1100 frame
->saved_geom
.w
=REGION_GEOM(frame
).w
;
1102 frame
->flags
|=(FRAME_MAXED_VERT
|FRAME_SAVED_VERT
);
1103 frame
->saved_geom
.y
=REGION_GEOM(frame
).y
;
1104 frame
->saved_geom
.h
=REGION_GEOM(frame
).h
;
1107 if(action
==SET_KEEP
)
1108 frame
->flags
|=FRAME_KEEP_FLAGS
;
1110 frame
->flags
&=~FRAME_KEEP_FLAGS
;
1113 void splitst_do_maxhelper(WSplit
*node
, int dir
, int action
)
1118 void splitsplit_do_maxhelper(WSplitSplit
*node
, int dir
, int action
)
1120 assert(node
->tl
!=NULL
&& node
->br
!=NULL
);
1121 split_do_maxhelper(node
->tl
, dir
, action
);
1122 split_do_maxhelper(node
->br
, dir
, action
);
1125 void split_do_maxhelper(WSplit
*node
, int dir
, int action
)
1127 CALL_DYN(split_do_maxhelper
, node
, (node
, dir
, action
));
1131 bool savedgeom_clashes_stdisp(WFrame
*frame
, int dir
)
1133 WRegion
*ws
=REGION_MANAGER(frame
);
1137 if(!OBJ_IS(ws
, WTiling
) || ((WTiling
*)ws
)->stdispnode
==NULL
)
1140 st
=((WTiling
*)ws
)->stdispnode
;
1141 ori
=flip_orientation(st
->orientation
);
1144 !is_same_dir(dir
, st
->orientation
) &&
1145 frame_neighbors_stdisp(frame
, st
) &&
1146 geom_clashes_stdisp(frame
->saved_geom
, st
)
1147 ? *wh(&frame
->saved_geom
, ori
)<*wh(®ION_GEOM(st
), ori
)
1151 bool splitregion_do_verify(WSplitRegion
*node
, int dir
)
1156 if(!OBJ_IS(node
->reg
, WFrame
))
1159 frame
=(WFrame
*)node
->reg
;
1161 ret
=is_maxed(frame
, dir
);
1164 frame
->flags
&=~(FRAME_MAXED_HORIZ
|FRAME_SAVED_HORIZ
);
1166 frame
->flags
&=~(FRAME_MAXED_VERT
|FRAME_SAVED_VERT
);
1168 if(savedgeom_clashes_stdisp(frame
, dir
))
1174 bool splitst_do_verify(WSplit
*node
, int dir
)
1179 bool splitsplit_do_verify(WSplitSplit
*node
, int dir
)
1182 assert(node
->tl
!=NULL
&& node
->br
!=NULL
);
1184 /* Avoid short-circuit evaluation. */
1185 ret1
=split_do_verify(node
->tl
, dir
);
1186 ret2
=split_do_verify(node
->br
, dir
);
1187 return ret1
&& ret2
;
1190 bool split_do_verify(WSplit
*node
, int dir
)
1193 CALL_DYN_RET(ret
, bool, split_do_verify
, node
, (node
, dir
));
1198 bool split_maximize(WSplit
*node
, int dir
, int action
)
1200 WSplit
*p
=maxparentdir(node
, dir
);
1202 return split_do_restore(p
, dir
);
1204 return split_do_verify(p
, dir
);
1206 split_do_maxhelper(p
, dir
, action
);
1214 /*{{{ Low-level resize code; request towards root */
1217 static void flexibility(WSplit
*node
, int dir
, int *shrink
, int *stretch
)
1219 if(dir
==SPLIT_VERTICAL
){
1220 *shrink
=maxof(0, node
->geom
.h
-node
->min_h
);
1221 if(OBJ_IS(node
, WSplitST
))
1222 *stretch
=maxof(0, node
->max_h
-node
->geom
.h
);
1226 *shrink
=maxof(0, node
->geom
.w
-node
->min_w
);
1227 if(OBJ_IS(node
, WSplitST
))
1228 *stretch
=maxof(0, node
->max_w
-node
->geom
.w
);
1235 static void calc_amount(int *amount
, int rs
, WSplit
*other
, int dir
)
1237 int shrink
, stretch
;
1239 flexibility(other
, dir
, &shrink
, &stretch
);
1242 *amount
=minof(rs
, shrink
);
1244 *amount
=-minof(-rs
, stretch
);
1251 static void splitsplit_do_rqsize(WSplitSplit
*p
, WSplit
*node
,
1252 RootwardAmount
*ha
, RootwardAmount
*va
,
1253 WRectangle
*rg
, bool tryonly
)
1255 WPrimn hprimn
=PRIMN_ANY
, vprimn
=PRIMN_ANY
;
1256 WRectangle og
, pg
, ng
;
1262 assert(!ha
->any
|| ha
->tl
==0);
1263 assert(!va
->any
|| va
->tl
==0);
1264 assert(p
->tl
==node
|| p
->br
==node
);
1274 ca
=(p
->dir
==SPLIT_VERTICAL
? va
: ha
);
1276 if(thisnode
==PRIMN_TL
|| ca
->any
){
1277 calc_amount(&amount
, ca
->br
, other
, p
->dir
);
1279 }else/*if(thisnode==PRIMN_BR)*/{
1280 calc_amount(&amount
, ca
->tl
, other
, p
->dir
);
1284 if(((WSplit
*)p
)->parent
==NULL
/*||
1285 (ha->tl==0 && ha->br==0 && va->tl==0 && va->br==0)*/){
1286 if(((WSplit
*)p
)->ws_if_root
!=NULL
)
1287 pg
=REGION_GEOM((WTiling
*)(((WSplit
*)p
)->ws_if_root
));
1289 pg
=((WSplit
*)p
)->geom
;
1291 splitinner_do_rqsize(((WSplit
*)p
)->parent
, (WSplit
*)p
, ha
, va
,
1295 assert(pg
.w
>=0 && pg
.h
>=0);
1300 if(p
->dir
==SPLIT_VERTICAL
){
1301 ng
.h
=maxof(0, node
->geom
.h
+amount
);
1302 og
.h
=maxof(0, other
->geom
.h
-amount
);
1303 adjust_sizes(&(ng
.h
), &(og
.h
), pg
.h
, ng
.h
+og
.h
,
1304 node
->min_h
, other
->min_h
, node
->max_h
, other
->max_h
,
1305 PRIMN_TL
/* node is passed as tl param */);
1306 if(thisnode
==PRIMN_TL
)
1307 og
.y
=pg
.y
+pg
.h
-og
.h
;
1309 ng
.y
=pg
.y
+pg
.h
-ng
.h
;
1312 ng
.w
=maxof(0, node
->geom
.w
+amount
);
1313 og
.w
=maxof(0, other
->geom
.w
-amount
);
1314 adjust_sizes(&(ng
.w
), &(og
.w
), pg
.w
, ng
.w
+og
.w
,
1315 node
->min_w
, other
->min_w
, node
->max_w
, other
->max_w
,
1316 PRIMN_TL
/* node is passed as tl param */);
1317 if(thisnode
==PRIMN_TL
)
1318 og
.x
=pg
.x
+pg
.w
-og
.w
;
1320 ng
.x
=pg
.x
+pg
.w
-ng
.w
;
1325 /* Entä jos 'other' on stdisp? */
1326 split_do_resize(other
, &og
, hprimn
, vprimn
, FALSE
);
1328 ((WSplit
*)p
)->geom
=pg
;
1335 void splitinner_do_rqsize(WSplitInner
*p
, WSplit
*node
,
1336 RootwardAmount
*ha
, RootwardAmount
*va
,
1337 WRectangle
*rg
, bool tryonly
)
1339 CALL_DYN(splitinner_do_rqsize
, p
, (p
, node
, ha
, va
, rg
, tryonly
));
1343 static void initra(RootwardAmount
*ra
, int p
, int s
, int op
, int os
,
1348 ra
->br
=(p
+s
)-(op
+os
);
1356 void split_do_rqgeom_(WSplit
*node
, const WRectangle
*ng
,
1357 bool hany
, bool vany
, WRectangle
*rg
,
1360 RootwardAmount ha
, va
;
1362 if(node
->parent
==NULL
){
1363 if(node
->ws_if_root
!=NULL
)
1364 *rg
=REGION_GEOM((WTiling
*)(node
->ws_if_root
));
1368 initra(&ha
, ng
->x
, ng
->w
, node
->geom
.x
, node
->geom
.w
, hany
);
1369 initra(&va
, ng
->y
, ng
->h
, node
->geom
.y
, node
->geom
.h
, vany
);
1371 splitinner_do_rqsize(node
->parent
, node
, &ha
, &va
, rg
, tryonly
);
1379 /*{{{ Resize interface */
1382 static void bnd(int *pos
, int *sz
, int opos
, int osz
, int minsz
, int maxsz
)
1384 int ud
=abs(*pos
-opos
);
1385 int dd
=abs((*pos
+*sz
)-(opos
+osz
));
1389 bound(sz
, minsz
, maxsz
);
1390 *pos
+=(szrq
-*sz
)*ud
/(ud
+dd
);
1395 WSplit
*split_find_root(WSplit
*split
)
1397 if(split
->parent
==NULL
)
1399 return split_find_root((WSplit
*)split
->parent
);
1403 void splittree_rqgeom(WSplit
*sub
, int flags
, const WRectangle
*geom_
,
1404 WRectangle
*geomret
)
1406 bool hany
=flags
®ION_RQGEOM_WEAK_X
;
1407 bool vany
=flags
®ION_RQGEOM_WEAK_Y
;
1408 bool tryonly
=flags
®ION_RQGEOM_TRYONLY
;
1409 WRectangle geom
=*geom_
;
1411 WSplit
*root
=split_find_root(sub
);
1416 split_update_bounds(root
, TRUE
);
1418 if(OBJ_IS(sub
, WSplitST
)){
1419 WSplitST
*sub_as_stdisp
=(WSplitST
*)sub
;
1421 if(flags
®ION_RQGEOM_TRYONLY
){
1422 warn(TR("REGION_RQGEOM_TRYONLY unsupported for status display."));
1426 split_regularise_stdisp(sub_as_stdisp
);
1428 if(sub_as_stdisp
->orientation
==REGION_ORIENTATION_HORIZONTAL
){
1429 if(geom_
->h
==geom
.h
)
1433 if(geom_
->w
==geom
.w
)
1437 split_update_bounds(root
, TRUE
);
1440 /* Handle internal size bounds */
1441 bnd(&(geom
.x
), &(geom
.w
), sub
->geom
.x
, sub
->geom
.w
,
1442 sub
->min_w
, sub
->max_w
);
1443 bnd(&(geom
.y
), &(geom
.h
), sub
->geom
.y
, sub
->geom
.h
,
1444 sub
->min_h
, sub
->max_h
);
1446 /* Check if we should resize to both tl and br */
1449 geom
.w
+=sub
->geom
.x
-geom
.x
;
1454 geom
.h
+=sub
->geom
.y
-geom
.y
;
1458 splittree_begin_resize();
1460 split_do_rqgeom_(sub
, &geom
, hany
, vany
, geomret
, tryonly
);
1463 split_do_resize(sub
, geomret
, hany
, vany
, FALSE
);
1464 splittree_end_resize();
1473 * Attempt to resize and/or move the split tree starting at \var{node}.
1474 * Behaviour and the \var{g} parameter are as for \fnref{WRegion.rqgeom}
1475 * operating on \var{node} (if it were a \type{WRegion}).
1478 ExtlTab
split_rqgeom(WSplit
*node
, ExtlTab g
)
1480 WRectangle geom
, ogeom
;
1481 int flags
=REGION_RQGEOM_WEAK_ALL
;
1486 if(extl_table_gets_i(g
, "x", &(geom
.x
)))
1487 flags
&=~REGION_RQGEOM_WEAK_X
;
1488 if(extl_table_gets_i(g
, "y", &(geom
.y
)))
1489 flags
&=~REGION_RQGEOM_WEAK_Y
;
1490 if(extl_table_gets_i(g
, "w", &(geom
.w
)))
1491 flags
&=~REGION_RQGEOM_WEAK_W
;
1492 if(extl_table_gets_i(g
, "h", &(geom
.h
)))
1493 flags
&=~REGION_RQGEOM_WEAK_H
;
1495 geom
.w
=maxof(1, geom
.w
);
1496 geom
.h
=maxof(1, geom
.h
);
1498 splittree_rqgeom(node
, flags
, &geom
, &ogeom
);
1500 return extl_table_from_rectangle(&ogeom
);
1503 warn(TR("Invalid node."));
1504 return extl_table_none();
1514 void splittree_changeroot(WSplit
*root
, WSplit
*node
)
1516 WTiling
*ws
=(WTiling
*)(root
->ws_if_root
);
1519 assert(ws
->split_tree
==root
);
1520 root
->ws_if_root
=NULL
;
1521 ws
->split_tree
=node
;
1523 node
->ws_if_root
=ws
;
1529 static void splitsplit_replace(WSplitSplit
*split
, WSplit
*child
,
1532 assert(split
->tl
==child
|| split
->br
==child
);
1534 if(split
->tl
==child
)
1541 what
->parent
=(WSplitInner
*)split
;
1542 what
->ws_if_root
=NULL
; /* May not be needed. */
1546 void splitinner_replace(WSplitInner
*split
, WSplit
*child
, WSplit
*what
)
1548 CALL_DYN(splitinner_replace
, split
, (split
, child
, what
));
1552 WSplitRegion
*splittree_split(WSplit
*node
, int dir
, WPrimn primn
,
1553 int minsize
, WRegionSimpleCreateFn
*fn
,
1558 WSplitSplit
*nsplit
;
1559 WSplitRegion
*nnode
;
1560 WSplitInner
*psplit
;
1565 assert(node
!=NULL
&& parent
!=NULL
);
1567 splittree_begin_resize();
1569 node
=dodge_stdisp(node
, FALSE
);
1574 if(OBJ_IS(node
, WSplitST
)){
1575 warn(TR("Splitting the status display is not allowed."));
1579 if(primn
!=PRIMN_TL
&& primn
!=PRIMN_BR
)
1581 if(dir
!=SPLIT_HORIZONTAL
&& dir
!=SPLIT_VERTICAL
)
1584 split_update_bounds(split_find_root(node
), TRUE
);
1585 objmin
=(dir
==SPLIT_VERTICAL
? node
->min_h
: node
->min_w
);
1587 s
=split_size(node
, dir
);
1588 sn
=maxof(minsize
, s
/2);
1589 so
=maxof(objmin
, s
-sn
);
1594 if(dir
==SPLIT_VERTICAL
)
1598 split_do_rqgeom_(node
, &ng
, TRUE
, TRUE
, &rg
, TRUE
);
1599 rs
=(dir
==SPLIT_VERTICAL
? rg
.h
: rg
.w
);
1600 if(rs
<minsize
+objmin
){
1601 warn(TR("Unable to split: not enough free space."));
1604 split_do_rqgeom_(node
, &ng
, TRUE
, TRUE
, &rg
, FALSE
);
1605 rs
=(dir
==SPLIT_VERTICAL
? rg
.h
: rg
.w
);
1610 so
=maxof(rs
/2, objmin
);
1615 splittree_scan_stdisp_rootward(node
);
1618 /* Create split and new window
1620 fp
.mode
=REGION_FIT_EXACT
;
1623 nsplit
=create_splitsplit(&(fp
.g
), dir
);
1628 if(dir
==SPLIT_VERTICAL
){
1638 nreg
=fn(parent
, &fp
);
1641 destroy_obj((Obj
*)nsplit
);
1645 nnode
=create_splitregion(&(fp
.g
), nreg
);
1647 destroy_obj((Obj
*)nreg
);
1648 destroy_obj((Obj
*)nsplit
);
1652 /* Now that everything's ok, resize and move original node.
1655 if(dir
==SPLIT_VERTICAL
){
1665 split_do_resize(node
, &ng
,
1666 (dir
==SPLIT_HORIZONTAL
? primn
: PRIMN_ANY
),
1667 (dir
==SPLIT_VERTICAL
? primn
: PRIMN_ANY
),
1670 /* Set up split structure
1672 psplit
=node
->parent
;
1675 splitinner_replace(psplit
, node
, (WSplit
*)nsplit
);
1677 splittree_changeroot(node
, (WSplit
*)nsplit
);
1679 node
->parent
=(WSplitInner
*)nsplit
;
1680 ((WSplit
*)nnode
)->parent
=(WSplitInner
*)nsplit
;
1682 if(primn
==PRIMN_BR
){
1684 nsplit
->br
=(WSplit
*)nnode
;
1685 nsplit
->current
=SPLIT_CURRENT_TL
;
1687 nsplit
->tl
=(WSplit
*)nnode
;
1689 nsplit
->current
=SPLIT_CURRENT_BR
;
1692 splittree_end_resize();
1704 static void splitsplit_remove(WSplitSplit
*node
, WSplit
*child
,
1707 static int nstdisp
=0;
1708 WSplitInner
*parent
;
1710 int hprimn
=PRIMN_ANY
, vprimn
=PRIMN_ANY
;
1712 assert(node
->tl
==child
|| node
->br
==child
);
1714 if(node
->tl
==child
){
1716 if(node
->dir
==SPLIT_VERTICAL
)
1722 if(node
->dir
==SPLIT_VERTICAL
)
1728 assert(other
!=NULL
);
1730 if(nstdisp
==0 && reclaim_space
&& OBJ_IS(other
, WSplitST
)){
1731 /* Try to move stdisp out of the way. */
1732 split_try_unsink_stdisp(node
, FALSE
, TRUE
);
1733 assert(child
->parent
!=NULL
);
1735 splitinner_remove(child
->parent
, child
, reclaim_space
);
1740 parent
=((WSplit
*)node
)->parent
;
1743 splitinner_replace(parent
, (WSplit
*)node
, other
);
1745 splittree_changeroot((WSplit
*)node
, other
);
1748 split_resize(other
, &(((WSplit
*)node
)->geom
), hprimn
, vprimn
);
1754 ((WSplit
*)node
)->parent
=NULL
;
1755 destroy_obj((Obj
*)node
);
1759 void splitinner_remove(WSplitInner
*node
, WSplit
*child
, bool reclaim_space
)
1761 CALL_DYN(splitinner_remove
, node
, (node
, child
, reclaim_space
));
1765 void splittree_remove(WSplit
*node
, bool reclaim_space
)
1767 if(node
->parent
!=NULL
)
1768 splitinner_remove(node
->parent
, node
, reclaim_space
);
1769 else if(node
->ws_if_root
!=NULL
)
1770 splittree_changeroot(node
, NULL
);
1772 destroy_obj((Obj
*)node
);
1779 /*{{{ Tree traversal */
1782 static bool defaultfilter(WSplit
*node
)
1784 return (OBJ_IS(node
, WSplitRegion
) &&
1785 ((WSplitRegion
*)node
)->reg
!=NULL
);
1789 static WSplit
*split_current_todir_default(WSplit
*node
,
1790 WPrimn hprimn
, WPrimn vprimn
,
1791 WSplitFilter
*filter
)
1794 filter
=defaultfilter
;
1796 return (filter(node
) ? node
: NULL
);
1800 static WSplit
*splitsplit_current_todir(WSplitSplit
*node
,
1801 WPrimn hprimn
, WPrimn vprimn
,
1802 WSplitFilter
*filter
)
1804 WPrimn primn
=(node
->dir
==SPLIT_HORIZONTAL
? hprimn
: vprimn
);
1805 WSplit
*first
, *second
, *ret
;
1807 if(primn
==PRIMN_TL
||
1808 (primn
==PRIMN_ANY
&& node
->current
==SPLIT_CURRENT_TL
)){
1811 }else if(primn
==PRIMN_BR
||
1812 (primn
==PRIMN_ANY
&& node
->current
==SPLIT_CURRENT_BR
)){
1819 ret
=split_current_todir(first
, hprimn
, vprimn
, filter
);
1821 ret
=split_current_todir(second
, hprimn
, vprimn
, filter
);
1822 if(ret
==NULL
&& filter
!=NULL
){
1823 if(filter((WSplit
*)node
))
1831 WSplit
*split_current_todir(WSplit
*node
, WPrimn hprimn
, WPrimn vprimn
,
1832 WSplitFilter
*filter
)
1835 CALL_DYN_RET(ret
, WSplit
*, split_current_todir
, node
,
1836 (node
, hprimn
, vprimn
, filter
));
1841 /* Note: both hprimn and vprimn are inverted when descending. Therefore
1842 * one should be either PRIMN_NONE or PRIMN_ANY for sensible geometric
1843 * navigation. (Both are PRIMN_TL or PRIMN_BR for pseudo-linear
1844 * next/previous navigation.)
1846 WSplit
*splitsplit_nextto(WSplitSplit
*node
, WSplit
*child
,
1847 WPrimn hprimn
, WPrimn vprimn
,
1848 WSplitFilter
*filter
)
1850 WPrimn primn
=(node
->dir
==SPLIT_HORIZONTAL
? hprimn
: vprimn
);
1851 WSplit
*split
=NULL
, *nnode
=NULL
;
1853 if(node
->tl
==child
&& (primn
==PRIMN_BR
|| primn
==PRIMN_ANY
))
1855 else if(node
->br
==child
&& (primn
==PRIMN_TL
|| primn
==PRIMN_ANY
))
1859 nnode
=split_current_todir(split
,
1860 primn_none2any(primn_invert(hprimn
)),
1861 primn_none2any(primn_invert(vprimn
)),
1866 nnode
=split_nextto((WSplit
*)node
, hprimn
, vprimn
, filter
);
1872 WSplit
*splitinner_nextto(WSplitInner
*node
, WSplit
*child
,
1873 WPrimn hprimn
, WPrimn vprimn
,
1874 WSplitFilter
*filter
)
1877 CALL_DYN_RET(ret
, WSplit
*, splitinner_nextto
, node
,
1878 (node
, child
, hprimn
, vprimn
, filter
));
1883 WSplit
*split_nextto(WSplit
*node
, WPrimn hprimn
, WPrimn vprimn
,
1884 WSplitFilter
*filter
)
1886 while(node
->parent
!=NULL
){
1887 WSplit
*ret
=splitinner_nextto(node
->parent
, node
,
1888 hprimn
, vprimn
, filter
);
1891 node
=(WSplit
*)node
->parent
;
1897 void splitinner_mark_current_default(WSplitInner
*split
, WSplit
*child
)
1899 if(((WSplit
*)split
)->parent
!=NULL
)
1900 splitinner_mark_current(((WSplit
*)split
)->parent
, (WSplit
*)split
);
1904 void splitsplit_mark_current(WSplitSplit
*split
, WSplit
*child
)
1906 assert(child
==split
->tl
|| child
==split
->br
);
1908 split
->current
=(split
->tl
==child
? SPLIT_CURRENT_TL
: SPLIT_CURRENT_BR
);
1910 splitinner_mark_current_default(&(split
->isplit
), child
);
1914 void splitinner_mark_current(WSplitInner
*split
, WSplit
*child
)
1916 CALL_DYN(splitinner_mark_current
, split
, (split
, child
));
1920 static void splitsplit_forall(WSplitSplit
*node
, WSplitFn
*fn
)
1927 void splitinner_forall(WSplitInner
*node
, WSplitFn
*fn
)
1929 CALL_DYN(splitinner_forall
, node
, (node
, fn
));
1933 static WSplit
*splitsplit_current(WSplitSplit
*split
)
1935 return (split
->current
==SPLIT_CURRENT_TL
? split
->tl
: split
->br
);
1940 * Returns the most previously active child node of \var{split}.
1944 WSplit
*splitinner_current(WSplitInner
*node
)
1947 CALL_DYN_RET(ret
, WSplit
*, splitinner_current
, node
, (node
));
1955 /*{{{ X window handling */
1958 static void splitregion_stacking(WSplitRegion
*split
,
1959 Window
*bottomret
, Window
*topret
)
1963 if(split
->reg
!=NULL
)
1964 region_stacking(split
->reg
, bottomret
, topret
);
1968 void splitsplit_stacking(WSplitSplit
*split
,
1969 Window
*bottomret
, Window
*topret
)
1971 Window tlb
=None
, tlt
=None
;
1972 Window brb
=None
, brt
=None
;
1974 split_stacking(split
->tl
, &tlb
, &tlt
);
1975 split_stacking(split
->br
, &brb
, &brt
);
1977 /* To make sure that this condition holds is left to the workspace
1978 * code to do after a split tree has been loaded or modified.
1980 if(split
->current
==SPLIT_CURRENT_TL
){
1981 *topret
=(tlt
!=None
? tlt
: brt
);
1982 *bottomret
=(brb
!=None
? brb
: tlb
);
1984 *topret
=(brt
!=None
? brt
: tlt
);
1985 *bottomret
=(tlb
!=None
? tlb
: brb
);
1989 void split_stacking(WSplit
*split
, Window
*bottomret
, Window
*topret
)
1994 CALL_DYN(split_stacking
, split
, (split
, bottomret
, topret
));
1999 static void splitregion_restack(WSplitRegion
*split
, Window other
, int mode
)
2001 if(split
->reg
!=NULL
)
2002 region_restack(split
->reg
, other
, mode
);
2005 void splitsplit_restack(WSplitSplit
*split
, Window other
, int mode
)
2007 Window bottom
=None
, top
=None
;
2008 WSplit
*first
, *second
;
2010 if(split
->current
==SPLIT_CURRENT_TL
){
2018 split_restack(first
, other
, mode
);
2019 split_stacking(first
, &bottom
, &top
);
2024 split_restack(second
, other
, mode
);
2027 void split_restack(WSplit
*split
, Window other
, int mode
)
2029 CALL_DYN(split_restack
, split
, (split
, other
, mode
));
2033 static void splitregion_map(WSplitRegion
*split
)
2035 if(split
->reg
!=NULL
)
2036 region_map(split
->reg
);
2039 static void splitinner_map(WSplitInner
*split
)
2041 splitinner_forall(split
, split_map
);
2044 void split_map(WSplit
*split
)
2046 CALL_DYN(split_map
, split
, (split
));
2050 static void splitregion_unmap(WSplitRegion
*split
)
2052 if(split
->reg
!=NULL
)
2053 region_unmap(split
->reg
);
2056 static void splitinner_unmap(WSplitInner
*split
)
2058 splitinner_forall(split
, split_unmap
);
2061 void split_unmap(WSplit
*split
)
2063 CALL_DYN(split_unmap
, split
, (split
));
2067 static void splitregion_reparent(WSplitRegion
*split
, WWindow
*wwin
)
2069 if(split
->reg
!=NULL
){
2070 WRectangle g
=split
->split
.geom
;
2071 region_reparent(split
->reg
, wwin
, &g
, REGION_FIT_EXACT
);
2076 static void splitsplit_reparent(WSplitSplit
*split
, WWindow
*wwin
)
2078 if(split
->current
==SPLIT_CURRENT_TL
){
2079 split_reparent(split
->br
, wwin
);
2080 split_reparent(split
->tl
, wwin
);
2082 split_reparent(split
->tl
, wwin
);
2083 split_reparent(split
->br
, wwin
);
2088 void split_reparent(WSplit
*split
, WWindow
*wwin
)
2090 CALL_DYN(split_reparent
, split
, (split
, wwin
));
2097 /*{{{ Transpose, flip, rotate */
2100 void splitsplit_flip_default(WSplitSplit
*split
)
2102 WRectangle tlng
, brng
;
2103 WRectangle
*sg
=&((WSplit
*)split
)->geom
;
2106 assert(split
->tl
!=NULL
&& split
->br
!=NULL
);
2108 split_update_bounds((WSplit
*)split
, TRUE
);
2110 tlng
=split
->tl
->geom
;
2111 brng
=split
->br
->geom
;
2113 if(split
->dir
==SPLIT_HORIZONTAL
){
2115 tlng
.x
=sg
->x
+sg
->w
-tlng
.w
;
2118 tlng
.y
=sg
->y
+sg
->h
-tlng
.h
;
2122 split
->tl
=split
->br
;
2124 split
->current
=(split
->current
==SPLIT_CURRENT_TL
2126 : SPLIT_CURRENT_TL
);
2128 split_do_resize(split
->tl
, &brng
, PRIMN_ANY
, PRIMN_ANY
, FALSE
);
2129 split_do_resize(split
->br
, &tlng
, PRIMN_ANY
, PRIMN_ANY
, FALSE
);
2133 static void splitsplit_flip_(WSplitSplit
*split
)
2135 CALL_DYN(splitsplit_flip
, split
, (split
));
2140 * Flip contents of \var{split}.
2143 void splitsplit_flip(WSplitSplit
*split
)
2145 splittree_begin_resize();
2147 split
=OBJ_CAST(dodge_stdisp((WSplit
*)split
, FALSE
), WSplitSplit
);
2152 splitsplit_flip_(split
);
2154 splittree_end_resize();
2165 static FlipDir flipdir
=FLIP_VERTICAL
;
2168 static void do_flip(WSplit
*split
)
2170 WSplitSplit
*ss
=OBJ_CAST(split
, WSplitSplit
);
2173 if((flipdir
==FLIP_ANY
2174 || (ss
->dir
==SPLIT_VERTICAL
&& flipdir
==FLIP_VERTICAL
)
2175 || (ss
->dir
==SPLIT_HORIZONTAL
&& flipdir
==FLIP_HORIZONTAL
))
2176 && !OBJ_IS(ss
->tl
, WSplitST
)
2177 && !OBJ_IS(ss
->br
, WSplitST
)){
2178 splitsplit_flip_(ss
);
2182 if(OBJ_IS(ss
, WSplitInner
))
2183 splitinner_forall((WSplitInner
*)ss
, do_flip
);
2187 static void splittree_flip_dir(WSplit
*splittree
, FlipDir dir
)
2189 /* todo stdisp outta way */
2190 if(OBJ_IS(splittree
, WSplitInner
)){
2192 splitinner_forall((WSplitInner
*)splittree
, do_flip
);
2197 static bool split_fliptrans_to(WSplit
*node
, const WRectangle
*geom
,
2198 bool trans
, FlipDir flip
)
2203 splittree_begin_resize();
2205 /* split_do_resize can do things right if 'node' has stdisp as child,
2206 * but otherwise transpose will put the stdisp in a bad split
2207 * configuration if it is contained within 'node', so we must
2208 * first move it and its fixed parent split below node. For correct
2209 * geometry calculation we move it immediately below node, and
2210 * resize stdisp's fixed parent node instead.
2212 node2
=dodge_stdisp(node
, TRUE
);
2214 if(node
==NULL
|| node2
!=node
)
2217 split_update_bounds(node
, TRUE
);
2219 split_do_rqgeom_(node
, geom
, PRIMN_ANY
, PRIMN_ANY
, &rg
, FALSE
);
2221 split_do_resize(node
, &rg
, PRIMN_ANY
, PRIMN_ANY
, trans
);
2224 splittree_flip_dir(node
, flip
);
2226 splittree_end_resize();
2232 bool split_transpose_to(WSplit
*node
, const WRectangle
*geom
)
2234 return split_fliptrans_to(node
, geom
, TRUE
, FLIP_ANY
);
2239 * Transpose contents of \var{node}.
2242 void split_transpose(WSplit
*node
)
2244 WRectangle g
=node
->geom
;
2246 split_transpose_to(node
, &g
);
2250 bool split_rotate_to(WSplit
*node
, const WRectangle
*geom
, int rotation
)
2252 FlipDir flip
=FLIP_NONE
;
2255 if(rotation
==SCREEN_ROTATION_90
){
2256 flip
=FLIP_HORIZONTAL
;
2258 }else if(rotation
==SCREEN_ROTATION_180
){
2260 }else if(rotation
==SCREEN_ROTATION_270
){
2265 return split_fliptrans_to(node
, geom
, trans
, flip
);
2275 * Return parent split for \var{split}.
2279 WSplitInner
*split_parent(WSplit
*split
)
2281 return split
->parent
;
2286 * Returns the area of workspace used by the regions under \var{split}.
2290 ExtlTab
split_geom(WSplit
*split
)
2292 return extl_table_from_rectangle(&(split
->geom
));
2297 * Returns the top or left child node of \var{split} depending
2298 * on the direction of the split.
2302 WSplit
*splitsplit_tl(WSplitSplit
*split
)
2309 * Returns the bottom or right child node of \var{split} depending
2310 * on the direction of the split.
2314 WSplit
*splitsplit_br(WSplitSplit
*split
)
2320 * Returns the direction of \var{split}; either \codestr{vertical} or
2321 * \codestr{horizontal}.
2325 const char *splitsplit_dir(WSplitSplit
*split
)
2327 return (split
->dir
==SPLIT_VERTICAL
? "vertical" : "horizontal");
2332 * Returns the region contained in \var{node}.
2336 WRegion
*splitregion_reg(WSplitRegion
*node
)
2345 /*{{{ Save support */
2348 ExtlTab
split_base_config(WSplit
*node
)
2350 ExtlTab t
=extl_create_table();
2351 extl_table_sets_s(t
, "type", OBJ_TYPESTR(node
));
2356 static bool splitregion_get_config(WSplitRegion
*node
, ExtlTab
*ret
)
2363 if(!region_supports_save(node
->reg
)){
2364 warn(TR("Unable to get configuration for %s."),
2365 region_name(node
->reg
));
2369 rt
=region_get_configuration(node
->reg
);
2370 t
=split_base_config(&(node
->split
));
2371 extl_table_sets_t(t
, "regparams", rt
);
2372 extl_unref_table(rt
);
2379 static bool splitst_get_config(WSplitST
*node
, ExtlTab
*ret
)
2381 *ret
=split_base_config((WSplit
*)node
);
2386 static bool splitsplit_get_config(WSplitSplit
*node
, ExtlTab
*ret
)
2388 ExtlTab tab
, tltab
, brtab
;
2391 if(!split_get_config(node
->tl
, &tltab
))
2392 return split_get_config(node
->br
, ret
);
2394 if(!split_get_config(node
->br
, &brtab
)){
2399 tab
=split_base_config((WSplit
*)node
);
2401 tls
=split_size(node
->tl
, node
->dir
);
2402 brs
=split_size(node
->br
, node
->dir
);
2404 extl_table_sets_s(tab
, "dir", (node
->dir
==SPLIT_VERTICAL
2405 ? "vertical" : "horizontal"));
2407 extl_table_sets_i(tab
, "tls", tls
);
2408 extl_table_sets_t(tab
, "tl", tltab
);
2409 extl_unref_table(tltab
);
2411 extl_table_sets_i(tab
, "brs", brs
);
2412 extl_table_sets_t(tab
, "br", brtab
);
2413 extl_unref_table(brtab
);
2421 bool split_get_config(WSplit
*node
, ExtlTab
*tabret
)
2424 CALL_DYN_RET(ret
, bool, split_get_config
, node
, (node
, tabret
));
2432 /*{{{ The classes */
2435 static DynFunTab split_dynfuntab
[]={
2436 {split_do_resize
, split_do_resize_default
},
2437 {(DynFun
*)split_current_todir
, (DynFun
*)split_current_todir_default
},
2441 static DynFunTab splitinner_dynfuntab
[]={
2442 {splitinner_mark_current
, splitinner_mark_current_default
},
2443 {split_map
, splitinner_map
},
2444 {split_unmap
, splitinner_unmap
},
2448 static DynFunTab splitsplit_dynfuntab
[]={
2449 {split_update_bounds
, splitsplit_update_bounds
},
2450 {split_do_resize
, splitsplit_do_resize
},
2451 {split_do_maxhelper
, splitsplit_do_maxhelper
},
2452 {(DynFun
*)split_do_restore
, (DynFun
*)splitsplit_do_restore
},
2453 {(DynFun
*)split_do_verify
, (DynFun
*)splitsplit_do_verify
},
2454 {splitinner_do_rqsize
, splitsplit_do_rqsize
},
2455 {splitinner_replace
, splitsplit_replace
},
2456 {splitinner_remove
, splitsplit_remove
},
2457 {(DynFun
*)split_current_todir
, (DynFun
*)splitsplit_current_todir
},
2458 {(DynFun
*)splitinner_current
, (DynFun
*)splitsplit_current
},
2459 {(DynFun
*)splitinner_nextto
, (DynFun
*)splitsplit_nextto
},
2460 {splitinner_mark_current
, splitsplit_mark_current
},
2461 {(DynFun
*)split_get_config
, (DynFun
*)splitsplit_get_config
},
2462 {splitinner_forall
, splitsplit_forall
},
2463 {split_restack
, splitsplit_restack
},
2464 {split_stacking
, splitsplit_stacking
},
2465 {split_reparent
, splitsplit_reparent
},
2466 {splitsplit_flip
, splitsplit_flip_default
},
2470 static DynFunTab splitregion_dynfuntab
[]={
2471 {split_update_bounds
, splitregion_update_bounds
},
2472 {split_do_resize
, splitregion_do_resize
},
2473 {split_do_maxhelper
, splitregion_do_maxhelper
},
2474 {(DynFun
*)split_do_restore
, (DynFun
*)splitregion_do_restore
},
2475 {(DynFun
*)split_do_verify
, (DynFun
*)splitregion_do_verify
},
2476 {(DynFun
*)split_get_config
, (DynFun
*)splitregion_get_config
},
2477 {split_map
, splitregion_map
},
2478 {split_unmap
, splitregion_unmap
},
2479 {split_restack
, splitregion_restack
},
2480 {split_stacking
, splitregion_stacking
},
2481 {split_reparent
, splitregion_reparent
},
2485 static DynFunTab splitst_dynfuntab
[]={
2486 {split_update_bounds
, splitst_update_bounds
},
2487 {split_do_resize
, splitst_do_resize
},
2488 {split_do_maxhelper
, splitst_do_maxhelper
},
2489 {(DynFun
*)split_do_restore
, (DynFun
*)splitst_do_restore
},
2490 {(DynFun
*)split_do_verify
, (DynFun
*)splitst_do_verify
},
2491 {(DynFun
*)split_get_config
, (DynFun
*)splitst_get_config
},
2497 IMPLCLASS(WSplit
, Obj
, split_deinit
, split_dynfuntab
);
2500 IMPLCLASS(WSplitInner
, WSplit
, splitinner_deinit
, splitinner_dynfuntab
);
2503 IMPLCLASS(WSplitSplit
, WSplitInner
, splitsplit_deinit
, splitsplit_dynfuntab
);
2506 IMPLCLASS(WSplitRegion
, WSplit
, splitregion_deinit
, splitregion_dynfuntab
);
2509 IMPLCLASS(WSplitST
, WSplitRegion
, splitst_deinit
, splitst_dynfuntab
);