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: value->x); |
34 | _gtk_css_value_unref (value: 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 | 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 | |
60 | static gboolean |
61 | gtk_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 | |
68 | static GtkCssValue * |
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: 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 | |
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 (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 | |
146 | done: |
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 | |
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 * |
164 | _gtk_css_position_value_new (GtkCssValue *x, |
165 | GtkCssValue *y) |
166 | { |
167 | GtkCssValue *result; |
168 | |
169 | result = _gtk_css_value_new (GtkCssValue, >K_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 | |
178 | static GtkCssValue * |
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 (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 | |
293 | GtkCssValue * |
294 | _gtk_css_position_value_parse (GtkCssParser *parser) |
295 | { |
296 | return position_value_parse (parser, FALSE); |
297 | } |
298 | |
299 | GtkCssValue * |
300 | _gtk_css_position_value_try_parse (GtkCssParser *parser) |
301 | { |
302 | return position_value_parse (parser, TRUE); |
303 | } |
304 | |
305 | GtkCssValue * |
306 | gtk_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 | |
331 | double |
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 == >K_CSS_VALUE_POSITION, 0.0); |
337 | |
338 | return _gtk_css_number_value_get (number: position->x, one_hundred_percent); |
339 | } |
340 | |
341 | double |
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 == >K_CSS_VALUE_POSITION, 0.0); |
347 | |
348 | return _gtk_css_number_value_get (number: position->y, one_hundred_percent); |
349 | } |
350 | |
351 | |