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