1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif
4 
5 #include <Elementary.h>
6 #include "elm_priv.h"
7 
8 // This class store the size information in a compressed array and unpack it
9 // when necessary. It does maintain a cache of up to 3 uncompressed slot.
10 // That cache could get dropped when the application is entering the 'pause'
11 // state.
12 
13 #define EFL_UI_EXACT_MODEL_CONTENT 1024
14 #define EFL_UI_EXACT_MODEL_CONTENT_LENGTH (EFL_UI_EXACT_MODEL_CONTENT * sizeof (unsigned int))
15 
16 // For now only vertical logic is implemented. Horizontal list and grid are not supported.
17 
18 typedef struct _Efl_Ui_Exact_Model_Data Efl_Ui_Exact_Model_Data;
19 struct _Efl_Ui_Exact_Model_Data
20 {
21    Efl_Ui_Exact_Model_Data *parent;
22 
23    struct {
24       Eina_List *width;
25       Eina_List *height;
26    } compressed;
27 
28    struct {
29       unsigned int width;
30       unsigned int height;
31    } total_size;
32 
33    struct {
34       unsigned int *width;
35       unsigned int *height;
36       unsigned int start_offset;
37       unsigned short usage;
38       Eina_Bool defined : 1;
39       struct {
40          Eina_Bool width : 1;
41          Eina_Bool height : 1;
42       } decompressed;
43    } slot[3];
44 };
45 
46 static Efl_Object *
_efl_ui_exact_model_efl_object_constructor(Eo * obj,Efl_Ui_Exact_Model_Data * pd)47 _efl_ui_exact_model_efl_object_constructor(Eo *obj, Efl_Ui_Exact_Model_Data *pd)
48 {
49    Eo *parent = efl_parent_get(obj);
50 
51    if (parent && efl_isa(parent, EFL_UI_EXACT_MODEL_CLASS))
52      pd->parent = efl_data_scope_get(efl_parent_get(obj), EFL_UI_EXACT_MODEL_CLASS);
53 
54    return efl_constructor(efl_super(obj, EFL_UI_EXACT_MODEL_CLASS));
55 }
56 
57 static Eina_List *
_efl_ui_exact_model_slot_compress(unsigned int index,Eina_List * compressed,unsigned int * buffer)58 _efl_ui_exact_model_slot_compress(unsigned int index, Eina_List *compressed, unsigned int *buffer)
59 {
60    unsigned int list_index = index / EFL_UI_EXACT_MODEL_CONTENT;
61    static Eina_Binbuf *z = NULL;
62    Eina_Binbuf *cbuf;
63    Eina_Binbuf *tbuf;
64    Eina_List *l = NULL;
65    unsigned int i;
66 
67    l = eina_list_nth_list(compressed, list_index);
68 
69    tbuf = eina_binbuf_manage_new((unsigned char *) buffer, EFL_UI_EXACT_MODEL_CONTENT_LENGTH, EINA_TRUE);
70    if (!tbuf) return compressed;
71 
72    cbuf = emile_compress(tbuf, EMILE_LZ4, EMILE_COMPRESSOR_FAST);
73    eina_binbuf_free(tbuf);
74    if (!cbuf) return compressed;
75 
76    // Make sure the list has all the buffer up to the needed one filled with valid data
77    if (list_index)
78      {
79         // Create the compressed zero buffer once.
80         if (!z)
81           {
82              unsigned char *zmem;
83 
84              zmem = calloc(EFL_UI_EXACT_MODEL_CONTENT, sizeof (unsigned int));
85              if (!zmem)
86                {
87                   if (cbuf) eina_binbuf_free(cbuf);
88                   return compressed;
89                }
90 
91              tbuf = eina_binbuf_manage_new(zmem, EFL_UI_EXACT_MODEL_CONTENT_LENGTH, EINA_TRUE);
92              if (!tbuf)
93                {
94                   if (cbuf) eina_binbuf_free(cbuf);
95                   if (zmem) free(zmem);
96                   return compressed;
97                }
98 
99              z = emile_compress(tbuf, EMILE_LZ4, EMILE_COMPRESSOR_FAST);
100 
101              eina_binbuf_free(tbuf);
102              free(zmem);
103           }
104 
105         // Fill the list all the way to the needed index with buffer full of zero
106         for (i = 0; i < list_index; i++)
107           {
108              compressed = eina_list_append(compressed, z);
109           }
110         l = eina_list_last(compressed);
111      }
112 
113    // Replace older buffer by newer buffer
114    tbuf = eina_list_data_get(l);
115    compressed = eina_list_prepend_relative(compressed, l, cbuf);
116    compressed = eina_list_remove_list(compressed, l);
117    if (tbuf != z) eina_binbuf_free(tbuf);
118 
119    return compressed;
120 }
121 
122 static unsigned int *
_efl_ui_exact_model_buffer_expand(unsigned int list_index,unsigned int * buffer,Eina_List * list)123 _efl_ui_exact_model_buffer_expand(unsigned int list_index, unsigned int *buffer, Eina_List *list)
124 {
125    Eina_Binbuf *tmp;
126    Eina_List *l = NULL;
127 
128    if (!buffer) buffer = malloc(EFL_UI_EXACT_MODEL_CONTENT_LENGTH);
129 
130    l = eina_list_nth_list(list, list_index);
131 
132    // Check if the data is in the list
133    if (!l)
134      {
135         // Not found -> everything is assumed to be zero
136         memset(buffer, 0, EFL_UI_EXACT_MODEL_CONTENT_LENGTH);
137         return buffer;
138      }
139 
140    // Found -> expand in buffer
141    tmp = eina_binbuf_manage_new((unsigned char*) buffer, EFL_UI_EXACT_MODEL_CONTENT_LENGTH, EINA_TRUE);
142    emile_expand(eina_list_data_get(l), tmp, EMILE_LZ4);
143    eina_binbuf_free(tmp);
144 
145    return buffer;
146 }
147 
148 static unsigned char
_efl_ui_exact_model_slot_find(Efl_Ui_Exact_Model_Data * pd,unsigned int index,Eina_Bool width_get,Eina_Bool height_get)149 _efl_ui_exact_model_slot_find(Efl_Ui_Exact_Model_Data *pd, unsigned int index,
150                               Eina_Bool width_get, Eina_Bool height_get)
151 {
152    unsigned char lookup;
153    unsigned char found = EINA_C_ARRAY_LENGTH(pd->parent->slot);
154 
155    for (lookup = 0; lookup < EINA_C_ARRAY_LENGTH(pd->parent->slot); lookup++)
156      {
157         // Check if the slot has valid content
158         if (!pd->parent->slot[lookup].defined)
159           continue;
160         if (pd->parent->slot[lookup].start_offset <= index &&
161             index < pd->parent->slot[lookup].start_offset + EFL_UI_EXACT_MODEL_CONTENT)
162           found = lookup;
163         // Reduce usage to find unused slot.
164         if (pd->parent->slot[lookup].usage > 0)
165           pd->parent->slot[lookup].usage--;
166      }
167 
168    // Do we need to find a new slot?
169    if (found == EINA_C_ARRAY_LENGTH(pd->parent->slot))
170      {
171         found = 0;
172         for (lookup = 0; lookup < EINA_C_ARRAY_LENGTH(pd->parent->slot); lookup++)
173           {
174              if (!pd->parent->slot[lookup].defined)
175                {
176                   // Found an empty slot, let's use that.
177                   found = lookup;
178                   break;
179                }
180              if (pd->parent->slot[lookup].usage < pd->parent->slot[found].usage)
181                found = lookup;
182           }
183 
184         // Commit change back to the stored buffer list
185         if (pd->parent->slot[found].defined &&
186             (pd->parent->slot[found].width ||
187              pd->parent->slot[found].height))
188           {
189              if (pd->parent->slot[found].width &&
190                  pd->parent->slot[found].decompressed.width)
191                pd->parent->compressed.width = _efl_ui_exact_model_slot_compress(index,
192                                                                                 pd->parent->compressed.width,
193                                                                                 pd->parent->slot[found].width);
194              if (pd->parent->slot[found].height &&
195                  pd->parent->slot[found].decompressed.height)
196                pd->parent->compressed.height = _efl_ui_exact_model_slot_compress(index,
197                                                                                  pd->parent->compressed.height,
198                                                                                  pd->parent->slot[found].height);
199           }
200 
201         pd->parent->slot[found].defined = EINA_TRUE;
202         pd->parent->slot[found].decompressed.width = EINA_FALSE;
203         pd->parent->slot[found].decompressed.height = EINA_FALSE;
204         pd->parent->slot[found].start_offset = index / EFL_UI_EXACT_MODEL_CONTENT;
205      }
206 
207    // Increase usage of the returned slot for now
208    pd->parent->slot[found].usage++;
209 
210    // Unpack the data if requested
211    if (width_get && !pd->parent->slot[found].decompressed.width)
212      {
213         pd->parent->slot[found].width = _efl_ui_exact_model_buffer_expand(pd->parent->slot[found].start_offset,
214                                                                           pd->parent->slot[found].width,
215                                                                           pd->parent->compressed.width);
216         pd->parent->slot[found].decompressed.width = EINA_TRUE;
217      }
218    if (height_get && !pd->parent->slot[found].decompressed.height)
219      {
220         pd->parent->slot[found].height = _efl_ui_exact_model_buffer_expand(pd->parent->slot[found].start_offset,
221                                                                           pd->parent->slot[found].height,
222                                                                           pd->parent->compressed.height);
223         pd->parent->slot[found].decompressed.height = EINA_TRUE;
224      }
225 
226    return found;
227 }
228 
229 static Eina_Future *
_efl_ui_exact_model_efl_model_property_set(Eo * obj,Efl_Ui_Exact_Model_Data * pd,const char * property,Eina_Value * value)230 _efl_ui_exact_model_efl_model_property_set(Eo *obj, Efl_Ui_Exact_Model_Data *pd,
231                                            const char *property, Eina_Value *value)
232 {
233    if (pd->parent)
234     {
235        if (eina_streq(property, _efl_model_property_selfw))
236          {
237             unsigned int index;
238             unsigned char found;
239 
240             index = efl_composite_model_index_get(obj);
241             found = _efl_ui_exact_model_slot_find(pd, index, EINA_TRUE, EINA_FALSE);
242             if (!eina_value_uint_convert(value, &pd->parent->slot[found].width[index % EFL_UI_EXACT_MODEL_CONTENT]))
243               return efl_loop_future_rejected(obj, EFL_MODEL_ERROR_INCORRECT_VALUE);
244             // We succeeded so let's update the max total size width (As we only handle vertical list case at the moment)
245             if (pd->parent->total_size.width < pd->parent->slot[found].width[index % EFL_UI_EXACT_MODEL_CONTENT])
246               pd->parent->total_size.width = pd->parent->slot[found].width[index % EFL_UI_EXACT_MODEL_CONTENT];
247             return efl_loop_future_resolved(obj, eina_value_uint_init(pd->parent->slot[found].width[index % EFL_UI_EXACT_MODEL_CONTENT]));
248          }
249        if (eina_streq(property, _efl_model_property_selfh))
250          {
251             unsigned int old_value;
252             unsigned int index;
253             unsigned char found;
254 
255             index = efl_composite_model_index_get(obj);
256             found = _efl_ui_exact_model_slot_find(pd, index, EINA_FALSE, EINA_TRUE);
257             old_value = pd->parent->slot[found].height[index % EFL_UI_EXACT_MODEL_CONTENT];
258             if (!eina_value_uint_convert(value, &pd->parent->slot[found].height[index % EFL_UI_EXACT_MODEL_CONTENT]))
259               return efl_loop_future_rejected(obj, EFL_MODEL_ERROR_INCORRECT_VALUE);
260             // We succeeded so let's update the total size
261             pd->parent->total_size.height += pd->parent->slot[found].height[index % EFL_UI_EXACT_MODEL_CONTENT] - old_value;
262             return efl_loop_future_resolved(obj, eina_value_uint_init(pd->parent->slot[found].height[index % EFL_UI_EXACT_MODEL_CONTENT]));
263          }
264        // The following property are calculated by the model and so READ_ONLY
265        if (eina_streq(property, _efl_model_property_totalh))
266          {
267             return efl_loop_future_rejected(obj, EFL_MODEL_ERROR_READ_ONLY);
268          }
269        if (eina_streq(property, _efl_model_property_totalw))
270          {
271             return efl_loop_future_rejected(obj, EFL_MODEL_ERROR_READ_ONLY);
272          }
273     }
274 
275    if (eina_streq(property, _efl_model_property_itemw))
276      {
277         // The exact model can not guess a general item size if asked
278         // and should refuse to remember anything like that.
279         return efl_loop_future_rejected(obj, EFL_MODEL_ERROR_READ_ONLY);
280      }
281    if (eina_streq(property, _efl_model_property_itemh))
282      {
283         // The exact model can not guess a general item size if asked
284         // and should refuse to remember anything like that.
285         return efl_loop_future_rejected(obj, EFL_MODEL_ERROR_READ_ONLY);
286      }
287 
288    return efl_model_property_set(efl_super(obj, EFL_UI_EXACT_MODEL_CLASS), property, value);
289 }
290 
291 static Eina_Value *
_efl_ui_exact_model_efl_model_property_get(const Eo * obj,Efl_Ui_Exact_Model_Data * pd,const char * property)292 _efl_ui_exact_model_efl_model_property_get(const Eo *obj, Efl_Ui_Exact_Model_Data *pd,
293                                            const char *property)
294 {
295    if (pd->parent)
296      {
297         if (eina_streq(property, _efl_model_property_selfw))
298           {
299              unsigned int index;
300              unsigned char found;
301 
302              index = efl_composite_model_index_get(obj);
303              found = _efl_ui_exact_model_slot_find(pd, index, EINA_TRUE, EINA_FALSE);
304              return eina_value_uint_new(pd->parent->slot[found].width[index % EFL_UI_EXACT_MODEL_CONTENT]);
305           }
306         if (eina_streq(property, _efl_model_property_selfh))
307           {
308              unsigned int index;
309              unsigned char found;
310 
311              index = efl_composite_model_index_get(obj);
312              found = _efl_ui_exact_model_slot_find(pd, index, EINA_FALSE, EINA_TRUE);
313              return eina_value_uint_new(pd->parent->slot[found].height[index % EFL_UI_EXACT_MODEL_CONTENT]);
314           }
315      }
316    if (eina_streq(property, _efl_model_property_totalh))
317      {
318         return eina_value_uint_new(pd->total_size.height);
319      }
320    if (eina_streq(property, _efl_model_property_totalw))
321      {
322         return eina_value_uint_new(pd->total_size.width);
323      }
324    if (eina_streq(property, _efl_model_property_itemw))
325      {
326         // The exact model can not guess a general item size if asked.
327         return eina_value_error_new(EAGAIN);
328      }
329    if (eina_streq(property, _efl_model_property_itemh))
330      {
331         // The exact model can not guess a general item size if asked.
332         return eina_value_error_new(EAGAIN);
333      }
334    return efl_model_property_get(efl_super(obj, EFL_UI_EXACT_MODEL_CLASS), property);
335 }
336 
337 #include "efl_ui_exact_model.eo.c"
338