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