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