/* GTK - The GIMP Toolkit * Copyright (C) 2010 Carlos Garnacho * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "config.h" #include "gtkstylepropertyprivate.h" #include #include #include #include #include #include #include "gtkcssprovider.h" #include "gtkcssparserprivate.h" #include "gtkcssshorthandpropertyprivate.h" #include "gtkcssstylepropertyprivate.h" #include "gtkcsstypesprivate.h" #include "gtkintl.h" #include "gtkprivatetypebuiltins.h" #include "gtkstylepropertiesprivate.h" /* the actual parsers we have */ #include "gtkanimationdescription.h" #include "gtkbindings.h" #include "gtkgradient.h" #include "gtkshadowprivate.h" #include "gtkthemingengine.h" #include "gtktypebuiltins.h" #include "gtkwin32themeprivate.h" /* this is in case round() is not provided by the compiler, * such as in the case of C89 compilers, like MSVC */ #include "fallback-c89.c" enum { PROP_0, PROP_NAME, PROP_VALUE_TYPE }; static GHashTable *parse_funcs = NULL; static GHashTable *print_funcs = NULL; static GPtrArray *__style_property_array = NULL; G_DEFINE_ABSTRACT_TYPE (GtkStyleProperty, _gtk_style_property, G_TYPE_OBJECT) static void gtk_style_property_finalize (GObject *object) { GtkStyleProperty *property = GTK_STYLE_PROPERTY (object); g_warning ("finalizing %s `%s', how could this happen?", G_OBJECT_TYPE_NAME (object), property->name); G_OBJECT_CLASS (_gtk_style_property_parent_class)->finalize (object); } static void gtk_style_property_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GtkStyleProperty *property = GTK_STYLE_PROPERTY (object); GtkStylePropertyClass *klass = GTK_STYLE_PROPERTY_GET_CLASS (property); switch (prop_id) { case PROP_NAME: property->name = g_value_dup_string (value); g_assert (property->name); g_assert (g_hash_table_lookup (klass->properties, property->name) == NULL); g_hash_table_insert (klass->properties, property->name, property); break; case PROP_VALUE_TYPE: property->value_type = g_value_get_gtype (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gtk_style_property_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GtkStyleProperty *property = GTK_STYLE_PROPERTY (object); switch (prop_id) { case PROP_NAME: g_value_set_string (value, property->name); break; case PROP_VALUE_TYPE: g_value_set_gtype (value, property->value_type); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void _gtk_style_property_class_init (GtkStylePropertyClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = gtk_style_property_finalize; object_class->set_property = gtk_style_property_set_property; object_class->get_property = gtk_style_property_get_property; g_object_class_install_property (object_class, PROP_NAME, g_param_spec_string ("name", P_("Property name"), P_("The name of the property"), NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_VALUE_TYPE, g_param_spec_gtype ("value-type", P_("Value type"), P_("The value type returned by GtkStyleContext"), G_TYPE_NONE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); klass->properties = g_hash_table_new (g_str_hash, g_str_equal); } static void _gtk_style_property_init (GtkStyleProperty *property) { property->value_type = G_TYPE_NONE; } static void register_conversion_function (GType type, GtkStyleParseFunc parse, GtkStylePrintFunc print) { if (parse) g_hash_table_insert (parse_funcs, GSIZE_TO_POINTER (type), parse); if (print) g_hash_table_insert (print_funcs, GSIZE_TO_POINTER (type), print); } static void string_append_double (GString *string, double d) { char buf[G_ASCII_DTOSTR_BUF_SIZE]; g_ascii_dtostr (buf, sizeof (buf), d); g_string_append (string, buf); } static void string_append_string (GString *str, const char *string) { gsize len; g_string_append_c (str, '"'); do { len = strcspn (string, "\"\n\r\f"); g_string_append (str, string); string += len; switch (*string) { case '\0': break; case '\n': g_string_append (str, "\\A "); break; case '\r': g_string_append (str, "\\D "); break; case '\f': g_string_append (str, "\\C "); break; case '\"': g_string_append (str, "\\\""); break; default: g_assert_not_reached (); break; } } while (*string); g_string_append_c (str, '"'); } /*** IMPLEMENTATIONS ***/ static gboolean enum_parse (GtkCssParser *parser, GType type, int *res) { char *str; if (_gtk_css_parser_try_enum (parser, type, res)) return TRUE; str = _gtk_css_parser_try_ident (parser, TRUE); if (str == NULL) { _gtk_css_parser_error (parser, "Expected an identifier"); return FALSE; } _gtk_css_parser_error (parser, "Unknown value '%s' for enum type '%s'", str, g_type_name (type)); g_free (str); return FALSE; } static void enum_print (int value, GType type, GString *string) { GEnumClass *enum_class; GEnumValue *enum_value; enum_class = g_type_class_ref (type); enum_value = g_enum_get_value (enum_class, value); g_string_append (string, enum_value->value_nick); g_type_class_unref (enum_class); } static gboolean rgba_value_parse (GtkCssParser *parser, GFile *base, GValue *value) { GtkSymbolicColor *symbolic; GdkRGBA rgba; symbolic = _gtk_css_parser_read_symbolic_color (parser); if (symbolic == NULL) return FALSE; if (gtk_symbolic_color_resolve (symbolic, NULL, &rgba)) { g_value_set_boxed (value, &rgba); gtk_symbolic_color_unref (symbolic); } else { g_value_unset (value); g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR); g_value_take_boxed (value, symbolic); } return TRUE; } static void rgba_value_print (const GValue *value, GString *string) { const GdkRGBA *rgba = g_value_get_boxed (value); if (rgba == NULL) g_string_append (string, "none"); else { char *s = gdk_rgba_to_string (rgba); g_string_append (string, s); g_free (s); } } static gboolean color_value_parse (GtkCssParser *parser, GFile *base, GValue *value) { GtkSymbolicColor *symbolic; GdkRGBA rgba; symbolic = _gtk_css_parser_read_symbolic_color (parser); if (symbolic == NULL) return FALSE; if (gtk_symbolic_color_resolve (symbolic, NULL, &rgba)) { GdkColor color; color.red = rgba.red * 65535. + 0.5; color.green = rgba.green * 65535. + 0.5; color.blue = rgba.blue * 65535. + 0.5; g_value_set_boxed (value, &color); } else { g_value_unset (value); g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR); g_value_take_boxed (value, symbolic); } return TRUE; } static void color_value_print (const GValue *value, GString *string) { const GdkColor *color = g_value_get_boxed (value); if (color == NULL) g_string_append (string, "none"); else { char *s = gdk_color_to_string (color); g_string_append (string, s); g_free (s); } } static gboolean symbolic_color_value_parse (GtkCssParser *parser, GFile *base, GValue *value) { GtkSymbolicColor *symbolic; symbolic = _gtk_css_parser_read_symbolic_color (parser); if (symbolic == NULL) return FALSE; g_value_take_boxed (value, symbolic); return TRUE; } static void symbolic_color_value_print (const GValue *value, GString *string) { GtkSymbolicColor *symbolic = g_value_get_boxed (value); if (symbolic == NULL) g_string_append (string, "none"); else { char *s = gtk_symbolic_color_to_string (symbolic); g_string_append (string, s); g_free (s); } } static gboolean font_family_parse (GtkCssParser *parser, GFile *base, GValue *value) { GPtrArray *names; char *name; /* We don't special case generic families. Pango should do * that for us */ names = g_ptr_array_new (); do { name = _gtk_css_parser_try_ident (parser, TRUE); if (name) { GString *string = g_string_new (name); g_free (name); while ((name = _gtk_css_parser_try_ident (parser, TRUE))) { g_string_append_c (string, ' '); g_string_append (string, name); g_free (name); } name = g_string_free (string, FALSE); } else { name = _gtk_css_parser_read_string (parser); if (name == NULL) { g_ptr_array_free (names, TRUE); return FALSE; } } g_ptr_array_add (names, name); } while (_gtk_css_parser_try (parser, ",", TRUE)); /* NULL-terminate array */ g_ptr_array_add (names, NULL); g_value_set_boxed (value, g_ptr_array_free (names, FALSE)); return TRUE; } static void font_family_value_print (const GValue *value, GString *string) { const char **names = g_value_get_boxed (value); if (names == NULL || *names == NULL) { g_string_append (string, "none"); return; } string_append_string (string, *names); names++; while (*names) { g_string_append (string, ", "); string_append_string (string, *names); names++; } } static gboolean font_description_value_parse (GtkCssParser *parser, GFile *base, GValue *value) { PangoFontDescription *font_desc; guint mask; char *str; str = _gtk_css_parser_read_value (parser); if (str == NULL) return FALSE; font_desc = pango_font_description_from_string (str); mask = pango_font_description_get_set_fields (font_desc); /* These values are not really correct, * but the fields must be set, so we set them to something */ if ((mask & PANGO_FONT_MASK_FAMILY) == 0) pango_font_description_set_family_static (font_desc, "Sans"); if ((mask & PANGO_FONT_MASK_SIZE) == 0) pango_font_description_set_size (font_desc, 10 * PANGO_SCALE); g_free (str); g_value_take_boxed (value, font_desc); return TRUE; } static void font_description_value_print (const GValue *value, GString *string) { const PangoFontDescription *desc = g_value_get_boxed (value); if (desc == NULL) g_string_append (string, "none"); else { char *s = pango_font_description_to_string (desc); g_string_append (string, s); g_free (s); } } static gboolean boolean_value_parse (GtkCssParser *parser, GFile *base, GValue *value) { if (_gtk_css_parser_try (parser, "true", TRUE) || _gtk_css_parser_try (parser, "1", TRUE)) { g_value_set_boolean (value, TRUE); return TRUE; } else if (_gtk_css_parser_try (parser, "false", TRUE) || _gtk_css_parser_try (parser, "0", TRUE)) { g_value_set_boolean (value, FALSE); return TRUE; } else { _gtk_css_parser_error (parser, "Expected a boolean value"); return FALSE; } } static void boolean_value_print (const GValue *value, GString *string) { if (g_value_get_boolean (value)) g_string_append (string, "true"); else g_string_append (string, "false"); } static gboolean int_value_parse (GtkCssParser *parser, GFile *base, GValue *value) { gint i; if (_gtk_css_parser_begins_with (parser, '-')) { int res = _gtk_win32_theme_int_parse (parser, base, &i); if (res >= 0) { g_value_set_int (value, i); return res > 0; } /* < 0 => continue */ } if (!_gtk_css_parser_try_int (parser, &i)) { _gtk_css_parser_error (parser, "Expected a valid integer value"); return FALSE; } g_value_set_int (value, i); return TRUE; } static void int_value_print (const GValue *value, GString *string) { g_string_append_printf (string, "%d", g_value_get_int (value)); } static gboolean uint_value_parse (GtkCssParser *parser, GFile *base, GValue *value) { guint u; if (!_gtk_css_parser_try_uint (parser, &u)) { _gtk_css_parser_error (parser, "Expected a valid unsigned value"); return FALSE; } g_value_set_uint (value, u); return TRUE; } static void uint_value_print (const GValue *value, GString *string) { g_string_append_printf (string, "%u", g_value_get_uint (value)); } static gboolean double_value_parse (GtkCssParser *parser, GFile *base, GValue *value) { gdouble d; if (!_gtk_css_parser_try_double (parser, &d)) { _gtk_css_parser_error (parser, "Expected a number"); return FALSE; } g_value_set_double (value, d); return TRUE; } static void double_value_print (const GValue *value, GString *string) { string_append_double (string, g_value_get_double (value)); } static gboolean float_value_parse (GtkCssParser *parser, GFile *base, GValue *value) { gdouble d; if (!_gtk_css_parser_try_double (parser, &d)) { _gtk_css_parser_error (parser, "Expected a number"); return FALSE; } g_value_set_float (value, d); return TRUE; } static void float_value_print (const GValue *value, GString *string) { string_append_double (string, g_value_get_float (value)); } static gboolean string_value_parse (GtkCssParser *parser, GFile *base, GValue *value) { char *str = _gtk_css_parser_read_string (parser); if (str == NULL) return FALSE; g_value_take_string (value, str); return TRUE; } static void string_value_print (const GValue *value, GString *str) { string_append_string (str, g_value_get_string (value)); } static gboolean theming_engine_value_parse (GtkCssParser *parser, GFile *base, GValue *value) { GtkThemingEngine *engine; char *str; if (_gtk_css_parser_try (parser, "none", TRUE)) { g_value_set_object (value, gtk_theming_engine_load (NULL)); return TRUE; } str = _gtk_css_parser_try_ident (parser, TRUE); if (str == NULL) { _gtk_css_parser_error (parser, "Expected a valid theme name"); return FALSE; } engine = gtk_theming_engine_load (str); if (engine == NULL) { _gtk_css_parser_error (parser, "Themeing engine '%s' not found", str); g_free (str); return FALSE; } g_value_set_object (value, engine); g_free (str); return TRUE; } static void theming_engine_value_print (const GValue *value, GString *string) { GtkThemingEngine *engine; char *name; engine = g_value_get_object (value); if (engine == NULL) g_string_append (string, "none"); else { /* XXX: gtk_theming_engine_get_name()? */ g_object_get (engine, "name", &name, NULL); g_string_append (string, name ? name : "none"); g_free (name); } } static gboolean animation_description_value_parse (GtkCssParser *parser, GFile *base, GValue *value) { GtkAnimationDescription *desc; char *str; str = _gtk_css_parser_read_value (parser); if (str == NULL) return FALSE; desc = _gtk_animation_description_from_string (str); g_free (str); if (desc == NULL) { _gtk_css_parser_error (parser, "Invalid animation description"); return FALSE; } g_value_take_boxed (value, desc); return TRUE; } static void animation_description_value_print (const GValue *value, GString *string) { GtkAnimationDescription *desc = g_value_get_boxed (value); if (desc == NULL) g_string_append (string, "none"); else _gtk_animation_description_print (desc, string); } static gboolean border_value_parse (GtkCssParser *parser, GFile *base, GValue *value) { GtkBorder border = { 0, }; guint i, numbers[4]; for (i = 0; i < G_N_ELEMENTS (numbers); i++) { if (_gtk_css_parser_begins_with (parser, '-')) { /* These are strictly speaking signed, but we want to be able to use them for unsigned types too, as the actual ranges of values make this safe */ int res = _gtk_win32_theme_int_parse (parser, base, (int *)&numbers[i]); if (res == 0) /* Parse error, report */ return FALSE; if (res < 0) /* Nothing known to expand */ break; } else { if (!_gtk_css_parser_try_uint (parser, &numbers[i])) break; /* XXX: shouldn't allow spaces here? */ _gtk_css_parser_try (parser, "px", TRUE); } } if (i == 0) { _gtk_css_parser_error (parser, "Expected valid border"); return FALSE; } border.top = numbers[0]; if (i > 1) border.right = numbers[1]; else border.right = border.top; if (i > 2) border.bottom = numbers[2]; else border.bottom = border.top; if (i > 3) border.left = numbers[3]; else border.left = border.right; g_value_set_boxed (value, &border); return TRUE; } static void border_value_print (const GValue *value, GString *string) { const GtkBorder *border = g_value_get_boxed (value); if (border == NULL) g_string_append (string, "none"); else if (border->left != border->right) g_string_append_printf (string, "%d %d %d %d", border->top, border->right, border->bottom, border->left); else if (border->top != border->bottom) g_string_append_printf (string, "%d %d %d", border->top, border->right, border->bottom); else if (border->top != border->left) g_string_append_printf (string, "%d %d", border->top, border->right); else g_string_append_printf (string, "%d", border->top); } static gboolean gradient_value_parse (GtkCssParser *parser, GFile *base, GValue *value) { GtkGradient *gradient; cairo_pattern_type_t type; gdouble coords[6]; guint i; if (!_gtk_css_parser_try (parser, "-gtk-gradient", TRUE)) { _gtk_css_parser_error (parser, "Expected '-gtk-gradient'"); return FALSE; } if (!_gtk_css_parser_try (parser, "(", TRUE)) { _gtk_css_parser_error (parser, "Expected '(' after '-gtk-gradient'"); return FALSE; } /* Parse gradient type */ if (_gtk_css_parser_try (parser, "linear", TRUE)) type = CAIRO_PATTERN_TYPE_LINEAR; else if (_gtk_css_parser_try (parser, "radial", TRUE)) type = CAIRO_PATTERN_TYPE_RADIAL; else { _gtk_css_parser_error (parser, "Gradient type must be 'radial' or 'linear'"); return FALSE; } /* Parse start/stop position parameters */ for (i = 0; i < 2; i++) { if (! _gtk_css_parser_try (parser, ",", TRUE)) { _gtk_css_parser_error (parser, "Expected ','"); return FALSE; } if (_gtk_css_parser_try (parser, "left", TRUE)) coords[i * 3] = 0; else if (_gtk_css_parser_try (parser, "right", TRUE)) coords[i * 3] = 1; else if (_gtk_css_parser_try (parser, "center", TRUE)) coords[i * 3] = 0.5; else if (!_gtk_css_parser_try_double (parser, &coords[i * 3])) { _gtk_css_parser_error (parser, "Expected a valid X coordinate"); return FALSE; } if (_gtk_css_parser_try (parser, "top", TRUE)) coords[i * 3 + 1] = 0; else if (_gtk_css_parser_try (parser, "bottom", TRUE)) coords[i * 3 + 1] = 1; else if (_gtk_css_parser_try (parser, "center", TRUE)) coords[i * 3 + 1] = 0.5; else if (!_gtk_css_parser_try_double (parser, &coords[i * 3 + 1])) { _gtk_css_parser_error (parser, "Expected a valid Y coordinate"); return FALSE; } if (type == CAIRO_PATTERN_TYPE_RADIAL) { /* Parse radius */ if (! _gtk_css_parser_try (parser, ",", TRUE)) { _gtk_css_parser_error (parser, "Expected ','"); return FALSE; } if (! _gtk_css_parser_try_double (parser, &coords[(i * 3) + 2])) { _gtk_css_parser_error (parser, "Expected a numer for the radius"); return FALSE; } } } if (type == CAIRO_PATTERN_TYPE_LINEAR) gradient = gtk_gradient_new_linear (coords[0], coords[1], coords[3], coords[4]); else gradient = gtk_gradient_new_radial (coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]); while (_gtk_css_parser_try (parser, ",", TRUE)) { GtkSymbolicColor *color; gdouble position; if (_gtk_css_parser_try (parser, "from", TRUE)) { position = 0; if (!_gtk_css_parser_try (parser, "(", TRUE)) { gtk_gradient_unref (gradient); _gtk_css_parser_error (parser, "Expected '('"); return FALSE; } } else if (_gtk_css_parser_try (parser, "to", TRUE)) { position = 1; if (!_gtk_css_parser_try (parser, "(", TRUE)) { gtk_gradient_unref (gradient); _gtk_css_parser_error (parser, "Expected '('"); return FALSE; } } else if (_gtk_css_parser_try (parser, "color-stop", TRUE)) { if (!_gtk_css_parser_try (parser, "(", TRUE)) { gtk_gradient_unref (gradient); _gtk_css_parser_error (parser, "Expected '('"); return FALSE; } if (!_gtk_css_parser_try_double (parser, &position)) { gtk_gradient_unref (gradient); _gtk_css_parser_error (parser, "Expected a valid number"); return FALSE; } if (!_gtk_css_parser_try (parser, ",", TRUE)) { gtk_gradient_unref (gradient); _gtk_css_parser_error (parser, "Expected a comma"); return FALSE; } } else { gtk_gradient_unref (gradient); _gtk_css_parser_error (parser, "Not a valid color-stop definition"); return FALSE; } color = _gtk_css_parser_read_symbolic_color (parser); if (color == NULL) { gtk_gradient_unref (gradient); return FALSE; } gtk_gradient_add_color_stop (gradient, position, color); gtk_symbolic_color_unref (color); if (!_gtk_css_parser_try (parser, ")", TRUE)) { gtk_gradient_unref (gradient); _gtk_css_parser_error (parser, "Expected ')'"); return FALSE; } } if (!_gtk_css_parser_try (parser, ")", TRUE)) { gtk_gradient_unref (gradient); _gtk_css_parser_error (parser, "Expected ')'"); return FALSE; } g_value_take_boxed (value, gradient); return TRUE; } static void gradient_value_print (const GValue *value, GString *string) { GtkGradient *gradient = g_value_get_boxed (value); if (gradient == NULL) g_string_append (string, "none"); else { char *s = gtk_gradient_to_string (gradient); g_string_append (string, s); g_free (s); } } static GFile * gtk_css_parse_url (GtkCssParser *parser, GFile *base) { gchar *path; GFile *file; if (_gtk_css_parser_try (parser, "url", FALSE)) { if (!_gtk_css_parser_try (parser, "(", TRUE)) { _gtk_css_parser_skip_whitespace (parser); if (_gtk_css_parser_try (parser, "(", TRUE)) { GError *error; error = g_error_new_literal (GTK_CSS_PROVIDER_ERROR, GTK_CSS_PROVIDER_ERROR_DEPRECATED, "Whitespace between 'url' and '(' is deprecated"); _gtk_css_parser_take_error (parser, error); } else { _gtk_css_parser_error (parser, "Expected '(' after 'url'"); return NULL; } } path = _gtk_css_parser_read_string (parser); if (path == NULL) return NULL; if (!_gtk_css_parser_try (parser, ")", TRUE)) { _gtk_css_parser_error (parser, "No closing ')' found for 'url'"); g_free (path); return NULL; } } else { path = _gtk_css_parser_try_name (parser, TRUE); if (path == NULL) { _gtk_css_parser_error (parser, "Not a valid url"); return NULL; } } file = g_file_resolve_relative_path (base, path); g_free (path); return file; } static gboolean pattern_value_parse (GtkCssParser *parser, GFile *base, GValue *value) { if (_gtk_css_parser_try (parser, "none", TRUE)) { /* nothing to do here */ } else if (_gtk_css_parser_begins_with (parser, '-')) { int res; res = _gtk_win32_theme_part_parse (parser, base, value); if (res >= 0) return res > 0; /* < 0 => continue */ g_value_unset (value); g_value_init (value, GTK_TYPE_GRADIENT); return gradient_value_parse (parser, base, value); } else { GError *error = NULL; gchar *path; GdkPixbuf *pixbuf; GFile *file; cairo_surface_t *surface; cairo_pattern_t *pattern; cairo_t *cr; cairo_matrix_t matrix; file = gtk_css_parse_url (parser, base); if (file == NULL) return FALSE; path = g_file_get_path (file); g_object_unref (file); pixbuf = gdk_pixbuf_new_from_file (path, &error); g_free (path); if (pixbuf == NULL) { _gtk_css_parser_take_error (parser, error); return FALSE; } surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf)); cr = cairo_create (surface); gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0); cairo_paint (cr); pattern = cairo_pattern_create_for_surface (surface); cairo_matrix_init_scale (&matrix, gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf)); cairo_pattern_set_matrix (pattern, &matrix); cairo_surface_destroy (surface); cairo_destroy (cr); g_object_unref (pixbuf); g_value_take_boxed (value, pattern); } return TRUE; } static cairo_status_t surface_write (void *closure, const unsigned char *data, unsigned int length) { g_byte_array_append (closure, data, length); return CAIRO_STATUS_SUCCESS; } static void surface_print (cairo_surface_t *surface, GString * string) { #if CAIRO_HAS_PNG_FUNCTIONS GByteArray *array; char *base64; array = g_byte_array_new (); cairo_surface_write_to_png_stream (surface, surface_write, array); base64 = g_base64_encode (array->data, array->len); g_byte_array_free (array, TRUE); g_string_append (string, "url(\"data:image/png;base64,"); g_string_append (string, base64); g_string_append (string, "\")"); g_free (base64); #else g_string_append (string, "none /* you need cairo png functions enabled to make this work */"); #endif } static void pattern_value_print (const GValue *value, GString *string) { cairo_pattern_t *pattern; cairo_surface_t *surface; pattern = g_value_get_boxed (value); if (pattern == NULL) { g_string_append (string, "none"); return; } switch (cairo_pattern_get_type (pattern)) { case CAIRO_PATTERN_TYPE_SURFACE: if (cairo_pattern_get_surface (pattern, &surface) != CAIRO_STATUS_SUCCESS) { g_assert_not_reached (); } surface_print (surface, string); break; case CAIRO_PATTERN_TYPE_SOLID: case CAIRO_PATTERN_TYPE_LINEAR: case CAIRO_PATTERN_TYPE_RADIAL: default: g_assert_not_reached (); break; } } static gboolean shadow_value_parse (GtkCssParser *parser, GFile *base, GValue *value) { gboolean have_inset, have_color, have_lengths; gdouble hoffset, voffset, blur, spread; GtkSymbolicColor *color; GtkShadow *shadow; guint i; shadow = _gtk_shadow_new (); if (_gtk_css_parser_try (parser, "none", TRUE)) return TRUE; do { have_inset = have_lengths = have_color = FALSE; for (i = 0; i < 3; i++) { if (!have_inset && _gtk_css_parser_try (parser, "inset", TRUE)) { have_inset = TRUE; continue; } if (!have_lengths && _gtk_css_parser_try_double (parser, &hoffset)) { have_lengths = TRUE; if (!_gtk_css_parser_try_double (parser, &voffset)) { _gtk_css_parser_error (parser, "Horizontal and vertical offsets are required"); _gtk_shadow_unref (shadow); return FALSE; } if (!_gtk_css_parser_try_double (parser, &blur)) blur = 0; if (!_gtk_css_parser_try_double (parser, &spread)) spread = 0; continue; } if (!have_color) { have_color = TRUE; /* XXX: the color is optional and UA-defined if it's missing, * but it doesn't really make sense for us... */ color = _gtk_css_parser_read_symbolic_color (parser); if (color == NULL) { _gtk_shadow_unref (shadow); return FALSE; } } } if (!have_color || !have_lengths) { _gtk_css_parser_error (parser, "Must specify at least color and offsets"); _gtk_shadow_unref (shadow); return FALSE; } _gtk_shadow_append (shadow, hoffset, voffset, blur, spread, have_inset, color); gtk_symbolic_color_unref (color); } while (_gtk_css_parser_try (parser, ",", TRUE)); g_value_take_boxed (value, shadow); return TRUE; } static void shadow_value_print (const GValue *value, GString *string) { GtkShadow *shadow; shadow = g_value_get_boxed (value); if (shadow == NULL) g_string_append (string, "none"); else _gtk_shadow_print (shadow, string); } static gboolean background_repeat_value_parse (GtkCssParser *parser, GFile *file, GValue *value) { GtkCssBackgroundRepeat repeat; int style; if (!enum_parse (parser, GTK_TYPE_CSS_BACKGROUND_REPEAT_STYLE, &style)) return FALSE; repeat.repeat = style; g_value_set_boxed (value, &repeat); return TRUE; } static void background_repeat_value_print (const GValue *value, GString *string) { GtkCssBackgroundRepeat *repeat; repeat = g_value_get_boxed (value); enum_print (repeat->repeat, GTK_TYPE_CSS_BACKGROUND_REPEAT_STYLE, string); } static gboolean border_image_repeat_value_parse (GtkCssParser *parser, GFile *file, GValue *value) { GtkCssBorderImageRepeat image_repeat; GtkCssBorderRepeatStyle styles[2]; gint i, v; for (i = 0; i < 2; i++) { if (_gtk_css_parser_try_enum (parser, GTK_TYPE_CSS_BORDER_REPEAT_STYLE, &v)) styles[i] = v; else if (i == 0) { styles[1] = styles[0] = GTK_CSS_REPEAT_STYLE_STRETCH; break; } else styles[i] = styles[0]; } image_repeat.hrepeat = styles[0]; image_repeat.vrepeat = styles[1]; g_value_set_boxed (value, &image_repeat); return TRUE; } static void border_image_repeat_value_print (const GValue *value, GString *string) { GtkCssBorderImageRepeat *image_repeat; image_repeat = g_value_get_boxed (value); enum_print (image_repeat->hrepeat, GTK_TYPE_CSS_BORDER_REPEAT_STYLE, string); if (image_repeat->hrepeat != image_repeat->vrepeat) { g_string_append (string, " "); enum_print (image_repeat->vrepeat, GTK_TYPE_CSS_BORDER_REPEAT_STYLE, string); } } static gboolean enum_value_parse (GtkCssParser *parser, GFile *base, GValue *value) { int v; if (enum_parse (parser, G_VALUE_TYPE (value), &v)) { g_value_set_enum (value, v); return TRUE; } return FALSE; } static void enum_value_print (const GValue *value, GString *string) { enum_print (g_value_get_enum (value), G_VALUE_TYPE (value), string); } static gboolean flags_value_parse (GtkCssParser *parser, GFile *base, GValue *value) { GFlagsClass *flags_class; GFlagsValue *flag_value; guint flags = 0; char *str; flags_class = g_type_class_ref (G_VALUE_TYPE (value)); do { str = _gtk_css_parser_try_ident (parser, TRUE); if (str == NULL) { _gtk_css_parser_error (parser, "Expected an identifier"); g_type_class_unref (flags_class); return FALSE; } flag_value = g_flags_get_value_by_nick (flags_class, str); if (!flag_value) { _gtk_css_parser_error (parser, "Unknown flag value '%s' for type '%s'", str, g_type_name (G_VALUE_TYPE (value))); /* XXX Do we want to return FALSE here? We can get * forward-compatibility for new values this way */ g_free (str); g_type_class_unref (flags_class); return FALSE; } g_free (str); } while (_gtk_css_parser_try (parser, ",", FALSE)); g_type_class_unref (flags_class); g_value_set_enum (value, flags); return TRUE; } static void flags_value_print (const GValue *value, GString *string) { GFlagsClass *flags_class; guint i, flags; flags_class = g_type_class_ref (G_VALUE_TYPE (value)); flags = g_value_get_flags (value); for (i = 0; i < flags_class->n_values; i++) { GFlagsValue *flags_value = &flags_class->values[i]; if (flags & flags_value->value) { if (string->len != 0) g_string_append (string, ", "); g_string_append (string, flags_value->value_nick); } } g_type_class_unref (flags_class); } static gboolean bindings_value_parse (GtkCssParser *parser, GFile *base, GValue *value) { GPtrArray *array; GtkBindingSet *binding_set; char *name; array = g_ptr_array_new (); do { name = _gtk_css_parser_try_ident (parser, TRUE); if (name == NULL) { _gtk_css_parser_error (parser, "Not a valid binding name"); g_ptr_array_free (array, TRUE); return FALSE; } binding_set = gtk_binding_set_find (name); if (!binding_set) { _gtk_css_parser_error (parser, "No binding set named '%s'", name); g_free (name); continue; } g_ptr_array_add (array, binding_set); g_free (name); } while (_gtk_css_parser_try (parser, ",", TRUE)); g_value_take_boxed (value, array); return TRUE; } static void bindings_value_print (const GValue *value, GString *string) { GPtrArray *array; guint i; array = g_value_get_boxed (value); for (i = 0; i < array->len; i++) { GtkBindingSet *binding_set = g_ptr_array_index (array, i); if (i > 0) g_string_append (string, ", "); g_string_append (string, binding_set->set_name); } } static gboolean border_corner_radius_value_parse (GtkCssParser *parser, GFile *base, GValue *value) { GtkCssBorderCornerRadius corner; if (!_gtk_css_parser_try_double (parser, &corner.horizontal)) { _gtk_css_parser_error (parser, "Expected a number"); return FALSE; } else if (corner.horizontal < 0) goto negative; if (!_gtk_css_parser_try_double (parser, &corner.vertical)) corner.vertical = corner.horizontal; else if (corner.vertical < 0) goto negative; g_value_set_boxed (value, &corner); return TRUE; negative: _gtk_css_parser_error (parser, "Border radius values cannot be negative"); return FALSE; } static void border_corner_radius_value_print (const GValue *value, GString *string) { GtkCssBorderCornerRadius *corner; corner = g_value_get_boxed (value); if (corner == NULL) { g_string_append (string, "none"); return; } string_append_double (string, corner->horizontal); if (corner->horizontal != corner->vertical) { g_string_append_c (string, ' '); string_append_double (string, corner->vertical); } } static gboolean transparent_color_value_parse (GtkCssParser *parser, GFile *base, GValue *value) { if (_gtk_css_parser_try (parser, "transparent", TRUE)) { GdkRGBA transparent = { 0, 0, 0, 0 }; g_value_set_boxed (value, &transparent); return TRUE; } return rgba_value_parse (parser, base, value); } /*** API ***/ guint _gtk_style_property_get_count (void) { return __style_property_array ? __style_property_array->len : 0; } GtkStyleProperty * _gtk_style_property_get (guint id) { g_assert (__style_property_array); return g_ptr_array_index (__style_property_array, id); } static void _gtk_style_property_generate_id (GtkStyleProperty *node) { if (__style_property_array == NULL) __style_property_array = g_ptr_array_new (); node->id = __style_property_array->len; g_ptr_array_add (__style_property_array, node); } static void css_string_funcs_init (void) { if (G_LIKELY (parse_funcs != NULL)) return; parse_funcs = g_hash_table_new (NULL, NULL); print_funcs = g_hash_table_new (NULL, NULL); register_conversion_function (GDK_TYPE_RGBA, rgba_value_parse, rgba_value_print); register_conversion_function (GDK_TYPE_COLOR, color_value_parse, color_value_print); register_conversion_function (GTK_TYPE_SYMBOLIC_COLOR, symbolic_color_value_parse, symbolic_color_value_print); register_conversion_function (PANGO_TYPE_FONT_DESCRIPTION, font_description_value_parse, font_description_value_print); register_conversion_function (G_TYPE_BOOLEAN, boolean_value_parse, boolean_value_print); register_conversion_function (G_TYPE_INT, int_value_parse, int_value_print); register_conversion_function (G_TYPE_UINT, uint_value_parse, uint_value_print); register_conversion_function (G_TYPE_DOUBLE, double_value_parse, double_value_print); register_conversion_function (G_TYPE_FLOAT, float_value_parse, float_value_print); register_conversion_function (G_TYPE_STRING, string_value_parse, string_value_print); register_conversion_function (GTK_TYPE_THEMING_ENGINE, theming_engine_value_parse, theming_engine_value_print); register_conversion_function (GTK_TYPE_ANIMATION_DESCRIPTION, animation_description_value_parse, animation_description_value_print); register_conversion_function (GTK_TYPE_BORDER, border_value_parse, border_value_print); register_conversion_function (GTK_TYPE_GRADIENT, gradient_value_parse, gradient_value_print); register_conversion_function (CAIRO_GOBJECT_TYPE_PATTERN, pattern_value_parse, pattern_value_print); register_conversion_function (GTK_TYPE_CSS_BORDER_IMAGE_REPEAT, border_image_repeat_value_parse, border_image_repeat_value_print); register_conversion_function (GTK_TYPE_SHADOW, shadow_value_parse, shadow_value_print); register_conversion_function (G_TYPE_ENUM, enum_value_parse, enum_value_print); register_conversion_function (G_TYPE_FLAGS, flags_value_parse, flags_value_print); register_conversion_function (GTK_TYPE_CSS_BACKGROUND_REPEAT, background_repeat_value_parse, background_repeat_value_print); } gboolean _gtk_style_property_parse_value (GtkStyleProperty *property, GValue *value, GtkCssParser *parser, GFile *base) { GtkStyleParseFunc func; g_return_val_if_fail (value != NULL, FALSE); g_return_val_if_fail (parser != NULL, FALSE); css_string_funcs_init (); if (property) { if (_gtk_css_parser_try (parser, "initial", TRUE)) { /* the initial value can be explicitly specified with the * ‘initial’ keyword which all properties accept. */ g_value_unset (value); g_value_init (value, GTK_TYPE_CSS_SPECIAL_VALUE); g_value_set_enum (value, GTK_CSS_INITIAL); return TRUE; } else if (_gtk_css_parser_try (parser, "inherit", TRUE)) { /* All properties accept the ‘inherit’ value which * explicitly specifies that the value will be determined * by inheritance. The ‘inherit’ value can be used to * strengthen inherited values in the cascade, and it can * also be used on properties that are not normally inherited. */ g_value_unset (value); g_value_init (value, GTK_TYPE_CSS_SPECIAL_VALUE); g_value_set_enum (value, GTK_CSS_INHERIT); return TRUE; } else if (property->property_parse_func) { GError *error = NULL; char *value_str; gboolean success; value_str = _gtk_css_parser_read_value (parser); if (value_str == NULL) return FALSE; success = (*property->property_parse_func) (value_str, value, &error); g_free (value_str); return success; } func = property->parse_func; } else func = NULL; if (func == NULL) func = g_hash_table_lookup (parse_funcs, GSIZE_TO_POINTER (G_VALUE_TYPE (value))); if (func == NULL) func = g_hash_table_lookup (parse_funcs, GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value)))); if (func == NULL) { _gtk_css_parser_error (parser, "Cannot convert to type '%s'", g_type_name (G_VALUE_TYPE (value))); return FALSE; } return (*func) (parser, base, value); } void _gtk_style_property_print_value (GtkStyleProperty *property, const GValue *value, GString *string) { GtkStylePrintFunc func; css_string_funcs_init (); if (G_VALUE_HOLDS (value, GTK_TYPE_CSS_SPECIAL_VALUE)) func = enum_value_print; else if (property) func = property->print_func; else func = NULL; if (func == NULL) func = g_hash_table_lookup (print_funcs, GSIZE_TO_POINTER (G_VALUE_TYPE (value))); if (func == NULL) func = g_hash_table_lookup (print_funcs, GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value)))); if (func == NULL) { char *s = g_strdup_value_contents (value); g_string_append (string, s); g_free (s); return; } func (value, string); } static void _gtk_style_property_default_value (GtkStyleProperty *property, GtkStyleProperties *properties, GtkStateFlags state, GValue *value) { g_value_copy (&property->initial_value, value); } gboolean _gtk_style_property_is_inherit (GtkStyleProperty *property) { g_return_val_if_fail (property != NULL, FALSE); return property->flags & GTK_STYLE_PROPERTY_INHERIT ? TRUE : FALSE; } guint _gtk_style_property_get_id (GtkStyleProperty *property) { g_return_val_if_fail (property != NULL, FALSE); return property->id; } static gboolean resolve_color (GtkStyleProperties *props, GValue *value) { GdkRGBA color; /* Resolve symbolic color to GdkRGBA */ if (!gtk_symbolic_color_resolve (g_value_get_boxed (value), props, &color)) return FALSE; /* Store it back, this is where GdkRGBA caching happens */ g_value_unset (value); g_value_init (value, GDK_TYPE_RGBA); g_value_set_boxed (value, &color); return TRUE; } static gboolean resolve_color_rgb (GtkStyleProperties *props, GValue *value) { GdkColor color = { 0 }; GdkRGBA rgba; if (!gtk_symbolic_color_resolve (g_value_get_boxed (value), props, &rgba)) return FALSE; color.red = rgba.red * 65535. + 0.5; color.green = rgba.green * 65535. + 0.5; color.blue = rgba.blue * 65535. + 0.5; g_value_unset (value); g_value_init (value, GDK_TYPE_COLOR); g_value_set_boxed (value, &color); return TRUE; } static gboolean resolve_win32_theme_part (GtkStyleProperties *props, GValue *value, GValue *value_out, GtkStylePropertyContext *context) { GtkWin32ThemePart *part; cairo_pattern_t *pattern; part = g_value_get_boxed (value); if (part == NULL) return FALSE; pattern = _gtk_win32_theme_part_render (part, context->width, context->height); g_value_take_boxed (value_out, pattern); return TRUE; } static gboolean resolve_gradient (GtkStyleProperties *props, GValue *value) { cairo_pattern_t *gradient; if (!gtk_gradient_resolve (g_value_get_boxed (value), props, &gradient)) return FALSE; /* Store it back, this is where cairo_pattern_t caching happens */ g_value_unset (value); g_value_init (value, CAIRO_GOBJECT_TYPE_PATTERN); g_value_take_boxed (value, gradient); return TRUE; } static gboolean resolve_shadow (GtkStyleProperties *props, GValue *value) { GtkShadow *resolved, *base; base = g_value_get_boxed (value); if (base == NULL) return TRUE; if (_gtk_shadow_get_resolved (base)) return TRUE; resolved = _gtk_shadow_resolve (base, props); if (resolved == NULL) return FALSE; g_value_take_boxed (value, resolved); return TRUE; } static void _gtk_style_property_resolve (GtkStyleProperty *property, GtkStyleProperties *props, GtkStateFlags state, GtkStylePropertyContext *context, GValue *val, GValue *val_out) { if (G_VALUE_TYPE (val) == GTK_TYPE_CSS_SPECIAL_VALUE) { GtkCssSpecialValue special = g_value_get_enum (val); g_value_unset (val); switch (special) { case GTK_CSS_CURRENT_COLOR: g_assert (property->pspec->value_type == GDK_TYPE_RGBA); gtk_style_properties_get_property (props, "color", state, val); break; case GTK_CSS_INHERIT: case GTK_CSS_INITIAL: default: g_assert_not_reached (); } } else if (G_VALUE_TYPE (val) == GTK_TYPE_SYMBOLIC_COLOR) { if (property->pspec->value_type == GDK_TYPE_RGBA) { if (resolve_color (props, val)) goto out; } else if (property->pspec->value_type == GDK_TYPE_COLOR) { if (resolve_color_rgb (props, val)) goto out; } g_value_unset (val); g_value_init (val, property->pspec->value_type); _gtk_style_property_default_value (property, props, state, val); } else if (G_VALUE_TYPE (val) == GDK_TYPE_RGBA) { if (g_value_get_boxed (val) == NULL) _gtk_style_property_default_value (property, props, state, val); } else if (G_VALUE_TYPE (val) == GTK_TYPE_GRADIENT) { g_return_if_fail (property->pspec->value_type == CAIRO_GOBJECT_TYPE_PATTERN); if (!resolve_gradient (props, val)) { g_value_unset (val); g_value_init (val, CAIRO_GOBJECT_TYPE_PATTERN); _gtk_style_property_default_value (property, props, state, val); } } else if (G_VALUE_TYPE (val) == GTK_TYPE_SHADOW) { if (!resolve_shadow (props, val)) _gtk_style_property_default_value (property, props, state, val); } else if (G_VALUE_TYPE (val) == GTK_TYPE_WIN32_THEME_PART) { if (resolve_win32_theme_part (props, val, val_out, context)) return; /* Don't copy val, this sets val_out */ _gtk_style_property_default_value (property, props, state, val); } out: g_value_copy (val, val_out); } const GValue * _gtk_style_property_get_initial_value (GtkStyleProperty *property) { g_return_val_if_fail (property != NULL, NULL); return &property->initial_value; } GParameter * _gtk_style_property_unpack (GtkStyleProperty *property, const GValue *value, guint *n_params) { g_return_val_if_fail (property != NULL, NULL); g_return_val_if_fail (property->unpack_func != NULL, NULL); g_return_val_if_fail (value != NULL, NULL); g_return_val_if_fail (n_params != NULL, NULL); return property->unpack_func (value, n_params); } static void _gtk_style_property_pack (GtkStyleProperty *property, GtkStyleProperties *props, GtkStateFlags state, GtkStylePropertyContext *context, GValue *value) { g_return_if_fail (property != NULL); g_return_if_fail (property->pack_func != NULL); g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props)); g_return_if_fail (G_IS_VALUE (value)); property->pack_func (value, props, state, context); } void _gtk_style_property_assign (GtkStyleProperty *property, GtkStyleProperties *props, GtkStateFlags state, const GValue *value) { g_return_if_fail (GTK_IS_STYLE_PROPERTY (property)); g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props)); g_return_if_fail (value != NULL); if (GTK_IS_CSS_SHORTHAND_PROPERTY (property)) { GParameter *parameters; guint i, n_parameters; parameters = _gtk_style_property_unpack (property, value, &n_parameters); for (i = 0; i < n_parameters; i++) { _gtk_style_property_assign (_gtk_style_property_lookup (parameters[i].name), props, state, ¶meters[i].value); g_value_unset (¶meters[i].value); } g_free (parameters); return; } else if (GTK_IS_CSS_STYLE_PROPERTY (property)) { _gtk_style_properties_set_property_by_property (props, property, state, value); } else { g_assert_not_reached (); } } void _gtk_style_property_query (GtkStyleProperty *property, GtkStyleProperties *props, GtkStateFlags state, GtkStylePropertyContext *context, GValue *value) { const GValue *val; g_return_if_fail (property != NULL); g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props)); g_return_if_fail (context != NULL); g_return_if_fail (value != NULL); val = _gtk_style_properties_peek_property (props, property, state); g_value_init (value, property->pspec->value_type); if (val) _gtk_style_property_resolve (property, props, state, context, (GValue *) val, value); else if (GTK_IS_CSS_SHORTHAND_PROPERTY (property)) _gtk_style_property_pack (property, props, state, context, value); else _gtk_style_property_default_value (property, props, state, value); } #define rgba_init(rgba, r, g, b, a) G_STMT_START{ \ (rgba)->red = (r); \ (rgba)->green = (g); \ (rgba)->blue = (b); \ (rgba)->alpha = (a); \ }G_STMT_END static void gtk_style_property_init_properties (void) { static gboolean initialized = FALSE; GValue value = { 0, }; char *default_font_family[] = { "Sans", NULL }; GdkRGBA rgba; if (G_LIKELY (initialized)) return; initialized = TRUE; g_value_init (&value, GDK_TYPE_RGBA); rgba_init (&rgba, 1, 1, 1, 1); g_value_set_boxed (&value, &rgba); _gtk_style_property_register (g_param_spec_boxed ("color", "Foreground color", "Foreground color", GDK_TYPE_RGBA, 0), GTK_STYLE_PROPERTY_INHERIT, NULL, NULL, NULL, &value); rgba_init (&rgba, 0, 0, 0, 0); g_value_set_boxed (&value, &rgba); _gtk_style_property_register (g_param_spec_boxed ("background-color", "Background color", "Background color", GDK_TYPE_RGBA, 0), 0, NULL, transparent_color_value_parse, NULL, &value); g_value_unset (&value); g_value_init (&value, G_TYPE_STRV); g_value_set_boxed (&value, default_font_family); _gtk_style_property_register (g_param_spec_boxed ("font-family", "Font family", "Font family", G_TYPE_STRV, 0), GTK_STYLE_PROPERTY_INHERIT, NULL, font_family_parse, font_family_value_print, &value); g_value_unset (&value); _gtk_style_property_register (g_param_spec_enum ("font-style", "Font style", "Font style", PANGO_TYPE_STYLE, PANGO_STYLE_NORMAL, 0), GTK_STYLE_PROPERTY_INHERIT, NULL, NULL, NULL, NULL); _gtk_style_property_register (g_param_spec_enum ("font-variant", "Font variant", "Font variant", PANGO_TYPE_VARIANT, PANGO_VARIANT_NORMAL, 0), GTK_STYLE_PROPERTY_INHERIT, NULL, NULL, NULL, NULL); /* xxx: need to parse this properly, ie parse the numbers */ _gtk_style_property_register (g_param_spec_enum ("font-weight", "Font weight", "Font weight", PANGO_TYPE_WEIGHT, PANGO_WEIGHT_NORMAL, 0), GTK_STYLE_PROPERTY_INHERIT, NULL, NULL, NULL, NULL); g_value_init (&value, G_TYPE_DOUBLE); g_value_set_double (&value, 10); _gtk_style_property_register (g_param_spec_double ("font-size", "Font size", "Font size", 0, G_MAXDOUBLE, 0, 0), GTK_STYLE_PROPERTY_INHERIT, NULL, NULL, NULL, &value); g_value_unset (&value); _gtk_style_property_register (g_param_spec_boxed ("text-shadow", "Text shadow", "Text shadow", GTK_TYPE_SHADOW, 0), GTK_STYLE_PROPERTY_INHERIT, NULL, NULL, NULL, NULL); _gtk_style_property_register (g_param_spec_boxed ("icon-shadow", "Icon shadow", "Icon shadow", GTK_TYPE_SHADOW, 0), GTK_STYLE_PROPERTY_INHERIT, NULL, NULL, NULL, NULL); gtk_style_properties_register_property (NULL, g_param_spec_boxed ("box-shadow", "Box shadow", "Box shadow", GTK_TYPE_SHADOW, 0)); gtk_style_properties_register_property (NULL, g_param_spec_int ("margin-top", "margin top", "Margin at top", 0, G_MAXINT, 0, 0)); gtk_style_properties_register_property (NULL, g_param_spec_int ("margin-left", "margin left", "Margin at left", 0, G_MAXINT, 0, 0)); gtk_style_properties_register_property (NULL, g_param_spec_int ("margin-bottom", "margin bottom", "Margin at bottom", 0, G_MAXINT, 0, 0)); gtk_style_properties_register_property (NULL, g_param_spec_int ("margin-right", "margin right", "Margin at right", 0, G_MAXINT, 0, 0)); gtk_style_properties_register_property (NULL, g_param_spec_int ("padding-top", "padding top", "Padding at top", 0, G_MAXINT, 0, 0)); gtk_style_properties_register_property (NULL, g_param_spec_int ("padding-left", "padding left", "Padding at left", 0, G_MAXINT, 0, 0)); gtk_style_properties_register_property (NULL, g_param_spec_int ("padding-bottom", "padding bottom", "Padding at bottom", 0, G_MAXINT, 0, 0)); gtk_style_properties_register_property (NULL, g_param_spec_int ("padding-right", "padding right", "Padding at right", 0, G_MAXINT, 0, 0)); gtk_style_properties_register_property (NULL, g_param_spec_int ("border-top-width", "border top width", "Border width at top", 0, G_MAXINT, 0, 0)); gtk_style_properties_register_property (NULL, g_param_spec_int ("border-left-width", "border left width", "Border width at left", 0, G_MAXINT, 0, 0)); gtk_style_properties_register_property (NULL, g_param_spec_int ("border-bottom-width", "border bottom width", "Border width at bottom", 0, G_MAXINT, 0, 0)); gtk_style_properties_register_property (NULL, g_param_spec_int ("border-right-width", "border right width", "Border width at right", 0, G_MAXINT, 0, 0)); _gtk_style_property_register (g_param_spec_boxed ("border-top-left-radius", "Border top left radius", "Border radius of top left corner, in pixels", GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0), 0, NULL, border_corner_radius_value_parse, border_corner_radius_value_print, NULL); _gtk_style_property_register (g_param_spec_boxed ("border-top-right-radius", "Border top right radius", "Border radius of top right corner, in pixels", GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0), 0, NULL, border_corner_radius_value_parse, border_corner_radius_value_print, NULL); _gtk_style_property_register (g_param_spec_boxed ("border-bottom-right-radius", "Border bottom right radius", "Border radius of bottom right corner, in pixels", GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0), 0, NULL, border_corner_radius_value_parse, border_corner_radius_value_print, NULL); _gtk_style_property_register (g_param_spec_boxed ("border-bottom-left-radius", "Border bottom left radius", "Border radius of bottom left corner, in pixels", GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0), 0, NULL, border_corner_radius_value_parse, border_corner_radius_value_print, NULL); gtk_style_properties_register_property (NULL, g_param_spec_enum ("border-style", "Border style", "Border style", GTK_TYPE_BORDER_STYLE, GTK_BORDER_STYLE_NONE, 0)); gtk_style_properties_register_property (NULL, g_param_spec_enum ("background-clip", "Background clip", "Background clip", GTK_TYPE_CSS_AREA, GTK_CSS_AREA_BORDER_BOX, 0)); gtk_style_properties_register_property (NULL, g_param_spec_enum ("background-origin", "Background origin", "Background origin", GTK_TYPE_CSS_AREA, GTK_CSS_AREA_PADDING_BOX, 0)); g_value_init (&value, GTK_TYPE_CSS_SPECIAL_VALUE); g_value_set_enum (&value, GTK_CSS_CURRENT_COLOR); _gtk_style_property_register (g_param_spec_boxed ("border-top-color", "Border top color", "Border top color", GDK_TYPE_RGBA, 0), 0, NULL, transparent_color_value_parse, NULL, &value); _gtk_style_property_register (g_param_spec_boxed ("border-right-color", "Border right color", "Border right color", GDK_TYPE_RGBA, 0), 0, NULL, transparent_color_value_parse, NULL, &value); _gtk_style_property_register (g_param_spec_boxed ("border-bottom-color", "Border bottom color", "Border bottom color", GDK_TYPE_RGBA, 0), 0, NULL, transparent_color_value_parse, NULL, &value); _gtk_style_property_register (g_param_spec_boxed ("border-left-color", "Border left color", "Border left color", GDK_TYPE_RGBA, 0), 0, NULL, transparent_color_value_parse, NULL, &value); g_value_unset (&value); gtk_style_properties_register_property (NULL, g_param_spec_boxed ("background-image", "Background Image", "Background Image", CAIRO_GOBJECT_TYPE_PATTERN, 0)); gtk_style_properties_register_property (NULL, g_param_spec_boxed ("background-repeat", "Background repeat", "Background repeat", GTK_TYPE_CSS_BACKGROUND_REPEAT, 0)); gtk_style_properties_register_property (NULL, g_param_spec_boxed ("border-image-source", "Border image source", "Border image source", CAIRO_GOBJECT_TYPE_PATTERN, 0)); gtk_style_properties_register_property (NULL, g_param_spec_boxed ("border-image-repeat", "Border image repeat", "Border image repeat", GTK_TYPE_CSS_BORDER_IMAGE_REPEAT, 0)); gtk_style_properties_register_property (NULL, g_param_spec_boxed ("border-image-slice", "Border image slice", "Border image slice", GTK_TYPE_BORDER, 0)); g_value_init (&value, GTK_TYPE_BORDER); _gtk_style_property_register (g_param_spec_boxed ("border-image-width", "Border image width", "Border image width", GTK_TYPE_BORDER, 0), 0, NULL, NULL, NULL, &value); g_value_unset (&value); gtk_style_properties_register_property (NULL, g_param_spec_object ("engine", "Theming Engine", "Theming Engine", GTK_TYPE_THEMING_ENGINE, 0)); gtk_style_properties_register_property (NULL, g_param_spec_boxed ("transition", "Transition animation description", "Transition animation description", GTK_TYPE_ANIMATION_DESCRIPTION, 0)); /* Private property holding the binding sets */ _gtk_style_property_register (g_param_spec_boxed ("gtk-key-bindings", "Key bindings", "Key bindings", G_TYPE_PTR_ARRAY, 0), 0, NULL, bindings_value_parse, bindings_value_print, NULL); /* initialize shorthands last, they depend on the real properties existing */ _gtk_css_shorthand_property_init_properties (); } /** * _gtk_style_property_lookup: * @name: name of the property to lookup * * Looks up the CSS property with the given @name. If no such * property exists, %NULL is returned. * * Returns: (transfer none): The property or %NULL if no * property with the given name exists. **/ GtkStyleProperty * _gtk_style_property_lookup (const char *name) { GtkStylePropertyClass *klass; g_return_val_if_fail (name != NULL, NULL); gtk_style_property_init_properties (); klass = g_type_class_peek (GTK_TYPE_STYLE_PROPERTY); return g_hash_table_lookup (klass->properties, name); } /** * _gtk_style_property_get_name: * @property: the property to query * * Gets the name of the given property. * * Returns: the name of the property **/ const char * _gtk_style_property_get_name (GtkStyleProperty *property) { g_return_val_if_fail (GTK_IS_STYLE_PROPERTY (property), NULL); return property->name; } /** * _gtk_style_property_get_value_type: * @property: the property to query * * Gets the value type of the @property, if the property is usable * in public API via _gtk_style_property_assign() and * _gtk_style_property_query(). If the @property is not usable in that * way, %G_TYPE_NONE is returned. * * Returns: the value type in use or %G_TYPE_NONE if none. **/ GType _gtk_style_property_get_value_type (GtkStyleProperty *property) { g_return_val_if_fail (GTK_IS_STYLE_PROPERTY (property), G_TYPE_NONE); return property->value_type; } void _gtk_style_property_register (GParamSpec *pspec, GtkStylePropertyFlags flags, GtkStylePropertyParser property_parse_func, GtkStyleParseFunc parse_func, GtkStylePrintFunc print_func, const GValue * initial_value) { GtkStyleProperty *node; node = g_object_new (GTK_TYPE_CSS_STYLE_PROPERTY, "name", pspec->name, "value-type", pspec->value_type, NULL); node->flags = flags; node->pspec = pspec; node->property_parse_func = property_parse_func; node->parse_func = parse_func; node->print_func = print_func; _gtk_style_property_generate_id (node); /* initialize the initial value */ if (initial_value) { g_value_init (&node->initial_value, G_VALUE_TYPE (initial_value)); g_value_copy (initial_value, &node->initial_value); } else { g_value_init (&node->initial_value, pspec->value_type); if (pspec->value_type == GTK_TYPE_THEMING_ENGINE) g_value_set_object (&node->initial_value, gtk_theming_engine_load (NULL)); else if (pspec->value_type == PANGO_TYPE_FONT_DESCRIPTION) g_value_take_boxed (&node->initial_value, pango_font_description_from_string ("Sans 10")); else if (pspec->value_type == GDK_TYPE_RGBA) { GdkRGBA color; gdk_rgba_parse (&color, "pink"); g_value_set_boxed (&node->initial_value, &color); } else if (pspec->value_type == GTK_TYPE_BORDER) { g_value_take_boxed (&node->initial_value, gtk_border_new ()); } else g_param_value_set_default (pspec, &node->initial_value); } }