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