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