]> Pileus Git - ~andy/gtk/blob - gtk/gtkstyleproperties.c
cb4c6ec877fdf007c00e1795a9ec8b1a2c3d3401
[~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 void
283 gtk_style_properties_provider_init (GtkStyleProviderIface *iface)
284 {
285 }
286
287 static GtkCssValue *
288 gtk_style_properties_provider_get_color (GtkStyleProviderPrivate *provider,
289                                          const char              *name)
290 {
291   return _gtk_symbolic_color_get_css_value (gtk_style_properties_lookup_color (GTK_STYLE_PROPERTIES (provider), name));
292 }
293
294 static void
295 gtk_style_properties_provider_lookup (GtkStyleProviderPrivate *provider,
296                                       const GtkCssMatcher     *matcher,
297                                       GtkCssLookup            *lookup)
298 {
299   GtkStyleProperties *props;
300   GtkStylePropertiesPrivate *priv;
301   GHashTableIter iter;
302   gpointer key, value;
303
304   props = GTK_STYLE_PROPERTIES (provider);
305   priv = props->priv;
306
307   /* Merge symbolic style properties */
308   g_hash_table_iter_init (&iter, priv->properties);
309
310   while (g_hash_table_iter_next (&iter, &key, &value))
311     {
312       GtkCssStyleProperty *prop = key;
313       PropertyData *data = value;
314       GtkCssValue *value;
315       guint id;
316
317       id = _gtk_css_style_property_get_id (prop);
318
319       if (!_gtk_css_lookup_is_missing (lookup, id))
320           continue;
321
322       value = property_data_match_state (data, _gtk_css_matcher_get_state (matcher));
323       if (value == NULL)
324         continue;
325
326       _gtk_css_lookup_set_computed (lookup, id, NULL, value);
327     }
328 }
329
330 static GtkCssChange
331 gtk_style_properties_provider_get_change (GtkStyleProviderPrivate *provider,
332                                           const GtkCssMatcher     *matcher)
333 {
334   return GTK_CSS_CHANGE_STATE;
335 }
336
337 static void
338 gtk_style_properties_provider_private_init (GtkStyleProviderPrivateInterface *iface)
339 {
340   iface->get_color = gtk_style_properties_provider_get_color;
341   iface->lookup = gtk_style_properties_provider_lookup;
342   iface->get_change = gtk_style_properties_provider_get_change;
343 }
344
345 /* GtkStyleProperties methods */
346
347 /**
348  * gtk_style_properties_new:
349  *
350  * Returns a newly created #GtkStyleProperties
351  *
352  * Returns: a new #GtkStyleProperties
353  **/
354 GtkStyleProperties *
355 gtk_style_properties_new (void)
356 {
357   return g_object_new (GTK_TYPE_STYLE_PROPERTIES, NULL);
358 }
359
360 /**
361  * gtk_style_properties_map_color:
362  * @props: a #GtkStyleProperties
363  * @name: color name
364  * @color: #GtkSymbolicColor to map @name to
365  *
366  * Maps @color so it can be referenced by @name. See
367  * gtk_style_properties_lookup_color()
368  *
369  * Since: 3.0
370  **/
371 void
372 gtk_style_properties_map_color (GtkStyleProperties *props,
373                                 const gchar        *name,
374                                 GtkSymbolicColor   *color)
375 {
376   GtkStylePropertiesPrivate *priv;
377
378   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
379   g_return_if_fail (name != NULL);
380   g_return_if_fail (color != NULL);
381
382   priv = props->priv;
383
384   if (G_UNLIKELY (!priv->color_map))
385     priv->color_map = g_hash_table_new_full (g_str_hash,
386                                              g_str_equal,
387                                              (GDestroyNotify) g_free,
388                                              (GDestroyNotify) gtk_symbolic_color_unref);
389
390   g_hash_table_replace (priv->color_map,
391                         g_strdup (name),
392                         gtk_symbolic_color_ref (color));
393
394   _gtk_style_provider_private_changed (GTK_STYLE_PROVIDER_PRIVATE (props));
395 }
396
397 /**
398  * gtk_style_properties_lookup_color:
399  * @props: a #GtkStyleProperties
400  * @name: color name to lookup
401  *
402  * Returns the symbolic color that is mapped
403  * to @name.
404  *
405  * Returns: (transfer none): The mapped color
406  *
407  * Since: 3.0
408  **/
409 GtkSymbolicColor *
410 gtk_style_properties_lookup_color (GtkStyleProperties *props,
411                                    const gchar        *name)
412 {
413   GtkStylePropertiesPrivate *priv;
414
415   g_return_val_if_fail (GTK_IS_STYLE_PROPERTIES (props), NULL);
416   g_return_val_if_fail (name != NULL, NULL);
417
418   priv = props->priv;
419
420   if (!priv->color_map)
421     return NULL;
422
423   return g_hash_table_lookup (priv->color_map, name);
424 }
425
426 void
427 _gtk_style_properties_set_property_by_property (GtkStyleProperties  *props,
428                                                 GtkCssStyleProperty *style_prop,
429                                                 GtkStateFlags        state,
430                                                 GtkCssValue         *value)
431 {
432   GtkStylePropertiesPrivate *priv;
433   PropertyData *prop;
434   ValueData *val;
435
436   priv = props->priv;
437   prop = g_hash_table_lookup (priv->properties, style_prop);
438
439   if (!prop)
440     {
441       prop = property_data_new ();
442       g_hash_table_insert (priv->properties, (gpointer) style_prop, prop);
443     }
444
445   val = property_data_get_value (prop, state);
446
447   _gtk_css_value_unref (val->value);
448   val->value = _gtk_css_value_ref (value);
449
450   _gtk_style_provider_private_changed (GTK_STYLE_PROVIDER_PRIVATE (props));
451 }
452
453 /**
454  * gtk_style_properties_set_property:
455  * @props: a #GtkStyleProperties
456  * @property: styling property to set
457  * @state: state to set the value for
458  * @value: new value for the property
459  *
460  * Sets a styling property in @props.
461  *
462  * Since: 3.0
463  **/
464 void
465 gtk_style_properties_set_property (GtkStyleProperties *props,
466                                    const gchar        *property,
467                                    GtkStateFlags       state,
468                                    const GValue       *value)
469 {
470   GtkStyleProperty *node;
471
472   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
473   g_return_if_fail (property != NULL);
474   g_return_if_fail (value != NULL);
475
476   node = _gtk_style_property_lookup (property);
477
478   if (!node)
479     {
480       g_warning ("Style property \"%s\" is not registered", property);
481       return;
482     }
483   if (_gtk_style_property_get_value_type (node) == G_TYPE_NONE)
484     {
485       g_warning ("Style property \"%s\" is not settable", property);
486       return;
487     }
488   
489   _gtk_style_property_assign (node, props, state, value);
490 }
491
492 /**
493  * gtk_style_properties_set_valist:
494  * @props: a #GtkStyleProperties
495  * @state: state to set the values for
496  * @args: va_list of property name/value pairs, followed by %NULL
497  *
498  * Sets several style properties on @props.
499  *
500  * Since: 3.0
501  **/
502 void
503 gtk_style_properties_set_valist (GtkStyleProperties *props,
504                                  GtkStateFlags       state,
505                                  va_list             args)
506 {
507   const gchar *property_name;
508
509   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
510
511   property_name = va_arg (args, const gchar *);
512
513   while (property_name)
514     {
515       GtkStyleProperty *node;
516       gchar *error = NULL;
517       GType val_type;
518       GValue val = G_VALUE_INIT;
519
520       node = _gtk_style_property_lookup (property_name);
521
522       if (!node)
523         {
524           g_warning ("Style property \"%s\" is not registered", property_name);
525           break;
526         }
527
528       val_type = _gtk_style_property_get_value_type (node);
529       if (val_type == G_TYPE_NONE)
530         {
531           g_warning ("Style property \"%s\" is not settable", property_name);
532           break;
533         }
534
535       G_VALUE_COLLECT_INIT (&val, _gtk_style_property_get_value_type (node),
536                             args, 0, &error);
537       if (error)
538         {
539           g_warning ("Could not set style property \"%s\": %s", property_name, error);
540           g_value_unset (&val);
541           g_free (error);
542           break;
543         }
544
545       _gtk_style_property_assign (node, props, state, &val);
546       g_value_unset (&val);
547
548       property_name = va_arg (args, const gchar *);
549     }
550 }
551
552 /**
553  * gtk_style_properties_set:
554  * @props: a #GtkStyleProperties
555  * @state: state to set the values for
556  * @...: property name/value pairs, followed by %NULL
557  *
558  * Sets several style properties on @props.
559  *
560  * Since: 3.0
561  **/
562 void
563 gtk_style_properties_set (GtkStyleProperties *props,
564                           GtkStateFlags       state,
565                           ...)
566 {
567   va_list args;
568
569   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
570
571   va_start (args, state);
572   gtk_style_properties_set_valist (props, state, args);
573   va_end (args);
574 }
575
576 GtkCssValue *
577 _gtk_style_properties_peek_property (GtkStyleProperties  *props,
578                                      GtkCssStyleProperty *property,
579                                      GtkStateFlags        state)
580 {
581   GtkStylePropertiesPrivate *priv;
582   PropertyData *prop;
583
584   g_return_val_if_fail (GTK_IS_STYLE_PROPERTIES (props), FALSE);
585   g_return_val_if_fail (property != NULL, FALSE);
586
587   priv = props->priv;
588   prop = g_hash_table_lookup (priv->properties, property);
589   if (prop == NULL)
590     return NULL;
591
592   return property_data_match_state (prop, state);
593 }
594
595 typedef struct {
596   GtkStyleProperties *props;
597   GtkStateFlags       state;
598 } StyleQueryData;
599
600 static GtkCssValue *
601 style_query_func (guint    id,
602                   gpointer data)
603 {
604   StyleQueryData *query = data;
605
606   return _gtk_style_properties_peek_property (query->props,
607                                               _gtk_css_style_property_lookup_by_id (id),
608                                               query->state);
609 }
610
611 /**
612  * gtk_style_properties_get_property:
613  * @props: a #GtkStyleProperties
614  * @property: style property name
615  * @state: state to retrieve the property value for
616  * @value: (out) (transfer full):  return location for the style property value.
617  *
618  * Gets a style property from @props for the given state. When done with @value,
619  * g_value_unset() needs to be called to free any allocated memory.
620  *
621  * Returns: %TRUE if the property exists in @props, %FALSE otherwise
622  *
623  * Since: 3.0
624  **/
625 gboolean
626 gtk_style_properties_get_property (GtkStyleProperties *props,
627                                    const gchar        *property,
628                                    GtkStateFlags       state,
629                                    GValue             *value)
630 {
631   StyleQueryData query = { props, state };
632   GtkStyleProperty *node;
633
634   g_return_val_if_fail (GTK_IS_STYLE_PROPERTIES (props), FALSE);
635   g_return_val_if_fail (property != NULL, FALSE);
636   g_return_val_if_fail (value != NULL, FALSE);
637
638   node = _gtk_style_property_lookup (property);
639   if (!node)
640     {
641       g_warning ("Style property \"%s\" is not registered", property);
642       return FALSE;
643     }
644   if (_gtk_style_property_get_value_type (node) == G_TYPE_NONE)
645     {
646       g_warning ("Style property \"%s\" is not gettable", property);
647       return FALSE;
648     }
649
650   _gtk_style_property_query (node,
651                              value,
652                              style_query_func,
653                              &query);
654
655   return TRUE;
656 }
657
658 /**
659  * gtk_style_properties_get_valist:
660  * @props: a #GtkStyleProperties
661  * @state: state to retrieve the property values for
662  * @args: va_list of property name/return location pairs, followed by %NULL
663  *
664  * Retrieves several style property values from @props for a given state.
665  *
666  * Since: 3.0
667  **/
668 void
669 gtk_style_properties_get_valist (GtkStyleProperties *props,
670                                  GtkStateFlags       state,
671                                  va_list             args)
672 {
673   const gchar *property_name;
674
675   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
676
677   property_name = va_arg (args, const gchar *);
678
679   while (property_name)
680     {
681       gchar *error = NULL;
682       GValue value = G_VALUE_INIT;
683
684       if (!gtk_style_properties_get_property (props,
685                                               property_name,
686                                               state,
687                                               &value))
688         break;
689
690       G_VALUE_LCOPY (&value, args, 0, &error);
691       g_value_unset (&value);
692
693       if (error)
694         {
695           g_warning ("Could not get style property \"%s\": %s", property_name, error);
696           g_free (error);
697           break;
698         }
699
700       property_name = va_arg (args, const gchar *);
701     }
702 }
703
704 /**
705  * gtk_style_properties_get:
706  * @props: a #GtkStyleProperties
707  * @state: state to retrieve the property values for
708  * @...: property name /return value pairs, followed by %NULL
709  *
710  * Retrieves several style property values from @props for a
711  * given state.
712  *
713  * Since: 3.0
714  **/
715 void
716 gtk_style_properties_get (GtkStyleProperties *props,
717                           GtkStateFlags       state,
718                           ...)
719 {
720   va_list args;
721
722   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
723
724   va_start (args, state);
725   gtk_style_properties_get_valist (props, state, args);
726   va_end (args);
727 }
728
729 /**
730  * gtk_style_properties_unset_property:
731  * @props: a #GtkStyleProperties
732  * @property: property to unset
733  * @state: state to unset
734  *
735  * Unsets a style property in @props.
736  *
737  * Since: 3.0
738  **/
739 void
740 gtk_style_properties_unset_property (GtkStyleProperties *props,
741                                      const gchar        *property,
742                                      GtkStateFlags       state)
743 {
744   GtkStylePropertiesPrivate *priv;
745   GtkStyleProperty *node;
746   PropertyData *prop;
747   guint pos;
748
749   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
750   g_return_if_fail (property != NULL);
751
752   node = _gtk_style_property_lookup (property);
753
754   if (!node)
755     {
756       g_warning ("Style property \"%s\" is not registered", property);
757       return;
758     }
759   if (_gtk_style_property_get_value_type (node) == G_TYPE_NONE)
760     {
761       g_warning ("Style property \"%s\" is not settable", property);
762       return;
763     }
764
765   if (GTK_IS_CSS_SHORTHAND_PROPERTY (node))
766     {
767       GtkCssShorthandProperty *shorthand = GTK_CSS_SHORTHAND_PROPERTY (node);
768
769       for (pos = 0; pos < _gtk_css_shorthand_property_get_n_subproperties (shorthand); pos++)
770         {
771           GtkCssStyleProperty *sub = _gtk_css_shorthand_property_get_subproperty (shorthand, pos);
772           gtk_style_properties_unset_property (props,
773                                                _gtk_style_property_get_name (GTK_STYLE_PROPERTY (sub)),
774                                                state);
775         }
776       return;
777     }
778
779   priv = props->priv;
780   prop = g_hash_table_lookup (priv->properties, node);
781
782   if (!prop)
783     return;
784
785   if (property_data_find_position (prop, state, &pos))
786     {
787       ValueData *data;
788
789       data = &g_array_index (prop->values, ValueData, pos);
790
791       _gtk_css_value_unref (data->value);
792       data->value = NULL;
793
794       g_array_remove_index (prop->values, pos);
795
796       _gtk_style_provider_private_changed (GTK_STYLE_PROVIDER_PRIVATE (props));
797     }
798 }
799
800 /**
801  * gtk_style_properties_clear:
802  * @props: a #GtkStyleProperties
803  *
804  * Clears all style information from @props.
805  **/
806 void
807 gtk_style_properties_clear (GtkStyleProperties *props)
808 {
809   GtkStylePropertiesPrivate *priv;
810
811   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
812
813   priv = props->priv;
814   g_hash_table_remove_all (priv->properties);
815
816   _gtk_style_provider_private_changed (GTK_STYLE_PROVIDER_PRIVATE (props));
817 }
818
819 /**
820  * gtk_style_properties_merge:
821  * @props: a #GtkStyleProperties
822  * @props_to_merge: a second #GtkStyleProperties
823  * @replace: whether to replace values or not
824  *
825  * Merges into @props all the style information contained
826  * in @props_to_merge. If @replace is %TRUE, the values
827  * will be overwritten, if it is %FALSE, the older values
828  * will prevail.
829  *
830  * Since: 3.0
831  **/
832 void
833 gtk_style_properties_merge (GtkStyleProperties       *props,
834                             const GtkStyleProperties *props_to_merge,
835                             gboolean                  replace)
836 {
837   GtkStylePropertiesPrivate *priv, *priv_to_merge;
838   GHashTableIter iter;
839   gpointer key, value;
840
841   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
842   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props_to_merge));
843
844   priv = props->priv;
845   priv_to_merge = props_to_merge->priv;
846
847   /* Merge symbolic color map */
848   if (priv_to_merge->color_map)
849     {
850       g_hash_table_iter_init (&iter, priv_to_merge->color_map);
851
852       while (g_hash_table_iter_next (&iter, &key, &value))
853         {
854           const gchar *name;
855           GtkSymbolicColor *color;
856
857           name = key;
858           color = value;
859
860           if (!replace &&
861               g_hash_table_lookup (priv->color_map, name))
862             continue;
863
864           gtk_style_properties_map_color (props, name, color);
865         }
866     }
867
868   /* Merge symbolic style properties */
869   g_hash_table_iter_init (&iter, priv_to_merge->properties);
870
871   while (g_hash_table_iter_next (&iter, &key, &value))
872     {
873       PropertyData *prop_to_merge = value;
874       PropertyData *prop;
875       guint i;
876
877       prop = g_hash_table_lookup (priv->properties, key);
878
879       if (!prop)
880         {
881           prop = property_data_new ();
882           g_hash_table_insert (priv->properties, key, prop);
883         }
884
885       for (i = 0; i < prop_to_merge->values->len; i++)
886         {
887           ValueData *data;
888           ValueData *value;
889
890           data = &g_array_index (prop_to_merge->values, ValueData, i);
891
892           if (replace && data->state == GTK_STATE_FLAG_NORMAL &&
893               _gtk_is_css_typed_value_of_type (data->value, PANGO_TYPE_FONT_DESCRIPTION))
894             {
895               /* Let normal state override all states
896                * previously set in the original set
897                */
898               property_data_remove_values (prop);
899             }
900
901           value = property_data_get_value (prop, data->state);
902
903           if (_gtk_is_css_typed_value_of_type (data->value, PANGO_TYPE_FONT_DESCRIPTION) &&
904               value->value != NULL)
905             {
906               PangoFontDescription *font_desc;
907               PangoFontDescription *font_desc_to_merge;
908
909               /* Handle merging of font descriptions */
910               font_desc = g_value_get_boxed (_gtk_css_typed_value_get (value->value));
911               font_desc_to_merge = g_value_get_boxed (_gtk_css_typed_value_get (data->value));
912
913               pango_font_description_merge (font_desc, font_desc_to_merge, replace);
914             }
915           else if (_gtk_is_css_typed_value_of_type (data->value, G_TYPE_PTR_ARRAY) &&
916                    value->value != NULL)
917             {
918               GPtrArray *array, *array_to_merge;
919               gint i;
920
921               /* Append the array, mainly thought
922                * for the gtk-key-bindings property
923                */
924               array = g_value_get_boxed (_gtk_css_typed_value_get (value->value));
925               array_to_merge = g_value_get_boxed (_gtk_css_typed_value_get (data->value));
926
927               for (i = 0; i < array_to_merge->len; i++)
928                 g_ptr_array_add (array, g_ptr_array_index (array_to_merge, i));
929             }
930           else if (replace || value->value == NULL)
931             {
932               _gtk_css_value_unref (value->value);
933               value->value = _gtk_css_value_ref (data->value);
934             }
935         }
936     }
937
938   _gtk_style_provider_private_changed (GTK_STYLE_PROVIDER_PRIVATE (props));
939 }