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