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