/* * Copyright © 2011 Red Hat Inc. * * 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.1 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, see . * * Authors: Benjamin Otte */ #include "config.h" #include "gtkcsscustompropertyprivate.h" #include #include "gtkcssstylefuncsprivate.h" #include "gtkstylepropertiesprivate.h" #include "gtkthemingengine.h" G_DEFINE_TYPE (GtkCssCustomProperty, _gtk_css_custom_property, GTK_TYPE_CSS_STYLE_PROPERTY) static GType gtk_css_custom_property_get_specified_type (GParamSpec *pspec) { if (pspec->value_type == GDK_TYPE_RGBA || pspec->value_type == GDK_TYPE_COLOR) return GTK_TYPE_SYMBOLIC_COLOR; else return pspec->value_type; } static GtkCssValue * gtk_css_custom_property_parse_value (GtkStyleProperty *property, GtkCssParser *parser, GFile *base) { GtkCssCustomProperty *custom = GTK_CSS_CUSTOM_PROPERTY (property); GValue value = G_VALUE_INIT; GtkCssValue *result; gboolean success; if (custom->property_parse_func) { GError *error = NULL; char *value_str; g_value_init (&value, _gtk_style_property_get_value_type (property)); value_str = _gtk_css_parser_read_value (parser); if (value_str != NULL) { success = (* custom->property_parse_func) (value_str, &value, &error); g_free (value_str); } else success = FALSE; } else { g_value_init (&value, gtk_css_custom_property_get_specified_type (custom->pspec)); success = _gtk_css_style_parse_value (&value, parser, base); } if (!success) { g_value_unset (&value); return NULL; } result = _gtk_css_value_new_from_gvalue (&value); g_value_unset (&value); return result; } static void gtk_css_custom_property_query (GtkStyleProperty *property, GValue *value, GtkStyleQueryFunc query_func, gpointer query_data) { GtkCssStyleProperty *style = GTK_CSS_STYLE_PROPERTY (property); GtkCssValue *css_value; css_value = (* query_func) (_gtk_css_style_property_get_id (style), query_data); if (css_value == NULL) css_value =_gtk_css_style_property_get_initial_value (style); _gtk_css_value_init_gvalue (css_value, value); g_assert (GTK_CSS_CUSTOM_PROPERTY (property)->pspec->value_type == G_VALUE_TYPE (value)); } static void gtk_css_custom_property_assign (GtkStyleProperty *property, GtkStyleProperties *props, GtkStateFlags state, const GValue *value) { GtkCssValue *css_value = _gtk_css_value_new_from_gvalue (value); _gtk_style_properties_set_property_by_property (props, GTK_CSS_STYLE_PROPERTY (property), state, css_value); _gtk_css_value_unref (css_value); } static void _gtk_css_custom_property_class_init (GtkCssCustomPropertyClass *klass) { GtkStylePropertyClass *property_class = GTK_STYLE_PROPERTY_CLASS (klass); property_class->parse_value = gtk_css_custom_property_parse_value; property_class->query = gtk_css_custom_property_query; property_class->assign = gtk_css_custom_property_assign; } static GtkCssValue * gtk_css_custom_property_compute_value (GtkCssStyleProperty *property, GtkStyleContext *context, GtkCssValue *specified) { GtkCssCustomProperty *custom = GTK_CSS_CUSTOM_PROPERTY (property); return _gtk_css_style_compute_value (context, custom->pspec->value_type, specified); } static void _gtk_css_custom_property_init (GtkCssCustomProperty *custom) { GtkCssStyleProperty *style = GTK_CSS_STYLE_PROPERTY (custom); style->compute_value = gtk_css_custom_property_compute_value; } static GtkCssValue * gtk_css_custom_property_create_initial_value (GParamSpec *pspec) { GValue value = G_VALUE_INIT; GtkCssValue *result; g_value_init (&value, pspec->value_type); if (pspec->value_type == GTK_TYPE_THEMING_ENGINE) g_value_set_object (&value, gtk_theming_engine_load (NULL)); else if (pspec->value_type == PANGO_TYPE_FONT_DESCRIPTION) g_value_take_boxed (&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 (&value, &color); } else if (pspec->value_type == GDK_TYPE_COLOR) { GdkColor color; gdk_color_parse ("pink", &color); g_value_set_boxed (&value, &color); } else if (pspec->value_type == GTK_TYPE_BORDER) { g_value_take_boxed (&value, gtk_border_new ()); } else g_param_value_set_default (pspec, &value); result = _gtk_css_value_new_from_gvalue (&value); g_value_unset (&value); return result; } /* Property registration functions */ /** * gtk_theming_engine_register_property: (skip) * @name_space: namespace for the property name * @parse_func: parsing function to use, or %NULL * @pspec: the #GParamSpec for the new property * * Registers a property so it can be used in the CSS file format, * on the CSS file the property will look like * "-${@name_space}-${property_name}". being * ${property_name} the given to @pspec. @name_space will usually * be the theme engine name. * * For any type a @parse_func may be provided, being this function * used for turning any property value (between ':' and ';') in * CSS to the #GValue needed. For basic types there is already * builtin parsing support, so %NULL may be provided for these * cases. * * * Engines must ensure property registration happens exactly once, * usually GTK+ deals with theming engines as singletons, so this * should be guaranteed to happen once, but bear this in mind * when creating #GtkThemeEngines yourself. * * * * In order to make use of the custom registered properties in * the CSS file, make sure the engine is loaded first by specifying * the engine property, either in a previous rule or within the same * one. * * * { * engine: someengine; * -SomeEngine-custom-property: 2; * } * * * * Since: 3.0 **/ void gtk_theming_engine_register_property (const gchar *name_space, GtkStylePropertyParser parse_func, GParamSpec *pspec) { GtkCssCustomProperty *node; GtkCssValue *initial; gchar *name; g_return_if_fail (name_space != NULL); g_return_if_fail (strchr (name_space, ' ') == NULL); g_return_if_fail (G_IS_PARAM_SPEC (pspec)); name = g_strdup_printf ("-%s-%s", name_space, pspec->name); /* This also initializes the default properties */ if (_gtk_style_property_lookup (pspec->name)) { g_warning ("a property with name '%s' already exists", name); g_free (name); return; } initial = gtk_css_custom_property_create_initial_value (pspec); node = g_object_new (GTK_TYPE_CSS_CUSTOM_PROPERTY, "initial-value", initial, "name", name, "value-type", pspec->value_type, NULL); node->pspec = pspec; node->property_parse_func = parse_func; _gtk_css_value_unref (initial); g_free (name); } /** * gtk_style_properties_register_property: (skip) * @parse_func: parsing function to use, or %NULL * @pspec: the #GParamSpec for the new property * * Registers a property so it can be used in the CSS file format. * This function is the low-level equivalent of * gtk_theming_engine_register_property(), if you are implementing * a theming engine, you want to use that function instead. * * Since: 3.0 **/ void gtk_style_properties_register_property (GtkStylePropertyParser parse_func, GParamSpec *pspec) { GtkCssCustomProperty *node; GtkCssValue *initial; g_return_if_fail (G_IS_PARAM_SPEC (pspec)); /* This also initializes the default properties */ if (_gtk_style_property_lookup (pspec->name)) { g_warning ("a property with name '%s' already exists", pspec->name); return; } initial = gtk_css_custom_property_create_initial_value (pspec); node = g_object_new (GTK_TYPE_CSS_CUSTOM_PROPERTY, "initial-value", initial, "name", pspec->name, "value-type", pspec->value_type, NULL); node->pspec = pspec; node->property_parse_func = parse_func; _gtk_css_value_unref (initial); } /** * gtk_style_properties_lookup_property: (skip) * @property_name: property name to look up * @parse_func: (out): return location for the parse function * @pspec: (out) (transfer none): return location for the #GParamSpec * * Returns %TRUE if a property has been registered, if @pspec or * @parse_func are not %NULL, the #GParamSpec and parsing function * will be respectively returned. * * Returns: %TRUE if the property is registered, %FALSE otherwise * * Since: 3.0 **/ gboolean gtk_style_properties_lookup_property (const gchar *property_name, GtkStylePropertyParser *parse_func, GParamSpec **pspec) { GtkStyleProperty *node; gboolean found = FALSE; g_return_val_if_fail (property_name != NULL, FALSE); node = _gtk_style_property_lookup (property_name); if (GTK_IS_CSS_CUSTOM_PROPERTY (node)) { GtkCssCustomProperty *custom = GTK_CSS_CUSTOM_PROPERTY (node); if (pspec) *pspec = custom->pspec; if (parse_func) *parse_func = custom->property_parse_func; found = TRUE; } return found; }