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