]> Pileus Git - ~andy/gtk/blob - gtk/gtkcssstylefuncs.c
b19a3e60c03fe0a204ff26f55979ee3f68ae3f67
[~andy/gtk] / gtk / gtkcssstylefuncs.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, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include "config.h"
19
20 #include "gtkcssstylefuncsprivate.h"
21
22 #include <errno.h>
23 #include <math.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include <gdk-pixbuf/gdk-pixbuf.h>
28 #include <cairo-gobject.h>
29
30 #include "gtkcsscolorvalueprivate.h"
31 #include "gtkcssimagegradientprivate.h"
32 #include "gtkcssprovider.h"
33 #include "gtkcssrgbavalueprivate.h"
34 #include "gtkcsstypedvalueprivate.h"
35 #include "gtkcsstypesprivate.h"
36 #include "gtkgradient.h"
37 #include "gtkgradientprivate.h"
38 #include "gtkprivatetypebuiltins.h"
39 #include "gtkstylecontextprivate.h"
40 #include "gtksymboliccolorprivate.h"
41 #include "gtkthemingengine.h"
42 #include "gtktypebuiltins.h"
43 #include "gtkwin32themeprivate.h"
44
45 /* this is in case round() is not provided by the compiler, 
46  * such as in the case of C89 compilers, like MSVC
47  */
48 #include "fallback-c89.c"
49
50 static GHashTable *parse_funcs = NULL;
51 static GHashTable *print_funcs = NULL;
52 static GHashTable *compute_funcs = NULL;
53
54 typedef gboolean         (* GtkStyleParseFunc)             (GtkCssParser            *parser,
55                                                             GValue                  *value);
56 typedef void             (* GtkStylePrintFunc)             (const GValue            *value,
57                                                             GString                 *string);
58 typedef GtkCssValue *    (* GtkStyleComputeFunc)           (GtkStyleProviderPrivate *provider,
59                                                             GtkCssComputedValues    *values,
60                                                             GtkCssComputedValues    *parent_values,
61                                                             GtkCssValue             *specified,
62                                                             GtkCssDependencies      *dependencies);
63
64 static void
65 register_conversion_function (GType               type,
66                               GtkStyleParseFunc   parse,
67                               GtkStylePrintFunc   print,
68                               GtkStyleComputeFunc compute)
69 {
70   if (parse)
71     g_hash_table_insert (parse_funcs, GSIZE_TO_POINTER (type), parse);
72   if (print)
73     g_hash_table_insert (print_funcs, GSIZE_TO_POINTER (type), print);
74   if (compute)
75     g_hash_table_insert (compute_funcs, GSIZE_TO_POINTER (type), compute);
76 }
77
78 static void
79 string_append_double (GString *string,
80                       double   d)
81 {
82   char buf[G_ASCII_DTOSTR_BUF_SIZE];
83
84   g_ascii_dtostr (buf, sizeof (buf), d);
85   g_string_append (string, buf);
86 }
87
88 static void
89 string_append_string (GString    *str,
90                       const char *string)
91 {
92   gsize len;
93
94   g_string_append_c (str, '"');
95
96   do {
97     len = strcspn (string, "\"\n\r\f");
98     g_string_append (str, string);
99     string += len;
100     switch (*string)
101       {
102       case '\0':
103         break;
104       case '\n':
105         g_string_append (str, "\\A ");
106         break;
107       case '\r':
108         g_string_append (str, "\\D ");
109         break;
110       case '\f':
111         g_string_append (str, "\\C ");
112         break;
113       case '\"':
114         g_string_append (str, "\\\"");
115         break;
116       default:
117         g_assert_not_reached ();
118         break;
119       }
120   } while (*string);
121
122   g_string_append_c (str, '"');
123 }
124
125 /*** IMPLEMENTATIONS ***/
126
127 static gboolean 
128 enum_parse (GtkCssParser *parser,
129             GType         type,
130             int          *res)
131 {
132   char *str;
133
134   if (_gtk_css_parser_try_enum (parser, type, res))
135     return TRUE;
136
137   str = _gtk_css_parser_try_ident (parser, TRUE);
138   if (str == NULL)
139     {
140       _gtk_css_parser_error (parser, "Expected an identifier");
141       return FALSE;
142     }
143
144   _gtk_css_parser_error (parser,
145                          "Unknown value '%s' for enum type '%s'",
146                          str, g_type_name (type));
147   g_free (str);
148
149   return FALSE;
150 }
151
152 static void
153 enum_print (int         value,
154             GType       type,
155             GString    *string)
156 {
157   GEnumClass *enum_class;
158   GEnumValue *enum_value;
159
160   enum_class = g_type_class_ref (type);
161   enum_value = g_enum_get_value (enum_class, value);
162
163   g_string_append (string, enum_value->value_nick);
164
165   g_type_class_unref (enum_class);
166 }
167
168 static gboolean
169 rgba_value_parse (GtkCssParser *parser,
170                   GValue       *value)
171 {
172   GtkSymbolicColor *symbolic;
173   GdkRGBA rgba;
174
175   symbolic = _gtk_css_symbolic_value_new (parser);
176   if (symbolic == NULL)
177     return FALSE;
178
179   if (gtk_symbolic_color_resolve (symbolic, NULL, &rgba))
180     {
181       g_value_set_boxed (value, &rgba);
182       gtk_symbolic_color_unref (symbolic);
183     }
184   else
185     {
186       g_value_unset (value);
187       g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
188       g_value_take_boxed (value, symbolic);
189     }
190
191   return TRUE;
192 }
193
194 static void
195 rgba_value_print (const GValue *value,
196                   GString      *string)
197 {
198   const GdkRGBA *rgba = g_value_get_boxed (value);
199
200   if (rgba == NULL)
201     g_string_append (string, "none");
202   else
203     {
204       char *s = gdk_rgba_to_string (rgba);
205       g_string_append (string, s);
206       g_free (s);
207     }
208 }
209
210 static GtkCssValue *
211 rgba_value_compute (GtkStyleProviderPrivate *provider,
212                     GtkCssComputedValues    *values,
213                     GtkCssComputedValues    *parent_values,
214                     GtkCssValue             *specified,
215                     GtkCssDependencies      *dependencies)
216 {
217   GdkRGBA white = { 1, 1, 1, 1 };
218   const GValue *value;
219
220   value = _gtk_css_typed_value_get (specified);
221   
222   if (G_VALUE_HOLDS (value, GTK_TYPE_SYMBOLIC_COLOR))
223     {
224       GtkSymbolicColor *symbolic = g_value_get_boxed (value);
225       GtkCssValue *val;
226       GValue new_value = G_VALUE_INIT;
227       GdkRGBA rgba;
228
229       val = _gtk_css_color_value_resolve (_gtk_symbolic_color_get_css_value (symbolic),
230                                           provider,
231                                           _gtk_css_computed_values_get_value (values, GTK_CSS_PROPERTY_COLOR),
232                                           GTK_CSS_DEPENDS_ON_COLOR,
233                                           dependencies);
234       if (val != NULL)
235         {
236           rgba = *_gtk_css_rgba_value_get_rgba (val);
237           _gtk_css_value_unref (val);
238         }
239       else
240         rgba = white;
241
242       g_value_init (&new_value, GDK_TYPE_RGBA);
243       g_value_set_boxed (&new_value, &rgba);
244       return _gtk_css_typed_value_new_take (&new_value);
245     }
246   else
247     return _gtk_css_value_ref (specified);
248 }
249
250 static gboolean 
251 color_value_parse (GtkCssParser *parser,
252                    GValue       *value)
253 {
254   GtkSymbolicColor *symbolic;
255   GdkRGBA rgba;
256
257   symbolic = _gtk_css_symbolic_value_new (parser);
258   if (symbolic == NULL)
259     return FALSE;
260
261   if (gtk_symbolic_color_resolve (symbolic, NULL, &rgba))
262     {
263       GdkColor color;
264
265       color.red = rgba.red * 65535. + 0.5;
266       color.green = rgba.green * 65535. + 0.5;
267       color.blue = rgba.blue * 65535. + 0.5;
268
269       g_value_set_boxed (value, &color);
270     }
271   else
272     {
273       g_value_unset (value);
274       g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
275       g_value_take_boxed (value, symbolic);
276     }
277
278   return TRUE;
279 }
280
281 static void
282 color_value_print (const GValue *value,
283                    GString      *string)
284 {
285   const GdkColor *color = g_value_get_boxed (value);
286
287   if (color == NULL)
288     g_string_append (string, "none");
289   else
290     {
291       char *s = gdk_color_to_string (color);
292       g_string_append (string, s);
293       g_free (s);
294     }
295 }
296
297 static GtkCssValue *
298 color_value_compute (GtkStyleProviderPrivate *provider,
299                      GtkCssComputedValues    *values,
300                      GtkCssComputedValues    *parent_values,
301                      GtkCssValue             *specified,
302                      GtkCssDependencies      *dependencies)
303 {
304   GdkColor color = { 0, 65535, 65535, 65535 };
305   const GValue *value;
306
307   value = _gtk_css_typed_value_get (specified);
308   
309   if (G_VALUE_HOLDS (value, GTK_TYPE_SYMBOLIC_COLOR))
310     {
311       GValue new_value = G_VALUE_INIT;
312       GtkCssValue *val;
313
314       val = _gtk_css_color_value_resolve (_gtk_symbolic_color_get_css_value (g_value_get_boxed (value)),
315                                           provider,
316                                           _gtk_css_computed_values_get_value (values, GTK_CSS_PROPERTY_COLOR),
317                                           GTK_CSS_DEPENDS_ON_COLOR,
318                                           dependencies);
319       if (val != NULL)
320         {
321           const GdkRGBA *rgba = _gtk_css_rgba_value_get_rgba (val);
322           color.red = rgba->red * 65535. + 0.5;
323           color.green = rgba->green * 65535. + 0.5;
324           color.blue = rgba->blue * 65535. + 0.5;
325           _gtk_css_value_unref (val);
326         }
327       
328       g_value_init (&new_value, GDK_TYPE_COLOR);
329       g_value_set_boxed (&new_value, &color);
330       return _gtk_css_typed_value_new_take (&new_value);
331     }
332   else
333     return _gtk_css_value_ref (specified);
334 }
335
336 static gboolean
337 symbolic_color_value_parse (GtkCssParser *parser,
338                             GValue       *value)
339 {
340   GtkSymbolicColor *symbolic;
341
342   symbolic = _gtk_css_symbolic_value_new (parser);
343   if (symbolic == NULL)
344     return FALSE;
345
346   g_value_take_boxed (value, symbolic);
347   return TRUE;
348 }
349
350 static void
351 symbolic_color_value_print (const GValue *value,
352                             GString      *string)
353 {
354   GtkSymbolicColor *symbolic = g_value_get_boxed (value);
355
356   if (symbolic == NULL)
357     g_string_append (string, "none");
358   else
359     {
360       char *s = gtk_symbolic_color_to_string (symbolic);
361       g_string_append (string, s);
362       g_free (s);
363     }
364 }
365
366 static gboolean 
367 font_description_value_parse (GtkCssParser *parser,
368                               GValue       *value)
369 {
370   PangoFontDescription *font_desc;
371   guint mask;
372   char *str;
373
374   str = _gtk_css_parser_read_value (parser);
375   if (str == NULL)
376     return FALSE;
377
378   font_desc = pango_font_description_from_string (str);
379   mask = pango_font_description_get_set_fields (font_desc);
380   /* These values are not really correct,
381    * but the fields must be set, so we set them to something */
382   if ((mask & PANGO_FONT_MASK_FAMILY) == 0)
383     pango_font_description_set_family_static (font_desc, "Sans");
384   if ((mask & PANGO_FONT_MASK_SIZE) == 0)
385     pango_font_description_set_size (font_desc, 10 * PANGO_SCALE);
386   g_free (str);
387   g_value_take_boxed (value, font_desc);
388   return TRUE;
389 }
390
391 static void
392 font_description_value_print (const GValue *value,
393                               GString      *string)
394 {
395   const PangoFontDescription *desc = g_value_get_boxed (value);
396
397   if (desc == NULL)
398     g_string_append (string, "none");
399   else
400     {
401       char *s = pango_font_description_to_string (desc);
402       g_string_append (string, s);
403       g_free (s);
404     }
405 }
406
407 static gboolean 
408 boolean_value_parse (GtkCssParser *parser,
409                      GValue       *value)
410 {
411   if (_gtk_css_parser_try (parser, "true", TRUE) ||
412       _gtk_css_parser_try (parser, "1", TRUE))
413     {
414       g_value_set_boolean (value, TRUE);
415       return TRUE;
416     }
417   else if (_gtk_css_parser_try (parser, "false", TRUE) ||
418            _gtk_css_parser_try (parser, "0", TRUE))
419     {
420       g_value_set_boolean (value, FALSE);
421       return TRUE;
422     }
423   else
424     {
425       _gtk_css_parser_error (parser, "Expected a boolean value");
426       return FALSE;
427     }
428 }
429
430 static void
431 boolean_value_print (const GValue *value,
432                      GString      *string)
433 {
434   if (g_value_get_boolean (value))
435     g_string_append (string, "true");
436   else
437     g_string_append (string, "false");
438 }
439
440 static gboolean 
441 int_value_parse (GtkCssParser *parser,
442                  GValue       *value)
443 {
444   gint i;
445
446   if (_gtk_css_parser_begins_with (parser, '-'))
447     {
448       int res = _gtk_win32_theme_int_parse (parser, &i);
449       if (res >= 0)
450         {
451           g_value_set_int (value, i);
452           return res > 0;
453         }
454       /* < 0 => continue */
455     }
456
457   if (!_gtk_css_parser_try_int (parser, &i))
458     {
459       _gtk_css_parser_error (parser, "Expected a valid integer value");
460       return FALSE;
461     }
462
463   g_value_set_int (value, i);
464   return TRUE;
465 }
466
467 static void
468 int_value_print (const GValue *value,
469                  GString      *string)
470 {
471   g_string_append_printf (string, "%d", g_value_get_int (value));
472 }
473
474 static gboolean 
475 uint_value_parse (GtkCssParser *parser,
476                   GValue       *value)
477 {
478   guint u;
479
480   if (!_gtk_css_parser_try_uint (parser, &u))
481     {
482       _gtk_css_parser_error (parser, "Expected a valid unsigned value");
483       return FALSE;
484     }
485
486   g_value_set_uint (value, u);
487   return TRUE;
488 }
489
490 static void
491 uint_value_print (const GValue *value,
492                   GString      *string)
493 {
494   g_string_append_printf (string, "%u", g_value_get_uint (value));
495 }
496
497 static gboolean 
498 double_value_parse (GtkCssParser *parser,
499                     GValue       *value)
500 {
501   gdouble d;
502
503   if (!_gtk_css_parser_try_double (parser, &d))
504     {
505       _gtk_css_parser_error (parser, "Expected a number");
506       return FALSE;
507     }
508
509   g_value_set_double (value, d);
510   return TRUE;
511 }
512
513 static void
514 double_value_print (const GValue *value,
515                     GString      *string)
516 {
517   string_append_double (string, g_value_get_double (value));
518 }
519
520 static gboolean 
521 float_value_parse (GtkCssParser *parser,
522                    GValue       *value)
523 {
524   gdouble d;
525
526   if (!_gtk_css_parser_try_double (parser, &d))
527     {
528       _gtk_css_parser_error (parser, "Expected a number");
529       return FALSE;
530     }
531
532   g_value_set_float (value, d);
533   return TRUE;
534 }
535
536 static void
537 float_value_print (const GValue *value,
538                    GString      *string)
539 {
540   string_append_double (string, g_value_get_float (value));
541 }
542
543 static gboolean 
544 string_value_parse (GtkCssParser *parser,
545                     GValue       *value)
546 {
547   char *str = _gtk_css_parser_read_string (parser);
548
549   if (str == NULL)
550     return FALSE;
551
552   g_value_take_string (value, str);
553   return TRUE;
554 }
555
556 static void
557 string_value_print (const GValue *value,
558                     GString      *str)
559 {
560   string_append_string (str, g_value_get_string (value));
561 }
562
563 static gboolean 
564 theming_engine_value_parse (GtkCssParser *parser,
565                             GValue       *value)
566 {
567   GtkThemingEngine *engine;
568   char *str;
569
570   if (_gtk_css_parser_try (parser, "none", TRUE))
571     {
572       g_value_set_object (value, gtk_theming_engine_load (NULL));
573       return TRUE;
574     }
575
576   str = _gtk_css_parser_try_ident (parser, TRUE);
577   if (str == NULL)
578     {
579       _gtk_css_parser_error (parser, "Expected a valid theme name");
580       return FALSE;
581     }
582
583   engine = gtk_theming_engine_load (str);
584
585   if (engine == NULL)
586     {
587       _gtk_css_parser_error (parser, "Theming engine '%s' not found", str);
588       g_free (str);
589       return FALSE;
590     }
591
592   g_value_set_object (value, engine);
593   g_free (str);
594   return TRUE;
595 }
596
597 static void
598 theming_engine_value_print (const GValue *value,
599                             GString      *string)
600 {
601   GtkThemingEngine *engine;
602   char *name;
603
604   engine = g_value_get_object (value);
605   if (engine == NULL)
606     g_string_append (string, "none");
607   else
608     {
609       /* XXX: gtk_theming_engine_get_name()? */
610       g_object_get (engine, "name", &name, NULL);
611       g_string_append (string, name ? name : "none");
612       g_free (name);
613     }
614 }
615
616 static gboolean 
617 border_value_parse (GtkCssParser *parser,
618                     GValue       *value)
619 {
620   GtkBorder border = { 0, };
621   guint i;
622   int numbers[4];
623
624   for (i = 0; i < G_N_ELEMENTS (numbers); i++)
625     {
626       if (_gtk_css_parser_begins_with (parser, '-'))
627         {
628           /* These are strictly speaking signed, but we want to be able to use them
629              for unsigned types too, as the actual ranges of values make this safe */
630           int res = _gtk_win32_theme_int_parse (parser, &numbers[i]);
631
632           if (res == 0) /* Parse error, report */
633             return FALSE;
634
635           if (res < 0) /* Nothing known to expand */
636             break;
637         }
638       else
639         {
640           if (!_gtk_css_parser_try_length (parser, &numbers[i]))
641             break;
642         }
643     }
644
645   if (i == 0)
646     {
647       _gtk_css_parser_error (parser, "Expected valid border");
648       return FALSE;
649     }
650
651   border.top = numbers[0];
652   if (i > 1)
653     border.right = numbers[1];
654   else
655     border.right = border.top;
656   if (i > 2)
657     border.bottom = numbers[2];
658   else
659     border.bottom = border.top;
660   if (i > 3)
661     border.left = numbers[3];
662   else
663     border.left = border.right;
664
665   g_value_set_boxed (value, &border);
666   return TRUE;
667 }
668
669 static void
670 border_value_print (const GValue *value, GString *string)
671 {
672   const GtkBorder *border = g_value_get_boxed (value);
673
674   if (border == NULL)
675     g_string_append (string, "none");
676   else if (border->left != border->right)
677     g_string_append_printf (string, "%d %d %d %d", border->top, border->right, border->bottom, border->left);
678   else if (border->top != border->bottom)
679     g_string_append_printf (string, "%d %d %d", border->top, border->right, border->bottom);
680   else if (border->top != border->left)
681     g_string_append_printf (string, "%d %d", border->top, border->right);
682   else
683     g_string_append_printf (string, "%d", border->top);
684 }
685
686 static gboolean 
687 gradient_value_parse (GtkCssParser *parser,
688                       GValue       *value)
689 {
690   GtkGradient *gradient;
691
692   gradient = _gtk_gradient_parse (parser);
693   if (gradient == NULL)
694     return FALSE;
695
696   g_value_take_boxed (value, gradient);
697   return TRUE;
698 }
699
700 static void
701 gradient_value_print (const GValue *value,
702                       GString      *string)
703 {
704   GtkGradient *gradient = g_value_get_boxed (value);
705
706   if (gradient == NULL)
707     g_string_append (string, "none");
708   else
709     {
710       char *s = gtk_gradient_to_string (gradient);
711       g_string_append (string, s);
712       g_free (s);
713     }
714 }
715
716 static gboolean 
717 pattern_value_parse (GtkCssParser *parser,
718                      GValue       *value)
719 {
720   if (_gtk_css_parser_try (parser, "none", TRUE))
721     {
722       /* nothing to do here */
723     }
724   else if (_gtk_css_parser_begins_with (parser, '-'))
725     {
726       g_value_unset (value);
727       g_value_init (value, GTK_TYPE_GRADIENT);
728       return gradient_value_parse (parser, value);
729     }
730   else
731     {
732       GError *error = NULL;
733       gchar *path;
734       GdkPixbuf *pixbuf;
735       GFile *file;
736       cairo_surface_t *surface;
737       cairo_pattern_t *pattern;
738       cairo_t *cr;
739       cairo_matrix_t matrix;
740
741       file = _gtk_css_parser_read_url (parser);
742       if (file == NULL)
743         return FALSE;
744
745       path = g_file_get_path (file);
746       g_object_unref (file);
747
748       pixbuf = gdk_pixbuf_new_from_file (path, &error);
749       g_free (path);
750       if (pixbuf == NULL)
751         {
752           _gtk_css_parser_take_error (parser, error);
753           return FALSE;
754         }
755
756       surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
757                                             gdk_pixbuf_get_width (pixbuf),
758                                             gdk_pixbuf_get_height (pixbuf));
759       cr = cairo_create (surface);
760       gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
761       cairo_paint (cr);
762       pattern = cairo_pattern_create_for_surface (surface);
763
764       cairo_matrix_init_scale (&matrix,
765                                gdk_pixbuf_get_width (pixbuf),
766                                gdk_pixbuf_get_height (pixbuf));
767       cairo_pattern_set_matrix (pattern, &matrix);
768
769       cairo_surface_destroy (surface);
770       cairo_destroy (cr);
771       g_object_unref (pixbuf);
772
773       g_value_take_boxed (value, pattern);
774     }
775   
776   return TRUE;
777 }
778
779 static cairo_status_t
780 surface_write (void                *closure,
781                const unsigned char *data,
782                unsigned int         length)
783 {
784   g_byte_array_append (closure, data, length);
785
786   return CAIRO_STATUS_SUCCESS;
787 }
788
789 static void
790 surface_print (cairo_surface_t *surface,
791                GString *        string)
792 {
793 #if CAIRO_HAS_PNG_FUNCTIONS
794   GByteArray *array;
795   char *base64;
796   
797   array = g_byte_array_new ();
798   cairo_surface_write_to_png_stream (surface, surface_write, array);
799   base64 = g_base64_encode (array->data, array->len);
800   g_byte_array_free (array, TRUE);
801
802   g_string_append (string, "url(\"data:image/png;base64,");
803   g_string_append (string, base64);
804   g_string_append (string, "\")");
805
806   g_free (base64);
807 #else
808   g_string_append (string, "none /* you need cairo png functions enabled to make this work */");
809 #endif
810 }
811
812 static void
813 pattern_value_print (const GValue *value,
814                      GString      *string)
815 {
816   cairo_pattern_t *pattern;
817   cairo_surface_t *surface;
818
819   pattern = g_value_get_boxed (value);
820
821   if (pattern == NULL)
822     {
823       g_string_append (string, "none");
824       return;
825     }
826
827   switch (cairo_pattern_get_type (pattern))
828     {
829     case CAIRO_PATTERN_TYPE_SURFACE:
830       if (cairo_pattern_get_surface (pattern, &surface) != CAIRO_STATUS_SUCCESS)
831         {
832           g_assert_not_reached ();
833         }
834       surface_print (surface, string);
835       break;
836     case CAIRO_PATTERN_TYPE_LINEAR:
837     case CAIRO_PATTERN_TYPE_RADIAL:
838       g_string_append (string, "none /* FIXME: add support for printing gradients */");
839       break;
840     case CAIRO_PATTERN_TYPE_SOLID:
841     default:
842       g_assert_not_reached ();
843       break;
844     }
845 }
846
847 static GtkCssValue *
848 pattern_value_compute (GtkStyleProviderPrivate *provider,
849                        GtkCssComputedValues    *values,
850                        GtkCssComputedValues    *parent_values,
851                        GtkCssValue             *specified,
852                        GtkCssDependencies      *dependencies)
853 {
854   const GValue *value = _gtk_css_typed_value_get (specified);
855
856   if (G_VALUE_HOLDS (value, GTK_TYPE_GRADIENT))
857     {
858       GValue new_value = G_VALUE_INIT;
859       cairo_pattern_t *gradient;
860       
861       gradient = _gtk_gradient_resolve_full (g_value_get_boxed (value), provider, values, parent_values, dependencies);
862
863       g_value_init (&new_value, CAIRO_GOBJECT_TYPE_PATTERN);
864       g_value_take_boxed (&new_value, gradient);
865       return _gtk_css_typed_value_new_take (&new_value);
866     }
867   else
868     return _gtk_css_value_ref (specified);
869 }
870
871 static gboolean 
872 enum_value_parse (GtkCssParser *parser,
873                   GValue       *value)
874 {
875   int v;
876
877   if (enum_parse (parser, G_VALUE_TYPE (value), &v))
878     {
879       g_value_set_enum (value, v);
880       return TRUE;
881     }
882
883   return FALSE;
884 }
885
886 static void
887 enum_value_print (const GValue *value,
888                   GString      *string)
889 {
890   enum_print (g_value_get_enum (value), G_VALUE_TYPE (value), string);
891 }
892
893 static gboolean 
894 flags_value_parse (GtkCssParser *parser,
895                    GValue       *value)
896 {
897   GFlagsClass *flags_class;
898   GFlagsValue *flag_value;
899   guint flags = 0;
900   char *str;
901
902   flags_class = g_type_class_ref (G_VALUE_TYPE (value));
903
904   do {
905     str = _gtk_css_parser_try_ident (parser, TRUE);
906     if (str == NULL)
907       {
908         _gtk_css_parser_error (parser, "Expected an identifier");
909         g_type_class_unref (flags_class);
910         return FALSE;
911       }
912
913       flag_value = g_flags_get_value_by_nick (flags_class, str);
914       if (!flag_value)
915         {
916           _gtk_css_parser_error (parser,
917                                  "Unknown flag value '%s' for type '%s'",
918                                  str, g_type_name (G_VALUE_TYPE (value)));
919           /* XXX Do we want to return FALSE here? We can get
920            * forward-compatibility for new values this way
921            */
922           g_free (str);
923           g_type_class_unref (flags_class);
924           return FALSE;
925         }
926
927       g_free (str);
928     }
929   while (_gtk_css_parser_try (parser, ",", FALSE));
930
931   g_type_class_unref (flags_class);
932
933   g_value_set_enum (value, flags);
934
935   return TRUE;
936 }
937
938 static void
939 flags_value_print (const GValue *value,
940                    GString      *string)
941 {
942   GFlagsClass *flags_class;
943   guint i, flags;
944
945   flags_class = g_type_class_ref (G_VALUE_TYPE (value));
946   flags = g_value_get_flags (value);
947
948   for (i = 0; i < flags_class->n_values; i++)
949     {
950       GFlagsValue *flags_value = &flags_class->values[i];
951
952       if (flags & flags_value->value)
953         {
954           if (string->len != 0)
955             g_string_append (string, ", ");
956
957           g_string_append (string, flags_value->value_nick);
958         }
959     }
960
961   g_type_class_unref (flags_class);
962 }
963
964 /*** API ***/
965
966 static void
967 gtk_css_style_funcs_init (void)
968 {
969   if (G_LIKELY (parse_funcs != NULL))
970     return;
971
972   parse_funcs = g_hash_table_new (NULL, NULL);
973   print_funcs = g_hash_table_new (NULL, NULL);
974   compute_funcs = g_hash_table_new (NULL, NULL);
975
976   register_conversion_function (GDK_TYPE_RGBA,
977                                 rgba_value_parse,
978                                 rgba_value_print,
979                                 rgba_value_compute);
980   register_conversion_function (GDK_TYPE_COLOR,
981                                 color_value_parse,
982                                 color_value_print,
983                                 color_value_compute);
984   register_conversion_function (GTK_TYPE_SYMBOLIC_COLOR,
985                                 symbolic_color_value_parse,
986                                 symbolic_color_value_print,
987                                 NULL);
988   register_conversion_function (PANGO_TYPE_FONT_DESCRIPTION,
989                                 font_description_value_parse,
990                                 font_description_value_print,
991                                 NULL);
992   register_conversion_function (G_TYPE_BOOLEAN,
993                                 boolean_value_parse,
994                                 boolean_value_print,
995                                 NULL);
996   register_conversion_function (G_TYPE_INT,
997                                 int_value_parse,
998                                 int_value_print,
999                                 NULL);
1000   register_conversion_function (G_TYPE_UINT,
1001                                 uint_value_parse,
1002                                 uint_value_print,
1003                                 NULL);
1004   register_conversion_function (G_TYPE_DOUBLE,
1005                                 double_value_parse,
1006                                 double_value_print,
1007                                 NULL);
1008   register_conversion_function (G_TYPE_FLOAT,
1009                                 float_value_parse,
1010                                 float_value_print,
1011                                 NULL);
1012   register_conversion_function (G_TYPE_STRING,
1013                                 string_value_parse,
1014                                 string_value_print,
1015                                 NULL);
1016   register_conversion_function (GTK_TYPE_THEMING_ENGINE,
1017                                 theming_engine_value_parse,
1018                                 theming_engine_value_print,
1019                                 NULL);
1020   register_conversion_function (GTK_TYPE_BORDER,
1021                                 border_value_parse,
1022                                 border_value_print,
1023                                 NULL);
1024   register_conversion_function (GTK_TYPE_GRADIENT,
1025                                 gradient_value_parse,
1026                                 gradient_value_print,
1027                                 NULL);
1028   register_conversion_function (CAIRO_GOBJECT_TYPE_PATTERN,
1029                                 pattern_value_parse,
1030                                 pattern_value_print,
1031                                 pattern_value_compute);
1032   register_conversion_function (G_TYPE_ENUM,
1033                                 enum_value_parse,
1034                                 enum_value_print,
1035                                 NULL);
1036   register_conversion_function (G_TYPE_FLAGS,
1037                                 flags_value_parse,
1038                                 flags_value_print,
1039                                 NULL);
1040 }
1041
1042 /**
1043  * _gtk_css_style_parse_value:
1044  * @value: the value to parse into. Must be a valid initialized #GValue
1045  * @parser: the parser to parse from
1046  *
1047  * This is the generic parsing function used for CSS values. If the
1048  * function fails to parse a value, it will emit an error on @parser,
1049  * return %FALSE and not touch @value.
1050  *
1051  * Returns: %TRUE if parsing succeeded.
1052  **/
1053 gboolean
1054 _gtk_css_style_parse_value (GValue       *value,
1055                             GtkCssParser *parser)
1056 {
1057   GtkStyleParseFunc func;
1058
1059   g_return_val_if_fail (value != NULL, FALSE);
1060   g_return_val_if_fail (parser != NULL, FALSE);
1061
1062   gtk_css_style_funcs_init ();
1063
1064   func = g_hash_table_lookup (parse_funcs,
1065                               GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
1066   if (func == NULL)
1067     func = g_hash_table_lookup (parse_funcs,
1068                                 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
1069
1070   if (func == NULL)
1071     {
1072       _gtk_css_parser_error (parser,
1073                              "Cannot convert to type '%s'",
1074                              g_type_name (G_VALUE_TYPE (value)));
1075       return FALSE;
1076     }
1077
1078   return (*func) (parser, value);
1079 }
1080
1081 /**
1082  * _gtk_css_style_print_value:
1083  * @value: an initialized GValue returned from _gtk_css_style_parse()
1084  * @string: the string to print into
1085  *
1086  * Prints @value into @string as a CSS value. If @value is not a
1087  * valid value, a random string will be printed instead.
1088  **/
1089 void
1090 _gtk_css_style_print_value (const GValue *value,
1091                             GString      *string)
1092 {
1093   GtkStylePrintFunc func;
1094
1095   gtk_css_style_funcs_init ();
1096
1097   func = g_hash_table_lookup (print_funcs,
1098                               GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
1099   if (func == NULL)
1100     func = g_hash_table_lookup (print_funcs,
1101                                 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
1102
1103   if (func == NULL)
1104     {
1105       char *s = g_strdup_value_contents (value);
1106       g_string_append (string, s);
1107       g_free (s);
1108       return;
1109     }
1110   
1111   func (value, string);
1112 }
1113
1114 /**
1115  * _gtk_css_style_compute_value:
1116  * @provider: Style provider to look up information from
1117  * @values: The values to compute for
1118  * @parent_values: Values to look up inherited values from
1119  * @target_type: Type the resulting value should have
1120  * @specified: the value to use for the computation
1121  * @dependencies: (out): Value initialized with 0 to take the dependencies
1122  *     of the returned value
1123  *
1124  * Converts the @specified value into the @computed value using the
1125  * information in @context. The values must have matching types, ie
1126  * @specified must be a result of a call to
1127  * _gtk_css_style_parse_value() with the same type as @computed.
1128  *
1129  * Returns: the resulting value
1130  **/
1131 GtkCssValue *
1132 _gtk_css_style_compute_value (GtkStyleProviderPrivate *provider,
1133                               GtkCssComputedValues    *values,
1134                               GtkCssComputedValues    *parent_values,
1135                               GType                    target_type,
1136                               GtkCssValue             *specified,
1137                               GtkCssDependencies      *dependencies)
1138 {
1139   GtkStyleComputeFunc func;
1140
1141   g_return_val_if_fail (GTK_IS_STYLE_PROVIDER (provider), NULL);
1142   g_return_val_if_fail (GTK_IS_CSS_COMPUTED_VALUES (values), NULL);
1143   g_return_val_if_fail (parent_values == NULL || GTK_IS_CSS_COMPUTED_VALUES (parent_values), NULL);
1144   g_return_val_if_fail (*dependencies == 0, NULL);
1145
1146   gtk_css_style_funcs_init ();
1147
1148   func = g_hash_table_lookup (compute_funcs,
1149                               GSIZE_TO_POINTER (target_type));
1150   if (func == NULL)
1151     func = g_hash_table_lookup (compute_funcs,
1152                                 GSIZE_TO_POINTER (g_type_fundamental (target_type)));
1153
1154   if (func)
1155     return func (provider, values, parent_values, specified, dependencies);
1156   else
1157     return _gtk_css_value_ref (specified);
1158 }
1159