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, see <http://www.gnu.org/licenses/>.Free
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/.
27 #include <pango/pangocairo.h>
30 #define GTK_TYPE_FILL_LAYOUT_RENDERER (_gtk_fill_layout_renderer_get_type())
31 #define GTK_FILL_LAYOUT_RENDERER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GTK_TYPE_FILL_LAYOUT_RENDERER, GtkFillLayoutRenderer))
32 #define GTK_IS_FILL_LAYOUT_RENDERER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GTK_TYPE_FILL_LAYOUT_RENDERER))
33 #define GTK_FILL_LAYOUT_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILL_LAYOUT_RENDERER, GtkFillLayoutRendererClass))
34 #define GTK_IS_FILL_LAYOUT_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILL_LAYOUT_RENDERER))
35 #define GTK_FILL_LAYOUT_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILL_LAYOUT_RENDERER, GtkFillLayoutRendererClass))
37 typedef struct _GtkFillLayoutRenderer GtkFillLayoutRenderer;
38 typedef struct _GtkFillLayoutRendererClass GtkFillLayoutRendererClass;
40 struct _GtkFillLayoutRenderer
42 PangoRenderer parent_instance;
47 struct _GtkFillLayoutRendererClass
49 PangoRendererClass parent_class;
52 GType _gtk_fill_layout_renderer_get_type (void);
54 G_DEFINE_TYPE (GtkFillLayoutRenderer, _gtk_fill_layout_renderer, PANGO_TYPE_RENDERER)
57 gtk_fill_layout_renderer_draw_glyphs (PangoRenderer *renderer,
59 PangoGlyphString *glyphs,
63 GtkFillLayoutRenderer *text_renderer = GTK_FILL_LAYOUT_RENDERER (renderer);
65 cairo_move_to (text_renderer->cr, (double)x / PANGO_SCALE, (double)y / PANGO_SCALE);
66 pango_cairo_show_glyph_string (text_renderer->cr, font, glyphs);
70 gtk_fill_layout_renderer_draw_glyph_item (PangoRenderer *renderer,
72 PangoGlyphItem *glyph_item,
76 GtkFillLayoutRenderer *text_renderer = GTK_FILL_LAYOUT_RENDERER (renderer);
78 cairo_move_to (text_renderer->cr, (double)x / PANGO_SCALE, (double)y / PANGO_SCALE);
79 pango_cairo_show_glyph_item (text_renderer->cr, text, glyph_item);
83 gtk_fill_layout_renderer_draw_rectangle (PangoRenderer *renderer,
90 GtkFillLayoutRenderer *text_renderer = GTK_FILL_LAYOUT_RENDERER (renderer);
92 if (part == PANGO_RENDER_PART_BACKGROUND)
95 cairo_rectangle (text_renderer->cr,
96 (double)x / PANGO_SCALE, (double)y / PANGO_SCALE,
97 (double)width / PANGO_SCALE, (double)height / PANGO_SCALE);
98 cairo_fill (text_renderer->cr);
102 gtk_fill_layout_renderer_draw_trapezoid (PangoRenderer *renderer,
103 PangoRenderPart part,
111 GtkFillLayoutRenderer *text_renderer = GTK_FILL_LAYOUT_RENDERER (renderer);
112 cairo_matrix_t matrix;
115 cr = text_renderer->cr;
119 /* use identity scale, but keep translation */
120 cairo_get_matrix (cr, &matrix);
121 matrix.xx = matrix.yy = 1;
122 matrix.xy = matrix.yx = 0;
123 cairo_set_matrix (cr, &matrix);
125 cairo_move_to (cr, x11, y1_);
126 cairo_line_to (cr, x21, y1_);
127 cairo_line_to (cr, x22, y2);
128 cairo_line_to (cr, x12, y2);
129 cairo_close_path (cr);
137 gtk_fill_layout_renderer_draw_error_underline (PangoRenderer *renderer,
143 GtkFillLayoutRenderer *text_renderer = GTK_FILL_LAYOUT_RENDERER (renderer);
145 pango_cairo_show_error_underline (text_renderer->cr,
146 (double)x / PANGO_SCALE, (double)y / PANGO_SCALE,
147 (double)width / PANGO_SCALE, (double)height / PANGO_SCALE);
151 gtk_fill_layout_renderer_draw_shape (PangoRenderer *renderer,
152 PangoAttrShape *attr,
156 GtkFillLayoutRenderer *text_renderer = GTK_FILL_LAYOUT_RENDERER (renderer);
157 cairo_t *cr = text_renderer->cr;
159 PangoCairoShapeRendererFunc shape_renderer;
160 gpointer shape_renderer_data;
162 layout = pango_renderer_get_layout (renderer);
167 shape_renderer = pango_cairo_context_get_shape_renderer (pango_layout_get_context (layout),
168 &shape_renderer_data);
175 cairo_move_to (cr, (double)x / PANGO_SCALE, (double)y / PANGO_SCALE);
177 shape_renderer (cr, attr, FALSE, shape_renderer_data);
183 gtk_fill_layout_renderer_finalize (GObject *object)
185 G_OBJECT_CLASS (_gtk_fill_layout_renderer_parent_class)->finalize (object);
189 _gtk_fill_layout_renderer_init (GtkFillLayoutRenderer *renderer)
194 _gtk_fill_layout_renderer_class_init (GtkFillLayoutRendererClass *klass)
196 GObjectClass *object_class = G_OBJECT_CLASS (klass);
198 PangoRendererClass *renderer_class = PANGO_RENDERER_CLASS (klass);
200 renderer_class->draw_glyphs = gtk_fill_layout_renderer_draw_glyphs;
201 renderer_class->draw_glyph_item = gtk_fill_layout_renderer_draw_glyph_item;
202 renderer_class->draw_rectangle = gtk_fill_layout_renderer_draw_rectangle;
203 renderer_class->draw_trapezoid = gtk_fill_layout_renderer_draw_trapezoid;
204 renderer_class->draw_error_underline = gtk_fill_layout_renderer_draw_error_underline;
205 renderer_class->draw_shape = gtk_fill_layout_renderer_draw_shape;
207 object_class->finalize = gtk_fill_layout_renderer_finalize;
211 _gtk_pango_fill_layout (cairo_t *cr,
214 static GtkFillLayoutRenderer *renderer = NULL;
215 gboolean has_current_point;
216 double current_x, current_y;
218 has_current_point = cairo_has_current_point (cr);
219 cairo_get_current_point (cr, ¤t_x, ¤t_y);
221 if (renderer == NULL)
222 renderer = g_object_new (GTK_TYPE_FILL_LAYOUT_RENDERER, NULL);
225 cairo_translate (cr, current_x, current_y);
228 pango_renderer_draw_layout (PANGO_RENDERER (renderer), layout, 0, 0);
232 if (has_current_point)
233 cairo_move_to (cr, current_x, current_y);
236 static AtkAttributeSet *
237 add_attribute (AtkAttributeSet *attributes,
238 AtkTextAttribute attr,
243 at = g_new (AtkAttribute, 1);
244 at->name = g_strdup (atk_text_attribute_get_name (attr));
245 at->value = g_strdup (value);
247 return g_slist_prepend (attributes, at);
251 * _gtk_pango_get_default_attributes:
252 * @attributes: a #AtkAttributeSet to add the attributes to
253 * @layout: the #PangoLayout from which to get attributes
255 * Adds the default text attributes from @layout to @attributes,
256 * after translating them from Pango attributes to ATK attributes.
258 * This is a convenience function that can be used to implement
259 * support for the #AtkText interface in widgets using Pango
262 * Returns: the modified @attributes
265 _gtk_pango_get_default_attributes (AtkAttributeSet *attributes,
268 PangoContext *context;
272 context = pango_layout_get_context (layout);
275 PangoLanguage *language;
276 PangoFontDescription *font;
278 language = pango_context_get_language (context);
280 attributes = add_attribute (attributes, ATK_TEXT_ATTR_LANGUAGE,
281 pango_language_to_string (language));
283 font = pango_context_get_font_description (context);
287 attributes = add_attribute (attributes, ATK_TEXT_ATTR_STYLE,
288 atk_text_attribute_get_value (ATK_TEXT_ATTR_STYLE,
289 pango_font_description_get_style (font)));
290 attributes = add_attribute (attributes, ATK_TEXT_ATTR_VARIANT,
291 atk_text_attribute_get_value (ATK_TEXT_ATTR_VARIANT,
292 pango_font_description_get_variant (font)));
293 attributes = add_attribute (attributes, ATK_TEXT_ATTR_STRETCH,
294 atk_text_attribute_get_value (ATK_TEXT_ATTR_STRETCH,
295 pango_font_description_get_stretch (font)));
296 attributes = add_attribute (attributes, ATK_TEXT_ATTR_FAMILY_NAME,
297 pango_font_description_get_family (font));
298 g_snprintf (buf, 60, "%d", pango_font_description_get_weight (font));
299 attributes = add_attribute (attributes, ATK_TEXT_ATTR_WEIGHT, buf);
300 g_snprintf (buf, 60, "%i", pango_font_description_get_size (font) / PANGO_SCALE);
301 attributes = add_attribute (attributes, ATK_TEXT_ATTR_SIZE, buf);
304 if (pango_layout_get_justify (layout))
310 PangoAlignment align;
312 align = pango_layout_get_alignment (layout);
313 if (align == PANGO_ALIGN_LEFT)
315 else if (align == PANGO_ALIGN_CENTER)
317 else /* PANGO_ALIGN_RIGHT */
320 attributes = add_attribute (attributes, ATK_TEXT_ATTR_JUSTIFICATION,
321 atk_text_attribute_get_value (ATK_TEXT_ATTR_JUSTIFICATION, i));
322 mode = pango_layout_get_wrap (layout);
323 if (mode == PANGO_WRAP_WORD)
325 else /* PANGO_WRAP_CHAR */
327 attributes = add_attribute (attributes, ATK_TEXT_ATTR_WRAP_MODE,
328 atk_text_attribute_get_value (ATK_TEXT_ATTR_WRAP_MODE, i));
330 attributes = add_attribute (attributes, ATK_TEXT_ATTR_STRIKETHROUGH,
331 atk_text_attribute_get_value (ATK_TEXT_ATTR_STRIKETHROUGH, 0));
332 attributes = add_attribute (attributes, ATK_TEXT_ATTR_UNDERLINE,
333 atk_text_attribute_get_value (ATK_TEXT_ATTR_UNDERLINE, 0));
334 attributes = add_attribute (attributes, ATK_TEXT_ATTR_RISE, "0");
335 attributes = add_attribute (attributes, ATK_TEXT_ATTR_SCALE, "1");
336 attributes = add_attribute (attributes, ATK_TEXT_ATTR_BG_FULL_HEIGHT, "0");
337 attributes = add_attribute (attributes, ATK_TEXT_ATTR_PIXELS_INSIDE_WRAP, "0");
338 attributes = add_attribute (attributes, ATK_TEXT_ATTR_PIXELS_BELOW_LINES, "0");
339 attributes = add_attribute (attributes, ATK_TEXT_ATTR_PIXELS_ABOVE_LINES, "0");
340 attributes = add_attribute (attributes, ATK_TEXT_ATTR_EDITABLE,
341 atk_text_attribute_get_value (ATK_TEXT_ATTR_EDITABLE, 0));
342 attributes = add_attribute (attributes, ATK_TEXT_ATTR_INVISIBLE,
343 atk_text_attribute_get_value (ATK_TEXT_ATTR_INVISIBLE, 0));
344 attributes = add_attribute (attributes, ATK_TEXT_ATTR_INDENT, "0");
345 attributes = add_attribute (attributes, ATK_TEXT_ATTR_RIGHT_MARGIN, "0");
346 attributes = add_attribute (attributes, ATK_TEXT_ATTR_LEFT_MARGIN, "0");
352 * _gtk_pango_get_run_attributes:
353 * @attributes: a #AtkAttributeSet to add attributes to
354 * @layout: the #PangoLayout to get the attributes from
355 * @offset: the offset at which the attributes are wanted
356 * @start_offset: return location for the starting offset
358 * @end_offset: return location for the ending offset of the
361 * Finds the 'run' around index (i.e. the maximal range of characters
362 * where the set of applicable attributes remains constant) and
363 * returns the starting and ending offsets for it.
365 * The attributes for the run are added to @attributes, after
366 * translating them from Pango attributes to ATK attributes.
368 * This is a convenience function that can be used to implement
369 * support for the #AtkText interface in widgets using Pango
372 * Returns: the modified #AtkAttributeSet
375 _gtk_pango_get_run_attributes (AtkAttributeSet *attributes,
381 PangoAttrIterator *iter;
383 PangoAttrString *pango_string;
384 PangoAttrInt *pango_int;
385 PangoAttrColor *pango_color;
386 PangoAttrLanguage *pango_lang;
387 PangoAttrFloat *pango_float;
388 gint index, start_index, end_index;
394 text = pango_layout_get_text (layout);
395 len = g_utf8_strlen (text, -1);
397 /* Grab the attributes of the PangoLayout, if any */
398 attr = pango_layout_get_attributes (layout);
407 iter = pango_attr_list_get_iterator (attr);
408 /* Get invariant range offsets */
409 /* If offset out of range, set offset in range */
415 index = g_utf8_offset_to_pointer (text, offset) - text;
416 pango_attr_iterator_range (iter, &start_index, &end_index);
420 if (index >= start_index && index < end_index)
422 *start_offset = g_utf8_pointer_to_offset (text, text + start_index);
423 if (end_index == G_MAXINT) /* Last iterator */
426 *end_offset = g_utf8_pointer_to_offset (text, text + end_index);
429 is_next = pango_attr_iterator_next (iter);
430 pango_attr_iterator_range (iter, &start_index, &end_index);
434 pango_string = (PangoAttrString*) pango_attr_iterator_get (iter, PANGO_ATTR_FAMILY);
435 if (pango_string != NULL)
437 value = g_strdup_printf ("%s", pango_string->value);
438 attributes = add_attribute (attributes, ATK_TEXT_ATTR_FAMILY_NAME, value);
441 pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_STYLE);
442 if (pango_int != NULL)
444 attributes = add_attribute (attributes, ATK_TEXT_ATTR_STYLE,
445 atk_text_attribute_get_value (ATK_TEXT_ATTR_STYLE, pango_int->value));
447 pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_WEIGHT);
448 if (pango_int != NULL)
450 value = g_strdup_printf ("%i", pango_int->value);
451 attributes = add_attribute (attributes, ATK_TEXT_ATTR_WEIGHT, value);
454 pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_VARIANT);
455 if (pango_int != NULL)
457 attributes = add_attribute (attributes, ATK_TEXT_ATTR_VARIANT,
458 atk_text_attribute_get_value (ATK_TEXT_ATTR_VARIANT, pango_int->value));
460 pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_STRETCH);
461 if (pango_int != NULL)
463 attributes = add_attribute (attributes, ATK_TEXT_ATTR_STRETCH,
464 atk_text_attribute_get_value (ATK_TEXT_ATTR_STRETCH, pango_int->value));
466 pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_SIZE);
467 if (pango_int != NULL)
469 value = g_strdup_printf ("%i", pango_int->value / PANGO_SCALE);
470 attributes = add_attribute (attributes, ATK_TEXT_ATTR_SIZE, value);
473 pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_UNDERLINE);
474 if (pango_int != NULL)
476 attributes = add_attribute (attributes, ATK_TEXT_ATTR_UNDERLINE,
477 atk_text_attribute_get_value (ATK_TEXT_ATTR_UNDERLINE, pango_int->value));
479 pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_STRIKETHROUGH);
480 if (pango_int != NULL)
482 attributes = add_attribute (attributes, ATK_TEXT_ATTR_STRIKETHROUGH,
483 atk_text_attribute_get_value (ATK_TEXT_ATTR_STRIKETHROUGH, pango_int->value));
485 pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_RISE);
486 if (pango_int != NULL)
488 value = g_strdup_printf ("%i", pango_int->value);
489 attributes = add_attribute (attributes, ATK_TEXT_ATTR_RISE, value);
492 pango_lang = (PangoAttrLanguage*) pango_attr_iterator_get (iter, PANGO_ATTR_LANGUAGE);
493 if (pango_lang != NULL)
495 attributes = add_attribute (attributes, ATK_TEXT_ATTR_LANGUAGE,
496 pango_language_to_string (pango_lang->value));
498 pango_float = (PangoAttrFloat*) pango_attr_iterator_get (iter, PANGO_ATTR_SCALE);
499 if (pango_float != NULL)
501 value = g_strdup_printf ("%g", pango_float->value);
502 attributes = add_attribute (attributes, ATK_TEXT_ATTR_SCALE, value);
505 pango_color = (PangoAttrColor*) pango_attr_iterator_get (iter, PANGO_ATTR_FOREGROUND);
506 if (pango_color != NULL)
508 value = g_strdup_printf ("%u,%u,%u",
509 pango_color->color.red,
510 pango_color->color.green,
511 pango_color->color.blue);
512 attributes = add_attribute (attributes, ATK_TEXT_ATTR_FG_COLOR, value);
515 pango_color = (PangoAttrColor*) pango_attr_iterator_get (iter, PANGO_ATTR_BACKGROUND);
516 if (pango_color != NULL)
518 value = g_strdup_printf ("%u,%u,%u",
519 pango_color->color.red,
520 pango_color->color.green,
521 pango_color->color.blue);
522 attributes = add_attribute (attributes, ATK_TEXT_ATTR_BG_COLOR, value);
525 pango_attr_iterator_destroy (iter);
531 * _gtk_pango_move_chars:
532 * @layout: a #PangoLayout
533 * @offset: a character offset in @layout
534 * @count: the number of characters to move from @offset
536 * Returns the position that is @count characters from the
537 * given @offset. @count may be positive or negative.
539 * For the purpose of this function, characters are defined
540 * by what Pango considers cursor positions.
542 * Returns: the new position
545 _gtk_pango_move_chars (PangoLayout *layout,
549 const PangoLogAttr *attrs;
552 attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs);
554 while (count > 0 && offset < n_attrs - 1)
558 while (offset < n_attrs - 1 && !attrs[offset].is_cursor_position);
562 while (count < 0 && offset > 0)
566 while (offset > 0 && !attrs[offset].is_cursor_position);
575 * _gtk_pango_move_words:
576 * @layout: a #PangoLayout
577 * @offset: a character offset in @layout
578 * @count: the number of words to move from @offset
580 * Returns the position that is @count words from the
581 * given @offset. @count may be positive or negative.
583 * If @count is positive, the returned position will
584 * be a word end, otherwise it will be a word start.
585 * See the Pango documentation for details on how
586 * word starts and ends are defined.
588 * Returns: the new position
591 _gtk_pango_move_words (PangoLayout *layout,
595 const PangoLogAttr *attrs;
598 attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs);
600 while (count > 0 && offset < n_attrs - 1)
604 while (offset < n_attrs - 1 && !attrs[offset].is_word_end);
608 while (count < 0 && offset > 0)
612 while (offset > 0 && !attrs[offset].is_word_start);
621 * _gtk_pango_move_sentences:
622 * @layout: a #PangoLayout
623 * @offset: a character offset in @layout
624 * @count: the number of sentences to move from @offset
626 * Returns the position that is @count sentences from the
627 * given @offset. @count may be positive or negative.
629 * If @count is positive, the returned position will
630 * be a sentence end, otherwise it will be a sentence start.
631 * See the Pango documentation for details on how
632 * sentence starts and ends are defined.
634 * Returns: the new position
637 _gtk_pango_move_sentences (PangoLayout *layout,
641 const PangoLogAttr *attrs;
644 attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs);
646 while (count > 0 && offset < n_attrs - 1)
650 while (offset < n_attrs - 1 && !attrs[offset].is_sentence_end);
654 while (count < 0 && offset > 0)
658 while (offset > 0 && !attrs[offset].is_sentence_start);
667 * _gtk_pango_move_lines:
668 * @layout: a #PangoLayout
669 * @offset: a character offset in @layout
670 * @count: the number of lines to move from @offset
672 * Returns the position that is @count lines from the
673 * given @offset. @count may be positive or negative.
675 * If @count is negative, the returned position will
676 * be the start of a line, else it will be the end of
679 * Returns: the new position
682 _gtk_pango_move_lines (PangoLayout *layout,
687 PangoLayoutLine *line;
694 text = pango_layout_get_text (layout);
695 index = g_utf8_offset_to_pointer (text, offset) - text;
696 lines = pango_layout_get_lines (layout);
700 for (l = lines; l; l = l->next)
703 if (index < line->start_index + line->length)
714 line = g_slist_nth_data (lines, num);
716 return g_utf8_pointer_to_offset (text, text + line->start_index);
720 line_pos = index - line->start_index;
722 len = g_slist_length (lines);
724 if (num >= len || (count == 0 && num == len - 1))
725 return g_utf8_strlen (text, -1) - 1;
728 pos = line->start_index + line_pos;
729 if (pos >= line->start_index + line->length)
730 pos = line->start_index + line->length - 1;
732 return g_utf8_pointer_to_offset (text, text + pos);
737 * _gtk_pango_is_inside_word:
738 * @layout: a #PangoLayout
739 * @offset: a character offset in @layout
741 * Returns whether the given position is inside
744 * Returns: %TRUE if @offset is inside a word
747 _gtk_pango_is_inside_word (PangoLayout *layout,
750 const PangoLogAttr *attrs;
753 attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs);
755 while (offset >= 0 &&
756 !(attrs[offset].is_word_start || attrs[offset].is_word_end))
760 return attrs[offset].is_word_start;
766 * _gtk_pango_is_inside_sentence:
767 * @layout: a #PangoLayout
768 * @offset: a character offset in @layout
770 * Returns whether the given position is inside
773 * Returns: %TRUE if @offset is inside a sentence
776 _gtk_pango_is_inside_sentence (PangoLayout *layout,
779 const PangoLogAttr *attrs;
782 attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs);
784 while (offset >= 0 &&
785 !(attrs[offset].is_sentence_start || attrs[offset].is_sentence_end))
789 return attrs[offset].is_sentence_start;
795 pango_layout_get_line_before (PangoLayout *layout,
796 AtkTextBoundary boundary_type,
801 PangoLayoutIter *iter;
802 PangoLayoutLine *line, *prev_line = NULL, *prev_prev_line = NULL;
803 gint index, start_index, end_index;
805 gboolean found = FALSE;
807 text = pango_layout_get_text (layout);
808 index = g_utf8_offset_to_pointer (text, offset) - text;
809 iter = pango_layout_get_iter (layout);
812 line = pango_layout_iter_get_line (iter);
813 start_index = line->start_index;
814 end_index = start_index + line->length;
816 if (index >= start_index && index <= end_index)
818 /* Found line for offset */
821 switch (boundary_type)
823 case ATK_TEXT_BOUNDARY_LINE_START:
824 end_index = start_index;
825 start_index = prev_line->start_index;
827 case ATK_TEXT_BOUNDARY_LINE_END:
829 start_index = prev_prev_line->start_index + prev_prev_line->length;
832 end_index = prev_line->start_index + prev_line->length;
835 g_assert_not_reached();
839 start_index = end_index = 0;
845 prev_prev_line = prev_line;
848 while (pango_layout_iter_next_line (iter));
852 start_index = prev_line->start_index + prev_line->length;
853 end_index = start_index;
855 pango_layout_iter_free (iter);
857 *start_offset = g_utf8_pointer_to_offset (text, text + start_index);
858 *end_offset = g_utf8_pointer_to_offset (text, text + end_index);
862 pango_layout_get_line_at (PangoLayout *layout,
863 AtkTextBoundary boundary_type,
868 PangoLayoutIter *iter;
869 PangoLayoutLine *line, *prev_line = NULL;
870 gint index, start_index, end_index;
872 gboolean found = FALSE;
874 text = pango_layout_get_text (layout);
875 index = g_utf8_offset_to_pointer (text, offset) - text;
876 iter = pango_layout_get_iter (layout);
879 line = pango_layout_iter_get_line (iter);
880 start_index = line->start_index;
881 end_index = start_index + line->length;
883 if (index >= start_index && index <= end_index)
885 /* Found line for offset */
886 switch (boundary_type)
888 case ATK_TEXT_BOUNDARY_LINE_START:
889 if (pango_layout_iter_next_line (iter))
890 end_index = pango_layout_iter_get_line (iter)->start_index;
892 case ATK_TEXT_BOUNDARY_LINE_END:
894 start_index = prev_line->start_index + prev_line->length;
897 g_assert_not_reached();
906 while (pango_layout_iter_next_line (iter));
910 start_index = prev_line->start_index + prev_line->length;
911 end_index = start_index;
913 pango_layout_iter_free (iter);
915 *start_offset = g_utf8_pointer_to_offset (text, text + start_index);
916 *end_offset = g_utf8_pointer_to_offset (text, text + end_index);
920 pango_layout_get_line_after (PangoLayout *layout,
921 AtkTextBoundary boundary_type,
926 PangoLayoutIter *iter;
927 PangoLayoutLine *line, *prev_line = NULL;
928 gint index, start_index, end_index;
930 gboolean found = FALSE;
932 text = pango_layout_get_text (layout);
933 index = g_utf8_offset_to_pointer (text, offset) - text;
934 iter = pango_layout_get_iter (layout);
937 line = pango_layout_iter_get_line (iter);
938 start_index = line->start_index;
939 end_index = start_index + line->length;
941 if (index >= start_index && index <= end_index)
943 /* Found line for offset */
944 if (pango_layout_iter_next_line (iter))
946 line = pango_layout_iter_get_line (iter);
947 switch (boundary_type)
949 case ATK_TEXT_BOUNDARY_LINE_START:
950 start_index = line->start_index;
951 if (pango_layout_iter_next_line (iter))
952 end_index = pango_layout_iter_get_line (iter)->start_index;
954 end_index = start_index + line->length;
956 case ATK_TEXT_BOUNDARY_LINE_END:
957 start_index = end_index;
958 end_index = line->start_index + line->length;
961 g_assert_not_reached();
965 start_index = end_index;
973 while (pango_layout_iter_next_line (iter));
977 start_index = prev_line->start_index + prev_line->length;
978 end_index = start_index;
980 pango_layout_iter_free (iter);
982 *start_offset = g_utf8_pointer_to_offset (text, text + start_index);
983 *end_offset = g_utf8_pointer_to_offset (text, text + end_index);
987 * _gtk_pango_get_text_before:
988 * @layout: a #PangoLayout
989 * @boundary_type: a #AtkTextBoundary
990 * @offset: a character offset in @layout
991 * @start_offset: return location for the start of the returned text
992 * @end_offset: return location for the end of the return text
994 * Gets a slice of the text from @layout before @offset.
996 * The @boundary_type determines the size of the returned slice of
997 * text. For the exact semantics of this function, see
998 * atk_text_get_text_before_offset().
1000 * Returns: a newly allocated string containing a slice of text
1001 * from layout. Free with g_free().
1004 _gtk_pango_get_text_before (PangoLayout *layout,
1005 AtkTextBoundary boundary_type,
1012 const PangoLogAttr *attrs;
1015 text = pango_layout_get_text (layout);
1021 return g_strdup ("");
1024 attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs);
1029 switch (boundary_type)
1031 case ATK_TEXT_BOUNDARY_CHAR:
1032 start = _gtk_pango_move_chars (layout, start, -1);
1035 case ATK_TEXT_BOUNDARY_WORD_START:
1036 if (!attrs[start].is_word_start)
1037 start = _gtk_pango_move_words (layout, start, -1);
1039 start = _gtk_pango_move_words (layout, start, -1);
1042 case ATK_TEXT_BOUNDARY_WORD_END:
1043 if (_gtk_pango_is_inside_word (layout, start) &&
1044 !attrs[start].is_word_start)
1045 start = _gtk_pango_move_words (layout, start, -1);
1046 while (!attrs[start].is_word_end && start > 0)
1047 start = _gtk_pango_move_chars (layout, start, -1);
1049 start = _gtk_pango_move_words (layout, start, -1);
1050 while (!attrs[start].is_word_end && start > 0)
1051 start = _gtk_pango_move_chars (layout, start, -1);
1054 case ATK_TEXT_BOUNDARY_SENTENCE_START:
1055 if (!attrs[start].is_sentence_start)
1056 start = _gtk_pango_move_sentences (layout, start, -1);
1058 start = _gtk_pango_move_sentences (layout, start, -1);
1061 case ATK_TEXT_BOUNDARY_SENTENCE_END:
1062 if (_gtk_pango_is_inside_sentence (layout, start) &&
1063 !attrs[start].is_sentence_start)
1064 start = _gtk_pango_move_sentences (layout, start, -1);
1065 while (!attrs[start].is_sentence_end && start > 0)
1066 start = _gtk_pango_move_chars (layout, start, -1);
1068 start = _gtk_pango_move_sentences (layout, start, -1);
1069 while (!attrs[start].is_sentence_end && start > 0)
1070 start = _gtk_pango_move_chars (layout, start, -1);
1073 case ATK_TEXT_BOUNDARY_LINE_START:
1074 case ATK_TEXT_BOUNDARY_LINE_END:
1075 pango_layout_get_line_before (layout, boundary_type, offset, &start, &end);
1079 *start_offset = start;
1082 g_assert (start <= end);
1084 return g_utf8_substring (text, start, end);
1088 * _gtk_pango_get_text_after:
1089 * @layout: a #PangoLayout
1090 * @boundary_type: a #AtkTextBoundary
1091 * @offset: a character offset in @layout
1092 * @start_offset: return location for the start of the returned text
1093 * @end_offset: return location for the end of the return text
1095 * Gets a slice of the text from @layout after @offset.
1097 * The @boundary_type determines the size of the returned slice of
1098 * text. For the exact semantics of this function, see
1099 * atk_text_get_text_after_offset().
1101 * Returns: a newly allocated string containing a slice of text
1102 * from layout. Free with g_free().
1105 _gtk_pango_get_text_after (PangoLayout *layout,
1106 AtkTextBoundary boundary_type,
1113 const PangoLogAttr *attrs;
1116 text = pango_layout_get_text (layout);
1122 return g_strdup ("");
1125 attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs);
1130 switch (boundary_type)
1132 case ATK_TEXT_BOUNDARY_CHAR:
1133 start = _gtk_pango_move_chars (layout, start, 1);
1135 end = _gtk_pango_move_chars (layout, end, 1);
1138 case ATK_TEXT_BOUNDARY_WORD_START:
1139 if (_gtk_pango_is_inside_word (layout, end))
1140 end = _gtk_pango_move_words (layout, end, 1);
1141 while (!attrs[end].is_word_start && end < n_attrs - 1)
1142 end = _gtk_pango_move_chars (layout, end, 1);
1144 if (end < n_attrs - 1)
1146 end = _gtk_pango_move_words (layout, end, 1);
1147 while (!attrs[end].is_word_start && end < n_attrs - 1)
1148 end = _gtk_pango_move_chars (layout, end, 1);
1152 case ATK_TEXT_BOUNDARY_WORD_END:
1153 end = _gtk_pango_move_words (layout, end, 1);
1155 if (end < n_attrs - 1)
1156 end = _gtk_pango_move_words (layout, end, 1);
1159 case ATK_TEXT_BOUNDARY_SENTENCE_START:
1160 if (_gtk_pango_is_inside_sentence (layout, end))
1161 end = _gtk_pango_move_sentences (layout, end, 1);
1162 while (!attrs[end].is_sentence_start && end < n_attrs - 1)
1163 end = _gtk_pango_move_chars (layout, end, 1);
1165 if (end < n_attrs - 1)
1167 end = _gtk_pango_move_sentences (layout, end, 1);
1168 while (!attrs[end].is_sentence_start && end < n_attrs - 1)
1169 end = _gtk_pango_move_chars (layout, end, 1);
1173 case ATK_TEXT_BOUNDARY_SENTENCE_END:
1174 end = _gtk_pango_move_sentences (layout, end, 1);
1176 if (end < n_attrs - 1)
1177 end = _gtk_pango_move_sentences (layout, end, 1);
1180 case ATK_TEXT_BOUNDARY_LINE_START:
1181 case ATK_TEXT_BOUNDARY_LINE_END:
1182 pango_layout_get_line_after (layout, boundary_type, offset, &start, &end);
1186 *start_offset = start;
1189 g_assert (start <= end);
1191 return g_utf8_substring (text, start, end);
1195 * _gtk_pango_get_text_at:
1196 * @layout: a #PangoLayout
1197 * @boundary_type: a #AtkTextBoundary
1198 * @offset: a character offset in @layout
1199 * @start_offset: return location for the start of the returned text
1200 * @end_offset: return location for the end of the return text
1202 * Gets a slice of the text from @layout at @offset.
1204 * The @boundary_type determines the size of the returned slice of
1205 * text. For the exact semantics of this function, see
1206 * atk_text_get_text_after_offset().
1208 * Returns: a newly allocated string containing a slice of text
1209 * from layout. Free with g_free().
1212 _gtk_pango_get_text_at (PangoLayout *layout,
1213 AtkTextBoundary boundary_type,
1220 const PangoLogAttr *attrs;
1223 text = pango_layout_get_text (layout);
1229 return g_strdup ("");
1232 attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs);
1237 switch (boundary_type)
1239 case ATK_TEXT_BOUNDARY_CHAR:
1240 end = _gtk_pango_move_chars (layout, end, 1);
1243 case ATK_TEXT_BOUNDARY_WORD_START:
1244 if (!attrs[start].is_word_start)
1245 start = _gtk_pango_move_words (layout, start, -1);
1246 if (_gtk_pango_is_inside_word (layout, end))
1247 end = _gtk_pango_move_words (layout, end, 1);
1248 while (!attrs[end].is_word_start && end < n_attrs - 1)
1249 end = _gtk_pango_move_chars (layout, end, 1);
1252 case ATK_TEXT_BOUNDARY_WORD_END:
1253 if (_gtk_pango_is_inside_word (layout, start) &&
1254 !attrs[start].is_word_start)
1255 start = _gtk_pango_move_words (layout, start, -1);
1256 while (!attrs[start].is_word_end && start > 0)
1257 start = _gtk_pango_move_chars (layout, start, -1);
1258 end = _gtk_pango_move_words (layout, end, 1);
1261 case ATK_TEXT_BOUNDARY_SENTENCE_START:
1262 if (!attrs[start].is_sentence_start)
1263 start = _gtk_pango_move_sentences (layout, start, -1);
1264 if (_gtk_pango_is_inside_sentence (layout, end))
1265 end = _gtk_pango_move_sentences (layout, end, 1);
1266 while (!attrs[end].is_sentence_start && end < n_attrs - 1)
1267 end = _gtk_pango_move_chars (layout, end, 1);
1270 case ATK_TEXT_BOUNDARY_SENTENCE_END:
1271 if (_gtk_pango_is_inside_sentence (layout, start) &&
1272 !attrs[start].is_sentence_start)
1273 start = _gtk_pango_move_sentences (layout, start, -1);
1274 while (!attrs[start].is_sentence_end && start > 0)
1275 start = _gtk_pango_move_chars (layout, start, -1);
1276 end = _gtk_pango_move_sentences (layout, end, 1);
1279 case ATK_TEXT_BOUNDARY_LINE_START:
1280 case ATK_TEXT_BOUNDARY_LINE_END:
1281 pango_layout_get_line_at (layout, boundary_type, offset, &start, &end);
1285 *start_offset = start;
1288 g_assert (start <= end);
1290 return g_utf8_substring (text, start, end);