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 "gtkcssrgbavalueprivate.h"
32 #include "gtkcsssectionprivate.h"
33 #include "gtkcssshorthandpropertyprivate.h"
34 #include "gtkcssstringvalueprivate.h"
35 #include "gtkcssstylepropertyprivate.h"
36 #include "gtkcsstransitionprivate.h"
37 #include "gtkstyleanimationprivate.h"
38 #include "gtkstylepropertyprivate.h"
39 #include "gtkstyleproviderprivate.h"
40 
G_DEFINE_ABSTRACT_TYPE(GtkCssStyle,gtk_css_style,G_TYPE_OBJECT)41 G_DEFINE_ABSTRACT_TYPE (GtkCssStyle, gtk_css_style, G_TYPE_OBJECT)
42 
43 static GtkCssSection *
44 gtk_css_style_real_get_section (GtkCssStyle *style,
45                                 guint        id)
46 {
47   return NULL;
48 }
49 
50 static gboolean
gtk_css_style_real_is_static(GtkCssStyle * style)51 gtk_css_style_real_is_static (GtkCssStyle *style)
52 {
53   return TRUE;
54 }
55 
56 static void
gtk_css_style_class_init(GtkCssStyleClass * klass)57 gtk_css_style_class_init (GtkCssStyleClass *klass)
58 {
59   klass->get_section = gtk_css_style_real_get_section;
60   klass->is_static = gtk_css_style_real_is_static;
61 }
62 
63 static void
gtk_css_style_init(GtkCssStyle * style)64 gtk_css_style_init (GtkCssStyle *style)
65 {
66 }
67 
68 GtkCssValue *
gtk_css_style_get_value(GtkCssStyle * style,guint id)69 gtk_css_style_get_value (GtkCssStyle *style,
70                           guint        id)
71 {
72   gtk_internal_return_val_if_fail (GTK_IS_CSS_STYLE (style), NULL);
73 
74   return GTK_CSS_STYLE_GET_CLASS (style)->get_value (style, id);
75 }
76 
77 GtkCssSection *
gtk_css_style_get_section(GtkCssStyle * style,guint id)78 gtk_css_style_get_section (GtkCssStyle *style,
79                            guint        id)
80 {
81   gtk_internal_return_val_if_fail (GTK_IS_CSS_STYLE (style), NULL);
82 
83   return GTK_CSS_STYLE_GET_CLASS (style)->get_section (style, id);
84 }
85 
86 GtkBitmask *
gtk_css_style_add_difference(GtkBitmask * accumulated,GtkCssStyle * style,GtkCssStyle * other)87 gtk_css_style_add_difference (GtkBitmask  *accumulated,
88                               GtkCssStyle *style,
89                               GtkCssStyle *other)
90 {
91   gint len, i;
92 
93   if (style == other)
94     return accumulated;
95 
96   len = _gtk_css_style_property_get_n_properties ();
97   for (i = 0; i < len; i++)
98     {
99       if (_gtk_bitmask_get (accumulated, i))
100         continue;
101 
102       if (!_gtk_css_value_equal (gtk_css_style_get_value (style, i),
103                                  gtk_css_style_get_value (other, i)))
104         accumulated = _gtk_bitmask_set (accumulated, i, TRUE);
105     }
106 
107   return accumulated;
108 }
109 
110 gboolean
gtk_css_style_is_static(GtkCssStyle * style)111 gtk_css_style_is_static (GtkCssStyle *style)
112 {
113   gtk_internal_return_val_if_fail (GTK_IS_CSS_STYLE (style), TRUE);
114 
115   return GTK_CSS_STYLE_GET_CLASS (style)->is_static (style);
116 }
117 
118 /*
119  * gtk_css_style_print:
120  * @style: a #GtkCssStyle
121  * @string: the #GString to print to
122  * @indent: level of indentation to use
123  * @skip_initial: %TRUE to skip properties that have their initial value
124  *
125  * Print the @style to @string, in CSS format. Every property is printed
126  * on a line by itself, indented by @indent spaces. If @skip_initial is
127  * %TRUE, properties are only printed if their value in @style is different
128  * from the initial value of the property.
129  *
130  * Returns: %TRUE is any properties have been printed
131  */
132 gboolean
gtk_css_style_print(GtkCssStyle * style,GString * string,guint indent,gboolean skip_initial)133 gtk_css_style_print (GtkCssStyle *style,
134                      GString     *string,
135                      guint        indent,
136                      gboolean     skip_initial)
137 {
138   guint i;
139   gboolean retval = FALSE;
140 
141   g_return_val_if_fail (GTK_IS_CSS_STYLE (style), FALSE);
142   g_return_val_if_fail (string != NULL, FALSE);
143 
144   for (i = 0; i < _gtk_css_style_property_get_n_properties (); i++)
145     {
146       GtkCssSection *section;
147       GtkCssStyleProperty *prop;
148       GtkCssValue *value;
149       const char *name;
150 
151       section = gtk_css_style_get_section (style, i);
152       if (!section && skip_initial)
153         continue;
154 
155       prop = _gtk_css_style_property_lookup_by_id (i);
156       name = _gtk_style_property_get_name (GTK_STYLE_PROPERTY (prop));
157       value = gtk_css_style_get_value (style, i);
158 
159       g_string_append_printf (string, "%*s%s: ", indent, "", name);
160       _gtk_css_value_print (value, string);
161       g_string_append_c (string, ';');
162 
163       if (section)
164         {
165           g_string_append (string, " /* ");
166           _gtk_css_section_print (section, string);
167           g_string_append (string, " */");
168         }
169 
170       g_string_append_c (string, '\n');
171 
172       retval = TRUE;
173     }
174 
175   return retval;
176 }
177 
178 char *
gtk_css_style_to_string(GtkCssStyle * style)179 gtk_css_style_to_string (GtkCssStyle *style)
180 {
181   GString *string;
182 
183   g_return_val_if_fail (GTK_IS_CSS_STYLE (style), NULL);
184 
185   string = g_string_new ("");
186 
187   gtk_css_style_print (style, string, 0, FALSE);
188 
189   return g_string_free (string, FALSE);
190 }
191 
192 static PangoUnderline
get_pango_underline_from_style(GtkTextDecorationStyle style)193 get_pango_underline_from_style (GtkTextDecorationStyle style)
194 {
195   switch (style)
196     {
197     case GTK_CSS_TEXT_DECORATION_STYLE_DOUBLE:
198       return PANGO_UNDERLINE_DOUBLE;
199     case GTK_CSS_TEXT_DECORATION_STYLE_WAVY:
200       return PANGO_UNDERLINE_ERROR;
201     case GTK_CSS_TEXT_DECORATION_STYLE_SOLID:
202     default:
203       return PANGO_UNDERLINE_SINGLE;
204     }
205 
206   g_return_val_if_reached (PANGO_UNDERLINE_SINGLE);
207 }
208 
209 static PangoAttrList *
add_pango_attr(PangoAttrList * attrs,PangoAttribute * attr)210 add_pango_attr (PangoAttrList  *attrs,
211                 PangoAttribute *attr)
212 {
213   if (attrs == NULL)
214     attrs = pango_attr_list_new ();
215 
216   pango_attr_list_insert (attrs, attr);
217 
218   return attrs;
219 }
220 
221 PangoAttrList *
gtk_css_style_get_pango_attributes(GtkCssStyle * style)222 gtk_css_style_get_pango_attributes (GtkCssStyle *style)
223 {
224   PangoAttrList *attrs = NULL;
225   GtkTextDecorationLine decoration_line;
226   GtkTextDecorationStyle decoration_style;
227   const GdkRGBA *color;
228   const GdkRGBA *decoration_color;
229   gint letter_spacing;
230   const char *font_feature_settings;
231 
232   /* text-decoration */
233   decoration_line = _gtk_css_text_decoration_line_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_TEXT_DECORATION_LINE));
234   decoration_style = _gtk_css_text_decoration_style_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_TEXT_DECORATION_STYLE));
235   color = _gtk_css_rgba_value_get_rgba (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_COLOR));
236   decoration_color = _gtk_css_rgba_value_get_rgba (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_TEXT_DECORATION_COLOR));
237 
238   switch (decoration_line)
239     {
240     case GTK_CSS_TEXT_DECORATION_LINE_UNDERLINE:
241       attrs = add_pango_attr (attrs, pango_attr_underline_new (get_pango_underline_from_style (decoration_style)));
242       if (!gdk_rgba_equal (color, decoration_color))
243         attrs = add_pango_attr (attrs, pango_attr_underline_color_new (decoration_color->red * 65535. + 0.5,
244                                                                        decoration_color->green * 65535. + 0.5,
245                                                                        decoration_color->blue * 65535. + 0.5));
246       break;
247     case GTK_CSS_TEXT_DECORATION_LINE_LINE_THROUGH:
248       attrs = add_pango_attr (attrs, pango_attr_strikethrough_new (TRUE));
249       if (!gdk_rgba_equal (color, decoration_color))
250         attrs = add_pango_attr (attrs, pango_attr_strikethrough_color_new (decoration_color->red * 65535. + 0.5,
251                                                                            decoration_color->green * 65535. + 0.5,
252                                                                            decoration_color->blue * 65535. + 0.5));
253       break;
254     case GTK_CSS_TEXT_DECORATION_LINE_NONE:
255     default:
256       break;
257     }
258 
259   /* letter-spacing */
260   letter_spacing = _gtk_css_number_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_LETTER_SPACING), 100);
261   if (letter_spacing != 0)
262     {
263       attrs = add_pango_attr (attrs, pango_attr_letter_spacing_new (letter_spacing * PANGO_SCALE));
264     }
265 
266   /* font-feature-settings */
267   font_feature_settings = _gtk_css_string_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_FONT_FEATURE_SETTINGS));
268   if (font_feature_settings != NULL)
269     {
270       attrs = add_pango_attr (attrs, pango_attr_font_features_new (font_feature_settings));
271     }
272 
273   return attrs;
274 }
275 
276 static GtkCssValue *
query_func(guint id,gpointer values)277 query_func (guint    id,
278             gpointer values)
279 {
280   return gtk_css_style_get_value (values, id);
281 }
282 
283 PangoFontDescription *
gtk_css_style_get_pango_font(GtkCssStyle * style)284 gtk_css_style_get_pango_font (GtkCssStyle *style)
285 {
286   GtkStyleProperty *prop;
287   GValue value = { 0, };
288 
289   prop = _gtk_style_property_lookup ("font");
290   _gtk_style_property_query (prop, &value, query_func, style);
291 
292   return (PangoFontDescription *)g_value_get_boxed (&value);
293 }
294