1 /*
2  * Copyright © 2012 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 "gtkprivate.h"
23 #include "gtkcssstyleprivate.h"
24 
25 #include "gtkcssanimationprivate.h"
26 #include "gtkcssarrayvalueprivate.h"
27 #include "gtkcssenumvalueprivate.h"
28 #include "gtkcssinheritvalueprivate.h"
29 #include "gtkcssinitialvalueprivate.h"
30 #include "gtkcssnumbervalueprivate.h"
31 #include "gtkcsscolorvalueprivate.h"
32 #include "gtkcssshorthandpropertyprivate.h"
33 #include "gtkcssstringvalueprivate.h"
34 #include "gtkcssfontvariationsvalueprivate.h"
35 #include "gtkcssfontfeaturesvalueprivate.h"
36 #include "gtkcssstylepropertyprivate.h"
37 #include "gtkcsstransitionprivate.h"
38 #include "gtkstyleanimationprivate.h"
39 #include "gtkstylepropertyprivate.h"
40 #include "gtkstyleproviderprivate.h"
41 
G_DEFINE_ABSTRACT_TYPE(GtkCssStyle,gtk_css_style,G_TYPE_OBJECT)42 G_DEFINE_ABSTRACT_TYPE (GtkCssStyle, gtk_css_style, G_TYPE_OBJECT)
43 
44 static GtkCssSection *
45 gtk_css_style_real_get_section (GtkCssStyle *style,
46                                 guint        id)
47 {
48   return NULL;
49 }
50 
51 static gboolean
gtk_css_style_real_is_static(GtkCssStyle * style)52 gtk_css_style_real_is_static (GtkCssStyle *style)
53 {
54   return TRUE;
55 }
56 
57 
58 static void
gtk_css_style_finalize(GObject * object)59 gtk_css_style_finalize (GObject *object)
60 {
61   GtkCssStyle *style = GTK_CSS_STYLE (object);
62 
63   gtk_css_values_unref ((GtkCssValues *)style->core);
64   gtk_css_values_unref ((GtkCssValues *)style->background);
65   gtk_css_values_unref ((GtkCssValues *)style->border);
66   gtk_css_values_unref ((GtkCssValues *)style->icon);
67   gtk_css_values_unref ((GtkCssValues *)style->outline);
68   gtk_css_values_unref ((GtkCssValues *)style->font);
69   gtk_css_values_unref ((GtkCssValues *)style->font_variant);
70   gtk_css_values_unref ((GtkCssValues *)style->animation);
71   gtk_css_values_unref ((GtkCssValues *)style->transition);
72   gtk_css_values_unref ((GtkCssValues *)style->size);
73   gtk_css_values_unref ((GtkCssValues *)style->other);
74 
75   G_OBJECT_CLASS (gtk_css_style_parent_class)->finalize (object);
76 }
77 
78 static void
gtk_css_style_class_init(GtkCssStyleClass * klass)79 gtk_css_style_class_init (GtkCssStyleClass *klass)
80 {
81   GObjectClass *object_class = G_OBJECT_CLASS (klass);
82 
83   object_class->finalize = gtk_css_style_finalize;
84 
85   klass->get_section = gtk_css_style_real_get_section;
86   klass->is_static = gtk_css_style_real_is_static;
87 }
88 
89 static void
gtk_css_style_init(GtkCssStyle * style)90 gtk_css_style_init (GtkCssStyle *style)
91 {
92 }
93 
94 GtkCssValue *
gtk_css_style_get_value(GtkCssStyle * style,guint id)95 gtk_css_style_get_value (GtkCssStyle *style,
96                          guint        id)
97 {
98   switch (id)
99     {
100     case GTK_CSS_PROPERTY_COLOR:
101       return style->core->color;
102     case GTK_CSS_PROPERTY_DPI:
103       return style->core->dpi;
104     case GTK_CSS_PROPERTY_FONT_SIZE:
105       return style->core->font_size;
106     case GTK_CSS_PROPERTY_ICON_PALETTE:
107       return style->core->icon_palette;
108     case GTK_CSS_PROPERTY_BACKGROUND_COLOR:
109       return style->background->background_color;
110     case GTK_CSS_PROPERTY_FONT_FAMILY:
111       return style->font->font_family;
112     case GTK_CSS_PROPERTY_FONT_STYLE:
113       return style->font->font_style;
114     case GTK_CSS_PROPERTY_FONT_WEIGHT:
115       return style->font->font_weight;
116     case GTK_CSS_PROPERTY_FONT_STRETCH:
117       return style->font->font_stretch;
118     case GTK_CSS_PROPERTY_LETTER_SPACING:
119       return style->font->letter_spacing;
120     case GTK_CSS_PROPERTY_TEXT_DECORATION_LINE:
121       return style->font_variant->text_decoration_line;
122     case GTK_CSS_PROPERTY_TEXT_DECORATION_COLOR:
123       return style->font_variant->text_decoration_color ? style->font_variant->text_decoration_color : style->core->color;
124     case GTK_CSS_PROPERTY_TEXT_DECORATION_STYLE:
125       return style->font_variant->text_decoration_style;
126     case GTK_CSS_PROPERTY_FONT_KERNING:
127       return style->font_variant->font_kerning;
128     case GTK_CSS_PROPERTY_FONT_VARIANT_LIGATURES:
129       return style->font_variant->font_variant_ligatures;
130     case GTK_CSS_PROPERTY_FONT_VARIANT_POSITION:
131       return style->font_variant->font_variant_position;
132     case GTK_CSS_PROPERTY_FONT_VARIANT_CAPS:
133       return style->font_variant->font_variant_caps;
134     case GTK_CSS_PROPERTY_FONT_VARIANT_NUMERIC:
135       return style->font_variant->font_variant_numeric;
136     case GTK_CSS_PROPERTY_FONT_VARIANT_ALTERNATES:
137       return style->font_variant->font_variant_alternates;
138     case GTK_CSS_PROPERTY_FONT_VARIANT_EAST_ASIAN:
139       return style->font_variant->font_variant_east_asian;
140     case GTK_CSS_PROPERTY_TEXT_SHADOW:
141       return style->font->text_shadow;
142     case GTK_CSS_PROPERTY_BOX_SHADOW:
143       return style->background->box_shadow;
144     case GTK_CSS_PROPERTY_MARGIN_TOP:
145       return style->size->margin_top;
146     case GTK_CSS_PROPERTY_MARGIN_LEFT:
147       return style->size->margin_left;
148     case GTK_CSS_PROPERTY_MARGIN_BOTTOM:
149       return style->size->margin_bottom;
150     case GTK_CSS_PROPERTY_MARGIN_RIGHT:
151       return style->size->margin_right;
152     case GTK_CSS_PROPERTY_PADDING_TOP:
153       return style->size->padding_top;
154     case GTK_CSS_PROPERTY_PADDING_LEFT:
155       return style->size->padding_left;
156     case GTK_CSS_PROPERTY_PADDING_BOTTOM:
157       return style->size->padding_bottom;
158     case GTK_CSS_PROPERTY_PADDING_RIGHT:
159       return style->size->padding_right;
160     case GTK_CSS_PROPERTY_BORDER_TOP_STYLE:
161       return style->border->border_top_style;
162     case GTK_CSS_PROPERTY_BORDER_TOP_WIDTH:
163       return style->border->border_top_width;
164     case GTK_CSS_PROPERTY_BORDER_LEFT_STYLE:
165       return style->border->border_left_style;
166     case GTK_CSS_PROPERTY_BORDER_LEFT_WIDTH:
167       return style->border->border_left_width;
168     case GTK_CSS_PROPERTY_BORDER_BOTTOM_STYLE:
169       return style->border->border_bottom_style;
170     case GTK_CSS_PROPERTY_BORDER_BOTTOM_WIDTH:
171       return style->border->border_bottom_width;
172     case GTK_CSS_PROPERTY_BORDER_RIGHT_STYLE:
173       return style->border->border_right_style;
174     case GTK_CSS_PROPERTY_BORDER_RIGHT_WIDTH:
175       return style->border->border_right_width;
176     case GTK_CSS_PROPERTY_BORDER_TOP_LEFT_RADIUS:
177       return style->border->border_top_left_radius;
178     case GTK_CSS_PROPERTY_BORDER_TOP_RIGHT_RADIUS:
179       return style->border->border_top_right_radius;
180     case GTK_CSS_PROPERTY_BORDER_BOTTOM_RIGHT_RADIUS:
181       return style->border->border_bottom_right_radius;
182     case GTK_CSS_PROPERTY_BORDER_BOTTOM_LEFT_RADIUS:
183       return style->border->border_bottom_left_radius;
184     case GTK_CSS_PROPERTY_OUTLINE_STYLE:
185       return style->outline->outline_style;
186     case GTK_CSS_PROPERTY_OUTLINE_WIDTH:
187       return style->outline->outline_width;
188     case GTK_CSS_PROPERTY_OUTLINE_OFFSET:
189       return style->outline->outline_offset;
190     case GTK_CSS_PROPERTY_BACKGROUND_CLIP:
191       return style->background->background_clip;
192     case GTK_CSS_PROPERTY_BACKGROUND_ORIGIN:
193       return style->background->background_origin;
194     case GTK_CSS_PROPERTY_BACKGROUND_SIZE:
195       return style->background->background_size;
196     case GTK_CSS_PROPERTY_BACKGROUND_POSITION:
197       return style->background->background_position;
198     case GTK_CSS_PROPERTY_BORDER_TOP_COLOR:
199       return style->border->border_top_color ? style->border->border_top_color : style->core->color;
200     case GTK_CSS_PROPERTY_BORDER_RIGHT_COLOR:
201       return style->border->border_right_color ? style->border->border_right_color : style->core->color;
202     case GTK_CSS_PROPERTY_BORDER_BOTTOM_COLOR:
203       return style->border->border_bottom_color ? style->border->border_bottom_color : style->core->color;
204     case GTK_CSS_PROPERTY_BORDER_LEFT_COLOR:
205       return style->border->border_left_color ? style->border->border_left_color: style->core->color;
206     case GTK_CSS_PROPERTY_OUTLINE_COLOR:
207       return style->outline->outline_color ? style->outline->outline_color : style->core->color;
208     case GTK_CSS_PROPERTY_BACKGROUND_REPEAT:
209       return style->background->background_repeat;
210     case GTK_CSS_PROPERTY_BACKGROUND_IMAGE:
211       return style->background->background_image;
212     case GTK_CSS_PROPERTY_BACKGROUND_BLEND_MODE:
213       return style->background->background_blend_mode;
214     case GTK_CSS_PROPERTY_BORDER_IMAGE_SOURCE:
215       return style->border->border_image_source;
216     case GTK_CSS_PROPERTY_BORDER_IMAGE_REPEAT:
217       return style->border->border_image_repeat;
218     case GTK_CSS_PROPERTY_BORDER_IMAGE_SLICE:
219       return style->border->border_image_slice;
220     case GTK_CSS_PROPERTY_BORDER_IMAGE_WIDTH:
221       return style->border->border_image_width;
222     case GTK_CSS_PROPERTY_ICON_SOURCE:
223       return style->other->icon_source;
224     case GTK_CSS_PROPERTY_ICON_SIZE:
225       return style->icon->icon_size;
226     case GTK_CSS_PROPERTY_ICON_SHADOW:
227       return style->icon->icon_shadow;
228     case GTK_CSS_PROPERTY_ICON_STYLE:
229       return style->icon->icon_style;
230     case GTK_CSS_PROPERTY_ICON_TRANSFORM:
231       return style->other->icon_transform;
232     case GTK_CSS_PROPERTY_ICON_FILTER:
233       return style->other->icon_filter;
234     case GTK_CSS_PROPERTY_BORDER_SPACING:
235       return style->size->border_spacing;
236     case GTK_CSS_PROPERTY_TRANSFORM:
237       return style->other->transform;
238     case GTK_CSS_PROPERTY_TRANSFORM_ORIGIN:
239       return style->other->transform_origin;
240     case GTK_CSS_PROPERTY_MIN_WIDTH:
241       return style->size->min_width;
242     case GTK_CSS_PROPERTY_MIN_HEIGHT:
243       return style->size->min_height;
244     case GTK_CSS_PROPERTY_TRANSITION_PROPERTY:
245       return style->transition->transition_property;
246     case GTK_CSS_PROPERTY_TRANSITION_DURATION:
247       return style->transition->transition_duration;
248     case GTK_CSS_PROPERTY_TRANSITION_TIMING_FUNCTION:
249       return style->transition->transition_timing_function;
250     case GTK_CSS_PROPERTY_TRANSITION_DELAY:
251       return style->transition->transition_delay;
252     case GTK_CSS_PROPERTY_ANIMATION_NAME:
253       return style->animation->animation_name;
254     case GTK_CSS_PROPERTY_ANIMATION_DURATION:
255       return style->animation->animation_duration;
256     case GTK_CSS_PROPERTY_ANIMATION_TIMING_FUNCTION:
257       return style->animation->animation_timing_function;
258     case GTK_CSS_PROPERTY_ANIMATION_ITERATION_COUNT:
259       return style->animation->animation_iteration_count;
260     case GTK_CSS_PROPERTY_ANIMATION_DIRECTION:
261       return style->animation->animation_direction;
262     case GTK_CSS_PROPERTY_ANIMATION_PLAY_STATE:
263       return style->animation->animation_play_state;
264     case GTK_CSS_PROPERTY_ANIMATION_DELAY:
265       return style->animation->animation_delay;
266     case GTK_CSS_PROPERTY_ANIMATION_FILL_MODE:
267       return style->animation->animation_fill_mode;
268     case GTK_CSS_PROPERTY_OPACITY:
269       return style->other->opacity;
270     case GTK_CSS_PROPERTY_FILTER:
271       return style->other->filter;
272     case GTK_CSS_PROPERTY_CARET_COLOR:
273       return style->font->caret_color ? style->font->caret_color : style->core->color;
274     case GTK_CSS_PROPERTY_SECONDARY_CARET_COLOR:
275       return style->font->secondary_caret_color ? style->font->secondary_caret_color : style->core->color;
276     case GTK_CSS_PROPERTY_FONT_FEATURE_SETTINGS:
277       return style->font->font_feature_settings;
278     case GTK_CSS_PROPERTY_FONT_VARIATION_SETTINGS:
279       return style->font->font_variation_settings;
280 
281     default:
282       g_assert_not_reached ();
283     }
284 }
285 
286 GtkCssSection *
gtk_css_style_get_section(GtkCssStyle * style,guint id)287 gtk_css_style_get_section (GtkCssStyle *style,
288                            guint        id)
289 {
290   gtk_internal_return_val_if_fail (GTK_IS_CSS_STYLE (style), NULL);
291 
292   return GTK_CSS_STYLE_GET_CLASS (style)->get_section (style, id);
293 }
294 
295 gboolean
gtk_css_style_is_static(GtkCssStyle * style)296 gtk_css_style_is_static (GtkCssStyle *style)
297 {
298   return GTK_CSS_STYLE_GET_CLASS (style)->is_static (style);
299 }
300 
301 GtkCssStaticStyle *
gtk_css_style_get_static_style(GtkCssStyle * style)302 gtk_css_style_get_static_style (GtkCssStyle *style)
303 {
304   gtk_internal_return_val_if_fail (GTK_IS_CSS_STYLE (style), NULL);
305 
306   return GTK_CSS_STYLE_GET_CLASS (style)->get_static_style (style);
307 }
308 
309 /*
310  * gtk_css_style_print:
311  * @style: a `GtkCssStyle`
312  * @string: the `GString` to print to
313  * @indent: level of indentation to use
314  * @skip_initial: %TRUE to skip properties that have their initial value
315  *
316  * Print the @style to @string, in CSS format. Every property is printed
317  * on a line by itself, indented by @indent spaces. If @skip_initial is
318  * %TRUE, properties are only printed if their value in @style is different
319  * from the initial value of the property.
320  *
321  * Returns: %TRUE is any properties have been printed
322  */
323 gboolean
gtk_css_style_print(GtkCssStyle * style,GString * string,guint indent,gboolean skip_initial)324 gtk_css_style_print (GtkCssStyle *style,
325                      GString     *string,
326                      guint        indent,
327                      gboolean     skip_initial)
328 {
329   guint i;
330   gboolean retval = FALSE;
331 
332   g_return_val_if_fail (GTK_IS_CSS_STYLE (style), FALSE);
333   g_return_val_if_fail (string != NULL, FALSE);
334 
335   for (i = 0; i < _gtk_css_style_property_get_n_properties (); i++)
336     {
337       GtkCssSection *section;
338       GtkCssStyleProperty *prop;
339       GtkCssValue *value;
340       const char *name;
341 
342       section = gtk_css_style_get_section (style, i);
343       if (!section && skip_initial)
344         continue;
345 
346       prop = _gtk_css_style_property_lookup_by_id (i);
347       name = _gtk_style_property_get_name (GTK_STYLE_PROPERTY (prop));
348       value = gtk_css_style_get_value (style, i);
349 
350       g_string_append_printf (string, "%*s%s: ", indent, "", name);
351       _gtk_css_value_print (value, string);
352       g_string_append_c (string, ';');
353 
354       if (section)
355         {
356           g_string_append (string, " /* ");
357           gtk_css_section_print (section, string);
358           g_string_append (string, " */");
359         }
360 
361       g_string_append_c (string, '\n');
362 
363       retval = TRUE;
364     }
365 
366   return retval;
367 }
368 
369 char *
gtk_css_style_to_string(GtkCssStyle * style)370 gtk_css_style_to_string (GtkCssStyle *style)
371 {
372   GString *string;
373 
374   g_return_val_if_fail (GTK_IS_CSS_STYLE (style), NULL);
375 
376   string = g_string_new ("");
377 
378   gtk_css_style_print (style, string, 0, FALSE);
379 
380   return g_string_free (string, FALSE);
381 }
382 
383 static PangoUnderline
get_pango_underline_from_style(GtkTextDecorationStyle style)384 get_pango_underline_from_style (GtkTextDecorationStyle style)
385 {
386   switch (style)
387     {
388     case GTK_CSS_TEXT_DECORATION_STYLE_DOUBLE:
389       return PANGO_UNDERLINE_DOUBLE;
390     case GTK_CSS_TEXT_DECORATION_STYLE_WAVY:
391       return PANGO_UNDERLINE_ERROR;
392     case GTK_CSS_TEXT_DECORATION_STYLE_SOLID:
393     default:
394       return PANGO_UNDERLINE_SINGLE;
395     }
396 
397   g_return_val_if_reached (PANGO_UNDERLINE_SINGLE);
398 }
399 
400 static PangoOverline
get_pango_overline_from_style(GtkTextDecorationStyle style)401 get_pango_overline_from_style (GtkTextDecorationStyle style)
402 {
403   return PANGO_OVERLINE_SINGLE;
404 }
405 
406 static PangoAttrList *
add_pango_attr(PangoAttrList * attrs,PangoAttribute * attr)407 add_pango_attr (PangoAttrList  *attrs,
408                 PangoAttribute *attr)
409 {
410   if (attrs == NULL)
411     attrs = pango_attr_list_new ();
412 
413   pango_attr_list_insert (attrs, attr);
414 
415   return attrs;
416 }
417 
418 static void
append_separated(GString ** s,const char * text)419 append_separated (GString    **s,
420                   const char  *text)
421 {
422   if (G_UNLIKELY (!*s))
423     *s = g_string_new (NULL);
424 
425   if ((*s)->len > 0)
426     g_string_append (*s, ", ");
427 
428   g_string_append (*s, text);
429 }
430 
431 char *
gtk_css_style_compute_font_features(GtkCssStyle * style)432 gtk_css_style_compute_font_features (GtkCssStyle *style)
433 {
434   GtkCssFontVariantLigature ligatures;
435   GtkCssFontVariantNumeric numeric;
436   GtkCssFontVariantEastAsian east_asian;
437   char *settings;
438   GString *s = NULL;
439 
440   switch (_gtk_css_font_kerning_value_get (style->font_variant->font_kerning))
441     {
442     case GTK_CSS_FONT_KERNING_NORMAL:
443       append_separated (&s, "kern 1");
444       break;
445     case GTK_CSS_FONT_KERNING_NONE:
446       append_separated (&s, "kern 0");
447       break;
448     case GTK_CSS_FONT_KERNING_AUTO:
449     default:
450       break;
451     }
452 
453   ligatures = _gtk_css_font_variant_ligature_value_get (style->font_variant->font_variant_ligatures);
454   if (ligatures == GTK_CSS_FONT_VARIANT_LIGATURE_NORMAL)
455     {
456       /* all defaults */
457     }
458   else if (ligatures == GTK_CSS_FONT_VARIANT_LIGATURE_NONE)
459     append_separated (&s, "liga 0, clig 0, dlig 0, hlig 0, calt 0");
460   else
461     {
462       if (ligatures & GTK_CSS_FONT_VARIANT_LIGATURE_COMMON_LIGATURES)
463         append_separated (&s, "liga 1, clig 1");
464       if (ligatures & GTK_CSS_FONT_VARIANT_LIGATURE_NO_COMMON_LIGATURES)
465         append_separated (&s, "liga 0, clig 0");
466       if (ligatures & GTK_CSS_FONT_VARIANT_LIGATURE_DISCRETIONARY_LIGATURES)
467         append_separated (&s, "dlig 1");
468       if (ligatures & GTK_CSS_FONT_VARIANT_LIGATURE_NO_DISCRETIONARY_LIGATURES)
469         append_separated (&s, "dlig 0");
470       if (ligatures & GTK_CSS_FONT_VARIANT_LIGATURE_HISTORICAL_LIGATURES)
471         append_separated (&s, "hlig 1");
472       if (ligatures & GTK_CSS_FONT_VARIANT_LIGATURE_NO_HISTORICAL_LIGATURES)
473         append_separated (&s, "hlig 0");
474       if (ligatures & GTK_CSS_FONT_VARIANT_LIGATURE_CONTEXTUAL)
475         append_separated (&s, "calt 1");
476       if (ligatures & GTK_CSS_FONT_VARIANT_LIGATURE_NO_CONTEXTUAL)
477         append_separated (&s, "calt 0");
478     }
479 
480   switch (_gtk_css_font_variant_position_value_get (style->font_variant->font_variant_position))
481     {
482     case GTK_CSS_FONT_VARIANT_POSITION_SUB:
483       append_separated (&s, "subs 1");
484       break;
485     case GTK_CSS_FONT_VARIANT_POSITION_SUPER:
486       append_separated (&s, "sups 1");
487       break;
488     case GTK_CSS_FONT_VARIANT_POSITION_NORMAL:
489     default:
490       break;
491     }
492 
493   switch (_gtk_css_font_variant_caps_value_get (style->font_variant->font_variant_caps))
494     {
495     case GTK_CSS_FONT_VARIANT_CAPS_SMALL_CAPS:
496       append_separated (&s, "smcp 1");
497       break;
498     case GTK_CSS_FONT_VARIANT_CAPS_ALL_SMALL_CAPS:
499       append_separated (&s, "c2sc 1, smcp 1");
500       break;
501     case GTK_CSS_FONT_VARIANT_CAPS_PETITE_CAPS:
502       append_separated (&s, "pcap 1");
503       break;
504     case GTK_CSS_FONT_VARIANT_CAPS_ALL_PETITE_CAPS:
505       append_separated (&s, "c2pc 1, pcap 1");
506       break;
507     case GTK_CSS_FONT_VARIANT_CAPS_UNICASE:
508       append_separated (&s, "unic 1");
509       break;
510     case GTK_CSS_FONT_VARIANT_CAPS_TITLING_CAPS:
511       append_separated (&s, "titl 1");
512       break;
513     case GTK_CSS_FONT_VARIANT_CAPS_NORMAL:
514     default:
515       break;
516     }
517 
518   numeric = _gtk_css_font_variant_numeric_value_get (style->font_variant->font_variant_numeric);
519   if (numeric == GTK_CSS_FONT_VARIANT_NUMERIC_NORMAL)
520     {
521       /* all defaults */
522     }
523   else
524     {
525       if (numeric & GTK_CSS_FONT_VARIANT_NUMERIC_LINING_NUMS)
526         append_separated (&s, "lnum 1");
527       if (numeric & GTK_CSS_FONT_VARIANT_NUMERIC_OLDSTYLE_NUMS)
528         append_separated (&s, "onum 1");
529       if (numeric & GTK_CSS_FONT_VARIANT_NUMERIC_PROPORTIONAL_NUMS)
530         append_separated (&s, "pnum 1");
531       if (numeric & GTK_CSS_FONT_VARIANT_NUMERIC_TABULAR_NUMS)
532         append_separated (&s, "tnum 1");
533       if (numeric & GTK_CSS_FONT_VARIANT_NUMERIC_DIAGONAL_FRACTIONS)
534         append_separated (&s, "frac 1");
535       if (numeric & GTK_CSS_FONT_VARIANT_NUMERIC_STACKED_FRACTIONS)
536         append_separated (&s, "afrc 1");
537       if (numeric & GTK_CSS_FONT_VARIANT_NUMERIC_ORDINAL)
538         append_separated (&s, "ordn 1");
539       if (numeric & GTK_CSS_FONT_VARIANT_NUMERIC_SLASHED_ZERO)
540         append_separated (&s, "zero 1");
541     }
542 
543   switch (_gtk_css_font_variant_alternate_value_get (style->font_variant->font_variant_alternates))
544     {
545     case GTK_CSS_FONT_VARIANT_ALTERNATE_HISTORICAL_FORMS:
546       append_separated (&s, "hist 1");
547       break;
548     case GTK_CSS_FONT_VARIANT_ALTERNATE_NORMAL:
549     default:
550       break;
551     }
552 
553   east_asian = _gtk_css_font_variant_east_asian_value_get (style->font_variant->font_variant_east_asian);
554   if (east_asian == GTK_CSS_FONT_VARIANT_EAST_ASIAN_NORMAL)
555     {
556       /* all defaults */
557     }
558   else
559     {
560       if (east_asian & GTK_CSS_FONT_VARIANT_EAST_ASIAN_JIS78)
561         append_separated (&s, "jp78 1");
562       if (east_asian & GTK_CSS_FONT_VARIANT_EAST_ASIAN_JIS83)
563         append_separated (&s, "jp83 1");
564       if (east_asian & GTK_CSS_FONT_VARIANT_EAST_ASIAN_JIS90)
565         append_separated (&s, "jp90 1");
566       if (east_asian & GTK_CSS_FONT_VARIANT_EAST_ASIAN_JIS04)
567         append_separated (&s, "jp04 1");
568       if (east_asian & GTK_CSS_FONT_VARIANT_EAST_ASIAN_SIMPLIFIED)
569         append_separated (&s, "smpl 1");
570       if (east_asian & GTK_CSS_FONT_VARIANT_EAST_ASIAN_TRADITIONAL)
571         append_separated (&s, "trad 1");
572       if (east_asian & GTK_CSS_FONT_VARIANT_EAST_ASIAN_FULL_WIDTH)
573         append_separated (&s, "fwid 1");
574       if (east_asian & GTK_CSS_FONT_VARIANT_EAST_ASIAN_PROPORTIONAL)
575         append_separated (&s, "pwid 1");
576       if (east_asian & GTK_CSS_FONT_VARIANT_EAST_ASIAN_RUBY)
577         append_separated (&s, "ruby 1");
578     }
579 
580   settings = gtk_css_font_features_value_get_features (style->font->font_feature_settings);
581   if (settings)
582     {
583       append_separated (&s, settings);
584       g_free (settings);
585     }
586 
587   if (s)
588     return g_string_free (s, FALSE);
589   else
590     return NULL;
591 }
592 
593 PangoAttrList *
gtk_css_style_get_pango_attributes(GtkCssStyle * style)594 gtk_css_style_get_pango_attributes (GtkCssStyle *style)
595 {
596   PangoAttrList *attrs = NULL;
597   GtkTextDecorationLine decoration_line;
598   GtkTextDecorationStyle decoration_style;
599   const GdkRGBA *color;
600   const GdkRGBA *decoration_color;
601   int letter_spacing;
602 
603   /* text-decoration */
604   decoration_line = _gtk_css_text_decoration_line_value_get (style->font_variant->text_decoration_line);
605   decoration_style = _gtk_css_text_decoration_style_value_get (style->font_variant->text_decoration_style);
606   color = gtk_css_color_value_get_rgba (style->core->color);
607   decoration_color = gtk_css_color_value_get_rgba (style->font_variant->text_decoration_color
608                                                    ? style->font_variant->text_decoration_color
609                                                    : style->core->color);
610 
611   if (decoration_line & GTK_CSS_TEXT_DECORATION_LINE_UNDERLINE)
612     {
613       attrs = add_pango_attr (attrs, pango_attr_underline_new (get_pango_underline_from_style (decoration_style)));
614       if (!gdk_rgba_equal (color, decoration_color))
615         attrs = add_pango_attr (attrs, pango_attr_underline_color_new (decoration_color->red * 65535. + 0.5,
616                                                                        decoration_color->green * 65535. + 0.5,
617                                                                        decoration_color->blue * 65535. + 0.5));
618     }
619   if (decoration_line & GTK_CSS_TEXT_DECORATION_LINE_OVERLINE)
620     {
621       attrs = add_pango_attr (attrs, pango_attr_overline_new (get_pango_overline_from_style (decoration_style)));
622       if (!gdk_rgba_equal (color, decoration_color))
623         attrs = add_pango_attr (attrs, pango_attr_overline_color_new (decoration_color->red * 65535. + 0.5,
624                                                                       decoration_color->green * 65535. + 0.5,
625                                                                       decoration_color->blue * 65535. + 0.5));
626     }
627   if (decoration_line & GTK_CSS_TEXT_DECORATION_LINE_LINE_THROUGH)
628     {
629       attrs = add_pango_attr (attrs, pango_attr_strikethrough_new (TRUE));
630       if (!gdk_rgba_equal (color, decoration_color))
631         attrs = add_pango_attr (attrs, pango_attr_strikethrough_color_new (decoration_color->red * 65535. + 0.5,
632                                                                            decoration_color->green * 65535. + 0.5,
633                                                                            decoration_color->blue * 65535. + 0.5));
634     }
635 
636   /* letter-spacing */
637   letter_spacing = _gtk_css_number_value_get (style->font->letter_spacing, 100);
638   if (letter_spacing != 0)
639     {
640       attrs = add_pango_attr (attrs, pango_attr_letter_spacing_new (letter_spacing * PANGO_SCALE));
641     }
642 
643   /* OpenType features */
644   {
645     char *font_features = gtk_css_style_compute_font_features (style);
646 
647     if (font_features)
648       {
649         attrs = add_pango_attr (attrs, pango_attr_font_features_new (font_features));
650         g_free (font_features);
651       }
652   }
653 
654   return attrs;
655 }
656 
657 PangoFontDescription *
gtk_css_style_get_pango_font(GtkCssStyle * style)658 gtk_css_style_get_pango_font (GtkCssStyle *style)
659 {
660   PangoFontDescription *description;
661   GtkCssValue *v;
662   char *str;
663 
664   description = pango_font_description_new ();
665 
666   v = style->font->font_family;
667   if (_gtk_css_array_value_get_n_values (v) > 1)
668     {
669       int i;
670       GString *s = g_string_new ("");
671 
672       for (i = 0; i < _gtk_css_array_value_get_n_values (v); i++)
673         {
674           if (i > 0)
675             g_string_append (s, ",");
676           g_string_append (s, _gtk_css_string_value_get (_gtk_css_array_value_get_nth (v, i)));
677         }
678 
679       pango_font_description_set_family (description, s->str);
680       g_string_free (s, TRUE);
681     }
682   else
683     {
684       pango_font_description_set_family (description,
685                                          _gtk_css_string_value_get (_gtk_css_array_value_get_nth (v, 0)));
686     }
687 
688   v = style->core->font_size;
689   pango_font_description_set_absolute_size (description, round (_gtk_css_number_value_get (v, 100) * PANGO_SCALE));
690 
691   v = style->font->font_style;
692   pango_font_description_set_style (description, _gtk_css_font_style_value_get (v));
693 
694   v = style->font->font_weight;
695   pango_font_description_set_weight (description, _gtk_css_number_value_get (v, 100));
696 
697   v = style->font->font_stretch;
698   pango_font_description_set_stretch (description, _gtk_css_font_stretch_value_get (v));
699 
700   v = style->font->font_variation_settings;
701   str = gtk_css_font_variations_value_get_variations (v);
702   if (str)
703     pango_font_description_set_variations (description, str);
704   g_free (str);
705 
706   return description;
707 }
708 
709 /* Refcounted value structs */
710 
711 static const int values_size[] = {
712   sizeof (GtkCssCoreValues),
713   sizeof (GtkCssBackgroundValues),
714   sizeof (GtkCssBorderValues),
715   sizeof (GtkCssIconValues),
716   sizeof (GtkCssOutlineValues),
717   sizeof (GtkCssFontValues),
718   sizeof (GtkCssFontVariantValues),
719   sizeof (GtkCssAnimationValues),
720   sizeof (GtkCssTransitionValues),
721   sizeof (GtkCssSizeValues),
722   sizeof (GtkCssOtherValues)
723 };
724 
725 #define TYPE_INDEX(type) ((type) - ((type) % 2))
726 #define VALUES_SIZE(type) (values_size[(type) / 2])
727 #define N_VALUES(type) ((VALUES_SIZE(type) - sizeof (GtkCssValues)) / sizeof (GtkCssValue *))
728 
729 #define GET_VALUES(v) (GtkCssValue **)((guint8 *)(v) + sizeof (GtkCssValues))
730 
gtk_css_values_ref(GtkCssValues * values)731 GtkCssValues *gtk_css_values_ref (GtkCssValues *values)
732 {
733   values->ref_count++;
734 
735   return values;
736 }
737 
738 static void
gtk_css_values_free(GtkCssValues * values)739 gtk_css_values_free (GtkCssValues *values)
740 {
741   int i;
742   GtkCssValue **v = GET_VALUES (values);
743 
744   for (i = 0; i < N_VALUES (values->type); i++)
745     {
746       if (v[i])
747         gtk_css_value_unref (v[i]);
748     }
749 
750   g_free (values);
751 }
752 
gtk_css_values_unref(GtkCssValues * values)753 void gtk_css_values_unref (GtkCssValues *values)
754 {
755   if (!values)
756     return;
757 
758   values->ref_count--;
759 
760   if (values->ref_count == 0)
761     gtk_css_values_free (values);
762 }
763 
764 GtkCssValues *
gtk_css_values_copy(GtkCssValues * values)765 gtk_css_values_copy (GtkCssValues *values)
766 {
767   GtkCssValues *copy;
768   GtkCssValue **v, **v2;
769   int i;
770 
771   copy = gtk_css_values_new (TYPE_INDEX(values->type));
772 
773   v = GET_VALUES (values);
774   v2 = GET_VALUES (copy);
775 
776   for (i = 0; i < N_VALUES (values->type); i++)
777     {
778       if (v[i])
779         v2[i] = gtk_css_value_ref (v[i]);
780     }
781 
782   return copy;
783 }
784 
785 GtkCssValues *
gtk_css_values_new(GtkCssValuesType type)786 gtk_css_values_new (GtkCssValuesType type)
787 {
788   GtkCssValues *values;
789 
790   values = (GtkCssValues *)g_malloc0 (VALUES_SIZE(type));
791   values->ref_count = 1;
792   values->type = type;
793 
794   return values;
795 }
796