]> Pileus Git - ~andy/gtk/blob - gtk/gtkcssstyleproperty.c
styleproperty: Let parse_value() initialize the value
[~andy/gtk] / gtk / gtkcssstyleproperty.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, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  *
18  * Authors: Benjamin Otte <otte@gnome.org>
19  */
20
21 #include "config.h"
22
23 #include "gtkcssstylepropertyprivate.h"
24
25 #include "gtkcssstylefuncsprivate.h"
26 #include "gtkcsstypesprivate.h"
27 #include "gtkintl.h"
28 #include "gtkprivatetypebuiltins.h"
29 #include "gtkstylepropertiesprivate.h"
30
31 /* for resolvage */
32 #include <cairo-gobject.h>
33 #include "gtkgradient.h"
34 #include "gtkshadowprivate.h"
35 #include "gtkwin32themeprivate.h"
36
37
38 enum {
39   PROP_0,
40   PROP_ID,
41   PROP_INHERIT,
42   PROP_INITIAL
43 };
44
45 G_DEFINE_TYPE (GtkCssStyleProperty, _gtk_css_style_property, GTK_TYPE_STYLE_PROPERTY)
46
47 static void
48 gtk_css_style_property_constructed (GObject *object)
49 {
50   GtkCssStyleProperty *property = GTK_CSS_STYLE_PROPERTY (object);
51   GtkCssStylePropertyClass *klass = GTK_CSS_STYLE_PROPERTY_GET_CLASS (property);
52
53   property->id = klass->style_properties->len;
54   g_ptr_array_add (klass->style_properties, property);
55
56   G_OBJECT_CLASS (_gtk_css_style_property_parent_class)->constructed (object);
57 }
58
59 static void
60 gtk_css_style_property_set_property (GObject      *object,
61                                      guint         prop_id,
62                                      const GValue *value,
63                                      GParamSpec   *pspec)
64 {
65   GtkCssStyleProperty *property = GTK_CSS_STYLE_PROPERTY (object);
66   const GValue *initial;
67
68   switch (prop_id)
69     {
70     case PROP_INHERIT:
71       property->inherit = g_value_get_boolean (value);
72       break;
73     case PROP_INITIAL:
74       initial = g_value_get_boxed (value);
75       g_assert (initial);
76       g_value_init (&property->initial_value, G_VALUE_TYPE (initial));
77       g_value_copy (initial, &property->initial_value);
78       break;
79     default:
80       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
81       break;
82     }
83 }
84
85 static void
86 gtk_css_style_property_get_property (GObject    *object,
87                                      guint       prop_id,
88                                      GValue     *value,
89                                      GParamSpec *pspec)
90 {
91   GtkCssStyleProperty *property = GTK_CSS_STYLE_PROPERTY (object);
92
93   switch (prop_id)
94     {
95     case PROP_ID:
96       g_value_set_boolean (value, property->id);
97       break;
98     case PROP_INHERIT:
99       g_value_set_boolean (value, property->inherit);
100       break;
101     case PROP_INITIAL:
102       g_value_set_boxed (value, &property->initial_value);
103       break;
104     default:
105       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
106       break;
107     }
108 }
109
110 static void
111 _gtk_css_style_property_assign (GtkStyleProperty   *property,
112                                 GtkStyleProperties *props,
113                                 GtkStateFlags       state,
114                                 const GValue       *value)
115 {
116   _gtk_style_properties_set_property_by_property (props,
117                                                   GTK_CSS_STYLE_PROPERTY (property),
118                                                   state,
119                                                   value);
120 }
121
122 static void
123 _gtk_style_property_default_value (GtkStyleProperty   *property,
124                                    GtkStyleProperties *properties,
125                                    GtkStateFlags       state,
126                                    GValue             *value)
127 {
128   g_value_copy (_gtk_css_style_property_get_initial_value (GTK_CSS_STYLE_PROPERTY (property)), value);
129 }
130
131 static gboolean
132 resolve_color (GtkStyleProperties *props,
133                GValue             *value)
134 {
135   GdkRGBA color;
136
137   /* Resolve symbolic color to GdkRGBA */
138   if (!gtk_symbolic_color_resolve (g_value_get_boxed (value), props, &color))
139     return FALSE;
140
141   /* Store it back, this is where GdkRGBA caching happens */
142   g_value_unset (value);
143   g_value_init (value, GDK_TYPE_RGBA);
144   g_value_set_boxed (value, &color);
145
146   return TRUE;
147 }
148
149 static gboolean
150 resolve_color_rgb (GtkStyleProperties *props,
151                    GValue             *value)
152 {
153   GdkColor color = { 0 };
154   GdkRGBA rgba;
155
156   if (!gtk_symbolic_color_resolve (g_value_get_boxed (value), props, &rgba))
157     return FALSE;
158
159   color.red = rgba.red * 65535. + 0.5;
160   color.green = rgba.green * 65535. + 0.5;
161   color.blue = rgba.blue * 65535. + 0.5;
162
163   g_value_unset (value);
164   g_value_init (value, GDK_TYPE_COLOR);
165   g_value_set_boxed (value, &color);
166
167   return TRUE;
168 }
169
170 static gboolean
171 resolve_win32_theme_part (GtkStyleProperties *props,
172                           GValue             *value,
173                           GValue             *value_out,
174                           GtkStylePropertyContext *context)
175 {
176   GtkWin32ThemePart  *part;
177   cairo_pattern_t *pattern;
178
179   part = g_value_get_boxed (value);
180   if (part == NULL)
181     return FALSE;
182
183   pattern = _gtk_win32_theme_part_render (part, context->width, context->height);
184
185   g_value_take_boxed (value_out, pattern);
186
187   return TRUE;
188 }
189
190
191 static gboolean
192 resolve_gradient (GtkStyleProperties *props,
193                   GValue             *value)
194 {
195   cairo_pattern_t *gradient;
196
197   if (!gtk_gradient_resolve (g_value_get_boxed (value), props, &gradient))
198     return FALSE;
199
200   /* Store it back, this is where cairo_pattern_t caching happens */
201   g_value_unset (value);
202   g_value_init (value, CAIRO_GOBJECT_TYPE_PATTERN);
203   g_value_take_boxed (value, gradient);
204
205   return TRUE;
206 }
207
208 static gboolean
209 resolve_shadow (GtkStyleProperties *props,
210                 GValue *value)
211 {
212   GtkShadow *resolved, *base;
213
214   base = g_value_get_boxed (value);
215
216   if (base == NULL)
217     return TRUE;
218   
219   if (_gtk_shadow_get_resolved (base))
220     return TRUE;
221
222   resolved = _gtk_shadow_resolve (base, props);
223   if (resolved == NULL)
224     return FALSE;
225
226   g_value_take_boxed (value, resolved);
227
228   return TRUE;
229 }
230
231 static void
232 _gtk_style_property_resolve (GtkStyleProperty       *property,
233                              GtkStyleProperties     *props,
234                              GtkStateFlags           state,
235                              GtkStylePropertyContext *context,
236                              GValue                 *val,
237                              GValue                 *val_out)
238 {
239   if (G_VALUE_TYPE (val) == GTK_TYPE_CSS_SPECIAL_VALUE)
240     {
241       GtkCssSpecialValue special = g_value_get_enum (val);
242
243       g_value_unset (val);
244       switch (special)
245         {
246         case GTK_CSS_CURRENT_COLOR:
247           g_assert (_gtk_style_property_get_value_type (property) == GDK_TYPE_RGBA);
248           gtk_style_properties_get_property (props, "color", state, val);
249           break;
250         case GTK_CSS_INHERIT:
251         case GTK_CSS_INITIAL:
252         default:
253           g_assert_not_reached ();
254         }
255     }
256   else if (G_VALUE_TYPE (val) == GTK_TYPE_SYMBOLIC_COLOR)
257     {
258       if (_gtk_style_property_get_value_type (property) == GDK_TYPE_RGBA)
259         {
260           if (resolve_color (props, val))
261             goto out;
262         }
263       else if (_gtk_style_property_get_value_type (property) == GDK_TYPE_COLOR)
264         {
265           if (resolve_color_rgb (props, val))
266             goto out;
267         }
268       
269       g_value_unset (val);
270       g_value_init (val, _gtk_style_property_get_value_type (property));
271       _gtk_style_property_default_value (property, props, state, val);
272     }
273   else if (G_VALUE_TYPE (val) == GDK_TYPE_RGBA)
274     {
275       if (g_value_get_boxed (val) == NULL)
276         _gtk_style_property_default_value (property, props, state, val);
277     }
278   else if (G_VALUE_TYPE (val) == GTK_TYPE_GRADIENT)
279     {
280       g_return_if_fail (_gtk_style_property_get_value_type (property) == CAIRO_GOBJECT_TYPE_PATTERN);
281
282       if (!resolve_gradient (props, val))
283         {
284           g_value_unset (val);
285           g_value_init (val, CAIRO_GOBJECT_TYPE_PATTERN);
286           _gtk_style_property_default_value (property, props, state, val);
287         }
288     }
289   else if (G_VALUE_TYPE (val) == GTK_TYPE_SHADOW)
290     {
291       if (!resolve_shadow (props, val))
292         _gtk_style_property_default_value (property, props, state, val);
293     }
294   else if (G_VALUE_TYPE (val) == GTK_TYPE_WIN32_THEME_PART)
295     {
296       if (resolve_win32_theme_part (props, val, val_out, context))
297         return; /* Don't copy val, this sets val_out */
298       _gtk_style_property_default_value (property, props, state, val);
299     }
300
301  out:
302   g_value_copy (val, val_out);
303 }
304
305 static void
306 _gtk_css_style_property_query (GtkStyleProperty   *property,
307                                GtkStyleProperties *props,
308                                GtkStateFlags       state,
309                                GtkStylePropertyContext *context,
310                                GValue             *value)
311 {
312   const GValue *val;
313   
314   val = _gtk_style_properties_peek_property (props, GTK_CSS_STYLE_PROPERTY (property), state);
315   if (val)
316     _gtk_style_property_resolve (property, props, state, context, (GValue *) val, value);
317   else
318     _gtk_style_property_default_value (property, props, state, value);
319 }
320
321 static gboolean
322 gtk_css_style_property_parse_value (GtkStyleProperty *property,
323                                     GValue           *value,
324                                     GtkCssParser     *parser,
325                                     GFile            *base)
326 {
327   gboolean success;
328
329   if (_gtk_css_parser_try (parser, "initial", TRUE))
330     {
331       /* the initial value can be explicitly specified with the
332        * ‘initial’ keyword which all properties accept.
333        */
334       g_value_init (value, GTK_TYPE_CSS_SPECIAL_VALUE);
335       g_value_set_enum (value, GTK_CSS_INITIAL);
336       return TRUE;
337     }
338   else if (_gtk_css_parser_try (parser, "inherit", TRUE))
339     {
340       /* All properties accept the ‘inherit’ value which
341        * explicitly specifies that the value will be determined
342        * by inheritance. The ‘inherit’ value can be used to
343        * strengthen inherited values in the cascade, and it can
344        * also be used on properties that are not normally inherited.
345        */
346       g_value_init (value, GTK_TYPE_CSS_SPECIAL_VALUE);
347       g_value_set_enum (value, GTK_CSS_INHERIT);
348       return TRUE;
349     }
350   else if (property->property_parse_func)
351     {
352       GError *error = NULL;
353       char *value_str;
354       
355       value_str = _gtk_css_parser_read_value (parser);
356       if (value_str == NULL)
357         return FALSE;
358       
359       g_value_init (value, _gtk_style_property_get_value_type (property));
360       success = (*property->property_parse_func) (value_str, value, &error);
361
362       g_free (value_str);
363       if (!success)
364         g_value_unset (value);
365
366       return success;
367     }
368
369   g_value_init (value, _gtk_style_property_get_value_type (property));
370   if (property->parse_func)
371     success = (* property->parse_func) (parser, base, value);
372   else
373     success = _gtk_css_style_parse_value (value, parser, base);
374
375   if (!success)
376     g_value_unset (value);
377
378   return success;
379 }
380
381 static void
382 _gtk_css_style_property_class_init (GtkCssStylePropertyClass *klass)
383 {
384   GObjectClass *object_class = G_OBJECT_CLASS (klass);
385   GtkStylePropertyClass *property_class = GTK_STYLE_PROPERTY_CLASS (klass);
386
387   object_class->constructed = gtk_css_style_property_constructed;
388   object_class->set_property = gtk_css_style_property_set_property;
389   object_class->get_property = gtk_css_style_property_get_property;
390
391   g_object_class_install_property (object_class,
392                                    PROP_ID,
393                                    g_param_spec_uint ("id",
394                                                       P_("ID"),
395                                                       P_("The numeric id for quick access"),
396                                                       0, G_MAXUINT, 0,
397                                                       G_PARAM_READABLE));
398   g_object_class_install_property (object_class,
399                                    PROP_INHERIT,
400                                    g_param_spec_boolean ("inherit",
401                                                          P_("Inherit"),
402                                                          P_("Set if the value is inherited by default"),
403                                                          FALSE,
404                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
405   g_object_class_install_property (object_class,
406                                    PROP_INITIAL,
407                                    g_param_spec_boxed ("initial-value",
408                                                        P_("Initial value"),
409                                                        P_("The initial specified value used for this property"),
410                                                        G_TYPE_VALUE,
411                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
412
413   property_class->assign = _gtk_css_style_property_assign;
414   property_class->query = _gtk_css_style_property_query;
415   property_class->parse_value = gtk_css_style_property_parse_value;
416
417   klass->style_properties = g_ptr_array_new ();
418 }
419
420
421 static void
422 _gtk_css_style_property_init (GtkCssStyleProperty *style_property)
423 {
424 }
425
426 /**
427  * _gtk_css_style_property_get_n_properties:
428  *
429  * Gets the number of style properties. This number can increase when new
430  * theme engines are loaded. Shorthand properties are not included here.
431  *
432  * Returns: The number of style properties.
433  **/
434 guint
435 _gtk_css_style_property_get_n_properties (void)
436 {
437   GtkCssStylePropertyClass *klass;
438
439   klass = g_type_class_peek (GTK_TYPE_CSS_STYLE_PROPERTY);
440
441   return klass->style_properties->len;
442 }
443
444 /**
445  * _gtk_css_style_property_lookup_by_id:
446  * @id: the id of the property
447  *
448  * Gets the style property with the given id. All style properties (but not
449  * shorthand properties) are indexable by id so that it's easy to use arrays
450  * when doing style lookups.
451  *
452  * Returns: (transfer none): The style property with the given id
453  **/
454 GtkCssStyleProperty *
455 _gtk_css_style_property_lookup_by_id (guint id)
456 {
457   GtkCssStylePropertyClass *klass;
458
459   klass = g_type_class_peek (GTK_TYPE_CSS_STYLE_PROPERTY);
460
461   return g_ptr_array_index (klass->style_properties, id);
462 }
463
464 /**
465  * _gtk_css_style_property_is_inherit:
466  * @property: the property
467  *
468  * Queries if the given @property is inherited. See
469  * <ulink url="http://www.w3.org/TR/css3-cascade/#inheritance>
470  * the CSS documentation</ulink> for an explanation of this concept.
471  *
472  * Returns: %TRUE if the property is inherited by default.
473  **/
474 gboolean
475 _gtk_css_style_property_is_inherit (GtkCssStyleProperty *property)
476 {
477   g_return_val_if_fail (GTK_IS_CSS_STYLE_PROPERTY (property), 0);
478
479   return property->inherit;
480 }
481
482 /**
483  * _gtk_css_style_property_get_id:
484  * @property: the property
485  *
486  * Gets the id for the given property. IDs are used to allow using arrays
487  * for style lookups.
488  *
489  * Returns: The id of the property
490  **/
491 guint
492 _gtk_css_style_property_get_id (GtkCssStyleProperty *property)
493 {
494   g_return_val_if_fail (GTK_IS_CSS_STYLE_PROPERTY (property), 0);
495
496   return property->id;
497 }
498
499 /**
500  * _gtk_css_style_property_get_initial_value:
501  * @property: the property
502  *
503  * Queries the initial value of the given @property. See
504  * <ulink url="http://www.w3.org/TR/css3-cascade/#intial>
505  * the CSS documentation</ulink> for an explanation of this concept.
506  *
507  * Returns: a reference to the initial value. The value will never change.
508  **/
509 const GValue *
510 _gtk_css_style_property_get_initial_value (GtkCssStyleProperty *property)
511 {
512   g_return_val_if_fail (GTK_IS_CSS_STYLE_PROPERTY (property), NULL);
513
514   return &property->initial_value;
515 }
516
517 /**
518  * _gtk_css_style_property_print_value:
519  * @property: the property
520  * @value: the value to print
521  * @string: the string to print to
522  *
523  * Prints @value to the given @string in CSS format. The @value must be a
524  * valid specified value as parsed using the parse functions or as assigned
525  * via _gtk_style_property_assign().
526  **/
527 void
528 _gtk_css_style_property_print_value (GtkCssStyleProperty    *property,
529                                      const GValue           *value,
530                                      GString                *string)
531 {
532   g_return_if_fail (GTK_IS_CSS_STYLE_PROPERTY (property));
533   g_return_if_fail (value != NULL);
534   g_return_if_fail (string != NULL);
535
536   if (G_VALUE_HOLDS (value, GTK_TYPE_CSS_SPECIAL_VALUE))
537     {
538       GEnumClass *enum_class;
539       GEnumValue *enum_value;
540
541       enum_class = g_type_class_ref (GTK_TYPE_CSS_SPECIAL_VALUE);
542       enum_value = g_enum_get_value (enum_class, g_value_get_enum (value));
543
544       g_string_append (string, enum_value->value_nick);
545
546       g_type_class_unref (enum_class);
547     }
548   else if (GTK_STYLE_PROPERTY (property)->print_func)
549     (* GTK_STYLE_PROPERTY (property)->print_func) (value, string);
550   else
551     _gtk_css_style_print_value (value, string);
552 }
553