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 | |
31 | static gboolean |
32 | attr_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 | */ |
48 | PangoAttrList * |
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 | |
63 | static PangoAttribute * |
64 | attribute_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 | |
295 | void |
296 | gtk_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 | |