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