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 "gtkborderimageprivate.h"
29 #include "gtkcsstypesprivate.h"
31 /* this is in case round() is not provided by the compiler,
32 * such as in the case of C89 compilers, like MSVC
34 #include "fallback-c89.c"
39 border_image_value_parse (GtkCssParser *parser,
43 GValue temp = G_VALUE_INIT;
44 cairo_pattern_t *pattern = NULL;
45 gconstpointer *boxed = NULL;
47 GtkBorder slice, *width = NULL, *parsed_slice;
48 GtkCssBorderImageRepeat repeat, *parsed_repeat;
49 gboolean retval = FALSE;
50 GtkBorderImage *image = NULL;
52 if (_gtk_css_parser_try (parser, "none", TRUE))
55 g_value_init (&temp, CAIRO_GOBJECT_TYPE_PATTERN);
57 if (!_gtk_style_property_parse_value (NULL, &temp, parser, base))
60 boxed_type = G_VALUE_TYPE (&temp);
61 if (boxed_type != CAIRO_GOBJECT_TYPE_PATTERN)
62 boxed = g_value_dup_boxed (&temp);
64 pattern = g_value_dup_boxed (&temp);
66 g_value_unset (&temp);
67 g_value_init (&temp, GTK_TYPE_BORDER);
69 if (!_gtk_style_property_parse_value (NULL, &temp, parser, base))
72 parsed_slice = g_value_get_boxed (&temp);
73 slice = *parsed_slice;
75 if (_gtk_css_parser_try (parser, "/", TRUE))
77 g_value_unset (&temp);
78 g_value_init (&temp, GTK_TYPE_BORDER);
80 if (!_gtk_style_property_parse_value (NULL, &temp, parser, base))
83 width = g_value_dup_boxed (&temp);
86 g_value_unset (&temp);
87 g_value_init (&temp, GTK_TYPE_CSS_BORDER_IMAGE_REPEAT);
89 if (!_gtk_style_property_parse_value (NULL, &temp, parser, base))
92 parsed_repeat = g_value_get_boxed (&temp);
93 repeat = *parsed_repeat;
95 g_value_unset (&temp);
98 image = _gtk_border_image_new_for_boxed (boxed_type, boxed, &slice, width, &repeat);
99 else if (pattern != NULL)
100 image = _gtk_border_image_new (pattern, &slice, width, &repeat);
105 g_value_take_boxed (value, image);
110 cairo_pattern_destroy (pattern);
113 g_boxed_free (boxed_type, boxed);
116 gtk_border_free (width);
122 border_radius_value_parse (GtkCssParser *parser,
126 GtkCssBorderRadius border;
128 if (!_gtk_css_parser_try_double (parser, &border.top_left.horizontal))
130 _gtk_css_parser_error (parser, "Expected a number");
133 else if (border.top_left.horizontal < 0)
136 if (_gtk_css_parser_try_double (parser, &border.top_right.horizontal))
138 if (border.top_right.horizontal < 0)
140 if (_gtk_css_parser_try_double (parser, &border.bottom_right.horizontal))
142 if (border.bottom_right.horizontal < 0)
144 if (!_gtk_css_parser_try_double (parser, &border.bottom_left.horizontal))
145 border.bottom_left.horizontal = border.top_right.horizontal;
146 else if (border.bottom_left.horizontal < 0)
151 border.bottom_right.horizontal = border.top_left.horizontal;
152 border.bottom_left.horizontal = border.top_right.horizontal;
157 border.top_right.horizontal = border.top_left.horizontal;
158 border.bottom_right.horizontal = border.top_left.horizontal;
159 border.bottom_left.horizontal = border.top_left.horizontal;
162 if (_gtk_css_parser_try (parser, "/", TRUE))
164 if (!_gtk_css_parser_try_double (parser, &border.top_left.vertical))
166 _gtk_css_parser_error (parser, "Expected a number");
169 else if (border.top_left.vertical < 0)
172 if (_gtk_css_parser_try_double (parser, &border.top_right.vertical))
174 if (border.top_right.vertical < 0)
176 if (_gtk_css_parser_try_double (parser, &border.bottom_right.vertical))
178 if (border.bottom_right.vertical < 0)
180 if (!_gtk_css_parser_try_double (parser, &border.bottom_left.vertical))
181 border.bottom_left.vertical = border.top_right.vertical;
182 else if (border.bottom_left.vertical < 0)
187 border.bottom_right.vertical = border.top_left.vertical;
188 border.bottom_left.vertical = border.top_right.vertical;
193 border.top_right.vertical = border.top_left.vertical;
194 border.bottom_right.vertical = border.top_left.vertical;
195 border.bottom_left.vertical = border.top_left.vertical;
200 border.top_left.vertical = border.top_left.horizontal;
201 border.top_right.vertical = border.top_right.horizontal;
202 border.bottom_right.vertical = border.bottom_right.horizontal;
203 border.bottom_left.vertical = border.bottom_left.horizontal;
206 /* border-radius is an int property for backwards-compat reasons */
207 g_value_unset (value);
208 g_value_init (value, GTK_TYPE_CSS_BORDER_RADIUS);
209 g_value_set_boxed (value, &border);
214 _gtk_css_parser_error (parser, "Border radius values cannot be negative");
219 border_color_shorthand_value_parse (GtkCssParser *parser,
223 GtkSymbolicColor *symbolic;
226 array = g_ptr_array_new_with_free_func ((GDestroyNotify) gtk_symbolic_color_unref);
230 if (_gtk_css_parser_try (parser, "transparent", TRUE))
232 GdkRGBA transparent = { 0, 0, 0, 0 };
234 symbolic = gtk_symbolic_color_new_literal (&transparent);
238 symbolic = _gtk_css_parser_read_symbolic_color (parser);
240 if (symbolic == NULL)
244 g_ptr_array_add (array, symbolic);
246 while (array->len < 4 &&
247 !_gtk_css_parser_is_eof (parser) &&
248 !_gtk_css_parser_begins_with (parser, ';') &&
249 !_gtk_css_parser_begins_with (parser, '}'));
254 g_assert_not_reached ();
257 g_ptr_array_add (array, gtk_symbolic_color_ref (g_ptr_array_index (array, 0)));
260 g_ptr_array_add (array, gtk_symbolic_color_ref (g_ptr_array_index (array, 0)));
263 g_ptr_array_add (array, gtk_symbolic_color_ref (g_ptr_array_index (array, 1)));
269 g_value_unset (value);
270 g_value_init (value, G_TYPE_PTR_ARRAY);
271 g_value_take_boxed (value, array);
279 string_append_double (GString *string,
282 char buf[G_ASCII_DTOSTR_BUF_SIZE];
284 g_ascii_dtostr (buf, sizeof (buf), d);
285 g_string_append (string, buf);
289 border_radius_value_print (const GValue *value,
292 GtkCssBorderRadius *border;
294 border = g_value_get_boxed (value);
298 g_string_append (string, "none");
302 string_append_double (string, border->top_left.horizontal);
303 if (border->top_left.horizontal != border->top_right.horizontal ||
304 border->top_left.horizontal != border->bottom_right.horizontal ||
305 border->top_left.horizontal != border->bottom_left.horizontal)
307 g_string_append_c (string, ' ');
308 string_append_double (string, border->top_right.horizontal);
309 if (border->top_left.horizontal != border->bottom_right.horizontal ||
310 border->top_right.horizontal != border->bottom_left.horizontal)
312 g_string_append_c (string, ' ');
313 string_append_double (string, border->bottom_right.horizontal);
314 if (border->top_right.horizontal != border->bottom_left.horizontal)
316 g_string_append_c (string, ' ');
317 string_append_double (string, border->bottom_left.horizontal);
322 if (border->top_left.horizontal != border->top_left.vertical ||
323 border->top_right.horizontal != border->top_right.vertical ||
324 border->bottom_right.horizontal != border->bottom_right.vertical ||
325 border->bottom_left.horizontal != border->bottom_left.vertical)
327 g_string_append (string, " / ");
328 string_append_double (string, border->top_left.vertical);
329 if (border->top_left.vertical != border->top_right.vertical ||
330 border->top_left.vertical != border->bottom_right.vertical ||
331 border->top_left.vertical != border->bottom_left.vertical)
333 g_string_append_c (string, ' ');
334 string_append_double (string, border->top_right.vertical);
335 if (border->top_left.vertical != border->bottom_right.vertical ||
336 border->top_right.vertical != border->bottom_left.vertical)
338 g_string_append_c (string, ' ');
339 string_append_double (string, border->bottom_right.vertical);
340 if (border->top_right.vertical != border->bottom_left.vertical)
342 g_string_append_c (string, ' ');
343 string_append_double (string, border->bottom_left.vertical);
354 unpack_border (const GValue *value,
361 GParameter *parameter = g_new0 (GParameter, 4);
362 GtkBorder *border = g_value_get_boxed (value);
364 parameter[0].name = top;
365 g_value_init (¶meter[0].value, G_TYPE_INT);
366 g_value_set_int (¶meter[0].value, border->top);
367 parameter[1].name = left;
368 g_value_init (¶meter[1].value, G_TYPE_INT);
369 g_value_set_int (¶meter[1].value, border->left);
370 parameter[2].name = bottom;
371 g_value_init (¶meter[2].value, G_TYPE_INT);
372 g_value_set_int (¶meter[2].value, border->bottom);
373 parameter[3].name = right;
374 g_value_init (¶meter[3].value, G_TYPE_INT);
375 g_value_set_int (¶meter[3].value, border->right);
382 pack_border (GValue *value,
383 GtkStyleProperties *props,
393 gtk_style_properties_get (props,
406 g_value_set_boxed (value, &border);
410 unpack_border_width (const GValue *value,
413 return unpack_border (value, n_params,
414 "border-top-width", "border-left-width",
415 "border-bottom-width", "border-right-width");
419 pack_border_width (GValue *value,
420 GtkStyleProperties *props,
422 GtkStylePropertyContext *context)
424 pack_border (value, props, state,
425 "border-top-width", "border-left-width",
426 "border-bottom-width", "border-right-width");
430 unpack_padding (const GValue *value,
433 return unpack_border (value, n_params,
434 "padding-top", "padding-left",
435 "padding-bottom", "padding-right");
439 pack_padding (GValue *value,
440 GtkStyleProperties *props,
442 GtkStylePropertyContext *context)
444 pack_border (value, props, state,
445 "padding-top", "padding-left",
446 "padding-bottom", "padding-right");
450 unpack_margin (const GValue *value,
453 return unpack_border (value, n_params,
454 "margin-top", "margin-left",
455 "margin-bottom", "margin-right");
459 pack_margin (GValue *value,
460 GtkStyleProperties *props,
462 GtkStylePropertyContext *context)
464 pack_border (value, props, state,
465 "margin-top", "margin-left",
466 "margin-bottom", "margin-right");
470 unpack_border_radius (const GValue *value,
473 GParameter *parameter = g_new0 (GParameter, 4);
474 GtkCssBorderRadius *border;
476 if (G_VALUE_HOLDS_BOXED (value))
477 border = g_value_get_boxed (value);
481 parameter[0].name = "border-top-left-radius";
482 g_value_init (¶meter[0].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
483 parameter[1].name = "border-top-right-radius";
484 g_value_init (¶meter[1].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
485 parameter[2].name = "border-bottom-right-radius";
486 g_value_init (¶meter[2].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
487 parameter[3].name = "border-bottom-left-radius";
488 g_value_init (¶meter[3].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
491 g_value_set_boxed (¶meter[0].value, &border->top_left);
492 g_value_set_boxed (¶meter[1].value, &border->top_right);
493 g_value_set_boxed (¶meter[2].value, &border->bottom_right);
494 g_value_set_boxed (¶meter[3].value, &border->bottom_left);
502 pack_border_radius (GValue *value,
503 GtkStyleProperties *props,
505 GtkStylePropertyContext *context)
507 GtkCssBorderCornerRadius *top_left;
509 /* NB: We are an int property, so we have to resolve to an int here.
510 * So we just resolve to an int. We pick one and stick to it.
511 * Lesson learned: Don't query border-radius shorthand, query the
512 * real properties instead. */
513 gtk_style_properties_get (props,
515 "border-top-left-radius", &top_left,
519 g_value_set_int (value, top_left->horizontal);
525 unpack_font_description (const GValue *value,
528 GParameter *parameter = g_new0 (GParameter, 5);
529 PangoFontDescription *description;
533 /* For backwards compat, we only unpack values that are indeed set.
534 * For strict CSS conformance we need to unpack all of them.
535 * Note that we do set all of them in the parse function, so it
536 * will not have effects when parsing CSS files. It will though
537 * for custom style providers.
540 description = g_value_get_boxed (value);
544 mask = pango_font_description_get_set_fields (description);
548 if (mask & PANGO_FONT_MASK_FAMILY)
550 GPtrArray *strv = g_ptr_array_new ();
552 g_ptr_array_add (strv, g_strdup (pango_font_description_get_family (description)));
553 g_ptr_array_add (strv, NULL);
554 parameter[n].name = "font-family";
555 g_value_init (¶meter[n].value, G_TYPE_STRV);
556 g_value_take_boxed (¶meter[n].value,
557 g_ptr_array_free (strv, FALSE));
561 if (mask & PANGO_FONT_MASK_STYLE)
563 parameter[n].name = "font-style";
564 g_value_init (¶meter[n].value, PANGO_TYPE_STYLE);
565 g_value_set_enum (¶meter[n].value,
566 pango_font_description_get_style (description));
570 if (mask & PANGO_FONT_MASK_VARIANT)
572 parameter[n].name = "font-variant";
573 g_value_init (¶meter[n].value, PANGO_TYPE_VARIANT);
574 g_value_set_enum (¶meter[n].value,
575 pango_font_description_get_variant (description));
579 if (mask & PANGO_FONT_MASK_WEIGHT)
581 parameter[n].name = "font-weight";
582 g_value_init (¶meter[n].value, PANGO_TYPE_WEIGHT);
583 g_value_set_enum (¶meter[n].value,
584 pango_font_description_get_weight (description));
588 if (mask & PANGO_FONT_MASK_SIZE)
590 parameter[n].name = "font-size";
591 g_value_init (¶meter[n].value, G_TYPE_DOUBLE);
592 g_value_set_double (¶meter[n].value,
593 (double) pango_font_description_get_size (description) / PANGO_SCALE);
603 pack_font_description (GValue *value,
604 GtkStyleProperties *props,
606 GtkStylePropertyContext *context)
608 PangoFontDescription *description;
611 PangoVariant variant;
615 gtk_style_properties_get (props,
617 "font-family", &families,
618 "font-style", &style,
619 "font-variant", &variant,
620 "font-weight", &weight,
624 description = pango_font_description_new ();
625 /* xxx: Can we set all the families here somehow? */
627 pango_font_description_set_family (description, families[0]);
628 pango_font_description_set_size (description, round (size * PANGO_SCALE));
629 pango_font_description_set_style (description, style);
630 pango_font_description_set_variant (description, variant);
631 pango_font_description_set_weight (description, weight);
633 g_strfreev (families);
635 g_value_take_boxed (value, description);
639 unpack_border_color (const GValue *value,
642 GParameter *parameter = g_new0 (GParameter, 4);
645 type = G_VALUE_TYPE (value);
646 if (type == G_TYPE_PTR_ARRAY)
647 type = GTK_TYPE_SYMBOLIC_COLOR;
649 parameter[0].name = "border-top-color";
650 g_value_init (¶meter[0].value, type);
651 parameter[1].name = "border-right-color";
652 g_value_init (¶meter[1].value, type);
653 parameter[2].name = "border-bottom-color";
654 g_value_init (¶meter[2].value, type);
655 parameter[3].name = "border-left-color";
656 g_value_init (¶meter[3].value, type);
658 if (G_VALUE_TYPE (value) == G_TYPE_PTR_ARRAY)
660 GPtrArray *array = g_value_get_boxed (value);
663 for (i = 0; i < 4; i++)
664 g_value_set_boxed (¶meter[i].value, g_ptr_array_index (array, i));
668 /* can be RGBA or symbolic color */
669 gpointer p = g_value_get_boxed (value);
671 g_value_set_boxed (¶meter[0].value, p);
672 g_value_set_boxed (¶meter[1].value, p);
673 g_value_set_boxed (¶meter[2].value, p);
674 g_value_set_boxed (¶meter[3].value, p);
682 pack_border_color (GValue *value,
683 GtkStyleProperties *props,
685 GtkStylePropertyContext *context)
687 /* NB: We are a color property, so we have to resolve to a color here.
688 * So we just resolve to a color. We pick one and stick to it.
689 * Lesson learned: Don't query border-color shorthand, query the
690 * real properties instead. */
691 g_value_unset (value);
692 gtk_style_properties_get_property (props, "border-top-color", state, value);
695 /*** UNSET FUNCS ***/
698 unset_font_description (GtkStyleProperties *props,
701 gtk_style_properties_unset_property (props, "font-family", state);
702 gtk_style_properties_unset_property (props, "font-style", state);
703 gtk_style_properties_unset_property (props, "font-variant", state);
704 gtk_style_properties_unset_property (props, "font-weight", state);
705 gtk_style_properties_unset_property (props, "font-size", state);
709 unset_margin (GtkStyleProperties *props,
712 gtk_style_properties_unset_property (props, "margin-top", state);
713 gtk_style_properties_unset_property (props, "margin-right", state);
714 gtk_style_properties_unset_property (props, "margin-bottom", state);
715 gtk_style_properties_unset_property (props, "margin-left", state);
719 unset_padding (GtkStyleProperties *props,
722 gtk_style_properties_unset_property (props, "padding-top", state);
723 gtk_style_properties_unset_property (props, "padding-right", state);
724 gtk_style_properties_unset_property (props, "padding-bottom", state);
725 gtk_style_properties_unset_property (props, "padding-left", state);
729 unset_border_width (GtkStyleProperties *props,
732 gtk_style_properties_unset_property (props, "border-top-width", state);
733 gtk_style_properties_unset_property (props, "border-right-width", state);
734 gtk_style_properties_unset_property (props, "border-bottom-width", state);
735 gtk_style_properties_unset_property (props, "border-left-width", state);
739 unset_border_radius (GtkStyleProperties *props,
742 gtk_style_properties_unset_property (props, "border-top-right-radius", state);
743 gtk_style_properties_unset_property (props, "border-bottom-right-radius", state);
744 gtk_style_properties_unset_property (props, "border-bottom-left-radius", state);
745 gtk_style_properties_unset_property (props, "border-top-left-radius", state);
749 unset_border_color (GtkStyleProperties *props,
752 gtk_style_properties_unset_property (props, "border-top-color", state);
753 gtk_style_properties_unset_property (props, "border-right-color", state);
754 gtk_style_properties_unset_property (props, "border-bottom-color", state);
755 gtk_style_properties_unset_property (props, "border-left-color", state);
759 unset_border_image (GtkStyleProperties *props,
762 gtk_style_properties_unset_property (props, "border-image-source", state);
763 gtk_style_properties_unset_property (props, "border-image-slice", state);
764 gtk_style_properties_unset_property (props, "border-image-repeat", state);
765 gtk_style_properties_unset_property (props, "border-image-width", state);
769 _gtk_css_shorthand_property_register (GParamSpec *pspec,
770 GtkStylePropertyFlags flags,
771 GtkStylePropertyParser property_parse_func,
772 GtkStyleUnpackFunc unpack_func,
773 GtkStylePackFunc pack_func,
774 GtkStyleParseFunc parse_func,
775 GtkStylePrintFunc print_func,
776 const GValue * initial_value,
777 GtkStyleUnsetFunc unset_func)
779 GtkStyleProperty *node;
781 g_return_if_fail (pack_func != NULL);
782 g_return_if_fail (unpack_func != NULL);
784 node = g_object_new (GTK_TYPE_CSS_SHORTHAND_PROPERTY,
790 node->property_parse_func = property_parse_func;
791 node->pack_func = pack_func;
792 node->unpack_func = unpack_func;
793 node->parse_func = parse_func;
794 node->print_func = print_func;
795 node->unset_func = unset_func;
799 _gtk_css_shorthand_property_init_properties (void)
801 _gtk_css_shorthand_property_register (g_param_spec_boxed ("font",
804 PANGO_TYPE_FONT_DESCRIPTION, 0),
805 GTK_STYLE_PROPERTY_INHERIT,
807 unpack_font_description,
808 pack_font_description,
812 unset_font_description);
813 _gtk_css_shorthand_property_register (g_param_spec_boxed ("margin",
825 _gtk_css_shorthand_property_register (g_param_spec_boxed ("padding",
837 _gtk_css_shorthand_property_register (g_param_spec_boxed ("border-width",
839 "Border width, in pixels",
849 _gtk_css_shorthand_property_register (g_param_spec_int ("border-radius",
851 "Border radius, in pixels",
855 unpack_border_radius,
857 border_radius_value_parse,
858 border_radius_value_print,
860 unset_border_radius);
861 _gtk_css_shorthand_property_register (g_param_spec_boxed ("border-color",
869 border_color_shorthand_value_parse,
873 _gtk_css_shorthand_property_register (g_param_spec_boxed ("border-image",
876 GTK_TYPE_BORDER_IMAGE, 0),
879 _gtk_border_image_unpack,
880 _gtk_border_image_pack,
881 border_image_value_parse,