Fix atspi_accessible_get_index_in_parent when the parent is not cached
[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 <string.h>
27
28 static gboolean enable_caching = FALSE;
29
30 static void
31 atspi_action_interface_init (AtspiAction *action)
32 {
33 }
34
35 static void
36 atspi_collection_interface_init (AtspiCollection *component)
37 {
38 }
39
40 static void
41 atspi_component_interface_init (AtspiComponent *component)
42 {
43 }
44
45 static void
46 atspi_document_interface_init (AtspiDocument *document)
47 {
48 }
49
50 static void
51 atspi_editable_text_interface_init (AtspiEditableText *editable_text)
52 {
53 }
54
55 static void
56 atspi_hypertext_interface_init (AtspiHypertext *hypertext)
57 {
58 }
59
60 static void
61 atspi_image_interface_init (AtspiImage *image)
62 {
63 }
64
65 static void
66 atspi_selection_interface_init (AtspiSelection *selection)
67 {
68 }
69
70 static void
71 atspi_table_interface_init (AtspiTable *table)
72 {
73 }
74
75 static void
76 atspi_text_interface_init (AtspiText *text)
77 {
78 }
79
80 static void
81 atspi_value_interface_init (AtspiValue *value)
82 {
83 }
84
85 G_DEFINE_TYPE_WITH_CODE (AtspiAccessible, atspi_accessible, ATSPI_TYPE_OBJECT,
86                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_ACTION, atspi_action_interface_init)
87                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_COLLECTION, atspi_collection_interface_init)
88                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_COMPONENT, atspi_component_interface_init)
89                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_DOCUMENT, atspi_document_interface_init)
90                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_EDITABLE_TEXT, atspi_editable_text_interface_init)
91                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_HYPERTEXT, atspi_hypertext_interface_init)
92                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_IMAGE, atspi_image_interface_init)
93                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_SELECTION, atspi_selection_interface_init)
94                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_TABLE, atspi_table_interface_init)
95                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_TEXT, atspi_text_interface_init)
96                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_VALUE, atspi_value_interface_init))
97
98 #ifdef DEBUG_REF_COUNTS
99 static gint accessible_count = 0;
100 #endif
101
102 static void
103 atspi_accessible_init (AtspiAccessible *accessible)
104 {
105 #ifdef DEBUG_REF_COUNTS
106   accessible_count++;
107   printf("at-spi: init: %d objects\n", accessible_count);
108 #endif
109 }
110
111 static void
112 atspi_accessible_dispose (GObject *object)
113 {
114   AtspiAccessible *accessible = ATSPI_ACCESSIBLE (object);
115   AtspiEvent e;
116   AtspiAccessible *parent;
117
118   /* TODO: Only fire if object not already marked defunct */
119   memset (&e, 0, sizeof (e));
120   e.type = "object:state-changed:defunct";
121   e.source = accessible;
122   e.detail1 = 1;
123   e.detail2 = 0;
124   _atspi_send_event (&e);
125
126   if (accessible->states)
127   {
128     g_object_unref (accessible->states);
129     accessible->states = NULL;
130   }
131
132   parent = accessible->accessible_parent;
133   if (parent && parent->children)
134   {
135     GList*ls = g_list_find (parent->children, accessible);
136     if(ls)
137     {
138       gboolean replace = (ls == parent->children);
139       ls = g_list_remove (ls, accessible);
140       if (replace)
141         parent->children = ls;
142       g_object_unref (object);
143     }
144   }
145
146   if (parent)
147   {
148     g_object_unref (parent);
149     accessible->accessible_parent = NULL;
150   }
151
152   G_OBJECT_CLASS (atspi_accessible_parent_class) ->dispose (object);
153 }
154
155 static void
156 atspi_accessible_finalize (GObject *object)
157 {
158   AtspiAccessible *accessible = ATSPI_ACCESSIBLE (object);
159
160     g_free (accessible->description);
161     g_free (accessible->name);
162   if (accessible->attributes)
163     g_hash_table_unref (accessible->attributes);
164
165 #ifdef DEBUG_REF_COUNTS
166   accessible_count--;
167   g_print ("at-spi: finalize: %d objects\n", accessible_count);
168 #endif
169
170   G_OBJECT_CLASS (atspi_accessible_parent_class)
171     ->finalize (object);
172 }
173
174 static void
175 atspi_accessible_class_init (AtspiAccessibleClass *klass)
176 {
177   GObjectClass *object_class = G_OBJECT_CLASS (klass);
178
179   object_class->dispose = atspi_accessible_dispose;
180   object_class->finalize = atspi_accessible_finalize;
181 }
182
183 /* TODO: Generate following from spec? */
184 static const char *role_names [] =
185 {
186   "invalid",
187   "accelerator label",
188   "alert",
189   "animation",
190   "arrow",
191   "calendar",
192   "canvas",
193   "check box",
194   "check menu item",
195   "color chooser",
196   "column header",
197   "combo box",
198   "dateeditor",
199   "desktop icon",
200   "desktop frame",
201   "dial",
202   "dialog",
203   "directory pane",
204   "drawing area",
205   "file chooser",
206   "filler",
207   "focus traversable",
208   "fontchooser",
209   "frame",
210   "glass pane",
211   "html container",
212   "icon",
213   "image",
214   "internal frame",
215   "label",
216   "layered pane",
217   "list",
218   "list item",
219   "menu",
220   "menu bar",
221   "menu item",
222   "option pane",
223   "page tab",
224   "page tab list",
225   "panel",
226   "password text",
227   "popup menu",
228   "progress bar",
229   "push button",
230   "radio button",
231   "radio menu item",
232   "root pane",
233   "row header",
234   "scroll bar",
235   "scroll pane",
236   "separator",
237   "slider",
238   "spin button",
239   "split pane",
240   "statusbar",
241   "table",
242   "table cell",
243   "table column header",
244   "table row header",
245   "tear off menu item",
246   "terminal",
247   "text",
248   "toggle button",
249   "tool bar",
250   "tool tip",
251   "tree",
252   "tree table",
253   "unknown",
254   "viewport",
255   "window",
256   NULL,
257   "header",
258   "footer",
259   "paragraph",
260   "ruler",
261   "application",
262   "autocomplete",
263   "editbar",
264   "embedded component",
265   "entry",
266   "chart",
267   "caption",
268   "document frame",
269   "heading",
270   "page",
271   "section",
272   "redundant object",
273   "form",
274   "link",
275   "input method window",
276   "table row",
277   "tree item",
278   "document spreadsheet",
279   "document presentation",
280   "document text",
281   "document web",
282   "document email",
283   "comment",
284   "list box",
285   "grouping",
286   "image map",
287   "notification",
288   "info bar"
289 };
290
291 #define MAX_ROLES (sizeof (role_names) / sizeof (char *))
292
293 /**
294  * atspi_role_get_name:
295  * @role: an #AtspiRole object to query.
296  *
297  * Gets a localizable string that indicates the name of an #AtspiRole.
298  * <em>DEPRECATED.</em>
299  *
300  * Returns: a localizable string name for an #AtspiRole enumerated type.
301  **/
302 gchar *
303 atspi_role_get_name (AtspiRole role)
304 {
305   if (role < MAX_ROLES && role_names [(int) role])
306     {
307       return g_strdup (role_names [(int) role]);
308     }
309   else
310     {
311       return g_strdup ("");
312     }
313 }
314
315 /**
316  * atspi_accessible_get_name:
317  * @obj: a pointer to the #AtspiAccessible object on which to operate.
318  *
319  * Gets the name of an #AtspiAccessible object.
320  *
321  * Returns: a UTF-8 string indicating the name of the #AtspiAccessible object 
322  * or NULL on exception.
323  **/
324 gchar *
325 atspi_accessible_get_name (AtspiAccessible *obj, GError **error)
326 {
327   g_return_val_if_fail (obj != NULL, g_strdup (""));
328   if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_NAME))
329   {
330     if (!_atspi_dbus_get_property (obj, atspi_interface_accessible, "Name", error,
331                                    "s", &obj->name))
332       return g_strdup ("");
333     _atspi_accessible_add_cache (obj, ATSPI_CACHE_NAME);
334   }
335   return g_strdup (obj->name);
336 }
337
338 /**
339  * atspi_accessible_get_description:
340  * @obj: a pointer to the #AtspiAccessible object on which to operate.
341  *
342  * Gets the description of an #AtspiAccessible object.
343  *
344  * Returns: a UTF-8 string describing the #AtspiAccessible object 
345  * or NULL on exception.
346  **/
347 gchar *
348 atspi_accessible_get_description (AtspiAccessible *obj, GError **error)
349 {
350   g_return_val_if_fail (obj != NULL, g_strdup (""));
351
352   if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_DESCRIPTION))
353   {
354     if (!_atspi_dbus_get_property (obj, atspi_interface_accessible,
355                                    "Description", error, "s",
356                                    &obj->description))
357       return g_strdup ("");
358     _atspi_accessible_add_cache (obj, ATSPI_CACHE_DESCRIPTION);
359   }
360   return g_strdup (obj->description);
361 }
362
363 const char *str_parent = "Parent";
364
365 /**
366  * atspi_accessible_get_parent:
367  * @obj: a pointer to the #AtspiAccessible object to query.
368  *
369  * Gets an #AtspiAccessible object's parent container.
370  *
371  * Returns: (transfer full): a pointer to the #AtspiAccessible object which
372  *          contains the given #AtspiAccessible instance, or NULL if the @obj
373  *          has no parent container.
374  *
375  **/
376 AtspiAccessible *
377 atspi_accessible_get_parent (AtspiAccessible *obj, GError **error)
378 {
379   g_return_val_if_fail (obj != NULL, NULL);
380
381   if (obj->parent.app &&
382       !_atspi_accessible_test_cache (obj, ATSPI_CACHE_PARENT))
383   {
384     DBusMessage *message, *reply;
385     DBusMessageIter iter, iter_variant;
386     message = dbus_message_new_method_call (obj->parent.app->bus_name,
387                                             obj->parent.path,
388                                             DBUS_INTERFACE_PROPERTIES, "Get");
389     if (!message)
390       return NULL;
391     dbus_message_append_args (message, DBUS_TYPE_STRING, &atspi_interface_accessible,
392                                DBUS_TYPE_STRING, &str_parent,
393                               DBUS_TYPE_INVALID);
394     reply = _atspi_dbus_send_with_reply_and_block (message, error);
395     if (!reply)
396       return NULL;
397     if (strcmp (dbus_message_get_signature (reply), "v") != 0)
398     {
399       dbus_message_unref (reply);
400       return NULL;
401     }
402     dbus_message_iter_init (reply, &iter);
403     dbus_message_iter_recurse (&iter, &iter_variant);
404     obj->accessible_parent = _atspi_dbus_return_accessible_from_iter (&iter_variant);
405     dbus_message_unref (reply);
406     _atspi_accessible_add_cache (obj, ATSPI_CACHE_PARENT);
407   }
408   if (!obj->accessible_parent)
409     return NULL;
410   return g_object_ref (obj->accessible_parent);
411 }
412
413 /**
414  * atspi_accessible_get_child_count:
415  * @obj: a pointer to the #AtspiAccessible object on which to operate.
416  *
417  * Gets the number of children contained by an #AtspiAccessible object.
418  *
419  * Returns: a #long indicating the number of #AtspiAccessible children
420  *          contained by an #AtspiAccessible object or -1 on exception.
421  *
422  **/
423 gint
424 atspi_accessible_get_child_count (AtspiAccessible *obj, GError **error)
425 {
426   g_return_val_if_fail (obj != NULL, -1);
427
428   if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_CHILDREN))
429   {
430     dbus_int32_t ret;
431     if (!_atspi_dbus_get_property (obj, atspi_interface_accessible,
432                                    "ChildCount", error, "i", &ret))
433       return -1;
434     return ret;
435   }
436
437   return g_list_length (obj->children);
438 }
439
440 /**
441  * atspi_accessible_get_child_at_index:
442  * @obj: a pointer to the #AtspiAccessible object on which to operate.
443  * @child_index: a #long indicating which child is specified.
444  *
445  * Gets the #AtspiAccessible child of an #AtspiAccessible object at a given index.
446  *
447  * Returns: (transfer full): a pointer to the #AtspiAccessible child object at
448  * index @child_index or NULL on exception.
449  **/
450 AtspiAccessible *
451 atspi_accessible_get_child_at_index (AtspiAccessible *obj,
452                             gint    child_index,
453                             GError **error)
454 {
455   AtspiAccessible *child;
456
457   g_return_val_if_fail (obj != NULL, NULL);
458
459   if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_CHILDREN))
460   {
461     DBusMessage *reply;
462     reply = _atspi_dbus_call_partial (obj, atspi_interface_accessible,
463                                      "GetChildAtIndex", error, "i",
464                                      child_index);
465     return _atspi_dbus_return_accessible_from_message (reply);
466   }
467
468   child = g_list_nth_data (obj->children, child_index);
469   if (!child)
470     return NULL;
471   return g_object_ref (child);
472 }
473
474 /**
475  * atspi_accessible_get_index_in_parent:
476  * @obj: a pointer to the #AtspiAccessible object on which to operate.
477  *
478  * Gets the index of an #AtspiAccessible object within its parent's 
479  * #AtspiAccessible children list.
480  *
481  * Returns: a #glong indicating the index of the #AtspiAccessible object
482  *          in its parent,
483  *          or -1 if @obj has no containing parent or on exception.
484  **/
485 gint
486 atspi_accessible_get_index_in_parent (AtspiAccessible *obj, GError **error)
487 {
488   GList *l;
489   gint i = 0;
490
491   g_return_val_if_fail (obj != NULL, -1);
492   if (_atspi_accessible_test_cache (obj, ATSPI_CACHE_PARENT) &&
493       !obj->accessible_parent)
494     return -1;
495   if (!obj->accessible_parent ||
496       !_atspi_accessible_test_cache (obj->accessible_parent,
497                                      ATSPI_CACHE_CHILDREN))
498   {
499     dbus_int32_t ret = -1;
500     _atspi_dbus_call (obj, atspi_interface_accessible,
501                       "GetIndexInParent", NULL, "=>i", &ret);
502     return ret;
503   }
504
505   l = obj->accessible_parent->children;
506   while (l)
507   {
508     if (l->data == obj) return i;
509     l = g_list_next (l);
510     i++;
511   }
512   return -1;
513 }
514
515 typedef struct
516 {
517   dbus_uint32_t type;
518   GArray *targets;
519 } Accessibility_Relation;
520
521 /**
522  * atspi_accessible_get_relation_set:
523  * @obj: a pointer to the #AtspiAccessible object on which to operate.
524  *
525  * Gets the set of #AtspiRelation objects which describes this #AtspiAccessible object's
526  * relationships with other #AtspiAccessible objects.
527  *
528  * Returns: (element-type AtspiAccessible*) (transfer full): a #GArray of
529  *          #AtspiRelation pointers or NULL on exception.
530  **/
531 GArray *
532 atspi_accessible_get_relation_set (AtspiAccessible *obj, GError **error)
533 {
534   DBusMessage *reply;
535   DBusMessageIter iter, iter_array;
536   GArray *ret;
537
538   g_return_val_if_fail (obj != NULL, NULL);
539
540   reply = _atspi_dbus_call_partial (obj, atspi_interface_accessible, "GetRelationSet", error, "");
541   if (!reply)
542     return NULL;
543   _ATSPI_DBUS_CHECK_SIG (reply, "a(ua(so))", error, NULL);
544
545   ret = g_array_new (TRUE, TRUE, sizeof (AtspiRelation *));
546   dbus_message_iter_init (reply, &iter);
547   dbus_message_iter_recurse (&iter, &iter_array);
548   while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
549   {
550     AtspiRelation *relation;
551     relation = _atspi_relation_new_from_iter (&iter_array);
552     ret = g_array_append_val (ret, relation);
553     dbus_message_iter_next (&iter_array);
554   }
555   dbus_message_unref (reply);
556   return ret;
557 }
558
559 /**
560  * atspi_accessible_get_role:
561  * @obj: a pointer to the #AtspiAccessible object on which to operate.
562  *
563  * Gets the UI role played by an #AtspiAccessible object.
564  * This role's name can be obtained via atspi_accessible_get_role_name ().
565  *
566  * Returns: the #AtspiRole of an #AtspiAccessible object.
567  *
568  **/
569 AtspiRole
570 atspi_accessible_get_role (AtspiAccessible *obj, GError **error)
571 {
572   g_return_val_if_fail (obj != NULL, ATSPI_ROLE_INVALID);
573
574   if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_ROLE))
575   {
576     dbus_uint32_t role;
577     /* TODO: Make this a property */
578     if (_atspi_dbus_call (obj, atspi_interface_accessible, "GetRole", error, "=>u", &role))
579     {
580       obj->role = role;
581     _atspi_accessible_add_cache (obj, ATSPI_CACHE_ROLE);
582     }
583   }
584   return obj->role;
585 }
586
587 /**
588  * atspi_accessible_get_role_name:
589  * @obj: a pointer to the #AtspiAccessible object on which to operate.
590  *
591  * Gets a UTF-8 string corresponding to the name of the role played by an object.
592  * This method will return useful values for roles that fall outside the
593  * enumeration used in atspi_accessible_get_role ().
594  *
595  * Returns: a UTF-8 string specifying the type of UI role played by an
596  * #AtspiAccessible object.
597  *
598  **/
599 gchar *
600 atspi_accessible_get_role_name (AtspiAccessible *obj, GError **error)
601 {
602   char *retval = NULL;
603   AtspiRole role;
604
605   g_return_val_if_fail (obj != NULL, NULL);
606
607   role = atspi_accessible_get_role (obj, error);
608   if (role >= 0 && role < MAX_ROLES && role != ATSPI_ROLE_EXTENDED)
609     return g_strdup (role_names [role]);
610
611   _atspi_dbus_call (obj, atspi_interface_accessible, "GetRoleName", error, "=>s", &retval);
612
613   if (!retval)
614     retval = g_strdup ("");
615
616   return retval;
617 }
618
619 /**
620  * atspi_accessible_get_localized_role_name:
621  * @obj: a pointer to the #AtspiAccessible object on which to operate.
622  *
623  * Gets a UTF-8 string corresponding to the name of the role played by an 
624  * object, translated to the current locale.
625  * This method will return useful values for roles that fall outside the
626  * enumeration used in atspi_accessible_getRole ().
627  *
628  * Returns: a localized, UTF-8 string specifying the type of UI role played 
629  * by an #AtspiAccessible object.
630  *
631  **/
632 gchar *
633 atspi_accessible_get_localized_role_name (AtspiAccessible *obj, GError **error)
634 {
635   char *retval = NULL;
636
637   g_return_val_if_fail (obj != NULL, NULL);
638
639   _atspi_dbus_call (obj, atspi_interface_accessible, "GetLocalizedRoleName", error, "=>s", &retval);
640
641   if (!retval)
642     return g_strdup ("");
643
644   return retval;
645 }
646
647 static AtspiStateSet *
648 defunct_set ()
649 {
650   AtspiStateSet *set = atspi_state_set_new (NULL);
651   atspi_state_set_add (set, ATSPI_STATE_DEFUNCT);
652   return set;
653 }
654
655 /**
656  * atspi_accessible_get_state_set:
657  * @obj: a pointer to the #AtspiAccessible object on which to operate.
658  *
659  * Gets the states currently held by an object.
660  *
661  * Returns: (transfer full): a pointer to an #AtspiStateSet representing an
662  * object's current state set.
663  **/
664 AtspiStateSet *
665 atspi_accessible_get_state_set (AtspiAccessible *obj)
666 {
667   /* TODO: Should take a GError **, but would be an API break */
668   if (!obj->parent.app || !obj->parent.app->bus)
669     return defunct_set ();
670
671   if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_STATES))
672   {
673     DBusMessage *reply;
674     DBusMessageIter iter;
675     reply = _atspi_dbus_call_partial (obj, atspi_interface_accessible,
676                                       "GetState", NULL, "");
677     _ATSPI_DBUS_CHECK_SIG (reply, "au", NULL, defunct_set ());
678     dbus_message_iter_init (reply, &iter);
679     _atspi_dbus_set_state (obj, &iter);
680     dbus_message_unref (reply);
681     _atspi_accessible_add_cache (obj, ATSPI_CACHE_STATES);
682   }
683
684   return g_object_ref (obj->states);
685 }
686
687 /**
688  * atspi_accessible_get_attributes:
689  * @obj: The #AtspiAccessible being queried.
690  *
691  * Gets the #AttributeSet representing any assigned 
692  * name-value pair attributes or annotations for this object.
693  * For typographic, textual, or textually-semantic attributes, see
694  * atspi_text_get_attributes instead.
695  *
696  * Returns: (element-type gchar* gchar*) (transfer full): The name-value-pair
697  * attributes assigned to this object.
698  */
699 GHashTable *
700 atspi_accessible_get_attributes (AtspiAccessible *obj, GError **error)
701 {
702   DBusMessage *message;
703
704     g_return_val_if_fail (obj != NULL, NULL);
705
706   if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_ATTRIBUTES))
707   {
708     message = _atspi_dbus_call_partial (obj, atspi_interface_accessible,
709                                         "GetAttributes", error, "");
710     obj->attributes = _atspi_dbus_return_hash_from_message (message);
711     _atspi_accessible_add_cache (obj, ATSPI_CACHE_ATTRIBUTES);
712   }
713
714   if (!obj->attributes)
715     return NULL;
716   return g_hash_table_ref (obj->attributes);
717 }
718
719 static void
720 add_to_attribute_array (gpointer key, gpointer value, gpointer data)
721 {
722   GArray **array = (GArray **)data;
723   gchar *str = g_strconcat (key, ":", value, NULL);
724   *array = g_array_append_val (*array, str);
725 }
726
727 /**
728  * atspi_accessible_get_attributes_as_array:
729  * @obj: The #AtspiAccessible being queried.
730  *
731  * Gets a #GArray representing any assigned 
732  * name-value pair attributes or annotations for this object.
733  * For typographic, textual, or textually-semantic attributes, see
734  * atspi_text_get_attributes_as_array instead.
735  *
736  * Returns: (element-type gchar*) (transfer full): The name-value-pair
737  *          attributes assigned to this object.
738  */
739 GArray *
740 atspi_accessible_get_attributes_as_array (AtspiAccessible *obj, GError **error)
741 {
742   DBusMessage *message;
743
744     g_return_val_if_fail (obj != NULL, NULL);
745
746   if (_atspi_accessible_get_cache_mask (obj) & ATSPI_CACHE_ATTRIBUTES)
747   {
748     GArray *array = g_array_new (TRUE, TRUE, sizeof (gchar *));
749     GHashTable *attributes = atspi_accessible_get_attributes (obj, error);
750     if (!attributes)
751       return NULL;
752     g_hash_table_foreach (attributes, add_to_attribute_array, &array);
753     g_hash_table_unref (attributes);
754     return array;
755   }
756
757   message = _atspi_dbus_call_partial (obj, atspi_interface_accessible, "GetAttributes", error, "");
758   return _atspi_dbus_return_attribute_array_from_message (message);
759 }
760
761 /**
762  * atspi_accessible_get_application:
763  * @obj: The #AtspiAccessible being queried.
764  *
765  * Gets the containing #AtspiApplication for an object.
766  *
767  * Returns: (transfer full): the containing #AtspiApplication instance for
768  *          this object.
769  */
770 AtspiAccessible *
771 atspi_accessible_get_application (AtspiAccessible *obj, GError **error)
772 {
773   AtspiAccessible *parent;
774
775   g_object_ref (obj);
776   for (;;)
777   {
778     parent = atspi_accessible_get_parent (obj, NULL);
779     if (!parent && obj->parent.app &&
780         atspi_accessible_get_role (obj, NULL) != ATSPI_ROLE_APPLICATION)
781     {
782       AtspiAccessible *root = g_object_ref (obj->parent.app->root);
783       if (root)
784       {
785         g_object_unref (obj);
786         if (atspi_accessible_get_role (root, NULL) == ATSPI_ROLE_DESKTOP_FRAME)
787         {
788           g_object_unref (root);
789           return NULL;
790         }
791         return root;
792       }
793     }
794     if (!parent || parent == obj ||
795         atspi_accessible_get_role (parent, NULL) == ATSPI_ROLE_DESKTOP_FRAME)
796     return obj;
797     g_object_unref (obj);
798     obj = parent;
799   }
800 }
801
802 /* Application-specific methods */
803
804 /**
805  * atspi_accessible_get_toolkit_name:
806  * @obj: a pointer to the #AtspiAccessible object on which to operate.
807  *
808  * Gets the toolkit name for an #AtspiAccessible object.
809  * Only works on application root objects.
810  *
811  * Returns: a UTF-8 string indicating the toolkit name for the #AtspiAccessible object or NULL on exception.
812  **/
813 gchar *
814 atspi_accessible_get_toolkit_name (AtspiAccessible *obj, GError **error)
815 {
816   g_return_val_if_fail (obj != NULL, NULL);
817
818   if (!obj->parent.app)
819     return NULL;
820
821   if (!obj->parent.app->toolkit_name)
822     _atspi_dbus_get_property (obj, atspi_interface_application, "ToolkitName",
823                               error, "s", &obj->parent.app->toolkit_name);
824
825   return g_strdup (obj->parent.app->toolkit_name);
826 }
827
828 /**
829  * atspi_accessible_get_toolkit_version:
830  * @obj: a pointer to the #AtspiAccessible object on which to operate.
831  *
832  * Gets the toolkit version for an #AtspiAccessible object.
833  * Only works on application root objects.
834  *
835  * Returns: a UTF-8 string indicating the toolkit version for the #AtspiAccessible object or NULL on exception.
836  **/
837 gchar *
838 atspi_accessible_get_toolkit_version (AtspiAccessible *obj, GError **error)
839 {
840   g_return_val_if_fail (obj != NULL, NULL);
841
842   if (!obj->parent.app)
843     return NULL;
844
845   if (!obj->parent.app->toolkit_version)
846     _atspi_dbus_get_property (obj, atspi_interface_application, "Version",
847                               error, "s", &obj->parent.app->toolkit_version);
848
849   return g_strdup (obj->parent.app->toolkit_version);
850 }
851
852 /**
853  * atspi_accessible_get_atspi_version:
854  * @obj: a pointer to the #AtspiAccessible object on which to operate.
855  *
856  * Gets the AT-SPI IPC specification version supported by the application
857  * pointed to by the #AtspiAccessible object.
858  * Only works on application root objects.
859  *
860  * Returns: a UTF-8 string indicating the AT-SPI version for the #AtspiAccessible object or NULL on exception.
861  **/
862 gchar *
863 atspi_accessible_get_atspi_version (AtspiAccessible *obj, GError **error)
864 {
865   g_return_val_if_fail (obj != NULL, NULL);
866
867   if (!obj->parent.app)
868     return NULL;
869
870   if (!obj->parent.app->atspi_version)
871     _atspi_dbus_get_property (obj, atspi_interface_application, "AtspiVersion",
872                               error, "s", &obj->parent.app->atspi_version);
873
874   return g_strdup (obj->parent.app->atspi_version);
875 }
876
877 /**
878  * atspi_accessible_get_id:
879  * @obj: a pointer to the #AtspiAccessible object on which to operate.
880  *
881  * Gets the application id for a #AtspiAccessible object.
882  * Only works on application root objects.
883  *
884  * Returns: a positive #gint indicating the id for the #AtspiAccessible object 
885  * or -1 on exception.
886  **/
887 gint
888 atspi_accessible_get_id (AtspiAccessible *obj, GError **error)
889 {
890   gint ret = -1;
891
892   g_return_val_if_fail (obj != NULL, -1);
893
894   if (!_atspi_dbus_get_property (obj, atspi_interface_application, "Id", error, "i", &ret))
895       return -1;
896   return ret;
897 }
898
899
900 /* Interface query methods */
901
902 static gboolean
903 _atspi_accessible_is_a (AtspiAccessible *accessible,
904                       const char *interface_name)
905 {
906   int n;
907
908   if (accessible == NULL)
909     {
910       return FALSE;
911     }
912
913   if (!_atspi_accessible_test_cache (accessible, ATSPI_CACHE_INTERFACES))
914   {
915     DBusMessage *reply;
916     DBusMessageIter iter;
917     reply = _atspi_dbus_call_partial (accessible, atspi_interface_accessible,
918                                       "GetInterfaces", NULL, "");
919     _ATSPI_DBUS_CHECK_SIG (reply, "as", NULL, FALSE);
920     dbus_message_iter_init (reply, &iter);
921     _atspi_dbus_set_interfaces (accessible, &iter);
922     dbus_message_unref (reply);
923     _atspi_accessible_add_cache (accessible, ATSPI_CACHE_INTERFACES);
924   }
925
926   n = _atspi_get_iface_num (interface_name);
927   if (n == -1) return FALSE;
928   return (gboolean) ((accessible->interfaces & (1 << n))? TRUE: FALSE);
929 }
930
931 /**
932  * atspi_accessible_is_action:
933  * @obj: a pointer to the #AtspiAccessible instance to query.
934  *
935  * Query whether the specified #AtspiAccessible implements the 
936  * #AtspiAction interface.
937  *
938  * Returns: #TRUE if @obj implements the #AtspiAction interface,
939  *          #FALSE otherwise.
940  **/
941 gboolean
942 atspi_accessible_is_action (AtspiAccessible *obj)
943 {
944   return _atspi_accessible_is_a (obj,
945                               atspi_interface_action);
946 }
947
948 /**
949  * atspi_accessible_is_application:
950  * @obj: a pointer to the #AtspiAccessible instance to query.
951  *
952  * Query whether the specified #AtspiAccessible implements the
953  * #AtspiApplication interface.
954  *
955  * Returns: #TRUE if @obj implements the #AtspiApplication interface,
956  *          #FALSE otherwise.
957  **/
958 gboolean
959 atspi_accessible_is_application (AtspiAccessible *obj)
960 {
961   return _atspi_accessible_is_a (obj,
962                               atspi_interface_application);
963 }
964
965 /**                      
966  * atspi_accessible_is_collection:
967  * @obj: a pointer to the #AtspiAccessible instance to query.
968  *
969  * Query whether the specified #AtspiAccessible implements the
970  * #AtspiCollection interface.
971  *
972  * Returns: #TRUE if @obj implements the #AtspiCollection interface,
973  *          #FALSE otherwise.
974  **/
975 gboolean
976 atspi_accessible_is_collection (AtspiAccessible *obj)
977 {
978      return _atspi_accessible_is_a (obj,
979                               atspi_interface_collection);
980 }
981
982 /**
983  * atspi_accessible_is_component:
984  * @obj: a pointer to the #AtspiAccessible instance to query.
985  *
986  * Query whether the specified #AtspiAccessible implements #AtspiComponent.
987  *
988  * Returns: #TRUE if @obj implements the #AtspiComponent interface,
989  *          #FALSE otherwise.
990  **/
991 gboolean
992 atspi_accessible_is_component (AtspiAccessible *obj)
993 {
994   return _atspi_accessible_is_a (obj,
995                               atspi_interface_component);
996 }
997
998 /**
999  * atspi_accessible_is_document:
1000  * @obj: a pointer to the #AtspiAccessible instance to query.
1001  *
1002  * Query whether the specified #AtspiAccessible implements the
1003  * #AtspiDocument interface.
1004  *
1005  * Returns: #TRUE if @obj implements the #AtspiDocument interface,
1006  *          #FALSE otherwise.
1007  **/
1008 gboolean
1009 atspi_accessible_is_document (AtspiAccessible *obj)
1010 {
1011   return _atspi_accessible_is_a (obj,
1012                               atspi_interface_document);
1013 }
1014
1015 /**
1016  * atspi_accessible_is_editable_text:
1017  * @obj: a pointer to the #AtspiAccessible instance to query.
1018  *
1019  * Query whether the specified #AtspiAccessible implements the
1020  * #AtspiEditableText interface.
1021  *
1022  * Returns: #TRUE if @obj implements the #AtspiEditableText interface,
1023  *          #FALSE otherwise.
1024  **/
1025 gboolean
1026 atspi_accessible_is_editable_text (AtspiAccessible *obj)
1027 {
1028   return _atspi_accessible_is_a (obj,
1029                               atspi_interface_editable_text);
1030 }
1031                                                                                                                                                                         
1032 /**
1033  * atspi_accessible_is_hypertext:
1034  * @obj: a pointer to the #AtspiAccessible instance to query.
1035  *
1036  * Query whether the specified #AtspiAccessible implements the
1037  * #AtspiHypertext interface.
1038  *
1039  * Returns: #TRUE if @obj implements the #AtspiHypertext interface,
1040  *          #FALSE otherwise.
1041  **/
1042 gboolean
1043 atspi_accessible_is_hypertext (AtspiAccessible *obj)
1044 {
1045   return _atspi_accessible_is_a (obj,
1046                               atspi_interface_hypertext);
1047 }
1048
1049 /**
1050  * atspi_accessible_is_hyperlink:
1051  * @obj: a pointer to the #AtspiAccessible instance to query.
1052  *
1053  * Query whether the specified #AtspiAccessible implements the 
1054  * #AtspiHyperlink interface.
1055  *
1056  * Returns: #TRUE if @obj implements the #AtspiHypertext interface,
1057  *          #FALSE otherwise.
1058  **/
1059 gboolean
1060 atspi_accessible_is_hyperlink (AtspiAccessible *obj)
1061 {
1062   return _atspi_accessible_is_a (obj,
1063                               atspi_interface_hyperlink);
1064 }
1065
1066 /**
1067  * atspi_accessible_is_image:
1068  * @obj: a pointer to the #AtspiAccessible instance to query.
1069  *
1070  * Query whether the specified #AtspiAccessible implements the
1071  * #AtspiImage interface.
1072  *
1073  * Returns: #TRUE if @obj implements the #AtspiImage interface,
1074  *          #FALSE otherwise.
1075 **/
1076 gboolean
1077 atspi_accessible_is_image (AtspiAccessible *obj)
1078 {
1079   return _atspi_accessible_is_a (obj,
1080                               atspi_interface_image);
1081 }
1082
1083 /**
1084  * atspi_accessible_is_selection:
1085  * @obj: a pointer to the #AtspiAccessible instance to query.
1086  *
1087  * Query whether the specified #AtspiAccessible implements the
1088  * #AtspiSelection interface.
1089  *
1090  * Returns: #TRUE if @obj implements the #AtspiSelection interface,
1091  *          #FALSE otherwise.
1092 **/
1093 gboolean
1094 atspi_accessible_is_selection (AtspiAccessible *obj)
1095 {
1096   return _atspi_accessible_is_a (obj,
1097                               atspi_interface_selection);
1098 }
1099
1100 /**
1101  * atspi_accessible_is_table:
1102  * @obj: a pointer to the #AtspiAccessible instance to query.
1103  *
1104  * Query whether the specified #AtspiAccessible implements the
1105  * #AtspiTable interface.
1106  *
1107  * Returns: #TRUE if @obj implements the #AtspiTable interface,
1108  *          #FALSE otherwise.
1109 **/
1110 gboolean
1111 atspi_accessible_is_table (AtspiAccessible *obj)
1112 {
1113   return _atspi_accessible_is_a (obj,
1114                               atspi_interface_table);
1115 }
1116
1117 /**
1118  * atspi_accessible_is_streamable_content:
1119  * @obj: a pointer to the #AtspiAccessible instance to query.
1120  *
1121  * Query whether the specified #AtspiAccessible implements the
1122  * #AtspiStreamableContent interface.
1123  *
1124  * Returns: #TRUE if @obj implements the #AtspiStreamableContent interface,
1125  *          #FALSE otherwise.
1126 **/
1127 gboolean
1128 atspi_accessible_is_streamable_content (AtspiAccessible *obj)
1129 {
1130 #if 0
1131   return _atspi_accessible_is_a (obj,
1132                               atspi_interface_streamable_content);
1133 #else
1134   g_warning ("Streamable content not implemented");
1135   return FALSE;
1136 #endif
1137 }
1138
1139 /**
1140  * atspi_accessible_is_text:
1141  * @obj: a pointer to the #AtspiAccessible instance to query.
1142  *
1143  * Query whether the specified #AtspiAccessible implements the 
1144  * #AtspiText interface.
1145  *
1146  * Returns: #TRUE if @obj implements the #AtspiText interface,
1147  *          #FALSE otherwise.
1148 **/
1149 gboolean
1150 atspi_accessible_is_text (AtspiAccessible *obj)
1151 {
1152   return _atspi_accessible_is_a (obj,
1153                               atspi_interface_text);
1154 }
1155
1156 /**
1157  * atspi_accessible_is_value:
1158  * @obj: a pointer to the #AtspiAccessible instance to query.
1159  *
1160  * Query whether the specified #AtspiAccessible implements the
1161  * #AtspiValue interface.
1162  *
1163  * Returns: #TRUE if @obj implements the #AtspiValue interface,
1164  *          #FALSE otherwise.
1165 **/
1166 gboolean
1167 atspi_accessible_is_value (AtspiAccessible *obj)
1168 {
1169   return _atspi_accessible_is_a (obj,
1170                               atspi_interface_value);
1171 }
1172
1173 /**
1174  * atspi_accessible_get_action:
1175  * @obj: a pointer to the #AtspiAccessible instance to query.
1176  *
1177  * Gets the #AtspiAction interface for an #AtspiAccessible.
1178  *
1179  * Returns: (transfer full): a pointer to an #AtspiAction interface
1180  *          instance, or NULL if @obj does not implement #AtspiAction.
1181  **/
1182 AtspiAction *
1183 atspi_accessible_get_action (AtspiAccessible *accessible)
1184 {
1185   return (_atspi_accessible_is_a (accessible, atspi_interface_action) ?
1186           g_object_ref (ATSPI_ACTION (accessible)) : NULL);  
1187 }
1188
1189 /**
1190  * atspi_accessible_get_collection:
1191  * @obj: a pointer to the #AtspiAccessible instance to query.
1192  *
1193  * Gets the #AtspiCollection interface for an #AtspiAccessible.
1194  *
1195  * Returns: (transfer full): a pointer to an #AtspiCollection interface
1196  *          instance, or NULL if @obj does not implement #AtspiCollection.
1197  **/
1198 AtspiCollection *
1199 atspi_accessible_get_collection (AtspiAccessible *accessible)
1200 {
1201   return (_atspi_accessible_is_a (accessible, atspi_interface_collection) ?
1202           g_object_ref (ATSPI_COLLECTION (accessible)) : NULL);  
1203 }
1204
1205 /**
1206  * atspi_accessible_get_component:
1207  * @obj: a pointer to the #AtspiAccessible instance to query.
1208  *
1209  * Gets the #AtspiComponent interface for an #AtspiAccessible.
1210  *
1211  * Returns: (transfer full): a pointer to an #AtspiComponent interface
1212  *          instance, or NULL if @obj does not implement #AtspiComponent.
1213  **/
1214 AtspiComponent *
1215 atspi_accessible_get_component (AtspiAccessible *obj)
1216 {
1217   return (_atspi_accessible_is_a (obj, atspi_interface_component) ?
1218           g_object_ref (ATSPI_COMPONENT (obj)) : NULL);
1219 }
1220
1221 /**
1222  * atspi_accessible_get_document:
1223  * @obj: a pointer to the #AtspiAccessible instance to query.
1224  *
1225  * Gets the #AtspiDocument interface for an #AtspiAccessible.
1226  *
1227  * Returns: (transfer full): a pointer to an #AtspiDocument interface
1228  *          instance, or NULL if @obj does not implement #AtspiDocument.
1229  **/
1230 AtspiDocument *
1231 atspi_accessible_get_document (AtspiAccessible *accessible)
1232 {
1233   return (_atspi_accessible_is_a (accessible, atspi_interface_document) ?
1234           g_object_ref (ATSPI_DOCUMENT (accessible)) : NULL);  
1235 }
1236
1237 /**
1238  * atspi_accessible_get_editable_text:
1239  * @obj: a pointer to the #AtspiAccessible instance to query.
1240  *
1241  * Gets the #AtspiEditableText interface for an #AtspiAccessible.
1242  *
1243  * Returns: (transfer full): a pointer to an #AtspiEditableText interface
1244  *          instance, or NULL if @obj does not implement #AtspiEditableText.
1245  **/
1246 AtspiEditableText *
1247 atspi_accessible_get_editable_text (AtspiAccessible *accessible)
1248 {
1249   return (_atspi_accessible_is_a (accessible, atspi_interface_editable_text) ?
1250           g_object_ref (ATSPI_EDITABLE_TEXT (accessible)) : NULL);  
1251 }
1252
1253 /**
1254  * atspi_accessible_get_hyperlink:
1255  * @obj: a pointer to the #AtspiAccessible object on which to operate.
1256  *
1257  * Gets the #AtspiHyperlink interface for an #AtspiAccessible.
1258  *
1259  * Returns: (transfer full): the #AtspiHyperlink object associated with
1260  *          the given #AtspiAccessible, or NULL if not supported.
1261  **/
1262 AtspiHyperlink *
1263 atspi_accessible_get_hyperlink (AtspiAccessible *accessible)
1264 {
1265   return (_atspi_accessible_is_a (accessible, atspi_interface_hyperlink) ?
1266           _atspi_hyperlink_new (accessible->parent.app, accessible->parent.path) : NULL);
1267 }
1268
1269 /**
1270  * atspi_accessible_get_hypertext:
1271  * @obj: a pointer to the #AtspiAccessible instance to query.
1272  *
1273  * Gets the #AtspiHypertext interface for an #AtspiAccessible.
1274  *
1275  * Returns: (transfer full): a pointer to an #AtspiHypertext interface
1276  *          instance, or NULL if @obj does not implement #AtspiHypertext.
1277  **/
1278 AtspiHypertext *
1279 atspi_accessible_get_hypertext (AtspiAccessible *accessible)
1280 {
1281   return (_atspi_accessible_is_a (accessible, atspi_interface_hypertext) ?
1282           g_object_ref (ATSPI_HYPERTEXT (accessible)) : NULL);  
1283 }
1284
1285 /**
1286  * atspi_accessible_get_image:
1287  * @obj: a pointer to the #AtspiAccessible instance to query.
1288  *
1289  * Gets the #AtspiImage interface for an #AtspiAccessible.
1290  *
1291  * Returns: (transfer full): a pointer to an #AtspiImage interface instance, or
1292  *          NULL if @obj does not implement #AtspiImage.
1293  **/
1294 AtspiImage *
1295 atspi_accessible_get_image (AtspiAccessible *accessible)
1296 {
1297   return (_atspi_accessible_is_a (accessible, atspi_interface_image) ?
1298           g_object_ref (ATSPI_IMAGE (accessible)) : NULL);  
1299 }
1300
1301 /**
1302  * atspi_accessible_get_selection:
1303  * @obj: a pointer to the #AtspiAccessible instance to query.
1304  *
1305  * Gets the #AtspiSelection interface for an #AtspiAccessible.
1306  *
1307  * Returns: (transfer full): a pointer to an #AtspiSelection interface
1308  *          instance, or NULL if @obj does not implement #AtspiSelection.
1309  **/
1310 AtspiSelection *
1311 atspi_accessible_get_selection (AtspiAccessible *accessible)
1312 {
1313   return (_atspi_accessible_is_a (accessible, atspi_interface_selection) ?
1314           g_object_ref (ATSPI_SELECTION (accessible)) : NULL);  
1315 }
1316
1317 #if 0
1318 /**
1319  * atspi_accessible_get_streamable_content:
1320  * @obj: a pointer to the #AtspiAccessible instance to query.
1321  *
1322  * Gets the #AtspiStreamableContent interface for an #AtspiAccessible.
1323  *
1324  * Returns: (transfer full): a pointer to an #AtspiStreamableContent interface
1325  *          instance, or NULL if @obj does not implement #AtspiStreamableContent.
1326  **/
1327 AtspiStreamableContent *
1328 atspi_accessible_get_streamable_content (AtspiAccessible *accessible)
1329 {
1330   return (_atspi_accessible_is_a (accessible, atspi_interface_streamable_content) ?
1331           accessible : NULL);  
1332 }
1333 #endif
1334
1335 /**
1336  * atspi_accessible_get_table:
1337  * @obj: a pointer to the #AtspiAccessible instance to query.
1338  *
1339  * Gets the #AtspiTable interface for an #AtspiAccessible.
1340  *
1341  * Returns: (transfer full): a pointer to an #AtspiTable interface instance, or
1342  *          NULL if @obj does not implement #AtspiTable.
1343  **/
1344 AtspiTable *
1345 atspi_accessible_get_table (AtspiAccessible *obj)
1346 {
1347   return (_atspi_accessible_is_a (obj, atspi_interface_table) ?
1348           g_object_ref (ATSPI_TABLE (obj)) : NULL);  
1349 }
1350
1351 /**
1352  * atspi_accessible_get_text:
1353  * @obj: a pointer to the #AtspiAccessible instance to query.
1354  *
1355  * Gets the #AtspiTable interface for an #AtspiAccessible.
1356  *
1357  * Returns: (transfer full): a pointer to an #AtspiText interface instance, or
1358  *          NULL if @obj does not implement #AtspiText.
1359  **/
1360 AtspiText *
1361 atspi_accessible_get_text (AtspiAccessible *obj)
1362 {
1363   return (_atspi_accessible_is_a (obj, atspi_interface_text) ?
1364           g_object_ref (ATSPI_TEXT (obj)) : NULL);
1365 }
1366
1367 /**
1368  * atspi_accessible_get_value:
1369  * @obj: a pointer to the #AtspiAccessible instance to query.
1370  *
1371  * Gets the #AtspiTable interface for an #AtspiAccessible.
1372  *
1373  * Returns: (transfer full): a pointer to an #AtspiValue interface instance, or
1374  *          NULL if @obj does not implement #AtspiValue.
1375  **/
1376 AtspiValue *
1377 atspi_accessible_get_value (AtspiAccessible *accessible)
1378 {
1379   return (_atspi_accessible_is_a (accessible, atspi_interface_value) ?
1380           g_object_ref (ATSPI_VALUE (accessible)) : NULL);  
1381 }
1382
1383 static void
1384 append_const_val (GArray *array, const gchar *val)
1385 {
1386   gchar *dup = g_strdup (val);
1387
1388   if (dup)
1389     g_array_append_val (array, dup);
1390 }
1391
1392 /**
1393  * atspi_accessible_get_interfaces:
1394  * @obj: The #AtspiAccessible to query.
1395  *
1396  * A set of pointers to all interfaces supported by an #AtspiAccessible.
1397  *
1398  * Returns: (element-type gchar*) (transfer full): A #GArray of strings
1399  *          describing the interfaces supported by the object.  Interfaces are
1400  *          denoted in short-hand (i.e. "Component", "Text" etc.).
1401  **/
1402 GArray *
1403 atspi_accessible_get_interfaces (AtspiAccessible *obj)
1404 {
1405   GArray *ret = g_array_new (TRUE, TRUE, sizeof (gchar *));
1406
1407   g_return_val_if_fail (obj != NULL, NULL);
1408
1409   append_const_val (ret, "Accessible");
1410   if (atspi_accessible_is_action (obj))
1411     append_const_val (ret, "Action");
1412   if (atspi_accessible_is_collection (obj))
1413     append_const_val (ret, "Collection");
1414   if (atspi_accessible_is_component (obj))
1415     append_const_val (ret, "Component");
1416   if (atspi_accessible_is_document (obj))
1417     append_const_val (ret, "Document");
1418   if (atspi_accessible_is_editable_text (obj))
1419     append_const_val (ret, "EditableText");
1420   if (atspi_accessible_is_hypertext (obj))
1421     append_const_val (ret, "Hypertext");
1422   if (atspi_accessible_is_hyperlink (obj))
1423     append_const_val (ret, "Hyperlink");
1424   if (atspi_accessible_is_image (obj))
1425     append_const_val (ret, "Image");
1426   if (atspi_accessible_is_selection (obj))
1427     append_const_val (ret, "Selection");
1428   if (atspi_accessible_is_table (obj))
1429     append_const_val (ret, "Table");
1430   if (atspi_accessible_is_text (obj))
1431     append_const_val (ret, "Text");
1432   if (atspi_accessible_is_value (obj))
1433     append_const_val (ret, "Value");
1434
1435   return ret;
1436 }
1437
1438 AtspiAccessible * 
1439 _atspi_accessible_new (AtspiApplication *app, const gchar *path)
1440 {
1441   AtspiAccessible *accessible;
1442   
1443   accessible = g_object_new (ATSPI_TYPE_ACCESSIBLE, NULL);
1444   g_return_val_if_fail (accessible != NULL, NULL);
1445
1446   accessible->parent.app = g_object_ref (app);
1447   accessible->parent.path = g_strdup (path);
1448
1449   return accessible;
1450 }
1451
1452 /**
1453  * atspi_accessible_set_cache_mask:
1454  * @accessible: The #AtspiAccessible to operate on.  Must be the desktop or
1455  *             the root of an application.
1456  * @mask: (type int): An #AtspiCache specifying a bit mask of the types of data to cache.
1457  *
1458  * Sets the type of data to cache for accessibles.
1459  * If this is not set for an application or is reset to ATSPI_CACHE_UNDEFINED,
1460  * then the desktop's cache flag will be used.
1461  * If the desktop's cache flag is also undefined, then all possible data will
1462  * be cached.
1463  * This function is intended to work around bugs in toolkits where the proper
1464  * events are not raised / to aid in testing for such bugs.
1465  **/
1466 void
1467 atspi_accessible_set_cache_mask (AtspiAccessible *accessible, AtspiCache mask)
1468 {
1469   g_return_if_fail (accessible != NULL);
1470   g_return_if_fail (accessible->parent.app != NULL);
1471   g_return_if_fail (accessible == accessible->parent.app->root);
1472   accessible->parent.app->cache = mask;
1473   enable_caching = TRUE;
1474 }
1475
1476 /**
1477  * atspi_accessible_clear_cache:
1478  * @accessible: The #AtspiAccessible whose cache to clear.
1479  *
1480  * Clears the cached information for the given accessible and all of its
1481  * descendants.
1482  */
1483 void
1484 atspi_accessible_clear_cache (AtspiAccessible *accessible)
1485 {
1486   GList *l;
1487
1488   if (accessible)
1489   {
1490     accessible->cached_properties = ATSPI_CACHE_NONE;
1491     for (l = accessible->children; l; l = l->next)
1492       atspi_accessible_clear_cache (l->data);
1493   }
1494 }
1495
1496 /**
1497  * atspi_accessible_get_process_id:
1498  * @accessible: The #AtspiAccessible to query.
1499  *
1500  * Returns the process id associated with the given accessible.  Mainly
1501  * added for debugging; it is a shortcut to explicitly querying the
1502  * accessible's app->bus_name and then calling GetConnectionUnixProcessID.
1503  *
1504  * Returns: The process ID, or -1 if defunct.
1505  **/
1506 guint
1507 atspi_accessible_get_process_id (AtspiAccessible *accessible, GError **error)
1508 {
1509   DBusMessage *message, *reply;
1510   DBusConnection *bus = _atspi_bus ();
1511   dbus_uint32_t pid = -1;
1512   DBusError d_error;
1513
1514   if (!accessible->parent.app || !accessible->parent.app->bus_name)
1515     return -1;
1516
1517   message = dbus_message_new_method_call ("org.freedesktop.DBus",
1518                                           "/org/freedesktop/DBus",
1519                                           "org.freedesktop.DBus",
1520                                           "GetConnectionUnixProcessID");
1521   dbus_message_append_args (message, DBUS_TYPE_STRING,
1522                             &accessible->parent.app->bus_name,
1523                             DBUS_TYPE_INVALID);
1524   dbus_error_init (&d_error);
1525   reply = dbus_connection_send_with_reply_and_block (bus, message, -1, &d_error);
1526   dbus_message_unref (message);
1527   dbus_message_get_args (reply, NULL, DBUS_TYPE_UINT32, &pid, DBUS_TYPE_INVALID);
1528   dbus_message_unref (reply);
1529   if (dbus_error_is_set (&d_error))
1530     {
1531       g_warning ("GetConnectionUnixProcessID failed: %s", d_error.message);
1532       dbus_error_free (&d_error);
1533     }
1534   return pid;
1535 }
1536
1537 AtspiCache
1538 _atspi_accessible_get_cache_mask (AtspiAccessible *accessible)
1539 {
1540   AtspiCache mask;
1541
1542   if (!accessible->parent.app)
1543     return ATSPI_CACHE_NONE;
1544
1545  mask = accessible->parent.app->cache;
1546   if (mask == ATSPI_CACHE_UNDEFINED &&
1547       accessible->parent.app->root &&
1548       accessible->parent.app->root->accessible_parent)
1549   {
1550     AtspiAccessible *desktop = atspi_get_desktop (0);
1551     mask = desktop->parent.app->cache;
1552     g_object_unref (desktop);
1553   }
1554
1555   if (mask == ATSPI_CACHE_UNDEFINED)
1556     mask = ATSPI_CACHE_DEFAULT;
1557
1558   return mask;
1559 }
1560
1561 gboolean
1562 _atspi_accessible_test_cache (AtspiAccessible *accessible, AtspiCache flag)
1563 {
1564   AtspiCache mask = _atspi_accessible_get_cache_mask (accessible);
1565   AtspiCache result = accessible->cached_properties & mask & flag;
1566   if (accessible->states && atspi_state_set_contains (accessible->states, ATSPI_STATE_TRANSIENT))
1567     return FALSE;
1568   return (result != 0 && (atspi_main_loop || enable_caching) &&
1569           !atspi_no_cache);
1570 }
1571
1572 void
1573 _atspi_accessible_add_cache (AtspiAccessible *accessible, AtspiCache flag)
1574 {
1575   AtspiCache mask = _atspi_accessible_get_cache_mask (accessible);
1576
1577   accessible->cached_properties |= flag & mask;
1578 }