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