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 "gtkcssstylefuncsprivate.h"
30 #include "gtkcsstypesprivate.h"
32 /* this is in case round() is not provided by the compiler,
33 * such as in the case of C89 compilers, like MSVC
35 #include "fallback-c89.c"
40 parse_border (GtkCssShorthandProperty *shorthand,
45 GValue temp = G_VALUE_INIT;
48 g_value_init (&temp, GTK_TYPE_BORDER);
49 if (!_gtk_css_style_parse_value (&temp, parser, base))
51 g_value_unset (&temp);
55 border = g_value_get_boxed (&temp);
57 g_value_init (&values[0], G_TYPE_INT);
58 g_value_init (&values[1], G_TYPE_INT);
59 g_value_init (&values[2], G_TYPE_INT);
60 g_value_init (&values[3], G_TYPE_INT);
61 g_value_set_int (&values[0], border->top);
62 g_value_set_int (&values[1], border->right);
63 g_value_set_int (&values[2], border->bottom);
64 g_value_set_int (&values[3], border->left);
66 g_value_unset (&temp);
74 border_image_value_parse (GtkCssParser *parser,
78 GValue temp = G_VALUE_INIT;
79 cairo_pattern_t *pattern = NULL;
80 gconstpointer *boxed = NULL;
82 GtkBorder slice, *width = NULL, *parsed_slice;
83 GtkCssBorderImageRepeat repeat, *parsed_repeat;
84 gboolean retval = FALSE;
85 GtkBorderImage *image = NULL;
87 if (_gtk_css_parser_try (parser, "none", TRUE))
90 g_value_init (&temp, CAIRO_GOBJECT_TYPE_PATTERN);
92 if (!_gtk_css_style_parse_value (&temp, parser, base))
95 boxed_type = G_VALUE_TYPE (&temp);
96 if (boxed_type != CAIRO_GOBJECT_TYPE_PATTERN)
97 boxed = g_value_dup_boxed (&temp);
99 pattern = g_value_dup_boxed (&temp);
101 g_value_unset (&temp);
102 g_value_init (&temp, GTK_TYPE_BORDER);
104 if (!_gtk_css_style_parse_value (&temp, parser, base))
107 parsed_slice = g_value_get_boxed (&temp);
108 slice = *parsed_slice;
110 if (_gtk_css_parser_try (parser, "/", TRUE))
112 g_value_unset (&temp);
113 g_value_init (&temp, GTK_TYPE_BORDER);
115 if (!_gtk_css_style_parse_value (&temp, parser, base))
118 width = g_value_dup_boxed (&temp);
121 g_value_unset (&temp);
122 g_value_init (&temp, GTK_TYPE_CSS_BORDER_IMAGE_REPEAT);
124 if (!_gtk_css_style_parse_value (&temp, parser, base))
127 parsed_repeat = g_value_get_boxed (&temp);
128 repeat = *parsed_repeat;
130 g_value_unset (&temp);
133 image = _gtk_border_image_new_for_boxed (boxed_type, boxed, &slice, width, &repeat);
134 else if (pattern != NULL)
135 image = _gtk_border_image_new (pattern, &slice, width, &repeat);
140 g_value_take_boxed (value, image);
145 cairo_pattern_destroy (pattern);
148 g_boxed_free (boxed_type, boxed);
151 gtk_border_free (width);
157 border_radius_value_parse (GtkCssParser *parser,
161 GtkCssBorderRadius border;
163 if (!_gtk_css_parser_try_double (parser, &border.top_left.horizontal))
165 _gtk_css_parser_error (parser, "Expected a number");
168 else if (border.top_left.horizontal < 0)
171 if (_gtk_css_parser_try_double (parser, &border.top_right.horizontal))
173 if (border.top_right.horizontal < 0)
175 if (_gtk_css_parser_try_double (parser, &border.bottom_right.horizontal))
177 if (border.bottom_right.horizontal < 0)
179 if (!_gtk_css_parser_try_double (parser, &border.bottom_left.horizontal))
180 border.bottom_left.horizontal = border.top_right.horizontal;
181 else if (border.bottom_left.horizontal < 0)
186 border.bottom_right.horizontal = border.top_left.horizontal;
187 border.bottom_left.horizontal = border.top_right.horizontal;
192 border.top_right.horizontal = border.top_left.horizontal;
193 border.bottom_right.horizontal = border.top_left.horizontal;
194 border.bottom_left.horizontal = border.top_left.horizontal;
197 if (_gtk_css_parser_try (parser, "/", TRUE))
199 if (!_gtk_css_parser_try_double (parser, &border.top_left.vertical))
201 _gtk_css_parser_error (parser, "Expected a number");
204 else if (border.top_left.vertical < 0)
207 if (_gtk_css_parser_try_double (parser, &border.top_right.vertical))
209 if (border.top_right.vertical < 0)
211 if (_gtk_css_parser_try_double (parser, &border.bottom_right.vertical))
213 if (border.bottom_right.vertical < 0)
215 if (!_gtk_css_parser_try_double (parser, &border.bottom_left.vertical))
216 border.bottom_left.vertical = border.top_right.vertical;
217 else if (border.bottom_left.vertical < 0)
222 border.bottom_right.vertical = border.top_left.vertical;
223 border.bottom_left.vertical = border.top_right.vertical;
228 border.top_right.vertical = border.top_left.vertical;
229 border.bottom_right.vertical = border.top_left.vertical;
230 border.bottom_left.vertical = border.top_left.vertical;
235 border.top_left.vertical = border.top_left.horizontal;
236 border.top_right.vertical = border.top_right.horizontal;
237 border.bottom_right.vertical = border.bottom_right.horizontal;
238 border.bottom_left.vertical = border.bottom_left.horizontal;
241 /* border-radius is an int property for backwards-compat reasons */
242 g_value_unset (value);
243 g_value_init (value, GTK_TYPE_CSS_BORDER_RADIUS);
244 g_value_set_boxed (value, &border);
249 _gtk_css_parser_error (parser, "Border radius values cannot be negative");
254 border_color_shorthand_value_parse (GtkCssParser *parser,
258 GtkSymbolicColor *symbolic;
261 array = g_ptr_array_new_with_free_func ((GDestroyNotify) gtk_symbolic_color_unref);
265 if (_gtk_css_parser_try (parser, "transparent", TRUE))
267 GdkRGBA transparent = { 0, 0, 0, 0 };
269 symbolic = gtk_symbolic_color_new_literal (&transparent);
273 symbolic = _gtk_css_parser_read_symbolic_color (parser);
275 if (symbolic == NULL)
279 g_ptr_array_add (array, symbolic);
281 while (array->len < 4 &&
282 !_gtk_css_parser_is_eof (parser) &&
283 !_gtk_css_parser_begins_with (parser, ';') &&
284 !_gtk_css_parser_begins_with (parser, '}'));
289 g_assert_not_reached ();
292 g_ptr_array_add (array, gtk_symbolic_color_ref (g_ptr_array_index (array, 0)));
295 g_ptr_array_add (array, gtk_symbolic_color_ref (g_ptr_array_index (array, 0)));
298 g_ptr_array_add (array, gtk_symbolic_color_ref (g_ptr_array_index (array, 1)));
304 g_value_unset (value);
305 g_value_init (value, G_TYPE_PTR_ARRAY);
306 g_value_take_boxed (value, array);
314 unpack_border (const GValue *value,
321 GParameter *parameter = g_new0 (GParameter, 4);
322 GtkBorder *border = g_value_get_boxed (value);
324 parameter[0].name = top;
325 g_value_init (¶meter[0].value, G_TYPE_INT);
326 g_value_set_int (¶meter[0].value, border->top);
327 parameter[1].name = left;
328 g_value_init (¶meter[1].value, G_TYPE_INT);
329 g_value_set_int (¶meter[1].value, border->left);
330 parameter[2].name = bottom;
331 g_value_init (¶meter[2].value, G_TYPE_INT);
332 g_value_set_int (¶meter[2].value, border->bottom);
333 parameter[3].name = right;
334 g_value_init (¶meter[3].value, G_TYPE_INT);
335 g_value_set_int (¶meter[3].value, border->right);
342 pack_border (GValue *value,
343 GtkStyleProperties *props,
353 gtk_style_properties_get (props,
366 g_value_set_boxed (value, &border);
370 unpack_border_width (const GValue *value,
373 return unpack_border (value, n_params,
374 "border-top-width", "border-left-width",
375 "border-bottom-width", "border-right-width");
379 pack_border_width (GValue *value,
380 GtkStyleProperties *props,
382 GtkStylePropertyContext *context)
384 pack_border (value, props, state,
385 "border-top-width", "border-left-width",
386 "border-bottom-width", "border-right-width");
390 unpack_padding (const GValue *value,
393 return unpack_border (value, n_params,
394 "padding-top", "padding-left",
395 "padding-bottom", "padding-right");
399 pack_padding (GValue *value,
400 GtkStyleProperties *props,
402 GtkStylePropertyContext *context)
404 pack_border (value, props, state,
405 "padding-top", "padding-left",
406 "padding-bottom", "padding-right");
410 unpack_margin (const GValue *value,
413 return unpack_border (value, n_params,
414 "margin-top", "margin-left",
415 "margin-bottom", "margin-right");
419 pack_margin (GValue *value,
420 GtkStyleProperties *props,
422 GtkStylePropertyContext *context)
424 pack_border (value, props, state,
425 "margin-top", "margin-left",
426 "margin-bottom", "margin-right");
430 unpack_border_radius (const GValue *value,
433 GParameter *parameter = g_new0 (GParameter, 4);
434 GtkCssBorderRadius *border;
436 if (G_VALUE_HOLDS_BOXED (value))
437 border = g_value_get_boxed (value);
441 parameter[0].name = "border-top-left-radius";
442 g_value_init (¶meter[0].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
443 parameter[1].name = "border-top-right-radius";
444 g_value_init (¶meter[1].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
445 parameter[2].name = "border-bottom-right-radius";
446 g_value_init (¶meter[2].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
447 parameter[3].name = "border-bottom-left-radius";
448 g_value_init (¶meter[3].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
451 g_value_set_boxed (¶meter[0].value, &border->top_left);
452 g_value_set_boxed (¶meter[1].value, &border->top_right);
453 g_value_set_boxed (¶meter[2].value, &border->bottom_right);
454 g_value_set_boxed (¶meter[3].value, &border->bottom_left);
462 pack_border_radius (GValue *value,
463 GtkStyleProperties *props,
465 GtkStylePropertyContext *context)
467 GtkCssBorderCornerRadius *top_left;
469 /* NB: We are an int property, so we have to resolve to an int here.
470 * So we just resolve to an int. We pick one and stick to it.
471 * Lesson learned: Don't query border-radius shorthand, query the
472 * real properties instead. */
473 gtk_style_properties_get (props,
475 "border-top-left-radius", &top_left,
479 g_value_set_int (value, top_left->horizontal);
485 unpack_font_description (const GValue *value,
488 GParameter *parameter = g_new0 (GParameter, 5);
489 PangoFontDescription *description;
493 /* For backwards compat, we only unpack values that are indeed set.
494 * For strict CSS conformance we need to unpack all of them.
495 * Note that we do set all of them in the parse function, so it
496 * will not have effects when parsing CSS files. It will though
497 * for custom style providers.
500 description = g_value_get_boxed (value);
504 mask = pango_font_description_get_set_fields (description);
508 if (mask & PANGO_FONT_MASK_FAMILY)
510 GPtrArray *strv = g_ptr_array_new ();
512 g_ptr_array_add (strv, g_strdup (pango_font_description_get_family (description)));
513 g_ptr_array_add (strv, NULL);
514 parameter[n].name = "font-family";
515 g_value_init (¶meter[n].value, G_TYPE_STRV);
516 g_value_take_boxed (¶meter[n].value,
517 g_ptr_array_free (strv, FALSE));
521 if (mask & PANGO_FONT_MASK_STYLE)
523 parameter[n].name = "font-style";
524 g_value_init (¶meter[n].value, PANGO_TYPE_STYLE);
525 g_value_set_enum (¶meter[n].value,
526 pango_font_description_get_style (description));
530 if (mask & PANGO_FONT_MASK_VARIANT)
532 parameter[n].name = "font-variant";
533 g_value_init (¶meter[n].value, PANGO_TYPE_VARIANT);
534 g_value_set_enum (¶meter[n].value,
535 pango_font_description_get_variant (description));
539 if (mask & PANGO_FONT_MASK_WEIGHT)
541 parameter[n].name = "font-weight";
542 g_value_init (¶meter[n].value, PANGO_TYPE_WEIGHT);
543 g_value_set_enum (¶meter[n].value,
544 pango_font_description_get_weight (description));
548 if (mask & PANGO_FONT_MASK_SIZE)
550 parameter[n].name = "font-size";
551 g_value_init (¶meter[n].value, G_TYPE_DOUBLE);
552 g_value_set_double (¶meter[n].value,
553 (double) pango_font_description_get_size (description) / PANGO_SCALE);
563 pack_font_description (GValue *value,
564 GtkStyleProperties *props,
566 GtkStylePropertyContext *context)
568 PangoFontDescription *description;
571 PangoVariant variant;
575 gtk_style_properties_get (props,
577 "font-family", &families,
578 "font-style", &style,
579 "font-variant", &variant,
580 "font-weight", &weight,
584 description = pango_font_description_new ();
585 /* xxx: Can we set all the families here somehow? */
587 pango_font_description_set_family (description, families[0]);
588 pango_font_description_set_size (description, round (size * PANGO_SCALE));
589 pango_font_description_set_style (description, style);
590 pango_font_description_set_variant (description, variant);
591 pango_font_description_set_weight (description, weight);
593 g_strfreev (families);
595 g_value_take_boxed (value, description);
599 unpack_border_color (const GValue *value,
602 GParameter *parameter = g_new0 (GParameter, 4);
605 type = G_VALUE_TYPE (value);
606 if (type == G_TYPE_PTR_ARRAY)
607 type = GTK_TYPE_SYMBOLIC_COLOR;
609 parameter[0].name = "border-top-color";
610 g_value_init (¶meter[0].value, type);
611 parameter[1].name = "border-right-color";
612 g_value_init (¶meter[1].value, type);
613 parameter[2].name = "border-bottom-color";
614 g_value_init (¶meter[2].value, type);
615 parameter[3].name = "border-left-color";
616 g_value_init (¶meter[3].value, type);
618 if (G_VALUE_TYPE (value) == G_TYPE_PTR_ARRAY)
620 GPtrArray *array = g_value_get_boxed (value);
623 for (i = 0; i < 4; i++)
624 g_value_set_boxed (¶meter[i].value, g_ptr_array_index (array, i));
628 /* can be RGBA or symbolic color */
629 gpointer p = g_value_get_boxed (value);
631 g_value_set_boxed (¶meter[0].value, p);
632 g_value_set_boxed (¶meter[1].value, p);
633 g_value_set_boxed (¶meter[2].value, p);
634 g_value_set_boxed (¶meter[3].value, p);
642 pack_border_color (GValue *value,
643 GtkStyleProperties *props,
645 GtkStylePropertyContext *context)
647 /* NB: We are a color property, so we have to resolve to a color here.
648 * So we just resolve to a color. We pick one and stick to it.
649 * Lesson learned: Don't query border-color shorthand, query the
650 * real properties instead. */
651 g_value_unset (value);
652 gtk_style_properties_get_property (props, "border-top-color", state, value);
656 _gtk_css_shorthand_property_register (const char *name,
658 const char **subproperties,
659 GtkCssShorthandPropertyParseFunc parse_func,
660 GtkStyleUnpackFunc unpack_func,
661 GtkStylePackFunc pack_func,
662 GtkStyleParseFunc old_parse_func)
664 GtkStyleProperty *node;
666 g_return_if_fail (pack_func != NULL);
667 g_return_if_fail (unpack_func != NULL);
669 node = g_object_new (GTK_TYPE_CSS_SHORTHAND_PROPERTY,
671 "value-type", value_type,
672 "subproperties", subproperties,
676 GTK_CSS_SHORTHAND_PROPERTY (node)->parse = parse_func;
677 node->pack_func = pack_func;
678 node->unpack_func = unpack_func;
679 node->parse_func = old_parse_func;
683 _gtk_css_shorthand_property_init_properties (void)
685 /* The order is important here, be careful when changing it */
686 const char *font_subproperties[] = { "font-family", "font-style", "font-variant", "font-weight", "font-size", NULL };
687 const char *margin_subproperties[] = { "margin-top", "margin-right", "margin-bottom", "margin-left", NULL };
688 const char *padding_subproperties[] = { "padding-top", "padding-right", "padding-bottom", "padding-left", NULL };
689 const char *border_width_subproperties[] = { "border-top-width", "border-right-width", "border-bottom-width", "border-left-width", NULL };
690 const char *border_radius_subproperties[] = { "border-top-left-radius", "border-top-right-radius",
691 "border-bottom-right-radius", "border-bottom-left-radius", NULL };
692 const char *border_color_subproperties[] = { "border-top-color", "border-right-color", "border-bottom-color", "border-left-color", NULL };
693 const char *border_image_subproperties[] = { "border-image-source", "border-image-slice", "border-image-width", "border-image-repeat", NULL };
695 _gtk_css_shorthand_property_register ("font",
696 PANGO_TYPE_FONT_DESCRIPTION,
699 unpack_font_description,
700 pack_font_description,
702 _gtk_css_shorthand_property_register ("margin",
704 margin_subproperties,
709 _gtk_css_shorthand_property_register ("padding",
711 padding_subproperties,
716 _gtk_css_shorthand_property_register ("border-width",
718 border_width_subproperties,
723 _gtk_css_shorthand_property_register ("border-radius",
725 border_radius_subproperties,
727 unpack_border_radius,
729 border_radius_value_parse);
730 _gtk_css_shorthand_property_register ("border-color",
732 border_color_subproperties,
736 border_color_shorthand_value_parse);
737 _gtk_css_shorthand_property_register ("border-image",
738 GTK_TYPE_BORDER_IMAGE,
739 border_image_subproperties,
741 _gtk_border_image_unpack,
742 _gtk_border_image_pack,
743 border_image_value_parse);