1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
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 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
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
22 #include "gtkstylepropertyprivate.h"
29 #include <gdk-pixbuf/gdk-pixbuf.h>
30 #include <cairo-gobject.h>
32 #include "gtkcssprovider.h"
33 #include "gtkcssparserprivate.h"
34 #include "gtkcsstypesprivate.h"
36 /* the actual parsers we have */
37 #include "gtkanimationdescription.h"
38 #include "gtkbindings.h"
39 #include "gtkborderimageprivate.h"
40 #include "gtkgradient.h"
41 #include "gtkshadowprivate.h"
42 #include "gtkthemingengine.h"
43 #include "gtktypebuiltins.h"
45 static GHashTable *parse_funcs = NULL;
46 static GHashTable *print_funcs = NULL;
47 static GHashTable *properties = NULL;
50 register_conversion_function (GType type,
51 GtkStyleParseFunc parse,
52 GtkStylePrintFunc print)
55 g_hash_table_insert (parse_funcs, GSIZE_TO_POINTER (type), parse);
57 g_hash_table_insert (print_funcs, GSIZE_TO_POINTER (type), print);
61 string_append_double (GString *string,
64 char buf[G_ASCII_DTOSTR_BUF_SIZE];
66 g_ascii_dtostr (buf, sizeof (buf), d);
67 g_string_append (string, buf);
71 string_append_string (GString *str,
76 g_string_append_c (str, '"');
79 len = strcspn (string, "\"\n\r\f");
80 g_string_append (str, string);
87 g_string_append (str, "\\A ");
90 g_string_append (str, "\\D ");
93 g_string_append (str, "\\C ");
96 g_string_append (str, "\\\"");
99 g_assert_not_reached ();
104 g_string_append_c (str, '"');
107 /*** IMPLEMENTATIONS ***/
110 rgba_value_parse (GtkCssParser *parser,
114 GtkSymbolicColor *symbolic;
117 symbolic = _gtk_css_parser_read_symbolic_color (parser);
118 if (symbolic == NULL)
121 if (gtk_symbolic_color_resolve (symbolic, NULL, &rgba))
123 g_value_set_boxed (value, &rgba);
124 gtk_symbolic_color_unref (symbolic);
128 g_value_unset (value);
129 g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
130 g_value_take_boxed (value, symbolic);
137 rgba_value_print (const GValue *value,
140 const GdkRGBA *rgba = g_value_get_boxed (value);
143 g_string_append (string, "none");
146 char *s = gdk_rgba_to_string (rgba);
147 g_string_append (string, s);
153 color_value_parse (GtkCssParser *parser,
157 GtkSymbolicColor *symbolic;
160 symbolic = _gtk_css_parser_read_symbolic_color (parser);
161 if (symbolic == NULL)
164 if (gtk_symbolic_color_resolve (symbolic, NULL, &rgba))
168 color.red = rgba.red * 65535. + 0.5;
169 color.green = rgba.green * 65535. + 0.5;
170 color.blue = rgba.blue * 65535. + 0.5;
172 g_value_set_boxed (value, &color);
176 g_value_unset (value);
177 g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
178 g_value_take_boxed (value, symbolic);
185 color_value_print (const GValue *value,
188 const GdkColor *color = g_value_get_boxed (value);
191 g_string_append (string, "none");
194 char *s = gdk_color_to_string (color);
195 g_string_append (string, s);
201 symbolic_color_value_parse (GtkCssParser *parser,
205 GtkSymbolicColor *symbolic;
207 symbolic = _gtk_css_parser_read_symbolic_color (parser);
208 if (symbolic == NULL)
211 g_value_take_boxed (value, symbolic);
216 symbolic_color_value_print (const GValue *value,
219 GtkSymbolicColor *symbolic = g_value_get_boxed (value);
221 if (symbolic == NULL)
222 g_string_append (string, "none");
225 char *s = gtk_symbolic_color_to_string (symbolic);
226 g_string_append (string, s);
232 font_family_parse (GtkCssParser *parser,
239 /* We don't special case generic families. Pango should do
242 names = g_ptr_array_new ();
245 name = _gtk_css_parser_try_ident (parser, TRUE);
248 GString *string = g_string_new (name);
250 while ((name = _gtk_css_parser_try_ident (parser, TRUE)))
252 g_string_append_c (string, ' ');
253 g_string_append (string, name);
256 name = g_string_free (string, FALSE);
260 name = _gtk_css_parser_read_string (parser);
263 g_ptr_array_free (names, TRUE);
268 g_ptr_array_add (names, name);
269 } while (_gtk_css_parser_try (parser, ",", TRUE));
271 /* NULL-terminate array */
272 g_ptr_array_add (names, NULL);
273 g_value_set_boxed (value, g_ptr_array_free (names, FALSE));
278 font_family_value_print (const GValue *value,
281 const char **names = g_value_get_boxed (value);
283 if (names == NULL || *names == NULL)
285 g_string_append (string, "none");
289 string_append_string (string, *names);
293 g_string_append (string, ", ");
294 string_append_string (string, *names);
300 font_description_value_parse (GtkCssParser *parser,
304 PangoFontDescription *font_desc;
307 str = _gtk_css_parser_read_value (parser);
311 font_desc = pango_font_description_from_string (str);
313 g_value_take_boxed (value, font_desc);
318 font_description_value_print (const GValue *value,
321 const PangoFontDescription *desc = g_value_get_boxed (value);
324 g_string_append (string, "none");
327 char *s = pango_font_description_to_string (desc);
328 g_string_append (string, s);
334 boolean_value_parse (GtkCssParser *parser,
338 if (_gtk_css_parser_try (parser, "true", TRUE) ||
339 _gtk_css_parser_try (parser, "1", TRUE))
341 g_value_set_boolean (value, TRUE);
344 else if (_gtk_css_parser_try (parser, "false", TRUE) ||
345 _gtk_css_parser_try (parser, "0", TRUE))
347 g_value_set_boolean (value, FALSE);
352 _gtk_css_parser_error (parser, "Expected a boolean value");
358 boolean_value_print (const GValue *value,
361 if (g_value_get_boolean (value))
362 g_string_append (string, "true");
364 g_string_append (string, "false");
368 int_value_parse (GtkCssParser *parser,
374 if (!_gtk_css_parser_try_int (parser, &i))
376 _gtk_css_parser_error (parser, "Expected a valid integer value");
380 g_value_set_int (value, i);
385 int_value_print (const GValue *value,
388 g_string_append_printf (string, "%d", g_value_get_int (value));
392 uint_value_parse (GtkCssParser *parser,
398 if (!_gtk_css_parser_try_uint (parser, &u))
400 _gtk_css_parser_error (parser, "Expected a valid unsigned value");
404 g_value_set_uint (value, u);
409 uint_value_print (const GValue *value,
412 g_string_append_printf (string, "%u", g_value_get_uint (value));
416 double_value_parse (GtkCssParser *parser,
422 if (!_gtk_css_parser_try_double (parser, &d))
424 _gtk_css_parser_error (parser, "Expected a number");
428 g_value_set_double (value, d);
433 double_value_print (const GValue *value,
436 string_append_double (string, g_value_get_double (value));
440 float_value_parse (GtkCssParser *parser,
446 if (!_gtk_css_parser_try_double (parser, &d))
448 _gtk_css_parser_error (parser, "Expected a number");
452 g_value_set_float (value, d);
457 float_value_print (const GValue *value,
460 string_append_double (string, g_value_get_float (value));
464 string_value_parse (GtkCssParser *parser,
468 char *str = _gtk_css_parser_read_string (parser);
473 g_value_take_string (value, str);
478 string_value_print (const GValue *value,
481 string_append_string (str, g_value_get_string (value));
485 theming_engine_value_parse (GtkCssParser *parser,
489 GtkThemingEngine *engine;
492 str = _gtk_css_parser_try_ident (parser, TRUE);
495 _gtk_css_parser_error (parser, "Expected a valid theme name");
499 engine = gtk_theming_engine_load (str);
502 _gtk_css_parser_error (parser, "Themeing engine '%s' not found", str);
507 g_value_set_object (value, engine);
513 theming_engine_value_print (const GValue *value,
516 GtkThemingEngine *engine;
519 engine = g_value_get_object (value);
521 g_string_append (string, "none");
524 /* XXX: gtk_theming_engine_get_name()? */
525 g_object_get (engine, "name", &name, NULL);
526 g_string_append (string, name);
532 animation_description_value_parse (GtkCssParser *parser,
536 GtkAnimationDescription *desc;
539 str = _gtk_css_parser_read_value (parser);
543 desc = _gtk_animation_description_from_string (str);
548 _gtk_css_parser_error (parser, "Invalid animation description");
552 g_value_take_boxed (value, desc);
557 animation_description_value_print (const GValue *value,
560 GtkAnimationDescription *desc = g_value_get_boxed (value);
563 g_string_append (string, "none");
565 _gtk_animation_description_print (desc, string);
569 border_value_parse (GtkCssParser *parser,
573 GtkBorder border = { 0, };
576 for (i = 0; i < G_N_ELEMENTS (numbers); i++)
578 if (!_gtk_css_parser_try_uint (parser, &numbers[i]))
581 /* XXX: shouldn't allow spaces here? */
582 _gtk_css_parser_try (parser, "px", TRUE);
587 _gtk_css_parser_error (parser, "Expected valid border");
591 border.top = numbers[0];
593 border.right = numbers[1];
595 border.right = border.top;
597 border.bottom = numbers[2];
599 border.bottom = border.top;
601 border.left = numbers[3];
603 border.left = border.right;
605 g_value_set_boxed (value, &border);
610 border_value_print (const GValue *value, GString *string)
612 const GtkBorder *border = g_value_get_boxed (value);
615 g_string_append (string, "none");
616 else if (border->left != border->right)
617 g_string_append_printf (string, "%d %d %d %d", border->top, border->right, border->bottom, border->left);
618 else if (border->top != border->bottom)
619 g_string_append_printf (string, "%d %d %d", border->top, border->right, border->bottom);
620 else if (border->top != border->left)
621 g_string_append_printf (string, "%d %d", border->top, border->right);
623 g_string_append_printf (string, "%d", border->top);
627 gradient_value_parse (GtkCssParser *parser,
631 GtkGradient *gradient;
632 cairo_pattern_type_t type;
636 if (!_gtk_css_parser_try (parser, "-gtk-gradient", TRUE))
638 _gtk_css_parser_error (parser,
639 "Expected '-gtk-gradient'");
643 if (!_gtk_css_parser_try (parser, "(", TRUE))
645 _gtk_css_parser_error (parser,
646 "Expected '(' after '-gtk-gradient'");
650 /* Parse gradient type */
651 if (_gtk_css_parser_try (parser, "linear", TRUE))
652 type = CAIRO_PATTERN_TYPE_LINEAR;
653 else if (_gtk_css_parser_try (parser, "radial", TRUE))
654 type = CAIRO_PATTERN_TYPE_RADIAL;
657 _gtk_css_parser_error (parser,
658 "Gradient type must be 'radial' or 'linear'");
662 /* Parse start/stop position parameters */
663 for (i = 0; i < 2; i++)
665 if (! _gtk_css_parser_try (parser, ",", TRUE))
667 _gtk_css_parser_error (parser,
672 if (_gtk_css_parser_try (parser, "left", TRUE))
674 else if (_gtk_css_parser_try (parser, "right", TRUE))
676 else if (_gtk_css_parser_try (parser, "center", TRUE))
678 else if (!_gtk_css_parser_try_double (parser, &coords[i * 3]))
680 _gtk_css_parser_error (parser,
681 "Expected a valid X coordinate");
685 if (_gtk_css_parser_try (parser, "top", TRUE))
686 coords[i * 3 + 1] = 0;
687 else if (_gtk_css_parser_try (parser, "bottom", TRUE))
688 coords[i * 3 + 1] = 1;
689 else if (_gtk_css_parser_try (parser, "center", TRUE))
690 coords[i * 3 + 1] = 0.5;
691 else if (!_gtk_css_parser_try_double (parser, &coords[i * 3 + 1]))
693 _gtk_css_parser_error (parser,
694 "Expected a valid Y coordinate");
698 if (type == CAIRO_PATTERN_TYPE_RADIAL)
701 if (! _gtk_css_parser_try (parser, ",", TRUE))
703 _gtk_css_parser_error (parser,
708 if (! _gtk_css_parser_try_double (parser, &coords[(i * 3) + 2]))
710 _gtk_css_parser_error (parser,
711 "Expected a numer for the radius");
717 if (type == CAIRO_PATTERN_TYPE_LINEAR)
718 gradient = gtk_gradient_new_linear (coords[0], coords[1], coords[3], coords[4]);
720 gradient = gtk_gradient_new_radial (coords[0], coords[1], coords[2],
721 coords[3], coords[4], coords[5]);
723 while (_gtk_css_parser_try (parser, ",", TRUE))
725 GtkSymbolicColor *color;
728 if (_gtk_css_parser_try (parser, "from", TRUE))
732 if (!_gtk_css_parser_try (parser, "(", TRUE))
734 gtk_gradient_unref (gradient);
735 _gtk_css_parser_error (parser,
741 else if (_gtk_css_parser_try (parser, "to", TRUE))
745 if (!_gtk_css_parser_try (parser, "(", TRUE))
747 gtk_gradient_unref (gradient);
748 _gtk_css_parser_error (parser,
754 else if (_gtk_css_parser_try (parser, "color-stop", TRUE))
756 if (!_gtk_css_parser_try (parser, "(", TRUE))
758 gtk_gradient_unref (gradient);
759 _gtk_css_parser_error (parser,
764 if (!_gtk_css_parser_try_double (parser, &position))
766 gtk_gradient_unref (gradient);
767 _gtk_css_parser_error (parser,
768 "Expected a valid number");
772 if (!_gtk_css_parser_try (parser, ",", TRUE))
774 gtk_gradient_unref (gradient);
775 _gtk_css_parser_error (parser,
782 gtk_gradient_unref (gradient);
783 _gtk_css_parser_error (parser,
784 "Not a valid color-stop definition");
788 color = _gtk_css_parser_read_symbolic_color (parser);
791 gtk_gradient_unref (gradient);
795 gtk_gradient_add_color_stop (gradient, position, color);
796 gtk_symbolic_color_unref (color);
798 if (!_gtk_css_parser_try (parser, ")", TRUE))
800 gtk_gradient_unref (gradient);
801 _gtk_css_parser_error (parser,
807 if (!_gtk_css_parser_try (parser, ")", TRUE))
809 gtk_gradient_unref (gradient);
810 _gtk_css_parser_error (parser,
815 g_value_take_boxed (value, gradient);
820 gradient_value_print (const GValue *value,
823 GtkGradient *gradient = g_value_get_boxed (value);
825 if (gradient == NULL)
826 g_string_append (string, "none");
829 char *s = gtk_gradient_to_string (gradient);
830 g_string_append (string, s);
836 gtk_css_parse_url (GtkCssParser *parser,
842 if (_gtk_css_parser_try (parser, "url", FALSE))
844 if (!_gtk_css_parser_try (parser, "(", TRUE))
846 _gtk_css_parser_skip_whitespace (parser);
847 if (_gtk_css_parser_try (parser, "(", TRUE))
851 error = g_error_new_literal (GTK_CSS_PROVIDER_ERROR,
852 GTK_CSS_PROVIDER_ERROR_DEPRECATED,
853 "Whitespace between 'url' and '(' is not allowed");
855 _gtk_css_parser_take_error (parser, error);
859 _gtk_css_parser_error (parser, "Expected '(' after 'url'");
864 path = _gtk_css_parser_read_string (parser);
868 if (!_gtk_css_parser_try (parser, ")", TRUE))
870 _gtk_css_parser_error (parser, "No closing ')' found for 'url'");
877 path = _gtk_css_parser_try_name (parser, TRUE);
880 _gtk_css_parser_error (parser, "Not a valid url");
885 file = g_file_resolve_relative_path (base, path);
892 pattern_value_parse (GtkCssParser *parser,
896 if (_gtk_css_parser_begins_with (parser, '-'))
898 g_value_unset (value);
899 g_value_init (value, GTK_TYPE_GRADIENT);
900 return gradient_value_parse (parser, base, value);
904 GError *error = NULL;
908 cairo_surface_t *surface;
909 cairo_pattern_t *pattern;
911 cairo_matrix_t matrix;
913 file = gtk_css_parse_url (parser, base);
917 path = g_file_get_path (file);
918 g_object_unref (file);
920 pixbuf = gdk_pixbuf_new_from_file (path, &error);
924 _gtk_css_parser_take_error (parser, error);
928 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
929 gdk_pixbuf_get_width (pixbuf),
930 gdk_pixbuf_get_height (pixbuf));
931 cr = cairo_create (surface);
932 gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
934 pattern = cairo_pattern_create_for_surface (surface);
936 cairo_matrix_init_scale (&matrix,
937 gdk_pixbuf_get_width (pixbuf),
938 gdk_pixbuf_get_height (pixbuf));
939 cairo_pattern_set_matrix (pattern, &matrix);
941 cairo_surface_destroy (surface);
943 g_object_unref (pixbuf);
945 g_value_take_boxed (value, pattern);
952 shadow_value_parse (GtkCssParser *parser,
956 gboolean have_inset, have_color, have_lengths;
957 gdouble hoffset, voffset, blur, spread;
958 GtkSymbolicColor *color;
962 shadow = _gtk_shadow_new ();
966 have_inset = have_lengths = have_color = FALSE;
968 for (i = 0; i < 3; i++)
971 _gtk_css_parser_try (parser, "inset", TRUE))
978 _gtk_css_parser_try_double (parser, &hoffset))
982 if (!_gtk_css_parser_try_double (parser, &voffset))
984 _gtk_css_parser_error (parser, "Horizontal and vertical offsets are required");
985 _gtk_shadow_unref (shadow);
989 if (!_gtk_css_parser_try_double (parser, &blur))
992 if (!_gtk_css_parser_try_double (parser, &spread))
1002 /* XXX: the color is optional and UA-defined if it's missing,
1003 * but it doesn't really make sense for us...
1005 color = _gtk_css_parser_read_symbolic_color (parser);
1009 _gtk_shadow_unref (shadow);
1015 if (!have_color || !have_lengths)
1017 _gtk_css_parser_error (parser, "Must specify at least color and offsets");
1018 _gtk_shadow_unref (shadow);
1022 _gtk_shadow_append (shadow,
1027 gtk_symbolic_color_unref (color);
1030 while (_gtk_css_parser_try (parser, ",", TRUE));
1032 g_value_take_boxed (value, shadow);
1037 shadow_value_print (const GValue *value,
1042 shadow = g_value_get_boxed (value);
1045 g_string_append (string, "none");
1047 _gtk_shadow_print (shadow, string);
1051 border_image_repeat_value_parse (GtkCssParser *parser,
1055 GtkCssBorderImageRepeat image_repeat;
1056 GtkCssRepeatStyle styles[2];
1059 for (i = 0; i < 2; i++)
1061 if (_gtk_css_parser_try (parser, "stretch", TRUE))
1062 styles[i] = GTK_CSS_REPEAT_STYLE_NONE;
1063 else if (_gtk_css_parser_try (parser, "repeat", TRUE))
1064 styles[i] = GTK_CSS_REPEAT_STYLE_REPEAT;
1065 else if (_gtk_css_parser_try (parser, "round", TRUE))
1066 styles[i] = GTK_CSS_REPEAT_STYLE_ROUND;
1067 else if (_gtk_css_parser_try (parser, "space", TRUE))
1068 styles[i] = GTK_CSS_REPEAT_STYLE_SPACE;
1071 styles[1] = styles[0] = GTK_CSS_REPEAT_STYLE_NONE;
1075 styles[i] = styles[0];
1078 image_repeat.hrepeat = styles[0];
1079 image_repeat.vrepeat = styles[1];
1081 g_value_set_boxed (value, &image_repeat);
1086 static const gchar *
1087 border_image_repeat_style_to_string (GtkCssRepeatStyle repeat)
1091 case GTK_CSS_REPEAT_STYLE_NONE:
1093 case GTK_CSS_REPEAT_STYLE_REPEAT:
1095 case GTK_CSS_REPEAT_STYLE_ROUND:
1097 case GTK_CSS_REPEAT_STYLE_SPACE:
1105 border_image_repeat_value_print (const GValue *value,
1108 GtkCssBorderImageRepeat *image_repeat;
1110 image_repeat = g_value_get_boxed (value);
1112 g_string_append_printf (string, "%s %s",
1113 border_image_repeat_style_to_string (image_repeat->hrepeat),
1114 border_image_repeat_style_to_string (image_repeat->vrepeat));
1118 border_image_value_parse (GtkCssParser *parser,
1122 GValue temp = { 0, };
1123 cairo_pattern_t *pattern = NULL;
1124 GtkGradient *gradient = NULL;
1125 GtkBorder slice, *width = NULL, *parsed_slice;
1126 GtkCssBorderImageRepeat repeat, *parsed_repeat;
1127 gboolean retval = FALSE;
1128 GtkBorderImage *image = NULL;
1130 g_value_init (&temp, CAIRO_GOBJECT_TYPE_PATTERN);
1132 if (!pattern_value_parse (parser, base, &temp))
1135 if (G_VALUE_TYPE (&temp) == GTK_TYPE_GRADIENT)
1136 gradient = g_value_dup_boxed (&temp);
1138 pattern = g_value_dup_boxed (&temp);
1140 g_value_unset (&temp);
1141 g_value_init (&temp, GTK_TYPE_BORDER);
1143 if (!border_value_parse (parser, base, &temp))
1146 parsed_slice = g_value_get_boxed (&temp);
1147 slice = *parsed_slice;
1149 if (_gtk_css_parser_try (parser, "/", TRUE))
1151 g_value_unset (&temp);
1152 g_value_init (&temp, GTK_TYPE_BORDER);
1154 if (!border_value_parse (parser, base, &temp))
1157 width = g_value_dup_boxed (&temp);
1160 g_value_unset (&temp);
1161 g_value_init (&temp, GTK_TYPE_CSS_BORDER_IMAGE_REPEAT);
1163 if (!border_image_repeat_value_parse (parser, base, &temp))
1166 parsed_repeat = g_value_get_boxed (&temp);
1167 repeat = *parsed_repeat;
1169 g_value_unset (&temp);
1171 if (gradient != NULL)
1172 image = _gtk_border_image_new_for_gradient (gradient, &slice, width, &repeat);
1173 else if (pattern != NULL)
1174 image = _gtk_border_image_new (pattern, &slice, width, &repeat);
1179 g_value_take_boxed (value, image);
1183 if (pattern != NULL)
1184 cairo_pattern_destroy (pattern);
1186 if (gradient != NULL)
1187 gtk_gradient_unref (gradient);
1190 gtk_border_free (width);
1196 enum_value_parse (GtkCssParser *parser,
1200 GEnumClass *enum_class;
1201 GEnumValue *enum_value;
1204 str = _gtk_css_parser_try_ident (parser, TRUE);
1207 _gtk_css_parser_error (parser, "Expected an identifier");
1211 enum_class = g_type_class_ref (G_VALUE_TYPE (value));
1212 enum_value = g_enum_get_value_by_nick (enum_class, str);
1215 g_value_set_enum (value, enum_value->value);
1217 _gtk_css_parser_error (parser,
1218 "Unknown value '%s' for enum type '%s'",
1219 str, g_type_name (G_VALUE_TYPE (value)));
1221 g_type_class_unref (enum_class);
1224 return enum_value != NULL;
1228 enum_value_print (const GValue *value,
1231 GEnumClass *enum_class;
1232 GEnumValue *enum_value;
1234 enum_class = g_type_class_ref (G_VALUE_TYPE (value));
1235 enum_value = g_enum_get_value (enum_class, g_value_get_enum (value));
1237 g_string_append (string, enum_value->value_nick);
1239 g_type_class_unref (enum_class);
1243 flags_value_parse (GtkCssParser *parser,
1247 GFlagsClass *flags_class;
1248 GFlagsValue *flag_value;
1252 flags_class = g_type_class_ref (G_VALUE_TYPE (value));
1255 str = _gtk_css_parser_try_ident (parser, TRUE);
1258 _gtk_css_parser_error (parser, "Expected an identifier");
1259 g_type_class_unref (flags_class);
1263 flag_value = g_flags_get_value_by_nick (flags_class, str);
1266 _gtk_css_parser_error (parser,
1267 "Unknown flag value '%s' for type '%s'",
1268 str, g_type_name (G_VALUE_TYPE (value)));
1269 /* XXX Do we want to return FALSE here? We can get
1270 * forward-compatibility for new values this way
1273 g_type_class_unref (flags_class);
1279 while (_gtk_css_parser_try (parser, ",", FALSE));
1281 g_type_class_unref (flags_class);
1283 g_value_set_enum (value, flags);
1289 flags_value_print (const GValue *value,
1292 GFlagsClass *flags_class;
1295 flags_class = g_type_class_ref (G_VALUE_TYPE (value));
1296 flags = g_value_get_flags (value);
1298 for (i = 0; i < flags_class->n_values; i++)
1300 GFlagsValue *flags_value = &flags_class->values[i];
1302 if (flags & flags_value->value)
1304 if (string->len != 0)
1305 g_string_append (string, ", ");
1307 g_string_append (string, flags_value->value_nick);
1311 g_type_class_unref (flags_class);
1315 bindings_value_parse (GtkCssParser *parser,
1320 GtkBindingSet *binding_set;
1323 array = g_ptr_array_new ();
1326 name = _gtk_css_parser_try_ident (parser, TRUE);
1329 _gtk_css_parser_error (parser, "Not a valid binding name");
1330 g_ptr_array_free (array, TRUE);
1334 binding_set = gtk_binding_set_find (name);
1338 _gtk_css_parser_error (parser, "No binding set named '%s'", name);
1343 g_ptr_array_add (array, binding_set);
1346 while (_gtk_css_parser_try (parser, ",", TRUE));
1348 g_value_take_boxed (value, array);
1354 bindings_value_print (const GValue *value,
1360 array = g_value_get_boxed (value);
1362 for (i = 0; i < array->len; i++)
1364 GtkBindingSet *binding_set = g_ptr_array_index (array, i);
1367 g_string_append (string, ", ");
1368 g_string_append (string, binding_set->set_name);
1373 border_corner_radius_value_parse (GtkCssParser *parser,
1377 GtkCssBorderCornerRadius corner;
1379 if (!_gtk_css_parser_try_double (parser, &corner.horizontal))
1381 _gtk_css_parser_error (parser, "Expected a number");
1384 else if (corner.horizontal < 0)
1387 if (!_gtk_css_parser_try_double (parser, &corner.vertical))
1388 corner.vertical = corner.horizontal;
1389 else if (corner.vertical < 0)
1392 g_value_set_boxed (value, &corner);
1396 _gtk_css_parser_error (parser, "Border radius values cannot be negative");
1401 border_corner_radius_value_print (const GValue *value,
1404 GtkCssBorderCornerRadius *corner;
1406 corner = g_value_get_boxed (value);
1410 g_string_append (string, "none");
1414 string_append_double (string, corner->horizontal);
1415 if (corner->horizontal != corner->vertical)
1417 g_string_append_c (string, ' ');
1418 string_append_double (string, corner->vertical);
1423 border_radius_value_parse (GtkCssParser *parser,
1427 GtkCssBorderRadius border;
1429 if (!_gtk_css_parser_try_double (parser, &border.top_left.horizontal))
1431 _gtk_css_parser_error (parser, "Expected a number");
1434 else if (border.top_left.horizontal < 0)
1437 if (_gtk_css_parser_try_double (parser, &border.top_right.horizontal))
1439 if (border.top_right.horizontal < 0)
1441 if (_gtk_css_parser_try_double (parser, &border.bottom_right.horizontal))
1443 if (border.bottom_right.horizontal < 0)
1445 if (!_gtk_css_parser_try_double (parser, &border.bottom_left.horizontal))
1446 border.bottom_left.horizontal = border.top_right.horizontal;
1447 else if (border.bottom_left.horizontal < 0)
1452 border.bottom_right.horizontal = border.top_left.horizontal;
1453 border.bottom_left.horizontal = border.top_right.horizontal;
1458 border.top_right.horizontal = border.top_left.horizontal;
1459 border.bottom_right.horizontal = border.top_left.horizontal;
1460 border.bottom_left.horizontal = border.top_left.horizontal;
1463 if (_gtk_css_parser_try (parser, "/", TRUE))
1465 if (!_gtk_css_parser_try_double (parser, &border.top_left.vertical))
1467 _gtk_css_parser_error (parser, "Expected a number");
1470 else if (border.top_left.vertical < 0)
1473 if (_gtk_css_parser_try_double (parser, &border.top_right.vertical))
1475 if (border.top_right.vertical < 0)
1477 if (_gtk_css_parser_try_double (parser, &border.bottom_right.vertical))
1479 if (border.bottom_right.vertical < 0)
1481 if (!_gtk_css_parser_try_double (parser, &border.bottom_left.vertical))
1482 border.bottom_left.vertical = border.top_right.vertical;
1483 else if (border.bottom_left.vertical < 0)
1488 border.bottom_right.vertical = border.top_left.vertical;
1489 border.bottom_left.vertical = border.top_right.vertical;
1494 border.top_right.vertical = border.top_left.vertical;
1495 border.bottom_right.vertical = border.top_left.vertical;
1496 border.bottom_left.vertical = border.top_left.vertical;
1501 border.top_left.vertical = border.top_left.horizontal;
1502 border.top_right.vertical = border.top_right.horizontal;
1503 border.bottom_right.vertical = border.bottom_right.horizontal;
1504 border.bottom_left.vertical = border.bottom_left.horizontal;
1507 /* border-radius is an int property for backwards-compat reasons */
1508 g_value_unset (value);
1509 g_value_init (value, GTK_TYPE_CSS_BORDER_RADIUS);
1510 g_value_set_boxed (value, &border);
1515 _gtk_css_parser_error (parser, "Border radius values cannot be negative");
1520 border_radius_value_print (const GValue *value,
1523 GtkCssBorderRadius *border;
1525 border = g_value_get_boxed (value);
1529 g_string_append (string, "none");
1533 string_append_double (string, border->top_left.horizontal);
1534 if (border->top_left.horizontal != border->top_right.horizontal ||
1535 border->top_left.horizontal != border->bottom_right.horizontal ||
1536 border->top_left.horizontal != border->bottom_left.horizontal)
1538 g_string_append_c (string, ' ');
1539 string_append_double (string, border->top_right.horizontal);
1540 if (border->top_left.horizontal != border->bottom_right.horizontal ||
1541 border->top_right.horizontal != border->bottom_left.horizontal)
1543 g_string_append_c (string, ' ');
1544 string_append_double (string, border->bottom_right.horizontal);
1545 if (border->top_right.horizontal != border->bottom_left.horizontal)
1547 g_string_append_c (string, ' ');
1548 string_append_double (string, border->bottom_left.horizontal);
1553 if (border->top_left.horizontal != border->top_left.vertical ||
1554 border->top_right.horizontal != border->top_right.vertical ||
1555 border->bottom_right.horizontal != border->bottom_right.vertical ||
1556 border->bottom_left.horizontal != border->bottom_left.vertical)
1558 g_string_append (string, " / ");
1559 string_append_double (string, border->top_left.vertical);
1560 if (border->top_left.vertical != border->top_right.vertical ||
1561 border->top_left.vertical != border->bottom_right.vertical ||
1562 border->top_left.vertical != border->bottom_left.vertical)
1564 g_string_append_c (string, ' ');
1565 string_append_double (string, border->top_right.vertical);
1566 if (border->top_left.vertical != border->bottom_right.vertical ||
1567 border->top_right.vertical != border->bottom_left.vertical)
1569 g_string_append_c (string, ' ');
1570 string_append_double (string, border->bottom_right.vertical);
1571 if (border->top_right.vertical != border->bottom_left.vertical)
1573 g_string_append_c (string, ' ');
1574 string_append_double (string, border->bottom_left.vertical);
1583 border_color_value_parse (GtkCssParser *parser,
1587 if (_gtk_css_parser_try (parser, "transparent", TRUE))
1589 GdkRGBA transparent = { 0, 0, 0, 0 };
1591 g_value_set_boxed (value, &transparent);
1596 return rgba_value_parse (parser, base, value);
1600 border_color_shorthand_value_parse (GtkCssParser *parser,
1604 GtkSymbolicColor *symbolic;
1607 array = g_ptr_array_new_with_free_func ((GDestroyNotify) gtk_symbolic_color_unref);
1611 if (_gtk_css_parser_try (parser, "transparent", TRUE))
1613 GdkRGBA transparent = { 0, 0, 0, 0 };
1615 symbolic = gtk_symbolic_color_new_literal (&transparent);
1619 symbolic = _gtk_css_parser_read_symbolic_color (parser);
1621 if (symbolic == NULL)
1625 g_ptr_array_add (array, symbolic);
1627 while (array->len < 4 &&
1628 !_gtk_css_parser_is_eof (parser) &&
1629 !_gtk_css_parser_begins_with (parser, ';') &&
1630 !_gtk_css_parser_begins_with (parser, '}'));
1635 g_assert_not_reached ();
1638 g_ptr_array_add (array, gtk_symbolic_color_ref (g_ptr_array_index (array, 0)));
1641 g_ptr_array_add (array, gtk_symbolic_color_ref (g_ptr_array_index (array, 0)));
1644 g_ptr_array_add (array, gtk_symbolic_color_ref (g_ptr_array_index (array, 1)));
1650 g_value_unset (value);
1651 g_value_init (value, G_TYPE_PTR_ARRAY);
1652 g_value_take_boxed (value, array);
1660 unpack_border (const GValue *value,
1667 GParameter *parameter = g_new0 (GParameter, 4);
1668 GtkBorder *border = g_value_get_boxed (value);
1670 parameter[0].name = top;
1671 g_value_init (¶meter[0].value, G_TYPE_INT);
1672 g_value_set_int (¶meter[0].value, border->top);
1673 parameter[1].name = left;
1674 g_value_init (¶meter[1].value, G_TYPE_INT);
1675 g_value_set_int (¶meter[1].value, border->left);
1676 parameter[2].name = bottom;
1677 g_value_init (¶meter[2].value, G_TYPE_INT);
1678 g_value_set_int (¶meter[2].value, border->bottom);
1679 parameter[3].name = right;
1680 g_value_init (¶meter[3].value, G_TYPE_INT);
1681 g_value_set_int (¶meter[3].value, border->right);
1688 pack_border (GValue *value,
1689 GtkStyleProperties *props,
1690 GtkStateFlags state,
1699 gtk_style_properties_get (props,
1712 g_value_set_boxed (value, &border);
1716 unpack_border_width (const GValue *value,
1719 return unpack_border (value, n_params,
1720 "border-top-width", "border-left-width",
1721 "border-bottom-width", "border-right-width");
1725 pack_border_width (GValue *value,
1726 GtkStyleProperties *props,
1727 GtkStateFlags state)
1729 pack_border (value, props, state,
1730 "border-top-width", "border-left-width",
1731 "border-bottom-width", "border-right-width");
1735 unpack_padding (const GValue *value,
1738 return unpack_border (value, n_params,
1739 "padding-top", "padding-left",
1740 "padding-bottom", "padding-right");
1744 pack_padding (GValue *value,
1745 GtkStyleProperties *props,
1746 GtkStateFlags state)
1748 pack_border (value, props, state,
1749 "padding-top", "padding-left",
1750 "padding-bottom", "padding-right");
1754 unpack_margin (const GValue *value,
1757 return unpack_border (value, n_params,
1758 "margin-top", "margin-left",
1759 "margin-bottom", "margin-right");
1763 pack_margin (GValue *value,
1764 GtkStyleProperties *props,
1765 GtkStateFlags state)
1767 pack_border (value, props, state,
1768 "margin-top", "margin-left",
1769 "margin-bottom", "margin-right");
1773 unpack_border_radius (const GValue *value,
1776 GParameter *parameter = g_new0 (GParameter, 4);
1777 GtkCssBorderRadius *border;
1779 if (G_VALUE_HOLDS_BOXED (value))
1780 border = g_value_get_boxed (value);
1784 parameter[0].name = "border-top-left-radius";
1785 g_value_init (¶meter[0].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
1786 parameter[1].name = "border-top-right-radius";
1787 g_value_init (¶meter[1].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
1788 parameter[2].name = "border-bottom-right-radius";
1789 g_value_init (¶meter[2].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
1790 parameter[3].name = "border-bottom-left-radius";
1791 g_value_init (¶meter[3].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
1794 g_value_set_boxed (¶meter[0].value, &border->top_left);
1795 g_value_set_boxed (¶meter[1].value, &border->top_right);
1796 g_value_set_boxed (¶meter[2].value, &border->bottom_right);
1797 g_value_set_boxed (¶meter[3].value, &border->bottom_left);
1805 pack_border_radius (GValue *value,
1806 GtkStyleProperties *props,
1807 GtkStateFlags state)
1809 GtkCssBorderCornerRadius *top_left;
1811 /* NB: We are an int property, so we have to resolve to an int here.
1812 * So we just resolve to an int. We pick one and stick to it.
1813 * Lesson learned: Don't query border-radius shorthand, query the
1814 * real properties instead. */
1815 gtk_style_properties_get (props,
1817 "border-top-left-radius", &top_left,
1821 g_value_set_int (value, top_left->horizontal);
1827 unpack_font_description (const GValue *value,
1830 GParameter *parameter = g_new0 (GParameter, 5);
1831 PangoFontDescription *description;
1835 /* For backwards compat, we only unpack values that are indeed set.
1836 * For strict CSS conformance we need to unpack all of them.
1837 * Note that we do set all of them in the parse function, so it
1838 * will not have effects when parsing CSS files. It will though
1839 * for custom style providers.
1842 description = g_value_get_boxed (value);
1846 mask = pango_font_description_get_set_fields (description);
1850 if (mask & PANGO_FONT_MASK_FAMILY)
1852 GPtrArray *strv = g_ptr_array_new ();
1854 g_ptr_array_add (strv, g_strdup (pango_font_description_get_family (description)));
1855 g_ptr_array_add (strv, NULL);
1856 parameter[n].name = "font-family";
1857 g_value_init (¶meter[n].value, G_TYPE_STRV);
1858 g_value_take_boxed (¶meter[n].value,
1859 g_ptr_array_free (strv, FALSE));
1863 if (mask & PANGO_FONT_MASK_STYLE)
1865 parameter[n].name = "font-style";
1866 g_value_init (¶meter[n].value, PANGO_TYPE_STYLE);
1867 g_value_set_enum (¶meter[n].value,
1868 pango_font_description_get_style (description));
1872 if (mask & PANGO_FONT_MASK_VARIANT)
1874 parameter[n].name = "font-variant";
1875 g_value_init (¶meter[n].value, PANGO_TYPE_VARIANT);
1876 g_value_set_enum (¶meter[n].value,
1877 pango_font_description_get_variant (description));
1881 if (mask & PANGO_FONT_MASK_WEIGHT)
1883 parameter[n].name = "font-weight";
1884 g_value_init (¶meter[n].value, PANGO_TYPE_WEIGHT);
1885 g_value_set_enum (¶meter[n].value,
1886 pango_font_description_get_weight (description));
1890 if (mask & PANGO_FONT_MASK_SIZE)
1892 parameter[n].name = "font-size";
1893 g_value_init (¶meter[n].value, G_TYPE_DOUBLE);
1894 g_value_set_double (¶meter[n].value,
1895 (double) pango_font_description_get_size (description) / PANGO_SCALE);
1905 pack_font_description (GValue *value,
1906 GtkStyleProperties *props,
1907 GtkStateFlags state)
1909 PangoFontDescription *description;
1912 PangoVariant variant;
1916 gtk_style_properties_get (props,
1918 "font-family", &families,
1919 "font-style", &style,
1920 "font-variant", &variant,
1921 "font-weight", &weight,
1925 description = pango_font_description_new ();
1926 /* xxx: Can we set all the families here somehow? */
1928 pango_font_description_set_family (description, families[0]);
1929 pango_font_description_set_size (description, round (size * PANGO_SCALE));
1930 pango_font_description_set_style (description, style);
1931 pango_font_description_set_variant (description, variant);
1932 pango_font_description_set_weight (description, weight);
1936 g_value_take_boxed (value, description);
1940 unpack_border_color (const GValue *value,
1943 GParameter *parameter = g_new0 (GParameter, 4);
1946 type = G_VALUE_TYPE (value);
1947 if (type == G_TYPE_PTR_ARRAY)
1948 type = GTK_TYPE_SYMBOLIC_COLOR;
1950 parameter[0].name = "border-top-color";
1951 g_value_init (¶meter[0].value, type);
1952 parameter[1].name = "border-right-color";
1953 g_value_init (¶meter[1].value, type);
1954 parameter[2].name = "border-bottom-color";
1955 g_value_init (¶meter[2].value, type);
1956 parameter[3].name = "border-left-color";
1957 g_value_init (¶meter[3].value, type);
1959 if (G_VALUE_TYPE (value) == G_TYPE_PTR_ARRAY)
1961 GPtrArray *array = g_value_get_boxed (value);
1964 for (i = 0; i < 4; i++)
1965 g_value_set_boxed (¶meter[i].value, g_ptr_array_index (array, i));
1969 /* can be RGBA or symbolic color */
1970 gpointer p = g_value_get_boxed (value);
1972 g_value_set_boxed (¶meter[0].value, p);
1973 g_value_set_boxed (¶meter[1].value, p);
1974 g_value_set_boxed (¶meter[2].value, p);
1975 g_value_set_boxed (¶meter[3].value, p);
1983 pack_border_color (GValue *value,
1984 GtkStyleProperties *props,
1985 GtkStateFlags state)
1987 /* NB: We are a color property, so we have to resolve to a color here.
1988 * So we just resolve to a color. We pick one and stick to it.
1989 * Lesson learned: Don't query border-color shorthand, query the
1990 * real properties instead. */
1991 g_value_unset (value);
1992 gtk_style_properties_get_property (props, "border-top-color", state, value);
1995 /*** UNSET FUNCS ***/
1998 unset_font_description (GtkStyleProperties *props,
1999 GtkStateFlags state)
2001 gtk_style_properties_unset_property (props, "font-family", state);
2002 gtk_style_properties_unset_property (props, "font-style", state);
2003 gtk_style_properties_unset_property (props, "font-variant", state);
2004 gtk_style_properties_unset_property (props, "font-weight", state);
2005 gtk_style_properties_unset_property (props, "font-size", state);
2009 unset_margin (GtkStyleProperties *props,
2010 GtkStateFlags state)
2012 gtk_style_properties_unset_property (props, "margin-top", state);
2013 gtk_style_properties_unset_property (props, "margin-right", state);
2014 gtk_style_properties_unset_property (props, "margin-bottom", state);
2015 gtk_style_properties_unset_property (props, "margin-left", state);
2019 unset_padding (GtkStyleProperties *props,
2020 GtkStateFlags state)
2022 gtk_style_properties_unset_property (props, "padding-top", state);
2023 gtk_style_properties_unset_property (props, "padding-right", state);
2024 gtk_style_properties_unset_property (props, "padding-bottom", state);
2025 gtk_style_properties_unset_property (props, "padding-left", state);
2029 unset_border_width (GtkStyleProperties *props,
2030 GtkStateFlags state)
2032 gtk_style_properties_unset_property (props, "border-top-width", state);
2033 gtk_style_properties_unset_property (props, "border-right-width", state);
2034 gtk_style_properties_unset_property (props, "border-bottom-width", state);
2035 gtk_style_properties_unset_property (props, "border-left-width", state);
2039 unset_border_radius (GtkStyleProperties *props,
2040 GtkStateFlags state)
2042 gtk_style_properties_unset_property (props, "border-top-right-radius", state);
2043 gtk_style_properties_unset_property (props, "border-bottom-right-radius", state);
2044 gtk_style_properties_unset_property (props, "border-bottom-left-radius", state);
2045 gtk_style_properties_unset_property (props, "border-top-left-radius", state);
2049 unset_border_color (GtkStyleProperties *props,
2050 GtkStateFlags state)
2052 gtk_style_properties_unset_property (props, "border-top-color", state);
2053 gtk_style_properties_unset_property (props, "border-right-color", state);
2054 gtk_style_properties_unset_property (props, "border-bottom-color", state);
2055 gtk_style_properties_unset_property (props, "border-left-color", state);
2059 unset_border_image (GtkStyleProperties *props,
2060 GtkStateFlags state)
2062 gtk_style_properties_unset_property (props, "border-image-source", state);
2063 gtk_style_properties_unset_property (props, "border-image-slice", state);
2064 gtk_style_properties_unset_property (props, "border-image-repeat", state);
2065 gtk_style_properties_unset_property (props, "border-image-width", state);
2068 /*** default values ***/
2071 border_image_width_default_value (GtkStyleProperties *props,
2072 GtkStateFlags state,
2078 border_color_default_value (GtkStyleProperties *props,
2079 GtkStateFlags state,
2082 g_value_unset (value);
2083 gtk_style_properties_get_property (props, "color", state, value);
2089 css_string_funcs_init (void)
2091 if (G_LIKELY (parse_funcs != NULL))
2094 parse_funcs = g_hash_table_new (NULL, NULL);
2095 print_funcs = g_hash_table_new (NULL, NULL);
2097 register_conversion_function (GDK_TYPE_RGBA,
2100 register_conversion_function (GDK_TYPE_COLOR,
2103 register_conversion_function (GTK_TYPE_SYMBOLIC_COLOR,
2104 symbolic_color_value_parse,
2105 symbolic_color_value_print);
2106 register_conversion_function (PANGO_TYPE_FONT_DESCRIPTION,
2107 font_description_value_parse,
2108 font_description_value_print);
2109 register_conversion_function (G_TYPE_BOOLEAN,
2110 boolean_value_parse,
2111 boolean_value_print);
2112 register_conversion_function (G_TYPE_INT,
2115 register_conversion_function (G_TYPE_UINT,
2118 register_conversion_function (G_TYPE_DOUBLE,
2120 double_value_print);
2121 register_conversion_function (G_TYPE_FLOAT,
2124 register_conversion_function (G_TYPE_STRING,
2126 string_value_print);
2127 register_conversion_function (GTK_TYPE_THEMING_ENGINE,
2128 theming_engine_value_parse,
2129 theming_engine_value_print);
2130 register_conversion_function (GTK_TYPE_ANIMATION_DESCRIPTION,
2131 animation_description_value_parse,
2132 animation_description_value_print);
2133 register_conversion_function (GTK_TYPE_BORDER,
2135 border_value_print);
2136 register_conversion_function (GTK_TYPE_GRADIENT,
2137 gradient_value_parse,
2138 gradient_value_print);
2139 register_conversion_function (CAIRO_GOBJECT_TYPE_PATTERN,
2140 pattern_value_parse,
2142 register_conversion_function (GTK_TYPE_BORDER_IMAGE,
2143 border_image_value_parse,
2145 register_conversion_function (GTK_TYPE_CSS_BORDER_IMAGE_REPEAT,
2146 border_image_repeat_value_parse,
2147 border_image_repeat_value_print);
2148 register_conversion_function (GTK_TYPE_SHADOW,
2150 shadow_value_print);
2151 register_conversion_function (G_TYPE_ENUM,
2154 register_conversion_function (G_TYPE_FLAGS,
2160 _gtk_style_property_parse_value (const GtkStyleProperty *property,
2162 GtkCssParser *parser,
2165 GtkStyleParseFunc func;
2167 g_return_val_if_fail (value != NULL, FALSE);
2168 g_return_val_if_fail (parser != NULL, FALSE);
2170 css_string_funcs_init ();
2174 if (_gtk_css_parser_try (parser, "none", TRUE))
2176 /* Insert the default value, so it has an opportunity
2177 * to override other style providers when merged
2179 g_param_value_set_default (property->pspec, value);
2182 else if (property->property_parse_func)
2184 GError *error = NULL;
2188 value_str = _gtk_css_parser_read_value (parser);
2189 if (value_str == NULL)
2192 success = (*property->property_parse_func) (value_str, value, &error);
2199 func = property->parse_func;
2205 func = g_hash_table_lookup (parse_funcs,
2206 GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
2208 func = g_hash_table_lookup (parse_funcs,
2209 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
2213 _gtk_css_parser_error (parser,
2214 "Cannot convert to type '%s'",
2215 g_type_name (G_VALUE_TYPE (value)));
2219 return (*func) (parser, base, value);
2223 _gtk_style_property_print_value (const GtkStyleProperty *property,
2224 const GValue *value,
2227 GtkStylePrintFunc func;
2229 css_string_funcs_init ();
2232 func = property->print_func;
2237 func = g_hash_table_lookup (print_funcs,
2238 GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
2240 func = g_hash_table_lookup (print_funcs,
2241 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
2245 char *s = g_strdup_value_contents (value);
2246 g_string_append (string, s);
2251 func (value, string);
2255 _gtk_style_property_default_value (const GtkStyleProperty *property,
2256 GtkStyleProperties *properties,
2257 GtkStateFlags state,
2260 if (property->default_value_func)
2261 property->default_value_func (properties, state, value);
2262 else if (property->pspec->value_type == GTK_TYPE_THEMING_ENGINE)
2263 g_value_set_object (value, gtk_theming_engine_load (NULL));
2264 else if (property->pspec->value_type == PANGO_TYPE_FONT_DESCRIPTION)
2265 g_value_take_boxed (value, pango_font_description_from_string ("Sans 10"));
2266 else if (property->pspec->value_type == GDK_TYPE_RGBA)
2269 gdk_rgba_parse (&color, "pink");
2270 g_value_set_boxed (value, &color);
2272 else if (property->pspec->value_type == GTK_TYPE_BORDER)
2274 g_value_take_boxed (value, gtk_border_new ());
2277 g_param_value_set_default (property->pspec, value);
2281 _gtk_style_property_is_inherit (const GtkStyleProperty *property)
2283 g_return_val_if_fail (property != NULL, FALSE);
2285 return property->flags & GTK_STYLE_PROPERTY_INHERIT ? TRUE : FALSE;
2289 resolve_color (GtkStyleProperties *props,
2294 /* Resolve symbolic color to GdkRGBA */
2295 if (!gtk_symbolic_color_resolve (g_value_get_boxed (value), props, &color))
2298 /* Store it back, this is where GdkRGBA caching happens */
2299 g_value_unset (value);
2300 g_value_init (value, GDK_TYPE_RGBA);
2301 g_value_set_boxed (value, &color);
2307 resolve_color_rgb (GtkStyleProperties *props,
2310 GdkColor color = { 0 };
2313 if (!gtk_symbolic_color_resolve (g_value_get_boxed (value), props, &rgba))
2316 color.red = rgba.red * 65535. + 0.5;
2317 color.green = rgba.green * 65535. + 0.5;
2318 color.blue = rgba.blue * 65535. + 0.5;
2320 g_value_unset (value);
2321 g_value_init (value, GDK_TYPE_COLOR);
2322 g_value_set_boxed (value, &color);
2328 resolve_gradient (GtkStyleProperties *props,
2331 cairo_pattern_t *gradient;
2333 if (!gtk_gradient_resolve (g_value_get_boxed (value), props, &gradient))
2336 /* Store it back, this is where cairo_pattern_t caching happens */
2337 g_value_unset (value);
2338 g_value_init (value, CAIRO_GOBJECT_TYPE_PATTERN);
2339 g_value_take_boxed (value, gradient);
2345 resolve_shadow (GtkStyleProperties *props,
2348 GtkShadow *resolved, *base;
2350 base = g_value_get_boxed (value);
2355 if (_gtk_shadow_get_resolved (base))
2358 resolved = _gtk_shadow_resolve (base, props);
2359 if (resolved == NULL)
2362 g_value_take_boxed (value, resolved);
2368 _gtk_style_property_resolve (const GtkStyleProperty *property,
2369 GtkStyleProperties *props,
2370 GtkStateFlags state,
2373 if (G_VALUE_TYPE (val) == GTK_TYPE_SYMBOLIC_COLOR)
2375 if (property->pspec->value_type == GDK_TYPE_RGBA)
2377 if (resolve_color (props, val))
2380 else if (property->pspec->value_type == GDK_TYPE_COLOR)
2382 if (resolve_color_rgb (props, val))
2386 g_value_unset (val);
2387 g_value_init (val, property->pspec->value_type);
2388 _gtk_style_property_default_value (property, props, state, val);
2390 else if (G_VALUE_TYPE (val) == GDK_TYPE_RGBA)
2392 if (g_value_get_boxed (val) == NULL)
2393 _gtk_style_property_default_value (property, props, state, val);
2395 else if (G_VALUE_TYPE (val) == GTK_TYPE_GRADIENT)
2397 g_return_if_fail (property->pspec->value_type == CAIRO_GOBJECT_TYPE_PATTERN);
2399 if (!resolve_gradient (props, val))
2401 g_value_unset (val);
2402 g_value_init (val, CAIRO_GOBJECT_TYPE_PATTERN);
2403 _gtk_style_property_default_value (property, props, state, val);
2406 else if (G_VALUE_TYPE (val) == GTK_TYPE_SHADOW)
2408 if (!resolve_shadow (props, val))
2409 _gtk_style_property_default_value (property, props, state, val);
2414 _gtk_style_property_is_shorthand (const GtkStyleProperty *property)
2416 g_return_val_if_fail (property != NULL, FALSE);
2418 return property->pack_func != NULL;
2422 _gtk_style_property_unpack (const GtkStyleProperty *property,
2423 const GValue *value,
2426 g_return_val_if_fail (property != NULL, NULL);
2427 g_return_val_if_fail (property->unpack_func != NULL, NULL);
2428 g_return_val_if_fail (value != NULL, NULL);
2429 g_return_val_if_fail (n_params != NULL, NULL);
2431 return property->unpack_func (value, n_params);
2435 _gtk_style_property_pack (const GtkStyleProperty *property,
2436 GtkStyleProperties *props,
2437 GtkStateFlags state,
2440 g_return_if_fail (property != NULL);
2441 g_return_if_fail (property->pack_func != NULL);
2442 g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
2443 g_return_if_fail (G_IS_VALUE (value));
2445 property->pack_func (value, props, state);
2449 gtk_style_property_init (void)
2451 if (G_LIKELY (properties))
2454 /* stuff is never freed, so no need for free functions */
2455 properties = g_hash_table_new (g_str_hash, g_str_equal);
2457 /* note that gtk_style_properties_register_property() calls this function,
2458 * so make sure we're sanely inited to avoid infloops */
2460 _gtk_style_property_register (g_param_spec_boxed ("color",
2464 GTK_STYLE_PROPERTY_INHERIT,
2473 gtk_style_properties_register_property (NULL,
2474 g_param_spec_boxed ("background-color",
2479 _gtk_style_property_register (g_param_spec_boxed ("font-family",
2483 GTK_STYLE_PROPERTY_INHERIT,
2488 font_family_value_print,
2491 _gtk_style_property_register (g_param_spec_enum ("font-style",
2495 PANGO_STYLE_NORMAL, 0),
2496 GTK_STYLE_PROPERTY_INHERIT,
2504 _gtk_style_property_register (g_param_spec_enum ("font-variant",
2508 PANGO_VARIANT_NORMAL, 0),
2509 GTK_STYLE_PROPERTY_INHERIT,
2517 /* xxx: need to parse this properly, ie parse the numbers */
2518 _gtk_style_property_register (g_param_spec_enum ("font-weight",
2522 PANGO_WEIGHT_NORMAL, 0),
2523 GTK_STYLE_PROPERTY_INHERIT,
2531 _gtk_style_property_register (g_param_spec_double ("font-size",
2534 0, G_MAXDOUBLE, 0, 0),
2535 GTK_STYLE_PROPERTY_INHERIT,
2543 _gtk_style_property_register (g_param_spec_boxed ("font",
2546 PANGO_TYPE_FONT_DESCRIPTION, 0),
2547 GTK_STYLE_PROPERTY_INHERIT,
2549 unpack_font_description,
2550 pack_font_description,
2551 font_description_value_parse,
2552 font_description_value_print,
2554 unset_font_description);
2556 _gtk_style_property_register (g_param_spec_boxed ("text-shadow",
2559 GTK_TYPE_SHADOW, 0),
2560 GTK_STYLE_PROPERTY_INHERIT,
2569 _gtk_style_property_register (g_param_spec_boxed ("icon-shadow",
2572 GTK_TYPE_SHADOW, 0),
2573 GTK_STYLE_PROPERTY_INHERIT,
2582 gtk_style_properties_register_property (NULL,
2583 g_param_spec_boxed ("box-shadow",
2586 GTK_TYPE_SHADOW, 0));
2587 gtk_style_properties_register_property (NULL,
2588 g_param_spec_int ("margin-top",
2591 0, G_MAXINT, 0, 0));
2592 gtk_style_properties_register_property (NULL,
2593 g_param_spec_int ("margin-left",
2596 0, G_MAXINT, 0, 0));
2597 gtk_style_properties_register_property (NULL,
2598 g_param_spec_int ("margin-bottom",
2601 0, G_MAXINT, 0, 0));
2602 gtk_style_properties_register_property (NULL,
2603 g_param_spec_int ("margin-right",
2606 0, G_MAXINT, 0, 0));
2607 _gtk_style_property_register (g_param_spec_boxed ("margin",
2610 GTK_TYPE_BORDER, 0),
2619 gtk_style_properties_register_property (NULL,
2620 g_param_spec_int ("padding-top",
2623 0, G_MAXINT, 0, 0));
2624 gtk_style_properties_register_property (NULL,
2625 g_param_spec_int ("padding-left",
2628 0, G_MAXINT, 0, 0));
2629 gtk_style_properties_register_property (NULL,
2630 g_param_spec_int ("padding-bottom",
2632 "Padding at bottom",
2633 0, G_MAXINT, 0, 0));
2634 gtk_style_properties_register_property (NULL,
2635 g_param_spec_int ("padding-right",
2638 0, G_MAXINT, 0, 0));
2639 _gtk_style_property_register (g_param_spec_boxed ("padding",
2642 GTK_TYPE_BORDER, 0),
2651 gtk_style_properties_register_property (NULL,
2652 g_param_spec_int ("border-top-width",
2654 "Border width at top",
2655 0, G_MAXINT, 0, 0));
2656 gtk_style_properties_register_property (NULL,
2657 g_param_spec_int ("border-left-width",
2658 "border left width",
2659 "Border width at left",
2660 0, G_MAXINT, 0, 0));
2661 gtk_style_properties_register_property (NULL,
2662 g_param_spec_int ("border-bottom-width",
2663 "border bottom width",
2664 "Border width at bottom",
2665 0, G_MAXINT, 0, 0));
2666 gtk_style_properties_register_property (NULL,
2667 g_param_spec_int ("border-right-width",
2668 "border right width",
2669 "Border width at right",
2670 0, G_MAXINT, 0, 0));
2671 _gtk_style_property_register (g_param_spec_boxed ("border-width",
2673 "Border width, in pixels",
2674 GTK_TYPE_BORDER, 0),
2677 unpack_border_width,
2682 unset_border_width);
2684 _gtk_style_property_register (g_param_spec_boxed ("border-top-left-radius",
2685 "Border top left radius",
2686 "Border radius of top left corner, in pixels",
2687 GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
2692 border_corner_radius_value_parse,
2693 border_corner_radius_value_print,
2696 _gtk_style_property_register (g_param_spec_boxed ("border-top-right-radius",
2697 "Border top right radius",
2698 "Border radius of top right corner, in pixels",
2699 GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
2704 border_corner_radius_value_parse,
2705 border_corner_radius_value_print,
2708 _gtk_style_property_register (g_param_spec_boxed ("border-bottom-right-radius",
2709 "Border bottom right radius",
2710 "Border radius of bottom right corner, in pixels",
2711 GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
2716 border_corner_radius_value_parse,
2717 border_corner_radius_value_print,
2720 _gtk_style_property_register (g_param_spec_boxed ("border-bottom-left-radius",
2721 "Border bottom left radius",
2722 "Border radius of bottom left corner, in pixels",
2723 GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
2728 border_corner_radius_value_parse,
2729 border_corner_radius_value_print,
2732 _gtk_style_property_register (g_param_spec_int ("border-radius",
2734 "Border radius, in pixels",
2738 unpack_border_radius,
2740 border_radius_value_parse,
2741 border_radius_value_print,
2743 unset_border_radius);
2745 gtk_style_properties_register_property (NULL,
2746 g_param_spec_enum ("border-style",
2749 GTK_TYPE_BORDER_STYLE,
2750 GTK_BORDER_STYLE_NONE, 0));
2751 _gtk_style_property_register (g_param_spec_boxed ("border-top-color",
2759 border_color_value_parse,
2761 border_color_default_value,
2763 _gtk_style_property_register (g_param_spec_boxed ("border-right-color",
2764 "Border right color",
2765 "Border right color",
2771 border_color_value_parse,
2773 border_color_default_value,
2775 _gtk_style_property_register (g_param_spec_boxed ("border-bottom-color",
2776 "Border bottom color",
2777 "Border bottom color",
2783 border_color_value_parse,
2785 border_color_default_value,
2787 _gtk_style_property_register (g_param_spec_boxed ("border-left-color",
2788 "Border left color",
2789 "Border left color",
2795 border_color_value_parse,
2797 border_color_default_value,
2799 _gtk_style_property_register (g_param_spec_boxed ("border-color",
2805 unpack_border_color,
2807 border_color_shorthand_value_parse,
2810 unset_border_color);
2812 gtk_style_properties_register_property (NULL,
2813 g_param_spec_boxed ("background-image",
2816 CAIRO_GOBJECT_TYPE_PATTERN, 0));
2817 gtk_style_properties_register_property (NULL,
2818 g_param_spec_boxed ("border-image-source",
2819 "Border image source",
2820 "Border image source",
2821 CAIRO_GOBJECT_TYPE_PATTERN, 0));
2822 gtk_style_properties_register_property (NULL,
2823 g_param_spec_boxed ("border-image-repeat",
2824 "Border image repeat",
2825 "Border image repeat",
2826 GTK_TYPE_CSS_BORDER_IMAGE_REPEAT, 0));
2827 gtk_style_properties_register_property (NULL,
2828 g_param_spec_boxed ("border-image-slice",
2829 "Border image slice",
2830 "Border image slice",
2831 GTK_TYPE_BORDER, 0));
2832 _gtk_style_property_register (g_param_spec_boxed ("border-image-width",
2833 "Border image width",
2834 "Border image width",
2835 GTK_TYPE_BORDER, 0),
2842 border_image_width_default_value,
2844 _gtk_style_property_register (g_param_spec_boxed ("border-image",
2847 GTK_TYPE_BORDER_IMAGE, 0),
2850 _gtk_border_image_unpack,
2851 _gtk_border_image_pack,
2855 unset_border_image);
2856 gtk_style_properties_register_property (NULL,
2857 g_param_spec_object ("engine",
2860 GTK_TYPE_THEMING_ENGINE, 0));
2861 gtk_style_properties_register_property (NULL,
2862 g_param_spec_boxed ("transition",
2863 "Transition animation description",
2864 "Transition animation description",
2865 GTK_TYPE_ANIMATION_DESCRIPTION, 0));
2867 /* Private property holding the binding sets */
2868 _gtk_style_property_register (g_param_spec_boxed ("gtk-key-bindings",
2871 G_TYPE_PTR_ARRAY, 0),
2876 bindings_value_parse,
2877 bindings_value_print,
2882 const GtkStyleProperty *
2883 _gtk_style_property_lookup (const char *name)
2885 gtk_style_property_init ();
2887 return g_hash_table_lookup (properties, name);
2891 _gtk_style_property_register (GParamSpec *pspec,
2892 GtkStylePropertyFlags flags,
2893 GtkStylePropertyParser property_parse_func,
2894 GtkStyleUnpackFunc unpack_func,
2895 GtkStylePackFunc pack_func,
2896 GtkStyleParseFunc parse_func,
2897 GtkStylePrintFunc print_func,
2898 GtkStyleDefaultValueFunc default_value_func,
2899 GtkStyleUnsetFunc unset_func)
2901 const GtkStyleProperty *existing;
2902 GtkStyleProperty *node;
2904 g_return_if_fail ((pack_func == NULL) == (unpack_func == NULL));
2906 gtk_style_property_init ();
2908 existing = _gtk_style_property_lookup (pspec->name);
2909 if (existing != NULL)
2911 g_warning ("Property \"%s\" was already registered with type %s",
2912 pspec->name, g_type_name (existing->pspec->value_type));
2916 node = g_slice_new0 (GtkStyleProperty);
2917 node->flags = flags;
2918 node->pspec = pspec;
2919 node->property_parse_func = property_parse_func;
2920 node->pack_func = pack_func;
2921 node->unpack_func = unpack_func;
2922 node->parse_func = parse_func;
2923 node->print_func = print_func;
2924 node->default_value_func = default_value_func;
2925 node->unset_func = unset_func;
2927 g_hash_table_insert (properties, pspec->name, node);