2 * ion/mod_tiling/tiling.c
4 * Copyright (c) Tuomo Valkonen 1999-2006.
6 * Ion is free software; you can redistribute it and/or modify it under
7 * the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
14 #include <libtu/objp.h>
15 #include <libtu/minmax.h>
16 #include <libtu/ptrlist.h>
17 #include <libmainloop/defer.h>
18 #include <libmainloop/signal.h>
20 #include <ioncore/common.h>
21 #include <ioncore/rootwin.h>
22 #include <ioncore/focus.h>
23 #include <ioncore/global.h>
24 #include <ioncore/region.h>
25 #include <ioncore/manage.h>
26 #include <ioncore/screen.h>
27 #include <ioncore/names.h>
28 #include <ioncore/saveload.h>
29 #include <ioncore/attach.h>
30 #include <ioncore/resize.h>
31 #include <libextl/extl.h>
32 #include <ioncore/regbind.h>
33 #include <ioncore/extlconv.h>
34 #include <ioncore/xwindow.h>
35 #include <ioncore/navi.h>
36 #include "placement.h"
39 #include "splitfloat.h"
40 #include "split-stdisp.h"
45 static WTilingIterTmp tiling_iter_default_tmp;
48 /*{{{ Some helper routines */
51 #define STDISP_OF(WS) \
52 ((WS)->stdispnode!=NULL ? (WS)->stdispnode->regnode.reg : NULL)
55 static WSplitRegion *get_node_check(WTiling *ws, WRegion *reg)
62 node=splittree_node_of(reg);
64 if(node==NULL || REGION_MANAGER(reg)!=(WRegion*)ws)
71 static bool check_node(WTiling *ws, WSplit *split)
74 return check_node(ws, (WSplit*)split->parent);
76 if((split->ws_if_root!=(void*)ws)){
77 warn(TR("Split not on workspace."));
87 /*{{{ Dynfun implementations */
90 static void reparent_mgd(WRegion *sub, WWindow *par)
93 subfp.g=REGION_GEOM(sub);
94 subfp.mode=REGION_FIT_EXACT;
95 if(!region_fitrep(sub, par, &subfp)){
96 warn(TR("Error reparenting %s."), region_name(sub));
97 region_detach_manager(sub);
102 bool tiling_fitrep(WTiling *ws, WWindow *par, const WFitParams *fp)
108 if(!region_same_rootwin((WRegion*)ws, (WRegion*)par))
111 region_unset_parent((WRegion*)ws);
113 XReparentWindow(ioncore_g.dpy, ws->dummywin,
114 par->win, fp->g.x, fp->g.y);
116 region_set_parent((WRegion*)ws, par);
118 if(ws->split_tree!=NULL)
119 split_reparent(ws->split_tree, par);
122 REGION_GEOM(ws)=fp->g;
124 if(ws->split_tree!=NULL){
126 if(fp->mode®ION_FIT_ROTATE)
127 ok=split_rotate_to(ws->split_tree, &(fp->g), fp->rotation);
129 split_resize(ws->split_tree, &(fp->g), PRIMN_ANY, PRIMN_ANY);
136 void tiling_managed_rqgeom(WTiling *ws, WRegion *mgd,
137 const WRQGeomParams *rq,
140 WSplitRegion *node=get_node_check(ws, mgd);
141 if(node!=NULL && ws->split_tree!=NULL)
142 splittree_rqgeom((WSplit*)node, rq->flags, &rq->geom, geomret);
146 void tiling_map(WTiling *ws)
148 REGION_MARK_MAPPED(ws);
149 XMapWindow(ioncore_g.dpy, ws->dummywin);
151 if(ws->split_tree!=NULL)
152 split_map(ws->split_tree);
156 void tiling_unmap(WTiling *ws)
158 REGION_MARK_UNMAPPED(ws);
159 XUnmapWindow(ioncore_g.dpy, ws->dummywin);
161 if(ws->split_tree!=NULL)
162 split_unmap(ws->split_tree);
166 void tiling_fallback_focus(WTiling *ws, bool warp)
168 region_finalise_focusing((WRegion*)ws, ws->dummywin, warp);
172 void tiling_do_set_focus(WTiling *ws, bool warp)
174 WRegion *sub=tiling_current(ws);
177 tiling_fallback_focus(ws, warp);
181 region_do_set_focus(sub, warp);
185 static WTimer *restack_timer=NULL;
188 static void restack_handler(WTimer *tmr, Obj *obj)
191 WTiling *ws=(WTiling*)obj;
192 split_restack(ws->split_tree, ws->dummywin, Above);
197 bool tiling_managed_prepare_focus(WTiling *ws, WRegion *reg,
198 int flags, WPrepareFocusResult *res)
202 if(!region_prepare_focus((WRegion*)ws, flags, res))
205 node=get_node_check(ws, reg);
207 if(node!=NULL && node->split.parent!=NULL)
208 splitinner_mark_current(node->split.parent, &(node->split));
210 /* WSplitSplit uses activity based stacking as required on WAutoWS,
211 * so we must restack here.
213 if(ws->split_tree!=NULL){
214 int rd=mod_tiling_raise_delay;
215 bool use_timer=rd>0 && flags®ION_GOTO_ENTERWINDOW;
218 if(restack_timer!=NULL){
219 Obj *obj=restack_timer->objwatch.obj;
221 timer_reset(restack_timer);
222 restack_handler(restack_timer, obj);
225 restack_timer=create_timer();
229 if(use_timer && restack_timer!=NULL){
230 timer_set(restack_timer, rd, restack_handler, (Obj*)ws);
232 split_restack(ws->split_tree, ws->dummywin, Above);
243 void tiling_restack(WTiling *ws, Window other, int mode)
245 xwindow_restack(ws->dummywin, other, mode);
246 if(ws->split_tree!=NULL)
247 split_restack(ws->split_tree, ws->dummywin, Above);
251 void tiling_stacking(WTiling *ws, Window *bottomret, Window *topret)
253 Window sbottom=None, stop=None;
255 if(ws->split_tree!=None)
256 split_stacking(ws->split_tree, &sbottom, &stop);
258 *bottomret=ws->dummywin;
259 *topret=(stop!=None ? stop : ws->dummywin);
263 Window tiling_xwindow(const WTiling *ws)
270 WRegion *tiling_rqclose_propagate(WTiling *ws, WRegion *maybe_sub)
272 return (region_rqclose((WRegion*)ws, FALSE) ? (WRegion*)ws : NULL);
276 WPHolder *tiling_prepare_manage_transient(WTiling *ws,
277 const WClientWin *transient,
278 const WManageParams *param,
281 /* Transient manager searches should not cross tilings unless
282 * explicitly floated.
284 if(extl_table_is_bool_set(transient->proptab, "float")){
285 return region_prepare_manage_transient_default((WRegion*)ws,
298 /*{{{ Status display support code */
301 static bool regnodefilter(WSplit *split)
303 return OBJ_IS(split, WSplitRegion);
307 void tiling_unmanage_stdisp(WTiling *ws, bool permanent, bool nofocus)
309 WSplitRegion *tofocus=NULL;
313 if(ws->stdispnode==NULL)
316 od=ws->stdispnode->regnode.reg;
319 if(!nofocus && REGION_IS_ACTIVE(od) &&
320 region_may_control_focus((WRegion*)ws)){
322 tofocus=(WSplitRegion*)split_nextto((WSplit*)(ws->stdispnode),
323 PRIMN_ANY, PRIMN_ANY,
326 /* Reset node_of info here so tiling_managed_remove will not
329 splittree_set_node_of(od, NULL);
330 tiling_do_managed_remove(ws, od);
334 WSplit *node=(WSplit*)ws->stdispnode;
336 splittree_remove(node, TRUE);
341 region_set_focus(tofocus->reg);
343 tiling_fallback_focus(ws, FALSE);
348 static void tiling_create_stdispnode(WTiling *ws, WRegion *stdisp,
349 int corner, int orientation,
352 int flags=REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y;
353 WRectangle *wg=®ION_GEOM(ws), dg;
354 WSplitST *stdispnode;
357 assert(ws->split_tree!=NULL);
359 if(orientation==REGION_ORIENTATION_HORIZONTAL){
363 dg.y=((corner==MPLEX_STDISP_BL || corner==MPLEX_STDISP_BR)
370 dg.x=((corner==MPLEX_STDISP_TR || corner==MPLEX_STDISP_BR)
375 stdispnode=create_splitst(&dg, stdisp);
377 if(stdispnode==NULL){
378 warn(TR("Unable to create a node for status display."));
382 stdispnode->corner=corner;
383 stdispnode->orientation=orientation;
384 stdispnode->fullsize=fullsize;
386 split=create_splitsplit(wg, (orientation==REGION_ORIENTATION_HORIZONTAL
388 : SPLIT_HORIZONTAL));
391 warn(TR("Unable to create new split for status display."));
392 stdispnode->regnode.reg=NULL;
393 destroy_obj((Obj*)stdispnode);
397 /* Set up new split tree */
398 ((WSplit*)stdispnode)->parent=(WSplitInner*)split;
399 ws->split_tree->parent=(WSplitInner*)split;
400 ws->split_tree->ws_if_root=NULL;
402 if((orientation==REGION_ORIENTATION_HORIZONTAL &&
403 (corner==MPLEX_STDISP_BL || corner==MPLEX_STDISP_BR)) ||
404 (orientation==REGION_ORIENTATION_VERTICAL &&
405 (corner==MPLEX_STDISP_TR || corner==MPLEX_STDISP_BR))){
406 split->tl=ws->split_tree;
407 split->br=(WSplit*)stdispnode;
408 split->current=SPLIT_CURRENT_TL;
410 split->tl=(WSplit*)stdispnode;
411 split->br=ws->split_tree;
412 split->current=SPLIT_CURRENT_BR;
415 ws->split_tree=(WSplit*)split;
416 ((WSplit*)split)->ws_if_root=ws;
417 ws->stdispnode=stdispnode;
421 void tiling_manage_stdisp(WTiling *ws, WRegion *stdisp,
422 const WMPlexSTDispInfo *di)
424 bool mcf=region_may_control_focus((WRegion*)ws);
425 int flags=REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y;
426 int orientation=region_orientation(stdisp);
428 WRectangle dg, *stdg;
430 if(orientation!=REGION_ORIENTATION_VERTICAL /*&&
431 orientation!=REGION_ORIENTATION_HORIZONTAL*/){
432 orientation=REGION_ORIENTATION_HORIZONTAL;
435 if(ws->stdispnode==NULL || ws->stdispnode->regnode.reg!=stdisp)
436 region_detach_manager(stdisp);
438 /* Remove old stdisp if corner and orientation don't match.
440 if(ws->stdispnode!=NULL && (di->pos!=ws->stdispnode->corner ||
441 orientation!=ws->stdispnode->orientation)){
442 tiling_unmanage_stdisp(ws, TRUE, TRUE);
445 if(ws->stdispnode==NULL){
446 tiling_create_stdispnode(ws, stdisp, di->pos, orientation,
448 if(ws->stdispnode==NULL)
451 WRegion *od=ws->stdispnode->regnode.reg;
453 act=REGION_IS_ACTIVE(od);
454 splittree_set_node_of(od, NULL);
455 tiling_managed_remove(ws, od);
456 assert(ws->stdispnode->regnode.reg==NULL);
459 ws->stdispnode->fullsize=di->fullsize;
460 ws->stdispnode->regnode.reg=stdisp;
461 splittree_set_node_of(stdisp, &(ws->stdispnode->regnode));
464 if(!tiling_managed_add(ws, stdisp)){
465 tiling_unmanage_stdisp(ws, TRUE, TRUE);
469 dg=((WSplit*)(ws->stdispnode))->geom;
471 dg.h=stdisp_recommended_h(ws->stdispnode);
472 dg.w=stdisp_recommended_w(ws->stdispnode);
474 splittree_rqgeom((WSplit*)(ws->stdispnode), flags, &dg, FALSE);
476 stdg=&(((WSplit*)ws->stdispnode)->geom);
478 if(stdisp->geom.x!=stdg->x || stdisp->geom.y!=stdg->y ||
479 stdisp->geom.w!=stdg->w || stdisp->geom.h!=stdg->h){
480 region_fit(stdisp, stdg, REGION_FIT_EXACT);
483 /* Restack to ensure the split tree is stacked in the expected order. */
484 if(ws->split_tree!=NULL)
485 split_restack(ws->split_tree, ws->dummywin, Above);
488 region_set_focus(stdisp);
495 /*{{{ Create/destroy */
498 bool tiling_managed_add_default(WTiling *ws, WRegion *reg)
500 Window bottom=None, top=None;
503 if(STDISP_OF(ws)!=reg){
504 if(!ptrlist_insert_last(&(ws->managed_list), reg))
508 region_set_manager(reg, (WRegion*)ws);
510 frame=OBJ_CAST(reg, WFrame);
512 WFrameMode mode=frame_mode(frame);
513 if(mode!=FRAME_MODE_TILED && mode!=FRAME_MODE_TILED_ALT)
514 frame_set_mode(frame, FRAME_MODE_TILED);
517 if(REGION_IS_MAPPED(ws))
520 if(region_may_control_focus((WRegion*)ws)){
521 WRegion *curr=tiling_current(ws);
522 if(curr==NULL || !REGION_IS_ACTIVE(curr))
530 bool tiling_managed_add(WTiling *ws, WRegion *reg)
533 CALL_DYN_RET(ret, bool, tiling_managed_add, ws, (ws, reg));
538 static WRegion *create_initial_frame(WTiling *ws, WWindow *parent,
539 const WFitParams *fp)
541 WRegion *reg=ws->create_frame_fn(parent, fp);
546 ws->split_tree=(WSplit*)create_splitregion(&(fp->g), reg);
547 if(ws->split_tree==NULL){
548 destroy_obj((Obj*)reg);
551 ws->split_tree->ws_if_root=ws;
553 if(!tiling_managed_add(ws, reg)){
554 destroy_obj((Obj*)reg);
555 destroy_obj((Obj*)ws->split_tree);
563 static WRegion *create_frame_tiling(WWindow *parent, const WFitParams *fp)
565 return (WRegion*)create_frame(parent, fp, FRAME_MODE_TILED);
569 bool tiling_init(WTiling *ws, WWindow *parent, const WFitParams *fp,
570 WRegionSimpleCreateFn *create_frame_fn, bool ci)
573 ws->create_frame_fn=(create_frame_fn
575 : create_frame_tiling);
577 ws->managed_list=NULL;
579 ws->dummywin=XCreateWindow(ioncore_g.dpy, parent->win,
580 fp->g.x, fp->g.y, 1, 1, 0,
581 CopyFromParent, InputOnly,
582 CopyFromParent, 0, NULL);
583 if(ws->dummywin==None)
586 region_init(&(ws->reg), parent, fp);
588 ws->reg.flags|=(REGION_GRAB_ON_PARENT|
592 if(create_initial_frame(ws, parent, fp)==NULL){
593 XDestroyWindow(ioncore_g.dpy, ws->dummywin);
598 XSelectInput(ioncore_g.dpy, ws->dummywin,
599 FocusChangeMask|KeyPressMask|KeyReleaseMask|
600 ButtonPressMask|ButtonReleaseMask);
601 XSaveContext(ioncore_g.dpy, ws->dummywin, ioncore_g.win_context,
604 region_register(&(ws->reg));
605 region_add_bindmap((WRegion*)ws, mod_tiling_tiling_bindmap);
611 WTiling *create_tiling(WWindow *parent, const WFitParams *fp,
612 WRegionSimpleCreateFn *create_frame_fn, bool ci)
614 CREATEOBJ_IMPL(WTiling, tiling, (p, parent, fp, create_frame_fn, ci));
618 WTiling *create_tiling_simple(WWindow *parent, const WFitParams *fp)
620 return create_tiling(parent, fp, NULL, TRUE);
624 void tiling_deinit(WTiling *ws)
628 WMPlex *remanage_mplex=NULL;
630 tiling_unmanage_stdisp(ws, FALSE, TRUE);
632 FOR_ALL_MANAGED_BY_TILING(reg, ws, tmp){
633 destroy_obj((Obj*)reg);
636 FOR_ALL_MANAGED_BY_TILING(reg, ws, tmp){
640 if(ws->split_tree!=NULL)
641 destroy_obj((Obj*)(ws->split_tree));
643 XDeleteContext(ioncore_g.dpy, ws->dummywin, ioncore_g.win_context);
644 XDestroyWindow(ioncore_g.dpy, ws->dummywin);
647 region_deinit(&(ws->reg));
651 bool tiling_managed_may_destroy(WTiling *ws, WRegion *reg)
656 FOR_ALL_MANAGED_BY_TILING(mgd, ws, tmp){
657 if(mgd!=STDISP_OF(ws) && mgd!=reg){
662 return region_manager_allows_destroying((WRegion*)ws);
666 bool tiling_may_destroy(WTiling *ws, WRegion *reg)
671 FOR_ALL_MANAGED_BY_TILING(mgd, ws, tmp){
672 if(mgd!=STDISP_OF(ws)){
673 warn(TR("Workspace not empty - refusing to destroy."));
682 bool tiling_rescue_clientwins(WTiling *ws, WPHolder *ph)
686 ptrlist_iter_init(&tmp, ws->managed_list);
688 return region_rescue_some_clientwins((WRegion*)ws, ph,
689 (WRegionIterator*)ptrlist_iter,
694 void tiling_do_managed_remove(WTiling *ws, WRegion *reg)
696 if(STDISP_OF(ws)==reg){
697 ws->stdispnode->regnode.reg=NULL;
699 ptrlist_remove(&(ws->managed_list), reg);
702 region_unset_manager(reg, (WRegion*)ws);
706 static bool nostdispfilter(WSplit *node)
708 return (OBJ_IS(node, WSplitRegion) && !OBJ_IS(node, WSplitST));
712 void tiling_managed_remove(WTiling *ws, WRegion *reg)
714 bool ds=OBJ_IS_BEING_DESTROYED(ws);
715 bool act=REGION_IS_ACTIVE(reg);
716 bool mcf=region_may_control_focus((WRegion*)ws);
717 WSplitRegion *node=get_node_check(ws, reg);
720 other=tiling_do_navi_next(ws, reg, REGION_NAVI_ANY, TRUE, FALSE);
722 tiling_do_managed_remove(ws, reg);
724 if(node==(WSplitRegion*)(ws->stdispnode))
730 splittree_remove((WSplit*)node, (!ds && other!=NULL));
734 mainloop_defer_destroy((Obj*)ws);
741 static bool mplexfilter(WSplit *node)
743 WSplitRegion *regnode=OBJ_CAST(node, WSplitRegion);
745 return (regnode!=NULL && regnode->reg!=NULL &&
746 OBJ_IS(regnode->reg, WMPlex));
750 static WPHolder *find_ph_result=NULL;
751 static WRegion *find_ph_param=NULL;
754 static bool find_ph(WSplit *split)
756 WSplitRegion *sr=OBJ_CAST(split, WSplitRegion);
758 assert(find_ph_result==NULL);
760 if(sr==NULL || sr->reg==NULL)
763 find_ph_result=region_get_rescue_pholder_for(sr->reg, find_ph_param);
765 return (find_ph_result!=NULL);
769 WPHolder *tiling_get_rescue_pholder_for(WTiling *ws, WRegion *mgd)
771 WSplit *node=(WSplit*)get_node_check(ws, mgd);
778 if(ws->split_tree!=NULL){
779 split_current_todir(ws->split_tree, PRIMN_ANY, PRIMN_ANY,
784 split_nextto(node, PRIMN_ANY, PRIMN_ANY, find_ph);
785 if(find_ph_result!=NULL)
787 node=(WSplit*)node->parent;
805 static void navi_to_primn(WRegionNavi nh, WPrimn *hprimn, WPrimn *vprimn,
808 /* choice should be PRIMN_ANY or PRIMN_NONE */
811 case REGION_NAVI_BEG:
816 case REGION_NAVI_END:
821 case REGION_NAVI_LEFT:
826 case REGION_NAVI_RIGHT:
831 case REGION_NAVI_TOP:
836 case REGION_NAVI_BOTTOM:
842 case REGION_NAVI_ANY:
850 static WRegion *node_reg(WSplit *node)
852 WSplitRegion *rnode=OBJ_CAST(node, WSplitRegion);
853 return (rnode!=NULL ? rnode->reg : NULL);
857 WRegion *tiling_do_navi_next(WTiling *ws, WRegion *reg,
858 WRegionNavi nh, bool nowrap,
861 WSplitFilter *filter=(any ? NULL : nostdispfilter);
862 WPrimn hprimn, vprimn;
865 navi_to_primn(nh, &hprimn, &vprimn, PRIMN_NONE);
868 reg=tiling_current(ws);
871 WSplitRegion *node=get_node_check(ws, reg);
873 nxt=node_reg(split_nextto((WSplit*)node, hprimn, vprimn,
878 if(nxt==NULL && !nowrap){
879 nxt=node_reg(split_current_todir(ws->split_tree,
880 primn_none2any(primn_invert(hprimn)),
881 primn_none2any(primn_invert(vprimn)),
889 WRegion *tiling_do_navi_first(WTiling *ws, WRegionNavi nh, bool any)
891 WSplitFilter *filter=(any ? NULL : nostdispfilter);
892 WPrimn hprimn, vprimn;
894 navi_to_primn(nh, &hprimn, &vprimn, PRIMN_ANY);
896 return node_reg(split_current_todir(ws->split_tree,
897 hprimn, vprimn, filter));
901 WRegion *tiling_navi_next(WTiling *ws, WRegion *reg,
902 WRegionNavi nh, WRegionNaviData *data)
904 WRegion *nxt=tiling_do_navi_next(ws, reg, nh, TRUE, FALSE);
906 return region_navi_cont(&ws->reg, nxt, data);
910 WRegion *tiling_navi_first(WTiling *ws, WRegionNavi nh,
911 WRegionNaviData *data)
913 WRegion *reg=tiling_do_navi_first(ws, nh, FALSE);
915 return region_navi_cont(&ws->reg, reg, data);
922 /*{{{ Split/unsplit */
925 static bool get_split_dir_primn(const char *str, int *dir, int *primn)
927 WPrimn hprimn, vprimn;
930 if(!ioncore_string_to_navi(str, &nh))
933 navi_to_primn(nh, &hprimn, &vprimn, PRIMN_NONE);
935 if(hprimn==PRIMN_NONE){
938 }else if(vprimn==PRIMN_NONE){
939 *dir=SPLIT_HORIZONTAL;
942 warn(TR("Invalid direction"));
950 static bool get_split_dir_primn_float(const char *str, int *dir, int *primn,
953 if(strncmp(str, "floating:", 9)==0){
955 return get_split_dir_primn(str+9, dir, primn);
958 return get_split_dir_primn(str, dir, primn);
963 #define SPLIT_MINS 16 /* totally arbitrary */
966 static WFrame *tiling_do_split(WTiling *ws, WSplit *node,
967 const char *dirstr, int minw, int minh)
969 int dir, primn, mins;
974 if(node==NULL || ws->split_tree==NULL){
975 warn(TR("Invalid node."));
979 if(!get_split_dir_primn_float(dirstr, &dir, &primn, &floating))
982 mins=(dir==SPLIT_VERTICAL ? minh : minw);
985 nnode=splittree_split(node, dir, primn, mins,
989 nnode=splittree_split_floating(node, dir, primn, mins,
990 ws->create_frame_fn, ws);
994 warn(TR("Unable to split."));
998 /* We must restack here to ensure the split tree is stacked in the
1001 if(ws->split_tree!=NULL)
1002 split_restack(ws->split_tree, ws->dummywin, Above);
1004 newframe=OBJ_CAST(nnode->reg, WFrame);
1005 assert(newframe!=NULL);
1007 if(!tiling_managed_add(ws, nnode->reg)){
1009 destroy_obj((Obj*)nnode);
1010 destroy_obj((Obj*)newframe);
1015 if(ws->split_tree!=NULL)
1016 split_restack(ws->split_tree, ws->dummywin, Above);
1023 * Create a new frame on \var{ws} above/below/left of/right of
1024 * \var{node} as indicated by \var{dirstr}. If \var{dirstr} is
1025 * prefixed with ''floating:'' a floating split is created.
1028 WFrame *tiling_split(WTiling *ws, WSplit *node, const char *dirstr)
1030 if(!check_node(ws, node))
1033 return tiling_do_split(ws, node, dirstr,
1034 SPLIT_MINS, SPLIT_MINS);
1039 * Same as \fnref{WTiling.split} at the root of the split tree.
1042 WFrame *tiling_split_top(WTiling *ws, const char *dirstr)
1044 return tiling_do_split(ws, ws->split_tree, dirstr,
1045 SPLIT_MINS, SPLIT_MINS);
1050 * Split \var{frame} creating a new frame to direction \var{dirstr}
1051 * (one of ''left'', ''right'', ''top'' or ''bottom'') of \var{frame}.
1052 * If \var{attach_current} is set, the region currently displayed in
1053 * \var{frame}, if any, is moved to thenew frame.
1054 * If \var{dirstr} is prefixed with ''floating:'' a floating split is
1058 WFrame *tiling_split_at(WTiling *ws, WFrame *frame, const char *dirstr,
1059 bool attach_current)
1068 node=get_node_check(ws, (WRegion*)frame);
1070 newframe=tiling_do_split(ws, (WSplit*)node, dirstr,
1071 region_min_w((WRegion*)frame),
1072 region_min_h((WRegion*)frame));
1077 curr=mplex_mx_current(&(frame->mplex));
1079 if(attach_current && curr!=NULL)
1080 mplex_attach_simple(&(newframe->mplex), curr, MPLEX_ATTACH_SWITCHTO);
1082 if(region_may_control_focus((WRegion*)frame))
1083 region_goto((WRegion*)newframe);
1090 * Try to relocate regions managed by \var{frame} to another frame
1091 * and, if possible, destroy the frame.
1094 bool tiling_unsplit_at(WTiling *ws, WFrame *frame)
1097 warn(TR("Nil frame."));
1101 if(REGION_MANAGER(frame)!=(WRegion*)ws){
1102 warn(TR("The frame is not managed by the workspace."));
1106 return region_rqclose((WRegion*)frame, TRUE);
1113 /*{{{ Navigation etc. exports */
1116 WRegion *tiling_current(WTiling *ws)
1118 WSplitRegion *node=NULL;
1119 if(ws->split_tree!=NULL){
1120 node=(WSplitRegion*)split_current_todir(ws->split_tree,
1121 PRIMN_ANY, PRIMN_ANY, NULL);
1123 return (node ? node->reg : NULL);
1128 * Returns a list of regions managed by the workspace (frames, mostly).
1132 ExtlTab tiling_managed_list(WTiling *ws)
1136 ptrlist_iter_init(&tmp, ws->managed_list);
1138 return extl_obj_iterable_to_table((ObjIterator*)ptrlist_iter, &tmp);
1143 * Returns the root of the split tree.
1147 WSplit *tiling_split_tree(WTiling *ws)
1149 return ws->split_tree;
1154 * Return the most previously active region next to \var{reg} in
1155 * direction \var{dirstr} (left/right/up/down). The region \var{reg}
1156 * must be managed by \var{ws}. If \var{any} is not set, the status display
1157 * is not considered.
1161 WRegion *tiling_nextto(WTiling *ws, WRegion *reg, const char *dirstr,
1166 if(!ioncore_string_to_navi(dirstr, &nh))
1169 return tiling_do_navi_next(ws, reg, nh, FALSE, any);
1174 * Return the most previously active region on \var{ws} with no
1175 * other regions next to it in direction \var{dirstr}
1176 * (left/right/up/down). If \var{any} is not set, the status
1177 * display is not considered.
1181 WRegion *tiling_farthest(WTiling *ws, const char *dirstr, bool any)
1185 if(!ioncore_string_to_navi(dirstr, &nh))
1188 return tiling_do_navi_first(ws, nh, any);
1193 * For region \var{reg} managed by \var{ws} return the \type{WSplit}
1194 * a leaf of which \var{reg} is.
1198 WSplitRegion *tiling_node_of(WTiling *ws, WRegion *reg)
1201 warn(TR("Nil parameter."));
1205 if(REGION_MANAGER(reg)!=(WRegion*)ws){
1206 warn(TR("Manager doesn't match."));
1210 return splittree_node_of(reg);
1217 /*{{{ Flip and transpose */
1220 static WSplitSplit *get_at_split(WTiling *ws, WRegion *reg)
1226 split=OBJ_CAST(ws->split_tree, WSplitSplit);
1229 else if(split->br==(WSplit*)ws->stdispnode)
1230 return OBJ_CAST(split->tl, WSplitSplit);
1231 else if(split->tl==(WSplit*)ws->stdispnode)
1232 return OBJ_CAST(split->br, WSplitSplit);
1237 node=(WSplit*)get_node_check(ws, reg);
1242 if(node==(WSplit*)ws->stdispnode){
1243 warn(TR("The status display is not a valid parameter for "
1248 split=OBJ_CAST(node->parent, WSplitSplit);
1250 if(split!=NULL && (split->tl==(WSplit*)ws->stdispnode ||
1251 split->br==(WSplit*)ws->stdispnode)){
1252 split=OBJ_CAST(((WSplit*)split)->parent, WSplitSplit);
1260 * Flip \var{ws} at \var{reg} or root if nil.
1263 bool iowns_flip_at(WTiling *ws, WRegion *reg)
1265 WSplitSplit *split=get_at_split(ws, reg);
1270 splitsplit_flip(split);
1277 * Transpose \var{ws} at \var{reg} or root if nil.
1280 bool iowns_transpose_at(WTiling *ws, WRegion *reg)
1282 WSplitSplit *split=get_at_split(ws, reg);
1287 split_transpose((WSplit*)split);
1296 /*{{{ Floating toggle */
1299 static void replace(WSplitSplit *split, WSplitSplit *nsplit)
1301 WSplitInner *psplit=split->isplit.split.parent;
1303 nsplit->tl=split->tl;
1305 nsplit->tl->parent=(WSplitInner*)nsplit;
1307 nsplit->br=split->br;
1309 nsplit->br->parent=(WSplitInner*)nsplit;
1312 splitinner_replace((WSplitInner*)psplit, (WSplit*)split,
1315 splittree_changeroot((WSplit*)split, (WSplit*)nsplit);
1320 WSplitSplit *tiling_set_floating(WTiling *ws, WSplitSplit *split, int sp)
1322 bool set=OBJ_IS(split, WSplitFloat);
1323 bool nset=libtu_do_setparam(sp, set);
1324 const WRectangle *g=&((WSplit*)split)->geom;
1331 ns=(WSplitSplit*)create_splitfloat(g, ws, split->dir);
1333 if(OBJ_IS(split->tl, WSplitST) || OBJ_IS(split->br, WSplitST)){
1334 warn(TR("Refusing to float split directly containing the "
1335 "status display."));
1338 ns=create_splitsplit(g, split->dir);
1343 split_resize((WSplit*)ns, g, PRIMN_ANY, PRIMN_ANY);
1344 mainloop_defer_destroy((Obj*)split);
1352 * Toggle floating of a split's sides at \var{split} as indicated by the
1353 * parameter \var{how} (set/unset/toggle). A split of the appropriate is
1354 * returned, if there was a change.
1356 EXTL_EXPORT_AS(WTiling, set_floating)
1357 WSplitSplit *tiling_set_floating_extl(WTiling *ws, WSplitSplit *split,
1360 if(!check_node(ws, (WSplit*)split))
1362 return tiling_set_floating(ws, split, libtu_string_to_setparam(how));
1367 * Toggle floating of the sides of a split containin \var{reg} as indicated
1368 * by the parameters \var{how} (set/unset/toggle) and \var{dirstr}
1369 * (left/right/up/down/any). The new status is returned (and \code{false}
1372 EXTL_EXPORT_AS(WTiling, set_floating_at)
1373 bool tiling_set_floating_at_extl(WTiling *ws, WRegion *reg, const char *how,
1376 WPrimn hprimn=PRIMN_ANY, vprimn=PRIMN_ANY;
1377 WSplitSplit *split, *nsplit;
1380 node=(WSplit*)get_node_check(ws, reg);
1388 if(!ioncore_string_to_navi(dirstr, &nh))
1391 navi_to_primn(nh, &hprimn, &vprimn, PRIMN_NONE);
1395 split=OBJ_CAST(node->parent, WSplitSplit);
1397 warn(TR("No suitable split here."));
1401 if(!OBJ_IS(split->tl, WSplitST) && !OBJ_IS(split->br, WSplitST)){
1402 WPrimn tmp=(split->dir==SPLIT_VERTICAL ? vprimn : hprimn);
1404 || (node==split->tl && tmp==PRIMN_BR)
1405 || (node==split->br && tmp==PRIMN_TL)){
1410 node=(WSplit*)split;
1413 nsplit=tiling_set_floating(ws, split, libtu_string_to_setparam(how));
1415 return OBJ_IS((Obj*)(nsplit==NULL ? split : nsplit), WSplitFloat);
1425 ExtlTab tiling_get_configuration(WTiling *ws)
1427 ExtlTab tab, split_tree=extl_table_none();
1429 tab=region_get_base_configuration((WRegion*)ws);
1431 if(ws->split_tree!=NULL){
1432 if(!split_get_config(ws->split_tree, &split_tree))
1433 warn(TR("Could not get split tree."));
1436 extl_table_sets_t(tab, "split_tree", split_tree);
1437 extl_unref_table(split_tree);
1449 WSplit *load_splitst(WTiling *ws, const WRectangle *geom, ExtlTab tab)
1453 if(ws->stdispnode!=NULL){
1454 warn(TR("Workspace already has a status display node."));
1458 st=create_splitst(geom, NULL);
1464 static bool do_attach(WTiling *ws, WRegion *reg, void *p)
1466 WSplitRegion *node=create_splitregion(®ION_GEOM(reg), reg);
1471 if(!tiling_managed_add(ws, reg)){
1473 destroy_obj((Obj*)node);
1477 *(WSplitRegion**)p=node;
1483 WSplit *load_splitregion(WTiling *ws, const WRectangle *geom, ExtlTab tab)
1485 WWindow *par=REGION_PARENT(ws);
1486 WRegionAttachData data;
1491 if(!extl_table_gets_t(tab, "regparams", &rt)){
1492 warn(TR("Missing region parameters."));
1496 data.type=REGION_ATTACH_LOAD;
1501 fp.mode=REGION_FIT_EXACT;
1503 region_attach_helper((WRegion*)ws, par, &fp,
1504 (WRegionDoAttachFn*)do_attach, &node, &data);
1506 extl_unref_table(rt);
1514 WSplit *load_splitsplit(WTiling *ws, const WRectangle *geom, ExtlTab tab)
1516 WSplit *tl=NULL, *br=NULL;
1524 set+=(extl_table_gets_i(tab, "tls", &tls)==TRUE);
1525 set+=(extl_table_gets_i(tab, "brs", &brs)==TRUE);
1526 set+=(extl_table_gets_s(tab, "dir", &dir_str)==TRUE);
1531 if(strcmp(dir_str, "vertical")==0){
1533 }else if(strcmp(dir_str, "horizontal")==0){
1534 dir=SPLIT_HORIZONTAL;
1536 warn(TR("Invalid direction."));
1542 split=create_splitsplit(geom, dir);
1546 tls=maxof(tls, MINS);
1547 brs=maxof(brs, MINS);
1550 if(dir==SPLIT_HORIZONTAL){
1551 tls=maxof(0, geom->w)*tls/(tls+brs);
1554 tls=maxof(0, geom->h)*tls/(tls+brs);
1558 if(extl_table_gets_t(tab, "tl", &subtab)){
1559 tl=tiling_load_node(ws, &geom2, subtab);
1560 extl_unref_table(subtab);
1564 if(dir==SPLIT_HORIZONTAL){
1572 if(extl_table_gets_t(tab, "br", &subtab)){
1573 br=tiling_load_node(ws, &geom2, subtab);
1574 extl_unref_table(subtab);
1577 if(tl==NULL || br==NULL){
1578 /* PRIMN_TL/BR instead of ANY because of stdisp. */
1579 destroy_obj((Obj*)split);
1581 split_do_resize(tl, geom, PRIMN_BR, PRIMN_BR, FALSE);
1585 split_do_resize(br, geom, PRIMN_TL, PRIMN_TL, FALSE);
1591 tl->parent=(WSplitInner*)split;
1592 br->parent=(WSplitInner*)split;
1594 /*split->tmpsize=tls;*/
1598 return (WSplit*)split;
1602 WSplit *tiling_load_node_default(WTiling *ws, const WRectangle *geom,
1608 extl_table_gets_s(tab, "type", &typestr);
1611 warn(TR("No split type given."));
1615 if(strcmp(typestr, "WSplitRegion")==0)
1616 node=load_splitregion(ws, geom, tab);
1617 else if(strcmp(typestr, "WSplitSplit")==0)
1618 node=load_splitsplit(ws, geom, tab);
1619 else if(strcmp(typestr, "WSplitFloat")==0)
1620 node=load_splitfloat(ws, geom, tab);
1621 else if(strcmp(typestr, "WSplitST")==0)
1622 node=NULL;/*load_splitst(ws, geom, tab);*/
1624 warn(TR("Unknown split type."));
1632 WSplit *tiling_load_node(WTiling *ws, const WRectangle *geom, ExtlTab tab)
1635 CALL_DYN_RET(ret, WSplit*, tiling_load_node, ws, (ws, geom, tab));
1641 WRegion *tiling_load(WWindow *par, const WFitParams *fp, ExtlTab tab)
1647 if(extl_table_gets_t(tab, "split_tree", &treetab))
1650 ws=create_tiling(par, fp, NULL, ci);
1654 extl_unref_table(treetab);
1659 ws->split_tree=tiling_load_node(ws, ®ION_GEOM(ws), treetab);
1660 extl_unref_table(treetab);
1663 if(ws->split_tree==NULL){
1664 warn(TR("The workspace is empty."));
1665 destroy_obj((Obj*)ws);
1669 ws->split_tree->ws_if_root=ws;
1670 split_restack(ws->split_tree, ws->dummywin, Above);
1672 return (WRegion*)ws;
1679 /*{{{ Dynamic function table and class implementation */
1682 static DynFunTab tiling_dynfuntab[]={
1689 {region_do_set_focus,
1690 tiling_do_set_focus},
1692 {(DynFun*)region_fitrep,
1693 (DynFun*)tiling_fitrep},
1695 {region_managed_rqgeom,
1696 tiling_managed_rqgeom},
1698 {region_managed_remove,
1699 tiling_managed_remove},
1701 {(DynFun*)region_managed_prepare_focus,
1702 (DynFun*)tiling_managed_prepare_focus},
1704 {(DynFun*)region_prepare_manage,
1705 (DynFun*)tiling_prepare_manage},
1707 {(DynFun*)region_rescue_clientwins,
1708 (DynFun*)tiling_rescue_clientwins},
1710 {(DynFun*)region_get_rescue_pholder_for,
1711 (DynFun*)tiling_get_rescue_pholder_for},
1713 {(DynFun*)region_get_configuration,
1714 (DynFun*)tiling_get_configuration},
1716 {(DynFun*)region_managed_may_destroy,
1717 (DynFun*)tiling_managed_may_destroy},
1719 {(DynFun*)region_may_destroy,
1720 (DynFun*)tiling_may_destroy},
1722 {(DynFun*)region_current,
1723 (DynFun*)tiling_current},
1725 {(DynFun*)tiling_managed_add,
1726 (DynFun*)tiling_managed_add_default},
1728 {region_manage_stdisp,
1729 tiling_manage_stdisp},
1731 {region_unmanage_stdisp,
1732 tiling_unmanage_stdisp},
1734 {(DynFun*)tiling_load_node,
1735 (DynFun*)tiling_load_node_default},
1743 {(DynFun*)region_navi_first,
1744 (DynFun*)tiling_navi_first},
1746 {(DynFun*)region_navi_next,
1747 (DynFun*)tiling_navi_next},
1749 {(DynFun*)region_xwindow,
1750 (DynFun*)tiling_xwindow},
1752 {(DynFun*)region_prepare_manage_transient,
1753 (DynFun*)tiling_prepare_manage_transient},
1760 IMPLCLASS(WTiling, WRegion, tiling_deinit, tiling_dynfuntab);