]> Pileus Git - ~andy/gtk/blob - gtk/gtkstyleproperty.c
styleproperty: Add a helper function for printing doubles
[~andy/gtk] / gtk / gtkstyleproperty.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "config.h"
21
22 #include "gtkstylepropertyprivate.h"
23
24 #include <errno.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include <gdk-pixbuf/gdk-pixbuf.h>
29 #include <cairo-gobject.h>
30
31 #include "gtkcssprovider.h"
32 #include "gtkcssparserprivate.h"
33
34 /* the actual parsers we have */
35 #include "gtkanimationdescription.h"
36 #include "gtkbindings.h"
37 #include "gtk9slice.h"
38 #include "gtkgradient.h"
39 #include "gtkshadowprivate.h"
40 #include "gtkthemingengine.h"
41 #include "gtktypebuiltins.h"
42
43 static GHashTable *parse_funcs = NULL;
44 static GHashTable *print_funcs = NULL;
45 static GHashTable *properties = NULL;
46
47 static void
48 register_conversion_function (GType             type,
49                               GtkStyleParseFunc parse,
50                               GtkStylePrintFunc print)
51 {
52   if (parse)
53     g_hash_table_insert (parse_funcs, GSIZE_TO_POINTER (type), parse);
54   if (print)
55     g_hash_table_insert (print_funcs, GSIZE_TO_POINTER (type), print);
56 }
57
58 /*** IMPLEMENTATIONS ***/
59
60 static gboolean 
61 rgba_value_parse (GtkCssParser *parser,
62                   GFile        *base,
63                   GValue       *value)
64 {
65   GtkSymbolicColor *symbolic;
66   GdkRGBA rgba;
67
68   symbolic = _gtk_css_parser_read_symbolic_color (parser);
69   if (symbolic == NULL)
70     return FALSE;
71
72   if (gtk_symbolic_color_resolve (symbolic, NULL, &rgba))
73     {
74       g_value_set_boxed (value, &rgba);
75       gtk_symbolic_color_unref (symbolic);
76     }
77   else
78     {
79       g_value_unset (value);
80       g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
81       g_value_take_boxed (value, symbolic);
82     }
83
84   return TRUE;
85 }
86
87 static void
88 rgba_value_print (const GValue *value,
89                   GString      *string)
90 {
91   const GdkRGBA *rgba = g_value_get_boxed (value);
92
93   if (rgba == NULL)
94     g_string_append (string, "none");
95   else
96     {
97       char *s = gdk_rgba_to_string (rgba);
98       g_string_append (string, s);
99       g_free (s);
100     }
101 }
102
103 static gboolean 
104 color_value_parse (GtkCssParser *parser,
105                    GFile        *base,
106                    GValue       *value)
107 {
108   GtkSymbolicColor *symbolic;
109   GdkRGBA rgba;
110
111   symbolic = _gtk_css_parser_read_symbolic_color (parser);
112   if (symbolic == NULL)
113     return FALSE;
114
115   if (gtk_symbolic_color_resolve (symbolic, NULL, &rgba))
116     {
117       GdkColor color;
118
119       color.red = rgba.red * 65535. + 0.5;
120       color.green = rgba.green * 65535. + 0.5;
121       color.blue = rgba.blue * 65535. + 0.5;
122
123       g_value_set_boxed (value, &color);
124     }
125   else
126     {
127       g_value_unset (value);
128       g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
129       g_value_take_boxed (value, symbolic);
130     }
131
132   return TRUE;
133 }
134
135 static void
136 color_value_print (const GValue *value,
137                    GString      *string)
138 {
139   const GdkColor *color = g_value_get_boxed (value);
140
141   if (color == NULL)
142     g_string_append (string, "none");
143   else
144     {
145       char *s = gdk_color_to_string (color);
146       g_string_append (string, s);
147       g_free (s);
148     }
149 }
150
151 static gboolean
152 symbolic_color_value_parse (GtkCssParser *parser,
153                             GFile        *base,
154                             GValue       *value)
155 {
156   GtkSymbolicColor *symbolic;
157
158   symbolic = _gtk_css_parser_read_symbolic_color (parser);
159   if (symbolic == NULL)
160     return FALSE;
161
162   g_value_take_boxed (value, symbolic);
163   return TRUE;
164 }
165
166 static void
167 symbolic_color_value_print (const GValue *value,
168                             GString      *string)
169 {
170   GtkSymbolicColor *symbolic = g_value_get_boxed (value);
171
172   if (symbolic == NULL)
173     g_string_append (string, "none");
174   else
175     {
176       char *s = gtk_symbolic_color_to_string (symbolic);
177       g_string_append (string, s);
178       g_free (s);
179     }
180 }
181
182 static gboolean 
183 font_description_value_parse (GtkCssParser *parser,
184                               GFile        *base,
185                               GValue       *value)
186 {
187   PangoFontDescription *font_desc;
188   char *str;
189
190   str = _gtk_css_parser_read_value (parser);
191   if (str == NULL)
192     return FALSE;
193
194   font_desc = pango_font_description_from_string (str);
195   g_free (str);
196   g_value_take_boxed (value, font_desc);
197   return TRUE;
198 }
199
200 static void
201 font_description_value_print (const GValue *value,
202                               GString      *string)
203 {
204   const PangoFontDescription *desc = g_value_get_boxed (value);
205
206   if (desc == NULL)
207     g_string_append (string, "none");
208   else
209     {
210       char *s = pango_font_description_to_string (desc);
211       g_string_append (string, s);
212       g_free (s);
213     }
214 }
215
216 static gboolean 
217 boolean_value_parse (GtkCssParser *parser,
218                      GFile        *base,
219                      GValue       *value)
220 {
221   if (_gtk_css_parser_try (parser, "true", TRUE) ||
222       _gtk_css_parser_try (parser, "1", TRUE))
223     {
224       g_value_set_boolean (value, TRUE);
225       return TRUE;
226     }
227   else if (_gtk_css_parser_try (parser, "false", TRUE) ||
228            _gtk_css_parser_try (parser, "0", TRUE))
229     {
230       g_value_set_boolean (value, FALSE);
231       return TRUE;
232     }
233   else
234     {
235       _gtk_css_parser_error (parser, "Expected a boolean value");
236       return FALSE;
237     }
238 }
239
240 static void
241 boolean_value_print (const GValue *value,
242                      GString      *string)
243 {
244   if (g_value_get_boolean (value))
245     g_string_append (string, "true");
246   else
247     g_string_append (string, "false");
248 }
249
250 static gboolean 
251 int_value_parse (GtkCssParser *parser,
252                  GFile        *base,
253                  GValue       *value)
254 {
255   gint i;
256
257   if (!_gtk_css_parser_try_int (parser, &i))
258     {
259       _gtk_css_parser_error (parser, "Expected a valid integer value");
260       return FALSE;
261     }
262
263   g_value_set_int (value, i);
264   return TRUE;
265 }
266
267 static void
268 int_value_print (const GValue *value,
269                  GString      *string)
270 {
271   g_string_append_printf (string, "%d", g_value_get_int (value));
272 }
273
274 static gboolean 
275 uint_value_parse (GtkCssParser *parser,
276                   GFile        *base,
277                   GValue       *value)
278 {
279   guint u;
280
281   if (!_gtk_css_parser_try_uint (parser, &u))
282     {
283       _gtk_css_parser_error (parser, "Expected a valid unsigned value");
284       return FALSE;
285     }
286
287   g_value_set_uint (value, u);
288   return TRUE;
289 }
290
291 static void
292 uint_value_print (const GValue *value,
293                   GString      *string)
294 {
295   g_string_append_printf (string, "%u", g_value_get_uint (value));
296 }
297
298 static gboolean 
299 double_value_parse (GtkCssParser *parser,
300                     GFile        *base,
301                     GValue       *value)
302 {
303   gdouble d;
304
305   if (!_gtk_css_parser_try_double (parser, &d))
306     {
307       _gtk_css_parser_error (parser, "Expected a number");
308       return FALSE;
309     }
310
311   g_value_set_double (value, d);
312   return TRUE;
313 }
314
315 static void
316 string_append_double (GString *string,
317                       double   d)
318 {
319   char buf[G_ASCII_DTOSTR_BUF_SIZE];
320
321   g_ascii_dtostr (buf, sizeof (buf), d);
322   g_string_append (string, buf);
323 }
324
325 static void
326 double_value_print (const GValue *value,
327                     GString      *string)
328 {
329   string_append_double (string, g_value_get_double (value));
330 }
331
332 static gboolean 
333 float_value_parse (GtkCssParser *parser,
334                    GFile        *base,
335                    GValue       *value)
336 {
337   gdouble d;
338
339   if (!_gtk_css_parser_try_double (parser, &d))
340     {
341       _gtk_css_parser_error (parser, "Expected a number");
342       return FALSE;
343     }
344
345   g_value_set_float (value, d);
346   return TRUE;
347 }
348
349 static void
350 float_value_print (const GValue *value,
351                    GString      *string)
352 {
353   string_append_double (string, g_value_get_float (value));
354 }
355
356 static gboolean 
357 string_value_parse (GtkCssParser *parser,
358                     GFile        *base,
359                     GValue       *value)
360 {
361   char *str = _gtk_css_parser_read_string (parser);
362
363   if (str == NULL)
364     return FALSE;
365
366   g_value_take_string (value, str);
367   return TRUE;
368 }
369
370 static void
371 string_value_print (const GValue *value,
372                     GString      *str)
373 {
374   const char *string;
375   gsize len;
376
377   string = g_value_get_string (value);
378   g_string_append_c (str, '"');
379
380   do {
381     len = strcspn (string, "\"\n\r\f");
382     g_string_append (str, string);
383     string += len;
384     switch (*string)
385       {
386       case '\0':
387         break;
388       case '\n':
389         g_string_append (str, "\\A ");
390         break;
391       case '\r':
392         g_string_append (str, "\\D ");
393         break;
394       case '\f':
395         g_string_append (str, "\\C ");
396         break;
397       case '\"':
398         g_string_append (str, "\\\"");
399         break;
400       default:
401         g_assert_not_reached ();
402         break;
403       }
404   } while (*string);
405
406   g_string_append_c (str, '"');
407 }
408
409 static gboolean 
410 theming_engine_value_parse (GtkCssParser *parser,
411                             GFile        *base,
412                             GValue       *value)
413 {
414   GtkThemingEngine *engine;
415   char *str;
416
417   str = _gtk_css_parser_try_ident (parser, TRUE);
418   if (str == NULL)
419     {
420       _gtk_css_parser_error (parser, "Expected a valid theme name");
421       return FALSE;
422     }
423
424   engine = gtk_theming_engine_load (str);
425   if (engine == NULL)
426     {
427       _gtk_css_parser_error (parser, "Themeing engine '%s' not found", str);
428       g_free (str);
429       return FALSE;
430     }
431
432   g_value_set_object (value, engine);
433   g_free (str);
434   return TRUE;
435 }
436
437 static void
438 theming_engine_value_print (const GValue *value,
439                             GString      *string)
440 {
441   GtkThemingEngine *engine;
442   char *name;
443
444   engine = g_value_get_object (value);
445   if (engine == NULL)
446     g_string_append (string, "none");
447   else
448     {
449       /* XXX: gtk_theming_engine_get_name()? */
450       g_object_get (engine, "name", &name, NULL);
451       g_string_append (string, name);
452       g_free (name);
453     }
454 }
455
456 static gboolean 
457 animation_description_value_parse (GtkCssParser *parser,
458                                    GFile        *base,
459                                    GValue       *value)
460 {
461   GtkAnimationDescription *desc;
462   char *str;
463
464   str = _gtk_css_parser_read_value (parser);
465   if (str == NULL)
466     return FALSE;
467
468   desc = _gtk_animation_description_from_string (str);
469   g_free (str);
470
471   if (desc == NULL)
472     {
473       _gtk_css_parser_error (parser, "Invalid animation description");
474       return FALSE;
475     }
476   
477   g_value_take_boxed (value, desc);
478   return TRUE;
479 }
480
481 static void
482 animation_description_value_print (const GValue *value,
483                                    GString      *string)
484 {
485   GtkAnimationDescription *desc = g_value_get_boxed (value);
486
487   if (desc == NULL)
488     g_string_append (string, "none");
489   else
490     _gtk_animation_description_print (desc, string);
491 }
492
493 static gboolean 
494 border_value_parse (GtkCssParser *parser,
495                     GFile        *base,
496                     GValue       *value)
497 {
498   GtkBorder border = { 0, };
499   guint i, numbers[4];
500
501   for (i = 0; i < G_N_ELEMENTS (numbers); i++)
502     {
503       if (!_gtk_css_parser_try_uint (parser, &numbers[i]))
504         break;
505
506       /* XXX: shouldn't allow spaces here? */
507       _gtk_css_parser_try (parser, "px", TRUE);
508     }
509
510   if (i == 0)
511     {
512       _gtk_css_parser_error (parser, "Expected valid border");
513       return FALSE;
514     }
515
516   border.top = numbers[0];
517   if (i > 1)
518     border.right = numbers[1];
519   else
520     border.right = border.top;
521   if (i > 2)
522     border.bottom = numbers[2];
523   else
524     border.bottom = border.top;
525   if (i > 3)
526     border.left = numbers[3];
527   else
528     border.left = border.right;
529
530   g_value_set_boxed (value, &border);
531   return TRUE;
532 }
533
534 static void
535 border_value_print (const GValue *value, GString *string)
536 {
537   const GtkBorder *border = g_value_get_boxed (value);
538
539   if (border == NULL)
540     g_string_append (string, "none");
541   else if (border->left != border->right)
542     g_string_append_printf (string, "%d %d %d %d", border->top, border->right, border->bottom, border->left);
543   else if (border->top != border->bottom)
544     g_string_append_printf (string, "%d %d %d", border->top, border->right, border->bottom);
545   else if (border->top != border->left)
546     g_string_append_printf (string, "%d %d", border->top, border->right);
547   else
548     g_string_append_printf (string, "%d", border->top);
549 }
550
551 static gboolean 
552 gradient_value_parse (GtkCssParser *parser,
553                       GFile        *base,
554                       GValue       *value)
555 {
556   GtkGradient *gradient;
557   cairo_pattern_type_t type;
558   gdouble coords[6];
559   guint i;
560
561   if (!_gtk_css_parser_try (parser, "-gtk-gradient", TRUE))
562     {
563       _gtk_css_parser_error (parser,
564                              "Expected '-gtk-gradient'");
565       return FALSE;
566     }
567
568   if (!_gtk_css_parser_try (parser, "(", TRUE))
569     {
570       _gtk_css_parser_error (parser,
571                              "Expected '(' after '-gtk-gradient'");
572       return FALSE;
573     }
574
575   /* Parse gradient type */
576   if (_gtk_css_parser_try (parser, "linear", TRUE))
577     type = CAIRO_PATTERN_TYPE_LINEAR;
578   else if (_gtk_css_parser_try (parser, "radial", TRUE))
579     type = CAIRO_PATTERN_TYPE_RADIAL;
580   else
581     {
582       _gtk_css_parser_error (parser,
583                              "Gradient type must be 'radial' or 'linear'");
584       return FALSE;
585     }
586
587   /* Parse start/stop position parameters */
588   for (i = 0; i < 2; i++)
589     {
590       if (! _gtk_css_parser_try (parser, ",", TRUE))
591         {
592           _gtk_css_parser_error (parser,
593                                  "Expected ','");
594           return FALSE;
595         }
596
597       if (_gtk_css_parser_try (parser, "left", TRUE))
598         coords[i * 3] = 0;
599       else if (_gtk_css_parser_try (parser, "right", TRUE))
600         coords[i * 3] = 1;
601       else if (_gtk_css_parser_try (parser, "center", TRUE))
602         coords[i * 3] = 0.5;
603       else if (!_gtk_css_parser_try_double (parser, &coords[i * 3]))
604         {
605           _gtk_css_parser_error (parser,
606                                  "Expected a valid X coordinate");
607           return FALSE;
608         }
609
610       if (_gtk_css_parser_try (parser, "top", TRUE))
611         coords[i * 3 + 1] = 0;
612       else if (_gtk_css_parser_try (parser, "bottom", TRUE))
613         coords[i * 3 + 1] = 1;
614       else if (_gtk_css_parser_try (parser, "center", TRUE))
615         coords[i * 3 + 1] = 0.5;
616       else if (!_gtk_css_parser_try_double (parser, &coords[i * 3 + 1]))
617         {
618           _gtk_css_parser_error (parser,
619                                  "Expected a valid Y coordinate");
620           return FALSE;
621         }
622
623       if (type == CAIRO_PATTERN_TYPE_RADIAL)
624         {
625           /* Parse radius */
626           if (! _gtk_css_parser_try (parser, ",", TRUE))
627             {
628               _gtk_css_parser_error (parser,
629                                      "Expected ','");
630               return FALSE;
631             }
632
633           if (! _gtk_css_parser_try_double (parser, &coords[(i * 3) + 2]))
634             {
635               _gtk_css_parser_error (parser,
636                                      "Expected a numer for the radius");
637               return FALSE;
638             }
639         }
640     }
641
642   if (type == CAIRO_PATTERN_TYPE_LINEAR)
643     gradient = gtk_gradient_new_linear (coords[0], coords[1], coords[3], coords[4]);
644   else
645     gradient = gtk_gradient_new_radial (coords[0], coords[1], coords[2],
646                                         coords[3], coords[4], coords[5]);
647
648   while (_gtk_css_parser_try (parser, ",", TRUE))
649     {
650       GtkSymbolicColor *color;
651       gdouble position;
652
653       if (_gtk_css_parser_try (parser, "from", TRUE))
654         {
655           position = 0;
656
657           if (!_gtk_css_parser_try (parser, "(", TRUE))
658             {
659               gtk_gradient_unref (gradient);
660               _gtk_css_parser_error (parser,
661                                      "Expected '('");
662               return FALSE;
663             }
664
665         }
666       else if (_gtk_css_parser_try (parser, "to", TRUE))
667         {
668           position = 1;
669
670           if (!_gtk_css_parser_try (parser, "(", TRUE))
671             {
672               gtk_gradient_unref (gradient);
673               _gtk_css_parser_error (parser,
674                                      "Expected '('");
675               return FALSE;
676             }
677
678         }
679       else if (_gtk_css_parser_try (parser, "color-stop", TRUE))
680         {
681           if (!_gtk_css_parser_try (parser, "(", TRUE))
682             {
683               gtk_gradient_unref (gradient);
684               _gtk_css_parser_error (parser,
685                                      "Expected '('");
686               return FALSE;
687             }
688
689           if (!_gtk_css_parser_try_double (parser, &position))
690             {
691               gtk_gradient_unref (gradient);
692               _gtk_css_parser_error (parser,
693                                      "Expected a valid number");
694               return FALSE;
695             }
696
697           if (!_gtk_css_parser_try (parser, ",", TRUE))
698             {
699               gtk_gradient_unref (gradient);
700               _gtk_css_parser_error (parser,
701                                      "Expected a comma");
702               return FALSE;
703             }
704         }
705       else
706         {
707           gtk_gradient_unref (gradient);
708           _gtk_css_parser_error (parser,
709                                  "Not a valid color-stop definition");
710           return FALSE;
711         }
712
713       color = _gtk_css_parser_read_symbolic_color (parser);
714       if (color == NULL)
715         {
716           gtk_gradient_unref (gradient);
717           return FALSE;
718         }
719
720       gtk_gradient_add_color_stop (gradient, position, color);
721       gtk_symbolic_color_unref (color);
722
723       if (!_gtk_css_parser_try (parser, ")", TRUE))
724         {
725           gtk_gradient_unref (gradient);
726           _gtk_css_parser_error (parser,
727                                  "Expected ')'");
728           return FALSE;
729         }
730     }
731
732   if (!_gtk_css_parser_try (parser, ")", TRUE))
733     {
734       gtk_gradient_unref (gradient);
735       _gtk_css_parser_error (parser,
736                              "Expected ')'");
737       return FALSE;
738     }
739
740   g_value_take_boxed (value, gradient);
741   return TRUE;
742 }
743
744 static void
745 gradient_value_print (const GValue *value,
746                       GString      *string)
747 {
748   GtkGradient *gradient = g_value_get_boxed (value);
749
750   if (gradient == NULL)
751     g_string_append (string, "none");
752   else
753     {
754       char *s = gtk_gradient_to_string (gradient);
755       g_string_append (string, s);
756       g_free (s);
757     }
758 }
759
760 static GFile *
761 gtk_css_parse_url (GtkCssParser *parser,
762                    GFile        *base)
763 {
764   gchar *path;
765   GFile *file;
766
767   if (_gtk_css_parser_try (parser, "url", FALSE))
768     {
769       if (!_gtk_css_parser_try (parser, "(", TRUE))
770         {
771           _gtk_css_parser_skip_whitespace (parser);
772           if (_gtk_css_parser_try (parser, "(", TRUE))
773             {
774               GError *error;
775               
776               error = g_error_new_literal (GTK_CSS_PROVIDER_ERROR,
777                                            GTK_CSS_PROVIDER_ERROR_DEPRECATED,
778                                            "Whitespace between 'url' and '(' is not allowed");
779                              
780               _gtk_css_parser_take_error (parser, error);
781             }
782           else
783             {
784               _gtk_css_parser_error (parser, "Expected '(' after 'url'");
785               return NULL;
786             }
787         }
788
789       path = _gtk_css_parser_read_string (parser);
790       if (path == NULL)
791         return NULL;
792
793       if (!_gtk_css_parser_try (parser, ")", TRUE))
794         {
795           _gtk_css_parser_error (parser, "No closing ')' found for 'url'");
796           g_free (path);
797           return NULL;
798         }
799     }
800   else
801     {
802       path = _gtk_css_parser_try_name (parser, TRUE);
803       if (path == NULL)
804         {
805           _gtk_css_parser_error (parser, "Not a valid url");
806           return NULL;
807         }
808     }
809
810   file = g_file_resolve_relative_path (base, path);
811   g_free (path);
812
813   return file;
814 }
815
816 static gboolean 
817 pattern_value_parse (GtkCssParser *parser,
818                      GFile        *base,
819                      GValue       *value)
820 {
821   if (_gtk_css_parser_begins_with (parser, '-'))
822     {
823       g_value_unset (value);
824       g_value_init (value, GTK_TYPE_GRADIENT);
825       return gradient_value_parse (parser, base, value);
826     }
827   else
828     {
829       GError *error = NULL;
830       gchar *path;
831       GdkPixbuf *pixbuf;
832       GFile *file;
833       cairo_surface_t *surface;
834       cairo_pattern_t *pattern;
835       cairo_t *cr;
836       cairo_matrix_t matrix;
837
838       file = gtk_css_parse_url (parser, base);
839       if (file == NULL)
840         return FALSE;
841
842       path = g_file_get_path (file);
843       g_object_unref (file);
844
845       pixbuf = gdk_pixbuf_new_from_file (path, &error);
846       g_free (path);
847       if (pixbuf == NULL)
848         {
849           _gtk_css_parser_take_error (parser, error);
850           return FALSE;
851         }
852
853       surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
854                                             gdk_pixbuf_get_width (pixbuf),
855                                             gdk_pixbuf_get_height (pixbuf));
856       cr = cairo_create (surface);
857       gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
858       cairo_paint (cr);
859       pattern = cairo_pattern_create_for_surface (surface);
860
861       cairo_matrix_init_scale (&matrix,
862                                gdk_pixbuf_get_width (pixbuf),
863                                gdk_pixbuf_get_height (pixbuf));
864       cairo_pattern_set_matrix (pattern, &matrix);
865
866       cairo_surface_destroy (surface);
867       cairo_destroy (cr);
868       g_object_unref (pixbuf);
869
870       g_value_take_boxed (value, pattern);
871     }
872   
873   return TRUE;
874 }
875
876 static gboolean
877 shadow_value_parse (GtkCssParser *parser,
878                     GFile *base,
879                     GValue *value)
880 {
881   gboolean inset;
882   gdouble hoffset, voffset, blur, spread;
883   GtkSymbolicColor *color;
884   GtkShadow *shadow;
885
886   shadow = _gtk_shadow_new ();
887
888   do
889     {
890       inset = _gtk_css_parser_try (parser, "inset", TRUE);
891
892       if (!_gtk_css_parser_try_double (parser, &hoffset) ||
893           !_gtk_css_parser_try_double (parser, &voffset))
894         {
895           _gtk_css_parser_error (parser, "Horizontal and vertical offsets are required");
896           _gtk_shadow_unref (shadow);
897           return FALSE;
898         }
899
900       if (!_gtk_css_parser_try_double (parser, &blur))
901         blur = 0;
902
903       if (!_gtk_css_parser_try_double (parser, &spread))
904         spread = 0;
905
906       /* XXX: the color is optional and UA-defined if it's missing,
907        * but it doesn't really make sense for us...
908        */
909       color = _gtk_css_parser_read_symbolic_color (parser);
910
911       if (color == NULL)
912         {
913           _gtk_shadow_unref (shadow);
914           return FALSE;
915         }
916
917       _gtk_shadow_append (shadow,
918                           hoffset, voffset,
919                           blur, spread,
920                           inset, color);
921
922       gtk_symbolic_color_unref (color);
923
924     }
925   while (_gtk_css_parser_try (parser, ",", TRUE));
926
927   g_value_take_boxed (value, shadow);
928   return TRUE;
929 }
930
931 static void
932 shadow_value_print (const GValue *value,
933                     GString      *string)
934 {
935   GtkShadow *shadow;
936
937   shadow = g_value_get_boxed (value);
938
939   if (shadow == NULL)
940     g_string_append (string, "none");
941   else
942     _gtk_shadow_print (shadow, string);
943 }
944
945 static gboolean 
946 slice_value_parse (GtkCssParser *parser,
947                    GFile        *base,
948                    GValue       *value)
949 {
950   gdouble distance_top, distance_bottom;
951   gdouble distance_left, distance_right;
952   GtkSliceSideModifier mods[2];
953   GdkPixbuf *pixbuf;
954   Gtk9Slice *slice;
955   GFile *file;
956   GError *error = NULL;
957   gint i;
958   char *path;
959
960   /* Parse image url */
961   file = gtk_css_parse_url (parser, base);
962   if (!file)
963       return FALSE;
964
965   if (!_gtk_css_parser_try_double (parser, &distance_top) ||
966       !_gtk_css_parser_try_double (parser, &distance_right) ||
967       !_gtk_css_parser_try_double (parser, &distance_bottom) ||
968       !_gtk_css_parser_try_double (parser, &distance_left))
969     {
970       _gtk_css_parser_error (parser, "Expected a number");
971       g_object_unref (file);
972       return FALSE;
973     }
974
975   for (i = 0; i < 2; i++)
976     {
977       if (_gtk_css_parser_try (parser, "stretch", TRUE))
978         mods[i] = GTK_SLICE_STRETCH;
979       else if (_gtk_css_parser_try (parser, "repeat", TRUE))
980         mods[i] = GTK_SLICE_REPEAT;
981       else if (i == 0)
982         {
983           mods[1] = mods[0] = GTK_SLICE_STRETCH;
984           break;
985         }
986       else
987         mods[i] = mods[0];
988     }
989
990   path = g_file_get_path (file);
991   g_object_unref (file);
992   pixbuf = gdk_pixbuf_new_from_file (path, &error);
993   g_free (path);
994   if (!pixbuf)
995     {
996       _gtk_css_parser_take_error (parser, error);
997       return FALSE;
998     }
999
1000   slice = _gtk_9slice_new (pixbuf,
1001                            distance_top, distance_bottom,
1002                            distance_left, distance_right,
1003                            mods[0], mods[1]);
1004   g_object_unref (pixbuf);
1005
1006   g_value_take_boxed (value, slice);
1007   return TRUE;
1008 }
1009
1010 static gboolean 
1011 enum_value_parse (GtkCssParser *parser,
1012                   GFile        *base,
1013                   GValue       *value)
1014 {
1015   GEnumClass *enum_class;
1016   GEnumValue *enum_value;
1017   char *str;
1018
1019   str = _gtk_css_parser_try_ident (parser, TRUE);
1020   if (str == NULL)
1021     {
1022       _gtk_css_parser_error (parser, "Expected an identifier");
1023       return FALSE;
1024     }
1025
1026   enum_class = g_type_class_ref (G_VALUE_TYPE (value));
1027   enum_value = g_enum_get_value_by_nick (enum_class, str);
1028
1029   if (enum_value)
1030     g_value_set_enum (value, enum_value->value);
1031   else
1032     _gtk_css_parser_error (parser,
1033                            "Unknown value '%s' for enum type '%s'",
1034                            str, g_type_name (G_VALUE_TYPE (value)));
1035   
1036   g_type_class_unref (enum_class);
1037   g_free (str);
1038
1039   return enum_value != NULL;
1040 }
1041
1042 static void
1043 enum_value_print (const GValue *value,
1044                   GString      *string)
1045 {
1046   GEnumClass *enum_class;
1047   GEnumValue *enum_value;
1048
1049   enum_class = g_type_class_ref (G_VALUE_TYPE (value));
1050   enum_value = g_enum_get_value (enum_class, g_value_get_enum (value));
1051
1052   g_string_append (string, enum_value->value_nick);
1053
1054   g_type_class_unref (enum_class);
1055 }
1056
1057 static gboolean 
1058 flags_value_parse (GtkCssParser *parser,
1059                    GFile        *base,
1060                    GValue       *value)
1061 {
1062   GFlagsClass *flags_class;
1063   GFlagsValue *flag_value;
1064   guint flags = 0;
1065   char *str;
1066
1067   flags_class = g_type_class_ref (G_VALUE_TYPE (value));
1068
1069   do {
1070     str = _gtk_css_parser_try_ident (parser, TRUE);
1071     if (str == NULL)
1072       {
1073         _gtk_css_parser_error (parser, "Expected an identifier");
1074         g_type_class_unref (flags_class);
1075         return FALSE;
1076       }
1077
1078       flag_value = g_flags_get_value_by_nick (flags_class, str);
1079       if (!flag_value)
1080         {
1081           _gtk_css_parser_error (parser,
1082                                  "Unknown flag value '%s' for type '%s'",
1083                                  str, g_type_name (G_VALUE_TYPE (value)));
1084           /* XXX Do we want to return FALSE here? We can get
1085            * forward-compatibility for new values this way
1086            */
1087           g_free (str);
1088           g_type_class_unref (flags_class);
1089           return FALSE;
1090         }
1091
1092       g_free (str);
1093     }
1094   while (_gtk_css_parser_try (parser, ",", FALSE));
1095
1096   g_type_class_unref (flags_class);
1097
1098   g_value_set_enum (value, flags);
1099
1100   return TRUE;
1101 }
1102
1103 static void
1104 flags_value_print (const GValue *value,
1105                    GString      *string)
1106 {
1107   GFlagsClass *flags_class;
1108   guint i, flags;
1109
1110   flags_class = g_type_class_ref (G_VALUE_TYPE (value));
1111   flags = g_value_get_flags (value);
1112
1113   for (i = 0; i < flags_class->n_values; i++)
1114     {
1115       GFlagsValue *flags_value = &flags_class->values[i];
1116
1117       if (flags & flags_value->value)
1118         {
1119           if (string->len != 0)
1120             g_string_append (string, ", ");
1121
1122           g_string_append (string, flags_value->value_nick);
1123         }
1124     }
1125
1126   g_type_class_unref (flags_class);
1127 }
1128
1129 static gboolean 
1130 bindings_value_parse (GtkCssParser *parser,
1131                       GFile        *base,
1132                       GValue       *value)
1133 {
1134   GPtrArray *array;
1135   GtkBindingSet *binding_set;
1136   char *name;
1137
1138   array = g_ptr_array_new ();
1139
1140   do {
1141       name = _gtk_css_parser_try_ident (parser, TRUE);
1142       if (name == NULL)
1143         {
1144           _gtk_css_parser_error (parser, "Not a valid binding name");
1145           g_ptr_array_free (array, TRUE);
1146           return FALSE;
1147         }
1148
1149       binding_set = gtk_binding_set_find (name);
1150
1151       if (!binding_set)
1152         {
1153           _gtk_css_parser_error (parser, "No binding set named '%s'", name);
1154           g_free (name);
1155           continue;
1156         }
1157
1158       g_ptr_array_add (array, binding_set);
1159       g_free (name);
1160     }
1161   while (_gtk_css_parser_try (parser, ",", TRUE));
1162
1163   g_value_take_boxed (value, array);
1164
1165   return TRUE;
1166 }
1167
1168 static void
1169 bindings_value_print (const GValue *value,
1170                       GString      *string)
1171 {
1172   GPtrArray *array;
1173   guint i;
1174
1175   array = g_value_get_boxed (value);
1176
1177   for (i = 0; i < array->len; i++)
1178     {
1179       GtkBindingSet *binding_set = g_ptr_array_index (array, i);
1180
1181       if (i > 0)
1182         g_string_append (string, ", ");
1183       g_string_append (string, binding_set->set_name);
1184     }
1185 }
1186
1187 /*** PACKING ***/
1188
1189 static GParameter *
1190 unpack_border (const GValue *value,
1191                guint        *n_params,
1192                const char   *top,
1193                const char   *left,
1194                const char   *bottom,
1195                const char   *right)
1196 {
1197   GParameter *parameter = g_new0 (GParameter, 4);
1198   GtkBorder *border = g_value_get_boxed (value);
1199
1200   parameter[0].name = top;
1201   g_value_init (&parameter[0].value, G_TYPE_INT);
1202   g_value_set_int (&parameter[0].value, border->top);
1203   parameter[1].name = left;
1204   g_value_init (&parameter[1].value, G_TYPE_INT);
1205   g_value_set_int (&parameter[1].value, border->left);
1206   parameter[2].name = bottom;
1207   g_value_init (&parameter[2].value, G_TYPE_INT);
1208   g_value_set_int (&parameter[2].value, border->bottom);
1209   parameter[3].name = right;
1210   g_value_init (&parameter[3].value, G_TYPE_INT);
1211   g_value_set_int (&parameter[3].value, border->right);
1212
1213   *n_params = 4;
1214   return parameter;
1215 }
1216
1217 static void
1218 pack_border (GValue             *value,
1219              GtkStyleProperties *props,
1220              GtkStateFlags       state,
1221              const char         *top,
1222              const char         *left,
1223              const char         *bottom,
1224              const char         *right)
1225 {
1226   GtkBorder border;
1227   int t, l, b, r;
1228
1229   gtk_style_properties_get (props,
1230                             state,
1231                             top, &t,
1232                             left, &l,
1233                             bottom, &b,
1234                             right, &r,
1235                             NULL);
1236
1237   border.top = t;
1238   border.left = l;
1239   border.bottom = b;
1240   border.right = r;
1241
1242   g_value_set_boxed (value, &border);
1243 }
1244
1245 static GParameter *
1246 unpack_border_width (const GValue *value,
1247                      guint        *n_params)
1248 {
1249   return unpack_border (value, n_params,
1250                         "border-top-width", "border-left-width",
1251                         "border-bottom-width", "border-right-width");
1252 }
1253
1254 static void
1255 pack_border_width (GValue             *value,
1256                    GtkStyleProperties *props,
1257                    GtkStateFlags       state)
1258 {
1259   pack_border (value, props, state,
1260                "border-top-width", "border-left-width",
1261                "border-bottom-width", "border-right-width");
1262 }
1263
1264 static GParameter *
1265 unpack_padding (const GValue *value,
1266                 guint        *n_params)
1267 {
1268   return unpack_border (value, n_params,
1269                         "padding-top", "padding-left",
1270                         "padding-bottom", "padding-right");
1271 }
1272
1273 static void
1274 pack_padding (GValue             *value,
1275               GtkStyleProperties *props,
1276               GtkStateFlags       state)
1277 {
1278   pack_border (value, props, state,
1279                "padding-top", "padding-left",
1280                "padding-bottom", "padding-right");
1281 }
1282
1283 static GParameter *
1284 unpack_margin (const GValue *value,
1285                guint        *n_params)
1286 {
1287   return unpack_border (value, n_params,
1288                         "margin-top", "margin-left",
1289                         "margin-bottom", "margin-right");
1290 }
1291
1292 static void
1293 pack_margin (GValue             *value,
1294              GtkStyleProperties *props,
1295              GtkStateFlags       state)
1296 {
1297   pack_border (value, props, state,
1298                "margin-top", "margin-left",
1299                "margin-bottom", "margin-right");
1300 }
1301
1302 /*** API ***/
1303
1304 static void
1305 css_string_funcs_init (void)
1306 {
1307   if (G_LIKELY (parse_funcs != NULL))
1308     return;
1309
1310   parse_funcs = g_hash_table_new (NULL, NULL);
1311   print_funcs = g_hash_table_new (NULL, NULL);
1312
1313   register_conversion_function (GDK_TYPE_RGBA,
1314                                 rgba_value_parse,
1315                                 rgba_value_print);
1316   register_conversion_function (GDK_TYPE_COLOR,
1317                                 color_value_parse,
1318                                 color_value_print);
1319   register_conversion_function (GTK_TYPE_SYMBOLIC_COLOR,
1320                                 symbolic_color_value_parse,
1321                                 symbolic_color_value_print);
1322   register_conversion_function (PANGO_TYPE_FONT_DESCRIPTION,
1323                                 font_description_value_parse,
1324                                 font_description_value_print);
1325   register_conversion_function (G_TYPE_BOOLEAN,
1326                                 boolean_value_parse,
1327                                 boolean_value_print);
1328   register_conversion_function (G_TYPE_INT,
1329                                 int_value_parse,
1330                                 int_value_print);
1331   register_conversion_function (G_TYPE_UINT,
1332                                 uint_value_parse,
1333                                 uint_value_print);
1334   register_conversion_function (G_TYPE_DOUBLE,
1335                                 double_value_parse,
1336                                 double_value_print);
1337   register_conversion_function (G_TYPE_FLOAT,
1338                                 float_value_parse,
1339                                 float_value_print);
1340   register_conversion_function (G_TYPE_STRING,
1341                                 string_value_parse,
1342                                 string_value_print);
1343   register_conversion_function (GTK_TYPE_THEMING_ENGINE,
1344                                 theming_engine_value_parse,
1345                                 theming_engine_value_print);
1346   register_conversion_function (GTK_TYPE_ANIMATION_DESCRIPTION,
1347                                 animation_description_value_parse,
1348                                 animation_description_value_print);
1349   register_conversion_function (GTK_TYPE_BORDER,
1350                                 border_value_parse,
1351                                 border_value_print);
1352   register_conversion_function (GTK_TYPE_GRADIENT,
1353                                 gradient_value_parse,
1354                                 gradient_value_print);
1355   register_conversion_function (CAIRO_GOBJECT_TYPE_PATTERN,
1356                                 pattern_value_parse,
1357                                 NULL);
1358   register_conversion_function (GTK_TYPE_9SLICE,
1359                                 slice_value_parse,
1360                                 NULL);
1361   register_conversion_function (GTK_TYPE_SHADOW,
1362                                 shadow_value_parse,
1363                                 shadow_value_print);
1364   register_conversion_function (G_TYPE_ENUM,
1365                                 enum_value_parse,
1366                                 enum_value_print);
1367   register_conversion_function (G_TYPE_FLAGS,
1368                                 flags_value_parse,
1369                                 flags_value_print);
1370 }
1371
1372 gboolean
1373 _gtk_style_property_parse_value (const GtkStyleProperty *property,
1374                                  GValue                 *value,
1375                                  GtkCssParser           *parser,
1376                                  GFile                  *base)
1377 {
1378   GtkStyleParseFunc func;
1379
1380   g_return_val_if_fail (value != NULL, FALSE);
1381   g_return_val_if_fail (parser != NULL, FALSE);
1382
1383   css_string_funcs_init ();
1384
1385   if (property)
1386     {
1387       if (_gtk_css_parser_try (parser, "none", TRUE))
1388         {
1389           /* Insert the default value, so it has an opportunity
1390            * to override other style providers when merged
1391            */
1392           g_param_value_set_default (property->pspec, value);
1393           return TRUE;
1394         }
1395       else if (property->property_parse_func)
1396         {
1397           GError *error = NULL;
1398           char *value_str;
1399           gboolean success;
1400           
1401           value_str = _gtk_css_parser_read_value (parser);
1402           if (value_str == NULL)
1403             return FALSE;
1404           
1405           success = (*property->property_parse_func) (value_str, value, &error);
1406
1407           g_free (value_str);
1408
1409           return success;
1410         }
1411
1412       func = property->parse_func;
1413     }
1414   else
1415     func = NULL;
1416
1417   if (func == NULL)
1418     func = g_hash_table_lookup (parse_funcs,
1419                                 GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
1420   if (func == NULL)
1421     func = g_hash_table_lookup (parse_funcs,
1422                                 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
1423
1424   if (func == NULL)
1425     {
1426       _gtk_css_parser_error (parser,
1427                              "Cannot convert to type '%s'",
1428                              g_type_name (G_VALUE_TYPE (value)));
1429       return FALSE;
1430     }
1431
1432   return (*func) (parser, base, value);
1433 }
1434
1435 void
1436 _gtk_style_property_print_value (const GtkStyleProperty *property,
1437                                  const GValue           *value,
1438                                  GString                *string)
1439 {
1440   GtkStylePrintFunc func;
1441
1442   css_string_funcs_init ();
1443
1444   if (property)
1445     func = property->print_func;
1446   else
1447     func = NULL;
1448
1449   if (func == NULL)
1450     func = g_hash_table_lookup (print_funcs,
1451                                 GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
1452   if (func == NULL)
1453     func = g_hash_table_lookup (print_funcs,
1454                                 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
1455
1456   if (func == NULL)
1457     {
1458       char *s = g_strdup_value_contents (value);
1459       g_string_append (string, s);
1460       g_free (s);
1461       return;
1462     }
1463   
1464   func (value, string);
1465 }
1466
1467 gboolean
1468 _gtk_style_property_is_shorthand  (const GtkStyleProperty *property)
1469 {
1470   g_return_val_if_fail (property != NULL, FALSE);
1471
1472   return property->pack_func != NULL;
1473 }
1474
1475 GParameter *
1476 _gtk_style_property_unpack (const GtkStyleProperty *property,
1477                             const GValue           *value,
1478                             guint                  *n_params)
1479 {
1480   g_return_val_if_fail (property != NULL, NULL);
1481   g_return_val_if_fail (property->unpack_func != NULL, NULL);
1482   g_return_val_if_fail (value != NULL, NULL);
1483   g_return_val_if_fail (n_params != NULL, NULL);
1484
1485   return property->unpack_func (value, n_params);
1486 }
1487
1488 void
1489 _gtk_style_property_pack (const GtkStyleProperty *property,
1490                           GtkStyleProperties     *props,
1491                           GtkStateFlags           state,
1492                           GValue                 *value)
1493 {
1494   g_return_if_fail (property != NULL);
1495   g_return_if_fail (property->pack_func != NULL);
1496   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
1497   g_return_if_fail (G_IS_VALUE (value));
1498
1499   property->pack_func (value, props, state);
1500 }
1501
1502 static void
1503 gtk_style_property_init (void)
1504 {
1505   GParamSpec *pspec;
1506
1507   if (G_LIKELY (properties))
1508     return;
1509
1510   /* stuff is never freed, so no need for free functions */
1511   properties = g_hash_table_new (g_str_hash, g_str_equal);
1512
1513   /* note that gtk_style_properties_register_property() calls this function,
1514    * so make sure we're sanely inited to avoid infloops */
1515
1516   pspec = g_param_spec_boxed ("color",
1517                               "Foreground color",
1518                               "Foreground color",
1519                               GDK_TYPE_RGBA, 0);
1520   gtk_style_param_set_inherit (pspec, TRUE);
1521   gtk_style_properties_register_property (NULL, pspec);
1522
1523   gtk_style_properties_register_property (NULL,
1524                                           g_param_spec_boxed ("background-color",
1525                                                               "Background color",
1526                                                               "Background color",
1527                                                               GDK_TYPE_RGBA, 0));
1528
1529   pspec = g_param_spec_boxed ("font",
1530                               "Font Description",
1531                               "Font Description",
1532                               PANGO_TYPE_FONT_DESCRIPTION, 0);
1533   gtk_style_param_set_inherit (pspec, TRUE);
1534   gtk_style_properties_register_property (NULL, pspec);
1535
1536   pspec = g_param_spec_boxed ("text-shadow",
1537                               "Text shadow",
1538                               "Text shadow",
1539                               GTK_TYPE_SHADOW, 0);
1540   gtk_style_param_set_inherit (pspec, TRUE);
1541   gtk_style_properties_register_property (NULL, pspec);
1542
1543   gtk_style_properties_register_property (NULL,
1544                                           g_param_spec_int ("margin-top",
1545                                                             "margin top",
1546                                                             "Margin at top",
1547                                                             0, G_MAXINT, 0, 0));
1548   gtk_style_properties_register_property (NULL,
1549                                           g_param_spec_int ("margin-left",
1550                                                             "margin left",
1551                                                             "Margin at left",
1552                                                             0, G_MAXINT, 0, 0));
1553   gtk_style_properties_register_property (NULL,
1554                                           g_param_spec_int ("margin-bottom",
1555                                                             "margin bottom",
1556                                                             "Margin at bottom",
1557                                                             0, G_MAXINT, 0, 0));
1558   gtk_style_properties_register_property (NULL,
1559                                           g_param_spec_int ("margin-right",
1560                                                             "margin right",
1561                                                             "Margin at right",
1562                                                             0, G_MAXINT, 0, 0));
1563   _gtk_style_property_register           (g_param_spec_boxed ("margin",
1564                                                               "Margin",
1565                                                               "Margin",
1566                                                               GTK_TYPE_BORDER, 0),
1567                                           NULL,
1568                                           unpack_margin,
1569                                           pack_margin,
1570                                           NULL,
1571                                           NULL);
1572   gtk_style_properties_register_property (NULL,
1573                                           g_param_spec_int ("padding-top",
1574                                                             "padding top",
1575                                                             "Padding at top",
1576                                                             0, G_MAXINT, 0, 0));
1577   gtk_style_properties_register_property (NULL,
1578                                           g_param_spec_int ("padding-left",
1579                                                             "padding left",
1580                                                             "Padding at left",
1581                                                             0, G_MAXINT, 0, 0));
1582   gtk_style_properties_register_property (NULL,
1583                                           g_param_spec_int ("padding-bottom",
1584                                                             "padding bottom",
1585                                                             "Padding at bottom",
1586                                                             0, G_MAXINT, 0, 0));
1587   gtk_style_properties_register_property (NULL,
1588                                           g_param_spec_int ("padding-right",
1589                                                             "padding right",
1590                                                             "Padding at right",
1591                                                             0, G_MAXINT, 0, 0));
1592   _gtk_style_property_register           (g_param_spec_boxed ("padding",
1593                                                               "Padding",
1594                                                               "Padding",
1595                                                               GTK_TYPE_BORDER, 0),
1596                                           NULL,
1597                                           unpack_padding,
1598                                           pack_padding,
1599                                           NULL,
1600                                           NULL);
1601   gtk_style_properties_register_property (NULL,
1602                                           g_param_spec_int ("border-top-width",
1603                                                             "border top width",
1604                                                             "Border width at top",
1605                                                             0, G_MAXINT, 0, 0));
1606   gtk_style_properties_register_property (NULL,
1607                                           g_param_spec_int ("border-left-width",
1608                                                             "border left width",
1609                                                             "Border width at left",
1610                                                             0, G_MAXINT, 0, 0));
1611   gtk_style_properties_register_property (NULL,
1612                                           g_param_spec_int ("border-bottom-width",
1613                                                             "border bottom width",
1614                                                             "Border width at bottom",
1615                                                             0, G_MAXINT, 0, 0));
1616   gtk_style_properties_register_property (NULL,
1617                                           g_param_spec_int ("border-right-width",
1618                                                             "border right width",
1619                                                             "Border width at right",
1620                                                             0, G_MAXINT, 0, 0));
1621   _gtk_style_property_register           (g_param_spec_boxed ("border-width",
1622                                                               "Border width",
1623                                                               "Border width, in pixels",
1624                                                               GTK_TYPE_BORDER, 0),
1625                                           NULL,
1626                                           unpack_border_width,
1627                                           pack_border_width,
1628                                           NULL,
1629                                           NULL);
1630   gtk_style_properties_register_property (NULL,
1631                                           g_param_spec_int ("border-radius",
1632                                                             "Border radius",
1633                                                             "Border radius, in pixels",
1634                                                             0, G_MAXINT, 0, 0));
1635   gtk_style_properties_register_property (NULL,
1636                                           g_param_spec_enum ("border-style",
1637                                                              "Border style",
1638                                                              "Border style",
1639                                                              GTK_TYPE_BORDER_STYLE,
1640                                                              GTK_BORDER_STYLE_NONE, 0));
1641   gtk_style_properties_register_property (NULL,
1642                                           g_param_spec_boxed ("border-color",
1643                                                               "Border color",
1644                                                               "Border color",
1645                                                               GDK_TYPE_RGBA, 0));
1646   gtk_style_properties_register_property (NULL,
1647                                           g_param_spec_boxed ("background-image",
1648                                                               "Background Image",
1649                                                               "Background Image",
1650                                                               CAIRO_GOBJECT_TYPE_PATTERN, 0));
1651   gtk_style_properties_register_property (NULL,
1652                                           g_param_spec_boxed ("border-image",
1653                                                               "Border Image",
1654                                                               "Border Image",
1655                                                               GTK_TYPE_9SLICE, 0));
1656   gtk_style_properties_register_property (NULL,
1657                                           g_param_spec_object ("engine",
1658                                                                "Theming Engine",
1659                                                                "Theming Engine",
1660                                                                GTK_TYPE_THEMING_ENGINE, 0));
1661   gtk_style_properties_register_property (NULL,
1662                                           g_param_spec_boxed ("transition",
1663                                                               "Transition animation description",
1664                                                               "Transition animation description",
1665                                                               GTK_TYPE_ANIMATION_DESCRIPTION, 0));
1666
1667   /* Private property holding the binding sets */
1668   _gtk_style_property_register           (g_param_spec_boxed ("gtk-key-bindings",
1669                                                               "Key bindings",
1670                                                               "Key bindings",
1671                                                               G_TYPE_PTR_ARRAY, 0),
1672                                           NULL,
1673                                           NULL,
1674                                           NULL,
1675                                           bindings_value_parse,
1676                                           bindings_value_print);
1677 }
1678
1679 const GtkStyleProperty *
1680 _gtk_style_property_lookup (const char *name)
1681 {
1682   gtk_style_property_init ();
1683
1684   return g_hash_table_lookup (properties, name);
1685 }
1686
1687 void
1688 _gtk_style_property_register (GParamSpec             *pspec,
1689                               GtkStylePropertyParser  property_parse_func,
1690                               GtkStyleUnpackFunc      unpack_func,
1691                               GtkStylePackFunc        pack_func,
1692                               GtkStyleParseFunc       parse_func,
1693                               GtkStylePrintFunc       print_func)
1694 {
1695   const GtkStyleProperty *existing;
1696   GtkStyleProperty *node;
1697
1698   g_return_if_fail ((pack_func == NULL) == (unpack_func == NULL));
1699
1700   gtk_style_property_init ();
1701
1702   existing = _gtk_style_property_lookup (pspec->name);
1703   if (existing != NULL)
1704     {
1705       g_warning ("Property \"%s\" was already registered with type %s",
1706                  pspec->name, g_type_name (existing->pspec->value_type));
1707       return;
1708     }
1709
1710   node = g_slice_new0 (GtkStyleProperty);
1711   node->pspec = pspec;
1712   node->property_parse_func = property_parse_func;
1713   node->pack_func = pack_func;
1714   node->unpack_func = unpack_func;
1715   node->parse_func = parse_func;
1716   node->print_func = print_func;
1717
1718   g_hash_table_insert (properties, pspec->name, node);
1719 }