]> Pileus Git - ~andy/gtk/blob - gtk/gtkcssstylepropertyimpl.c
styleproperty: Save some memory
[~andy/gtk] / gtk / gtkcssstylepropertyimpl.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 "gtkstylepropertyprivate.h"
21
22 #include <gobject/gvaluecollector.h>
23 #include <gdk-pixbuf/gdk-pixbuf.h>
24 #include <cairo-gobject.h>
25 #include <math.h>
26
27 #include "gtkcssparserprivate.h"
28 #include "gtkcssstylefuncsprivate.h"
29 #include "gtkcssstylepropertyprivate.h"
30 #include "gtkcsstypesprivate.h"
31 #include "gtkintl.h"
32 #include "gtkprivatetypebuiltins.h"
33 #include "gtkstylepropertiesprivate.h"
34
35 /* this is in case round() is not provided by the compiler, 
36  * such as in the case of C89 compilers, like MSVC
37  */
38 #include "fallback-c89.c"
39
40 /* the actual parsers we have */
41 #include "gtkanimationdescription.h"
42 #include "gtkbindings.h"
43 #include "gtkcssimageprivate.h"
44 #include "gtkgradient.h"
45 #include "gtkshadowprivate.h"
46 #include "gtksymboliccolorprivate.h"
47 #include "gtkthemingengine.h"
48 #include "gtktypebuiltins.h"
49 #include "gtkwin32themeprivate.h"
50
51 /*** REGISTRATION ***/
52
53 static void
54 gtk_css_style_property_register (const char *                   name,
55                                  GType                          specified_type,
56                                  GType                          computed_type,
57                                  GType                          value_type,
58                                  GtkStylePropertyFlags          flags,
59                                  GtkCssStylePropertyParseFunc   parse_value,
60                                  GtkCssStylePropertyPrintFunc   print_value,
61                                  GtkCssStylePropertyComputeFunc compute_value,
62                                  ...)
63 {
64   GtkCssStyleProperty *node;
65   GValue initial_gvalue = G_VALUE_INIT;
66   GtkCssValue *initial_value;
67   char *error = NULL;
68   va_list args;
69
70   va_start (args, compute_value);
71   G_VALUE_COLLECT_INIT (&initial_gvalue, specified_type,
72                         args, 0, &error);
73   if (error)
74     {
75       g_error ("property `%s' initial value is broken: %s", name, error);
76       g_value_unset (&initial_gvalue);
77       return;
78     }
79
80   va_end (args);
81
82   initial_value = _gtk_css_value_new_take_gvalue (&initial_gvalue);
83
84   node = g_object_new (GTK_TYPE_CSS_STYLE_PROPERTY,
85                        "value-type", value_type,
86                        "computed-type", computed_type,
87                        "inherit", (flags & GTK_STYLE_PROPERTY_INHERIT) ? TRUE : FALSE,
88                        "initial-value", initial_value,
89                        "name", name,
90                        NULL);
91   
92   if (parse_value)
93     node->parse_value = parse_value;
94   if (print_value)
95     node->print_value = print_value;
96   if (compute_value)
97     node->compute_value = compute_value;
98
99   _gtk_css_value_unref (initial_value);
100 }
101
102 /*** HELPERS ***/
103
104 static void
105 string_append_string (GString    *str,
106                       const char *string)
107 {
108   gsize len;
109
110   g_string_append_c (str, '"');
111
112   do {
113     len = strcspn (string, "\"\n\r\f");
114     g_string_append (str, string);
115     string += len;
116     switch (*string)
117       {
118       case '\0':
119         break;
120       case '\n':
121         g_string_append (str, "\\A ");
122         break;
123       case '\r':
124         g_string_append (str, "\\D ");
125         break;
126       case '\f':
127         g_string_append (str, "\\C ");
128         break;
129       case '\"':
130         g_string_append (str, "\\\"");
131         break;
132       default:
133         g_assert_not_reached ();
134         break;
135       }
136   } while (*string);
137
138   g_string_append_c (str, '"');
139 }
140
141 /*** IMPLEMENTATIONS ***/
142
143 static GtkCssValue *
144 color_compute (GtkCssStyleProperty    *property,
145                GtkStyleContext        *context,
146                GtkCssValue            *specified)
147 {
148   GtkSymbolicColor *symbolic = _gtk_css_value_get_symbolic_color (specified);
149   GtkCssValue *resolved;
150
151   if (symbolic == _gtk_symbolic_color_get_current_color ())
152     {
153       /* The computed value of the ‘currentColor’ keyword is the computed
154        * value of the ‘color’ property. If the ‘currentColor’ keyword is
155        * set on the ‘color’ property itself, it is treated as ‘color: inherit’. 
156        */
157       if (g_str_equal (_gtk_style_property_get_name (GTK_STYLE_PROPERTY (property)), "color"))
158         {
159           GtkStyleContext *parent = gtk_style_context_get_parent (context);
160
161           if (parent)
162             return _gtk_css_value_ref (_gtk_style_context_peek_property (parent, "color"));
163           else
164             return _gtk_css_style_compute_value (context,
165                                                  GDK_TYPE_RGBA,
166                                                  _gtk_css_style_property_get_initial_value (property));
167         }
168       else
169         {
170           return _gtk_css_value_ref (_gtk_style_context_peek_property (context, "color"));
171         }
172     }
173   else if ((resolved = _gtk_style_context_resolve_color_value (context,
174                                                                symbolic)) != NULL)
175     {
176       return resolved;
177     }
178   else
179     {
180       return color_compute (property,
181                             context,
182                             _gtk_css_style_property_get_initial_value (property));
183     }
184 }
185
186 static gboolean
187 font_family_parse (GtkCssStyleProperty *property,
188                    GValue              *value,
189                    GtkCssParser        *parser,
190                    GFile               *base)
191 {
192   GPtrArray *names;
193   char *name;
194
195   /* We don't special case generic families. Pango should do
196    * that for us */
197
198   names = g_ptr_array_new ();
199
200   do {
201     name = _gtk_css_parser_try_ident (parser, TRUE);
202     if (name)
203       {
204         GString *string = g_string_new (name);
205         g_free (name);
206         while ((name = _gtk_css_parser_try_ident (parser, TRUE)))
207           {
208             g_string_append_c (string, ' ');
209             g_string_append (string, name);
210             g_free (name);
211           }
212         name = g_string_free (string, FALSE);
213       }
214     else 
215       {
216         name = _gtk_css_parser_read_string (parser);
217         if (name == NULL)
218           {
219             g_ptr_array_free (names, TRUE);
220             return FALSE;
221           }
222       }
223
224     g_ptr_array_add (names, name);
225   } while (_gtk_css_parser_try (parser, ",", TRUE));
226
227   /* NULL-terminate array */
228   g_ptr_array_add (names, NULL);
229   g_value_set_boxed (value, g_ptr_array_free (names, FALSE));
230   return TRUE;
231 }
232
233 static void
234 font_family_value_print (GtkCssStyleProperty *property,
235                          const GValue        *value,
236                          GString             *string)
237 {
238   const char **names = g_value_get_boxed (value);
239
240   if (names == NULL || *names == NULL)
241     {
242       g_string_append (string, "none");
243       return;
244     }
245
246   string_append_string (string, *names);
247   names++;
248   while (*names)
249     {
250       g_string_append (string, ", ");
251       string_append_string (string, *names);
252       names++;
253     }
254 }
255
256 static gboolean 
257 bindings_value_parse (GtkCssStyleProperty *property,
258                       GValue              *value,
259                       GtkCssParser        *parser,
260                       GFile               *base)
261 {
262   GPtrArray *array;
263   GtkBindingSet *binding_set;
264   char *name;
265
266   array = g_ptr_array_new ();
267
268   do {
269       name = _gtk_css_parser_try_ident (parser, TRUE);
270       if (name == NULL)
271         {
272           _gtk_css_parser_error (parser, "Not a valid binding name");
273           g_ptr_array_free (array, TRUE);
274           return FALSE;
275         }
276
277       binding_set = gtk_binding_set_find (name);
278
279       if (!binding_set)
280         {
281           _gtk_css_parser_error (parser, "No binding set named '%s'", name);
282           g_free (name);
283           continue;
284         }
285
286       g_ptr_array_add (array, binding_set);
287       g_free (name);
288     }
289   while (_gtk_css_parser_try (parser, ",", TRUE));
290
291   g_value_take_boxed (value, array);
292
293   return TRUE;
294 }
295
296 static void
297 bindings_value_print (GtkCssStyleProperty *property,
298                       const GValue        *value,
299                       GString             *string)
300 {
301   GPtrArray *array;
302   guint i;
303
304   array = g_value_get_boxed (value);
305
306   for (i = 0; i < array->len; i++)
307     {
308       GtkBindingSet *binding_set = g_ptr_array_index (array, i);
309
310       if (i > 0)
311         g_string_append (string, ", ");
312       g_string_append (string, binding_set->set_name);
313     }
314 }
315
316 static gboolean 
317 border_corner_radius_value_parse (GtkCssStyleProperty *property,
318                                   GValue              *value,
319                                   GtkCssParser        *parser,
320                                   GFile               *base)
321 {
322   GtkCssBorderCornerRadius corner;
323
324   if (!_gtk_css_parser_read_number (parser,
325                                     &corner.horizontal,
326                                     GTK_CSS_POSITIVE_ONLY
327                                     | GTK_CSS_PARSE_PERCENT
328                                     | GTK_CSS_NUMBER_AS_PIXELS
329                                     | GTK_CSS_PARSE_LENGTH))
330     return FALSE;
331
332   if (!_gtk_css_parser_has_number (parser))
333     corner.vertical = corner.horizontal;
334   else if (!_gtk_css_parser_read_number (parser,
335                                          &corner.vertical,
336                                          GTK_CSS_POSITIVE_ONLY
337                                          | GTK_CSS_PARSE_PERCENT
338                                          | GTK_CSS_NUMBER_AS_PIXELS
339                                          | GTK_CSS_PARSE_LENGTH))
340     return FALSE;
341
342   g_value_set_boxed (value, &corner);
343   return TRUE;
344 }
345
346 static void
347 border_corner_radius_value_print (GtkCssStyleProperty *property,
348                                   const GValue        *value,
349                                   GString             *string)
350 {
351   GtkCssBorderCornerRadius *corner;
352
353   corner = g_value_get_boxed (value);
354
355   _gtk_css_number_print (&corner->horizontal, string);
356
357   if (!_gtk_css_number_equal (&corner->horizontal, &corner->vertical))
358     {
359       g_string_append_c (string, ' ');
360       _gtk_css_number_print (&corner->vertical, string);
361     }
362 }
363
364 static gboolean 
365 css_image_value_parse (GtkCssStyleProperty *property,
366                        GValue              *value,
367                        GtkCssParser        *parser,
368                        GFile               *base)
369 {
370   GtkCssImage *image;
371
372   if (_gtk_css_parser_try (parser, "none", TRUE))
373     image = NULL;
374   else
375     {
376       image = _gtk_css_image_new_parse (parser, base);
377       if (image == NULL)
378         return FALSE;
379     }
380
381   g_value_take_object (value, image);
382   return TRUE;
383 }
384
385 static void
386 css_image_value_print (GtkCssStyleProperty *property,
387                        const GValue        *value,
388                        GString             *string)
389 {
390   GtkCssImage *image = g_value_get_object (value);
391
392   if (image)
393     _gtk_css_image_print (image, string);
394   else
395     g_string_append (string, "none");
396 }
397
398 static GtkCssValue *
399 css_image_value_compute (GtkCssStyleProperty    *property,
400                          GtkStyleContext        *context,
401                          GtkCssValue            *specified)
402 {
403   GtkCssImage *image, *computed;
404   
405   image = _gtk_css_value_get_image (specified);
406
407   if (image == NULL)
408     return _gtk_css_value_ref (specified);
409
410   computed = _gtk_css_image_compute (image, context);
411
412   if (computed == image)
413     {
414       g_object_unref (computed);
415       return _gtk_css_value_ref (specified);
416     }
417
418   return _gtk_css_value_new_take_image (image);
419 }
420
421 static gboolean 
422 parse_margin (GtkCssStyleProperty *property,
423               GValue              *value,
424               GtkCssParser        *parser,
425               GFile               *base)
426 {
427   GtkCssNumber number;
428
429   if (!_gtk_css_parser_read_number (parser,
430                                     &number, 
431                                     GTK_CSS_NUMBER_AS_PIXELS
432                                     | GTK_CSS_PARSE_LENGTH))
433     return FALSE;
434
435   g_value_set_boxed (value, &number);
436   return TRUE;
437 }
438
439 static GtkCssValue *
440 compute_margin (GtkCssStyleProperty *property,
441                 GtkStyleContext     *context,
442                 GtkCssValue         *specified)
443 {
444   GtkCssNumber number;
445   
446   if (_gtk_css_number_compute (&number,
447                                _gtk_css_value_get_number (specified),
448                                context))
449     {
450       return _gtk_css_value_new_from_number (&number);
451     }
452   return  _gtk_css_value_ref (specified);
453 }
454
455 static gboolean 
456 parse_padding (GtkCssStyleProperty *property,
457                GValue              *value,
458                GtkCssParser        *parser,
459                GFile               *base)
460 {
461   GtkCssNumber number;
462
463   if (!_gtk_css_parser_read_number (parser,
464                                     &number, 
465                                     GTK_CSS_POSITIVE_ONLY
466                                     | GTK_CSS_NUMBER_AS_PIXELS
467                                     | GTK_CSS_PARSE_LENGTH))
468     return FALSE;
469
470   g_value_set_boxed (value, &number);
471   return TRUE;
472 }
473
474 static GtkCssValue *
475 compute_padding (GtkCssStyleProperty *property,
476                  GtkStyleContext     *context,
477                  GtkCssValue         *specified)
478 {
479   GtkCssNumber number;
480
481   if (_gtk_css_number_compute (&number,
482                                _gtk_css_value_get_number (specified),
483                                context))
484     return _gtk_css_value_new_from_number (&number);
485   return _gtk_css_value_ref (specified);
486 }
487
488 static gboolean 
489 parse_border_width (GtkCssStyleProperty *property,
490                     GValue              *value,
491                     GtkCssParser        *parser,
492                     GFile               *base)
493 {
494   GtkCssNumber number;
495
496   if (!_gtk_css_parser_read_number (parser,
497                                     &number, 
498                                     GTK_CSS_POSITIVE_ONLY
499                                     | GTK_CSS_NUMBER_AS_PIXELS
500                                     | GTK_CSS_PARSE_LENGTH))
501     return FALSE;
502
503   g_value_set_boxed (value, &number);
504   return TRUE;
505 }
506
507 static GtkCssValue *
508 compute_border_width (GtkCssStyleProperty    *property,
509                       GtkStyleContext        *context,
510                       GtkCssValue            *specified)
511 {
512   GtkCssStyleProperty *style;
513   GtkBorderStyle border_style;
514   GtkCssNumber number;
515   int value = 0;
516   
517   /* The -1 is magic that is only true because we register the style
518    * properties directly after the width properties.
519    */
520   style = _gtk_css_style_property_lookup_by_id (_gtk_css_style_property_get_id (property) - 1);
521   
522   border_style = _gtk_css_value_get_border_style (_gtk_style_context_peek_property (context, _gtk_style_property_get_name (GTK_STYLE_PROPERTY (style))));
523
524   if (border_style == GTK_BORDER_STYLE_NONE ||
525       border_style == GTK_BORDER_STYLE_HIDDEN)
526     {
527       value = 0;
528     }
529   else
530     {
531       _gtk_css_number_compute (&number,
532                                _gtk_css_value_get_number (specified),
533                                context);
534       value = round (number.value);
535     }
536   return _gtk_css_value_new_from_int (value);
537 }
538
539 static gboolean
540 background_repeat_value_parse (GtkCssStyleProperty *property,
541                                GValue              *value,
542                                GtkCssParser        *parser,
543                                GFile               *base)
544 {
545   int repeat, vertical;
546
547   if (!_gtk_css_parser_try_enum (parser, GTK_TYPE_CSS_BACKGROUND_REPEAT, &repeat))
548     {
549       _gtk_css_parser_error (parser, "Not a valid value");
550       return FALSE;
551     }
552
553   if (repeat <= GTK_CSS_BACKGROUND_REPEAT_MASK)
554     {
555       if (_gtk_css_parser_try_enum (parser, GTK_TYPE_CSS_BACKGROUND_REPEAT, &vertical))
556         {
557           if (vertical >= GTK_CSS_BACKGROUND_REPEAT_MASK)
558             {
559               _gtk_css_parser_error (parser, "Not a valid 2nd value");
560               return FALSE;
561             }
562           else
563             repeat |= vertical << GTK_CSS_BACKGROUND_REPEAT_SHIFT;
564         }
565       else
566         repeat |= repeat << GTK_CSS_BACKGROUND_REPEAT_SHIFT;
567     }
568
569   g_value_set_enum (value, repeat);
570   return TRUE;
571 }
572
573 static void
574 background_repeat_value_print (GtkCssStyleProperty *property,
575                                const GValue        *value,
576                                GString             *string)
577 {
578   GEnumClass *enum_class;
579   GEnumValue *enum_value;
580   GtkCssBackgroundRepeat repeat;
581
582   repeat = g_value_get_enum (value);
583   enum_class = g_type_class_ref (GTK_TYPE_CSS_BACKGROUND_REPEAT);
584   enum_value = g_enum_get_value (enum_class, repeat);
585
586   /* only triggers for 'repeat-x' and 'repeat-y' */
587   if (enum_value)
588     g_string_append (string, enum_value->value_nick);
589   else
590     {
591       enum_value = g_enum_get_value (enum_class, GTK_CSS_BACKGROUND_HORIZONTAL (repeat));
592       g_string_append (string, enum_value->value_nick);
593
594       if (GTK_CSS_BACKGROUND_HORIZONTAL (repeat) != GTK_CSS_BACKGROUND_VERTICAL (repeat))
595         {
596           enum_value = g_enum_get_value (enum_class, GTK_CSS_BACKGROUND_VERTICAL (repeat));
597           g_string_append (string, " ");
598           g_string_append (string, enum_value->value_nick);
599         }
600     }
601
602   g_type_class_unref (enum_class);
603 }
604
605 static gboolean
606 background_size_parse (GtkCssStyleProperty *property,
607                        GValue              *value,
608                        GtkCssParser        *parser,
609                        GFile               *base)
610 {
611   GtkCssBackgroundSize size = { GTK_CSS_NUMBER_INIT (0, GTK_CSS_PX), GTK_CSS_NUMBER_INIT (0, GTK_CSS_PX), FALSE, FALSE};
612
613   if (_gtk_css_parser_try (parser, "cover", TRUE))
614     size.cover = TRUE;
615   else if (_gtk_css_parser_try (parser, "contain", TRUE))
616     size.contain = TRUE;
617   else
618     {
619       if (_gtk_css_parser_try (parser, "auto", TRUE))
620         _gtk_css_number_init (&size.width, 0, GTK_CSS_PX);
621       else if (!_gtk_css_parser_read_number (parser,
622                                              &size.width,
623                                              GTK_CSS_POSITIVE_ONLY
624                                              | GTK_CSS_PARSE_PERCENT
625                                              | GTK_CSS_PARSE_LENGTH))
626         return FALSE;
627
628       if (_gtk_css_parser_try (parser, "auto", TRUE))
629         _gtk_css_number_init (&size.height, 0, GTK_CSS_PX);
630       else if (_gtk_css_parser_has_number (parser))
631         {
632           if (!_gtk_css_parser_read_number (parser,
633                                             &size.height,
634                                             GTK_CSS_POSITIVE_ONLY
635                                             | GTK_CSS_PARSE_PERCENT
636                                             | GTK_CSS_PARSE_LENGTH))
637             return FALSE;
638         }
639       else
640         _gtk_css_number_init (&size.height, 0, GTK_CSS_PX);
641     }
642
643   g_value_set_boxed (value, &size);
644   return TRUE;
645 }
646
647 static void
648 background_size_print (GtkCssStyleProperty *property,
649                        const GValue        *value,
650                        GString             *string)
651 {
652   GtkCssBackgroundSize *size = g_value_get_boxed (value);
653
654   if (size->cover)
655     g_string_append (string, "cover");
656   else if (size->contain)
657     g_string_append (string, "contain");
658   else
659     {
660       if (size->width.value == 0)
661         g_string_append (string, "auto");
662       else
663         _gtk_css_number_print (&size->width, string);
664
665       if (size->height.value != 0)
666         {
667           g_string_append (string, " ");
668           _gtk_css_number_print (&size->height, string);
669         }
670     }
671 }
672
673 static GtkCssValue *
674 background_size_compute (GtkCssStyleProperty    *property,
675                          GtkStyleContext        *context,
676                          GtkCssValue            *specified)
677 {
678   GtkCssBackgroundSize *ssize = _gtk_css_value_get_background_size (specified);
679   GtkCssBackgroundSize csize;
680   gboolean changed;
681
682   csize.cover = ssize->cover;
683   csize.contain = ssize->contain;
684   changed = _gtk_css_number_compute (&csize.width,
685                                      &ssize->width,
686                                      context);
687   changed |= _gtk_css_number_compute (&csize.height,
688                                       &ssize->height,
689                                       context);
690   if (changed)
691     return _gtk_css_value_new_from_background_size (&csize);
692   return _gtk_css_value_ref (specified);
693 }
694
695 static gboolean
696 background_position_parse (GtkCssStyleProperty *property,
697                            GValue              *value,
698                            GtkCssParser        *parser,
699                            GFile               *base)
700 {
701   static const struct {
702     const char *name;
703     guint       percentage;
704     gboolean    horizontal;
705     gboolean    vertical;
706   } names[] = {
707     { "left",     0, TRUE,  FALSE },
708     { "right",  100, TRUE,  FALSE },
709     { "center",  50, TRUE,  TRUE  },
710     { "top",      0, FALSE, TRUE  },
711     { "bottom", 100, FALSE, TRUE  },
712     { NULL    ,   0, TRUE,  FALSE }, /* used for numbers */
713     { NULL    ,  50, TRUE,  TRUE  }  /* used for no value */
714   };
715   GtkCssBackgroundPosition pos;
716   GtkCssNumber *missing;
717   guint first, second;
718
719   for (first = 0; names[first].name != NULL; first++)
720     {
721       if (_gtk_css_parser_try (parser, names[first].name, TRUE))
722         {
723           if (names[first].horizontal)
724             {
725               _gtk_css_number_init (&pos.x, names[first].percentage, GTK_CSS_PERCENT);
726               missing = &pos.y;
727             }
728           else
729             {
730               _gtk_css_number_init (&pos.y, names[first].percentage, GTK_CSS_PERCENT);
731               missing = &pos.x;
732             }
733           break;
734         }
735     }
736   if (names[first].name == NULL)
737     {
738       missing = &pos.y;
739       if (!_gtk_css_parser_read_number (parser,
740                                         &pos.x,
741                                         GTK_CSS_PARSE_PERCENT
742                                         | GTK_CSS_PARSE_LENGTH))
743         return FALSE;
744     }
745
746   for (second = 0; names[second].name != NULL; second++)
747     {
748       if (_gtk_css_parser_try (parser, names[second].name, TRUE))
749         {
750           _gtk_css_number_init (missing, names[second].percentage, GTK_CSS_PERCENT);
751           break;
752         }
753     }
754
755   if (names[second].name == NULL)
756     {
757       if (_gtk_css_parser_has_number (parser))
758         {
759           if (missing != &pos.y)
760             {
761               _gtk_css_parser_error (parser, "Invalid combination of values");
762               return FALSE;
763             }
764           if (!_gtk_css_parser_read_number (parser,
765                                             missing,
766                                             GTK_CSS_PARSE_PERCENT
767                                             | GTK_CSS_PARSE_LENGTH))
768             return FALSE;
769         }
770       else
771         {
772           second++;
773           _gtk_css_number_init (missing, 50, GTK_CSS_PERCENT);
774         }
775     }
776   else
777     {
778       if ((names[first].horizontal && !names[second].vertical) ||
779           (!names[first].horizontal && !names[second].horizontal))
780         {
781           _gtk_css_parser_error (parser, "Invalid combination of values");
782           return FALSE;
783         }
784     }
785
786   g_value_set_boxed (value, &pos);
787   return TRUE;
788 }
789
790 static void
791 background_position_print (GtkCssStyleProperty *property,
792                            const GValue        *value,
793                            GString             *string)
794 {
795   GtkCssBackgroundPosition *pos = g_value_get_boxed (value);
796   static const GtkCssNumber center = GTK_CSS_NUMBER_INIT (50, GTK_CSS_PERCENT);
797   static const struct {
798     const char *x_name;
799     const char *y_name;
800     GtkCssNumber number;
801   } values[] = { 
802     { "left",   "top",    GTK_CSS_NUMBER_INIT (0,   GTK_CSS_PERCENT) },
803     { "right",  "bottom", GTK_CSS_NUMBER_INIT (100, GTK_CSS_PERCENT) }
804   };
805   guint i;
806
807   if (_gtk_css_number_equal (&pos->x, &center))
808     {
809       if (_gtk_css_number_equal (&pos->y, &center))
810         {
811           g_string_append (string, "center");
812           return;
813         }
814     }
815   else
816     {
817       for (i = 0; i < G_N_ELEMENTS (values); i++)
818         {
819           if (_gtk_css_number_equal (&pos->x, &values[i].number))
820             {
821               g_string_append (string, values[i].x_name);
822               break;
823             }
824         }
825       if (i == G_N_ELEMENTS (values))
826         _gtk_css_number_print (&pos->x, string);
827
828       if (_gtk_css_number_equal (&pos->y, &center))
829         return;
830
831       g_string_append_c (string, ' ');
832     }
833
834   for (i = 0; i < G_N_ELEMENTS (values); i++)
835     {
836       if (_gtk_css_number_equal (&pos->y, &values[i].number))
837         {
838           g_string_append (string, values[i].y_name);
839           break;
840         }
841     }
842   if (i == G_N_ELEMENTS (values))
843     {
844       if (_gtk_css_number_equal (&pos->x, &center))
845         g_string_append (string, "center ");
846       _gtk_css_number_print (&pos->y, string);
847     }
848 }
849
850 static GtkCssValue *
851 background_position_compute (GtkCssStyleProperty    *property,
852                              GtkStyleContext        *context,
853                              GtkCssValue            *specified)
854 {
855   GtkCssBackgroundPosition *spos = _gtk_css_value_get_background_position (specified);
856   GtkCssBackgroundPosition cpos;
857   gboolean changed;
858
859   changed = _gtk_css_number_compute (&cpos.x,
860                                      &spos->x,
861                                      context);
862   changed |= _gtk_css_number_compute (&cpos.y,
863                                       &spos->y,
864                                       context);
865   if (changed)
866     return _gtk_css_value_new_from_background_position (&cpos);
867   return _gtk_css_value_ref (specified);
868 }
869
870 /*** REGISTRATION ***/
871
872 static GtkSymbolicColor *
873 gtk_symbolic_color_new_rgba (double red,
874                              double green,
875                              double blue,
876                              double alpha)
877 {
878   GdkRGBA rgba = { red, green, blue, alpha };
879
880   return gtk_symbolic_color_new_literal (&rgba);
881 }
882
883 void
884 _gtk_css_style_property_init_properties (void)
885 {
886   char *default_font_family[] = { "Sans", NULL };
887   GtkCssNumber number;
888   GtkSymbolicColor *symbolic;
889   GtkCssBackgroundSize default_background_size = { GTK_CSS_NUMBER_INIT (0, GTK_CSS_PX), GTK_CSS_NUMBER_INIT (0, GTK_CSS_PX), FALSE, FALSE };
890   GtkCssBackgroundPosition default_background_position = { GTK_CSS_NUMBER_INIT (0, GTK_CSS_PERCENT), GTK_CSS_NUMBER_INIT (0, GTK_CSS_PERCENT)};
891   GtkCssBorderCornerRadius no_corner_radius = { GTK_CSS_NUMBER_INIT (0, GTK_CSS_PX), GTK_CSS_NUMBER_INIT (0, GTK_CSS_PX) };
892   GtkBorder border_of_ones = { 1, 1, 1, 1 };
893   GtkCssBorderImageRepeat border_image_repeat = { GTK_CSS_REPEAT_STYLE_STRETCH, GTK_CSS_REPEAT_STYLE_STRETCH };
894
895   /* Initialize "color" and "font-size" first,
896    * so that when computing values later they are
897    * done first. That way, 'currentColor' and font
898    * sizes in em can be looked up properly */
899   symbolic = gtk_symbolic_color_new_rgba (1, 1, 1, 1);
900   gtk_css_style_property_register        ("color",
901                                           GTK_TYPE_SYMBOLIC_COLOR,
902                                           GDK_TYPE_RGBA,
903                                           GDK_TYPE_RGBA,
904                                           GTK_STYLE_PROPERTY_INHERIT,
905                                           NULL,
906                                           NULL,
907                                           color_compute,
908                                           symbolic);
909   gtk_symbolic_color_unref (symbolic);
910   gtk_css_style_property_register        ("font-size",
911                                           G_TYPE_DOUBLE,
912                                           G_TYPE_DOUBLE,
913                                           G_TYPE_DOUBLE,
914                                           GTK_STYLE_PROPERTY_INHERIT,
915                                           NULL,
916                                           NULL,
917                                           NULL,
918                                           10.0);
919
920   /* properties that aren't referenced when computing values
921    * start here */
922   symbolic = gtk_symbolic_color_new_rgba (0, 0, 0, 0);
923   gtk_css_style_property_register        ("background-color",
924                                           GTK_TYPE_SYMBOLIC_COLOR,
925                                           GDK_TYPE_RGBA,
926                                           GDK_TYPE_RGBA,
927                                           0,
928                                           NULL,
929                                           NULL,
930                                           color_compute,
931                                           symbolic);
932   gtk_symbolic_color_unref (symbolic);
933
934   gtk_css_style_property_register        ("font-family",
935                                           G_TYPE_STRV,
936                                           G_TYPE_STRV,
937                                           G_TYPE_STRV,
938                                           GTK_STYLE_PROPERTY_INHERIT,
939                                           font_family_parse,
940                                           font_family_value_print,
941                                           NULL,
942                                           default_font_family);
943   gtk_css_style_property_register        ("font-style",
944                                           PANGO_TYPE_STYLE,
945                                           PANGO_TYPE_STYLE,
946                                           PANGO_TYPE_STYLE,
947                                           GTK_STYLE_PROPERTY_INHERIT,
948                                           NULL,
949                                           NULL,
950                                           NULL,
951                                           PANGO_STYLE_NORMAL);
952   gtk_css_style_property_register        ("font-variant",
953                                           PANGO_TYPE_VARIANT,
954                                           PANGO_TYPE_VARIANT,
955                                           PANGO_TYPE_VARIANT,
956                                           GTK_STYLE_PROPERTY_INHERIT,
957                                           NULL,
958                                           NULL,
959                                           NULL,
960                                           PANGO_VARIANT_NORMAL);
961   /* xxx: need to parse this properly, ie parse the numbers */
962   gtk_css_style_property_register        ("font-weight",
963                                           PANGO_TYPE_WEIGHT,
964                                           PANGO_TYPE_WEIGHT,
965                                           PANGO_TYPE_WEIGHT,
966                                           GTK_STYLE_PROPERTY_INHERIT,
967                                           NULL,
968                                           NULL,
969                                           NULL,
970                                           PANGO_WEIGHT_NORMAL);
971
972   gtk_css_style_property_register        ("text-shadow",
973                                           GTK_TYPE_SHADOW,
974                                           GTK_TYPE_SHADOW,
975                                           GTK_TYPE_SHADOW,
976                                           GTK_STYLE_PROPERTY_INHERIT,
977                                           NULL,
978                                           NULL,
979                                           NULL,
980                                           NULL);
981
982   gtk_css_style_property_register        ("icon-shadow",
983                                           GTK_TYPE_SHADOW,
984                                           GTK_TYPE_SHADOW,
985                                           GTK_TYPE_SHADOW,
986                                           GTK_STYLE_PROPERTY_INHERIT,
987                                           NULL,
988                                           NULL,
989                                           NULL,
990                                           NULL);
991
992   gtk_css_style_property_register        ("box-shadow",
993                                           GTK_TYPE_SHADOW,
994                                           GTK_TYPE_SHADOW,
995                                           GTK_TYPE_SHADOW,
996                                           0,
997                                           NULL,
998                                           NULL,
999                                           NULL,
1000                                           NULL);
1001
1002   _gtk_css_number_init (&number, 0, GTK_CSS_PX);
1003   gtk_css_style_property_register        ("margin-top",
1004                                           GTK_TYPE_CSS_NUMBER,
1005                                           GTK_TYPE_CSS_NUMBER,
1006                                           G_TYPE_INT,
1007                                           0,
1008                                           parse_margin,
1009                                           NULL,
1010                                           compute_margin,
1011                                           &number);
1012   gtk_css_style_property_register        ("margin-left",
1013                                           GTK_TYPE_CSS_NUMBER,
1014                                           GTK_TYPE_CSS_NUMBER,
1015                                           G_TYPE_INT,
1016                                           0,
1017                                           parse_margin,
1018                                           NULL,
1019                                           compute_margin,
1020                                           &number);
1021   gtk_css_style_property_register        ("margin-bottom",
1022                                           GTK_TYPE_CSS_NUMBER,
1023                                           GTK_TYPE_CSS_NUMBER,
1024                                           G_TYPE_INT,
1025                                           0,
1026                                           parse_margin,
1027                                           NULL,
1028                                           compute_margin,
1029                                           &number);
1030   gtk_css_style_property_register        ("margin-right",
1031                                           GTK_TYPE_CSS_NUMBER,
1032                                           GTK_TYPE_CSS_NUMBER,
1033                                           G_TYPE_INT,
1034                                           0,
1035                                           parse_margin,
1036                                           NULL,
1037                                           compute_margin,
1038                                           &number);
1039   gtk_css_style_property_register        ("padding-top",
1040                                           GTK_TYPE_CSS_NUMBER,
1041                                           GTK_TYPE_CSS_NUMBER,
1042                                           G_TYPE_INT,
1043                                           0,
1044                                           parse_padding,
1045                                           NULL,
1046                                           compute_padding,
1047                                           &number);
1048   gtk_css_style_property_register        ("padding-left",
1049                                           GTK_TYPE_CSS_NUMBER,
1050                                           GTK_TYPE_CSS_NUMBER,
1051                                           G_TYPE_INT,
1052                                           0,
1053                                           parse_padding,
1054                                           NULL,
1055                                           compute_padding,
1056                                           &number);
1057   gtk_css_style_property_register        ("padding-bottom",
1058                                           GTK_TYPE_CSS_NUMBER,
1059                                           GTK_TYPE_CSS_NUMBER,
1060                                           G_TYPE_INT,
1061                                           0,
1062                                           parse_padding,
1063                                           NULL,
1064                                           compute_padding,
1065                                           &number);
1066   gtk_css_style_property_register        ("padding-right",
1067                                           GTK_TYPE_CSS_NUMBER,
1068                                           GTK_TYPE_CSS_NUMBER,
1069                                           G_TYPE_INT,
1070                                           0,
1071                                           parse_padding,
1072                                           NULL,
1073                                           compute_padding,
1074                                           &number);
1075   /* IMPORTANT: compute_border_width() requires that the border-width
1076    * properties be immeditaly followed by the border-style properties
1077    */
1078   gtk_css_style_property_register        ("border-top-style",
1079                                           GTK_TYPE_BORDER_STYLE,
1080                                           GTK_TYPE_BORDER_STYLE,
1081                                           GTK_TYPE_BORDER_STYLE,
1082                                           0,
1083                                           NULL,
1084                                           NULL,
1085                                           NULL,
1086                                           GTK_BORDER_STYLE_NONE);
1087   gtk_css_style_property_register        ("border-top-width",
1088                                           GTK_TYPE_CSS_NUMBER,
1089                                           G_TYPE_INT,
1090                                           G_TYPE_INT,
1091                                           0,
1092                                           parse_border_width,
1093                                           NULL,
1094                                           compute_border_width,
1095                                           &number);
1096   gtk_css_style_property_register        ("border-left-style",
1097                                           GTK_TYPE_BORDER_STYLE,
1098                                           GTK_TYPE_BORDER_STYLE,
1099                                           GTK_TYPE_BORDER_STYLE,
1100                                           0,
1101                                           NULL,
1102                                           NULL,
1103                                           NULL,
1104                                           GTK_BORDER_STYLE_NONE);
1105   gtk_css_style_property_register        ("border-left-width",
1106                                           GTK_TYPE_CSS_NUMBER,
1107                                           G_TYPE_INT,
1108                                           G_TYPE_INT,
1109                                           0,
1110                                           parse_border_width,
1111                                           NULL,
1112                                           compute_border_width,
1113                                           &number);
1114   gtk_css_style_property_register        ("border-bottom-style",
1115                                           GTK_TYPE_BORDER_STYLE,
1116                                           GTK_TYPE_BORDER_STYLE,
1117                                           GTK_TYPE_BORDER_STYLE,
1118                                           0,
1119                                           NULL,
1120                                           NULL,
1121                                           NULL,
1122                                           GTK_BORDER_STYLE_NONE);
1123   gtk_css_style_property_register        ("border-bottom-width",
1124                                           GTK_TYPE_CSS_NUMBER,
1125                                           G_TYPE_INT,
1126                                           G_TYPE_INT,
1127                                           0,
1128                                           parse_border_width,
1129                                           NULL,
1130                                           compute_border_width,
1131                                           &number);
1132   gtk_css_style_property_register        ("border-right-style",
1133                                           GTK_TYPE_BORDER_STYLE,
1134                                           GTK_TYPE_BORDER_STYLE,
1135                                           GTK_TYPE_BORDER_STYLE,
1136                                           0,
1137                                           NULL,
1138                                           NULL,
1139                                           NULL,
1140                                           GTK_BORDER_STYLE_NONE);
1141   gtk_css_style_property_register        ("border-right-width",
1142                                           GTK_TYPE_CSS_NUMBER,
1143                                           G_TYPE_INT,
1144                                           G_TYPE_INT,
1145                                           0,
1146                                           parse_border_width,
1147                                           NULL,
1148                                           compute_border_width,
1149                                           &number);
1150
1151   gtk_css_style_property_register        ("border-top-left-radius",
1152                                           GTK_TYPE_CSS_BORDER_CORNER_RADIUS,
1153                                           GTK_TYPE_CSS_BORDER_CORNER_RADIUS,
1154                                           GTK_TYPE_CSS_BORDER_CORNER_RADIUS,
1155                                           0,
1156                                           border_corner_radius_value_parse,
1157                                           border_corner_radius_value_print,
1158                                           NULL,
1159                                           &no_corner_radius);
1160   gtk_css_style_property_register        ("border-top-right-radius",
1161                                           GTK_TYPE_CSS_BORDER_CORNER_RADIUS,
1162                                           GTK_TYPE_CSS_BORDER_CORNER_RADIUS,
1163                                           GTK_TYPE_CSS_BORDER_CORNER_RADIUS,
1164                                           0,
1165                                           border_corner_radius_value_parse,
1166                                           border_corner_radius_value_print,
1167                                           NULL,
1168                                           &no_corner_radius);
1169   gtk_css_style_property_register        ("border-bottom-right-radius",
1170                                           GTK_TYPE_CSS_BORDER_CORNER_RADIUS,
1171                                           GTK_TYPE_CSS_BORDER_CORNER_RADIUS,
1172                                           GTK_TYPE_CSS_BORDER_CORNER_RADIUS,
1173                                           0,
1174                                           border_corner_radius_value_parse,
1175                                           border_corner_radius_value_print,
1176                                           NULL,
1177                                           &no_corner_radius);
1178   gtk_css_style_property_register        ("border-bottom-left-radius",
1179                                           GTK_TYPE_CSS_BORDER_CORNER_RADIUS,
1180                                           GTK_TYPE_CSS_BORDER_CORNER_RADIUS,
1181                                           GTK_TYPE_CSS_BORDER_CORNER_RADIUS,
1182                                           0,
1183                                           border_corner_radius_value_parse,
1184                                           border_corner_radius_value_print,
1185                                           NULL,
1186                                           &no_corner_radius);
1187
1188   gtk_css_style_property_register        ("outline-style",
1189                                           GTK_TYPE_BORDER_STYLE,
1190                                           GTK_TYPE_BORDER_STYLE,
1191                                           GTK_TYPE_BORDER_STYLE,
1192                                           0,
1193                                           NULL,
1194                                           NULL,
1195                                           NULL,
1196                                           GTK_BORDER_STYLE_NONE);
1197   gtk_css_style_property_register        ("outline-width",
1198                                           GTK_TYPE_CSS_NUMBER,
1199                                           G_TYPE_INT,
1200                                           G_TYPE_INT,
1201                                           0,
1202                                           parse_border_width,
1203                                           NULL,
1204                                           compute_border_width,
1205                                           &number);
1206   gtk_css_style_property_register        ("outline-offset",
1207                                           G_TYPE_INT,
1208                                           G_TYPE_INT,
1209                                           G_TYPE_INT,
1210                                           0,
1211                                           NULL,
1212                                           NULL,
1213                                           NULL,
1214                                           0);
1215
1216   gtk_css_style_property_register        ("background-clip",
1217                                           GTK_TYPE_CSS_AREA,
1218                                           GTK_TYPE_CSS_AREA,
1219                                           GTK_TYPE_CSS_AREA,
1220                                           0,
1221                                           NULL,
1222                                           NULL,
1223                                           NULL,
1224                                           GTK_CSS_AREA_BORDER_BOX);
1225   gtk_css_style_property_register        ("background-origin",
1226                                           GTK_TYPE_CSS_AREA,
1227                                           GTK_TYPE_CSS_AREA,
1228                                           GTK_TYPE_CSS_AREA,
1229                                           0,
1230                                           NULL,
1231                                           NULL,
1232                                           NULL,
1233                                           GTK_CSS_AREA_PADDING_BOX);
1234   gtk_css_style_property_register        ("background-size",
1235                                           GTK_TYPE_CSS_BACKGROUND_SIZE,
1236                                           GTK_TYPE_CSS_BACKGROUND_SIZE,
1237                                           G_TYPE_NONE,
1238                                           0,
1239                                           background_size_parse,
1240                                           background_size_print,
1241                                           background_size_compute,
1242                                           &default_background_size);
1243   gtk_css_style_property_register        ("background-position",
1244                                           GTK_TYPE_CSS_BACKGROUND_POSITION,
1245                                           GTK_TYPE_CSS_BACKGROUND_POSITION,
1246                                           G_TYPE_NONE,
1247                                           0,
1248                                           background_position_parse,
1249                                           background_position_print,
1250                                           background_position_compute,
1251                                           &default_background_position);
1252
1253   gtk_css_style_property_register        ("border-top-color",
1254                                           GTK_TYPE_SYMBOLIC_COLOR,
1255                                           GDK_TYPE_RGBA,
1256                                           GDK_TYPE_RGBA,
1257                                           0,
1258                                           NULL,
1259                                           NULL,
1260                                           color_compute,
1261                                           _gtk_symbolic_color_get_current_color ());
1262   gtk_css_style_property_register        ("border-right-color",
1263                                           GTK_TYPE_SYMBOLIC_COLOR,
1264                                           GDK_TYPE_RGBA,
1265                                           GDK_TYPE_RGBA,
1266                                           0,
1267                                           NULL,
1268                                           NULL,
1269                                           color_compute,
1270                                           _gtk_symbolic_color_get_current_color ());
1271   gtk_css_style_property_register        ("border-bottom-color",
1272                                           GTK_TYPE_SYMBOLIC_COLOR,
1273                                           GDK_TYPE_RGBA,
1274                                           GDK_TYPE_RGBA,
1275                                           0,
1276                                           NULL,
1277                                           NULL,
1278                                           color_compute,
1279                                           _gtk_symbolic_color_get_current_color ());
1280   gtk_css_style_property_register        ("border-left-color",
1281                                           GTK_TYPE_SYMBOLIC_COLOR,
1282                                           GDK_TYPE_RGBA,
1283                                           GDK_TYPE_RGBA,
1284                                           0,
1285                                           NULL,
1286                                           NULL,
1287                                           color_compute,
1288                                           _gtk_symbolic_color_get_current_color ());
1289   gtk_css_style_property_register        ("outline-color",
1290                                           GTK_TYPE_SYMBOLIC_COLOR,
1291                                           GDK_TYPE_RGBA,
1292                                           GDK_TYPE_RGBA,
1293                                           0,
1294                                           NULL,
1295                                           NULL,
1296                                           color_compute,
1297                                           _gtk_symbolic_color_get_current_color ());
1298
1299   gtk_css_style_property_register        ("background-repeat",
1300                                           GTK_TYPE_CSS_BACKGROUND_REPEAT,
1301                                           GTK_TYPE_CSS_BACKGROUND_REPEAT,
1302                                           GTK_TYPE_CSS_BACKGROUND_REPEAT,
1303                                           0,
1304                                           background_repeat_value_parse,
1305                                           background_repeat_value_print,
1306                                           NULL,
1307                                           GTK_CSS_BACKGROUND_REPEAT | (GTK_CSS_BACKGROUND_REPEAT << GTK_CSS_BACKGROUND_REPEAT_SHIFT));
1308   gtk_css_style_property_register        ("background-image",
1309                                           GTK_TYPE_CSS_IMAGE,
1310                                           GTK_TYPE_CSS_IMAGE,
1311                                           CAIRO_GOBJECT_TYPE_PATTERN,
1312                                           0,
1313                                           css_image_value_parse,
1314                                           css_image_value_print,
1315                                           css_image_value_compute,
1316                                           NULL);
1317
1318   gtk_css_style_property_register        ("border-image-source",
1319                                           GTK_TYPE_CSS_IMAGE,
1320                                           GTK_TYPE_CSS_IMAGE,
1321                                           CAIRO_GOBJECT_TYPE_PATTERN,
1322                                           0,
1323                                           css_image_value_parse,
1324                                           css_image_value_print,
1325                                           css_image_value_compute,
1326                                           NULL);
1327   gtk_css_style_property_register        ("border-image-repeat",
1328                                           GTK_TYPE_CSS_BORDER_IMAGE_REPEAT,
1329                                           GTK_TYPE_CSS_BORDER_IMAGE_REPEAT,
1330                                           GTK_TYPE_CSS_BORDER_IMAGE_REPEAT,
1331                                           0,
1332                                           NULL,
1333                                           NULL,
1334                                           NULL,
1335                                           &border_image_repeat);
1336
1337   /* XXX: The initial value is wrong, it should be 100% */
1338   gtk_css_style_property_register        ("border-image-slice",
1339                                           GTK_TYPE_BORDER,
1340                                           GTK_TYPE_BORDER,
1341                                           GTK_TYPE_BORDER,
1342                                           0,
1343                                           NULL,
1344                                           NULL,
1345                                           NULL,
1346                                           &border_of_ones);
1347   gtk_css_style_property_register        ("border-image-width",
1348                                           GTK_TYPE_BORDER,
1349                                           GTK_TYPE_BORDER,
1350                                           GTK_TYPE_BORDER,
1351                                           0,
1352                                           NULL,
1353                                           NULL,
1354                                           NULL,
1355                                           NULL);
1356   gtk_css_style_property_register        ("engine",
1357                                           GTK_TYPE_THEMING_ENGINE,
1358                                           GTK_TYPE_THEMING_ENGINE,
1359                                           GTK_TYPE_THEMING_ENGINE,
1360                                           0,
1361                                           NULL,
1362                                           NULL,
1363                                           NULL,
1364                                           gtk_theming_engine_load (NULL));
1365   gtk_css_style_property_register        ("transition",
1366                                           GTK_TYPE_ANIMATION_DESCRIPTION,
1367                                           GTK_TYPE_ANIMATION_DESCRIPTION,
1368                                           GTK_TYPE_ANIMATION_DESCRIPTION,
1369                                           0,
1370                                           NULL,
1371                                           NULL,
1372                                           NULL,
1373                                           NULL);
1374
1375   /* Private property holding the binding sets */
1376   gtk_css_style_property_register        ("gtk-key-bindings",
1377                                           G_TYPE_PTR_ARRAY,
1378                                           G_TYPE_PTR_ARRAY,
1379                                           G_TYPE_PTR_ARRAY,
1380                                           0,
1381                                           bindings_value_parse,
1382                                           bindings_value_print,
1383                                           NULL,
1384                                           NULL);
1385 }
1386