Enhancements to Accessibility::Text, based on patches from
[platform/core/uifw/at-spi2-atk.git] / libspi / text.c
1 /*
2  * AT-SPI - Assistive Technology Service Provider Interface
3  * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
4  *
5  * Copyright 2001, 2002 Sun Microsystems Inc.,
6  * Copyright 2001, 2002 Ximian, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 /* text.c : implements the Text interface */
25
26 #include <config.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <atk/atktext.h>
31 #include <libspi/text.h>
32 #include <libspi/spi-private.h>
33
34 /* Our parent Gtk object type */
35 #define PARENT_TYPE SPI_TYPE_BASE
36
37 typedef struct {
38   gint x;
39   gint y;
40   gint w;
41   gint h;
42 } SpiTextRect;
43
44 static SpiTextRect *
45 _spi_text_rect_union (SpiTextRect *aggregate, SpiTextRect *subrect)
46 {
47   if (subrect != NULL)
48     {
49       /* 'normalize' subrect */
50       if (subrect->w < 0)
51         {
52           subrect->x += subrect->w;
53           subrect->w *= -1;
54         }
55       if (subrect->h < 0)
56         {
57           subrect->y += subrect->h;
58           subrect->h *= -1;
59         }
60       if (aggregate == NULL)
61         {
62           aggregate = g_new (SpiTextRect, 1);
63           memcpy (aggregate, subrect, sizeof (SpiTextRect));
64         }
65       else
66         {
67           gint ax2 = aggregate->x + aggregate->w;
68           gint ay2 = aggregate->y + aggregate->h; 
69           gint sx2 = subrect->x + subrect->w; 
70           gint sy2 = subrect->y + subrect->h;
71           if (subrect->x < aggregate->x)
72             {
73               aggregate->w += (aggregate->x - subrect->x);
74               aggregate->x = subrect->x;
75             }
76           if (sx2 > ax2)
77             {
78               aggregate->w += (sx2 - ax2);
79             }
80           if (subrect->y < aggregate->y)
81             {
82               aggregate->h += (aggregate->y - subrect->y);
83               aggregate->y = subrect->y;
84             }
85           if (sy2 > ay2)
86             {
87               aggregate->h += (sy2 - ay2);
88             }
89         }
90     }
91   return aggregate;
92 }
93
94 static AtkText *
95 get_text_from_servant (PortableServer_Servant servant)
96 {
97   SpiBase *object = SPI_BASE (bonobo_object_from_servant (servant));
98
99   g_return_val_if_fail (object, NULL);
100   g_return_val_if_fail (ATK_IS_OBJECT(object->gobj), NULL);
101   return ATK_TEXT (object->gobj);
102 }
103
104 static CORBA_string
105 impl_getText (PortableServer_Servant servant,
106               const CORBA_long       startOffset,
107               const CORBA_long       endOffset,
108               CORBA_Environment     *ev)
109 {
110   gchar *txt;
111   CORBA_string rv;
112   AtkText *text = get_text_from_servant (servant);
113
114   g_return_val_if_fail (text != NULL, CORBA_string_dup (""));
115   
116   txt = atk_text_get_text (text, startOffset, endOffset);
117   if (txt)
118     {
119       rv = CORBA_string_dup (txt);
120       g_free (txt);
121     }
122   else
123     rv = CORBA_string_dup ("");
124
125   return rv;
126 }
127
128
129 static CORBA_string
130 impl_getTextAfterOffset (PortableServer_Servant servant,
131                          const CORBA_long offset,
132                          const
133                          Accessibility_TEXT_BOUNDARY_TYPE
134                          type, CORBA_long * startOffset,
135                          CORBA_long * endOffset,
136                          CORBA_Environment *ev)
137 {
138   gchar *txt;
139   CORBA_char *rv;
140   gint intStartOffset, intEndOffset;
141   AtkText *text = get_text_from_servant (servant);
142
143   g_return_val_if_fail (text != NULL, CORBA_string_dup (""));
144
145   txt = atk_text_get_text_after_offset (text,
146                                         offset, (AtkTextBoundary) type,
147                                         &intStartOffset, &intEndOffset);
148   *startOffset = intStartOffset;
149   *endOffset = intEndOffset;
150
151   if (txt)
152     {
153       rv = CORBA_string_dup (txt);
154       g_free (txt);
155     }
156   else
157     rv = CORBA_string_dup ("");
158
159   return rv;
160 }
161
162
163 static CORBA_string
164 impl_getTextAtOffset (PortableServer_Servant servant,
165                       const CORBA_long offset,
166                       const Accessibility_TEXT_BOUNDARY_TYPE type,
167                       CORBA_long * startOffset,
168                       CORBA_long * endOffset,
169                       CORBA_Environment *ev)
170 {
171   gchar *txt;
172   CORBA_char *rv;
173   gint intStartOffset, intEndOffset;
174   AtkText *text = get_text_from_servant (servant);
175
176   g_return_val_if_fail (text != NULL, CORBA_string_dup (""));
177
178   txt = atk_text_get_text_at_offset (
179           text,
180           offset, (AtkTextBoundary) type,
181           &intStartOffset, &intEndOffset);
182
183   *startOffset = intStartOffset;
184   *endOffset = intEndOffset;
185
186   if (txt)
187     {
188       rv = CORBA_string_dup (txt);
189       g_free (txt);
190     }
191   else
192     rv = CORBA_string_dup ("");
193
194   return rv;
195 }
196
197
198 static CORBA_unsigned_long
199 impl_getCharacterAtOffset (PortableServer_Servant servant,
200                            const CORBA_long offset,
201                            CORBA_Environment *ev)
202 {
203   AtkText *text = get_text_from_servant (servant);
204
205   g_return_val_if_fail (text != NULL, 0);
206
207   return atk_text_get_character_at_offset (text, offset);
208 }
209
210
211 static CORBA_string
212 impl_getTextBeforeOffset (PortableServer_Servant servant,
213                           const CORBA_long offset,
214                           const
215                           Accessibility_TEXT_BOUNDARY_TYPE
216                           type, CORBA_long * startOffset,
217                           CORBA_long * endOffset,
218                           CORBA_Environment *ev)
219 {
220   gchar *txt;
221   CORBA_char *rv;
222   gint intStartOffset, intEndOffset;
223   AtkText *text = get_text_from_servant (servant);
224
225   g_return_val_if_fail (text != NULL, CORBA_string_dup (""));
226
227   txt = atk_text_get_text_before_offset (text,
228                                          offset, (AtkTextBoundary) type,
229                                          &intStartOffset, &intEndOffset);
230
231   *startOffset = intStartOffset;
232   *endOffset = intEndOffset;
233
234   if (txt)
235     {
236       rv = CORBA_string_dup (txt);
237       g_free (txt);
238     }
239   else
240     rv = CORBA_string_dup ("");
241
242   return rv;
243 }
244
245
246 static CORBA_long
247 impl__get_caretOffset (PortableServer_Servant servant,
248                      CORBA_Environment *ev)
249 {
250   AtkText *text = get_text_from_servant (servant);
251
252   g_return_val_if_fail (text != NULL, -1);
253
254   return atk_text_get_caret_offset (text);
255 }
256
257
258 static CORBA_char *
259 _string_from_attribute_set (AtkAttributeSet *set)
260 {
261   gchar *attributes, *tmp, *tmp2;
262   CORBA_char *rv;
263   GSList *cur_attr;
264   AtkAttribute *at;
265   
266   attributes = g_strdup ("");
267   cur_attr = (GSList *) set;
268   while (cur_attr)
269     {
270       at = (AtkAttribute *) cur_attr->data;
271       tmp = g_strdup_printf ("%s%s:%s%s",
272                              ((GSList *)(set) == cur_attr) ? "" : " ",
273                              at->name, at->value,
274                              (cur_attr->next) ? ";" : "");
275       tmp2 = g_strconcat (attributes, tmp, NULL);
276       g_free (tmp);
277       g_free (attributes);
278       attributes = tmp2;
279       cur_attr = cur_attr->next;
280     }
281   rv = CORBA_string_dup (attributes);
282   g_free (attributes);
283   return rv;
284 }
285
286
287
288 static CORBA_string
289 impl_getAttributes (PortableServer_Servant servant,
290                     const CORBA_long offset,
291                     CORBA_long * startOffset,
292                     CORBA_long * endOffset,
293                     CORBA_Environment *ev)
294 {
295   AtkAttributeSet *set;
296   gint intstart_offset, intend_offset;
297   CORBA_char *rv;
298   AtkText *text = get_text_from_servant (servant);
299
300   g_return_val_if_fail (text != NULL, CORBA_string_dup (""));
301
302   set = atk_text_get_run_attributes (text, offset,
303                                      &intstart_offset, &intend_offset);
304   *startOffset = intstart_offset;
305   *endOffset = intend_offset;
306   rv = _string_from_attribute_set (set);
307   atk_attribute_set_free (set);
308   return rv;  
309 }
310
311 static CORBA_string
312 impl_getAttributeValue (PortableServer_Servant servant,
313                         const CORBA_long offset,
314                         const CORBA_char *attributename,
315                         CORBA_long * startOffset,
316                         CORBA_long * endOffset,
317                         CORBA_boolean * defined,
318                         CORBA_Environment *ev)
319 {
320   AtkAttributeSet *set;
321   gint intstart_offset, intend_offset;
322   GSList *cur_attr;
323   CORBA_string rv = NULL;
324   AtkText *text = get_text_from_servant (servant);
325   AtkAttribute *at;
326
327   g_return_val_if_fail (text != NULL, CORBA_string_dup (""));
328
329   set = atk_text_get_run_attributes (text, offset,
330                                      &intstart_offset, &intend_offset);
331   *startOffset = intstart_offset;
332   *endOffset = intend_offset;
333   *defined = FALSE;
334   cur_attr = (GSList *) set;
335   while (cur_attr)
336     {
337       at = (AtkAttribute *) cur_attr->data;
338       if (!strcmp (at->name, attributename))
339       {
340           rv = CORBA_string_dup (at->value);
341           *defined = TRUE;
342           break;
343       }
344       cur_attr = cur_attr->next;
345     }
346   atk_attribute_set_free (set);
347   return (rv ? rv : CORBA_string_dup (""));  
348 }
349
350 static CORBA_string
351 impl_getDefaultAttributes (PortableServer_Servant servant,
352                            CORBA_Environment *ev)
353 {
354   AtkAttributeSet *set;
355   CORBA_char *rv;
356   AtkText *text = get_text_from_servant (servant);
357
358   g_return_val_if_fail (text != NULL, CORBA_string_dup (""));
359
360   set = atk_text_get_default_attributes (text);
361
362   rv = _string_from_attribute_set (set);
363   atk_attribute_set_free (set);
364   return rv;  
365 }
366
367 static void 
368 impl_getCharacterExtents (PortableServer_Servant servant,
369                           const CORBA_long offset, CORBA_long * x,
370                           CORBA_long * y, CORBA_long * width,
371                           CORBA_long * height,
372                           const CORBA_short coordType,
373                           CORBA_Environment *ev)
374 {
375   AtkText *text = get_text_from_servant (servant);
376   gint ix, iy, iw, ih;
377
378   g_return_if_fail (text != NULL);
379
380   atk_text_get_character_extents (
381           text, offset,
382           &ix, &iy, &iw, &ih,
383           (AtkCoordType) coordType);
384   *x = ix;
385   *y = iy;
386   *width = iw;
387   *height = ih;
388 }
389
390
391 static CORBA_long
392 impl__get_characterCount (PortableServer_Servant servant,
393                           CORBA_Environment    *ev)
394 {
395   AtkText *text = get_text_from_servant (servant);
396
397   g_return_val_if_fail (text != NULL, 0);
398
399   return atk_text_get_character_count (text);
400 }
401
402
403 static CORBA_long
404 impl_getOffsetAtPoint (PortableServer_Servant servant,
405                        const CORBA_long x, const CORBA_long y,
406                        const CORBA_short coordType,
407                        CORBA_Environment *ev)
408 {
409   AtkText *text = get_text_from_servant (servant);
410
411   g_return_val_if_fail (text != NULL, -1);
412
413   return atk_text_get_offset_at_point (text,
414                                   x, y,
415                                   (AtkCoordType) coordType);
416 }
417
418
419 static CORBA_long
420 impl_getNSelections (PortableServer_Servant servant,
421                      CORBA_Environment *ev)
422 {
423   AtkText *text = get_text_from_servant (servant);
424
425   g_return_val_if_fail (text != NULL, 0);
426
427   return atk_text_get_n_selections (text);
428 }
429
430
431 static void 
432 impl_getSelection (PortableServer_Servant servant,
433                    const CORBA_long selectionNum,
434                    CORBA_long * startOffset, CORBA_long * endOffset,
435                    CORBA_Environment *ev)
436 {
437   AtkText *text = get_text_from_servant (servant);
438   gint intStartOffset, intEndOffset;
439   
440   g_return_if_fail (text != NULL);
441
442   /* atk_text_get_selection returns gchar* which we discard */
443   g_free (atk_text_get_selection (text, selectionNum,
444                                   &intStartOffset, &intEndOffset));
445   
446   *startOffset = intStartOffset;
447   *endOffset = intEndOffset;
448 }
449
450
451 static CORBA_boolean
452 impl_addSelection (PortableServer_Servant servant,
453                    const CORBA_long startOffset,
454                    const CORBA_long endOffset,
455                    CORBA_Environment *ev)
456 {
457   AtkText *text = get_text_from_servant (servant);
458
459   g_return_val_if_fail (text != NULL, FALSE);
460
461   return atk_text_add_selection (text,
462                             startOffset, endOffset);
463 }
464
465
466 static CORBA_boolean
467 impl_removeSelection (PortableServer_Servant servant,
468                       const CORBA_long selectionNum,
469                       CORBA_Environment *ev)
470 {
471   AtkText *text = get_text_from_servant (servant);
472
473   g_return_val_if_fail (text != NULL, FALSE);
474
475   return atk_text_remove_selection (text, selectionNum);
476 }
477
478
479 static CORBA_boolean
480 impl_setSelection (PortableServer_Servant servant,
481                    const CORBA_long selectionNum,
482                    const CORBA_long startOffset,
483                    const CORBA_long endOffset,
484                    CORBA_Environment *ev)
485 {
486   AtkText *text = get_text_from_servant (servant);
487
488   g_return_val_if_fail (text != NULL, FALSE);
489
490   return atk_text_set_selection (text,
491                             selectionNum, startOffset, endOffset);
492 }
493
494
495 static CORBA_boolean
496 impl_setCaretOffset (PortableServer_Servant servant,
497                      const CORBA_long value,
498                      CORBA_Environment *ev)
499 {
500   AtkText *text = get_text_from_servant (servant);
501
502   g_return_val_if_fail (text != NULL, FALSE);
503
504   return atk_text_set_caret_offset (text, value);
505 }
506
507 #define SPI_TEXT_MIN_RANGE_FOR_LINE_CHECK 6
508
509 static void
510 impl_getRangeExtents(PortableServer_Servant servant,
511                      const CORBA_long startOffset,
512                      const CORBA_long endOffset,
513                      CORBA_long * x, CORBA_long * y,
514                      CORBA_long * width,
515                      CORBA_long * height,
516                      const CORBA_short coordType,
517                      CORBA_Environment * ev)
518 {
519   AtkText *text = get_text_from_servant (servant);
520   SpiTextRect cbounds, bounds;
521   int i;
522
523   g_return_if_fail (text != NULL);
524   
525   atk_text_get_character_extents (text, startOffset,
526                                   &bounds.x, &bounds.y, &bounds.w, &bounds.h,
527                                   (AtkCoordType) coordType);
528   /* no equivalent ATK API yet, must do the hard way. :-( */
529   for (i = startOffset + 1; i < endOffset; i++) 
530     {
531       atk_text_get_character_extents (text, i,
532                                       &cbounds.x, &cbounds.y, &cbounds.w, &cbounds.h,
533                                       (AtkCoordType) coordType);
534       _spi_text_rect_union (&bounds, &cbounds);
535     }
536
537   *x = bounds.x;
538   *y = bounds.y;
539   *width = bounds.w;
540   *height = bounds.h;
541 }
542
543 static Accessibility_Text_RangeList *
544 _spi_text_range_seq_from_gslist (GSList *range_list) 
545
546   Accessibility_Text_RangeList *rangeList = 
547     Accessibility_Text_RangeList__alloc ();
548   int i, len = g_slist_length (range_list);
549   GSList *list = range_list;
550
551   rangeList->_length = len;
552   rangeList->_buffer = Accessibility_Text_RangeList_allocbuf (len);
553   for (i = 0; i < len; ++i) 
554     {
555       memcpy (&rangeList->_buffer[i], list->data, sizeof (Accessibility_Text_Range));
556       rangeList->_buffer[i].data._type = TC_null;
557       rangeList->_buffer[i].data._value = NULL;
558       rangeList->_buffer[i].data._release = TRUE;
559       g_free (list->data);
560       list = g_slist_next (range_list);
561     }
562   g_slist_free (range_list);
563
564   return rangeList;
565 }
566
567 static gboolean
568 _spi_bounds_contain (SpiTextRect *clip, SpiTextRect *cbounds, 
569                      Accessibility_TEXT_CLIP_TYPE xClipType, 
570                      Accessibility_TEXT_CLIP_TYPE yClipType)
571 {
572   gint clipx2 = clip->x + clip->w;
573   gint clipy2 = clip->y + clip->h;
574   gint charx2 = cbounds->x + cbounds->w;
575   gint chary2 = cbounds->y + cbounds->h;
576   gboolean x_min_ok, y_min_ok, x_max_ok, y_max_ok;
577
578   x_min_ok = (cbounds->x >= clip->x) || 
579     ((charx2 >= clip->x) && 
580      ((xClipType == Accessibility_TEXT_CLIP_NONE) || 
581       (xClipType == Accessibility_TEXT_CLIP_MAX)));
582   x_max_ok = (charx2 <= clipx2) || 
583     ((cbounds->x <= clipx2) && 
584      ((xClipType == Accessibility_TEXT_CLIP_NONE) || 
585       (xClipType == Accessibility_TEXT_CLIP_MIN)));
586   y_min_ok = (cbounds->y >= clip->y) || 
587     ((chary2 >= clip->y) && 
588      ((yClipType == Accessibility_TEXT_CLIP_NONE) || 
589       (yClipType == Accessibility_TEXT_CLIP_MAX)));
590   y_max_ok = (chary2 <= clipy2) || 
591     ((cbounds->y <= clipy2) && 
592      ((yClipType == Accessibility_TEXT_CLIP_NONE) || 
593       (yClipType == Accessibility_TEXT_CLIP_MIN)));
594   
595   if (x_min_ok && y_min_ok && x_max_ok && y_max_ok)
596     return TRUE;
597   else 
598     return FALSE;
599 }
600
601 static Accessibility_Text_RangeList *
602 impl_getBoundedRanges(PortableServer_Servant servant,
603                       const CORBA_long x,
604                       const CORBA_long y,
605                       const CORBA_long width,
606                       const CORBA_long height,
607                       const CORBA_short coordType,
608                       const Accessibility_TEXT_CLIP_TYPE xClipType,
609                       const Accessibility_TEXT_CLIP_TYPE yClipType, 
610                       CORBA_Environment * ev)
611 {
612   AtkText *text = get_text_from_servant (servant);
613   GSList *range_list = NULL;
614   SpiTextRect clip;
615   int startOffset = 0, endOffset = atk_text_get_character_count (text);
616   int curr_offset;
617   gint minLineStart, minLineEnd, maxLineStart, maxLineEnd;
618   long bounds_min_offset;
619   long bounds_max_offset;
620
621   clip.x = x;
622   clip.y = y;
623   clip.w = width;
624   clip.h = height;
625
626   /* for horizontal text layouts, at least, the following check helps. */
627   bounds_min_offset =  atk_text_get_offset_at_point (text, x, y, 
628                                                      (AtkCoordType) coordType);
629   bounds_max_offset =  atk_text_get_offset_at_point (text, x + width, y + height, 
630                                                      (AtkCoordType) coordType);
631   atk_text_get_text_at_offset (text, bounds_min_offset, 
632                                ATK_TEXT_BOUNDARY_LINE_START,
633                                &minLineStart, &minLineEnd);
634   atk_text_get_text_at_offset (text, bounds_max_offset, 
635                                ATK_TEXT_BOUNDARY_LINE_START,
636                                &maxLineStart, &maxLineEnd);
637   startOffset = MIN (minLineStart, maxLineStart);
638   endOffset  = MAX (minLineEnd, maxLineEnd);
639
640   curr_offset = startOffset;
641
642   while (curr_offset < endOffset) 
643     {
644       int offset = startOffset;
645       SpiTextRect cbounds;
646       while (curr_offset < endOffset) 
647         {
648           atk_text_get_character_extents (text, curr_offset, 
649                                           &cbounds.x, &cbounds.y, 
650                                           &cbounds.w, &cbounds.h, 
651                                           (AtkCoordType) coordType);
652           if (!_spi_bounds_contain (&clip, &cbounds, xClipType, yClipType))
653             break;
654           curr_offset++;
655         }
656       /* add the range to our list */
657       if (curr_offset > offset) 
658         {
659           Accessibility_Text_Range *range = g_malloc (sizeof (Accessibility_Text_Range));
660           char *s;
661           range->startOffset = offset;
662           range->endOffset = curr_offset;
663           s = atk_text_get_text (text, offset, curr_offset);
664           range->content = CORBA_string_dup (s ? s : "");
665           range_list = g_slist_append (range_list, range);
666           offset = curr_offset;
667         }
668       offset++;
669     }  
670   return _spi_text_range_seq_from_gslist (range_list); /* frees the GSList too */
671 }
672
673
674
675 static Accessibility_AttributeSet *     
676 impl_getAttributeRun (PortableServer_Servant servant,              
677                       const CORBA_long offset, 
678                       CORBA_long *startOffset, CORBA_long *endOffset, 
679                       const CORBA_boolean includeDefaults, 
680                       CORBA_Environment *ev){
681                       
682      AtkAttributeSet *attributes, *default_attributes = NULL;
683      gint intstart_offset, intend_offset;
684      Accessibility_AttributeSet *retval = NULL;
685      AtkText *text = get_text_from_servant (servant);
686      gint n_attributes = 0, total_attributes = 0, n_default_attributes = 0;
687      gint i, j;
688      
689      g_return_val_if_fail (text != NULL, NULL);
690
691      attributes = atk_text_get_run_attributes (text, offset,
692                                                &intstart_offset, &intend_offset);
693
694      if (attributes) total_attributes = n_attributes = g_slist_length (attributes);
695      if (includeDefaults)
696      {
697          default_attributes = atk_text_get_default_attributes (text);
698          if (default_attributes)
699              n_default_attributes = g_slist_length (default_attributes);
700          total_attributes += n_default_attributes;
701      }
702
703      *startOffset = intstart_offset;
704      *endOffset = intend_offset; 
705
706      if (total_attributes)
707      {
708          retval = CORBA_sequence_CORBA_string__alloc ();
709          retval->_length = retval->_maximum = total_attributes;
710          retval->_buffer = CORBA_sequence_CORBA_string_allocbuf (total_attributes);
711          CORBA_sequence_set_release (retval, CORBA_TRUE);
712          
713          for (i = 0; i < n_attributes; ++i)
714          {
715              retval->_buffer[i] = CORBA_string_dup (g_slist_nth_data (attributes, i));
716          }
717          
718          for (j = 0; j < n_default_attributes; ++i, ++j)
719          {
720              retval->_buffer[i] = CORBA_string_dup (g_slist_nth_data (default_attributes, j));
721          }
722          
723          atk_attribute_set_free (attributes);
724          if (default_attributes)
725              atk_attribute_set_free (default_attributes);
726      }
727      return retval;
728 }
729
730 static Accessibility_AttributeSet *
731 impl_getDefaultAttributeSet (PortableServer_Servant servant,
732                              CORBA_Environment *ev){
733      AtkAttributeSet *attributes;
734      Accessibility_AttributeSet *retval = NULL;
735      AtkText *text = get_text_from_servant (servant);
736      gint n_attributes = 0;
737      gint i;
738      
739      g_return_val_if_fail (text != NULL, NULL);
740      
741      attributes = atk_text_get_default_attributes (text);
742      
743      if (attributes) 
744      {
745          n_attributes = g_slist_length (attributes);
746
747          retval = CORBA_sequence_CORBA_string__alloc ();
748          retval->_length = retval->_maximum = n_attributes;
749          retval->_buffer = CORBA_sequence_CORBA_string_allocbuf (n_attributes);
750          CORBA_sequence_set_release (retval, CORBA_TRUE);
751          
752          for (i = 0; i < n_attributes; ++i)
753          {
754              retval->_buffer[i] = CORBA_string_dup (g_slist_nth_data (attributes, i));
755          }
756          atk_attribute_set_free (attributes);
757      }     
758      return retval;       
759 }
760
761
762 static void
763 spi_text_class_init (SpiTextClass *klass)
764 {
765   POA_Accessibility_Text__epv *epv = &klass->epv;
766
767   /* Initialize epv table */
768
769   epv->getText = impl_getText;
770   epv->getTextAfterOffset = impl_getTextAfterOffset;
771   epv->getCharacterAtOffset = impl_getCharacterAtOffset;
772   epv->getTextAtOffset = impl_getTextAtOffset;
773   epv->getTextBeforeOffset = impl_getTextBeforeOffset;
774   epv->_get_caretOffset = impl__get_caretOffset;
775   epv->getAttributes = impl_getAttributes;
776   epv->getDefaultAttributes = impl_getDefaultAttributes;
777   epv->getCharacterExtents = impl_getCharacterExtents;
778   epv->_get_characterCount = impl__get_characterCount;
779   epv->getOffsetAtPoint = impl_getOffsetAtPoint;
780   epv->getNSelections = impl_getNSelections;
781   epv->getSelection = impl_getSelection;
782   epv->addSelection = impl_addSelection;
783   epv->removeSelection = impl_removeSelection;
784   epv->setSelection = impl_setSelection;
785   epv->setCaretOffset = impl_setCaretOffset;
786   epv->getRangeExtents = impl_getRangeExtents;
787   epv->getBoundedRanges = impl_getBoundedRanges;
788   epv->getAttributeValue = impl_getAttributeValue;
789   epv->getAttributeRun = impl_getAttributeRun;
790   epv->getDefaultAttributeSet = impl_getDefaultAttributeSet;
791 }
792
793 static void
794 spi_text_init (SpiText *text)
795 {
796 }
797
798 BONOBO_TYPE_FUNC_FULL (SpiText,
799                        Accessibility_Text,
800                        PARENT_TYPE,
801                        spi_text)
802
803 void
804 spi_text_construct (SpiText *text, AtkObject *obj)
805 {
806   spi_base_construct (SPI_BASE (text), G_OBJECT(obj));
807 }
808
809
810 SpiText *
811 spi_text_interface_new (AtkObject *obj)
812 {
813   SpiText *retval;
814
815   g_return_val_if_fail (ATK_IS_TEXT (obj), NULL);
816
817   retval = g_object_new (SPI_TEXT_TYPE, NULL);
818
819   spi_text_construct (retval, obj);
820
821   return retval;
822 }