1 /* GAIL - The GNOME Accessibility Implementation Library
2 * Copyright 2001 Sun Microsystems Inc.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
23 #include "gailtextutil.h"
26 * SECTION:gailtextutil
27 * @Short_description: GailTextUtil is a utility class which can be used to
28 * implement some of the #AtkText functions for accessible objects
29 * which implement #AtkText.
30 * @Title: GailTextUtil
32 * GailTextUtil is a utility class which can be used to implement the
33 * #AtkText functions which get text for accessible objects which implement
36 * In GAIL it is used by the accsesible objects for #GnomeCanvasText, #GtkEntry,
37 * #GtkLabel, #GtkCellRendererText and #GtkTextView.
40 static void gail_text_util_class_init (GailTextUtilClass *klass);
42 static void gail_text_util_init (GailTextUtil *textutil);
43 static void gail_text_util_finalize (GObject *object);
46 static void get_pango_text_offsets (PangoLayout *layout,
47 GtkTextBuffer *buffer,
48 GailOffsetType function,
49 AtkTextBoundary boundary_type,
53 GtkTextIter *start_iter,
54 GtkTextIter *end_iter);
55 static GObjectClass *parent_class = NULL;
58 gail_text_util_get_type(void)
60 static GType type = 0;
64 const GTypeInfo tinfo =
66 sizeof (GailTextUtilClass),
67 (GBaseInitFunc) NULL, /* base init */
68 (GBaseFinalizeFunc) NULL, /* base finalize */
69 (GClassInitFunc) gail_text_util_class_init,
70 (GClassFinalizeFunc) NULL, /* class finalize */
71 NULL, /* class data */
74 (GInstanceInitFunc) gail_text_util_init,
75 NULL, /* value table */
78 type = g_type_register_static (G_TYPE_OBJECT, "EvGailTextUtil", &tinfo, 0);
86 * This function creates a new GailTextUtil object.
88 * Returns: the GailTextUtil object
91 gail_text_util_new (void)
93 return GAIL_TEXT_UTIL (g_object_new (GAIL_TYPE_TEXT_UTIL, NULL));
97 gail_text_util_init (GailTextUtil *textutil)
99 textutil->buffer = NULL;
103 gail_text_util_class_init (GailTextUtilClass *klass)
105 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
107 parent_class = g_type_class_peek_parent (klass);
109 gobject_class->finalize = gail_text_util_finalize;
113 gail_text_util_finalize (GObject *object)
115 GailTextUtil *textutil = GAIL_TEXT_UTIL (object);
117 if (textutil->buffer)
118 g_object_unref (textutil->buffer);
120 G_OBJECT_CLASS (parent_class)->finalize (object);
124 * gail_text_util_text_setup:
125 * @textutil: The #GailTextUtil to be initialized.
126 * @text: A gchar* which points to the text to be stored in the GailTextUtil
128 * This function initializes the GailTextUtil with the specified character string,
131 gail_text_util_text_setup (GailTextUtil *textutil,
134 g_return_if_fail (GAIL_IS_TEXT_UTIL (textutil));
136 if (textutil->buffer)
140 g_object_unref (textutil->buffer);
141 textutil->buffer = NULL;
147 textutil->buffer = gtk_text_buffer_new (NULL);
150 gtk_text_buffer_set_text (textutil->buffer, text, -1);
154 * gail_text_util_buffer_setup:
155 * @textutil: A #GailTextUtil to be initialized
156 * @buffer: The #GtkTextBuffer which identifies the text to be stored in the GailUtil.
158 * This function initializes the GailTextUtil with the specified GtkTextBuffer
161 gail_text_util_buffer_setup (GailTextUtil *textutil,
162 GtkTextBuffer *buffer)
164 g_return_if_fail (GAIL_IS_TEXT_UTIL (textutil));
166 textutil->buffer = g_object_ref (buffer);
170 * gail_text_util_get_text:
171 * @textutil: A #GailTextUtil
172 * @layout: A gpointer which is a PangoLayout, a GtkTreeView of NULL
173 * @function: An enumeration specifying whether to return the text before, at, or
175 * @boundary_type: The boundary type.
176 * @offset: The offset of the text in the GailTextUtil
177 * @start_offset: Address of location in which the start offset is returned
178 * @end_offset: Address of location in which the end offset is returned
180 * This function gets the requested substring from the text in the GtkTextUtil.
181 * The layout is used only for getting the text on a line. The value is NULL
182 * for a GtkTextView which is not wrapped, is a GtkTextView for a GtkTextView
183 * which is wrapped and is a PangoLayout otherwise.
185 * Returns: the substring requested
188 gail_text_util_get_text (GailTextUtil *textutil,
190 GailOffsetType function,
191 AtkTextBoundary boundary_type,
196 GtkTextIter start, end;
198 GtkTextBuffer *buffer;
200 g_return_val_if_fail (GAIL_IS_TEXT_UTIL (textutil), NULL);
202 buffer = textutil->buffer;
210 if (!gtk_text_buffer_get_char_count (buffer))
214 return g_strdup ("");
216 gtk_text_buffer_get_iter_at_offset (buffer, &start, offset);
223 case GAIL_BEFORE_OFFSET:
224 switch (boundary_type)
226 case ATK_TEXT_BOUNDARY_CHAR:
227 gtk_text_iter_backward_char(&start);
229 case ATK_TEXT_BOUNDARY_WORD_START:
230 if (!gtk_text_iter_starts_word (&start))
231 gtk_text_iter_backward_word_start (&start);
233 gtk_text_iter_backward_word_start(&start);
235 case ATK_TEXT_BOUNDARY_WORD_END:
236 if (gtk_text_iter_inside_word (&start) &&
237 !gtk_text_iter_starts_word (&start))
238 gtk_text_iter_backward_word_start (&start);
239 while (!gtk_text_iter_ends_word (&start))
241 if (!gtk_text_iter_backward_char (&start))
245 gtk_text_iter_backward_word_start(&start);
246 while (!gtk_text_iter_ends_word (&start))
248 if (!gtk_text_iter_backward_char (&start))
252 case ATK_TEXT_BOUNDARY_SENTENCE_START:
253 if (!gtk_text_iter_starts_sentence (&start))
254 gtk_text_iter_backward_sentence_start (&start);
256 gtk_text_iter_backward_sentence_start (&start);
258 case ATK_TEXT_BOUNDARY_SENTENCE_END:
259 if (gtk_text_iter_inside_sentence (&start) &&
260 !gtk_text_iter_starts_sentence (&start))
261 gtk_text_iter_backward_sentence_start (&start);
262 while (!gtk_text_iter_ends_sentence (&start))
264 if (!gtk_text_iter_backward_char (&start))
268 gtk_text_iter_backward_sentence_start (&start);
269 while (!gtk_text_iter_ends_sentence (&start))
271 if (!gtk_text_iter_backward_char (&start))
275 case ATK_TEXT_BOUNDARY_LINE_START:
278 line_number = gtk_text_iter_get_line (&start);
279 if (line_number == 0)
281 gtk_text_buffer_get_iter_at_offset (buffer,
286 gtk_text_iter_backward_line (&start);
287 gtk_text_iter_forward_line (&start);
290 gtk_text_iter_backward_line (&start);
292 else if GTK_IS_TEXT_VIEW (layout)
294 GtkTextView *view = GTK_TEXT_VIEW (layout);
296 gtk_text_view_backward_display_line_start (view, &start);
298 gtk_text_view_backward_display_line (view, &start);
300 else if (PANGO_IS_LAYOUT (layout))
301 get_pango_text_offsets (PANGO_LAYOUT (layout),
311 case ATK_TEXT_BOUNDARY_LINE_END:
314 line_number = gtk_text_iter_get_line (&start);
315 if (line_number == 0)
317 gtk_text_buffer_get_iter_at_offset (buffer,
323 gtk_text_iter_backward_line (&start);
325 while (!gtk_text_iter_ends_line (&start))
327 if (!gtk_text_iter_backward_char (&start))
330 gtk_text_iter_forward_to_line_end (&end);
333 else if GTK_IS_TEXT_VIEW (layout)
335 GtkTextView *view = GTK_TEXT_VIEW (layout);
337 gtk_text_view_backward_display_line_start (view, &start);
338 if (!gtk_text_iter_is_start (&start))
340 gtk_text_view_backward_display_line (view, &start);
342 if (!gtk_text_iter_is_start (&start))
344 gtk_text_view_backward_display_line (view, &start);
345 gtk_text_view_forward_display_line_end (view, &start);
347 gtk_text_view_forward_display_line_end (view, &end);
354 else if (PANGO_IS_LAYOUT (layout))
355 get_pango_text_offsets (PANGO_LAYOUT (layout),
369 switch (boundary_type)
371 case ATK_TEXT_BOUNDARY_CHAR:
372 gtk_text_iter_forward_char (&end);
374 case ATK_TEXT_BOUNDARY_WORD_START:
375 if (!gtk_text_iter_starts_word (&start))
376 gtk_text_iter_backward_word_start (&start);
377 if (gtk_text_iter_inside_word (&end))
378 gtk_text_iter_forward_word_end (&end);
379 while (!gtk_text_iter_starts_word (&end))
381 if (!gtk_text_iter_forward_char (&end))
385 case ATK_TEXT_BOUNDARY_WORD_END:
386 if (gtk_text_iter_inside_word (&start) &&
387 !gtk_text_iter_starts_word (&start))
388 gtk_text_iter_backward_word_start (&start);
389 while (!gtk_text_iter_ends_word (&start))
391 if (!gtk_text_iter_backward_char (&start))
394 gtk_text_iter_forward_word_end (&end);
396 case ATK_TEXT_BOUNDARY_SENTENCE_START:
397 if (!gtk_text_iter_starts_sentence (&start))
398 gtk_text_iter_backward_sentence_start (&start);
399 if (gtk_text_iter_inside_sentence (&end))
400 gtk_text_iter_forward_sentence_end (&end);
401 while (!gtk_text_iter_starts_sentence (&end))
403 if (!gtk_text_iter_forward_char (&end))
407 case ATK_TEXT_BOUNDARY_SENTENCE_END:
408 if (gtk_text_iter_inside_sentence (&start) &&
409 !gtk_text_iter_starts_sentence (&start))
410 gtk_text_iter_backward_sentence_start (&start);
411 while (!gtk_text_iter_ends_sentence (&start))
413 if (!gtk_text_iter_backward_char (&start))
416 gtk_text_iter_forward_sentence_end (&end);
418 case ATK_TEXT_BOUNDARY_LINE_START:
421 line_number = gtk_text_iter_get_line (&start);
422 if (line_number == 0)
424 gtk_text_buffer_get_iter_at_offset (buffer,
429 gtk_text_iter_backward_line (&start);
430 gtk_text_iter_forward_line (&start);
432 gtk_text_iter_forward_line (&end);
434 else if GTK_IS_TEXT_VIEW (layout)
436 GtkTextView *view = GTK_TEXT_VIEW (layout);
438 gtk_text_view_backward_display_line_start (view, &start);
440 * The call to gtk_text_iter_forward_to_end() is needed
441 * because of bug 81960
443 if (!gtk_text_view_forward_display_line (view, &end))
444 gtk_text_iter_forward_to_end (&end);
446 else if PANGO_IS_LAYOUT (layout)
447 get_pango_text_offsets (PANGO_LAYOUT (layout),
458 case ATK_TEXT_BOUNDARY_LINE_END:
461 line_number = gtk_text_iter_get_line (&start);
462 if (line_number == 0)
464 gtk_text_buffer_get_iter_at_offset (buffer,
469 gtk_text_iter_backward_line (&start);
470 gtk_text_iter_forward_line (&start);
472 while (!gtk_text_iter_ends_line (&start))
474 if (!gtk_text_iter_backward_char (&start))
477 gtk_text_iter_forward_to_line_end (&end);
479 else if GTK_IS_TEXT_VIEW (layout)
481 GtkTextView *view = GTK_TEXT_VIEW (layout);
483 gtk_text_view_backward_display_line_start (view, &start);
484 if (!gtk_text_iter_is_start (&start))
486 gtk_text_view_backward_display_line (view, &start);
487 gtk_text_view_forward_display_line_end (view, &start);
489 gtk_text_view_forward_display_line_end (view, &end);
491 else if PANGO_IS_LAYOUT (layout)
492 get_pango_text_offsets (PANGO_LAYOUT (layout),
505 case GAIL_AFTER_OFFSET:
506 switch (boundary_type)
508 case ATK_TEXT_BOUNDARY_CHAR:
509 gtk_text_iter_forward_char(&start);
510 gtk_text_iter_forward_chars(&end, 2);
512 case ATK_TEXT_BOUNDARY_WORD_START:
513 if (gtk_text_iter_inside_word (&end))
514 gtk_text_iter_forward_word_end (&end);
515 while (!gtk_text_iter_starts_word (&end))
517 if (!gtk_text_iter_forward_char (&end))
521 if (!gtk_text_iter_is_end (&end))
523 gtk_text_iter_forward_word_end (&end);
524 while (!gtk_text_iter_starts_word (&end))
526 if (!gtk_text_iter_forward_char (&end))
531 case ATK_TEXT_BOUNDARY_WORD_END:
532 gtk_text_iter_forward_word_end (&end);
534 if (!gtk_text_iter_is_end (&end))
535 gtk_text_iter_forward_word_end (&end);
537 case ATK_TEXT_BOUNDARY_SENTENCE_START:
538 if (gtk_text_iter_inside_sentence (&end))
539 gtk_text_iter_forward_sentence_end (&end);
540 while (!gtk_text_iter_starts_sentence (&end))
542 if (!gtk_text_iter_forward_char (&end))
546 if (!gtk_text_iter_is_end (&end))
548 gtk_text_iter_forward_sentence_end (&end);
549 while (!gtk_text_iter_starts_sentence (&end))
551 if (!gtk_text_iter_forward_char (&end))
556 case ATK_TEXT_BOUNDARY_SENTENCE_END:
557 gtk_text_iter_forward_sentence_end (&end);
559 if (!gtk_text_iter_is_end (&end))
560 gtk_text_iter_forward_sentence_end (&end);
562 case ATK_TEXT_BOUNDARY_LINE_START:
565 gtk_text_iter_forward_line (&end);
567 gtk_text_iter_forward_line (&end);
569 else if GTK_IS_TEXT_VIEW (layout)
571 GtkTextView *view = GTK_TEXT_VIEW (layout);
573 gtk_text_view_forward_display_line (view, &end);
575 gtk_text_view_forward_display_line (view, &end);
577 else if (PANGO_IS_LAYOUT (layout))
578 get_pango_text_offsets (PANGO_LAYOUT (layout),
588 case ATK_TEXT_BOUNDARY_LINE_END:
591 gtk_text_iter_forward_line (&start);
593 if (!gtk_text_iter_is_end (&start))
595 while (!gtk_text_iter_ends_line (&start))
597 if (!gtk_text_iter_backward_char (&start))
600 gtk_text_iter_forward_to_line_end (&end);
603 else if GTK_IS_TEXT_VIEW (layout)
605 GtkTextView *view = GTK_TEXT_VIEW (layout);
607 gtk_text_view_forward_display_line_end (view, &end);
609 gtk_text_view_forward_display_line (view, &end);
610 gtk_text_view_forward_display_line_end (view, &end);
612 else if (PANGO_IS_LAYOUT (layout))
613 get_pango_text_offsets (PANGO_LAYOUT (layout),
626 *start_offset = gtk_text_iter_get_offset (&start);
627 *end_offset = gtk_text_iter_get_offset (&end);
629 return gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
633 * gail_text_util_get_substring:
634 * @textutil: A #GailTextUtil
635 * @start_pos: The start position of the substring
636 * @end_pos: The end position of the substring.
638 * Gets the substring indicated by @start_pos and @end_pos
640 * Returns: the substring indicated by @start_pos and @end_pos
643 gail_text_util_get_substring (GailTextUtil *textutil,
647 GtkTextIter start, end;
648 GtkTextBuffer *buffer;
650 g_return_val_if_fail(GAIL_IS_TEXT_UTIL (textutil), NULL);
652 buffer = textutil->buffer;
656 gtk_text_buffer_get_iter_at_offset (buffer, &start, start_pos);
658 gtk_text_buffer_get_end_iter (buffer, &end);
660 gtk_text_buffer_get_iter_at_offset (buffer, &end, end_pos);
662 return gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
666 get_pango_text_offsets (PangoLayout *layout,
667 GtkTextBuffer *buffer,
668 GailOffsetType function,
669 AtkTextBoundary boundary_type,
673 GtkTextIter *start_iter,
674 GtkTextIter *end_iter)
676 PangoLayoutIter *iter;
677 PangoLayoutLine *line, *prev_line = NULL, *prev_prev_line = NULL;
678 gint index, start_index, end_index;
680 gboolean found = FALSE;
682 text = pango_layout_get_text (layout);
683 index = g_utf8_offset_to_pointer (text, offset) - text;
684 iter = pango_layout_get_iter (layout);
687 line = pango_layout_iter_get_line (iter);
688 start_index = line->start_index;
689 end_index = start_index + line->length;
691 if (index >= start_index && index <= end_index)
694 * Found line for offset
698 case GAIL_BEFORE_OFFSET:
700 * We want the previous line
704 switch (boundary_type)
706 case ATK_TEXT_BOUNDARY_LINE_START:
707 end_index = start_index;
708 start_index = prev_line->start_index;
710 case ATK_TEXT_BOUNDARY_LINE_END:
712 start_index = prev_prev_line->start_index +
713 prev_prev_line->length;
714 end_index = prev_line->start_index + prev_line->length;
717 g_assert_not_reached();
721 start_index = end_index = 0;
724 switch (boundary_type)
726 case ATK_TEXT_BOUNDARY_LINE_START:
727 if (pango_layout_iter_next_line (iter))
728 end_index = pango_layout_iter_get_line (iter)->start_index;
730 case ATK_TEXT_BOUNDARY_LINE_END:
732 start_index = prev_line->start_index +
736 g_assert_not_reached();
739 case GAIL_AFTER_OFFSET:
741 * We want the next line
743 if (pango_layout_iter_next_line (iter))
745 line = pango_layout_iter_get_line (iter);
746 switch (boundary_type)
748 case ATK_TEXT_BOUNDARY_LINE_START:
749 start_index = line->start_index;
750 if (pango_layout_iter_next_line (iter))
751 end_index = pango_layout_iter_get_line (iter)->start_index;
753 end_index = start_index + line->length;
755 case ATK_TEXT_BOUNDARY_LINE_END:
756 start_index = end_index;
757 end_index = line->start_index + line->length;
760 g_assert_not_reached();
764 start_index = end_index;
770 prev_prev_line = prev_line;
773 while (pango_layout_iter_next_line (iter));
777 start_index = prev_line->start_index + prev_line->length;
778 end_index = start_index;
780 pango_layout_iter_free (iter);
781 *start_offset = g_utf8_pointer_to_offset (text, text + start_index);
782 *end_offset = g_utf8_pointer_to_offset (text, text + end_index);
784 gtk_text_buffer_get_iter_at_offset (buffer, start_iter, *start_offset);
785 gtk_text_buffer_get_iter_at_offset (buffer, end_iter, *end_offset);