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 /*** default values ***/
1998 border_image_width_default_value (GtkStyleProperties *props,
1999 GtkStateFlags state,
2005 border_color_default_value (GtkStyleProperties *props,
2006 GtkStateFlags state,
2009 g_value_unset (value);
2010 gtk_style_properties_get_property (props, "color", state, value);
2016 css_string_funcs_init (void)
2018 if (G_LIKELY (parse_funcs != NULL))
2021 parse_funcs = g_hash_table_new (NULL, NULL);
2022 print_funcs = g_hash_table_new (NULL, NULL);
2024 register_conversion_function (GDK_TYPE_RGBA,
2027 register_conversion_function (GDK_TYPE_COLOR,
2030 register_conversion_function (GTK_TYPE_SYMBOLIC_COLOR,
2031 symbolic_color_value_parse,
2032 symbolic_color_value_print);
2033 register_conversion_function (PANGO_TYPE_FONT_DESCRIPTION,
2034 font_description_value_parse,
2035 font_description_value_print);
2036 register_conversion_function (G_TYPE_BOOLEAN,
2037 boolean_value_parse,
2038 boolean_value_print);
2039 register_conversion_function (G_TYPE_INT,
2042 register_conversion_function (G_TYPE_UINT,
2045 register_conversion_function (G_TYPE_DOUBLE,
2047 double_value_print);
2048 register_conversion_function (G_TYPE_FLOAT,
2051 register_conversion_function (G_TYPE_STRING,
2053 string_value_print);
2054 register_conversion_function (GTK_TYPE_THEMING_ENGINE,
2055 theming_engine_value_parse,
2056 theming_engine_value_print);
2057 register_conversion_function (GTK_TYPE_ANIMATION_DESCRIPTION,
2058 animation_description_value_parse,
2059 animation_description_value_print);
2060 register_conversion_function (GTK_TYPE_BORDER,
2062 border_value_print);
2063 register_conversion_function (GTK_TYPE_GRADIENT,
2064 gradient_value_parse,
2065 gradient_value_print);
2066 register_conversion_function (CAIRO_GOBJECT_TYPE_PATTERN,
2067 pattern_value_parse,
2069 register_conversion_function (GTK_TYPE_BORDER_IMAGE,
2070 border_image_value_parse,
2072 register_conversion_function (GTK_TYPE_CSS_BORDER_IMAGE_REPEAT,
2073 border_image_repeat_value_parse,
2074 border_image_repeat_value_print);
2075 register_conversion_function (GTK_TYPE_SHADOW,
2077 shadow_value_print);
2078 register_conversion_function (G_TYPE_ENUM,
2081 register_conversion_function (G_TYPE_FLAGS,
2087 _gtk_style_property_parse_value (const GtkStyleProperty *property,
2089 GtkCssParser *parser,
2092 GtkStyleParseFunc func;
2094 g_return_val_if_fail (value != NULL, FALSE);
2095 g_return_val_if_fail (parser != NULL, FALSE);
2097 css_string_funcs_init ();
2101 if (_gtk_css_parser_try (parser, "none", TRUE))
2103 /* Insert the default value, so it has an opportunity
2104 * to override other style providers when merged
2106 g_param_value_set_default (property->pspec, value);
2109 else if (property->property_parse_func)
2111 GError *error = NULL;
2115 value_str = _gtk_css_parser_read_value (parser);
2116 if (value_str == NULL)
2119 success = (*property->property_parse_func) (value_str, value, &error);
2126 func = property->parse_func;
2132 func = g_hash_table_lookup (parse_funcs,
2133 GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
2135 func = g_hash_table_lookup (parse_funcs,
2136 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
2140 _gtk_css_parser_error (parser,
2141 "Cannot convert to type '%s'",
2142 g_type_name (G_VALUE_TYPE (value)));
2146 return (*func) (parser, base, value);
2150 _gtk_style_property_print_value (const GtkStyleProperty *property,
2151 const GValue *value,
2154 GtkStylePrintFunc func;
2156 css_string_funcs_init ();
2159 func = property->print_func;
2164 func = g_hash_table_lookup (print_funcs,
2165 GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
2167 func = g_hash_table_lookup (print_funcs,
2168 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
2172 char *s = g_strdup_value_contents (value);
2173 g_string_append (string, s);
2178 func (value, string);
2182 _gtk_style_property_default_value (const GtkStyleProperty *property,
2183 GtkStyleProperties *properties,
2184 GtkStateFlags state,
2187 if (property->default_value_func)
2188 property->default_value_func (properties, state, value);
2189 else if (property->pspec->value_type == GTK_TYPE_THEMING_ENGINE)
2190 g_value_set_object (value, gtk_theming_engine_load (NULL));
2191 else if (property->pspec->value_type == PANGO_TYPE_FONT_DESCRIPTION)
2192 g_value_take_boxed (value, pango_font_description_from_string ("Sans 10"));
2193 else if (property->pspec->value_type == GDK_TYPE_RGBA)
2196 gdk_rgba_parse (&color, "pink");
2197 g_value_set_boxed (value, &color);
2199 else if (property->pspec->value_type == GTK_TYPE_BORDER)
2201 g_value_take_boxed (value, gtk_border_new ());
2204 g_param_value_set_default (property->pspec, value);
2208 _gtk_style_property_is_inherit (const GtkStyleProperty *property)
2210 g_return_val_if_fail (property != NULL, FALSE);
2212 return property->flags & GTK_STYLE_PROPERTY_INHERIT ? TRUE : FALSE;
2216 resolve_color (GtkStyleProperties *props,
2221 /* Resolve symbolic color to GdkRGBA */
2222 if (!gtk_symbolic_color_resolve (g_value_get_boxed (value), props, &color))
2225 /* Store it back, this is where GdkRGBA caching happens */
2226 g_value_unset (value);
2227 g_value_init (value, GDK_TYPE_RGBA);
2228 g_value_set_boxed (value, &color);
2234 resolve_color_rgb (GtkStyleProperties *props,
2237 GdkColor color = { 0 };
2240 if (!gtk_symbolic_color_resolve (g_value_get_boxed (value), props, &rgba))
2243 color.red = rgba.red * 65535. + 0.5;
2244 color.green = rgba.green * 65535. + 0.5;
2245 color.blue = rgba.blue * 65535. + 0.5;
2247 g_value_unset (value);
2248 g_value_init (value, GDK_TYPE_COLOR);
2249 g_value_set_boxed (value, &color);
2255 resolve_gradient (GtkStyleProperties *props,
2258 cairo_pattern_t *gradient;
2260 if (!gtk_gradient_resolve (g_value_get_boxed (value), props, &gradient))
2263 /* Store it back, this is where cairo_pattern_t caching happens */
2264 g_value_unset (value);
2265 g_value_init (value, CAIRO_GOBJECT_TYPE_PATTERN);
2266 g_value_take_boxed (value, gradient);
2272 resolve_shadow (GtkStyleProperties *props,
2275 GtkShadow *resolved, *base;
2277 base = g_value_get_boxed (value);
2282 if (_gtk_shadow_get_resolved (base))
2285 resolved = _gtk_shadow_resolve (base, props);
2286 if (resolved == NULL)
2289 g_value_take_boxed (value, resolved);
2295 _gtk_style_property_resolve (const GtkStyleProperty *property,
2296 GtkStyleProperties *props,
2297 GtkStateFlags state,
2300 if (G_VALUE_TYPE (val) == GTK_TYPE_SYMBOLIC_COLOR)
2302 if (property->pspec->value_type == GDK_TYPE_RGBA)
2304 if (resolve_color (props, val))
2307 else if (property->pspec->value_type == GDK_TYPE_COLOR)
2309 if (resolve_color_rgb (props, val))
2313 g_value_unset (val);
2314 g_value_init (val, property->pspec->value_type);
2315 _gtk_style_property_default_value (property, props, state, val);
2317 else if (G_VALUE_TYPE (val) == GDK_TYPE_RGBA)
2319 if (g_value_get_boxed (val) == NULL)
2320 _gtk_style_property_default_value (property, props, state, val);
2322 else if (G_VALUE_TYPE (val) == GTK_TYPE_GRADIENT)
2324 g_return_if_fail (property->pspec->value_type == CAIRO_GOBJECT_TYPE_PATTERN);
2326 if (!resolve_gradient (props, val))
2328 g_value_unset (val);
2329 g_value_init (val, CAIRO_GOBJECT_TYPE_PATTERN);
2330 _gtk_style_property_default_value (property, props, state, val);
2333 else if (G_VALUE_TYPE (val) == GTK_TYPE_SHADOW)
2335 if (!resolve_shadow (props, val))
2336 _gtk_style_property_default_value (property, props, state, val);
2341 _gtk_style_property_is_shorthand (const GtkStyleProperty *property)
2343 g_return_val_if_fail (property != NULL, FALSE);
2345 return property->pack_func != NULL;
2349 _gtk_style_property_unpack (const GtkStyleProperty *property,
2350 const GValue *value,
2353 g_return_val_if_fail (property != NULL, NULL);
2354 g_return_val_if_fail (property->unpack_func != NULL, NULL);
2355 g_return_val_if_fail (value != NULL, NULL);
2356 g_return_val_if_fail (n_params != NULL, NULL);
2358 return property->unpack_func (value, n_params);
2362 _gtk_style_property_pack (const GtkStyleProperty *property,
2363 GtkStyleProperties *props,
2364 GtkStateFlags state,
2367 g_return_if_fail (property != NULL);
2368 g_return_if_fail (property->pack_func != NULL);
2369 g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
2370 g_return_if_fail (G_IS_VALUE (value));
2372 property->pack_func (value, props, state);
2376 gtk_style_property_init (void)
2378 if (G_LIKELY (properties))
2381 /* stuff is never freed, so no need for free functions */
2382 properties = g_hash_table_new (g_str_hash, g_str_equal);
2384 /* note that gtk_style_properties_register_property() calls this function,
2385 * so make sure we're sanely inited to avoid infloops */
2387 _gtk_style_property_register (g_param_spec_boxed ("color",
2391 GTK_STYLE_PROPERTY_INHERIT,
2399 gtk_style_properties_register_property (NULL,
2400 g_param_spec_boxed ("background-color",
2405 _gtk_style_property_register (g_param_spec_boxed ("font-family",
2409 GTK_STYLE_PROPERTY_INHERIT,
2414 font_family_value_print,
2416 _gtk_style_property_register (g_param_spec_enum ("font-style",
2420 PANGO_STYLE_NORMAL, 0),
2421 GTK_STYLE_PROPERTY_INHERIT,
2428 _gtk_style_property_register (g_param_spec_enum ("font-variant",
2432 PANGO_VARIANT_NORMAL, 0),
2433 GTK_STYLE_PROPERTY_INHERIT,
2440 /* xxx: need to parse this properly, ie parse the numbers */
2441 _gtk_style_property_register (g_param_spec_enum ("font-weight",
2445 PANGO_WEIGHT_NORMAL, 0),
2446 GTK_STYLE_PROPERTY_INHERIT,
2453 _gtk_style_property_register (g_param_spec_double ("font-size",
2456 0, G_MAXDOUBLE, 0, 0),
2457 GTK_STYLE_PROPERTY_INHERIT,
2464 _gtk_style_property_register (g_param_spec_boxed ("font",
2467 PANGO_TYPE_FONT_DESCRIPTION, 0),
2468 GTK_STYLE_PROPERTY_INHERIT,
2470 unpack_font_description,
2471 pack_font_description,
2472 font_description_value_parse,
2473 font_description_value_print,
2476 _gtk_style_property_register (g_param_spec_boxed ("text-shadow",
2479 GTK_TYPE_SHADOW, 0),
2480 GTK_STYLE_PROPERTY_INHERIT,
2488 _gtk_style_property_register (g_param_spec_boxed ("icon-shadow",
2491 GTK_TYPE_SHADOW, 0),
2492 GTK_STYLE_PROPERTY_INHERIT,
2500 gtk_style_properties_register_property (NULL,
2501 g_param_spec_boxed ("box-shadow",
2504 GTK_TYPE_SHADOW, 0));
2505 gtk_style_properties_register_property (NULL,
2506 g_param_spec_int ("margin-top",
2509 0, G_MAXINT, 0, 0));
2510 gtk_style_properties_register_property (NULL,
2511 g_param_spec_int ("margin-left",
2514 0, G_MAXINT, 0, 0));
2515 gtk_style_properties_register_property (NULL,
2516 g_param_spec_int ("margin-bottom",
2519 0, G_MAXINT, 0, 0));
2520 gtk_style_properties_register_property (NULL,
2521 g_param_spec_int ("margin-right",
2524 0, G_MAXINT, 0, 0));
2525 _gtk_style_property_register (g_param_spec_boxed ("margin",
2528 GTK_TYPE_BORDER, 0),
2536 gtk_style_properties_register_property (NULL,
2537 g_param_spec_int ("padding-top",
2540 0, G_MAXINT, 0, 0));
2541 gtk_style_properties_register_property (NULL,
2542 g_param_spec_int ("padding-left",
2545 0, G_MAXINT, 0, 0));
2546 gtk_style_properties_register_property (NULL,
2547 g_param_spec_int ("padding-bottom",
2549 "Padding at bottom",
2550 0, G_MAXINT, 0, 0));
2551 gtk_style_properties_register_property (NULL,
2552 g_param_spec_int ("padding-right",
2555 0, G_MAXINT, 0, 0));
2556 _gtk_style_property_register (g_param_spec_boxed ("padding",
2559 GTK_TYPE_BORDER, 0),
2567 gtk_style_properties_register_property (NULL,
2568 g_param_spec_int ("border-top-width",
2570 "Border width at top",
2571 0, G_MAXINT, 0, 0));
2572 gtk_style_properties_register_property (NULL,
2573 g_param_spec_int ("border-left-width",
2574 "border left width",
2575 "Border width at left",
2576 0, G_MAXINT, 0, 0));
2577 gtk_style_properties_register_property (NULL,
2578 g_param_spec_int ("border-bottom-width",
2579 "border bottom width",
2580 "Border width at bottom",
2581 0, G_MAXINT, 0, 0));
2582 gtk_style_properties_register_property (NULL,
2583 g_param_spec_int ("border-right-width",
2584 "border right width",
2585 "Border width at right",
2586 0, G_MAXINT, 0, 0));
2587 _gtk_style_property_register (g_param_spec_boxed ("border-width",
2589 "Border width, in pixels",
2590 GTK_TYPE_BORDER, 0),
2593 unpack_border_width,
2599 _gtk_style_property_register (g_param_spec_boxed ("border-top-left-radius",
2600 "Border top left radius",
2601 "Border radius of top left corner, in pixels",
2602 GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
2607 border_corner_radius_value_parse,
2608 border_corner_radius_value_print,
2610 _gtk_style_property_register (g_param_spec_boxed ("border-top-right-radius",
2611 "Border top right radius",
2612 "Border radius of top right corner, in pixels",
2613 GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
2618 border_corner_radius_value_parse,
2619 border_corner_radius_value_print,
2621 _gtk_style_property_register (g_param_spec_boxed ("border-bottom-right-radius",
2622 "Border bottom right radius",
2623 "Border radius of bottom right corner, in pixels",
2624 GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
2629 border_corner_radius_value_parse,
2630 border_corner_radius_value_print,
2632 _gtk_style_property_register (g_param_spec_boxed ("border-bottom-left-radius",
2633 "Border bottom left radius",
2634 "Border radius of bottom left corner, in pixels",
2635 GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
2640 border_corner_radius_value_parse,
2641 border_corner_radius_value_print,
2643 _gtk_style_property_register (g_param_spec_int ("border-radius",
2645 "Border radius, in pixels",
2649 unpack_border_radius,
2651 border_radius_value_parse,
2652 border_radius_value_print,
2655 gtk_style_properties_register_property (NULL,
2656 g_param_spec_enum ("border-style",
2659 GTK_TYPE_BORDER_STYLE,
2660 GTK_BORDER_STYLE_NONE, 0));
2661 _gtk_style_property_register (g_param_spec_boxed ("border-top-color",
2669 border_color_value_parse,
2671 border_color_default_value);
2672 _gtk_style_property_register (g_param_spec_boxed ("border-right-color",
2673 "Border right color",
2674 "Border right color",
2680 border_color_value_parse,
2682 border_color_default_value);
2683 _gtk_style_property_register (g_param_spec_boxed ("border-bottom-color",
2684 "Border bottom color",
2685 "Border bottom color",
2691 border_color_value_parse,
2693 border_color_default_value);
2694 _gtk_style_property_register (g_param_spec_boxed ("border-left-color",
2695 "Border left color",
2696 "Border left color",
2702 border_color_value_parse,
2704 border_color_default_value);
2705 _gtk_style_property_register (g_param_spec_boxed ("border-color",
2711 unpack_border_color,
2713 border_color_shorthand_value_parse,
2717 gtk_style_properties_register_property (NULL,
2718 g_param_spec_boxed ("background-image",
2721 CAIRO_GOBJECT_TYPE_PATTERN, 0));
2722 gtk_style_properties_register_property (NULL,
2723 g_param_spec_boxed ("border-image-source",
2724 "Border image source",
2725 "Border image source",
2726 CAIRO_GOBJECT_TYPE_PATTERN, 0));
2727 gtk_style_properties_register_property (NULL,
2728 g_param_spec_boxed ("border-image-repeat",
2729 "Border image repeat",
2730 "Border image repeat",
2731 GTK_TYPE_CSS_BORDER_IMAGE_REPEAT, 0));
2732 gtk_style_properties_register_property (NULL,
2733 g_param_spec_boxed ("border-image-slice",
2734 "Border image slice",
2735 "Border image slice",
2736 GTK_TYPE_BORDER, 0));
2737 _gtk_style_property_register (g_param_spec_boxed ("border-image-width",
2738 "Border image width",
2739 "Border image width",
2740 GTK_TYPE_BORDER, 0),
2747 border_image_width_default_value);
2748 _gtk_style_property_register (g_param_spec_boxed ("border-image",
2751 GTK_TYPE_BORDER_IMAGE, 0),
2754 _gtk_border_image_unpack,
2755 _gtk_border_image_pack,
2759 gtk_style_properties_register_property (NULL,
2760 g_param_spec_object ("engine",
2763 GTK_TYPE_THEMING_ENGINE, 0));
2764 gtk_style_properties_register_property (NULL,
2765 g_param_spec_boxed ("transition",
2766 "Transition animation description",
2767 "Transition animation description",
2768 GTK_TYPE_ANIMATION_DESCRIPTION, 0));
2770 /* Private property holding the binding sets */
2771 _gtk_style_property_register (g_param_spec_boxed ("gtk-key-bindings",
2774 G_TYPE_PTR_ARRAY, 0),
2779 bindings_value_parse,
2780 bindings_value_print,
2784 const GtkStyleProperty *
2785 _gtk_style_property_lookup (const char *name)
2787 gtk_style_property_init ();
2789 return g_hash_table_lookup (properties, name);
2793 _gtk_style_property_register (GParamSpec *pspec,
2794 GtkStylePropertyFlags flags,
2795 GtkStylePropertyParser property_parse_func,
2796 GtkStyleUnpackFunc unpack_func,
2797 GtkStylePackFunc pack_func,
2798 GtkStyleParseFunc parse_func,
2799 GtkStylePrintFunc print_func,
2800 GtkStyleDefaultValueFunc default_value_func)
2802 const GtkStyleProperty *existing;
2803 GtkStyleProperty *node;
2805 g_return_if_fail ((pack_func == NULL) == (unpack_func == NULL));
2807 gtk_style_property_init ();
2809 existing = _gtk_style_property_lookup (pspec->name);
2810 if (existing != NULL)
2812 g_warning ("Property \"%s\" was already registered with type %s",
2813 pspec->name, g_type_name (existing->pspec->value_type));
2817 node = g_slice_new0 (GtkStyleProperty);
2818 node->flags = flags;
2819 node->pspec = pspec;
2820 node->property_parse_func = property_parse_func;
2821 node->pack_func = pack_func;
2822 node->unpack_func = unpack_func;
2823 node->parse_func = parse_func;
2824 node->print_func = print_func;
2825 node->default_value_func = default_value_func;
2827 g_hash_table_insert (properties, pspec->name, node);