2 * Copyright © 2011 Red Hat Inc.
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.
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.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 * Authors: Benjamin Otte <otte@gnome.org>
23 #include "gtkcssshorthandpropertyprivate.h"
25 #include <cairo-gobject.h>
28 #include "gtkcssimageprivate.h"
29 #include "gtkcssstylefuncsprivate.h"
30 #include "gtkcsstypesprivate.h"
31 #include "gtkprivatetypebuiltins.h"
32 #include "gtktypebuiltins.h"
34 /* this is in case round() is not provided by the compiler,
35 * such as in the case of C89 compilers, like MSVC
37 #include "fallback-c89.c"
42 value_is_done_parsing (GtkCssParser *parser)
44 return _gtk_css_parser_is_eof (parser) ||
45 _gtk_css_parser_begins_with (parser, ';') ||
46 _gtk_css_parser_begins_with (parser, '}');
50 parse_border (GtkCssShorthandProperty *shorthand,
55 GValue temp = G_VALUE_INIT;
58 g_value_init (&temp, GTK_TYPE_BORDER);
59 if (!_gtk_css_style_parse_value (&temp, parser, base))
61 g_value_unset (&temp);
65 border = g_value_get_boxed (&temp);
67 g_value_init (&values[0], G_TYPE_INT);
68 g_value_init (&values[1], G_TYPE_INT);
69 g_value_init (&values[2], G_TYPE_INT);
70 g_value_init (&values[3], G_TYPE_INT);
71 g_value_set_int (&values[0], border->top);
72 g_value_set_int (&values[1], border->right);
73 g_value_set_int (&values[2], border->bottom);
74 g_value_set_int (&values[3], border->left);
76 g_value_unset (&temp);
82 parse_border_radius (GtkCssShorthandProperty *shorthand,
87 GtkCssBorderCornerRadius borders[4];
90 for (i = 0; i < G_N_ELEMENTS (borders); i++)
92 if (!_gtk_css_parser_try_double (parser, &borders[i].horizontal))
94 if (borders[i].horizontal < 0)
96 _gtk_css_parser_error (parser, "Border radius values cannot be negative");
103 _gtk_css_parser_error (parser, "Expected a number");
107 /* The magic (i - 1) >> 1 below makes it take the correct value
108 * according to spec. Feel free to check the 4 cases */
109 for (; i < G_N_ELEMENTS (borders); i++)
110 borders[i].horizontal = borders[(i - 1) >> 1].horizontal;
112 if (_gtk_css_parser_try (parser, "/", TRUE))
114 for (i = 0; i < G_N_ELEMENTS (borders); i++)
116 if (!_gtk_css_parser_try_double (parser, &borders[i].vertical))
118 if (borders[i].vertical < 0)
120 _gtk_css_parser_error (parser, "Border radius values cannot be negative");
127 _gtk_css_parser_error (parser, "Expected a number");
131 for (; i < G_N_ELEMENTS (borders); i++)
132 borders[i].vertical = borders[(i - 1) >> 1].vertical;
137 for (i = 0; i < G_N_ELEMENTS (borders); i++)
138 borders[i].vertical = borders[i].horizontal;
141 for (i = 0; i < G_N_ELEMENTS (borders); i++)
143 g_value_init (&values[i], GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
144 g_value_set_boxed (&values[i], &borders[i]);
151 parse_border_color (GtkCssShorthandProperty *shorthand,
153 GtkCssParser *parser,
156 GtkSymbolicColor *symbolic;
159 for (i = 0; i < 4; i++)
161 symbolic = _gtk_css_parser_read_symbolic_color (parser);
162 if (symbolic == NULL)
165 g_value_init (&values[i], GTK_TYPE_SYMBOLIC_COLOR);
166 g_value_set_boxed (&values[i], symbolic);
168 if (value_is_done_parsing (parser))
172 for (i++; i < 4; i++)
174 g_value_init (&values[i], GTK_TYPE_SYMBOLIC_COLOR);
175 g_value_copy (&values[(i - 1) >> 1], &values[i]);
182 parse_border_style (GtkCssShorthandProperty *shorthand,
184 GtkCssParser *parser,
187 GtkBorderStyle styles[4];
190 for (i = 0; i < 4; i++)
192 if (!_gtk_css_parser_try_enum (parser, GTK_TYPE_BORDER_STYLE, (int *)&styles[i]))
198 _gtk_css_parser_error (parser, "Expected a border style");
202 for (; i < G_N_ELEMENTS (styles); i++)
203 styles[i] = styles[(i - 1) >> 1];
205 for (i = 0; i < G_N_ELEMENTS (styles); i++)
207 g_value_init (&values[i], GTK_TYPE_BORDER_STYLE);
208 g_value_set_enum (&values[i], styles[i]);
215 parse_border_image (GtkCssShorthandProperty *shorthand,
217 GtkCssParser *parser,
222 if (_gtk_css_parser_try (parser, "none", TRUE))
226 image = _gtk_css_image_new_parse (parser, base);
230 g_value_init (&values[0], GTK_TYPE_CSS_IMAGE);
231 g_value_set_object (&values[0], image);
233 if (value_is_done_parsing (parser))
236 g_value_init (&values[1], GTK_TYPE_BORDER);
237 if (!_gtk_css_style_parse_value (&values[1], parser, base))
240 if (_gtk_css_parser_try (parser, "/", TRUE))
242 g_value_init (&values[2], GTK_TYPE_BORDER);
243 if (!_gtk_css_style_parse_value (&values[2], parser, base))
247 if (value_is_done_parsing (parser))
250 g_value_init (&values[3], GTK_TYPE_CSS_BORDER_IMAGE_REPEAT);
251 if (!_gtk_css_style_parse_value (&values[3], parser, base))
258 parse_font (GtkCssShorthandProperty *shorthand,
260 GtkCssParser *parser,
263 PangoFontDescription *desc;
267 str = _gtk_css_parser_read_value (parser);
271 desc = pango_font_description_from_string (str);
274 mask = pango_font_description_get_set_fields (desc);
276 if (mask & PANGO_FONT_MASK_FAMILY)
278 GPtrArray *strv = g_ptr_array_new ();
280 g_ptr_array_add (strv, g_strdup (pango_font_description_get_family (desc)));
281 g_ptr_array_add (strv, NULL);
282 g_value_init (&values[0], G_TYPE_STRV);
283 g_value_take_boxed (&values[0], g_ptr_array_free (strv, FALSE));
285 if (mask & PANGO_FONT_MASK_STYLE)
287 g_value_init (&values[1], PANGO_TYPE_STYLE);
288 g_value_set_enum (&values[1], pango_font_description_get_style (desc));
290 if (mask & PANGO_FONT_MASK_VARIANT)
292 g_value_init (&values[2], PANGO_TYPE_VARIANT);
293 g_value_set_enum (&values[2], pango_font_description_get_variant (desc));
295 if (mask & PANGO_FONT_MASK_WEIGHT)
297 g_value_init (&values[3], PANGO_TYPE_WEIGHT);
298 g_value_set_enum (&values[3], pango_font_description_get_weight (desc));
300 if (mask & PANGO_FONT_MASK_SIZE)
302 g_value_init (&values[4], G_TYPE_DOUBLE);
303 g_value_set_double (&values[4],
304 (double) pango_font_description_get_size (desc) / PANGO_SCALE);
307 pango_font_description_free (desc);
313 parse_background (GtkCssShorthandProperty *shorthand,
315 GtkCssParser *parser,
323 if (!G_IS_VALUE (&values[0]) &&
324 (_gtk_css_parser_has_prefix (parser, "none") ||
325 _gtk_css_image_can_parse (parser)))
329 if (_gtk_css_parser_try (parser, "none", TRUE))
333 image = _gtk_css_image_new_parse (parser, base);
338 g_value_init (&values[0], GTK_TYPE_CSS_IMAGE);
339 g_value_take_object (&values[0], image);
341 else if (!G_IS_VALUE (&values[1]) &&
342 _gtk_css_parser_try_enum (parser, GTK_TYPE_CSS_BACKGROUND_REPEAT, &enum_value))
344 if (enum_value <= GTK_CSS_BACKGROUND_REPEAT_MASK)
348 if (_gtk_css_parser_try_enum (parser, GTK_TYPE_CSS_BACKGROUND_REPEAT, &vertical))
350 if (vertical >= GTK_CSS_BACKGROUND_REPEAT_MASK)
352 _gtk_css_parser_error (parser, "Not a valid 2nd value for border-repeat");
356 enum_value |= vertical << GTK_CSS_BACKGROUND_REPEAT_SHIFT;
359 enum_value |= enum_value << GTK_CSS_BACKGROUND_REPEAT_SHIFT;
362 g_value_init (&values[1], GTK_TYPE_CSS_BACKGROUND_REPEAT);
363 g_value_set_enum (&values[1], enum_value);
365 else if ((!G_IS_VALUE (&values[2]) || !G_IS_VALUE (&values[3])) &&
366 _gtk_css_parser_try_enum (parser, GTK_TYPE_CSS_AREA, &enum_value))
368 guint idx = !G_IS_VALUE (&values[2]) ? 2 : 3;
369 g_value_init (&values[idx], GTK_TYPE_CSS_AREA);
370 g_value_set_enum (&values[idx], enum_value);
372 else if (!G_IS_VALUE (&values[4]))
374 GtkSymbolicColor *symbolic;
376 symbolic = _gtk_css_parser_read_symbolic_color (parser);
377 if (symbolic == NULL)
380 g_value_init (&values[4], GTK_TYPE_SYMBOLIC_COLOR);
381 g_value_take_boxed (&values[4], symbolic);
385 /* We parsed everything and there's still stuff left?
386 * Pretend we didn't notice and let the normal code produce
387 * a 'junk at end of value' error */
391 while (!value_is_done_parsing (parser));
399 unpack_border (const GValue *value,
406 GParameter *parameter = g_new0 (GParameter, 4);
407 GtkBorder *border = g_value_get_boxed (value);
409 parameter[0].name = top;
410 g_value_init (¶meter[0].value, G_TYPE_INT);
411 g_value_set_int (¶meter[0].value, border->top);
412 parameter[1].name = left;
413 g_value_init (¶meter[1].value, G_TYPE_INT);
414 g_value_set_int (¶meter[1].value, border->left);
415 parameter[2].name = bottom;
416 g_value_init (¶meter[2].value, G_TYPE_INT);
417 g_value_set_int (¶meter[2].value, border->bottom);
418 parameter[3].name = right;
419 g_value_init (¶meter[3].value, G_TYPE_INT);
420 g_value_set_int (¶meter[3].value, border->right);
427 pack_border (GValue *value,
428 GtkStyleProperties *props,
438 gtk_style_properties_get (props,
451 g_value_set_boxed (value, &border);
455 unpack_border_width (const GValue *value,
458 return unpack_border (value, n_params,
459 "border-top-width", "border-left-width",
460 "border-bottom-width", "border-right-width");
464 pack_border_width (GValue *value,
465 GtkStyleProperties *props,
468 pack_border (value, props, state,
469 "border-top-width", "border-left-width",
470 "border-bottom-width", "border-right-width");
474 unpack_padding (const GValue *value,
477 return unpack_border (value, n_params,
478 "padding-top", "padding-left",
479 "padding-bottom", "padding-right");
483 pack_padding (GValue *value,
484 GtkStyleProperties *props,
487 pack_border (value, props, state,
488 "padding-top", "padding-left",
489 "padding-bottom", "padding-right");
493 unpack_margin (const GValue *value,
496 return unpack_border (value, n_params,
497 "margin-top", "margin-left",
498 "margin-bottom", "margin-right");
502 pack_margin (GValue *value,
503 GtkStyleProperties *props,
506 pack_border (value, props, state,
507 "margin-top", "margin-left",
508 "margin-bottom", "margin-right");
512 unpack_border_radius (const GValue *value,
515 GParameter *parameter = g_new0 (GParameter, 4);
516 GtkCssBorderCornerRadius border;
518 border.horizontal = border.vertical = g_value_get_int (value);
520 parameter[0].name = "border-top-left-radius";
521 g_value_init (¶meter[0].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
522 g_value_set_boxed (¶meter[0].value, &border);
523 parameter[1].name = "border-top-right-radius";
524 g_value_init (¶meter[1].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
525 g_value_set_boxed (¶meter[1].value, &border);
526 parameter[2].name = "border-bottom-right-radius";
527 g_value_init (¶meter[2].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
528 g_value_set_boxed (¶meter[2].value, &border);
529 parameter[3].name = "border-bottom-left-radius";
530 g_value_init (¶meter[3].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
531 g_value_set_boxed (¶meter[3].value, &border);
538 pack_border_radius (GValue *value,
539 GtkStyleProperties *props,
542 GtkCssBorderCornerRadius *top_left;
544 /* NB: We are an int property, so we have to resolve to an int here.
545 * So we just resolve to an int. We pick one and stick to it.
546 * Lesson learned: Don't query border-radius shorthand, query the
547 * real properties instead. */
548 gtk_style_properties_get (props,
550 "border-top-left-radius", &top_left,
554 g_value_set_int (value, top_left->horizontal);
560 unpack_font_description (const GValue *value,
563 GParameter *parameter = g_new0 (GParameter, 5);
564 PangoFontDescription *description;
568 /* For backwards compat, we only unpack values that are indeed set.
569 * For strict CSS conformance we need to unpack all of them.
570 * Note that we do set all of them in the parse function, so it
571 * will not have effects when parsing CSS files. It will though
572 * for custom style providers.
575 description = g_value_get_boxed (value);
579 mask = pango_font_description_get_set_fields (description);
583 if (mask & PANGO_FONT_MASK_FAMILY)
585 GPtrArray *strv = g_ptr_array_new ();
587 g_ptr_array_add (strv, g_strdup (pango_font_description_get_family (description)));
588 g_ptr_array_add (strv, NULL);
589 parameter[n].name = "font-family";
590 g_value_init (¶meter[n].value, G_TYPE_STRV);
591 g_value_take_boxed (¶meter[n].value,
592 g_ptr_array_free (strv, FALSE));
596 if (mask & PANGO_FONT_MASK_STYLE)
598 parameter[n].name = "font-style";
599 g_value_init (¶meter[n].value, PANGO_TYPE_STYLE);
600 g_value_set_enum (¶meter[n].value,
601 pango_font_description_get_style (description));
605 if (mask & PANGO_FONT_MASK_VARIANT)
607 parameter[n].name = "font-variant";
608 g_value_init (¶meter[n].value, PANGO_TYPE_VARIANT);
609 g_value_set_enum (¶meter[n].value,
610 pango_font_description_get_variant (description));
614 if (mask & PANGO_FONT_MASK_WEIGHT)
616 parameter[n].name = "font-weight";
617 g_value_init (¶meter[n].value, PANGO_TYPE_WEIGHT);
618 g_value_set_enum (¶meter[n].value,
619 pango_font_description_get_weight (description));
623 if (mask & PANGO_FONT_MASK_SIZE)
625 parameter[n].name = "font-size";
626 g_value_init (¶meter[n].value, G_TYPE_DOUBLE);
627 g_value_set_double (¶meter[n].value,
628 (double) pango_font_description_get_size (description) / PANGO_SCALE);
638 pack_font_description (GValue *value,
639 GtkStyleProperties *props,
642 PangoFontDescription *description;
645 PangoVariant variant;
649 gtk_style_properties_get (props,
651 "font-family", &families,
652 "font-style", &style,
653 "font-variant", &variant,
654 "font-weight", &weight,
658 description = pango_font_description_new ();
659 /* xxx: Can we set all the families here somehow? */
661 pango_font_description_set_family (description, families[0]);
662 pango_font_description_set_size (description, round (size * PANGO_SCALE));
663 pango_font_description_set_style (description, style);
664 pango_font_description_set_variant (description, variant);
665 pango_font_description_set_weight (description, weight);
667 g_strfreev (families);
669 g_value_take_boxed (value, description);
673 unpack_border_color (const GValue *value,
676 GParameter *parameter = g_new0 (GParameter, 4);
679 type = G_VALUE_TYPE (value);
680 if (type == G_TYPE_PTR_ARRAY)
681 type = GTK_TYPE_SYMBOLIC_COLOR;
683 parameter[0].name = "border-top-color";
684 g_value_init (¶meter[0].value, type);
685 parameter[1].name = "border-right-color";
686 g_value_init (¶meter[1].value, type);
687 parameter[2].name = "border-bottom-color";
688 g_value_init (¶meter[2].value, type);
689 parameter[3].name = "border-left-color";
690 g_value_init (¶meter[3].value, type);
692 if (G_VALUE_TYPE (value) == G_TYPE_PTR_ARRAY)
694 GPtrArray *array = g_value_get_boxed (value);
697 for (i = 0; i < 4; i++)
698 g_value_set_boxed (¶meter[i].value, g_ptr_array_index (array, i));
702 /* can be RGBA or symbolic color */
703 gpointer p = g_value_get_boxed (value);
705 g_value_set_boxed (¶meter[0].value, p);
706 g_value_set_boxed (¶meter[1].value, p);
707 g_value_set_boxed (¶meter[2].value, p);
708 g_value_set_boxed (¶meter[3].value, p);
716 pack_border_color (GValue *value,
717 GtkStyleProperties *props,
720 /* NB: We are a color property, so we have to resolve to a color here.
721 * So we just resolve to a color. We pick one and stick to it.
722 * Lesson learned: Don't query border-color shorthand, query the
723 * real properties instead. */
724 g_value_unset (value);
725 gtk_style_properties_get_property (props, "border-top-color", state, value);
729 unpack_border_style (const GValue *value,
732 GParameter *parameter = g_new0 (GParameter, 4);
733 GtkBorderStyle style;
735 style = g_value_get_enum (value);
737 parameter[0].name = "border-top-style";
738 g_value_init (¶meter[0].value, GTK_TYPE_BORDER_STYLE);
739 g_value_set_enum (¶meter[0].value, style);
740 parameter[1].name = "border-right-style";
741 g_value_init (¶meter[1].value, GTK_TYPE_BORDER_STYLE);
742 g_value_set_enum (¶meter[1].value, style);
743 parameter[2].name = "border-bottom-style";
744 g_value_init (¶meter[2].value, GTK_TYPE_BORDER_STYLE);
745 g_value_set_enum (¶meter[2].value, style);
746 parameter[3].name = "border-left-style";
747 g_value_init (¶meter[3].value, GTK_TYPE_BORDER_STYLE);
748 g_value_set_enum (¶meter[3].value, style);
755 pack_border_style (GValue *value,
756 GtkStyleProperties *props,
759 /* NB: We can just resolve to a style. We pick one and stick to it.
760 * Lesson learned: Don't query border-style shorthand, query the
761 * real properties instead. */
762 g_value_unset (value);
763 gtk_style_properties_get_property (props, "border-top-style", state, value);
767 _gtk_css_shorthand_property_register (const char *name,
769 const char **subproperties,
770 GtkCssShorthandPropertyParseFunc parse_func,
771 GtkStyleUnpackFunc unpack_func,
772 GtkStylePackFunc pack_func)
774 GtkStyleProperty *node;
776 node = g_object_new (GTK_TYPE_CSS_SHORTHAND_PROPERTY,
778 "value-type", value_type,
779 "subproperties", subproperties,
782 GTK_CSS_SHORTHAND_PROPERTY (node)->parse = parse_func;
783 node->pack_func = pack_func;
784 node->unpack_func = unpack_func;
788 _gtk_css_shorthand_property_init_properties (void)
790 /* The order is important here, be careful when changing it */
791 const char *font_subproperties[] = { "font-family", "font-style", "font-variant", "font-weight", "font-size", NULL };
792 const char *margin_subproperties[] = { "margin-top", "margin-right", "margin-bottom", "margin-left", NULL };
793 const char *padding_subproperties[] = { "padding-top", "padding-right", "padding-bottom", "padding-left", NULL };
794 const char *border_width_subproperties[] = { "border-top-width", "border-right-width", "border-bottom-width", "border-left-width", NULL };
795 const char *border_radius_subproperties[] = { "border-top-left-radius", "border-top-right-radius",
796 "border-bottom-right-radius", "border-bottom-left-radius", NULL };
797 const char *border_color_subproperties[] = { "border-top-color", "border-right-color", "border-bottom-color", "border-left-color", NULL };
798 const char *border_style_subproperties[] = { "border-top-style", "border-right-style", "border-bottom-style", "border-left-style", NULL };
799 const char *border_image_subproperties[] = { "border-image-source", "border-image-slice", "border-image-width", "border-image-repeat", NULL };
800 const char *background_subproperties[] = { "background-image", "background-repeat", "background-clip", "background-origin",
801 "background-color", NULL };
803 _gtk_css_shorthand_property_register ("font",
804 PANGO_TYPE_FONT_DESCRIPTION,
807 unpack_font_description,
808 pack_font_description);
809 _gtk_css_shorthand_property_register ("margin",
811 margin_subproperties,
815 _gtk_css_shorthand_property_register ("padding",
817 padding_subproperties,
821 _gtk_css_shorthand_property_register ("border-width",
823 border_width_subproperties,
827 _gtk_css_shorthand_property_register ("border-radius",
829 border_radius_subproperties,
831 unpack_border_radius,
833 _gtk_css_shorthand_property_register ("border-color",
835 border_color_subproperties,
839 _gtk_css_shorthand_property_register ("border-style",
840 GTK_TYPE_BORDER_STYLE,
841 border_style_subproperties,
845 _gtk_css_shorthand_property_register ("border-image",
847 border_image_subproperties,
851 _gtk_css_shorthand_property_register ("background",
853 background_subproperties,