1 #include <Elementary.h>
2 #include <Elementary_Cursor.h>
5 typedef struct _Elm_Store_Filesystem Elm_Store_Filesystem;
6 typedef struct _Elm_Store_Item_Filesystem Elm_Store_Item_Filesystem;
8 #define ELM_STORE_MAGIC 0x3f89ea56
9 #define ELM_STORE_FILESYSTEM_MAGIC 0x3f89ea57
10 #define ELM_STORE_ITEM_MAGIC 0x5afe8c1d
15 void (*free)(Elm_Store *store);
18 void (*free)(Elm_Store_Item *item);
21 Ecore_Thread *list_th;
30 Elm_Store_Item_List_Cb func;
35 Elm_Store_Item_Fetch_Cb func;
40 Elm_Store_Item_Unfetch_Cb func;
45 Eina_Bool fetch_thread : 1;
48 struct _Elm_Store_Item
53 Elm_Object_Item *item;
54 Ecore_Thread *fetch_th;
56 const Elm_Store_Item_Mapping *mapping;
60 Eina_Bool was_live : 1;
61 Eina_Bool realized : 1;
62 Eina_Bool fetched : 1;
65 struct _Elm_Store_Filesystem
72 struct _Elm_Store_Item_Filesystem
78 static Elm_Genlist_Item_Class _store_item_class;
81 _store_cache_trim(Elm_Store *st)
83 while ((st->realized) &&
84 (((int)eina_list_count(st->realized) - st->realized_count)
87 Elm_Store_Item *sti = st->realized->data;
90 st->realized = eina_list_remove_list(st->realized, st->realized);
91 sti->realized = EINA_FALSE;
93 eina_lock_take(&sti->lock);
96 eina_lock_release(&sti->lock);
99 ecore_thread_cancel(sti->fetch_th);
100 sti->fetch_th = NULL;
102 eina_lock_take(&sti->lock);
104 sti->fetched = EINA_FALSE;
105 //// let fetch/unfetch do the locking
106 // eina_lock_release(&sti->lock);
107 if (st->cb.unfetch.func)
108 st->cb.unfetch.func(st->cb.unfetch.data, sti);
109 // eina_lock_take(&sti->lock);
111 eina_lock_release(&sti->lock);
116 _store_genlist_del(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
118 Elm_Store *st = data;
122 ecore_thread_cancel(st->list_th);
125 st->realized = eina_list_free(st->realized);
128 Elm_Store_Item *sti = (Elm_Store_Item *)st->items;
131 ecore_job_del(sti->eval_job);
132 sti->eval_job = NULL;
136 ecore_thread_cancel(sti->fetch_th);
137 sti->fetch_th = NULL;
139 if (sti->store->item.free) sti->store->item.free(sti);
140 eina_lock_take(&sti->lock);
143 if (st->cb.unfetch.func)
144 st->cb.unfetch.func(st->cb.unfetch.data, sti);
147 eina_lock_release(&sti->lock);
148 eina_lock_free(&sti->lock);
152 // FIXME: kill threads and more
155 ////// **** WARNING ***********************************************************
156 //// * This function runs inside a thread outside efl mainloop. Be careful! *
157 // ************************************************************************
158 /* TODO: refactor lock part into core? this does not depend on filesystm part */
160 _store_filesystem_fetch_do(void *data, Ecore_Thread *th __UNUSED__)
162 Elm_Store_Item *sti = data;
163 eina_lock_take(&sti->lock);
166 eina_lock_release(&sti->lock);
171 //// let fetch/unfetch do the locking
172 // eina_lock_release(&sti->lock);
173 if (sti->store->cb.fetch.func)
174 sti->store->cb.fetch.func(sti->store->cb.fetch.data, sti);
175 // eina_lock_take(&sti->lock);
176 sti->fetched = EINA_TRUE;
178 eina_lock_release(&sti->lock);
180 // ************************************************************************
181 //// * End of separate thread function. *
182 ////// ************************************************************************
183 /* TODO: refactor lock part into core? this does not depend on filesystm part */
185 _store_filesystem_fetch_end(void *data, Ecore_Thread *th)
187 Elm_Store_Item *sti = data;
188 eina_lock_take(&sti->lock);
189 if (sti->data) elm_genlist_item_update(sti->item);
190 eina_lock_release(&sti->lock);
191 if (th == sti->fetch_th) sti->fetch_th = NULL;
194 /* TODO: refactor lock part into core? this does not depend on filesystm part */
196 _store_filesystem_fetch_cancel(void *data, Ecore_Thread *th)
198 Elm_Store_Item *sti = data;
199 eina_lock_take(&sti->lock);
200 if (th == sti->fetch_th) sti->fetch_th = NULL;
201 if (sti->data) elm_genlist_item_update(sti->item);
202 eina_lock_release(&sti->lock);
206 _store_item_eval(void *data)
208 Elm_Store_Item *sti = data;
209 sti->eval_job = NULL;
210 if (sti->live == sti->was_live) return;
211 sti->was_live = sti->live;
214 _store_cache_trim(sti->store);
216 sti->store->realized = eina_list_remove(sti->store->realized, sti);
217 sti->store->realized = eina_list_append(sti->store->realized, sti);
218 sti->realized = EINA_TRUE;
219 if ((sti->store->fetch_thread) && (!sti->fetch_th))
220 sti->fetch_th = ecore_thread_run(_store_filesystem_fetch_do,
221 _store_filesystem_fetch_end,
222 _store_filesystem_fetch_cancel,
224 else if ((!sti->store->fetch_thread))
226 _store_filesystem_fetch_do(sti, NULL);
227 _store_filesystem_fetch_end(sti, NULL);
234 ecore_thread_cancel(sti->fetch_th);
235 sti->fetch_th = NULL;
237 _store_cache_trim(sti->store);
242 _store_genlist_item_realized(void *data, Evas_Object *obj __UNUSED__, void *event_info)
244 Elm_Store *st = data;
245 Elm_Object_Item *gli = event_info;
246 Elm_Store_Item *sti = elm_object_item_data_get(gli);
248 st->realized_count++;
249 sti->live = EINA_TRUE;
250 if (sti->eval_job) ecore_job_del(sti->eval_job);
251 sti->eval_job = ecore_job_add(_store_item_eval, sti);
255 _store_genlist_item_unrealized(void *data, Evas_Object *obj __UNUSED__, void *event_info)
257 Elm_Store *st = data;
258 Elm_Object_Item *gli = event_info;
259 Elm_Store_Item *sti = elm_object_item_data_get(gli);
261 st->realized_count--;
262 sti->live = EINA_FALSE;
263 if (sti->eval_job) ecore_job_del(sti->eval_job);
264 sti->eval_job = ecore_job_add(_store_item_eval, sti);
267 static const Elm_Store_Item_Mapping *
268 _store_item_mapping_find(Elm_Store_Item *sti, const char *part)
270 const Elm_Store_Item_Mapping *m;
272 for (m = sti->mapping; m; m ++)
274 if (m->type == ELM_STORE_ITEM_MAPPING_NONE) break;
275 if (!strcmp(part, m->part)) return m;
281 _store_item_text_get(void *data, Evas_Object *obj __UNUSED__, const char *part)
283 Elm_Store_Item *sti = data;
285 eina_lock_take(&sti->lock);
288 const Elm_Store_Item_Mapping *m = _store_item_mapping_find(sti, part);
293 case ELM_STORE_ITEM_MAPPING_LABEL:
294 s = *(char **)(((unsigned char *)sti->data) + m->offset);
296 case ELM_STORE_ITEM_MAPPING_CUSTOM:
297 if (m->details.custom.func)
298 s = m->details.custom.func(sti->data, sti, part);
305 eina_lock_release(&sti->lock);
306 return s ? strdup(s) : NULL;
310 _store_item_content_get(void *data, Evas_Object *obj, const char *part)
312 Elm_Store_Item *sti = data;
313 eina_lock_take(&sti->lock);
316 const Elm_Store_Item_Mapping *m = _store_item_mapping_find(sti, part);
319 Evas_Object *ic = NULL;
320 const char *s = NULL;
324 case ELM_STORE_ITEM_MAPPING_ICON:
325 ic = elm_icon_add(obj);
326 s = *(char **)(((unsigned char *)sti->data) + m->offset);
327 elm_icon_order_lookup_set(ic, m->details.icon.lookup_order);
328 evas_object_size_hint_aspect_set(ic,
329 EVAS_ASPECT_CONTROL_VERTICAL,
332 elm_image_smooth_set(ic, m->details.icon.smooth);
333 elm_image_no_scale_set(ic, m->details.icon.no_scale);
334 elm_image_resizable_set(ic,
335 m->details.icon.scale_up,
336 m->details.icon.scale_down);
339 if (m->details.icon.standard_name)
340 elm_icon_standard_set(ic, s);
342 elm_image_file_set(ic, s, NULL);
345 case ELM_STORE_ITEM_MAPPING_PHOTO:
346 ic = elm_icon_add(obj);
347 s = *(char **)(((unsigned char *)sti->data) + m->offset);
348 elm_photo_size_set(ic, m->details.photo.size);
350 elm_photo_file_set(ic, s);
352 case ELM_STORE_ITEM_MAPPING_CUSTOM:
353 if (m->details.custom.func)
354 ic = m->details.custom.func(sti->data, sti, part);
359 eina_lock_release(&sti->lock);
363 eina_lock_release(&sti->lock);
368 _store_item_del(void *data __UNUSED__, Evas_Object *obj __UNUSED__)
372 ////// **** WARNING ***********************************************************
373 //// * This function runs inside a thread outside efl mainloop. Be careful! *
374 // ************************************************************************
376 _store_filesystem_sort_cb(void *d1, void *d2)
378 Elm_Store_Item_Info *info1 = d1, *info2 = d2;
379 if ((!info1->sort_id) || (!info2->sort_id)) return 0;
380 return strcoll(info1->sort_id, info2->sort_id);
384 _store_filesystem_list_do(void *data, Ecore_Thread *th __UNUSED__)
386 Elm_Store_Filesystem *st = data;
388 const Eina_File_Direct_Info *finf;
389 Eina_List *sorted = NULL;
390 Elm_Store_Item_Info_Filesystem *info;
392 // FIXME: need a way to abstract the open, list, feed items from list
393 // and maybe get initial sortable key vals etc.
394 it = eina_file_stat_ls(st->dir);
396 EINA_ITERATOR_FOREACH(it, finf)
399 size_t pathsz = finf->path_length + 1;
401 if (finf->path[finf->name_start] == '.') continue ;
403 info = calloc(1, sizeof(Elm_Store_Item_Info_Filesystem) + pathsz);
405 info->path = ((char *)info) + sizeof(Elm_Store_Item_Info_Filesystem);
406 memcpy(info->path, finf->path, pathsz);
408 if (st->base.cb.list.func)
409 ok = st->base.cb.list.func(st->base.cb.list.data, &info->base);
412 if (!st->base.sorted) ecore_thread_feedback(th, info);
413 else sorted = eina_list_append(sorted, info);
417 if (info->base.sort_id) free(info->base.sort_id);
420 if (ecore_thread_check(th)) break;
422 eina_iterator_free(it);
425 sorted = eina_list_sort(sorted, 0,
426 EINA_COMPARE_CB(_store_filesystem_sort_cb));
427 EINA_LIST_FREE(sorted, info)
429 if (!ecore_thread_check(th)) ecore_thread_feedback(th, info);
433 // ************************************************************************
434 //// * End of separate thread function. *
435 ////// ************************************************************************
438 _store_filesystem_list_end(void *data, Ecore_Thread *th)
440 Elm_Store *st = data;
441 if (th == st->list_th) st->list_th = NULL;
445 _store_filesystem_list_cancel(void *data, Ecore_Thread *th)
447 Elm_Store *st = data;
448 if (th == st->list_th) st->list_th = NULL;
452 _store_filesystem_list_update(void *data, Ecore_Thread *th __UNUSED__, void *msg)
454 Elm_Store *st = data;
455 Elm_Store_Item_Filesystem *sti;
456 Elm_Genlist_Item_Class *itc;
457 Elm_Store_Item_Info_Filesystem *info = msg;
459 sti = calloc(1, sizeof(Elm_Store_Item_Filesystem));
461 eina_lock_new(&sti->base.lock);
462 EINA_MAGIC_SET(&(sti->base), ELM_STORE_ITEM_MAGIC);
463 sti->base.store = st;
464 sti->base.data = info->base.data;
465 sti->base.mapping = info->base.mapping;
466 sti->path = eina_stringshare_add(info->path);
468 itc = info->base.item_class;
469 if (!itc) itc = &_store_item_class;
472 itc->func.text_get = _store_item_text_get;
473 itc->func.content_get = _store_item_content_get;
474 itc->func.state_get = NULL; // FIXME: support state gets later
475 itc->func.del = _store_item_del;
478 // FIXME: handle being a parent (tree)
479 sti->base.item = elm_genlist_item_append(st->genlist, itc,
482 ELM_GENLIST_ITEM_NONE,
484 NULL/* func data */);
485 st->items = eina_inlist_append(st->items, (Eina_Inlist *)sti);
487 if (info->base.sort_id) free(info->base.sort_id);
493 _elm_store_new(size_t size)
495 Elm_Store *st = calloc(1, size);
496 EINA_SAFETY_ON_NULL_RETURN_VAL(st, NULL);
498 // TODO: BEGIN - move to elm_store_init()
499 eina_magic_string_set(ELM_STORE_MAGIC, "Elm_Store");
500 eina_magic_string_set(ELM_STORE_FILESYSTEM_MAGIC, "Elm_Store_Filesystem");
501 eina_magic_string_set(ELM_STORE_ITEM_MAGIC, "Elm_Store_Item");
502 // setup default item class (always the same) if list cb doesnt provide one
503 _store_item_class.item_style = "default";
504 _store_item_class.func.text_get = _store_item_text_get;
505 _store_item_class.func.content_get = _store_item_content_get;
506 _store_item_class.func.state_get = NULL; // FIXME: support state gets later
507 _store_item_class.func.del = _store_item_del;
508 // TODO: END - move to elm_store_init()
510 EINA_MAGIC_SET(st, ELM_STORE_MAGIC);
512 st->fetch_thread = EINA_TRUE;
515 #define elm_store_new(type) (type*)_elm_store_new(sizeof(type))
518 _elm_store_filesystem_free(Elm_Store *store)
520 Elm_Store_Filesystem *st = (Elm_Store_Filesystem *)store;
521 eina_stringshare_del(st->dir);
525 _elm_store_filesystem_item_free(Elm_Store_Item *item)
527 Elm_Store_Item_Filesystem *sti = (Elm_Store_Item_Filesystem *)item;
528 eina_stringshare_del(sti->path);
532 elm_store_filesystem_new(void)
534 Elm_Store_Filesystem *st = elm_store_new(Elm_Store_Filesystem);
535 EINA_SAFETY_ON_NULL_RETURN_VAL(st, NULL);
537 EINA_MAGIC_SET(st, ELM_STORE_FILESYSTEM_MAGIC);
538 st->base.free = _elm_store_filesystem_free;
539 st->base.item.free = _elm_store_filesystem_item_free;
545 elm_store_free(Elm_Store *st)
547 void (*item_free)(Elm_Store_Item *);
548 if (!EINA_MAGIC_CHECK(st, ELM_STORE_MAGIC)) return;
551 ecore_thread_cancel(st->list_th);
554 st->realized = eina_list_free(st->realized);
555 item_free = st->item.free;
558 Elm_Store_Item *sti = (Elm_Store_Item *)st->items;
561 ecore_job_del(sti->eval_job);
562 sti->eval_job = NULL;
566 ecore_thread_cancel(sti->fetch_th);
567 sti->fetch_th = NULL;
569 if (item_free) item_free(sti);
570 eina_lock_take(&sti->lock);
573 if (st->cb.unfetch.func)
574 st->cb.unfetch.func(st->cb.unfetch.data, sti);
577 eina_lock_release(&sti->lock);
578 eina_lock_free(&sti->lock);
583 evas_object_event_callback_del_full(st->genlist, EVAS_CALLBACK_DEL, _store_genlist_del, st);
584 evas_object_smart_callback_del(st->genlist, "realized", _store_genlist_item_realized);
585 evas_object_smart_callback_del(st->genlist, "unrealized", _store_genlist_item_unrealized);
586 elm_genlist_clear(st->genlist);
589 if (st->free) st->free(st);
594 elm_store_target_genlist_set(Elm_Store *st, Evas_Object *obj)
596 if (!EINA_MAGIC_CHECK(st, ELM_STORE_MAGIC)) return;
597 if (st->genlist == obj) return;
600 evas_object_event_callback_del_full(st->genlist, EVAS_CALLBACK_DEL, _store_genlist_del, st);
601 evas_object_smart_callback_del(st->genlist, "realized", _store_genlist_item_realized);
602 evas_object_smart_callback_del(st->genlist, "unrealized", _store_genlist_item_unrealized);
603 elm_genlist_clear(st->genlist);
606 if (!st->genlist) return;
607 evas_object_smart_callback_add(st->genlist, "realized", _store_genlist_item_realized, st);
608 evas_object_smart_callback_add(st->genlist, "unrealized", _store_genlist_item_unrealized, st);
609 evas_object_event_callback_add(st->genlist, EVAS_CALLBACK_DEL, _store_genlist_del, st);
610 elm_genlist_clear(st->genlist);
614 elm_store_filesystem_directory_set(Elm_Store *store, const char *dir)
616 Elm_Store_Filesystem *st = (Elm_Store_Filesystem *)store;
617 if (!EINA_MAGIC_CHECK(store, ELM_STORE_MAGIC)) return;
618 if (!EINA_MAGIC_CHECK(st, ELM_STORE_FILESYSTEM_MAGIC)) return;
621 ecore_thread_cancel(store->list_th);
622 store->list_th = NULL;
624 if (!eina_stringshare_replace(&st->dir, dir)) return;
625 store->list_th = ecore_thread_feedback_run(_store_filesystem_list_do,
626 _store_filesystem_list_update,
627 _store_filesystem_list_end,
628 _store_filesystem_list_cancel,
633 elm_store_filesystem_directory_get(const Elm_Store *store)
635 const Elm_Store_Filesystem *st = (const Elm_Store_Filesystem *)store;
636 if (!EINA_MAGIC_CHECK(store, ELM_STORE_MAGIC)) return NULL;
637 if (!EINA_MAGIC_CHECK(st, ELM_STORE_FILESYSTEM_MAGIC)) return NULL;
642 elm_store_cache_set(Elm_Store *st, int max)
644 if (!EINA_MAGIC_CHECK(st, ELM_STORE_MAGIC)) return;
645 if (max < 0) max = 0;
647 _store_cache_trim(st);
651 elm_store_cache_get(const Elm_Store *st)
653 if (!EINA_MAGIC_CHECK(st, ELM_STORE_MAGIC)) return 0;
654 return st->cache_max;
658 elm_store_list_func_set(Elm_Store *st, Elm_Store_Item_List_Cb func, const void *data)
660 if (!EINA_MAGIC_CHECK(st, ELM_STORE_MAGIC)) return;
661 st->cb.list.func = func;
662 st->cb.list.data = (void *)data;
666 elm_store_fetch_func_set(Elm_Store *st, Elm_Store_Item_Fetch_Cb func, const void *data)
668 if (!EINA_MAGIC_CHECK(st, ELM_STORE_MAGIC)) return;
669 st->cb.fetch.func = func;
670 st->cb.fetch.data = (void *)data;
674 elm_store_fetch_thread_set(Elm_Store *st, Eina_Bool use_thread)
676 if (!EINA_MAGIC_CHECK(st, ELM_STORE_MAGIC)) return;
677 st->fetch_thread = !!use_thread;
681 elm_store_fetch_thread_get(const Elm_Store *st)
683 if (!EINA_MAGIC_CHECK(st, ELM_STORE_MAGIC)) return EINA_FALSE;
684 return st->fetch_thread;
688 elm_store_unfetch_func_set(Elm_Store *st, Elm_Store_Item_Unfetch_Cb func, const void *data)
690 if (!EINA_MAGIC_CHECK(st, ELM_STORE_MAGIC)) return;
691 st->cb.unfetch.func = func;
692 st->cb.unfetch.data = (void *)data;
696 elm_store_sorted_set(Elm_Store *st, Eina_Bool sorted)
698 if (!EINA_MAGIC_CHECK(st, ELM_STORE_MAGIC)) return;
703 elm_store_sorted_get(const Elm_Store *st)
705 if (!EINA_MAGIC_CHECK(st, ELM_STORE_MAGIC)) return EINA_FALSE;
710 elm_store_item_data_set(Elm_Store_Item *sti, void *data)
712 if (!EINA_MAGIC_CHECK(sti, ELM_STORE_ITEM_MAGIC)) return;
713 //// let fetch/unfetch do the locking
714 // eina_lock_take(&sti->lock);
716 // eina_lock_release(&sti->lock);
720 elm_store_item_data_get(Elm_Store_Item *sti)
722 if (!EINA_MAGIC_CHECK(sti, ELM_STORE_ITEM_MAGIC)) return NULL;
724 //// let fetch/unfetch do the locking
725 // eina_lock_take(&sti->lock);
727 // eina_lock_release(&sti->lock);
731 EAPI const Elm_Store *
732 elm_store_item_store_get(const Elm_Store_Item *sti)
734 if (!EINA_MAGIC_CHECK(sti, ELM_STORE_ITEM_MAGIC)) return NULL;
739 EAPI const Elm_Object_Item *
740 elm_store_item_genlist_item_get(const Elm_Store_Item *sti)
742 if (!EINA_MAGIC_CHECK(sti, ELM_STORE_ITEM_MAGIC)) return NULL;
748 elm_store_item_filesystem_path_get(const Elm_Store_Item *item)
750 Elm_Store_Item_Filesystem *sti = (Elm_Store_Item_Filesystem *)item;
751 Elm_Store_Filesystem *st;
752 if (!EINA_MAGIC_CHECK(item, ELM_STORE_ITEM_MAGIC)) return NULL;
753 if (!EINA_MAGIC_CHECK(item->store, ELM_STORE_MAGIC)) return NULL;
754 /* ensure we're dealing with filesystem item */
755 st = (Elm_Store_Filesystem *)item->store;
756 if (!EINA_MAGIC_CHECK(st, ELM_STORE_FILESYSTEM_MAGIC)) return NULL;