Fix for bug 675004 - object:state-changed:defunct events are emitted as object:state...
[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 (!obj->accessible_parent) return -1;
493   if (!_atspi_accessible_test_cache (obj->accessible_parent,
494                                      ATSPI_CACHE_CHILDREN))
495   {
496     dbus_int32_t ret = -1;
497     _atspi_dbus_call (obj, atspi_interface_accessible,
498                       "GetIndexInParent", NULL, "=>i", &ret);
499     return ret;
500   }
501
502   l = obj->accessible_parent->children;
503   while (l)
504   {
505     if (l->data == obj) return i;
506     l = g_list_next (l);
507     i++;
508   }
509   return -1;
510 }
511
512 typedef struct
513 {
514   dbus_uint32_t type;
515   GArray *targets;
516 } Accessibility_Relation;
517
518 /**
519  * atspi_accessible_get_relation_set:
520  * @obj: a pointer to the #AtspiAccessible object on which to operate.
521  *
522  * Gets the set of #AtspiRelation objects which describes this #AtspiAccessible object's
523  * relationships with other #AtspiAccessible objects.
524  *
525  * Returns: (element-type AtspiAccessible*) (transfer full): a #GArray of
526  *          #AtspiRelation pointers or NULL on exception.
527  **/
528 GArray *
529 atspi_accessible_get_relation_set (AtspiAccessible *obj, GError **error)
530 {
531   DBusMessage *reply;
532   DBusMessageIter iter, iter_array;
533   GArray *ret;
534
535   g_return_val_if_fail (obj != NULL, NULL);
536
537   reply = _atspi_dbus_call_partial (obj, atspi_interface_accessible, "GetRelationSet", error, "");
538   if (!reply)
539     return NULL;
540   _ATSPI_DBUS_CHECK_SIG (reply, "a(ua(so))", error, NULL);
541
542   ret = g_array_new (TRUE, TRUE, sizeof (AtspiRelation *));
543   dbus_message_iter_init (reply, &iter);
544   dbus_message_iter_recurse (&iter, &iter_array);
545   while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
546   {
547     AtspiRelation *relation;
548     relation = _atspi_relation_new_from_iter (&iter_array);
549     ret = g_array_append_val (ret, relation);
550     dbus_message_iter_next (&iter_array);
551   }
552   dbus_message_unref (reply);
553   return ret;
554 }
555
556 /**
557  * atspi_accessible_get_role:
558  * @obj: a pointer to the #AtspiAccessible object on which to operate.
559  *
560  * Gets the UI role played by an #AtspiAccessible object.
561  * This role's name can be obtained via atspi_accessible_get_role_name ().
562  *
563  * Returns: the #AtspiRole of an #AtspiAccessible object.
564  *
565  **/
566 AtspiRole
567 atspi_accessible_get_role (AtspiAccessible *obj, GError **error)
568 {
569   g_return_val_if_fail (obj != NULL, ATSPI_ROLE_INVALID);
570
571   if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_ROLE))
572   {
573     dbus_uint32_t role;
574     /* TODO: Make this a property */
575     if (_atspi_dbus_call (obj, atspi_interface_accessible, "GetRole", error, "=>u", &role))
576     {
577       obj->role = role;
578     _atspi_accessible_add_cache (obj, ATSPI_CACHE_ROLE);
579     }
580   }
581   return obj->role;
582 }
583
584 /**
585  * atspi_accessible_get_role_name:
586  * @obj: a pointer to the #AtspiAccessible object on which to operate.
587  *
588  * Gets a UTF-8 string corresponding to the name of the role played by an object.
589  * This method will return useful values for roles that fall outside the
590  * enumeration used in atspi_accessible_get_role ().
591  *
592  * Returns: a UTF-8 string specifying the type of UI role played by an
593  * #AtspiAccessible object.
594  *
595  **/
596 gchar *
597 atspi_accessible_get_role_name (AtspiAccessible *obj, GError **error)
598 {
599   char *retval = NULL;
600   AtspiRole role;
601
602   g_return_val_if_fail (obj != NULL, NULL);
603
604   role = atspi_accessible_get_role (obj, error);
605   if (role >= 0 && role < MAX_ROLES && role != ATSPI_ROLE_EXTENDED)
606     return g_strdup (role_names [role]);
607
608   _atspi_dbus_call (obj, atspi_interface_accessible, "GetRoleName", error, "=>s", &retval);
609
610   if (!retval)
611     retval = g_strdup ("");
612
613   return retval;
614 }
615
616 /**
617  * atspi_accessible_get_localized_role_name:
618  * @obj: a pointer to the #AtspiAccessible object on which to operate.
619  *
620  * Gets a UTF-8 string corresponding to the name of the role played by an 
621  * object, translated to the current locale.
622  * This method will return useful values for roles that fall outside the
623  * enumeration used in atspi_accessible_getRole ().
624  *
625  * Returns: a localized, UTF-8 string specifying the type of UI role played 
626  * by an #AtspiAccessible object.
627  *
628  **/
629 gchar *
630 atspi_accessible_get_localized_role_name (AtspiAccessible *obj, GError **error)
631 {
632   char *retval = NULL;
633
634   g_return_val_if_fail (obj != NULL, NULL);
635
636   _atspi_dbus_call (obj, atspi_interface_accessible, "GetLocalizedRoleName", error, "=>s", &retval);
637
638   if (!retval)
639     return g_strdup ("");
640
641   return retval;
642 }
643
644 static AtspiStateSet *
645 defunct_set ()
646 {
647   AtspiStateSet *set = atspi_state_set_new (NULL);
648   atspi_state_set_add (set, ATSPI_STATE_DEFUNCT);
649   return set;
650 }
651
652 /**
653  * atspi_accessible_get_state_set:
654  * @obj: a pointer to the #AtspiAccessible object on which to operate.
655  *
656  * Gets the states currently held by an object.
657  *
658  * Returns: (transfer full): a pointer to an #AtspiStateSet representing an
659  * object's current state set.
660  **/
661 AtspiStateSet *
662 atspi_accessible_get_state_set (AtspiAccessible *obj)
663 {
664   if (!obj->parent.app || !obj->parent.app->bus)
665     return defunct_set ();
666
667
668   if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_STATES))
669   {
670     DBusMessage *reply;
671     DBusMessageIter iter;
672     reply = _atspi_dbus_call_partial (obj, atspi_interface_accessible,
673                                       "GetState", NULL, "");
674     _ATSPI_DBUS_CHECK_SIG (reply, "au", NULL, defunct_set ());
675     dbus_message_iter_init (reply, &iter);
676     _atspi_dbus_set_state (obj, &iter);
677     dbus_message_unref (reply);
678     _atspi_accessible_add_cache (obj, ATSPI_CACHE_STATES);
679   }
680
681   return g_object_ref (obj->states);
682 }
683
684 /**
685  * atspi_accessible_get_attributes:
686  * @obj: The #AtspiAccessible being queried.
687  *
688  * Gets the #AttributeSet representing any assigned 
689  * name-value pair attributes or annotations for this object.
690  * For typographic, textual, or textually-semantic attributes, see
691  * atspi_text_get_attributes instead.
692  *
693  * Returns: (element-type gchar* gchar*) (transfer full): The name-value-pair
694  * attributes assigned to this object.
695  */
696 GHashTable *
697 atspi_accessible_get_attributes (AtspiAccessible *obj, GError **error)
698 {
699   DBusMessage *message;
700
701     g_return_val_if_fail (obj != NULL, NULL);
702
703   if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_ATTRIBUTES))
704   {
705     message = _atspi_dbus_call_partial (obj, atspi_interface_accessible,
706                                         "GetAttributes", error, "");
707     obj->attributes = _atspi_dbus_return_hash_from_message (message);
708     _atspi_accessible_add_cache (obj, ATSPI_CACHE_ATTRIBUTES);
709   }
710
711   if (!obj->attributes)
712     return NULL;
713   return g_hash_table_ref (obj->attributes);
714 }
715
716 static void
717 add_to_attribute_array (gpointer key, gpointer value, gpointer data)
718 {
719   GArray **array = (GArray **)data;
720   gchar *str = g_strconcat (key, ":", value, NULL);
721   *array = g_array_append_val (*array, str);
722 }
723
724 /**
725  * atspi_accessible_get_attributes_as_array:
726  * @obj: The #AtspiAccessible being queried.
727  *
728  * Gets a #GArray representing any assigned 
729  * name-value pair attributes or annotations for this object.
730  * For typographic, textual, or textually-semantic attributes, see
731  * atspi_text_get_attributes_as_array instead.
732  *
733  * Returns: (element-type gchar*) (transfer full): The name-value-pair
734  *          attributes assigned to this object.
735  */
736 GArray *
737 atspi_accessible_get_attributes_as_array (AtspiAccessible *obj, GError **error)
738 {
739   DBusMessage *message;
740
741     g_return_val_if_fail (obj != NULL, NULL);
742
743   if (_atspi_accessible_get_cache_mask (obj) & ATSPI_CACHE_ATTRIBUTES)
744   {
745     GArray *array = g_array_new (TRUE, TRUE, sizeof (gchar *));
746     GHashTable *attributes = atspi_accessible_get_attributes (obj, error);
747     if (!attributes)
748       return NULL;
749     g_hash_table_foreach (attributes, add_to_attribute_array, &array);
750     g_hash_table_unref (attributes);
751     return array;
752   }
753
754   message = _atspi_dbus_call_partial (obj, atspi_interface_accessible, "GetAttributes", error, "");
755   return _atspi_dbus_return_attribute_array_from_message (message);
756 }
757
758 /**
759  * atspi_accessible_get_application:
760  * @obj: The #AtspiAccessible being queried.
761  *
762  * Gets the containing #AtspiApplication for an object.
763  *
764  * Returns: (transfer full): the containing #AtspiApplication instance for
765  *          this object.
766  */
767 AtspiAccessible *
768 atspi_accessible_get_application (AtspiAccessible *obj, GError **error)
769 {
770   AtspiAccessible *parent;
771
772   g_object_ref (obj);
773   for (;;)
774   {
775     parent = atspi_accessible_get_parent (obj, NULL);
776     if (!parent && obj->parent.app &&
777         atspi_accessible_get_role (obj, NULL) != ATSPI_ROLE_APPLICATION)
778     {
779       AtspiAccessible *root = g_object_ref (obj->parent.app->root);
780       if (root)
781       {
782         g_object_unref (obj);
783         if (atspi_accessible_get_role (root, NULL) == ATSPI_ROLE_DESKTOP_FRAME)
784         {
785           g_object_unref (root);
786           return NULL;
787         }
788         return root;
789       }
790     }
791     if (!parent || parent == obj ||
792         atspi_accessible_get_role (parent, NULL) == ATSPI_ROLE_DESKTOP_FRAME)
793     return obj;
794     g_object_unref (obj);
795     obj = parent;
796   }
797 }
798
799 /* Application-specific methods */
800
801 /**
802  * atspi_accessible_get_toolkit_name:
803  * @obj: a pointer to the #AtspiAccessible object on which to operate.
804  *
805  * Gets the toolkit name for an #AtspiAccessible object.
806  * Only works on application root objects.
807  *
808  * Returns: a UTF-8 string indicating the toolkit name for the #AtspiAccessible object or NULL on exception.
809  **/
810 gchar *
811 atspi_accessible_get_toolkit_name (AtspiAccessible *obj, GError **error)
812 {
813   g_return_val_if_fail (obj != NULL, NULL);
814
815   if (!obj->parent.app)
816     return NULL;
817
818   if (!obj->parent.app->toolkit_name)
819     _atspi_dbus_get_property (obj, atspi_interface_application, "ToolkitName",
820                               error, "s", &obj->parent.app->toolkit_name);
821
822   return g_strdup (obj->parent.app->toolkit_name);
823 }
824
825 /**
826  * atspi_accessible_get_toolkit_version:
827  * @obj: a pointer to the #AtspiAccessible object on which to operate.
828  *
829  * Gets the toolkit version for an #AtspiAccessible object.
830  * Only works on application root objects.
831  *
832  * Returns: a UTF-8 string indicating the toolkit version for the #AtspiAccessible object or NULL on exception.
833  **/
834 gchar *
835 atspi_accessible_get_toolkit_version (AtspiAccessible *obj, GError **error)
836 {
837   g_return_val_if_fail (obj != NULL, NULL);
838
839   if (!obj->parent.app)
840     return NULL;
841
842   if (!obj->parent.app->toolkit_version)
843     _atspi_dbus_get_property (obj, atspi_interface_application, "Version",
844                               error, "s", &obj->parent.app->toolkit_version);
845
846   return g_strdup (obj->parent.app->toolkit_version);
847 }
848
849 /**
850  * atspi_accessible_get_atspi_version:
851  * @obj: a pointer to the #AtspiAccessible object on which to operate.
852  *
853  * Gets the AT-SPI IPC specification version supported by the application
854  * pointed to by the #AtspiAccessible object.
855  * Only works on application root objects.
856  *
857  * Returns: a UTF-8 string indicating the AT-SPI version for the #AtspiAccessible object or NULL on exception.
858  **/
859 gchar *
860 atspi_accessible_get_atspi_version (AtspiAccessible *obj, GError **error)
861 {
862   g_return_val_if_fail (obj != NULL, NULL);
863
864   if (!obj->parent.app)
865     return NULL;
866
867   if (!obj->parent.app->atspi_version)
868     _atspi_dbus_get_property (obj, atspi_interface_application, "AtspiVersion",
869                               error, "s", &obj->parent.app->atspi_version);
870
871   return g_strdup (obj->parent.app->atspi_version);
872 }
873
874 /**
875  * atspi_accessible_get_toolkit_version:
876  * @obj: a pointer to the #AtspiAccessible object on which to operate.
877  *
878  * Gets the application id for a #AtspiAccessible object.
879  * Only works on application root objects.
880  *
881  * Returns: a positive #gint indicating the id for the #AtspiAccessible object 
882  * or -1 on exception.
883  **/
884 gint
885 atspi_accessible_get_id (AtspiAccessible *obj, GError **error)
886 {
887   gint ret = -1;
888
889   g_return_val_if_fail (obj != NULL, -1);
890
891   if (!_atspi_dbus_get_property (obj, atspi_interface_application, "Id", error, "i", &ret))
892       return -1;
893   return ret;
894 }
895
896
897 /* Interface query methods */
898
899 static gboolean
900 _atspi_accessible_is_a (AtspiAccessible *accessible,
901                       const char *interface_name)
902 {
903   int n;
904
905   if (accessible == NULL)
906     {
907       return FALSE;
908     }
909
910   if (!_atspi_accessible_test_cache (accessible, ATSPI_CACHE_INTERFACES))
911   {
912     DBusMessage *reply;
913     DBusMessageIter iter;
914     reply = _atspi_dbus_call_partial (accessible, atspi_interface_accessible,
915                                       "GetInterfaces", NULL, "");
916     _ATSPI_DBUS_CHECK_SIG (reply, "as", NULL, FALSE);
917     dbus_message_iter_init (reply, &iter);
918     _atspi_dbus_set_interfaces (accessible, &iter);
919     dbus_message_unref (reply);
920     _atspi_accessible_add_cache (accessible, ATSPI_CACHE_INTERFACES);
921   }
922
923   n = _atspi_get_iface_num (interface_name);
924   if (n == -1) return FALSE;
925   return (gboolean) ((accessible->interfaces & (1 << n))? TRUE: FALSE);
926 }
927
928 /**
929  * atspi_accessible_is_action:
930  * @obj: a pointer to the #AtspiAccessible instance to query.
931  *
932  * Query whether the specified #AtspiAccessible implements the 
933  * #AtspiAction interface.
934  *
935  * Returns: #TRUE if @obj implements the #AtspiAction interface,
936  *          #FALSE otherwise.
937  **/
938 gboolean
939 atspi_accessible_is_action (AtspiAccessible *obj)
940 {
941   return _atspi_accessible_is_a (obj,
942                               atspi_interface_action);
943 }
944
945 /**
946  * atspi_accessible_is_application:
947  * @obj: a pointer to the #AtspiAccessible instance to query.
948  *
949  * Query whether the specified #AtspiAccessible implements the
950  * #AtspiApplication interface.
951  *
952  * Returns: #TRUE if @obj implements the #AtspiApplication interface,
953  *          #FALSE otherwise.
954  **/
955 gboolean
956 atspi_accessible_is_application (AtspiAccessible *obj)
957 {
958   return _atspi_accessible_is_a (obj,
959                               atspi_interface_application);
960 }
961
962 /**                      
963  * atspi_accessible_is_collection:
964  * @obj: a pointer to the #AtspiAccessible instance to query.
965  *
966  * Query whether the specified #AtspiAccessible implements the
967  * #AtspiCollection interface.
968  *
969  * Returns: #TRUE if @obj implements the #AtspiCollection interface,
970  *          #FALSE otherwise.
971  **/
972 gboolean
973 atspi_accessible_is_collection (AtspiAccessible *obj)
974 {
975      return _atspi_accessible_is_a (obj,
976                               atspi_interface_collection);
977 }
978
979 /**
980  * atspi_accessible_is_component:
981  * @obj: a pointer to the #AtspiAccessible instance to query.
982  *
983  * Query whether the specified #AtspiAccessible implements #AtspiComponent.
984  *
985  * Returns: #TRUE if @obj implements the #AtspiComponent interface,
986  *          #FALSE otherwise.
987  **/
988 gboolean
989 atspi_accessible_is_component (AtspiAccessible *obj)
990 {
991   return _atspi_accessible_is_a (obj,
992                               atspi_interface_component);
993 }
994
995 /**
996  * atspi_accessible_is_document:
997  * @obj: a pointer to the #AtspiAccessible instance to query.
998  *
999  * Query whether the specified #AtspiAccessible implements the
1000  * #AtspiDocument interface.
1001  *
1002  * Returns: #TRUE if @obj implements the #AtspiDocument interface,
1003  *          #FALSE otherwise.
1004  **/
1005 gboolean
1006 atspi_accessible_is_document (AtspiAccessible *obj)
1007 {
1008   return _atspi_accessible_is_a (obj,
1009                               atspi_interface_document);
1010 }
1011
1012 /**
1013  * atspi_accessible_is_editable_text:
1014  * @obj: a pointer to the #AtspiAccessible instance to query.
1015  *
1016  * Query whether the specified #AtspiAccessible implements the
1017  * #AtspiEditableText interface.
1018  *
1019  * Returns: #TRUE if @obj implements the #AtspiEditableText interface,
1020  *          #FALSE otherwise.
1021  **/
1022 gboolean
1023 atspi_accessible_is_editable_text (AtspiAccessible *obj)
1024 {
1025   return _atspi_accessible_is_a (obj,
1026                               atspi_interface_editable_text);
1027 }
1028                                                                                                                                                                         
1029 /**
1030  * atspi_accessible_is_hypertext:
1031  * @obj: a pointer to the #AtspiAccessible instance to query.
1032  *
1033  * Query whether the specified #AtspiAccessible implements the
1034  * #AtspiHypertext interface.
1035  *
1036  * Returns: #TRUE if @obj implements the #AtspiHypertext interface,
1037  *          #FALSE otherwise.
1038  **/
1039 gboolean
1040 atspi_accessible_is_hypertext (AtspiAccessible *obj)
1041 {
1042   return _atspi_accessible_is_a (obj,
1043                               atspi_interface_hypertext);
1044 }
1045
1046 /**
1047  * atspi_accessible_is_hyperlink:
1048  * @obj: a pointer to the #AtspiAccessible instance to query.
1049  *
1050  * Query whether the specified #AtspiAccessible implements the 
1051  * #AtspiHyperlink interface.
1052  *
1053  * Returns: #TRUE if @obj implements the #AtspiHypertext interface,
1054  *          #FALSE otherwise.
1055  **/
1056 gboolean
1057 atspi_accessible_is_hyperlink (AtspiAccessible *obj)
1058 {
1059   return _atspi_accessible_is_a (obj,
1060                               atspi_interface_hyperlink);
1061 }
1062
1063 /**
1064  * atspi_accessible_is_image:
1065  * @obj: a pointer to the #AtspiAccessible instance to query.
1066  *
1067  * Query whether the specified #AtspiAccessible implements the
1068  * #AtspiImage interface.
1069  *
1070  * Returns: #TRUE if @obj implements the #AtspiImage interface,
1071  *          #FALSE otherwise.
1072 **/
1073 gboolean
1074 atspi_accessible_is_image (AtspiAccessible *obj)
1075 {
1076   return _atspi_accessible_is_a (obj,
1077                               atspi_interface_image);
1078 }
1079
1080 /**
1081  * atspi_accessible_is_selection:
1082  * @obj: a pointer to the #AtspiAccessible instance to query.
1083  *
1084  * Query whether the specified #AtspiAccessible implements the
1085  * #AtspiSelection interface.
1086  *
1087  * Returns: #TRUE if @obj implements the #AtspiSelection interface,
1088  *          #FALSE otherwise.
1089 **/
1090 gboolean
1091 atspi_accessible_is_selection (AtspiAccessible *obj)
1092 {
1093   return _atspi_accessible_is_a (obj,
1094                               atspi_interface_selection);
1095 }
1096
1097 /**
1098  * atspi_accessible_is_table:
1099  * @obj: a pointer to the #AtspiAccessible instance to query.
1100  *
1101  * Query whether the specified #AtspiAccessible implements the
1102  * #AtspiTable interface.
1103  *
1104  * Returns: #TRUE if @obj implements the #AtspiTable interface,
1105  *          #FALSE otherwise.
1106 **/
1107 gboolean
1108 atspi_accessible_is_table (AtspiAccessible *obj)
1109 {
1110   return _atspi_accessible_is_a (obj,
1111                               atspi_interface_table);
1112 }
1113
1114 /**
1115  * atspi_accessible_is_streamable_content:
1116  * @obj: a pointer to the #AtspiAccessible instance to query.
1117  *
1118  * Query whether the specified #AtspiAccessible implements the
1119  * #AtspiStreamableContent interface.
1120  *
1121  * Returns: #TRUE if @obj implements the #AtspiStreamableContent interface,
1122  *          #FALSE otherwise.
1123 **/
1124 gboolean
1125 atspi_accessible_is_streamable_content (AtspiAccessible *obj)
1126 {
1127 #if 0
1128   return _atspi_accessible_is_a (obj,
1129                               atspi_interface_streamable_content);
1130 #else
1131   g_warning ("Streamable content not implemented");
1132   return FALSE;
1133 #endif
1134 }
1135
1136 /**
1137  * atspi_accessible_is_text:
1138  * @obj: a pointer to the #AtspiAccessible instance to query.
1139  *
1140  * Query whether the specified #AtspiAccessible implements the 
1141  * #AtspiText interface.
1142  *
1143  * Returns: #TRUE if @obj implements the #AtspiText interface,
1144  *          #FALSE otherwise.
1145 **/
1146 gboolean
1147 atspi_accessible_is_text (AtspiAccessible *obj)
1148 {
1149   return _atspi_accessible_is_a (obj,
1150                               atspi_interface_text);
1151 }
1152
1153 /**
1154  * atspi_accessible_is_value:
1155  * @obj: a pointer to the #AtspiAccessible instance to query.
1156  *
1157  * Query whether the specified #AtspiAccessible implements the
1158  * #AtspiValue interface.
1159  *
1160  * Returns: #TRUE if @obj implements the #AtspiValue interface,
1161  *          #FALSE otherwise.
1162 **/
1163 gboolean
1164 atspi_accessible_is_value (AtspiAccessible *obj)
1165 {
1166   return _atspi_accessible_is_a (obj,
1167                               atspi_interface_value);
1168 }
1169
1170 /**
1171  * atspi_accessible_get_action:
1172  * @obj: a pointer to the #AtspiAccessible instance to query.
1173  *
1174  * Gets the #AtspiAction interface for an #AtspiAccessible.
1175  *
1176  * Returns: (transfer full): a pointer to an #AtspiAction interface
1177  *          instance, or NULL if @obj does not implement #AtspiAction.
1178  **/
1179 AtspiAction *
1180 atspi_accessible_get_action (AtspiAccessible *accessible)
1181 {
1182   return (_atspi_accessible_is_a (accessible, atspi_interface_action) ?
1183           g_object_ref (ATSPI_ACTION (accessible)) : NULL);  
1184 }
1185
1186 /**
1187  * atspi_accessible_get_collection:
1188  * @obj: a pointer to the #AtspiAccessible instance to query.
1189  *
1190  * Gets the #AtspiCollection interface for an #AtspiAccessible.
1191  *
1192  * Returns: (transfer full): a pointer to an #AtspiCollection interface
1193  *          instance, or NULL if @obj does not implement #AtspiCollection.
1194  **/
1195 AtspiCollection *
1196 atspi_accessible_get_collection (AtspiAccessible *accessible)
1197 {
1198   return (_atspi_accessible_is_a (accessible, atspi_interface_collection) ?
1199           g_object_ref (ATSPI_COLLECTION (accessible)) : NULL);  
1200 }
1201
1202 /**
1203  * atspi_accessible_get_component:
1204  * @obj: a pointer to the #AtspiAccessible instance to query.
1205  *
1206  * Gets the #AtspiComponent interface for an #AtspiAccessible.
1207  *
1208  * Returns: (transfer full): a pointer to an #AtspiComponent interface
1209  *          instance, or NULL if @obj does not implement #AtspiComponent.
1210  **/
1211 AtspiComponent *
1212 atspi_accessible_get_component (AtspiAccessible *obj)
1213 {
1214   return (_atspi_accessible_is_a (obj, atspi_interface_component) ?
1215           g_object_ref (ATSPI_COMPONENT (obj)) : NULL);
1216 }
1217
1218 /**
1219  * atspi_accessible_get_document:
1220  * @obj: a pointer to the #AtspiAccessible instance to query.
1221  *
1222  * Gets the #AtspiDocument interface for an #AtspiAccessible.
1223  *
1224  * Returns: (transfer full): a pointer to an #AtspiDocument interface
1225  *          instance, or NULL if @obj does not implement #AtspiDocument.
1226  **/
1227 AtspiDocument *
1228 atspi_accessible_get_document (AtspiAccessible *accessible)
1229 {
1230   return (_atspi_accessible_is_a (accessible, atspi_interface_document) ?
1231           g_object_ref (ATSPI_DOCUMENT (accessible)) : NULL);  
1232 }
1233
1234 /**
1235  * atspi_accessible_get_editable_text:
1236  * @obj: a pointer to the #AtspiAccessible instance to query.
1237  *
1238  * Gets the #AtspiEditableText interface for an #AtspiAccessible.
1239  *
1240  * Returns: (transfer full): a pointer to an #AtspiEditableText interface
1241  *          instance, or NULL if @obj does not implement #AtspiEditableText.
1242  **/
1243 AtspiEditableText *
1244 atspi_accessible_get_editable_text (AtspiAccessible *accessible)
1245 {
1246   return (_atspi_accessible_is_a (accessible, atspi_interface_editable_text) ?
1247           g_object_ref (ATSPI_EDITABLE_TEXT (accessible)) : NULL);  
1248 }
1249
1250 /**
1251  * atspi_accessible_get_hyperlink:
1252  * @obj: a pointer to the #AtspiAccessible object on which to operate.
1253  *
1254  * Gets the #AtspiHyperlink interface for an #AtspiAccessible.
1255  *
1256  * Returns: (transfer full): the #AtspiHyperlink object associated with
1257  *          the given #AtspiAccessible, or NULL if not supported.
1258  **/
1259 AtspiHyperlink *
1260 atspi_accessible_get_hyperlink (AtspiAccessible *accessible)
1261 {
1262   return (_atspi_accessible_is_a (accessible, atspi_interface_hyperlink) ?
1263           _atspi_hyperlink_new (accessible->parent.app, accessible->parent.path) : NULL);
1264 }
1265
1266 /**
1267  * atspi_accessible_get_hypertext:
1268  * @obj: a pointer to the #AtspiAccessible instance to query.
1269  *
1270  * Gets the #AtspiHypertext interface for an #AtspiAccessible.
1271  *
1272  * Returns: (transfer full): a pointer to an #AtspiHypertext interface
1273  *          instance, or NULL if @obj does not implement #AtspiHypertext.
1274  **/
1275 AtspiHypertext *
1276 atspi_accessible_get_hypertext (AtspiAccessible *accessible)
1277 {
1278   return (_atspi_accessible_is_a (accessible, atspi_interface_hypertext) ?
1279           g_object_ref (ATSPI_HYPERTEXT (accessible)) : NULL);  
1280 }
1281
1282 /**
1283  * atspi_accessible_get_image:
1284  * @obj: a pointer to the #AtspiAccessible instance to query.
1285  *
1286  * Gets the #AtspiImage interface for an #AtspiAccessible.
1287  *
1288  * Returns: (transfer full): a pointer to an #AtspiImage interface instance, or
1289  *          NULL if @obj does not implement #AtspiImage.
1290  **/
1291 AtspiImage *
1292 atspi_accessible_get_image (AtspiAccessible *accessible)
1293 {
1294   return (_atspi_accessible_is_a (accessible, atspi_interface_image) ?
1295           g_object_ref (ATSPI_IMAGE (accessible)) : NULL);  
1296 }
1297
1298 /**
1299  * atspi_accessible_get_selection:
1300  * @obj: a pointer to the #AtspiAccessible instance to query.
1301  *
1302  * Gets the #AtspiSelection interface for an #AtspiAccessible.
1303  *
1304  * Returns: (transfer full): a pointer to an #AtspiSelection interface
1305  *          instance, or NULL if @obj does not implement #AtspiSelection.
1306  **/
1307 AtspiSelection *
1308 atspi_accessible_get_selection (AtspiAccessible *accessible)
1309 {
1310   return (_atspi_accessible_is_a (accessible, atspi_interface_selection) ?
1311           g_object_ref (ATSPI_SELECTION (accessible)) : NULL);  
1312 }
1313
1314 #if 0
1315 /**
1316  * atspi_accessible_get_streamable_content:
1317  * @obj: a pointer to the #AtspiAccessible instance to query.
1318  *
1319  * Gets the #AtspiStreamableContent interface for an #AtspiAccessible.
1320  *
1321  * Returns: (transfer full): a pointer to an #AtspiStreamableContent interface
1322  *          instance, or NULL if @obj does not implement #AtspiStreamableContent.
1323  **/
1324 AtspiStreamableContent *
1325 atspi_accessible_get_streamable_content (AtspiAccessible *accessible)
1326 {
1327   return (_atspi_accessible_is_a (accessible, atspi_interface_streamable_content) ?
1328           accessible : NULL);  
1329 }
1330 #endif
1331
1332 /**
1333  * atspi_accessible_get_table:
1334  * @obj: a pointer to the #AtspiAccessible instance to query.
1335  *
1336  * Gets the #AtspiTable interface for an #AtspiAccessible.
1337  *
1338  * Returns: (transfer full): a pointer to an #AtspiTable interface instance, or
1339  *          NULL if @obj does not implement #AtspiTable.
1340  **/
1341 AtspiTable *
1342 atspi_accessible_get_table (AtspiAccessible *obj)
1343 {
1344   return (_atspi_accessible_is_a (obj, atspi_interface_table) ?
1345           g_object_ref (ATSPI_TABLE (obj)) : NULL);  
1346 }
1347
1348 /**
1349  * atspi_accessible_get_text:
1350  * @obj: a pointer to the #AtspiAccessible instance to query.
1351  *
1352  * Gets the #AtspiTable interface for an #AtspiAccessible.
1353  *
1354  * Returns: (transfer full): a pointer to an #AtspiText interface instance, or
1355  *          NULL if @obj does not implement #AtspiText.
1356  **/
1357 AtspiText *
1358 atspi_accessible_get_text (AtspiAccessible *obj)
1359 {
1360   return (_atspi_accessible_is_a (obj, atspi_interface_text) ?
1361           g_object_ref (ATSPI_TEXT (obj)) : NULL);
1362 }
1363
1364 /**
1365  * atspi_accessible_get_value:
1366  * @obj: a pointer to the #AtspiAccessible instance to query.
1367  *
1368  * Gets the #AtspiTable interface for an #AtspiAccessible.
1369  *
1370  * Returns: (transfer full): a pointer to an #AtspiValue interface instance, or
1371  *          NULL if @obj does not implement #AtspiValue.
1372  **/
1373 AtspiValue *
1374 atspi_accessible_get_value (AtspiAccessible *accessible)
1375 {
1376   return (_atspi_accessible_is_a (accessible, atspi_interface_value) ?
1377           g_object_ref (ATSPI_VALUE (accessible)) : NULL);  
1378 }
1379
1380 static void
1381 append_const_val (GArray *array, const gchar *val)
1382 {
1383   gchar *dup = g_strdup (val);
1384
1385   if (dup)
1386     g_array_append_val (array, dup);
1387 }
1388
1389 /**
1390  * atspi_accessible_get_interfaces:
1391  * @obj: The #AtspiAccessible to query.
1392  *
1393  * A set of pointers to all interfaces supported by an #AtspiAccessible.
1394  *
1395  * Returns: (element-type gchar*) (transfer full): A #GArray of strings
1396  *          describing the interfaces supported by the object.  Interfaces are
1397  *          denoted in short-hand (i.e. "Component", "Text" etc.).
1398  **/
1399 GArray *
1400 atspi_accessible_get_interfaces (AtspiAccessible *obj)
1401 {
1402   GArray *ret = g_array_new (TRUE, TRUE, sizeof (gchar *));
1403
1404   g_return_val_if_fail (obj != NULL, NULL);
1405
1406   append_const_val (ret, "Accessible");
1407   if (atspi_accessible_is_action (obj))
1408     append_const_val (ret, "Action");
1409   if (atspi_accessible_is_collection (obj))
1410     append_const_val (ret, "Collection");
1411   if (atspi_accessible_is_component (obj))
1412     append_const_val (ret, "Component");
1413   if (atspi_accessible_is_document (obj))
1414     append_const_val (ret, "Document");
1415   if (atspi_accessible_is_editable_text (obj))
1416     append_const_val (ret, "EditableText");
1417   if (atspi_accessible_is_hypertext (obj))
1418     append_const_val (ret, "Hypertext");
1419   if (atspi_accessible_is_hyperlink (obj))
1420     append_const_val (ret, "Hyperlink");
1421   if (atspi_accessible_is_image (obj))
1422     append_const_val (ret, "Image");
1423   if (atspi_accessible_is_selection (obj))
1424     append_const_val (ret, "Selection");
1425   if (atspi_accessible_is_table (obj))
1426     append_const_val (ret, "Table");
1427   if (atspi_accessible_is_text (obj))
1428     append_const_val (ret, "Text");
1429   if (atspi_accessible_is_value (obj))
1430     append_const_val (ret, "Value");
1431
1432   return ret;
1433 }
1434
1435 AtspiAccessible * 
1436 _atspi_accessible_new (AtspiApplication *app, const gchar *path)
1437 {
1438   AtspiAccessible *accessible;
1439   
1440   accessible = g_object_new (ATSPI_TYPE_ACCESSIBLE, NULL);
1441   g_return_val_if_fail (accessible != NULL, NULL);
1442
1443   accessible->parent.app = g_object_ref (app);
1444   accessible->parent.path = g_strdup (path);
1445
1446   return accessible;
1447 }
1448
1449 /**
1450  * atspi_accessible_set_cache_mask:
1451  *
1452  * @accessible: The #AtspiAccessible to operate on.  Must be the desktop or
1453  *             the root of an application.
1454  * @mask: (type int): An #AtspiCache specifying a bit mask of the types of data to cache.
1455  *
1456  * Sets the type of data to cache for accessibles.
1457  * If this is not set for an application or is reset to ATSPI_CACHE_UNDEFINED,
1458  * then the desktop's cache flag will be used.
1459  * If the desktop's cache flag is also undefined, then all possible data will
1460  * be cached.
1461  * This function is intended to work around bugs in toolkits where the proper
1462  * events are not raised / to aid in testing for such bugs.
1463  **/
1464 void
1465 atspi_accessible_set_cache_mask (AtspiAccessible *accessible, AtspiCache mask)
1466 {
1467   g_return_if_fail (accessible != NULL);
1468   g_return_if_fail (accessible->parent.app != NULL);
1469   g_return_if_fail (accessible == accessible->parent.app->root);
1470   accessible->parent.app->cache = mask;
1471   enable_caching = TRUE;
1472 }
1473
1474 /**
1475  * atspi_accessible_clear_cache:
1476  * @accessible: The #AtspiAccessible whose cache to clear.
1477  *
1478  * Clears the cached information for the given accessible and all of its
1479  * descendants.
1480  */
1481 void
1482 atspi_accessible_clear_cache (AtspiAccessible *accessible)
1483 {
1484   GList *l;
1485
1486   if (accessible)
1487   {
1488     accessible->cached_properties = ATSPI_CACHE_NONE;
1489     for (l = accessible->children; l; l = l->next)
1490       atspi_accessible_clear_cache (l->data);
1491   }
1492 }
1493
1494 /**
1495  * atspi_accessible_get_process_id:
1496  * @accessible: The #AtspiAccessible to query.
1497  *
1498  * Returns the process id associated with the given accessible.  Mainly
1499  * added for debugging; it is a shortcut to explicitly querying the
1500  * accessible's app->bus_name and then calling GetConnectionUnixProcessID.
1501  *
1502  * Returns: The process ID, or -1 if defunct.
1503  **/
1504 guint
1505 atspi_accessible_get_process_id (AtspiAccessible *accessible, GError **error)
1506 {
1507   DBusMessage *message, *reply;
1508   DBusConnection *bus = _atspi_bus ();
1509   dbus_uint32_t pid = -1;
1510   DBusError d_error;
1511
1512   if (!accessible->parent.app || !accessible->parent.app->bus_name)
1513     return -1;
1514
1515   message = dbus_message_new_method_call ("org.freedesktop.DBus",
1516                                           "/org/freedesktop/DBus",
1517                                           "org.freedesktop.DBus",
1518                                           "GetConnectionUnixProcessID");
1519   dbus_message_append_args (message, DBUS_TYPE_STRING,
1520                             &accessible->parent.app->bus_name,
1521                             DBUS_TYPE_INVALID);
1522   dbus_error_init (&d_error);
1523   reply = dbus_connection_send_with_reply_and_block (bus, message, -1, &d_error);
1524   dbus_message_unref (message);
1525   dbus_message_get_args (reply, NULL, DBUS_TYPE_UINT32, &pid, DBUS_TYPE_INVALID);
1526   dbus_message_unref (reply);
1527   dbus_error_free (&d_error);
1528   return pid;
1529 }
1530
1531 AtspiCache
1532 _atspi_accessible_get_cache_mask (AtspiAccessible *accessible)
1533 {
1534   AtspiCache mask;
1535
1536   if (!accessible->parent.app)
1537     return ATSPI_CACHE_NONE;
1538
1539  mask = accessible->parent.app->cache;
1540   if (mask == ATSPI_CACHE_UNDEFINED &&
1541       accessible->parent.app->root &&
1542       accessible->parent.app->root->accessible_parent)
1543   {
1544     AtspiAccessible *desktop = atspi_get_desktop (0);
1545     mask = desktop->parent.app->cache;
1546     g_object_unref (desktop);
1547   }
1548
1549   if (mask == ATSPI_CACHE_UNDEFINED)
1550     mask = ATSPI_CACHE_DEFAULT;
1551
1552   return mask;
1553 }
1554
1555 gboolean
1556 _atspi_accessible_test_cache (AtspiAccessible *accessible, AtspiCache flag)
1557 {
1558   AtspiCache mask = _atspi_accessible_get_cache_mask (accessible);
1559   AtspiCache result = accessible->cached_properties & mask & flag;
1560   if (accessible->states && atspi_state_set_contains (accessible->states, ATSPI_STATE_TRANSIENT))
1561     return FALSE;
1562   return (result != 0 && (atspi_main_loop || enable_caching) &&
1563           !atspi_no_cache);
1564 }
1565
1566 void
1567 _atspi_accessible_add_cache (AtspiAccessible *accessible, AtspiCache flag)
1568 {
1569   AtspiCache mask = _atspi_accessible_get_cache_mask (accessible);
1570
1571   accessible->cached_properties |= flag & mask;
1572 }