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