Fix meson build error
[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 "atspi-accessible-private.h"
27 #include <string.h>
28
29 static gboolean enable_caching = FALSE;
30 static guint quark_locale;
31
32 static void
33 atspi_action_interface_init (AtspiAction *action)
34 {
35 }
36
37 static void
38 atspi_collection_interface_init (AtspiCollection *component)
39 {
40 }
41
42 static void
43 atspi_component_interface_init (AtspiComponent *component)
44 {
45 }
46
47 static void
48 atspi_document_interface_init (AtspiDocument *document)
49 {
50 }
51
52 static void
53 atspi_editable_text_interface_init (AtspiEditableText *editable_text)
54 {
55 }
56
57 static void
58 atspi_hypertext_interface_init (AtspiHypertext *hypertext)
59 {
60 }
61
62 static void
63 atspi_image_interface_init (AtspiImage *image)
64 {
65 }
66
67 static void
68 atspi_selection_interface_init (AtspiSelection *selection)
69 {
70 }
71
72 static void
73 atspi_table_interface_init (AtspiTable *table)
74 {
75 }
76
77 static void
78 atspi_table_cell_interface_init (AtspiTableCell *cell)
79 {
80 }
81
82 static void
83 atspi_text_interface_init (AtspiText *text)
84 {
85 }
86
87 static void
88 atspi_value_interface_init (AtspiValue *value)
89 {
90 }
91
92 G_DEFINE_TYPE_WITH_CODE (AtspiAccessible, atspi_accessible, ATSPI_TYPE_OBJECT,
93                          G_ADD_PRIVATE (AtspiAccessible)
94                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_ACTION, atspi_action_interface_init)
95                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_COLLECTION, atspi_collection_interface_init)
96                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_COMPONENT, atspi_component_interface_init)
97                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_DOCUMENT, atspi_document_interface_init)
98                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_EDITABLE_TEXT, atspi_editable_text_interface_init)
99                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_HYPERTEXT, atspi_hypertext_interface_init)
100                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_IMAGE, atspi_image_interface_init)
101                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_SELECTION, atspi_selection_interface_init)
102                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_TABLE, atspi_table_interface_init)
103                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_TABLE_CELL, atspi_table_cell_interface_init)
104                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_TEXT, atspi_text_interface_init)
105                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_VALUE, atspi_value_interface_init))
106
107 #ifdef DEBUG_REF_COUNTS
108 static gint accessible_count = 0;
109 #endif
110
111 static void
112 atspi_accessible_init (AtspiAccessible *accessible)
113 {
114 #ifdef DEBUG_REF_COUNTS
115   accessible_count++;
116   g_hash_table_insert (_atspi_get_live_refs (), accessible, NULL);
117   g_print("at-spi: init: %d objects\n", accessible_count);
118 #endif
119
120   accessible->priv = atspi_accessible_get_instance_private (accessible);
121
122   accessible->children = g_ptr_array_new_with_free_func (g_object_unref);
123 }
124
125 static void
126 atspi_accessible_dispose (GObject *object)
127 {
128   AtspiAccessible *accessible = ATSPI_ACCESSIBLE (object);
129   AtspiEvent e;
130   AtspiAccessible *parent;
131   gint i;
132
133   /* TODO: Only fire if object not already marked defunct */
134   memset (&e, 0, sizeof (e));
135   e.type = "object:state-changed:defunct";
136   e.source = accessible;
137   e.detail1 = 1;
138   e.detail2 = 0;
139   _atspi_send_event (&e);
140
141   g_clear_object (&accessible->states);
142
143   parent = accessible->accessible_parent;
144   if (parent)
145   {
146     accessible->accessible_parent = NULL;
147     if (parent->children)
148       g_ptr_array_remove (parent->children, accessible);
149     g_object_unref (parent);
150   }
151
152   if (accessible->children) for (i = accessible->children->len - 1; i >= 0; i--)
153   {
154     AtspiAccessible *child = g_ptr_array_index (accessible->children, i);
155     if (child && child->accessible_parent == accessible)
156     {
157       child->accessible_parent = NULL;
158       g_object_unref (accessible);
159     }
160   }
161
162   if (accessible->children)
163   {
164     g_ptr_array_free (accessible->children, TRUE);
165     accessible->children = NULL;
166   }
167
168   G_OBJECT_CLASS (atspi_accessible_parent_class) ->dispose (object);
169 }
170
171 static void
172 atspi_accessible_finalize (GObject *object)
173 {
174   AtspiAccessible *accessible = ATSPI_ACCESSIBLE (object);
175
176   g_free (accessible->description);
177   g_free (accessible->name);
178
179   if (accessible->attributes)
180     g_hash_table_unref (accessible->attributes);
181
182   if (accessible->priv->cache)
183     g_hash_table_destroy (accessible->priv->cache);
184
185 #ifdef DEBUG_REF_COUNTS
186   accessible_count--;
187   g_hash_table_remove (_atspi_get_live_refs (), accessible);
188   g_print ("at-spi: finalize: %d objects\n", accessible_count);
189 #endif
190
191   G_OBJECT_CLASS (atspi_accessible_parent_class)->finalize (object);
192 }
193
194 static void
195 atspi_accessible_class_init (AtspiAccessibleClass *klass)
196 {
197   GObjectClass *object_class = G_OBJECT_CLASS (klass);
198
199   object_class->dispose = atspi_accessible_dispose;
200   object_class->finalize = atspi_accessible_finalize;
201
202   quark_locale = g_quark_from_string ("accessible-locale");
203 }
204
205 /**
206  * atspi_accessible_get_name:
207  * @obj: a pointer to the #AtspiAccessible object on which to operate.
208  *
209  * Gets the name of an #AtspiAccessible object.
210  *
211  * Returns: a UTF-8 string indicating the name of the #AtspiAccessible object 
212  * or NULL on exception.
213  **/
214 gchar *
215 atspi_accessible_get_name (AtspiAccessible *obj, GError **error)
216 {
217   g_return_val_if_fail (obj != NULL, g_strdup (""));
218   if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_NAME))
219   {
220     if (!_atspi_dbus_get_property (obj, atspi_interface_accessible, "Name", error,
221                                    "s", &obj->name))
222       return g_strdup ("");
223     _atspi_accessible_add_cache (obj, ATSPI_CACHE_NAME);
224   }
225   return g_strdup (obj->name);
226 }
227
228
229 /**
230  * atspi_accessible_get_unique_id:
231  * @obj: a pointer to the #AtspiAccessible object on which to operate.
232  *
233  * Gets the identificator, uniquely identifying object, or NULL if an error occured.
234  *
235  * Returns: a UTF-8 string describing the #AtspiAccessible object
236  * or NULL on exception or NULL object passed.
237  **/
238 gchar *
239 atspi_accessible_get_unique_id(AtspiAccessible *obj, GError **error)
240 {
241   if (!obj) {
242     g_set_error(error, ATSPI_ERROR, ATSPI_ERROR_IPC, "argument is null");
243     return NULL;
244   }
245
246   gchar *id = NULL;
247   gchar *bus_name = atspi_accessible_get_bus_name(obj, error);
248   if (bus_name && bus_name[0]) {
249     gchar *path = atspi_accessible_get_path(obj, error);
250     if (path && path[0])
251       id = g_strdup_printf("%s:%s", bus_name, path);
252           else
253       g_set_error(error, ATSPI_ERROR, ATSPI_ERROR_IPC, "failed to get path");
254     g_free(path);
255   }
256   else
257     g_set_error(error, ATSPI_ERROR, ATSPI_ERROR_IPC, "failed to get bus name");
258   g_free(bus_name);
259   return id;
260 }
261
262 /**
263  * atspi_accessible_get_bus_name:
264  * @obj: a pointer to the #AtspiAccessible object on which to operate.
265  *
266  * Gets the bus name, where object belongs.
267  *
268  * Returns: a UTF-8 string describing the #AtspiAccessible object's
269  * bus name or empty string on exception or NULL object passed.
270  **/
271 gchar *
272 atspi_accessible_get_bus_name(AtspiAccessible *obj, GError **error)
273 {
274   if (!obj || !obj->parent.app)
275     return g_strdup("");
276   return g_strdup (obj->parent.app->bus_name);
277 }
278
279 /**
280  * atspi_accessible_get_path:
281  * @obj: a pointer to the #AtspiAccessible object on which to operate.
282  *
283  * Gets the path, uniquely identifying object over its bus name.
284  *
285  * Returns: a UTF-8 string describing the #AtspiAccessible object
286  * or empty string on exception or NULL object passed.
287  **/
288 gchar *
289 atspi_accessible_get_path(AtspiAccessible *obj, GError **error)
290 {
291   static const char *prefix = "/org/a11y/atspi/accessible/";
292   static int prefix_len = 27;
293
294   if (!obj)
295     return g_strdup("");
296   AtspiObject *o = ATSPI_OBJECT (obj);
297   if (!o)
298     return g_strdup("");
299   if (strncmp(o->path, prefix, prefix_len) == 0)
300     return g_strdup(o->path + prefix_len);
301   return g_strdup (o->path);
302 }
303
304 /**
305  * atspi_accessible_get_navigable_at_point:
306  * @root: a pointer to the #AtspiAccessible to start search from.
307  * @x: a #gint specifying the x coordinate of the point in question.
308  * @y: a #gint specifying the y coordinate of the point in question.
309  * @ctype: the coordinate system of the point (@x, @y)
310  *         (e.g. ATSPI_COORD_TYPE_WINDOW, ATSPI_COORD_TYPE_SCREEN).
311  *
312  * Finds the accessible element closest to user (highest in z-order), at a given coordinate within an #AtspiAccessible.
313  * This should be the element, that should be picked, when doing mouse click or finger tap at given coordinates.
314  *
315  * Returns: (nullable) (transfer full): a pointer to an
316  *          #AtspiAccessible descendant (of any depth) of the specified component which
317  *          contains the point (@x, @y), or NULL if no descendant contains
318  *          the point.
319  **/
320 AtspiAccessible *
321 atspi_accessible_get_navigable_at_point (AtspiAccessible *root,
322                                           gint x,
323                                           gint y,
324                                           AtspiCoordType ctype, GError **error)
325 {
326   dbus_int32_t d_x = x, d_y = y;
327   dbus_uint32_t d_ctype = ctype;
328   DBusMessage *reply;
329   AtspiAccessible *return_value = NULL;
330   unsigned char recurse = 0;
331   DBusMessageIter iter;
332   AtspiAccessible *deputy = NULL;
333
334   g_return_val_if_fail (root != NULL, NULL);
335   do {
336     reply = _atspi_dbus_call_partial (root, atspi_interface_accessible, "GetNavigableAtPoint", error, "iiu", d_x, d_y, d_ctype);
337     // call failed, error is set, so we bail out
338     if (!reply) {
339       if (deputy) g_object_unref(deputy);
340       if (return_value) g_object_unref(return_value);
341       return NULL;
342     }
343     _ATSPI_DBUS_CHECK_SIG (reply, "(so)y(so)", NULL, NULL);
344
345     dbus_message_iter_init (reply, &iter);
346     AtspiAccessible *tmp = _atspi_dbus_return_accessible_from_iter (&iter);
347
348     unsigned char value = 0;
349     dbus_message_iter_get_basic (&iter, &value);
350     dbus_message_iter_next (&iter);
351     recurse = (value != 0);
352
353     /* keep deputy if tmp has deputy */
354     if (!deputy)
355       deputy = _atspi_dbus_return_accessible_from_iter (&iter);
356
357     dbus_message_unref(reply);
358
359     if (!tmp) {
360       if (deputy) {
361         /* TODO: need to check deputy works for return value */
362         if (return_value)
363           g_object_unref(return_value);
364         return deputy;
365       }
366       break;
367     }
368
369     if (return_value)
370       g_object_unref(return_value);
371     return_value = root = tmp;
372   } while(recurse);
373   return return_value;
374 }
375
376 /**
377  * atspi_accessible_get_reading_material:
378  * @obj: a pointer to the #AtspiAccessible object on which to operate.
379  *
380  * Gets reading material
381  *
382  * Returns: reading material to be used screen-reader side. This is not stable.
383  * You have to handle all alocated memory as below on screen-reader side.
384  *
385  * AtspiAccessibleReadingMaterial *rm
386  * g_object_unref(rm->parent);
387  * g_object_unref(rm->described_by_accessible);
388  * g_hash_table_unref(rm->attributes);
389  * free(rm->name);
390  * free(rm->labeled_by_name);
391  * free(rm->text_interface_name);
392  * free(rm->localized_role_name);
393  * free(rm->description);
394  * free(rm);
395  **/
396 AtspiAccessibleReadingMaterial *
397 atspi_accessible_get_reading_material (AtspiAccessible *obj, GError **error)
398 {
399   AtspiAccessible *parent;
400   AtspiAccessibleReadingMaterial *reading_material = NULL;
401   const char *name;
402   double current_value;
403   gint count;
404   guint64 val;
405   DBusMessage *reply;
406   DBusMessageIter iter;
407   DBusMessageIter iter_array;
408   dbus_uint32_t role;
409   dbus_uint32_t *states;
410   dbus_int32_t index_in_parent;
411   dbus_int32_t child_count;
412   dbus_bool_t is_selected;
413
414   g_return_val_if_fail (obj != NULL, NULL);
415
416   reply = _atspi_dbus_call_partial (obj, atspi_interface_accessible, "GetReadingMaterial", error, "");
417
418   _ATSPI_DBUS_CHECK_SIG (reply, "a{ss}sssuausiddddsibbii(so)auiui(so)", NULL, NULL);
419
420   reading_material = calloc(1, sizeof(AtspiAccessibleReadingMaterial));
421   if (!reading_material)
422   {
423     return reading_material;
424   }
425
426   dbus_message_iter_init (reply, &iter);
427
428   /* get attributes */
429   reading_material->attributes =  _atspi_dbus_hash_from_iter (&iter);
430   dbus_message_iter_next (&iter);
431
432   /* get name */
433   dbus_message_iter_get_basic (&iter, &name);
434   reading_material->name = g_strdup (name);
435   dbus_message_iter_next (&iter);
436
437   /* get name of relation LABELED_BY */
438   dbus_message_iter_get_basic (&iter, &name);
439   reading_material->labeled_by_name = g_strdup (name);
440   dbus_message_iter_next (&iter);
441
442   /* get name of text interface */
443   dbus_message_iter_get_basic (&iter, &name);
444   reading_material->text_interface_name = g_strdup (name);
445   dbus_message_iter_next (&iter);
446
447   /* get role */
448   dbus_message_iter_get_basic (&iter, &role);
449   reading_material->role = role;
450   dbus_message_iter_next (&iter);
451
452   /* get state set */
453   dbus_message_iter_recurse (&iter, &iter_array);
454   dbus_message_iter_get_fixed_array (&iter_array, &states, &count);
455   val = ((guint64)states [1]) << 32;
456   val += states [0];
457   reading_material->states = val;
458   dbus_message_iter_next (&iter);
459
460   /* get localized role name */
461   dbus_message_iter_get_basic (&iter, &name);
462   reading_material->localized_role_name = g_strdup (name);
463   dbus_message_iter_next (&iter);
464
465   /* get child count */
466   dbus_message_iter_get_basic (&iter, &child_count);
467   reading_material->child_count = child_count;
468   dbus_message_iter_next (&iter);
469
470   /* get current value */
471   dbus_message_iter_get_basic (&iter, &current_value);
472   reading_material->value = current_value;
473   dbus_message_iter_next (&iter);
474
475   /* get minimum increment */
476   dbus_message_iter_get_basic (&iter, &current_value);
477   reading_material->increment = current_value;
478   dbus_message_iter_next (&iter);
479
480   /* get maximum value */
481   dbus_message_iter_get_basic (&iter, &current_value);
482   reading_material->upper = current_value;
483   dbus_message_iter_next (&iter);
484
485   /* get minimum value */
486   dbus_message_iter_get_basic (&iter, &current_value);
487   reading_material->lower = current_value;
488   dbus_message_iter_next (&iter);
489
490   /* get description */
491   dbus_message_iter_get_basic (&iter, &name);
492   reading_material->description = g_strdup (name);
493   dbus_message_iter_next (&iter);
494
495   /* get index in parent */
496   dbus_message_iter_get_basic (&iter, &index_in_parent);
497   reading_material->index_in_parent = index_in_parent;
498   dbus_message_iter_next (&iter);
499
500   /* get selected in parent */
501   dbus_message_iter_get_basic (&iter, &is_selected);
502   reading_material->is_selected_in_parent = is_selected;
503   dbus_message_iter_next (&iter);
504
505   /* get has checkbox child */
506   dbus_message_iter_get_basic (&iter, &is_selected);
507   reading_material->has_checkbox_child = is_selected;
508   dbus_message_iter_next (&iter);
509
510   /* get list children count */
511   dbus_message_iter_get_basic (&iter, &child_count);
512   reading_material->list_children_count = child_count;
513   dbus_message_iter_next (&iter);
514
515   /* get first selected child index */
516   dbus_message_iter_get_basic (&iter, &index_in_parent);
517   reading_material->first_selected_child_index = index_in_parent;
518   dbus_message_iter_next (&iter);
519
520   ////////////////
521   /* get parent */
522   parent =  _atspi_dbus_return_accessible_from_iter (&iter);
523   reading_material->parent = parent;
524
525   /* parent states */
526   dbus_message_iter_recurse (&iter, &iter_array);
527   dbus_message_iter_get_fixed_array (&iter_array, &states, &count);
528   val = ((guint64)states [1]) << 32;
529   val += states [0];
530   reading_material->parent_states = val;
531   dbus_message_iter_next (&iter);
532
533   /* get parent child count */
534   dbus_message_iter_get_basic (&iter, &child_count);
535   reading_material->parent_child_count = child_count;
536   dbus_message_iter_next (&iter);
537
538   /* get parent role */
539   dbus_message_iter_get_basic (&iter, &role);
540   reading_material->parent_role = role;
541   dbus_message_iter_next (&iter);
542
543   /* get parent selected child count */
544   dbus_message_iter_get_basic (&iter, &child_count);
545   reading_material->parent_selected_child_count = child_count;
546   dbus_message_iter_next (&iter);
547
548   ////////////////////////////////////////
549   /* get relation object - DESCRIBED_BY */
550   parent =  _atspi_dbus_return_accessible_from_iter (&iter);
551   reading_material->described_by_accessible = parent;
552
553   return reading_material;
554 }
555
556 /**
557  * atspi_accessible_get_default_label_info:
558  * @obj: a pointer to the #AtspiAccessible object would be window.
559  *
560  * Gets default label information
561  *
562  * Returns: default label information to be used screen-reader side.
563  * This is not stable. And this depends on toolkit side UI definition.
564  * The candidate of default label object could be changed by UI definition.
565  * You have to handle all alocated memory as below on screen-reader side.
566  *
567  * AtspiAccessibleDefaultLabelInfo *dli
568  * g_hash_table_unref(dli->attributes);
569
570  * g_object_unref(dli->obj);
571  * free(dli);
572  **/
573 AtspiAccessibleDefaultLabelInfo *
574 atspi_accessible_get_default_label_info (AtspiAccessible *obj, GError **error)
575 {
576   AtspiAccessibleDefaultLabelInfo *default_label_info = NULL;
577   AtspiAccessible *default_label_object;
578   dbus_uint32_t role;
579   DBusMessage *reply;
580   DBusMessageIter iter;
581
582   g_return_val_if_fail (obj != NULL, NULL);
583
584   reply = _atspi_dbus_call_partial (obj, atspi_interface_accessible, "GetDefaultLabelInfo", error, "");
585
586   _ATSPI_DBUS_CHECK_SIG (reply, "(so)ua{ss}", NULL, NULL);
587
588   default_label_info = calloc(1, sizeof(AtspiAccessibleDefaultLabelInfo));
589   if (!default_label_info)
590   {
591     return default_label_info;
592   }
593
594   dbus_message_iter_init (reply, &iter);
595
596   default_label_object =  _atspi_dbus_return_accessible_from_iter (&iter);
597   default_label_info->obj = default_label_object;
598
599   dbus_message_iter_get_basic (&iter, &role);
600   default_label_info->role = role;
601   dbus_message_iter_next (&iter);
602
603   default_label_info->attributes =  _atspi_dbus_hash_from_iter (&iter);
604   dbus_message_iter_next (&iter);
605
606   return default_label_info;
607 }
608
609 static unsigned char are_objects_on_the_same_bus(AtspiAccessible *obj1, AtspiAccessible *obj2)
610 {
611   const char *bus_name_1 = obj1->parent.app->bus_name;
612   const char *bus_name_2 = obj2->parent.app->bus_name;
613   return strcmp(bus_name_1, bus_name_2) == 0;
614 }
615
616 static unsigned char object_is_valid(AtspiAccessible *obj)
617 {
618   if (!obj)
619     return 0;
620   AtspiStateSet *ss = atspi_accessible_get_state_set(obj);
621   if (!ss)
622     return 0;
623   unsigned char valid = atspi_state_set_contains(ss, ATSPI_STATE_DEFUNCT) == 0;
624   g_object_unref(ss);
625   return valid;
626 }
627
628 typedef enum {
629   NEIGHBOR_SEARCH_MODE_NORMAL = 0,
630   NEIGHBOR_SEARCH_MODE_RECURSE_FROM_ROOT = 1,
631   NEIGHBOR_SEARCH_MODE_CONTINUE_AFTER_FAILED_RECURSING = 2,
632   NEIGHBOR_SEARCH_MODE_RECURSE_TO_OUTSIDE = 3,
633 } GetNeighborSearchMode;
634 /**
635  * atspi_accessible_get_neighbor:
636  * @root: a pointer to a #AtspiAccessible, which represents current root of subtree to search
637  * @start: a pointer to the #AtspiAccessible to start search from (can be null, which means start from root)
638  * @direction: direction, in which search (forward or backward)
639  *
640  * Calculates next (or previous) accessible element in logical order or null if none found.
641  *
642  * Returns: (nullable) (transfer full): a pointer to an
643  *          #AtspiAccessible element, which is next (previous) in logical order or null if none found.
644  **/
645 AtspiAccessible *
646 atspi_accessible_get_neighbor (AtspiAccessible *root,
647                                           AtspiAccessible *start,
648                                           AtspiNeighborSearchDirection direction,
649                                           GError **error)
650 {
651   g_return_val_if_fail (object_is_valid(root), NULL);
652   if (!object_is_valid(start))
653     start = root;
654   const char *root_path = ATSPI_OBJECT(root)->path;
655   AtspiAccessible *return_value = NULL;
656   g_object_ref(start);
657   unsigned char recurse;
658   GetNeighborSearchMode search_mode = NEIGHBOR_SEARCH_MODE_NORMAL;
659   GQueue *children_root_stack = g_queue_new();
660   DBusMessageIter iter;
661
662   while(1) {
663     const char *path = are_objects_on_the_same_bus(root, start) ? root_path : "";
664     DBusMessage *reply = _atspi_dbus_call_partial (start, atspi_interface_accessible, "GetNeighbor", error, "sii", path, (int)direction, (int)search_mode);
665     // call failed, error is set, so we bail out
666     if (!reply) break;
667
668     _ATSPI_DBUS_CHECK_SIG (reply, "(so)y", error, NULL);
669     dbus_message_iter_init (reply, &iter);
670     AtspiAccessible *ret = _atspi_dbus_return_accessible_from_iter (&iter);
671
672     unsigned char value = 0;
673     dbus_message_iter_get_basic (&iter, &value);
674     dbus_message_iter_next (&iter);
675     recurse = (value != 0);
676
677     dbus_message_unref(reply);
678
679     // got return value and request for recursive search, it means ret is on another bridge, than start
680     // thus we're recursing. should the recurse failed to find anything it will end with
681     if (ret && recurse) {
682       g_object_unref(G_OBJECT(start));
683       start = ret;
684       g_object_ref(start);
685       if (are_objects_on_the_same_bus(root, ret))
686         {
687           search_mode = NEIGHBOR_SEARCH_MODE_RECURSE_TO_OUTSIDE;
688         }
689       else
690         {
691           g_queue_push_tail(children_root_stack, ret);
692           search_mode = NEIGHBOR_SEARCH_MODE_RECURSE_FROM_ROOT;
693         }
694       continue;
695     }
696     // found the one we've been looking for
697     if (ret) {
698       g_object_unref(G_OBJECT(start));
699       return_value = ret;
700       break;
701     }
702
703     // we've stepped into different bridges previously and now we're going back to the last one
704     // and continuing search where we left
705     if (!g_queue_is_empty(children_root_stack)) {
706       g_object_unref(G_OBJECT(start));
707       start = g_queue_pop_tail(children_root_stack);
708
709       search_mode = NEIGHBOR_SEARCH_MODE_CONTINUE_AFTER_FAILED_RECURSING;
710       continue;
711     }
712     // there's no more bridges to check, but we might have started from one
713     // in that case there might be bridges "below" start, which we yet have to visit
714     if (!are_objects_on_the_same_bus(root, start)) {
715       unsigned char continue_loop = 1;
716       while(continue_loop) {
717         AtspiAccessible *parent = atspi_accessible_get_parent(start, NULL);
718         continue_loop = parent ? are_objects_on_the_same_bus(start, parent) : 0;
719         g_object_unref(G_OBJECT(start));
720         start = parent;
721       }
722
723       // going up thru parents put us in weird place (we didnt meet root on the way)
724       // so we bail out
725       if (!start)
726         break;
727
728       // start object now points to different bridge and must be treated as "resume after recursing"
729       search_mode = NEIGHBOR_SEARCH_MODE_CONTINUE_AFTER_FAILED_RECURSING;
730       continue;
731     }
732
733     // nothing found
734     g_object_unref(start);
735     break;
736   }
737   while(!g_queue_is_empty(children_root_stack))
738     g_object_unref(g_queue_pop_tail(children_root_stack));
739   g_queue_free(children_root_stack);
740
741   return return_value;
742 }
743
744 /**
745  * atspi_accessible_get_description:
746  * @obj: a pointer to the #AtspiAccessible object on which to operate.
747  *
748  * Gets the description of an #AtspiAccessible object.
749  *
750  * Returns: a UTF-8 string describing the #AtspiAccessible object 
751  * or NULL on exception.
752  **/
753 gchar *
754 atspi_accessible_get_description (AtspiAccessible *obj, GError **error)
755 {
756   g_return_val_if_fail (obj != NULL, g_strdup (""));
757
758   if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_DESCRIPTION))
759   {
760     if (!_atspi_dbus_get_property (obj, atspi_interface_accessible,
761                                    "Description", error, "s",
762                                    &obj->description))
763       return g_strdup ("");
764     _atspi_accessible_add_cache (obj, ATSPI_CACHE_DESCRIPTION);
765   }
766   return g_strdup (obj->description);
767 }
768
769 const char *str_parent = "Parent";
770
771 /**
772  * atspi_accessible_get_parent:
773  * @obj: a pointer to the #AtspiAccessible object to query.
774  *
775  * Gets an #AtspiAccessible object's parent container.
776  *
777  * Returns: (nullable) (transfer full): a pointer to the
778  *          #AtspiAccessible object which contains the given
779  *          #AtspiAccessible instance, or NULL if the @obj has no
780  *          parent container.
781  *
782  **/
783 AtspiAccessible *
784 atspi_accessible_get_parent (AtspiAccessible *obj, GError **error)
785 {
786   g_return_val_if_fail (obj != NULL, NULL);
787
788   if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_PARENT))
789   {
790     DBusMessage *message, *reply;
791     DBusMessageIter iter, iter_variant;
792     if (!obj->parent.app)
793       return NULL;
794     message = dbus_message_new_method_call (obj->parent.app->bus_name,
795                                             obj->parent.path,
796                                             DBUS_INTERFACE_PROPERTIES, "Get");
797     if (!message)
798       return NULL;
799     dbus_message_append_args (message, DBUS_TYPE_STRING, &atspi_interface_accessible,
800                                DBUS_TYPE_STRING, &str_parent,
801                               DBUS_TYPE_INVALID);
802     reply = _atspi_dbus_send_with_reply_and_block (message, error);
803     if (!reply)
804       return NULL;
805     if (strcmp (dbus_message_get_signature (reply), "v") != 0)
806     {
807       dbus_message_unref (reply);
808       return NULL;
809     }
810     dbus_message_iter_init (reply, &iter);
811     dbus_message_iter_recurse (&iter, &iter_variant);
812     obj->accessible_parent = _atspi_dbus_return_accessible_from_iter (&iter_variant);
813     dbus_message_unref (reply);
814     _atspi_accessible_add_cache (obj, ATSPI_CACHE_PARENT);
815   }
816   if (!obj->accessible_parent)
817     return NULL;
818   return g_object_ref (obj->accessible_parent);
819 }
820
821 /**
822  * atspi_accessible_get_child_count:
823  * @obj: a pointer to the #AtspiAccessible object on which to operate.
824  *
825  * Gets the number of children contained by an #AtspiAccessible object.
826  *
827  * Returns: a #long indicating the number of #AtspiAccessible children
828  *          contained by an #AtspiAccessible object or -1 on exception.
829  *
830  **/
831 gint
832 atspi_accessible_get_child_count (AtspiAccessible *obj, GError **error)
833 {
834   g_return_val_if_fail (obj != NULL, -1);
835
836   if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_CHILDREN))
837   {
838     dbus_int32_t ret;
839     if (!_atspi_dbus_get_property (obj, atspi_interface_accessible,
840                                    "ChildCount", error, "i", &ret))
841       return -1;
842     return ret;
843   }
844
845   if (!obj->children)
846     return 0;   /* assume it's disposed */
847
848   return obj->children->len;
849 }
850
851 /**
852  * atspi_accessible_get_child_at_index:
853  * @obj: a pointer to the #AtspiAccessible object on which to operate.
854  * @child_index: a #long indicating which child is specified.
855  *
856  * Gets the #AtspiAccessible child of an #AtspiAccessible object at a given index.
857  *
858  * Returns: (transfer full): a pointer to the #AtspiAccessible child object at
859  * index @child_index or NULL on exception.
860  **/
861 AtspiAccessible *
862 atspi_accessible_get_child_at_index (AtspiAccessible *obj,
863                             gint    child_index,
864                             GError **error)
865 {
866   AtspiAccessible *child;
867   DBusMessage *reply;
868
869   g_return_val_if_fail (obj != NULL, NULL);
870
871   if (_atspi_accessible_test_cache (obj, ATSPI_CACHE_CHILDREN))
872   {
873     if (!obj->children)
874       return NULL;      /* assume disposed */
875
876     child = g_ptr_array_index (obj->children, child_index);
877     if (child)
878       return g_object_ref (child);
879   }
880
881   reply = _atspi_dbus_call_partial (obj, atspi_interface_accessible,
882                                    "GetChildAtIndex", error, "i", child_index);
883   child = _atspi_dbus_return_accessible_from_message (reply);
884
885   if (!child)
886     return NULL;
887
888   if (_atspi_accessible_test_cache (obj, ATSPI_CACHE_CHILDREN))
889   {
890       if (child_index >= obj->children->len)
891         g_ptr_array_set_size (obj->children, child_index + 1);
892     g_ptr_array_index (obj->children, child_index) = g_object_ref (child);
893   }
894   return child;
895 }
896
897 /**
898  * atspi_accessible_get_index_in_parent:
899  * @obj: a pointer to the #AtspiAccessible object on which to operate.
900  *
901  * Gets the index of an #AtspiAccessible object within its parent's 
902  * #AtspiAccessible children list.
903  *
904  * Returns: a #glong indicating the index of the #AtspiAccessible object
905  *          in its parent,
906  *          or -1 if @obj has no containing parent or on exception.
907  **/
908 gint
909 atspi_accessible_get_index_in_parent (AtspiAccessible *obj, GError **error)
910 {
911   gint i = 0;
912   dbus_int32_t ret = -1;
913
914   g_return_val_if_fail (obj != NULL, -1);
915   if (_atspi_accessible_test_cache (obj, ATSPI_CACHE_PARENT))
916   {
917     if (!obj->accessible_parent)
918       return -1;
919
920     if (!_atspi_accessible_test_cache (obj->accessible_parent, ATSPI_CACHE_CHILDREN) || !obj->accessible_parent->children)
921         goto dbus;
922
923     for (i = 0; i < obj->accessible_parent->children->len; i++)
924       if (g_ptr_array_index (obj->accessible_parent->children, i) == obj)
925         return i;
926   }
927
928 dbus:
929   _atspi_dbus_call (obj, atspi_interface_accessible,
930                     "GetIndexInParent", NULL, "=>i", &ret);
931   return ret;
932 }
933
934 typedef struct
935 {
936   dbus_uint32_t type;
937   GArray *targets;
938 } Accessibility_Relation;
939
940 /**
941  * atspi_accessible_get_relation_set:
942  * @obj: a pointer to the #AtspiAccessible object on which to operate.
943  *
944  * Gets the set of #AtspiRelation objects which describes this #AtspiAccessible object's
945  * relationships with other #AtspiAccessible objects.
946  *
947  * Returns: (element-type AtspiRelation*) (transfer full): a #GArray of
948  *          #AtspiRelation pointers or NULL on exception.
949  **/
950 GArray *
951 atspi_accessible_get_relation_set (AtspiAccessible *obj, GError **error)
952 {
953   DBusMessage *reply;
954   DBusMessageIter iter, iter_array;
955   GArray *ret;
956
957   g_return_val_if_fail (obj != NULL, NULL);
958
959   reply = _atspi_dbus_call_partial (obj, atspi_interface_accessible, "GetRelationSet", error, "");
960   if (!reply)
961     return NULL;
962   _ATSPI_DBUS_CHECK_SIG (reply, "a(ua(so))", error, NULL);
963
964   ret = g_array_new (TRUE, TRUE, sizeof (AtspiRelation *));
965   dbus_message_iter_init (reply, &iter);
966   dbus_message_iter_recurse (&iter, &iter_array);
967   while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
968   {
969     AtspiRelation *relation;
970     relation = _atspi_relation_new_from_iter (&iter_array);
971     ret = g_array_append_val (ret, relation);
972     dbus_message_iter_next (&iter_array);
973   }
974   dbus_message_unref (reply);
975   return ret;
976 }
977
978 /**
979  * atspi_accessible_get_role:
980  * @obj: a pointer to the #AtspiAccessible object on which to operate.
981  *
982  * Gets the UI role played by an #AtspiAccessible object.
983  * This role's name can be obtained via atspi_accessible_get_role_name ().
984  *
985  * Returns: the #AtspiRole of an #AtspiAccessible object.
986  *
987  **/
988 AtspiRole
989 atspi_accessible_get_role (AtspiAccessible *obj, GError **error)
990 {
991   g_return_val_if_fail (obj != NULL, ATSPI_ROLE_INVALID);
992
993   if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_ROLE))
994   {
995     dbus_uint32_t role;
996     /* TODO: Make this a property */
997     if (_atspi_dbus_call (obj, atspi_interface_accessible, "GetRole", error, "=>u", &role))
998     {
999       obj->role = role;
1000     _atspi_accessible_add_cache (obj, ATSPI_CACHE_ROLE);
1001     }
1002   }
1003   return obj->role;
1004 }
1005
1006 /**
1007  * atspi_accessible_get_role_name:
1008  * @obj: a pointer to the #AtspiAccessible object on which to operate.
1009  *
1010  * Gets a UTF-8 string corresponding to the name of the role played by an object.
1011  * This method will return useful values for roles that fall outside the
1012  * enumeration used in atspi_accessible_get_role ().
1013  *
1014  * Returns: a UTF-8 string specifying the type of UI role played by an
1015  * #AtspiAccessible object.
1016  *
1017  **/
1018 gchar *
1019 atspi_accessible_get_role_name (AtspiAccessible *obj, GError **error)
1020 {
1021   gchar *retval = NULL;
1022   AtspiRole role;
1023
1024   g_return_val_if_fail (obj != NULL, NULL);
1025
1026   role = atspi_accessible_get_role (obj, error);
1027   if (role >= 0 && role < ATSPI_ROLE_COUNT && role != ATSPI_ROLE_EXTENDED)
1028     return atspi_role_get_name (role);
1029
1030   _atspi_dbus_call (obj, atspi_interface_accessible, "GetRoleName", error, "=>s", &retval);
1031
1032   if (!retval)
1033     retval = g_strdup ("");
1034
1035   return retval;
1036 }
1037
1038 /**
1039  * atspi_accessible_get_localized_role_name:
1040  * @obj: a pointer to the #AtspiAccessible object on which to operate.
1041  *
1042  * Gets a UTF-8 string corresponding to the name of the role played by an
1043  * object, translated to the current locale.
1044  * This method will return useful values for roles that fall outside the
1045  * enumeration used in atspi_accessible_getRole ().
1046  *
1047  * Returns: a localized, UTF-8 string specifying the type of UI role played
1048  * by an #AtspiAccessible object.
1049  *
1050  **/
1051 gchar *
1052 atspi_accessible_get_localized_role_name (AtspiAccessible *obj, GError **error)
1053 {
1054   char *retval = NULL;
1055
1056   g_return_val_if_fail (obj != NULL, NULL);
1057
1058   _atspi_dbus_call (obj, atspi_interface_accessible, "GetLocalizedRoleName", error, "=>s", &retval);
1059
1060   if (!retval)
1061     return g_strdup ("");
1062
1063   return retval;
1064 }
1065
1066 static AtspiStateSet *
1067 defunct_set ()
1068 {
1069   AtspiStateSet *set = atspi_state_set_new (NULL);
1070   atspi_state_set_add (set, ATSPI_STATE_DEFUNCT);
1071   return set;
1072 }
1073
1074 /**
1075  * atspi_accessible_get_state_set:
1076  * @obj: a pointer to the #AtspiAccessible object on which to operate.
1077  *
1078  * Gets the states currently held by an object.
1079  *
1080  * Returns: (transfer full): a pointer to an #AtspiStateSet representing an
1081  * object's current state set.
1082  **/
1083 AtspiStateSet *
1084 atspi_accessible_get_state_set (AtspiAccessible *obj)
1085 {
1086   /* TODO: Should take a GError **, but would be an API break */
1087   if (!obj->parent.app || !obj->parent.app->bus)
1088     return defunct_set ();
1089
1090   if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_STATES))
1091   {
1092     DBusMessage *reply;
1093     DBusMessageIter iter;
1094     reply = _atspi_dbus_call_partial (obj, atspi_interface_accessible,
1095                                       "GetState", NULL, "");
1096     _ATSPI_DBUS_CHECK_SIG (reply, "au", NULL, defunct_set ());
1097     dbus_message_iter_init (reply, &iter);
1098     _atspi_dbus_set_state (obj, &iter);
1099     dbus_message_unref (reply);
1100     _atspi_accessible_add_cache (obj, ATSPI_CACHE_STATES);
1101   }
1102   return g_object_ref (obj->states);
1103 }
1104
1105 /**
1106  * atspi_accessible_get_attributes:
1107  * @obj: The #AtspiAccessible being queried.
1108  *
1109  * Gets the #AttributeSet representing any assigned
1110  * name-value pair attributes or annotations for this object.
1111  * For typographic, textual, or textually-semantic attributes, see
1112  * atspi_text_get_attributes instead.
1113  *
1114  * Returns: (element-type gchar* gchar*) (transfer full): The name-value-pair
1115  * attributes assigned to this object.
1116  */
1117 GHashTable *
1118 atspi_accessible_get_attributes (AtspiAccessible *obj, GError **error)
1119 {
1120   DBusMessage *message;
1121
1122     g_return_val_if_fail (obj != NULL, NULL);
1123
1124   if (obj->priv->cache)
1125   {
1126     GValue *val = g_hash_table_lookup (obj->priv->cache, "Attributes");
1127     if (val)
1128       return g_value_dup_boxed (val);
1129   }
1130
1131   if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_ATTRIBUTES))
1132   {
1133     message = _atspi_dbus_call_partial (obj, atspi_interface_accessible,
1134                                         "GetAttributes", error, "");
1135     obj->attributes = _atspi_dbus_return_hash_from_message (message);
1136     _atspi_accessible_add_cache (obj, ATSPI_CACHE_ATTRIBUTES);
1137   }
1138
1139   if (!obj->attributes)
1140     return NULL;
1141   return g_hash_table_ref (obj->attributes);
1142 }
1143
1144 static void
1145 add_to_attribute_array (gpointer key, gpointer value, gpointer data)
1146 {
1147   GArray **array = (GArray **)data;
1148   gchar *str = g_strconcat (key, ":", value, NULL);
1149   *array = g_array_append_val (*array, str);
1150 }
1151
1152 /**
1153  * atspi_accessible_get_attributes_as_array:
1154  * @obj: The #AtspiAccessible being queried.
1155  *
1156  * Gets a #GArray representing any assigned
1157  * name-value pair attributes or annotations for this object.
1158  * For typographic, textual, or textually-semantic attributes, see
1159  * atspi_text_get_attributes_as_array instead.
1160  *
1161  * Returns: (element-type gchar*) (transfer full): The name-value-pair
1162  *          attributes assigned to this object.
1163  */
1164 GArray *
1165 atspi_accessible_get_attributes_as_array (AtspiAccessible *obj, GError **error)
1166 {
1167   DBusMessage *message;
1168
1169     g_return_val_if_fail (obj != NULL, NULL);
1170
1171   if (obj->priv->cache)
1172   {
1173     GValue *val = g_hash_table_lookup (obj->priv->cache, "Attributes");
1174     if (val)
1175     {
1176       GArray *array = g_array_new (TRUE, TRUE, sizeof (gchar *));
1177       GHashTable *attributes = g_value_get_boxed (val);
1178       g_hash_table_foreach (attributes, add_to_attribute_array, &array);
1179       return array;
1180     }
1181   }
1182
1183   message = _atspi_dbus_call_partial (obj, atspi_interface_accessible, "GetAttributes", error, "");
1184   return _atspi_dbus_return_attribute_array_from_message (message);
1185 }
1186
1187 /**
1188  * atspi_accessible_get_application:
1189  * @obj: The #AtspiAccessible being queried.
1190  *
1191  * Gets the containing #AtspiApplication for an object.
1192  *
1193  * Returns: (transfer full): the containing #AtspiApplication instance for
1194  *          this object.
1195  */
1196 AtspiAccessible *
1197 atspi_accessible_get_application (AtspiAccessible *obj, GError **error)
1198 {
1199   AtspiAccessible *parent;
1200
1201   g_object_ref (obj);
1202   for (;;)
1203   {
1204     parent = atspi_accessible_get_parent (obj, NULL);
1205     if (!parent && obj->parent.app &&
1206         atspi_accessible_get_role (obj, NULL) != ATSPI_ROLE_APPLICATION)
1207     {
1208       AtspiAccessible *root = g_object_ref (obj->parent.app->root);
1209       if (root)
1210       {
1211         g_object_unref (obj);
1212         if (atspi_accessible_get_role (root, NULL) == ATSPI_ROLE_DESKTOP_FRAME)
1213         {
1214           g_object_unref (root);
1215           return NULL;
1216         }
1217         return root;
1218       }
1219     }
1220     if (!parent || parent == obj ||
1221         atspi_accessible_get_role (parent, NULL) == ATSPI_ROLE_DESKTOP_FRAME)
1222   {
1223     if (parent)
1224       g_object_unref (parent);
1225     return obj;
1226   }
1227     g_object_unref (obj);
1228     obj = parent;
1229   }
1230 }
1231
1232 /* Application-specific methods */
1233
1234 /**
1235  * atspi_accessible_get_toolkit_name:
1236  * @obj: a pointer to the #AtspiAccessible object on which to operate.
1237  *
1238  * Gets the toolkit name for an #AtspiAccessible object.
1239  * Only works on application root objects.
1240  *
1241  * Returns: a UTF-8 string indicating the toolkit name for the #AtspiAccessible object or NULL on exception.
1242  **/
1243 gchar *
1244 atspi_accessible_get_toolkit_name (AtspiAccessible *obj, GError **error)
1245 {
1246   g_return_val_if_fail (obj != NULL, NULL);
1247
1248   if (!obj->parent.app)
1249     return NULL;
1250
1251   if (!obj->parent.app->toolkit_name)
1252     _atspi_dbus_get_property (obj, atspi_interface_application, "ToolkitName",
1253                               error, "s", &obj->parent.app->toolkit_name);
1254
1255   return g_strdup (obj->parent.app->toolkit_name);
1256 }
1257
1258 /**
1259  * atspi_accessible_get_toolkit_version:
1260  * @obj: a pointer to the #AtspiAccessible object on which to operate.
1261  *
1262  * Gets the toolkit version for an #AtspiAccessible object.
1263  * Only works on application root objects.
1264  *
1265  * Returns: a UTF-8 string indicating the toolkit version for the #AtspiAccessible object or NULL on exception.
1266  **/
1267 gchar *
1268 atspi_accessible_get_toolkit_version (AtspiAccessible *obj, GError **error)
1269 {
1270   g_return_val_if_fail (obj != NULL, NULL);
1271
1272   if (!obj->parent.app)
1273     return NULL;
1274
1275   if (!obj->parent.app->toolkit_version)
1276     _atspi_dbus_get_property (obj, atspi_interface_application, "Version",
1277                               error, "s", &obj->parent.app->toolkit_version);
1278
1279   return g_strdup (obj->parent.app->toolkit_version);
1280 }
1281
1282 /**
1283  * atspi_accessible_get_atspi_version:
1284  * @obj: a pointer to the #AtspiAccessible object on which to operate.
1285  *
1286  * Gets the AT-SPI IPC specification version supported by the application
1287  * pointed to by the #AtspiAccessible object.
1288  * Only works on application root objects.
1289  *
1290  * Returns: a UTF-8 string indicating the AT-SPI version for the #AtspiAccessible object or NULL on exception.
1291  **/
1292 gchar *
1293 atspi_accessible_get_atspi_version (AtspiAccessible *obj, GError **error)
1294 {
1295   g_return_val_if_fail (obj != NULL, NULL);
1296
1297   if (!obj->parent.app)
1298     return NULL;
1299
1300   if (!obj->parent.app->atspi_version)
1301     _atspi_dbus_get_property (obj, atspi_interface_application, "AtspiVersion",
1302                               error, "s", &obj->parent.app->atspi_version);
1303
1304   return g_strdup (obj->parent.app->atspi_version);
1305 }
1306
1307 /**
1308  * atspi_accessible_get_id:
1309  * @obj: a pointer to the #AtspiAccessible object on which to operate.
1310  *
1311  * Gets the application id for a #AtspiAccessible object.
1312  * Only works on application root objects.
1313  *
1314  * Returns: a positive #gint indicating the id for the #AtspiAccessible object
1315  * or -1 on exception.
1316  **/
1317 gint
1318 atspi_accessible_get_id (AtspiAccessible *obj, GError **error)
1319 {
1320   gint ret = -1;
1321
1322   g_return_val_if_fail (obj != NULL, -1);
1323
1324   if (!_atspi_dbus_get_property (obj, atspi_interface_application, "Id", error, "i", &ret))
1325       return -1;
1326   return ret;
1327 }
1328
1329
1330 /* Interface query methods */
1331
1332 static gboolean
1333 _atspi_accessible_is_a (AtspiAccessible *accessible,
1334                       const char *interface_name)
1335 {
1336   int n;
1337
1338   if (accessible == NULL)
1339     {
1340       return FALSE;
1341     }
1342
1343   if (!_atspi_accessible_test_cache (accessible, ATSPI_CACHE_INTERFACES))
1344   {
1345     DBusMessage *reply;
1346     DBusMessageIter iter;
1347     reply = _atspi_dbus_call_partial (accessible, atspi_interface_accessible,
1348                                       "GetInterfaces", NULL, "");
1349     _ATSPI_DBUS_CHECK_SIG (reply, "as", NULL, FALSE);
1350     dbus_message_iter_init (reply, &iter);
1351     _atspi_dbus_set_interfaces (accessible, &iter);
1352     dbus_message_unref (reply);
1353     _atspi_accessible_add_cache (accessible, ATSPI_CACHE_INTERFACES);
1354   }
1355
1356   n = _atspi_get_iface_num (interface_name);
1357   if (n == -1) return FALSE;
1358   return (gboolean) ((accessible->interfaces & (1 << n))? TRUE: FALSE);
1359 }
1360
1361 /**
1362  * atspi_accessible_is_action:
1363  * @obj: a pointer to the #AtspiAccessible instance to query.
1364  *
1365  * Query whether the specified #AtspiAccessible implements the
1366  * #AtspiAction interface.
1367  *
1368  * Returns: #TRUE if @obj implements the #AtspiAction interface,
1369  *          #FALSE otherwise.
1370  **/
1371 gboolean
1372 atspi_accessible_is_action (AtspiAccessible *obj)
1373 {
1374   return _atspi_accessible_is_a (obj,
1375                               atspi_interface_action);
1376 }
1377
1378 /**
1379  * atspi_accessible_is_application:
1380  * @obj: a pointer to the #AtspiAccessible instance to query.
1381  *
1382  * Query whether the specified #AtspiAccessible implements the
1383  * #AtspiApplication interface.
1384  *
1385  * Returns: #TRUE if @obj implements the #AtspiApplication interface,
1386  *          #FALSE otherwise.
1387  **/
1388 gboolean
1389 atspi_accessible_is_application (AtspiAccessible *obj)
1390 {
1391   return _atspi_accessible_is_a (obj,
1392                               atspi_interface_application);
1393 }
1394
1395 /**
1396  * atspi_accessible_is_collection:
1397  * @obj: a pointer to the #AtspiAccessible instance to query.
1398  *
1399  * Query whether the specified #AtspiAccessible implements the
1400  * #AtspiCollection interface.
1401  *
1402  * Returns: #TRUE if @obj implements the #AtspiCollection interface,
1403  *          #FALSE otherwise.
1404  **/
1405 gboolean
1406 atspi_accessible_is_collection (AtspiAccessible *obj)
1407 {
1408      return _atspi_accessible_is_a (obj,
1409                               atspi_interface_collection);
1410 }
1411
1412 /**
1413  * atspi_accessible_is_component:
1414  * @obj: a pointer to the #AtspiAccessible instance to query.
1415  *
1416  * Query whether the specified #AtspiAccessible implements #AtspiComponent.
1417  *
1418  * Returns: #TRUE if @obj implements the #AtspiComponent interface,
1419  *          #FALSE otherwise.
1420  **/
1421 gboolean
1422 atspi_accessible_is_component (AtspiAccessible *obj)
1423 {
1424   return _atspi_accessible_is_a (obj,
1425                               atspi_interface_component);
1426 }
1427
1428 /**
1429  * atspi_accessible_is_document:
1430  * @obj: a pointer to the #AtspiAccessible instance to query.
1431  *
1432  * Query whether the specified #AtspiAccessible implements the
1433  * #AtspiDocument interface.
1434  *
1435  * Returns: #TRUE if @obj implements the #AtspiDocument interface,
1436  *          #FALSE otherwise.
1437  **/
1438 gboolean
1439 atspi_accessible_is_document (AtspiAccessible *obj)
1440 {
1441   return _atspi_accessible_is_a (obj,
1442                               atspi_interface_document);
1443 }
1444
1445 /**
1446  * atspi_accessible_is_editable_text:
1447  * @obj: a pointer to the #AtspiAccessible instance to query.
1448  *
1449  * Query whether the specified #AtspiAccessible implements the
1450  * #AtspiEditableText interface.
1451  *
1452  * Returns: #TRUE if @obj implements the #AtspiEditableText interface,
1453  *          #FALSE otherwise.
1454  **/
1455 gboolean
1456 atspi_accessible_is_editable_text (AtspiAccessible *obj)
1457 {
1458   return _atspi_accessible_is_a (obj,
1459                               atspi_interface_editable_text);
1460 }
1461
1462 /**
1463  * atspi_accessible_is_hypertext:
1464  * @obj: a pointer to the #AtspiAccessible instance to query.
1465  *
1466  * Query whether the specified #AtspiAccessible implements the
1467  * #AtspiHypertext interface.
1468  *
1469  * Returns: #TRUE if @obj implements the #AtspiHypertext interface,
1470  *          #FALSE otherwise.
1471  **/
1472 gboolean
1473 atspi_accessible_is_hypertext (AtspiAccessible *obj)
1474 {
1475   return _atspi_accessible_is_a (obj,
1476                               atspi_interface_hypertext);
1477 }
1478
1479 /**
1480  * atspi_accessible_is_hyperlink:
1481  * @obj: a pointer to the #AtspiAccessible instance to query.
1482  *
1483  * Query whether the specified #AtspiAccessible implements the
1484  * #AtspiHyperlink interface.
1485  *
1486  * Returns: #TRUE if @obj implements the #AtspiHypertext interface,
1487  *          #FALSE otherwise.
1488  **/
1489 gboolean
1490 atspi_accessible_is_hyperlink (AtspiAccessible *obj)
1491 {
1492   return _atspi_accessible_is_a (obj,
1493                               atspi_interface_hyperlink);
1494 }
1495
1496 /**
1497  * atspi_accessible_is_image:
1498  * @obj: a pointer to the #AtspiAccessible instance to query.
1499  *
1500  * Query whether the specified #AtspiAccessible implements the
1501  * #AtspiImage interface.
1502  *
1503  * Returns: #TRUE if @obj implements the #AtspiImage interface,
1504  *          #FALSE otherwise.
1505 **/
1506 gboolean
1507 atspi_accessible_is_image (AtspiAccessible *obj)
1508 {
1509   return _atspi_accessible_is_a (obj,
1510                               atspi_interface_image);
1511 }
1512
1513 /**
1514  * atspi_accessible_is_selection:
1515  * @obj: a pointer to the #AtspiAccessible instance to query.
1516  *
1517  * Query whether the specified #AtspiAccessible implements the
1518  * #AtspiSelection interface.
1519  *
1520  * Returns: #TRUE if @obj implements the #AtspiSelection interface,
1521  *          #FALSE otherwise.
1522 **/
1523 gboolean
1524 atspi_accessible_is_selection (AtspiAccessible *obj)
1525 {
1526   return _atspi_accessible_is_a (obj,
1527                               atspi_interface_selection);
1528 }
1529
1530 /**
1531  * atspi_accessible_is_table:
1532  * @obj: a pointer to the #AtspiAccessible instance to query.
1533  *
1534  * Query whether the specified #AtspiAccessible implements the
1535  * #AtspiTable interface.
1536  *
1537  * Returns: #TRUE if @obj implements the #AtspiTable interface,
1538  *          #FALSE otherwise.
1539 **/
1540 gboolean
1541 atspi_accessible_is_table (AtspiAccessible *obj)
1542 {
1543   return _atspi_accessible_is_a (obj,
1544                               atspi_interface_table);
1545 }
1546
1547 /**
1548  * atspi_accessible_is_table_cell:
1549  * @obj: a pointer to the #AtspiAccessible instance to query.
1550  *
1551  * Query whether the specified #AtspiAccessible implements the
1552  * #AtspiTableCell interface.
1553  *
1554  * Returns: #TRUE if @obj implements the #AtspiTable interface,
1555  *          #FALSE otherwise.
1556 **/
1557 gboolean
1558 atspi_accessible_is_table_cell (AtspiAccessible *obj)
1559 {
1560   return _atspi_accessible_is_a (obj,
1561                               atspi_interface_table_cell);
1562 }
1563
1564 /**
1565  * atspi_accessible_is_streamable_content:
1566  * @obj: a pointer to the #AtspiAccessible instance to query.
1567  *
1568  * Query whether the specified #AtspiAccessible implements the
1569  * #AtspiStreamableContent interface.
1570  *
1571  * Returns: #TRUE if @obj implements the #AtspiStreamableContent interface,
1572  *          #FALSE otherwise.
1573 **/
1574 gboolean
1575 atspi_accessible_is_streamable_content (AtspiAccessible *obj)
1576 {
1577 #if 0
1578   return _atspi_accessible_is_a (obj,
1579                               atspi_interface_streamable_content);
1580 #else
1581   g_warning ("Streamable content not implemented");
1582   return FALSE;
1583 #endif
1584 }
1585
1586 /**
1587  * atspi_accessible_is_text:
1588  * @obj: a pointer to the #AtspiAccessible instance to query.
1589  *
1590  * Query whether the specified #AtspiAccessible implements the
1591  * #AtspiText interface.
1592  *
1593  * Returns: #TRUE if @obj implements the #AtspiText interface,
1594  *          #FALSE otherwise.
1595 **/
1596 gboolean
1597 atspi_accessible_is_text (AtspiAccessible *obj)
1598 {
1599   return _atspi_accessible_is_a (obj,
1600                               atspi_interface_text);
1601 }
1602
1603 /**
1604  * atspi_accessible_is_value:
1605  * @obj: a pointer to the #AtspiAccessible instance to query.
1606  *
1607  * Query whether the specified #AtspiAccessible implements the
1608  * #AtspiValue interface.
1609  *
1610  * Returns: #TRUE if @obj implements the #AtspiValue interface,
1611  *          #FALSE otherwise.
1612 **/
1613 gboolean
1614 atspi_accessible_is_value (AtspiAccessible *obj)
1615 {
1616   return _atspi_accessible_is_a (obj,
1617                               atspi_interface_value);
1618 }
1619
1620 /**
1621  * atspi_accessible_get_action: (rename-to atspi_accessible_get_action_iface)
1622  * @obj: a pointer to the #AtspiAccessible instance to query.
1623  *
1624  * Gets the #AtspiAction interface for an #AtspiAccessible.
1625  *
1626  * Returns: (transfer full): a pointer to an #AtspiAction interface
1627  *          instance, or NULL if @obj does not implement #AtspiAction.
1628  *
1629  * Deprecated: 2.10: Use atspi_accessible_get_action_iface instead.
1630  **/
1631 AtspiAction *
1632 atspi_accessible_get_action (AtspiAccessible *accessible)
1633 {
1634   return (_atspi_accessible_is_a (accessible, atspi_interface_action) ?
1635           g_object_ref (ATSPI_ACTION (accessible)) : NULL);
1636 }
1637
1638 /**
1639  * atspi_accessible_get_action_iface:
1640  * @obj: a pointer to the #AtspiAccessible instance to query.
1641  *
1642  * Gets the #AtspiAction interface for an #AtspiAccessible.
1643  *
1644  * Returns: (transfer full): a pointer to an #AtspiAction interface
1645  *          instance, or NULL if @obj does not implement #AtspiAction.
1646  **/
1647 AtspiAction *
1648 atspi_accessible_get_action_iface (AtspiAccessible *accessible)
1649 {
1650   return (_atspi_accessible_is_a (accessible, atspi_interface_action) ?
1651           g_object_ref (ATSPI_ACTION (accessible)) : NULL);
1652 }
1653
1654 /**
1655  * atspi_accessible_get_collection: (rename-to atspi_accessible_get_collection_iface)
1656  * @obj: a pointer to the #AtspiAccessible instance to query.
1657  *
1658  * Gets the #AtspiCollection interface for an #AtspiAccessible.
1659  *
1660  * Returns: (transfer full): a pointer to an #AtspiCollection interface
1661  *          instance, or NULL if @obj does not implement #AtspiCollection.
1662  *
1663  * Deprecated: 2.10: Use atspi_accessible_get_collection_iface instead.
1664  **/
1665 AtspiCollection *
1666 atspi_accessible_get_collection (AtspiAccessible *accessible)
1667 {
1668   return (_atspi_accessible_is_a (accessible, atspi_interface_collection) ?
1669           g_object_ref (ATSPI_COLLECTION (accessible)) : NULL);
1670 }
1671
1672 /**
1673  * atspi_accessible_get_collection_iface:
1674  * @obj: a pointer to the #AtspiAccessible instance to query.
1675  *
1676  * Gets the #AtspiCollection interface for an #AtspiAccessible.
1677  *
1678  * Returns: (transfer full): a pointer to an #AtspiCollection interface
1679  *          instance, or NULL if @obj does not implement #AtspiCollection.
1680  **/
1681 AtspiCollection *
1682 atspi_accessible_get_collection_iface (AtspiAccessible *accessible)
1683 {
1684   return (_atspi_accessible_is_a (accessible, atspi_interface_collection) ?
1685           g_object_ref (ATSPI_COLLECTION (accessible)) : NULL);
1686 }
1687
1688 /**
1689  * atspi_accessible_get_component: (rename-to atspi_accessible_get_component_iface)
1690  * @obj: a pointer to the #AtspiAccessible instance to query.
1691  *
1692  * Gets the #AtspiComponent interface for an #AtspiAccessible.
1693  *
1694  * Returns: (transfer full): a pointer to an #AtspiComponent interface
1695  *          instance, or NULL if @obj does not implement #AtspiComponent.
1696  *
1697  * Deprecated: 2.10: Use atspi_accessible_get_component_iface instead.
1698  **/
1699 AtspiComponent *
1700 atspi_accessible_get_component (AtspiAccessible *obj)
1701 {
1702   return (_atspi_accessible_is_a (obj, atspi_interface_component) ?
1703           g_object_ref (ATSPI_COMPONENT (obj)) : NULL);
1704 }
1705
1706 /**
1707  * atspi_accessible_get_component_iface:
1708  * @obj: a pointer to the #AtspiAccessible instance to query.
1709  *
1710  * Gets the #AtspiComponent interface for an #AtspiAccessible.
1711  *
1712  * Returns: (transfer full): a pointer to an #AtspiComponent interface
1713  *          instance, or NULL if @obj does not implement #AtspiComponent.
1714  **/
1715 AtspiComponent *
1716 atspi_accessible_get_component_iface (AtspiAccessible *obj)
1717 {
1718   return (_atspi_accessible_is_a (obj, atspi_interface_component) ?
1719           g_object_ref (ATSPI_COMPONENT (obj)) : NULL);
1720 }
1721
1722 /**
1723  * atspi_accessible_get_document: (rename-to atspi_accessible_get_document_iface)
1724  * @obj: a pointer to the #AtspiAccessible instance to query.
1725  *
1726  * Gets the #AtspiDocument interface for an #AtspiAccessible.
1727  *
1728  * Returns: (transfer full): a pointer to an #AtspiDocument interface
1729  *          instance, or NULL if @obj does not implement #AtspiDocument.
1730  *
1731  * Deprecated: 2.10: Use atspi_accessible_get_document_iface instead.
1732  **/
1733 AtspiDocument *
1734 atspi_accessible_get_document (AtspiAccessible *accessible)
1735 {
1736   return (_atspi_accessible_is_a (accessible, atspi_interface_document) ?
1737           g_object_ref (ATSPI_DOCUMENT (accessible)) : NULL);
1738 }
1739
1740 /**
1741  * atspi_accessible_get_document_iface:
1742  * @obj: a pointer to the #AtspiAccessible instance to query.
1743  *
1744  * Gets the #AtspiDocument interface for an #AtspiAccessible.
1745  *
1746  * Returns: (transfer full): a pointer to an #AtspiDocument interface
1747  *          instance, or NULL if @obj does not implement #AtspiDocument.
1748  **/
1749 AtspiDocument *
1750 atspi_accessible_get_document_iface (AtspiAccessible *accessible)
1751 {
1752   return (_atspi_accessible_is_a (accessible, atspi_interface_document) ?
1753           g_object_ref (ATSPI_DOCUMENT (accessible)) : NULL);
1754 }
1755
1756 /**
1757  * atspi_accessible_get_editable_text: (rename-to atspi_accessible_get_editable_text_iface)
1758  * @obj: a pointer to the #AtspiAccessible instance to query.
1759  *
1760  * Gets the #AtspiEditableText interface for an #AtspiAccessible.
1761  *
1762  * Returns: (transfer full): a pointer to an #AtspiEditableText interface
1763  *          instance, or NULL if @obj does not implement #AtspiEditableText.
1764  *
1765  * Deprecated: 2.10: Use atspi_accessible_get_editable_text_iface instead.
1766  **/
1767 AtspiEditableText *
1768 atspi_accessible_get_editable_text (AtspiAccessible *accessible)
1769 {
1770   return (_atspi_accessible_is_a (accessible, atspi_interface_editable_text) ?
1771           g_object_ref (ATSPI_EDITABLE_TEXT (accessible)) : NULL);
1772 }
1773
1774 /**
1775  * atspi_accessible_get_editable_text_iface:
1776  * @obj: a pointer to the #AtspiAccessible instance to query.
1777  *
1778  * Gets the #AtspiEditableText interface for an #AtspiAccessible.
1779  *
1780  * Returns: (transfer full): a pointer to an #AtspiEditableText interface
1781  *          instance, or NULL if @obj does not implement #AtspiEditableText.
1782  **/
1783 AtspiEditableText *
1784 atspi_accessible_get_editable_text_iface (AtspiAccessible *accessible)
1785 {
1786   return (_atspi_accessible_is_a (accessible, atspi_interface_editable_text) ?
1787           g_object_ref (ATSPI_EDITABLE_TEXT (accessible)) : NULL);
1788 }
1789
1790 /**
1791  * atspi_accessible_get_hyperlink:
1792  * @obj: a pointer to the #AtspiAccessible object on which to operate.
1793  *
1794  * Gets the #AtspiHyperlink interface for an #AtspiAccessible.
1795  *
1796  * Returns: (transfer full): the #AtspiHyperlink object associated with
1797  *          the given #AtspiAccessible, or NULL if not supported.
1798  **/
1799 AtspiHyperlink *
1800 atspi_accessible_get_hyperlink (AtspiAccessible *accessible)
1801 {
1802   return (_atspi_accessible_is_a (accessible, atspi_interface_hyperlink) ?
1803           _atspi_hyperlink_new (accessible->parent.app, accessible->parent.path) : NULL);
1804 }
1805
1806 /**
1807  * atspi_accessible_get_hypertext: (rename-to atspi_accessible_get_hypertext_iface)
1808  * @obj: a pointer to the #AtspiAccessible instance to query.
1809  *
1810  * Gets the #AtspiHypertext interface for an #AtspiAccessible.
1811  *
1812  * Returns: (transfer full): a pointer to an #AtspiHypertext interface
1813  *          instance, or NULL if @obj does not implement #AtspiHypertext.
1814  *
1815  * Deprecated: 2.10: Use atspi_accessible_get_hypertext_iface instead.
1816  **/
1817 AtspiHypertext *
1818 atspi_accessible_get_hypertext (AtspiAccessible *accessible)
1819 {
1820   return (_atspi_accessible_is_a (accessible, atspi_interface_hypertext) ?
1821           g_object_ref (ATSPI_HYPERTEXT (accessible)) : NULL);
1822 }
1823
1824 /**
1825  * atspi_accessible_get_hypertext_iface:
1826  * @obj: a pointer to the #AtspiAccessible instance to query.
1827  *
1828  * Gets the #AtspiHypertext interface for an #AtspiAccessible.
1829  *
1830  * Returns: (transfer full): a pointer to an #AtspiHypertext interface
1831  *          instance, or NULL if @obj does not implement #AtspiHypertext.
1832  **/
1833 AtspiHypertext *
1834 atspi_accessible_get_hypertext_iface (AtspiAccessible *accessible)
1835 {
1836   return (_atspi_accessible_is_a (accessible, atspi_interface_hypertext) ?
1837           g_object_ref (ATSPI_HYPERTEXT (accessible)) : NULL);
1838 }
1839
1840 /**
1841  * atspi_accessible_get_image: (rename-to atspi_accessible_get_image_iface)
1842  * @obj: a pointer to the #AtspiAccessible instance to query.
1843  *
1844  * Gets the #AtspiImage interface for an #AtspiAccessible.
1845  *
1846  * Returns: (transfer full): a pointer to an #AtspiImage interface instance, or
1847  *          NULL if @obj does not implement #AtspiImage.
1848  *
1849  * Deprecated: 2.10: Use atspi_accessible_get_image_iface instead.
1850  **/
1851 AtspiImage *
1852 atspi_accessible_get_image (AtspiAccessible *accessible)
1853 {
1854   return (_atspi_accessible_is_a (accessible, atspi_interface_image) ?
1855           g_object_ref (ATSPI_IMAGE (accessible)) : NULL);
1856 }
1857
1858 /**
1859  * atspi_accessible_get_image_iface:
1860  * @obj: a pointer to the #AtspiAccessible instance to query.
1861  *
1862  * Gets the #AtspiImage interface for an #AtspiAccessible.
1863  *
1864  * Returns: (transfer full): a pointer to an #AtspiImage interface instance, or
1865  *          NULL if @obj does not implement #AtspiImage.
1866  **/
1867 AtspiImage *
1868 atspi_accessible_get_image_iface (AtspiAccessible *accessible)
1869 {
1870   return (_atspi_accessible_is_a (accessible, atspi_interface_image) ?
1871           g_object_ref (ATSPI_IMAGE (accessible)) : NULL);
1872 }
1873
1874 /**
1875  * atspi_accessible_get_selection: (rename-to atspi_accessible_get_selection_iface)
1876  * @obj: a pointer to the #AtspiAccessible instance to query.
1877  *
1878  * Gets the #AtspiSelection interface for an #AtspiAccessible.
1879  *
1880  * Returns: (transfer full): a pointer to an #AtspiSelection interface
1881  *          instance, or NULL if @obj does not implement #AtspiSelection.
1882  *
1883  * Deprecated: 2.10: Use atspi_accessible_get_selection_iface instead.
1884  **/
1885 AtspiSelection *
1886 atspi_accessible_get_selection (AtspiAccessible *accessible)
1887 {
1888   return (_atspi_accessible_is_a (accessible, atspi_interface_selection) ?
1889           g_object_ref (ATSPI_SELECTION (accessible)) : NULL);
1890 }
1891
1892 /**
1893  * atspi_accessible_get_selection_iface:
1894  * @obj: a pointer to the #AtspiAccessible instance to query.
1895  *
1896  * Gets the #AtspiSelection interface for an #AtspiAccessible.
1897  *
1898  * Returns: (transfer full): a pointer to an #AtspiSelection interface
1899  *          instance, or NULL if @obj does not implement #AtspiSelection.
1900  **/
1901 AtspiSelection *
1902 atspi_accessible_get_selection_iface (AtspiAccessible *accessible)
1903 {
1904   return (_atspi_accessible_is_a (accessible, atspi_interface_selection) ?
1905           g_object_ref (ATSPI_SELECTION (accessible)) : NULL);
1906 }
1907
1908 #if 0
1909 /**
1910  * atspi_accessible_get_streamable_content:
1911  * @obj: a pointer to the #AtspiAccessible instance to query.
1912  *
1913  * Gets the #AtspiStreamableContent interface for an #AtspiAccessible.
1914  *
1915  * Returns: (transfer full): a pointer to an #AtspiStreamableContent interface
1916  *          instance, or NULL if @obj does not implement #AtspiStreamableContent.
1917  **/
1918 AtspiStreamableContent *
1919 atspi_accessible_get_streamable_content (AtspiAccessible *accessible)
1920 {
1921   return (_atspi_accessible_is_a (accessible, atspi_interface_streamable_content) ?
1922           accessible : NULL);
1923 }
1924 #endif
1925
1926 /**
1927  * atspi_accessible_get_table: (rename-to atspi_accessible_get_table_iface)
1928  * @obj: a pointer to the #AtspiAccessible instance to query.
1929  *
1930  * Gets the #AtspiTable interface for an #AtspiAccessible.
1931  *
1932  * Returns: (transfer full): a pointer to an #AtspiTable interface instance, or
1933  *          NULL if @obj does not implement #AtspiTable.
1934  *
1935  * Deprecated: 2.10: Use atspi_accessible_get_table_iface instead.
1936  **/
1937 AtspiTable *
1938 atspi_accessible_get_table (AtspiAccessible *obj)
1939 {
1940   return (_atspi_accessible_is_a (obj, atspi_interface_table) ?
1941           g_object_ref (ATSPI_TABLE (obj)) : NULL);
1942 }
1943
1944 /**
1945  * atspi_accessible_get_table_iface:
1946  * @obj: a pointer to the #AtspiAccessible instance to query.
1947  *
1948  * Gets the #AtspiTable interface for an #AtspiAccessible.
1949  *
1950  * Returns: (transfer full): a pointer to an #AtspiTable interface instance, or
1951  *          NULL if @obj does not implement #AtspiTable.
1952  **/
1953 AtspiTable *
1954 atspi_accessible_get_table_iface (AtspiAccessible *obj)
1955 {
1956   return (_atspi_accessible_is_a (obj, atspi_interface_table) ?
1957           g_object_ref (ATSPI_TABLE (obj)) : NULL);
1958 }
1959
1960 /**
1961  * atspi_accessible_get_table_cell:
1962  * @obj: a pointer to the #AtspiAccessible instance to query.
1963  *
1964  * Gets the #AtspiTableCell interface for an #AtspiAccessible.
1965  *
1966  * Returns: (transfer full): a pointer to an #AtspiTableCell interface instance,
1967  *          or NULL if @obj does not implement #AtspiTable.
1968  **/
1969 AtspiTableCell *
1970 atspi_accessible_get_table_cell (AtspiAccessible *obj)
1971 {
1972   return (_atspi_accessible_is_a (obj, atspi_interface_table_cell) ?
1973           g_object_ref (ATSPI_TABLE_CELL (obj)) : NULL);
1974 }
1975
1976 /**
1977  * atspi_accessible_get_text: (rename-to atspi_accessible_get_text_iface)
1978  * @obj: a pointer to the #AtspiAccessible instance to query.
1979  *
1980  * Gets the #AtspiTable interface for an #AtspiAccessible.
1981  *
1982  * Returns: (transfer full): a pointer to an #AtspiText interface instance, or
1983  *          NULL if @obj does not implement #AtspiText.
1984  *
1985  * Deprecated: 2.10: Use atspi_accessible_get_text_iface instead.
1986  **/
1987 AtspiText *
1988 atspi_accessible_get_text (AtspiAccessible *obj)
1989 {
1990   return (_atspi_accessible_is_a (obj, atspi_interface_text) ?
1991           g_object_ref (ATSPI_TEXT (obj)) : NULL);
1992 }
1993
1994 /**
1995  * atspi_accessible_get_text_iface:
1996  * @obj: a pointer to the #AtspiAccessible instance to query.
1997  *
1998  * Gets the #AtspiTable interface for an #AtspiAccessible.
1999  *
2000  * Returns: (transfer full): a pointer to an #AtspiText interface instance, or
2001  *          NULL if @obj does not implement #AtspiText.
2002  **/
2003 AtspiText *
2004 atspi_accessible_get_text_iface (AtspiAccessible *obj)
2005 {
2006   return (_atspi_accessible_is_a (obj, atspi_interface_text) ?
2007           g_object_ref (ATSPI_TEXT (obj)) : NULL);
2008 }
2009
2010 /**
2011  * atspi_accessible_get_value: (rename-to atspi_accessible_get_value_iface)
2012  * @obj: a pointer to the #AtspiAccessible instance to query.
2013  *
2014  * Gets the #AtspiTable interface for an #AtspiAccessible.
2015  *
2016  * Returns: (transfer full): a pointer to an #AtspiValue interface instance, or
2017  *          NULL if @obj does not implement #AtspiValue.
2018  *
2019  * Deprecated: 2.10: Use atspi_accessible_get_value_iface instead.
2020  **/
2021 AtspiValue *
2022 atspi_accessible_get_value (AtspiAccessible *accessible)
2023 {
2024   return (_atspi_accessible_is_a (accessible, atspi_interface_value) ?
2025           g_object_ref (ATSPI_VALUE (accessible)) : NULL);
2026 }
2027
2028 /**
2029  * atspi_accessible_get_value_iface:
2030  * @obj: a pointer to the #AtspiAccessible instance to query.
2031  *
2032  * Gets the #AtspiTable interface for an #AtspiAccessible.
2033  *
2034  * Returns: (transfer full): a pointer to an #AtspiValue interface instance, or
2035  *          NULL if @obj does not implement #AtspiValue.
2036  **/
2037 AtspiValue *
2038 atspi_accessible_get_value_iface (AtspiAccessible *accessible)
2039 {
2040   return (_atspi_accessible_is_a (accessible, atspi_interface_value) ?
2041           g_object_ref (ATSPI_VALUE (accessible)) : NULL);
2042 }
2043
2044 static void
2045 append_const_val (GArray *array, const gchar *val)
2046 {
2047   gchar *dup = g_strdup (val);
2048
2049   if (dup)
2050     g_array_append_val (array, dup);
2051 }
2052
2053 /**
2054  * atspi_accessible_get_interfaces:
2055  * @obj: The #AtspiAccessible to query.
2056  *
2057  * A set of pointers to all interfaces supported by an #AtspiAccessible.
2058  *
2059  * Returns: (element-type gchar*) (transfer full): A #GArray of strings
2060  *          describing the interfaces supported by the object.  Interfaces are
2061  *          denoted in short-hand (i.e. "Component", "Text" etc.).
2062  **/
2063 GArray *
2064 atspi_accessible_get_interfaces (AtspiAccessible *obj)
2065 {
2066   GArray *ret;
2067
2068   g_return_val_if_fail (obj != NULL, NULL);
2069
2070   ret  = g_array_new (TRUE, TRUE, sizeof (gchar *));
2071
2072   append_const_val (ret, "Accessible");
2073   if (atspi_accessible_is_action (obj))
2074     append_const_val (ret, "Action");
2075   if (atspi_accessible_is_collection (obj))
2076     append_const_val (ret, "Collection");
2077   if (atspi_accessible_is_component (obj))
2078     append_const_val (ret, "Component");
2079   if (atspi_accessible_is_document (obj))
2080     append_const_val (ret, "Document");
2081   if (atspi_accessible_is_editable_text (obj))
2082     append_const_val (ret, "EditableText");
2083   if (atspi_accessible_is_hypertext (obj))
2084     append_const_val (ret, "Hypertext");
2085   if (atspi_accessible_is_hyperlink (obj))
2086     append_const_val (ret, "Hyperlink");
2087   if (atspi_accessible_is_image (obj))
2088     append_const_val (ret, "Image");
2089   if (atspi_accessible_is_selection (obj))
2090     append_const_val (ret, "Selection");
2091   if (atspi_accessible_is_table (obj))
2092     append_const_val (ret, "Table");
2093   if (atspi_accessible_is_table_cell (obj))
2094     append_const_val (ret, "TableCell");
2095   if (atspi_accessible_is_text (obj))
2096     append_const_val (ret, "Text");
2097   if (atspi_accessible_is_value (obj))
2098     append_const_val (ret, "Value");
2099
2100   return ret;
2101 }
2102
2103 AtspiAccessible *
2104 _atspi_accessible_new (AtspiApplication *app, const gchar *path)
2105 {
2106   AtspiAccessible *accessible;
2107
2108   accessible = g_object_new (ATSPI_TYPE_ACCESSIBLE, NULL);
2109   g_return_val_if_fail (accessible != NULL, NULL);
2110
2111   accessible->parent.app = g_object_ref (app);
2112   accessible->parent.path = g_strdup (path);
2113
2114   return accessible;
2115 }
2116
2117 /**
2118  * atspi_accessible_set_cache_mask:
2119  * @accessible: The #AtspiAccessible to operate on.  Must be the desktop or
2120  *             the root of an application.
2121  * @mask: An #AtspiCache specifying a bit mask of the types of data to cache.
2122  *
2123  * Sets the type of data to cache for accessibles.
2124  * If this is not set for an application or is reset to ATSPI_CACHE_UNDEFINED,
2125  * then the desktop's cache flag will be used.
2126  * If the desktop's cache flag is also undefined, then all possible data will
2127  * be cached.
2128  * This function is intended to work around bugs in toolkits where the proper
2129  * events are not raised / to aid in testing for such bugs.
2130  **/
2131 void
2132 atspi_accessible_set_cache_mask (AtspiAccessible *accessible, AtspiCache mask)
2133 {
2134   g_return_if_fail (accessible != NULL);
2135   g_return_if_fail (accessible->parent.app != NULL);
2136   g_return_if_fail (accessible == accessible->parent.app->root);
2137   accessible->parent.app->cache = mask;
2138   enable_caching = TRUE;
2139 }
2140
2141 /**
2142  * atspi_accessible_clear_cache:
2143  * @obj: The #AtspiAccessible whose cache to clear.
2144  *
2145  * Clears the cached information for the given accessible and all of its
2146  * descendants.
2147  */
2148 void
2149 atspi_accessible_clear_cache (AtspiAccessible *obj)
2150 {
2151   gint i;
2152
2153   if (obj)
2154   {
2155     obj->cached_properties = ATSPI_CACHE_NONE;
2156     if (obj->children)
2157       for (i = 0; i < obj->children->len; i++)
2158         atspi_accessible_clear_cache (g_ptr_array_index (obj->children, i));
2159   }
2160 }
2161
2162 /**
2163  * atspi_accessible_get_process_id:
2164  * @accessible: The #AtspiAccessible to query.
2165  * @error: a pointer to a %NULL #GError pointer
2166  *
2167  * Returns the process id associated with the given accessible.  Mainly
2168  * added for debugging; it is a shortcut to explicitly querying the
2169  * accessible's app->bus_name and then calling GetConnectionUnixProcessID.
2170  *
2171  * Returns: The process ID or undetermined value if @error is set.
2172  **/
2173 guint
2174 atspi_accessible_get_process_id (AtspiAccessible *accessible, GError **error)
2175 {
2176   DBusMessage *message, *reply;
2177   DBusConnection *bus = _atspi_bus ();
2178   dbus_uint32_t pid = -1;
2179   DBusError d_error;
2180
2181   if (!accessible->parent.app || !accessible->parent.app->bus_name)
2182     {
2183       g_set_error_literal(error, ATSPI_ERROR, ATSPI_ERROR_IPC, "Process is defunct");
2184       return -1;
2185     }
2186
2187   message = dbus_message_new_method_call ("org.freedesktop.DBus",
2188                                           "/org/freedesktop/DBus",
2189                                           "org.freedesktop.DBus",
2190                                           "GetConnectionUnixProcessID");
2191   dbus_message_append_args (message, DBUS_TYPE_STRING,
2192                             &accessible->parent.app->bus_name,
2193                             DBUS_TYPE_INVALID);
2194   dbus_error_init (&d_error);
2195   reply = dbus_connection_send_with_reply_and_block (bus, message, -1, &d_error);
2196   dbus_message_unref (message);
2197   if (reply)
2198   {
2199     if (!strcmp (dbus_message_get_signature (reply), "u"))
2200       dbus_message_get_args (reply, NULL, DBUS_TYPE_UINT32, &pid, DBUS_TYPE_INVALID);
2201     dbus_message_unref (reply);
2202   }
2203   if (dbus_error_is_set (&d_error))
2204     {
2205       g_set_error_literal(error, ATSPI_ERROR, ATSPI_ERROR_IPC, "Process is defunct");
2206       dbus_error_free (&d_error);
2207     }
2208   return pid;
2209 }
2210
2211 AtspiCache
2212 _atspi_accessible_get_cache_mask (AtspiAccessible *accessible)
2213 {
2214   AtspiCache mask;
2215
2216   if (!accessible->parent.app)
2217     return ATSPI_CACHE_NONE;
2218
2219  mask = accessible->parent.app->cache;
2220   if (mask == ATSPI_CACHE_UNDEFINED &&
2221       accessible->parent.app->root &&
2222       accessible->parent.app->root->accessible_parent)
2223   {
2224     AtspiAccessible *desktop = atspi_get_desktop (0);
2225     mask = desktop->parent.app->cache;
2226     g_object_unref (desktop);
2227   }
2228
2229   if (mask == ATSPI_CACHE_UNDEFINED)
2230     mask = ATSPI_CACHE_DEFAULT;
2231
2232   return mask;
2233 }
2234
2235 gboolean
2236 _atspi_accessible_test_cache (AtspiAccessible *accessible, AtspiCache flag)
2237 {
2238   AtspiCache mask = _atspi_accessible_get_cache_mask (accessible);
2239   AtspiCache result = accessible->cached_properties & mask & flag;
2240   if (accessible->states && atspi_state_set_contains (accessible->states, ATSPI_STATE_TRANSIENT))
2241     return FALSE;
2242   return (result != 0 && (atspi_main_loop || enable_caching ||
2243                           flag == ATSPI_CACHE_INTERFACES) &&
2244           !atspi_no_cache);
2245 }
2246
2247 void
2248 _atspi_accessible_add_cache (AtspiAccessible *accessible, AtspiCache flag)
2249 {
2250   AtspiCache mask = _atspi_accessible_get_cache_mask (accessible);
2251
2252   accessible->cached_properties |= flag & mask;
2253 }
2254
2255 /**
2256  * atspi_accessible_get_locale:
2257  * @accessible: an #AtspiAccessible
2258  *
2259  * Gets a UTF-8 string indicating the POSIX-style LC_MESSAGES locale
2260  * of @accessible.
2261  *
2262  * Since: 2.7.91
2263  *
2264  * Returns: a UTF-8 string indicating the POSIX-style LC_MESSAGES
2265  *          locale of @accessible.
2266  **/
2267 const gchar*
2268 atspi_accessible_get_object_locale (AtspiAccessible *accessible, GError **error)
2269 {
2270   gchar *locale;
2271
2272   g_return_val_if_fail (accessible != NULL, NULL);
2273
2274   locale = g_object_get_qdata (G_OBJECT (accessible), quark_locale);
2275   if (!locale)
2276   {
2277     if (!_atspi_dbus_get_property (accessible, atspi_interface_accessible,
2278                                    "Locale", error, "s", &locale))
2279       return NULL;
2280     if (locale)
2281       g_object_set_qdata_full (G_OBJECT (accessible), quark_locale, locale,
2282                                g_free);
2283   }
2284   return locale;
2285 }
2286
2287 void
2288 free_value (gpointer data)
2289 {
2290   GValue *value = data;
2291
2292   g_value_unset (value);
2293   g_free (value);
2294 }
2295
2296 GHashTable *
2297 _atspi_accessible_ref_cache (AtspiAccessible *accessible)
2298 {
2299   AtspiAccessiblePrivate *priv = accessible->priv;
2300
2301   priv->cache_ref_count++;
2302   if (priv->cache)
2303     return g_hash_table_ref (priv->cache);
2304   priv->cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
2305                                        free_value);
2306   return priv->cache;
2307 }
2308
2309 void
2310 _atspi_accessible_unref_cache (AtspiAccessible *accessible)
2311 {
2312   AtspiAccessiblePrivate *priv = accessible->priv;
2313
2314   if (priv->cache)
2315   {
2316     g_hash_table_unref (priv->cache);
2317     if (--priv->cache_ref_count == 0)
2318       priv->cache = NULL;
2319   }
2320 }