atspi: add new relationship append/remove/clear API
authorLukasz Stanislawski <lukasz.stanislawski@gmail.com>
Sun, 4 Oct 2015 20:30:31 +0000 (22:30 +0200)
committerLukasz Stanislawski <l.stanislaws@samsung.com>
Mon, 5 Oct 2015 06:58:12 +0000 (08:58 +0200)
Allow to add some extra contextul information about accessibility objects
which can be used by Assistive Technology to provide better user experience.

API is still marked as beta.

@feature

src/lib/elm_atspi_bridge.c
src/lib/elm_interface_atspi_accessible.c
src/lib/elm_interface_atspi_accessible.eo
src/lib/elm_interface_atspi_accessible.h
src/lib/elm_widget.c
src/lib/elm_widget.eo

index 93411535089f506f0c99b3e47e9722cdd7e660f8..b4056553aa3d3c59058e3178874f9c3040ec849f 100644 (file)
@@ -786,11 +786,12 @@ _accessible_get_relation_set(const Eldbus_Service_Interface *iface EINA_UNUSED,
 {
    const char *obj_path = eldbus_message_path_get(msg);
    Eo *bridge = eldbus_service_object_data_get(iface, ELM_ATSPI_BRIDGE_CLASS_NAME);
-   Eo *obj = _bridge_object_from_path(bridge, obj_path);
+   Eo *rel_obj, *obj = _bridge_object_from_path(bridge, obj_path);
    Eldbus_Message *ret = NULL;
    Eldbus_Message_Iter *iter = NULL, *iter_array = NULL, *iter_array2 = NULL, *iter_struct;
    Elm_Atspi_Relation *rel;
-   Eina_List *rels;
+   Eina_List *l, *l2;
+   Elm_Atspi_Relation_Set rels;
 
    ELM_ATSPI_OBJ_CHECK_OR_RETURN_DBUS_ERROR(obj, ELM_INTERFACE_ATSPI_ACCESSIBLE_MIXIN, msg);
 
@@ -803,17 +804,18 @@ _accessible_get_relation_set(const Eldbus_Service_Interface *iface EINA_UNUSED,
 
    eo_do(obj, rels = elm_interface_atspi_accessible_relation_set_get());
 
-   EINA_LIST_FREE(rels, rel)
+   EINA_LIST_FOREACH(rels, l, rel)
      {
         iter_struct = eldbus_message_iter_container_new(iter_array, 'r', NULL);
         eldbus_message_iter_basic_append(iter_struct, 'u', _elm_relation_to_atspi_relation(rel->type));
         iter_array2 = eldbus_message_iter_container_new(iter_struct, 'a', "(so)");
         EINA_SAFETY_ON_NULL_GOTO(iter_array2, fail);
-        _bridge_iter_object_reference_append(bridge, iter_array2, rel->obj);
+        EINA_LIST_FOREACH(rel->objects, l2, rel_obj)
+           _bridge_iter_object_reference_append(bridge, iter_array2, rel_obj);
         eldbus_message_iter_container_close(iter_struct, iter_array2);
         eldbus_message_iter_container_close(iter_array, iter_struct);
-        free(rel);
      }
+   elm_atspi_relation_set_free(rels);
    eldbus_message_iter_container_close(iter, iter_array);
 
    return ret;
index 709d142ff0646fe8d03895bea5d9ba8cbec07ee8..0c0a213fcd0a97ea007441c701d0c671a80ff4c5 100644 (file)
@@ -127,6 +127,7 @@ struct _Elm_Interface_Atspi_Accessible_Data
    const char    *name;
    const char    *description;
    const char    *translation_domain;
+   Elm_Atspi_Relation_Set relations;
    Elm_Interface_Atspi_Accessible *parent;
 };
 
@@ -290,12 +291,10 @@ _elm_interface_atspi_accessible_state_set_get(Eo *obj EINA_UNUSED, Elm_Interface
    return 0;
 }
 
-EOLIAN Eina_List*
+EOLIAN Elm_Atspi_Relation_Set
 _elm_interface_atspi_accessible_relation_set_get(Eo *obj EINA_UNUSED, Elm_Interface_Atspi_Accessible_Data *pd EINA_UNUSED)
 {
-   WRN("The %s object does not implement the \"accessible_relation_set\" function.",
-       eo_class_name_get(eo_class_get(obj)));
-   return NULL;
+   return elm_atspi_relation_set_clone(pd->relations);
 }
 
 EAPI void elm_atspi_attributes_list_free(Eina_List *list)
@@ -369,4 +368,177 @@ _elm_interface_atspi_accessible_translation_domain_get(Eo *obj EINA_UNUSED, Elm_
    return pd->translation_domain;
 }
 
+EAPI void
+elm_atspi_relation_free(Elm_Atspi_Relation *relation)
+{
+   eina_list_free(relation->objects);
+   free(relation);
+}
+
+EAPI Elm_Atspi_Relation *
+elm_atspi_relation_clone(const Elm_Atspi_Relation *relation)
+{
+   Elm_Atspi_Relation *ret = calloc(sizeof(Elm_Atspi_Relation), 1);
+   if (!ret) return NULL;
+
+   ret->type = relation->type;
+   ret->objects = eina_list_clone(relation->objects);
+   return ret;
+}
+
+static Eina_Bool
+_on_rel_obj_del(void *data, Eo *obj, const Eo_Event_Description *desc EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+   Elm_Atspi_Relation_Set *set = data;
+   Elm_Atspi_Relation *rel;
+   Eina_List *l, *l2, *p, *p2;
+   Eo *rel_obj;
+
+   EINA_LIST_FOREACH_SAFE(*set, l, l2, rel)
+     {
+        EINA_LIST_FOREACH_SAFE(rel->objects, p, p2, rel_obj)
+          {
+             if (rel_obj == obj)
+               rel->objects = eina_list_remove_list(rel->objects, p);
+          }
+        if (!rel->objects)
+          {
+             *set = eina_list_remove_list(*set, l);
+             free(rel);
+          }
+     }
+   return EINA_TRUE;
+}
+
+EAPI Eina_Bool
+elm_atspi_relation_set_relation_append(Elm_Atspi_Relation_Set *set, Elm_Atspi_Relation_Type type, const Eo *rel_obj)
+{
+   Elm_Atspi_Relation *rel;
+   Eina_List *l;
+
+   if (!eo_isa(rel_obj, ELM_INTERFACE_ATSPI_ACCESSIBLE_MIXIN))
+     return EINA_FALSE;
+
+   EINA_LIST_FOREACH(*set, l, rel)
+     {
+        if (rel->type == type)
+          {
+             if (!eina_list_data_find(rel->objects, rel_obj))
+               {
+                  rel->objects = eina_list_append(rel->objects, rel_obj);
+                  eo_do(rel_obj, eo_event_callback_add(EO_BASE_EVENT_DEL, _on_rel_obj_del, set));
+               }
+             return EINA_TRUE;
+          }
+     }
+
+   rel = calloc(sizeof(Elm_Atspi_Relation), 1);
+   if (!rel) return EINA_FALSE;
+
+   rel->type = type;
+   rel->objects = eina_list_append(rel->objects, rel_obj);
+   *set = eina_list_append(*set, rel);
+
+   eo_do(rel_obj, eo_event_callback_add(EO_BASE_EVENT_DEL, _on_rel_obj_del, set));
+   return EINA_TRUE;
+}
+
+EAPI void
+elm_atspi_relation_set_relation_remove(Elm_Atspi_Relation_Set *set, Elm_Atspi_Relation_Type type, const Eo *rel_obj)
+{
+   Eina_List *l;
+   Elm_Atspi_Relation *rel;
+
+   EINA_LIST_FOREACH(*set, l, rel)
+     {
+        if (rel->type == type)
+          {
+             if (eina_list_data_find(rel->objects, rel_obj))
+               {
+                  eo_do(rel_obj, eo_event_callback_del(EO_BASE_EVENT_DEL, _on_rel_obj_del, set));
+                  rel->objects = eina_list_remove(rel->objects, rel_obj);
+               }
+             if (!rel->objects)
+               {
+                  *set = eina_list_remove(*set, rel);
+                  elm_atspi_relation_free(rel);
+               }
+             return;
+          }
+     }
+}
+
+EAPI void
+elm_atspi_relation_set_relation_type_remove(Elm_Atspi_Relation_Set *set, Elm_Atspi_Relation_Type type)
+{
+   Eina_List *l;
+   Elm_Atspi_Relation *rel;
+   Eo *obj;
+
+   EINA_LIST_FOREACH(*set, l, rel)
+     {
+        if (rel->type == type)
+          {
+             EINA_LIST_FOREACH(rel->objects, l, obj)
+                eo_do(obj, eo_event_callback_del(EO_BASE_EVENT_DEL, _on_rel_obj_del, set));
+             *set = eina_list_remove(*set, rel);
+             elm_atspi_relation_free(rel);
+             return;
+          }
+     }
+}
+
+EAPI void
+elm_atspi_relation_set_free(Elm_Atspi_Relation_Set set)
+{
+   Elm_Atspi_Relation *rel;
+   Eina_List *l;
+   Eo *obj;
+
+   EINA_LIST_FREE(set, rel)
+     {
+        EINA_LIST_FOREACH(rel->objects, l, obj)
+           eo_do(obj, eo_event_callback_del(EO_BASE_EVENT_DEL, _on_rel_obj_del, set));
+        elm_atspi_relation_free(rel);
+     }
+}
+
+EAPI Elm_Atspi_Relation_Set
+elm_atspi_relation_set_clone(const Elm_Atspi_Relation_Set set)
+{
+   Elm_Atspi_Relation_Set ret = NULL;
+   Eina_List *l;
+   Elm_Atspi_Relation *rel;
+
+   EINA_LIST_FOREACH(set, l, rel)
+     {
+        Elm_Atspi_Relation *cpy = elm_atspi_relation_clone(rel);
+        ret = eina_list_append(ret, cpy);
+     }
+
+   return ret;
+}
+
+EOLIAN static Eina_Bool
+_elm_interface_atspi_accessible_relationship_append(Eo *obj EINA_UNUSED, Elm_Interface_Atspi_Accessible_Data *sd, Elm_Atspi_Relation_Type type, const Elm_Interface_Atspi_Accessible *relation_obj)
+{
+   return elm_atspi_relation_set_relation_append(&sd->relations, type, relation_obj);
+}
+
+EOLIAN static void
+_elm_interface_atspi_accessible_relationship_remove(Eo *obj EINA_UNUSED, Elm_Interface_Atspi_Accessible_Data *sd, Elm_Atspi_Relation_Type type, const Elm_Interface_Atspi_Accessible *relation_obj)
+{
+   if (relation_obj)
+     elm_atspi_relation_set_relation_remove(&sd->relations, type, relation_obj);
+   else
+     elm_atspi_relation_set_relation_type_remove(&sd->relations, type);
+}
+
+EOLIAN static void
+_elm_interface_atspi_accessible_relationships_clear(Eo *obj EINA_UNUSED, Elm_Interface_Atspi_Accessible_Data *sd)
+{
+   elm_atspi_relation_set_free(sd->relations);
+   sd->relations = NULL;
+}
+
 #include "elm_interface_atspi_accessible.eo.c"
index f6a4ed7e2b5e5ef104619b989bb3dadde5d79fb2..65b6b95c974155149349574547778abffa475738 100644 (file)
@@ -27,7 +27,7 @@ mixin Elm_Interface_Atspi_Accessible ()
             [[Gets an string describing ATSPI widget role name. Lists and elements Should be free by a user.]]
          }
          values {
-            relations: own(list<own(Elm_Atspi_Relation *)> *);
+            relations: Elm_Atspi_Relation_Set;
          }
       }
       @property role @protected {
@@ -147,6 +147,42 @@ mixin Elm_Interface_Atspi_Accessible ()
             domain: const(char)*; [[ translation domain ]]
          }
       }
+      relationship_append @protected {
+         [[Defines the relationship between two accessible objects.
+
+           Adds unique relation between source object and relation_object of a
+           given type.
+
+           Relationships can be queried by Assistive Technology clients to
+           provide customized feedback, improving overall user experience.
+
+           Relationship_append API is asymmetric, which means that
+           appending, for example, relation ELM_ATSPI_RELATION_FLOWS_TO from object A to B,
+           do NOT append relation ELM_ATSPI_RELATION_FLOWS_FROM from object B to
+           object A.
+
+           return: EINA_TRUE is relationship was successfully appended, EINA_FALSE
+           otherwise]]
+         return: bool;
+         params {
+            @in type: Elm_Atspi_Relation_Type;
+            @in relation_object: const(Elm_Interface_Atspi_Accessible)*;
+         }
+      }
+      relationship_remove @protected {
+         [[Removes the relationship between two accessible objects.
+
+          If relation_object is NULL function removes all relations
+          of given type.
+         ]]
+         params {
+            @in type: Elm_Atspi_Relation_Type;
+            @in relation_object: const(Elm_Interface_Atspi_Accessible)*;
+         }
+      }
+      relationships_clear @protected {
+         [[Removes all relationships in accessible object.]]
+      }
    }
    events {
       property,changed: const(char)*;
index 0012caa41e1a0bdbb290c27350ea3414cb8ce740..8ebe244a1024508271841bd33fd2a3b7766f32be 100644 (file)
@@ -252,7 +252,7 @@ typedef struct _Elm_Atspi_Attribute Elm_Atspi_Attribute;
 struct _Elm_Atspi_Relation
 {
    Elm_Atspi_Relation_Type type;
-   const Eo *obj;
+   Eina_List *objects;
 };
 
 typedef struct _Elm_Atspi_Relation Elm_Atspi_Relation;
@@ -262,6 +262,43 @@ typedef struct _Elm_Atspi_Relation Elm_Atspi_Relation;
  */
 EAPI void elm_atspi_attributes_list_free(Eina_List *list);
 
+typedef Eina_List *Elm_Atspi_Relation_Set;
+
+/**
+ * Frees relation.
+ */
+EAPI void elm_atspi_relation_free(Elm_Atspi_Relation *relation);
+
+/**
+ * Clones relation.
+ */
+EAPI Elm_Atspi_Relation * elm_atspi_relation_clone(const Elm_Atspi_Relation *relation);
+
+/**
+ * Appends relation to relation set
+ */
+EAPI Eina_Bool elm_atspi_relation_set_relation_append(Elm_Atspi_Relation_Set *set, Elm_Atspi_Relation_Type type, const Eo *rel_obj);
+
+/**
+ * Removes relation from relation set
+ */
+EAPI void elm_atspi_relation_set_relation_remove(Elm_Atspi_Relation_Set *set, Elm_Atspi_Relation_Type type, const Eo *rel_obj);
+
+/**
+ * Removes all relation from relation set of a given type
+ */
+EAPI void elm_atspi_relation_set_relation_type_remove(Elm_Atspi_Relation_Set *set, Elm_Atspi_Relation_Type type);
+
+/**
+ * Frees Elm_Atspi_Relation_Set
+ */
+EAPI void elm_atspi_relation_set_free(Elm_Atspi_Relation_Set set);
+
+/**
+ * Clones Elm_Atspi_Relation_Set
+ */
+EAPI Elm_Atspi_Relation_Set elm_atspi_relation_set_clone(const Elm_Atspi_Relation_Set set);
+
 #ifdef EFL_EO_API_SUPPORT
 
 /**
index 73d024a532a442d84677d001c4034bd36a5f3814..4a9022f78b0d44f7339f92dc948f75be9b4d65a7 100644 (file)
@@ -4431,9 +4431,12 @@ _elm_widget_item_eo_base_destructor(Eo *eo_item, Elm_Widget_Item_Data *item)
      }
    eina_hash_free(item->labels);
 
-   eo_do(eo_item, elm_interface_atspi_accessible_description_set(NULL));
-   eo_do(eo_item, elm_interface_atspi_accessible_name_set(NULL));
-   eo_do(eo_item, elm_interface_atspi_accessible_translation_domain_set(NULL));
+   eo_do(eo_item,
+         elm_interface_atspi_accessible_description_set(NULL),
+         elm_interface_atspi_accessible_name_set(NULL),
+         elm_interface_atspi_accessible_translation_domain_set(NULL),
+         elm_interface_atspi_accessible_relationships_clear()
+         );
 
    if (_elm_config->atspi_mode && item->widget)
      elm_interface_atspi_accessible_children_changed_del_signal_emit(item->widget, eo_item);
@@ -5706,9 +5709,12 @@ _elm_widget_eo_base_constructor(Eo *obj, Elm_Widget_Smart_Data *sd EINA_UNUSED)
 EOLIAN static void
 _elm_widget_eo_base_destructor(Eo *obj, Elm_Widget_Smart_Data *sd EINA_UNUSED)
 {
-   eo_do(obj, elm_interface_atspi_accessible_description_set(NULL));
-   eo_do(obj, elm_interface_atspi_accessible_name_set(NULL));
-   eo_do(obj, elm_interface_atspi_accessible_translation_domain_set(NULL));
+   eo_do(obj,
+         elm_interface_atspi_accessible_description_set(NULL),
+         elm_interface_atspi_accessible_name_set(NULL),
+         elm_interface_atspi_accessible_translation_domain_set(NULL),
+         elm_interface_atspi_accessible_relationships_clear()
+         );
    elm_interface_atspi_accessible_removed(obj);
 
    eo_do_super(obj, ELM_WIDGET_CLASS, eo_destructor());
@@ -5881,42 +5887,6 @@ _elm_widget_elm_interface_atspi_accessible_attributes_get(Eo *obj, Elm_Widget_Sm
    return ret;
 }
 
-static Elm_Atspi_Relation*
-_relation_new(Elm_Atspi_Relation_Type type, Eo *obj)
-{
-   Elm_Atspi_Relation *rel = calloc(1, sizeof(Elm_Atspi_Relation));
-   if (!rel) return NULL;
-
-   rel->type = type;
-   rel->obj = obj;
-
-   return rel;
-}
-
-EOLIAN static Eina_List*
-_elm_widget_elm_interface_atspi_accessible_relation_set_get(Eo *obj, Elm_Widget_Smart_Data *pd EINA_UNUSED)
-{
-   Eina_List *list = NULL;
-   Elm_Atspi_Relation *rel;
-   Evas_Object *rel_obj;
-
-   rel_obj = elm_object_focus_next_object_get(obj, ELM_FOCUS_NEXT);
-   if (eo_isa(rel_obj, ELM_INTERFACE_ATSPI_ACCESSIBLE_MIXIN))
-     {
-        rel = _relation_new(ELM_ATSPI_RELATION_FLOWS_TO, rel_obj);
-        list = eina_list_append(list, rel);
-     }
-
-   rel_obj = elm_object_focus_next_object_get(obj, ELM_FOCUS_PREVIOUS);
-   if (eo_isa(rel_obj, ELM_INTERFACE_ATSPI_ACCESSIBLE_MIXIN))
-     {
-        rel = _relation_new(ELM_ATSPI_RELATION_FLOWS_FROM, rel_obj);
-        list = eina_list_append(list, rel);
-     }
-
-   return list;
-}
-
 EOLIAN static void
 _elm_widget_item_elm_interface_atspi_component_extents_get(Eo *obj EINA_UNUSED, Elm_Widget_Item_Data *sd EINA_UNUSED, Eina_Bool screen_coords, int *x, int *y, int *w, int *h)
 {
index f53d2c478a7ef6048bb7a06479525dc0b9a9cc16..35bed55f1d06d6cc90db8ec0f478ed47713b3190 100644 (file)
@@ -771,7 +771,6 @@ abstract Elm.Widget (Evas.Object_Smart, Elm_Interface_Atspi_Accessible, Elm_Inte
       Elm_Interface_Atspi_Accessible.children.get;
       Elm_Interface_Atspi_Accessible.parent.get;
       Elm_Interface_Atspi_Accessible.attributes.get;
-      Elm_Interface_Atspi_Accessible.relation_set.get;
       Elm_Interface_Atspi_Component.focus_grab;
    }
    events {