]> Pileus Git - ~andy/gtk/blob - gtk/gtkcssshorthandpropertyimpl.c
shorthand: Remove a bunch of unused functionality
[~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 /*** PACKING ***/
277
278 static GParameter *
279 unpack_border (const GValue *value,
280                guint        *n_params,
281                const char   *top,
282                const char   *left,
283                const char   *bottom,
284                const char   *right)
285 {
286   GParameter *parameter = g_new0 (GParameter, 4);
287   GtkBorder *border = g_value_get_boxed (value);
288
289   parameter[0].name = top;
290   g_value_init (&parameter[0].value, G_TYPE_INT);
291   g_value_set_int (&parameter[0].value, border->top);
292   parameter[1].name = left;
293   g_value_init (&parameter[1].value, G_TYPE_INT);
294   g_value_set_int (&parameter[1].value, border->left);
295   parameter[2].name = bottom;
296   g_value_init (&parameter[2].value, G_TYPE_INT);
297   g_value_set_int (&parameter[2].value, border->bottom);
298   parameter[3].name = right;
299   g_value_init (&parameter[3].value, G_TYPE_INT);
300   g_value_set_int (&parameter[3].value, border->right);
301
302   *n_params = 4;
303   return parameter;
304 }
305
306 static void
307 pack_border (GValue             *value,
308              GtkStyleProperties *props,
309              GtkStateFlags       state,
310              const char         *top,
311              const char         *left,
312              const char         *bottom,
313              const char         *right)
314 {
315   GtkBorder border;
316   int t, l, b, r;
317
318   gtk_style_properties_get (props,
319                             state,
320                             top, &t,
321                             left, &l,
322                             bottom, &b,
323                             right, &r,
324                             NULL);
325
326   border.top = t;
327   border.left = l;
328   border.bottom = b;
329   border.right = r;
330
331   g_value_set_boxed (value, &border);
332 }
333
334 static GParameter *
335 unpack_border_width (const GValue *value,
336                      guint        *n_params)
337 {
338   return unpack_border (value, n_params,
339                         "border-top-width", "border-left-width",
340                         "border-bottom-width", "border-right-width");
341 }
342
343 static void
344 pack_border_width (GValue             *value,
345                    GtkStyleProperties *props,
346                    GtkStateFlags       state,
347                    GtkStylePropertyContext *context)
348 {
349   pack_border (value, props, state,
350                "border-top-width", "border-left-width",
351                "border-bottom-width", "border-right-width");
352 }
353
354 static GParameter *
355 unpack_padding (const GValue *value,
356                 guint        *n_params)
357 {
358   return unpack_border (value, n_params,
359                         "padding-top", "padding-left",
360                         "padding-bottom", "padding-right");
361 }
362
363 static void
364 pack_padding (GValue             *value,
365               GtkStyleProperties *props,
366               GtkStateFlags       state,
367               GtkStylePropertyContext *context)
368 {
369   pack_border (value, props, state,
370                "padding-top", "padding-left",
371                "padding-bottom", "padding-right");
372 }
373
374 static GParameter *
375 unpack_margin (const GValue *value,
376                guint        *n_params)
377 {
378   return unpack_border (value, n_params,
379                         "margin-top", "margin-left",
380                         "margin-bottom", "margin-right");
381 }
382
383 static void
384 pack_margin (GValue             *value,
385              GtkStyleProperties *props,
386              GtkStateFlags       state,
387              GtkStylePropertyContext *context)
388 {
389   pack_border (value, props, state,
390                "margin-top", "margin-left",
391                "margin-bottom", "margin-right");
392 }
393
394 static GParameter *
395 unpack_border_radius (const GValue *value,
396                       guint        *n_params)
397 {
398   GParameter *parameter = g_new0 (GParameter, 4);
399   GtkCssBorderRadius *border;
400   
401   if (G_VALUE_HOLDS_BOXED (value))
402     border = g_value_get_boxed (value);
403   else
404     border = NULL;
405
406   parameter[0].name = "border-top-left-radius";
407   g_value_init (&parameter[0].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
408   parameter[1].name = "border-top-right-radius";
409   g_value_init (&parameter[1].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
410   parameter[2].name = "border-bottom-right-radius";
411   g_value_init (&parameter[2].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
412   parameter[3].name = "border-bottom-left-radius";
413   g_value_init (&parameter[3].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
414   if (border)
415     {
416       g_value_set_boxed (&parameter[0].value, &border->top_left);
417       g_value_set_boxed (&parameter[1].value, &border->top_right);
418       g_value_set_boxed (&parameter[2].value, &border->bottom_right);
419       g_value_set_boxed (&parameter[3].value, &border->bottom_left);
420     }
421
422   *n_params = 4;
423   return parameter;
424 }
425
426 static void
427 pack_border_radius (GValue             *value,
428                     GtkStyleProperties *props,
429                     GtkStateFlags       state,
430                     GtkStylePropertyContext *context)
431 {
432   GtkCssBorderCornerRadius *top_left;
433
434   /* NB: We are an int property, so we have to resolve to an int here.
435    * So we just resolve to an int. We pick one and stick to it.
436    * Lesson learned: Don't query border-radius shorthand, query the 
437    * real properties instead. */
438   gtk_style_properties_get (props,
439                             state,
440                             "border-top-left-radius", &top_left,
441                             NULL);
442
443   if (top_left)
444     g_value_set_int (value, top_left->horizontal);
445
446   g_free (top_left);
447 }
448
449 static GParameter *
450 unpack_font_description (const GValue *value,
451                          guint        *n_params)
452 {
453   GParameter *parameter = g_new0 (GParameter, 5);
454   PangoFontDescription *description;
455   PangoFontMask mask;
456   guint n;
457   
458   /* For backwards compat, we only unpack values that are indeed set.
459    * For strict CSS conformance we need to unpack all of them.
460    * Note that we do set all of them in the parse function, so it
461    * will not have effects when parsing CSS files. It will though
462    * for custom style providers.
463    */
464
465   description = g_value_get_boxed (value);
466   n = 0;
467
468   if (description)
469     mask = pango_font_description_get_set_fields (description);
470   else
471     mask = 0;
472
473   if (mask & PANGO_FONT_MASK_FAMILY)
474     {
475       GPtrArray *strv = g_ptr_array_new ();
476
477       g_ptr_array_add (strv, g_strdup (pango_font_description_get_family (description)));
478       g_ptr_array_add (strv, NULL);
479       parameter[n].name = "font-family";
480       g_value_init (&parameter[n].value, G_TYPE_STRV);
481       g_value_take_boxed (&parameter[n].value,
482                           g_ptr_array_free (strv, FALSE));
483       n++;
484     }
485
486   if (mask & PANGO_FONT_MASK_STYLE)
487     {
488       parameter[n].name = "font-style";
489       g_value_init (&parameter[n].value, PANGO_TYPE_STYLE);
490       g_value_set_enum (&parameter[n].value,
491                         pango_font_description_get_style (description));
492       n++;
493     }
494
495   if (mask & PANGO_FONT_MASK_VARIANT)
496     {
497       parameter[n].name = "font-variant";
498       g_value_init (&parameter[n].value, PANGO_TYPE_VARIANT);
499       g_value_set_enum (&parameter[n].value,
500                         pango_font_description_get_variant (description));
501       n++;
502     }
503
504   if (mask & PANGO_FONT_MASK_WEIGHT)
505     {
506       parameter[n].name = "font-weight";
507       g_value_init (&parameter[n].value, PANGO_TYPE_WEIGHT);
508       g_value_set_enum (&parameter[n].value,
509                         pango_font_description_get_weight (description));
510       n++;
511     }
512
513   if (mask & PANGO_FONT_MASK_SIZE)
514     {
515       parameter[n].name = "font-size";
516       g_value_init (&parameter[n].value, G_TYPE_DOUBLE);
517       g_value_set_double (&parameter[n].value,
518                           (double) pango_font_description_get_size (description) / PANGO_SCALE);
519       n++;
520     }
521
522   *n_params = n;
523
524   return parameter;
525 }
526
527 static void
528 pack_font_description (GValue             *value,
529                        GtkStyleProperties *props,
530                        GtkStateFlags       state,
531                        GtkStylePropertyContext *context)
532 {
533   PangoFontDescription *description;
534   char **families;
535   PangoStyle style;
536   PangoVariant variant;
537   PangoWeight weight;
538   double size;
539
540   gtk_style_properties_get (props,
541                             state,
542                             "font-family", &families,
543                             "font-style", &style,
544                             "font-variant", &variant,
545                             "font-weight", &weight,
546                             "font-size", &size,
547                             NULL);
548
549   description = pango_font_description_new ();
550   /* xxx: Can we set all the families here somehow? */
551   if (families)
552     pango_font_description_set_family (description, families[0]);
553   pango_font_description_set_size (description, round (size * PANGO_SCALE));
554   pango_font_description_set_style (description, style);
555   pango_font_description_set_variant (description, variant);
556   pango_font_description_set_weight (description, weight);
557
558   g_strfreev (families);
559
560   g_value_take_boxed (value, description);
561 }
562
563 static GParameter *
564 unpack_border_color (const GValue *value,
565                      guint        *n_params)
566 {
567   GParameter *parameter = g_new0 (GParameter, 4);
568   GType type;
569   
570   type = G_VALUE_TYPE (value);
571   if (type == G_TYPE_PTR_ARRAY)
572     type = GTK_TYPE_SYMBOLIC_COLOR;
573
574   parameter[0].name = "border-top-color";
575   g_value_init (&parameter[0].value, type);
576   parameter[1].name = "border-right-color";
577   g_value_init (&parameter[1].value, type);
578   parameter[2].name = "border-bottom-color";
579   g_value_init (&parameter[2].value, type);
580   parameter[3].name = "border-left-color";
581   g_value_init (&parameter[3].value, type);
582
583   if (G_VALUE_TYPE (value) == G_TYPE_PTR_ARRAY)
584     {
585       GPtrArray *array = g_value_get_boxed (value);
586       guint i;
587
588       for (i = 0; i < 4; i++)
589         g_value_set_boxed (&parameter[i].value, g_ptr_array_index (array, i));
590     }
591   else
592     {
593       /* can be RGBA or symbolic color */
594       gpointer p = g_value_get_boxed (value);
595
596       g_value_set_boxed (&parameter[0].value, p);
597       g_value_set_boxed (&parameter[1].value, p);
598       g_value_set_boxed (&parameter[2].value, p);
599       g_value_set_boxed (&parameter[3].value, p);
600     }
601
602   *n_params = 4;
603   return parameter;
604 }
605
606 static void
607 pack_border_color (GValue             *value,
608                    GtkStyleProperties *props,
609                    GtkStateFlags       state,
610                    GtkStylePropertyContext *context)
611 {
612   /* NB: We are a color property, so we have to resolve to a color here.
613    * So we just resolve to a color. We pick one and stick to it.
614    * Lesson learned: Don't query border-color shorthand, query the 
615    * real properties instead. */
616   g_value_unset (value);
617   gtk_style_properties_get_property (props, "border-top-color", state, value);
618 }
619
620 static void
621 _gtk_css_shorthand_property_register (GParamSpec               *pspec,
622                                       const char              **subproperties,
623                                       GtkStyleUnpackFunc        unpack_func,
624                                       GtkStylePackFunc          pack_func,
625                                       GtkStyleParseFunc         parse_func)
626 {
627   GtkStyleProperty *node;
628
629   g_return_if_fail (pack_func != NULL);
630   g_return_if_fail (unpack_func != NULL);
631
632   node = g_object_new (GTK_TYPE_CSS_SHORTHAND_PROPERTY,
633                        "name", pspec->name,
634                        "value-type", pspec->value_type,
635                        "subproperties", subproperties,
636                        NULL);
637
638   node->pspec = pspec;
639   node->pack_func = pack_func;
640   node->unpack_func = unpack_func;
641   node->parse_func = parse_func;
642 }
643
644 void
645 _gtk_css_shorthand_property_init_properties (void)
646 {
647   const char *font_subproperties[] = { "font-family", "font-style", "font-variant", "font-weight", "font-size", NULL };
648   const char *margin_subproperties[] = { "margin-top", "margin-right", "margin-bottom", "margin-left", NULL };
649   const char *padding_subproperties[] = { "padding-top", "padding-right", "padding-bottom", "padding-left", NULL };
650   const char *border_width_subproperties[] = { "border-top-width", "border-right-width", "border-bottom-width", "border-left-width", NULL };
651   const char *border_radius_subproperties[] = { "border-top-left-radius", "border-top-right-radius",
652                                                 "border-bottom-right-radius", "border-bottom-left-radius", NULL };
653   const char *border_color_subproperties[] = { "border-top-color", "border-right-color", "border-bottom-color", "border-left-color", NULL };
654   const char *border_image_subproperties[] = { "border-image-source", "border-image-slice", "border-image-width", "border-image-repeat", NULL };
655
656   _gtk_css_shorthand_property_register   (g_param_spec_boxed ("font",
657                                                               "Font Description",
658                                                               "Font Description",
659                                                               PANGO_TYPE_FONT_DESCRIPTION, 0),
660                                           font_subproperties,
661                                           unpack_font_description,
662                                           pack_font_description,
663                                           NULL);
664   _gtk_css_shorthand_property_register   (g_param_spec_boxed ("margin",
665                                                               "Margin",
666                                                               "Margin",
667                                                               GTK_TYPE_BORDER, 0),
668                                           margin_subproperties,
669                                           unpack_margin,
670                                           pack_margin,
671                                           NULL);
672   _gtk_css_shorthand_property_register   (g_param_spec_boxed ("padding",
673                                                               "Padding",
674                                                               "Padding",
675                                                               GTK_TYPE_BORDER, 0),
676                                           padding_subproperties,
677                                           unpack_padding,
678                                           pack_padding,
679                                           NULL);
680   _gtk_css_shorthand_property_register   (g_param_spec_boxed ("border-width",
681                                                               "Border width",
682                                                               "Border width, in pixels",
683                                                               GTK_TYPE_BORDER, 0),
684                                           border_width_subproperties,
685                                           unpack_border_width,
686                                           pack_border_width,
687                                           NULL);
688   _gtk_css_shorthand_property_register   (g_param_spec_int ("border-radius",
689                                                             "Border radius",
690                                                             "Border radius, in pixels",
691                                                             0, G_MAXINT, 0, 0),
692                                           border_radius_subproperties,
693                                           unpack_border_radius,
694                                           pack_border_radius,
695                                           border_radius_value_parse);
696   _gtk_css_shorthand_property_register   (g_param_spec_boxed ("border-color",
697                                                               "Border color",
698                                                               "Border color",
699                                                               GDK_TYPE_RGBA, 0),
700                                           border_color_subproperties,
701                                           unpack_border_color,
702                                           pack_border_color,
703                                           border_color_shorthand_value_parse);
704   _gtk_css_shorthand_property_register   (g_param_spec_boxed ("border-image",
705                                                               "Border Image",
706                                                               "Border Image",
707                                                               GTK_TYPE_BORDER_IMAGE, 0),
708                                           border_image_subproperties,
709                                           _gtk_border_image_unpack,
710                                           _gtk_border_image_pack,
711                                           border_image_value_parse);
712 }