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