Slightly better logging for font loading
[notion/jeffpc.git] / mod_tiling / split.c
blobccfcf54bef8058232be189cf6cfa128f5ed615ef
1 /*
2 * ion/mod_tiling/split.c
4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
7 */
9 #include <limits.h>
10 #include <string.h>
11 #include <X11/Xmd.h>
13 #include <libtu/minmax.h>
14 #include <libtu/rb.h>
15 #include <libtu/objp.h>
16 #include <ioncore/common.h>
17 #include <ioncore/focus.h>
18 #include <ioncore/global.h>
19 #include <ioncore/window.h>
20 #include <ioncore/resize.h>
21 #include <ioncore/attach.h>
22 #include <ioncore/manage.h>
23 #include <ioncore/extlconv.h>
24 #include <ioncore/rectangle.h>
25 #include <ioncore/saveload.h>
26 #include <ioncore/names.h>
27 #include "tiling.h"
28 #include "split.h"
29 #include "split-stdisp.h"
32 static Rb_node split_of_map=NULL;
35 /*{{{ Geometry helper functions */
38 int split_size(WSplit *split, int dir)
40 return (dir==SPLIT_HORIZONTAL ? split->geom.w : split->geom.h);
43 int split_other_size(WSplit *split, int dir)
45 return (dir==SPLIT_VERTICAL ? split->geom.w : split->geom.h);
48 int split_pos(WSplit *split, int dir)
50 return (dir==SPLIT_HORIZONTAL ? split->geom.x : split->geom.y);
53 int split_other_pos(WSplit *split, int dir)
55 return (dir==SPLIT_VERTICAL ? split->geom.x : split->geom.y);
59 static int reg_calcresize(WRegion *reg, int dir, int nsize)
61 int tmp;
63 if(dir==SPLIT_HORIZONTAL)
64 tmp=region_min_w(reg);
65 else
66 tmp=region_min_h(reg);
68 return (nsize<tmp ? tmp : nsize);
72 /* No, these are not even supposed to be proper/consistent
73 * Z \cup {\infty, -\infty} calculation rules.
76 static int infadd(int x, int y)
78 if(x==INT_MAX || y==INT_MAX)
79 return INT_MAX;
80 else
81 return x+y;
85 static int infsub(int x, int y)
87 if(x==INT_MAX)
88 return INT_MAX;
89 else if(y==INT_MAX)
90 return 0;
91 else
92 return x-y;
96 /* Negative "unused space" means no SPLIT_UNUSED under a node, while
97 * zero unused space means there's a zero-sized SPLIT_UNUSED under the
98 * node.
100 static int unusedadd(int x, int y)
102 if(x<0 && y<0)
103 return -1;
104 return maxof(x, 0)+maxof(y, 0);
108 static void bound(int *what, int min, int max)
110 if(*what<min)
111 *what=min;
112 else if(*what>max)
113 *what=max;
117 /*}}}*/
120 /*{{{ Functions to get and set a region's containing node */
123 #define node_of_reg splittree_node_of
125 WSplitRegion *splittree_node_of(WRegion *reg)
127 Rb_node node=NULL;
128 int found=0;
130 /*assert(REGION_MANAGER_CHK(reg, WTiling)!=NULL);*/
132 if(split_of_map!=NULL){
133 node=rb_find_pkey_n(split_of_map, reg, &found);
134 if(found)
135 return (WSplitRegion*)(node->v.val);
138 return NULL;
142 #define set_node_of_reg splittree_set_node_of
145 bool splittree_set_node_of(WRegion *reg, WSplitRegion *split)
147 Rb_node node=NULL;
148 int found;
150 /*assert(REGION_MANAGER_CHK(reg, WTiling)!=NULL);*/
152 if(split_of_map==NULL){
153 if(split==NULL)
154 return TRUE;
155 split_of_map=make_rb();
156 if(split_of_map==NULL)
157 return FALSE;
160 node=rb_find_pkey_n(split_of_map, reg, &found);
161 if(found)
162 rb_delete_node(node);
164 return (rb_insertp(split_of_map, reg, split)!=NULL);
168 /*}}}*/
171 /*{{{ Primn */
174 WPrimn primn_invert(WPrimn primn)
176 return (primn==PRIMN_TL
177 ? PRIMN_BR
178 : (primn==PRIMN_BR
179 ? PRIMN_TL
180 : primn));
184 WPrimn primn_none2any(WPrimn primn)
186 return (primn==PRIMN_NONE ? PRIMN_ANY : primn);
190 /*}}}*/
193 /*{{{ Create */
196 bool split_init(WSplit *split, const WRectangle *geom)
198 split->parent=NULL;
199 split->ws_if_root=NULL;
200 split->geom=*geom;
201 split->min_w=0;
202 split->min_h=0;
203 split->max_w=INT_MAX;
204 split->max_h=INT_MAX;
205 split->unused_w=-1;
206 split->unused_h=-1;
207 return TRUE;
210 bool splitinner_init(WSplitInner *split, const WRectangle *geom)
212 return split_init(&(split->split), geom);
216 bool splitsplit_init(WSplitSplit *split, const WRectangle *geom, int dir)
218 splitinner_init(&(split->isplit), geom);
219 split->dir=dir;
220 split->tl=NULL;
221 split->br=NULL;
222 split->current=SPLIT_CURRENT_TL;
223 return TRUE;
227 bool splitregion_init(WSplitRegion *split, const WRectangle *geom,
228 WRegion *reg)
230 split_init(&(split->split), geom);
231 split->reg=reg;
232 if(reg!=NULL)
233 set_node_of_reg(reg, split);
234 return TRUE;
238 bool splitst_init(WSplitST *split, const WRectangle *geom, WRegion *reg)
240 splitregion_init(&(split->regnode), geom, reg);
241 split->orientation=REGION_ORIENTATION_HORIZONTAL;
242 split->corner=MPLEX_STDISP_BL;
243 return TRUE;
247 WSplitSplit *create_splitsplit(const WRectangle *geom, int dir)
249 CREATEOBJ_IMPL(WSplitSplit, splitsplit, (p, geom, dir));
253 WSplitRegion *create_splitregion(const WRectangle *geom, WRegion *reg)
255 CREATEOBJ_IMPL(WSplitRegion, splitregion, (p, geom, reg));
259 WSplitST *create_splitst(const WRectangle *geom, WRegion *reg)
261 CREATEOBJ_IMPL(WSplitST, splitst, (p, geom, reg));
265 /*}}}*/
268 /*{{{ Deinit */
271 void split_deinit(WSplit *split)
273 assert(split->parent==NULL);
277 void splitinner_deinit(WSplitInner *split)
279 split_deinit(&(split->split));
283 void splitsplit_deinit(WSplitSplit *split)
285 if(split->tl!=NULL){
286 split->tl->parent=NULL;
287 destroy_obj((Obj*)(split->tl));
289 if(split->br!=NULL){
290 split->br->parent=NULL;
291 destroy_obj((Obj*)(split->br));
294 splitinner_deinit(&(split->isplit));
298 void splitregion_deinit(WSplitRegion *split)
300 if(split->reg!=NULL){
301 set_node_of_reg(split->reg, NULL);
302 split->reg=NULL;
305 split_deinit(&(split->split));
309 void splitst_deinit(WSplitST *split)
311 splitregion_deinit(&(split->regnode));
315 /*}}}*/
318 /*{{{ Size bounds management */
321 static void splitregion_update_bounds(WSplitRegion *node, bool recursive)
323 WSizeHints hints;
324 WSplit *snode=(WSplit*)node;
326 assert(node->reg!=NULL);
328 region_size_hints(node->reg, &hints);
330 snode->min_w=maxof(1, hints.min_set ? hints.min_width : 1);
331 snode->max_w=INT_MAX;
332 snode->unused_w=-1;
334 snode->min_h=maxof(1, hints.min_set ? hints.min_height : 1);
335 snode->max_h=INT_MAX;
336 snode->unused_h=-1;
340 static void splitst_update_bounds(WSplitST *node, bool rec)
342 WSplit *snode=(WSplit*)node;
344 if(node->regnode.reg==NULL){
345 snode->min_w=CF_STDISP_MIN_SZ;
346 snode->min_h=CF_STDISP_MIN_SZ;
347 snode->max_w=CF_STDISP_MIN_SZ;
348 snode->max_h=CF_STDISP_MIN_SZ;
349 }else{
350 WSizeHints hints;
351 region_size_hints(node->regnode.reg, &hints);
352 snode->min_w=maxof(1, hints.min_set ? hints.min_width : 1);
353 snode->max_w=maxof(snode->min_w, hints.min_width);
354 snode->min_h=maxof(1, hints.min_set ? hints.min_height : 1);
355 snode->max_h=maxof(snode->min_h, hints.min_height);
358 snode->unused_w=-1;
359 snode->unused_h=-1;
361 if(node->orientation==REGION_ORIENTATION_HORIZONTAL){
362 snode->min_w=CF_STDISP_MIN_SZ;
363 snode->max_w=INT_MAX;
364 }else{
365 snode->min_h=CF_STDISP_MIN_SZ;
366 snode->max_h=INT_MAX;
371 static void splitsplit_update_bounds(WSplitSplit *split, bool recursive)
373 WSplit *tl, *br;
374 WSplit *node=(WSplit*)split;
376 assert(split->tl!=NULL && split->br!=NULL);
378 tl=split->tl;
379 br=split->br;
381 if(recursive){
382 split_update_bounds(tl, TRUE);
383 split_update_bounds(br, TRUE);
386 if(split->dir==SPLIT_HORIZONTAL){
387 node->max_w=infadd(tl->max_w, br->max_w);
388 node->min_w=infadd(tl->min_w, br->min_w);
389 node->unused_w=unusedadd(tl->unused_w, br->unused_w);
390 node->min_h=maxof(tl->min_h, br->min_h);
391 node->max_h=maxof(minof(tl->max_h, br->max_h), node->min_h);
392 node->unused_h=minof(tl->unused_h, br->unused_h);
393 }else{
394 node->max_h=infadd(tl->max_h, br->max_h);
395 node->min_h=infadd(tl->min_h, br->min_h);
396 node->unused_h=unusedadd(tl->unused_h, br->unused_h);
397 node->min_w=maxof(tl->min_w, br->min_w);
398 node->max_w=maxof(minof(tl->max_w, br->max_w), node->min_w);
399 node->unused_w=minof(tl->unused_w, br->unused_w);
404 void split_update_bounds(WSplit *node, bool recursive)
406 CALL_DYN(split_update_bounds, node, (node, recursive));
410 void splitsplit_update_geom_from_children(WSplitSplit *node)
412 WSplit *split=(WSplit*)node;
414 if(node->dir==SPLIT_VERTICAL){
415 ((WSplit*)node)->geom.h=node->tl->geom.h+node->br->geom.h;
416 ((WSplit*)node)->geom.y=node->tl->geom.y;
417 }else if(node->dir==SPLIT_HORIZONTAL){
418 ((WSplit*)node)->geom.w=node->tl->geom.w+node->br->geom.w;
419 ((WSplit*)node)->geom.x=node->tl->geom.x;
424 /*}}}*/
427 /*{{{ Status display handling helper functions. */
430 static WSplitST *saw_stdisp=NULL;
433 void splittree_begin_resize()
435 saw_stdisp=NULL;
439 void splittree_end_resize()
441 if(saw_stdisp!=NULL){
442 split_regularise_stdisp(saw_stdisp);
443 saw_stdisp=NULL;
448 static void splittree_scan_stdisp_rootward_(WSplitInner *node_)
450 WSplitSplit *node=OBJ_CAST(node_, WSplitSplit);
452 if(node!=NULL){
453 if(OBJ_IS(node->tl, WSplitST)){
454 saw_stdisp=(WSplitST*)(node->tl);
455 return;
456 }else if(OBJ_IS(node->br, WSplitST)){
457 saw_stdisp=(WSplitST*)(node->br);
458 return;
462 if(node_->split.parent!=NULL)
463 splittree_scan_stdisp_rootward_(node_->split.parent);
467 void splittree_scan_stdisp_rootward(WSplit *node)
469 if(node->parent!=NULL)
470 splittree_scan_stdisp_rootward_(node->parent);
474 static WSplitST *splittree_scan_stdisp(WSplit *node_, bool set_saw)
476 WSplitST *r=NULL;
477 WSplitSplit *node=OBJ_CAST(node_, WSplitSplit);
479 if(node==NULL)
480 return NULL;
482 r=OBJ_CAST(node->tl, WSplitST);
483 if(r==NULL)
484 r=OBJ_CAST(node->br, WSplitST);
486 if(r!=NULL){
487 if(set_saw)
488 saw_stdisp=r;
489 return r;
492 r=splittree_scan_stdisp(node->tl, set_saw);
493 if(r==NULL)
494 r=splittree_scan_stdisp(node->br, set_saw);
496 return r;
500 static bool stdisp_immediate_child(WSplitSplit *node)
502 return (node!=NULL && (OBJ_IS(node->tl, WSplitST) ||
503 OBJ_IS(node->br, WSplitST)));
507 static WSplit *dodge_stdisp(WSplit *node, bool keep_within)
509 WSplitST *stdisp;
510 WSplitSplit *stdispp;
512 stdisp=splittree_scan_stdisp(node, TRUE);
514 if(stdisp==NULL)
515 return node;
517 stdispp=OBJ_CAST(((WSplit*)stdisp)->parent, WSplitSplit);
519 if(stdispp==NULL)
520 return node;
522 if((WSplit*)stdispp==node){
523 /* Node itself immediately contains stdisp. Due to the way
524 * try_unsink works, stdisp this will not change, so another
525 * node must be used, if we want to fully dodge stdisp.
527 return (keep_within
528 ? node
529 : (stdispp->tl==(WSplit*)stdisp
530 ? stdispp->br
531 : stdispp->tl));
535 if(!split_try_unsink_stdisp(stdispp, FALSE, TRUE)){
536 warn(TR("Unable to move the status display out of way."));
537 return NULL;
539 }while(stdispp->tl!=node && stdispp->br!=node);
541 return node;
545 /*}}}*/
548 /*{{{ Low-level resize code; from root to leaf */
551 static void split_do_resize_default(WSplit *node, const WRectangle *ng,
552 WPrimn hprimn, WPrimn vprimn,
553 bool transpose)
555 node->geom=*ng;
559 static void splitregion_do_resize(WSplitRegion *node, const WRectangle *ng,
560 WPrimn hprimn, WPrimn vprimn,
561 bool transpose)
563 assert(node->reg!=NULL);
564 region_fit(node->reg, ng, REGION_FIT_EXACT);
565 split_update_bounds(&(node->split), FALSE);
566 node->split.geom=*ng;
570 static void splitst_do_resize(WSplitST *node, const WRectangle *ng,
571 WPrimn hprimn, WPrimn vprimn,
572 bool transpose)
574 saw_stdisp=node;
576 if(node->regnode.reg==NULL){
577 ((WSplit*)node)->geom=*ng;
578 }else{
579 splitregion_do_resize(&(node->regnode), ng, hprimn, vprimn,
580 transpose);
585 static int other_dir(int dir)
587 return (dir==SPLIT_VERTICAL ? SPLIT_HORIZONTAL : SPLIT_VERTICAL);
591 static void adjust_sizes(int *tls_, int *brs_, int nsize, int sz,
592 int tlmin, int brmin, int tlmax, int brmax,
593 int primn)
595 int tls=*tls_;
596 int brs=*brs_;
598 if(primn==PRIMN_TL){
599 tls=tls+nsize-sz;
600 bound(&tls, tlmin, tlmax);
601 brs=nsize-tls;
602 bound(&brs, brmin, brmax);
603 tls=nsize-brs;
604 bound(&tls, tlmin, tlmax);
605 }else if(primn==PRIMN_BR){
606 brs=brs+nsize-sz;
607 bound(&brs, brmin, brmax);
608 tls=nsize-brs;
609 bound(&tls, tlmin, tlmax);
610 brs=nsize-tls;
611 bound(&brs, brmin, brmax);
612 }else{ /* && PRIMN_ANY */
613 tls=tls*nsize/sz;
614 bound(&tls, tlmin, tlmax);
615 brs=nsize-tls;
616 bound(&brs, brmin, brmax);
617 tls=nsize-brs;
618 bound(&tls, tlmin, tlmax);
621 *tls_=tls;
622 *brs_=brs;
626 static void get_minmaxunused(WSplit *node, int dir,
627 int *min, int *max, int *unused)
629 if(dir==SPLIT_VERTICAL){
630 *min=node->min_h;
631 *max=maxof(*min, node->max_h);
632 *unused=minof(node->unused_h, node->geom.h);
633 }else{
634 *min=node->min_w;
635 *max=maxof(*min, node->max_w);
636 *unused=minof(node->unused_w, node->geom.w);
641 void splitsplit_do_resize(WSplitSplit *node, const WRectangle *ng,
642 WPrimn hprimn, WPrimn vprimn, bool transpose)
644 assert(ng->w>=0 && ng->h>=0);
645 assert(node->tl!=NULL && node->br!=NULL);
646 assert(!transpose || (hprimn==PRIMN_ANY && vprimn==PRIMN_ANY));
649 WSplit *tl=node->tl, *br=node->br;
650 int tls=split_size((WSplit*)tl, node->dir);
651 int brs=split_size((WSplit*)br, node->dir);
652 int sz=tls+brs;
653 /* Status display can not be transposed. */
654 int dir=((transpose && !stdisp_immediate_child(node))
655 ? other_dir(node->dir)
656 : node->dir);
657 int nsize=(dir==SPLIT_VERTICAL ? ng->h : ng->w);
658 int primn=(dir==SPLIT_VERTICAL ? vprimn : hprimn);
659 int tlmin, tlmax, tlunused, tlused;
660 int brmin, brmax, brunused, brused;
661 WRectangle tlg=*ng, brg=*ng;
663 get_minmaxunused(tl, dir, &tlmin, &tlmax, &tlunused);
664 get_minmaxunused(br, dir, &brmin, &brmax, &brunused);
666 tlused=maxof(0, tls-maxof(0, tlunused));
667 brused=maxof(0, brs-maxof(0, brunused));
668 /* tlmin, brmin >= 1 => (tls>=tlmin, brs>=brmin => sz>0) */
670 if(sz>2){
671 if(primn==PRIMN_ANY && (tlunused>=0 || brunused>=0)){
672 if(nsize<=tlused+brused){
673 /* Need to shrink a tangible node */
674 adjust_sizes(&tls, &brs, nsize, sz,
675 tlmin, brmin, tlused, brused, primn);
676 }else{
677 /* Just expand or shrink unused space */
678 adjust_sizes(&tls, &brs, nsize, sz,
679 tlused, brused,
680 (tlunused<0 ? tlused : tlmax),
681 (brunused<0 ? brused : brmax), primn);
684 }else{
685 adjust_sizes(&tls, &brs, nsize, sz,
686 tlmin, brmin, tlmax, brmax, primn);
690 if(tls+brs!=nsize){
691 /* Bad fit; just size proportionally. */
692 if(sz<=2){
693 tls=nsize/2;
694 brs=nsize-tls;
695 }else{
696 tls=split_size(tl, node->dir)*nsize/sz;
697 brs=nsize-tls;
701 if(dir==SPLIT_VERTICAL){
702 tlg.h=tls;
703 brg.y+=tls;
704 brg.h=brs;
705 }else{
706 tlg.w=tls;
707 brg.x+=tls;
708 brg.w=brs;
711 split_do_resize(tl, &tlg, hprimn, vprimn, transpose);
712 split_do_resize(br, &brg, hprimn, vprimn, transpose);
714 node->dir=dir;
715 ((WSplit*)node)->geom=*ng;
716 split_update_bounds((WSplit*)node, FALSE);
721 void split_do_resize(WSplit *node, const WRectangle *ng,
722 WPrimn hprimn, WPrimn vprimn, bool transpose)
724 CALL_DYN(split_do_resize, node, (node, ng, hprimn, vprimn, transpose));
728 void split_resize(WSplit *node, const WRectangle *ng,
729 WPrimn hprimn, WPrimn vprimn)
731 split_update_bounds(node, TRUE);
732 splittree_begin_resize();
733 split_do_resize(node, ng, hprimn, vprimn, FALSE);
734 splittree_end_resize();
738 /*}}}*/
741 /*{{{ Save, restore and verify code for maximization */
744 bool splits_are_related(WSplit *p, WSplit *node)
746 if(p==node)
747 return TRUE;
749 return
750 node->parent!=NULL
751 ? splits_are_related(p, (WSplit*)node->parent)
752 : FALSE;
756 WSplit *maxparentdir_rel(WSplit *p, WSplit *node, int dir)
758 /* Descending from p, try to determine the first split of type dir between
759 * p and node, while ignoring a potential stdisp. */
760 if(OBJ_IS(p, WSplitSplit)){
761 WSplitSplit *sp=(WSplitSplit*)p;
762 assert(sp->tl!=NULL && sp->br!=NULL);
763 assert(splits_are_related(sp->tl, node) ||
764 splits_are_related(sp->br, node));
766 if(OBJ_IS(sp->tl, WSplitST))
767 return maxparentdir_rel(sp->br, node, dir);
768 if(OBJ_IS(sp->br, WSplitST))
769 return maxparentdir_rel(sp->tl, node, dir);
771 if(sp->dir!=dir){
772 return
773 splits_are_related(sp->tl, node)
774 ? maxparentdir_rel(sp->tl, node, dir)
775 : maxparentdir_rel(sp->br, node, dir);
779 return p;
783 WSplit *maxparent(WSplit *node)
785 WSplit *p=(WSplit*)node->parent;
786 return p==NULL ? node : maxparent(p);
790 WSplit *maxparentdir(WSplit *node, int dir)
792 return maxparentdir_rel(maxparent(node), node, dir);
795 int *wh(WRectangle *geom, int orientation)
797 return orientation==REGION_ORIENTATION_HORIZONTAL ? &geom->w : &geom->h;
800 int *xy(WRectangle *geom, int orientation)
802 return orientation==REGION_ORIENTATION_HORIZONTAL ? &geom->x : &geom->y;
805 bool is_lt(int orientation, int corner)
807 /* Read as "is_left" or "is_top", depending on the orientation. */
808 return
809 orientation==REGION_ORIENTATION_HORIZONTAL
810 ? corner==MPLEX_STDISP_TL || corner==MPLEX_STDISP_BL
811 : corner==MPLEX_STDISP_TL || corner==MPLEX_STDISP_TR;
814 int flip_orientation(int orientation)
816 return
817 orientation==REGION_ORIENTATION_HORIZONTAL
818 ? REGION_ORIENTATION_VERTICAL
819 : REGION_ORIENTATION_HORIZONTAL;
822 WRectangle stdisp_recommended_geom(WSplitST *st, WRectangle wsg)
824 /* wsg holds the geometry of the workspace that st is on. */
825 WRectangle stg=REGION_GEOM(st->regnode.reg);
826 int ori=st->orientation;
827 stg.w=stdisp_recommended_w(st);
828 stg.h=stdisp_recommended_h(st);
830 if(!is_lt(ori, st->corner))
831 *xy(&stg, ori)=*wh(&wsg, ori)-*wh(&stg, ori);
833 return stg;
836 bool geom_overlaps_stgeom_xy(WRectangle geom, WSplitST *st, WRectangle stg)
838 /* TRUE FALSE
840 * ------ ------
841 * |geom| |geom|
842 * ------ ------
843 * ----- -----
844 * |stg| |stg|
845 * ----- -----
847 int ori=st->orientation;
849 return
850 is_lt(ori, st->corner)
851 ? *xy(&geom, ori)<*wh(&stg, ori)
852 : *xy(&geom, ori)+*wh(&geom, ori)>*xy(&stg, ori);
855 bool geom_aligned_stdisp(WRectangle geom, WSplitST *st)
857 /* TRUE FALSE FALSE
859 * ------
860 * |geom|
861 * ------ ------
862 * |geom| ------
863 * ----- ------ ----- |geom| -----
864 * |stg| |stg| ------ |stg|
865 * ----- ----- -----
868 WRectangle stg=REGION_GEOM(st->regnode.reg);
869 int ori=flip_orientation(st->orientation);
871 return
872 is_lt(ori, st->corner)
873 ? *xy(&geom, ori)==*wh(&stg, ori)
874 : *xy(&geom, ori)+*wh(&geom, ori)==*xy(&stg, ori);
877 void grow_by_stdisp_wh(WRectangle *geom, WSplitST *st)
879 /* BEFORE AFTER
882 * ------ ------
883 * |geom| | |
884 * ----- ------ ----- |geom|
885 * |stg| |stg| | |
886 * ----- ----- ------
889 WRectangle stg=REGION_GEOM(st->regnode.reg);
890 int ori=flip_orientation(st->orientation);
892 if(is_lt(ori, st->corner))
893 *xy(geom, ori)=0;
894 *wh(geom, ori)+=*wh(&stg, ori);
897 bool frame_neighbors_stdisp(WFrame *frame, WSplitST *st)
899 return
900 geom_overlaps_stgeom_xy(REGION_GEOM(frame), st, REGION_GEOM(st)) &&
901 geom_aligned_stdisp(REGION_GEOM(frame), st);
904 bool geom_clashes_stdisp(WRectangle geom, WSplitST *st)
906 WRectangle stg=REGION_GEOM(st->regnode.reg);
907 int ori=flip_orientation(st->orientation);
908 return
909 is_lt(ori, st->corner)
910 ? *xy(&geom, ori)==0
911 : *xy(&geom, ori)+*wh(&geom, ori)==*xy(&stg, ori)+*wh(&stg, ori);
914 bool is_same_dir(int dir, int ori)
916 return
917 (dir==SPLIT_HORIZONTAL && ori==REGION_ORIENTATION_HORIZONTAL) ||
918 (dir==SPLIT_VERTICAL && ori==REGION_ORIENTATION_VERTICAL);
921 bool is_maxed(WFrame *frame, int dir)
923 return
924 dir==SPLIT_HORIZONTAL
925 ? frame->flags&FRAME_MAXED_HORIZ && frame->flags&FRAME_SAVED_HORIZ
926 : frame->flags&FRAME_MAXED_VERT && frame->flags&FRAME_SAVED_VERT;
929 bool update_geom_from_stdisp(WFrame *frame, WRectangle *ng, int dir)
931 WRegion *ws=REGION_MANAGER(frame);
932 WSplitST *st;
933 WRectangle stg;
934 WRectangle rstg;
935 WRectangle ngr;
936 int ori;
938 if(!OBJ_IS(ws, WTiling) || ((WTiling*)ws)->stdispnode==NULL)
939 return FALSE;
941 st=((WTiling*)ws)->stdispnode;
943 if(st->fullsize || !frame_neighbors_stdisp(frame, st))
944 return FALSE;
946 rstg=stdisp_recommended_geom(st, ws->geom);
948 if(is_same_dir(dir, st->orientation) &&
949 !geom_overlaps_stgeom_xy(*ng, st, rstg))
951 grow_by_stdisp_wh(ng, st);
952 if(is_maxed(frame, other_dir(dir)) &&
953 geom_aligned_stdisp(frame->saved_geom, st))
955 grow_by_stdisp_wh(&frame->saved_geom, st);
957 return TRUE;
960 if(!is_same_dir(dir, st->orientation) &&
961 geom_clashes_stdisp(frame->saved_geom, st))
963 stg=REGION_GEOM(st->regnode.reg);
964 ori=flip_orientation(st->orientation);
965 if(is_lt(ori, st->corner))
966 *xy(ng, ori)+=*wh(&stg, ori);
967 /* We've checked that this makes sense when verifying the saved layout. */
968 *wh(ng, ori)-=*wh(&stg, ori);
971 return FALSE;
974 bool splitregion_do_restore(WSplitRegion *node, int dir)
976 WFrame *frame;
977 WRectangle geom=((WSplit*)node)->geom;
978 WRectangle fakegeom;
979 bool ret;
980 bool other_max;
982 if(!OBJ_IS(node->reg, WFrame))
983 return FALSE;
985 frame=(WFrame*)node->reg;
986 if(dir==SPLIT_HORIZONTAL){
987 geom.x=frame->saved_geom.x;
988 geom.w=frame->saved_geom.w;
989 }else{
990 geom.y=frame->saved_geom.y;
991 geom.h=frame->saved_geom.h;
994 other_max=
995 dir==SPLIT_HORIZONTAL
996 ? frame->flags&FRAME_MAXED_VERT
997 : frame->flags&FRAME_MAXED_HORIZ;
999 fakegeom=geom;
1000 ret=update_geom_from_stdisp(frame, &geom, dir);
1002 /* Tell the region the correct geometry to avoid redrawing it again when
1003 * the stdisp is resized by split_regularise_stdisp. Some clients (notably
1004 * ncurses based ones) don't seem to react well to being resized multiple
1005 * times within a short amount of time and don't refresh themselves
1006 * correctly. */
1007 region_fit(node->reg, &geom, REGION_FIT_EXACT);
1009 split_update_bounds(&(node->split), FALSE);
1011 /* Keep the old geometry for the WSplit. Otherwise the tiling would be
1012 * inconsistent, by for example having horizontal WSplitSplit's whose
1013 * children have different heights. The call to split_regularise_stdisp
1014 * below will take care of correcting the geometry of the WSplit and it
1015 * behaves badly when the tiling is inconsistent. */
1016 node->split.geom=ret ? fakegeom : geom;
1018 frame->flags|=other_max;
1019 return ret;
1022 bool splitst_do_restore(WSplit *node, int dir)
1024 return FALSE;
1027 bool splitsplit_do_restore(WSplitSplit *node, int dir)
1029 bool ret1, ret2, ret=FALSE;
1030 WSplit *snode=(WSplit*)node;
1032 WSplitST *st;
1033 WSplit *other;
1034 WRectangle stg;
1035 WRectangle og;
1037 assert(node->tl!=NULL && node->br!=NULL);
1039 if(stdisp_immediate_child(node)){
1040 if(OBJ_IS(node->tl, WSplitST)){
1041 st=(WSplitST*)node->tl;
1042 other=node->br;
1043 }else{
1044 st=(WSplitST*)node->br;
1045 other=node->tl;
1047 stg=((WSplit*)st)->geom;
1048 split_do_restore(other, dir);
1049 og=other->geom;
1050 if(node->dir==SPLIT_HORIZONTAL){
1051 stg.y=og.y;
1052 stg.h=og.h;
1053 }else{
1054 stg.x=og.x;
1055 stg.w=og.w;
1057 if(rectangle_compare(&stg, &((WSplit*)st)->geom)){
1058 splitst_do_resize(st, &stg, PRIMN_ANY, PRIMN_ANY, FALSE);
1059 ret=TRUE;
1061 }else{
1062 /* Avoid short-circuit evaluation. */
1063 ret1=split_do_restore(node->tl, dir);
1064 ret2=split_do_restore(node->br, dir);
1065 ret=ret1 || ret2;
1068 snode->geom.x=node->tl->geom.x;
1069 snode->geom.y=node->tl->geom.y;
1070 if(node->dir==SPLIT_HORIZONTAL){
1071 snode->geom.w=node->tl->geom.w+node->br->geom.w;
1072 snode->geom.h=node->tl->geom.h;
1074 if(node->dir==SPLIT_VERTICAL){
1075 snode->geom.w=node->tl->geom.w;
1076 snode->geom.h=node->tl->geom.h+node->br->geom.h;
1079 return ret;
1082 bool split_do_restore(WSplit *node, int dir)
1084 bool ret = FALSE;
1085 CALL_DYN_RET(ret, bool, split_do_restore, node, (node, dir));
1086 return ret;
1090 void splitregion_do_maxhelper(WSplitRegion *node, int dir, int action)
1092 WFrame *frame;
1093 if(!OBJ_IS(node->reg, WFrame))
1094 return;
1095 frame=(WFrame*)node->reg;
1097 if(action==SAVE){
1098 frame->flags|=FRAME_KEEP_FLAGS;
1099 if(dir==HORIZONTAL){
1100 frame->flags|=(FRAME_MAXED_HORIZ|FRAME_SAVED_HORIZ);
1101 frame->saved_geom.x=REGION_GEOM(frame).x;
1102 frame->saved_geom.w=REGION_GEOM(frame).w;
1103 }else{
1104 frame->flags|=(FRAME_MAXED_VERT|FRAME_SAVED_VERT);
1105 frame->saved_geom.y=REGION_GEOM(frame).y;
1106 frame->saved_geom.h=REGION_GEOM(frame).h;
1109 if(action==SET_KEEP)
1110 frame->flags|=FRAME_KEEP_FLAGS;
1111 if(action==RM_KEEP)
1112 frame->flags&=~FRAME_KEEP_FLAGS;
1115 void splitst_do_maxhelper(WSplit *node, int dir, int action)
1117 return;
1120 void splitsplit_do_maxhelper(WSplitSplit *node, int dir, int action)
1122 assert(node->tl!=NULL && node->br!=NULL);
1123 split_do_maxhelper(node->tl, dir, action);
1124 split_do_maxhelper(node->br, dir, action);
1127 void split_do_maxhelper(WSplit *node, int dir, int action)
1129 CALL_DYN(split_do_maxhelper, node, (node, dir, action));
1133 bool savedgeom_clashes_stdisp(WFrame *frame, int dir)
1135 WRegion *ws=REGION_MANAGER(frame);
1136 WSplitST *st;
1137 int ori;
1139 if(!OBJ_IS(ws, WTiling) || ((WTiling*)ws)->stdispnode==NULL)
1140 return TRUE;
1142 st=((WTiling*)ws)->stdispnode;
1143 ori=flip_orientation(st->orientation);
1145 return
1146 !is_same_dir(dir, st->orientation) &&
1147 frame_neighbors_stdisp(frame, st) &&
1148 geom_clashes_stdisp(frame->saved_geom, st)
1149 ? *wh(&frame->saved_geom, ori)<*wh(&REGION_GEOM(st), ori)
1150 : FALSE;
1153 bool splitregion_do_verify(WSplitRegion *node, int dir)
1155 WFrame *frame;
1156 bool ret=FALSE;
1158 if(!OBJ_IS(node->reg, WFrame))
1159 return FALSE;
1161 frame=(WFrame*)node->reg;
1163 ret=is_maxed(frame, dir);
1165 if(dir==HORIZONTAL)
1166 frame->flags&=~(FRAME_MAXED_HORIZ|FRAME_SAVED_HORIZ);
1167 else
1168 frame->flags&=~(FRAME_MAXED_VERT|FRAME_SAVED_VERT);
1170 if(savedgeom_clashes_stdisp(frame, dir))
1171 return FALSE;
1173 return ret;
1176 bool splitst_do_verify(WSplit *node, int dir)
1178 return TRUE;
1181 bool splitsplit_do_verify(WSplitSplit *node, int dir)
1183 bool ret1, ret2;
1184 assert(node->tl!=NULL && node->br!=NULL);
1186 /* Avoid short-circuit evaluation. */
1187 ret1=split_do_verify(node->tl, dir);
1188 ret2=split_do_verify(node->br, dir);
1189 return ret1 && ret2;
1192 bool split_do_verify(WSplit *node, int dir)
1194 bool ret = FALSE;
1195 CALL_DYN_RET(ret, bool, split_do_verify, node, (node, dir));
1196 return ret;
1200 bool split_maximize(WSplit *node, int dir, int action)
1202 WSplit *p=maxparentdir(node, dir);
1203 if(action==RESTORE)
1204 return split_do_restore(p, dir);
1205 if(action==VERIFY)
1206 return split_do_verify(p, dir);
1208 split_do_maxhelper(p, dir, action);
1209 return TRUE;
1213 /*}}}*/
1216 /*{{{ Low-level resize code; request towards root */
1219 static void flexibility(WSplit *node, int dir, int *shrink, int *stretch)
1221 if(dir==SPLIT_VERTICAL){
1222 *shrink=maxof(0, node->geom.h-node->min_h);
1223 if(OBJ_IS(node, WSplitST))
1224 *stretch=maxof(0, node->max_h-node->geom.h);
1225 else
1226 *stretch=INT_MAX;
1227 }else{
1228 *shrink=maxof(0, node->geom.w-node->min_w);
1229 if(OBJ_IS(node, WSplitST))
1230 *stretch=maxof(0, node->max_w-node->geom.w);
1231 else
1232 *stretch=INT_MAX;
1237 static void calc_amount(int *amount, int rs, WSplit *other, int dir)
1239 int shrink, stretch;
1241 flexibility(other, dir, &shrink, &stretch);
1243 if(rs>0)
1244 *amount=minof(rs, shrink);
1245 else if(rs<0)
1246 *amount=-minof(-rs, stretch);
1247 else
1248 *amount=0;
1253 static void splitsplit_do_rqsize(WSplitSplit *p, WSplit *node,
1254 RootwardAmount *ha, RootwardAmount *va,
1255 WRectangle *rg, bool tryonly)
1257 WPrimn hprimn=PRIMN_ANY, vprimn=PRIMN_ANY;
1258 WRectangle og, pg, ng;
1259 RootwardAmount *ca;
1260 WSplit *other;
1261 WPrimn thisnode;
1262 int amount;
1264 assert(!ha->any || ha->tl==0);
1265 assert(!va->any || va->tl==0);
1266 assert(p->tl==node || p->br==node);
1268 if(p->tl==node){
1269 other=p->br;
1270 thisnode=PRIMN_TL;
1271 }else{
1272 other=p->tl;
1273 thisnode=PRIMN_BR;
1276 ca=(p->dir==SPLIT_VERTICAL ? va : ha);
1278 if(thisnode==PRIMN_TL || ca->any){
1279 calc_amount(&amount, ca->br, other, p->dir);
1280 ca->br-=amount;
1281 }else/*if(thisnode==PRIMN_BR)*/{
1282 calc_amount(&amount, ca->tl, other, p->dir);
1283 ca->tl-=amount;
1286 if(((WSplit*)p)->parent==NULL /*||
1287 (ha->tl==0 && ha->br==0 && va->tl==0 && va->br==0)*/){
1288 if(((WSplit*)p)->ws_if_root!=NULL)
1289 pg=REGION_GEOM((WTiling*)(((WSplit*)p)->ws_if_root));
1290 else
1291 pg=((WSplit*)p)->geom;
1292 }else{
1293 splitinner_do_rqsize(((WSplit*)p)->parent, (WSplit*)p, ha, va,
1294 &pg, tryonly);
1297 assert(pg.w>=0 && pg.h>=0);
1299 og=pg;
1300 ng=pg;
1302 if(p->dir==SPLIT_VERTICAL){
1303 ng.h=maxof(0, node->geom.h+amount);
1304 og.h=maxof(0, other->geom.h-amount);
1305 adjust_sizes(&(ng.h), &(og.h), pg.h, ng.h+og.h,
1306 node->min_h, other->min_h, node->max_h, other->max_h,
1307 PRIMN_TL /* node is passed as tl param */);
1308 if(thisnode==PRIMN_TL)
1309 og.y=pg.y+pg.h-og.h;
1310 else
1311 ng.y=pg.y+pg.h-ng.h;
1312 vprimn=thisnode;
1313 }else{
1314 ng.w=maxof(0, node->geom.w+amount);
1315 og.w=maxof(0, other->geom.w-amount);
1316 adjust_sizes(&(ng.w), &(og.w), pg.w, ng.w+og.w,
1317 node->min_w, other->min_w, node->max_w, other->max_w,
1318 PRIMN_TL /* node is passed as tl param */);
1319 if(thisnode==PRIMN_TL)
1320 og.x=pg.x+pg.w-og.w;
1321 else
1322 ng.x=pg.x+pg.w-ng.w;
1323 hprimn=thisnode;
1326 if(!tryonly){
1327 /* Entä jos 'other' on stdisp? */
1328 split_do_resize(other, &og, hprimn, vprimn, FALSE);
1330 ((WSplit*)p)->geom=pg;
1333 *rg=ng;
1337 void splitinner_do_rqsize(WSplitInner *p, WSplit *node,
1338 RootwardAmount *ha, RootwardAmount *va,
1339 WRectangle *rg, bool tryonly)
1341 CALL_DYN(splitinner_do_rqsize, p, (p, node, ha, va, rg, tryonly));
1345 static void initra(RootwardAmount *ra, int p, int s, int op, int os,
1346 bool any)
1348 ra->any=any;
1349 ra->tl=op-p;
1350 ra->br=(p+s)-(op+os);
1351 if(any){
1352 ra->br+=ra->tl;
1353 ra->tl=0;
1358 void split_do_rqgeom_(WSplit *node, const WRectangle *ng,
1359 bool hany, bool vany, WRectangle *rg,
1360 bool tryonly)
1362 RootwardAmount ha, va;
1364 if(node->parent==NULL){
1365 if(node->ws_if_root!=NULL)
1366 *rg=REGION_GEOM((WTiling*)(node->ws_if_root));
1367 else
1368 *rg=*ng;
1369 }else{
1370 initra(&ha, ng->x, ng->w, node->geom.x, node->geom.w, hany);
1371 initra(&va, ng->y, ng->h, node->geom.y, node->geom.h, vany);
1373 splitinner_do_rqsize(node->parent, node, &ha, &va, rg, tryonly);
1378 /*}}}*/
1381 /*{{{ Resize interface */
1384 static void bnd(int *pos, int *sz, int opos, int osz, int minsz, int maxsz)
1386 int ud=abs(*pos-opos);
1387 int dd=abs((*pos+*sz)-(opos+osz));
1388 int szrq=*sz;
1390 if(ud+dd!=0){
1391 bound(sz, minsz, maxsz);
1392 *pos+=(szrq-*sz)*ud/(ud+dd);
1397 WSplit *split_find_root(WSplit *split)
1399 if(split->parent==NULL)
1400 return split;
1401 return split_find_root((WSplit*)split->parent);
1405 void splittree_rqgeom(WSplit *sub, int flags, const WRectangle *geom_,
1406 WRectangle *geomret)
1408 bool hany=flags&REGION_RQGEOM_WEAK_X;
1409 bool vany=flags&REGION_RQGEOM_WEAK_Y;
1410 bool tryonly=flags&REGION_RQGEOM_TRYONLY;
1411 WRectangle geom=*geom_;
1412 WRectangle retg;
1413 WSplit *root=split_find_root(sub);
1415 if(geomret==NULL)
1416 geomret=&retg;
1418 split_update_bounds(root, TRUE);
1420 if(OBJ_IS(sub, WSplitST)){
1421 WSplitST *sub_as_stdisp=(WSplitST*)sub;
1423 if(flags&REGION_RQGEOM_TRYONLY){
1424 warn(TR("REGION_RQGEOM_TRYONLY unsupported for status display."));
1425 *geomret=sub->geom;
1426 return;
1428 split_regularise_stdisp(sub_as_stdisp);
1429 geom=sub->geom;
1430 if(sub_as_stdisp->orientation==REGION_ORIENTATION_HORIZONTAL){
1431 if(geom_->h==geom.h)
1432 return;
1433 geom.h=geom_->h;
1434 }else{
1435 if(geom_->w==geom.w)
1436 return;
1437 geom.w=geom_->w;
1439 split_update_bounds(root, TRUE);
1442 /* Handle internal size bounds */
1443 bnd(&(geom.x), &(geom.w), sub->geom.x, sub->geom.w,
1444 sub->min_w, sub->max_w);
1445 bnd(&(geom.y), &(geom.h), sub->geom.y, sub->geom.h,
1446 sub->min_h, sub->max_h);
1448 /* Check if we should resize to both tl and br */
1450 if(hany){
1451 geom.w+=sub->geom.x-geom.x;
1452 geom.x=sub->geom.x;
1455 if(vany){
1456 geom.h+=sub->geom.y-geom.y;
1457 geom.y=sub->geom.y;
1460 splittree_begin_resize();
1462 split_do_rqgeom_(sub, &geom, hany, vany, geomret, tryonly);
1464 if(!tryonly){
1465 split_do_resize(sub, geomret, hany, vany, FALSE);
1466 splittree_end_resize();
1467 *geomret=sub->geom;
1468 }else{
1469 saw_stdisp=NULL;
1474 /*EXTL_DOC
1475 * Attempt to resize and/or move the split tree starting at \var{node}.
1476 * Behaviour and the \var{g} parameter are as for \fnref{WRegion.rqgeom}
1477 * operating on \var{node} (if it were a \type{WRegion}).
1479 EXTL_EXPORT_MEMBER
1480 ExtlTab split_rqgeom(WSplit *node, ExtlTab g)
1482 WRectangle geom, ogeom;
1483 int flags=REGION_RQGEOM_WEAK_ALL;
1485 geom=node->geom;
1486 ogeom=geom;
1488 if(extl_table_gets_i(g, "x", &(geom.x)))
1489 flags&=~REGION_RQGEOM_WEAK_X;
1490 if(extl_table_gets_i(g, "y", &(geom.y)))
1491 flags&=~REGION_RQGEOM_WEAK_Y;
1492 if(extl_table_gets_i(g, "w", &(geom.w)))
1493 flags&=~REGION_RQGEOM_WEAK_W;
1494 if(extl_table_gets_i(g, "h", &(geom.h)))
1495 flags&=~REGION_RQGEOM_WEAK_H;
1497 geom.w=maxof(1, geom.w);
1498 geom.h=maxof(1, geom.h);
1500 splittree_rqgeom(node, flags, &geom, &ogeom);
1502 return extl_table_from_rectangle(&ogeom);
1504 err:
1505 warn(TR("Invalid node."));
1506 return extl_table_none();
1510 /*}}}*/
1513 /*{{{ Split */
1516 void splittree_changeroot(WSplit *root, WSplit *node)
1518 WTiling *ws=(WTiling*)(root->ws_if_root);
1520 assert(ws!=NULL);
1521 assert(ws->split_tree==root);
1522 root->ws_if_root=NULL;
1523 ws->split_tree=node;
1524 if(node!=NULL){
1525 node->ws_if_root=ws;
1526 node->parent=NULL;
1531 static void splitsplit_replace(WSplitSplit *split, WSplit *child,
1532 WSplit *what)
1534 assert(split->tl==child || split->br==child);
1536 if(split->tl==child)
1537 split->tl=what;
1538 else
1539 split->br=what;
1541 child->parent=NULL;
1543 what->parent=(WSplitInner*)split;
1544 what->ws_if_root=NULL; /* May not be needed. */
1548 void splitinner_replace(WSplitInner *split, WSplit *child, WSplit *what)
1550 CALL_DYN(splitinner_replace, split, (split, child, what));
1554 WSplitRegion *splittree_split(WSplit *node, int dir, WPrimn primn,
1555 int minsize, WRegionSimpleCreateFn *fn,
1556 WWindow *parent)
1558 int objmin, objmax;
1559 int s, sn, so, pos;
1560 WSplitSplit *nsplit;
1561 WSplitRegion *nnode;
1562 WSplitInner *psplit;
1563 WRegion *nreg;
1564 WFitParams fp;
1565 WRectangle ng, rg;
1567 assert(node!=NULL && parent!=NULL);
1569 splittree_begin_resize();
1571 node=dodge_stdisp(node, FALSE);
1573 if(node==NULL)
1574 return NULL;
1576 if(OBJ_IS(node, WSplitST)){
1577 warn(TR("Splitting the status display is not allowed."));
1578 return NULL;
1581 if(primn!=PRIMN_TL && primn!=PRIMN_BR)
1582 primn=PRIMN_BR;
1583 if(dir!=SPLIT_HORIZONTAL && dir!=SPLIT_VERTICAL)
1584 dir=SPLIT_VERTICAL;
1586 split_update_bounds(split_find_root(node), TRUE);
1587 objmin=(dir==SPLIT_VERTICAL ? node->min_h : node->min_w);
1589 s=split_size(node, dir);
1590 sn=maxof(minsize, s/2);
1591 so=maxof(objmin, s-sn);
1593 if(sn+so!=s){
1594 int rs;
1595 ng=node->geom;
1596 if(dir==SPLIT_VERTICAL)
1597 ng.h=sn+so;
1598 else
1599 ng.w=sn+so;
1600 split_do_rqgeom_(node, &ng, TRUE, TRUE, &rg, TRUE);
1601 rs=(dir==SPLIT_VERTICAL ? rg.h : rg.w);
1602 if(rs<minsize+objmin){
1603 warn(TR("Unable to split: not enough free space."));
1604 return NULL;
1606 split_do_rqgeom_(node, &ng, TRUE, TRUE, &rg, FALSE);
1607 rs=(dir==SPLIT_VERTICAL ? rg.h : rg.w);
1608 if(minsize>rs/2){
1609 sn=minsize;
1610 so=rs-sn;
1611 }else{
1612 so=maxof(rs/2, objmin);
1613 sn=rs-so;
1615 }else{
1616 rg=node->geom;
1617 splittree_scan_stdisp_rootward(node);
1620 /* Create split and new window
1622 fp.mode=REGION_FIT_EXACT;
1623 fp.g=rg;
1625 nsplit=create_splitsplit(&(fp.g), dir);
1627 if(nsplit==NULL)
1628 return NULL;
1630 if(dir==SPLIT_VERTICAL){
1631 if(primn==PRIMN_BR)
1632 fp.g.y+=so;
1633 fp.g.h=sn;
1634 }else{
1635 if(primn==PRIMN_BR)
1636 fp.g.x+=so;
1637 fp.g.w=sn;
1640 nreg=fn(parent, &fp);
1642 if(nreg==NULL){
1643 destroy_obj((Obj*)nsplit);
1644 return NULL;
1647 nnode=create_splitregion(&(fp.g), nreg);
1648 if(nnode==NULL){
1649 destroy_obj((Obj*)nreg);
1650 destroy_obj((Obj*)nsplit);
1651 return NULL;
1654 /* Now that everything's ok, resize and move original node.
1656 ng=rg;
1657 if(dir==SPLIT_VERTICAL){
1658 ng.h=so;
1659 if(primn==PRIMN_TL)
1660 ng.y+=sn;
1661 }else{
1662 ng.w=so;
1663 if(primn==PRIMN_TL)
1664 ng.x+=sn;
1667 split_do_resize(node, &ng,
1668 (dir==SPLIT_HORIZONTAL ? primn : PRIMN_ANY),
1669 (dir==SPLIT_VERTICAL ? primn : PRIMN_ANY),
1670 FALSE);
1672 /* Set up split structure
1674 psplit=node->parent;
1676 if(psplit!=NULL)
1677 splitinner_replace(psplit, node, (WSplit*)nsplit);
1678 else
1679 splittree_changeroot(node, (WSplit*)nsplit);
1681 node->parent=(WSplitInner*)nsplit;
1682 ((WSplit*)nnode)->parent=(WSplitInner*)nsplit;
1684 if(primn==PRIMN_BR){
1685 nsplit->tl=node;
1686 nsplit->br=(WSplit*)nnode;
1687 nsplit->current=SPLIT_CURRENT_TL;
1688 }else{
1689 nsplit->tl=(WSplit*)nnode;
1690 nsplit->br=node;
1691 nsplit->current=SPLIT_CURRENT_BR;
1694 splittree_end_resize();
1696 return nnode;
1700 /*}}}*/
1703 /*{{{ Remove */
1706 static void splitsplit_remove(WSplitSplit *node, WSplit *child,
1707 bool reclaim_space)
1709 static int nstdisp=0;
1710 WSplitInner *parent;
1711 WSplit *other;
1712 int hprimn=PRIMN_ANY, vprimn=PRIMN_ANY;
1714 assert(node->tl==child || node->br==child);
1716 if(node->tl==child){
1717 other=node->br;
1718 if(node->dir==SPLIT_VERTICAL)
1719 vprimn=PRIMN_TL;
1720 else
1721 hprimn=PRIMN_TL;
1722 }else{
1723 other=node->tl;
1724 if(node->dir==SPLIT_VERTICAL)
1725 vprimn=PRIMN_BR;
1726 else
1727 hprimn=PRIMN_BR;
1730 assert(other!=NULL);
1732 if(nstdisp==0 && reclaim_space && OBJ_IS(other, WSplitST)){
1733 /* Try to move stdisp out of the way. */
1734 split_try_unsink_stdisp(node, FALSE, TRUE);
1735 assert(child->parent!=NULL);
1736 nstdisp++;
1737 splitinner_remove(child->parent, child, reclaim_space);
1738 nstdisp--;
1739 return;
1742 parent=((WSplit*)node)->parent;
1744 if(parent!=NULL)
1745 splitinner_replace(parent, (WSplit*)node, other);
1746 else
1747 splittree_changeroot((WSplit*)node, other);
1749 if(reclaim_space)
1750 split_resize(other, &(((WSplit*)node)->geom), hprimn, vprimn);
1752 child->parent=NULL;
1754 node->tl=NULL;
1755 node->br=NULL;
1756 ((WSplit*)node)->parent=NULL;
1757 destroy_obj((Obj*)node);
1761 void splitinner_remove(WSplitInner *node, WSplit *child, bool reclaim_space)
1763 CALL_DYN(splitinner_remove, node, (node, child, reclaim_space));
1767 void splittree_remove(WSplit *node, bool reclaim_space)
1769 if(node->parent!=NULL)
1770 splitinner_remove(node->parent, node, reclaim_space);
1771 else if(node->ws_if_root!=NULL)
1772 splittree_changeroot(node, NULL);
1774 destroy_obj((Obj*)node);
1778 /*}}}*/
1781 /*{{{ Tree traversal */
1784 static bool defaultfilter(WSplit *node)
1786 return (OBJ_IS(node, WSplitRegion) &&
1787 ((WSplitRegion*)node)->reg!=NULL);
1791 static WSplit *split_current_todir_default(WSplit *node,
1792 WPrimn hprimn, WPrimn vprimn,
1793 WSplitFilter *filter)
1795 if(filter==NULL)
1796 filter=defaultfilter;
1798 return (filter(node) ? node : NULL);
1802 static WSplit *splitsplit_current_todir(WSplitSplit *node,
1803 WPrimn hprimn, WPrimn vprimn,
1804 WSplitFilter *filter)
1806 WPrimn primn=(node->dir==SPLIT_HORIZONTAL ? hprimn : vprimn);
1807 WSplit *first, *second, *ret;
1809 if(primn==PRIMN_TL ||
1810 (primn==PRIMN_ANY && node->current==SPLIT_CURRENT_TL)){
1811 first=node->tl;
1812 second=node->br;
1813 }else if(primn==PRIMN_BR ||
1814 (primn==PRIMN_ANY && node->current==SPLIT_CURRENT_BR)){
1815 first=node->br;
1816 second=node->tl;
1817 }else{
1818 return NULL;
1821 ret=split_current_todir(first, hprimn, vprimn, filter);
1822 if(ret==NULL)
1823 ret=split_current_todir(second, hprimn, vprimn, filter);
1824 if(ret==NULL && filter!=NULL){
1825 if(filter((WSplit*)node))
1826 ret=(WSplit*)node;
1829 return ret;
1833 WSplit *split_current_todir(WSplit *node, WPrimn hprimn, WPrimn vprimn,
1834 WSplitFilter *filter)
1836 WSplit *ret=NULL;
1837 CALL_DYN_RET(ret, WSplit*, split_current_todir, node,
1838 (node, hprimn, vprimn, filter));
1839 return ret;
1843 /* Note: both hprimn and vprimn are inverted when descending. Therefore
1844 * one should be either PRIMN_NONE or PRIMN_ANY for sensible geometric
1845 * navigation. (Both are PRIMN_TL or PRIMN_BR for pseudo-linear
1846 * next/previous navigation.)
1848 WSplit *splitsplit_nextto(WSplitSplit *node, WSplit *child,
1849 WPrimn hprimn, WPrimn vprimn,
1850 WSplitFilter *filter)
1852 WPrimn primn=(node->dir==SPLIT_HORIZONTAL ? hprimn : vprimn);
1853 WSplit *split=NULL, *nnode=NULL;
1855 if(node->tl==child && (primn==PRIMN_BR || primn==PRIMN_ANY))
1856 split=node->br;
1857 else if(node->br==child && (primn==PRIMN_TL || primn==PRIMN_ANY))
1858 split=node->tl;
1860 if(split!=NULL){
1861 nnode=split_current_todir(split,
1862 primn_none2any(primn_invert(hprimn)),
1863 primn_none2any(primn_invert(vprimn)),
1864 filter);
1867 if(nnode==NULL)
1868 nnode=split_nextto((WSplit*)node, hprimn, vprimn, filter);
1870 return nnode;
1874 WSplit *splitinner_nextto(WSplitInner *node, WSplit *child,
1875 WPrimn hprimn, WPrimn vprimn,
1876 WSplitFilter *filter)
1878 WSplit *ret=NULL;
1879 CALL_DYN_RET(ret, WSplit*, splitinner_nextto, node,
1880 (node, child, hprimn, vprimn, filter));
1881 return ret;
1885 WSplit *split_nextto(WSplit *node, WPrimn hprimn, WPrimn vprimn,
1886 WSplitFilter *filter)
1888 while(node->parent!=NULL){
1889 WSplit *ret=splitinner_nextto(node->parent, node,
1890 hprimn, vprimn, filter);
1891 if(ret!=NULL)
1892 return ret;
1893 node=(WSplit*)node->parent;
1895 return NULL;
1899 void splitinner_mark_current_default(WSplitInner *split, WSplit *child)
1901 if(((WSplit*)split)->parent!=NULL)
1902 splitinner_mark_current(((WSplit*)split)->parent, (WSplit*)split);
1906 void splitsplit_mark_current(WSplitSplit *split, WSplit *child)
1908 assert(child==split->tl || child==split->br);
1910 split->current=(split->tl==child ? SPLIT_CURRENT_TL : SPLIT_CURRENT_BR);
1912 splitinner_mark_current_default(&(split->isplit), child);
1916 void splitinner_mark_current(WSplitInner *split, WSplit *child)
1918 CALL_DYN(splitinner_mark_current, split, (split, child));
1922 static void splitsplit_forall(WSplitSplit *node, WSplitFn *fn)
1924 fn(node->tl);
1925 fn(node->br);
1929 void splitinner_forall(WSplitInner *node, WSplitFn *fn)
1931 CALL_DYN(splitinner_forall, node, (node, fn));
1935 static WSplit *splitsplit_current(WSplitSplit *split)
1937 return (split->current==SPLIT_CURRENT_TL ? split->tl : split->br);
1941 /*EXTL_DOC
1942 * Returns the most previously active child node of \var{split}.
1944 EXTL_SAFE
1945 EXTL_EXPORT_MEMBER
1946 WSplit *splitinner_current(WSplitInner *node)
1948 WSplit *ret=NULL;
1949 CALL_DYN_RET(ret, WSplit*, splitinner_current, node, (node));
1950 return ret;
1954 /*}}}*/
1957 /*{{{ X window handling */
1960 static void splitregion_stacking(WSplitRegion *split,
1961 Window *bottomret, Window *topret)
1963 *bottomret=None;
1964 *topret=None;
1965 if(split->reg!=NULL)
1966 region_stacking(split->reg, bottomret, topret);
1970 void splitsplit_stacking(WSplitSplit *split,
1971 Window *bottomret, Window *topret)
1973 Window tlb=None, tlt=None;
1974 Window brb=None, brt=None;
1976 split_stacking(split->tl, &tlb, &tlt);
1977 split_stacking(split->br, &brb, &brt);
1979 /* To make sure that this condition holds is left to the workspace
1980 * code to do after a split tree has been loaded or modified.
1982 if(split->current==SPLIT_CURRENT_TL){
1983 *topret=(tlt!=None ? tlt : brt);
1984 *bottomret=(brb!=None ? brb : tlb);
1985 }else{
1986 *topret=(brt!=None ? brt : tlt);
1987 *bottomret=(tlb!=None ? tlb : brb);
1991 void split_stacking(WSplit *split, Window *bottomret, Window *topret)
1993 *bottomret=None;
1994 *topret=None;
1996 CALL_DYN(split_stacking, split, (split, bottomret, topret));
2001 static void splitregion_restack(WSplitRegion *split, Window other, int mode)
2003 if(split->reg!=NULL)
2004 region_restack(split->reg, other, mode);
2007 void splitsplit_restack(WSplitSplit *split, Window other, int mode)
2009 Window bottom=None, top=None;
2010 WSplit *first, *second;
2012 if(split->current==SPLIT_CURRENT_TL){
2013 first=split->br;
2014 second=split->tl;
2015 }else{
2016 first=split->tl;
2017 second=split->br;
2020 split_restack(first, other, mode);
2021 split_stacking(first, &bottom, &top);
2022 if(top!=None){
2023 other=top;
2024 mode=Above;
2026 split_restack(second, other, mode);
2029 void split_restack(WSplit *split, Window other, int mode)
2031 CALL_DYN(split_restack, split, (split, other, mode));
2035 static void splitregion_map(WSplitRegion *split)
2037 if(split->reg!=NULL)
2038 region_map(split->reg);
2041 static void splitinner_map(WSplitInner *split)
2043 splitinner_forall(split, split_map);
2046 void split_map(WSplit *split)
2048 CALL_DYN(split_map, split, (split));
2052 static void splitregion_unmap(WSplitRegion *split)
2054 if(split->reg!=NULL)
2055 region_unmap(split->reg);
2058 static void splitinner_unmap(WSplitInner *split)
2060 splitinner_forall(split, split_unmap);
2063 void split_unmap(WSplit *split)
2065 CALL_DYN(split_unmap, split, (split));
2069 static void splitregion_reparent(WSplitRegion *split, WWindow *wwin)
2071 if(split->reg!=NULL){
2072 WRectangle g=split->split.geom;
2073 region_reparent(split->reg, wwin, &g, REGION_FIT_EXACT);
2078 static void splitsplit_reparent(WSplitSplit *split, WWindow *wwin)
2080 if(split->current==SPLIT_CURRENT_TL){
2081 split_reparent(split->br, wwin);
2082 split_reparent(split->tl, wwin);
2083 }else{
2084 split_reparent(split->tl, wwin);
2085 split_reparent(split->br, wwin);
2090 void split_reparent(WSplit *split, WWindow *wwin)
2092 CALL_DYN(split_reparent, split, (split, wwin));
2096 /*}}}*/
2099 /*{{{ Transpose, flip, rotate */
2102 void splitsplit_flip_default(WSplitSplit *split)
2104 WRectangle tlng, brng;
2105 WRectangle *sg=&((WSplit*)split)->geom;
2106 WSplit *tmp;
2108 assert(split->tl!=NULL && split->br!=NULL);
2110 split_update_bounds((WSplit*)split, TRUE);
2112 tlng=split->tl->geom;
2113 brng=split->br->geom;
2115 if(split->dir==SPLIT_HORIZONTAL){
2116 brng.x=sg->x;
2117 tlng.x=sg->x+sg->w-tlng.w;
2118 }else{
2119 brng.y=sg->y;
2120 tlng.y=sg->y+sg->h-tlng.h;
2123 tmp=split->tl;
2124 split->tl=split->br;
2125 split->br=tmp;
2126 split->current=(split->current==SPLIT_CURRENT_TL
2127 ? SPLIT_CURRENT_BR
2128 : SPLIT_CURRENT_TL);
2130 split_do_resize(split->tl, &brng, PRIMN_ANY, PRIMN_ANY, FALSE);
2131 split_do_resize(split->br, &tlng, PRIMN_ANY, PRIMN_ANY, FALSE);
2135 static void splitsplit_flip_(WSplitSplit *split)
2137 CALL_DYN(splitsplit_flip, split, (split));
2141 /*EXTL_DOC
2142 * Flip contents of \var{split}.
2144 EXTL_EXPORT_MEMBER
2145 void splitsplit_flip(WSplitSplit *split)
2147 splittree_begin_resize();
2149 split=OBJ_CAST(dodge_stdisp((WSplit*)split, FALSE), WSplitSplit);
2151 if(split==NULL)
2152 return;
2154 splitsplit_flip_(split);
2156 splittree_end_resize();
2159 typedef enum{
2160 FLIP_VERTICAL,
2161 FLIP_HORIZONTAL,
2162 FLIP_NONE,
2163 FLIP_ANY
2164 } FlipDir;
2167 static FlipDir flipdir=FLIP_VERTICAL;
2170 static void do_flip(WSplit *split)
2172 WSplitSplit *ss=OBJ_CAST(split, WSplitSplit);
2174 if(ss!=NULL){
2175 if((flipdir==FLIP_ANY
2176 || (ss->dir==SPLIT_VERTICAL && flipdir==FLIP_VERTICAL)
2177 || (ss->dir==SPLIT_HORIZONTAL && flipdir==FLIP_HORIZONTAL))
2178 && !OBJ_IS(ss->tl, WSplitST)
2179 && !OBJ_IS(ss->br, WSplitST)){
2180 splitsplit_flip_(ss);
2184 if(OBJ_IS(ss, WSplitInner))
2185 splitinner_forall((WSplitInner*)ss, do_flip);
2189 static void splittree_flip_dir(WSplit *splittree, FlipDir dir)
2191 /* todo stdisp outta way */
2192 if(OBJ_IS(splittree, WSplitInner)){
2193 flipdir=dir;
2194 splitinner_forall((WSplitInner*)splittree, do_flip);
2199 static bool split_fliptrans_to(WSplit *node, const WRectangle *geom,
2200 bool trans, FlipDir flip)
2202 WRectangle rg;
2203 WSplit *node2;
2205 splittree_begin_resize();
2207 /* split_do_resize can do things right if 'node' has stdisp as child,
2208 * but otherwise transpose will put the stdisp in a bad split
2209 * configuration if it is contained within 'node', so we must
2210 * first move it and its fixed parent split below node. For correct
2211 * geometry calculation we move it immediately below node, and
2212 * resize stdisp's fixed parent node instead.
2214 node2=dodge_stdisp(node, TRUE);
2216 if(node==NULL || node2!=node)
2217 return FALSE;
2219 split_update_bounds(node, TRUE);
2221 split_do_rqgeom_(node, geom, PRIMN_ANY, PRIMN_ANY, &rg, FALSE);
2223 split_do_resize(node, &rg, PRIMN_ANY, PRIMN_ANY, trans);
2225 if(flip!=FLIP_NONE)
2226 splittree_flip_dir(node, flip);
2228 splittree_end_resize();
2230 return TRUE;
2234 bool split_transpose_to(WSplit *node, const WRectangle *geom)
2236 return split_fliptrans_to(node, geom, TRUE, FLIP_ANY);
2240 /*EXTL_DOC
2241 * Transpose contents of \var{node}.
2243 EXTL_EXPORT_MEMBER
2244 void split_transpose(WSplit *node)
2246 WRectangle g=node->geom;
2248 split_transpose_to(node, &g);
2252 bool split_rotate_to(WSplit *node, const WRectangle *geom, int rotation)
2254 FlipDir flip=FLIP_NONE;
2255 bool trans=FALSE;
2257 if(rotation==SCREEN_ROTATION_90){
2258 flip=FLIP_HORIZONTAL;
2259 trans=TRUE;
2260 }else if(rotation==SCREEN_ROTATION_180){
2261 flip=FLIP_ANY;
2262 }else if(rotation==SCREEN_ROTATION_270){
2263 flip=FLIP_VERTICAL;
2264 trans=TRUE;
2267 return split_fliptrans_to(node, geom, trans, flip);
2270 /*}}}*/
2273 /*{{{ Exports */
2276 /*EXTL_DOC
2277 * Return parent split for \var{split}.
2279 EXTL_SAFE
2280 EXTL_EXPORT_MEMBER
2281 WSplitInner *split_parent(WSplit *split)
2283 return split->parent;
2287 /*EXTL_DOC
2288 * Returns the area of workspace used by the regions under \var{split}.
2290 EXTL_SAFE
2291 EXTL_EXPORT_MEMBER
2292 ExtlTab split_geom(WSplit *split)
2294 return extl_table_from_rectangle(&(split->geom));
2298 /*EXTL_DOC
2299 * Returns the top or left child node of \var{split} depending
2300 * on the direction of the split.
2302 EXTL_SAFE
2303 EXTL_EXPORT_MEMBER
2304 WSplit *splitsplit_tl(WSplitSplit *split)
2306 return split->tl;
2310 /*EXTL_DOC
2311 * Returns the bottom or right child node of \var{split} depending
2312 * on the direction of the split.
2314 EXTL_SAFE
2315 EXTL_EXPORT_MEMBER
2316 WSplit *splitsplit_br(WSplitSplit *split)
2318 return split->br;
2321 /*EXTL_DOC
2322 * Returns the direction of \var{split}; either \codestr{vertical} or
2323 * \codestr{horizontal}.
2325 EXTL_SAFE
2326 EXTL_EXPORT_MEMBER
2327 const char *splitsplit_dir(WSplitSplit *split)
2329 return (split->dir==SPLIT_VERTICAL ? "vertical" : "horizontal");
2333 /*EXTL_DOC
2334 * Returns the region contained in \var{node}.
2336 EXTL_SAFE
2337 EXTL_EXPORT_MEMBER
2338 WRegion *splitregion_reg(WSplitRegion *node)
2340 return node->reg;
2344 /*}}}*/
2347 /*{{{ Save support */
2350 ExtlTab split_base_config(WSplit *node)
2352 ExtlTab t=extl_create_table();
2353 extl_table_sets_s(t, "type", OBJ_TYPESTR(node));
2354 return t;
2358 static bool splitregion_get_config(WSplitRegion *node, ExtlTab *ret)
2360 ExtlTab rt, t;
2362 if(node->reg==NULL)
2363 return FALSE;
2365 if(!region_supports_save(node->reg)){
2366 warn(TR("Unable to get configuration for %s."),
2367 region_name(node->reg));
2368 return FALSE;
2371 rt=region_get_configuration(node->reg);
2372 t=split_base_config(&(node->split));
2373 extl_table_sets_t(t, "regparams", rt);
2374 extl_unref_table(rt);
2375 *ret=t;
2377 return TRUE;
2381 static bool splitst_get_config(WSplitST *node, ExtlTab *ret)
2383 *ret=split_base_config((WSplit*)node);
2384 return TRUE;
2388 static bool splitsplit_get_config(WSplitSplit *node, ExtlTab *ret)
2390 ExtlTab tab, tltab, brtab;
2391 int tls, brs;
2393 if(!split_get_config(node->tl, &tltab))
2394 return split_get_config(node->br, ret);
2396 if(!split_get_config(node->br, &brtab)){
2397 *ret=tltab;
2398 return TRUE;
2401 tab=split_base_config((WSplit*)node);
2403 tls=split_size(node->tl, node->dir);
2404 brs=split_size(node->br, node->dir);
2406 extl_table_sets_s(tab, "dir", (node->dir==SPLIT_VERTICAL
2407 ? "vertical" : "horizontal"));
2409 extl_table_sets_i(tab, "tls", tls);
2410 extl_table_sets_t(tab, "tl", tltab);
2411 extl_unref_table(tltab);
2413 extl_table_sets_i(tab, "brs", brs);
2414 extl_table_sets_t(tab, "br", brtab);
2415 extl_unref_table(brtab);
2417 *ret=tab;
2419 return TRUE;
2423 bool split_get_config(WSplit *node, ExtlTab *tabret)
2425 bool ret=FALSE;
2426 CALL_DYN_RET(ret, bool, split_get_config, node, (node, tabret));
2427 return ret;
2431 /*}}}*/
2434 /*{{{ The classes */
2437 static DynFunTab split_dynfuntab[]={
2438 {split_do_resize, split_do_resize_default},
2439 {(DynFun*)split_current_todir, (DynFun*)split_current_todir_default},
2440 END_DYNFUNTAB,
2443 static DynFunTab splitinner_dynfuntab[]={
2444 {splitinner_mark_current, splitinner_mark_current_default},
2445 {split_map, splitinner_map},
2446 {split_unmap, splitinner_unmap},
2447 END_DYNFUNTAB,
2450 static DynFunTab splitsplit_dynfuntab[]={
2451 {split_update_bounds, splitsplit_update_bounds},
2452 {split_do_resize, splitsplit_do_resize},
2453 {split_do_maxhelper, splitsplit_do_maxhelper},
2454 {(DynFun*)split_do_restore, (DynFun*)splitsplit_do_restore},
2455 {(DynFun*)split_do_verify, (DynFun*)splitsplit_do_verify},
2456 {splitinner_do_rqsize, splitsplit_do_rqsize},
2457 {splitinner_replace, splitsplit_replace},
2458 {splitinner_remove, splitsplit_remove},
2459 {(DynFun*)split_current_todir, (DynFun*)splitsplit_current_todir},
2460 {(DynFun*)splitinner_current, (DynFun*)splitsplit_current},
2461 {(DynFun*)splitinner_nextto, (DynFun*)splitsplit_nextto},
2462 {splitinner_mark_current, splitsplit_mark_current},
2463 {(DynFun*)split_get_config, (DynFun*)splitsplit_get_config},
2464 {splitinner_forall, splitsplit_forall},
2465 {split_restack, splitsplit_restack},
2466 {split_stacking, splitsplit_stacking},
2467 {split_reparent, splitsplit_reparent},
2468 {splitsplit_flip, splitsplit_flip_default},
2469 END_DYNFUNTAB,
2472 static DynFunTab splitregion_dynfuntab[]={
2473 {split_update_bounds, splitregion_update_bounds},
2474 {split_do_resize, splitregion_do_resize},
2475 {split_do_maxhelper, splitregion_do_maxhelper},
2476 {(DynFun*)split_do_restore, (DynFun*)splitregion_do_restore},
2477 {(DynFun*)split_do_verify, (DynFun*)splitregion_do_verify},
2478 {(DynFun*)split_get_config, (DynFun*)splitregion_get_config},
2479 {split_map, splitregion_map},
2480 {split_unmap, splitregion_unmap},
2481 {split_restack, splitregion_restack},
2482 {split_stacking, splitregion_stacking},
2483 {split_reparent, splitregion_reparent},
2484 END_DYNFUNTAB,
2487 static DynFunTab splitst_dynfuntab[]={
2488 {split_update_bounds, splitst_update_bounds},
2489 {split_do_resize, splitst_do_resize},
2490 {split_do_maxhelper, splitst_do_maxhelper},
2491 {(DynFun*)split_do_restore, (DynFun*)splitst_do_restore},
2492 {(DynFun*)split_do_verify, (DynFun*)splitst_do_verify},
2493 {(DynFun*)split_get_config, (DynFun*)splitst_get_config},
2494 END_DYNFUNTAB,
2498 EXTL_EXPORT
2499 IMPLCLASS(WSplit, Obj, split_deinit, split_dynfuntab);
2501 EXTL_EXPORT
2502 IMPLCLASS(WSplitInner, WSplit, splitinner_deinit, splitinner_dynfuntab);
2504 EXTL_EXPORT
2505 IMPLCLASS(WSplitSplit, WSplitInner, splitsplit_deinit, splitsplit_dynfuntab);
2507 EXTL_EXPORT
2508 IMPLCLASS(WSplitRegion, WSplit, splitregion_deinit, splitregion_dynfuntab);
2510 EXTL_EXPORT
2511 IMPLCLASS(WSplitST, WSplitRegion, splitst_deinit, splitst_dynfuntab);
2514 /*}}}*/