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"
28 #include <gdk-pixbuf/gdk-pixbuf.h>
29 #include <cairo-gobject.h>
31 #include "gtkcssprovider.h"
32 #include "gtkcssparserprivate.h"
34 /* the actual parsers we have */
35 #include "gtkanimationdescription.h"
36 #include "gtkbindings.h"
37 #include "gtk9slice.h"
38 #include "gtkgradient.h"
39 #include "gtkshadowprivate.h"
40 #include "gtkthemingengine.h"
41 #include "gtktypebuiltins.h"
43 typedef gboolean (* ParseFunc) (GtkCssParser *parser,
46 typedef void (* PrintFunc) (const GValue *value,
49 static GHashTable *parse_funcs = NULL;
50 static GHashTable *print_funcs = NULL;
51 static GHashTable *properties = NULL;
54 register_conversion_function (GType type,
59 g_hash_table_insert (parse_funcs, GSIZE_TO_POINTER (type), parse);
61 g_hash_table_insert (print_funcs, GSIZE_TO_POINTER (type), print);
64 /*** IMPLEMENTATIONS ***/
67 rgba_value_parse (GtkCssParser *parser,
71 GtkSymbolicColor *symbolic;
74 symbolic = _gtk_css_parser_read_symbolic_color (parser);
78 if (gtk_symbolic_color_resolve (symbolic, NULL, &rgba))
80 g_value_set_boxed (value, &rgba);
81 gtk_symbolic_color_unref (symbolic);
85 g_value_unset (value);
86 g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
87 g_value_take_boxed (value, symbolic);
94 rgba_value_print (const GValue *value,
97 const GdkRGBA *rgba = g_value_get_boxed (value);
100 g_string_append (string, "none");
103 char *s = gdk_rgba_to_string (rgba);
104 g_string_append (string, s);
110 color_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))
125 color.red = rgba.red * 65535. + 0.5;
126 color.green = rgba.green * 65535. + 0.5;
127 color.blue = rgba.blue * 65535. + 0.5;
129 g_value_set_boxed (value, &color);
133 g_value_unset (value);
134 g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
135 g_value_take_boxed (value, symbolic);
142 color_value_print (const GValue *value,
145 const GdkColor *color = g_value_get_boxed (value);
148 g_string_append (string, "none");
151 char *s = gdk_color_to_string (color);
152 g_string_append (string, s);
158 symbolic_color_value_parse (GtkCssParser *parser,
162 GtkSymbolicColor *symbolic;
164 symbolic = _gtk_css_parser_read_symbolic_color (parser);
165 if (symbolic == NULL)
168 g_value_take_boxed (value, symbolic);
173 symbolic_color_value_print (const GValue *value,
176 GtkSymbolicColor *symbolic = g_value_get_boxed (value);
178 if (symbolic == NULL)
179 g_string_append (string, "none");
182 char *s = gtk_symbolic_color_to_string (symbolic);
183 g_string_append (string, s);
189 font_description_value_parse (GtkCssParser *parser,
193 PangoFontDescription *font_desc;
196 str = _gtk_css_parser_read_value (parser);
200 font_desc = pango_font_description_from_string (str);
202 g_value_take_boxed (value, font_desc);
207 font_description_value_print (const GValue *value,
210 const PangoFontDescription *desc = g_value_get_boxed (value);
213 g_string_append (string, "none");
216 char *s = pango_font_description_to_string (desc);
217 g_string_append (string, s);
223 boolean_value_parse (GtkCssParser *parser,
227 if (_gtk_css_parser_try (parser, "true", TRUE) ||
228 _gtk_css_parser_try (parser, "1", TRUE))
230 g_value_set_boolean (value, TRUE);
233 else if (_gtk_css_parser_try (parser, "false", TRUE) ||
234 _gtk_css_parser_try (parser, "0", TRUE))
236 g_value_set_boolean (value, FALSE);
241 _gtk_css_parser_error (parser, "Expected a boolean value");
247 boolean_value_print (const GValue *value,
250 if (g_value_get_boolean (value))
251 g_string_append (string, "true");
253 g_string_append (string, "false");
257 int_value_parse (GtkCssParser *parser,
263 if (!_gtk_css_parser_try_int (parser, &i))
265 _gtk_css_parser_error (parser, "Expected a valid integer value");
269 g_value_set_int (value, i);
274 int_value_print (const GValue *value,
277 g_string_append_printf (string, "%d", g_value_get_int (value));
281 uint_value_parse (GtkCssParser *parser,
287 if (!_gtk_css_parser_try_uint (parser, &u))
289 _gtk_css_parser_error (parser, "Expected a valid unsigned value");
293 g_value_set_uint (value, u);
298 uint_value_print (const GValue *value,
301 g_string_append_printf (string, "%u", g_value_get_uint (value));
305 double_value_parse (GtkCssParser *parser,
311 if (!_gtk_css_parser_try_double (parser, &d))
313 _gtk_css_parser_error (parser, "Expected a number");
317 g_value_set_double (value, d);
322 double_value_print (const GValue *value,
325 char buf[G_ASCII_DTOSTR_BUF_SIZE];
327 g_ascii_dtostr (buf, sizeof (buf), g_value_get_double (value));
328 g_string_append (string, buf);
332 float_value_parse (GtkCssParser *parser,
338 if (!_gtk_css_parser_try_double (parser, &d))
340 _gtk_css_parser_error (parser, "Expected a number");
344 g_value_set_float (value, d);
349 float_value_print (const GValue *value,
352 char buf[G_ASCII_DTOSTR_BUF_SIZE];
354 g_ascii_dtostr (buf, sizeof (buf), g_value_get_float (value));
355 g_string_append (string, buf);
359 string_value_parse (GtkCssParser *parser,
363 char *str = _gtk_css_parser_read_string (parser);
368 g_value_take_string (value, str);
373 string_value_print (const GValue *value,
379 string = g_value_get_string (value);
380 g_string_append_c (str, '"');
383 len = strcspn (string, "\"\n\r\f");
384 g_string_append (str, string);
391 g_string_append (str, "\\A ");
394 g_string_append (str, "\\D ");
397 g_string_append (str, "\\C ");
400 g_string_append (str, "\\\"");
403 g_assert_not_reached ();
408 g_string_append_c (str, '"');
412 theming_engine_value_parse (GtkCssParser *parser,
416 GtkThemingEngine *engine;
419 str = _gtk_css_parser_try_ident (parser, TRUE);
422 _gtk_css_parser_error (parser, "Expected a valid theme name");
426 engine = gtk_theming_engine_load (str);
429 _gtk_css_parser_error (parser, "Themeing engine '%s' not found", str);
434 g_value_set_object (value, engine);
440 theming_engine_value_print (const GValue *value,
443 GtkThemingEngine *engine;
446 engine = g_value_get_object (value);
448 g_string_append (string, "none");
451 /* XXX: gtk_theming_engine_get_name()? */
452 g_object_get (engine, "name", &name, NULL);
453 g_string_append (string, name);
459 animation_description_value_parse (GtkCssParser *parser,
463 GtkAnimationDescription *desc;
466 str = _gtk_css_parser_read_value (parser);
470 desc = _gtk_animation_description_from_string (str);
475 _gtk_css_parser_error (parser, "Invalid animation description");
479 g_value_take_boxed (value, desc);
484 animation_description_value_print (const GValue *value,
487 GtkAnimationDescription *desc = g_value_get_boxed (value);
490 g_string_append (string, "none");
492 _gtk_animation_description_print (desc, string);
496 border_value_parse (GtkCssParser *parser,
500 GtkBorder border = { 0, };
503 for (i = 0; i < G_N_ELEMENTS (numbers); i++)
505 if (!_gtk_css_parser_try_uint (parser, &numbers[i]))
508 /* XXX: shouldn't allow spaces here? */
509 _gtk_css_parser_try (parser, "px", TRUE);
514 _gtk_css_parser_error (parser, "Expected valid border");
518 border.top = numbers[0];
520 border.right = numbers[1];
522 border.right = border.top;
524 border.bottom = numbers[2];
526 border.bottom = border.top;
528 border.left = numbers[3];
530 border.left = border.right;
532 g_value_set_boxed (value, &border);
537 border_value_print (const GValue *value, GString *string)
539 const GtkBorder *border = g_value_get_boxed (value);
542 g_string_append (string, "none");
543 else if (border->left != border->right)
544 g_string_append_printf (string, "%d %d %d %d", border->top, border->right, border->bottom, border->left);
545 else if (border->top != border->bottom)
546 g_string_append_printf (string, "%d %d %d", border->top, border->right, border->bottom);
547 else if (border->top != border->left)
548 g_string_append_printf (string, "%d %d", border->top, border->right);
550 g_string_append_printf (string, "%d", border->top);
554 gradient_value_parse (GtkCssParser *parser,
558 GtkGradient *gradient;
559 cairo_pattern_type_t type;
563 if (!_gtk_css_parser_try (parser, "-gtk-gradient", TRUE))
565 _gtk_css_parser_error (parser,
566 "Expected '-gtk-gradient'");
570 if (!_gtk_css_parser_try (parser, "(", TRUE))
572 _gtk_css_parser_error (parser,
573 "Expected '(' after '-gtk-gradient'");
577 /* Parse gradient type */
578 if (_gtk_css_parser_try (parser, "linear", TRUE))
579 type = CAIRO_PATTERN_TYPE_LINEAR;
580 else if (_gtk_css_parser_try (parser, "radial", TRUE))
581 type = CAIRO_PATTERN_TYPE_RADIAL;
584 _gtk_css_parser_error (parser,
585 "Gradient type must be 'radial' or 'linear'");
589 /* Parse start/stop position parameters */
590 for (i = 0; i < 2; i++)
592 if (! _gtk_css_parser_try (parser, ",", TRUE))
594 _gtk_css_parser_error (parser,
599 if (_gtk_css_parser_try (parser, "left", TRUE))
601 else if (_gtk_css_parser_try (parser, "right", TRUE))
603 else if (_gtk_css_parser_try (parser, "center", TRUE))
605 else if (!_gtk_css_parser_try_double (parser, &coords[i * 3]))
607 _gtk_css_parser_error (parser,
608 "Expected a valid X coordinate");
612 if (_gtk_css_parser_try (parser, "top", TRUE))
613 coords[i * 3 + 1] = 0;
614 else if (_gtk_css_parser_try (parser, "bottom", TRUE))
615 coords[i * 3 + 1] = 1;
616 else if (_gtk_css_parser_try (parser, "center", TRUE))
617 coords[i * 3 + 1] = 0.5;
618 else if (!_gtk_css_parser_try_double (parser, &coords[i * 3 + 1]))
620 _gtk_css_parser_error (parser,
621 "Expected a valid Y coordinate");
625 if (type == CAIRO_PATTERN_TYPE_RADIAL)
628 if (! _gtk_css_parser_try (parser, ",", TRUE))
630 _gtk_css_parser_error (parser,
635 if (! _gtk_css_parser_try_double (parser, &coords[(i * 3) + 2]))
637 _gtk_css_parser_error (parser,
638 "Expected a numer for the radius");
644 if (type == CAIRO_PATTERN_TYPE_LINEAR)
645 gradient = gtk_gradient_new_linear (coords[0], coords[1], coords[3], coords[4]);
647 gradient = gtk_gradient_new_radial (coords[0], coords[1], coords[2],
648 coords[3], coords[4], coords[5]);
650 while (_gtk_css_parser_try (parser, ",", TRUE))
652 GtkSymbolicColor *color;
655 if (_gtk_css_parser_try (parser, "from", TRUE))
659 if (!_gtk_css_parser_try (parser, "(", TRUE))
661 gtk_gradient_unref (gradient);
662 _gtk_css_parser_error (parser,
668 else if (_gtk_css_parser_try (parser, "to", TRUE))
672 if (!_gtk_css_parser_try (parser, "(", TRUE))
674 gtk_gradient_unref (gradient);
675 _gtk_css_parser_error (parser,
681 else if (_gtk_css_parser_try (parser, "color-stop", TRUE))
683 if (!_gtk_css_parser_try (parser, "(", TRUE))
685 gtk_gradient_unref (gradient);
686 _gtk_css_parser_error (parser,
691 if (!_gtk_css_parser_try_double (parser, &position))
693 gtk_gradient_unref (gradient);
694 _gtk_css_parser_error (parser,
695 "Expected a valid number");
699 if (!_gtk_css_parser_try (parser, ",", TRUE))
701 gtk_gradient_unref (gradient);
702 _gtk_css_parser_error (parser,
709 gtk_gradient_unref (gradient);
710 _gtk_css_parser_error (parser,
711 "Not a valid color-stop definition");
715 color = _gtk_css_parser_read_symbolic_color (parser);
718 gtk_gradient_unref (gradient);
722 gtk_gradient_add_color_stop (gradient, position, color);
723 gtk_symbolic_color_unref (color);
725 if (!_gtk_css_parser_try (parser, ")", TRUE))
727 gtk_gradient_unref (gradient);
728 _gtk_css_parser_error (parser,
734 if (!_gtk_css_parser_try (parser, ")", TRUE))
736 gtk_gradient_unref (gradient);
737 _gtk_css_parser_error (parser,
742 g_value_take_boxed (value, gradient);
747 gradient_value_print (const GValue *value,
750 GtkGradient *gradient = g_value_get_boxed (value);
752 if (gradient == NULL)
753 g_string_append (string, "none");
756 char *s = gtk_gradient_to_string (gradient);
757 g_string_append (string, s);
763 gtk_css_parse_url (GtkCssParser *parser,
769 if (_gtk_css_parser_try (parser, "url", FALSE))
771 if (!_gtk_css_parser_try (parser, "(", TRUE))
773 _gtk_css_parser_skip_whitespace (parser);
774 if (_gtk_css_parser_try (parser, "(", TRUE))
778 error = g_error_new_literal (GTK_CSS_PROVIDER_ERROR,
779 GTK_CSS_PROVIDER_ERROR_DEPRECATED,
780 "Whitespace between 'url' and '(' is not allowed");
782 _gtk_css_parser_take_error (parser, error);
786 _gtk_css_parser_error (parser, "Expected '(' after 'url'");
791 path = _gtk_css_parser_read_string (parser);
795 if (!_gtk_css_parser_try (parser, ")", TRUE))
797 _gtk_css_parser_error (parser, "No closing ')' found for 'url'");
804 path = _gtk_css_parser_try_name (parser, TRUE);
807 _gtk_css_parser_error (parser, "Not a valid url");
812 file = g_file_resolve_relative_path (base, path);
819 pattern_value_parse (GtkCssParser *parser,
823 if (_gtk_css_parser_begins_with (parser, '-'))
825 g_value_unset (value);
826 g_value_init (value, GTK_TYPE_GRADIENT);
827 return gradient_value_parse (parser, base, value);
831 GError *error = NULL;
835 cairo_surface_t *surface;
836 cairo_pattern_t *pattern;
838 cairo_matrix_t matrix;
840 file = gtk_css_parse_url (parser, base);
844 path = g_file_get_path (file);
845 g_object_unref (file);
847 pixbuf = gdk_pixbuf_new_from_file (path, &error);
851 _gtk_css_parser_take_error (parser, error);
855 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
856 gdk_pixbuf_get_width (pixbuf),
857 gdk_pixbuf_get_height (pixbuf));
858 cr = cairo_create (surface);
859 gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
861 pattern = cairo_pattern_create_for_surface (surface);
863 cairo_matrix_init_scale (&matrix,
864 gdk_pixbuf_get_width (pixbuf),
865 gdk_pixbuf_get_height (pixbuf));
866 cairo_pattern_set_matrix (pattern, &matrix);
868 cairo_surface_destroy (surface);
870 g_object_unref (pixbuf);
872 g_value_take_boxed (value, pattern);
879 shadow_value_parse (GtkCssParser *parser,
884 gdouble hoffset, voffset, blur, spread;
885 GtkSymbolicColor *color;
888 shadow = _gtk_shadow_new ();
892 inset = _gtk_css_parser_try (parser, "inset", TRUE);
894 if (!_gtk_css_parser_try_double (parser, &hoffset) ||
895 !_gtk_css_parser_try_double (parser, &voffset))
897 _gtk_css_parser_error (parser, "Horizontal and vertical offsets are required");
898 _gtk_shadow_unref (shadow);
902 if (!_gtk_css_parser_try_double (parser, &blur))
905 if (!_gtk_css_parser_try_double (parser, &spread))
908 /* XXX: the color is optional and UA-defined if it's missing,
909 * but it doesn't really make sense for us...
911 color = _gtk_css_parser_read_symbolic_color (parser);
915 _gtk_shadow_unref (shadow);
919 _gtk_shadow_append (shadow,
924 gtk_symbolic_color_unref (color);
927 while (_gtk_css_parser_try (parser, ",", TRUE));
929 g_value_take_boxed (value, shadow);
934 shadow_value_print (const GValue *value,
939 shadow = g_value_get_boxed (value);
942 g_string_append (string, "none");
944 _gtk_shadow_print (shadow, string);
948 slice_value_parse (GtkCssParser *parser,
952 gdouble distance_top, distance_bottom;
953 gdouble distance_left, distance_right;
954 GtkSliceSideModifier mods[2];
958 GError *error = NULL;
962 /* Parse image url */
963 file = gtk_css_parse_url (parser, base);
967 if (!_gtk_css_parser_try_double (parser, &distance_top) ||
968 !_gtk_css_parser_try_double (parser, &distance_right) ||
969 !_gtk_css_parser_try_double (parser, &distance_bottom) ||
970 !_gtk_css_parser_try_double (parser, &distance_left))
972 _gtk_css_parser_error (parser, "Expected a number");
973 g_object_unref (file);
977 for (i = 0; i < 2; i++)
979 if (_gtk_css_parser_try (parser, "stretch", TRUE))
980 mods[i] = GTK_SLICE_STRETCH;
981 else if (_gtk_css_parser_try (parser, "repeat", TRUE))
982 mods[i] = GTK_SLICE_REPEAT;
985 mods[1] = mods[0] = GTK_SLICE_STRETCH;
992 path = g_file_get_path (file);
993 g_object_unref (file);
994 pixbuf = gdk_pixbuf_new_from_file (path, &error);
998 _gtk_css_parser_take_error (parser, error);
1002 slice = _gtk_9slice_new (pixbuf,
1003 distance_top, distance_bottom,
1004 distance_left, distance_right,
1006 g_object_unref (pixbuf);
1008 g_value_take_boxed (value, slice);
1013 enum_value_parse (GtkCssParser *parser,
1017 GEnumClass *enum_class;
1018 GEnumValue *enum_value;
1021 str = _gtk_css_parser_try_ident (parser, TRUE);
1024 _gtk_css_parser_error (parser, "Expected an identifier");
1028 enum_class = g_type_class_ref (G_VALUE_TYPE (value));
1029 enum_value = g_enum_get_value_by_nick (enum_class, str);
1032 g_value_set_enum (value, enum_value->value);
1034 _gtk_css_parser_error (parser,
1035 "Unknown value '%s' for enum type '%s'",
1036 str, g_type_name (G_VALUE_TYPE (value)));
1038 g_type_class_unref (enum_class);
1041 return enum_value != NULL;
1045 enum_value_print (const GValue *value,
1048 GEnumClass *enum_class;
1049 GEnumValue *enum_value;
1051 enum_class = g_type_class_ref (G_VALUE_TYPE (value));
1052 enum_value = g_enum_get_value (enum_class, g_value_get_enum (value));
1054 g_string_append (string, enum_value->value_nick);
1056 g_type_class_unref (enum_class);
1060 flags_value_parse (GtkCssParser *parser,
1064 GFlagsClass *flags_class;
1065 GFlagsValue *flag_value;
1069 flags_class = g_type_class_ref (G_VALUE_TYPE (value));
1072 str = _gtk_css_parser_try_ident (parser, TRUE);
1075 _gtk_css_parser_error (parser, "Expected an identifier");
1076 g_type_class_unref (flags_class);
1080 flag_value = g_flags_get_value_by_nick (flags_class, str);
1083 _gtk_css_parser_error (parser,
1084 "Unknown flag value '%s' for type '%s'",
1085 str, g_type_name (G_VALUE_TYPE (value)));
1086 /* XXX Do we want to return FALSE here? We can get
1087 * forward-compatibility for new values this way
1090 g_type_class_unref (flags_class);
1096 while (_gtk_css_parser_try (parser, ",", FALSE));
1098 g_type_class_unref (flags_class);
1100 g_value_set_enum (value, flags);
1106 flags_value_print (const GValue *value,
1109 GFlagsClass *flags_class;
1112 flags_class = g_type_class_ref (G_VALUE_TYPE (value));
1113 flags = g_value_get_flags (value);
1115 for (i = 0; i < flags_class->n_values; i++)
1117 GFlagsValue *flags_value = &flags_class->values[i];
1119 if (flags & flags_value->value)
1121 if (string->len != 0)
1122 g_string_append (string, ", ");
1124 g_string_append (string, flags_value->value_nick);
1128 g_type_class_unref (flags_class);
1132 bindings_value_parse (GtkCssParser *parser,
1137 GtkBindingSet *binding_set;
1140 array = g_ptr_array_new ();
1143 name = _gtk_css_parser_try_ident (parser, TRUE);
1146 _gtk_css_parser_error (parser, "Not a valid binding name");
1147 g_ptr_array_free (array, TRUE);
1151 binding_set = gtk_binding_set_find (name);
1155 _gtk_css_parser_error (parser, "No binding set named '%s'", name);
1160 g_ptr_array_add (array, binding_set);
1163 while (_gtk_css_parser_try (parser, ",", TRUE));
1165 g_value_take_boxed (value, array);
1171 bindings_value_print (const GValue *value,
1177 array = g_value_get_boxed (value);
1179 for (i = 0; i < array->len; i++)
1181 GtkBindingSet *binding_set = g_ptr_array_index (array, i);
1184 g_string_append (string, ", ");
1185 g_string_append (string, binding_set->set_name);
1192 unpack_border (const GValue *value,
1199 GParameter *parameter = g_new0 (GParameter, 4);
1200 GtkBorder *border = g_value_get_boxed (value);
1202 parameter[0].name = top;
1203 g_value_init (¶meter[0].value, G_TYPE_INT);
1204 g_value_set_int (¶meter[0].value, border->top);
1205 parameter[1].name = left;
1206 g_value_init (¶meter[1].value, G_TYPE_INT);
1207 g_value_set_int (¶meter[1].value, border->left);
1208 parameter[2].name = bottom;
1209 g_value_init (¶meter[2].value, G_TYPE_INT);
1210 g_value_set_int (¶meter[2].value, border->bottom);
1211 parameter[3].name = right;
1212 g_value_init (¶meter[3].value, G_TYPE_INT);
1213 g_value_set_int (¶meter[3].value, border->right);
1220 pack_border (GValue *value,
1221 GtkStyleProperties *props,
1222 GtkStateFlags state,
1231 gtk_style_properties_get (props,
1244 g_value_set_boxed (value, &border);
1248 unpack_border_width (const GValue *value,
1251 return unpack_border (value, n_params,
1252 "border-top-width", "border-left-width",
1253 "border-bottom-width", "border-right-width");
1257 pack_border_width (GValue *value,
1258 GtkStyleProperties *props,
1259 GtkStateFlags state)
1261 pack_border (value, props, state,
1262 "border-top-width", "border-left-width",
1263 "border-bottom-width", "border-right-width");
1267 unpack_padding (const GValue *value,
1270 return unpack_border (value, n_params,
1271 "padding-top", "padding-left",
1272 "padding-bottom", "padding-right");
1276 pack_padding (GValue *value,
1277 GtkStyleProperties *props,
1278 GtkStateFlags state)
1280 pack_border (value, props, state,
1281 "padding-top", "padding-left",
1282 "padding-bottom", "padding-right");
1286 unpack_margin (const GValue *value,
1289 return unpack_border (value, n_params,
1290 "margin-top", "margin-left",
1291 "margin-bottom", "margin-right");
1295 pack_margin (GValue *value,
1296 GtkStyleProperties *props,
1297 GtkStateFlags state)
1299 pack_border (value, props, state,
1300 "margin-top", "margin-left",
1301 "margin-bottom", "margin-right");
1307 css_string_funcs_init (void)
1309 if (G_LIKELY (parse_funcs != NULL))
1312 parse_funcs = g_hash_table_new (NULL, NULL);
1313 print_funcs = g_hash_table_new (NULL, NULL);
1315 register_conversion_function (GDK_TYPE_RGBA,
1318 register_conversion_function (GDK_TYPE_COLOR,
1321 register_conversion_function (GTK_TYPE_SYMBOLIC_COLOR,
1322 symbolic_color_value_parse,
1323 symbolic_color_value_print);
1324 register_conversion_function (PANGO_TYPE_FONT_DESCRIPTION,
1325 font_description_value_parse,
1326 font_description_value_print);
1327 register_conversion_function (G_TYPE_BOOLEAN,
1328 boolean_value_parse,
1329 boolean_value_print);
1330 register_conversion_function (G_TYPE_INT,
1333 register_conversion_function (G_TYPE_UINT,
1336 register_conversion_function (G_TYPE_DOUBLE,
1338 double_value_print);
1339 register_conversion_function (G_TYPE_FLOAT,
1342 register_conversion_function (G_TYPE_STRING,
1344 string_value_print);
1345 register_conversion_function (GTK_TYPE_THEMING_ENGINE,
1346 theming_engine_value_parse,
1347 theming_engine_value_print);
1348 register_conversion_function (GTK_TYPE_ANIMATION_DESCRIPTION,
1349 animation_description_value_parse,
1350 animation_description_value_print);
1351 register_conversion_function (GTK_TYPE_BORDER,
1353 border_value_print);
1354 register_conversion_function (GTK_TYPE_GRADIENT,
1355 gradient_value_parse,
1356 gradient_value_print);
1357 register_conversion_function (CAIRO_GOBJECT_TYPE_PATTERN,
1358 pattern_value_parse,
1360 register_conversion_function (GTK_TYPE_9SLICE,
1363 register_conversion_function (GTK_TYPE_SHADOW,
1365 shadow_value_print);
1366 register_conversion_function (G_TYPE_ENUM,
1369 register_conversion_function (G_TYPE_FLAGS,
1372 register_conversion_function (G_TYPE_PTR_ARRAY,
1373 bindings_value_parse,
1374 bindings_value_print);
1378 _gtk_css_value_parse (GValue *value,
1379 GtkCssParser *parser,
1384 g_return_val_if_fail (value != NULL, FALSE);
1385 g_return_val_if_fail (parser != NULL, FALSE);
1387 css_string_funcs_init ();
1389 func = g_hash_table_lookup (parse_funcs,
1390 GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
1392 func = g_hash_table_lookup (parse_funcs,
1393 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
1397 _gtk_css_parser_error (parser,
1398 "Cannot convert to type '%s'",
1399 g_type_name (G_VALUE_TYPE (value)));
1403 return (*func) (parser, base, value);
1407 _gtk_css_value_to_string (const GValue *value)
1411 css_string_funcs_init ();
1413 func = g_hash_table_lookup (print_funcs,
1414 GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
1416 func = g_hash_table_lookup (print_funcs,
1417 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
1421 GString *string = g_string_new (NULL);
1422 func (value, string);
1423 return g_string_free (string, FALSE);
1426 return g_strdup_value_contents (value);
1430 _gtk_style_property_is_shorthand (const GtkStyleProperty *property)
1432 g_return_val_if_fail (property != NULL, FALSE);
1434 return property->pack_func != NULL;
1438 _gtk_style_property_unpack (const GtkStyleProperty *property,
1439 const GValue *value,
1442 g_return_val_if_fail (property != NULL, NULL);
1443 g_return_val_if_fail (property->unpack_func != NULL, NULL);
1444 g_return_val_if_fail (value != NULL, NULL);
1445 g_return_val_if_fail (n_params != NULL, NULL);
1447 return property->unpack_func (value, n_params);
1451 _gtk_style_property_pack (const GtkStyleProperty *property,
1452 GtkStyleProperties *props,
1453 GtkStateFlags state,
1456 g_return_if_fail (property != NULL);
1457 g_return_if_fail (property->pack_func != NULL);
1458 g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
1459 g_return_if_fail (G_IS_VALUE (value));
1461 property->pack_func (value, props, state);
1465 gtk_style_property_init (void)
1469 if (G_LIKELY (properties))
1472 /* stuff is never freed, so no need for free functions */
1473 properties = g_hash_table_new (g_str_hash, g_str_equal);
1475 /* note that gtk_style_properties_register_property() calls this function,
1476 * so make sure we're sanely inited to avoid infloops */
1478 pspec = g_param_spec_boxed ("color",
1482 gtk_style_param_set_inherit (pspec, TRUE);
1483 gtk_style_properties_register_property (NULL, pspec);
1485 gtk_style_properties_register_property (NULL,
1486 g_param_spec_boxed ("background-color",
1491 pspec = g_param_spec_boxed ("font",
1494 PANGO_TYPE_FONT_DESCRIPTION, 0);
1495 gtk_style_param_set_inherit (pspec, TRUE);
1496 gtk_style_properties_register_property (NULL, pspec);
1498 pspec = g_param_spec_boxed ("text-shadow",
1501 GTK_TYPE_SHADOW, 0);
1502 gtk_style_param_set_inherit (pspec, TRUE);
1503 gtk_style_properties_register_property (NULL, pspec);
1505 gtk_style_properties_register_property (NULL,
1506 g_param_spec_int ("margin-top",
1509 0, G_MAXINT, 0, 0));
1510 gtk_style_properties_register_property (NULL,
1511 g_param_spec_int ("margin-left",
1514 0, G_MAXINT, 0, 0));
1515 gtk_style_properties_register_property (NULL,
1516 g_param_spec_int ("margin-bottom",
1519 0, G_MAXINT, 0, 0));
1520 gtk_style_properties_register_property (NULL,
1521 g_param_spec_int ("margin-right",
1524 0, G_MAXINT, 0, 0));
1525 _gtk_style_property_register (g_param_spec_boxed ("margin",
1528 GTK_TYPE_BORDER, 0),
1532 gtk_style_properties_register_property (NULL,
1533 g_param_spec_int ("padding-top",
1536 0, G_MAXINT, 0, 0));
1537 gtk_style_properties_register_property (NULL,
1538 g_param_spec_int ("padding-left",
1541 0, G_MAXINT, 0, 0));
1542 gtk_style_properties_register_property (NULL,
1543 g_param_spec_int ("padding-bottom",
1545 "Padding at bottom",
1546 0, G_MAXINT, 0, 0));
1547 gtk_style_properties_register_property (NULL,
1548 g_param_spec_int ("padding-right",
1551 0, G_MAXINT, 0, 0));
1552 _gtk_style_property_register (g_param_spec_boxed ("padding",
1555 GTK_TYPE_BORDER, 0),
1559 gtk_style_properties_register_property (NULL,
1560 g_param_spec_int ("border-top-width",
1562 "Border width at top",
1563 0, G_MAXINT, 0, 0));
1564 gtk_style_properties_register_property (NULL,
1565 g_param_spec_int ("border-left-width",
1566 "border left width",
1567 "Border width at left",
1568 0, G_MAXINT, 0, 0));
1569 gtk_style_properties_register_property (NULL,
1570 g_param_spec_int ("border-bottom-width",
1571 "border bottom width",
1572 "Border width at bottom",
1573 0, G_MAXINT, 0, 0));
1574 gtk_style_properties_register_property (NULL,
1575 g_param_spec_int ("border-right-width",
1576 "border right width",
1577 "Border width at right",
1578 0, G_MAXINT, 0, 0));
1579 _gtk_style_property_register (g_param_spec_boxed ("border-width",
1581 "Border width, in pixels",
1582 GTK_TYPE_BORDER, 0),
1584 unpack_border_width,
1586 gtk_style_properties_register_property (NULL,
1587 g_param_spec_int ("border-radius",
1589 "Border radius, in pixels",
1590 0, G_MAXINT, 0, 0));
1591 gtk_style_properties_register_property (NULL,
1592 g_param_spec_enum ("border-style",
1595 GTK_TYPE_BORDER_STYLE,
1596 GTK_BORDER_STYLE_NONE, 0));
1597 gtk_style_properties_register_property (NULL,
1598 g_param_spec_boxed ("border-color",
1602 gtk_style_properties_register_property (NULL,
1603 g_param_spec_boxed ("background-image",
1606 CAIRO_GOBJECT_TYPE_PATTERN, 0));
1607 gtk_style_properties_register_property (NULL,
1608 g_param_spec_boxed ("border-image",
1611 GTK_TYPE_9SLICE, 0));
1612 gtk_style_properties_register_property (NULL,
1613 g_param_spec_object ("engine",
1616 GTK_TYPE_THEMING_ENGINE, 0));
1617 gtk_style_properties_register_property (NULL,
1618 g_param_spec_boxed ("transition",
1619 "Transition animation description",
1620 "Transition animation description",
1621 GTK_TYPE_ANIMATION_DESCRIPTION, 0));
1623 /* Private property holding the binding sets */
1624 gtk_style_properties_register_property (NULL,
1625 g_param_spec_boxed ("gtk-key-bindings",
1628 G_TYPE_PTR_ARRAY, 0));
1631 const GtkStyleProperty *
1632 _gtk_style_property_lookup (const char *name)
1634 gtk_style_property_init ();
1636 return g_hash_table_lookup (properties, name);
1640 _gtk_style_property_register (GParamSpec *pspec,
1641 GtkStylePropertyParser parse_func,
1642 GtkStyleUnpackFunc unpack_func,
1643 GtkStylePackFunc pack_func)
1645 const GtkStyleProperty *existing;
1646 GtkStyleProperty *node;
1648 g_return_if_fail ((pack_func == NULL) == (unpack_func == NULL));
1650 gtk_style_property_init ();
1652 existing = _gtk_style_property_lookup (pspec->name);
1653 if (existing != NULL)
1655 g_warning ("Property \"%s\" was already registered with type %s",
1656 pspec->name, g_type_name (existing->pspec->value_type));
1660 node = g_slice_new0 (GtkStyleProperty);
1661 node->pspec = pspec;
1662 node->parse_func = parse_func;
1663 node->pack_func = pack_func;
1664 node->unpack_func = unpack_func;
1666 g_hash_table_insert (properties, pspec->name, node);