]> Pileus Git - ~andy/gtk/blob - gtk/gtkstyleproperty.c
border-image: add support for border-image-width too
[~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 inset;
888   gdouble hoffset, voffset, blur, spread;
889   GtkSymbolicColor *color;
890   GtkShadow *shadow;
891
892   shadow = _gtk_shadow_new ();
893
894   do
895     {
896       inset = _gtk_css_parser_try (parser, "inset", TRUE);
897
898       if (!_gtk_css_parser_try_double (parser, &hoffset) ||
899           !_gtk_css_parser_try_double (parser, &voffset))
900         {
901           _gtk_css_parser_error (parser, "Horizontal and vertical offsets are required");
902           _gtk_shadow_unref (shadow);
903           return FALSE;
904         }
905
906       if (!_gtk_css_parser_try_double (parser, &blur))
907         blur = 0;
908
909       if (!_gtk_css_parser_try_double (parser, &spread))
910         spread = 0;
911
912       /* XXX: the color is optional and UA-defined if it's missing,
913        * but it doesn't really make sense for us...
914        */
915       color = _gtk_css_parser_read_symbolic_color (parser);
916
917       if (color == NULL)
918         {
919           _gtk_shadow_unref (shadow);
920           return FALSE;
921         }
922
923       _gtk_shadow_append (shadow,
924                           hoffset, voffset,
925                           blur, spread,
926                           inset, color);
927
928       gtk_symbolic_color_unref (color);
929
930     }
931   while (_gtk_css_parser_try (parser, ",", TRUE));
932
933   g_value_take_boxed (value, shadow);
934   return TRUE;
935 }
936
937 static void
938 shadow_value_print (const GValue *value,
939                     GString      *string)
940 {
941   GtkShadow *shadow;
942
943   shadow = g_value_get_boxed (value);
944
945   if (shadow == NULL)
946     g_string_append (string, "none");
947   else
948     _gtk_shadow_print (shadow, string);
949 }
950
951 static gboolean
952 border_image_repeat_value_parse (GtkCssParser *parser,
953                                  GFile *file,
954                                  GValue *value)
955 {
956   GtkCssBorderImageRepeat image_repeat;
957   GtkCssRepeatStyle styles[2];
958   gint i;
959
960   for (i = 0; i < 2; i++)
961     {
962       if (_gtk_css_parser_try (parser, "stretch", TRUE))
963         styles[i] = GTK_CSS_REPEAT_STYLE_NONE;
964       else if (_gtk_css_parser_try (parser, "repeat", TRUE))
965         styles[i] = GTK_CSS_REPEAT_STYLE_REPEAT;
966       else if (_gtk_css_parser_try (parser, "round", TRUE))
967         styles[i] = GTK_CSS_REPEAT_STYLE_ROUND;
968       else if (_gtk_css_parser_try (parser, "space", TRUE))
969         styles[i] = GTK_CSS_REPEAT_STYLE_SPACE;
970       else if (i == 0)
971         {
972           styles[1] = styles[0] = GTK_CSS_REPEAT_STYLE_NONE;
973           break;
974         }
975       else
976         styles[i] = styles[0];
977     }
978
979   image_repeat.hrepeat = styles[0];
980   image_repeat.vrepeat = styles[1];
981
982   g_value_set_boxed (value, &image_repeat);
983
984   return TRUE;
985 }
986
987 static const gchar *
988 border_image_repeat_style_to_string (GtkCssRepeatStyle repeat)
989 {
990   switch (repeat)
991     {
992     case GTK_CSS_REPEAT_STYLE_NONE:
993       return "stretch";
994     case GTK_CSS_REPEAT_STYLE_REPEAT:
995       return "repeat";
996     case GTK_CSS_REPEAT_STYLE_ROUND:
997       return "round";
998     case GTK_CSS_REPEAT_STYLE_SPACE:
999       return "space";
1000     default:
1001       return NULL;
1002     }
1003 }
1004
1005 static void
1006 border_image_repeat_value_print (const GValue *value,
1007                                  GString      *string)
1008 {
1009   GtkCssBorderImageRepeat *image_repeat;
1010
1011   image_repeat = g_value_get_boxed (value);
1012
1013   g_string_append_printf (string, "%s %s",
1014                           border_image_repeat_style_to_string (image_repeat->hrepeat),
1015                           border_image_repeat_style_to_string (image_repeat->vrepeat));
1016 }
1017
1018 static gboolean
1019 border_image_value_parse (GtkCssParser *parser,
1020                           GFile *base,
1021                           GValue *value)
1022 {
1023   GValue temp = { 0, };
1024   cairo_pattern_t *pattern = NULL;
1025   GtkGradient *gradient = NULL;
1026   GtkBorder slice, *width = NULL, *parsed_slice;
1027   GtkCssBorderImageRepeat repeat, *parsed_repeat;
1028   gboolean retval = FALSE;
1029   GtkBorderImage *image = NULL;
1030
1031   g_value_init (&temp, CAIRO_GOBJECT_TYPE_PATTERN);
1032
1033   if (!pattern_value_parse (parser, base, &temp))
1034     return FALSE;
1035
1036   if (G_VALUE_TYPE (&temp) == GTK_TYPE_GRADIENT)
1037     gradient = g_value_dup_boxed (&temp);
1038   else
1039     pattern = g_value_dup_boxed (&temp);
1040
1041   g_value_unset (&temp);
1042   g_value_init (&temp, GTK_TYPE_BORDER);
1043
1044   if (!border_value_parse (parser, base, &temp))
1045     goto out;
1046
1047   parsed_slice = g_value_get_boxed (&temp);
1048   slice = *parsed_slice;
1049
1050   if (_gtk_css_parser_try (parser, "/", TRUE))
1051     {
1052       g_value_unset (&temp);
1053       g_value_init (&temp, GTK_TYPE_BORDER);
1054
1055       if (!border_value_parse (parser, base, &temp))
1056         goto out;
1057
1058       width = g_value_dup_boxed (&temp);
1059     }
1060
1061   g_value_unset (&temp);
1062   g_value_init (&temp, GTK_TYPE_CSS_BORDER_IMAGE_REPEAT);
1063
1064   if (!border_image_repeat_value_parse (parser, base, &temp))
1065     goto out;
1066
1067   parsed_repeat = g_value_get_boxed (&temp);
1068   repeat = *parsed_repeat;
1069
1070   g_value_unset (&temp);
1071
1072   if (gradient != NULL)
1073     image = _gtk_border_image_new_for_gradient (gradient, &slice, width, &repeat);
1074   else if (pattern != NULL)
1075     image = _gtk_border_image_new (pattern, &slice, width, &repeat);
1076
1077   if (image != NULL)
1078     {
1079       retval = TRUE;
1080       g_value_take_boxed (value, image);
1081     }
1082
1083  out:
1084   if (pattern != NULL)
1085     cairo_pattern_destroy (pattern);
1086
1087   if (gradient != NULL)
1088     gtk_gradient_unref (gradient);
1089
1090   if (width != NULL)
1091     gtk_border_free (width);
1092
1093   return retval;
1094 }
1095
1096 static gboolean 
1097 enum_value_parse (GtkCssParser *parser,
1098                   GFile        *base,
1099                   GValue       *value)
1100 {
1101   GEnumClass *enum_class;
1102   GEnumValue *enum_value;
1103   char *str;
1104
1105   str = _gtk_css_parser_try_ident (parser, TRUE);
1106   if (str == NULL)
1107     {
1108       _gtk_css_parser_error (parser, "Expected an identifier");
1109       return FALSE;
1110     }
1111
1112   enum_class = g_type_class_ref (G_VALUE_TYPE (value));
1113   enum_value = g_enum_get_value_by_nick (enum_class, str);
1114
1115   if (enum_value)
1116     g_value_set_enum (value, enum_value->value);
1117   else
1118     _gtk_css_parser_error (parser,
1119                            "Unknown value '%s' for enum type '%s'",
1120                            str, g_type_name (G_VALUE_TYPE (value)));
1121   
1122   g_type_class_unref (enum_class);
1123   g_free (str);
1124
1125   return enum_value != NULL;
1126 }
1127
1128 static void
1129 enum_value_print (const GValue *value,
1130                   GString      *string)
1131 {
1132   GEnumClass *enum_class;
1133   GEnumValue *enum_value;
1134
1135   enum_class = g_type_class_ref (G_VALUE_TYPE (value));
1136   enum_value = g_enum_get_value (enum_class, g_value_get_enum (value));
1137
1138   g_string_append (string, enum_value->value_nick);
1139
1140   g_type_class_unref (enum_class);
1141 }
1142
1143 static gboolean 
1144 flags_value_parse (GtkCssParser *parser,
1145                    GFile        *base,
1146                    GValue       *value)
1147 {
1148   GFlagsClass *flags_class;
1149   GFlagsValue *flag_value;
1150   guint flags = 0;
1151   char *str;
1152
1153   flags_class = g_type_class_ref (G_VALUE_TYPE (value));
1154
1155   do {
1156     str = _gtk_css_parser_try_ident (parser, TRUE);
1157     if (str == NULL)
1158       {
1159         _gtk_css_parser_error (parser, "Expected an identifier");
1160         g_type_class_unref (flags_class);
1161         return FALSE;
1162       }
1163
1164       flag_value = g_flags_get_value_by_nick (flags_class, str);
1165       if (!flag_value)
1166         {
1167           _gtk_css_parser_error (parser,
1168                                  "Unknown flag value '%s' for type '%s'",
1169                                  str, g_type_name (G_VALUE_TYPE (value)));
1170           /* XXX Do we want to return FALSE here? We can get
1171            * forward-compatibility for new values this way
1172            */
1173           g_free (str);
1174           g_type_class_unref (flags_class);
1175           return FALSE;
1176         }
1177
1178       g_free (str);
1179     }
1180   while (_gtk_css_parser_try (parser, ",", FALSE));
1181
1182   g_type_class_unref (flags_class);
1183
1184   g_value_set_enum (value, flags);
1185
1186   return TRUE;
1187 }
1188
1189 static void
1190 flags_value_print (const GValue *value,
1191                    GString      *string)
1192 {
1193   GFlagsClass *flags_class;
1194   guint i, flags;
1195
1196   flags_class = g_type_class_ref (G_VALUE_TYPE (value));
1197   flags = g_value_get_flags (value);
1198
1199   for (i = 0; i < flags_class->n_values; i++)
1200     {
1201       GFlagsValue *flags_value = &flags_class->values[i];
1202
1203       if (flags & flags_value->value)
1204         {
1205           if (string->len != 0)
1206             g_string_append (string, ", ");
1207
1208           g_string_append (string, flags_value->value_nick);
1209         }
1210     }
1211
1212   g_type_class_unref (flags_class);
1213 }
1214
1215 static gboolean 
1216 bindings_value_parse (GtkCssParser *parser,
1217                       GFile        *base,
1218                       GValue       *value)
1219 {
1220   GPtrArray *array;
1221   GtkBindingSet *binding_set;
1222   char *name;
1223
1224   array = g_ptr_array_new ();
1225
1226   do {
1227       name = _gtk_css_parser_try_ident (parser, TRUE);
1228       if (name == NULL)
1229         {
1230           _gtk_css_parser_error (parser, "Not a valid binding name");
1231           g_ptr_array_free (array, TRUE);
1232           return FALSE;
1233         }
1234
1235       binding_set = gtk_binding_set_find (name);
1236
1237       if (!binding_set)
1238         {
1239           _gtk_css_parser_error (parser, "No binding set named '%s'", name);
1240           g_free (name);
1241           continue;
1242         }
1243
1244       g_ptr_array_add (array, binding_set);
1245       g_free (name);
1246     }
1247   while (_gtk_css_parser_try (parser, ",", TRUE));
1248
1249   g_value_take_boxed (value, array);
1250
1251   return TRUE;
1252 }
1253
1254 static void
1255 bindings_value_print (const GValue *value,
1256                       GString      *string)
1257 {
1258   GPtrArray *array;
1259   guint i;
1260
1261   array = g_value_get_boxed (value);
1262
1263   for (i = 0; i < array->len; i++)
1264     {
1265       GtkBindingSet *binding_set = g_ptr_array_index (array, i);
1266
1267       if (i > 0)
1268         g_string_append (string, ", ");
1269       g_string_append (string, binding_set->set_name);
1270     }
1271 }
1272
1273 static gboolean 
1274 border_corner_radius_value_parse (GtkCssParser *parser,
1275                                   GFile        *base,
1276                                   GValue       *value)
1277 {
1278   GtkCssBorderCornerRadius corner;
1279
1280   if (!_gtk_css_parser_try_double (parser, &corner.horizontal))
1281     {
1282       _gtk_css_parser_error (parser, "Expected a number");
1283       return FALSE;
1284     }
1285   else if (corner.horizontal < 0)
1286     goto negative;
1287
1288   if (!_gtk_css_parser_try_double (parser, &corner.vertical))
1289     corner.vertical = corner.horizontal;
1290   else if (corner.vertical < 0)
1291     goto negative;
1292
1293   g_value_set_boxed (value, &corner);
1294   return TRUE;
1295
1296 negative:
1297   _gtk_css_parser_error (parser, "Border radius values cannot be negative");
1298   return FALSE;
1299 }
1300
1301 static void
1302 border_corner_radius_value_print (const GValue *value,
1303                                   GString      *string)
1304 {
1305   GtkCssBorderCornerRadius *corner;
1306
1307   corner = g_value_get_boxed (value);
1308
1309   if (corner == NULL)
1310     {
1311       g_string_append (string, "none");
1312       return;
1313     }
1314
1315   string_append_double (string, corner->horizontal);
1316   if (corner->horizontal != corner->vertical)
1317     {
1318       g_string_append_c (string, ' ');
1319       string_append_double (string, corner->vertical);
1320     }
1321 }
1322
1323 static gboolean 
1324 border_radius_value_parse (GtkCssParser *parser,
1325                            GFile        *base,
1326                            GValue       *value)
1327 {
1328   GtkCssBorderRadius border;
1329
1330   if (!_gtk_css_parser_try_double (parser, &border.top_left.horizontal))
1331     {
1332       _gtk_css_parser_error (parser, "Expected a number");
1333       return FALSE;
1334     }
1335   else if (border.top_left.horizontal < 0)
1336     goto negative;
1337
1338   if (_gtk_css_parser_try_double (parser, &border.top_right.horizontal))
1339     {
1340       if (border.top_right.horizontal < 0)
1341         goto negative;
1342       if (_gtk_css_parser_try_double (parser, &border.bottom_right.horizontal))
1343         {
1344           if (border.bottom_right.horizontal < 0)
1345             goto negative;
1346           if (!_gtk_css_parser_try_double (parser, &border.bottom_left.horizontal))
1347             border.bottom_left.horizontal = border.top_right.horizontal;
1348           else if (border.bottom_left.horizontal < 0)
1349             goto negative;
1350         }
1351       else
1352         {
1353           border.bottom_right.horizontal = border.top_left.horizontal;
1354           border.bottom_left.horizontal = border.top_right.horizontal;
1355         }
1356     }
1357   else
1358     {
1359       border.top_right.horizontal = border.top_left.horizontal;
1360       border.bottom_right.horizontal = border.top_left.horizontal;
1361       border.bottom_left.horizontal = border.top_left.horizontal;
1362     }
1363
1364   if (_gtk_css_parser_try (parser, "/", TRUE))
1365     {
1366       if (!_gtk_css_parser_try_double (parser, &border.top_left.vertical))
1367         {
1368           _gtk_css_parser_error (parser, "Expected a number");
1369           return FALSE;
1370         }
1371       else if (border.top_left.vertical < 0)
1372         goto negative;
1373
1374       if (_gtk_css_parser_try_double (parser, &border.top_right.vertical))
1375         {
1376           if (border.top_right.vertical < 0)
1377             goto negative;
1378           if (_gtk_css_parser_try_double (parser, &border.bottom_right.vertical))
1379             {
1380               if (border.bottom_right.vertical < 0)
1381                 goto negative;
1382               if (!_gtk_css_parser_try_double (parser, &border.bottom_left.vertical))
1383                 border.bottom_left.vertical = border.top_right.vertical;
1384               else if (border.bottom_left.vertical < 0)
1385                 goto negative;
1386             }
1387           else
1388             {
1389               border.bottom_right.vertical = border.top_left.vertical;
1390               border.bottom_left.vertical = border.top_right.vertical;
1391             }
1392         }
1393       else
1394         {
1395           border.top_right.vertical = border.top_left.vertical;
1396           border.bottom_right.vertical = border.top_left.vertical;
1397           border.bottom_left.vertical = border.top_left.vertical;
1398         }
1399     }
1400   else
1401     {
1402       border.top_left.vertical = border.top_left.horizontal;
1403       border.top_right.vertical = border.top_right.horizontal;
1404       border.bottom_right.vertical = border.bottom_right.horizontal;
1405       border.bottom_left.vertical = border.bottom_left.horizontal;
1406     }
1407
1408   /* border-radius is an int property for backwards-compat reasons */
1409   g_value_unset (value);
1410   g_value_init (value, GTK_TYPE_CSS_BORDER_RADIUS);
1411   g_value_set_boxed (value, &border);
1412
1413   return TRUE;
1414
1415 negative:
1416   _gtk_css_parser_error (parser, "Border radius values cannot be negative");
1417   return FALSE;
1418 }
1419
1420 static void
1421 border_radius_value_print (const GValue *value,
1422                            GString      *string)
1423 {
1424   GtkCssBorderRadius *border;
1425
1426   border = g_value_get_boxed (value);
1427
1428   if (border == NULL)
1429     {
1430       g_string_append (string, "none");
1431       return;
1432     }
1433
1434   string_append_double (string, border->top_left.horizontal);
1435   if (border->top_left.horizontal != border->top_right.horizontal ||
1436       border->top_left.horizontal != border->bottom_right.horizontal ||
1437       border->top_left.horizontal != border->bottom_left.horizontal)
1438     {
1439       g_string_append_c (string, ' ');
1440       string_append_double (string, border->top_right.horizontal);
1441       if (border->top_left.horizontal != border->bottom_right.horizontal ||
1442           border->top_right.horizontal != border->bottom_left.horizontal)
1443         {
1444           g_string_append_c (string, ' ');
1445           string_append_double (string, border->bottom_right.horizontal);
1446           if (border->top_right.horizontal != border->bottom_left.horizontal)
1447             {
1448               g_string_append_c (string, ' ');
1449               string_append_double (string, border->bottom_left.horizontal);
1450             }
1451         }
1452     }
1453
1454   if (border->top_left.horizontal != border->top_left.vertical ||
1455       border->top_right.horizontal != border->top_right.vertical ||
1456       border->bottom_right.horizontal != border->bottom_right.vertical ||
1457       border->bottom_left.horizontal != border->bottom_left.vertical)
1458     {
1459       g_string_append (string, " / ");
1460       string_append_double (string, border->top_left.vertical);
1461       if (border->top_left.vertical != border->top_right.vertical ||
1462           border->top_left.vertical != border->bottom_right.vertical ||
1463           border->top_left.vertical != border->bottom_left.vertical)
1464         {
1465           g_string_append_c (string, ' ');
1466           string_append_double (string, border->top_right.vertical);
1467           if (border->top_left.vertical != border->bottom_right.vertical ||
1468               border->top_right.vertical != border->bottom_left.vertical)
1469             {
1470               g_string_append_c (string, ' ');
1471               string_append_double (string, border->bottom_right.vertical);
1472               if (border->top_right.vertical != border->bottom_left.vertical)
1473                 {
1474                   g_string_append_c (string, ' ');
1475                   string_append_double (string, border->bottom_left.vertical);
1476                 }
1477             }
1478         }
1479
1480     }
1481 }
1482
1483 /*** PACKING ***/
1484
1485 static GParameter *
1486 unpack_border (const GValue *value,
1487                guint        *n_params,
1488                const char   *top,
1489                const char   *left,
1490                const char   *bottom,
1491                const char   *right)
1492 {
1493   GParameter *parameter = g_new0 (GParameter, 4);
1494   GtkBorder *border = g_value_get_boxed (value);
1495
1496   parameter[0].name = top;
1497   g_value_init (&parameter[0].value, G_TYPE_INT);
1498   g_value_set_int (&parameter[0].value, border->top);
1499   parameter[1].name = left;
1500   g_value_init (&parameter[1].value, G_TYPE_INT);
1501   g_value_set_int (&parameter[1].value, border->left);
1502   parameter[2].name = bottom;
1503   g_value_init (&parameter[2].value, G_TYPE_INT);
1504   g_value_set_int (&parameter[2].value, border->bottom);
1505   parameter[3].name = right;
1506   g_value_init (&parameter[3].value, G_TYPE_INT);
1507   g_value_set_int (&parameter[3].value, border->right);
1508
1509   *n_params = 4;
1510   return parameter;
1511 }
1512
1513 static void
1514 pack_border (GValue             *value,
1515              GtkStyleProperties *props,
1516              GtkStateFlags       state,
1517              const char         *top,
1518              const char         *left,
1519              const char         *bottom,
1520              const char         *right)
1521 {
1522   GtkBorder border;
1523   int t, l, b, r;
1524
1525   gtk_style_properties_get (props,
1526                             state,
1527                             top, &t,
1528                             left, &l,
1529                             bottom, &b,
1530                             right, &r,
1531                             NULL);
1532
1533   border.top = t;
1534   border.left = l;
1535   border.bottom = b;
1536   border.right = r;
1537
1538   g_value_set_boxed (value, &border);
1539 }
1540
1541 static GParameter *
1542 unpack_border_width (const GValue *value,
1543                      guint        *n_params)
1544 {
1545   return unpack_border (value, n_params,
1546                         "border-top-width", "border-left-width",
1547                         "border-bottom-width", "border-right-width");
1548 }
1549
1550 static void
1551 pack_border_width (GValue             *value,
1552                    GtkStyleProperties *props,
1553                    GtkStateFlags       state)
1554 {
1555   pack_border (value, props, state,
1556                "border-top-width", "border-left-width",
1557                "border-bottom-width", "border-right-width");
1558 }
1559
1560 static GParameter *
1561 unpack_padding (const GValue *value,
1562                 guint        *n_params)
1563 {
1564   return unpack_border (value, n_params,
1565                         "padding-top", "padding-left",
1566                         "padding-bottom", "padding-right");
1567 }
1568
1569 static void
1570 pack_padding (GValue             *value,
1571               GtkStyleProperties *props,
1572               GtkStateFlags       state)
1573 {
1574   pack_border (value, props, state,
1575                "padding-top", "padding-left",
1576                "padding-bottom", "padding-right");
1577 }
1578
1579 static GParameter *
1580 unpack_margin (const GValue *value,
1581                guint        *n_params)
1582 {
1583   return unpack_border (value, n_params,
1584                         "margin-top", "margin-left",
1585                         "margin-bottom", "margin-right");
1586 }
1587
1588 static void
1589 pack_margin (GValue             *value,
1590              GtkStyleProperties *props,
1591              GtkStateFlags       state)
1592 {
1593   pack_border (value, props, state,
1594                "margin-top", "margin-left",
1595                "margin-bottom", "margin-right");
1596 }
1597
1598 static GParameter *
1599 unpack_border_radius (const GValue *value,
1600                       guint        *n_params)
1601 {
1602   GParameter *parameter = g_new0 (GParameter, 4);
1603   GtkCssBorderRadius *border;
1604   
1605   if (G_VALUE_HOLDS_BOXED (value))
1606     border = g_value_get_boxed (value);
1607   else
1608     border = NULL;
1609
1610   parameter[0].name = "border-top-left-radius";
1611   g_value_init (&parameter[0].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
1612   parameter[1].name = "border-top-right-radius";
1613   g_value_init (&parameter[1].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
1614   parameter[2].name = "border-bottom-right-radius";
1615   g_value_init (&parameter[2].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
1616   parameter[3].name = "border-bottom-left-radius";
1617   g_value_init (&parameter[3].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
1618   if (border)
1619     {
1620       g_value_set_boxed (&parameter[0].value, &border->top_left);
1621       g_value_set_boxed (&parameter[1].value, &border->top_right);
1622       g_value_set_boxed (&parameter[2].value, &border->bottom_right);
1623       g_value_set_boxed (&parameter[3].value, &border->bottom_left);
1624     }
1625
1626   *n_params = 4;
1627   return parameter;
1628 }
1629
1630 static void
1631 pack_border_radius (GValue             *value,
1632                     GtkStyleProperties *props,
1633                     GtkStateFlags       state)
1634 {
1635   GtkCssBorderCornerRadius *top_left;
1636
1637   /* NB: We are an int property, so we have to resolve to an int here.
1638    * So we just resolve to an int. We pick one and stick to it.
1639    * Lesson learned: Don't query border-radius shorthand, query the 
1640    * real properties instead. */
1641   gtk_style_properties_get (props,
1642                             state,
1643                             "border-top-left-radius", &top_left,
1644                             NULL);
1645
1646   if (top_left)
1647     g_value_set_int (value, top_left->horizontal);
1648
1649   g_free (top_left);
1650 }
1651
1652 /*** API ***/
1653
1654 static void
1655 css_string_funcs_init (void)
1656 {
1657   if (G_LIKELY (parse_funcs != NULL))
1658     return;
1659
1660   parse_funcs = g_hash_table_new (NULL, NULL);
1661   print_funcs = g_hash_table_new (NULL, NULL);
1662
1663   register_conversion_function (GDK_TYPE_RGBA,
1664                                 rgba_value_parse,
1665                                 rgba_value_print);
1666   register_conversion_function (GDK_TYPE_COLOR,
1667                                 color_value_parse,
1668                                 color_value_print);
1669   register_conversion_function (GTK_TYPE_SYMBOLIC_COLOR,
1670                                 symbolic_color_value_parse,
1671                                 symbolic_color_value_print);
1672   register_conversion_function (PANGO_TYPE_FONT_DESCRIPTION,
1673                                 font_description_value_parse,
1674                                 font_description_value_print);
1675   register_conversion_function (G_TYPE_BOOLEAN,
1676                                 boolean_value_parse,
1677                                 boolean_value_print);
1678   register_conversion_function (G_TYPE_INT,
1679                                 int_value_parse,
1680                                 int_value_print);
1681   register_conversion_function (G_TYPE_UINT,
1682                                 uint_value_parse,
1683                                 uint_value_print);
1684   register_conversion_function (G_TYPE_DOUBLE,
1685                                 double_value_parse,
1686                                 double_value_print);
1687   register_conversion_function (G_TYPE_FLOAT,
1688                                 float_value_parse,
1689                                 float_value_print);
1690   register_conversion_function (G_TYPE_STRING,
1691                                 string_value_parse,
1692                                 string_value_print);
1693   register_conversion_function (GTK_TYPE_THEMING_ENGINE,
1694                                 theming_engine_value_parse,
1695                                 theming_engine_value_print);
1696   register_conversion_function (GTK_TYPE_ANIMATION_DESCRIPTION,
1697                                 animation_description_value_parse,
1698                                 animation_description_value_print);
1699   register_conversion_function (GTK_TYPE_BORDER,
1700                                 border_value_parse,
1701                                 border_value_print);
1702   register_conversion_function (GTK_TYPE_GRADIENT,
1703                                 gradient_value_parse,
1704                                 gradient_value_print);
1705   register_conversion_function (CAIRO_GOBJECT_TYPE_PATTERN,
1706                                 pattern_value_parse,
1707                                 NULL);
1708   register_conversion_function (GTK_TYPE_BORDER_IMAGE,
1709                                 border_image_value_parse,
1710                                 NULL);
1711   register_conversion_function (GTK_TYPE_CSS_BORDER_IMAGE_REPEAT,
1712                                 border_image_repeat_value_parse,
1713                                 border_image_repeat_value_print);
1714   register_conversion_function (GTK_TYPE_SHADOW,
1715                                 shadow_value_parse,
1716                                 shadow_value_print);
1717   register_conversion_function (G_TYPE_ENUM,
1718                                 enum_value_parse,
1719                                 enum_value_print);
1720   register_conversion_function (G_TYPE_FLAGS,
1721                                 flags_value_parse,
1722                                 flags_value_print);
1723 }
1724
1725 gboolean
1726 _gtk_style_property_parse_value (const GtkStyleProperty *property,
1727                                  GValue                 *value,
1728                                  GtkCssParser           *parser,
1729                                  GFile                  *base)
1730 {
1731   GtkStyleParseFunc func;
1732
1733   g_return_val_if_fail (value != NULL, FALSE);
1734   g_return_val_if_fail (parser != NULL, FALSE);
1735
1736   css_string_funcs_init ();
1737
1738   if (property)
1739     {
1740       if (_gtk_css_parser_try (parser, "none", TRUE))
1741         {
1742           /* Insert the default value, so it has an opportunity
1743            * to override other style providers when merged
1744            */
1745           g_param_value_set_default (property->pspec, value);
1746           return TRUE;
1747         }
1748       else if (property->property_parse_func)
1749         {
1750           GError *error = NULL;
1751           char *value_str;
1752           gboolean success;
1753           
1754           value_str = _gtk_css_parser_read_value (parser);
1755           if (value_str == NULL)
1756             return FALSE;
1757           
1758           success = (*property->property_parse_func) (value_str, value, &error);
1759
1760           g_free (value_str);
1761
1762           return success;
1763         }
1764
1765       func = property->parse_func;
1766     }
1767   else
1768     func = NULL;
1769
1770   if (func == NULL)
1771     func = g_hash_table_lookup (parse_funcs,
1772                                 GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
1773   if (func == NULL)
1774     func = g_hash_table_lookup (parse_funcs,
1775                                 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
1776
1777   if (func == NULL)
1778     {
1779       _gtk_css_parser_error (parser,
1780                              "Cannot convert to type '%s'",
1781                              g_type_name (G_VALUE_TYPE (value)));
1782       return FALSE;
1783     }
1784
1785   return (*func) (parser, base, value);
1786 }
1787
1788 void
1789 _gtk_style_property_print_value (const GtkStyleProperty *property,
1790                                  const GValue           *value,
1791                                  GString                *string)
1792 {
1793   GtkStylePrintFunc func;
1794
1795   css_string_funcs_init ();
1796
1797   if (property)
1798     func = property->print_func;
1799   else
1800     func = NULL;
1801
1802   if (func == NULL)
1803     func = g_hash_table_lookup (print_funcs,
1804                                 GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
1805   if (func == NULL)
1806     func = g_hash_table_lookup (print_funcs,
1807                                 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
1808
1809   if (func == NULL)
1810     {
1811       char *s = g_strdup_value_contents (value);
1812       g_string_append (string, s);
1813       g_free (s);
1814       return;
1815     }
1816   
1817   func (value, string);
1818 }
1819
1820 void
1821 _gtk_style_property_default_value (const GtkStyleProperty *property,
1822                                    GtkStyleProperties     *properties,
1823                                    GValue                 *value)
1824 {
1825   if (property->default_value_func)
1826     property->default_value_func (properties, value);
1827   else if (property->pspec->value_type == GTK_TYPE_THEMING_ENGINE)
1828     g_value_set_object (value, gtk_theming_engine_load (NULL));
1829   else if (property->pspec->value_type == PANGO_TYPE_FONT_DESCRIPTION)
1830     g_value_take_boxed (value, pango_font_description_from_string ("Sans 10"));
1831   else if (property->pspec->value_type == GDK_TYPE_RGBA)
1832     {
1833       GdkRGBA color;
1834       gdk_rgba_parse (&color, "pink");
1835       g_value_set_boxed (value, &color);
1836     }
1837   else if (property->pspec->value_type == GTK_TYPE_BORDER)
1838     {
1839       g_value_take_boxed (value, gtk_border_new ());
1840     }
1841   else
1842     g_param_value_set_default (property->pspec, value);
1843 }
1844
1845 gboolean
1846 _gtk_style_property_is_inherit (const GtkStyleProperty *property)
1847 {
1848   g_return_val_if_fail (property != NULL, FALSE);
1849
1850   return property->flags & GTK_STYLE_PROPERTY_INHERIT ? TRUE : FALSE;
1851 }
1852
1853 static gboolean
1854 resolve_color (GtkStyleProperties *props,
1855                GValue             *value)
1856 {
1857   GdkRGBA color;
1858
1859   /* Resolve symbolic color to GdkRGBA */
1860   if (!gtk_symbolic_color_resolve (g_value_get_boxed (value), props, &color))
1861     return FALSE;
1862
1863   /* Store it back, this is where GdkRGBA caching happens */
1864   g_value_unset (value);
1865   g_value_init (value, GDK_TYPE_RGBA);
1866   g_value_set_boxed (value, &color);
1867
1868   return TRUE;
1869 }
1870
1871 static gboolean
1872 resolve_color_rgb (GtkStyleProperties *props,
1873                    GValue             *value)
1874 {
1875   GdkColor color = { 0 };
1876   GdkRGBA rgba;
1877
1878   if (!gtk_symbolic_color_resolve (g_value_get_boxed (value), props, &rgba))
1879     return FALSE;
1880
1881   color.red = rgba.red * 65535. + 0.5;
1882   color.green = rgba.green * 65535. + 0.5;
1883   color.blue = rgba.blue * 65535. + 0.5;
1884
1885   g_value_unset (value);
1886   g_value_init (value, GDK_TYPE_COLOR);
1887   g_value_set_boxed (value, &color);
1888
1889   return TRUE;
1890 }
1891
1892 static gboolean
1893 resolve_gradient (GtkStyleProperties *props,
1894                   GValue             *value)
1895 {
1896   cairo_pattern_t *gradient;
1897
1898   if (!gtk_gradient_resolve (g_value_get_boxed (value), props, &gradient))
1899     return FALSE;
1900
1901   /* Store it back, this is where cairo_pattern_t caching happens */
1902   g_value_unset (value);
1903   g_value_init (value, CAIRO_GOBJECT_TYPE_PATTERN);
1904   g_value_take_boxed (value, gradient);
1905
1906   return TRUE;
1907 }
1908
1909 static gboolean
1910 resolve_shadow (GtkStyleProperties *props,
1911                 GValue *value)
1912 {
1913   GtkShadow *resolved, *base;
1914
1915   base = g_value_get_boxed (value);
1916
1917   if (base == NULL)
1918     return TRUE;
1919   
1920   if (_gtk_shadow_get_resolved (base))
1921     return TRUE;
1922
1923   resolved = _gtk_shadow_resolve (base, props);
1924   if (resolved == NULL)
1925     return FALSE;
1926
1927   g_value_take_boxed (value, resolved);
1928
1929   return TRUE;
1930 }
1931
1932 void
1933 _gtk_style_property_resolve (const GtkStyleProperty *property,
1934                              GtkStyleProperties     *props,
1935                              GValue                 *val)
1936 {
1937   if (G_VALUE_TYPE (val) == GTK_TYPE_SYMBOLIC_COLOR)
1938     {
1939       if (property->pspec->value_type == GDK_TYPE_RGBA)
1940         {
1941           if (!resolve_color (props, val))
1942             _gtk_style_property_resolve (property, props, val);
1943         }
1944       else if (property->pspec->value_type == GDK_TYPE_COLOR)
1945         {
1946           if (!resolve_color_rgb (props, val))
1947             _gtk_style_property_resolve (property, props, val);
1948         }
1949       else
1950         _gtk_style_property_resolve (property, props, val);
1951     }
1952   else if (G_VALUE_TYPE (val) == GTK_TYPE_GRADIENT)
1953     {
1954       g_return_if_fail (property->pspec->value_type == CAIRO_GOBJECT_TYPE_PATTERN);
1955
1956       if (!resolve_gradient (props, val))
1957         _gtk_style_property_resolve (property, props, val);
1958     }
1959   else if (G_VALUE_TYPE (val) == GTK_TYPE_SHADOW)
1960     {
1961       if (!resolve_shadow (props, val))
1962         _gtk_style_property_resolve (property, props, val);
1963     }
1964 }
1965
1966 gboolean
1967 _gtk_style_property_is_shorthand  (const GtkStyleProperty *property)
1968 {
1969   g_return_val_if_fail (property != NULL, FALSE);
1970
1971   return property->pack_func != NULL;
1972 }
1973
1974 GParameter *
1975 _gtk_style_property_unpack (const GtkStyleProperty *property,
1976                             const GValue           *value,
1977                             guint                  *n_params)
1978 {
1979   g_return_val_if_fail (property != NULL, NULL);
1980   g_return_val_if_fail (property->unpack_func != NULL, NULL);
1981   g_return_val_if_fail (value != NULL, NULL);
1982   g_return_val_if_fail (n_params != NULL, NULL);
1983
1984   return property->unpack_func (value, n_params);
1985 }
1986
1987 void
1988 _gtk_style_property_pack (const GtkStyleProperty *property,
1989                           GtkStyleProperties     *props,
1990                           GtkStateFlags           state,
1991                           GValue                 *value)
1992 {
1993   g_return_if_fail (property != NULL);
1994   g_return_if_fail (property->pack_func != NULL);
1995   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
1996   g_return_if_fail (G_IS_VALUE (value));
1997
1998   property->pack_func (value, props, state);
1999 }
2000
2001 static void
2002 gtk_style_property_init (void)
2003 {
2004   if (G_LIKELY (properties))
2005     return;
2006
2007   /* stuff is never freed, so no need for free functions */
2008   properties = g_hash_table_new (g_str_hash, g_str_equal);
2009
2010   /* note that gtk_style_properties_register_property() calls this function,
2011    * so make sure we're sanely inited to avoid infloops */
2012
2013   _gtk_style_property_register           (g_param_spec_boxed ("color",
2014                                           "Foreground color",
2015                                           "Foreground color",
2016                                           GDK_TYPE_RGBA, 0),
2017                                           GTK_STYLE_PROPERTY_INHERIT,
2018                                           NULL,
2019                                           NULL,
2020                                           NULL,
2021                                           NULL,
2022                                           NULL,
2023                                           NULL);
2024
2025   gtk_style_properties_register_property (NULL,
2026                                           g_param_spec_boxed ("background-color",
2027                                                               "Background color",
2028                                                               "Background color",
2029                                                               GDK_TYPE_RGBA, 0));
2030
2031   _gtk_style_property_register           (g_param_spec_boxed ("font",
2032                                                               "Font Description",
2033                                                               "Font Description",
2034                                                               PANGO_TYPE_FONT_DESCRIPTION, 0),
2035                                           GTK_STYLE_PROPERTY_INHERIT,
2036                                           NULL,
2037                                           NULL,
2038                                           NULL,
2039                                           font_description_value_parse,
2040                                           font_description_value_print,
2041                                           NULL);
2042
2043   _gtk_style_property_register           (g_param_spec_boxed ("text-shadow",
2044                                                               "Text shadow",
2045                                                               "Text shadow",
2046                                                               GTK_TYPE_SHADOW, 0),
2047                                           GTK_STYLE_PROPERTY_INHERIT,
2048                                           NULL,
2049                                           NULL,
2050                                           NULL,
2051                                           NULL,
2052                                           NULL,
2053                                           NULL);
2054
2055   _gtk_style_property_register           (g_param_spec_boxed ("icon-shadow",
2056                                                               "Icon shadow",
2057                                                               "Icon shadow",
2058                                                               GTK_TYPE_SHADOW, 0),
2059                                           GTK_STYLE_PROPERTY_INHERIT,
2060                                           NULL,
2061                                           NULL,
2062                                           NULL,
2063                                           NULL,
2064                                           NULL,
2065                                           NULL);
2066
2067   gtk_style_properties_register_property (NULL,
2068                                           g_param_spec_int ("margin-top",
2069                                                             "margin top",
2070                                                             "Margin at top",
2071                                                             0, G_MAXINT, 0, 0));
2072   gtk_style_properties_register_property (NULL,
2073                                           g_param_spec_int ("margin-left",
2074                                                             "margin left",
2075                                                             "Margin at left",
2076                                                             0, G_MAXINT, 0, 0));
2077   gtk_style_properties_register_property (NULL,
2078                                           g_param_spec_int ("margin-bottom",
2079                                                             "margin bottom",
2080                                                             "Margin at bottom",
2081                                                             0, G_MAXINT, 0, 0));
2082   gtk_style_properties_register_property (NULL,
2083                                           g_param_spec_int ("margin-right",
2084                                                             "margin right",
2085                                                             "Margin at right",
2086                                                             0, G_MAXINT, 0, 0));
2087   _gtk_style_property_register           (g_param_spec_boxed ("margin",
2088                                                               "Margin",
2089                                                               "Margin",
2090                                                               GTK_TYPE_BORDER, 0),
2091                                           0,
2092                                           NULL,
2093                                           unpack_margin,
2094                                           pack_margin,
2095                                           NULL,
2096                                           NULL,
2097                                           NULL);
2098   gtk_style_properties_register_property (NULL,
2099                                           g_param_spec_int ("padding-top",
2100                                                             "padding top",
2101                                                             "Padding at top",
2102                                                             0, G_MAXINT, 0, 0));
2103   gtk_style_properties_register_property (NULL,
2104                                           g_param_spec_int ("padding-left",
2105                                                             "padding left",
2106                                                             "Padding at left",
2107                                                             0, G_MAXINT, 0, 0));
2108   gtk_style_properties_register_property (NULL,
2109                                           g_param_spec_int ("padding-bottom",
2110                                                             "padding bottom",
2111                                                             "Padding at bottom",
2112                                                             0, G_MAXINT, 0, 0));
2113   gtk_style_properties_register_property (NULL,
2114                                           g_param_spec_int ("padding-right",
2115                                                             "padding right",
2116                                                             "Padding at right",
2117                                                             0, G_MAXINT, 0, 0));
2118   _gtk_style_property_register           (g_param_spec_boxed ("padding",
2119                                                               "Padding",
2120                                                               "Padding",
2121                                                               GTK_TYPE_BORDER, 0),
2122                                           0,
2123                                           NULL,
2124                                           unpack_padding,
2125                                           pack_padding,
2126                                           NULL,
2127                                           NULL,
2128                                           NULL);
2129   gtk_style_properties_register_property (NULL,
2130                                           g_param_spec_int ("border-top-width",
2131                                                             "border top width",
2132                                                             "Border width at top",
2133                                                             0, G_MAXINT, 0, 0));
2134   gtk_style_properties_register_property (NULL,
2135                                           g_param_spec_int ("border-left-width",
2136                                                             "border left width",
2137                                                             "Border width at left",
2138                                                             0, G_MAXINT, 0, 0));
2139   gtk_style_properties_register_property (NULL,
2140                                           g_param_spec_int ("border-bottom-width",
2141                                                             "border bottom width",
2142                                                             "Border width at bottom",
2143                                                             0, G_MAXINT, 0, 0));
2144   gtk_style_properties_register_property (NULL,
2145                                           g_param_spec_int ("border-right-width",
2146                                                             "border right width",
2147                                                             "Border width at right",
2148                                                             0, G_MAXINT, 0, 0));
2149   _gtk_style_property_register           (g_param_spec_boxed ("border-width",
2150                                                               "Border width",
2151                                                               "Border width, in pixels",
2152                                                               GTK_TYPE_BORDER, 0),
2153                                           0,
2154                                           NULL,
2155                                           unpack_border_width,
2156                                           pack_border_width,
2157                                           NULL,
2158                                           NULL,
2159                                           NULL);
2160
2161   _gtk_style_property_register           (g_param_spec_boxed ("border-top-left-radius",
2162                                                               "Border top left radius",
2163                                                               "Border radius of top left corner, in pixels",
2164                                                               GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
2165                                           0,
2166                                           NULL,
2167                                           NULL,
2168                                           NULL,
2169                                           border_corner_radius_value_parse,
2170                                           border_corner_radius_value_print,
2171                                           NULL);
2172   _gtk_style_property_register           (g_param_spec_boxed ("border-top-right-radius",
2173                                                               "Border top right radius",
2174                                                               "Border radius of top right corner, in pixels",
2175                                                               GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
2176                                           0,
2177                                           NULL,
2178                                           NULL,
2179                                           NULL,
2180                                           border_corner_radius_value_parse,
2181                                           border_corner_radius_value_print,
2182                                           NULL);
2183   _gtk_style_property_register           (g_param_spec_boxed ("border-bottom-right-radius",
2184                                                               "Border bottom right radius",
2185                                                               "Border radius of bottom right corner, in pixels",
2186                                                               GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
2187                                           0,
2188                                           NULL,
2189                                           NULL,
2190                                           NULL,
2191                                           border_corner_radius_value_parse,
2192                                           border_corner_radius_value_print,
2193                                           NULL);
2194   _gtk_style_property_register           (g_param_spec_boxed ("border-bottom-left-radius",
2195                                                               "Border bottom left radius",
2196                                                               "Border radius of bottom left corner, in pixels",
2197                                                               GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
2198                                           0,
2199                                           NULL,
2200                                           NULL,
2201                                           NULL,
2202                                           border_corner_radius_value_parse,
2203                                           border_corner_radius_value_print,
2204                                           NULL);
2205   _gtk_style_property_register           (g_param_spec_int ("border-radius",
2206                                                             "Border radius",
2207                                                             "Border radius, in pixels",
2208                                                             0, G_MAXINT, 0, 0),
2209                                           0,
2210                                           NULL,
2211                                           unpack_border_radius,
2212                                           pack_border_radius,
2213                                           border_radius_value_parse,
2214                                           border_radius_value_print,
2215                                           NULL);
2216
2217   gtk_style_properties_register_property (NULL,
2218                                           g_param_spec_enum ("border-style",
2219                                                              "Border style",
2220                                                              "Border style",
2221                                                              GTK_TYPE_BORDER_STYLE,
2222                                                              GTK_BORDER_STYLE_NONE, 0));
2223   gtk_style_properties_register_property (NULL,
2224                                           g_param_spec_boxed ("border-color",
2225                                                               "Border color",
2226                                                               "Border color",
2227                                                               GDK_TYPE_RGBA, 0));
2228   gtk_style_properties_register_property (NULL,
2229                                           g_param_spec_boxed ("background-image",
2230                                                               "Background Image",
2231                                                               "Background Image",
2232                                                               CAIRO_GOBJECT_TYPE_PATTERN, 0));
2233   gtk_style_properties_register_property (NULL,
2234                                           g_param_spec_boxed ("border-image-source",
2235                                                               "Border image source",
2236                                                               "Border image source",
2237                                                               CAIRO_GOBJECT_TYPE_PATTERN, 0));
2238   gtk_style_properties_register_property (NULL,
2239                                           g_param_spec_boxed ("border-image-repeat",
2240                                                               "Border image repeat",
2241                                                               "Border image repeat",
2242                                                               GTK_TYPE_CSS_BORDER_IMAGE_REPEAT, 0));
2243   gtk_style_properties_register_property (NULL,
2244                                           g_param_spec_boxed ("border-image-slice",
2245                                                               "Border image slice",
2246                                                               "Border image slice",
2247                                                               GTK_TYPE_BORDER, 0));
2248   gtk_style_properties_register_property (NULL,
2249                                           g_param_spec_boxed ("border-image-width",
2250                                                               "Border image width",
2251                                                               "Border image width",
2252                                                               GTK_TYPE_BORDER, 0));  
2253   _gtk_style_property_register           (g_param_spec_boxed ("border-image",
2254                                                               "Border Image",
2255                                                               "Border Image",
2256                                                               GTK_TYPE_BORDER_IMAGE, 0),
2257                                           0,
2258                                           NULL,
2259                                           _gtk_border_image_unpack,
2260                                           _gtk_border_image_pack,
2261                                           NULL,
2262                                           NULL,
2263                                           NULL);
2264   gtk_style_properties_register_property (NULL,
2265                                           g_param_spec_object ("engine",
2266                                                                "Theming Engine",
2267                                                                "Theming Engine",
2268                                                                GTK_TYPE_THEMING_ENGINE, 0));
2269   gtk_style_properties_register_property (NULL,
2270                                           g_param_spec_boxed ("transition",
2271                                                               "Transition animation description",
2272                                                               "Transition animation description",
2273                                                               GTK_TYPE_ANIMATION_DESCRIPTION, 0));
2274
2275   /* Private property holding the binding sets */
2276   _gtk_style_property_register           (g_param_spec_boxed ("gtk-key-bindings",
2277                                                               "Key bindings",
2278                                                               "Key bindings",
2279                                                               G_TYPE_PTR_ARRAY, 0),
2280                                           0,
2281                                           NULL,
2282                                           NULL,
2283                                           NULL,
2284                                           bindings_value_parse,
2285                                           bindings_value_print,
2286                                           NULL);
2287 }
2288
2289 const GtkStyleProperty *
2290 _gtk_style_property_lookup (const char *name)
2291 {
2292   gtk_style_property_init ();
2293
2294   return g_hash_table_lookup (properties, name);
2295 }
2296
2297 void
2298 _gtk_style_property_register (GParamSpec               *pspec,
2299                               GtkStylePropertyFlags     flags,
2300                               GtkStylePropertyParser    property_parse_func,
2301                               GtkStyleUnpackFunc        unpack_func,
2302                               GtkStylePackFunc          pack_func,
2303                               GtkStyleParseFunc         parse_func,
2304                               GtkStylePrintFunc         print_func,
2305                               GtkStyleDefaultValueFunc  default_value_func)
2306 {
2307   const GtkStyleProperty *existing;
2308   GtkStyleProperty *node;
2309
2310   g_return_if_fail ((pack_func == NULL) == (unpack_func == NULL));
2311
2312   gtk_style_property_init ();
2313
2314   existing = _gtk_style_property_lookup (pspec->name);
2315   if (existing != NULL)
2316     {
2317       g_warning ("Property \"%s\" was already registered with type %s",
2318                  pspec->name, g_type_name (existing->pspec->value_type));
2319       return;
2320     }
2321
2322   node = g_slice_new0 (GtkStyleProperty);
2323   node->flags = flags;
2324   node->pspec = pspec;
2325   node->property_parse_func = property_parse_func;
2326   node->pack_func = pack_func;
2327   node->unpack_func = unpack_func;
2328   node->parse_func = parse_func;
2329   node->print_func = print_func;
2330   node->default_value_func = default_value_func;
2331
2332   g_hash_table_insert (properties, pspec->name, node);
2333 }