]> Pileus Git - ~andy/gtk/blob - gtk/gtkstyleproperty.c
css: Change to_string() to print()
[~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
34 /* the actual parsers we have */
35 #include "gtkanimationdescription.h"
36 #include "gtkbindings.h"
37 #include "gtk9slice.h"
38 #include "gtkgradient.h"
39 #include "gtkshadowprivate.h"
40 #include "gtkthemingengine.h"
41 #include "gtktypebuiltins.h"
42
43 typedef gboolean (* ParseFunc)        (GtkCssParser  *parser,
44                                        GFile         *base,
45                                        GValue        *value);
46 typedef void     (* PrintFunc)        (const GValue  *value,
47                                        GString       *string);
48
49 static GHashTable *parse_funcs = NULL;
50 static GHashTable *print_funcs = NULL;
51 static GHashTable *properties = NULL;
52
53 static void
54 register_conversion_function (GType          type,
55                               ParseFunc      parse,
56                               PrintFunc      print)
57 {
58   if (parse)
59     g_hash_table_insert (parse_funcs, GSIZE_TO_POINTER (type), parse);
60   if (print)
61     g_hash_table_insert (print_funcs, GSIZE_TO_POINTER (type), print);
62 }
63
64 /*** IMPLEMENTATIONS ***/
65
66 static gboolean 
67 rgba_value_parse (GtkCssParser *parser,
68                   GFile        *base,
69                   GValue       *value)
70 {
71   GtkSymbolicColor *symbolic;
72   GdkRGBA rgba;
73
74   symbolic = _gtk_css_parser_read_symbolic_color (parser);
75   if (symbolic == NULL)
76     return FALSE;
77
78   if (gtk_symbolic_color_resolve (symbolic, NULL, &rgba))
79     {
80       g_value_set_boxed (value, &rgba);
81       gtk_symbolic_color_unref (symbolic);
82     }
83   else
84     {
85       g_value_unset (value);
86       g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
87       g_value_take_boxed (value, symbolic);
88     }
89
90   return TRUE;
91 }
92
93 static void
94 rgba_value_print (const GValue *value,
95                   GString      *string)
96 {
97   const GdkRGBA *rgba = g_value_get_boxed (value);
98
99   if (rgba == NULL)
100     g_string_append (string, "none");
101   else
102     {
103       char *s = gdk_rgba_to_string (rgba);
104       g_string_append (string, s);
105       g_free (s);
106     }
107 }
108
109 static gboolean 
110 color_value_parse (GtkCssParser *parser,
111                    GFile        *base,
112                    GValue       *value)
113 {
114   GtkSymbolicColor *symbolic;
115   GdkRGBA rgba;
116
117   symbolic = _gtk_css_parser_read_symbolic_color (parser);
118   if (symbolic == NULL)
119     return FALSE;
120
121   if (gtk_symbolic_color_resolve (symbolic, NULL, &rgba))
122     {
123       GdkColor color;
124
125       color.red = rgba.red * 65535. + 0.5;
126       color.green = rgba.green * 65535. + 0.5;
127       color.blue = rgba.blue * 65535. + 0.5;
128
129       g_value_set_boxed (value, &color);
130     }
131   else
132     {
133       g_value_unset (value);
134       g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
135       g_value_take_boxed (value, symbolic);
136     }
137
138   return TRUE;
139 }
140
141 static void
142 color_value_print (const GValue *value,
143                    GString      *string)
144 {
145   const GdkColor *color = g_value_get_boxed (value);
146
147   if (color == NULL)
148     g_string_append (string, "none");
149   else
150     {
151       char *s = gdk_color_to_string (color);
152       g_string_append (string, s);
153       g_free (s);
154     }
155 }
156
157 static gboolean
158 symbolic_color_value_parse (GtkCssParser *parser,
159                             GFile        *base,
160                             GValue       *value)
161 {
162   GtkSymbolicColor *symbolic;
163
164   symbolic = _gtk_css_parser_read_symbolic_color (parser);
165   if (symbolic == NULL)
166     return FALSE;
167
168   g_value_take_boxed (value, symbolic);
169   return TRUE;
170 }
171
172 static void
173 symbolic_color_value_print (const GValue *value,
174                             GString      *string)
175 {
176   GtkSymbolicColor *symbolic = g_value_get_boxed (value);
177
178   if (symbolic == NULL)
179     g_string_append (string, "none");
180   else
181     {
182       char *s = gtk_symbolic_color_to_string (symbolic);
183       g_string_append (string, s);
184       g_free (s);
185     }
186 }
187
188 static gboolean 
189 font_description_value_parse (GtkCssParser *parser,
190                               GFile        *base,
191                               GValue       *value)
192 {
193   PangoFontDescription *font_desc;
194   char *str;
195
196   str = _gtk_css_parser_read_value (parser);
197   if (str == NULL)
198     return FALSE;
199
200   font_desc = pango_font_description_from_string (str);
201   g_free (str);
202   g_value_take_boxed (value, font_desc);
203   return TRUE;
204 }
205
206 static void
207 font_description_value_print (const GValue *value,
208                               GString      *string)
209 {
210   const PangoFontDescription *desc = g_value_get_boxed (value);
211
212   if (desc == NULL)
213     g_string_append (string, "none");
214   else
215     {
216       char *s = pango_font_description_to_string (desc);
217       g_string_append (string, s);
218       g_free (s);
219     }
220 }
221
222 static gboolean 
223 boolean_value_parse (GtkCssParser *parser,
224                      GFile        *base,
225                      GValue       *value)
226 {
227   if (_gtk_css_parser_try (parser, "true", TRUE) ||
228       _gtk_css_parser_try (parser, "1", TRUE))
229     {
230       g_value_set_boolean (value, TRUE);
231       return TRUE;
232     }
233   else if (_gtk_css_parser_try (parser, "false", TRUE) ||
234            _gtk_css_parser_try (parser, "0", TRUE))
235     {
236       g_value_set_boolean (value, FALSE);
237       return TRUE;
238     }
239   else
240     {
241       _gtk_css_parser_error (parser, "Expected a boolean value");
242       return FALSE;
243     }
244 }
245
246 static void
247 boolean_value_print (const GValue *value,
248                      GString      *string)
249 {
250   if (g_value_get_boolean (value))
251     g_string_append (string, "true");
252   else
253     g_string_append (string, "false");
254 }
255
256 static gboolean 
257 int_value_parse (GtkCssParser *parser,
258                  GFile        *base,
259                  GValue       *value)
260 {
261   gint i;
262
263   if (!_gtk_css_parser_try_int (parser, &i))
264     {
265       _gtk_css_parser_error (parser, "Expected a valid integer value");
266       return FALSE;
267     }
268
269   g_value_set_int (value, i);
270   return TRUE;
271 }
272
273 static void
274 int_value_print (const GValue *value,
275                  GString      *string)
276 {
277   g_string_append_printf (string, "%d", g_value_get_int (value));
278 }
279
280 static gboolean 
281 uint_value_parse (GtkCssParser *parser,
282                   GFile        *base,
283                   GValue       *value)
284 {
285   guint u;
286
287   if (!_gtk_css_parser_try_uint (parser, &u))
288     {
289       _gtk_css_parser_error (parser, "Expected a valid unsigned value");
290       return FALSE;
291     }
292
293   g_value_set_uint (value, u);
294   return TRUE;
295 }
296
297 static void
298 uint_value_print (const GValue *value,
299                   GString      *string)
300 {
301   g_string_append_printf (string, "%u", g_value_get_uint (value));
302 }
303
304 static gboolean 
305 double_value_parse (GtkCssParser *parser,
306                     GFile        *base,
307                     GValue       *value)
308 {
309   gdouble d;
310
311   if (!_gtk_css_parser_try_double (parser, &d))
312     {
313       _gtk_css_parser_error (parser, "Expected a number");
314       return FALSE;
315     }
316
317   g_value_set_double (value, d);
318   return TRUE;
319 }
320
321 static void
322 double_value_print (const GValue *value,
323                     GString      *string)
324 {
325   char buf[G_ASCII_DTOSTR_BUF_SIZE];
326
327   g_ascii_dtostr (buf, sizeof (buf), g_value_get_double (value));
328   g_string_append (string, buf);
329 }
330
331 static gboolean 
332 float_value_parse (GtkCssParser *parser,
333                    GFile        *base,
334                    GValue       *value)
335 {
336   gdouble d;
337
338   if (!_gtk_css_parser_try_double (parser, &d))
339     {
340       _gtk_css_parser_error (parser, "Expected a number");
341       return FALSE;
342     }
343
344   g_value_set_float (value, d);
345   return TRUE;
346 }
347
348 static void
349 float_value_print (const GValue *value,
350                    GString      *string)
351 {
352   char buf[G_ASCII_DTOSTR_BUF_SIZE];
353
354   g_ascii_dtostr (buf, sizeof (buf), g_value_get_float (value));
355   g_string_append (string, buf);
356 }
357
358 static gboolean 
359 string_value_parse (GtkCssParser *parser,
360                     GFile        *base,
361                     GValue       *value)
362 {
363   char *str = _gtk_css_parser_read_string (parser);
364
365   if (str == NULL)
366     return FALSE;
367
368   g_value_take_string (value, str);
369   return TRUE;
370 }
371
372 static void
373 string_value_print (const GValue *value,
374                     GString      *str)
375 {
376   const char *string;
377   gsize len;
378
379   string = g_value_get_string (value);
380   g_string_append_c (str, '"');
381
382   do {
383     len = strcspn (string, "\"\n\r\f");
384     g_string_append (str, string);
385     string += len;
386     switch (*string)
387       {
388       case '\0':
389         break;
390       case '\n':
391         g_string_append (str, "\\A ");
392         break;
393       case '\r':
394         g_string_append (str, "\\D ");
395         break;
396       case '\f':
397         g_string_append (str, "\\C ");
398         break;
399       case '\"':
400         g_string_append (str, "\\\"");
401         break;
402       default:
403         g_assert_not_reached ();
404         break;
405       }
406   } while (*string);
407
408   g_string_append_c (str, '"');
409 }
410
411 static gboolean 
412 theming_engine_value_parse (GtkCssParser *parser,
413                             GFile        *base,
414                             GValue       *value)
415 {
416   GtkThemingEngine *engine;
417   char *str;
418
419   str = _gtk_css_parser_try_ident (parser, TRUE);
420   if (str == NULL)
421     {
422       _gtk_css_parser_error (parser, "Expected a valid theme name");
423       return FALSE;
424     }
425
426   engine = gtk_theming_engine_load (str);
427   if (engine == NULL)
428     {
429       _gtk_css_parser_error (parser, "Themeing engine '%s' not found", str);
430       g_free (str);
431       return FALSE;
432     }
433
434   g_value_set_object (value, engine);
435   g_free (str);
436   return TRUE;
437 }
438
439 static void
440 theming_engine_value_print (const GValue *value,
441                             GString      *string)
442 {
443   GtkThemingEngine *engine;
444   char *name;
445
446   engine = g_value_get_object (value);
447   if (engine == NULL)
448     g_string_append (string, "none");
449   else
450     {
451       /* XXX: gtk_theming_engine_get_name()? */
452       g_object_get (engine, "name", &name, NULL);
453       g_string_append (string, name);
454       g_free (name);
455     }
456 }
457
458 static gboolean 
459 animation_description_value_parse (GtkCssParser *parser,
460                                    GFile        *base,
461                                    GValue       *value)
462 {
463   GtkAnimationDescription *desc;
464   char *str;
465
466   str = _gtk_css_parser_read_value (parser);
467   if (str == NULL)
468     return FALSE;
469
470   desc = _gtk_animation_description_from_string (str);
471   g_free (str);
472
473   if (desc == NULL)
474     {
475       _gtk_css_parser_error (parser, "Invalid animation description");
476       return FALSE;
477     }
478   
479   g_value_take_boxed (value, desc);
480   return TRUE;
481 }
482
483 static void
484 animation_description_value_print (const GValue *value,
485                                    GString      *string)
486 {
487   GtkAnimationDescription *desc = g_value_get_boxed (value);
488
489   if (desc == NULL)
490     g_string_append (string, "none");
491   else
492     _gtk_animation_description_print (desc, string);
493 }
494
495 static gboolean 
496 border_value_parse (GtkCssParser *parser,
497                     GFile        *base,
498                     GValue       *value)
499 {
500   GtkBorder border = { 0, };
501   guint i, numbers[4];
502
503   for (i = 0; i < G_N_ELEMENTS (numbers); i++)
504     {
505       if (!_gtk_css_parser_try_uint (parser, &numbers[i]))
506         break;
507
508       /* XXX: shouldn't allow spaces here? */
509       _gtk_css_parser_try (parser, "px", TRUE);
510     }
511
512   if (i == 0)
513     {
514       _gtk_css_parser_error (parser, "Expected valid border");
515       return FALSE;
516     }
517
518   border.top = numbers[0];
519   if (i > 1)
520     border.right = numbers[1];
521   else
522     border.right = border.top;
523   if (i > 2)
524     border.bottom = numbers[2];
525   else
526     border.bottom = border.top;
527   if (i > 3)
528     border.left = numbers[3];
529   else
530     border.left = border.right;
531
532   g_value_set_boxed (value, &border);
533   return TRUE;
534 }
535
536 static void
537 border_value_print (const GValue *value, GString *string)
538 {
539   const GtkBorder *border = g_value_get_boxed (value);
540
541   if (border == NULL)
542     g_string_append (string, "none");
543   else if (border->left != border->right)
544     g_string_append_printf (string, "%d %d %d %d", border->top, border->right, border->bottom, border->left);
545   else if (border->top != border->bottom)
546     g_string_append_printf (string, "%d %d %d", border->top, border->right, border->bottom);
547   else if (border->top != border->left)
548     g_string_append_printf (string, "%d %d", border->top, border->right);
549   else
550     g_string_append_printf (string, "%d", border->top);
551 }
552
553 static gboolean 
554 gradient_value_parse (GtkCssParser *parser,
555                       GFile        *base,
556                       GValue       *value)
557 {
558   GtkGradient *gradient;
559   cairo_pattern_type_t type;
560   gdouble coords[6];
561   guint i;
562
563   if (!_gtk_css_parser_try (parser, "-gtk-gradient", TRUE))
564     {
565       _gtk_css_parser_error (parser,
566                              "Expected '-gtk-gradient'");
567       return FALSE;
568     }
569
570   if (!_gtk_css_parser_try (parser, "(", TRUE))
571     {
572       _gtk_css_parser_error (parser,
573                              "Expected '(' after '-gtk-gradient'");
574       return FALSE;
575     }
576
577   /* Parse gradient type */
578   if (_gtk_css_parser_try (parser, "linear", TRUE))
579     type = CAIRO_PATTERN_TYPE_LINEAR;
580   else if (_gtk_css_parser_try (parser, "radial", TRUE))
581     type = CAIRO_PATTERN_TYPE_RADIAL;
582   else
583     {
584       _gtk_css_parser_error (parser,
585                              "Gradient type must be 'radial' or 'linear'");
586       return FALSE;
587     }
588
589   /* Parse start/stop position parameters */
590   for (i = 0; i < 2; i++)
591     {
592       if (! _gtk_css_parser_try (parser, ",", TRUE))
593         {
594           _gtk_css_parser_error (parser,
595                                  "Expected ','");
596           return FALSE;
597         }
598
599       if (_gtk_css_parser_try (parser, "left", TRUE))
600         coords[i * 3] = 0;
601       else if (_gtk_css_parser_try (parser, "right", TRUE))
602         coords[i * 3] = 1;
603       else if (_gtk_css_parser_try (parser, "center", TRUE))
604         coords[i * 3] = 0.5;
605       else if (!_gtk_css_parser_try_double (parser, &coords[i * 3]))
606         {
607           _gtk_css_parser_error (parser,
608                                  "Expected a valid X coordinate");
609           return FALSE;
610         }
611
612       if (_gtk_css_parser_try (parser, "top", TRUE))
613         coords[i * 3 + 1] = 0;
614       else if (_gtk_css_parser_try (parser, "bottom", TRUE))
615         coords[i * 3 + 1] = 1;
616       else if (_gtk_css_parser_try (parser, "center", TRUE))
617         coords[i * 3 + 1] = 0.5;
618       else if (!_gtk_css_parser_try_double (parser, &coords[i * 3 + 1]))
619         {
620           _gtk_css_parser_error (parser,
621                                  "Expected a valid Y coordinate");
622           return FALSE;
623         }
624
625       if (type == CAIRO_PATTERN_TYPE_RADIAL)
626         {
627           /* Parse radius */
628           if (! _gtk_css_parser_try (parser, ",", TRUE))
629             {
630               _gtk_css_parser_error (parser,
631                                      "Expected ','");
632               return FALSE;
633             }
634
635           if (! _gtk_css_parser_try_double (parser, &coords[(i * 3) + 2]))
636             {
637               _gtk_css_parser_error (parser,
638                                      "Expected a numer for the radius");
639               return FALSE;
640             }
641         }
642     }
643
644   if (type == CAIRO_PATTERN_TYPE_LINEAR)
645     gradient = gtk_gradient_new_linear (coords[0], coords[1], coords[3], coords[4]);
646   else
647     gradient = gtk_gradient_new_radial (coords[0], coords[1], coords[2],
648                                         coords[3], coords[4], coords[5]);
649
650   while (_gtk_css_parser_try (parser, ",", TRUE))
651     {
652       GtkSymbolicColor *color;
653       gdouble position;
654
655       if (_gtk_css_parser_try (parser, "from", TRUE))
656         {
657           position = 0;
658
659           if (!_gtk_css_parser_try (parser, "(", TRUE))
660             {
661               gtk_gradient_unref (gradient);
662               _gtk_css_parser_error (parser,
663                                      "Expected '('");
664               return FALSE;
665             }
666
667         }
668       else if (_gtk_css_parser_try (parser, "to", TRUE))
669         {
670           position = 1;
671
672           if (!_gtk_css_parser_try (parser, "(", TRUE))
673             {
674               gtk_gradient_unref (gradient);
675               _gtk_css_parser_error (parser,
676                                      "Expected '('");
677               return FALSE;
678             }
679
680         }
681       else if (_gtk_css_parser_try (parser, "color-stop", TRUE))
682         {
683           if (!_gtk_css_parser_try (parser, "(", TRUE))
684             {
685               gtk_gradient_unref (gradient);
686               _gtk_css_parser_error (parser,
687                                      "Expected '('");
688               return FALSE;
689             }
690
691           if (!_gtk_css_parser_try_double (parser, &position))
692             {
693               gtk_gradient_unref (gradient);
694               _gtk_css_parser_error (parser,
695                                      "Expected a valid number");
696               return FALSE;
697             }
698
699           if (!_gtk_css_parser_try (parser, ",", TRUE))
700             {
701               gtk_gradient_unref (gradient);
702               _gtk_css_parser_error (parser,
703                                      "Expected a comma");
704               return FALSE;
705             }
706         }
707       else
708         {
709           gtk_gradient_unref (gradient);
710           _gtk_css_parser_error (parser,
711                                  "Not a valid color-stop definition");
712           return FALSE;
713         }
714
715       color = _gtk_css_parser_read_symbolic_color (parser);
716       if (color == NULL)
717         {
718           gtk_gradient_unref (gradient);
719           return FALSE;
720         }
721
722       gtk_gradient_add_color_stop (gradient, position, color);
723       gtk_symbolic_color_unref (color);
724
725       if (!_gtk_css_parser_try (parser, ")", TRUE))
726         {
727           gtk_gradient_unref (gradient);
728           _gtk_css_parser_error (parser,
729                                  "Expected ')'");
730           return FALSE;
731         }
732     }
733
734   if (!_gtk_css_parser_try (parser, ")", TRUE))
735     {
736       gtk_gradient_unref (gradient);
737       _gtk_css_parser_error (parser,
738                              "Expected ')'");
739       return FALSE;
740     }
741
742   g_value_take_boxed (value, gradient);
743   return TRUE;
744 }
745
746 static void
747 gradient_value_print (const GValue *value,
748                       GString      *string)
749 {
750   GtkGradient *gradient = g_value_get_boxed (value);
751
752   if (gradient == NULL)
753     g_string_append (string, "none");
754   else
755     {
756       char *s = gtk_gradient_to_string (gradient);
757       g_string_append (string, s);
758       g_free (s);
759     }
760 }
761
762 static GFile *
763 gtk_css_parse_url (GtkCssParser *parser,
764                    GFile        *base)
765 {
766   gchar *path;
767   GFile *file;
768
769   if (_gtk_css_parser_try (parser, "url", FALSE))
770     {
771       if (!_gtk_css_parser_try (parser, "(", TRUE))
772         {
773           _gtk_css_parser_skip_whitespace (parser);
774           if (_gtk_css_parser_try (parser, "(", TRUE))
775             {
776               GError *error;
777               
778               error = g_error_new_literal (GTK_CSS_PROVIDER_ERROR,
779                                            GTK_CSS_PROVIDER_ERROR_DEPRECATED,
780                                            "Whitespace between 'url' and '(' is not allowed");
781                              
782               _gtk_css_parser_take_error (parser, error);
783             }
784           else
785             {
786               _gtk_css_parser_error (parser, "Expected '(' after 'url'");
787               return NULL;
788             }
789         }
790
791       path = _gtk_css_parser_read_string (parser);
792       if (path == NULL)
793         return NULL;
794
795       if (!_gtk_css_parser_try (parser, ")", TRUE))
796         {
797           _gtk_css_parser_error (parser, "No closing ')' found for 'url'");
798           g_free (path);
799           return NULL;
800         }
801     }
802   else
803     {
804       path = _gtk_css_parser_try_name (parser, TRUE);
805       if (path == NULL)
806         {
807           _gtk_css_parser_error (parser, "Not a valid url");
808           return NULL;
809         }
810     }
811
812   file = g_file_resolve_relative_path (base, path);
813   g_free (path);
814
815   return file;
816 }
817
818 static gboolean 
819 pattern_value_parse (GtkCssParser *parser,
820                      GFile        *base,
821                      GValue       *value)
822 {
823   if (_gtk_css_parser_begins_with (parser, '-'))
824     {
825       g_value_unset (value);
826       g_value_init (value, GTK_TYPE_GRADIENT);
827       return gradient_value_parse (parser, base, value);
828     }
829   else
830     {
831       GError *error = NULL;
832       gchar *path;
833       GdkPixbuf *pixbuf;
834       GFile *file;
835       cairo_surface_t *surface;
836       cairo_pattern_t *pattern;
837       cairo_t *cr;
838       cairo_matrix_t matrix;
839
840       file = gtk_css_parse_url (parser, base);
841       if (file == NULL)
842         return FALSE;
843
844       path = g_file_get_path (file);
845       g_object_unref (file);
846
847       pixbuf = gdk_pixbuf_new_from_file (path, &error);
848       g_free (path);
849       if (pixbuf == NULL)
850         {
851           _gtk_css_parser_take_error (parser, error);
852           return FALSE;
853         }
854
855       surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
856                                             gdk_pixbuf_get_width (pixbuf),
857                                             gdk_pixbuf_get_height (pixbuf));
858       cr = cairo_create (surface);
859       gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
860       cairo_paint (cr);
861       pattern = cairo_pattern_create_for_surface (surface);
862
863       cairo_matrix_init_scale (&matrix,
864                                gdk_pixbuf_get_width (pixbuf),
865                                gdk_pixbuf_get_height (pixbuf));
866       cairo_pattern_set_matrix (pattern, &matrix);
867
868       cairo_surface_destroy (surface);
869       cairo_destroy (cr);
870       g_object_unref (pixbuf);
871
872       g_value_take_boxed (value, pattern);
873     }
874   
875   return TRUE;
876 }
877
878 static gboolean
879 shadow_value_parse (GtkCssParser *parser,
880                     GFile *base,
881                     GValue *value)
882 {
883   gboolean inset;
884   gdouble hoffset, voffset, blur, spread;
885   GtkSymbolicColor *color;
886   GtkShadow *shadow;
887
888   shadow = _gtk_shadow_new ();
889
890   do
891     {
892       inset = _gtk_css_parser_try (parser, "inset", TRUE);
893
894       if (!_gtk_css_parser_try_double (parser, &hoffset) ||
895           !_gtk_css_parser_try_double (parser, &voffset))
896         {
897           _gtk_css_parser_error (parser, "Horizontal and vertical offsets are required");
898           _gtk_shadow_unref (shadow);
899           return FALSE;
900         }
901
902       if (!_gtk_css_parser_try_double (parser, &blur))
903         blur = 0;
904
905       if (!_gtk_css_parser_try_double (parser, &spread))
906         spread = 0;
907
908       /* XXX: the color is optional and UA-defined if it's missing,
909        * but it doesn't really make sense for us...
910        */
911       color = _gtk_css_parser_read_symbolic_color (parser);
912
913       if (color == NULL)
914         {
915           _gtk_shadow_unref (shadow);
916           return FALSE;
917         }
918
919       _gtk_shadow_append (shadow,
920                           hoffset, voffset,
921                           blur, spread,
922                           inset, color);
923
924       gtk_symbolic_color_unref (color);
925
926     }
927   while (_gtk_css_parser_try (parser, ",", TRUE));
928
929   g_value_take_boxed (value, shadow);
930   return TRUE;
931 }
932
933 static void
934 shadow_value_print (const GValue *value,
935                     GString      *string)
936 {
937   GtkShadow *shadow;
938
939   shadow = g_value_get_boxed (value);
940
941   if (shadow == NULL)
942     g_string_append (string, "none");
943   else
944     _gtk_shadow_print (shadow, string);
945 }
946
947 static gboolean 
948 slice_value_parse (GtkCssParser *parser,
949                    GFile        *base,
950                    GValue       *value)
951 {
952   gdouble distance_top, distance_bottom;
953   gdouble distance_left, distance_right;
954   GtkSliceSideModifier mods[2];
955   GdkPixbuf *pixbuf;
956   Gtk9Slice *slice;
957   GFile *file;
958   GError *error = NULL;
959   gint i;
960   char *path;
961
962   /* Parse image url */
963   file = gtk_css_parse_url (parser, base);
964   if (!file)
965       return FALSE;
966
967   if (!_gtk_css_parser_try_double (parser, &distance_top) ||
968       !_gtk_css_parser_try_double (parser, &distance_right) ||
969       !_gtk_css_parser_try_double (parser, &distance_bottom) ||
970       !_gtk_css_parser_try_double (parser, &distance_left))
971     {
972       _gtk_css_parser_error (parser, "Expected a number");
973       g_object_unref (file);
974       return FALSE;
975     }
976
977   for (i = 0; i < 2; i++)
978     {
979       if (_gtk_css_parser_try (parser, "stretch", TRUE))
980         mods[i] = GTK_SLICE_STRETCH;
981       else if (_gtk_css_parser_try (parser, "repeat", TRUE))
982         mods[i] = GTK_SLICE_REPEAT;
983       else if (i == 0)
984         {
985           mods[1] = mods[0] = GTK_SLICE_STRETCH;
986           break;
987         }
988       else
989         mods[i] = mods[0];
990     }
991
992   path = g_file_get_path (file);
993   g_object_unref (file);
994   pixbuf = gdk_pixbuf_new_from_file (path, &error);
995   g_free (path);
996   if (!pixbuf)
997     {
998       _gtk_css_parser_take_error (parser, error);
999       return FALSE;
1000     }
1001
1002   slice = _gtk_9slice_new (pixbuf,
1003                            distance_top, distance_bottom,
1004                            distance_left, distance_right,
1005                            mods[0], mods[1]);
1006   g_object_unref (pixbuf);
1007
1008   g_value_take_boxed (value, slice);
1009   return TRUE;
1010 }
1011
1012 static gboolean 
1013 enum_value_parse (GtkCssParser *parser,
1014                   GFile        *base,
1015                   GValue       *value)
1016 {
1017   GEnumClass *enum_class;
1018   GEnumValue *enum_value;
1019   char *str;
1020
1021   str = _gtk_css_parser_try_ident (parser, TRUE);
1022   if (str == NULL)
1023     {
1024       _gtk_css_parser_error (parser, "Expected an identifier");
1025       return FALSE;
1026     }
1027
1028   enum_class = g_type_class_ref (G_VALUE_TYPE (value));
1029   enum_value = g_enum_get_value_by_nick (enum_class, str);
1030
1031   if (enum_value)
1032     g_value_set_enum (value, enum_value->value);
1033   else
1034     _gtk_css_parser_error (parser,
1035                            "Unknown value '%s' for enum type '%s'",
1036                            str, g_type_name (G_VALUE_TYPE (value)));
1037   
1038   g_type_class_unref (enum_class);
1039   g_free (str);
1040
1041   return enum_value != NULL;
1042 }
1043
1044 static void
1045 enum_value_print (const GValue *value,
1046                   GString      *string)
1047 {
1048   GEnumClass *enum_class;
1049   GEnumValue *enum_value;
1050
1051   enum_class = g_type_class_ref (G_VALUE_TYPE (value));
1052   enum_value = g_enum_get_value (enum_class, g_value_get_enum (value));
1053
1054   g_string_append (string, enum_value->value_nick);
1055
1056   g_type_class_unref (enum_class);
1057 }
1058
1059 static gboolean 
1060 flags_value_parse (GtkCssParser *parser,
1061                    GFile        *base,
1062                    GValue       *value)
1063 {
1064   GFlagsClass *flags_class;
1065   GFlagsValue *flag_value;
1066   guint flags = 0;
1067   char *str;
1068
1069   flags_class = g_type_class_ref (G_VALUE_TYPE (value));
1070
1071   do {
1072     str = _gtk_css_parser_try_ident (parser, TRUE);
1073     if (str == NULL)
1074       {
1075         _gtk_css_parser_error (parser, "Expected an identifier");
1076         g_type_class_unref (flags_class);
1077         return FALSE;
1078       }
1079
1080       flag_value = g_flags_get_value_by_nick (flags_class, str);
1081       if (!flag_value)
1082         {
1083           _gtk_css_parser_error (parser,
1084                                  "Unknown flag value '%s' for type '%s'",
1085                                  str, g_type_name (G_VALUE_TYPE (value)));
1086           /* XXX Do we want to return FALSE here? We can get
1087            * forward-compatibility for new values this way
1088            */
1089           g_free (str);
1090           g_type_class_unref (flags_class);
1091           return FALSE;
1092         }
1093
1094       g_free (str);
1095     }
1096   while (_gtk_css_parser_try (parser, ",", FALSE));
1097
1098   g_type_class_unref (flags_class);
1099
1100   g_value_set_enum (value, flags);
1101
1102   return TRUE;
1103 }
1104
1105 static void
1106 flags_value_print (const GValue *value,
1107                    GString      *string)
1108 {
1109   GFlagsClass *flags_class;
1110   guint i, flags;
1111
1112   flags_class = g_type_class_ref (G_VALUE_TYPE (value));
1113   flags = g_value_get_flags (value);
1114
1115   for (i = 0; i < flags_class->n_values; i++)
1116     {
1117       GFlagsValue *flags_value = &flags_class->values[i];
1118
1119       if (flags & flags_value->value)
1120         {
1121           if (string->len != 0)
1122             g_string_append (string, ", ");
1123
1124           g_string_append (string, flags_value->value_nick);
1125         }
1126     }
1127
1128   g_type_class_unref (flags_class);
1129 }
1130
1131 static gboolean 
1132 bindings_value_parse (GtkCssParser *parser,
1133                       GFile        *base,
1134                       GValue       *value)
1135 {
1136   GPtrArray *array;
1137   GtkBindingSet *binding_set;
1138   char *name;
1139
1140   array = g_ptr_array_new ();
1141
1142   do {
1143       name = _gtk_css_parser_try_ident (parser, TRUE);
1144       if (name == NULL)
1145         {
1146           _gtk_css_parser_error (parser, "Not a valid binding name");
1147           g_ptr_array_free (array, TRUE);
1148           return FALSE;
1149         }
1150
1151       binding_set = gtk_binding_set_find (name);
1152
1153       if (!binding_set)
1154         {
1155           _gtk_css_parser_error (parser, "No binding set named '%s'", name);
1156           g_free (name);
1157           continue;
1158         }
1159
1160       g_ptr_array_add (array, binding_set);
1161       g_free (name);
1162     }
1163   while (_gtk_css_parser_try (parser, ",", TRUE));
1164
1165   g_value_take_boxed (value, array);
1166
1167   return TRUE;
1168 }
1169
1170 static void
1171 bindings_value_print (const GValue *value,
1172                       GString      *string)
1173 {
1174   GPtrArray *array;
1175   guint i;
1176
1177   array = g_value_get_boxed (value);
1178
1179   for (i = 0; i < array->len; i++)
1180     {
1181       GtkBindingSet *binding_set = g_ptr_array_index (array, i);
1182
1183       if (i > 0)
1184         g_string_append (string, ", ");
1185       g_string_append (string, binding_set->set_name);
1186     }
1187 }
1188
1189 /*** PACKING ***/
1190
1191 static GParameter *
1192 unpack_border (const GValue *value,
1193                guint        *n_params,
1194                const char   *top,
1195                const char   *left,
1196                const char   *bottom,
1197                const char   *right)
1198 {
1199   GParameter *parameter = g_new0 (GParameter, 4);
1200   GtkBorder *border = g_value_get_boxed (value);
1201
1202   parameter[0].name = top;
1203   g_value_init (&parameter[0].value, G_TYPE_INT);
1204   g_value_set_int (&parameter[0].value, border->top);
1205   parameter[1].name = left;
1206   g_value_init (&parameter[1].value, G_TYPE_INT);
1207   g_value_set_int (&parameter[1].value, border->left);
1208   parameter[2].name = bottom;
1209   g_value_init (&parameter[2].value, G_TYPE_INT);
1210   g_value_set_int (&parameter[2].value, border->bottom);
1211   parameter[3].name = right;
1212   g_value_init (&parameter[3].value, G_TYPE_INT);
1213   g_value_set_int (&parameter[3].value, border->right);
1214
1215   *n_params = 4;
1216   return parameter;
1217 }
1218
1219 static void
1220 pack_border (GValue             *value,
1221              GtkStyleProperties *props,
1222              GtkStateFlags       state,
1223              const char         *top,
1224              const char         *left,
1225              const char         *bottom,
1226              const char         *right)
1227 {
1228   GtkBorder border;
1229   int t, l, b, r;
1230
1231   gtk_style_properties_get (props,
1232                             state,
1233                             top, &t,
1234                             left, &l,
1235                             bottom, &b,
1236                             right, &r,
1237                             NULL);
1238
1239   border.top = t;
1240   border.left = l;
1241   border.bottom = b;
1242   border.right = r;
1243
1244   g_value_set_boxed (value, &border);
1245 }
1246
1247 static GParameter *
1248 unpack_border_width (const GValue *value,
1249                      guint        *n_params)
1250 {
1251   return unpack_border (value, n_params,
1252                         "border-top-width", "border-left-width",
1253                         "border-bottom-width", "border-right-width");
1254 }
1255
1256 static void
1257 pack_border_width (GValue             *value,
1258                    GtkStyleProperties *props,
1259                    GtkStateFlags       state)
1260 {
1261   pack_border (value, props, state,
1262                "border-top-width", "border-left-width",
1263                "border-bottom-width", "border-right-width");
1264 }
1265
1266 static GParameter *
1267 unpack_padding (const GValue *value,
1268                 guint        *n_params)
1269 {
1270   return unpack_border (value, n_params,
1271                         "padding-top", "padding-left",
1272                         "padding-bottom", "padding-right");
1273 }
1274
1275 static void
1276 pack_padding (GValue             *value,
1277               GtkStyleProperties *props,
1278               GtkStateFlags       state)
1279 {
1280   pack_border (value, props, state,
1281                "padding-top", "padding-left",
1282                "padding-bottom", "padding-right");
1283 }
1284
1285 static GParameter *
1286 unpack_margin (const GValue *value,
1287                guint        *n_params)
1288 {
1289   return unpack_border (value, n_params,
1290                         "margin-top", "margin-left",
1291                         "margin-bottom", "margin-right");
1292 }
1293
1294 static void
1295 pack_margin (GValue             *value,
1296              GtkStyleProperties *props,
1297              GtkStateFlags       state)
1298 {
1299   pack_border (value, props, state,
1300                "margin-top", "margin-left",
1301                "margin-bottom", "margin-right");
1302 }
1303
1304 /*** API ***/
1305
1306 static void
1307 css_string_funcs_init (void)
1308 {
1309   if (G_LIKELY (parse_funcs != NULL))
1310     return;
1311
1312   parse_funcs = g_hash_table_new (NULL, NULL);
1313   print_funcs = g_hash_table_new (NULL, NULL);
1314
1315   register_conversion_function (GDK_TYPE_RGBA,
1316                                 rgba_value_parse,
1317                                 rgba_value_print);
1318   register_conversion_function (GDK_TYPE_COLOR,
1319                                 color_value_parse,
1320                                 color_value_print);
1321   register_conversion_function (GTK_TYPE_SYMBOLIC_COLOR,
1322                                 symbolic_color_value_parse,
1323                                 symbolic_color_value_print);
1324   register_conversion_function (PANGO_TYPE_FONT_DESCRIPTION,
1325                                 font_description_value_parse,
1326                                 font_description_value_print);
1327   register_conversion_function (G_TYPE_BOOLEAN,
1328                                 boolean_value_parse,
1329                                 boolean_value_print);
1330   register_conversion_function (G_TYPE_INT,
1331                                 int_value_parse,
1332                                 int_value_print);
1333   register_conversion_function (G_TYPE_UINT,
1334                                 uint_value_parse,
1335                                 uint_value_print);
1336   register_conversion_function (G_TYPE_DOUBLE,
1337                                 double_value_parse,
1338                                 double_value_print);
1339   register_conversion_function (G_TYPE_FLOAT,
1340                                 float_value_parse,
1341                                 float_value_print);
1342   register_conversion_function (G_TYPE_STRING,
1343                                 string_value_parse,
1344                                 string_value_print);
1345   register_conversion_function (GTK_TYPE_THEMING_ENGINE,
1346                                 theming_engine_value_parse,
1347                                 theming_engine_value_print);
1348   register_conversion_function (GTK_TYPE_ANIMATION_DESCRIPTION,
1349                                 animation_description_value_parse,
1350                                 animation_description_value_print);
1351   register_conversion_function (GTK_TYPE_BORDER,
1352                                 border_value_parse,
1353                                 border_value_print);
1354   register_conversion_function (GTK_TYPE_GRADIENT,
1355                                 gradient_value_parse,
1356                                 gradient_value_print);
1357   register_conversion_function (CAIRO_GOBJECT_TYPE_PATTERN,
1358                                 pattern_value_parse,
1359                                 NULL);
1360   register_conversion_function (GTK_TYPE_9SLICE,
1361                                 slice_value_parse,
1362                                 NULL);
1363   register_conversion_function (GTK_TYPE_SHADOW,
1364                                 shadow_value_parse,
1365                                 shadow_value_print);
1366   register_conversion_function (G_TYPE_ENUM,
1367                                 enum_value_parse,
1368                                 enum_value_print);
1369   register_conversion_function (G_TYPE_FLAGS,
1370                                 flags_value_parse,
1371                                 flags_value_print);
1372   register_conversion_function (G_TYPE_PTR_ARRAY,
1373                                 bindings_value_parse,
1374                                 bindings_value_print);
1375 }
1376
1377 gboolean
1378 _gtk_css_value_parse (GValue       *value,
1379                       GtkCssParser *parser,
1380                       GFile        *base)
1381 {
1382   ParseFunc func;
1383
1384   g_return_val_if_fail (value != NULL, FALSE);
1385   g_return_val_if_fail (parser != NULL, FALSE);
1386
1387   css_string_funcs_init ();
1388
1389   func = g_hash_table_lookup (parse_funcs,
1390                               GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
1391   if (func == NULL)
1392     func = g_hash_table_lookup (parse_funcs,
1393                                 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
1394
1395   if (func == NULL)
1396     {
1397       _gtk_css_parser_error (parser,
1398                              "Cannot convert to type '%s'",
1399                              g_type_name (G_VALUE_TYPE (value)));
1400       return FALSE;
1401     }
1402
1403   return (*func) (parser, base, value);
1404 }
1405
1406 char *
1407 _gtk_css_value_to_string (const GValue *value)
1408 {
1409   PrintFunc func;
1410
1411   css_string_funcs_init ();
1412
1413   func = g_hash_table_lookup (print_funcs,
1414                               GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
1415   if (func == NULL)
1416     func = g_hash_table_lookup (print_funcs,
1417                                 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
1418
1419   if (func)
1420     {
1421       GString *string = g_string_new (NULL);
1422       func (value, string);
1423       return g_string_free (string, FALSE);
1424     }
1425
1426   return g_strdup_value_contents (value);
1427 }
1428
1429 gboolean
1430 _gtk_style_property_is_shorthand  (const GtkStyleProperty *property)
1431 {
1432   g_return_val_if_fail (property != NULL, FALSE);
1433
1434   return property->pack_func != NULL;
1435 }
1436
1437 GParameter *
1438 _gtk_style_property_unpack (const GtkStyleProperty *property,
1439                             const GValue           *value,
1440                             guint                  *n_params)
1441 {
1442   g_return_val_if_fail (property != NULL, NULL);
1443   g_return_val_if_fail (property->unpack_func != NULL, NULL);
1444   g_return_val_if_fail (value != NULL, NULL);
1445   g_return_val_if_fail (n_params != NULL, NULL);
1446
1447   return property->unpack_func (value, n_params);
1448 }
1449
1450 void
1451 _gtk_style_property_pack (const GtkStyleProperty *property,
1452                           GtkStyleProperties     *props,
1453                           GtkStateFlags           state,
1454                           GValue                 *value)
1455 {
1456   g_return_if_fail (property != NULL);
1457   g_return_if_fail (property->pack_func != NULL);
1458   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
1459   g_return_if_fail (G_IS_VALUE (value));
1460
1461   property->pack_func (value, props, state);
1462 }
1463
1464 static void
1465 gtk_style_property_init (void)
1466 {
1467   GParamSpec *pspec;
1468
1469   if (G_LIKELY (properties))
1470     return;
1471
1472   /* stuff is never freed, so no need for free functions */
1473   properties = g_hash_table_new (g_str_hash, g_str_equal);
1474
1475   /* note that gtk_style_properties_register_property() calls this function,
1476    * so make sure we're sanely inited to avoid infloops */
1477
1478   pspec = g_param_spec_boxed ("color",
1479                               "Foreground color",
1480                               "Foreground color",
1481                               GDK_TYPE_RGBA, 0);
1482   gtk_style_param_set_inherit (pspec, TRUE);
1483   gtk_style_properties_register_property (NULL, pspec);
1484
1485   gtk_style_properties_register_property (NULL,
1486                                           g_param_spec_boxed ("background-color",
1487                                                               "Background color",
1488                                                               "Background color",
1489                                                               GDK_TYPE_RGBA, 0));
1490
1491   pspec = g_param_spec_boxed ("font",
1492                               "Font Description",
1493                               "Font Description",
1494                               PANGO_TYPE_FONT_DESCRIPTION, 0);
1495   gtk_style_param_set_inherit (pspec, TRUE);
1496   gtk_style_properties_register_property (NULL, pspec);
1497
1498   pspec = g_param_spec_boxed ("text-shadow",
1499                               "Text shadow",
1500                               "Text shadow",
1501                               GTK_TYPE_SHADOW, 0);
1502   gtk_style_param_set_inherit (pspec, TRUE);
1503   gtk_style_properties_register_property (NULL, pspec);
1504
1505   gtk_style_properties_register_property (NULL,
1506                                           g_param_spec_int ("margin-top",
1507                                                             "margin top",
1508                                                             "Margin at top",
1509                                                             0, G_MAXINT, 0, 0));
1510   gtk_style_properties_register_property (NULL,
1511                                           g_param_spec_int ("margin-left",
1512                                                             "margin left",
1513                                                             "Margin at left",
1514                                                             0, G_MAXINT, 0, 0));
1515   gtk_style_properties_register_property (NULL,
1516                                           g_param_spec_int ("margin-bottom",
1517                                                             "margin bottom",
1518                                                             "Margin at bottom",
1519                                                             0, G_MAXINT, 0, 0));
1520   gtk_style_properties_register_property (NULL,
1521                                           g_param_spec_int ("margin-right",
1522                                                             "margin right",
1523                                                             "Margin at right",
1524                                                             0, G_MAXINT, 0, 0));
1525   _gtk_style_property_register           (g_param_spec_boxed ("margin",
1526                                                               "Margin",
1527                                                               "Margin",
1528                                                               GTK_TYPE_BORDER, 0),
1529                                           NULL,
1530                                           unpack_margin,
1531                                           pack_margin);
1532   gtk_style_properties_register_property (NULL,
1533                                           g_param_spec_int ("padding-top",
1534                                                             "padding top",
1535                                                             "Padding at top",
1536                                                             0, G_MAXINT, 0, 0));
1537   gtk_style_properties_register_property (NULL,
1538                                           g_param_spec_int ("padding-left",
1539                                                             "padding left",
1540                                                             "Padding at left",
1541                                                             0, G_MAXINT, 0, 0));
1542   gtk_style_properties_register_property (NULL,
1543                                           g_param_spec_int ("padding-bottom",
1544                                                             "padding bottom",
1545                                                             "Padding at bottom",
1546                                                             0, G_MAXINT, 0, 0));
1547   gtk_style_properties_register_property (NULL,
1548                                           g_param_spec_int ("padding-right",
1549                                                             "padding right",
1550                                                             "Padding at right",
1551                                                             0, G_MAXINT, 0, 0));
1552   _gtk_style_property_register           (g_param_spec_boxed ("padding",
1553                                                               "Padding",
1554                                                               "Padding",
1555                                                               GTK_TYPE_BORDER, 0),
1556                                           NULL,
1557                                           unpack_padding,
1558                                           pack_padding);
1559   gtk_style_properties_register_property (NULL,
1560                                           g_param_spec_int ("border-top-width",
1561                                                             "border top width",
1562                                                             "Border width at top",
1563                                                             0, G_MAXINT, 0, 0));
1564   gtk_style_properties_register_property (NULL,
1565                                           g_param_spec_int ("border-left-width",
1566                                                             "border left width",
1567                                                             "Border width at left",
1568                                                             0, G_MAXINT, 0, 0));
1569   gtk_style_properties_register_property (NULL,
1570                                           g_param_spec_int ("border-bottom-width",
1571                                                             "border bottom width",
1572                                                             "Border width at bottom",
1573                                                             0, G_MAXINT, 0, 0));
1574   gtk_style_properties_register_property (NULL,
1575                                           g_param_spec_int ("border-right-width",
1576                                                             "border right width",
1577                                                             "Border width at right",
1578                                                             0, G_MAXINT, 0, 0));
1579   _gtk_style_property_register           (g_param_spec_boxed ("border-width",
1580                                                               "Border width",
1581                                                               "Border width, in pixels",
1582                                                               GTK_TYPE_BORDER, 0),
1583                                           NULL,
1584                                           unpack_border_width,
1585                                           pack_border_width);
1586   gtk_style_properties_register_property (NULL,
1587                                           g_param_spec_int ("border-radius",
1588                                                             "Border radius",
1589                                                             "Border radius, in pixels",
1590                                                             0, G_MAXINT, 0, 0));
1591   gtk_style_properties_register_property (NULL,
1592                                           g_param_spec_enum ("border-style",
1593                                                              "Border style",
1594                                                              "Border style",
1595                                                              GTK_TYPE_BORDER_STYLE,
1596                                                              GTK_BORDER_STYLE_NONE, 0));
1597   gtk_style_properties_register_property (NULL,
1598                                           g_param_spec_boxed ("border-color",
1599                                                               "Border color",
1600                                                               "Border color",
1601                                                               GDK_TYPE_RGBA, 0));
1602   gtk_style_properties_register_property (NULL,
1603                                           g_param_spec_boxed ("background-image",
1604                                                               "Background Image",
1605                                                               "Background Image",
1606                                                               CAIRO_GOBJECT_TYPE_PATTERN, 0));
1607   gtk_style_properties_register_property (NULL,
1608                                           g_param_spec_boxed ("border-image",
1609                                                               "Border Image",
1610                                                               "Border Image",
1611                                                               GTK_TYPE_9SLICE, 0));
1612   gtk_style_properties_register_property (NULL,
1613                                           g_param_spec_object ("engine",
1614                                                                "Theming Engine",
1615                                                                "Theming Engine",
1616                                                                GTK_TYPE_THEMING_ENGINE, 0));
1617   gtk_style_properties_register_property (NULL,
1618                                           g_param_spec_boxed ("transition",
1619                                                               "Transition animation description",
1620                                                               "Transition animation description",
1621                                                               GTK_TYPE_ANIMATION_DESCRIPTION, 0));
1622
1623   /* Private property holding the binding sets */
1624   gtk_style_properties_register_property (NULL,
1625                                           g_param_spec_boxed ("gtk-key-bindings",
1626                                                               "Key bindings",
1627                                                               "Key bindings",
1628                                                               G_TYPE_PTR_ARRAY, 0));
1629 }
1630
1631 const GtkStyleProperty *
1632 _gtk_style_property_lookup (const char *name)
1633 {
1634   gtk_style_property_init ();
1635
1636   return g_hash_table_lookup (properties, name);
1637 }
1638
1639 void
1640 _gtk_style_property_register (GParamSpec             *pspec,
1641                               GtkStylePropertyParser  parse_func,
1642                               GtkStyleUnpackFunc      unpack_func,
1643                               GtkStylePackFunc        pack_func)
1644 {
1645   const GtkStyleProperty *existing;
1646   GtkStyleProperty *node;
1647
1648   g_return_if_fail ((pack_func == NULL) == (unpack_func == NULL));
1649
1650   gtk_style_property_init ();
1651
1652   existing = _gtk_style_property_lookup (pspec->name);
1653   if (existing != NULL)
1654     {
1655       g_warning ("Property \"%s\" was already registered with type %s",
1656                  pspec->name, g_type_name (existing->pspec->value_type));
1657       return;
1658     }
1659
1660   node = g_slice_new0 (GtkStyleProperty);
1661   node->pspec = pspec;
1662   node->parse_func = parse_func;
1663   node->pack_func = pack_func;
1664   node->unpack_func = unpack_func;
1665
1666   g_hash_table_insert (properties, pspec->name, node);
1667 }