Add navigation helper functions for screen-reader and friends (part 2)
[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
344   g_return_val_if_fail (root != NULL, NULL);
345   do {
346     reply = _atspi_dbus_call_partial (root, atspi_interface_accessible, "GetNavigableAtPoint", error, "iiu", d_x, d_y, d_ctype);
347
348     AtspiAccessible *tmp = _atspi_dbus_return_accessible_and_recurse_info_from_message (reply, &recurse);
349     if (!tmp) break;
350     if (return_value)
351       g_object_unref(return_value);
352     return_value = root = tmp;
353   } while(recurse);
354   return return_value;
355 }
356
357 static unsigned char are_objects_on_the_same_bus(AtspiAccessible *obj1, AtspiAccessible *obj2)
358 {
359   const char *bus_name_1 = obj1->parent.app->bus_name;
360   const char *bus_name_2 = obj2->parent.app->bus_name;
361   return strcmp(bus_name_1, bus_name_2) == 0;
362 }
363
364 static unsigned char object_is_valid(AtspiAccessible *obj)
365 {
366   if (!obj)
367     return 0;
368   AtspiStateSet *ss = atspi_accessible_get_state_set(obj);
369   if (!ss)
370     return 0;
371   unsigned char valid = atspi_state_set_contains(ss, ATSPI_STATE_DEFUNCT) == 0;
372   g_object_unref(ss);
373   return valid;
374 }
375
376 typedef enum {
377   NEIGHBOR_SEARCH_MODE_NORMAL = 0,
378   NEIGHBOR_SEARCH_MODE_RECURSE_FROM_ROOT = 1,
379   NEIGHBOR_SEARCH_MODE_CONTINUE_AFTER_FAILED_RECURSING = 2,
380 } GetNeighborSearchMode;
381 /**
382  * atspi_accessible_get_neighbor:
383  * @root: a pointer to a #AtspiAccessible, which represents current root of subtree to search
384  * @start: a pointer to the #AtspiAccessible to start search from (can be null, which means start from root)
385  * @direction: direction, in which search (forward or backward)
386  *
387  * Calculates next (or previous) accessible element in logical order or null if none found.
388  *
389  * Returns: (nullable) (transfer full): a pointer to an
390  *          #AtspiAccessible element, which is next (previous) in logical order or null if none found.
391  **/
392 AtspiAccessible *
393 atspi_accessible_get_neighbor (AtspiAccessible *root,
394                                           AtspiAccessible *start,
395                                           AtspiNeighborSearchDirection direction,
396                                           GError **error)
397 {
398   g_return_val_if_fail (object_is_valid(root), NULL);
399   if (!object_is_valid(start))
400     start = root;
401   const char *root_path = ATSPI_OBJECT(root)->path;
402   AtspiAccessible *return_value = NULL;
403   g_object_ref(start);
404   unsigned char recurse;
405   GetNeighborSearchMode search_mode = NEIGHBOR_SEARCH_MODE_NORMAL;
406   GQueue *children_root_stack = g_queue_new();
407
408   while(1) {
409     const char *path = are_objects_on_the_same_bus(root, start) ? root_path : "";
410     DBusMessage *reply = _atspi_dbus_call_partial (start, atspi_interface_accessible, "GetNeighbor", error, "sii", path, (int)direction, (int)search_mode);
411     AtspiAccessible *ret = _atspi_dbus_return_accessible_and_recurse_info_from_message (reply, &recurse);
412     // got return value and request for recursive search, it means ret is on another bridge, than start
413     // thus we're recursing. should the recurse failed to find anything it will end with
414     if (ret && recurse) {
415       g_object_unref(G_OBJECT(start));
416       g_queue_push_tail(children_root_stack, ret);
417       start = ret;
418       g_object_ref(start);
419       search_mode = NEIGHBOR_SEARCH_MODE_RECURSE_FROM_ROOT;
420       continue;
421     }
422     // found the one we've been looking for
423     if (ret) {
424       g_object_unref(G_OBJECT(start));
425       return_value = ret;
426       break;
427     }
428
429     // we've stepped into different bridges previously and now we're going back to the last one
430     // and continuing search where we left
431     if (!g_queue_is_empty(children_root_stack)) {
432       g_object_unref(G_OBJECT(start));
433       start = g_queue_pop_tail(children_root_stack);
434       search_mode = NEIGHBOR_SEARCH_MODE_CONTINUE_AFTER_FAILED_RECURSING;
435       continue;
436     }
437     // there's no more bridges to check, but we might have started from one
438     // in that case there might be bridges "below" start, which we yet have to visit
439     if (!are_objects_on_the_same_bus(root, start)) {
440       unsigned char continue_loop = 1;
441       while(continue_loop) {
442         AtspiAccessible *parent = atspi_accessible_get_parent(start, NULL);
443         continue_loop = parent ? are_objects_on_the_same_bus(start, parent) : 0;
444         g_object_unref(G_OBJECT(start));
445         start = parent;
446       }
447       // going up thru parents put us in weird place (we didnt meet root on the way)
448       // so we bail out
449       if (!start)
450         break;
451
452       // start object now points to different bridge and must be treated as "resume after recursing"
453       search_mode = NEIGHBOR_SEARCH_MODE_CONTINUE_AFTER_FAILED_RECURSING;
454       continue;
455     }
456
457     // nothing found
458     g_object_unref(start);
459     return_value = NULL;
460     break;
461   }
462   while(!g_queue_is_empty(children_root_stack))
463     g_object_unref(g_queue_pop_tail(children_root_stack));
464   g_queue_free(children_root_stack);
465
466   return return_value;
467 }
468
469 /**
470  * atspi_accessible_get_description:
471  * @obj: a pointer to the #AtspiAccessible object on which to operate.
472  *
473  * Gets the description of an #AtspiAccessible object.
474  *
475  * Returns: a UTF-8 string describing the #AtspiAccessible object
476  * or NULL on exception.
477  **/
478 gchar *
479 atspi_accessible_get_description (AtspiAccessible *obj, GError **error)
480 {
481   g_return_val_if_fail (obj != NULL, g_strdup (""));
482
483   if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_DESCRIPTION))
484   {
485     if (!_atspi_dbus_get_property (obj, atspi_interface_accessible,
486                                    "Description", error, "s",
487                                    &obj->description))
488       return g_strdup ("");
489     _atspi_accessible_add_cache (obj, ATSPI_CACHE_DESCRIPTION);
490   }
491   return g_strdup (obj->description);
492 }
493
494 const char *str_parent = "Parent";
495
496 /**
497  * atspi_accessible_get_parent:
498  * @obj: a pointer to the #AtspiAccessible object to query.
499  *
500  * Gets an #AtspiAccessible object's parent container.
501  *
502  * Returns: (nullable) (transfer full): a pointer to the
503  *          #AtspiAccessible object which contains the given
504  *          #AtspiAccessible instance, or NULL if the @obj has no
505  *          parent container.
506  *
507  **/
508 AtspiAccessible *
509 atspi_accessible_get_parent (AtspiAccessible *obj, GError **error)
510 {
511   g_return_val_if_fail (obj != NULL, NULL);
512
513   if (obj->parent.app &&
514       !_atspi_accessible_test_cache (obj, ATSPI_CACHE_PARENT))
515   {
516     DBusMessage *message, *reply;
517     DBusMessageIter iter, iter_variant;
518     message = dbus_message_new_method_call (obj->parent.app->bus_name,
519                                             obj->parent.path,
520                                             DBUS_INTERFACE_PROPERTIES, "Get");
521     if (!message)
522       return NULL;
523     dbus_message_append_args (message, DBUS_TYPE_STRING, &atspi_interface_accessible,
524                                DBUS_TYPE_STRING, &str_parent,
525                               DBUS_TYPE_INVALID);
526     reply = _atspi_dbus_send_with_reply_and_block (message, error);
527     if (!reply)
528       return NULL;
529     if (strcmp (dbus_message_get_signature (reply), "v") != 0)
530     {
531       dbus_message_unref (reply);
532       return NULL;
533     }
534     dbus_message_iter_init (reply, &iter);
535     dbus_message_iter_recurse (&iter, &iter_variant);
536     obj->accessible_parent = _atspi_dbus_return_accessible_from_iter (&iter_variant);
537     dbus_message_unref (reply);
538     _atspi_accessible_add_cache (obj, ATSPI_CACHE_PARENT);
539   }
540   if (!obj->accessible_parent)
541     return NULL;
542   return g_object_ref (obj->accessible_parent);
543 }
544
545 /**
546  * atspi_accessible_get_child_count:
547  * @obj: a pointer to the #AtspiAccessible object on which to operate.
548  *
549  * Gets the number of children contained by an #AtspiAccessible object.
550  *
551  * Returns: a #long indicating the number of #AtspiAccessible children
552  *          contained by an #AtspiAccessible object or -1 on exception.
553  *
554  **/
555 gint
556 atspi_accessible_get_child_count (AtspiAccessible *obj, GError **error)
557 {
558   g_return_val_if_fail (obj != NULL, -1);
559
560   if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_CHILDREN))
561   {
562     dbus_int32_t ret;
563     if (!_atspi_dbus_get_property (obj, atspi_interface_accessible,
564                                    "ChildCount", error, "i", &ret))
565       return -1;
566     return ret;
567   }
568
569   return g_list_length (obj->children);
570 }
571
572 /**
573  * atspi_accessible_get_child_at_index:
574  * @obj: a pointer to the #AtspiAccessible object on which to operate.
575  * @child_index: a #long indicating which child is specified.
576  *
577  * Gets the #AtspiAccessible child of an #AtspiAccessible object at a given index.
578  *
579  * Returns: (transfer full): a pointer to the #AtspiAccessible child object at
580  * index @child_index or NULL on exception.
581  **/
582 AtspiAccessible *
583 atspi_accessible_get_child_at_index (AtspiAccessible *obj,
584                             gint    child_index,
585                             GError **error)
586 {
587   AtspiAccessible *child;
588
589   g_return_val_if_fail (obj != NULL, NULL);
590
591   if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_CHILDREN))
592   {
593     DBusMessage *reply;
594     reply = _atspi_dbus_call_partial (obj, atspi_interface_accessible,
595                                      "GetChildAtIndex", error, "i",
596                                      child_index);
597     return _atspi_dbus_return_accessible_from_message (reply);
598   }
599
600   child = g_list_nth_data (obj->children, child_index);
601   if (!child)
602     return NULL;
603   return g_object_ref (child);
604 }
605
606 /**
607  * atspi_accessible_get_index_in_parent:
608  * @obj: a pointer to the #AtspiAccessible object on which to operate.
609  *
610  * Gets the index of an #AtspiAccessible object within its parent's
611  * #AtspiAccessible children list.
612  *
613  * Returns: a #glong indicating the index of the #AtspiAccessible object
614  *          in its parent,
615  *          or -1 if @obj has no containing parent or on exception.
616  **/
617 gint
618 atspi_accessible_get_index_in_parent (AtspiAccessible *obj, GError **error)
619 {
620   GList *l;
621   gint i = 0;
622
623   g_return_val_if_fail (obj != NULL, -1);
624   if (_atspi_accessible_test_cache (obj, ATSPI_CACHE_PARENT) &&
625       !obj->accessible_parent)
626     return -1;
627   if (!obj->accessible_parent ||
628       !_atspi_accessible_test_cache (obj->accessible_parent,
629                                      ATSPI_CACHE_CHILDREN))
630   {
631     dbus_int32_t ret = -1;
632     _atspi_dbus_call (obj, atspi_interface_accessible,
633                       "GetIndexInParent", NULL, "=>i", &ret);
634     return ret;
635   }
636
637   l = obj->accessible_parent->children;
638   while (l)
639   {
640     if (l->data == obj) return i;
641     l = g_list_next (l);
642     i++;
643   }
644   return -1;
645 }
646
647 typedef struct
648 {
649   dbus_uint32_t type;
650   GArray *targets;
651 } Accessibility_Relation;
652
653 /**
654  * atspi_accessible_get_relation_set:
655  * @obj: a pointer to the #AtspiAccessible object on which to operate.
656  *
657  * Gets the set of #AtspiRelation objects which describes this #AtspiAccessible object's
658  * relationships with other #AtspiAccessible objects.
659  *
660  * Returns: (element-type AtspiRelation*) (transfer full): a #GArray of
661  *          #AtspiRelation pointers or NULL on exception.
662  **/
663 GArray *
664 atspi_accessible_get_relation_set (AtspiAccessible *obj, GError **error)
665 {
666   DBusMessage *reply;
667   DBusMessageIter iter, iter_array;
668   GArray *ret;
669
670   g_return_val_if_fail (obj != NULL, NULL);
671
672   reply = _atspi_dbus_call_partial (obj, atspi_interface_accessible, "GetRelationSet", error, "");
673   if (!reply)
674     return NULL;
675   _ATSPI_DBUS_CHECK_SIG (reply, "a(ua(so))", error, NULL);
676
677   ret = g_array_new (TRUE, TRUE, sizeof (AtspiRelation *));
678   dbus_message_iter_init (reply, &iter);
679   dbus_message_iter_recurse (&iter, &iter_array);
680   while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
681   {
682     AtspiRelation *relation;
683     relation = _atspi_relation_new_from_iter (&iter_array);
684     ret = g_array_append_val (ret, relation);
685     dbus_message_iter_next (&iter_array);
686   }
687   dbus_message_unref (reply);
688   return ret;
689 }
690
691 /**
692  * atspi_accessible_get_role:
693  * @obj: a pointer to the #AtspiAccessible object on which to operate.
694  *
695  * Gets the UI role played by an #AtspiAccessible object.
696  * This role's name can be obtained via atspi_accessible_get_role_name ().
697  *
698  * Returns: the #AtspiRole of an #AtspiAccessible object.
699  *
700  **/
701 AtspiRole
702 atspi_accessible_get_role (AtspiAccessible *obj, GError **error)
703 {
704   g_return_val_if_fail (obj != NULL, ATSPI_ROLE_INVALID);
705
706   if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_ROLE))
707   {
708     dbus_uint32_t role;
709     /* TODO: Make this a property */
710     if (_atspi_dbus_call (obj, atspi_interface_accessible, "GetRole", error, "=>u", &role))
711     {
712       obj->role = role;
713     _atspi_accessible_add_cache (obj, ATSPI_CACHE_ROLE);
714     }
715   }
716   return obj->role;
717 }
718
719 /**
720  * atspi_accessible_get_role_name:
721  * @obj: a pointer to the #AtspiAccessible object on which to operate.
722  *
723  * Gets a UTF-8 string corresponding to the name of the role played by an object.
724  * This method will return useful values for roles that fall outside the
725  * enumeration used in atspi_accessible_get_role ().
726  *
727  * Returns: a UTF-8 string specifying the type of UI role played by an
728  * #AtspiAccessible object.
729  *
730  **/
731 gchar *
732 atspi_accessible_get_role_name (AtspiAccessible *obj, GError **error)
733 {
734   gchar *retval = NULL;
735   AtspiRole role;
736
737   g_return_val_if_fail (obj != NULL, NULL);
738
739   role = atspi_accessible_get_role (obj, error);
740   if (role >= 0 && role < ATSPI_ROLE_COUNT && role != ATSPI_ROLE_EXTENDED)
741     return atspi_role_get_name (role);
742
743   _atspi_dbus_call (obj, atspi_interface_accessible, "GetRoleName", error, "=>s", &retval);
744
745   if (!retval)
746     retval = g_strdup ("");
747
748   return retval;
749 }
750
751 /**
752  * atspi_accessible_get_localized_role_name:
753  * @obj: a pointer to the #AtspiAccessible object on which to operate.
754  *
755  * Gets a UTF-8 string corresponding to the name of the role played by an
756  * object, translated to the current locale.
757  * This method will return useful values for roles that fall outside the
758  * enumeration used in atspi_accessible_getRole ().
759  *
760  * Returns: a localized, UTF-8 string specifying the type of UI role played
761  * by an #AtspiAccessible object.
762  *
763  **/
764 gchar *
765 atspi_accessible_get_localized_role_name (AtspiAccessible *obj, GError **error)
766 {
767   char *retval = NULL;
768
769   g_return_val_if_fail (obj != NULL, NULL);
770
771   _atspi_dbus_call (obj, atspi_interface_accessible, "GetLocalizedRoleName", error, "=>s", &retval);
772
773   if (!retval)
774     return g_strdup ("");
775
776   return retval;
777 }
778
779 static AtspiStateSet *
780 defunct_set ()
781 {
782   AtspiStateSet *set = atspi_state_set_new (NULL);
783   atspi_state_set_add (set, ATSPI_STATE_DEFUNCT);
784   return set;
785 }
786
787 /**
788  * atspi_accessible_get_state_set:
789  * @obj: a pointer to the #AtspiAccessible object on which to operate.
790  *
791  * Gets the states currently held by an object.
792  *
793  * Returns: (transfer full): a pointer to an #AtspiStateSet representing an
794  * object's current state set.
795  **/
796 AtspiStateSet *
797 atspi_accessible_get_state_set (AtspiAccessible *obj)
798 {
799   /* TODO: Should take a GError **, but would be an API break */
800   if (!obj->parent.app || !obj->parent.app->bus)
801     return defunct_set ();
802
803   if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_STATES))
804   {
805     DBusMessage *reply;
806     DBusMessageIter iter;
807     reply = _atspi_dbus_call_partial (obj, atspi_interface_accessible,
808                                       "GetState", NULL, "");
809     _ATSPI_DBUS_CHECK_SIG (reply, "au", NULL, defunct_set ());
810     dbus_message_iter_init (reply, &iter);
811     _atspi_dbus_set_state (obj, &iter);
812     dbus_message_unref (reply);
813     _atspi_accessible_add_cache (obj, ATSPI_CACHE_STATES);
814   }
815   return g_object_ref (obj->states);
816 }
817
818 /**
819  * atspi_accessible_get_attributes:
820  * @obj: The #AtspiAccessible being queried.
821  *
822  * Gets the #AttributeSet representing any assigned
823  * name-value pair attributes or annotations for this object.
824  * For typographic, textual, or textually-semantic attributes, see
825  * atspi_text_get_attributes instead.
826  *
827  * Returns: (element-type gchar* gchar*) (transfer full): The name-value-pair
828  * attributes assigned to this object.
829  */
830 GHashTable *
831 atspi_accessible_get_attributes (AtspiAccessible *obj, GError **error)
832 {
833   DBusMessage *message;
834
835     g_return_val_if_fail (obj != NULL, NULL);
836
837   if (obj->priv->cache)
838   {
839     GValue *val = g_hash_table_lookup (obj->priv->cache, "Attributes");
840     if (val)
841       return g_value_dup_boxed (val);
842   }
843
844   if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_ATTRIBUTES))
845   {
846     message = _atspi_dbus_call_partial (obj, atspi_interface_accessible,
847                                         "GetAttributes", error, "");
848     obj->attributes = _atspi_dbus_return_hash_from_message (message);
849     _atspi_accessible_add_cache (obj, ATSPI_CACHE_ATTRIBUTES);
850   }
851
852   if (!obj->attributes)
853     return NULL;
854   return g_hash_table_ref (obj->attributes);
855 }
856
857 static void
858 add_to_attribute_array (gpointer key, gpointer value, gpointer data)
859 {
860   GArray **array = (GArray **)data;
861   gchar *str = g_strconcat (key, ":", value, NULL);
862   *array = g_array_append_val (*array, str);
863 }
864
865 /**
866  * atspi_accessible_get_attributes_as_array:
867  * @obj: The #AtspiAccessible being queried.
868  *
869  * Gets a #GArray representing any assigned
870  * name-value pair attributes or annotations for this object.
871  * For typographic, textual, or textually-semantic attributes, see
872  * atspi_text_get_attributes_as_array instead.
873  *
874  * Returns: (element-type gchar*) (transfer full): The name-value-pair
875  *          attributes assigned to this object.
876  */
877 GArray *
878 atspi_accessible_get_attributes_as_array (AtspiAccessible *obj, GError **error)
879 {
880   DBusMessage *message;
881
882     g_return_val_if_fail (obj != NULL, NULL);
883
884   if (obj->priv->cache)
885   {
886     GValue *val = g_hash_table_lookup (obj->priv->cache, "Attributes");
887     if (val)
888     {
889       GArray *array = g_array_new (TRUE, TRUE, sizeof (gchar *));
890       GHashTable *attributes = g_value_get_boxed (val);
891       g_hash_table_foreach (attributes, add_to_attribute_array, &array);
892       return array;
893     }
894   }
895
896   message = _atspi_dbus_call_partial (obj, atspi_interface_accessible, "GetAttributes", error, "");
897   return _atspi_dbus_return_attribute_array_from_message (message);
898 }
899
900 /**
901  * atspi_accessible_get_application:
902  * @obj: The #AtspiAccessible being queried.
903  *
904  * Gets the containing #AtspiApplication for an object.
905  *
906  * Returns: (transfer full): the containing #AtspiApplication instance for
907  *          this object.
908  */
909 AtspiAccessible *
910 atspi_accessible_get_application (AtspiAccessible *obj, GError **error)
911 {
912   AtspiAccessible *parent;
913
914   g_object_ref (obj);
915   for (;;)
916   {
917     parent = atspi_accessible_get_parent (obj, NULL);
918     if (!parent && obj->parent.app &&
919         atspi_accessible_get_role (obj, NULL) != ATSPI_ROLE_APPLICATION)
920     {
921       AtspiAccessible *root = g_object_ref (obj->parent.app->root);
922       if (root)
923       {
924         g_object_unref (obj);
925         if (atspi_accessible_get_role (root, NULL) == ATSPI_ROLE_DESKTOP_FRAME)
926         {
927           g_object_unref (root);
928           return NULL;
929         }
930         return root;
931       }
932     }
933     if (!parent || parent == obj ||
934         atspi_accessible_get_role (parent, NULL) == ATSPI_ROLE_DESKTOP_FRAME)
935   {
936     if (parent)
937       g_object_unref (parent);
938     return obj;
939   }
940     g_object_unref (obj);
941     obj = parent;
942   }
943 }
944
945 /* Application-specific methods */
946
947 /**
948  * atspi_accessible_get_toolkit_name:
949  * @obj: a pointer to the #AtspiAccessible object on which to operate.
950  *
951  * Gets the toolkit name for an #AtspiAccessible object.
952  * Only works on application root objects.
953  *
954  * Returns: a UTF-8 string indicating the toolkit name for the #AtspiAccessible object or NULL on exception.
955  **/
956 gchar *
957 atspi_accessible_get_toolkit_name (AtspiAccessible *obj, GError **error)
958 {
959   g_return_val_if_fail (obj != NULL, NULL);
960
961   if (!obj->parent.app)
962     return NULL;
963
964   if (!obj->parent.app->toolkit_name)
965     _atspi_dbus_get_property (obj, atspi_interface_application, "ToolkitName",
966                               error, "s", &obj->parent.app->toolkit_name);
967
968   return g_strdup (obj->parent.app->toolkit_name);
969 }
970
971 /**
972  * atspi_accessible_get_toolkit_version:
973  * @obj: a pointer to the #AtspiAccessible object on which to operate.
974  *
975  * Gets the toolkit version for an #AtspiAccessible object.
976  * Only works on application root objects.
977  *
978  * Returns: a UTF-8 string indicating the toolkit version for the #AtspiAccessible object or NULL on exception.
979  **/
980 gchar *
981 atspi_accessible_get_toolkit_version (AtspiAccessible *obj, GError **error)
982 {
983   g_return_val_if_fail (obj != NULL, NULL);
984
985   if (!obj->parent.app)
986     return NULL;
987
988   if (!obj->parent.app->toolkit_version)
989     _atspi_dbus_get_property (obj, atspi_interface_application, "Version",
990                               error, "s", &obj->parent.app->toolkit_version);
991
992   return g_strdup (obj->parent.app->toolkit_version);
993 }
994
995 /**
996  * atspi_accessible_get_atspi_version:
997  * @obj: a pointer to the #AtspiAccessible object on which to operate.
998  *
999  * Gets the AT-SPI IPC specification version supported by the application
1000  * pointed to by the #AtspiAccessible object.
1001  * Only works on application root objects.
1002  *
1003  * Returns: a UTF-8 string indicating the AT-SPI version for the #AtspiAccessible object or NULL on exception.
1004  **/
1005 gchar *
1006 atspi_accessible_get_atspi_version (AtspiAccessible *obj, GError **error)
1007 {
1008   g_return_val_if_fail (obj != NULL, NULL);
1009
1010   if (!obj->parent.app)
1011     return NULL;
1012
1013   if (!obj->parent.app->atspi_version)
1014     _atspi_dbus_get_property (obj, atspi_interface_application, "AtspiVersion",
1015                               error, "s", &obj->parent.app->atspi_version);
1016
1017   return g_strdup (obj->parent.app->atspi_version);
1018 }
1019
1020 /**
1021  * atspi_accessible_get_id:
1022  * @obj: a pointer to the #AtspiAccessible object on which to operate.
1023  *
1024  * Gets the application id for a #AtspiAccessible object.
1025  * Only works on application root objects.
1026  *
1027  * Returns: a positive #gint indicating the id for the #AtspiAccessible object
1028  * or -1 on exception.
1029  **/
1030 gint
1031 atspi_accessible_get_id (AtspiAccessible *obj, GError **error)
1032 {
1033   gint ret = -1;
1034
1035   g_return_val_if_fail (obj != NULL, -1);
1036
1037   if (!_atspi_dbus_get_property (obj, atspi_interface_application, "Id", error, "i", &ret))
1038       return -1;
1039   return ret;
1040 }
1041
1042
1043 /* Interface query methods */
1044
1045 static gboolean
1046 _atspi_accessible_is_a (AtspiAccessible *accessible,
1047                       const char *interface_name)
1048 {
1049   int n;
1050
1051   if (accessible == NULL)
1052     {
1053       return FALSE;
1054     }
1055
1056   if (!_atspi_accessible_test_cache (accessible, ATSPI_CACHE_INTERFACES))
1057   {
1058     DBusMessage *reply;
1059     DBusMessageIter iter;
1060     reply = _atspi_dbus_call_partial (accessible, atspi_interface_accessible,
1061                                       "GetInterfaces", NULL, "");
1062     _ATSPI_DBUS_CHECK_SIG (reply, "as", NULL, FALSE);
1063     dbus_message_iter_init (reply, &iter);
1064     _atspi_dbus_set_interfaces (accessible, &iter);
1065     dbus_message_unref (reply);
1066     _atspi_accessible_add_cache (accessible, ATSPI_CACHE_INTERFACES);
1067   }
1068
1069   n = _atspi_get_iface_num (interface_name);
1070   if (n == -1) return FALSE;
1071   return (gboolean) ((accessible->interfaces & (1 << n))? TRUE: FALSE);
1072 }
1073
1074 /**
1075  * atspi_accessible_is_action:
1076  * @obj: a pointer to the #AtspiAccessible instance to query.
1077  *
1078  * Query whether the specified #AtspiAccessible implements the
1079  * #AtspiAction interface.
1080  *
1081  * Returns: #TRUE if @obj implements the #AtspiAction interface,
1082  *          #FALSE otherwise.
1083  **/
1084 gboolean
1085 atspi_accessible_is_action (AtspiAccessible *obj)
1086 {
1087   return _atspi_accessible_is_a (obj,
1088                               atspi_interface_action);
1089 }
1090
1091 /**
1092  * atspi_accessible_is_application:
1093  * @obj: a pointer to the #AtspiAccessible instance to query.
1094  *
1095  * Query whether the specified #AtspiAccessible implements the
1096  * #AtspiApplication interface.
1097  *
1098  * Returns: #TRUE if @obj implements the #AtspiApplication interface,
1099  *          #FALSE otherwise.
1100  **/
1101 gboolean
1102 atspi_accessible_is_application (AtspiAccessible *obj)
1103 {
1104   return _atspi_accessible_is_a (obj,
1105                               atspi_interface_application);
1106 }
1107
1108 /**
1109  * atspi_accessible_is_collection:
1110  * @obj: a pointer to the #AtspiAccessible instance to query.
1111  *
1112  * Query whether the specified #AtspiAccessible implements the
1113  * #AtspiCollection interface.
1114  *
1115  * Returns: #TRUE if @obj implements the #AtspiCollection interface,
1116  *          #FALSE otherwise.
1117  **/
1118 gboolean
1119 atspi_accessible_is_collection (AtspiAccessible *obj)
1120 {
1121      return _atspi_accessible_is_a (obj,
1122                               atspi_interface_collection);
1123 }
1124
1125 /**
1126  * atspi_accessible_is_component:
1127  * @obj: a pointer to the #AtspiAccessible instance to query.
1128  *
1129  * Query whether the specified #AtspiAccessible implements #AtspiComponent.
1130  *
1131  * Returns: #TRUE if @obj implements the #AtspiComponent interface,
1132  *          #FALSE otherwise.
1133  **/
1134 gboolean
1135 atspi_accessible_is_component (AtspiAccessible *obj)
1136 {
1137   return _atspi_accessible_is_a (obj,
1138                               atspi_interface_component);
1139 }
1140
1141 /**
1142  * atspi_accessible_is_document:
1143  * @obj: a pointer to the #AtspiAccessible instance to query.
1144  *
1145  * Query whether the specified #AtspiAccessible implements the
1146  * #AtspiDocument interface.
1147  *
1148  * Returns: #TRUE if @obj implements the #AtspiDocument interface,
1149  *          #FALSE otherwise.
1150  **/
1151 gboolean
1152 atspi_accessible_is_document (AtspiAccessible *obj)
1153 {
1154   return _atspi_accessible_is_a (obj,
1155                               atspi_interface_document);
1156 }
1157
1158 /**
1159  * atspi_accessible_is_editable_text:
1160  * @obj: a pointer to the #AtspiAccessible instance to query.
1161  *
1162  * Query whether the specified #AtspiAccessible implements the
1163  * #AtspiEditableText interface.
1164  *
1165  * Returns: #TRUE if @obj implements the #AtspiEditableText interface,
1166  *          #FALSE otherwise.
1167  **/
1168 gboolean
1169 atspi_accessible_is_editable_text (AtspiAccessible *obj)
1170 {
1171   return _atspi_accessible_is_a (obj,
1172                               atspi_interface_editable_text);
1173 }
1174
1175 /**
1176  * atspi_accessible_is_hypertext:
1177  * @obj: a pointer to the #AtspiAccessible instance to query.
1178  *
1179  * Query whether the specified #AtspiAccessible implements the
1180  * #AtspiHypertext interface.
1181  *
1182  * Returns: #TRUE if @obj implements the #AtspiHypertext interface,
1183  *          #FALSE otherwise.
1184  **/
1185 gboolean
1186 atspi_accessible_is_hypertext (AtspiAccessible *obj)
1187 {
1188   return _atspi_accessible_is_a (obj,
1189                               atspi_interface_hypertext);
1190 }
1191
1192 /**
1193  * atspi_accessible_is_hyperlink:
1194  * @obj: a pointer to the #AtspiAccessible instance to query.
1195  *
1196  * Query whether the specified #AtspiAccessible implements the
1197  * #AtspiHyperlink interface.
1198  *
1199  * Returns: #TRUE if @obj implements the #AtspiHypertext interface,
1200  *          #FALSE otherwise.
1201  **/
1202 gboolean
1203 atspi_accessible_is_hyperlink (AtspiAccessible *obj)
1204 {
1205   return _atspi_accessible_is_a (obj,
1206                               atspi_interface_hyperlink);
1207 }
1208
1209 /**
1210  * atspi_accessible_is_image:
1211  * @obj: a pointer to the #AtspiAccessible instance to query.
1212  *
1213  * Query whether the specified #AtspiAccessible implements the
1214  * #AtspiImage interface.
1215  *
1216  * Returns: #TRUE if @obj implements the #AtspiImage interface,
1217  *          #FALSE otherwise.
1218 **/
1219 gboolean
1220 atspi_accessible_is_image (AtspiAccessible *obj)
1221 {
1222   return _atspi_accessible_is_a (obj,
1223                               atspi_interface_image);
1224 }
1225
1226 /**
1227  * atspi_accessible_is_selection:
1228  * @obj: a pointer to the #AtspiAccessible instance to query.
1229  *
1230  * Query whether the specified #AtspiAccessible implements the
1231  * #AtspiSelection interface.
1232  *
1233  * Returns: #TRUE if @obj implements the #AtspiSelection interface,
1234  *          #FALSE otherwise.
1235 **/
1236 gboolean
1237 atspi_accessible_is_selection (AtspiAccessible *obj)
1238 {
1239   return _atspi_accessible_is_a (obj,
1240                               atspi_interface_selection);
1241 }
1242
1243 /**
1244  * atspi_accessible_is_table:
1245  * @obj: a pointer to the #AtspiAccessible instance to query.
1246  *
1247  * Query whether the specified #AtspiAccessible implements the
1248  * #AtspiTable interface.
1249  *
1250  * Returns: #TRUE if @obj implements the #AtspiTable interface,
1251  *          #FALSE otherwise.
1252 **/
1253 gboolean
1254 atspi_accessible_is_table (AtspiAccessible *obj)
1255 {
1256   return _atspi_accessible_is_a (obj,
1257                               atspi_interface_table);
1258 }
1259
1260 /**
1261  * atspi_accessible_is_table_cell:
1262  * @obj: a pointer to the #AtspiAccessible instance to query.
1263  *
1264  * Query whether the specified #AtspiAccessible implements the
1265  * #AtspiTableCell interface.
1266  *
1267  * Returns: #TRUE if @obj implements the #AtspiTable interface,
1268  *          #FALSE otherwise.
1269 **/
1270 gboolean
1271 atspi_accessible_is_table_cell (AtspiAccessible *obj)
1272 {
1273   return _atspi_accessible_is_a (obj,
1274                               atspi_interface_table_cell);
1275 }
1276
1277 /**
1278  * atspi_accessible_is_streamable_content:
1279  * @obj: a pointer to the #AtspiAccessible instance to query.
1280  *
1281  * Query whether the specified #AtspiAccessible implements the
1282  * #AtspiStreamableContent interface.
1283  *
1284  * Returns: #TRUE if @obj implements the #AtspiStreamableContent interface,
1285  *          #FALSE otherwise.
1286 **/
1287 gboolean
1288 atspi_accessible_is_streamable_content (AtspiAccessible *obj)
1289 {
1290 #if 0
1291   return _atspi_accessible_is_a (obj,
1292                               atspi_interface_streamable_content);
1293 #else
1294   g_warning ("Streamable content not implemented");
1295   return FALSE;
1296 #endif
1297 }
1298
1299 /**
1300  * atspi_accessible_is_text:
1301  * @obj: a pointer to the #AtspiAccessible instance to query.
1302  *
1303  * Query whether the specified #AtspiAccessible implements the
1304  * #AtspiText interface.
1305  *
1306  * Returns: #TRUE if @obj implements the #AtspiText interface,
1307  *          #FALSE otherwise.
1308 **/
1309 gboolean
1310 atspi_accessible_is_text (AtspiAccessible *obj)
1311 {
1312   return _atspi_accessible_is_a (obj,
1313                               atspi_interface_text);
1314 }
1315
1316 /**
1317  * atspi_accessible_is_value:
1318  * @obj: a pointer to the #AtspiAccessible instance to query.
1319  *
1320  * Query whether the specified #AtspiAccessible implements the
1321  * #AtspiValue interface.
1322  *
1323  * Returns: #TRUE if @obj implements the #AtspiValue interface,
1324  *          #FALSE otherwise.
1325 **/
1326 gboolean
1327 atspi_accessible_is_value (AtspiAccessible *obj)
1328 {
1329   return _atspi_accessible_is_a (obj,
1330                               atspi_interface_value);
1331 }
1332
1333 /**
1334  * atspi_accessible_get_action: (rename-to atspi_accessible_get_action_iface)
1335  * @obj: a pointer to the #AtspiAccessible instance to query.
1336  *
1337  * Gets the #AtspiAction interface for an #AtspiAccessible.
1338  *
1339  * Returns: (transfer full): a pointer to an #AtspiAction interface
1340  *          instance, or NULL if @obj does not implement #AtspiAction.
1341  *
1342  * Deprecated: 2.10: Use atspi_accessible_get_action_iface instead.
1343  **/
1344 AtspiAction *
1345 atspi_accessible_get_action (AtspiAccessible *accessible)
1346 {
1347   return (_atspi_accessible_is_a (accessible, atspi_interface_action) ?
1348           g_object_ref (ATSPI_ACTION (accessible)) : NULL);
1349 }
1350
1351 /**
1352  * atspi_accessible_get_action_iface:
1353  * @obj: a pointer to the #AtspiAccessible instance to query.
1354  *
1355  * Gets the #AtspiAction interface for an #AtspiAccessible.
1356  *
1357  * Returns: (transfer full): a pointer to an #AtspiAction interface
1358  *          instance, or NULL if @obj does not implement #AtspiAction.
1359  **/
1360 AtspiAction *
1361 atspi_accessible_get_action_iface (AtspiAccessible *accessible)
1362 {
1363   return (_atspi_accessible_is_a (accessible, atspi_interface_action) ?
1364           g_object_ref (ATSPI_ACTION (accessible)) : NULL);
1365 }
1366
1367 /**
1368  * atspi_accessible_get_collection: (rename-to atspi_accessible_get_collection_iface)
1369  * @obj: a pointer to the #AtspiAccessible instance to query.
1370  *
1371  * Gets the #AtspiCollection interface for an #AtspiAccessible.
1372  *
1373  * Returns: (transfer full): a pointer to an #AtspiCollection interface
1374  *          instance, or NULL if @obj does not implement #AtspiCollection.
1375  *
1376  * Deprecated: 2.10: Use atspi_accessible_get_collection_iface instead.
1377  **/
1378 AtspiCollection *
1379 atspi_accessible_get_collection (AtspiAccessible *accessible)
1380 {
1381   return (_atspi_accessible_is_a (accessible, atspi_interface_collection) ?
1382           g_object_ref (ATSPI_COLLECTION (accessible)) : NULL);
1383 }
1384
1385 /**
1386  * atspi_accessible_get_collection_iface:
1387  * @obj: a pointer to the #AtspiAccessible instance to query.
1388  *
1389  * Gets the #AtspiCollection interface for an #AtspiAccessible.
1390  *
1391  * Returns: (transfer full): a pointer to an #AtspiCollection interface
1392  *          instance, or NULL if @obj does not implement #AtspiCollection.
1393  **/
1394 AtspiCollection *
1395 atspi_accessible_get_collection_iface (AtspiAccessible *accessible)
1396 {
1397   return (_atspi_accessible_is_a (accessible, atspi_interface_collection) ?
1398           g_object_ref (ATSPI_COLLECTION (accessible)) : NULL);
1399 }
1400
1401 /**
1402  * atspi_accessible_get_component: (rename-to atspi_accessible_get_component_iface)
1403  * @obj: a pointer to the #AtspiAccessible instance to query.
1404  *
1405  * Gets the #AtspiComponent interface for an #AtspiAccessible.
1406  *
1407  * Returns: (transfer full): a pointer to an #AtspiComponent interface
1408  *          instance, or NULL if @obj does not implement #AtspiComponent.
1409  *
1410  * Deprecated: 2.10: Use atspi_accessible_get_component_iface instead.
1411  **/
1412 AtspiComponent *
1413 atspi_accessible_get_component (AtspiAccessible *obj)
1414 {
1415   return (_atspi_accessible_is_a (obj, atspi_interface_component) ?
1416           g_object_ref (ATSPI_COMPONENT (obj)) : NULL);
1417 }
1418
1419 /**
1420  * atspi_accessible_get_component_iface:
1421  * @obj: a pointer to the #AtspiAccessible instance to query.
1422  *
1423  * Gets the #AtspiComponent interface for an #AtspiAccessible.
1424  *
1425  * Returns: (transfer full): a pointer to an #AtspiComponent interface
1426  *          instance, or NULL if @obj does not implement #AtspiComponent.
1427  **/
1428 AtspiComponent *
1429 atspi_accessible_get_component_iface (AtspiAccessible *obj)
1430 {
1431   return (_atspi_accessible_is_a (obj, atspi_interface_component) ?
1432           g_object_ref (ATSPI_COMPONENT (obj)) : NULL);
1433 }
1434
1435 /**
1436  * atspi_accessible_get_document: (rename-to atspi_accessible_get_document_iface)
1437  * @obj: a pointer to the #AtspiAccessible instance to query.
1438  *
1439  * Gets the #AtspiDocument interface for an #AtspiAccessible.
1440  *
1441  * Returns: (transfer full): a pointer to an #AtspiDocument interface
1442  *          instance, or NULL if @obj does not implement #AtspiDocument.
1443  *
1444  * Deprecated: 2.10: Use atspi_accessible_get_document_iface instead.
1445  **/
1446 AtspiDocument *
1447 atspi_accessible_get_document (AtspiAccessible *accessible)
1448 {
1449   return (_atspi_accessible_is_a (accessible, atspi_interface_document) ?
1450           g_object_ref (ATSPI_DOCUMENT (accessible)) : NULL);
1451 }
1452
1453 /**
1454  * atspi_accessible_get_document_iface:
1455  * @obj: a pointer to the #AtspiAccessible instance to query.
1456  *
1457  * Gets the #AtspiDocument interface for an #AtspiAccessible.
1458  *
1459  * Returns: (transfer full): a pointer to an #AtspiDocument interface
1460  *          instance, or NULL if @obj does not implement #AtspiDocument.
1461  **/
1462 AtspiDocument *
1463 atspi_accessible_get_document_iface (AtspiAccessible *accessible)
1464 {
1465   return (_atspi_accessible_is_a (accessible, atspi_interface_document) ?
1466           g_object_ref (ATSPI_DOCUMENT (accessible)) : NULL);
1467 }
1468
1469 /**
1470  * atspi_accessible_get_editable_text: (rename-to atspi_accessible_get_editable_text_iface)
1471  * @obj: a pointer to the #AtspiAccessible instance to query.
1472  *
1473  * Gets the #AtspiEditableText interface for an #AtspiAccessible.
1474  *
1475  * Returns: (transfer full): a pointer to an #AtspiEditableText interface
1476  *          instance, or NULL if @obj does not implement #AtspiEditableText.
1477  *
1478  * Deprecated: 2.10: Use atspi_accessible_get_editable_text_iface instead.
1479  **/
1480 AtspiEditableText *
1481 atspi_accessible_get_editable_text (AtspiAccessible *accessible)
1482 {
1483   return (_atspi_accessible_is_a (accessible, atspi_interface_editable_text) ?
1484           g_object_ref (ATSPI_EDITABLE_TEXT (accessible)) : NULL);
1485 }
1486
1487 /**
1488  * atspi_accessible_get_editable_text_iface:
1489  * @obj: a pointer to the #AtspiAccessible instance to query.
1490  *
1491  * Gets the #AtspiEditableText interface for an #AtspiAccessible.
1492  *
1493  * Returns: (transfer full): a pointer to an #AtspiEditableText interface
1494  *          instance, or NULL if @obj does not implement #AtspiEditableText.
1495  **/
1496 AtspiEditableText *
1497 atspi_accessible_get_editable_text_iface (AtspiAccessible *accessible)
1498 {
1499   return (_atspi_accessible_is_a (accessible, atspi_interface_editable_text) ?
1500           g_object_ref (ATSPI_EDITABLE_TEXT (accessible)) : NULL);
1501 }
1502
1503 /**
1504  * atspi_accessible_get_hyperlink:
1505  * @obj: a pointer to the #AtspiAccessible object on which to operate.
1506  *
1507  * Gets the #AtspiHyperlink interface for an #AtspiAccessible.
1508  *
1509  * Returns: (transfer full): the #AtspiHyperlink object associated with
1510  *          the given #AtspiAccessible, or NULL if not supported.
1511  **/
1512 AtspiHyperlink *
1513 atspi_accessible_get_hyperlink (AtspiAccessible *accessible)
1514 {
1515   return (_atspi_accessible_is_a (accessible, atspi_interface_hyperlink) ?
1516           _atspi_hyperlink_new (accessible->parent.app, accessible->parent.path) : NULL);
1517 }
1518
1519 /**
1520  * atspi_accessible_get_hypertext: (rename-to atspi_accessible_get_hypertext_iface)
1521  * @obj: a pointer to the #AtspiAccessible instance to query.
1522  *
1523  * Gets the #AtspiHypertext interface for an #AtspiAccessible.
1524  *
1525  * Returns: (transfer full): a pointer to an #AtspiHypertext interface
1526  *          instance, or NULL if @obj does not implement #AtspiHypertext.
1527  *
1528  * Deprecated: 2.10: Use atspi_accessible_get_hypertext_iface instead.
1529  **/
1530 AtspiHypertext *
1531 atspi_accessible_get_hypertext (AtspiAccessible *accessible)
1532 {
1533   return (_atspi_accessible_is_a (accessible, atspi_interface_hypertext) ?
1534           g_object_ref (ATSPI_HYPERTEXT (accessible)) : NULL);
1535 }
1536
1537 /**
1538  * atspi_accessible_get_hypertext_iface:
1539  * @obj: a pointer to the #AtspiAccessible instance to query.
1540  *
1541  * Gets the #AtspiHypertext interface for an #AtspiAccessible.
1542  *
1543  * Returns: (transfer full): a pointer to an #AtspiHypertext interface
1544  *          instance, or NULL if @obj does not implement #AtspiHypertext.
1545  **/
1546 AtspiHypertext *
1547 atspi_accessible_get_hypertext_iface (AtspiAccessible *accessible)
1548 {
1549   return (_atspi_accessible_is_a (accessible, atspi_interface_hypertext) ?
1550           g_object_ref (ATSPI_HYPERTEXT (accessible)) : NULL);
1551 }
1552
1553 /**
1554  * atspi_accessible_get_image: (rename-to atspi_accessible_get_image_iface)
1555  * @obj: a pointer to the #AtspiAccessible instance to query.
1556  *
1557  * Gets the #AtspiImage interface for an #AtspiAccessible.
1558  *
1559  * Returns: (transfer full): a pointer to an #AtspiImage interface instance, or
1560  *          NULL if @obj does not implement #AtspiImage.
1561  *
1562  * Deprecated: 2.10: Use atspi_accessible_get_image_iface instead.
1563  **/
1564 AtspiImage *
1565 atspi_accessible_get_image (AtspiAccessible *accessible)
1566 {
1567   return (_atspi_accessible_is_a (accessible, atspi_interface_image) ?
1568           g_object_ref (ATSPI_IMAGE (accessible)) : NULL);
1569 }
1570
1571 /**
1572  * atspi_accessible_get_image_iface:
1573  * @obj: a pointer to the #AtspiAccessible instance to query.
1574  *
1575  * Gets the #AtspiImage interface for an #AtspiAccessible.
1576  *
1577  * Returns: (transfer full): a pointer to an #AtspiImage interface instance, or
1578  *          NULL if @obj does not implement #AtspiImage.
1579  **/
1580 AtspiImage *
1581 atspi_accessible_get_image_iface (AtspiAccessible *accessible)
1582 {
1583   return (_atspi_accessible_is_a (accessible, atspi_interface_image) ?
1584           g_object_ref (ATSPI_IMAGE (accessible)) : NULL);
1585 }
1586
1587 /**
1588  * atspi_accessible_get_selection: (rename-to atspi_accessible_get_selection_iface)
1589  * @obj: a pointer to the #AtspiAccessible instance to query.
1590  *
1591  * Gets the #AtspiSelection interface for an #AtspiAccessible.
1592  *
1593  * Returns: (transfer full): a pointer to an #AtspiSelection interface
1594  *          instance, or NULL if @obj does not implement #AtspiSelection.
1595  *
1596  * Deprecated: 2.10: Use atspi_accessible_get_selection_iface instead.
1597  **/
1598 AtspiSelection *
1599 atspi_accessible_get_selection (AtspiAccessible *accessible)
1600 {
1601   return (_atspi_accessible_is_a (accessible, atspi_interface_selection) ?
1602           g_object_ref (ATSPI_SELECTION (accessible)) : NULL);
1603 }
1604
1605 /**
1606  * atspi_accessible_get_selection_iface:
1607  * @obj: a pointer to the #AtspiAccessible instance to query.
1608  *
1609  * Gets the #AtspiSelection interface for an #AtspiAccessible.
1610  *
1611  * Returns: (transfer full): a pointer to an #AtspiSelection interface
1612  *          instance, or NULL if @obj does not implement #AtspiSelection.
1613  **/
1614 AtspiSelection *
1615 atspi_accessible_get_selection_iface (AtspiAccessible *accessible)
1616 {
1617   return (_atspi_accessible_is_a (accessible, atspi_interface_selection) ?
1618           g_object_ref (ATSPI_SELECTION (accessible)) : NULL);
1619 }
1620
1621 #if 0
1622 /**
1623  * atspi_accessible_get_streamable_content:
1624  * @obj: a pointer to the #AtspiAccessible instance to query.
1625  *
1626  * Gets the #AtspiStreamableContent interface for an #AtspiAccessible.
1627  *
1628  * Returns: (transfer full): a pointer to an #AtspiStreamableContent interface
1629  *          instance, or NULL if @obj does not implement #AtspiStreamableContent.
1630  **/
1631 AtspiStreamableContent *
1632 atspi_accessible_get_streamable_content (AtspiAccessible *accessible)
1633 {
1634   return (_atspi_accessible_is_a (accessible, atspi_interface_streamable_content) ?
1635           accessible : NULL);
1636 }
1637 #endif
1638
1639 /**
1640  * atspi_accessible_get_table: (rename-to atspi_accessible_get_table_iface)
1641  * @obj: a pointer to the #AtspiAccessible instance to query.
1642  *
1643  * Gets the #AtspiTable interface for an #AtspiAccessible.
1644  *
1645  * Returns: (transfer full): a pointer to an #AtspiTable interface instance, or
1646  *          NULL if @obj does not implement #AtspiTable.
1647  *
1648  * Deprecated: 2.10: Use atspi_accessible_get_table_iface instead.
1649  **/
1650 AtspiTable *
1651 atspi_accessible_get_table (AtspiAccessible *obj)
1652 {
1653   return (_atspi_accessible_is_a (obj, atspi_interface_table) ?
1654           g_object_ref (ATSPI_TABLE (obj)) : NULL);
1655 }
1656
1657 /**
1658  * atspi_accessible_get_table_iface:
1659  * @obj: a pointer to the #AtspiAccessible instance to query.
1660  *
1661  * Gets the #AtspiTable interface for an #AtspiAccessible.
1662  *
1663  * Returns: (transfer full): a pointer to an #AtspiTable interface instance, or
1664  *          NULL if @obj does not implement #AtspiTable.
1665  **/
1666 AtspiTable *
1667 atspi_accessible_get_table_iface (AtspiAccessible *obj)
1668 {
1669   return (_atspi_accessible_is_a (obj, atspi_interface_table) ?
1670           g_object_ref (ATSPI_TABLE (obj)) : NULL);
1671 }
1672
1673 /**
1674  * atspi_accessible_get_table_cell:
1675  * @obj: a pointer to the #AtspiAccessible instance to query.
1676  *
1677  * Gets the #AtspiTableCell interface for an #AtspiAccessible.
1678  *
1679  * Returns: (transfer full): a pointer to an #AtspiTableCell interface instance,
1680  *          or NULL if @obj does not implement #AtspiTable.
1681  **/
1682 AtspiTableCell *
1683 atspi_accessible_get_table_cell (AtspiAccessible *obj)
1684 {
1685   return (_atspi_accessible_is_a (obj, atspi_interface_table_cell) ?
1686           g_object_ref (ATSPI_TABLE_CELL (obj)) : NULL);
1687 }
1688
1689 /**
1690  * atspi_accessible_get_text: (rename-to atspi_accessible_get_text_iface)
1691  * @obj: a pointer to the #AtspiAccessible instance to query.
1692  *
1693  * Gets the #AtspiTable interface for an #AtspiAccessible.
1694  *
1695  * Returns: (transfer full): a pointer to an #AtspiText interface instance, or
1696  *          NULL if @obj does not implement #AtspiText.
1697  *
1698  * Deprecated: 2.10: Use atspi_accessible_get_text_iface instead.
1699  **/
1700 AtspiText *
1701 atspi_accessible_get_text (AtspiAccessible *obj)
1702 {
1703   return (_atspi_accessible_is_a (obj, atspi_interface_text) ?
1704           g_object_ref (ATSPI_TEXT (obj)) : NULL);
1705 }
1706
1707 /**
1708  * atspi_accessible_get_text_iface:
1709  * @obj: a pointer to the #AtspiAccessible instance to query.
1710  *
1711  * Gets the #AtspiTable interface for an #AtspiAccessible.
1712  *
1713  * Returns: (transfer full): a pointer to an #AtspiText interface instance, or
1714  *          NULL if @obj does not implement #AtspiText.
1715  **/
1716 AtspiText *
1717 atspi_accessible_get_text_iface (AtspiAccessible *obj)
1718 {
1719   return (_atspi_accessible_is_a (obj, atspi_interface_text) ?
1720           g_object_ref (ATSPI_TEXT (obj)) : NULL);
1721 }
1722
1723 /**
1724  * atspi_accessible_get_value: (rename-to atspi_accessible_get_value_iface)
1725  * @obj: a pointer to the #AtspiAccessible instance to query.
1726  *
1727  * Gets the #AtspiTable interface for an #AtspiAccessible.
1728  *
1729  * Returns: (transfer full): a pointer to an #AtspiValue interface instance, or
1730  *          NULL if @obj does not implement #AtspiValue.
1731  *
1732  * Deprecated: 2.10: Use atspi_accessible_get_value_iface instead.
1733  **/
1734 AtspiValue *
1735 atspi_accessible_get_value (AtspiAccessible *accessible)
1736 {
1737   return (_atspi_accessible_is_a (accessible, atspi_interface_value) ?
1738           g_object_ref (ATSPI_VALUE (accessible)) : NULL);
1739 }
1740
1741 /**
1742  * atspi_accessible_get_value_iface:
1743  * @obj: a pointer to the #AtspiAccessible instance to query.
1744  *
1745  * Gets the #AtspiTable interface for an #AtspiAccessible.
1746  *
1747  * Returns: (transfer full): a pointer to an #AtspiValue interface instance, or
1748  *          NULL if @obj does not implement #AtspiValue.
1749  **/
1750 AtspiValue *
1751 atspi_accessible_get_value_iface (AtspiAccessible *accessible)
1752 {
1753   return (_atspi_accessible_is_a (accessible, atspi_interface_value) ?
1754           g_object_ref (ATSPI_VALUE (accessible)) : NULL);
1755 }
1756
1757 static void
1758 append_const_val (GArray *array, const gchar *val)
1759 {
1760   gchar *dup = g_strdup (val);
1761
1762   if (dup)
1763     g_array_append_val (array, dup);
1764 }
1765
1766 /**
1767  * atspi_accessible_get_interfaces:
1768  * @obj: The #AtspiAccessible to query.
1769  *
1770  * A set of pointers to all interfaces supported by an #AtspiAccessible.
1771  *
1772  * Returns: (element-type gchar*) (transfer full): A #GArray of strings
1773  *          describing the interfaces supported by the object.  Interfaces are
1774  *          denoted in short-hand (i.e. "Component", "Text" etc.).
1775  **/
1776 GArray *
1777 atspi_accessible_get_interfaces (AtspiAccessible *obj)
1778 {
1779   GArray *ret = g_array_new (TRUE, TRUE, sizeof (gchar *));
1780
1781   g_return_val_if_fail (obj != NULL, NULL);
1782
1783   append_const_val (ret, "Accessible");
1784   if (atspi_accessible_is_action (obj))
1785     append_const_val (ret, "Action");
1786   if (atspi_accessible_is_collection (obj))
1787     append_const_val (ret, "Collection");
1788   if (atspi_accessible_is_component (obj))
1789     append_const_val (ret, "Component");
1790   if (atspi_accessible_is_document (obj))
1791     append_const_val (ret, "Document");
1792   if (atspi_accessible_is_editable_text (obj))
1793     append_const_val (ret, "EditableText");
1794   if (atspi_accessible_is_hypertext (obj))
1795     append_const_val (ret, "Hypertext");
1796   if (atspi_accessible_is_hyperlink (obj))
1797     append_const_val (ret, "Hyperlink");
1798   if (atspi_accessible_is_image (obj))
1799     append_const_val (ret, "Image");
1800   if (atspi_accessible_is_selection (obj))
1801     append_const_val (ret, "Selection");
1802   if (atspi_accessible_is_table (obj))
1803     append_const_val (ret, "Table");
1804   if (atspi_accessible_is_table_cell (obj))
1805     append_const_val (ret, "TableCell");
1806   if (atspi_accessible_is_text (obj))
1807     append_const_val (ret, "Text");
1808   if (atspi_accessible_is_value (obj))
1809     append_const_val (ret, "Value");
1810
1811   return ret;
1812 }
1813
1814 AtspiAccessible *
1815 _atspi_accessible_new (AtspiApplication *app, const gchar *path)
1816 {
1817   AtspiAccessible *accessible;
1818
1819   accessible = g_object_new (ATSPI_TYPE_ACCESSIBLE, NULL);
1820   g_return_val_if_fail (accessible != NULL, NULL);
1821
1822   accessible->parent.app = g_object_ref (app);
1823   accessible->parent.path = g_strdup (path);
1824
1825   return accessible;
1826 }
1827
1828 /**
1829  * atspi_accessible_set_cache_mask:
1830  * @accessible: The #AtspiAccessible to operate on.  Must be the desktop or
1831  *             the root of an application.
1832  * @mask: An #AtspiCache specifying a bit mask of the types of data to cache.
1833  *
1834  * Sets the type of data to cache for accessibles.
1835  * If this is not set for an application or is reset to ATSPI_CACHE_UNDEFINED,
1836  * then the desktop's cache flag will be used.
1837  * If the desktop's cache flag is also undefined, then all possible data will
1838  * be cached.
1839  * This function is intended to work around bugs in toolkits where the proper
1840  * events are not raised / to aid in testing for such bugs.
1841  **/
1842 void
1843 atspi_accessible_set_cache_mask (AtspiAccessible *accessible, AtspiCache mask)
1844 {
1845   g_return_if_fail (accessible != NULL);
1846   g_return_if_fail (accessible->parent.app != NULL);
1847   g_return_if_fail (accessible == accessible->parent.app->root);
1848   accessible->parent.app->cache = mask;
1849   enable_caching = TRUE;
1850 }
1851
1852 /**
1853  * atspi_accessible_clear_cache:
1854  * @accessible: The #AtspiAccessible whose cache to clear.
1855  *
1856  * Clears the cached information for the given accessible and all of its
1857  * descendants.
1858  */
1859 void
1860 atspi_accessible_clear_cache (AtspiAccessible *accessible)
1861 {
1862   GList *l;
1863
1864   if (accessible)
1865   {
1866     accessible->cached_properties = ATSPI_CACHE_NONE;
1867     for (l = accessible->children; l; l = l->next)
1868       atspi_accessible_clear_cache (l->data);
1869   }
1870 }
1871
1872 /**
1873  * atspi_accessible_get_process_id:
1874  * @accessible: The #AtspiAccessible to query.
1875  * @error: a pointer to a %NULL #GError pointer
1876  *
1877  * Returns the process id associated with the given accessible.  Mainly
1878  * added for debugging; it is a shortcut to explicitly querying the
1879  * accessible's app->bus_name and then calling GetConnectionUnixProcessID.
1880  *
1881  * Returns: The process ID or undetermined value if @error is set.
1882  **/
1883 guint
1884 atspi_accessible_get_process_id (AtspiAccessible *accessible, GError **error)
1885 {
1886   DBusMessage *message, *reply;
1887   DBusConnection *bus = _atspi_bus ();
1888   dbus_uint32_t pid = -1;
1889   DBusError d_error;
1890
1891   if (!accessible->parent.app || !accessible->parent.app->bus_name)
1892     {
1893       g_set_error_literal(error, ATSPI_ERROR, ATSPI_ERROR_IPC, "Process is defunct");
1894       return -1;
1895     }
1896
1897   message = dbus_message_new_method_call ("org.freedesktop.DBus",
1898                                           "/org/freedesktop/DBus",
1899                                           "org.freedesktop.DBus",
1900                                           "GetConnectionUnixProcessID");
1901   dbus_message_append_args (message, DBUS_TYPE_STRING,
1902                             &accessible->parent.app->bus_name,
1903                             DBUS_TYPE_INVALID);
1904   dbus_error_init (&d_error);
1905   reply = dbus_connection_send_with_reply_and_block (bus, message, -1, &d_error);
1906   dbus_message_unref (message);
1907   if (reply)
1908   {
1909     if (!strcmp (dbus_message_get_signature (reply), "u"))
1910       dbus_message_get_args (reply, NULL, DBUS_TYPE_UINT32, &pid, DBUS_TYPE_INVALID);
1911     dbus_message_unref (reply);
1912   }
1913   if (dbus_error_is_set (&d_error))
1914     {
1915       g_set_error_literal(error, ATSPI_ERROR, ATSPI_ERROR_IPC, "Process is defunct");
1916       dbus_error_free (&d_error);
1917     }
1918   return pid;
1919 }
1920
1921 AtspiCache
1922 _atspi_accessible_get_cache_mask (AtspiAccessible *accessible)
1923 {
1924   AtspiCache mask;
1925
1926   if (!accessible->parent.app)
1927     return ATSPI_CACHE_NONE;
1928
1929  mask = accessible->parent.app->cache;
1930   if (mask == ATSPI_CACHE_UNDEFINED &&
1931       accessible->parent.app->root &&
1932       accessible->parent.app->root->accessible_parent)
1933   {
1934     AtspiAccessible *desktop = atspi_get_desktop (0);
1935     mask = desktop->parent.app->cache;
1936     g_object_unref (desktop);
1937   }
1938
1939   if (mask == ATSPI_CACHE_UNDEFINED)
1940     mask = ATSPI_CACHE_DEFAULT;
1941
1942   return mask;
1943 }
1944
1945 gboolean
1946 _atspi_accessible_test_cache (AtspiAccessible *accessible, AtspiCache flag)
1947 {
1948   AtspiCache mask = _atspi_accessible_get_cache_mask (accessible);
1949   AtspiCache result = accessible->cached_properties & mask & flag;
1950   if (accessible->states && atspi_state_set_contains (accessible->states, ATSPI_STATE_TRANSIENT))
1951     return FALSE;
1952   return (result != 0 && (atspi_main_loop || enable_caching ||
1953                           flag == ATSPI_CACHE_INTERFACES) &&
1954           !atspi_no_cache);
1955 }
1956
1957 void
1958 _atspi_accessible_add_cache (AtspiAccessible *accessible, AtspiCache flag)
1959 {
1960   AtspiCache mask = _atspi_accessible_get_cache_mask (accessible);
1961
1962   accessible->cached_properties |= flag & mask;
1963 }
1964
1965 /**
1966  * atspi_accessible_get_locale:
1967  * @accessible: an #AtspiAccessible
1968  *
1969  * Gets a UTF-8 string indicating the POSIX-style LC_MESSAGES locale
1970  * of @accessible.
1971  *
1972  * Since: 2.7.91
1973  *
1974  * Returns: a UTF-8 string indicating the POSIX-style LC_MESSAGES
1975  *          locale of @accessible.
1976  **/
1977 const gchar*
1978 atspi_accessible_get_object_locale (AtspiAccessible *accessible, GError **error)
1979 {
1980   gchar *locale;
1981
1982   g_return_val_if_fail (accessible != NULL, NULL);
1983
1984   locale = g_object_get_qdata (G_OBJECT (accessible), quark_locale);
1985   if (!locale)
1986   {
1987     if (!_atspi_dbus_get_property (accessible, atspi_interface_accessible,
1988                                    "Locale", error, "s", &locale))
1989       return NULL;
1990     if (locale)
1991       g_object_set_qdata_full (G_OBJECT (accessible), quark_locale, locale,
1992                                g_free);
1993   }
1994   return locale;
1995 }
1996
1997 void
1998 free_value (gpointer data)
1999 {
2000   GValue *value = data;
2001
2002   g_value_unset (value);
2003   g_free (value);
2004 }
2005
2006 GHashTable *
2007 _atspi_accessible_ref_cache (AtspiAccessible *accessible)
2008 {
2009   AtspiAccessiblePrivate *priv = accessible->priv;
2010
2011   priv->cache_ref_count++;
2012   if (priv->cache)
2013     return g_hash_table_ref (priv->cache);
2014   priv->cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
2015                                        free_value);
2016   return priv->cache;
2017 }
2018
2019 void
2020 _atspi_accessible_unref_cache (AtspiAccessible *accessible)
2021 {
2022   AtspiAccessiblePrivate *priv = accessible->priv;
2023
2024   if (priv->cache)
2025   {
2026     g_hash_table_unref (priv->cache);
2027     if (--priv->cache_ref_count == 0)
2028       priv->cache = NULL;
2029   }
2030 }