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