Use macros for interface names
[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_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   startOffset = intstart_offset;
379   endOffset = intend_offset;
380   rv = _string_from_attribute_set (set);
381   reply = dbus_message_new_method_return (message);
382   if (reply)
383     {
384       dbus_message_append_args (reply, DBUS_TYPE_INT32, &startOffset,
385                                 DBUS_TYPE_INT32, &endOffset, DBUS_TYPE_STRING,
386                                 &rv, DBUS_TYPE_INVALID);
387     }
388   g_free (rv);
389   atk_attribute_set_free (set);
390   return reply;
391 }
392
393 static DBusMessage *
394 impl_getDefaultAttributes (DBusConnection * bus, DBusMessage * message,
395                            void *user_data)
396 {
397   AtkText *text = get_text (message);
398   char *rv;
399   DBusError error;
400   DBusMessage *reply;
401   AtkAttributeSet *set;
402
403   if (!text)
404     return spi_dbus_general_error (message);
405   dbus_error_init (&error);
406
407   set = atk_text_get_default_attributes (text);
408   rv = _string_from_attribute_set (set);
409   reply = dbus_message_new_method_return (message);
410   if (reply)
411     {
412       dbus_message_append_args (reply, DBUS_TYPE_STRING, &rv,
413                                 DBUS_TYPE_INVALID);
414     }
415   g_free (rv);
416   atk_attribute_set_free (set);
417   return reply;
418 }
419
420 static DBusMessage *
421 impl_getCharacterExtents (DBusConnection * bus, DBusMessage * message,
422                           void *user_data)
423 {
424   AtkText *text = get_text (message);
425   dbus_int32_t offset;
426   dbus_uint32_t coordType;
427   dbus_int32_t x, y, width, height;
428   gint ix = 0, iy = 0, iw = 0, ih = 0;
429   DBusError error;
430   DBusMessage *reply;
431
432   if (!text)
433     return spi_dbus_general_error (message);
434   dbus_error_init (&error);
435   if (!dbus_message_get_args
436       (message, &error, DBUS_TYPE_INT32, &offset, DBUS_TYPE_INT32, &coordType,
437        DBUS_TYPE_INVALID))
438     {
439       return SPI_DBUS_RETURN_ERROR (message, &error);
440     }
441   atk_text_get_character_extents (text, offset, &ix, &iy, &iw, &ih,
442                                   (AtkCoordType) coordType);
443   x = ix;
444   y = iy;
445   width = iw;
446   height = ih;
447   reply = dbus_message_new_method_return (message);
448   if (reply)
449     {
450       dbus_message_append_args (reply, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32,
451                                 &y, DBUS_TYPE_INT32, &width, DBUS_TYPE_INT32,
452                                 &height, DBUS_TYPE_INVALID);
453     }
454   return reply;
455 }
456
457 static DBusMessage *
458 impl_getOffsetAtPoint (DBusConnection * bus, DBusMessage * message,
459                        void *user_data)
460 {
461   AtkText *text = get_text (message);
462   dbus_int32_t x, y;
463   dbus_uint32_t coordType;
464   dbus_int32_t rv;
465   DBusError error;
466   DBusMessage *reply;
467
468   if (!text)
469     return spi_dbus_general_error (message);
470   dbus_error_init (&error);
471   if (!dbus_message_get_args
472       (message, &error, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, &y,
473        DBUS_TYPE_UINT32, &coordType, DBUS_TYPE_INVALID))
474     {
475       return SPI_DBUS_RETURN_ERROR (message, &error);
476     }
477   rv = atk_text_get_offset_at_point (text, x, y, coordType);
478   reply = dbus_message_new_method_return (message);
479   if (reply)
480     {
481       dbus_message_append_args (reply, DBUS_TYPE_INT32, &rv,
482                                 DBUS_TYPE_INVALID);
483     }
484   return reply;
485 }
486
487 static DBusMessage *
488 impl_getNSelections (DBusConnection * bus, DBusMessage * message,
489                      void *user_data)
490 {
491   AtkText *text = get_text (message);
492   dbus_int32_t rv;
493   DBusMessage *reply;
494
495   if (!text)
496     return spi_dbus_general_error (message);
497   rv = atk_text_get_n_selections (text);
498   reply = dbus_message_new_method_return (message);
499   if (reply)
500     {
501       dbus_message_append_args (reply, DBUS_TYPE_INT32, &rv,
502                                 DBUS_TYPE_INVALID);
503     }
504   return reply;
505 }
506
507 static DBusMessage *
508 impl_getSelection (DBusConnection * bus, DBusMessage * message,
509                    void *user_data)
510 {
511   AtkText *text = get_text (message);
512   dbus_int32_t selectionNum;
513   dbus_int32_t startOffset, endOffset;
514   gint intstart_offset = 0, intend_offset = 0;
515   DBusError error;
516   DBusMessage *reply;
517
518   if (!text)
519     return spi_dbus_general_error (message);
520   dbus_error_init (&error);
521   if (!dbus_message_get_args
522       (message, &error, DBUS_TYPE_INT32, &selectionNum, DBUS_TYPE_INVALID))
523     {
524       return SPI_DBUS_RETURN_ERROR (message, &error);
525     }
526   /* atk_text_get_selection returns gchar * which we discard */
527   g_free (atk_text_get_selection
528           (text, selectionNum, &intstart_offset, &intend_offset));
529   startOffset = intstart_offset;
530   endOffset = intend_offset;
531   reply = dbus_message_new_method_return (message);
532   if (reply)
533     {
534       dbus_message_append_args (reply, DBUS_TYPE_INT32, &startOffset,
535                                 DBUS_TYPE_INT32, &endOffset,
536                                 DBUS_TYPE_INVALID);
537     }
538   return reply;
539 }
540
541 static DBusMessage *
542 impl_addSelection (DBusConnection * bus, DBusMessage * message,
543                    void *user_data)
544 {
545   AtkText *text = get_text (message);
546   dbus_int32_t startOffset, endOffset;
547   dbus_bool_t rv;
548   DBusError error;
549   DBusMessage *reply;
550
551   if (!text)
552     return spi_dbus_general_error (message);
553   dbus_error_init (&error);
554   if (!dbus_message_get_args
555       (message, &error, DBUS_TYPE_INT32, &startOffset, DBUS_TYPE_INT32,
556        &endOffset, DBUS_TYPE_INVALID))
557     {
558       return SPI_DBUS_RETURN_ERROR (message, &error);
559     }
560   rv = atk_text_add_selection (text, startOffset, endOffset);
561   reply = dbus_message_new_method_return (message);
562   if (reply)
563     {
564       dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv,
565                                 DBUS_TYPE_INVALID);
566     }
567   return reply;
568 }
569
570 static DBusMessage *
571 impl_removeSelection (DBusConnection * bus, DBusMessage * message,
572                       void *user_data)
573 {
574   AtkText *text = get_text (message);
575   dbus_int32_t selectionNum;
576   dbus_bool_t rv;
577   DBusError error;
578   DBusMessage *reply;
579
580   if (!text)
581     return spi_dbus_general_error (message);
582   dbus_error_init (&error);
583   if (!dbus_message_get_args
584       (message, &error, DBUS_TYPE_INT32, &selectionNum, DBUS_TYPE_INVALID))
585     {
586       return SPI_DBUS_RETURN_ERROR (message, &error);
587     }
588   rv = atk_text_remove_selection (text, selectionNum);
589   reply = dbus_message_new_method_return (message);
590   if (reply)
591     {
592       dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv,
593                                 DBUS_TYPE_INVALID);
594     }
595   return reply;
596 }
597
598 static DBusMessage *
599 impl_setSelection (DBusConnection * bus, DBusMessage * message,
600                    void *user_data)
601 {
602   AtkText *text = get_text (message);
603   dbus_int32_t selectionNum, startOffset, endOffset;
604   dbus_bool_t rv;
605   DBusError error;
606   DBusMessage *reply;
607
608   if (!text)
609     return spi_dbus_general_error (message);
610   dbus_error_init (&error);
611   if (!dbus_message_get_args
612       (message, &error, DBUS_TYPE_INT32, &selectionNum, DBUS_TYPE_INT32,
613        &startOffset, DBUS_TYPE_INT32, &endOffset, DBUS_TYPE_INVALID))
614     {
615       return SPI_DBUS_RETURN_ERROR (message, &error);
616     }
617   rv = atk_text_set_selection (text, selectionNum, startOffset, endOffset);
618   reply = dbus_message_new_method_return (message);
619   if (reply)
620     {
621       dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv,
622                                 DBUS_TYPE_INVALID);
623     }
624   return reply;
625 }
626
627 static DBusMessage *
628 impl_getRangeExtents (DBusConnection * bus, DBusMessage * message,
629                       void *user_data)
630 {
631   AtkText *text = get_text (message);
632   dbus_int32_t startOffset, endOffset;
633   dbus_uint32_t coordType;
634   AtkTextRectangle rect;
635   dbus_int32_t x, y, width, height;
636   DBusError error;
637   DBusMessage *reply;
638
639   if (!text)
640     return spi_dbus_general_error (message);
641   dbus_error_init (&error);
642   if (!dbus_message_get_args
643       (message, &error, DBUS_TYPE_INT32, &startOffset, DBUS_TYPE_INT32,
644        &endOffset, DBUS_TYPE_UINT32, &coordType, DBUS_TYPE_INVALID))
645     {
646       return SPI_DBUS_RETURN_ERROR (message, &error);
647     }
648   memset (&rect, 0, sizeof (rect));
649   atk_text_get_range_extents (text, startOffset, endOffset,
650                               (AtkCoordType) coordType, &rect);
651   x = rect.x;
652   y = rect.y;
653   width = rect.width;
654   height = rect.height;
655   reply = dbus_message_new_method_return (message);
656   if (reply)
657     {
658       dbus_message_append_args (reply, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32,
659                                 &y, DBUS_TYPE_INT32, &width, DBUS_TYPE_INT32,
660                                 &height, DBUS_TYPE_INVALID);
661     }
662   return reply;
663 }
664
665 #define MAXRANGELEN 512
666
667 static DBusMessage *
668 impl_getBoundedRanges (DBusConnection * bus, DBusMessage * message,
669                        void *user_data)
670 {
671   AtkText *text = get_text (message);
672   dbus_int32_t x, y, width, height;
673   dbus_uint32_t coordType, xClipType, yClipType;
674   DBusError error;
675   AtkTextRange **range_list = NULL;
676   AtkTextRectangle rect;
677   DBusMessage *reply;
678   DBusMessageIter iter, array, struc, variant;
679
680   if (!text)
681     return spi_dbus_general_error (message);
682   dbus_error_init (&error);
683   if (!dbus_message_get_args
684       (message, &error, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, &y,
685        DBUS_TYPE_INT32, &height, DBUS_TYPE_INT32, &width, DBUS_TYPE_INT32,
686        &coordType, DBUS_TYPE_INT32, &xClipType, DBUS_TYPE_INT32, &yClipType,
687        DBUS_TYPE_INVALID))
688     {
689       return SPI_DBUS_RETURN_ERROR (message, &error);
690     }
691   rect.x = x;
692   rect.y = y;
693   rect.width = width;
694   rect.height = height;
695
696   range_list =
697     atk_text_get_bounded_ranges (text, &rect, (AtkCoordType) coordType,
698                                  (AtkTextClipType) xClipType,
699                                  (AtkTextClipType) yClipType);
700   reply = dbus_message_new_method_return (message);
701   if (!reply)
702     return NULL;
703   /* This isn't pleasant. */
704   dbus_message_iter_init_append (reply, &iter);
705   if (dbus_message_iter_open_container
706       (&iter, DBUS_TYPE_ARRAY, "(iisv)", &array))
707     {
708       int len;
709       for (len = 0; len < MAXRANGELEN && range_list[len]; ++len)
710         {
711           if (dbus_message_iter_open_container
712               (&array, DBUS_TYPE_STRUCT, NULL, &struc))
713             {
714               dbus_int32_t val;
715               val = range_list[len]->start_offset;
716               dbus_message_iter_append_basic (&struc, DBUS_TYPE_INT32, &val);
717               val = range_list[len]->end_offset;
718               dbus_message_iter_append_basic (&struc, DBUS_TYPE_INT32, &val);
719               dbus_message_iter_append_basic (&struc, DBUS_TYPE_STRING,
720                                               &range_list[len]->content);
721               /* The variant is unimplemented in atk, but I don't want to
722                * unilaterally muck with the spec and remove it, so I'll just
723                * throw in a dummy value */
724               if (dbus_message_iter_open_container
725                   (&array, DBUS_TYPE_VARIANT, "i", &variant))
726                 {
727                   dbus_uint32_t dummy = 0;
728                   dbus_message_iter_append_basic (&variant, DBUS_TYPE_INT32,
729                                                   &dummy);
730                   dbus_message_iter_close_container (&struc, &variant);
731                 }
732               dbus_message_iter_close_container (&array, &struc);
733             }
734         }
735       dbus_message_iter_close_container (&iter, &array);
736     }
737   return reply;
738 }
739
740 static DBusMessage *
741 impl_getAttributeRun (DBusConnection * bus, DBusMessage * message,
742                       void *user_data)
743 {
744   DBusError error;
745   AtkText *text = get_text (message);
746   dbus_int32_t offset;
747   dbus_bool_t includeDefaults;
748   dbus_int32_t startOffset, endOffset;
749   gint intstart_offset = 0, intend_offset = 0;
750   DBusMessage *reply;
751   AtkAttributeSet *attributes, *default_attributes = NULL;
752   AtkAttribute *attr = NULL;
753   char **retval;
754   gint n_attributes = 0, total_attributes = 0, n_default_attributes = 0;
755   gint i, j;
756
757   if (!text)
758     return spi_dbus_general_error (message);
759   dbus_error_init (&error);
760   if (!dbus_message_get_args
761       (message, &error, DBUS_TYPE_INT32, &offset, DBUS_TYPE_BOOLEAN,
762        &includeDefaults, DBUS_TYPE_INVALID))
763     {
764       return SPI_DBUS_RETURN_ERROR (message, &error);
765     }
766
767   attributes =
768     atk_text_get_run_attributes (text, offset, &intstart_offset,
769                                  &intend_offset);
770
771   if (attributes)
772     total_attributes = n_attributes = g_slist_length (attributes);
773
774   if (includeDefaults)
775     {
776       default_attributes = atk_text_get_default_attributes (text);
777       if (default_attributes)
778         n_default_attributes = g_slist_length (default_attributes);
779       total_attributes += n_default_attributes;
780     }
781
782   startOffset = intstart_offset;
783   endOffset = intend_offset;
784
785   retval = (char **) g_malloc (total_attributes * sizeof (char *));
786
787   if (total_attributes)
788     {
789       for (i = 0; i < n_attributes; ++i)
790         {
791           attr = g_slist_nth_data (attributes, i);
792           retval[i] = g_strconcat (attr->name, ":", attr->value, NULL);
793         }
794
795       for (j = 0; j < n_default_attributes; ++i, ++j)
796         {
797           attr = g_slist_nth_data (default_attributes, j);
798           retval[i] = g_strconcat (attr->name, ":", attr->value, NULL);
799         }
800
801       atk_attribute_set_free (attributes);
802       if (default_attributes)
803         atk_attribute_set_free (default_attributes);
804     }
805   reply = dbus_message_new_method_return (message);
806   if (reply)
807     {
808       dbus_message_append_args (reply, DBUS_TYPE_INT32, &startOffset,
809                                 DBUS_TYPE_INT32, &endOffset, DBUS_TYPE_ARRAY,
810                                 DBUS_TYPE_STRING, &retval, total_attributes,
811                                 DBUS_TYPE_INVALID);
812     }
813   for (i = 0; i < total_attributes; i++)
814     g_free (retval[i]);
815   g_free (retval);
816   return reply;
817 }
818
819 static DBusMessage *
820 impl_getDefaultAttributeSet (DBusConnection * bus, DBusMessage * message,
821                              void *user_data)
822 {
823   AtkText *text = get_text (message);
824   DBusMessage *reply;
825   AtkAttributeSet *attributes;
826   AtkAttribute *attr = NULL;
827   char **retval;
828   gint n_attributes = 0;
829   gint i;
830
831   if (!text)
832     return spi_dbus_general_error (message);
833
834   attributes = atk_text_get_default_attributes (text);
835   if (attributes)
836     n_attributes = g_slist_length (attributes);
837
838   retval = g_new (char *, n_attributes);
839
840   for (i = 0; i < n_attributes; ++i)
841     {
842       attr = g_slist_nth_data (attributes, i);
843       retval[i] = g_strconcat (attr->name, ":", attr->value, NULL);
844     }
845   if (attributes)
846     atk_attribute_set_free (attributes);
847   reply = dbus_message_new_method_return (message);
848   if (reply)
849     {
850       dbus_message_append_args (reply, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
851                                 &retval, n_attributes, DBUS_TYPE_INVALID);
852     }
853   for (i = 0; i < n_attributes; i++)
854     g_free (retval[i]);
855   g_free (retval);
856   return reply;
857 }
858
859 static DRouteMethod methods[] = {
860   {impl_getText, "getText"},
861   {impl_setCaretOffset, "setCaretOffset"},
862   {impl_getTextBeforeOffset, "getTextBeforeOffset"},
863   {impl_getTextAtOffset, "getTextAtOffset"},
864   {impl_getTextAfterOffset, "getTextAfterOffset"},
865   {impl_getCharacterAtOffset, "getCharacterAtOffset"},
866   {impl_getAttributeValue, "getAttributeValue"},
867   {impl_getAttributes, "getAttributes"},
868   {impl_getDefaultAttributes, "getDefaultAttributes"},
869   {impl_getCharacterExtents, "getCharacterExtents"},
870   {impl_getOffsetAtPoint, "getOffsetAtPoint"},
871   {impl_getNSelections, "getNSelections"},
872   {impl_getSelection, "getSelection"},
873   {impl_addSelection, "addSelection"},
874   {impl_removeSelection, "removeSelection"},
875   {impl_setSelection, "setSelection"},
876   {impl_getRangeExtents, "getRangeExtents"},
877   {impl_getBoundedRanges, "getBoundedRanges"},
878   {impl_getAttributeRun, "getAttributeRun"},
879   {impl_getDefaultAttributeSet, "getDefaultAttributeSet"},
880   {NULL, NULL}
881 };
882
883 static DRouteProperty properties[] = {
884   {impl_get_characterCount, NULL, "characterCount"},
885   {impl_get_caretOffset, NULL, "caretOffset"},
886   {NULL, NULL, NULL}
887 };
888
889 void
890 spi_initialize_text (DRouteData * data)
891 {
892   droute_add_interface (data, SPI_DBUS_INTERFACE_TEXT, methods,
893                         properties,
894                         (DRouteGetDatumFunction) get_text_from_path, NULL);
895 };