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