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