2.36.0
[platform/upstream/atk.git] / atk / atktext.c
1 /* ATK - The Accessibility Toolkit for GTK+
2  * Copyright 2001 Sun Microsystems Inc.
3  *
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.
8  *
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.
13  *
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.
18  */
19
20 #include "config.h"
21
22 #include "atk.h"
23 #include "atkmarshal.h"
24
25 #include <string.h>
26
27 /**
28  * SECTION:atktext
29  * @Short_description: The ATK interface implemented by components
30  *  with text content.
31  * @Title:AtkText
32  *
33  * #AtkText should be implemented by #AtkObjects on behalf of widgets
34  * that have text content which is either attributed or otherwise
35  * non-trivial.  #AtkObjects whose text content is simple,
36  * unattributed, and very brief may expose that content via
37  * #atk_object_get_name instead; however if the text is editable,
38  * multi-line, typically longer than three or four words, attributed,
39  * selectable, or if the object already uses the 'name' ATK property
40  * for other information, the #AtkText interface should be used to
41  * expose the text content.  In the case of editable text content,
42  * #AtkEditableText (a subtype of the #AtkText interface) should be
43  * implemented instead.
44  *
45  *  #AtkText provides not only traversal facilities and change
46  * notification for text content, but also caret tracking and glyph
47  * bounding box calculations.  Note that the text strings are exposed
48  * as UTF-8, and are therefore potentially multi-byte, and
49  * caret-to-byte offset mapping makes no assumptions about the
50  * character length; also bounding box glyph-to-offset mapping may be
51  * complex for languages which use ligatures.
52  */
53
54 static GPtrArray *extra_attributes = NULL;
55
56 enum {
57   TEXT_CHANGED,
58   TEXT_CARET_MOVED,
59   TEXT_SELECTION_CHANGED,
60   TEXT_ATTRIBUTES_CHANGED,
61   TEXT_INSERT,
62   TEXT_REMOVE,
63   LAST_SIGNAL
64 };
65
66 static const char boolean[] =
67   "false\0"
68   "true";
69 static const guint8 boolean_offsets[] = {
70   0, 6
71 };
72
73 static const char style[] =
74   "normal\0"
75   "oblique\0"
76   "italic";
77 static const guint8 style_offsets[] = {
78   0, 7, 15
79 };
80
81 static const char variant[] =
82   "normal\0"
83   "small_caps";
84 static const guint8 variant_offsets[] = {
85   0, 7
86 };
87
88 static const char stretch[] =
89   "ultra_condensed\0"
90   "extra_condensed\0"
91   "condensed\0"
92   "semi_condensed\0"
93   "normal\0"
94   "semi_expanded\0"
95   "expanded\0"
96   "extra_expanded\0"
97   "ultra_expanded";
98 static const guint8 stretch_offsets[] = {
99   0, 16, 32, 42, 57, 64, 78, 87, 102
100 };
101
102 static const char justification[] =
103   "left\0"
104   "right\0"
105   "center\0"
106   "fill";
107 static const guint8 justification_offsets[] = {
108   0, 5, 11, 18
109 };
110
111 static const char direction[] =
112   "none\0"
113   "ltr\0"
114   "rtl";
115 static const guint8 direction_offsets[] = {
116   0, 5, 9
117 };
118
119 static const char wrap_mode[] =
120   "none\0"
121   "char\0"
122   "word\0"
123   "word_char";
124 static const guint8 wrap_mode_offsets[] = {
125   0, 5, 10, 15
126 };
127
128 static const char underline[] =
129   "none\0"
130   "single\0"
131   "double\0"
132   "low\0"
133   "error";
134 static const guint8 underline_offsets[] = {
135   0, 5, 12, 19, 23
136 };
137
138 static const char text_position[] =
139   "baseline\0"
140   "super\0"
141   "sub\0";
142 static const guint8 text_position_offsets[] = {
143   0, 9, 15,
144 };
145
146 static void atk_text_base_init (AtkTextIface *class);
147
148 static void atk_text_real_get_range_extents  (AtkText          *text,
149                                               gint             start_offset,
150                                               gint             end_offset,
151                                               AtkCoordType     coord_type,
152                                               AtkTextRectangle *rect);
153
154 static AtkTextRange** atk_text_real_get_bounded_ranges (AtkText          *text,
155                                                         AtkTextRectangle *rect,
156                                                         AtkCoordType     coord_type,
157                                                         AtkTextClipType  x_clip_type,
158                                                         AtkTextClipType  y_clip_type);
159
160 static guint atk_text_signals[LAST_SIGNAL] = { 0 };
161
162 GType
163 atk_text_get_type (void)
164 {
165   static GType type = 0;
166
167   if (!type) 
168     {
169       static const GTypeInfo tinfo =
170       {
171         sizeof (AtkTextIface),
172         (GBaseInitFunc) atk_text_base_init,
173         (GBaseFinalizeFunc) NULL,
174         (GClassInitFunc) NULL /* atk_text_interface_init */ ,
175         (GClassFinalizeFunc) NULL,
176
177       };
178
179       type = g_type_register_static (G_TYPE_INTERFACE, "AtkText", &tinfo, 0);
180     }
181
182   return type;
183 }
184
185 static void
186 atk_text_base_init (AtkTextIface *class)
187 {
188   static gboolean initialized = FALSE;
189   
190   if (! initialized)
191     {
192       /* 
193        * Note that text_changed signal supports details "insert", "delete", 
194        * possibly "replace". 
195        */
196      
197       class->get_range_extents = atk_text_real_get_range_extents; 
198       class->get_bounded_ranges = atk_text_real_get_bounded_ranges; 
199
200       /**
201        * AtkText::text-changed:
202        * @atktext: the object which received the signal.
203        * @arg1: The position (character offset) of the insertion or deletion.
204        * @arg2: The length (in characters) of text inserted or deleted.
205        *
206        * The "text-changed" signal is emitted when the text of the
207        * object which implements the AtkText interface changes, This
208        * signal will have a detail which is either "insert" or
209        * "delete" which identifies whether the text change was an
210        * insertion or a deletion.
211        *
212        * Deprecated: 2.9.4: Use #AtkObject::text-insert or
213        * #AtkObject::text-remove instead.
214        */
215       atk_text_signals[TEXT_CHANGED] =
216         g_signal_new ("text_changed",
217                       ATK_TYPE_TEXT,
218                       G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
219                       G_STRUCT_OFFSET (AtkTextIface, text_changed), 
220                       (GSignalAccumulator) NULL, NULL,
221                       atk_marshal_VOID__INT_INT,
222                       G_TYPE_NONE,
223                       2, G_TYPE_INT, G_TYPE_INT);
224
225       /**
226        * AtkText::text-insert:
227        * @atktext: the object which received the signal.
228        * @arg1: The position (character offset) of the insertion.
229        * @arg2: The length (in characters) of text inserted.
230        * @arg3: The new text inserted
231        *
232        * The "text-insert" signal is emitted when a new text is
233        * inserted. If the signal was not triggered by the user
234        * (e.g. typing or pasting text), the "system" detail should be
235        * included.
236        */
237       atk_text_signals[TEXT_INSERT] =
238         g_signal_new ("text_insert",
239                       ATK_TYPE_TEXT,
240                       G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
241                       0,
242                       (GSignalAccumulator) NULL, NULL,
243                       atk_marshal_VOID__INT_INT_STRING,
244                       G_TYPE_NONE,
245                       3, G_TYPE_INT, G_TYPE_INT, G_TYPE_STRING);
246
247       /**
248        * AtkText::text-remove:
249        * @atktext: the object which received the signal.
250        * @arg1: The position (character offset) of the removal.
251        * @arg2: The length (in characters) of text removed.
252        * @arg3: The old text removed
253        *
254        * The "text-remove" signal is emitted when a new text is
255        * removed. If the signal was not triggered by the user
256        * (e.g. typing or pasting text), the "system" detail should be
257        * included.
258        */
259       atk_text_signals[TEXT_REMOVE] =
260         g_signal_new ("text_remove",
261                       ATK_TYPE_TEXT,
262                       G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
263                       0,
264                       (GSignalAccumulator) NULL, NULL,
265                       atk_marshal_VOID__INT_INT_STRING,
266                       G_TYPE_NONE,
267                       3, G_TYPE_INT, G_TYPE_INT, G_TYPE_STRING);
268
269       /**
270        * AtkText::text-caret-moved:
271        * @atktext: the object which received the signal.
272        * @arg1: The new position of the text caret.
273        *
274        * The "text-caret-moved" signal is emitted when the caret
275        * position of the text of an object which implements AtkText
276        * changes.
277        */
278       atk_text_signals[TEXT_CARET_MOVED] =
279         g_signal_new ("text_caret_moved",
280                       ATK_TYPE_TEXT,
281                       G_SIGNAL_RUN_LAST,
282                       G_STRUCT_OFFSET (AtkTextIface, text_caret_moved),
283                       (GSignalAccumulator) NULL, NULL,
284                       g_cclosure_marshal_VOID__INT,
285                       G_TYPE_NONE,
286                       1, G_TYPE_INT);
287
288       /**
289        * AtkText::text-selection-changed:
290        * @atktext: the object which received the signal.
291        *
292        * The "text-selection-changed" signal is emitted when the
293        * selected text of an object which implements AtkText changes.
294        */
295       atk_text_signals[TEXT_SELECTION_CHANGED] =
296         g_signal_new ("text_selection_changed",
297                       ATK_TYPE_TEXT,
298                       G_SIGNAL_RUN_LAST,
299                       G_STRUCT_OFFSET (AtkTextIface, text_selection_changed),
300                       (GSignalAccumulator) NULL, NULL,
301                       g_cclosure_marshal_VOID__VOID,
302                       G_TYPE_NONE, 0);
303       /**
304        * AtkText::text-attributes-changed:
305        * @atktext: the object which received the signal.
306        *
307        * The "text-attributes-changed" signal is emitted when the text
308        * attributes of the text of an object which implements AtkText
309        * changes.
310        */
311       atk_text_signals[TEXT_ATTRIBUTES_CHANGED] =
312         g_signal_new ("text_attributes_changed",
313                       ATK_TYPE_TEXT,
314                       G_SIGNAL_RUN_LAST,
315                       G_STRUCT_OFFSET (AtkTextIface, text_attributes_changed),
316                       (GSignalAccumulator) NULL, NULL,
317                       g_cclosure_marshal_VOID__VOID,
318                       G_TYPE_NONE, 0);
319
320       
321       initialized = TRUE;
322     }
323 }
324
325 /**
326  * atk_text_get_text:
327  * @text: an #AtkText
328  * @start_offset: a starting character offset within @text
329  * @end_offset: an ending character offset within @text, or -1 for the end of the string.
330  *
331  * Gets the specified text.
332  *
333  * Returns: a newly allocated string containing the text from @start_offset up
334  *          to, but not including @end_offset. Use g_free() to free the returned
335  *          string.
336  **/
337 gchar*
338 atk_text_get_text (AtkText      *text,
339                    gint         start_offset,
340                    gint         end_offset)
341 {
342   AtkTextIface *iface;
343   
344   g_return_val_if_fail (ATK_IS_TEXT (text), NULL);
345
346   iface = ATK_TEXT_GET_IFACE (text);
347
348   if (start_offset < 0 || end_offset < -1 ||
349       (end_offset != -1 && end_offset < start_offset))
350     return NULL;
351
352   if (iface->get_text)
353     return (*(iface->get_text)) (text, start_offset, end_offset);
354   else
355     return NULL;
356 }
357
358 /**
359  * atk_text_get_character_at_offset:
360  * @text: an #AtkText
361  * @offset: a character offset within @text
362  *
363  * Gets the specified text.
364  *
365  * Returns: the character at @offset or 0 in the case of failure.
366  **/
367 gunichar
368 atk_text_get_character_at_offset (AtkText      *text,
369                                   gint         offset)
370 {
371   AtkTextIface *iface;
372
373   g_return_val_if_fail (ATK_IS_TEXT (text), (gunichar) 0);
374
375   iface = ATK_TEXT_GET_IFACE (text);
376
377   if (iface->get_character_at_offset)
378     return (*(iface->get_character_at_offset)) (text, offset);
379   else
380     return (gunichar) 0;
381 }
382
383 /**
384  * atk_text_get_text_after_offset:
385  * @text: an #AtkText
386  * @offset: position
387  * @boundary_type: An #AtkTextBoundary
388  * @start_offset: (out): the starting character offset of the returned string
389  * @end_offset: (out): the offset of the first character after the
390  *              returned substring
391  *
392  * Gets the specified text.
393  *
394  * Deprecated: 2.9.3: Please use atk_text_get_string_at_offset() instead.
395  *
396  * Returns: a newly allocated string containing the text after @offset bounded
397  *          by the specified @boundary_type. Use g_free() to free the returned
398  *          string.
399  **/
400 gchar*
401 atk_text_get_text_after_offset (AtkText          *text,
402                                 gint             offset,
403                                 AtkTextBoundary  boundary_type,
404                                 gint             *start_offset,
405                                 gint             *end_offset)
406 {
407   AtkTextIface *iface;
408   gint local_start_offset, local_end_offset;
409   gint *real_start_offset, *real_end_offset;
410
411   g_return_val_if_fail (ATK_IS_TEXT (text), NULL);
412
413   if (start_offset)
414     real_start_offset = start_offset;
415   else
416     real_start_offset = &local_start_offset;
417   if (end_offset)
418     real_end_offset = end_offset;
419   else
420     real_end_offset = &local_end_offset;
421
422   if (offset < 0)
423     return NULL;
424
425   iface = ATK_TEXT_GET_IFACE (text);
426
427   if (iface->get_text_after_offset)
428     return (*(iface->get_text_after_offset)) (text, offset, boundary_type, real_start_offset, real_end_offset);
429   else
430     return NULL;
431 }
432
433 /**
434  * atk_text_get_text_at_offset:
435  * @text: an #AtkText
436  * @offset: position
437  * @boundary_type: An #AtkTextBoundary
438  * @start_offset: (out): the starting character offset of the returned string
439  * @end_offset: (out): the offset of the first character after the
440  *              returned substring
441  *
442  * Gets the specified text.
443  *
444  * If the boundary_type if ATK_TEXT_BOUNDARY_CHAR the character at the
445  * offset is returned.
446  *
447  * If the boundary_type is ATK_TEXT_BOUNDARY_WORD_START the returned string
448  * is from the word start at or before the offset to the word start after
449  * the offset.
450  *
451  * The returned string will contain the word at the offset if the offset
452  * is inside a word and will contain the word before the offset if the
453  * offset is not inside a word.
454  *
455  * If the boundary type is ATK_TEXT_BOUNDARY_SENTENCE_START the returned
456  * string is from the sentence start at or before the offset to the sentence
457  * start after the offset.
458  *
459  * The returned string will contain the sentence at the offset if the offset
460  * is inside a sentence and will contain the sentence before the offset
461  * if the offset is not inside a sentence.
462  *
463  * If the boundary type is ATK_TEXT_BOUNDARY_LINE_START the returned
464  * string is from the line start at or before the offset to the line
465  * start after the offset.
466  *
467  * Deprecated: This method is deprecated since ATK version
468  * 2.9.4. Please use atk_text_get_string_at_offset() instead.
469  *
470  * Returns: a newly allocated string containing the text at @offset bounded
471  *          by the specified @boundary_type. Use g_free() to free the returned
472  *          string.
473  **/
474 gchar*
475 atk_text_get_text_at_offset (AtkText          *text,
476                              gint             offset,
477                              AtkTextBoundary  boundary_type,
478                              gint             *start_offset,
479                              gint             *end_offset)
480 {
481   AtkTextIface *iface;
482   gint local_start_offset, local_end_offset;
483   gint *real_start_offset, *real_end_offset;
484
485   g_return_val_if_fail (ATK_IS_TEXT (text), NULL);
486
487   if (start_offset)
488     real_start_offset = start_offset;
489   else
490     real_start_offset = &local_start_offset;
491   if (end_offset)
492     real_end_offset = end_offset;
493   else
494     real_end_offset = &local_end_offset;
495
496   iface = ATK_TEXT_GET_IFACE (text);
497
498   if (iface->get_text_at_offset)
499     return (*(iface->get_text_at_offset)) (text, offset, boundary_type, real_start_offset, real_end_offset);
500   else
501     return NULL;
502 }
503
504 /**
505  * atk_text_get_text_before_offset:
506  * @text: an #AtkText
507  * @offset: position
508  * @boundary_type: An #AtkTextBoundary
509  * @start_offset: (out): the starting character offset of the returned string
510  * @end_offset: (out): the offset of the first character after the
511  *              returned substring
512  *
513  * Gets the specified text.
514  *
515  * Deprecated: 2.9.3: Please use atk_text_get_string_at_offset() instead.
516  *
517  * Returns: a newly allocated string containing the text before @offset bounded
518  *          by the specified @boundary_type. Use g_free() to free the returned
519  *          string.
520  **/
521 gchar*
522 atk_text_get_text_before_offset (AtkText          *text,
523                                  gint             offset,
524                                  AtkTextBoundary  boundary_type,
525                                  gint             *start_offset,
526                                  gint             *end_offset)
527 {
528   AtkTextIface *iface;
529   gint local_start_offset, local_end_offset;
530   gint *real_start_offset, *real_end_offset;
531
532   g_return_val_if_fail (ATK_IS_TEXT (text), NULL);
533
534   if (start_offset)
535     real_start_offset = start_offset;
536   else
537     real_start_offset = &local_start_offset;
538   if (end_offset)
539     real_end_offset = end_offset;
540   else
541     real_end_offset = &local_end_offset;
542
543   if (offset < 0)
544     return NULL;
545
546   iface = ATK_TEXT_GET_IFACE (text);
547
548   if (iface->get_text_before_offset)
549     return (*(iface->get_text_before_offset)) (text, offset, boundary_type, real_start_offset, real_end_offset);
550   else
551     return NULL;
552 }
553
554 /**
555  * atk_text_get_string_at_offset:
556  * @text: an #AtkText
557  * @offset: position
558  * @granularity: An #AtkTextGranularity
559  * @start_offset: (out): the starting character offset of the returned string, or -1
560  *                in the case of error (e.g. invalid offset, not implemented)
561  * @end_offset: (out): the offset of the first character after the returned string,
562  *              or -1 in the case of error (e.g. invalid offset, not implemented)
563  *
564  * Gets a portion of the text exposed through an #AtkText according to a given @offset
565  * and a specific @granularity, along with the start and end offsets defining the
566  * boundaries of such a portion of text.
567  *
568  * If @granularity is ATK_TEXT_GRANULARITY_CHAR the character at the
569  * offset is returned.
570  *
571  * If @granularity is ATK_TEXT_GRANULARITY_WORD the returned string
572  * is from the word start at or before the offset to the word start after
573  * the offset.
574  *
575  * The returned string will contain the word at the offset if the offset
576  * is inside a word and will contain the word before the offset if the
577  * offset is not inside a word.
578  *
579  * If @granularity is ATK_TEXT_GRANULARITY_SENTENCE the returned string
580  * is from the sentence start at or before the offset to the sentence
581  * start after the offset.
582  *
583  * The returned string will contain the sentence at the offset if the offset
584  * is inside a sentence and will contain the sentence before the offset
585  * if the offset is not inside a sentence.
586  *
587  * If @granularity is ATK_TEXT_GRANULARITY_LINE the returned string
588  * is from the line start at or before the offset to the line
589  * start after the offset.
590  *
591  * If @granularity is ATK_TEXT_GRANULARITY_PARAGRAPH the returned string
592  * is from the start of the paragraph at or before the offset to the start
593  * of the following paragraph after the offset.
594  *
595  * Since: 2.10
596  *
597  * Returns: (nullable): a newly allocated string containing the text at
598  *          the @offset bounded by the specified @granularity. Use g_free()
599  *          to free the returned string.  Returns %NULL if the offset is invalid
600  *          or no implementation is available.
601  **/
602 gchar* atk_text_get_string_at_offset (AtkText *text,
603                                       gint offset,
604                                       AtkTextGranularity granularity,
605                                       gint *start_offset,
606                                       gint *end_offset)
607 {
608   AtkTextIface *iface;
609   gint local_start_offset, local_end_offset;
610   gint *real_start_offset, *real_end_offset;
611
612   g_return_val_if_fail (ATK_IS_TEXT (text), NULL);
613
614   if (start_offset)
615     {
616       *start_offset = -1;
617       real_start_offset = start_offset;
618     }
619   else
620     real_start_offset = &local_start_offset;
621
622   if (end_offset)
623     {
624       *end_offset = -1;
625       real_end_offset = end_offset;
626     }
627   else
628     real_end_offset = &local_end_offset;
629
630   if (offset < 0)
631     return NULL;
632
633   iface = ATK_TEXT_GET_IFACE (text);
634
635   if (iface->get_string_at_offset)
636     return (*(iface->get_string_at_offset)) (text, offset, granularity, real_start_offset, real_end_offset);
637   else
638     return NULL;
639 }
640
641 /**
642  * atk_text_get_caret_offset:
643  * @text: an #AtkText
644  *
645  * Gets the offset of the position of the caret (cursor).
646  *
647  * Returns: the character offset of the position of the caret or -1 if
648  *          the caret is not located inside the element or in the case of
649  *          any other failure.
650  **/
651 gint
652 atk_text_get_caret_offset (AtkText *text)
653 {
654   AtkTextIface *iface;
655
656   g_return_val_if_fail (ATK_IS_TEXT (text), 0);
657
658   iface = ATK_TEXT_GET_IFACE (text);
659
660   if (iface->get_caret_offset)
661     return (*(iface->get_caret_offset)) (text);
662   else
663     return -1;
664 }
665
666 /**
667  * atk_text_get_character_extents:
668  * @text: an #AtkText
669  * @offset: The offset of the text character for which bounding information is required.
670  * @x: (out) (optional): Pointer for the x coordinate of the bounding box
671  * @y: (out) (optional): Pointer for the y coordinate of the bounding box
672  * @width: (out) (optional): Pointer for the width of the bounding box
673  * @height: (out) (optional): Pointer for the height of the bounding box
674  * @coords: specify whether coordinates are relative to the screen or widget window 
675  *
676  * If the extent can not be obtained (e.g. missing support), all of x, y, width,
677  * height are set to -1.
678  *
679  * Get the bounding box containing the glyph representing the character at 
680  *     a particular text offset. 
681  **/
682 void
683 atk_text_get_character_extents (AtkText *text,
684                                 gint offset,
685                                 gint *x,
686                                 gint *y,
687                                 gint *width,
688                                 gint *height,
689                                 AtkCoordType coords)
690 {
691   AtkTextIface *iface;
692   gint local_x, local_y, local_width, local_height;
693   gint *real_x, *real_y, *real_width, *real_height;
694
695   g_return_if_fail (ATK_IS_TEXT (text));
696
697   if (x)
698     real_x = x;
699   else
700     real_x = &local_x;
701   if (y)
702     real_y = y;
703   else
704     real_y = &local_y;
705   if (width)
706     real_width = width;
707   else
708     real_width = &local_width;
709   if (height)
710     real_height = height;
711   else
712     real_height = &local_height;
713
714   *real_x = -1;
715   *real_y = -1;
716   *real_width = -1;
717   *real_height = -1;
718
719   if (offset < 0)
720     return;
721  
722   iface = ATK_TEXT_GET_IFACE (text);
723
724   if (iface->get_character_extents)
725     (*(iface->get_character_extents)) (text, offset, real_x, real_y, real_width, real_height, coords);
726
727   if (*real_width <0)
728     {
729       *real_x = *real_x + *real_width;
730       *real_width *= -1;
731     }
732 }
733
734 /**
735  * atk_text_get_run_attributes:
736  *@text: an #AtkText
737  *@offset: the character offset at which to get the attributes, -1 means the offset of
738  *the character to be inserted at the caret location.
739  *@start_offset: (out): the address to put the start offset of the range
740  *@end_offset: (out): the address to put the end offset of the range
741  *
742  *Creates an #AtkAttributeSet which consists of the attributes explicitly
743  *set at the position @offset in the text. @start_offset and @end_offset are
744  *set to the start and end of the range around @offset where the attributes are
745  *invariant. Note that @end_offset is the offset of the first character
746  *after the range.  See the enum AtkTextAttribute for types of text 
747  *attributes that can be returned. Note that other attributes may also be 
748  *returned.
749  *
750  *Returns: (transfer full): an #AtkAttributeSet which contains the attributes
751  *         explicitly set at @offset. This #AtkAttributeSet should be freed by
752  *         a call to atk_attribute_set_free().
753  **/
754 AtkAttributeSet* 
755 atk_text_get_run_attributes (AtkText          *text,
756                              gint             offset,
757                              gint             *start_offset,
758                              gint             *end_offset)
759 {
760   AtkTextIface *iface;
761   gint local_start_offset, local_end_offset;
762   gint *real_start_offset, *real_end_offset;
763
764   g_return_val_if_fail (ATK_IS_TEXT (text), NULL);
765
766   if (start_offset)
767     real_start_offset = start_offset;
768   else
769     real_start_offset = &local_start_offset;
770   if (end_offset)
771     real_end_offset = end_offset;
772   else
773     real_end_offset = &local_end_offset;
774
775   if (offset < -1)
776     return NULL;
777
778   iface = ATK_TEXT_GET_IFACE (text);
779
780   if (iface->get_run_attributes)
781     return (*(iface->get_run_attributes)) (text, offset, real_start_offset, real_end_offset);
782   else
783     return NULL;
784 }
785
786 /**
787  * atk_text_get_default_attributes:
788  *@text: an #AtkText
789  *
790  *Creates an #AtkAttributeSet which consists of the default values of
791  *attributes for the text. See the enum AtkTextAttribute for types of text 
792  *attributes that can be returned. Note that other attributes may also be 
793  *returned.
794  *
795  *Returns: (transfer full): an #AtkAttributeSet which contains the default text
796  *          attributes for this #AtkText. This #AtkAttributeSet should be freed by
797  *          a call to atk_attribute_set_free().
798  */
799 AtkAttributeSet*
800 atk_text_get_default_attributes (AtkText          *text)
801 {
802   AtkTextIface *iface;
803
804   g_return_val_if_fail (ATK_IS_TEXT (text), NULL);
805
806   iface = ATK_TEXT_GET_IFACE (text);
807
808   if (iface->get_default_attributes)
809     return (*(iface->get_default_attributes)) (text);
810   else
811     return NULL;
812 }
813
814 /**
815  * atk_text_get_character_count:
816  * @text: an #AtkText
817  *
818  * Gets the character count.
819  *
820  * Returns: the number of characters or -1 in case of failure.
821  **/
822 gint
823 atk_text_get_character_count (AtkText *text)
824 {
825   AtkTextIface *iface;
826
827   g_return_val_if_fail (ATK_IS_TEXT (text), -1);
828
829   iface = ATK_TEXT_GET_IFACE (text);
830
831   if (iface->get_character_count)
832     return (*(iface->get_character_count)) (text);
833   else
834     return -1;
835 }
836
837 /**
838  * atk_text_get_offset_at_point:
839  * @text: an #AtkText
840  * @x: screen x-position of character
841  * @y: screen y-position of character
842  * @coords: specify whether coordinates are relative to the screen or
843  * widget window 
844  *
845  * Gets the offset of the character located at coordinates @x and @y. @x and @y
846  * are interpreted as being relative to the screen or this widget's window
847  * depending on @coords.
848  *
849  * Returns: the offset to the character which is located at  the specified
850  *          @x and @y coordinates of -1 in case of failure.
851  **/
852 gint
853 atk_text_get_offset_at_point (AtkText *text,
854                               gint x,
855                               gint y,
856                               AtkCoordType coords)
857 {
858   AtkTextIface *iface;
859
860   g_return_val_if_fail (ATK_IS_TEXT (text), -1);
861
862   iface = ATK_TEXT_GET_IFACE (text);
863
864   if (iface->get_offset_at_point)
865     return (*(iface->get_offset_at_point)) (text, x, y, coords);
866   else
867     return -1;
868 }
869
870 /**
871  * atk_text_get_n_selections:
872  * @text: an #AtkText
873  *
874  * Gets the number of selected regions.
875  *
876  * Returns: The number of selected regions, or -1 in the case of failure.
877  **/
878 gint
879 atk_text_get_n_selections (AtkText *text)
880 {
881   AtkTextIface *iface;
882
883   g_return_val_if_fail (ATK_IS_TEXT (text), -1);
884
885   iface = ATK_TEXT_GET_IFACE (text);
886
887   if (iface->get_n_selections)
888     return (*(iface->get_n_selections)) (text);
889   else
890     return -1;
891 }
892
893 /**
894  * atk_text_get_selection:
895  * @text: an #AtkText
896  * @selection_num: The selection number.  The selected regions are
897  * assigned numbers that correspond to how far the region is from the
898  * start of the text.  The selected region closest to the beginning
899  * of the text region is assigned the number 0, etc.  Note that adding,
900  * moving or deleting a selected region can change the numbering.
901  * @start_offset: (out): passes back the starting character offset of the selected region
902  * @end_offset: (out): passes back the ending character offset (offset immediately past)
903  * of the selected region
904  *
905  * Gets the text from the specified selection.
906  *
907  * Returns: a newly allocated string containing the selected text. Use g_free()
908  *          to free the returned string.
909  **/
910 gchar*
911 atk_text_get_selection (AtkText *text, 
912                         gint    selection_num,
913                         gint    *start_offset,
914                         gint    *end_offset)
915 {
916   AtkTextIface *iface;
917   gint local_start_offset, local_end_offset;
918   gint *real_start_offset, *real_end_offset;
919
920   g_return_val_if_fail (ATK_IS_TEXT (text), NULL);
921
922   if (start_offset)
923     real_start_offset = start_offset;
924   else
925     real_start_offset = &local_start_offset;
926   if (end_offset)
927     real_end_offset = end_offset;
928   else
929     real_end_offset = &local_end_offset;
930
931   iface = ATK_TEXT_GET_IFACE (text);
932
933   if (iface->get_selection)
934   {
935     return (*(iface->get_selection)) (text, selection_num,
936        real_start_offset, real_end_offset);
937   }
938   else
939     return NULL;
940 }
941
942 /**
943  * atk_text_add_selection:
944  * @text: an #AtkText
945  * @start_offset: the starting character offset of the selected region
946  * @end_offset: the offset of the first character after the selected region.
947  *
948  * Adds a selection bounded by the specified offsets.
949  *
950  * Returns: %TRUE if successful, %FALSE otherwise
951  **/
952 gboolean
953 atk_text_add_selection (AtkText *text, 
954                         gint    start_offset,
955                         gint    end_offset)
956 {
957   AtkTextIface *iface;
958
959   g_return_val_if_fail (ATK_IS_TEXT (text), FALSE);
960
961   iface = ATK_TEXT_GET_IFACE (text);
962
963   if (iface->add_selection)
964     return (*(iface->add_selection)) (text, start_offset, end_offset);
965   else
966     return FALSE;
967 }
968
969 /**
970  * atk_text_remove_selection:
971  * @text: an #AtkText
972  * @selection_num: The selection number.  The selected regions are
973  * assigned numbers that correspond to how far the region is from the
974  * start of the text.  The selected region closest to the beginning
975  * of the text region is assigned the number 0, etc.  Note that adding,
976  * moving or deleting a selected region can change the numbering.
977  *
978  * Removes the specified selection.
979  *
980  * Returns: %TRUE if successful, %FALSE otherwise
981  **/
982 gboolean
983 atk_text_remove_selection (AtkText *text, 
984                            gint    selection_num)
985 {
986   AtkTextIface *iface;
987
988   g_return_val_if_fail (ATK_IS_TEXT (text), FALSE);
989
990   iface = ATK_TEXT_GET_IFACE (text);
991
992   if (iface->remove_selection)
993     return (*(iface->remove_selection)) (text, selection_num);
994   else
995     return FALSE;
996 }
997
998 /**
999  * atk_text_set_selection:
1000  * @text: an #AtkText
1001  * @selection_num: The selection number.  The selected regions are
1002  * assigned numbers that correspond to how far the region is from the
1003  * start of the text.  The selected region closest to the beginning
1004  * of the text region is assigned the number 0, etc.  Note that adding,
1005  * moving or deleting a selected region can change the numbering.
1006  * @start_offset: the new starting character offset of the selection
1007  * @end_offset: the new end position of (e.g. offset immediately past) 
1008  * the selection
1009  *
1010  * Changes the start and end offset of the specified selection.
1011  *
1012  * Returns: %TRUE if successful, %FALSE otherwise
1013  **/
1014 gboolean
1015 atk_text_set_selection (AtkText *text, 
1016                         gint    selection_num,
1017                         gint    start_offset, 
1018                         gint    end_offset)
1019 {
1020   AtkTextIface *iface;
1021
1022   g_return_val_if_fail (ATK_IS_TEXT (text), FALSE);
1023
1024   iface = ATK_TEXT_GET_IFACE (text);
1025
1026   if (iface->set_selection)
1027   {
1028     return (*(iface->set_selection)) (text, selection_num,
1029        start_offset, end_offset);
1030   }
1031   else
1032     return FALSE;
1033 }
1034
1035 /**
1036  * atk_text_set_caret_offset:
1037  * @text: an #AtkText
1038  * @offset: the character offset of the new caret position
1039  *
1040  * Sets the caret (cursor) position to the specified @offset.
1041  *
1042  * In the case of rich-text content, this method should either grab focus
1043  * or move the sequential focus navigation starting point (if the application
1044  * supports this concept) as if the user had clicked on the new caret position.
1045  * Typically, this means that the target of this operation is the node containing
1046  * the new caret position or one of its ancestors. In other words, after this
1047  * method is called, if the user advances focus, it should move to the first
1048  * focusable node following the new caret position.
1049  *
1050  * Calling this method should also scroll the application viewport in a way
1051  * that matches the behavior of the application's typical caret motion or tab
1052  * navigation as closely as possible. This also means that if the application's
1053  * caret motion or focus navigation does not trigger a scroll operation, this
1054  * method should not trigger one either. If the application does not have a caret
1055  * motion or focus navigation operation, this method should try to scroll the new
1056  * caret position into view while minimizing unnecessary scroll motion.
1057  *
1058  * Returns: %TRUE if successful, %FALSE otherwise.
1059  **/
1060 gboolean
1061 atk_text_set_caret_offset (AtkText *text,
1062                            gint    offset)
1063 {
1064   AtkTextIface *iface;
1065
1066   g_return_val_if_fail (ATK_IS_TEXT (text), FALSE);
1067
1068   iface = ATK_TEXT_GET_IFACE (text);
1069
1070   if (iface->set_caret_offset)
1071     {
1072       return (*(iface->set_caret_offset)) (text, offset);
1073     }
1074   else
1075     {
1076       return FALSE;
1077     }
1078 }
1079
1080 /**
1081  * atk_text_get_range_extents:
1082  * @text: an #AtkText
1083  * @start_offset: The offset of the first text character for which boundary 
1084  *        information is required.
1085  * @end_offset: The offset of the text character after the last character 
1086  *        for which boundary information is required.
1087  * @coord_type: Specify whether coordinates are relative to the screen or widget window.
1088  * @rect: (out): A pointer to a AtkTextRectangle which is filled in by this function.
1089  *
1090  * Get the bounding box for text within the specified range.
1091  *
1092  * If the extents can not be obtained (e.g. or missing support), the rectangle
1093  * fields are set to -1.
1094  *
1095  * Since: 1.3
1096  **/
1097 void
1098 atk_text_get_range_extents (AtkText          *text,
1099                             gint             start_offset,
1100                             gint             end_offset,
1101                             AtkCoordType     coord_type,
1102                             AtkTextRectangle *rect)
1103 {
1104   AtkTextIface *iface;
1105
1106   g_return_if_fail (ATK_IS_TEXT (text));
1107   g_return_if_fail (rect);
1108   g_return_if_fail (start_offset >= 0 && start_offset < end_offset);
1109
1110   iface = ATK_TEXT_GET_IFACE (text);
1111
1112   if (iface->get_range_extents)
1113     (*(iface->get_range_extents)) (text, start_offset, end_offset, coord_type, rect);
1114   else
1115     {
1116       rect->x = -1;
1117       rect->y = -1;
1118       rect->width = -1;
1119       rect->height = -1;
1120     }
1121 }
1122
1123 /**
1124  * atk_text_get_bounded_ranges: (virtual get_bounded_ranges)
1125  * @text: an #AtkText
1126  * @rect: An AtkTextRectangle giving the dimensions of the bounding box.
1127  * @coord_type: Specify whether coordinates are relative to the screen or widget window.
1128  * @x_clip_type: Specify the horizontal clip type.
1129  * @y_clip_type: Specify the vertical clip type.
1130  *
1131  * Get the ranges of text in the specified bounding box.
1132  *
1133  * Since: 1.3
1134  *
1135  * Returns: (array zero-terminated=1): Array of AtkTextRange. The last
1136  *          element of the array returned by this function will be NULL.
1137  **/
1138 AtkTextRange**
1139 atk_text_get_bounded_ranges (AtkText          *text,
1140                              AtkTextRectangle *rect,
1141                              AtkCoordType      coord_type,
1142                              AtkTextClipType   x_clip_type,
1143                              AtkTextClipType   y_clip_type)
1144 {
1145   AtkTextIface *iface;
1146
1147   g_return_val_if_fail (ATK_IS_TEXT (text), NULL);
1148   g_return_val_if_fail (rect, NULL);
1149
1150   iface = ATK_TEXT_GET_IFACE (text);
1151
1152   if (iface->get_bounded_ranges)
1153     return (*(iface->get_bounded_ranges)) (text, rect, coord_type, x_clip_type, y_clip_type);
1154   else
1155     return NULL;
1156 }
1157
1158 /**
1159  * atk_attribute_set_free:
1160  * @attrib_set: The #AtkAttributeSet to free
1161  *
1162  * Frees the memory used by an #AtkAttributeSet, including all its
1163  * #AtkAttributes.
1164  **/
1165 void
1166 atk_attribute_set_free (AtkAttributeSet *attrib_set)
1167 {
1168   GSList *temp;
1169
1170   temp = attrib_set;
1171
1172   while (temp != NULL)
1173     {
1174       AtkAttribute *att;
1175
1176       att = temp->data;
1177
1178       g_free (att->name);
1179       g_free (att->value);
1180       g_free (att);
1181       temp = temp->next;
1182     }
1183   g_slist_free (attrib_set);
1184 }
1185
1186 /**
1187  * atk_text_attribute_register:
1188  * @name: a name string
1189  *
1190  * Associate @name with a new #AtkTextAttribute
1191  *
1192  * Returns: an #AtkTextAttribute associated with @name
1193  **/
1194 AtkTextAttribute
1195 atk_text_attribute_register (const gchar *name)
1196 {
1197   g_return_val_if_fail (name, ATK_TEXT_ATTR_INVALID);
1198
1199   if (!extra_attributes)
1200     extra_attributes = g_ptr_array_new ();
1201
1202   g_ptr_array_add (extra_attributes, g_strdup (name));
1203   return extra_attributes->len + ATK_TEXT_ATTR_LAST_DEFINED;
1204 }
1205
1206 /**
1207  * atk_text_attribute_get_name:
1208  * @attr: The #AtkTextAttribute whose name is required
1209  *
1210  * Gets the name corresponding to the #AtkTextAttribute
1211  *
1212  * Returns: a string containing the name; this string should not be freed
1213  **/
1214 const gchar*
1215 atk_text_attribute_get_name (AtkTextAttribute attr)
1216 {
1217   GTypeClass *type_class;
1218   GEnumValue *value;
1219   const gchar *name = NULL;
1220
1221   type_class = g_type_class_ref (ATK_TYPE_TEXT_ATTRIBUTE);
1222   g_return_val_if_fail (G_IS_ENUM_CLASS (type_class), NULL);
1223
1224   value = g_enum_get_value (G_ENUM_CLASS (type_class), attr);
1225
1226   if (value)
1227     {
1228       name = value->value_nick;
1229     }
1230   else
1231     {
1232       if (extra_attributes)
1233         {
1234           gint n = attr;
1235
1236           n -= ATK_TEXT_ATTR_LAST_DEFINED + 1;
1237
1238           if (n < extra_attributes->len)
1239
1240             name = g_ptr_array_index (extra_attributes, n);
1241         }
1242     }
1243   g_type_class_unref (type_class);
1244   return name;
1245 }
1246
1247 /**
1248  * atk_text_attribute_for_name:
1249  * @name: a string which is the (non-localized) name of an ATK text attribute.
1250  *
1251  * Get the #AtkTextAttribute type corresponding to a text attribute name.
1252  *
1253  * Returns: the #AtkTextAttribute enumerated type corresponding to the specified
1254  *          name, or #ATK_TEXT_ATTRIBUTE_INVALID if no matching text attribute
1255  *          is found.
1256  **/
1257 AtkTextAttribute
1258 atk_text_attribute_for_name (const gchar *name)
1259 {
1260   GTypeClass *type_class;
1261   GEnumValue *value;
1262   AtkTextAttribute type = ATK_TEXT_ATTR_INVALID;
1263
1264   g_return_val_if_fail (name, ATK_TEXT_ATTR_INVALID);
1265
1266   type_class = g_type_class_ref (ATK_TYPE_TEXT_ATTRIBUTE);
1267   g_return_val_if_fail (G_IS_ENUM_CLASS (type_class), ATK_TEXT_ATTR_INVALID);
1268
1269   value = g_enum_get_value_by_nick (G_ENUM_CLASS (type_class), name);
1270
1271   if (value)
1272     {
1273       type = value->value;
1274     }
1275   else
1276     {
1277       gint i;
1278
1279       if (extra_attributes)
1280         {
1281           for (i = 0; i < extra_attributes->len; i++)
1282             {
1283               gchar *extra_attribute = (gchar *)g_ptr_array_index (extra_attributes, i);
1284
1285               g_return_val_if_fail (extra_attribute, ATK_TEXT_ATTR_INVALID);
1286
1287               if (strcmp (name, extra_attribute) == 0)
1288                 {
1289                   type = i + 1 + ATK_TEXT_ATTR_LAST_DEFINED;
1290                   break;
1291                 }
1292             }
1293         }
1294     }
1295   g_type_class_unref (type_class);
1296
1297   return type;
1298 }
1299
1300 /**
1301  * atk_text_attribute_get_value:
1302  * @attr: The #AtkTextAttribute for which a value is required
1303  * @index_: The index of the required value
1304  *
1305  * Gets the value for the index of the #AtkTextAttribute
1306  *
1307  * Returns: (nullable): a string containing the value; this string
1308  * should not be freed; %NULL is returned if there are no values
1309  * maintained for the attr value.
1310  **/
1311 const gchar*
1312 atk_text_attribute_get_value (AtkTextAttribute attr,
1313                               gint             index)
1314 {
1315   switch (attr)
1316     {
1317     case ATK_TEXT_ATTR_INVISIBLE:
1318     case ATK_TEXT_ATTR_EDITABLE:
1319     case ATK_TEXT_ATTR_BG_FULL_HEIGHT:
1320     case ATK_TEXT_ATTR_STRIKETHROUGH:
1321     case ATK_TEXT_ATTR_BG_STIPPLE:
1322     case ATK_TEXT_ATTR_FG_STIPPLE:
1323       g_assert (index >= 0 && index < G_N_ELEMENTS (boolean_offsets));
1324       return boolean + boolean_offsets[index];
1325     case ATK_TEXT_ATTR_UNDERLINE:
1326       g_assert (index >= 0 && index < G_N_ELEMENTS (underline_offsets));
1327       return underline + underline_offsets[index];
1328     case ATK_TEXT_ATTR_WRAP_MODE:
1329       g_assert (index >= 0 && index < G_N_ELEMENTS (wrap_mode_offsets));
1330       return wrap_mode + wrap_mode_offsets[index];
1331     case ATK_TEXT_ATTR_DIRECTION:
1332       g_assert (index >= 0 && index < G_N_ELEMENTS (direction_offsets));
1333       return direction + direction_offsets[index];
1334     case ATK_TEXT_ATTR_JUSTIFICATION:
1335       g_assert (index >= 0 && index < G_N_ELEMENTS (justification_offsets));
1336       return justification + justification_offsets[index];
1337     case ATK_TEXT_ATTR_STRETCH:
1338       g_assert (index >= 0 && index < G_N_ELEMENTS (stretch_offsets));
1339       return stretch + stretch_offsets[index];
1340     case ATK_TEXT_ATTR_VARIANT:
1341       g_assert (index >= 0 && index < G_N_ELEMENTS (variant_offsets));
1342       return variant + variant_offsets[index];
1343     case ATK_TEXT_ATTR_STYLE:
1344       g_assert (index >= 0 && index < G_N_ELEMENTS (style_offsets));
1345       return style + style_offsets[index];
1346     case ATK_TEXT_ATTR_TEXT_POSITION:
1347       g_assert (index >= 0 && index < G_N_ELEMENTS (text_position_offsets));
1348       return text_position + text_position_offsets[index];
1349     default:
1350       return NULL;
1351    }
1352 }
1353
1354 static void
1355 atk_text_rectangle_union (AtkTextRectangle *src1,
1356                           AtkTextRectangle *src2,
1357                           AtkTextRectangle *dest)
1358 {
1359   gint dest_x, dest_y;
1360
1361   /*
1362    * Some invocations of e.g. atk_text_get_character_extents
1363    * may return "-1" rectangles for character positions without support for
1364    * getting an extent. In that case we have to ignore them instead of using -1
1365    * values in computations.
1366    */
1367   if (src1->width == -1)
1368     {
1369       *dest = *src2;
1370       return;
1371     }
1372   if (src2->width == -1)
1373     {
1374       *dest = *src1;
1375       return;
1376     }
1377
1378   dest_x = MIN (src1->x, src2->x);
1379   dest_y = MIN (src1->y, src2->y);
1380   dest->width = MAX (src1->x + src1->width, src2->x + src2->width) - dest_x;
1381   dest->height = MAX (src1->y + src1->height, src2->y + src2->height) - dest_y;
1382   dest->x = dest_x;
1383   dest->y = dest_y;
1384 }
1385
1386 static gboolean
1387 atk_text_rectangle_contain (AtkTextRectangle *clip,
1388                             AtkTextRectangle *bounds,
1389                             AtkTextClipType  x_clip_type,
1390                             AtkTextClipType  y_clip_type)
1391 {
1392   gboolean x_min_ok, x_max_ok, y_min_ok, y_max_ok;
1393
1394   x_min_ok = (bounds->x >= clip->x) ||
1395              ((bounds->x + bounds->width >= clip->x) &&
1396               ((x_clip_type == ATK_TEXT_CLIP_NONE) ||
1397                (x_clip_type == ATK_TEXT_CLIP_MAX)));
1398
1399   x_max_ok = (bounds->x + bounds->width <= clip->x + clip->width) ||
1400              ((bounds->x <= clip->x + clip->width) &&
1401               ((x_clip_type == ATK_TEXT_CLIP_NONE) ||
1402                (x_clip_type == ATK_TEXT_CLIP_MIN)));
1403
1404   y_min_ok = (bounds->y >= clip->y) ||
1405              ((bounds->y + bounds->height >= clip->y) &&
1406               ((y_clip_type == ATK_TEXT_CLIP_NONE) ||
1407                (y_clip_type == ATK_TEXT_CLIP_MAX)));
1408
1409   y_max_ok = (bounds->y + bounds->height <= clip->y + clip->height) ||
1410              ((bounds->y <= clip->y + clip->height) &&
1411               ((y_clip_type == ATK_TEXT_CLIP_NONE) ||
1412                (y_clip_type == ATK_TEXT_CLIP_MIN)));
1413
1414   return (x_min_ok && x_max_ok && y_min_ok && y_max_ok);
1415   
1416 }
1417
1418 /**
1419  * atk_text_scroll_substring_to:
1420  * @text: an #AtkText
1421  * @start_offset: start offset in the @text
1422  * @end_offset: end offset in the @text, or -1 for the end of the text.
1423  * @type: specify where the object should be made visible.
1424  *
1425  * Makes a substring of @text visible on the screen by scrolling all necessary parents.
1426  *
1427  * Since: 2.32
1428  *
1429  * Returns: whether scrolling was successful.
1430  */
1431 gboolean
1432 atk_text_scroll_substring_to (AtkText       *text,
1433                               gint          start_offset,
1434                               gint          end_offset,
1435                               AtkScrollType type)
1436 {
1437   AtkTextIface *iface = NULL;
1438   g_return_val_if_fail (ATK_IS_TEXT (text), FALSE);
1439
1440   iface = ATK_TEXT_GET_IFACE (text);
1441
1442   if (iface->scroll_substring_to)
1443     return (iface->scroll_substring_to) (text, start_offset, end_offset, type);
1444   else
1445     return FALSE;
1446 }
1447
1448 /**
1449  * atk_text_scroll_substring_to_point:
1450  * @text: an #AtkText
1451  * @start_offset: start offset in the @text
1452  * @end_offset: end offset in the @text, or -1 for the end of the text.
1453  * @coords: specify whether coordinates are relative to the screen or to the
1454  * parent object.
1455  * @x: x-position where to scroll to
1456  * @y: y-position where to scroll to
1457  *
1458  * Move the top-left of a substring of @text to a given position of the screen
1459  * by scrolling all necessary parents.
1460  *
1461  * Since: 2.32
1462  *
1463  * Returns: whether scrolling was successful.
1464  */
1465 gboolean
1466 atk_text_scroll_substring_to_point (AtkText      *text,
1467                                     gint         start_offset,
1468                                     gint         end_offset,
1469                                     AtkCoordType coords,
1470                                     gint         x,
1471                                     gint         y)
1472 {
1473   AtkTextIface *iface = NULL;
1474   g_return_val_if_fail (ATK_IS_TEXT (text), FALSE);
1475
1476   iface = ATK_TEXT_GET_IFACE (text);
1477
1478   if (iface->scroll_substring_to_point)
1479     return (iface->scroll_substring_to_point) (text, start_offset, end_offset, coords, x, y);
1480   else
1481     return FALSE;
1482 }
1483
1484 static void 
1485 atk_text_real_get_range_extents (AtkText           *text,
1486                                  gint              start_offset,
1487                                  gint              end_offset,
1488                                  AtkCoordType      coord_type,
1489                                  AtkTextRectangle  *rect)
1490 {
1491   gint i;
1492   AtkTextRectangle cbounds, bounds;
1493
1494   atk_text_get_character_extents (text, start_offset,
1495                                   &bounds.x, &bounds.y,
1496                                   &bounds.width, &bounds.height,
1497                                   coord_type);
1498
1499   for (i = start_offset + 1; i < end_offset; i++)
1500     {
1501       atk_text_get_character_extents (text, i,
1502                                       &cbounds.x, &cbounds.y, 
1503                                       &cbounds.width, &cbounds.height, 
1504                                       coord_type);
1505       atk_text_rectangle_union (&bounds, &cbounds, &bounds);
1506     }
1507
1508   rect->x = bounds.x;
1509   rect->y = bounds.y;
1510   rect->width = bounds.width;
1511   rect->height = bounds.height;
1512 }
1513
1514 static AtkTextRange**
1515 atk_text_real_get_bounded_ranges (AtkText          *text,
1516                                   AtkTextRectangle *rect,
1517                                   AtkCoordType     coord_type,
1518                                   AtkTextClipType  x_clip_type,
1519                                   AtkTextClipType  y_clip_type)
1520 {
1521   gint bounds_min_offset, bounds_max_offset;
1522   gint min_line_start, min_line_end;
1523   gint max_line_start, max_line_end;
1524   gchar *line;
1525   gint curr_offset;
1526   gint offset;
1527   gint num_ranges = 0;
1528   gint range_size = 1;
1529   AtkTextRectangle cbounds;
1530   AtkTextRange **range;
1531
1532   range = NULL;
1533   bounds_min_offset = atk_text_get_offset_at_point (text, rect->x, rect->y, coord_type);
1534   bounds_max_offset = atk_text_get_offset_at_point (text, rect->x + rect->width, rect->y + rect->height, coord_type);
1535
1536   if (bounds_min_offset == 0 &&
1537       bounds_min_offset == bounds_max_offset)
1538     return NULL;
1539
1540   line = atk_text_get_text_at_offset (text, bounds_min_offset, 
1541                                       ATK_TEXT_BOUNDARY_LINE_START,
1542                                       &min_line_start, &min_line_end);
1543   g_free (line);
1544   line = atk_text_get_text_at_offset (text, bounds_max_offset, 
1545                                       ATK_TEXT_BOUNDARY_LINE_START,
1546                                       &max_line_start, &max_line_end);
1547   g_free (line);
1548   bounds_min_offset = MIN (min_line_start, max_line_start);
1549   bounds_max_offset = MAX (min_line_end, max_line_end);
1550
1551   curr_offset = bounds_min_offset;
1552   while (curr_offset < bounds_max_offset)
1553     {
1554       offset = curr_offset;
1555
1556       while (curr_offset < bounds_max_offset)
1557         {
1558           atk_text_get_character_extents (text, curr_offset,
1559                                           &cbounds.x, &cbounds.y,
1560                                           &cbounds.width, &cbounds.height,
1561                                           coord_type);
1562           if (!atk_text_rectangle_contain (rect, &cbounds, x_clip_type, y_clip_type))
1563             break;
1564           curr_offset++;
1565         }
1566       if (curr_offset > offset)
1567         {
1568           AtkTextRange *one_range = g_new (AtkTextRange, 1);
1569
1570           one_range->start_offset = offset;
1571           one_range->end_offset = curr_offset;
1572           one_range->content = atk_text_get_text (text, offset, curr_offset);
1573           atk_text_get_range_extents (text, offset, curr_offset, coord_type, &one_range->bounds);
1574
1575           if (num_ranges >= range_size - 1)
1576             {
1577               range_size *= 2;
1578               range = g_realloc (range, range_size * sizeof (gpointer));
1579             }
1580           range[num_ranges] = one_range;
1581           num_ranges++; 
1582         }   
1583       curr_offset++;
1584       if (range)
1585         range[num_ranges] = NULL;
1586     }
1587   return range;
1588 }
1589
1590 /**
1591  * atk_text_free_ranges:
1592  * @ranges: (array): A pointer to an array of #AtkTextRange which is
1593  *   to be freed.
1594  *
1595  * Frees the memory associated with an array of AtkTextRange. It is assumed
1596  * that the array was returned by the function atk_text_get_bounded_ranges
1597  * and is NULL terminated.
1598  *
1599  * Since: 1.3
1600  **/
1601 void
1602 atk_text_free_ranges (AtkTextRange **ranges)
1603 {
1604   AtkTextRange **first = ranges;
1605
1606   if (ranges)
1607     {
1608       while (*ranges)
1609         {
1610           AtkTextRange *range;
1611
1612           range = *ranges;
1613           ranges++;
1614           g_free (range->content);
1615           g_free (range);
1616         }
1617       g_free (first);
1618     }
1619 }
1620
1621 static AtkTextRange *
1622 atk_text_range_copy (AtkTextRange *src)
1623 {
1624   AtkTextRange *dst = g_new0 (AtkTextRange, 1);
1625   dst->bounds = src->bounds;
1626   dst->start_offset = src->start_offset;
1627   dst->end_offset = src->end_offset;
1628   if (src->content)
1629     dst->content = g_strdup (src->content);
1630   return dst;
1631 }
1632
1633 static void
1634 atk_text_range_free (AtkTextRange *range)
1635 {
1636   g_free (range->content);
1637   g_free (range);
1638 }
1639
1640 G_DEFINE_BOXED_TYPE (AtkTextRange, atk_text_range, atk_text_range_copy,
1641                      atk_text_range_free)