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