451a92bb02ea41efc94bfc8942639710c1213d60
[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 = atk_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 = atk_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_getCharacterAtOffset (DBusConnection * bus, DBusMessage * message,
246                          void *user_data)
247 {
248   AtkText *text = get_text (message);
249   dbus_int32_t offset;
250   dbus_int32_t ch;
251   DBusError error;
252   DBusMessage *reply;
253
254   if (!text)
255     return spi_dbus_general_error (message);
256   dbus_error_init (&error);
257   if (!dbus_message_get_args
258       (message, &error, DBUS_TYPE_INT32, &offset, DBUS_TYPE_INVALID))
259     {
260       return SPI_DBUS_RETURN_ERROR (message, &error);
261     }
262   ch = atk_text_get_character_at_offset (text, offset);
263   reply = dbus_message_new_method_return (message);
264   if (reply)
265     {
266       dbus_message_append_args (reply, DBUS_TYPE_INT32, &ch, DBUS_TYPE_INVALID);
267     }
268   return reply;
269 }
270
271 static DBusMessage *
272 impl_getAttributeValue (DBusConnection * bus, DBusMessage * message,
273                         void *user_data)
274 {
275   AtkText *text = get_text (message);
276   dbus_int32_t offset;
277   char *attributeName;
278   dbus_int32_t startOffset, endOffset;
279   dbus_bool_t defined;
280   gint intstart_offset = 0, intend_offset = 0;
281   char *rv;
282   DBusError error;
283   DBusMessage *reply;
284   AtkAttributeSet *set;
285   GSList *cur_attr;
286   AtkAttribute *at;
287
288   if (!text)
289     return spi_dbus_general_error (message);
290   dbus_error_init (&error);
291   if (!dbus_message_get_args
292       (message, &error, DBUS_TYPE_INT32, &offset, DBUS_TYPE_STRING,
293        &attributeName, DBUS_TYPE_INVALID))
294     {
295       return SPI_DBUS_RETURN_ERROR (message, &error);
296     }
297
298   set = atk_text_get_run_attributes (text, offset,
299                                      &intstart_offset, &intend_offset);
300   startOffset = intstart_offset;
301   endOffset = intend_offset;
302   defined = FALSE;
303   cur_attr = (GSList *) set;
304   while (cur_attr)
305     {
306       at = (AtkAttribute *) cur_attr->data;
307       if (!strcmp (at->name, attributeName))
308         {
309           rv = at->value;
310           defined = TRUE;
311           break;
312         }
313       cur_attr = cur_attr->next;
314     }
315   if (!rv)
316     rv = "";
317   reply = dbus_message_new_method_return (message);
318   if (reply)
319     {
320       dbus_message_append_args (reply, DBUS_TYPE_INT32, &startOffset,
321                                 DBUS_TYPE_INT32, &endOffset,
322                                 DBUS_TYPE_BOOLEAN, &defined, DBUS_TYPE_STRING,
323                                 &rv, DBUS_TYPE_INVALID);
324     }
325   atk_attribute_set_free (set);
326   return reply;
327 }
328
329 static char *
330 _string_from_attribute_set (AtkAttributeSet * set)
331 {
332   gchar *attributes, *tmp, *tmp2;
333   GSList *cur_attr;
334   AtkAttribute *at;
335
336   attributes = g_strdup ("");
337   cur_attr = (GSList *) set;
338   while (cur_attr)
339     {
340       at = (AtkAttribute *) cur_attr->data;
341       tmp = g_strdup_printf ("%s%s:%s%s",
342                              ((GSList *) (set) == cur_attr) ? "" : " ",
343                              at->name, at->value,
344                              (cur_attr->next) ? ";" : "");
345       tmp2 = g_strconcat (attributes, tmp, NULL);
346       g_free (tmp);
347       g_free (attributes);
348       attributes = tmp2;
349       cur_attr = cur_attr->next;
350     }
351   return attributes;
352 }
353
354 static DBusMessage *
355 impl_getAttributes (DBusConnection * bus, DBusMessage * message,
356                     void *user_data)
357 {
358   AtkText *text = get_text (message);
359   dbus_int32_t offset;
360   dbus_int32_t startOffset, endOffset;
361   gint intstart_offset, intend_offset;
362   char *rv;
363   DBusError error;
364   DBusMessage *reply;
365   AtkAttributeSet *set;
366
367   if (!text)
368     return spi_dbus_general_error (message);
369   dbus_error_init (&error);
370   if (!dbus_message_get_args
371       (message, &error, DBUS_TYPE_INT32, &offset, DBUS_TYPE_INVALID))
372     {
373       return SPI_DBUS_RETURN_ERROR (message, &error);
374     }
375
376   set = atk_text_get_run_attributes (text, offset,
377                                      &intstart_offset, &intend_offset);
378
379   rv = _string_from_attribute_set (set);
380
381   startOffset = intstart_offset;
382   endOffset = intend_offset;
383   reply = dbus_message_new_method_return (message);
384   if (reply)
385     {
386       dbus_message_append_args (reply, DBUS_TYPE_STRING, &rv, DBUS_TYPE_INT32, &startOffset,
387                                 DBUS_TYPE_INT32, &endOffset, DBUS_TYPE_INVALID);
388     }
389   atk_attribute_set_free (set);
390   g_free(rv);
391   return reply;
392 }
393
394 static DBusMessage *
395 impl_getDefaultAttributes (DBusConnection * bus, DBusMessage * message,
396                            void *user_data)
397 {
398   AtkText *text = get_text (message);
399   char *rv;
400   DBusError error;
401   DBusMessage *reply;
402   AtkAttributeSet *set;
403
404   if (!text)
405     return spi_dbus_general_error (message);
406   dbus_error_init (&error);
407
408   set = atk_text_get_default_attributes (text);
409   rv = _string_from_attribute_set (set);
410   reply = dbus_message_new_method_return (message);
411   if (reply)
412     {
413       dbus_message_append_args (reply, DBUS_TYPE_STRING, &rv,
414                                 DBUS_TYPE_INVALID);
415     }
416   g_free (rv);
417   atk_attribute_set_free (set);
418   return reply;
419 }
420
421 static DBusMessage *
422 impl_getCharacterExtents (DBusConnection * bus, DBusMessage * message,
423                           void *user_data)
424 {
425   AtkText *text = get_text (message);
426   dbus_int32_t offset;
427   dbus_int16_t coordType;
428   dbus_int32_t x, y, width, height;
429   gint ix = 0, iy = 0, iw = 0, ih = 0;
430   DBusError error;
431   DBusMessage *reply;
432
433   if (!text)
434     return spi_dbus_general_error (message);
435   dbus_error_init (&error);
436   if (!dbus_message_get_args
437       (message, &error, DBUS_TYPE_INT32, &offset, DBUS_TYPE_INT16, &coordType,
438        DBUS_TYPE_INVALID))
439     {
440       return SPI_DBUS_RETURN_ERROR (message, &error);
441     }
442   atk_text_get_character_extents (text, offset, &ix, &iy, &iw, &ih,
443                                   (AtkCoordType) coordType);
444   x = ix;
445   y = iy;
446   width = iw;
447   height = ih;
448   reply = dbus_message_new_method_return (message);
449   if (reply)
450     {
451       dbus_message_append_args (reply, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32,
452                                 &y, DBUS_TYPE_INT32, &width, DBUS_TYPE_INT32,
453                                 &height, DBUS_TYPE_INVALID);
454     }
455   return reply;
456 }
457
458 static DBusMessage *
459 impl_getOffsetAtPoint (DBusConnection * bus, DBusMessage * message,
460                        void *user_data)
461 {
462   AtkText *text = get_text (message);
463   dbus_int32_t x, y;
464   dbus_int16_t coordType;
465   dbus_int32_t rv;
466   DBusError error;
467   DBusMessage *reply;
468
469   if (!text)
470     return spi_dbus_general_error (message);
471   dbus_error_init (&error);
472   if (!dbus_message_get_args
473       (message, &error, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, &y,
474        DBUS_TYPE_INT16, &coordType, DBUS_TYPE_INVALID))
475     {
476       return SPI_DBUS_RETURN_ERROR (message, &error);
477     }
478   rv = atk_text_get_offset_at_point (text, x, y, coordType);
479   reply = dbus_message_new_method_return (message);
480   if (reply)
481     {
482       dbus_message_append_args (reply, DBUS_TYPE_INT32, &rv,
483                                 DBUS_TYPE_INVALID);
484     }
485   return reply;
486 }
487
488 static DBusMessage *
489 impl_getNSelections (DBusConnection * bus, DBusMessage * message,
490                      void *user_data)
491 {
492   AtkText *text = get_text (message);
493   dbus_int32_t rv;
494   DBusMessage *reply;
495
496   if (!text)
497     return spi_dbus_general_error (message);
498   rv = atk_text_get_n_selections (text);
499   reply = dbus_message_new_method_return (message);
500   if (reply)
501     {
502       dbus_message_append_args (reply, DBUS_TYPE_INT32, &rv,
503                                 DBUS_TYPE_INVALID);
504     }
505   return reply;
506 }
507
508 static DBusMessage *
509 impl_getSelection (DBusConnection * bus, DBusMessage * message,
510                    void *user_data)
511 {
512   AtkText *text = get_text (message);
513   dbus_int32_t selectionNum;
514   dbus_int32_t startOffset, endOffset;
515   gint intstart_offset = 0, intend_offset = 0;
516   DBusError error;
517   DBusMessage *reply;
518
519   if (!text)
520     return spi_dbus_general_error (message);
521   dbus_error_init (&error);
522   if (!dbus_message_get_args
523       (message, &error, DBUS_TYPE_INT32, &selectionNum, DBUS_TYPE_INVALID))
524     {
525       return SPI_DBUS_RETURN_ERROR (message, &error);
526     }
527   /* atk_text_get_selection returns gchar * which we discard */
528   g_free (atk_text_get_selection
529           (text, selectionNum, &intstart_offset, &intend_offset));
530   startOffset = intstart_offset;
531   endOffset = intend_offset;
532   reply = dbus_message_new_method_return (message);
533   if (reply)
534     {
535       dbus_message_append_args (reply, DBUS_TYPE_INT32, &startOffset,
536                                 DBUS_TYPE_INT32, &endOffset,
537                                 DBUS_TYPE_INVALID);
538     }
539   return reply;
540 }
541
542 static DBusMessage *
543 impl_addSelection (DBusConnection * bus, DBusMessage * message,
544                    void *user_data)
545 {
546   AtkText *text = get_text (message);
547   dbus_int32_t startOffset, endOffset;
548   dbus_bool_t rv;
549   DBusError error;
550   DBusMessage *reply;
551
552   if (!text)
553     return spi_dbus_general_error (message);
554   dbus_error_init (&error);
555   if (!dbus_message_get_args
556       (message, &error, DBUS_TYPE_INT32, &startOffset, DBUS_TYPE_INT32,
557        &endOffset, DBUS_TYPE_INVALID))
558     {
559       return SPI_DBUS_RETURN_ERROR (message, &error);
560     }
561   rv = atk_text_add_selection (text, startOffset, endOffset);
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_removeSelection (DBusConnection * bus, DBusMessage * message,
573                       void *user_data)
574 {
575   AtkText *text = get_text (message);
576   dbus_int32_t selectionNum;
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_INVALID))
586     {
587       return SPI_DBUS_RETURN_ERROR (message, &error);
588     }
589   rv = atk_text_remove_selection (text, selectionNum);
590   reply = dbus_message_new_method_return (message);
591   if (reply)
592     {
593       dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv,
594                                 DBUS_TYPE_INVALID);
595     }
596   return reply;
597 }
598
599 static DBusMessage *
600 impl_setSelection (DBusConnection * bus, DBusMessage * message,
601                    void *user_data)
602 {
603   AtkText *text = get_text (message);
604   dbus_int32_t selectionNum, startOffset, endOffset;
605   dbus_bool_t rv;
606   DBusError error;
607   DBusMessage *reply;
608
609   if (!text)
610     return spi_dbus_general_error (message);
611   dbus_error_init (&error);
612   if (!dbus_message_get_args
613       (message, &error, DBUS_TYPE_INT32, &selectionNum, DBUS_TYPE_INT32,
614        &startOffset, DBUS_TYPE_INT32, &endOffset, DBUS_TYPE_INVALID))
615     {
616       return SPI_DBUS_RETURN_ERROR (message, &error);
617     }
618   rv = atk_text_set_selection (text, selectionNum, startOffset, endOffset);
619   reply = dbus_message_new_method_return (message);
620   if (reply)
621     {
622       dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv,
623                                 DBUS_TYPE_INVALID);
624     }
625   return reply;
626 }
627
628 static DBusMessage *
629 impl_getRangeExtents (DBusConnection * bus, DBusMessage * message,
630                       void *user_data)
631 {
632   AtkText *text = get_text (message);
633   dbus_int32_t startOffset, endOffset;
634   dbus_int16_t coordType;
635   AtkTextRectangle rect;
636   dbus_int32_t x, y, width, height;
637   DBusError error;
638   DBusMessage *reply;
639
640   if (!text)
641     return spi_dbus_general_error (message);
642   dbus_error_init (&error);
643   if (!dbus_message_get_args
644       (message, &error, DBUS_TYPE_INT32, &startOffset, DBUS_TYPE_INT32,
645        &endOffset, DBUS_TYPE_INT16, &coordType, DBUS_TYPE_INVALID))
646     {
647       return SPI_DBUS_RETURN_ERROR (message, &error);
648     }
649   memset (&rect, 0, sizeof (rect));
650   atk_text_get_range_extents (text, startOffset, endOffset,
651                               (AtkCoordType) coordType, &rect);
652   x = rect.x;
653   y = rect.y;
654   width = rect.width;
655   height = rect.height;
656   reply = dbus_message_new_method_return (message);
657   if (reply)
658     {
659       dbus_message_append_args (reply, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32,
660                                 &y, DBUS_TYPE_INT32, &width, DBUS_TYPE_INT32,
661                                 &height, DBUS_TYPE_INVALID);
662     }
663   return reply;
664 }
665
666 #define MAXRANGELEN 512
667
668 static DBusMessage *
669 impl_getBoundedRanges (DBusConnection * bus, DBusMessage * message,
670                        void *user_data)
671 {
672   AtkText *text = get_text (message);
673   dbus_int32_t x, y, width, height;
674   dbus_int16_t coordType, xClipType, yClipType;
675   DBusError error;
676   AtkTextRange **range_list = NULL;
677   AtkTextRectangle rect;
678   DBusMessage *reply;
679   DBusMessageIter iter, array, struc, variant;
680
681   if (!text)
682     return spi_dbus_general_error (message);
683   dbus_error_init (&error);
684   if (!dbus_message_get_args
685       (message, &error, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, &y,
686        DBUS_TYPE_INT32, &height, DBUS_TYPE_INT32, &width, DBUS_TYPE_INT32,
687        &coordType, DBUS_TYPE_INT32, &xClipType, DBUS_TYPE_INT32, &yClipType,
688        DBUS_TYPE_INVALID))
689     {
690       return SPI_DBUS_RETURN_ERROR (message, &error);
691     }
692   rect.x = x;
693   rect.y = y;
694   rect.width = width;
695   rect.height = height;
696
697   range_list =
698     atk_text_get_bounded_ranges (text, &rect, (AtkCoordType) coordType,
699                                  (AtkTextClipType) xClipType,
700                                  (AtkTextClipType) yClipType);
701   reply = dbus_message_new_method_return (message);
702   if (!reply)
703     return NULL;
704   /* This isn't pleasant. */
705   dbus_message_iter_init_append (reply, &iter);
706   if (dbus_message_iter_open_container
707       (&iter, DBUS_TYPE_ARRAY, "(iisv)", &array))
708     {
709       int len;
710       for (len = 0; len < MAXRANGELEN && range_list[len]; ++len)
711         {
712           if (dbus_message_iter_open_container
713               (&array, DBUS_TYPE_STRUCT, NULL, &struc))
714             {
715               dbus_int32_t val;
716               val = range_list[len]->start_offset;
717               dbus_message_iter_append_basic (&struc, DBUS_TYPE_INT32, &val);
718               val = range_list[len]->end_offset;
719               dbus_message_iter_append_basic (&struc, DBUS_TYPE_INT32, &val);
720               dbus_message_iter_append_basic (&struc, DBUS_TYPE_STRING,
721                                               &range_list[len]->content);
722               /* The variant is unimplemented in atk, but I don't want to
723                * unilaterally muck with the spec and remove it, so I'll just
724                * throw in a dummy value */
725               if (dbus_message_iter_open_container
726                   (&array, DBUS_TYPE_VARIANT, "i", &variant))
727                 {
728                   dbus_uint32_t dummy = 0;
729                   dbus_message_iter_append_basic (&variant, DBUS_TYPE_INT32,
730                                                   &dummy);
731                   dbus_message_iter_close_container (&struc, &variant);
732                 }
733               dbus_message_iter_close_container (&array, &struc);
734             }
735         }
736       dbus_message_iter_close_container (&iter, &array);
737     }
738   return reply;
739 }
740
741 static DBusMessage *
742 impl_getAttributeRun (DBusConnection * bus, DBusMessage * message,
743                       void *user_data)
744 {
745   DBusError error;
746   AtkText *text = get_text (message);
747   dbus_int32_t offset;
748   dbus_bool_t includeDefaults;
749   dbus_int32_t startOffset, endOffset;
750   gint intstart_offset = 0, intend_offset = 0;
751   DBusMessage *reply;
752   AtkAttributeSet *attributes, *default_attributes = NULL;
753   AtkAttribute *attr = NULL;
754   char **retval;
755   gint n_attributes = 0, total_attributes = 0, n_default_attributes = 0;
756   gint i, j;
757
758   if (!text)
759     return spi_dbus_general_error (message);
760   dbus_error_init (&error);
761   if (!dbus_message_get_args
762       (message, &error, DBUS_TYPE_INT32, &offset, DBUS_TYPE_BOOLEAN,
763        &includeDefaults, DBUS_TYPE_INVALID))
764     {
765       return SPI_DBUS_RETURN_ERROR (message, &error);
766     }
767
768   attributes =
769     atk_text_get_run_attributes (text, offset, &intstart_offset,
770                                  &intend_offset);
771
772   if (attributes)
773     total_attributes = n_attributes = g_slist_length (attributes);
774
775   if (includeDefaults)
776     {
777       default_attributes = atk_text_get_default_attributes (text);
778       if (default_attributes)
779         n_default_attributes = g_slist_length (default_attributes);
780       total_attributes += n_default_attributes;
781     }
782
783   startOffset = intstart_offset;
784   endOffset = intend_offset;
785
786   retval = (char **) g_malloc (total_attributes * sizeof (char *));
787
788   if (total_attributes)
789     {
790       for (i = 0; i < n_attributes; ++i)
791         {
792           attr = g_slist_nth_data (attributes, i);
793           retval[i] = g_strconcat (attr->name, ":", attr->value, NULL);
794         }
795
796       for (j = 0; j < n_default_attributes; ++i, ++j)
797         {
798           attr = g_slist_nth_data (default_attributes, j);
799           retval[i] = g_strconcat (attr->name, ":", attr->value, NULL);
800         }
801
802       atk_attribute_set_free (attributes);
803       if (default_attributes)
804         atk_attribute_set_free (default_attributes);
805     }
806   reply = dbus_message_new_method_return (message);
807   if (reply)
808     {
809       dbus_message_append_args (reply, DBUS_TYPE_INT32, &startOffset,
810                                 DBUS_TYPE_INT32, &endOffset, DBUS_TYPE_ARRAY,
811                                 DBUS_TYPE_STRING, &retval, total_attributes,
812                                 DBUS_TYPE_INVALID);
813     }
814   for (i = 0; i < total_attributes; i++)
815     g_free (retval[i]);
816   g_free (retval);
817   return reply;
818 }
819
820 static DBusMessage *
821 impl_getDefaultAttributeSet (DBusConnection * bus, DBusMessage * message,
822                              void *user_data)
823 {
824   AtkText *text = get_text (message);
825   DBusMessage *reply;
826   AtkAttributeSet *attributes;
827   AtkAttribute *attr = NULL;
828   char **retval;
829   gint n_attributes = 0;
830   gint i;
831
832   if (!text)
833     return spi_dbus_general_error (message);
834
835   attributes = atk_text_get_default_attributes (text);
836   if (attributes)
837     n_attributes = g_slist_length (attributes);
838
839   retval = g_new (char *, n_attributes);
840
841   for (i = 0; i < n_attributes; ++i)
842     {
843       attr = g_slist_nth_data (attributes, i);
844       retval[i] = g_strconcat (attr->name, ":", attr->value, NULL);
845     }
846   if (attributes)
847     atk_attribute_set_free (attributes);
848   reply = dbus_message_new_method_return (message);
849   if (reply)
850     {
851       dbus_message_append_args (reply, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
852                                 &retval, n_attributes, DBUS_TYPE_INVALID);
853     }
854   for (i = 0; i < n_attributes; i++)
855     g_free (retval[i]);
856   g_free (retval);
857   return reply;
858 }
859
860 static DRouteMethod methods[] = {
861   {impl_getText, "getText"},
862   {impl_setCaretOffset, "setCaretOffset"},
863   {impl_getTextBeforeOffset, "getTextBeforeOffset"},
864   {impl_getTextAtOffset, "getTextAtOffset"},
865   {impl_getTextAfterOffset, "getTextAfterOffset"},
866   {impl_getCharacterAtOffset, "getCharacterAtOffset"},
867   {impl_getAttributeValue, "getAttributeValue"},
868   {impl_getAttributes, "getAttributes"},
869   {impl_getDefaultAttributes, "getDefaultAttributes"},
870   {impl_getCharacterExtents, "getCharacterExtents"},
871   {impl_getOffsetAtPoint, "getOffsetAtPoint"},
872   {impl_getNSelections, "getNSelections"},
873   {impl_getSelection, "getSelection"},
874   {impl_addSelection, "addSelection"},
875   {impl_removeSelection, "removeSelection"},
876   {impl_setSelection, "setSelection"},
877   {impl_getRangeExtents, "getRangeExtents"},
878   {impl_getBoundedRanges, "getBoundedRanges"},
879   {impl_getAttributeRun, "getAttributeRun"},
880   {impl_getDefaultAttributeSet, "getDefaultAttributeSet"},
881   {NULL, NULL}
882 };
883
884 static DRouteProperty properties[] = {
885   {impl_get_characterCount, NULL, "characterCount"},
886   {impl_get_caretOffset, NULL, "caretOffset"},
887   {NULL, NULL, NULL}
888 };
889
890 void
891 spi_initialize_text (DRouteData * data)
892 {
893   droute_add_interface (data, SPI_DBUS_INTERFACE_TEXT, methods,
894                         properties,
895                         (DRouteGetDatumFunction) get_text_from_path, NULL);
896 };