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