]> Pileus Git - ~andy/gtk/blob - gtk/gtkstyleproperties.c
styleproperties: Rename enum value
[~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                                 NULL);
321 }
322
323 /**
324  * gtk_style_properties_lookup_property: (skip)
325  * @property_name: property name to look up
326  * @parse_func: (out): return location for the parse function
327  * @pspec: (out) (transfer none): return location for the #GParamSpec
328  *
329  * Returns %TRUE if a property has been registered, if @pspec or
330  * @parse_func are not %NULL, the #GParamSpec and parsing function
331  * will be respectively returned.
332  *
333  * Returns: %TRUE if the property is registered, %FALSE otherwise
334  *
335  * Since: 3.0
336  **/
337 gboolean
338 gtk_style_properties_lookup_property (const gchar             *property_name,
339                                       GtkStylePropertyParser  *parse_func,
340                                       GParamSpec             **pspec)
341 {
342   const GtkStyleProperty *node;
343   gboolean found = FALSE;
344
345   g_return_val_if_fail (property_name != NULL, FALSE);
346
347   node = _gtk_style_property_lookup (property_name);
348
349   if (node)
350     {
351       if (pspec)
352         *pspec = node->pspec;
353
354       if (parse_func)
355         *parse_func = node->property_parse_func;
356
357       found = TRUE;
358     }
359
360   return found;
361 }
362
363 /* GParamSpec functionality */
364
365 enum {
366   GTK_STYLE_PARAM_INHERIT = 1 << G_PARAM_USER_SHIFT
367 };
368
369 /**
370  * gtk_style_param_set_inherit:
371  * @pspec: A style param
372  * @inherit: whether the @pspec value should be inherited
373  *
374  * Sets whether a param spec installed with function such as
375  * gtk_style_properties_register_property() or
376  * gtk_widget_class_install_style_property() should inherit their
377  * value from the parent widget if it is not set instead of using
378  * the default value of @pspec. See the
379  * <ulink url="http://www.w3.org/TR/CSS21/cascade.html#inheritance">
380  * CSS specification's description of inheritance</ulink> for a
381  * longer description of this concept.
382  *
383  * By default, param specs do not inherit their value.
384  **/
385 void
386 gtk_style_param_set_inherit (GParamSpec *pspec,
387                              gboolean    inherit)
388 {
389   if (inherit)
390     pspec->flags |= GTK_STYLE_PARAM_INHERIT;
391   else
392     pspec->flags &= ~GTK_STYLE_PARAM_INHERIT;
393 }
394
395 /**
396  * gtk_style_param_get_inherit:
397  * @pspec: a style param
398  *
399  * Checks if the value of this param should be inherited from the parent
400  * #GtkWidget instead of using the default value when it has not been
401  * specified. See gtk_style_param_set_inherit() for more details.
402  *
403  * Returns: %TRUE if the param should inherit its value
404  **/
405 gboolean
406 gtk_style_param_get_inherit (GParamSpec *pspec)
407 {
408   return (pspec->flags & GTK_STYLE_PARAM_INHERIT) ? TRUE : FALSE;
409 }
410
411 /* GtkStyleProperties methods */
412
413 /**
414  * gtk_style_properties_new:
415  *
416  * Returns a newly created #GtkStyleProperties
417  *
418  * Returns: a new #GtkStyleProperties
419  **/
420 GtkStyleProperties *
421 gtk_style_properties_new (void)
422 {
423   return g_object_new (GTK_TYPE_STYLE_PROPERTIES, NULL);
424 }
425
426 /**
427  * gtk_style_properties_map_color:
428  * @props: a #GtkStyleProperties
429  * @name: color name
430  * @color: #GtkSymbolicColor to map @name to
431  *
432  * Maps @color so it can be referenced by @name. See
433  * gtk_style_properties_lookup_color()
434  *
435  * Since: 3.0
436  **/
437 void
438 gtk_style_properties_map_color (GtkStyleProperties *props,
439                                 const gchar        *name,
440                                 GtkSymbolicColor   *color)
441 {
442   GtkStylePropertiesPrivate *priv;
443
444   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
445   g_return_if_fail (name != NULL);
446   g_return_if_fail (color != NULL);
447
448   priv = props->priv;
449
450   if (G_UNLIKELY (!priv->color_map))
451     priv->color_map = g_hash_table_new_full (g_str_hash,
452                                              g_str_equal,
453                                              (GDestroyNotify) g_free,
454                                              (GDestroyNotify) gtk_symbolic_color_unref);
455
456   g_hash_table_replace (priv->color_map,
457                         g_strdup (name),
458                         gtk_symbolic_color_ref (color));
459 }
460
461 /**
462  * gtk_style_properties_lookup_color:
463  * @props: a #GtkStyleProperties
464  * @name: color name to lookup
465  *
466  * Returns the symbolic color that is mapped
467  * to @name.
468  *
469  * Returns: (transfer none): The mapped color
470  *
471  * Since: 3.0
472  **/
473 GtkSymbolicColor *
474 gtk_style_properties_lookup_color (GtkStyleProperties *props,
475                                    const gchar        *name)
476 {
477   GtkStylePropertiesPrivate *priv;
478
479   g_return_val_if_fail (GTK_IS_STYLE_PROPERTIES (props), NULL);
480   g_return_val_if_fail (name != NULL, NULL);
481
482   priv = props->priv;
483
484   if (!priv->color_map)
485     return NULL;
486
487   return g_hash_table_lookup (priv->color_map, name);
488 }
489
490 void
491 _gtk_style_properties_set_property_by_property (GtkStyleProperties     *props,
492                                                 const GtkStyleProperty *style_prop,
493                                                 GtkStateFlags           state,
494                                                 const GValue           *value)
495 {
496   GtkStylePropertiesPrivate *priv;
497   PropertyData *prop;
498   GType value_type;
499   GValue *val;
500
501   value_type = G_VALUE_TYPE (value);
502
503   if (style_prop->pspec->value_type == GDK_TYPE_RGBA ||
504       style_prop->pspec->value_type == GDK_TYPE_COLOR)
505     {
506       /* Allow GtkSymbolicColor as well */
507       g_return_if_fail (value_type == GDK_TYPE_RGBA ||
508                         value_type == GDK_TYPE_COLOR ||
509                         value_type == GTK_TYPE_SYMBOLIC_COLOR);
510     }
511   else if (style_prop->pspec->value_type == CAIRO_GOBJECT_TYPE_PATTERN)
512     {
513       /* Allow GtkGradient as a substitute */
514       g_return_if_fail (value_type == CAIRO_GOBJECT_TYPE_PATTERN ||
515                         value_type == GTK_TYPE_GRADIENT);
516     }
517   else if (style_prop->pspec->value_type == G_TYPE_INT)
518     {
519       g_return_if_fail (value_type == G_TYPE_INT ||
520                         value_type == GTK_TYPE_CSS_BORDER_RADIUS);
521     }
522   else
523     g_return_if_fail (style_prop->pspec->value_type == value_type);
524
525   if (_gtk_style_property_is_shorthand (style_prop))
526     {
527       GParameter *parameters;
528       guint i, n_parameters;
529
530       parameters = _gtk_style_property_unpack (style_prop, value, &n_parameters);
531
532       for (i = 0; i < n_parameters; i++)
533         {
534           gtk_style_properties_set_property (props,
535                                              parameters[i].name,
536                                              state,
537                                              &parameters[i].value);
538           g_value_unset (&parameters[i].value);
539         }
540       g_free (parameters);
541       return;
542     }
543
544   priv = props->priv;
545   prop = g_hash_table_lookup (priv->properties, style_prop);
546
547   if (!prop)
548     {
549       prop = property_data_new ();
550       g_hash_table_insert (priv->properties, (gpointer) style_prop, prop);
551     }
552
553   val = property_data_get_value (prop, state);
554
555   if (G_VALUE_TYPE (val) == value_type)
556     g_value_reset (val);
557   else
558     {
559       if (G_IS_VALUE (val))
560         g_value_unset (val);
561
562       g_value_init (val, value_type);
563     }
564
565   g_value_copy (value, val);
566   if (style_prop->pspec->value_type == value_type)
567     g_param_value_validate (style_prop->pspec, val);
568 }
569
570 /**
571  * gtk_style_properties_set_property:
572  * @props: a #GtkStyleProperties
573  * @property: styling property to set
574  * @state: state to set the value for
575  * @value: new value for the property
576  *
577  * Sets a styling property in @props.
578  *
579  * Since: 3.0
580  **/
581 void
582 gtk_style_properties_set_property (GtkStyleProperties *props,
583                                    const gchar        *property,
584                                    GtkStateFlags       state,
585                                    const GValue       *value)
586 {
587   const GtkStyleProperty *node;
588
589   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
590   g_return_if_fail (property != NULL);
591   g_return_if_fail (value != NULL);
592
593   node = _gtk_style_property_lookup (property);
594
595   if (!node)
596     {
597       g_warning ("Style property \"%s\" is not registered", property);
598       return;
599     }
600
601   _gtk_style_properties_set_property_by_property (props,
602                                                   node,
603                                                   state,
604                                                   value);
605 }
606
607 /**
608  * gtk_style_properties_set_valist:
609  * @props: a #GtkStyleProperties
610  * @state: state to set the values for
611  * @args: va_list of property name/value pairs, followed by %NULL
612  *
613  * Sets several style properties on @props.
614  *
615  * Since: 3.0
616  **/
617 void
618 gtk_style_properties_set_valist (GtkStyleProperties *props,
619                                  GtkStateFlags       state,
620                                  va_list             args)
621 {
622   const gchar *property_name;
623
624   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
625
626   property_name = va_arg (args, const gchar *);
627
628   while (property_name)
629     {
630       const GtkStyleProperty *node;
631       gchar *error = NULL;
632       GValue val = { 0 };
633
634       node = _gtk_style_property_lookup (property_name);
635
636       if (!node)
637         {
638           g_warning ("Style property \"%s\" is not registered", property_name);
639           break;
640         }
641
642       G_VALUE_COLLECT_INIT (&val, node->pspec->value_type,
643                             args, 0, &error);
644       if (error)
645         {
646           g_warning ("Could not set style property \"%s\": %s", property_name, error);
647           g_value_unset (&val);
648           g_free (error);
649           break;
650         }
651
652       _gtk_style_properties_set_property_by_property (props, node, state, &val);
653       g_value_unset (&val);
654
655       property_name = va_arg (args, const gchar *);
656     }
657 }
658
659 /**
660  * gtk_style_properties_set:
661  * @props: a #GtkStyleProperties
662  * @state: state to set the values for
663  * @...: property name/value pairs, followed by %NULL
664  *
665  * Sets several style properties on @props.
666  *
667  * Since: 3.0
668  **/
669 void
670 gtk_style_properties_set (GtkStyleProperties *props,
671                           GtkStateFlags       state,
672                           ...)
673 {
674   va_list args;
675
676   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
677
678   va_start (args, state);
679   gtk_style_properties_set_valist (props, state, args);
680   va_end (args);
681 }
682
683 static gboolean
684 resolve_color (GtkStyleProperties *props,
685                GValue             *value)
686 {
687   GdkRGBA color;
688
689   /* Resolve symbolic color to GdkRGBA */
690   if (!gtk_symbolic_color_resolve (g_value_get_boxed (value), props, &color))
691     return FALSE;
692
693   /* Store it back, this is where GdkRGBA caching happens */
694   g_value_unset (value);
695   g_value_init (value, GDK_TYPE_RGBA);
696   g_value_set_boxed (value, &color);
697
698   return TRUE;
699 }
700
701 static gboolean
702 resolve_color_rgb (GtkStyleProperties *props,
703                    GValue             *value)
704 {
705   GdkColor color = { 0 };
706   GdkRGBA rgba;
707
708   if (!gtk_symbolic_color_resolve (g_value_get_boxed (value), props, &rgba))
709     return FALSE;
710
711   color.red = rgba.red * 65535. + 0.5;
712   color.green = rgba.green * 65535. + 0.5;
713   color.blue = rgba.blue * 65535. + 0.5;
714
715   g_value_unset (value);
716   g_value_init (value, GDK_TYPE_COLOR);
717   g_value_set_boxed (value, &color);
718
719   return TRUE;
720 }
721
722 static gboolean
723 resolve_gradient (GtkStyleProperties *props,
724                   GValue             *value)
725 {
726   cairo_pattern_t *gradient;
727
728   if (!gtk_gradient_resolve (g_value_get_boxed (value), props, &gradient))
729     return FALSE;
730
731   /* Store it back, this is where cairo_pattern_t caching happens */
732   g_value_unset (value);
733   g_value_init (value, CAIRO_GOBJECT_TYPE_PATTERN);
734   g_value_take_boxed (value, gradient);
735
736   return TRUE;
737 }
738
739 static gboolean
740 resolve_shadow (GtkStyleProperties *props,
741                 GValue *value)
742 {
743   GtkShadow *resolved, *base;
744
745   base = g_value_get_boxed (value);
746
747   if (base == NULL)
748     return TRUE;
749   
750   if (_gtk_shadow_get_resolved (base))
751     return TRUE;
752
753   resolved = _gtk_shadow_resolve (base, props);
754   if (resolved == NULL)
755     return FALSE;
756
757   g_value_take_boxed (value, resolved);
758
759   return TRUE;
760 }
761
762 static gboolean
763 style_properties_resolve_type (GtkStyleProperties     *props,
764                                const GtkStyleProperty *node,
765                                GValue                 *val)
766 {
767   if (G_VALUE_TYPE (val) == GTK_TYPE_SYMBOLIC_COLOR)
768     {
769       if (node->pspec->value_type == GDK_TYPE_RGBA)
770         {
771           if (!resolve_color (props, val))
772             return FALSE;
773         }
774       else if (node->pspec->value_type == GDK_TYPE_COLOR)
775         {
776           if (!resolve_color_rgb (props, val))
777             return FALSE;
778         }
779       else
780         return FALSE;
781     }
782   else if (G_VALUE_TYPE (val) == GTK_TYPE_GRADIENT)
783     {
784       g_return_val_if_fail (node->pspec->value_type == CAIRO_GOBJECT_TYPE_PATTERN, FALSE);
785
786       if (!resolve_gradient (props, val))
787         return FALSE;
788     }
789   else if (G_VALUE_TYPE (val) == GTK_TYPE_SHADOW)
790     {
791       if (!resolve_shadow (props, val))
792         return FALSE;
793     }
794
795   return TRUE;
796 }
797
798 /* NB: Will return NULL for shorthands */
799 const GValue *
800 _gtk_style_properties_peek_property (GtkStyleProperties      *props,
801                                      const gchar             *prop_name,
802                                      GtkStateFlags            state,
803                                      const GtkStyleProperty **property)
804 {
805   GtkStylePropertiesPrivate *priv;
806   const GtkStyleProperty *node;
807   PropertyData *prop;
808   GValue *val;
809
810   g_return_val_if_fail (GTK_IS_STYLE_PROPERTIES (props), NULL);
811   g_return_val_if_fail (prop_name != NULL, NULL);
812
813   node = _gtk_style_property_lookup (prop_name);
814   if (property)
815     *property = node;
816
817   if (!node)
818     {
819       g_warning ("Style property \"%s\" is not registered", prop_name);
820       return NULL;
821     }
822
823   priv = props->priv;
824   prop = g_hash_table_lookup (priv->properties, node);
825
826   if (!prop)
827     return NULL;
828
829   val = property_data_match_state (prop, state);
830
831   if (val &&
832       !style_properties_resolve_type (props, node, val))
833     return NULL;
834
835   return val;
836 }
837
838 /**
839  * gtk_style_properties_get_property:
840  * @props: a #GtkStyleProperties
841  * @property: style property name
842  * @state: state to retrieve the property value for
843  * @value: (out) (transfer full):  return location for the style property value.
844  *
845  * Gets a style property from @props for the given state. When done with @value,
846  * g_value_unset() needs to be called to free any allocated memory.
847  *
848  * Returns: %TRUE if the property exists in @props, %FALSE otherwise
849  *
850  * Since: 3.0
851  **/
852 gboolean
853 gtk_style_properties_get_property (GtkStyleProperties *props,
854                                    const gchar        *property,
855                                    GtkStateFlags       state,
856                                    GValue             *value)
857 {
858   const GtkStyleProperty *node;
859   const GValue *val;
860
861   g_return_val_if_fail (GTK_IS_STYLE_PROPERTIES (props), FALSE);
862   g_return_val_if_fail (property != NULL, FALSE);
863   g_return_val_if_fail (value != NULL, FALSE);
864
865   val = _gtk_style_properties_peek_property (props, property, state, &node);
866
867   if (!node)
868     return FALSE;
869
870   g_value_init (value, node->pspec->value_type);
871
872   if (val)
873     g_value_copy (val, value);
874   else if (_gtk_style_property_is_shorthand (node))
875     _gtk_style_property_pack (node, props, state, value);
876   else
877     _gtk_style_property_default_value (node, props, value);
878
879   return TRUE;
880 }
881
882 /**
883  * gtk_style_properties_get_valist:
884  * @props: a #GtkStyleProperties
885  * @state: state to retrieve the property values for
886  * @args: va_list of property name/return location pairs, followed by %NULL
887  *
888  * Retrieves several style property values from @props for a given state.
889  *
890  * Since: 3.0
891  **/
892 void
893 gtk_style_properties_get_valist (GtkStyleProperties *props,
894                                  GtkStateFlags       state,
895                                  va_list             args)
896 {
897   const gchar *property_name;
898
899   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
900
901   property_name = va_arg (args, const gchar *);
902
903   while (property_name)
904     {
905       const GtkStyleProperty *node;
906       gchar *error = NULL;
907       const GValue *val;
908
909       val = _gtk_style_properties_peek_property (props, property_name, state, &node);
910       if (!node)
911         break;
912
913       if (val)
914         {
915           G_VALUE_LCOPY (val, args, 0, &error);
916         }
917       else if (_gtk_style_property_is_shorthand (node))
918         {
919           GValue packed = { 0 };
920
921           g_value_init (&packed, node->pspec->value_type);
922           _gtk_style_property_pack (node, props, state, &packed);
923           G_VALUE_LCOPY (&packed, args, 0, &error);
924           g_value_unset (&packed);
925         }
926       else
927         {
928           GValue default_value = { 0 };
929
930           g_value_init (&default_value, node->pspec->value_type);
931           _gtk_style_property_default_value (node, props, &default_value);
932           G_VALUE_LCOPY (&default_value, args, 0, &error);
933           g_value_unset (&default_value);
934         }
935
936       if (error)
937         {
938           g_warning ("Could not get style property \"%s\": %s", property_name, error);
939           g_free (error);
940           break;
941         }
942
943       property_name = va_arg (args, const gchar *);
944     }
945 }
946
947 /**
948  * gtk_style_properties_get:
949  * @props: a #GtkStyleProperties
950  * @state: state to retrieve the property values for
951  * @...: property name /return value pairs, followed by %NULL
952  *
953  * Retrieves several style property values from @props for a
954  * given state.
955  *
956  * Since: 3.0
957  **/
958 void
959 gtk_style_properties_get (GtkStyleProperties *props,
960                           GtkStateFlags       state,
961                           ...)
962 {
963   va_list args;
964
965   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
966
967   va_start (args, state);
968   gtk_style_properties_get_valist (props, state, args);
969   va_end (args);
970 }
971
972 /**
973  * gtk_style_properties_unset_property:
974  * @props: a #GtkStyleProperties
975  * @property: property to unset
976  * @state: state to unset
977  *
978  * Unsets a style property in @props.
979  *
980  * Since: 3.0
981  **/
982 void
983 gtk_style_properties_unset_property (GtkStyleProperties *props,
984                                      const gchar        *property,
985                                      GtkStateFlags       state)
986 {
987   GtkStylePropertiesPrivate *priv;
988   const GtkStyleProperty *node;
989   PropertyData *prop;
990   guint pos;
991
992   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
993   g_return_if_fail (property != NULL);
994
995   node = _gtk_style_property_lookup (property);
996
997   if (!node)
998     {
999       g_warning ("Style property \"%s\" is not registered", property);
1000       return;
1001     }
1002
1003   priv = props->priv;
1004   prop = g_hash_table_lookup (priv->properties, node);
1005
1006   if (!prop)
1007     return;
1008
1009   if (property_data_find_position (prop, state, &pos))
1010     {
1011       ValueData *data;
1012
1013       data = &g_array_index (prop->values, ValueData, pos);
1014
1015       if (G_IS_VALUE (&data->value))
1016         g_value_unset (&data->value);
1017
1018       g_array_remove_index (prop->values, pos);
1019     }
1020 }
1021
1022 /**
1023  * gtk_style_properties_clear:
1024  * @props: a #GtkStyleProperties
1025  *
1026  * Clears all style information from @props.
1027  **/
1028 void
1029 gtk_style_properties_clear (GtkStyleProperties *props)
1030 {
1031   GtkStylePropertiesPrivate *priv;
1032
1033   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
1034
1035   priv = props->priv;
1036   g_hash_table_remove_all (priv->properties);
1037 }
1038
1039 /**
1040  * gtk_style_properties_merge:
1041  * @props: a #GtkStyleProperties
1042  * @props_to_merge: a second #GtkStyleProperties
1043  * @replace: whether to replace values or not
1044  *
1045  * Merges into @props all the style information contained
1046  * in @props_to_merge. If @replace is %TRUE, the values
1047  * will be overwritten, if it is %FALSE, the older values
1048  * will prevail.
1049  *
1050  * Since: 3.0
1051  **/
1052 void
1053 gtk_style_properties_merge (GtkStyleProperties       *props,
1054                             const GtkStyleProperties *props_to_merge,
1055                             gboolean                  replace)
1056 {
1057   GtkStylePropertiesPrivate *priv, *priv_to_merge;
1058   GHashTableIter iter;
1059   gpointer key, value;
1060
1061   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
1062   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props_to_merge));
1063
1064   priv = props->priv;
1065   priv_to_merge = props_to_merge->priv;
1066
1067   /* Merge symbolic color map */
1068   if (priv_to_merge->color_map)
1069     {
1070       g_hash_table_iter_init (&iter, priv_to_merge->color_map);
1071
1072       while (g_hash_table_iter_next (&iter, &key, &value))
1073         {
1074           const gchar *name;
1075           GtkSymbolicColor *color;
1076
1077           name = key;
1078           color = value;
1079
1080           if (!replace &&
1081               g_hash_table_lookup (priv->color_map, name))
1082             continue;
1083
1084           gtk_style_properties_map_color (props, name, color);
1085         }
1086     }
1087
1088   /* Merge symbolic style properties */
1089   g_hash_table_iter_init (&iter, priv_to_merge->properties);
1090
1091   while (g_hash_table_iter_next (&iter, &key, &value))
1092     {
1093       PropertyData *prop_to_merge = value;
1094       PropertyData *prop;
1095       guint i;
1096
1097       prop = g_hash_table_lookup (priv->properties, key);
1098
1099       if (!prop)
1100         {
1101           prop = property_data_new ();
1102           g_hash_table_insert (priv->properties, key, prop);
1103         }
1104
1105       for (i = 0; i < prop_to_merge->values->len; i++)
1106         {
1107           ValueData *data;
1108           GValue *value;
1109
1110           data = &g_array_index (prop_to_merge->values, ValueData, i);
1111
1112           if (replace && data->state == GTK_STATE_FLAG_NORMAL &&
1113               G_VALUE_TYPE (&data->value) != PANGO_TYPE_FONT_DESCRIPTION)
1114             {
1115               /* Let normal state override all states
1116                * previously set in the original set
1117                */
1118               property_data_remove_values (prop);
1119             }
1120
1121           value = property_data_get_value (prop, data->state);
1122
1123           if (G_VALUE_TYPE (&data->value) == PANGO_TYPE_FONT_DESCRIPTION &&
1124               G_IS_VALUE (value))
1125             {
1126               PangoFontDescription *font_desc;
1127               PangoFontDescription *font_desc_to_merge;
1128
1129               /* Handle merging of font descriptions */
1130               font_desc = g_value_get_boxed (value);
1131               font_desc_to_merge = g_value_get_boxed (&data->value);
1132
1133               pango_font_description_merge (font_desc, font_desc_to_merge, replace);
1134             }
1135           else if (G_VALUE_TYPE (&data->value) == G_TYPE_PTR_ARRAY &&
1136                    G_IS_VALUE (value))
1137             {
1138               GPtrArray *array, *array_to_merge;
1139               gint i;
1140
1141               /* Append the array, mainly thought
1142                * for the gtk-key-bindings property
1143                */
1144               array = g_value_get_boxed (value);
1145               array_to_merge = g_value_get_boxed (&data->value);
1146
1147               for (i = 0; i < array_to_merge->len; i++)
1148                 g_ptr_array_add (array, g_ptr_array_index (array_to_merge, i));
1149             }
1150           else if (replace || !G_IS_VALUE (value))
1151             {
1152               if (!G_IS_VALUE (value))
1153                 g_value_init (value, G_VALUE_TYPE (&data->value));
1154               else if (G_VALUE_TYPE (value) != G_VALUE_TYPE (&data->value))
1155                 {
1156                   g_value_unset (value);
1157                   g_value_init (value, G_VALUE_TYPE (&data->value));
1158                 }
1159
1160               g_value_copy (&data->value, value);
1161             }
1162         }
1163     }
1164 }