]> Pileus Git - ~andy/gtk/blob - gtk/gtkcssshorthandpropertyimpl.c
shorthand: use array values for shorthand properties parsing
[~andy/gtk] / gtk / gtkcssshorthandpropertyimpl.c
1 /*
2  * Copyright © 2011 Red Hat Inc.
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.1 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  * Authors: Benjamin Otte <otte@gnome.org>
18  */
19
20 #include "config.h"
21
22 #include "gtkcssshorthandpropertyprivate.h"
23
24 #include <cairo-gobject.h>
25 #include <math.h>
26
27 #include "gtkcssarrayvalueprivate.h"
28 #include "gtkcssbordervalueprivate.h"
29 #include "gtkcsscornervalueprivate.h"
30 #include "gtkcsseasevalueprivate.h"
31 #include "gtkcssenumvalueprivate.h"
32 #include "gtkcssimageprivate.h"
33 #include "gtkcssimagevalueprivate.h"
34 #include "gtkcssnumbervalueprivate.h"
35 #include "gtkcssrepeatvalueprivate.h"
36 #include "gtkcssstringvalueprivate.h"
37 #include "gtkcssstylefuncsprivate.h"
38 #include "gtkcssvalueprivate.h"
39 #include "gtkstylepropertiesprivate.h"
40 #include "gtksymboliccolorprivate.h"
41 #include "gtktypebuiltins.h"
42
43 /* this is in case round() is not provided by the compiler, 
44  * such as in the case of C89 compilers, like MSVC
45  */
46 #include "fallback-c89.c"
47
48 /*** PARSING ***/
49
50 static gboolean
51 value_is_done_parsing (GtkCssParser *parser)
52 {
53   return _gtk_css_parser_is_eof (parser) ||
54          _gtk_css_parser_begins_with (parser, ',') ||
55          _gtk_css_parser_begins_with (parser, ';') ||
56          _gtk_css_parser_begins_with (parser, '}');
57 }
58
59 static gboolean
60 parse_four_numbers (GtkCssShorthandProperty  *shorthand,
61                     GtkCssValue             **values,
62                     GtkCssParser             *parser,
63                     GtkCssNumberParseFlags    flags)
64 {
65   guint i;
66
67   for (i = 0; i < 4; i++)
68     {
69       if (!_gtk_css_parser_has_number (parser))
70         break;
71
72       values[i] = _gtk_css_number_value_parse (parser, flags);
73       if (values[i] == NULL)
74         return FALSE;
75     }
76
77   if (i == 0)
78     {
79       _gtk_css_parser_error (parser, "Expected a length");
80       return FALSE;
81     }
82
83   for (; i < 4; i++)
84     {
85       values[i] = _gtk_css_value_ref (values[(i - 1) >> 1]);
86     }
87
88   return TRUE;
89 }
90
91 static gboolean
92 parse_margin (GtkCssShorthandProperty  *shorthand,
93               GtkCssValue             **values,
94               GtkCssParser             *parser)
95 {
96   return parse_four_numbers (shorthand,
97                              values,
98                              parser,
99                              GTK_CSS_NUMBER_AS_PIXELS
100                              | GTK_CSS_PARSE_LENGTH);
101 }
102
103 static gboolean
104 parse_padding (GtkCssShorthandProperty  *shorthand,
105                GtkCssValue             **values,
106                GtkCssParser             *parser)
107 {
108   return parse_four_numbers (shorthand,
109                              values,
110                              parser,
111                              GTK_CSS_POSITIVE_ONLY
112                              | GTK_CSS_NUMBER_AS_PIXELS
113                              | GTK_CSS_PARSE_LENGTH);
114 }
115
116 static gboolean
117 parse_border_width (GtkCssShorthandProperty  *shorthand,
118                     GtkCssValue             **values,
119                     GtkCssParser             *parser)
120 {
121   return parse_four_numbers (shorthand,
122                              values,
123                              parser,
124                              GTK_CSS_POSITIVE_ONLY
125                              | GTK_CSS_NUMBER_AS_PIXELS
126                              | GTK_CSS_PARSE_LENGTH);
127 }
128
129 static gboolean 
130 parse_border_radius (GtkCssShorthandProperty  *shorthand,
131                      GtkCssValue             **values,
132                      GtkCssParser             *parser)
133 {
134   GtkCssValue *x[4] = { NULL, }, *y[4] = { NULL, };
135   guint i;
136
137   for (i = 0; i < 4; i++)
138     {
139       if (!_gtk_css_parser_has_number (parser))
140         break;
141       x[i] = _gtk_css_number_value_parse (parser,
142                                           GTK_CSS_POSITIVE_ONLY
143                                           | GTK_CSS_PARSE_PERCENT
144                                           | GTK_CSS_NUMBER_AS_PIXELS
145                                           | GTK_CSS_PARSE_LENGTH);
146       if (x[i] == NULL)
147         goto fail;
148     }
149
150   if (i == 0)
151     {
152       _gtk_css_parser_error (parser, "Expected a number");
153       goto fail;
154     }
155
156   /* The magic (i - 1) >> 1 below makes it take the correct value
157    * according to spec. Feel free to check the 4 cases */
158   for (; i < 4; i++)
159     x[i] = _gtk_css_value_ref (x[(i - 1) >> 1]);
160
161   if (_gtk_css_parser_try (parser, "/", TRUE))
162     {
163       for (i = 0; i < 4; i++)
164         {
165           if (!_gtk_css_parser_has_number (parser))
166             break;
167           y[i] = _gtk_css_number_value_parse (parser,
168                                               GTK_CSS_POSITIVE_ONLY
169                                               | GTK_CSS_PARSE_PERCENT
170                                               | GTK_CSS_NUMBER_AS_PIXELS
171                                               | GTK_CSS_PARSE_LENGTH);
172           if (y[i] == NULL)
173             goto fail;
174         }
175
176       if (i == 0)
177         {
178           _gtk_css_parser_error (parser, "Expected a number");
179           goto fail;
180         }
181
182       for (; i < 4; i++)
183         y[i] = _gtk_css_value_ref (y[(i - 1) >> 1]);
184     }
185   else
186     {
187       for (i = 0; i < 4; i++)
188         y[i] = _gtk_css_value_ref (x[i]);
189     }
190
191   for (i = 0; i < 4; i++)
192     {
193       values[i] = _gtk_css_corner_value_new (x[i], y[i]);
194     }
195
196   return TRUE;
197
198 fail:
199   for (i = 0; i < 4; i++)
200     {
201       if (x[i])
202         _gtk_css_value_unref (x[i]);
203       if (y[i])
204         _gtk_css_value_unref (y[i]);
205     }
206   return FALSE;
207 }
208
209 static gboolean 
210 parse_border_color (GtkCssShorthandProperty  *shorthand,
211                     GtkCssValue             **values,
212                     GtkCssParser             *parser)
213 {
214   guint i;
215
216   for (i = 0; i < 4; i++)
217     {
218       values[i] = _gtk_css_symbolic_value_new (parser);
219       if (values[i] == NULL)
220         return FALSE;
221
222       if (value_is_done_parsing (parser))
223         break;
224     }
225
226   for (i++; i < 4; i++)
227     {
228       values[i] = _gtk_css_value_ref (values[(i - 1) >> 1]);
229     }
230
231   return TRUE;
232 }
233
234 static gboolean
235 parse_border_style (GtkCssShorthandProperty  *shorthand,
236                     GtkCssValue             **values,
237                     GtkCssParser             *parser)
238 {
239   guint i;
240
241   for (i = 0; i < 4; i++)
242     {
243       values[i] = _gtk_css_border_style_value_try_parse (parser);
244       if (values[i] == NULL)
245         break;
246     }
247
248   if (i == 0)
249     {
250       _gtk_css_parser_error (parser, "Expected a border style");
251       return FALSE;
252     }
253
254   for (; i < 4; i++)
255     values[i] = _gtk_css_value_ref (values[(i - 1) >> 1]);
256
257   return TRUE;
258 }
259
260 static gboolean
261 parse_border_image (GtkCssShorthandProperty  *shorthand,
262                     GtkCssValue             **values,
263                     GtkCssParser             *parser)
264 {
265   do
266     {
267       if (values[0] == NULL &&
268           (_gtk_css_parser_has_prefix (parser, "none") ||
269            _gtk_css_image_can_parse (parser)))
270         {
271           GtkCssImage *image;
272
273           if (_gtk_css_parser_try (parser, "none", TRUE))
274             image = NULL;
275           else
276             {
277               image = _gtk_css_image_new_parse (parser);
278               if (image == NULL)
279                 return FALSE;
280             }
281
282           values[0] = _gtk_css_image_value_new (image);
283         }
284       else if (values[3] == NULL &&
285                (values[3] = _gtk_css_border_repeat_value_try_parse (parser)))
286         {
287           /* please move along */
288         }
289       else if (values[1] == NULL)
290         {
291           values[1] = _gtk_css_border_value_parse (parser,
292                                                    GTK_CSS_PARSE_PERCENT
293                                                    | GTK_CSS_PARSE_NUMBER
294                                                    | GTK_CSS_POSITIVE_ONLY,
295                                                    FALSE,
296                                                    TRUE);
297           if (values[1] == NULL)
298             return FALSE;
299
300           if (_gtk_css_parser_try (parser, "/", TRUE))
301             {
302               values[2] = _gtk_css_border_value_parse (parser,
303                                                        GTK_CSS_PARSE_PERCENT
304                                                        | GTK_CSS_PARSE_LENGTH
305                                                        | GTK_CSS_PARSE_NUMBER
306                                                        | GTK_CSS_POSITIVE_ONLY,
307                                                        TRUE,
308                                                        FALSE);
309               if (values[2] == NULL)
310                 return FALSE;
311             }
312         }
313       else
314         {
315           /* We parsed everything and there's still stuff left?
316            * Pretend we didn't notice and let the normal code produce
317            * a 'junk at end of value' error */
318           break;
319         }
320     }
321   while (!value_is_done_parsing (parser));
322
323   return TRUE;
324 }
325
326 static gboolean
327 parse_border_side (GtkCssShorthandProperty  *shorthand,
328                    GtkCssValue             **values,
329                    GtkCssParser             *parser)
330 {
331   do
332   {
333     if (values[0] == NULL &&
334          _gtk_css_parser_has_number (parser))
335       {
336         values[0] = _gtk_css_number_value_parse (parser,
337                                                  GTK_CSS_POSITIVE_ONLY
338                                                  | GTK_CSS_NUMBER_AS_PIXELS
339                                                  | GTK_CSS_PARSE_LENGTH);
340         if (values[0] == NULL)
341           return FALSE;
342       }
343     else if (values[1] == NULL &&
344              (values[1] = _gtk_css_border_style_value_try_parse (parser)))
345       {
346         /* Nothing to do */
347       }
348     else if (values[2] == NULL)
349       {
350         values[2] = _gtk_css_symbolic_value_new (parser);
351         if (values[2] == NULL)
352           return FALSE;
353       }
354   }
355   while (!value_is_done_parsing (parser));
356
357   return TRUE;
358 }
359
360 static gboolean
361 parse_border (GtkCssShorthandProperty  *shorthand,
362               GtkCssValue             **values,
363               GtkCssParser             *parser)
364 {
365   do
366   {
367     if (values[0] == NULL &&
368          _gtk_css_parser_has_number (parser))
369       {
370         values[0] = _gtk_css_number_value_parse (parser,
371                                                  GTK_CSS_POSITIVE_ONLY
372                                                  | GTK_CSS_NUMBER_AS_PIXELS
373                                                  | GTK_CSS_PARSE_LENGTH);
374         if (values[0] == NULL)
375           return FALSE;
376         values[1] = _gtk_css_value_ref (values[0]);
377         values[2] = _gtk_css_value_ref (values[0]);
378         values[3] = _gtk_css_value_ref (values[0]);
379       }
380     else if (values[4] == NULL &&
381              (values[4] = _gtk_css_border_style_value_try_parse (parser)))
382       {
383         values[5] = _gtk_css_value_ref (values[4]);
384         values[6] = _gtk_css_value_ref (values[4]);
385         values[7] = _gtk_css_value_ref (values[4]);
386       }
387     else if (!G_IS_VALUE (&values[8]))
388       {
389         values[8] = _gtk_css_symbolic_value_new (parser);
390         if (values[8] == NULL)
391           return FALSE;
392
393         values[9] = _gtk_css_value_ref (values[8]);
394         values[10] = _gtk_css_value_ref (values[8]);
395         values[11] = _gtk_css_value_ref (values[8]);
396       }
397     else
398       {
399         /* We parsed everything and there's still stuff left?
400          * Pretend we didn't notice and let the normal code produce
401          * a 'junk at end of value' error */
402         break;
403       }
404   }
405   while (!value_is_done_parsing (parser));
406
407   /* Note that border-image values are not set: according to the spec
408      they just need to be reset when using the border shorthand */
409
410   return TRUE;
411 }
412
413 static gboolean
414 parse_font (GtkCssShorthandProperty  *shorthand,
415             GtkCssValue             **values,
416             GtkCssParser             *parser)
417 {
418   PangoFontDescription *desc;
419   guint mask;
420   char *str;
421
422   str = _gtk_css_parser_read_value (parser);
423   if (str == NULL)
424     return FALSE;
425
426   desc = pango_font_description_from_string (str);
427   g_free (str);
428
429   mask = pango_font_description_get_set_fields (desc);
430
431   if (mask & PANGO_FONT_MASK_FAMILY)
432     {
433       values[0] = _gtk_css_array_value_new (_gtk_css_string_value_new (pango_font_description_get_family (desc)));
434     }
435   if (mask & PANGO_FONT_MASK_STYLE)
436     {
437       values[1] = _gtk_css_font_style_value_new (pango_font_description_get_style (desc));
438     }
439   if (mask & PANGO_FONT_MASK_VARIANT)
440     {
441       values[2] = _gtk_css_font_variant_value_new (pango_font_description_get_variant (desc));
442     }
443   if (mask & PANGO_FONT_MASK_WEIGHT)
444     {
445       values[3] = _gtk_css_font_weight_value_new (pango_font_description_get_weight (desc));
446     }
447   if (mask & PANGO_FONT_MASK_SIZE)
448     {
449       values[4] = _gtk_css_number_value_new ((double) pango_font_description_get_size (desc) / PANGO_SCALE, GTK_CSS_PX);
450     }
451
452   pango_font_description_free (desc);
453
454   return TRUE;
455 }
456
457 static gboolean
458 parse_background (GtkCssShorthandProperty  *shorthand,
459                   GtkCssValue             **values,
460                   GtkCssParser             *parser)
461 {
462   GtkCssValue *value;
463
464   do
465     {
466       /* the image part */
467       if (values[0] == NULL &&
468           (_gtk_css_parser_has_prefix (parser, "none") ||
469            _gtk_css_image_can_parse (parser)))
470         {
471           GtkCssImage *image;
472
473           if (_gtk_css_parser_try (parser, "none", TRUE))
474             image = NULL;
475           else
476             {
477               image = _gtk_css_image_new_parse (parser);
478               if (image == NULL)
479                 return FALSE;
480             }
481
482           values[0] = _gtk_css_array_value_new (_gtk_css_image_value_new (image));
483         }
484       else if (values[1] == NULL &&
485                (value = _gtk_css_background_repeat_value_try_parse (parser)))
486         {
487           values[1] = _gtk_css_array_value_new (value);
488           value = NULL;
489         }
490       else if ((values[2] == NULL || values[3] == NULL) &&
491                (value = _gtk_css_area_value_try_parse (parser)))
492         {
493           values[3] = _gtk_css_array_value_new (value);
494
495           if (values[2] == NULL)
496             {
497               values[2] = values[3];
498               values[3] = NULL;
499             }
500           value = NULL;
501         }
502       else if (values[4] == NULL)
503         {
504           value = _gtk_css_symbolic_value_new (parser);
505           if (value == NULL)
506             return FALSE;
507
508           values[4] = _gtk_css_array_value_new (value);
509           value = NULL;
510         }
511       else
512         {
513           /* We parsed everything and there's still stuff left?
514            * Pretend we didn't notice and let the normal code produce
515            * a 'junk at end of value' error */
516           break;
517         }
518     }
519   while (!value_is_done_parsing (parser));
520
521   return TRUE;
522 }
523
524 static gboolean
525 parse_one_transition (GtkCssShorthandProperty  *shorthand,
526                       GtkCssValue             **values,
527                       GtkCssParser             *parser)
528 {
529   do
530     {
531       /* the image part */
532       if (values[2] == NULL &&
533           _gtk_css_parser_has_number (parser) && !_gtk_css_parser_begins_with (parser, '-'))
534         {
535           GtkCssValue *number = _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_TIME);
536
537           if (number == NULL)
538             return FALSE;
539
540           if (values[1] == NULL)
541             values[1] = number;
542           else
543             values[2] = number;
544         }
545       else if (values[3] == NULL &&
546                _gtk_css_ease_value_can_parse (parser))
547         {
548           values[3] = _gtk_css_ease_value_parse (parser);
549
550           if (values[3] == NULL)
551             return FALSE;
552         }
553       else if (values[0] == NULL)
554         {
555           values[0] = _gtk_css_ident_value_try_parse (parser);
556           if (values[0] == NULL)
557             {
558               _gtk_css_parser_error (parser, "Unknown value for property");
559               return FALSE;
560             }
561
562         }
563       else
564         {
565           /* We parsed everything and there's still stuff left?
566            * Pretend we didn't notice and let the normal code produce
567            * a 'junk at end of value' error */
568           break;
569         }
570     }
571   while (!value_is_done_parsing (parser));
572
573   return TRUE;
574 }
575
576 static gboolean
577 parse_transition (GtkCssShorthandProperty  *shorthand,
578                   GtkCssValue             **values,
579                   GtkCssParser             *parser)
580 {
581   GtkCssValue *step_values[4];
582   GPtrArray *arrays[4];
583   guint i;
584
585   for (i = 0; i < 4; i++)
586     {
587       arrays[i] = g_ptr_array_new ();
588       step_values[i] = NULL;
589     }
590
591   do {
592     if (!parse_one_transition (shorthand, step_values, parser))
593       {
594         for (i = 0; i < 4; i++)
595           {
596             g_ptr_array_set_free_func (arrays[i], (GDestroyNotify) _gtk_css_value_unref);
597             g_ptr_array_unref (arrays[i]);
598             return FALSE;
599           }
600       }
601
602       for (i = 0; i < 4; i++)
603         {
604           if (step_values[i] == NULL)
605             {
606               GtkCssValue *initial = _gtk_css_style_property_get_initial_value (
607                                          _gtk_css_shorthand_property_get_subproperty (shorthand, i));
608               step_values[i] = _gtk_css_value_ref (_gtk_css_array_value_get_nth (initial, 0));
609             }
610
611           g_ptr_array_add (arrays[i], step_values[i]);
612           step_values[i] = NULL;
613         }
614   } while (_gtk_css_parser_try (parser, ",", TRUE));
615
616   for (i = 0; i < 4; i++)
617     {
618       values[i] = _gtk_css_array_value_new_from_array ((GtkCssValue **) arrays[i]->pdata, arrays[i]->len);
619       g_ptr_array_unref (arrays[i]);
620     }
621
622   return TRUE;
623 }
624
625 /*** PACKING ***/
626
627 static void
628 unpack_border (GtkCssShorthandProperty *shorthand,
629                GtkStyleProperties      *props,
630                GtkStateFlags            state,
631                const GValue            *value)
632 {
633   GValue v = G_VALUE_INIT;
634   GtkBorder *border = g_value_get_boxed (value);
635
636   g_value_init (&v, G_TYPE_INT);
637
638   g_value_set_int (&v, border->top);
639   _gtk_style_property_assign (GTK_STYLE_PROPERTY (_gtk_css_shorthand_property_get_subproperty (shorthand, 0)), props, state, &v);
640   g_value_set_int (&v, border->right);
641   _gtk_style_property_assign (GTK_STYLE_PROPERTY (_gtk_css_shorthand_property_get_subproperty (shorthand, 1)), props, state, &v);
642   g_value_set_int (&v, border->bottom);
643   _gtk_style_property_assign (GTK_STYLE_PROPERTY (_gtk_css_shorthand_property_get_subproperty (shorthand, 2)), props, state, &v);
644   g_value_set_int (&v, border->left);
645   _gtk_style_property_assign (GTK_STYLE_PROPERTY (_gtk_css_shorthand_property_get_subproperty (shorthand, 3)), props, state, &v);
646
647   g_value_unset (&v);
648 }
649
650 static void
651 pack_border (GtkCssShorthandProperty *shorthand,
652              GValue                  *value,
653              GtkStyleQueryFunc        query_func,
654              gpointer                 query_data)
655 {
656   GtkCssStyleProperty *prop;
657   GtkBorder border;
658   GValue v;
659
660   prop = _gtk_css_shorthand_property_get_subproperty (shorthand, 0);
661   _gtk_style_property_query (GTK_STYLE_PROPERTY (prop), &v, query_func, query_data);
662   border.top = g_value_get_int (&v);
663   g_value_unset (&v);
664
665   prop = _gtk_css_shorthand_property_get_subproperty (shorthand, 1);
666   _gtk_style_property_query (GTK_STYLE_PROPERTY (prop), &v, query_func, query_data);
667   border.right = g_value_get_int (&v);
668   g_value_unset (&v);
669
670   prop = _gtk_css_shorthand_property_get_subproperty (shorthand, 2);
671   _gtk_style_property_query (GTK_STYLE_PROPERTY (prop), &v, query_func, query_data);
672   border.bottom = g_value_get_int (&v);
673   g_value_unset (&v);
674
675   prop = _gtk_css_shorthand_property_get_subproperty (shorthand, 3);
676   _gtk_style_property_query (GTK_STYLE_PROPERTY (prop), &v, query_func, query_data);
677   border.left = g_value_get_int (&v);
678   g_value_unset (&v);
679
680   g_value_init (value, GTK_TYPE_BORDER);
681   g_value_set_boxed (value, &border);
682 }
683
684 static void
685 unpack_border_radius (GtkCssShorthandProperty *shorthand,
686                       GtkStyleProperties      *props,
687                       GtkStateFlags            state,
688                       const GValue            *value)
689 {
690   GtkCssValue *css_value;
691   guint i;
692   
693   css_value = _gtk_css_corner_value_new (_gtk_css_number_value_new (g_value_get_int (value), GTK_CSS_PX),
694                                          _gtk_css_number_value_new (g_value_get_int (value), GTK_CSS_PX));
695
696   for (i = 0; i < 4; i++)
697     _gtk_style_properties_set_property_by_property (props,
698                                                     _gtk_css_shorthand_property_get_subproperty (shorthand, i),
699                                                     state,
700                                                     css_value);
701
702   _gtk_css_value_unref (css_value);
703 }
704
705 static void
706 pack_border_radius (GtkCssShorthandProperty *shorthand,
707                     GValue                  *value,
708                     GtkStyleQueryFunc        query_func,
709                     gpointer                 query_data)
710 {
711   GtkCssStyleProperty *prop;
712   GtkCssValue *v;
713   int i = 0;
714
715   prop = GTK_CSS_STYLE_PROPERTY (_gtk_style_property_lookup ("border-top-left-radius"));
716   v = (* query_func) (_gtk_css_style_property_get_id (prop), query_data);
717   if (v)
718     i = _gtk_css_corner_value_get_x (v, 100);
719
720   g_value_init (value, G_TYPE_INT);
721   g_value_set_int (value, i);
722 }
723
724 static void
725 unpack_font_description (GtkCssShorthandProperty *shorthand,
726                          GtkStyleProperties      *props,
727                          GtkStateFlags            state,
728                          const GValue            *value)
729 {
730   GtkStyleProperty *prop;
731   PangoFontDescription *description;
732   PangoFontMask mask;
733   GValue v = G_VALUE_INIT;
734   
735   /* For backwards compat, we only unpack values that are indeed set.
736    * For strict CSS conformance we need to unpack all of them.
737    * Note that we do set all of them in the parse function, so it
738    * will not have effects when parsing CSS files. It will though
739    * for custom style providers.
740    */
741
742   description = g_value_get_boxed (value);
743
744   if (description)
745     mask = pango_font_description_get_set_fields (description);
746   else
747     mask = 0;
748
749   if (mask & PANGO_FONT_MASK_FAMILY)
750     {
751       GPtrArray *strv = g_ptr_array_new ();
752
753       g_ptr_array_add (strv, g_strdup (pango_font_description_get_family (description)));
754       g_ptr_array_add (strv, NULL);
755       g_value_init (&v, G_TYPE_STRV);
756       g_value_take_boxed (&v, g_ptr_array_free (strv, FALSE));
757
758       prop = _gtk_style_property_lookup ("font-family");
759       _gtk_style_property_assign (prop, props, state, &v);
760       g_value_unset (&v);
761     }
762
763   if (mask & PANGO_FONT_MASK_STYLE)
764     {
765       g_value_init (&v, PANGO_TYPE_STYLE);
766       g_value_set_enum (&v, pango_font_description_get_style (description));
767
768       prop = _gtk_style_property_lookup ("font-style");
769       _gtk_style_property_assign (prop, props, state, &v);
770       g_value_unset (&v);
771     }
772
773   if (mask & PANGO_FONT_MASK_VARIANT)
774     {
775       g_value_init (&v, PANGO_TYPE_VARIANT);
776       g_value_set_enum (&v, pango_font_description_get_variant (description));
777
778       prop = _gtk_style_property_lookup ("font-variant");
779       _gtk_style_property_assign (prop, props, state, &v);
780       g_value_unset (&v);
781     }
782
783   if (mask & PANGO_FONT_MASK_WEIGHT)
784     {
785       g_value_init (&v, PANGO_TYPE_WEIGHT);
786       g_value_set_enum (&v, pango_font_description_get_weight (description));
787
788       prop = _gtk_style_property_lookup ("font-weight");
789       _gtk_style_property_assign (prop, props, state, &v);
790       g_value_unset (&v);
791     }
792
793   if (mask & PANGO_FONT_MASK_SIZE)
794     {
795       g_value_init (&v, G_TYPE_DOUBLE);
796       g_value_set_double (&v, (double) pango_font_description_get_size (description) / PANGO_SCALE);
797
798       prop = _gtk_style_property_lookup ("font-size");
799       _gtk_style_property_assign (prop, props, state, &v);
800       g_value_unset (&v);
801     }
802 }
803
804 static void
805 pack_font_description (GtkCssShorthandProperty *shorthand,
806                        GValue                  *value,
807                        GtkStyleQueryFunc        query_func,
808                        gpointer                 query_data)
809 {
810   PangoFontDescription *description;
811   GtkCssValue *v;
812
813   description = pango_font_description_new ();
814
815   v = (* query_func) (_gtk_css_style_property_get_id (GTK_CSS_STYLE_PROPERTY (_gtk_style_property_lookup ("font-family"))), query_data);
816   if (v)
817     {
818       /* xxx: Can we set all the families here somehow? */
819       pango_font_description_set_family (description, _gtk_css_string_value_get (_gtk_css_array_value_get_nth (v, 0)));
820     }
821
822   v = (* query_func) (_gtk_css_style_property_get_id (GTK_CSS_STYLE_PROPERTY (_gtk_style_property_lookup ("font-size"))), query_data);
823   if (v)
824     pango_font_description_set_size (description, round (_gtk_css_number_value_get (v, 100) * PANGO_SCALE));
825
826   v = (* query_func) (_gtk_css_style_property_get_id (GTK_CSS_STYLE_PROPERTY (_gtk_style_property_lookup ("font-style"))), query_data);
827   if (v)
828     pango_font_description_set_style (description, _gtk_css_font_style_value_get (v));
829
830   v = (* query_func) (_gtk_css_style_property_get_id (GTK_CSS_STYLE_PROPERTY (_gtk_style_property_lookup ("font-variant"))), query_data);
831   if (v)
832     pango_font_description_set_variant (description, _gtk_css_font_variant_value_get (v));
833
834   v = (* query_func) (_gtk_css_style_property_get_id (GTK_CSS_STYLE_PROPERTY (_gtk_style_property_lookup ("font-weight"))), query_data);
835   if (v)
836     pango_font_description_set_weight (description, _gtk_css_font_weight_value_get (v));
837
838   g_value_init (value, PANGO_TYPE_FONT_DESCRIPTION);
839   g_value_take_boxed (value, description);
840 }
841
842 static void
843 unpack_to_everything (GtkCssShorthandProperty *shorthand,
844                       GtkStyleProperties      *props,
845                       GtkStateFlags            state,
846                       const GValue            *value)
847 {
848   GtkCssStyleProperty *prop;
849   guint i, n;
850   
851   n = _gtk_css_shorthand_property_get_n_subproperties (shorthand);
852
853   for (i = 0; i < n; i++)
854     {
855       prop = _gtk_css_shorthand_property_get_subproperty (shorthand, i);
856       _gtk_style_property_assign (GTK_STYLE_PROPERTY (prop), props, state, value);
857     }
858 }
859
860 static void
861 pack_first_element (GtkCssShorthandProperty *shorthand,
862                     GValue                  *value,
863                     GtkStyleQueryFunc        query_func,
864                     gpointer                 query_data)
865 {
866   GtkCssStyleProperty *prop;
867
868   /* NB: This is a fallback for properties that originally were
869    * not used as shorthand. We just pick the first subproperty
870    * as a representative.
871    * Lesson learned: Don't query the shorthand, query the 
872    * real properties instead. */
873   prop = _gtk_css_shorthand_property_get_subproperty (shorthand, 0);
874   _gtk_style_property_query (GTK_STYLE_PROPERTY (prop),
875                              value,
876                              query_func,
877                              query_data);
878 }
879
880 static void
881 _gtk_css_shorthand_property_register (const char                        *name,
882                                       GType                              value_type,
883                                       const char                       **subproperties,
884                                       GtkCssShorthandPropertyParseFunc   parse_func,
885                                       GtkCssShorthandPropertyAssignFunc  assign_func,
886                                       GtkCssShorthandPropertyQueryFunc   query_func)
887 {
888   GtkCssShorthandProperty *node;
889
890   node = g_object_new (GTK_TYPE_CSS_SHORTHAND_PROPERTY,
891                        "name", name,
892                        "value-type", value_type,
893                        "subproperties", subproperties,
894                        NULL);
895
896   node->parse = parse_func;
897   node->assign = assign_func;
898   node->query = query_func;
899 }
900
901 void
902 _gtk_css_shorthand_property_init_properties (void)
903 {
904   /* The order is important here, be careful when changing it */
905   const char *font_subproperties[] = { "font-family", "font-style", "font-variant", "font-weight", "font-size", NULL };
906   const char *margin_subproperties[] = { "margin-top", "margin-right", "margin-bottom", "margin-left", NULL };
907   const char *padding_subproperties[] = { "padding-top", "padding-right", "padding-bottom", "padding-left", NULL };
908   const char *border_width_subproperties[] = { "border-top-width", "border-right-width", "border-bottom-width", "border-left-width", NULL };
909   const char *border_radius_subproperties[] = { "border-top-left-radius", "border-top-right-radius",
910                                                 "border-bottom-right-radius", "border-bottom-left-radius", NULL };
911   const char *border_color_subproperties[] = { "border-top-color", "border-right-color", "border-bottom-color", "border-left-color", NULL };
912   const char *border_style_subproperties[] = { "border-top-style", "border-right-style", "border-bottom-style", "border-left-style", NULL };
913   const char *border_image_subproperties[] = { "border-image-source", "border-image-slice", "border-image-width", "border-image-repeat", NULL };
914   const char *border_top_subproperties[] = { "border-top-width", "border-top-style", "border-top-color", NULL };
915   const char *border_right_subproperties[] = { "border-right-width", "border-right-style", "border-right-color", NULL };
916   const char *border_bottom_subproperties[] = { "border-bottom-width", "border-bottom-style", "border-bottom-color", NULL };
917   const char *border_left_subproperties[] = { "border-left-width", "border-left-style", "border-left-color", NULL };
918   const char *border_subproperties[] = { "border-top-width", "border-right-width", "border-bottom-width", "border-left-width",
919                                          "border-top-style", "border-right-style", "border-bottom-style", "border-left-style",
920                                          "border-top-color", "border-right-color", "border-bottom-color", "border-left-color",
921                                          "border-image-source", "border-image-slice", "border-image-width", "border-image-repeat", NULL };
922   const char *outline_subproperties[] = { "outline-width", "outline-style", "outline-color", NULL };
923   const char *background_subproperties[] = { "background-image", "background-repeat", "background-clip", "background-origin",
924                                              "background-color", NULL };
925   const char *transition_subproperties[] = { "transition-property", "transition-duration", "transition-delay", "transition-timing-function", NULL };
926
927   _gtk_css_shorthand_property_register   ("font",
928                                           PANGO_TYPE_FONT_DESCRIPTION,
929                                           font_subproperties,
930                                           parse_font,
931                                           unpack_font_description,
932                                           pack_font_description);
933   _gtk_css_shorthand_property_register   ("margin",
934                                           GTK_TYPE_BORDER,
935                                           margin_subproperties,
936                                           parse_margin,
937                                           unpack_border,
938                                           pack_border);
939   _gtk_css_shorthand_property_register   ("padding",
940                                           GTK_TYPE_BORDER,
941                                           padding_subproperties,
942                                           parse_padding,
943                                           unpack_border,
944                                           pack_border);
945   _gtk_css_shorthand_property_register   ("border-width",
946                                           GTK_TYPE_BORDER,
947                                           border_width_subproperties,
948                                           parse_border_width,
949                                           unpack_border,
950                                           pack_border);
951   _gtk_css_shorthand_property_register   ("border-radius",
952                                           G_TYPE_INT,
953                                           border_radius_subproperties,
954                                           parse_border_radius,
955                                           unpack_border_radius,
956                                           pack_border_radius);
957   _gtk_css_shorthand_property_register   ("border-color",
958                                           GDK_TYPE_RGBA,
959                                           border_color_subproperties,
960                                           parse_border_color,
961                                           unpack_to_everything,
962                                           pack_first_element);
963   _gtk_css_shorthand_property_register   ("border-style",
964                                           GTK_TYPE_BORDER_STYLE,
965                                           border_style_subproperties,
966                                           parse_border_style,
967                                           unpack_to_everything,
968                                           pack_first_element);
969   _gtk_css_shorthand_property_register   ("border-image",
970                                           G_TYPE_NONE,
971                                           border_image_subproperties,
972                                           parse_border_image,
973                                           NULL,
974                                           NULL);
975   _gtk_css_shorthand_property_register   ("border-top",
976                                           G_TYPE_NONE,
977                                           border_top_subproperties,
978                                           parse_border_side,
979                                           NULL,
980                                           NULL);
981   _gtk_css_shorthand_property_register   ("border-right",
982                                           G_TYPE_NONE,
983                                           border_right_subproperties,
984                                           parse_border_side,
985                                           NULL,
986                                           NULL);
987   _gtk_css_shorthand_property_register   ("border-bottom",
988                                           G_TYPE_NONE,
989                                           border_bottom_subproperties,
990                                           parse_border_side,
991                                           NULL,
992                                           NULL);
993   _gtk_css_shorthand_property_register   ("border-left",
994                                           G_TYPE_NONE,
995                                           border_left_subproperties,
996                                           parse_border_side,
997                                           NULL,
998                                           NULL);
999   _gtk_css_shorthand_property_register   ("border",
1000                                           G_TYPE_NONE,
1001                                           border_subproperties,
1002                                           parse_border,
1003                                           NULL,
1004                                           NULL);
1005   _gtk_css_shorthand_property_register   ("outline",
1006                                           G_TYPE_NONE,
1007                                           outline_subproperties,
1008                                           parse_border_side,
1009                                           NULL,
1010                                           NULL);
1011   _gtk_css_shorthand_property_register   ("background",
1012                                           G_TYPE_NONE,
1013                                           background_subproperties,
1014                                           parse_background,
1015                                           NULL,
1016                                           NULL);
1017   _gtk_css_shorthand_property_register   ("transition",
1018                                           G_TYPE_NONE,
1019                                           transition_subproperties,
1020                                           parse_transition,
1021                                           NULL,
1022                                           NULL);
1023 }