trunk: changeset 1927
[notion/jeffpc.git] / mod_panews / placement.c
blob0b9911d233e9dea3fa399168d7510c7b0ec32e93
1 /*
2 * ion/mod_panews/placement.h
4 * Copyright (c) Tuomo Valkonen 1999-2005.
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.
12 #include <limits.h>
13 #include <math.h>
14 #include <string.h>
16 #include <libtu/minmax.h>
17 #include <libtu/objp.h>
18 #include <libextl/extl.h>
19 #include <libmainloop/defer.h>
21 #include <ioncore/common.h>
22 #include <ioncore/global.h>
23 #include <ioncore/clientwin.h>
24 #include <ioncore/attach.h>
25 #include <ioncore/manage.h>
26 #include <ioncore/framep.h>
27 #include <ioncore/names.h>
28 #include <ioncore/region-iter.h>
29 #include <ioncore/resize.h>
30 #include <mod_ionws/split.h>
31 #include <mod_ionws/split-stdisp.h>
32 #include "placement.h"
33 #include "panews.h"
34 #include "splitext.h"
35 #include "unusedwin.h"
38 WHook *panews_init_layout_alt=NULL;
39 WHook *panews_make_placement_alt=NULL;
42 /*{{{ create_frame_for */
45 static WFrame *create_frame_for(WPaneWS *ws, WRegion *reg)
47 WWindow *par=REGION_PARENT(ws);
48 WFitParams fp;
49 WRectangle mg;
50 WFrame *frame;
52 if(par==NULL)
53 return NULL;
55 fp.g=REGION_GEOM(ws);
56 fp.mode=REGION_FIT_BOUNDS;
58 frame=(WFrame*)ws->ionws.create_frame_fn(par, &fp);
60 if(frame==NULL)
61 return NULL;
63 frame->flags|=FRAME_DEST_EMPTY;
65 mplex_managed_geom((WMPlex*)frame, &mg);
67 fp.g.w=REGION_GEOM(reg).w+(REGION_GEOM(frame).w-mg.w);
68 fp.g.h=REGION_GEOM(reg).h+(REGION_GEOM(frame).h-mg.h);
69 fp.mode=REGION_FIT_EXACT;
71 region_fitrep((WRegion*)frame, NULL, &fp);
73 return (WFrame*)frame;
77 /*}}}*/
80 /*{{{ Placement scan */
83 static bool mrsh_layout_extl(ExtlFn fn, WPaneWSPlacementParams *p)
85 ExtlTab t=extl_create_table();
86 bool ret=FALSE;
88 extl_table_sets_o(t, "ws", (Obj*)p->ws);
89 extl_table_sets_o(t, "frame", (Obj*)p->frame);
90 extl_table_sets_o(t, "reg", (Obj*)p->reg);
91 extl_table_sets_o(t, "specifier", (Obj*)p->specifier);
93 extl_call(fn, "t", "b", t, &ret);
95 if(ret){
96 ret=FALSE;
98 extl_table_gets_i(t, "res_w", &(p->res_w));
99 extl_table_gets_i(t, "res_h", &(p->res_h));
101 if(extl_table_gets_o(t, "res_node", (Obj**)&(p->res_node))){
102 if(OBJ_IS(p->res_node, WSplitUnused)){
103 if(!extl_table_gets_t(t, "res_config", &(p->res_config))){
104 warn(TR("Malfunctioning placement hook; condition #%d."), 1);
105 goto err;
107 }else if(!OBJ_IS(p->res_node, WSplitRegion)){
108 warn(TR("Malfunctioning placement hook; condition #%d."), 2);
109 goto err;
114 extl_unref_table(t);
116 return ret;
118 err:
119 p->res_node=NULL;
120 extl_unref_table(t);
121 return FALSE;
125 static bool plainregionfilter(WSplit *node)
127 return (strcmp(OBJ_TYPESTR(node), "WSplitRegion")==0);
131 static bool fallback_filter(WSplit *node)
133 return (OBJ_IS(node, WSplitUnused) || plainregionfilter(node));
137 static bool fallback_layout(WPaneWSPlacementParams *p)
139 if(p->ws->ionws.split_tree==NULL)
140 return FALSE;
142 if(p->specifier!=NULL){
143 p->res_node=(WSplit*)p->specifier;
144 }else{
145 p->res_node=split_current_todir(p->ws->ionws.split_tree, SPLIT_ANY,
146 PRIMN_ANY, fallback_filter);
149 if(p->res_node!=NULL && OBJ_IS(p->res_node, WSplitUnused)){
150 p->res_config=extl_create_table();
151 if(p->res_config==extl_table_none() || p->frame==NULL)
152 return FALSE;
153 extl_table_sets_o(p->res_config, "reg", (Obj*)(p->frame));
156 return (p->res_node!=NULL);
160 /*}}}*/
163 /*{{{ Split/replace unused code */
166 static bool do_replace(WPaneWS *ws, WFrame *frame, WRegion *reg,
167 WPaneWSPlacementParams *rs)
169 WSplit *u=rs->res_node;
170 WSplit *node=ionws_load_node(&(ws->ionws), &(u->geom), rs->res_config);
172 assert(OBJ_IS(u, WSplitUnused));
174 if(node==NULL){
175 warn(TR("Malfunctioning placement hook; condition #%d."), 3);
176 return FALSE;
179 if(REGION_MANAGER(frame)!=(WRegion*)ws){
180 warn(TR("Malfunctioning placement hook; condition #%d."), 4);
181 destroy_obj((Obj*)node);
182 return FALSE;
185 if(u->parent!=NULL)
186 splitinner_replace(u->parent, u, node);
187 else
188 splittree_changeroot((WSplit*)u, node);
190 u->parent=NULL;
191 mainloop_defer_destroy((Obj*)u);
193 if(ws->ionws.stdispnode!=NULL)
194 split_regularise_stdisp(ws->ionws.stdispnode);
196 if(ws->ionws.split_tree!=NULL)
197 split_restack(ws->ionws.split_tree, ((WGenWS*)ws)->dummywin, Above);
199 return TRUE;
202 /*}}}*/
205 /*{{{ The main dynfun */
208 static bool current_unused(WPaneWS *ws)
210 return OBJ_IS(ionws_current(&ws->ionws), WUnusedWin);
214 static WRegion *panews_get_target(WPaneWS *ws, WSplitUnused *specifier,
215 WRegion *reg)
217 WRegion *target=NULL;
218 WFrame *frame=create_frame_for(ws, reg);
219 WSplit **tree=&(ws->ionws.split_tree);
220 WPaneWSPlacementParams rs;
222 assert(ws->ionws.split_tree!=NULL);
224 rs.ws=ws;
225 rs.frame=frame;
226 rs.reg=reg;
227 rs.specifier=specifier;
228 rs.res_node=NULL;
229 rs.res_config=extl_table_none();
230 rs.res_w=-1;
231 rs.res_h=-1;
233 if(frame!=NULL){
234 split_update_bounds(*tree, TRUE);
236 assert(panews_make_placement_alt!=NULL);
238 hook_call_p(panews_make_placement_alt, &rs,
239 (WHookMarshallExtl*)mrsh_layout_extl);
242 if(rs.res_node==NULL && specifier==NULL)
243 fallback_layout(&rs);
245 if(rs.res_node!=NULL){
246 /* Resize */
247 if(rs.res_w>0 || rs.res_h>0){
248 WRectangle grq=rs.res_node->geom;
249 int gflags=REGION_RQGEOM_WEAK_ALL;
251 if(rs.res_w>0){
252 grq.w=rs.res_w;
253 gflags&=~REGION_RQGEOM_WEAK_W;
256 if(rs.res_h>0){
257 grq.h=rs.res_h;
258 gflags&=~REGION_RQGEOM_WEAK_H;
261 splittree_rqgeom(rs.res_node, gflags, &grq, NULL);
264 if(OBJ_IS(rs.res_node, WSplitUnused)){
265 if(frame!=NULL){
266 if(do_replace(ws, frame, reg, &rs))
267 target=(WRegion*)frame;
269 }else{
270 assert(OBJ_IS(rs.res_node, WSplitRegion));
271 target=((WSplitRegion*)rs.res_node)->reg;
274 extl_unref_table(rs.res_config);
277 if(frame!=NULL && target!=(WRegion*)frame)
278 destroy_obj((Obj*)frame);
280 if(target!=NULL && current_unused(ws))
281 region_goto(target);
283 return target;
287 bool panews_manage_clientwin(WPaneWS *ws, WClientWin *cwin,
288 const WManageParams *param, int redir)
290 WRegion *target=panews_get_target(ws, NULL, (WRegion*)cwin);
292 if(target!=NULL){
293 if(region_manage_clientwin(target, cwin, param,
294 MANAGE_REDIR_PREFER_YES)){
295 return TRUE;
299 warn(TR("Ooops... could not find a region to attach client window to "
300 "on workspace %s."), region_name((WRegion*)ws));
301 return FALSE;
305 bool panews_handle_unused_drop(WPaneWS *ws, WSplitUnused *specifier,
306 WRegion *reg)
308 WRegion *target=panews_get_target(ws, specifier, reg);
310 if(target==NULL || !OBJ_IS(target, WMPlex))
311 return FALSE;
313 return (mplex_attach_simple((WMPlex*)target, reg,
314 MPLEX_ATTACH_SWITCHTO)!=NULL);
318 /*}}}*/