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