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