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 "gtkcssstylefuncsprivate.h"
29 #include <gdk-pixbuf/gdk-pixbuf.h>
30 #include <cairo-gobject.h>
32 #include "gtkanimationdescription.h"
33 #include "gtkcssprovider.h"
34 #include "gtkcsstypesprivate.h"
35 #include "gtkgradient.h"
36 #include "gtkprivatetypebuiltins.h"
37 #include "gtkshadowprivate.h"
38 #include "gtkstylecontextprivate.h"
39 #include "gtkthemingengine.h"
40 #include "gtktypebuiltins.h"
41 #include "gtkwin32themeprivate.h"
43 /* this is in case round() is not provided by the compiler,
44 * such as in the case of C89 compilers, like MSVC
46 #include "fallback-c89.c"
48 static GHashTable *parse_funcs = NULL;
49 static GHashTable *print_funcs = NULL;
50 static GHashTable *compute_funcs = NULL;
52 typedef gboolean (* GtkStyleParseFunc) (GtkCssParser *parser,
55 typedef void (* GtkStylePrintFunc) (const GValue *value,
57 typedef void (* GtkStylePrintFunc) (const GValue *value,
59 typedef void (* GtkStyleComputeFunc) (GValue *computed,
60 GtkStyleContext *context,
61 const GValue *specified);
64 register_conversion_function (GType type,
65 GtkStyleParseFunc parse,
66 GtkStylePrintFunc print,
67 GtkStyleComputeFunc compute)
70 g_hash_table_insert (parse_funcs, GSIZE_TO_POINTER (type), parse);
72 g_hash_table_insert (print_funcs, GSIZE_TO_POINTER (type), print);
74 g_hash_table_insert (compute_funcs, GSIZE_TO_POINTER (type), compute);
78 string_append_double (GString *string,
81 char buf[G_ASCII_DTOSTR_BUF_SIZE];
83 g_ascii_dtostr (buf, sizeof (buf), d);
84 g_string_append (string, buf);
88 string_append_string (GString *str,
93 g_string_append_c (str, '"');
96 len = strcspn (string, "\"\n\r\f");
97 g_string_append (str, string);
104 g_string_append (str, "\\A ");
107 g_string_append (str, "\\D ");
110 g_string_append (str, "\\C ");
113 g_string_append (str, "\\\"");
116 g_assert_not_reached ();
121 g_string_append_c (str, '"');
124 /*** IMPLEMENTATIONS ***/
127 enum_parse (GtkCssParser *parser,
133 if (_gtk_css_parser_try_enum (parser, type, res))
136 str = _gtk_css_parser_try_ident (parser, TRUE);
139 _gtk_css_parser_error (parser, "Expected an identifier");
143 _gtk_css_parser_error (parser,
144 "Unknown value '%s' for enum type '%s'",
145 str, g_type_name (type));
152 enum_print (int value,
156 GEnumClass *enum_class;
157 GEnumValue *enum_value;
159 enum_class = g_type_class_ref (type);
160 enum_value = g_enum_get_value (enum_class, value);
162 g_string_append (string, enum_value->value_nick);
164 g_type_class_unref (enum_class);
168 rgba_value_parse (GtkCssParser *parser,
172 GtkSymbolicColor *symbolic;
175 symbolic = _gtk_css_parser_read_symbolic_color (parser);
176 if (symbolic == NULL)
179 if (gtk_symbolic_color_resolve (symbolic, NULL, &rgba))
181 g_value_set_boxed (value, &rgba);
182 gtk_symbolic_color_unref (symbolic);
186 g_value_unset (value);
187 g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
188 g_value_take_boxed (value, symbolic);
195 rgba_value_print (const GValue *value,
198 const GdkRGBA *rgba = g_value_get_boxed (value);
201 g_string_append (string, "none");
204 char *s = gdk_rgba_to_string (rgba);
205 g_string_append (string, s);
211 rgba_value_compute (GValue *computed,
212 GtkStyleContext *context,
213 const GValue *specified)
215 GdkRGBA rgba, white = { 1, 1, 1, 1 };
217 if (G_VALUE_HOLDS (specified, GTK_TYPE_SYMBOLIC_COLOR))
219 if (_gtk_style_context_resolve_color (context,
220 g_value_get_boxed (specified),
222 g_value_set_boxed (computed, &rgba);
224 g_value_set_boxed (computed, &white);
227 g_value_copy (specified, computed);
231 color_value_parse (GtkCssParser *parser,
235 GtkSymbolicColor *symbolic;
238 symbolic = _gtk_css_parser_read_symbolic_color (parser);
239 if (symbolic == NULL)
242 if (gtk_symbolic_color_resolve (symbolic, NULL, &rgba))
246 color.red = rgba.red * 65535. + 0.5;
247 color.green = rgba.green * 65535. + 0.5;
248 color.blue = rgba.blue * 65535. + 0.5;
250 g_value_set_boxed (value, &color);
254 g_value_unset (value);
255 g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
256 g_value_take_boxed (value, symbolic);
263 color_value_print (const GValue *value,
266 const GdkColor *color = g_value_get_boxed (value);
269 g_string_append (string, "none");
272 char *s = gdk_color_to_string (color);
273 g_string_append (string, s);
279 color_value_compute (GValue *computed,
280 GtkStyleContext *context,
281 const GValue *specified)
284 GdkColor color = { 0, 65535, 65535, 65535 };
286 if (G_VALUE_HOLDS (specified, GTK_TYPE_SYMBOLIC_COLOR))
288 if (_gtk_style_context_resolve_color (context,
289 g_value_get_boxed (specified),
292 color.red = rgba.red * 65535. + 0.5;
293 color.green = rgba.green * 65535. + 0.5;
294 color.blue = rgba.blue * 65535. + 0.5;
297 g_value_set_boxed (computed, &color);
300 g_value_copy (specified, computed);
304 symbolic_color_value_parse (GtkCssParser *parser,
308 GtkSymbolicColor *symbolic;
310 symbolic = _gtk_css_parser_read_symbolic_color (parser);
311 if (symbolic == NULL)
314 g_value_take_boxed (value, symbolic);
319 symbolic_color_value_print (const GValue *value,
322 GtkSymbolicColor *symbolic = g_value_get_boxed (value);
324 if (symbolic == NULL)
325 g_string_append (string, "none");
328 char *s = gtk_symbolic_color_to_string (symbolic);
329 g_string_append (string, s);
335 font_description_value_parse (GtkCssParser *parser,
339 PangoFontDescription *font_desc;
343 str = _gtk_css_parser_read_value (parser);
347 font_desc = pango_font_description_from_string (str);
348 mask = pango_font_description_get_set_fields (font_desc);
349 /* These values are not really correct,
350 * but the fields must be set, so we set them to something */
351 if ((mask & PANGO_FONT_MASK_FAMILY) == 0)
352 pango_font_description_set_family_static (font_desc, "Sans");
353 if ((mask & PANGO_FONT_MASK_SIZE) == 0)
354 pango_font_description_set_size (font_desc, 10 * PANGO_SCALE);
356 g_value_take_boxed (value, font_desc);
361 font_description_value_print (const GValue *value,
364 const PangoFontDescription *desc = g_value_get_boxed (value);
367 g_string_append (string, "none");
370 char *s = pango_font_description_to_string (desc);
371 g_string_append (string, s);
377 boolean_value_parse (GtkCssParser *parser,
381 if (_gtk_css_parser_try (parser, "true", TRUE) ||
382 _gtk_css_parser_try (parser, "1", TRUE))
384 g_value_set_boolean (value, TRUE);
387 else if (_gtk_css_parser_try (parser, "false", TRUE) ||
388 _gtk_css_parser_try (parser, "0", TRUE))
390 g_value_set_boolean (value, FALSE);
395 _gtk_css_parser_error (parser, "Expected a boolean value");
401 boolean_value_print (const GValue *value,
404 if (g_value_get_boolean (value))
405 g_string_append (string, "true");
407 g_string_append (string, "false");
411 int_value_parse (GtkCssParser *parser,
417 if (_gtk_css_parser_begins_with (parser, '-'))
419 int res = _gtk_win32_theme_int_parse (parser, base, &i);
422 g_value_set_int (value, i);
425 /* < 0 => continue */
428 if (!_gtk_css_parser_try_int (parser, &i))
430 _gtk_css_parser_error (parser, "Expected a valid integer value");
434 g_value_set_int (value, i);
439 int_value_print (const GValue *value,
442 g_string_append_printf (string, "%d", g_value_get_int (value));
446 uint_value_parse (GtkCssParser *parser,
452 if (!_gtk_css_parser_try_uint (parser, &u))
454 _gtk_css_parser_error (parser, "Expected a valid unsigned value");
458 g_value_set_uint (value, u);
463 uint_value_print (const GValue *value,
466 g_string_append_printf (string, "%u", g_value_get_uint (value));
470 double_value_parse (GtkCssParser *parser,
476 if (!_gtk_css_parser_try_double (parser, &d))
478 _gtk_css_parser_error (parser, "Expected a number");
482 g_value_set_double (value, d);
487 double_value_print (const GValue *value,
490 string_append_double (string, g_value_get_double (value));
494 float_value_parse (GtkCssParser *parser,
500 if (!_gtk_css_parser_try_double (parser, &d))
502 _gtk_css_parser_error (parser, "Expected a number");
506 g_value_set_float (value, d);
511 float_value_print (const GValue *value,
514 string_append_double (string, g_value_get_float (value));
518 string_value_parse (GtkCssParser *parser,
522 char *str = _gtk_css_parser_read_string (parser);
527 g_value_take_string (value, str);
532 string_value_print (const GValue *value,
535 string_append_string (str, g_value_get_string (value));
539 theming_engine_value_parse (GtkCssParser *parser,
543 GtkThemingEngine *engine;
546 if (_gtk_css_parser_try (parser, "none", TRUE))
548 g_value_set_object (value, gtk_theming_engine_load (NULL));
552 str = _gtk_css_parser_try_ident (parser, TRUE);
555 _gtk_css_parser_error (parser, "Expected a valid theme name");
559 engine = gtk_theming_engine_load (str);
563 _gtk_css_parser_error (parser, "Themeing engine '%s' not found", str);
568 g_value_set_object (value, engine);
574 theming_engine_value_print (const GValue *value,
577 GtkThemingEngine *engine;
580 engine = g_value_get_object (value);
582 g_string_append (string, "none");
585 /* XXX: gtk_theming_engine_get_name()? */
586 g_object_get (engine, "name", &name, NULL);
587 g_string_append (string, name ? name : "none");
593 animation_description_value_parse (GtkCssParser *parser,
597 GtkAnimationDescription *desc;
600 str = _gtk_css_parser_read_value (parser);
604 desc = _gtk_animation_description_from_string (str);
609 _gtk_css_parser_error (parser, "Invalid animation description");
613 g_value_take_boxed (value, desc);
618 animation_description_value_print (const GValue *value,
621 GtkAnimationDescription *desc = g_value_get_boxed (value);
624 g_string_append (string, "none");
626 _gtk_animation_description_print (desc, string);
630 border_value_parse (GtkCssParser *parser,
634 GtkBorder border = { 0, };
637 for (i = 0; i < G_N_ELEMENTS (numbers); i++)
639 if (_gtk_css_parser_begins_with (parser, '-'))
641 /* These are strictly speaking signed, but we want to be able to use them
642 for unsigned types too, as the actual ranges of values make this safe */
643 int res = _gtk_win32_theme_int_parse (parser, base, (int *)&numbers[i]);
645 if (res == 0) /* Parse error, report */
648 if (res < 0) /* Nothing known to expand */
653 if (!_gtk_css_parser_try_uint (parser, &numbers[i]))
656 /* XXX: shouldn't allow spaces here? */
657 _gtk_css_parser_try (parser, "px", TRUE);
663 _gtk_css_parser_error (parser, "Expected valid border");
667 border.top = numbers[0];
669 border.right = numbers[1];
671 border.right = border.top;
673 border.bottom = numbers[2];
675 border.bottom = border.top;
677 border.left = numbers[3];
679 border.left = border.right;
681 g_value_set_boxed (value, &border);
686 border_value_print (const GValue *value, GString *string)
688 const GtkBorder *border = g_value_get_boxed (value);
691 g_string_append (string, "none");
692 else if (border->left != border->right)
693 g_string_append_printf (string, "%d %d %d %d", border->top, border->right, border->bottom, border->left);
694 else if (border->top != border->bottom)
695 g_string_append_printf (string, "%d %d %d", border->top, border->right, border->bottom);
696 else if (border->top != border->left)
697 g_string_append_printf (string, "%d %d", border->top, border->right);
699 g_string_append_printf (string, "%d", border->top);
703 gradient_value_parse (GtkCssParser *parser,
707 GtkGradient *gradient;
708 cairo_pattern_type_t type;
712 if (!_gtk_css_parser_try (parser, "-gtk-gradient", TRUE))
714 _gtk_css_parser_error (parser,
715 "Expected '-gtk-gradient'");
719 if (!_gtk_css_parser_try (parser, "(", TRUE))
721 _gtk_css_parser_error (parser,
722 "Expected '(' after '-gtk-gradient'");
726 /* Parse gradient type */
727 if (_gtk_css_parser_try (parser, "linear", TRUE))
728 type = CAIRO_PATTERN_TYPE_LINEAR;
729 else if (_gtk_css_parser_try (parser, "radial", TRUE))
730 type = CAIRO_PATTERN_TYPE_RADIAL;
733 _gtk_css_parser_error (parser,
734 "Gradient type must be 'radial' or 'linear'");
738 /* Parse start/stop position parameters */
739 for (i = 0; i < 2; i++)
741 if (! _gtk_css_parser_try (parser, ",", TRUE))
743 _gtk_css_parser_error (parser,
748 if (_gtk_css_parser_try (parser, "left", TRUE))
750 else if (_gtk_css_parser_try (parser, "right", TRUE))
752 else if (_gtk_css_parser_try (parser, "center", TRUE))
754 else if (!_gtk_css_parser_try_double (parser, &coords[i * 3]))
756 _gtk_css_parser_error (parser,
757 "Expected a valid X coordinate");
761 if (_gtk_css_parser_try (parser, "top", TRUE))
762 coords[i * 3 + 1] = 0;
763 else if (_gtk_css_parser_try (parser, "bottom", TRUE))
764 coords[i * 3 + 1] = 1;
765 else if (_gtk_css_parser_try (parser, "center", TRUE))
766 coords[i * 3 + 1] = 0.5;
767 else if (!_gtk_css_parser_try_double (parser, &coords[i * 3 + 1]))
769 _gtk_css_parser_error (parser,
770 "Expected a valid Y coordinate");
774 if (type == CAIRO_PATTERN_TYPE_RADIAL)
777 if (! _gtk_css_parser_try (parser, ",", TRUE))
779 _gtk_css_parser_error (parser,
784 if (! _gtk_css_parser_try_double (parser, &coords[(i * 3) + 2]))
786 _gtk_css_parser_error (parser,
787 "Expected a numer for the radius");
793 if (type == CAIRO_PATTERN_TYPE_LINEAR)
794 gradient = gtk_gradient_new_linear (coords[0], coords[1], coords[3], coords[4]);
796 gradient = gtk_gradient_new_radial (coords[0], coords[1], coords[2],
797 coords[3], coords[4], coords[5]);
799 while (_gtk_css_parser_try (parser, ",", TRUE))
801 GtkSymbolicColor *color;
804 if (_gtk_css_parser_try (parser, "from", TRUE))
808 if (!_gtk_css_parser_try (parser, "(", TRUE))
810 gtk_gradient_unref (gradient);
811 _gtk_css_parser_error (parser,
817 else if (_gtk_css_parser_try (parser, "to", TRUE))
821 if (!_gtk_css_parser_try (parser, "(", TRUE))
823 gtk_gradient_unref (gradient);
824 _gtk_css_parser_error (parser,
830 else if (_gtk_css_parser_try (parser, "color-stop", TRUE))
832 if (!_gtk_css_parser_try (parser, "(", TRUE))
834 gtk_gradient_unref (gradient);
835 _gtk_css_parser_error (parser,
840 if (!_gtk_css_parser_try_double (parser, &position))
842 gtk_gradient_unref (gradient);
843 _gtk_css_parser_error (parser,
844 "Expected a valid number");
848 if (!_gtk_css_parser_try (parser, ",", TRUE))
850 gtk_gradient_unref (gradient);
851 _gtk_css_parser_error (parser,
858 gtk_gradient_unref (gradient);
859 _gtk_css_parser_error (parser,
860 "Not a valid color-stop definition");
864 color = _gtk_css_parser_read_symbolic_color (parser);
867 gtk_gradient_unref (gradient);
871 gtk_gradient_add_color_stop (gradient, position, color);
872 gtk_symbolic_color_unref (color);
874 if (!_gtk_css_parser_try (parser, ")", TRUE))
876 gtk_gradient_unref (gradient);
877 _gtk_css_parser_error (parser,
883 if (!_gtk_css_parser_try (parser, ")", TRUE))
885 gtk_gradient_unref (gradient);
886 _gtk_css_parser_error (parser,
891 g_value_take_boxed (value, gradient);
896 gradient_value_print (const GValue *value,
899 GtkGradient *gradient = g_value_get_boxed (value);
901 if (gradient == NULL)
902 g_string_append (string, "none");
905 char *s = gtk_gradient_to_string (gradient);
906 g_string_append (string, s);
912 gtk_css_parse_url (GtkCssParser *parser,
918 if (_gtk_css_parser_try (parser, "url", FALSE))
920 if (!_gtk_css_parser_try (parser, "(", TRUE))
922 _gtk_css_parser_skip_whitespace (parser);
923 if (_gtk_css_parser_try (parser, "(", TRUE))
927 error = g_error_new_literal (GTK_CSS_PROVIDER_ERROR,
928 GTK_CSS_PROVIDER_ERROR_DEPRECATED,
929 "Whitespace between 'url' and '(' is deprecated");
931 _gtk_css_parser_take_error (parser, error);
935 _gtk_css_parser_error (parser, "Expected '(' after 'url'");
940 path = _gtk_css_parser_read_string (parser);
944 if (!_gtk_css_parser_try (parser, ")", TRUE))
946 _gtk_css_parser_error (parser, "No closing ')' found for 'url'");
953 path = _gtk_css_parser_try_name (parser, TRUE);
956 _gtk_css_parser_error (parser, "Not a valid url");
961 file = g_file_resolve_relative_path (base, path);
968 pattern_value_parse (GtkCssParser *parser,
972 if (_gtk_css_parser_try (parser, "none", TRUE))
974 /* nothing to do here */
976 else if (_gtk_css_parser_begins_with (parser, '-'))
979 res = _gtk_win32_theme_part_parse (parser, base, value);
982 /* < 0 => continue */
983 g_value_unset (value);
984 g_value_init (value, GTK_TYPE_GRADIENT);
985 return gradient_value_parse (parser, base, value);
989 GError *error = NULL;
993 cairo_surface_t *surface;
994 cairo_pattern_t *pattern;
996 cairo_matrix_t matrix;
998 file = gtk_css_parse_url (parser, base);
1002 path = g_file_get_path (file);
1003 g_object_unref (file);
1005 pixbuf = gdk_pixbuf_new_from_file (path, &error);
1009 _gtk_css_parser_take_error (parser, error);
1013 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
1014 gdk_pixbuf_get_width (pixbuf),
1015 gdk_pixbuf_get_height (pixbuf));
1016 cr = cairo_create (surface);
1017 gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
1019 pattern = cairo_pattern_create_for_surface (surface);
1021 cairo_matrix_init_scale (&matrix,
1022 gdk_pixbuf_get_width (pixbuf),
1023 gdk_pixbuf_get_height (pixbuf));
1024 cairo_pattern_set_matrix (pattern, &matrix);
1026 cairo_surface_destroy (surface);
1028 g_object_unref (pixbuf);
1030 g_value_take_boxed (value, pattern);
1036 static cairo_status_t
1037 surface_write (void *closure,
1038 const unsigned char *data,
1039 unsigned int length)
1041 g_byte_array_append (closure, data, length);
1043 return CAIRO_STATUS_SUCCESS;
1047 surface_print (cairo_surface_t *surface,
1050 #if CAIRO_HAS_PNG_FUNCTIONS
1054 array = g_byte_array_new ();
1055 cairo_surface_write_to_png_stream (surface, surface_write, array);
1056 base64 = g_base64_encode (array->data, array->len);
1057 g_byte_array_free (array, TRUE);
1059 g_string_append (string, "url(\"data:image/png;base64,");
1060 g_string_append (string, base64);
1061 g_string_append (string, "\")");
1065 g_string_append (string, "none /* you need cairo png functions enabled to make this work */");
1070 pattern_value_print (const GValue *value,
1073 cairo_pattern_t *pattern;
1074 cairo_surface_t *surface;
1076 pattern = g_value_get_boxed (value);
1078 if (pattern == NULL)
1080 g_string_append (string, "none");
1084 switch (cairo_pattern_get_type (pattern))
1086 case CAIRO_PATTERN_TYPE_SURFACE:
1087 if (cairo_pattern_get_surface (pattern, &surface) != CAIRO_STATUS_SUCCESS)
1089 g_assert_not_reached ();
1091 surface_print (surface, string);
1093 case CAIRO_PATTERN_TYPE_SOLID:
1094 case CAIRO_PATTERN_TYPE_LINEAR:
1095 case CAIRO_PATTERN_TYPE_RADIAL:
1097 g_assert_not_reached ();
1103 pattern_value_compute (GValue *computed,
1104 GtkStyleContext *context,
1105 const GValue *specified)
1107 if (G_VALUE_HOLDS (specified, GTK_TYPE_GRADIENT))
1109 cairo_pattern_t *gradient;
1111 gradient = gtk_gradient_resolve_for_context (g_value_get_boxed (specified), context);
1113 g_value_take_boxed (computed, gradient);
1116 g_value_copy (specified, computed);
1120 shadow_value_parse (GtkCssParser *parser,
1124 gboolean have_inset, have_color, have_lengths;
1125 gdouble hoffset, voffset, blur, spread;
1126 GtkSymbolicColor *color;
1130 shadow = _gtk_shadow_new ();
1132 if (_gtk_css_parser_try (parser, "none", TRUE))
1137 have_inset = have_lengths = have_color = FALSE;
1139 for (i = 0; i < 3; i++)
1142 _gtk_css_parser_try (parser, "inset", TRUE))
1148 if (!have_lengths &&
1149 _gtk_css_parser_try_double (parser, &hoffset))
1151 have_lengths = TRUE;
1153 if (!_gtk_css_parser_try_double (parser, &voffset))
1155 _gtk_css_parser_error (parser, "Horizontal and vertical offsets are required");
1156 _gtk_shadow_unref (shadow);
1160 if (!_gtk_css_parser_try_double (parser, &blur))
1163 if (!_gtk_css_parser_try_double (parser, &spread))
1173 /* XXX: the color is optional and UA-defined if it's missing,
1174 * but it doesn't really make sense for us...
1176 color = _gtk_css_parser_read_symbolic_color (parser);
1180 _gtk_shadow_unref (shadow);
1186 if (!have_color || !have_lengths)
1188 _gtk_css_parser_error (parser, "Must specify at least color and offsets");
1189 _gtk_shadow_unref (shadow);
1193 _gtk_shadow_append (shadow,
1198 gtk_symbolic_color_unref (color);
1201 while (_gtk_css_parser_try (parser, ",", TRUE));
1203 g_value_take_boxed (value, shadow);
1208 shadow_value_print (const GValue *value,
1213 shadow = g_value_get_boxed (value);
1216 g_string_append (string, "none");
1218 _gtk_shadow_print (shadow, string);
1222 shadow_value_compute (GValue *computed,
1223 GtkStyleContext *context,
1224 const GValue *specified)
1228 shadow = g_value_get_boxed (specified);
1230 shadow = _gtk_shadow_resolve (shadow, context);
1232 g_value_take_boxed (computed, shadow);
1236 background_repeat_value_parse (GtkCssParser *parser,
1240 GtkCssBackgroundRepeat repeat;
1243 if (!enum_parse (parser, GTK_TYPE_CSS_BACKGROUND_REPEAT_STYLE, &style))
1246 repeat.repeat = style;
1248 g_value_set_boxed (value, &repeat);
1254 background_repeat_value_print (const GValue *value,
1257 GtkCssBackgroundRepeat *repeat;
1259 repeat = g_value_get_boxed (value);
1261 enum_print (repeat->repeat, GTK_TYPE_CSS_BACKGROUND_REPEAT_STYLE, string);
1265 border_image_repeat_value_parse (GtkCssParser *parser,
1269 GtkCssBorderImageRepeat image_repeat;
1270 GtkCssBorderRepeatStyle styles[2];
1273 for (i = 0; i < 2; i++)
1275 if (_gtk_css_parser_try_enum (parser, GTK_TYPE_CSS_BORDER_REPEAT_STYLE, &v))
1279 styles[1] = styles[0] = GTK_CSS_REPEAT_STYLE_STRETCH;
1283 styles[i] = styles[0];
1286 image_repeat.hrepeat = styles[0];
1287 image_repeat.vrepeat = styles[1];
1289 g_value_set_boxed (value, &image_repeat);
1295 border_image_repeat_value_print (const GValue *value,
1298 GtkCssBorderImageRepeat *image_repeat;
1300 image_repeat = g_value_get_boxed (value);
1302 enum_print (image_repeat->hrepeat, GTK_TYPE_CSS_BORDER_REPEAT_STYLE, string);
1303 if (image_repeat->hrepeat != image_repeat->vrepeat)
1305 g_string_append (string, " ");
1306 enum_print (image_repeat->vrepeat, GTK_TYPE_CSS_BORDER_REPEAT_STYLE, string);
1311 enum_value_parse (GtkCssParser *parser,
1317 if (enum_parse (parser, G_VALUE_TYPE (value), &v))
1319 g_value_set_enum (value, v);
1327 enum_value_print (const GValue *value,
1330 enum_print (g_value_get_enum (value), G_VALUE_TYPE (value), string);
1334 flags_value_parse (GtkCssParser *parser,
1338 GFlagsClass *flags_class;
1339 GFlagsValue *flag_value;
1343 flags_class = g_type_class_ref (G_VALUE_TYPE (value));
1346 str = _gtk_css_parser_try_ident (parser, TRUE);
1349 _gtk_css_parser_error (parser, "Expected an identifier");
1350 g_type_class_unref (flags_class);
1354 flag_value = g_flags_get_value_by_nick (flags_class, str);
1357 _gtk_css_parser_error (parser,
1358 "Unknown flag value '%s' for type '%s'",
1359 str, g_type_name (G_VALUE_TYPE (value)));
1360 /* XXX Do we want to return FALSE here? We can get
1361 * forward-compatibility for new values this way
1364 g_type_class_unref (flags_class);
1370 while (_gtk_css_parser_try (parser, ",", FALSE));
1372 g_type_class_unref (flags_class);
1374 g_value_set_enum (value, flags);
1380 flags_value_print (const GValue *value,
1383 GFlagsClass *flags_class;
1386 flags_class = g_type_class_ref (G_VALUE_TYPE (value));
1387 flags = g_value_get_flags (value);
1389 for (i = 0; i < flags_class->n_values; i++)
1391 GFlagsValue *flags_value = &flags_class->values[i];
1393 if (flags & flags_value->value)
1395 if (string->len != 0)
1396 g_string_append (string, ", ");
1398 g_string_append (string, flags_value->value_nick);
1402 g_type_class_unref (flags_class);
1408 gtk_css_style_funcs_init (void)
1410 if (G_LIKELY (parse_funcs != NULL))
1413 parse_funcs = g_hash_table_new (NULL, NULL);
1414 print_funcs = g_hash_table_new (NULL, NULL);
1415 compute_funcs = g_hash_table_new (NULL, NULL);
1417 register_conversion_function (GDK_TYPE_RGBA,
1420 rgba_value_compute);
1421 register_conversion_function (GDK_TYPE_COLOR,
1424 color_value_compute);
1425 register_conversion_function (GTK_TYPE_SYMBOLIC_COLOR,
1426 symbolic_color_value_parse,
1427 symbolic_color_value_print,
1429 register_conversion_function (PANGO_TYPE_FONT_DESCRIPTION,
1430 font_description_value_parse,
1431 font_description_value_print,
1433 register_conversion_function (G_TYPE_BOOLEAN,
1434 boolean_value_parse,
1435 boolean_value_print,
1437 register_conversion_function (G_TYPE_INT,
1441 register_conversion_function (G_TYPE_UINT,
1445 register_conversion_function (G_TYPE_DOUBLE,
1449 register_conversion_function (G_TYPE_FLOAT,
1453 register_conversion_function (G_TYPE_STRING,
1457 register_conversion_function (GTK_TYPE_THEMING_ENGINE,
1458 theming_engine_value_parse,
1459 theming_engine_value_print,
1461 register_conversion_function (GTK_TYPE_ANIMATION_DESCRIPTION,
1462 animation_description_value_parse,
1463 animation_description_value_print,
1465 register_conversion_function (GTK_TYPE_BORDER,
1469 register_conversion_function (GTK_TYPE_GRADIENT,
1470 gradient_value_parse,
1471 gradient_value_print,
1473 register_conversion_function (CAIRO_GOBJECT_TYPE_PATTERN,
1474 pattern_value_parse,
1475 pattern_value_print,
1476 pattern_value_compute);
1477 register_conversion_function (GTK_TYPE_CSS_BORDER_IMAGE_REPEAT,
1478 border_image_repeat_value_parse,
1479 border_image_repeat_value_print,
1481 register_conversion_function (GTK_TYPE_SHADOW,
1484 shadow_value_compute);
1485 register_conversion_function (G_TYPE_ENUM,
1489 register_conversion_function (G_TYPE_FLAGS,
1493 register_conversion_function (GTK_TYPE_CSS_BACKGROUND_REPEAT,
1494 background_repeat_value_parse,
1495 background_repeat_value_print,
1500 * _gtk_css_style_parse_value:
1501 * @value: the value to parse into. Must be a valid initialized #GValue
1502 * @parser: the parser to parse from
1503 * @base: the base URL for @parser
1505 * This is the generic parsing function used for CSS values. If the
1506 * function fails to parse a value, it will emit an error on @parser,
1507 * return %FALSE and not touch @value.
1509 * Returns: %TRUE if parsing succeeded.
1512 _gtk_css_style_parse_value (GValue *value,
1513 GtkCssParser *parser,
1516 GtkStyleParseFunc func;
1518 g_return_val_if_fail (value != NULL, FALSE);
1519 g_return_val_if_fail (parser != NULL, FALSE);
1521 gtk_css_style_funcs_init ();
1523 func = g_hash_table_lookup (parse_funcs,
1524 GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
1526 func = g_hash_table_lookup (parse_funcs,
1527 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
1531 _gtk_css_parser_error (parser,
1532 "Cannot convert to type '%s'",
1533 g_type_name (G_VALUE_TYPE (value)));
1537 return (*func) (parser, base, value);
1541 * _gtk_css_style_print_value:
1542 * @value: an initialized GValue returned from _gtk_css_style_parse()
1543 * @string: the string to print into
1545 * Prints @value into @string as a CSS value. If @value is not a
1546 * valid value, a random string will be printed instead.
1549 _gtk_css_style_print_value (const GValue *value,
1552 GtkStylePrintFunc func;
1554 gtk_css_style_funcs_init ();
1556 func = g_hash_table_lookup (print_funcs,
1557 GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
1559 func = g_hash_table_lookup (print_funcs,
1560 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
1564 char *s = g_strdup_value_contents (value);
1565 g_string_append (string, s);
1570 func (value, string);
1574 * _gtk_css_style_compute_value:
1575 * @computed: (out): a value to be filled with the result
1576 * @context: the context to use for computing the value
1577 * @specified: the value to use for the computation
1579 * Converts the @specified value into the @computed value using the
1580 * information in @context. The values must have matching types, ie
1581 * @specified must be a result of a call to
1582 * _gtk_css_style_parse_value() with the same type as @computed.
1585 _gtk_css_style_compute_value (GValue *computed,
1586 GtkStyleContext *context,
1587 const GValue *specified)
1589 GtkStyleComputeFunc func;
1591 g_return_if_fail (G_IS_VALUE (computed));
1592 g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
1593 g_return_if_fail (G_IS_VALUE (specified));
1595 gtk_css_style_funcs_init ();
1597 func = g_hash_table_lookup (compute_funcs,
1598 GSIZE_TO_POINTER (G_VALUE_TYPE (computed)));
1600 func = g_hash_table_lookup (compute_funcs,
1601 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (computed))));
1604 func (computed, context, specified);
1606 g_value_copy (specified, computed);