Use explicit window names for WFrames
[notion/jeffpc.git] / mod_tiling / tiling.c
blob35fa8c905dcaa5e0abc0075599898e5d6db292b1
1 /*
2 * ion/mod_tiling/tiling.c
4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
7 */
9 #include <string.h>
11 #include <X11/Xatom.h>
13 #include <libtu/objp.h>
14 #include <libtu/minmax.h>
15 #include <libtu/ptrlist.h>
16 #include <libmainloop/defer.h>
17 #include <libmainloop/signal.h>
19 #include <ioncore/common.h>
20 #include <ioncore/rootwin.h>
21 #include <ioncore/focus.h>
22 #include <ioncore/global.h>
23 #include <ioncore/region.h>
24 #include <ioncore/manage.h>
25 #include <ioncore/screen.h>
26 #include <ioncore/names.h>
27 #include <ioncore/saveload.h>
28 #include <ioncore/attach.h>
29 #include <ioncore/resize.h>
30 #include <libextl/extl.h>
31 #include <ioncore/regbind.h>
32 #include <ioncore/extlconv.h>
33 #include <ioncore/xwindow.h>
34 #include <ioncore/navi.h>
35 #include <ioncore/property.h>
36 #include "placement.h"
37 #include "tiling.h"
38 #include "split.h"
39 #include "splitfloat.h"
40 #include "split-stdisp.h"
41 #include "main.h"
45 static WTilingIterTmp tiling_iter_default_tmp;
48 /*{{{ Some helper routines */
51 static WSplitRegion *get_node_check(WTiling *ws, WRegion *reg)
53 WSplitRegion *node;
55 if(reg==NULL)
56 return NULL;
58 node=splittree_node_of(reg);
60 if(node==NULL || REGION_MANAGER(reg)!=(WRegion*)ws)
61 return NULL;
63 return node;
67 static bool check_node(WTiling *ws, WSplit *split)
69 if(split->parent)
70 return check_node(ws, (WSplit*)split->parent);
72 if((split->ws_if_root!=(void*)ws)){
73 warn(TR("Split not on workspace."));
74 return FALSE;
76 return TRUE;
80 /*}}}*/
83 /*{{{ Dynfun implementations */
86 static void reparent_mgd(WRegion *sub, WWindow *par)
88 WFitParams subfp;
89 subfp.g=REGION_GEOM(sub);
90 subfp.mode=REGION_FIT_EXACT;
91 if(!region_fitrep(sub, par, &subfp)){
92 warn(TR("Error reparenting %s."), region_name(sub));
93 region_detach_manager(sub);
98 bool tiling_fitrep(WTiling *ws, WWindow *par, const WFitParams *fp)
100 WTilingIterTmp tmp;
101 bool ok=FALSE;
103 if(par!=NULL){
104 if(!region_same_rootwin((WRegion*)ws, (WRegion*)par))
105 return FALSE;
107 region_unset_parent((WRegion*)ws);
109 XReparentWindow(ioncore_g.dpy, ws->dummywin,
110 par->win, fp->g.x, fp->g.y);
112 region_set_parent((WRegion*)ws, par);
114 if(ws->split_tree!=NULL)
115 split_reparent(ws->split_tree, par);
118 REGION_GEOM(ws)=fp->g;
120 if(ws->split_tree!=NULL){
121 bool done=FALSE;
122 if(fp->mode&REGION_FIT_ROTATE)
123 ok=split_rotate_to(ws->split_tree, &(fp->g), fp->rotation);
124 if(!ok)
125 split_resize(ws->split_tree, &(fp->g), PRIMN_ANY, PRIMN_ANY);
128 return TRUE;
132 void tiling_managed_rqgeom(WTiling *ws, WRegion *mgd,
133 const WRQGeomParams *rq,
134 WRectangle *geomret)
136 WSplitRegion *node=get_node_check(ws, mgd);
137 if(node!=NULL && ws->split_tree!=NULL)
138 splittree_rqgeom((WSplit*)node, rq->flags, &rq->geom, geomret);
141 bool tiling_managed_maximize(WTiling *ws, WRegion *mgd, int dir, int action)
143 WSplitRegion *node=get_node_check(ws, mgd);
144 bool ret;
145 if(node!=NULL && ws->split_tree!=NULL){
146 ret=split_maximize((WSplit*)node, dir, action);
147 if(action==RESTORE && ret)
148 split_regularise_stdisp(ws->stdispnode);
149 return ret;
151 else
152 return FALSE;
156 void tiling_map(WTiling *ws)
158 REGION_MARK_MAPPED(ws);
159 XMapWindow(ioncore_g.dpy, ws->dummywin);
161 if(ws->split_tree!=NULL)
162 split_map(ws->split_tree);
166 void tiling_unmap(WTiling *ws)
168 REGION_MARK_UNMAPPED(ws);
169 XUnmapWindow(ioncore_g.dpy, ws->dummywin);
171 if(ws->split_tree!=NULL)
172 split_unmap(ws->split_tree);
176 void tiling_fallback_focus(WTiling *ws, bool warp)
178 region_finalise_focusing((WRegion*)ws, ws->dummywin, warp, CurrentTime, TRUE);
182 void tiling_do_set_focus(WTiling *ws, bool warp)
184 WRegion *sub=tiling_current(ws);
186 if(sub==NULL){
187 tiling_fallback_focus(ws, warp);
188 return;
191 region_do_set_focus(sub, warp);
195 static WTimer *restack_timer=NULL;
198 static void restack_handler(WTimer *tmr, Obj *obj)
200 if(obj!=NULL){
201 WTiling *ws=(WTiling*)obj;
202 split_restack(ws->split_tree, ws->dummywin, Above);
207 bool tiling_managed_prepare_focus(WTiling *ws, WRegion *reg,
208 int flags, WPrepareFocusResult *res)
210 WSplitRegion *node;
212 if(!region_prepare_focus((WRegion*)ws, flags, res))
213 return FALSE;
215 node=get_node_check(ws, reg);
217 if(node!=NULL && node->split.parent!=NULL)
218 splitinner_mark_current(node->split.parent, &(node->split));
220 /* WSplitSplit uses activity based stacking as required on WAutoWS,
221 * so we must restack here.
223 if(ws->split_tree!=NULL){
224 int rd=mod_tiling_raise_delay;
225 bool use_timer=rd>0 && flags&REGION_GOTO_ENTERWINDOW;
227 if(use_timer){
228 if(restack_timer!=NULL){
229 Obj *obj=restack_timer->objwatch.obj;
230 if(obj!=(Obj*)ws){
231 timer_reset(restack_timer);
232 restack_handler(restack_timer, obj);
234 }else{
235 restack_timer=create_timer();
239 if(use_timer && restack_timer!=NULL){
240 timer_set(restack_timer, rd, restack_handler, (Obj*)ws);
241 }else{
242 split_restack(ws->split_tree, ws->dummywin, Above);
246 res->reg=reg;
247 res->flags=flags;
248 return TRUE;
253 void tiling_restack(WTiling *ws, Window other, int mode)
255 xwindow_restack(ws->dummywin, other, mode);
256 if(ws->split_tree!=NULL)
257 split_restack(ws->split_tree, ws->dummywin, Above);
261 void tiling_stacking(WTiling *ws, Window *bottomret, Window *topret)
263 Window sbottom=None, stop=None;
265 if(ws->split_tree!=None)
266 split_stacking(ws->split_tree, &sbottom, &stop);
268 *bottomret=ws->dummywin;
269 *topret=(stop!=None ? stop : ws->dummywin);
273 Window tiling_xwindow(const WTiling *ws)
275 return ws->dummywin;
279 /*}}}*/
282 /*{{{ Status display support code */
285 static bool regnodefilter(WSplit *split)
287 return OBJ_IS(split, WSplitRegion);
291 void tiling_unmanage_stdisp(WTiling *ws, bool permanent, bool nofocus)
293 WSplitRegion *tofocus=NULL;
294 bool setfocus=FALSE;
295 WRegion *od;
297 if(ws->stdispnode==NULL)
298 return;
300 od=ws->stdispnode->regnode.reg;
302 if(od!=NULL){
303 if(!nofocus && REGION_IS_ACTIVE(od) &&
304 region_may_control_focus((WRegion*)ws)){
305 setfocus=TRUE;
306 tofocus=(WSplitRegion*)split_nextto((WSplit*)(ws->stdispnode),
307 PRIMN_ANY, PRIMN_ANY,
308 regnodefilter);
310 /* Reset node_of info here so tiling_managed_remove will not
311 * remove the node.
313 splittree_set_node_of(od, NULL);
314 tiling_do_managed_remove(ws, od);
317 if(permanent){
318 WSplit *node=(WSplit*)ws->stdispnode;
319 ws->stdispnode=NULL;
320 splittree_remove(node, TRUE);
323 if(setfocus){
324 if(tofocus!=NULL)
325 region_set_focus(tofocus->reg);
326 else
327 tiling_fallback_focus(ws, FALSE);
332 static void tiling_create_stdispnode(WTiling *ws, WRegion *stdisp,
333 int corner, int orientation,
334 bool fullsize)
336 int flags=REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y;
337 WRectangle *wg=&REGION_GEOM(ws), dg;
338 WSplitST *stdispnode;
339 WSplitSplit *split;
341 assert(ws->split_tree!=NULL);
343 if(orientation==REGION_ORIENTATION_HORIZONTAL){
344 dg.x=wg->x;
345 dg.w=wg->w;
346 dg.h=0;
347 dg.y=((corner==MPLEX_STDISP_BL || corner==MPLEX_STDISP_BR)
348 ? wg->y+wg->h
349 : 0);
350 }else{
351 dg.y=wg->y;
352 dg.h=wg->h;
353 dg.w=0;
354 dg.x=((corner==MPLEX_STDISP_TR || corner==MPLEX_STDISP_BR)
355 ? wg->x+wg->w
356 : 0);
359 stdispnode=create_splitst(&dg, stdisp);
361 if(stdispnode==NULL){
362 warn(TR("Unable to create a node for status display."));
363 return;
366 stdispnode->corner=corner;
367 stdispnode->orientation=orientation;
368 stdispnode->fullsize=fullsize;
370 split=create_splitsplit(wg, (orientation==REGION_ORIENTATION_HORIZONTAL
371 ? SPLIT_VERTICAL
372 : SPLIT_HORIZONTAL));
374 if(split==NULL){
375 warn(TR("Unable to create new split for status display."));
376 stdispnode->regnode.reg=NULL;
377 destroy_obj((Obj*)stdispnode);
378 return;
381 /* Set up new split tree */
382 ((WSplit*)stdispnode)->parent=(WSplitInner*)split;
383 ws->split_tree->parent=(WSplitInner*)split;
384 ws->split_tree->ws_if_root=NULL;
386 if((orientation==REGION_ORIENTATION_HORIZONTAL &&
387 (corner==MPLEX_STDISP_BL || corner==MPLEX_STDISP_BR)) ||
388 (orientation==REGION_ORIENTATION_VERTICAL &&
389 (corner==MPLEX_STDISP_TR || corner==MPLEX_STDISP_BR))){
390 split->tl=ws->split_tree;
391 split->br=(WSplit*)stdispnode;
392 split->current=SPLIT_CURRENT_TL;
393 }else{
394 split->tl=(WSplit*)stdispnode;
395 split->br=ws->split_tree;
396 split->current=SPLIT_CURRENT_BR;
399 ws->split_tree=(WSplit*)split;
400 ((WSplit*)split)->ws_if_root=ws;
401 ws->stdispnode=stdispnode;
405 void tiling_manage_stdisp(WTiling *ws, WRegion *stdisp,
406 const WMPlexSTDispInfo *di)
408 bool mcf=region_may_control_focus((WRegion*)ws);
409 int flags=REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y;
410 int orientation=region_orientation(stdisp);
411 bool act=FALSE;
412 WRectangle dg, *stdg;
414 if(orientation!=REGION_ORIENTATION_VERTICAL /*&&
415 orientation!=REGION_ORIENTATION_HORIZONTAL*/){
416 orientation=REGION_ORIENTATION_HORIZONTAL;
419 if(ws->stdispnode==NULL || ws->stdispnode->regnode.reg!=stdisp)
420 region_detach_manager(stdisp);
422 /* Remove old stdisp if corner and orientation don't match.
424 if(ws->stdispnode!=NULL && (di->pos!=ws->stdispnode->corner ||
425 orientation!=ws->stdispnode->orientation)){
426 tiling_unmanage_stdisp(ws, TRUE, TRUE);
429 if(ws->stdispnode==NULL){
430 tiling_create_stdispnode(ws, stdisp, di->pos, orientation,
431 di->fullsize);
432 if(ws->stdispnode==NULL)
433 return;
434 }else{
435 WRegion *od=ws->stdispnode->regnode.reg;
436 if(od!=NULL){
437 act=REGION_IS_ACTIVE(od);
438 splittree_set_node_of(od, NULL);
439 tiling_managed_remove(ws, od);
440 assert(ws->stdispnode->regnode.reg==NULL);
443 ws->stdispnode->fullsize=di->fullsize;
444 ws->stdispnode->regnode.reg=stdisp;
445 splittree_set_node_of(stdisp, &(ws->stdispnode->regnode));
448 if(!tiling_managed_add(ws, stdisp)){
449 tiling_unmanage_stdisp(ws, TRUE, TRUE);
450 return;
453 stdisp->flags|=REGION_SKIP_FOCUS;
455 dg=((WSplit*)(ws->stdispnode))->geom;
457 dg.h=stdisp_recommended_h(ws->stdispnode);
458 dg.w=stdisp_recommended_w(ws->stdispnode);
460 splittree_rqgeom((WSplit*)(ws->stdispnode), flags, &dg, FALSE);
462 stdg=&(((WSplit*)ws->stdispnode)->geom);
464 if(stdisp->geom.x!=stdg->x || stdisp->geom.y!=stdg->y ||
465 stdisp->geom.w!=stdg->w || stdisp->geom.h!=stdg->h){
466 region_fit(stdisp, stdg, REGION_FIT_EXACT);
469 /* Restack to ensure the split tree is stacked in the expected order. */
470 if(ws->split_tree!=NULL)
471 split_restack(ws->split_tree, ws->dummywin, Above);
473 if(mcf && act)
474 region_set_focus(stdisp);
478 /*}}}*/
481 /*{{{ Create/destroy */
484 bool tiling_managed_add_default(WTiling *ws, WRegion *reg)
486 Window bottom=None, top=None;
487 WFrame *frame;
489 if(TILING_STDISP_OF(ws)!=reg){
490 if(!ptrlist_insert_last(&(ws->managed_list), reg))
491 return FALSE;
494 region_set_manager(reg, (WRegion*)ws);
496 frame=OBJ_CAST(reg, WFrame);
497 if(frame!=NULL){
498 if(framemode_unalt(frame_mode(frame))!=FRAME_MODE_TILED)
499 frame_set_mode(frame, FRAME_MODE_TILED);
502 if(REGION_IS_MAPPED(ws))
503 region_map(reg);
505 if(region_may_control_focus((WRegion*)ws)){
506 WRegion *curr=tiling_current(ws);
507 if(curr==NULL || !REGION_IS_ACTIVE(curr))
508 region_warp(reg);
511 return TRUE;
515 bool tiling_managed_add(WTiling *ws, WRegion *reg)
517 bool ret=FALSE;
518 CALL_DYN_RET(ret, bool, tiling_managed_add, ws, (ws, reg));
519 return ret;
523 bool tiling_do_attach_initial(WTiling *ws, WRegion *reg)
525 assert(ws->split_tree==NULL);
527 ws->split_tree=(WSplit*)create_splitregion(&REGION_GEOM(reg), reg);
528 if(ws->split_tree==NULL)
529 return FALSE;
531 ws->split_tree->ws_if_root=ws;
533 if(!tiling_managed_add(ws, reg)){
534 destroy_obj((Obj*)ws->split_tree);
535 ws->split_tree=NULL;
536 return FALSE;
539 return TRUE;
543 static WRegion *create_frame_tiling(WWindow *parent, const WFitParams *fp)
545 return (WRegion*)create_frame(parent, fp, FRAME_MODE_TILED, "Tiling Frame");
549 bool tiling_init(WTiling *ws, WWindow *parent, const WFitParams *fp,
550 WRegionSimpleCreateFn *create_frame_fn, bool ci)
552 const char *p[1];
554 ws->split_tree=NULL;
555 ws->create_frame_fn=(create_frame_fn
556 ? create_frame_fn
557 : create_frame_tiling);
558 ws->stdispnode=NULL;
559 ws->managed_list=NULL;
560 ws->batchop=FALSE;
562 ws->dummywin=XCreateWindow(ioncore_g.dpy, parent->win,
563 fp->g.x, fp->g.y, 1, 1, 0,
564 CopyFromParent, InputOnly,
565 CopyFromParent, 0, NULL);
566 if(ws->dummywin==None)
567 return FALSE;
569 p[0] = "Notion WTiling dummy window";
570 xwindow_set_text_property(ws->dummywin, XA_WM_NAME, p, 1);
572 region_init(&(ws->reg), parent, fp);
574 ws->reg.flags|=(REGION_GRAB_ON_PARENT|
575 REGION_PLEASE_WARP);
577 if(ci){
578 WRegionAttachData data;
579 WRegion *res;
581 data.type=REGION_ATTACH_NEW;
582 data.u.n.fn=(WRegionCreateFn*)ws->create_frame_fn;
583 data.u.n.param=NULL;
585 res=region_attach_helper((WRegion*)ws, parent, fp,
586 (WRegionDoAttachFn*)tiling_do_attach_initial,
587 NULL, &data);
589 if(res==NULL){
590 XDestroyWindow(ioncore_g.dpy, ws->dummywin);
591 return FALSE;
595 XSelectInput(ioncore_g.dpy, ws->dummywin,
596 FocusChangeMask|KeyPressMask|KeyReleaseMask|
597 ButtonPressMask|ButtonReleaseMask);
598 XSaveContext(ioncore_g.dpy, ws->dummywin, ioncore_g.win_context,
599 (XPointer)ws);
601 region_register(&(ws->reg));
602 region_add_bindmap((WRegion*)ws, mod_tiling_tiling_bindmap);
604 return TRUE;
608 WTiling *create_tiling(WWindow *parent, const WFitParams *fp,
609 WRegionSimpleCreateFn *create_frame_fn, bool ci)
611 CREATEOBJ_IMPL(WTiling, tiling, (p, parent, fp, create_frame_fn, ci));
615 WTiling *create_tiling_simple(WWindow *parent, const WFitParams *fp)
617 return create_tiling(parent, fp, NULL, TRUE);
621 void tiling_deinit(WTiling *ws)
623 WRegion *reg;
624 WTilingIterTmp tmp;
625 WMPlex *remanage_mplex=NULL;
627 tiling_unmanage_stdisp(ws, FALSE, TRUE);
629 FOR_ALL_MANAGED_BY_TILING(reg, ws, tmp){
630 destroy_obj((Obj*)reg);
633 FOR_ALL_MANAGED_BY_TILING(reg, ws, tmp){
634 assert(FALSE);
637 if(ws->split_tree!=NULL)
638 destroy_obj((Obj*)(ws->split_tree));
640 XDeleteContext(ioncore_g.dpy, ws->dummywin, ioncore_g.win_context);
641 XDestroyWindow(ioncore_g.dpy, ws->dummywin);
642 ws->dummywin=None;
644 region_deinit(&(ws->reg));
648 WRegion *tiling_managed_disposeroot(WTiling *ws, WRegion *reg)
650 WTilingIterTmp tmp;
651 WRegion *mgd;
653 if(ws->batchop)
654 return reg;
656 FOR_ALL_MANAGED_BY_TILING(mgd, ws, tmp){
657 if(mgd!=TILING_STDISP_OF(ws) && mgd!=reg)
658 return reg;
661 return region_disposeroot((WRegion*)ws);
665 bool tiling_rescue_clientwins(WTiling *ws, WRescueInfo *info)
667 WTilingIterTmp tmp;
669 ptrlist_iter_init(&tmp, ws->managed_list);
671 return region_rescue_some_clientwins((WRegion*)ws, info,
672 (WRegionIterator*)ptrlist_iter,
673 &tmp);
677 void tiling_do_managed_remove(WTiling *ws, WRegion *reg)
679 if(TILING_STDISP_OF(ws)==reg){
680 ws->stdispnode->regnode.reg=NULL;
681 }else{
682 ptrlist_remove(&(ws->managed_list), reg);
685 region_unset_manager(reg, (WRegion*)ws);
686 splittree_set_node_of(reg, NULL);
690 static bool nostdispfilter(WSplit *node)
692 return (OBJ_IS(node, WSplitRegion) && !OBJ_IS(node, WSplitST));
696 void tiling_managed_remove(WTiling *ws, WRegion *reg)
698 bool act=REGION_IS_ACTIVE(reg);
699 bool mcf=region_may_control_focus((WRegion*)ws);
700 WSplitRegion *node=get_node_check(ws, reg);
701 bool norestore=(OBJ_IS_BEING_DESTROYED(ws) || ws->batchop);
702 WRegion *other=NULL;
704 if(!norestore)
705 other=tiling_do_navi_next(ws, reg, REGION_NAVI_ANY, TRUE, FALSE);
707 tiling_do_managed_remove(ws, reg);
709 if(node==(WSplitRegion*)(ws->stdispnode))
710 ws->stdispnode=NULL;
712 if(node!=NULL){
713 bool reused=FALSE;
715 if(other==NULL && !norestore){
716 WWindow *par=REGION_PARENT(ws);
717 WFitParams fp;
719 assert(par!=NULL);
721 fp.g=node->split.geom;
722 fp.mode=REGION_FIT_EXACT;
724 other=(ws->create_frame_fn)(par, &fp);
726 if(other!=NULL){
727 node->reg=other;
728 splittree_set_node_of(other, node);
729 tiling_managed_add(ws, other);
730 reused=TRUE;
731 }else{
732 warn(TR("Tiling in useless state."));
736 if(!reused)
737 splittree_remove((WSplit*)node, (!norestore && other!=NULL));
740 if(!norestore && other!=NULL && act && mcf)
741 region_warp(other);
745 static bool mplexfilter(WSplit *node)
747 WSplitRegion *regnode=OBJ_CAST(node, WSplitRegion);
749 return (regnode!=NULL && regnode->reg!=NULL &&
750 OBJ_IS(regnode->reg, WMPlex));
754 static WPHolder *find_ph_result=NULL;
755 static WRegion *find_ph_param=NULL;
758 static bool find_ph(WSplit *split)
760 WSplitRegion *sr=OBJ_CAST(split, WSplitRegion);
762 assert(find_ph_result==NULL);
764 if(sr==NULL || sr->reg==NULL)
765 return FALSE;
767 find_ph_result=region_get_rescue_pholder_for(sr->reg, find_ph_param);
769 return (find_ph_result!=NULL);
773 WPHolder *tiling_get_rescue_pholder_for(WTiling *ws, WRegion *mgd)
775 WSplit *node=(WSplit*)get_node_check(ws, mgd);
776 WPHolder *ph;
778 find_ph_result=NULL;
779 find_ph_param=mgd;
781 if(node==NULL){
782 if(ws->split_tree!=NULL){
783 split_current_todir(ws->split_tree, PRIMN_ANY, PRIMN_ANY,
784 find_ph);
786 }else{
787 while(node!=NULL){
788 split_nextto(node, PRIMN_ANY, PRIMN_ANY, find_ph);
789 if(find_ph_result!=NULL)
790 break;
791 node=(WSplit*)node->parent;
795 ph=find_ph_result;
796 find_ph_result=NULL;
797 find_ph_param=NULL;
799 return ph;
803 /*}}}*/
806 /*{{{ Navigation */
809 static void navi_to_primn(WRegionNavi nh, WPrimn *hprimn, WPrimn *vprimn,
810 WPrimn choice)
812 /* choice should be PRIMN_ANY or PRIMN_NONE */
814 switch(nh){
815 case REGION_NAVI_BEG:
816 *vprimn=PRIMN_TL;
817 *hprimn=PRIMN_TL;
818 break;
820 case REGION_NAVI_END:
821 *vprimn=PRIMN_BR;
822 *hprimn=PRIMN_BR;
823 break;
825 case REGION_NAVI_LEFT:
826 *hprimn=PRIMN_TL;
827 *vprimn=choice;
828 break;
830 case REGION_NAVI_RIGHT:
831 *hprimn=PRIMN_BR;
832 *vprimn=choice;
833 break;
835 case REGION_NAVI_TOP:
836 *hprimn=choice;
837 *vprimn=PRIMN_TL;
838 break;
840 case REGION_NAVI_BOTTOM:
841 *hprimn=choice;
842 *vprimn=PRIMN_BR;
843 break;
845 default:
846 case REGION_NAVI_ANY:
847 *hprimn=PRIMN_ANY;
848 *vprimn=PRIMN_ANY;
849 break;
854 static WRegion *node_reg(WSplit *node)
856 WSplitRegion *rnode=OBJ_CAST(node, WSplitRegion);
857 return (rnode!=NULL ? rnode->reg : NULL);
861 WRegion *tiling_do_navi_next(WTiling *ws, WRegion *reg,
862 WRegionNavi nh, bool nowrap,
863 bool any)
865 WSplitFilter *filter=(any ? NULL : nostdispfilter);
866 WPrimn hprimn, vprimn;
867 WRegion *nxt=NULL;
869 navi_to_primn(nh, &hprimn, &vprimn, PRIMN_NONE);
871 if(reg==NULL)
872 reg=tiling_current(ws);
874 if(reg!=NULL){
875 WSplitRegion *node=get_node_check(ws, reg);
876 if(node!=NULL){
877 nxt=node_reg(split_nextto((WSplit*)node, hprimn, vprimn,
878 filter));
882 if(nxt==NULL && !nowrap){
883 nxt=node_reg(split_current_todir(ws->split_tree,
884 primn_none2any(primn_invert(hprimn)),
885 primn_none2any(primn_invert(vprimn)),
886 filter));
889 return nxt;
893 WRegion *tiling_do_navi_first(WTiling *ws, WRegionNavi nh, bool any)
895 WSplitFilter *filter=(any ? NULL : nostdispfilter);
896 WPrimn hprimn, vprimn;
898 navi_to_primn(nh, &hprimn, &vprimn, PRIMN_ANY);
900 return node_reg(split_current_todir(ws->split_tree,
901 hprimn, vprimn, filter));
905 WRegion *tiling_navi_next(WTiling *ws, WRegion *reg,
906 WRegionNavi nh, WRegionNaviData *data)
908 WRegion *nxt=tiling_do_navi_next(ws, reg, nh, TRUE, FALSE);
910 return region_navi_cont(&ws->reg, nxt, data);
914 WRegion *tiling_navi_first(WTiling *ws, WRegionNavi nh,
915 WRegionNaviData *data)
917 WRegion *reg=tiling_do_navi_first(ws, nh, FALSE);
919 return region_navi_cont(&ws->reg, reg, data);
923 /*}}}*/
926 /*{{{ Split/unsplit */
929 static bool get_split_dir_primn(const char *str, int *dir, int *primn)
931 WPrimn hprimn, vprimn;
932 WRegionNavi nh;
934 if(!ioncore_string_to_navi(str, &nh))
935 return FALSE;
937 navi_to_primn(nh, &hprimn, &vprimn, PRIMN_NONE);
939 if(hprimn==PRIMN_NONE){
940 *dir=SPLIT_VERTICAL;
941 *primn=vprimn;
942 }else if(vprimn==PRIMN_NONE){
943 *dir=SPLIT_HORIZONTAL;
944 *primn=hprimn;
945 }else{
946 warn(TR("Invalid direction"));
947 return FALSE;
950 return TRUE;
954 static bool get_split_dir_primn_float(const char *str, int *dir, int *primn,
955 bool *floating)
957 if(strncmp(str, "floating:", 9)==0){
958 *floating=TRUE;
959 return get_split_dir_primn(str+9, dir, primn);
960 }else{
961 *floating=FALSE;
962 return get_split_dir_primn(str, dir, primn);
967 #define SPLIT_MINS 16 /* totally arbitrary */
970 static WFrame *tiling_do_split(WTiling *ws, WSplit *node,
971 const char *dirstr, int minw, int minh)
973 int dir, primn, mins;
974 bool floating=FALSE;
975 WFrame *newframe;
976 WSplitRegion *nnode;
978 if(node==NULL || ws->split_tree==NULL){
979 warn(TR("Invalid node."));
980 return NULL;
983 if(!get_split_dir_primn_float(dirstr, &dir, &primn, &floating))
984 return NULL;
986 mins=(dir==SPLIT_VERTICAL ? minh : minw);
988 if(!floating){
989 nnode=splittree_split(node, dir, primn, mins,
990 ws->create_frame_fn,
991 REGION_PARENT(ws));
992 }else{
993 nnode=splittree_split_floating(node, dir, primn, mins,
994 ws->create_frame_fn, ws);
997 if(nnode==NULL){
998 warn(TR("Unable to split."));
999 return NULL;
1002 /* We must restack here to ensure the split tree is stacked in the
1003 * expected order.
1005 if(ws->split_tree!=NULL)
1006 split_restack(ws->split_tree, ws->dummywin, Above);
1008 newframe=OBJ_CAST(nnode->reg, WFrame);
1009 assert(newframe!=NULL);
1011 if(!tiling_managed_add(ws, nnode->reg)){
1012 nnode->reg=NULL;
1013 destroy_obj((Obj*)nnode);
1014 destroy_obj((Obj*)newframe);
1015 return NULL;
1018 return newframe;
1022 /*EXTL_DOC
1023 * Create a new frame on \var{ws} \codestr{above}, \codestr{below}
1024 * \codestr{left} of, or \codestr{right} of \var{node} as indicated
1025 * by \var{dirstr}. If \var{dirstr} is prefixed with
1026 * \codestr{floating:} a floating split is created.
1028 EXTL_EXPORT_MEMBER
1029 WFrame *tiling_split(WTiling *ws, WSplit *node, const char *dirstr)
1031 if(!check_node(ws, node))
1032 return NULL;
1034 return tiling_do_split(ws, node, dirstr,
1035 SPLIT_MINS, SPLIT_MINS);
1039 /*EXTL_DOC
1040 * Same as \fnref{WTiling.split} at the root of the split tree.
1042 EXTL_EXPORT_MEMBER
1043 WFrame *tiling_split_top(WTiling *ws, const char *dirstr)
1045 return tiling_do_split(ws, ws->split_tree, dirstr,
1046 SPLIT_MINS, SPLIT_MINS);
1050 /*EXTL_DOC
1051 * Split \var{frame} creating a new frame to direction \var{dirstr}
1052 * (one of \codestr{left}, \codestr{right}, \codestr{top} or
1053 * \codestr{bottom}) of \var{frame}.
1054 * If \var{attach_current} is set, the region currently displayed in
1055 * \var{frame}, if any, is moved to thenew frame.
1056 * If \var{dirstr} is prefixed with \codestr{floating:}, a floating
1057 * split is created.
1059 EXTL_EXPORT_MEMBER
1060 WFrame *tiling_split_at(WTiling *ws, WFrame *frame, const char *dirstr,
1061 bool attach_current)
1063 WRegion *curr;
1064 WSplitRegion *node;
1065 WFrame *newframe;
1067 if(frame==NULL)
1068 return NULL;
1070 node=get_node_check(ws, (WRegion*)frame);
1072 newframe=tiling_do_split(ws, (WSplit*)node, dirstr,
1073 region_min_w((WRegion*)frame),
1074 region_min_h((WRegion*)frame));
1076 if(newframe==NULL)
1077 return NULL;
1079 curr=mplex_mx_current(&(frame->mplex));
1081 if(attach_current && curr!=NULL)
1082 mplex_attach_simple(&(newframe->mplex), curr, MPLEX_ATTACH_SWITCHTO);
1084 if(region_may_control_focus((WRegion*)frame))
1085 region_goto((WRegion*)newframe);
1087 return newframe;
1091 /*EXTL_DOC
1092 * Try to relocate regions managed by \var{reg} to another frame
1093 * and, if possible, destroy it.
1095 EXTL_EXPORT_MEMBER
1096 void tiling_unsplit_at(WTiling *ws, WRegion *reg)
1098 WPHolder *ph;
1100 if(reg==NULL || REGION_MANAGER(reg)!=(WRegion*)ws)
1101 return;
1103 ph=region_get_rescue_pholder_for((WRegion*)ws, reg);
1105 if(ph!=NULL){
1106 region_rescue(reg, ph, REGION_RESCUE_NODEEP|REGION_RESCUE_PHFLAGS_OK);
1107 destroy_obj((Obj*)ph);
1110 region_defer_rqdispose(reg);
1114 /*}}}*/
1117 /*{{{ Navigation etc. exports */
1120 WRegion *tiling_current(WTiling *ws)
1122 WSplitRegion *node=NULL;
1123 if(ws->split_tree!=NULL){
1124 node=(WSplitRegion*)split_current_todir(ws->split_tree,
1125 PRIMN_ANY, PRIMN_ANY, NULL);
1127 return (node ? node->reg : NULL);
1131 /*EXTL_DOC
1132 * Iterate over managed regions of \var{ws} until \var{iterfn} returns
1133 * \code{false}.
1134 * The function is called in protected mode.
1135 * This routine returns \code{true} if it reaches the end of list
1136 * without this happening.
1138 EXTL_SAFE
1139 EXTL_EXPORT_MEMBER
1140 bool tiling_managed_i(WTiling *ws, ExtlFn iterfn)
1142 PtrListIterTmp tmp;
1144 ptrlist_iter_init(&tmp, ws->managed_list);
1146 return extl_iter_objlist_(iterfn, (ObjIterator*)ptrlist_iter, &tmp);
1150 /*EXTL_DOC
1151 * Returns the root of the split tree.
1153 EXTL_SAFE
1154 EXTL_EXPORT_MEMBER
1155 WSplit *tiling_split_tree(WTiling *ws)
1157 return ws->split_tree;
1161 /*EXTL_DOC
1162 * Return the most previously active region next to \var{reg} in
1163 * direction \var{dirstr} (\codestr{left}, \codestr{right}, \codestr{up},
1164 * or \codestr{down}). The region \var{reg}
1165 * must be managed by \var{ws}. If \var{any} is not set, the status display
1166 * is not considered.
1168 EXTL_SAFE
1169 EXTL_EXPORT_MEMBER
1170 WRegion *tiling_nextto(WTiling *ws, WRegion *reg, const char *dirstr,
1171 bool any)
1173 WRegionNavi nh;
1175 if(!ioncore_string_to_navi(dirstr, &nh))
1176 return NULL;
1178 return tiling_do_navi_next(ws, reg, nh, FALSE, any);
1182 /*EXTL_DOC
1183 * Return the most previously active region on \var{ws} with no
1184 * other regions next to it in direction \var{dirstr}
1185 * (\codestr{left}, \codestr{right}, \codestr{up}, or \codestr{down}).
1186 * If \var{any} is not set, the status display is not considered.
1188 EXTL_SAFE
1189 EXTL_EXPORT_MEMBER
1190 WRegion *tiling_farthest(WTiling *ws, const char *dirstr, bool any)
1192 WRegionNavi nh;
1194 if(!ioncore_string_to_navi(dirstr, &nh))
1195 return NULL;
1197 return tiling_do_navi_first(ws, nh, any);
1201 /*EXTL_DOC
1202 * For region \var{reg} managed by \var{ws} return the \type{WSplit}
1203 * a leaf of which \var{reg} is.
1205 EXTL_SAFE
1206 EXTL_EXPORT_MEMBER
1207 WSplitRegion *tiling_node_of(WTiling *ws, WRegion *reg)
1209 if(reg==NULL){
1210 warn(TR("Nil parameter."));
1211 return NULL;
1214 if(REGION_MANAGER(reg)!=(WRegion*)ws){
1215 warn(TR("Manager doesn't match."));
1216 return NULL;
1219 return splittree_node_of(reg);
1223 /*}}}*/
1226 /*{{{ Flip and transpose */
1229 static WSplitSplit *get_at_split(WTiling *ws, WRegion *reg)
1231 WSplit *node;
1232 WSplitSplit *split;
1234 if(reg==NULL){
1235 split=OBJ_CAST(ws->split_tree, WSplitSplit);
1236 if(split==NULL)
1237 return NULL;
1238 else if(split->br==(WSplit*)ws->stdispnode)
1239 return OBJ_CAST(split->tl, WSplitSplit);
1240 else if(split->tl==(WSplit*)ws->stdispnode)
1241 return OBJ_CAST(split->br, WSplitSplit);
1242 else
1243 return split;
1246 node=(WSplit*)get_node_check(ws, reg);
1248 if(node==NULL)
1249 return NULL;
1251 if(node==(WSplit*)ws->stdispnode){
1252 warn(TR("The status display is not a valid parameter for "
1253 "this routine."));
1254 return NULL;
1257 split=OBJ_CAST(node->parent, WSplitSplit);
1259 if(split!=NULL && (split->tl==(WSplit*)ws->stdispnode ||
1260 split->br==(WSplit*)ws->stdispnode)){
1261 split=OBJ_CAST(((WSplit*)split)->parent, WSplitSplit);
1264 return split;
1268 /*EXTL_DOC
1269 * Flip \var{ws} at \var{reg} or root if nil.
1271 EXTL_EXPORT_MEMBER
1272 bool iowns_flip_at(WTiling *ws, WRegion *reg)
1274 WSplitSplit *split=get_at_split(ws, reg);
1276 if(split==NULL){
1277 return FALSE;
1278 }else{
1279 splitsplit_flip(split);
1280 return TRUE;
1285 /*EXTL_DOC
1286 * Transpose \var{ws} at \var{reg} or root if nil.
1288 EXTL_EXPORT_MEMBER
1289 bool iowns_transpose_at(WTiling *ws, WRegion *reg)
1291 WSplitSplit *split=get_at_split(ws, reg);
1293 if(split==NULL){
1294 return FALSE;
1295 }else{
1296 split_transpose((WSplit*)split);
1297 return TRUE;
1302 /*}}}*/
1305 /*{{{ Floating toggle */
1308 static void replace(WSplitSplit *split, WSplitSplit *nsplit)
1310 WSplitInner *psplit=split->isplit.split.parent;
1312 nsplit->tl=split->tl;
1313 split->tl=NULL;
1314 nsplit->tl->parent=(WSplitInner*)nsplit;
1316 nsplit->br=split->br;
1317 split->br=NULL;
1318 nsplit->br->parent=(WSplitInner*)nsplit;
1320 if(psplit!=NULL){
1321 splitinner_replace((WSplitInner*)psplit, (WSplit*)split,
1322 (WSplit*)nsplit);
1323 }else{
1324 splittree_changeroot((WSplit*)split, (WSplit*)nsplit);
1329 WSplitSplit *tiling_set_floating(WTiling *ws, WSplitSplit *split, int sp)
1331 bool set=OBJ_IS(split, WSplitFloat);
1332 bool nset=libtu_do_setparam(sp, set);
1333 const WRectangle *g=&((WSplit*)split)->geom;
1334 WSplitSplit *ns;
1336 if(!XOR(nset, set))
1337 return split;
1339 if(nset){
1340 ns=(WSplitSplit*)create_splitfloat(g, ws, split->dir);
1341 }else{
1342 if(OBJ_IS(split->tl, WSplitST) || OBJ_IS(split->br, WSplitST)){
1343 warn(TR("Refusing to float split directly containing the "
1344 "status display."));
1345 return NULL;
1347 ns=create_splitsplit(g, split->dir);
1350 if(ns!=NULL){
1351 replace(split, ns);
1352 split_resize((WSplit*)ns, g, PRIMN_ANY, PRIMN_ANY);
1353 mainloop_defer_destroy((Obj*)split);
1356 return ns;
1360 /*EXTL_DOC
1361 * Toggle floating of a split's sides at \var{split} as indicated by the
1362 * parameter \var{how} (\codestr{set}, \codestr{unset}, or \codestr{toggle}).
1363 * A split of the appropriate is returned, if there was a change.
1365 EXTL_EXPORT_AS(WTiling, set_floating)
1366 WSplitSplit *tiling_set_floating_extl(WTiling *ws, WSplitSplit *split,
1367 const char *how)
1369 if(!check_node(ws, (WSplit*)split))
1370 return NULL;
1371 return tiling_set_floating(ws, split, libtu_string_to_setparam(how));
1375 /*EXTL_DOC
1376 * Toggle floating of the sides of a split containin \var{reg} as indicated
1377 * by the parameters \var{how} (\codestr{set}, \codestr{unset}, or
1378 * \codestr{toggle}) and \var{dirstr} (\codestr{left}, \codestr{right},
1379 * \codestr{up}, or \codestr{down}). The new status is returned
1380 * (and \code{false} also on error).
1382 EXTL_EXPORT_AS(WTiling, set_floating_at)
1383 bool tiling_set_floating_at_extl(WTiling *ws, WRegion *reg, const char *how,
1384 const char *dirstr)
1386 WPrimn hprimn=PRIMN_ANY, vprimn=PRIMN_ANY;
1387 WSplitSplit *split, *nsplit;
1388 WSplit *node;
1390 node=(WSplit*)get_node_check(ws, reg);
1391 if(node==NULL)
1392 return FALSE;
1395 if(dirstr!=NULL){
1396 WRegionNavi nh;
1398 if(!ioncore_string_to_navi(dirstr, &nh))
1399 return FALSE;
1401 navi_to_primn(nh, &hprimn, &vprimn, PRIMN_NONE);
1404 while(TRUE){
1405 split=OBJ_CAST(node->parent, WSplitSplit);
1406 if(split==NULL){
1407 warn(TR("No suitable split here."));
1408 return FALSE;
1411 if(!OBJ_IS(split->tl, WSplitST) && !OBJ_IS(split->br, WSplitST)){
1412 WPrimn tmp=(split->dir==SPLIT_VERTICAL ? vprimn : hprimn);
1413 if(tmp==PRIMN_ANY
1414 || (node==split->tl && tmp==PRIMN_BR)
1415 || (node==split->br && tmp==PRIMN_TL)){
1416 break;
1420 node=(WSplit*)split;
1423 nsplit=tiling_set_floating(ws, split, libtu_string_to_setparam(how));
1425 return OBJ_IS((Obj*)(nsplit==NULL ? split : nsplit), WSplitFloat);
1429 /*}}}*/
1432 /*{{{ Save */
1435 ExtlTab tiling_get_configuration(WTiling *ws)
1437 ExtlTab tab, split_tree=extl_table_none();
1439 tab=region_get_base_configuration((WRegion*)ws);
1441 if(ws->split_tree!=NULL){
1442 if(!split_get_config(ws->split_tree, &split_tree))
1443 warn(TR("Could not get split tree."));
1446 extl_table_sets_t(tab, "split_tree", split_tree);
1447 extl_unref_table(split_tree);
1449 return tab;
1453 /*}}}*/
1456 /*{{{ Load */
1459 WSplit *load_splitst(WTiling *ws, const WRectangle *geom, ExtlTab tab)
1461 WSplitST *st;
1463 if(ws->stdispnode!=NULL){
1464 warn(TR("Workspace already has a status display node."));
1465 return NULL;
1468 st=create_splitst(geom, NULL);
1469 ws->stdispnode=st;
1470 return (WSplit*)st;
1474 static bool do_attach(WTiling *ws, WRegion *reg, void *p)
1476 WSplitRegion *node=create_splitregion(&REGION_GEOM(reg), reg);
1478 if(node==NULL)
1479 return FALSE;
1481 if(!tiling_managed_add(ws, reg)){
1482 node->reg=NULL;
1483 destroy_obj((Obj*)node);
1484 return FALSE;
1487 *(WSplitRegion**)p=node;
1489 return TRUE;
1493 WSplit *load_splitregion(WTiling *ws, const WRectangle *geom, ExtlTab tab)
1495 WWindow *par=REGION_PARENT(ws);
1496 WRegionAttachData data;
1497 WSplit *node=NULL;
1498 WFitParams fp;
1499 ExtlTab rt;
1501 if(!extl_table_gets_t(tab, "regparams", &rt)){
1502 warn(TR("Missing region parameters."));
1503 return NULL;
1506 data.type=REGION_ATTACH_LOAD;
1507 data.u.tab=rt;
1509 assert(par!=NULL);
1510 fp.g=*geom;
1511 fp.mode=REGION_FIT_EXACT;
1513 region_attach_helper((WRegion*)ws, par, &fp,
1514 (WRegionDoAttachFn*)do_attach, &node, &data);
1516 extl_unref_table(rt);
1518 return node;
1522 #define MINS 1
1524 WSplit *load_splitsplit(WTiling *ws, const WRectangle *geom, ExtlTab tab)
1526 WSplit *tl=NULL, *br=NULL;
1527 WSplitSplit *split;
1528 char *dir_str;
1529 int dir, brs, tls;
1530 ExtlTab subtab;
1531 WRectangle geom2;
1532 int set=0;
1534 set+=(extl_table_gets_i(tab, "tls", &tls)==TRUE);
1535 set+=(extl_table_gets_i(tab, "brs", &brs)==TRUE);
1536 set+=(extl_table_gets_s(tab, "dir", &dir_str)==TRUE);
1538 if(set!=3)
1539 return NULL;
1541 if(strcmp(dir_str, "vertical")==0){
1542 dir=SPLIT_VERTICAL;
1543 }else if(strcmp(dir_str, "horizontal")==0){
1544 dir=SPLIT_HORIZONTAL;
1545 }else{
1546 warn(TR("Invalid direction."));
1547 free(dir_str);
1548 return NULL;
1550 free(dir_str);
1552 split=create_splitsplit(geom, dir);
1553 if(split==NULL)
1554 return NULL;
1556 tls=maxof(tls, MINS);
1557 brs=maxof(brs, MINS);
1559 geom2=*geom;
1560 if(dir==SPLIT_HORIZONTAL){
1561 tls=maxof(0, geom->w)*tls/(tls+brs);
1562 geom2.w=tls;
1563 }else{
1564 tls=maxof(0, geom->h)*tls/(tls+brs);
1565 geom2.h=tls;
1568 if(extl_table_gets_t(tab, "tl", &subtab)){
1569 tl=tiling_load_node(ws, &geom2, subtab);
1570 extl_unref_table(subtab);
1573 geom2=*geom;
1574 if(dir==SPLIT_HORIZONTAL){
1575 geom2.w-=tls;
1576 geom2.x+=tls;
1577 }else{
1578 geom2.h-=tls;
1579 geom2.y+=tls;
1582 if(extl_table_gets_t(tab, "br", &subtab)){
1583 br=tiling_load_node(ws, &geom2, subtab);
1584 extl_unref_table(subtab);
1587 if(tl==NULL || br==NULL){
1588 /* PRIMN_TL/BR instead of ANY because of stdisp. */
1589 destroy_obj((Obj*)split);
1590 if(tl!=NULL){
1591 split_do_resize(tl, geom, PRIMN_BR, PRIMN_BR, FALSE);
1592 return tl;
1594 if(br!=NULL){
1595 split_do_resize(br, geom, PRIMN_TL, PRIMN_TL, FALSE);
1596 return br;
1598 return NULL;
1601 tl->parent=(WSplitInner*)split;
1602 br->parent=(WSplitInner*)split;
1604 /*split->tmpsize=tls;*/
1605 split->tl=tl;
1606 split->br=br;
1608 return (WSplit*)split;
1612 WSplit *tiling_load_node_default(WTiling *ws, const WRectangle *geom,
1613 ExtlTab tab)
1615 char *typestr=NULL;
1616 WSplit *node=NULL;
1618 extl_table_gets_s(tab, "type", &typestr);
1620 if(typestr==NULL){
1621 warn(TR("No split type given."));
1622 return NULL;
1625 if(strcmp(typestr, "WSplitRegion")==0)
1626 node=load_splitregion(ws, geom, tab);
1627 else if(strcmp(typestr, "WSplitSplit")==0)
1628 node=load_splitsplit(ws, geom, tab);
1629 else if(strcmp(typestr, "WSplitFloat")==0)
1630 node=load_splitfloat(ws, geom, tab);
1631 else if(strcmp(typestr, "WSplitST")==0)
1632 node=NULL;/*load_splitst(ws, geom, tab);*/
1633 else
1634 warn(TR("Unknown split type."));
1636 free(typestr);
1638 return node;
1642 WSplit *tiling_load_node(WTiling *ws, const WRectangle *geom, ExtlTab tab)
1644 WSplit *ret=NULL;
1645 CALL_DYN_RET(ret, WSplit*, tiling_load_node, ws, (ws, geom, tab));
1646 return ret;
1651 WRegion *tiling_load(WWindow *par, const WFitParams *fp, ExtlTab tab)
1653 WTiling *ws;
1654 ExtlTab treetab;
1655 bool ci=TRUE;
1657 if(extl_table_gets_t(tab, "split_tree", &treetab))
1658 ci=FALSE;
1660 ws=create_tiling(par, fp, NULL, ci);
1662 if(ws==NULL){
1663 if(!ci)
1664 extl_unref_table(treetab);
1665 return NULL;
1668 if(!ci){
1669 ws->split_tree=tiling_load_node(ws, &REGION_GEOM(ws), treetab);
1670 extl_unref_table(treetab);
1673 if(ws->split_tree==NULL){
1674 warn(TR("The workspace is empty."));
1675 destroy_obj((Obj*)ws);
1676 return NULL;
1679 ws->split_tree->ws_if_root=ws;
1680 split_restack(ws->split_tree, ws->dummywin, Above);
1682 return (WRegion*)ws;
1686 /*}}}*/
1689 /*{{{ Dynamic function table and class implementation */
1692 static DynFunTab tiling_dynfuntab[]={
1693 {region_map,
1694 tiling_map},
1696 {region_unmap,
1697 tiling_unmap},
1699 {region_do_set_focus,
1700 tiling_do_set_focus},
1702 {(DynFun*)region_fitrep,
1703 (DynFun*)tiling_fitrep},
1705 {region_managed_rqgeom,
1706 tiling_managed_rqgeom},
1708 {(DynFun*)region_managed_maximize,
1709 (DynFun*)tiling_managed_maximize},
1711 {region_managed_remove,
1712 tiling_managed_remove},
1714 {(DynFun*)region_managed_prepare_focus,
1715 (DynFun*)tiling_managed_prepare_focus},
1717 {(DynFun*)region_prepare_manage,
1718 (DynFun*)tiling_prepare_manage},
1720 {(DynFun*)region_rescue_clientwins,
1721 (DynFun*)tiling_rescue_clientwins},
1723 {(DynFun*)region_get_rescue_pholder_for,
1724 (DynFun*)tiling_get_rescue_pholder_for},
1726 {(DynFun*)region_get_configuration,
1727 (DynFun*)tiling_get_configuration},
1729 {(DynFun*)region_managed_disposeroot,
1730 (DynFun*)tiling_managed_disposeroot},
1732 {(DynFun*)region_current,
1733 (DynFun*)tiling_current},
1735 {(DynFun*)tiling_managed_add,
1736 (DynFun*)tiling_managed_add_default},
1738 {region_manage_stdisp,
1739 tiling_manage_stdisp},
1741 {region_unmanage_stdisp,
1742 tiling_unmanage_stdisp},
1744 {(DynFun*)tiling_load_node,
1745 (DynFun*)tiling_load_node_default},
1747 {region_restack,
1748 tiling_restack},
1750 {region_stacking,
1751 tiling_stacking},
1753 {(DynFun*)region_navi_first,
1754 (DynFun*)tiling_navi_first},
1756 {(DynFun*)region_navi_next,
1757 (DynFun*)tiling_navi_next},
1759 {(DynFun*)region_xwindow,
1760 (DynFun*)tiling_xwindow},
1762 END_DYNFUNTAB
1766 EXTL_EXPORT
1767 IMPLCLASS(WTiling, WRegion, tiling_deinit, tiling_dynfuntab);
1770 /*}}}*/