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