Update comments in atk_text_get_run_attributes() and
[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
23 enum {
24   TEXT_CHANGED,
25   CARET_MOVED,
26   LAST_SIGNAL
27 };
28
29 static const gchar * text_attr_name[] = {
30   "left_margin",
31   "right_margin",
32   "indent",
33   "invisible",
34   "editable",
35   "pixels_above_lines",
36   "pixels_below_lines",
37   "pixels_inside_wrap",
38   "bg_full_height",
39   "rise",
40   "underline",
41   "strikethrough",
42   "size",
43   "scale",
44   "weight",
45   "language",
46   "family_name",
47   "bg_color",
48   "fg_color",
49   "bg_stipple",
50   "fg_stipple",
51   "wrap_mode",
52   "direction",
53   "justification",
54   "stretch",
55   "variant",
56   "slant_style",
57 };
58
59 static const gchar *bool[] = {"false",
60                               "true"};
61 static const gchar *style[] = {"normal",
62                                "oblique",
63                                "italic"};
64 static const gchar *variant[] = {"normal",
65                                  "small_caps"};
66 static const gchar *stretch[] = {"ultra_condensed",
67                                  "extra_condensed",
68                                  "condensed",
69                                  "semi_condensed",
70                                  "normal",
71                                  "semi_expanded",
72                                  "expanded",
73                                  "extra_expanded",
74                                  "ultra_expanded"};
75 static const gchar *justification[] = {"left",
76                                        "right",
77                                        "center",
78
79                                        "fill"};
80 static const gchar *direction[] = {"none",
81                                    "ltr",
82                                    "rtl"};
83 static const gchar *wrap_mode[] = {"none",
84                                    "char",
85                                    "word"};
86 static const gchar *underline[] = {"none",
87                                    "single",
88                                    "double",
89                                    "low"};
90
91 struct _AtkTextIfaceClass
92 {
93   GObjectClass parent;
94 };
95
96 typedef struct _AtkTextIfaceClass AtkTextIfaceClass;
97
98 static void atk_text_base_init (gpointer *g_class);
99
100 static guint atk_text_signals[LAST_SIGNAL] = { 0 };
101
102 GType
103 atk_text_get_type ()
104 {
105   static GType type = 0;
106
107   if (!type) 
108     {
109       static const GTypeInfo tinfo =
110       {
111         sizeof (AtkTextIface),
112         (GBaseInitFunc) atk_text_base_init,
113         (GBaseFinalizeFunc) NULL,
114         (GClassInitFunc) NULL /* atk_text_interface_init */ ,
115         (GClassFinalizeFunc) NULL,
116
117       };
118
119       type = g_type_register_static (G_TYPE_INTERFACE, "AtkText", &tinfo, 0);
120     }
121
122   return type;
123 }
124
125 static void
126 atk_text_base_init (gpointer *g_class)
127 {
128   static gboolean initialized = FALSE;
129   
130   if (! initialized)
131     {
132       /* 
133        * Note that text_changed signal supports details "insert", "delete", 
134        * possibly "replace". 
135        */
136       
137       atk_text_signals[TEXT_CHANGED] =
138         g_signal_new ("text_changed",
139                       ATK_TYPE_TEXT,
140                       G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
141                       G_STRUCT_OFFSET (AtkTextIface, text_changed), 
142                       (GSignalAccumulator) NULL, NULL,
143                       atk_marshal_VOID__INT_INT,
144                       G_TYPE_NONE,
145                       2, G_TYPE_INT, G_TYPE_INT);
146       
147       atk_text_signals[CARET_MOVED] =
148         g_signal_new ("text_caret_moved",
149                       ATK_TYPE_TEXT,
150                       G_SIGNAL_RUN_LAST,
151                       G_STRUCT_OFFSET (AtkTextIface, caret_changed),
152                       (GSignalAccumulator) NULL, NULL,
153                       g_cclosure_marshal_VOID__INT,
154                       G_TYPE_NONE,
155                       1, G_TYPE_INT);
156       
157       initialized = TRUE;
158     }
159 }
160
161 /**
162  * atk_text_get_text:
163  * @text: an #AtkText
164  * @start_offset: start position
165  * @end_offset: end position
166  *
167  * Gets the specified text.
168  *
169  * Returns: the text from @start_offset up to, but not including @end_offset.
170  **/
171 gchar*
172 atk_text_get_text (AtkText      *text,
173                    gint         start_offset,
174                    gint         end_offset)
175 {
176   AtkTextIface *iface;
177   
178   g_return_val_if_fail (ATK_IS_TEXT (text), NULL);
179
180   iface = ATK_TEXT_GET_IFACE (text);
181
182   if (iface->get_text)
183     return (*(iface->get_text)) (text, start_offset, end_offset);
184   else
185     return NULL;
186 }
187
188 /**
189  * atk_text_get_character_at_offset:
190  * @text: an #AtkText
191  * @offset: position
192  *
193  * Gets the specified text.
194  *
195  * Returns: the character at @offset.
196  **/
197 gunichar
198 atk_text_get_character_at_offset (AtkText      *text,
199                                   gint         offset)
200 {
201   AtkTextIface *iface;
202
203   g_return_val_if_fail (ATK_IS_TEXT (text), (gunichar) 0);
204
205   iface = ATK_TEXT_GET_IFACE (text);
206
207   if (iface->get_character_at_offset)
208     return (*(iface->get_character_at_offset)) (text, offset);
209   else
210     return (gunichar) 0;
211 }
212
213 /**
214  * atk_text_get_text_after_offset:
215  * @text: an #AtkText
216  * @offset: position
217  * @boundary_type: An #AtkTextBoundary
218  * @start_offset: the start offset of the returned string.
219  * @end_offset: the end offset of the returned string.
220  *
221  * Gets the specified text.
222  * If the boundary type is ATK_TEXT_BOUNDARY_WORD_START or
223  * ATK_TEXT_BOUNDARY_WORD_END part of a word may be returned.
224  * If the boundary type is ATK_TEXT_BOUNDARY_SENTENCE_START the start point 
225  * will be the offset and will continue to the start of the next sentence. 
226  * The first word may not be a complete word. Similarly for 
227  * ATK_TEXT_BOUNDARY_SENTENCE_END, ATK_TEXT_BOUNDARY_LINE_START and
228  * ATK_TEXT_BOUNDARY_LINE_END
229  *
230  * Returns: the text after @offset up to the specified @boundary_type.
231  **/
232 gchar*
233 atk_text_get_text_after_offset (AtkText          *text,
234                                 gint             offset,
235                                 AtkTextBoundary  boundary_type,
236                                 gint             *start_offset,
237                                 gint             *end_offset)
238 {
239   AtkTextIface *iface;
240   gint local_start_offset, local_end_offset;
241   gint *real_start_offset, *real_end_offset;
242
243   g_return_val_if_fail (ATK_IS_TEXT (text), NULL);
244
245   if (start_offset)
246     real_start_offset = start_offset;
247   else
248     real_start_offset = &local_start_offset;
249   if (end_offset)
250     real_end_offset = end_offset;
251   else
252     real_end_offset = &local_end_offset;
253
254   iface = ATK_TEXT_GET_IFACE (text);
255
256   if (iface->get_text_after_offset)
257     return (*(iface->get_text_after_offset)) (text, offset, boundary_type, real_start_offset, real_end_offset);
258   else
259     return NULL;
260 }
261
262 /**
263  * atk_text_get_text_at_offset:
264  * @text: an #AtkText
265  * @offset: position
266  * @boundary_type: An #AtkTextBoundary
267  * @start_offset: the start offset of the returned string.
268  * @end_offset: the end offset of the returned string.
269  *
270  * Gets the specified text.
271  * If the boundary_type is ATK_TEXT_BOUNDARY_WORD_START or 
272  * ATK_TEXT_BOUNDARY_WORD_END a complete word is returned; 
273  * if the boundary type is  ATK_TEXT_BOUNDARY_SENTENCE_START or 
274  * ATK_TEXT_BOUNDARY_SENTENCE_END a complete sentence
275  * is returned; if the boundary type is ATK_TEXT_BOUNDARY_LINE_START or
276  * ATK_TEXT_BOUNDARY_LINE_END a complete line is returned.
277  *
278  * Returns: the text at @offset up to the specified @boundary_type.
279  **/
280 gchar*
281 atk_text_get_text_at_offset (AtkText          *text,
282                              gint             offset,
283                              AtkTextBoundary  boundary_type,
284                              gint             *start_offset,
285                              gint             *end_offset)
286 {
287   AtkTextIface *iface;
288   gint local_start_offset, local_end_offset;
289   gint *real_start_offset, *real_end_offset;
290
291   g_return_val_if_fail (ATK_IS_TEXT (text), NULL);
292
293   if (start_offset)
294     real_start_offset = start_offset;
295   else
296     real_start_offset = &local_start_offset;
297   if (end_offset)
298     real_end_offset = end_offset;
299   else
300     real_end_offset = &local_end_offset;
301
302   iface = ATK_TEXT_GET_IFACE (text);
303
304   if (iface->get_text_at_offset)
305     return (*(iface->get_text_at_offset)) (text, offset, boundary_type, real_start_offset, real_end_offset);
306   else
307     return NULL;
308 }
309
310 /**
311  * atk_text_get_text_before_offset:
312  * @text: an #AtkText
313  * @offset: position
314  * @boundary_type: An #AtkTextBoundary
315  * @start_offset: the start offset of the returned string.
316  * @end_offset: the end offset of the returned string.
317  *
318  * Gets the specified text.
319  * If the boundary type is ATK_TEXT_BOUNDARY_WORD_START or
320  * ATK_TEXT_BOUNDARY_WORD_END part of a word may be returned.
321  * If the boundary type is ATK_TEXT_BOUNDARY_SENTENCE_START the start point 
322  * will be at the start of the sentence, and will continue to the offset. 
323  * The last word may not be a complete word. Similarly for 
324  * ATK_TEXT_BOUNDARY_SENTENCE_END, ATK_TEXT_BOUNDARY_LINE_START and
325  * ATK_TEXT_BOUNDARY_LINE_END
326  *
327  * Returns: the text before @offset starting from the specified @boundary_type.
328  **/
329 gchar*
330 atk_text_get_text_before_offset (AtkText          *text,
331                                  gint             offset,
332                                  AtkTextBoundary  boundary_type,
333                                  gint             *start_offset,
334                                  gint             *end_offset)
335 {
336   AtkTextIface *iface;
337   gint local_start_offset, local_end_offset;
338   gint *real_start_offset, *real_end_offset;
339
340   g_return_val_if_fail (ATK_IS_TEXT (text), NULL);
341
342   if (start_offset)
343     real_start_offset = start_offset;
344   else
345     real_start_offset = &local_start_offset;
346   if (end_offset)
347     real_end_offset = end_offset;
348   else
349     real_end_offset = &local_end_offset;
350
351   iface = ATK_TEXT_GET_IFACE (text);
352
353   if (iface->get_text_before_offset)
354     return (*(iface->get_text_before_offset)) (text, offset, boundary_type, real_start_offset, real_end_offset);
355   else
356     return NULL;
357 }
358
359 /**
360  * atk_text_get_caret_offset:
361  * @text: an #AtkText
362  *
363  * Gets the offset position of the caret (cursor).
364  *
365  * Returns: the offset position of the caret (cursor).
366  **/
367 gint
368 atk_text_get_caret_offset (AtkText *text)
369 {
370   AtkTextIface *iface;
371
372   g_return_val_if_fail (ATK_IS_TEXT (text), -1);
373
374   iface = ATK_TEXT_GET_IFACE (text);
375
376   if (iface->get_caret_offset)
377     return (*(iface->get_caret_offset)) (text);
378   else
379     return -1;
380 }
381
382 /**
383  * atk_text_get_character_extents:
384  * @text: an #AtkText
385  * @offset: position
386  * @x: x-position of character
387  * @y: y-position of character
388  * @width: width of character
389  * @height: height of character
390  * @coords: specify whether coordinates are relative to the screen or widget window 
391  *
392  * Given an @offset, the @x, @y, @width, and @height values are filled
393  * appropriately. 
394  **/
395 void
396 atk_text_get_character_extents (AtkText *text,
397                                 gint offset,
398                                 gint *x,
399                                 gint *y,
400                                 gint *width,
401                                 gint *height,
402                                 AtkCoordType coords)
403 {
404   AtkTextIface *iface;
405   gint local_x, local_y, local_width, local_height;
406   gint *real_x, *real_y, *real_width, *real_height;
407
408   g_return_if_fail (ATK_IS_TEXT (text));
409
410   if (x)
411     real_x = x;
412   else
413     real_x = &local_x;
414   if (y)
415     real_y = y;
416   else
417     real_y = &local_y;
418   if (width)
419     real_width = width;
420   else
421     real_width = &local_width;
422   if (height)
423     real_height = height;
424   else
425     real_height = &local_height;
426
427   iface = ATK_TEXT_GET_IFACE (text);
428
429   if (iface->get_character_extents)
430     (*(iface->get_character_extents)) (text, offset, real_x, real_y, real_width, real_height, coords);
431   else
432     {
433       *real_x = 0;
434       *real_y = 0;
435       *real_width = 0;
436       *real_height = 0;
437     }
438 }
439
440 /**
441  *atk_text_get_run_attributes:
442  *@text: an #AtkText
443  *@offset: the offset at which to get the attributes
444  *@start_offset: the address to put the start offset of the range
445  *@end_offset: the address to put the end offset of the range
446  *
447  *Creates an #AtkAttributeSet which consists of the attributes explicitly
448  *set at the position @offset in the text. @start_offset and @end_offset are
449  *set to the start and end of the range around @offset where the attributes are
450  *invariant. See the enum AtkTextAttribute for types of text attributes that 
451  *can be returned. Note that other attributes may also be returned.
452  *
453  *Returns: an #AtkAttributeSet which contains the attributes explicitly set
454  *at @offset. This #AtkAttributeSet should be freed by a call to
455  *atk_attribute_set_free().
456  **/
457 AtkAttributeSet* 
458 atk_text_get_run_attributes (AtkText          *text,
459                              gint             offset,
460                              gint             *start_offset,
461                              gint             *end_offset)
462 {
463   AtkTextIface *iface;
464   gint local_start_offset, local_end_offset;
465   gint *real_start_offset, *real_end_offset;
466
467   g_return_val_if_fail (ATK_IS_TEXT (text), NULL);
468
469   if (start_offset)
470     real_start_offset = start_offset;
471   else
472     real_start_offset = &local_start_offset;
473   if (end_offset)
474     real_end_offset = end_offset;
475   else
476     real_end_offset = &local_end_offset;
477
478   iface = ATK_TEXT_GET_IFACE (text);
479
480   if (iface->get_run_attributes)
481     return (*(iface->get_run_attributes)) (text, offset, real_start_offset, real_end_offset);
482   else
483     return NULL;
484 }
485
486 /**
487  *atk_text_get_default_attributes:
488  *@text: an #AtkText
489  *
490  *Creates an #AtkAttributeSet which consists of the default values of
491  *attributes for the text. See the enum AtkTextAttribute for types of text 
492  *attributes that can be returned. Note that other attributes may also be 
493  *returned.
494  *
495  *Returns: an #AtkAttributeSet which contains the default values of attributes.
496  *at @offset. This #AtkAttributeSet should be freed by a call to
497  *atk_attribute_set_free().
498  */
499 AtkAttributeSet* 
500 atk_text_get_default_attributes (AtkText          *text)
501 {
502   AtkTextIface *iface;
503
504   g_return_val_if_fail (ATK_IS_TEXT (text), NULL);
505
506   iface = ATK_TEXT_GET_IFACE (text);
507
508   if (iface->get_default_attributes)
509     return (*(iface->get_default_attributes)) (text);
510   else
511     return NULL;
512 }
513
514 /**
515  * atk_text_get_character_count:
516  * @text: an #AtkText
517  *
518  * Gets the character count.
519  *
520  * Returns: the number of characters.
521  **/
522 gint
523 atk_text_get_character_count (AtkText *text)
524 {
525   AtkTextIface *iface;
526
527   g_return_val_if_fail (ATK_IS_TEXT (text), -1);
528
529   iface = ATK_TEXT_GET_IFACE (text);
530
531   if (iface->get_character_count)
532     return (*(iface->get_character_count)) (text);
533   else
534     return -1;
535 }
536
537 /**
538  * atk_text_get_offset_at_point:
539  * @text: an #AtkText
540  * @x: screen x-position of character
541  * @y: screen y-position of character
542  * @coords: specify whether coordinates are relative to the screen or
543  * widget window 
544  *
545  * Gets the offset of the character located at coordinates @x and @y. @x and @y
546  * are interpreted as being relative to the screen or this widget's window
547  * depending on @coords.
548  *
549  * Returns: the offset to the character which is located at
550  * the specified @x and @y coordinates.
551  **/
552 gint
553 atk_text_get_offset_at_point (AtkText *text,
554                               gint x,
555                               gint y,
556                               AtkCoordType coords)
557 {
558   AtkTextIface *iface;
559
560   g_return_val_if_fail (ATK_IS_TEXT (text), -1);
561
562   iface = ATK_TEXT_GET_IFACE (text);
563
564   if (iface->get_offset_at_point)
565     return (*(iface->get_offset_at_point)) (text, x, y, coords);
566   else
567     return -1;
568 }
569
570 /**
571  * atk_text_get_n_selections:
572  * @text: an #AtkText
573  *
574  * Gets the number of selected regions.
575  *
576  * Returns: The number of selected regions, or -1 if a failure
577  *   occurred.
578  **/
579 gint
580 atk_text_get_n_selections (AtkText *text)
581 {
582   AtkTextIface *iface;
583
584   g_return_val_if_fail (ATK_IS_TEXT (text), -1);
585
586   iface = ATK_TEXT_GET_IFACE (text);
587
588   if (iface->get_n_selections)
589     return (*(iface->get_n_selections)) (text);
590   else
591     return -1;
592 }
593
594 /**
595  * atk_text_get_selection:
596  * @text: an #AtkText
597  * @selection_num: The selection number.  The selected regions are
598  * assigned numbers that correspond to how far the region is from the
599  * start of the text.  The selected region closest to the beginning
600  * of the text region is assigned the number 0, etc.  Note that adding,
601  * moving or deleting a selected region can change the numbering.
602  * @start_offset: passes back the start position of the selected region
603  * @end_offset: passes back the end position of the selected region
604  *
605  * Gets the text from the specified selection.
606  *
607  * Returns: the selected text.
608  **/
609 gchar*
610 atk_text_get_selection (AtkText *text, 
611                         gint    selection_num,
612                         gint    *start_offset,
613                         gint    *end_offset)
614 {
615   AtkTextIface *iface;
616   gint local_start_offset, local_end_offset;
617   gint *real_start_offset, *real_end_offset;
618
619   g_return_val_if_fail (ATK_IS_TEXT (text), NULL);
620
621   if (start_offset)
622     real_start_offset = start_offset;
623   else
624     real_start_offset = &local_start_offset;
625   if (end_offset)
626     real_end_offset = end_offset;
627   else
628     real_start_offset = &local_end_offset;
629
630   iface = ATK_TEXT_GET_IFACE (text);
631
632   if (iface->get_selection)
633   {
634     return (*(iface->get_selection)) (text, selection_num,
635        real_start_offset, real_end_offset);
636   }
637   else
638     return NULL;
639 }
640
641 /**
642  * atk_text_add_selection:
643  * @text: an #AtkText
644  * @start_offset: the start position of the selected region
645  * @end_offset: the end position of the selected region
646  *
647  * Adds a selection bounded by the specified offsets.
648  *
649  * Returns: %TRUE if success, %FALSE otherwise
650  **/
651 gboolean
652 atk_text_add_selection (AtkText *text, 
653                         gint    start_offset,
654                         gint    end_offset)
655 {
656   AtkTextIface *iface;
657
658   g_return_val_if_fail (ATK_IS_TEXT (text), FALSE);
659
660   iface = ATK_TEXT_GET_IFACE (text);
661
662   if (iface->add_selection)
663     return (*(iface->add_selection)) (text, start_offset, end_offset);
664   else
665     return FALSE;
666 }
667
668 /**
669  * atk_text_remove_selection:
670  * @text: an #AtkText
671  * @selection_num: The selection number.  The selected regions are
672  * assigned numbers that correspond to how far the region is from the
673  * start of the text.  The selected region closest to the beginning
674  * of the text region is assigned the number 0, etc.  Note that adding,
675  * moving or deleting a selected region can change the numbering.
676  *
677  * Removes the specified selection.
678  *
679  * Returns: %TRUE if success, %FALSE otherwise
680  **/
681 gboolean
682 atk_text_remove_selection (AtkText *text, 
683                            gint    selection_num)
684 {
685   AtkTextIface *iface;
686
687   g_return_val_if_fail (ATK_IS_TEXT (text), FALSE);
688
689   iface = ATK_TEXT_GET_IFACE (text);
690
691   if (iface->remove_selection)
692     return (*(iface->remove_selection)) (text, selection_num);
693   else
694     return FALSE;
695 }
696
697 /**
698  * atk_text_set_selection:
699  * @text: an #AtkText
700  * @selection_num: The selection number.  The selected regions are
701  * assigned numbers that correspond to how far the region is from the
702  * start of the text.  The selected region closest to the beginning
703  * of the text region is assigned the number 0, etc.  Note that adding,
704  * moving or deleting a selected region can change the numbering.
705  * @start_offset: the new start position of the selection
706  * @end_offset: the new end position of the selection
707  *
708  * Changes the start and end offset of the specified selection.
709  *
710  * Returns: %TRUE if success, %FALSE otherwise
711  **/
712 gboolean
713 atk_text_set_selection (AtkText *text, 
714                         gint    selection_num,
715                         gint    start_offset, 
716                         gint    end_offset)
717 {
718   AtkTextIface *iface;
719
720   g_return_val_if_fail (ATK_IS_TEXT (text), FALSE);
721
722   iface = ATK_TEXT_GET_IFACE (text);
723
724   if (iface->set_selection)
725   {
726     return (*(iface->set_selection)) (text, selection_num,
727        start_offset, end_offset);
728   }
729   else
730     return FALSE;
731 }
732
733 /**
734  * atk_text_set_caret_offset:
735  * @text: an #AtkText
736  * @offset: position
737  *
738  * Sets the caret (cursor) position to the specified @offset.
739  *
740  * Returns: %TRUE if success, %FALSE otherwise.
741  **/
742 gboolean
743 atk_text_set_caret_offset (AtkText *text,
744                            gint    offset)
745 {
746   AtkTextIface *iface;
747
748   g_return_val_if_fail (ATK_IS_TEXT (text), FALSE);
749
750   iface = ATK_TEXT_GET_IFACE (text);
751
752   if (iface->set_caret_offset)
753     {
754       return (*(iface->set_caret_offset)) (text, offset);
755     }
756   else
757     {
758       return FALSE;
759     }
760 }
761
762 /**
763  * atk_attribute_set_free:
764  * @attrib_set: The #AtkAttributeSet to free
765  *
766  * Frees the memory used by an #AtkAttributeSet, including all its
767  * #AtkAttributes.
768  **/
769 void
770 atk_attribute_set_free(AtkAttributeSet *attrib_set)
771 {
772   GSList *temp;
773
774   temp = attrib_set;
775
776   while (temp != NULL)
777     {
778       AtkAttribute *att;
779
780       att = temp->data;
781
782       g_free (att->name);
783       g_free (att->value);
784       g_free (att);
785       temp = temp->next;
786     }
787   g_slist_free (attrib_set);
788 }
789
790 /**
791  * atk_attribute_get_name:
792  * @attr: The #AtkTextAttribute whose name is required
793  *
794  * Returns the name corresponding to the attr value.
795  **/
796 G_CONST_RETURN gchar*
797 atk_attribute_get_name (AtkTextAttribute attr)
798 {
799   g_assert (attr >= 0 && attr <= ATK_TEXT_ATTR_STYLE);
800   return text_attr_name[attr];
801 }
802
803 /**
804  * atk_attribute_get_value:
805  * @attr: The #AtkTextAttribute for which a value is required
806  * @index: The index of the required value
807  *
808  * Returns the value corresponding to the attr value and index.
809  * NULL is returned if there are no values maintained for the attr value. 
810  **/
811 G_CONST_RETURN gchar*
812 atk_attribute_get_value (AtkTextAttribute attr,
813                          gint             index)
814 {
815   switch (attr)
816     {
817     case ATK_TEXT_ATTR_INVISIBLE:
818     case ATK_TEXT_ATTR_EDITABLE:
819     case ATK_TEXT_ATTR_BG_FULL_HEIGHT:
820     case ATK_TEXT_ATTR_STRIKETHROUGH:
821     case ATK_TEXT_ATTR_BG_STIPPLE:
822     case ATK_TEXT_ATTR_FG_STIPPLE:
823       g_assert (index >= 0 && index < 2);
824       return bool[index];
825     case ATK_TEXT_ATTR_UNDERLINE:
826       g_assert (index >= 0 && index < 4);
827       return underline[index];
828     case ATK_TEXT_ATTR_WRAP_MODE:
829       g_assert (index >= 0 && index < 3);
830       return wrap_mode[index];
831     case ATK_TEXT_ATTR_DIRECTION:
832       g_assert (index >= 0 && index < 3);
833       return direction[index];
834     case ATK_TEXT_ATTR_JUSTIFICATION:
835       g_assert (index >= 0 && index < 3);
836       return justification[index];
837     case ATK_TEXT_ATTR_STRETCH:
838       g_assert (index >= 0 && index < 9);
839       return stretch[index];
840     case ATK_TEXT_ATTR_VARIANT:
841       g_assert (index >= 0 && index < 2);
842       return variant[index];
843     case ATK_TEXT_ATTR_STYLE:
844       g_assert (index >= 0 && index < 3);
845       return style[index];
846     default:
847       return NULL;
848    }
849 }