File: | ctk/ctkpango.c |
Warning: | line 728, column 14 Access to field 'data' results in a dereference of a null pointer (loaded from variable 'l') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* ctkpango.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 CTK+ Team and others 1997-2000. See the AUTHORS | |||
20 | * file for a list of people on the CTK+ Team. See the ChangeLog | |||
21 | * files for a list of changes. These files are distributed with | |||
22 | * CTK+ at ftp://ftp.ctk.org/pub/ctk/. | |||
23 | */ | |||
24 | ||||
25 | #include "config.h" | |||
26 | #include "ctkpango.h" | |||
27 | #include <pango/pangocairo.h> | |||
28 | #include <fribidi.h> | |||
29 | #include "ctkintl.h" | |||
30 | ||||
31 | #define CTK_TYPE_FILL_LAYOUT_RENDERER(_ctk_fill_layout_renderer_get_type()) (_ctk_fill_layout_renderer_get_type()) | |||
32 | #define CTK_FILL_LAYOUT_RENDERER(object)((((CtkFillLayoutRenderer*) (void *) g_type_check_instance_cast ((GTypeInstance*) ((object)), ((_ctk_fill_layout_renderer_get_type ())))))) (G_TYPE_CHECK_INSTANCE_CAST ((object), CTK_TYPE_FILL_LAYOUT_RENDERER, CtkFillLayoutRenderer)(((CtkFillLayoutRenderer*) (void *) g_type_check_instance_cast ((GTypeInstance*) ((object)), ((_ctk_fill_layout_renderer_get_type ())))))) | |||
33 | #define CTK_IS_FILL_LAYOUT_RENDERER(object)(((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) ( (object)); GType __t = ((_ctk_fill_layout_renderer_get_type() )); gboolean __r; if (!__inst) __r = (0); else if (__inst-> g_class && __inst->g_class->g_type == __t) __r = (!(0)); else __r = g_type_check_instance_is_a (__inst, __t); __r; })))) (G_TYPE_CHECK_INSTANCE_TYPE ((object), CTK_TYPE_FILL_LAYOUT_RENDERER)((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) ( (object)); GType __t = ((_ctk_fill_layout_renderer_get_type() )); gboolean __r; if (!__inst) __r = (0); else if (__inst-> g_class && __inst->g_class->g_type == __t) __r = (!(0)); else __r = g_type_check_instance_is_a (__inst, __t); __r; })))) | |||
34 | #define CTK_FILL_LAYOUT_RENDERER_CLASS(klass)((((CtkFillLayoutRendererClass*) (void *) g_type_check_class_cast ((GTypeClass*) ((klass)), ((_ctk_fill_layout_renderer_get_type ())))))) (G_TYPE_CHECK_CLASS_CAST ((klass), CTK_TYPE_FILL_LAYOUT_RENDERER, CtkFillLayoutRendererClass)(((CtkFillLayoutRendererClass*) (void *) g_type_check_class_cast ((GTypeClass*) ((klass)), ((_ctk_fill_layout_renderer_get_type ())))))) | |||
35 | #define CTK_IS_FILL_LAYOUT_RENDERER_CLASS(klass)(((__extension__ ({ GTypeClass *__class = (GTypeClass*) ((klass )); GType __t = ((_ctk_fill_layout_renderer_get_type())); gboolean __r; if (!__class) __r = (0); else if (__class->g_type == __t) __r = (!(0)); else __r = g_type_check_class_is_a (__class , __t); __r; })))) (G_TYPE_CHECK_CLASS_TYPE ((klass), CTK_TYPE_FILL_LAYOUT_RENDERER)((__extension__ ({ GTypeClass *__class = (GTypeClass*) ((klass )); GType __t = ((_ctk_fill_layout_renderer_get_type())); gboolean __r; if (!__class) __r = (0); else if (__class->g_type == __t) __r = (!(0)); else __r = g_type_check_class_is_a (__class , __t); __r; })))) | |||
36 | #define CTK_FILL_LAYOUT_RENDERER_GET_CLASS(obj)((((CtkFillLayoutRendererClass*) (((GTypeInstance*) ((obj)))-> g_class)))) (G_TYPE_INSTANCE_GET_CLASS ((obj), CTK_TYPE_FILL_LAYOUT_RENDERER, CtkFillLayoutRendererClass)(((CtkFillLayoutRendererClass*) (((GTypeInstance*) ((obj)))-> g_class)))) | |||
37 | ||||
38 | typedef struct _CtkFillLayoutRenderer CtkFillLayoutRenderer; | |||
39 | typedef struct _CtkFillLayoutRendererClass CtkFillLayoutRendererClass; | |||
40 | ||||
41 | struct _CtkFillLayoutRenderer | |||
42 | { | |||
43 | PangoRenderer parent_instance; | |||
44 | ||||
45 | cairo_t *cr; | |||
46 | }; | |||
47 | ||||
48 | struct _CtkFillLayoutRendererClass | |||
49 | { | |||
50 | PangoRendererClass parent_class; | |||
51 | }; | |||
52 | ||||
53 | GType _ctk_fill_layout_renderer_get_type (void); | |||
54 | ||||
55 | G_DEFINE_TYPE (CtkFillLayoutRenderer, _ctk_fill_layout_renderer, PANGO_TYPE_RENDERER)static void _ctk_fill_layout_renderer_init (CtkFillLayoutRenderer *self); static void _ctk_fill_layout_renderer_class_init (CtkFillLayoutRendererClass *klass); static GType _ctk_fill_layout_renderer_get_type_once (void); static gpointer _ctk_fill_layout_renderer_parent_class = ((void*)0); static gint CtkFillLayoutRenderer_private_offset ; static void _ctk_fill_layout_renderer_class_intern_init (gpointer klass) { _ctk_fill_layout_renderer_parent_class = g_type_class_peek_parent (klass); if (CtkFillLayoutRenderer_private_offset != 0) g_type_class_adjust_private_offset (klass, &CtkFillLayoutRenderer_private_offset); _ctk_fill_layout_renderer_class_init ((CtkFillLayoutRendererClass*) klass); } __attribute__ ((__unused__ )) static inline gpointer _ctk_fill_layout_renderer_get_instance_private (CtkFillLayoutRenderer *self) { return (((gpointer) ((guint8 *) (self) + (glong) (CtkFillLayoutRenderer_private_offset)))) ; } GType _ctk_fill_layout_renderer_get_type (void) { static GType static_g_define_type_id = 0; if ((__extension__ ({ _Static_assert (sizeof *(&static_g_define_type_id) == sizeof (gpointer) , "Expression evaluates to false"); (void) (0 ? (gpointer) * ( &static_g_define_type_id) : ((void*)0)); (!(__extension__ ({ _Static_assert (sizeof *(&static_g_define_type_id) == sizeof (gpointer), "Expression evaluates to false"); __typeof__ (*(&static_g_define_type_id)) gapg_temp_newval; __typeof__ ((&static_g_define_type_id)) gapg_temp_atomic = (&static_g_define_type_id ); __atomic_load (gapg_temp_atomic, &gapg_temp_newval, 5) ; gapg_temp_newval; })) && g_once_init_enter_pointer ( &static_g_define_type_id)); })) ) { GType g_define_type_id = _ctk_fill_layout_renderer_get_type_once (); (__extension__ ({ _Static_assert (sizeof *(&static_g_define_type_id) == sizeof (gpointer), "Expression evaluates to false"); 0 ? (void ) (*(&static_g_define_type_id) = (g_define_type_id)) : (void ) 0; g_once_init_leave_pointer ((&static_g_define_type_id ), (gpointer) (guintptr) (g_define_type_id)); })) ; } return static_g_define_type_id ; } __attribute__ ((__noinline__)) static GType _ctk_fill_layout_renderer_get_type_once (void) { GType g_define_type_id = g_type_register_static_simple ((pango_renderer_get_type()), g_intern_static_string ("CtkFillLayoutRenderer" ), sizeof (CtkFillLayoutRendererClass), (GClassInitFunc)(void (*)(void)) _ctk_fill_layout_renderer_class_intern_init, sizeof (CtkFillLayoutRenderer), (GInstanceInitFunc)(void (*)(void)) _ctk_fill_layout_renderer_init, (GTypeFlags) 0); { {{};} } return g_define_type_id; } | |||
56 | ||||
57 | static void | |||
58 | ctk_fill_layout_renderer_draw_glyphs (PangoRenderer *renderer, | |||
59 | PangoFont *font, | |||
60 | PangoGlyphString *glyphs, | |||
61 | int x, | |||
62 | int y) | |||
63 | { | |||
64 | CtkFillLayoutRenderer *text_renderer = CTK_FILL_LAYOUT_RENDERER (renderer)((((CtkFillLayoutRenderer*) (void *) g_type_check_instance_cast ((GTypeInstance*) ((renderer)), ((_ctk_fill_layout_renderer_get_type ())))))); | |||
65 | ||||
66 | cairo_move_to (text_renderer->cr, (double)x / PANGO_SCALE1024, (double)y / PANGO_SCALE1024); | |||
67 | pango_cairo_show_glyph_string (text_renderer->cr, font, glyphs); | |||
68 | } | |||
69 | ||||
70 | static void | |||
71 | ctk_fill_layout_renderer_draw_glyph_item (PangoRenderer *renderer, | |||
72 | const char *text, | |||
73 | PangoGlyphItem *glyph_item, | |||
74 | int x, | |||
75 | int y) | |||
76 | { | |||
77 | CtkFillLayoutRenderer *text_renderer = CTK_FILL_LAYOUT_RENDERER (renderer)((((CtkFillLayoutRenderer*) (void *) g_type_check_instance_cast ((GTypeInstance*) ((renderer)), ((_ctk_fill_layout_renderer_get_type ())))))); | |||
78 | ||||
79 | cairo_move_to (text_renderer->cr, (double)x / PANGO_SCALE1024, (double)y / PANGO_SCALE1024); | |||
80 | pango_cairo_show_glyph_item (text_renderer->cr, text, glyph_item); | |||
81 | } | |||
82 | ||||
83 | static void | |||
84 | ctk_fill_layout_renderer_draw_rectangle (PangoRenderer *renderer, | |||
85 | PangoRenderPart part, | |||
86 | int x, | |||
87 | int y, | |||
88 | int width, | |||
89 | int height) | |||
90 | { | |||
91 | CtkFillLayoutRenderer *text_renderer = CTK_FILL_LAYOUT_RENDERER (renderer)((((CtkFillLayoutRenderer*) (void *) g_type_check_instance_cast ((GTypeInstance*) ((renderer)), ((_ctk_fill_layout_renderer_get_type ())))))); | |||
92 | ||||
93 | if (part == PANGO_RENDER_PART_BACKGROUND) | |||
94 | return; | |||
95 | ||||
96 | cairo_rectangle (text_renderer->cr, | |||
97 | (double)x / PANGO_SCALE1024, (double)y / PANGO_SCALE1024, | |||
98 | (double)width / PANGO_SCALE1024, (double)height / PANGO_SCALE1024); | |||
99 | cairo_fill (text_renderer->cr); | |||
100 | } | |||
101 | ||||
102 | static void | |||
103 | ctk_fill_layout_renderer_draw_trapezoid (PangoRenderer *renderer, | |||
104 | PangoRenderPart part G_GNUC_UNUSED__attribute__ ((__unused__)), | |||
105 | double y1_, | |||
106 | double x11, | |||
107 | double x21, | |||
108 | double y2, | |||
109 | double x12, | |||
110 | double x22) | |||
111 | { | |||
112 | CtkFillLayoutRenderer *text_renderer = CTK_FILL_LAYOUT_RENDERER (renderer)((((CtkFillLayoutRenderer*) (void *) g_type_check_instance_cast ((GTypeInstance*) ((renderer)), ((_ctk_fill_layout_renderer_get_type ())))))); | |||
113 | cairo_matrix_t matrix; | |||
114 | cairo_t *cr; | |||
115 | ||||
116 | cr = text_renderer->cr; | |||
117 | ||||
118 | cairo_save (cr); | |||
119 | ||||
120 | /* use identity scale, but keep translation */ | |||
121 | cairo_get_matrix (cr, &matrix); | |||
122 | matrix.xx = matrix.yy = 1; | |||
123 | matrix.xy = matrix.yx = 0; | |||
124 | cairo_set_matrix (cr, &matrix); | |||
125 | ||||
126 | cairo_move_to (cr, x11, y1_); | |||
127 | cairo_line_to (cr, x21, y1_); | |||
128 | cairo_line_to (cr, x22, y2); | |||
129 | cairo_line_to (cr, x12, y2); | |||
130 | cairo_close_path (cr); | |||
131 | ||||
132 | cairo_fill (cr); | |||
133 | ||||
134 | cairo_restore (cr); | |||
135 | } | |||
136 | ||||
137 | static void | |||
138 | ctk_fill_layout_renderer_draw_error_underline (PangoRenderer *renderer, | |||
139 | int x, | |||
140 | int y, | |||
141 | int width, | |||
142 | int height) | |||
143 | { | |||
144 | CtkFillLayoutRenderer *text_renderer = CTK_FILL_LAYOUT_RENDERER (renderer)((((CtkFillLayoutRenderer*) (void *) g_type_check_instance_cast ((GTypeInstance*) ((renderer)), ((_ctk_fill_layout_renderer_get_type ())))))); | |||
145 | ||||
146 | pango_cairo_show_error_underline (text_renderer->cr, | |||
147 | (double)x / PANGO_SCALE1024, (double)y / PANGO_SCALE1024, | |||
148 | (double)width / PANGO_SCALE1024, (double)height / PANGO_SCALE1024); | |||
149 | } | |||
150 | ||||
151 | static void | |||
152 | ctk_fill_layout_renderer_draw_shape (PangoRenderer *renderer, | |||
153 | PangoAttrShape *attr, | |||
154 | int x, | |||
155 | int y) | |||
156 | { | |||
157 | CtkFillLayoutRenderer *text_renderer = CTK_FILL_LAYOUT_RENDERER (renderer)((((CtkFillLayoutRenderer*) (void *) g_type_check_instance_cast ((GTypeInstance*) ((renderer)), ((_ctk_fill_layout_renderer_get_type ())))))); | |||
158 | cairo_t *cr = text_renderer->cr; | |||
159 | PangoLayout *layout; | |||
160 | PangoCairoShapeRendererFunc shape_renderer; | |||
161 | gpointer shape_renderer_data; | |||
162 | ||||
163 | layout = pango_renderer_get_layout (renderer); | |||
164 | ||||
165 | if (!layout) | |||
166 | return; | |||
167 | ||||
168 | shape_renderer = pango_cairo_context_get_shape_renderer (pango_layout_get_context (layout), | |||
169 | &shape_renderer_data); | |||
170 | ||||
171 | if (!shape_renderer) | |||
172 | return; | |||
173 | ||||
174 | cairo_save (cr); | |||
175 | ||||
176 | cairo_move_to (cr, (double)x / PANGO_SCALE1024, (double)y / PANGO_SCALE1024); | |||
177 | ||||
178 | shape_renderer (cr, attr, FALSE(0), shape_renderer_data); | |||
179 | ||||
180 | cairo_restore (cr); | |||
181 | } | |||
182 | ||||
183 | static void | |||
184 | ctk_fill_layout_renderer_finalize (GObject *object) | |||
185 | { | |||
186 | G_OBJECT_CLASS (_ctk_fill_layout_renderer_parent_class)((((GObjectClass*) (void *) g_type_check_class_cast ((GTypeClass *) ((_ctk_fill_layout_renderer_parent_class)), (((GType) ((20 ) << (2))))))))->finalize (object); | |||
187 | } | |||
188 | ||||
189 | static void | |||
190 | _ctk_fill_layout_renderer_init (CtkFillLayoutRenderer *renderer G_GNUC_UNUSED__attribute__ ((__unused__))) | |||
191 | { | |||
192 | } | |||
193 | ||||
194 | static void | |||
195 | _ctk_fill_layout_renderer_class_init (CtkFillLayoutRendererClass *klass) | |||
196 | { | |||
197 | GObjectClass *object_class = G_OBJECT_CLASS (klass)((((GObjectClass*) (void *) g_type_check_class_cast ((GTypeClass *) ((klass)), (((GType) ((20) << (2)))))))); | |||
198 | ||||
199 | PangoRendererClass *renderer_class = PANGO_RENDERER_CLASS (klass)((((PangoRendererClass*) (void *) g_type_check_class_cast ((GTypeClass *) ((klass)), ((pango_renderer_get_type())))))); | |||
200 | ||||
201 | renderer_class->draw_glyphs = ctk_fill_layout_renderer_draw_glyphs; | |||
202 | renderer_class->draw_glyph_item = ctk_fill_layout_renderer_draw_glyph_item; | |||
203 | renderer_class->draw_rectangle = ctk_fill_layout_renderer_draw_rectangle; | |||
204 | renderer_class->draw_trapezoid = ctk_fill_layout_renderer_draw_trapezoid; | |||
205 | renderer_class->draw_error_underline = ctk_fill_layout_renderer_draw_error_underline; | |||
206 | renderer_class->draw_shape = ctk_fill_layout_renderer_draw_shape; | |||
207 | ||||
208 | object_class->finalize = ctk_fill_layout_renderer_finalize; | |||
209 | } | |||
210 | ||||
211 | void | |||
212 | _ctk_pango_fill_layout (cairo_t *cr, | |||
213 | PangoLayout *layout) | |||
214 | { | |||
215 | static CtkFillLayoutRenderer *renderer = NULL((void*)0); | |||
216 | gboolean has_current_point; | |||
217 | double current_x, current_y; | |||
218 | ||||
219 | has_current_point = cairo_has_current_point (cr); | |||
220 | cairo_get_current_point (cr, ¤t_x, ¤t_y); | |||
221 | ||||
222 | if (renderer == NULL((void*)0)) | |||
223 | renderer = g_object_new (CTK_TYPE_FILL_LAYOUT_RENDERER(_ctk_fill_layout_renderer_get_type()), NULL((void*)0)); | |||
224 | ||||
225 | cairo_save (cr); | |||
226 | cairo_translate (cr, current_x, current_y); | |||
227 | ||||
228 | renderer->cr = cr; | |||
229 | pango_renderer_draw_layout (PANGO_RENDERER (renderer)((((PangoRenderer*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((renderer)), ((pango_renderer_get_type())))))), layout, 0, 0); | |||
230 | ||||
231 | cairo_restore (cr); | |||
232 | ||||
233 | if (has_current_point) | |||
234 | cairo_move_to (cr, current_x, current_y); | |||
235 | } | |||
236 | ||||
237 | static AtkAttributeSet * | |||
238 | add_attribute (AtkAttributeSet *attributes, | |||
239 | AtkTextAttribute attr, | |||
240 | const gchar *value) | |||
241 | { | |||
242 | AtkAttribute *at; | |||
243 | ||||
244 | at = g_new (AtkAttribute, 1)((AtkAttribute *) g_malloc_n ((1), sizeof (AtkAttribute))); | |||
245 | at->name = g_strdup (atk_text_attribute_get_name (attr))g_strdup_inline (atk_text_attribute_get_name (attr)); | |||
246 | at->value = g_strdup (value)g_strdup_inline (value); | |||
247 | ||||
248 | return g_slist_prepend (attributes, at); | |||
249 | } | |||
250 | ||||
251 | /* | |||
252 | * _ctk_pango_get_default_attributes: | |||
253 | * @attributes: a #AtkAttributeSet to add the attributes to | |||
254 | * @layout: the #PangoLayout from which to get attributes | |||
255 | * | |||
256 | * Adds the default text attributes from @layout to @attributes, | |||
257 | * after translating them from Pango attributes to ATK attributes. | |||
258 | * | |||
259 | * This is a convenience function that can be used to implement | |||
260 | * support for the #AtkText interface in widgets using Pango | |||
261 | * layouts. | |||
262 | * | |||
263 | * Returns: the modified @attributes | |||
264 | */ | |||
265 | AtkAttributeSet* | |||
266 | _ctk_pango_get_default_attributes (AtkAttributeSet *attributes, | |||
267 | PangoLayout *layout) | |||
268 | { | |||
269 | PangoContext *context; | |||
270 | gint i; | |||
271 | PangoWrapMode mode; | |||
272 | ||||
273 | context = pango_layout_get_context (layout); | |||
274 | if (context) | |||
275 | { | |||
276 | PangoLanguage *language; | |||
277 | PangoFontDescription *font; | |||
278 | ||||
279 | language = pango_context_get_language (context); | |||
280 | if (language) | |||
281 | attributes = add_attribute (attributes, ATK_TEXT_ATTR_LANGUAGE, | |||
282 | pango_language_to_string (language)((const char *)language)); | |||
283 | ||||
284 | font = pango_context_get_font_description (context); | |||
285 | if (font) | |||
286 | { | |||
287 | gchar buf[60]; | |||
288 | attributes = add_attribute (attributes, ATK_TEXT_ATTR_STYLE, | |||
289 | atk_text_attribute_get_value (ATK_TEXT_ATTR_STYLE, | |||
290 | pango_font_description_get_style (font))); | |||
291 | attributes = add_attribute (attributes, ATK_TEXT_ATTR_VARIANT, | |||
292 | atk_text_attribute_get_value (ATK_TEXT_ATTR_VARIANT, | |||
293 | pango_font_description_get_variant (font))); | |||
294 | attributes = add_attribute (attributes, ATK_TEXT_ATTR_STRETCH, | |||
295 | atk_text_attribute_get_value (ATK_TEXT_ATTR_STRETCH, | |||
296 | pango_font_description_get_stretch (font))); | |||
297 | attributes = add_attribute (attributes, ATK_TEXT_ATTR_FAMILY_NAME, | |||
298 | pango_font_description_get_family (font)); | |||
299 | g_snprintf (buf, 60, "%d", pango_font_description_get_weight (font)); | |||
300 | attributes = add_attribute (attributes, ATK_TEXT_ATTR_WEIGHT, buf); | |||
301 | g_snprintf (buf, 60, "%i", pango_font_description_get_size (font) / PANGO_SCALE1024); | |||
302 | attributes = add_attribute (attributes, ATK_TEXT_ATTR_SIZE, buf); | |||
303 | } | |||
304 | } | |||
305 | if (pango_layout_get_justify (layout)) | |||
306 | { | |||
307 | i = 3; | |||
308 | } | |||
309 | else | |||
310 | { | |||
311 | PangoAlignment align; | |||
312 | ||||
313 | align = pango_layout_get_alignment (layout); | |||
314 | if (align == PANGO_ALIGN_LEFT) | |||
315 | i = 0; | |||
316 | else if (align == PANGO_ALIGN_CENTER) | |||
317 | i = 2; | |||
318 | else /* PANGO_ALIGN_RIGHT */ | |||
319 | i = 1; | |||
320 | } | |||
321 | attributes = add_attribute (attributes, ATK_TEXT_ATTR_JUSTIFICATION, | |||
322 | atk_text_attribute_get_value (ATK_TEXT_ATTR_JUSTIFICATION, i)); | |||
323 | mode = pango_layout_get_wrap (layout); | |||
324 | if (mode == PANGO_WRAP_WORD) | |||
325 | i = 2; | |||
326 | else /* PANGO_WRAP_CHAR */ | |||
327 | i = 1; | |||
328 | attributes = add_attribute (attributes, ATK_TEXT_ATTR_WRAP_MODE, | |||
329 | atk_text_attribute_get_value (ATK_TEXT_ATTR_WRAP_MODE, i)); | |||
330 | ||||
331 | attributes = add_attribute (attributes, ATK_TEXT_ATTR_STRIKETHROUGH, | |||
332 | atk_text_attribute_get_value (ATK_TEXT_ATTR_STRIKETHROUGH, 0)); | |||
333 | attributes = add_attribute (attributes, ATK_TEXT_ATTR_UNDERLINE, | |||
334 | atk_text_attribute_get_value (ATK_TEXT_ATTR_UNDERLINE, 0)); | |||
335 | attributes = add_attribute (attributes, ATK_TEXT_ATTR_RISE, "0"); | |||
336 | attributes = add_attribute (attributes, ATK_TEXT_ATTR_SCALE, "1"); | |||
337 | attributes = add_attribute (attributes, ATK_TEXT_ATTR_BG_FULL_HEIGHT, "0"); | |||
338 | attributes = add_attribute (attributes, ATK_TEXT_ATTR_PIXELS_INSIDE_WRAP, "0"); | |||
339 | attributes = add_attribute (attributes, ATK_TEXT_ATTR_PIXELS_BELOW_LINES, "0"); | |||
340 | attributes = add_attribute (attributes, ATK_TEXT_ATTR_PIXELS_ABOVE_LINES, "0"); | |||
341 | attributes = add_attribute (attributes, ATK_TEXT_ATTR_EDITABLE, | |||
342 | atk_text_attribute_get_value (ATK_TEXT_ATTR_EDITABLE, 0)); | |||
343 | attributes = add_attribute (attributes, ATK_TEXT_ATTR_INVISIBLE, | |||
344 | atk_text_attribute_get_value (ATK_TEXT_ATTR_INVISIBLE, 0)); | |||
345 | attributes = add_attribute (attributes, ATK_TEXT_ATTR_INDENT, "0"); | |||
346 | attributes = add_attribute (attributes, ATK_TEXT_ATTR_RIGHT_MARGIN, "0"); | |||
347 | attributes = add_attribute (attributes, ATK_TEXT_ATTR_LEFT_MARGIN, "0"); | |||
348 | ||||
349 | return attributes; | |||
350 | } | |||
351 | ||||
352 | /* | |||
353 | * _ctk_pango_get_run_attributes: | |||
354 | * @attributes: a #AtkAttributeSet to add attributes to | |||
355 | * @layout: the #PangoLayout to get the attributes from | |||
356 | * @offset: the offset at which the attributes are wanted | |||
357 | * @start_offset: return location for the starting offset | |||
358 | * of the current run | |||
359 | * @end_offset: return location for the ending offset of the | |||
360 | * current run | |||
361 | * | |||
362 | * Finds the “run” around index (i.e. the maximal range of characters | |||
363 | * where the set of applicable attributes remains constant) and | |||
364 | * returns the starting and ending offsets for it. | |||
365 | * | |||
366 | * The attributes for the run are added to @attributes, after | |||
367 | * translating them from Pango attributes to ATK attributes. | |||
368 | * | |||
369 | * This is a convenience function that can be used to implement | |||
370 | * support for the #AtkText interface in widgets using Pango | |||
371 | * layouts. | |||
372 | * | |||
373 | * Returns: the modified #AtkAttributeSet | |||
374 | */ | |||
375 | AtkAttributeSet * | |||
376 | _ctk_pango_get_run_attributes (AtkAttributeSet *attributes, | |||
377 | PangoLayout *layout, | |||
378 | gint offset, | |||
379 | gint *start_offset, | |||
380 | gint *end_offset) | |||
381 | { | |||
382 | PangoAttrIterator *iter; | |||
383 | PangoAttrList *attr; | |||
384 | PangoAttrString *pango_string; | |||
385 | PangoAttrInt *pango_int; | |||
386 | PangoAttrColor *pango_color; | |||
387 | PangoAttrLanguage *pango_lang; | |||
388 | PangoAttrFloat *pango_float; | |||
389 | gint index, start_index, end_index; | |||
390 | gboolean is_next; | |||
391 | glong len; | |||
392 | const gchar *text; | |||
393 | gchar *value; | |||
394 | ||||
395 | text = pango_layout_get_text (layout); | |||
396 | len = g_utf8_strlen (text, -1); | |||
397 | ||||
398 | /* Grab the attributes of the PangoLayout, if any */ | |||
399 | attr = pango_layout_get_attributes (layout); | |||
400 | ||||
401 | if (attr == NULL((void*)0)) | |||
402 | { | |||
403 | *start_offset = 0; | |||
404 | *end_offset = len; | |||
405 | return attributes; | |||
406 | } | |||
407 | ||||
408 | iter = pango_attr_list_get_iterator (attr); | |||
409 | /* Get invariant range offsets */ | |||
410 | /* If offset out of range, set offset in range */ | |||
411 | if (offset > len) | |||
412 | offset = len; | |||
413 | else if (offset < 0) | |||
414 | offset = 0; | |||
415 | ||||
416 | index = g_utf8_offset_to_pointer (text, offset) - text; | |||
417 | pango_attr_iterator_range (iter, &start_index, &end_index); | |||
418 | is_next = TRUE(!(0)); | |||
419 | while (is_next) | |||
420 | { | |||
421 | if (index >= start_index && index < end_index) | |||
422 | { | |||
423 | *start_offset = g_utf8_pointer_to_offset (text, text + start_index); | |||
424 | if (end_index == G_MAXINT2147483647) /* Last iterator */ | |||
425 | end_index = len; | |||
426 | ||||
427 | *end_offset = g_utf8_pointer_to_offset (text, text + end_index); | |||
428 | break; | |||
429 | } | |||
430 | is_next = pango_attr_iterator_next (iter); | |||
431 | pango_attr_iterator_range (iter, &start_index, &end_index); | |||
432 | } | |||
433 | ||||
434 | /* Get attributes */ | |||
435 | pango_string = (PangoAttrString*) pango_attr_iterator_get (iter, PANGO_ATTR_FAMILY); | |||
436 | if (pango_string != NULL((void*)0)) | |||
437 | { | |||
438 | value = g_strdup_printf ("%s", pango_string->value); | |||
439 | attributes = add_attribute (attributes, ATK_TEXT_ATTR_FAMILY_NAME, value); | |||
440 | g_free (value); | |||
441 | } | |||
442 | pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_STYLE); | |||
443 | if (pango_int != NULL((void*)0)) | |||
444 | { | |||
445 | attributes = add_attribute (attributes, ATK_TEXT_ATTR_STYLE, | |||
446 | atk_text_attribute_get_value (ATK_TEXT_ATTR_STYLE, pango_int->value)); | |||
447 | } | |||
448 | pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_WEIGHT); | |||
449 | if (pango_int != NULL((void*)0)) | |||
450 | { | |||
451 | value = g_strdup_printf ("%i", pango_int->value); | |||
452 | attributes = add_attribute (attributes, ATK_TEXT_ATTR_WEIGHT, value); | |||
453 | g_free (value); | |||
454 | } | |||
455 | pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_VARIANT); | |||
456 | if (pango_int != NULL((void*)0)) | |||
457 | { | |||
458 | attributes = add_attribute (attributes, ATK_TEXT_ATTR_VARIANT, | |||
459 | atk_text_attribute_get_value (ATK_TEXT_ATTR_VARIANT, pango_int->value)); | |||
460 | } | |||
461 | pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_STRETCH); | |||
462 | if (pango_int != NULL((void*)0)) | |||
463 | { | |||
464 | attributes = add_attribute (attributes, ATK_TEXT_ATTR_STRETCH, | |||
465 | atk_text_attribute_get_value (ATK_TEXT_ATTR_STRETCH, pango_int->value)); | |||
466 | } | |||
467 | pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_SIZE); | |||
468 | if (pango_int != NULL((void*)0)) | |||
469 | { | |||
470 | value = g_strdup_printf ("%i", pango_int->value / PANGO_SCALE1024); | |||
471 | attributes = add_attribute (attributes, ATK_TEXT_ATTR_SIZE, value); | |||
472 | g_free (value); | |||
473 | } | |||
474 | pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_UNDERLINE); | |||
475 | if (pango_int != NULL((void*)0)) | |||
476 | { | |||
477 | attributes = add_attribute (attributes, ATK_TEXT_ATTR_UNDERLINE, | |||
478 | atk_text_attribute_get_value (ATK_TEXT_ATTR_UNDERLINE, pango_int->value)); | |||
479 | } | |||
480 | pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_STRIKETHROUGH); | |||
481 | if (pango_int != NULL((void*)0)) | |||
482 | { | |||
483 | attributes = add_attribute (attributes, ATK_TEXT_ATTR_STRIKETHROUGH, | |||
484 | atk_text_attribute_get_value (ATK_TEXT_ATTR_STRIKETHROUGH, pango_int->value)); | |||
485 | } | |||
486 | pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_RISE); | |||
487 | if (pango_int != NULL((void*)0)) | |||
488 | { | |||
489 | value = g_strdup_printf ("%i", pango_int->value); | |||
490 | attributes = add_attribute (attributes, ATK_TEXT_ATTR_RISE, value); | |||
491 | g_free (value); | |||
492 | } | |||
493 | pango_lang = (PangoAttrLanguage*) pango_attr_iterator_get (iter, PANGO_ATTR_LANGUAGE); | |||
494 | if (pango_lang != NULL((void*)0)) | |||
495 | { | |||
496 | attributes = add_attribute (attributes, ATK_TEXT_ATTR_LANGUAGE, | |||
497 | pango_language_to_string (pango_lang->value)((const char *)pango_lang->value)); | |||
498 | } | |||
499 | pango_float = (PangoAttrFloat*) pango_attr_iterator_get (iter, PANGO_ATTR_SCALE); | |||
500 | if (pango_float != NULL((void*)0)) | |||
501 | { | |||
502 | value = g_strdup_printf ("%g", pango_float->value); | |||
503 | attributes = add_attribute (attributes, ATK_TEXT_ATTR_SCALE, value); | |||
504 | g_free (value); | |||
505 | } | |||
506 | pango_color = (PangoAttrColor*) pango_attr_iterator_get (iter, PANGO_ATTR_FOREGROUND); | |||
507 | if (pango_color != NULL((void*)0)) | |||
508 | { | |||
509 | value = g_strdup_printf ("%u,%u,%u", | |||
510 | pango_color->color.red, | |||
511 | pango_color->color.green, | |||
512 | pango_color->color.blue); | |||
513 | attributes = add_attribute (attributes, ATK_TEXT_ATTR_FG_COLOR, value); | |||
514 | g_free (value); | |||
515 | } | |||
516 | pango_color = (PangoAttrColor*) pango_attr_iterator_get (iter, PANGO_ATTR_BACKGROUND); | |||
517 | if (pango_color != NULL((void*)0)) | |||
518 | { | |||
519 | value = g_strdup_printf ("%u,%u,%u", | |||
520 | pango_color->color.red, | |||
521 | pango_color->color.green, | |||
522 | pango_color->color.blue); | |||
523 | attributes = add_attribute (attributes, ATK_TEXT_ATTR_BG_COLOR, value); | |||
524 | g_free (value); | |||
525 | } | |||
526 | pango_attr_iterator_destroy (iter); | |||
527 | ||||
528 | return attributes; | |||
529 | } | |||
530 | ||||
531 | /* | |||
532 | * _ctk_pango_move_chars: | |||
533 | * @layout: a #PangoLayout | |||
534 | * @offset: a character offset in @layout | |||
535 | * @count: the number of characters to move from @offset | |||
536 | * | |||
537 | * Returns the position that is @count characters from the | |||
538 | * given @offset. @count may be positive or negative. | |||
539 | * | |||
540 | * For the purpose of this function, characters are defined | |||
541 | * by what Pango considers cursor positions. | |||
542 | * | |||
543 | * Returns: the new position | |||
544 | */ | |||
545 | gint | |||
546 | _ctk_pango_move_chars (PangoLayout *layout, | |||
547 | gint offset, | |||
548 | gint count) | |||
549 | { | |||
550 | const PangoLogAttr *attrs; | |||
551 | gint n_attrs; | |||
552 | ||||
553 | attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs); | |||
554 | ||||
555 | while (count > 0 && offset < n_attrs - 1) | |||
556 | { | |||
557 | do | |||
558 | offset++; | |||
559 | while (offset < n_attrs - 1 && !attrs[offset].is_cursor_position); | |||
560 | ||||
561 | count--; | |||
562 | } | |||
563 | while (count < 0 && offset > 0) | |||
564 | { | |||
565 | do | |||
566 | offset--; | |||
567 | while (offset > 0 && !attrs[offset].is_cursor_position); | |||
568 | ||||
569 | count++; | |||
570 | } | |||
571 | ||||
572 | return offset; | |||
573 | } | |||
574 | ||||
575 | /* | |||
576 | * _ctk_pango_move_words: | |||
577 | * @layout: a #PangoLayout | |||
578 | * @offset: a character offset in @layout | |||
579 | * @count: the number of words to move from @offset | |||
580 | * | |||
581 | * Returns the position that is @count words from the | |||
582 | * given @offset. @count may be positive or negative. | |||
583 | * | |||
584 | * If @count is positive, the returned position will | |||
585 | * be a word end, otherwise it will be a word start. | |||
586 | * See the Pango documentation for details on how | |||
587 | * word starts and ends are defined. | |||
588 | * | |||
589 | * Returns: the new position | |||
590 | */ | |||
591 | gint | |||
592 | _ctk_pango_move_words (PangoLayout *layout, | |||
593 | gint offset, | |||
594 | gint count) | |||
595 | { | |||
596 | const PangoLogAttr *attrs; | |||
597 | gint n_attrs; | |||
598 | ||||
599 | attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs); | |||
600 | ||||
601 | while (count > 0 && offset < n_attrs - 1) | |||
602 | { | |||
603 | do | |||
604 | offset++; | |||
605 | while (offset < n_attrs - 1 && !attrs[offset].is_word_end); | |||
606 | ||||
607 | count--; | |||
608 | } | |||
609 | while (count < 0 && offset > 0) | |||
610 | { | |||
611 | do | |||
612 | offset--; | |||
613 | while (offset > 0 && !attrs[offset].is_word_start); | |||
614 | ||||
615 | count++; | |||
616 | } | |||
617 | ||||
618 | return offset; | |||
619 | } | |||
620 | ||||
621 | /* | |||
622 | * _ctk_pango_move_sentences: | |||
623 | * @layout: a #PangoLayout | |||
624 | * @offset: a character offset in @layout | |||
625 | * @count: the number of sentences to move from @offset | |||
626 | * | |||
627 | * Returns the position that is @count sentences from the | |||
628 | * given @offset. @count may be positive or negative. | |||
629 | * | |||
630 | * If @count is positive, the returned position will | |||
631 | * be a sentence end, otherwise it will be a sentence start. | |||
632 | * See the Pango documentation for details on how | |||
633 | * sentence starts and ends are defined. | |||
634 | * | |||
635 | * Returns: the new position | |||
636 | */ | |||
637 | gint | |||
638 | _ctk_pango_move_sentences (PangoLayout *layout, | |||
639 | gint offset, | |||
640 | gint count) | |||
641 | { | |||
642 | const PangoLogAttr *attrs; | |||
643 | gint n_attrs; | |||
644 | ||||
645 | attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs); | |||
646 | ||||
647 | while (count > 0 && offset < n_attrs - 1) | |||
648 | { | |||
649 | do | |||
650 | offset++; | |||
651 | while (offset < n_attrs - 1 && !attrs[offset].is_sentence_end); | |||
652 | ||||
653 | count--; | |||
654 | } | |||
655 | while (count < 0 && offset > 0) | |||
656 | { | |||
657 | do | |||
658 | offset--; | |||
659 | while (offset > 0 && !attrs[offset].is_sentence_start); | |||
660 | ||||
661 | count++; | |||
662 | } | |||
663 | ||||
664 | return offset; | |||
665 | } | |||
666 | ||||
667 | /* | |||
668 | * _ctk_pango_move_lines: | |||
669 | * @layout: a #PangoLayout | |||
670 | * @offset: a character offset in @layout | |||
671 | * @count: the number of lines to move from @offset | |||
672 | * | |||
673 | * Returns the position that is @count lines from the | |||
674 | * given @offset. @count may be positive or negative. | |||
675 | * | |||
676 | * If @count is negative, the returned position will | |||
677 | * be the start of a line, else it will be the end of | |||
678 | * line. | |||
679 | * | |||
680 | * Returns: the new position | |||
681 | */ | |||
682 | gint | |||
683 | _ctk_pango_move_lines (PangoLayout *layout, | |||
684 | gint offset, | |||
685 | gint count) | |||
686 | { | |||
687 | GSList *lines, *l; | |||
688 | PangoLayoutLine *line; | |||
689 | gint num; | |||
690 | const gchar *text; | |||
691 | gint pos, line_pos; | |||
692 | gint index; | |||
693 | gint len; | |||
694 | ||||
695 | text = pango_layout_get_text (layout); | |||
696 | index = g_utf8_offset_to_pointer (text, offset) - text; | |||
697 | lines = pango_layout_get_lines (layout); | |||
698 | line = NULL((void*)0); | |||
699 | ||||
700 | num = 0; | |||
701 | for (l = lines; l; l = l->next) | |||
| ||||
702 | { | |||
703 | line = l->data; | |||
704 | if (index < line->start_index + line->length) | |||
705 | break; | |||
706 | num++; | |||
707 | } | |||
708 | ||||
709 | if (count < 0) | |||
710 | { | |||
711 | num += count; | |||
712 | if (num < 0) | |||
713 | num = 0; | |||
714 | ||||
715 | line = g_slist_nth_data (lines, num); | |||
716 | ||||
717 | return g_utf8_pointer_to_offset (text, text + line->start_index); | |||
718 | } | |||
719 | else | |||
720 | { | |||
721 | line_pos = index - line->start_index; | |||
722 | ||||
723 | len = g_slist_length (lines); | |||
724 | num += count; | |||
725 | if (num >= len || (count == 0 && num == len - 1)) | |||
726 | return g_utf8_strlen (text, -1) - 1; | |||
727 | ||||
728 | line = l->data; | |||
| ||||
729 | pos = line->start_index + line_pos; | |||
730 | if (pos >= line->start_index + line->length) | |||
731 | pos = line->start_index + line->length - 1; | |||
732 | ||||
733 | return g_utf8_pointer_to_offset (text, text + pos); | |||
734 | } | |||
735 | } | |||
736 | ||||
737 | /* | |||
738 | * _ctk_pango_is_inside_word: | |||
739 | * @layout: a #PangoLayout | |||
740 | * @offset: a character offset in @layout | |||
741 | * | |||
742 | * Returns whether the given position is inside | |||
743 | * a word. | |||
744 | * | |||
745 | * Returns: %TRUE if @offset is inside a word | |||
746 | */ | |||
747 | gboolean | |||
748 | _ctk_pango_is_inside_word (PangoLayout *layout, | |||
749 | gint offset) | |||
750 | { | |||
751 | const PangoLogAttr *attrs; | |||
752 | gint n_attrs; | |||
753 | ||||
754 | attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs); | |||
755 | ||||
756 | while (offset >= 0 && | |||
757 | !(attrs[offset].is_word_start || attrs[offset].is_word_end)) | |||
758 | offset--; | |||
759 | ||||
760 | if (offset >= 0) | |||
761 | return attrs[offset].is_word_start; | |||
762 | ||||
763 | return FALSE(0); | |||
764 | } | |||
765 | ||||
766 | /* | |||
767 | * _ctk_pango_is_inside_sentence: | |||
768 | * @layout: a #PangoLayout | |||
769 | * @offset: a character offset in @layout | |||
770 | * | |||
771 | * Returns whether the given position is inside | |||
772 | * a sentence. | |||
773 | * | |||
774 | * Returns: %TRUE if @offset is inside a sentence | |||
775 | */ | |||
776 | gboolean | |||
777 | _ctk_pango_is_inside_sentence (PangoLayout *layout, | |||
778 | gint offset) | |||
779 | { | |||
780 | const PangoLogAttr *attrs; | |||
781 | gint n_attrs; | |||
782 | ||||
783 | attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs); | |||
784 | ||||
785 | while (offset >= 0 && | |||
786 | !(attrs[offset].is_sentence_start || attrs[offset].is_sentence_end)) | |||
787 | offset--; | |||
788 | ||||
789 | if (offset >= 0) | |||
790 | return attrs[offset].is_sentence_start; | |||
791 | ||||
792 | return FALSE(0); | |||
793 | } | |||
794 | ||||
795 | static void | |||
796 | pango_layout_get_line_before (PangoLayout *layout, | |||
797 | AtkTextBoundary boundary_type, | |||
798 | gint offset, | |||
799 | gint *start_offset, | |||
800 | gint *end_offset) | |||
801 | { | |||
802 | PangoLayoutIter *iter; | |||
803 | PangoLayoutLine *line, *prev_line = NULL((void*)0), *prev_prev_line = NULL((void*)0); | |||
804 | gint index, start_index, end_index; | |||
805 | const gchar *text; | |||
806 | gboolean found = FALSE(0); | |||
807 | ||||
808 | text = pango_layout_get_text (layout); | |||
809 | index = g_utf8_offset_to_pointer (text, offset) - text; | |||
810 | iter = pango_layout_get_iter (layout); | |||
811 | do | |||
812 | { | |||
813 | line = pango_layout_iter_get_line (iter); | |||
814 | start_index = line->start_index; | |||
815 | end_index = start_index + line->length; | |||
816 | ||||
817 | if (index >= start_index && index <= end_index) | |||
818 | { | |||
819 | /* Found line for offset */ | |||
820 | if (prev_line) | |||
821 | { | |||
822 | switch (boundary_type) | |||
823 | { | |||
824 | case ATK_TEXT_BOUNDARY_LINE_START: | |||
825 | end_index = start_index; | |||
826 | start_index = prev_line->start_index; | |||
827 | break; | |||
828 | case ATK_TEXT_BOUNDARY_LINE_END: | |||
829 | if (prev_prev_line) | |||
830 | start_index = prev_prev_line->start_index + prev_prev_line->length; | |||
831 | else | |||
832 | start_index = 0; | |||
833 | end_index = prev_line->start_index + prev_line->length; | |||
834 | break; | |||
835 | default: | |||
836 | g_assert_not_reached()do { g_assertion_message_expr ("Ctk", "ctkpango.c", 836, ((const char*) (__func__)), ((void*)0)); } while (0); | |||
837 | } | |||
838 | } | |||
839 | else | |||
840 | start_index = end_index = 0; | |||
841 | ||||
842 | found = TRUE(!(0)); | |||
843 | break; | |||
844 | } | |||
845 | ||||
846 | prev_prev_line = prev_line; | |||
847 | prev_line = line; | |||
848 | } | |||
849 | while (pango_layout_iter_next_line (iter)); | |||
850 | ||||
851 | if (!found) | |||
852 | { | |||
853 | start_index = prev_line->start_index + prev_line->length; | |||
854 | end_index = start_index; | |||
855 | } | |||
856 | pango_layout_iter_free (iter); | |||
857 | ||||
858 | *start_offset = g_utf8_pointer_to_offset (text, text + start_index); | |||
859 | *end_offset = g_utf8_pointer_to_offset (text, text + end_index); | |||
860 | } | |||
861 | ||||
862 | static void | |||
863 | pango_layout_get_line_at (PangoLayout *layout, | |||
864 | AtkTextBoundary boundary_type, | |||
865 | gint offset, | |||
866 | gint *start_offset, | |||
867 | gint *end_offset) | |||
868 | { | |||
869 | PangoLayoutIter *iter; | |||
870 | PangoLayoutLine *line, *prev_line = NULL((void*)0); | |||
871 | gint index, start_index, end_index; | |||
872 | const gchar *text; | |||
873 | gboolean found = FALSE(0); | |||
874 | ||||
875 | text = pango_layout_get_text (layout); | |||
876 | index = g_utf8_offset_to_pointer (text, offset) - text; | |||
877 | iter = pango_layout_get_iter (layout); | |||
878 | do | |||
879 | { | |||
880 | line = pango_layout_iter_get_line (iter); | |||
881 | start_index = line->start_index; | |||
882 | end_index = start_index + line->length; | |||
883 | ||||
884 | if (index >= start_index && index <= end_index) | |||
885 | { | |||
886 | /* Found line for offset */ | |||
887 | switch (boundary_type) | |||
888 | { | |||
889 | case ATK_TEXT_BOUNDARY_LINE_START: | |||
890 | if (pango_layout_iter_next_line (iter)) | |||
891 | end_index = pango_layout_iter_get_line (iter)->start_index; | |||
892 | break; | |||
893 | case ATK_TEXT_BOUNDARY_LINE_END: | |||
894 | if (prev_line) | |||
895 | start_index = prev_line->start_index + prev_line->length; | |||
896 | break; | |||
897 | default: | |||
898 | g_assert_not_reached()do { g_assertion_message_expr ("Ctk", "ctkpango.c", 898, ((const char*) (__func__)), ((void*)0)); } while (0); | |||
899 | } | |||
900 | ||||
901 | found = TRUE(!(0)); | |||
902 | break; | |||
903 | } | |||
904 | ||||
905 | prev_line = line; | |||
906 | } | |||
907 | while (pango_layout_iter_next_line (iter)); | |||
908 | ||||
909 | if (!found) | |||
910 | { | |||
911 | start_index = prev_line->start_index + prev_line->length; | |||
912 | end_index = start_index; | |||
913 | } | |||
914 | pango_layout_iter_free (iter); | |||
915 | ||||
916 | *start_offset = g_utf8_pointer_to_offset (text, text + start_index); | |||
917 | *end_offset = g_utf8_pointer_to_offset (text, text + end_index); | |||
918 | } | |||
919 | ||||
920 | static void | |||
921 | pango_layout_get_line_after (PangoLayout *layout, | |||
922 | AtkTextBoundary boundary_type, | |||
923 | gint offset, | |||
924 | gint *start_offset, | |||
925 | gint *end_offset) | |||
926 | { | |||
927 | PangoLayoutIter *iter; | |||
928 | PangoLayoutLine *line, *prev_line = NULL((void*)0); | |||
929 | gint index, start_index, end_index; | |||
930 | const gchar *text; | |||
931 | gboolean found = FALSE(0); | |||
932 | ||||
933 | text = pango_layout_get_text (layout); | |||
934 | index = g_utf8_offset_to_pointer (text, offset) - text; | |||
935 | iter = pango_layout_get_iter (layout); | |||
936 | do | |||
937 | { | |||
938 | line = pango_layout_iter_get_line (iter); | |||
939 | start_index = line->start_index; | |||
940 | end_index = start_index + line->length; | |||
941 | ||||
942 | if (index >= start_index && index <= end_index) | |||
943 | { | |||
944 | /* Found line for offset */ | |||
945 | if (pango_layout_iter_next_line (iter)) | |||
946 | { | |||
947 | line = pango_layout_iter_get_line (iter); | |||
948 | switch (boundary_type) | |||
949 | { | |||
950 | case ATK_TEXT_BOUNDARY_LINE_START: | |||
951 | start_index = line->start_index; | |||
952 | if (pango_layout_iter_next_line (iter)) | |||
953 | end_index = pango_layout_iter_get_line (iter)->start_index; | |||
954 | else | |||
955 | end_index = start_index + line->length; | |||
956 | break; | |||
957 | case ATK_TEXT_BOUNDARY_LINE_END: | |||
958 | start_index = end_index; | |||
959 | end_index = line->start_index + line->length; | |||
960 | break; | |||
961 | default: | |||
962 | g_assert_not_reached()do { g_assertion_message_expr ("Ctk", "ctkpango.c", 962, ((const char*) (__func__)), ((void*)0)); } while (0); | |||
963 | } | |||
964 | } | |||
965 | else | |||
966 | start_index = end_index; | |||
967 | ||||
968 | found = TRUE(!(0)); | |||
969 | break; | |||
970 | } | |||
971 | ||||
972 | prev_line = line; | |||
973 | } | |||
974 | while (pango_layout_iter_next_line (iter)); | |||
975 | ||||
976 | if (!found) | |||
977 | { | |||
978 | start_index = prev_line->start_index + prev_line->length; | |||
979 | end_index = start_index; | |||
980 | } | |||
981 | pango_layout_iter_free (iter); | |||
982 | ||||
983 | *start_offset = g_utf8_pointer_to_offset (text, text + start_index); | |||
984 | *end_offset = g_utf8_pointer_to_offset (text, text + end_index); | |||
985 | } | |||
986 | ||||
987 | /* | |||
988 | * _ctk_pango_get_text_before: | |||
989 | * @layout: a #PangoLayout | |||
990 | * @boundary_type: a #AtkTextBoundary | |||
991 | * @offset: a character offset in @layout | |||
992 | * @start_offset: return location for the start of the returned text | |||
993 | * @end_offset: return location for the end of the return text | |||
994 | * | |||
995 | * Gets a slice of the text from @layout before @offset. | |||
996 | * | |||
997 | * The @boundary_type determines the size of the returned slice of | |||
998 | * text. For the exact semantics of this function, see | |||
999 | * atk_text_get_text_before_offset(). | |||
1000 | * | |||
1001 | * Returns: a newly allocated string containing a slice of text | |||
1002 | * from layout. Free with g_free(). | |||
1003 | */ | |||
1004 | gchar * | |||
1005 | _ctk_pango_get_text_before (PangoLayout *layout, | |||
1006 | AtkTextBoundary boundary_type, | |||
1007 | gint offset, | |||
1008 | gint *start_offset, | |||
1009 | gint *end_offset) | |||
1010 | { | |||
1011 | const gchar *text; | |||
1012 | gint start, end; | |||
1013 | const PangoLogAttr *attrs; | |||
1014 | gint n_attrs; | |||
1015 | ||||
1016 | text = pango_layout_get_text (layout); | |||
1017 | ||||
1018 | if (text[0] == 0) | |||
1019 | { | |||
1020 | *start_offset = 0; | |||
1021 | *end_offset = 0; | |||
1022 | return g_strdup ("")g_strdup_inline (""); | |||
1023 | } | |||
1024 | ||||
1025 | attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs); | |||
1026 | ||||
1027 | start = offset; | |||
1028 | end = start; | |||
1029 | ||||
1030 | switch (boundary_type) | |||
1031 | { | |||
1032 | case ATK_TEXT_BOUNDARY_CHAR: | |||
1033 | start = _ctk_pango_move_chars (layout, start, -1); | |||
1034 | break; | |||
1035 | ||||
1036 | case ATK_TEXT_BOUNDARY_WORD_START: | |||
1037 | if (!attrs[start].is_word_start) | |||
1038 | start = _ctk_pango_move_words (layout, start, -1); | |||
1039 | end = start; | |||
1040 | start = _ctk_pango_move_words (layout, start, -1); | |||
1041 | break; | |||
1042 | ||||
1043 | case ATK_TEXT_BOUNDARY_WORD_END: | |||
1044 | if (_ctk_pango_is_inside_word (layout, start) && | |||
1045 | !attrs[start].is_word_start) | |||
1046 | start = _ctk_pango_move_words (layout, start, -1); | |||
1047 | while (!attrs[start].is_word_end && start > 0) | |||
1048 | start = _ctk_pango_move_chars (layout, start, -1); | |||
1049 | end = start; | |||
1050 | start = _ctk_pango_move_words (layout, start, -1); | |||
1051 | while (!attrs[start].is_word_end && start > 0) | |||
1052 | start = _ctk_pango_move_chars (layout, start, -1); | |||
1053 | break; | |||
1054 | ||||
1055 | case ATK_TEXT_BOUNDARY_SENTENCE_START: | |||
1056 | if (!attrs[start].is_sentence_start) | |||
1057 | start = _ctk_pango_move_sentences (layout, start, -1); | |||
1058 | end = start; | |||
1059 | start = _ctk_pango_move_sentences (layout, start, -1); | |||
1060 | break; | |||
1061 | ||||
1062 | case ATK_TEXT_BOUNDARY_SENTENCE_END: | |||
1063 | if (_ctk_pango_is_inside_sentence (layout, start) && | |||
1064 | !attrs[start].is_sentence_start) | |||
1065 | start = _ctk_pango_move_sentences (layout, start, -1); | |||
1066 | while (!attrs[start].is_sentence_end && start > 0) | |||
1067 | start = _ctk_pango_move_chars (layout, start, -1); | |||
1068 | end = start; | |||
1069 | start = _ctk_pango_move_sentences (layout, start, -1); | |||
1070 | while (!attrs[start].is_sentence_end && start > 0) | |||
1071 | start = _ctk_pango_move_chars (layout, start, -1); | |||
1072 | break; | |||
1073 | ||||
1074 | case ATK_TEXT_BOUNDARY_LINE_START: | |||
1075 | case ATK_TEXT_BOUNDARY_LINE_END: | |||
1076 | pango_layout_get_line_before (layout, boundary_type, offset, &start, &end); | |||
1077 | break; | |||
1078 | } | |||
1079 | ||||
1080 | *start_offset = start; | |||
1081 | *end_offset = end; | |||
1082 | ||||
1083 | g_assert (start <= end)do { if (start <= end) ; else g_assertion_message_expr ("Ctk" , "ctkpango.c", 1083, ((const char*) (__func__)), "start <= end" ); } while (0); | |||
1084 | ||||
1085 | return g_utf8_substring (text, start, end); | |||
1086 | } | |||
1087 | ||||
1088 | /* | |||
1089 | * _ctk_pango_get_text_after: | |||
1090 | * @layout: a #PangoLayout | |||
1091 | * @boundary_type: a #AtkTextBoundary | |||
1092 | * @offset: a character offset in @layout | |||
1093 | * @start_offset: return location for the start of the returned text | |||
1094 | * @end_offset: return location for the end of the return text | |||
1095 | * | |||
1096 | * Gets a slice of the text from @layout after @offset. | |||
1097 | * | |||
1098 | * The @boundary_type determines the size of the returned slice of | |||
1099 | * text. For the exact semantics of this function, see | |||
1100 | * atk_text_get_text_after_offset(). | |||
1101 | * | |||
1102 | * Returns: a newly allocated string containing a slice of text | |||
1103 | * from layout. Free with g_free(). | |||
1104 | */ | |||
1105 | gchar * | |||
1106 | _ctk_pango_get_text_after (PangoLayout *layout, | |||
1107 | AtkTextBoundary boundary_type, | |||
1108 | gint offset, | |||
1109 | gint *start_offset, | |||
1110 | gint *end_offset) | |||
1111 | { | |||
1112 | const gchar *text; | |||
1113 | gint start, end; | |||
1114 | const PangoLogAttr *attrs; | |||
1115 | gint n_attrs; | |||
1116 | ||||
1117 | text = pango_layout_get_text (layout); | |||
1118 | ||||
1119 | if (text[0] == 0) | |||
1120 | { | |||
1121 | *start_offset = 0; | |||
1122 | *end_offset = 0; | |||
1123 | return g_strdup ("")g_strdup_inline (""); | |||
1124 | } | |||
1125 | ||||
1126 | attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs); | |||
1127 | ||||
1128 | start = offset; | |||
1129 | end = start; | |||
1130 | ||||
1131 | switch (boundary_type) | |||
1132 | { | |||
1133 | case ATK_TEXT_BOUNDARY_CHAR: | |||
1134 | start = _ctk_pango_move_chars (layout, start, 1); | |||
1135 | end = start; | |||
1136 | end = _ctk_pango_move_chars (layout, end, 1); | |||
1137 | break; | |||
1138 | ||||
1139 | case ATK_TEXT_BOUNDARY_WORD_START: | |||
1140 | if (_ctk_pango_is_inside_word (layout, end)) | |||
1141 | end = _ctk_pango_move_words (layout, end, 1); | |||
1142 | while (!attrs[end].is_word_start && end < n_attrs - 1) | |||
1143 | end = _ctk_pango_move_chars (layout, end, 1); | |||
1144 | start = end; | |||
1145 | if (end < n_attrs - 1) | |||
1146 | { | |||
1147 | end = _ctk_pango_move_words (layout, end, 1); | |||
1148 | while (!attrs[end].is_word_start && end < n_attrs - 1) | |||
1149 | end = _ctk_pango_move_chars (layout, end, 1); | |||
1150 | } | |||
1151 | break; | |||
1152 | ||||
1153 | case ATK_TEXT_BOUNDARY_WORD_END: | |||
1154 | end = _ctk_pango_move_words (layout, end, 1); | |||
1155 | start = end; | |||
1156 | if (end < n_attrs - 1) | |||
1157 | end = _ctk_pango_move_words (layout, end, 1); | |||
1158 | break; | |||
1159 | ||||
1160 | case ATK_TEXT_BOUNDARY_SENTENCE_START: | |||
1161 | if (_ctk_pango_is_inside_sentence (layout, end)) | |||
1162 | end = _ctk_pango_move_sentences (layout, end, 1); | |||
1163 | while (!attrs[end].is_sentence_start && end < n_attrs - 1) | |||
1164 | end = _ctk_pango_move_chars (layout, end, 1); | |||
1165 | start = end; | |||
1166 | if (end < n_attrs - 1) | |||
1167 | { | |||
1168 | end = _ctk_pango_move_sentences (layout, end, 1); | |||
1169 | while (!attrs[end].is_sentence_start && end < n_attrs - 1) | |||
1170 | end = _ctk_pango_move_chars (layout, end, 1); | |||
1171 | } | |||
1172 | break; | |||
1173 | ||||
1174 | case ATK_TEXT_BOUNDARY_SENTENCE_END: | |||
1175 | end = _ctk_pango_move_sentences (layout, end, 1); | |||
1176 | start = end; | |||
1177 | if (end < n_attrs - 1) | |||
1178 | end = _ctk_pango_move_sentences (layout, end, 1); | |||
1179 | break; | |||
1180 | ||||
1181 | case ATK_TEXT_BOUNDARY_LINE_START: | |||
1182 | case ATK_TEXT_BOUNDARY_LINE_END: | |||
1183 | pango_layout_get_line_after (layout, boundary_type, offset, &start, &end); | |||
1184 | break; | |||
1185 | } | |||
1186 | ||||
1187 | *start_offset = start; | |||
1188 | *end_offset = end; | |||
1189 | ||||
1190 | g_assert (start <= end)do { if (start <= end) ; else g_assertion_message_expr ("Ctk" , "ctkpango.c", 1190, ((const char*) (__func__)), "start <= end" ); } while (0); | |||
1191 | ||||
1192 | return g_utf8_substring (text, start, end); | |||
1193 | } | |||
1194 | ||||
1195 | /* | |||
1196 | * _ctk_pango_get_text_at: | |||
1197 | * @layout: a #PangoLayout | |||
1198 | * @boundary_type: a #AtkTextBoundary | |||
1199 | * @offset: a character offset in @layout | |||
1200 | * @start_offset: return location for the start of the returned text | |||
1201 | * @end_offset: return location for the end of the return text | |||
1202 | * | |||
1203 | * Gets a slice of the text from @layout at @offset. | |||
1204 | * | |||
1205 | * The @boundary_type determines the size of the returned slice of | |||
1206 | * text. For the exact semantics of this function, see | |||
1207 | * atk_text_get_text_after_offset(). | |||
1208 | * | |||
1209 | * Returns: a newly allocated string containing a slice of text | |||
1210 | * from layout. Free with g_free(). | |||
1211 | */ | |||
1212 | gchar * | |||
1213 | _ctk_pango_get_text_at (PangoLayout *layout, | |||
1214 | AtkTextBoundary boundary_type, | |||
1215 | gint offset, | |||
1216 | gint *start_offset, | |||
1217 | gint *end_offset) | |||
1218 | { | |||
1219 | const gchar *text; | |||
1220 | gint start, end; | |||
1221 | const PangoLogAttr *attrs; | |||
1222 | gint n_attrs; | |||
1223 | ||||
1224 | text = pango_layout_get_text (layout); | |||
1225 | ||||
1226 | if (text[0] == 0) | |||
1227 | { | |||
1228 | *start_offset = 0; | |||
1229 | *end_offset = 0; | |||
1230 | return g_strdup ("")g_strdup_inline (""); | |||
1231 | } | |||
1232 | ||||
1233 | attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs); | |||
1234 | ||||
1235 | start = offset; | |||
1236 | end = start; | |||
1237 | ||||
1238 | switch (boundary_type) | |||
1239 | { | |||
1240 | case ATK_TEXT_BOUNDARY_CHAR: | |||
1241 | end = _ctk_pango_move_chars (layout, end, 1); | |||
1242 | break; | |||
1243 | ||||
1244 | case ATK_TEXT_BOUNDARY_WORD_START: | |||
1245 | if (!attrs[start].is_word_start) | |||
1246 | start = _ctk_pango_move_words (layout, start, -1); | |||
1247 | if (_ctk_pango_is_inside_word (layout, end)) | |||
1248 | end = _ctk_pango_move_words (layout, end, 1); | |||
1249 | while (!attrs[end].is_word_start && end < n_attrs - 1) | |||
1250 | end = _ctk_pango_move_chars (layout, end, 1); | |||
1251 | break; | |||
1252 | ||||
1253 | case ATK_TEXT_BOUNDARY_WORD_END: | |||
1254 | if (_ctk_pango_is_inside_word (layout, start) && | |||
1255 | !attrs[start].is_word_start) | |||
1256 | start = _ctk_pango_move_words (layout, start, -1); | |||
1257 | while (!attrs[start].is_word_end && start > 0) | |||
1258 | start = _ctk_pango_move_chars (layout, start, -1); | |||
1259 | end = _ctk_pango_move_words (layout, end, 1); | |||
1260 | break; | |||
1261 | ||||
1262 | case ATK_TEXT_BOUNDARY_SENTENCE_START: | |||
1263 | if (!attrs[start].is_sentence_start) | |||
1264 | start = _ctk_pango_move_sentences (layout, start, -1); | |||
1265 | if (_ctk_pango_is_inside_sentence (layout, end)) | |||
1266 | end = _ctk_pango_move_sentences (layout, end, 1); | |||
1267 | while (!attrs[end].is_sentence_start && end < n_attrs - 1) | |||
1268 | end = _ctk_pango_move_chars (layout, end, 1); | |||
1269 | break; | |||
1270 | ||||
1271 | case ATK_TEXT_BOUNDARY_SENTENCE_END: | |||
1272 | if (_ctk_pango_is_inside_sentence (layout, start) && | |||
1273 | !attrs[start].is_sentence_start) | |||
1274 | start = _ctk_pango_move_sentences (layout, start, -1); | |||
1275 | while (!attrs[start].is_sentence_end && start > 0) | |||
1276 | start = _ctk_pango_move_chars (layout, start, -1); | |||
1277 | end = _ctk_pango_move_sentences (layout, end, 1); | |||
1278 | break; | |||
1279 | ||||
1280 | case ATK_TEXT_BOUNDARY_LINE_START: | |||
1281 | case ATK_TEXT_BOUNDARY_LINE_END: | |||
1282 | pango_layout_get_line_at (layout, boundary_type, offset, &start, &end); | |||
1283 | break; | |||
1284 | } | |||
1285 | ||||
1286 | *start_offset = start; | |||
1287 | *end_offset = end; | |||
1288 | ||||
1289 | g_assert (start <= end)do { if (start <= end) ; else g_assertion_message_expr ("Ctk" , "ctkpango.c", 1289, ((const char*) (__func__)), "start <= end" ); } while (0); | |||
1290 | ||||
1291 | return g_utf8_substring (text, start, end); | |||
1292 | } | |||
1293 | ||||
1294 | static gboolean | |||
1295 | attr_list_merge_filter (PangoAttribute *attribute, | |||
1296 | gpointer list) | |||
1297 | { | |||
1298 | pango_attr_list_change (list, pango_attribute_copy (attribute)); | |||
1299 | return FALSE(0); | |||
1300 | } | |||
1301 | ||||
1302 | /* | |||
1303 | * _ctk_pango_attr_list_merge: | |||
1304 | * @into: a #PangoAttrList where attributes are merged or %NULL | |||
1305 | * @from: a #PangoAttrList with the attributes to merge or %NULL | |||
1306 | * | |||
1307 | * Merges attributes from @from into @into. | |||
1308 | * | |||
1309 | * Returns: the merged list. | |||
1310 | */ | |||
1311 | PangoAttrList * | |||
1312 | _ctk_pango_attr_list_merge (PangoAttrList *into, | |||
1313 | PangoAttrList *from) | |||
1314 | { | |||
1315 | if (from) | |||
1316 | { | |||
1317 | if (into) | |||
1318 | pango_attr_list_filter (from, attr_list_merge_filter, into); | |||
1319 | else | |||
1320 | return pango_attr_list_ref (from); | |||
1321 | } | |||
1322 | ||||
1323 | return into; | |||
1324 | } | |||
1325 | ||||
1326 | PangoDirection | |||
1327 | _ctk_pango_unichar_direction (gunichar ch) | |||
1328 | { | |||
1329 | FriBidiCharType fribidi_ch_type; | |||
1330 | ||||
1331 | G_STATIC_ASSERT (sizeof (FriBidiChar) == sizeof (gunichar))_Static_assert (sizeof (FriBidiChar) == sizeof (gunichar), "Expression evaluates to false" ); | |||
1332 | ||||
1333 | fribidi_ch_type = fribidi_get_bidi_type (ch); | |||
1334 | ||||
1335 | if (!FRIBIDI_IS_STRONG (fribidi_ch_type)((fribidi_ch_type) & 0x00000010L)) | |||
1336 | return PANGO_DIRECTION_NEUTRAL; | |||
1337 | else if (FRIBIDI_IS_RTL (fribidi_ch_type)((fribidi_ch_type) & 0x00000001L)) | |||
1338 | return PANGO_DIRECTION_RTL; | |||
1339 | else | |||
1340 | return PANGO_DIRECTION_LTR; | |||
1341 | } | |||
1342 | ||||
1343 | PangoDirection | |||
1344 | _ctk_pango_find_base_dir (const gchar *text, | |||
1345 | gint length) | |||
1346 | { | |||
1347 | PangoDirection dir = PANGO_DIRECTION_NEUTRAL; | |||
1348 | const gchar *p; | |||
1349 | ||||
1350 | g_return_val_if_fail (text != NULL || length == 0, PANGO_DIRECTION_NEUTRAL)do { if ((text != ((void*)0) || length == 0)) { } else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__)), "text != NULL || length == 0" ); return (PANGO_DIRECTION_NEUTRAL); } } while (0); | |||
1351 | ||||
1352 | p = text; | |||
1353 | while ((length < 0 || p < text + length) && *p) | |||
1354 | { | |||
1355 | gunichar wc = g_utf8_get_char (p); | |||
1356 | ||||
1357 | dir = _ctk_pango_unichar_direction (wc); | |||
1358 | ||||
1359 | if (dir != PANGO_DIRECTION_NEUTRAL) | |||
1360 | break; | |||
1361 | ||||
1362 | p = g_utf8_next_char (p)((p) + g_utf8_skip[*(const guchar *)(p)]); | |||
1363 | } | |||
1364 | ||||
1365 | return dir; | |||
1366 | } | |||
1367 |