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