]> Pileus Git - ~andy/gtk/blob - gtk/gtkstyleproperty.c
styleproperty: Factor out the string print function
[~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 "gtk9slice.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 slice_value_parse (GtkCssParser *parser,
953                    GFile        *base,
954                    GValue       *value)
955 {
956   gdouble distance_top, distance_bottom;
957   gdouble distance_left, distance_right;
958   GtkSliceSideModifier mods[2];
959   GdkPixbuf *pixbuf;
960   Gtk9Slice *slice;
961   GFile *file;
962   GError *error = NULL;
963   gint i;
964   char *path;
965
966   /* Parse image url */
967   file = gtk_css_parse_url (parser, base);
968   if (!file)
969       return FALSE;
970
971   if (!_gtk_css_parser_try_double (parser, &distance_top) ||
972       !_gtk_css_parser_try_double (parser, &distance_right) ||
973       !_gtk_css_parser_try_double (parser, &distance_bottom) ||
974       !_gtk_css_parser_try_double (parser, &distance_left))
975     {
976       _gtk_css_parser_error (parser, "Expected a number");
977       g_object_unref (file);
978       return FALSE;
979     }
980
981   for (i = 0; i < 2; i++)
982     {
983       if (_gtk_css_parser_try (parser, "stretch", TRUE))
984         mods[i] = GTK_SLICE_STRETCH;
985       else if (_gtk_css_parser_try (parser, "repeat", TRUE))
986         mods[i] = GTK_SLICE_REPEAT;
987       else if (i == 0)
988         {
989           mods[1] = mods[0] = GTK_SLICE_STRETCH;
990           break;
991         }
992       else
993         mods[i] = mods[0];
994     }
995
996   path = g_file_get_path (file);
997   g_object_unref (file);
998   pixbuf = gdk_pixbuf_new_from_file (path, &error);
999   g_free (path);
1000   if (!pixbuf)
1001     {
1002       _gtk_css_parser_take_error (parser, error);
1003       return FALSE;
1004     }
1005
1006   slice = _gtk_9slice_new (pixbuf,
1007                            distance_top, distance_bottom,
1008                            distance_left, distance_right,
1009                            mods[0], mods[1]);
1010   g_object_unref (pixbuf);
1011
1012   g_value_take_boxed (value, slice);
1013   return TRUE;
1014 }
1015
1016 static gboolean 
1017 enum_value_parse (GtkCssParser *parser,
1018                   GFile        *base,
1019                   GValue       *value)
1020 {
1021   GEnumClass *enum_class;
1022   GEnumValue *enum_value;
1023   char *str;
1024
1025   str = _gtk_css_parser_try_ident (parser, TRUE);
1026   if (str == NULL)
1027     {
1028       _gtk_css_parser_error (parser, "Expected an identifier");
1029       return FALSE;
1030     }
1031
1032   enum_class = g_type_class_ref (G_VALUE_TYPE (value));
1033   enum_value = g_enum_get_value_by_nick (enum_class, str);
1034
1035   if (enum_value)
1036     g_value_set_enum (value, enum_value->value);
1037   else
1038     _gtk_css_parser_error (parser,
1039                            "Unknown value '%s' for enum type '%s'",
1040                            str, g_type_name (G_VALUE_TYPE (value)));
1041   
1042   g_type_class_unref (enum_class);
1043   g_free (str);
1044
1045   return enum_value != NULL;
1046 }
1047
1048 static void
1049 enum_value_print (const GValue *value,
1050                   GString      *string)
1051 {
1052   GEnumClass *enum_class;
1053   GEnumValue *enum_value;
1054
1055   enum_class = g_type_class_ref (G_VALUE_TYPE (value));
1056   enum_value = g_enum_get_value (enum_class, g_value_get_enum (value));
1057
1058   g_string_append (string, enum_value->value_nick);
1059
1060   g_type_class_unref (enum_class);
1061 }
1062
1063 static gboolean 
1064 flags_value_parse (GtkCssParser *parser,
1065                    GFile        *base,
1066                    GValue       *value)
1067 {
1068   GFlagsClass *flags_class;
1069   GFlagsValue *flag_value;
1070   guint flags = 0;
1071   char *str;
1072
1073   flags_class = g_type_class_ref (G_VALUE_TYPE (value));
1074
1075   do {
1076     str = _gtk_css_parser_try_ident (parser, TRUE);
1077     if (str == NULL)
1078       {
1079         _gtk_css_parser_error (parser, "Expected an identifier");
1080         g_type_class_unref (flags_class);
1081         return FALSE;
1082       }
1083
1084       flag_value = g_flags_get_value_by_nick (flags_class, str);
1085       if (!flag_value)
1086         {
1087           _gtk_css_parser_error (parser,
1088                                  "Unknown flag value '%s' for type '%s'",
1089                                  str, g_type_name (G_VALUE_TYPE (value)));
1090           /* XXX Do we want to return FALSE here? We can get
1091            * forward-compatibility for new values this way
1092            */
1093           g_free (str);
1094           g_type_class_unref (flags_class);
1095           return FALSE;
1096         }
1097
1098       g_free (str);
1099     }
1100   while (_gtk_css_parser_try (parser, ",", FALSE));
1101
1102   g_type_class_unref (flags_class);
1103
1104   g_value_set_enum (value, flags);
1105
1106   return TRUE;
1107 }
1108
1109 static void
1110 flags_value_print (const GValue *value,
1111                    GString      *string)
1112 {
1113   GFlagsClass *flags_class;
1114   guint i, flags;
1115
1116   flags_class = g_type_class_ref (G_VALUE_TYPE (value));
1117   flags = g_value_get_flags (value);
1118
1119   for (i = 0; i < flags_class->n_values; i++)
1120     {
1121       GFlagsValue *flags_value = &flags_class->values[i];
1122
1123       if (flags & flags_value->value)
1124         {
1125           if (string->len != 0)
1126             g_string_append (string, ", ");
1127
1128           g_string_append (string, flags_value->value_nick);
1129         }
1130     }
1131
1132   g_type_class_unref (flags_class);
1133 }
1134
1135 static gboolean 
1136 bindings_value_parse (GtkCssParser *parser,
1137                       GFile        *base,
1138                       GValue       *value)
1139 {
1140   GPtrArray *array;
1141   GtkBindingSet *binding_set;
1142   char *name;
1143
1144   array = g_ptr_array_new ();
1145
1146   do {
1147       name = _gtk_css_parser_try_ident (parser, TRUE);
1148       if (name == NULL)
1149         {
1150           _gtk_css_parser_error (parser, "Not a valid binding name");
1151           g_ptr_array_free (array, TRUE);
1152           return FALSE;
1153         }
1154
1155       binding_set = gtk_binding_set_find (name);
1156
1157       if (!binding_set)
1158         {
1159           _gtk_css_parser_error (parser, "No binding set named '%s'", name);
1160           g_free (name);
1161           continue;
1162         }
1163
1164       g_ptr_array_add (array, binding_set);
1165       g_free (name);
1166     }
1167   while (_gtk_css_parser_try (parser, ",", TRUE));
1168
1169   g_value_take_boxed (value, array);
1170
1171   return TRUE;
1172 }
1173
1174 static void
1175 bindings_value_print (const GValue *value,
1176                       GString      *string)
1177 {
1178   GPtrArray *array;
1179   guint i;
1180
1181   array = g_value_get_boxed (value);
1182
1183   for (i = 0; i < array->len; i++)
1184     {
1185       GtkBindingSet *binding_set = g_ptr_array_index (array, i);
1186
1187       if (i > 0)
1188         g_string_append (string, ", ");
1189       g_string_append (string, binding_set->set_name);
1190     }
1191 }
1192
1193 static gboolean 
1194 border_corner_radius_value_parse (GtkCssParser *parser,
1195                                   GFile        *base,
1196                                   GValue       *value)
1197 {
1198   GtkCssBorderCornerRadius corner;
1199
1200   if (!_gtk_css_parser_try_double (parser, &corner.horizontal))
1201     {
1202       _gtk_css_parser_error (parser, "Expected a number");
1203       return FALSE;
1204     }
1205   else if (corner.horizontal < 0)
1206     goto negative;
1207
1208   if (!_gtk_css_parser_try_double (parser, &corner.vertical))
1209     corner.vertical = corner.horizontal;
1210   else if (corner.vertical < 0)
1211     goto negative;
1212
1213   g_value_set_boxed (value, &corner);
1214   return TRUE;
1215
1216 negative:
1217   _gtk_css_parser_error (parser, "Border radius values cannot be negative");
1218   return FALSE;
1219 }
1220
1221 static void
1222 border_corner_radius_value_print (const GValue *value,
1223                                   GString      *string)
1224 {
1225   GtkCssBorderCornerRadius *corner;
1226
1227   corner = g_value_get_boxed (value);
1228
1229   if (corner == NULL)
1230     {
1231       g_string_append (string, "none");
1232       return;
1233     }
1234
1235   string_append_double (string, corner->horizontal);
1236   if (corner->horizontal != corner->vertical)
1237     {
1238       g_string_append_c (string, ' ');
1239       string_append_double (string, corner->vertical);
1240     }
1241 }
1242
1243 static gboolean 
1244 border_radius_value_parse (GtkCssParser *parser,
1245                            GFile        *base,
1246                            GValue       *value)
1247 {
1248   GtkCssBorderRadius border;
1249
1250   if (!_gtk_css_parser_try_double (parser, &border.top_left.horizontal))
1251     {
1252       _gtk_css_parser_error (parser, "Expected a number");
1253       return FALSE;
1254     }
1255   else if (border.top_left.horizontal < 0)
1256     goto negative;
1257
1258   if (_gtk_css_parser_try_double (parser, &border.top_right.horizontal))
1259     {
1260       if (border.top_right.horizontal < 0)
1261         goto negative;
1262       if (_gtk_css_parser_try_double (parser, &border.bottom_right.horizontal))
1263         {
1264           if (border.bottom_right.horizontal < 0)
1265             goto negative;
1266           if (!_gtk_css_parser_try_double (parser, &border.bottom_left.horizontal))
1267             border.bottom_left.horizontal = border.top_right.horizontal;
1268           else if (border.bottom_left.horizontal < 0)
1269             goto negative;
1270         }
1271       else
1272         {
1273           border.bottom_right.horizontal = border.top_left.horizontal;
1274           border.bottom_left.horizontal = border.top_right.horizontal;
1275         }
1276     }
1277   else
1278     {
1279       border.top_right.horizontal = border.top_left.horizontal;
1280       border.bottom_right.horizontal = border.top_left.horizontal;
1281       border.bottom_left.horizontal = border.top_left.horizontal;
1282     }
1283
1284   if (_gtk_css_parser_try (parser, "/", TRUE))
1285     {
1286       if (!_gtk_css_parser_try_double (parser, &border.top_left.vertical))
1287         {
1288           _gtk_css_parser_error (parser, "Expected a number");
1289           return FALSE;
1290         }
1291       else if (border.top_left.vertical < 0)
1292         goto negative;
1293
1294       if (_gtk_css_parser_try_double (parser, &border.top_right.vertical))
1295         {
1296           if (border.top_right.vertical < 0)
1297             goto negative;
1298           if (_gtk_css_parser_try_double (parser, &border.bottom_right.vertical))
1299             {
1300               if (border.bottom_right.vertical < 0)
1301                 goto negative;
1302               if (!_gtk_css_parser_try_double (parser, &border.bottom_left.vertical))
1303                 border.bottom_left.vertical = border.top_right.vertical;
1304               else if (border.bottom_left.vertical < 0)
1305                 goto negative;
1306             }
1307           else
1308             {
1309               border.bottom_right.vertical = border.top_left.vertical;
1310               border.bottom_left.vertical = border.top_right.vertical;
1311             }
1312         }
1313       else
1314         {
1315           border.top_right.vertical = border.top_left.vertical;
1316           border.bottom_right.vertical = border.top_left.vertical;
1317           border.bottom_left.vertical = border.top_left.vertical;
1318         }
1319     }
1320   else
1321     {
1322       border.top_left.vertical = border.top_left.horizontal;
1323       border.top_right.vertical = border.top_right.horizontal;
1324       border.bottom_right.vertical = border.bottom_right.horizontal;
1325       border.bottom_left.vertical = border.bottom_left.horizontal;
1326     }
1327
1328   /* border-radius is an int property for backwards-compat reasons */
1329   g_value_unset (value);
1330   g_value_init (value, GTK_TYPE_CSS_BORDER_RADIUS);
1331   g_value_set_boxed (value, &border);
1332
1333   return TRUE;
1334
1335 negative:
1336   _gtk_css_parser_error (parser, "Border radius values cannot be negative");
1337   return FALSE;
1338 }
1339
1340 static void
1341 border_radius_value_print (const GValue *value,
1342                            GString      *string)
1343 {
1344   GtkCssBorderRadius *border;
1345
1346   border = g_value_get_boxed (value);
1347
1348   if (border == NULL)
1349     {
1350       g_string_append (string, "none");
1351       return;
1352     }
1353
1354   string_append_double (string, border->top_left.horizontal);
1355   if (border->top_left.horizontal != border->top_right.horizontal ||
1356       border->top_left.horizontal != border->bottom_right.horizontal ||
1357       border->top_left.horizontal != border->bottom_left.horizontal)
1358     {
1359       g_string_append_c (string, ' ');
1360       string_append_double (string, border->top_right.horizontal);
1361       if (border->top_left.horizontal != border->bottom_right.horizontal ||
1362           border->top_right.horizontal != border->bottom_left.horizontal)
1363         {
1364           g_string_append_c (string, ' ');
1365           string_append_double (string, border->bottom_right.horizontal);
1366           if (border->top_right.horizontal != border->bottom_left.horizontal)
1367             {
1368               g_string_append_c (string, ' ');
1369               string_append_double (string, border->bottom_left.horizontal);
1370             }
1371         }
1372     }
1373
1374   if (border->top_left.horizontal != border->top_left.vertical ||
1375       border->top_right.horizontal != border->top_right.vertical ||
1376       border->bottom_right.horizontal != border->bottom_right.vertical ||
1377       border->bottom_left.horizontal != border->bottom_left.vertical)
1378     {
1379       g_string_append (string, " / ");
1380       string_append_double (string, border->top_left.vertical);
1381       if (border->top_left.vertical != border->top_right.vertical ||
1382           border->top_left.vertical != border->bottom_right.vertical ||
1383           border->top_left.vertical != border->bottom_left.vertical)
1384         {
1385           g_string_append_c (string, ' ');
1386           string_append_double (string, border->top_right.vertical);
1387           if (border->top_left.vertical != border->bottom_right.vertical ||
1388               border->top_right.vertical != border->bottom_left.vertical)
1389             {
1390               g_string_append_c (string, ' ');
1391               string_append_double (string, border->bottom_right.vertical);
1392               if (border->top_right.vertical != border->bottom_left.vertical)
1393                 {
1394                   g_string_append_c (string, ' ');
1395                   string_append_double (string, border->bottom_left.vertical);
1396                 }
1397             }
1398         }
1399
1400     }
1401 }
1402
1403 /*** PACKING ***/
1404
1405 static GParameter *
1406 unpack_border (const GValue *value,
1407                guint        *n_params,
1408                const char   *top,
1409                const char   *left,
1410                const char   *bottom,
1411                const char   *right)
1412 {
1413   GParameter *parameter = g_new0 (GParameter, 4);
1414   GtkBorder *border = g_value_get_boxed (value);
1415
1416   parameter[0].name = top;
1417   g_value_init (&parameter[0].value, G_TYPE_INT);
1418   g_value_set_int (&parameter[0].value, border->top);
1419   parameter[1].name = left;
1420   g_value_init (&parameter[1].value, G_TYPE_INT);
1421   g_value_set_int (&parameter[1].value, border->left);
1422   parameter[2].name = bottom;
1423   g_value_init (&parameter[2].value, G_TYPE_INT);
1424   g_value_set_int (&parameter[2].value, border->bottom);
1425   parameter[3].name = right;
1426   g_value_init (&parameter[3].value, G_TYPE_INT);
1427   g_value_set_int (&parameter[3].value, border->right);
1428
1429   *n_params = 4;
1430   return parameter;
1431 }
1432
1433 static void
1434 pack_border (GValue             *value,
1435              GtkStyleProperties *props,
1436              GtkStateFlags       state,
1437              const char         *top,
1438              const char         *left,
1439              const char         *bottom,
1440              const char         *right)
1441 {
1442   GtkBorder border;
1443   int t, l, b, r;
1444
1445   gtk_style_properties_get (props,
1446                             state,
1447                             top, &t,
1448                             left, &l,
1449                             bottom, &b,
1450                             right, &r,
1451                             NULL);
1452
1453   border.top = t;
1454   border.left = l;
1455   border.bottom = b;
1456   border.right = r;
1457
1458   g_value_set_boxed (value, &border);
1459 }
1460
1461 static GParameter *
1462 unpack_border_width (const GValue *value,
1463                      guint        *n_params)
1464 {
1465   return unpack_border (value, n_params,
1466                         "border-top-width", "border-left-width",
1467                         "border-bottom-width", "border-right-width");
1468 }
1469
1470 static void
1471 pack_border_width (GValue             *value,
1472                    GtkStyleProperties *props,
1473                    GtkStateFlags       state)
1474 {
1475   pack_border (value, props, state,
1476                "border-top-width", "border-left-width",
1477                "border-bottom-width", "border-right-width");
1478 }
1479
1480 static GParameter *
1481 unpack_padding (const GValue *value,
1482                 guint        *n_params)
1483 {
1484   return unpack_border (value, n_params,
1485                         "padding-top", "padding-left",
1486                         "padding-bottom", "padding-right");
1487 }
1488
1489 static void
1490 pack_padding (GValue             *value,
1491               GtkStyleProperties *props,
1492               GtkStateFlags       state)
1493 {
1494   pack_border (value, props, state,
1495                "padding-top", "padding-left",
1496                "padding-bottom", "padding-right");
1497 }
1498
1499 static GParameter *
1500 unpack_margin (const GValue *value,
1501                guint        *n_params)
1502 {
1503   return unpack_border (value, n_params,
1504                         "margin-top", "margin-left",
1505                         "margin-bottom", "margin-right");
1506 }
1507
1508 static void
1509 pack_margin (GValue             *value,
1510              GtkStyleProperties *props,
1511              GtkStateFlags       state)
1512 {
1513   pack_border (value, props, state,
1514                "margin-top", "margin-left",
1515                "margin-bottom", "margin-right");
1516 }
1517
1518 static GParameter *
1519 unpack_border_radius (const GValue *value,
1520                       guint        *n_params)
1521 {
1522   GParameter *parameter = g_new0 (GParameter, 4);
1523   GtkCssBorderRadius *border;
1524   
1525   if (G_VALUE_HOLDS_BOXED (value))
1526     border = g_value_get_boxed (value);
1527   else
1528     border = NULL;
1529
1530   parameter[0].name = "border-top-left-radius";
1531   g_value_init (&parameter[0].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
1532   parameter[1].name = "border-top-right-radius";
1533   g_value_init (&parameter[1].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
1534   parameter[2].name = "border-bottom-right-radius";
1535   g_value_init (&parameter[2].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
1536   parameter[3].name = "border-bottom-left-radius";
1537   g_value_init (&parameter[3].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
1538   if (border)
1539     {
1540       g_value_set_boxed (&parameter[0].value, &border->top_left);
1541       g_value_set_boxed (&parameter[1].value, &border->top_right);
1542       g_value_set_boxed (&parameter[2].value, &border->bottom_right);
1543       g_value_set_boxed (&parameter[3].value, &border->bottom_left);
1544     }
1545
1546   *n_params = 4;
1547   return parameter;
1548 }
1549
1550 static void
1551 pack_border_radius (GValue             *value,
1552                     GtkStyleProperties *props,
1553                     GtkStateFlags       state)
1554 {
1555   GtkCssBorderCornerRadius *top_left;
1556
1557   /* NB: We are an int property, so we have to resolve to an int here.
1558    * So we just resolve to an int. We pick one and stick to it.
1559    * Lesson learned: Don't query border-radius shorthand, query the 
1560    * real properties instead. */
1561   gtk_style_properties_get (props,
1562                             state,
1563                             "border-top-left-radius", &top_left,
1564                             NULL);
1565
1566   if (top_left)
1567     g_value_set_int (value, top_left->horizontal);
1568
1569   g_free (top_left);
1570 }
1571
1572 /*** API ***/
1573
1574 static void
1575 css_string_funcs_init (void)
1576 {
1577   if (G_LIKELY (parse_funcs != NULL))
1578     return;
1579
1580   parse_funcs = g_hash_table_new (NULL, NULL);
1581   print_funcs = g_hash_table_new (NULL, NULL);
1582
1583   register_conversion_function (GDK_TYPE_RGBA,
1584                                 rgba_value_parse,
1585                                 rgba_value_print);
1586   register_conversion_function (GDK_TYPE_COLOR,
1587                                 color_value_parse,
1588                                 color_value_print);
1589   register_conversion_function (GTK_TYPE_SYMBOLIC_COLOR,
1590                                 symbolic_color_value_parse,
1591                                 symbolic_color_value_print);
1592   register_conversion_function (PANGO_TYPE_FONT_DESCRIPTION,
1593                                 font_description_value_parse,
1594                                 font_description_value_print);
1595   register_conversion_function (G_TYPE_BOOLEAN,
1596                                 boolean_value_parse,
1597                                 boolean_value_print);
1598   register_conversion_function (G_TYPE_INT,
1599                                 int_value_parse,
1600                                 int_value_print);
1601   register_conversion_function (G_TYPE_UINT,
1602                                 uint_value_parse,
1603                                 uint_value_print);
1604   register_conversion_function (G_TYPE_DOUBLE,
1605                                 double_value_parse,
1606                                 double_value_print);
1607   register_conversion_function (G_TYPE_FLOAT,
1608                                 float_value_parse,
1609                                 float_value_print);
1610   register_conversion_function (G_TYPE_STRING,
1611                                 string_value_parse,
1612                                 string_value_print);
1613   register_conversion_function (GTK_TYPE_THEMING_ENGINE,
1614                                 theming_engine_value_parse,
1615                                 theming_engine_value_print);
1616   register_conversion_function (GTK_TYPE_ANIMATION_DESCRIPTION,
1617                                 animation_description_value_parse,
1618                                 animation_description_value_print);
1619   register_conversion_function (GTK_TYPE_BORDER,
1620                                 border_value_parse,
1621                                 border_value_print);
1622   register_conversion_function (GTK_TYPE_GRADIENT,
1623                                 gradient_value_parse,
1624                                 gradient_value_print);
1625   register_conversion_function (CAIRO_GOBJECT_TYPE_PATTERN,
1626                                 pattern_value_parse,
1627                                 NULL);
1628   register_conversion_function (GTK_TYPE_9SLICE,
1629                                 slice_value_parse,
1630                                 NULL);
1631   register_conversion_function (GTK_TYPE_SHADOW,
1632                                 shadow_value_parse,
1633                                 shadow_value_print);
1634   register_conversion_function (G_TYPE_ENUM,
1635                                 enum_value_parse,
1636                                 enum_value_print);
1637   register_conversion_function (G_TYPE_FLAGS,
1638                                 flags_value_parse,
1639                                 flags_value_print);
1640 }
1641
1642 gboolean
1643 _gtk_style_property_parse_value (const GtkStyleProperty *property,
1644                                  GValue                 *value,
1645                                  GtkCssParser           *parser,
1646                                  GFile                  *base)
1647 {
1648   GtkStyleParseFunc func;
1649
1650   g_return_val_if_fail (value != NULL, FALSE);
1651   g_return_val_if_fail (parser != NULL, FALSE);
1652
1653   css_string_funcs_init ();
1654
1655   if (property)
1656     {
1657       if (_gtk_css_parser_try (parser, "none", TRUE))
1658         {
1659           /* Insert the default value, so it has an opportunity
1660            * to override other style providers when merged
1661            */
1662           g_param_value_set_default (property->pspec, value);
1663           return TRUE;
1664         }
1665       else if (property->property_parse_func)
1666         {
1667           GError *error = NULL;
1668           char *value_str;
1669           gboolean success;
1670           
1671           value_str = _gtk_css_parser_read_value (parser);
1672           if (value_str == NULL)
1673             return FALSE;
1674           
1675           success = (*property->property_parse_func) (value_str, value, &error);
1676
1677           g_free (value_str);
1678
1679           return success;
1680         }
1681
1682       func = property->parse_func;
1683     }
1684   else
1685     func = NULL;
1686
1687   if (func == NULL)
1688     func = g_hash_table_lookup (parse_funcs,
1689                                 GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
1690   if (func == NULL)
1691     func = g_hash_table_lookup (parse_funcs,
1692                                 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
1693
1694   if (func == NULL)
1695     {
1696       _gtk_css_parser_error (parser,
1697                              "Cannot convert to type '%s'",
1698                              g_type_name (G_VALUE_TYPE (value)));
1699       return FALSE;
1700     }
1701
1702   return (*func) (parser, base, value);
1703 }
1704
1705 void
1706 _gtk_style_property_print_value (const GtkStyleProperty *property,
1707                                  const GValue           *value,
1708                                  GString                *string)
1709 {
1710   GtkStylePrintFunc func;
1711
1712   css_string_funcs_init ();
1713
1714   if (property)
1715     func = property->print_func;
1716   else
1717     func = NULL;
1718
1719   if (func == NULL)
1720     func = g_hash_table_lookup (print_funcs,
1721                                 GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
1722   if (func == NULL)
1723     func = g_hash_table_lookup (print_funcs,
1724                                 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
1725
1726   if (func == NULL)
1727     {
1728       char *s = g_strdup_value_contents (value);
1729       g_string_append (string, s);
1730       g_free (s);
1731       return;
1732     }
1733   
1734   func (value, string);
1735 }
1736
1737 void
1738 _gtk_style_property_default_value (const GtkStyleProperty *property,
1739                                    GtkStyleProperties     *properties,
1740                                    GValue                 *value)
1741 {
1742   if (property->default_value_func)
1743     property->default_value_func (properties, value);
1744   else if (property->pspec->value_type == GTK_TYPE_THEMING_ENGINE)
1745     g_value_set_object (value, gtk_theming_engine_load (NULL));
1746   else if (property->pspec->value_type == PANGO_TYPE_FONT_DESCRIPTION)
1747     g_value_take_boxed (value, pango_font_description_from_string ("Sans 10"));
1748   else if (property->pspec->value_type == GDK_TYPE_RGBA)
1749     {
1750       GdkRGBA color;
1751       gdk_rgba_parse (&color, "pink");
1752       g_value_set_boxed (value, &color);
1753     }
1754   else if (property->pspec->value_type == GTK_TYPE_BORDER)
1755     {
1756       g_value_take_boxed (value, gtk_border_new ());
1757     }
1758   else
1759     g_param_value_set_default (property->pspec, value);
1760 }
1761
1762 gboolean
1763 _gtk_style_property_is_shorthand  (const GtkStyleProperty *property)
1764 {
1765   g_return_val_if_fail (property != NULL, FALSE);
1766
1767   return property->pack_func != NULL;
1768 }
1769
1770 GParameter *
1771 _gtk_style_property_unpack (const GtkStyleProperty *property,
1772                             const GValue           *value,
1773                             guint                  *n_params)
1774 {
1775   g_return_val_if_fail (property != NULL, NULL);
1776   g_return_val_if_fail (property->unpack_func != NULL, NULL);
1777   g_return_val_if_fail (value != NULL, NULL);
1778   g_return_val_if_fail (n_params != NULL, NULL);
1779
1780   return property->unpack_func (value, n_params);
1781 }
1782
1783 void
1784 _gtk_style_property_pack (const GtkStyleProperty *property,
1785                           GtkStyleProperties     *props,
1786                           GtkStateFlags           state,
1787                           GValue                 *value)
1788 {
1789   g_return_if_fail (property != NULL);
1790   g_return_if_fail (property->pack_func != NULL);
1791   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
1792   g_return_if_fail (G_IS_VALUE (value));
1793
1794   property->pack_func (value, props, state);
1795 }
1796
1797 static void
1798 gtk_style_property_init (void)
1799 {
1800   GParamSpec *pspec;
1801
1802   if (G_LIKELY (properties))
1803     return;
1804
1805   /* stuff is never freed, so no need for free functions */
1806   properties = g_hash_table_new (g_str_hash, g_str_equal);
1807
1808   /* note that gtk_style_properties_register_property() calls this function,
1809    * so make sure we're sanely inited to avoid infloops */
1810
1811   pspec = g_param_spec_boxed ("color",
1812                               "Foreground color",
1813                               "Foreground color",
1814                               GDK_TYPE_RGBA, 0);
1815   gtk_style_param_set_inherit (pspec, TRUE);
1816   gtk_style_properties_register_property (NULL, pspec);
1817
1818   gtk_style_properties_register_property (NULL,
1819                                           g_param_spec_boxed ("background-color",
1820                                                               "Background color",
1821                                                               "Background color",
1822                                                               GDK_TYPE_RGBA, 0));
1823
1824   pspec = g_param_spec_boxed ("font",
1825                               "Font Description",
1826                               "Font Description",
1827                               PANGO_TYPE_FONT_DESCRIPTION, 0);
1828   gtk_style_param_set_inherit (pspec, TRUE);
1829   gtk_style_properties_register_property (NULL, pspec);
1830
1831   pspec = g_param_spec_boxed ("text-shadow",
1832                               "Text shadow",
1833                               "Text shadow",
1834                               GTK_TYPE_SHADOW, 0);
1835   gtk_style_param_set_inherit (pspec, TRUE);
1836   gtk_style_properties_register_property (NULL, pspec);
1837
1838   gtk_style_properties_register_property (NULL,
1839                                           g_param_spec_int ("margin-top",
1840                                                             "margin top",
1841                                                             "Margin at top",
1842                                                             0, G_MAXINT, 0, 0));
1843   gtk_style_properties_register_property (NULL,
1844                                           g_param_spec_int ("margin-left",
1845                                                             "margin left",
1846                                                             "Margin at left",
1847                                                             0, G_MAXINT, 0, 0));
1848   gtk_style_properties_register_property (NULL,
1849                                           g_param_spec_int ("margin-bottom",
1850                                                             "margin bottom",
1851                                                             "Margin at bottom",
1852                                                             0, G_MAXINT, 0, 0));
1853   gtk_style_properties_register_property (NULL,
1854                                           g_param_spec_int ("margin-right",
1855                                                             "margin right",
1856                                                             "Margin at right",
1857                                                             0, G_MAXINT, 0, 0));
1858   _gtk_style_property_register           (g_param_spec_boxed ("margin",
1859                                                               "Margin",
1860                                                               "Margin",
1861                                                               GTK_TYPE_BORDER, 0),
1862                                           NULL,
1863                                           unpack_margin,
1864                                           pack_margin,
1865                                           NULL,
1866                                           NULL,
1867                                           NULL);
1868   gtk_style_properties_register_property (NULL,
1869                                           g_param_spec_int ("padding-top",
1870                                                             "padding top",
1871                                                             "Padding at top",
1872                                                             0, G_MAXINT, 0, 0));
1873   gtk_style_properties_register_property (NULL,
1874                                           g_param_spec_int ("padding-left",
1875                                                             "padding left",
1876                                                             "Padding at left",
1877                                                             0, G_MAXINT, 0, 0));
1878   gtk_style_properties_register_property (NULL,
1879                                           g_param_spec_int ("padding-bottom",
1880                                                             "padding bottom",
1881                                                             "Padding at bottom",
1882                                                             0, G_MAXINT, 0, 0));
1883   gtk_style_properties_register_property (NULL,
1884                                           g_param_spec_int ("padding-right",
1885                                                             "padding right",
1886                                                             "Padding at right",
1887                                                             0, G_MAXINT, 0, 0));
1888   _gtk_style_property_register           (g_param_spec_boxed ("padding",
1889                                                               "Padding",
1890                                                               "Padding",
1891                                                               GTK_TYPE_BORDER, 0),
1892                                           NULL,
1893                                           unpack_padding,
1894                                           pack_padding,
1895                                           NULL,
1896                                           NULL,
1897                                           NULL);
1898   gtk_style_properties_register_property (NULL,
1899                                           g_param_spec_int ("border-top-width",
1900                                                             "border top width",
1901                                                             "Border width at top",
1902                                                             0, G_MAXINT, 0, 0));
1903   gtk_style_properties_register_property (NULL,
1904                                           g_param_spec_int ("border-left-width",
1905                                                             "border left width",
1906                                                             "Border width at left",
1907                                                             0, G_MAXINT, 0, 0));
1908   gtk_style_properties_register_property (NULL,
1909                                           g_param_spec_int ("border-bottom-width",
1910                                                             "border bottom width",
1911                                                             "Border width at bottom",
1912                                                             0, G_MAXINT, 0, 0));
1913   gtk_style_properties_register_property (NULL,
1914                                           g_param_spec_int ("border-right-width",
1915                                                             "border right width",
1916                                                             "Border width at right",
1917                                                             0, G_MAXINT, 0, 0));
1918   _gtk_style_property_register           (g_param_spec_boxed ("border-width",
1919                                                               "Border width",
1920                                                               "Border width, in pixels",
1921                                                               GTK_TYPE_BORDER, 0),
1922                                           NULL,
1923                                           unpack_border_width,
1924                                           pack_border_width,
1925                                           NULL,
1926                                           NULL,
1927                                           NULL);
1928
1929   _gtk_style_property_register           (g_param_spec_boxed ("border-top-left-radius",
1930                                                               "Border top left radius",
1931                                                               "Border radius of top left corner, in pixels",
1932                                                               GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
1933                                           NULL,
1934                                           NULL,
1935                                           NULL,
1936                                           border_corner_radius_value_parse,
1937                                           border_corner_radius_value_print,
1938                                           NULL);
1939   _gtk_style_property_register           (g_param_spec_boxed ("border-top-right-radius",
1940                                                               "Border top right radius",
1941                                                               "Border radius of top right corner, in pixels",
1942                                                               GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
1943                                           NULL,
1944                                           NULL,
1945                                           NULL,
1946                                           border_corner_radius_value_parse,
1947                                           border_corner_radius_value_print,
1948                                           NULL);
1949   _gtk_style_property_register           (g_param_spec_boxed ("border-bottom-right-radius",
1950                                                               "Border bottom right radius",
1951                                                               "Border radius of bottom right corner, in pixels",
1952                                                               GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
1953                                           NULL,
1954                                           NULL,
1955                                           NULL,
1956                                           border_corner_radius_value_parse,
1957                                           border_corner_radius_value_print,
1958                                           NULL);
1959   _gtk_style_property_register           (g_param_spec_boxed ("border-bottom-left-radius",
1960                                                               "Border bottom left radius",
1961                                                               "Border radius of bottom left corner, in pixels",
1962                                                               GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
1963                                           NULL,
1964                                           NULL,
1965                                           NULL,
1966                                           border_corner_radius_value_parse,
1967                                           border_corner_radius_value_print,
1968                                           NULL);
1969   _gtk_style_property_register           (g_param_spec_int ("border-radius",
1970                                                             "Border radius",
1971                                                             "Border radius, in pixels",
1972                                                             0, G_MAXINT, 0, 0),
1973                                           NULL,
1974                                           unpack_border_radius,
1975                                           pack_border_radius,
1976                                           border_radius_value_parse,
1977                                           border_radius_value_print,
1978                                           NULL);
1979
1980   gtk_style_properties_register_property (NULL,
1981                                           g_param_spec_enum ("border-style",
1982                                                              "Border style",
1983                                                              "Border style",
1984                                                              GTK_TYPE_BORDER_STYLE,
1985                                                              GTK_BORDER_STYLE_NONE, 0));
1986   gtk_style_properties_register_property (NULL,
1987                                           g_param_spec_boxed ("border-color",
1988                                                               "Border color",
1989                                                               "Border color",
1990                                                               GDK_TYPE_RGBA, 0));
1991   gtk_style_properties_register_property (NULL,
1992                                           g_param_spec_boxed ("background-image",
1993                                                               "Background Image",
1994                                                               "Background Image",
1995                                                               CAIRO_GOBJECT_TYPE_PATTERN, 0));
1996   gtk_style_properties_register_property (NULL,
1997                                           g_param_spec_boxed ("border-image",
1998                                                               "Border Image",
1999                                                               "Border Image",
2000                                                               GTK_TYPE_9SLICE, 0));
2001   gtk_style_properties_register_property (NULL,
2002                                           g_param_spec_object ("engine",
2003                                                                "Theming Engine",
2004                                                                "Theming Engine",
2005                                                                GTK_TYPE_THEMING_ENGINE, 0));
2006   gtk_style_properties_register_property (NULL,
2007                                           g_param_spec_boxed ("transition",
2008                                                               "Transition animation description",
2009                                                               "Transition animation description",
2010                                                               GTK_TYPE_ANIMATION_DESCRIPTION, 0));
2011
2012   /* Private property holding the binding sets */
2013   _gtk_style_property_register           (g_param_spec_boxed ("gtk-key-bindings",
2014                                                               "Key bindings",
2015                                                               "Key bindings",
2016                                                               G_TYPE_PTR_ARRAY, 0),
2017                                           NULL,
2018                                           NULL,
2019                                           NULL,
2020                                           bindings_value_parse,
2021                                           bindings_value_print,
2022                                           NULL);
2023 }
2024
2025 const GtkStyleProperty *
2026 _gtk_style_property_lookup (const char *name)
2027 {
2028   gtk_style_property_init ();
2029
2030   return g_hash_table_lookup (properties, name);
2031 }
2032
2033 void
2034 _gtk_style_property_register (GParamSpec               *pspec,
2035                               GtkStylePropertyParser    property_parse_func,
2036                               GtkStyleUnpackFunc        unpack_func,
2037                               GtkStylePackFunc          pack_func,
2038                               GtkStyleParseFunc         parse_func,
2039                               GtkStylePrintFunc         print_func,
2040                               GtkStyleDefaultValueFunc  default_value_func)
2041 {
2042   const GtkStyleProperty *existing;
2043   GtkStyleProperty *node;
2044
2045   g_return_if_fail ((pack_func == NULL) == (unpack_func == NULL));
2046
2047   gtk_style_property_init ();
2048
2049   existing = _gtk_style_property_lookup (pspec->name);
2050   if (existing != NULL)
2051     {
2052       g_warning ("Property \"%s\" was already registered with type %s",
2053                  pspec->name, g_type_name (existing->pspec->value_type));
2054       return;
2055     }
2056
2057   node = g_slice_new0 (GtkStyleProperty);
2058   node->pspec = pspec;
2059   node->property_parse_func = property_parse_func;
2060   node->pack_func = pack_func;
2061   node->unpack_func = unpack_func;
2062   node->parse_func = parse_func;
2063   node->print_func = print_func;
2064   node->default_value_func = default_value_func;
2065
2066   g_hash_table_insert (properties, pspec->name, node);
2067 }