]> Pileus Git - ~andy/gtk/blob - gtk/gtkcsspositionvalue.c
cssvalue: First step of proper dependency tracking
[~andy/gtk] / gtk / gtkcsspositionvalue.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2011 Red Hat, Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include "config.h"
19
20 #include "gtkcsspositionvalueprivate.h"
21
22 #include "gtkcssnumbervalueprivate.h"
23
24 struct _GtkCssValue {
25   GTK_CSS_VALUE_BASE
26   GtkCssValue *x;
27   GtkCssValue *y;
28 };
29
30 static void
31 gtk_css_value_position_free (GtkCssValue *value)
32 {
33   _gtk_css_value_unref (value->x);
34   _gtk_css_value_unref (value->y);
35
36   g_slice_free (GtkCssValue, value);
37 }
38
39 static GtkCssValue *
40 gtk_css_value_position_compute (GtkCssValue        *position,
41                                 guint               property_id,
42                                 GtkStyleContext    *context,
43                                 GtkCssDependencies *dependencies)
44 {
45   GtkCssValue *x, *y;
46   GtkCssDependencies x_deps, y_deps;
47
48   x = _gtk_css_value_compute (position->x, property_id, context, &x_deps);
49   y = _gtk_css_value_compute (position->y, property_id, context, &y_deps);
50   *dependencies = _gtk_css_dependencies_union (x_deps, y_deps);
51   if (x == position->x && y == position->y)
52     {
53       _gtk_css_value_unref (x);
54       _gtk_css_value_unref (y);
55       return _gtk_css_value_ref (position);
56     }
57
58   return _gtk_css_position_value_new (x, y);
59 }
60
61 static gboolean
62 gtk_css_value_position_equal (const GtkCssValue *position1,
63                               const GtkCssValue *position2)
64 {
65   return _gtk_css_value_equal (position1->x, position2->x)
66       && _gtk_css_value_equal (position1->y, position2->y);
67 }
68
69 static GtkCssValue *
70 gtk_css_value_position_transition (GtkCssValue *start,
71                                    GtkCssValue *end,
72                                    double       progress)
73 {
74   GtkCssValue *x, *y;
75
76   x = _gtk_css_value_transition (start->x, end->x, progress);
77   if (x == NULL)
78     return NULL;
79   y = _gtk_css_value_transition (start->y, end->y, progress);
80   if (y == NULL)
81     {
82       _gtk_css_value_unref (x);
83       return NULL;
84     }
85
86   return _gtk_css_position_value_new (x, y);
87 }
88
89 static void
90 gtk_css_value_position_print (const GtkCssValue *position,
91                               GString           *string)
92 {
93   struct {
94     const char *x_name;
95     const char *y_name;
96     GtkCssValue *number;
97   } values[] = { 
98     { "left",   "top",    _gtk_css_number_value_new (0, GTK_CSS_PERCENT) },
99     { "right",  "bottom", _gtk_css_number_value_new (100, GTK_CSS_PERCENT) }
100   };
101   GtkCssValue *center = _gtk_css_number_value_new (50, GTK_CSS_PERCENT);
102   guint i;
103
104   if (_gtk_css_value_equal (position->x, center))
105     {
106       if (_gtk_css_value_equal (position->y, center))
107         {
108           g_string_append (string, "center");
109           goto done;
110         }
111     }
112   else
113     {
114       for (i = 0; i < G_N_ELEMENTS (values); i++)
115         {
116           if (_gtk_css_value_equal (position->x, values[i].number))
117             {
118               g_string_append (string, values[i].x_name);
119               break;
120             }
121         }
122       if (i == G_N_ELEMENTS (values))
123         _gtk_css_value_print (position->x, string);
124
125       if (_gtk_css_value_equal (position->y, center))
126         goto done;
127
128       g_string_append_c (string, ' ');
129     }
130
131   for (i = 0; i < G_N_ELEMENTS (values); i++)
132     {
133       if (_gtk_css_value_equal (position->y, values[i].number))
134         {
135           g_string_append (string, values[i].y_name);
136           goto done;
137         }
138     }
139   if (i == G_N_ELEMENTS (values))
140     {
141       if (_gtk_css_value_equal (position->x, center))
142         g_string_append (string, "center ");
143       _gtk_css_value_print (position->y, string);
144     }
145
146 done:
147   for (i = 0; i < G_N_ELEMENTS (values); i++)
148     _gtk_css_value_unref (values[i].number);
149   _gtk_css_value_unref (center);
150 }
151
152 static const GtkCssValueClass GTK_CSS_VALUE_POSITION = {
153   gtk_css_value_position_free,
154   gtk_css_value_position_compute,
155   gtk_css_value_position_equal,
156   gtk_css_value_position_transition,
157   gtk_css_value_position_print
158 };
159
160 GtkCssValue *
161 _gtk_css_position_value_new (GtkCssValue *x,
162                              GtkCssValue *y)
163 {
164   GtkCssValue *result;
165
166   result = _gtk_css_value_new (GtkCssValue, &GTK_CSS_VALUE_POSITION);
167   result->x = x;
168   result->y = y;
169
170   return result;
171 }
172
173 GtkCssValue *
174 _gtk_css_position_value_parse (GtkCssParser *parser)
175 {
176   static const struct {
177     const char *name;
178     guint       percentage;
179     gboolean    horizontal;
180     gboolean    vertical;
181   } names[] = {
182     { "left",     0, TRUE,  FALSE },
183     { "right",  100, TRUE,  FALSE },
184     { "center",  50, TRUE,  TRUE  },
185     { "top",      0, FALSE, TRUE  },
186     { "bottom", 100, FALSE, TRUE  },
187     { NULL    ,   0, TRUE,  FALSE }, /* used for numbers */
188     { NULL    ,  50, TRUE,  TRUE  }  /* used for no value */
189   };
190   GtkCssValue *x, *y;
191   GtkCssValue **missing;
192   guint first, second;
193
194   for (first = 0; names[first].name != NULL; first++)
195     {
196       if (_gtk_css_parser_try (parser, names[first].name, TRUE))
197         {
198           if (names[first].horizontal)
199             {
200               x = _gtk_css_number_value_new (names[first].percentage, GTK_CSS_PERCENT);
201               missing = &y;
202             }
203           else
204             {
205               y = _gtk_css_number_value_new (names[first].percentage, GTK_CSS_PERCENT);
206               missing = &x;
207             }
208           break;
209         }
210     }
211   if (names[first].name == NULL)
212     {
213       if (_gtk_css_parser_has_number (parser))
214         {
215           missing = &y;
216           x = _gtk_css_number_value_parse (parser,
217                                            GTK_CSS_PARSE_PERCENT
218                                            | GTK_CSS_PARSE_LENGTH);
219
220           if (x == NULL)
221             return NULL;
222         }
223       else
224         return NULL;
225     }
226
227   for (second = 0; names[second].name != NULL; second++)
228     {
229       if (_gtk_css_parser_try (parser, names[second].name, TRUE))
230         {
231           *missing = _gtk_css_number_value_new (names[second].percentage, GTK_CSS_PERCENT);
232           break;
233         }
234     }
235
236   if (names[second].name == NULL)
237     {
238       if (_gtk_css_parser_has_number (parser))
239         {
240           if (missing != &y)
241             {
242               _gtk_css_parser_error (parser, "Invalid combination of values");
243               _gtk_css_value_unref (y);
244               return NULL;
245             }
246           y = _gtk_css_number_value_parse (parser,
247                                            GTK_CSS_PARSE_PERCENT
248                                            | GTK_CSS_PARSE_LENGTH);
249           if (y == NULL)
250             {
251               _gtk_css_value_unref (x);
252               return NULL;
253             }
254         }
255       else
256         {
257           second++;
258           *missing = _gtk_css_number_value_new (50, GTK_CSS_PERCENT);
259         }
260     }
261   else
262     {
263       if ((names[first].horizontal && !names[second].vertical) ||
264           (!names[first].horizontal && !names[second].horizontal))
265         {
266           _gtk_css_parser_error (parser, "Invalid combination of values");
267           _gtk_css_value_unref (x);
268           _gtk_css_value_unref (y);
269           return NULL;
270         }
271     }
272
273   return _gtk_css_position_value_new (x, y);
274 }
275
276 double
277 _gtk_css_position_value_get_x (const GtkCssValue *position,
278                                double             one_hundred_percent)
279 {
280   g_return_val_if_fail (position != NULL, 0.0);
281   g_return_val_if_fail (position->class == &GTK_CSS_VALUE_POSITION, 0.0);
282
283   return _gtk_css_number_value_get (position->x, one_hundred_percent);
284 }
285
286 double
287 _gtk_css_position_value_get_y (const GtkCssValue *position,
288                                double             one_hundred_percent)
289 {
290   g_return_val_if_fail (position != NULL, 0.0);
291   g_return_val_if_fail (position->class == &GTK_CSS_VALUE_POSITION, 0.0);
292
293   return _gtk_css_number_value_get (position->y, one_hundred_percent);
294 }
295