1 /* gtkpango.c - pango-related utilities
3 * Copyright (c) 2010 Red Hat, Inc.
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.
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.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free
17 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
21 * file for a list of people on the GTK+ Team. See the ChangeLog
22 * files for a list of changes. These files are distributed with
23 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
28 #include <pango/pangocairo.h>
31 #define GTK_TYPE_FILL_LAYOUT_RENDERER (_gtk_fill_layout_renderer_get_type())
32 #define GTK_FILL_LAYOUT_RENDERER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GTK_TYPE_FILL_LAYOUT_RENDERER, GtkFillLayoutRenderer))
33 #define GTK_IS_FILL_LAYOUT_RENDERER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GTK_TYPE_FILL_LAYOUT_RENDERER))
34 #define GTK_FILL_LAYOUT_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILL_LAYOUT_RENDERER, GtkFillLayoutRendererClass))
35 #define GTK_IS_FILL_LAYOUT_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILL_LAYOUT_RENDERER))
36 #define GTK_FILL_LAYOUT_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILL_LAYOUT_RENDERER, GtkFillLayoutRendererClass))
38 typedef struct _GtkFillLayoutRenderer GtkFillLayoutRenderer;
39 typedef struct _GtkFillLayoutRendererClass GtkFillLayoutRendererClass;
41 struct _GtkFillLayoutRenderer
43 PangoRenderer parent_instance;
48 struct _GtkFillLayoutRendererClass
50 PangoRendererClass parent_class;
53 G_DEFINE_TYPE (GtkFillLayoutRenderer, _gtk_fill_layout_renderer, PANGO_TYPE_RENDERER)
56 gtk_fill_layout_renderer_draw_glyphs (PangoRenderer *renderer,
58 PangoGlyphString *glyphs,
62 GtkFillLayoutRenderer *text_renderer = GTK_FILL_LAYOUT_RENDERER (renderer);
64 cairo_move_to (text_renderer->cr, (double)x / PANGO_SCALE, (double)y / PANGO_SCALE);
65 pango_cairo_show_glyph_string (text_renderer->cr, font, glyphs);
69 gtk_fill_layout_renderer_draw_glyph_item (PangoRenderer *renderer,
71 PangoGlyphItem *glyph_item,
75 GtkFillLayoutRenderer *text_renderer = GTK_FILL_LAYOUT_RENDERER (renderer);
77 cairo_move_to (text_renderer->cr, (double)x / PANGO_SCALE, (double)y / PANGO_SCALE);
78 pango_cairo_show_glyph_item (text_renderer->cr, text, glyph_item);
82 gtk_fill_layout_renderer_draw_rectangle (PangoRenderer *renderer,
89 GtkFillLayoutRenderer *text_renderer = GTK_FILL_LAYOUT_RENDERER (renderer);
91 if (part == PANGO_RENDER_PART_BACKGROUND)
94 cairo_rectangle (text_renderer->cr,
95 (double)x / PANGO_SCALE, (double)y / PANGO_SCALE,
96 (double)width / PANGO_SCALE, (double)height / PANGO_SCALE);
97 cairo_fill (text_renderer->cr);
101 gtk_fill_layout_renderer_draw_trapezoid (PangoRenderer *renderer,
102 PangoRenderPart part,
110 GtkFillLayoutRenderer *text_renderer = GTK_FILL_LAYOUT_RENDERER (renderer);
111 cairo_matrix_t matrix;
114 cr = text_renderer->cr;
118 /* use identity scale, but keep translation */
119 cairo_get_matrix (cr, &matrix);
120 matrix.xx = matrix.yy = 1;
121 matrix.xy = matrix.yx = 0;
122 cairo_set_matrix (cr, &matrix);
124 cairo_move_to (cr, x11, y1_);
125 cairo_line_to (cr, x21, y1_);
126 cairo_line_to (cr, x22, y2);
127 cairo_line_to (cr, x12, y2);
128 cairo_close_path (cr);
136 gtk_fill_layout_renderer_draw_error_underline (PangoRenderer *renderer,
142 GtkFillLayoutRenderer *text_renderer = GTK_FILL_LAYOUT_RENDERER (renderer);
144 pango_cairo_show_error_underline (text_renderer->cr,
145 (double)x / PANGO_SCALE, (double)y / PANGO_SCALE,
146 (double)width / PANGO_SCALE, (double)height / PANGO_SCALE);
150 gtk_fill_layout_renderer_draw_shape (PangoRenderer *renderer,
151 PangoAttrShape *attr,
155 GtkFillLayoutRenderer *text_renderer = GTK_FILL_LAYOUT_RENDERER (renderer);
156 cairo_t *cr = text_renderer->cr;
158 PangoCairoShapeRendererFunc shape_renderer;
159 gpointer shape_renderer_data;
161 layout = pango_renderer_get_layout (renderer);
166 shape_renderer = pango_cairo_context_get_shape_renderer (pango_layout_get_context (layout),
167 &shape_renderer_data);
174 cairo_move_to (cr, (double)x / PANGO_SCALE, (double)y / PANGO_SCALE);
176 shape_renderer (cr, attr, FALSE, shape_renderer_data);
182 gtk_fill_layout_renderer_finalize (GObject *object)
184 G_OBJECT_CLASS (_gtk_fill_layout_renderer_parent_class)->finalize (object);
188 _gtk_fill_layout_renderer_init (GtkFillLayoutRenderer *renderer)
193 _gtk_fill_layout_renderer_class_init (GtkFillLayoutRendererClass *klass)
195 GObjectClass *object_class = G_OBJECT_CLASS (klass);
197 PangoRendererClass *renderer_class = PANGO_RENDERER_CLASS (klass);
199 renderer_class->draw_glyphs = gtk_fill_layout_renderer_draw_glyphs;
200 renderer_class->draw_glyph_item = gtk_fill_layout_renderer_draw_glyph_item;
201 renderer_class->draw_rectangle = gtk_fill_layout_renderer_draw_rectangle;
202 renderer_class->draw_trapezoid = gtk_fill_layout_renderer_draw_trapezoid;
203 renderer_class->draw_error_underline = gtk_fill_layout_renderer_draw_error_underline;
204 renderer_class->draw_shape = gtk_fill_layout_renderer_draw_shape;
206 object_class->finalize = gtk_fill_layout_renderer_finalize;
210 _gtk_pango_fill_layout (cairo_t *cr,
213 static GtkFillLayoutRenderer *renderer = NULL;
214 gboolean has_current_point;
215 double current_x, current_y;
217 has_current_point = cairo_has_current_point (cr);
218 cairo_get_current_point (cr, ¤t_x, ¤t_y);
220 if (renderer == NULL)
221 renderer = g_object_new (GTK_TYPE_FILL_LAYOUT_RENDERER, NULL);
224 cairo_translate (cr, current_x, current_y);
227 pango_renderer_draw_layout (PANGO_RENDERER (renderer), layout, 0, 0);
231 if (has_current_point)
232 cairo_move_to (cr, current_x, current_y);
235 static AtkAttributeSet *
236 add_attribute (AtkAttributeSet *attributes,
237 AtkTextAttribute attr,
242 at = g_new (AtkAttribute, 1);
243 at->name = g_strdup (atk_text_attribute_get_name (attr));
244 at->value = g_strdup (value);
246 return g_slist_prepend (attributes, at);
250 * _gtk_pango_get_default_attributes:
251 * @attributes: a #AtkAttributeSet to add the attributes to
252 * @layout: the #PangoLayout from which to get attributes
254 * Adds the default text attributes from @layout to @attributes,
255 * after translating them from Pango attributes to ATK attributes.
257 * This is a convenience function that can be used to implement
258 * support for the #AtkText interface in widgets using Pango
261 * Returns: the modified @attributes
264 _gtk_pango_get_default_attributes (AtkAttributeSet *attributes,
267 PangoContext *context;
271 context = pango_layout_get_context (layout);
274 PangoLanguage *language;
275 PangoFontDescription *font;
277 language = pango_context_get_language (context);
279 attributes = add_attribute (attributes, ATK_TEXT_ATTR_LANGUAGE,
280 pango_language_to_string (language));
282 font = pango_context_get_font_description (context);
286 attributes = add_attribute (attributes, ATK_TEXT_ATTR_STYLE,
287 atk_text_attribute_get_value (ATK_TEXT_ATTR_STYLE,
288 pango_font_description_get_style (font)));
289 attributes = add_attribute (attributes, ATK_TEXT_ATTR_VARIANT,
290 atk_text_attribute_get_value (ATK_TEXT_ATTR_VARIANT,
291 pango_font_description_get_variant (font)));
292 attributes = add_attribute (attributes, ATK_TEXT_ATTR_STRETCH,
293 atk_text_attribute_get_value (ATK_TEXT_ATTR_STRETCH,
294 pango_font_description_get_stretch (font)));
295 attributes = add_attribute (attributes, ATK_TEXT_ATTR_FAMILY_NAME,
296 pango_font_description_get_family (font));
297 g_snprintf (buf, 60, "%d", pango_font_description_get_weight (font));
298 attributes = add_attribute (attributes, ATK_TEXT_ATTR_WEIGHT, buf);
299 g_snprintf (buf, 60, "%i", pango_font_description_get_size (font) / PANGO_SCALE);
300 attributes = add_attribute (attributes, ATK_TEXT_ATTR_SIZE, buf);
303 if (pango_layout_get_justify (layout))
309 PangoAlignment align;
311 align = pango_layout_get_alignment (layout);
312 if (align == PANGO_ALIGN_LEFT)
314 else if (align == PANGO_ALIGN_CENTER)
316 else /* PANGO_ALIGN_RIGHT */
319 attributes = add_attribute (attributes, ATK_TEXT_ATTR_JUSTIFICATION,
320 atk_text_attribute_get_value (ATK_TEXT_ATTR_JUSTIFICATION, i));
321 mode = pango_layout_get_wrap (layout);
322 if (mode == PANGO_WRAP_WORD)
324 else /* PANGO_WRAP_CHAR */
326 attributes = add_attribute (attributes, ATK_TEXT_ATTR_WRAP_MODE,
327 atk_text_attribute_get_value (ATK_TEXT_ATTR_WRAP_MODE, i));
329 attributes = add_attribute (attributes, ATK_TEXT_ATTR_STRIKETHROUGH,
330 atk_text_attribute_get_value (ATK_TEXT_ATTR_STRIKETHROUGH, 0));
331 attributes = add_attribute (attributes, ATK_TEXT_ATTR_UNDERLINE,
332 atk_text_attribute_get_value (ATK_TEXT_ATTR_UNDERLINE, 0));
333 attributes = add_attribute (attributes, ATK_TEXT_ATTR_RISE, "0");
334 attributes = add_attribute (attributes, ATK_TEXT_ATTR_SCALE, "1");
335 attributes = add_attribute (attributes, ATK_TEXT_ATTR_BG_FULL_HEIGHT, "0");
336 attributes = add_attribute (attributes, ATK_TEXT_ATTR_PIXELS_INSIDE_WRAP, "0");
337 attributes = add_attribute (attributes, ATK_TEXT_ATTR_PIXELS_BELOW_LINES, "0");
338 attributes = add_attribute (attributes, ATK_TEXT_ATTR_PIXELS_ABOVE_LINES, "0");
339 attributes = add_attribute (attributes, ATK_TEXT_ATTR_EDITABLE,
340 atk_text_attribute_get_value (ATK_TEXT_ATTR_EDITABLE, 0));
341 attributes = add_attribute (attributes, ATK_TEXT_ATTR_INVISIBLE,
342 atk_text_attribute_get_value (ATK_TEXT_ATTR_INVISIBLE, 0));
343 attributes = add_attribute (attributes, ATK_TEXT_ATTR_INDENT, "0");
344 attributes = add_attribute (attributes, ATK_TEXT_ATTR_RIGHT_MARGIN, "0");
345 attributes = add_attribute (attributes, ATK_TEXT_ATTR_LEFT_MARGIN, "0");
351 * _gtk_pango_get_run_attributes:
352 * @attributes: a #AtkAttributeSet to add attributes to
353 * @layout: the #PangoLayout to get the attributes from
354 * @offset: the offset at which the attributes are wanted
355 * @start_offset: return location for the starting offset
357 * @end_offset: return location for the ending offset of the
360 * Finds the 'run' around index (i.e. the maximal range of characters
361 * where the set of applicable attributes remains constant) and
362 * returns the starting and ending offsets for it.
364 * The attributes for the run are added to @attributes, after
365 * translating them from Pango attributes to ATK attributes.
367 * This is a convenience function that can be used to implement
368 * support for the #AtkText interface in widgets using Pango
371 * Returns: the modified #AtkAttributeSet
374 _gtk_pango_get_run_attributes (AtkAttributeSet *attributes,
380 PangoAttrIterator *iter;
382 PangoAttrString *pango_string;
383 PangoAttrInt *pango_int;
384 PangoAttrColor *pango_color;
385 PangoAttrLanguage *pango_lang;
386 PangoAttrFloat *pango_float;
387 gint index, start_index, end_index;
393 text = pango_layout_get_text (layout);
394 len = g_utf8_strlen (text, -1);
396 /* Grab the attributes of the PangoLayout, if any */
397 attr = pango_layout_get_attributes (layout);
406 iter = pango_attr_list_get_iterator (attr);
407 /* Get invariant range offsets */
408 /* If offset out of range, set offset in range */
414 index = g_utf8_offset_to_pointer (text, offset) - text;
415 pango_attr_iterator_range (iter, &start_index, &end_index);
419 if (index >= start_index && index < end_index)
421 *start_offset = g_utf8_pointer_to_offset (text, text + start_index);
422 if (end_index == G_MAXINT) /* Last iterator */
425 *end_offset = g_utf8_pointer_to_offset (text, text + end_index);
428 is_next = pango_attr_iterator_next (iter);
429 pango_attr_iterator_range (iter, &start_index, &end_index);
433 pango_string = (PangoAttrString*) pango_attr_iterator_get (iter, PANGO_ATTR_FAMILY);
434 if (pango_string != NULL)
436 value = g_strdup_printf ("%s", pango_string->value);
437 attributes = add_attribute (attributes, ATK_TEXT_ATTR_FAMILY_NAME, value);
440 pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_STYLE);
441 if (pango_int != NULL)
443 attributes = add_attribute (attributes, ATK_TEXT_ATTR_STYLE,
444 atk_text_attribute_get_value (ATK_TEXT_ATTR_STYLE, pango_int->value));
446 pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_WEIGHT);
447 if (pango_int != NULL)
449 value = g_strdup_printf ("%i", pango_int->value);
450 attributes = add_attribute (attributes, ATK_TEXT_ATTR_WEIGHT, value);
453 pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_VARIANT);
454 if (pango_int != NULL)
456 attributes = add_attribute (attributes, ATK_TEXT_ATTR_VARIANT,
457 atk_text_attribute_get_value (ATK_TEXT_ATTR_VARIANT, pango_int->value));
459 pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_STRETCH);
460 if (pango_int != NULL)
462 attributes = add_attribute (attributes, ATK_TEXT_ATTR_STRETCH,
463 atk_text_attribute_get_value (ATK_TEXT_ATTR_STRETCH, pango_int->value));
465 pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_SIZE);
466 if (pango_int != NULL)
468 value = g_strdup_printf ("%i", pango_int->value / PANGO_SCALE);
469 attributes = add_attribute (attributes, ATK_TEXT_ATTR_SIZE, value);
472 pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_UNDERLINE);
473 if (pango_int != NULL)
475 attributes = add_attribute (attributes, ATK_TEXT_ATTR_UNDERLINE,
476 atk_text_attribute_get_value (ATK_TEXT_ATTR_UNDERLINE, pango_int->value));
478 pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_STRIKETHROUGH);
479 if (pango_int != NULL)
481 attributes = add_attribute (attributes, ATK_TEXT_ATTR_STRIKETHROUGH,
482 atk_text_attribute_get_value (ATK_TEXT_ATTR_STRIKETHROUGH, pango_int->value));
484 pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_RISE);
485 if (pango_int != NULL)
487 value = g_strdup_printf ("%i", pango_int->value);
488 attributes = add_attribute (attributes, ATK_TEXT_ATTR_RISE, value);
491 pango_lang = (PangoAttrLanguage*) pango_attr_iterator_get (iter, PANGO_ATTR_LANGUAGE);
492 if (pango_lang != NULL)
494 attributes = add_attribute (attributes, ATK_TEXT_ATTR_LANGUAGE,
495 pango_language_to_string (pango_lang->value));
497 pango_float = (PangoAttrFloat*) pango_attr_iterator_get (iter, PANGO_ATTR_SCALE);
498 if (pango_float != NULL)
500 value = g_strdup_printf ("%g", pango_float->value);
501 attributes = add_attribute (attributes, ATK_TEXT_ATTR_SCALE, value);
504 pango_color = (PangoAttrColor*) pango_attr_iterator_get (iter, PANGO_ATTR_FOREGROUND);
505 if (pango_color != NULL)
507 value = g_strdup_printf ("%u,%u,%u",
508 pango_color->color.red,
509 pango_color->color.green,
510 pango_color->color.blue);
511 attributes = add_attribute (attributes, ATK_TEXT_ATTR_FG_COLOR, value);
514 pango_color = (PangoAttrColor*) pango_attr_iterator_get (iter, PANGO_ATTR_BACKGROUND);
515 if (pango_color != NULL)
517 value = g_strdup_printf ("%u,%u,%u",
518 pango_color->color.red,
519 pango_color->color.green,
520 pango_color->color.blue);
521 attributes = add_attribute (attributes, ATK_TEXT_ATTR_BG_COLOR, value);
524 pango_attr_iterator_destroy (iter);
530 * _gtk_pango_move_chars:
531 * @layout: a #PangoLayout
532 * @offset: a character offset in @layout
533 * @count: the number of characters to move from @offset
535 * Returns the position that is @count characters from the
536 * given @offset. @count may be positive or negative.
538 * For the purpose of this function, characters are defined
539 * by what Pango considers cursor positions.
541 * Returns: the new position
544 _gtk_pango_move_chars (PangoLayout *layout,
548 const PangoLogAttr *attrs;
551 attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs);
553 while (count > 0 && offset < n_attrs - 1)
557 while (offset < n_attrs - 1 && !attrs[offset].is_cursor_position);
561 while (count < 0 && offset > 0)
565 while (offset > 0 && !attrs[offset].is_cursor_position);
574 * _gtk_pango_move_words:
575 * @layout: a #PangoLayout
576 * @offset: a character offset in @layout
577 * @count: the number of words to move from @offset
579 * Returns the position that is @count words from the
580 * given @offset. @count may be positive or negative.
582 * If @count is positive, the returned position will
583 * be a word end, otherwise it will be a word start.
584 * See the Pango documentation for details on how
585 * word starts and ends are defined.
587 * Returns: the new position
590 _gtk_pango_move_words (PangoLayout *layout,
594 const PangoLogAttr *attrs;
597 attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs);
599 while (count > 0 && offset < n_attrs - 1)
603 while (offset < n_attrs - 1 && !attrs[offset].is_word_end);
607 while (count < 0 && offset > 0)
611 while (offset > 0 && !attrs[offset].is_word_start);
620 * _gtk_pango_move_sentences:
621 * @layout: a #PangoLayout
622 * @offset: a character offset in @layout
623 * @count: the number of sentences to move from @offset
625 * Returns the position that is @count sentences from the
626 * given @offset. @count may be positive or negative.
628 * If @count is positive, the returned position will
629 * be a sentence end, otherwise it will be a sentence start.
630 * See the Pango documentation for details on how
631 * sentence starts and ends are defined.
633 * Returns: the new position
636 _gtk_pango_move_sentences (PangoLayout *layout,
640 const PangoLogAttr *attrs;
643 attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs);
645 while (count > 0 && offset < n_attrs - 1)
649 while (offset < n_attrs - 1 && !attrs[offset].is_sentence_end);
653 while (count < 0 && offset > 0)
657 while (offset > 0 && !attrs[offset].is_sentence_start);
666 * _gtk_pango_move_lines:
667 * @layout: a #PangoLayout
668 * @offset: a character offset in @layout
669 * @count: the number of lines to move from @offset
671 * Returns the position that is @count lines from the
672 * given @offset. @count may be positive or negative.
674 * If @count is negative, the returned position will
675 * be the start of a line, else it will be the end of
678 * Returns: the new position
681 _gtk_pango_move_lines (PangoLayout *layout,
686 PangoLayoutLine *line;
693 text = pango_layout_get_text (layout);
694 index = g_utf8_offset_to_pointer (text, offset) - text;
695 lines = pango_layout_get_lines (layout);
699 for (l = lines; l; l = l->next)
702 if (index < line->start_index + line->length)
713 line = g_slist_nth_data (lines, num);
715 return g_utf8_pointer_to_offset (text, text + line->start_index);
719 line_pos = index - line->start_index;
721 len = g_slist_length (lines);
723 if (num >= len || (count == 0 && num == len - 1))
724 return g_utf8_strlen (text, -1) - 1;
727 pos = line->start_index + line_pos;
728 if (pos >= line->start_index + line->length)
729 pos = line->start_index + line->length - 1;
731 return g_utf8_pointer_to_offset (text, text + pos);
736 * _gtk_pango_is_inside_word:
737 * @layout: a #PangoLayout
738 * @offset: a character offset in @layout
740 * Returns whether the given position is inside
743 * Returns: %TRUE if @offset is inside a word
746 _gtk_pango_is_inside_word (PangoLayout *layout,
749 const PangoLogAttr *attrs;
752 attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs);
754 while (offset >= 0 &&
755 !(attrs[offset].is_word_start || attrs[offset].is_word_end))
759 return attrs[offset].is_word_start;
765 * _gtk_pango_is_inside_sentence:
766 * @layout: a #PangoLayout
767 * @offset: a character offset in @layout
769 * Returns whether the given position is inside
772 * Returns: %TRUE if @offset is inside a sentence
775 _gtk_pango_is_inside_sentence (PangoLayout *layout,
778 const PangoLogAttr *attrs;
781 attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs);
783 while (offset >= 0 &&
784 !(attrs[offset].is_sentence_start || attrs[offset].is_sentence_end))
788 return attrs[offset].is_sentence_start;
794 pango_layout_get_line_before (PangoLayout *layout,
795 AtkTextBoundary boundary_type,
800 PangoLayoutIter *iter;
801 PangoLayoutLine *line, *prev_line = NULL, *prev_prev_line = NULL;
802 gint index, start_index, end_index;
804 gboolean found = FALSE;
806 text = pango_layout_get_text (layout);
807 index = g_utf8_offset_to_pointer (text, offset) - text;
808 iter = pango_layout_get_iter (layout);
811 line = pango_layout_iter_get_line (iter);
812 start_index = line->start_index;
813 end_index = start_index + line->length;
815 if (index >= start_index && index <= end_index)
817 /* Found line for offset */
820 switch (boundary_type)
822 case ATK_TEXT_BOUNDARY_LINE_START:
823 end_index = start_index;
824 start_index = prev_line->start_index;
826 case ATK_TEXT_BOUNDARY_LINE_END:
828 start_index = prev_prev_line->start_index + prev_prev_line->length;
831 end_index = prev_line->start_index + prev_line->length;
834 g_assert_not_reached();
838 start_index = end_index = 0;
844 prev_prev_line = prev_line;
847 while (pango_layout_iter_next_line (iter));
851 start_index = prev_line->start_index + prev_line->length;
852 end_index = start_index;
854 pango_layout_iter_free (iter);
856 *start_offset = g_utf8_pointer_to_offset (text, text + start_index);
857 *end_offset = g_utf8_pointer_to_offset (text, text + end_index);
861 pango_layout_get_line_at (PangoLayout *layout,
862 AtkTextBoundary boundary_type,
867 PangoLayoutIter *iter;
868 PangoLayoutLine *line, *prev_line = NULL;
869 gint index, start_index, end_index;
871 gboolean found = FALSE;
873 text = pango_layout_get_text (layout);
874 index = g_utf8_offset_to_pointer (text, offset) - text;
875 iter = pango_layout_get_iter (layout);
878 line = pango_layout_iter_get_line (iter);
879 start_index = line->start_index;
880 end_index = start_index + line->length;
882 if (index >= start_index && index <= end_index)
884 /* Found line for offset */
885 switch (boundary_type)
887 case ATK_TEXT_BOUNDARY_LINE_START:
888 if (pango_layout_iter_next_line (iter))
889 end_index = pango_layout_iter_get_line (iter)->start_index;
891 case ATK_TEXT_BOUNDARY_LINE_END:
893 start_index = prev_line->start_index + prev_line->length;
896 g_assert_not_reached();
905 while (pango_layout_iter_next_line (iter));
909 start_index = prev_line->start_index + prev_line->length;
910 end_index = start_index;
912 pango_layout_iter_free (iter);
914 *start_offset = g_utf8_pointer_to_offset (text, text + start_index);
915 *end_offset = g_utf8_pointer_to_offset (text, text + end_index);
919 pango_layout_get_line_after (PangoLayout *layout,
920 AtkTextBoundary boundary_type,
925 PangoLayoutIter *iter;
926 PangoLayoutLine *line, *prev_line = NULL;
927 gint index, start_index, end_index;
929 gboolean found = FALSE;
931 text = pango_layout_get_text (layout);
932 index = g_utf8_offset_to_pointer (text, offset) - text;
933 iter = pango_layout_get_iter (layout);
936 line = pango_layout_iter_get_line (iter);
937 start_index = line->start_index;
938 end_index = start_index + line->length;
940 if (index >= start_index && index <= end_index)
942 /* Found line for offset */
943 if (pango_layout_iter_next_line (iter))
945 line = pango_layout_iter_get_line (iter);
946 switch (boundary_type)
948 case ATK_TEXT_BOUNDARY_LINE_START:
949 start_index = line->start_index;
950 if (pango_layout_iter_next_line (iter))
951 end_index = pango_layout_iter_get_line (iter)->start_index;
953 end_index = start_index + line->length;
955 case ATK_TEXT_BOUNDARY_LINE_END:
956 start_index = end_index;
957 end_index = line->start_index + line->length;
960 g_assert_not_reached();
964 start_index = end_index;
972 while (pango_layout_iter_next_line (iter));
976 start_index = prev_line->start_index + prev_line->length;
977 end_index = start_index;
979 pango_layout_iter_free (iter);
981 *start_offset = g_utf8_pointer_to_offset (text, text + start_index);
982 *end_offset = g_utf8_pointer_to_offset (text, text + end_index);
986 * _gtk_pango_get_text_before:
987 * @layout: a #PangoLayout
988 * @boundary_type: a #AtkTextBoundary
989 * @offset: a character offset in @layout
990 * @start_offset: return location for the start of the returned text
991 * @end_offset: return location for the end of the return text
993 * Gets a slice of the text from @layout before @offset.
995 * The @boundary_type determines the size of the returned slice of
996 * text. For the exact semantics of this function, see
997 * atk_text_get_text_before_offset().
999 * Returns: a newly allocated string containing a slice of text
1000 * from layout. Free with g_free().
1003 _gtk_pango_get_text_before (PangoLayout *layout,
1004 AtkTextBoundary boundary_type,
1011 const PangoLogAttr *attrs;
1014 text = pango_layout_get_text (layout);
1020 return g_strdup ("");
1023 attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs);
1028 switch (boundary_type)
1030 case ATK_TEXT_BOUNDARY_CHAR:
1031 start = _gtk_pango_move_chars (layout, start, -1);
1034 case ATK_TEXT_BOUNDARY_WORD_START:
1035 if (!attrs[start].is_word_start)
1036 start = _gtk_pango_move_words (layout, start, -1);
1038 start = _gtk_pango_move_words (layout, start, -1);
1041 case ATK_TEXT_BOUNDARY_WORD_END:
1042 if (_gtk_pango_is_inside_word (layout, start) &&
1043 !attrs[start].is_word_start)
1044 start = _gtk_pango_move_words (layout, start, -1);
1045 while (!attrs[start].is_word_end && start > 0)
1046 start = _gtk_pango_move_chars (layout, start, -1);
1048 start = _gtk_pango_move_words (layout, start, -1);
1049 while (!attrs[start].is_word_end && start > 0)
1050 start = _gtk_pango_move_chars (layout, start, -1);
1053 case ATK_TEXT_BOUNDARY_SENTENCE_START:
1054 if (!attrs[start].is_sentence_start)
1055 start = _gtk_pango_move_sentences (layout, start, -1);
1057 start = _gtk_pango_move_sentences (layout, start, -1);
1060 case ATK_TEXT_BOUNDARY_SENTENCE_END:
1061 if (_gtk_pango_is_inside_sentence (layout, start) &&
1062 !attrs[start].is_sentence_start)
1063 start = _gtk_pango_move_sentences (layout, start, -1);
1064 while (!attrs[start].is_sentence_end && start > 0)
1065 start = _gtk_pango_move_chars (layout, start, -1);
1067 start = _gtk_pango_move_sentences (layout, start, -1);
1068 while (!attrs[start].is_sentence_end && start > 0)
1069 start = _gtk_pango_move_chars (layout, start, -1);
1072 case ATK_TEXT_BOUNDARY_LINE_START:
1073 case ATK_TEXT_BOUNDARY_LINE_END:
1074 pango_layout_get_line_before (layout, boundary_type, offset, &start, &end);
1078 *start_offset = start;
1081 g_assert (start <= end);
1083 return g_utf8_substring (text, start, end);
1087 * _gtk_pango_get_text_after:
1088 * @layout: a #PangoLayout
1089 * @boundary_type: a #AtkTextBoundary
1090 * @offset: a character offset in @layout
1091 * @start_offset: return location for the start of the returned text
1092 * @end_offset: return location for the end of the return text
1094 * Gets a slice of the text from @layout after @offset.
1096 * The @boundary_type determines the size of the returned slice of
1097 * text. For the exact semantics of this function, see
1098 * atk_text_get_text_after_offset().
1100 * Returns: a newly allocated string containing a slice of text
1101 * from layout. Free with g_free().
1104 _gtk_pango_get_text_after (PangoLayout *layout,
1105 AtkTextBoundary boundary_type,
1112 const PangoLogAttr *attrs;
1115 text = pango_layout_get_text (layout);
1121 return g_strdup ("");
1124 attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs);
1129 switch (boundary_type)
1131 case ATK_TEXT_BOUNDARY_CHAR:
1132 start = _gtk_pango_move_chars (layout, start, 1);
1134 end = _gtk_pango_move_chars (layout, end, 1);
1137 case ATK_TEXT_BOUNDARY_WORD_START:
1138 if (_gtk_pango_is_inside_word (layout, end))
1139 end = _gtk_pango_move_words (layout, end, 1);
1140 while (!attrs[end].is_word_start && end < n_attrs - 1)
1141 end = _gtk_pango_move_chars (layout, end, 1);
1143 if (end < n_attrs - 1)
1145 end = _gtk_pango_move_words (layout, end, 1);
1146 while (!attrs[end].is_word_end && end < n_attrs - 1)
1147 end = _gtk_pango_move_chars (layout, end, 1);
1151 case ATK_TEXT_BOUNDARY_WORD_END:
1152 end = _gtk_pango_move_words (layout, end, 1);
1154 if (end < n_attrs - 1)
1155 end = _gtk_pango_move_words (layout, end, 1);
1158 case ATK_TEXT_BOUNDARY_SENTENCE_START:
1159 if (_gtk_pango_is_inside_sentence (layout, end))
1160 end = _gtk_pango_move_sentences (layout, end, 1);
1161 while (!attrs[end].is_sentence_end && end < n_attrs - 1)
1162 end = _gtk_pango_move_chars (layout, end, 1);
1164 if (end < n_attrs - 1)
1166 end = _gtk_pango_move_sentences (layout, end, 1);
1167 while (!attrs[end].is_sentence_start && end < n_attrs - 1)
1168 end = _gtk_pango_move_chars (layout, end, 1);
1172 case ATK_TEXT_BOUNDARY_SENTENCE_END:
1173 end = _gtk_pango_move_sentences (layout, end, 1);
1175 if (end < n_attrs - 1)
1176 end = _gtk_pango_move_sentences (layout, end, 1);
1179 case ATK_TEXT_BOUNDARY_LINE_START:
1180 case ATK_TEXT_BOUNDARY_LINE_END:
1181 pango_layout_get_line_after (layout, boundary_type, offset, &start, &end);
1185 *start_offset = start;
1188 g_assert (start <= end);
1190 return g_utf8_substring (text, start, end);
1194 * _gtk_pango_get_text_at:
1195 * @layout: a #PangoLayout
1196 * @boundary_type: a #AtkTextBoundary
1197 * @offset: a character offset in @layout
1198 * @start_offset: return location for the start of the returned text
1199 * @end_offset: return location for the end of the return text
1201 * Gets a slice of the text from @layout at @offset.
1203 * The @boundary_type determines the size of the returned slice of
1204 * text. For the exact semantics of this function, see
1205 * atk_text_get_text_after_offset().
1207 * Returns: a newly allocated string containing a slice of text
1208 * from layout. Free with g_free().
1211 _gtk_pango_get_text_at (PangoLayout *layout,
1212 AtkTextBoundary boundary_type,
1219 const PangoLogAttr *attrs;
1222 text = pango_layout_get_text (layout);
1228 return g_strdup ("");
1231 attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs);
1236 switch (boundary_type)
1238 case ATK_TEXT_BOUNDARY_CHAR:
1239 end = _gtk_pango_move_chars (layout, end, 1);
1242 case ATK_TEXT_BOUNDARY_WORD_START:
1243 if (!attrs[start].is_word_start)
1244 start = _gtk_pango_move_words (layout, start, -1);
1245 if (_gtk_pango_is_inside_word (layout, end))
1246 end = _gtk_pango_move_words (layout, end, 1);
1247 while (!attrs[end].is_word_start && end < n_attrs - 1)
1248 end = _gtk_pango_move_chars (layout, end, -1);
1251 case ATK_TEXT_BOUNDARY_WORD_END:
1252 if (_gtk_pango_is_inside_word (layout, start) &&
1253 !attrs[start].is_word_start)
1254 start = _gtk_pango_move_words (layout, start, -1);
1255 while (!attrs[start].is_word_end && start > 0)
1256 start = _gtk_pango_move_chars (layout, start, -1);
1257 end = _gtk_pango_move_words (layout, end, 1);
1260 case ATK_TEXT_BOUNDARY_SENTENCE_START:
1261 if (!attrs[start].is_sentence_start)
1262 start = _gtk_pango_move_sentences (layout, start, -1);
1263 if (_gtk_pango_is_inside_sentence (layout, end))
1264 end = _gtk_pango_move_sentences (layout, end, 1);
1265 while (!attrs[end].is_word_end && end < n_attrs - 1)
1266 end = _gtk_pango_move_chars (layout, end, 1);
1269 case ATK_TEXT_BOUNDARY_SENTENCE_END:
1270 if (_gtk_pango_is_inside_sentence (layout, start) &&
1271 !attrs[start].is_sentence_start)
1272 start = _gtk_pango_move_sentences (layout, start, -1);
1273 while (!attrs[start].is_sentence_end && start > 0)
1274 start = _gtk_pango_move_chars (layout, start, -1);
1275 end = _gtk_pango_move_sentences (layout, end, 1);
1278 case ATK_TEXT_BOUNDARY_LINE_START:
1279 case ATK_TEXT_BOUNDARY_LINE_END:
1280 pango_layout_get_line_at (layout, boundary_type, offset, &start, &end);
1284 *start_offset = start;
1287 g_assert (start <= end);
1289 return g_utf8_substring (text, start, end);