]> Pileus Git - ~andy/gtk/blob - gtk/gtkcssshorthandpropertyimpl.c
styleproperty: Add custom registration func for shorthands
[~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, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  *
18  * Authors: Benjamin Otte <otte@gnome.org>
19  */
20
21 #include "config.h"
22
23 #include "gtkcssshorthandpropertyprivate.h"
24
25 #include <cairo-gobject.h>
26 #include <math.h>
27
28 #include "gtkborderimageprivate.h"
29 #include "gtkcsstypesprivate.h"
30
31 /* this is in case round() is not provided by the compiler, 
32  * such as in the case of C89 compilers, like MSVC
33  */
34 #include "fallback-c89.c"
35
36 /*** PARSING ***/
37
38 static gboolean
39 border_image_value_parse (GtkCssParser *parser,
40                           GFile *base,
41                           GValue *value)
42 {
43   GValue temp = G_VALUE_INIT;
44   cairo_pattern_t *pattern = NULL;
45   gconstpointer *boxed = NULL;
46   GType boxed_type;
47   GtkBorder slice, *width = NULL, *parsed_slice;
48   GtkCssBorderImageRepeat repeat, *parsed_repeat;
49   gboolean retval = FALSE;
50   GtkBorderImage *image = NULL;
51
52   if (_gtk_css_parser_try (parser, "none", TRUE))
53     return TRUE;
54
55   g_value_init (&temp, CAIRO_GOBJECT_TYPE_PATTERN);
56
57   if (!_gtk_style_property_parse_value (NULL, &temp, parser, base))
58     return FALSE;
59
60   boxed_type = G_VALUE_TYPE (&temp);
61   if (boxed_type != CAIRO_GOBJECT_TYPE_PATTERN)
62     boxed = g_value_dup_boxed (&temp);
63   else
64     pattern = g_value_dup_boxed (&temp);
65
66   g_value_unset (&temp);
67   g_value_init (&temp, GTK_TYPE_BORDER);
68
69   if (!_gtk_style_property_parse_value (NULL, &temp, parser, base))
70     goto out;
71
72   parsed_slice = g_value_get_boxed (&temp);
73   slice = *parsed_slice;
74
75   if (_gtk_css_parser_try (parser, "/", TRUE))
76     {
77       g_value_unset (&temp);
78       g_value_init (&temp, GTK_TYPE_BORDER);
79
80       if (!_gtk_style_property_parse_value (NULL, &temp, parser, base))
81         goto out;
82
83       width = g_value_dup_boxed (&temp);
84     }
85
86   g_value_unset (&temp);
87   g_value_init (&temp, GTK_TYPE_CSS_BORDER_IMAGE_REPEAT);
88
89   if (!_gtk_style_property_parse_value (NULL, &temp, parser, base))
90     goto out;
91
92   parsed_repeat = g_value_get_boxed (&temp);
93   repeat = *parsed_repeat;
94
95   g_value_unset (&temp);
96
97   if (boxed != NULL)
98     image = _gtk_border_image_new_for_boxed (boxed_type, boxed, &slice, width, &repeat);
99   else if (pattern != NULL)
100     image = _gtk_border_image_new (pattern, &slice, width, &repeat);
101
102   if (image != NULL)
103     {
104       retval = TRUE;
105       g_value_take_boxed (value, image);
106     }
107
108  out:
109   if (pattern != NULL)
110     cairo_pattern_destroy (pattern);
111
112   if (boxed != NULL)
113     g_boxed_free (boxed_type, boxed);
114
115   if (width != NULL)
116     gtk_border_free (width);
117
118   return retval;
119 }
120
121 static gboolean 
122 border_radius_value_parse (GtkCssParser *parser,
123                            GFile        *base,
124                            GValue       *value)
125 {
126   GtkCssBorderRadius border;
127
128   if (!_gtk_css_parser_try_double (parser, &border.top_left.horizontal))
129     {
130       _gtk_css_parser_error (parser, "Expected a number");
131       return FALSE;
132     }
133   else if (border.top_left.horizontal < 0)
134     goto negative;
135
136   if (_gtk_css_parser_try_double (parser, &border.top_right.horizontal))
137     {
138       if (border.top_right.horizontal < 0)
139         goto negative;
140       if (_gtk_css_parser_try_double (parser, &border.bottom_right.horizontal))
141         {
142           if (border.bottom_right.horizontal < 0)
143             goto negative;
144           if (!_gtk_css_parser_try_double (parser, &border.bottom_left.horizontal))
145             border.bottom_left.horizontal = border.top_right.horizontal;
146           else if (border.bottom_left.horizontal < 0)
147             goto negative;
148         }
149       else
150         {
151           border.bottom_right.horizontal = border.top_left.horizontal;
152           border.bottom_left.horizontal = border.top_right.horizontal;
153         }
154     }
155   else
156     {
157       border.top_right.horizontal = border.top_left.horizontal;
158       border.bottom_right.horizontal = border.top_left.horizontal;
159       border.bottom_left.horizontal = border.top_left.horizontal;
160     }
161
162   if (_gtk_css_parser_try (parser, "/", TRUE))
163     {
164       if (!_gtk_css_parser_try_double (parser, &border.top_left.vertical))
165         {
166           _gtk_css_parser_error (parser, "Expected a number");
167           return FALSE;
168         }
169       else if (border.top_left.vertical < 0)
170         goto negative;
171
172       if (_gtk_css_parser_try_double (parser, &border.top_right.vertical))
173         {
174           if (border.top_right.vertical < 0)
175             goto negative;
176           if (_gtk_css_parser_try_double (parser, &border.bottom_right.vertical))
177             {
178               if (border.bottom_right.vertical < 0)
179                 goto negative;
180               if (!_gtk_css_parser_try_double (parser, &border.bottom_left.vertical))
181                 border.bottom_left.vertical = border.top_right.vertical;
182               else if (border.bottom_left.vertical < 0)
183                 goto negative;
184             }
185           else
186             {
187               border.bottom_right.vertical = border.top_left.vertical;
188               border.bottom_left.vertical = border.top_right.vertical;
189             }
190         }
191       else
192         {
193           border.top_right.vertical = border.top_left.vertical;
194           border.bottom_right.vertical = border.top_left.vertical;
195           border.bottom_left.vertical = border.top_left.vertical;
196         }
197     }
198   else
199     {
200       border.top_left.vertical = border.top_left.horizontal;
201       border.top_right.vertical = border.top_right.horizontal;
202       border.bottom_right.vertical = border.bottom_right.horizontal;
203       border.bottom_left.vertical = border.bottom_left.horizontal;
204     }
205
206   /* border-radius is an int property for backwards-compat reasons */
207   g_value_unset (value);
208   g_value_init (value, GTK_TYPE_CSS_BORDER_RADIUS);
209   g_value_set_boxed (value, &border);
210
211   return TRUE;
212
213 negative:
214   _gtk_css_parser_error (parser, "Border radius values cannot be negative");
215   return FALSE;
216 }
217
218 static gboolean 
219 border_color_shorthand_value_parse (GtkCssParser *parser,
220                                     GFile        *base,
221                                     GValue       *value)
222 {
223   GtkSymbolicColor *symbolic;
224   GPtrArray *array;
225
226   array = g_ptr_array_new_with_free_func ((GDestroyNotify) gtk_symbolic_color_unref);
227
228   do
229     {
230       if (_gtk_css_parser_try (parser, "transparent", TRUE))
231         {
232           GdkRGBA transparent = { 0, 0, 0, 0 };
233           
234           symbolic = gtk_symbolic_color_new_literal (&transparent);
235         }
236       else
237         {
238           symbolic = _gtk_css_parser_read_symbolic_color (parser);
239       
240           if (symbolic == NULL)
241             return FALSE;
242         }
243       
244       g_ptr_array_add (array, symbolic);
245     }
246   while (array->len < 4 && 
247          !_gtk_css_parser_is_eof (parser) &&
248          !_gtk_css_parser_begins_with (parser, ';') &&
249          !_gtk_css_parser_begins_with (parser, '}'));
250
251   switch (array->len)
252     {
253       default:
254         g_assert_not_reached ();
255         break;
256       case 1:
257         g_ptr_array_add (array, gtk_symbolic_color_ref (g_ptr_array_index (array, 0)));
258         /* fall through */
259       case 2:
260         g_ptr_array_add (array, gtk_symbolic_color_ref (g_ptr_array_index (array, 0)));
261         /* fall through */
262       case 3:
263         g_ptr_array_add (array, gtk_symbolic_color_ref (g_ptr_array_index (array, 1)));
264         /* fall through */
265       case 4:
266         break;
267     }
268
269   g_value_unset (value);
270   g_value_init (value, G_TYPE_PTR_ARRAY);
271   g_value_take_boxed (value, array);
272
273   return TRUE;
274 }
275
276 /*** PRINTING ***/
277
278 static void
279 string_append_double (GString *string,
280                       double   d)
281 {
282   char buf[G_ASCII_DTOSTR_BUF_SIZE];
283
284   g_ascii_dtostr (buf, sizeof (buf), d);
285   g_string_append (string, buf);
286 }
287
288 static void
289 border_radius_value_print (const GValue *value,
290                            GString      *string)
291 {
292   GtkCssBorderRadius *border;
293
294   border = g_value_get_boxed (value);
295
296   if (border == NULL)
297     {
298       g_string_append (string, "none");
299       return;
300     }
301
302   string_append_double (string, border->top_left.horizontal);
303   if (border->top_left.horizontal != border->top_right.horizontal ||
304       border->top_left.horizontal != border->bottom_right.horizontal ||
305       border->top_left.horizontal != border->bottom_left.horizontal)
306     {
307       g_string_append_c (string, ' ');
308       string_append_double (string, border->top_right.horizontal);
309       if (border->top_left.horizontal != border->bottom_right.horizontal ||
310           border->top_right.horizontal != border->bottom_left.horizontal)
311         {
312           g_string_append_c (string, ' ');
313           string_append_double (string, border->bottom_right.horizontal);
314           if (border->top_right.horizontal != border->bottom_left.horizontal)
315             {
316               g_string_append_c (string, ' ');
317               string_append_double (string, border->bottom_left.horizontal);
318             }
319         }
320     }
321
322   if (border->top_left.horizontal != border->top_left.vertical ||
323       border->top_right.horizontal != border->top_right.vertical ||
324       border->bottom_right.horizontal != border->bottom_right.vertical ||
325       border->bottom_left.horizontal != border->bottom_left.vertical)
326     {
327       g_string_append (string, " / ");
328       string_append_double (string, border->top_left.vertical);
329       if (border->top_left.vertical != border->top_right.vertical ||
330           border->top_left.vertical != border->bottom_right.vertical ||
331           border->top_left.vertical != border->bottom_left.vertical)
332         {
333           g_string_append_c (string, ' ');
334           string_append_double (string, border->top_right.vertical);
335           if (border->top_left.vertical != border->bottom_right.vertical ||
336               border->top_right.vertical != border->bottom_left.vertical)
337             {
338               g_string_append_c (string, ' ');
339               string_append_double (string, border->bottom_right.vertical);
340               if (border->top_right.vertical != border->bottom_left.vertical)
341                 {
342                   g_string_append_c (string, ' ');
343                   string_append_double (string, border->bottom_left.vertical);
344                 }
345             }
346         }
347
348     }
349 }
350
351 /*** PACKING ***/
352
353 static GParameter *
354 unpack_border (const GValue *value,
355                guint        *n_params,
356                const char   *top,
357                const char   *left,
358                const char   *bottom,
359                const char   *right)
360 {
361   GParameter *parameter = g_new0 (GParameter, 4);
362   GtkBorder *border = g_value_get_boxed (value);
363
364   parameter[0].name = top;
365   g_value_init (&parameter[0].value, G_TYPE_INT);
366   g_value_set_int (&parameter[0].value, border->top);
367   parameter[1].name = left;
368   g_value_init (&parameter[1].value, G_TYPE_INT);
369   g_value_set_int (&parameter[1].value, border->left);
370   parameter[2].name = bottom;
371   g_value_init (&parameter[2].value, G_TYPE_INT);
372   g_value_set_int (&parameter[2].value, border->bottom);
373   parameter[3].name = right;
374   g_value_init (&parameter[3].value, G_TYPE_INT);
375   g_value_set_int (&parameter[3].value, border->right);
376
377   *n_params = 4;
378   return parameter;
379 }
380
381 static void
382 pack_border (GValue             *value,
383              GtkStyleProperties *props,
384              GtkStateFlags       state,
385              const char         *top,
386              const char         *left,
387              const char         *bottom,
388              const char         *right)
389 {
390   GtkBorder border;
391   int t, l, b, r;
392
393   gtk_style_properties_get (props,
394                             state,
395                             top, &t,
396                             left, &l,
397                             bottom, &b,
398                             right, &r,
399                             NULL);
400
401   border.top = t;
402   border.left = l;
403   border.bottom = b;
404   border.right = r;
405
406   g_value_set_boxed (value, &border);
407 }
408
409 static GParameter *
410 unpack_border_width (const GValue *value,
411                      guint        *n_params)
412 {
413   return unpack_border (value, n_params,
414                         "border-top-width", "border-left-width",
415                         "border-bottom-width", "border-right-width");
416 }
417
418 static void
419 pack_border_width (GValue             *value,
420                    GtkStyleProperties *props,
421                    GtkStateFlags       state,
422                    GtkStylePropertyContext *context)
423 {
424   pack_border (value, props, state,
425                "border-top-width", "border-left-width",
426                "border-bottom-width", "border-right-width");
427 }
428
429 static GParameter *
430 unpack_padding (const GValue *value,
431                 guint        *n_params)
432 {
433   return unpack_border (value, n_params,
434                         "padding-top", "padding-left",
435                         "padding-bottom", "padding-right");
436 }
437
438 static void
439 pack_padding (GValue             *value,
440               GtkStyleProperties *props,
441               GtkStateFlags       state,
442               GtkStylePropertyContext *context)
443 {
444   pack_border (value, props, state,
445                "padding-top", "padding-left",
446                "padding-bottom", "padding-right");
447 }
448
449 static GParameter *
450 unpack_margin (const GValue *value,
451                guint        *n_params)
452 {
453   return unpack_border (value, n_params,
454                         "margin-top", "margin-left",
455                         "margin-bottom", "margin-right");
456 }
457
458 static void
459 pack_margin (GValue             *value,
460              GtkStyleProperties *props,
461              GtkStateFlags       state,
462              GtkStylePropertyContext *context)
463 {
464   pack_border (value, props, state,
465                "margin-top", "margin-left",
466                "margin-bottom", "margin-right");
467 }
468
469 static GParameter *
470 unpack_border_radius (const GValue *value,
471                       guint        *n_params)
472 {
473   GParameter *parameter = g_new0 (GParameter, 4);
474   GtkCssBorderRadius *border;
475   
476   if (G_VALUE_HOLDS_BOXED (value))
477     border = g_value_get_boxed (value);
478   else
479     border = NULL;
480
481   parameter[0].name = "border-top-left-radius";
482   g_value_init (&parameter[0].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
483   parameter[1].name = "border-top-right-radius";
484   g_value_init (&parameter[1].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
485   parameter[2].name = "border-bottom-right-radius";
486   g_value_init (&parameter[2].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
487   parameter[3].name = "border-bottom-left-radius";
488   g_value_init (&parameter[3].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
489   if (border)
490     {
491       g_value_set_boxed (&parameter[0].value, &border->top_left);
492       g_value_set_boxed (&parameter[1].value, &border->top_right);
493       g_value_set_boxed (&parameter[2].value, &border->bottom_right);
494       g_value_set_boxed (&parameter[3].value, &border->bottom_left);
495     }
496
497   *n_params = 4;
498   return parameter;
499 }
500
501 static void
502 pack_border_radius (GValue             *value,
503                     GtkStyleProperties *props,
504                     GtkStateFlags       state,
505                     GtkStylePropertyContext *context)
506 {
507   GtkCssBorderCornerRadius *top_left;
508
509   /* NB: We are an int property, so we have to resolve to an int here.
510    * So we just resolve to an int. We pick one and stick to it.
511    * Lesson learned: Don't query border-radius shorthand, query the 
512    * real properties instead. */
513   gtk_style_properties_get (props,
514                             state,
515                             "border-top-left-radius", &top_left,
516                             NULL);
517
518   if (top_left)
519     g_value_set_int (value, top_left->horizontal);
520
521   g_free (top_left);
522 }
523
524 static GParameter *
525 unpack_font_description (const GValue *value,
526                          guint        *n_params)
527 {
528   GParameter *parameter = g_new0 (GParameter, 5);
529   PangoFontDescription *description;
530   PangoFontMask mask;
531   guint n;
532   
533   /* For backwards compat, we only unpack values that are indeed set.
534    * For strict CSS conformance we need to unpack all of them.
535    * Note that we do set all of them in the parse function, so it
536    * will not have effects when parsing CSS files. It will though
537    * for custom style providers.
538    */
539
540   description = g_value_get_boxed (value);
541   n = 0;
542
543   if (description)
544     mask = pango_font_description_get_set_fields (description);
545   else
546     mask = 0;
547
548   if (mask & PANGO_FONT_MASK_FAMILY)
549     {
550       GPtrArray *strv = g_ptr_array_new ();
551
552       g_ptr_array_add (strv, g_strdup (pango_font_description_get_family (description)));
553       g_ptr_array_add (strv, NULL);
554       parameter[n].name = "font-family";
555       g_value_init (&parameter[n].value, G_TYPE_STRV);
556       g_value_take_boxed (&parameter[n].value,
557                           g_ptr_array_free (strv, FALSE));
558       n++;
559     }
560
561   if (mask & PANGO_FONT_MASK_STYLE)
562     {
563       parameter[n].name = "font-style";
564       g_value_init (&parameter[n].value, PANGO_TYPE_STYLE);
565       g_value_set_enum (&parameter[n].value,
566                         pango_font_description_get_style (description));
567       n++;
568     }
569
570   if (mask & PANGO_FONT_MASK_VARIANT)
571     {
572       parameter[n].name = "font-variant";
573       g_value_init (&parameter[n].value, PANGO_TYPE_VARIANT);
574       g_value_set_enum (&parameter[n].value,
575                         pango_font_description_get_variant (description));
576       n++;
577     }
578
579   if (mask & PANGO_FONT_MASK_WEIGHT)
580     {
581       parameter[n].name = "font-weight";
582       g_value_init (&parameter[n].value, PANGO_TYPE_WEIGHT);
583       g_value_set_enum (&parameter[n].value,
584                         pango_font_description_get_weight (description));
585       n++;
586     }
587
588   if (mask & PANGO_FONT_MASK_SIZE)
589     {
590       parameter[n].name = "font-size";
591       g_value_init (&parameter[n].value, G_TYPE_DOUBLE);
592       g_value_set_double (&parameter[n].value,
593                           (double) pango_font_description_get_size (description) / PANGO_SCALE);
594       n++;
595     }
596
597   *n_params = n;
598
599   return parameter;
600 }
601
602 static void
603 pack_font_description (GValue             *value,
604                        GtkStyleProperties *props,
605                        GtkStateFlags       state,
606                        GtkStylePropertyContext *context)
607 {
608   PangoFontDescription *description;
609   char **families;
610   PangoStyle style;
611   PangoVariant variant;
612   PangoWeight weight;
613   double size;
614
615   gtk_style_properties_get (props,
616                             state,
617                             "font-family", &families,
618                             "font-style", &style,
619                             "font-variant", &variant,
620                             "font-weight", &weight,
621                             "font-size", &size,
622                             NULL);
623
624   description = pango_font_description_new ();
625   /* xxx: Can we set all the families here somehow? */
626   if (families)
627     pango_font_description_set_family (description, families[0]);
628   pango_font_description_set_size (description, round (size * PANGO_SCALE));
629   pango_font_description_set_style (description, style);
630   pango_font_description_set_variant (description, variant);
631   pango_font_description_set_weight (description, weight);
632
633   g_strfreev (families);
634
635   g_value_take_boxed (value, description);
636 }
637
638 static GParameter *
639 unpack_border_color (const GValue *value,
640                      guint        *n_params)
641 {
642   GParameter *parameter = g_new0 (GParameter, 4);
643   GType type;
644   
645   type = G_VALUE_TYPE (value);
646   if (type == G_TYPE_PTR_ARRAY)
647     type = GTK_TYPE_SYMBOLIC_COLOR;
648
649   parameter[0].name = "border-top-color";
650   g_value_init (&parameter[0].value, type);
651   parameter[1].name = "border-right-color";
652   g_value_init (&parameter[1].value, type);
653   parameter[2].name = "border-bottom-color";
654   g_value_init (&parameter[2].value, type);
655   parameter[3].name = "border-left-color";
656   g_value_init (&parameter[3].value, type);
657
658   if (G_VALUE_TYPE (value) == G_TYPE_PTR_ARRAY)
659     {
660       GPtrArray *array = g_value_get_boxed (value);
661       guint i;
662
663       for (i = 0; i < 4; i++)
664         g_value_set_boxed (&parameter[i].value, g_ptr_array_index (array, i));
665     }
666   else
667     {
668       /* can be RGBA or symbolic color */
669       gpointer p = g_value_get_boxed (value);
670
671       g_value_set_boxed (&parameter[0].value, p);
672       g_value_set_boxed (&parameter[1].value, p);
673       g_value_set_boxed (&parameter[2].value, p);
674       g_value_set_boxed (&parameter[3].value, p);
675     }
676
677   *n_params = 4;
678   return parameter;
679 }
680
681 static void
682 pack_border_color (GValue             *value,
683                    GtkStyleProperties *props,
684                    GtkStateFlags       state,
685                    GtkStylePropertyContext *context)
686 {
687   /* NB: We are a color property, so we have to resolve to a color here.
688    * So we just resolve to a color. We pick one and stick to it.
689    * Lesson learned: Don't query border-color shorthand, query the 
690    * real properties instead. */
691   g_value_unset (value);
692   gtk_style_properties_get_property (props, "border-top-color", state, value);
693 }
694
695 /*** UNSET FUNCS ***/
696
697 static void
698 unset_font_description (GtkStyleProperties *props,
699                         GtkStateFlags       state)
700 {
701   gtk_style_properties_unset_property (props, "font-family", state);
702   gtk_style_properties_unset_property (props, "font-style", state);
703   gtk_style_properties_unset_property (props, "font-variant", state);
704   gtk_style_properties_unset_property (props, "font-weight", state);
705   gtk_style_properties_unset_property (props, "font-size", state);
706 }
707
708 static void
709 unset_margin (GtkStyleProperties *props,
710               GtkStateFlags       state)
711 {
712   gtk_style_properties_unset_property (props, "margin-top", state);
713   gtk_style_properties_unset_property (props, "margin-right", state);
714   gtk_style_properties_unset_property (props, "margin-bottom", state);
715   gtk_style_properties_unset_property (props, "margin-left", state);
716 }
717
718 static void
719 unset_padding (GtkStyleProperties *props,
720                GtkStateFlags       state)
721 {
722   gtk_style_properties_unset_property (props, "padding-top", state);
723   gtk_style_properties_unset_property (props, "padding-right", state);
724   gtk_style_properties_unset_property (props, "padding-bottom", state);
725   gtk_style_properties_unset_property (props, "padding-left", state);
726 }
727
728 static void
729 unset_border_width (GtkStyleProperties *props,
730                     GtkStateFlags       state)
731 {
732   gtk_style_properties_unset_property (props, "border-top-width", state);
733   gtk_style_properties_unset_property (props, "border-right-width", state);
734   gtk_style_properties_unset_property (props, "border-bottom-width", state);
735   gtk_style_properties_unset_property (props, "border-left-width", state);
736 }
737
738 static void
739 unset_border_radius (GtkStyleProperties *props,
740                      GtkStateFlags       state)
741 {
742   gtk_style_properties_unset_property (props, "border-top-right-radius", state);
743   gtk_style_properties_unset_property (props, "border-bottom-right-radius", state);
744   gtk_style_properties_unset_property (props, "border-bottom-left-radius", state);
745   gtk_style_properties_unset_property (props, "border-top-left-radius", state);
746 }
747
748 static void
749 unset_border_color (GtkStyleProperties *props,
750                     GtkStateFlags       state)
751 {
752   gtk_style_properties_unset_property (props, "border-top-color", state);
753   gtk_style_properties_unset_property (props, "border-right-color", state);
754   gtk_style_properties_unset_property (props, "border-bottom-color", state);
755   gtk_style_properties_unset_property (props, "border-left-color", state);
756 }
757
758 static void
759 unset_border_image (GtkStyleProperties *props,
760                     GtkStateFlags       state)
761 {
762   gtk_style_properties_unset_property (props, "border-image-source", state);
763   gtk_style_properties_unset_property (props, "border-image-slice", state);
764   gtk_style_properties_unset_property (props, "border-image-repeat", state);
765   gtk_style_properties_unset_property (props, "border-image-width", state);
766 }
767
768 static void
769 _gtk_css_shorthand_property_register (GParamSpec               *pspec,
770                                       GtkStylePropertyFlags     flags,
771                                       GtkStylePropertyParser    property_parse_func,
772                                       GtkStyleUnpackFunc        unpack_func,
773                                       GtkStylePackFunc          pack_func,
774                                       GtkStyleParseFunc         parse_func,
775                                       GtkStylePrintFunc         print_func,
776                                       const GValue *            initial_value,
777                                       GtkStyleUnsetFunc         unset_func)
778 {
779   GtkStyleProperty *node;
780
781   g_return_if_fail (pack_func != NULL);
782   g_return_if_fail (unpack_func != NULL);
783
784   node = g_object_new (GTK_TYPE_CSS_SHORTHAND_PROPERTY,
785                        "name", pspec->name,
786                        NULL);
787
788   node->flags = flags;
789   node->pspec = pspec;
790   node->property_parse_func = property_parse_func;
791   node->pack_func = pack_func;
792   node->unpack_func = unpack_func;
793   node->parse_func = parse_func;
794   node->print_func = print_func;
795   node->unset_func = unset_func;
796 }
797
798 void
799 _gtk_css_shorthand_property_init_properties (void)
800 {
801   _gtk_css_shorthand_property_register   (g_param_spec_boxed ("font",
802                                                               "Font Description",
803                                                               "Font Description",
804                                                               PANGO_TYPE_FONT_DESCRIPTION, 0),
805                                           GTK_STYLE_PROPERTY_INHERIT,
806                                           NULL,
807                                           unpack_font_description,
808                                           pack_font_description,
809                                           NULL,
810                                           NULL,
811                                           NULL,
812                                           unset_font_description);
813   _gtk_css_shorthand_property_register   (g_param_spec_boxed ("margin",
814                                                               "Margin",
815                                                               "Margin",
816                                                               GTK_TYPE_BORDER, 0),
817                                           0,
818                                           NULL,
819                                           unpack_margin,
820                                           pack_margin,
821                                           NULL,
822                                           NULL,
823                                           NULL,
824                                           unset_margin);
825   _gtk_css_shorthand_property_register   (g_param_spec_boxed ("padding",
826                                                               "Padding",
827                                                               "Padding",
828                                                               GTK_TYPE_BORDER, 0),
829                                           0,
830                                           NULL,
831                                           unpack_padding,
832                                           pack_padding,
833                                           NULL,
834                                           NULL,
835                                           NULL,
836                                           unset_padding);
837   _gtk_css_shorthand_property_register   (g_param_spec_boxed ("border-width",
838                                                               "Border width",
839                                                               "Border width, in pixels",
840                                                               GTK_TYPE_BORDER, 0),
841                                           0,
842                                           NULL,
843                                           unpack_border_width,
844                                           pack_border_width,
845                                           NULL,
846                                           NULL,
847                                           NULL,
848                                           unset_border_width);
849   _gtk_css_shorthand_property_register   (g_param_spec_int ("border-radius",
850                                                             "Border radius",
851                                                             "Border radius, in pixels",
852                                                             0, G_MAXINT, 0, 0),
853                                           0,
854                                           NULL,
855                                           unpack_border_radius,
856                                           pack_border_radius,
857                                           border_radius_value_parse,
858                                           border_radius_value_print,
859                                           NULL,
860                                           unset_border_radius);
861   _gtk_css_shorthand_property_register   (g_param_spec_boxed ("border-color",
862                                                               "Border color",
863                                                               "Border color",
864                                                               GDK_TYPE_RGBA, 0),
865                                           0,
866                                           NULL,
867                                           unpack_border_color,
868                                           pack_border_color,
869                                           border_color_shorthand_value_parse,
870                                           NULL,
871                                           NULL,
872                                           unset_border_color);
873   _gtk_css_shorthand_property_register   (g_param_spec_boxed ("border-image",
874                                                               "Border Image",
875                                                               "Border Image",
876                                                               GTK_TYPE_BORDER_IMAGE, 0),
877                                           0,
878                                           NULL,
879                                           _gtk_border_image_unpack,
880                                           _gtk_border_image_pack,
881                                           border_image_value_parse,
882                                           NULL,
883                                           NULL,
884                                           unset_border_image);
885 }