Add navigation helper functions for screen-reader and friends (part 2) 58/136158/1
authorRadoslaw Cybulski <r.cybulski@partner.samsung.com>
Thu, 9 Mar 2017 17:09:34 +0000 (18:09 +0100)
committerShinwoo Kim <cinoo.kim@samsung.com>
Wed, 28 Jun 2017 08:49:51 +0000 (17:49 +0900)
Change-Id: I67eb6da5262a9de721dc2cb569255079dc221ec9

atspi/atspi-accessible.c
atspi/atspi-accessible.h
atspi/atspi-constants.h

index a579254..71fd816 100644 (file)
@@ -341,7 +341,7 @@ atspi_accessible_get_navigable_at_point (AtspiAccessible *root,
   AtspiAccessible *return_value = NULL;
   unsigned char recurse = 0;
 
-  g_return_val_if_fail (root != NULL, FALSE);
+  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);
 
@@ -354,6 +354,118 @@ atspi_accessible_get_navigable_at_point (AtspiAccessible *root,
   return return_value;
 }
 
+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,
+} 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();
+
+  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);
+    AtspiAccessible *ret = _atspi_dbus_return_accessible_and_recurse_info_from_message (reply, &recurse);
+    // 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));
+      g_queue_push_tail(children_root_stack, ret);
+      start = ret;
+      g_object_ref(start);
+      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);
+    return_value = NULL;
+    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.
index 82b779e..293946b 100644 (file)
@@ -83,6 +83,8 @@ gchar * atspi_accessible_get_unique_id (AtspiAccessible *obj, GError **error);
 
 AtspiAccessible *atspi_accessible_get_navigable_at_point (AtspiAccessible *root, gint x, gint y, AtspiCoordType ctype, GError **error);
 
+AtspiAccessible *atspi_accessible_get_neighbor (AtspiAccessible *root, AtspiAccessible *start, AtspiNeighborSearchDirection direction, GError **error);
+
 AtspiAccessible * atspi_accessible_get_parent (AtspiAccessible *obj, GError **error);
 
 gint atspi_accessible_get_child_count (AtspiAccessible *obj, GError **error);
index dd10679..24f3852 100644 (file)
@@ -136,6 +136,11 @@ typedef enum {
     ATSPI_COORD_TYPE_WINDOW,
 } AtspiCoordType;
 
+typedef enum {
+       ATSPI_NEIGHBOR_SEARCH_FORWARD = 1,
+       ATSPI_NEIGHBOR_SEARCH_BACKWARD = 2,
+} AtspiNeighborSearchDirection;
+
 /**
  * ATSPI_COORD_TYPE_COUNT:
  *