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