1/*
2 * Copyright © 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.1 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 * Authors: Benjamin Otte <otte@gnome.org>
18 */
19
20#include "config.h"
21
22#include "gtkcssshorthandpropertyprivate.h"
23
24#include <cairo-gobject.h>
25#include <math.h>
26
27#include "gtkcssarrayvalueprivate.h"
28#include "gtkcssbgsizevalueprivate.h"
29#include "gtkcssbordervalueprivate.h"
30#include "gtkcsscolorvalueprivate.h"
31#include "gtkcsscornervalueprivate.h"
32#include "gtkcsseasevalueprivate.h"
33#include "gtkcssenumvalueprivate.h"
34#include "gtkcssimageprivate.h"
35#include "gtkcssimagevalueprivate.h"
36#include "gtkcssnumbervalueprivate.h"
37#include "gtkcsspositionvalueprivate.h"
38#include "gtkcssrepeatvalueprivate.h"
39#include "gtkcssstringvalueprivate.h"
40#include "gtkcssvalueprivate.h"
41#include "gtktypebuiltins.h"
42
43/*** PARSING ***/
44
45static gboolean
46value_is_done_parsing (GtkCssParser *parser)
47{
48 return gtk_css_parser_has_token (self: parser, token_type: GTK_CSS_TOKEN_EOF) ||
49 gtk_css_parser_has_token (self: parser, token_type: GTK_CSS_TOKEN_COMMA) ||
50 gtk_css_parser_has_token (self: parser, token_type: GTK_CSS_TOKEN_SEMICOLON) ||
51 gtk_css_parser_has_token (self: parser, token_type: GTK_CSS_TOKEN_CLOSE_CURLY);
52}
53
54static gboolean
55parse_four_numbers (GtkCssShorthandProperty *shorthand,
56 GtkCssValue **values,
57 GtkCssParser *parser,
58 GtkCssNumberParseFlags flags)
59{
60 guint i;
61
62 for (i = 0; i < 4; i++)
63 {
64 if (!gtk_css_number_value_can_parse (parser))
65 break;
66
67 values[i] = _gtk_css_number_value_parse (parser, flags);
68 if (values[i] == NULL)
69 return FALSE;
70 }
71
72 if (i == 0)
73 {
74 gtk_css_parser_error_syntax (self: parser, format: "Expected a length");
75 return FALSE;
76 }
77
78 for (; i < 4; i++)
79 {
80 values[i] = _gtk_css_value_ref (value: values[(i - 1) >> 1]);
81 }
82
83 return TRUE;
84}
85
86static gboolean
87parse_margin (GtkCssShorthandProperty *shorthand,
88 GtkCssValue **values,
89 GtkCssParser *parser)
90{
91 return parse_four_numbers (shorthand,
92 values,
93 parser,
94 flags: GTK_CSS_PARSE_LENGTH);
95}
96
97static gboolean
98parse_padding (GtkCssShorthandProperty *shorthand,
99 GtkCssValue **values,
100 GtkCssParser *parser)
101{
102 return parse_four_numbers (shorthand,
103 values,
104 parser,
105 flags: GTK_CSS_POSITIVE_ONLY
106 | GTK_CSS_PARSE_LENGTH);
107}
108
109static gboolean
110parse_border_width (GtkCssShorthandProperty *shorthand,
111 GtkCssValue **values,
112 GtkCssParser *parser)
113{
114 return parse_four_numbers (shorthand,
115 values,
116 parser,
117 flags: GTK_CSS_POSITIVE_ONLY
118 | GTK_CSS_PARSE_LENGTH);
119}
120
121static gboolean
122parse_border_radius (GtkCssShorthandProperty *shorthand,
123 GtkCssValue **values,
124 GtkCssParser *parser)
125{
126 GtkCssValue *x[4] = { NULL, }, *y[4] = { NULL, };
127 guint i;
128
129 for (i = 0; i < 4; i++)
130 {
131 if (!gtk_css_number_value_can_parse (parser))
132 break;
133 x[i] = _gtk_css_number_value_parse (parser,
134 flags: GTK_CSS_POSITIVE_ONLY
135 | GTK_CSS_PARSE_PERCENT
136 | GTK_CSS_PARSE_LENGTH);
137 if (x[i] == NULL)
138 goto fail;
139 }
140
141 if (i == 0)
142 {
143 gtk_css_parser_error_syntax (self: parser, format: "Expected a number");
144 goto fail;
145 }
146
147 /* The magic (i - 1) >> 1 below makes it take the correct value
148 * according to spec. Feel free to check the 4 cases
149 */
150 for (; i < 4; i++)
151 x[i] = _gtk_css_value_ref (value: x[(i - 1) >> 1]);
152
153 if (gtk_css_parser_try_delim (self: parser, codepoint: '/'))
154 {
155 for (i = 0; i < 4; i++)
156 {
157 if (!gtk_css_number_value_can_parse (parser))
158 break;
159 y[i] = _gtk_css_number_value_parse (parser,
160 flags: GTK_CSS_POSITIVE_ONLY
161 | GTK_CSS_PARSE_PERCENT
162 | GTK_CSS_PARSE_LENGTH);
163 if (y[i] == NULL)
164 goto fail;
165 }
166
167 if (i == 0)
168 {
169 gtk_css_parser_error_syntax (self: parser, format: "Expected a number");
170 goto fail;
171 }
172
173 for (; i < 4; i++)
174 y[i] = _gtk_css_value_ref (value: y[(i - 1) >> 1]);
175 }
176 else
177 {
178 for (i = 0; i < 4; i++)
179 y[i] = _gtk_css_value_ref (value: x[i]);
180 }
181
182 for (i = 0; i < 4; i++)
183 {
184 values[i] = _gtk_css_corner_value_new (x: x[i], y: y[i]);
185 }
186
187 return TRUE;
188
189fail:
190 for (i = 0; i < 4; i++)
191 {
192 if (x[i])
193 _gtk_css_value_unref (value: x[i]);
194 if (y[i])
195 _gtk_css_value_unref (value: y[i]);
196 }
197 return FALSE;
198}
199
200static gboolean
201parse_border_color (GtkCssShorthandProperty *shorthand,
202 GtkCssValue **values,
203 GtkCssParser *parser)
204{
205 guint i;
206
207 for (i = 0; i < 4; i++)
208 {
209 values[i] = _gtk_css_color_value_parse (parser);
210 if (values[i] == NULL)
211 return FALSE;
212
213 if (value_is_done_parsing (parser))
214 break;
215 }
216
217 for (i++; i < 4; i++)
218 {
219 values[i] = _gtk_css_value_ref (value: values[(i - 1) >> 1]);
220 }
221
222 return TRUE;
223}
224
225static gboolean
226parse_border_style (GtkCssShorthandProperty *shorthand,
227 GtkCssValue **values,
228 GtkCssParser *parser)
229{
230 guint i;
231
232 for (i = 0; i < 4; i++)
233 {
234 values[i] = _gtk_css_border_style_value_try_parse (parser);
235 if (values[i] == NULL)
236 break;
237 }
238
239 if (i == 0)
240 {
241 gtk_css_parser_error_syntax (self: parser, format: "Expected a border style");
242 return FALSE;
243 }
244
245 for (; i < 4; i++)
246 values[i] = _gtk_css_value_ref (value: values[(i - 1) >> 1]);
247
248 return TRUE;
249}
250
251static gboolean
252parse_border_image (GtkCssShorthandProperty *shorthand,
253 GtkCssValue **values,
254 GtkCssParser *parser)
255{
256 do
257 {
258 if (values[0] == NULL &&
259 (gtk_css_parser_has_ident (self: parser, ident: "none") ||
260 _gtk_css_image_can_parse (parser)))
261 {
262 GtkCssImage *image;
263
264 if (gtk_css_parser_try_ident (self: parser, ident: "none"))
265 image = NULL;
266 else
267 {
268 image = _gtk_css_image_new_parse (parser);
269 if (image == NULL)
270 return FALSE;
271 }
272
273 values[0] = _gtk_css_image_value_new (image);
274 }
275 else if (values[3] == NULL &&
276 (values[3] = _gtk_css_border_repeat_value_try_parse (parser)))
277 {
278 /* please move along */
279 }
280 else if (values[1] == NULL)
281 {
282 values[1] = _gtk_css_border_value_parse (parser,
283 flags: GTK_CSS_PARSE_PERCENT
284 | GTK_CSS_PARSE_NUMBER
285 | GTK_CSS_POSITIVE_ONLY,
286 FALSE,
287 TRUE);
288 if (values[1] == NULL)
289 return FALSE;
290
291 if (gtk_css_parser_try_delim (self: parser, codepoint: '/'))
292 {
293 values[2] = _gtk_css_border_value_parse (parser,
294 flags: GTK_CSS_PARSE_PERCENT
295 | GTK_CSS_PARSE_LENGTH
296 | GTK_CSS_PARSE_NUMBER
297 | GTK_CSS_POSITIVE_ONLY,
298 TRUE,
299 FALSE);
300 if (values[2] == NULL)
301 return FALSE;
302 }
303 }
304 else
305 {
306 /* We parsed everything and there's still stuff left?
307 * Pretend we didn't notice and let the normal code produce
308 * a 'junk at end of value' error
309 */
310 break;
311 }
312 }
313 while (!value_is_done_parsing (parser));
314
315 return TRUE;
316}
317
318static gboolean
319parse_border_side (GtkCssShorthandProperty *shorthand,
320 GtkCssValue **values,
321 GtkCssParser *parser)
322{
323 do
324 {
325 if (values[0] == NULL &&
326 gtk_css_number_value_can_parse (parser))
327 {
328 values[0] = _gtk_css_number_value_parse (parser,
329 flags: GTK_CSS_POSITIVE_ONLY
330 | GTK_CSS_PARSE_LENGTH);
331 if (values[0] == NULL)
332 return FALSE;
333 }
334 else if (values[1] == NULL &&
335 (values[1] = _gtk_css_border_style_value_try_parse (parser)))
336 {
337 /* Nothing to do */
338 }
339 else if (values[2] == NULL)
340 {
341 values[2] = _gtk_css_color_value_parse (parser);
342 if (values[2] == NULL)
343 return FALSE;
344 }
345 else
346 {
347 /* We parsed and there's still stuff left?
348 * Pretend we didn't notice and let the normal code produce
349 * a 'junk at end of value' error
350 */
351 break;
352 }
353 }
354 while (!value_is_done_parsing (parser));
355
356 return TRUE;
357}
358
359static gboolean
360parse_border (GtkCssShorthandProperty *shorthand,
361 GtkCssValue **values,
362 GtkCssParser *parser)
363{
364 do
365 {
366 if (values[0] == NULL &&
367 gtk_css_number_value_can_parse (parser))
368 {
369 values[0] = _gtk_css_number_value_parse (parser,
370 flags: GTK_CSS_POSITIVE_ONLY
371 | GTK_CSS_PARSE_LENGTH);
372 if (values[0] == NULL)
373 return FALSE;
374 values[1] = _gtk_css_value_ref (value: values[0]);
375 values[2] = _gtk_css_value_ref (value: values[0]);
376 values[3] = _gtk_css_value_ref (value: values[0]);
377 }
378 else if (values[4] == NULL &&
379 (values[4] = _gtk_css_border_style_value_try_parse (parser)))
380 {
381 values[5] = _gtk_css_value_ref (value: values[4]);
382 values[6] = _gtk_css_value_ref (value: values[4]);
383 values[7] = _gtk_css_value_ref (value: values[4]);
384 }
385 else if (values[8] == NULL)
386 {
387 values[8] = _gtk_css_color_value_parse (parser);
388 if (values[8] == NULL)
389 return FALSE;
390
391 values[9] = _gtk_css_value_ref (value: values[8]);
392 values[10] = _gtk_css_value_ref (value: values[8]);
393 values[11] = _gtk_css_value_ref (value: values[8]);
394 }
395 else
396 {
397 /* We parsed everything and there's still stuff left?
398 * Pretend we didn't notice and let the normal code produce
399 * a 'junk at end of value' error
400 */
401 break;
402 }
403 }
404 while (!value_is_done_parsing (parser));
405
406 /* Note that border-image values are not set: according to the spec
407 * they just need to be reset when using the border shorthand
408 */
409
410 return TRUE;
411}
412
413static GtkCssValue *
414_gtk_css_font_variant_value_try_parse (GtkCssParser *parser)
415{
416 if (gtk_css_parser_try_ident (self: parser, ident: "normal"))
417 return _gtk_css_ident_value_new (ident: "normal");
418 else if (gtk_css_parser_try_ident (self: parser, ident: "small-caps"))
419 return _gtk_css_ident_value_new (ident: "small-caps");
420 return NULL;
421}
422
423static gboolean
424parse_font (GtkCssShorthandProperty *shorthand,
425 GtkCssValue **values,
426 GtkCssParser *parser)
427{
428 gboolean parsed_one;
429
430 do
431 {
432 parsed_one = FALSE;
433
434 if (values[1] == NULL)
435 {
436 values[1] = _gtk_css_font_style_value_try_parse (parser);
437 parsed_one = parsed_one || values[1] != NULL;
438 }
439
440 if (values[2] == NULL)
441 {
442 values[2] = _gtk_css_font_variant_value_try_parse (parser);
443 parsed_one = parsed_one || values[2] != NULL;
444 }
445
446 if (values[3] == NULL)
447 {
448 values[3] = gtk_css_font_weight_value_try_parse (parser);
449 if (values[3] == NULL && gtk_css_number_value_can_parse (parser))
450 {
451 /* This needs to check for font-size, too */
452 GtkCssValue *num = _gtk_css_number_value_parse (parser,
453 flags: GTK_CSS_PARSE_NUMBER |
454 GTK_CSS_PARSE_LENGTH |
455 GTK_CSS_PARSE_PERCENT |
456 GTK_CSS_POSITIVE_ONLY);
457 if (num == NULL)
458 return FALSE;
459
460 if (gtk_css_number_value_get_dimension (value: num) != GTK_CSS_DIMENSION_NUMBER)
461 {
462 values[5] = num;
463 goto have_font_size;
464 }
465
466 values[3] = num;
467 if (_gtk_css_number_value_get (number: values[3], one_hundred_percent: 100) < 1 ||
468 _gtk_css_number_value_get (number: values[3], one_hundred_percent: 100) > 1000)
469 {
470 gtk_css_parser_error_value (self: parser, format: "Font weight values must be between 1 and 1000");
471 g_clear_pointer (&values[3], gtk_css_value_unref);
472 return FALSE;
473 }
474 }
475 parsed_one = parsed_one || values[3] != NULL;
476 }
477
478 if (values[4] == NULL)
479 {
480 values[4] = _gtk_css_font_stretch_value_try_parse (parser);
481 parsed_one = parsed_one || values[4] != NULL;
482 }
483 }
484 while (parsed_one && !value_is_done_parsing (parser));
485
486 values[5] = gtk_css_font_size_value_parse (parser);
487 if (values[5] == NULL)
488 return FALSE;
489
490have_font_size:
491 values[0] = gtk_css_font_family_value_parse (parser);
492 if (values[0] == NULL)
493 return FALSE;
494
495 return TRUE;
496}
497
498static gboolean
499parse_one_background (GtkCssShorthandProperty *shorthand,
500 GtkCssValue **values,
501 GtkCssParser *parser)
502{
503 GtkCssValue *value = NULL;
504
505 do
506 {
507 /* the image part */
508 if (values[0] == NULL &&
509 (gtk_css_parser_has_ident (self: parser, ident: "none") ||
510 _gtk_css_image_can_parse (parser)))
511 {
512 GtkCssImage *image;
513
514 if (gtk_css_parser_try_ident (self: parser, ident: "none"))
515 image = NULL;
516 else
517 {
518 image = _gtk_css_image_new_parse (parser);
519 if (image == NULL)
520 return FALSE;
521 }
522
523 values[0] = _gtk_css_image_value_new (image);
524 }
525 else if (values[1] == NULL &&
526 (value = _gtk_css_position_value_try_parse (parser)))
527 {
528 values[1] = value;
529 value = NULL;
530
531 if (gtk_css_parser_try_delim (self: parser, codepoint: '/') &&
532 (value = _gtk_css_bg_size_value_parse (parser)))
533 {
534 values[2] = value;
535 value = NULL;
536 }
537 }
538 else if (values[3] == NULL &&
539 (value = _gtk_css_background_repeat_value_try_parse (parser)))
540 {
541 values[3] = value;
542 value = NULL;
543 }
544 else if ((values[4] == NULL || values[5] == NULL) &&
545 (value = _gtk_css_area_value_try_parse (parser)))
546 {
547 values[4] = value;
548
549 if (values[5] == NULL)
550 {
551 values[5] = values[4];
552 values[4] = NULL;
553 }
554 value = NULL;
555 }
556 else if (values[6] == NULL)
557 {
558 value = _gtk_css_color_value_parse (parser);
559 if (value == NULL)
560 values[6] = _gtk_css_value_ref (value: _gtk_css_style_property_get_initial_value
561 (property: _gtk_css_shorthand_property_get_subproperty (shorthand, property: 6)));
562 else
563 values[6] = value;
564
565 value = NULL;
566 }
567 else
568 {
569 /* We parsed everything and there's still stuff left?
570 * Pretend we didn't notice and let the normal code produce
571 * a 'junk at end of value' error
572 */
573 break;
574 }
575 }
576 while (!value_is_done_parsing (parser));
577
578 if (values[5] != NULL && values[4] == NULL)
579 values[4] = _gtk_css_value_ref (value: values[5]);
580
581 return TRUE;
582}
583
584static gboolean
585parse_background (GtkCssShorthandProperty *shorthand,
586 GtkCssValue **values,
587 GtkCssParser *parser)
588{
589 GtkCssValue *step_values[7];
590 GPtrArray *arrays[6];
591 guint i;
592
593 for (i = 0; i < 6; i++)
594 {
595 arrays[i] = g_ptr_array_new ();
596 step_values[i] = NULL;
597 }
598
599 step_values[6] = NULL;
600
601 do {
602 if (!parse_one_background (shorthand, values: step_values, parser))
603 {
604 for (i = 0; i < 6; i++)
605 {
606 g_ptr_array_set_free_func (array: arrays[i], element_free_func: (GDestroyNotify) _gtk_css_value_unref);
607 g_ptr_array_unref (array: arrays[i]);
608 }
609 return FALSE;
610 }
611
612 for (i = 0; i < 6; i++)
613 {
614 if (step_values[i] == NULL)
615 {
616 GtkCssValue *initial = _gtk_css_style_property_get_initial_value (
617 property: _gtk_css_shorthand_property_get_subproperty (shorthand, property: i));
618 step_values[i] = _gtk_css_value_ref (value: _gtk_css_array_value_get_nth (value: initial, i: 0));
619 }
620
621 g_ptr_array_add (array: arrays[i], data: step_values[i]);
622 step_values[i] = NULL;
623 }
624 } while (gtk_css_parser_try_token (self: parser, token_type: GTK_CSS_TOKEN_COMMA));
625
626 for (i = 0; i < 6; i++)
627 {
628 values[i] = _gtk_css_array_value_new_from_array (values: (GtkCssValue **) arrays[i]->pdata, n_values: arrays[i]->len);
629 g_ptr_array_unref (array: arrays[i]);
630 }
631
632 values[6] = step_values[6];
633
634 return TRUE;
635}
636
637static gboolean
638has_transition_property (GtkCssParser *parser,
639 gpointer option_data,
640 gpointer user_data)
641{
642 return gtk_css_parser_has_token (self: parser, token_type: GTK_CSS_TOKEN_IDENT);
643}
644
645static gboolean
646parse_transition_property (GtkCssParser *parser,
647 gpointer option_data,
648 gpointer user_data)
649{
650 GtkCssValue **value = option_data;
651
652 *value = _gtk_css_ident_value_try_parse (parser);
653 g_assert (*value);
654
655 return TRUE;
656}
657
658static gboolean
659parse_transition_time (GtkCssParser *parser,
660 gpointer option_data,
661 gpointer user_data)
662{
663 GtkCssValue **value = option_data;
664
665 *value = _gtk_css_number_value_parse (parser, flags: GTK_CSS_PARSE_TIME);
666
667 return *value != NULL;
668}
669
670static gboolean
671parse_transition_timing_function (GtkCssParser *parser,
672 gpointer option_data,
673 gpointer user_data)
674{
675 GtkCssValue **value = option_data;
676
677 *value = _gtk_css_ease_value_parse (parser);
678
679 return *value != NULL;
680}
681
682static gboolean
683parse_one_transition (GtkCssShorthandProperty *shorthand,
684 GtkCssValue **values,
685 GtkCssParser *parser)
686{
687 const GtkCssParseOption options[] = {
688 { (void *) _gtk_css_ease_value_can_parse, parse_transition_timing_function, &values[3] },
689 { (void *) gtk_css_number_value_can_parse, parse_transition_time, &values[1] },
690 { (void *) gtk_css_number_value_can_parse, parse_transition_time, &values[2] },
691 { (void *) has_transition_property, parse_transition_property, &values[0] },
692 };
693
694 return gtk_css_parser_consume_any (parser, options, G_N_ELEMENTS (options), NULL);
695}
696
697static gboolean
698parse_transition (GtkCssShorthandProperty *shorthand,
699 GtkCssValue **values,
700 GtkCssParser *parser)
701{
702 GtkCssValue *step_values[4];
703 GPtrArray *arrays[4];
704 guint i;
705
706 for (i = 0; i < 4; i++)
707 {
708 arrays[i] = g_ptr_array_new ();
709 step_values[i] = NULL;
710 }
711
712 do {
713 if (!parse_one_transition (shorthand, values: step_values, parser))
714 {
715 for (i = 0; i < 4; i++)
716 {
717 g_ptr_array_set_free_func (array: arrays[i], element_free_func: (GDestroyNotify) _gtk_css_value_unref);
718 g_ptr_array_unref (array: arrays[i]);
719 }
720 return FALSE;
721 }
722
723 for (i = 0; i < 4; i++)
724 {
725 if (step_values[i] == NULL)
726 {
727 GtkCssValue *initial = _gtk_css_style_property_get_initial_value (
728 property: _gtk_css_shorthand_property_get_subproperty (shorthand, property: i));
729 step_values[i] = _gtk_css_value_ref (value: _gtk_css_array_value_get_nth (value: initial, i: 0));
730 }
731
732 g_ptr_array_add (array: arrays[i], data: step_values[i]);
733 step_values[i] = NULL;
734 }
735 } while (gtk_css_parser_try_token (self: parser, token_type: GTK_CSS_TOKEN_COMMA));
736
737 for (i = 0; i < 4; i++)
738 {
739 values[i] = _gtk_css_array_value_new_from_array (values: (GtkCssValue **) arrays[i]->pdata, n_values: arrays[i]->len);
740 g_ptr_array_unref (array: arrays[i]);
741 }
742
743 return TRUE;
744}
745
746static gboolean
747parse_one_animation (GtkCssShorthandProperty *shorthand,
748 GtkCssValue **values,
749 GtkCssParser *parser)
750{
751 do
752 {
753 if (values[1] == NULL && gtk_css_parser_try_ident (self: parser, ident: "infinite"))
754 {
755 values[1] = _gtk_css_number_value_new (HUGE_VAL, unit: GTK_CSS_NUMBER);
756 }
757 else if ((values[1] == NULL || values[3] == NULL) &&
758 gtk_css_number_value_can_parse (parser))
759 {
760 GtkCssValue *value;
761
762 value = _gtk_css_number_value_parse (parser,
763 flags: GTK_CSS_POSITIVE_ONLY
764 | (values[1] == NULL ? GTK_CSS_PARSE_NUMBER : 0)
765 | (values[3] == NULL ? GTK_CSS_PARSE_TIME : 0));
766 if (value == NULL)
767 return FALSE;
768
769 if (gtk_css_number_value_get_dimension (value) == GTK_CSS_DIMENSION_NUMBER)
770 values[1] = value;
771 else if (values[2] == NULL)
772 values[2] = value;
773 else
774 values[3] = value;
775 }
776 else if (values[4] == NULL &&
777 _gtk_css_ease_value_can_parse (parser))
778 {
779 values[4] = _gtk_css_ease_value_parse (parser);
780
781 if (values[4] == NULL)
782 return FALSE;
783 }
784 else if (values[5] == NULL &&
785 (values[5] = _gtk_css_direction_value_try_parse (parser)))
786 {
787 /* nothing to do */
788 }
789 else if (values[6] == NULL &&
790 (values[6] = _gtk_css_fill_mode_value_try_parse (parser)))
791 {
792 /* nothing to do */
793 }
794 else if (values[0] == NULL &&
795 (values[0] = _gtk_css_ident_value_try_parse (parser)))
796 {
797 /* nothing to do */
798 /* keep in mind though that this needs to come last as fill modes, directions
799 * etc are valid idents */
800 }
801 else
802 {
803 /* We parsed everything and there's still stuff left?
804 * Pretend we didn't notice and let the normal code produce
805 * a 'junk at end of value' error */
806 break;
807 }
808 }
809 while (!value_is_done_parsing (parser));
810
811 return TRUE;
812}
813
814static gboolean
815parse_animation (GtkCssShorthandProperty *shorthand,
816 GtkCssValue **values,
817 GtkCssParser *parser)
818{
819 GtkCssValue *step_values[7];
820 GPtrArray *arrays[7];
821 guint i;
822
823 for (i = 0; i < 7; i++)
824 {
825 arrays[i] = g_ptr_array_new ();
826 step_values[i] = NULL;
827 }
828
829 do {
830 if (!parse_one_animation (shorthand, values: step_values, parser))
831 {
832 for (i = 0; i < 7; i++)
833 {
834 g_ptr_array_set_free_func (array: arrays[i], element_free_func: (GDestroyNotify) _gtk_css_value_unref);
835 g_ptr_array_unref (array: arrays[i]);
836 }
837 return FALSE;
838 }
839
840 for (i = 0; i < 7; i++)
841 {
842 if (step_values[i] == NULL)
843 {
844 GtkCssValue *initial = _gtk_css_style_property_get_initial_value (
845 property: _gtk_css_shorthand_property_get_subproperty (shorthand, property: i));
846 step_values[i] = _gtk_css_value_ref (value: _gtk_css_array_value_get_nth (value: initial, i: 0));
847 }
848
849 g_ptr_array_add (array: arrays[i], data: step_values[i]);
850 step_values[i] = NULL;
851 }
852 } while (gtk_css_parser_try_token (self: parser, token_type: GTK_CSS_TOKEN_COMMA));
853
854 for (i = 0; i < 7; i++)
855 {
856 values[i] = _gtk_css_array_value_new_from_array (values: (GtkCssValue **) arrays[i]->pdata, n_values: arrays[i]->len);
857 g_ptr_array_unref (array: arrays[i]);
858 }
859
860 return TRUE;
861}
862
863static gboolean
864parse_text_decoration (GtkCssShorthandProperty *shorthand,
865 GtkCssValue **values,
866 GtkCssParser *parser)
867{
868 GtkTextDecorationLine line = 0;
869
870 do
871 {
872 GtkTextDecorationLine parsed_line;
873
874 parsed_line = _gtk_css_text_decoration_line_try_parse_one (parser, base: line);
875
876 if (parsed_line == 0 && line != 0)
877 {
878 gtk_css_parser_error_value (self: parser, format: "Invalid combination of text-decoration-line values");
879 return FALSE;
880 }
881
882 if (parsed_line != line)
883 {
884 line = parsed_line;
885 }
886 else if (values[1] == NULL &&
887 (values[1] = _gtk_css_text_decoration_style_value_try_parse (parser)))
888 {
889 if (values[1] == NULL)
890 return FALSE;
891 }
892 else if (values[2] == NULL)
893 {
894 values[2] = _gtk_css_color_value_parse (parser);
895 if (values[2] == NULL)
896 return FALSE;
897 }
898 else
899 {
900 /* We parsed and there's still stuff left?
901 * Pretend we didn't notice and let the normal code produce
902 * a 'junk at end of value' error */
903 break;
904 }
905 }
906 while (!value_is_done_parsing (parser));
907
908 if (line != 0)
909 {
910 values[0] = _gtk_css_text_decoration_line_value_new (line);
911 if (values[0] == NULL)
912 {
913 gtk_css_parser_error_value (self: parser, format: "Invalid combination of text-decoration-line values");
914 return FALSE;
915 }
916 }
917
918 return TRUE;
919}
920
921static gboolean
922parse_font_variant (GtkCssShorthandProperty *shorthand,
923 GtkCssValue **values,
924 GtkCssParser *parser)
925{
926 if (gtk_css_parser_try_ident (self: parser, ident: "normal"))
927 {
928 /* all initial values */
929 }
930 else if (gtk_css_parser_try_ident (self: parser, ident: "none"))
931 {
932 /* all initial values, except for font-variant-ligatures */
933 values[0] = _gtk_css_font_variant_ligature_value_new (ligatures: GTK_CSS_FONT_VARIANT_LIGATURE_NONE);
934 }
935 else
936 {
937 GtkCssFontVariantLigature ligatures;
938 GtkCssFontVariantNumeric numeric;
939 GtkCssFontVariantEastAsian east_asian;
940
941 ligatures = 0;
942 numeric = 0;
943 east_asian = 0;
944 do {
945 GtkCssFontVariantLigature parsed_ligature;
946 GtkCssFontVariantNumeric parsed_numeric;
947 GtkCssFontVariantEastAsian parsed_east_asian;
948
949 parsed_ligature = _gtk_css_font_variant_ligature_try_parse_one (parser, base: ligatures);
950 if (parsed_ligature == 0 && ligatures != 0)
951 {
952 gtk_css_parser_error_value (self: parser, format: "Invalid combination of ligature values");
953 return FALSE;
954 }
955 if (parsed_ligature == GTK_CSS_FONT_VARIANT_LIGATURE_NORMAL ||
956 parsed_ligature == GTK_CSS_FONT_VARIANT_LIGATURE_NONE)
957 {
958 gtk_css_parser_error_value (self: parser, format: "Unexpected ligature value");
959 return FALSE;
960 }
961 if (parsed_ligature != ligatures)
962 {
963 ligatures = parsed_ligature;
964 goto found;
965 }
966
967 parsed_numeric = _gtk_css_font_variant_numeric_try_parse_one (parser, base: numeric);
968 if (parsed_numeric == 0 && numeric != 0)
969 {
970 gtk_css_parser_error_value (self: parser, format: "Invalid combination of numeric values");
971 return FALSE;
972 }
973 if (parsed_numeric == GTK_CSS_FONT_VARIANT_NUMERIC_NORMAL)
974 {
975 gtk_css_parser_error_value (self: parser, format: "Unexpected numeric value");
976 return FALSE;
977 }
978 if (parsed_numeric != numeric)
979 {
980 numeric = parsed_numeric;
981 goto found;
982 }
983
984 parsed_east_asian = _gtk_css_font_variant_east_asian_try_parse_one (parser, base: east_asian);
985 if (parsed_east_asian == 0 && east_asian != 0)
986 {
987 gtk_css_parser_error_value (self: parser, format: "Invalid combination of east asian values");
988 return FALSE;
989 }
990 if (parsed_east_asian == GTK_CSS_FONT_VARIANT_EAST_ASIAN_NORMAL)
991 {
992 gtk_css_parser_error_value (self: parser, format: "Unexpected east asian value");
993 return FALSE;
994 }
995 if (parsed_east_asian != east_asian)
996 {
997 east_asian = parsed_east_asian;
998 goto found;
999 }
1000
1001 if (values[1] == NULL)
1002 {
1003 values[1] = _gtk_css_font_variant_position_value_try_parse (parser);
1004 if (values[1])
1005 {
1006 if (_gtk_css_font_variant_position_value_get (value: values[1]) == GTK_CSS_FONT_VARIANT_POSITION_NORMAL)
1007 {
1008 gtk_css_parser_error_value (self: parser, format: "Unexpected position value");
1009 return FALSE;
1010 }
1011 goto found;
1012 }
1013 }
1014 if (values[2] == NULL)
1015 {
1016 values[2] = _gtk_css_font_variant_caps_value_try_parse (parser);
1017 if (values[2])
1018 {
1019 if (_gtk_css_font_variant_caps_value_get (value: values[2]) == GTK_CSS_FONT_VARIANT_CAPS_NORMAL)
1020 {
1021 gtk_css_parser_error_value (self: parser, format: "Unexpected caps value");
1022 return FALSE;
1023 }
1024 goto found;
1025 }
1026 }
1027
1028 if (values[4] == NULL)
1029 {
1030 values[4] = _gtk_css_font_variant_alternate_value_try_parse (parser);
1031 if (values[4])
1032 {
1033 if (_gtk_css_font_variant_alternate_value_get (value: values[4]) == GTK_CSS_FONT_VARIANT_ALTERNATE_NORMAL)
1034 {
1035 gtk_css_parser_error_value (self: parser, format: "Unexpected alternate value");
1036 return FALSE;
1037 }
1038 goto found;
1039 }
1040 }
1041
1042 gtk_css_parser_error_value (self: parser, format: "Unknown value for property");
1043 return FALSE;
1044
1045found:
1046 if (value_is_done_parsing (parser))
1047 break;
1048
1049 } while (1);
1050
1051 if (ligatures != 0)
1052 {
1053 values[0] = _gtk_css_font_variant_ligature_value_new (ligatures);
1054 if (values[0] == NULL)
1055 {
1056 gtk_css_parser_error_value (self: parser, format: "Invalid combination of ligature values");
1057 return FALSE;
1058 }
1059 }
1060
1061 if (numeric != 0)
1062 {
1063 values[3] = _gtk_css_font_variant_numeric_value_new (numeric);
1064 if (values[3] == NULL)
1065 {
1066 gtk_css_parser_error_value (self: parser, format: "Invalid combination of numeric values");
1067 return FALSE;
1068 }
1069 }
1070
1071 if (east_asian != 0)
1072 {
1073 values[5] = _gtk_css_font_variant_east_asian_value_new (east_asian);
1074 if (values[5] == NULL)
1075 {
1076 gtk_css_parser_error_value (self: parser, format: "Invalid combination of east asian values");
1077 return FALSE;
1078 }
1079 }
1080 }
1081
1082 return TRUE;
1083}
1084
1085static gboolean
1086parse_all (GtkCssShorthandProperty *shorthand,
1087 GtkCssValue **values,
1088 GtkCssParser *parser)
1089{
1090 gtk_css_parser_error_syntax (self: parser, format: "The 'all' property can only be set to 'initial', 'inherit' or 'unset'");
1091 return FALSE;
1092}
1093
1094static void
1095gtk_css_shorthand_property_register (const char *name,
1096 const char **subproperties,
1097 GtkCssShorthandPropertyParseFunc parse_func)
1098{
1099 GtkCssShorthandProperty *node;
1100
1101 node = g_object_new (GTK_TYPE_CSS_SHORTHAND_PROPERTY,
1102 first_property_name: "name", name,
1103 "subproperties", subproperties,
1104 NULL);
1105
1106 node->parse = parse_func;
1107}
1108
1109/* NB: return value is transfer: container */
1110static const char **
1111get_all_subproperties (void)
1112{
1113 const char **properties;
1114 guint i, n;
1115
1116 n = _gtk_css_style_property_get_n_properties ();
1117 properties = g_new (const char *, n + 1);
1118 properties[n] = NULL;
1119
1120 for (i = 0; i < n; i++)
1121 {
1122 properties[i] = _gtk_style_property_get_name (GTK_STYLE_PROPERTY (_gtk_css_style_property_lookup_by_id (i)));
1123 }
1124
1125 return properties;
1126}
1127
1128void
1129_gtk_css_shorthand_property_init_properties (void)
1130{
1131 /* The order is important here, be careful when changing it */
1132 const char *font_subproperties[] = { "font-family", "font-style", "font-variant-caps", "font-weight", "font-stretch", "font-size", NULL };
1133 const char *margin_subproperties[] = { "margin-top", "margin-right", "margin-bottom", "margin-left", NULL };
1134 const char *padding_subproperties[] = { "padding-top", "padding-right", "padding-bottom", "padding-left", NULL };
1135 const char *border_width_subproperties[] = { "border-top-width", "border-right-width", "border-bottom-width", "border-left-width", NULL };
1136 const char *border_radius_subproperties[] = { "border-top-left-radius", "border-top-right-radius",
1137 "border-bottom-right-radius", "border-bottom-left-radius", NULL };
1138 const char *border_color_subproperties[] = { "border-top-color", "border-right-color", "border-bottom-color", "border-left-color", NULL };
1139 const char *border_style_subproperties[] = { "border-top-style", "border-right-style", "border-bottom-style", "border-left-style", NULL };
1140 const char *border_image_subproperties[] = { "border-image-source", "border-image-slice", "border-image-width", "border-image-repeat", NULL };
1141 const char *border_top_subproperties[] = { "border-top-width", "border-top-style", "border-top-color", NULL };
1142 const char *border_right_subproperties[] = { "border-right-width", "border-right-style", "border-right-color", NULL };
1143 const char *border_bottom_subproperties[] = { "border-bottom-width", "border-bottom-style", "border-bottom-color", NULL };
1144 const char *border_left_subproperties[] = { "border-left-width", "border-left-style", "border-left-color", NULL };
1145 const char *border_subproperties[] = { "border-top-width", "border-right-width", "border-bottom-width", "border-left-width",
1146 "border-top-style", "border-right-style", "border-bottom-style", "border-left-style",
1147 "border-top-color", "border-right-color", "border-bottom-color", "border-left-color",
1148 "border-image-source", "border-image-slice", "border-image-width", "border-image-repeat", NULL };
1149 const char *outline_subproperties[] = { "outline-width", "outline-style", "outline-color", NULL };
1150 const char *background_subproperties[] = { "background-image", "background-position", "background-size", "background-repeat", "background-clip", "background-origin",
1151 "background-color", NULL };
1152 const char *transition_subproperties[] = { "transition-property", "transition-duration", "transition-delay", "transition-timing-function", NULL };
1153 const char *animation_subproperties[] = { "animation-name", "animation-iteration-count", "animation-duration", "animation-delay",
1154 "animation-timing-function", "animation-direction", "animation-fill-mode", NULL };
1155 const char *text_decoration_subproperties[] = { "text-decoration-line", "text-decoration-style", "text-decoration-color", NULL };
1156 const char *font_variant_subproperties[] = { "font-variant-ligatures", "font-variant-position", "font-variant-caps", "font-variant-numeric", "font-variant-alternates", "font-variant-east-asian", NULL };
1157
1158 const char **all_subproperties;
1159
1160 gtk_css_shorthand_property_register (name: "font",
1161 subproperties: font_subproperties,
1162 parse_func: parse_font);
1163 gtk_css_shorthand_property_register (name: "margin",
1164 subproperties: margin_subproperties,
1165 parse_func: parse_margin);
1166 gtk_css_shorthand_property_register (name: "padding",
1167 subproperties: padding_subproperties,
1168 parse_func: parse_padding);
1169 gtk_css_shorthand_property_register (name: "border-width",
1170 subproperties: border_width_subproperties,
1171 parse_func: parse_border_width);
1172 gtk_css_shorthand_property_register (name: "border-radius",
1173 subproperties: border_radius_subproperties,
1174 parse_func: parse_border_radius);
1175 gtk_css_shorthand_property_register (name: "border-color",
1176 subproperties: border_color_subproperties,
1177 parse_func: parse_border_color);
1178 gtk_css_shorthand_property_register (name: "border-style",
1179 subproperties: border_style_subproperties,
1180 parse_func: parse_border_style);
1181 gtk_css_shorthand_property_register (name: "border-image",
1182 subproperties: border_image_subproperties,
1183 parse_func: parse_border_image);
1184 gtk_css_shorthand_property_register (name: "border-top",
1185 subproperties: border_top_subproperties,
1186 parse_func: parse_border_side);
1187 gtk_css_shorthand_property_register (name: "border-right",
1188 subproperties: border_right_subproperties,
1189 parse_func: parse_border_side);
1190 gtk_css_shorthand_property_register (name: "border-bottom",
1191 subproperties: border_bottom_subproperties,
1192 parse_func: parse_border_side);
1193 gtk_css_shorthand_property_register (name: "border-left",
1194 subproperties: border_left_subproperties,
1195 parse_func: parse_border_side);
1196 gtk_css_shorthand_property_register (name: "border",
1197 subproperties: border_subproperties,
1198 parse_func: parse_border);
1199 gtk_css_shorthand_property_register (name: "outline",
1200 subproperties: outline_subproperties,
1201 parse_func: parse_border_side);
1202 gtk_css_shorthand_property_register (name: "background",
1203 subproperties: background_subproperties,
1204 parse_func: parse_background);
1205 gtk_css_shorthand_property_register (name: "transition",
1206 subproperties: transition_subproperties,
1207 parse_func: parse_transition);
1208 gtk_css_shorthand_property_register (name: "animation",
1209 subproperties: animation_subproperties,
1210 parse_func: parse_animation);
1211 gtk_css_shorthand_property_register (name: "text-decoration",
1212 subproperties: text_decoration_subproperties,
1213 parse_func: parse_text_decoration);
1214 gtk_css_shorthand_property_register (name: "font-variant",
1215 subproperties: font_variant_subproperties,
1216 parse_func: parse_font_variant);
1217
1218 all_subproperties = get_all_subproperties ();
1219 gtk_css_shorthand_property_register (name: "all",
1220 subproperties: all_subproperties,
1221 parse_func: parse_all);
1222 g_free (mem: all_subproperties);
1223}
1224

source code of gtk/gtk/gtkcssshorthandpropertyimpl.c