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
gtk_css_value_position_free(GtkCssValue * value)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 *
gtk_css_value_position_compute(GtkCssValue * position,guint property_id,GtkStyleProvider * provider,GtkCssStyle * style,GtkCssStyle * parent_style)40 gtk_css_value_position_compute (GtkCssValue      *position,
41                                 guint             property_id,
42                                 GtkStyleProvider *provider,
43                                 GtkCssStyle      *style,
44                                 GtkCssStyle      *parent_style)
45 {
46   GtkCssValue *x, *y;
47 
48   x = _gtk_css_value_compute (position->x, property_id, provider, style, parent_style);
49   y = _gtk_css_value_compute (position->y, property_id, provider, style, parent_style);
50   if (x == position->x && y == position->y)
51     {
52       _gtk_css_value_unref (x);
53       _gtk_css_value_unref (y);
54       return _gtk_css_value_ref (position);
55     }
56 
57   return _gtk_css_position_value_new (x, y);
58 }
59 
60 static gboolean
gtk_css_value_position_equal(const GtkCssValue * position1,const GtkCssValue * position2)61 gtk_css_value_position_equal (const GtkCssValue *position1,
62                               const GtkCssValue *position2)
63 {
64   return _gtk_css_value_equal (position1->x, position2->x)
65       && _gtk_css_value_equal (position1->y, position2->y);
66 }
67 
68 static GtkCssValue *
gtk_css_value_position_transition(GtkCssValue * start,GtkCssValue * end,guint property_id,double progress)69 gtk_css_value_position_transition (GtkCssValue *start,
70                                    GtkCssValue *end,
71                                    guint        property_id,
72                                    double       progress)
73 {
74   GtkCssValue *x, *y;
75 
76   x = _gtk_css_value_transition (start->x, end->x, property_id, progress);
77   if (x == NULL)
78     return NULL;
79   y = _gtk_css_value_transition (start->y, end->y, property_id, 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
gtk_css_value_position_print(const GtkCssValue * position,GString * string)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   "GtkCssPositionValue",
154   gtk_css_value_position_free,
155   gtk_css_value_position_compute,
156   gtk_css_value_position_equal,
157   gtk_css_value_position_transition,
158   NULL,
159   NULL,
160   gtk_css_value_position_print
161 };
162 
163 GtkCssValue *
_gtk_css_position_value_new(GtkCssValue * x,GtkCssValue * y)164 _gtk_css_position_value_new (GtkCssValue *x,
165                              GtkCssValue *y)
166 {
167   GtkCssValue *result;
168 
169   result = _gtk_css_value_new (GtkCssValue, &GTK_CSS_VALUE_POSITION);
170   result->x = x;
171   result->y = y;
172   result->is_computed = gtk_css_value_is_computed (x) &&
173                         gtk_css_value_is_computed (y);
174 
175   return result;
176 }
177 
178 static GtkCssValue *
position_value_parse(GtkCssParser * parser,gboolean try)179 position_value_parse (GtkCssParser *parser, gboolean try)
180 {
181   static const struct {
182     const char *name;
183     guint       percentage;
184     gboolean    horizontal;
185     gboolean    swap;
186   } names[] = {
187     { "left",     0, TRUE,  FALSE },
188     { "right",  100, TRUE,  FALSE },
189     { "center",  50, TRUE,  TRUE  },
190     { "top",      0, FALSE, FALSE },
191     { "bottom", 100, FALSE, FALSE  },
192   };
193   GtkCssValue *x = NULL, *y = NULL;
194   gboolean swap = FALSE;
195   guint i;
196 
197   for (i = 0; i < G_N_ELEMENTS (names); i++)
198     {
199       if (gtk_css_parser_try_ident (parser, names[i].name))
200         {
201           if (names[i].horizontal)
202 	    x = _gtk_css_number_value_new (names[i].percentage, GTK_CSS_PERCENT);
203           else
204 	    y = _gtk_css_number_value_new (names[i].percentage, GTK_CSS_PERCENT);
205           swap = names[i].swap;
206           break;
207         }
208     }
209   if (i == G_N_ELEMENTS (names))
210     {
211       if (gtk_css_number_value_can_parse (parser))
212         {
213           x = _gtk_css_number_value_parse (parser,
214                                            GTK_CSS_PARSE_PERCENT
215                                            | GTK_CSS_PARSE_LENGTH);
216 
217           if (x == NULL)
218             return NULL;
219         }
220       else
221         {
222           if (!try)
223             gtk_css_parser_error_syntax (parser, "Unrecognized position value");
224           return NULL;
225         }
226     }
227 
228   for (i = 0; i < G_N_ELEMENTS (names); i++)
229     {
230       if (!swap && !names[i].swap)
231         {
232           if (names[i].horizontal && x != NULL)
233             continue;
234           if (!names[i].horizontal && y != NULL)
235             continue;
236         }
237 
238       if (gtk_css_parser_try_ident (parser, names[i].name))
239         {
240           if (x)
241             {
242               if (names[i].horizontal && !names[i].swap)
243                 {
244                   y = x;
245 	          x = _gtk_css_number_value_new (names[i].percentage, GTK_CSS_PERCENT);
246                 }
247               else
248                 {
249 	          y = _gtk_css_number_value_new (names[i].percentage, GTK_CSS_PERCENT);
250                 }
251             }
252           else
253             {
254               g_assert (names[i].horizontal || names[i].swap);
255 	      x = _gtk_css_number_value_new (names[i].percentage, GTK_CSS_PERCENT);
256             }
257           break;
258         }
259     }
260 
261   if (i == G_N_ELEMENTS (names))
262     {
263       if (gtk_css_number_value_can_parse (parser))
264         {
265           if (y != NULL)
266             {
267               if (!try)
268                 gtk_css_parser_error_syntax (parser, "Invalid combination of values");
269               _gtk_css_value_unref (y);
270               return NULL;
271             }
272           y = _gtk_css_number_value_parse (parser,
273                                            GTK_CSS_PARSE_PERCENT
274                                            | GTK_CSS_PARSE_LENGTH);
275           if (y == NULL)
276             {
277               _gtk_css_value_unref (x);
278 	      return NULL;
279             }
280         }
281       else
282         {
283           if (y)
284             x = _gtk_css_number_value_new (50, GTK_CSS_PERCENT);
285           else
286             y = _gtk_css_number_value_new (50, GTK_CSS_PERCENT);
287         }
288     }
289 
290   return _gtk_css_position_value_new (x, y);
291 }
292 
293 GtkCssValue *
_gtk_css_position_value_parse(GtkCssParser * parser)294 _gtk_css_position_value_parse (GtkCssParser *parser)
295 {
296   return position_value_parse (parser, FALSE);
297 }
298 
299 GtkCssValue *
_gtk_css_position_value_try_parse(GtkCssParser * parser)300 _gtk_css_position_value_try_parse (GtkCssParser *parser)
301 {
302   return position_value_parse (parser, TRUE);
303 }
304 
305 GtkCssValue *
gtk_css_position_value_parse_spacing(GtkCssParser * parser)306 gtk_css_position_value_parse_spacing (GtkCssParser *parser)
307 {
308   GtkCssValue *x, *y;
309 
310   x = _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_LENGTH | GTK_CSS_POSITIVE_ONLY);
311   if (x == NULL)
312     return NULL;
313 
314   if (gtk_css_number_value_can_parse (parser))
315     {
316       y = _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_LENGTH | GTK_CSS_POSITIVE_ONLY);
317       if (y == NULL)
318         {
319           _gtk_css_value_unref (x);
320           return NULL;
321         }
322     }
323   else
324     {
325       y = _gtk_css_value_ref (x);
326     }
327 
328   return _gtk_css_position_value_new (x, y);
329 }
330 
331 double
_gtk_css_position_value_get_x(const GtkCssValue * position,double one_hundred_percent)332 _gtk_css_position_value_get_x (const GtkCssValue *position,
333                                double             one_hundred_percent)
334 {
335   g_return_val_if_fail (position != NULL, 0.0);
336   g_return_val_if_fail (position->class == &GTK_CSS_VALUE_POSITION, 0.0);
337 
338   return _gtk_css_number_value_get (position->x, one_hundred_percent);
339 }
340 
341 double
_gtk_css_position_value_get_y(const GtkCssValue * position,double one_hundred_percent)342 _gtk_css_position_value_get_y (const GtkCssValue *position,
343                                double             one_hundred_percent)
344 {
345   g_return_val_if_fail (position != NULL, 0.0);
346   g_return_val_if_fail (position->class == &GTK_CSS_VALUE_POSITION, 0.0);
347 
348   return _gtk_css_number_value_get (position->y, one_hundred_percent);
349 }
350 
351