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