e7f1446c1e60ae0301d9b03e13178ce62d621f75
[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
179   if (accessible->attributes)
180     g_hash_table_unref (accessible->attributes);
181
182   if (accessible->priv->cache)
183     g_hash_table_destroy (accessible->priv->cache);
184
185 #ifdef DEBUG_REF_COUNTS
186   accessible_count--;
187   g_hash_table_remove (_atspi_get_live_refs (), accessible);
188   g_print ("at-spi: finalize: %d objects\n", accessible_count);
189 #endif
190
191   G_OBJECT_CLASS (atspi_accessible_parent_class)->finalize (object);
192 }
193
194 static void
195 atspi_accessible_class_init (AtspiAccessibleClass *klass)
196 {
197   GObjectClass *object_class = G_OBJECT_CLASS (klass);
198
199   object_class->dispose = atspi_accessible_dispose;
200   object_class->finalize = atspi_accessible_finalize;
201
202   quark_locale = g_quark_from_string ("accessible-locale");
203 }
204
205 /**
206  * atspi_accessible_get_name:
207  * @obj: a pointer to the #AtspiAccessible object on which to operate.
208  *
209  * Gets the name of an #AtspiAccessible object.
210  *
211  * Returns: a UTF-8 string indicating the name of the #AtspiAccessible object 
212  * or NULL on exception.
213  **/
214 gchar *
215 atspi_accessible_get_name (AtspiAccessible *obj, GError **error)
216 {
217   g_return_val_if_fail (obj != NULL, g_strdup (""));
218   if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_NAME))
219   {
220     if (!_atspi_dbus_get_property (obj, atspi_interface_accessible, "Name", error,
221                                    "s", &obj->name))
222       return g_strdup ("");
223     _atspi_accessible_add_cache (obj, ATSPI_CACHE_NAME);
224   }
225   return g_strdup (obj->name);
226 }
227
228 /**
229  * atspi_accessible_get_description:
230  * @obj: a pointer to the #AtspiAccessible object on which to operate.
231  *
232  * Gets the description of an #AtspiAccessible object.
233  *
234  * Returns: a UTF-8 string describing the #AtspiAccessible object 
235  * or NULL on exception.
236  **/
237 gchar *
238 atspi_accessible_get_description (AtspiAccessible *obj, GError **error)
239 {
240   g_return_val_if_fail (obj != NULL, g_strdup (""));
241
242   if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_DESCRIPTION))
243   {
244     if (!_atspi_dbus_get_property (obj, atspi_interface_accessible,
245                                    "Description", error, "s",
246                                    &obj->description))
247       return g_strdup ("");
248     _atspi_accessible_add_cache (obj, ATSPI_CACHE_DESCRIPTION);
249   }
250   return g_strdup (obj->description);
251 }
252
253 const char *str_parent = "Parent";
254
255 /**
256  * atspi_accessible_get_parent:
257  * @obj: a pointer to the #AtspiAccessible object to query.
258  *
259  * Gets an #AtspiAccessible object's parent container.
260  *
261  * Returns: (nullable) (transfer full): a pointer to the
262  *          #AtspiAccessible object which contains the given
263  *          #AtspiAccessible instance, or NULL if the @obj has no
264  *          parent container.
265  *
266  **/
267 AtspiAccessible *
268 atspi_accessible_get_parent (AtspiAccessible *obj, GError **error)
269 {
270   g_return_val_if_fail (obj != NULL, NULL);
271
272   if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_PARENT))
273   {
274     DBusMessage *message, *reply;
275     DBusMessageIter iter, iter_variant;
276     if (!obj->parent.app)
277       return NULL;
278     message = dbus_message_new_method_call (obj->parent.app->bus_name,
279                                             obj->parent.path,
280                                             DBUS_INTERFACE_PROPERTIES, "Get");
281     if (!message)
282       return NULL;
283     dbus_message_append_args (message, DBUS_TYPE_STRING, &atspi_interface_accessible,
284                                DBUS_TYPE_STRING, &str_parent,
285                               DBUS_TYPE_INVALID);
286     reply = _atspi_dbus_send_with_reply_and_block (message, error);
287     if (!reply)
288       return NULL;
289     if (strcmp (dbus_message_get_signature (reply), "v") != 0)
290     {
291       dbus_message_unref (reply);
292       return NULL;
293     }
294     dbus_message_iter_init (reply, &iter);
295     dbus_message_iter_recurse (&iter, &iter_variant);
296     obj->accessible_parent = _atspi_dbus_return_accessible_from_iter (&iter_variant);
297     dbus_message_unref (reply);
298     _atspi_accessible_add_cache (obj, ATSPI_CACHE_PARENT);
299   }
300   if (!obj->accessible_parent)
301     return NULL;
302   return g_object_ref (obj->accessible_parent);
303 }
304
305 /**
306  * atspi_accessible_get_child_count:
307  * @obj: a pointer to the #AtspiAccessible object on which to operate.
308  *
309  * Gets the number of children contained by an #AtspiAccessible object.
310  *
311  * Returns: a #long indicating the number of #AtspiAccessible children
312  *          contained by an #AtspiAccessible object or -1 on exception.
313  *
314  **/
315 gint
316 atspi_accessible_get_child_count (AtspiAccessible *obj, GError **error)
317 {
318   g_return_val_if_fail (obj != NULL, -1);
319
320   if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_CHILDREN))
321   {
322     dbus_int32_t ret;
323     if (!_atspi_dbus_get_property (obj, atspi_interface_accessible,
324                                    "ChildCount", error, "i", &ret))
325       return -1;
326     return ret;
327   }
328
329   if (!obj->children)
330     return 0;   /* assume it's disposed */
331
332   return obj->children->len;
333 }
334
335 /**
336  * atspi_accessible_get_child_at_index:
337  * @obj: a pointer to the #AtspiAccessible object on which to operate.
338  * @child_index: a #long indicating which child is specified.
339  *
340  * Gets the #AtspiAccessible child of an #AtspiAccessible object at a given index.
341  *
342  * Returns: (transfer full): a pointer to the #AtspiAccessible child object at
343  * index @child_index or NULL on exception.
344  **/
345 AtspiAccessible *
346 atspi_accessible_get_child_at_index (AtspiAccessible *obj,
347                             gint    child_index,
348                             GError **error)
349 {
350   AtspiAccessible *child;
351   DBusMessage *reply;
352
353   g_return_val_if_fail (obj != NULL, NULL);
354
355   if (_atspi_accessible_test_cache (obj, ATSPI_CACHE_CHILDREN))
356   {
357     if (!obj->children)
358       return NULL;      /* assume disposed */
359
360     child = g_ptr_array_index (obj->children, child_index);
361     if (child)
362       return g_object_ref (child);
363   }
364
365   reply = _atspi_dbus_call_partial (obj, atspi_interface_accessible,
366                                    "GetChildAtIndex", error, "i", child_index);
367   child = _atspi_dbus_return_accessible_from_message (reply);
368
369   if (!child)
370     return NULL;
371
372   if (_atspi_accessible_test_cache (obj, ATSPI_CACHE_CHILDREN))
373   {
374       if (child_index >= obj->children->len)
375         g_ptr_array_set_size (obj->children, child_index + 1);
376     g_ptr_array_index (obj->children, child_index) = g_object_ref (child);
377   }
378   return child;
379 }
380
381 /**
382  * atspi_accessible_get_index_in_parent:
383  * @obj: a pointer to the #AtspiAccessible object on which to operate.
384  *
385  * Gets the index of an #AtspiAccessible object within its parent's 
386  * #AtspiAccessible children list.
387  *
388  * Returns: a #glong indicating the index of the #AtspiAccessible object
389  *          in its parent,
390  *          or -1 if @obj has no containing parent or on exception.
391  **/
392 gint
393 atspi_accessible_get_index_in_parent (AtspiAccessible *obj, GError **error)
394 {
395   gint i = 0;
396   dbus_int32_t ret = -1;
397
398   g_return_val_if_fail (obj != NULL, -1);
399   if (_atspi_accessible_test_cache (obj, ATSPI_CACHE_PARENT))
400   {
401     if (!obj->accessible_parent)
402       return -1;
403
404     if (!_atspi_accessible_test_cache (obj->accessible_parent, ATSPI_CACHE_CHILDREN) || !obj->accessible_parent->children)
405         goto dbus;
406
407     for (i = 0; i < obj->accessible_parent->children->len; i++)
408       if (g_ptr_array_index (obj->accessible_parent->children, i) == obj)
409         return i;
410   }
411
412 dbus:
413   _atspi_dbus_call (obj, atspi_interface_accessible,
414                     "GetIndexInParent", NULL, "=>i", &ret);
415   return ret;
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: (rename-to atspi_accessible_get_action_iface)
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  **/
1116 AtspiAction *
1117 atspi_accessible_get_action (AtspiAccessible *accessible)
1118 {
1119   return (_atspi_accessible_is_a (accessible, atspi_interface_action) ?
1120           g_object_ref (ATSPI_ACTION (accessible)) : NULL);  
1121 }
1122
1123 /**
1124  * atspi_accessible_get_action_iface:
1125  * @obj: a pointer to the #AtspiAccessible instance to query.
1126  *
1127  * Gets the #AtspiAction interface for an #AtspiAccessible.
1128  *
1129  * Returns: (transfer full): a pointer to an #AtspiAction interface
1130  *          instance, or NULL if @obj does not implement #AtspiAction.
1131  **/
1132 AtspiAction *
1133 atspi_accessible_get_action_iface (AtspiAccessible *accessible)
1134 {
1135   return (_atspi_accessible_is_a (accessible, atspi_interface_action) ?
1136           g_object_ref (ATSPI_ACTION (accessible)) : NULL);  
1137 }
1138
1139 /**
1140  * atspi_accessible_get_collection: (rename-to atspi_accessible_get_collection_iface)
1141  * @obj: a pointer to the #AtspiAccessible instance to query.
1142  *
1143  * Gets the #AtspiCollection interface for an #AtspiAccessible.
1144  *
1145  * Returns: (transfer full): a pointer to an #AtspiCollection interface
1146  *          instance, or NULL if @obj does not implement #AtspiCollection.
1147  *
1148  * Deprecated: 2.10: Use atspi_accessible_get_collection_iface instead.
1149  **/
1150 AtspiCollection *
1151 atspi_accessible_get_collection (AtspiAccessible *accessible)
1152 {
1153   return (_atspi_accessible_is_a (accessible, atspi_interface_collection) ?
1154           g_object_ref (ATSPI_COLLECTION (accessible)) : NULL);  
1155 }
1156
1157 /**
1158  * atspi_accessible_get_collection_iface:
1159  * @obj: a pointer to the #AtspiAccessible instance to query.
1160  *
1161  * Gets the #AtspiCollection interface for an #AtspiAccessible.
1162  *
1163  * Returns: (transfer full): a pointer to an #AtspiCollection interface
1164  *          instance, or NULL if @obj does not implement #AtspiCollection.
1165  **/
1166 AtspiCollection *
1167 atspi_accessible_get_collection_iface (AtspiAccessible *accessible)
1168 {
1169   return (_atspi_accessible_is_a (accessible, atspi_interface_collection) ?
1170           g_object_ref (ATSPI_COLLECTION (accessible)) : NULL);  
1171 }
1172
1173 /**
1174  * atspi_accessible_get_component: (rename-to atspi_accessible_get_component_iface)
1175  * @obj: a pointer to the #AtspiAccessible instance to query.
1176  *
1177  * Gets the #AtspiComponent interface for an #AtspiAccessible.
1178  *
1179  * Returns: (transfer full): a pointer to an #AtspiComponent interface
1180  *          instance, or NULL if @obj does not implement #AtspiComponent.
1181  *
1182  * Deprecated: 2.10: Use atspi_accessible_get_component_iface instead.
1183  **/
1184 AtspiComponent *
1185 atspi_accessible_get_component (AtspiAccessible *obj)
1186 {
1187   return (_atspi_accessible_is_a (obj, atspi_interface_component) ?
1188           g_object_ref (ATSPI_COMPONENT (obj)) : NULL);
1189 }
1190
1191 /**
1192  * atspi_accessible_get_component_iface:
1193  * @obj: a pointer to the #AtspiAccessible instance to query.
1194  *
1195  * Gets the #AtspiComponent interface for an #AtspiAccessible.
1196  *
1197  * Returns: (transfer full): a pointer to an #AtspiComponent interface
1198  *          instance, or NULL if @obj does not implement #AtspiComponent.
1199  **/
1200 AtspiComponent *
1201 atspi_accessible_get_component_iface (AtspiAccessible *obj)
1202 {
1203   return (_atspi_accessible_is_a (obj, atspi_interface_component) ?
1204           g_object_ref (ATSPI_COMPONENT (obj)) : NULL);
1205 }
1206
1207 /**
1208  * atspi_accessible_get_document: (rename-to atspi_accessible_get_document_iface)
1209  * @obj: a pointer to the #AtspiAccessible instance to query.
1210  *
1211  * Gets the #AtspiDocument interface for an #AtspiAccessible.
1212  *
1213  * Returns: (transfer full): a pointer to an #AtspiDocument interface
1214  *          instance, or NULL if @obj does not implement #AtspiDocument.
1215  *
1216  * Deprecated: 2.10: Use atspi_accessible_get_document_iface instead.
1217  **/
1218 AtspiDocument *
1219 atspi_accessible_get_document (AtspiAccessible *accessible)
1220 {
1221   return (_atspi_accessible_is_a (accessible, atspi_interface_document) ?
1222           g_object_ref (ATSPI_DOCUMENT (accessible)) : NULL);  
1223 }
1224
1225 /**
1226  * atspi_accessible_get_document_iface:
1227  * @obj: a pointer to the #AtspiAccessible instance to query.
1228  *
1229  * Gets the #AtspiDocument interface for an #AtspiAccessible.
1230  *
1231  * Returns: (transfer full): a pointer to an #AtspiDocument interface
1232  *          instance, or NULL if @obj does not implement #AtspiDocument.
1233  **/
1234 AtspiDocument *
1235 atspi_accessible_get_document_iface (AtspiAccessible *accessible)
1236 {
1237   return (_atspi_accessible_is_a (accessible, atspi_interface_document) ?
1238           g_object_ref (ATSPI_DOCUMENT (accessible)) : NULL);  
1239 }
1240
1241 /**
1242  * atspi_accessible_get_editable_text: (rename-to atspi_accessible_get_editable_text_iface)
1243  * @obj: a pointer to the #AtspiAccessible instance to query.
1244  *
1245  * Gets the #AtspiEditableText interface for an #AtspiAccessible.
1246  *
1247  * Returns: (transfer full): a pointer to an #AtspiEditableText interface
1248  *          instance, or NULL if @obj does not implement #AtspiEditableText.
1249  *
1250  * Deprecated: 2.10: Use atspi_accessible_get_editable_text_iface instead.
1251  **/
1252 AtspiEditableText *
1253 atspi_accessible_get_editable_text (AtspiAccessible *accessible)
1254 {
1255   return (_atspi_accessible_is_a (accessible, atspi_interface_editable_text) ?
1256           g_object_ref (ATSPI_EDITABLE_TEXT (accessible)) : NULL);  
1257 }
1258
1259 /**
1260  * atspi_accessible_get_editable_text_iface:
1261  * @obj: a pointer to the #AtspiAccessible instance to query.
1262  *
1263  * Gets the #AtspiEditableText interface for an #AtspiAccessible.
1264  *
1265  * Returns: (transfer full): a pointer to an #AtspiEditableText interface
1266  *          instance, or NULL if @obj does not implement #AtspiEditableText.
1267  **/
1268 AtspiEditableText *
1269 atspi_accessible_get_editable_text_iface (AtspiAccessible *accessible)
1270 {
1271   return (_atspi_accessible_is_a (accessible, atspi_interface_editable_text) ?
1272           g_object_ref (ATSPI_EDITABLE_TEXT (accessible)) : NULL);  
1273 }
1274
1275 /**
1276  * atspi_accessible_get_hyperlink:
1277  * @obj: a pointer to the #AtspiAccessible object on which to operate.
1278  *
1279  * Gets the #AtspiHyperlink interface for an #AtspiAccessible.
1280  *
1281  * Returns: (transfer full): the #AtspiHyperlink object associated with
1282  *          the given #AtspiAccessible, or NULL if not supported.
1283  **/
1284 AtspiHyperlink *
1285 atspi_accessible_get_hyperlink (AtspiAccessible *accessible)
1286 {
1287   return (_atspi_accessible_is_a (accessible, atspi_interface_hyperlink) ?
1288           _atspi_hyperlink_new (accessible->parent.app, accessible->parent.path) : NULL);
1289 }
1290
1291 /**
1292  * atspi_accessible_get_hypertext: (rename-to atspi_accessible_get_hypertext_iface)
1293  * @obj: a pointer to the #AtspiAccessible instance to query.
1294  *
1295  * Gets the #AtspiHypertext interface for an #AtspiAccessible.
1296  *
1297  * Returns: (transfer full): a pointer to an #AtspiHypertext interface
1298  *          instance, or NULL if @obj does not implement #AtspiHypertext.
1299  *
1300  * Deprecated: 2.10: Use atspi_accessible_get_hypertext_iface instead.
1301  **/
1302 AtspiHypertext *
1303 atspi_accessible_get_hypertext (AtspiAccessible *accessible)
1304 {
1305   return (_atspi_accessible_is_a (accessible, atspi_interface_hypertext) ?
1306           g_object_ref (ATSPI_HYPERTEXT (accessible)) : NULL);  
1307 }
1308
1309 /**
1310  * atspi_accessible_get_hypertext_iface:
1311  * @obj: a pointer to the #AtspiAccessible instance to query.
1312  *
1313  * Gets the #AtspiHypertext interface for an #AtspiAccessible.
1314  *
1315  * Returns: (transfer full): a pointer to an #AtspiHypertext interface
1316  *          instance, or NULL if @obj does not implement #AtspiHypertext.
1317  **/
1318 AtspiHypertext *
1319 atspi_accessible_get_hypertext_iface (AtspiAccessible *accessible)
1320 {
1321   return (_atspi_accessible_is_a (accessible, atspi_interface_hypertext) ?
1322           g_object_ref (ATSPI_HYPERTEXT (accessible)) : NULL);  
1323 }
1324
1325 /**
1326  * atspi_accessible_get_image: (rename-to atspi_accessible_get_image_iface)
1327  * @obj: a pointer to the #AtspiAccessible instance to query.
1328  *
1329  * Gets the #AtspiImage interface for an #AtspiAccessible.
1330  *
1331  * Returns: (transfer full): a pointer to an #AtspiImage interface instance, or
1332  *          NULL if @obj does not implement #AtspiImage.
1333  *
1334  * Deprecated: 2.10: Use atspi_accessible_get_image_iface instead.
1335  **/
1336 AtspiImage *
1337 atspi_accessible_get_image (AtspiAccessible *accessible)
1338 {
1339   return (_atspi_accessible_is_a (accessible, atspi_interface_image) ?
1340           g_object_ref (ATSPI_IMAGE (accessible)) : NULL);  
1341 }
1342
1343 /**
1344  * atspi_accessible_get_image_iface:
1345  * @obj: a pointer to the #AtspiAccessible instance to query.
1346  *
1347  * Gets the #AtspiImage interface for an #AtspiAccessible.
1348  *
1349  * Returns: (transfer full): a pointer to an #AtspiImage interface instance, or
1350  *          NULL if @obj does not implement #AtspiImage.
1351  **/
1352 AtspiImage *
1353 atspi_accessible_get_image_iface (AtspiAccessible *accessible)
1354 {
1355   return (_atspi_accessible_is_a (accessible, atspi_interface_image) ?
1356           g_object_ref (ATSPI_IMAGE (accessible)) : NULL);  
1357 }
1358
1359 /**
1360  * atspi_accessible_get_selection: (rename-to atspi_accessible_get_selection_iface)
1361  * @obj: a pointer to the #AtspiAccessible instance to query.
1362  *
1363  * Gets the #AtspiSelection interface for an #AtspiAccessible.
1364  *
1365  * Returns: (transfer full): a pointer to an #AtspiSelection interface
1366  *          instance, or NULL if @obj does not implement #AtspiSelection.
1367  *
1368  * Deprecated: 2.10: Use atspi_accessible_get_selection_iface instead.
1369  **/
1370 AtspiSelection *
1371 atspi_accessible_get_selection (AtspiAccessible *accessible)
1372 {
1373   return (_atspi_accessible_is_a (accessible, atspi_interface_selection) ?
1374           g_object_ref (ATSPI_SELECTION (accessible)) : NULL);  
1375 }
1376
1377 /**
1378  * atspi_accessible_get_selection_iface:
1379  * @obj: a pointer to the #AtspiAccessible instance to query.
1380  *
1381  * Gets the #AtspiSelection interface for an #AtspiAccessible.
1382  *
1383  * Returns: (transfer full): a pointer to an #AtspiSelection interface
1384  *          instance, or NULL if @obj does not implement #AtspiSelection.
1385  **/
1386 AtspiSelection *
1387 atspi_accessible_get_selection_iface (AtspiAccessible *accessible)
1388 {
1389   return (_atspi_accessible_is_a (accessible, atspi_interface_selection) ?
1390           g_object_ref (ATSPI_SELECTION (accessible)) : NULL);  
1391 }
1392
1393 #if 0
1394 /**
1395  * atspi_accessible_get_streamable_content:
1396  * @obj: a pointer to the #AtspiAccessible instance to query.
1397  *
1398  * Gets the #AtspiStreamableContent interface for an #AtspiAccessible.
1399  *
1400  * Returns: (transfer full): a pointer to an #AtspiStreamableContent interface
1401  *          instance, or NULL if @obj does not implement #AtspiStreamableContent.
1402  **/
1403 AtspiStreamableContent *
1404 atspi_accessible_get_streamable_content (AtspiAccessible *accessible)
1405 {
1406   return (_atspi_accessible_is_a (accessible, atspi_interface_streamable_content) ?
1407           accessible : NULL);  
1408 }
1409 #endif
1410
1411 /**
1412  * atspi_accessible_get_table: (rename-to atspi_accessible_get_table_iface)
1413  * @obj: a pointer to the #AtspiAccessible instance to query.
1414  *
1415  * Gets the #AtspiTable interface for an #AtspiAccessible.
1416  *
1417  * Returns: (transfer full): a pointer to an #AtspiTable interface instance, or
1418  *          NULL if @obj does not implement #AtspiTable.
1419  *
1420  * Deprecated: 2.10: Use atspi_accessible_get_table_iface instead.
1421  **/
1422 AtspiTable *
1423 atspi_accessible_get_table (AtspiAccessible *obj)
1424 {
1425   return (_atspi_accessible_is_a (obj, atspi_interface_table) ?
1426           g_object_ref (ATSPI_TABLE (obj)) : NULL);  
1427 }
1428
1429 /**
1430  * atspi_accessible_get_table_iface:
1431  * @obj: a pointer to the #AtspiAccessible instance to query.
1432  *
1433  * Gets the #AtspiTable interface for an #AtspiAccessible.
1434  *
1435  * Returns: (transfer full): a pointer to an #AtspiTable interface instance, or
1436  *          NULL if @obj does not implement #AtspiTable.
1437  **/
1438 AtspiTable *
1439 atspi_accessible_get_table_iface (AtspiAccessible *obj)
1440 {
1441   return (_atspi_accessible_is_a (obj, atspi_interface_table) ?
1442           g_object_ref (ATSPI_TABLE (obj)) : NULL);  
1443 }
1444
1445 /**
1446  * atspi_accessible_get_table_cell:
1447  * @obj: a pointer to the #AtspiAccessible instance to query.
1448  *
1449  * Gets the #AtspiTableCell interface for an #AtspiAccessible.
1450  *
1451  * Returns: (transfer full): a pointer to an #AtspiTableCell interface instance,
1452  *          or NULL if @obj does not implement #AtspiTable.
1453  **/
1454 AtspiTableCell *
1455 atspi_accessible_get_table_cell (AtspiAccessible *obj)
1456 {
1457   return (_atspi_accessible_is_a (obj, atspi_interface_table_cell) ?
1458           g_object_ref (ATSPI_TABLE_CELL (obj)) : NULL);  
1459 }
1460
1461 /**
1462  * atspi_accessible_get_text: (rename-to atspi_accessible_get_text_iface)
1463  * @obj: a pointer to the #AtspiAccessible instance to query.
1464  *
1465  * Gets the #AtspiTable interface for an #AtspiAccessible.
1466  *
1467  * Returns: (transfer full): a pointer to an #AtspiText interface instance, or
1468  *          NULL if @obj does not implement #AtspiText.
1469  *
1470  * Deprecated: 2.10: Use atspi_accessible_get_text_iface instead.
1471  **/
1472 AtspiText *
1473 atspi_accessible_get_text (AtspiAccessible *obj)
1474 {
1475   return (_atspi_accessible_is_a (obj, atspi_interface_text) ?
1476           g_object_ref (ATSPI_TEXT (obj)) : NULL);
1477 }
1478
1479 /**
1480  * atspi_accessible_get_text_iface:
1481  * @obj: a pointer to the #AtspiAccessible instance to query.
1482  *
1483  * Gets the #AtspiTable interface for an #AtspiAccessible.
1484  *
1485  * Returns: (transfer full): a pointer to an #AtspiText interface instance, or
1486  *          NULL if @obj does not implement #AtspiText.
1487  **/
1488 AtspiText *
1489 atspi_accessible_get_text_iface (AtspiAccessible *obj)
1490 {
1491   return (_atspi_accessible_is_a (obj, atspi_interface_text) ?
1492           g_object_ref (ATSPI_TEXT (obj)) : NULL);
1493 }
1494
1495 /**
1496  * atspi_accessible_get_value: (rename-to atspi_accessible_get_value_iface)
1497  * @obj: a pointer to the #AtspiAccessible instance to query.
1498  *
1499  * Gets the #AtspiTable interface for an #AtspiAccessible.
1500  *
1501  * Returns: (transfer full): a pointer to an #AtspiValue interface instance, or
1502  *          NULL if @obj does not implement #AtspiValue.
1503  *
1504  * Deprecated: 2.10: Use atspi_accessible_get_value_iface instead.
1505  **/
1506 AtspiValue *
1507 atspi_accessible_get_value (AtspiAccessible *accessible)
1508 {
1509   return (_atspi_accessible_is_a (accessible, atspi_interface_value) ?
1510           g_object_ref (ATSPI_VALUE (accessible)) : NULL);  
1511 }
1512
1513 /**
1514  * atspi_accessible_get_value_iface:
1515  * @obj: a pointer to the #AtspiAccessible instance to query.
1516  *
1517  * Gets the #AtspiTable interface for an #AtspiAccessible.
1518  *
1519  * Returns: (transfer full): a pointer to an #AtspiValue interface instance, or
1520  *          NULL if @obj does not implement #AtspiValue.
1521  **/
1522 AtspiValue *
1523 atspi_accessible_get_value_iface (AtspiAccessible *accessible)
1524 {
1525   return (_atspi_accessible_is_a (accessible, atspi_interface_value) ?
1526           g_object_ref (ATSPI_VALUE (accessible)) : NULL);  
1527 }
1528
1529 static void
1530 append_const_val (GArray *array, const gchar *val)
1531 {
1532   gchar *dup = g_strdup (val);
1533
1534   if (dup)
1535     g_array_append_val (array, dup);
1536 }
1537
1538 /**
1539  * atspi_accessible_get_interfaces:
1540  * @obj: The #AtspiAccessible to query.
1541  *
1542  * A set of pointers to all interfaces supported by an #AtspiAccessible.
1543  *
1544  * Returns: (element-type gchar*) (transfer full): A #GArray of strings
1545  *          describing the interfaces supported by the object.  Interfaces are
1546  *          denoted in short-hand (i.e. "Component", "Text" etc.).
1547  **/
1548 GArray *
1549 atspi_accessible_get_interfaces (AtspiAccessible *obj)
1550 {
1551   GArray *ret = g_array_new (TRUE, TRUE, sizeof (gchar *));
1552
1553   g_return_val_if_fail (obj != NULL, NULL);
1554
1555   append_const_val (ret, "Accessible");
1556   if (atspi_accessible_is_action (obj))
1557     append_const_val (ret, "Action");
1558   if (atspi_accessible_is_collection (obj))
1559     append_const_val (ret, "Collection");
1560   if (atspi_accessible_is_component (obj))
1561     append_const_val (ret, "Component");
1562   if (atspi_accessible_is_document (obj))
1563     append_const_val (ret, "Document");
1564   if (atspi_accessible_is_editable_text (obj))
1565     append_const_val (ret, "EditableText");
1566   if (atspi_accessible_is_hypertext (obj))
1567     append_const_val (ret, "Hypertext");
1568   if (atspi_accessible_is_hyperlink (obj))
1569     append_const_val (ret, "Hyperlink");
1570   if (atspi_accessible_is_image (obj))
1571     append_const_val (ret, "Image");
1572   if (atspi_accessible_is_selection (obj))
1573     append_const_val (ret, "Selection");
1574   if (atspi_accessible_is_table (obj))
1575     append_const_val (ret, "Table");
1576   if (atspi_accessible_is_table_cell (obj))
1577     append_const_val (ret, "TableCell");
1578   if (atspi_accessible_is_text (obj))
1579     append_const_val (ret, "Text");
1580   if (atspi_accessible_is_value (obj))
1581     append_const_val (ret, "Value");
1582
1583   return ret;
1584 }
1585
1586 AtspiAccessible * 
1587 _atspi_accessible_new (AtspiApplication *app, const gchar *path)
1588 {
1589   AtspiAccessible *accessible;
1590   
1591   accessible = g_object_new (ATSPI_TYPE_ACCESSIBLE, NULL);
1592   g_return_val_if_fail (accessible != NULL, NULL);
1593
1594   accessible->parent.app = g_object_ref (app);
1595   accessible->parent.path = g_strdup (path);
1596
1597   return accessible;
1598 }
1599
1600 /**
1601  * atspi_accessible_set_cache_mask:
1602  * @accessible: The #AtspiAccessible to operate on.  Must be the desktop or
1603  *             the root of an application.
1604  * @mask: An #AtspiCache specifying a bit mask of the types of data to cache.
1605  *
1606  * Sets the type of data to cache for accessibles.
1607  * If this is not set for an application or is reset to ATSPI_CACHE_UNDEFINED,
1608  * then the desktop's cache flag will be used.
1609  * If the desktop's cache flag is also undefined, then all possible data will
1610  * be cached.
1611  * This function is intended to work around bugs in toolkits where the proper
1612  * events are not raised / to aid in testing for such bugs.
1613  **/
1614 void
1615 atspi_accessible_set_cache_mask (AtspiAccessible *accessible, AtspiCache mask)
1616 {
1617   g_return_if_fail (accessible != NULL);
1618   g_return_if_fail (accessible->parent.app != NULL);
1619   g_return_if_fail (accessible == accessible->parent.app->root);
1620   accessible->parent.app->cache = mask;
1621   enable_caching = TRUE;
1622 }
1623
1624 /**
1625  * atspi_accessible_clear_cache:
1626  * @obj: The #AtspiAccessible whose cache to clear.
1627  *
1628  * Clears the cached information for the given accessible and all of its
1629  * descendants.
1630  */
1631 void
1632 atspi_accessible_clear_cache (AtspiAccessible *obj)
1633 {
1634   gint i;
1635
1636   if (obj)
1637   {
1638     obj->cached_properties = ATSPI_CACHE_NONE;
1639     if (obj->children)
1640       for (i = 0; i < obj->children->len; i++)
1641         atspi_accessible_clear_cache (g_ptr_array_index (obj->children, i));
1642   }
1643 }
1644
1645 /**
1646  * atspi_accessible_get_process_id:
1647  * @accessible: The #AtspiAccessible to query.
1648  * @error: a pointer to a %NULL #GError pointer
1649  *
1650  * Returns the process id associated with the given accessible.  Mainly
1651  * added for debugging; it is a shortcut to explicitly querying the
1652  * accessible's app->bus_name and then calling GetConnectionUnixProcessID.
1653  *
1654  * Returns: The process ID or undetermined value if @error is set.
1655  **/
1656 guint
1657 atspi_accessible_get_process_id (AtspiAccessible *accessible, GError **error)
1658 {
1659   DBusMessage *message, *reply;
1660   DBusConnection *bus = _atspi_bus ();
1661   dbus_uint32_t pid = -1;
1662   DBusError d_error;
1663
1664   if (!accessible->parent.app || !accessible->parent.app->bus_name)
1665     {
1666       g_set_error_literal(error, ATSPI_ERROR, ATSPI_ERROR_IPC, "Process is defunct");
1667       return -1;
1668     }
1669
1670   message = dbus_message_new_method_call ("org.freedesktop.DBus",
1671                                           "/org/freedesktop/DBus",
1672                                           "org.freedesktop.DBus",
1673                                           "GetConnectionUnixProcessID");
1674   dbus_message_append_args (message, DBUS_TYPE_STRING,
1675                             &accessible->parent.app->bus_name,
1676                             DBUS_TYPE_INVALID);
1677   dbus_error_init (&d_error);
1678   reply = dbus_connection_send_with_reply_and_block (bus, message, -1, &d_error);
1679   dbus_message_unref (message);
1680   if (reply)
1681   {
1682     if (!strcmp (dbus_message_get_signature (reply), "u"))
1683       dbus_message_get_args (reply, NULL, DBUS_TYPE_UINT32, &pid, DBUS_TYPE_INVALID);
1684     dbus_message_unref (reply);
1685   }
1686   if (dbus_error_is_set (&d_error))
1687     {
1688       g_set_error_literal(error, ATSPI_ERROR, ATSPI_ERROR_IPC, "Process is defunct");
1689       dbus_error_free (&d_error);
1690     }
1691   return pid;
1692 }
1693
1694 AtspiCache
1695 _atspi_accessible_get_cache_mask (AtspiAccessible *accessible)
1696 {
1697   AtspiCache mask;
1698
1699   if (!accessible->parent.app)
1700     return ATSPI_CACHE_NONE;
1701
1702  mask = accessible->parent.app->cache;
1703   if (mask == ATSPI_CACHE_UNDEFINED &&
1704       accessible->parent.app->root &&
1705       accessible->parent.app->root->accessible_parent)
1706   {
1707     AtspiAccessible *desktop = atspi_get_desktop (0);
1708     mask = desktop->parent.app->cache;
1709     g_object_unref (desktop);
1710   }
1711
1712   if (mask == ATSPI_CACHE_UNDEFINED)
1713     mask = ATSPI_CACHE_DEFAULT;
1714
1715   return mask;
1716 }
1717
1718 gboolean
1719 _atspi_accessible_test_cache (AtspiAccessible *accessible, AtspiCache flag)
1720 {
1721   AtspiCache mask = _atspi_accessible_get_cache_mask (accessible);
1722   AtspiCache result = accessible->cached_properties & mask & flag;
1723   if (accessible->states && atspi_state_set_contains (accessible->states, ATSPI_STATE_TRANSIENT))
1724     return FALSE;
1725   return (result != 0 && (atspi_main_loop || enable_caching ||
1726                           flag == ATSPI_CACHE_INTERFACES) &&
1727           !atspi_no_cache);
1728 }
1729
1730 void
1731 _atspi_accessible_add_cache (AtspiAccessible *accessible, AtspiCache flag)
1732 {
1733   AtspiCache mask = _atspi_accessible_get_cache_mask (accessible);
1734
1735   accessible->cached_properties |= flag & mask;
1736 }
1737
1738 /**
1739  * atspi_accessible_get_locale:
1740  * @accessible: an #AtspiAccessible
1741  *
1742  * Gets a UTF-8 string indicating the POSIX-style LC_MESSAGES locale
1743  * of @accessible.
1744  *
1745  * Since: 2.7.91
1746  *
1747  * Returns: a UTF-8 string indicating the POSIX-style LC_MESSAGES
1748  *          locale of @accessible.
1749  **/
1750 const gchar*
1751 atspi_accessible_get_object_locale (AtspiAccessible *accessible, GError **error)
1752 {
1753   gchar *locale;
1754
1755   g_return_val_if_fail (accessible != NULL, NULL);
1756
1757   locale = g_object_get_qdata (G_OBJECT (accessible), quark_locale);
1758   if (!locale)
1759   {
1760     if (!_atspi_dbus_get_property (accessible, atspi_interface_accessible,
1761                                    "Locale", error, "s", &locale))
1762       return NULL;
1763     if (locale)
1764       g_object_set_qdata_full (G_OBJECT (accessible), quark_locale, locale,
1765                                g_free);
1766   }
1767   return locale;
1768 }
1769
1770 void
1771 free_value (gpointer data)
1772 {
1773   GValue *value = data;
1774
1775   g_value_unset (value);
1776   g_free (value);
1777 }
1778
1779 GHashTable *
1780 _atspi_accessible_ref_cache (AtspiAccessible *accessible)
1781 {
1782   AtspiAccessiblePrivate *priv = accessible->priv;
1783
1784   priv->cache_ref_count++;
1785   if (priv->cache)
1786     return g_hash_table_ref (priv->cache);
1787   priv->cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
1788                                        free_value);
1789   return priv->cache;
1790 }
1791
1792 void
1793 _atspi_accessible_unref_cache (AtspiAccessible *accessible)
1794 {
1795   AtspiAccessiblePrivate *priv = accessible->priv;
1796
1797   if (priv->cache)
1798   {
1799     g_hash_table_unref (priv->cache);
1800     if (--priv->cache_ref_count == 0)
1801       priv->cache = NULL;
1802   }
1803 }