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
24struct _GtkCssValue {
25 GTK_CSS_VALUE_BASE
26 GtkCssValue *x;
27 GtkCssValue *y;
28};
29
30static void
31gtk_css_value_position_free (GtkCssValue *value)
32{
33 _gtk_css_value_unref (value: value->x);
34 _gtk_css_value_unref (value: value->y);
35
36 g_slice_free (GtkCssValue, value);
37}
38
39static GtkCssValue *
40gtk_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 (value: position->x, property_id, provider, style, parent_style);
49 y = _gtk_css_value_compute (value: position->y, property_id, provider, style, parent_style);
50 if (x == position->x && y == position->y)
51 {
52 _gtk_css_value_unref (value: x);
53 _gtk_css_value_unref (value: y);
54 return _gtk_css_value_ref (value: position);
55 }
56
57 return _gtk_css_position_value_new (x, y);
58}
59
60static gboolean
61gtk_css_value_position_equal (const GtkCssValue *position1,
62 const GtkCssValue *position2)
63{
64 return _gtk_css_value_equal (value1: position1->x, value2: position2->x)
65 && _gtk_css_value_equal (value1: position1->y, value2: position2->y);
66}
67
68static GtkCssValue *
69gtk_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: start->x, end: end->x, property_id, progress);
77 if (x == NULL)
78 return NULL;
79 y = _gtk_css_value_transition (start: start->y, end: end->y, property_id, progress);
80 if (y == NULL)
81 {
82 _gtk_css_value_unref (value: x);
83 return NULL;
84 }
85
86 return _gtk_css_position_value_new (x, y);
87}
88
89static void
90gtk_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 (value: 0, unit: GTK_CSS_PERCENT) },
99 { "right", "bottom", _gtk_css_number_value_new (value: 100, unit: GTK_CSS_PERCENT) }
100 };
101 GtkCssValue *center = _gtk_css_number_value_new (value: 50, unit: GTK_CSS_PERCENT);
102 guint i;
103
104 if (_gtk_css_value_equal (value1: position->x, value2: center))
105 {
106 if (_gtk_css_value_equal (value1: position->y, value2: center))
107 {
108 g_string_append (string, val: "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 (value1: position->x, value2: values[i].number))
117 {
118 g_string_append (string, val: values[i].x_name);
119 break;
120 }
121 }
122 if (i == G_N_ELEMENTS (values))
123 _gtk_css_value_print (value: position->x, string);
124
125 if (_gtk_css_value_equal (value1: position->y, value2: 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 (value1: position->y, value2: values[i].number))
134 {
135 g_string_append (string, val: values[i].y_name);
136 goto done;
137 }
138 }
139 if (i == G_N_ELEMENTS (values))
140 {
141 if (_gtk_css_value_equal (value1: position->x, value2: center))
142 g_string_append (string, val: "center ");
143 _gtk_css_value_print (value: position->y, string);
144 }
145
146done:
147 for (i = 0; i < G_N_ELEMENTS (values); i++)
148 _gtk_css_value_unref (value: values[i].number);
149 _gtk_css_value_unref (value: center);
150}
151
152static 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
163GtkCssValue *
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 (value: x) &&
173 gtk_css_value_is_computed (value: y);
174
175 return result;
176}
177
178static GtkCssValue *
179position_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 (self: parser, ident: names[i].name))
200 {
201 if (names[i].horizontal)
202 x = _gtk_css_number_value_new (value: names[i].percentage, unit: GTK_CSS_PERCENT);
203 else
204 y = _gtk_css_number_value_new (value: names[i].percentage, unit: 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 flags: 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 (self: parser, format: "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 (self: parser, ident: 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 (value: names[i].percentage, unit: GTK_CSS_PERCENT);
246 }
247 else
248 {
249 y = _gtk_css_number_value_new (value: names[i].percentage, unit: GTK_CSS_PERCENT);
250 }
251 }
252 else
253 {
254 g_assert (names[i].horizontal || names[i].swap);
255 x = _gtk_css_number_value_new (value: names[i].percentage, unit: 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 (self: parser, format: "Invalid combination of values");
269 _gtk_css_value_unref (value: y);
270 return NULL;
271 }
272 y = _gtk_css_number_value_parse (parser,
273 flags: GTK_CSS_PARSE_PERCENT
274 | GTK_CSS_PARSE_LENGTH);
275 if (y == NULL)
276 {
277 _gtk_css_value_unref (value: x);
278 return NULL;
279 }
280 }
281 else
282 {
283 if (y)
284 x = _gtk_css_number_value_new (value: 50, unit: GTK_CSS_PERCENT);
285 else
286 y = _gtk_css_number_value_new (value: 50, unit: GTK_CSS_PERCENT);
287 }
288 }
289
290 return _gtk_css_position_value_new (x, y);
291}
292
293GtkCssValue *
294_gtk_css_position_value_parse (GtkCssParser *parser)
295{
296 return position_value_parse (parser, FALSE);
297}
298
299GtkCssValue *
300_gtk_css_position_value_try_parse (GtkCssParser *parser)
301{
302 return position_value_parse (parser, TRUE);
303}
304
305GtkCssValue *
306gtk_css_position_value_parse_spacing (GtkCssParser *parser)
307{
308 GtkCssValue *x, *y;
309
310 x = _gtk_css_number_value_parse (parser, flags: 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, flags: GTK_CSS_PARSE_LENGTH | GTK_CSS_POSITIVE_ONLY);
317 if (y == NULL)
318 {
319 _gtk_css_value_unref (value: x);
320 return NULL;
321 }
322 }
323 else
324 {
325 y = _gtk_css_value_ref (value: x);
326 }
327
328 return _gtk_css_position_value_new (x, y);
329}
330
331double
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 (number: position->x, one_hundred_percent);
339}
340
341double
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 (number: position->y, one_hundred_percent);
349}
350
351

source code of gtk/gtk/gtkcsspositionvalue.c