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 /* No, these are not even supposed to be proper/consistent
60 * Z \cup {\infty, -\infty} calculation rules.
63 static int infadd(int x
, int y
)
65 if(x
==INT_MAX
|| y
==INT_MAX
)
72 /* Negative "unused space" means no SPLIT_UNUSED under a node, while
73 * zero unused space means there's a zero-sized SPLIT_UNUSED under the
76 static int unusedadd(int x
, int y
)
80 return maxof(x
, 0)+maxof(y
, 0);
84 static void bound(int *what
, int min
, int max
)
96 /*{{{ Functions to get and set a region's containing node */
99 #define node_of_reg splittree_node_of
101 WSplitRegion
*splittree_node_of(WRegion
*reg
)
106 /*assert(REGION_MANAGER_CHK(reg, WTiling)!=NULL);*/
108 if(split_of_map
!=NULL
){
109 node
=rb_find_pkey_n(split_of_map
, reg
, &found
);
111 return (WSplitRegion
*)(node
->v
.val
);
118 #define set_node_of_reg splittree_set_node_of
121 bool splittree_set_node_of(WRegion
*reg
, WSplitRegion
*split
)
126 /*assert(REGION_MANAGER_CHK(reg, WTiling)!=NULL);*/
128 if(split_of_map
==NULL
){
131 split_of_map
=make_rb();
132 if(split_of_map
==NULL
)
136 node
=rb_find_pkey_n(split_of_map
, reg
, &found
);
138 rb_delete_node(node
);
140 return (rb_insertp(split_of_map
, reg
, split
)!=NULL
);
150 WPrimn
primn_invert(WPrimn primn
)
152 return (primn
==PRIMN_TL
160 WPrimn
primn_none2any(WPrimn primn
)
162 return (primn
==PRIMN_NONE
? PRIMN_ANY
: primn
);
172 bool split_init(WSplit
*split
, const WRectangle
*geom
)
175 split
->ws_if_root
=NULL
;
179 split
->max_w
=INT_MAX
;
180 split
->max_h
=INT_MAX
;
186 bool splitinner_init(WSplitInner
*split
, const WRectangle
*geom
)
188 return split_init(&(split
->split
), geom
);
192 bool splitsplit_init(WSplitSplit
*split
, const WRectangle
*geom
, int dir
)
194 splitinner_init(&(split
->isplit
), geom
);
198 split
->current
=SPLIT_CURRENT_TL
;
203 bool splitregion_init(WSplitRegion
*split
, const WRectangle
*geom
,
206 split_init(&(split
->split
), geom
);
209 set_node_of_reg(reg
, split
);
214 bool splitst_init(WSplitST
*split
, const WRectangle
*geom
, WRegion
*reg
)
216 splitregion_init(&(split
->regnode
), geom
, reg
);
217 split
->orientation
=REGION_ORIENTATION_HORIZONTAL
;
218 split
->corner
=MPLEX_STDISP_BL
;
223 WSplitSplit
*create_splitsplit(const WRectangle
*geom
, int dir
)
225 CREATEOBJ_IMPL(WSplitSplit
, splitsplit
, (p
, geom
, dir
));
229 WSplitRegion
*create_splitregion(const WRectangle
*geom
, WRegion
*reg
)
231 CREATEOBJ_IMPL(WSplitRegion
, splitregion
, (p
, geom
, reg
));
235 WSplitST
*create_splitst(const WRectangle
*geom
, WRegion
*reg
)
237 CREATEOBJ_IMPL(WSplitST
, splitst
, (p
, geom
, reg
));
247 void split_deinit(WSplit
*split
)
249 assert(split
->parent
==NULL
);
253 void splitinner_deinit(WSplitInner
*split
)
255 split_deinit(&(split
->split
));
259 void splitsplit_deinit(WSplitSplit
*split
)
262 split
->tl
->parent
=NULL
;
263 destroy_obj((Obj
*)(split
->tl
));
266 split
->br
->parent
=NULL
;
267 destroy_obj((Obj
*)(split
->br
));
270 splitinner_deinit(&(split
->isplit
));
274 void splitregion_deinit(WSplitRegion
*split
)
276 if(split
->reg
!=NULL
){
277 set_node_of_reg(split
->reg
, NULL
);
281 split_deinit(&(split
->split
));
285 void splitst_deinit(WSplitST
*split
)
287 splitregion_deinit(&(split
->regnode
));
294 /*{{{ Size bounds management */
297 static void splitregion_update_bounds(WSplitRegion
*node
, bool UNUSED(recursive
))
300 WSplit
*snode
=(WSplit
*)node
;
302 assert(node
->reg
!=NULL
);
304 region_size_hints(node
->reg
, &hints
);
306 snode
->min_w
=maxof(1, hints
.min_set
? hints
.min_width
: 1);
307 snode
->max_w
=INT_MAX
;
310 snode
->min_h
=maxof(1, hints
.min_set
? hints
.min_height
: 1);
311 snode
->max_h
=INT_MAX
;
316 static void splitst_update_bounds(WSplitST
*node
, bool UNUSED(rec
))
318 WSplit
*snode
=(WSplit
*)node
;
320 if(node
->regnode
.reg
==NULL
){
321 snode
->min_w
=CF_STDISP_MIN_SZ
;
322 snode
->min_h
=CF_STDISP_MIN_SZ
;
323 snode
->max_w
=CF_STDISP_MIN_SZ
;
324 snode
->max_h
=CF_STDISP_MIN_SZ
;
327 region_size_hints(node
->regnode
.reg
, &hints
);
328 snode
->min_w
=maxof(1, hints
.min_set
? hints
.min_width
: 1);
329 snode
->max_w
=maxof(snode
->min_w
, hints
.min_width
);
330 snode
->min_h
=maxof(1, hints
.min_set
? hints
.min_height
: 1);
331 snode
->max_h
=maxof(snode
->min_h
, hints
.min_height
);
337 if(node
->orientation
==REGION_ORIENTATION_HORIZONTAL
){
338 snode
->min_w
=CF_STDISP_MIN_SZ
;
339 snode
->max_w
=INT_MAX
;
341 snode
->min_h
=CF_STDISP_MIN_SZ
;
342 snode
->max_h
=INT_MAX
;
347 static void splitsplit_update_bounds(WSplitSplit
*split
, bool recursive
)
350 WSplit
*node
=(WSplit
*)split
;
352 assert(split
->tl
!=NULL
&& split
->br
!=NULL
);
358 split_update_bounds(tl
, TRUE
);
359 split_update_bounds(br
, TRUE
);
362 if(split
->dir
==SPLIT_HORIZONTAL
){
363 node
->max_w
=infadd(tl
->max_w
, br
->max_w
);
364 node
->min_w
=infadd(tl
->min_w
, br
->min_w
);
365 node
->unused_w
=unusedadd(tl
->unused_w
, br
->unused_w
);
366 node
->min_h
=maxof(tl
->min_h
, br
->min_h
);
367 node
->max_h
=maxof(minof(tl
->max_h
, br
->max_h
), node
->min_h
);
368 node
->unused_h
=minof(tl
->unused_h
, br
->unused_h
);
370 node
->max_h
=infadd(tl
->max_h
, br
->max_h
);
371 node
->min_h
=infadd(tl
->min_h
, br
->min_h
);
372 node
->unused_h
=unusedadd(tl
->unused_h
, br
->unused_h
);
373 node
->min_w
=maxof(tl
->min_w
, br
->min_w
);
374 node
->max_w
=maxof(minof(tl
->max_w
, br
->max_w
), node
->min_w
);
375 node
->unused_w
=minof(tl
->unused_w
, br
->unused_w
);
380 void split_update_bounds(WSplit
*node
, bool recursive
)
382 CALL_DYN(split_update_bounds
, node
, (node
, recursive
));
386 void splitsplit_update_geom_from_children(WSplitSplit
*node
)
388 if(node
->dir
==SPLIT_VERTICAL
){
389 ((WSplit
*)node
)->geom
.h
=node
->tl
->geom
.h
+node
->br
->geom
.h
;
390 ((WSplit
*)node
)->geom
.y
=node
->tl
->geom
.y
;
391 }else if(node
->dir
==SPLIT_HORIZONTAL
){
392 ((WSplit
*)node
)->geom
.w
=node
->tl
->geom
.w
+node
->br
->geom
.w
;
393 ((WSplit
*)node
)->geom
.x
=node
->tl
->geom
.x
;
401 /*{{{ Status display handling helper functions. */
404 static WSplitST
*saw_stdisp
=NULL
;
407 void splittree_begin_resize()
413 void splittree_end_resize()
415 if(saw_stdisp
!=NULL
){
416 split_regularise_stdisp(saw_stdisp
);
422 static void splittree_scan_stdisp_rootward_(WSplitInner
*node_
)
424 WSplitSplit
*node
=OBJ_CAST(node_
, WSplitSplit
);
427 if(OBJ_IS(node
->tl
, WSplitST
)){
428 saw_stdisp
=(WSplitST
*)(node
->tl
);
430 }else if(OBJ_IS(node
->br
, WSplitST
)){
431 saw_stdisp
=(WSplitST
*)(node
->br
);
436 if(node_
->split
.parent
!=NULL
)
437 splittree_scan_stdisp_rootward_(node_
->split
.parent
);
441 void splittree_scan_stdisp_rootward(WSplit
*node
)
443 if(node
->parent
!=NULL
)
444 splittree_scan_stdisp_rootward_(node
->parent
);
448 static WSplitST
*splittree_scan_stdisp(WSplit
*node_
, bool set_saw
)
451 WSplitSplit
*node
=OBJ_CAST(node_
, WSplitSplit
);
456 r
=OBJ_CAST(node
->tl
, WSplitST
);
458 r
=OBJ_CAST(node
->br
, WSplitST
);
466 r
=splittree_scan_stdisp(node
->tl
, set_saw
);
468 r
=splittree_scan_stdisp(node
->br
, set_saw
);
474 static bool stdisp_immediate_child(WSplitSplit
*node
)
476 return (node
!=NULL
&& (OBJ_IS(node
->tl
, WSplitST
) ||
477 OBJ_IS(node
->br
, WSplitST
)));
481 static WSplit
*dodge_stdisp(WSplit
*node
, bool keep_within
)
484 WSplitSplit
*stdispp
;
486 stdisp
=splittree_scan_stdisp(node
, TRUE
);
491 stdispp
=OBJ_CAST(((WSplit
*)stdisp
)->parent
, WSplitSplit
);
496 if((WSplit
*)stdispp
==node
){
497 /* Node itself immediately contains stdisp. Due to the way
498 * try_unsink works, stdisp this will not change, so another
499 * node must be used, if we want to fully dodge stdisp.
503 : (stdispp
->tl
==(WSplit
*)stdisp
509 if(!split_try_unsink_stdisp(stdispp
, FALSE
, TRUE
)){
510 warn(TR("Unable to move the status display out of way."));
513 }while(stdispp
->tl
!=node
&& stdispp
->br
!=node
);
522 /*{{{ Low-level resize code; from root to leaf */
525 static void split_do_resize_default(WSplit
*node
, const WRectangle
*ng
,
526 WPrimn
UNUSED(hprimn
), WPrimn
UNUSED(vprimn
),
527 bool UNUSED(transpose
))
533 static void splitregion_do_resize(WSplitRegion
*node
, const WRectangle
*ng
,
534 WPrimn
UNUSED(hprimn
), WPrimn
UNUSED(vprimn
),
535 bool UNUSED(transpose
))
537 assert(node
->reg
!=NULL
);
538 region_fit(node
->reg
, ng
, REGION_FIT_EXACT
);
539 split_update_bounds(&(node
->split
), FALSE
);
540 node
->split
.geom
=*ng
;
544 static void splitst_do_resize(WSplitST
*node
, const WRectangle
*ng
,
545 WPrimn hprimn
, WPrimn vprimn
,
550 if(node
->regnode
.reg
==NULL
){
551 ((WSplit
*)node
)->geom
=*ng
;
553 splitregion_do_resize(&(node
->regnode
), ng
, hprimn
, vprimn
,
559 static int other_dir(int dir
)
561 return (dir
==SPLIT_VERTICAL
? SPLIT_HORIZONTAL
: SPLIT_VERTICAL
);
565 static void adjust_sizes(int *tls_
, int *brs_
, int nsize
, int sz
,
566 int tlmin
, int brmin
, int tlmax
, int brmax
,
574 bound(&tls
, tlmin
, tlmax
);
576 bound(&brs
, brmin
, brmax
);
578 bound(&tls
, tlmin
, tlmax
);
579 }else if(primn
==PRIMN_BR
){
581 bound(&brs
, brmin
, brmax
);
583 bound(&tls
, tlmin
, tlmax
);
585 bound(&brs
, brmin
, brmax
);
586 }else{ /* && PRIMN_ANY */
588 bound(&tls
, tlmin
, tlmax
);
590 bound(&brs
, brmin
, brmax
);
592 bound(&tls
, tlmin
, tlmax
);
600 static void get_minmaxunused(WSplit
*node
, int dir
,
601 int *min
, int *max
, int *unused
)
603 if(dir
==SPLIT_VERTICAL
){
605 *max
=maxof(*min
, node
->max_h
);
606 *unused
=minof(node
->unused_h
, node
->geom
.h
);
609 *max
=maxof(*min
, node
->max_w
);
610 *unused
=minof(node
->unused_w
, node
->geom
.w
);
615 void splitsplit_do_resize(WSplitSplit
*node
, const WRectangle
*ng
,
616 WPrimn hprimn
, WPrimn vprimn
, bool transpose
)
618 assert(ng
->w
>=0 && ng
->h
>=0);
619 assert(node
->tl
!=NULL
&& node
->br
!=NULL
);
620 assert(!transpose
|| (hprimn
==PRIMN_ANY
&& vprimn
==PRIMN_ANY
));
623 WSplit
*tl
=node
->tl
, *br
=node
->br
;
624 int tls
=split_size((WSplit
*)tl
, node
->dir
);
625 int brs
=split_size((WSplit
*)br
, node
->dir
);
627 /* Status display can not be transposed. */
628 int dir
=((transpose
&& !stdisp_immediate_child(node
))
629 ? other_dir(node
->dir
)
631 int nsize
=(dir
==SPLIT_VERTICAL
? ng
->h
: ng
->w
);
632 int primn
=(dir
==SPLIT_VERTICAL
? vprimn
: hprimn
);
633 int tlmin
, tlmax
, tlunused
, tlused
;
634 int brmin
, brmax
, brunused
, brused
;
635 WRectangle tlg
=*ng
, brg
=*ng
;
637 get_minmaxunused(tl
, dir
, &tlmin
, &tlmax
, &tlunused
);
638 get_minmaxunused(br
, dir
, &brmin
, &brmax
, &brunused
);
640 tlused
=maxof(0, tls
-maxof(0, tlunused
));
641 brused
=maxof(0, brs
-maxof(0, brunused
));
642 /* tlmin, brmin >= 1 => (tls>=tlmin, brs>=brmin => sz>0) */
645 if(primn
==PRIMN_ANY
&& (tlunused
>=0 || brunused
>=0)){
646 if(nsize
<=tlused
+brused
){
647 /* Need to shrink a tangible node */
648 adjust_sizes(&tls
, &brs
, nsize
, sz
,
649 tlmin
, brmin
, tlused
, brused
, primn
);
651 /* Just expand or shrink unused space */
652 adjust_sizes(&tls
, &brs
, nsize
, sz
,
654 (tlunused
<0 ? tlused
: tlmax
),
655 (brunused
<0 ? brused
: brmax
), primn
);
659 adjust_sizes(&tls
, &brs
, nsize
, sz
,
660 tlmin
, brmin
, tlmax
, brmax
, primn
);
665 /* Bad fit; just size proportionally. */
670 tls
=split_size(tl
, node
->dir
)*nsize
/sz
;
675 if(dir
==SPLIT_VERTICAL
){
685 split_do_resize(tl
, &tlg
, hprimn
, vprimn
, transpose
);
686 split_do_resize(br
, &brg
, hprimn
, vprimn
, transpose
);
689 ((WSplit
*)node
)->geom
=*ng
;
690 split_update_bounds((WSplit
*)node
, FALSE
);
695 void split_do_resize(WSplit
*node
, const WRectangle
*ng
,
696 WPrimn hprimn
, WPrimn vprimn
, bool transpose
)
698 CALL_DYN(split_do_resize
, node
, (node
, ng
, hprimn
, vprimn
, transpose
));
702 void split_resize(WSplit
*node
, const WRectangle
*ng
,
703 WPrimn hprimn
, WPrimn vprimn
)
705 split_update_bounds(node
, TRUE
);
706 splittree_begin_resize();
707 split_do_resize(node
, ng
, hprimn
, vprimn
, FALSE
);
708 splittree_end_resize();
715 /*{{{ Save, restore and verify code for maximization */
718 bool splits_are_related(WSplit
*p
, WSplit
*node
)
725 ? splits_are_related(p
, (WSplit
*)node
->parent
)
730 WSplit
*maxparentdir_rel(WSplit
*p
, WSplit
*node
, int dir
)
732 /* Descending from p, try to determine the first split of type dir between
733 * p and node, while ignoring a potential stdisp. */
734 if(OBJ_IS(p
, WSplitSplit
)){
735 WSplitSplit
*sp
=(WSplitSplit
*)p
;
736 assert(sp
->tl
!=NULL
&& sp
->br
!=NULL
);
737 assert(splits_are_related(sp
->tl
, node
) ||
738 splits_are_related(sp
->br
, node
));
740 if(OBJ_IS(sp
->tl
, WSplitST
))
741 return maxparentdir_rel(sp
->br
, node
, dir
);
742 if(OBJ_IS(sp
->br
, WSplitST
))
743 return maxparentdir_rel(sp
->tl
, node
, dir
);
747 splits_are_related(sp
->tl
, node
)
748 ? maxparentdir_rel(sp
->tl
, node
, dir
)
749 : maxparentdir_rel(sp
->br
, node
, dir
);
757 WSplit
*maxparent(WSplit
*node
)
759 WSplit
*p
=(WSplit
*)node
->parent
;
760 return p
==NULL
? node
: maxparent(p
);
764 WSplit
*maxparentdir(WSplit
*node
, int dir
)
766 return maxparentdir_rel(maxparent(node
), node
, dir
);
769 int *wh(WRectangle
*geom
, int orientation
)
771 return orientation
==REGION_ORIENTATION_HORIZONTAL
? &geom
->w
: &geom
->h
;
774 int *xy(WRectangle
*geom
, int orientation
)
776 return orientation
==REGION_ORIENTATION_HORIZONTAL
? &geom
->x
: &geom
->y
;
779 bool is_lt(int orientation
, int corner
)
781 /* Read as "is_left" or "is_top", depending on the orientation. */
783 orientation
==REGION_ORIENTATION_HORIZONTAL
784 ? corner
==MPLEX_STDISP_TL
|| corner
==MPLEX_STDISP_BL
785 : corner
==MPLEX_STDISP_TL
|| corner
==MPLEX_STDISP_TR
;
788 int flip_orientation(int orientation
)
791 orientation
==REGION_ORIENTATION_HORIZONTAL
792 ? REGION_ORIENTATION_VERTICAL
793 : REGION_ORIENTATION_HORIZONTAL
;
796 WRectangle
stdisp_recommended_geom(WSplitST
*st
, WRectangle wsg
)
798 /* wsg holds the geometry of the workspace that st is on. */
799 WRectangle stg
=REGION_GEOM(st
->regnode
.reg
);
800 int ori
=st
->orientation
;
801 stg
.w
=stdisp_recommended_w(st
);
802 stg
.h
=stdisp_recommended_h(st
);
804 if(!is_lt(ori
, st
->corner
))
805 *xy(&stg
, ori
)=*wh(&wsg
, ori
)-*wh(&stg
, ori
);
810 bool geom_overlaps_stgeom_xy(WRectangle geom
, WSplitST
*st
, WRectangle stg
)
821 int ori
=st
->orientation
;
824 is_lt(ori
, st
->corner
)
825 ? *xy(&geom
, ori
)<*wh(&stg
, ori
)
826 : *xy(&geom
, ori
)+*wh(&geom
, ori
)>*xy(&stg
, ori
);
829 bool geom_aligned_stdisp(WRectangle geom
, WSplitST
*st
)
837 * ----- ------ ----- |geom| -----
838 * |stg| |stg| ------ |stg|
842 WRectangle stg
=REGION_GEOM(st
->regnode
.reg
);
843 int ori
=flip_orientation(st
->orientation
);
846 is_lt(ori
, st
->corner
)
847 ? *xy(&geom
, ori
)==*wh(&stg
, ori
)
848 : *xy(&geom
, ori
)+*wh(&geom
, ori
)==*xy(&stg
, ori
);
851 void grow_by_stdisp_wh(WRectangle
*geom
, WSplitST
*st
)
858 * ----- ------ ----- |geom|
863 WRectangle stg
=REGION_GEOM(st
->regnode
.reg
);
864 int ori
=flip_orientation(st
->orientation
);
866 if(is_lt(ori
, st
->corner
))
868 *wh(geom
, ori
)+=*wh(&stg
, ori
);
871 bool frame_neighbors_stdisp(WFrame
*frame
, WSplitST
*st
)
874 geom_overlaps_stgeom_xy(REGION_GEOM(frame
), st
, REGION_GEOM(st
)) &&
875 geom_aligned_stdisp(REGION_GEOM(frame
), st
);
878 bool geom_clashes_stdisp(WRectangle geom
, WSplitST
*st
)
880 WRectangle stg
=REGION_GEOM(st
->regnode
.reg
);
881 int ori
=flip_orientation(st
->orientation
);
883 is_lt(ori
, st
->corner
)
885 : *xy(&geom
, ori
)+*wh(&geom
, ori
)==*xy(&stg
, ori
)+*wh(&stg
, ori
);
888 bool is_same_dir(int dir
, int ori
)
891 (dir
==SPLIT_HORIZONTAL
&& ori
==REGION_ORIENTATION_HORIZONTAL
) ||
892 (dir
==SPLIT_VERTICAL
&& ori
==REGION_ORIENTATION_VERTICAL
);
895 bool is_maxed(WFrame
*frame
, int dir
)
898 dir
==SPLIT_HORIZONTAL
899 ? frame
->flags
&FRAME_MAXED_HORIZ
&& frame
->flags
&FRAME_SAVED_HORIZ
900 : frame
->flags
&FRAME_MAXED_VERT
&& frame
->flags
&FRAME_SAVED_VERT
;
903 bool update_geom_from_stdisp(WFrame
*frame
, WRectangle
*ng
, int dir
)
905 WRegion
*ws
=REGION_MANAGER(frame
);
911 if(!OBJ_IS(ws
, WTiling
) || ((WTiling
*)ws
)->stdispnode
==NULL
)
914 st
=((WTiling
*)ws
)->stdispnode
;
916 if(st
->fullsize
|| !frame_neighbors_stdisp(frame
, st
))
919 rstg
=stdisp_recommended_geom(st
, ws
->geom
);
921 if(is_same_dir(dir
, st
->orientation
) &&
922 !geom_overlaps_stgeom_xy(*ng
, st
, rstg
))
924 grow_by_stdisp_wh(ng
, st
);
925 if(is_maxed(frame
, other_dir(dir
)) &&
926 geom_aligned_stdisp(frame
->saved_geom
, st
))
928 grow_by_stdisp_wh(&frame
->saved_geom
, st
);
933 if(!is_same_dir(dir
, st
->orientation
) &&
934 geom_clashes_stdisp(frame
->saved_geom
, st
))
936 stg
=REGION_GEOM(st
->regnode
.reg
);
937 ori
=flip_orientation(st
->orientation
);
938 if(is_lt(ori
, st
->corner
))
939 *xy(ng
, ori
)+=*wh(&stg
, ori
);
940 /* We've checked that this makes sense when verifying the saved layout. */
941 *wh(ng
, ori
)-=*wh(&stg
, ori
);
947 bool splitregion_do_restore(WSplitRegion
*node
, int dir
)
950 WRectangle geom
=((WSplit
*)node
)->geom
;
955 if(!OBJ_IS(node
->reg
, WFrame
))
958 frame
=(WFrame
*)node
->reg
;
959 if(dir
==SPLIT_HORIZONTAL
){
960 geom
.x
=frame
->saved_geom
.x
;
961 geom
.w
=frame
->saved_geom
.w
;
963 geom
.y
=frame
->saved_geom
.y
;
964 geom
.h
=frame
->saved_geom
.h
;
968 dir
==SPLIT_HORIZONTAL
969 ? frame
->flags
&FRAME_MAXED_VERT
970 : frame
->flags
&FRAME_MAXED_HORIZ
;
973 ret
=update_geom_from_stdisp(frame
, &geom
, dir
);
975 /* Tell the region the correct geometry to avoid redrawing it again when
976 * the stdisp is resized by split_regularise_stdisp. Some clients (notably
977 * ncurses based ones) don't seem to react well to being resized multiple
978 * times within a short amount of time and don't refresh themselves
980 region_fit(node
->reg
, &geom
, REGION_FIT_EXACT
);
982 split_update_bounds(&(node
->split
), FALSE
);
984 /* Keep the old geometry for the WSplit. Otherwise the tiling would be
985 * inconsistent, by for example having horizontal WSplitSplit's whose
986 * children have different heights. The call to split_regularise_stdisp
987 * below will take care of correcting the geometry of the WSplit and it
988 * behaves badly when the tiling is inconsistent. */
989 node
->split
.geom
=ret
? fakegeom
: geom
;
991 frame
->flags
|=other_max
;
995 bool splitst_do_restore(WSplit
*UNUSED(node
), int UNUSED(dir
))
1000 bool splitsplit_do_restore(WSplitSplit
*node
, int dir
)
1002 bool ret1
, ret2
, ret
=FALSE
;
1003 WSplit
*snode
=(WSplit
*)node
;
1010 assert(node
->tl
!=NULL
&& node
->br
!=NULL
);
1012 if(stdisp_immediate_child(node
)){
1013 if(OBJ_IS(node
->tl
, WSplitST
)){
1014 st
=(WSplitST
*)node
->tl
;
1017 st
=(WSplitST
*)node
->br
;
1020 stg
=((WSplit
*)st
)->geom
;
1021 split_do_restore(other
, dir
);
1023 if(node
->dir
==SPLIT_HORIZONTAL
){
1030 if(rectangle_compare(&stg
, &((WSplit
*)st
)->geom
)){
1031 splitst_do_resize(st
, &stg
, PRIMN_ANY
, PRIMN_ANY
, FALSE
);
1035 /* Avoid short-circuit evaluation. */
1036 ret1
=split_do_restore(node
->tl
, dir
);
1037 ret2
=split_do_restore(node
->br
, dir
);
1041 snode
->geom
.x
=node
->tl
->geom
.x
;
1042 snode
->geom
.y
=node
->tl
->geom
.y
;
1043 if(node
->dir
==SPLIT_HORIZONTAL
){
1044 snode
->geom
.w
=node
->tl
->geom
.w
+node
->br
->geom
.w
;
1045 snode
->geom
.h
=node
->tl
->geom
.h
;
1047 if(node
->dir
==SPLIT_VERTICAL
){
1048 snode
->geom
.w
=node
->tl
->geom
.w
;
1049 snode
->geom
.h
=node
->tl
->geom
.h
+node
->br
->geom
.h
;
1055 bool split_do_restore(WSplit
*node
, int dir
)
1058 CALL_DYN_RET(ret
, bool, split_do_restore
, node
, (node
, dir
));
1063 void splitregion_do_maxhelper(WSplitRegion
*node
, int dir
, int action
)
1066 if(!OBJ_IS(node
->reg
, WFrame
))
1068 frame
=(WFrame
*)node
->reg
;
1071 frame
->flags
|=FRAME_KEEP_FLAGS
;
1072 if(dir
==HORIZONTAL
){
1073 frame
->flags
|=(FRAME_MAXED_HORIZ
|FRAME_SAVED_HORIZ
);
1074 frame
->saved_geom
.x
=REGION_GEOM(frame
).x
;
1075 frame
->saved_geom
.w
=REGION_GEOM(frame
).w
;
1077 frame
->flags
|=(FRAME_MAXED_VERT
|FRAME_SAVED_VERT
);
1078 frame
->saved_geom
.y
=REGION_GEOM(frame
).y
;
1079 frame
->saved_geom
.h
=REGION_GEOM(frame
).h
;
1082 if(action
==SET_KEEP
)
1083 frame
->flags
|=FRAME_KEEP_FLAGS
;
1085 frame
->flags
&=~FRAME_KEEP_FLAGS
;
1088 void splitst_do_maxhelper(WSplit
*UNUSED(node
), int UNUSED(dir
), int UNUSED(action
))
1093 void splitsplit_do_maxhelper(WSplitSplit
*node
, int dir
, int action
)
1095 assert(node
->tl
!=NULL
&& node
->br
!=NULL
);
1096 split_do_maxhelper(node
->tl
, dir
, action
);
1097 split_do_maxhelper(node
->br
, dir
, action
);
1100 void split_do_maxhelper(WSplit
*node
, int dir
, int action
)
1102 CALL_DYN(split_do_maxhelper
, node
, (node
, dir
, action
));
1106 bool savedgeom_clashes_stdisp(WFrame
*frame
, int dir
)
1108 WRegion
*ws
=REGION_MANAGER(frame
);
1112 if(!OBJ_IS(ws
, WTiling
) || ((WTiling
*)ws
)->stdispnode
==NULL
)
1115 st
=((WTiling
*)ws
)->stdispnode
;
1116 ori
=flip_orientation(st
->orientation
);
1119 !is_same_dir(dir
, st
->orientation
) &&
1120 frame_neighbors_stdisp(frame
, st
) &&
1121 geom_clashes_stdisp(frame
->saved_geom
, st
)
1122 ? *wh(&frame
->saved_geom
, ori
)<*wh(®ION_GEOM(st
), ori
)
1126 bool splitregion_do_verify(WSplitRegion
*node
, int dir
)
1131 if(!OBJ_IS(node
->reg
, WFrame
))
1134 frame
=(WFrame
*)node
->reg
;
1136 ret
=is_maxed(frame
, dir
);
1139 frame
->flags
&=~(FRAME_MAXED_HORIZ
|FRAME_SAVED_HORIZ
);
1141 frame
->flags
&=~(FRAME_MAXED_VERT
|FRAME_SAVED_VERT
);
1143 if(savedgeom_clashes_stdisp(frame
, dir
))
1149 bool splitst_do_verify(WSplit
*UNUSED(node
), int UNUSED(dir
))
1154 bool splitsplit_do_verify(WSplitSplit
*node
, int dir
)
1157 assert(node
->tl
!=NULL
&& node
->br
!=NULL
);
1159 /* Avoid short-circuit evaluation. */
1160 ret1
=split_do_verify(node
->tl
, dir
);
1161 ret2
=split_do_verify(node
->br
, dir
);
1162 return ret1
&& ret2
;
1165 bool split_do_verify(WSplit
*node
, int dir
)
1168 CALL_DYN_RET(ret
, bool, split_do_verify
, node
, (node
, dir
));
1173 bool split_maximize(WSplit
*node
, int dir
, int action
)
1175 WSplit
*p
=maxparentdir(node
, dir
);
1177 return split_do_restore(p
, dir
);
1179 return split_do_verify(p
, dir
);
1181 split_do_maxhelper(p
, dir
, action
);
1189 /*{{{ Low-level resize code; request towards root */
1192 static void flexibility(WSplit
*node
, int dir
, int *shrink
, int *stretch
)
1194 if(dir
==SPLIT_VERTICAL
){
1195 *shrink
=maxof(0, node
->geom
.h
-node
->min_h
);
1196 if(OBJ_IS(node
, WSplitST
))
1197 *stretch
=maxof(0, node
->max_h
-node
->geom
.h
);
1201 *shrink
=maxof(0, node
->geom
.w
-node
->min_w
);
1202 if(OBJ_IS(node
, WSplitST
))
1203 *stretch
=maxof(0, node
->max_w
-node
->geom
.w
);
1210 static void calc_amount(int *amount
, int rs
, WSplit
*other
, int dir
)
1212 int shrink
, stretch
;
1214 flexibility(other
, dir
, &shrink
, &stretch
);
1217 *amount
=minof(rs
, shrink
);
1219 *amount
=-minof(-rs
, stretch
);
1226 static void splitsplit_do_rqsize(WSplitSplit
*p
, WSplit
*node
,
1227 RootwardAmount
*ha
, RootwardAmount
*va
,
1228 WRectangle
*rg
, bool tryonly
)
1230 WPrimn hprimn
=PRIMN_ANY
, vprimn
=PRIMN_ANY
;
1231 WRectangle og
, pg
, ng
;
1237 assert(!ha
->any
|| ha
->tl
==0);
1238 assert(!va
->any
|| va
->tl
==0);
1239 assert(p
->tl
==node
|| p
->br
==node
);
1249 ca
=(p
->dir
==SPLIT_VERTICAL
? va
: ha
);
1251 if(thisnode
==PRIMN_TL
|| ca
->any
){
1252 calc_amount(&amount
, ca
->br
, other
, p
->dir
);
1254 }else/*if(thisnode==PRIMN_BR)*/{
1255 calc_amount(&amount
, ca
->tl
, other
, p
->dir
);
1259 if(((WSplit
*)p
)->parent
==NULL
/*||
1260 (ha->tl==0 && ha->br==0 && va->tl==0 && va->br==0)*/){
1261 if(((WSplit
*)p
)->ws_if_root
!=NULL
)
1262 pg
=REGION_GEOM((WTiling
*)(((WSplit
*)p
)->ws_if_root
));
1264 pg
=((WSplit
*)p
)->geom
;
1266 splitinner_do_rqsize(((WSplit
*)p
)->parent
, (WSplit
*)p
, ha
, va
,
1270 assert(pg
.w
>=0 && pg
.h
>=0);
1275 if(p
->dir
==SPLIT_VERTICAL
){
1276 ng
.h
=maxof(0, node
->geom
.h
+amount
);
1277 og
.h
=maxof(0, other
->geom
.h
-amount
);
1278 adjust_sizes(&(ng
.h
), &(og
.h
), pg
.h
, ng
.h
+og
.h
,
1279 node
->min_h
, other
->min_h
, node
->max_h
, other
->max_h
,
1280 PRIMN_TL
/* node is passed as tl param */);
1281 if(thisnode
==PRIMN_TL
)
1282 og
.y
=pg
.y
+pg
.h
-og
.h
;
1284 ng
.y
=pg
.y
+pg
.h
-ng
.h
;
1287 ng
.w
=maxof(0, node
->geom
.w
+amount
);
1288 og
.w
=maxof(0, other
->geom
.w
-amount
);
1289 adjust_sizes(&(ng
.w
), &(og
.w
), pg
.w
, ng
.w
+og
.w
,
1290 node
->min_w
, other
->min_w
, node
->max_w
, other
->max_w
,
1291 PRIMN_TL
/* node is passed as tl param */);
1292 if(thisnode
==PRIMN_TL
)
1293 og
.x
=pg
.x
+pg
.w
-og
.w
;
1295 ng
.x
=pg
.x
+pg
.w
-ng
.w
;
1300 /* Entä jos 'other' on stdisp? */
1301 split_do_resize(other
, &og
, hprimn
, vprimn
, FALSE
);
1303 ((WSplit
*)p
)->geom
=pg
;
1310 void splitinner_do_rqsize(WSplitInner
*p
, WSplit
*node
,
1311 RootwardAmount
*ha
, RootwardAmount
*va
,
1312 WRectangle
*rg
, bool tryonly
)
1314 CALL_DYN(splitinner_do_rqsize
, p
, (p
, node
, ha
, va
, rg
, tryonly
));
1318 static void initra(RootwardAmount
*ra
, int p
, int s
, int op
, int os
,
1323 ra
->br
=(p
+s
)-(op
+os
);
1331 void split_do_rqgeom_(WSplit
*node
, const WRectangle
*ng
,
1332 bool hany
, bool vany
, WRectangle
*rg
,
1335 RootwardAmount ha
, va
;
1337 if(node
->parent
==NULL
){
1338 if(node
->ws_if_root
!=NULL
)
1339 *rg
=REGION_GEOM((WTiling
*)(node
->ws_if_root
));
1343 initra(&ha
, ng
->x
, ng
->w
, node
->geom
.x
, node
->geom
.w
, hany
);
1344 initra(&va
, ng
->y
, ng
->h
, node
->geom
.y
, node
->geom
.h
, vany
);
1346 splitinner_do_rqsize(node
->parent
, node
, &ha
, &va
, rg
, tryonly
);
1354 /*{{{ Resize interface */
1357 static void bnd(int *pos
, int *sz
, int opos
, int osz
, int minsz
, int maxsz
)
1359 int ud
=abs(*pos
-opos
);
1360 int dd
=abs((*pos
+*sz
)-(opos
+osz
));
1364 bound(sz
, minsz
, maxsz
);
1365 *pos
+=(szrq
-*sz
)*ud
/(ud
+dd
);
1370 WSplit
*split_find_root(WSplit
*split
)
1372 if(split
->parent
==NULL
)
1374 return split_find_root((WSplit
*)split
->parent
);
1378 void splittree_rqgeom(WSplit
*sub
, int flags
, const WRectangle
*geom_
,
1379 WRectangle
*geomret
)
1381 bool hany
=flags
®ION_RQGEOM_WEAK_X
;
1382 bool vany
=flags
®ION_RQGEOM_WEAK_Y
;
1383 bool tryonly
=flags
®ION_RQGEOM_TRYONLY
;
1384 WRectangle geom
=*geom_
;
1386 WSplit
*root
=split_find_root(sub
);
1391 split_update_bounds(root
, TRUE
);
1393 if(OBJ_IS(sub
, WSplitST
)){
1394 WSplitST
*sub_as_stdisp
=(WSplitST
*)sub
;
1396 if(flags
®ION_RQGEOM_TRYONLY
){
1397 warn(TR("REGION_RQGEOM_TRYONLY unsupported for status display."));
1401 split_regularise_stdisp(sub_as_stdisp
);
1403 if(sub_as_stdisp
->orientation
==REGION_ORIENTATION_HORIZONTAL
){
1404 if(geom_
->h
==geom
.h
)
1408 if(geom_
->w
==geom
.w
)
1412 split_update_bounds(root
, TRUE
);
1415 /* Handle internal size bounds */
1416 bnd(&(geom
.x
), &(geom
.w
), sub
->geom
.x
, sub
->geom
.w
,
1417 sub
->min_w
, sub
->max_w
);
1418 bnd(&(geom
.y
), &(geom
.h
), sub
->geom
.y
, sub
->geom
.h
,
1419 sub
->min_h
, sub
->max_h
);
1421 /* Check if we should resize to both tl and br */
1424 geom
.w
+=sub
->geom
.x
-geom
.x
;
1429 geom
.h
+=sub
->geom
.y
-geom
.y
;
1433 splittree_begin_resize();
1435 split_do_rqgeom_(sub
, &geom
, hany
, vany
, geomret
, tryonly
);
1438 split_do_resize(sub
, geomret
, hany
, vany
, FALSE
);
1439 splittree_end_resize();
1448 * Attempt to resize and/or move the split tree starting at \var{node}.
1449 * Behaviour and the \var{g} parameter are as for \fnref{WRegion.rqgeom}
1450 * operating on \var{node} (if it were a \type{WRegion}).
1453 ExtlTab
split_rqgeom(WSplit
*node
, ExtlTab g
)
1455 WRectangle geom
, ogeom
;
1456 int flags
=REGION_RQGEOM_WEAK_ALL
;
1461 if(extl_table_gets_i(g
, "x", &(geom
.x
)))
1462 flags
&=~REGION_RQGEOM_WEAK_X
;
1463 if(extl_table_gets_i(g
, "y", &(geom
.y
)))
1464 flags
&=~REGION_RQGEOM_WEAK_Y
;
1465 if(extl_table_gets_i(g
, "w", &(geom
.w
)))
1466 flags
&=~REGION_RQGEOM_WEAK_W
;
1467 if(extl_table_gets_i(g
, "h", &(geom
.h
)))
1468 flags
&=~REGION_RQGEOM_WEAK_H
;
1470 geom
.w
=maxof(1, geom
.w
);
1471 geom
.h
=maxof(1, geom
.h
);
1473 splittree_rqgeom(node
, flags
, &geom
, &ogeom
);
1475 return extl_table_from_rectangle(&ogeom
);
1485 void splittree_changeroot(WSplit
*root
, WSplit
*node
)
1487 WTiling
*ws
=(WTiling
*)(root
->ws_if_root
);
1490 assert(ws
->split_tree
==root
);
1491 root
->ws_if_root
=NULL
;
1492 ws
->split_tree
=node
;
1494 node
->ws_if_root
=ws
;
1500 static void splitsplit_replace(WSplitSplit
*split
, WSplit
*child
,
1503 assert(split
->tl
==child
|| split
->br
==child
);
1505 if(split
->tl
==child
)
1512 what
->parent
=(WSplitInner
*)split
;
1513 what
->ws_if_root
=NULL
; /* May not be needed. */
1517 void splitinner_replace(WSplitInner
*split
, WSplit
*child
, WSplit
*what
)
1519 CALL_DYN(splitinner_replace
, split
, (split
, child
, what
));
1523 WSplitRegion
*splittree_split(WSplit
*node
, int dir
, WPrimn primn
,
1524 int minsize
, WRegionSimpleCreateFn
*fn
,
1529 WSplitSplit
*nsplit
;
1530 WSplitRegion
*nnode
;
1531 WSplitInner
*psplit
;
1536 assert(node
!=NULL
&& parent
!=NULL
);
1538 splittree_begin_resize();
1540 node
=dodge_stdisp(node
, FALSE
);
1545 if(OBJ_IS(node
, WSplitST
)){
1546 warn(TR("Splitting the status display is not allowed."));
1550 if(primn
!=PRIMN_TL
&& primn
!=PRIMN_BR
)
1552 if(dir
!=SPLIT_HORIZONTAL
&& dir
!=SPLIT_VERTICAL
)
1555 split_update_bounds(split_find_root(node
), TRUE
);
1556 objmin
=(dir
==SPLIT_VERTICAL
? node
->min_h
: node
->min_w
);
1558 s
=split_size(node
, dir
);
1559 sn
=maxof(minsize
, s
/2);
1560 so
=maxof(objmin
, s
-sn
);
1565 if(dir
==SPLIT_VERTICAL
)
1569 split_do_rqgeom_(node
, &ng
, TRUE
, TRUE
, &rg
, TRUE
);
1570 rs
=(dir
==SPLIT_VERTICAL
? rg
.h
: rg
.w
);
1571 if(rs
<minsize
+objmin
){
1572 warn(TR("Unable to split: not enough free space."));
1575 split_do_rqgeom_(node
, &ng
, TRUE
, TRUE
, &rg
, FALSE
);
1576 rs
=(dir
==SPLIT_VERTICAL
? rg
.h
: rg
.w
);
1581 so
=maxof(rs
/2, objmin
);
1586 splittree_scan_stdisp_rootward(node
);
1589 /* Create split and new window
1591 fp
.mode
=REGION_FIT_EXACT
;
1594 nsplit
=create_splitsplit(&(fp
.g
), dir
);
1599 if(dir
==SPLIT_VERTICAL
){
1609 nreg
=fn(parent
, &fp
);
1612 destroy_obj((Obj
*)nsplit
);
1616 nnode
=create_splitregion(&(fp
.g
), nreg
);
1618 destroy_obj((Obj
*)nreg
);
1619 destroy_obj((Obj
*)nsplit
);
1623 /* Now that everything's ok, resize and move original node.
1626 if(dir
==SPLIT_VERTICAL
){
1636 split_do_resize(node
, &ng
,
1637 (dir
==SPLIT_HORIZONTAL
? primn
: PRIMN_ANY
),
1638 (dir
==SPLIT_VERTICAL
? primn
: PRIMN_ANY
),
1641 /* Set up split structure
1643 psplit
=node
->parent
;
1646 splitinner_replace(psplit
, node
, (WSplit
*)nsplit
);
1648 splittree_changeroot(node
, (WSplit
*)nsplit
);
1650 node
->parent
=(WSplitInner
*)nsplit
;
1651 ((WSplit
*)nnode
)->parent
=(WSplitInner
*)nsplit
;
1653 if(primn
==PRIMN_BR
){
1655 nsplit
->br
=(WSplit
*)nnode
;
1656 nsplit
->current
=SPLIT_CURRENT_TL
;
1658 nsplit
->tl
=(WSplit
*)nnode
;
1660 nsplit
->current
=SPLIT_CURRENT_BR
;
1663 splittree_end_resize();
1675 static void splitsplit_remove(WSplitSplit
*node
, WSplit
*child
,
1678 static int nstdisp
=0;
1679 WSplitInner
*parent
;
1681 int hprimn
=PRIMN_ANY
, vprimn
=PRIMN_ANY
;
1683 assert(node
->tl
==child
|| node
->br
==child
);
1685 if(node
->tl
==child
){
1687 if(node
->dir
==SPLIT_VERTICAL
)
1693 if(node
->dir
==SPLIT_VERTICAL
)
1699 assert(other
!=NULL
);
1701 if(nstdisp
==0 && reclaim_space
&& OBJ_IS(other
, WSplitST
)){
1702 /* Try to move stdisp out of the way. */
1703 split_try_unsink_stdisp(node
, FALSE
, TRUE
);
1704 assert(child
->parent
!=NULL
);
1706 splitinner_remove(child
->parent
, child
, reclaim_space
);
1711 parent
=((WSplit
*)node
)->parent
;
1714 splitinner_replace(parent
, (WSplit
*)node
, other
);
1716 splittree_changeroot((WSplit
*)node
, other
);
1719 split_resize(other
, &(((WSplit
*)node
)->geom
), hprimn
, vprimn
);
1725 ((WSplit
*)node
)->parent
=NULL
;
1726 destroy_obj((Obj
*)node
);
1730 void splitinner_remove(WSplitInner
*node
, WSplit
*child
, bool reclaim_space
)
1732 CALL_DYN(splitinner_remove
, node
, (node
, child
, reclaim_space
));
1736 void splittree_remove(WSplit
*node
, bool reclaim_space
)
1738 if(node
->parent
!=NULL
)
1739 splitinner_remove(node
->parent
, node
, reclaim_space
);
1740 else if(node
->ws_if_root
!=NULL
)
1741 splittree_changeroot(node
, NULL
);
1743 destroy_obj((Obj
*)node
);
1750 /*{{{ Tree traversal */
1753 static bool defaultfilter(WSplit
*node
)
1755 return (OBJ_IS(node
, WSplitRegion
) &&
1756 ((WSplitRegion
*)node
)->reg
!=NULL
);
1760 static WSplit
*split_current_todir_default(WSplit
*node
,
1761 WPrimn
UNUSED(hprimn
), WPrimn
UNUSED(vprimn
),
1762 WSplitFilter
*filter
)
1765 filter
=defaultfilter
;
1767 return (filter(node
) ? node
: NULL
);
1771 static WSplit
*splitsplit_current_todir(WSplitSplit
*node
,
1772 WPrimn hprimn
, WPrimn vprimn
,
1773 WSplitFilter
*filter
)
1775 WPrimn primn
=(node
->dir
==SPLIT_HORIZONTAL
? hprimn
: vprimn
);
1776 WSplit
*first
, *second
, *ret
;
1778 if(primn
==PRIMN_TL
||
1779 (primn
==PRIMN_ANY
&& node
->current
==SPLIT_CURRENT_TL
)){
1782 }else if(primn
==PRIMN_BR
||
1783 (primn
==PRIMN_ANY
&& node
->current
==SPLIT_CURRENT_BR
)){
1790 ret
=split_current_todir(first
, hprimn
, vprimn
, filter
);
1792 ret
=split_current_todir(second
, hprimn
, vprimn
, filter
);
1793 if(ret
==NULL
&& filter
!=NULL
){
1794 if(filter((WSplit
*)node
))
1802 WSplit
*split_current_todir(WSplit
*node
, WPrimn hprimn
, WPrimn vprimn
,
1803 WSplitFilter
*filter
)
1806 CALL_DYN_RET(ret
, WSplit
*, split_current_todir
, node
,
1807 (node
, hprimn
, vprimn
, filter
));
1812 /* Note: both hprimn and vprimn are inverted when descending. Therefore
1813 * one should be either PRIMN_NONE or PRIMN_ANY for sensible geometric
1814 * navigation. (Both are PRIMN_TL or PRIMN_BR for pseudo-linear
1815 * next/previous navigation.)
1817 WSplit
*splitsplit_nextto(WSplitSplit
*node
, WSplit
*child
,
1818 WPrimn hprimn
, WPrimn vprimn
,
1819 WSplitFilter
*filter
)
1821 WPrimn primn
=(node
->dir
==SPLIT_HORIZONTAL
? hprimn
: vprimn
);
1822 WSplit
*split
=NULL
, *nnode
=NULL
;
1824 if(node
->tl
==child
&& (primn
==PRIMN_BR
|| primn
==PRIMN_ANY
))
1826 else if(node
->br
==child
&& (primn
==PRIMN_TL
|| primn
==PRIMN_ANY
))
1830 nnode
=split_current_todir(split
,
1831 primn_none2any(primn_invert(hprimn
)),
1832 primn_none2any(primn_invert(vprimn
)),
1837 nnode
=split_nextto((WSplit
*)node
, hprimn
, vprimn
, filter
);
1843 WSplit
*splitinner_nextto(WSplitInner
*node
, WSplit
*child
,
1844 WPrimn hprimn
, WPrimn vprimn
,
1845 WSplitFilter
*filter
)
1848 CALL_DYN_RET(ret
, WSplit
*, splitinner_nextto
, node
,
1849 (node
, child
, hprimn
, vprimn
, filter
));
1854 WSplit
*split_nextto(WSplit
*node
, WPrimn hprimn
, WPrimn vprimn
,
1855 WSplitFilter
*filter
)
1857 while(node
->parent
!=NULL
){
1858 WSplit
*ret
=splitinner_nextto(node
->parent
, node
,
1859 hprimn
, vprimn
, filter
);
1862 node
=(WSplit
*)node
->parent
;
1868 void splitinner_mark_current_default(WSplitInner
*split
, WSplit
*UNUSED(child
))
1870 if(((WSplit
*)split
)->parent
!=NULL
)
1871 splitinner_mark_current(((WSplit
*)split
)->parent
, (WSplit
*)split
);
1875 void splitsplit_mark_current(WSplitSplit
*split
, WSplit
*child
)
1877 assert(child
==split
->tl
|| child
==split
->br
);
1879 split
->current
=(split
->tl
==child
? SPLIT_CURRENT_TL
: SPLIT_CURRENT_BR
);
1881 splitinner_mark_current_default(&(split
->isplit
), child
);
1885 void splitinner_mark_current(WSplitInner
*split
, WSplit
*child
)
1887 CALL_DYN(splitinner_mark_current
, split
, (split
, child
));
1891 static void splitsplit_forall(WSplitSplit
*node
, WSplitFn
*fn
)
1898 void splitinner_forall(WSplitInner
*node
, WSplitFn
*fn
)
1900 CALL_DYN(splitinner_forall
, node
, (node
, fn
));
1904 static WSplit
*splitsplit_current(WSplitSplit
*split
)
1906 return (split
->current
==SPLIT_CURRENT_TL
? split
->tl
: split
->br
);
1911 * Returns the most previously active child node of \var{split}.
1915 WSplit
*splitinner_current(WSplitInner
*node
)
1918 CALL_DYN_RET(ret
, WSplit
*, splitinner_current
, node
, (node
));
1926 /*{{{ X window handling */
1929 static void splitregion_stacking(WSplitRegion
*split
,
1930 Window
*bottomret
, Window
*topret
)
1934 if(split
->reg
!=NULL
)
1935 region_stacking(split
->reg
, bottomret
, topret
);
1939 void splitsplit_stacking(WSplitSplit
*split
,
1940 Window
*bottomret
, Window
*topret
)
1942 Window tlb
=None
, tlt
=None
;
1943 Window brb
=None
, brt
=None
;
1945 split_stacking(split
->tl
, &tlb
, &tlt
);
1946 split_stacking(split
->br
, &brb
, &brt
);
1948 /* To make sure that this condition holds is left to the workspace
1949 * code to do after a split tree has been loaded or modified.
1951 if(split
->current
==SPLIT_CURRENT_TL
){
1952 *topret
=(tlt
!=None
? tlt
: brt
);
1953 *bottomret
=(brb
!=None
? brb
: tlb
);
1955 *topret
=(brt
!=None
? brt
: tlt
);
1956 *bottomret
=(tlb
!=None
? tlb
: brb
);
1960 void split_stacking(WSplit
*split
, Window
*bottomret
, Window
*topret
)
1965 CALL_DYN(split_stacking
, split
, (split
, bottomret
, topret
));
1970 static void splitregion_restack(WSplitRegion
*split
, Window other
, int mode
)
1972 if(split
->reg
!=NULL
)
1973 region_restack(split
->reg
, other
, mode
);
1976 void splitsplit_restack(WSplitSplit
*split
, Window other
, int mode
)
1978 Window bottom
=None
, top
=None
;
1979 WSplit
*first
, *second
;
1981 if(split
->current
==SPLIT_CURRENT_TL
){
1989 split_restack(first
, other
, mode
);
1990 split_stacking(first
, &bottom
, &top
);
1995 split_restack(second
, other
, mode
);
1998 void split_restack(WSplit
*split
, Window other
, int mode
)
2000 CALL_DYN(split_restack
, split
, (split
, other
, mode
));
2004 static void splitregion_map(WSplitRegion
*split
)
2006 if(split
->reg
!=NULL
)
2007 region_map(split
->reg
);
2010 static void splitinner_map(WSplitInner
*split
)
2012 splitinner_forall(split
, split_map
);
2015 void split_map(WSplit
*split
)
2017 CALL_DYN(split_map
, split
, (split
));
2021 static void splitregion_unmap(WSplitRegion
*split
)
2023 if(split
->reg
!=NULL
)
2024 region_unmap(split
->reg
);
2027 static void splitinner_unmap(WSplitInner
*split
)
2029 splitinner_forall(split
, split_unmap
);
2032 void split_unmap(WSplit
*split
)
2034 CALL_DYN(split_unmap
, split
, (split
));
2038 static void splitregion_reparent(WSplitRegion
*split
, WWindow
*wwin
)
2040 if(split
->reg
!=NULL
){
2041 WRectangle g
=split
->split
.geom
;
2042 region_reparent(split
->reg
, wwin
, &g
, REGION_FIT_EXACT
);
2047 static void splitsplit_reparent(WSplitSplit
*split
, WWindow
*wwin
)
2049 if(split
->current
==SPLIT_CURRENT_TL
){
2050 split_reparent(split
->br
, wwin
);
2051 split_reparent(split
->tl
, wwin
);
2053 split_reparent(split
->tl
, wwin
);
2054 split_reparent(split
->br
, wwin
);
2059 void split_reparent(WSplit
*split
, WWindow
*wwin
)
2061 CALL_DYN(split_reparent
, split
, (split
, wwin
));
2068 /*{{{ Transpose, flip, rotate */
2071 void splitsplit_flip_default(WSplitSplit
*split
)
2073 WRectangle tlng
, brng
;
2074 WRectangle
*sg
=&((WSplit
*)split
)->geom
;
2077 assert(split
->tl
!=NULL
&& split
->br
!=NULL
);
2079 split_update_bounds((WSplit
*)split
, TRUE
);
2081 tlng
=split
->tl
->geom
;
2082 brng
=split
->br
->geom
;
2084 if(split
->dir
==SPLIT_HORIZONTAL
){
2086 tlng
.x
=sg
->x
+sg
->w
-tlng
.w
;
2089 tlng
.y
=sg
->y
+sg
->h
-tlng
.h
;
2093 split
->tl
=split
->br
;
2095 split
->current
=(split
->current
==SPLIT_CURRENT_TL
2097 : SPLIT_CURRENT_TL
);
2099 split_do_resize(split
->tl
, &brng
, PRIMN_ANY
, PRIMN_ANY
, FALSE
);
2100 split_do_resize(split
->br
, &tlng
, PRIMN_ANY
, PRIMN_ANY
, FALSE
);
2104 static void splitsplit_flip_(WSplitSplit
*split
)
2106 CALL_DYN(splitsplit_flip
, split
, (split
));
2111 * Flip contents of \var{split}.
2114 void splitsplit_flip(WSplitSplit
*split
)
2116 splittree_begin_resize();
2118 split
=OBJ_CAST(dodge_stdisp((WSplit
*)split
, FALSE
), WSplitSplit
);
2123 splitsplit_flip_(split
);
2125 splittree_end_resize();
2136 static FlipDir flipdir
=FLIP_VERTICAL
;
2139 static void do_flip(WSplit
*split
)
2141 WSplitSplit
*ss
=OBJ_CAST(split
, WSplitSplit
);
2144 if((flipdir
==FLIP_ANY
2145 || (ss
->dir
==SPLIT_VERTICAL
&& flipdir
==FLIP_VERTICAL
)
2146 || (ss
->dir
==SPLIT_HORIZONTAL
&& flipdir
==FLIP_HORIZONTAL
))
2147 && !OBJ_IS(ss
->tl
, WSplitST
)
2148 && !OBJ_IS(ss
->br
, WSplitST
)){
2149 splitsplit_flip_(ss
);
2153 if(OBJ_IS(ss
, WSplitInner
))
2154 splitinner_forall((WSplitInner
*)ss
, do_flip
);
2158 static void splittree_flip_dir(WSplit
*splittree
, FlipDir dir
)
2160 /* todo stdisp outta way */
2161 if(OBJ_IS(splittree
, WSplitInner
)){
2163 splitinner_forall((WSplitInner
*)splittree
, do_flip
);
2168 static bool split_fliptrans_to(WSplit
*node
, const WRectangle
*geom
,
2169 bool trans
, FlipDir flip
)
2174 splittree_begin_resize();
2176 /* split_do_resize can do things right if 'node' has stdisp as child,
2177 * but otherwise transpose will put the stdisp in a bad split
2178 * configuration if it is contained within 'node', so we must
2179 * first move it and its fixed parent split below node. For correct
2180 * geometry calculation we move it immediately below node, and
2181 * resize stdisp's fixed parent node instead.
2183 node2
=dodge_stdisp(node
, TRUE
);
2185 if(node
==NULL
|| node2
!=node
)
2188 split_update_bounds(node
, TRUE
);
2190 split_do_rqgeom_(node
, geom
, PRIMN_ANY
, PRIMN_ANY
, &rg
, FALSE
);
2192 split_do_resize(node
, &rg
, PRIMN_ANY
, PRIMN_ANY
, trans
);
2195 splittree_flip_dir(node
, flip
);
2197 splittree_end_resize();
2203 bool split_transpose_to(WSplit
*node
, const WRectangle
*geom
)
2205 return split_fliptrans_to(node
, geom
, TRUE
, FLIP_ANY
);
2210 * Transpose contents of \var{node}.
2213 void split_transpose(WSplit
*node
)
2215 WRectangle g
=node
->geom
;
2217 split_transpose_to(node
, &g
);
2221 bool split_rotate_to(WSplit
*node
, const WRectangle
*geom
, int rotation
)
2223 FlipDir flip
=FLIP_NONE
;
2226 if(rotation
==SCREEN_ROTATION_90
){
2227 flip
=FLIP_HORIZONTAL
;
2229 }else if(rotation
==SCREEN_ROTATION_180
){
2231 }else if(rotation
==SCREEN_ROTATION_270
){
2236 return split_fliptrans_to(node
, geom
, trans
, flip
);
2246 * Return parent split for \var{split}.
2250 WSplitInner
*split_parent(WSplit
*split
)
2252 return split
->parent
;
2257 * Returns the area of workspace used by the regions under \var{split}.
2261 ExtlTab
split_geom(WSplit
*split
)
2263 return extl_table_from_rectangle(&(split
->geom
));
2268 * Returns the top or left child node of \var{split} depending
2269 * on the direction of the split.
2273 WSplit
*splitsplit_tl(WSplitSplit
*split
)
2280 * Returns the bottom or right child node of \var{split} depending
2281 * on the direction of the split.
2285 WSplit
*splitsplit_br(WSplitSplit
*split
)
2291 * Returns the direction of \var{split}; either \codestr{vertical} or
2292 * \codestr{horizontal}.
2296 const char *splitsplit_dir(WSplitSplit
*split
)
2298 return (split
->dir
==SPLIT_VERTICAL
? "vertical" : "horizontal");
2303 * Returns the region contained in \var{node}.
2307 WRegion
*splitregion_reg(WSplitRegion
*node
)
2316 /*{{{ Save support */
2319 ExtlTab
split_base_config(WSplit
*node
)
2321 ExtlTab t
=extl_create_table();
2322 extl_table_sets_s(t
, "type", OBJ_TYPESTR(node
));
2327 static bool splitregion_get_config(WSplitRegion
*node
, ExtlTab
*ret
)
2334 if(!region_supports_save(node
->reg
)){
2335 warn(TR("Unable to get configuration for %s."),
2336 region_name(node
->reg
));
2340 rt
=region_get_configuration(node
->reg
);
2341 t
=split_base_config(&(node
->split
));
2342 extl_table_sets_t(t
, "regparams", rt
);
2343 extl_unref_table(rt
);
2350 static bool splitst_get_config(WSplitST
*node
, ExtlTab
*ret
)
2352 *ret
=split_base_config((WSplit
*)node
);
2357 static bool splitsplit_get_config(WSplitSplit
*node
, ExtlTab
*ret
)
2359 ExtlTab tab
, tltab
, brtab
;
2362 if(!split_get_config(node
->tl
, &tltab
))
2363 return split_get_config(node
->br
, ret
);
2365 if(!split_get_config(node
->br
, &brtab
)){
2370 tab
=split_base_config((WSplit
*)node
);
2372 tls
=split_size(node
->tl
, node
->dir
);
2373 brs
=split_size(node
->br
, node
->dir
);
2375 extl_table_sets_s(tab
, "dir", (node
->dir
==SPLIT_VERTICAL
2376 ? "vertical" : "horizontal"));
2378 extl_table_sets_i(tab
, "tls", tls
);
2379 extl_table_sets_t(tab
, "tl", tltab
);
2380 extl_unref_table(tltab
);
2382 extl_table_sets_i(tab
, "brs", brs
);
2383 extl_table_sets_t(tab
, "br", brtab
);
2384 extl_unref_table(brtab
);
2392 bool split_get_config(WSplit
*node
, ExtlTab
*tabret
)
2395 CALL_DYN_RET(ret
, bool, split_get_config
, node
, (node
, tabret
));
2403 /*{{{ The classes */
2406 static DynFunTab split_dynfuntab
[]={
2407 {split_do_resize
, split_do_resize_default
},
2408 {(DynFun
*)split_current_todir
, (DynFun
*)split_current_todir_default
},
2412 static DynFunTab splitinner_dynfuntab
[]={
2413 {splitinner_mark_current
, splitinner_mark_current_default
},
2414 {split_map
, splitinner_map
},
2415 {split_unmap
, splitinner_unmap
},
2419 static DynFunTab splitsplit_dynfuntab
[]={
2420 {split_update_bounds
, splitsplit_update_bounds
},
2421 {split_do_resize
, splitsplit_do_resize
},
2422 {split_do_maxhelper
, splitsplit_do_maxhelper
},
2423 {(DynFun
*)split_do_restore
, (DynFun
*)splitsplit_do_restore
},
2424 {(DynFun
*)split_do_verify
, (DynFun
*)splitsplit_do_verify
},
2425 {splitinner_do_rqsize
, splitsplit_do_rqsize
},
2426 {splitinner_replace
, splitsplit_replace
},
2427 {splitinner_remove
, splitsplit_remove
},
2428 {(DynFun
*)split_current_todir
, (DynFun
*)splitsplit_current_todir
},
2429 {(DynFun
*)splitinner_current
, (DynFun
*)splitsplit_current
},
2430 {(DynFun
*)splitinner_nextto
, (DynFun
*)splitsplit_nextto
},
2431 {splitinner_mark_current
, splitsplit_mark_current
},
2432 {(DynFun
*)split_get_config
, (DynFun
*)splitsplit_get_config
},
2433 {splitinner_forall
, splitsplit_forall
},
2434 {split_restack
, splitsplit_restack
},
2435 {split_stacking
, splitsplit_stacking
},
2436 {split_reparent
, splitsplit_reparent
},
2437 {splitsplit_flip
, splitsplit_flip_default
},
2441 static DynFunTab splitregion_dynfuntab
[]={
2442 {split_update_bounds
, splitregion_update_bounds
},
2443 {split_do_resize
, splitregion_do_resize
},
2444 {split_do_maxhelper
, splitregion_do_maxhelper
},
2445 {(DynFun
*)split_do_restore
, (DynFun
*)splitregion_do_restore
},
2446 {(DynFun
*)split_do_verify
, (DynFun
*)splitregion_do_verify
},
2447 {(DynFun
*)split_get_config
, (DynFun
*)splitregion_get_config
},
2448 {split_map
, splitregion_map
},
2449 {split_unmap
, splitregion_unmap
},
2450 {split_restack
, splitregion_restack
},
2451 {split_stacking
, splitregion_stacking
},
2452 {split_reparent
, splitregion_reparent
},
2456 static DynFunTab splitst_dynfuntab
[]={
2457 {split_update_bounds
, splitst_update_bounds
},
2458 {split_do_resize
, splitst_do_resize
},
2459 {split_do_maxhelper
, splitst_do_maxhelper
},
2460 {(DynFun
*)split_do_restore
, (DynFun
*)splitst_do_restore
},
2461 {(DynFun
*)split_do_verify
, (DynFun
*)splitst_do_verify
},
2462 {(DynFun
*)split_get_config
, (DynFun
*)splitst_get_config
},
2468 IMPLCLASS(WSplit
, Obj
, split_deinit
, split_dynfuntab
);
2471 IMPLCLASS(WSplitInner
, WSplit
, splitinner_deinit
, splitinner_dynfuntab
);
2474 IMPLCLASS(WSplitSplit
, WSplitInner
, splitsplit_deinit
, splitsplit_dynfuntab
);
2477 IMPLCLASS(WSplitRegion
, WSplit
, splitregion_deinit
, splitregion_dynfuntab
);
2480 IMPLCLASS(WSplitST
, WSplitRegion
, splitst_deinit
, splitst_dynfuntab
);