]> Pileus Git - ~andy/gtk/blob - gtk/gtkstyleproperty.c
styleproperty: unconstify
[~andy/gtk] / gtk / gtkstyleproperty.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "config.h"
21
22 #include "gtkstylepropertyprivate.h"
23
24 #include <errno.h>
25 #include <math.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include <gdk-pixbuf/gdk-pixbuf.h>
30 #include <cairo-gobject.h>
31
32 #include "gtkcssprovider.h"
33 #include "gtkcssparserprivate.h"
34 #include "gtkcssshorthandpropertyprivate.h"
35 #include "gtkcssstylepropertyprivate.h"
36 #include "gtkcsstypesprivate.h"
37 #include "gtkintl.h"
38 #include "gtkprivatetypebuiltins.h"
39 #include "gtkstylepropertiesprivate.h"
40
41 /* the actual parsers we have */
42 #include "gtkanimationdescription.h"
43 #include "gtkbindings.h"
44 #include "gtkgradient.h"
45 #include "gtkshadowprivate.h"
46 #include "gtkthemingengine.h"
47 #include "gtktypebuiltins.h"
48 #include "gtkwin32themeprivate.h"
49
50 /* this is in case round() is not provided by the compiler, 
51  * such as in the case of C89 compilers, like MSVC
52  */
53 #include "fallback-c89.c"
54
55 enum {
56   PROP_0,
57   PROP_NAME
58 };
59
60 static GHashTable *parse_funcs = NULL;
61 static GHashTable *print_funcs = NULL;
62 static GPtrArray *__style_property_array = NULL;
63
64 G_DEFINE_ABSTRACT_TYPE (GtkStyleProperty, _gtk_style_property, G_TYPE_OBJECT)
65
66 static void
67 gtk_style_property_finalize (GObject *object)
68 {
69   GtkStyleProperty *property = GTK_STYLE_PROPERTY (object);
70
71   g_warning ("finalizing %s `%s', how could this happen?", G_OBJECT_TYPE_NAME (object), property->name);
72
73   G_OBJECT_CLASS (_gtk_style_property_parent_class)->finalize (object);
74 }
75
76 static void
77 gtk_style_property_set_property (GObject      *object,
78                                  guint         prop_id,
79                                  const GValue *value,
80                                  GParamSpec   *pspec)
81 {
82   GtkStyleProperty *property = GTK_STYLE_PROPERTY (object);
83   GtkStylePropertyClass *klass = GTK_STYLE_PROPERTY_GET_CLASS (property);
84
85   switch (prop_id)
86     {
87     case PROP_NAME:
88       property->name = g_value_dup_string (value);
89       g_assert (property->name);
90       g_assert (g_hash_table_lookup (klass->properties, property->name) == NULL);
91       g_hash_table_insert (klass->properties, property->name, property);
92       break;
93     default:
94       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
95       break;
96     }
97 }
98
99 static void
100 gtk_style_property_get_property (GObject    *object,
101                                  guint       prop_id,
102                                  GValue     *value,
103                                  GParamSpec *pspec)
104 {
105   GtkStyleProperty *property = GTK_STYLE_PROPERTY (object);
106
107   switch (prop_id)
108     {
109     case PROP_NAME:
110       g_value_set_string (value, property->name);
111       break;
112     default:
113       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
114       break;
115     }
116 }
117
118 static void
119 _gtk_style_property_class_init (GtkStylePropertyClass *klass)
120 {
121   GObjectClass *object_class = G_OBJECT_CLASS (klass);
122
123   object_class->finalize = gtk_style_property_finalize;
124   object_class->set_property = gtk_style_property_set_property;
125   object_class->get_property = gtk_style_property_get_property;
126
127   g_object_class_install_property (object_class,
128                                    PROP_NAME,
129                                    g_param_spec_string ("name",
130                                                         P_("Property name"),
131                                                         P_("The name of the property"),
132                                                         NULL,
133                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
134
135   klass->properties = g_hash_table_new (g_str_hash, g_str_equal);
136 }
137
138 static void
139 _gtk_style_property_init (GtkStyleProperty *property)
140 {
141 }
142
143 static void
144 register_conversion_function (GType             type,
145                               GtkStyleParseFunc parse,
146                               GtkStylePrintFunc print)
147 {
148   if (parse)
149     g_hash_table_insert (parse_funcs, GSIZE_TO_POINTER (type), parse);
150   if (print)
151     g_hash_table_insert (print_funcs, GSIZE_TO_POINTER (type), print);
152 }
153
154 static void
155 string_append_double (GString *string,
156                       double   d)
157 {
158   char buf[G_ASCII_DTOSTR_BUF_SIZE];
159
160   g_ascii_dtostr (buf, sizeof (buf), d);
161   g_string_append (string, buf);
162 }
163
164 static void
165 string_append_string (GString    *str,
166                       const char *string)
167 {
168   gsize len;
169
170   g_string_append_c (str, '"');
171
172   do {
173     len = strcspn (string, "\"\n\r\f");
174     g_string_append (str, string);
175     string += len;
176     switch (*string)
177       {
178       case '\0':
179         break;
180       case '\n':
181         g_string_append (str, "\\A ");
182         break;
183       case '\r':
184         g_string_append (str, "\\D ");
185         break;
186       case '\f':
187         g_string_append (str, "\\C ");
188         break;
189       case '\"':
190         g_string_append (str, "\\\"");
191         break;
192       default:
193         g_assert_not_reached ();
194         break;
195       }
196   } while (*string);
197
198   g_string_append_c (str, '"');
199 }
200
201 /*** IMPLEMENTATIONS ***/
202
203 static gboolean 
204 enum_parse (GtkCssParser *parser,
205             GType         type,
206             int          *res)
207 {
208   char *str;
209
210   if (_gtk_css_parser_try_enum (parser, type, res))
211     return TRUE;
212
213   str = _gtk_css_parser_try_ident (parser, TRUE);
214   if (str == NULL)
215     {
216       _gtk_css_parser_error (parser, "Expected an identifier");
217       return FALSE;
218     }
219
220   _gtk_css_parser_error (parser,
221                          "Unknown value '%s' for enum type '%s'",
222                          str, g_type_name (type));
223   g_free (str);
224
225   return FALSE;
226 }
227
228 static void
229 enum_print (int         value,
230             GType       type,
231             GString    *string)
232 {
233   GEnumClass *enum_class;
234   GEnumValue *enum_value;
235
236   enum_class = g_type_class_ref (type);
237   enum_value = g_enum_get_value (enum_class, value);
238
239   g_string_append (string, enum_value->value_nick);
240
241   g_type_class_unref (enum_class);
242 }
243
244 static gboolean
245 rgba_value_parse (GtkCssParser *parser,
246                   GFile        *base,
247                   GValue       *value)
248 {
249   GtkSymbolicColor *symbolic;
250   GdkRGBA rgba;
251
252   symbolic = _gtk_css_parser_read_symbolic_color (parser);
253   if (symbolic == NULL)
254     return FALSE;
255
256   if (gtk_symbolic_color_resolve (symbolic, NULL, &rgba))
257     {
258       g_value_set_boxed (value, &rgba);
259       gtk_symbolic_color_unref (symbolic);
260     }
261   else
262     {
263       g_value_unset (value);
264       g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
265       g_value_take_boxed (value, symbolic);
266     }
267
268   return TRUE;
269 }
270
271 static void
272 rgba_value_print (const GValue *value,
273                   GString      *string)
274 {
275   const GdkRGBA *rgba = g_value_get_boxed (value);
276
277   if (rgba == NULL)
278     g_string_append (string, "none");
279   else
280     {
281       char *s = gdk_rgba_to_string (rgba);
282       g_string_append (string, s);
283       g_free (s);
284     }
285 }
286
287 static gboolean 
288 color_value_parse (GtkCssParser *parser,
289                    GFile        *base,
290                    GValue       *value)
291 {
292   GtkSymbolicColor *symbolic;
293   GdkRGBA rgba;
294
295   symbolic = _gtk_css_parser_read_symbolic_color (parser);
296   if (symbolic == NULL)
297     return FALSE;
298
299   if (gtk_symbolic_color_resolve (symbolic, NULL, &rgba))
300     {
301       GdkColor color;
302
303       color.red = rgba.red * 65535. + 0.5;
304       color.green = rgba.green * 65535. + 0.5;
305       color.blue = rgba.blue * 65535. + 0.5;
306
307       g_value_set_boxed (value, &color);
308     }
309   else
310     {
311       g_value_unset (value);
312       g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
313       g_value_take_boxed (value, symbolic);
314     }
315
316   return TRUE;
317 }
318
319 static void
320 color_value_print (const GValue *value,
321                    GString      *string)
322 {
323   const GdkColor *color = g_value_get_boxed (value);
324
325   if (color == NULL)
326     g_string_append (string, "none");
327   else
328     {
329       char *s = gdk_color_to_string (color);
330       g_string_append (string, s);
331       g_free (s);
332     }
333 }
334
335 static gboolean
336 symbolic_color_value_parse (GtkCssParser *parser,
337                             GFile        *base,
338                             GValue       *value)
339 {
340   GtkSymbolicColor *symbolic;
341
342   symbolic = _gtk_css_parser_read_symbolic_color (parser);
343   if (symbolic == NULL)
344     return FALSE;
345
346   g_value_take_boxed (value, symbolic);
347   return TRUE;
348 }
349
350 static void
351 symbolic_color_value_print (const GValue *value,
352                             GString      *string)
353 {
354   GtkSymbolicColor *symbolic = g_value_get_boxed (value);
355
356   if (symbolic == NULL)
357     g_string_append (string, "none");
358   else
359     {
360       char *s = gtk_symbolic_color_to_string (symbolic);
361       g_string_append (string, s);
362       g_free (s);
363     }
364 }
365
366 static gboolean
367 font_family_parse (GtkCssParser *parser,
368                    GFile        *base,
369                    GValue       *value)
370 {
371   GPtrArray *names;
372   char *name;
373
374   /* We don't special case generic families. Pango should do
375    * that for us */
376
377   names = g_ptr_array_new ();
378
379   do {
380     name = _gtk_css_parser_try_ident (parser, TRUE);
381     if (name)
382       {
383         GString *string = g_string_new (name);
384         g_free (name);
385         while ((name = _gtk_css_parser_try_ident (parser, TRUE)))
386           {
387             g_string_append_c (string, ' ');
388             g_string_append (string, name);
389             g_free (name);
390           }
391         name = g_string_free (string, FALSE);
392       }
393     else 
394       {
395         name = _gtk_css_parser_read_string (parser);
396         if (name == NULL)
397           {
398             g_ptr_array_free (names, TRUE);
399             return FALSE;
400           }
401       }
402
403     g_ptr_array_add (names, name);
404   } while (_gtk_css_parser_try (parser, ",", TRUE));
405
406   /* NULL-terminate array */
407   g_ptr_array_add (names, NULL);
408   g_value_set_boxed (value, g_ptr_array_free (names, FALSE));
409   return TRUE;
410 }
411
412 static void
413 font_family_value_print (const GValue *value,
414                          GString      *string)
415 {
416   const char **names = g_value_get_boxed (value);
417
418   if (names == NULL || *names == NULL)
419     {
420       g_string_append (string, "none");
421       return;
422     }
423
424   string_append_string (string, *names);
425   names++;
426   while (*names)
427     {
428       g_string_append (string, ", ");
429       string_append_string (string, *names);
430       names++;
431     }
432 }
433
434 static gboolean 
435 font_description_value_parse (GtkCssParser *parser,
436                               GFile        *base,
437                               GValue       *value)
438 {
439   PangoFontDescription *font_desc;
440   guint mask;
441   char *str;
442
443   str = _gtk_css_parser_read_value (parser);
444   if (str == NULL)
445     return FALSE;
446
447   font_desc = pango_font_description_from_string (str);
448   mask = pango_font_description_get_set_fields (font_desc);
449   /* These values are not really correct,
450    * but the fields must be set, so we set them to something */
451   if ((mask & PANGO_FONT_MASK_FAMILY) == 0)
452     pango_font_description_set_family_static (font_desc, "Sans");
453   if ((mask & PANGO_FONT_MASK_SIZE) == 0)
454     pango_font_description_set_size (font_desc, 10 * PANGO_SCALE);
455   g_free (str);
456   g_value_take_boxed (value, font_desc);
457   return TRUE;
458 }
459
460 static void
461 font_description_value_print (const GValue *value,
462                               GString      *string)
463 {
464   const PangoFontDescription *desc = g_value_get_boxed (value);
465
466   if (desc == NULL)
467     g_string_append (string, "none");
468   else
469     {
470       char *s = pango_font_description_to_string (desc);
471       g_string_append (string, s);
472       g_free (s);
473     }
474 }
475
476 static gboolean 
477 boolean_value_parse (GtkCssParser *parser,
478                      GFile        *base,
479                      GValue       *value)
480 {
481   if (_gtk_css_parser_try (parser, "true", TRUE) ||
482       _gtk_css_parser_try (parser, "1", TRUE))
483     {
484       g_value_set_boolean (value, TRUE);
485       return TRUE;
486     }
487   else if (_gtk_css_parser_try (parser, "false", TRUE) ||
488            _gtk_css_parser_try (parser, "0", TRUE))
489     {
490       g_value_set_boolean (value, FALSE);
491       return TRUE;
492     }
493   else
494     {
495       _gtk_css_parser_error (parser, "Expected a boolean value");
496       return FALSE;
497     }
498 }
499
500 static void
501 boolean_value_print (const GValue *value,
502                      GString      *string)
503 {
504   if (g_value_get_boolean (value))
505     g_string_append (string, "true");
506   else
507     g_string_append (string, "false");
508 }
509
510 static gboolean 
511 int_value_parse (GtkCssParser *parser,
512                  GFile        *base,
513                  GValue       *value)
514 {
515   gint i;
516
517   if (_gtk_css_parser_begins_with (parser, '-'))
518     {
519       int res = _gtk_win32_theme_int_parse (parser, base, &i);
520       if (res >= 0)
521         {
522           g_value_set_int (value, i);
523           return res > 0;
524         }
525       /* < 0 => continue */
526     }
527
528   if (!_gtk_css_parser_try_int (parser, &i))
529     {
530       _gtk_css_parser_error (parser, "Expected a valid integer value");
531       return FALSE;
532     }
533
534   g_value_set_int (value, i);
535   return TRUE;
536 }
537
538 static void
539 int_value_print (const GValue *value,
540                  GString      *string)
541 {
542   g_string_append_printf (string, "%d", g_value_get_int (value));
543 }
544
545 static gboolean 
546 uint_value_parse (GtkCssParser *parser,
547                   GFile        *base,
548                   GValue       *value)
549 {
550   guint u;
551
552   if (!_gtk_css_parser_try_uint (parser, &u))
553     {
554       _gtk_css_parser_error (parser, "Expected a valid unsigned value");
555       return FALSE;
556     }
557
558   g_value_set_uint (value, u);
559   return TRUE;
560 }
561
562 static void
563 uint_value_print (const GValue *value,
564                   GString      *string)
565 {
566   g_string_append_printf (string, "%u", g_value_get_uint (value));
567 }
568
569 static gboolean 
570 double_value_parse (GtkCssParser *parser,
571                     GFile        *base,
572                     GValue       *value)
573 {
574   gdouble d;
575
576   if (!_gtk_css_parser_try_double (parser, &d))
577     {
578       _gtk_css_parser_error (parser, "Expected a number");
579       return FALSE;
580     }
581
582   g_value_set_double (value, d);
583   return TRUE;
584 }
585
586 static void
587 double_value_print (const GValue *value,
588                     GString      *string)
589 {
590   string_append_double (string, g_value_get_double (value));
591 }
592
593 static gboolean 
594 float_value_parse (GtkCssParser *parser,
595                    GFile        *base,
596                    GValue       *value)
597 {
598   gdouble d;
599
600   if (!_gtk_css_parser_try_double (parser, &d))
601     {
602       _gtk_css_parser_error (parser, "Expected a number");
603       return FALSE;
604     }
605
606   g_value_set_float (value, d);
607   return TRUE;
608 }
609
610 static void
611 float_value_print (const GValue *value,
612                    GString      *string)
613 {
614   string_append_double (string, g_value_get_float (value));
615 }
616
617 static gboolean 
618 string_value_parse (GtkCssParser *parser,
619                     GFile        *base,
620                     GValue       *value)
621 {
622   char *str = _gtk_css_parser_read_string (parser);
623
624   if (str == NULL)
625     return FALSE;
626
627   g_value_take_string (value, str);
628   return TRUE;
629 }
630
631 static void
632 string_value_print (const GValue *value,
633                     GString      *str)
634 {
635   string_append_string (str, g_value_get_string (value));
636 }
637
638 static gboolean 
639 theming_engine_value_parse (GtkCssParser *parser,
640                             GFile        *base,
641                             GValue       *value)
642 {
643   GtkThemingEngine *engine;
644   char *str;
645
646   if (_gtk_css_parser_try (parser, "none", TRUE))
647     {
648       g_value_set_object (value, gtk_theming_engine_load (NULL));
649       return TRUE;
650     }
651
652   str = _gtk_css_parser_try_ident (parser, TRUE);
653   if (str == NULL)
654     {
655       _gtk_css_parser_error (parser, "Expected a valid theme name");
656       return FALSE;
657     }
658
659   engine = gtk_theming_engine_load (str);
660
661   if (engine == NULL)
662     {
663       _gtk_css_parser_error (parser, "Themeing engine '%s' not found", str);
664       g_free (str);
665       return FALSE;
666     }
667
668   g_value_set_object (value, engine);
669   g_free (str);
670   return TRUE;
671 }
672
673 static void
674 theming_engine_value_print (const GValue *value,
675                             GString      *string)
676 {
677   GtkThemingEngine *engine;
678   char *name;
679
680   engine = g_value_get_object (value);
681   if (engine == NULL)
682     g_string_append (string, "none");
683   else
684     {
685       /* XXX: gtk_theming_engine_get_name()? */
686       g_object_get (engine, "name", &name, NULL);
687       g_string_append (string, name ? name : "none");
688       g_free (name);
689     }
690 }
691
692 static gboolean 
693 animation_description_value_parse (GtkCssParser *parser,
694                                    GFile        *base,
695                                    GValue       *value)
696 {
697   GtkAnimationDescription *desc;
698   char *str;
699
700   str = _gtk_css_parser_read_value (parser);
701   if (str == NULL)
702     return FALSE;
703
704   desc = _gtk_animation_description_from_string (str);
705   g_free (str);
706
707   if (desc == NULL)
708     {
709       _gtk_css_parser_error (parser, "Invalid animation description");
710       return FALSE;
711     }
712   
713   g_value_take_boxed (value, desc);
714   return TRUE;
715 }
716
717 static void
718 animation_description_value_print (const GValue *value,
719                                    GString      *string)
720 {
721   GtkAnimationDescription *desc = g_value_get_boxed (value);
722
723   if (desc == NULL)
724     g_string_append (string, "none");
725   else
726     _gtk_animation_description_print (desc, string);
727 }
728
729 static gboolean 
730 border_value_parse (GtkCssParser *parser,
731                     GFile        *base,
732                     GValue       *value)
733 {
734   GtkBorder border = { 0, };
735   guint i, numbers[4];
736
737   for (i = 0; i < G_N_ELEMENTS (numbers); i++)
738     {
739       if (_gtk_css_parser_begins_with (parser, '-'))
740         {
741           /* These are strictly speaking signed, but we want to be able to use them
742              for unsigned types too, as the actual ranges of values make this safe */
743           int res = _gtk_win32_theme_int_parse (parser, base, (int *)&numbers[i]);
744
745           if (res == 0) /* Parse error, report */
746             return FALSE;
747
748           if (res < 0) /* Nothing known to expand */
749             break;
750         }
751       else
752         {
753           if (!_gtk_css_parser_try_uint (parser, &numbers[i]))
754             break;
755
756           /* XXX: shouldn't allow spaces here? */
757           _gtk_css_parser_try (parser, "px", TRUE);
758         }
759     }
760
761   if (i == 0)
762     {
763       _gtk_css_parser_error (parser, "Expected valid border");
764       return FALSE;
765     }
766
767   border.top = numbers[0];
768   if (i > 1)
769     border.right = numbers[1];
770   else
771     border.right = border.top;
772   if (i > 2)
773     border.bottom = numbers[2];
774   else
775     border.bottom = border.top;
776   if (i > 3)
777     border.left = numbers[3];
778   else
779     border.left = border.right;
780
781   g_value_set_boxed (value, &border);
782   return TRUE;
783 }
784
785 static void
786 border_value_print (const GValue *value, GString *string)
787 {
788   const GtkBorder *border = g_value_get_boxed (value);
789
790   if (border == NULL)
791     g_string_append (string, "none");
792   else if (border->left != border->right)
793     g_string_append_printf (string, "%d %d %d %d", border->top, border->right, border->bottom, border->left);
794   else if (border->top != border->bottom)
795     g_string_append_printf (string, "%d %d %d", border->top, border->right, border->bottom);
796   else if (border->top != border->left)
797     g_string_append_printf (string, "%d %d", border->top, border->right);
798   else
799     g_string_append_printf (string, "%d", border->top);
800 }
801
802 static gboolean 
803 gradient_value_parse (GtkCssParser *parser,
804                       GFile        *base,
805                       GValue       *value)
806 {
807   GtkGradient *gradient;
808   cairo_pattern_type_t type;
809   gdouble coords[6];
810   guint i;
811
812   if (!_gtk_css_parser_try (parser, "-gtk-gradient", TRUE))
813     {
814       _gtk_css_parser_error (parser,
815                              "Expected '-gtk-gradient'");
816       return FALSE;
817     }
818
819   if (!_gtk_css_parser_try (parser, "(", TRUE))
820     {
821       _gtk_css_parser_error (parser,
822                              "Expected '(' after '-gtk-gradient'");
823       return FALSE;
824     }
825
826   /* Parse gradient type */
827   if (_gtk_css_parser_try (parser, "linear", TRUE))
828     type = CAIRO_PATTERN_TYPE_LINEAR;
829   else if (_gtk_css_parser_try (parser, "radial", TRUE))
830     type = CAIRO_PATTERN_TYPE_RADIAL;
831   else
832     {
833       _gtk_css_parser_error (parser,
834                              "Gradient type must be 'radial' or 'linear'");
835       return FALSE;
836     }
837
838   /* Parse start/stop position parameters */
839   for (i = 0; i < 2; i++)
840     {
841       if (! _gtk_css_parser_try (parser, ",", TRUE))
842         {
843           _gtk_css_parser_error (parser,
844                                  "Expected ','");
845           return FALSE;
846         }
847
848       if (_gtk_css_parser_try (parser, "left", TRUE))
849         coords[i * 3] = 0;
850       else if (_gtk_css_parser_try (parser, "right", TRUE))
851         coords[i * 3] = 1;
852       else if (_gtk_css_parser_try (parser, "center", TRUE))
853         coords[i * 3] = 0.5;
854       else if (!_gtk_css_parser_try_double (parser, &coords[i * 3]))
855         {
856           _gtk_css_parser_error (parser,
857                                  "Expected a valid X coordinate");
858           return FALSE;
859         }
860
861       if (_gtk_css_parser_try (parser, "top", TRUE))
862         coords[i * 3 + 1] = 0;
863       else if (_gtk_css_parser_try (parser, "bottom", TRUE))
864         coords[i * 3 + 1] = 1;
865       else if (_gtk_css_parser_try (parser, "center", TRUE))
866         coords[i * 3 + 1] = 0.5;
867       else if (!_gtk_css_parser_try_double (parser, &coords[i * 3 + 1]))
868         {
869           _gtk_css_parser_error (parser,
870                                  "Expected a valid Y coordinate");
871           return FALSE;
872         }
873
874       if (type == CAIRO_PATTERN_TYPE_RADIAL)
875         {
876           /* Parse radius */
877           if (! _gtk_css_parser_try (parser, ",", TRUE))
878             {
879               _gtk_css_parser_error (parser,
880                                      "Expected ','");
881               return FALSE;
882             }
883
884           if (! _gtk_css_parser_try_double (parser, &coords[(i * 3) + 2]))
885             {
886               _gtk_css_parser_error (parser,
887                                      "Expected a numer for the radius");
888               return FALSE;
889             }
890         }
891     }
892
893   if (type == CAIRO_PATTERN_TYPE_LINEAR)
894     gradient = gtk_gradient_new_linear (coords[0], coords[1], coords[3], coords[4]);
895   else
896     gradient = gtk_gradient_new_radial (coords[0], coords[1], coords[2],
897                                         coords[3], coords[4], coords[5]);
898
899   while (_gtk_css_parser_try (parser, ",", TRUE))
900     {
901       GtkSymbolicColor *color;
902       gdouble position;
903
904       if (_gtk_css_parser_try (parser, "from", TRUE))
905         {
906           position = 0;
907
908           if (!_gtk_css_parser_try (parser, "(", TRUE))
909             {
910               gtk_gradient_unref (gradient);
911               _gtk_css_parser_error (parser,
912                                      "Expected '('");
913               return FALSE;
914             }
915
916         }
917       else if (_gtk_css_parser_try (parser, "to", TRUE))
918         {
919           position = 1;
920
921           if (!_gtk_css_parser_try (parser, "(", TRUE))
922             {
923               gtk_gradient_unref (gradient);
924               _gtk_css_parser_error (parser,
925                                      "Expected '('");
926               return FALSE;
927             }
928
929         }
930       else if (_gtk_css_parser_try (parser, "color-stop", TRUE))
931         {
932           if (!_gtk_css_parser_try (parser, "(", TRUE))
933             {
934               gtk_gradient_unref (gradient);
935               _gtk_css_parser_error (parser,
936                                      "Expected '('");
937               return FALSE;
938             }
939
940           if (!_gtk_css_parser_try_double (parser, &position))
941             {
942               gtk_gradient_unref (gradient);
943               _gtk_css_parser_error (parser,
944                                      "Expected a valid number");
945               return FALSE;
946             }
947
948           if (!_gtk_css_parser_try (parser, ",", TRUE))
949             {
950               gtk_gradient_unref (gradient);
951               _gtk_css_parser_error (parser,
952                                      "Expected a comma");
953               return FALSE;
954             }
955         }
956       else
957         {
958           gtk_gradient_unref (gradient);
959           _gtk_css_parser_error (parser,
960                                  "Not a valid color-stop definition");
961           return FALSE;
962         }
963
964       color = _gtk_css_parser_read_symbolic_color (parser);
965       if (color == NULL)
966         {
967           gtk_gradient_unref (gradient);
968           return FALSE;
969         }
970
971       gtk_gradient_add_color_stop (gradient, position, color);
972       gtk_symbolic_color_unref (color);
973
974       if (!_gtk_css_parser_try (parser, ")", TRUE))
975         {
976           gtk_gradient_unref (gradient);
977           _gtk_css_parser_error (parser,
978                                  "Expected ')'");
979           return FALSE;
980         }
981     }
982
983   if (!_gtk_css_parser_try (parser, ")", TRUE))
984     {
985       gtk_gradient_unref (gradient);
986       _gtk_css_parser_error (parser,
987                              "Expected ')'");
988       return FALSE;
989     }
990
991   g_value_take_boxed (value, gradient);
992   return TRUE;
993 }
994
995 static void
996 gradient_value_print (const GValue *value,
997                       GString      *string)
998 {
999   GtkGradient *gradient = g_value_get_boxed (value);
1000
1001   if (gradient == NULL)
1002     g_string_append (string, "none");
1003   else
1004     {
1005       char *s = gtk_gradient_to_string (gradient);
1006       g_string_append (string, s);
1007       g_free (s);
1008     }
1009 }
1010
1011 static GFile *
1012 gtk_css_parse_url (GtkCssParser *parser,
1013                    GFile        *base)
1014 {
1015   gchar *path;
1016   GFile *file;
1017
1018   if (_gtk_css_parser_try (parser, "url", FALSE))
1019     {
1020       if (!_gtk_css_parser_try (parser, "(", TRUE))
1021         {
1022           _gtk_css_parser_skip_whitespace (parser);
1023           if (_gtk_css_parser_try (parser, "(", TRUE))
1024             {
1025               GError *error;
1026               
1027               error = g_error_new_literal (GTK_CSS_PROVIDER_ERROR,
1028                                            GTK_CSS_PROVIDER_ERROR_DEPRECATED,
1029                                            "Whitespace between 'url' and '(' is deprecated");
1030                              
1031               _gtk_css_parser_take_error (parser, error);
1032             }
1033           else
1034             {
1035               _gtk_css_parser_error (parser, "Expected '(' after 'url'");
1036               return NULL;
1037             }
1038         }
1039
1040       path = _gtk_css_parser_read_string (parser);
1041       if (path == NULL)
1042         return NULL;
1043
1044       if (!_gtk_css_parser_try (parser, ")", TRUE))
1045         {
1046           _gtk_css_parser_error (parser, "No closing ')' found for 'url'");
1047           g_free (path);
1048           return NULL;
1049         }
1050     }
1051   else
1052     {
1053       path = _gtk_css_parser_try_name (parser, TRUE);
1054       if (path == NULL)
1055         {
1056           _gtk_css_parser_error (parser, "Not a valid url");
1057           return NULL;
1058         }
1059     }
1060
1061   file = g_file_resolve_relative_path (base, path);
1062   g_free (path);
1063
1064   return file;
1065 }
1066
1067 static gboolean 
1068 pattern_value_parse (GtkCssParser *parser,
1069                      GFile        *base,
1070                      GValue       *value)
1071 {
1072   if (_gtk_css_parser_try (parser, "none", TRUE))
1073     {
1074       /* nothing to do here */
1075     }
1076   else if (_gtk_css_parser_begins_with (parser, '-'))
1077     {
1078       int res;
1079       res = _gtk_win32_theme_part_parse (parser, base, value);
1080       if (res >= 0)
1081         return res > 0;
1082       /* < 0 => continue */
1083       g_value_unset (value);
1084       g_value_init (value, GTK_TYPE_GRADIENT);
1085       return gradient_value_parse (parser, base, value);
1086     }
1087   else
1088     {
1089       GError *error = NULL;
1090       gchar *path;
1091       GdkPixbuf *pixbuf;
1092       GFile *file;
1093       cairo_surface_t *surface;
1094       cairo_pattern_t *pattern;
1095       cairo_t *cr;
1096       cairo_matrix_t matrix;
1097
1098       file = gtk_css_parse_url (parser, base);
1099       if (file == NULL)
1100         return FALSE;
1101
1102       path = g_file_get_path (file);
1103       g_object_unref (file);
1104
1105       pixbuf = gdk_pixbuf_new_from_file (path, &error);
1106       g_free (path);
1107       if (pixbuf == NULL)
1108         {
1109           _gtk_css_parser_take_error (parser, error);
1110           return FALSE;
1111         }
1112
1113       surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
1114                                             gdk_pixbuf_get_width (pixbuf),
1115                                             gdk_pixbuf_get_height (pixbuf));
1116       cr = cairo_create (surface);
1117       gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
1118       cairo_paint (cr);
1119       pattern = cairo_pattern_create_for_surface (surface);
1120
1121       cairo_matrix_init_scale (&matrix,
1122                                gdk_pixbuf_get_width (pixbuf),
1123                                gdk_pixbuf_get_height (pixbuf));
1124       cairo_pattern_set_matrix (pattern, &matrix);
1125
1126       cairo_surface_destroy (surface);
1127       cairo_destroy (cr);
1128       g_object_unref (pixbuf);
1129
1130       g_value_take_boxed (value, pattern);
1131     }
1132   
1133   return TRUE;
1134 }
1135
1136 static cairo_status_t
1137 surface_write (void                *closure,
1138                const unsigned char *data,
1139                unsigned int         length)
1140 {
1141   g_byte_array_append (closure, data, length);
1142
1143   return CAIRO_STATUS_SUCCESS;
1144 }
1145
1146 static void
1147 surface_print (cairo_surface_t *surface,
1148                GString *        string)
1149 {
1150 #if CAIRO_HAS_PNG_FUNCTIONS
1151   GByteArray *array;
1152   char *base64;
1153   
1154   array = g_byte_array_new ();
1155   cairo_surface_write_to_png_stream (surface, surface_write, array);
1156   base64 = g_base64_encode (array->data, array->len);
1157   g_byte_array_free (array, TRUE);
1158
1159   g_string_append (string, "url(\"data:image/png;base64,");
1160   g_string_append (string, base64);
1161   g_string_append (string, "\")");
1162
1163   g_free (base64);
1164 #else
1165   g_string_append (string, "none /* you need cairo png functions enabled to make this work */");
1166 #endif
1167 }
1168
1169 static void
1170 pattern_value_print (const GValue *value,
1171                      GString      *string)
1172 {
1173   cairo_pattern_t *pattern;
1174   cairo_surface_t *surface;
1175
1176   pattern = g_value_get_boxed (value);
1177
1178   if (pattern == NULL)
1179     {
1180       g_string_append (string, "none");
1181       return;
1182     }
1183
1184   switch (cairo_pattern_get_type (pattern))
1185     {
1186     case CAIRO_PATTERN_TYPE_SURFACE:
1187       if (cairo_pattern_get_surface (pattern, &surface) != CAIRO_STATUS_SUCCESS)
1188         {
1189           g_assert_not_reached ();
1190         }
1191       surface_print (surface, string);
1192       break;
1193     case CAIRO_PATTERN_TYPE_SOLID:
1194     case CAIRO_PATTERN_TYPE_LINEAR:
1195     case CAIRO_PATTERN_TYPE_RADIAL:
1196     default:
1197       g_assert_not_reached ();
1198       break;
1199     }
1200 }
1201
1202 static gboolean
1203 shadow_value_parse (GtkCssParser *parser,
1204                     GFile *base,
1205                     GValue *value)
1206 {
1207   gboolean have_inset, have_color, have_lengths;
1208   gdouble hoffset, voffset, blur, spread;
1209   GtkSymbolicColor *color;
1210   GtkShadow *shadow;
1211   guint i;
1212
1213   shadow = _gtk_shadow_new ();
1214
1215   if (_gtk_css_parser_try (parser, "none", TRUE))
1216     return TRUE;
1217
1218   do
1219     {
1220       have_inset = have_lengths = have_color = FALSE;
1221
1222       for (i = 0; i < 3; i++)
1223         {
1224           if (!have_inset && 
1225               _gtk_css_parser_try (parser, "inset", TRUE))
1226             {
1227               have_inset = TRUE;
1228               continue;
1229             }
1230             
1231           if (!have_lengths &&
1232               _gtk_css_parser_try_double (parser, &hoffset))
1233             {
1234               have_lengths = TRUE;
1235
1236               if (!_gtk_css_parser_try_double (parser, &voffset))
1237                 {
1238                   _gtk_css_parser_error (parser, "Horizontal and vertical offsets are required");
1239                   _gtk_shadow_unref (shadow);
1240                   return FALSE;
1241                 }
1242
1243               if (!_gtk_css_parser_try_double (parser, &blur))
1244                 blur = 0;
1245
1246               if (!_gtk_css_parser_try_double (parser, &spread))
1247                 spread = 0;
1248
1249               continue;
1250             }
1251
1252           if (!have_color)
1253             {
1254               have_color = TRUE;
1255
1256               /* XXX: the color is optional and UA-defined if it's missing,
1257                * but it doesn't really make sense for us...
1258                */
1259               color = _gtk_css_parser_read_symbolic_color (parser);
1260
1261               if (color == NULL)
1262                 {
1263                   _gtk_shadow_unref (shadow);
1264                   return FALSE;
1265                 }
1266             }
1267         }
1268
1269       if (!have_color || !have_lengths)
1270         {
1271           _gtk_css_parser_error (parser, "Must specify at least color and offsets");
1272           _gtk_shadow_unref (shadow);
1273           return FALSE;
1274         }
1275
1276       _gtk_shadow_append (shadow,
1277                           hoffset, voffset,
1278                           blur, spread,
1279                           have_inset, color);
1280
1281       gtk_symbolic_color_unref (color);
1282
1283     }
1284   while (_gtk_css_parser_try (parser, ",", TRUE));
1285
1286   g_value_take_boxed (value, shadow);
1287   return TRUE;
1288 }
1289
1290 static void
1291 shadow_value_print (const GValue *value,
1292                     GString      *string)
1293 {
1294   GtkShadow *shadow;
1295
1296   shadow = g_value_get_boxed (value);
1297
1298   if (shadow == NULL)
1299     g_string_append (string, "none");
1300   else
1301     _gtk_shadow_print (shadow, string);
1302 }
1303
1304 static gboolean
1305 background_repeat_value_parse (GtkCssParser *parser,
1306                                GFile *file,
1307                                GValue *value)
1308 {
1309   GtkCssBackgroundRepeat repeat;
1310   int style;
1311
1312   if (!enum_parse (parser, GTK_TYPE_CSS_BACKGROUND_REPEAT_STYLE, &style))
1313     return FALSE;
1314
1315   repeat.repeat = style;
1316
1317   g_value_set_boxed (value, &repeat);
1318
1319   return TRUE;
1320 }
1321
1322 static void
1323 background_repeat_value_print (const GValue *value,
1324                                GString      *string)
1325 {
1326   GtkCssBackgroundRepeat *repeat;
1327
1328   repeat = g_value_get_boxed (value);
1329
1330   enum_print (repeat->repeat, GTK_TYPE_CSS_BACKGROUND_REPEAT_STYLE, string);
1331 }
1332
1333 static gboolean
1334 border_image_repeat_value_parse (GtkCssParser *parser,
1335                                  GFile *file,
1336                                  GValue *value)
1337 {
1338   GtkCssBorderImageRepeat image_repeat;
1339   GtkCssBorderRepeatStyle styles[2];
1340   gint i, v;
1341
1342   for (i = 0; i < 2; i++)
1343     {
1344       if (_gtk_css_parser_try_enum (parser, GTK_TYPE_CSS_BORDER_REPEAT_STYLE, &v))
1345         styles[i] = v;
1346       else if (i == 0)
1347         {
1348           styles[1] = styles[0] = GTK_CSS_REPEAT_STYLE_STRETCH;
1349           break;
1350         }
1351       else
1352         styles[i] = styles[0];
1353     }
1354
1355   image_repeat.hrepeat = styles[0];
1356   image_repeat.vrepeat = styles[1];
1357
1358   g_value_set_boxed (value, &image_repeat);
1359
1360   return TRUE;
1361 }
1362
1363 static void
1364 border_image_repeat_value_print (const GValue *value,
1365                                  GString      *string)
1366 {
1367   GtkCssBorderImageRepeat *image_repeat;
1368
1369   image_repeat = g_value_get_boxed (value);
1370
1371   enum_print (image_repeat->hrepeat, GTK_TYPE_CSS_BORDER_REPEAT_STYLE, string);
1372   if (image_repeat->hrepeat != image_repeat->vrepeat)
1373     {
1374       g_string_append (string, " ");
1375       enum_print (image_repeat->vrepeat, GTK_TYPE_CSS_BORDER_REPEAT_STYLE, string);
1376     }
1377 }
1378
1379 static gboolean 
1380 enum_value_parse (GtkCssParser *parser,
1381                   GFile        *base,
1382                   GValue       *value)
1383 {
1384   int v;
1385
1386   if (enum_parse (parser, G_VALUE_TYPE (value), &v))
1387     {
1388       g_value_set_enum (value, v);
1389       return TRUE;
1390     }
1391
1392   return FALSE;
1393 }
1394
1395 static void
1396 enum_value_print (const GValue *value,
1397                   GString      *string)
1398 {
1399   enum_print (g_value_get_enum (value), G_VALUE_TYPE (value), string);
1400 }
1401
1402 static gboolean 
1403 flags_value_parse (GtkCssParser *parser,
1404                    GFile        *base,
1405                    GValue       *value)
1406 {
1407   GFlagsClass *flags_class;
1408   GFlagsValue *flag_value;
1409   guint flags = 0;
1410   char *str;
1411
1412   flags_class = g_type_class_ref (G_VALUE_TYPE (value));
1413
1414   do {
1415     str = _gtk_css_parser_try_ident (parser, TRUE);
1416     if (str == NULL)
1417       {
1418         _gtk_css_parser_error (parser, "Expected an identifier");
1419         g_type_class_unref (flags_class);
1420         return FALSE;
1421       }
1422
1423       flag_value = g_flags_get_value_by_nick (flags_class, str);
1424       if (!flag_value)
1425         {
1426           _gtk_css_parser_error (parser,
1427                                  "Unknown flag value '%s' for type '%s'",
1428                                  str, g_type_name (G_VALUE_TYPE (value)));
1429           /* XXX Do we want to return FALSE here? We can get
1430            * forward-compatibility for new values this way
1431            */
1432           g_free (str);
1433           g_type_class_unref (flags_class);
1434           return FALSE;
1435         }
1436
1437       g_free (str);
1438     }
1439   while (_gtk_css_parser_try (parser, ",", FALSE));
1440
1441   g_type_class_unref (flags_class);
1442
1443   g_value_set_enum (value, flags);
1444
1445   return TRUE;
1446 }
1447
1448 static void
1449 flags_value_print (const GValue *value,
1450                    GString      *string)
1451 {
1452   GFlagsClass *flags_class;
1453   guint i, flags;
1454
1455   flags_class = g_type_class_ref (G_VALUE_TYPE (value));
1456   flags = g_value_get_flags (value);
1457
1458   for (i = 0; i < flags_class->n_values; i++)
1459     {
1460       GFlagsValue *flags_value = &flags_class->values[i];
1461
1462       if (flags & flags_value->value)
1463         {
1464           if (string->len != 0)
1465             g_string_append (string, ", ");
1466
1467           g_string_append (string, flags_value->value_nick);
1468         }
1469     }
1470
1471   g_type_class_unref (flags_class);
1472 }
1473
1474 static gboolean 
1475 bindings_value_parse (GtkCssParser *parser,
1476                       GFile        *base,
1477                       GValue       *value)
1478 {
1479   GPtrArray *array;
1480   GtkBindingSet *binding_set;
1481   char *name;
1482
1483   array = g_ptr_array_new ();
1484
1485   do {
1486       name = _gtk_css_parser_try_ident (parser, TRUE);
1487       if (name == NULL)
1488         {
1489           _gtk_css_parser_error (parser, "Not a valid binding name");
1490           g_ptr_array_free (array, TRUE);
1491           return FALSE;
1492         }
1493
1494       binding_set = gtk_binding_set_find (name);
1495
1496       if (!binding_set)
1497         {
1498           _gtk_css_parser_error (parser, "No binding set named '%s'", name);
1499           g_free (name);
1500           continue;
1501         }
1502
1503       g_ptr_array_add (array, binding_set);
1504       g_free (name);
1505     }
1506   while (_gtk_css_parser_try (parser, ",", TRUE));
1507
1508   g_value_take_boxed (value, array);
1509
1510   return TRUE;
1511 }
1512
1513 static void
1514 bindings_value_print (const GValue *value,
1515                       GString      *string)
1516 {
1517   GPtrArray *array;
1518   guint i;
1519
1520   array = g_value_get_boxed (value);
1521
1522   for (i = 0; i < array->len; i++)
1523     {
1524       GtkBindingSet *binding_set = g_ptr_array_index (array, i);
1525
1526       if (i > 0)
1527         g_string_append (string, ", ");
1528       g_string_append (string, binding_set->set_name);
1529     }
1530 }
1531
1532 static gboolean 
1533 border_corner_radius_value_parse (GtkCssParser *parser,
1534                                   GFile        *base,
1535                                   GValue       *value)
1536 {
1537   GtkCssBorderCornerRadius corner;
1538
1539   if (!_gtk_css_parser_try_double (parser, &corner.horizontal))
1540     {
1541       _gtk_css_parser_error (parser, "Expected a number");
1542       return FALSE;
1543     }
1544   else if (corner.horizontal < 0)
1545     goto negative;
1546
1547   if (!_gtk_css_parser_try_double (parser, &corner.vertical))
1548     corner.vertical = corner.horizontal;
1549   else if (corner.vertical < 0)
1550     goto negative;
1551
1552   g_value_set_boxed (value, &corner);
1553   return TRUE;
1554
1555 negative:
1556   _gtk_css_parser_error (parser, "Border radius values cannot be negative");
1557   return FALSE;
1558 }
1559
1560 static void
1561 border_corner_radius_value_print (const GValue *value,
1562                                   GString      *string)
1563 {
1564   GtkCssBorderCornerRadius *corner;
1565
1566   corner = g_value_get_boxed (value);
1567
1568   if (corner == NULL)
1569     {
1570       g_string_append (string, "none");
1571       return;
1572     }
1573
1574   string_append_double (string, corner->horizontal);
1575   if (corner->horizontal != corner->vertical)
1576     {
1577       g_string_append_c (string, ' ');
1578       string_append_double (string, corner->vertical);
1579     }
1580 }
1581
1582 static gboolean 
1583 transparent_color_value_parse (GtkCssParser *parser,
1584                                GFile        *base,
1585                                GValue       *value)
1586 {
1587   if (_gtk_css_parser_try (parser, "transparent", TRUE))
1588     {
1589       GdkRGBA transparent = { 0, 0, 0, 0 };
1590           
1591       g_value_set_boxed (value, &transparent);
1592
1593       return TRUE;
1594     }
1595
1596   return rgba_value_parse (parser, base, value);
1597 }
1598
1599 /*** API ***/
1600
1601 guint
1602 _gtk_style_property_get_count (void)
1603 {
1604   return __style_property_array ? __style_property_array->len : 0;
1605 }
1606
1607 GtkStyleProperty *
1608 _gtk_style_property_get (guint id)
1609 {
1610   g_assert (__style_property_array);
1611   
1612   return g_ptr_array_index (__style_property_array, id);
1613 }
1614
1615 static void
1616 _gtk_style_property_generate_id (GtkStyleProperty *node)
1617 {
1618   if (__style_property_array == NULL)
1619     __style_property_array = g_ptr_array_new ();
1620
1621   node->id = __style_property_array->len;
1622   g_ptr_array_add (__style_property_array, node);
1623 }
1624
1625 static void
1626 css_string_funcs_init (void)
1627 {
1628   if (G_LIKELY (parse_funcs != NULL))
1629     return;
1630
1631   parse_funcs = g_hash_table_new (NULL, NULL);
1632   print_funcs = g_hash_table_new (NULL, NULL);
1633
1634   register_conversion_function (GDK_TYPE_RGBA,
1635                                 rgba_value_parse,
1636                                 rgba_value_print);
1637   register_conversion_function (GDK_TYPE_COLOR,
1638                                 color_value_parse,
1639                                 color_value_print);
1640   register_conversion_function (GTK_TYPE_SYMBOLIC_COLOR,
1641                                 symbolic_color_value_parse,
1642                                 symbolic_color_value_print);
1643   register_conversion_function (PANGO_TYPE_FONT_DESCRIPTION,
1644                                 font_description_value_parse,
1645                                 font_description_value_print);
1646   register_conversion_function (G_TYPE_BOOLEAN,
1647                                 boolean_value_parse,
1648                                 boolean_value_print);
1649   register_conversion_function (G_TYPE_INT,
1650                                 int_value_parse,
1651                                 int_value_print);
1652   register_conversion_function (G_TYPE_UINT,
1653                                 uint_value_parse,
1654                                 uint_value_print);
1655   register_conversion_function (G_TYPE_DOUBLE,
1656                                 double_value_parse,
1657                                 double_value_print);
1658   register_conversion_function (G_TYPE_FLOAT,
1659                                 float_value_parse,
1660                                 float_value_print);
1661   register_conversion_function (G_TYPE_STRING,
1662                                 string_value_parse,
1663                                 string_value_print);
1664   register_conversion_function (GTK_TYPE_THEMING_ENGINE,
1665                                 theming_engine_value_parse,
1666                                 theming_engine_value_print);
1667   register_conversion_function (GTK_TYPE_ANIMATION_DESCRIPTION,
1668                                 animation_description_value_parse,
1669                                 animation_description_value_print);
1670   register_conversion_function (GTK_TYPE_BORDER,
1671                                 border_value_parse,
1672                                 border_value_print);
1673   register_conversion_function (GTK_TYPE_GRADIENT,
1674                                 gradient_value_parse,
1675                                 gradient_value_print);
1676   register_conversion_function (CAIRO_GOBJECT_TYPE_PATTERN,
1677                                 pattern_value_parse,
1678                                 pattern_value_print);
1679   register_conversion_function (GTK_TYPE_CSS_BORDER_IMAGE_REPEAT,
1680                                 border_image_repeat_value_parse,
1681                                 border_image_repeat_value_print);
1682   register_conversion_function (GTK_TYPE_SHADOW,
1683                                 shadow_value_parse,
1684                                 shadow_value_print);
1685   register_conversion_function (G_TYPE_ENUM,
1686                                 enum_value_parse,
1687                                 enum_value_print);
1688   register_conversion_function (G_TYPE_FLAGS,
1689                                 flags_value_parse,
1690                                 flags_value_print);
1691   register_conversion_function (GTK_TYPE_CSS_BACKGROUND_REPEAT,
1692                                 background_repeat_value_parse,
1693                                 background_repeat_value_print);
1694 }
1695
1696 gboolean
1697 _gtk_style_property_parse_value (GtkStyleProperty *property,
1698                                  GValue           *value,
1699                                  GtkCssParser     *parser,
1700                                  GFile            *base)
1701 {
1702   GtkStyleParseFunc func;
1703
1704   g_return_val_if_fail (value != NULL, FALSE);
1705   g_return_val_if_fail (parser != NULL, FALSE);
1706
1707   css_string_funcs_init ();
1708
1709   if (property)
1710     {
1711       if (_gtk_css_parser_try (parser, "initial", TRUE))
1712         {
1713           /* the initial value can be explicitly specified with the
1714            * â€˜initial’ keyword which all properties accept.
1715            */
1716           g_value_unset (value);
1717           g_value_init (value, GTK_TYPE_CSS_SPECIAL_VALUE);
1718           g_value_set_enum (value, GTK_CSS_INITIAL);
1719           return TRUE;
1720         }
1721       else if (_gtk_css_parser_try (parser, "inherit", TRUE))
1722         {
1723           /* All properties accept the â€˜inherit’ value which
1724            * explicitly specifies that the value will be determined
1725            * by inheritance. The â€˜inherit’ value can be used to
1726            * strengthen inherited values in the cascade, and it can
1727            * also be used on properties that are not normally inherited.
1728            */
1729           g_value_unset (value);
1730           g_value_init (value, GTK_TYPE_CSS_SPECIAL_VALUE);
1731           g_value_set_enum (value, GTK_CSS_INHERIT);
1732           return TRUE;
1733         }
1734       else if (property->property_parse_func)
1735         {
1736           GError *error = NULL;
1737           char *value_str;
1738           gboolean success;
1739           
1740           value_str = _gtk_css_parser_read_value (parser);
1741           if (value_str == NULL)
1742             return FALSE;
1743           
1744           success = (*property->property_parse_func) (value_str, value, &error);
1745
1746           g_free (value_str);
1747
1748           return success;
1749         }
1750
1751       func = property->parse_func;
1752     }
1753   else
1754     func = NULL;
1755
1756   if (func == NULL)
1757     func = g_hash_table_lookup (parse_funcs,
1758                                 GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
1759   if (func == NULL)
1760     func = g_hash_table_lookup (parse_funcs,
1761                                 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
1762
1763   if (func == NULL)
1764     {
1765       _gtk_css_parser_error (parser,
1766                              "Cannot convert to type '%s'",
1767                              g_type_name (G_VALUE_TYPE (value)));
1768       return FALSE;
1769     }
1770
1771   return (*func) (parser, base, value);
1772 }
1773
1774 void
1775 _gtk_style_property_print_value (GtkStyleProperty *property,
1776                                  const GValue     *value,
1777                                  GString          *string)
1778 {
1779   GtkStylePrintFunc func;
1780
1781   css_string_funcs_init ();
1782
1783   if (G_VALUE_HOLDS (value, GTK_TYPE_CSS_SPECIAL_VALUE))
1784     func = enum_value_print;
1785   else if (property)
1786     func = property->print_func;
1787   else
1788     func = NULL;
1789
1790   if (func == NULL)
1791     func = g_hash_table_lookup (print_funcs,
1792                                 GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
1793   if (func == NULL)
1794     func = g_hash_table_lookup (print_funcs,
1795                                 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
1796
1797   if (func == NULL)
1798     {
1799       char *s = g_strdup_value_contents (value);
1800       g_string_append (string, s);
1801       g_free (s);
1802       return;
1803     }
1804   
1805   func (value, string);
1806 }
1807
1808 static void
1809 _gtk_style_property_default_value (GtkStyleProperty   *property,
1810                                    GtkStyleProperties *properties,
1811                                    GtkStateFlags       state,
1812                                    GValue             *value)
1813 {
1814   g_value_copy (&property->initial_value, value);
1815 }
1816
1817 gboolean
1818 _gtk_style_property_is_inherit (GtkStyleProperty *property)
1819 {
1820   g_return_val_if_fail (property != NULL, FALSE);
1821
1822   return property->flags & GTK_STYLE_PROPERTY_INHERIT ? TRUE : FALSE;
1823 }
1824
1825 guint
1826 _gtk_style_property_get_id (GtkStyleProperty *property)
1827 {
1828   g_return_val_if_fail (property != NULL, FALSE);
1829
1830   return property->id;
1831 }
1832
1833 static gboolean
1834 resolve_color (GtkStyleProperties *props,
1835                GValue             *value)
1836 {
1837   GdkRGBA color;
1838
1839   /* Resolve symbolic color to GdkRGBA */
1840   if (!gtk_symbolic_color_resolve (g_value_get_boxed (value), props, &color))
1841     return FALSE;
1842
1843   /* Store it back, this is where GdkRGBA caching happens */
1844   g_value_unset (value);
1845   g_value_init (value, GDK_TYPE_RGBA);
1846   g_value_set_boxed (value, &color);
1847
1848   return TRUE;
1849 }
1850
1851 static gboolean
1852 resolve_color_rgb (GtkStyleProperties *props,
1853                    GValue             *value)
1854 {
1855   GdkColor color = { 0 };
1856   GdkRGBA rgba;
1857
1858   if (!gtk_symbolic_color_resolve (g_value_get_boxed (value), props, &rgba))
1859     return FALSE;
1860
1861   color.red = rgba.red * 65535. + 0.5;
1862   color.green = rgba.green * 65535. + 0.5;
1863   color.blue = rgba.blue * 65535. + 0.5;
1864
1865   g_value_unset (value);
1866   g_value_init (value, GDK_TYPE_COLOR);
1867   g_value_set_boxed (value, &color);
1868
1869   return TRUE;
1870 }
1871
1872 static gboolean
1873 resolve_win32_theme_part (GtkStyleProperties *props,
1874                           GValue             *value,
1875                           GValue             *value_out,
1876                           GtkStylePropertyContext *context)
1877 {
1878   GtkWin32ThemePart  *part;
1879   cairo_pattern_t *pattern;
1880
1881   part = g_value_get_boxed (value);
1882   if (part == NULL)
1883     return FALSE;
1884
1885   pattern = _gtk_win32_theme_part_render (part, context->width, context->height);
1886
1887   g_value_take_boxed (value_out, pattern);
1888
1889   return TRUE;
1890 }
1891
1892
1893 static gboolean
1894 resolve_gradient (GtkStyleProperties *props,
1895                   GValue             *value)
1896 {
1897   cairo_pattern_t *gradient;
1898
1899   if (!gtk_gradient_resolve (g_value_get_boxed (value), props, &gradient))
1900     return FALSE;
1901
1902   /* Store it back, this is where cairo_pattern_t caching happens */
1903   g_value_unset (value);
1904   g_value_init (value, CAIRO_GOBJECT_TYPE_PATTERN);
1905   g_value_take_boxed (value, gradient);
1906
1907   return TRUE;
1908 }
1909
1910 static gboolean
1911 resolve_shadow (GtkStyleProperties *props,
1912                 GValue *value)
1913 {
1914   GtkShadow *resolved, *base;
1915
1916   base = g_value_get_boxed (value);
1917
1918   if (base == NULL)
1919     return TRUE;
1920   
1921   if (_gtk_shadow_get_resolved (base))
1922     return TRUE;
1923
1924   resolved = _gtk_shadow_resolve (base, props);
1925   if (resolved == NULL)
1926     return FALSE;
1927
1928   g_value_take_boxed (value, resolved);
1929
1930   return TRUE;
1931 }
1932
1933 static void
1934 _gtk_style_property_resolve (GtkStyleProperty       *property,
1935                              GtkStyleProperties     *props,
1936                              GtkStateFlags           state,
1937                              GtkStylePropertyContext *context,
1938                              GValue                 *val,
1939                              GValue                 *val_out)
1940 {
1941   if (G_VALUE_TYPE (val) == GTK_TYPE_CSS_SPECIAL_VALUE)
1942     {
1943       GtkCssSpecialValue special = g_value_get_enum (val);
1944
1945       g_value_unset (val);
1946       switch (special)
1947         {
1948         case GTK_CSS_CURRENT_COLOR:
1949           g_assert (property->pspec->value_type == GDK_TYPE_RGBA);
1950           gtk_style_properties_get_property (props, "color", state, val);
1951           break;
1952         case GTK_CSS_INHERIT:
1953         case GTK_CSS_INITIAL:
1954         default:
1955           g_assert_not_reached ();
1956         }
1957     }
1958   else if (G_VALUE_TYPE (val) == GTK_TYPE_SYMBOLIC_COLOR)
1959     {
1960       if (property->pspec->value_type == GDK_TYPE_RGBA)
1961         {
1962           if (resolve_color (props, val))
1963             goto out;
1964         }
1965       else if (property->pspec->value_type == GDK_TYPE_COLOR)
1966         {
1967           if (resolve_color_rgb (props, val))
1968             goto out;
1969         }
1970       
1971       g_value_unset (val);
1972       g_value_init (val, property->pspec->value_type);
1973       _gtk_style_property_default_value (property, props, state, val);
1974     }
1975   else if (G_VALUE_TYPE (val) == GDK_TYPE_RGBA)
1976     {
1977       if (g_value_get_boxed (val) == NULL)
1978         _gtk_style_property_default_value (property, props, state, val);
1979     }
1980   else if (G_VALUE_TYPE (val) == GTK_TYPE_GRADIENT)
1981     {
1982       g_return_if_fail (property->pspec->value_type == CAIRO_GOBJECT_TYPE_PATTERN);
1983
1984       if (!resolve_gradient (props, val))
1985         {
1986           g_value_unset (val);
1987           g_value_init (val, CAIRO_GOBJECT_TYPE_PATTERN);
1988           _gtk_style_property_default_value (property, props, state, val);
1989         }
1990     }
1991   else if (G_VALUE_TYPE (val) == GTK_TYPE_SHADOW)
1992     {
1993       if (!resolve_shadow (props, val))
1994         _gtk_style_property_default_value (property, props, state, val);
1995     }
1996   else if (G_VALUE_TYPE (val) == GTK_TYPE_WIN32_THEME_PART)
1997     {
1998       if (resolve_win32_theme_part (props, val, val_out, context))
1999         return; /* Don't copy val, this sets val_out */
2000       _gtk_style_property_default_value (property, props, state, val);
2001     }
2002
2003  out:
2004   g_value_copy (val, val_out);
2005 }
2006
2007 const GValue *
2008 _gtk_style_property_get_initial_value (GtkStyleProperty *property)
2009 {
2010   g_return_val_if_fail (property != NULL, NULL);
2011
2012   return &property->initial_value;
2013 }
2014
2015 GParameter *
2016 _gtk_style_property_unpack (GtkStyleProperty *property,
2017                             const GValue     *value,
2018                             guint            *n_params)
2019 {
2020   g_return_val_if_fail (property != NULL, NULL);
2021   g_return_val_if_fail (property->unpack_func != NULL, NULL);
2022   g_return_val_if_fail (value != NULL, NULL);
2023   g_return_val_if_fail (n_params != NULL, NULL);
2024
2025   return property->unpack_func (value, n_params);
2026 }
2027
2028 static void
2029 _gtk_style_property_pack (GtkStyleProperty   *property,
2030                           GtkStyleProperties *props,
2031                           GtkStateFlags       state,
2032                           GtkStylePropertyContext *context,
2033                           GValue             *value)
2034 {
2035   g_return_if_fail (property != NULL);
2036   g_return_if_fail (property->pack_func != NULL);
2037   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
2038   g_return_if_fail (G_IS_VALUE (value));
2039
2040   property->pack_func (value, props, state, context);
2041 }
2042
2043 void
2044 _gtk_style_property_query (GtkStyleProperty        *property,
2045                            GtkStyleProperties      *props,
2046                            GtkStateFlags            state,
2047                            GtkStylePropertyContext *context,
2048                            GValue                  *value)
2049 {
2050   const GValue *val;
2051
2052   g_return_if_fail (property != NULL);
2053   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
2054   g_return_if_fail (context != NULL);
2055   g_return_if_fail (value != NULL);
2056
2057   val = _gtk_style_properties_peek_property (props, property, state);
2058   g_value_init (value, property->pspec->value_type);
2059
2060   if (val)
2061     _gtk_style_property_resolve (property, props, state, context, (GValue *) val, value);
2062   else if (GTK_IS_CSS_SHORTHAND_PROPERTY (property))
2063     _gtk_style_property_pack (property, props, state, context, value);
2064   else
2065     _gtk_style_property_default_value (property, props, state, value);
2066 }
2067
2068 #define rgba_init(rgba, r, g, b, a) G_STMT_START{ \
2069   (rgba)->red = (r); \
2070   (rgba)->green = (g); \
2071   (rgba)->blue = (b); \
2072   (rgba)->alpha = (a); \
2073 }G_STMT_END
2074 static void
2075 gtk_style_property_init_properties (void)
2076 {
2077   static gboolean initialized = FALSE;
2078   GValue value = { 0, };
2079   char *default_font_family[] = { "Sans", NULL };
2080   GdkRGBA rgba;
2081
2082   if (G_LIKELY (initialized))
2083     return;
2084
2085   initialized = TRUE;
2086
2087   g_value_init (&value, GDK_TYPE_RGBA);
2088   rgba_init (&rgba, 1, 1, 1, 1);
2089   g_value_set_boxed (&value, &rgba);
2090   _gtk_style_property_register           (g_param_spec_boxed ("color",
2091                                           "Foreground color",
2092                                           "Foreground color",
2093                                           GDK_TYPE_RGBA, 0),
2094                                           GTK_STYLE_PROPERTY_INHERIT,
2095                                           NULL,
2096                                           NULL,
2097                                           NULL,
2098                                           &value);
2099   rgba_init (&rgba, 0, 0, 0, 0);
2100   g_value_set_boxed (&value, &rgba);
2101   _gtk_style_property_register           (g_param_spec_boxed ("background-color",
2102                                           "Background color",
2103                                           "Background color",
2104                                           GDK_TYPE_RGBA, 0),
2105                                           0,
2106                                           NULL,
2107                                           transparent_color_value_parse,
2108                                           NULL,
2109                                           &value);
2110   g_value_unset (&value);
2111
2112   g_value_init (&value, G_TYPE_STRV);
2113   g_value_set_boxed (&value, default_font_family);
2114   _gtk_style_property_register           (g_param_spec_boxed ("font-family",
2115                                                               "Font family",
2116                                                               "Font family",
2117                                                               G_TYPE_STRV, 0),
2118                                           GTK_STYLE_PROPERTY_INHERIT,
2119                                           NULL,
2120                                           font_family_parse,
2121                                           font_family_value_print,
2122                                           &value);
2123   g_value_unset (&value);
2124   _gtk_style_property_register           (g_param_spec_enum ("font-style",
2125                                                              "Font style",
2126                                                              "Font style",
2127                                                              PANGO_TYPE_STYLE,
2128                                                              PANGO_STYLE_NORMAL, 0),
2129                                           GTK_STYLE_PROPERTY_INHERIT,
2130                                           NULL,
2131                                           NULL,
2132                                           NULL,
2133                                           NULL);
2134   _gtk_style_property_register           (g_param_spec_enum ("font-variant",
2135                                                              "Font variant",
2136                                                              "Font variant",
2137                                                              PANGO_TYPE_VARIANT,
2138                                                              PANGO_VARIANT_NORMAL, 0),
2139                                           GTK_STYLE_PROPERTY_INHERIT,
2140                                           NULL,
2141                                           NULL,
2142                                           NULL,
2143                                           NULL);
2144   /* xxx: need to parse this properly, ie parse the numbers */
2145   _gtk_style_property_register           (g_param_spec_enum ("font-weight",
2146                                                              "Font weight",
2147                                                              "Font weight",
2148                                                              PANGO_TYPE_WEIGHT,
2149                                                              PANGO_WEIGHT_NORMAL, 0),
2150                                           GTK_STYLE_PROPERTY_INHERIT,
2151                                           NULL,
2152                                           NULL,
2153                                           NULL,
2154                                           NULL);
2155   g_value_init (&value, G_TYPE_DOUBLE);
2156   g_value_set_double (&value, 10);
2157   _gtk_style_property_register           (g_param_spec_double ("font-size",
2158                                                                "Font size",
2159                                                                "Font size",
2160                                                                0, G_MAXDOUBLE, 0, 0),
2161                                           GTK_STYLE_PROPERTY_INHERIT,
2162                                           NULL,
2163                                           NULL,
2164                                           NULL,
2165                                           &value);
2166   g_value_unset (&value);
2167
2168   _gtk_style_property_register           (g_param_spec_boxed ("text-shadow",
2169                                                               "Text shadow",
2170                                                               "Text shadow",
2171                                                               GTK_TYPE_SHADOW, 0),
2172                                           GTK_STYLE_PROPERTY_INHERIT,
2173                                           NULL,
2174                                           NULL,
2175                                           NULL,
2176                                           NULL);
2177
2178   _gtk_style_property_register           (g_param_spec_boxed ("icon-shadow",
2179                                                               "Icon shadow",
2180                                                               "Icon shadow",
2181                                                               GTK_TYPE_SHADOW, 0),
2182                                           GTK_STYLE_PROPERTY_INHERIT,
2183                                           NULL,
2184                                           NULL,
2185                                           NULL,
2186                                           NULL);
2187
2188   gtk_style_properties_register_property (NULL,
2189                                           g_param_spec_boxed ("box-shadow",
2190                                                               "Box shadow",
2191                                                               "Box shadow",
2192                                                               GTK_TYPE_SHADOW, 0));
2193   gtk_style_properties_register_property (NULL,
2194                                           g_param_spec_int ("margin-top",
2195                                                             "margin top",
2196                                                             "Margin at top",
2197                                                             0, G_MAXINT, 0, 0));
2198   gtk_style_properties_register_property (NULL,
2199                                           g_param_spec_int ("margin-left",
2200                                                             "margin left",
2201                                                             "Margin at left",
2202                                                             0, G_MAXINT, 0, 0));
2203   gtk_style_properties_register_property (NULL,
2204                                           g_param_spec_int ("margin-bottom",
2205                                                             "margin bottom",
2206                                                             "Margin at bottom",
2207                                                             0, G_MAXINT, 0, 0));
2208   gtk_style_properties_register_property (NULL,
2209                                           g_param_spec_int ("margin-right",
2210                                                             "margin right",
2211                                                             "Margin at right",
2212                                                             0, G_MAXINT, 0, 0));
2213   gtk_style_properties_register_property (NULL,
2214                                           g_param_spec_int ("padding-top",
2215                                                             "padding top",
2216                                                             "Padding at top",
2217                                                             0, G_MAXINT, 0, 0));
2218   gtk_style_properties_register_property (NULL,
2219                                           g_param_spec_int ("padding-left",
2220                                                             "padding left",
2221                                                             "Padding at left",
2222                                                             0, G_MAXINT, 0, 0));
2223   gtk_style_properties_register_property (NULL,
2224                                           g_param_spec_int ("padding-bottom",
2225                                                             "padding bottom",
2226                                                             "Padding at bottom",
2227                                                             0, G_MAXINT, 0, 0));
2228   gtk_style_properties_register_property (NULL,
2229                                           g_param_spec_int ("padding-right",
2230                                                             "padding right",
2231                                                             "Padding at right",
2232                                                             0, G_MAXINT, 0, 0));
2233   gtk_style_properties_register_property (NULL,
2234                                           g_param_spec_int ("border-top-width",
2235                                                             "border top width",
2236                                                             "Border width at top",
2237                                                             0, G_MAXINT, 0, 0));
2238   gtk_style_properties_register_property (NULL,
2239                                           g_param_spec_int ("border-left-width",
2240                                                             "border left width",
2241                                                             "Border width at left",
2242                                                             0, G_MAXINT, 0, 0));
2243   gtk_style_properties_register_property (NULL,
2244                                           g_param_spec_int ("border-bottom-width",
2245                                                             "border bottom width",
2246                                                             "Border width at bottom",
2247                                                             0, G_MAXINT, 0, 0));
2248   gtk_style_properties_register_property (NULL,
2249                                           g_param_spec_int ("border-right-width",
2250                                                             "border right width",
2251                                                             "Border width at right",
2252                                                             0, G_MAXINT, 0, 0));
2253
2254   _gtk_style_property_register           (g_param_spec_boxed ("border-top-left-radius",
2255                                                               "Border top left radius",
2256                                                               "Border radius of top left corner, in pixels",
2257                                                               GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
2258                                           0,
2259                                           NULL,
2260                                           border_corner_radius_value_parse,
2261                                           border_corner_radius_value_print,
2262                                           NULL);
2263   _gtk_style_property_register           (g_param_spec_boxed ("border-top-right-radius",
2264                                                               "Border top right radius",
2265                                                               "Border radius of top right corner, in pixels",
2266                                                               GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
2267                                           0,
2268                                           NULL,
2269                                           border_corner_radius_value_parse,
2270                                           border_corner_radius_value_print,
2271                                           NULL);
2272   _gtk_style_property_register           (g_param_spec_boxed ("border-bottom-right-radius",
2273                                                               "Border bottom right radius",
2274                                                               "Border radius of bottom right corner, in pixels",
2275                                                               GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
2276                                           0,
2277                                           NULL,
2278                                           border_corner_radius_value_parse,
2279                                           border_corner_radius_value_print,
2280                                           NULL);
2281   _gtk_style_property_register           (g_param_spec_boxed ("border-bottom-left-radius",
2282                                                               "Border bottom left radius",
2283                                                               "Border radius of bottom left corner, in pixels",
2284                                                               GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
2285                                           0,
2286                                           NULL,
2287                                           border_corner_radius_value_parse,
2288                                           border_corner_radius_value_print,
2289                                           NULL);
2290
2291   gtk_style_properties_register_property (NULL,
2292                                           g_param_spec_enum ("border-style",
2293                                                              "Border style",
2294                                                              "Border style",
2295                                                              GTK_TYPE_BORDER_STYLE,
2296                                                              GTK_BORDER_STYLE_NONE, 0));
2297   gtk_style_properties_register_property (NULL,
2298                                           g_param_spec_enum ("background-clip",
2299                                                              "Background clip",
2300                                                              "Background clip",
2301                                                              GTK_TYPE_CSS_AREA,
2302                                                              GTK_CSS_AREA_BORDER_BOX, 0));
2303   gtk_style_properties_register_property (NULL,
2304                                           g_param_spec_enum ("background-origin",
2305                                                              "Background origin",
2306                                                              "Background origin",
2307                                                              GTK_TYPE_CSS_AREA,
2308                                                              GTK_CSS_AREA_PADDING_BOX, 0));
2309   g_value_init (&value, GTK_TYPE_CSS_SPECIAL_VALUE);
2310   g_value_set_enum (&value, GTK_CSS_CURRENT_COLOR);
2311   _gtk_style_property_register           (g_param_spec_boxed ("border-top-color",
2312                                                               "Border top color",
2313                                                               "Border top color",
2314                                                               GDK_TYPE_RGBA, 0),
2315                                           0,
2316                                           NULL,
2317                                           transparent_color_value_parse,
2318                                           NULL,
2319                                           &value);
2320   _gtk_style_property_register           (g_param_spec_boxed ("border-right-color",
2321                                                               "Border right color",
2322                                                               "Border right color",
2323                                                               GDK_TYPE_RGBA, 0),
2324                                           0,
2325                                           NULL,
2326                                           transparent_color_value_parse,
2327                                           NULL,
2328                                           &value);
2329   _gtk_style_property_register           (g_param_spec_boxed ("border-bottom-color",
2330                                                               "Border bottom color",
2331                                                               "Border bottom color",
2332                                                               GDK_TYPE_RGBA, 0),
2333                                           0,
2334                                           NULL,
2335                                           transparent_color_value_parse,
2336                                           NULL,
2337                                           &value);
2338   _gtk_style_property_register           (g_param_spec_boxed ("border-left-color",
2339                                                               "Border left color",
2340                                                               "Border left color",
2341                                                               GDK_TYPE_RGBA, 0),
2342                                           0,
2343                                           NULL,
2344                                           transparent_color_value_parse,
2345                                           NULL,
2346                                           &value);
2347   g_value_unset (&value);
2348
2349   gtk_style_properties_register_property (NULL,
2350                                           g_param_spec_boxed ("background-image",
2351                                                               "Background Image",
2352                                                               "Background Image",
2353                                                               CAIRO_GOBJECT_TYPE_PATTERN, 0));
2354   gtk_style_properties_register_property (NULL,
2355                                           g_param_spec_boxed ("background-repeat",
2356                                                               "Background repeat",
2357                                                               "Background repeat",
2358                                                               GTK_TYPE_CSS_BACKGROUND_REPEAT, 0));
2359
2360   gtk_style_properties_register_property (NULL,
2361                                           g_param_spec_boxed ("border-image-source",
2362                                                               "Border image source",
2363                                                               "Border image source",
2364                                                               CAIRO_GOBJECT_TYPE_PATTERN, 0));
2365   gtk_style_properties_register_property (NULL,
2366                                           g_param_spec_boxed ("border-image-repeat",
2367                                                               "Border image repeat",
2368                                                               "Border image repeat",
2369                                                               GTK_TYPE_CSS_BORDER_IMAGE_REPEAT, 0));
2370   gtk_style_properties_register_property (NULL,
2371                                           g_param_spec_boxed ("border-image-slice",
2372                                                               "Border image slice",
2373                                                               "Border image slice",
2374                                                               GTK_TYPE_BORDER, 0));
2375   g_value_init (&value, GTK_TYPE_BORDER);
2376   _gtk_style_property_register           (g_param_spec_boxed ("border-image-width",
2377                                                               "Border image width",
2378                                                               "Border image width",
2379                                                               GTK_TYPE_BORDER, 0),
2380                                           0,
2381                                           NULL,
2382                                           NULL,
2383                                           NULL,
2384                                           &value);
2385   g_value_unset (&value);
2386   gtk_style_properties_register_property (NULL,
2387                                           g_param_spec_object ("engine",
2388                                                                "Theming Engine",
2389                                                                "Theming Engine",
2390                                                                GTK_TYPE_THEMING_ENGINE, 0));
2391   gtk_style_properties_register_property (NULL,
2392                                           g_param_spec_boxed ("transition",
2393                                                               "Transition animation description",
2394                                                               "Transition animation description",
2395                                                               GTK_TYPE_ANIMATION_DESCRIPTION, 0));
2396
2397   /* Private property holding the binding sets */
2398   _gtk_style_property_register           (g_param_spec_boxed ("gtk-key-bindings",
2399                                                               "Key bindings",
2400                                                               "Key bindings",
2401                                                               G_TYPE_PTR_ARRAY, 0),
2402                                           0,
2403                                           NULL,
2404                                           bindings_value_parse,
2405                                           bindings_value_print,
2406                                           NULL);
2407
2408   /* initialize shorthands last, they depend on the real properties existing */
2409   _gtk_css_shorthand_property_init_properties ();
2410 }
2411
2412 /**
2413  * _gtk_style_property_lookup:
2414  * @name: name of the property to lookup
2415  *
2416  * Looks up the CSS property with the given @name. If no such
2417  * property exists, %NULL is returned.
2418  *
2419  * Returns: (transfer none): The property or %NULL if no
2420  *     property with the given name exists.
2421  **/
2422 GtkStyleProperty *
2423 _gtk_style_property_lookup (const char *name)
2424 {
2425   GtkStylePropertyClass *klass;
2426
2427   g_return_val_if_fail (name != NULL, NULL);
2428
2429   gtk_style_property_init_properties ();
2430
2431   klass = g_type_class_peek (GTK_TYPE_STYLE_PROPERTY);
2432
2433   return g_hash_table_lookup (klass->properties, name);
2434 }
2435
2436 /**
2437  * _gtk_style_property_get_name:
2438  * @property: the property to query
2439  *
2440  * Gets the name of the given property.
2441  *
2442  * Returns: the name of the property
2443  **/
2444 const char *
2445 _gtk_style_property_get_name (GtkStyleProperty *property)
2446 {
2447   g_return_val_if_fail (GTK_IS_STYLE_PROPERTY (property), NULL);
2448
2449   return property->name;
2450 }
2451
2452
2453 void
2454 _gtk_style_property_register (GParamSpec               *pspec,
2455                               GtkStylePropertyFlags     flags,
2456                               GtkStylePropertyParser    property_parse_func,
2457                               GtkStyleParseFunc         parse_func,
2458                               GtkStylePrintFunc         print_func,
2459                               const GValue *            initial_value)
2460 {
2461   GtkStyleProperty *node;
2462
2463   node = g_object_new (GTK_TYPE_CSS_STYLE_PROPERTY,
2464                        "name", pspec->name,
2465                        NULL);
2466   node->flags = flags;
2467   node->pspec = pspec;
2468   node->property_parse_func = property_parse_func;
2469   node->parse_func = parse_func;
2470   node->print_func = print_func;
2471
2472   _gtk_style_property_generate_id (node);
2473
2474   /* initialize the initial value */
2475   if (initial_value)
2476     {
2477       g_value_init (&node->initial_value, G_VALUE_TYPE (initial_value));
2478       g_value_copy (initial_value, &node->initial_value);
2479     }
2480   else
2481     {
2482       g_value_init (&node->initial_value, pspec->value_type);
2483       if (pspec->value_type == GTK_TYPE_THEMING_ENGINE)
2484         g_value_set_object (&node->initial_value, gtk_theming_engine_load (NULL));
2485       else if (pspec->value_type == PANGO_TYPE_FONT_DESCRIPTION)
2486         g_value_take_boxed (&node->initial_value, pango_font_description_from_string ("Sans 10"));
2487       else if (pspec->value_type == GDK_TYPE_RGBA)
2488         {
2489           GdkRGBA color;
2490           gdk_rgba_parse (&color, "pink");
2491           g_value_set_boxed (&node->initial_value, &color);
2492         }
2493       else if (pspec->value_type == GTK_TYPE_BORDER)
2494         {
2495           g_value_take_boxed (&node->initial_value, gtk_border_new ());
2496         }
2497       else
2498         g_param_value_set_default (pspec, &node->initial_value);
2499     }
2500 }