forked from enlightenment/efl
313 lines
8.7 KiB
C
313 lines
8.7 KiB
C
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#include <Efl.h>
|
|
#include <Eina.h>
|
|
#include <Eo.h>
|
|
#include <Ecore.h>
|
|
|
|
#include "ecore_internal.h"
|
|
|
|
typedef struct _Efl_Container_Property_Data Efl_Container_Property_Data;
|
|
typedef struct _Efl_Container_Model_Data Efl_Container_Model_Data;
|
|
|
|
struct _Efl_Container_Property_Data
|
|
{
|
|
Eina_Stringshare *name;
|
|
|
|
const Eina_Value_Type *type;
|
|
Eina_Array *values;
|
|
};
|
|
|
|
struct _Efl_Container_Model_Data
|
|
{
|
|
Efl_Container_Model_Data *parent;
|
|
|
|
Eina_Hash *properties; // All properties value for children
|
|
unsigned int children_count;
|
|
};
|
|
|
|
static void
|
|
_property_values_free(Eina_Array *values)
|
|
{
|
|
Eina_Value *v;
|
|
|
|
while ((v = eina_array_pop(values)))
|
|
eina_value_free(v);
|
|
eina_array_free(values);
|
|
}
|
|
|
|
static void
|
|
_property_data_free_cb(void *data)
|
|
{
|
|
Efl_Container_Property_Data *cpd = data;
|
|
|
|
eina_stringshare_del(cpd->name);
|
|
_property_values_free(cpd->values);
|
|
free(cpd);
|
|
}
|
|
|
|
static Efl_Object *
|
|
_efl_container_model_efl_object_constructor(Eo *obj,
|
|
Efl_Container_Model_Data *sd)
|
|
{
|
|
Eo *parent;
|
|
|
|
obj = efl_constructor(efl_super(obj, EFL_CONTAINER_MODEL_CLASS));
|
|
if (!obj) return NULL;
|
|
|
|
parent = efl_parent_get(obj);
|
|
if (efl_isa(parent, EFL_CONTAINER_MODEL_CLASS))
|
|
sd->parent = efl_data_scope_get(parent, EFL_CONTAINER_MODEL_CLASS);
|
|
|
|
sd->properties = eina_hash_stringshared_new(_property_data_free_cb);
|
|
|
|
return obj;
|
|
}
|
|
|
|
static Efl_Object *
|
|
_efl_container_model_efl_object_finalize(Eo *obj, Efl_Container_Model_Data *sd EINA_UNUSED)
|
|
{
|
|
if (!efl_ui_view_model_get(obj))
|
|
{
|
|
// Add a dummy object as source if there isn't any to allow using this class without source.
|
|
efl_ui_view_model_set(obj, efl_add(EFL_GENERIC_MODEL_CLASS, obj));
|
|
}
|
|
return efl_finalize(efl_super(obj, EFL_CONTAINER_MODEL_CLASS));
|
|
}
|
|
|
|
static void
|
|
_efl_container_model_efl_object_destructor(Eo *obj,
|
|
Efl_Container_Model_Data *sd)
|
|
{
|
|
eina_hash_free(sd->properties);
|
|
|
|
efl_destructor(efl_super(obj, EFL_CONTAINER_MODEL_CLASS));
|
|
}
|
|
|
|
static const Eina_Value_Type *
|
|
_efl_container_model_child_property_value_type_get(Eo *obj EINA_UNUSED,
|
|
Efl_Container_Model_Data *sd,
|
|
const char *property)
|
|
{
|
|
Efl_Container_Property_Data *cpd;
|
|
Eina_Stringshare *key;
|
|
|
|
key = eina_stringshare_add(property);
|
|
cpd = eina_hash_find(sd->properties, key);
|
|
eina_stringshare_del(key);
|
|
|
|
if (!cpd) return NULL;
|
|
return cpd->type;
|
|
}
|
|
|
|
static Eina_Bool
|
|
_efl_container_model_child_property_add(Eo *obj,
|
|
Efl_Container_Model_Data *sd,
|
|
const char *name,
|
|
const Eina_Value_Type *type,
|
|
Eina_Iterator *values)
|
|
{
|
|
Eina_Array *arr = NULL;
|
|
void *original;
|
|
Efl_Container_Property_Data *cpd = NULL;
|
|
unsigned int i;
|
|
Eina_Error err = EFL_MODEL_ERROR_INCORRECT_VALUE;
|
|
|
|
name = eina_stringshare_add(name);
|
|
|
|
if (!type || !values)
|
|
{
|
|
EINA_LOG_WARN("Invalid input data");
|
|
goto on_error;
|
|
}
|
|
|
|
err = ENOMEM;
|
|
arr = eina_array_new(4);
|
|
if (!arr) goto on_error;
|
|
|
|
EINA_ITERATOR_FOREACH(values, original)
|
|
{
|
|
Eina_Value *copy = eina_value_new(type);
|
|
Eina_Bool r;
|
|
|
|
if (type == EINA_VALUE_TYPE_STRINGSHARE ||
|
|
type == EINA_VALUE_TYPE_STRING)
|
|
r = eina_value_set(copy, original);
|
|
else
|
|
r = eina_value_pset(copy, original);
|
|
|
|
if (!r)
|
|
{
|
|
eina_value_free(copy);
|
|
copy = eina_value_error_new(EINA_ERROR_VALUE_FAILED);
|
|
}
|
|
|
|
eina_array_push(arr, copy);
|
|
}
|
|
eina_iterator_free(values);
|
|
|
|
err = EFL_MODEL_ERROR_UNKNOWN;
|
|
|
|
cpd = eina_hash_find(sd->properties, name);
|
|
if (!cpd)
|
|
{
|
|
cpd = calloc(1, sizeof(Efl_Container_Property_Data));
|
|
if (!cpd)
|
|
goto on_error;
|
|
|
|
cpd->name = eina_stringshare_ref(name);
|
|
cpd->type = type;
|
|
cpd->values = arr;
|
|
|
|
if (!eina_hash_direct_add(sd->properties, name, cpd))
|
|
goto on_error;
|
|
}
|
|
else
|
|
{
|
|
_property_values_free(cpd->values);
|
|
|
|
cpd->type = type;
|
|
cpd->values = arr;
|
|
}
|
|
|
|
for (i = sd->children_count; i < eina_array_count(arr); ++i)
|
|
{
|
|
Efl_Model_Children_Event cevt = { 0 };
|
|
|
|
cevt.index = i;
|
|
|
|
efl_event_callback_call(obj, EFL_MODEL_EVENT_CHILD_ADDED, &cevt);
|
|
}
|
|
|
|
if (eina_array_count(arr) > sd->children_count)
|
|
{
|
|
sd->children_count = eina_array_count(arr);
|
|
efl_event_callback_call(obj, EFL_MODEL_EVENT_CHILDREN_COUNT_CHANGED, NULL);
|
|
}
|
|
|
|
eina_stringshare_del(name);
|
|
return EINA_TRUE;
|
|
|
|
on_error:
|
|
eina_stringshare_del(name);
|
|
|
|
if (cpd) free(cpd);
|
|
if (arr) _property_values_free(arr);
|
|
eina_error_set(err);
|
|
return EINA_FALSE;
|
|
}
|
|
|
|
static Eina_Iterator *
|
|
_efl_container_model_efl_model_properties_get(const Eo *obj EINA_UNUSED,
|
|
Efl_Container_Model_Data *sd)
|
|
{
|
|
EFL_COMPOSITE_MODEL_PROPERTIES_SUPER(props,
|
|
obj, EFL_CONTAINER_MODEL_CLASS,
|
|
eina_hash_iterator_key_new(sd->parent->properties));
|
|
return props;
|
|
}
|
|
|
|
static Eina_Future *
|
|
_efl_container_model_efl_model_property_set(Eo *obj,
|
|
Efl_Container_Model_Data *pd,
|
|
const char *property,
|
|
Eina_Value *value)
|
|
{
|
|
if (pd->parent)
|
|
{
|
|
Efl_Container_Property_Data *cpd;
|
|
Eina_Stringshare *name;
|
|
unsigned int index;
|
|
Eina_Value r = EINA_VALUE_EMPTY;
|
|
|
|
name = eina_stringshare_add(property);
|
|
cpd = eina_hash_find(pd->parent->properties, name);
|
|
eina_stringshare_del(name);
|
|
|
|
// Check if this is a property of this object
|
|
if (!cpd) goto not_mine;
|
|
// Check if we can get the expected type for this property
|
|
if (!cpd->values)
|
|
return efl_loop_future_rejected(obj, EFL_MODEL_ERROR_INCORRECT_VALUE);
|
|
|
|
index = efl_composite_model_index_get(obj);
|
|
|
|
// Try converting the type before touching any data inside the Model
|
|
if (eina_value_type_get(value) != cpd->type)
|
|
{
|
|
eina_value_setup(&r, cpd->type);
|
|
|
|
if (!eina_value_convert(value, &r))
|
|
return efl_loop_future_rejected(obj, EFL_MODEL_ERROR_INCORRECT_VALUE);
|
|
}
|
|
else
|
|
{
|
|
eina_value_copy(&r, value);
|
|
}
|
|
|
|
// Expand the array to have enough space for this value if it wasn't already the case
|
|
while (index >= eina_array_count(cpd->values))
|
|
eina_array_push(cpd->values, eina_value_error_new(EFL_MODEL_ERROR_INCORRECT_VALUE));
|
|
|
|
// Clean the previous value and introduce the new one
|
|
eina_value_free(eina_array_data_get(cpd->values, index));
|
|
// Same type as the expected type, we can just replace the value
|
|
eina_array_data_set(cpd->values, index, eina_value_dup(&r));
|
|
|
|
eina_value_free(value);
|
|
return efl_loop_future_resolved(obj, r);
|
|
}
|
|
|
|
not_mine:
|
|
return efl_model_property_set(efl_super(obj, EFL_CONTAINER_MODEL_CLASS), property, value);
|
|
}
|
|
|
|
static Eina_Value *
|
|
_efl_container_model_efl_model_property_get(const Eo *obj,
|
|
Efl_Container_Model_Data *pd,
|
|
const char *property)
|
|
{
|
|
if (pd->parent)
|
|
{
|
|
Efl_Container_Property_Data *cpd;
|
|
Eina_Stringshare *name;
|
|
Eina_Value *copy;
|
|
unsigned int index;
|
|
|
|
name = eina_stringshare_add(property);
|
|
cpd = eina_hash_find(pd->parent->properties, name);
|
|
eina_stringshare_del(name);
|
|
|
|
// Check if this is a property of this object
|
|
if (!cpd) goto not_mine;
|
|
// Check if we can get the expected type for this property
|
|
if (!cpd->values) return eina_value_error_new(EFL_MODEL_ERROR_INCORRECT_VALUE);
|
|
|
|
index = efl_composite_model_index_get(obj);
|
|
|
|
if (index >= eina_array_count(cpd->values))
|
|
return eina_value_error_new(EFL_MODEL_ERROR_INCORRECT_VALUE);
|
|
|
|
copy = eina_value_dup(eina_array_data_get(cpd->values, index));
|
|
|
|
return copy;
|
|
}
|
|
|
|
not_mine:
|
|
return efl_model_property_get(efl_super(obj, EFL_CONTAINER_MODEL_CLASS), property);
|
|
}
|
|
|
|
static unsigned int
|
|
_efl_container_model_efl_model_children_count_get(const Eo *obj EINA_UNUSED, Efl_Container_Model_Data *sd)
|
|
{
|
|
unsigned int pcount;
|
|
|
|
pcount = efl_model_children_count_get(efl_super(obj, EFL_CONTAINER_MODEL_CLASS));
|
|
|
|
return pcount > sd->children_count ? pcount : sd->children_count;
|
|
}
|
|
|
|
#include "efl_container_model.eo.c"
|