Remove unused variables
[notion.git] / mod_tiling / split.c
bloba209e3a745e0a1b38dcd8ed4924bdc03d10c97bc
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 UNUSED(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 UNUSED(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 UNUSED(hprimn), WPrimn UNUSED(vprimn),
551 bool UNUSED(transpose))
553 node->geom=*ng;
557 static void splitregion_do_resize(WSplitRegion *node, const WRectangle *ng,
558 WPrimn UNUSED(hprimn), WPrimn UNUSED(vprimn),
559 bool UNUSED(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 int ori;
935 if(!OBJ_IS(ws, WTiling) || ((WTiling*)ws)->stdispnode==NULL)
936 return FALSE;
938 st=((WTiling*)ws)->stdispnode;
940 if(st->fullsize || !frame_neighbors_stdisp(frame, st))
941 return FALSE;
943 rstg=stdisp_recommended_geom(st, ws->geom);
945 if(is_same_dir(dir, st->orientation) &&
946 !geom_overlaps_stgeom_xy(*ng, st, rstg))
948 grow_by_stdisp_wh(ng, st);
949 if(is_maxed(frame, other_dir(dir)) &&
950 geom_aligned_stdisp(frame->saved_geom, st))
952 grow_by_stdisp_wh(&frame->saved_geom, st);
954 return TRUE;
957 if(!is_same_dir(dir, st->orientation) &&
958 geom_clashes_stdisp(frame->saved_geom, st))
960 stg=REGION_GEOM(st->regnode.reg);
961 ori=flip_orientation(st->orientation);
962 if(is_lt(ori, st->corner))
963 *xy(ng, ori)+=*wh(&stg, ori);
964 /* We've checked that this makes sense when verifying the saved layout. */
965 *wh(ng, ori)-=*wh(&stg, ori);
968 return FALSE;
971 bool splitregion_do_restore(WSplitRegion *node, int dir)
973 WFrame *frame;
974 WRectangle geom=((WSplit*)node)->geom;
975 WRectangle fakegeom;
976 bool ret;
977 bool other_max;
979 if(!OBJ_IS(node->reg, WFrame))
980 return FALSE;
982 frame=(WFrame*)node->reg;
983 if(dir==SPLIT_HORIZONTAL){
984 geom.x=frame->saved_geom.x;
985 geom.w=frame->saved_geom.w;
986 }else{
987 geom.y=frame->saved_geom.y;
988 geom.h=frame->saved_geom.h;
991 other_max=
992 dir==SPLIT_HORIZONTAL
993 ? frame->flags&FRAME_MAXED_VERT
994 : frame->flags&FRAME_MAXED_HORIZ;
996 fakegeom=geom;
997 ret=update_geom_from_stdisp(frame, &geom, dir);
999 /* Tell the region the correct geometry to avoid redrawing it again when
1000 * the stdisp is resized by split_regularise_stdisp. Some clients (notably
1001 * ncurses based ones) don't seem to react well to being resized multiple
1002 * times within a short amount of time and don't refresh themselves
1003 * correctly. */
1004 region_fit(node->reg, &geom, REGION_FIT_EXACT);
1006 split_update_bounds(&(node->split), FALSE);
1008 /* Keep the old geometry for the WSplit. Otherwise the tiling would be
1009 * inconsistent, by for example having horizontal WSplitSplit's whose
1010 * children have different heights. The call to split_regularise_stdisp
1011 * below will take care of correcting the geometry of the WSplit and it
1012 * behaves badly when the tiling is inconsistent. */
1013 node->split.geom=ret ? fakegeom : geom;
1015 frame->flags|=other_max;
1016 return ret;
1019 bool splitst_do_restore(WSplit *UNUSED(node), int UNUSED(dir))
1021 return FALSE;
1024 bool splitsplit_do_restore(WSplitSplit *node, int dir)
1026 bool ret1, ret2, ret=FALSE;
1027 WSplit *snode=(WSplit*)node;
1029 WSplitST *st;
1030 WSplit *other;
1031 WRectangle stg;
1032 WRectangle og;
1034 assert(node->tl!=NULL && node->br!=NULL);
1036 if(stdisp_immediate_child(node)){
1037 if(OBJ_IS(node->tl, WSplitST)){
1038 st=(WSplitST*)node->tl;
1039 other=node->br;
1040 }else{
1041 st=(WSplitST*)node->br;
1042 other=node->tl;
1044 stg=((WSplit*)st)->geom;
1045 split_do_restore(other, dir);
1046 og=other->geom;
1047 if(node->dir==SPLIT_HORIZONTAL){
1048 stg.y=og.y;
1049 stg.h=og.h;
1050 }else{
1051 stg.x=og.x;
1052 stg.w=og.w;
1054 if(rectangle_compare(&stg, &((WSplit*)st)->geom)){
1055 splitst_do_resize(st, &stg, PRIMN_ANY, PRIMN_ANY, FALSE);
1056 ret=TRUE;
1058 }else{
1059 /* Avoid short-circuit evaluation. */
1060 ret1=split_do_restore(node->tl, dir);
1061 ret2=split_do_restore(node->br, dir);
1062 ret=ret1 || ret2;
1065 snode->geom.x=node->tl->geom.x;
1066 snode->geom.y=node->tl->geom.y;
1067 if(node->dir==SPLIT_HORIZONTAL){
1068 snode->geom.w=node->tl->geom.w+node->br->geom.w;
1069 snode->geom.h=node->tl->geom.h;
1071 if(node->dir==SPLIT_VERTICAL){
1072 snode->geom.w=node->tl->geom.w;
1073 snode->geom.h=node->tl->geom.h+node->br->geom.h;
1076 return ret;
1079 bool split_do_restore(WSplit *node, int dir)
1081 bool ret = FALSE;
1082 CALL_DYN_RET(ret, bool, split_do_restore, node, (node, dir));
1083 return ret;
1087 void splitregion_do_maxhelper(WSplitRegion *node, int dir, int action)
1089 WFrame *frame;
1090 if(!OBJ_IS(node->reg, WFrame))
1091 return;
1092 frame=(WFrame*)node->reg;
1094 if(action==SAVE){
1095 frame->flags|=FRAME_KEEP_FLAGS;
1096 if(dir==HORIZONTAL){
1097 frame->flags|=(FRAME_MAXED_HORIZ|FRAME_SAVED_HORIZ);
1098 frame->saved_geom.x=REGION_GEOM(frame).x;
1099 frame->saved_geom.w=REGION_GEOM(frame).w;
1100 }else{
1101 frame->flags|=(FRAME_MAXED_VERT|FRAME_SAVED_VERT);
1102 frame->saved_geom.y=REGION_GEOM(frame).y;
1103 frame->saved_geom.h=REGION_GEOM(frame).h;
1106 if(action==SET_KEEP)
1107 frame->flags|=FRAME_KEEP_FLAGS;
1108 if(action==RM_KEEP)
1109 frame->flags&=~FRAME_KEEP_FLAGS;
1112 void splitst_do_maxhelper(WSplit *UNUSED(node), int UNUSED(dir), int UNUSED(action))
1114 return;
1117 void splitsplit_do_maxhelper(WSplitSplit *node, int dir, int action)
1119 assert(node->tl!=NULL && node->br!=NULL);
1120 split_do_maxhelper(node->tl, dir, action);
1121 split_do_maxhelper(node->br, dir, action);
1124 void split_do_maxhelper(WSplit *node, int dir, int action)
1126 CALL_DYN(split_do_maxhelper, node, (node, dir, action));
1130 bool savedgeom_clashes_stdisp(WFrame *frame, int dir)
1132 WRegion *ws=REGION_MANAGER(frame);
1133 WSplitST *st;
1134 int ori;
1136 if(!OBJ_IS(ws, WTiling) || ((WTiling*)ws)->stdispnode==NULL)
1137 return TRUE;
1139 st=((WTiling*)ws)->stdispnode;
1140 ori=flip_orientation(st->orientation);
1142 return
1143 !is_same_dir(dir, st->orientation) &&
1144 frame_neighbors_stdisp(frame, st) &&
1145 geom_clashes_stdisp(frame->saved_geom, st)
1146 ? *wh(&frame->saved_geom, ori)<*wh(&REGION_GEOM(st), ori)
1147 : FALSE;
1150 bool splitregion_do_verify(WSplitRegion *node, int dir)
1152 WFrame *frame;
1153 bool ret=FALSE;
1155 if(!OBJ_IS(node->reg, WFrame))
1156 return FALSE;
1158 frame=(WFrame*)node->reg;
1160 ret=is_maxed(frame, dir);
1162 if(dir==HORIZONTAL)
1163 frame->flags&=~(FRAME_MAXED_HORIZ|FRAME_SAVED_HORIZ);
1164 else
1165 frame->flags&=~(FRAME_MAXED_VERT|FRAME_SAVED_VERT);
1167 if(savedgeom_clashes_stdisp(frame, dir))
1168 return FALSE;
1170 return ret;
1173 bool splitst_do_verify(WSplit *UNUSED(node), int UNUSED(dir))
1175 return TRUE;
1178 bool splitsplit_do_verify(WSplitSplit *node, int dir)
1180 bool ret1, ret2;
1181 assert(node->tl!=NULL && node->br!=NULL);
1183 /* Avoid short-circuit evaluation. */
1184 ret1=split_do_verify(node->tl, dir);
1185 ret2=split_do_verify(node->br, dir);
1186 return ret1 && ret2;
1189 bool split_do_verify(WSplit *node, int dir)
1191 bool ret = FALSE;
1192 CALL_DYN_RET(ret, bool, split_do_verify, node, (node, dir));
1193 return ret;
1197 bool split_maximize(WSplit *node, int dir, int action)
1199 WSplit *p=maxparentdir(node, dir);
1200 if(action==RESTORE)
1201 return split_do_restore(p, dir);
1202 if(action==VERIFY)
1203 return split_do_verify(p, dir);
1205 split_do_maxhelper(p, dir, action);
1206 return TRUE;
1210 /*}}}*/
1213 /*{{{ Low-level resize code; request towards root */
1216 static void flexibility(WSplit *node, int dir, int *shrink, int *stretch)
1218 if(dir==SPLIT_VERTICAL){
1219 *shrink=maxof(0, node->geom.h-node->min_h);
1220 if(OBJ_IS(node, WSplitST))
1221 *stretch=maxof(0, node->max_h-node->geom.h);
1222 else
1223 *stretch=INT_MAX;
1224 }else{
1225 *shrink=maxof(0, node->geom.w-node->min_w);
1226 if(OBJ_IS(node, WSplitST))
1227 *stretch=maxof(0, node->max_w-node->geom.w);
1228 else
1229 *stretch=INT_MAX;
1234 static void calc_amount(int *amount, int rs, WSplit *other, int dir)
1236 int shrink, stretch;
1238 flexibility(other, dir, &shrink, &stretch);
1240 if(rs>0)
1241 *amount=minof(rs, shrink);
1242 else if(rs<0)
1243 *amount=-minof(-rs, stretch);
1244 else
1245 *amount=0;
1250 static void splitsplit_do_rqsize(WSplitSplit *p, WSplit *node,
1251 RootwardAmount *ha, RootwardAmount *va,
1252 WRectangle *rg, bool tryonly)
1254 WPrimn hprimn=PRIMN_ANY, vprimn=PRIMN_ANY;
1255 WRectangle og, pg, ng;
1256 RootwardAmount *ca;
1257 WSplit *other;
1258 WPrimn thisnode;
1259 int amount;
1261 assert(!ha->any || ha->tl==0);
1262 assert(!va->any || va->tl==0);
1263 assert(p->tl==node || p->br==node);
1265 if(p->tl==node){
1266 other=p->br;
1267 thisnode=PRIMN_TL;
1268 }else{
1269 other=p->tl;
1270 thisnode=PRIMN_BR;
1273 ca=(p->dir==SPLIT_VERTICAL ? va : ha);
1275 if(thisnode==PRIMN_TL || ca->any){
1276 calc_amount(&amount, ca->br, other, p->dir);
1277 ca->br-=amount;
1278 }else/*if(thisnode==PRIMN_BR)*/{
1279 calc_amount(&amount, ca->tl, other, p->dir);
1280 ca->tl-=amount;
1283 if(((WSplit*)p)->parent==NULL /*||
1284 (ha->tl==0 && ha->br==0 && va->tl==0 && va->br==0)*/){
1285 if(((WSplit*)p)->ws_if_root!=NULL)
1286 pg=REGION_GEOM((WTiling*)(((WSplit*)p)->ws_if_root));
1287 else
1288 pg=((WSplit*)p)->geom;
1289 }else{
1290 splitinner_do_rqsize(((WSplit*)p)->parent, (WSplit*)p, ha, va,
1291 &pg, tryonly);
1294 assert(pg.w>=0 && pg.h>=0);
1296 og=pg;
1297 ng=pg;
1299 if(p->dir==SPLIT_VERTICAL){
1300 ng.h=maxof(0, node->geom.h+amount);
1301 og.h=maxof(0, other->geom.h-amount);
1302 adjust_sizes(&(ng.h), &(og.h), pg.h, ng.h+og.h,
1303 node->min_h, other->min_h, node->max_h, other->max_h,
1304 PRIMN_TL /* node is passed as tl param */);
1305 if(thisnode==PRIMN_TL)
1306 og.y=pg.y+pg.h-og.h;
1307 else
1308 ng.y=pg.y+pg.h-ng.h;
1309 vprimn=thisnode;
1310 }else{
1311 ng.w=maxof(0, node->geom.w+amount);
1312 og.w=maxof(0, other->geom.w-amount);
1313 adjust_sizes(&(ng.w), &(og.w), pg.w, ng.w+og.w,
1314 node->min_w, other->min_w, node->max_w, other->max_w,
1315 PRIMN_TL /* node is passed as tl param */);
1316 if(thisnode==PRIMN_TL)
1317 og.x=pg.x+pg.w-og.w;
1318 else
1319 ng.x=pg.x+pg.w-ng.w;
1320 hprimn=thisnode;
1323 if(!tryonly){
1324 /* Entä jos 'other' on stdisp? */
1325 split_do_resize(other, &og, hprimn, vprimn, FALSE);
1327 ((WSplit*)p)->geom=pg;
1330 *rg=ng;
1334 void splitinner_do_rqsize(WSplitInner *p, WSplit *node,
1335 RootwardAmount *ha, RootwardAmount *va,
1336 WRectangle *rg, bool tryonly)
1338 CALL_DYN(splitinner_do_rqsize, p, (p, node, ha, va, rg, tryonly));
1342 static void initra(RootwardAmount *ra, int p, int s, int op, int os,
1343 bool any)
1345 ra->any=any;
1346 ra->tl=op-p;
1347 ra->br=(p+s)-(op+os);
1348 if(any){
1349 ra->br+=ra->tl;
1350 ra->tl=0;
1355 void split_do_rqgeom_(WSplit *node, const WRectangle *ng,
1356 bool hany, bool vany, WRectangle *rg,
1357 bool tryonly)
1359 RootwardAmount ha, va;
1361 if(node->parent==NULL){
1362 if(node->ws_if_root!=NULL)
1363 *rg=REGION_GEOM((WTiling*)(node->ws_if_root));
1364 else
1365 *rg=*ng;
1366 }else{
1367 initra(&ha, ng->x, ng->w, node->geom.x, node->geom.w, hany);
1368 initra(&va, ng->y, ng->h, node->geom.y, node->geom.h, vany);
1370 splitinner_do_rqsize(node->parent, node, &ha, &va, rg, tryonly);
1375 /*}}}*/
1378 /*{{{ Resize interface */
1381 static void bnd(int *pos, int *sz, int opos, int osz, int minsz, int maxsz)
1383 int ud=abs(*pos-opos);
1384 int dd=abs((*pos+*sz)-(opos+osz));
1385 int szrq=*sz;
1387 if(ud+dd!=0){
1388 bound(sz, minsz, maxsz);
1389 *pos+=(szrq-*sz)*ud/(ud+dd);
1394 WSplit *split_find_root(WSplit *split)
1396 if(split->parent==NULL)
1397 return split;
1398 return split_find_root((WSplit*)split->parent);
1402 void splittree_rqgeom(WSplit *sub, int flags, const WRectangle *geom_,
1403 WRectangle *geomret)
1405 bool hany=flags&REGION_RQGEOM_WEAK_X;
1406 bool vany=flags&REGION_RQGEOM_WEAK_Y;
1407 bool tryonly=flags&REGION_RQGEOM_TRYONLY;
1408 WRectangle geom=*geom_;
1409 WRectangle retg;
1410 WSplit *root=split_find_root(sub);
1412 if(geomret==NULL)
1413 geomret=&retg;
1415 split_update_bounds(root, TRUE);
1417 if(OBJ_IS(sub, WSplitST)){
1418 WSplitST *sub_as_stdisp=(WSplitST*)sub;
1420 if(flags&REGION_RQGEOM_TRYONLY){
1421 warn(TR("REGION_RQGEOM_TRYONLY unsupported for status display."));
1422 *geomret=sub->geom;
1423 return;
1425 split_regularise_stdisp(sub_as_stdisp);
1426 geom=sub->geom;
1427 if(sub_as_stdisp->orientation==REGION_ORIENTATION_HORIZONTAL){
1428 if(geom_->h==geom.h)
1429 return;
1430 geom.h=geom_->h;
1431 }else{
1432 if(geom_->w==geom.w)
1433 return;
1434 geom.w=geom_->w;
1436 split_update_bounds(root, TRUE);
1439 /* Handle internal size bounds */
1440 bnd(&(geom.x), &(geom.w), sub->geom.x, sub->geom.w,
1441 sub->min_w, sub->max_w);
1442 bnd(&(geom.y), &(geom.h), sub->geom.y, sub->geom.h,
1443 sub->min_h, sub->max_h);
1445 /* Check if we should resize to both tl and br */
1447 if(hany){
1448 geom.w+=sub->geom.x-geom.x;
1449 geom.x=sub->geom.x;
1452 if(vany){
1453 geom.h+=sub->geom.y-geom.y;
1454 geom.y=sub->geom.y;
1457 splittree_begin_resize();
1459 split_do_rqgeom_(sub, &geom, hany, vany, geomret, tryonly);
1461 if(!tryonly){
1462 split_do_resize(sub, geomret, hany, vany, FALSE);
1463 splittree_end_resize();
1464 *geomret=sub->geom;
1465 }else{
1466 saw_stdisp=NULL;
1471 /*EXTL_DOC
1472 * Attempt to resize and/or move the split tree starting at \var{node}.
1473 * Behaviour and the \var{g} parameter are as for \fnref{WRegion.rqgeom}
1474 * operating on \var{node} (if it were a \type{WRegion}).
1476 EXTL_EXPORT_MEMBER
1477 ExtlTab split_rqgeom(WSplit *node, ExtlTab g)
1479 WRectangle geom, ogeom;
1480 int flags=REGION_RQGEOM_WEAK_ALL;
1482 geom=node->geom;
1483 ogeom=geom;
1485 if(extl_table_gets_i(g, "x", &(geom.x)))
1486 flags&=~REGION_RQGEOM_WEAK_X;
1487 if(extl_table_gets_i(g, "y", &(geom.y)))
1488 flags&=~REGION_RQGEOM_WEAK_Y;
1489 if(extl_table_gets_i(g, "w", &(geom.w)))
1490 flags&=~REGION_RQGEOM_WEAK_W;
1491 if(extl_table_gets_i(g, "h", &(geom.h)))
1492 flags&=~REGION_RQGEOM_WEAK_H;
1494 geom.w=maxof(1, geom.w);
1495 geom.h=maxof(1, geom.h);
1497 splittree_rqgeom(node, flags, &geom, &ogeom);
1499 return extl_table_from_rectangle(&ogeom);
1503 /*}}}*/
1506 /*{{{ Split */
1509 void splittree_changeroot(WSplit *root, WSplit *node)
1511 WTiling *ws=(WTiling*)(root->ws_if_root);
1513 assert(ws!=NULL);
1514 assert(ws->split_tree==root);
1515 root->ws_if_root=NULL;
1516 ws->split_tree=node;
1517 if(node!=NULL){
1518 node->ws_if_root=ws;
1519 node->parent=NULL;
1524 static void splitsplit_replace(WSplitSplit *split, WSplit *child,
1525 WSplit *what)
1527 assert(split->tl==child || split->br==child);
1529 if(split->tl==child)
1530 split->tl=what;
1531 else
1532 split->br=what;
1534 child->parent=NULL;
1536 what->parent=(WSplitInner*)split;
1537 what->ws_if_root=NULL; /* May not be needed. */
1541 void splitinner_replace(WSplitInner *split, WSplit *child, WSplit *what)
1543 CALL_DYN(splitinner_replace, split, (split, child, what));
1547 WSplitRegion *splittree_split(WSplit *node, int dir, WPrimn primn,
1548 int minsize, WRegionSimpleCreateFn *fn,
1549 WWindow *parent)
1551 int objmin;
1552 int s, sn, so;
1553 WSplitSplit *nsplit;
1554 WSplitRegion *nnode;
1555 WSplitInner *psplit;
1556 WRegion *nreg;
1557 WFitParams fp;
1558 WRectangle ng, rg;
1560 assert(node!=NULL && parent!=NULL);
1562 splittree_begin_resize();
1564 node=dodge_stdisp(node, FALSE);
1566 if(node==NULL)
1567 return NULL;
1569 if(OBJ_IS(node, WSplitST)){
1570 warn(TR("Splitting the status display is not allowed."));
1571 return NULL;
1574 if(primn!=PRIMN_TL && primn!=PRIMN_BR)
1575 primn=PRIMN_BR;
1576 if(dir!=SPLIT_HORIZONTAL && dir!=SPLIT_VERTICAL)
1577 dir=SPLIT_VERTICAL;
1579 split_update_bounds(split_find_root(node), TRUE);
1580 objmin=(dir==SPLIT_VERTICAL ? node->min_h : node->min_w);
1582 s=split_size(node, dir);
1583 sn=maxof(minsize, s/2);
1584 so=maxof(objmin, s-sn);
1586 if(sn+so!=s){
1587 int rs;
1588 ng=node->geom;
1589 if(dir==SPLIT_VERTICAL)
1590 ng.h=sn+so;
1591 else
1592 ng.w=sn+so;
1593 split_do_rqgeom_(node, &ng, TRUE, TRUE, &rg, TRUE);
1594 rs=(dir==SPLIT_VERTICAL ? rg.h : rg.w);
1595 if(rs<minsize+objmin){
1596 warn(TR("Unable to split: not enough free space."));
1597 return NULL;
1599 split_do_rqgeom_(node, &ng, TRUE, TRUE, &rg, FALSE);
1600 rs=(dir==SPLIT_VERTICAL ? rg.h : rg.w);
1601 if(minsize>rs/2){
1602 sn=minsize;
1603 so=rs-sn;
1604 }else{
1605 so=maxof(rs/2, objmin);
1606 sn=rs-so;
1608 }else{
1609 rg=node->geom;
1610 splittree_scan_stdisp_rootward(node);
1613 /* Create split and new window
1615 fp.mode=REGION_FIT_EXACT;
1616 fp.g=rg;
1618 nsplit=create_splitsplit(&(fp.g), dir);
1620 if(nsplit==NULL)
1621 return NULL;
1623 if(dir==SPLIT_VERTICAL){
1624 if(primn==PRIMN_BR)
1625 fp.g.y+=so;
1626 fp.g.h=sn;
1627 }else{
1628 if(primn==PRIMN_BR)
1629 fp.g.x+=so;
1630 fp.g.w=sn;
1633 nreg=fn(parent, &fp);
1635 if(nreg==NULL){
1636 destroy_obj((Obj*)nsplit);
1637 return NULL;
1640 nnode=create_splitregion(&(fp.g), nreg);
1641 if(nnode==NULL){
1642 destroy_obj((Obj*)nreg);
1643 destroy_obj((Obj*)nsplit);
1644 return NULL;
1647 /* Now that everything's ok, resize and move original node.
1649 ng=rg;
1650 if(dir==SPLIT_VERTICAL){
1651 ng.h=so;
1652 if(primn==PRIMN_TL)
1653 ng.y+=sn;
1654 }else{
1655 ng.w=so;
1656 if(primn==PRIMN_TL)
1657 ng.x+=sn;
1660 split_do_resize(node, &ng,
1661 (dir==SPLIT_HORIZONTAL ? primn : PRIMN_ANY),
1662 (dir==SPLIT_VERTICAL ? primn : PRIMN_ANY),
1663 FALSE);
1665 /* Set up split structure
1667 psplit=node->parent;
1669 if(psplit!=NULL)
1670 splitinner_replace(psplit, node, (WSplit*)nsplit);
1671 else
1672 splittree_changeroot(node, (WSplit*)nsplit);
1674 node->parent=(WSplitInner*)nsplit;
1675 ((WSplit*)nnode)->parent=(WSplitInner*)nsplit;
1677 if(primn==PRIMN_BR){
1678 nsplit->tl=node;
1679 nsplit->br=(WSplit*)nnode;
1680 nsplit->current=SPLIT_CURRENT_TL;
1681 }else{
1682 nsplit->tl=(WSplit*)nnode;
1683 nsplit->br=node;
1684 nsplit->current=SPLIT_CURRENT_BR;
1687 splittree_end_resize();
1689 return nnode;
1693 /*}}}*/
1696 /*{{{ Remove */
1699 static void splitsplit_remove(WSplitSplit *node, WSplit *child,
1700 bool reclaim_space)
1702 static int nstdisp=0;
1703 WSplitInner *parent;
1704 WSplit *other;
1705 int hprimn=PRIMN_ANY, vprimn=PRIMN_ANY;
1707 assert(node->tl==child || node->br==child);
1709 if(node->tl==child){
1710 other=node->br;
1711 if(node->dir==SPLIT_VERTICAL)
1712 vprimn=PRIMN_TL;
1713 else
1714 hprimn=PRIMN_TL;
1715 }else{
1716 other=node->tl;
1717 if(node->dir==SPLIT_VERTICAL)
1718 vprimn=PRIMN_BR;
1719 else
1720 hprimn=PRIMN_BR;
1723 assert(other!=NULL);
1725 if(nstdisp==0 && reclaim_space && OBJ_IS(other, WSplitST)){
1726 /* Try to move stdisp out of the way. */
1727 split_try_unsink_stdisp(node, FALSE, TRUE);
1728 assert(child->parent!=NULL);
1729 nstdisp++;
1730 splitinner_remove(child->parent, child, reclaim_space);
1731 nstdisp--;
1732 return;
1735 parent=((WSplit*)node)->parent;
1737 if(parent!=NULL)
1738 splitinner_replace(parent, (WSplit*)node, other);
1739 else
1740 splittree_changeroot((WSplit*)node, other);
1742 if(reclaim_space)
1743 split_resize(other, &(((WSplit*)node)->geom), hprimn, vprimn);
1745 child->parent=NULL;
1747 node->tl=NULL;
1748 node->br=NULL;
1749 ((WSplit*)node)->parent=NULL;
1750 destroy_obj((Obj*)node);
1754 void splitinner_remove(WSplitInner *node, WSplit *child, bool reclaim_space)
1756 CALL_DYN(splitinner_remove, node, (node, child, reclaim_space));
1760 void splittree_remove(WSplit *node, bool reclaim_space)
1762 if(node->parent!=NULL)
1763 splitinner_remove(node->parent, node, reclaim_space);
1764 else if(node->ws_if_root!=NULL)
1765 splittree_changeroot(node, NULL);
1767 destroy_obj((Obj*)node);
1771 /*}}}*/
1774 /*{{{ Tree traversal */
1777 static bool defaultfilter(WSplit *node)
1779 return (OBJ_IS(node, WSplitRegion) &&
1780 ((WSplitRegion*)node)->reg!=NULL);
1784 static WSplit *split_current_todir_default(WSplit *node,
1785 WPrimn UNUSED(hprimn), WPrimn UNUSED(vprimn),
1786 WSplitFilter *filter)
1788 if(filter==NULL)
1789 filter=defaultfilter;
1791 return (filter(node) ? node : NULL);
1795 static WSplit *splitsplit_current_todir(WSplitSplit *node,
1796 WPrimn hprimn, WPrimn vprimn,
1797 WSplitFilter *filter)
1799 WPrimn primn=(node->dir==SPLIT_HORIZONTAL ? hprimn : vprimn);
1800 WSplit *first, *second, *ret;
1802 if(primn==PRIMN_TL ||
1803 (primn==PRIMN_ANY && node->current==SPLIT_CURRENT_TL)){
1804 first=node->tl;
1805 second=node->br;
1806 }else if(primn==PRIMN_BR ||
1807 (primn==PRIMN_ANY && node->current==SPLIT_CURRENT_BR)){
1808 first=node->br;
1809 second=node->tl;
1810 }else{
1811 return NULL;
1814 ret=split_current_todir(first, hprimn, vprimn, filter);
1815 if(ret==NULL)
1816 ret=split_current_todir(second, hprimn, vprimn, filter);
1817 if(ret==NULL && filter!=NULL){
1818 if(filter((WSplit*)node))
1819 ret=(WSplit*)node;
1822 return ret;
1826 WSplit *split_current_todir(WSplit *node, WPrimn hprimn, WPrimn vprimn,
1827 WSplitFilter *filter)
1829 WSplit *ret=NULL;
1830 CALL_DYN_RET(ret, WSplit*, split_current_todir, node,
1831 (node, hprimn, vprimn, filter));
1832 return ret;
1836 /* Note: both hprimn and vprimn are inverted when descending. Therefore
1837 * one should be either PRIMN_NONE or PRIMN_ANY for sensible geometric
1838 * navigation. (Both are PRIMN_TL or PRIMN_BR for pseudo-linear
1839 * next/previous navigation.)
1841 WSplit *splitsplit_nextto(WSplitSplit *node, WSplit *child,
1842 WPrimn hprimn, WPrimn vprimn,
1843 WSplitFilter *filter)
1845 WPrimn primn=(node->dir==SPLIT_HORIZONTAL ? hprimn : vprimn);
1846 WSplit *split=NULL, *nnode=NULL;
1848 if(node->tl==child && (primn==PRIMN_BR || primn==PRIMN_ANY))
1849 split=node->br;
1850 else if(node->br==child && (primn==PRIMN_TL || primn==PRIMN_ANY))
1851 split=node->tl;
1853 if(split!=NULL){
1854 nnode=split_current_todir(split,
1855 primn_none2any(primn_invert(hprimn)),
1856 primn_none2any(primn_invert(vprimn)),
1857 filter);
1860 if(nnode==NULL)
1861 nnode=split_nextto((WSplit*)node, hprimn, vprimn, filter);
1863 return nnode;
1867 WSplit *splitinner_nextto(WSplitInner *node, WSplit *child,
1868 WPrimn hprimn, WPrimn vprimn,
1869 WSplitFilter *filter)
1871 WSplit *ret=NULL;
1872 CALL_DYN_RET(ret, WSplit*, splitinner_nextto, node,
1873 (node, child, hprimn, vprimn, filter));
1874 return ret;
1878 WSplit *split_nextto(WSplit *node, WPrimn hprimn, WPrimn vprimn,
1879 WSplitFilter *filter)
1881 while(node->parent!=NULL){
1882 WSplit *ret=splitinner_nextto(node->parent, node,
1883 hprimn, vprimn, filter);
1884 if(ret!=NULL)
1885 return ret;
1886 node=(WSplit*)node->parent;
1888 return NULL;
1892 void splitinner_mark_current_default(WSplitInner *split, WSplit *UNUSED(child))
1894 if(((WSplit*)split)->parent!=NULL)
1895 splitinner_mark_current(((WSplit*)split)->parent, (WSplit*)split);
1899 void splitsplit_mark_current(WSplitSplit *split, WSplit *child)
1901 assert(child==split->tl || child==split->br);
1903 split->current=(split->tl==child ? SPLIT_CURRENT_TL : SPLIT_CURRENT_BR);
1905 splitinner_mark_current_default(&(split->isplit), child);
1909 void splitinner_mark_current(WSplitInner *split, WSplit *child)
1911 CALL_DYN(splitinner_mark_current, split, (split, child));
1915 static void splitsplit_forall(WSplitSplit *node, WSplitFn *fn)
1917 fn(node->tl);
1918 fn(node->br);
1922 void splitinner_forall(WSplitInner *node, WSplitFn *fn)
1924 CALL_DYN(splitinner_forall, node, (node, fn));
1928 static WSplit *splitsplit_current(WSplitSplit *split)
1930 return (split->current==SPLIT_CURRENT_TL ? split->tl : split->br);
1934 /*EXTL_DOC
1935 * Returns the most previously active child node of \var{split}.
1937 EXTL_SAFE
1938 EXTL_EXPORT_MEMBER
1939 WSplit *splitinner_current(WSplitInner *node)
1941 WSplit *ret=NULL;
1942 CALL_DYN_RET(ret, WSplit*, splitinner_current, node, (node));
1943 return ret;
1947 /*}}}*/
1950 /*{{{ X window handling */
1953 static void splitregion_stacking(WSplitRegion *split,
1954 Window *bottomret, Window *topret)
1956 *bottomret=None;
1957 *topret=None;
1958 if(split->reg!=NULL)
1959 region_stacking(split->reg, bottomret, topret);
1963 void splitsplit_stacking(WSplitSplit *split,
1964 Window *bottomret, Window *topret)
1966 Window tlb=None, tlt=None;
1967 Window brb=None, brt=None;
1969 split_stacking(split->tl, &tlb, &tlt);
1970 split_stacking(split->br, &brb, &brt);
1972 /* To make sure that this condition holds is left to the workspace
1973 * code to do after a split tree has been loaded or modified.
1975 if(split->current==SPLIT_CURRENT_TL){
1976 *topret=(tlt!=None ? tlt : brt);
1977 *bottomret=(brb!=None ? brb : tlb);
1978 }else{
1979 *topret=(brt!=None ? brt : tlt);
1980 *bottomret=(tlb!=None ? tlb : brb);
1984 void split_stacking(WSplit *split, Window *bottomret, Window *topret)
1986 *bottomret=None;
1987 *topret=None;
1989 CALL_DYN(split_stacking, split, (split, bottomret, topret));
1994 static void splitregion_restack(WSplitRegion *split, Window other, int mode)
1996 if(split->reg!=NULL)
1997 region_restack(split->reg, other, mode);
2000 void splitsplit_restack(WSplitSplit *split, Window other, int mode)
2002 Window bottom=None, top=None;
2003 WSplit *first, *second;
2005 if(split->current==SPLIT_CURRENT_TL){
2006 first=split->br;
2007 second=split->tl;
2008 }else{
2009 first=split->tl;
2010 second=split->br;
2013 split_restack(first, other, mode);
2014 split_stacking(first, &bottom, &top);
2015 if(top!=None){
2016 other=top;
2017 mode=Above;
2019 split_restack(second, other, mode);
2022 void split_restack(WSplit *split, Window other, int mode)
2024 CALL_DYN(split_restack, split, (split, other, mode));
2028 static void splitregion_map(WSplitRegion *split)
2030 if(split->reg!=NULL)
2031 region_map(split->reg);
2034 static void splitinner_map(WSplitInner *split)
2036 splitinner_forall(split, split_map);
2039 void split_map(WSplit *split)
2041 CALL_DYN(split_map, split, (split));
2045 static void splitregion_unmap(WSplitRegion *split)
2047 if(split->reg!=NULL)
2048 region_unmap(split->reg);
2051 static void splitinner_unmap(WSplitInner *split)
2053 splitinner_forall(split, split_unmap);
2056 void split_unmap(WSplit *split)
2058 CALL_DYN(split_unmap, split, (split));
2062 static void splitregion_reparent(WSplitRegion *split, WWindow *wwin)
2064 if(split->reg!=NULL){
2065 WRectangle g=split->split.geom;
2066 region_reparent(split->reg, wwin, &g, REGION_FIT_EXACT);
2071 static void splitsplit_reparent(WSplitSplit *split, WWindow *wwin)
2073 if(split->current==SPLIT_CURRENT_TL){
2074 split_reparent(split->br, wwin);
2075 split_reparent(split->tl, wwin);
2076 }else{
2077 split_reparent(split->tl, wwin);
2078 split_reparent(split->br, wwin);
2083 void split_reparent(WSplit *split, WWindow *wwin)
2085 CALL_DYN(split_reparent, split, (split, wwin));
2089 /*}}}*/
2092 /*{{{ Transpose, flip, rotate */
2095 void splitsplit_flip_default(WSplitSplit *split)
2097 WRectangle tlng, brng;
2098 WRectangle *sg=&((WSplit*)split)->geom;
2099 WSplit *tmp;
2101 assert(split->tl!=NULL && split->br!=NULL);
2103 split_update_bounds((WSplit*)split, TRUE);
2105 tlng=split->tl->geom;
2106 brng=split->br->geom;
2108 if(split->dir==SPLIT_HORIZONTAL){
2109 brng.x=sg->x;
2110 tlng.x=sg->x+sg->w-tlng.w;
2111 }else{
2112 brng.y=sg->y;
2113 tlng.y=sg->y+sg->h-tlng.h;
2116 tmp=split->tl;
2117 split->tl=split->br;
2118 split->br=tmp;
2119 split->current=(split->current==SPLIT_CURRENT_TL
2120 ? SPLIT_CURRENT_BR
2121 : SPLIT_CURRENT_TL);
2123 split_do_resize(split->tl, &brng, PRIMN_ANY, PRIMN_ANY, FALSE);
2124 split_do_resize(split->br, &tlng, PRIMN_ANY, PRIMN_ANY, FALSE);
2128 static void splitsplit_flip_(WSplitSplit *split)
2130 CALL_DYN(splitsplit_flip, split, (split));
2134 /*EXTL_DOC
2135 * Flip contents of \var{split}.
2137 EXTL_EXPORT_MEMBER
2138 void splitsplit_flip(WSplitSplit *split)
2140 splittree_begin_resize();
2142 split=OBJ_CAST(dodge_stdisp((WSplit*)split, FALSE), WSplitSplit);
2144 if(split==NULL)
2145 return;
2147 splitsplit_flip_(split);
2149 splittree_end_resize();
2152 typedef enum{
2153 FLIP_VERTICAL,
2154 FLIP_HORIZONTAL,
2155 FLIP_NONE,
2156 FLIP_ANY
2157 } FlipDir;
2160 static FlipDir flipdir=FLIP_VERTICAL;
2163 static void do_flip(WSplit *split)
2165 WSplitSplit *ss=OBJ_CAST(split, WSplitSplit);
2167 if(ss!=NULL){
2168 if((flipdir==FLIP_ANY
2169 || (ss->dir==SPLIT_VERTICAL && flipdir==FLIP_VERTICAL)
2170 || (ss->dir==SPLIT_HORIZONTAL && flipdir==FLIP_HORIZONTAL))
2171 && !OBJ_IS(ss->tl, WSplitST)
2172 && !OBJ_IS(ss->br, WSplitST)){
2173 splitsplit_flip_(ss);
2177 if(OBJ_IS(ss, WSplitInner))
2178 splitinner_forall((WSplitInner*)ss, do_flip);
2182 static void splittree_flip_dir(WSplit *splittree, FlipDir dir)
2184 /* todo stdisp outta way */
2185 if(OBJ_IS(splittree, WSplitInner)){
2186 flipdir=dir;
2187 splitinner_forall((WSplitInner*)splittree, do_flip);
2192 static bool split_fliptrans_to(WSplit *node, const WRectangle *geom,
2193 bool trans, FlipDir flip)
2195 WRectangle rg;
2196 WSplit *node2;
2198 splittree_begin_resize();
2200 /* split_do_resize can do things right if 'node' has stdisp as child,
2201 * but otherwise transpose will put the stdisp in a bad split
2202 * configuration if it is contained within 'node', so we must
2203 * first move it and its fixed parent split below node. For correct
2204 * geometry calculation we move it immediately below node, and
2205 * resize stdisp's fixed parent node instead.
2207 node2=dodge_stdisp(node, TRUE);
2209 if(node==NULL || node2!=node)
2210 return FALSE;
2212 split_update_bounds(node, TRUE);
2214 split_do_rqgeom_(node, geom, PRIMN_ANY, PRIMN_ANY, &rg, FALSE);
2216 split_do_resize(node, &rg, PRIMN_ANY, PRIMN_ANY, trans);
2218 if(flip!=FLIP_NONE)
2219 splittree_flip_dir(node, flip);
2221 splittree_end_resize();
2223 return TRUE;
2227 bool split_transpose_to(WSplit *node, const WRectangle *geom)
2229 return split_fliptrans_to(node, geom, TRUE, FLIP_ANY);
2233 /*EXTL_DOC
2234 * Transpose contents of \var{node}.
2236 EXTL_EXPORT_MEMBER
2237 void split_transpose(WSplit *node)
2239 WRectangle g=node->geom;
2241 split_transpose_to(node, &g);
2245 bool split_rotate_to(WSplit *node, const WRectangle *geom, int rotation)
2247 FlipDir flip=FLIP_NONE;
2248 bool trans=FALSE;
2250 if(rotation==SCREEN_ROTATION_90){
2251 flip=FLIP_HORIZONTAL;
2252 trans=TRUE;
2253 }else if(rotation==SCREEN_ROTATION_180){
2254 flip=FLIP_ANY;
2255 }else if(rotation==SCREEN_ROTATION_270){
2256 flip=FLIP_VERTICAL;
2257 trans=TRUE;
2260 return split_fliptrans_to(node, geom, trans, flip);
2263 /*}}}*/
2266 /*{{{ Exports */
2269 /*EXTL_DOC
2270 * Return parent split for \var{split}.
2272 EXTL_SAFE
2273 EXTL_EXPORT_MEMBER
2274 WSplitInner *split_parent(WSplit *split)
2276 return split->parent;
2280 /*EXTL_DOC
2281 * Returns the area of workspace used by the regions under \var{split}.
2283 EXTL_SAFE
2284 EXTL_EXPORT_MEMBER
2285 ExtlTab split_geom(WSplit *split)
2287 return extl_table_from_rectangle(&(split->geom));
2291 /*EXTL_DOC
2292 * Returns the top or left child node of \var{split} depending
2293 * on the direction of the split.
2295 EXTL_SAFE
2296 EXTL_EXPORT_MEMBER
2297 WSplit *splitsplit_tl(WSplitSplit *split)
2299 return split->tl;
2303 /*EXTL_DOC
2304 * Returns the bottom or right child node of \var{split} depending
2305 * on the direction of the split.
2307 EXTL_SAFE
2308 EXTL_EXPORT_MEMBER
2309 WSplit *splitsplit_br(WSplitSplit *split)
2311 return split->br;
2314 /*EXTL_DOC
2315 * Returns the direction of \var{split}; either \codestr{vertical} or
2316 * \codestr{horizontal}.
2318 EXTL_SAFE
2319 EXTL_EXPORT_MEMBER
2320 const char *splitsplit_dir(WSplitSplit *split)
2322 return (split->dir==SPLIT_VERTICAL ? "vertical" : "horizontal");
2326 /*EXTL_DOC
2327 * Returns the region contained in \var{node}.
2329 EXTL_SAFE
2330 EXTL_EXPORT_MEMBER
2331 WRegion *splitregion_reg(WSplitRegion *node)
2333 return node->reg;
2337 /*}}}*/
2340 /*{{{ Save support */
2343 ExtlTab split_base_config(WSplit *node)
2345 ExtlTab t=extl_create_table();
2346 extl_table_sets_s(t, "type", OBJ_TYPESTR(node));
2347 return t;
2351 static bool splitregion_get_config(WSplitRegion *node, ExtlTab *ret)
2353 ExtlTab rt, t;
2355 if(node->reg==NULL)
2356 return FALSE;
2358 if(!region_supports_save(node->reg)){
2359 warn(TR("Unable to get configuration for %s."),
2360 region_name(node->reg));
2361 return FALSE;
2364 rt=region_get_configuration(node->reg);
2365 t=split_base_config(&(node->split));
2366 extl_table_sets_t(t, "regparams", rt);
2367 extl_unref_table(rt);
2368 *ret=t;
2370 return TRUE;
2374 static bool splitst_get_config(WSplitST *node, ExtlTab *ret)
2376 *ret=split_base_config((WSplit*)node);
2377 return TRUE;
2381 static bool splitsplit_get_config(WSplitSplit *node, ExtlTab *ret)
2383 ExtlTab tab, tltab, brtab;
2384 int tls, brs;
2386 if(!split_get_config(node->tl, &tltab))
2387 return split_get_config(node->br, ret);
2389 if(!split_get_config(node->br, &brtab)){
2390 *ret=tltab;
2391 return TRUE;
2394 tab=split_base_config((WSplit*)node);
2396 tls=split_size(node->tl, node->dir);
2397 brs=split_size(node->br, node->dir);
2399 extl_table_sets_s(tab, "dir", (node->dir==SPLIT_VERTICAL
2400 ? "vertical" : "horizontal"));
2402 extl_table_sets_i(tab, "tls", tls);
2403 extl_table_sets_t(tab, "tl", tltab);
2404 extl_unref_table(tltab);
2406 extl_table_sets_i(tab, "brs", brs);
2407 extl_table_sets_t(tab, "br", brtab);
2408 extl_unref_table(brtab);
2410 *ret=tab;
2412 return TRUE;
2416 bool split_get_config(WSplit *node, ExtlTab *tabret)
2418 bool ret=FALSE;
2419 CALL_DYN_RET(ret, bool, split_get_config, node, (node, tabret));
2420 return ret;
2424 /*}}}*/
2427 /*{{{ The classes */
2430 static DynFunTab split_dynfuntab[]={
2431 {split_do_resize, split_do_resize_default},
2432 {(DynFun*)split_current_todir, (DynFun*)split_current_todir_default},
2433 END_DYNFUNTAB,
2436 static DynFunTab splitinner_dynfuntab[]={
2437 {splitinner_mark_current, splitinner_mark_current_default},
2438 {split_map, splitinner_map},
2439 {split_unmap, splitinner_unmap},
2440 END_DYNFUNTAB,
2443 static DynFunTab splitsplit_dynfuntab[]={
2444 {split_update_bounds, splitsplit_update_bounds},
2445 {split_do_resize, splitsplit_do_resize},
2446 {split_do_maxhelper, splitsplit_do_maxhelper},
2447 {(DynFun*)split_do_restore, (DynFun*)splitsplit_do_restore},
2448 {(DynFun*)split_do_verify, (DynFun*)splitsplit_do_verify},
2449 {splitinner_do_rqsize, splitsplit_do_rqsize},
2450 {splitinner_replace, splitsplit_replace},
2451 {splitinner_remove, splitsplit_remove},
2452 {(DynFun*)split_current_todir, (DynFun*)splitsplit_current_todir},
2453 {(DynFun*)splitinner_current, (DynFun*)splitsplit_current},
2454 {(DynFun*)splitinner_nextto, (DynFun*)splitsplit_nextto},
2455 {splitinner_mark_current, splitsplit_mark_current},
2456 {(DynFun*)split_get_config, (DynFun*)splitsplit_get_config},
2457 {splitinner_forall, splitsplit_forall},
2458 {split_restack, splitsplit_restack},
2459 {split_stacking, splitsplit_stacking},
2460 {split_reparent, splitsplit_reparent},
2461 {splitsplit_flip, splitsplit_flip_default},
2462 END_DYNFUNTAB,
2465 static DynFunTab splitregion_dynfuntab[]={
2466 {split_update_bounds, splitregion_update_bounds},
2467 {split_do_resize, splitregion_do_resize},
2468 {split_do_maxhelper, splitregion_do_maxhelper},
2469 {(DynFun*)split_do_restore, (DynFun*)splitregion_do_restore},
2470 {(DynFun*)split_do_verify, (DynFun*)splitregion_do_verify},
2471 {(DynFun*)split_get_config, (DynFun*)splitregion_get_config},
2472 {split_map, splitregion_map},
2473 {split_unmap, splitregion_unmap},
2474 {split_restack, splitregion_restack},
2475 {split_stacking, splitregion_stacking},
2476 {split_reparent, splitregion_reparent},
2477 END_DYNFUNTAB,
2480 static DynFunTab splitst_dynfuntab[]={
2481 {split_update_bounds, splitst_update_bounds},
2482 {split_do_resize, splitst_do_resize},
2483 {split_do_maxhelper, splitst_do_maxhelper},
2484 {(DynFun*)split_do_restore, (DynFun*)splitst_do_restore},
2485 {(DynFun*)split_do_verify, (DynFun*)splitst_do_verify},
2486 {(DynFun*)split_get_config, (DynFun*)splitst_get_config},
2487 END_DYNFUNTAB,
2491 EXTL_EXPORT
2492 IMPLCLASS(WSplit, Obj, split_deinit, split_dynfuntab);
2494 EXTL_EXPORT
2495 IMPLCLASS(WSplitInner, WSplit, splitinner_deinit, splitinner_dynfuntab);
2497 EXTL_EXPORT
2498 IMPLCLASS(WSplitSplit, WSplitInner, splitsplit_deinit, splitsplit_dynfuntab);
2500 EXTL_EXPORT
2501 IMPLCLASS(WSplitRegion, WSplit, splitregion_deinit, splitregion_dynfuntab);
2503 EXTL_EXPORT
2504 IMPLCLASS(WSplitST, WSplitRegion, splitst_deinit, splitst_dynfuntab);
2507 /*}}}*/