]> Pileus Git - ~andy/gtk/blob - gtk/gtkcsscustomproperty.c
customproperty: Implement assign vfunc
[~andy/gtk] / gtk / gtkcsscustomproperty.c
1 /*
2  * Copyright © 2011 Red Hat Inc.
3  *
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.1 of the License, or (at your option) any later version.
8  *
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.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16  *
17  * Authors: Benjamin Otte <otte@gnome.org>
18  */
19
20 #include "config.h"
21
22 #include "gtkcsscustompropertyprivate.h"
23
24 #include <string.h>
25
26 #include "gtkcssstylefuncsprivate.h"
27 #include "gtkstylepropertiesprivate.h"
28 #include "gtkthemingengine.h"
29
30 G_DEFINE_TYPE (GtkCssCustomProperty, _gtk_css_custom_property, GTK_TYPE_CSS_STYLE_PROPERTY)
31
32 static GType
33 gtk_css_custom_property_get_specified_type (GParamSpec *pspec)
34 {
35   if (pspec->value_type == GDK_TYPE_RGBA ||
36       pspec->value_type == GDK_TYPE_COLOR)
37     return GTK_TYPE_SYMBOLIC_COLOR;
38   else
39     return pspec->value_type;
40 }
41
42 static GtkCssValue *
43 gtk_css_custom_property_parse_value (GtkStyleProperty *property,
44                                      GtkCssParser     *parser,
45                                      GFile            *base)
46 {
47   GtkCssCustomProperty *custom = GTK_CSS_CUSTOM_PROPERTY (property);
48   GValue value = G_VALUE_INIT;
49   GtkCssValue *result;
50   gboolean success;
51
52   if (custom->property_parse_func)
53     {
54       GError *error = NULL;
55       char *value_str;
56       
57       g_value_init (&value, _gtk_style_property_get_value_type (property));
58
59       value_str = _gtk_css_parser_read_value (parser);
60       if (value_str != NULL)
61         {
62           success = (* custom->property_parse_func) (value_str, &value, &error);
63           g_free (value_str);
64         }
65       else
66         success = FALSE;
67     }
68   else
69     {
70       g_value_init (&value, gtk_css_custom_property_get_specified_type (custom->pspec));
71
72       success = _gtk_css_style_parse_value (&value, parser, base);
73     }
74
75   if (!success)
76     {
77       g_value_unset (&value);
78       return NULL;
79     }
80
81   result = _gtk_css_value_new_from_gvalue (&value);
82   g_value_unset (&value);
83
84   return result;
85 }
86
87 static void
88 gtk_css_custom_property_query (GtkStyleProperty   *property,
89                                GValue             *value,
90                                GtkStyleQueryFunc   query_func,
91                                gpointer            query_data)
92 {
93   GtkCssStyleProperty *style = GTK_CSS_STYLE_PROPERTY (property);
94   GtkCssValue *css_value;
95   
96   css_value = (* query_func) (_gtk_css_style_property_get_id (style), query_data);
97   if (css_value == NULL)
98     css_value =_gtk_css_style_property_get_initial_value (style);
99
100   _gtk_css_value_init_gvalue (css_value, value);
101   g_assert (GTK_CSS_CUSTOM_PROPERTY (property)->pspec->value_type == G_VALUE_TYPE (value));
102 }
103
104 static void
105 gtk_css_custom_property_assign (GtkStyleProperty   *property,
106                                 GtkStyleProperties *props,
107                                 GtkStateFlags       state,
108                                 const GValue       *value)
109 {
110   GtkCssValue *css_value = _gtk_css_value_new_from_gvalue (value);
111   _gtk_style_properties_set_property_by_property (props,
112                                                   GTK_CSS_STYLE_PROPERTY (property),
113                                                   state,
114                                                   css_value);
115   _gtk_css_value_unref (css_value);
116 }
117
118 static void
119 _gtk_css_custom_property_class_init (GtkCssCustomPropertyClass *klass)
120 {
121   GtkStylePropertyClass *property_class = GTK_STYLE_PROPERTY_CLASS (klass);
122
123   property_class->parse_value = gtk_css_custom_property_parse_value;
124   property_class->query = gtk_css_custom_property_query;
125   property_class->assign = gtk_css_custom_property_assign;
126 }
127
128 static GtkCssValue *
129 gtk_css_custom_property_compute_value (GtkCssStyleProperty *property,
130                                        GtkStyleContext     *context,
131                                        GtkCssValue         *specified)
132 {
133   GtkCssCustomProperty *custom = GTK_CSS_CUSTOM_PROPERTY (property);
134
135   return _gtk_css_style_compute_value (context, custom->pspec->value_type, specified);
136 }
137
138 static void
139 _gtk_css_custom_property_init (GtkCssCustomProperty *custom)
140 {
141   GtkCssStyleProperty *style = GTK_CSS_STYLE_PROPERTY (custom);
142
143   style->compute_value = gtk_css_custom_property_compute_value;
144 }
145
146 static GtkCssValue *
147 gtk_css_custom_property_create_initial_value (GParamSpec *pspec)
148 {
149   GValue value = G_VALUE_INIT;
150   GtkCssValue *result;
151
152   g_value_init (&value, pspec->value_type);
153
154   if (pspec->value_type == GTK_TYPE_THEMING_ENGINE)
155     g_value_set_object (&value, gtk_theming_engine_load (NULL));
156   else if (pspec->value_type == PANGO_TYPE_FONT_DESCRIPTION)
157     g_value_take_boxed (&value, pango_font_description_from_string ("Sans 10"));
158   else if (pspec->value_type == GDK_TYPE_RGBA)
159     {
160       GdkRGBA color;
161       gdk_rgba_parse (&color, "pink");
162       g_value_set_boxed (&value, &color);
163     }
164   else if (pspec->value_type == GDK_TYPE_COLOR)
165     {
166       GdkColor color;
167       gdk_color_parse ("pink", &color);
168       g_value_set_boxed (&value, &color);
169     }
170   else if (pspec->value_type == GTK_TYPE_BORDER)
171     {
172       g_value_take_boxed (&value, gtk_border_new ());
173     }
174   else
175     g_param_value_set_default (pspec, &value);
176
177   result = _gtk_css_value_new_from_gvalue (&value);
178   g_value_unset (&value);
179
180   return result;
181 }
182
183 /* Property registration functions */
184
185 /**
186  * gtk_theming_engine_register_property: (skip)
187  * @name_space: namespace for the property name
188  * @parse_func: parsing function to use, or %NULL
189  * @pspec: the #GParamSpec for the new property
190  *
191  * Registers a property so it can be used in the CSS file format,
192  * on the CSS file the property will look like
193  * "-${@name_space}-${property_name}". being
194  * ${property_name} the given to @pspec. @name_space will usually
195  * be the theme engine name.
196  *
197  * For any type a @parse_func may be provided, being this function
198  * used for turning any property value (between ':' and ';') in
199  * CSS to the #GValue needed. For basic types there is already
200  * builtin parsing support, so %NULL may be provided for these
201  * cases.
202  *
203  * <note>
204  * Engines must ensure property registration happens exactly once,
205  * usually GTK+ deals with theming engines as singletons, so this
206  * should be guaranteed to happen once, but bear this in mind
207  * when creating #GtkThemeEngine<!-- -->s yourself.
208  * </note>
209  *
210  * <note>
211  * In order to make use of the custom registered properties in
212  * the CSS file, make sure the engine is loaded first by specifying
213  * the engine property, either in a previous rule or within the same
214  * one.
215  * <programlisting>
216  * &ast; {
217  *     engine: someengine;
218  *     -SomeEngine-custom-property: 2;
219  * }
220  * </programlisting>
221  * </note>
222  *
223  * Since: 3.0
224  **/
225 void
226 gtk_theming_engine_register_property (const gchar            *name_space,
227                                       GtkStylePropertyParser  parse_func,
228                                       GParamSpec             *pspec)
229 {
230   GtkCssCustomProperty *node;
231   GtkCssValue *initial;
232   gchar *name;
233
234   g_return_if_fail (name_space != NULL);
235   g_return_if_fail (strchr (name_space, ' ') == NULL);
236   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
237
238   name = g_strdup_printf ("-%s-%s", name_space, pspec->name);
239
240   /* This also initializes the default properties */
241   if (_gtk_style_property_lookup (pspec->name))
242     {
243       g_warning ("a property with name '%s' already exists", name);
244       g_free (name);
245       return;
246     }
247   
248   initial = gtk_css_custom_property_create_initial_value (pspec);
249
250   node = g_object_new (GTK_TYPE_CSS_CUSTOM_PROPERTY,
251                        "initial-value", initial,
252                        "name", name,
253                        "value-type", pspec->value_type,
254                        NULL);
255   node->pspec = pspec;
256   node->property_parse_func = parse_func;
257
258   _gtk_css_value_unref (initial);
259   g_free (name);
260 }
261
262 /**
263  * gtk_style_properties_register_property: (skip)
264  * @parse_func: parsing function to use, or %NULL
265  * @pspec: the #GParamSpec for the new property
266  *
267  * Registers a property so it can be used in the CSS file format.
268  * This function is the low-level equivalent of
269  * gtk_theming_engine_register_property(), if you are implementing
270  * a theming engine, you want to use that function instead.
271  *
272  * Since: 3.0
273  **/
274 void
275 gtk_style_properties_register_property (GtkStylePropertyParser  parse_func,
276                                         GParamSpec             *pspec)
277 {
278   GtkCssCustomProperty *node;
279   GtkCssValue *initial;
280
281   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
282
283   /* This also initializes the default properties */
284   if (_gtk_style_property_lookup (pspec->name))
285     {
286       g_warning ("a property with name '%s' already exists", pspec->name);
287       return;
288     }
289   
290   initial = gtk_css_custom_property_create_initial_value (pspec);
291
292   node = g_object_new (GTK_TYPE_CSS_CUSTOM_PROPERTY,
293                        "initial-value", initial,
294                        "name", pspec->name,
295                        "value-type", pspec->value_type,
296                        NULL);
297   node->pspec = pspec;
298   node->property_parse_func = parse_func;
299
300   _gtk_css_value_unref (initial);
301 }
302
303 /**
304  * gtk_style_properties_lookup_property: (skip)
305  * @property_name: property name to look up
306  * @parse_func: (out): return location for the parse function
307  * @pspec: (out) (transfer none): return location for the #GParamSpec
308  *
309  * Returns %TRUE if a property has been registered, if @pspec or
310  * @parse_func are not %NULL, the #GParamSpec and parsing function
311  * will be respectively returned.
312  *
313  * Returns: %TRUE if the property is registered, %FALSE otherwise
314  *
315  * Since: 3.0
316  **/
317 gboolean
318 gtk_style_properties_lookup_property (const gchar             *property_name,
319                                       GtkStylePropertyParser  *parse_func,
320                                       GParamSpec             **pspec)
321 {
322   GtkStyleProperty *node;
323   gboolean found = FALSE;
324
325   g_return_val_if_fail (property_name != NULL, FALSE);
326
327   node = _gtk_style_property_lookup (property_name);
328
329   if (GTK_IS_CSS_CUSTOM_PROPERTY (node))
330     {
331       GtkCssCustomProperty *custom = GTK_CSS_CUSTOM_PROPERTY (node);
332
333       if (pspec)
334         *pspec = custom->pspec;
335
336       if (parse_func)
337         *parse_func = custom->property_parse_func;
338
339       found = TRUE;
340     }
341
342   return found;
343 }
344