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