forked from enlightenment/efl
537 lines
15 KiB
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"
|