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