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