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