2.38.0
[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 Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 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  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  * Boston, MA 02110-1301, 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   gint intstart_offset = 0, intend_offset = 0;
360   char *rv = NULL;
361   DBusMessage *reply;
362   AtkAttributeSet *set;
363   GSList *cur_attr;
364   AtkAttribute *at;
365
366   g_return_val_if_fail (ATK_IS_TEXT (user_data),
367                         droute_not_yet_handled_error (message));
368   if (!dbus_message_get_args
369       (message, NULL, DBUS_TYPE_INT32, &offset, DBUS_TYPE_STRING,
370        &attributeName, DBUS_TYPE_INVALID))
371     {
372       return droute_invalid_arguments_error (message);
373     }
374
375   set = atk_text_get_run_attributes (text, offset,
376                                      &intstart_offset, &intend_offset);
377   cur_attr = (GSList *) set;
378   while (cur_attr)
379     {
380       at = (AtkAttribute *) cur_attr->data;
381       if (!strcmp (at->name, attributeName))
382         {
383           rv = at->value;
384           break;
385         }
386       cur_attr = cur_attr->next;
387     }
388   if (!rv)
389     rv = "";
390   reply = dbus_message_new_method_return (message);
391   if (reply)
392     {
393       dbus_message_append_args (reply, DBUS_TYPE_STRING, &rv, DBUS_TYPE_INVALID);
394     }
395   atk_attribute_set_free (set);
396   return reply;
397 }
398
399 static DBusMessage *
400 impl_GetAttributes (DBusConnection * bus, DBusMessage * message,
401                     void *user_data)
402 {
403   AtkText *text = (AtkText *) user_data;
404   dbus_int32_t offset;
405   dbus_int32_t startOffset, endOffset;
406   gint intstart_offset, intend_offset;
407   DBusMessage *reply;
408   AtkAttributeSet *set;
409   DBusMessageIter iter;
410
411   g_return_val_if_fail (ATK_IS_TEXT (user_data),
412                         droute_not_yet_handled_error (message));
413   if (!dbus_message_get_args
414       (message, NULL, DBUS_TYPE_INT32, &offset, DBUS_TYPE_INVALID))
415     {
416       return droute_invalid_arguments_error (message);
417     }
418
419   set = atk_text_get_run_attributes (text, offset,
420                                      &intstart_offset, &intend_offset);
421
422   startOffset = intstart_offset;
423   endOffset = intend_offset;
424   reply = dbus_message_new_method_return (message);
425   if (reply)
426     {
427       dbus_message_iter_init_append (reply, &iter);
428       spi_object_append_attribute_set (&iter, set);
429       dbus_message_append_args (reply, DBUS_TYPE_INT32, &startOffset,
430                                 DBUS_TYPE_INT32, &endOffset,
431                                 DBUS_TYPE_INVALID);
432     }
433   atk_attribute_set_free (set);
434   return reply;
435 }
436
437 static DBusMessage *
438 impl_GetDefaultAttributes (DBusConnection * bus, DBusMessage * message,
439                            void *user_data)
440 {
441   AtkText *text = (AtkText *) user_data;
442   DBusMessage *reply;
443   AtkAttributeSet *set;
444   DBusMessageIter iter;
445
446   g_return_val_if_fail (ATK_IS_TEXT (user_data),
447                         droute_not_yet_handled_error (message));
448
449   set = atk_text_get_default_attributes (text);
450   reply = dbus_message_new_method_return (message);
451   if (reply)
452     {
453       dbus_message_iter_init_append (reply, &iter);
454       spi_object_append_attribute_set (&iter, set);
455     }
456   atk_attribute_set_free (set);
457   return reply;
458 }
459
460 static DBusMessage *
461 impl_GetCharacterExtents (DBusConnection * bus, DBusMessage * message,
462                           void *user_data)
463 {
464   AtkText *text = (AtkText *) user_data;
465   dbus_int32_t offset;
466   dbus_uint32_t coordType;
467   dbus_int32_t x, y, width, height;
468   gint ix = 0, iy = 0, iw = 0, ih = 0;
469   DBusMessage *reply;
470
471   g_return_val_if_fail (ATK_IS_TEXT (user_data),
472                         droute_not_yet_handled_error (message));
473   if (!dbus_message_get_args
474       (message, NULL, DBUS_TYPE_INT32, &offset, DBUS_TYPE_UINT32,
475        &coordType, DBUS_TYPE_INVALID))
476     {
477       return droute_invalid_arguments_error (message);
478     }
479   atk_text_get_character_extents (text, offset, &ix, &iy, &iw, &ih,
480                                   (AtkCoordType) coordType);
481   x = ix;
482   y = iy;
483   width = iw;
484   height = ih;
485   reply = dbus_message_new_method_return (message);
486   if (reply)
487     {
488       dbus_message_append_args (reply, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32,
489                                 &y, DBUS_TYPE_INT32, &width, DBUS_TYPE_INT32,
490                                 &height, DBUS_TYPE_INVALID);
491     }
492   return reply;
493 }
494
495 static DBusMessage *
496 impl_GetOffsetAtPoint (DBusConnection * bus, DBusMessage * message,
497                        void *user_data)
498 {
499   AtkText *text = (AtkText *) user_data;
500   dbus_int32_t x, y;
501   dbus_uint32_t coordType;
502   dbus_int32_t rv;
503   DBusMessage *reply;
504
505   g_return_val_if_fail (ATK_IS_TEXT (user_data),
506                         droute_not_yet_handled_error (message));
507   if (!dbus_message_get_args
508       (message, NULL, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, &y,
509        DBUS_TYPE_UINT32, &coordType, DBUS_TYPE_INVALID))
510     {
511       return droute_invalid_arguments_error (message);
512     }
513   rv = atk_text_get_offset_at_point (text, x, y, coordType);
514   reply = dbus_message_new_method_return (message);
515   if (reply)
516     {
517       dbus_message_append_args (reply, DBUS_TYPE_INT32, &rv,
518                                 DBUS_TYPE_INVALID);
519     }
520   return reply;
521 }
522
523 static DBusMessage *
524 impl_GetNSelections (DBusConnection * bus, DBusMessage * message,
525                      void *user_data)
526 {
527   AtkText *text = (AtkText *) user_data;
528   dbus_int32_t rv;
529   DBusMessage *reply;
530
531   g_return_val_if_fail (ATK_IS_TEXT (user_data),
532                         droute_not_yet_handled_error (message));
533   rv = atk_text_get_n_selections (text);
534   reply = dbus_message_new_method_return (message);
535   if (reply)
536     {
537       dbus_message_append_args (reply, DBUS_TYPE_INT32, &rv,
538                                 DBUS_TYPE_INVALID);
539     }
540   return reply;
541 }
542
543 static DBusMessage *
544 impl_GetSelection (DBusConnection * bus, DBusMessage * message,
545                    void *user_data)
546 {
547   AtkText *text = (AtkText *) user_data;
548   dbus_int32_t selectionNum;
549   dbus_int32_t startOffset, endOffset;
550   gint intstart_offset = 0, intend_offset = 0;
551   DBusMessage *reply;
552
553   g_return_val_if_fail (ATK_IS_TEXT (user_data),
554                         droute_not_yet_handled_error (message));
555   if (!dbus_message_get_args
556       (message, NULL, DBUS_TYPE_INT32, &selectionNum, DBUS_TYPE_INVALID))
557     {
558       return droute_invalid_arguments_error (message);
559     }
560   /* atk_text_get_selection returns gchar * which we discard */
561   g_free (atk_text_get_selection
562           (text, selectionNum, &intstart_offset, &intend_offset));
563   startOffset = intstart_offset;
564   endOffset = intend_offset;
565   reply = dbus_message_new_method_return (message);
566   if (reply)
567     {
568       dbus_message_append_args (reply, DBUS_TYPE_INT32, &startOffset,
569                                 DBUS_TYPE_INT32, &endOffset,
570                                 DBUS_TYPE_INVALID);
571     }
572   return reply;
573 }
574
575 static DBusMessage *
576 impl_AddSelection (DBusConnection * bus, DBusMessage * message,
577                    void *user_data)
578 {
579   AtkText *text = (AtkText *) user_data;
580   dbus_int32_t startOffset, endOffset;
581   dbus_bool_t rv;
582   DBusMessage *reply;
583
584   g_return_val_if_fail (ATK_IS_TEXT (user_data),
585                         droute_not_yet_handled_error (message));
586   if (!dbus_message_get_args
587       (message, NULL, DBUS_TYPE_INT32, &startOffset, DBUS_TYPE_INT32,
588        &endOffset, DBUS_TYPE_INVALID))
589     {
590       return droute_invalid_arguments_error (message);
591     }
592   rv = atk_text_add_selection (text, startOffset, endOffset);
593   reply = dbus_message_new_method_return (message);
594   if (reply)
595     {
596       dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv,
597                                 DBUS_TYPE_INVALID);
598     }
599   return reply;
600 }
601
602 static DBusMessage *
603 impl_RemoveSelection (DBusConnection * bus, DBusMessage * message,
604                       void *user_data)
605 {
606   AtkText *text = (AtkText *) user_data;
607   dbus_int32_t selectionNum;
608   dbus_bool_t rv;
609   DBusMessage *reply;
610
611   g_return_val_if_fail (ATK_IS_TEXT (user_data),
612                         droute_not_yet_handled_error (message));
613   if (!dbus_message_get_args
614       (message, NULL, DBUS_TYPE_INT32, &selectionNum, DBUS_TYPE_INVALID))
615     {
616       return droute_invalid_arguments_error (message);
617     }
618   rv = atk_text_remove_selection (text, selectionNum);
619   reply = dbus_message_new_method_return (message);
620   if (reply)
621     {
622       dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv,
623                                 DBUS_TYPE_INVALID);
624     }
625   return reply;
626 }
627
628 static DBusMessage *
629 impl_SetSelection (DBusConnection * bus, DBusMessage * message,
630                    void *user_data)
631 {
632   AtkText *text = (AtkText *) user_data;
633   dbus_int32_t selectionNum, startOffset, endOffset;
634   dbus_bool_t rv;
635   DBusMessage *reply;
636
637   g_return_val_if_fail (ATK_IS_TEXT (user_data),
638                         droute_not_yet_handled_error (message));
639   if (!dbus_message_get_args
640       (message, NULL, DBUS_TYPE_INT32, &selectionNum, DBUS_TYPE_INT32,
641        &startOffset, DBUS_TYPE_INT32, &endOffset, DBUS_TYPE_INVALID))
642     {
643       return droute_invalid_arguments_error (message);
644     }
645   rv = atk_text_set_selection (text, selectionNum, startOffset, endOffset);
646   reply = dbus_message_new_method_return (message);
647   if (reply)
648     {
649       dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv,
650                                 DBUS_TYPE_INVALID);
651     }
652   return reply;
653 }
654
655 static DBusMessage *
656 impl_GetRangeExtents (DBusConnection * bus, DBusMessage * message,
657                       void *user_data)
658 {
659   AtkText *text = (AtkText *) user_data;
660   dbus_int32_t startOffset, endOffset;
661   dbus_uint32_t coordType;
662   AtkTextRectangle rect;
663   dbus_int32_t x, y, width, height;
664   DBusMessage *reply;
665
666   g_return_val_if_fail (ATK_IS_TEXT (user_data),
667                         droute_not_yet_handled_error (message));
668   if (!dbus_message_get_args
669       (message, NULL, DBUS_TYPE_INT32, &startOffset, DBUS_TYPE_INT32,
670        &endOffset, DBUS_TYPE_UINT32, &coordType, DBUS_TYPE_INVALID))
671     {
672       return droute_invalid_arguments_error (message);
673     }
674   memset (&rect, 0, sizeof (rect));
675   atk_text_get_range_extents (text, startOffset, endOffset,
676                               (AtkCoordType) coordType, &rect);
677   x = rect.x;
678   y = rect.y;
679   width = rect.width;
680   height = rect.height;
681   reply = dbus_message_new_method_return (message);
682   if (reply)
683     {
684       dbus_message_append_args (reply, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32,
685                                 &y, DBUS_TYPE_INT32, &width, DBUS_TYPE_INT32,
686                                 &height, DBUS_TYPE_INVALID);
687     }
688   return reply;
689 }
690
691 #define MAXRANGELEN 512
692
693 static DBusMessage *
694 impl_GetBoundedRanges (DBusConnection * bus, DBusMessage * message,
695                        void *user_data)
696 {
697   AtkText *text = (AtkText *) user_data;
698   dbus_int32_t x, y, width, height;
699   dbus_uint32_t coordType, xClipType, yClipType;
700   AtkTextRange **range_list = NULL;
701   AtkTextRectangle rect;
702   DBusMessage *reply;
703   DBusMessageIter iter, array, struc, variant;
704
705   g_return_val_if_fail (ATK_IS_TEXT (user_data),
706                         droute_not_yet_handled_error (message));
707   if (!dbus_message_get_args
708       (message, NULL, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, &y,
709        DBUS_TYPE_INT32, &height, DBUS_TYPE_INT32, &width, DBUS_TYPE_UINT32,
710        &coordType, DBUS_TYPE_UINT32, &xClipType, DBUS_TYPE_UINT32, &yClipType,
711        DBUS_TYPE_INVALID))
712     {
713       return droute_invalid_arguments_error (message);
714     }
715   rect.x = x;
716   rect.y = y;
717   rect.width = width;
718   rect.height = height;
719
720   range_list =
721     atk_text_get_bounded_ranges (text, &rect, (AtkCoordType) coordType,
722                                  (AtkTextClipType) xClipType,
723                                  (AtkTextClipType) yClipType);
724   reply = dbus_message_new_method_return (message);
725   if (!reply)
726     return NULL;
727   /* This isn't pleasant. */
728   dbus_message_iter_init_append (reply, &iter);
729   if (dbus_message_iter_open_container
730       (&iter, DBUS_TYPE_ARRAY, "(iisv)", &array))
731     {
732       int len;
733       int count = (range_list ? MAXRANGELEN : 0);
734       for (len = 0; len < count && range_list[len]; ++len)
735         {
736           if (dbus_message_iter_open_container
737               (&array, DBUS_TYPE_STRUCT, NULL, &struc))
738             {
739               dbus_int32_t val;
740               val = range_list[len]->start_offset;
741               dbus_message_iter_append_basic (&struc, DBUS_TYPE_INT32, &val);
742               val = range_list[len]->end_offset;
743               dbus_message_iter_append_basic (&struc, DBUS_TYPE_INT32, &val);
744               dbus_message_iter_append_basic (&struc, DBUS_TYPE_STRING,
745                                               &range_list[len]->content);
746               /* The variant is unimplemented in atk, but I don't want to
747                * unilaterally muck with the spec and remove it, so I'll just
748                * throw in a dummy value */
749               if (dbus_message_iter_open_container
750                   (&struc, DBUS_TYPE_VARIANT, "i", &variant))
751                 {
752                   dbus_uint32_t dummy = 0;
753                   dbus_message_iter_append_basic (&variant, DBUS_TYPE_INT32,
754                                                   &dummy);
755                   dbus_message_iter_close_container (&struc, &variant);
756                 }
757               dbus_message_iter_close_container (&array, &struc);
758               g_free (range_list[len]->content);
759               g_free (range_list[len]);
760             }
761         }
762       dbus_message_iter_close_container (&iter, &array);
763     }
764
765   if (range_list)
766     g_free (range_list);
767
768   return reply;
769 }
770
771 static DBusMessage *
772 impl_GetAttributeRun (DBusConnection * bus, DBusMessage * message,
773                       void *user_data)
774 {
775   AtkText *text = (AtkText *) user_data;
776   dbus_int32_t offset;
777   dbus_bool_t includeDefaults;
778   dbus_int32_t startOffset, endOffset;
779   gint intstart_offset = 0, intend_offset = 0;
780   DBusMessage *reply;
781   AtkAttributeSet *attributes = NULL;
782   DBusMessageIter iter;
783
784   g_return_val_if_fail (ATK_IS_TEXT (user_data),
785                         droute_not_yet_handled_error (message));
786   if (!dbus_message_get_args
787       (message, NULL, DBUS_TYPE_INT32, &offset, DBUS_TYPE_BOOLEAN,
788        &includeDefaults, DBUS_TYPE_INVALID))
789     {
790       return droute_invalid_arguments_error (message);
791     }
792
793   if (includeDefaults)
794     {
795       attributes = g_slist_concat (attributes,
796                                    atk_text_get_default_attributes (text));
797     }
798
799   attributes = g_slist_concat (attributes,
800                                atk_text_get_run_attributes (text, offset,
801                                                             &intstart_offset,
802                                                             &intend_offset));
803
804   reply = dbus_message_new_method_return (message);
805   if (!reply)
806     return NULL;
807
808   dbus_message_iter_init_append (reply, &iter);
809   spi_object_append_attribute_set (&iter, attributes);
810
811   startOffset = intstart_offset;
812   endOffset = intend_offset;
813   dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &startOffset);
814   dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &endOffset);
815
816   atk_attribute_set_free (attributes);
817
818   return reply;
819 }
820
821 static DBusMessage *
822 impl_GetDefaultAttributeSet (DBusConnection * bus, DBusMessage * message,
823                              void *user_data)
824 {
825   AtkText *text = (AtkText *) user_data;
826   DBusMessage *reply;
827   DBusMessageIter iter;
828   AtkAttributeSet *attributes;
829
830   g_return_val_if_fail (ATK_IS_TEXT (user_data),
831                         droute_not_yet_handled_error (message));
832
833   attributes = atk_text_get_default_attributes (text);
834
835   reply = dbus_message_new_method_return (message);
836   if (reply)
837     {
838       dbus_message_iter_init_append (reply, &iter);
839       spi_object_append_attribute_set (&iter, attributes);
840     }
841
842   if (attributes)
843     atk_attribute_set_free (attributes);
844
845   return reply;
846 }
847
848 static DBusMessage *
849 impl_ScrollSubstringTo (DBusConnection * bus,
850                         DBusMessage * message, void *user_data)
851 {
852   AtkText *text = (AtkText *) user_data;
853   dbus_int32_t startOffset, endOffset;
854   dbus_uint32_t type;
855   dbus_bool_t ret;
856   DBusMessage *reply = NULL;
857
858   g_return_val_if_fail (ATK_IS_TEXT (user_data),
859                         droute_not_yet_handled_error (message));
860
861   if (!dbus_message_get_args
862        (message, NULL, DBUS_TYPE_INT32, &startOffset,
863                        DBUS_TYPE_INT32, &endOffset,
864                        DBUS_TYPE_UINT32, &type,
865                        DBUS_TYPE_INVALID))
866     {
867       return droute_invalid_arguments_error (message);
868     }
869
870   ret = atk_text_scroll_substring_to (text, startOffset, endOffset, type);
871
872   reply = dbus_message_new_method_return (message);
873   if (reply)
874     {
875       dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &ret,
876                                 DBUS_TYPE_INVALID);
877     }
878   return reply;
879 }
880
881 static DBusMessage *
882 impl_ScrollSubstringToPoint (DBusConnection * bus,
883                              DBusMessage * message, void *user_data)
884 {
885   AtkText *text = (AtkText *) user_data;
886   dbus_int32_t startOffset, endOffset;
887   dbus_uint32_t type;
888   dbus_int32_t x, y;
889   dbus_bool_t ret;
890   DBusMessage *reply = NULL;
891
892   g_return_val_if_fail (ATK_IS_TEXT (user_data),
893                         droute_not_yet_handled_error (message));
894
895   if (!dbus_message_get_args
896        (message, NULL, DBUS_TYPE_INT32, &startOffset,
897                        DBUS_TYPE_INT32, &endOffset,
898                        DBUS_TYPE_UINT32, &type,
899                        DBUS_TYPE_INT32, &x,
900                        DBUS_TYPE_INT32, &y,
901                        DBUS_TYPE_INVALID))
902     {
903       return droute_invalid_arguments_error (message);
904     }
905
906   ret = atk_text_scroll_substring_to_point (text, startOffset, endOffset, type, x, y);
907
908   reply = dbus_message_new_method_return (message);
909   if (reply)
910     {
911       dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &ret,
912                                 DBUS_TYPE_INVALID);
913     }
914   return reply;
915 }
916
917 static DRouteMethod methods[] = {
918   {impl_GetText, "GetText"},
919   {impl_SetCaretOffset, "SetCaretOffset"},
920   {impl_GetTextBeforeOffset, "GetTextBeforeOffset"},
921   {impl_GetTextAtOffset, "GetTextAtOffset"},
922   {impl_GetTextAfterOffset, "GetTextAfterOffset"},
923   {impl_GetStringAtOffset, "GetStringAtOffset"},
924   {impl_GetCharacterAtOffset, "GetCharacterAtOffset"},
925   {impl_GetAttributeValue, "GetAttributeValue"},
926   {impl_GetAttributes, "GetAttributes"},
927   {impl_GetDefaultAttributes, "GetDefaultAttributes"},
928   {impl_GetCharacterExtents, "GetCharacterExtents"},
929   {impl_GetOffsetAtPoint, "GetOffsetAtPoint"},
930   {impl_GetNSelections, "GetNSelections"},
931   {impl_GetSelection, "GetSelection"},
932   {impl_AddSelection, "AddSelection"},
933   {impl_RemoveSelection, "RemoveSelection"},
934   {impl_SetSelection, "SetSelection"},
935   {impl_GetRangeExtents, "GetRangeExtents"},
936   {impl_GetBoundedRanges, "GetBoundedRanges"},
937   {impl_GetAttributeRun, "GetAttributeRun"},
938   {impl_GetDefaultAttributeSet, "GetDefaultAttributeSet"},
939   {impl_ScrollSubstringTo, "ScrollSubstringTo"},
940   {impl_ScrollSubstringToPoint, "ScrollSubstringToPoint"},
941   {NULL, NULL}
942 };
943
944 static DRouteProperty properties[] = {
945   {impl_get_CharacterCount, NULL, "CharacterCount"},
946   {impl_get_CaretOffset, NULL, "CaretOffset"},
947   {NULL, NULL, NULL}
948 };
949
950 void
951 spi_initialize_text (DRoutePath * path)
952 {
953   spi_atk_add_interface (path,
954                          ATSPI_DBUS_INTERFACE_TEXT, spi_org_a11y_atspi_Text, methods, properties);
955 };