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