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