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