]> Pileus Git - ~andy/gtk/blob - gtk/gtkstyleproperty.c
css: Convert border style parsing to use enums
[~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 #include "gtkprivatetypebuiltins.h"
36
37 /* the actual parsers we have */
38 #include "gtkanimationdescription.h"
39 #include "gtkbindings.h"
40 #include "gtkborderimageprivate.h"
41 #include "gtkgradient.h"
42 #include "gtkshadowprivate.h"
43 #include "gtkthemingengine.h"
44 #include "gtktypebuiltins.h"
45 #include "gtkwin32themeprivate.h"
46
47 /* this is in case round() is not provided by the compiler, 
48  * such as in the case of C89 compilers, like MSVC
49  */
50 #include "fallback-c89.c"
51
52 static GHashTable *parse_funcs = NULL;
53 static GHashTable *print_funcs = NULL;
54 static GHashTable *properties = NULL;
55
56 static void
57 register_conversion_function (GType             type,
58                               GtkStyleParseFunc parse,
59                               GtkStylePrintFunc print)
60 {
61   if (parse)
62     g_hash_table_insert (parse_funcs, GSIZE_TO_POINTER (type), parse);
63   if (print)
64     g_hash_table_insert (print_funcs, GSIZE_TO_POINTER (type), print);
65 }
66
67 static void
68 string_append_double (GString *string,
69                       double   d)
70 {
71   char buf[G_ASCII_DTOSTR_BUF_SIZE];
72
73   g_ascii_dtostr (buf, sizeof (buf), d);
74   g_string_append (string, buf);
75 }
76
77 static void
78 string_append_string (GString    *str,
79                       const char *string)
80 {
81   gsize len;
82
83   g_string_append_c (str, '"');
84
85   do {
86     len = strcspn (string, "\"\n\r\f");
87     g_string_append (str, string);
88     string += len;
89     switch (*string)
90       {
91       case '\0':
92         break;
93       case '\n':
94         g_string_append (str, "\\A ");
95         break;
96       case '\r':
97         g_string_append (str, "\\D ");
98         break;
99       case '\f':
100         g_string_append (str, "\\C ");
101         break;
102       case '\"':
103         g_string_append (str, "\\\"");
104         break;
105       default:
106         g_assert_not_reached ();
107         break;
108       }
109   } while (*string);
110
111   g_string_append_c (str, '"');
112 }
113
114 /*** IMPLEMENTATIONS ***/
115
116 static gboolean 
117 enum_parse (GtkCssParser *parser,
118             GType         type,
119             int          *res)
120 {
121   char *str;
122
123   if (_gtk_css_parser_try_enum (parser, type, res))
124     return TRUE;
125
126   str = _gtk_css_parser_try_ident (parser, TRUE);
127   if (str == NULL)
128     {
129       _gtk_css_parser_error (parser, "Expected an identifier");
130       return FALSE;
131     }
132
133   _gtk_css_parser_error (parser,
134                          "Unknown value '%s' for enum type '%s'",
135                          str, g_type_name (type));
136   g_free (str);
137
138   return FALSE;
139 }
140
141 static void
142 enum_print (int         value,
143             GType       type,
144             GString    *string)
145 {
146   GEnumClass *enum_class;
147   GEnumValue *enum_value;
148
149   enum_class = g_type_class_ref (type);
150   enum_value = g_enum_get_value (enum_class, value);
151
152   g_string_append (string, enum_value->value_nick);
153
154   g_type_class_unref (enum_class);
155 }
156
157 static gboolean
158 rgba_value_parse (GtkCssParser *parser,
159                   GFile        *base,
160                   GValue       *value)
161 {
162   GtkSymbolicColor *symbolic;
163   GdkRGBA rgba;
164
165   symbolic = _gtk_css_parser_read_symbolic_color (parser);
166   if (symbolic == NULL)
167     return FALSE;
168
169   if (gtk_symbolic_color_resolve (symbolic, NULL, &rgba))
170     {
171       g_value_set_boxed (value, &rgba);
172       gtk_symbolic_color_unref (symbolic);
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 rgba_value_print (const GValue *value,
186                   GString      *string)
187 {
188   const GdkRGBA *rgba = g_value_get_boxed (value);
189
190   if (rgba == NULL)
191     g_string_append (string, "none");
192   else
193     {
194       char *s = gdk_rgba_to_string (rgba);
195       g_string_append (string, s);
196       g_free (s);
197     }
198 }
199
200 static gboolean 
201 color_value_parse (GtkCssParser *parser,
202                    GFile        *base,
203                    GValue       *value)
204 {
205   GtkSymbolicColor *symbolic;
206   GdkRGBA rgba;
207
208   symbolic = _gtk_css_parser_read_symbolic_color (parser);
209   if (symbolic == NULL)
210     return FALSE;
211
212   if (gtk_symbolic_color_resolve (symbolic, NULL, &rgba))
213     {
214       GdkColor color;
215
216       color.red = rgba.red * 65535. + 0.5;
217       color.green = rgba.green * 65535. + 0.5;
218       color.blue = rgba.blue * 65535. + 0.5;
219
220       g_value_set_boxed (value, &color);
221     }
222   else
223     {
224       g_value_unset (value);
225       g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
226       g_value_take_boxed (value, symbolic);
227     }
228
229   return TRUE;
230 }
231
232 static void
233 color_value_print (const GValue *value,
234                    GString      *string)
235 {
236   const GdkColor *color = g_value_get_boxed (value);
237
238   if (color == NULL)
239     g_string_append (string, "none");
240   else
241     {
242       char *s = gdk_color_to_string (color);
243       g_string_append (string, s);
244       g_free (s);
245     }
246 }
247
248 static gboolean
249 symbolic_color_value_parse (GtkCssParser *parser,
250                             GFile        *base,
251                             GValue       *value)
252 {
253   GtkSymbolicColor *symbolic;
254
255   symbolic = _gtk_css_parser_read_symbolic_color (parser);
256   if (symbolic == NULL)
257     return FALSE;
258
259   g_value_take_boxed (value, symbolic);
260   return TRUE;
261 }
262
263 static void
264 symbolic_color_value_print (const GValue *value,
265                             GString      *string)
266 {
267   GtkSymbolicColor *symbolic = g_value_get_boxed (value);
268
269   if (symbolic == NULL)
270     g_string_append (string, "none");
271   else
272     {
273       char *s = gtk_symbolic_color_to_string (symbolic);
274       g_string_append (string, s);
275       g_free (s);
276     }
277 }
278
279 static gboolean
280 font_family_parse (GtkCssParser *parser,
281                    GFile        *base,
282                    GValue       *value)
283 {
284   GPtrArray *names;
285   char *name;
286
287   /* We don't special case generic families. Pango should do
288    * that for us */
289
290   names = g_ptr_array_new ();
291
292   do {
293     name = _gtk_css_parser_try_ident (parser, TRUE);
294     if (name)
295       {
296         GString *string = g_string_new (name);
297         g_free (name);
298         while ((name = _gtk_css_parser_try_ident (parser, TRUE)))
299           {
300             g_string_append_c (string, ' ');
301             g_string_append (string, name);
302             g_free (name);
303           }
304         name = g_string_free (string, FALSE);
305       }
306     else 
307       {
308         name = _gtk_css_parser_read_string (parser);
309         if (name == NULL)
310           {
311             g_ptr_array_free (names, TRUE);
312             return FALSE;
313           }
314       }
315
316     g_ptr_array_add (names, name);
317   } while (_gtk_css_parser_try (parser, ",", TRUE));
318
319   /* NULL-terminate array */
320   g_ptr_array_add (names, NULL);
321   g_value_set_boxed (value, g_ptr_array_free (names, FALSE));
322   return TRUE;
323 }
324
325 static void
326 font_family_value_print (const GValue *value,
327                          GString      *string)
328 {
329   const char **names = g_value_get_boxed (value);
330
331   if (names == NULL || *names == NULL)
332     {
333       g_string_append (string, "none");
334       return;
335     }
336
337   string_append_string (string, *names);
338   names++;
339   while (*names)
340     {
341       g_string_append (string, ", ");
342       string_append_string (string, *names);
343       names++;
344     }
345 }
346
347 static gboolean 
348 font_description_value_parse (GtkCssParser *parser,
349                               GFile        *base,
350                               GValue       *value)
351 {
352   PangoFontDescription *font_desc;
353   guint mask;
354   char *str;
355
356   str = _gtk_css_parser_read_value (parser);
357   if (str == NULL)
358     return FALSE;
359
360   font_desc = pango_font_description_from_string (str);
361   mask = pango_font_description_get_set_fields (font_desc);
362   /* These values are not really correct,
363    * but the fields must be set, so we set them to something */
364   if ((mask & PANGO_FONT_MASK_FAMILY) == 0)
365     pango_font_description_set_family_static (font_desc, "Sans");
366   if ((mask & PANGO_FONT_MASK_SIZE) == 0)
367     pango_font_description_set_size (font_desc, 10 * PANGO_SCALE);
368   g_free (str);
369   g_value_take_boxed (value, font_desc);
370   return TRUE;
371 }
372
373 static void
374 font_description_value_print (const GValue *value,
375                               GString      *string)
376 {
377   const PangoFontDescription *desc = g_value_get_boxed (value);
378
379   if (desc == NULL)
380     g_string_append (string, "none");
381   else
382     {
383       char *s = pango_font_description_to_string (desc);
384       g_string_append (string, s);
385       g_free (s);
386     }
387 }
388
389 static gboolean 
390 boolean_value_parse (GtkCssParser *parser,
391                      GFile        *base,
392                      GValue       *value)
393 {
394   if (_gtk_css_parser_try (parser, "true", TRUE) ||
395       _gtk_css_parser_try (parser, "1", TRUE))
396     {
397       g_value_set_boolean (value, TRUE);
398       return TRUE;
399     }
400   else if (_gtk_css_parser_try (parser, "false", TRUE) ||
401            _gtk_css_parser_try (parser, "0", TRUE))
402     {
403       g_value_set_boolean (value, FALSE);
404       return TRUE;
405     }
406   else
407     {
408       _gtk_css_parser_error (parser, "Expected a boolean value");
409       return FALSE;
410     }
411 }
412
413 static void
414 boolean_value_print (const GValue *value,
415                      GString      *string)
416 {
417   if (g_value_get_boolean (value))
418     g_string_append (string, "true");
419   else
420     g_string_append (string, "false");
421 }
422
423 static gboolean 
424 int_value_parse (GtkCssParser *parser,
425                  GFile        *base,
426                  GValue       *value)
427 {
428   gint i;
429
430   if (_gtk_css_parser_begins_with (parser, '-'))
431     {
432       int res = _gtk_win32_theme_int_parse (parser, base, &i);
433       if (res >= 0)
434         {
435           g_value_set_int (value, i);
436           return res > 0;
437         }
438       /* < 0 => continue */
439     }
440
441   if (!_gtk_css_parser_try_int (parser, &i))
442     {
443       _gtk_css_parser_error (parser, "Expected a valid integer value");
444       return FALSE;
445     }
446
447   g_value_set_int (value, i);
448   return TRUE;
449 }
450
451 static void
452 int_value_print (const GValue *value,
453                  GString      *string)
454 {
455   g_string_append_printf (string, "%d", g_value_get_int (value));
456 }
457
458 static gboolean 
459 uint_value_parse (GtkCssParser *parser,
460                   GFile        *base,
461                   GValue       *value)
462 {
463   guint u;
464
465   if (!_gtk_css_parser_try_uint (parser, &u))
466     {
467       _gtk_css_parser_error (parser, "Expected a valid unsigned value");
468       return FALSE;
469     }
470
471   g_value_set_uint (value, u);
472   return TRUE;
473 }
474
475 static void
476 uint_value_print (const GValue *value,
477                   GString      *string)
478 {
479   g_string_append_printf (string, "%u", g_value_get_uint (value));
480 }
481
482 static gboolean 
483 double_value_parse (GtkCssParser *parser,
484                     GFile        *base,
485                     GValue       *value)
486 {
487   gdouble d;
488
489   if (!_gtk_css_parser_try_double (parser, &d))
490     {
491       _gtk_css_parser_error (parser, "Expected a number");
492       return FALSE;
493     }
494
495   g_value_set_double (value, d);
496   return TRUE;
497 }
498
499 static void
500 double_value_print (const GValue *value,
501                     GString      *string)
502 {
503   string_append_double (string, g_value_get_double (value));
504 }
505
506 static gboolean 
507 float_value_parse (GtkCssParser *parser,
508                    GFile        *base,
509                    GValue       *value)
510 {
511   gdouble d;
512
513   if (!_gtk_css_parser_try_double (parser, &d))
514     {
515       _gtk_css_parser_error (parser, "Expected a number");
516       return FALSE;
517     }
518
519   g_value_set_float (value, d);
520   return TRUE;
521 }
522
523 static void
524 float_value_print (const GValue *value,
525                    GString      *string)
526 {
527   string_append_double (string, g_value_get_float (value));
528 }
529
530 static gboolean 
531 string_value_parse (GtkCssParser *parser,
532                     GFile        *base,
533                     GValue       *value)
534 {
535   char *str = _gtk_css_parser_read_string (parser);
536
537   if (str == NULL)
538     return FALSE;
539
540   g_value_take_string (value, str);
541   return TRUE;
542 }
543
544 static void
545 string_value_print (const GValue *value,
546                     GString      *str)
547 {
548   string_append_string (str, g_value_get_string (value));
549 }
550
551 static gboolean 
552 theming_engine_value_parse (GtkCssParser *parser,
553                             GFile        *base,
554                             GValue       *value)
555 {
556   GtkThemingEngine *engine;
557   char *str;
558
559   str = _gtk_css_parser_try_ident (parser, TRUE);
560   if (str == NULL)
561     {
562       _gtk_css_parser_error (parser, "Expected a valid theme name");
563       return FALSE;
564     }
565
566   engine = gtk_theming_engine_load (str);
567   if (engine == NULL)
568     {
569       _gtk_css_parser_error (parser, "Themeing engine '%s' not found", str);
570       g_free (str);
571       return FALSE;
572     }
573
574   g_value_set_object (value, engine);
575   g_free (str);
576   return TRUE;
577 }
578
579 static void
580 theming_engine_value_print (const GValue *value,
581                             GString      *string)
582 {
583   GtkThemingEngine *engine;
584   char *name;
585
586   engine = g_value_get_object (value);
587   if (engine == NULL)
588     g_string_append (string, "none");
589   else
590     {
591       /* XXX: gtk_theming_engine_get_name()? */
592       g_object_get (engine, "name", &name, NULL);
593       g_string_append (string, name ? name : "none");
594       g_free (name);
595     }
596 }
597
598 static gboolean 
599 animation_description_value_parse (GtkCssParser *parser,
600                                    GFile        *base,
601                                    GValue       *value)
602 {
603   GtkAnimationDescription *desc;
604   char *str;
605
606   str = _gtk_css_parser_read_value (parser);
607   if (str == NULL)
608     return FALSE;
609
610   desc = _gtk_animation_description_from_string (str);
611   g_free (str);
612
613   if (desc == NULL)
614     {
615       _gtk_css_parser_error (parser, "Invalid animation description");
616       return FALSE;
617     }
618   
619   g_value_take_boxed (value, desc);
620   return TRUE;
621 }
622
623 static void
624 animation_description_value_print (const GValue *value,
625                                    GString      *string)
626 {
627   GtkAnimationDescription *desc = g_value_get_boxed (value);
628
629   if (desc == NULL)
630     g_string_append (string, "none");
631   else
632     _gtk_animation_description_print (desc, string);
633 }
634
635 static gboolean 
636 border_value_parse (GtkCssParser *parser,
637                     GFile        *base,
638                     GValue       *value)
639 {
640   GtkBorder border = { 0, };
641   guint i, numbers[4];
642
643   for (i = 0; i < G_N_ELEMENTS (numbers); i++)
644     {
645       if (_gtk_css_parser_begins_with (parser, '-'))
646         {
647           /* These are strictly speaking signed, but we want to be able to use them
648              for unsigned types too, as the actual ranges of values make this safe */
649           int res = _gtk_win32_theme_int_parse (parser, base, (int *)&numbers[i]);
650
651           if (res == 0) /* Parse error, report */
652             return FALSE;
653
654           if (res < 0) /* Nothing known to expand */
655             break;
656         }
657       else
658         {
659           if (!_gtk_css_parser_try_uint (parser, &numbers[i]))
660             break;
661
662           /* XXX: shouldn't allow spaces here? */
663           _gtk_css_parser_try (parser, "px", TRUE);
664         }
665     }
666
667   if (i == 0)
668     {
669       _gtk_css_parser_error (parser, "Expected valid border");
670       return FALSE;
671     }
672
673   border.top = numbers[0];
674   if (i > 1)
675     border.right = numbers[1];
676   else
677     border.right = border.top;
678   if (i > 2)
679     border.bottom = numbers[2];
680   else
681     border.bottom = border.top;
682   if (i > 3)
683     border.left = numbers[3];
684   else
685     border.left = border.right;
686
687   g_value_set_boxed (value, &border);
688   return TRUE;
689 }
690
691 static void
692 border_value_print (const GValue *value, GString *string)
693 {
694   const GtkBorder *border = g_value_get_boxed (value);
695
696   if (border == NULL)
697     g_string_append (string, "none");
698   else if (border->left != border->right)
699     g_string_append_printf (string, "%d %d %d %d", border->top, border->right, border->bottom, border->left);
700   else if (border->top != border->bottom)
701     g_string_append_printf (string, "%d %d %d", border->top, border->right, border->bottom);
702   else if (border->top != border->left)
703     g_string_append_printf (string, "%d %d", border->top, border->right);
704   else
705     g_string_append_printf (string, "%d", border->top);
706 }
707
708 static gboolean 
709 gradient_value_parse (GtkCssParser *parser,
710                       GFile        *base,
711                       GValue       *value)
712 {
713   GtkGradient *gradient;
714   cairo_pattern_type_t type;
715   gdouble coords[6];
716   guint i;
717
718   if (!_gtk_css_parser_try (parser, "-gtk-gradient", TRUE))
719     {
720       _gtk_css_parser_error (parser,
721                              "Expected '-gtk-gradient'");
722       return FALSE;
723     }
724
725   if (!_gtk_css_parser_try (parser, "(", TRUE))
726     {
727       _gtk_css_parser_error (parser,
728                              "Expected '(' after '-gtk-gradient'");
729       return FALSE;
730     }
731
732   /* Parse gradient type */
733   if (_gtk_css_parser_try (parser, "linear", TRUE))
734     type = CAIRO_PATTERN_TYPE_LINEAR;
735   else if (_gtk_css_parser_try (parser, "radial", TRUE))
736     type = CAIRO_PATTERN_TYPE_RADIAL;
737   else
738     {
739       _gtk_css_parser_error (parser,
740                              "Gradient type must be 'radial' or 'linear'");
741       return FALSE;
742     }
743
744   /* Parse start/stop position parameters */
745   for (i = 0; i < 2; i++)
746     {
747       if (! _gtk_css_parser_try (parser, ",", TRUE))
748         {
749           _gtk_css_parser_error (parser,
750                                  "Expected ','");
751           return FALSE;
752         }
753
754       if (_gtk_css_parser_try (parser, "left", TRUE))
755         coords[i * 3] = 0;
756       else if (_gtk_css_parser_try (parser, "right", TRUE))
757         coords[i * 3] = 1;
758       else if (_gtk_css_parser_try (parser, "center", TRUE))
759         coords[i * 3] = 0.5;
760       else if (!_gtk_css_parser_try_double (parser, &coords[i * 3]))
761         {
762           _gtk_css_parser_error (parser,
763                                  "Expected a valid X coordinate");
764           return FALSE;
765         }
766
767       if (_gtk_css_parser_try (parser, "top", TRUE))
768         coords[i * 3 + 1] = 0;
769       else if (_gtk_css_parser_try (parser, "bottom", TRUE))
770         coords[i * 3 + 1] = 1;
771       else if (_gtk_css_parser_try (parser, "center", TRUE))
772         coords[i * 3 + 1] = 0.5;
773       else if (!_gtk_css_parser_try_double (parser, &coords[i * 3 + 1]))
774         {
775           _gtk_css_parser_error (parser,
776                                  "Expected a valid Y coordinate");
777           return FALSE;
778         }
779
780       if (type == CAIRO_PATTERN_TYPE_RADIAL)
781         {
782           /* Parse radius */
783           if (! _gtk_css_parser_try (parser, ",", TRUE))
784             {
785               _gtk_css_parser_error (parser,
786                                      "Expected ','");
787               return FALSE;
788             }
789
790           if (! _gtk_css_parser_try_double (parser, &coords[(i * 3) + 2]))
791             {
792               _gtk_css_parser_error (parser,
793                                      "Expected a numer for the radius");
794               return FALSE;
795             }
796         }
797     }
798
799   if (type == CAIRO_PATTERN_TYPE_LINEAR)
800     gradient = gtk_gradient_new_linear (coords[0], coords[1], coords[3], coords[4]);
801   else
802     gradient = gtk_gradient_new_radial (coords[0], coords[1], coords[2],
803                                         coords[3], coords[4], coords[5]);
804
805   while (_gtk_css_parser_try (parser, ",", TRUE))
806     {
807       GtkSymbolicColor *color;
808       gdouble position;
809
810       if (_gtk_css_parser_try (parser, "from", TRUE))
811         {
812           position = 0;
813
814           if (!_gtk_css_parser_try (parser, "(", TRUE))
815             {
816               gtk_gradient_unref (gradient);
817               _gtk_css_parser_error (parser,
818                                      "Expected '('");
819               return FALSE;
820             }
821
822         }
823       else if (_gtk_css_parser_try (parser, "to", TRUE))
824         {
825           position = 1;
826
827           if (!_gtk_css_parser_try (parser, "(", TRUE))
828             {
829               gtk_gradient_unref (gradient);
830               _gtk_css_parser_error (parser,
831                                      "Expected '('");
832               return FALSE;
833             }
834
835         }
836       else if (_gtk_css_parser_try (parser, "color-stop", TRUE))
837         {
838           if (!_gtk_css_parser_try (parser, "(", TRUE))
839             {
840               gtk_gradient_unref (gradient);
841               _gtk_css_parser_error (parser,
842                                      "Expected '('");
843               return FALSE;
844             }
845
846           if (!_gtk_css_parser_try_double (parser, &position))
847             {
848               gtk_gradient_unref (gradient);
849               _gtk_css_parser_error (parser,
850                                      "Expected a valid number");
851               return FALSE;
852             }
853
854           if (!_gtk_css_parser_try (parser, ",", TRUE))
855             {
856               gtk_gradient_unref (gradient);
857               _gtk_css_parser_error (parser,
858                                      "Expected a comma");
859               return FALSE;
860             }
861         }
862       else
863         {
864           gtk_gradient_unref (gradient);
865           _gtk_css_parser_error (parser,
866                                  "Not a valid color-stop definition");
867           return FALSE;
868         }
869
870       color = _gtk_css_parser_read_symbolic_color (parser);
871       if (color == NULL)
872         {
873           gtk_gradient_unref (gradient);
874           return FALSE;
875         }
876
877       gtk_gradient_add_color_stop (gradient, position, color);
878       gtk_symbolic_color_unref (color);
879
880       if (!_gtk_css_parser_try (parser, ")", TRUE))
881         {
882           gtk_gradient_unref (gradient);
883           _gtk_css_parser_error (parser,
884                                  "Expected ')'");
885           return FALSE;
886         }
887     }
888
889   if (!_gtk_css_parser_try (parser, ")", TRUE))
890     {
891       gtk_gradient_unref (gradient);
892       _gtk_css_parser_error (parser,
893                              "Expected ')'");
894       return FALSE;
895     }
896
897   g_value_take_boxed (value, gradient);
898   return TRUE;
899 }
900
901 static void
902 gradient_value_print (const GValue *value,
903                       GString      *string)
904 {
905   GtkGradient *gradient = g_value_get_boxed (value);
906
907   if (gradient == NULL)
908     g_string_append (string, "none");
909   else
910     {
911       char *s = gtk_gradient_to_string (gradient);
912       g_string_append (string, s);
913       g_free (s);
914     }
915 }
916
917 static GFile *
918 gtk_css_parse_url (GtkCssParser *parser,
919                    GFile        *base)
920 {
921   gchar *path;
922   GFile *file;
923
924   if (_gtk_css_parser_try (parser, "url", FALSE))
925     {
926       if (!_gtk_css_parser_try (parser, "(", TRUE))
927         {
928           _gtk_css_parser_skip_whitespace (parser);
929           if (_gtk_css_parser_try (parser, "(", TRUE))
930             {
931               GError *error;
932               
933               error = g_error_new_literal (GTK_CSS_PROVIDER_ERROR,
934                                            GTK_CSS_PROVIDER_ERROR_DEPRECATED,
935                                            "Whitespace between 'url' and '(' is deprecated");
936                              
937               _gtk_css_parser_take_error (parser, error);
938             }
939           else
940             {
941               _gtk_css_parser_error (parser, "Expected '(' after 'url'");
942               return NULL;
943             }
944         }
945
946       path = _gtk_css_parser_read_string (parser);
947       if (path == NULL)
948         return NULL;
949
950       if (!_gtk_css_parser_try (parser, ")", TRUE))
951         {
952           _gtk_css_parser_error (parser, "No closing ')' found for 'url'");
953           g_free (path);
954           return NULL;
955         }
956     }
957   else
958     {
959       path = _gtk_css_parser_try_name (parser, TRUE);
960       if (path == NULL)
961         {
962           _gtk_css_parser_error (parser, "Not a valid url");
963           return NULL;
964         }
965     }
966
967   file = g_file_resolve_relative_path (base, path);
968   g_free (path);
969
970   return file;
971 }
972
973 static gboolean 
974 pattern_value_parse (GtkCssParser *parser,
975                      GFile        *base,
976                      GValue       *value)
977 {
978   if (_gtk_css_parser_begins_with (parser, '-'))
979     {
980       int res;
981       res = _gtk_win32_theme_part_parse (parser, base, value);
982       if (res >= 0)
983         return res > 0;
984       /* < 0 => continue */
985       g_value_unset (value);
986       g_value_init (value, GTK_TYPE_GRADIENT);
987       return gradient_value_parse (parser, base, value);
988     }
989   else
990     {
991       GError *error = NULL;
992       gchar *path;
993       GdkPixbuf *pixbuf;
994       GFile *file;
995       cairo_surface_t *surface;
996       cairo_pattern_t *pattern;
997       cairo_t *cr;
998       cairo_matrix_t matrix;
999
1000       file = gtk_css_parse_url (parser, base);
1001       if (file == NULL)
1002         return FALSE;
1003
1004       path = g_file_get_path (file);
1005       g_object_unref (file);
1006
1007       pixbuf = gdk_pixbuf_new_from_file (path, &error);
1008       g_free (path);
1009       if (pixbuf == NULL)
1010         {
1011           _gtk_css_parser_take_error (parser, error);
1012           return FALSE;
1013         }
1014
1015       surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
1016                                             gdk_pixbuf_get_width (pixbuf),
1017                                             gdk_pixbuf_get_height (pixbuf));
1018       cr = cairo_create (surface);
1019       gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
1020       cairo_paint (cr);
1021       pattern = cairo_pattern_create_for_surface (surface);
1022
1023       cairo_matrix_init_scale (&matrix,
1024                                gdk_pixbuf_get_width (pixbuf),
1025                                gdk_pixbuf_get_height (pixbuf));
1026       cairo_pattern_set_matrix (pattern, &matrix);
1027
1028       cairo_surface_destroy (surface);
1029       cairo_destroy (cr);
1030       g_object_unref (pixbuf);
1031
1032       g_value_take_boxed (value, pattern);
1033     }
1034   
1035   return TRUE;
1036 }
1037
1038 static cairo_status_t
1039 surface_write (void                *closure,
1040                const unsigned char *data,
1041                unsigned int         length)
1042 {
1043   g_byte_array_append (closure, data, length);
1044
1045   return CAIRO_STATUS_SUCCESS;
1046 }
1047
1048 static void
1049 surface_print (cairo_surface_t *surface,
1050                GString *        string)
1051 {
1052 #if CAIRO_HAS_PNG_FUNCTIONS
1053   GByteArray *array;
1054   char *base64;
1055   
1056   array = g_byte_array_new ();
1057   cairo_surface_write_to_png_stream (surface, surface_write, array);
1058   base64 = g_base64_encode (array->data, array->len);
1059   g_byte_array_free (array, TRUE);
1060
1061   g_string_append (string, "url(\"data:image/png;base64,");
1062   g_string_append (string, base64);
1063   g_string_append (string, "\")");
1064
1065   g_free (base64);
1066 #else
1067   g_string_append (string, "none /* you need cairo png functions enabled to make this work */");
1068 #endif
1069 }
1070
1071 static void
1072 pattern_value_print (const GValue *value,
1073                      GString      *string)
1074 {
1075   cairo_pattern_t *pattern;
1076   cairo_surface_t *surface;
1077
1078   pattern = g_value_get_boxed (value);
1079
1080   if (pattern == NULL)
1081     {
1082       g_string_append (string, "none");
1083       return;
1084     }
1085
1086   switch (cairo_pattern_get_type (pattern))
1087     {
1088     case CAIRO_PATTERN_TYPE_SURFACE:
1089       if (cairo_pattern_get_surface (pattern, &surface) != CAIRO_STATUS_SUCCESS)
1090         {
1091           g_assert_not_reached ();
1092         }
1093       surface_print (surface, string);
1094       break;
1095     case CAIRO_PATTERN_TYPE_SOLID:
1096     case CAIRO_PATTERN_TYPE_LINEAR:
1097     case CAIRO_PATTERN_TYPE_RADIAL:
1098     default:
1099       g_assert_not_reached ();
1100       break;
1101     }
1102 }
1103
1104 static gboolean
1105 shadow_value_parse (GtkCssParser *parser,
1106                     GFile *base,
1107                     GValue *value)
1108 {
1109   gboolean have_inset, have_color, have_lengths;
1110   gdouble hoffset, voffset, blur, spread;
1111   GtkSymbolicColor *color;
1112   GtkShadow *shadow;
1113   guint i;
1114
1115   shadow = _gtk_shadow_new ();
1116
1117   do
1118     {
1119       have_inset = have_lengths = have_color = FALSE;
1120
1121       for (i = 0; i < 3; i++)
1122         {
1123           if (!have_inset && 
1124               _gtk_css_parser_try (parser, "inset", TRUE))
1125             {
1126               have_inset = TRUE;
1127               continue;
1128             }
1129             
1130           if (!have_lengths &&
1131               _gtk_css_parser_try_double (parser, &hoffset))
1132             {
1133               have_lengths = TRUE;
1134
1135               if (!_gtk_css_parser_try_double (parser, &voffset))
1136                 {
1137                   _gtk_css_parser_error (parser, "Horizontal and vertical offsets are required");
1138                   _gtk_shadow_unref (shadow);
1139                   return FALSE;
1140                 }
1141
1142               if (!_gtk_css_parser_try_double (parser, &blur))
1143                 blur = 0;
1144
1145               if (!_gtk_css_parser_try_double (parser, &spread))
1146                 spread = 0;
1147
1148               continue;
1149             }
1150
1151           if (!have_color)
1152             {
1153               have_color = TRUE;
1154
1155               /* XXX: the color is optional and UA-defined if it's missing,
1156                * but it doesn't really make sense for us...
1157                */
1158               color = _gtk_css_parser_read_symbolic_color (parser);
1159
1160               if (color == NULL)
1161                 {
1162                   _gtk_shadow_unref (shadow);
1163                   return FALSE;
1164                 }
1165             }
1166         }
1167
1168       if (!have_color || !have_lengths)
1169         {
1170           _gtk_css_parser_error (parser, "Must specify at least color and offsets");
1171           _gtk_shadow_unref (shadow);
1172           return FALSE;
1173         }
1174
1175       _gtk_shadow_append (shadow,
1176                           hoffset, voffset,
1177                           blur, spread,
1178                           have_inset, color);
1179
1180       gtk_symbolic_color_unref (color);
1181
1182     }
1183   while (_gtk_css_parser_try (parser, ",", TRUE));
1184
1185   g_value_take_boxed (value, shadow);
1186   return TRUE;
1187 }
1188
1189 static void
1190 shadow_value_print (const GValue *value,
1191                     GString      *string)
1192 {
1193   GtkShadow *shadow;
1194
1195   shadow = g_value_get_boxed (value);
1196
1197   if (shadow == NULL)
1198     g_string_append (string, "none");
1199   else
1200     _gtk_shadow_print (shadow, string);
1201 }
1202
1203 static gboolean
1204 background_repeat_value_parse (GtkCssParser *parser,
1205                                GFile *file,
1206                                GValue *value)
1207 {
1208   GtkCssBackgroundRepeat repeat;
1209   int style;
1210
1211   if (!enum_parse (parser, GTK_TYPE_CSS_BACKGROUND_REPEAT_STYLE, &style))
1212     return FALSE;
1213
1214   repeat.repeat = style;
1215
1216   g_value_set_boxed (value, &repeat);
1217
1218   return TRUE;
1219 }
1220
1221 static void
1222 background_repeat_value_print (const GValue *value,
1223                                GString      *string)
1224 {
1225   GtkCssBackgroundRepeat *repeat;
1226
1227   repeat = g_value_get_boxed (value);
1228
1229   enum_print (repeat->repeat, GTK_TYPE_CSS_BACKGROUND_REPEAT_STYLE, string);
1230 }
1231
1232 static gboolean
1233 border_image_repeat_value_parse (GtkCssParser *parser,
1234                                  GFile *file,
1235                                  GValue *value)
1236 {
1237   GtkCssBorderImageRepeat image_repeat;
1238   GtkCssBorderRepeatStyle styles[2];
1239   gint i, v;
1240
1241   for (i = 0; i < 2; i++)
1242     {
1243       if (_gtk_css_parser_try_enum (parser, GTK_TYPE_CSS_BORDER_REPEAT_STYLE, &v))
1244         styles[i] = v;
1245       else if (i == 0)
1246         {
1247           styles[1] = styles[0] = GTK_CSS_REPEAT_STYLE_STRETCH;
1248           break;
1249         }
1250       else
1251         styles[i] = styles[0];
1252     }
1253
1254   image_repeat.hrepeat = styles[0];
1255   image_repeat.vrepeat = styles[1];
1256
1257   g_value_set_boxed (value, &image_repeat);
1258
1259   return TRUE;
1260 }
1261
1262 static void
1263 border_image_repeat_value_print (const GValue *value,
1264                                  GString      *string)
1265 {
1266   GtkCssBorderImageRepeat *image_repeat;
1267
1268   image_repeat = g_value_get_boxed (value);
1269
1270   enum_print (image_repeat->hrepeat, GTK_TYPE_CSS_BORDER_REPEAT_STYLE, string);
1271   if (image_repeat->hrepeat != image_repeat->vrepeat)
1272     {
1273       g_string_append (string, " ");
1274       enum_print (image_repeat->vrepeat, GTK_TYPE_CSS_BORDER_REPEAT_STYLE, string);
1275     }
1276 }
1277
1278 static gboolean
1279 border_image_value_parse (GtkCssParser *parser,
1280                           GFile *base,
1281                           GValue *value)
1282 {
1283   GValue temp = G_VALUE_INIT;
1284   cairo_pattern_t *pattern = NULL;
1285   gconstpointer *boxed = NULL;
1286   GType boxed_type;
1287   GtkBorder slice, *width = NULL, *parsed_slice;
1288   GtkCssBorderImageRepeat repeat, *parsed_repeat;
1289   gboolean retval = FALSE;
1290   GtkBorderImage *image = NULL;
1291
1292   g_value_init (&temp, CAIRO_GOBJECT_TYPE_PATTERN);
1293
1294   if (!pattern_value_parse (parser, base, &temp))
1295     return FALSE;
1296
1297   boxed_type = G_VALUE_TYPE (&temp);
1298   if (boxed_type != CAIRO_GOBJECT_TYPE_PATTERN)
1299     boxed = g_value_dup_boxed (&temp);
1300   else
1301     pattern = g_value_dup_boxed (&temp);
1302
1303   g_value_unset (&temp);
1304   g_value_init (&temp, GTK_TYPE_BORDER);
1305
1306   if (!border_value_parse (parser, base, &temp))
1307     goto out;
1308
1309   parsed_slice = g_value_get_boxed (&temp);
1310   slice = *parsed_slice;
1311
1312   if (_gtk_css_parser_try (parser, "/", TRUE))
1313     {
1314       g_value_unset (&temp);
1315       g_value_init (&temp, GTK_TYPE_BORDER);
1316
1317       if (!border_value_parse (parser, base, &temp))
1318         goto out;
1319
1320       width = g_value_dup_boxed (&temp);
1321     }
1322
1323   g_value_unset (&temp);
1324   g_value_init (&temp, GTK_TYPE_CSS_BORDER_IMAGE_REPEAT);
1325
1326   if (!border_image_repeat_value_parse (parser, base, &temp))
1327     goto out;
1328
1329   parsed_repeat = g_value_get_boxed (&temp);
1330   repeat = *parsed_repeat;
1331
1332   g_value_unset (&temp);
1333
1334   if (boxed != NULL)
1335     image = _gtk_border_image_new_for_boxed (boxed_type, boxed, &slice, width, &repeat);
1336   else if (pattern != NULL)
1337     image = _gtk_border_image_new (pattern, &slice, width, &repeat);
1338
1339   if (image != NULL)
1340     {
1341       retval = TRUE;
1342       g_value_take_boxed (value, image);
1343     }
1344
1345  out:
1346   if (pattern != NULL)
1347     cairo_pattern_destroy (pattern);
1348
1349   if (boxed != NULL)
1350     g_boxed_free (boxed_type, boxed);
1351
1352   if (width != NULL)
1353     gtk_border_free (width);
1354
1355   return retval;
1356 }
1357
1358 static gboolean 
1359 enum_value_parse (GtkCssParser *parser,
1360                   GFile        *base,
1361                   GValue       *value)
1362 {
1363   int v;
1364
1365   if (enum_parse (parser, G_VALUE_TYPE (value), &v))
1366     {
1367       g_value_set_enum (value, v);
1368       return TRUE;
1369     }
1370
1371   return FALSE;
1372 }
1373
1374 static void
1375 enum_value_print (const GValue *value,
1376                   GString      *string)
1377 {
1378   enum_print (g_value_get_enum (value), G_VALUE_TYPE (value), string);
1379 }
1380
1381 static gboolean 
1382 flags_value_parse (GtkCssParser *parser,
1383                    GFile        *base,
1384                    GValue       *value)
1385 {
1386   GFlagsClass *flags_class;
1387   GFlagsValue *flag_value;
1388   guint flags = 0;
1389   char *str;
1390
1391   flags_class = g_type_class_ref (G_VALUE_TYPE (value));
1392
1393   do {
1394     str = _gtk_css_parser_try_ident (parser, TRUE);
1395     if (str == NULL)
1396       {
1397         _gtk_css_parser_error (parser, "Expected an identifier");
1398         g_type_class_unref (flags_class);
1399         return FALSE;
1400       }
1401
1402       flag_value = g_flags_get_value_by_nick (flags_class, str);
1403       if (!flag_value)
1404         {
1405           _gtk_css_parser_error (parser,
1406                                  "Unknown flag value '%s' for type '%s'",
1407                                  str, g_type_name (G_VALUE_TYPE (value)));
1408           /* XXX Do we want to return FALSE here? We can get
1409            * forward-compatibility for new values this way
1410            */
1411           g_free (str);
1412           g_type_class_unref (flags_class);
1413           return FALSE;
1414         }
1415
1416       g_free (str);
1417     }
1418   while (_gtk_css_parser_try (parser, ",", FALSE));
1419
1420   g_type_class_unref (flags_class);
1421
1422   g_value_set_enum (value, flags);
1423
1424   return TRUE;
1425 }
1426
1427 static void
1428 flags_value_print (const GValue *value,
1429                    GString      *string)
1430 {
1431   GFlagsClass *flags_class;
1432   guint i, flags;
1433
1434   flags_class = g_type_class_ref (G_VALUE_TYPE (value));
1435   flags = g_value_get_flags (value);
1436
1437   for (i = 0; i < flags_class->n_values; i++)
1438     {
1439       GFlagsValue *flags_value = &flags_class->values[i];
1440
1441       if (flags & flags_value->value)
1442         {
1443           if (string->len != 0)
1444             g_string_append (string, ", ");
1445
1446           g_string_append (string, flags_value->value_nick);
1447         }
1448     }
1449
1450   g_type_class_unref (flags_class);
1451 }
1452
1453 static gboolean 
1454 bindings_value_parse (GtkCssParser *parser,
1455                       GFile        *base,
1456                       GValue       *value)
1457 {
1458   GPtrArray *array;
1459   GtkBindingSet *binding_set;
1460   char *name;
1461
1462   array = g_ptr_array_new ();
1463
1464   do {
1465       name = _gtk_css_parser_try_ident (parser, TRUE);
1466       if (name == NULL)
1467         {
1468           _gtk_css_parser_error (parser, "Not a valid binding name");
1469           g_ptr_array_free (array, TRUE);
1470           return FALSE;
1471         }
1472
1473       binding_set = gtk_binding_set_find (name);
1474
1475       if (!binding_set)
1476         {
1477           _gtk_css_parser_error (parser, "No binding set named '%s'", name);
1478           g_free (name);
1479           continue;
1480         }
1481
1482       g_ptr_array_add (array, binding_set);
1483       g_free (name);
1484     }
1485   while (_gtk_css_parser_try (parser, ",", TRUE));
1486
1487   g_value_take_boxed (value, array);
1488
1489   return TRUE;
1490 }
1491
1492 static void
1493 bindings_value_print (const GValue *value,
1494                       GString      *string)
1495 {
1496   GPtrArray *array;
1497   guint i;
1498
1499   array = g_value_get_boxed (value);
1500
1501   for (i = 0; i < array->len; i++)
1502     {
1503       GtkBindingSet *binding_set = g_ptr_array_index (array, i);
1504
1505       if (i > 0)
1506         g_string_append (string, ", ");
1507       g_string_append (string, binding_set->set_name);
1508     }
1509 }
1510
1511 static gboolean 
1512 border_corner_radius_value_parse (GtkCssParser *parser,
1513                                   GFile        *base,
1514                                   GValue       *value)
1515 {
1516   GtkCssBorderCornerRadius corner;
1517
1518   if (!_gtk_css_parser_try_double (parser, &corner.horizontal))
1519     {
1520       _gtk_css_parser_error (parser, "Expected a number");
1521       return FALSE;
1522     }
1523   else if (corner.horizontal < 0)
1524     goto negative;
1525
1526   if (!_gtk_css_parser_try_double (parser, &corner.vertical))
1527     corner.vertical = corner.horizontal;
1528   else if (corner.vertical < 0)
1529     goto negative;
1530
1531   g_value_set_boxed (value, &corner);
1532   return TRUE;
1533
1534 negative:
1535   _gtk_css_parser_error (parser, "Border radius values cannot be negative");
1536   return FALSE;
1537 }
1538
1539 static void
1540 border_corner_radius_value_print (const GValue *value,
1541                                   GString      *string)
1542 {
1543   GtkCssBorderCornerRadius *corner;
1544
1545   corner = g_value_get_boxed (value);
1546
1547   if (corner == NULL)
1548     {
1549       g_string_append (string, "none");
1550       return;
1551     }
1552
1553   string_append_double (string, corner->horizontal);
1554   if (corner->horizontal != corner->vertical)
1555     {
1556       g_string_append_c (string, ' ');
1557       string_append_double (string, corner->vertical);
1558     }
1559 }
1560
1561 static gboolean 
1562 border_radius_value_parse (GtkCssParser *parser,
1563                            GFile        *base,
1564                            GValue       *value)
1565 {
1566   GtkCssBorderRadius border;
1567
1568   if (!_gtk_css_parser_try_double (parser, &border.top_left.horizontal))
1569     {
1570       _gtk_css_parser_error (parser, "Expected a number");
1571       return FALSE;
1572     }
1573   else if (border.top_left.horizontal < 0)
1574     goto negative;
1575
1576   if (_gtk_css_parser_try_double (parser, &border.top_right.horizontal))
1577     {
1578       if (border.top_right.horizontal < 0)
1579         goto negative;
1580       if (_gtk_css_parser_try_double (parser, &border.bottom_right.horizontal))
1581         {
1582           if (border.bottom_right.horizontal < 0)
1583             goto negative;
1584           if (!_gtk_css_parser_try_double (parser, &border.bottom_left.horizontal))
1585             border.bottom_left.horizontal = border.top_right.horizontal;
1586           else if (border.bottom_left.horizontal < 0)
1587             goto negative;
1588         }
1589       else
1590         {
1591           border.bottom_right.horizontal = border.top_left.horizontal;
1592           border.bottom_left.horizontal = border.top_right.horizontal;
1593         }
1594     }
1595   else
1596     {
1597       border.top_right.horizontal = border.top_left.horizontal;
1598       border.bottom_right.horizontal = border.top_left.horizontal;
1599       border.bottom_left.horizontal = border.top_left.horizontal;
1600     }
1601
1602   if (_gtk_css_parser_try (parser, "/", TRUE))
1603     {
1604       if (!_gtk_css_parser_try_double (parser, &border.top_left.vertical))
1605         {
1606           _gtk_css_parser_error (parser, "Expected a number");
1607           return FALSE;
1608         }
1609       else if (border.top_left.vertical < 0)
1610         goto negative;
1611
1612       if (_gtk_css_parser_try_double (parser, &border.top_right.vertical))
1613         {
1614           if (border.top_right.vertical < 0)
1615             goto negative;
1616           if (_gtk_css_parser_try_double (parser, &border.bottom_right.vertical))
1617             {
1618               if (border.bottom_right.vertical < 0)
1619                 goto negative;
1620               if (!_gtk_css_parser_try_double (parser, &border.bottom_left.vertical))
1621                 border.bottom_left.vertical = border.top_right.vertical;
1622               else if (border.bottom_left.vertical < 0)
1623                 goto negative;
1624             }
1625           else
1626             {
1627               border.bottom_right.vertical = border.top_left.vertical;
1628               border.bottom_left.vertical = border.top_right.vertical;
1629             }
1630         }
1631       else
1632         {
1633           border.top_right.vertical = border.top_left.vertical;
1634           border.bottom_right.vertical = border.top_left.vertical;
1635           border.bottom_left.vertical = border.top_left.vertical;
1636         }
1637     }
1638   else
1639     {
1640       border.top_left.vertical = border.top_left.horizontal;
1641       border.top_right.vertical = border.top_right.horizontal;
1642       border.bottom_right.vertical = border.bottom_right.horizontal;
1643       border.bottom_left.vertical = border.bottom_left.horizontal;
1644     }
1645
1646   /* border-radius is an int property for backwards-compat reasons */
1647   g_value_unset (value);
1648   g_value_init (value, GTK_TYPE_CSS_BORDER_RADIUS);
1649   g_value_set_boxed (value, &border);
1650
1651   return TRUE;
1652
1653 negative:
1654   _gtk_css_parser_error (parser, "Border radius values cannot be negative");
1655   return FALSE;
1656 }
1657
1658 static void
1659 border_radius_value_print (const GValue *value,
1660                            GString      *string)
1661 {
1662   GtkCssBorderRadius *border;
1663
1664   border = g_value_get_boxed (value);
1665
1666   if (border == NULL)
1667     {
1668       g_string_append (string, "none");
1669       return;
1670     }
1671
1672   string_append_double (string, border->top_left.horizontal);
1673   if (border->top_left.horizontal != border->top_right.horizontal ||
1674       border->top_left.horizontal != border->bottom_right.horizontal ||
1675       border->top_left.horizontal != border->bottom_left.horizontal)
1676     {
1677       g_string_append_c (string, ' ');
1678       string_append_double (string, border->top_right.horizontal);
1679       if (border->top_left.horizontal != border->bottom_right.horizontal ||
1680           border->top_right.horizontal != border->bottom_left.horizontal)
1681         {
1682           g_string_append_c (string, ' ');
1683           string_append_double (string, border->bottom_right.horizontal);
1684           if (border->top_right.horizontal != border->bottom_left.horizontal)
1685             {
1686               g_string_append_c (string, ' ');
1687               string_append_double (string, border->bottom_left.horizontal);
1688             }
1689         }
1690     }
1691
1692   if (border->top_left.horizontal != border->top_left.vertical ||
1693       border->top_right.horizontal != border->top_right.vertical ||
1694       border->bottom_right.horizontal != border->bottom_right.vertical ||
1695       border->bottom_left.horizontal != border->bottom_left.vertical)
1696     {
1697       g_string_append (string, " / ");
1698       string_append_double (string, border->top_left.vertical);
1699       if (border->top_left.vertical != border->top_right.vertical ||
1700           border->top_left.vertical != border->bottom_right.vertical ||
1701           border->top_left.vertical != border->bottom_left.vertical)
1702         {
1703           g_string_append_c (string, ' ');
1704           string_append_double (string, border->top_right.vertical);
1705           if (border->top_left.vertical != border->bottom_right.vertical ||
1706               border->top_right.vertical != border->bottom_left.vertical)
1707             {
1708               g_string_append_c (string, ' ');
1709               string_append_double (string, border->bottom_right.vertical);
1710               if (border->top_right.vertical != border->bottom_left.vertical)
1711                 {
1712                   g_string_append_c (string, ' ');
1713                   string_append_double (string, border->bottom_left.vertical);
1714                 }
1715             }
1716         }
1717
1718     }
1719 }
1720
1721 static gboolean 
1722 transparent_color_value_parse (GtkCssParser *parser,
1723                                GFile        *base,
1724                                GValue       *value)
1725 {
1726   if (_gtk_css_parser_try (parser, "transparent", TRUE))
1727     {
1728       GdkRGBA transparent = { 0, 0, 0, 0 };
1729           
1730       g_value_set_boxed (value, &transparent);
1731
1732       return TRUE;
1733     }
1734
1735   return rgba_value_parse (parser, base, value);
1736 }
1737
1738 static gboolean 
1739 border_color_shorthand_value_parse (GtkCssParser *parser,
1740                                     GFile        *base,
1741                                     GValue       *value)
1742 {
1743   GtkSymbolicColor *symbolic;
1744   GPtrArray *array;
1745
1746   array = g_ptr_array_new_with_free_func ((GDestroyNotify) gtk_symbolic_color_unref);
1747
1748   do
1749     {
1750       if (_gtk_css_parser_try (parser, "transparent", TRUE))
1751         {
1752           GdkRGBA transparent = { 0, 0, 0, 0 };
1753           
1754           symbolic = gtk_symbolic_color_new_literal (&transparent);
1755         }
1756       else
1757         {
1758           symbolic = _gtk_css_parser_read_symbolic_color (parser);
1759       
1760           if (symbolic == NULL)
1761             return FALSE;
1762         }
1763       
1764       g_ptr_array_add (array, symbolic);
1765     }
1766   while (array->len < 4 && 
1767          !_gtk_css_parser_is_eof (parser) &&
1768          !_gtk_css_parser_begins_with (parser, ';') &&
1769          !_gtk_css_parser_begins_with (parser, '}'));
1770
1771   switch (array->len)
1772     {
1773       default:
1774         g_assert_not_reached ();
1775         break;
1776       case 1:
1777         g_ptr_array_add (array, gtk_symbolic_color_ref (g_ptr_array_index (array, 0)));
1778         /* fall through */
1779       case 2:
1780         g_ptr_array_add (array, gtk_symbolic_color_ref (g_ptr_array_index (array, 0)));
1781         /* fall through */
1782       case 3:
1783         g_ptr_array_add (array, gtk_symbolic_color_ref (g_ptr_array_index (array, 1)));
1784         /* fall through */
1785       case 4:
1786         break;
1787     }
1788
1789   g_value_unset (value);
1790   g_value_init (value, G_TYPE_PTR_ARRAY);
1791   g_value_take_boxed (value, array);
1792
1793   return TRUE;
1794 }
1795
1796 /*** PACKING ***/
1797
1798 static GParameter *
1799 unpack_border (const GValue *value,
1800                guint        *n_params,
1801                const char   *top,
1802                const char   *left,
1803                const char   *bottom,
1804                const char   *right)
1805 {
1806   GParameter *parameter = g_new0 (GParameter, 4);
1807   GtkBorder *border = g_value_get_boxed (value);
1808
1809   parameter[0].name = top;
1810   g_value_init (&parameter[0].value, G_TYPE_INT);
1811   g_value_set_int (&parameter[0].value, border->top);
1812   parameter[1].name = left;
1813   g_value_init (&parameter[1].value, G_TYPE_INT);
1814   g_value_set_int (&parameter[1].value, border->left);
1815   parameter[2].name = bottom;
1816   g_value_init (&parameter[2].value, G_TYPE_INT);
1817   g_value_set_int (&parameter[2].value, border->bottom);
1818   parameter[3].name = right;
1819   g_value_init (&parameter[3].value, G_TYPE_INT);
1820   g_value_set_int (&parameter[3].value, border->right);
1821
1822   *n_params = 4;
1823   return parameter;
1824 }
1825
1826 static void
1827 pack_border (GValue             *value,
1828              GtkStyleProperties *props,
1829              GtkStateFlags       state,
1830              const char         *top,
1831              const char         *left,
1832              const char         *bottom,
1833              const char         *right)
1834 {
1835   GtkBorder border;
1836   int t, l, b, r;
1837
1838   gtk_style_properties_get (props,
1839                             state,
1840                             top, &t,
1841                             left, &l,
1842                             bottom, &b,
1843                             right, &r,
1844                             NULL);
1845
1846   border.top = t;
1847   border.left = l;
1848   border.bottom = b;
1849   border.right = r;
1850
1851   g_value_set_boxed (value, &border);
1852 }
1853
1854 static GParameter *
1855 unpack_border_width (const GValue *value,
1856                      guint        *n_params)
1857 {
1858   return unpack_border (value, n_params,
1859                         "border-top-width", "border-left-width",
1860                         "border-bottom-width", "border-right-width");
1861 }
1862
1863 static void
1864 pack_border_width (GValue             *value,
1865                    GtkStyleProperties *props,
1866                    GtkStateFlags       state,
1867                    GtkStylePropertyContext *context)
1868 {
1869   pack_border (value, props, state,
1870                "border-top-width", "border-left-width",
1871                "border-bottom-width", "border-right-width");
1872 }
1873
1874 static GParameter *
1875 unpack_padding (const GValue *value,
1876                 guint        *n_params)
1877 {
1878   return unpack_border (value, n_params,
1879                         "padding-top", "padding-left",
1880                         "padding-bottom", "padding-right");
1881 }
1882
1883 static void
1884 pack_padding (GValue             *value,
1885               GtkStyleProperties *props,
1886               GtkStateFlags       state,
1887               GtkStylePropertyContext *context)
1888 {
1889   pack_border (value, props, state,
1890                "padding-top", "padding-left",
1891                "padding-bottom", "padding-right");
1892 }
1893
1894 static GParameter *
1895 unpack_margin (const GValue *value,
1896                guint        *n_params)
1897 {
1898   return unpack_border (value, n_params,
1899                         "margin-top", "margin-left",
1900                         "margin-bottom", "margin-right");
1901 }
1902
1903 static void
1904 pack_margin (GValue             *value,
1905              GtkStyleProperties *props,
1906              GtkStateFlags       state,
1907              GtkStylePropertyContext *context)
1908 {
1909   pack_border (value, props, state,
1910                "margin-top", "margin-left",
1911                "margin-bottom", "margin-right");
1912 }
1913
1914 static GParameter *
1915 unpack_border_radius (const GValue *value,
1916                       guint        *n_params)
1917 {
1918   GParameter *parameter = g_new0 (GParameter, 4);
1919   GtkCssBorderRadius *border;
1920   
1921   if (G_VALUE_HOLDS_BOXED (value))
1922     border = g_value_get_boxed (value);
1923   else
1924     border = NULL;
1925
1926   parameter[0].name = "border-top-left-radius";
1927   g_value_init (&parameter[0].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
1928   parameter[1].name = "border-top-right-radius";
1929   g_value_init (&parameter[1].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
1930   parameter[2].name = "border-bottom-right-radius";
1931   g_value_init (&parameter[2].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
1932   parameter[3].name = "border-bottom-left-radius";
1933   g_value_init (&parameter[3].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
1934   if (border)
1935     {
1936       g_value_set_boxed (&parameter[0].value, &border->top_left);
1937       g_value_set_boxed (&parameter[1].value, &border->top_right);
1938       g_value_set_boxed (&parameter[2].value, &border->bottom_right);
1939       g_value_set_boxed (&parameter[3].value, &border->bottom_left);
1940     }
1941
1942   *n_params = 4;
1943   return parameter;
1944 }
1945
1946 static void
1947 pack_border_radius (GValue             *value,
1948                     GtkStyleProperties *props,
1949                     GtkStateFlags       state,
1950                     GtkStylePropertyContext *context)
1951 {
1952   GtkCssBorderCornerRadius *top_left;
1953
1954   /* NB: We are an int property, so we have to resolve to an int here.
1955    * So we just resolve to an int. We pick one and stick to it.
1956    * Lesson learned: Don't query border-radius shorthand, query the 
1957    * real properties instead. */
1958   gtk_style_properties_get (props,
1959                             state,
1960                             "border-top-left-radius", &top_left,
1961                             NULL);
1962
1963   if (top_left)
1964     g_value_set_int (value, top_left->horizontal);
1965
1966   g_free (top_left);
1967 }
1968
1969 static GParameter *
1970 unpack_font_description (const GValue *value,
1971                          guint        *n_params)
1972 {
1973   GParameter *parameter = g_new0 (GParameter, 5);
1974   PangoFontDescription *description;
1975   PangoFontMask mask;
1976   guint n;
1977   
1978   /* For backwards compat, we only unpack values that are indeed set.
1979    * For strict CSS conformance we need to unpack all of them.
1980    * Note that we do set all of them in the parse function, so it
1981    * will not have effects when parsing CSS files. It will though
1982    * for custom style providers.
1983    */
1984
1985   description = g_value_get_boxed (value);
1986   n = 0;
1987
1988   if (description)
1989     mask = pango_font_description_get_set_fields (description);
1990   else
1991     mask = 0;
1992
1993   if (mask & PANGO_FONT_MASK_FAMILY)
1994     {
1995       GPtrArray *strv = g_ptr_array_new ();
1996
1997       g_ptr_array_add (strv, g_strdup (pango_font_description_get_family (description)));
1998       g_ptr_array_add (strv, NULL);
1999       parameter[n].name = "font-family";
2000       g_value_init (&parameter[n].value, G_TYPE_STRV);
2001       g_value_take_boxed (&parameter[n].value,
2002                           g_ptr_array_free (strv, FALSE));
2003       n++;
2004     }
2005
2006   if (mask & PANGO_FONT_MASK_STYLE)
2007     {
2008       parameter[n].name = "font-style";
2009       g_value_init (&parameter[n].value, PANGO_TYPE_STYLE);
2010       g_value_set_enum (&parameter[n].value,
2011                         pango_font_description_get_style (description));
2012       n++;
2013     }
2014
2015   if (mask & PANGO_FONT_MASK_VARIANT)
2016     {
2017       parameter[n].name = "font-variant";
2018       g_value_init (&parameter[n].value, PANGO_TYPE_VARIANT);
2019       g_value_set_enum (&parameter[n].value,
2020                         pango_font_description_get_variant (description));
2021       n++;
2022     }
2023
2024   if (mask & PANGO_FONT_MASK_WEIGHT)
2025     {
2026       parameter[n].name = "font-weight";
2027       g_value_init (&parameter[n].value, PANGO_TYPE_WEIGHT);
2028       g_value_set_enum (&parameter[n].value,
2029                         pango_font_description_get_weight (description));
2030       n++;
2031     }
2032
2033   if (mask & PANGO_FONT_MASK_SIZE)
2034     {
2035       parameter[n].name = "font-size";
2036       g_value_init (&parameter[n].value, G_TYPE_DOUBLE);
2037       g_value_set_double (&parameter[n].value,
2038                           (double) pango_font_description_get_size (description) / PANGO_SCALE);
2039       n++;
2040     }
2041
2042   *n_params = n;
2043
2044   return parameter;
2045 }
2046
2047 static void
2048 pack_font_description (GValue             *value,
2049                        GtkStyleProperties *props,
2050                        GtkStateFlags       state,
2051                        GtkStylePropertyContext *context)
2052 {
2053   PangoFontDescription *description;
2054   char **families;
2055   PangoStyle style;
2056   PangoVariant variant;
2057   PangoWeight weight;
2058   double size;
2059
2060   gtk_style_properties_get (props,
2061                             state,
2062                             "font-family", &families,
2063                             "font-style", &style,
2064                             "font-variant", &variant,
2065                             "font-weight", &weight,
2066                             "font-size", &size,
2067                             NULL);
2068
2069   description = pango_font_description_new ();
2070   /* xxx: Can we set all the families here somehow? */
2071   if (families)
2072     pango_font_description_set_family (description, families[0]);
2073   pango_font_description_set_size (description, round (size * PANGO_SCALE));
2074   pango_font_description_set_style (description, style);
2075   pango_font_description_set_variant (description, variant);
2076   pango_font_description_set_weight (description, weight);
2077
2078   g_strfreev (families);
2079
2080   g_value_take_boxed (value, description);
2081 }
2082
2083 static GParameter *
2084 unpack_border_color (const GValue *value,
2085                      guint        *n_params)
2086 {
2087   GParameter *parameter = g_new0 (GParameter, 4);
2088   GType type;
2089   
2090   type = G_VALUE_TYPE (value);
2091   if (type == G_TYPE_PTR_ARRAY)
2092     type = GTK_TYPE_SYMBOLIC_COLOR;
2093
2094   parameter[0].name = "border-top-color";
2095   g_value_init (&parameter[0].value, type);
2096   parameter[1].name = "border-right-color";
2097   g_value_init (&parameter[1].value, type);
2098   parameter[2].name = "border-bottom-color";
2099   g_value_init (&parameter[2].value, type);
2100   parameter[3].name = "border-left-color";
2101   g_value_init (&parameter[3].value, type);
2102
2103   if (G_VALUE_TYPE (value) == G_TYPE_PTR_ARRAY)
2104     {
2105       GPtrArray *array = g_value_get_boxed (value);
2106       guint i;
2107
2108       for (i = 0; i < 4; i++)
2109         g_value_set_boxed (&parameter[i].value, g_ptr_array_index (array, i));
2110     }
2111   else
2112     {
2113       /* can be RGBA or symbolic color */
2114       gpointer p = g_value_get_boxed (value);
2115
2116       g_value_set_boxed (&parameter[0].value, p);
2117       g_value_set_boxed (&parameter[1].value, p);
2118       g_value_set_boxed (&parameter[2].value, p);
2119       g_value_set_boxed (&parameter[3].value, p);
2120     }
2121
2122   *n_params = 4;
2123   return parameter;
2124 }
2125
2126 static void
2127 pack_border_color (GValue             *value,
2128                    GtkStyleProperties *props,
2129                    GtkStateFlags       state,
2130                    GtkStylePropertyContext *context)
2131 {
2132   /* NB: We are a color property, so we have to resolve to a color here.
2133    * So we just resolve to a color. We pick one and stick to it.
2134    * Lesson learned: Don't query border-color shorthand, query the 
2135    * real properties instead. */
2136   g_value_unset (value);
2137   gtk_style_properties_get_property (props, "border-top-color", state, value);
2138 }
2139
2140 /*** UNSET FUNCS ***/
2141
2142 static void
2143 unset_font_description (GtkStyleProperties *props,
2144                         GtkStateFlags       state)
2145 {
2146   gtk_style_properties_unset_property (props, "font-family", state);
2147   gtk_style_properties_unset_property (props, "font-style", state);
2148   gtk_style_properties_unset_property (props, "font-variant", state);
2149   gtk_style_properties_unset_property (props, "font-weight", state);
2150   gtk_style_properties_unset_property (props, "font-size", state);
2151 }
2152
2153 static void
2154 unset_margin (GtkStyleProperties *props,
2155               GtkStateFlags       state)
2156 {
2157   gtk_style_properties_unset_property (props, "margin-top", state);
2158   gtk_style_properties_unset_property (props, "margin-right", state);
2159   gtk_style_properties_unset_property (props, "margin-bottom", state);
2160   gtk_style_properties_unset_property (props, "margin-left", state);
2161 }
2162
2163 static void
2164 unset_padding (GtkStyleProperties *props,
2165                GtkStateFlags       state)
2166 {
2167   gtk_style_properties_unset_property (props, "padding-top", state);
2168   gtk_style_properties_unset_property (props, "padding-right", state);
2169   gtk_style_properties_unset_property (props, "padding-bottom", state);
2170   gtk_style_properties_unset_property (props, "padding-left", state);
2171 }
2172
2173 static void
2174 unset_border_width (GtkStyleProperties *props,
2175                     GtkStateFlags       state)
2176 {
2177   gtk_style_properties_unset_property (props, "border-top-width", state);
2178   gtk_style_properties_unset_property (props, "border-right-width", state);
2179   gtk_style_properties_unset_property (props, "border-bottom-width", state);
2180   gtk_style_properties_unset_property (props, "border-left-width", state);
2181 }
2182
2183 static void
2184 unset_border_radius (GtkStyleProperties *props,
2185                      GtkStateFlags       state)
2186 {
2187   gtk_style_properties_unset_property (props, "border-top-right-radius", state);
2188   gtk_style_properties_unset_property (props, "border-bottom-right-radius", state);
2189   gtk_style_properties_unset_property (props, "border-bottom-left-radius", state);
2190   gtk_style_properties_unset_property (props, "border-top-left-radius", state);
2191 }
2192
2193 static void
2194 unset_border_color (GtkStyleProperties *props,
2195                     GtkStateFlags       state)
2196 {
2197   gtk_style_properties_unset_property (props, "border-top-color", state);
2198   gtk_style_properties_unset_property (props, "border-right-color", state);
2199   gtk_style_properties_unset_property (props, "border-bottom-color", state);
2200   gtk_style_properties_unset_property (props, "border-left-color", state);
2201 }
2202
2203 static void
2204 unset_border_image (GtkStyleProperties *props,
2205                     GtkStateFlags       state)
2206 {
2207   gtk_style_properties_unset_property (props, "border-image-source", state);
2208   gtk_style_properties_unset_property (props, "border-image-slice", state);
2209   gtk_style_properties_unset_property (props, "border-image-repeat", state);
2210   gtk_style_properties_unset_property (props, "border-image-width", state);
2211 }
2212
2213 /*** default values ***/
2214
2215 static void
2216 border_image_width_default_value (GtkStyleProperties *props,
2217                                   GtkStateFlags       state,
2218                                   GValue             *value)
2219 {
2220 }
2221
2222 static void
2223 background_color_default_value (GtkStyleProperties *props,
2224                                 GtkStateFlags       state,
2225                                 GValue             *value)
2226 {
2227   GdkRGBA transparent_black = { 0, 0, 0, 0 };
2228
2229   g_value_set_boxed (value, &transparent_black);
2230 }
2231
2232 static void
2233 border_color_default_value (GtkStyleProperties *props,
2234                             GtkStateFlags       state,
2235                             GValue             *value)
2236 {
2237   g_value_unset (value);
2238   gtk_style_properties_get_property (props, "color", state, value);
2239 }
2240
2241 /*** API ***/
2242
2243 static void
2244 css_string_funcs_init (void)
2245 {
2246   if (G_LIKELY (parse_funcs != NULL))
2247     return;
2248
2249   parse_funcs = g_hash_table_new (NULL, NULL);
2250   print_funcs = g_hash_table_new (NULL, NULL);
2251
2252   register_conversion_function (GDK_TYPE_RGBA,
2253                                 rgba_value_parse,
2254                                 rgba_value_print);
2255   register_conversion_function (GDK_TYPE_COLOR,
2256                                 color_value_parse,
2257                                 color_value_print);
2258   register_conversion_function (GTK_TYPE_SYMBOLIC_COLOR,
2259                                 symbolic_color_value_parse,
2260                                 symbolic_color_value_print);
2261   register_conversion_function (PANGO_TYPE_FONT_DESCRIPTION,
2262                                 font_description_value_parse,
2263                                 font_description_value_print);
2264   register_conversion_function (G_TYPE_BOOLEAN,
2265                                 boolean_value_parse,
2266                                 boolean_value_print);
2267   register_conversion_function (G_TYPE_INT,
2268                                 int_value_parse,
2269                                 int_value_print);
2270   register_conversion_function (G_TYPE_UINT,
2271                                 uint_value_parse,
2272                                 uint_value_print);
2273   register_conversion_function (G_TYPE_DOUBLE,
2274                                 double_value_parse,
2275                                 double_value_print);
2276   register_conversion_function (G_TYPE_FLOAT,
2277                                 float_value_parse,
2278                                 float_value_print);
2279   register_conversion_function (G_TYPE_STRING,
2280                                 string_value_parse,
2281                                 string_value_print);
2282   register_conversion_function (GTK_TYPE_THEMING_ENGINE,
2283                                 theming_engine_value_parse,
2284                                 theming_engine_value_print);
2285   register_conversion_function (GTK_TYPE_ANIMATION_DESCRIPTION,
2286                                 animation_description_value_parse,
2287                                 animation_description_value_print);
2288   register_conversion_function (GTK_TYPE_BORDER,
2289                                 border_value_parse,
2290                                 border_value_print);
2291   register_conversion_function (GTK_TYPE_GRADIENT,
2292                                 gradient_value_parse,
2293                                 gradient_value_print);
2294   register_conversion_function (CAIRO_GOBJECT_TYPE_PATTERN,
2295                                 pattern_value_parse,
2296                                 pattern_value_print);
2297   register_conversion_function (GTK_TYPE_BORDER_IMAGE,
2298                                 border_image_value_parse,
2299                                 NULL);
2300   register_conversion_function (GTK_TYPE_CSS_BORDER_IMAGE_REPEAT,
2301                                 border_image_repeat_value_parse,
2302                                 border_image_repeat_value_print);
2303   register_conversion_function (GTK_TYPE_SHADOW,
2304                                 shadow_value_parse,
2305                                 shadow_value_print);
2306   register_conversion_function (G_TYPE_ENUM,
2307                                 enum_value_parse,
2308                                 enum_value_print);
2309   register_conversion_function (G_TYPE_FLAGS,
2310                                 flags_value_parse,
2311                                 flags_value_print);
2312   register_conversion_function (GTK_TYPE_CSS_BACKGROUND_REPEAT,
2313                                 background_repeat_value_parse,
2314                                 background_repeat_value_print);
2315 }
2316
2317 gboolean
2318 _gtk_style_property_parse_value (const GtkStyleProperty *property,
2319                                  GValue                 *value,
2320                                  GtkCssParser           *parser,
2321                                  GFile                  *base)
2322 {
2323   GtkStyleParseFunc func;
2324
2325   g_return_val_if_fail (value != NULL, FALSE);
2326   g_return_val_if_fail (parser != NULL, FALSE);
2327
2328   css_string_funcs_init ();
2329
2330   if (property)
2331     {
2332       if (_gtk_css_parser_try (parser, "none", TRUE))
2333         {
2334           /* Insert the default value, so it has an opportunity
2335            * to override other style providers when merged
2336            */
2337           g_param_value_set_default (property->pspec, value);
2338           return TRUE;
2339         }
2340       else if (property->property_parse_func)
2341         {
2342           GError *error = NULL;
2343           char *value_str;
2344           gboolean success;
2345           
2346           value_str = _gtk_css_parser_read_value (parser);
2347           if (value_str == NULL)
2348             return FALSE;
2349           
2350           success = (*property->property_parse_func) (value_str, value, &error);
2351
2352           g_free (value_str);
2353
2354           return success;
2355         }
2356
2357       func = property->parse_func;
2358     }
2359   else
2360     func = NULL;
2361
2362   if (func == NULL)
2363     func = g_hash_table_lookup (parse_funcs,
2364                                 GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
2365   if (func == NULL)
2366     func = g_hash_table_lookup (parse_funcs,
2367                                 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
2368
2369   if (func == NULL)
2370     {
2371       _gtk_css_parser_error (parser,
2372                              "Cannot convert to type '%s'",
2373                              g_type_name (G_VALUE_TYPE (value)));
2374       return FALSE;
2375     }
2376
2377   return (*func) (parser, base, value);
2378 }
2379
2380 void
2381 _gtk_style_property_print_value (const GtkStyleProperty *property,
2382                                  const GValue           *value,
2383                                  GString                *string)
2384 {
2385   GtkStylePrintFunc func;
2386
2387   css_string_funcs_init ();
2388
2389   if (property)
2390     func = property->print_func;
2391   else
2392     func = NULL;
2393
2394   if (func == NULL)
2395     func = g_hash_table_lookup (print_funcs,
2396                                 GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
2397   if (func == NULL)
2398     func = g_hash_table_lookup (print_funcs,
2399                                 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
2400
2401   if (func == NULL)
2402     {
2403       char *s = g_strdup_value_contents (value);
2404       g_string_append (string, s);
2405       g_free (s);
2406       return;
2407     }
2408   
2409   func (value, string);
2410 }
2411
2412 void
2413 _gtk_style_property_default_value (const GtkStyleProperty *property,
2414                                    GtkStyleProperties     *properties,
2415                                    GtkStateFlags           state,
2416                                    GValue                 *value)
2417 {
2418   if (property->default_value_func)
2419     property->default_value_func (properties, state, value);
2420   else if (property->pspec->value_type == GTK_TYPE_THEMING_ENGINE)
2421     g_value_set_object (value, gtk_theming_engine_load (NULL));
2422   else if (property->pspec->value_type == PANGO_TYPE_FONT_DESCRIPTION)
2423     g_value_take_boxed (value, pango_font_description_from_string ("Sans 10"));
2424   else if (property->pspec->value_type == GDK_TYPE_RGBA)
2425     {
2426       GdkRGBA color;
2427       gdk_rgba_parse (&color, "pink");
2428       g_value_set_boxed (value, &color);
2429     }
2430   else if (property->pspec->value_type == GTK_TYPE_BORDER)
2431     {
2432       g_value_take_boxed (value, gtk_border_new ());
2433     }
2434   else
2435     g_param_value_set_default (property->pspec, value);
2436 }
2437
2438 gboolean
2439 _gtk_style_property_is_inherit (const GtkStyleProperty *property)
2440 {
2441   g_return_val_if_fail (property != NULL, FALSE);
2442
2443   return property->flags & GTK_STYLE_PROPERTY_INHERIT ? TRUE : FALSE;
2444 }
2445
2446 static gboolean
2447 resolve_color (GtkStyleProperties *props,
2448                GValue             *value)
2449 {
2450   GdkRGBA color;
2451
2452   /* Resolve symbolic color to GdkRGBA */
2453   if (!gtk_symbolic_color_resolve (g_value_get_boxed (value), props, &color))
2454     return FALSE;
2455
2456   /* Store it back, this is where GdkRGBA caching happens */
2457   g_value_unset (value);
2458   g_value_init (value, GDK_TYPE_RGBA);
2459   g_value_set_boxed (value, &color);
2460
2461   return TRUE;
2462 }
2463
2464 static gboolean
2465 resolve_color_rgb (GtkStyleProperties *props,
2466                    GValue             *value)
2467 {
2468   GdkColor color = { 0 };
2469   GdkRGBA rgba;
2470
2471   if (!gtk_symbolic_color_resolve (g_value_get_boxed (value), props, &rgba))
2472     return FALSE;
2473
2474   color.red = rgba.red * 65535. + 0.5;
2475   color.green = rgba.green * 65535. + 0.5;
2476   color.blue = rgba.blue * 65535. + 0.5;
2477
2478   g_value_unset (value);
2479   g_value_init (value, GDK_TYPE_COLOR);
2480   g_value_set_boxed (value, &color);
2481
2482   return TRUE;
2483 }
2484
2485 static gboolean
2486 resolve_win32_theme_part (GtkStyleProperties *props,
2487                           GValue             *value,
2488                           GValue             *value_out,
2489                           GtkStylePropertyContext *context)
2490 {
2491   GtkWin32ThemePart  *part;
2492   cairo_pattern_t *pattern;
2493
2494   part = g_value_get_boxed (value);
2495   if (part == NULL)
2496     return FALSE;
2497
2498   pattern = _gtk_win32_theme_part_render (part, context->width, context->height);
2499
2500   g_value_take_boxed (value_out, pattern);
2501
2502   return TRUE;
2503 }
2504
2505
2506 static gboolean
2507 resolve_gradient (GtkStyleProperties *props,
2508                   GValue             *value)
2509 {
2510   cairo_pattern_t *gradient;
2511
2512   if (!gtk_gradient_resolve (g_value_get_boxed (value), props, &gradient))
2513     return FALSE;
2514
2515   /* Store it back, this is where cairo_pattern_t caching happens */
2516   g_value_unset (value);
2517   g_value_init (value, CAIRO_GOBJECT_TYPE_PATTERN);
2518   g_value_take_boxed (value, gradient);
2519
2520   return TRUE;
2521 }
2522
2523 static gboolean
2524 resolve_shadow (GtkStyleProperties *props,
2525                 GValue *value)
2526 {
2527   GtkShadow *resolved, *base;
2528
2529   base = g_value_get_boxed (value);
2530
2531   if (base == NULL)
2532     return TRUE;
2533   
2534   if (_gtk_shadow_get_resolved (base))
2535     return TRUE;
2536
2537   resolved = _gtk_shadow_resolve (base, props);
2538   if (resolved == NULL)
2539     return FALSE;
2540
2541   g_value_take_boxed (value, resolved);
2542
2543   return TRUE;
2544 }
2545
2546 void
2547 _gtk_style_property_resolve (const GtkStyleProperty *property,
2548                              GtkStyleProperties     *props,
2549                              GtkStateFlags           state,
2550                              GtkStylePropertyContext *context,
2551                              GValue                 *val,
2552                              GValue                 *val_out)
2553 {
2554   if (G_VALUE_TYPE (val) == GTK_TYPE_SYMBOLIC_COLOR)
2555     {
2556       if (property->pspec->value_type == GDK_TYPE_RGBA)
2557         {
2558           if (resolve_color (props, val))
2559             goto out;
2560         }
2561       else if (property->pspec->value_type == GDK_TYPE_COLOR)
2562         {
2563           if (resolve_color_rgb (props, val))
2564             goto out;
2565         }
2566       
2567       g_value_unset (val);
2568       g_value_init (val, property->pspec->value_type);
2569       _gtk_style_property_default_value (property, props, state, val);
2570     }
2571   else if (G_VALUE_TYPE (val) == GDK_TYPE_RGBA)
2572     {
2573       if (g_value_get_boxed (val) == NULL)
2574         _gtk_style_property_default_value (property, props, state, val);
2575     }
2576   else if (G_VALUE_TYPE (val) == GTK_TYPE_GRADIENT)
2577     {
2578       g_return_if_fail (property->pspec->value_type == CAIRO_GOBJECT_TYPE_PATTERN);
2579
2580       if (!resolve_gradient (props, val))
2581         {
2582           g_value_unset (val);
2583           g_value_init (val, CAIRO_GOBJECT_TYPE_PATTERN);
2584           _gtk_style_property_default_value (property, props, state, val);
2585         }
2586     }
2587   else if (G_VALUE_TYPE (val) == GTK_TYPE_SHADOW)
2588     {
2589       if (!resolve_shadow (props, val))
2590         _gtk_style_property_default_value (property, props, state, val);
2591     }
2592   else if (G_VALUE_TYPE (val) == GTK_TYPE_WIN32_THEME_PART)
2593     {
2594       if (resolve_win32_theme_part (props, val, val_out, context))
2595         return; /* Don't copy val, this sets val_out */
2596       _gtk_style_property_default_value (property, props, state, val);
2597     }
2598
2599  out:
2600   g_value_copy (val, val_out);
2601 }
2602
2603 gboolean
2604 _gtk_style_property_is_shorthand  (const GtkStyleProperty *property)
2605 {
2606   g_return_val_if_fail (property != NULL, FALSE);
2607
2608   return property->pack_func != NULL;
2609 }
2610
2611 GParameter *
2612 _gtk_style_property_unpack (const GtkStyleProperty *property,
2613                             const GValue           *value,
2614                             guint                  *n_params)
2615 {
2616   g_return_val_if_fail (property != NULL, NULL);
2617   g_return_val_if_fail (property->unpack_func != NULL, NULL);
2618   g_return_val_if_fail (value != NULL, NULL);
2619   g_return_val_if_fail (n_params != NULL, NULL);
2620
2621   return property->unpack_func (value, n_params);
2622 }
2623
2624 void
2625 _gtk_style_property_pack (const GtkStyleProperty *property,
2626                           GtkStyleProperties     *props,
2627                           GtkStateFlags           state,
2628                           GtkStylePropertyContext *context,
2629                           GValue                 *value)
2630 {
2631   g_return_if_fail (property != NULL);
2632   g_return_if_fail (property->pack_func != NULL);
2633   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
2634   g_return_if_fail (G_IS_VALUE (value));
2635
2636   property->pack_func (value, props, state, context);
2637 }
2638
2639 static void
2640 gtk_style_property_init (void)
2641 {
2642   if (G_LIKELY (properties))
2643     return;
2644
2645   /* stuff is never freed, so no need for free functions */
2646   properties = g_hash_table_new (g_str_hash, g_str_equal);
2647
2648   /* note that gtk_style_properties_register_property() calls this function,
2649    * so make sure we're sanely inited to avoid infloops */
2650
2651   _gtk_style_property_register           (g_param_spec_boxed ("color",
2652                                           "Foreground color",
2653                                           "Foreground color",
2654                                           GDK_TYPE_RGBA, 0),
2655                                           GTK_STYLE_PROPERTY_INHERIT,
2656                                           NULL,
2657                                           NULL,
2658                                           NULL,
2659                                           NULL,
2660                                           NULL,
2661                                           NULL,
2662                                           NULL);
2663   _gtk_style_property_register           (g_param_spec_boxed ("background-color",
2664                                           "Background color",
2665                                           "Background color",
2666                                           GDK_TYPE_RGBA, 0),
2667                                           0,
2668                                           NULL,
2669                                           NULL,
2670                                           NULL,
2671                                           transparent_color_value_parse,
2672                                           NULL,
2673                                           background_color_default_value,
2674                                           NULL);
2675
2676   _gtk_style_property_register           (g_param_spec_boxed ("font-family",
2677                                                               "Font family",
2678                                                               "Font family",
2679                                                               G_TYPE_STRV, 0),
2680                                           GTK_STYLE_PROPERTY_INHERIT,
2681                                           NULL,
2682                                           NULL,
2683                                           NULL,
2684                                           font_family_parse,
2685                                           font_family_value_print,
2686                                           NULL,
2687                                           NULL);
2688   _gtk_style_property_register           (g_param_spec_enum ("font-style",
2689                                                              "Font style",
2690                                                              "Font style",
2691                                                              PANGO_TYPE_STYLE,
2692                                                              PANGO_STYLE_NORMAL, 0),
2693                                           GTK_STYLE_PROPERTY_INHERIT,
2694                                           NULL,
2695                                           NULL,
2696                                           NULL,
2697                                           NULL,
2698                                           NULL,
2699                                           NULL,
2700                                           NULL);
2701   _gtk_style_property_register           (g_param_spec_enum ("font-variant",
2702                                                              "Font variant",
2703                                                              "Font variant",
2704                                                              PANGO_TYPE_VARIANT,
2705                                                              PANGO_VARIANT_NORMAL, 0),
2706                                           GTK_STYLE_PROPERTY_INHERIT,
2707                                           NULL,
2708                                           NULL,
2709                                           NULL,
2710                                           NULL,
2711                                           NULL,
2712                                           NULL,
2713                                           NULL);
2714   /* xxx: need to parse this properly, ie parse the numbers */
2715   _gtk_style_property_register           (g_param_spec_enum ("font-weight",
2716                                                              "Font weight",
2717                                                              "Font weight",
2718                                                              PANGO_TYPE_WEIGHT,
2719                                                              PANGO_WEIGHT_NORMAL, 0),
2720                                           GTK_STYLE_PROPERTY_INHERIT,
2721                                           NULL,
2722                                           NULL,
2723                                           NULL,
2724                                           NULL,
2725                                           NULL,
2726                                           NULL,
2727                                           NULL);
2728   _gtk_style_property_register           (g_param_spec_double ("font-size",
2729                                                                "Font size",
2730                                                                "Font size",
2731                                                                0, G_MAXDOUBLE, 0, 0),
2732                                           GTK_STYLE_PROPERTY_INHERIT,
2733                                           NULL,
2734                                           NULL,
2735                                           NULL,
2736                                           NULL,
2737                                           NULL,
2738                                           NULL,
2739                                           NULL);
2740   _gtk_style_property_register           (g_param_spec_boxed ("font",
2741                                                               "Font Description",
2742                                                               "Font Description",
2743                                                               PANGO_TYPE_FONT_DESCRIPTION, 0),
2744                                           GTK_STYLE_PROPERTY_INHERIT,
2745                                           NULL,
2746                                           unpack_font_description,
2747                                           pack_font_description,
2748                                           font_description_value_parse,
2749                                           font_description_value_print,
2750                                           NULL,
2751                                           unset_font_description);
2752
2753   _gtk_style_property_register           (g_param_spec_boxed ("text-shadow",
2754                                                               "Text shadow",
2755                                                               "Text shadow",
2756                                                               GTK_TYPE_SHADOW, 0),
2757                                           GTK_STYLE_PROPERTY_INHERIT,
2758                                           NULL,
2759                                           NULL,
2760                                           NULL,
2761                                           NULL,
2762                                           NULL,
2763                                           NULL,
2764                                           NULL);
2765
2766   _gtk_style_property_register           (g_param_spec_boxed ("icon-shadow",
2767                                                               "Icon shadow",
2768                                                               "Icon shadow",
2769                                                               GTK_TYPE_SHADOW, 0),
2770                                           GTK_STYLE_PROPERTY_INHERIT,
2771                                           NULL,
2772                                           NULL,
2773                                           NULL,
2774                                           NULL,
2775                                           NULL,
2776                                           NULL,
2777                                           NULL);
2778
2779   gtk_style_properties_register_property (NULL,
2780                                           g_param_spec_boxed ("box-shadow",
2781                                                               "Box shadow",
2782                                                               "Box shadow",
2783                                                               GTK_TYPE_SHADOW, 0));
2784   gtk_style_properties_register_property (NULL,
2785                                           g_param_spec_int ("margin-top",
2786                                                             "margin top",
2787                                                             "Margin at top",
2788                                                             0, G_MAXINT, 0, 0));
2789   gtk_style_properties_register_property (NULL,
2790                                           g_param_spec_int ("margin-left",
2791                                                             "margin left",
2792                                                             "Margin at left",
2793                                                             0, G_MAXINT, 0, 0));
2794   gtk_style_properties_register_property (NULL,
2795                                           g_param_spec_int ("margin-bottom",
2796                                                             "margin bottom",
2797                                                             "Margin at bottom",
2798                                                             0, G_MAXINT, 0, 0));
2799   gtk_style_properties_register_property (NULL,
2800                                           g_param_spec_int ("margin-right",
2801                                                             "margin right",
2802                                                             "Margin at right",
2803                                                             0, G_MAXINT, 0, 0));
2804   _gtk_style_property_register           (g_param_spec_boxed ("margin",
2805                                                               "Margin",
2806                                                               "Margin",
2807                                                               GTK_TYPE_BORDER, 0),
2808                                           0,
2809                                           NULL,
2810                                           unpack_margin,
2811                                           pack_margin,
2812                                           NULL,
2813                                           NULL,
2814                                           NULL,
2815                                           unset_margin);
2816   gtk_style_properties_register_property (NULL,
2817                                           g_param_spec_int ("padding-top",
2818                                                             "padding top",
2819                                                             "Padding at top",
2820                                                             0, G_MAXINT, 0, 0));
2821   gtk_style_properties_register_property (NULL,
2822                                           g_param_spec_int ("padding-left",
2823                                                             "padding left",
2824                                                             "Padding at left",
2825                                                             0, G_MAXINT, 0, 0));
2826   gtk_style_properties_register_property (NULL,
2827                                           g_param_spec_int ("padding-bottom",
2828                                                             "padding bottom",
2829                                                             "Padding at bottom",
2830                                                             0, G_MAXINT, 0, 0));
2831   gtk_style_properties_register_property (NULL,
2832                                           g_param_spec_int ("padding-right",
2833                                                             "padding right",
2834                                                             "Padding at right",
2835                                                             0, G_MAXINT, 0, 0));
2836   _gtk_style_property_register           (g_param_spec_boxed ("padding",
2837                                                               "Padding",
2838                                                               "Padding",
2839                                                               GTK_TYPE_BORDER, 0),
2840                                           0,
2841                                           NULL,
2842                                           unpack_padding,
2843                                           pack_padding,
2844                                           NULL,
2845                                           NULL,
2846                                           NULL,
2847                                           unset_padding);
2848   gtk_style_properties_register_property (NULL,
2849                                           g_param_spec_int ("border-top-width",
2850                                                             "border top width",
2851                                                             "Border width at top",
2852                                                             0, G_MAXINT, 0, 0));
2853   gtk_style_properties_register_property (NULL,
2854                                           g_param_spec_int ("border-left-width",
2855                                                             "border left width",
2856                                                             "Border width at left",
2857                                                             0, G_MAXINT, 0, 0));
2858   gtk_style_properties_register_property (NULL,
2859                                           g_param_spec_int ("border-bottom-width",
2860                                                             "border bottom width",
2861                                                             "Border width at bottom",
2862                                                             0, G_MAXINT, 0, 0));
2863   gtk_style_properties_register_property (NULL,
2864                                           g_param_spec_int ("border-right-width",
2865                                                             "border right width",
2866                                                             "Border width at right",
2867                                                             0, G_MAXINT, 0, 0));
2868   _gtk_style_property_register           (g_param_spec_boxed ("border-width",
2869                                                               "Border width",
2870                                                               "Border width, in pixels",
2871                                                               GTK_TYPE_BORDER, 0),
2872                                           0,
2873                                           NULL,
2874                                           unpack_border_width,
2875                                           pack_border_width,
2876                                           NULL,
2877                                           NULL,
2878                                           NULL,
2879                                           unset_border_width);
2880
2881   _gtk_style_property_register           (g_param_spec_boxed ("border-top-left-radius",
2882                                                               "Border top left radius",
2883                                                               "Border radius of top left corner, in pixels",
2884                                                               GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
2885                                           0,
2886                                           NULL,
2887                                           NULL,
2888                                           NULL,
2889                                           border_corner_radius_value_parse,
2890                                           border_corner_radius_value_print,
2891                                           NULL,
2892                                           NULL);
2893   _gtk_style_property_register           (g_param_spec_boxed ("border-top-right-radius",
2894                                                               "Border top right radius",
2895                                                               "Border radius of top right corner, in pixels",
2896                                                               GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
2897                                           0,
2898                                           NULL,
2899                                           NULL,
2900                                           NULL,
2901                                           border_corner_radius_value_parse,
2902                                           border_corner_radius_value_print,
2903                                           NULL,
2904                                           NULL);
2905   _gtk_style_property_register           (g_param_spec_boxed ("border-bottom-right-radius",
2906                                                               "Border bottom right radius",
2907                                                               "Border radius of bottom right corner, in pixels",
2908                                                               GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
2909                                           0,
2910                                           NULL,
2911                                           NULL,
2912                                           NULL,
2913                                           border_corner_radius_value_parse,
2914                                           border_corner_radius_value_print,
2915                                           NULL,
2916                                           NULL);
2917   _gtk_style_property_register           (g_param_spec_boxed ("border-bottom-left-radius",
2918                                                               "Border bottom left radius",
2919                                                               "Border radius of bottom left corner, in pixels",
2920                                                               GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
2921                                           0,
2922                                           NULL,
2923                                           NULL,
2924                                           NULL,
2925                                           border_corner_radius_value_parse,
2926                                           border_corner_radius_value_print,
2927                                           NULL,
2928                                           NULL);
2929   _gtk_style_property_register           (g_param_spec_int ("border-radius",
2930                                                             "Border radius",
2931                                                             "Border radius, in pixels",
2932                                                             0, G_MAXINT, 0, 0),
2933                                           0,
2934                                           NULL,
2935                                           unpack_border_radius,
2936                                           pack_border_radius,
2937                                           border_radius_value_parse,
2938                                           border_radius_value_print,
2939                                           NULL,
2940                                           unset_border_radius);
2941
2942   gtk_style_properties_register_property (NULL,
2943                                           g_param_spec_enum ("border-style",
2944                                                              "Border style",
2945                                                              "Border style",
2946                                                              GTK_TYPE_BORDER_STYLE,
2947                                                              GTK_BORDER_STYLE_NONE, 0));
2948   _gtk_style_property_register           (g_param_spec_boxed ("border-top-color",
2949                                                               "Border top color",
2950                                                               "Border top color",
2951                                                               GDK_TYPE_RGBA, 0),
2952                                           0,
2953                                           NULL,
2954                                           NULL,
2955                                           NULL,
2956                                           transparent_color_value_parse,
2957                                           NULL,
2958                                           border_color_default_value,
2959                                           NULL);
2960   _gtk_style_property_register           (g_param_spec_boxed ("border-right-color",
2961                                                               "Border right color",
2962                                                               "Border right color",
2963                                                               GDK_TYPE_RGBA, 0),
2964                                           0,
2965                                           NULL,
2966                                           NULL,
2967                                           NULL,
2968                                           transparent_color_value_parse,
2969                                           NULL,
2970                                           border_color_default_value,
2971                                           NULL);
2972   _gtk_style_property_register           (g_param_spec_boxed ("border-bottom-color",
2973                                                               "Border bottom color",
2974                                                               "Border bottom color",
2975                                                               GDK_TYPE_RGBA, 0),
2976                                           0,
2977                                           NULL,
2978                                           NULL,
2979                                           NULL,
2980                                           transparent_color_value_parse,
2981                                           NULL,
2982                                           border_color_default_value,
2983                                           NULL);
2984   _gtk_style_property_register           (g_param_spec_boxed ("border-left-color",
2985                                                               "Border left color",
2986                                                               "Border left color",
2987                                                               GDK_TYPE_RGBA, 0),
2988                                           0,
2989                                           NULL,
2990                                           NULL,
2991                                           NULL,
2992                                           transparent_color_value_parse,
2993                                           NULL,
2994                                           border_color_default_value,
2995                                           NULL);
2996   _gtk_style_property_register           (g_param_spec_boxed ("border-color",
2997                                                               "Border color",
2998                                                               "Border color",
2999                                                               GDK_TYPE_RGBA, 0),
3000                                           0,
3001                                           NULL,
3002                                           unpack_border_color,
3003                                           pack_border_color,
3004                                           border_color_shorthand_value_parse,
3005                                           NULL,
3006                                           NULL,
3007                                           unset_border_color);
3008
3009   gtk_style_properties_register_property (NULL,
3010                                           g_param_spec_boxed ("background-image",
3011                                                               "Background Image",
3012                                                               "Background Image",
3013                                                               CAIRO_GOBJECT_TYPE_PATTERN, 0));
3014   gtk_style_properties_register_property (NULL,
3015                                           g_param_spec_boxed ("background-repeat",
3016                                                               "Background repeat",
3017                                                               "Background repeat",
3018                                                               GTK_TYPE_CSS_BACKGROUND_REPEAT, 0));
3019
3020   gtk_style_properties_register_property (NULL,
3021                                           g_param_spec_boxed ("border-image-source",
3022                                                               "Border image source",
3023                                                               "Border image source",
3024                                                               CAIRO_GOBJECT_TYPE_PATTERN, 0));
3025   gtk_style_properties_register_property (NULL,
3026                                           g_param_spec_boxed ("border-image-repeat",
3027                                                               "Border image repeat",
3028                                                               "Border image repeat",
3029                                                               GTK_TYPE_CSS_BORDER_IMAGE_REPEAT, 0));
3030   gtk_style_properties_register_property (NULL,
3031                                           g_param_spec_boxed ("border-image-slice",
3032                                                               "Border image slice",
3033                                                               "Border image slice",
3034                                                               GTK_TYPE_BORDER, 0));
3035   _gtk_style_property_register           (g_param_spec_boxed ("border-image-width",
3036                                                               "Border image width",
3037                                                               "Border image width",
3038                                                               GTK_TYPE_BORDER, 0),
3039                                           0,
3040                                           NULL,
3041                                           NULL,
3042                                           NULL,
3043                                           NULL,
3044                                           NULL,
3045                                           border_image_width_default_value,
3046                                           NULL);
3047   _gtk_style_property_register           (g_param_spec_boxed ("border-image",
3048                                                               "Border Image",
3049                                                               "Border Image",
3050                                                               GTK_TYPE_BORDER_IMAGE, 0),
3051                                           0,
3052                                           NULL,
3053                                           _gtk_border_image_unpack,
3054                                           _gtk_border_image_pack,
3055                                           NULL,
3056                                           NULL,
3057                                           NULL,
3058                                           unset_border_image);
3059   gtk_style_properties_register_property (NULL,
3060                                           g_param_spec_object ("engine",
3061                                                                "Theming Engine",
3062                                                                "Theming Engine",
3063                                                                GTK_TYPE_THEMING_ENGINE, 0));
3064   gtk_style_properties_register_property (NULL,
3065                                           g_param_spec_boxed ("transition",
3066                                                               "Transition animation description",
3067                                                               "Transition animation description",
3068                                                               GTK_TYPE_ANIMATION_DESCRIPTION, 0));
3069
3070   /* Private property holding the binding sets */
3071   _gtk_style_property_register           (g_param_spec_boxed ("gtk-key-bindings",
3072                                                               "Key bindings",
3073                                                               "Key bindings",
3074                                                               G_TYPE_PTR_ARRAY, 0),
3075                                           0,
3076                                           NULL,
3077                                           NULL,
3078                                           NULL,
3079                                           bindings_value_parse,
3080                                           bindings_value_print,
3081                                           NULL,
3082                                           NULL);
3083 }
3084
3085 const GtkStyleProperty *
3086 _gtk_style_property_lookup (const char *name)
3087 {
3088   gtk_style_property_init ();
3089
3090   return g_hash_table_lookup (properties, name);
3091 }
3092
3093 void
3094 _gtk_style_property_register (GParamSpec               *pspec,
3095                               GtkStylePropertyFlags     flags,
3096                               GtkStylePropertyParser    property_parse_func,
3097                               GtkStyleUnpackFunc        unpack_func,
3098                               GtkStylePackFunc          pack_func,
3099                               GtkStyleParseFunc         parse_func,
3100                               GtkStylePrintFunc         print_func,
3101                               GtkStyleDefaultValueFunc  default_value_func,
3102                               GtkStyleUnsetFunc         unset_func)
3103 {
3104   const GtkStyleProperty *existing;
3105   GtkStyleProperty *node;
3106
3107   g_return_if_fail ((pack_func == NULL) == (unpack_func == NULL));
3108
3109   gtk_style_property_init ();
3110
3111   existing = _gtk_style_property_lookup (pspec->name);
3112   if (existing != NULL)
3113     {
3114       g_warning ("Property \"%s\" was already registered with type %s",
3115                  pspec->name, g_type_name (existing->pspec->value_type));
3116       return;
3117     }
3118
3119   node = g_slice_new0 (GtkStyleProperty);
3120   node->flags = flags;
3121   node->pspec = pspec;
3122   node->property_parse_func = property_parse_func;
3123   node->pack_func = pack_func;
3124   node->unpack_func = unpack_func;
3125   node->parse_func = parse_func;
3126   node->print_func = print_func;
3127   node->default_value_func = default_value_func;
3128   node->unset_func = unset_func;
3129
3130   /* pspec owns name */
3131   g_hash_table_insert (properties, (gchar *)pspec->name, node);
3132 }