Initial commit.
[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 2008 Novell, Inc.
6  * Copyright 2001, 2002 Sun Microsystems Inc.,
7  * Copyright 2001, 2002 Ximian, Inc.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24
25 #include "accessible.h"
26 #include <string.h>
27
28 static AtkText *
29 get_text (DBusMessage * message)
30 {
31   AtkObject *obj = spi_dbus_get_object (dbus_message_get_path (message));
32   if (!obj)
33     return NULL;
34   return ATK_TEXT (obj);
35 }
36
37 static AtkText *
38 get_text_from_path (const char *path, void *user_data)
39 {
40   AtkObject *obj = spi_dbus_get_object (path);
41   if (!obj)
42     return NULL;
43   return ATK_TEXT (obj);
44 }
45
46 static dbus_bool_t
47 impl_get_characterCount (const char *path, DBusMessageIter * iter,
48                          void *user_data)
49 {
50   AtkText *text = get_text_from_path (path, user_data);
51   if (!text)
52     return FALSE;
53   return droute_return_v_int32 (iter, atk_text_get_character_count (text));
54 }
55
56 static char *
57 impl_get_characterCount_str (void *datum)
58 {
59   g_assert (ATK_IS_TEXT (datum));
60
61   return g_strdup_printf ("%d",
62                           atk_text_get_character_count ((AtkText *) datum));
63 }
64
65 static dbus_bool_t
66 impl_get_caretOffset (const char *path, DBusMessageIter * iter,
67                       void *user_data)
68 {
69   AtkText *text = get_text_from_path (path, user_data);
70   if (!text)
71     return FALSE;
72   return droute_return_v_int32 (iter, atk_text_get_caret_offset (text));
73 }
74
75 static char *
76 impl_get_caretOffset_str (void *datum)
77 {
78   g_assert (ATK_IS_TEXT (datum));
79
80   return g_strdup_printf ("%d",
81                           atk_text_get_caret_offset ((AtkText *) datum));
82 }
83
84 static DBusMessage *
85 impl_getText (DBusConnection * bus, DBusMessage * message, void *user_data)
86 {
87   AtkText *text = get_text (message);
88   dbus_int32_t startOffset, endOffset;
89   gchar *txt;
90   DBusError error;
91   DBusMessage *reply;
92
93   if (!text)
94     return spi_dbus_general_error (message);
95   dbus_error_init (&error);
96   if (!dbus_message_get_args
97       (message, &error, DBUS_TYPE_INT32, &startOffset, DBUS_TYPE_INT32,
98        &endOffset, DBUS_TYPE_INVALID))
99     {
100       return SPI_DBUS_RETURN_ERROR (message, &error);
101     }
102   txt = atk_text_get_text (text, startOffset, endOffset);
103   if (!txt)
104     txt = g_strdup ("");
105   reply = dbus_message_new_method_return (message);
106   if (reply)
107     {
108       dbus_message_append_args (reply, DBUS_TYPE_STRING, &txt,
109                                 DBUS_TYPE_INVALID);
110     }
111   g_free (txt);
112   return reply;
113 }
114
115 static DBusMessage *
116 impl_setCaretOffset (DBusConnection * bus, DBusMessage * message,
117                      void *user_data)
118 {
119   AtkText *text = get_text (message);
120   dbus_int32_t offset;
121   dbus_bool_t rv;
122   DBusError error;
123   DBusMessage *reply;
124
125   if (!text)
126     return spi_dbus_general_error (message);
127   dbus_error_init (&error);
128   if (!dbus_message_get_args
129       (message, &error, DBUS_TYPE_INT32, &offset, DBUS_TYPE_INVALID))
130     {
131       return SPI_DBUS_RETURN_ERROR (message, &error);
132     }
133   rv = atk_text_set_caret_offset (text, offset);
134   reply = dbus_message_new_method_return (message);
135   if (reply)
136     {
137       dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv,
138                                 DBUS_TYPE_INVALID);
139     }
140   return reply;
141 }
142
143 static DBusMessage *
144 impl_getTextBeforeOffset (DBusConnection * bus, DBusMessage * message,
145                           void *user_data)
146 {
147   AtkText *text = get_text (message);
148   dbus_int32_t offset;
149   dbus_uint32_t type;
150   gchar *txt;
151   dbus_int32_t startOffset, endOffset;
152   gint intstart_offset = 0, intend_offset = 0;
153   DBusError error;
154   DBusMessage *reply;
155
156   if (!text)
157     return spi_dbus_general_error (message);
158   dbus_error_init (&error);
159   if (!dbus_message_get_args
160       (message, &error, DBUS_TYPE_INT32, &offset, DBUS_TYPE_UINT32, &type,
161        DBUS_TYPE_INVALID))
162     {
163       return SPI_DBUS_RETURN_ERROR (message, &error);
164     }
165   txt =
166     atk_text_get_text_before_offset (text, offset, (AtkTextBoundary) type,
167                                      &intstart_offset, &intend_offset);
168   startOffset = intstart_offset;
169   endOffset = intend_offset;
170   if (!txt)
171     txt = g_strdup ("");
172   reply = dbus_message_new_method_return (message);
173   if (reply)
174     {
175       dbus_message_append_args (reply, DBUS_TYPE_INT32, &startOffset,
176                                 DBUS_TYPE_INT32, &endOffset, DBUS_TYPE_STRING,
177                                 &txt, DBUS_TYPE_INVALID);
178     }
179   g_free (txt);
180   return reply;
181 }
182
183 static DBusMessage *
184 impl_getTextAtOffset (DBusConnection * bus, DBusMessage * message,
185                       void *user_data)
186 {
187   AtkText *text = get_text (message);
188   dbus_int32_t offset, type;
189   gchar *txt;
190   dbus_int32_t startOffset, endOffset;
191   gint intstart_offset = 0, intend_offset = 0;
192   DBusError error;
193   DBusMessage *reply;
194
195   if (!text)
196     return spi_dbus_general_error (message);
197   dbus_error_init (&error);
198   if (!dbus_message_get_args
199       (message, &error, DBUS_TYPE_INT32, &offset, DBUS_TYPE_UINT32, &type,
200        DBUS_TYPE_INVALID))
201     {
202       return SPI_DBUS_RETURN_ERROR (message, &error);
203     }
204   txt =
205     atk_text_get_text_at_offset (text, offset, (AtkTextBoundary) type,
206                                  &intstart_offset, &intend_offset);
207   startOffset = intstart_offset;
208   endOffset = intend_offset;
209   if (!txt)
210     txt = g_strdup ("");
211   reply = dbus_message_new_method_return (message);
212   if (reply)
213     {
214       dbus_message_append_args (reply, DBUS_TYPE_INT32, &startOffset,
215                                 DBUS_TYPE_INT32, &endOffset, DBUS_TYPE_STRING,
216                                 &txt, DBUS_TYPE_INVALID);
217     }
218   g_free (txt);
219   return reply;
220 }
221
222 static DBusMessage *
223 impl_getTextAfterOffset (DBusConnection * bus, DBusMessage * message,
224                          void *user_data)
225 {
226   AtkText *text = get_text (message);
227   dbus_int32_t offset;
228   dbus_uint32_t type;
229   gchar *txt;
230   dbus_int32_t startOffset, endOffset;
231   gint intstart_offset = 0, intend_offset = 0;
232   DBusError error;
233   DBusMessage *reply;
234
235   if (!text)
236     return spi_dbus_general_error (message);
237   dbus_error_init (&error);
238   if (!dbus_message_get_args
239       (message, &error, DBUS_TYPE_INT32, &offset, DBUS_TYPE_UINT32, &type,
240        DBUS_TYPE_INVALID))
241     {
242       return SPI_DBUS_RETURN_ERROR (message, &error);
243     }
244   txt =
245     atk_text_get_text_after_offset (text, offset, (AtkTextBoundary) type,
246                                     &intstart_offset, &intend_offset);
247   startOffset = intstart_offset;
248   endOffset = intend_offset;
249   if (!txt)
250     txt = g_strdup ("");
251   reply = dbus_message_new_method_return (message);
252   if (reply)
253     {
254       dbus_message_append_args (reply, DBUS_TYPE_INT32, &startOffset,
255                                 DBUS_TYPE_INT32, &endOffset, DBUS_TYPE_STRING,
256                                 &txt, DBUS_TYPE_INVALID);
257     }
258   g_free (txt);
259   return reply;
260 }
261
262 static DBusMessage *
263 impl_getAttributeValue (DBusConnection * bus, DBusMessage * message,
264                         void *user_data)
265 {
266   AtkText *text = get_text (message);
267   dbus_int32_t offset;
268   char *attributeName;
269   dbus_int32_t startOffset, endOffset;
270   dbus_bool_t defined;
271   gint intstart_offset = 0, intend_offset = 0;
272   char *rv;
273   DBusError error;
274   DBusMessage *reply;
275   AtkAttributeSet *set;
276   GSList *cur_attr;
277   AtkAttribute *at;
278
279   if (!text)
280     return spi_dbus_general_error (message);
281   dbus_error_init (&error);
282   if (!dbus_message_get_args
283       (message, &error, DBUS_TYPE_INT32, &offset, DBUS_TYPE_STRING,
284        &attributeName, DBUS_TYPE_INVALID))
285     {
286       return SPI_DBUS_RETURN_ERROR (message, &error);
287     }
288
289   set = atk_text_get_run_attributes (text, offset,
290                                      &intstart_offset, &intend_offset);
291   startOffset = intstart_offset;
292   endOffset = intend_offset;
293   defined = FALSE;
294   cur_attr = (GSList *) set;
295   while (cur_attr)
296     {
297       at = (AtkAttribute *) cur_attr->data;
298       if (!strcmp (at->name, attributeName))
299         {
300           rv = at->value;
301           defined = TRUE;
302           break;
303         }
304       cur_attr = cur_attr->next;
305     }
306   if (!rv)
307     rv = "";
308   reply = dbus_message_new_method_return (message);
309   if (reply)
310     {
311       dbus_message_append_args (reply, DBUS_TYPE_INT32, &startOffset,
312                                 DBUS_TYPE_INT32, &endOffset,
313                                 DBUS_TYPE_BOOLEAN, &defined, DBUS_TYPE_STRING,
314                                 &rv, DBUS_TYPE_INVALID);
315     }
316   atk_attribute_set_free (set);
317   return reply;
318 }
319
320 static char *
321 _string_from_attribute_set (AtkAttributeSet * set)
322 {
323   gchar *attributes, *tmp, *tmp2;
324   GSList *cur_attr;
325   AtkAttribute *at;
326
327   attributes = g_strdup ("");
328   cur_attr = (GSList *) set;
329   while (cur_attr)
330     {
331       at = (AtkAttribute *) cur_attr->data;
332       tmp = g_strdup_printf ("%s%s:%s%s",
333                              ((GSList *) (set) == cur_attr) ? "" : " ",
334                              at->name, at->value,
335                              (cur_attr->next) ? ";" : "");
336       tmp2 = g_strconcat (attributes, tmp, NULL);
337       g_free (tmp);
338       g_free (attributes);
339       attributes = tmp2;
340       cur_attr = cur_attr->next;
341     }
342   return attributes;
343 }
344
345 static DBusMessage *
346 impl_getAttributes (DBusConnection * bus, DBusMessage * message,
347                     void *user_data)
348 {
349   AtkText *text = get_text (message);
350   dbus_int32_t offset;
351   dbus_int32_t startOffset, endOffset;
352   gint intstart_offset, intend_offset;
353   char *rv;
354   DBusError error;
355   DBusMessage *reply;
356   AtkAttributeSet *set;
357
358   if (!text)
359     return spi_dbus_general_error (message);
360   dbus_error_init (&error);
361   if (!dbus_message_get_args
362       (message, &error, DBUS_TYPE_INT32, &offset, DBUS_TYPE_INVALID))
363     {
364       return SPI_DBUS_RETURN_ERROR (message, &error);
365     }
366
367   set = atk_text_get_run_attributes (text, offset,
368                                      &intstart_offset, &intend_offset);
369   startOffset = intstart_offset;
370   endOffset = intend_offset;
371   rv = _string_from_attribute_set (set);
372   reply = dbus_message_new_method_return (message);
373   if (reply)
374     {
375       dbus_message_append_args (reply, DBUS_TYPE_INT32, &startOffset,
376                                 DBUS_TYPE_INT32, &endOffset, DBUS_TYPE_STRING,
377                                 &rv, DBUS_TYPE_INVALID);
378     }
379   g_free (rv);
380   atk_attribute_set_free (set);
381   return reply;
382 }
383
384 static DBusMessage *
385 impl_getDefaultAttributes (DBusConnection * bus, DBusMessage * message,
386                            void *user_data)
387 {
388   AtkText *text = get_text (message);
389   char *rv;
390   DBusError error;
391   DBusMessage *reply;
392   AtkAttributeSet *set;
393
394   if (!text)
395     return spi_dbus_general_error (message);
396   dbus_error_init (&error);
397
398   set = atk_text_get_default_attributes (text);
399   rv = _string_from_attribute_set (set);
400   reply = dbus_message_new_method_return (message);
401   if (reply)
402     {
403       dbus_message_append_args (reply, DBUS_TYPE_STRING, &rv,
404                                 DBUS_TYPE_INVALID);
405     }
406   g_free (rv);
407   atk_attribute_set_free (set);
408   return reply;
409 }
410
411 static DBusMessage *
412 impl_getCharacterExtents (DBusConnection * bus, DBusMessage * message,
413                           void *user_data)
414 {
415   AtkText *text = get_text (message);
416   dbus_int32_t offset;
417   dbus_uint32_t coordType;
418   dbus_int32_t x, y, width, height;
419   gint ix = 0, iy = 0, iw = 0, ih = 0;
420   DBusError error;
421   DBusMessage *reply;
422
423   if (!text)
424     return spi_dbus_general_error (message);
425   dbus_error_init (&error);
426   if (!dbus_message_get_args
427       (message, &error, DBUS_TYPE_INT32, &offset, DBUS_TYPE_INT32, &coordType,
428        DBUS_TYPE_INVALID))
429     {
430       return SPI_DBUS_RETURN_ERROR (message, &error);
431     }
432   atk_text_get_character_extents (text, offset, &ix, &iy, &iw, &ih,
433                                   (AtkCoordType) coordType);
434   x = ix;
435   y = iy;
436   width = iw;
437   height = ih;
438   reply = dbus_message_new_method_return (message);
439   if (reply)
440     {
441       dbus_message_append_args (reply, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32,
442                                 &y, DBUS_TYPE_INT32, &width, DBUS_TYPE_INT32,
443                                 &height, DBUS_TYPE_INVALID);
444     }
445   return reply;
446 }
447
448 static DBusMessage *
449 impl_getOffsetAtPoint (DBusConnection * bus, DBusMessage * message,
450                        void *user_data)
451 {
452   AtkText *text = get_text (message);
453   dbus_int32_t x, y;
454   dbus_uint32_t coordType;
455   dbus_int32_t rv;
456   DBusError error;
457   DBusMessage *reply;
458
459   if (!text)
460     return spi_dbus_general_error (message);
461   dbus_error_init (&error);
462   if (!dbus_message_get_args
463       (message, &error, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, &y,
464        DBUS_TYPE_UINT32, &coordType, DBUS_TYPE_INVALID))
465     {
466       return SPI_DBUS_RETURN_ERROR (message, &error);
467     }
468   rv = atk_text_get_offset_at_point (text, x, y, coordType);
469   reply = dbus_message_new_method_return (message);
470   if (reply)
471     {
472       dbus_message_append_args (reply, DBUS_TYPE_INT32, &rv,
473                                 DBUS_TYPE_INVALID);
474     }
475   return reply;
476 }
477
478 static DBusMessage *
479 impl_getNSelections (DBusConnection * bus, DBusMessage * message,
480                      void *user_data)
481 {
482   AtkText *text = get_text (message);
483   dbus_int32_t rv;
484   DBusMessage *reply;
485
486   if (!text)
487     return spi_dbus_general_error (message);
488   rv = atk_text_get_n_selections (text);
489   reply = dbus_message_new_method_return (message);
490   if (reply)
491     {
492       dbus_message_append_args (reply, DBUS_TYPE_INT32, &rv,
493                                 DBUS_TYPE_INVALID);
494     }
495   return reply;
496 }
497
498 static DBusMessage *
499 impl_getSelection (DBusConnection * bus, DBusMessage * message,
500                    void *user_data)
501 {
502   AtkText *text = get_text (message);
503   dbus_int32_t selectionNum;
504   dbus_int32_t startOffset, endOffset;
505   gint intstart_offset = 0, intend_offset = 0;
506   DBusError error;
507   DBusMessage *reply;
508
509   if (!text)
510     return spi_dbus_general_error (message);
511   dbus_error_init (&error);
512   if (!dbus_message_get_args
513       (message, &error, DBUS_TYPE_INT32, &selectionNum, DBUS_TYPE_INVALID))
514     {
515       return SPI_DBUS_RETURN_ERROR (message, &error);
516     }
517   /* atk_text_get_selection returns gchar * which we discard */
518   g_free (atk_text_get_selection
519           (text, selectionNum, &intstart_offset, &intend_offset));
520   startOffset = intstart_offset;
521   endOffset = intend_offset;
522   reply = dbus_message_new_method_return (message);
523   if (reply)
524     {
525       dbus_message_append_args (reply, DBUS_TYPE_INT32, &startOffset,
526                                 DBUS_TYPE_INT32, &endOffset,
527                                 DBUS_TYPE_INVALID);
528     }
529   return reply;
530 }
531
532 static DBusMessage *
533 impl_addSelection (DBusConnection * bus, DBusMessage * message,
534                    void *user_data)
535 {
536   AtkText *text = get_text (message);
537   dbus_int32_t startOffset, endOffset;
538   dbus_bool_t rv;
539   DBusError error;
540   DBusMessage *reply;
541
542   if (!text)
543     return spi_dbus_general_error (message);
544   dbus_error_init (&error);
545   if (!dbus_message_get_args
546       (message, &error, DBUS_TYPE_INT32, &startOffset, DBUS_TYPE_INT32,
547        &endOffset, DBUS_TYPE_INVALID))
548     {
549       return SPI_DBUS_RETURN_ERROR (message, &error);
550     }
551   rv = atk_text_add_selection (text, startOffset, endOffset);
552   reply = dbus_message_new_method_return (message);
553   if (reply)
554     {
555       dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv,
556                                 DBUS_TYPE_INVALID);
557     }
558   return reply;
559 }
560
561 static DBusMessage *
562 impl_removeSelection (DBusConnection * bus, DBusMessage * message,
563                       void *user_data)
564 {
565   AtkText *text = get_text (message);
566   dbus_int32_t selectionNum;
567   dbus_bool_t rv;
568   DBusError error;
569   DBusMessage *reply;
570
571   if (!text)
572     return spi_dbus_general_error (message);
573   dbus_error_init (&error);
574   if (!dbus_message_get_args
575       (message, &error, DBUS_TYPE_INT32, &selectionNum, DBUS_TYPE_INVALID))
576     {
577       return SPI_DBUS_RETURN_ERROR (message, &error);
578     }
579   rv = atk_text_remove_selection (text, selectionNum);
580   reply = dbus_message_new_method_return (message);
581   if (reply)
582     {
583       dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv,
584                                 DBUS_TYPE_INVALID);
585     }
586   return reply;
587 }
588
589 static DBusMessage *
590 impl_setSelection (DBusConnection * bus, DBusMessage * message,
591                    void *user_data)
592 {
593   AtkText *text = get_text (message);
594   dbus_int32_t selectionNum, startOffset, endOffset;
595   dbus_bool_t rv;
596   DBusError error;
597   DBusMessage *reply;
598
599   if (!text)
600     return spi_dbus_general_error (message);
601   dbus_error_init (&error);
602   if (!dbus_message_get_args
603       (message, &error, DBUS_TYPE_INT32, &selectionNum, DBUS_TYPE_INT32,
604        &startOffset, DBUS_TYPE_INT32, &endOffset, DBUS_TYPE_INVALID))
605     {
606       return SPI_DBUS_RETURN_ERROR (message, &error);
607     }
608   rv = atk_text_set_selection (text, selectionNum, startOffset, endOffset);
609   reply = dbus_message_new_method_return (message);
610   if (reply)
611     {
612       dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv,
613                                 DBUS_TYPE_INVALID);
614     }
615   return reply;
616 }
617
618 static DBusMessage *
619 impl_getRangeExtents (DBusConnection * bus, DBusMessage * message,
620                       void *user_data)
621 {
622   AtkText *text = get_text (message);
623   dbus_int32_t startOffset, endOffset;
624   dbus_uint32_t coordType;
625   AtkTextRectangle rect;
626   dbus_int32_t x, y, width, height;
627   DBusError error;
628   DBusMessage *reply;
629
630   if (!text)
631     return spi_dbus_general_error (message);
632   dbus_error_init (&error);
633   if (!dbus_message_get_args
634       (message, &error, DBUS_TYPE_INT32, &startOffset, DBUS_TYPE_INT32,
635        &endOffset, DBUS_TYPE_UINT32, &coordType, DBUS_TYPE_INVALID))
636     {
637       return SPI_DBUS_RETURN_ERROR (message, &error);
638     }
639   memset (&rect, 0, sizeof (rect));
640   atk_text_get_range_extents (text, startOffset, endOffset,
641                               (AtkCoordType) coordType, &rect);
642   x = rect.x;
643   y = rect.y;
644   width = rect.width;
645   height = rect.height;
646   reply = dbus_message_new_method_return (message);
647   if (reply)
648     {
649       dbus_message_append_args (reply, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32,
650                                 &y, DBUS_TYPE_INT32, &width, DBUS_TYPE_INT32,
651                                 &height, DBUS_TYPE_INVALID);
652     }
653   return reply;
654 }
655
656 #define MAXRANGELEN 512
657
658 static DBusMessage *
659 impl_getBoundedRanges (DBusConnection * bus, DBusMessage * message,
660                        void *user_data)
661 {
662   AtkText *text = get_text (message);
663   dbus_int32_t x, y, width, height;
664   dbus_uint32_t coordType, xClipType, yClipType;
665   DBusError error;
666   AtkTextRange **range_list = NULL;
667   AtkTextRectangle rect;
668   DBusMessage *reply;
669   DBusMessageIter iter, array, struc, variant;
670
671   if (!text)
672     return spi_dbus_general_error (message);
673   dbus_error_init (&error);
674   if (!dbus_message_get_args
675       (message, &error, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, &y,
676        DBUS_TYPE_INT32, &height, DBUS_TYPE_INT32, &width, DBUS_TYPE_INT32,
677        &coordType, DBUS_TYPE_INT32, &xClipType, DBUS_TYPE_INT32, &yClipType,
678        DBUS_TYPE_INVALID))
679     {
680       return SPI_DBUS_RETURN_ERROR (message, &error);
681     }
682   rect.x = x;
683   rect.y = y;
684   rect.width = width;
685   rect.height = height;
686
687   range_list =
688     atk_text_get_bounded_ranges (text, &rect, (AtkCoordType) coordType,
689                                  (AtkTextClipType) xClipType,
690                                  (AtkTextClipType) yClipType);
691   reply = dbus_message_new_method_return (message);
692   if (!reply)
693     return NULL;
694   /* This isn't pleasant. */
695   dbus_message_iter_init_append (reply, &iter);
696   if (dbus_message_iter_open_container
697       (&iter, DBUS_TYPE_ARRAY, "(iisv)", &array))
698     {
699       int len;
700       for (len = 0; len < MAXRANGELEN && range_list[len]; ++len)
701         {
702           if (dbus_message_iter_open_container
703               (&array, DBUS_TYPE_STRUCT, NULL, &struc))
704             {
705               dbus_int32_t val;
706               val = range_list[len]->start_offset;
707               dbus_message_iter_append_basic (&struc, DBUS_TYPE_INT32, &val);
708               val = range_list[len]->end_offset;
709               dbus_message_iter_append_basic (&struc, DBUS_TYPE_INT32, &val);
710               dbus_message_iter_append_basic (&struc, DBUS_TYPE_STRING,
711                                               &range_list[len]->content);
712               /* The variant is unimplemented in atk, but I don't want to
713                * unilaterally muck with the spec and remove it, so I'll just
714                * throw in a dummy value */
715               if (dbus_message_iter_open_container
716                   (&array, DBUS_TYPE_VARIANT, "i", &variant))
717                 {
718                   dbus_uint32_t dummy = 0;
719                   dbus_message_iter_append_basic (&variant, DBUS_TYPE_INT32,
720                                                   &dummy);
721                   dbus_message_iter_close_container (&struc, &variant);
722                 }
723               dbus_message_iter_close_container (&array, &struc);
724             }
725         }
726       dbus_message_iter_close_container (&iter, &array);
727     }
728   return reply;
729 }
730
731 static DBusMessage *
732 impl_getAttributeRun (DBusConnection * bus, DBusMessage * message,
733                       void *user_data)
734 {
735   DBusError error;
736   AtkText *text = get_text (message);
737   dbus_int32_t offset;
738   dbus_bool_t includeDefaults;
739   dbus_int32_t startOffset, endOffset;
740   gint intstart_offset = 0, intend_offset = 0;
741   DBusMessage *reply;
742   AtkAttributeSet *attributes, *default_attributes = NULL;
743   AtkAttribute *attr = NULL;
744   char **retval;
745   gint n_attributes = 0, total_attributes = 0, n_default_attributes = 0;
746   gint i, j;
747
748   if (!text)
749     return spi_dbus_general_error (message);
750   dbus_error_init (&error);
751   if (!dbus_message_get_args
752       (message, &error, DBUS_TYPE_INT32, &offset, DBUS_TYPE_BOOLEAN,
753        &includeDefaults, DBUS_TYPE_INVALID))
754     {
755       return SPI_DBUS_RETURN_ERROR (message, &error);
756     }
757
758   attributes =
759     atk_text_get_run_attributes (text, offset, &intstart_offset,
760                                  &intend_offset);
761
762   if (attributes)
763     total_attributes = n_attributes = g_slist_length (attributes);
764
765   if (includeDefaults)
766     {
767       default_attributes = atk_text_get_default_attributes (text);
768       if (default_attributes)
769         n_default_attributes = g_slist_length (default_attributes);
770       total_attributes += n_default_attributes;
771     }
772
773   startOffset = intstart_offset;
774   endOffset = intend_offset;
775
776   retval = (char **) g_malloc (total_attributes * sizeof (char *));
777
778   if (total_attributes)
779     {
780       for (i = 0; i < n_attributes; ++i)
781         {
782           attr = g_slist_nth_data (attributes, i);
783           retval[i] = g_strconcat (attr->name, ":", attr->value, NULL);
784         }
785
786       for (j = 0; j < n_default_attributes; ++i, ++j)
787         {
788           attr = g_slist_nth_data (default_attributes, j);
789           retval[i] = g_strconcat (attr->name, ":", attr->value, NULL);
790         }
791
792       atk_attribute_set_free (attributes);
793       if (default_attributes)
794         atk_attribute_set_free (default_attributes);
795     }
796   reply = dbus_message_new_method_return (message);
797   if (reply)
798     {
799       dbus_message_append_args (reply, DBUS_TYPE_INT32, &startOffset,
800                                 DBUS_TYPE_INT32, &endOffset, DBUS_TYPE_ARRAY,
801                                 DBUS_TYPE_STRING, &retval, total_attributes,
802                                 DBUS_TYPE_INVALID);
803     }
804   for (i = 0; i < total_attributes; i++)
805     g_free (retval[i]);
806   g_free (retval);
807   return reply;
808 }
809
810 static DBusMessage *
811 impl_getDefaultAttributeSet (DBusConnection * bus, DBusMessage * message,
812                              void *user_data)
813 {
814   AtkText *text = get_text (message);
815   DBusMessage *reply;
816   AtkAttributeSet *attributes;
817   AtkAttribute *attr = NULL;
818   char **retval;
819   gint n_attributes = 0;
820   gint i;
821
822   if (!text)
823     return spi_dbus_general_error (message);
824
825   attributes = atk_text_get_default_attributes (text);
826   if (attributes)
827     n_attributes = g_slist_length (attributes);
828
829   retval = (char **) malloc (n_attributes * sizeof (char *));
830
831   for (i = 0; i < n_attributes; ++i)
832     {
833       attr = g_slist_nth_data (attributes, i);
834       retval[i] = g_strconcat (attr->name, ":", attr->value, NULL);
835     }
836   if (attributes)
837     atk_attribute_set_free (attributes);
838   reply = dbus_message_new_method_return (message);
839   if (reply)
840     {
841       dbus_message_append_args (reply, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
842                                 &retval, n_attributes, DBUS_TYPE_INVALID);
843     }
844   for (i = 0; i < n_attributes; i++)
845     g_free (retval[i]);
846   g_free (retval);
847   return reply;
848 }
849
850 static DRouteMethod methods[] = {
851   {DROUTE_METHOD, impl_getText, "getText",
852    "i,startOffset,i:i,endOffset,i:s,,o"},
853   {DROUTE_METHOD, impl_setCaretOffset, "setCaretOffset", "i,offset,i:b,,o"},
854   {DROUTE_METHOD, impl_getTextBeforeOffset, "getTextBeforeOffset",
855    "i,offset,i:u,type,i:i,startOffset,o:i,endOffset,o:s,,o"},
856   {DROUTE_METHOD, impl_getTextAtOffset, "getTextAtOffset",
857    "i,offset,i:u,type,i:i,startOffset,o:i,endOffset,o:s,,o"},
858   {DROUTE_METHOD, impl_getTextAfterOffset, "getTextAfterOffset",
859    "i,offset,i:u,type,i:i,startOffset,o:i,endOffset,o:s,,o"},
860   {DROUTE_METHOD, impl_getAttributeValue, "getAttributeValue",
861    "i,offset,i:s,attributeName,i:i,startOffset,o:i,endOffset,o:b,defined,o:s,,o"},
862   {DROUTE_METHOD, impl_getAttributes, "getAttributes",
863    "i,offset,i:i,startOffset,o:i,endOffset,o:s,,o"},
864   {DROUTE_METHOD, impl_getDefaultAttributes, "getDefaultAttributes", "s,,o"},
865   {DROUTE_METHOD, impl_getCharacterExtents, "getCharacterExtents",
866    "i,offset,i:i,x,o:i,y,o:i,width,o:i,height,o:u,coordType,i"},
867   {DROUTE_METHOD, impl_getOffsetAtPoint, "getOffsetAtPoint",
868    "i,x,i:i,y,i:u,coordType,i:i,,o"},
869   {DROUTE_METHOD, impl_getNSelections, "getNSelections", "i,,o"},
870   {DROUTE_METHOD, impl_getSelection, "getSelection",
871    "i,selectionNum,i:i,startOffset,o:i,endOffset,o"},
872   {DROUTE_METHOD, impl_addSelection, "addSelection",
873    "i,startOffset,i:i,endOffset,i:b,,o"},
874   {DROUTE_METHOD, impl_removeSelection, "removeSelection",
875    "i,selectionNum,i:b,,o"},
876   {DROUTE_METHOD, impl_setSelection, "setSelection",
877    "i,selectionNum,i:i,startOffset,i:i,endOffset,i:b,,o"},
878   {DROUTE_METHOD, impl_getRangeExtents, "getRangeExtents",
879    "i,startOffset,i:i,endOffset,i:i,x,o:i,y,o:i,width,o:i,height,o:u,coordType,i"},
880   {DROUTE_METHOD, impl_getBoundedRanges, "getBoundedRanges",
881    "i,x,i:i,y,i:i,width,i:i,height,i:u,coordType,i:u,xClipType,i:u,yClipType,i:a(iisv),,o"},
882   {DROUTE_METHOD, impl_getAttributeRun, "getAttributeRun",
883    "i,offset,i:i,startOffset,o:i,endOffset,o:b,includeDefaults,i:as,,o"},
884   {DROUTE_METHOD, impl_getDefaultAttributeSet, "getDefaultAttributeSet",
885    "as,,o"},
886   {0, NULL, NULL, NULL}
887 };
888
889 static DRouteProperty properties[] = {
890   {impl_get_characterCount, impl_get_characterCount_str, NULL, NULL,
891    "characterCount", "i"},
892   {impl_get_caretOffset, impl_get_caretOffset_str, NULL, NULL, "caretOffset",
893    "i"},
894   {NULL, NULL, NULL, NULL, NULL, NULL}
895 };
896
897 void
898 spi_initialize_text (DRouteData * data)
899 {
900   droute_add_interface (data, "org.freedesktop.accessibility.Text", methods,
901                         properties,
902                         (DRouteGetDatumFunction) get_text_from_path, NULL);
903 };