Include <stdlib.h> to fix ISan build.
[platform/upstream/at-spi2-core.git] / atspi / atspi-accessible.c
1 /*
2  * AT-SPI - Assistive Technology Service Provider Interface
3  * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
4  *
5  * Copyright 2001, 2002 Sun Microsystems Inc.,
6  * Copyright 2001, 2002 Ximian, Inc.
7  * Copyright 2010, 2011 Novell, 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 "atspi-private.h"
26 #include "atspi-accessible-private.h"
27 #include <string.h>
28 #include <stdlib.h>
29
30 static gboolean enable_caching = FALSE;
31 static guint quark_locale;
32
33 static void
34 atspi_action_interface_init (AtspiAction *action)
35 {
36 }
37
38 static void
39 atspi_collection_interface_init (AtspiCollection *component)
40 {
41 }
42
43 static void
44 atspi_component_interface_init (AtspiComponent *component)
45 {
46 }
47
48 static void
49 atspi_document_interface_init (AtspiDocument *document)
50 {
51 }
52
53 static void
54 atspi_editable_text_interface_init (AtspiEditableText *editable_text)
55 {
56 }
57
58 static void
59 atspi_hypertext_interface_init (AtspiHypertext *hypertext)
60 {
61 }
62
63 static void
64 atspi_image_interface_init (AtspiImage *image)
65 {
66 }
67
68 static void
69 atspi_selection_interface_init (AtspiSelection *selection)
70 {
71 }
72
73 static void
74 atspi_table_interface_init (AtspiTable *table)
75 {
76 }
77
78 static void
79 atspi_table_cell_interface_init (AtspiTableCell *cell)
80 {
81 }
82
83 static void
84 atspi_text_interface_init (AtspiText *text)
85 {
86 }
87
88 static void
89 atspi_value_interface_init (AtspiValue *value)
90 {
91 }
92
93 G_DEFINE_TYPE_WITH_CODE (AtspiAccessible, atspi_accessible, ATSPI_TYPE_OBJECT,
94                          G_ADD_PRIVATE (AtspiAccessible)
95                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_ACTION, atspi_action_interface_init)
96                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_COLLECTION, atspi_collection_interface_init)
97                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_COMPONENT, atspi_component_interface_init)
98                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_DOCUMENT, atspi_document_interface_init)
99                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_EDITABLE_TEXT, atspi_editable_text_interface_init)
100                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_HYPERTEXT, atspi_hypertext_interface_init)
101                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_IMAGE, atspi_image_interface_init)
102                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_SELECTION, atspi_selection_interface_init)
103                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_TABLE, atspi_table_interface_init)
104                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_TABLE_CELL, atspi_table_cell_interface_init)
105                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_TEXT, atspi_text_interface_init)
106                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_VALUE, atspi_value_interface_init))
107
108 #ifdef DEBUG_REF_COUNTS
109 static gint accessible_count = 0;
110 #endif
111
112 static void
113 atspi_accessible_init (AtspiAccessible *accessible)
114 {
115 #ifdef DEBUG_REF_COUNTS
116   accessible_count++;
117   g_hash_table_insert (_atspi_get_live_refs (), accessible, NULL);
118   g_print("at-spi: init: %d objects\n", accessible_count);
119 #endif
120
121   accessible->priv = atspi_accessible_get_instance_private (accessible);
122 }
123
124 static void
125 atspi_accessible_dispose (GObject *object)
126 {
127   AtspiAccessible *accessible = ATSPI_ACCESSIBLE (object);
128   AtspiEvent e;
129   AtspiAccessible *parent;
130   GList *children;
131   GList *l;
132
133   /* TODO: Only fire if object not already marked defunct */
134   memset (&e, 0, sizeof (e));
135   e.type = "object:state-changed:defunct";
136   e.source = accessible;
137   e.detail1 = 1;
138   e.detail2 = 0;
139   _atspi_send_event (&e);
140
141   if (accessible->states)
142   {
143     g_object_unref (accessible->states);
144     accessible->states = NULL;
145   }
146
147   parent = accessible->accessible_parent;
148   if (parent && parent->children)
149   {
150     GList*ls = g_list_find (parent->children, accessible);
151     if(ls)
152     {
153       gboolean replace = (ls == parent->children);
154       ls = g_list_remove (ls, accessible);
155       if (replace)
156         parent->children = ls;
157       g_object_unref (object);
158     }
159   }
160
161   if (parent)
162   {
163     g_object_unref (parent);
164     accessible->accessible_parent = NULL;
165   }
166
167   children = accessible->children;
168   accessible->children = NULL;
169   for (l = children; l; l = l->next)
170   {
171     AtspiAccessible *child = l->data;
172     if (child && child->accessible_parent == accessible)
173     {
174       g_object_unref (accessible);
175       child->accessible_parent = NULL;
176     }
177     g_object_unref (child);
178   }
179   g_list_free (children);
180
181   G_OBJECT_CLASS (atspi_accessible_parent_class) ->dispose (object);
182 }
183
184 static void
185 atspi_accessible_finalize (GObject *object)
186 {
187   AtspiAccessible *accessible = ATSPI_ACCESSIBLE (object);
188
189     g_free (accessible->description);
190     g_free (accessible->name);
191   if (accessible->attributes)
192     g_hash_table_unref (accessible->attributes);
193
194     if (accessible->priv->cache)
195       g_hash_table_destroy (accessible->priv->cache);
196
197 #ifdef DEBUG_REF_COUNTS
198   accessible_count--;
199   g_hash_table_remove (_atspi_get_live_refs (), accessible);
200   g_print ("at-spi: finalize: %d objects\n", accessible_count);
201 #endif
202
203   G_OBJECT_CLASS (atspi_accessible_parent_class)
204     ->finalize (object);
205 }
206
207 static void
208 atspi_accessible_class_init (AtspiAccessibleClass *klass)
209 {
210   GObjectClass *object_class = G_OBJECT_CLASS (klass);
211
212   object_class->dispose = atspi_accessible_dispose;
213   object_class->finalize = atspi_accessible_finalize;
214
215   quark_locale = g_quark_from_string ("accessible-locale");
216 }
217
218 /**
219  * atspi_accessible_get_name:
220  * @obj: a pointer to the #AtspiAccessible object on which to operate.
221  *
222  * Gets the name of an #AtspiAccessible object.
223  *
224  * Returns: a UTF-8 string indicating the name of the #AtspiAccessible object
225  * or NULL on exception.
226  **/
227 gchar *
228 atspi_accessible_get_name (AtspiAccessible *obj, GError **error)
229 {
230   g_return_val_if_fail (obj != NULL, g_strdup (""));
231   if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_NAME))
232   {
233     if (!_atspi_dbus_get_property (obj, atspi_interface_accessible, "Name", error,
234                                    "s", &obj->name))
235       return g_strdup ("");
236     _atspi_accessible_add_cache (obj, ATSPI_CACHE_NAME);
237   }
238   return g_strdup (obj->name);
239 }
240
241
242 /**
243  * atspi_accessible_get_unique_id:
244  * @obj: a pointer to the #AtspiAccessible object on which to operate.
245  *
246  * Gets the identificator, uniquely identifying object, or NULL if an error occured.
247  *
248  * Returns: a UTF-8 string describing the #AtspiAccessible object
249  * or NULL on exception or NULL object passed.
250  **/
251 gchar *
252 atspi_accessible_get_unique_id(AtspiAccessible *obj, GError **error)
253 {
254   if (!obj) {
255     g_set_error(error, ATSPI_ERROR, ATSPI_ERROR_IPC, "argument is null");
256     return NULL;
257   }
258
259   gchar *id = NULL;
260   gchar *bus_name = atspi_accessible_get_bus_name(obj, error);
261   if (bus_name && bus_name[0]) {
262     gchar *path = atspi_accessible_get_path(obj, error);
263     if (path && path[0])
264       id = g_strdup_printf("%s:%s", bus_name, path);
265           else
266       g_set_error(error, ATSPI_ERROR, ATSPI_ERROR_IPC, "failed to get path");
267     g_free(path);
268   }
269   else
270     g_set_error(error, ATSPI_ERROR, ATSPI_ERROR_IPC, "failed to get bus name");
271   g_free(bus_name);
272   return id;
273 }
274
275 /**
276  * atspi_accessible_get_bus_name:
277  * @obj: a pointer to the #AtspiAccessible object on which to operate.
278  *
279  * Gets the bus name, where object belongs.
280  *
281  * Returns: a UTF-8 string describing the #AtspiAccessible object's
282  * bus name or empty string on exception or NULL object passed.
283  **/
284 gchar *
285 atspi_accessible_get_bus_name(AtspiAccessible *obj, GError **error)
286 {
287   if (!obj || !obj->parent.app)
288     return g_strdup("");
289   return g_strdup (obj->parent.app->bus_name);
290 }
291
292 /**
293  * atspi_accessible_get_path:
294  * @obj: a pointer to the #AtspiAccessible object on which to operate.
295  *
296  * Gets the path, uniquely identifying object over its bus name.
297  *
298  * Returns: a UTF-8 string describing the #AtspiAccessible object
299  * or empty string on exception or NULL object passed.
300  **/
301 gchar *
302 atspi_accessible_get_path(AtspiAccessible *obj, GError **error)
303 {
304   static const char *prefix = "/org/a11y/atspi/accessible/";
305   static int prefix_len = 27;
306
307   if (!obj)
308     return g_strdup("");
309   AtspiObject *o = ATSPI_OBJECT (obj);
310   if (!o)
311     return g_strdup("");
312   if (strncmp(o->path, prefix, prefix_len) == 0)
313     return g_strdup(o->path + prefix_len);
314   return g_strdup (o->path);
315 }
316
317 /**
318  * atspi_accessible_get_navigable_at_point:
319  * @root: a pointer to the #AtspiAccessible to start search from.
320  * @x: a #gint specifying the x coordinate of the point in question.
321  * @y: a #gint specifying the y coordinate of the point in question.
322  * @ctype: the coordinate system of the point (@x, @y)
323  *         (e.g. ATSPI_COORD_TYPE_WINDOW, ATSPI_COORD_TYPE_SCREEN).
324  *
325  * Finds the accessible element closest to user (highest in z-order), at a given coordinate within an #AtspiAccessible.
326  * This should be the element, that should be picked, when doing mouse click or finger tap at given coordinates.
327  *
328  * Returns: (nullable) (transfer full): a pointer to an
329  *          #AtspiAccessible descendant (of any depth) of the specified component which
330  *          contains the point (@x, @y), or NULL if no descendant contains
331  *          the point.
332  **/
333 AtspiAccessible *
334 atspi_accessible_get_navigable_at_point (AtspiAccessible *root,
335                                           gint x,
336                                           gint y,
337                                           AtspiCoordType ctype, GError **error)
338 {
339   dbus_int32_t d_x = x, d_y = y;
340   dbus_uint32_t d_ctype = ctype;
341   DBusMessage *reply;
342   AtspiAccessible *return_value = NULL;
343   unsigned char recurse = 0;
344   DBusMessageIter iter;
345   AtspiAccessible *deputy = NULL;
346
347   g_return_val_if_fail (root != NULL, NULL);
348   do {
349     reply = _atspi_dbus_call_partial (root, atspi_interface_accessible, "GetNavigableAtPoint", error, "iiu", d_x, d_y, d_ctype);
350     // call failed, error is set, so we bail out
351     if (!reply) {
352       if (deputy) g_object_unref(deputy);
353       if (return_value) g_object_unref(return_value);
354       return NULL;
355     }
356     _ATSPI_DBUS_CHECK_SIG (reply, "(so)y(so)", NULL, NULL);
357
358     dbus_message_iter_init (reply, &iter);
359     AtspiAccessible *tmp = _atspi_dbus_return_accessible_from_iter (&iter);
360
361     unsigned char value = 0;
362     dbus_message_iter_get_basic (&iter, &value);
363     dbus_message_iter_next (&iter);
364     recurse = (value != 0);
365
366     /* keep deputy if tmp has deputy */
367     if (!deputy)
368       deputy = _atspi_dbus_return_accessible_from_iter (&iter);
369
370     dbus_message_unref(reply);
371
372     if (!tmp) {
373       if (deputy) {
374         /* TODO: need to check deputy works for return value */
375         if (return_value)
376           g_object_unref(return_value);
377         return deputy;
378       }
379       break;
380     }
381
382     if (return_value)
383       g_object_unref(return_value);
384     return_value = root = tmp;
385   } while(recurse);
386   return return_value;
387 }
388
389 /**
390  * atspi_accessible_get_reading_material:
391  * @obj: a pointer to the #AtspiAccessible object on which to operate.
392  *
393  * Gets reading material
394  *
395  * Returns: reading material to be used screen-reader side. This is not stable.
396  * You have to handle all alocated memory as below on screen-reader side.
397  *
398  * AtspiAccessibleReadingMaterial *rm
399  * g_object_unref(rm->parent);
400  * g_object_unref(rm->described_by_accessible);
401  * g_hash_table_unref(rm->attributes);
402  * free(rm->name);
403  * free(rm->labeled_by_name);
404  * free(rm->text_interface_name);
405  * free(rm->localized_role_name);
406  * free(rm->description);
407  * free(rm);
408  **/
409 AtspiAccessibleReadingMaterial *
410 atspi_accessible_get_reading_material (AtspiAccessible *obj, GError **error)
411 {
412   AtspiAccessible *parent;
413   AtspiAccessibleReadingMaterial *reading_material = NULL;
414   const char *name;
415   double current_value;
416   gint count;
417   guint64 val;
418   DBusMessage *reply;
419   DBusMessageIter iter;
420   DBusMessageIter iter_array;
421   dbus_uint32_t role;
422   dbus_uint32_t *states;
423   dbus_int32_t index_in_parent;
424   dbus_int32_t child_count;
425   dbus_bool_t is_selected;
426
427   g_return_val_if_fail (obj != NULL, NULL);
428
429   reply = _atspi_dbus_call_partial (obj, atspi_interface_accessible, "GetReadingMaterial", error, "");
430
431   _ATSPI_DBUS_CHECK_SIG (reply, "a{ss}sssuausiddddsibbii(so)auiui(so)", NULL, NULL);
432
433   reading_material = calloc(1, sizeof(AtspiAccessibleReadingMaterial));
434   if (!reading_material)
435   {
436     return reading_material;
437   }
438
439   dbus_message_iter_init (reply, &iter);
440
441   /* get attributes */
442   reading_material->attributes =  _atspi_dbus_hash_from_iter (&iter);
443   dbus_message_iter_next (&iter);
444
445   /* get name */
446   dbus_message_iter_get_basic (&iter, &name);
447   reading_material->name = g_strdup (name);
448   dbus_message_iter_next (&iter);
449
450   /* get name of relation LABELED_BY */
451   dbus_message_iter_get_basic (&iter, &name);
452   reading_material->labeled_by_name = g_strdup (name);
453   dbus_message_iter_next (&iter);
454
455   /* get name of text interface */
456   dbus_message_iter_get_basic (&iter, &name);
457   reading_material->text_interface_name = g_strdup (name);
458   dbus_message_iter_next (&iter);
459
460   /* get role */
461   dbus_message_iter_get_basic (&iter, &role);
462   reading_material->role = role;
463   dbus_message_iter_next (&iter);
464
465   /* get state set */
466   dbus_message_iter_recurse (&iter, &iter_array);
467   dbus_message_iter_get_fixed_array (&iter_array, &states, &count);
468   val = ((guint64)states [1]) << 32;
469   val += states [0];
470   reading_material->states = val;
471   dbus_message_iter_next (&iter);
472
473   /* get localized role name */
474   dbus_message_iter_get_basic (&iter, &name);
475   reading_material->localized_role_name = g_strdup (name);
476   dbus_message_iter_next (&iter);
477
478   /* get child count */
479   dbus_message_iter_get_basic (&iter, &child_count);
480   reading_material->child_count = child_count;
481   dbus_message_iter_next (&iter);
482
483   /* get current value */
484   dbus_message_iter_get_basic (&iter, &current_value);
485   reading_material->value = current_value;
486   dbus_message_iter_next (&iter);
487
488   /* get minimum increment */
489   dbus_message_iter_get_basic (&iter, &current_value);
490   reading_material->increment = current_value;
491   dbus_message_iter_next (&iter);
492
493   /* get maximum value */
494   dbus_message_iter_get_basic (&iter, &current_value);
495   reading_material->upper = current_value;
496   dbus_message_iter_next (&iter);
497
498   /* get minimum value */
499   dbus_message_iter_get_basic (&iter, &current_value);
500   reading_material->lower = current_value;
501   dbus_message_iter_next (&iter);
502
503   /* get description */
504   dbus_message_iter_get_basic (&iter, &name);
505   reading_material->description = g_strdup (name);
506   dbus_message_iter_next (&iter);
507
508   /* get index in parent */
509   dbus_message_iter_get_basic (&iter, &index_in_parent);
510   reading_material->index_in_parent = index_in_parent;
511   dbus_message_iter_next (&iter);
512
513   /* get selected in parent */
514   dbus_message_iter_get_basic (&iter, &is_selected);
515   reading_material->is_selected_in_parent = is_selected;
516   dbus_message_iter_next (&iter);
517
518   /* get has checkbox child */
519   dbus_message_iter_get_basic (&iter, &is_selected);
520   reading_material->has_checkbox_child = is_selected;
521   dbus_message_iter_next (&iter);
522
523   /* get list children count */
524   dbus_message_iter_get_basic (&iter, &child_count);
525   reading_material->list_children_count = child_count;
526   dbus_message_iter_next (&iter);
527
528   /* get first selected child index */
529   dbus_message_iter_get_basic (&iter, &index_in_parent);
530   reading_material->first_selected_child_index = index_in_parent;
531   dbus_message_iter_next (&iter);
532
533   ////////////////
534   /* get parent */
535   parent =  _atspi_dbus_return_accessible_from_iter (&iter);
536   reading_material->parent = parent;
537
538   /* parent states */
539   dbus_message_iter_recurse (&iter, &iter_array);
540   dbus_message_iter_get_fixed_array (&iter_array, &states, &count);
541   val = ((guint64)states [1]) << 32;
542   val += states [0];
543   reading_material->parent_states = val;
544   dbus_message_iter_next (&iter);
545
546   /* get parent child count */
547   dbus_message_iter_get_basic (&iter, &child_count);
548   reading_material->parent_child_count = child_count;
549   dbus_message_iter_next (&iter);
550
551   /* get parent role */
552   dbus_message_iter_get_basic (&iter, &role);
553   reading_material->parent_role = role;
554   dbus_message_iter_next (&iter);
555
556   /* get parent selected child count */
557   dbus_message_iter_get_basic (&iter, &child_count);
558   reading_material->parent_selected_child_count = child_count;
559   dbus_message_iter_next (&iter);
560
561   ////////////////////////////////////////
562   /* get relation object - DESCRIBED_BY */
563   parent =  _atspi_dbus_return_accessible_from_iter (&iter);
564   reading_material->described_by_accessible = parent;
565
566   return reading_material;
567 }
568
569 /**
570  * atspi_accessible_get_default_label_info:
571  * @obj: a pointer to the #AtspiAccessible object would be window.
572  *
573  * Gets default label information
574  *
575  * Returns: default label information to be used screen-reader side.
576  * This is not stable. And this depends on toolkit side UI definition.
577  * The candidate of default label object could be changed by UI definition.
578  * You have to handle all alocated memory as below on screen-reader side.
579  *
580  * AtspiAccessibleDefaultLabelInfo *dli
581  * g_object_unref(dli->obj);
582  * free(dli);
583  **/
584 AtspiAccessibleDefaultLabelInfo *
585 atspi_accessible_get_default_label_info (AtspiAccessible *obj, GError **error)
586 {
587   AtspiAccessibleDefaultLabelInfo *default_label_info = NULL;
588   AtspiAccessible *default_label_object;
589   dbus_uint32_t role;
590   DBusMessage *reply;
591   DBusMessageIter iter;
592
593   g_return_val_if_fail (obj != NULL, NULL);
594
595   reply = _atspi_dbus_call_partial (obj, atspi_interface_accessible, "GetDefaultLabelInfo", error, "");
596
597   _ATSPI_DBUS_CHECK_SIG (reply, "(so)u", NULL, NULL);
598
599   default_label_info = calloc(1, sizeof(AtspiAccessibleDefaultLabelInfo));
600   if (!default_label_info)
601   {
602     return default_label_info;
603   }
604
605   dbus_message_iter_init (reply, &iter);
606
607   default_label_object =  _atspi_dbus_return_accessible_from_iter (&iter);
608   default_label_info->obj = default_label_object;
609
610   dbus_message_iter_get_basic (&iter, &role);
611   default_label_info->role = role;
612   dbus_message_iter_next (&iter);
613
614   return default_label_info;
615 }
616
617 static unsigned char are_objects_on_the_same_bus(AtspiAccessible *obj1, AtspiAccessible *obj2)
618 {
619   const char *bus_name_1 = obj1->parent.app->bus_name;
620   const char *bus_name_2 = obj2->parent.app->bus_name;
621   return strcmp(bus_name_1, bus_name_2) == 0;
622 }
623
624 static unsigned char object_is_valid(AtspiAccessible *obj)
625 {
626   if (!obj)
627     return 0;
628   AtspiStateSet *ss = atspi_accessible_get_state_set(obj);
629   if (!ss)
630     return 0;
631   unsigned char valid = atspi_state_set_contains(ss, ATSPI_STATE_DEFUNCT) == 0;
632   g_object_unref(ss);
633   return valid;
634 }
635
636 typedef enum {
637   NEIGHBOR_SEARCH_MODE_NORMAL = 0,
638   NEIGHBOR_SEARCH_MODE_RECURSE_FROM_ROOT = 1,
639   NEIGHBOR_SEARCH_MODE_CONTINUE_AFTER_FAILED_RECURSING = 2,
640   NEIGHBOR_SEARCH_MODE_RECURSE_TO_OUTSIDE = 3,
641 } GetNeighborSearchMode;
642 /**
643  * atspi_accessible_get_neighbor:
644  * @root: a pointer to a #AtspiAccessible, which represents current root of subtree to search
645  * @start: a pointer to the #AtspiAccessible to start search from (can be null, which means start from root)
646  * @direction: direction, in which search (forward or backward)
647  *
648  * Calculates next (or previous) accessible element in logical order or null if none found.
649  *
650  * Returns: (nullable) (transfer full): a pointer to an
651  *          #AtspiAccessible element, which is next (previous) in logical order or null if none found.
652  **/
653 AtspiAccessible *
654 atspi_accessible_get_neighbor (AtspiAccessible *root,
655                                           AtspiAccessible *start,
656                                           AtspiNeighborSearchDirection direction,
657                                           GError **error)
658 {
659   g_return_val_if_fail (object_is_valid(root), NULL);
660   if (!object_is_valid(start))
661     start = root;
662   const char *root_path = ATSPI_OBJECT(root)->path;
663   AtspiAccessible *return_value = NULL;
664   g_object_ref(start);
665   unsigned char recurse;
666   GetNeighborSearchMode search_mode = NEIGHBOR_SEARCH_MODE_NORMAL;
667   GQueue *children_root_stack = g_queue_new();
668   DBusMessageIter iter;
669
670   while(1) {
671     const char *path = are_objects_on_the_same_bus(root, start) ? root_path : "";
672     DBusMessage *reply = _atspi_dbus_call_partial (start, atspi_interface_accessible, "GetNeighbor", error, "sii", path, (int)direction, (int)search_mode);
673     // call failed, error is set, so we bail out
674     if (!reply) break;
675
676     _ATSPI_DBUS_CHECK_SIG (reply, "(so)y", error, NULL);
677     dbus_message_iter_init (reply, &iter);
678     AtspiAccessible *ret = _atspi_dbus_return_accessible_from_iter (&iter);
679
680     unsigned char value = 0;
681     dbus_message_iter_get_basic (&iter, &value);
682     dbus_message_iter_next (&iter);
683     recurse = (value != 0);
684
685     dbus_message_unref(reply);
686
687     // got return value and request for recursive search, it means ret is on another bridge, than start
688     // thus we're recursing. should the recurse failed to find anything it will end with
689     if (ret && recurse) {
690       g_object_unref(G_OBJECT(start));
691       start = ret;
692       g_object_ref(start);
693       if (are_objects_on_the_same_bus(root, ret))
694         {
695           search_mode = NEIGHBOR_SEARCH_MODE_RECURSE_TO_OUTSIDE;
696         }
697       else
698         {
699           g_queue_push_tail(children_root_stack, ret);
700           search_mode = NEIGHBOR_SEARCH_MODE_RECURSE_FROM_ROOT;
701         }
702       continue;
703     }
704     // found the one we've been looking for
705     if (ret) {
706       g_object_unref(G_OBJECT(start));
707       return_value = ret;
708       break;
709     }
710
711     // we've stepped into different bridges previously and now we're going back to the last one
712     // and continuing search where we left
713     if (!g_queue_is_empty(children_root_stack)) {
714       g_object_unref(G_OBJECT(start));
715       start = g_queue_pop_tail(children_root_stack);
716
717       search_mode = NEIGHBOR_SEARCH_MODE_CONTINUE_AFTER_FAILED_RECURSING;
718       continue;
719     }
720     // there's no more bridges to check, but we might have started from one
721     // in that case there might be bridges "below" start, which we yet have to visit
722     if (!are_objects_on_the_same_bus(root, start)) {
723       unsigned char continue_loop = 1;
724       while(continue_loop) {
725         AtspiAccessible *parent = atspi_accessible_get_parent(start, NULL);
726         continue_loop = parent ? are_objects_on_the_same_bus(start, parent) : 0;
727         g_object_unref(G_OBJECT(start));
728         start = parent;
729       }
730
731       // going up thru parents put us in weird place (we didnt meet root on the way)
732       // so we bail out
733       if (!start)
734         break;
735
736       // start object now points to different bridge and must be treated as "resume after recursing"
737       search_mode = NEIGHBOR_SEARCH_MODE_CONTINUE_AFTER_FAILED_RECURSING;
738       continue;
739     }
740
741     // nothing found
742     g_object_unref(start);
743     break;
744   }
745   while(!g_queue_is_empty(children_root_stack))
746     g_object_unref(g_queue_pop_tail(children_root_stack));
747   g_queue_free(children_root_stack);
748
749   return return_value;
750 }
751
752 /**
753  * atspi_accessible_get_description:
754  * @obj: a pointer to the #AtspiAccessible object on which to operate.
755  *
756  * Gets the description of an #AtspiAccessible object.
757  *
758  * Returns: a UTF-8 string describing the #AtspiAccessible object
759  * or NULL on exception.
760  **/
761 gchar *
762 atspi_accessible_get_description (AtspiAccessible *obj, GError **error)
763 {
764   g_return_val_if_fail (obj != NULL, g_strdup (""));
765
766   if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_DESCRIPTION))
767   {
768     if (!_atspi_dbus_get_property (obj, atspi_interface_accessible,
769                                    "Description", error, "s",
770                                    &obj->description))
771       return g_strdup ("");
772     _atspi_accessible_add_cache (obj, ATSPI_CACHE_DESCRIPTION);
773   }
774   return g_strdup (obj->description);
775 }
776
777 const char *str_parent = "Parent";
778
779 /**
780  * atspi_accessible_get_parent:
781  * @obj: a pointer to the #AtspiAccessible object to query.
782  *
783  * Gets an #AtspiAccessible object's parent container.
784  *
785  * Returns: (nullable) (transfer full): a pointer to the
786  *          #AtspiAccessible object which contains the given
787  *          #AtspiAccessible instance, or NULL if the @obj has no
788  *          parent container.
789  *
790  **/
791 AtspiAccessible *
792 atspi_accessible_get_parent (AtspiAccessible *obj, GError **error)
793 {
794   g_return_val_if_fail (obj != NULL, NULL);
795
796   if (obj->parent.app &&
797       !_atspi_accessible_test_cache (obj, ATSPI_CACHE_PARENT))
798   {
799     DBusMessage *message, *reply;
800     DBusMessageIter iter, iter_variant;
801     message = dbus_message_new_method_call (obj->parent.app->bus_name,
802                                             obj->parent.path,
803                                             DBUS_INTERFACE_PROPERTIES, "Get");
804     if (!message)
805       return NULL;
806     dbus_message_append_args (message, DBUS_TYPE_STRING, &atspi_interface_accessible,
807                                DBUS_TYPE_STRING, &str_parent,
808                               DBUS_TYPE_INVALID);
809     reply = _atspi_dbus_send_with_reply_and_block (message, error);
810     if (!reply)
811       return NULL;
812     if (strcmp (dbus_message_get_signature (reply), "v") != 0)
813     {
814       dbus_message_unref (reply);
815       return NULL;
816     }
817     dbus_message_iter_init (reply, &iter);
818     dbus_message_iter_recurse (&iter, &iter_variant);
819     obj->accessible_parent = _atspi_dbus_return_accessible_from_iter (&iter_variant);
820     dbus_message_unref (reply);
821     _atspi_accessible_add_cache (obj, ATSPI_CACHE_PARENT);
822   }
823   if (!obj->accessible_parent)
824     return NULL;
825   return g_object_ref (obj->accessible_parent);
826 }
827
828 /**
829  * atspi_accessible_get_child_count:
830  * @obj: a pointer to the #AtspiAccessible object on which to operate.
831  *
832  * Gets the number of children contained by an #AtspiAccessible object.
833  *
834  * Returns: a #long indicating the number of #AtspiAccessible children
835  *          contained by an #AtspiAccessible object or -1 on exception.
836  *
837  **/
838 gint
839 atspi_accessible_get_child_count (AtspiAccessible *obj, GError **error)
840 {
841   g_return_val_if_fail (obj != NULL, -1);
842
843   if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_CHILDREN))
844   {
845     dbus_int32_t ret;
846     if (!_atspi_dbus_get_property (obj, atspi_interface_accessible,
847                                    "ChildCount", error, "i", &ret))
848       return -1;
849     return ret;
850   }
851
852   return g_list_length (obj->children);
853 }
854
855 /**
856  * atspi_accessible_get_child_at_index:
857  * @obj: a pointer to the #AtspiAccessible object on which to operate.
858  * @child_index: a #long indicating which child is specified.
859  *
860  * Gets the #AtspiAccessible child of an #AtspiAccessible object at a given index.
861  *
862  * Returns: (transfer full): a pointer to the #AtspiAccessible child object at
863  * index @child_index or NULL on exception.
864  **/
865 AtspiAccessible *
866 atspi_accessible_get_child_at_index (AtspiAccessible *obj,
867                             gint    child_index,
868                             GError **error)
869 {
870   AtspiAccessible *child;
871
872   g_return_val_if_fail (obj != NULL, NULL);
873
874   if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_CHILDREN))
875   {
876     DBusMessage *reply;
877     reply = _atspi_dbus_call_partial (obj, atspi_interface_accessible,
878                                      "GetChildAtIndex", error, "i",
879                                      child_index);
880     return _atspi_dbus_return_accessible_from_message (reply);
881   }
882
883   child = g_list_nth_data (obj->children, child_index);
884   if (!child)
885     return NULL;
886   return g_object_ref (child);
887 }
888
889 /**
890  * atspi_accessible_get_index_in_parent:
891  * @obj: a pointer to the #AtspiAccessible object on which to operate.
892  *
893  * Gets the index of an #AtspiAccessible object within its parent's
894  * #AtspiAccessible children list.
895  *
896  * Returns: a #glong indicating the index of the #AtspiAccessible object
897  *          in its parent,
898  *          or -1 if @obj has no containing parent or on exception.
899  **/
900 gint
901 atspi_accessible_get_index_in_parent (AtspiAccessible *obj, GError **error)
902 {
903   GList *l;
904   gint i = 0;
905
906   g_return_val_if_fail (obj != NULL, -1);
907   if (_atspi_accessible_test_cache (obj, ATSPI_CACHE_PARENT) &&
908       !obj->accessible_parent)
909     return -1;
910   if (!obj->accessible_parent ||
911       !_atspi_accessible_test_cache (obj->accessible_parent,
912                                      ATSPI_CACHE_CHILDREN))
913   {
914     dbus_int32_t ret = -1;
915     _atspi_dbus_call (obj, atspi_interface_accessible,
916                       "GetIndexInParent", NULL, "=>i", &ret);
917     return ret;
918   }
919
920   l = obj->accessible_parent->children;
921   while (l)
922   {
923     if (l->data == obj) return i;
924     l = g_list_next (l);
925     i++;
926   }
927   return -1;
928 }
929
930 typedef struct
931 {
932   dbus_uint32_t type;
933   GArray *targets;
934 } Accessibility_Relation;
935
936 /**
937  * atspi_accessible_get_relation_set:
938  * @obj: a pointer to the #AtspiAccessible object on which to operate.
939  *
940  * Gets the set of #AtspiRelation objects which describes this #AtspiAccessible object's
941  * relationships with other #AtspiAccessible objects.
942  *
943  * Returns: (element-type AtspiRelation*) (transfer full): a #GArray of
944  *          #AtspiRelation pointers or NULL on exception.
945  **/
946 GArray *
947 atspi_accessible_get_relation_set (AtspiAccessible *obj, GError **error)
948 {
949   DBusMessage *reply;
950   DBusMessageIter iter, iter_array;
951   GArray *ret;
952
953   g_return_val_if_fail (obj != NULL, NULL);
954
955   reply = _atspi_dbus_call_partial (obj, atspi_interface_accessible, "GetRelationSet", error, "");
956   if (!reply)
957     return NULL;
958   _ATSPI_DBUS_CHECK_SIG (reply, "a(ua(so))", error, NULL);
959
960   ret = g_array_new (TRUE, TRUE, sizeof (AtspiRelation *));
961   dbus_message_iter_init (reply, &iter);
962   dbus_message_iter_recurse (&iter, &iter_array);
963   while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
964   {
965     AtspiRelation *relation;
966     relation = _atspi_relation_new_from_iter (&iter_array);
967     ret = g_array_append_val (ret, relation);
968     dbus_message_iter_next (&iter_array);
969   }
970   dbus_message_unref (reply);
971   return ret;
972 }
973
974 /**
975  * atspi_accessible_get_role:
976  * @obj: a pointer to the #AtspiAccessible object on which to operate.
977  *
978  * Gets the UI role played by an #AtspiAccessible object.
979  * This role's name can be obtained via atspi_accessible_get_role_name ().
980  *
981  * Returns: the #AtspiRole of an #AtspiAccessible object.
982  *
983  **/
984 AtspiRole
985 atspi_accessible_get_role (AtspiAccessible *obj, GError **error)
986 {
987   g_return_val_if_fail (obj != NULL, ATSPI_ROLE_INVALID);
988
989   if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_ROLE))
990   {
991     dbus_uint32_t role;
992     /* TODO: Make this a property */
993     if (_atspi_dbus_call (obj, atspi_interface_accessible, "GetRole", error, "=>u", &role))
994     {
995       obj->role = role;
996     _atspi_accessible_add_cache (obj, ATSPI_CACHE_ROLE);
997     }
998   }
999   return obj->role;
1000 }
1001
1002 /**
1003  * atspi_accessible_get_role_name:
1004  * @obj: a pointer to the #AtspiAccessible object on which to operate.
1005  *
1006  * Gets a UTF-8 string corresponding to the name of the role played by an object.
1007  * This method will return useful values for roles that fall outside the
1008  * enumeration used in atspi_accessible_get_role ().
1009  *
1010  * Returns: a UTF-8 string specifying the type of UI role played by an
1011  * #AtspiAccessible object.
1012  *
1013  **/
1014 gchar *
1015 atspi_accessible_get_role_name (AtspiAccessible *obj, GError **error)
1016 {
1017   gchar *retval = NULL;
1018   AtspiRole role;
1019
1020   g_return_val_if_fail (obj != NULL, NULL);
1021
1022   role = atspi_accessible_get_role (obj, error);
1023   if (role >= 0 && role < ATSPI_ROLE_COUNT && role != ATSPI_ROLE_EXTENDED)
1024     return atspi_role_get_name (role);
1025
1026   _atspi_dbus_call (obj, atspi_interface_accessible, "GetRoleName", error, "=>s", &retval);
1027
1028   if (!retval)
1029     retval = g_strdup ("");
1030
1031   return retval;
1032 }
1033
1034 /**
1035  * atspi_accessible_get_localized_role_name:
1036  * @obj: a pointer to the #AtspiAccessible object on which to operate.
1037  *
1038  * Gets a UTF-8 string corresponding to the name of the role played by an
1039  * object, translated to the current locale.
1040  * This method will return useful values for roles that fall outside the
1041  * enumeration used in atspi_accessible_getRole ().
1042  *
1043  * Returns: a localized, UTF-8 string specifying the type of UI role played
1044  * by an #AtspiAccessible object.
1045  *
1046  **/
1047 gchar *
1048 atspi_accessible_get_localized_role_name (AtspiAccessible *obj, GError **error)
1049 {
1050   char *retval = NULL;
1051
1052   g_return_val_if_fail (obj != NULL, NULL);
1053
1054   _atspi_dbus_call (obj, atspi_interface_accessible, "GetLocalizedRoleName", error, "=>s", &retval);
1055
1056   if (!retval)
1057     return g_strdup ("");
1058
1059   return retval;
1060 }
1061
1062 static AtspiStateSet *
1063 defunct_set ()
1064 {
1065   AtspiStateSet *set = atspi_state_set_new (NULL);
1066   atspi_state_set_add (set, ATSPI_STATE_DEFUNCT);
1067   return set;
1068 }
1069
1070 /**
1071  * atspi_accessible_get_state_set:
1072  * @obj: a pointer to the #AtspiAccessible object on which to operate.
1073  *
1074  * Gets the states currently held by an object.
1075  *
1076  * Returns: (transfer full): a pointer to an #AtspiStateSet representing an
1077  * object's current state set.
1078  **/
1079 AtspiStateSet *
1080 atspi_accessible_get_state_set (AtspiAccessible *obj)
1081 {
1082   /* TODO: Should take a GError **, but would be an API break */
1083   if (!obj->parent.app || !obj->parent.app->bus)
1084     return defunct_set ();
1085
1086   if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_STATES))
1087   {
1088     DBusMessage *reply;
1089     DBusMessageIter iter;
1090     reply = _atspi_dbus_call_partial (obj, atspi_interface_accessible,
1091                                       "GetState", NULL, "");
1092     _ATSPI_DBUS_CHECK_SIG (reply, "au", NULL, defunct_set ());
1093     dbus_message_iter_init (reply, &iter);
1094     _atspi_dbus_set_state (obj, &iter);
1095     dbus_message_unref (reply);
1096     _atspi_accessible_add_cache (obj, ATSPI_CACHE_STATES);
1097   }
1098   return g_object_ref (obj->states);
1099 }
1100
1101 /**
1102  * atspi_accessible_get_attributes:
1103  * @obj: The #AtspiAccessible being queried.
1104  *
1105  * Gets the #AttributeSet representing any assigned
1106  * name-value pair attributes or annotations for this object.
1107  * For typographic, textual, or textually-semantic attributes, see
1108  * atspi_text_get_attributes instead.
1109  *
1110  * Returns: (element-type gchar* gchar*) (transfer full): The name-value-pair
1111  * attributes assigned to this object.
1112  */
1113 GHashTable *
1114 atspi_accessible_get_attributes (AtspiAccessible *obj, GError **error)
1115 {
1116   DBusMessage *message;
1117
1118     g_return_val_if_fail (obj != NULL, NULL);
1119
1120   if (obj->priv->cache)
1121   {
1122     GValue *val = g_hash_table_lookup (obj->priv->cache, "Attributes");
1123     if (val)
1124       return g_value_dup_boxed (val);
1125   }
1126
1127   if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_ATTRIBUTES))
1128   {
1129     message = _atspi_dbus_call_partial (obj, atspi_interface_accessible,
1130                                         "GetAttributes", error, "");
1131     obj->attributes = _atspi_dbus_return_hash_from_message (message);
1132     _atspi_accessible_add_cache (obj, ATSPI_CACHE_ATTRIBUTES);
1133   }
1134
1135   if (!obj->attributes)
1136     return NULL;
1137   return g_hash_table_ref (obj->attributes);
1138 }
1139
1140 static void
1141 add_to_attribute_array (gpointer key, gpointer value, gpointer data)
1142 {
1143   GArray **array = (GArray **)data;
1144   gchar *str = g_strconcat (key, ":", value, NULL);
1145   *array = g_array_append_val (*array, str);
1146 }
1147
1148 /**
1149  * atspi_accessible_get_attributes_as_array:
1150  * @obj: The #AtspiAccessible being queried.
1151  *
1152  * Gets a #GArray representing any assigned
1153  * name-value pair attributes or annotations for this object.
1154  * For typographic, textual, or textually-semantic attributes, see
1155  * atspi_text_get_attributes_as_array instead.
1156  *
1157  * Returns: (element-type gchar*) (transfer full): The name-value-pair
1158  *          attributes assigned to this object.
1159  */
1160 GArray *
1161 atspi_accessible_get_attributes_as_array (AtspiAccessible *obj, GError **error)
1162 {
1163   DBusMessage *message;
1164
1165     g_return_val_if_fail (obj != NULL, NULL);
1166
1167   if (obj->priv->cache)
1168   {
1169     GValue *val = g_hash_table_lookup (obj->priv->cache, "Attributes");
1170     if (val)
1171     {
1172       GArray *array = g_array_new (TRUE, TRUE, sizeof (gchar *));
1173       GHashTable *attributes = g_value_get_boxed (val);
1174       g_hash_table_foreach (attributes, add_to_attribute_array, &array);
1175       return array;
1176     }
1177   }
1178
1179   message = _atspi_dbus_call_partial (obj, atspi_interface_accessible, "GetAttributes", error, "");
1180   return _atspi_dbus_return_attribute_array_from_message (message);
1181 }
1182
1183 /**
1184  * atspi_accessible_get_application:
1185  * @obj: The #AtspiAccessible being queried.
1186  *
1187  * Gets the containing #AtspiApplication for an object.
1188  *
1189  * Returns: (transfer full): the containing #AtspiApplication instance for
1190  *          this object.
1191  */
1192 AtspiAccessible *
1193 atspi_accessible_get_application (AtspiAccessible *obj, GError **error)
1194 {
1195   AtspiAccessible *parent;
1196
1197   g_object_ref (obj);
1198   for (;;)
1199   {
1200     parent = atspi_accessible_get_parent (obj, NULL);
1201     if (!parent && obj->parent.app &&
1202         atspi_accessible_get_role (obj, NULL) != ATSPI_ROLE_APPLICATION)
1203     {
1204       AtspiAccessible *root = g_object_ref (obj->parent.app->root);
1205       if (root)
1206       {
1207         g_object_unref (obj);
1208         if (atspi_accessible_get_role (root, NULL) == ATSPI_ROLE_DESKTOP_FRAME)
1209         {
1210           g_object_unref (root);
1211           return NULL;
1212         }
1213         return root;
1214       }
1215     }
1216     if (!parent || parent == obj ||
1217         atspi_accessible_get_role (parent, NULL) == ATSPI_ROLE_DESKTOP_FRAME)
1218   {
1219     if (parent)
1220       g_object_unref (parent);
1221     return obj;
1222   }
1223     g_object_unref (obj);
1224     obj = parent;
1225   }
1226 }
1227
1228 /* Application-specific methods */
1229
1230 /**
1231  * atspi_accessible_get_toolkit_name:
1232  * @obj: a pointer to the #AtspiAccessible object on which to operate.
1233  *
1234  * Gets the toolkit name for an #AtspiAccessible object.
1235  * Only works on application root objects.
1236  *
1237  * Returns: a UTF-8 string indicating the toolkit name for the #AtspiAccessible object or NULL on exception.
1238  **/
1239 gchar *
1240 atspi_accessible_get_toolkit_name (AtspiAccessible *obj, GError **error)
1241 {
1242   g_return_val_if_fail (obj != NULL, NULL);
1243
1244   if (!obj->parent.app)
1245     return NULL;
1246
1247   if (!obj->parent.app->toolkit_name)
1248     _atspi_dbus_get_property (obj, atspi_interface_application, "ToolkitName",
1249                               error, "s", &obj->parent.app->toolkit_name);
1250
1251   return g_strdup (obj->parent.app->toolkit_name);
1252 }
1253
1254 /**
1255  * atspi_accessible_get_toolkit_version:
1256  * @obj: a pointer to the #AtspiAccessible object on which to operate.
1257  *
1258  * Gets the toolkit version for an #AtspiAccessible object.
1259  * Only works on application root objects.
1260  *
1261  * Returns: a UTF-8 string indicating the toolkit version for the #AtspiAccessible object or NULL on exception.
1262  **/
1263 gchar *
1264 atspi_accessible_get_toolkit_version (AtspiAccessible *obj, GError **error)
1265 {
1266   g_return_val_if_fail (obj != NULL, NULL);
1267
1268   if (!obj->parent.app)
1269     return NULL;
1270
1271   if (!obj->parent.app->toolkit_version)
1272     _atspi_dbus_get_property (obj, atspi_interface_application, "Version",
1273                               error, "s", &obj->parent.app->toolkit_version);
1274
1275   return g_strdup (obj->parent.app->toolkit_version);
1276 }
1277
1278 /**
1279  * atspi_accessible_get_atspi_version:
1280  * @obj: a pointer to the #AtspiAccessible object on which to operate.
1281  *
1282  * Gets the AT-SPI IPC specification version supported by the application
1283  * pointed to by the #AtspiAccessible object.
1284  * Only works on application root objects.
1285  *
1286  * Returns: a UTF-8 string indicating the AT-SPI version for the #AtspiAccessible object or NULL on exception.
1287  **/
1288 gchar *
1289 atspi_accessible_get_atspi_version (AtspiAccessible *obj, GError **error)
1290 {
1291   g_return_val_if_fail (obj != NULL, NULL);
1292
1293   if (!obj->parent.app)
1294     return NULL;
1295
1296   if (!obj->parent.app->atspi_version)
1297     _atspi_dbus_get_property (obj, atspi_interface_application, "AtspiVersion",
1298                               error, "s", &obj->parent.app->atspi_version);
1299
1300   return g_strdup (obj->parent.app->atspi_version);
1301 }
1302
1303 /**
1304  * atspi_accessible_get_id:
1305  * @obj: a pointer to the #AtspiAccessible object on which to operate.
1306  *
1307  * Gets the application id for a #AtspiAccessible object.
1308  * Only works on application root objects.
1309  *
1310  * Returns: a positive #gint indicating the id for the #AtspiAccessible object
1311  * or -1 on exception.
1312  **/
1313 gint
1314 atspi_accessible_get_id (AtspiAccessible *obj, GError **error)
1315 {
1316   gint ret = -1;
1317
1318   g_return_val_if_fail (obj != NULL, -1);
1319
1320   if (!_atspi_dbus_get_property (obj, atspi_interface_application, "Id", error, "i", &ret))
1321       return -1;
1322   return ret;
1323 }
1324
1325
1326 /* Interface query methods */
1327
1328 static gboolean
1329 _atspi_accessible_is_a (AtspiAccessible *accessible,
1330                       const char *interface_name)
1331 {
1332   int n;
1333
1334   if (accessible == NULL)
1335     {
1336       return FALSE;
1337     }
1338
1339   if (!_atspi_accessible_test_cache (accessible, ATSPI_CACHE_INTERFACES))
1340   {
1341     DBusMessage *reply;
1342     DBusMessageIter iter;
1343     reply = _atspi_dbus_call_partial (accessible, atspi_interface_accessible,
1344                                       "GetInterfaces", NULL, "");
1345     _ATSPI_DBUS_CHECK_SIG (reply, "as", NULL, FALSE);
1346     dbus_message_iter_init (reply, &iter);
1347     _atspi_dbus_set_interfaces (accessible, &iter);
1348     dbus_message_unref (reply);
1349     _atspi_accessible_add_cache (accessible, ATSPI_CACHE_INTERFACES);
1350   }
1351
1352   n = _atspi_get_iface_num (interface_name);
1353   if (n == -1) return FALSE;
1354   return (gboolean) ((accessible->interfaces & (1 << n))? TRUE: FALSE);
1355 }
1356
1357 /**
1358  * atspi_accessible_is_action:
1359  * @obj: a pointer to the #AtspiAccessible instance to query.
1360  *
1361  * Query whether the specified #AtspiAccessible implements the
1362  * #AtspiAction interface.
1363  *
1364  * Returns: #TRUE if @obj implements the #AtspiAction interface,
1365  *          #FALSE otherwise.
1366  **/
1367 gboolean
1368 atspi_accessible_is_action (AtspiAccessible *obj)
1369 {
1370   return _atspi_accessible_is_a (obj,
1371                               atspi_interface_action);
1372 }
1373
1374 /**
1375  * atspi_accessible_is_application:
1376  * @obj: a pointer to the #AtspiAccessible instance to query.
1377  *
1378  * Query whether the specified #AtspiAccessible implements the
1379  * #AtspiApplication interface.
1380  *
1381  * Returns: #TRUE if @obj implements the #AtspiApplication interface,
1382  *          #FALSE otherwise.
1383  **/
1384 gboolean
1385 atspi_accessible_is_application (AtspiAccessible *obj)
1386 {
1387   return _atspi_accessible_is_a (obj,
1388                               atspi_interface_application);
1389 }
1390
1391 /**
1392  * atspi_accessible_is_collection:
1393  * @obj: a pointer to the #AtspiAccessible instance to query.
1394  *
1395  * Query whether the specified #AtspiAccessible implements the
1396  * #AtspiCollection interface.
1397  *
1398  * Returns: #TRUE if @obj implements the #AtspiCollection interface,
1399  *          #FALSE otherwise.
1400  **/
1401 gboolean
1402 atspi_accessible_is_collection (AtspiAccessible *obj)
1403 {
1404      return _atspi_accessible_is_a (obj,
1405                               atspi_interface_collection);
1406 }
1407
1408 /**
1409  * atspi_accessible_is_component:
1410  * @obj: a pointer to the #AtspiAccessible instance to query.
1411  *
1412  * Query whether the specified #AtspiAccessible implements #AtspiComponent.
1413  *
1414  * Returns: #TRUE if @obj implements the #AtspiComponent interface,
1415  *          #FALSE otherwise.
1416  **/
1417 gboolean
1418 atspi_accessible_is_component (AtspiAccessible *obj)
1419 {
1420   return _atspi_accessible_is_a (obj,
1421                               atspi_interface_component);
1422 }
1423
1424 /**
1425  * atspi_accessible_is_document:
1426  * @obj: a pointer to the #AtspiAccessible instance to query.
1427  *
1428  * Query whether the specified #AtspiAccessible implements the
1429  * #AtspiDocument interface.
1430  *
1431  * Returns: #TRUE if @obj implements the #AtspiDocument interface,
1432  *          #FALSE otherwise.
1433  **/
1434 gboolean
1435 atspi_accessible_is_document (AtspiAccessible *obj)
1436 {
1437   return _atspi_accessible_is_a (obj,
1438                               atspi_interface_document);
1439 }
1440
1441 /**
1442  * atspi_accessible_is_editable_text:
1443  * @obj: a pointer to the #AtspiAccessible instance to query.
1444  *
1445  * Query whether the specified #AtspiAccessible implements the
1446  * #AtspiEditableText interface.
1447  *
1448  * Returns: #TRUE if @obj implements the #AtspiEditableText interface,
1449  *          #FALSE otherwise.
1450  **/
1451 gboolean
1452 atspi_accessible_is_editable_text (AtspiAccessible *obj)
1453 {
1454   return _atspi_accessible_is_a (obj,
1455                               atspi_interface_editable_text);
1456 }
1457
1458 /**
1459  * atspi_accessible_is_hypertext:
1460  * @obj: a pointer to the #AtspiAccessible instance to query.
1461  *
1462  * Query whether the specified #AtspiAccessible implements the
1463  * #AtspiHypertext interface.
1464  *
1465  * Returns: #TRUE if @obj implements the #AtspiHypertext interface,
1466  *          #FALSE otherwise.
1467  **/
1468 gboolean
1469 atspi_accessible_is_hypertext (AtspiAccessible *obj)
1470 {
1471   return _atspi_accessible_is_a (obj,
1472                               atspi_interface_hypertext);
1473 }
1474
1475 /**
1476  * atspi_accessible_is_hyperlink:
1477  * @obj: a pointer to the #AtspiAccessible instance to query.
1478  *
1479  * Query whether the specified #AtspiAccessible implements the
1480  * #AtspiHyperlink interface.
1481  *
1482  * Returns: #TRUE if @obj implements the #AtspiHypertext interface,
1483  *          #FALSE otherwise.
1484  **/
1485 gboolean
1486 atspi_accessible_is_hyperlink (AtspiAccessible *obj)
1487 {
1488   return _atspi_accessible_is_a (obj,
1489                               atspi_interface_hyperlink);
1490 }
1491
1492 /**
1493  * atspi_accessible_is_image:
1494  * @obj: a pointer to the #AtspiAccessible instance to query.
1495  *
1496  * Query whether the specified #AtspiAccessible implements the
1497  * #AtspiImage interface.
1498  *
1499  * Returns: #TRUE if @obj implements the #AtspiImage interface,
1500  *          #FALSE otherwise.
1501 **/
1502 gboolean
1503 atspi_accessible_is_image (AtspiAccessible *obj)
1504 {
1505   return _atspi_accessible_is_a (obj,
1506                               atspi_interface_image);
1507 }
1508
1509 /**
1510  * atspi_accessible_is_selection:
1511  * @obj: a pointer to the #AtspiAccessible instance to query.
1512  *
1513  * Query whether the specified #AtspiAccessible implements the
1514  * #AtspiSelection interface.
1515  *
1516  * Returns: #TRUE if @obj implements the #AtspiSelection interface,
1517  *          #FALSE otherwise.
1518 **/
1519 gboolean
1520 atspi_accessible_is_selection (AtspiAccessible *obj)
1521 {
1522   return _atspi_accessible_is_a (obj,
1523                               atspi_interface_selection);
1524 }
1525
1526 /**
1527  * atspi_accessible_is_table:
1528  * @obj: a pointer to the #AtspiAccessible instance to query.
1529  *
1530  * Query whether the specified #AtspiAccessible implements the
1531  * #AtspiTable interface.
1532  *
1533  * Returns: #TRUE if @obj implements the #AtspiTable interface,
1534  *          #FALSE otherwise.
1535 **/
1536 gboolean
1537 atspi_accessible_is_table (AtspiAccessible *obj)
1538 {
1539   return _atspi_accessible_is_a (obj,
1540                               atspi_interface_table);
1541 }
1542
1543 /**
1544  * atspi_accessible_is_table_cell:
1545  * @obj: a pointer to the #AtspiAccessible instance to query.
1546  *
1547  * Query whether the specified #AtspiAccessible implements the
1548  * #AtspiTableCell interface.
1549  *
1550  * Returns: #TRUE if @obj implements the #AtspiTable interface,
1551  *          #FALSE otherwise.
1552 **/
1553 gboolean
1554 atspi_accessible_is_table_cell (AtspiAccessible *obj)
1555 {
1556   return _atspi_accessible_is_a (obj,
1557                               atspi_interface_table_cell);
1558 }
1559
1560 /**
1561  * atspi_accessible_is_streamable_content:
1562  * @obj: a pointer to the #AtspiAccessible instance to query.
1563  *
1564  * Query whether the specified #AtspiAccessible implements the
1565  * #AtspiStreamableContent interface.
1566  *
1567  * Returns: #TRUE if @obj implements the #AtspiStreamableContent interface,
1568  *          #FALSE otherwise.
1569 **/
1570 gboolean
1571 atspi_accessible_is_streamable_content (AtspiAccessible *obj)
1572 {
1573 #if 0
1574   return _atspi_accessible_is_a (obj,
1575                               atspi_interface_streamable_content);
1576 #else
1577   g_warning ("Streamable content not implemented");
1578   return FALSE;
1579 #endif
1580 }
1581
1582 /**
1583  * atspi_accessible_is_text:
1584  * @obj: a pointer to the #AtspiAccessible instance to query.
1585  *
1586  * Query whether the specified #AtspiAccessible implements the
1587  * #AtspiText interface.
1588  *
1589  * Returns: #TRUE if @obj implements the #AtspiText interface,
1590  *          #FALSE otherwise.
1591 **/
1592 gboolean
1593 atspi_accessible_is_text (AtspiAccessible *obj)
1594 {
1595   return _atspi_accessible_is_a (obj,
1596                               atspi_interface_text);
1597 }
1598
1599 /**
1600  * atspi_accessible_is_value:
1601  * @obj: a pointer to the #AtspiAccessible instance to query.
1602  *
1603  * Query whether the specified #AtspiAccessible implements the
1604  * #AtspiValue interface.
1605  *
1606  * Returns: #TRUE if @obj implements the #AtspiValue interface,
1607  *          #FALSE otherwise.
1608 **/
1609 gboolean
1610 atspi_accessible_is_value (AtspiAccessible *obj)
1611 {
1612   return _atspi_accessible_is_a (obj,
1613                               atspi_interface_value);
1614 }
1615
1616 /**
1617  * atspi_accessible_get_action: (rename-to atspi_accessible_get_action_iface)
1618  * @obj: a pointer to the #AtspiAccessible instance to query.
1619  *
1620  * Gets the #AtspiAction interface for an #AtspiAccessible.
1621  *
1622  * Returns: (transfer full): a pointer to an #AtspiAction interface
1623  *          instance, or NULL if @obj does not implement #AtspiAction.
1624  *
1625  * Deprecated: 2.10: Use atspi_accessible_get_action_iface instead.
1626  **/
1627 AtspiAction *
1628 atspi_accessible_get_action (AtspiAccessible *accessible)
1629 {
1630   return (_atspi_accessible_is_a (accessible, atspi_interface_action) ?
1631           g_object_ref (ATSPI_ACTION (accessible)) : NULL);
1632 }
1633
1634 /**
1635  * atspi_accessible_get_action_iface:
1636  * @obj: a pointer to the #AtspiAccessible instance to query.
1637  *
1638  * Gets the #AtspiAction interface for an #AtspiAccessible.
1639  *
1640  * Returns: (transfer full): a pointer to an #AtspiAction interface
1641  *          instance, or NULL if @obj does not implement #AtspiAction.
1642  **/
1643 AtspiAction *
1644 atspi_accessible_get_action_iface (AtspiAccessible *accessible)
1645 {
1646   return (_atspi_accessible_is_a (accessible, atspi_interface_action) ?
1647           g_object_ref (ATSPI_ACTION (accessible)) : NULL);
1648 }
1649
1650 /**
1651  * atspi_accessible_get_collection: (rename-to atspi_accessible_get_collection_iface)
1652  * @obj: a pointer to the #AtspiAccessible instance to query.
1653  *
1654  * Gets the #AtspiCollection interface for an #AtspiAccessible.
1655  *
1656  * Returns: (transfer full): a pointer to an #AtspiCollection interface
1657  *          instance, or NULL if @obj does not implement #AtspiCollection.
1658  *
1659  * Deprecated: 2.10: Use atspi_accessible_get_collection_iface instead.
1660  **/
1661 AtspiCollection *
1662 atspi_accessible_get_collection (AtspiAccessible *accessible)
1663 {
1664   return (_atspi_accessible_is_a (accessible, atspi_interface_collection) ?
1665           g_object_ref (ATSPI_COLLECTION (accessible)) : NULL);
1666 }
1667
1668 /**
1669  * atspi_accessible_get_collection_iface:
1670  * @obj: a pointer to the #AtspiAccessible instance to query.
1671  *
1672  * Gets the #AtspiCollection interface for an #AtspiAccessible.
1673  *
1674  * Returns: (transfer full): a pointer to an #AtspiCollection interface
1675  *          instance, or NULL if @obj does not implement #AtspiCollection.
1676  **/
1677 AtspiCollection *
1678 atspi_accessible_get_collection_iface (AtspiAccessible *accessible)
1679 {
1680   return (_atspi_accessible_is_a (accessible, atspi_interface_collection) ?
1681           g_object_ref (ATSPI_COLLECTION (accessible)) : NULL);
1682 }
1683
1684 /**
1685  * atspi_accessible_get_component: (rename-to atspi_accessible_get_component_iface)
1686  * @obj: a pointer to the #AtspiAccessible instance to query.
1687  *
1688  * Gets the #AtspiComponent interface for an #AtspiAccessible.
1689  *
1690  * Returns: (transfer full): a pointer to an #AtspiComponent interface
1691  *          instance, or NULL if @obj does not implement #AtspiComponent.
1692  *
1693  * Deprecated: 2.10: Use atspi_accessible_get_component_iface instead.
1694  **/
1695 AtspiComponent *
1696 atspi_accessible_get_component (AtspiAccessible *obj)
1697 {
1698   return (_atspi_accessible_is_a (obj, atspi_interface_component) ?
1699           g_object_ref (ATSPI_COMPONENT (obj)) : NULL);
1700 }
1701
1702 /**
1703  * atspi_accessible_get_component_iface:
1704  * @obj: a pointer to the #AtspiAccessible instance to query.
1705  *
1706  * Gets the #AtspiComponent interface for an #AtspiAccessible.
1707  *
1708  * Returns: (transfer full): a pointer to an #AtspiComponent interface
1709  *          instance, or NULL if @obj does not implement #AtspiComponent.
1710  **/
1711 AtspiComponent *
1712 atspi_accessible_get_component_iface (AtspiAccessible *obj)
1713 {
1714   return (_atspi_accessible_is_a (obj, atspi_interface_component) ?
1715           g_object_ref (ATSPI_COMPONENT (obj)) : NULL);
1716 }
1717
1718 /**
1719  * atspi_accessible_get_document: (rename-to atspi_accessible_get_document_iface)
1720  * @obj: a pointer to the #AtspiAccessible instance to query.
1721  *
1722  * Gets the #AtspiDocument interface for an #AtspiAccessible.
1723  *
1724  * Returns: (transfer full): a pointer to an #AtspiDocument interface
1725  *          instance, or NULL if @obj does not implement #AtspiDocument.
1726  *
1727  * Deprecated: 2.10: Use atspi_accessible_get_document_iface instead.
1728  **/
1729 AtspiDocument *
1730 atspi_accessible_get_document (AtspiAccessible *accessible)
1731 {
1732   return (_atspi_accessible_is_a (accessible, atspi_interface_document) ?
1733           g_object_ref (ATSPI_DOCUMENT (accessible)) : NULL);
1734 }
1735
1736 /**
1737  * atspi_accessible_get_document_iface:
1738  * @obj: a pointer to the #AtspiAccessible instance to query.
1739  *
1740  * Gets the #AtspiDocument interface for an #AtspiAccessible.
1741  *
1742  * Returns: (transfer full): a pointer to an #AtspiDocument interface
1743  *          instance, or NULL if @obj does not implement #AtspiDocument.
1744  **/
1745 AtspiDocument *
1746 atspi_accessible_get_document_iface (AtspiAccessible *accessible)
1747 {
1748   return (_atspi_accessible_is_a (accessible, atspi_interface_document) ?
1749           g_object_ref (ATSPI_DOCUMENT (accessible)) : NULL);
1750 }
1751
1752 /**
1753  * atspi_accessible_get_editable_text: (rename-to atspi_accessible_get_editable_text_iface)
1754  * @obj: a pointer to the #AtspiAccessible instance to query.
1755  *
1756  * Gets the #AtspiEditableText interface for an #AtspiAccessible.
1757  *
1758  * Returns: (transfer full): a pointer to an #AtspiEditableText interface
1759  *          instance, or NULL if @obj does not implement #AtspiEditableText.
1760  *
1761  * Deprecated: 2.10: Use atspi_accessible_get_editable_text_iface instead.
1762  **/
1763 AtspiEditableText *
1764 atspi_accessible_get_editable_text (AtspiAccessible *accessible)
1765 {
1766   return (_atspi_accessible_is_a (accessible, atspi_interface_editable_text) ?
1767           g_object_ref (ATSPI_EDITABLE_TEXT (accessible)) : NULL);
1768 }
1769
1770 /**
1771  * atspi_accessible_get_editable_text_iface:
1772  * @obj: a pointer to the #AtspiAccessible instance to query.
1773  *
1774  * Gets the #AtspiEditableText interface for an #AtspiAccessible.
1775  *
1776  * Returns: (transfer full): a pointer to an #AtspiEditableText interface
1777  *          instance, or NULL if @obj does not implement #AtspiEditableText.
1778  **/
1779 AtspiEditableText *
1780 atspi_accessible_get_editable_text_iface (AtspiAccessible *accessible)
1781 {
1782   return (_atspi_accessible_is_a (accessible, atspi_interface_editable_text) ?
1783           g_object_ref (ATSPI_EDITABLE_TEXT (accessible)) : NULL);
1784 }
1785
1786 /**
1787  * atspi_accessible_get_hyperlink:
1788  * @obj: a pointer to the #AtspiAccessible object on which to operate.
1789  *
1790  * Gets the #AtspiHyperlink interface for an #AtspiAccessible.
1791  *
1792  * Returns: (transfer full): the #AtspiHyperlink object associated with
1793  *          the given #AtspiAccessible, or NULL if not supported.
1794  **/
1795 AtspiHyperlink *
1796 atspi_accessible_get_hyperlink (AtspiAccessible *accessible)
1797 {
1798   return (_atspi_accessible_is_a (accessible, atspi_interface_hyperlink) ?
1799           _atspi_hyperlink_new (accessible->parent.app, accessible->parent.path) : NULL);
1800 }
1801
1802 /**
1803  * atspi_accessible_get_hypertext: (rename-to atspi_accessible_get_hypertext_iface)
1804  * @obj: a pointer to the #AtspiAccessible instance to query.
1805  *
1806  * Gets the #AtspiHypertext interface for an #AtspiAccessible.
1807  *
1808  * Returns: (transfer full): a pointer to an #AtspiHypertext interface
1809  *          instance, or NULL if @obj does not implement #AtspiHypertext.
1810  *
1811  * Deprecated: 2.10: Use atspi_accessible_get_hypertext_iface instead.
1812  **/
1813 AtspiHypertext *
1814 atspi_accessible_get_hypertext (AtspiAccessible *accessible)
1815 {
1816   return (_atspi_accessible_is_a (accessible, atspi_interface_hypertext) ?
1817           g_object_ref (ATSPI_HYPERTEXT (accessible)) : NULL);
1818 }
1819
1820 /**
1821  * atspi_accessible_get_hypertext_iface:
1822  * @obj: a pointer to the #AtspiAccessible instance to query.
1823  *
1824  * Gets the #AtspiHypertext interface for an #AtspiAccessible.
1825  *
1826  * Returns: (transfer full): a pointer to an #AtspiHypertext interface
1827  *          instance, or NULL if @obj does not implement #AtspiHypertext.
1828  **/
1829 AtspiHypertext *
1830 atspi_accessible_get_hypertext_iface (AtspiAccessible *accessible)
1831 {
1832   return (_atspi_accessible_is_a (accessible, atspi_interface_hypertext) ?
1833           g_object_ref (ATSPI_HYPERTEXT (accessible)) : NULL);
1834 }
1835
1836 /**
1837  * atspi_accessible_get_image: (rename-to atspi_accessible_get_image_iface)
1838  * @obj: a pointer to the #AtspiAccessible instance to query.
1839  *
1840  * Gets the #AtspiImage interface for an #AtspiAccessible.
1841  *
1842  * Returns: (transfer full): a pointer to an #AtspiImage interface instance, or
1843  *          NULL if @obj does not implement #AtspiImage.
1844  *
1845  * Deprecated: 2.10: Use atspi_accessible_get_image_iface instead.
1846  **/
1847 AtspiImage *
1848 atspi_accessible_get_image (AtspiAccessible *accessible)
1849 {
1850   return (_atspi_accessible_is_a (accessible, atspi_interface_image) ?
1851           g_object_ref (ATSPI_IMAGE (accessible)) : NULL);
1852 }
1853
1854 /**
1855  * atspi_accessible_get_image_iface:
1856  * @obj: a pointer to the #AtspiAccessible instance to query.
1857  *
1858  * Gets the #AtspiImage interface for an #AtspiAccessible.
1859  *
1860  * Returns: (transfer full): a pointer to an #AtspiImage interface instance, or
1861  *          NULL if @obj does not implement #AtspiImage.
1862  **/
1863 AtspiImage *
1864 atspi_accessible_get_image_iface (AtspiAccessible *accessible)
1865 {
1866   return (_atspi_accessible_is_a (accessible, atspi_interface_image) ?
1867           g_object_ref (ATSPI_IMAGE (accessible)) : NULL);
1868 }
1869
1870 /**
1871  * atspi_accessible_get_selection: (rename-to atspi_accessible_get_selection_iface)
1872  * @obj: a pointer to the #AtspiAccessible instance to query.
1873  *
1874  * Gets the #AtspiSelection interface for an #AtspiAccessible.
1875  *
1876  * Returns: (transfer full): a pointer to an #AtspiSelection interface
1877  *          instance, or NULL if @obj does not implement #AtspiSelection.
1878  *
1879  * Deprecated: 2.10: Use atspi_accessible_get_selection_iface instead.
1880  **/
1881 AtspiSelection *
1882 atspi_accessible_get_selection (AtspiAccessible *accessible)
1883 {
1884   return (_atspi_accessible_is_a (accessible, atspi_interface_selection) ?
1885           g_object_ref (ATSPI_SELECTION (accessible)) : NULL);
1886 }
1887
1888 /**
1889  * atspi_accessible_get_selection_iface:
1890  * @obj: a pointer to the #AtspiAccessible instance to query.
1891  *
1892  * Gets the #AtspiSelection interface for an #AtspiAccessible.
1893  *
1894  * Returns: (transfer full): a pointer to an #AtspiSelection interface
1895  *          instance, or NULL if @obj does not implement #AtspiSelection.
1896  **/
1897 AtspiSelection *
1898 atspi_accessible_get_selection_iface (AtspiAccessible *accessible)
1899 {
1900   return (_atspi_accessible_is_a (accessible, atspi_interface_selection) ?
1901           g_object_ref (ATSPI_SELECTION (accessible)) : NULL);
1902 }
1903
1904 #if 0
1905 /**
1906  * atspi_accessible_get_streamable_content:
1907  * @obj: a pointer to the #AtspiAccessible instance to query.
1908  *
1909  * Gets the #AtspiStreamableContent interface for an #AtspiAccessible.
1910  *
1911  * Returns: (transfer full): a pointer to an #AtspiStreamableContent interface
1912  *          instance, or NULL if @obj does not implement #AtspiStreamableContent.
1913  **/
1914 AtspiStreamableContent *
1915 atspi_accessible_get_streamable_content (AtspiAccessible *accessible)
1916 {
1917   return (_atspi_accessible_is_a (accessible, atspi_interface_streamable_content) ?
1918           accessible : NULL);
1919 }
1920 #endif
1921
1922 /**
1923  * atspi_accessible_get_table: (rename-to atspi_accessible_get_table_iface)
1924  * @obj: a pointer to the #AtspiAccessible instance to query.
1925  *
1926  * Gets the #AtspiTable interface for an #AtspiAccessible.
1927  *
1928  * Returns: (transfer full): a pointer to an #AtspiTable interface instance, or
1929  *          NULL if @obj does not implement #AtspiTable.
1930  *
1931  * Deprecated: 2.10: Use atspi_accessible_get_table_iface instead.
1932  **/
1933 AtspiTable *
1934 atspi_accessible_get_table (AtspiAccessible *obj)
1935 {
1936   return (_atspi_accessible_is_a (obj, atspi_interface_table) ?
1937           g_object_ref (ATSPI_TABLE (obj)) : NULL);
1938 }
1939
1940 /**
1941  * atspi_accessible_get_table_iface:
1942  * @obj: a pointer to the #AtspiAccessible instance to query.
1943  *
1944  * Gets the #AtspiTable interface for an #AtspiAccessible.
1945  *
1946  * Returns: (transfer full): a pointer to an #AtspiTable interface instance, or
1947  *          NULL if @obj does not implement #AtspiTable.
1948  **/
1949 AtspiTable *
1950 atspi_accessible_get_table_iface (AtspiAccessible *obj)
1951 {
1952   return (_atspi_accessible_is_a (obj, atspi_interface_table) ?
1953           g_object_ref (ATSPI_TABLE (obj)) : NULL);
1954 }
1955
1956 /**
1957  * atspi_accessible_get_table_cell:
1958  * @obj: a pointer to the #AtspiAccessible instance to query.
1959  *
1960  * Gets the #AtspiTableCell interface for an #AtspiAccessible.
1961  *
1962  * Returns: (transfer full): a pointer to an #AtspiTableCell interface instance,
1963  *          or NULL if @obj does not implement #AtspiTable.
1964  **/
1965 AtspiTableCell *
1966 atspi_accessible_get_table_cell (AtspiAccessible *obj)
1967 {
1968   return (_atspi_accessible_is_a (obj, atspi_interface_table_cell) ?
1969           g_object_ref (ATSPI_TABLE_CELL (obj)) : NULL);
1970 }
1971
1972 /**
1973  * atspi_accessible_get_text: (rename-to atspi_accessible_get_text_iface)
1974  * @obj: a pointer to the #AtspiAccessible instance to query.
1975  *
1976  * Gets the #AtspiTable interface for an #AtspiAccessible.
1977  *
1978  * Returns: (transfer full): a pointer to an #AtspiText interface instance, or
1979  *          NULL if @obj does not implement #AtspiText.
1980  *
1981  * Deprecated: 2.10: Use atspi_accessible_get_text_iface instead.
1982  **/
1983 AtspiText *
1984 atspi_accessible_get_text (AtspiAccessible *obj)
1985 {
1986   return (_atspi_accessible_is_a (obj, atspi_interface_text) ?
1987           g_object_ref (ATSPI_TEXT (obj)) : NULL);
1988 }
1989
1990 /**
1991  * atspi_accessible_get_text_iface:
1992  * @obj: a pointer to the #AtspiAccessible instance to query.
1993  *
1994  * Gets the #AtspiTable interface for an #AtspiAccessible.
1995  *
1996  * Returns: (transfer full): a pointer to an #AtspiText interface instance, or
1997  *          NULL if @obj does not implement #AtspiText.
1998  **/
1999 AtspiText *
2000 atspi_accessible_get_text_iface (AtspiAccessible *obj)
2001 {
2002   return (_atspi_accessible_is_a (obj, atspi_interface_text) ?
2003           g_object_ref (ATSPI_TEXT (obj)) : NULL);
2004 }
2005
2006 /**
2007  * atspi_accessible_get_value: (rename-to atspi_accessible_get_value_iface)
2008  * @obj: a pointer to the #AtspiAccessible instance to query.
2009  *
2010  * Gets the #AtspiTable interface for an #AtspiAccessible.
2011  *
2012  * Returns: (transfer full): a pointer to an #AtspiValue interface instance, or
2013  *          NULL if @obj does not implement #AtspiValue.
2014  *
2015  * Deprecated: 2.10: Use atspi_accessible_get_value_iface instead.
2016  **/
2017 AtspiValue *
2018 atspi_accessible_get_value (AtspiAccessible *accessible)
2019 {
2020   return (_atspi_accessible_is_a (accessible, atspi_interface_value) ?
2021           g_object_ref (ATSPI_VALUE (accessible)) : NULL);
2022 }
2023
2024 /**
2025  * atspi_accessible_get_value_iface:
2026  * @obj: a pointer to the #AtspiAccessible instance to query.
2027  *
2028  * Gets the #AtspiTable interface for an #AtspiAccessible.
2029  *
2030  * Returns: (transfer full): a pointer to an #AtspiValue interface instance, or
2031  *          NULL if @obj does not implement #AtspiValue.
2032  **/
2033 AtspiValue *
2034 atspi_accessible_get_value_iface (AtspiAccessible *accessible)
2035 {
2036   return (_atspi_accessible_is_a (accessible, atspi_interface_value) ?
2037           g_object_ref (ATSPI_VALUE (accessible)) : NULL);
2038 }
2039
2040 static void
2041 append_const_val (GArray *array, const gchar *val)
2042 {
2043   gchar *dup = g_strdup (val);
2044
2045   if (dup)
2046     g_array_append_val (array, dup);
2047 }
2048
2049 /**
2050  * atspi_accessible_get_interfaces:
2051  * @obj: The #AtspiAccessible to query.
2052  *
2053  * A set of pointers to all interfaces supported by an #AtspiAccessible.
2054  *
2055  * Returns: (element-type gchar*) (transfer full): A #GArray of strings
2056  *          describing the interfaces supported by the object.  Interfaces are
2057  *          denoted in short-hand (i.e. "Component", "Text" etc.).
2058  **/
2059 GArray *
2060 atspi_accessible_get_interfaces (AtspiAccessible *obj)
2061 {
2062   GArray *ret = g_array_new (TRUE, TRUE, sizeof (gchar *));
2063
2064   g_return_val_if_fail (obj != NULL, NULL);
2065
2066   append_const_val (ret, "Accessible");
2067   if (atspi_accessible_is_action (obj))
2068     append_const_val (ret, "Action");
2069   if (atspi_accessible_is_collection (obj))
2070     append_const_val (ret, "Collection");
2071   if (atspi_accessible_is_component (obj))
2072     append_const_val (ret, "Component");
2073   if (atspi_accessible_is_document (obj))
2074     append_const_val (ret, "Document");
2075   if (atspi_accessible_is_editable_text (obj))
2076     append_const_val (ret, "EditableText");
2077   if (atspi_accessible_is_hypertext (obj))
2078     append_const_val (ret, "Hypertext");
2079   if (atspi_accessible_is_hyperlink (obj))
2080     append_const_val (ret, "Hyperlink");
2081   if (atspi_accessible_is_image (obj))
2082     append_const_val (ret, "Image");
2083   if (atspi_accessible_is_selection (obj))
2084     append_const_val (ret, "Selection");
2085   if (atspi_accessible_is_table (obj))
2086     append_const_val (ret, "Table");
2087   if (atspi_accessible_is_table_cell (obj))
2088     append_const_val (ret, "TableCell");
2089   if (atspi_accessible_is_text (obj))
2090     append_const_val (ret, "Text");
2091   if (atspi_accessible_is_value (obj))
2092     append_const_val (ret, "Value");
2093
2094   return ret;
2095 }
2096
2097 AtspiAccessible *
2098 _atspi_accessible_new (AtspiApplication *app, const gchar *path)
2099 {
2100   AtspiAccessible *accessible;
2101
2102   accessible = g_object_new (ATSPI_TYPE_ACCESSIBLE, NULL);
2103   g_return_val_if_fail (accessible != NULL, NULL);
2104
2105   accessible->parent.app = g_object_ref (app);
2106   accessible->parent.path = g_strdup (path);
2107
2108   return accessible;
2109 }
2110
2111 /**
2112  * atspi_accessible_set_cache_mask:
2113  * @accessible: The #AtspiAccessible to operate on.  Must be the desktop or
2114  *             the root of an application.
2115  * @mask: An #AtspiCache specifying a bit mask of the types of data to cache.
2116  *
2117  * Sets the type of data to cache for accessibles.
2118  * If this is not set for an application or is reset to ATSPI_CACHE_UNDEFINED,
2119  * then the desktop's cache flag will be used.
2120  * If the desktop's cache flag is also undefined, then all possible data will
2121  * be cached.
2122  * This function is intended to work around bugs in toolkits where the proper
2123  * events are not raised / to aid in testing for such bugs.
2124  **/
2125 void
2126 atspi_accessible_set_cache_mask (AtspiAccessible *accessible, AtspiCache mask)
2127 {
2128   g_return_if_fail (accessible != NULL);
2129   g_return_if_fail (accessible->parent.app != NULL);
2130   g_return_if_fail (accessible == accessible->parent.app->root);
2131   accessible->parent.app->cache = mask;
2132   enable_caching = TRUE;
2133 }
2134
2135 /**
2136  * atspi_accessible_clear_cache:
2137  * @accessible: The #AtspiAccessible whose cache to clear.
2138  *
2139  * Clears the cached information for the given accessible and all of its
2140  * descendants.
2141  */
2142 void
2143 atspi_accessible_clear_cache (AtspiAccessible *accessible)
2144 {
2145   GList *l;
2146
2147   if (accessible)
2148   {
2149     accessible->cached_properties = ATSPI_CACHE_NONE;
2150     for (l = accessible->children; l; l = l->next)
2151       atspi_accessible_clear_cache (l->data);
2152   }
2153 }
2154
2155 /**
2156  * atspi_accessible_get_process_id:
2157  * @accessible: The #AtspiAccessible to query.
2158  * @error: a pointer to a %NULL #GError pointer
2159  *
2160  * Returns the process id associated with the given accessible.  Mainly
2161  * added for debugging; it is a shortcut to explicitly querying the
2162  * accessible's app->bus_name and then calling GetConnectionUnixProcessID.
2163  *
2164  * Returns: The process ID or undetermined value if @error is set.
2165  **/
2166 guint
2167 atspi_accessible_get_process_id (AtspiAccessible *accessible, GError **error)
2168 {
2169   DBusMessage *message, *reply;
2170   DBusConnection *bus = _atspi_bus ();
2171   dbus_uint32_t pid = -1;
2172   DBusError d_error;
2173
2174   if (!accessible->parent.app || !accessible->parent.app->bus_name)
2175     {
2176       g_set_error_literal(error, ATSPI_ERROR, ATSPI_ERROR_IPC, "Process is defunct");
2177       return -1;
2178     }
2179
2180   message = dbus_message_new_method_call ("org.freedesktop.DBus",
2181                                           "/org/freedesktop/DBus",
2182                                           "org.freedesktop.DBus",
2183                                           "GetConnectionUnixProcessID");
2184   dbus_message_append_args (message, DBUS_TYPE_STRING,
2185                             &accessible->parent.app->bus_name,
2186                             DBUS_TYPE_INVALID);
2187   dbus_error_init (&d_error);
2188   reply = dbus_connection_send_with_reply_and_block (bus, message, -1, &d_error);
2189   dbus_message_unref (message);
2190   if (reply)
2191   {
2192     if (!strcmp (dbus_message_get_signature (reply), "u"))
2193       dbus_message_get_args (reply, NULL, DBUS_TYPE_UINT32, &pid, DBUS_TYPE_INVALID);
2194     dbus_message_unref (reply);
2195   }
2196   if (dbus_error_is_set (&d_error))
2197     {
2198       g_set_error_literal(error, ATSPI_ERROR, ATSPI_ERROR_IPC, "Process is defunct");
2199       dbus_error_free (&d_error);
2200     }
2201   return pid;
2202 }
2203
2204 AtspiCache
2205 _atspi_accessible_get_cache_mask (AtspiAccessible *accessible)
2206 {
2207   AtspiCache mask;
2208
2209   if (!accessible->parent.app)
2210     return ATSPI_CACHE_NONE;
2211
2212  mask = accessible->parent.app->cache;
2213   if (mask == ATSPI_CACHE_UNDEFINED &&
2214       accessible->parent.app->root &&
2215       accessible->parent.app->root->accessible_parent)
2216   {
2217     AtspiAccessible *desktop = atspi_get_desktop (0);
2218     mask = desktop->parent.app->cache;
2219     g_object_unref (desktop);
2220   }
2221
2222   if (mask == ATSPI_CACHE_UNDEFINED)
2223     mask = ATSPI_CACHE_DEFAULT;
2224
2225   return mask;
2226 }
2227
2228 gboolean
2229 _atspi_accessible_test_cache (AtspiAccessible *accessible, AtspiCache flag)
2230 {
2231   AtspiCache mask = _atspi_accessible_get_cache_mask (accessible);
2232   AtspiCache result = accessible->cached_properties & mask & flag;
2233   if (accessible->states && atspi_state_set_contains (accessible->states, ATSPI_STATE_TRANSIENT))
2234     return FALSE;
2235   return (result != 0 && (atspi_main_loop || enable_caching ||
2236                           flag == ATSPI_CACHE_INTERFACES) &&
2237           !atspi_no_cache);
2238 }
2239
2240 void
2241 _atspi_accessible_add_cache (AtspiAccessible *accessible, AtspiCache flag)
2242 {
2243   AtspiCache mask = _atspi_accessible_get_cache_mask (accessible);
2244
2245   accessible->cached_properties |= flag & mask;
2246 }
2247
2248 /**
2249  * atspi_accessible_get_locale:
2250  * @accessible: an #AtspiAccessible
2251  *
2252  * Gets a UTF-8 string indicating the POSIX-style LC_MESSAGES locale
2253  * of @accessible.
2254  *
2255  * Since: 2.7.91
2256  *
2257  * Returns: a UTF-8 string indicating the POSIX-style LC_MESSAGES
2258  *          locale of @accessible.
2259  **/
2260 const gchar*
2261 atspi_accessible_get_object_locale (AtspiAccessible *accessible, GError **error)
2262 {
2263   gchar *locale;
2264
2265   g_return_val_if_fail (accessible != NULL, NULL);
2266
2267   locale = g_object_get_qdata (G_OBJECT (accessible), quark_locale);
2268   if (!locale)
2269   {
2270     if (!_atspi_dbus_get_property (accessible, atspi_interface_accessible,
2271                                    "Locale", error, "s", &locale))
2272       return NULL;
2273     if (locale)
2274       g_object_set_qdata_full (G_OBJECT (accessible), quark_locale, locale,
2275                                g_free);
2276   }
2277   return locale;
2278 }
2279
2280 void
2281 free_value (gpointer data)
2282 {
2283   GValue *value = data;
2284
2285   g_value_unset (value);
2286   g_free (value);
2287 }
2288
2289 GHashTable *
2290 _atspi_accessible_ref_cache (AtspiAccessible *accessible)
2291 {
2292   AtspiAccessiblePrivate *priv = accessible->priv;
2293
2294   priv->cache_ref_count++;
2295   if (priv->cache)
2296     return g_hash_table_ref (priv->cache);
2297   priv->cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
2298                                        free_value);
2299   return priv->cache;
2300 }
2301
2302 void
2303 _atspi_accessible_unref_cache (AtspiAccessible *accessible)
2304 {
2305   AtspiAccessiblePrivate *priv = accessible->priv;
2306
2307   if (priv->cache)
2308   {
2309     g_hash_table_unref (priv->cache);
2310     if (--priv->cache_ref_count == 0)
2311       priv->cache = NULL;
2312   }
2313 }