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