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