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 unpack_border (const GValue *value,
286 GParameter *parameter = g_new0 (GParameter, 4);
287 GtkBorder *border = g_value_get_boxed (value);
289 parameter[0].name = top;
290 g_value_init (¶meter[0].value, G_TYPE_INT);
291 g_value_set_int (¶meter[0].value, border->top);
292 parameter[1].name = left;
293 g_value_init (¶meter[1].value, G_TYPE_INT);
294 g_value_set_int (¶meter[1].value, border->left);
295 parameter[2].name = bottom;
296 g_value_init (¶meter[2].value, G_TYPE_INT);
297 g_value_set_int (¶meter[2].value, border->bottom);
298 parameter[3].name = right;
299 g_value_init (¶meter[3].value, G_TYPE_INT);
300 g_value_set_int (¶meter[3].value, border->right);
307 pack_border (GValue *value,
308 GtkStyleProperties *props,
318 gtk_style_properties_get (props,
331 g_value_set_boxed (value, &border);
335 unpack_border_width (const GValue *value,
338 return unpack_border (value, n_params,
339 "border-top-width", "border-left-width",
340 "border-bottom-width", "border-right-width");
344 pack_border_width (GValue *value,
345 GtkStyleProperties *props,
347 GtkStylePropertyContext *context)
349 pack_border (value, props, state,
350 "border-top-width", "border-left-width",
351 "border-bottom-width", "border-right-width");
355 unpack_padding (const GValue *value,
358 return unpack_border (value, n_params,
359 "padding-top", "padding-left",
360 "padding-bottom", "padding-right");
364 pack_padding (GValue *value,
365 GtkStyleProperties *props,
367 GtkStylePropertyContext *context)
369 pack_border (value, props, state,
370 "padding-top", "padding-left",
371 "padding-bottom", "padding-right");
375 unpack_margin (const GValue *value,
378 return unpack_border (value, n_params,
379 "margin-top", "margin-left",
380 "margin-bottom", "margin-right");
384 pack_margin (GValue *value,
385 GtkStyleProperties *props,
387 GtkStylePropertyContext *context)
389 pack_border (value, props, state,
390 "margin-top", "margin-left",
391 "margin-bottom", "margin-right");
395 unpack_border_radius (const GValue *value,
398 GParameter *parameter = g_new0 (GParameter, 4);
399 GtkCssBorderRadius *border;
401 if (G_VALUE_HOLDS_BOXED (value))
402 border = g_value_get_boxed (value);
406 parameter[0].name = "border-top-left-radius";
407 g_value_init (¶meter[0].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
408 parameter[1].name = "border-top-right-radius";
409 g_value_init (¶meter[1].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
410 parameter[2].name = "border-bottom-right-radius";
411 g_value_init (¶meter[2].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
412 parameter[3].name = "border-bottom-left-radius";
413 g_value_init (¶meter[3].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
416 g_value_set_boxed (¶meter[0].value, &border->top_left);
417 g_value_set_boxed (¶meter[1].value, &border->top_right);
418 g_value_set_boxed (¶meter[2].value, &border->bottom_right);
419 g_value_set_boxed (¶meter[3].value, &border->bottom_left);
427 pack_border_radius (GValue *value,
428 GtkStyleProperties *props,
430 GtkStylePropertyContext *context)
432 GtkCssBorderCornerRadius *top_left;
434 /* NB: We are an int property, so we have to resolve to an int here.
435 * So we just resolve to an int. We pick one and stick to it.
436 * Lesson learned: Don't query border-radius shorthand, query the
437 * real properties instead. */
438 gtk_style_properties_get (props,
440 "border-top-left-radius", &top_left,
444 g_value_set_int (value, top_left->horizontal);
450 unpack_font_description (const GValue *value,
453 GParameter *parameter = g_new0 (GParameter, 5);
454 PangoFontDescription *description;
458 /* For backwards compat, we only unpack values that are indeed set.
459 * For strict CSS conformance we need to unpack all of them.
460 * Note that we do set all of them in the parse function, so it
461 * will not have effects when parsing CSS files. It will though
462 * for custom style providers.
465 description = g_value_get_boxed (value);
469 mask = pango_font_description_get_set_fields (description);
473 if (mask & PANGO_FONT_MASK_FAMILY)
475 GPtrArray *strv = g_ptr_array_new ();
477 g_ptr_array_add (strv, g_strdup (pango_font_description_get_family (description)));
478 g_ptr_array_add (strv, NULL);
479 parameter[n].name = "font-family";
480 g_value_init (¶meter[n].value, G_TYPE_STRV);
481 g_value_take_boxed (¶meter[n].value,
482 g_ptr_array_free (strv, FALSE));
486 if (mask & PANGO_FONT_MASK_STYLE)
488 parameter[n].name = "font-style";
489 g_value_init (¶meter[n].value, PANGO_TYPE_STYLE);
490 g_value_set_enum (¶meter[n].value,
491 pango_font_description_get_style (description));
495 if (mask & PANGO_FONT_MASK_VARIANT)
497 parameter[n].name = "font-variant";
498 g_value_init (¶meter[n].value, PANGO_TYPE_VARIANT);
499 g_value_set_enum (¶meter[n].value,
500 pango_font_description_get_variant (description));
504 if (mask & PANGO_FONT_MASK_WEIGHT)
506 parameter[n].name = "font-weight";
507 g_value_init (¶meter[n].value, PANGO_TYPE_WEIGHT);
508 g_value_set_enum (¶meter[n].value,
509 pango_font_description_get_weight (description));
513 if (mask & PANGO_FONT_MASK_SIZE)
515 parameter[n].name = "font-size";
516 g_value_init (¶meter[n].value, G_TYPE_DOUBLE);
517 g_value_set_double (¶meter[n].value,
518 (double) pango_font_description_get_size (description) / PANGO_SCALE);
528 pack_font_description (GValue *value,
529 GtkStyleProperties *props,
531 GtkStylePropertyContext *context)
533 PangoFontDescription *description;
536 PangoVariant variant;
540 gtk_style_properties_get (props,
542 "font-family", &families,
543 "font-style", &style,
544 "font-variant", &variant,
545 "font-weight", &weight,
549 description = pango_font_description_new ();
550 /* xxx: Can we set all the families here somehow? */
552 pango_font_description_set_family (description, families[0]);
553 pango_font_description_set_size (description, round (size * PANGO_SCALE));
554 pango_font_description_set_style (description, style);
555 pango_font_description_set_variant (description, variant);
556 pango_font_description_set_weight (description, weight);
558 g_strfreev (families);
560 g_value_take_boxed (value, description);
564 unpack_border_color (const GValue *value,
567 GParameter *parameter = g_new0 (GParameter, 4);
570 type = G_VALUE_TYPE (value);
571 if (type == G_TYPE_PTR_ARRAY)
572 type = GTK_TYPE_SYMBOLIC_COLOR;
574 parameter[0].name = "border-top-color";
575 g_value_init (¶meter[0].value, type);
576 parameter[1].name = "border-right-color";
577 g_value_init (¶meter[1].value, type);
578 parameter[2].name = "border-bottom-color";
579 g_value_init (¶meter[2].value, type);
580 parameter[3].name = "border-left-color";
581 g_value_init (¶meter[3].value, type);
583 if (G_VALUE_TYPE (value) == G_TYPE_PTR_ARRAY)
585 GPtrArray *array = g_value_get_boxed (value);
588 for (i = 0; i < 4; i++)
589 g_value_set_boxed (¶meter[i].value, g_ptr_array_index (array, i));
593 /* can be RGBA or symbolic color */
594 gpointer p = g_value_get_boxed (value);
596 g_value_set_boxed (¶meter[0].value, p);
597 g_value_set_boxed (¶meter[1].value, p);
598 g_value_set_boxed (¶meter[2].value, p);
599 g_value_set_boxed (¶meter[3].value, p);
607 pack_border_color (GValue *value,
608 GtkStyleProperties *props,
610 GtkStylePropertyContext *context)
612 /* NB: We are a color property, so we have to resolve to a color here.
613 * So we just resolve to a color. We pick one and stick to it.
614 * Lesson learned: Don't query border-color shorthand, query the
615 * real properties instead. */
616 g_value_unset (value);
617 gtk_style_properties_get_property (props, "border-top-color", state, value);
621 _gtk_css_shorthand_property_register (GParamSpec *pspec,
622 const char **subproperties,
623 GtkStyleUnpackFunc unpack_func,
624 GtkStylePackFunc pack_func,
625 GtkStyleParseFunc parse_func)
627 GtkStyleProperty *node;
629 g_return_if_fail (pack_func != NULL);
630 g_return_if_fail (unpack_func != NULL);
632 node = g_object_new (GTK_TYPE_CSS_SHORTHAND_PROPERTY,
634 "value-type", pspec->value_type,
635 "subproperties", subproperties,
639 node->pack_func = pack_func;
640 node->unpack_func = unpack_func;
641 node->parse_func = parse_func;
645 _gtk_css_shorthand_property_init_properties (void)
647 const char *font_subproperties[] = { "font-family", "font-style", "font-variant", "font-weight", "font-size", NULL };
648 const char *margin_subproperties[] = { "margin-top", "margin-right", "margin-bottom", "margin-left", NULL };
649 const char *padding_subproperties[] = { "padding-top", "padding-right", "padding-bottom", "padding-left", NULL };
650 const char *border_width_subproperties[] = { "border-top-width", "border-right-width", "border-bottom-width", "border-left-width", NULL };
651 const char *border_radius_subproperties[] = { "border-top-left-radius", "border-top-right-radius",
652 "border-bottom-right-radius", "border-bottom-left-radius", NULL };
653 const char *border_color_subproperties[] = { "border-top-color", "border-right-color", "border-bottom-color", "border-left-color", NULL };
654 const char *border_image_subproperties[] = { "border-image-source", "border-image-slice", "border-image-width", "border-image-repeat", NULL };
656 _gtk_css_shorthand_property_register (g_param_spec_boxed ("font",
659 PANGO_TYPE_FONT_DESCRIPTION, 0),
661 unpack_font_description,
662 pack_font_description,
664 _gtk_css_shorthand_property_register (g_param_spec_boxed ("margin",
668 margin_subproperties,
672 _gtk_css_shorthand_property_register (g_param_spec_boxed ("padding",
676 padding_subproperties,
680 _gtk_css_shorthand_property_register (g_param_spec_boxed ("border-width",
682 "Border width, in pixels",
684 border_width_subproperties,
688 _gtk_css_shorthand_property_register (g_param_spec_int ("border-radius",
690 "Border radius, in pixels",
692 border_radius_subproperties,
693 unpack_border_radius,
695 border_radius_value_parse);
696 _gtk_css_shorthand_property_register (g_param_spec_boxed ("border-color",
700 border_color_subproperties,
703 border_color_shorthand_value_parse);
704 _gtk_css_shorthand_property_register (g_param_spec_boxed ("border-image",
707 GTK_TYPE_BORDER_IMAGE, 0),
708 border_image_subproperties,
709 _gtk_border_image_unpack,
710 _gtk_border_image_pack,
711 border_image_value_parse);