Remove/mark some unused functions and parameters
[notion.git] / mod_tiling / split.c
blob28ad6dec8a978382f9c81e7a4d1b378cca54314a
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 /* 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)
66 return INT_MAX;
67 else
68 return x+y;
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
74 * node.
76 static int unusedadd(int x, int y)
78 if(x<0 && y<0)
79 return -1;
80 return maxof(x, 0)+maxof(y, 0);
84 static void bound(int *what, int min, int max)
86 if(*what<min)
87 *what=min;
88 else if(*what>max)
89 *what=max;
93 /*}}}*/
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)
103 Rb_node node=NULL;
104 int found=0;
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);
110 if(found)
111 return (WSplitRegion*)(node->v.val);
114 return NULL;
118 #define set_node_of_reg splittree_set_node_of
121 bool splittree_set_node_of(WRegion *reg, WSplitRegion *split)
123 Rb_node node=NULL;
124 int found;
126 /*assert(REGION_MANAGER_CHK(reg, WTiling)!=NULL);*/
128 if(split_of_map==NULL){
129 if(split==NULL)
130 return TRUE;
131 split_of_map=make_rb();
132 if(split_of_map==NULL)
133 return FALSE;
136 node=rb_find_pkey_n(split_of_map, reg, &found);
137 if(found)
138 rb_delete_node(node);
140 return (rb_insertp(split_of_map, reg, split)!=NULL);
144 /*}}}*/
147 /*{{{ Primn */
150 WPrimn primn_invert(WPrimn primn)
152 return (primn==PRIMN_TL
153 ? PRIMN_BR
154 : (primn==PRIMN_BR
155 ? PRIMN_TL
156 : primn));
160 WPrimn primn_none2any(WPrimn primn)
162 return (primn==PRIMN_NONE ? PRIMN_ANY : primn);
166 /*}}}*/
169 /*{{{ Create */
172 bool split_init(WSplit *split, const WRectangle *geom)
174 split->parent=NULL;
175 split->ws_if_root=NULL;
176 split->geom=*geom;
177 split->min_w=0;
178 split->min_h=0;
179 split->max_w=INT_MAX;
180 split->max_h=INT_MAX;
181 split->unused_w=-1;
182 split->unused_h=-1;
183 return TRUE;
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);
195 split->dir=dir;
196 split->tl=NULL;
197 split->br=NULL;
198 split->current=SPLIT_CURRENT_TL;
199 return TRUE;
203 bool splitregion_init(WSplitRegion *split, const WRectangle *geom,
204 WRegion *reg)
206 split_init(&(split->split), geom);
207 split->reg=reg;
208 if(reg!=NULL)
209 set_node_of_reg(reg, split);
210 return TRUE;
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;
219 return TRUE;
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));
241 /*}}}*/
244 /*{{{ Deinit */
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)
261 if(split->tl!=NULL){
262 split->tl->parent=NULL;
263 destroy_obj((Obj*)(split->tl));
265 if(split->br!=NULL){
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);
278 split->reg=NULL;
281 split_deinit(&(split->split));
285 void splitst_deinit(WSplitST *split)
287 splitregion_deinit(&(split->regnode));
291 /*}}}*/
294 /*{{{ Size bounds management */
297 static void splitregion_update_bounds(WSplitRegion *node, bool UNUSED(recursive))
299 WSizeHints hints;
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;
308 snode->unused_w=-1;
310 snode->min_h=maxof(1, hints.min_set ? hints.min_height : 1);
311 snode->max_h=INT_MAX;
312 snode->unused_h=-1;
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;
325 }else{
326 WSizeHints hints;
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);
334 snode->unused_w=-1;
335 snode->unused_h=-1;
337 if(node->orientation==REGION_ORIENTATION_HORIZONTAL){
338 snode->min_w=CF_STDISP_MIN_SZ;
339 snode->max_w=INT_MAX;
340 }else{
341 snode->min_h=CF_STDISP_MIN_SZ;
342 snode->max_h=INT_MAX;
347 static void splitsplit_update_bounds(WSplitSplit *split, bool recursive)
349 WSplit *tl, *br;
350 WSplit *node=(WSplit*)split;
352 assert(split->tl!=NULL && split->br!=NULL);
354 tl=split->tl;
355 br=split->br;
357 if(recursive){
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);
369 }else{
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;
398 /*}}}*/
401 /*{{{ Status display handling helper functions. */
404 static WSplitST *saw_stdisp=NULL;
407 void splittree_begin_resize()
409 saw_stdisp=NULL;
413 void splittree_end_resize()
415 if(saw_stdisp!=NULL){
416 split_regularise_stdisp(saw_stdisp);
417 saw_stdisp=NULL;
422 static void splittree_scan_stdisp_rootward_(WSplitInner *node_)
424 WSplitSplit *node=OBJ_CAST(node_, WSplitSplit);
426 if(node!=NULL){
427 if(OBJ_IS(node->tl, WSplitST)){
428 saw_stdisp=(WSplitST*)(node->tl);
429 return;
430 }else if(OBJ_IS(node->br, WSplitST)){
431 saw_stdisp=(WSplitST*)(node->br);
432 return;
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)
450 WSplitST *r=NULL;
451 WSplitSplit *node=OBJ_CAST(node_, WSplitSplit);
453 if(node==NULL)
454 return NULL;
456 r=OBJ_CAST(node->tl, WSplitST);
457 if(r==NULL)
458 r=OBJ_CAST(node->br, WSplitST);
460 if(r!=NULL){
461 if(set_saw)
462 saw_stdisp=r;
463 return r;
466 r=splittree_scan_stdisp(node->tl, set_saw);
467 if(r==NULL)
468 r=splittree_scan_stdisp(node->br, set_saw);
470 return r;
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)
483 WSplitST *stdisp;
484 WSplitSplit *stdispp;
486 stdisp=splittree_scan_stdisp(node, TRUE);
488 if(stdisp==NULL)
489 return node;
491 stdispp=OBJ_CAST(((WSplit*)stdisp)->parent, WSplitSplit);
493 if(stdispp==NULL)
494 return node;
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.
501 return (keep_within
502 ? node
503 : (stdispp->tl==(WSplit*)stdisp
504 ? stdispp->br
505 : stdispp->tl));
509 if(!split_try_unsink_stdisp(stdispp, FALSE, TRUE)){
510 warn(TR("Unable to move the status display out of way."));
511 return NULL;
513 }while(stdispp->tl!=node && stdispp->br!=node);
515 return node;
519 /*}}}*/
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))
529 node->geom=*ng;
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,
546 bool transpose)
548 saw_stdisp=node;
550 if(node->regnode.reg==NULL){
551 ((WSplit*)node)->geom=*ng;
552 }else{
553 splitregion_do_resize(&(node->regnode), ng, hprimn, vprimn,
554 transpose);
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,
567 int primn)
569 int tls=*tls_;
570 int brs=*brs_;
572 if(primn==PRIMN_TL){
573 tls=tls+nsize-sz;
574 bound(&tls, tlmin, tlmax);
575 brs=nsize-tls;
576 bound(&brs, brmin, brmax);
577 tls=nsize-brs;
578 bound(&tls, tlmin, tlmax);
579 }else if(primn==PRIMN_BR){
580 brs=brs+nsize-sz;
581 bound(&brs, brmin, brmax);
582 tls=nsize-brs;
583 bound(&tls, tlmin, tlmax);
584 brs=nsize-tls;
585 bound(&brs, brmin, brmax);
586 }else{ /* && PRIMN_ANY */
587 tls=tls*nsize/sz;
588 bound(&tls, tlmin, tlmax);
589 brs=nsize-tls;
590 bound(&brs, brmin, brmax);
591 tls=nsize-brs;
592 bound(&tls, tlmin, tlmax);
595 *tls_=tls;
596 *brs_=brs;
600 static void get_minmaxunused(WSplit *node, int dir,
601 int *min, int *max, int *unused)
603 if(dir==SPLIT_VERTICAL){
604 *min=node->min_h;
605 *max=maxof(*min, node->max_h);
606 *unused=minof(node->unused_h, node->geom.h);
607 }else{
608 *min=node->min_w;
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);
626 int sz=tls+brs;
627 /* Status display can not be transposed. */
628 int dir=((transpose && !stdisp_immediate_child(node))
629 ? other_dir(node->dir)
630 : 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) */
644 if(sz>2){
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);
650 }else{
651 /* Just expand or shrink unused space */
652 adjust_sizes(&tls, &brs, nsize, sz,
653 tlused, brused,
654 (tlunused<0 ? tlused : tlmax),
655 (brunused<0 ? brused : brmax), primn);
658 }else{
659 adjust_sizes(&tls, &brs, nsize, sz,
660 tlmin, brmin, tlmax, brmax, primn);
664 if(tls+brs!=nsize){
665 /* Bad fit; just size proportionally. */
666 if(sz<=2){
667 tls=nsize/2;
668 brs=nsize-tls;
669 }else{
670 tls=split_size(tl, node->dir)*nsize/sz;
671 brs=nsize-tls;
675 if(dir==SPLIT_VERTICAL){
676 tlg.h=tls;
677 brg.y+=tls;
678 brg.h=brs;
679 }else{
680 tlg.w=tls;
681 brg.x+=tls;
682 brg.w=brs;
685 split_do_resize(tl, &tlg, hprimn, vprimn, transpose);
686 split_do_resize(br, &brg, hprimn, vprimn, transpose);
688 node->dir=dir;
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();
712 /*}}}*/
715 /*{{{ Save, restore and verify code for maximization */
718 bool splits_are_related(WSplit *p, WSplit *node)
720 if(p==node)
721 return TRUE;
723 return
724 node->parent!=NULL
725 ? splits_are_related(p, (WSplit*)node->parent)
726 : FALSE;
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);
745 if(sp->dir!=dir){
746 return
747 splits_are_related(sp->tl, node)
748 ? maxparentdir_rel(sp->tl, node, dir)
749 : maxparentdir_rel(sp->br, node, dir);
753 return p;
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. */
782 return
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)
790 return
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);
807 return stg;
810 bool geom_overlaps_stgeom_xy(WRectangle geom, WSplitST *st, WRectangle stg)
812 /* TRUE FALSE
814 * ------ ------
815 * |geom| |geom|
816 * ------ ------
817 * ----- -----
818 * |stg| |stg|
819 * ----- -----
821 int ori=st->orientation;
823 return
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)
831 /* TRUE FALSE FALSE
833 * ------
834 * |geom|
835 * ------ ------
836 * |geom| ------
837 * ----- ------ ----- |geom| -----
838 * |stg| |stg| ------ |stg|
839 * ----- ----- -----
842 WRectangle stg=REGION_GEOM(st->regnode.reg);
843 int ori=flip_orientation(st->orientation);
845 return
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)
853 /* BEFORE AFTER
856 * ------ ------
857 * |geom| | |
858 * ----- ------ ----- |geom|
859 * |stg| |stg| | |
860 * ----- ----- ------
863 WRectangle stg=REGION_GEOM(st->regnode.reg);
864 int ori=flip_orientation(st->orientation);
866 if(is_lt(ori, st->corner))
867 *xy(geom, ori)=0;
868 *wh(geom, ori)+=*wh(&stg, ori);
871 bool frame_neighbors_stdisp(WFrame *frame, WSplitST *st)
873 return
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);
882 return
883 is_lt(ori, st->corner)
884 ? *xy(&geom, ori)==0
885 : *xy(&geom, ori)+*wh(&geom, ori)==*xy(&stg, ori)+*wh(&stg, ori);
888 bool is_same_dir(int dir, int ori)
890 return
891 (dir==SPLIT_HORIZONTAL && ori==REGION_ORIENTATION_HORIZONTAL) ||
892 (dir==SPLIT_VERTICAL && ori==REGION_ORIENTATION_VERTICAL);
895 bool is_maxed(WFrame *frame, int dir)
897 return
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);
906 WSplitST *st;
907 WRectangle stg;
908 WRectangle rstg;
909 int ori;
911 if(!OBJ_IS(ws, WTiling) || ((WTiling*)ws)->stdispnode==NULL)
912 return FALSE;
914 st=((WTiling*)ws)->stdispnode;
916 if(st->fullsize || !frame_neighbors_stdisp(frame, st))
917 return FALSE;
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);
930 return TRUE;
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);
944 return FALSE;
947 bool splitregion_do_restore(WSplitRegion *node, int dir)
949 WFrame *frame;
950 WRectangle geom=((WSplit*)node)->geom;
951 WRectangle fakegeom;
952 bool ret;
953 bool other_max;
955 if(!OBJ_IS(node->reg, WFrame))
956 return FALSE;
958 frame=(WFrame*)node->reg;
959 if(dir==SPLIT_HORIZONTAL){
960 geom.x=frame->saved_geom.x;
961 geom.w=frame->saved_geom.w;
962 }else{
963 geom.y=frame->saved_geom.y;
964 geom.h=frame->saved_geom.h;
967 other_max=
968 dir==SPLIT_HORIZONTAL
969 ? frame->flags&FRAME_MAXED_VERT
970 : frame->flags&FRAME_MAXED_HORIZ;
972 fakegeom=geom;
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
979 * correctly. */
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;
992 return ret;
995 bool splitst_do_restore(WSplit *UNUSED(node), int UNUSED(dir))
997 return FALSE;
1000 bool splitsplit_do_restore(WSplitSplit *node, int dir)
1002 bool ret1, ret2, ret=FALSE;
1003 WSplit *snode=(WSplit*)node;
1005 WSplitST *st;
1006 WSplit *other;
1007 WRectangle stg;
1008 WRectangle og;
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;
1015 other=node->br;
1016 }else{
1017 st=(WSplitST*)node->br;
1018 other=node->tl;
1020 stg=((WSplit*)st)->geom;
1021 split_do_restore(other, dir);
1022 og=other->geom;
1023 if(node->dir==SPLIT_HORIZONTAL){
1024 stg.y=og.y;
1025 stg.h=og.h;
1026 }else{
1027 stg.x=og.x;
1028 stg.w=og.w;
1030 if(rectangle_compare(&stg, &((WSplit*)st)->geom)){
1031 splitst_do_resize(st, &stg, PRIMN_ANY, PRIMN_ANY, FALSE);
1032 ret=TRUE;
1034 }else{
1035 /* Avoid short-circuit evaluation. */
1036 ret1=split_do_restore(node->tl, dir);
1037 ret2=split_do_restore(node->br, dir);
1038 ret=ret1 || ret2;
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;
1052 return ret;
1055 bool split_do_restore(WSplit *node, int dir)
1057 bool ret = FALSE;
1058 CALL_DYN_RET(ret, bool, split_do_restore, node, (node, dir));
1059 return ret;
1063 void splitregion_do_maxhelper(WSplitRegion *node, int dir, int action)
1065 WFrame *frame;
1066 if(!OBJ_IS(node->reg, WFrame))
1067 return;
1068 frame=(WFrame*)node->reg;
1070 if(action==SAVE){
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;
1076 }else{
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;
1084 if(action==RM_KEEP)
1085 frame->flags&=~FRAME_KEEP_FLAGS;
1088 void splitst_do_maxhelper(WSplit *UNUSED(node), int UNUSED(dir), int UNUSED(action))
1090 return;
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);
1109 WSplitST *st;
1110 int ori;
1112 if(!OBJ_IS(ws, WTiling) || ((WTiling*)ws)->stdispnode==NULL)
1113 return TRUE;
1115 st=((WTiling*)ws)->stdispnode;
1116 ori=flip_orientation(st->orientation);
1118 return
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(&REGION_GEOM(st), ori)
1123 : FALSE;
1126 bool splitregion_do_verify(WSplitRegion *node, int dir)
1128 WFrame *frame;
1129 bool ret=FALSE;
1131 if(!OBJ_IS(node->reg, WFrame))
1132 return FALSE;
1134 frame=(WFrame*)node->reg;
1136 ret=is_maxed(frame, dir);
1138 if(dir==HORIZONTAL)
1139 frame->flags&=~(FRAME_MAXED_HORIZ|FRAME_SAVED_HORIZ);
1140 else
1141 frame->flags&=~(FRAME_MAXED_VERT|FRAME_SAVED_VERT);
1143 if(savedgeom_clashes_stdisp(frame, dir))
1144 return FALSE;
1146 return ret;
1149 bool splitst_do_verify(WSplit *UNUSED(node), int UNUSED(dir))
1151 return TRUE;
1154 bool splitsplit_do_verify(WSplitSplit *node, int dir)
1156 bool ret1, ret2;
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)
1167 bool ret = FALSE;
1168 CALL_DYN_RET(ret, bool, split_do_verify, node, (node, dir));
1169 return ret;
1173 bool split_maximize(WSplit *node, int dir, int action)
1175 WSplit *p=maxparentdir(node, dir);
1176 if(action==RESTORE)
1177 return split_do_restore(p, dir);
1178 if(action==VERIFY)
1179 return split_do_verify(p, dir);
1181 split_do_maxhelper(p, dir, action);
1182 return TRUE;
1186 /*}}}*/
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);
1198 else
1199 *stretch=INT_MAX;
1200 }else{
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);
1204 else
1205 *stretch=INT_MAX;
1210 static void calc_amount(int *amount, int rs, WSplit *other, int dir)
1212 int shrink, stretch;
1214 flexibility(other, dir, &shrink, &stretch);
1216 if(rs>0)
1217 *amount=minof(rs, shrink);
1218 else if(rs<0)
1219 *amount=-minof(-rs, stretch);
1220 else
1221 *amount=0;
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;
1232 RootwardAmount *ca;
1233 WSplit *other;
1234 WPrimn thisnode;
1235 int amount;
1237 assert(!ha->any || ha->tl==0);
1238 assert(!va->any || va->tl==0);
1239 assert(p->tl==node || p->br==node);
1241 if(p->tl==node){
1242 other=p->br;
1243 thisnode=PRIMN_TL;
1244 }else{
1245 other=p->tl;
1246 thisnode=PRIMN_BR;
1249 ca=(p->dir==SPLIT_VERTICAL ? va : ha);
1251 if(thisnode==PRIMN_TL || ca->any){
1252 calc_amount(&amount, ca->br, other, p->dir);
1253 ca->br-=amount;
1254 }else/*if(thisnode==PRIMN_BR)*/{
1255 calc_amount(&amount, ca->tl, other, p->dir);
1256 ca->tl-=amount;
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));
1263 else
1264 pg=((WSplit*)p)->geom;
1265 }else{
1266 splitinner_do_rqsize(((WSplit*)p)->parent, (WSplit*)p, ha, va,
1267 &pg, tryonly);
1270 assert(pg.w>=0 && pg.h>=0);
1272 og=pg;
1273 ng=pg;
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;
1283 else
1284 ng.y=pg.y+pg.h-ng.h;
1285 vprimn=thisnode;
1286 }else{
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;
1294 else
1295 ng.x=pg.x+pg.w-ng.w;
1296 hprimn=thisnode;
1299 if(!tryonly){
1300 /* Entä jos 'other' on stdisp? */
1301 split_do_resize(other, &og, hprimn, vprimn, FALSE);
1303 ((WSplit*)p)->geom=pg;
1306 *rg=ng;
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,
1319 bool any)
1321 ra->any=any;
1322 ra->tl=op-p;
1323 ra->br=(p+s)-(op+os);
1324 if(any){
1325 ra->br+=ra->tl;
1326 ra->tl=0;
1331 void split_do_rqgeom_(WSplit *node, const WRectangle *ng,
1332 bool hany, bool vany, WRectangle *rg,
1333 bool tryonly)
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));
1340 else
1341 *rg=*ng;
1342 }else{
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);
1351 /*}}}*/
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));
1361 int szrq=*sz;
1363 if(ud+dd!=0){
1364 bound(sz, minsz, maxsz);
1365 *pos+=(szrq-*sz)*ud/(ud+dd);
1370 WSplit *split_find_root(WSplit *split)
1372 if(split->parent==NULL)
1373 return split;
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&REGION_RQGEOM_WEAK_X;
1382 bool vany=flags&REGION_RQGEOM_WEAK_Y;
1383 bool tryonly=flags&REGION_RQGEOM_TRYONLY;
1384 WRectangle geom=*geom_;
1385 WRectangle retg;
1386 WSplit *root=split_find_root(sub);
1388 if(geomret==NULL)
1389 geomret=&retg;
1391 split_update_bounds(root, TRUE);
1393 if(OBJ_IS(sub, WSplitST)){
1394 WSplitST *sub_as_stdisp=(WSplitST*)sub;
1396 if(flags&REGION_RQGEOM_TRYONLY){
1397 warn(TR("REGION_RQGEOM_TRYONLY unsupported for status display."));
1398 *geomret=sub->geom;
1399 return;
1401 split_regularise_stdisp(sub_as_stdisp);
1402 geom=sub->geom;
1403 if(sub_as_stdisp->orientation==REGION_ORIENTATION_HORIZONTAL){
1404 if(geom_->h==geom.h)
1405 return;
1406 geom.h=geom_->h;
1407 }else{
1408 if(geom_->w==geom.w)
1409 return;
1410 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 */
1423 if(hany){
1424 geom.w+=sub->geom.x-geom.x;
1425 geom.x=sub->geom.x;
1428 if(vany){
1429 geom.h+=sub->geom.y-geom.y;
1430 geom.y=sub->geom.y;
1433 splittree_begin_resize();
1435 split_do_rqgeom_(sub, &geom, hany, vany, geomret, tryonly);
1437 if(!tryonly){
1438 split_do_resize(sub, geomret, hany, vany, FALSE);
1439 splittree_end_resize();
1440 *geomret=sub->geom;
1441 }else{
1442 saw_stdisp=NULL;
1447 /*EXTL_DOC
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}).
1452 EXTL_EXPORT_MEMBER
1453 ExtlTab split_rqgeom(WSplit *node, ExtlTab g)
1455 WRectangle geom, ogeom;
1456 int flags=REGION_RQGEOM_WEAK_ALL;
1458 geom=node->geom;
1459 ogeom=geom;
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);
1479 /*}}}*/
1482 /*{{{ Split */
1485 void splittree_changeroot(WSplit *root, WSplit *node)
1487 WTiling *ws=(WTiling*)(root->ws_if_root);
1489 assert(ws!=NULL);
1490 assert(ws->split_tree==root);
1491 root->ws_if_root=NULL;
1492 ws->split_tree=node;
1493 if(node!=NULL){
1494 node->ws_if_root=ws;
1495 node->parent=NULL;
1500 static void splitsplit_replace(WSplitSplit *split, WSplit *child,
1501 WSplit *what)
1503 assert(split->tl==child || split->br==child);
1505 if(split->tl==child)
1506 split->tl=what;
1507 else
1508 split->br=what;
1510 child->parent=NULL;
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,
1525 WWindow *parent)
1527 int objmin;
1528 int s, sn, so;
1529 WSplitSplit *nsplit;
1530 WSplitRegion *nnode;
1531 WSplitInner *psplit;
1532 WRegion *nreg;
1533 WFitParams fp;
1534 WRectangle ng, rg;
1536 assert(node!=NULL && parent!=NULL);
1538 splittree_begin_resize();
1540 node=dodge_stdisp(node, FALSE);
1542 if(node==NULL)
1543 return NULL;
1545 if(OBJ_IS(node, WSplitST)){
1546 warn(TR("Splitting the status display is not allowed."));
1547 return NULL;
1550 if(primn!=PRIMN_TL && primn!=PRIMN_BR)
1551 primn=PRIMN_BR;
1552 if(dir!=SPLIT_HORIZONTAL && dir!=SPLIT_VERTICAL)
1553 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);
1562 if(sn+so!=s){
1563 int rs;
1564 ng=node->geom;
1565 if(dir==SPLIT_VERTICAL)
1566 ng.h=sn+so;
1567 else
1568 ng.w=sn+so;
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."));
1573 return NULL;
1575 split_do_rqgeom_(node, &ng, TRUE, TRUE, &rg, FALSE);
1576 rs=(dir==SPLIT_VERTICAL ? rg.h : rg.w);
1577 if(minsize>rs/2){
1578 sn=minsize;
1579 so=rs-sn;
1580 }else{
1581 so=maxof(rs/2, objmin);
1582 sn=rs-so;
1584 }else{
1585 rg=node->geom;
1586 splittree_scan_stdisp_rootward(node);
1589 /* Create split and new window
1591 fp.mode=REGION_FIT_EXACT;
1592 fp.g=rg;
1594 nsplit=create_splitsplit(&(fp.g), dir);
1596 if(nsplit==NULL)
1597 return NULL;
1599 if(dir==SPLIT_VERTICAL){
1600 if(primn==PRIMN_BR)
1601 fp.g.y+=so;
1602 fp.g.h=sn;
1603 }else{
1604 if(primn==PRIMN_BR)
1605 fp.g.x+=so;
1606 fp.g.w=sn;
1609 nreg=fn(parent, &fp);
1611 if(nreg==NULL){
1612 destroy_obj((Obj*)nsplit);
1613 return NULL;
1616 nnode=create_splitregion(&(fp.g), nreg);
1617 if(nnode==NULL){
1618 destroy_obj((Obj*)nreg);
1619 destroy_obj((Obj*)nsplit);
1620 return NULL;
1623 /* Now that everything's ok, resize and move original node.
1625 ng=rg;
1626 if(dir==SPLIT_VERTICAL){
1627 ng.h=so;
1628 if(primn==PRIMN_TL)
1629 ng.y+=sn;
1630 }else{
1631 ng.w=so;
1632 if(primn==PRIMN_TL)
1633 ng.x+=sn;
1636 split_do_resize(node, &ng,
1637 (dir==SPLIT_HORIZONTAL ? primn : PRIMN_ANY),
1638 (dir==SPLIT_VERTICAL ? primn : PRIMN_ANY),
1639 FALSE);
1641 /* Set up split structure
1643 psplit=node->parent;
1645 if(psplit!=NULL)
1646 splitinner_replace(psplit, node, (WSplit*)nsplit);
1647 else
1648 splittree_changeroot(node, (WSplit*)nsplit);
1650 node->parent=(WSplitInner*)nsplit;
1651 ((WSplit*)nnode)->parent=(WSplitInner*)nsplit;
1653 if(primn==PRIMN_BR){
1654 nsplit->tl=node;
1655 nsplit->br=(WSplit*)nnode;
1656 nsplit->current=SPLIT_CURRENT_TL;
1657 }else{
1658 nsplit->tl=(WSplit*)nnode;
1659 nsplit->br=node;
1660 nsplit->current=SPLIT_CURRENT_BR;
1663 splittree_end_resize();
1665 return nnode;
1669 /*}}}*/
1672 /*{{{ Remove */
1675 static void splitsplit_remove(WSplitSplit *node, WSplit *child,
1676 bool reclaim_space)
1678 static int nstdisp=0;
1679 WSplitInner *parent;
1680 WSplit *other;
1681 int hprimn=PRIMN_ANY, vprimn=PRIMN_ANY;
1683 assert(node->tl==child || node->br==child);
1685 if(node->tl==child){
1686 other=node->br;
1687 if(node->dir==SPLIT_VERTICAL)
1688 vprimn=PRIMN_TL;
1689 else
1690 hprimn=PRIMN_TL;
1691 }else{
1692 other=node->tl;
1693 if(node->dir==SPLIT_VERTICAL)
1694 vprimn=PRIMN_BR;
1695 else
1696 hprimn=PRIMN_BR;
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);
1705 nstdisp++;
1706 splitinner_remove(child->parent, child, reclaim_space);
1707 nstdisp--;
1708 return;
1711 parent=((WSplit*)node)->parent;
1713 if(parent!=NULL)
1714 splitinner_replace(parent, (WSplit*)node, other);
1715 else
1716 splittree_changeroot((WSplit*)node, other);
1718 if(reclaim_space)
1719 split_resize(other, &(((WSplit*)node)->geom), hprimn, vprimn);
1721 child->parent=NULL;
1723 node->tl=NULL;
1724 node->br=NULL;
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);
1747 /*}}}*/
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)
1764 if(filter==NULL)
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)){
1780 first=node->tl;
1781 second=node->br;
1782 }else if(primn==PRIMN_BR ||
1783 (primn==PRIMN_ANY && node->current==SPLIT_CURRENT_BR)){
1784 first=node->br;
1785 second=node->tl;
1786 }else{
1787 return NULL;
1790 ret=split_current_todir(first, hprimn, vprimn, filter);
1791 if(ret==NULL)
1792 ret=split_current_todir(second, hprimn, vprimn, filter);
1793 if(ret==NULL && filter!=NULL){
1794 if(filter((WSplit*)node))
1795 ret=(WSplit*)node;
1798 return ret;
1802 WSplit *split_current_todir(WSplit *node, WPrimn hprimn, WPrimn vprimn,
1803 WSplitFilter *filter)
1805 WSplit *ret=NULL;
1806 CALL_DYN_RET(ret, WSplit*, split_current_todir, node,
1807 (node, hprimn, vprimn, filter));
1808 return ret;
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))
1825 split=node->br;
1826 else if(node->br==child && (primn==PRIMN_TL || primn==PRIMN_ANY))
1827 split=node->tl;
1829 if(split!=NULL){
1830 nnode=split_current_todir(split,
1831 primn_none2any(primn_invert(hprimn)),
1832 primn_none2any(primn_invert(vprimn)),
1833 filter);
1836 if(nnode==NULL)
1837 nnode=split_nextto((WSplit*)node, hprimn, vprimn, filter);
1839 return nnode;
1843 WSplit *splitinner_nextto(WSplitInner *node, WSplit *child,
1844 WPrimn hprimn, WPrimn vprimn,
1845 WSplitFilter *filter)
1847 WSplit *ret=NULL;
1848 CALL_DYN_RET(ret, WSplit*, splitinner_nextto, node,
1849 (node, child, hprimn, vprimn, filter));
1850 return ret;
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);
1860 if(ret!=NULL)
1861 return ret;
1862 node=(WSplit*)node->parent;
1864 return NULL;
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)
1893 fn(node->tl);
1894 fn(node->br);
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);
1910 /*EXTL_DOC
1911 * Returns the most previously active child node of \var{split}.
1913 EXTL_SAFE
1914 EXTL_EXPORT_MEMBER
1915 WSplit *splitinner_current(WSplitInner *node)
1917 WSplit *ret=NULL;
1918 CALL_DYN_RET(ret, WSplit*, splitinner_current, node, (node));
1919 return ret;
1923 /*}}}*/
1926 /*{{{ X window handling */
1929 static void splitregion_stacking(WSplitRegion *split,
1930 Window *bottomret, Window *topret)
1932 *bottomret=None;
1933 *topret=None;
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);
1954 }else{
1955 *topret=(brt!=None ? brt : tlt);
1956 *bottomret=(tlb!=None ? tlb : brb);
1960 void split_stacking(WSplit *split, Window *bottomret, Window *topret)
1962 *bottomret=None;
1963 *topret=None;
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){
1982 first=split->br;
1983 second=split->tl;
1984 }else{
1985 first=split->tl;
1986 second=split->br;
1989 split_restack(first, other, mode);
1990 split_stacking(first, &bottom, &top);
1991 if(top!=None){
1992 other=top;
1993 mode=Above;
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);
2052 }else{
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));
2065 /*}}}*/
2068 /*{{{ Transpose, flip, rotate */
2071 void splitsplit_flip_default(WSplitSplit *split)
2073 WRectangle tlng, brng;
2074 WRectangle *sg=&((WSplit*)split)->geom;
2075 WSplit *tmp;
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){
2085 brng.x=sg->x;
2086 tlng.x=sg->x+sg->w-tlng.w;
2087 }else{
2088 brng.y=sg->y;
2089 tlng.y=sg->y+sg->h-tlng.h;
2092 tmp=split->tl;
2093 split->tl=split->br;
2094 split->br=tmp;
2095 split->current=(split->current==SPLIT_CURRENT_TL
2096 ? SPLIT_CURRENT_BR
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));
2110 /*EXTL_DOC
2111 * Flip contents of \var{split}.
2113 EXTL_EXPORT_MEMBER
2114 void splitsplit_flip(WSplitSplit *split)
2116 splittree_begin_resize();
2118 split=OBJ_CAST(dodge_stdisp((WSplit*)split, FALSE), WSplitSplit);
2120 if(split==NULL)
2121 return;
2123 splitsplit_flip_(split);
2125 splittree_end_resize();
2128 typedef enum{
2129 FLIP_VERTICAL,
2130 FLIP_HORIZONTAL,
2131 FLIP_NONE,
2132 FLIP_ANY
2133 } FlipDir;
2136 static FlipDir flipdir=FLIP_VERTICAL;
2139 static void do_flip(WSplit *split)
2141 WSplitSplit *ss=OBJ_CAST(split, WSplitSplit);
2143 if(ss!=NULL){
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)){
2162 flipdir=dir;
2163 splitinner_forall((WSplitInner*)splittree, do_flip);
2168 static bool split_fliptrans_to(WSplit *node, const WRectangle *geom,
2169 bool trans, FlipDir flip)
2171 WRectangle rg;
2172 WSplit *node2;
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)
2186 return FALSE;
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);
2194 if(flip!=FLIP_NONE)
2195 splittree_flip_dir(node, flip);
2197 splittree_end_resize();
2199 return TRUE;
2203 bool split_transpose_to(WSplit *node, const WRectangle *geom)
2205 return split_fliptrans_to(node, geom, TRUE, FLIP_ANY);
2209 /*EXTL_DOC
2210 * Transpose contents of \var{node}.
2212 EXTL_EXPORT_MEMBER
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;
2224 bool trans=FALSE;
2226 if(rotation==SCREEN_ROTATION_90){
2227 flip=FLIP_HORIZONTAL;
2228 trans=TRUE;
2229 }else if(rotation==SCREEN_ROTATION_180){
2230 flip=FLIP_ANY;
2231 }else if(rotation==SCREEN_ROTATION_270){
2232 flip=FLIP_VERTICAL;
2233 trans=TRUE;
2236 return split_fliptrans_to(node, geom, trans, flip);
2239 /*}}}*/
2242 /*{{{ Exports */
2245 /*EXTL_DOC
2246 * Return parent split for \var{split}.
2248 EXTL_SAFE
2249 EXTL_EXPORT_MEMBER
2250 WSplitInner *split_parent(WSplit *split)
2252 return split->parent;
2256 /*EXTL_DOC
2257 * Returns the area of workspace used by the regions under \var{split}.
2259 EXTL_SAFE
2260 EXTL_EXPORT_MEMBER
2261 ExtlTab split_geom(WSplit *split)
2263 return extl_table_from_rectangle(&(split->geom));
2267 /*EXTL_DOC
2268 * Returns the top or left child node of \var{split} depending
2269 * on the direction of the split.
2271 EXTL_SAFE
2272 EXTL_EXPORT_MEMBER
2273 WSplit *splitsplit_tl(WSplitSplit *split)
2275 return split->tl;
2279 /*EXTL_DOC
2280 * Returns the bottom or right child node of \var{split} depending
2281 * on the direction of the split.
2283 EXTL_SAFE
2284 EXTL_EXPORT_MEMBER
2285 WSplit *splitsplit_br(WSplitSplit *split)
2287 return split->br;
2290 /*EXTL_DOC
2291 * Returns the direction of \var{split}; either \codestr{vertical} or
2292 * \codestr{horizontal}.
2294 EXTL_SAFE
2295 EXTL_EXPORT_MEMBER
2296 const char *splitsplit_dir(WSplitSplit *split)
2298 return (split->dir==SPLIT_VERTICAL ? "vertical" : "horizontal");
2302 /*EXTL_DOC
2303 * Returns the region contained in \var{node}.
2305 EXTL_SAFE
2306 EXTL_EXPORT_MEMBER
2307 WRegion *splitregion_reg(WSplitRegion *node)
2309 return node->reg;
2313 /*}}}*/
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));
2323 return t;
2327 static bool splitregion_get_config(WSplitRegion *node, ExtlTab *ret)
2329 ExtlTab rt, t;
2331 if(node->reg==NULL)
2332 return FALSE;
2334 if(!region_supports_save(node->reg)){
2335 warn(TR("Unable to get configuration for %s."),
2336 region_name(node->reg));
2337 return FALSE;
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);
2344 *ret=t;
2346 return TRUE;
2350 static bool splitst_get_config(WSplitST *node, ExtlTab *ret)
2352 *ret=split_base_config((WSplit*)node);
2353 return TRUE;
2357 static bool splitsplit_get_config(WSplitSplit *node, ExtlTab *ret)
2359 ExtlTab tab, tltab, brtab;
2360 int tls, brs;
2362 if(!split_get_config(node->tl, &tltab))
2363 return split_get_config(node->br, ret);
2365 if(!split_get_config(node->br, &brtab)){
2366 *ret=tltab;
2367 return TRUE;
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);
2386 *ret=tab;
2388 return TRUE;
2392 bool split_get_config(WSplit *node, ExtlTab *tabret)
2394 bool ret=FALSE;
2395 CALL_DYN_RET(ret, bool, split_get_config, node, (node, tabret));
2396 return ret;
2400 /*}}}*/
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},
2409 END_DYNFUNTAB,
2412 static DynFunTab splitinner_dynfuntab[]={
2413 {splitinner_mark_current, splitinner_mark_current_default},
2414 {split_map, splitinner_map},
2415 {split_unmap, splitinner_unmap},
2416 END_DYNFUNTAB,
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},
2438 END_DYNFUNTAB,
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},
2453 END_DYNFUNTAB,
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},
2463 END_DYNFUNTAB,
2467 EXTL_EXPORT
2468 IMPLCLASS(WSplit, Obj, split_deinit, split_dynfuntab);
2470 EXTL_EXPORT
2471 IMPLCLASS(WSplitInner, WSplit, splitinner_deinit, splitinner_dynfuntab);
2473 EXTL_EXPORT
2474 IMPLCLASS(WSplitSplit, WSplitInner, splitsplit_deinit, splitsplit_dynfuntab);
2476 EXTL_EXPORT
2477 IMPLCLASS(WSplitRegion, WSplit, splitregion_deinit, splitregion_dynfuntab);
2479 EXTL_EXPORT
2480 IMPLCLASS(WSplitST, WSplitRegion, splitst_deinit, splitst_dynfuntab);
2483 /*}}}*/