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