]> Pileus Git - ~andy/gtk/blob - gtk/gtkstyleproperty.c
css: Ensure that font shorthand parsing sets font name and size
[~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_printf (string, "%s %s",
1187                           border_image_repeat_style_to_string (image_repeat->hrepeat),
1188                           border_image_repeat_style_to_string (image_repeat->vrepeat));
1189 }
1190
1191 static gboolean
1192 border_image_value_parse (GtkCssParser *parser,
1193                           GFile *base,
1194                           GValue *value)
1195 {
1196   GValue temp = { 0, };
1197   cairo_pattern_t *pattern = NULL;
1198   GtkGradient *gradient = NULL;
1199   GtkBorder slice, *width = NULL, *parsed_slice;
1200   GtkCssBorderImageRepeat repeat, *parsed_repeat;
1201   gboolean retval = FALSE;
1202   GtkBorderImage *image = NULL;
1203
1204   g_value_init (&temp, CAIRO_GOBJECT_TYPE_PATTERN);
1205
1206   if (!pattern_value_parse (parser, base, &temp))
1207     return FALSE;
1208
1209   if (G_VALUE_TYPE (&temp) == GTK_TYPE_GRADIENT)
1210     gradient = g_value_dup_boxed (&temp);
1211   else
1212     pattern = g_value_dup_boxed (&temp);
1213
1214   g_value_unset (&temp);
1215   g_value_init (&temp, GTK_TYPE_BORDER);
1216
1217   if (!border_value_parse (parser, base, &temp))
1218     goto out;
1219
1220   parsed_slice = g_value_get_boxed (&temp);
1221   slice = *parsed_slice;
1222
1223   if (_gtk_css_parser_try (parser, "/", TRUE))
1224     {
1225       g_value_unset (&temp);
1226       g_value_init (&temp, GTK_TYPE_BORDER);
1227
1228       if (!border_value_parse (parser, base, &temp))
1229         goto out;
1230
1231       width = g_value_dup_boxed (&temp);
1232     }
1233
1234   g_value_unset (&temp);
1235   g_value_init (&temp, GTK_TYPE_CSS_BORDER_IMAGE_REPEAT);
1236
1237   if (!border_image_repeat_value_parse (parser, base, &temp))
1238     goto out;
1239
1240   parsed_repeat = g_value_get_boxed (&temp);
1241   repeat = *parsed_repeat;
1242
1243   g_value_unset (&temp);
1244
1245   if (gradient != NULL)
1246     image = _gtk_border_image_new_for_gradient (gradient, &slice, width, &repeat);
1247   else if (pattern != NULL)
1248     image = _gtk_border_image_new (pattern, &slice, width, &repeat);
1249
1250   if (image != NULL)
1251     {
1252       retval = TRUE;
1253       g_value_take_boxed (value, image);
1254     }
1255
1256  out:
1257   if (pattern != NULL)
1258     cairo_pattern_destroy (pattern);
1259
1260   if (gradient != NULL)
1261     gtk_gradient_unref (gradient);
1262
1263   if (width != NULL)
1264     gtk_border_free (width);
1265
1266   return retval;
1267 }
1268
1269 static gboolean 
1270 enum_value_parse (GtkCssParser *parser,
1271                   GFile        *base,
1272                   GValue       *value)
1273 {
1274   GEnumClass *enum_class;
1275   GEnumValue *enum_value;
1276   char *str;
1277
1278   str = _gtk_css_parser_try_ident (parser, TRUE);
1279   if (str == NULL)
1280     {
1281       _gtk_css_parser_error (parser, "Expected an identifier");
1282       return FALSE;
1283     }
1284
1285   enum_class = g_type_class_ref (G_VALUE_TYPE (value));
1286   enum_value = g_enum_get_value_by_nick (enum_class, str);
1287
1288   if (enum_value)
1289     g_value_set_enum (value, enum_value->value);
1290   else
1291     _gtk_css_parser_error (parser,
1292                            "Unknown value '%s' for enum type '%s'",
1293                            str, g_type_name (G_VALUE_TYPE (value)));
1294   
1295   g_type_class_unref (enum_class);
1296   g_free (str);
1297
1298   return enum_value != NULL;
1299 }
1300
1301 static void
1302 enum_value_print (const GValue *value,
1303                   GString      *string)
1304 {
1305   GEnumClass *enum_class;
1306   GEnumValue *enum_value;
1307
1308   enum_class = g_type_class_ref (G_VALUE_TYPE (value));
1309   enum_value = g_enum_get_value (enum_class, g_value_get_enum (value));
1310
1311   g_string_append (string, enum_value->value_nick);
1312
1313   g_type_class_unref (enum_class);
1314 }
1315
1316 static gboolean 
1317 flags_value_parse (GtkCssParser *parser,
1318                    GFile        *base,
1319                    GValue       *value)
1320 {
1321   GFlagsClass *flags_class;
1322   GFlagsValue *flag_value;
1323   guint flags = 0;
1324   char *str;
1325
1326   flags_class = g_type_class_ref (G_VALUE_TYPE (value));
1327
1328   do {
1329     str = _gtk_css_parser_try_ident (parser, TRUE);
1330     if (str == NULL)
1331       {
1332         _gtk_css_parser_error (parser, "Expected an identifier");
1333         g_type_class_unref (flags_class);
1334         return FALSE;
1335       }
1336
1337       flag_value = g_flags_get_value_by_nick (flags_class, str);
1338       if (!flag_value)
1339         {
1340           _gtk_css_parser_error (parser,
1341                                  "Unknown flag value '%s' for type '%s'",
1342                                  str, g_type_name (G_VALUE_TYPE (value)));
1343           /* XXX Do we want to return FALSE here? We can get
1344            * forward-compatibility for new values this way
1345            */
1346           g_free (str);
1347           g_type_class_unref (flags_class);
1348           return FALSE;
1349         }
1350
1351       g_free (str);
1352     }
1353   while (_gtk_css_parser_try (parser, ",", FALSE));
1354
1355   g_type_class_unref (flags_class);
1356
1357   g_value_set_enum (value, flags);
1358
1359   return TRUE;
1360 }
1361
1362 static void
1363 flags_value_print (const GValue *value,
1364                    GString      *string)
1365 {
1366   GFlagsClass *flags_class;
1367   guint i, flags;
1368
1369   flags_class = g_type_class_ref (G_VALUE_TYPE (value));
1370   flags = g_value_get_flags (value);
1371
1372   for (i = 0; i < flags_class->n_values; i++)
1373     {
1374       GFlagsValue *flags_value = &flags_class->values[i];
1375
1376       if (flags & flags_value->value)
1377         {
1378           if (string->len != 0)
1379             g_string_append (string, ", ");
1380
1381           g_string_append (string, flags_value->value_nick);
1382         }
1383     }
1384
1385   g_type_class_unref (flags_class);
1386 }
1387
1388 static gboolean 
1389 bindings_value_parse (GtkCssParser *parser,
1390                       GFile        *base,
1391                       GValue       *value)
1392 {
1393   GPtrArray *array;
1394   GtkBindingSet *binding_set;
1395   char *name;
1396
1397   array = g_ptr_array_new ();
1398
1399   do {
1400       name = _gtk_css_parser_try_ident (parser, TRUE);
1401       if (name == NULL)
1402         {
1403           _gtk_css_parser_error (parser, "Not a valid binding name");
1404           g_ptr_array_free (array, TRUE);
1405           return FALSE;
1406         }
1407
1408       binding_set = gtk_binding_set_find (name);
1409
1410       if (!binding_set)
1411         {
1412           _gtk_css_parser_error (parser, "No binding set named '%s'", name);
1413           g_free (name);
1414           continue;
1415         }
1416
1417       g_ptr_array_add (array, binding_set);
1418       g_free (name);
1419     }
1420   while (_gtk_css_parser_try (parser, ",", TRUE));
1421
1422   g_value_take_boxed (value, array);
1423
1424   return TRUE;
1425 }
1426
1427 static void
1428 bindings_value_print (const GValue *value,
1429                       GString      *string)
1430 {
1431   GPtrArray *array;
1432   guint i;
1433
1434   array = g_value_get_boxed (value);
1435
1436   for (i = 0; i < array->len; i++)
1437     {
1438       GtkBindingSet *binding_set = g_ptr_array_index (array, i);
1439
1440       if (i > 0)
1441         g_string_append (string, ", ");
1442       g_string_append (string, binding_set->set_name);
1443     }
1444 }
1445
1446 static gboolean 
1447 border_corner_radius_value_parse (GtkCssParser *parser,
1448                                   GFile        *base,
1449                                   GValue       *value)
1450 {
1451   GtkCssBorderCornerRadius corner;
1452
1453   if (!_gtk_css_parser_try_double (parser, &corner.horizontal))
1454     {
1455       _gtk_css_parser_error (parser, "Expected a number");
1456       return FALSE;
1457     }
1458   else if (corner.horizontal < 0)
1459     goto negative;
1460
1461   if (!_gtk_css_parser_try_double (parser, &corner.vertical))
1462     corner.vertical = corner.horizontal;
1463   else if (corner.vertical < 0)
1464     goto negative;
1465
1466   g_value_set_boxed (value, &corner);
1467   return TRUE;
1468
1469 negative:
1470   _gtk_css_parser_error (parser, "Border radius values cannot be negative");
1471   return FALSE;
1472 }
1473
1474 static void
1475 border_corner_radius_value_print (const GValue *value,
1476                                   GString      *string)
1477 {
1478   GtkCssBorderCornerRadius *corner;
1479
1480   corner = g_value_get_boxed (value);
1481
1482   if (corner == NULL)
1483     {
1484       g_string_append (string, "none");
1485       return;
1486     }
1487
1488   string_append_double (string, corner->horizontal);
1489   if (corner->horizontal != corner->vertical)
1490     {
1491       g_string_append_c (string, ' ');
1492       string_append_double (string, corner->vertical);
1493     }
1494 }
1495
1496 static gboolean 
1497 border_radius_value_parse (GtkCssParser *parser,
1498                            GFile        *base,
1499                            GValue       *value)
1500 {
1501   GtkCssBorderRadius border;
1502
1503   if (!_gtk_css_parser_try_double (parser, &border.top_left.horizontal))
1504     {
1505       _gtk_css_parser_error (parser, "Expected a number");
1506       return FALSE;
1507     }
1508   else if (border.top_left.horizontal < 0)
1509     goto negative;
1510
1511   if (_gtk_css_parser_try_double (parser, &border.top_right.horizontal))
1512     {
1513       if (border.top_right.horizontal < 0)
1514         goto negative;
1515       if (_gtk_css_parser_try_double (parser, &border.bottom_right.horizontal))
1516         {
1517           if (border.bottom_right.horizontal < 0)
1518             goto negative;
1519           if (!_gtk_css_parser_try_double (parser, &border.bottom_left.horizontal))
1520             border.bottom_left.horizontal = border.top_right.horizontal;
1521           else if (border.bottom_left.horizontal < 0)
1522             goto negative;
1523         }
1524       else
1525         {
1526           border.bottom_right.horizontal = border.top_left.horizontal;
1527           border.bottom_left.horizontal = border.top_right.horizontal;
1528         }
1529     }
1530   else
1531     {
1532       border.top_right.horizontal = border.top_left.horizontal;
1533       border.bottom_right.horizontal = border.top_left.horizontal;
1534       border.bottom_left.horizontal = border.top_left.horizontal;
1535     }
1536
1537   if (_gtk_css_parser_try (parser, "/", TRUE))
1538     {
1539       if (!_gtk_css_parser_try_double (parser, &border.top_left.vertical))
1540         {
1541           _gtk_css_parser_error (parser, "Expected a number");
1542           return FALSE;
1543         }
1544       else if (border.top_left.vertical < 0)
1545         goto negative;
1546
1547       if (_gtk_css_parser_try_double (parser, &border.top_right.vertical))
1548         {
1549           if (border.top_right.vertical < 0)
1550             goto negative;
1551           if (_gtk_css_parser_try_double (parser, &border.bottom_right.vertical))
1552             {
1553               if (border.bottom_right.vertical < 0)
1554                 goto negative;
1555               if (!_gtk_css_parser_try_double (parser, &border.bottom_left.vertical))
1556                 border.bottom_left.vertical = border.top_right.vertical;
1557               else if (border.bottom_left.vertical < 0)
1558                 goto negative;
1559             }
1560           else
1561             {
1562               border.bottom_right.vertical = border.top_left.vertical;
1563               border.bottom_left.vertical = border.top_right.vertical;
1564             }
1565         }
1566       else
1567         {
1568           border.top_right.vertical = border.top_left.vertical;
1569           border.bottom_right.vertical = border.top_left.vertical;
1570           border.bottom_left.vertical = border.top_left.vertical;
1571         }
1572     }
1573   else
1574     {
1575       border.top_left.vertical = border.top_left.horizontal;
1576       border.top_right.vertical = border.top_right.horizontal;
1577       border.bottom_right.vertical = border.bottom_right.horizontal;
1578       border.bottom_left.vertical = border.bottom_left.horizontal;
1579     }
1580
1581   /* border-radius is an int property for backwards-compat reasons */
1582   g_value_unset (value);
1583   g_value_init (value, GTK_TYPE_CSS_BORDER_RADIUS);
1584   g_value_set_boxed (value, &border);
1585
1586   return TRUE;
1587
1588 negative:
1589   _gtk_css_parser_error (parser, "Border radius values cannot be negative");
1590   return FALSE;
1591 }
1592
1593 static void
1594 border_radius_value_print (const GValue *value,
1595                            GString      *string)
1596 {
1597   GtkCssBorderRadius *border;
1598
1599   border = g_value_get_boxed (value);
1600
1601   if (border == NULL)
1602     {
1603       g_string_append (string, "none");
1604       return;
1605     }
1606
1607   string_append_double (string, border->top_left.horizontal);
1608   if (border->top_left.horizontal != border->top_right.horizontal ||
1609       border->top_left.horizontal != border->bottom_right.horizontal ||
1610       border->top_left.horizontal != border->bottom_left.horizontal)
1611     {
1612       g_string_append_c (string, ' ');
1613       string_append_double (string, border->top_right.horizontal);
1614       if (border->top_left.horizontal != border->bottom_right.horizontal ||
1615           border->top_right.horizontal != border->bottom_left.horizontal)
1616         {
1617           g_string_append_c (string, ' ');
1618           string_append_double (string, border->bottom_right.horizontal);
1619           if (border->top_right.horizontal != border->bottom_left.horizontal)
1620             {
1621               g_string_append_c (string, ' ');
1622               string_append_double (string, border->bottom_left.horizontal);
1623             }
1624         }
1625     }
1626
1627   if (border->top_left.horizontal != border->top_left.vertical ||
1628       border->top_right.horizontal != border->top_right.vertical ||
1629       border->bottom_right.horizontal != border->bottom_right.vertical ||
1630       border->bottom_left.horizontal != border->bottom_left.vertical)
1631     {
1632       g_string_append (string, " / ");
1633       string_append_double (string, border->top_left.vertical);
1634       if (border->top_left.vertical != border->top_right.vertical ||
1635           border->top_left.vertical != border->bottom_right.vertical ||
1636           border->top_left.vertical != border->bottom_left.vertical)
1637         {
1638           g_string_append_c (string, ' ');
1639           string_append_double (string, border->top_right.vertical);
1640           if (border->top_left.vertical != border->bottom_right.vertical ||
1641               border->top_right.vertical != border->bottom_left.vertical)
1642             {
1643               g_string_append_c (string, ' ');
1644               string_append_double (string, border->bottom_right.vertical);
1645               if (border->top_right.vertical != border->bottom_left.vertical)
1646                 {
1647                   g_string_append_c (string, ' ');
1648                   string_append_double (string, border->bottom_left.vertical);
1649                 }
1650             }
1651         }
1652
1653     }
1654 }
1655
1656 static gboolean 
1657 border_color_value_parse (GtkCssParser *parser,
1658                           GFile        *base,
1659                           GValue       *value)
1660 {
1661   if (_gtk_css_parser_try (parser, "transparent", TRUE))
1662     {
1663       GdkRGBA transparent = { 0, 0, 0, 0 };
1664           
1665       g_value_set_boxed (value, &transparent);
1666
1667       return TRUE;
1668     }
1669
1670   return rgba_value_parse (parser, base, value);
1671 }
1672
1673 static gboolean 
1674 border_color_shorthand_value_parse (GtkCssParser *parser,
1675                                     GFile        *base,
1676                                     GValue       *value)
1677 {
1678   GtkSymbolicColor *symbolic;
1679   GPtrArray *array;
1680
1681   array = g_ptr_array_new_with_free_func ((GDestroyNotify) gtk_symbolic_color_unref);
1682
1683   do
1684     {
1685       if (_gtk_css_parser_try (parser, "transparent", TRUE))
1686         {
1687           GdkRGBA transparent = { 0, 0, 0, 0 };
1688           
1689           symbolic = gtk_symbolic_color_new_literal (&transparent);
1690         }
1691       else
1692         {
1693           symbolic = _gtk_css_parser_read_symbolic_color (parser);
1694       
1695           if (symbolic == NULL)
1696             return FALSE;
1697         }
1698       
1699       g_ptr_array_add (array, symbolic);
1700     }
1701   while (array->len < 4 && 
1702          !_gtk_css_parser_is_eof (parser) &&
1703          !_gtk_css_parser_begins_with (parser, ';') &&
1704          !_gtk_css_parser_begins_with (parser, '}'));
1705
1706   switch (array->len)
1707     {
1708       default:
1709         g_assert_not_reached ();
1710         break;
1711       case 1:
1712         g_ptr_array_add (array, gtk_symbolic_color_ref (g_ptr_array_index (array, 0)));
1713         /* fall through */
1714       case 2:
1715         g_ptr_array_add (array, gtk_symbolic_color_ref (g_ptr_array_index (array, 0)));
1716         /* fall through */
1717       case 3:
1718         g_ptr_array_add (array, gtk_symbolic_color_ref (g_ptr_array_index (array, 1)));
1719         /* fall through */
1720       case 4:
1721         break;
1722     }
1723
1724   g_value_unset (value);
1725   g_value_init (value, G_TYPE_PTR_ARRAY);
1726   g_value_take_boxed (value, array);
1727
1728   return TRUE;
1729 }
1730
1731 /*** PACKING ***/
1732
1733 static GParameter *
1734 unpack_border (const GValue *value,
1735                guint        *n_params,
1736                const char   *top,
1737                const char   *left,
1738                const char   *bottom,
1739                const char   *right)
1740 {
1741   GParameter *parameter = g_new0 (GParameter, 4);
1742   GtkBorder *border = g_value_get_boxed (value);
1743
1744   parameter[0].name = top;
1745   g_value_init (&parameter[0].value, G_TYPE_INT);
1746   g_value_set_int (&parameter[0].value, border->top);
1747   parameter[1].name = left;
1748   g_value_init (&parameter[1].value, G_TYPE_INT);
1749   g_value_set_int (&parameter[1].value, border->left);
1750   parameter[2].name = bottom;
1751   g_value_init (&parameter[2].value, G_TYPE_INT);
1752   g_value_set_int (&parameter[2].value, border->bottom);
1753   parameter[3].name = right;
1754   g_value_init (&parameter[3].value, G_TYPE_INT);
1755   g_value_set_int (&parameter[3].value, border->right);
1756
1757   *n_params = 4;
1758   return parameter;
1759 }
1760
1761 static void
1762 pack_border (GValue             *value,
1763              GtkStyleProperties *props,
1764              GtkStateFlags       state,
1765              const char         *top,
1766              const char         *left,
1767              const char         *bottom,
1768              const char         *right)
1769 {
1770   GtkBorder border;
1771   int t, l, b, r;
1772
1773   gtk_style_properties_get (props,
1774                             state,
1775                             top, &t,
1776                             left, &l,
1777                             bottom, &b,
1778                             right, &r,
1779                             NULL);
1780
1781   border.top = t;
1782   border.left = l;
1783   border.bottom = b;
1784   border.right = r;
1785
1786   g_value_set_boxed (value, &border);
1787 }
1788
1789 static GParameter *
1790 unpack_border_width (const GValue *value,
1791                      guint        *n_params)
1792 {
1793   return unpack_border (value, n_params,
1794                         "border-top-width", "border-left-width",
1795                         "border-bottom-width", "border-right-width");
1796 }
1797
1798 static void
1799 pack_border_width (GValue             *value,
1800                    GtkStyleProperties *props,
1801                    GtkStateFlags       state)
1802 {
1803   pack_border (value, props, state,
1804                "border-top-width", "border-left-width",
1805                "border-bottom-width", "border-right-width");
1806 }
1807
1808 static GParameter *
1809 unpack_padding (const GValue *value,
1810                 guint        *n_params)
1811 {
1812   return unpack_border (value, n_params,
1813                         "padding-top", "padding-left",
1814                         "padding-bottom", "padding-right");
1815 }
1816
1817 static void
1818 pack_padding (GValue             *value,
1819               GtkStyleProperties *props,
1820               GtkStateFlags       state)
1821 {
1822   pack_border (value, props, state,
1823                "padding-top", "padding-left",
1824                "padding-bottom", "padding-right");
1825 }
1826
1827 static GParameter *
1828 unpack_margin (const GValue *value,
1829                guint        *n_params)
1830 {
1831   return unpack_border (value, n_params,
1832                         "margin-top", "margin-left",
1833                         "margin-bottom", "margin-right");
1834 }
1835
1836 static void
1837 pack_margin (GValue             *value,
1838              GtkStyleProperties *props,
1839              GtkStateFlags       state)
1840 {
1841   pack_border (value, props, state,
1842                "margin-top", "margin-left",
1843                "margin-bottom", "margin-right");
1844 }
1845
1846 static GParameter *
1847 unpack_border_radius (const GValue *value,
1848                       guint        *n_params)
1849 {
1850   GParameter *parameter = g_new0 (GParameter, 4);
1851   GtkCssBorderRadius *border;
1852   
1853   if (G_VALUE_HOLDS_BOXED (value))
1854     border = g_value_get_boxed (value);
1855   else
1856     border = NULL;
1857
1858   parameter[0].name = "border-top-left-radius";
1859   g_value_init (&parameter[0].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
1860   parameter[1].name = "border-top-right-radius";
1861   g_value_init (&parameter[1].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
1862   parameter[2].name = "border-bottom-right-radius";
1863   g_value_init (&parameter[2].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
1864   parameter[3].name = "border-bottom-left-radius";
1865   g_value_init (&parameter[3].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
1866   if (border)
1867     {
1868       g_value_set_boxed (&parameter[0].value, &border->top_left);
1869       g_value_set_boxed (&parameter[1].value, &border->top_right);
1870       g_value_set_boxed (&parameter[2].value, &border->bottom_right);
1871       g_value_set_boxed (&parameter[3].value, &border->bottom_left);
1872     }
1873
1874   *n_params = 4;
1875   return parameter;
1876 }
1877
1878 static void
1879 pack_border_radius (GValue             *value,
1880                     GtkStyleProperties *props,
1881                     GtkStateFlags       state)
1882 {
1883   GtkCssBorderCornerRadius *top_left;
1884
1885   /* NB: We are an int property, so we have to resolve to an int here.
1886    * So we just resolve to an int. We pick one and stick to it.
1887    * Lesson learned: Don't query border-radius shorthand, query the 
1888    * real properties instead. */
1889   gtk_style_properties_get (props,
1890                             state,
1891                             "border-top-left-radius", &top_left,
1892                             NULL);
1893
1894   if (top_left)
1895     g_value_set_int (value, top_left->horizontal);
1896
1897   g_free (top_left);
1898 }
1899
1900 static GParameter *
1901 unpack_font_description (const GValue *value,
1902                          guint        *n_params)
1903 {
1904   GParameter *parameter = g_new0 (GParameter, 5);
1905   PangoFontDescription *description;
1906   PangoFontMask mask;
1907   guint n;
1908   
1909   /* For backwards compat, we only unpack values that are indeed set.
1910    * For strict CSS conformance we need to unpack all of them.
1911    * Note that we do set all of them in the parse function, so it
1912    * will not have effects when parsing CSS files. It will though
1913    * for custom style providers.
1914    */
1915
1916   description = g_value_get_boxed (value);
1917   n = 0;
1918
1919   if (description)
1920     mask = pango_font_description_get_set_fields (description);
1921   else
1922     mask = 0;
1923
1924   if (mask & PANGO_FONT_MASK_FAMILY)
1925     {
1926       GPtrArray *strv = g_ptr_array_new ();
1927
1928       g_ptr_array_add (strv, g_strdup (pango_font_description_get_family (description)));
1929       g_ptr_array_add (strv, NULL);
1930       parameter[n].name = "font-family";
1931       g_value_init (&parameter[n].value, G_TYPE_STRV);
1932       g_value_take_boxed (&parameter[n].value,
1933                           g_ptr_array_free (strv, FALSE));
1934       n++;
1935     }
1936
1937   if (mask & PANGO_FONT_MASK_STYLE)
1938     {
1939       parameter[n].name = "font-style";
1940       g_value_init (&parameter[n].value, PANGO_TYPE_STYLE);
1941       g_value_set_enum (&parameter[n].value,
1942                         pango_font_description_get_style (description));
1943       n++;
1944     }
1945
1946   if (mask & PANGO_FONT_MASK_VARIANT)
1947     {
1948       parameter[n].name = "font-variant";
1949       g_value_init (&parameter[n].value, PANGO_TYPE_VARIANT);
1950       g_value_set_enum (&parameter[n].value,
1951                         pango_font_description_get_variant (description));
1952       n++;
1953     }
1954
1955   if (mask & PANGO_FONT_MASK_WEIGHT)
1956     {
1957       parameter[n].name = "font-weight";
1958       g_value_init (&parameter[n].value, PANGO_TYPE_WEIGHT);
1959       g_value_set_enum (&parameter[n].value,
1960                         pango_font_description_get_weight (description));
1961       n++;
1962     }
1963
1964   if (mask & PANGO_FONT_MASK_SIZE)
1965     {
1966       parameter[n].name = "font-size";
1967       g_value_init (&parameter[n].value, G_TYPE_DOUBLE);
1968       g_value_set_double (&parameter[n].value,
1969                           (double) pango_font_description_get_size (description) / PANGO_SCALE);
1970       n++;
1971     }
1972
1973   *n_params = n;
1974
1975   return parameter;
1976 }
1977
1978 static void
1979 pack_font_description (GValue             *value,
1980                        GtkStyleProperties *props,
1981                        GtkStateFlags       state)
1982 {
1983   PangoFontDescription *description;
1984   char **families;
1985   PangoStyle style;
1986   PangoVariant variant;
1987   PangoWeight weight;
1988   double size;
1989
1990   gtk_style_properties_get (props,
1991                             state,
1992                             "font-family", &families,
1993                             "font-style", &style,
1994                             "font-variant", &variant,
1995                             "font-weight", &weight,
1996                             "font-size", &size,
1997                             NULL);
1998
1999   description = pango_font_description_new ();
2000   /* xxx: Can we set all the families here somehow? */
2001   if (families)
2002     pango_font_description_set_family (description, families[0]);
2003   pango_font_description_set_size (description, round (size * PANGO_SCALE));
2004   pango_font_description_set_style (description, style);
2005   pango_font_description_set_variant (description, variant);
2006   pango_font_description_set_weight (description, weight);
2007
2008   g_free (families);
2009
2010   g_value_take_boxed (value, description);
2011 }
2012
2013 static GParameter *
2014 unpack_border_color (const GValue *value,
2015                      guint        *n_params)
2016 {
2017   GParameter *parameter = g_new0 (GParameter, 4);
2018   GType type;
2019   
2020   type = G_VALUE_TYPE (value);
2021   if (type == G_TYPE_PTR_ARRAY)
2022     type = GTK_TYPE_SYMBOLIC_COLOR;
2023
2024   parameter[0].name = "border-top-color";
2025   g_value_init (&parameter[0].value, type);
2026   parameter[1].name = "border-right-color";
2027   g_value_init (&parameter[1].value, type);
2028   parameter[2].name = "border-bottom-color";
2029   g_value_init (&parameter[2].value, type);
2030   parameter[3].name = "border-left-color";
2031   g_value_init (&parameter[3].value, type);
2032
2033   if (G_VALUE_TYPE (value) == G_TYPE_PTR_ARRAY)
2034     {
2035       GPtrArray *array = g_value_get_boxed (value);
2036       guint i;
2037
2038       for (i = 0; i < 4; i++)
2039         g_value_set_boxed (&parameter[i].value, g_ptr_array_index (array, i));
2040     }
2041   else
2042     {
2043       /* can be RGBA or symbolic color */
2044       gpointer p = g_value_get_boxed (value);
2045
2046       g_value_set_boxed (&parameter[0].value, p);
2047       g_value_set_boxed (&parameter[1].value, p);
2048       g_value_set_boxed (&parameter[2].value, p);
2049       g_value_set_boxed (&parameter[3].value, p);
2050     }
2051
2052   *n_params = 4;
2053   return parameter;
2054 }
2055
2056 static void
2057 pack_border_color (GValue             *value,
2058                    GtkStyleProperties *props,
2059                    GtkStateFlags       state)
2060 {
2061   /* NB: We are a color property, so we have to resolve to a color here.
2062    * So we just resolve to a color. We pick one and stick to it.
2063    * Lesson learned: Don't query border-color shorthand, query the 
2064    * real properties instead. */
2065   g_value_unset (value);
2066   gtk_style_properties_get_property (props, "border-top-color", state, value);
2067 }
2068
2069 /*** UNSET FUNCS ***/
2070
2071 static void
2072 unset_font_description (GtkStyleProperties *props,
2073                         GtkStateFlags       state)
2074 {
2075   gtk_style_properties_unset_property (props, "font-family", state);
2076   gtk_style_properties_unset_property (props, "font-style", state);
2077   gtk_style_properties_unset_property (props, "font-variant", state);
2078   gtk_style_properties_unset_property (props, "font-weight", state);
2079   gtk_style_properties_unset_property (props, "font-size", state);
2080 }
2081
2082 static void
2083 unset_margin (GtkStyleProperties *props,
2084               GtkStateFlags       state)
2085 {
2086   gtk_style_properties_unset_property (props, "margin-top", state);
2087   gtk_style_properties_unset_property (props, "margin-right", state);
2088   gtk_style_properties_unset_property (props, "margin-bottom", state);
2089   gtk_style_properties_unset_property (props, "margin-left", state);
2090 }
2091
2092 static void
2093 unset_padding (GtkStyleProperties *props,
2094                GtkStateFlags       state)
2095 {
2096   gtk_style_properties_unset_property (props, "padding-top", state);
2097   gtk_style_properties_unset_property (props, "padding-right", state);
2098   gtk_style_properties_unset_property (props, "padding-bottom", state);
2099   gtk_style_properties_unset_property (props, "padding-left", state);
2100 }
2101
2102 static void
2103 unset_border_width (GtkStyleProperties *props,
2104                     GtkStateFlags       state)
2105 {
2106   gtk_style_properties_unset_property (props, "border-top-width", state);
2107   gtk_style_properties_unset_property (props, "border-right-width", state);
2108   gtk_style_properties_unset_property (props, "border-bottom-width", state);
2109   gtk_style_properties_unset_property (props, "border-left-width", state);
2110 }
2111
2112 static void
2113 unset_border_radius (GtkStyleProperties *props,
2114                      GtkStateFlags       state)
2115 {
2116   gtk_style_properties_unset_property (props, "border-top-right-radius", state);
2117   gtk_style_properties_unset_property (props, "border-bottom-right-radius", state);
2118   gtk_style_properties_unset_property (props, "border-bottom-left-radius", state);
2119   gtk_style_properties_unset_property (props, "border-top-left-radius", state);
2120 }
2121
2122 static void
2123 unset_border_color (GtkStyleProperties *props,
2124                     GtkStateFlags       state)
2125 {
2126   gtk_style_properties_unset_property (props, "border-top-color", state);
2127   gtk_style_properties_unset_property (props, "border-right-color", state);
2128   gtk_style_properties_unset_property (props, "border-bottom-color", state);
2129   gtk_style_properties_unset_property (props, "border-left-color", state);
2130 }
2131
2132 static void
2133 unset_border_image (GtkStyleProperties *props,
2134                     GtkStateFlags       state)
2135 {
2136   gtk_style_properties_unset_property (props, "border-image-source", state);
2137   gtk_style_properties_unset_property (props, "border-image-slice", state);
2138   gtk_style_properties_unset_property (props, "border-image-repeat", state);
2139   gtk_style_properties_unset_property (props, "border-image-width", state);
2140 }
2141
2142 /*** default values ***/
2143
2144 static void
2145 border_image_width_default_value (GtkStyleProperties *props,
2146                                   GtkStateFlags       state,
2147                                   GValue             *value)
2148 {
2149 }
2150
2151 static void
2152 border_color_default_value (GtkStyleProperties *props,
2153                             GtkStateFlags       state,
2154                             GValue             *value)
2155 {
2156   g_value_unset (value);
2157   gtk_style_properties_get_property (props, "color", state, value);
2158 }
2159
2160 /*** API ***/
2161
2162 static void
2163 css_string_funcs_init (void)
2164 {
2165   if (G_LIKELY (parse_funcs != NULL))
2166     return;
2167
2168   parse_funcs = g_hash_table_new (NULL, NULL);
2169   print_funcs = g_hash_table_new (NULL, NULL);
2170
2171   register_conversion_function (GDK_TYPE_RGBA,
2172                                 rgba_value_parse,
2173                                 rgba_value_print);
2174   register_conversion_function (GDK_TYPE_COLOR,
2175                                 color_value_parse,
2176                                 color_value_print);
2177   register_conversion_function (GTK_TYPE_SYMBOLIC_COLOR,
2178                                 symbolic_color_value_parse,
2179                                 symbolic_color_value_print);
2180   register_conversion_function (PANGO_TYPE_FONT_DESCRIPTION,
2181                                 font_description_value_parse,
2182                                 font_description_value_print);
2183   register_conversion_function (G_TYPE_BOOLEAN,
2184                                 boolean_value_parse,
2185                                 boolean_value_print);
2186   register_conversion_function (G_TYPE_INT,
2187                                 int_value_parse,
2188                                 int_value_print);
2189   register_conversion_function (G_TYPE_UINT,
2190                                 uint_value_parse,
2191                                 uint_value_print);
2192   register_conversion_function (G_TYPE_DOUBLE,
2193                                 double_value_parse,
2194                                 double_value_print);
2195   register_conversion_function (G_TYPE_FLOAT,
2196                                 float_value_parse,
2197                                 float_value_print);
2198   register_conversion_function (G_TYPE_STRING,
2199                                 string_value_parse,
2200                                 string_value_print);
2201   register_conversion_function (GTK_TYPE_THEMING_ENGINE,
2202                                 theming_engine_value_parse,
2203                                 theming_engine_value_print);
2204   register_conversion_function (GTK_TYPE_ANIMATION_DESCRIPTION,
2205                                 animation_description_value_parse,
2206                                 animation_description_value_print);
2207   register_conversion_function (GTK_TYPE_BORDER,
2208                                 border_value_parse,
2209                                 border_value_print);
2210   register_conversion_function (GTK_TYPE_GRADIENT,
2211                                 gradient_value_parse,
2212                                 gradient_value_print);
2213   register_conversion_function (CAIRO_GOBJECT_TYPE_PATTERN,
2214                                 pattern_value_parse,
2215                                 pattern_value_print);
2216   register_conversion_function (GTK_TYPE_BORDER_IMAGE,
2217                                 border_image_value_parse,
2218                                 NULL);
2219   register_conversion_function (GTK_TYPE_CSS_BORDER_IMAGE_REPEAT,
2220                                 border_image_repeat_value_parse,
2221                                 border_image_repeat_value_print);
2222   register_conversion_function (GTK_TYPE_SHADOW,
2223                                 shadow_value_parse,
2224                                 shadow_value_print);
2225   register_conversion_function (G_TYPE_ENUM,
2226                                 enum_value_parse,
2227                                 enum_value_print);
2228   register_conversion_function (G_TYPE_FLAGS,
2229                                 flags_value_parse,
2230                                 flags_value_print);
2231 }
2232
2233 gboolean
2234 _gtk_style_property_parse_value (const GtkStyleProperty *property,
2235                                  GValue                 *value,
2236                                  GtkCssParser           *parser,
2237                                  GFile                  *base)
2238 {
2239   GtkStyleParseFunc func;
2240
2241   g_return_val_if_fail (value != NULL, FALSE);
2242   g_return_val_if_fail (parser != NULL, FALSE);
2243
2244   css_string_funcs_init ();
2245
2246   if (property)
2247     {
2248       if (_gtk_css_parser_try (parser, "none", TRUE))
2249         {
2250           /* Insert the default value, so it has an opportunity
2251            * to override other style providers when merged
2252            */
2253           g_param_value_set_default (property->pspec, value);
2254           return TRUE;
2255         }
2256       else if (property->property_parse_func)
2257         {
2258           GError *error = NULL;
2259           char *value_str;
2260           gboolean success;
2261           
2262           value_str = _gtk_css_parser_read_value (parser);
2263           if (value_str == NULL)
2264             return FALSE;
2265           
2266           success = (*property->property_parse_func) (value_str, value, &error);
2267
2268           g_free (value_str);
2269
2270           return success;
2271         }
2272
2273       func = property->parse_func;
2274     }
2275   else
2276     func = NULL;
2277
2278   if (func == NULL)
2279     func = g_hash_table_lookup (parse_funcs,
2280                                 GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
2281   if (func == NULL)
2282     func = g_hash_table_lookup (parse_funcs,
2283                                 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
2284
2285   if (func == NULL)
2286     {
2287       _gtk_css_parser_error (parser,
2288                              "Cannot convert to type '%s'",
2289                              g_type_name (G_VALUE_TYPE (value)));
2290       return FALSE;
2291     }
2292
2293   return (*func) (parser, base, value);
2294 }
2295
2296 void
2297 _gtk_style_property_print_value (const GtkStyleProperty *property,
2298                                  const GValue           *value,
2299                                  GString                *string)
2300 {
2301   GtkStylePrintFunc func;
2302
2303   css_string_funcs_init ();
2304
2305   if (property)
2306     func = property->print_func;
2307   else
2308     func = NULL;
2309
2310   if (func == NULL)
2311     func = g_hash_table_lookup (print_funcs,
2312                                 GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
2313   if (func == NULL)
2314     func = g_hash_table_lookup (print_funcs,
2315                                 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
2316
2317   if (func == NULL)
2318     {
2319       char *s = g_strdup_value_contents (value);
2320       g_string_append (string, s);
2321       g_free (s);
2322       return;
2323     }
2324   
2325   func (value, string);
2326 }
2327
2328 void
2329 _gtk_style_property_default_value (const GtkStyleProperty *property,
2330                                    GtkStyleProperties     *properties,
2331                                    GtkStateFlags           state,
2332                                    GValue                 *value)
2333 {
2334   if (property->default_value_func)
2335     property->default_value_func (properties, state, value);
2336   else if (property->pspec->value_type == GTK_TYPE_THEMING_ENGINE)
2337     g_value_set_object (value, gtk_theming_engine_load (NULL));
2338   else if (property->pspec->value_type == PANGO_TYPE_FONT_DESCRIPTION)
2339     g_value_take_boxed (value, pango_font_description_from_string ("Sans 10"));
2340   else if (property->pspec->value_type == GDK_TYPE_RGBA)
2341     {
2342       GdkRGBA color;
2343       gdk_rgba_parse (&color, "pink");
2344       g_value_set_boxed (value, &color);
2345     }
2346   else if (property->pspec->value_type == GTK_TYPE_BORDER)
2347     {
2348       g_value_take_boxed (value, gtk_border_new ());
2349     }
2350   else
2351     g_param_value_set_default (property->pspec, value);
2352 }
2353
2354 gboolean
2355 _gtk_style_property_is_inherit (const GtkStyleProperty *property)
2356 {
2357   g_return_val_if_fail (property != NULL, FALSE);
2358
2359   return property->flags & GTK_STYLE_PROPERTY_INHERIT ? TRUE : FALSE;
2360 }
2361
2362 static gboolean
2363 resolve_color (GtkStyleProperties *props,
2364                GValue             *value)
2365 {
2366   GdkRGBA color;
2367
2368   /* Resolve symbolic color to GdkRGBA */
2369   if (!gtk_symbolic_color_resolve (g_value_get_boxed (value), props, &color))
2370     return FALSE;
2371
2372   /* Store it back, this is where GdkRGBA caching happens */
2373   g_value_unset (value);
2374   g_value_init (value, GDK_TYPE_RGBA);
2375   g_value_set_boxed (value, &color);
2376
2377   return TRUE;
2378 }
2379
2380 static gboolean
2381 resolve_color_rgb (GtkStyleProperties *props,
2382                    GValue             *value)
2383 {
2384   GdkColor color = { 0 };
2385   GdkRGBA rgba;
2386
2387   if (!gtk_symbolic_color_resolve (g_value_get_boxed (value), props, &rgba))
2388     return FALSE;
2389
2390   color.red = rgba.red * 65535. + 0.5;
2391   color.green = rgba.green * 65535. + 0.5;
2392   color.blue = rgba.blue * 65535. + 0.5;
2393
2394   g_value_unset (value);
2395   g_value_init (value, GDK_TYPE_COLOR);
2396   g_value_set_boxed (value, &color);
2397
2398   return TRUE;
2399 }
2400
2401 static gboolean
2402 resolve_gradient (GtkStyleProperties *props,
2403                   GValue             *value)
2404 {
2405   cairo_pattern_t *gradient;
2406
2407   if (!gtk_gradient_resolve (g_value_get_boxed (value), props, &gradient))
2408     return FALSE;
2409
2410   /* Store it back, this is where cairo_pattern_t caching happens */
2411   g_value_unset (value);
2412   g_value_init (value, CAIRO_GOBJECT_TYPE_PATTERN);
2413   g_value_take_boxed (value, gradient);
2414
2415   return TRUE;
2416 }
2417
2418 static gboolean
2419 resolve_shadow (GtkStyleProperties *props,
2420                 GValue *value)
2421 {
2422   GtkShadow *resolved, *base;
2423
2424   base = g_value_get_boxed (value);
2425
2426   if (base == NULL)
2427     return TRUE;
2428   
2429   if (_gtk_shadow_get_resolved (base))
2430     return TRUE;
2431
2432   resolved = _gtk_shadow_resolve (base, props);
2433   if (resolved == NULL)
2434     return FALSE;
2435
2436   g_value_take_boxed (value, resolved);
2437
2438   return TRUE;
2439 }
2440
2441 void
2442 _gtk_style_property_resolve (const GtkStyleProperty *property,
2443                              GtkStyleProperties     *props,
2444                              GtkStateFlags           state,
2445                              GValue                 *val)
2446 {
2447   if (G_VALUE_TYPE (val) == GTK_TYPE_SYMBOLIC_COLOR)
2448     {
2449       if (property->pspec->value_type == GDK_TYPE_RGBA)
2450         {
2451           if (resolve_color (props, val))
2452             return;
2453         }
2454       else if (property->pspec->value_type == GDK_TYPE_COLOR)
2455         {
2456           if (resolve_color_rgb (props, val))
2457             return;
2458         }
2459       
2460       g_value_unset (val);
2461       g_value_init (val, property->pspec->value_type);
2462       _gtk_style_property_default_value (property, props, state, val);
2463     }
2464   else if (G_VALUE_TYPE (val) == GDK_TYPE_RGBA)
2465     {
2466       if (g_value_get_boxed (val) == NULL)
2467         _gtk_style_property_default_value (property, props, state, val);
2468     }
2469   else if (G_VALUE_TYPE (val) == GTK_TYPE_GRADIENT)
2470     {
2471       g_return_if_fail (property->pspec->value_type == CAIRO_GOBJECT_TYPE_PATTERN);
2472
2473       if (!resolve_gradient (props, val))
2474         {
2475           g_value_unset (val);
2476           g_value_init (val, CAIRO_GOBJECT_TYPE_PATTERN);
2477           _gtk_style_property_default_value (property, props, state, val);
2478         }
2479     }
2480   else if (G_VALUE_TYPE (val) == GTK_TYPE_SHADOW)
2481     {
2482       if (!resolve_shadow (props, val))
2483         _gtk_style_property_default_value (property, props, state, val);
2484     }
2485 }
2486
2487 gboolean
2488 _gtk_style_property_is_shorthand  (const GtkStyleProperty *property)
2489 {
2490   g_return_val_if_fail (property != NULL, FALSE);
2491
2492   return property->pack_func != NULL;
2493 }
2494
2495 GParameter *
2496 _gtk_style_property_unpack (const GtkStyleProperty *property,
2497                             const GValue           *value,
2498                             guint                  *n_params)
2499 {
2500   g_return_val_if_fail (property != NULL, NULL);
2501   g_return_val_if_fail (property->unpack_func != NULL, NULL);
2502   g_return_val_if_fail (value != NULL, NULL);
2503   g_return_val_if_fail (n_params != NULL, NULL);
2504
2505   return property->unpack_func (value, n_params);
2506 }
2507
2508 void
2509 _gtk_style_property_pack (const GtkStyleProperty *property,
2510                           GtkStyleProperties     *props,
2511                           GtkStateFlags           state,
2512                           GValue                 *value)
2513 {
2514   g_return_if_fail (property != NULL);
2515   g_return_if_fail (property->pack_func != NULL);
2516   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
2517   g_return_if_fail (G_IS_VALUE (value));
2518
2519   property->pack_func (value, props, state);
2520 }
2521
2522 static void
2523 gtk_style_property_init (void)
2524 {
2525   if (G_LIKELY (properties))
2526     return;
2527
2528   /* stuff is never freed, so no need for free functions */
2529   properties = g_hash_table_new (g_str_hash, g_str_equal);
2530
2531   /* note that gtk_style_properties_register_property() calls this function,
2532    * so make sure we're sanely inited to avoid infloops */
2533
2534   _gtk_style_property_register           (g_param_spec_boxed ("color",
2535                                           "Foreground color",
2536                                           "Foreground color",
2537                                           GDK_TYPE_RGBA, 0),
2538                                           GTK_STYLE_PROPERTY_INHERIT,
2539                                           NULL,
2540                                           NULL,
2541                                           NULL,
2542                                           NULL,
2543                                           NULL,
2544                                           NULL,
2545                                           NULL);
2546
2547   gtk_style_properties_register_property (NULL,
2548                                           g_param_spec_boxed ("background-color",
2549                                                               "Background color",
2550                                                               "Background color",
2551                                                               GDK_TYPE_RGBA, 0));
2552
2553   _gtk_style_property_register           (g_param_spec_boxed ("font-family",
2554                                                               "Font family",
2555                                                               "Font family",
2556                                                               G_TYPE_STRV, 0),
2557                                           GTK_STYLE_PROPERTY_INHERIT,
2558                                           NULL,
2559                                           NULL,
2560                                           NULL,
2561                                           font_family_parse,
2562                                           font_family_value_print,
2563                                           NULL,
2564                                           NULL);
2565   _gtk_style_property_register           (g_param_spec_enum ("font-style",
2566                                                              "Font style",
2567                                                              "Font style",
2568                                                              PANGO_TYPE_STYLE,
2569                                                              PANGO_STYLE_NORMAL, 0),
2570                                           GTK_STYLE_PROPERTY_INHERIT,
2571                                           NULL,
2572                                           NULL,
2573                                           NULL,
2574                                           NULL,
2575                                           NULL,
2576                                           NULL,
2577                                           NULL);
2578   _gtk_style_property_register           (g_param_spec_enum ("font-variant",
2579                                                              "Font variant",
2580                                                              "Font variant",
2581                                                              PANGO_TYPE_VARIANT,
2582                                                              PANGO_VARIANT_NORMAL, 0),
2583                                           GTK_STYLE_PROPERTY_INHERIT,
2584                                           NULL,
2585                                           NULL,
2586                                           NULL,
2587                                           NULL,
2588                                           NULL,
2589                                           NULL,
2590                                           NULL);
2591   /* xxx: need to parse this properly, ie parse the numbers */
2592   _gtk_style_property_register           (g_param_spec_enum ("font-weight",
2593                                                              "Font weight",
2594                                                              "Font weight",
2595                                                              PANGO_TYPE_WEIGHT,
2596                                                              PANGO_WEIGHT_NORMAL, 0),
2597                                           GTK_STYLE_PROPERTY_INHERIT,
2598                                           NULL,
2599                                           NULL,
2600                                           NULL,
2601                                           NULL,
2602                                           NULL,
2603                                           NULL,
2604                                           NULL);
2605   _gtk_style_property_register           (g_param_spec_double ("font-size",
2606                                                                "Font size",
2607                                                                "Font size",
2608                                                                0, G_MAXDOUBLE, 0, 0),
2609                                           GTK_STYLE_PROPERTY_INHERIT,
2610                                           NULL,
2611                                           NULL,
2612                                           NULL,
2613                                           NULL,
2614                                           NULL,
2615                                           NULL,
2616                                           NULL);
2617   _gtk_style_property_register           (g_param_spec_boxed ("font",
2618                                                               "Font Description",
2619                                                               "Font Description",
2620                                                               PANGO_TYPE_FONT_DESCRIPTION, 0),
2621                                           GTK_STYLE_PROPERTY_INHERIT,
2622                                           NULL,
2623                                           unpack_font_description,
2624                                           pack_font_description,
2625                                           font_description_value_parse,
2626                                           font_description_value_print,
2627                                           NULL,
2628                                           unset_font_description);
2629
2630   _gtk_style_property_register           (g_param_spec_boxed ("text-shadow",
2631                                                               "Text shadow",
2632                                                               "Text shadow",
2633                                                               GTK_TYPE_SHADOW, 0),
2634                                           GTK_STYLE_PROPERTY_INHERIT,
2635                                           NULL,
2636                                           NULL,
2637                                           NULL,
2638                                           NULL,
2639                                           NULL,
2640                                           NULL,
2641                                           NULL);
2642
2643   _gtk_style_property_register           (g_param_spec_boxed ("icon-shadow",
2644                                                               "Icon shadow",
2645                                                               "Icon shadow",
2646                                                               GTK_TYPE_SHADOW, 0),
2647                                           GTK_STYLE_PROPERTY_INHERIT,
2648                                           NULL,
2649                                           NULL,
2650                                           NULL,
2651                                           NULL,
2652                                           NULL,
2653                                           NULL,
2654                                           NULL);
2655
2656   gtk_style_properties_register_property (NULL,
2657                                           g_param_spec_boxed ("box-shadow",
2658                                                               "Box shadow",
2659                                                               "Box shadow",
2660                                                               GTK_TYPE_SHADOW, 0));
2661   gtk_style_properties_register_property (NULL,
2662                                           g_param_spec_int ("margin-top",
2663                                                             "margin top",
2664                                                             "Margin at top",
2665                                                             0, G_MAXINT, 0, 0));
2666   gtk_style_properties_register_property (NULL,
2667                                           g_param_spec_int ("margin-left",
2668                                                             "margin left",
2669                                                             "Margin at left",
2670                                                             0, G_MAXINT, 0, 0));
2671   gtk_style_properties_register_property (NULL,
2672                                           g_param_spec_int ("margin-bottom",
2673                                                             "margin bottom",
2674                                                             "Margin at bottom",
2675                                                             0, G_MAXINT, 0, 0));
2676   gtk_style_properties_register_property (NULL,
2677                                           g_param_spec_int ("margin-right",
2678                                                             "margin right",
2679                                                             "Margin at right",
2680                                                             0, G_MAXINT, 0, 0));
2681   _gtk_style_property_register           (g_param_spec_boxed ("margin",
2682                                                               "Margin",
2683                                                               "Margin",
2684                                                               GTK_TYPE_BORDER, 0),
2685                                           0,
2686                                           NULL,
2687                                           unpack_margin,
2688                                           pack_margin,
2689                                           NULL,
2690                                           NULL,
2691                                           NULL,
2692                                           unset_margin);
2693   gtk_style_properties_register_property (NULL,
2694                                           g_param_spec_int ("padding-top",
2695                                                             "padding top",
2696                                                             "Padding at top",
2697                                                             0, G_MAXINT, 0, 0));
2698   gtk_style_properties_register_property (NULL,
2699                                           g_param_spec_int ("padding-left",
2700                                                             "padding left",
2701                                                             "Padding at left",
2702                                                             0, G_MAXINT, 0, 0));
2703   gtk_style_properties_register_property (NULL,
2704                                           g_param_spec_int ("padding-bottom",
2705                                                             "padding bottom",
2706                                                             "Padding at bottom",
2707                                                             0, G_MAXINT, 0, 0));
2708   gtk_style_properties_register_property (NULL,
2709                                           g_param_spec_int ("padding-right",
2710                                                             "padding right",
2711                                                             "Padding at right",
2712                                                             0, G_MAXINT, 0, 0));
2713   _gtk_style_property_register           (g_param_spec_boxed ("padding",
2714                                                               "Padding",
2715                                                               "Padding",
2716                                                               GTK_TYPE_BORDER, 0),
2717                                           0,
2718                                           NULL,
2719                                           unpack_padding,
2720                                           pack_padding,
2721                                           NULL,
2722                                           NULL,
2723                                           NULL,
2724                                           unset_padding);
2725   gtk_style_properties_register_property (NULL,
2726                                           g_param_spec_int ("border-top-width",
2727                                                             "border top width",
2728                                                             "Border width at top",
2729                                                             0, G_MAXINT, 0, 0));
2730   gtk_style_properties_register_property (NULL,
2731                                           g_param_spec_int ("border-left-width",
2732                                                             "border left width",
2733                                                             "Border width at left",
2734                                                             0, G_MAXINT, 0, 0));
2735   gtk_style_properties_register_property (NULL,
2736                                           g_param_spec_int ("border-bottom-width",
2737                                                             "border bottom width",
2738                                                             "Border width at bottom",
2739                                                             0, G_MAXINT, 0, 0));
2740   gtk_style_properties_register_property (NULL,
2741                                           g_param_spec_int ("border-right-width",
2742                                                             "border right width",
2743                                                             "Border width at right",
2744                                                             0, G_MAXINT, 0, 0));
2745   _gtk_style_property_register           (g_param_spec_boxed ("border-width",
2746                                                               "Border width",
2747                                                               "Border width, in pixels",
2748                                                               GTK_TYPE_BORDER, 0),
2749                                           0,
2750                                           NULL,
2751                                           unpack_border_width,
2752                                           pack_border_width,
2753                                           NULL,
2754                                           NULL,
2755                                           NULL,
2756                                           unset_border_width);
2757
2758   _gtk_style_property_register           (g_param_spec_boxed ("border-top-left-radius",
2759                                                               "Border top left radius",
2760                                                               "Border radius of top left corner, in pixels",
2761                                                               GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
2762                                           0,
2763                                           NULL,
2764                                           NULL,
2765                                           NULL,
2766                                           border_corner_radius_value_parse,
2767                                           border_corner_radius_value_print,
2768                                           NULL,
2769                                           NULL);
2770   _gtk_style_property_register           (g_param_spec_boxed ("border-top-right-radius",
2771                                                               "Border top right radius",
2772                                                               "Border radius of top right corner, in pixels",
2773                                                               GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
2774                                           0,
2775                                           NULL,
2776                                           NULL,
2777                                           NULL,
2778                                           border_corner_radius_value_parse,
2779                                           border_corner_radius_value_print,
2780                                           NULL,
2781                                           NULL);
2782   _gtk_style_property_register           (g_param_spec_boxed ("border-bottom-right-radius",
2783                                                               "Border bottom right radius",
2784                                                               "Border radius of bottom right corner, in pixels",
2785                                                               GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
2786                                           0,
2787                                           NULL,
2788                                           NULL,
2789                                           NULL,
2790                                           border_corner_radius_value_parse,
2791                                           border_corner_radius_value_print,
2792                                           NULL,
2793                                           NULL);
2794   _gtk_style_property_register           (g_param_spec_boxed ("border-bottom-left-radius",
2795                                                               "Border bottom left radius",
2796                                                               "Border radius of bottom left corner, in pixels",
2797                                                               GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
2798                                           0,
2799                                           NULL,
2800                                           NULL,
2801                                           NULL,
2802                                           border_corner_radius_value_parse,
2803                                           border_corner_radius_value_print,
2804                                           NULL,
2805                                           NULL);
2806   _gtk_style_property_register           (g_param_spec_int ("border-radius",
2807                                                             "Border radius",
2808                                                             "Border radius, in pixels",
2809                                                             0, G_MAXINT, 0, 0),
2810                                           0,
2811                                           NULL,
2812                                           unpack_border_radius,
2813                                           pack_border_radius,
2814                                           border_radius_value_parse,
2815                                           border_radius_value_print,
2816                                           NULL,
2817                                           unset_border_radius);
2818
2819   gtk_style_properties_register_property (NULL,
2820                                           g_param_spec_enum ("border-style",
2821                                                              "Border style",
2822                                                              "Border style",
2823                                                              GTK_TYPE_BORDER_STYLE,
2824                                                              GTK_BORDER_STYLE_NONE, 0));
2825   _gtk_style_property_register           (g_param_spec_boxed ("border-top-color",
2826                                                               "Border top color",
2827                                                               "Border top color",
2828                                                               GDK_TYPE_RGBA, 0),
2829                                           0,
2830                                           NULL,
2831                                           NULL,
2832                                           NULL,
2833                                           border_color_value_parse,
2834                                           NULL,
2835                                           border_color_default_value,
2836                                           NULL);
2837   _gtk_style_property_register           (g_param_spec_boxed ("border-right-color",
2838                                                               "Border right color",
2839                                                               "Border right color",
2840                                                               GDK_TYPE_RGBA, 0),
2841                                           0,
2842                                           NULL,
2843                                           NULL,
2844                                           NULL,
2845                                           border_color_value_parse,
2846                                           NULL,
2847                                           border_color_default_value,
2848                                           NULL);
2849   _gtk_style_property_register           (g_param_spec_boxed ("border-bottom-color",
2850                                                               "Border bottom color",
2851                                                               "Border bottom color",
2852                                                               GDK_TYPE_RGBA, 0),
2853                                           0,
2854                                           NULL,
2855                                           NULL,
2856                                           NULL,
2857                                           border_color_value_parse,
2858                                           NULL,
2859                                           border_color_default_value,
2860                                           NULL);
2861   _gtk_style_property_register           (g_param_spec_boxed ("border-left-color",
2862                                                               "Border left color",
2863                                                               "Border left color",
2864                                                               GDK_TYPE_RGBA, 0),
2865                                           0,
2866                                           NULL,
2867                                           NULL,
2868                                           NULL,
2869                                           border_color_value_parse,
2870                                           NULL,
2871                                           border_color_default_value,
2872                                           NULL);
2873   _gtk_style_property_register           (g_param_spec_boxed ("border-color",
2874                                                               "Border color",
2875                                                               "Border color",
2876                                                               GDK_TYPE_RGBA, 0),
2877                                           0,
2878                                           NULL,
2879                                           unpack_border_color,
2880                                           pack_border_color,
2881                                           border_color_shorthand_value_parse,
2882                                           NULL,
2883                                           NULL,
2884                                           unset_border_color);
2885
2886   gtk_style_properties_register_property (NULL,
2887                                           g_param_spec_boxed ("background-image",
2888                                                               "Background Image",
2889                                                               "Background Image",
2890                                                               CAIRO_GOBJECT_TYPE_PATTERN, 0));
2891   gtk_style_properties_register_property (NULL,
2892                                           g_param_spec_boxed ("border-image-source",
2893                                                               "Border image source",
2894                                                               "Border image source",
2895                                                               CAIRO_GOBJECT_TYPE_PATTERN, 0));
2896   gtk_style_properties_register_property (NULL,
2897                                           g_param_spec_boxed ("border-image-repeat",
2898                                                               "Border image repeat",
2899                                                               "Border image repeat",
2900                                                               GTK_TYPE_CSS_BORDER_IMAGE_REPEAT, 0));
2901   gtk_style_properties_register_property (NULL,
2902                                           g_param_spec_boxed ("border-image-slice",
2903                                                               "Border image slice",
2904                                                               "Border image slice",
2905                                                               GTK_TYPE_BORDER, 0));
2906   _gtk_style_property_register           (g_param_spec_boxed ("border-image-width",
2907                                                               "Border image width",
2908                                                               "Border image width",
2909                                                               GTK_TYPE_BORDER, 0),
2910                                           0,
2911                                           NULL,
2912                                           NULL,
2913                                           NULL,
2914                                           NULL,
2915                                           NULL,
2916                                           border_image_width_default_value,
2917                                           NULL);
2918   _gtk_style_property_register           (g_param_spec_boxed ("border-image",
2919                                                               "Border Image",
2920                                                               "Border Image",
2921                                                               GTK_TYPE_BORDER_IMAGE, 0),
2922                                           0,
2923                                           NULL,
2924                                           _gtk_border_image_unpack,
2925                                           _gtk_border_image_pack,
2926                                           NULL,
2927                                           NULL,
2928                                           NULL,
2929                                           unset_border_image);
2930   gtk_style_properties_register_property (NULL,
2931                                           g_param_spec_object ("engine",
2932                                                                "Theming Engine",
2933                                                                "Theming Engine",
2934                                                                GTK_TYPE_THEMING_ENGINE, 0));
2935   gtk_style_properties_register_property (NULL,
2936                                           g_param_spec_boxed ("transition",
2937                                                               "Transition animation description",
2938                                                               "Transition animation description",
2939                                                               GTK_TYPE_ANIMATION_DESCRIPTION, 0));
2940
2941   /* Private property holding the binding sets */
2942   _gtk_style_property_register           (g_param_spec_boxed ("gtk-key-bindings",
2943                                                               "Key bindings",
2944                                                               "Key bindings",
2945                                                               G_TYPE_PTR_ARRAY, 0),
2946                                           0,
2947                                           NULL,
2948                                           NULL,
2949                                           NULL,
2950                                           bindings_value_parse,
2951                                           bindings_value_print,
2952                                           NULL,
2953                                           NULL);
2954 }
2955
2956 const GtkStyleProperty *
2957 _gtk_style_property_lookup (const char *name)
2958 {
2959   gtk_style_property_init ();
2960
2961   return g_hash_table_lookup (properties, name);
2962 }
2963
2964 void
2965 _gtk_style_property_register (GParamSpec               *pspec,
2966                               GtkStylePropertyFlags     flags,
2967                               GtkStylePropertyParser    property_parse_func,
2968                               GtkStyleUnpackFunc        unpack_func,
2969                               GtkStylePackFunc          pack_func,
2970                               GtkStyleParseFunc         parse_func,
2971                               GtkStylePrintFunc         print_func,
2972                               GtkStyleDefaultValueFunc  default_value_func,
2973                               GtkStyleUnsetFunc         unset_func)
2974 {
2975   const GtkStyleProperty *existing;
2976   GtkStyleProperty *node;
2977
2978   g_return_if_fail ((pack_func == NULL) == (unpack_func == NULL));
2979
2980   gtk_style_property_init ();
2981
2982   existing = _gtk_style_property_lookup (pspec->name);
2983   if (existing != NULL)
2984     {
2985       g_warning ("Property \"%s\" was already registered with type %s",
2986                  pspec->name, g_type_name (existing->pspec->value_type));
2987       return;
2988     }
2989
2990   node = g_slice_new0 (GtkStyleProperty);
2991   node->flags = flags;
2992   node->pspec = pspec;
2993   node->property_parse_func = property_parse_func;
2994   node->pack_func = pack_func;
2995   node->unpack_func = unpack_func;
2996   node->parse_func = parse_func;
2997   node->print_func = print_func;
2998   node->default_value_func = default_value_func;
2999   node->unset_func = unset_func;
3000
3001   g_hash_table_insert (properties, pspec->name, node);
3002 }