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