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