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