--- /dev/null
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <Elementary.h>
+#include "elm_priv.h"
+
+// This class store the size information in a compressed array and unpack it
+// when necessary. It does maintain a cache of up to 3 uncompressed slot.
+// That cache could get dropped when the application is entering the 'pause'
+// state.
+
+#define EFL_UI_MODEL_EXACT_CONTENT 1024
+#define EFL_UI_MODEL_EXACT_CONTENT_LENGTH (EFL_UI_MODEL_EXACT_CONTENT * sizeof (unsigned int))
+
+// For now only vertical logic is implemented. Horizontal list and grid are not supported.
+
+typedef struct _Efl_Ui_Model_Exact_Data Efl_Ui_Model_Exact_Data;
+struct _Efl_Ui_Model_Exact_Data
+{
+ Efl_Ui_Model_Exact_Data *parent;
+
+ struct {
+ Eina_List *width;
+ Eina_List *height;
+ } compressed;
+
+ struct {
+ unsigned int width;
+ unsigned int height;
+ } total_size;
+
+ struct {
+ unsigned int *width;
+ unsigned int *height;
+ unsigned int start_offset;
+ unsigned short usage;
+ Eina_Bool defined : 1;
+ struct {
+ Eina_Bool width : 1;
+ Eina_Bool height : 1;
+ } decompressed;
+ } slot[3];
+};
+
+static Efl_Object *
+_efl_ui_model_exact_efl_object_constructor(Eo *obj, Efl_Ui_Model_Exact_Data *pd)
+{
+ Eo *parent = efl_parent_get(obj);
+
+ if (parent && efl_isa(parent, EFL_UI_MODEL_EXACT_CLASS))
+ pd->parent = efl_data_scope_get(efl_parent_get(obj), EFL_UI_MODEL_EXACT_CLASS);
+
+ return efl_constructor(efl_super(obj, EFL_UI_MODEL_EXACT_CLASS));
+}
+
+static unsigned int
+_efl_ui_model_exact_list_find(unsigned int list_index, Eina_List *start, Eina_List **l)
+{
+ Eina_Binbuf *tbuf;
+
+ EINA_LIST_FOREACH(start, *l, tbuf)
+ {
+ if (list_index == 0) break;
+ list_index--;
+ }
+
+ return list_index;
+}
+
+static Eina_List *
+_efl_ui_model_exact_slot_compress(unsigned int index, Eina_List *compressed, unsigned int *buffer)
+{
+ unsigned int list_index = index / EFL_UI_MODEL_EXACT_CONTENT;
+ static Eina_Binbuf *z = NULL;
+ Eina_Binbuf *cbuf;
+ Eina_Binbuf *tbuf;
+ Eina_List *l = NULL;
+
+ _efl_ui_model_exact_list_find(list_index, compressed, &l);
+
+ tbuf = eina_binbuf_manage_new((unsigned char *) buffer, EFL_UI_MODEL_EXACT_CONTENT_LENGTH, EINA_TRUE);
+ cbuf = emile_compress(tbuf, EMILE_LZ4, EMILE_COMPRESSOR_FAST);
+ eina_binbuf_free(tbuf);
+
+ if (!tbuf || !cbuf) return compressed;
+
+ // Make sure the list has all the buffer up to the needed one filled with valid data
+ if (list_index)
+ {
+ // Create the compressed zero buffer once.
+ if (!z)
+ {
+ unsigned char *zmem;
+
+ zmem = calloc(EFL_UI_MODEL_EXACT_CONTENT, sizeof (unsigned int));
+ if (!zmem) return compressed;
+
+ tbuf = eina_binbuf_manage_new(zmem, EFL_UI_MODEL_EXACT_CONTENT_LENGTH, EINA_TRUE);
+ if (!tbuf) return compressed;
+
+ z = emile_compress(tbuf, EMILE_LZ4, EMILE_COMPRESSOR_FAST);
+
+ eina_binbuf_free(tbuf);
+ free(zmem);
+ }
+
+ // Fill the list all the way to the needed index with buffer full of zero
+ while (list_index) compressed = eina_list_append(compressed, z);
+ l = eina_list_last(compressed);
+ }
+
+ // Replace older buffer by newer buffer
+ tbuf = eina_list_data_get(l);
+ compressed = eina_list_prepend_relative(compressed, l, cbuf);
+ compressed = eina_list_remove_list(compressed, l);
+ if (tbuf != z) eina_binbuf_free(tbuf);
+
+ return compressed;
+}
+
+static unsigned int *
+_efl_ui_model_exact_buffer_expand(unsigned int list_index, unsigned int *buffer, Eina_List *list)
+{
+ unsigned int found;
+ Eina_Binbuf *tmp;
+ Eina_List *l = NULL;
+
+ if (!buffer) buffer = malloc(EFL_UI_MODEL_EXACT_CONTENT_LENGTH);
+
+ found = _efl_ui_model_exact_list_find(list_index, list, &l);
+
+ // Check if the data is in the list
+ if (!found)
+ {
+ // Not found -> everything is assumed to be zero
+ memset(buffer, 0, EFL_UI_MODEL_EXACT_CONTENT_LENGTH);
+ return buffer;
+ }
+
+ // Found -> expand in buffer
+ tmp = eina_binbuf_manage_new((unsigned char*) buffer, EFL_UI_MODEL_EXACT_CONTENT_LENGTH, EINA_TRUE);
+ emile_expand(eina_list_data_get(l), tmp, EMILE_LZ4);
+ eina_binbuf_free(tmp);
+
+ return buffer;
+}
+
+static unsigned char
+_efl_ui_model_exact_slot_find(Efl_Ui_Model_Exact_Data *pd, unsigned int index,
+ Eina_Bool width_get, Eina_Bool height_get)
+{
+ unsigned char lookup;
+ unsigned char found = EINA_C_ARRAY_LENGTH(pd->parent->slot);
+
+ for (lookup = 0; lookup < EINA_C_ARRAY_LENGTH(pd->parent->slot); lookup++)
+ {
+ // Check if the slot has valid content
+ if (!pd->parent->slot[lookup].defined)
+ continue;
+ if (pd->parent->slot[lookup].start_offset <= index &&
+ index < pd->parent->slot[lookup].start_offset + EFL_UI_MODEL_EXACT_CONTENT)
+ found = lookup;
+ // Reduce usage to find unused slot.
+ if (pd->parent->slot[lookup].usage > 0)
+ pd->parent->slot[lookup].usage--;
+ }
+
+ // Do we need to find a new slot?
+ if (found == EINA_C_ARRAY_LENGTH(pd->parent->slot))
+ {
+ found = 0;
+ for (lookup = 0; lookup < EINA_C_ARRAY_LENGTH(pd->parent->slot); lookup++)
+ {
+ if (!pd->parent->slot[lookup].defined)
+ {
+ // Found an empty slot, let's use that.
+ found = lookup;
+ break;
+ }
+ if (pd->parent->slot[lookup].usage < pd->parent->slot[found].usage)
+ found = lookup;
+ }
+
+ // Commit change back to the stored buffer list
+ if (pd->parent->slot[found].defined &&
+ (pd->parent->slot[found].width ||
+ pd->parent->slot[found].height))
+ {
+ if (pd->parent->slot[found].width &&
+ pd->parent->slot[found].decompressed.width)
+ pd->parent->compressed.width = _efl_ui_model_exact_slot_compress(index,
+ pd->parent->compressed.width,
+ pd->parent->slot[found].width);
+ if (pd->parent->slot[found].height &&
+ pd->parent->slot[found].decompressed.height)
+ pd->parent->compressed.height = _efl_ui_model_exact_slot_compress(index,
+ pd->parent->compressed.height,
+ pd->parent->slot[found].height);
+ }
+
+ pd->parent->slot[found].defined = EINA_TRUE;
+ pd->parent->slot[found].decompressed.width = EINA_FALSE;
+ pd->parent->slot[found].decompressed.height = EINA_FALSE;
+ pd->parent->slot[found].start_offset = index / EFL_UI_MODEL_EXACT_CONTENT;
+ }
+
+ // Increase usage of the returnd slot for now
+ pd->parent->slot[found].usage++;
+
+ // Unpack the data if requested
+ if (width_get && !pd->parent->slot[found].decompressed.width)
+ {
+ pd->parent->slot[found].width = _efl_ui_model_exact_buffer_expand(pd->parent->slot[found].start_offset,
+ pd->parent->slot[found].width,
+ pd->parent->compressed.width);
+ pd->parent->slot[found].decompressed.width = EINA_TRUE;
+ }
+ if (height_get && !pd->parent->slot[found].decompressed.height)
+ {
+ pd->parent->slot[found].height = _efl_ui_model_exact_buffer_expand(pd->parent->slot[found].start_offset,
+ pd->parent->slot[found].height,
+ pd->parent->compressed.height);
+ pd->parent->slot[found].decompressed.height = EINA_TRUE;
+ }
+
+ return found;
+}
+
+static Eina_Future *
+_efl_ui_model_exact_efl_model_property_set(Eo *obj, Efl_Ui_Model_Exact_Data *pd,
+ const char *property, Eina_Value *value)
+{
+ if (pd->parent)
+ {
+ if (!strcmp(property, _efl_model_property_selfw))
+ {
+ unsigned int index;
+ unsigned char found;
+
+ index = efl_model_composite_index_get(obj);
+ found = _efl_ui_model_exact_slot_find(pd, index, EINA_TRUE, EINA_FALSE);
+ if (!eina_value_uint_convert(value, &pd->parent->slot[found].width[index % EFL_UI_MODEL_EXACT_CONTENT]))
+ return efl_loop_future_rejected(obj, EFL_MODEL_ERROR_INCORRECT_VALUE);
+ // We succeeded so let's update the max total size width (As we only handle vertical list case at the moment)
+ if (pd->parent->total_size.width < pd->parent->slot[found].width[index % EFL_UI_MODEL_EXACT_CONTENT])
+ pd->parent->total_size.width = pd->parent->slot[found].width[index % EFL_UI_MODEL_EXACT_CONTENT];
+ return efl_loop_future_resolved(obj, eina_value_uint_init(pd->parent->slot[found].width[index % EFL_UI_MODEL_EXACT_CONTENT]));
+ }
+ if (!strcmp(property, _efl_model_property_selfh))
+ {
+ unsigned int old_value;
+ unsigned int index;
+ unsigned char found;
+
+ index = efl_model_composite_index_get(obj);
+ found = _efl_ui_model_exact_slot_find(pd, index, EINA_FALSE, EINA_TRUE);
+ old_value = pd->parent->slot[found].height[index % EFL_UI_MODEL_EXACT_CONTENT];
+ if (!eina_value_uint_convert(value, &pd->parent->slot[found].height[index % EFL_UI_MODEL_EXACT_CONTENT]))
+ return efl_loop_future_rejected(obj, EFL_MODEL_ERROR_INCORRECT_VALUE);
+ // We succeeded so let's update the total size
+ pd->parent->total_size.height += pd->parent->slot[found].height[index % EFL_UI_MODEL_EXACT_CONTENT] - old_value;
+ return efl_loop_future_resolved(obj, eina_value_uint_init(pd->parent->slot[found].height[index % EFL_UI_MODEL_EXACT_CONTENT]));
+ }
+ // The following property are calculated by the model and so READ_ONLY
+ if (!strcmp(property, _efl_model_property_totalh))
+ {
+ return efl_loop_future_rejected(obj, EFL_MODEL_ERROR_READ_ONLY);
+ }
+ if (!strcmp(property, _efl_model_property_totalw))
+ {
+ return efl_loop_future_rejected(obj, EFL_MODEL_ERROR_READ_ONLY);
+ }
+ }
+
+ if (!strcmp(property, _efl_model_property_itemw))
+ {
+ // The exact model can not guess a general item size if asked
+ // and should refuse to remember anything like that.
+ return efl_loop_future_rejected(obj, EFL_MODEL_ERROR_READ_ONLY);
+ }
+ if (!strcmp(property, _efl_model_property_itemh))
+ {
+ // The exact model can not guess a general item size if asked
+ // and should refuse to remember anything like that.
+ return efl_loop_future_rejected(obj, EFL_MODEL_ERROR_READ_ONLY);
+ }
+
+ return efl_model_property_set(efl_super(obj, EFL_UI_MODEL_EXACT_CLASS), property, value);
+}
+
+static Eina_Value *
+_efl_ui_model_exact_efl_model_property_get(const Eo *obj, Efl_Ui_Model_Exact_Data *pd,
+ const char *property)
+{
+ if (pd->parent)
+ {
+ if (!strcmp(property, _efl_model_property_selfw))
+ {
+ unsigned int index;
+ unsigned char found;
+
+ index = efl_model_composite_index_get(obj);
+ found = _efl_ui_model_exact_slot_find(pd, index, EINA_TRUE, EINA_FALSE);
+ return eina_value_uint_new(pd->parent->slot[found].width[index % EFL_UI_MODEL_EXACT_CONTENT]);
+ }
+ if (!strcmp(property, _efl_model_property_selfh))
+ {
+ unsigned int index;
+ unsigned char found;
+
+ index = efl_model_composite_index_get(obj);
+ found = _efl_ui_model_exact_slot_find(pd, index, EINA_FALSE, EINA_TRUE);
+ return eina_value_uint_new(pd->parent->slot[found].height[index % EFL_UI_MODEL_EXACT_CONTENT]);
+ }
+ }
+ if (!strcmp(property, _efl_model_property_totalh))
+ {
+ return eina_value_uint_new(pd->total_size.height);
+ }
+ if (!strcmp(property, _efl_model_property_totalw))
+ {
+ return eina_value_uint_new(pd->total_size.width);
+ }
+ if (!strcmp(property, _efl_model_property_itemw))
+ {
+ // The exact model can not guess a general item size if asked.
+ return eina_value_error_new(EAGAIN);
+ }
+ if (!strcmp(property, _efl_model_property_itemh))
+ {
+ // The exact model can not guess a general item size if asked.
+ return eina_value_error_new(EAGAIN);
+ }
+ return efl_model_property_get(efl_super(obj, EFL_UI_MODEL_EXACT_CLASS), property);
+}
+
+#include "efl_ui_model_exact.eo.c"