Ensure that DBus errors are freed
[platform/core/uifw/at-spi2-atk.git] / atk-adaptor / adaptors / text-adaptor.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 <string.h>
26
27 #include <atk/atk.h>
28 #include <droute/droute.h>
29
30 #include "spi-dbus.h"
31 #include "object.h"
32 #include "introspection.h"
33
34 static dbus_bool_t
35 impl_get_CharacterCount (DBusMessageIter * iter, void *user_data)
36 {
37   AtkText *text = (AtkText *) user_data;
38   g_return_val_if_fail (ATK_IS_TEXT (user_data), FALSE);
39   return droute_return_v_int32 (iter, atk_text_get_character_count (text));
40 }
41
42 static dbus_bool_t
43 impl_get_CaretOffset (DBusMessageIter * iter, void *user_data)
44 {
45   AtkText *text = (AtkText *) user_data;
46   g_return_val_if_fail (ATK_IS_TEXT (user_data), FALSE);
47   return droute_return_v_int32 (iter, atk_text_get_caret_offset (text));
48 }
49
50 static gchar *
51 validate_allocated_string (gchar *str)
52 {
53   if (!str)
54     return g_strdup ("");
55   if (!g_utf8_validate (str, -1, NULL))
56     {
57       g_warning ("atk-bridge: received bad UTF-8 string from a get_text function");
58       g_free (str);
59       return g_strdup ("");
60     }
61   return str;
62 }
63
64 static DBusMessage *
65 impl_GetText (DBusConnection * bus, DBusMessage * message, void *user_data)
66 {
67   AtkText *text = (AtkText *) user_data;
68   dbus_int32_t startOffset, endOffset;
69   gchar *txt;
70   DBusMessage *reply;
71
72   g_return_val_if_fail (ATK_IS_TEXT (user_data),
73                         droute_not_yet_handled_error (message));
74   if (!dbus_message_get_args
75       (message, NULL, DBUS_TYPE_INT32, &startOffset, DBUS_TYPE_INT32,
76        &endOffset, DBUS_TYPE_INVALID))
77     {
78       return droute_invalid_arguments_error (message);
79     }
80   txt = atk_text_get_text (text, startOffset, endOffset);
81   txt = validate_allocated_string (txt);
82   reply = dbus_message_new_method_return (message);
83   if (reply)
84     {
85       dbus_message_append_args (reply, DBUS_TYPE_STRING, &txt,
86                                 DBUS_TYPE_INVALID);
87     }
88   g_free (txt);
89   return reply;
90 }
91
92 static DBusMessage *
93 impl_SetCaretOffset (DBusConnection * bus, DBusMessage * message,
94                      void *user_data)
95 {
96   AtkText *text = (AtkText *) user_data;
97   dbus_int32_t offset;
98   dbus_bool_t rv;
99   DBusMessage *reply;
100
101   g_return_val_if_fail (ATK_IS_TEXT (user_data),
102                         droute_not_yet_handled_error (message));
103   if (!dbus_message_get_args
104       (message, NULL, DBUS_TYPE_INT32, &offset, DBUS_TYPE_INVALID))
105     {
106       return droute_invalid_arguments_error (message);
107     }
108   rv = atk_text_set_caret_offset (text, offset);
109   reply = dbus_message_new_method_return (message);
110   if (reply)
111     {
112       dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv,
113                                 DBUS_TYPE_INVALID);
114     }
115   return reply;
116 }
117
118 static DBusMessage *
119 impl_GetTextBeforeOffset (DBusConnection * bus, DBusMessage * message,
120                           void *user_data)
121 {
122   AtkText *text = (AtkText *) user_data;
123   dbus_int32_t offset;
124   dbus_uint32_t type;
125   gchar *txt;
126   dbus_int32_t startOffset, endOffset;
127   gint intstart_offset = 0, intend_offset = 0;
128   DBusMessage *reply;
129
130   g_return_val_if_fail (ATK_IS_TEXT (user_data),
131                         droute_not_yet_handled_error (message));
132   if (!dbus_message_get_args
133       (message, NULL, DBUS_TYPE_INT32, &offset, DBUS_TYPE_UINT32, &type,
134        DBUS_TYPE_INVALID))
135     {
136       return droute_invalid_arguments_error (message);
137     }
138   txt =
139     atk_text_get_text_before_offset (text, offset, (AtkTextBoundary) type,
140                                      &intstart_offset, &intend_offset);
141   startOffset = intstart_offset;
142   endOffset = intend_offset;
143   txt = validate_allocated_string (txt);
144   reply = dbus_message_new_method_return (message);
145   if (reply)
146     {
147       dbus_message_append_args (reply, DBUS_TYPE_STRING, &txt,
148                                 DBUS_TYPE_INT32, &startOffset,
149                                 DBUS_TYPE_INT32, &endOffset,
150                                 DBUS_TYPE_INVALID);
151     }
152   g_free (txt);
153   return reply;
154 }
155
156 static DBusMessage *
157 impl_GetTextAtOffset (DBusConnection * bus, DBusMessage * message,
158                       void *user_data)
159 {
160   AtkText *text = (AtkText *) user_data;
161   dbus_int32_t offset, type;
162   gchar *txt;
163   dbus_int32_t startOffset, endOffset;
164   gint intstart_offset = 0, intend_offset = 0;
165   DBusMessage *reply;
166
167   g_return_val_if_fail (ATK_IS_TEXT (user_data),
168                         droute_not_yet_handled_error (message));
169   if (!dbus_message_get_args
170       (message, NULL, DBUS_TYPE_INT32, &offset, DBUS_TYPE_UINT32, &type,
171        DBUS_TYPE_INVALID))
172     {
173       return droute_invalid_arguments_error (message);
174     }
175   txt =
176     atk_text_get_text_at_offset (text, offset, (AtkTextBoundary) type,
177                                  &intstart_offset, &intend_offset);
178   startOffset = intstart_offset;
179   endOffset = intend_offset;
180   txt = validate_allocated_string (txt);
181   reply = dbus_message_new_method_return (message);
182   if (reply)
183     {
184       dbus_message_append_args (reply, DBUS_TYPE_STRING, &txt,
185                                 DBUS_TYPE_INT32, &startOffset,
186                                 DBUS_TYPE_INT32, &endOffset,
187                                 DBUS_TYPE_INVALID);
188     }
189   g_free (txt);
190   return reply;
191 }
192
193 static DBusMessage *
194 impl_GetTextAfterOffset (DBusConnection * bus, DBusMessage * message,
195                          void *user_data)
196 {
197   AtkText *text = (AtkText *) user_data;
198   dbus_int32_t offset;
199   dbus_uint32_t type;
200   gchar *txt;
201   dbus_int32_t startOffset, endOffset;
202   gint intstart_offset = 0, intend_offset = 0;
203   DBusMessage *reply;
204
205   g_return_val_if_fail (ATK_IS_TEXT (user_data),
206                         droute_not_yet_handled_error (message));
207   if (!dbus_message_get_args
208       (message, NULL, DBUS_TYPE_INT32, &offset, DBUS_TYPE_UINT32, &type,
209        DBUS_TYPE_INVALID))
210     {
211       return droute_invalid_arguments_error (message);
212     }
213   txt =
214     atk_text_get_text_after_offset (text, offset, (AtkTextBoundary) type,
215                                     &intstart_offset, &intend_offset);
216   startOffset = intstart_offset;
217   endOffset = intend_offset;
218   txt = validate_allocated_string (txt);
219   reply = dbus_message_new_method_return (message);
220   if (reply)
221     {
222       dbus_message_append_args (reply, DBUS_TYPE_STRING, &txt,
223                                 DBUS_TYPE_INT32, &startOffset,
224                                 DBUS_TYPE_INT32, &endOffset,
225                                 DBUS_TYPE_INVALID);
226     }
227   g_free (txt);
228   return reply;
229 }
230
231 static DBusMessage *
232 impl_GetCharacterAtOffset (DBusConnection * bus, DBusMessage * message,
233                            void *user_data)
234 {
235   AtkText *text = (AtkText *) user_data;
236   dbus_int32_t offset;
237   dbus_int32_t ch;
238   DBusMessage *reply;
239
240   g_return_val_if_fail (ATK_IS_TEXT (user_data),
241                         droute_not_yet_handled_error (message));
242   if (!dbus_message_get_args
243       (message, NULL, DBUS_TYPE_INT32, &offset, DBUS_TYPE_INVALID))
244     {
245       return droute_invalid_arguments_error (message);
246     }
247   ch = atk_text_get_character_at_offset (text, offset);
248   reply = dbus_message_new_method_return (message);
249   if (reply)
250     {
251       dbus_message_append_args (reply, DBUS_TYPE_INT32, &ch,
252                                 DBUS_TYPE_INVALID);
253     }
254   return reply;
255 }
256
257 static DBusMessage *
258 impl_GetAttributeValue (DBusConnection * bus, DBusMessage * message,
259                         void *user_data)
260 {
261   AtkText *text = (AtkText *) user_data;
262   dbus_int32_t offset;
263   char *attributeName;
264   dbus_int32_t startOffset, endOffset;
265   dbus_bool_t defined;
266   gint intstart_offset = 0, intend_offset = 0;
267   char *rv = NULL;
268   DBusMessage *reply;
269   AtkAttributeSet *set;
270   GSList *cur_attr;
271   AtkAttribute *at;
272
273   g_return_val_if_fail (ATK_IS_TEXT (user_data),
274                         droute_not_yet_handled_error (message));
275   if (!dbus_message_get_args
276       (message, NULL, DBUS_TYPE_INT32, &offset, DBUS_TYPE_STRING,
277        &attributeName, DBUS_TYPE_INVALID))
278     {
279       return droute_invalid_arguments_error (message);
280     }
281
282   set = atk_text_get_run_attributes (text, offset,
283                                      &intstart_offset, &intend_offset);
284   startOffset = intstart_offset;
285   endOffset = intend_offset;
286   defined = FALSE;
287   cur_attr = (GSList *) set;
288   while (cur_attr)
289     {
290       at = (AtkAttribute *) cur_attr->data;
291       if (!strcmp (at->name, attributeName))
292         {
293           rv = at->value;
294           defined = TRUE;
295           break;
296         }
297       cur_attr = cur_attr->next;
298     }
299   if (!rv)
300     rv = "";
301   reply = dbus_message_new_method_return (message);
302   if (reply)
303     {
304       dbus_message_append_args (reply, DBUS_TYPE_STRING, &rv, DBUS_TYPE_INT32,
305                                 &startOffset, DBUS_TYPE_INT32, &endOffset,
306                                 DBUS_TYPE_BOOLEAN, &defined,
307                                 DBUS_TYPE_INVALID);
308     }
309   atk_attribute_set_free (set);
310   return reply;
311 }
312
313 static DBusMessage *
314 impl_GetAttributes (DBusConnection * bus, DBusMessage * message,
315                     void *user_data)
316 {
317   AtkText *text = (AtkText *) user_data;
318   dbus_int32_t offset;
319   dbus_int32_t startOffset, endOffset;
320   gint intstart_offset, intend_offset;
321   DBusMessage *reply;
322   AtkAttributeSet *set;
323   DBusMessageIter iter;
324
325   g_return_val_if_fail (ATK_IS_TEXT (user_data),
326                         droute_not_yet_handled_error (message));
327   if (!dbus_message_get_args
328       (message, NULL, DBUS_TYPE_INT32, &offset, DBUS_TYPE_INVALID))
329     {
330       return droute_invalid_arguments_error (message);
331     }
332
333   set = atk_text_get_run_attributes (text, offset,
334                                      &intstart_offset, &intend_offset);
335
336   startOffset = intstart_offset;
337   endOffset = intend_offset;
338   reply = dbus_message_new_method_return (message);
339   if (reply)
340     {
341       dbus_message_iter_init_append (reply, &iter);
342       spi_object_append_attribute_set (&iter, set);
343       dbus_message_append_args (reply, DBUS_TYPE_INT32, &startOffset,
344                                 DBUS_TYPE_INT32, &endOffset,
345                                 DBUS_TYPE_INVALID);
346     }
347   atk_attribute_set_free (set);
348   return reply;
349 }
350
351 static DBusMessage *
352 impl_GetDefaultAttributes (DBusConnection * bus, DBusMessage * message,
353                            void *user_data)
354 {
355   AtkText *text = (AtkText *) user_data;
356   DBusMessage *reply;
357   AtkAttributeSet *set;
358   DBusMessageIter iter;
359
360   g_return_val_if_fail (ATK_IS_TEXT (user_data),
361                         droute_not_yet_handled_error (message));
362
363   set = atk_text_get_default_attributes (text);
364   reply = dbus_message_new_method_return (message);
365   if (reply)
366     {
367       dbus_message_iter_init_append (reply, &iter);
368       spi_object_append_attribute_set (&iter, set);
369     }
370   atk_attribute_set_free (set);
371   return reply;
372 }
373
374 static DBusMessage *
375 impl_GetCharacterExtents (DBusConnection * bus, DBusMessage * message,
376                           void *user_data)
377 {
378   AtkText *text = (AtkText *) user_data;
379   dbus_int32_t offset;
380   dbus_uint32_t coordType;
381   dbus_int32_t x, y, width, height;
382   gint ix = 0, iy = 0, iw = 0, ih = 0;
383   DBusMessage *reply;
384
385   g_return_val_if_fail (ATK_IS_TEXT (user_data),
386                         droute_not_yet_handled_error (message));
387   if (!dbus_message_get_args
388       (message, NULL, DBUS_TYPE_INT32, &offset, DBUS_TYPE_UINT32,
389        &coordType, DBUS_TYPE_INVALID))
390     {
391       return droute_invalid_arguments_error (message);
392     }
393   atk_text_get_character_extents (text, offset, &ix, &iy, &iw, &ih,
394                                   (AtkCoordType) coordType);
395   x = ix;
396   y = iy;
397   width = iw;
398   height = ih;
399   reply = dbus_message_new_method_return (message);
400   if (reply)
401     {
402       dbus_message_append_args (reply, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32,
403                                 &y, DBUS_TYPE_INT32, &width, DBUS_TYPE_INT32,
404                                 &height, DBUS_TYPE_INVALID);
405     }
406   return reply;
407 }
408
409 static DBusMessage *
410 impl_GetOffsetAtPoint (DBusConnection * bus, DBusMessage * message,
411                        void *user_data)
412 {
413   AtkText *text = (AtkText *) user_data;
414   dbus_int32_t x, y;
415   dbus_uint32_t coordType;
416   dbus_int32_t rv;
417   DBusMessage *reply;
418
419   g_return_val_if_fail (ATK_IS_TEXT (user_data),
420                         droute_not_yet_handled_error (message));
421   if (!dbus_message_get_args
422       (message, NULL, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, &y,
423        DBUS_TYPE_UINT32, &coordType, DBUS_TYPE_INVALID))
424     {
425       return droute_invalid_arguments_error (message);
426     }
427   rv = atk_text_get_offset_at_point (text, x, y, coordType);
428   reply = dbus_message_new_method_return (message);
429   if (reply)
430     {
431       dbus_message_append_args (reply, DBUS_TYPE_INT32, &rv,
432                                 DBUS_TYPE_INVALID);
433     }
434   return reply;
435 }
436
437 static DBusMessage *
438 impl_GetNSelections (DBusConnection * bus, DBusMessage * message,
439                      void *user_data)
440 {
441   AtkText *text = (AtkText *) user_data;
442   dbus_int32_t rv;
443   DBusMessage *reply;
444
445   g_return_val_if_fail (ATK_IS_TEXT (user_data),
446                         droute_not_yet_handled_error (message));
447   rv = atk_text_get_n_selections (text);
448   reply = dbus_message_new_method_return (message);
449   if (reply)
450     {
451       dbus_message_append_args (reply, DBUS_TYPE_INT32, &rv,
452                                 DBUS_TYPE_INVALID);
453     }
454   return reply;
455 }
456
457 static DBusMessage *
458 impl_GetSelection (DBusConnection * bus, DBusMessage * message,
459                    void *user_data)
460 {
461   AtkText *text = (AtkText *) user_data;
462   dbus_int32_t selectionNum;
463   dbus_int32_t startOffset, endOffset;
464   gint intstart_offset = 0, intend_offset = 0;
465   DBusMessage *reply;
466
467   g_return_val_if_fail (ATK_IS_TEXT (user_data),
468                         droute_not_yet_handled_error (message));
469   if (!dbus_message_get_args
470       (message, NULL, DBUS_TYPE_INT32, &selectionNum, DBUS_TYPE_INVALID))
471     {
472       return droute_invalid_arguments_error (message);
473     }
474   /* atk_text_get_selection returns gchar * which we discard */
475   g_free (atk_text_get_selection
476           (text, selectionNum, &intstart_offset, &intend_offset));
477   startOffset = intstart_offset;
478   endOffset = intend_offset;
479   reply = dbus_message_new_method_return (message);
480   if (reply)
481     {
482       dbus_message_append_args (reply, DBUS_TYPE_INT32, &startOffset,
483                                 DBUS_TYPE_INT32, &endOffset,
484                                 DBUS_TYPE_INVALID);
485     }
486   return reply;
487 }
488
489 static DBusMessage *
490 impl_AddSelection (DBusConnection * bus, DBusMessage * message,
491                    void *user_data)
492 {
493   AtkText *text = (AtkText *) user_data;
494   dbus_int32_t startOffset, endOffset;
495   dbus_bool_t rv;
496   DBusMessage *reply;
497
498   g_return_val_if_fail (ATK_IS_TEXT (user_data),
499                         droute_not_yet_handled_error (message));
500   if (!dbus_message_get_args
501       (message, NULL, DBUS_TYPE_INT32, &startOffset, DBUS_TYPE_INT32,
502        &endOffset, DBUS_TYPE_INVALID))
503     {
504       return droute_invalid_arguments_error (message);
505     }
506   rv = atk_text_add_selection (text, startOffset, endOffset);
507   reply = dbus_message_new_method_return (message);
508   if (reply)
509     {
510       dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv,
511                                 DBUS_TYPE_INVALID);
512     }
513   return reply;
514 }
515
516 static DBusMessage *
517 impl_RemoveSelection (DBusConnection * bus, DBusMessage * message,
518                       void *user_data)
519 {
520   AtkText *text = (AtkText *) user_data;
521   dbus_int32_t selectionNum;
522   dbus_bool_t rv;
523   DBusMessage *reply;
524
525   g_return_val_if_fail (ATK_IS_TEXT (user_data),
526                         droute_not_yet_handled_error (message));
527   if (!dbus_message_get_args
528       (message, NULL, DBUS_TYPE_INT32, &selectionNum, DBUS_TYPE_INVALID))
529     {
530       return droute_invalid_arguments_error (message);
531     }
532   rv = atk_text_remove_selection (text, selectionNum);
533   reply = dbus_message_new_method_return (message);
534   if (reply)
535     {
536       dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv,
537                                 DBUS_TYPE_INVALID);
538     }
539   return reply;
540 }
541
542 static DBusMessage *
543 impl_SetSelection (DBusConnection * bus, DBusMessage * message,
544                    void *user_data)
545 {
546   AtkText *text = (AtkText *) user_data;
547   dbus_int32_t selectionNum, startOffset, endOffset;
548   dbus_bool_t rv;
549   DBusMessage *reply;
550
551   g_return_val_if_fail (ATK_IS_TEXT (user_data),
552                         droute_not_yet_handled_error (message));
553   if (!dbus_message_get_args
554       (message, NULL, DBUS_TYPE_INT32, &selectionNum, DBUS_TYPE_INT32,
555        &startOffset, DBUS_TYPE_INT32, &endOffset, DBUS_TYPE_INVALID))
556     {
557       return droute_invalid_arguments_error (message);
558     }
559   rv = atk_text_set_selection (text, selectionNum, startOffset, endOffset);
560   reply = dbus_message_new_method_return (message);
561   if (reply)
562     {
563       dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv,
564                                 DBUS_TYPE_INVALID);
565     }
566   return reply;
567 }
568
569 static DBusMessage *
570 impl_GetRangeExtents (DBusConnection * bus, DBusMessage * message,
571                       void *user_data)
572 {
573   AtkText *text = (AtkText *) user_data;
574   dbus_int32_t startOffset, endOffset;
575   dbus_uint32_t coordType;
576   AtkTextRectangle rect;
577   dbus_int32_t x, y, width, height;
578   DBusMessage *reply;
579
580   g_return_val_if_fail (ATK_IS_TEXT (user_data),
581                         droute_not_yet_handled_error (message));
582   if (!dbus_message_get_args
583       (message, NULL, DBUS_TYPE_INT32, &startOffset, DBUS_TYPE_INT32,
584        &endOffset, DBUS_TYPE_UINT32, &coordType, DBUS_TYPE_INVALID))
585     {
586       return droute_invalid_arguments_error (message);
587     }
588   memset (&rect, 0, sizeof (rect));
589   atk_text_get_range_extents (text, startOffset, endOffset,
590                               (AtkCoordType) coordType, &rect);
591   x = rect.x;
592   y = rect.y;
593   width = rect.width;
594   height = rect.height;
595   reply = dbus_message_new_method_return (message);
596   if (reply)
597     {
598       dbus_message_append_args (reply, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32,
599                                 &y, DBUS_TYPE_INT32, &width, DBUS_TYPE_INT32,
600                                 &height, DBUS_TYPE_INVALID);
601     }
602   return reply;
603 }
604
605 #define MAXRANGELEN 512
606
607 static DBusMessage *
608 impl_GetBoundedRanges (DBusConnection * bus, DBusMessage * message,
609                        void *user_data)
610 {
611   AtkText *text = (AtkText *) user_data;
612   dbus_int32_t x, y, width, height;
613   dbus_uint32_t coordType, xClipType, yClipType;
614   AtkTextRange **range_list = NULL;
615   AtkTextRectangle rect;
616   DBusMessage *reply;
617   DBusMessageIter iter, array, struc, variant;
618
619   g_return_val_if_fail (ATK_IS_TEXT (user_data),
620                         droute_not_yet_handled_error (message));
621   if (!dbus_message_get_args
622       (message, NULL, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, &y,
623        DBUS_TYPE_INT32, &height, DBUS_TYPE_INT32, &width, DBUS_TYPE_UINT32,
624        &coordType, DBUS_TYPE_UINT32, &xClipType, DBUS_TYPE_UINT32, &yClipType,
625        DBUS_TYPE_INVALID))
626     {
627       return droute_invalid_arguments_error (message);
628     }
629   rect.x = x;
630   rect.y = y;
631   rect.width = width;
632   rect.height = height;
633
634   range_list =
635     atk_text_get_bounded_ranges (text, &rect, (AtkCoordType) coordType,
636                                  (AtkTextClipType) xClipType,
637                                  (AtkTextClipType) yClipType);
638   reply = dbus_message_new_method_return (message);
639   if (!reply)
640     return NULL;
641   /* This isn't pleasant. */
642   dbus_message_iter_init_append (reply, &iter);
643   if (dbus_message_iter_open_container
644       (&iter, DBUS_TYPE_ARRAY, "(iisv)", &array))
645     {
646       int len;
647       for (len = 0; len < MAXRANGELEN && range_list[len]; ++len)
648         {
649           if (dbus_message_iter_open_container
650               (&array, DBUS_TYPE_STRUCT, NULL, &struc))
651             {
652               dbus_int32_t val;
653               val = range_list[len]->start_offset;
654               dbus_message_iter_append_basic (&struc, DBUS_TYPE_INT32, &val);
655               val = range_list[len]->end_offset;
656               dbus_message_iter_append_basic (&struc, DBUS_TYPE_INT32, &val);
657               dbus_message_iter_append_basic (&struc, DBUS_TYPE_STRING,
658                                               &range_list[len]->content);
659               /* The variant is unimplemented in atk, but I don't want to
660                * unilaterally muck with the spec and remove it, so I'll just
661                * throw in a dummy value */
662               if (dbus_message_iter_open_container
663                   (&struc, DBUS_TYPE_VARIANT, "i", &variant))
664                 {
665                   dbus_uint32_t dummy = 0;
666                   dbus_message_iter_append_basic (&variant, DBUS_TYPE_INT32,
667                                                   &dummy);
668                   dbus_message_iter_close_container (&struc, &variant);
669                 }
670               dbus_message_iter_close_container (&array, &struc);
671             }
672         }
673       dbus_message_iter_close_container (&iter, &array);
674     }
675   return reply;
676 }
677
678 static DBusMessage *
679 impl_GetAttributeRun (DBusConnection * bus, DBusMessage * message,
680                       void *user_data)
681 {
682   AtkText *text = (AtkText *) user_data;
683   dbus_int32_t offset;
684   dbus_bool_t includeDefaults;
685   dbus_int32_t startOffset, endOffset;
686   gint intstart_offset = 0, intend_offset = 0;
687   DBusMessage *reply;
688   AtkAttributeSet *attributes = NULL;
689   DBusMessageIter iter;
690
691   g_return_val_if_fail (ATK_IS_TEXT (user_data),
692                         droute_not_yet_handled_error (message));
693   if (!dbus_message_get_args
694       (message, NULL, DBUS_TYPE_INT32, &offset, DBUS_TYPE_BOOLEAN,
695        &includeDefaults, DBUS_TYPE_INVALID))
696     {
697       return droute_invalid_arguments_error (message);
698     }
699
700   attributes =
701     atk_text_get_run_attributes (text, offset, &intstart_offset,
702                                  &intend_offset);
703
704   if (includeDefaults)
705     {
706       attributes = g_slist_concat (attributes,
707                                    atk_text_get_default_attributes (text));
708     }
709
710   reply = dbus_message_new_method_return (message);
711   if (!reply)
712     return NULL;
713
714   dbus_message_iter_init_append (reply, &iter);
715   spi_object_append_attribute_set (&iter, attributes);
716
717   startOffset = intstart_offset;
718   endOffset = intend_offset;
719   dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &startOffset);
720   dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &endOffset);
721
722   atk_attribute_set_free (attributes);
723
724   return reply;
725 }
726
727 static DBusMessage *
728 impl_GetDefaultAttributeSet (DBusConnection * bus, DBusMessage * message,
729                              void *user_data)
730 {
731   AtkText *text = (AtkText *) user_data;
732   DBusMessage *reply;
733   DBusMessageIter iter;
734   AtkAttributeSet *attributes;
735
736   g_return_val_if_fail (ATK_IS_TEXT (user_data),
737                         droute_not_yet_handled_error (message));
738
739   attributes = atk_text_get_default_attributes (text);
740
741   reply = dbus_message_new_method_return (message);
742   if (reply)
743     {
744       dbus_message_iter_init_append (reply, &iter);
745       spi_object_append_attribute_set (&iter, attributes);
746     }
747
748   if (attributes)
749     atk_attribute_set_free (attributes);
750
751   return reply;
752 }
753
754 static DRouteMethod methods[] = {
755   {impl_GetText, "GetText"},
756   {impl_SetCaretOffset, "SetCaretOffset"},
757   {impl_GetTextBeforeOffset, "GetTextBeforeOffset"},
758   {impl_GetTextAtOffset, "GetTextAtOffset"},
759   {impl_GetTextAfterOffset, "GetTextAfterOffset"},
760   {impl_GetCharacterAtOffset, "GetCharacterAtOffset"},
761   {impl_GetAttributeValue, "GetAttributeValue"},
762   {impl_GetAttributes, "GetAttributes"},
763   {impl_GetDefaultAttributes, "GetDefaultAttributes"},
764   {impl_GetCharacterExtents, "GetCharacterExtents"},
765   {impl_GetOffsetAtPoint, "GetOffsetAtPoint"},
766   {impl_GetNSelections, "GetNSelections"},
767   {impl_GetSelection, "GetSelection"},
768   {impl_AddSelection, "AddSelection"},
769   {impl_RemoveSelection, "RemoveSelection"},
770   {impl_SetSelection, "SetSelection"},
771   {impl_GetRangeExtents, "GetRangeExtents"},
772   {impl_GetBoundedRanges, "GetBoundedRanges"},
773   {impl_GetAttributeRun, "GetAttributeRun"},
774   {impl_GetDefaultAttributeSet, "GetDefaultAttributeSet"},
775   {NULL, NULL}
776 };
777
778 static DRouteProperty properties[] = {
779   {impl_get_CharacterCount, NULL, "CharacterCount"},
780   {impl_get_CaretOffset, NULL, "CaretOffset"},
781   {NULL, NULL, NULL}
782 };
783
784 void
785 spi_initialize_text (DRoutePath * path)
786 {
787   droute_path_add_interface (path,
788                              ATSPI_DBUS_INTERFACE_TEXT, spi_org_a11y_atspi_Text, methods, properties);
789 };