--- /dev/null
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <Efl.h>
+#include <Eina.h>
+#include <Eo.h>
+#include <Ecore.h>
+
+#include "ecore_internal.h"
+
+#include "efl_model_composite_private.h"
+
+typedef struct _Efl_Model_View_Data Efl_Model_View_Data;
+typedef struct _Efl_Model_View_Bind Efl_Model_View_Bind;
+typedef struct _Efl_Model_View_Logic Efl_Model_View_Logic;
+typedef struct _Efl_Model_View_Property_Ref Efl_Model_View_Property_Ref;
+
+struct _Efl_Model_View_Data
+{
+ // FIXME: If parent is set, always access parent... recursively?
+ Efl_Model_View_Data *parent;
+
+ Eina_Hash *bound; // Stringhash of Efl_Model_View_Bind
+ Eina_Hash *logics; // Stringhash of Efl_Model_View_Logic
+
+ Eina_Hash *deduplication; // Stringhash of Efl_Model_View_Property_Ref
+
+ struct {
+ Eina_Bool property_changed : 1;
+ Eina_Bool child_added : 1;
+ Eina_Bool child_removed : 1;
+ } propagating; // Boolean to prevent reentrance event emission on the same object
+ Eina_Bool finalized : 1;
+ Eina_Bool children_bind : 1; // Define if child object should be automatically binded
+};
+
+struct _Efl_Model_View_Bind
+{
+ Eina_Stringshare *source;
+ Eina_List *destinations;
+};
+
+struct _Efl_Model_View_Logic
+{
+ struct {
+ EflModelViewPropertyGet fct;
+ Eina_Free_Cb free_cb;
+ void *data;
+ } get;
+ struct {
+ EflModelViewPropertySet fct;
+ Eina_Free_Cb free_cb;
+ void *data;
+ } set;
+
+ Efl_Object *object;
+ Eina_List *sources;
+ Eina_Stringshare *property;
+};
+
+struct _Efl_Model_View_Property_Ref
+{
+ EINA_REFCOUNT;
+ Eina_Stringshare *property;
+};
+
+static void
+_ref_free(void *data)
+{
+ Efl_Model_View_Property_Ref *r = data;
+
+ eina_stringshare_del(r->property);
+ free(r);
+}
+
+static void
+_ref_add(Efl_Model_View_Data *pd, Eina_Stringshare *property)
+{
+ Efl_Model_View_Property_Ref *r;
+
+ r = eina_hash_find(pd->deduplication, property);
+ if (!r)
+ {
+ r = calloc(1, sizeof (Efl_Model_View_Property_Ref));
+ if (!r) return ;
+ r->property = eina_stringshare_ref(property);
+
+ eina_hash_direct_add(pd->deduplication, r->property, r);
+ }
+
+ EINA_REFCOUNT_REF(r);
+}
+
+static void
+_ref_del(Efl_Model_View_Data *pd, Eina_Stringshare *property)
+{
+ Efl_Model_View_Property_Ref *r;
+
+ r = eina_hash_find(pd->deduplication, property);
+ if (!r) return ;
+
+ EINA_REFCOUNT_UNREF(r)
+ eina_hash_del(pd->deduplication, property, r);
+}
+
+static void
+_logic_free(void *data)
+{
+ Efl_Model_View_Logic *logic = data;
+ Eina_Stringshare *source;
+
+ logic->get.free_cb(logic->get.data);
+ logic->set.free_cb(logic->set.data);
+ EINA_LIST_FREE(logic->sources, source)
+ {
+ efl_model_view_property_unbind(logic->object, source, logic->property);
+ eina_stringshare_del(source);
+ }
+ eina_stringshare_del(logic->property);
+ free(logic);
+}
+
+static Eina_Error
+_efl_model_view_property_logic_add(Eo *obj, Efl_Model_View_Data *pd,
+ const char *property,
+ void *get_data, EflModelViewPropertyGet get, Eina_Free_Cb get_free_cb,
+ void *set_data, EflModelViewPropertySet set, Eina_Free_Cb set_free_cb,
+ Eina_Iterator *bound)
+{
+ Efl_Model_View_Logic *logic;
+ Eina_Stringshare *prop;
+ const char *source;
+
+ prop = eina_stringshare_add(property);
+
+ if (eina_hash_find(pd->logics, prop))
+ {
+ eina_stringshare_del(prop);
+ return EFL_MODEL_ERROR_INCORRECT_VALUE;
+ }
+
+ logic = calloc(1, sizeof (Efl_Model_View_Logic));
+ if (!logic) return ENOMEM;
+
+ logic->object = obj;
+ logic->property = prop;
+ logic->get.fct = get;
+ logic->get.free_cb = get_free_cb;
+ logic->get.data = get_data;
+ logic->set.fct = set;
+ logic->set.free_cb = set_free_cb;
+ logic->set.data = set_data;
+
+ eina_hash_direct_add(pd->logics, prop, logic);
+
+ EINA_ITERATOR_FOREACH(bound, source)
+ {
+ logic->sources = eina_list_append(logic->sources, eina_stringshare_add(source));
+ efl_model_view_property_bind(obj, source, property);
+ }
+
+ return 0;
+}
+
+static Eina_Error
+_efl_model_view_property_logic_del(Eo *obj, Efl_Model_View_Data *pd,
+ const char *property)
+{
+ Efl_Model_View_Logic *logic;
+
+ logic = eina_hash_find(pd->logics, property);
+ if (!logic) return EFL_MODEL_ERROR_INCORRECT_VALUE;
+ eina_hash_del(pd->logics, property, logic);
+ return 0;
+}
+
+static void
+_efl_model_view_property_bind(Eo *obj, Efl_Model_View_Data *pd,
+ const char *source, const char *destination)
+{
+ Efl_Model_View_Bind *bind;
+ Eina_Stringshare *src;
+ Eina_Stringshare *dst;
+
+ if (!source || !destination) return ;
+
+ src = eina_stringshare_add(source);
+ bind = eina_hash_find(pd->bound, src);
+ if (!bind)
+ {
+ bind = calloc(1, sizeof (Efl_Model_View_Bind));
+ if (!bind) goto on_error;
+ bind->source = eina_stringshare_ref(src);
+
+ eina_hash_direct_add(pd->bound, bind->source, bind);
+ }
+
+ dst = eina_stringshare_add(destination);
+ bind->destinations = eina_list_append(bind->destinations, dst);
+ _ref_add(pd, dst);
+
+ on_error:
+ eina_stringshare_del(src);
+}
+
+static void
+_efl_model_view_property_unbind(Eo *obj, Efl_Model_View_Data *pd,
+ const char *source, const char *destination)
+{
+ Efl_Model_View_Bind *bind;
+ Eina_Stringshare *src;
+ Eina_Stringshare *dst;
+ Eina_Stringshare *cmp;
+ Eina_List *l;
+
+ if (!source || !destination) return ;
+ src = eina_stringshare_add(source);
+ bind = eina_hash_find(pd->bound, src);
+ if (!bind) goto on_error;
+
+ dst = eina_stringshare_add(destination);
+
+ EINA_LIST_FOREACH(bind->destinations, l, cmp)
+ if (cmp == dst)
+ {
+ bind->destinations = eina_list_remove_list(bind->destinations, l);
+ break;
+ }
+
+ if (!bind->destinations)
+ eina_hash_del(pd->bound, dst, bind);
+
+ _ref_del(pd, dst);
+ eina_stringshare_del(dst);
+
+ on_error:
+ eina_stringshare_del(src);
+}
+
+static void
+_bind_free(void *data)
+{
+ Efl_Model_View_Bind *bind = data;
+ Eina_Stringshare *dst;
+
+ eina_stringshare_del(bind->source);
+
+ EINA_LIST_FREE(bind->destinations, dst)
+ eina_stringshare_del(dst);
+
+ free(bind);
+}
+
+static Efl_Model_View_Bind *
+_efl_model_view_property_bind_lookup(Efl_Model_View_Data *pd, Eina_Stringshare *src)
+{
+ Efl_Model_View_Bind *bind;
+
+ bind = eina_hash_find(pd->bound, src);
+ if (!bind && pd->parent) return _efl_model_view_property_bind_lookup(pd->parent, src);
+ return bind;
+}
+
+static void
+_efl_model_view_property_changed(void *data, const Efl_Event *event)
+{
+ Efl_Model_View_Data *pd = data;
+ Efl_Model_Property_Event *ev = event->info;
+ Efl_Model_Property_Event nev = { 0 };
+ const char *property;
+ Eina_Stringshare *src;
+ Eina_Array_Iterator iterator;
+ unsigned int i;
+
+ if (pd->propagating.property_changed) return ;
+ pd->propagating.property_changed = EINA_TRUE;
+
+ // Our strategy is to rebuild a new Property_Event and cancel the current one.
+ efl_event_callback_stop(event->object);
+
+ nev.changed_properties = eina_array_new(1);
+
+ EINA_ARRAY_ITER_NEXT(ev->changed_properties, i, property, iterator)
+ {
+ Efl_Model_View_Bind *bind;
+
+ eina_array_push(nev.changed_properties, property);
+
+ src = eina_stringshare_add(property);
+ bind = _efl_model_view_property_bind_lookup(pd, src);
+ if (bind)
+ {
+ Eina_Stringshare *dest;
+ Eina_List *l;
+
+ EINA_LIST_FOREACH(bind->destinations, l, dest)
+ eina_array_push(nev.changed_properties, dest);
+ }
+ }
+
+ efl_event_callback_call(event->object, EFL_MODEL_EVENT_PROPERTIES_CHANGED, &nev);
+
+ eina_array_free(nev.changed_properties);
+
+ pd->propagating.property_changed = EINA_FALSE;
+}
+
+static void
+_efl_model_view_children_bind_set(Eo *obj, Efl_Model_View_Data *pd, Eina_Bool enable)
+{
+ if (pd->finalized) return;
+
+ pd->children_bind = enable;
+}
+
+static Eina_Bool
+_efl_model_view_children_bind_get(const Eo *obj, Efl_Model_View_Data *pd)
+{
+ return pd->children_bind;
+}
+
+static void
+_efl_model_view_parent_data(Efl_Model_View *child, Efl_Model_View_Data *ppd)
+{
+ Efl_Model_View_Data *cpd;
+
+ cpd = efl_data_scope_get(child, EFL_MODEL_VIEW_CLASS);
+ cpd->parent = ppd;
+ cpd->propagating = ppd->propagating;
+}
+
+static Efl_Model_View *
+_efl_model_view_child_lookup(Efl_Model_View_Data *pd, Efl_Object *parent, Efl_Model *view)
+{
+ Efl_Model_View *co;
+
+ co = efl_key_wref_get(view, "_efl.model_view");
+ if (co) return co;
+
+ co = efl_add(EFL_MODEL_VIEW_CLASS, parent,
+ efl_ui_view_model_set(efl_added, view),
+ _efl_model_view_parent_data(efl_added, pd));
+ if (!co) return NULL;
+
+ efl_key_wref_set(view, "_efl.model_view", co);
+
+ return co;
+}
+
+static void
+_efl_model_view_child_added(void *data, const Efl_Event *event)
+{
+ Efl_Model_Children_Event *ev = event->info;
+ Efl_Model_Children_Event nevt = { 0 };
+ Efl_Model_View_Data *pd = data;
+ Efl_Model_View *co;
+
+ if (pd->propagating.child_added) return ;
+ if (!pd->children_bind) return;
+ if (!ev->child) return;
+
+ pd->propagating.child_added = EINA_TRUE;
+
+ // Our strategy is to rebuild a new Child_Add and cancel the current one.
+ efl_event_callback_stop(event->object);
+
+ co = _efl_model_view_child_lookup(pd, event->object, ev->child);
+ if (!co) return;
+
+ nevt.index = ev->index;
+ nevt.child = co;
+
+ efl_event_callback_call(event->object, EFL_MODEL_EVENT_CHILD_ADDED, &nevt);
+
+ pd->propagating.child_added = EINA_FALSE;
+}
+
+static void
+_efl_model_view_child_removed(void *data, const Efl_Event *event)
+{
+ Efl_Model_Children_Event *ev = event->info;
+ Efl_Model_Children_Event nevt = { 0 };
+ Efl_Model_View_Data *pd = data;
+ Efl_Model_View *co;
+
+ if (pd->propagating.child_removed) return ;
+ if (!pd->children_bind) return;
+ if (!ev->child) return;
+
+ pd->propagating.child_removed = EINA_TRUE;
+
+ // Our strategy is to rebuild a new Child_Add and cancel the current one.
+ efl_event_callback_stop(event->object);
+
+ co = _efl_model_view_child_lookup(pd, event->object, ev->child);
+ if (!co) return;
+
+ nevt.index = ev->index;
+ nevt.child = co;
+
+ efl_event_callback_call(event->object, EFL_MODEL_EVENT_CHILD_REMOVED, &nevt);
+
+ // The object is being destroyed, there is no point in us keeping the ModelView proxy alive.
+ efl_del(co);
+
+ pd->propagating.child_removed = EINA_FALSE;
+}
+
+EFL_CALLBACKS_ARRAY_DEFINE(efl_model_view_intercept,
+ { EFL_MODEL_EVENT_PROPERTIES_CHANGED, _efl_model_view_property_changed },
+ { EFL_MODEL_EVENT_CHILD_ADDED, _efl_model_view_child_added },
+ { EFL_MODEL_EVENT_CHILD_REMOVED, _efl_model_view_child_removed })
+
+static Efl_Object *
+_efl_model_view_efl_object_constructor(Eo *obj, Efl_Model_View_Data *pd)
+{
+ obj = efl_constructor(efl_super(obj, EFL_MODEL_VIEW_CLASS));
+
+ pd->children_bind = EINA_TRUE;
+ pd->bound = eina_hash_stringshared_new(_bind_free);
+ pd->logics = eina_hash_stringshared_new(_logic_free);
+ pd->deduplication = eina_hash_stringshared_new(_ref_free);
+
+ efl_event_callback_array_priority_add(obj, efl_model_view_intercept(), EFL_CALLBACK_PRIORITY_BEFORE, pd);
+
+ return obj;
+}
+
+static Efl_Object *
+_efl_model_view_efl_object_finalize(Eo *obj, Efl_Model_View_Data *pd)
+{
+ pd->finalized = EINA_TRUE;
+
+ return efl_finalize(efl_super(obj, EFL_MODEL_VIEW_CLASS));
+}
+
+static void
+_efl_model_view_efl_object_destructor(Eo *obj, Efl_Model_View_Data *pd)
+{
+ efl_event_callback_array_del(obj, efl_model_view_intercept(), pd);
+
+ eina_hash_free(pd->bound);
+ pd->bound = NULL;
+
+ eina_hash_free(pd->logics);
+ pd->logics = NULL;
+
+ efl_destructor(efl_super(obj, EFL_MODEL_VIEW_CLASS));
+}
+
+static Efl_Model_View_Logic *
+_efl_model_view_property_logic_lookup(Efl_Model_View_Data *pd, Eina_Stringshare *property)
+{
+ Efl_Model_View_Logic *logic;
+
+ if (!pd) return NULL;
+ logic = eina_hash_find(pd->logics, property);
+ if (!logic) return _efl_model_view_property_logic_lookup(pd->parent, property);
+ return logic;
+}
+
+static Eina_Future *
+_efl_model_view_efl_model_property_set(Eo *obj, Efl_Model_View_Data *pd,
+ const char *property, Eina_Value *value)
+{
+ Efl_Model_View_Logic *logic;
+ Eina_Stringshare *prop;
+ Eina_Future *f;
+
+ prop = eina_stringshare_add(property);
+ logic = _efl_model_view_property_logic_lookup(pd, prop);
+ if (logic)
+ f = logic->set.fct(logic->get.data, obj, prop, value);
+ else
+ f = efl_model_property_set(efl_super(obj, EFL_MODEL_VIEW_CLASS), property, value);
+
+ eina_stringshare_del(prop);
+ return f;
+}
+
+static Eina_Value *
+_efl_model_view_efl_model_property_get(const Eo *obj, Efl_Model_View_Data *pd,
+ const char *property)
+{
+ Efl_Model_View_Logic *logic;
+ Eina_Stringshare *prop;
+ Eina_Value *r;
+
+ prop = eina_stringshare_add(property);
+ logic = _efl_model_view_property_logic_lookup(pd, prop);
+ if (logic)
+ r = logic->get.fct(logic->get.data, obj, prop);
+ else
+ r = efl_model_property_get(efl_super(obj, EFL_MODEL_VIEW_CLASS), property);
+
+ eina_stringshare_del(prop);
+ return r;
+}
+
+static Eina_Iterator *
+_efl_model_view_efl_model_properties_get(const Eo *obj, Efl_Model_View_Data *pd)
+{
+ EFL_MODEL_COMPOSITE_PROPERTIES_SUPER(props, obj, EFL_MODEL_VIEW_CLASS,
+ eina_hash_iterator_key_new(pd->deduplication));
+
+ return props;
+}
+
+typedef struct _Efl_Model_View_Slice_Request Efl_Model_View_Slice_Request;
+struct _Efl_Model_View_Slice_Request
+{
+ Efl_Model_View_Data *pd;
+ unsigned int start;
+};
+
+static Eina_Value
+_efl_model_view_slice_then(Eo *o, void *data, const Eina_Value v)
+{
+ Efl_Model_View_Slice_Request *req = data;
+ Eo *target;
+ Eina_Value r = EINA_VALUE_EMPTY;
+ unsigned int i, len;
+
+ eina_value_array_setup(&r, EINA_VALUE_TYPE_OBJECT, 4);
+
+ EINA_VALUE_ARRAY_FOREACH(&v, len, i, target)
+ {
+ Eo *composite;
+
+ composite = _efl_model_view_child_lookup(req->pd, o, target);
+ eina_value_array_append(&r, composite);
+ }
+
+ return r;
+}
+
+static void
+_efl_model_view_slice_clean(Eo *o EINA_UNUSED, void *data, const Eina_Future *dead_future EINA_UNUSED)
+{
+ free(data);
+}
+
+static Eina_Future *
+_efl_model_view_efl_model_children_slice_get(Eo *obj, Efl_Model_View_Data *pd,
+ unsigned int start, unsigned int count)
+{
+ Efl_Model_View_Slice_Request *req;
+ Eina_Future *f;
+
+ f = efl_model_children_slice_get(efl_super(obj, EFL_MODEL_VIEW_CLASS), start, count);
+
+ req = malloc(sizeof (Efl_Model_View_Slice_Request));
+ if (!req)
+ {
+ eina_future_cancel(f);
+ return efl_loop_future_rejected(obj, ENOMEM);
+ }
+
+ req->pd = pd;
+ req->start = start;
+
+ return efl_future_then(obj, f, .success_type = EINA_VALUE_TYPE_ARRAY,
+ .success = _efl_model_view_slice_then,
+ .free = _efl_model_view_slice_clean,
+ .data = req);
+}
+
+#include "efl_model_view.eo.c"