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