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