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