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