More cleanups of dead code
[notion.git] / mod_tiling / split.c
blob86a312115d0a98713ec978a20ccdbca789e96834
1 /*
2 * ion/mod_tiling/split.c
4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
7 */
9 #include <limits.h>
10 #include <string.h>
11 #include <X11/Xmd.h>
13 #include <libtu/minmax.h>
14 #include <libtu/rb.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>
27 #include "tiling.h"
28 #include "split.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)
61 int tmp;
63 if(dir==SPLIT_HORIZONTAL)
64 tmp=region_min_w(reg);
65 else
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)
79 return INT_MAX;
80 else
81 return x+y;
85 static int infsub(int x, int y)
87 if(x==INT_MAX)
88 return INT_MAX;
89 else if(y==INT_MAX)
90 return 0;
91 else
92 return x-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
98 * node.
100 static int unusedadd(int x, int y)
102 if(x<0 && y<0)
103 return -1;
104 return maxof(x, 0)+maxof(y, 0);
108 static void bound(int *what, int min, int max)
110 if(*what<min)
111 *what=min;
112 else if(*what>max)
113 *what=max;
117 /*}}}*/
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)
127 Rb_node node=NULL;
128 int found=0;
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);
134 if(found)
135 return (WSplitRegion*)(node->v.val);
138 return NULL;
142 #define set_node_of_reg splittree_set_node_of
145 bool splittree_set_node_of(WRegion *reg, WSplitRegion *split)
147 Rb_node node=NULL;
148 int found;
150 /*assert(REGION_MANAGER_CHK(reg, WTiling)!=NULL);*/
152 if(split_of_map==NULL){
153 if(split==NULL)
154 return TRUE;
155 split_of_map=make_rb();
156 if(split_of_map==NULL)
157 return FALSE;
160 node=rb_find_pkey_n(split_of_map, reg, &found);
161 if(found)
162 rb_delete_node(node);
164 return (rb_insertp(split_of_map, reg, split)!=NULL);
168 /*}}}*/
171 /*{{{ Primn */
174 WPrimn primn_invert(WPrimn primn)
176 return (primn==PRIMN_TL
177 ? PRIMN_BR
178 : (primn==PRIMN_BR
179 ? PRIMN_TL
180 : primn));
184 WPrimn primn_none2any(WPrimn primn)
186 return (primn==PRIMN_NONE ? PRIMN_ANY : primn);
190 /*}}}*/
193 /*{{{ Create */
196 bool split_init(WSplit *split, const WRectangle *geom)
198 split->parent=NULL;
199 split->ws_if_root=NULL;
200 split->geom=*geom;
201 split->min_w=0;
202 split->min_h=0;
203 split->max_w=INT_MAX;
204 split->max_h=INT_MAX;
205 split->unused_w=-1;
206 split->unused_h=-1;
207 return TRUE;
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);
219 split->dir=dir;
220 split->tl=NULL;
221 split->br=NULL;
222 split->current=SPLIT_CURRENT_TL;
223 return TRUE;
227 bool splitregion_init(WSplitRegion *split, const WRectangle *geom,
228 WRegion *reg)
230 split_init(&(split->split), geom);
231 split->reg=reg;
232 if(reg!=NULL)
233 set_node_of_reg(reg, split);
234 return TRUE;
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;
243 return TRUE;
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));
265 /*}}}*/
268 /*{{{ Deinit */
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)
285 if(split->tl!=NULL){
286 split->tl->parent=NULL;
287 destroy_obj((Obj*)(split->tl));
289 if(split->br!=NULL){
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);
302 split->reg=NULL;
305 split_deinit(&(split->split));
309 void splitst_deinit(WSplitST *split)
311 splitregion_deinit(&(split->regnode));
315 /*}}}*/
318 /*{{{ Size bounds management */
321 static void splitregion_update_bounds(WSplitRegion *node, bool recursive)
323 WSizeHints hints;
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;
332 snode->unused_w=-1;
334 snode->min_h=maxof(1, hints.min_set ? hints.min_height : 1);
335 snode->max_h=INT_MAX;
336 snode->unused_h=-1;
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;
349 }else{
350 WSizeHints hints;
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);
358 snode->unused_w=-1;
359 snode->unused_h=-1;
361 if(node->orientation==REGION_ORIENTATION_HORIZONTAL){
362 snode->min_w=CF_STDISP_MIN_SZ;
363 snode->max_w=INT_MAX;
364 }else{
365 snode->min_h=CF_STDISP_MIN_SZ;
366 snode->max_h=INT_MAX;
371 static void splitsplit_update_bounds(WSplitSplit *split, bool recursive)
373 WSplit *tl, *br;
374 WSplit *node=(WSplit*)split;
376 assert(split->tl!=NULL && split->br!=NULL);
378 tl=split->tl;
379 br=split->br;
381 if(recursive){
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);
393 }else{
394 node->max_h=infadd(tl->max_h, br->max_h);
395 node->min_h=infadd(tl->min_h, br->min_h);
396 node->unused_h=unusedadd(tl->unused_h, br->unused_h);
397 node->min_w=maxof(tl->min_w, br->min_w);
398 node->max_w=maxof(minof(tl->max_w, br->max_w), node->min_w);
399 node->unused_w=minof(tl->unused_w, br->unused_w);
404 void split_update_bounds(WSplit *node, bool recursive)
406 CALL_DYN(split_update_bounds, node, (node, recursive));
410 void splitsplit_update_geom_from_children(WSplitSplit *node)
412 if(node->dir==SPLIT_VERTICAL){
413 ((WSplit*)node)->geom.h=node->tl->geom.h+node->br->geom.h;
414 ((WSplit*)node)->geom.y=node->tl->geom.y;
415 }else if(node->dir==SPLIT_HORIZONTAL){
416 ((WSplit*)node)->geom.w=node->tl->geom.w+node->br->geom.w;
417 ((WSplit*)node)->geom.x=node->tl->geom.x;
422 /*}}}*/
425 /*{{{ Status display handling helper functions. */
428 static WSplitST *saw_stdisp=NULL;
431 void splittree_begin_resize()
433 saw_stdisp=NULL;
437 void splittree_end_resize()
439 if(saw_stdisp!=NULL){
440 split_regularise_stdisp(saw_stdisp);
441 saw_stdisp=NULL;
446 static void splittree_scan_stdisp_rootward_(WSplitInner *node_)
448 WSplitSplit *node=OBJ_CAST(node_, WSplitSplit);
450 if(node!=NULL){
451 if(OBJ_IS(node->tl, WSplitST)){
452 saw_stdisp=(WSplitST*)(node->tl);
453 return;
454 }else if(OBJ_IS(node->br, WSplitST)){
455 saw_stdisp=(WSplitST*)(node->br);
456 return;
460 if(node_->split.parent!=NULL)
461 splittree_scan_stdisp_rootward_(node_->split.parent);
465 void splittree_scan_stdisp_rootward(WSplit *node)
467 if(node->parent!=NULL)
468 splittree_scan_stdisp_rootward_(node->parent);
472 static WSplitST *splittree_scan_stdisp(WSplit *node_, bool set_saw)
474 WSplitST *r=NULL;
475 WSplitSplit *node=OBJ_CAST(node_, WSplitSplit);
477 if(node==NULL)
478 return NULL;
480 r=OBJ_CAST(node->tl, WSplitST);
481 if(r==NULL)
482 r=OBJ_CAST(node->br, WSplitST);
484 if(r!=NULL){
485 if(set_saw)
486 saw_stdisp=r;
487 return r;
490 r=splittree_scan_stdisp(node->tl, set_saw);
491 if(r==NULL)
492 r=splittree_scan_stdisp(node->br, set_saw);
494 return r;
498 static bool stdisp_immediate_child(WSplitSplit *node)
500 return (node!=NULL && (OBJ_IS(node->tl, WSplitST) ||
501 OBJ_IS(node->br, WSplitST)));
505 static WSplit *dodge_stdisp(WSplit *node, bool keep_within)
507 WSplitST *stdisp;
508 WSplitSplit *stdispp;
510 stdisp=splittree_scan_stdisp(node, TRUE);
512 if(stdisp==NULL)
513 return node;
515 stdispp=OBJ_CAST(((WSplit*)stdisp)->parent, WSplitSplit);
517 if(stdispp==NULL)
518 return node;
520 if((WSplit*)stdispp==node){
521 /* Node itself immediately contains stdisp. Due to the way
522 * try_unsink works, stdisp this will not change, so another
523 * node must be used, if we want to fully dodge stdisp.
525 return (keep_within
526 ? node
527 : (stdispp->tl==(WSplit*)stdisp
528 ? stdispp->br
529 : stdispp->tl));
533 if(!split_try_unsink_stdisp(stdispp, FALSE, TRUE)){
534 warn(TR("Unable to move the status display out of way."));
535 return NULL;
537 }while(stdispp->tl!=node && stdispp->br!=node);
539 return node;
543 /*}}}*/
546 /*{{{ Low-level resize code; from root to leaf */
549 static void split_do_resize_default(WSplit *node, const WRectangle *ng,
550 WPrimn hprimn, WPrimn vprimn,
551 bool transpose)
553 node->geom=*ng;
557 static void splitregion_do_resize(WSplitRegion *node, const WRectangle *ng,
558 WPrimn hprimn, WPrimn vprimn,
559 bool transpose)
561 assert(node->reg!=NULL);
562 region_fit(node->reg, ng, REGION_FIT_EXACT);
563 split_update_bounds(&(node->split), FALSE);
564 node->split.geom=*ng;
568 static void splitst_do_resize(WSplitST *node, const WRectangle *ng,
569 WPrimn hprimn, WPrimn vprimn,
570 bool transpose)
572 saw_stdisp=node;
574 if(node->regnode.reg==NULL){
575 ((WSplit*)node)->geom=*ng;
576 }else{
577 splitregion_do_resize(&(node->regnode), ng, hprimn, vprimn,
578 transpose);
583 static int other_dir(int dir)
585 return (dir==SPLIT_VERTICAL ? SPLIT_HORIZONTAL : SPLIT_VERTICAL);
589 static void adjust_sizes(int *tls_, int *brs_, int nsize, int sz,
590 int tlmin, int brmin, int tlmax, int brmax,
591 int primn)
593 int tls=*tls_;
594 int brs=*brs_;
596 if(primn==PRIMN_TL){
597 tls=tls+nsize-sz;
598 bound(&tls, tlmin, tlmax);
599 brs=nsize-tls;
600 bound(&brs, brmin, brmax);
601 tls=nsize-brs;
602 bound(&tls, tlmin, tlmax);
603 }else if(primn==PRIMN_BR){
604 brs=brs+nsize-sz;
605 bound(&brs, brmin, brmax);
606 tls=nsize-brs;
607 bound(&tls, tlmin, tlmax);
608 brs=nsize-tls;
609 bound(&brs, brmin, brmax);
610 }else{ /* && PRIMN_ANY */
611 tls=tls*nsize/sz;
612 bound(&tls, tlmin, tlmax);
613 brs=nsize-tls;
614 bound(&brs, brmin, brmax);
615 tls=nsize-brs;
616 bound(&tls, tlmin, tlmax);
619 *tls_=tls;
620 *brs_=brs;
624 static void get_minmaxunused(WSplit *node, int dir,
625 int *min, int *max, int *unused)
627 if(dir==SPLIT_VERTICAL){
628 *min=node->min_h;
629 *max=maxof(*min, node->max_h);
630 *unused=minof(node->unused_h, node->geom.h);
631 }else{
632 *min=node->min_w;
633 *max=maxof(*min, node->max_w);
634 *unused=minof(node->unused_w, node->geom.w);
639 void splitsplit_do_resize(WSplitSplit *node, const WRectangle *ng,
640 WPrimn hprimn, WPrimn vprimn, bool transpose)
642 assert(ng->w>=0 && ng->h>=0);
643 assert(node->tl!=NULL && node->br!=NULL);
644 assert(!transpose || (hprimn==PRIMN_ANY && vprimn==PRIMN_ANY));
647 WSplit *tl=node->tl, *br=node->br;
648 int tls=split_size((WSplit*)tl, node->dir);
649 int brs=split_size((WSplit*)br, node->dir);
650 int sz=tls+brs;
651 /* Status display can not be transposed. */
652 int dir=((transpose && !stdisp_immediate_child(node))
653 ? other_dir(node->dir)
654 : node->dir);
655 int nsize=(dir==SPLIT_VERTICAL ? ng->h : ng->w);
656 int primn=(dir==SPLIT_VERTICAL ? vprimn : hprimn);
657 int tlmin, tlmax, tlunused, tlused;
658 int brmin, brmax, brunused, brused;
659 WRectangle tlg=*ng, brg=*ng;
661 get_minmaxunused(tl, dir, &tlmin, &tlmax, &tlunused);
662 get_minmaxunused(br, dir, &brmin, &brmax, &brunused);
664 tlused=maxof(0, tls-maxof(0, tlunused));
665 brused=maxof(0, brs-maxof(0, brunused));
666 /* tlmin, brmin >= 1 => (tls>=tlmin, brs>=brmin => sz>0) */
668 if(sz>2){
669 if(primn==PRIMN_ANY && (tlunused>=0 || brunused>=0)){
670 if(nsize<=tlused+brused){
671 /* Need to shrink a tangible node */
672 adjust_sizes(&tls, &brs, nsize, sz,
673 tlmin, brmin, tlused, brused, primn);
674 }else{
675 /* Just expand or shrink unused space */
676 adjust_sizes(&tls, &brs, nsize, sz,
677 tlused, brused,
678 (tlunused<0 ? tlused : tlmax),
679 (brunused<0 ? brused : brmax), primn);
682 }else{
683 adjust_sizes(&tls, &brs, nsize, sz,
684 tlmin, brmin, tlmax, brmax, primn);
688 if(tls+brs!=nsize){
689 /* Bad fit; just size proportionally. */
690 if(sz<=2){
691 tls=nsize/2;
692 brs=nsize-tls;
693 }else{
694 tls=split_size(tl, node->dir)*nsize/sz;
695 brs=nsize-tls;
699 if(dir==SPLIT_VERTICAL){
700 tlg.h=tls;
701 brg.y+=tls;
702 brg.h=brs;
703 }else{
704 tlg.w=tls;
705 brg.x+=tls;
706 brg.w=brs;
709 split_do_resize(tl, &tlg, hprimn, vprimn, transpose);
710 split_do_resize(br, &brg, hprimn, vprimn, transpose);
712 node->dir=dir;
713 ((WSplit*)node)->geom=*ng;
714 split_update_bounds((WSplit*)node, FALSE);
719 void split_do_resize(WSplit *node, const WRectangle *ng,
720 WPrimn hprimn, WPrimn vprimn, bool transpose)
722 CALL_DYN(split_do_resize, node, (node, ng, hprimn, vprimn, transpose));
726 void split_resize(WSplit *node, const WRectangle *ng,
727 WPrimn hprimn, WPrimn vprimn)
729 split_update_bounds(node, TRUE);
730 splittree_begin_resize();
731 split_do_resize(node, ng, hprimn, vprimn, FALSE);
732 splittree_end_resize();
736 /*}}}*/
739 /*{{{ Save, restore and verify code for maximization */
742 bool splits_are_related(WSplit *p, WSplit *node)
744 if(p==node)
745 return TRUE;
747 return
748 node->parent!=NULL
749 ? splits_are_related(p, (WSplit*)node->parent)
750 : FALSE;
754 WSplit *maxparentdir_rel(WSplit *p, WSplit *node, int dir)
756 /* Descending from p, try to determine the first split of type dir between
757 * p and node, while ignoring a potential stdisp. */
758 if(OBJ_IS(p, WSplitSplit)){
759 WSplitSplit *sp=(WSplitSplit*)p;
760 assert(sp->tl!=NULL && sp->br!=NULL);
761 assert(splits_are_related(sp->tl, node) ||
762 splits_are_related(sp->br, node));
764 if(OBJ_IS(sp->tl, WSplitST))
765 return maxparentdir_rel(sp->br, node, dir);
766 if(OBJ_IS(sp->br, WSplitST))
767 return maxparentdir_rel(sp->tl, node, dir);
769 if(sp->dir!=dir){
770 return
771 splits_are_related(sp->tl, node)
772 ? maxparentdir_rel(sp->tl, node, dir)
773 : maxparentdir_rel(sp->br, node, dir);
777 return p;
781 WSplit *maxparent(WSplit *node)
783 WSplit *p=(WSplit*)node->parent;
784 return p==NULL ? node : maxparent(p);
788 WSplit *maxparentdir(WSplit *node, int dir)
790 return maxparentdir_rel(maxparent(node), node, dir);
793 int *wh(WRectangle *geom, int orientation)
795 return orientation==REGION_ORIENTATION_HORIZONTAL ? &geom->w : &geom->h;
798 int *xy(WRectangle *geom, int orientation)
800 return orientation==REGION_ORIENTATION_HORIZONTAL ? &geom->x : &geom->y;
803 bool is_lt(int orientation, int corner)
805 /* Read as "is_left" or "is_top", depending on the orientation. */
806 return
807 orientation==REGION_ORIENTATION_HORIZONTAL
808 ? corner==MPLEX_STDISP_TL || corner==MPLEX_STDISP_BL
809 : corner==MPLEX_STDISP_TL || corner==MPLEX_STDISP_TR;
812 int flip_orientation(int orientation)
814 return
815 orientation==REGION_ORIENTATION_HORIZONTAL
816 ? REGION_ORIENTATION_VERTICAL
817 : REGION_ORIENTATION_HORIZONTAL;
820 WRectangle stdisp_recommended_geom(WSplitST *st, WRectangle wsg)
822 /* wsg holds the geometry of the workspace that st is on. */
823 WRectangle stg=REGION_GEOM(st->regnode.reg);
824 int ori=st->orientation;
825 stg.w=stdisp_recommended_w(st);
826 stg.h=stdisp_recommended_h(st);
828 if(!is_lt(ori, st->corner))
829 *xy(&stg, ori)=*wh(&wsg, ori)-*wh(&stg, ori);
831 return stg;
834 bool geom_overlaps_stgeom_xy(WRectangle geom, WSplitST *st, WRectangle stg)
836 /* TRUE FALSE
838 * ------ ------
839 * |geom| |geom|
840 * ------ ------
841 * ----- -----
842 * |stg| |stg|
843 * ----- -----
845 int ori=st->orientation;
847 return
848 is_lt(ori, st->corner)
849 ? *xy(&geom, ori)<*wh(&stg, ori)
850 : *xy(&geom, ori)+*wh(&geom, ori)>*xy(&stg, ori);
853 bool geom_aligned_stdisp(WRectangle geom, WSplitST *st)
855 /* TRUE FALSE FALSE
857 * ------
858 * |geom|
859 * ------ ------
860 * |geom| ------
861 * ----- ------ ----- |geom| -----
862 * |stg| |stg| ------ |stg|
863 * ----- ----- -----
866 WRectangle stg=REGION_GEOM(st->regnode.reg);
867 int ori=flip_orientation(st->orientation);
869 return
870 is_lt(ori, st->corner)
871 ? *xy(&geom, ori)==*wh(&stg, ori)
872 : *xy(&geom, ori)+*wh(&geom, ori)==*xy(&stg, ori);
875 void grow_by_stdisp_wh(WRectangle *geom, WSplitST *st)
877 /* BEFORE AFTER
880 * ------ ------
881 * |geom| | |
882 * ----- ------ ----- |geom|
883 * |stg| |stg| | |
884 * ----- ----- ------
887 WRectangle stg=REGION_GEOM(st->regnode.reg);
888 int ori=flip_orientation(st->orientation);
890 if(is_lt(ori, st->corner))
891 *xy(geom, ori)=0;
892 *wh(geom, ori)+=*wh(&stg, ori);
895 bool frame_neighbors_stdisp(WFrame *frame, WSplitST *st)
897 return
898 geom_overlaps_stgeom_xy(REGION_GEOM(frame), st, REGION_GEOM(st)) &&
899 geom_aligned_stdisp(REGION_GEOM(frame), st);
902 bool geom_clashes_stdisp(WRectangle geom, WSplitST *st)
904 WRectangle stg=REGION_GEOM(st->regnode.reg);
905 int ori=flip_orientation(st->orientation);
906 return
907 is_lt(ori, st->corner)
908 ? *xy(&geom, ori)==0
909 : *xy(&geom, ori)+*wh(&geom, ori)==*xy(&stg, ori)+*wh(&stg, ori);
912 bool is_same_dir(int dir, int ori)
914 return
915 (dir==SPLIT_HORIZONTAL && ori==REGION_ORIENTATION_HORIZONTAL) ||
916 (dir==SPLIT_VERTICAL && ori==REGION_ORIENTATION_VERTICAL);
919 bool is_maxed(WFrame *frame, int dir)
921 return
922 dir==SPLIT_HORIZONTAL
923 ? frame->flags&FRAME_MAXED_HORIZ && frame->flags&FRAME_SAVED_HORIZ
924 : frame->flags&FRAME_MAXED_VERT && frame->flags&FRAME_SAVED_VERT;
927 bool update_geom_from_stdisp(WFrame *frame, WRectangle *ng, int dir)
929 WRegion *ws=REGION_MANAGER(frame);
930 WSplitST *st;
931 WRectangle stg;
932 WRectangle rstg;
933 WRectangle ngr;
934 int ori;
936 if(!OBJ_IS(ws, WTiling) || ((WTiling*)ws)->stdispnode==NULL)
937 return FALSE;
939 st=((WTiling*)ws)->stdispnode;
941 if(st->fullsize || !frame_neighbors_stdisp(frame, st))
942 return FALSE;
944 rstg=stdisp_recommended_geom(st, ws->geom);
946 if(is_same_dir(dir, st->orientation) &&
947 !geom_overlaps_stgeom_xy(*ng, st, rstg))
949 grow_by_stdisp_wh(ng, st);
950 if(is_maxed(frame, other_dir(dir)) &&
951 geom_aligned_stdisp(frame->saved_geom, st))
953 grow_by_stdisp_wh(&frame->saved_geom, st);
955 return TRUE;
958 if(!is_same_dir(dir, st->orientation) &&
959 geom_clashes_stdisp(frame->saved_geom, st))
961 stg=REGION_GEOM(st->regnode.reg);
962 ori=flip_orientation(st->orientation);
963 if(is_lt(ori, st->corner))
964 *xy(ng, ori)+=*wh(&stg, ori);
965 /* We've checked that this makes sense when verifying the saved layout. */
966 *wh(ng, ori)-=*wh(&stg, ori);
969 return FALSE;
972 bool splitregion_do_restore(WSplitRegion *node, int dir)
974 WFrame *frame;
975 WRectangle geom=((WSplit*)node)->geom;
976 WRectangle fakegeom;
977 bool ret;
978 bool other_max;
980 if(!OBJ_IS(node->reg, WFrame))
981 return FALSE;
983 frame=(WFrame*)node->reg;
984 if(dir==SPLIT_HORIZONTAL){
985 geom.x=frame->saved_geom.x;
986 geom.w=frame->saved_geom.w;
987 }else{
988 geom.y=frame->saved_geom.y;
989 geom.h=frame->saved_geom.h;
992 other_max=
993 dir==SPLIT_HORIZONTAL
994 ? frame->flags&FRAME_MAXED_VERT
995 : frame->flags&FRAME_MAXED_HORIZ;
997 fakegeom=geom;
998 ret=update_geom_from_stdisp(frame, &geom, dir);
1000 /* Tell the region the correct geometry to avoid redrawing it again when
1001 * the stdisp is resized by split_regularise_stdisp. Some clients (notably
1002 * ncurses based ones) don't seem to react well to being resized multiple
1003 * times within a short amount of time and don't refresh themselves
1004 * correctly. */
1005 region_fit(node->reg, &geom, REGION_FIT_EXACT);
1007 split_update_bounds(&(node->split), FALSE);
1009 /* Keep the old geometry for the WSplit. Otherwise the tiling would be
1010 * inconsistent, by for example having horizontal WSplitSplit's whose
1011 * children have different heights. The call to split_regularise_stdisp
1012 * below will take care of correcting the geometry of the WSplit and it
1013 * behaves badly when the tiling is inconsistent. */
1014 node->split.geom=ret ? fakegeom : geom;
1016 frame->flags|=other_max;
1017 return ret;
1020 bool splitst_do_restore(WSplit *node, int dir)
1022 return FALSE;
1025 bool splitsplit_do_restore(WSplitSplit *node, int dir)
1027 bool ret1, ret2, ret=FALSE;
1028 WSplit *snode=(WSplit*)node;
1030 WSplitST *st;
1031 WSplit *other;
1032 WRectangle stg;
1033 WRectangle og;
1035 assert(node->tl!=NULL && node->br!=NULL);
1037 if(stdisp_immediate_child(node)){
1038 if(OBJ_IS(node->tl, WSplitST)){
1039 st=(WSplitST*)node->tl;
1040 other=node->br;
1041 }else{
1042 st=(WSplitST*)node->br;
1043 other=node->tl;
1045 stg=((WSplit*)st)->geom;
1046 split_do_restore(other, dir);
1047 og=other->geom;
1048 if(node->dir==SPLIT_HORIZONTAL){
1049 stg.y=og.y;
1050 stg.h=og.h;
1051 }else{
1052 stg.x=og.x;
1053 stg.w=og.w;
1055 if(rectangle_compare(&stg, &((WSplit*)st)->geom)){
1056 splitst_do_resize(st, &stg, PRIMN_ANY, PRIMN_ANY, FALSE);
1057 ret=TRUE;
1059 }else{
1060 /* Avoid short-circuit evaluation. */
1061 ret1=split_do_restore(node->tl, dir);
1062 ret2=split_do_restore(node->br, dir);
1063 ret=ret1 || ret2;
1066 snode->geom.x=node->tl->geom.x;
1067 snode->geom.y=node->tl->geom.y;
1068 if(node->dir==SPLIT_HORIZONTAL){
1069 snode->geom.w=node->tl->geom.w+node->br->geom.w;
1070 snode->geom.h=node->tl->geom.h;
1072 if(node->dir==SPLIT_VERTICAL){
1073 snode->geom.w=node->tl->geom.w;
1074 snode->geom.h=node->tl->geom.h+node->br->geom.h;
1077 return ret;
1080 bool split_do_restore(WSplit *node, int dir)
1082 bool ret = FALSE;
1083 CALL_DYN_RET(ret, bool, split_do_restore, node, (node, dir));
1084 return ret;
1088 void splitregion_do_maxhelper(WSplitRegion *node, int dir, int action)
1090 WFrame *frame;
1091 if(!OBJ_IS(node->reg, WFrame))
1092 return;
1093 frame=(WFrame*)node->reg;
1095 if(action==SAVE){
1096 frame->flags|=FRAME_KEEP_FLAGS;
1097 if(dir==HORIZONTAL){
1098 frame->flags|=(FRAME_MAXED_HORIZ|FRAME_SAVED_HORIZ);
1099 frame->saved_geom.x=REGION_GEOM(frame).x;
1100 frame->saved_geom.w=REGION_GEOM(frame).w;
1101 }else{
1102 frame->flags|=(FRAME_MAXED_VERT|FRAME_SAVED_VERT);
1103 frame->saved_geom.y=REGION_GEOM(frame).y;
1104 frame->saved_geom.h=REGION_GEOM(frame).h;
1107 if(action==SET_KEEP)
1108 frame->flags|=FRAME_KEEP_FLAGS;
1109 if(action==RM_KEEP)
1110 frame->flags&=~FRAME_KEEP_FLAGS;
1113 void splitst_do_maxhelper(WSplit *node, int dir, int action)
1115 return;
1118 void splitsplit_do_maxhelper(WSplitSplit *node, int dir, int action)
1120 assert(node->tl!=NULL && node->br!=NULL);
1121 split_do_maxhelper(node->tl, dir, action);
1122 split_do_maxhelper(node->br, dir, action);
1125 void split_do_maxhelper(WSplit *node, int dir, int action)
1127 CALL_DYN(split_do_maxhelper, node, (node, dir, action));
1131 bool savedgeom_clashes_stdisp(WFrame *frame, int dir)
1133 WRegion *ws=REGION_MANAGER(frame);
1134 WSplitST *st;
1135 int ori;
1137 if(!OBJ_IS(ws, WTiling) || ((WTiling*)ws)->stdispnode==NULL)
1138 return TRUE;
1140 st=((WTiling*)ws)->stdispnode;
1141 ori=flip_orientation(st->orientation);
1143 return
1144 !is_same_dir(dir, st->orientation) &&
1145 frame_neighbors_stdisp(frame, st) &&
1146 geom_clashes_stdisp(frame->saved_geom, st)
1147 ? *wh(&frame->saved_geom, ori)<*wh(&REGION_GEOM(st), ori)
1148 : FALSE;
1151 bool splitregion_do_verify(WSplitRegion *node, int dir)
1153 WFrame *frame;
1154 bool ret=FALSE;
1156 if(!OBJ_IS(node->reg, WFrame))
1157 return FALSE;
1159 frame=(WFrame*)node->reg;
1161 ret=is_maxed(frame, dir);
1163 if(dir==HORIZONTAL)
1164 frame->flags&=~(FRAME_MAXED_HORIZ|FRAME_SAVED_HORIZ);
1165 else
1166 frame->flags&=~(FRAME_MAXED_VERT|FRAME_SAVED_VERT);
1168 if(savedgeom_clashes_stdisp(frame, dir))
1169 return FALSE;
1171 return ret;
1174 bool splitst_do_verify(WSplit *node, int dir)
1176 return TRUE;
1179 bool splitsplit_do_verify(WSplitSplit *node, int dir)
1181 bool ret1, ret2;
1182 assert(node->tl!=NULL && node->br!=NULL);
1184 /* Avoid short-circuit evaluation. */
1185 ret1=split_do_verify(node->tl, dir);
1186 ret2=split_do_verify(node->br, dir);
1187 return ret1 && ret2;
1190 bool split_do_verify(WSplit *node, int dir)
1192 bool ret = FALSE;
1193 CALL_DYN_RET(ret, bool, split_do_verify, node, (node, dir));
1194 return ret;
1198 bool split_maximize(WSplit *node, int dir, int action)
1200 WSplit *p=maxparentdir(node, dir);
1201 if(action==RESTORE)
1202 return split_do_restore(p, dir);
1203 if(action==VERIFY)
1204 return split_do_verify(p, dir);
1206 split_do_maxhelper(p, dir, action);
1207 return TRUE;
1211 /*}}}*/
1214 /*{{{ Low-level resize code; request towards root */
1217 static void flexibility(WSplit *node, int dir, int *shrink, int *stretch)
1219 if(dir==SPLIT_VERTICAL){
1220 *shrink=maxof(0, node->geom.h-node->min_h);
1221 if(OBJ_IS(node, WSplitST))
1222 *stretch=maxof(0, node->max_h-node->geom.h);
1223 else
1224 *stretch=INT_MAX;
1225 }else{
1226 *shrink=maxof(0, node->geom.w-node->min_w);
1227 if(OBJ_IS(node, WSplitST))
1228 *stretch=maxof(0, node->max_w-node->geom.w);
1229 else
1230 *stretch=INT_MAX;
1235 static void calc_amount(int *amount, int rs, WSplit *other, int dir)
1237 int shrink, stretch;
1239 flexibility(other, dir, &shrink, &stretch);
1241 if(rs>0)
1242 *amount=minof(rs, shrink);
1243 else if(rs<0)
1244 *amount=-minof(-rs, stretch);
1245 else
1246 *amount=0;
1251 static void splitsplit_do_rqsize(WSplitSplit *p, WSplit *node,
1252 RootwardAmount *ha, RootwardAmount *va,
1253 WRectangle *rg, bool tryonly)
1255 WPrimn hprimn=PRIMN_ANY, vprimn=PRIMN_ANY;
1256 WRectangle og, pg, ng;
1257 RootwardAmount *ca;
1258 WSplit *other;
1259 WPrimn thisnode;
1260 int amount;
1262 assert(!ha->any || ha->tl==0);
1263 assert(!va->any || va->tl==0);
1264 assert(p->tl==node || p->br==node);
1266 if(p->tl==node){
1267 other=p->br;
1268 thisnode=PRIMN_TL;
1269 }else{
1270 other=p->tl;
1271 thisnode=PRIMN_BR;
1274 ca=(p->dir==SPLIT_VERTICAL ? va : ha);
1276 if(thisnode==PRIMN_TL || ca->any){
1277 calc_amount(&amount, ca->br, other, p->dir);
1278 ca->br-=amount;
1279 }else/*if(thisnode==PRIMN_BR)*/{
1280 calc_amount(&amount, ca->tl, other, p->dir);
1281 ca->tl-=amount;
1284 if(((WSplit*)p)->parent==NULL /*||
1285 (ha->tl==0 && ha->br==0 && va->tl==0 && va->br==0)*/){
1286 if(((WSplit*)p)->ws_if_root!=NULL)
1287 pg=REGION_GEOM((WTiling*)(((WSplit*)p)->ws_if_root));
1288 else
1289 pg=((WSplit*)p)->geom;
1290 }else{
1291 splitinner_do_rqsize(((WSplit*)p)->parent, (WSplit*)p, ha, va,
1292 &pg, tryonly);
1295 assert(pg.w>=0 && pg.h>=0);
1297 og=pg;
1298 ng=pg;
1300 if(p->dir==SPLIT_VERTICAL){
1301 ng.h=maxof(0, node->geom.h+amount);
1302 og.h=maxof(0, other->geom.h-amount);
1303 adjust_sizes(&(ng.h), &(og.h), pg.h, ng.h+og.h,
1304 node->min_h, other->min_h, node->max_h, other->max_h,
1305 PRIMN_TL /* node is passed as tl param */);
1306 if(thisnode==PRIMN_TL)
1307 og.y=pg.y+pg.h-og.h;
1308 else
1309 ng.y=pg.y+pg.h-ng.h;
1310 vprimn=thisnode;
1311 }else{
1312 ng.w=maxof(0, node->geom.w+amount);
1313 og.w=maxof(0, other->geom.w-amount);
1314 adjust_sizes(&(ng.w), &(og.w), pg.w, ng.w+og.w,
1315 node->min_w, other->min_w, node->max_w, other->max_w,
1316 PRIMN_TL /* node is passed as tl param */);
1317 if(thisnode==PRIMN_TL)
1318 og.x=pg.x+pg.w-og.w;
1319 else
1320 ng.x=pg.x+pg.w-ng.w;
1321 hprimn=thisnode;
1324 if(!tryonly){
1325 /* Entä jos 'other' on stdisp? */
1326 split_do_resize(other, &og, hprimn, vprimn, FALSE);
1328 ((WSplit*)p)->geom=pg;
1331 *rg=ng;
1335 void splitinner_do_rqsize(WSplitInner *p, WSplit *node,
1336 RootwardAmount *ha, RootwardAmount *va,
1337 WRectangle *rg, bool tryonly)
1339 CALL_DYN(splitinner_do_rqsize, p, (p, node, ha, va, rg, tryonly));
1343 static void initra(RootwardAmount *ra, int p, int s, int op, int os,
1344 bool any)
1346 ra->any=any;
1347 ra->tl=op-p;
1348 ra->br=(p+s)-(op+os);
1349 if(any){
1350 ra->br+=ra->tl;
1351 ra->tl=0;
1356 void split_do_rqgeom_(WSplit *node, const WRectangle *ng,
1357 bool hany, bool vany, WRectangle *rg,
1358 bool tryonly)
1360 RootwardAmount ha, va;
1362 if(node->parent==NULL){
1363 if(node->ws_if_root!=NULL)
1364 *rg=REGION_GEOM((WTiling*)(node->ws_if_root));
1365 else
1366 *rg=*ng;
1367 }else{
1368 initra(&ha, ng->x, ng->w, node->geom.x, node->geom.w, hany);
1369 initra(&va, ng->y, ng->h, node->geom.y, node->geom.h, vany);
1371 splitinner_do_rqsize(node->parent, node, &ha, &va, rg, tryonly);
1376 /*}}}*/
1379 /*{{{ Resize interface */
1382 static void bnd(int *pos, int *sz, int opos, int osz, int minsz, int maxsz)
1384 int ud=abs(*pos-opos);
1385 int dd=abs((*pos+*sz)-(opos+osz));
1386 int szrq=*sz;
1388 if(ud+dd!=0){
1389 bound(sz, minsz, maxsz);
1390 *pos+=(szrq-*sz)*ud/(ud+dd);
1395 WSplit *split_find_root(WSplit *split)
1397 if(split->parent==NULL)
1398 return split;
1399 return split_find_root((WSplit*)split->parent);
1403 void splittree_rqgeom(WSplit *sub, int flags, const WRectangle *geom_,
1404 WRectangle *geomret)
1406 bool hany=flags&REGION_RQGEOM_WEAK_X;
1407 bool vany=flags&REGION_RQGEOM_WEAK_Y;
1408 bool tryonly=flags&REGION_RQGEOM_TRYONLY;
1409 WRectangle geom=*geom_;
1410 WRectangle retg;
1411 WSplit *root=split_find_root(sub);
1413 if(geomret==NULL)
1414 geomret=&retg;
1416 split_update_bounds(root, TRUE);
1418 if(OBJ_IS(sub, WSplitST)){
1419 WSplitST *sub_as_stdisp=(WSplitST*)sub;
1421 if(flags&REGION_RQGEOM_TRYONLY){
1422 warn(TR("REGION_RQGEOM_TRYONLY unsupported for status display."));
1423 *geomret=sub->geom;
1424 return;
1426 split_regularise_stdisp(sub_as_stdisp);
1427 geom=sub->geom;
1428 if(sub_as_stdisp->orientation==REGION_ORIENTATION_HORIZONTAL){
1429 if(geom_->h==geom.h)
1430 return;
1431 geom.h=geom_->h;
1432 }else{
1433 if(geom_->w==geom.w)
1434 return;
1435 geom.w=geom_->w;
1437 split_update_bounds(root, TRUE);
1440 /* Handle internal size bounds */
1441 bnd(&(geom.x), &(geom.w), sub->geom.x, sub->geom.w,
1442 sub->min_w, sub->max_w);
1443 bnd(&(geom.y), &(geom.h), sub->geom.y, sub->geom.h,
1444 sub->min_h, sub->max_h);
1446 /* Check if we should resize to both tl and br */
1448 if(hany){
1449 geom.w+=sub->geom.x-geom.x;
1450 geom.x=sub->geom.x;
1453 if(vany){
1454 geom.h+=sub->geom.y-geom.y;
1455 geom.y=sub->geom.y;
1458 splittree_begin_resize();
1460 split_do_rqgeom_(sub, &geom, hany, vany, geomret, tryonly);
1462 if(!tryonly){
1463 split_do_resize(sub, geomret, hany, vany, FALSE);
1464 splittree_end_resize();
1465 *geomret=sub->geom;
1466 }else{
1467 saw_stdisp=NULL;
1472 /*EXTL_DOC
1473 * Attempt to resize and/or move the split tree starting at \var{node}.
1474 * Behaviour and the \var{g} parameter are as for \fnref{WRegion.rqgeom}
1475 * operating on \var{node} (if it were a \type{WRegion}).
1477 EXTL_EXPORT_MEMBER
1478 ExtlTab split_rqgeom(WSplit *node, ExtlTab g)
1480 WRectangle geom, ogeom;
1481 int flags=REGION_RQGEOM_WEAK_ALL;
1483 geom=node->geom;
1484 ogeom=geom;
1486 if(extl_table_gets_i(g, "x", &(geom.x)))
1487 flags&=~REGION_RQGEOM_WEAK_X;
1488 if(extl_table_gets_i(g, "y", &(geom.y)))
1489 flags&=~REGION_RQGEOM_WEAK_Y;
1490 if(extl_table_gets_i(g, "w", &(geom.w)))
1491 flags&=~REGION_RQGEOM_WEAK_W;
1492 if(extl_table_gets_i(g, "h", &(geom.h)))
1493 flags&=~REGION_RQGEOM_WEAK_H;
1495 geom.w=maxof(1, geom.w);
1496 geom.h=maxof(1, geom.h);
1498 splittree_rqgeom(node, flags, &geom, &ogeom);
1500 return extl_table_from_rectangle(&ogeom);
1504 /*}}}*/
1507 /*{{{ Split */
1510 void splittree_changeroot(WSplit *root, WSplit *node)
1512 WTiling *ws=(WTiling*)(root->ws_if_root);
1514 assert(ws!=NULL);
1515 assert(ws->split_tree==root);
1516 root->ws_if_root=NULL;
1517 ws->split_tree=node;
1518 if(node!=NULL){
1519 node->ws_if_root=ws;
1520 node->parent=NULL;
1525 static void splitsplit_replace(WSplitSplit *split, WSplit *child,
1526 WSplit *what)
1528 assert(split->tl==child || split->br==child);
1530 if(split->tl==child)
1531 split->tl=what;
1532 else
1533 split->br=what;
1535 child->parent=NULL;
1537 what->parent=(WSplitInner*)split;
1538 what->ws_if_root=NULL; /* May not be needed. */
1542 void splitinner_replace(WSplitInner *split, WSplit *child, WSplit *what)
1544 CALL_DYN(splitinner_replace, split, (split, child, what));
1548 WSplitRegion *splittree_split(WSplit *node, int dir, WPrimn primn,
1549 int minsize, WRegionSimpleCreateFn *fn,
1550 WWindow *parent)
1552 int objmin;
1553 int s, sn, so;
1554 WSplitSplit *nsplit;
1555 WSplitRegion *nnode;
1556 WSplitInner *psplit;
1557 WRegion *nreg;
1558 WFitParams fp;
1559 WRectangle ng, rg;
1561 assert(node!=NULL && parent!=NULL);
1563 splittree_begin_resize();
1565 node=dodge_stdisp(node, FALSE);
1567 if(node==NULL)
1568 return NULL;
1570 if(OBJ_IS(node, WSplitST)){
1571 warn(TR("Splitting the status display is not allowed."));
1572 return NULL;
1575 if(primn!=PRIMN_TL && primn!=PRIMN_BR)
1576 primn=PRIMN_BR;
1577 if(dir!=SPLIT_HORIZONTAL && dir!=SPLIT_VERTICAL)
1578 dir=SPLIT_VERTICAL;
1580 split_update_bounds(split_find_root(node), TRUE);
1581 objmin=(dir==SPLIT_VERTICAL ? node->min_h : node->min_w);
1583 s=split_size(node, dir);
1584 sn=maxof(minsize, s/2);
1585 so=maxof(objmin, s-sn);
1587 if(sn+so!=s){
1588 int rs;
1589 ng=node->geom;
1590 if(dir==SPLIT_VERTICAL)
1591 ng.h=sn+so;
1592 else
1593 ng.w=sn+so;
1594 split_do_rqgeom_(node, &ng, TRUE, TRUE, &rg, TRUE);
1595 rs=(dir==SPLIT_VERTICAL ? rg.h : rg.w);
1596 if(rs<minsize+objmin){
1597 warn(TR("Unable to split: not enough free space."));
1598 return NULL;
1600 split_do_rqgeom_(node, &ng, TRUE, TRUE, &rg, FALSE);
1601 rs=(dir==SPLIT_VERTICAL ? rg.h : rg.w);
1602 if(minsize>rs/2){
1603 sn=minsize;
1604 so=rs-sn;
1605 }else{
1606 so=maxof(rs/2, objmin);
1607 sn=rs-so;
1609 }else{
1610 rg=node->geom;
1611 splittree_scan_stdisp_rootward(node);
1614 /* Create split and new window
1616 fp.mode=REGION_FIT_EXACT;
1617 fp.g=rg;
1619 nsplit=create_splitsplit(&(fp.g), dir);
1621 if(nsplit==NULL)
1622 return NULL;
1624 if(dir==SPLIT_VERTICAL){
1625 if(primn==PRIMN_BR)
1626 fp.g.y+=so;
1627 fp.g.h=sn;
1628 }else{
1629 if(primn==PRIMN_BR)
1630 fp.g.x+=so;
1631 fp.g.w=sn;
1634 nreg=fn(parent, &fp);
1636 if(nreg==NULL){
1637 destroy_obj((Obj*)nsplit);
1638 return NULL;
1641 nnode=create_splitregion(&(fp.g), nreg);
1642 if(nnode==NULL){
1643 destroy_obj((Obj*)nreg);
1644 destroy_obj((Obj*)nsplit);
1645 return NULL;
1648 /* Now that everything's ok, resize and move original node.
1650 ng=rg;
1651 if(dir==SPLIT_VERTICAL){
1652 ng.h=so;
1653 if(primn==PRIMN_TL)
1654 ng.y+=sn;
1655 }else{
1656 ng.w=so;
1657 if(primn==PRIMN_TL)
1658 ng.x+=sn;
1661 split_do_resize(node, &ng,
1662 (dir==SPLIT_HORIZONTAL ? primn : PRIMN_ANY),
1663 (dir==SPLIT_VERTICAL ? primn : PRIMN_ANY),
1664 FALSE);
1666 /* Set up split structure
1668 psplit=node->parent;
1670 if(psplit!=NULL)
1671 splitinner_replace(psplit, node, (WSplit*)nsplit);
1672 else
1673 splittree_changeroot(node, (WSplit*)nsplit);
1675 node->parent=(WSplitInner*)nsplit;
1676 ((WSplit*)nnode)->parent=(WSplitInner*)nsplit;
1678 if(primn==PRIMN_BR){
1679 nsplit->tl=node;
1680 nsplit->br=(WSplit*)nnode;
1681 nsplit->current=SPLIT_CURRENT_TL;
1682 }else{
1683 nsplit->tl=(WSplit*)nnode;
1684 nsplit->br=node;
1685 nsplit->current=SPLIT_CURRENT_BR;
1688 splittree_end_resize();
1690 return nnode;
1694 /*}}}*/
1697 /*{{{ Remove */
1700 static void splitsplit_remove(WSplitSplit *node, WSplit *child,
1701 bool reclaim_space)
1703 static int nstdisp=0;
1704 WSplitInner *parent;
1705 WSplit *other;
1706 int hprimn=PRIMN_ANY, vprimn=PRIMN_ANY;
1708 assert(node->tl==child || node->br==child);
1710 if(node->tl==child){
1711 other=node->br;
1712 if(node->dir==SPLIT_VERTICAL)
1713 vprimn=PRIMN_TL;
1714 else
1715 hprimn=PRIMN_TL;
1716 }else{
1717 other=node->tl;
1718 if(node->dir==SPLIT_VERTICAL)
1719 vprimn=PRIMN_BR;
1720 else
1721 hprimn=PRIMN_BR;
1724 assert(other!=NULL);
1726 if(nstdisp==0 && reclaim_space && OBJ_IS(other, WSplitST)){
1727 /* Try to move stdisp out of the way. */
1728 split_try_unsink_stdisp(node, FALSE, TRUE);
1729 assert(child->parent!=NULL);
1730 nstdisp++;
1731 splitinner_remove(child->parent, child, reclaim_space);
1732 nstdisp--;
1733 return;
1736 parent=((WSplit*)node)->parent;
1738 if(parent!=NULL)
1739 splitinner_replace(parent, (WSplit*)node, other);
1740 else
1741 splittree_changeroot((WSplit*)node, other);
1743 if(reclaim_space)
1744 split_resize(other, &(((WSplit*)node)->geom), hprimn, vprimn);
1746 child->parent=NULL;
1748 node->tl=NULL;
1749 node->br=NULL;
1750 ((WSplit*)node)->parent=NULL;
1751 destroy_obj((Obj*)node);
1755 void splitinner_remove(WSplitInner *node, WSplit *child, bool reclaim_space)
1757 CALL_DYN(splitinner_remove, node, (node, child, reclaim_space));
1761 void splittree_remove(WSplit *node, bool reclaim_space)
1763 if(node->parent!=NULL)
1764 splitinner_remove(node->parent, node, reclaim_space);
1765 else if(node->ws_if_root!=NULL)
1766 splittree_changeroot(node, NULL);
1768 destroy_obj((Obj*)node);
1772 /*}}}*/
1775 /*{{{ Tree traversal */
1778 static bool defaultfilter(WSplit *node)
1780 return (OBJ_IS(node, WSplitRegion) &&
1781 ((WSplitRegion*)node)->reg!=NULL);
1785 static WSplit *split_current_todir_default(WSplit *node,
1786 WPrimn hprimn, WPrimn vprimn,
1787 WSplitFilter *filter)
1789 if(filter==NULL)
1790 filter=defaultfilter;
1792 return (filter(node) ? node : NULL);
1796 static WSplit *splitsplit_current_todir(WSplitSplit *node,
1797 WPrimn hprimn, WPrimn vprimn,
1798 WSplitFilter *filter)
1800 WPrimn primn=(node->dir==SPLIT_HORIZONTAL ? hprimn : vprimn);
1801 WSplit *first, *second, *ret;
1803 if(primn==PRIMN_TL ||
1804 (primn==PRIMN_ANY && node->current==SPLIT_CURRENT_TL)){
1805 first=node->tl;
1806 second=node->br;
1807 }else if(primn==PRIMN_BR ||
1808 (primn==PRIMN_ANY && node->current==SPLIT_CURRENT_BR)){
1809 first=node->br;
1810 second=node->tl;
1811 }else{
1812 return NULL;
1815 ret=split_current_todir(first, hprimn, vprimn, filter);
1816 if(ret==NULL)
1817 ret=split_current_todir(second, hprimn, vprimn, filter);
1818 if(ret==NULL && filter!=NULL){
1819 if(filter((WSplit*)node))
1820 ret=(WSplit*)node;
1823 return ret;
1827 WSplit *split_current_todir(WSplit *node, WPrimn hprimn, WPrimn vprimn,
1828 WSplitFilter *filter)
1830 WSplit *ret=NULL;
1831 CALL_DYN_RET(ret, WSplit*, split_current_todir, node,
1832 (node, hprimn, vprimn, filter));
1833 return ret;
1837 /* Note: both hprimn and vprimn are inverted when descending. Therefore
1838 * one should be either PRIMN_NONE or PRIMN_ANY for sensible geometric
1839 * navigation. (Both are PRIMN_TL or PRIMN_BR for pseudo-linear
1840 * next/previous navigation.)
1842 WSplit *splitsplit_nextto(WSplitSplit *node, WSplit *child,
1843 WPrimn hprimn, WPrimn vprimn,
1844 WSplitFilter *filter)
1846 WPrimn primn=(node->dir==SPLIT_HORIZONTAL ? hprimn : vprimn);
1847 WSplit *split=NULL, *nnode=NULL;
1849 if(node->tl==child && (primn==PRIMN_BR || primn==PRIMN_ANY))
1850 split=node->br;
1851 else if(node->br==child && (primn==PRIMN_TL || primn==PRIMN_ANY))
1852 split=node->tl;
1854 if(split!=NULL){
1855 nnode=split_current_todir(split,
1856 primn_none2any(primn_invert(hprimn)),
1857 primn_none2any(primn_invert(vprimn)),
1858 filter);
1861 if(nnode==NULL)
1862 nnode=split_nextto((WSplit*)node, hprimn, vprimn, filter);
1864 return nnode;
1868 WSplit *splitinner_nextto(WSplitInner *node, WSplit *child,
1869 WPrimn hprimn, WPrimn vprimn,
1870 WSplitFilter *filter)
1872 WSplit *ret=NULL;
1873 CALL_DYN_RET(ret, WSplit*, splitinner_nextto, node,
1874 (node, child, hprimn, vprimn, filter));
1875 return ret;
1879 WSplit *split_nextto(WSplit *node, WPrimn hprimn, WPrimn vprimn,
1880 WSplitFilter *filter)
1882 while(node->parent!=NULL){
1883 WSplit *ret=splitinner_nextto(node->parent, node,
1884 hprimn, vprimn, filter);
1885 if(ret!=NULL)
1886 return ret;
1887 node=(WSplit*)node->parent;
1889 return NULL;
1893 void splitinner_mark_current_default(WSplitInner *split, WSplit *child)
1895 if(((WSplit*)split)->parent!=NULL)
1896 splitinner_mark_current(((WSplit*)split)->parent, (WSplit*)split);
1900 void splitsplit_mark_current(WSplitSplit *split, WSplit *child)
1902 assert(child==split->tl || child==split->br);
1904 split->current=(split->tl==child ? SPLIT_CURRENT_TL : SPLIT_CURRENT_BR);
1906 splitinner_mark_current_default(&(split->isplit), child);
1910 void splitinner_mark_current(WSplitInner *split, WSplit *child)
1912 CALL_DYN(splitinner_mark_current, split, (split, child));
1916 static void splitsplit_forall(WSplitSplit *node, WSplitFn *fn)
1918 fn(node->tl);
1919 fn(node->br);
1923 void splitinner_forall(WSplitInner *node, WSplitFn *fn)
1925 CALL_DYN(splitinner_forall, node, (node, fn));
1929 static WSplit *splitsplit_current(WSplitSplit *split)
1931 return (split->current==SPLIT_CURRENT_TL ? split->tl : split->br);
1935 /*EXTL_DOC
1936 * Returns the most previously active child node of \var{split}.
1938 EXTL_SAFE
1939 EXTL_EXPORT_MEMBER
1940 WSplit *splitinner_current(WSplitInner *node)
1942 WSplit *ret=NULL;
1943 CALL_DYN_RET(ret, WSplit*, splitinner_current, node, (node));
1944 return ret;
1948 /*}}}*/
1951 /*{{{ X window handling */
1954 static void splitregion_stacking(WSplitRegion *split,
1955 Window *bottomret, Window *topret)
1957 *bottomret=None;
1958 *topret=None;
1959 if(split->reg!=NULL)
1960 region_stacking(split->reg, bottomret, topret);
1964 void splitsplit_stacking(WSplitSplit *split,
1965 Window *bottomret, Window *topret)
1967 Window tlb=None, tlt=None;
1968 Window brb=None, brt=None;
1970 split_stacking(split->tl, &tlb, &tlt);
1971 split_stacking(split->br, &brb, &brt);
1973 /* To make sure that this condition holds is left to the workspace
1974 * code to do after a split tree has been loaded or modified.
1976 if(split->current==SPLIT_CURRENT_TL){
1977 *topret=(tlt!=None ? tlt : brt);
1978 *bottomret=(brb!=None ? brb : tlb);
1979 }else{
1980 *topret=(brt!=None ? brt : tlt);
1981 *bottomret=(tlb!=None ? tlb : brb);
1985 void split_stacking(WSplit *split, Window *bottomret, Window *topret)
1987 *bottomret=None;
1988 *topret=None;
1990 CALL_DYN(split_stacking, split, (split, bottomret, topret));
1995 static void splitregion_restack(WSplitRegion *split, Window other, int mode)
1997 if(split->reg!=NULL)
1998 region_restack(split->reg, other, mode);
2001 void splitsplit_restack(WSplitSplit *split, Window other, int mode)
2003 Window bottom=None, top=None;
2004 WSplit *first, *second;
2006 if(split->current==SPLIT_CURRENT_TL){
2007 first=split->br;
2008 second=split->tl;
2009 }else{
2010 first=split->tl;
2011 second=split->br;
2014 split_restack(first, other, mode);
2015 split_stacking(first, &bottom, &top);
2016 if(top!=None){
2017 other=top;
2018 mode=Above;
2020 split_restack(second, other, mode);
2023 void split_restack(WSplit *split, Window other, int mode)
2025 CALL_DYN(split_restack, split, (split, other, mode));
2029 static void splitregion_map(WSplitRegion *split)
2031 if(split->reg!=NULL)
2032 region_map(split->reg);
2035 static void splitinner_map(WSplitInner *split)
2037 splitinner_forall(split, split_map);
2040 void split_map(WSplit *split)
2042 CALL_DYN(split_map, split, (split));
2046 static void splitregion_unmap(WSplitRegion *split)
2048 if(split->reg!=NULL)
2049 region_unmap(split->reg);
2052 static void splitinner_unmap(WSplitInner *split)
2054 splitinner_forall(split, split_unmap);
2057 void split_unmap(WSplit *split)
2059 CALL_DYN(split_unmap, split, (split));
2063 static void splitregion_reparent(WSplitRegion *split, WWindow *wwin)
2065 if(split->reg!=NULL){
2066 WRectangle g=split->split.geom;
2067 region_reparent(split->reg, wwin, &g, REGION_FIT_EXACT);
2072 static void splitsplit_reparent(WSplitSplit *split, WWindow *wwin)
2074 if(split->current==SPLIT_CURRENT_TL){
2075 split_reparent(split->br, wwin);
2076 split_reparent(split->tl, wwin);
2077 }else{
2078 split_reparent(split->tl, wwin);
2079 split_reparent(split->br, wwin);
2084 void split_reparent(WSplit *split, WWindow *wwin)
2086 CALL_DYN(split_reparent, split, (split, wwin));
2090 /*}}}*/
2093 /*{{{ Transpose, flip, rotate */
2096 void splitsplit_flip_default(WSplitSplit *split)
2098 WRectangle tlng, brng;
2099 WRectangle *sg=&((WSplit*)split)->geom;
2100 WSplit *tmp;
2102 assert(split->tl!=NULL && split->br!=NULL);
2104 split_update_bounds((WSplit*)split, TRUE);
2106 tlng=split->tl->geom;
2107 brng=split->br->geom;
2109 if(split->dir==SPLIT_HORIZONTAL){
2110 brng.x=sg->x;
2111 tlng.x=sg->x+sg->w-tlng.w;
2112 }else{
2113 brng.y=sg->y;
2114 tlng.y=sg->y+sg->h-tlng.h;
2117 tmp=split->tl;
2118 split->tl=split->br;
2119 split->br=tmp;
2120 split->current=(split->current==SPLIT_CURRENT_TL
2121 ? SPLIT_CURRENT_BR
2122 : SPLIT_CURRENT_TL);
2124 split_do_resize(split->tl, &brng, PRIMN_ANY, PRIMN_ANY, FALSE);
2125 split_do_resize(split->br, &tlng, PRIMN_ANY, PRIMN_ANY, FALSE);
2129 static void splitsplit_flip_(WSplitSplit *split)
2131 CALL_DYN(splitsplit_flip, split, (split));
2135 /*EXTL_DOC
2136 * Flip contents of \var{split}.
2138 EXTL_EXPORT_MEMBER
2139 void splitsplit_flip(WSplitSplit *split)
2141 splittree_begin_resize();
2143 split=OBJ_CAST(dodge_stdisp((WSplit*)split, FALSE), WSplitSplit);
2145 if(split==NULL)
2146 return;
2148 splitsplit_flip_(split);
2150 splittree_end_resize();
2153 typedef enum{
2154 FLIP_VERTICAL,
2155 FLIP_HORIZONTAL,
2156 FLIP_NONE,
2157 FLIP_ANY
2158 } FlipDir;
2161 static FlipDir flipdir=FLIP_VERTICAL;
2164 static void do_flip(WSplit *split)
2166 WSplitSplit *ss=OBJ_CAST(split, WSplitSplit);
2168 if(ss!=NULL){
2169 if((flipdir==FLIP_ANY
2170 || (ss->dir==SPLIT_VERTICAL && flipdir==FLIP_VERTICAL)
2171 || (ss->dir==SPLIT_HORIZONTAL && flipdir==FLIP_HORIZONTAL))
2172 && !OBJ_IS(ss->tl, WSplitST)
2173 && !OBJ_IS(ss->br, WSplitST)){
2174 splitsplit_flip_(ss);
2178 if(OBJ_IS(ss, WSplitInner))
2179 splitinner_forall((WSplitInner*)ss, do_flip);
2183 static void splittree_flip_dir(WSplit *splittree, FlipDir dir)
2185 /* todo stdisp outta way */
2186 if(OBJ_IS(splittree, WSplitInner)){
2187 flipdir=dir;
2188 splitinner_forall((WSplitInner*)splittree, do_flip);
2193 static bool split_fliptrans_to(WSplit *node, const WRectangle *geom,
2194 bool trans, FlipDir flip)
2196 WRectangle rg;
2197 WSplit *node2;
2199 splittree_begin_resize();
2201 /* split_do_resize can do things right if 'node' has stdisp as child,
2202 * but otherwise transpose will put the stdisp in a bad split
2203 * configuration if it is contained within 'node', so we must
2204 * first move it and its fixed parent split below node. For correct
2205 * geometry calculation we move it immediately below node, and
2206 * resize stdisp's fixed parent node instead.
2208 node2=dodge_stdisp(node, TRUE);
2210 if(node==NULL || node2!=node)
2211 return FALSE;
2213 split_update_bounds(node, TRUE);
2215 split_do_rqgeom_(node, geom, PRIMN_ANY, PRIMN_ANY, &rg, FALSE);
2217 split_do_resize(node, &rg, PRIMN_ANY, PRIMN_ANY, trans);
2219 if(flip!=FLIP_NONE)
2220 splittree_flip_dir(node, flip);
2222 splittree_end_resize();
2224 return TRUE;
2228 bool split_transpose_to(WSplit *node, const WRectangle *geom)
2230 return split_fliptrans_to(node, geom, TRUE, FLIP_ANY);
2234 /*EXTL_DOC
2235 * Transpose contents of \var{node}.
2237 EXTL_EXPORT_MEMBER
2238 void split_transpose(WSplit *node)
2240 WRectangle g=node->geom;
2242 split_transpose_to(node, &g);
2246 bool split_rotate_to(WSplit *node, const WRectangle *geom, int rotation)
2248 FlipDir flip=FLIP_NONE;
2249 bool trans=FALSE;
2251 if(rotation==SCREEN_ROTATION_90){
2252 flip=FLIP_HORIZONTAL;
2253 trans=TRUE;
2254 }else if(rotation==SCREEN_ROTATION_180){
2255 flip=FLIP_ANY;
2256 }else if(rotation==SCREEN_ROTATION_270){
2257 flip=FLIP_VERTICAL;
2258 trans=TRUE;
2261 return split_fliptrans_to(node, geom, trans, flip);
2264 /*}}}*/
2267 /*{{{ Exports */
2270 /*EXTL_DOC
2271 * Return parent split for \var{split}.
2273 EXTL_SAFE
2274 EXTL_EXPORT_MEMBER
2275 WSplitInner *split_parent(WSplit *split)
2277 return split->parent;
2281 /*EXTL_DOC
2282 * Returns the area of workspace used by the regions under \var{split}.
2284 EXTL_SAFE
2285 EXTL_EXPORT_MEMBER
2286 ExtlTab split_geom(WSplit *split)
2288 return extl_table_from_rectangle(&(split->geom));
2292 /*EXTL_DOC
2293 * Returns the top or left child node of \var{split} depending
2294 * on the direction of the split.
2296 EXTL_SAFE
2297 EXTL_EXPORT_MEMBER
2298 WSplit *splitsplit_tl(WSplitSplit *split)
2300 return split->tl;
2304 /*EXTL_DOC
2305 * Returns the bottom or right child node of \var{split} depending
2306 * on the direction of the split.
2308 EXTL_SAFE
2309 EXTL_EXPORT_MEMBER
2310 WSplit *splitsplit_br(WSplitSplit *split)
2312 return split->br;
2315 /*EXTL_DOC
2316 * Returns the direction of \var{split}; either \codestr{vertical} or
2317 * \codestr{horizontal}.
2319 EXTL_SAFE
2320 EXTL_EXPORT_MEMBER
2321 const char *splitsplit_dir(WSplitSplit *split)
2323 return (split->dir==SPLIT_VERTICAL ? "vertical" : "horizontal");
2327 /*EXTL_DOC
2328 * Returns the region contained in \var{node}.
2330 EXTL_SAFE
2331 EXTL_EXPORT_MEMBER
2332 WRegion *splitregion_reg(WSplitRegion *node)
2334 return node->reg;
2338 /*}}}*/
2341 /*{{{ Save support */
2344 ExtlTab split_base_config(WSplit *node)
2346 ExtlTab t=extl_create_table();
2347 extl_table_sets_s(t, "type", OBJ_TYPESTR(node));
2348 return t;
2352 static bool splitregion_get_config(WSplitRegion *node, ExtlTab *ret)
2354 ExtlTab rt, t;
2356 if(node->reg==NULL)
2357 return FALSE;
2359 if(!region_supports_save(node->reg)){
2360 warn(TR("Unable to get configuration for %s."),
2361 region_name(node->reg));
2362 return FALSE;
2365 rt=region_get_configuration(node->reg);
2366 t=split_base_config(&(node->split));
2367 extl_table_sets_t(t, "regparams", rt);
2368 extl_unref_table(rt);
2369 *ret=t;
2371 return TRUE;
2375 static bool splitst_get_config(WSplitST *node, ExtlTab *ret)
2377 *ret=split_base_config((WSplit*)node);
2378 return TRUE;
2382 static bool splitsplit_get_config(WSplitSplit *node, ExtlTab *ret)
2384 ExtlTab tab, tltab, brtab;
2385 int tls, brs;
2387 if(!split_get_config(node->tl, &tltab))
2388 return split_get_config(node->br, ret);
2390 if(!split_get_config(node->br, &brtab)){
2391 *ret=tltab;
2392 return TRUE;
2395 tab=split_base_config((WSplit*)node);
2397 tls=split_size(node->tl, node->dir);
2398 brs=split_size(node->br, node->dir);
2400 extl_table_sets_s(tab, "dir", (node->dir==SPLIT_VERTICAL
2401 ? "vertical" : "horizontal"));
2403 extl_table_sets_i(tab, "tls", tls);
2404 extl_table_sets_t(tab, "tl", tltab);
2405 extl_unref_table(tltab);
2407 extl_table_sets_i(tab, "brs", brs);
2408 extl_table_sets_t(tab, "br", brtab);
2409 extl_unref_table(brtab);
2411 *ret=tab;
2413 return TRUE;
2417 bool split_get_config(WSplit *node, ExtlTab *tabret)
2419 bool ret=FALSE;
2420 CALL_DYN_RET(ret, bool, split_get_config, node, (node, tabret));
2421 return ret;
2425 /*}}}*/
2428 /*{{{ The classes */
2431 static DynFunTab split_dynfuntab[]={
2432 {split_do_resize, split_do_resize_default},
2433 {(DynFun*)split_current_todir, (DynFun*)split_current_todir_default},
2434 END_DYNFUNTAB,
2437 static DynFunTab splitinner_dynfuntab[]={
2438 {splitinner_mark_current, splitinner_mark_current_default},
2439 {split_map, splitinner_map},
2440 {split_unmap, splitinner_unmap},
2441 END_DYNFUNTAB,
2444 static DynFunTab splitsplit_dynfuntab[]={
2445 {split_update_bounds, splitsplit_update_bounds},
2446 {split_do_resize, splitsplit_do_resize},
2447 {split_do_maxhelper, splitsplit_do_maxhelper},
2448 {(DynFun*)split_do_restore, (DynFun*)splitsplit_do_restore},
2449 {(DynFun*)split_do_verify, (DynFun*)splitsplit_do_verify},
2450 {splitinner_do_rqsize, splitsplit_do_rqsize},
2451 {splitinner_replace, splitsplit_replace},
2452 {splitinner_remove, splitsplit_remove},
2453 {(DynFun*)split_current_todir, (DynFun*)splitsplit_current_todir},
2454 {(DynFun*)splitinner_current, (DynFun*)splitsplit_current},
2455 {(DynFun*)splitinner_nextto, (DynFun*)splitsplit_nextto},
2456 {splitinner_mark_current, splitsplit_mark_current},
2457 {(DynFun*)split_get_config, (DynFun*)splitsplit_get_config},
2458 {splitinner_forall, splitsplit_forall},
2459 {split_restack, splitsplit_restack},
2460 {split_stacking, splitsplit_stacking},
2461 {split_reparent, splitsplit_reparent},
2462 {splitsplit_flip, splitsplit_flip_default},
2463 END_DYNFUNTAB,
2466 static DynFunTab splitregion_dynfuntab[]={
2467 {split_update_bounds, splitregion_update_bounds},
2468 {split_do_resize, splitregion_do_resize},
2469 {split_do_maxhelper, splitregion_do_maxhelper},
2470 {(DynFun*)split_do_restore, (DynFun*)splitregion_do_restore},
2471 {(DynFun*)split_do_verify, (DynFun*)splitregion_do_verify},
2472 {(DynFun*)split_get_config, (DynFun*)splitregion_get_config},
2473 {split_map, splitregion_map},
2474 {split_unmap, splitregion_unmap},
2475 {split_restack, splitregion_restack},
2476 {split_stacking, splitregion_stacking},
2477 {split_reparent, splitregion_reparent},
2478 END_DYNFUNTAB,
2481 static DynFunTab splitst_dynfuntab[]={
2482 {split_update_bounds, splitst_update_bounds},
2483 {split_do_resize, splitst_do_resize},
2484 {split_do_maxhelper, splitst_do_maxhelper},
2485 {(DynFun*)split_do_restore, (DynFun*)splitst_do_restore},
2486 {(DynFun*)split_do_verify, (DynFun*)splitst_do_verify},
2487 {(DynFun*)split_get_config, (DynFun*)splitst_get_config},
2488 END_DYNFUNTAB,
2492 EXTL_EXPORT
2493 IMPLCLASS(WSplit, Obj, split_deinit, split_dynfuntab);
2495 EXTL_EXPORT
2496 IMPLCLASS(WSplitInner, WSplit, splitinner_deinit, splitinner_dynfuntab);
2498 EXTL_EXPORT
2499 IMPLCLASS(WSplitSplit, WSplitInner, splitsplit_deinit, splitsplit_dynfuntab);
2501 EXTL_EXPORT
2502 IMPLCLASS(WSplitRegion, WSplit, splitregion_deinit, splitregion_dynfuntab);
2504 EXTL_EXPORT
2505 IMPLCLASS(WSplitST, WSplitRegion, splitst_deinit, splitst_dynfuntab);
2508 /*}}}*/