1 #ifdef HAVE_CONFIG_H
2 # include "elementary_config.h"
3 #endif
4 #include <Eo.h>
5 #include <Efl.h>
6 #include <Elementary.h>
7 
8 #include "elm_priv.h"
9 #include "elm_genlist_eo.h"
10 
11 #include <assert.h>
12 
13 #define MY_CLASS ELM_VIEW_LIST_CLASS
14 #define MY_CLASS_NAME "View List"
15 
16 typedef struct _Elm_View_List_Data Elm_View_List_Data;
17 typedef struct _View_List_ItemData View_List_ItemData;
18 typedef struct _View_List_ValueItem View_List_ValueItem;
19 
20 struct _Elm_View_List_Data
21 {
22    Eo *view;
23    Evas_Object *genlist;
24    View_List_ItemData *rootdata;
25    Elm_Genlist_Item_Class *itc;
26    Elm_Genlist_Item_Type itype;
27 
28    struct {
29       Eina_Hash *properties;
30       Eo *model;
31    } connect;
32 };
33 
34 struct _View_List_ItemData
35 {
36    Elm_View_List_Data *priv;
37    Elm_Object_Item *item;
38    Eo *model;
39    View_List_ItemData *parent;
40    Eina_List *values;
41 };
42 
43 struct _View_List_ValueItem
44 {
45    Eina_Stringshare *part;
46    Eina_Value *value;
47    Elm_Object_Item *item;
48 };
49 
50 static void _efl_model_load_children(View_List_ItemData *);
51 static void _efl_model_children_added_cb(void *, const Efl_Event *event);
52 static void _efl_model_children_removed_cb(void *, const Efl_Event *event);
53 static void _efl_model_properties_change_cb(void *, const Efl_Event *event);
54 
55 static void _expand_request_cb(void *data EINA_UNUSED, const Efl_Event *event);
56 static void _contract_request_cb(void *data EINA_UNUSED, const Efl_Event *event);
57 static void _contracted_cb(void *data EINA_UNUSED, const Efl_Event *event);
58 
59 /* --- Genlist Callbacks --- */
60 EFL_CALLBACKS_ARRAY_DEFINE(model_callbacks,
61                            { EFL_MODEL_EVENT_CHILD_ADDED, _efl_model_children_added_cb },
62                            { EFL_MODEL_EVENT_CHILD_REMOVED, _efl_model_children_removed_cb });
63 EFL_CALLBACKS_ARRAY_DEFINE(genlist_callbacks,
64                           { ELM_GENLIST_EVENT_EXPAND_REQUEST, _expand_request_cb },
65                           { ELM_GENLIST_EVENT_CONTRACT_REQUEST, _contract_request_cb },
66                           { ELM_GENLIST_EVENT_CONTRACTED, _contracted_cb });
67 
68 static void
_item_sel_cb(void * data,Evas_Object * obj EINA_UNUSED,void * event_info EINA_UNUSED)69 _item_sel_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
70 {
71    View_List_ItemData *idata = data;
72 
73    EINA_SAFETY_ON_NULL_RETURN(idata);
74 
75    efl_event_callback_legacy_call(idata->priv->view, ELM_VIEW_LIST_EVENT_MODEL_SELECTED, idata->model);
76 }
77 
78 static void
_item_del(void * data,Evas_Object * obj EINA_UNUSED)79 _item_del(void *data, Evas_Object *obj EINA_UNUSED)
80 {
81    View_List_ItemData *idata = data;
82 
83    if (!idata) return;
84 
85    efl_event_callback_array_del(idata->model, model_callbacks(), idata);
86    efl_event_callback_del(idata->model, EFL_MODEL_EVENT_PROPERTIES_CHANGED, _efl_model_properties_change_cb, idata);
87 
88    efl_unref(idata->model);
89    idata->model = NULL;
90    idata->item = NULL;
91    idata->parent = NULL;
92    idata->priv = NULL;
93 
94    free(idata);
95 }
96 
97 static Evas_Object *
_item_content_get(void * data,Evas_Object * obj EINA_UNUSED,const char * part)98 _item_content_get(void *data, Evas_Object *obj EINA_UNUSED, const char *part)
99 {
100    const Eina_Value_Type *vtype;
101    const char *prop;
102    Eina_Value *value = NULL;
103    Evas_Object *content = NULL;
104    View_List_ItemData *idata = data;
105    EINA_SAFETY_ON_NULL_RETURN_VAL(data, NULL);
106    EINA_SAFETY_ON_NULL_RETURN_VAL(part, NULL);
107 
108    if (!idata->item) return NULL;
109 
110    prop = eina_hash_find(idata->priv->connect.properties, part);
111    // If no property are connected, let's not try to guess randomly.
112    if (!prop) return NULL;
113 
114    value = efl_model_property_get(idata->model, prop);
115    if (value == NULL) return NULL;
116 
117    vtype = eina_value_type_get(value);
118    if (vtype == EINA_VALUE_TYPE_BLOB)
119      {
120         Eina_Value_Blob out;
121 
122         eina_value_get(value, &out);
123         if (out.memory != NULL)
124           {
125              content = elm_image_add(obj);
126              //XXX: need copy memory??
127              elm_image_memfile_set(content, out.memory, out.size, NULL, NULL);
128           }
129      }
130    else if (vtype == EINA_VALUE_TYPE_FILE)
131      {
132         Eina_File *f = NULL;
133 
134         eina_value_get(value, &f);
135 
136         content = elm_image_add(obj);
137         elm_image_mmap_set(content, f, NULL);
138      }
139    else if (vtype == EINA_VALUE_TYPE_OBJECT)
140      {
141         eina_value_get(value, &content);
142      }
143    else
144      {
145          char *str = NULL;
146          str = eina_value_to_string(value);
147          content = elm_icon_add(obj);
148          if (elm_icon_standard_set(content, str))
149            {
150                evas_object_size_hint_aspect_set(content, EVAS_ASPECT_CONTROL_VERTICAL, 1, 1);
151            }
152          else
153            {
154                evas_object_del(content);
155                content = NULL;
156            }
157          free(str);
158      }
159    eina_value_free(value);
160 
161    return content;
162 }
163 
164 static char *
_item_text_get(void * data,Evas_Object * obj EINA_UNUSED,const char * part)165 _item_text_get(void *data, Evas_Object *obj EINA_UNUSED, const char *part)
166 {
167    Eina_Value *value = NULL;
168    const char *prop;
169    char *text = NULL;
170    View_List_ItemData *idata = data;
171 
172    EINA_SAFETY_ON_NULL_RETURN_VAL(data, NULL);
173    EINA_SAFETY_ON_NULL_RETURN_VAL(part, NULL);
174    if (!idata->item) return NULL;
175 
176    prop = eina_hash_find(idata->priv->connect.properties, part);
177    if (!prop) prop = part;
178 
179    value = efl_model_property_get(idata->model, prop);
180    if (value == NULL) return NULL;
181 
182    text = eina_value_to_string(value);
183 
184    eina_value_free(value);
185 
186    return text;
187 }
188 
189 static void
_expand_request_cb(void * data EINA_UNUSED,const Efl_Event * event)190 _expand_request_cb(void *data EINA_UNUSED, const Efl_Event *event)
191 {
192    Elm_Object_Item *item = event->info;
193    View_List_ItemData *idata = elm_object_item_data_get(item);
194 
195    EINA_SAFETY_ON_NULL_RETURN(idata);
196 
197    efl_event_callback_array_add(idata->model, model_callbacks(), idata);
198 
199    _efl_model_load_children(idata);
200 }
201 
202 static void
_contract_request_cb(void * data EINA_UNUSED,const Efl_Event * event)203 _contract_request_cb(void *data EINA_UNUSED, const Efl_Event *event)
204 {
205    Elm_Object_Item *item = event->info;
206    View_List_ItemData *idata = elm_object_item_data_get(item);
207 
208    efl_event_callback_array_del(idata->model, model_callbacks(), idata);
209    elm_genlist_item_expanded_set(item, EINA_FALSE);
210 }
211 
212 static void
_contracted_cb(void * data EINA_UNUSED,const Efl_Event * event)213 _contracted_cb(void *data EINA_UNUSED, const Efl_Event *event)
214 {
215    Elm_Object_Item *glit = event->info;
216    elm_genlist_item_subitems_clear(glit);
217 }
218 
219 static void
_genlist_deleted(void * data,Evas * e EINA_UNUSED,Evas_Object * obj,void * event_info EINA_UNUSED)220 _genlist_deleted(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED)
221 {
222    Elm_View_List_Data *priv = data;
223 
224    if (priv && priv->genlist && priv->genlist == obj)
225      {
226         efl_event_callback_array_del(priv->genlist, genlist_callbacks(), priv);
227         efl_unref(priv->genlist);
228         priv->genlist = NULL;
229      }
230 }
231 
232 /* --- Efl_Model Callbacks --- */
233 static void
_efl_model_properties_change_cb(void * data,const Efl_Event * event)234 _efl_model_properties_change_cb(void *data, const Efl_Event *event)
235 {
236    View_List_ItemData *idata = data;
237    Efl_Model_Property_Event *evt = event->info;
238 
239    EINA_SAFETY_ON_NULL_RETURN(idata);
240    EINA_SAFETY_ON_NULL_RETURN(evt);
241 
242    if (idata->item)
243      elm_genlist_item_update(idata->item);
244 }
245 
246 static Eina_Value
_efl_model_load_children_then(void * data,const Eina_Value v,const Eina_Future * ev EINA_UNUSED)247 _efl_model_load_children_then(void *data, const Eina_Value v,
248                               const Eina_Future *ev EINA_UNUSED)
249 {
250    View_List_ItemData *pdata = data;
251    Elm_View_List_Data *priv = pdata->priv;
252    unsigned int i, len;
253    Efl_Model *child = NULL;
254 
255    if (eina_value_type_get(&v) == EINA_VALUE_TYPE_ERROR)
256      goto end;
257 
258    EINA_VALUE_ARRAY_FOREACH(&v, len, i, child)
259      {
260         View_List_ItemData *idata = calloc(1, sizeof(View_List_ItemData));
261         if (!idata) continue ;
262 
263         idata->priv = priv;
264         idata->parent = pdata;
265         idata->model = efl_ref(child);
266 
267         efl_event_callback_add(idata->model, EFL_MODEL_EVENT_PROPERTIES_CHANGED,
268                                _efl_model_properties_change_cb, idata);
269 
270         idata->item = elm_genlist_item_append(priv->genlist, priv->itc, idata, pdata->item,
271                                               priv->itype, _item_sel_cb, idata);
272      }
273 
274    if (i > 0 && pdata->item)
275      elm_genlist_item_expanded_set(pdata->item, EINA_TRUE);
276 
277  end:
278    return v;
279 }
280 
281 static void
_efl_model_load_children(View_List_ItemData * pdata)282 _efl_model_load_children(View_List_ItemData *pdata)
283 {
284    Eina_Future *f;
285 
286    f = efl_model_children_slice_get(pdata->priv->connect.model, 0,
287                                     efl_model_children_count_get(pdata->priv->connect.model));
288    f = eina_future_then(f, _efl_model_load_children_then, pdata, NULL);
289    efl_future_then(pdata->priv->genlist, f);
290 }
291 
292 static void
_efl_model_children_removed_cb(void * data,const Efl_Event * event)293 _efl_model_children_removed_cb(void *data, const Efl_Event *event)
294 {
295    Efl_Model_Children_Event* evt = event->info;
296    View_List_ItemData *idata = data;
297    Elm_Object_Item *item;
298    const Eina_List *subitems, *l;
299    unsigned int i = 0;
300 
301    subitems = elm_genlist_item_subitems_get(idata->item);
302 
303    EINA_LIST_FOREACH(subitems, l, item)
304      {
305         if (i == evt->index) break ;
306         i++;
307      }
308 
309    if (i != evt->index) return ;
310    elm_object_item_del(item);
311 }
312 
313 static void
_efl_model_children_added_cb(void * data,const Efl_Event * event)314 _efl_model_children_added_cb(void *data, const Efl_Event *event)
315 {
316    Efl_Model_Children_Event* evt = event->info;
317    View_List_ItemData *idata = data;
318    Eina_Future *f;
319 
320    f = efl_model_children_slice_get(idata->priv->connect.model, evt->index, 1);
321    f = eina_future_then(f, _efl_model_load_children_then, idata, NULL);
322    efl_future_then(idata->priv->genlist, f);
323 }
324 
325 static void
_priv_model_set(Elm_View_List_Data * priv,Eo * model)326 _priv_model_set(Elm_View_List_Data *priv, Eo *model)
327 {
328    if (priv->connect.model)
329      {
330         efl_event_callback_array_del(priv->connect.model, model_callbacks(), priv->rootdata);
331         elm_obj_genlist_clear(priv->genlist);
332      }
333 
334    efl_replace(&priv->connect.model, model);
335 
336    if (model == NULL) return;
337 
338    priv->rootdata->model = priv->connect.model;
339 
340    efl_event_callback_array_add(priv->connect.model, model_callbacks(), priv->rootdata);
341    _efl_model_load_children(priv->rootdata);
342 }
343 
344 /**
345  * @brief Elm View List Class impl.
346  */
347 static void
_elm_view_list_genlist_set(Eo * obj,Elm_View_List_Data * priv,Evas_Object * genlist,Elm_Genlist_Item_Type itype,const char * istyle)348 _elm_view_list_genlist_set(Eo *obj, Elm_View_List_Data *priv, Evas_Object *genlist,
349                            Elm_Genlist_Item_Type itype, const char *istyle)
350 {
351    priv->view = obj;
352    priv->genlist = genlist;
353    priv->itype = itype;
354    EINA_SAFETY_ON_NULL_RETURN(priv->genlist);
355    efl_ref(priv->genlist);
356 
357    priv->rootdata = calloc(1, sizeof(View_List_ItemData));
358    priv->rootdata->priv = priv;
359 
360    priv->itc = elm_genlist_item_class_new();
361    if (istyle)
362      priv->itc->item_style = strdup(istyle);
363    priv->itc->func.text_get = _item_text_get;
364    priv->itc->func.content_get = _item_content_get;
365    priv->itc->func.state_get = NULL;
366    priv->itc->func.del = _item_del;
367    priv->connect.properties = eina_hash_string_superfast_new(free);
368 
369    efl_event_callback_array_add(priv->genlist, genlist_callbacks(), priv);
370    evas_object_event_callback_add(priv->genlist, EVAS_CALLBACK_DEL, _genlist_deleted, priv);
371 }
372 
373 
374 static void
_elm_view_list_efl_object_destructor(Eo * obj,Elm_View_List_Data * priv)375 _elm_view_list_efl_object_destructor(Eo *obj, Elm_View_List_Data *priv)
376 {
377    EINA_SAFETY_ON_NULL_RETURN(priv);
378    EINA_SAFETY_ON_NULL_RETURN(obj);
379 
380    efl_event_callback_array_del(priv->connect.model, model_callbacks(), priv->rootdata);
381 
382    elm_obj_genlist_clear(priv->genlist);
383    free((void *)priv->itc->item_style);
384    elm_genlist_item_class_free(priv->itc);
385 
386    eina_hash_free(priv->connect.properties);
387    free(priv->rootdata);
388    priv->rootdata = NULL;
389 
390    if (priv->genlist)
391      {
392         evas_object_event_callback_del(priv->genlist, EVAS_CALLBACK_DEL, _genlist_deleted);
393         efl_event_callback_array_del(priv->genlist, genlist_callbacks(), priv);
394         efl_unref(priv->genlist);
395      }
396 
397    efl_unref(priv->connect.model);
398 
399    efl_destructor(efl_super(obj, MY_CLASS));
400 }
401 
402 static void
_elm_view_list_evas_object_get(Eo * obj,Elm_View_List_Data * priv,Evas_Object ** widget)403 _elm_view_list_evas_object_get(Eo *obj, Elm_View_List_Data *priv, Evas_Object **widget)
404 {
405    EINA_SAFETY_ON_NULL_RETURN(priv);
406    EINA_SAFETY_ON_NULL_RETURN(obj);
407    EINA_SAFETY_ON_NULL_RETURN(widget);
408 
409    *widget = priv->genlist;
410 }
411 
412 static void
_elm_view_list_property_connect(Eo * obj EINA_UNUSED,Elm_View_List_Data * priv,const char * property,const char * part)413 _elm_view_list_property_connect(Eo *obj EINA_UNUSED, Elm_View_List_Data *priv,
414                                 const char *property, const char *part)
415 {
416    EINA_SAFETY_ON_NULL_RETURN(priv);
417 
418    EINA_SAFETY_ON_NULL_RETURN(priv->connect.properties);
419    EINA_SAFETY_ON_NULL_RETURN(property);
420    EINA_SAFETY_ON_NULL_RETURN(part);
421 
422    free(eina_hash_set(priv->connect.properties, part, strdup(property)));
423 }
424 
425 static void
_elm_view_list_model_set(Eo * obj EINA_UNUSED,Elm_View_List_Data * priv,Efl_Model * model)426 _elm_view_list_model_set(Eo *obj EINA_UNUSED, Elm_View_List_Data *priv, Efl_Model *model)
427 {
428    _priv_model_set(priv, model);
429 }
430 
431 static Efl_Model *
_elm_view_list_model_get(const Eo * obj EINA_UNUSED,Elm_View_List_Data * priv)432 _elm_view_list_model_get(const Eo *obj EINA_UNUSED, Elm_View_List_Data *priv)
433 {
434    return priv->connect.model;
435 }
436 #include "elm_view_list_eo.c"
437