Better error message
[notion/jeffpc.git] / ioncore / sizepolicy.c
blobea2fb9ded0193a36215f0b9e8b1c5fdfda78807b
1 /*
2 * ion/ioncore/sizepolicy.c
4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
7 */
9 #include <libtu/minmax.h>
10 #include <string.h>
12 #include "common.h"
13 #include "region.h"
14 #include "resize.h"
15 #include "sizehint.h"
16 #include "sizepolicy.h"
20 static int fit_x(int x, int w, const WRectangle *max_geom)
22 int mw=maxof(max_geom->w, 1);
23 w=minof(mw, w);
24 return minof(maxof(x, max_geom->x), max_geom->x+mw-w);
28 static int fit_y(int y, int h, const WRectangle *max_geom)
30 int mh=maxof(max_geom->h, 1);
31 h=minof(mh, h);
32 return minof(maxof(y, max_geom->y), max_geom->y+mh-h);
36 static void do_gravity(const WRectangle *max_geom, int szplcy,
37 WRectangle *geom)
39 /* Assumed: width and height already adjusted within limits */
40 if(geom->h<1)
41 geom->h=1;
42 if(geom->w<1)
43 geom->w=1;
45 switch(szplcy&SIZEPOLICY_HORIZ_MASK){
46 case SIZEPOLICY_HORIZ_LEFT:
47 geom->x=max_geom->x;
48 break;
50 case SIZEPOLICY_HORIZ_RIGHT:
51 geom->x=max_geom->x+max_geom->w-geom->w;
52 break;
54 case SIZEPOLICY_HORIZ_CENTER:
55 geom->x=max_geom->x+max_geom->w/2-geom->w/2;
56 break;
58 default:
59 geom->x=fit_x(geom->x, geom->w, max_geom);
62 switch(szplcy&SIZEPOLICY_VERT_MASK){
63 case SIZEPOLICY_VERT_TOP:
64 geom->y=max_geom->y;
65 break;
67 case SIZEPOLICY_VERT_BOTTOM:
68 geom->y=max_geom->y+max_geom->h-geom->h;
69 break;
71 case SIZEPOLICY_VERT_CENTER:
72 geom->y=max_geom->y+max_geom->h/2-geom->h/2;
73 break;
75 default:
76 geom->y=fit_x(geom->y, geom->h, max_geom);
81 static void gravity_stretch_policy(int szplcy, WRegion *reg,
82 const WRectangle *rq_geom, WFitParams *fp,
83 bool ws, bool hs)
85 WRectangle max_geom=fp->g;
86 int w, h;
88 fp->g=*rq_geom;
90 w=(ws ? max_geom.w : minof(rq_geom->w, max_geom.w));
91 h=(hs ? max_geom.h : minof(rq_geom->h, max_geom.h));
93 if(reg!=NULL)
94 region_size_hints_correct(reg, &w, &h, FALSE);
96 fp->g.w=w;
97 fp->g.h=h;
99 do_gravity(&max_geom, szplcy, &(fp->g));
103 static void sizepolicy_free_snap(WSizePolicy *szplcy, WRegion *reg,
104 WRectangle *rq_geom, int rq_flags,
105 WFitParams *fp)
107 WRectangle max_geom=fp->g;
108 bool fullw=((rq_flags&REGION_RQGEOM_WEAK_W) &&
109 (*szplcy&SIZEPOLICY_HORIZ_MASK)==SIZEPOLICY_HORIZ_CENTER);
110 bool fullh=((rq_flags&REGION_RQGEOM_WEAK_H) &&
111 (*szplcy&SIZEPOLICY_VERT_MASK)==SIZEPOLICY_VERT_CENTER);
113 int w=(fullw ? max_geom.w : minof(rq_geom->w, max_geom.w));
114 int h=(fullh ? max_geom.h : minof(rq_geom->h, max_geom.h));
115 int x_=0, y_=0;
117 /* ignore out-of-bound values for 'x' entirely */
118 if(!(rq_flags&REGION_RQGEOM_WEAK_X) && rq_geom->x > max_geom.w){
119 rq_flags|=REGION_RQGEOM_WEAK_X;
120 rq_geom->x = reg->geom.x;
123 /* ignore out-of-bound values for 'y' entirely */
124 if(!(rq_flags&REGION_RQGEOM_WEAK_Y) && rq_geom->y > max_geom.h){
125 rq_flags|=REGION_RQGEOM_WEAK_Y;
126 rq_geom->y = reg->geom.y;
129 if(!(rq_flags&REGION_RQGEOM_WEAK_X)
130 && rq_flags&REGION_RQGEOM_WEAK_W){
131 x_=fit_x(rq_geom->x, 1, &max_geom);
132 if(((*szplcy)&SIZEPOLICY_HORIZ_MASK)==SIZEPOLICY_HORIZ_RIGHT)
133 w=max_geom.x+max_geom.w-x_;
134 else
135 w=minof(w, max_geom.x+max_geom.w-x_);
138 if(!(rq_flags&REGION_RQGEOM_WEAK_Y)
139 && rq_flags&REGION_RQGEOM_WEAK_H){
140 y_=fit_x(rq_geom->y, 1, &max_geom);
141 if(((*szplcy)&SIZEPOLICY_VERT_MASK)==SIZEPOLICY_VERT_BOTTOM)
142 h=max_geom.y+max_geom.h-y_;
143 else
144 h=minof(h, max_geom.y+max_geom.h-y_);
147 if(reg!=NULL)
148 region_size_hints_correct(reg, &w, &h, FALSE);
150 fp->g.w=w;
151 fp->g.h=h;
153 if(!(rq_flags&REGION_RQGEOM_WEAK_X)
154 && rq_flags&REGION_RQGEOM_WEAK_W){
155 fp->g.x=x_;
156 }else if(rq_flags&REGION_RQGEOM_WEAK_X){
157 switch((*szplcy)&SIZEPOLICY_HORIZ_MASK){
158 case SIZEPOLICY_HORIZ_CENTER:
159 fp->g.x=max_geom.x+(max_geom.w-w)/2;
160 break;
162 case SIZEPOLICY_HORIZ_LEFT:
163 fp->g.x=max_geom.x;
164 break;
166 case SIZEPOLICY_HORIZ_RIGHT:
167 fp->g.x=max_geom.x+max_geom.w-w;
168 break;
170 default:
171 fp->g.x=fit_x(rq_geom->x, w, &max_geom);
172 break;
174 }else{
175 fp->g.x=fit_x(rq_geom->x, w, &max_geom);
178 if(!(rq_flags&REGION_RQGEOM_WEAK_Y)
179 && rq_flags&REGION_RQGEOM_WEAK_H){
180 fp->g.y=y_;
181 }else if(rq_flags&REGION_RQGEOM_WEAK_Y){
182 switch((*szplcy)&SIZEPOLICY_VERT_MASK){
183 case SIZEPOLICY_VERT_CENTER:
184 fp->g.y=max_geom.y+(max_geom.h-h)/2;
185 break;
187 case SIZEPOLICY_VERT_TOP:
188 fp->g.y=max_geom.y;
189 break;
191 case SIZEPOLICY_VERT_BOTTOM:
192 fp->g.y=max_geom.y+max_geom.h-h;
193 break;
195 default:
196 fp->g.y=fit_y(rq_geom->y, h, &max_geom);
197 break;
199 }else{
200 fp->g.y=fit_y(rq_geom->y, h, &max_geom);
203 (*szplcy)&=~(SIZEPOLICY_VERT_MASK|SIZEPOLICY_HORIZ_MASK);
205 *szplcy|=( (fullw || fp->g.x<=max_geom.x ? SIZEPOLICY_HORIZ_LEFT : 0)
206 |(fullw || fp->g.x+fp->g.w>=max_geom.x+max_geom.w ? SIZEPOLICY_HORIZ_RIGHT : 0)
207 |(fullh || fp->g.y<=max_geom.y ? SIZEPOLICY_VERT_TOP : 0)
208 |(fullh || fp->g.y+fp->g.h>=max_geom.y+max_geom.h ? SIZEPOLICY_VERT_BOTTOM : 0));
212 static WSizePolicy org(WSizePolicy base, WSizePolicy g)
214 if((base&SIZEPOLICY_VERT_MASK)==0)
215 base|=g&SIZEPOLICY_VERT_MASK;
217 if((base&SIZEPOLICY_HORIZ_MASK)==0)
218 base|=g&SIZEPOLICY_HORIZ_MASK;
220 return base;
224 void sizepolicy(WSizePolicy *szplcy, WRegion *reg,
225 const WRectangle *rq_geom, int rq_flags,
226 WFitParams *fp)
228 uint extra=fp->mode&REGION_FIT_ROTATE;
230 WRectangle tmp;
231 if(rq_geom!=NULL)
232 tmp=*rq_geom;
233 else if(reg!=NULL)
234 tmp=REGION_GEOM(reg);
235 else
236 tmp=fp->g;
238 if((*szplcy)&SIZEPOLICY_SHRUNK){
239 if(reg!=NULL){
240 tmp.w=region_min_w(reg);
241 tmp.h=region_min_h(reg);
242 }else{
243 tmp.w=1;
244 tmp.h=1;
246 rq_flags&=~(REGION_RQGEOM_WEAK_W|REGION_RQGEOM_WEAK_H);
249 fp->mode=REGION_FIT_EXACT|extra;
251 switch((*szplcy)&SIZEPOLICY_MASK){
252 case SIZEPOLICY_GRAVITY:
253 gravity_stretch_policy(*szplcy, reg, &tmp, fp, FALSE, FALSE);
254 break;
256 case SIZEPOLICY_STRETCH_LEFT:
257 gravity_stretch_policy(org(*szplcy, SIZEPOLICY_HORIZ_LEFT|SIZEPOLICY_VERT_CENTER),
258 reg, &tmp, fp, FALSE, TRUE);
259 break;
261 case SIZEPOLICY_STRETCH_RIGHT:
262 gravity_stretch_policy(org(*szplcy, SIZEPOLICY_HORIZ_RIGHT|SIZEPOLICY_VERT_CENTER),
263 reg, &tmp, fp, FALSE, TRUE);
264 break;
266 case SIZEPOLICY_STRETCH_TOP:
267 gravity_stretch_policy(org(*szplcy, SIZEPOLICY_VERT_TOP|SIZEPOLICY_HORIZ_CENTER),
268 reg, &tmp, fp, TRUE, FALSE);
269 break;
271 case SIZEPOLICY_STRETCH_BOTTOM:
272 gravity_stretch_policy(org(*szplcy, SIZEPOLICY_VERT_BOTTOM|SIZEPOLICY_HORIZ_CENTER),
273 reg, &tmp, fp, TRUE, FALSE);
274 break;
276 case SIZEPOLICY_FULL_EXACT:
277 gravity_stretch_policy(org(*szplcy, SIZEPOLICY_VERT_CENTER|SIZEPOLICY_HORIZ_CENTER),
278 reg, &tmp, fp, TRUE, TRUE);
279 break;
281 case SIZEPOLICY_FREE:
282 if(reg!=NULL)
283 region_size_hints_correct(reg, &tmp.w, &tmp.h, FALSE);
284 rectangle_constrain(&tmp, &(fp->g));
285 fp->g=tmp;
286 break;
288 case SIZEPOLICY_VISIBILITY_CONSTRAINED:
289 if(reg!=NULL)
290 region_size_hints_correct(reg, &tmp.w, &tmp.h, FALSE);
292 /* Constraint such that at least min(size, CF_VISIBILITY_CONSTRAINT)
293 * much is visible at each border.
295 int dx=maxof(0, tmp.w-CF_VISIBILITY_CONSTRAINT);
296 int dy=maxof(0, tmp.h-CF_VISIBILITY_CONSTRAINT);
297 tmp.x=maxof(fp->g.x-dx, minof(tmp.x, fp->g.x+fp->g.w+dx-tmp.w));
298 tmp.y=maxof(fp->g.y-dy, minof(tmp.y, fp->g.y+fp->g.h+dy-tmp.h));
300 fp->g=tmp;
301 break;
303 case SIZEPOLICY_UNCONSTRAINED:
304 if(reg!=NULL)
305 region_size_hints_correct(reg, &tmp.w, &tmp.h, TRUE);
306 fp->g=tmp;
307 break;
309 case SIZEPOLICY_FREE_GLUE:
310 sizepolicy_free_snap(szplcy, reg, &tmp, rq_flags, fp);
311 break;
313 case SIZEPOLICY_FULL_BOUNDS:
314 default:
315 fp->mode=REGION_FIT_BOUNDS|extra;
316 break;
321 /* translation table for sizepolicy specifications */
322 static StringIntMap szplcy_specs[] = {
323 {"default", SIZEPOLICY_DEFAULT},
324 {"full", SIZEPOLICY_FULL_EXACT},
325 {"full_bounds", SIZEPOLICY_FULL_BOUNDS},
326 {"free", SIZEPOLICY_FREE},
327 {"free_glue", SIZEPOLICY_FREE_GLUE},
328 {"northwest", SIZEPOLICY_GRAVITY_NORTHWEST},
329 {"north", SIZEPOLICY_GRAVITY_NORTH},
330 {"northeast", SIZEPOLICY_GRAVITY_NORTHEAST},
331 {"west", SIZEPOLICY_GRAVITY_WEST},
332 {"center", SIZEPOLICY_GRAVITY_CENTER},
333 {"east", SIZEPOLICY_GRAVITY_EAST},
334 {"southwest", SIZEPOLICY_GRAVITY_SOUTHWEST},
335 {"south", SIZEPOLICY_GRAVITY_SOUTH},
336 {"southeast", SIZEPOLICY_GRAVITY_SOUTHEAST},
337 {"stretch_top", SIZEPOLICY_STRETCH_TOP},
338 {"stretch_bottom", SIZEPOLICY_STRETCH_BOTTOM},
339 {"stretch_left", SIZEPOLICY_STRETCH_LEFT},
340 {"stretch_right", SIZEPOLICY_STRETCH_RIGHT},
341 {"free_glue_northwest", SIZEPOLICY_FREE_GLUE__NORTHWEST},
342 {"free_glue_north", SIZEPOLICY_FREE_GLUE__NORTH},
343 {"free_glue_northeast", SIZEPOLICY_FREE_GLUE__NORTHEAST},
344 {"free_glue_west", SIZEPOLICY_FREE_GLUE__WEST},
345 {"free_glue_center", SIZEPOLICY_FREE_GLUE__CENTER},
346 {"free_glue_east", SIZEPOLICY_FREE_GLUE__EAST},
347 {"free_glue_southwest", SIZEPOLICY_FREE_GLUE__SOUTHWEST},
348 {"free_glue_south", SIZEPOLICY_FREE_GLUE__SOUTH},
349 {"free_glue_southeast", SIZEPOLICY_FREE_GLUE__SOUTHEAST},
350 {"visibility_constrained", SIZEPOLICY_VISIBILITY_CONSTRAINED},
351 {"unconstrained", SIZEPOLICY_UNCONSTRAINED},
352 { NULL, SIZEPOLICY_DEFAULT} /* end marker */
356 bool string2sizepolicy(const char *szplcy, WSizePolicy *value)
358 int tmp;
360 tmp=stringintmap_value(szplcy_specs, szplcy, -1);
362 if(tmp==-1){
363 *value=SIZEPOLICY_DEFAULT;
364 return FALSE;
365 }else{
366 *value=tmp;
367 return TRUE;
372 const char *sizepolicy2string(WSizePolicy szplcy)
374 const char* str=stringintmap_key(szplcy_specs, szplcy, NULL);
375 if(str==NULL){
376 /* fall back on policy without modifiers if full name not found
378 * Without this, the scratchpad sometimes became impossible to resize
379 * after reboots.
380 * http://lists.berlios.de/pipermail/ion-general/2009-December/001775.html
381 * http://article.gmane.org/gmane.comp.window-managers.ion.general/8897/match=scratchpad
383 str=stringintmap_key(szplcy_specs, szplcy&0xff, NULL);
385 return str;