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