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