]> Pileus Git - ~andy/gtk/blob - gtk/gtkstyleproperty.c
styleproperty: Parse shadow options in any order
[~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 /*** API ***/
1683
1684 static void
1685 css_string_funcs_init (void)
1686 {
1687   if (G_LIKELY (parse_funcs != NULL))
1688     return;
1689
1690   parse_funcs = g_hash_table_new (NULL, NULL);
1691   print_funcs = g_hash_table_new (NULL, NULL);
1692
1693   register_conversion_function (GDK_TYPE_RGBA,
1694                                 rgba_value_parse,
1695                                 rgba_value_print);
1696   register_conversion_function (GDK_TYPE_COLOR,
1697                                 color_value_parse,
1698                                 color_value_print);
1699   register_conversion_function (GTK_TYPE_SYMBOLIC_COLOR,
1700                                 symbolic_color_value_parse,
1701                                 symbolic_color_value_print);
1702   register_conversion_function (PANGO_TYPE_FONT_DESCRIPTION,
1703                                 font_description_value_parse,
1704                                 font_description_value_print);
1705   register_conversion_function (G_TYPE_BOOLEAN,
1706                                 boolean_value_parse,
1707                                 boolean_value_print);
1708   register_conversion_function (G_TYPE_INT,
1709                                 int_value_parse,
1710                                 int_value_print);
1711   register_conversion_function (G_TYPE_UINT,
1712                                 uint_value_parse,
1713                                 uint_value_print);
1714   register_conversion_function (G_TYPE_DOUBLE,
1715                                 double_value_parse,
1716                                 double_value_print);
1717   register_conversion_function (G_TYPE_FLOAT,
1718                                 float_value_parse,
1719                                 float_value_print);
1720   register_conversion_function (G_TYPE_STRING,
1721                                 string_value_parse,
1722                                 string_value_print);
1723   register_conversion_function (GTK_TYPE_THEMING_ENGINE,
1724                                 theming_engine_value_parse,
1725                                 theming_engine_value_print);
1726   register_conversion_function (GTK_TYPE_ANIMATION_DESCRIPTION,
1727                                 animation_description_value_parse,
1728                                 animation_description_value_print);
1729   register_conversion_function (GTK_TYPE_BORDER,
1730                                 border_value_parse,
1731                                 border_value_print);
1732   register_conversion_function (GTK_TYPE_GRADIENT,
1733                                 gradient_value_parse,
1734                                 gradient_value_print);
1735   register_conversion_function (CAIRO_GOBJECT_TYPE_PATTERN,
1736                                 pattern_value_parse,
1737                                 NULL);
1738   register_conversion_function (GTK_TYPE_BORDER_IMAGE,
1739                                 border_image_value_parse,
1740                                 NULL);
1741   register_conversion_function (GTK_TYPE_CSS_BORDER_IMAGE_REPEAT,
1742                                 border_image_repeat_value_parse,
1743                                 border_image_repeat_value_print);
1744   register_conversion_function (GTK_TYPE_SHADOW,
1745                                 shadow_value_parse,
1746                                 shadow_value_print);
1747   register_conversion_function (G_TYPE_ENUM,
1748                                 enum_value_parse,
1749                                 enum_value_print);
1750   register_conversion_function (G_TYPE_FLAGS,
1751                                 flags_value_parse,
1752                                 flags_value_print);
1753 }
1754
1755 gboolean
1756 _gtk_style_property_parse_value (const GtkStyleProperty *property,
1757                                  GValue                 *value,
1758                                  GtkCssParser           *parser,
1759                                  GFile                  *base)
1760 {
1761   GtkStyleParseFunc func;
1762
1763   g_return_val_if_fail (value != NULL, FALSE);
1764   g_return_val_if_fail (parser != NULL, FALSE);
1765
1766   css_string_funcs_init ();
1767
1768   if (property)
1769     {
1770       if (_gtk_css_parser_try (parser, "none", TRUE))
1771         {
1772           /* Insert the default value, so it has an opportunity
1773            * to override other style providers when merged
1774            */
1775           g_param_value_set_default (property->pspec, value);
1776           return TRUE;
1777         }
1778       else if (property->property_parse_func)
1779         {
1780           GError *error = NULL;
1781           char *value_str;
1782           gboolean success;
1783           
1784           value_str = _gtk_css_parser_read_value (parser);
1785           if (value_str == NULL)
1786             return FALSE;
1787           
1788           success = (*property->property_parse_func) (value_str, value, &error);
1789
1790           g_free (value_str);
1791
1792           return success;
1793         }
1794
1795       func = property->parse_func;
1796     }
1797   else
1798     func = NULL;
1799
1800   if (func == NULL)
1801     func = g_hash_table_lookup (parse_funcs,
1802                                 GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
1803   if (func == NULL)
1804     func = g_hash_table_lookup (parse_funcs,
1805                                 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
1806
1807   if (func == NULL)
1808     {
1809       _gtk_css_parser_error (parser,
1810                              "Cannot convert to type '%s'",
1811                              g_type_name (G_VALUE_TYPE (value)));
1812       return FALSE;
1813     }
1814
1815   return (*func) (parser, base, value);
1816 }
1817
1818 void
1819 _gtk_style_property_print_value (const GtkStyleProperty *property,
1820                                  const GValue           *value,
1821                                  GString                *string)
1822 {
1823   GtkStylePrintFunc func;
1824
1825   css_string_funcs_init ();
1826
1827   if (property)
1828     func = property->print_func;
1829   else
1830     func = NULL;
1831
1832   if (func == NULL)
1833     func = g_hash_table_lookup (print_funcs,
1834                                 GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
1835   if (func == NULL)
1836     func = g_hash_table_lookup (print_funcs,
1837                                 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
1838
1839   if (func == NULL)
1840     {
1841       char *s = g_strdup_value_contents (value);
1842       g_string_append (string, s);
1843       g_free (s);
1844       return;
1845     }
1846   
1847   func (value, string);
1848 }
1849
1850 void
1851 _gtk_style_property_default_value (const GtkStyleProperty *property,
1852                                    GtkStyleProperties     *properties,
1853                                    GValue                 *value)
1854 {
1855   if (property->default_value_func)
1856     property->default_value_func (properties, value);
1857   else if (property->pspec->value_type == GTK_TYPE_THEMING_ENGINE)
1858     g_value_set_object (value, gtk_theming_engine_load (NULL));
1859   else if (property->pspec->value_type == PANGO_TYPE_FONT_DESCRIPTION)
1860     g_value_take_boxed (value, pango_font_description_from_string ("Sans 10"));
1861   else if (property->pspec->value_type == GDK_TYPE_RGBA)
1862     {
1863       GdkRGBA color;
1864       gdk_rgba_parse (&color, "pink");
1865       g_value_set_boxed (value, &color);
1866     }
1867   else if (property->pspec->value_type == GTK_TYPE_BORDER)
1868     {
1869       g_value_take_boxed (value, gtk_border_new ());
1870     }
1871   else
1872     g_param_value_set_default (property->pspec, value);
1873 }
1874
1875 gboolean
1876 _gtk_style_property_is_inherit (const GtkStyleProperty *property)
1877 {
1878   g_return_val_if_fail (property != NULL, FALSE);
1879
1880   return property->flags & GTK_STYLE_PROPERTY_INHERIT ? TRUE : FALSE;
1881 }
1882
1883 static gboolean
1884 resolve_color (GtkStyleProperties *props,
1885                GValue             *value)
1886 {
1887   GdkRGBA color;
1888
1889   /* Resolve symbolic color to GdkRGBA */
1890   if (!gtk_symbolic_color_resolve (g_value_get_boxed (value), props, &color))
1891     return FALSE;
1892
1893   /* Store it back, this is where GdkRGBA caching happens */
1894   g_value_unset (value);
1895   g_value_init (value, GDK_TYPE_RGBA);
1896   g_value_set_boxed (value, &color);
1897
1898   return TRUE;
1899 }
1900
1901 static gboolean
1902 resolve_color_rgb (GtkStyleProperties *props,
1903                    GValue             *value)
1904 {
1905   GdkColor color = { 0 };
1906   GdkRGBA rgba;
1907
1908   if (!gtk_symbolic_color_resolve (g_value_get_boxed (value), props, &rgba))
1909     return FALSE;
1910
1911   color.red = rgba.red * 65535. + 0.5;
1912   color.green = rgba.green * 65535. + 0.5;
1913   color.blue = rgba.blue * 65535. + 0.5;
1914
1915   g_value_unset (value);
1916   g_value_init (value, GDK_TYPE_COLOR);
1917   g_value_set_boxed (value, &color);
1918
1919   return TRUE;
1920 }
1921
1922 static gboolean
1923 resolve_gradient (GtkStyleProperties *props,
1924                   GValue             *value)
1925 {
1926   cairo_pattern_t *gradient;
1927
1928   if (!gtk_gradient_resolve (g_value_get_boxed (value), props, &gradient))
1929     return FALSE;
1930
1931   /* Store it back, this is where cairo_pattern_t caching happens */
1932   g_value_unset (value);
1933   g_value_init (value, CAIRO_GOBJECT_TYPE_PATTERN);
1934   g_value_take_boxed (value, gradient);
1935
1936   return TRUE;
1937 }
1938
1939 static gboolean
1940 resolve_shadow (GtkStyleProperties *props,
1941                 GValue *value)
1942 {
1943   GtkShadow *resolved, *base;
1944
1945   base = g_value_get_boxed (value);
1946
1947   if (base == NULL)
1948     return TRUE;
1949   
1950   if (_gtk_shadow_get_resolved (base))
1951     return TRUE;
1952
1953   resolved = _gtk_shadow_resolve (base, props);
1954   if (resolved == NULL)
1955     return FALSE;
1956
1957   g_value_take_boxed (value, resolved);
1958
1959   return TRUE;
1960 }
1961
1962 void
1963 _gtk_style_property_resolve (const GtkStyleProperty *property,
1964                              GtkStyleProperties     *props,
1965                              GValue                 *val)
1966 {
1967   if (G_VALUE_TYPE (val) == GTK_TYPE_SYMBOLIC_COLOR)
1968     {
1969       if (property->pspec->value_type == GDK_TYPE_RGBA)
1970         {
1971           if (!resolve_color (props, val))
1972             _gtk_style_property_resolve (property, props, val);
1973         }
1974       else if (property->pspec->value_type == GDK_TYPE_COLOR)
1975         {
1976           if (!resolve_color_rgb (props, val))
1977             _gtk_style_property_resolve (property, props, val);
1978         }
1979       else
1980         _gtk_style_property_resolve (property, props, val);
1981     }
1982   else if (G_VALUE_TYPE (val) == GTK_TYPE_GRADIENT)
1983     {
1984       g_return_if_fail (property->pspec->value_type == CAIRO_GOBJECT_TYPE_PATTERN);
1985
1986       if (!resolve_gradient (props, val))
1987         _gtk_style_property_resolve (property, props, val);
1988     }
1989   else if (G_VALUE_TYPE (val) == GTK_TYPE_SHADOW)
1990     {
1991       if (!resolve_shadow (props, val))
1992         _gtk_style_property_resolve (property, props, val);
1993     }
1994 }
1995
1996 gboolean
1997 _gtk_style_property_is_shorthand  (const GtkStyleProperty *property)
1998 {
1999   g_return_val_if_fail (property != NULL, FALSE);
2000
2001   return property->pack_func != NULL;
2002 }
2003
2004 GParameter *
2005 _gtk_style_property_unpack (const GtkStyleProperty *property,
2006                             const GValue           *value,
2007                             guint                  *n_params)
2008 {
2009   g_return_val_if_fail (property != NULL, NULL);
2010   g_return_val_if_fail (property->unpack_func != NULL, NULL);
2011   g_return_val_if_fail (value != NULL, NULL);
2012   g_return_val_if_fail (n_params != NULL, NULL);
2013
2014   return property->unpack_func (value, n_params);
2015 }
2016
2017 void
2018 _gtk_style_property_pack (const GtkStyleProperty *property,
2019                           GtkStyleProperties     *props,
2020                           GtkStateFlags           state,
2021                           GValue                 *value)
2022 {
2023   g_return_if_fail (property != NULL);
2024   g_return_if_fail (property->pack_func != NULL);
2025   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
2026   g_return_if_fail (G_IS_VALUE (value));
2027
2028   property->pack_func (value, props, state);
2029 }
2030
2031 static void
2032 gtk_style_property_init (void)
2033 {
2034   if (G_LIKELY (properties))
2035     return;
2036
2037   /* stuff is never freed, so no need for free functions */
2038   properties = g_hash_table_new (g_str_hash, g_str_equal);
2039
2040   /* note that gtk_style_properties_register_property() calls this function,
2041    * so make sure we're sanely inited to avoid infloops */
2042
2043   _gtk_style_property_register           (g_param_spec_boxed ("color",
2044                                           "Foreground color",
2045                                           "Foreground color",
2046                                           GDK_TYPE_RGBA, 0),
2047                                           GTK_STYLE_PROPERTY_INHERIT,
2048                                           NULL,
2049                                           NULL,
2050                                           NULL,
2051                                           NULL,
2052                                           NULL,
2053                                           NULL);
2054
2055   gtk_style_properties_register_property (NULL,
2056                                           g_param_spec_boxed ("background-color",
2057                                                               "Background color",
2058                                                               "Background color",
2059                                                               GDK_TYPE_RGBA, 0));
2060
2061   _gtk_style_property_register           (g_param_spec_boxed ("font",
2062                                                               "Font Description",
2063                                                               "Font Description",
2064                                                               PANGO_TYPE_FONT_DESCRIPTION, 0),
2065                                           GTK_STYLE_PROPERTY_INHERIT,
2066                                           NULL,
2067                                           NULL,
2068                                           NULL,
2069                                           font_description_value_parse,
2070                                           font_description_value_print,
2071                                           NULL);
2072
2073   _gtk_style_property_register           (g_param_spec_boxed ("text-shadow",
2074                                                               "Text shadow",
2075                                                               "Text shadow",
2076                                                               GTK_TYPE_SHADOW, 0),
2077                                           GTK_STYLE_PROPERTY_INHERIT,
2078                                           NULL,
2079                                           NULL,
2080                                           NULL,
2081                                           NULL,
2082                                           NULL,
2083                                           NULL);
2084
2085   _gtk_style_property_register           (g_param_spec_boxed ("icon-shadow",
2086                                                               "Icon shadow",
2087                                                               "Icon shadow",
2088                                                               GTK_TYPE_SHADOW, 0),
2089                                           GTK_STYLE_PROPERTY_INHERIT,
2090                                           NULL,
2091                                           NULL,
2092                                           NULL,
2093                                           NULL,
2094                                           NULL,
2095                                           NULL);
2096
2097   gtk_style_properties_register_property (NULL,
2098                                           g_param_spec_boxed ("box-shadow",
2099                                                               "Box shadow",
2100                                                               "Box shadow",
2101                                                               GTK_TYPE_SHADOW, 0));
2102   gtk_style_properties_register_property (NULL,
2103                                           g_param_spec_int ("margin-top",
2104                                                             "margin top",
2105                                                             "Margin at top",
2106                                                             0, G_MAXINT, 0, 0));
2107   gtk_style_properties_register_property (NULL,
2108                                           g_param_spec_int ("margin-left",
2109                                                             "margin left",
2110                                                             "Margin at left",
2111                                                             0, G_MAXINT, 0, 0));
2112   gtk_style_properties_register_property (NULL,
2113                                           g_param_spec_int ("margin-bottom",
2114                                                             "margin bottom",
2115                                                             "Margin at bottom",
2116                                                             0, G_MAXINT, 0, 0));
2117   gtk_style_properties_register_property (NULL,
2118                                           g_param_spec_int ("margin-right",
2119                                                             "margin right",
2120                                                             "Margin at right",
2121                                                             0, G_MAXINT, 0, 0));
2122   _gtk_style_property_register           (g_param_spec_boxed ("margin",
2123                                                               "Margin",
2124                                                               "Margin",
2125                                                               GTK_TYPE_BORDER, 0),
2126                                           0,
2127                                           NULL,
2128                                           unpack_margin,
2129                                           pack_margin,
2130                                           NULL,
2131                                           NULL,
2132                                           NULL);
2133   gtk_style_properties_register_property (NULL,
2134                                           g_param_spec_int ("padding-top",
2135                                                             "padding top",
2136                                                             "Padding at top",
2137                                                             0, G_MAXINT, 0, 0));
2138   gtk_style_properties_register_property (NULL,
2139                                           g_param_spec_int ("padding-left",
2140                                                             "padding left",
2141                                                             "Padding at left",
2142                                                             0, G_MAXINT, 0, 0));
2143   gtk_style_properties_register_property (NULL,
2144                                           g_param_spec_int ("padding-bottom",
2145                                                             "padding bottom",
2146                                                             "Padding at bottom",
2147                                                             0, G_MAXINT, 0, 0));
2148   gtk_style_properties_register_property (NULL,
2149                                           g_param_spec_int ("padding-right",
2150                                                             "padding right",
2151                                                             "Padding at right",
2152                                                             0, G_MAXINT, 0, 0));
2153   _gtk_style_property_register           (g_param_spec_boxed ("padding",
2154                                                               "Padding",
2155                                                               "Padding",
2156                                                               GTK_TYPE_BORDER, 0),
2157                                           0,
2158                                           NULL,
2159                                           unpack_padding,
2160                                           pack_padding,
2161                                           NULL,
2162                                           NULL,
2163                                           NULL);
2164   gtk_style_properties_register_property (NULL,
2165                                           g_param_spec_int ("border-top-width",
2166                                                             "border top width",
2167                                                             "Border width at top",
2168                                                             0, G_MAXINT, 0, 0));
2169   gtk_style_properties_register_property (NULL,
2170                                           g_param_spec_int ("border-left-width",
2171                                                             "border left width",
2172                                                             "Border width at left",
2173                                                             0, G_MAXINT, 0, 0));
2174   gtk_style_properties_register_property (NULL,
2175                                           g_param_spec_int ("border-bottom-width",
2176                                                             "border bottom width",
2177                                                             "Border width at bottom",
2178                                                             0, G_MAXINT, 0, 0));
2179   gtk_style_properties_register_property (NULL,
2180                                           g_param_spec_int ("border-right-width",
2181                                                             "border right width",
2182                                                             "Border width at right",
2183                                                             0, G_MAXINT, 0, 0));
2184   _gtk_style_property_register           (g_param_spec_boxed ("border-width",
2185                                                               "Border width",
2186                                                               "Border width, in pixels",
2187                                                               GTK_TYPE_BORDER, 0),
2188                                           0,
2189                                           NULL,
2190                                           unpack_border_width,
2191                                           pack_border_width,
2192                                           NULL,
2193                                           NULL,
2194                                           NULL);
2195
2196   _gtk_style_property_register           (g_param_spec_boxed ("border-top-left-radius",
2197                                                               "Border top left radius",
2198                                                               "Border radius of top left corner, in pixels",
2199                                                               GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
2200                                           0,
2201                                           NULL,
2202                                           NULL,
2203                                           NULL,
2204                                           border_corner_radius_value_parse,
2205                                           border_corner_radius_value_print,
2206                                           NULL);
2207   _gtk_style_property_register           (g_param_spec_boxed ("border-top-right-radius",
2208                                                               "Border top right radius",
2209                                                               "Border radius of top right corner, in pixels",
2210                                                               GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
2211                                           0,
2212                                           NULL,
2213                                           NULL,
2214                                           NULL,
2215                                           border_corner_radius_value_parse,
2216                                           border_corner_radius_value_print,
2217                                           NULL);
2218   _gtk_style_property_register           (g_param_spec_boxed ("border-bottom-right-radius",
2219                                                               "Border bottom right radius",
2220                                                               "Border radius of bottom right corner, in pixels",
2221                                                               GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
2222                                           0,
2223                                           NULL,
2224                                           NULL,
2225                                           NULL,
2226                                           border_corner_radius_value_parse,
2227                                           border_corner_radius_value_print,
2228                                           NULL);
2229   _gtk_style_property_register           (g_param_spec_boxed ("border-bottom-left-radius",
2230                                                               "Border bottom left radius",
2231                                                               "Border radius of bottom left corner, in pixels",
2232                                                               GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
2233                                           0,
2234                                           NULL,
2235                                           NULL,
2236                                           NULL,
2237                                           border_corner_radius_value_parse,
2238                                           border_corner_radius_value_print,
2239                                           NULL);
2240   _gtk_style_property_register           (g_param_spec_int ("border-radius",
2241                                                             "Border radius",
2242                                                             "Border radius, in pixels",
2243                                                             0, G_MAXINT, 0, 0),
2244                                           0,
2245                                           NULL,
2246                                           unpack_border_radius,
2247                                           pack_border_radius,
2248                                           border_radius_value_parse,
2249                                           border_radius_value_print,
2250                                           NULL);
2251
2252   gtk_style_properties_register_property (NULL,
2253                                           g_param_spec_enum ("border-style",
2254                                                              "Border style",
2255                                                              "Border style",
2256                                                              GTK_TYPE_BORDER_STYLE,
2257                                                              GTK_BORDER_STYLE_NONE, 0));
2258   gtk_style_properties_register_property (NULL,
2259                                           g_param_spec_boxed ("border-color",
2260                                                               "Border color",
2261                                                               "Border color",
2262                                                               GDK_TYPE_RGBA, 0));
2263   gtk_style_properties_register_property (NULL,
2264                                           g_param_spec_boxed ("background-image",
2265                                                               "Background Image",
2266                                                               "Background Image",
2267                                                               CAIRO_GOBJECT_TYPE_PATTERN, 0));
2268   gtk_style_properties_register_property (NULL,
2269                                           g_param_spec_boxed ("border-image-source",
2270                                                               "Border image source",
2271                                                               "Border image source",
2272                                                               CAIRO_GOBJECT_TYPE_PATTERN, 0));
2273   gtk_style_properties_register_property (NULL,
2274                                           g_param_spec_boxed ("border-image-repeat",
2275                                                               "Border image repeat",
2276                                                               "Border image repeat",
2277                                                               GTK_TYPE_CSS_BORDER_IMAGE_REPEAT, 0));
2278   gtk_style_properties_register_property (NULL,
2279                                           g_param_spec_boxed ("border-image-slice",
2280                                                               "Border image slice",
2281                                                               "Border image slice",
2282                                                               GTK_TYPE_BORDER, 0));
2283   gtk_style_properties_register_property (NULL,
2284                                           g_param_spec_boxed ("border-image-width",
2285                                                               "Border image width",
2286                                                               "Border image width",
2287                                                               GTK_TYPE_BORDER, 0));  
2288   _gtk_style_property_register           (g_param_spec_boxed ("border-image",
2289                                                               "Border Image",
2290                                                               "Border Image",
2291                                                               GTK_TYPE_BORDER_IMAGE, 0),
2292                                           0,
2293                                           NULL,
2294                                           _gtk_border_image_unpack,
2295                                           _gtk_border_image_pack,
2296                                           NULL,
2297                                           NULL,
2298                                           NULL);
2299   gtk_style_properties_register_property (NULL,
2300                                           g_param_spec_object ("engine",
2301                                                                "Theming Engine",
2302                                                                "Theming Engine",
2303                                                                GTK_TYPE_THEMING_ENGINE, 0));
2304   gtk_style_properties_register_property (NULL,
2305                                           g_param_spec_boxed ("transition",
2306                                                               "Transition animation description",
2307                                                               "Transition animation description",
2308                                                               GTK_TYPE_ANIMATION_DESCRIPTION, 0));
2309
2310   /* Private property holding the binding sets */
2311   _gtk_style_property_register           (g_param_spec_boxed ("gtk-key-bindings",
2312                                                               "Key bindings",
2313                                                               "Key bindings",
2314                                                               G_TYPE_PTR_ARRAY, 0),
2315                                           0,
2316                                           NULL,
2317                                           NULL,
2318                                           NULL,
2319                                           bindings_value_parse,
2320                                           bindings_value_print,
2321                                           NULL);
2322 }
2323
2324 const GtkStyleProperty *
2325 _gtk_style_property_lookup (const char *name)
2326 {
2327   gtk_style_property_init ();
2328
2329   return g_hash_table_lookup (properties, name);
2330 }
2331
2332 void
2333 _gtk_style_property_register (GParamSpec               *pspec,
2334                               GtkStylePropertyFlags     flags,
2335                               GtkStylePropertyParser    property_parse_func,
2336                               GtkStyleUnpackFunc        unpack_func,
2337                               GtkStylePackFunc          pack_func,
2338                               GtkStyleParseFunc         parse_func,
2339                               GtkStylePrintFunc         print_func,
2340                               GtkStyleDefaultValueFunc  default_value_func)
2341 {
2342   const GtkStyleProperty *existing;
2343   GtkStyleProperty *node;
2344
2345   g_return_if_fail ((pack_func == NULL) == (unpack_func == NULL));
2346
2347   gtk_style_property_init ();
2348
2349   existing = _gtk_style_property_lookup (pspec->name);
2350   if (existing != NULL)
2351     {
2352       g_warning ("Property \"%s\" was already registered with type %s",
2353                  pspec->name, g_type_name (existing->pspec->value_type));
2354       return;
2355     }
2356
2357   node = g_slice_new0 (GtkStyleProperty);
2358   node->flags = flags;
2359   node->pspec = pspec;
2360   node->property_parse_func = property_parse_func;
2361   node->pack_func = pack_func;
2362   node->unpack_func = unpack_func;
2363   node->parse_func = parse_func;
2364   node->print_func = print_func;
2365   node->default_value_func = default_value_func;
2366
2367   g_hash_table_insert (properties, pspec->name, node);
2368 }