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