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