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