* Copyright 2010, 2011 Novell, Inc.
*
* This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
+ * modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
+ * version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
*
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
*/
#include "atspi-private.h"
#include "atspi-accessible-private.h"
#include <string.h>
+enum {
+ REGION_CHANGED,
+ LAST_SIGNAL
+};
+
static gboolean enable_caching = FALSE;
static guint quark_locale;
+static guint atspi_accessible_signals[LAST_SIGNAL] = { 0, };
+
+static gboolean
+screen_reader_signal_watcher (GSignalInvocationHint *signal_hint,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer data)
+{
+ GObject *object;
+ AtspiAccessible *accessible;
+ GSignalQuery signal_query;
+ const char *name;
+ DBusMessage *signal;
+ DBusMessageIter iter, iter_struct, iter_variant, iter_array;
+ dbus_int32_t detail1, detail2;
+ const char *detail = "";
+
+ object = g_value_get_object (param_values + 0);
+ g_return_val_if_fail (ATSPI_IS_ACCESSIBLE(object), FALSE);
+
+ g_signal_query (signal_hint->signal_id, &signal_query);
+ name = signal_query.signal_name;
+ detail1 = g_value_get_int (param_values + 1);
+ detail2 = g_value_get_int (param_values + 2);
+ accessible = ATSPI_ACCESSIBLE (object);
+
+ signal = dbus_message_new_signal (ATSPI_DBUS_PATH_SCREEN_READER,
+ ATSPI_DBUS_INTERFACE_EVENT_SCREEN_READER,
+ "RegionChanged");
+ dbus_message_iter_init_append (signal, &iter);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &detail);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &detail1);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &detail2);
+ dbus_message_iter_open_container (&iter, DBUS_TYPE_VARIANT, "(so)",
+ &iter_variant);
+ dbus_message_iter_open_container (&iter_variant, DBUS_TYPE_STRUCT, NULL,
+ &iter_struct);
+ dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &accessible->parent.app->bus_name);
+ dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_OBJECT_PATH, &accessible->parent.path);
+ dbus_message_iter_close_container (&iter_variant, &iter_struct);
+ dbus_message_iter_close_container (&iter, &iter_variant);
+ dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "{sv}",
+ &iter_array);
+ dbus_message_iter_close_container (&iter, &iter_array);
+ dbus_connection_send (_atspi_bus (), signal, NULL);
+ dbus_message_unref (signal);
+ return TRUE;
+}
+
static void
atspi_action_interface_init (AtspiAction *action)
{
#endif
static void
+atspi_accessible_unref (GObject *accessible)
+{
+ if (accessible != NULL)
+ g_object_unref(accessible);
+}
+
+static void
atspi_accessible_init (AtspiAccessible *accessible)
{
#ifdef DEBUG_REF_COUNTS
#endif
accessible->priv = atspi_accessible_get_instance_private (accessible);
+
+ accessible->children = g_ptr_array_new_with_free_func (atspi_accessible_unref);
}
static void
AtspiAccessible *accessible = ATSPI_ACCESSIBLE (object);
AtspiEvent e;
AtspiAccessible *parent;
- GList *children;
- GList *l;
+ gint i;
/* TODO: Only fire if object not already marked defunct */
memset (&e, 0, sizeof (e));
e.detail2 = 0;
_atspi_send_event (&e);
- if (accessible->states)
- {
- g_object_unref (accessible->states);
- accessible->states = NULL;
- }
+ g_clear_object (&accessible->states);
parent = accessible->accessible_parent;
- if (parent && parent->children)
- {
- GList*ls = g_list_find (parent->children, accessible);
- if(ls)
- {
- gboolean replace = (ls == parent->children);
- ls = g_list_remove (ls, accessible);
- if (replace)
- parent->children = ls;
- g_object_unref (object);
- }
- }
-
if (parent)
{
- g_object_unref (parent);
accessible->accessible_parent = NULL;
+ if (parent->children)
+ g_ptr_array_remove (parent->children, accessible);
+ g_object_unref (parent);
}
- children = accessible->children;
- accessible->children = NULL;
- for (l = children; l; l = l->next)
+ if (accessible->children) for (i = accessible->children->len - 1; i >= 0; i--)
{
- AtspiAccessible *child = l->data;
+ AtspiAccessible *child = g_ptr_array_index (accessible->children, i);
if (child && child->accessible_parent == accessible)
{
- g_object_unref (accessible);
child->accessible_parent = NULL;
+ g_object_unref (accessible);
}
- g_object_unref (child);
}
- g_list_free (children);
+
+ if (accessible->children)
+ {
+ g_ptr_array_free (accessible->children, TRUE);
+ accessible->children = NULL;
+ }
G_OBJECT_CLASS (atspi_accessible_parent_class) ->dispose (object);
}
{
AtspiAccessible *accessible = ATSPI_ACCESSIBLE (object);
- g_free (accessible->description);
- g_free (accessible->name);
+ g_free (accessible->description);
+ g_free (accessible->name);
+
if (accessible->attributes)
g_hash_table_unref (accessible->attributes);
- if (accessible->priv->cache)
- g_hash_table_destroy (accessible->priv->cache);
+ if (accessible->priv->cache)
+ g_hash_table_destroy (accessible->priv->cache);
#ifdef DEBUG_REF_COUNTS
accessible_count--;
g_print ("at-spi: finalize: %d objects\n", accessible_count);
#endif
- G_OBJECT_CLASS (atspi_accessible_parent_class)
- ->finalize (object);
+ G_OBJECT_CLASS (atspi_accessible_parent_class)->finalize (object);
+
+ /* TODO: remove emission hook */
}
static void
object_class->finalize = atspi_accessible_finalize;
quark_locale = g_quark_from_string ("accessible-locale");
+
+ /**
+ * AtspiAccessible::region-changed:
+ * @atspiaccessible: the object which received the signal
+ * @arg1: an integer specifying the current offset of the text being read,
+ * if the object is textual.
+ * @arg2: an integer specifying the ending offset of the text being read,
+ * if the object is textual.
+ *
+ * The signal "region-changed" is emitted by a screen reader to indicate
+ * that it is now reading or tracking a new object, or, a new piece of
+ * text within an object. This allows a magnifier to gain the information
+ * needed to highlight the object that the screen reader is reading.
+ */
+ atspi_accessible_signals[REGION_CHANGED] =
+ g_signal_new ("region_changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (AtspiAccessibleClass, region_changed),
+ NULL, NULL,
+ atspi_marshal_VOID__INT_INT,
+ G_TYPE_NONE,
+ 2, G_TYPE_INT, G_TYPE_INT);
+
+ g_signal_add_emission_hook (atspi_accessible_signals[REGION_CHANGED], 0,
+ screen_reader_signal_watcher, NULL,
+ (GDestroyNotify) NULL);
}
/**
*
* Gets the name of an #AtspiAccessible object.
*
- * Returns: a UTF-8 string indicating the name of the #AtspiAccessible object
+ * Returns: a UTF-8 string indicating the name of the #AtspiAccessible object
* or NULL on exception.
**/
gchar *
return g_strdup (obj->name);
}
+
+/**
+ * atspi_accessible_get_unique_id:
+ * @obj: a pointer to the #AtspiAccessible object on which to operate.
+ *
+ * Gets the identificator, uniquely identifying object, or NULL if an error occured.
+ *
+ * Returns: a UTF-8 string describing the #AtspiAccessible object
+ * or NULL on exception or NULL object passed.
+ **/
+gchar *
+atspi_accessible_get_unique_id(AtspiAccessible *obj, GError **error)
+{
+ if (!obj) {
+ g_set_error(error, ATSPI_ERROR, ATSPI_ERROR_IPC, "argument is null");
+ return NULL;
+ }
+
+ gchar *id = NULL;
+ gchar *bus_name = atspi_accessible_get_bus_name(obj, error);
+ if (bus_name && bus_name[0]) {
+ gchar *path = atspi_accessible_get_path(obj, error);
+ if (path && path[0])
+ id = g_strdup_printf("%s:%s", bus_name, path);
+ else
+ g_set_error(error, ATSPI_ERROR, ATSPI_ERROR_IPC, "failed to get path");
+ g_free(path);
+ }
+ else
+ g_set_error(error, ATSPI_ERROR, ATSPI_ERROR_IPC, "failed to get bus name");
+ g_free(bus_name);
+ return id;
+}
+
+/**
+ * atspi_accessible_get_bus_name:
+ * @obj: a pointer to the #AtspiAccessible object on which to operate.
+ *
+ * Gets the bus name, where object belongs.
+ *
+ * Returns: a UTF-8 string describing the #AtspiAccessible object's
+ * bus name or empty string on exception or NULL object passed.
+ **/
+gchar *
+atspi_accessible_get_bus_name(AtspiAccessible *obj, GError **error)
+{
+ if (!obj || !obj->parent.app)
+ return g_strdup("");
+ return g_strdup (obj->parent.app->bus_name);
+}
+
/**
* atspi_accessible_get_path:
* @obj: a pointer to the #AtspiAccessible object on which to operate.
*
- * Gets the path, uniquely identifying object.
+ * Gets the path, uniquely identifying object over its bus name.
*
* Returns: a UTF-8 string describing the #AtspiAccessible object
* or empty string on exception or NULL object passed.
}
/**
+ * atspi_accessible_get_navigable_at_point:
+ * @root: a pointer to the #AtspiAccessible to start search from.
+ * @x: a #gint specifying the x coordinate of the point in question.
+ * @y: a #gint specifying the y coordinate of the point in question.
+ * @ctype: the coordinate system of the point (@x, @y)
+ * (e.g. ATSPI_COORD_TYPE_WINDOW, ATSPI_COORD_TYPE_SCREEN).
+ *
+ * Finds the accessible element closest to user (highest in z-order), at a given coordinate within an #AtspiAccessible.
+ * This should be the element, that should be picked, when doing mouse click or finger tap at given coordinates.
+ *
+ * Returns: (nullable) (transfer full): a pointer to an
+ * #AtspiAccessible descendant (of any depth) of the specified component which
+ * contains the point (@x, @y), or NULL if no descendant contains
+ * the point.
+ **/
+AtspiAccessible *
+atspi_accessible_get_navigable_at_point (AtspiAccessible *root,
+ gint x,
+ gint y,
+ AtspiCoordType ctype, GError **error)
+{
+ dbus_int32_t d_x = x, d_y = y;
+ dbus_uint32_t d_ctype = ctype;
+ DBusMessage *reply;
+ AtspiAccessible *return_value = NULL;
+ unsigned char recurse = 0;
+ DBusMessageIter iter;
+ AtspiAccessible *deputy = NULL;
+
+ g_return_val_if_fail (root != NULL, NULL);
+ do {
+ reply = _atspi_dbus_call_partial (root, atspi_interface_accessible, "GetNavigableAtPoint", error, "iiu", d_x, d_y, d_ctype);
+ // call failed, error is set, so we bail out
+ if (!reply) {
+ if (deputy) g_object_unref(deputy);
+ if (return_value) g_object_unref(return_value);
+ return NULL;
+ }
+ _ATSPI_DBUS_CHECK_SIG (reply, "(so)y(so)", NULL, NULL);
+
+ dbus_message_iter_init (reply, &iter);
+ AtspiAccessible *tmp = _atspi_dbus_return_accessible_from_iter (&iter);
+
+ unsigned char value = 0;
+ dbus_message_iter_get_basic (&iter, &value);
+ dbus_message_iter_next (&iter);
+ recurse = (value != 0);
+
+ /* keep deputy if tmp has deputy */
+ if (!deputy)
+ deputy = _atspi_dbus_return_accessible_from_iter (&iter);
+
+ dbus_message_unref(reply);
+
+ if (!tmp) {
+ if (deputy) {
+ /* TODO: need to check deputy works for return value */
+ if (return_value)
+ g_object_unref(return_value);
+ return deputy;
+ }
+ break;
+ }
+
+ if (return_value)
+ g_object_unref(return_value);
+ return_value = root = tmp;
+ } while(recurse);
+ return return_value;
+}
+
+/**
+ * atspi_accessible_get_reading_material:
+ * @obj: a pointer to the #AtspiAccessible object on which to operate.
+ *
+ * Gets reading material
+ *
+ * Returns: reading material to be used screen-reader side. This is not stable.
+ * You have to handle all alocated memory as below on screen-reader side.
+ *
+ * AtspiAccessibleReadingMaterial *rm
+ * g_object_unref(rm->parent);
+ * g_object_unref(rm->described_by_accessible);
+ * g_hash_table_unref(rm->attributes);
+ * free(rm->name);
+ * free(rm->labeled_by_name);
+ * free(rm->text_interface_name);
+ * free(rm->localized_role_name);
+ * free(rm->description);
+ * free(rm);
+ **/
+AtspiAccessibleReadingMaterial *
+atspi_accessible_get_reading_material (AtspiAccessible *obj, GError **error)
+{
+ AtspiAccessible *parent;
+ AtspiAccessibleReadingMaterial *reading_material = NULL;
+ const char *name;
+ double current_value;
+ gint count;
+ guint64 val;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter iter_array;
+ dbus_uint32_t role;
+ dbus_uint32_t *states;
+ dbus_int32_t index_in_parent;
+ dbus_int32_t child_count;
+ dbus_bool_t is_selected;
+
+ g_return_val_if_fail (obj != NULL, NULL);
+
+ reply = _atspi_dbus_call_partial (obj, atspi_interface_accessible, "GetReadingMaterial", error, "");
+
+ _ATSPI_DBUS_CHECK_SIG (reply, "a{ss}sssuausiddddsibbii(so)auiui(so)", NULL, NULL);
+
+ reading_material = calloc(1, sizeof(AtspiAccessibleReadingMaterial));
+ if (!reading_material)
+ {
+ return reading_material;
+ }
+
+ dbus_message_iter_init (reply, &iter);
+
+ /* get attributes */
+ reading_material->attributes = _atspi_dbus_hash_from_iter (&iter);
+ dbus_message_iter_next (&iter);
+
+ /* get name */
+ dbus_message_iter_get_basic (&iter, &name);
+ reading_material->name = g_strdup (name);
+ dbus_message_iter_next (&iter);
+
+ /* get name of relation LABELED_BY */
+ dbus_message_iter_get_basic (&iter, &name);
+ reading_material->labeled_by_name = g_strdup (name);
+ dbus_message_iter_next (&iter);
+
+ /* get name of text interface */
+ dbus_message_iter_get_basic (&iter, &name);
+ reading_material->text_interface_name = g_strdup (name);
+ dbus_message_iter_next (&iter);
+
+ /* get role */
+ dbus_message_iter_get_basic (&iter, &role);
+ reading_material->role = role;
+ dbus_message_iter_next (&iter);
+
+ /* get state set */
+ dbus_message_iter_recurse (&iter, &iter_array);
+ dbus_message_iter_get_fixed_array (&iter_array, &states, &count);
+ val = ((guint64)states [1]) << 32;
+ val += states [0];
+ reading_material->states = val;
+ dbus_message_iter_next (&iter);
+
+ /* get localized role name */
+ dbus_message_iter_get_basic (&iter, &name);
+ reading_material->localized_role_name = g_strdup (name);
+ dbus_message_iter_next (&iter);
+
+ /* get child count */
+ dbus_message_iter_get_basic (&iter, &child_count);
+ reading_material->child_count = child_count;
+ dbus_message_iter_next (&iter);
+
+ /* get current value */
+ dbus_message_iter_get_basic (&iter, ¤t_value);
+ reading_material->value = current_value;
+ dbus_message_iter_next (&iter);
+
+ /* get minimum increment */
+ dbus_message_iter_get_basic (&iter, ¤t_value);
+ reading_material->increment = current_value;
+ dbus_message_iter_next (&iter);
+
+ /* get maximum value */
+ dbus_message_iter_get_basic (&iter, ¤t_value);
+ reading_material->upper = current_value;
+ dbus_message_iter_next (&iter);
+
+ /* get minimum value */
+ dbus_message_iter_get_basic (&iter, ¤t_value);
+ reading_material->lower = current_value;
+ dbus_message_iter_next (&iter);
+
+ /* get description */
+ dbus_message_iter_get_basic (&iter, &name);
+ reading_material->description = g_strdup (name);
+ dbus_message_iter_next (&iter);
+
+ /* get index in parent */
+ dbus_message_iter_get_basic (&iter, &index_in_parent);
+ reading_material->index_in_parent = index_in_parent;
+ dbus_message_iter_next (&iter);
+
+ /* get selected in parent */
+ dbus_message_iter_get_basic (&iter, &is_selected);
+ reading_material->is_selected_in_parent = is_selected;
+ dbus_message_iter_next (&iter);
+
+ /* get has checkbox child */
+ dbus_message_iter_get_basic (&iter, &is_selected);
+ reading_material->has_checkbox_child = is_selected;
+ dbus_message_iter_next (&iter);
+
+ /* get list children count */
+ dbus_message_iter_get_basic (&iter, &child_count);
+ reading_material->list_children_count = child_count;
+ dbus_message_iter_next (&iter);
+
+ /* get first selected child index */
+ dbus_message_iter_get_basic (&iter, &index_in_parent);
+ reading_material->first_selected_child_index = index_in_parent;
+ dbus_message_iter_next (&iter);
+
+ ////////////////
+ /* get parent */
+ parent = _atspi_dbus_return_accessible_from_iter (&iter);
+ reading_material->parent = parent;
+
+ /* parent states */
+ dbus_message_iter_recurse (&iter, &iter_array);
+ dbus_message_iter_get_fixed_array (&iter_array, &states, &count);
+ val = ((guint64)states [1]) << 32;
+ val += states [0];
+ reading_material->parent_states = val;
+ dbus_message_iter_next (&iter);
+
+ /* get parent child count */
+ dbus_message_iter_get_basic (&iter, &child_count);
+ reading_material->parent_child_count = child_count;
+ dbus_message_iter_next (&iter);
+
+ /* get parent role */
+ dbus_message_iter_get_basic (&iter, &role);
+ reading_material->parent_role = role;
+ dbus_message_iter_next (&iter);
+
+ /* get parent selected child count */
+ dbus_message_iter_get_basic (&iter, &child_count);
+ reading_material->parent_selected_child_count = child_count;
+ dbus_message_iter_next (&iter);
+
+ ////////////////////////////////////////
+ /* get relation object - DESCRIBED_BY */
+ parent = _atspi_dbus_return_accessible_from_iter (&iter);
+ reading_material->described_by_accessible = parent;
+
+ return reading_material;
+}
+
+/**
+ * atspi_accessible_get_default_label_info:
+ * @obj: a pointer to the #AtspiAccessible object would be window.
+ *
+ * Gets default label information
+ *
+ * Returns: default label information to be used screen-reader side.
+ * This is not stable. And this depends on toolkit side UI definition.
+ * The candidate of default label object could be changed by UI definition.
+ * You have to handle all alocated memory as below on screen-reader side.
+ *
+ * AtspiAccessibleDefaultLabelInfo *dli
+ * g_hash_table_unref(dli->attributes);
+
+ * g_object_unref(dli->obj);
+ * free(dli);
+ **/
+AtspiAccessibleDefaultLabelInfo *
+atspi_accessible_get_default_label_info (AtspiAccessible *obj, GError **error)
+{
+ AtspiAccessibleDefaultLabelInfo *default_label_info = NULL;
+ AtspiAccessible *default_label_object;
+ dbus_uint32_t role;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+
+ g_return_val_if_fail (obj != NULL, NULL);
+
+ reply = _atspi_dbus_call_partial (obj, atspi_interface_accessible, "GetDefaultLabelInfo", error, "");
+
+ _ATSPI_DBUS_CHECK_SIG (reply, "(so)ua{ss}", NULL, NULL);
+
+ default_label_info = calloc(1, sizeof(AtspiAccessibleDefaultLabelInfo));
+ if (!default_label_info)
+ {
+ return default_label_info;
+ }
+
+ dbus_message_iter_init (reply, &iter);
+
+ default_label_object = _atspi_dbus_return_accessible_from_iter (&iter);
+ default_label_info->obj = default_label_object;
+
+ dbus_message_iter_get_basic (&iter, &role);
+ default_label_info->role = role;
+ dbus_message_iter_next (&iter);
+
+ default_label_info->attributes = _atspi_dbus_hash_from_iter (&iter);
+ dbus_message_iter_next (&iter);
+
+ return default_label_info;
+}
+
+static unsigned char are_objects_on_the_same_bus(AtspiAccessible *obj1, AtspiAccessible *obj2)
+{
+ const char *bus_name_1 = obj1->parent.app->bus_name;
+ const char *bus_name_2 = obj2->parent.app->bus_name;
+ return strcmp(bus_name_1, bus_name_2) == 0;
+}
+
+static unsigned char object_is_valid(AtspiAccessible *obj)
+{
+ if (!obj)
+ return 0;
+ AtspiStateSet *ss = atspi_accessible_get_state_set(obj);
+ if (!ss)
+ return 0;
+ unsigned char valid = atspi_state_set_contains(ss, ATSPI_STATE_DEFUNCT) == 0;
+ g_object_unref(ss);
+ return valid;
+}
+
+typedef enum {
+ NEIGHBOR_SEARCH_MODE_NORMAL = 0,
+ NEIGHBOR_SEARCH_MODE_RECURSE_FROM_ROOT = 1,
+ NEIGHBOR_SEARCH_MODE_CONTINUE_AFTER_FAILED_RECURSING = 2,
+ NEIGHBOR_SEARCH_MODE_RECURSE_TO_OUTSIDE = 3,
+} GetNeighborSearchMode;
+/**
+ * atspi_accessible_get_neighbor:
+ * @root: a pointer to a #AtspiAccessible, which represents current root of subtree to search
+ * @start: a pointer to the #AtspiAccessible to start search from (can be null, which means start from root)
+ * @direction: direction, in which search (forward or backward)
+ *
+ * Calculates next (or previous) accessible element in logical order or null if none found.
+ *
+ * Returns: (nullable) (transfer full): a pointer to an
+ * #AtspiAccessible element, which is next (previous) in logical order or null if none found.
+ **/
+AtspiAccessible *
+atspi_accessible_get_neighbor (AtspiAccessible *root,
+ AtspiAccessible *start,
+ AtspiNeighborSearchDirection direction,
+ GError **error)
+{
+ g_return_val_if_fail (object_is_valid(root), NULL);
+ if (!object_is_valid(start))
+ start = root;
+ const char *root_path = ATSPI_OBJECT(root)->path;
+ AtspiAccessible *return_value = NULL;
+ g_object_ref(start);
+ unsigned char recurse;
+ GetNeighborSearchMode search_mode = NEIGHBOR_SEARCH_MODE_NORMAL;
+ GQueue *children_root_stack = g_queue_new();
+ DBusMessageIter iter;
+
+ while(1) {
+ const char *path = are_objects_on_the_same_bus(root, start) ? root_path : "";
+ DBusMessage *reply = _atspi_dbus_call_partial (start, atspi_interface_accessible, "GetNeighbor", error, "sii", path, (int)direction, (int)search_mode);
+ // call failed, error is set, so we bail out
+ if (!reply) break;
+
+ _ATSPI_DBUS_CHECK_SIG (reply, "(so)y", error, NULL);
+ dbus_message_iter_init (reply, &iter);
+ AtspiAccessible *ret = _atspi_dbus_return_accessible_from_iter (&iter);
+
+ unsigned char value = 0;
+ dbus_message_iter_get_basic (&iter, &value);
+ dbus_message_iter_next (&iter);
+ recurse = (value != 0);
+
+ dbus_message_unref(reply);
+
+ // got return value and request for recursive search, it means ret is on another bridge, than start
+ // thus we're recursing. should the recurse failed to find anything it will end with
+ if (ret && recurse) {
+ g_object_unref(G_OBJECT(start));
+ start = ret;
+ g_object_ref(start);
+ if (are_objects_on_the_same_bus(root, ret))
+ {
+ search_mode = NEIGHBOR_SEARCH_MODE_RECURSE_TO_OUTSIDE;
+ }
+ else
+ {
+ g_queue_push_tail(children_root_stack, ret);
+ search_mode = NEIGHBOR_SEARCH_MODE_RECURSE_FROM_ROOT;
+ }
+ continue;
+ }
+ // found the one we've been looking for
+ if (ret) {
+ g_object_unref(G_OBJECT(start));
+ return_value = ret;
+ break;
+ }
+
+ // we've stepped into different bridges previously and now we're going back to the last one
+ // and continuing search where we left
+ if (!g_queue_is_empty(children_root_stack)) {
+ g_object_unref(G_OBJECT(start));
+ start = g_queue_pop_tail(children_root_stack);
+
+ search_mode = NEIGHBOR_SEARCH_MODE_CONTINUE_AFTER_FAILED_RECURSING;
+ continue;
+ }
+ // there's no more bridges to check, but we might have started from one
+ // in that case there might be bridges "below" start, which we yet have to visit
+ if (!are_objects_on_the_same_bus(root, start)) {
+ unsigned char continue_loop = 1;
+ while(continue_loop) {
+ AtspiAccessible *parent = atspi_accessible_get_parent(start, NULL);
+ continue_loop = parent ? are_objects_on_the_same_bus(start, parent) : 0;
+ g_object_unref(G_OBJECT(start));
+ start = parent;
+ }
+
+ // going up thru parents put us in weird place (we didnt meet root on the way)
+ // so we bail out
+ if (!start)
+ break;
+
+ // start object now points to different bridge and must be treated as "resume after recursing"
+ search_mode = NEIGHBOR_SEARCH_MODE_CONTINUE_AFTER_FAILED_RECURSING;
+ continue;
+ }
+
+ // nothing found
+ g_object_unref(start);
+ break;
+ }
+ while(!g_queue_is_empty(children_root_stack))
+ g_object_unref(g_queue_pop_tail(children_root_stack));
+ g_queue_free(children_root_stack);
+
+ return return_value;
+}
+
+/**
* atspi_accessible_get_description:
* @obj: a pointer to the #AtspiAccessible object on which to operate.
*
* Gets the description of an #AtspiAccessible object.
*
- * Returns: a UTF-8 string describing the #AtspiAccessible object
+ * Returns: a UTF-8 string describing the #AtspiAccessible object
* or NULL on exception.
**/
gchar *
{
g_return_val_if_fail (obj != NULL, NULL);
- if (obj->parent.app &&
- !_atspi_accessible_test_cache (obj, ATSPI_CACHE_PARENT))
+ if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_PARENT))
{
DBusMessage *message, *reply;
DBusMessageIter iter, iter_variant;
+ if (!obj->parent.app)
+ return NULL;
message = dbus_message_new_method_call (obj->parent.app->bus_name,
obj->parent.path,
DBUS_INTERFACE_PROPERTIES, "Get");
return ret;
}
- return g_list_length (obj->children);
+ if (!obj->children)
+ return 0; /* assume it's disposed */
+
+ return obj->children->len;
}
/**
GError **error)
{
AtspiAccessible *child;
+ DBusMessage *reply;
g_return_val_if_fail (obj != NULL, NULL);
- if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_CHILDREN))
+ if (_atspi_accessible_test_cache (obj, ATSPI_CACHE_CHILDREN))
{
- DBusMessage *reply;
- reply = _atspi_dbus_call_partial (obj, atspi_interface_accessible,
- "GetChildAtIndex", error, "i",
- child_index);
- return _atspi_dbus_return_accessible_from_message (reply);
+ if (!obj->children)
+ return NULL; /* assume disposed */
+
+ child = g_ptr_array_index (obj->children, child_index);
+ if (child)
+ return g_object_ref (child);
}
- child = g_list_nth_data (obj->children, child_index);
+ reply = _atspi_dbus_call_partial (obj, atspi_interface_accessible,
+ "GetChildAtIndex", error, "i", child_index);
+ child = _atspi_dbus_return_accessible_from_message (reply);
+
if (!child)
return NULL;
- return g_object_ref (child);
+
+ if (_atspi_accessible_test_cache (obj, ATSPI_CACHE_CHILDREN))
+ {
+ if (child_index >= obj->children->len)
+ g_ptr_array_set_size (obj->children, child_index + 1);
+ g_ptr_array_index (obj->children, child_index) = g_object_ref (child);
+ }
+ return child;
}
/**
* atspi_accessible_get_index_in_parent:
* @obj: a pointer to the #AtspiAccessible object on which to operate.
*
- * Gets the index of an #AtspiAccessible object within its parent's
+ * Gets the index of an #AtspiAccessible object within its parent's
* #AtspiAccessible children list.
*
* Returns: a #glong indicating the index of the #AtspiAccessible object
gint
atspi_accessible_get_index_in_parent (AtspiAccessible *obj, GError **error)
{
- GList *l;
gint i = 0;
+ dbus_int32_t ret = -1;
g_return_val_if_fail (obj != NULL, -1);
- if (_atspi_accessible_test_cache (obj, ATSPI_CACHE_PARENT) &&
- !obj->accessible_parent)
- return -1;
- if (!obj->accessible_parent ||
- !_atspi_accessible_test_cache (obj->accessible_parent,
- ATSPI_CACHE_CHILDREN))
+ if (_atspi_accessible_test_cache (obj, ATSPI_CACHE_PARENT))
{
- dbus_int32_t ret = -1;
- _atspi_dbus_call (obj, atspi_interface_accessible,
- "GetIndexInParent", NULL, "=>i", &ret);
- return ret;
- }
+ if (!obj->accessible_parent)
+ return -1;
- l = obj->accessible_parent->children;
- while (l)
- {
- if (l->data == obj) return i;
- l = g_list_next (l);
- i++;
+ if (!_atspi_accessible_test_cache (obj->accessible_parent, ATSPI_CACHE_CHILDREN) || !obj->accessible_parent->children)
+ goto dbus;
+
+ for (i = 0; i < obj->accessible_parent->children->len; i++)
+ if (g_ptr_array_index (obj->accessible_parent->children, i) == obj)
+ return i;
}
- return -1;
+
+dbus:
+ _atspi_dbus_call (obj, atspi_interface_accessible,
+ "GetIndexInParent", NULL, "=>i", &ret);
+ return ret;
}
typedef struct
GArray *
atspi_accessible_get_interfaces (AtspiAccessible *obj)
{
- GArray *ret = g_array_new (TRUE, TRUE, sizeof (gchar *));
+ GArray *ret;
g_return_val_if_fail (obj != NULL, NULL);
+ ret = g_array_new (TRUE, TRUE, sizeof (gchar *));
+
append_const_val (ret, "Accessible");
if (atspi_accessible_is_action (obj))
append_const_val (ret, "Action");
/**
* atspi_accessible_clear_cache:
- * @accessible: The #AtspiAccessible whose cache to clear.
+ * @obj: The #AtspiAccessible whose cache to clear.
*
* Clears the cached information for the given accessible and all of its
* descendants.
*/
void
-atspi_accessible_clear_cache (AtspiAccessible *accessible)
+atspi_accessible_clear_cache (AtspiAccessible *obj)
{
- GList *l;
+ gint i;
- if (accessible)
+ if (obj)
{
- accessible->cached_properties = ATSPI_CACHE_NONE;
- for (l = accessible->children; l; l = l->next)
- atspi_accessible_clear_cache (l->data);
+ obj->cached_properties = ATSPI_CACHE_NONE;
+ if (obj->children)
+ for (i = 0; i < obj->children->len; i++)
+ atspi_accessible_clear_cache (g_ptr_array_index (obj->children, i));
}
}
return locale;
}
+/**
+ * atspi_accessible_get_accessible_id:
+ * @obj: an #AtspiAccessible
+ *
+ * Gets the accessible id of the accessible. This is not meant to be presented
+ * to the user, but to be an id which is stable over application development.
+ * Typically, this is the gtkbuilder id.
+ *
+ * Since: 2.34
+ *
+ * Returns: a character string representing the accessible id of the
+ * #AtspiAccessible object or NULL on exception.
+ **/
+gchar*
+atspi_accessible_get_accessible_id (AtspiAccessible *obj, GError **error)
+{
+ gchar *accessible_id;
+
+ g_return_val_if_fail (obj != NULL, NULL);
+
+ if (!_atspi_dbus_get_property (obj, atspi_interface_accessible,
+ "AccessibleId", error, "s", &accessible_id))
+ return NULL;
+
+ return accessible_id;
+}
+
void
free_value (gpointer data)
{