]> Pileus Git - ~andy/gtk/blob - gtk/gtkstyleproperties.c
2f153044f409126003d1fdafc742749b46f6d2f5
[~andy/gtk] / gtk / gtkstyleproperties.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
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 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
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "config.h"
21
22 #include "gtkstylepropertiesprivate.h"
23
24 #include <stdlib.h>
25 #include <gobject/gvaluecollector.h>
26 #include <cairo-gobject.h>
27
28 #include "gtkstyleprovider.h"
29 #include "gtksymboliccolor.h"
30 #include "gtkthemingengine.h"
31 #include "gtkanimationdescription.h"
32 #include "gtkgradient.h"
33 #include "gtkshadowprivate.h"
34 #include "gtkcsstypesprivate.h"
35
36 #include "gtkstylepropertyprivate.h"
37 #include "gtkintl.h"
38
39 /**
40  * SECTION:gtkstyleproperties
41  * @Short_description: Store for style property information
42  * @Title: GtkStyleProperties
43  *
44  * GtkStyleProperties provides the storage for style information
45  * that is used by #GtkStyleContext and other #GtkStyleProvider
46  * implementations.
47  *
48  * Before style properties can be stored in GtkStyleProperties, they
49  * must be registered with gtk_style_properties_register_property().
50  *
51  * Unless you are writing a #GtkStyleProvider implementation, you
52  * are unlikely to use this API directly, as gtk_style_context_get()
53  * and its variants are the preferred way to access styling information
54  * from widget implementations and theming engine implementations
55  * should use the APIs provided by #GtkThemingEngine instead.
56  */
57
58 typedef struct GtkStylePropertiesPrivate GtkStylePropertiesPrivate;
59 typedef struct PropertyData PropertyData;
60 typedef struct ValueData ValueData;
61
62 struct ValueData
63 {
64   GtkStateFlags state;
65   GValue value;
66 };
67
68 struct PropertyData
69 {
70   GArray *values;
71 };
72
73 struct GtkStylePropertiesPrivate
74 {
75   GHashTable *color_map;
76   GHashTable *properties;
77 };
78
79 static void gtk_style_properties_provider_init (GtkStyleProviderIface *iface);
80 static void gtk_style_properties_finalize      (GObject      *object);
81
82
83 G_DEFINE_TYPE_EXTENDED (GtkStyleProperties, gtk_style_properties, G_TYPE_OBJECT, 0,
84                         G_IMPLEMENT_INTERFACE (GTK_TYPE_STYLE_PROVIDER,
85                                                gtk_style_properties_provider_init));
86
87 static void
88 gtk_style_properties_class_init (GtkStylePropertiesClass *klass)
89 {
90   GObjectClass *object_class = G_OBJECT_CLASS (klass);
91
92   object_class->finalize = gtk_style_properties_finalize;
93
94   g_type_class_add_private (object_class, sizeof (GtkStylePropertiesPrivate));
95 }
96
97 static PropertyData *
98 property_data_new (void)
99 {
100   PropertyData *data;
101
102   data = g_slice_new0 (PropertyData);
103   data->values = g_array_new (FALSE, FALSE, sizeof (ValueData));
104
105   return data;
106 }
107
108 static void
109 property_data_remove_values (PropertyData *data)
110 {
111   guint i;
112
113   for (i = 0; i < data->values->len; i++)
114     {
115       ValueData *value_data;
116
117       value_data = &g_array_index (data->values, ValueData, i);
118
119       if (G_IS_VALUE (&value_data->value))
120         g_value_unset (&value_data->value);
121     }
122
123   if (data->values->len > 0)
124     g_array_remove_range (data->values, 0, data->values->len);
125 }
126
127 static void
128 property_data_free (PropertyData *data)
129 {
130   property_data_remove_values (data);
131   g_array_free (data->values, TRUE);
132   g_slice_free (PropertyData, data);
133 }
134
135 static gboolean
136 property_data_find_position (PropertyData  *data,
137                              GtkStateFlags  state,
138                              guint         *pos)
139 {
140   gint min, max, mid;
141   gboolean found = FALSE;
142   guint position;
143
144   if (pos)
145     *pos = 0;
146
147   if (data->values->len == 0)
148     return FALSE;
149
150   /* Find position for the given state, or the position where
151    * it would be if not found, the array is ordered by the
152    * state flags.
153    */
154   min = 0;
155   max = data->values->len - 1;
156
157   do
158     {
159       ValueData *value_data;
160
161       mid = (min + max) / 2;
162       value_data = &g_array_index (data->values, ValueData, mid);
163
164       if (value_data->state == state)
165         {
166           found = TRUE;
167           position = mid;
168         }
169       else if (value_data->state < state)
170           position = min = mid + 1;
171       else
172         {
173           max = mid - 1;
174           position = mid;
175         }
176     }
177   while (!found && min <= max);
178
179   if (pos)
180     *pos = position;
181
182   return found;
183 }
184
185 static GValue *
186 property_data_get_value (PropertyData  *data,
187                          GtkStateFlags  state)
188 {
189   ValueData *val_data;
190   guint pos;
191
192   if (!property_data_find_position (data, state, &pos))
193     {
194       ValueData new = { 0 };
195
196       new.state = state;
197       g_array_insert_val (data->values, pos, new);
198     }
199
200   val_data = &g_array_index (data->values, ValueData, pos);
201
202   return &val_data->value;
203 }
204
205 static GValue *
206 property_data_match_state (PropertyData  *data,
207                            GtkStateFlags  state)
208 {
209   guint pos;
210   gint i;
211
212   if (property_data_find_position (data, state, &pos))
213     {
214       ValueData *val_data;
215
216       /* Exact match */
217       val_data = &g_array_index (data->values, ValueData, pos);
218       return &val_data->value;
219     }
220
221   if (pos >= data->values->len)
222     pos = data->values->len - 1;
223
224   /* No exact match, go downwards the list to find
225    * the closest match to the given state flags, as
226    * a side effect, there is an implicit precedence
227    * of higher flags over the smaller ones.
228    */
229   for (i = pos; i >= 0; i--)
230     {
231       ValueData *val_data;
232
233       val_data = &g_array_index (data->values, ValueData, i);
234
235        /* Check whether any of the requested
236         * flags are set, and no other flags are.
237         *
238         * Also, no flags acts as a wildcard, such
239         * value should be always in the first position
240         * in the array (if present) anyways.
241         */
242       if (val_data->state == 0 ||
243           ((val_data->state & state) != 0 &&
244            (val_data->state & ~state) == 0))
245         return &val_data->value;
246     }
247
248   return NULL;
249 }
250
251 static void
252 gtk_style_properties_init (GtkStyleProperties *props)
253 {
254   GtkStylePropertiesPrivate *priv;
255
256   priv = props->priv = G_TYPE_INSTANCE_GET_PRIVATE (props,
257                                                     GTK_TYPE_STYLE_PROPERTIES,
258                                                     GtkStylePropertiesPrivate);
259
260   priv->properties = g_hash_table_new_full (NULL, NULL, NULL,
261                                             (GDestroyNotify) property_data_free);
262 }
263
264 static void
265 gtk_style_properties_finalize (GObject *object)
266 {
267   GtkStylePropertiesPrivate *priv;
268   GtkStyleProperties *props;
269
270   props = GTK_STYLE_PROPERTIES (object);
271   priv = props->priv;
272   g_hash_table_destroy (priv->properties);
273
274   if (priv->color_map)
275     g_hash_table_destroy (priv->color_map);
276
277   G_OBJECT_CLASS (gtk_style_properties_parent_class)->finalize (object);
278 }
279
280 GtkStyleProperties *
281 gtk_style_properties_get_style (GtkStyleProvider *provider,
282                                 GtkWidgetPath    *path)
283 {
284   /* Return style set itself */
285   return g_object_ref (provider);
286 }
287
288 static void
289 gtk_style_properties_provider_init (GtkStyleProviderIface *iface)
290 {
291   iface->get_style = gtk_style_properties_get_style;
292 }
293
294 /* Property registration functions */
295
296 /**
297  * gtk_style_properties_register_property: (skip)
298  * @parse_func: parsing function to use, or %NULL
299  * @pspec: the #GParamSpec for the new property
300  *
301  * Registers a property so it can be used in the CSS file format.
302  * This function is the low-level equivalent of
303  * gtk_theming_engine_register_property(), if you are implementing
304  * a theming engine, you want to use that function instead.
305  *
306  * Since: 3.0
307  **/
308 void
309 gtk_style_properties_register_property (GtkStylePropertyParser  parse_func,
310                                         GParamSpec             *pspec)
311 {
312   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
313
314   _gtk_style_property_register (pspec,
315                                 parse_func,
316                                 NULL,
317                                 NULL,
318                                 NULL,
319                                 NULL);
320 }
321
322 /**
323  * gtk_style_properties_lookup_property: (skip)
324  * @property_name: property name to look up
325  * @parse_func: (out): return location for the parse function
326  * @pspec: (out) (transfer none): return location for the #GParamSpec
327  *
328  * Returns %TRUE if a property has been registered, if @pspec or
329  * @parse_func are not %NULL, the #GParamSpec and parsing function
330  * will be respectively returned.
331  *
332  * Returns: %TRUE if the property is registered, %FALSE otherwise
333  *
334  * Since: 3.0
335  **/
336 gboolean
337 gtk_style_properties_lookup_property (const gchar             *property_name,
338                                       GtkStylePropertyParser  *parse_func,
339                                       GParamSpec             **pspec)
340 {
341   const GtkStyleProperty *node;
342   gboolean found = FALSE;
343
344   g_return_val_if_fail (property_name != NULL, FALSE);
345
346   node = _gtk_style_property_lookup (property_name);
347
348   if (node)
349     {
350       if (pspec)
351         *pspec = node->pspec;
352
353       if (parse_func)
354         *parse_func = node->property_parse_func;
355
356       found = TRUE;
357     }
358
359   return found;
360 }
361
362 /* GParamSpec functionality */
363
364 enum {
365   GTK_STYLE_PROPERTY_INHERIT = 1 << G_PARAM_USER_SHIFT
366 };
367
368 /**
369  * gtk_style_param_set_inherit:
370  * @pspec: A style param
371  * @inherit: whether the @pspec value should be inherited
372  *
373  * Sets whether a param spec installed with function such as
374  * gtk_style_properties_register_property() or
375  * gtk_widget_class_install_style_property() should inherit their
376  * value from the parent widget if it is not set instead of using
377  * the default value of @pspec. See the
378  * <ulink url="http://www.w3.org/TR/CSS21/cascade.html#inheritance">
379  * CSS specification's description of inheritance</ulink> for a
380  * longer description of this concept.
381  *
382  * By default, param specs do not inherit their value.
383  **/
384 void
385 gtk_style_param_set_inherit (GParamSpec *pspec,
386                              gboolean    inherit)
387 {
388   if (inherit)
389     pspec->flags |= GTK_STYLE_PROPERTY_INHERIT;
390   else
391     pspec->flags &= ~GTK_STYLE_PROPERTY_INHERIT;
392 }
393
394 /**
395  * gtk_style_param_get_inherit:
396  * @pspec: a style param
397  *
398  * Checks if the value of this param should be inherited from the parent
399  * #GtkWidget instead of using the default value when it has not been
400  * specified. See gtk_style_param_set_inherit() for more details.
401  *
402  * Returns: %TRUE if the param should inherit its value
403  **/
404 gboolean
405 gtk_style_param_get_inherit (GParamSpec *pspec)
406 {
407   return (pspec->flags & GTK_STYLE_PROPERTY_INHERIT) ? TRUE : FALSE;
408 }
409
410 /* GtkStyleProperties methods */
411
412 /**
413  * gtk_style_properties_new:
414  *
415  * Returns a newly created #GtkStyleProperties
416  *
417  * Returns: a new #GtkStyleProperties
418  **/
419 GtkStyleProperties *
420 gtk_style_properties_new (void)
421 {
422   return g_object_new (GTK_TYPE_STYLE_PROPERTIES, NULL);
423 }
424
425 /**
426  * gtk_style_properties_map_color:
427  * @props: a #GtkStyleProperties
428  * @name: color name
429  * @color: #GtkSymbolicColor to map @name to
430  *
431  * Maps @color so it can be referenced by @name. See
432  * gtk_style_properties_lookup_color()
433  *
434  * Since: 3.0
435  **/
436 void
437 gtk_style_properties_map_color (GtkStyleProperties *props,
438                                 const gchar        *name,
439                                 GtkSymbolicColor   *color)
440 {
441   GtkStylePropertiesPrivate *priv;
442
443   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
444   g_return_if_fail (name != NULL);
445   g_return_if_fail (color != NULL);
446
447   priv = props->priv;
448
449   if (G_UNLIKELY (!priv->color_map))
450     priv->color_map = g_hash_table_new_full (g_str_hash,
451                                              g_str_equal,
452                                              (GDestroyNotify) g_free,
453                                              (GDestroyNotify) gtk_symbolic_color_unref);
454
455   g_hash_table_replace (priv->color_map,
456                         g_strdup (name),
457                         gtk_symbolic_color_ref (color));
458 }
459
460 /**
461  * gtk_style_properties_lookup_color:
462  * @props: a #GtkStyleProperties
463  * @name: color name to lookup
464  *
465  * Returns the symbolic color that is mapped
466  * to @name.
467  *
468  * Returns: (transfer none): The mapped color
469  *
470  * Since: 3.0
471  **/
472 GtkSymbolicColor *
473 gtk_style_properties_lookup_color (GtkStyleProperties *props,
474                                    const gchar        *name)
475 {
476   GtkStylePropertiesPrivate *priv;
477
478   g_return_val_if_fail (GTK_IS_STYLE_PROPERTIES (props), NULL);
479   g_return_val_if_fail (name != NULL, NULL);
480
481   priv = props->priv;
482
483   if (!priv->color_map)
484     return NULL;
485
486   return g_hash_table_lookup (priv->color_map, name);
487 }
488
489 void
490 _gtk_style_properties_set_property_by_property (GtkStyleProperties     *props,
491                                                 const GtkStyleProperty *style_prop,
492                                                 GtkStateFlags           state,
493                                                 const GValue           *value)
494 {
495   GtkStylePropertiesPrivate *priv;
496   PropertyData *prop;
497   GType value_type;
498   GValue *val;
499
500   value_type = G_VALUE_TYPE (value);
501
502   if (style_prop->pspec->value_type == GDK_TYPE_RGBA ||
503       style_prop->pspec->value_type == GDK_TYPE_COLOR)
504     {
505       /* Allow GtkSymbolicColor as well */
506       g_return_if_fail (value_type == GDK_TYPE_RGBA ||
507                         value_type == GDK_TYPE_COLOR ||
508                         value_type == GTK_TYPE_SYMBOLIC_COLOR);
509     }
510   else if (style_prop->pspec->value_type == CAIRO_GOBJECT_TYPE_PATTERN)
511     {
512       /* Allow GtkGradient as a substitute */
513       g_return_if_fail (value_type == CAIRO_GOBJECT_TYPE_PATTERN ||
514                         value_type == GTK_TYPE_GRADIENT);
515     }
516   else if (style_prop->pspec->value_type == G_TYPE_INT)
517     {
518       g_return_if_fail (value_type == G_TYPE_INT ||
519                         value_type == GTK_TYPE_CSS_BORDER_RADIUS);
520     }
521   else
522     g_return_if_fail (style_prop->pspec->value_type == value_type);
523
524   if (_gtk_style_property_is_shorthand (style_prop))
525     {
526       GParameter *parameters;
527       guint i, n_parameters;
528
529       parameters = _gtk_style_property_unpack (style_prop, value, &n_parameters);
530
531       for (i = 0; i < n_parameters; i++)
532         {
533           gtk_style_properties_set_property (props,
534                                              parameters[i].name,
535                                              state,
536                                              &parameters[i].value);
537           g_value_unset (&parameters[i].value);
538         }
539       g_free (parameters);
540       return;
541     }
542
543   priv = props->priv;
544   prop = g_hash_table_lookup (priv->properties, style_prop);
545
546   if (!prop)
547     {
548       prop = property_data_new ();
549       g_hash_table_insert (priv->properties, (gpointer) style_prop, prop);
550     }
551
552   val = property_data_get_value (prop, state);
553
554   if (G_VALUE_TYPE (val) == value_type)
555     g_value_reset (val);
556   else
557     {
558       if (G_IS_VALUE (val))
559         g_value_unset (val);
560
561       g_value_init (val, value_type);
562     }
563
564   g_value_copy (value, val);
565   if (style_prop->pspec->value_type == value_type)
566     g_param_value_validate (style_prop->pspec, val);
567 }
568
569 /**
570  * gtk_style_properties_set_property:
571  * @props: a #GtkStyleProperties
572  * @property: styling property to set
573  * @state: state to set the value for
574  * @value: new value for the property
575  *
576  * Sets a styling property in @props.
577  *
578  * Since: 3.0
579  **/
580 void
581 gtk_style_properties_set_property (GtkStyleProperties *props,
582                                    const gchar        *property,
583                                    GtkStateFlags       state,
584                                    const GValue       *value)
585 {
586   const GtkStyleProperty *node;
587
588   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
589   g_return_if_fail (property != NULL);
590   g_return_if_fail (value != NULL);
591
592   node = _gtk_style_property_lookup (property);
593
594   if (!node)
595     {
596       g_warning ("Style property \"%s\" is not registered", property);
597       return;
598     }
599
600   _gtk_style_properties_set_property_by_property (props,
601                                                   node,
602                                                   state,
603                                                   value);
604 }
605
606 /**
607  * gtk_style_properties_set_valist:
608  * @props: a #GtkStyleProperties
609  * @state: state to set the values for
610  * @args: va_list of property name/value pairs, followed by %NULL
611  *
612  * Sets several style properties on @props.
613  *
614  * Since: 3.0
615  **/
616 void
617 gtk_style_properties_set_valist (GtkStyleProperties *props,
618                                  GtkStateFlags       state,
619                                  va_list             args)
620 {
621   const gchar *property_name;
622
623   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
624
625   property_name = va_arg (args, const gchar *);
626
627   while (property_name)
628     {
629       const GtkStyleProperty *node;
630       gchar *error = NULL;
631       GValue val = { 0 };
632
633       node = _gtk_style_property_lookup (property_name);
634
635       if (!node)
636         {
637           g_warning ("Style property \"%s\" is not registered", property_name);
638           break;
639         }
640
641       G_VALUE_COLLECT_INIT (&val, node->pspec->value_type,
642                             args, 0, &error);
643       if (error)
644         {
645           g_warning ("Could not set style property \"%s\": %s", property_name, error);
646           g_value_unset (&val);
647           g_free (error);
648           break;
649         }
650
651       _gtk_style_properties_set_property_by_property (props, node, state, &val);
652       g_value_unset (&val);
653
654       property_name = va_arg (args, const gchar *);
655     }
656 }
657
658 /**
659  * gtk_style_properties_set:
660  * @props: a #GtkStyleProperties
661  * @state: state to set the values for
662  * @...: property name/value pairs, followed by %NULL
663  *
664  * Sets several style properties on @props.
665  *
666  * Since: 3.0
667  **/
668 void
669 gtk_style_properties_set (GtkStyleProperties *props,
670                           GtkStateFlags       state,
671                           ...)
672 {
673   va_list args;
674
675   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
676
677   va_start (args, state);
678   gtk_style_properties_set_valist (props, state, args);
679   va_end (args);
680 }
681
682 static gboolean
683 resolve_color (GtkStyleProperties *props,
684                GValue             *value)
685 {
686   GdkRGBA color;
687
688   /* Resolve symbolic color to GdkRGBA */
689   if (!gtk_symbolic_color_resolve (g_value_get_boxed (value), props, &color))
690     return FALSE;
691
692   /* Store it back, this is where GdkRGBA caching happens */
693   g_value_unset (value);
694   g_value_init (value, GDK_TYPE_RGBA);
695   g_value_set_boxed (value, &color);
696
697   return TRUE;
698 }
699
700 static gboolean
701 resolve_color_rgb (GtkStyleProperties *props,
702                    GValue             *value)
703 {
704   GdkColor color = { 0 };
705   GdkRGBA rgba;
706
707   if (!gtk_symbolic_color_resolve (g_value_get_boxed (value), props, &rgba))
708     return FALSE;
709
710   color.red = rgba.red * 65535. + 0.5;
711   color.green = rgba.green * 65535. + 0.5;
712   color.blue = rgba.blue * 65535. + 0.5;
713
714   g_value_unset (value);
715   g_value_init (value, GDK_TYPE_COLOR);
716   g_value_set_boxed (value, &color);
717
718   return TRUE;
719 }
720
721 static gboolean
722 resolve_gradient (GtkStyleProperties *props,
723                   GValue             *value)
724 {
725   cairo_pattern_t *gradient;
726
727   if (!gtk_gradient_resolve (g_value_get_boxed (value), props, &gradient))
728     return FALSE;
729
730   /* Store it back, this is where cairo_pattern_t caching happens */
731   g_value_unset (value);
732   g_value_init (value, CAIRO_GOBJECT_TYPE_PATTERN);
733   g_value_take_boxed (value, gradient);
734
735   return TRUE;
736 }
737
738 static gboolean
739 resolve_shadow (GtkStyleProperties *props,
740                 GValue *value)
741 {
742   GtkShadow *resolved, *base;
743
744   base = g_value_get_boxed (value);
745
746   if (base == NULL)
747     return TRUE;
748   
749   if (_gtk_shadow_get_resolved (base))
750     return TRUE;
751
752   resolved = _gtk_shadow_resolve (base, props);
753   if (resolved == NULL)
754     return FALSE;
755
756   g_value_take_boxed (value, resolved);
757
758   return TRUE;
759 }
760
761 static gboolean
762 style_properties_resolve_type (GtkStyleProperties     *props,
763                                const GtkStyleProperty *node,
764                                GValue                 *val)
765 {
766   if (G_VALUE_TYPE (val) == GTK_TYPE_SYMBOLIC_COLOR)
767     {
768       if (node->pspec->value_type == GDK_TYPE_RGBA)
769         {
770           if (!resolve_color (props, val))
771             return FALSE;
772         }
773       else if (node->pspec->value_type == GDK_TYPE_COLOR)
774         {
775           if (!resolve_color_rgb (props, val))
776             return FALSE;
777         }
778       else
779         return FALSE;
780     }
781   else if (G_VALUE_TYPE (val) == GTK_TYPE_GRADIENT)
782     {
783       g_return_val_if_fail (node->pspec->value_type == CAIRO_GOBJECT_TYPE_PATTERN, FALSE);
784
785       if (!resolve_gradient (props, val))
786         return FALSE;
787     }
788   else if (G_VALUE_TYPE (val) == GTK_TYPE_SHADOW)
789     {
790       if (!resolve_shadow (props, val))
791         return FALSE;
792     }
793
794   return TRUE;
795 }
796
797 static void
798 lookup_default_value (const GtkStyleProperty *node,
799                       GValue                 *value)
800 {
801   if (node->pspec->value_type == GTK_TYPE_THEMING_ENGINE)
802     g_value_set_object (value, gtk_theming_engine_load (NULL));
803   else if (node->pspec->value_type == PANGO_TYPE_FONT_DESCRIPTION)
804     g_value_take_boxed (value, pango_font_description_from_string ("Sans 10"));
805   else if (node->pspec->value_type == GDK_TYPE_RGBA)
806     {
807       GdkRGBA color;
808       gdk_rgba_parse (&color, "pink");
809       g_value_set_boxed (value, &color);
810     }
811   else if (node->pspec->value_type == GTK_TYPE_BORDER)
812     {
813       g_value_take_boxed (value, gtk_border_new ());
814     }
815   else
816     g_param_value_set_default (node->pspec, value);
817 }
818
819 /* NB: Will return NULL for shorthands */
820 const GValue *
821 _gtk_style_properties_peek_property (GtkStyleProperties      *props,
822                                      const gchar             *prop_name,
823                                      GtkStateFlags            state,
824                                      const GtkStyleProperty **property)
825 {
826   GtkStylePropertiesPrivate *priv;
827   const GtkStyleProperty *node;
828   PropertyData *prop;
829   GValue *val;
830
831   g_return_val_if_fail (GTK_IS_STYLE_PROPERTIES (props), NULL);
832   g_return_val_if_fail (prop_name != NULL, NULL);
833
834   node = _gtk_style_property_lookup (prop_name);
835   if (property)
836     *property = node;
837
838   if (!node)
839     {
840       g_warning ("Style property \"%s\" is not registered", prop_name);
841       return NULL;
842     }
843
844   priv = props->priv;
845   prop = g_hash_table_lookup (priv->properties, node);
846
847   if (!prop)
848     return NULL;
849
850   val = property_data_match_state (prop, state);
851
852   if (val &&
853       !style_properties_resolve_type (props, node, val))
854     return NULL;
855
856   return val;
857 }
858
859 /**
860  * gtk_style_properties_get_property:
861  * @props: a #GtkStyleProperties
862  * @property: style property name
863  * @state: state to retrieve the property value for
864  * @value: (out) (transfer full):  return location for the style property value.
865  *
866  * Gets a style property from @props for the given state. When done with @value,
867  * g_value_unset() needs to be called to free any allocated memory.
868  *
869  * Returns: %TRUE if the property exists in @props, %FALSE otherwise
870  *
871  * Since: 3.0
872  **/
873 gboolean
874 gtk_style_properties_get_property (GtkStyleProperties *props,
875                                    const gchar        *property,
876                                    GtkStateFlags       state,
877                                    GValue             *value)
878 {
879   const GtkStyleProperty *node;
880   const GValue *val;
881
882   g_return_val_if_fail (GTK_IS_STYLE_PROPERTIES (props), FALSE);
883   g_return_val_if_fail (property != NULL, FALSE);
884   g_return_val_if_fail (value != NULL, FALSE);
885
886   val = _gtk_style_properties_peek_property (props, property, state, &node);
887
888   if (!node)
889     return FALSE;
890
891   g_value_init (value, node->pspec->value_type);
892
893   if (val)
894     g_value_copy (val, value);
895   else if (_gtk_style_property_is_shorthand (node))
896     _gtk_style_property_pack (node, props, state, value);
897   else
898     lookup_default_value (node, value);
899
900   return TRUE;
901 }
902
903 /**
904  * gtk_style_properties_get_valist:
905  * @props: a #GtkStyleProperties
906  * @state: state to retrieve the property values for
907  * @args: va_list of property name/return location pairs, followed by %NULL
908  *
909  * Retrieves several style property values from @props for a given state.
910  *
911  * Since: 3.0
912  **/
913 void
914 gtk_style_properties_get_valist (GtkStyleProperties *props,
915                                  GtkStateFlags       state,
916                                  va_list             args)
917 {
918   const gchar *property_name;
919
920   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
921
922   property_name = va_arg (args, const gchar *);
923
924   while (property_name)
925     {
926       const GtkStyleProperty *node;
927       gchar *error = NULL;
928       const GValue *val;
929
930       val = _gtk_style_properties_peek_property (props, property_name, state, &node);
931       if (!node)
932         break;
933
934       if (val)
935         {
936           G_VALUE_LCOPY (val, args, 0, &error);
937         }
938       else if (_gtk_style_property_is_shorthand (node))
939         {
940           GValue packed = { 0 };
941
942           g_value_init (&packed, node->pspec->value_type);
943           _gtk_style_property_pack (node, props, state, &packed);
944           G_VALUE_LCOPY (&packed, args, 0, &error);
945           g_value_unset (&packed);
946         }
947       else
948         {
949           GValue default_value = { 0 };
950
951           g_value_init (&default_value, node->pspec->value_type);
952           lookup_default_value (node, &default_value);
953           G_VALUE_LCOPY (&default_value, args, 0, &error);
954           g_value_unset (&default_value);
955         }
956
957       if (error)
958         {
959           g_warning ("Could not get style property \"%s\": %s", property_name, error);
960           g_free (error);
961           break;
962         }
963
964       property_name = va_arg (args, const gchar *);
965     }
966 }
967
968 /**
969  * gtk_style_properties_get:
970  * @props: a #GtkStyleProperties
971  * @state: state to retrieve the property values for
972  * @...: property name /return value pairs, followed by %NULL
973  *
974  * Retrieves several style property values from @props for a
975  * given state.
976  *
977  * Since: 3.0
978  **/
979 void
980 gtk_style_properties_get (GtkStyleProperties *props,
981                           GtkStateFlags       state,
982                           ...)
983 {
984   va_list args;
985
986   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
987
988   va_start (args, state);
989   gtk_style_properties_get_valist (props, state, args);
990   va_end (args);
991 }
992
993 /**
994  * gtk_style_properties_unset_property:
995  * @props: a #GtkStyleProperties
996  * @property: property to unset
997  * @state: state to unset
998  *
999  * Unsets a style property in @props.
1000  *
1001  * Since: 3.0
1002  **/
1003 void
1004 gtk_style_properties_unset_property (GtkStyleProperties *props,
1005                                      const gchar        *property,
1006                                      GtkStateFlags       state)
1007 {
1008   GtkStylePropertiesPrivate *priv;
1009   const GtkStyleProperty *node;
1010   PropertyData *prop;
1011   guint pos;
1012
1013   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
1014   g_return_if_fail (property != NULL);
1015
1016   node = _gtk_style_property_lookup (property);
1017
1018   if (!node)
1019     {
1020       g_warning ("Style property \"%s\" is not registered", property);
1021       return;
1022     }
1023
1024   priv = props->priv;
1025   prop = g_hash_table_lookup (priv->properties, node);
1026
1027   if (!prop)
1028     return;
1029
1030   if (property_data_find_position (prop, state, &pos))
1031     {
1032       ValueData *data;
1033
1034       data = &g_array_index (prop->values, ValueData, pos);
1035
1036       if (G_IS_VALUE (&data->value))
1037         g_value_unset (&data->value);
1038
1039       g_array_remove_index (prop->values, pos);
1040     }
1041 }
1042
1043 /**
1044  * gtk_style_properties_clear:
1045  * @props: a #GtkStyleProperties
1046  *
1047  * Clears all style information from @props.
1048  **/
1049 void
1050 gtk_style_properties_clear (GtkStyleProperties *props)
1051 {
1052   GtkStylePropertiesPrivate *priv;
1053
1054   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
1055
1056   priv = props->priv;
1057   g_hash_table_remove_all (priv->properties);
1058 }
1059
1060 /**
1061  * gtk_style_properties_merge:
1062  * @props: a #GtkStyleProperties
1063  * @props_to_merge: a second #GtkStyleProperties
1064  * @replace: whether to replace values or not
1065  *
1066  * Merges into @props all the style information contained
1067  * in @props_to_merge. If @replace is %TRUE, the values
1068  * will be overwritten, if it is %FALSE, the older values
1069  * will prevail.
1070  *
1071  * Since: 3.0
1072  **/
1073 void
1074 gtk_style_properties_merge (GtkStyleProperties       *props,
1075                             const GtkStyleProperties *props_to_merge,
1076                             gboolean                  replace)
1077 {
1078   GtkStylePropertiesPrivate *priv, *priv_to_merge;
1079   GHashTableIter iter;
1080   gpointer key, value;
1081
1082   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
1083   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props_to_merge));
1084
1085   priv = props->priv;
1086   priv_to_merge = props_to_merge->priv;
1087
1088   /* Merge symbolic color map */
1089   if (priv_to_merge->color_map)
1090     {
1091       g_hash_table_iter_init (&iter, priv_to_merge->color_map);
1092
1093       while (g_hash_table_iter_next (&iter, &key, &value))
1094         {
1095           const gchar *name;
1096           GtkSymbolicColor *color;
1097
1098           name = key;
1099           color = value;
1100
1101           if (!replace &&
1102               g_hash_table_lookup (priv->color_map, name))
1103             continue;
1104
1105           gtk_style_properties_map_color (props, name, color);
1106         }
1107     }
1108
1109   /* Merge symbolic style properties */
1110   g_hash_table_iter_init (&iter, priv_to_merge->properties);
1111
1112   while (g_hash_table_iter_next (&iter, &key, &value))
1113     {
1114       PropertyData *prop_to_merge = value;
1115       PropertyData *prop;
1116       guint i;
1117
1118       prop = g_hash_table_lookup (priv->properties, key);
1119
1120       if (!prop)
1121         {
1122           prop = property_data_new ();
1123           g_hash_table_insert (priv->properties, key, prop);
1124         }
1125
1126       for (i = 0; i < prop_to_merge->values->len; i++)
1127         {
1128           ValueData *data;
1129           GValue *value;
1130
1131           data = &g_array_index (prop_to_merge->values, ValueData, i);
1132
1133           if (replace && data->state == GTK_STATE_FLAG_NORMAL &&
1134               G_VALUE_TYPE (&data->value) != PANGO_TYPE_FONT_DESCRIPTION)
1135             {
1136               /* Let normal state override all states
1137                * previously set in the original set
1138                */
1139               property_data_remove_values (prop);
1140             }
1141
1142           value = property_data_get_value (prop, data->state);
1143
1144           if (G_VALUE_TYPE (&data->value) == PANGO_TYPE_FONT_DESCRIPTION &&
1145               G_IS_VALUE (value))
1146             {
1147               PangoFontDescription *font_desc;
1148               PangoFontDescription *font_desc_to_merge;
1149
1150               /* Handle merging of font descriptions */
1151               font_desc = g_value_get_boxed (value);
1152               font_desc_to_merge = g_value_get_boxed (&data->value);
1153
1154               pango_font_description_merge (font_desc, font_desc_to_merge, replace);
1155             }
1156           else if (G_VALUE_TYPE (&data->value) == G_TYPE_PTR_ARRAY &&
1157                    G_IS_VALUE (value))
1158             {
1159               GPtrArray *array, *array_to_merge;
1160               gint i;
1161
1162               /* Append the array, mainly thought
1163                * for the gtk-key-bindings property
1164                */
1165               array = g_value_get_boxed (value);
1166               array_to_merge = g_value_get_boxed (&data->value);
1167
1168               for (i = 0; i < array_to_merge->len; i++)
1169                 g_ptr_array_add (array, g_ptr_array_index (array_to_merge, i));
1170             }
1171           else if (replace || !G_IS_VALUE (value))
1172             {
1173               if (!G_IS_VALUE (value))
1174                 g_value_init (value, G_VALUE_TYPE (&data->value));
1175               else if (G_VALUE_TYPE (value) != G_VALUE_TYPE (&data->value))
1176                 {
1177                   g_value_unset (value);
1178                   g_value_init (value, G_VALUE_TYPE (&data->value));
1179                 }
1180
1181               g_value_copy (&data->value, value);
1182             }
1183         }
1184     }
1185 }