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