]> Pileus Git - ~andy/gtk/blob - gtk/gtkcssshorthandpropertyimpl.c
shorthand: Remove old parse func support
[~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 "gtkcssstylefuncsprivate.h"
30 #include "gtkcsstypesprivate.h"
31
32 /* this is in case round() is not provided by the compiler, 
33  * such as in the case of C89 compilers, like MSVC
34  */
35 #include "fallback-c89.c"
36
37 /*** PARSING ***/
38
39 static gboolean
40 value_is_done_parsing (GtkCssParser *parser)
41 {
42   return _gtk_css_parser_is_eof (parser) ||
43          _gtk_css_parser_begins_with (parser, ';') ||
44          _gtk_css_parser_begins_with (parser, '}');
45 }
46
47 static gboolean
48 parse_border (GtkCssShorthandProperty *shorthand,
49               GValue                  *values,
50               GtkCssParser            *parser,
51               GFile                   *base)
52 {
53   GValue temp = G_VALUE_INIT;
54   GtkBorder *border;
55
56   g_value_init (&temp, GTK_TYPE_BORDER);
57   if (!_gtk_css_style_parse_value (&temp, parser, base))
58     {
59       g_value_unset (&temp);
60       return FALSE;
61     }
62
63   border = g_value_get_boxed (&temp);
64
65   g_value_init (&values[0], G_TYPE_INT);
66   g_value_init (&values[1], G_TYPE_INT);
67   g_value_init (&values[2], G_TYPE_INT);
68   g_value_init (&values[3], G_TYPE_INT);
69   g_value_set_int (&values[0], border->top);
70   g_value_set_int (&values[1], border->right);
71   g_value_set_int (&values[2], border->bottom);
72   g_value_set_int (&values[3], border->left);
73
74   g_value_unset (&temp);
75
76   return TRUE;
77 }
78                     
79 static gboolean 
80 parse_border_radius (GtkCssShorthandProperty *shorthand,
81                      GValue                  *values,
82                      GtkCssParser            *parser,
83                      GFile                   *base)
84 {
85   GtkCssBorderCornerRadius borders[4];
86   guint i;
87
88   for (i = 0; i < G_N_ELEMENTS (borders); i++)
89     {
90       if (!_gtk_css_parser_try_double (parser, &borders[i].horizontal))
91         break;
92       if (borders[i].horizontal < 0)
93         {
94           _gtk_css_parser_error (parser, "Border radius values cannot be negative");
95           return FALSE;
96         }
97     }
98
99   if (i == 0)
100     {
101       _gtk_css_parser_error (parser, "Expected a number");
102       return FALSE;
103     }
104
105   /* The magic (i - 1) >> 1 below makes it take the correct value
106    * according to spec. Feel free to check the 4 cases */
107   for (; i < G_N_ELEMENTS (borders); i++)
108     borders[i].horizontal = borders[(i - 1) >> 1].horizontal;
109
110   if (_gtk_css_parser_try (parser, "/", TRUE))
111     {
112       for (i = 0; i < G_N_ELEMENTS (borders); i++)
113         {
114           if (!_gtk_css_parser_try_double (parser, &borders[i].vertical))
115             break;
116           if (borders[i].vertical < 0)
117             {
118               _gtk_css_parser_error (parser, "Border radius values cannot be negative");
119               return FALSE;
120             }
121         }
122
123       if (i == 0)
124         {
125           _gtk_css_parser_error (parser, "Expected a number");
126           return FALSE;
127         }
128
129       for (; i < G_N_ELEMENTS (borders); i++)
130         borders[i].vertical = borders[(i - 1) >> 1].vertical;
131
132     }
133   else
134     {
135       for (i = 0; i < G_N_ELEMENTS (borders); i++)
136         borders[i].vertical = borders[i].horizontal;
137     }
138
139   for (i = 0; i < G_N_ELEMENTS (borders); i++)
140     {
141       g_value_init (&values[i], GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
142       g_value_set_boxed (&values[i], &borders[i]);
143     }
144
145   return TRUE;
146 }
147
148 static gboolean 
149 parse_border_color (GtkCssShorthandProperty *shorthand,
150                     GValue                  *values,
151                     GtkCssParser            *parser,
152                     GFile                   *base)
153 {
154   GtkSymbolicColor *symbolic;
155   guint i;
156
157   for (i = 0; i < 4; i++)
158     {
159       symbolic = _gtk_css_parser_read_symbolic_color (parser);
160       if (symbolic == NULL)
161         return FALSE;
162
163       g_value_init (&values[i], GTK_TYPE_SYMBOLIC_COLOR);
164       g_value_set_boxed (&values[i], symbolic);
165
166       if (value_is_done_parsing (parser))
167         break;
168     }
169
170   for (i++; i < 4; i++)
171     {
172       g_value_init (&values[i], GTK_TYPE_SYMBOLIC_COLOR);
173       g_value_copy (&values[(i - 1) >> 1], &values[i]);
174     }
175
176   return TRUE;
177 }
178
179 static gboolean
180 parse_border_image (GtkCssShorthandProperty *shorthand,
181                     GValue                  *values,
182                     GtkCssParser            *parser,
183                     GFile                   *base)
184 {
185   g_value_init (&values[0], CAIRO_GOBJECT_TYPE_PATTERN);
186   if (!_gtk_css_style_parse_value (&values[0], parser, base))
187     return FALSE;
188
189   if (value_is_done_parsing (parser))
190     return TRUE;
191
192   g_value_init (&values[1], GTK_TYPE_BORDER);
193   if (!_gtk_css_style_parse_value (&values[1], parser, base))
194     return FALSE;
195
196   if (_gtk_css_parser_try (parser, "/", TRUE))
197     {
198       g_value_init (&values[2], GTK_TYPE_BORDER);
199       if (!_gtk_css_style_parse_value (&values[2], parser, base))
200         return FALSE;
201     }
202
203   if (value_is_done_parsing (parser))
204     return TRUE;
205
206   g_value_init (&values[3], GTK_TYPE_CSS_BORDER_IMAGE_REPEAT);
207   if (!_gtk_css_style_parse_value (&values[3], parser, base))
208     return FALSE;
209
210   return TRUE;
211 }
212
213 static gboolean
214 parse_font (GtkCssShorthandProperty *shorthand,
215             GValue                  *values,
216             GtkCssParser            *parser,
217             GFile                   *base)
218 {
219   PangoFontDescription *desc;
220   guint mask;
221   char *str;
222
223   str = _gtk_css_parser_read_value (parser);
224   if (str == NULL)
225     return FALSE;
226
227   desc = pango_font_description_from_string (str);
228   g_free (str);
229
230   mask = pango_font_description_get_set_fields (desc);
231
232   if (mask & PANGO_FONT_MASK_FAMILY)
233     {
234       GPtrArray *strv = g_ptr_array_new ();
235
236       g_ptr_array_add (strv, g_strdup (pango_font_description_get_family (desc)));
237       g_ptr_array_add (strv, NULL);
238       g_value_init (&values[0], G_TYPE_STRV);
239       g_value_take_boxed (&values[0], g_ptr_array_free (strv, FALSE));
240     }
241   if (mask & PANGO_FONT_MASK_STYLE)
242     {
243       g_value_init (&values[1], PANGO_TYPE_STYLE);
244       g_value_set_enum (&values[1], pango_font_description_get_style (desc));
245     }
246   if (mask & PANGO_FONT_MASK_VARIANT)
247     {
248       g_value_init (&values[2], PANGO_TYPE_VARIANT);
249       g_value_set_enum (&values[2], pango_font_description_get_variant (desc));
250     }
251   if (mask & PANGO_FONT_MASK_WEIGHT)
252     {
253       g_value_init (&values[3], PANGO_TYPE_WEIGHT);
254       g_value_set_enum (&values[3], pango_font_description_get_weight (desc));
255     }
256   if (mask & PANGO_FONT_MASK_SIZE)
257     {
258       g_value_init (&values[4], G_TYPE_DOUBLE);
259       g_value_set_double (&values[4],
260                           (double) pango_font_description_get_size (desc) / PANGO_SCALE);
261     }
262
263   pango_font_description_free (desc);
264
265   return TRUE;
266 }
267
268 /*** PACKING ***/
269
270 static GParameter *
271 unpack_border (const GValue *value,
272                guint        *n_params,
273                const char   *top,
274                const char   *left,
275                const char   *bottom,
276                const char   *right)
277 {
278   GParameter *parameter = g_new0 (GParameter, 4);
279   GtkBorder *border = g_value_get_boxed (value);
280
281   parameter[0].name = top;
282   g_value_init (&parameter[0].value, G_TYPE_INT);
283   g_value_set_int (&parameter[0].value, border->top);
284   parameter[1].name = left;
285   g_value_init (&parameter[1].value, G_TYPE_INT);
286   g_value_set_int (&parameter[1].value, border->left);
287   parameter[2].name = bottom;
288   g_value_init (&parameter[2].value, G_TYPE_INT);
289   g_value_set_int (&parameter[2].value, border->bottom);
290   parameter[3].name = right;
291   g_value_init (&parameter[3].value, G_TYPE_INT);
292   g_value_set_int (&parameter[3].value, border->right);
293
294   *n_params = 4;
295   return parameter;
296 }
297
298 static void
299 pack_border (GValue             *value,
300              GtkStyleProperties *props,
301              GtkStateFlags       state,
302              const char         *top,
303              const char         *left,
304              const char         *bottom,
305              const char         *right)
306 {
307   GtkBorder border;
308   int t, l, b, r;
309
310   gtk_style_properties_get (props,
311                             state,
312                             top, &t,
313                             left, &l,
314                             bottom, &b,
315                             right, &r,
316                             NULL);
317
318   border.top = t;
319   border.left = l;
320   border.bottom = b;
321   border.right = r;
322
323   g_value_set_boxed (value, &border);
324 }
325
326 static GParameter *
327 unpack_border_width (const GValue *value,
328                      guint        *n_params)
329 {
330   return unpack_border (value, n_params,
331                         "border-top-width", "border-left-width",
332                         "border-bottom-width", "border-right-width");
333 }
334
335 static void
336 pack_border_width (GValue             *value,
337                    GtkStyleProperties *props,
338                    GtkStateFlags       state,
339                    GtkStylePropertyContext *context)
340 {
341   pack_border (value, props, state,
342                "border-top-width", "border-left-width",
343                "border-bottom-width", "border-right-width");
344 }
345
346 static GParameter *
347 unpack_padding (const GValue *value,
348                 guint        *n_params)
349 {
350   return unpack_border (value, n_params,
351                         "padding-top", "padding-left",
352                         "padding-bottom", "padding-right");
353 }
354
355 static void
356 pack_padding (GValue             *value,
357               GtkStyleProperties *props,
358               GtkStateFlags       state,
359               GtkStylePropertyContext *context)
360 {
361   pack_border (value, props, state,
362                "padding-top", "padding-left",
363                "padding-bottom", "padding-right");
364 }
365
366 static GParameter *
367 unpack_margin (const GValue *value,
368                guint        *n_params)
369 {
370   return unpack_border (value, n_params,
371                         "margin-top", "margin-left",
372                         "margin-bottom", "margin-right");
373 }
374
375 static void
376 pack_margin (GValue             *value,
377              GtkStyleProperties *props,
378              GtkStateFlags       state,
379              GtkStylePropertyContext *context)
380 {
381   pack_border (value, props, state,
382                "margin-top", "margin-left",
383                "margin-bottom", "margin-right");
384 }
385
386 static GParameter *
387 unpack_border_radius (const GValue *value,
388                       guint        *n_params)
389 {
390   GParameter *parameter = g_new0 (GParameter, 4);
391   GtkCssBorderCornerRadius border;
392   
393   border.horizontal = border.vertical = g_value_get_int (value);
394
395   parameter[0].name = "border-top-left-radius";
396   g_value_init (&parameter[0].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
397   g_value_set_boxed (&parameter[0].value, &border);
398   parameter[1].name = "border-top-right-radius";
399   g_value_init (&parameter[1].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
400   g_value_set_boxed (&parameter[1].value, &border);
401   parameter[2].name = "border-bottom-right-radius";
402   g_value_init (&parameter[2].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
403   g_value_set_boxed (&parameter[2].value, &border);
404   parameter[3].name = "border-bottom-left-radius";
405   g_value_init (&parameter[3].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
406   g_value_set_boxed (&parameter[3].value, &border);
407
408   *n_params = 4;
409   return parameter;
410 }
411
412 static void
413 pack_border_radius (GValue             *value,
414                     GtkStyleProperties *props,
415                     GtkStateFlags       state,
416                     GtkStylePropertyContext *context)
417 {
418   GtkCssBorderCornerRadius *top_left;
419
420   /* NB: We are an int property, so we have to resolve to an int here.
421    * So we just resolve to an int. We pick one and stick to it.
422    * Lesson learned: Don't query border-radius shorthand, query the 
423    * real properties instead. */
424   gtk_style_properties_get (props,
425                             state,
426                             "border-top-left-radius", &top_left,
427                             NULL);
428
429   if (top_left)
430     g_value_set_int (value, top_left->horizontal);
431
432   g_free (top_left);
433 }
434
435 static GParameter *
436 unpack_font_description (const GValue *value,
437                          guint        *n_params)
438 {
439   GParameter *parameter = g_new0 (GParameter, 5);
440   PangoFontDescription *description;
441   PangoFontMask mask;
442   guint n;
443   
444   /* For backwards compat, we only unpack values that are indeed set.
445    * For strict CSS conformance we need to unpack all of them.
446    * Note that we do set all of them in the parse function, so it
447    * will not have effects when parsing CSS files. It will though
448    * for custom style providers.
449    */
450
451   description = g_value_get_boxed (value);
452   n = 0;
453
454   if (description)
455     mask = pango_font_description_get_set_fields (description);
456   else
457     mask = 0;
458
459   if (mask & PANGO_FONT_MASK_FAMILY)
460     {
461       GPtrArray *strv = g_ptr_array_new ();
462
463       g_ptr_array_add (strv, g_strdup (pango_font_description_get_family (description)));
464       g_ptr_array_add (strv, NULL);
465       parameter[n].name = "font-family";
466       g_value_init (&parameter[n].value, G_TYPE_STRV);
467       g_value_take_boxed (&parameter[n].value,
468                           g_ptr_array_free (strv, FALSE));
469       n++;
470     }
471
472   if (mask & PANGO_FONT_MASK_STYLE)
473     {
474       parameter[n].name = "font-style";
475       g_value_init (&parameter[n].value, PANGO_TYPE_STYLE);
476       g_value_set_enum (&parameter[n].value,
477                         pango_font_description_get_style (description));
478       n++;
479     }
480
481   if (mask & PANGO_FONT_MASK_VARIANT)
482     {
483       parameter[n].name = "font-variant";
484       g_value_init (&parameter[n].value, PANGO_TYPE_VARIANT);
485       g_value_set_enum (&parameter[n].value,
486                         pango_font_description_get_variant (description));
487       n++;
488     }
489
490   if (mask & PANGO_FONT_MASK_WEIGHT)
491     {
492       parameter[n].name = "font-weight";
493       g_value_init (&parameter[n].value, PANGO_TYPE_WEIGHT);
494       g_value_set_enum (&parameter[n].value,
495                         pango_font_description_get_weight (description));
496       n++;
497     }
498
499   if (mask & PANGO_FONT_MASK_SIZE)
500     {
501       parameter[n].name = "font-size";
502       g_value_init (&parameter[n].value, G_TYPE_DOUBLE);
503       g_value_set_double (&parameter[n].value,
504                           (double) pango_font_description_get_size (description) / PANGO_SCALE);
505       n++;
506     }
507
508   *n_params = n;
509
510   return parameter;
511 }
512
513 static void
514 pack_font_description (GValue             *value,
515                        GtkStyleProperties *props,
516                        GtkStateFlags       state,
517                        GtkStylePropertyContext *context)
518 {
519   PangoFontDescription *description;
520   char **families;
521   PangoStyle style;
522   PangoVariant variant;
523   PangoWeight weight;
524   double size;
525
526   gtk_style_properties_get (props,
527                             state,
528                             "font-family", &families,
529                             "font-style", &style,
530                             "font-variant", &variant,
531                             "font-weight", &weight,
532                             "font-size", &size,
533                             NULL);
534
535   description = pango_font_description_new ();
536   /* xxx: Can we set all the families here somehow? */
537   if (families)
538     pango_font_description_set_family (description, families[0]);
539   pango_font_description_set_size (description, round (size * PANGO_SCALE));
540   pango_font_description_set_style (description, style);
541   pango_font_description_set_variant (description, variant);
542   pango_font_description_set_weight (description, weight);
543
544   g_strfreev (families);
545
546   g_value_take_boxed (value, description);
547 }
548
549 static GParameter *
550 unpack_border_color (const GValue *value,
551                      guint        *n_params)
552 {
553   GParameter *parameter = g_new0 (GParameter, 4);
554   GType type;
555   
556   type = G_VALUE_TYPE (value);
557   if (type == G_TYPE_PTR_ARRAY)
558     type = GTK_TYPE_SYMBOLIC_COLOR;
559
560   parameter[0].name = "border-top-color";
561   g_value_init (&parameter[0].value, type);
562   parameter[1].name = "border-right-color";
563   g_value_init (&parameter[1].value, type);
564   parameter[2].name = "border-bottom-color";
565   g_value_init (&parameter[2].value, type);
566   parameter[3].name = "border-left-color";
567   g_value_init (&parameter[3].value, type);
568
569   if (G_VALUE_TYPE (value) == G_TYPE_PTR_ARRAY)
570     {
571       GPtrArray *array = g_value_get_boxed (value);
572       guint i;
573
574       for (i = 0; i < 4; i++)
575         g_value_set_boxed (&parameter[i].value, g_ptr_array_index (array, i));
576     }
577   else
578     {
579       /* can be RGBA or symbolic color */
580       gpointer p = g_value_get_boxed (value);
581
582       g_value_set_boxed (&parameter[0].value, p);
583       g_value_set_boxed (&parameter[1].value, p);
584       g_value_set_boxed (&parameter[2].value, p);
585       g_value_set_boxed (&parameter[3].value, p);
586     }
587
588   *n_params = 4;
589   return parameter;
590 }
591
592 static void
593 pack_border_color (GValue             *value,
594                    GtkStyleProperties *props,
595                    GtkStateFlags       state,
596                    GtkStylePropertyContext *context)
597 {
598   /* NB: We are a color property, so we have to resolve to a color here.
599    * So we just resolve to a color. We pick one and stick to it.
600    * Lesson learned: Don't query border-color shorthand, query the 
601    * real properties instead. */
602   g_value_unset (value);
603   gtk_style_properties_get_property (props, "border-top-color", state, value);
604 }
605
606 static void
607 _gtk_css_shorthand_property_register (const char                        *name,
608                                       GType                              value_type,
609                                       const char                       **subproperties,
610                                       GtkCssShorthandPropertyParseFunc   parse_func,
611                                       GtkStyleUnpackFunc                 unpack_func,
612                                       GtkStylePackFunc                   pack_func)
613 {
614   GtkStyleProperty *node;
615
616   g_return_if_fail (pack_func != NULL);
617   g_return_if_fail (unpack_func != NULL);
618
619   node = g_object_new (GTK_TYPE_CSS_SHORTHAND_PROPERTY,
620                        "name", name,
621                        "value-type", value_type,
622                        "subproperties", subproperties,
623                        NULL);
624
625   GTK_CSS_SHORTHAND_PROPERTY (node)->parse = parse_func;
626   node->pack_func = pack_func;
627   node->unpack_func = unpack_func;
628 }
629
630 void
631 _gtk_css_shorthand_property_init_properties (void)
632 {
633   /* The order is important here, be careful when changing it */
634   const char *font_subproperties[] = { "font-family", "font-style", "font-variant", "font-weight", "font-size", NULL };
635   const char *margin_subproperties[] = { "margin-top", "margin-right", "margin-bottom", "margin-left", NULL };
636   const char *padding_subproperties[] = { "padding-top", "padding-right", "padding-bottom", "padding-left", NULL };
637   const char *border_width_subproperties[] = { "border-top-width", "border-right-width", "border-bottom-width", "border-left-width", NULL };
638   const char *border_radius_subproperties[] = { "border-top-left-radius", "border-top-right-radius",
639                                                 "border-bottom-right-radius", "border-bottom-left-radius", NULL };
640   const char *border_color_subproperties[] = { "border-top-color", "border-right-color", "border-bottom-color", "border-left-color", NULL };
641   const char *border_image_subproperties[] = { "border-image-source", "border-image-slice", "border-image-width", "border-image-repeat", NULL };
642
643   _gtk_css_shorthand_property_register   ("font",
644                                           PANGO_TYPE_FONT_DESCRIPTION,
645                                           font_subproperties,
646                                           parse_font,
647                                           unpack_font_description,
648                                           pack_font_description);
649   _gtk_css_shorthand_property_register   ("margin",
650                                           GTK_TYPE_BORDER,
651                                           margin_subproperties,
652                                           parse_border,
653                                           unpack_margin,
654                                           pack_margin);
655   _gtk_css_shorthand_property_register   ("padding",
656                                           GTK_TYPE_BORDER,
657                                           padding_subproperties,
658                                           parse_border,
659                                           unpack_padding,
660                                           pack_padding);
661   _gtk_css_shorthand_property_register   ("border-width",
662                                           GTK_TYPE_BORDER,
663                                           border_width_subproperties,
664                                           parse_border,
665                                           unpack_border_width,
666                                           pack_border_width);
667   _gtk_css_shorthand_property_register   ("border-radius",
668                                           G_TYPE_INT,
669                                           border_radius_subproperties,
670                                           parse_border_radius,
671                                           unpack_border_radius,
672                                           pack_border_radius);
673   _gtk_css_shorthand_property_register   ("border-color",
674                                           GDK_TYPE_RGBA,
675                                           border_color_subproperties,
676                                           parse_border_color,
677                                           unpack_border_color,
678                                           pack_border_color);
679   _gtk_css_shorthand_property_register   ("border-image",
680                                           GTK_TYPE_BORDER_IMAGE,
681                                           border_image_subproperties,
682                                           parse_border_image,
683                                           _gtk_border_image_unpack,
684                                           _gtk_border_image_pack);
685 }