1/* gtkpango.c - pango-related utilities
2 *
3 * Copyright (c) 2010 Red Hat, Inc.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library. If not, see <http://www.gnu.org/licenses/>.Free
17 */
18/*
19 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
20 * file for a list of people on the GTK+ Team. See the ChangeLog
21 * files for a list of changes. These files are distributed with
22 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
23 */
24
25#include "config.h"
26#include "gtkpango.h"
27#include <pango/pangocairo.h>
28#include "gtkintl.h"
29#include "gtkbuilderprivate.h"
30
31static gboolean
32attr_list_merge_filter (PangoAttribute *attribute,
33 gpointer list)
34{
35 pango_attr_list_change (list, attr: pango_attribute_copy (attr: attribute));
36 return FALSE;
37}
38
39/*
40 * _gtk_pango_attr_list_merge:
41 * @into: (nullable): a `PangoAttrList` where attributes are merged
42 * @from: (nullable): a `PangoAttrList` with the attributes to merge
43 *
44 * Merges attributes from @from into @into.
45 *
46 * Returns: the merged list.
47 */
48PangoAttrList *
49_gtk_pango_attr_list_merge (PangoAttrList *into,
50 PangoAttrList *from)
51{
52 if (from)
53 {
54 if (into)
55 pango_attr_list_filter (list: from, func: attr_list_merge_filter, data: into);
56 else
57 return pango_attr_list_ref (list: from);
58 }
59
60 return into;
61}
62
63static PangoAttribute *
64attribute_from_text (GtkBuilder *builder,
65 const char *name,
66 const char *value,
67 GError **error)
68{
69 PangoAttribute *attribute = NULL;
70 PangoAttrType type;
71 PangoLanguage *language;
72 PangoFontDescription *font_desc;
73 GdkRGBA *color;
74 GValue val = G_VALUE_INIT;
75
76 if (!gtk_builder_value_from_string_type (builder, type: PANGO_TYPE_ATTR_TYPE, string: name, value: &val, error))
77 return NULL;
78
79 type = g_value_get_enum (value: &val);
80 g_value_unset (value: &val);
81
82 switch (type)
83 {
84 /* PangoAttrLanguage */
85 case PANGO_ATTR_LANGUAGE:
86 if ((language = pango_language_from_string (language: value)))
87 {
88 attribute = pango_attr_language_new (language);
89 g_value_init (value: &val, G_TYPE_INT);
90 }
91 break;
92 /* PangoAttrInt */
93 case PANGO_ATTR_STYLE:
94 if (gtk_builder_value_from_string_type (builder, type: PANGO_TYPE_STYLE, string: value, value: &val, error))
95 attribute = pango_attr_style_new (style: g_value_get_enum (value: &val));
96 break;
97 case PANGO_ATTR_WEIGHT:
98 if (gtk_builder_value_from_string_type (builder, type: PANGO_TYPE_WEIGHT, string: value, value: &val, error))
99 attribute = pango_attr_weight_new (weight: g_value_get_enum (value: &val));
100 break;
101 case PANGO_ATTR_VARIANT:
102 if (gtk_builder_value_from_string_type (builder, type: PANGO_TYPE_VARIANT, string: value, value: &val, error))
103 attribute = pango_attr_variant_new (variant: g_value_get_enum (value: &val));
104 break;
105 case PANGO_ATTR_STRETCH:
106 if (gtk_builder_value_from_string_type (builder, type: PANGO_TYPE_STRETCH, string: value, value: &val, error))
107 attribute = pango_attr_stretch_new (stretch: g_value_get_enum (value: &val));
108 break;
109 case PANGO_ATTR_UNDERLINE:
110 if (gtk_builder_value_from_string_type (builder, type: PANGO_TYPE_UNDERLINE, string: value, value: &val, NULL))
111 attribute = pango_attr_underline_new (underline: g_value_get_enum (value: &val));
112 else
113 {
114 /* XXX: allow boolean for backwards compat, so ignore error */
115 /* Deprecate this somehow */
116 g_value_unset (value: &val);
117 if (gtk_builder_value_from_string_type (builder, G_TYPE_BOOLEAN, string: value, value: &val, error))
118 attribute = pango_attr_underline_new (underline: g_value_get_boolean (value: &val));
119 }
120 break;
121 case PANGO_ATTR_STRIKETHROUGH:
122 if (gtk_builder_value_from_string_type (builder, G_TYPE_BOOLEAN, string: value, value: &val, error))
123 attribute = pango_attr_strikethrough_new (strikethrough: g_value_get_boolean (value: &val));
124 break;
125 case PANGO_ATTR_GRAVITY:
126 if (gtk_builder_value_from_string_type (builder, type: PANGO_TYPE_GRAVITY, string: value, value: &val, error))
127 attribute = pango_attr_gravity_new (gravity: g_value_get_enum (value: &val));
128 break;
129 case PANGO_ATTR_GRAVITY_HINT:
130 if (gtk_builder_value_from_string_type (builder, type: PANGO_TYPE_GRAVITY_HINT, string: value, value: &val, error))
131 attribute = pango_attr_gravity_hint_new (hint: g_value_get_enum (value: &val));
132 break;
133 /* PangoAttrString */
134 case PANGO_ATTR_FAMILY:
135 attribute = pango_attr_family_new (family: value);
136 g_value_init (value: &val, G_TYPE_INT);
137 break;
138
139 /* PangoAttrSize */
140 case PANGO_ATTR_SIZE:
141 if (gtk_builder_value_from_string_type (builder, G_TYPE_INT, string: value, value: &val, error))
142 attribute = pango_attr_size_new (size: g_value_get_int (value: &val));
143 break;
144 case PANGO_ATTR_ABSOLUTE_SIZE:
145 if (gtk_builder_value_from_string_type (builder, G_TYPE_INT, string: value, value: &val, error))
146 attribute = pango_attr_size_new_absolute (size: g_value_get_int (value: &val));
147 break;
148
149 /* PangoAttrFontDesc */
150 case PANGO_ATTR_FONT_DESC:
151 if ((font_desc = pango_font_description_from_string (str: value)))
152 {
153 attribute = pango_attr_font_desc_new (desc: font_desc);
154 pango_font_description_free (desc: font_desc);
155 g_value_init (value: &val, G_TYPE_INT);
156 }
157 break;
158 /* PangoAttrColor */
159 case PANGO_ATTR_FOREGROUND:
160 if (gtk_builder_value_from_string_type (builder, GDK_TYPE_RGBA, string: value, value: &val, error))
161 {
162 color = g_value_get_boxed (value: &val);
163 attribute = pango_attr_foreground_new (red: color->red * 65535,
164 green: color->green * 65535,
165 blue: color->blue * 65535);
166 }
167 break;
168 case PANGO_ATTR_BACKGROUND:
169 if (gtk_builder_value_from_string_type (builder, GDK_TYPE_RGBA, string: value, value: &val, error))
170 {
171 color = g_value_get_boxed (value: &val);
172 attribute = pango_attr_background_new (red: color->red * 65535,
173 green: color->green * 65535,
174 blue: color->blue * 65535);
175 }
176 break;
177 case PANGO_ATTR_UNDERLINE_COLOR:
178 if (gtk_builder_value_from_string_type (builder, GDK_TYPE_RGBA, string: value, value: &val, error))
179 {
180 color = g_value_get_boxed (value: &val);
181 attribute = pango_attr_underline_color_new (red: color->red * 65535,
182 green: color->green * 65535,
183 blue: color->blue * 65535);
184 }
185 break;
186 case PANGO_ATTR_STRIKETHROUGH_COLOR:
187 if (gtk_builder_value_from_string_type (builder, GDK_TYPE_RGBA, string: value, value: &val, error))
188 {
189 color = g_value_get_boxed (value: &val);
190 attribute = pango_attr_strikethrough_color_new (red: color->red * 65535,
191 green: color->green * 65535,
192 blue: color->blue * 65535);
193 }
194 break;
195 /* PangoAttrShape */
196 case PANGO_ATTR_SHAPE:
197 /* Unsupported for now */
198 break;
199 /* PangoAttrFloat */
200 case PANGO_ATTR_SCALE:
201 if (gtk_builder_value_from_string_type (builder, G_TYPE_DOUBLE, string: value, value: &val, error))
202 attribute = pango_attr_scale_new (scale_factor: g_value_get_double (value: &val));
203 break;
204 case PANGO_ATTR_LETTER_SPACING:
205 if (gtk_builder_value_from_string_type (builder, G_TYPE_INT, string: value, value: &val, error))
206 attribute = pango_attr_letter_spacing_new (letter_spacing: g_value_get_int (value: &val));
207 break;
208 case PANGO_ATTR_RISE:
209 if (gtk_builder_value_from_string_type (builder, G_TYPE_INT, string: value, value: &val, error))
210 attribute = pango_attr_rise_new (rise: g_value_get_int (value: &val));
211 break;
212 case PANGO_ATTR_FALLBACK:
213 if (gtk_builder_value_from_string_type (builder, G_TYPE_BOOLEAN, string: value, value: &val, error))
214 attribute = pango_attr_fallback_new (enable_fallback: g_value_get_boolean (value: &val));
215 break;
216 case PANGO_ATTR_FONT_FEATURES:
217 attribute = pango_attr_font_features_new (features: value);
218 break;
219 case PANGO_ATTR_FOREGROUND_ALPHA:
220 if (gtk_builder_value_from_string_type (builder, G_TYPE_INT, string: value, value: &val, error))
221 attribute = pango_attr_foreground_alpha_new (alpha: (guint16)g_value_get_int (value: &val));
222 break;
223 case PANGO_ATTR_BACKGROUND_ALPHA:
224 if (gtk_builder_value_from_string_type (builder, G_TYPE_INT, string: value, value: &val, error))
225 attribute = pango_attr_background_alpha_new (alpha: (guint16)g_value_get_int (value: &val));
226 break;
227 case PANGO_ATTR_ALLOW_BREAKS:
228 if (gtk_builder_value_from_string_type (builder, G_TYPE_BOOLEAN, string: value, value: &val, error))
229 attribute = pango_attr_allow_breaks_new (allow_breaks: g_value_get_boolean (value: &val));
230 break;
231 case PANGO_ATTR_SHOW:
232 if (gtk_builder_value_from_string_type (builder, type: PANGO_TYPE_SHOW_FLAGS, string: value, value: &val, error))
233 attribute = pango_attr_show_new (flags: g_value_get_flags (value: &val));
234 break;
235 case PANGO_ATTR_INSERT_HYPHENS:
236 if (gtk_builder_value_from_string_type (builder, G_TYPE_BOOLEAN, string: value, value: &val, error))
237 attribute = pango_attr_insert_hyphens_new (insert_hyphens: g_value_get_boolean (value: &val));
238 break;
239 case PANGO_ATTR_OVERLINE:
240 if (gtk_builder_value_from_string_type (builder, type: PANGO_TYPE_OVERLINE, string: value, value: &val, NULL))
241 attribute = pango_attr_overline_new (overline: g_value_get_enum (value: &val));
242 break;
243 case PANGO_ATTR_OVERLINE_COLOR:
244 if (gtk_builder_value_from_string_type (builder, GDK_TYPE_RGBA, string: value, value: &val, error))
245 {
246 color = g_value_get_boxed (value: &val);
247 attribute = pango_attr_overline_color_new (red: color->red * 65535,
248 green: color->green * 65535,
249 blue: color->blue * 65535);
250 }
251 break;
252 case PANGO_ATTR_LINE_HEIGHT:
253 if (gtk_builder_value_from_string_type (builder, G_TYPE_DOUBLE, string: value, value: &val, error))
254 attribute = pango_attr_line_height_new (factor: g_value_get_double (value: &val));
255 break;
256 case PANGO_ATTR_ABSOLUTE_LINE_HEIGHT:
257 if (gtk_builder_value_from_string_type (builder, G_TYPE_INT, string: value, value: &val, error))
258 attribute = pango_attr_line_height_new_absolute (height: g_value_get_int (value: &val) * PANGO_SCALE);
259 break;
260 case PANGO_ATTR_TEXT_TRANSFORM:
261 if (gtk_builder_value_from_string_type (builder, type: PANGO_TYPE_TEXT_TRANSFORM, string: value, value: &val, error))
262 attribute = pango_attr_text_transform_new (transform: g_value_get_enum (value: &val));
263 break;
264 case PANGO_ATTR_WORD:
265 attribute = pango_attr_word_new ();
266 break;
267 case PANGO_ATTR_SENTENCE:
268 attribute = pango_attr_sentence_new ();
269 break;
270 case PANGO_ATTR_BASELINE_SHIFT:
271 if (gtk_builder_value_from_string_type (builder, type: PANGO_TYPE_BASELINE_SHIFT, string: value, value: &val, NULL))
272 attribute = pango_attr_baseline_shift_new (shift: g_value_get_enum (value: &val));
273 else if (gtk_builder_value_from_string_type (builder, G_TYPE_INT, string: value, value: &val, NULL))
274 attribute = pango_attr_baseline_shift_new (shift: g_value_get_enum (value: &val));
275 else
276 g_set_error (err: error,
277 GTK_BUILDER_ERROR,
278 code: GTK_BUILDER_ERROR_INVALID_VALUE,
279 format: "Could not parse '%s' as baseline shift value", value);
280 break;
281 case PANGO_ATTR_FONT_SCALE:
282 if (gtk_builder_value_from_string_type (builder, type: PANGO_TYPE_FONT_SCALE, string: value, value: &val, error))
283 attribute = pango_attr_font_scale_new (scale: g_value_get_enum (value: &val));
284 break;
285 case PANGO_ATTR_INVALID:
286 default:
287 break;
288 }
289
290 g_value_unset (value: &val);
291
292 return attribute;
293}
294
295void
296gtk_pango_attribute_start_element (GtkBuildableParseContext *context,
297 const char *element_name,
298 const char **names,
299 const char **values,
300 gpointer user_data,
301 GError **error)
302{
303 GtkPangoAttributeParserData *data = user_data;
304
305 if (strcmp (s1: element_name, s2: "attribute") == 0)
306 {
307 PangoAttribute *attr = NULL;
308 const char *name = NULL;
309 const char *value = NULL;
310 const char *start = NULL;
311 const char *end = NULL;
312 guint start_val = 0;
313 guint end_val = G_MAXUINT;
314 GValue val = G_VALUE_INIT;
315
316 if (!_gtk_builder_check_parent (builder: data->builder, context, parent_name: "attributes", error))
317 return;
318
319 if (!g_markup_collect_attributes (element_name, attribute_names: names, attribute_values: values, error,
320 first_type: G_MARKUP_COLLECT_STRING, first_attr: "name", &name,
321 G_MARKUP_COLLECT_STRING, "value", &value,
322 G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "start", &start,
323 G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "end", &end,
324 G_MARKUP_COLLECT_INVALID))
325 {
326 _gtk_builder_prefix_error (builder: data->builder, context, error);
327 return;
328 }
329
330 if (start)
331 {
332 if (!gtk_builder_value_from_string_type (builder: data->builder, G_TYPE_UINT, string: start, value: &val, error))
333 {
334 _gtk_builder_prefix_error (builder: data->builder, context, error);
335 return;
336 }
337 start_val = g_value_get_uint (value: &val);
338 g_value_unset (value: &val);
339 }
340
341 if (end)
342 {
343 if (!gtk_builder_value_from_string_type (builder: data->builder, G_TYPE_UINT, string: end, value: &val, error))
344 {
345 _gtk_builder_prefix_error (builder: data->builder, context, error);
346 return;
347 }
348 end_val = g_value_get_uint (value: &val);
349 g_value_unset (value: &val);
350 }
351
352 attr = attribute_from_text (builder: data->builder, name, value, error);
353 if (!attr)
354 {
355 _gtk_builder_prefix_error (builder: data->builder, context, error);
356 return;
357 }
358
359 attr->start_index = start_val;
360 attr->end_index = end_val;
361
362 if (!data->attrs)
363 data->attrs = pango_attr_list_new ();
364
365 pango_attr_list_insert (list: data->attrs, attr);
366 }
367 else if (strcmp (s1: element_name, s2: "attributes") == 0)
368 {
369 if (!_gtk_builder_check_parent (builder: data->builder, context, parent_name: "object", error))
370 return;
371
372 if (!g_markup_collect_attributes (element_name, attribute_names: names, attribute_values: values, error,
373 first_type: G_MARKUP_COLLECT_INVALID, NULL, NULL,
374 G_MARKUP_COLLECT_INVALID))
375 _gtk_builder_prefix_error (builder: data->builder, context, error);
376 }
377 else
378 {
379 _gtk_builder_error_unhandled_tag (builder: data->builder, context,
380 object: "GtkWidget", element_name,
381 error);
382 }
383}
384

source code of gtk/gtk/gtkpango.c