1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif
4
5 #include <Elementary.h>
6 #include "elm_priv.h"
7
8 #include "ecore_internal.h"
9
10 typedef struct _Efl_Ui_View_Model_Data Efl_Ui_View_Model_Data;
11 typedef struct _Efl_Ui_View_Model_Bind Efl_Ui_View_Model_Bind;
12 typedef struct _Efl_Ui_View_Model_Text Efl_Ui_View_Model_Text;
13 typedef struct _Efl_Ui_View_Model_Logic Efl_Ui_View_Model_Logic;
14 typedef struct _Efl_Ui_View_Model_Property_Ref Efl_Ui_View_Model_Property_Ref;
15
16 struct _Efl_Ui_View_Model_Data
17 {
18 // FIXME: If parent is set, always access parent... recursively?
19 Efl_Ui_View_Model_Data *parent;
20
21 Eina_Hash *bound; // Stringhash of Efl_Ui_View_Model_Bind
22 Eina_Hash *logics; // Stringhash of Efl_Ui_View_Model_Logic
23 Eina_Hash *texts; // Stringhash of Efl_Ui_View_Model_Text
24
25 Eina_Hash *deduplication; // Stringhash of Efl_Ui_View_Model_Property_Ref
26
27 struct {
28 Eina_Bool property_changed : 1;
29 Eina_Bool child_added : 1;
30 Eina_Bool child_removed : 1;
31 } propagating; // Boolean to prevent reentrance event emission on the same object
32 Eina_Bool finalized : 1;
33 Eina_Bool children_bind : 1; // Define if child object should be automatically bound
34 };
35
36 struct _Efl_Ui_View_Model_Text
37 {
38 Eina_Stringshare *name;
39 Eina_Stringshare *definition;
40 Eina_Stringshare *not_ready;
41 Eina_Stringshare *on_error;
42 Efl_Model *self;
43 };
44
45 struct _Efl_Ui_View_Model_Bind
46 {
47 Eina_Stringshare *source;
48 Eina_List *destinations;
49 };
50
51 struct _Efl_Ui_View_Model_Logic
52 {
53 struct {
54 EflUiViewModelPropertyGet fct;
55 Eina_Free_Cb free_cb;
56 void *data;
57 } get;
58 struct {
59 EflUiViewModelPropertySet fct;
60 Eina_Free_Cb free_cb;
61 void *data;
62 } set;
63
64 Efl_Object *object;
65 Eina_List *sources;
66 Eina_Stringshare *property;
67 };
68
69 struct _Efl_Ui_View_Model_Property_Ref
70 {
71 EINA_REFCOUNT;
72 Eina_Stringshare *property;
73 };
74
75 static void
_ref_free(void * data)76 _ref_free(void *data)
77 {
78 Efl_Ui_View_Model_Property_Ref *r = data;
79
80 eina_stringshare_del(r->property);
81 free(r);
82 }
83
84 static void
_ref_add(Efl_Ui_View_Model_Data * pd,Eina_Stringshare * property)85 _ref_add(Efl_Ui_View_Model_Data *pd, Eina_Stringshare *property)
86 {
87 Efl_Ui_View_Model_Property_Ref *r;
88
89 r = eina_hash_find(pd->deduplication, property);
90 if (!r)
91 {
92 r = calloc(1, sizeof (Efl_Ui_View_Model_Property_Ref));
93 if (!r) return ;
94 r->property = eina_stringshare_ref(property);
95
96 eina_hash_direct_add(pd->deduplication, r->property, r);
97 }
98
99 EINA_REFCOUNT_REF(r);
100 }
101
102 static void
_ref_del(Efl_Ui_View_Model_Data * pd,Eina_Stringshare * property)103 _ref_del(Efl_Ui_View_Model_Data *pd, Eina_Stringshare *property)
104 {
105 Efl_Ui_View_Model_Property_Ref *r;
106
107 r = eina_hash_find(pd->deduplication, property);
108 if (!r) return ;
109
110 EINA_REFCOUNT_UNREF(r)
111 eina_hash_del(pd->deduplication, property, r);
112 }
113
114 static void
_logic_free(void * data)115 _logic_free(void *data)
116 {
117 Efl_Ui_View_Model_Logic *logic = data;
118 Eina_Stringshare *source;
119
120 if (logic->get.free_cb) logic->get.free_cb(logic->get.data);
121 if (logic->set.free_cb) logic->set.free_cb(logic->set.data);
122 EINA_LIST_FREE(logic->sources, source)
123 {
124 efl_ui_view_model_property_unbind(logic->object, source, logic->property);
125 eina_stringshare_del(source);
126 }
127 eina_stringshare_del(logic->property);
128 free(logic);
129 }
130
131 static Eina_Value *
_efl_ui_view_model_property_dummy_get(void * data EINA_UNUSED,const Efl_Ui_View_Model * view_model EINA_UNUSED,Eina_Stringshare * property EINA_UNUSED)132 _efl_ui_view_model_property_dummy_get(void *data EINA_UNUSED,
133 const Efl_Ui_View_Model *view_model EINA_UNUSED,
134 Eina_Stringshare *property EINA_UNUSED)
135 {
136 return eina_value_error_new(EFL_MODEL_ERROR_NOT_SUPPORTED);
137 }
138
139 static Eina_Future *
_efl_ui_view_model_property_dummy_set(void * data EINA_UNUSED,Efl_Ui_View_Model * view_model,Eina_Stringshare * property EINA_UNUSED,Eina_Value * value EINA_UNUSED)140 _efl_ui_view_model_property_dummy_set(void *data EINA_UNUSED,
141 Efl_Ui_View_Model *view_model,
142 Eina_Stringshare *property EINA_UNUSED,
143 Eina_Value *value EINA_UNUSED)
144 {
145 return efl_loop_future_rejected(view_model, EFL_MODEL_ERROR_READ_ONLY);
146 }
147
148 static Eina_Error
_efl_ui_view_model_property_logic_add(Eo * obj,Efl_Ui_View_Model_Data * pd,const char * property,void * get_data,EflUiViewModelPropertyGet get,Eina_Free_Cb get_free_cb,void * set_data,EflUiViewModelPropertySet set,Eina_Free_Cb set_free_cb,Eina_Iterator * bound)149 _efl_ui_view_model_property_logic_add(Eo *obj, Efl_Ui_View_Model_Data *pd,
150 const char *property,
151 void *get_data, EflUiViewModelPropertyGet get, Eina_Free_Cb get_free_cb,
152 void *set_data, EflUiViewModelPropertySet set, Eina_Free_Cb set_free_cb,
153 Eina_Iterator *bound)
154 {
155 Efl_Ui_View_Model_Logic *logic;
156 Eina_Stringshare *prop;
157 const char *source;
158
159 prop = eina_stringshare_add(property);
160
161 if (eina_hash_find(pd->logics, prop))
162 {
163 eina_stringshare_del(prop);
164 return EFL_MODEL_ERROR_INCORRECT_VALUE;
165 }
166
167 logic = calloc(1, sizeof (Efl_Ui_View_Model_Logic));
168 if (!logic) return ENOMEM;
169
170 logic->object = obj;
171 logic->property = prop;
172 logic->get.fct = get ? get : _efl_ui_view_model_property_dummy_get;
173 logic->get.free_cb = get_free_cb;
174 logic->get.data = get_data;
175 logic->set.fct = set ? set : _efl_ui_view_model_property_dummy_set;
176 logic->set.free_cb = set_free_cb;
177 logic->set.data = set_data;
178
179 eina_hash_direct_add(pd->logics, prop, logic);
180
181 EINA_ITERATOR_FOREACH(bound, source)
182 {
183 logic->sources = eina_list_append(logic->sources, eina_stringshare_add(source));
184 efl_ui_view_model_property_bind(obj, source, property);
185 }
186 eina_iterator_free(bound);
187
188 return 0;
189 }
190
191 static Eina_Error
_efl_ui_view_model_property_logic_del(Eo * obj EINA_UNUSED,Efl_Ui_View_Model_Data * pd,const char * property)192 _efl_ui_view_model_property_logic_del(Eo *obj EINA_UNUSED, Efl_Ui_View_Model_Data *pd,
193 const char *property)
194 {
195 Efl_Ui_View_Model_Logic *logic;
196
197 logic = eina_hash_find(pd->logics, property);
198 if (!logic) return EFL_MODEL_ERROR_INCORRECT_VALUE;
199 eina_hash_del(pd->logics, property, logic);
200 return 0;
201 }
202
203 static int
_lookup_next_token(const char * definition,Eina_Slstr ** text,Eina_Slstr ** property)204 _lookup_next_token(const char *definition,
205 Eina_Slstr **text,
206 Eina_Slstr **property)
207 {
208 const char *lookup_text;
209 const char *lookup_property;
210
211 if (!definition) return 0;
212
213 *text = NULL;
214 *property = NULL;
215
216 lookup_text = strstr(definition, "${");
217 if (!lookup_text) goto on_error;
218 lookup_text += 2;
219
220 lookup_property = strchr(lookup_text, '}');
221 if (!lookup_property) goto on_error;
222
223 *text = eina_slstr_copy_new_length(definition, lookup_text - definition - 2);
224 *property = eina_slstr_copy_new_length(lookup_text, lookup_property - lookup_text);
225
226 return lookup_property + 1 - definition;
227
228 on_error:
229 if (strlen(definition) == 0) return 0;
230 *text = eina_slstr_copy_new(definition);
231 return strlen(definition);
232 }
233
234 static Eina_Error
_efl_ui_view_model_property_string_add(Eo * obj,Efl_Ui_View_Model_Data * pd,const char * name,const char * definition,const char * not_ready,const char * on_error)235 _efl_ui_view_model_property_string_add(Eo *obj, Efl_Ui_View_Model_Data *pd,
236 const char *name,
237 const char *definition,
238 const char *not_ready,
239 const char *on_error)
240 {
241 Efl_Ui_View_Model_Text *text;
242 Eina_Stringshare *sn;
243 Eina_Slstr *st = NULL;
244 Eina_Slstr *sp = NULL;
245 int lookup;
246 Eina_Error err = ENOMEM;
247
248 if (!name || !definition) return EFL_MODEL_ERROR_INCORRECT_VALUE;
249 if (!strlen(name)) return EFL_MODEL_ERROR_INCORRECT_VALUE;
250 if (!strlen(definition)) return EFL_MODEL_ERROR_INCORRECT_VALUE;
251 sn = eina_stringshare_add(name);
252
253 // Lookup if there is an existing property defined and undo it first
254 text = eina_hash_find(pd->texts, sn);
255 if (text) efl_ui_view_model_property_string_del(obj, sn);
256
257 text = calloc(1, sizeof (Efl_Ui_View_Model_Text));
258 if (!text) goto on_error;
259
260 err = EFL_MODEL_ERROR_INCORRECT_VALUE;
261
262 text->name = eina_stringshare_add(name);
263 text->definition = eina_stringshare_add(definition);
264 text->not_ready = not_ready ? eina_stringshare_add(not_ready) : NULL;
265 text->on_error = on_error ? eina_stringshare_add(on_error) : NULL;
266 text->self = obj;
267
268 for (lookup = _lookup_next_token(definition, &st, &sp);
269 lookup;
270 definition += lookup, lookup = _lookup_next_token(definition, &st, &sp))
271 {
272 if (sp) efl_ui_view_model_property_bind(obj, sp, name);
273 }
274
275 for (lookup = _lookup_next_token(not_ready, &st, &sp);
276 lookup;
277 not_ready += lookup, lookup = _lookup_next_token(not_ready, &st, &sp))
278 {
279 if (sp) efl_ui_view_model_property_bind(obj, sp, name);
280 }
281
282 for (lookup = _lookup_next_token(on_error, &st, &sp);
283 lookup;
284 on_error += lookup, lookup = _lookup_next_token(on_error, &st, &sp))
285 {
286 if (sp) efl_ui_view_model_property_bind(obj, sp, name);
287 }
288
289 eina_hash_direct_add(pd->texts, text->name, text);
290
291 return 0;
292
293 on_error:
294 eina_stringshare_del(sn);
295 free(text);
296 return err;
297 }
298
299 static void
_text_free(void * data)300 _text_free(void *data)
301 {
302 Efl_Ui_View_Model_Text *text = data;
303 Eina_Stringshare *st;
304 Eina_Stringshare *sp;
305 int lookup;
306 const char *tmp;
307
308 tmp = text->definition;
309 for (lookup = _lookup_next_token(tmp, &st, &sp);
310 lookup;
311 tmp += lookup, lookup = _lookup_next_token(tmp, &st, &sp))
312 {
313 if (sp) efl_ui_view_model_property_unbind(text->self, sp, text->name);
314 }
315
316 tmp = text->not_ready;
317 for (lookup = _lookup_next_token(tmp, &st, &sp);
318 lookup;
319 tmp += lookup, lookup = _lookup_next_token(tmp, &st, &sp))
320 {
321 if (sp) efl_ui_view_model_property_unbind(text->self, sp, text->name);
322 }
323
324 tmp = text->on_error;
325 for (lookup = _lookup_next_token(tmp, &st, &sp);
326 lookup;
327 tmp += lookup, lookup = _lookup_next_token(tmp, &st, &sp))
328 {
329 if (sp) efl_ui_view_model_property_unbind(text->self, sp, text->name);
330 }
331
332 eina_stringshare_del(text->name);
333 eina_stringshare_del(text->not_ready);
334 eina_stringshare_del(text->on_error);
335 free(text);
336 }
337
338 static Eina_Error
_efl_ui_view_model_property_string_del(Eo * obj EINA_UNUSED,Efl_Ui_View_Model_Data * pd,const char * name)339 _efl_ui_view_model_property_string_del(Eo *obj EINA_UNUSED,
340 Efl_Ui_View_Model_Data *pd,
341 const char *name)
342 {
343 Efl_Ui_View_Model_Text *text;
344 Eina_Stringshare *sn;
345 Eina_Error err = EFL_MODEL_ERROR_INCORRECT_VALUE;
346
347 if (!name) return EFL_MODEL_ERROR_INCORRECT_VALUE;
348
349 sn = eina_stringshare_add(name);
350 text = eina_hash_find(pd->texts, sn);
351 if (!text) goto on_error;
352 eina_hash_del(pd->texts, sn, text);
353 err = 0;
354
355 on_error:
356 eina_stringshare_del(sn);
357 return err;
358 }
359
360 static void
_efl_ui_view_model_property_bind(Eo * obj EINA_UNUSED,Efl_Ui_View_Model_Data * pd,const char * source,const char * destination)361 _efl_ui_view_model_property_bind(Eo *obj EINA_UNUSED, Efl_Ui_View_Model_Data *pd,
362 const char *source, const char *destination)
363 {
364 Efl_Ui_View_Model_Bind *bind;
365 Eina_Stringshare *src;
366 Eina_Stringshare *dst;
367
368 if (!source || !destination) return ;
369
370 src = eina_stringshare_add(source);
371 bind = eina_hash_find(pd->bound, src);
372 if (!bind)
373 {
374 bind = calloc(1, sizeof (Efl_Ui_View_Model_Bind));
375 if (!bind) goto on_error;
376 bind->source = eina_stringshare_ref(src);
377
378 eina_hash_direct_add(pd->bound, bind->source, bind);
379 }
380
381 dst = eina_stringshare_add(destination);
382 bind->destinations = eina_list_append(bind->destinations, dst);
383 _ref_add(pd, dst);
384
385 on_error:
386 eina_stringshare_del(src);
387 }
388
389 static void
_efl_ui_view_model_property_unbind(Eo * obj EINA_UNUSED,Efl_Ui_View_Model_Data * pd,const char * source,const char * destination)390 _efl_ui_view_model_property_unbind(Eo *obj EINA_UNUSED, Efl_Ui_View_Model_Data *pd,
391 const char *source, const char *destination)
392 {
393 Efl_Ui_View_Model_Bind *bind;
394 Eina_Stringshare *src;
395 Eina_Stringshare *dst;
396 Eina_Stringshare *cmp;
397 Eina_List *l;
398
399 if (!source || !destination) return ;
400 src = eina_stringshare_add(source);
401 bind = eina_hash_find(pd->bound, src);
402 if (!bind) goto on_error;
403
404 dst = eina_stringshare_add(destination);
405
406 EINA_LIST_FOREACH(bind->destinations, l, cmp)
407 if (cmp == dst)
408 {
409 bind->destinations = eina_list_remove_list(bind->destinations, l);
410 break;
411 }
412
413 if (!bind->destinations)
414 eina_hash_del(pd->bound, dst, bind);
415
416 _ref_del(pd, dst);
417 eina_stringshare_del(dst);
418
419 on_error:
420 eina_stringshare_del(src);
421 }
422
423 static void
_bind_free(void * data)424 _bind_free(void *data)
425 {
426 Efl_Ui_View_Model_Bind *bind = data;
427 Eina_Stringshare *dst;
428
429 eina_stringshare_del(bind->source);
430
431 EINA_LIST_FREE(bind->destinations, dst)
432 eina_stringshare_del(dst);
433
434 free(bind);
435 }
436
437 static void
_efl_ui_view_model_property_bind_lookup(Eina_Array * changed_properties,Efl_Ui_View_Model_Data * pd,Eina_Stringshare * src)438 _efl_ui_view_model_property_bind_lookup(Eina_Array *changed_properties,
439 Efl_Ui_View_Model_Data *pd,
440 Eina_Stringshare *src)
441 {
442 Efl_Ui_View_Model_Bind *bind;
443
444 if (!pd) return ;
445 bind = eina_hash_find(pd->bound, src);
446 if (bind)
447 {
448 Eina_Stringshare *dest;
449 Eina_List *l;
450
451 EINA_LIST_FOREACH(bind->destinations, l, dest)
452 {
453 // Check for duplicated entry first to avoid infinite recursion
454 Eina_Stringshare *dup = NULL;
455 Eina_Array_Iterator iterator;
456 unsigned int i;
457
458 EINA_ARRAY_ITER_NEXT(changed_properties, i, dup, iterator)
459 if (dup == dest) break;
460 if (dup == dest) continue ;
461
462 eina_array_push(changed_properties, dest);
463 _efl_ui_view_model_property_bind_lookup(changed_properties, pd, dest);
464 }
465 }
466 _efl_ui_view_model_property_bind_lookup(changed_properties, pd->parent, src);
467 }
468
469 static void
_efl_ui_view_model_property_changed(void * data,const Efl_Event * event)470 _efl_ui_view_model_property_changed(void *data, const Efl_Event *event)
471 {
472 Efl_Ui_View_Model_Data *pd = data;
473 Efl_Model_Property_Event *ev = event->info;
474 Efl_Model_Property_Event nev = { 0 };
475 const char *property;
476 Eina_Stringshare *src;
477 Eina_Array_Iterator iterator;
478 unsigned int i;
479
480 if (pd->propagating.property_changed) return ;
481 pd->propagating.property_changed = EINA_TRUE;
482
483 // Our strategy is to rebuild a new Property_Event and cancel the current one.
484 efl_event_callback_stop(event->object);
485
486 nev.changed_properties = eina_array_new(1);
487
488 EINA_ARRAY_ITER_NEXT(ev->changed_properties, i, property, iterator)
489 {
490 eina_array_push(nev.changed_properties, property);
491
492 src = eina_stringshare_ref(property);
493 _efl_ui_view_model_property_bind_lookup(nev.changed_properties, pd, src);
494 }
495
496 efl_event_callback_call(event->object, EFL_MODEL_EVENT_PROPERTIES_CHANGED, &nev);
497
498 eina_array_free(nev.changed_properties);
499
500 pd->propagating.property_changed = EINA_FALSE;
501 }
502
503 static void
_efl_ui_view_model_children_bind_set(Eo * obj EINA_UNUSED,Efl_Ui_View_Model_Data * pd,Eina_Bool enable)504 _efl_ui_view_model_children_bind_set(Eo *obj EINA_UNUSED, Efl_Ui_View_Model_Data *pd, Eina_Bool enable)
505 {
506 if (pd->finalized) return;
507
508 pd->children_bind = enable;
509 }
510
511 static Eina_Bool
_efl_ui_view_model_children_bind_get(const Eo * obj EINA_UNUSED,Efl_Ui_View_Model_Data * pd)512 _efl_ui_view_model_children_bind_get(const Eo *obj EINA_UNUSED, Efl_Ui_View_Model_Data *pd)
513 {
514 return pd->children_bind;
515 }
516
517 static void
_efl_ui_view_model_parent_data(Efl_Ui_View_Model * child,Efl_Ui_View_Model_Data * ppd)518 _efl_ui_view_model_parent_data(Efl_Ui_View_Model *child, Efl_Ui_View_Model_Data *ppd)
519 {
520 Efl_Ui_View_Model_Data *cpd;
521
522 cpd = efl_data_scope_get(child, EFL_UI_VIEW_MODEL_CLASS);
523 cpd->parent = ppd;
524 cpd->propagating = ppd->propagating;
525 }
526
527 static Efl_Ui_View_Model *
_efl_ui_view_model_child_lookup(Efl_Ui_View_Model_Data * pd,Efl_Object * parent,Efl_Model * view)528 _efl_ui_view_model_child_lookup(Efl_Ui_View_Model_Data *pd, Efl_Object *parent, Efl_Model *view)
529 {
530 EFL_COMPOSITE_LOOKUP_RETURN(co, parent, view, "_efl.ui.view_model");
531
532 co = efl_add(EFL_UI_VIEW_MODEL_CLASS, parent,
533 efl_ui_view_model_set(efl_added, view),
534 _efl_ui_view_model_parent_data(efl_added, pd));
535 if (!co) return NULL;
536
537 EFL_COMPOSITE_REMEMBER_RETURN(co, view);
538 }
539
540 static void
_efl_ui_view_model_child_added(void * data,const Efl_Event * event)541 _efl_ui_view_model_child_added(void *data, const Efl_Event *event)
542 {
543 Efl_Model_Children_Event *ev = event->info;
544 Efl_Model_Children_Event nevt = { 0 };
545 Efl_Ui_View_Model_Data *pd = data;
546 Efl_Ui_View_Model *co;
547
548 if (pd->propagating.child_added) return ;
549 if (!pd->children_bind) return;
550 if (!ev->child) return;
551
552 pd->propagating.child_added = EINA_TRUE;
553
554 // Our strategy is to rebuild a new Child_Add and cancel the current one.
555 efl_event_callback_stop(event->object);
556
557 co = _efl_ui_view_model_child_lookup(pd, event->object, ev->child);
558 if (!co) return;
559
560 nevt.index = ev->index;
561 nevt.child = co;
562
563 efl_event_callback_call(event->object, EFL_MODEL_EVENT_CHILD_ADDED, &nevt);
564
565 pd->propagating.child_added = EINA_FALSE;
566 }
567
568 static void
_efl_ui_view_model_child_removed(void * data,const Efl_Event * event)569 _efl_ui_view_model_child_removed(void *data, const Efl_Event *event)
570 {
571 Efl_Model_Children_Event *ev = event->info;
572 Efl_Model_Children_Event nevt = { 0 };
573 Efl_Ui_View_Model_Data *pd = data;
574 Efl_Ui_View_Model *co;
575
576 if (pd->propagating.child_removed) return ;
577 if (!pd->children_bind) return;
578 if (!ev->child) return;
579
580 pd->propagating.child_removed = EINA_TRUE;
581
582 // Our strategy is to rebuild a new Child_Add and cancel the current one.
583 efl_event_callback_stop(event->object);
584
585 co = _efl_ui_view_model_child_lookup(pd, event->object, ev->child);
586 if (!co) return;
587
588 nevt.index = ev->index;
589 nevt.child = co;
590
591 efl_event_callback_call(event->object, EFL_MODEL_EVENT_CHILD_REMOVED, &nevt);
592
593 // The object is being destroyed, there is no point in us keeping the ViewModel proxy alive.
594 efl_del(co);
595
596 pd->propagating.child_removed = EINA_FALSE;
597 }
598
599 EFL_CALLBACKS_ARRAY_DEFINE(efl_ui_view_model_intercept,
600 { EFL_MODEL_EVENT_PROPERTIES_CHANGED, _efl_ui_view_model_property_changed },
601 { EFL_MODEL_EVENT_CHILD_ADDED, _efl_ui_view_model_child_added },
602 { EFL_MODEL_EVENT_CHILD_REMOVED, _efl_ui_view_model_child_removed })
603
604 static Efl_Object *
_efl_ui_view_model_efl_object_constructor(Eo * obj,Efl_Ui_View_Model_Data * pd)605 _efl_ui_view_model_efl_object_constructor(Eo *obj, Efl_Ui_View_Model_Data *pd)
606 {
607 obj = efl_constructor(efl_super(obj, EFL_UI_VIEW_MODEL_CLASS));
608
609 pd->children_bind = EINA_TRUE;
610 pd->bound = eina_hash_stringshared_new(_bind_free);
611 pd->logics = eina_hash_stringshared_new(_logic_free);
612 pd->deduplication = eina_hash_stringshared_new(_ref_free);
613 pd->texts = eina_hash_stringshared_new(_text_free);
614
615 efl_event_callback_array_priority_add(obj, efl_ui_view_model_intercept(), EFL_CALLBACK_PRIORITY_BEFORE, pd);
616
617 return obj;
618 }
619
620 static Efl_Object *
_efl_ui_view_model_efl_object_finalize(Eo * obj,Efl_Ui_View_Model_Data * pd)621 _efl_ui_view_model_efl_object_finalize(Eo *obj, Efl_Ui_View_Model_Data *pd)
622 {
623 pd->finalized = EINA_TRUE;
624
625 return efl_finalize(efl_super(obj, EFL_UI_VIEW_MODEL_CLASS));
626 }
627
628 static void
_efl_ui_view_model_efl_object_destructor(Eo * obj,Efl_Ui_View_Model_Data * pd)629 _efl_ui_view_model_efl_object_destructor(Eo *obj, Efl_Ui_View_Model_Data *pd)
630 {
631 efl_event_callback_array_del(obj, efl_ui_view_model_intercept(), pd);
632
633 eina_hash_free(pd->bound);
634 pd->bound = NULL;
635
636 eina_hash_free(pd->logics);
637 pd->logics = NULL;
638
639 eina_hash_free(pd->texts);
640 pd->texts = NULL;
641
642 eina_hash_free(pd->deduplication);
643 pd->deduplication = NULL;
644
645 efl_destructor(efl_super(obj, EFL_UI_VIEW_MODEL_CLASS));
646 }
647
648 static Efl_Ui_View_Model_Logic *
_efl_ui_view_model_property_logic_lookup(Efl_Ui_View_Model_Data * pd,Eina_Stringshare * property)649 _efl_ui_view_model_property_logic_lookup(Efl_Ui_View_Model_Data *pd, Eina_Stringshare *property)
650 {
651 Efl_Ui_View_Model_Logic *logic;
652
653 if (!pd) return NULL;
654 logic = eina_hash_find(pd->logics, property);
655 if (!logic) return _efl_ui_view_model_property_logic_lookup(pd->parent, property);
656 return logic;
657 }
658
659 static Eina_Future *
_efl_ui_view_model_efl_model_property_set(Eo * obj,Efl_Ui_View_Model_Data * pd,const char * property,Eina_Value * value)660 _efl_ui_view_model_efl_model_property_set(Eo *obj, Efl_Ui_View_Model_Data *pd,
661 const char *property, Eina_Value *value)
662 {
663 Efl_Ui_View_Model_Logic *logic;
664 Eina_Stringshare *prop;
665 Eina_Future *f;
666
667 prop = eina_stringshare_add(property);
668 logic = _efl_ui_view_model_property_logic_lookup(pd, prop);
669 if (logic)
670 f = logic->set.fct(logic->get.data, obj, prop, value);
671 else
672 {
673 if (eina_hash_find(pd->texts, prop))
674 f = efl_loop_future_rejected(obj, EFL_MODEL_ERROR_READ_ONLY);
675 else
676 f = efl_model_property_set(efl_super(obj, EFL_UI_VIEW_MODEL_CLASS), property, value);
677 }
678
679 eina_stringshare_del(prop);
680 return f;
681 }
682
683 static Eina_Value *
_efl_ui_view_model_text_generate(const Eo * obj,Eina_Strbuf * out,Eina_Stringshare * pattern,Eina_Bool stop_on_error)684 _efl_ui_view_model_text_generate(const Eo *obj,
685 Eina_Strbuf *out,
686 Eina_Stringshare *pattern,
687 Eina_Bool stop_on_error)
688 {
689 Eina_Stringshare *st;
690 Eina_Stringshare *sp;
691 int lookup;
692
693 for (lookup = _lookup_next_token(pattern, &st, &sp);
694 lookup;
695 pattern += lookup, lookup = _lookup_next_token(pattern, &st, &sp))
696 {
697 Eina_Value *request;
698 char *sr;
699
700 eina_strbuf_append(out, st);
701
702 if (!sp) continue;
703
704 request = efl_model_property_get(obj, sp);
705 if (!request)
706 {
707 if (stop_on_error)
708 return eina_value_error_new(EFL_MODEL_ERROR_NOT_SUPPORTED);
709 eina_strbuf_append(out, "Unknown property");
710 continue;
711 }
712 if (eina_value_type_get(request) == EINA_VALUE_TYPE_ERROR && stop_on_error)
713 return request;
714
715 sr = eina_value_to_string(request);
716 eina_strbuf_append(out, sr);
717
718 free(sr);
719 eina_value_free(request);
720 }
721
722 return eina_value_string_new(eina_strbuf_string_get(out));
723 }
724
725 static Eina_Value *
_efl_ui_view_model_text_property_get(const Eo * obj,Efl_Ui_View_Model_Data * pd,Eina_Stringshare * prop)726 _efl_ui_view_model_text_property_get(const Eo *obj, Efl_Ui_View_Model_Data *pd, Eina_Stringshare *prop)
727 {
728 Efl_Ui_View_Model_Text *lookup;
729 Eina_Strbuf *buf;
730 Eina_Value *r;
731 Eina_Error err = 0;
732
733 if (!pd) return NULL;
734 lookup = eina_hash_find(pd->texts, prop);
735 // Lookup for property definition in the parent, but property value will be fetched on
736 // the child object doing the request.
737 if (!lookup) return _efl_ui_view_model_text_property_get(obj, pd->parent, prop);
738
739 buf = eina_strbuf_new();
740
741 r = _efl_ui_view_model_text_generate(obj, buf,
742 lookup->definition,
743 !!(lookup->on_error || lookup->not_ready));
744 if (eina_value_type_get(r) != EINA_VALUE_TYPE_ERROR)
745 goto done;
746 if (eina_value_error_get(r, &err) && err == EAGAIN && lookup->not_ready)
747 {
748 eina_strbuf_reset(buf);
749 eina_value_free(r);
750
751 r = _efl_ui_view_model_text_generate(obj, buf, lookup->not_ready, !!lookup->on_error);
752 if (eina_value_type_get(r) != EINA_VALUE_TYPE_ERROR)
753 goto done;
754 }
755 if (lookup->on_error)
756 {
757 eina_strbuf_reset(buf);
758 eina_value_free(r);
759
760 r = _efl_ui_view_model_text_generate(obj, buf, lookup->on_error, 0);
761 }
762
763 done:
764 eina_strbuf_free(buf);
765
766 return r;
767 }
768
769 static Eina_Value *
_efl_ui_view_model_efl_model_property_get(const Eo * obj,Efl_Ui_View_Model_Data * pd,const char * property)770 _efl_ui_view_model_efl_model_property_get(const Eo *obj, Efl_Ui_View_Model_Data *pd,
771 const char *property)
772 {
773 Efl_Ui_View_Model_Logic *logic;
774 Eina_Stringshare *prop;
775 Eina_Value *r;
776
777 prop = eina_stringshare_add(property);
778 logic = _efl_ui_view_model_property_logic_lookup(pd, prop);
779 if (logic)
780 r = logic->get.fct(logic->get.data, obj, prop);
781 else
782 {
783 r = _efl_ui_view_model_text_property_get(obj, pd, prop);
784 if (!r) r = efl_model_property_get(efl_super(obj, EFL_UI_VIEW_MODEL_CLASS), property);
785 }
786
787 eina_stringshare_del(prop);
788 return r;
789 }
790
791 static Eina_Iterator *
_efl_ui_view_model_efl_model_properties_get(const Eo * obj,Efl_Ui_View_Model_Data * pd)792 _efl_ui_view_model_efl_model_properties_get(const Eo *obj, Efl_Ui_View_Model_Data *pd)
793 {
794 EFL_COMPOSITE_MODEL_PROPERTIES_SUPER(props, obj, EFL_UI_VIEW_MODEL_CLASS,
795 eina_hash_iterator_key_new(pd->deduplication));
796
797 return props;
798 }
799
800 typedef struct _Efl_Ui_View_Model_Slice_Request Efl_Ui_View_Model_Slice_Request;
801 struct _Efl_Ui_View_Model_Slice_Request
802 {
803 Efl_Ui_View_Model_Data *pd;
804 unsigned int start;
805 };
806
807 static Eina_Value
_efl_ui_view_model_slice_then(Eo * o,void * data,const Eina_Value v)808 _efl_ui_view_model_slice_then(Eo *o, void *data, const Eina_Value v)
809 {
810 Efl_Ui_View_Model_Slice_Request *req = data;
811 Eo *target;
812 Eina_Value r = EINA_VALUE_EMPTY;
813 unsigned int i, len;
814
815 eina_value_array_setup(&r, EINA_VALUE_TYPE_OBJECT, 4);
816
817 EINA_VALUE_ARRAY_FOREACH(&v, len, i, target)
818 {
819 Eo *composite;
820
821 composite = _efl_ui_view_model_child_lookup(req->pd, o, target);
822 eina_value_array_append(&r, composite);
823 }
824
825 return r;
826 }
827
828 static void
_efl_ui_view_model_slice_clean(Eo * o EINA_UNUSED,void * data,const Eina_Future * dead_future EINA_UNUSED)829 _efl_ui_view_model_slice_clean(Eo *o EINA_UNUSED, void *data, const Eina_Future *dead_future EINA_UNUSED)
830 {
831 free(data);
832 }
833
834 static Eina_Future *
_efl_ui_view_model_efl_model_children_slice_get(Eo * obj,Efl_Ui_View_Model_Data * pd,unsigned int start,unsigned int count)835 _efl_ui_view_model_efl_model_children_slice_get(Eo *obj, Efl_Ui_View_Model_Data *pd,
836 unsigned int start, unsigned int count)
837 {
838 Efl_Ui_View_Model_Slice_Request *req;
839 Eina_Future *f;
840
841 f = efl_model_children_slice_get(efl_super(obj, EFL_UI_VIEW_MODEL_CLASS), start, count);
842
843 req = malloc(sizeof (Efl_Ui_View_Model_Slice_Request));
844 if (!req)
845 {
846 eina_future_cancel(f);
847 return efl_loop_future_rejected(obj, ENOMEM);
848 }
849
850 req->pd = pd;
851 req->start = start;
852
853 return efl_future_then(obj, f, .success_type = EINA_VALUE_TYPE_ARRAY,
854 .success = _efl_ui_view_model_slice_then,
855 .free = _efl_ui_view_model_slice_clean,
856 .data = req);
857 }
858
859 #include "efl_ui_view_model.eo.c"
860