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