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 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 /*{{{ Save, restore and verify code for maximization */
744 bool splits_are_related(WSplit
*p
, WSplit
*node
)
751 ? splits_are_related(p
, (WSplit
*)node
->parent
)
756 WSplit
*maxparentdir_rel(WSplit
*p
, WSplit
*node
, int dir
)
758 /* Descending from p, try to determine the first split of type dir between
759 * p and node, while ignoring a potential stdisp. */
760 if(OBJ_IS(p
, WSplitSplit
)){
761 WSplitSplit
*sp
=(WSplitSplit
*)p
;
762 assert(sp
->tl
!=NULL
&& sp
->br
!=NULL
);
763 assert(splits_are_related(sp
->tl
, node
) ||
764 splits_are_related(sp
->br
, node
));
766 if(OBJ_IS(sp
->tl
, WSplitST
))
767 return maxparentdir_rel(sp
->br
, node
, dir
);
768 if(OBJ_IS(sp
->br
, WSplitST
))
769 return maxparentdir_rel(sp
->tl
, node
, dir
);
773 splits_are_related(sp
->tl
, node
)
774 ? maxparentdir_rel(sp
->tl
, node
, dir
)
775 : maxparentdir_rel(sp
->br
, node
, dir
);
783 WSplit
*maxparent(WSplit
*node
)
785 WSplit
*p
=(WSplit
*)node
->parent
;
786 return p
==NULL
? node
: maxparent(p
);
790 WSplit
*maxparentdir(WSplit
*node
, int dir
)
792 return maxparentdir_rel(maxparent(node
), node
, dir
);
795 int *wh(WRectangle
*geom
, int orientation
)
797 return orientation
==REGION_ORIENTATION_HORIZONTAL
? &geom
->w
: &geom
->h
;
800 int *xy(WRectangle
*geom
, int orientation
)
802 return orientation
==REGION_ORIENTATION_HORIZONTAL
? &geom
->x
: &geom
->y
;
805 bool is_lt(int orientation
, int corner
)
807 /* Read as "is_left" or "is_top", depending on the orientation. */
809 orientation
==REGION_ORIENTATION_HORIZONTAL
810 ? corner
==MPLEX_STDISP_TL
|| corner
==MPLEX_STDISP_BL
811 : corner
==MPLEX_STDISP_TL
|| corner
==MPLEX_STDISP_TR
;
814 int flip_orientation(int orientation
)
817 orientation
==REGION_ORIENTATION_HORIZONTAL
818 ? REGION_ORIENTATION_VERTICAL
819 : REGION_ORIENTATION_HORIZONTAL
;
822 WRectangle
stdisp_recommended_geom(WSplitST
*st
, WRectangle wsg
)
824 /* wsg holds the geometry of the workspace that st is on. */
825 WRectangle stg
=REGION_GEOM(st
->regnode
.reg
);
826 int ori
=st
->orientation
;
827 stg
.w
=stdisp_recommended_w(st
);
828 stg
.h
=stdisp_recommended_h(st
);
830 if(!is_lt(ori
, st
->corner
))
831 *xy(&stg
, ori
)=*wh(&wsg
, ori
)-*wh(&stg
, ori
);
836 bool geom_overlaps_stgeom_xy(WRectangle geom
, WSplitST
*st
, WRectangle stg
)
847 int ori
=st
->orientation
;
850 is_lt(ori
, st
->corner
)
851 ? *xy(&geom
, ori
)<*wh(&stg
, ori
)
852 : *xy(&geom
, ori
)+*wh(&geom
, ori
)>*xy(&stg
, ori
);
855 bool geom_aligned_stdisp(WRectangle geom
, WSplitST
*st
)
863 * ----- ------ ----- |geom| -----
864 * |stg| |stg| ------ |stg|
868 WRectangle stg
=REGION_GEOM(st
->regnode
.reg
);
869 int ori
=flip_orientation(st
->orientation
);
872 is_lt(ori
, st
->corner
)
873 ? *xy(&geom
, ori
)==*wh(&stg
, ori
)
874 : *xy(&geom
, ori
)+*wh(&geom
, ori
)==*xy(&stg
, ori
);
877 void grow_by_stdisp_wh(WRectangle
*geom
, WSplitST
*st
)
884 * ----- ------ ----- |geom|
889 WRectangle stg
=REGION_GEOM(st
->regnode
.reg
);
890 int ori
=flip_orientation(st
->orientation
);
892 if(is_lt(ori
, st
->corner
))
894 *wh(geom
, ori
)+=*wh(&stg
, ori
);
897 bool frame_neighbors_stdisp(WFrame
*frame
, WSplitST
*st
)
900 geom_overlaps_stgeom_xy(REGION_GEOM(frame
), st
, REGION_GEOM(st
)) &&
901 geom_aligned_stdisp(REGION_GEOM(frame
), st
);
904 bool geom_clashes_stdisp(WRectangle geom
, WSplitST
*st
)
906 WRectangle stg
=REGION_GEOM(st
->regnode
.reg
);
907 int ori
=flip_orientation(st
->orientation
);
909 is_lt(ori
, st
->corner
)
911 : *xy(&geom
, ori
)+*wh(&geom
, ori
)==*xy(&stg
, ori
)+*wh(&stg
, ori
);
914 bool is_same_dir(int dir
, int ori
)
917 (dir
==SPLIT_HORIZONTAL
&& ori
==REGION_ORIENTATION_HORIZONTAL
) ||
918 (dir
==SPLIT_VERTICAL
&& ori
==REGION_ORIENTATION_VERTICAL
);
921 bool is_maxed(WFrame
*frame
, int dir
)
924 dir
==SPLIT_HORIZONTAL
925 ? frame
->flags
&FRAME_MAXED_HORIZ
&& frame
->flags
&FRAME_SAVED_HORIZ
926 : frame
->flags
&FRAME_MAXED_VERT
&& frame
->flags
&FRAME_SAVED_VERT
;
929 bool update_geom_from_stdisp(WFrame
*frame
, WRectangle
*ng
, int dir
)
931 WRegion
*ws
=REGION_MANAGER(frame
);
938 if(!OBJ_IS(ws
, WTiling
) || ((WTiling
*)ws
)->stdispnode
==NULL
)
941 st
=((WTiling
*)ws
)->stdispnode
;
943 if(st
->fullsize
|| !frame_neighbors_stdisp(frame
, st
))
946 rstg
=stdisp_recommended_geom(st
, ws
->geom
);
948 if(is_same_dir(dir
, st
->orientation
) &&
949 !geom_overlaps_stgeom_xy(*ng
, st
, rstg
))
951 grow_by_stdisp_wh(ng
, st
);
952 if(is_maxed(frame
, other_dir(dir
)) &&
953 geom_aligned_stdisp(frame
->saved_geom
, st
))
955 grow_by_stdisp_wh(&frame
->saved_geom
, st
);
960 if(!is_same_dir(dir
, st
->orientation
) &&
961 geom_clashes_stdisp(frame
->saved_geom
, st
))
963 stg
=REGION_GEOM(st
->regnode
.reg
);
964 ori
=flip_orientation(st
->orientation
);
965 if(is_lt(ori
, st
->corner
))
966 *xy(ng
, ori
)+=*wh(&stg
, ori
);
967 /* We've checked that this makes sense when verifying the saved layout. */
968 *wh(ng
, ori
)-=*wh(&stg
, ori
);
974 bool splitregion_do_restore(WSplitRegion
*node
, int dir
)
977 WRectangle geom
=((WSplit
*)node
)->geom
;
982 if(!OBJ_IS(node
->reg
, WFrame
))
985 frame
=(WFrame
*)node
->reg
;
986 if(dir
==SPLIT_HORIZONTAL
){
987 geom
.x
=frame
->saved_geom
.x
;
988 geom
.w
=frame
->saved_geom
.w
;
990 geom
.y
=frame
->saved_geom
.y
;
991 geom
.h
=frame
->saved_geom
.h
;
995 dir
==SPLIT_HORIZONTAL
996 ? frame
->flags
&FRAME_MAXED_VERT
997 : frame
->flags
&FRAME_MAXED_HORIZ
;
1000 ret
=update_geom_from_stdisp(frame
, &geom
, dir
);
1002 /* Tell the region the correct geometry to avoid redrawing it again when
1003 * the stdisp is resized by split_regularise_stdisp. Some clients (notably
1004 * ncurses based ones) don't seem to react well to being resized multiple
1005 * times within a short amount of time and don't refresh themselves
1007 region_fit(node
->reg
, &geom
, REGION_FIT_EXACT
);
1009 split_update_bounds(&(node
->split
), FALSE
);
1011 /* Keep the old geometry for the WSplit. Otherwise the tiling would be
1012 * inconsistent, by for example having horizontal WSplitSplit's whose
1013 * children have different heights. The call to split_regularise_stdisp
1014 * below will take care of correcting the geometry of the WSplit and it
1015 * behaves badly when the tiling is inconsistent. */
1016 node
->split
.geom
=ret
? fakegeom
: geom
;
1018 frame
->flags
|=other_max
;
1022 bool splitst_do_restore(WSplit
*node
, int dir
)
1027 bool splitsplit_do_restore(WSplitSplit
*node
, int dir
)
1029 bool ret1
, ret2
, ret
=FALSE
;
1030 WSplit
*snode
=(WSplit
*)node
;
1037 assert(node
->tl
!=NULL
&& node
->br
!=NULL
);
1039 if(stdisp_immediate_child(node
)){
1040 if(OBJ_IS(node
->tl
, WSplitST
)){
1041 st
=(WSplitST
*)node
->tl
;
1044 st
=(WSplitST
*)node
->br
;
1047 stg
=((WSplit
*)st
)->geom
;
1048 split_do_restore(other
, dir
);
1050 if(node
->dir
==SPLIT_HORIZONTAL
){
1057 if(rectangle_compare(&stg
, &((WSplit
*)st
)->geom
)){
1058 splitst_do_resize(st
, &stg
, PRIMN_ANY
, PRIMN_ANY
, FALSE
);
1062 /* Avoid short-circuit evaluation. */
1063 ret1
=split_do_restore(node
->tl
, dir
);
1064 ret2
=split_do_restore(node
->br
, dir
);
1068 snode
->geom
.x
=node
->tl
->geom
.x
;
1069 snode
->geom
.y
=node
->tl
->geom
.y
;
1070 if(node
->dir
==SPLIT_HORIZONTAL
){
1071 snode
->geom
.w
=node
->tl
->geom
.w
+node
->br
->geom
.w
;
1072 snode
->geom
.h
=node
->tl
->geom
.h
;
1074 if(node
->dir
==SPLIT_VERTICAL
){
1075 snode
->geom
.w
=node
->tl
->geom
.w
;
1076 snode
->geom
.h
=node
->tl
->geom
.h
+node
->br
->geom
.h
;
1082 bool split_do_restore(WSplit
*node
, int dir
)
1085 CALL_DYN_RET(ret
, bool, split_do_restore
, node
, (node
, dir
));
1090 void splitregion_do_maxhelper(WSplitRegion
*node
, int dir
, int action
)
1093 if(!OBJ_IS(node
->reg
, WFrame
))
1095 frame
=(WFrame
*)node
->reg
;
1098 frame
->flags
|=FRAME_KEEP_FLAGS
;
1099 if(dir
==HORIZONTAL
){
1100 frame
->flags
|=(FRAME_MAXED_HORIZ
|FRAME_SAVED_HORIZ
);
1101 frame
->saved_geom
.x
=REGION_GEOM(frame
).x
;
1102 frame
->saved_geom
.w
=REGION_GEOM(frame
).w
;
1104 frame
->flags
|=(FRAME_MAXED_VERT
|FRAME_SAVED_VERT
);
1105 frame
->saved_geom
.y
=REGION_GEOM(frame
).y
;
1106 frame
->saved_geom
.h
=REGION_GEOM(frame
).h
;
1109 if(action
==SET_KEEP
)
1110 frame
->flags
|=FRAME_KEEP_FLAGS
;
1112 frame
->flags
&=~FRAME_KEEP_FLAGS
;
1115 void splitst_do_maxhelper(WSplit
*node
, int dir
, int action
)
1120 void splitsplit_do_maxhelper(WSplitSplit
*node
, int dir
, int action
)
1122 assert(node
->tl
!=NULL
&& node
->br
!=NULL
);
1123 split_do_maxhelper(node
->tl
, dir
, action
);
1124 split_do_maxhelper(node
->br
, dir
, action
);
1127 void split_do_maxhelper(WSplit
*node
, int dir
, int action
)
1129 CALL_DYN(split_do_maxhelper
, node
, (node
, dir
, action
));
1133 bool savedgeom_clashes_stdisp(WFrame
*frame
, int dir
)
1135 WRegion
*ws
=REGION_MANAGER(frame
);
1139 if(!OBJ_IS(ws
, WTiling
) || ((WTiling
*)ws
)->stdispnode
==NULL
)
1142 st
=((WTiling
*)ws
)->stdispnode
;
1143 ori
=flip_orientation(st
->orientation
);
1146 !is_same_dir(dir
, st
->orientation
) &&
1147 frame_neighbors_stdisp(frame
, st
) &&
1148 geom_clashes_stdisp(frame
->saved_geom
, st
)
1149 ? *wh(&frame
->saved_geom
, ori
)<*wh(®ION_GEOM(st
), ori
)
1153 bool splitregion_do_verify(WSplitRegion
*node
, int dir
)
1158 if(!OBJ_IS(node
->reg
, WFrame
))
1161 frame
=(WFrame
*)node
->reg
;
1163 ret
=is_maxed(frame
, dir
);
1166 frame
->flags
&=~(FRAME_MAXED_HORIZ
|FRAME_SAVED_HORIZ
);
1168 frame
->flags
&=~(FRAME_MAXED_VERT
|FRAME_SAVED_VERT
);
1170 if(savedgeom_clashes_stdisp(frame
, dir
))
1176 bool splitst_do_verify(WSplit
*node
, int dir
)
1181 bool splitsplit_do_verify(WSplitSplit
*node
, int dir
)
1184 assert(node
->tl
!=NULL
&& node
->br
!=NULL
);
1186 /* Avoid short-circuit evaluation. */
1187 ret1
=split_do_verify(node
->tl
, dir
);
1188 ret2
=split_do_verify(node
->br
, dir
);
1189 return ret1
&& ret2
;
1192 bool split_do_verify(WSplit
*node
, int dir
)
1195 CALL_DYN_RET(ret
, bool, split_do_verify
, node
, (node
, dir
));
1200 bool split_maximize(WSplit
*node
, int dir
, int action
)
1202 WSplit
*p
=maxparentdir(node
, dir
);
1204 return split_do_restore(p
, dir
);
1206 return split_do_verify(p
, dir
);
1208 split_do_maxhelper(p
, dir
, action
);
1216 /*{{{ Low-level resize code; request towards root */
1219 static void flexibility(WSplit
*node
, int dir
, int *shrink
, int *stretch
)
1221 if(dir
==SPLIT_VERTICAL
){
1222 *shrink
=maxof(0, node
->geom
.h
-node
->min_h
);
1223 if(OBJ_IS(node
, WSplitST
))
1224 *stretch
=maxof(0, node
->max_h
-node
->geom
.h
);
1228 *shrink
=maxof(0, node
->geom
.w
-node
->min_w
);
1229 if(OBJ_IS(node
, WSplitST
))
1230 *stretch
=maxof(0, node
->max_w
-node
->geom
.w
);
1237 static void calc_amount(int *amount
, int rs
, WSplit
*other
, int dir
)
1239 int shrink
, stretch
;
1241 flexibility(other
, dir
, &shrink
, &stretch
);
1244 *amount
=minof(rs
, shrink
);
1246 *amount
=-minof(-rs
, stretch
);
1253 static void splitsplit_do_rqsize(WSplitSplit
*p
, WSplit
*node
,
1254 RootwardAmount
*ha
, RootwardAmount
*va
,
1255 WRectangle
*rg
, bool tryonly
)
1257 WPrimn hprimn
=PRIMN_ANY
, vprimn
=PRIMN_ANY
;
1258 WRectangle og
, pg
, ng
;
1264 assert(!ha
->any
|| ha
->tl
==0);
1265 assert(!va
->any
|| va
->tl
==0);
1266 assert(p
->tl
==node
|| p
->br
==node
);
1276 ca
=(p
->dir
==SPLIT_VERTICAL
? va
: ha
);
1278 if(thisnode
==PRIMN_TL
|| ca
->any
){
1279 calc_amount(&amount
, ca
->br
, other
, p
->dir
);
1281 }else/*if(thisnode==PRIMN_BR)*/{
1282 calc_amount(&amount
, ca
->tl
, other
, p
->dir
);
1286 if(((WSplit
*)p
)->parent
==NULL
/*||
1287 (ha->tl==0 && ha->br==0 && va->tl==0 && va->br==0)*/){
1288 if(((WSplit
*)p
)->ws_if_root
!=NULL
)
1289 pg
=REGION_GEOM((WTiling
*)(((WSplit
*)p
)->ws_if_root
));
1291 pg
=((WSplit
*)p
)->geom
;
1293 splitinner_do_rqsize(((WSplit
*)p
)->parent
, (WSplit
*)p
, ha
, va
,
1297 assert(pg
.w
>=0 && pg
.h
>=0);
1302 if(p
->dir
==SPLIT_VERTICAL
){
1303 ng
.h
=maxof(0, node
->geom
.h
+amount
);
1304 og
.h
=maxof(0, other
->geom
.h
-amount
);
1305 adjust_sizes(&(ng
.h
), &(og
.h
), pg
.h
, ng
.h
+og
.h
,
1306 node
->min_h
, other
->min_h
, node
->max_h
, other
->max_h
,
1307 PRIMN_TL
/* node is passed as tl param */);
1308 if(thisnode
==PRIMN_TL
)
1309 og
.y
=pg
.y
+pg
.h
-og
.h
;
1311 ng
.y
=pg
.y
+pg
.h
-ng
.h
;
1314 ng
.w
=maxof(0, node
->geom
.w
+amount
);
1315 og
.w
=maxof(0, other
->geom
.w
-amount
);
1316 adjust_sizes(&(ng
.w
), &(og
.w
), pg
.w
, ng
.w
+og
.w
,
1317 node
->min_w
, other
->min_w
, node
->max_w
, other
->max_w
,
1318 PRIMN_TL
/* node is passed as tl param */);
1319 if(thisnode
==PRIMN_TL
)
1320 og
.x
=pg
.x
+pg
.w
-og
.w
;
1322 ng
.x
=pg
.x
+pg
.w
-ng
.w
;
1327 /* Entä jos 'other' on stdisp? */
1328 split_do_resize(other
, &og
, hprimn
, vprimn
, FALSE
);
1330 ((WSplit
*)p
)->geom
=pg
;
1337 void splitinner_do_rqsize(WSplitInner
*p
, WSplit
*node
,
1338 RootwardAmount
*ha
, RootwardAmount
*va
,
1339 WRectangle
*rg
, bool tryonly
)
1341 CALL_DYN(splitinner_do_rqsize
, p
, (p
, node
, ha
, va
, rg
, tryonly
));
1345 static void initra(RootwardAmount
*ra
, int p
, int s
, int op
, int os
,
1350 ra
->br
=(p
+s
)-(op
+os
);
1358 void split_do_rqgeom_(WSplit
*node
, const WRectangle
*ng
,
1359 bool hany
, bool vany
, WRectangle
*rg
,
1362 RootwardAmount ha
, va
;
1364 if(node
->parent
==NULL
){
1365 if(node
->ws_if_root
!=NULL
)
1366 *rg
=REGION_GEOM((WTiling
*)(node
->ws_if_root
));
1370 initra(&ha
, ng
->x
, ng
->w
, node
->geom
.x
, node
->geom
.w
, hany
);
1371 initra(&va
, ng
->y
, ng
->h
, node
->geom
.y
, node
->geom
.h
, vany
);
1373 splitinner_do_rqsize(node
->parent
, node
, &ha
, &va
, rg
, tryonly
);
1381 /*{{{ Resize interface */
1384 static void bnd(int *pos
, int *sz
, int opos
, int osz
, int minsz
, int maxsz
)
1386 int ud
=abs(*pos
-opos
);
1387 int dd
=abs((*pos
+*sz
)-(opos
+osz
));
1391 bound(sz
, minsz
, maxsz
);
1392 *pos
+=(szrq
-*sz
)*ud
/(ud
+dd
);
1397 WSplit
*split_find_root(WSplit
*split
)
1399 if(split
->parent
==NULL
)
1401 return split_find_root((WSplit
*)split
->parent
);
1405 void splittree_rqgeom(WSplit
*sub
, int flags
, const WRectangle
*geom_
,
1406 WRectangle
*geomret
)
1408 bool hany
=flags
®ION_RQGEOM_WEAK_X
;
1409 bool vany
=flags
®ION_RQGEOM_WEAK_Y
;
1410 bool tryonly
=flags
®ION_RQGEOM_TRYONLY
;
1411 WRectangle geom
=*geom_
;
1413 WSplit
*root
=split_find_root(sub
);
1418 split_update_bounds(root
, TRUE
);
1420 if(OBJ_IS(sub
, WSplitST
)){
1421 WSplitST
*sub_as_stdisp
=(WSplitST
*)sub
;
1423 if(flags
®ION_RQGEOM_TRYONLY
){
1424 warn(TR("REGION_RQGEOM_TRYONLY unsupported for status display."));
1428 split_regularise_stdisp(sub_as_stdisp
);
1430 if(sub_as_stdisp
->orientation
==REGION_ORIENTATION_HORIZONTAL
){
1431 if(geom_
->h
==geom
.h
)
1435 if(geom_
->w
==geom
.w
)
1439 split_update_bounds(root
, TRUE
);
1442 /* Handle internal size bounds */
1443 bnd(&(geom
.x
), &(geom
.w
), sub
->geom
.x
, sub
->geom
.w
,
1444 sub
->min_w
, sub
->max_w
);
1445 bnd(&(geom
.y
), &(geom
.h
), sub
->geom
.y
, sub
->geom
.h
,
1446 sub
->min_h
, sub
->max_h
);
1448 /* Check if we should resize to both tl and br */
1451 geom
.w
+=sub
->geom
.x
-geom
.x
;
1456 geom
.h
+=sub
->geom
.y
-geom
.y
;
1460 splittree_begin_resize();
1462 split_do_rqgeom_(sub
, &geom
, hany
, vany
, geomret
, tryonly
);
1465 split_do_resize(sub
, geomret
, hany
, vany
, FALSE
);
1466 splittree_end_resize();
1475 * Attempt to resize and/or move the split tree starting at \var{node}.
1476 * Behaviour and the \var{g} parameter are as for \fnref{WRegion.rqgeom}
1477 * operating on \var{node} (if it were a \type{WRegion}).
1480 ExtlTab
split_rqgeom(WSplit
*node
, ExtlTab g
)
1482 WRectangle geom
, ogeom
;
1483 int flags
=REGION_RQGEOM_WEAK_ALL
;
1488 if(extl_table_gets_i(g
, "x", &(geom
.x
)))
1489 flags
&=~REGION_RQGEOM_WEAK_X
;
1490 if(extl_table_gets_i(g
, "y", &(geom
.y
)))
1491 flags
&=~REGION_RQGEOM_WEAK_Y
;
1492 if(extl_table_gets_i(g
, "w", &(geom
.w
)))
1493 flags
&=~REGION_RQGEOM_WEAK_W
;
1494 if(extl_table_gets_i(g
, "h", &(geom
.h
)))
1495 flags
&=~REGION_RQGEOM_WEAK_H
;
1497 geom
.w
=maxof(1, geom
.w
);
1498 geom
.h
=maxof(1, geom
.h
);
1500 splittree_rqgeom(node
, flags
, &geom
, &ogeom
);
1502 return extl_table_from_rectangle(&ogeom
);
1505 warn(TR("Invalid node."));
1506 return extl_table_none();
1516 void splittree_changeroot(WSplit
*root
, WSplit
*node
)
1518 WTiling
*ws
=(WTiling
*)(root
->ws_if_root
);
1521 assert(ws
->split_tree
==root
);
1522 root
->ws_if_root
=NULL
;
1523 ws
->split_tree
=node
;
1525 node
->ws_if_root
=ws
;
1531 static void splitsplit_replace(WSplitSplit
*split
, WSplit
*child
,
1534 assert(split
->tl
==child
|| split
->br
==child
);
1536 if(split
->tl
==child
)
1543 what
->parent
=(WSplitInner
*)split
;
1544 what
->ws_if_root
=NULL
; /* May not be needed. */
1548 void splitinner_replace(WSplitInner
*split
, WSplit
*child
, WSplit
*what
)
1550 CALL_DYN(splitinner_replace
, split
, (split
, child
, what
));
1554 WSplitRegion
*splittree_split(WSplit
*node
, int dir
, WPrimn primn
,
1555 int minsize
, WRegionSimpleCreateFn
*fn
,
1560 WSplitSplit
*nsplit
;
1561 WSplitRegion
*nnode
;
1562 WSplitInner
*psplit
;
1567 assert(node
!=NULL
&& parent
!=NULL
);
1569 splittree_begin_resize();
1571 node
=dodge_stdisp(node
, FALSE
);
1576 if(OBJ_IS(node
, WSplitST
)){
1577 warn(TR("Splitting the status display is not allowed."));
1581 if(primn
!=PRIMN_TL
&& primn
!=PRIMN_BR
)
1583 if(dir
!=SPLIT_HORIZONTAL
&& dir
!=SPLIT_VERTICAL
)
1586 split_update_bounds(split_find_root(node
), TRUE
);
1587 objmin
=(dir
==SPLIT_VERTICAL
? node
->min_h
: node
->min_w
);
1589 s
=split_size(node
, dir
);
1590 sn
=maxof(minsize
, s
/2);
1591 so
=maxof(objmin
, s
-sn
);
1596 if(dir
==SPLIT_VERTICAL
)
1600 split_do_rqgeom_(node
, &ng
, TRUE
, TRUE
, &rg
, TRUE
);
1601 rs
=(dir
==SPLIT_VERTICAL
? rg
.h
: rg
.w
);
1602 if(rs
<minsize
+objmin
){
1603 warn(TR("Unable to split: not enough free space."));
1606 split_do_rqgeom_(node
, &ng
, TRUE
, TRUE
, &rg
, FALSE
);
1607 rs
=(dir
==SPLIT_VERTICAL
? rg
.h
: rg
.w
);
1612 so
=maxof(rs
/2, objmin
);
1617 splittree_scan_stdisp_rootward(node
);
1620 /* Create split and new window
1622 fp
.mode
=REGION_FIT_EXACT
;
1625 nsplit
=create_splitsplit(&(fp
.g
), dir
);
1630 if(dir
==SPLIT_VERTICAL
){
1640 nreg
=fn(parent
, &fp
);
1643 destroy_obj((Obj
*)nsplit
);
1647 nnode
=create_splitregion(&(fp
.g
), nreg
);
1649 destroy_obj((Obj
*)nreg
);
1650 destroy_obj((Obj
*)nsplit
);
1654 /* Now that everything's ok, resize and move original node.
1657 if(dir
==SPLIT_VERTICAL
){
1667 split_do_resize(node
, &ng
,
1668 (dir
==SPLIT_HORIZONTAL
? primn
: PRIMN_ANY
),
1669 (dir
==SPLIT_VERTICAL
? primn
: PRIMN_ANY
),
1672 /* Set up split structure
1674 psplit
=node
->parent
;
1677 splitinner_replace(psplit
, node
, (WSplit
*)nsplit
);
1679 splittree_changeroot(node
, (WSplit
*)nsplit
);
1681 node
->parent
=(WSplitInner
*)nsplit
;
1682 ((WSplit
*)nnode
)->parent
=(WSplitInner
*)nsplit
;
1684 if(primn
==PRIMN_BR
){
1686 nsplit
->br
=(WSplit
*)nnode
;
1687 nsplit
->current
=SPLIT_CURRENT_TL
;
1689 nsplit
->tl
=(WSplit
*)nnode
;
1691 nsplit
->current
=SPLIT_CURRENT_BR
;
1694 splittree_end_resize();
1706 static void splitsplit_remove(WSplitSplit
*node
, WSplit
*child
,
1709 static int nstdisp
=0;
1710 WSplitInner
*parent
;
1712 int hprimn
=PRIMN_ANY
, vprimn
=PRIMN_ANY
;
1714 assert(node
->tl
==child
|| node
->br
==child
);
1716 if(node
->tl
==child
){
1718 if(node
->dir
==SPLIT_VERTICAL
)
1724 if(node
->dir
==SPLIT_VERTICAL
)
1730 assert(other
!=NULL
);
1732 if(nstdisp
==0 && reclaim_space
&& OBJ_IS(other
, WSplitST
)){
1733 /* Try to move stdisp out of the way. */
1734 split_try_unsink_stdisp(node
, FALSE
, TRUE
);
1735 assert(child
->parent
!=NULL
);
1737 splitinner_remove(child
->parent
, child
, reclaim_space
);
1742 parent
=((WSplit
*)node
)->parent
;
1745 splitinner_replace(parent
, (WSplit
*)node
, other
);
1747 splittree_changeroot((WSplit
*)node
, other
);
1750 split_resize(other
, &(((WSplit
*)node
)->geom
), hprimn
, vprimn
);
1756 ((WSplit
*)node
)->parent
=NULL
;
1757 destroy_obj((Obj
*)node
);
1761 void splitinner_remove(WSplitInner
*node
, WSplit
*child
, bool reclaim_space
)
1763 CALL_DYN(splitinner_remove
, node
, (node
, child
, reclaim_space
));
1767 void splittree_remove(WSplit
*node
, bool reclaim_space
)
1769 if(node
->parent
!=NULL
)
1770 splitinner_remove(node
->parent
, node
, reclaim_space
);
1771 else if(node
->ws_if_root
!=NULL
)
1772 splittree_changeroot(node
, NULL
);
1774 destroy_obj((Obj
*)node
);
1781 /*{{{ Tree traversal */
1784 static bool defaultfilter(WSplit
*node
)
1786 return (OBJ_IS(node
, WSplitRegion
) &&
1787 ((WSplitRegion
*)node
)->reg
!=NULL
);
1791 static WSplit
*split_current_todir_default(WSplit
*node
,
1792 WPrimn hprimn
, WPrimn vprimn
,
1793 WSplitFilter
*filter
)
1796 filter
=defaultfilter
;
1798 return (filter(node
) ? node
: NULL
);
1802 static WSplit
*splitsplit_current_todir(WSplitSplit
*node
,
1803 WPrimn hprimn
, WPrimn vprimn
,
1804 WSplitFilter
*filter
)
1806 WPrimn primn
=(node
->dir
==SPLIT_HORIZONTAL
? hprimn
: vprimn
);
1807 WSplit
*first
, *second
, *ret
;
1809 if(primn
==PRIMN_TL
||
1810 (primn
==PRIMN_ANY
&& node
->current
==SPLIT_CURRENT_TL
)){
1813 }else if(primn
==PRIMN_BR
||
1814 (primn
==PRIMN_ANY
&& node
->current
==SPLIT_CURRENT_BR
)){
1821 ret
=split_current_todir(first
, hprimn
, vprimn
, filter
);
1823 ret
=split_current_todir(second
, hprimn
, vprimn
, filter
);
1824 if(ret
==NULL
&& filter
!=NULL
){
1825 if(filter((WSplit
*)node
))
1833 WSplit
*split_current_todir(WSplit
*node
, WPrimn hprimn
, WPrimn vprimn
,
1834 WSplitFilter
*filter
)
1837 CALL_DYN_RET(ret
, WSplit
*, split_current_todir
, node
,
1838 (node
, hprimn
, vprimn
, filter
));
1843 /* Note: both hprimn and vprimn are inverted when descending. Therefore
1844 * one should be either PRIMN_NONE or PRIMN_ANY for sensible geometric
1845 * navigation. (Both are PRIMN_TL or PRIMN_BR for pseudo-linear
1846 * next/previous navigation.)
1848 WSplit
*splitsplit_nextto(WSplitSplit
*node
, WSplit
*child
,
1849 WPrimn hprimn
, WPrimn vprimn
,
1850 WSplitFilter
*filter
)
1852 WPrimn primn
=(node
->dir
==SPLIT_HORIZONTAL
? hprimn
: vprimn
);
1853 WSplit
*split
=NULL
, *nnode
=NULL
;
1855 if(node
->tl
==child
&& (primn
==PRIMN_BR
|| primn
==PRIMN_ANY
))
1857 else if(node
->br
==child
&& (primn
==PRIMN_TL
|| primn
==PRIMN_ANY
))
1861 nnode
=split_current_todir(split
,
1862 primn_none2any(primn_invert(hprimn
)),
1863 primn_none2any(primn_invert(vprimn
)),
1868 nnode
=split_nextto((WSplit
*)node
, hprimn
, vprimn
, filter
);
1874 WSplit
*splitinner_nextto(WSplitInner
*node
, WSplit
*child
,
1875 WPrimn hprimn
, WPrimn vprimn
,
1876 WSplitFilter
*filter
)
1879 CALL_DYN_RET(ret
, WSplit
*, splitinner_nextto
, node
,
1880 (node
, child
, hprimn
, vprimn
, filter
));
1885 WSplit
*split_nextto(WSplit
*node
, WPrimn hprimn
, WPrimn vprimn
,
1886 WSplitFilter
*filter
)
1888 while(node
->parent
!=NULL
){
1889 WSplit
*ret
=splitinner_nextto(node
->parent
, node
,
1890 hprimn
, vprimn
, filter
);
1893 node
=(WSplit
*)node
->parent
;
1899 void splitinner_mark_current_default(WSplitInner
*split
, WSplit
*child
)
1901 if(((WSplit
*)split
)->parent
!=NULL
)
1902 splitinner_mark_current(((WSplit
*)split
)->parent
, (WSplit
*)split
);
1906 void splitsplit_mark_current(WSplitSplit
*split
, WSplit
*child
)
1908 assert(child
==split
->tl
|| child
==split
->br
);
1910 split
->current
=(split
->tl
==child
? SPLIT_CURRENT_TL
: SPLIT_CURRENT_BR
);
1912 splitinner_mark_current_default(&(split
->isplit
), child
);
1916 void splitinner_mark_current(WSplitInner
*split
, WSplit
*child
)
1918 CALL_DYN(splitinner_mark_current
, split
, (split
, child
));
1922 static void splitsplit_forall(WSplitSplit
*node
, WSplitFn
*fn
)
1929 void splitinner_forall(WSplitInner
*node
, WSplitFn
*fn
)
1931 CALL_DYN(splitinner_forall
, node
, (node
, fn
));
1935 static WSplit
*splitsplit_current(WSplitSplit
*split
)
1937 return (split
->current
==SPLIT_CURRENT_TL
? split
->tl
: split
->br
);
1942 * Returns the most previously active child node of \var{split}.
1946 WSplit
*splitinner_current(WSplitInner
*node
)
1949 CALL_DYN_RET(ret
, WSplit
*, splitinner_current
, node
, (node
));
1957 /*{{{ X window handling */
1960 static void splitregion_stacking(WSplitRegion
*split
,
1961 Window
*bottomret
, Window
*topret
)
1965 if(split
->reg
!=NULL
)
1966 region_stacking(split
->reg
, bottomret
, topret
);
1970 void splitsplit_stacking(WSplitSplit
*split
,
1971 Window
*bottomret
, Window
*topret
)
1973 Window tlb
=None
, tlt
=None
;
1974 Window brb
=None
, brt
=None
;
1976 split_stacking(split
->tl
, &tlb
, &tlt
);
1977 split_stacking(split
->br
, &brb
, &brt
);
1979 /* To make sure that this condition holds is left to the workspace
1980 * code to do after a split tree has been loaded or modified.
1982 if(split
->current
==SPLIT_CURRENT_TL
){
1983 *topret
=(tlt
!=None
? tlt
: brt
);
1984 *bottomret
=(brb
!=None
? brb
: tlb
);
1986 *topret
=(brt
!=None
? brt
: tlt
);
1987 *bottomret
=(tlb
!=None
? tlb
: brb
);
1991 void split_stacking(WSplit
*split
, Window
*bottomret
, Window
*topret
)
1996 CALL_DYN(split_stacking
, split
, (split
, bottomret
, topret
));
2001 static void splitregion_restack(WSplitRegion
*split
, Window other
, int mode
)
2003 if(split
->reg
!=NULL
)
2004 region_restack(split
->reg
, other
, mode
);
2007 void splitsplit_restack(WSplitSplit
*split
, Window other
, int mode
)
2009 Window bottom
=None
, top
=None
;
2010 WSplit
*first
, *second
;
2012 if(split
->current
==SPLIT_CURRENT_TL
){
2020 split_restack(first
, other
, mode
);
2021 split_stacking(first
, &bottom
, &top
);
2026 split_restack(second
, other
, mode
);
2029 void split_restack(WSplit
*split
, Window other
, int mode
)
2031 CALL_DYN(split_restack
, split
, (split
, other
, mode
));
2035 static void splitregion_map(WSplitRegion
*split
)
2037 if(split
->reg
!=NULL
)
2038 region_map(split
->reg
);
2041 static void splitinner_map(WSplitInner
*split
)
2043 splitinner_forall(split
, split_map
);
2046 void split_map(WSplit
*split
)
2048 CALL_DYN(split_map
, split
, (split
));
2052 static void splitregion_unmap(WSplitRegion
*split
)
2054 if(split
->reg
!=NULL
)
2055 region_unmap(split
->reg
);
2058 static void splitinner_unmap(WSplitInner
*split
)
2060 splitinner_forall(split
, split_unmap
);
2063 void split_unmap(WSplit
*split
)
2065 CALL_DYN(split_unmap
, split
, (split
));
2069 static void splitregion_reparent(WSplitRegion
*split
, WWindow
*wwin
)
2071 if(split
->reg
!=NULL
){
2072 WRectangle g
=split
->split
.geom
;
2073 region_reparent(split
->reg
, wwin
, &g
, REGION_FIT_EXACT
);
2078 static void splitsplit_reparent(WSplitSplit
*split
, WWindow
*wwin
)
2080 if(split
->current
==SPLIT_CURRENT_TL
){
2081 split_reparent(split
->br
, wwin
);
2082 split_reparent(split
->tl
, wwin
);
2084 split_reparent(split
->tl
, wwin
);
2085 split_reparent(split
->br
, wwin
);
2090 void split_reparent(WSplit
*split
, WWindow
*wwin
)
2092 CALL_DYN(split_reparent
, split
, (split
, wwin
));
2099 /*{{{ Transpose, flip, rotate */
2102 void splitsplit_flip_default(WSplitSplit
*split
)
2104 WRectangle tlng
, brng
;
2105 WRectangle
*sg
=&((WSplit
*)split
)->geom
;
2108 assert(split
->tl
!=NULL
&& split
->br
!=NULL
);
2110 split_update_bounds((WSplit
*)split
, TRUE
);
2112 tlng
=split
->tl
->geom
;
2113 brng
=split
->br
->geom
;
2115 if(split
->dir
==SPLIT_HORIZONTAL
){
2117 tlng
.x
=sg
->x
+sg
->w
-tlng
.w
;
2120 tlng
.y
=sg
->y
+sg
->h
-tlng
.h
;
2124 split
->tl
=split
->br
;
2126 split
->current
=(split
->current
==SPLIT_CURRENT_TL
2128 : SPLIT_CURRENT_TL
);
2130 split_do_resize(split
->tl
, &brng
, PRIMN_ANY
, PRIMN_ANY
, FALSE
);
2131 split_do_resize(split
->br
, &tlng
, PRIMN_ANY
, PRIMN_ANY
, FALSE
);
2135 static void splitsplit_flip_(WSplitSplit
*split
)
2137 CALL_DYN(splitsplit_flip
, split
, (split
));
2142 * Flip contents of \var{split}.
2145 void splitsplit_flip(WSplitSplit
*split
)
2147 splittree_begin_resize();
2149 split
=OBJ_CAST(dodge_stdisp((WSplit
*)split
, FALSE
), WSplitSplit
);
2154 splitsplit_flip_(split
);
2156 splittree_end_resize();
2167 static FlipDir flipdir
=FLIP_VERTICAL
;
2170 static void do_flip(WSplit
*split
)
2172 WSplitSplit
*ss
=OBJ_CAST(split
, WSplitSplit
);
2175 if((flipdir
==FLIP_ANY
2176 || (ss
->dir
==SPLIT_VERTICAL
&& flipdir
==FLIP_VERTICAL
)
2177 || (ss
->dir
==SPLIT_HORIZONTAL
&& flipdir
==FLIP_HORIZONTAL
))
2178 && !OBJ_IS(ss
->tl
, WSplitST
)
2179 && !OBJ_IS(ss
->br
, WSplitST
)){
2180 splitsplit_flip_(ss
);
2184 if(OBJ_IS(ss
, WSplitInner
))
2185 splitinner_forall((WSplitInner
*)ss
, do_flip
);
2189 static void splittree_flip_dir(WSplit
*splittree
, FlipDir dir
)
2191 /* todo stdisp outta way */
2192 if(OBJ_IS(splittree
, WSplitInner
)){
2194 splitinner_forall((WSplitInner
*)splittree
, do_flip
);
2199 static bool split_fliptrans_to(WSplit
*node
, const WRectangle
*geom
,
2200 bool trans
, FlipDir flip
)
2205 splittree_begin_resize();
2207 /* split_do_resize can do things right if 'node' has stdisp as child,
2208 * but otherwise transpose will put the stdisp in a bad split
2209 * configuration if it is contained within 'node', so we must
2210 * first move it and its fixed parent split below node. For correct
2211 * geometry calculation we move it immediately below node, and
2212 * resize stdisp's fixed parent node instead.
2214 node2
=dodge_stdisp(node
, TRUE
);
2216 if(node
==NULL
|| node2
!=node
)
2219 split_update_bounds(node
, TRUE
);
2221 split_do_rqgeom_(node
, geom
, PRIMN_ANY
, PRIMN_ANY
, &rg
, FALSE
);
2223 split_do_resize(node
, &rg
, PRIMN_ANY
, PRIMN_ANY
, trans
);
2226 splittree_flip_dir(node
, flip
);
2228 splittree_end_resize();
2234 bool split_transpose_to(WSplit
*node
, const WRectangle
*geom
)
2236 return split_fliptrans_to(node
, geom
, TRUE
, FLIP_ANY
);
2241 * Transpose contents of \var{node}.
2244 void split_transpose(WSplit
*node
)
2246 WRectangle g
=node
->geom
;
2248 split_transpose_to(node
, &g
);
2252 bool split_rotate_to(WSplit
*node
, const WRectangle
*geom
, int rotation
)
2254 FlipDir flip
=FLIP_NONE
;
2257 if(rotation
==SCREEN_ROTATION_90
){
2258 flip
=FLIP_HORIZONTAL
;
2260 }else if(rotation
==SCREEN_ROTATION_180
){
2262 }else if(rotation
==SCREEN_ROTATION_270
){
2267 return split_fliptrans_to(node
, geom
, trans
, flip
);
2277 * Return parent split for \var{split}.
2281 WSplitInner
*split_parent(WSplit
*split
)
2283 return split
->parent
;
2288 * Returns the area of workspace used by the regions under \var{split}.
2292 ExtlTab
split_geom(WSplit
*split
)
2294 return extl_table_from_rectangle(&(split
->geom
));
2299 * Returns the top or left child node of \var{split} depending
2300 * on the direction of the split.
2304 WSplit
*splitsplit_tl(WSplitSplit
*split
)
2311 * Returns the bottom or right child node of \var{split} depending
2312 * on the direction of the split.
2316 WSplit
*splitsplit_br(WSplitSplit
*split
)
2322 * Returns the direction of \var{split}; either \codestr{vertical} or
2323 * \codestr{horizontal}.
2327 const char *splitsplit_dir(WSplitSplit
*split
)
2329 return (split
->dir
==SPLIT_VERTICAL
? "vertical" : "horizontal");
2334 * Returns the region contained in \var{node}.
2338 WRegion
*splitregion_reg(WSplitRegion
*node
)
2347 /*{{{ Save support */
2350 ExtlTab
split_base_config(WSplit
*node
)
2352 ExtlTab t
=extl_create_table();
2353 extl_table_sets_s(t
, "type", OBJ_TYPESTR(node
));
2358 static bool splitregion_get_config(WSplitRegion
*node
, ExtlTab
*ret
)
2365 if(!region_supports_save(node
->reg
)){
2366 warn(TR("Unable to get configuration for %s."),
2367 region_name(node
->reg
));
2371 rt
=region_get_configuration(node
->reg
);
2372 t
=split_base_config(&(node
->split
));
2373 extl_table_sets_t(t
, "regparams", rt
);
2374 extl_unref_table(rt
);
2381 static bool splitst_get_config(WSplitST
*node
, ExtlTab
*ret
)
2383 *ret
=split_base_config((WSplit
*)node
);
2388 static bool splitsplit_get_config(WSplitSplit
*node
, ExtlTab
*ret
)
2390 ExtlTab tab
, tltab
, brtab
;
2393 if(!split_get_config(node
->tl
, &tltab
))
2394 return split_get_config(node
->br
, ret
);
2396 if(!split_get_config(node
->br
, &brtab
)){
2401 tab
=split_base_config((WSplit
*)node
);
2403 tls
=split_size(node
->tl
, node
->dir
);
2404 brs
=split_size(node
->br
, node
->dir
);
2406 extl_table_sets_s(tab
, "dir", (node
->dir
==SPLIT_VERTICAL
2407 ? "vertical" : "horizontal"));
2409 extl_table_sets_i(tab
, "tls", tls
);
2410 extl_table_sets_t(tab
, "tl", tltab
);
2411 extl_unref_table(tltab
);
2413 extl_table_sets_i(tab
, "brs", brs
);
2414 extl_table_sets_t(tab
, "br", brtab
);
2415 extl_unref_table(brtab
);
2423 bool split_get_config(WSplit
*node
, ExtlTab
*tabret
)
2426 CALL_DYN_RET(ret
, bool, split_get_config
, node
, (node
, tabret
));
2434 /*{{{ The classes */
2437 static DynFunTab split_dynfuntab
[]={
2438 {split_do_resize
, split_do_resize_default
},
2439 {(DynFun
*)split_current_todir
, (DynFun
*)split_current_todir_default
},
2443 static DynFunTab splitinner_dynfuntab
[]={
2444 {splitinner_mark_current
, splitinner_mark_current_default
},
2445 {split_map
, splitinner_map
},
2446 {split_unmap
, splitinner_unmap
},
2450 static DynFunTab splitsplit_dynfuntab
[]={
2451 {split_update_bounds
, splitsplit_update_bounds
},
2452 {split_do_resize
, splitsplit_do_resize
},
2453 {split_do_maxhelper
, splitsplit_do_maxhelper
},
2454 {(DynFun
*)split_do_restore
, (DynFun
*)splitsplit_do_restore
},
2455 {(DynFun
*)split_do_verify
, (DynFun
*)splitsplit_do_verify
},
2456 {splitinner_do_rqsize
, splitsplit_do_rqsize
},
2457 {splitinner_replace
, splitsplit_replace
},
2458 {splitinner_remove
, splitsplit_remove
},
2459 {(DynFun
*)split_current_todir
, (DynFun
*)splitsplit_current_todir
},
2460 {(DynFun
*)splitinner_current
, (DynFun
*)splitsplit_current
},
2461 {(DynFun
*)splitinner_nextto
, (DynFun
*)splitsplit_nextto
},
2462 {splitinner_mark_current
, splitsplit_mark_current
},
2463 {(DynFun
*)split_get_config
, (DynFun
*)splitsplit_get_config
},
2464 {splitinner_forall
, splitsplit_forall
},
2465 {split_restack
, splitsplit_restack
},
2466 {split_stacking
, splitsplit_stacking
},
2467 {split_reparent
, splitsplit_reparent
},
2468 {splitsplit_flip
, splitsplit_flip_default
},
2472 static DynFunTab splitregion_dynfuntab
[]={
2473 {split_update_bounds
, splitregion_update_bounds
},
2474 {split_do_resize
, splitregion_do_resize
},
2475 {split_do_maxhelper
, splitregion_do_maxhelper
},
2476 {(DynFun
*)split_do_restore
, (DynFun
*)splitregion_do_restore
},
2477 {(DynFun
*)split_do_verify
, (DynFun
*)splitregion_do_verify
},
2478 {(DynFun
*)split_get_config
, (DynFun
*)splitregion_get_config
},
2479 {split_map
, splitregion_map
},
2480 {split_unmap
, splitregion_unmap
},
2481 {split_restack
, splitregion_restack
},
2482 {split_stacking
, splitregion_stacking
},
2483 {split_reparent
, splitregion_reparent
},
2487 static DynFunTab splitst_dynfuntab
[]={
2488 {split_update_bounds
, splitst_update_bounds
},
2489 {split_do_resize
, splitst_do_resize
},
2490 {split_do_maxhelper
, splitst_do_maxhelper
},
2491 {(DynFun
*)split_do_restore
, (DynFun
*)splitst_do_restore
},
2492 {(DynFun
*)split_do_verify
, (DynFun
*)splitst_do_verify
},
2493 {(DynFun
*)split_get_config
, (DynFun
*)splitst_get_config
},
2499 IMPLCLASS(WSplit
, Obj
, split_deinit
, split_dynfuntab
);
2502 IMPLCLASS(WSplitInner
, WSplit
, splitinner_deinit
, splitinner_dynfuntab
);
2505 IMPLCLASS(WSplitSplit
, WSplitInner
, splitsplit_deinit
, splitsplit_dynfuntab
);
2508 IMPLCLASS(WSplitRegion
, WSplit
, splitregion_deinit
, splitregion_dynfuntab
);
2511 IMPLCLASS(WSplitST
, WSplitRegion
, splitst_deinit
, splitst_dynfuntab
);