]> Pileus Git - ~andy/gtk/blob - gtk/gtkstyleproperty.c
styleproperty: Add flags
[~andy/gtk] / gtk / gtkstyleproperty.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 "gtkstylepropertyprivate.h"
23
24 #include <errno.h>
25 #include <math.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include <gdk-pixbuf/gdk-pixbuf.h>
30 #include <cairo-gobject.h>
31
32 #include "gtkcssprovider.h"
33 #include "gtkcssparserprivate.h"
34 #include "gtkcsstypesprivate.h"
35
36 /* the actual parsers we have */
37 #include "gtkanimationdescription.h"
38 #include "gtkbindings.h"
39 #include "gtk9slice.h"
40 #include "gtkgradient.h"
41 #include "gtkshadowprivate.h"
42 #include "gtkthemingengine.h"
43 #include "gtktypebuiltins.h"
44
45 static GHashTable *parse_funcs = NULL;
46 static GHashTable *print_funcs = NULL;
47 static GHashTable *properties = NULL;
48
49 static void
50 register_conversion_function (GType             type,
51                               GtkStyleParseFunc parse,
52                               GtkStylePrintFunc print)
53 {
54   if (parse)
55     g_hash_table_insert (parse_funcs, GSIZE_TO_POINTER (type), parse);
56   if (print)
57     g_hash_table_insert (print_funcs, GSIZE_TO_POINTER (type), print);
58 }
59
60 static void
61 string_append_double (GString *string,
62                       double   d)
63 {
64   char buf[G_ASCII_DTOSTR_BUF_SIZE];
65
66   g_ascii_dtostr (buf, sizeof (buf), d);
67   g_string_append (string, buf);
68 }
69
70 static void
71 string_append_string (GString    *str,
72                       const char *string)
73 {
74   gsize len;
75
76   g_string_append_c (str, '"');
77
78   do {
79     len = strcspn (string, "\"\n\r\f");
80     g_string_append (str, string);
81     string += len;
82     switch (*string)
83       {
84       case '\0':
85         break;
86       case '\n':
87         g_string_append (str, "\\A ");
88         break;
89       case '\r':
90         g_string_append (str, "\\D ");
91         break;
92       case '\f':
93         g_string_append (str, "\\C ");
94         break;
95       case '\"':
96         g_string_append (str, "\\\"");
97         break;
98       default:
99         g_assert_not_reached ();
100         break;
101       }
102   } while (*string);
103
104   g_string_append_c (str, '"');
105 }
106
107 /*** IMPLEMENTATIONS ***/
108
109 static gboolean 
110 rgba_value_parse (GtkCssParser *parser,
111                   GFile        *base,
112                   GValue       *value)
113 {
114   GtkSymbolicColor *symbolic;
115   GdkRGBA rgba;
116
117   symbolic = _gtk_css_parser_read_symbolic_color (parser);
118   if (symbolic == NULL)
119     return FALSE;
120
121   if (gtk_symbolic_color_resolve (symbolic, NULL, &rgba))
122     {
123       g_value_set_boxed (value, &rgba);
124       gtk_symbolic_color_unref (symbolic);
125     }
126   else
127     {
128       g_value_unset (value);
129       g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
130       g_value_take_boxed (value, symbolic);
131     }
132
133   return TRUE;
134 }
135
136 static void
137 rgba_value_print (const GValue *value,
138                   GString      *string)
139 {
140   const GdkRGBA *rgba = g_value_get_boxed (value);
141
142   if (rgba == NULL)
143     g_string_append (string, "none");
144   else
145     {
146       char *s = gdk_rgba_to_string (rgba);
147       g_string_append (string, s);
148       g_free (s);
149     }
150 }
151
152 static gboolean 
153 color_value_parse (GtkCssParser *parser,
154                    GFile        *base,
155                    GValue       *value)
156 {
157   GtkSymbolicColor *symbolic;
158   GdkRGBA rgba;
159
160   symbolic = _gtk_css_parser_read_symbolic_color (parser);
161   if (symbolic == NULL)
162     return FALSE;
163
164   if (gtk_symbolic_color_resolve (symbolic, NULL, &rgba))
165     {
166       GdkColor color;
167
168       color.red = rgba.red * 65535. + 0.5;
169       color.green = rgba.green * 65535. + 0.5;
170       color.blue = rgba.blue * 65535. + 0.5;
171
172       g_value_set_boxed (value, &color);
173     }
174   else
175     {
176       g_value_unset (value);
177       g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
178       g_value_take_boxed (value, symbolic);
179     }
180
181   return TRUE;
182 }
183
184 static void
185 color_value_print (const GValue *value,
186                    GString      *string)
187 {
188   const GdkColor *color = g_value_get_boxed (value);
189
190   if (color == NULL)
191     g_string_append (string, "none");
192   else
193     {
194       char *s = gdk_color_to_string (color);
195       g_string_append (string, s);
196       g_free (s);
197     }
198 }
199
200 static gboolean
201 symbolic_color_value_parse (GtkCssParser *parser,
202                             GFile        *base,
203                             GValue       *value)
204 {
205   GtkSymbolicColor *symbolic;
206
207   symbolic = _gtk_css_parser_read_symbolic_color (parser);
208   if (symbolic == NULL)
209     return FALSE;
210
211   g_value_take_boxed (value, symbolic);
212   return TRUE;
213 }
214
215 static void
216 symbolic_color_value_print (const GValue *value,
217                             GString      *string)
218 {
219   GtkSymbolicColor *symbolic = g_value_get_boxed (value);
220
221   if (symbolic == NULL)
222     g_string_append (string, "none");
223   else
224     {
225       char *s = gtk_symbolic_color_to_string (symbolic);
226       g_string_append (string, s);
227       g_free (s);
228     }
229 }
230
231 static gboolean
232 font_family_parse (GtkCssParser *parser,
233                    GFile        *base,
234                    GValue       *value)
235 {
236   GPtrArray *names;
237   char *name;
238
239   /* We don't special case generic families. Pango should do
240    * that for us */
241
242   names = g_ptr_array_new ();
243
244   do {
245     name = _gtk_css_parser_try_ident (parser, TRUE);
246     if (name)
247       {
248         GString *string = g_string_new (name);
249         g_free (name);
250         while ((name = _gtk_css_parser_try_ident (parser, TRUE)))
251           {
252             g_string_append_c (string, ' ');
253             g_string_append (string, name);
254             g_free (name);
255           }
256         name = g_string_free (string, FALSE);
257       }
258     else 
259       {
260         name = _gtk_css_parser_read_string (parser);
261         if (name == NULL)
262           {
263             g_ptr_array_free (names, TRUE);
264             return FALSE;
265           }
266       }
267
268     g_ptr_array_add (names, name);
269   } while (_gtk_css_parser_try (parser, ",", TRUE));
270
271   /* NULL-terminate array */
272   g_ptr_array_add (names, NULL);
273   g_value_set_boxed (value, g_ptr_array_free (names, FALSE));
274   return TRUE;
275 }
276
277 static void
278 font_family_value_print (const GValue *value,
279                          GString      *string)
280 {
281   const char **names = g_value_get_boxed (value);
282
283   if (names == NULL || *names == NULL)
284     {
285       g_string_append (string, "none");
286       return;
287     }
288
289   string_append_string (string, *names);
290   names++;
291   while (*names)
292     {
293       g_string_append (string, ", ");
294       string_append_string (string, *names);
295       names++;
296     }
297 }
298
299 static gboolean 
300 font_description_value_parse (GtkCssParser *parser,
301                               GFile        *base,
302                               GValue       *value)
303 {
304   PangoFontDescription *font_desc;
305   char *str;
306
307   str = _gtk_css_parser_read_value (parser);
308   if (str == NULL)
309     return FALSE;
310
311   font_desc = pango_font_description_from_string (str);
312   g_free (str);
313   g_value_take_boxed (value, font_desc);
314   return TRUE;
315 }
316
317 static void
318 font_description_value_print (const GValue *value,
319                               GString      *string)
320 {
321   const PangoFontDescription *desc = g_value_get_boxed (value);
322
323   if (desc == NULL)
324     g_string_append (string, "none");
325   else
326     {
327       char *s = pango_font_description_to_string (desc);
328       g_string_append (string, s);
329       g_free (s);
330     }
331 }
332
333 static gboolean 
334 boolean_value_parse (GtkCssParser *parser,
335                      GFile        *base,
336                      GValue       *value)
337 {
338   if (_gtk_css_parser_try (parser, "true", TRUE) ||
339       _gtk_css_parser_try (parser, "1", TRUE))
340     {
341       g_value_set_boolean (value, TRUE);
342       return TRUE;
343     }
344   else if (_gtk_css_parser_try (parser, "false", TRUE) ||
345            _gtk_css_parser_try (parser, "0", TRUE))
346     {
347       g_value_set_boolean (value, FALSE);
348       return TRUE;
349     }
350   else
351     {
352       _gtk_css_parser_error (parser, "Expected a boolean value");
353       return FALSE;
354     }
355 }
356
357 static void
358 boolean_value_print (const GValue *value,
359                      GString      *string)
360 {
361   if (g_value_get_boolean (value))
362     g_string_append (string, "true");
363   else
364     g_string_append (string, "false");
365 }
366
367 static gboolean 
368 int_value_parse (GtkCssParser *parser,
369                  GFile        *base,
370                  GValue       *value)
371 {
372   gint i;
373
374   if (!_gtk_css_parser_try_int (parser, &i))
375     {
376       _gtk_css_parser_error (parser, "Expected a valid integer value");
377       return FALSE;
378     }
379
380   g_value_set_int (value, i);
381   return TRUE;
382 }
383
384 static void
385 int_value_print (const GValue *value,
386                  GString      *string)
387 {
388   g_string_append_printf (string, "%d", g_value_get_int (value));
389 }
390
391 static gboolean 
392 uint_value_parse (GtkCssParser *parser,
393                   GFile        *base,
394                   GValue       *value)
395 {
396   guint u;
397
398   if (!_gtk_css_parser_try_uint (parser, &u))
399     {
400       _gtk_css_parser_error (parser, "Expected a valid unsigned value");
401       return FALSE;
402     }
403
404   g_value_set_uint (value, u);
405   return TRUE;
406 }
407
408 static void
409 uint_value_print (const GValue *value,
410                   GString      *string)
411 {
412   g_string_append_printf (string, "%u", g_value_get_uint (value));
413 }
414
415 static gboolean 
416 double_value_parse (GtkCssParser *parser,
417                     GFile        *base,
418                     GValue       *value)
419 {
420   gdouble d;
421
422   if (!_gtk_css_parser_try_double (parser, &d))
423     {
424       _gtk_css_parser_error (parser, "Expected a number");
425       return FALSE;
426     }
427
428   g_value_set_double (value, d);
429   return TRUE;
430 }
431
432 static void
433 double_value_print (const GValue *value,
434                     GString      *string)
435 {
436   string_append_double (string, g_value_get_double (value));
437 }
438
439 static gboolean 
440 float_value_parse (GtkCssParser *parser,
441                    GFile        *base,
442                    GValue       *value)
443 {
444   gdouble d;
445
446   if (!_gtk_css_parser_try_double (parser, &d))
447     {
448       _gtk_css_parser_error (parser, "Expected a number");
449       return FALSE;
450     }
451
452   g_value_set_float (value, d);
453   return TRUE;
454 }
455
456 static void
457 float_value_print (const GValue *value,
458                    GString      *string)
459 {
460   string_append_double (string, g_value_get_float (value));
461 }
462
463 static gboolean 
464 string_value_parse (GtkCssParser *parser,
465                     GFile        *base,
466                     GValue       *value)
467 {
468   char *str = _gtk_css_parser_read_string (parser);
469
470   if (str == NULL)
471     return FALSE;
472
473   g_value_take_string (value, str);
474   return TRUE;
475 }
476
477 static void
478 string_value_print (const GValue *value,
479                     GString      *str)
480 {
481   string_append_string (str, g_value_get_string (value));
482 }
483
484 static gboolean 
485 theming_engine_value_parse (GtkCssParser *parser,
486                             GFile        *base,
487                             GValue       *value)
488 {
489   GtkThemingEngine *engine;
490   char *str;
491
492   str = _gtk_css_parser_try_ident (parser, TRUE);
493   if (str == NULL)
494     {
495       _gtk_css_parser_error (parser, "Expected a valid theme name");
496       return FALSE;
497     }
498
499   engine = gtk_theming_engine_load (str);
500   if (engine == NULL)
501     {
502       _gtk_css_parser_error (parser, "Themeing engine '%s' not found", str);
503       g_free (str);
504       return FALSE;
505     }
506
507   g_value_set_object (value, engine);
508   g_free (str);
509   return TRUE;
510 }
511
512 static void
513 theming_engine_value_print (const GValue *value,
514                             GString      *string)
515 {
516   GtkThemingEngine *engine;
517   char *name;
518
519   engine = g_value_get_object (value);
520   if (engine == NULL)
521     g_string_append (string, "none");
522   else
523     {
524       /* XXX: gtk_theming_engine_get_name()? */
525       g_object_get (engine, "name", &name, NULL);
526       g_string_append (string, name);
527       g_free (name);
528     }
529 }
530
531 static gboolean 
532 animation_description_value_parse (GtkCssParser *parser,
533                                    GFile        *base,
534                                    GValue       *value)
535 {
536   GtkAnimationDescription *desc;
537   char *str;
538
539   str = _gtk_css_parser_read_value (parser);
540   if (str == NULL)
541     return FALSE;
542
543   desc = _gtk_animation_description_from_string (str);
544   g_free (str);
545
546   if (desc == NULL)
547     {
548       _gtk_css_parser_error (parser, "Invalid animation description");
549       return FALSE;
550     }
551   
552   g_value_take_boxed (value, desc);
553   return TRUE;
554 }
555
556 static void
557 animation_description_value_print (const GValue *value,
558                                    GString      *string)
559 {
560   GtkAnimationDescription *desc = g_value_get_boxed (value);
561
562   if (desc == NULL)
563     g_string_append (string, "none");
564   else
565     _gtk_animation_description_print (desc, string);
566 }
567
568 static gboolean 
569 border_value_parse (GtkCssParser *parser,
570                     GFile        *base,
571                     GValue       *value)
572 {
573   GtkBorder border = { 0, };
574   guint i, numbers[4];
575
576   for (i = 0; i < G_N_ELEMENTS (numbers); i++)
577     {
578       if (!_gtk_css_parser_try_uint (parser, &numbers[i]))
579         break;
580
581       /* XXX: shouldn't allow spaces here? */
582       _gtk_css_parser_try (parser, "px", TRUE);
583     }
584
585   if (i == 0)
586     {
587       _gtk_css_parser_error (parser, "Expected valid border");
588       return FALSE;
589     }
590
591   border.top = numbers[0];
592   if (i > 1)
593     border.right = numbers[1];
594   else
595     border.right = border.top;
596   if (i > 2)
597     border.bottom = numbers[2];
598   else
599     border.bottom = border.top;
600   if (i > 3)
601     border.left = numbers[3];
602   else
603     border.left = border.right;
604
605   g_value_set_boxed (value, &border);
606   return TRUE;
607 }
608
609 static void
610 border_value_print (const GValue *value, GString *string)
611 {
612   const GtkBorder *border = g_value_get_boxed (value);
613
614   if (border == NULL)
615     g_string_append (string, "none");
616   else if (border->left != border->right)
617     g_string_append_printf (string, "%d %d %d %d", border->top, border->right, border->bottom, border->left);
618   else if (border->top != border->bottom)
619     g_string_append_printf (string, "%d %d %d", border->top, border->right, border->bottom);
620   else if (border->top != border->left)
621     g_string_append_printf (string, "%d %d", border->top, border->right);
622   else
623     g_string_append_printf (string, "%d", border->top);
624 }
625
626 static gboolean 
627 gradient_value_parse (GtkCssParser *parser,
628                       GFile        *base,
629                       GValue       *value)
630 {
631   GtkGradient *gradient;
632   cairo_pattern_type_t type;
633   gdouble coords[6];
634   guint i;
635
636   if (!_gtk_css_parser_try (parser, "-gtk-gradient", TRUE))
637     {
638       _gtk_css_parser_error (parser,
639                              "Expected '-gtk-gradient'");
640       return FALSE;
641     }
642
643   if (!_gtk_css_parser_try (parser, "(", TRUE))
644     {
645       _gtk_css_parser_error (parser,
646                              "Expected '(' after '-gtk-gradient'");
647       return FALSE;
648     }
649
650   /* Parse gradient type */
651   if (_gtk_css_parser_try (parser, "linear", TRUE))
652     type = CAIRO_PATTERN_TYPE_LINEAR;
653   else if (_gtk_css_parser_try (parser, "radial", TRUE))
654     type = CAIRO_PATTERN_TYPE_RADIAL;
655   else
656     {
657       _gtk_css_parser_error (parser,
658                              "Gradient type must be 'radial' or 'linear'");
659       return FALSE;
660     }
661
662   /* Parse start/stop position parameters */
663   for (i = 0; i < 2; i++)
664     {
665       if (! _gtk_css_parser_try (parser, ",", TRUE))
666         {
667           _gtk_css_parser_error (parser,
668                                  "Expected ','");
669           return FALSE;
670         }
671
672       if (_gtk_css_parser_try (parser, "left", TRUE))
673         coords[i * 3] = 0;
674       else if (_gtk_css_parser_try (parser, "right", TRUE))
675         coords[i * 3] = 1;
676       else if (_gtk_css_parser_try (parser, "center", TRUE))
677         coords[i * 3] = 0.5;
678       else if (!_gtk_css_parser_try_double (parser, &coords[i * 3]))
679         {
680           _gtk_css_parser_error (parser,
681                                  "Expected a valid X coordinate");
682           return FALSE;
683         }
684
685       if (_gtk_css_parser_try (parser, "top", TRUE))
686         coords[i * 3 + 1] = 0;
687       else if (_gtk_css_parser_try (parser, "bottom", TRUE))
688         coords[i * 3 + 1] = 1;
689       else if (_gtk_css_parser_try (parser, "center", TRUE))
690         coords[i * 3 + 1] = 0.5;
691       else if (!_gtk_css_parser_try_double (parser, &coords[i * 3 + 1]))
692         {
693           _gtk_css_parser_error (parser,
694                                  "Expected a valid Y coordinate");
695           return FALSE;
696         }
697
698       if (type == CAIRO_PATTERN_TYPE_RADIAL)
699         {
700           /* Parse radius */
701           if (! _gtk_css_parser_try (parser, ",", TRUE))
702             {
703               _gtk_css_parser_error (parser,
704                                      "Expected ','");
705               return FALSE;
706             }
707
708           if (! _gtk_css_parser_try_double (parser, &coords[(i * 3) + 2]))
709             {
710               _gtk_css_parser_error (parser,
711                                      "Expected a numer for the radius");
712               return FALSE;
713             }
714         }
715     }
716
717   if (type == CAIRO_PATTERN_TYPE_LINEAR)
718     gradient = gtk_gradient_new_linear (coords[0], coords[1], coords[3], coords[4]);
719   else
720     gradient = gtk_gradient_new_radial (coords[0], coords[1], coords[2],
721                                         coords[3], coords[4], coords[5]);
722
723   while (_gtk_css_parser_try (parser, ",", TRUE))
724     {
725       GtkSymbolicColor *color;
726       gdouble position;
727
728       if (_gtk_css_parser_try (parser, "from", TRUE))
729         {
730           position = 0;
731
732           if (!_gtk_css_parser_try (parser, "(", TRUE))
733             {
734               gtk_gradient_unref (gradient);
735               _gtk_css_parser_error (parser,
736                                      "Expected '('");
737               return FALSE;
738             }
739
740         }
741       else if (_gtk_css_parser_try (parser, "to", TRUE))
742         {
743           position = 1;
744
745           if (!_gtk_css_parser_try (parser, "(", TRUE))
746             {
747               gtk_gradient_unref (gradient);
748               _gtk_css_parser_error (parser,
749                                      "Expected '('");
750               return FALSE;
751             }
752
753         }
754       else if (_gtk_css_parser_try (parser, "color-stop", TRUE))
755         {
756           if (!_gtk_css_parser_try (parser, "(", TRUE))
757             {
758               gtk_gradient_unref (gradient);
759               _gtk_css_parser_error (parser,
760                                      "Expected '('");
761               return FALSE;
762             }
763
764           if (!_gtk_css_parser_try_double (parser, &position))
765             {
766               gtk_gradient_unref (gradient);
767               _gtk_css_parser_error (parser,
768                                      "Expected a valid number");
769               return FALSE;
770             }
771
772           if (!_gtk_css_parser_try (parser, ",", TRUE))
773             {
774               gtk_gradient_unref (gradient);
775               _gtk_css_parser_error (parser,
776                                      "Expected a comma");
777               return FALSE;
778             }
779         }
780       else
781         {
782           gtk_gradient_unref (gradient);
783           _gtk_css_parser_error (parser,
784                                  "Not a valid color-stop definition");
785           return FALSE;
786         }
787
788       color = _gtk_css_parser_read_symbolic_color (parser);
789       if (color == NULL)
790         {
791           gtk_gradient_unref (gradient);
792           return FALSE;
793         }
794
795       gtk_gradient_add_color_stop (gradient, position, color);
796       gtk_symbolic_color_unref (color);
797
798       if (!_gtk_css_parser_try (parser, ")", TRUE))
799         {
800           gtk_gradient_unref (gradient);
801           _gtk_css_parser_error (parser,
802                                  "Expected ')'");
803           return FALSE;
804         }
805     }
806
807   if (!_gtk_css_parser_try (parser, ")", TRUE))
808     {
809       gtk_gradient_unref (gradient);
810       _gtk_css_parser_error (parser,
811                              "Expected ')'");
812       return FALSE;
813     }
814
815   g_value_take_boxed (value, gradient);
816   return TRUE;
817 }
818
819 static void
820 gradient_value_print (const GValue *value,
821                       GString      *string)
822 {
823   GtkGradient *gradient = g_value_get_boxed (value);
824
825   if (gradient == NULL)
826     g_string_append (string, "none");
827   else
828     {
829       char *s = gtk_gradient_to_string (gradient);
830       g_string_append (string, s);
831       g_free (s);
832     }
833 }
834
835 static GFile *
836 gtk_css_parse_url (GtkCssParser *parser,
837                    GFile        *base)
838 {
839   gchar *path;
840   GFile *file;
841
842   if (_gtk_css_parser_try (parser, "url", FALSE))
843     {
844       if (!_gtk_css_parser_try (parser, "(", TRUE))
845         {
846           _gtk_css_parser_skip_whitespace (parser);
847           if (_gtk_css_parser_try (parser, "(", TRUE))
848             {
849               GError *error;
850               
851               error = g_error_new_literal (GTK_CSS_PROVIDER_ERROR,
852                                            GTK_CSS_PROVIDER_ERROR_DEPRECATED,
853                                            "Whitespace between 'url' and '(' is not allowed");
854                              
855               _gtk_css_parser_take_error (parser, error);
856             }
857           else
858             {
859               _gtk_css_parser_error (parser, "Expected '(' after 'url'");
860               return NULL;
861             }
862         }
863
864       path = _gtk_css_parser_read_string (parser);
865       if (path == NULL)
866         return NULL;
867
868       if (!_gtk_css_parser_try (parser, ")", TRUE))
869         {
870           _gtk_css_parser_error (parser, "No closing ')' found for 'url'");
871           g_free (path);
872           return NULL;
873         }
874     }
875   else
876     {
877       path = _gtk_css_parser_try_name (parser, TRUE);
878       if (path == NULL)
879         {
880           _gtk_css_parser_error (parser, "Not a valid url");
881           return NULL;
882         }
883     }
884
885   file = g_file_resolve_relative_path (base, path);
886   g_free (path);
887
888   return file;
889 }
890
891 static gboolean 
892 pattern_value_parse (GtkCssParser *parser,
893                      GFile        *base,
894                      GValue       *value)
895 {
896   if (_gtk_css_parser_begins_with (parser, '-'))
897     {
898       g_value_unset (value);
899       g_value_init (value, GTK_TYPE_GRADIENT);
900       return gradient_value_parse (parser, base, value);
901     }
902   else
903     {
904       GError *error = NULL;
905       gchar *path;
906       GdkPixbuf *pixbuf;
907       GFile *file;
908       cairo_surface_t *surface;
909       cairo_pattern_t *pattern;
910       cairo_t *cr;
911       cairo_matrix_t matrix;
912
913       file = gtk_css_parse_url (parser, base);
914       if (file == NULL)
915         return FALSE;
916
917       path = g_file_get_path (file);
918       g_object_unref (file);
919
920       pixbuf = gdk_pixbuf_new_from_file (path, &error);
921       g_free (path);
922       if (pixbuf == NULL)
923         {
924           _gtk_css_parser_take_error (parser, error);
925           return FALSE;
926         }
927
928       surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
929                                             gdk_pixbuf_get_width (pixbuf),
930                                             gdk_pixbuf_get_height (pixbuf));
931       cr = cairo_create (surface);
932       gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
933       cairo_paint (cr);
934       pattern = cairo_pattern_create_for_surface (surface);
935
936       cairo_matrix_init_scale (&matrix,
937                                gdk_pixbuf_get_width (pixbuf),
938                                gdk_pixbuf_get_height (pixbuf));
939       cairo_pattern_set_matrix (pattern, &matrix);
940
941       cairo_surface_destroy (surface);
942       cairo_destroy (cr);
943       g_object_unref (pixbuf);
944
945       g_value_take_boxed (value, pattern);
946     }
947   
948   return TRUE;
949 }
950
951 static gboolean
952 shadow_value_parse (GtkCssParser *parser,
953                     GFile *base,
954                     GValue *value)
955 {
956   gboolean inset;
957   gdouble hoffset, voffset, blur, spread;
958   GtkSymbolicColor *color;
959   GtkShadow *shadow;
960
961   shadow = _gtk_shadow_new ();
962
963   do
964     {
965       inset = _gtk_css_parser_try (parser, "inset", TRUE);
966
967       if (!_gtk_css_parser_try_double (parser, &hoffset) ||
968           !_gtk_css_parser_try_double (parser, &voffset))
969         {
970           _gtk_css_parser_error (parser, "Horizontal and vertical offsets are required");
971           _gtk_shadow_unref (shadow);
972           return FALSE;
973         }
974
975       if (!_gtk_css_parser_try_double (parser, &blur))
976         blur = 0;
977
978       if (!_gtk_css_parser_try_double (parser, &spread))
979         spread = 0;
980
981       /* XXX: the color is optional and UA-defined if it's missing,
982        * but it doesn't really make sense for us...
983        */
984       color = _gtk_css_parser_read_symbolic_color (parser);
985
986       if (color == NULL)
987         {
988           _gtk_shadow_unref (shadow);
989           return FALSE;
990         }
991
992       _gtk_shadow_append (shadow,
993                           hoffset, voffset,
994                           blur, spread,
995                           inset, color);
996
997       gtk_symbolic_color_unref (color);
998
999     }
1000   while (_gtk_css_parser_try (parser, ",", TRUE));
1001
1002   g_value_take_boxed (value, shadow);
1003   return TRUE;
1004 }
1005
1006 static void
1007 shadow_value_print (const GValue *value,
1008                     GString      *string)
1009 {
1010   GtkShadow *shadow;
1011
1012   shadow = g_value_get_boxed (value);
1013
1014   if (shadow == NULL)
1015     g_string_append (string, "none");
1016   else
1017     _gtk_shadow_print (shadow, string);
1018 }
1019
1020 static gboolean 
1021 slice_value_parse (GtkCssParser *parser,
1022                    GFile        *base,
1023                    GValue       *value)
1024 {
1025   gdouble distance_top, distance_bottom;
1026   gdouble distance_left, distance_right;
1027   GtkSliceSideModifier mods[2];
1028   GdkPixbuf *pixbuf;
1029   Gtk9Slice *slice;
1030   GFile *file;
1031   GError *error = NULL;
1032   gint i;
1033   char *path;
1034
1035   /* Parse image url */
1036   file = gtk_css_parse_url (parser, base);
1037   if (!file)
1038       return FALSE;
1039
1040   if (!_gtk_css_parser_try_double (parser, &distance_top) ||
1041       !_gtk_css_parser_try_double (parser, &distance_right) ||
1042       !_gtk_css_parser_try_double (parser, &distance_bottom) ||
1043       !_gtk_css_parser_try_double (parser, &distance_left))
1044     {
1045       _gtk_css_parser_error (parser, "Expected a number");
1046       g_object_unref (file);
1047       return FALSE;
1048     }
1049
1050   for (i = 0; i < 2; i++)
1051     {
1052       if (_gtk_css_parser_try (parser, "stretch", TRUE))
1053         mods[i] = GTK_SLICE_STRETCH;
1054       else if (_gtk_css_parser_try (parser, "repeat", TRUE))
1055         mods[i] = GTK_SLICE_REPEAT;
1056       else if (i == 0)
1057         {
1058           mods[1] = mods[0] = GTK_SLICE_STRETCH;
1059           break;
1060         }
1061       else
1062         mods[i] = mods[0];
1063     }
1064
1065   path = g_file_get_path (file);
1066   g_object_unref (file);
1067   pixbuf = gdk_pixbuf_new_from_file (path, &error);
1068   g_free (path);
1069   if (!pixbuf)
1070     {
1071       _gtk_css_parser_take_error (parser, error);
1072       return FALSE;
1073     }
1074
1075   slice = _gtk_9slice_new (pixbuf,
1076                            distance_top, distance_bottom,
1077                            distance_left, distance_right,
1078                            mods[0], mods[1]);
1079   g_object_unref (pixbuf);
1080
1081   g_value_take_boxed (value, slice);
1082   return TRUE;
1083 }
1084
1085 static gboolean 
1086 enum_value_parse (GtkCssParser *parser,
1087                   GFile        *base,
1088                   GValue       *value)
1089 {
1090   GEnumClass *enum_class;
1091   GEnumValue *enum_value;
1092   char *str;
1093
1094   str = _gtk_css_parser_try_ident (parser, TRUE);
1095   if (str == NULL)
1096     {
1097       _gtk_css_parser_error (parser, "Expected an identifier");
1098       return FALSE;
1099     }
1100
1101   enum_class = g_type_class_ref (G_VALUE_TYPE (value));
1102   enum_value = g_enum_get_value_by_nick (enum_class, str);
1103
1104   if (enum_value)
1105     g_value_set_enum (value, enum_value->value);
1106   else
1107     _gtk_css_parser_error (parser,
1108                            "Unknown value '%s' for enum type '%s'",
1109                            str, g_type_name (G_VALUE_TYPE (value)));
1110   
1111   g_type_class_unref (enum_class);
1112   g_free (str);
1113
1114   return enum_value != NULL;
1115 }
1116
1117 static void
1118 enum_value_print (const GValue *value,
1119                   GString      *string)
1120 {
1121   GEnumClass *enum_class;
1122   GEnumValue *enum_value;
1123
1124   enum_class = g_type_class_ref (G_VALUE_TYPE (value));
1125   enum_value = g_enum_get_value (enum_class, g_value_get_enum (value));
1126
1127   g_string_append (string, enum_value->value_nick);
1128
1129   g_type_class_unref (enum_class);
1130 }
1131
1132 static gboolean 
1133 flags_value_parse (GtkCssParser *parser,
1134                    GFile        *base,
1135                    GValue       *value)
1136 {
1137   GFlagsClass *flags_class;
1138   GFlagsValue *flag_value;
1139   guint flags = 0;
1140   char *str;
1141
1142   flags_class = g_type_class_ref (G_VALUE_TYPE (value));
1143
1144   do {
1145     str = _gtk_css_parser_try_ident (parser, TRUE);
1146     if (str == NULL)
1147       {
1148         _gtk_css_parser_error (parser, "Expected an identifier");
1149         g_type_class_unref (flags_class);
1150         return FALSE;
1151       }
1152
1153       flag_value = g_flags_get_value_by_nick (flags_class, str);
1154       if (!flag_value)
1155         {
1156           _gtk_css_parser_error (parser,
1157                                  "Unknown flag value '%s' for type '%s'",
1158                                  str, g_type_name (G_VALUE_TYPE (value)));
1159           /* XXX Do we want to return FALSE here? We can get
1160            * forward-compatibility for new values this way
1161            */
1162           g_free (str);
1163           g_type_class_unref (flags_class);
1164           return FALSE;
1165         }
1166
1167       g_free (str);
1168     }
1169   while (_gtk_css_parser_try (parser, ",", FALSE));
1170
1171   g_type_class_unref (flags_class);
1172
1173   g_value_set_enum (value, flags);
1174
1175   return TRUE;
1176 }
1177
1178 static void
1179 flags_value_print (const GValue *value,
1180                    GString      *string)
1181 {
1182   GFlagsClass *flags_class;
1183   guint i, flags;
1184
1185   flags_class = g_type_class_ref (G_VALUE_TYPE (value));
1186   flags = g_value_get_flags (value);
1187
1188   for (i = 0; i < flags_class->n_values; i++)
1189     {
1190       GFlagsValue *flags_value = &flags_class->values[i];
1191
1192       if (flags & flags_value->value)
1193         {
1194           if (string->len != 0)
1195             g_string_append (string, ", ");
1196
1197           g_string_append (string, flags_value->value_nick);
1198         }
1199     }
1200
1201   g_type_class_unref (flags_class);
1202 }
1203
1204 static gboolean 
1205 bindings_value_parse (GtkCssParser *parser,
1206                       GFile        *base,
1207                       GValue       *value)
1208 {
1209   GPtrArray *array;
1210   GtkBindingSet *binding_set;
1211   char *name;
1212
1213   array = g_ptr_array_new ();
1214
1215   do {
1216       name = _gtk_css_parser_try_ident (parser, TRUE);
1217       if (name == NULL)
1218         {
1219           _gtk_css_parser_error (parser, "Not a valid binding name");
1220           g_ptr_array_free (array, TRUE);
1221           return FALSE;
1222         }
1223
1224       binding_set = gtk_binding_set_find (name);
1225
1226       if (!binding_set)
1227         {
1228           _gtk_css_parser_error (parser, "No binding set named '%s'", name);
1229           g_free (name);
1230           continue;
1231         }
1232
1233       g_ptr_array_add (array, binding_set);
1234       g_free (name);
1235     }
1236   while (_gtk_css_parser_try (parser, ",", TRUE));
1237
1238   g_value_take_boxed (value, array);
1239
1240   return TRUE;
1241 }
1242
1243 static void
1244 bindings_value_print (const GValue *value,
1245                       GString      *string)
1246 {
1247   GPtrArray *array;
1248   guint i;
1249
1250   array = g_value_get_boxed (value);
1251
1252   for (i = 0; i < array->len; i++)
1253     {
1254       GtkBindingSet *binding_set = g_ptr_array_index (array, i);
1255
1256       if (i > 0)
1257         g_string_append (string, ", ");
1258       g_string_append (string, binding_set->set_name);
1259     }
1260 }
1261
1262 static gboolean 
1263 border_corner_radius_value_parse (GtkCssParser *parser,
1264                                   GFile        *base,
1265                                   GValue       *value)
1266 {
1267   GtkCssBorderCornerRadius corner;
1268
1269   if (!_gtk_css_parser_try_double (parser, &corner.horizontal))
1270     {
1271       _gtk_css_parser_error (parser, "Expected a number");
1272       return FALSE;
1273     }
1274   else if (corner.horizontal < 0)
1275     goto negative;
1276
1277   if (!_gtk_css_parser_try_double (parser, &corner.vertical))
1278     corner.vertical = corner.horizontal;
1279   else if (corner.vertical < 0)
1280     goto negative;
1281
1282   g_value_set_boxed (value, &corner);
1283   return TRUE;
1284
1285 negative:
1286   _gtk_css_parser_error (parser, "Border radius values cannot be negative");
1287   return FALSE;
1288 }
1289
1290 static void
1291 border_corner_radius_value_print (const GValue *value,
1292                                   GString      *string)
1293 {
1294   GtkCssBorderCornerRadius *corner;
1295
1296   corner = g_value_get_boxed (value);
1297
1298   if (corner == NULL)
1299     {
1300       g_string_append (string, "none");
1301       return;
1302     }
1303
1304   string_append_double (string, corner->horizontal);
1305   if (corner->horizontal != corner->vertical)
1306     {
1307       g_string_append_c (string, ' ');
1308       string_append_double (string, corner->vertical);
1309     }
1310 }
1311
1312 static gboolean 
1313 border_radius_value_parse (GtkCssParser *parser,
1314                            GFile        *base,
1315                            GValue       *value)
1316 {
1317   GtkCssBorderRadius border;
1318
1319   if (!_gtk_css_parser_try_double (parser, &border.top_left.horizontal))
1320     {
1321       _gtk_css_parser_error (parser, "Expected a number");
1322       return FALSE;
1323     }
1324   else if (border.top_left.horizontal < 0)
1325     goto negative;
1326
1327   if (_gtk_css_parser_try_double (parser, &border.top_right.horizontal))
1328     {
1329       if (border.top_right.horizontal < 0)
1330         goto negative;
1331       if (_gtk_css_parser_try_double (parser, &border.bottom_right.horizontal))
1332         {
1333           if (border.bottom_right.horizontal < 0)
1334             goto negative;
1335           if (!_gtk_css_parser_try_double (parser, &border.bottom_left.horizontal))
1336             border.bottom_left.horizontal = border.top_right.horizontal;
1337           else if (border.bottom_left.horizontal < 0)
1338             goto negative;
1339         }
1340       else
1341         {
1342           border.bottom_right.horizontal = border.top_left.horizontal;
1343           border.bottom_left.horizontal = border.top_right.horizontal;
1344         }
1345     }
1346   else
1347     {
1348       border.top_right.horizontal = border.top_left.horizontal;
1349       border.bottom_right.horizontal = border.top_left.horizontal;
1350       border.bottom_left.horizontal = border.top_left.horizontal;
1351     }
1352
1353   if (_gtk_css_parser_try (parser, "/", TRUE))
1354     {
1355       if (!_gtk_css_parser_try_double (parser, &border.top_left.vertical))
1356         {
1357           _gtk_css_parser_error (parser, "Expected a number");
1358           return FALSE;
1359         }
1360       else if (border.top_left.vertical < 0)
1361         goto negative;
1362
1363       if (_gtk_css_parser_try_double (parser, &border.top_right.vertical))
1364         {
1365           if (border.top_right.vertical < 0)
1366             goto negative;
1367           if (_gtk_css_parser_try_double (parser, &border.bottom_right.vertical))
1368             {
1369               if (border.bottom_right.vertical < 0)
1370                 goto negative;
1371               if (!_gtk_css_parser_try_double (parser, &border.bottom_left.vertical))
1372                 border.bottom_left.vertical = border.top_right.vertical;
1373               else if (border.bottom_left.vertical < 0)
1374                 goto negative;
1375             }
1376           else
1377             {
1378               border.bottom_right.vertical = border.top_left.vertical;
1379               border.bottom_left.vertical = border.top_right.vertical;
1380             }
1381         }
1382       else
1383         {
1384           border.top_right.vertical = border.top_left.vertical;
1385           border.bottom_right.vertical = border.top_left.vertical;
1386           border.bottom_left.vertical = border.top_left.vertical;
1387         }
1388     }
1389   else
1390     {
1391       border.top_left.vertical = border.top_left.horizontal;
1392       border.top_right.vertical = border.top_right.horizontal;
1393       border.bottom_right.vertical = border.bottom_right.horizontal;
1394       border.bottom_left.vertical = border.bottom_left.horizontal;
1395     }
1396
1397   /* border-radius is an int property for backwards-compat reasons */
1398   g_value_unset (value);
1399   g_value_init (value, GTK_TYPE_CSS_BORDER_RADIUS);
1400   g_value_set_boxed (value, &border);
1401
1402   return TRUE;
1403
1404 negative:
1405   _gtk_css_parser_error (parser, "Border radius values cannot be negative");
1406   return FALSE;
1407 }
1408
1409 static void
1410 border_radius_value_print (const GValue *value,
1411                            GString      *string)
1412 {
1413   GtkCssBorderRadius *border;
1414
1415   border = g_value_get_boxed (value);
1416
1417   if (border == NULL)
1418     {
1419       g_string_append (string, "none");
1420       return;
1421     }
1422
1423   string_append_double (string, border->top_left.horizontal);
1424   if (border->top_left.horizontal != border->top_right.horizontal ||
1425       border->top_left.horizontal != border->bottom_right.horizontal ||
1426       border->top_left.horizontal != border->bottom_left.horizontal)
1427     {
1428       g_string_append_c (string, ' ');
1429       string_append_double (string, border->top_right.horizontal);
1430       if (border->top_left.horizontal != border->bottom_right.horizontal ||
1431           border->top_right.horizontal != border->bottom_left.horizontal)
1432         {
1433           g_string_append_c (string, ' ');
1434           string_append_double (string, border->bottom_right.horizontal);
1435           if (border->top_right.horizontal != border->bottom_left.horizontal)
1436             {
1437               g_string_append_c (string, ' ');
1438               string_append_double (string, border->bottom_left.horizontal);
1439             }
1440         }
1441     }
1442
1443   if (border->top_left.horizontal != border->top_left.vertical ||
1444       border->top_right.horizontal != border->top_right.vertical ||
1445       border->bottom_right.horizontal != border->bottom_right.vertical ||
1446       border->bottom_left.horizontal != border->bottom_left.vertical)
1447     {
1448       g_string_append (string, " / ");
1449       string_append_double (string, border->top_left.vertical);
1450       if (border->top_left.vertical != border->top_right.vertical ||
1451           border->top_left.vertical != border->bottom_right.vertical ||
1452           border->top_left.vertical != border->bottom_left.vertical)
1453         {
1454           g_string_append_c (string, ' ');
1455           string_append_double (string, border->top_right.vertical);
1456           if (border->top_left.vertical != border->bottom_right.vertical ||
1457               border->top_right.vertical != border->bottom_left.vertical)
1458             {
1459               g_string_append_c (string, ' ');
1460               string_append_double (string, border->bottom_right.vertical);
1461               if (border->top_right.vertical != border->bottom_left.vertical)
1462                 {
1463                   g_string_append_c (string, ' ');
1464                   string_append_double (string, border->bottom_left.vertical);
1465                 }
1466             }
1467         }
1468
1469     }
1470 }
1471
1472 /*** PACKING ***/
1473
1474 static GParameter *
1475 unpack_border (const GValue *value,
1476                guint        *n_params,
1477                const char   *top,
1478                const char   *left,
1479                const char   *bottom,
1480                const char   *right)
1481 {
1482   GParameter *parameter = g_new0 (GParameter, 4);
1483   GtkBorder *border = g_value_get_boxed (value);
1484
1485   parameter[0].name = top;
1486   g_value_init (&parameter[0].value, G_TYPE_INT);
1487   g_value_set_int (&parameter[0].value, border->top);
1488   parameter[1].name = left;
1489   g_value_init (&parameter[1].value, G_TYPE_INT);
1490   g_value_set_int (&parameter[1].value, border->left);
1491   parameter[2].name = bottom;
1492   g_value_init (&parameter[2].value, G_TYPE_INT);
1493   g_value_set_int (&parameter[2].value, border->bottom);
1494   parameter[3].name = right;
1495   g_value_init (&parameter[3].value, G_TYPE_INT);
1496   g_value_set_int (&parameter[3].value, border->right);
1497
1498   *n_params = 4;
1499   return parameter;
1500 }
1501
1502 static void
1503 pack_border (GValue             *value,
1504              GtkStyleProperties *props,
1505              GtkStateFlags       state,
1506              const char         *top,
1507              const char         *left,
1508              const char         *bottom,
1509              const char         *right)
1510 {
1511   GtkBorder border;
1512   int t, l, b, r;
1513
1514   gtk_style_properties_get (props,
1515                             state,
1516                             top, &t,
1517                             left, &l,
1518                             bottom, &b,
1519                             right, &r,
1520                             NULL);
1521
1522   border.top = t;
1523   border.left = l;
1524   border.bottom = b;
1525   border.right = r;
1526
1527   g_value_set_boxed (value, &border);
1528 }
1529
1530 static GParameter *
1531 unpack_border_width (const GValue *value,
1532                      guint        *n_params)
1533 {
1534   return unpack_border (value, n_params,
1535                         "border-top-width", "border-left-width",
1536                         "border-bottom-width", "border-right-width");
1537 }
1538
1539 static void
1540 pack_border_width (GValue             *value,
1541                    GtkStyleProperties *props,
1542                    GtkStateFlags       state)
1543 {
1544   pack_border (value, props, state,
1545                "border-top-width", "border-left-width",
1546                "border-bottom-width", "border-right-width");
1547 }
1548
1549 static GParameter *
1550 unpack_padding (const GValue *value,
1551                 guint        *n_params)
1552 {
1553   return unpack_border (value, n_params,
1554                         "padding-top", "padding-left",
1555                         "padding-bottom", "padding-right");
1556 }
1557
1558 static void
1559 pack_padding (GValue             *value,
1560               GtkStyleProperties *props,
1561               GtkStateFlags       state)
1562 {
1563   pack_border (value, props, state,
1564                "padding-top", "padding-left",
1565                "padding-bottom", "padding-right");
1566 }
1567
1568 static GParameter *
1569 unpack_margin (const GValue *value,
1570                guint        *n_params)
1571 {
1572   return unpack_border (value, n_params,
1573                         "margin-top", "margin-left",
1574                         "margin-bottom", "margin-right");
1575 }
1576
1577 static void
1578 pack_margin (GValue             *value,
1579              GtkStyleProperties *props,
1580              GtkStateFlags       state)
1581 {
1582   pack_border (value, props, state,
1583                "margin-top", "margin-left",
1584                "margin-bottom", "margin-right");
1585 }
1586
1587 static GParameter *
1588 unpack_border_radius (const GValue *value,
1589                       guint        *n_params)
1590 {
1591   GParameter *parameter = g_new0 (GParameter, 4);
1592   GtkCssBorderRadius *border;
1593   
1594   if (G_VALUE_HOLDS_BOXED (value))
1595     border = g_value_get_boxed (value);
1596   else
1597     border = NULL;
1598
1599   parameter[0].name = "border-top-left-radius";
1600   g_value_init (&parameter[0].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
1601   parameter[1].name = "border-top-right-radius";
1602   g_value_init (&parameter[1].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
1603   parameter[2].name = "border-bottom-right-radius";
1604   g_value_init (&parameter[2].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
1605   parameter[3].name = "border-bottom-left-radius";
1606   g_value_init (&parameter[3].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
1607   if (border)
1608     {
1609       g_value_set_boxed (&parameter[0].value, &border->top_left);
1610       g_value_set_boxed (&parameter[1].value, &border->top_right);
1611       g_value_set_boxed (&parameter[2].value, &border->bottom_right);
1612       g_value_set_boxed (&parameter[3].value, &border->bottom_left);
1613     }
1614
1615   *n_params = 4;
1616   return parameter;
1617 }
1618
1619 static void
1620 pack_border_radius (GValue             *value,
1621                     GtkStyleProperties *props,
1622                     GtkStateFlags       state)
1623 {
1624   GtkCssBorderCornerRadius *top_left;
1625
1626   /* NB: We are an int property, so we have to resolve to an int here.
1627    * So we just resolve to an int. We pick one and stick to it.
1628    * Lesson learned: Don't query border-radius shorthand, query the 
1629    * real properties instead. */
1630   gtk_style_properties_get (props,
1631                             state,
1632                             "border-top-left-radius", &top_left,
1633                             NULL);
1634
1635   if (top_left)
1636     g_value_set_int (value, top_left->horizontal);
1637
1638   g_free (top_left);
1639 }
1640
1641 static GParameter *
1642 unpack_font_description (const GValue *value,
1643                          guint        *n_params)
1644 {
1645   GParameter *parameter = g_new0 (GParameter, 5);
1646   PangoFontDescription *description;
1647   PangoFontMask mask;
1648   guint n;
1649   
1650   /* For backwards compat, we only unpack values that are indeed set.
1651    * For strict CSS conformance we need to unpack all of them.
1652    * Note that we do set all of them in the parse function, so it
1653    * will not have effects when parsing CSS files. It will though
1654    * for custom style providers.
1655    */
1656
1657   description = g_value_get_boxed (value);
1658   n = 0;
1659
1660   if (description)
1661     mask = pango_font_description_get_set_fields (description);
1662   else
1663     mask = 0;
1664
1665   if (mask & PANGO_FONT_MASK_FAMILY)
1666     {
1667       GPtrArray *strv = g_ptr_array_new ();
1668
1669       g_ptr_array_add (strv, g_strdup (pango_font_description_get_family (description)));
1670       g_ptr_array_add (strv, NULL);
1671       parameter[n].name = "font-family";
1672       g_value_init (&parameter[n].value, G_TYPE_STRV);
1673       g_value_take_boxed (&parameter[n].value,
1674                           g_ptr_array_free (strv, FALSE));
1675       n++;
1676     }
1677
1678   if (mask & PANGO_FONT_MASK_STYLE)
1679     {
1680       parameter[n].name = "font-style";
1681       g_value_init (&parameter[n].value, PANGO_TYPE_STYLE);
1682       g_value_set_enum (&parameter[n].value,
1683                         pango_font_description_get_style (description));
1684       n++;
1685     }
1686
1687   if (mask & PANGO_FONT_MASK_VARIANT)
1688     {
1689       parameter[n].name = "font-variant";
1690       g_value_init (&parameter[n].value, PANGO_TYPE_VARIANT);
1691       g_value_set_enum (&parameter[n].value,
1692                         pango_font_description_get_variant (description));
1693       n++;
1694     }
1695
1696   if (mask & PANGO_FONT_MASK_WEIGHT)
1697     {
1698       parameter[n].name = "font-weight";
1699       g_value_init (&parameter[n].value, PANGO_TYPE_WEIGHT);
1700       g_value_set_enum (&parameter[n].value,
1701                         pango_font_description_get_weight (description));
1702       n++;
1703     }
1704
1705   if (mask & PANGO_FONT_MASK_SIZE)
1706     {
1707       parameter[n].name = "font-size";
1708       g_value_init (&parameter[n].value, G_TYPE_DOUBLE);
1709       g_value_set_double (&parameter[n].value,
1710                           (double) pango_font_description_get_size (description) / PANGO_SCALE);
1711       n++;
1712     }
1713
1714   *n_params = n;
1715
1716   return parameter;
1717 }
1718
1719 static void
1720 pack_font_description (GValue             *value,
1721                        GtkStyleProperties *props,
1722                        GtkStateFlags       state)
1723 {
1724   PangoFontDescription *description;
1725   char **families;
1726   PangoStyle style;
1727   PangoVariant variant;
1728   PangoWeight weight;
1729   double size;
1730
1731   gtk_style_properties_get (props,
1732                             state,
1733                             "font-family", &families,
1734                             "font-style", &style,
1735                             "font-variant", &variant,
1736                             "font-weight", &weight,
1737                             "font-size", &size,
1738                             NULL);
1739
1740   description = pango_font_description_new ();
1741   /* xxx: Can we set all the families here somehow? */
1742   if (families)
1743     pango_font_description_set_family (description, families[0]);
1744   pango_font_description_set_size (description, round (size * PANGO_SCALE));
1745   pango_font_description_set_style (description, style);
1746   pango_font_description_set_variant (description, variant);
1747   pango_font_description_set_weight (description, weight);
1748
1749   g_free (families);
1750
1751   g_value_take_boxed (value, description);
1752 }
1753
1754 /*** API ***/
1755
1756 static void
1757 css_string_funcs_init (void)
1758 {
1759   if (G_LIKELY (parse_funcs != NULL))
1760     return;
1761
1762   parse_funcs = g_hash_table_new (NULL, NULL);
1763   print_funcs = g_hash_table_new (NULL, NULL);
1764
1765   register_conversion_function (GDK_TYPE_RGBA,
1766                                 rgba_value_parse,
1767                                 rgba_value_print);
1768   register_conversion_function (GDK_TYPE_COLOR,
1769                                 color_value_parse,
1770                                 color_value_print);
1771   register_conversion_function (GTK_TYPE_SYMBOLIC_COLOR,
1772                                 symbolic_color_value_parse,
1773                                 symbolic_color_value_print);
1774   register_conversion_function (PANGO_TYPE_FONT_DESCRIPTION,
1775                                 font_description_value_parse,
1776                                 font_description_value_print);
1777   register_conversion_function (G_TYPE_BOOLEAN,
1778                                 boolean_value_parse,
1779                                 boolean_value_print);
1780   register_conversion_function (G_TYPE_INT,
1781                                 int_value_parse,
1782                                 int_value_print);
1783   register_conversion_function (G_TYPE_UINT,
1784                                 uint_value_parse,
1785                                 uint_value_print);
1786   register_conversion_function (G_TYPE_DOUBLE,
1787                                 double_value_parse,
1788                                 double_value_print);
1789   register_conversion_function (G_TYPE_FLOAT,
1790                                 float_value_parse,
1791                                 float_value_print);
1792   register_conversion_function (G_TYPE_STRING,
1793                                 string_value_parse,
1794                                 string_value_print);
1795   register_conversion_function (GTK_TYPE_THEMING_ENGINE,
1796                                 theming_engine_value_parse,
1797                                 theming_engine_value_print);
1798   register_conversion_function (GTK_TYPE_ANIMATION_DESCRIPTION,
1799                                 animation_description_value_parse,
1800                                 animation_description_value_print);
1801   register_conversion_function (GTK_TYPE_BORDER,
1802                                 border_value_parse,
1803                                 border_value_print);
1804   register_conversion_function (GTK_TYPE_GRADIENT,
1805                                 gradient_value_parse,
1806                                 gradient_value_print);
1807   register_conversion_function (CAIRO_GOBJECT_TYPE_PATTERN,
1808                                 pattern_value_parse,
1809                                 NULL);
1810   register_conversion_function (GTK_TYPE_9SLICE,
1811                                 slice_value_parse,
1812                                 NULL);
1813   register_conversion_function (GTK_TYPE_SHADOW,
1814                                 shadow_value_parse,
1815                                 shadow_value_print);
1816   register_conversion_function (G_TYPE_ENUM,
1817                                 enum_value_parse,
1818                                 enum_value_print);
1819   register_conversion_function (G_TYPE_FLAGS,
1820                                 flags_value_parse,
1821                                 flags_value_print);
1822 }
1823
1824 gboolean
1825 _gtk_style_property_parse_value (const GtkStyleProperty *property,
1826                                  GValue                 *value,
1827                                  GtkCssParser           *parser,
1828                                  GFile                  *base)
1829 {
1830   GtkStyleParseFunc func;
1831
1832   g_return_val_if_fail (value != NULL, FALSE);
1833   g_return_val_if_fail (parser != NULL, FALSE);
1834
1835   css_string_funcs_init ();
1836
1837   if (property)
1838     {
1839       if (_gtk_css_parser_try (parser, "none", TRUE))
1840         {
1841           /* Insert the default value, so it has an opportunity
1842            * to override other style providers when merged
1843            */
1844           g_param_value_set_default (property->pspec, value);
1845           return TRUE;
1846         }
1847       else if (property->property_parse_func)
1848         {
1849           GError *error = NULL;
1850           char *value_str;
1851           gboolean success;
1852           
1853           value_str = _gtk_css_parser_read_value (parser);
1854           if (value_str == NULL)
1855             return FALSE;
1856           
1857           success = (*property->property_parse_func) (value_str, value, &error);
1858
1859           g_free (value_str);
1860
1861           return success;
1862         }
1863
1864       func = property->parse_func;
1865     }
1866   else
1867     func = NULL;
1868
1869   if (func == NULL)
1870     func = g_hash_table_lookup (parse_funcs,
1871                                 GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
1872   if (func == NULL)
1873     func = g_hash_table_lookup (parse_funcs,
1874                                 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
1875
1876   if (func == NULL)
1877     {
1878       _gtk_css_parser_error (parser,
1879                              "Cannot convert to type '%s'",
1880                              g_type_name (G_VALUE_TYPE (value)));
1881       return FALSE;
1882     }
1883
1884   return (*func) (parser, base, value);
1885 }
1886
1887 void
1888 _gtk_style_property_print_value (const GtkStyleProperty *property,
1889                                  const GValue           *value,
1890                                  GString                *string)
1891 {
1892   GtkStylePrintFunc func;
1893
1894   css_string_funcs_init ();
1895
1896   if (property)
1897     func = property->print_func;
1898   else
1899     func = NULL;
1900
1901   if (func == NULL)
1902     func = g_hash_table_lookup (print_funcs,
1903                                 GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
1904   if (func == NULL)
1905     func = g_hash_table_lookup (print_funcs,
1906                                 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
1907
1908   if (func == NULL)
1909     {
1910       char *s = g_strdup_value_contents (value);
1911       g_string_append (string, s);
1912       g_free (s);
1913       return;
1914     }
1915   
1916   func (value, string);
1917 }
1918
1919 void
1920 _gtk_style_property_default_value (const GtkStyleProperty *property,
1921                                    GtkStyleProperties     *properties,
1922                                    GValue                 *value)
1923 {
1924   if (property->default_value_func)
1925     property->default_value_func (properties, value);
1926   else if (property->pspec->value_type == GTK_TYPE_THEMING_ENGINE)
1927     g_value_set_object (value, gtk_theming_engine_load (NULL));
1928   else if (property->pspec->value_type == PANGO_TYPE_FONT_DESCRIPTION)
1929     g_value_take_boxed (value, pango_font_description_from_string ("Sans 10"));
1930   else if (property->pspec->value_type == GDK_TYPE_RGBA)
1931     {
1932       GdkRGBA color;
1933       gdk_rgba_parse (&color, "pink");
1934       g_value_set_boxed (value, &color);
1935     }
1936   else if (property->pspec->value_type == GTK_TYPE_BORDER)
1937     {
1938       g_value_take_boxed (value, gtk_border_new ());
1939     }
1940   else
1941     g_param_value_set_default (property->pspec, value);
1942 }
1943
1944 gboolean
1945 _gtk_style_property_is_inherit (const GtkStyleProperty *property)
1946 {
1947   g_return_val_if_fail (property != NULL, FALSE);
1948
1949   return property->flags & GTK_STYLE_PROPERTY_INHERIT ||
1950     gtk_style_param_get_inherit (property->pspec);
1951 }
1952
1953 gboolean
1954 _gtk_style_property_is_shorthand  (const GtkStyleProperty *property)
1955 {
1956   g_return_val_if_fail (property != NULL, FALSE);
1957
1958   return property->pack_func != NULL;
1959 }
1960
1961 GParameter *
1962 _gtk_style_property_unpack (const GtkStyleProperty *property,
1963                             const GValue           *value,
1964                             guint                  *n_params)
1965 {
1966   g_return_val_if_fail (property != NULL, NULL);
1967   g_return_val_if_fail (property->unpack_func != NULL, NULL);
1968   g_return_val_if_fail (value != NULL, NULL);
1969   g_return_val_if_fail (n_params != NULL, NULL);
1970
1971   return property->unpack_func (value, n_params);
1972 }
1973
1974 void
1975 _gtk_style_property_pack (const GtkStyleProperty *property,
1976                           GtkStyleProperties     *props,
1977                           GtkStateFlags           state,
1978                           GValue                 *value)
1979 {
1980   g_return_if_fail (property != NULL);
1981   g_return_if_fail (property->pack_func != NULL);
1982   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
1983   g_return_if_fail (G_IS_VALUE (value));
1984
1985   property->pack_func (value, props, state);
1986 }
1987
1988 static void
1989 gtk_style_property_init (void)
1990 {
1991   GParamSpec *pspec;
1992
1993   if (G_LIKELY (properties))
1994     return;
1995
1996   /* stuff is never freed, so no need for free functions */
1997   properties = g_hash_table_new (g_str_hash, g_str_equal);
1998
1999   /* note that gtk_style_properties_register_property() calls this function,
2000    * so make sure we're sanely inited to avoid infloops */
2001
2002   pspec = g_param_spec_boxed ("color",
2003                               "Foreground color",
2004                               "Foreground color",
2005                               GDK_TYPE_RGBA, 0);
2006   gtk_style_param_set_inherit (pspec, TRUE);
2007   gtk_style_properties_register_property (NULL, pspec);
2008
2009   gtk_style_properties_register_property (NULL,
2010                                           g_param_spec_boxed ("background-color",
2011                                                               "Background color",
2012                                                               "Background color",
2013                                                               GDK_TYPE_RGBA, 0));
2014
2015   pspec = g_param_spec_boxed ("font-family",
2016                               "Font family",
2017                               "Font family",
2018                               G_TYPE_STRV, 0);
2019   gtk_style_param_set_inherit (pspec, TRUE);
2020   _gtk_style_property_register           (pspec,
2021                                           0,
2022                                           NULL,
2023                                           NULL,
2024                                           NULL,
2025                                           font_family_parse,
2026                                           font_family_value_print,
2027                                           NULL);
2028   pspec = g_param_spec_enum ("font-style",
2029                              "Font style",
2030                              "Font style",
2031                              PANGO_TYPE_STYLE,
2032                              PANGO_STYLE_NORMAL, 0);
2033   gtk_style_param_set_inherit (pspec, TRUE);
2034   gtk_style_properties_register_property (NULL, pspec);
2035   pspec = g_param_spec_enum ("font-variant",
2036                              "Font variant",
2037                              "Font variant",
2038                              PANGO_TYPE_VARIANT,
2039                              PANGO_VARIANT_NORMAL, 0);
2040   gtk_style_param_set_inherit (pspec, TRUE);
2041   gtk_style_properties_register_property (NULL, pspec);
2042   /* xxx: need to parse this properly, ie parse the numbers */
2043   pspec = g_param_spec_enum ("font-weight",
2044                              "Font weight",
2045                              "Font weight",
2046                              PANGO_TYPE_WEIGHT,
2047                              PANGO_WEIGHT_NORMAL, 0);
2048   gtk_style_param_set_inherit (pspec, TRUE);
2049   gtk_style_properties_register_property (NULL, pspec);
2050   pspec = g_param_spec_double ("font-size",
2051                                "Font size",
2052                                "Font size",
2053                                0, G_MAXDOUBLE, 0, 0);
2054   gtk_style_param_set_inherit (pspec, TRUE);
2055   gtk_style_properties_register_property (NULL, pspec);
2056   pspec = g_param_spec_boxed ("font",
2057                               "Font Description",
2058                               "Font Description",
2059                               PANGO_TYPE_FONT_DESCRIPTION, 0);
2060   gtk_style_param_set_inherit (pspec, TRUE);
2061   _gtk_style_property_register           (pspec,
2062                                           0,
2063                                           NULL,
2064                                           unpack_font_description,
2065                                           pack_font_description,
2066                                           font_description_value_parse,
2067                                           font_description_value_print,
2068                                           NULL);
2069
2070   pspec = g_param_spec_boxed ("text-shadow",
2071                               "Text shadow",
2072                               "Text shadow",
2073                               GTK_TYPE_SHADOW, 0);
2074   gtk_style_param_set_inherit (pspec, TRUE);
2075   gtk_style_properties_register_property (NULL, pspec);
2076
2077   gtk_style_properties_register_property (NULL,
2078                                           g_param_spec_int ("margin-top",
2079                                                             "margin top",
2080                                                             "Margin at top",
2081                                                             0, G_MAXINT, 0, 0));
2082   gtk_style_properties_register_property (NULL,
2083                                           g_param_spec_int ("margin-left",
2084                                                             "margin left",
2085                                                             "Margin at left",
2086                                                             0, G_MAXINT, 0, 0));
2087   gtk_style_properties_register_property (NULL,
2088                                           g_param_spec_int ("margin-bottom",
2089                                                             "margin bottom",
2090                                                             "Margin at bottom",
2091                                                             0, G_MAXINT, 0, 0));
2092   gtk_style_properties_register_property (NULL,
2093                                           g_param_spec_int ("margin-right",
2094                                                             "margin right",
2095                                                             "Margin at right",
2096                                                             0, G_MAXINT, 0, 0));
2097   _gtk_style_property_register           (g_param_spec_boxed ("margin",
2098                                                               "Margin",
2099                                                               "Margin",
2100                                                               GTK_TYPE_BORDER, 0),
2101                                           0,
2102                                           NULL,
2103                                           unpack_margin,
2104                                           pack_margin,
2105                                           NULL,
2106                                           NULL,
2107                                           NULL);
2108   gtk_style_properties_register_property (NULL,
2109                                           g_param_spec_int ("padding-top",
2110                                                             "padding top",
2111                                                             "Padding at top",
2112                                                             0, G_MAXINT, 0, 0));
2113   gtk_style_properties_register_property (NULL,
2114                                           g_param_spec_int ("padding-left",
2115                                                             "padding left",
2116                                                             "Padding at left",
2117                                                             0, G_MAXINT, 0, 0));
2118   gtk_style_properties_register_property (NULL,
2119                                           g_param_spec_int ("padding-bottom",
2120                                                             "padding bottom",
2121                                                             "Padding at bottom",
2122                                                             0, G_MAXINT, 0, 0));
2123   gtk_style_properties_register_property (NULL,
2124                                           g_param_spec_int ("padding-right",
2125                                                             "padding right",
2126                                                             "Padding at right",
2127                                                             0, G_MAXINT, 0, 0));
2128   _gtk_style_property_register           (g_param_spec_boxed ("padding",
2129                                                               "Padding",
2130                                                               "Padding",
2131                                                               GTK_TYPE_BORDER, 0),
2132                                           0,
2133                                           NULL,
2134                                           unpack_padding,
2135                                           pack_padding,
2136                                           NULL,
2137                                           NULL,
2138                                           NULL);
2139   gtk_style_properties_register_property (NULL,
2140                                           g_param_spec_int ("border-top-width",
2141                                                             "border top width",
2142                                                             "Border width at top",
2143                                                             0, G_MAXINT, 0, 0));
2144   gtk_style_properties_register_property (NULL,
2145                                           g_param_spec_int ("border-left-width",
2146                                                             "border left width",
2147                                                             "Border width at left",
2148                                                             0, G_MAXINT, 0, 0));
2149   gtk_style_properties_register_property (NULL,
2150                                           g_param_spec_int ("border-bottom-width",
2151                                                             "border bottom width",
2152                                                             "Border width at bottom",
2153                                                             0, G_MAXINT, 0, 0));
2154   gtk_style_properties_register_property (NULL,
2155                                           g_param_spec_int ("border-right-width",
2156                                                             "border right width",
2157                                                             "Border width at right",
2158                                                             0, G_MAXINT, 0, 0));
2159   _gtk_style_property_register           (g_param_spec_boxed ("border-width",
2160                                                               "Border width",
2161                                                               "Border width, in pixels",
2162                                                               GTK_TYPE_BORDER, 0),
2163                                           0,
2164                                           NULL,
2165                                           unpack_border_width,
2166                                           pack_border_width,
2167                                           NULL,
2168                                           NULL,
2169                                           NULL);
2170
2171   _gtk_style_property_register           (g_param_spec_boxed ("border-top-left-radius",
2172                                                               "Border top left radius",
2173                                                               "Border radius of top left corner, in pixels",
2174                                                               GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
2175                                           0,
2176                                           NULL,
2177                                           NULL,
2178                                           NULL,
2179                                           border_corner_radius_value_parse,
2180                                           border_corner_radius_value_print,
2181                                           NULL);
2182   _gtk_style_property_register           (g_param_spec_boxed ("border-top-right-radius",
2183                                                               "Border top right radius",
2184                                                               "Border radius of top right corner, in pixels",
2185                                                               GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
2186                                           0,
2187                                           NULL,
2188                                           NULL,
2189                                           NULL,
2190                                           border_corner_radius_value_parse,
2191                                           border_corner_radius_value_print,
2192                                           NULL);
2193   _gtk_style_property_register           (g_param_spec_boxed ("border-bottom-right-radius",
2194                                                               "Border bottom right radius",
2195                                                               "Border radius of bottom right corner, in pixels",
2196                                                               GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
2197                                           0,
2198                                           NULL,
2199                                           NULL,
2200                                           NULL,
2201                                           border_corner_radius_value_parse,
2202                                           border_corner_radius_value_print,
2203                                           NULL);
2204   _gtk_style_property_register           (g_param_spec_boxed ("border-bottom-left-radius",
2205                                                               "Border bottom left radius",
2206                                                               "Border radius of bottom left corner, in pixels",
2207                                                               GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
2208                                           0,
2209                                           NULL,
2210                                           NULL,
2211                                           NULL,
2212                                           border_corner_radius_value_parse,
2213                                           border_corner_radius_value_print,
2214                                           NULL);
2215   _gtk_style_property_register           (g_param_spec_int ("border-radius",
2216                                                             "Border radius",
2217                                                             "Border radius, in pixels",
2218                                                             0, G_MAXINT, 0, 0),
2219                                           0,
2220                                           NULL,
2221                                           unpack_border_radius,
2222                                           pack_border_radius,
2223                                           border_radius_value_parse,
2224                                           border_radius_value_print,
2225                                           NULL);
2226
2227   gtk_style_properties_register_property (NULL,
2228                                           g_param_spec_enum ("border-style",
2229                                                              "Border style",
2230                                                              "Border style",
2231                                                              GTK_TYPE_BORDER_STYLE,
2232                                                              GTK_BORDER_STYLE_NONE, 0));
2233   gtk_style_properties_register_property (NULL,
2234                                           g_param_spec_boxed ("border-color",
2235                                                               "Border color",
2236                                                               "Border color",
2237                                                               GDK_TYPE_RGBA, 0));
2238   gtk_style_properties_register_property (NULL,
2239                                           g_param_spec_boxed ("background-image",
2240                                                               "Background Image",
2241                                                               "Background Image",
2242                                                               CAIRO_GOBJECT_TYPE_PATTERN, 0));
2243   gtk_style_properties_register_property (NULL,
2244                                           g_param_spec_boxed ("border-image",
2245                                                               "Border Image",
2246                                                               "Border Image",
2247                                                               GTK_TYPE_9SLICE, 0));
2248   gtk_style_properties_register_property (NULL,
2249                                           g_param_spec_object ("engine",
2250                                                                "Theming Engine",
2251                                                                "Theming Engine",
2252                                                                GTK_TYPE_THEMING_ENGINE, 0));
2253   gtk_style_properties_register_property (NULL,
2254                                           g_param_spec_boxed ("transition",
2255                                                               "Transition animation description",
2256                                                               "Transition animation description",
2257                                                               GTK_TYPE_ANIMATION_DESCRIPTION, 0));
2258
2259   /* Private property holding the binding sets */
2260   _gtk_style_property_register           (g_param_spec_boxed ("gtk-key-bindings",
2261                                                               "Key bindings",
2262                                                               "Key bindings",
2263                                                               G_TYPE_PTR_ARRAY, 0),
2264                                           0,
2265                                           NULL,
2266                                           NULL,
2267                                           NULL,
2268                                           bindings_value_parse,
2269                                           bindings_value_print,
2270                                           NULL);
2271 }
2272
2273 const GtkStyleProperty *
2274 _gtk_style_property_lookup (const char *name)
2275 {
2276   gtk_style_property_init ();
2277
2278   return g_hash_table_lookup (properties, name);
2279 }
2280
2281 void
2282 _gtk_style_property_register (GParamSpec               *pspec,
2283                               GtkStylePropertyFlags     flags,
2284                               GtkStylePropertyParser    property_parse_func,
2285                               GtkStyleUnpackFunc        unpack_func,
2286                               GtkStylePackFunc          pack_func,
2287                               GtkStyleParseFunc         parse_func,
2288                               GtkStylePrintFunc         print_func,
2289                               GtkStyleDefaultValueFunc  default_value_func)
2290 {
2291   const GtkStyleProperty *existing;
2292   GtkStyleProperty *node;
2293
2294   g_return_if_fail ((pack_func == NULL) == (unpack_func == NULL));
2295
2296   gtk_style_property_init ();
2297
2298   existing = _gtk_style_property_lookup (pspec->name);
2299   if (existing != NULL)
2300     {
2301       g_warning ("Property \"%s\" was already registered with type %s",
2302                  pspec->name, g_type_name (existing->pspec->value_type));
2303       return;
2304     }
2305
2306   node = g_slice_new0 (GtkStyleProperty);
2307   node->flags = flags;
2308   node->pspec = pspec;
2309   node->property_parse_func = property_parse_func;
2310   node->pack_func = pack_func;
2311   node->unpack_func = unpack_func;
2312   node->parse_func = parse_func;
2313   node->print_func = print_func;
2314   node->default_value_func = default_value_func;
2315
2316   g_hash_table_insert (properties, pspec->name, node);
2317 }