]> git.decadent.org.uk Git - ion3.git/blob - mod_panews/placement.c
73e269079136d93ce57081b9f61bcc9e28618ba8
[ion3.git] / mod_panews / placement.c
1 /*
2  * ion/mod_panews/placement.h
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2006. 
5  *
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.
10  */
11
12 #include <limits.h>
13 #include <math.h>
14 #include <string.h>
15
16 #include <libtu/minmax.h>
17 #include <libtu/objp.h>
18 #include <libextl/extl.h>
19 #include <libmainloop/defer.h>
20
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/resize.h>
29 #include <mod_tiling/split.h>
30 #include <mod_tiling/split-stdisp.h>
31 #include "placement.h"
32 #include "panews.h"
33 #include "splitext.h"
34 #include "unusedwin.h"
35
36
37 WHook *panews_init_layout_alt=NULL;
38 WHook *panews_make_placement_alt=NULL;
39
40
41 /*{{{ create_frame_for */
42
43
44 static WFrame *create_frame_for(WPaneWS *ws, WRegion *reg)
45 {
46     WWindow *par=REGION_PARENT(ws);
47     WFitParams fp;
48     WRectangle mg;
49     WFrame *frame;
50     
51     if(par==NULL)
52         return NULL;
53     
54     fp.g=REGION_GEOM(ws);
55     fp.mode=REGION_FIT_BOUNDS;
56     
57     frame=(WFrame*)ws->tiling.create_frame_fn(par, &fp);
58     
59     if(frame==NULL)
60         return NULL;
61
62     frame->flags|=FRAME_DEST_EMPTY;
63     
64     mplex_managed_geom((WMPlex*)frame, &mg);
65     
66     fp.g.w=REGION_GEOM(reg).w+(REGION_GEOM(frame).w-mg.w);
67     fp.g.h=REGION_GEOM(reg).h+(REGION_GEOM(frame).h-mg.h);
68     fp.mode=REGION_FIT_EXACT;
69     
70     region_fitrep((WRegion*)frame, NULL, &fp);
71     
72     return (WFrame*)frame;
73 }
74
75
76 /*}}}*/
77
78
79 /*{{{ Placement scan */
80
81
82 static bool mrsh_layout_extl(ExtlFn fn, WPaneWSPlacementParams *p)
83 {
84     ExtlTab t=extl_create_table();
85     bool ret=FALSE;
86     
87     extl_table_sets_o(t, "ws", (Obj*)p->ws);
88     extl_table_sets_o(t, "frame", (Obj*)p->frame);
89     extl_table_sets_o(t, "reg", (Obj*)p->reg);
90     extl_table_sets_o(t, "specifier", (Obj*)p->specifier);
91
92     extl_protect(NULL);
93     extl_call(fn, "t", "b", t, &ret);
94     extl_unprotect(NULL);
95     
96     if(ret){
97         ret=FALSE;
98
99         extl_table_gets_i(t, "res_w", &(p->res_w));
100         extl_table_gets_i(t, "res_h", &(p->res_h));
101         
102         if(extl_table_gets_o(t, "res_node", (Obj**)&(p->res_node))){
103             if(OBJ_IS(p->res_node, WSplitUnused)){
104                 if(!extl_table_gets_t(t, "res_config", &(p->res_config))){
105                     warn(TR("Malfunctioning placement hook; condition #%d."), 1);
106                     goto err;
107                 }
108             }else if(!OBJ_IS(p->res_node, WSplitRegion)){
109                 warn(TR("Malfunctioning placement hook; condition #%d."), 2);
110                 goto err;
111             }
112         }
113     }
114     
115     extl_unref_table(t);
116     
117     return ret;
118     
119 err:    
120     p->res_node=NULL;
121     extl_unref_table(t);
122     return FALSE;
123 }
124
125
126 static bool plainregionfilter(WSplit *node)
127 {
128     return (strcmp(OBJ_TYPESTR(node), "WSplitRegion")==0);
129 }
130
131
132 static bool fallback_filter(WSplit *node)
133 {
134     return (OBJ_IS(node, WSplitUnused) || plainregionfilter(node));
135 }
136
137
138 static bool fallback_layout(WPaneWSPlacementParams *p)
139 {
140     if(p->ws->tiling.split_tree==NULL)
141         return FALSE;
142     
143     if(p->specifier!=NULL){
144         p->res_node=(WSplit*)p->specifier;
145     }else{
146         p->res_node=split_current_todir(p->ws->tiling.split_tree, SPLIT_ANY,
147                                         PRIMN_ANY, fallback_filter);
148     }
149
150     if(p->res_node!=NULL && OBJ_IS(p->res_node, WSplitUnused)){
151         p->res_config=extl_create_table();
152         if(p->res_config==extl_table_none() || p->frame==NULL)
153             return FALSE;
154         extl_table_sets_o(p->res_config, "reg", (Obj*)(p->frame));
155     }
156     
157     return (p->res_node!=NULL);
158 }
159
160
161 /*}}}*/
162
163
164 /*{{{ Split/replace unused code */
165
166
167 static bool do_replace(WPaneWS *ws, WFrame *frame, WRegion *reg, 
168                        WPaneWSPlacementParams *rs)
169 {
170     WSplit *u=rs->res_node;
171     WSplit *node=tiling_load_node(&(ws->tiling), &(u->geom), rs->res_config);
172     
173     assert(OBJ_IS(u, WSplitUnused));
174     
175     if(node==NULL){
176         warn(TR("Malfunctioning placement hook; condition #%d."), 3);
177         return FALSE;
178     }
179
180     if(REGION_MANAGER(frame)!=(WRegion*)ws){
181         warn(TR("Malfunctioning placement hook; condition #%d."), 4);
182         destroy_obj((Obj*)node);
183         return FALSE;
184     }
185     
186     if(u->parent!=NULL)
187         splitinner_replace(u->parent, u, node);
188     else
189         splittree_changeroot((WSplit*)u, node);
190     
191     u->parent=NULL;
192     mainloop_defer_destroy((Obj*)u);
193     
194     if(ws->tiling.stdispnode!=NULL)
195         split_regularise_stdisp(ws->tiling.stdispnode);
196
197     if(ws->tiling.split_tree!=NULL)
198         split_restack(ws->tiling.split_tree, ws->tiling.dummywin, Above);
199
200     return TRUE;
201 }
202
203 /*}}}*/
204
205
206 /*{{{ The main dynfun */
207
208
209 static bool current_unused(WPaneWS *ws)
210 {
211     return OBJ_IS(tiling_current(&ws->tiling), WUnusedWin);
212 }
213
214
215 static WRegion *panews_get_target(WPaneWS *ws, WSplitUnused *specifier,
216                                   WRegion *reg)
217 {
218     WRegion *target=NULL;
219     WFrame *frame=create_frame_for(ws, reg);
220     WSplit **tree=&(ws->tiling.split_tree);
221     WPaneWSPlacementParams rs;
222     
223     assert(ws->tiling.split_tree!=NULL);
224
225     rs.ws=ws;
226     rs.frame=frame;
227     rs.reg=reg;
228     rs.specifier=specifier;
229     rs.res_node=NULL;
230     rs.res_config=extl_table_none();
231     rs.res_w=-1;
232     rs.res_h=-1;
233     
234     if(frame!=NULL){
235         split_update_bounds(*tree, TRUE);
236         
237         assert(panews_make_placement_alt!=NULL);
238         
239         hook_call_p(panews_make_placement_alt, &rs,
240                     (WHookMarshallExtl*)mrsh_layout_extl);
241     }
242         
243     if(rs.res_node==NULL && specifier==NULL)
244         fallback_layout(&rs);
245         
246     if(rs.res_node!=NULL){
247         /* Resize */
248         if(rs.res_w>0 || rs.res_h>0){
249             WRectangle grq=rs.res_node->geom;
250             int gflags=REGION_RQGEOM_WEAK_ALL;
251             
252             if(rs.res_w>0){
253                 grq.w=rs.res_w;
254                 gflags&=~REGION_RQGEOM_WEAK_W;
255             }
256             
257             if(rs.res_h>0){
258                 grq.h=rs.res_h;
259                 gflags&=~REGION_RQGEOM_WEAK_H;
260             }
261             
262             splittree_rqgeom(rs.res_node, gflags, &grq, NULL);
263         }
264         
265         if(OBJ_IS(rs.res_node, WSplitUnused)){
266             if(frame!=NULL){
267                 if(do_replace(ws, frame, reg, &rs))
268                     target=(WRegion*)frame;
269             }
270         }else{
271             assert(OBJ_IS(rs.res_node, WSplitRegion));
272             target=((WSplitRegion*)rs.res_node)->reg;
273         }
274         
275         extl_unref_table(rs.res_config);
276     }
277         
278     if(frame!=NULL && target!=(WRegion*)frame)
279         destroy_obj((Obj*)frame);
280     
281     if(target!=NULL && current_unused(ws))
282         region_goto(target);
283     
284     return target;
285 }
286
287
288 WPHolder *panews_prepare_manage(WPaneWS *ws, const WClientWin *cwin,
289                                 const WManageParams *param, int redir)
290 {
291     WRegion *target=panews_get_target(ws, NULL, (WRegion*)cwin);
292     WPHolder *ph;
293     
294     if(target!=NULL){
295         ph=region_prepare_manage(target, cwin, param, MANAGE_REDIR_PREFER_YES);
296         if(ph!=NULL)
297             return ph;
298     }
299
300     warn(TR("Ooops... could not find a region to attach client window to "
301             "on workspace %s."), region_name((WRegion*)ws));
302     return NULL;
303 }
304
305
306 bool panews_handle_unused_drop(WPaneWS *ws, WSplitUnused *specifier, 
307                                WRegion *reg)
308 {
309     WRegion *target=panews_get_target(ws, specifier, reg);
310     
311     if(target==NULL || !OBJ_IS(target, WMPlex))
312         return FALSE;
313     
314     return (mplex_attach_simple((WMPlex*)target, reg, 
315                                 MPLEX_ATTACH_SWITCHTO)!=NULL);
316 }
317
318
319 /*}}}*/
320