]> Pileus Git - ~andy/gtk/blob - gtk/gtkstyleset.c
Add GtkWidgetPath parameter to gtk_style_provider_get_style().
[~andy/gtk] / gtk / gtkstyleset.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "config.h"
21
22 #include <stdlib.h>
23 #include <gobject/gvaluecollector.h>
24
25 #include "gtkstyleprovider.h"
26 #include "gtkstyleset.h"
27 #include "gtkprivate.h"
28 #include "gtkintl.h"
29
30 #include "gtkalias.h"
31
32 typedef struct GtkStyleSetPrivate GtkStyleSetPrivate;
33 typedef struct PropertyData PropertyData;
34 typedef struct PropertyNode PropertyNode;
35
36 struct PropertyNode
37 {
38   GQuark property_quark;
39   GType property_type;
40   GValue default_value;
41 };
42
43 struct PropertyData
44 {
45   GValue values[GTK_STATE_LAST];
46 };
47
48 struct GtkStyleSetPrivate
49 {
50   GHashTable *properties;
51 };
52
53 static GArray *properties = NULL;
54
55 #define GTK_STYLE_SET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_STYLE_SET, GtkStyleSetPrivate))
56
57 static void gtk_style_set_provider_init (GtkStyleProviderIface *iface);
58 static void gtk_style_set_finalize      (GObject      *object);
59
60
61 G_DEFINE_TYPE_EXTENDED (GtkStyleSet, gtk_style_set, G_TYPE_OBJECT, 0,
62                         G_IMPLEMENT_INTERFACE (GTK_TYPE_STYLE_PROVIDER,
63                                                gtk_style_set_provider_init));
64
65 static void
66 gtk_style_set_class_init (GtkStyleSetClass *klass)
67 {
68   GObjectClass *object_class = G_OBJECT_CLASS (klass);
69   GdkColor black = { 0, 0, 0, 0 };
70   GdkColor white = { 0, 65535, 65535, 65535 };
71   PangoFontDescription *font_desc;
72   GtkBorder padding = { 0 };
73
74   object_class->finalize = gtk_style_set_finalize;
75
76   /* Initialize default property set */
77   gtk_style_set_register_property_color ("foreground-color", &white);
78   gtk_style_set_register_property_color ("background-color", &black);
79   gtk_style_set_register_property_color ("text-color", &white);
80   gtk_style_set_register_property_color ("base-color", &white);
81
82   font_desc = pango_font_description_from_string ("Sans 10");
83   gtk_style_set_register_property_font ("font", font_desc);
84   pango_font_description_free (font_desc);
85
86   gtk_style_set_register_property_border ("padding", &padding);
87
88   g_type_class_add_private (object_class, sizeof (GtkStyleSetPrivate));
89 }
90
91 static PropertyData *
92 property_data_new (void)
93 {
94   PropertyData *data;
95
96   data = g_slice_new0 (PropertyData);
97
98   return data;
99 }
100
101 static void
102 property_data_free (PropertyData *data)
103 {
104   gint i;
105
106   for (i = 0; i <= GTK_STATE_INSENSITIVE; i++)
107     {
108       if (G_IS_VALUE (&data->values[i]))
109         g_value_unset (&data->values[i]);
110     }
111
112   g_slice_free (PropertyData, data);
113 }
114
115 static void
116 gtk_style_set_init (GtkStyleSet *set)
117 {
118   GtkStyleSetPrivate *priv;
119
120   priv = GTK_STYLE_SET_GET_PRIVATE (set);
121   priv->properties = g_hash_table_new_full (NULL, NULL, NULL,
122                                             (GDestroyNotify) property_data_free);
123 }
124
125 static void
126 gtk_style_set_finalize (GObject *object)
127 {
128   GtkStyleSetPrivate *priv;
129
130   priv = GTK_STYLE_SET_GET_PRIVATE (object);
131   g_hash_table_destroy (priv->properties);
132
133   G_OBJECT_CLASS (gtk_style_set_parent_class)->finalize (object);
134 }
135
136 GtkStyleSet *
137 gtk_style_set_get_style (GtkStyleProvider *provider,
138                          GtkWidgetPath    *path)
139 {
140   /* Return style set itself */
141   return g_object_ref (provider);
142 }
143
144 static void
145 gtk_style_set_provider_init (GtkStyleProviderIface *iface)
146 {
147   iface->get_style = gtk_style_set_get_style;
148 }
149
150 static int
151 compare_property (gconstpointer p1,
152                   gconstpointer p2)
153 {
154   PropertyNode *key = (PropertyNode *) p1;
155   PropertyNode *node = (PropertyNode *) p2;
156
157   return (int) key->property_quark - node->property_quark;
158 }
159
160 static PropertyNode *
161 property_node_lookup (GQuark quark)
162 {
163   PropertyNode key = { 0 };
164
165   if (!quark)
166     return NULL;
167
168   if (!properties)
169     return NULL;
170
171   key.property_quark = quark;
172
173   return bsearch (&key, properties->data, properties->len,
174                   sizeof (PropertyNode), compare_property);
175 }
176
177 /* Property registration functions */
178 void
179 gtk_style_set_register_property (const gchar  *property_name,
180                                  GType         type,
181                                  const GValue *default_value)
182 {
183   PropertyNode *node, new = { 0 };
184   GQuark quark;
185   gint i;
186
187   g_return_if_fail (property_name != NULL);
188   g_return_if_fail (default_value != NULL);
189   g_return_if_fail (type == G_VALUE_TYPE (default_value));
190
191   if (G_UNLIKELY (!properties))
192     properties = g_array_new (FALSE, TRUE, sizeof (PropertyNode));
193
194   quark = g_quark_try_string (property_name);
195
196   if ((node = property_node_lookup (quark)) != NULL)
197     {
198       g_warning ("Property \"%s\" was already registered with type %s",
199                  property_name, g_type_name (node->property_type));
200       return;
201     }
202
203   quark = g_quark_from_string (property_name);
204
205   new.property_quark = quark;
206   new.property_type = type;
207
208   g_value_init (&new.default_value, type);
209   g_value_copy (default_value, &new.default_value);
210
211   for (i = 0; i < properties->len; i++)
212     {
213       node = &g_array_index (properties, PropertyNode, i);
214
215       if (node->property_quark > quark)
216         break;
217     }
218
219   g_array_insert_val (properties, i, new);
220 }
221
222 gboolean
223 gtk_style_set_lookup_property (const gchar *property_name,
224                                GType       *type,
225                                GValue      *default_value)
226 {
227   PropertyNode *node;
228   GtkStyleSetClass *klass;
229   GQuark quark;
230   GType t;
231   gint i;
232
233   g_return_val_if_fail (property_name != NULL, FALSE);
234
235   klass = g_type_class_ref (GTK_TYPE_STYLE_SET);
236   quark = g_quark_try_string (property_name);
237
238   if (quark == 0)
239     {
240       g_type_class_unref (klass);
241       return FALSE;
242     }
243
244   for (i = 0; i < properties->len; i++)
245     {
246       node = &g_array_index (properties, PropertyNode, i);
247
248       if (node->property_quark == quark)
249         {
250           if (type)
251             *type = node->property_type;
252
253           if (default_value)
254             {
255               g_value_init (default_value, G_VALUE_TYPE (&node->default_value));
256               g_value_copy (&node->default_value, default_value);
257             }
258
259           g_type_class_unref (klass);
260
261           return TRUE;
262         }
263       else if (node->property_quark > quark)
264         break;
265     }
266
267   g_type_class_unref (klass);
268
269   return FALSE;
270 }
271
272 void
273 gtk_style_set_register_property_color (const gchar *property_name,
274                                        GdkColor    *initial_value)
275 {
276   GValue value = { 0 };
277
278   g_return_if_fail (property_name != NULL);
279   g_return_if_fail (initial_value != NULL);
280
281   g_value_init (&value, GDK_TYPE_COLOR);
282   g_value_set_boxed (&value, initial_value);
283
284   gtk_style_set_register_property (property_name, GDK_TYPE_COLOR, &value);
285
286   g_value_unset (&value);
287 }
288
289 void
290 gtk_style_set_register_property_font (const gchar          *property_name,
291                                       PangoFontDescription *initial_value)
292 {
293   GValue value = { 0 };
294
295   g_return_if_fail (property_name != NULL);
296   g_return_if_fail (initial_value != NULL);
297
298   g_value_init (&value, PANGO_TYPE_FONT_DESCRIPTION);
299   g_value_set_boxed (&value, initial_value);
300
301   gtk_style_set_register_property (property_name, PANGO_TYPE_FONT_DESCRIPTION, &value);
302
303   g_value_unset (&value);
304 }
305
306 void
307 gtk_style_set_register_property_border (const gchar *property_name,
308                                         GtkBorder   *initial_value)
309 {
310   GValue value = { 0 };
311
312   g_return_if_fail (property_name != NULL);
313   g_return_if_fail (initial_value != NULL);
314
315   g_value_init (&value, GTK_TYPE_BORDER);
316   g_value_set_boxed (&value, initial_value);
317
318   gtk_style_set_register_property (property_name, GTK_TYPE_BORDER, &value);
319
320   g_value_unset (&value);
321 }
322
323 void
324 gtk_style_set_register_property_int (const gchar *property_name,
325                                      gint         initial_value)
326 {
327   GValue value = { 0 };
328
329   g_return_if_fail (property_name != NULL);
330
331   g_value_init (&value, G_TYPE_INT);
332   g_value_set_int (&value, initial_value);
333
334   gtk_style_set_register_property (property_name, G_TYPE_INT, &value);
335
336   g_value_unset (&value);
337 }
338
339 void
340 gtk_style_set_register_property_uint (const gchar *property_name,
341                                       guint        initial_value)
342 {
343   GValue value = { 0 };
344
345   g_return_if_fail (property_name != NULL);
346
347   g_value_init (&value, G_TYPE_UINT);
348   g_value_set_uint (&value, initial_value);
349
350   gtk_style_set_register_property (property_name, G_TYPE_UINT, &value);
351
352   g_value_unset (&value);
353 }
354
355 void
356 gtk_style_set_register_property_double (const gchar *property_name,
357                                         gdouble      initial_value)
358 {
359   GValue value = { 0 };
360
361   g_return_if_fail (property_name != NULL);
362
363   g_value_init (&value, G_TYPE_DOUBLE);
364   g_value_set_double (&value, initial_value);
365
366   gtk_style_set_register_property (property_name, G_TYPE_DOUBLE, &value);
367
368   g_value_unset (&value);
369 }
370
371 /* GtkStyleSet methods */
372
373 GtkStyleSet *
374 gtk_style_set_new (void)
375 {
376   return g_object_new (GTK_TYPE_STYLE_SET, NULL);
377 }
378
379 void
380 gtk_style_set_set_property (GtkStyleSet  *set,
381                             const gchar  *property,
382                             GtkStateType  state,
383                             const GValue *value)
384 {
385   GtkStyleSetPrivate *priv;
386   PropertyNode *node;
387   PropertyData *prop;
388
389   g_return_if_fail (GTK_IS_STYLE_SET (set));
390   g_return_if_fail (property != NULL);
391   g_return_if_fail (state < GTK_STATE_LAST);
392   g_return_if_fail (value != NULL);
393
394   node = property_node_lookup (g_quark_try_string (property));
395
396   if (!node)
397     {
398       g_warning ("Style property \"%s\" is not registered", property);
399       return;
400     }
401
402   g_return_if_fail (node->property_type == G_VALUE_TYPE (value));
403
404   priv = GTK_STYLE_SET_GET_PRIVATE (set);
405   prop = g_hash_table_lookup (priv->properties,
406                               GINT_TO_POINTER (node->property_quark));
407
408   if (!prop)
409     {
410       prop = property_data_new ();
411       g_hash_table_insert (priv->properties,
412                            GINT_TO_POINTER (node->property_quark),
413                            prop);
414     }
415
416   if (G_IS_VALUE (&prop->values[state]))
417     g_value_reset (&prop->values[state]);
418   else
419     g_value_init (&prop->values[state], node->property_type);
420
421   g_value_copy (value, &prop->values[state]);
422 }
423
424
425 void
426 gtk_style_set_set_valist (GtkStyleSet  *set,
427                           GtkStateType  state,
428                           va_list       args)
429 {
430   GtkStyleSetPrivate *priv;
431   const gchar *property_name;
432
433   g_return_if_fail (GTK_IS_STYLE_SET (set));
434   g_return_if_fail (state < GTK_STATE_LAST);
435
436   priv = GTK_STYLE_SET_GET_PRIVATE (set);
437   property_name = va_arg (args, const gchar *);
438
439   while (property_name)
440     {
441       PropertyNode *node;
442       PropertyData *prop;
443       gchar *error = NULL;
444
445       node = property_node_lookup (g_quark_try_string (property_name));
446
447       if (!node)
448         {
449           g_warning ("Style property \"%s\" is not registered", property_name);
450           break;
451         }
452
453       prop = g_hash_table_lookup (priv->properties,
454                                   GINT_TO_POINTER (node->property_quark));
455
456       if (!prop)
457         {
458           prop = property_data_new ();
459           g_hash_table_insert (priv->properties,
460                                GINT_TO_POINTER (node->property_quark),
461                                prop);
462         }
463
464       g_value_init (&prop->values[state], node->property_type);
465       G_VALUE_COLLECT (&prop->values[state], args, 0, &error);
466
467       if (error)
468         {
469           g_warning ("Could not set style property \"%s\": %s", property_name, error);
470           g_value_unset (&prop->values[state]);
471           g_free (error);
472           break;
473         }
474
475       property_name = va_arg (args, const gchar *);
476     }
477 }
478
479 void
480 gtk_style_set_set (GtkStyleSet  *set,
481                    GtkStateType  state,
482                    ...)
483 {
484   va_list args;
485
486   g_return_if_fail (GTK_IS_STYLE_SET (set));
487   g_return_if_fail (state < GTK_STATE_LAST);
488
489   va_start (args, state);
490   gtk_style_set_set_valist (set, state, args);
491   va_end (args);
492 }
493
494 gboolean
495 gtk_style_set_get_property (GtkStyleSet  *set,
496                             const gchar  *property,
497                             GtkStateType  state,
498                             GValue       *value)
499 {
500   GtkStyleSetPrivate *priv;
501   PropertyNode *node;
502   PropertyData *prop;
503
504   g_return_val_if_fail (GTK_IS_STYLE_SET (set), FALSE);
505   g_return_val_if_fail (property != NULL, FALSE);
506   g_return_val_if_fail (state < GTK_STATE_LAST, FALSE);
507   g_return_val_if_fail (value != NULL, FALSE);
508
509   node = property_node_lookup (g_quark_try_string (property));
510
511   if (!node)
512     {
513       g_warning ("Style property \"%s\" is not registered", property);
514       return FALSE;
515     }
516
517   priv = GTK_STYLE_SET_GET_PRIVATE (set);
518   prop = g_hash_table_lookup (priv->properties,
519                               GINT_TO_POINTER (node->property_quark));
520
521   g_value_init (value, node->property_type);
522
523   if (!prop ||
524       !G_IS_VALUE (&prop->values[state]))
525     g_value_copy (&node->default_value, value);
526   else
527     g_value_copy (&prop->values[state], value);
528
529   return TRUE;
530 }
531
532 void
533 gtk_style_set_get_valist (GtkStyleSet  *set,
534                           GtkStateType  state,
535                           va_list       args)
536 {
537   GtkStyleSetPrivate *priv;
538   const gchar *property_name;
539
540   g_return_if_fail (GTK_IS_STYLE_SET (set));
541   g_return_if_fail (state < GTK_STATE_LAST);
542
543   priv = GTK_STYLE_SET_GET_PRIVATE (set);
544   property_name = va_arg (args, const gchar *);
545
546   while (property_name)
547     {
548       PropertyNode *node;
549       PropertyData *prop;
550       gchar *error = NULL;
551
552       node = property_node_lookup (g_quark_try_string (property_name));
553
554       if (!node)
555         {
556           g_warning ("Style property \"%s\" is not registered", property_name);
557           break;
558         }
559
560       prop = g_hash_table_lookup (priv->properties,
561                                   GINT_TO_POINTER (node->property_quark));
562
563       if (!prop ||
564           !G_IS_VALUE (&prop->values[state]))
565         G_VALUE_LCOPY (&node->default_value, args, 0, &error);
566       else
567         G_VALUE_LCOPY (&prop->values[state], args, 0, &error);
568
569       if (error)
570         {
571           g_warning ("Could not get style property \"%s\": %s", property_name, error);
572           g_free (error);
573           break;
574         }
575
576       property_name = va_arg (args, const gchar *);
577     }
578 }
579
580 void
581 gtk_style_set_get (GtkStyleSet  *set,
582                    GtkStateType  state,
583                    ...)
584 {
585   va_list args;
586
587   g_return_if_fail (GTK_IS_STYLE_SET (set));
588   g_return_if_fail (state < GTK_STATE_LAST);
589
590   va_start (args, state);
591   gtk_style_set_get_valist (set, state, args);
592   va_end (args);
593 }
594
595 void
596 gtk_style_set_unset_property (GtkStyleSet  *set,
597                               const gchar  *property,
598                               GtkStateType  state)
599 {
600   GtkStyleSetPrivate *priv;
601   PropertyNode *node;
602   PropertyData *prop;
603
604   g_return_if_fail (GTK_IS_STYLE_SET (set));
605   g_return_if_fail (property != NULL);
606   g_return_if_fail (state < GTK_STATE_LAST);
607
608   node = property_node_lookup (g_quark_try_string (property));
609
610   if (!node)
611     {
612       g_warning ("Style property \"%s\" is not registered", property);
613       return;
614     }
615
616   priv = GTK_STYLE_SET_GET_PRIVATE (set);
617   prop = g_hash_table_lookup (priv->properties,
618                               GINT_TO_POINTER (node->property_quark));
619
620   if (!prop)
621     return;
622
623   g_value_unset (&prop->values[state]);
624 }
625
626 void
627 gtk_style_set_clear (GtkStyleSet *set)
628 {
629   GtkStyleSetPrivate *priv;
630
631   g_return_if_fail (GTK_IS_STYLE_SET (set));
632
633   priv = GTK_STYLE_SET_GET_PRIVATE (set);
634   g_hash_table_remove_all (priv->properties);
635 }
636
637 void
638 gtk_style_set_merge (GtkStyleSet       *set,
639                      const GtkStyleSet *set_to_merge,
640                      gboolean           replace)
641 {
642   GtkStyleSetPrivate *priv, *priv_to_merge;
643   GHashTableIter iter;
644   gpointer key, value;
645
646   g_return_if_fail (GTK_IS_STYLE_SET (set));
647   g_return_if_fail (GTK_IS_STYLE_SET (set_to_merge));
648
649   priv = GTK_STYLE_SET_GET_PRIVATE (set);
650   priv_to_merge = GTK_STYLE_SET_GET_PRIVATE (set_to_merge);
651
652   g_hash_table_iter_init (&iter, priv_to_merge->properties);
653
654   while (g_hash_table_iter_next (&iter, &key, &value))
655     {
656       PropertyData *prop_to_merge = value;
657       PropertyData *prop = NULL;
658       GtkStateType i;
659
660       for (i = GTK_STATE_NORMAL; i < GTK_STATE_LAST; i++)
661         {
662           if (G_VALUE_TYPE (&prop_to_merge->values[i]) == G_TYPE_INVALID)
663             continue;
664
665           if (!prop)
666             prop = g_hash_table_lookup (priv->properties, key);
667
668           if (!prop)
669             {
670               prop = property_data_new ();
671               g_hash_table_insert (priv->properties, key, prop);
672             }
673
674           if (replace ||
675               G_VALUE_TYPE (&prop->values[i]) == G_TYPE_INVALID)
676             {
677               if (G_VALUE_TYPE (&prop->values[i]) == G_TYPE_INVALID)
678                 g_value_init (&prop->values[i], G_VALUE_TYPE (&prop_to_merge->values[i]));
679
680               g_value_copy (&prop_to_merge->values[i],
681                             &prop->values[i]);
682             }
683         }
684     }
685 }
686
687 #define __GTK_STYLE_SET_C__
688 #include "gtkaliasdef.c"