80bb9214ea466bc1a0c7977427764a5899b9d6e0
[platform/core/uifw/at-spi2-atk.git] / atk-adaptor / adaptors / text-adaptor.c
1 /*
2  * AT-SPI - Assistive Technology Service Provider Interface
3  * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
4  *
5  * Copyright 2008 Novell, Inc.
6  * Copyright 2001, 2002 Sun Microsystems Inc.,
7  * Copyright 2001, 2002 Ximian, Inc.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24
25 #include <string.h>
26
27 #include <atk/atk.h>
28 #include <droute/droute.h>
29
30 #include "spi-dbus.h"
31 #include "object.h"
32 #include "introspection.h"
33
34 static dbus_bool_t
35 impl_get_CharacterCount (DBusMessageIter * iter, void *user_data)
36 {
37   AtkText *text = (AtkText *) user_data;
38   g_return_val_if_fail (ATK_IS_TEXT (user_data), FALSE);
39   return droute_return_v_int32 (iter, atk_text_get_character_count (text));
40 }
41
42 static dbus_bool_t
43 impl_get_CaretOffset (DBusMessageIter * iter, void *user_data)
44 {
45   AtkText *text = (AtkText *) user_data;
46   g_return_val_if_fail (ATK_IS_TEXT (user_data), FALSE);
47   return droute_return_v_int32 (iter, atk_text_get_caret_offset (text));
48 }
49
50 static gchar *
51 validate_allocated_string (gchar *str)
52 {
53   if (!str)
54     return g_strdup ("");
55   if (!g_utf8_validate (str, -1, NULL))
56     {
57       g_warning ("atk-bridge: received bad UTF-8 string from a get_text function");
58       g_free (str);
59       return g_strdup ("");
60     }
61   return str;
62 }
63
64 static DBusMessage *
65 impl_GetText (DBusConnection * bus, DBusMessage * message, void *user_data)
66 {
67   AtkText *text = (AtkText *) user_data;
68   dbus_int32_t startOffset, endOffset;
69   gchar *txt;
70   DBusMessage *reply;
71
72   g_return_val_if_fail (ATK_IS_TEXT (user_data),
73                         droute_not_yet_handled_error (message));
74   if (!dbus_message_get_args
75       (message, NULL, DBUS_TYPE_INT32, &startOffset, DBUS_TYPE_INT32,
76        &endOffset, DBUS_TYPE_INVALID))
77     {
78       return droute_invalid_arguments_error (message);
79     }
80   txt = atk_text_get_text (text, startOffset, endOffset);
81   txt = validate_allocated_string (txt);
82   reply = dbus_message_new_method_return (message);
83   if (reply)
84     {
85       dbus_message_append_args (reply, DBUS_TYPE_STRING, &txt,
86                                 DBUS_TYPE_INVALID);
87     }
88   g_free (txt);
89   return reply;
90 }
91
92 static DBusMessage *
93 impl_SetCaretOffset (DBusConnection * bus, DBusMessage * message,
94                      void *user_data)
95 {
96   AtkText *text = (AtkText *) user_data;
97   dbus_int32_t offset;
98   dbus_bool_t rv;
99   DBusMessage *reply;
100
101   g_return_val_if_fail (ATK_IS_TEXT (user_data),
102                         droute_not_yet_handled_error (message));
103   if (!dbus_message_get_args
104       (message, NULL, DBUS_TYPE_INT32, &offset, DBUS_TYPE_INVALID))
105     {
106       return droute_invalid_arguments_error (message);
107     }
108   rv = atk_text_set_caret_offset (text, offset);
109   reply = dbus_message_new_method_return (message);
110   if (reply)
111     {
112       dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv,
113                                 DBUS_TYPE_INVALID);
114     }
115   return reply;
116 }
117
118 static DBusMessage *
119 impl_GetTextBeforeOffset (DBusConnection * bus, DBusMessage * message,
120                           void *user_data)
121 {
122   AtkText *text = (AtkText *) user_data;
123   dbus_int32_t offset;
124   dbus_uint32_t type;
125   gchar *txt;
126   dbus_int32_t startOffset, endOffset;
127   gint intstart_offset = 0, intend_offset = 0;
128   DBusMessage *reply;
129
130   g_return_val_if_fail (ATK_IS_TEXT (user_data),
131                         droute_not_yet_handled_error (message));
132   if (!dbus_message_get_args
133       (message, NULL, DBUS_TYPE_INT32, &offset, DBUS_TYPE_UINT32, &type,
134        DBUS_TYPE_INVALID))
135     {
136       return droute_invalid_arguments_error (message);
137     }
138   txt =
139     atk_text_get_text_before_offset (text, offset, (AtkTextBoundary) type,
140                                      &intstart_offset, &intend_offset);
141   startOffset = intstart_offset;
142   endOffset = intend_offset;
143   txt = validate_allocated_string (txt);
144   reply = dbus_message_new_method_return (message);
145   if (reply)
146     {
147       dbus_message_append_args (reply, DBUS_TYPE_STRING, &txt,
148                                 DBUS_TYPE_INT32, &startOffset,
149                                 DBUS_TYPE_INT32, &endOffset,
150                                 DBUS_TYPE_INVALID);
151     }
152   g_free (txt);
153   return reply;
154 }
155
156 static DBusMessage *
157 impl_GetTextAtOffset (DBusConnection * bus, DBusMessage * message,
158                       void *user_data)
159 {
160   AtkText *text = (AtkText *) user_data;
161   dbus_int32_t offset, type;
162   gchar *txt;
163   dbus_int32_t startOffset, endOffset;
164   gint intstart_offset = 0, intend_offset = 0;
165   DBusMessage *reply;
166
167   g_return_val_if_fail (ATK_IS_TEXT (user_data),
168                         droute_not_yet_handled_error (message));
169   if (!dbus_message_get_args
170       (message, NULL, DBUS_TYPE_INT32, &offset, DBUS_TYPE_UINT32, &type,
171        DBUS_TYPE_INVALID))
172     {
173       return droute_invalid_arguments_error (message);
174     }
175   txt =
176     atk_text_get_text_at_offset (text, offset, (AtkTextBoundary) type,
177                                  &intstart_offset, &intend_offset);
178   startOffset = intstart_offset;
179   endOffset = intend_offset;
180   txt = validate_allocated_string (txt);
181   reply = dbus_message_new_method_return (message);
182   if (reply)
183     {
184       dbus_message_append_args (reply, DBUS_TYPE_STRING, &txt,
185                                 DBUS_TYPE_INT32, &startOffset,
186                                 DBUS_TYPE_INT32, &endOffset,
187                                 DBUS_TYPE_INVALID);
188     }
189   g_free (txt);
190   return reply;
191 }
192
193 static DBusMessage *
194 impl_GetTextAfterOffset (DBusConnection * bus, DBusMessage * message,
195                          void *user_data)
196 {
197   AtkText *text = (AtkText *) user_data;
198   dbus_int32_t offset;
199   dbus_uint32_t type;
200   gchar *txt;
201   dbus_int32_t startOffset, endOffset;
202   gint intstart_offset = 0, intend_offset = 0;
203   DBusMessage *reply;
204
205   g_return_val_if_fail (ATK_IS_TEXT (user_data),
206                         droute_not_yet_handled_error (message));
207   if (!dbus_message_get_args
208       (message, NULL, DBUS_TYPE_INT32, &offset, DBUS_TYPE_UINT32, &type,
209        DBUS_TYPE_INVALID))
210     {
211       return droute_invalid_arguments_error (message);
212     }
213   txt =
214     atk_text_get_text_after_offset (text, offset, (AtkTextBoundary) type,
215                                     &intstart_offset, &intend_offset);
216   startOffset = intstart_offset;
217   endOffset = intend_offset;
218   txt = validate_allocated_string (txt);
219   reply = dbus_message_new_method_return (message);
220   if (reply)
221     {
222       dbus_message_append_args (reply, DBUS_TYPE_STRING, &txt,
223                                 DBUS_TYPE_INT32, &startOffset,
224                                 DBUS_TYPE_INT32, &endOffset,
225                                 DBUS_TYPE_INVALID);
226     }
227   g_free (txt);
228   return reply;
229 }
230
231 static DBusMessage *
232 impl_GetCharacterAtOffset (DBusConnection * bus, DBusMessage * message,
233                            void *user_data)
234 {
235   AtkText *text = (AtkText *) user_data;
236   dbus_int32_t offset;
237   dbus_int32_t ch;
238   DBusMessage *reply;
239
240   g_return_val_if_fail (ATK_IS_TEXT (user_data),
241                         droute_not_yet_handled_error (message));
242   if (!dbus_message_get_args
243       (message, NULL, DBUS_TYPE_INT32, &offset, DBUS_TYPE_INVALID))
244     {
245       return droute_invalid_arguments_error (message);
246     }
247   ch = atk_text_get_character_at_offset (text, offset);
248   reply = dbus_message_new_method_return (message);
249   if (reply)
250     {
251       dbus_message_append_args (reply, DBUS_TYPE_INT32, &ch,
252                                 DBUS_TYPE_INVALID);
253     }
254   return reply;
255 }
256
257 static gchar *
258 get_text_for_legacy_implementations(AtkText *text,
259                                     gint offset,
260                                     AtkTextGranularity granularity,
261                                     gint *start_offset,
262                                     gint *end_offset)
263 {
264   gchar *txt = 0;
265   AtkTextBoundary boundary = 0;
266   switch (granularity) {
267   case ATK_TEXT_GRANULARITY_CHAR:
268     boundary = ATK_TEXT_BOUNDARY_CHAR;
269     break;
270
271   case ATK_TEXT_GRANULARITY_WORD:
272     boundary = ATK_TEXT_BOUNDARY_WORD_START;
273     break;
274
275   case ATK_TEXT_GRANULARITY_SENTENCE:
276     boundary = ATK_TEXT_BOUNDARY_SENTENCE_START;
277     break;
278
279   case ATK_TEXT_GRANULARITY_LINE:
280     boundary = ATK_TEXT_BOUNDARY_LINE_START;
281     break;
282
283   case ATK_TEXT_GRANULARITY_PARAGRAPH:
284     /* This is not implemented in previous versions of ATK */
285     txt = g_strdup("");
286     break;
287
288   default:
289     g_assert_not_reached();
290   }
291
292   if (!txt)
293     {
294       txt =
295         atk_text_get_text_at_offset (text, offset, boundary,
296                                      start_offset, end_offset);
297     }
298
299   return txt;
300 }
301
302 static DBusMessage *
303 impl_GetStringAtOffset (DBusConnection * bus, DBusMessage * message,
304                         void *user_data)
305 {
306   AtkText *text = (AtkText *) user_data;
307   dbus_int32_t offset;
308   dbus_uint32_t granularity;
309   gchar *txt = 0;
310   dbus_int32_t startOffset, endOffset;
311   gint intstart_offset = 0, intend_offset = 0;
312   DBusMessage *reply;
313
314   g_return_val_if_fail (ATK_IS_TEXT (user_data),
315                         droute_not_yet_handled_error (message));
316   if (!dbus_message_get_args
317       (message, NULL, DBUS_TYPE_INT32, &offset, DBUS_TYPE_UINT32, &granularity,
318        DBUS_TYPE_INVALID))
319     {
320       return droute_invalid_arguments_error (message);
321     }
322
323   txt =
324     atk_text_get_string_at_offset (text, offset, (AtkTextGranularity) granularity,
325                                    &intstart_offset, &intend_offset);
326
327   /* Accessibility layers implementing an older version of ATK (even if
328    * a new enough version of libatk is installed) might return NULL due
329    * not to provide an implementation for get_string_at_offset(), so we
330    * try with the legacy implementation if that's the case. */
331   if (!txt)
332     txt = get_text_for_legacy_implementations(text, offset,
333                                               (AtkTextGranularity) granularity,
334                                               &intstart_offset, &intend_offset);
335
336   startOffset = intstart_offset;
337   endOffset = intend_offset;
338   txt = validate_allocated_string (txt);
339   reply = dbus_message_new_method_return (message);
340   if (reply)
341     {
342       dbus_message_append_args (reply, DBUS_TYPE_STRING, &txt,
343                                 DBUS_TYPE_INT32, &startOffset,
344                                 DBUS_TYPE_INT32, &endOffset,
345                                 DBUS_TYPE_INVALID);
346     }
347   g_free (txt);
348   return reply;
349 }
350
351 static DBusMessage *
352 impl_GetAttributeValue (DBusConnection * bus, DBusMessage * message,
353                         void *user_data)
354 {
355   AtkText *text = (AtkText *) user_data;
356   dbus_int32_t offset;
357   char *attributeName;
358   dbus_int32_t startOffset, endOffset;
359   dbus_bool_t defined;
360   gint intstart_offset = 0, intend_offset = 0;
361   char *rv = NULL;
362   DBusMessage *reply;
363   AtkAttributeSet *set;
364   GSList *cur_attr;
365   AtkAttribute *at;
366
367   g_return_val_if_fail (ATK_IS_TEXT (user_data),
368                         droute_not_yet_handled_error (message));
369   if (!dbus_message_get_args
370       (message, NULL, DBUS_TYPE_INT32, &offset, DBUS_TYPE_STRING,
371        &attributeName, DBUS_TYPE_INVALID))
372     {
373       return droute_invalid_arguments_error (message);
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   defined = FALSE;
381   cur_attr = (GSList *) set;
382   while (cur_attr)
383     {
384       at = (AtkAttribute *) cur_attr->data;
385       if (!strcmp (at->name, attributeName))
386         {
387           rv = at->value;
388           defined = TRUE;
389           break;
390         }
391       cur_attr = cur_attr->next;
392     }
393   if (!rv)
394     rv = "";
395   reply = dbus_message_new_method_return (message);
396   if (reply)
397     {
398       dbus_message_append_args (reply, DBUS_TYPE_STRING, &rv, DBUS_TYPE_INT32,
399                                 &startOffset, DBUS_TYPE_INT32, &endOffset,
400                                 DBUS_TYPE_BOOLEAN, &defined,
401                                 DBUS_TYPE_INVALID);
402     }
403   atk_attribute_set_free (set);
404   return reply;
405 }
406
407 static DBusMessage *
408 impl_GetAttributes (DBusConnection * bus, DBusMessage * message,
409                     void *user_data)
410 {
411   AtkText *text = (AtkText *) user_data;
412   dbus_int32_t offset;
413   dbus_int32_t startOffset, endOffset;
414   gint intstart_offset, intend_offset;
415   DBusMessage *reply;
416   AtkAttributeSet *set;
417   DBusMessageIter iter;
418
419   g_return_val_if_fail (ATK_IS_TEXT (user_data),
420                         droute_not_yet_handled_error (message));
421   if (!dbus_message_get_args
422       (message, NULL, DBUS_TYPE_INT32, &offset, DBUS_TYPE_INVALID))
423     {
424       return droute_invalid_arguments_error (message);
425     }
426
427   set = atk_text_get_run_attributes (text, offset,
428                                      &intstart_offset, &intend_offset);
429
430   startOffset = intstart_offset;
431   endOffset = intend_offset;
432   reply = dbus_message_new_method_return (message);
433   if (reply)
434     {
435       dbus_message_iter_init_append (reply, &iter);
436       spi_object_append_attribute_set (&iter, set);
437       dbus_message_append_args (reply, DBUS_TYPE_INT32, &startOffset,
438                                 DBUS_TYPE_INT32, &endOffset,
439                                 DBUS_TYPE_INVALID);
440     }
441   atk_attribute_set_free (set);
442   return reply;
443 }
444
445 static DBusMessage *
446 impl_GetDefaultAttributes (DBusConnection * bus, DBusMessage * message,
447                            void *user_data)
448 {
449   AtkText *text = (AtkText *) user_data;
450   DBusMessage *reply;
451   AtkAttributeSet *set;
452   DBusMessageIter iter;
453
454   g_return_val_if_fail (ATK_IS_TEXT (user_data),
455                         droute_not_yet_handled_error (message));
456
457   set = atk_text_get_default_attributes (text);
458   reply = dbus_message_new_method_return (message);
459   if (reply)
460     {
461       dbus_message_iter_init_append (reply, &iter);
462       spi_object_append_attribute_set (&iter, set);
463     }
464   atk_attribute_set_free (set);
465   return reply;
466 }
467
468 static DBusMessage *
469 impl_GetCharacterExtents (DBusConnection * bus, DBusMessage * message,
470                           void *user_data)
471 {
472   AtkText *text = (AtkText *) user_data;
473   dbus_int32_t offset;
474   dbus_uint32_t coordType;
475   dbus_int32_t x, y, width, height;
476   gint ix = 0, iy = 0, iw = 0, ih = 0;
477   DBusMessage *reply;
478
479   g_return_val_if_fail (ATK_IS_TEXT (user_data),
480                         droute_not_yet_handled_error (message));
481   if (!dbus_message_get_args
482       (message, NULL, DBUS_TYPE_INT32, &offset, DBUS_TYPE_UINT32,
483        &coordType, DBUS_TYPE_INVALID))
484     {
485       return droute_invalid_arguments_error (message);
486     }
487   atk_text_get_character_extents (text, offset, &ix, &iy, &iw, &ih,
488                                   (AtkCoordType) coordType);
489   x = ix;
490   y = iy;
491   width = iw;
492   height = ih;
493   reply = dbus_message_new_method_return (message);
494   if (reply)
495     {
496       dbus_message_append_args (reply, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32,
497                                 &y, DBUS_TYPE_INT32, &width, DBUS_TYPE_INT32,
498                                 &height, DBUS_TYPE_INVALID);
499     }
500   return reply;
501 }
502
503 static DBusMessage *
504 impl_GetOffsetAtPoint (DBusConnection * bus, DBusMessage * message,
505                        void *user_data)
506 {
507   AtkText *text = (AtkText *) user_data;
508   dbus_int32_t x, y;
509   dbus_uint32_t coordType;
510   dbus_int32_t rv;
511   DBusMessage *reply;
512
513   g_return_val_if_fail (ATK_IS_TEXT (user_data),
514                         droute_not_yet_handled_error (message));
515   if (!dbus_message_get_args
516       (message, NULL, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, &y,
517        DBUS_TYPE_UINT32, &coordType, DBUS_TYPE_INVALID))
518     {
519       return droute_invalid_arguments_error (message);
520     }
521   rv = atk_text_get_offset_at_point (text, x, y, coordType);
522   reply = dbus_message_new_method_return (message);
523   if (reply)
524     {
525       dbus_message_append_args (reply, DBUS_TYPE_INT32, &rv,
526                                 DBUS_TYPE_INVALID);
527     }
528   return reply;
529 }
530
531 static DBusMessage *
532 impl_GetNSelections (DBusConnection * bus, DBusMessage * message,
533                      void *user_data)
534 {
535   AtkText *text = (AtkText *) user_data;
536   dbus_int32_t rv;
537   DBusMessage *reply;
538
539   g_return_val_if_fail (ATK_IS_TEXT (user_data),
540                         droute_not_yet_handled_error (message));
541   rv = atk_text_get_n_selections (text);
542   reply = dbus_message_new_method_return (message);
543   if (reply)
544     {
545       dbus_message_append_args (reply, DBUS_TYPE_INT32, &rv,
546                                 DBUS_TYPE_INVALID);
547     }
548   return reply;
549 }
550
551 static DBusMessage *
552 impl_GetSelection (DBusConnection * bus, DBusMessage * message,
553                    void *user_data)
554 {
555   AtkText *text = (AtkText *) user_data;
556   dbus_int32_t selectionNum;
557   dbus_int32_t startOffset, endOffset;
558   gint intstart_offset = 0, intend_offset = 0;
559   DBusMessage *reply;
560
561   g_return_val_if_fail (ATK_IS_TEXT (user_data),
562                         droute_not_yet_handled_error (message));
563   if (!dbus_message_get_args
564       (message, NULL, DBUS_TYPE_INT32, &selectionNum, DBUS_TYPE_INVALID))
565     {
566       return droute_invalid_arguments_error (message);
567     }
568   /* atk_text_get_selection returns gchar * which we discard */
569   g_free (atk_text_get_selection
570           (text, selectionNum, &intstart_offset, &intend_offset));
571   startOffset = intstart_offset;
572   endOffset = intend_offset;
573   reply = dbus_message_new_method_return (message);
574   if (reply)
575     {
576       dbus_message_append_args (reply, DBUS_TYPE_INT32, &startOffset,
577                                 DBUS_TYPE_INT32, &endOffset,
578                                 DBUS_TYPE_INVALID);
579     }
580   return reply;
581 }
582
583 static DBusMessage *
584 impl_AddSelection (DBusConnection * bus, DBusMessage * message,
585                    void *user_data)
586 {
587   AtkText *text = (AtkText *) user_data;
588   dbus_int32_t startOffset, endOffset;
589   dbus_bool_t rv;
590   DBusMessage *reply;
591
592   g_return_val_if_fail (ATK_IS_TEXT (user_data),
593                         droute_not_yet_handled_error (message));
594   if (!dbus_message_get_args
595       (message, NULL, DBUS_TYPE_INT32, &startOffset, DBUS_TYPE_INT32,
596        &endOffset, DBUS_TYPE_INVALID))
597     {
598       return droute_invalid_arguments_error (message);
599     }
600   rv = atk_text_add_selection (text, startOffset, endOffset);
601   reply = dbus_message_new_method_return (message);
602   if (reply)
603     {
604       dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv,
605                                 DBUS_TYPE_INVALID);
606     }
607   return reply;
608 }
609
610 static DBusMessage *
611 impl_RemoveSelection (DBusConnection * bus, DBusMessage * message,
612                       void *user_data)
613 {
614   AtkText *text = (AtkText *) user_data;
615   dbus_int32_t selectionNum;
616   dbus_bool_t rv;
617   DBusMessage *reply;
618
619   g_return_val_if_fail (ATK_IS_TEXT (user_data),
620                         droute_not_yet_handled_error (message));
621   if (!dbus_message_get_args
622       (message, NULL, DBUS_TYPE_INT32, &selectionNum, DBUS_TYPE_INVALID))
623     {
624       return droute_invalid_arguments_error (message);
625     }
626   rv = atk_text_remove_selection (text, selectionNum);
627   reply = dbus_message_new_method_return (message);
628   if (reply)
629     {
630       dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv,
631                                 DBUS_TYPE_INVALID);
632     }
633   return reply;
634 }
635
636 static DBusMessage *
637 impl_SetSelection (DBusConnection * bus, DBusMessage * message,
638                    void *user_data)
639 {
640   AtkText *text = (AtkText *) user_data;
641   dbus_int32_t selectionNum, startOffset, endOffset;
642   dbus_bool_t rv;
643   DBusMessage *reply;
644
645   g_return_val_if_fail (ATK_IS_TEXT (user_data),
646                         droute_not_yet_handled_error (message));
647   if (!dbus_message_get_args
648       (message, NULL, DBUS_TYPE_INT32, &selectionNum, DBUS_TYPE_INT32,
649        &startOffset, DBUS_TYPE_INT32, &endOffset, DBUS_TYPE_INVALID))
650     {
651       return droute_invalid_arguments_error (message);
652     }
653   rv = atk_text_set_selection (text, selectionNum, startOffset, endOffset);
654   reply = dbus_message_new_method_return (message);
655   if (reply)
656     {
657       dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv,
658                                 DBUS_TYPE_INVALID);
659     }
660   return reply;
661 }
662
663 static DBusMessage *
664 impl_GetRangeExtents (DBusConnection * bus, DBusMessage * message,
665                       void *user_data)
666 {
667   AtkText *text = (AtkText *) user_data;
668   dbus_int32_t startOffset, endOffset;
669   dbus_uint32_t coordType;
670   AtkTextRectangle rect;
671   dbus_int32_t x, y, width, height;
672   DBusMessage *reply;
673
674   g_return_val_if_fail (ATK_IS_TEXT (user_data),
675                         droute_not_yet_handled_error (message));
676   if (!dbus_message_get_args
677       (message, NULL, DBUS_TYPE_INT32, &startOffset, DBUS_TYPE_INT32,
678        &endOffset, DBUS_TYPE_UINT32, &coordType, DBUS_TYPE_INVALID))
679     {
680       return droute_invalid_arguments_error (message);
681     }
682   memset (&rect, 0, sizeof (rect));
683   atk_text_get_range_extents (text, startOffset, endOffset,
684                               (AtkCoordType) coordType, &rect);
685   x = rect.x;
686   y = rect.y;
687   width = rect.width;
688   height = rect.height;
689   reply = dbus_message_new_method_return (message);
690   if (reply)
691     {
692       dbus_message_append_args (reply, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32,
693                                 &y, DBUS_TYPE_INT32, &width, DBUS_TYPE_INT32,
694                                 &height, DBUS_TYPE_INVALID);
695     }
696   return reply;
697 }
698
699 #define MAXRANGELEN 512
700
701 static DBusMessage *
702 impl_GetBoundedRanges (DBusConnection * bus, DBusMessage * message,
703                        void *user_data)
704 {
705   AtkText *text = (AtkText *) user_data;
706   dbus_int32_t x, y, width, height;
707   dbus_uint32_t coordType, xClipType, yClipType;
708   AtkTextRange **range_list = NULL;
709   AtkTextRectangle rect;
710   DBusMessage *reply;
711   DBusMessageIter iter, array, struc, variant;
712
713   g_return_val_if_fail (ATK_IS_TEXT (user_data),
714                         droute_not_yet_handled_error (message));
715   if (!dbus_message_get_args
716       (message, NULL, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, &y,
717        DBUS_TYPE_INT32, &height, DBUS_TYPE_INT32, &width, DBUS_TYPE_UINT32,
718        &coordType, DBUS_TYPE_UINT32, &xClipType, DBUS_TYPE_UINT32, &yClipType,
719        DBUS_TYPE_INVALID))
720     {
721       return droute_invalid_arguments_error (message);
722     }
723   rect.x = x;
724   rect.y = y;
725   rect.width = width;
726   rect.height = height;
727
728   range_list =
729     atk_text_get_bounded_ranges (text, &rect, (AtkCoordType) coordType,
730                                  (AtkTextClipType) xClipType,
731                                  (AtkTextClipType) yClipType);
732   reply = dbus_message_new_method_return (message);
733   if (!reply)
734     return NULL;
735   /* This isn't pleasant. */
736   dbus_message_iter_init_append (reply, &iter);
737   if (dbus_message_iter_open_container
738       (&iter, DBUS_TYPE_ARRAY, "(iisv)", &array))
739     {
740       int len;
741       int count = (range_list ? MAXRANGELEN : 0);
742       for (len = 0; len < count && range_list[len]; ++len)
743         {
744           if (dbus_message_iter_open_container
745               (&array, DBUS_TYPE_STRUCT, NULL, &struc))
746             {
747               dbus_int32_t val;
748               val = range_list[len]->start_offset;
749               dbus_message_iter_append_basic (&struc, DBUS_TYPE_INT32, &val);
750               val = range_list[len]->end_offset;
751               dbus_message_iter_append_basic (&struc, DBUS_TYPE_INT32, &val);
752               dbus_message_iter_append_basic (&struc, DBUS_TYPE_STRING,
753                                               &range_list[len]->content);
754               /* The variant is unimplemented in atk, but I don't want to
755                * unilaterally muck with the spec and remove it, so I'll just
756                * throw in a dummy value */
757               if (dbus_message_iter_open_container
758                   (&struc, DBUS_TYPE_VARIANT, "i", &variant))
759                 {
760                   dbus_uint32_t dummy = 0;
761                   dbus_message_iter_append_basic (&variant, DBUS_TYPE_INT32,
762                                                   &dummy);
763                   dbus_message_iter_close_container (&struc, &variant);
764                 }
765               dbus_message_iter_close_container (&array, &struc);
766               g_free (range_list[len]->content);
767               g_free (range_list[len]);
768             }
769         }
770       dbus_message_iter_close_container (&iter, &array);
771     }
772
773   if (range_list)
774     g_free (range_list);
775
776   return reply;
777 }
778
779 static DBusMessage *
780 impl_GetAttributeRun (DBusConnection * bus, DBusMessage * message,
781                       void *user_data)
782 {
783   AtkText *text = (AtkText *) user_data;
784   dbus_int32_t offset;
785   dbus_bool_t includeDefaults;
786   dbus_int32_t startOffset, endOffset;
787   gint intstart_offset = 0, intend_offset = 0;
788   DBusMessage *reply;
789   AtkAttributeSet *attributes = NULL;
790   DBusMessageIter iter;
791
792   g_return_val_if_fail (ATK_IS_TEXT (user_data),
793                         droute_not_yet_handled_error (message));
794   if (!dbus_message_get_args
795       (message, NULL, DBUS_TYPE_INT32, &offset, DBUS_TYPE_BOOLEAN,
796        &includeDefaults, DBUS_TYPE_INVALID))
797     {
798       return droute_invalid_arguments_error (message);
799     }
800
801   attributes =
802     atk_text_get_run_attributes (text, offset, &intstart_offset,
803                                  &intend_offset);
804
805   if (includeDefaults)
806     {
807       attributes = g_slist_concat (attributes,
808                                    atk_text_get_default_attributes (text));
809     }
810
811   reply = dbus_message_new_method_return (message);
812   if (!reply)
813     return NULL;
814
815   dbus_message_iter_init_append (reply, &iter);
816   spi_object_append_attribute_set (&iter, attributes);
817
818   startOffset = intstart_offset;
819   endOffset = intend_offset;
820   dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &startOffset);
821   dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &endOffset);
822
823   atk_attribute_set_free (attributes);
824
825   return reply;
826 }
827
828 static DBusMessage *
829 impl_GetDefaultAttributeSet (DBusConnection * bus, DBusMessage * message,
830                              void *user_data)
831 {
832   AtkText *text = (AtkText *) user_data;
833   DBusMessage *reply;
834   DBusMessageIter iter;
835   AtkAttributeSet *attributes;
836
837   g_return_val_if_fail (ATK_IS_TEXT (user_data),
838                         droute_not_yet_handled_error (message));
839
840   attributes = atk_text_get_default_attributes (text);
841
842   reply = dbus_message_new_method_return (message);
843   if (reply)
844     {
845       dbus_message_iter_init_append (reply, &iter);
846       spi_object_append_attribute_set (&iter, attributes);
847     }
848
849   if (attributes)
850     atk_attribute_set_free (attributes);
851
852   return reply;
853 }
854
855 static DRouteMethod methods[] = {
856   {impl_GetText, "GetText"},
857   {impl_SetCaretOffset, "SetCaretOffset"},
858   {impl_GetTextBeforeOffset, "GetTextBeforeOffset"},
859   {impl_GetTextAtOffset, "GetTextAtOffset"},
860   {impl_GetTextAfterOffset, "GetTextAfterOffset"},
861   {impl_GetStringAtOffset, "GetStringAtOffset"},
862   {impl_GetCharacterAtOffset, "GetCharacterAtOffset"},
863   {impl_GetAttributeValue, "GetAttributeValue"},
864   {impl_GetAttributes, "GetAttributes"},
865   {impl_GetDefaultAttributes, "GetDefaultAttributes"},
866   {impl_GetCharacterExtents, "GetCharacterExtents"},
867   {impl_GetOffsetAtPoint, "GetOffsetAtPoint"},
868   {impl_GetNSelections, "GetNSelections"},
869   {impl_GetSelection, "GetSelection"},
870   {impl_AddSelection, "AddSelection"},
871   {impl_RemoveSelection, "RemoveSelection"},
872   {impl_SetSelection, "SetSelection"},
873   {impl_GetRangeExtents, "GetRangeExtents"},
874   {impl_GetBoundedRanges, "GetBoundedRanges"},
875   {impl_GetAttributeRun, "GetAttributeRun"},
876   {impl_GetDefaultAttributeSet, "GetDefaultAttributeSet"},
877   {NULL, NULL}
878 };
879
880 static DRouteProperty properties[] = {
881   {impl_get_CharacterCount, NULL, "CharacterCount"},
882   {impl_get_CaretOffset, NULL, "CaretOffset"},
883   {NULL, NULL, NULL}
884 };
885
886 void
887 spi_initialize_text (DRoutePath * path)
888 {
889   droute_path_add_interface (path,
890                              ATSPI_DBUS_INTERFACE_TEXT, spi_org_a11y_atspi_Text, methods, properties);
891 };