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