efl/src/lib/ecore/efl_filter_model.c

537 lines
15 KiB
C

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <Efl_Core.h>
#include "ecore_internal.h"
typedef struct _Efl_Filter_Model_Mapping Efl_Filter_Model_Mapping;
struct _Efl_Filter_Model_Mapping
{
EINA_RBTREE;
unsigned int original;
unsigned int mapped;
EINA_REFCOUNT;
};
typedef struct _Efl_Filter_Model_Data Efl_Filter_Model_Data;
struct _Efl_Filter_Model_Data
{
Efl_Filter_Model_Mapping *self;
Eina_Rbtree *mapping;
struct {
void *data;
EflFilterModel cb;
Eina_Free_Cb free_cb;
unsigned int count;
} filter;
unsigned int counted;
Eina_Bool counting_started : 1;
Eina_Bool processed : 1;
};
static Eina_Rbtree_Direction
_filter_mapping_cmp_cb(const Eina_Rbtree *left, const Eina_Rbtree *right, void *data EINA_UNUSED)
{
const Efl_Filter_Model_Mapping *l, *r;
l = (const Efl_Filter_Model_Mapping *) left;
r = (const Efl_Filter_Model_Mapping *) right;
if (l->mapped < r->mapped)
return EINA_RBTREE_LEFT;
return EINA_RBTREE_RIGHT;
}
static int
_filter_mapping_looking_cb(const Eina_Rbtree *node, const void *key,
int length EINA_UNUSED, void *data EINA_UNUSED)
{
const Efl_Filter_Model_Mapping *n = (const Efl_Filter_Model_Mapping *) node;
const unsigned int *k = key;
return n->mapped - *k;
}
static void
_efl_filter_model_filter_set(Eo *obj EINA_UNUSED, Efl_Filter_Model_Data *pd,
void *filter_data, EflFilterModel filter, Eina_Free_Cb filter_free_cb)
{
if (pd->filter.cb)
pd->filter.free_cb(pd->filter.data);
pd->filter.data = filter_data;
pd->filter.cb = filter;
pd->filter.free_cb = filter_free_cb;
}
static void
_rbtree_free_cb(Eina_Rbtree *node, void *data EINA_UNUSED)
{
Efl_Filter_Model_Mapping *m = (Efl_Filter_Model_Mapping*) node;
EINA_REFCOUNT_UNREF(m)
free(m);
}
typedef struct _Efl_Filter_Request Efl_Filter_Request;
struct _Efl_Filter_Request
{
Efl_Filter_Model_Data *pd;
Efl_Model *parent;
Efl_Model *child;
unsigned int index;
};
static Efl_Filter_Model *
_efl_filter_lookup(const Efl_Class *klass,
Efl_Model *parent, Efl_Model *view,
Efl_Filter_Model_Mapping *mapping)
{
Efl_Filter_Model *child;
Efl_Filter_Model_Data *cpd;
child = _efl_composite_lookup(klass, parent, view, mapping->mapped);
if (!child) return NULL;
cpd = efl_data_scope_get(child, EFL_FILTER_MODEL_CLASS);
cpd->processed = EINA_TRUE;
cpd->self = mapping;
EINA_REFCOUNT_REF(mapping);
return child;
}
static Eina_Value
_efl_filter_model_filter(Eo *o EINA_UNUSED, void *data, const Eina_Value v)
{
Efl_Filter_Model_Mapping *mapping;
Efl_Filter_Model *child;
Efl_Model_Children_Event cevt = { 0 };
Efl_Filter_Request *r = data;
Eina_Value ret = v;
Eina_Bool result = EINA_FALSE;
if (!eina_value_bool_get(&v, &result)) goto end;
if (!result) goto end;
mapping = calloc(1, sizeof (Efl_Filter_Model_Mapping));
if (!mapping)
{
ret = eina_value_bool_init(EINA_FALSE);
goto end;
}
EINA_REFCOUNT_INIT(mapping);
mapping->original = r->index;
mapping->mapped = r->pd->filter.count++;
r->pd->mapping = eina_rbtree_inline_insert(r->pd->mapping, EINA_RBTREE_GET(mapping),
_filter_mapping_cmp_cb, NULL);
child = _efl_filter_lookup(efl_class_get(r->parent), r->parent, r->child, mapping);
if (!child) goto end;
cevt.index = mapping->mapped;
cevt.child = child;
efl_event_callback_call(r->parent, EFL_MODEL_EVENT_CHILD_ADDED, &cevt);
efl_event_callback_call(r->parent, EFL_MODEL_EVENT_CHILDREN_COUNT_CHANGED, NULL);
efl_unref(cevt.child);
ret = eina_value_bool_init(EINA_TRUE);
end:
return ret;
}
static void
_efl_filter_model_filter_clean(Eo *o EINA_UNUSED, void *data, const Eina_Future *dead_future EINA_UNUSED)
{
Efl_Filter_Request *r = data;
efl_unref(r->parent);
efl_unref(r->child);
free(r);
}
static Eina_Value
_efl_filter_model_child_fetch(Eo *o EINA_UNUSED, void *data, const Eina_Value v)
{
Efl_Filter_Request *r = data;
unsigned int i, len;
Eina_Future *f;
Eo *target = NULL;
// Get the first and only child in the array
EINA_VALUE_ARRAY_FOREACH(&v, len, i, target)
break;
r->child = efl_ref(target);
f = r->pd->filter.cb(r->pd->filter.data, r->parent, r->child);
f = efl_future_then(r->parent, f,
.success = _efl_filter_model_filter,
.success_type = EINA_VALUE_TYPE_BOOL,
.free = _efl_filter_model_filter_clean,
.data = r);
return eina_future_as_value(f);
}
static Eina_Value
_efl_filter_model_child_error(Eo *o, void *data, Eina_Error err)
{
_efl_filter_model_filter_clean(o, data, NULL);
return eina_value_error_init(err);
}
static void
_efl_filter_model_child_added(void *data, const Efl_Event *event)
{
Efl_Filter_Model_Data *pd = data;
Efl_Model_Children_Event *ev = event->info;
Efl_Model *child = ev->child;
Efl_Filter_Request *r;
Eina_Future *f;
if (child)
{
Efl_Filter_Model_Data *cpd = efl_data_scope_get(child, EFL_FILTER_MODEL_CLASS);
if (cpd->processed) return ;
}
r = calloc(1, sizeof (Efl_Filter_Request));
if (!r) return ;
r->pd = pd;
r->index = ev->index;
r->parent = efl_ref(event->object);
if (!child)
{
f = efl_model_children_slice_get(efl_ui_view_model_get(r->parent), r->index, 1);
f = efl_future_then(event->object, f,
.success = _efl_filter_model_child_fetch,
.success_type = EINA_VALUE_TYPE_ARRAY,
.error = _efl_filter_model_child_error,
.data = r);
return ;
}
r->child = efl_ref(child);
f = pd->filter.cb(pd->filter.data, r->parent, r->child);
f = efl_future_then(event->object, f,
.success = _efl_filter_model_filter,
.success_type = EINA_VALUE_TYPE_BOOL,
.free = _efl_filter_model_filter_clean,
.data = r);
efl_event_callback_stop(event->object);
}
static void
_efl_filter_model_child_removed(void *data, const Efl_Event *event)
{
Efl_Filter_Model_Mapping *mapping;
Efl_Filter_Model_Data *pd = data;
Efl_Model_Children_Event *ev = event->info;
unsigned int removed = ev->index;
mapping = (void *)eina_rbtree_inline_lookup(pd->mapping,
&removed, sizeof (uint64_t),
_filter_mapping_looking_cb, NULL);
if (!mapping) return;
pd->mapping = eina_rbtree_inline_remove(pd->mapping, EINA_RBTREE_GET(mapping),
_filter_mapping_cmp_cb, NULL);
EINA_REFCOUNT_UNREF(mapping)
free(mapping);
// Update the tree for the index to reflect the removed child
for (removed++; removed < pd->filter.count; removed++)
{
mapping = (void *)eina_rbtree_inline_lookup(pd->mapping,
&removed, sizeof (uint64_t),
_filter_mapping_looking_cb, NULL);
if (!mapping) continue;
pd->mapping = eina_rbtree_inline_remove(pd->mapping, EINA_RBTREE_GET(mapping),
_filter_mapping_cmp_cb, NULL);
mapping->mapped--;
pd->mapping = eina_rbtree_inline_insert(pd->mapping, EINA_RBTREE_GET(mapping),
_filter_mapping_cmp_cb, NULL);
}
pd->filter.count--;
pd->counted--;
}
EFL_CALLBACKS_ARRAY_DEFINE(filters_callbacks,
{ EFL_MODEL_EVENT_CHILD_ADDED, _efl_filter_model_child_added },
{ EFL_MODEL_EVENT_CHILD_REMOVED, _efl_filter_model_child_removed });
static void
_efl_filter_model_efl_object_destructor(Eo *obj, Efl_Filter_Model_Data *pd)
{
eina_rbtree_delete(pd->mapping, _rbtree_free_cb, NULL);
if (pd->self)
{
EINA_REFCOUNT_UNREF(pd->self)
free(pd->self);
}
pd->self = NULL;
efl_destructor(efl_super(obj, EFL_FILTER_MODEL_CLASS));
}
static Eina_Value
_filter_remove_array(Eo *o EINA_UNUSED, void *data, const Eina_Value v)
{
Efl_Filter_Model_Mapping *mapping = data;
unsigned int i, len;
Eo *target = NULL;
EINA_VALUE_ARRAY_FOREACH(&v, len, i, target)
break;
if (efl_isa(target, EFL_FILTER_MODEL_CLASS))
{
Efl_Filter_Model_Data *pd = efl_data_scope_get(target, EFL_FILTER_MODEL_CLASS);
pd->self = mapping;
EINA_REFCOUNT_REF(pd->self);
}
return eina_value_object_init(target);
}
static Eina_Future *
_efl_filter_model_efl_model_children_slice_get(Eo *obj, Efl_Filter_Model_Data *pd,
unsigned int start, unsigned int count)
{
Efl_Filter_Model_Mapping **mapping = NULL;
Eina_Future **r = NULL;
Eina_Future *f;
unsigned int i;
Eina_Error err = ENOMEM;
if ((uint64_t) start + (uint64_t) count > pd->filter.count)
return efl_loop_future_rejected(obj, EFL_MODEL_ERROR_INCORRECT_VALUE);
if (count == 0)
return efl_loop_future_rejected(obj, EFL_MODEL_ERROR_INCORRECT_VALUE);
r = calloc(1, (count + 1) * sizeof (Eina_Future *));
if (!r) return efl_loop_future_rejected(obj, ENOMEM);
mapping = calloc(count, sizeof (Efl_Filter_Model_Mapping *));
if (!mapping) goto on_error;
for (i = 0; i < count; i++)
{
unsigned int lookup = start + i;
mapping[i] = (void *)eina_rbtree_inline_lookup(pd->mapping,
&lookup, sizeof (unsigned int),
_filter_mapping_looking_cb, NULL);
if (!mapping[i]) goto on_error;
}
for (i = 0; i < count; i++)
{
r[i] = efl_model_children_slice_get(efl_super(obj, EFL_FILTER_MODEL_CLASS),
mapping[i]->original, 1);
r[i] = efl_future_then(obj, r[i], .success_type = EINA_VALUE_TYPE_ARRAY,
.success = _filter_remove_array,
.data = mapping[i]);
if (!r[i]) goto on_error;
}
r[i] = EINA_FUTURE_SENTINEL;
f = efl_future_then(obj, eina_future_all_array(r), .success = _efl_future_all_repack);
free(r);
free(mapping);
return f;
on_error:
free(mapping);
if (r)
for (i = 0; i < count; i ++)
if (r[i]) eina_future_cancel(r[i]);
free(r);
return efl_loop_future_rejected(obj, err);
}
typedef struct _Efl_Filter_Model_Result Efl_Filter_Model_Result;
struct _Efl_Filter_Model_Result
{
Efl_Filter_Model_Data *pd;
unsigned int count;
Efl_Model *targets[1];
};
// This future receive an array of boolean that indicate if a fetched object is to be kept
static Eina_Value
_efl_filter_model_array_result_request(Eo *o EINA_UNUSED, void *data, const Eina_Value v)
{
Efl_Filter_Model_Result *req = data;
Efl_Filter_Model_Data *pd = req->pd;
unsigned int i, len;
Eina_Value request = EINA_VALUE_EMPTY;
unsigned int pcount = pd->filter.count;
EINA_VALUE_ARRAY_FOREACH(&v, len, i, request)
{
Efl_Filter_Model_Mapping *mapping;
Efl_Model_Children_Event cevt = { 0 };
Eina_Bool b;
if (eina_value_type_get(&request) != EINA_VALUE_TYPE_BOOL)
continue ;
if (!eina_value_bool_get(&request, &b)) continue;
if (!b) continue;
mapping = calloc(1, sizeof (Efl_Filter_Model_Mapping));
if (!mapping) continue;
EINA_REFCOUNT_INIT(mapping);
mapping->original = i;
mapping->mapped = pd->filter.count++;
// Insert in tree here
pd->mapping = eina_rbtree_inline_insert(pd->mapping, EINA_RBTREE_GET(mapping),
_filter_mapping_cmp_cb, NULL);
cevt.index = mapping->mapped;
cevt.child = _efl_filter_lookup(efl_class_get(o), o, req->targets[i], mapping);
if (!cevt.child) continue;
efl_event_callback_call(o, EFL_MODEL_EVENT_CHILD_ADDED, &cevt);
efl_unref(cevt.child);
}
if (pcount != pd->filter.count)
efl_event_callback_call(o, EFL_MODEL_EVENT_CHILDREN_COUNT_CHANGED, NULL);
return v;
}
static void
_efl_filter_model_array_result_free(Eo *o EINA_UNUSED, void *data, const Eina_Future *dead_future EINA_UNUSED)
{
Efl_Filter_Model_Result *req = data;
unsigned int i;
for (i = 0; i < req->count; i++)
efl_unref(req->targets[i]);
free(req);
}
// This future receive an array of children object
static Eina_Value
_efl_filter_model_array_fetch(Eo *o, void *data, const Eina_Value v)
{
Efl_Filter_Model_Result *req;
Efl_Filter_Model_Data *pd = data;
unsigned int i, len;
Eo *target = NULL;
Eina_Future **array = NULL;
Eina_Future *r;
Eina_Value res = v;
if (!eina_value_array_count(&v)) return v;
array = malloc((eina_value_array_count(&v) + 1) * sizeof (Eina_Future*));
if (!array) return eina_value_error_init(ENOMEM);
req = malloc(sizeof (Efl_Filter_Model_Result) +
sizeof (Eo*) * (eina_value_array_count(&v) - 1));
if (!req)
{
res = eina_value_error_init(ENOMEM);
goto on_error;
}
req->pd = pd;
req->count = eina_value_array_count(&v);
EINA_VALUE_ARRAY_FOREACH(&v, len, i, target)
{
array[i] = pd->filter.cb(pd->filter.data, o, target);
req->targets[i] = efl_ref(target);
}
array[i] = EINA_FUTURE_SENTINEL;
r = eina_future_all_array(array);
r = efl_future_then(o, r, .success_type = EINA_VALUE_TYPE_ARRAY,
.success = _efl_filter_model_array_result_request,
.free = _efl_filter_model_array_result_free,
.data = req);
res = eina_future_as_value(r);
on_error:
free(array);
return res;
}
static unsigned int
_efl_filter_model_efl_model_children_count_get(const Eo *obj, Efl_Filter_Model_Data *pd)
{
if (!pd->counting_started && pd->filter.cb)
{
pd->counting_started = EINA_TRUE;
// Start watching for children now
efl_event_callback_array_add((Eo *)obj, filters_callbacks(), pd);
// Start counting (which may trigger filter being added asynchronously)
pd->counted = efl_model_children_count_get(efl_super(obj, EFL_FILTER_MODEL_CLASS));
if (pd->counted > 0)
{
Eina_Future *f;
f = efl_model_children_slice_get(efl_ui_view_model_get(obj), 0, pd->counted);
efl_future_then(obj, f, .success_type = EINA_VALUE_TYPE_ARRAY,
.success = _efl_filter_model_array_fetch,
.data = pd);
}
}
return pd->filter.count;
}
static Eina_Value *
_efl_filter_model_efl_model_property_get(const Eo *obj, Efl_Filter_Model_Data *pd,
const char *property)
{
if (pd->self && eina_streq(property, EFL_COMPOSITE_MODEL_CHILD_INDEX))
{
return eina_value_uint64_new(pd->self->mapped);
}
return efl_model_property_get(efl_super(obj, EFL_FILTER_MODEL_CLASS), property);
}
static unsigned int
_efl_filter_model_efl_composite_model_index_get(const Eo *obj, Efl_Filter_Model_Data *pd)
{
if (pd->self) return pd->self->mapped;
return efl_composite_model_index_get(efl_super(obj, EFL_FILTER_MODEL_CLASS));
}
#include "efl_filter_model.eo.c"