]> git.decadent.org.uk Git - ion3.git/blob - ioncore/sizepolicy.c
Imported Upstream version 20090110
[ion3.git] / ioncore / sizepolicy.c
1 /*
2  * ion/ioncore/sizepolicy.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2009. 
5  *
6  * See the included file LICENSE for details.
7  */
8
9 #include <libtu/minmax.h>
10 #include <string.h>
11
12 #include "common.h"
13 #include "region.h"
14 #include "resize.h"
15 #include "sizehint.h"
16 #include "sizepolicy.h"
17
18
19
20 static int fit_x(int x, int w, const WRectangle *max_geom)
21 {
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);
25 }
26
27
28 static int fit_y(int y, int h, const WRectangle *max_geom)
29 {
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);
33 }
34
35
36 static void do_gravity(const WRectangle *max_geom, int szplcy,
37                        WRectangle *geom)
38 {
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;
44     
45     switch(szplcy&SIZEPOLICY_HORIZ_MASK){
46     case SIZEPOLICY_HORIZ_LEFT:
47         geom->x=max_geom->x;
48         break;
49         
50     case SIZEPOLICY_HORIZ_RIGHT:
51         geom->x=max_geom->x+max_geom->w-geom->w;
52         break;
53
54     case SIZEPOLICY_HORIZ_CENTER:
55         geom->x=max_geom->x+max_geom->w/2-geom->w/2;
56         break;
57         
58     default:
59         geom->x=fit_x(geom->x, geom->w, max_geom);
60     }
61
62     switch(szplcy&SIZEPOLICY_VERT_MASK){
63     case SIZEPOLICY_VERT_TOP:
64         geom->y=max_geom->y;
65         break;
66         
67     case SIZEPOLICY_VERT_BOTTOM:
68         geom->y=max_geom->y+max_geom->h-geom->h;
69         break;
70
71     case SIZEPOLICY_VERT_CENTER:
72         geom->y=max_geom->y+max_geom->h/2-geom->h/2;
73         break;
74         
75     default:
76         geom->y=fit_x(geom->y, geom->h, max_geom);
77     }
78 }
79
80
81 static void gravity_stretch_policy(int szplcy, WRegion *reg,
82                                    const WRectangle *rq_geom, WFitParams *fp, 
83                                    bool ws, bool hs)
84 {
85     WRectangle max_geom=fp->g;
86     int w, h;
87
88     fp->g=*rq_geom;
89     
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));
92     
93     if(reg!=NULL)
94         region_size_hints_correct(reg,  &w, &h, FALSE);
95     
96     fp->g.w=w;
97     fp->g.h=h;
98     
99     do_gravity(&max_geom, szplcy, &(fp->g));
100 }
101
102
103 static void sizepolicy_free_snap(WSizePolicy *szplcy, WRegion *reg,
104                                  WRectangle *rq_geom, int rq_flags,
105                                  WFitParams *fp)
106 {
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);
112
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;
116
117     
118     if(!(rq_flags&REGION_RQGEOM_WEAK_X) 
119        && rq_flags&REGION_RQGEOM_WEAK_W){
120         x_=fit_x(rq_geom->x, 1, &max_geom);
121         if(((*szplcy)&SIZEPOLICY_HORIZ_MASK)==SIZEPOLICY_HORIZ_RIGHT)
122             w=max_geom.x+max_geom.w-x_;
123         else
124             w=minof(w, max_geom.x+max_geom.w-x_);
125     }
126     
127     if(!(rq_flags&REGION_RQGEOM_WEAK_Y)
128        && rq_flags&REGION_RQGEOM_WEAK_H){
129         y_=fit_x(rq_geom->y, 1, &max_geom);
130         if(((*szplcy)&SIZEPOLICY_VERT_MASK)==SIZEPOLICY_VERT_BOTTOM)
131             h=max_geom.y+max_geom.h-y_;
132         else
133             h=minof(h, max_geom.y+max_geom.h-y_);
134     }
135        
136     if(reg!=NULL)
137         region_size_hints_correct(reg, &w, &h, FALSE);
138     
139     fp->g.w=w;
140     fp->g.h=h;
141     
142     if(!(rq_flags&REGION_RQGEOM_WEAK_X) 
143        && rq_flags&REGION_RQGEOM_WEAK_W){
144         fp->g.x=x_;
145     }else if(rq_flags&REGION_RQGEOM_WEAK_X){
146         switch((*szplcy)&SIZEPOLICY_HORIZ_MASK){
147         case SIZEPOLICY_HORIZ_CENTER:
148             fp->g.x=max_geom.x+(max_geom.w-w)/2;
149             break;
150  
151         case SIZEPOLICY_HORIZ_LEFT:
152             fp->g.x=max_geom.x;
153             break;
154             
155         case SIZEPOLICY_HORIZ_RIGHT:
156             fp->g.x=max_geom.x+max_geom.w-w;
157             break;
158             
159         default:
160             fp->g.x=fit_x(rq_geom->x, w, &max_geom);
161             break;
162         }
163     }else{
164         fp->g.x=fit_x(rq_geom->x, w, &max_geom);
165     }
166     
167     if(!(rq_flags&REGION_RQGEOM_WEAK_Y)
168        && rq_flags&REGION_RQGEOM_WEAK_H){
169         fp->g.y=y_;
170     }else if(rq_flags&REGION_RQGEOM_WEAK_Y){
171         switch((*szplcy)&SIZEPOLICY_VERT_MASK){
172         case SIZEPOLICY_VERT_CENTER:
173             fp->g.y=max_geom.y+(max_geom.h-h)/2;
174             break;
175             
176         case SIZEPOLICY_VERT_TOP:
177             fp->g.y=max_geom.y;
178             break;
179             
180         case SIZEPOLICY_VERT_BOTTOM:
181             fp->g.y=max_geom.y+max_geom.h-h;
182             break;
183             
184         default:
185             fp->g.y=fit_y(rq_geom->y, h, &max_geom);
186             break;
187         }
188     }else{
189         fp->g.y=fit_y(rq_geom->y, h, &max_geom);
190     }
191     
192     (*szplcy)&=~(SIZEPOLICY_VERT_MASK|SIZEPOLICY_HORIZ_MASK);
193     
194     *szplcy|=( (fullw || fp->g.x<=max_geom.x ? SIZEPOLICY_HORIZ_LEFT : 0)
195               |(fullw || fp->g.x+fp->g.w>=max_geom.x+max_geom.w ? SIZEPOLICY_HORIZ_RIGHT : 0)
196               |(fullh || fp->g.y<=max_geom.y ? SIZEPOLICY_VERT_TOP : 0)
197               |(fullh || fp->g.y+fp->g.h>=max_geom.y+max_geom.h ? SIZEPOLICY_VERT_BOTTOM : 0));
198 }
199
200
201 static WSizePolicy org(WSizePolicy base, WSizePolicy g)
202 {
203     if((base&SIZEPOLICY_VERT_MASK)==0)
204         base|=g&SIZEPOLICY_VERT_MASK;
205         
206     if((base&SIZEPOLICY_HORIZ_MASK)==0)
207         base|=g&SIZEPOLICY_HORIZ_MASK;
208     
209     return base;
210 }
211
212
213 void sizepolicy(WSizePolicy *szplcy, WRegion *reg,
214                 const WRectangle *rq_geom, int rq_flags,
215                 WFitParams *fp)
216 {
217     uint extra=fp->mode&REGION_FIT_ROTATE;
218     
219     WRectangle tmp;
220     if(rq_geom!=NULL)
221         tmp=*rq_geom;
222     else if(reg!=NULL)
223         tmp=REGION_GEOM(reg);
224     else
225         tmp=fp->g;
226     
227     if((*szplcy)&SIZEPOLICY_SHRUNK){
228         if(reg!=NULL){
229             tmp.w=region_min_w(reg);
230             tmp.h=region_min_h(reg);
231         }else{
232             tmp.w=1;
233             tmp.h=1;
234         }
235         rq_flags&=~(REGION_RQGEOM_WEAK_W|REGION_RQGEOM_WEAK_H);
236     }
237
238     fp->mode=REGION_FIT_EXACT|extra;
239     
240     switch((*szplcy)&SIZEPOLICY_MASK){
241     case SIZEPOLICY_GRAVITY:
242         gravity_stretch_policy(*szplcy, reg, &tmp, fp, FALSE, FALSE);
243         break;
244         
245     case SIZEPOLICY_STRETCH_LEFT:
246         gravity_stretch_policy(org(*szplcy, SIZEPOLICY_HORIZ_LEFT|SIZEPOLICY_VERT_CENTER), 
247                                reg, &tmp, fp, FALSE, TRUE);
248         break;
249         
250     case SIZEPOLICY_STRETCH_RIGHT:
251         gravity_stretch_policy(org(*szplcy, SIZEPOLICY_HORIZ_RIGHT|SIZEPOLICY_VERT_CENTER), 
252                                reg, &tmp, fp, FALSE, TRUE);
253         break;
254         
255     case SIZEPOLICY_STRETCH_TOP:
256         gravity_stretch_policy(org(*szplcy, SIZEPOLICY_VERT_TOP|SIZEPOLICY_HORIZ_CENTER), 
257                                reg, &tmp, fp, TRUE, FALSE);
258         break;
259         
260     case SIZEPOLICY_STRETCH_BOTTOM:
261         gravity_stretch_policy(org(*szplcy, SIZEPOLICY_VERT_BOTTOM|SIZEPOLICY_HORIZ_CENTER), 
262                                reg, &tmp, fp, TRUE, FALSE);
263         break;
264         
265     case SIZEPOLICY_FULL_EXACT:
266         gravity_stretch_policy(org(*szplcy, SIZEPOLICY_VERT_CENTER|SIZEPOLICY_HORIZ_CENTER), 
267                                reg, &tmp, fp, TRUE, TRUE);
268         break;
269         
270     case SIZEPOLICY_FREE:
271         if(reg!=NULL)
272             region_size_hints_correct(reg, &tmp.w, &tmp.h, FALSE);
273         rectangle_constrain(&tmp, &(fp->g));
274         fp->g=tmp;
275         break;
276         
277     case SIZEPOLICY_VISIBILITY_CONSTRAINED:
278         if(reg!=NULL)
279             region_size_hints_correct(reg, &tmp.w, &tmp.h, FALSE);
280         { 
281             /* Constraint such that at least min(size, CF_VISIBILITY_CONSTRAINT) 
282              * much is visible at each border.
283              */
284             int dx=maxof(0, tmp.w-CF_VISIBILITY_CONSTRAINT);
285             int dy=maxof(0, tmp.h-CF_VISIBILITY_CONSTRAINT);
286             tmp.x=maxof(fp->g.x-dx, minof(tmp.x, fp->g.x+fp->g.w+dx-tmp.w));
287             tmp.y=maxof(fp->g.y-dy, minof(tmp.y, fp->g.y+fp->g.h+dy-tmp.h));
288         }
289         fp->g=tmp;
290         break;
291         
292     case SIZEPOLICY_UNCONSTRAINED:
293         if(reg!=NULL)
294             region_size_hints_correct(reg, &tmp.w, &tmp.h, TRUE);
295         fp->g=tmp;
296         break;
297
298     case SIZEPOLICY_FREE_GLUE:
299         sizepolicy_free_snap(szplcy, reg, &tmp, rq_flags, fp);
300         break;
301         
302     case SIZEPOLICY_FULL_BOUNDS:
303     default:
304         fp->mode=REGION_FIT_BOUNDS|extra;
305         break;
306     }
307 }
308
309
310 /* translation table for sizepolicy specifications */
311 static StringIntMap szplcy_specs[] = {
312     {"default",         SIZEPOLICY_DEFAULT},
313     {"full",            SIZEPOLICY_FULL_EXACT},
314     {"full_bounds",     SIZEPOLICY_FULL_BOUNDS},
315     {"free",            SIZEPOLICY_FREE},
316     {"free_glue",       SIZEPOLICY_FREE_GLUE},
317     {"northwest",       SIZEPOLICY_GRAVITY_NORTHWEST},
318     {"north",           SIZEPOLICY_GRAVITY_NORTH},
319     {"northeast",       SIZEPOLICY_GRAVITY_NORTHEAST},
320     {"west",            SIZEPOLICY_GRAVITY_WEST},
321     {"center",          SIZEPOLICY_GRAVITY_CENTER},
322     {"east",            SIZEPOLICY_GRAVITY_EAST},
323     {"southwest",       SIZEPOLICY_GRAVITY_SOUTHWEST},
324     {"south",           SIZEPOLICY_GRAVITY_SOUTH},
325     {"southeast",       SIZEPOLICY_GRAVITY_SOUTHEAST},
326     {"stretch_top",     SIZEPOLICY_STRETCH_TOP},
327     {"stretch_bottom",  SIZEPOLICY_STRETCH_BOTTOM},
328     {"stretch_left",    SIZEPOLICY_STRETCH_LEFT},
329     {"stretch_right",   SIZEPOLICY_STRETCH_RIGHT},
330     {"free_glue_northwest",  SIZEPOLICY_FREE_GLUE__NORTHWEST},
331     {"free_glue_north",      SIZEPOLICY_FREE_GLUE__NORTH},
332     {"free_glue_northeast",  SIZEPOLICY_FREE_GLUE__NORTHEAST},
333     {"free_glue_west",       SIZEPOLICY_FREE_GLUE__WEST},
334     {"free_glue_center",     SIZEPOLICY_FREE_GLUE__CENTER},
335     {"free_glue_east",       SIZEPOLICY_FREE_GLUE__EAST},
336     {"free_glue_southwest",  SIZEPOLICY_FREE_GLUE__SOUTHWEST},
337     {"free_glue_south",      SIZEPOLICY_FREE_GLUE__SOUTH},
338     {"free_glue_southeast",  SIZEPOLICY_FREE_GLUE__SOUTHEAST},
339     {"visibility_constrained",   SIZEPOLICY_VISIBILITY_CONSTRAINED},
340     {"unconstrained",   SIZEPOLICY_UNCONSTRAINED},
341     { NULL,             SIZEPOLICY_DEFAULT}   /* end marker */
342 };
343
344
345 bool string2sizepolicy(const char *szplcy, WSizePolicy *value)
346 {
347     int tmp;
348     
349     tmp=stringintmap_value(szplcy_specs, szplcy, -1);
350     
351     if(tmp==-1){
352         *value=SIZEPOLICY_DEFAULT;
353         return FALSE;
354     }else{
355         *value=tmp;
356         return TRUE;
357     }
358 }
359
360
361 const char *sizepolicy2string(WSizePolicy szplcy)
362 {
363     return stringintmap_key(szplcy_specs, szplcy, NULL);
364 }