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);
17 void (*free)(Elm_Store_Item *item);
20 Ecore_Thread *list_th;
27 Elm_Store_Item_List_Cb func;
31 Elm_Store_Item_Fetch_Cb func;
35 Elm_Store_Item_Unfetch_Cb func;
40 Eina_Bool fetch_thread : 1;
43 struct _Elm_Store_Item
48 Elm_Object_Item *item;
49 Ecore_Thread *fetch_th;
51 const Elm_Store_Item_Mapping *mapping;
55 Eina_Bool was_live : 1;
56 Eina_Bool realized : 1;
57 Eina_Bool fetched : 1;
60 struct _Elm_Store_Filesystem
67 struct _Elm_Store_Item_Filesystem
73 static Elm_Genlist_Item_Class _store_item_class;
76 _store_cache_trim(Elm_Store *st)
78 while ((st->realized ) &&
79 (((int)eina_list_count(st->realized) - st->realized_count)
82 Elm_Store_Item *sti = st->realized->data;
85 st->realized = eina_list_remove_list(st->realized, st->realized);
86 sti->realized = EINA_FALSE;
88 eina_lock_take(&sti->lock);
91 eina_lock_release(&sti->lock);
94 ecore_thread_cancel(sti->fetch_th);
97 eina_lock_take(&sti->lock);
99 sti->fetched = EINA_FALSE;
100 //// let fetch/unfetch do the locking
101 // eina_lock_release(&sti->lock);
102 if (st->cb.unfetch.func)
103 st->cb.unfetch.func(st->cb.unfetch.data, sti);
104 // eina_lock_take(&sti->lock);
106 eina_lock_release(&sti->lock);
111 _store_genlist_del(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
113 Elm_Store *st = data;
117 ecore_thread_cancel(st->list_th);
120 eina_list_free(st->realized);
123 Elm_Store_Item *sti = (Elm_Store_Item *)st->items;
124 if (sti->eval_job) ecore_job_del(sti->eval_job);
127 ecore_thread_cancel(sti->fetch_th);
128 sti->fetch_th = NULL;
130 if (sti->store->item.free) sti->store->item.free(sti);
131 eina_lock_take(&sti->lock);
134 if (st->cb.unfetch.func)
135 st->cb.unfetch.func(st->cb.unfetch.data, sti);
138 eina_lock_release(&sti->lock);
139 eina_lock_free(&sti->lock);
143 // FIXME: kill threads and more
146 ////// **** WARNING ***********************************************************
147 //// * This function runs inside a thread outside efl mainloop. Be careful! *
148 // ************************************************************************
149 /* TODO: refactor lock part into core? this does not depend on filesystm part */
151 _store_filesystem_fetch_do(void *data, Ecore_Thread *th __UNUSED__)
153 Elm_Store_Item *sti = data;
154 eina_lock_take(&sti->lock);
157 eina_lock_release(&sti->lock);
162 //// let fetch/unfetch do the locking
163 // eina_lock_release(&sti->lock);
164 if (sti->store->cb.fetch.func)
165 sti->store->cb.fetch.func(sti->store->cb.fetch.data, sti);
166 // eina_lock_take(&sti->lock);
167 sti->fetched = EINA_TRUE;
169 eina_lock_release(&sti->lock);
171 // ************************************************************************
172 //// * End of separate thread function. *
173 ////// ************************************************************************
174 /* TODO: refactor lock part into core? this does not depend on filesystm part */
176 _store_filesystem_fetch_end(void *data, Ecore_Thread *th)
178 Elm_Store_Item *sti = data;
179 eina_lock_take(&sti->lock);
180 if (sti->data) elm_genlist_item_update(sti->item);
181 eina_lock_release(&sti->lock);
182 if (th == sti->fetch_th) sti->fetch_th = NULL;
185 /* TODO: refactor lock part into core? this does not depend on filesystm part */
187 _store_filesystem_fetch_cancel(void *data, Ecore_Thread *th)
189 Elm_Store_Item *sti = data;
190 eina_lock_take(&sti->lock);
191 if (th == sti->fetch_th) sti->fetch_th = NULL;
192 if (sti->data) elm_genlist_item_update(sti->item);
193 eina_lock_release(&sti->lock);
197 _store_item_eval(void *data)
199 Elm_Store_Item *sti = data;
200 sti->eval_job = NULL;
201 if (sti->live == sti->was_live) return;
202 sti->was_live = sti->live;
205 _store_cache_trim(sti->store);
207 sti->store->realized = eina_list_remove(sti->store->realized, sti);
208 sti->store->realized = eina_list_append(sti->store->realized, sti);
209 sti->realized = EINA_TRUE;
210 if ((sti->store->fetch_thread) && (!sti->fetch_th))
211 sti->fetch_th = ecore_thread_run(_store_filesystem_fetch_do,
212 _store_filesystem_fetch_end,
213 _store_filesystem_fetch_cancel,
215 else if ((!sti->store->fetch_thread))
217 _store_filesystem_fetch_do(sti, NULL);
218 _store_filesystem_fetch_end(sti, NULL);
225 ecore_thread_cancel(sti->fetch_th);
226 sti->fetch_th = NULL;
228 _store_cache_trim(sti->store);
233 _store_genlist_item_realized(void *data, Evas_Object *obj __UNUSED__, void *event_info)
235 Elm_Store *st = data;
236 Elm_Object_Item *gli = event_info;
237 Elm_Store_Item *sti = elm_object_item_data_get(gli);
239 st->realized_count++;
240 sti->live = EINA_TRUE;
241 if (sti->eval_job) ecore_job_del(sti->eval_job);
242 sti->eval_job = ecore_job_add(_store_item_eval, sti);
246 _store_genlist_item_unrealized(void *data, Evas_Object *obj __UNUSED__, void *event_info)
248 Elm_Store *st = data;
249 Elm_Object_Item *gli = event_info;
250 Elm_Store_Item *sti = elm_object_item_data_get(gli);
252 st->realized_count--;
253 sti->live = EINA_FALSE;
254 if (sti->eval_job) ecore_job_del(sti->eval_job);
255 sti->eval_job = ecore_job_add(_store_item_eval, sti);
258 static const Elm_Store_Item_Mapping *
259 _store_item_mapping_find(Elm_Store_Item *sti, const char *part)
261 const Elm_Store_Item_Mapping *m;
263 for (m = sti->mapping; m; m ++)
265 if (m->type == ELM_STORE_ITEM_MAPPING_NONE) break;
266 if (!strcmp(part, m->part)) return m;
272 _store_item_text_get(void *data, Evas_Object *obj __UNUSED__, const char *part)
274 Elm_Store_Item *sti = data;
276 eina_lock_take(&sti->lock);
279 const Elm_Store_Item_Mapping *m = _store_item_mapping_find(sti, part);
284 case ELM_STORE_ITEM_MAPPING_LABEL:
285 s = *(char **)(((unsigned char *)sti->data) + m->offset);
287 case ELM_STORE_ITEM_MAPPING_CUSTOM:
288 if (m->details.custom.func)
289 s = m->details.custom.func(sti->data, sti, part);
296 eina_lock_release(&sti->lock);
297 return s ? strdup(s) : NULL;
301 _store_item_content_get(void *data, Evas_Object *obj, const char *part)
303 Elm_Store_Item *sti = data;
304 eina_lock_take(&sti->lock);
307 const Elm_Store_Item_Mapping *m = _store_item_mapping_find(sti, part);
310 Evas_Object *ic = NULL;
311 const char *s = NULL;
315 case ELM_STORE_ITEM_MAPPING_ICON:
316 ic = elm_icon_add(obj);
317 s = *(char **)(((unsigned char *)sti->data) + m->offset);
318 elm_icon_order_lookup_set(ic, m->details.icon.lookup_order);
319 evas_object_size_hint_aspect_set(ic,
320 EVAS_ASPECT_CONTROL_VERTICAL,
323 elm_icon_smooth_set(ic, m->details.icon.smooth);
324 elm_icon_no_scale_set(ic, m->details.icon.no_scale);
325 elm_icon_resizable_set(ic,
326 m->details.icon.scale_up,
327 m->details.icon.scale_down);
330 if (m->details.icon.standard_name)
331 elm_icon_standard_set(ic, s);
333 elm_icon_file_set(ic, s, NULL);
336 case ELM_STORE_ITEM_MAPPING_PHOTO:
337 ic = elm_icon_add(obj);
338 s = *(char **)(((unsigned char *)sti->data) + m->offset);
339 elm_photo_size_set(ic, m->details.photo.size);
341 elm_photo_file_set(ic, s);
343 case ELM_STORE_ITEM_MAPPING_CUSTOM:
344 if (m->details.custom.func)
345 ic = m->details.custom.func(sti->data, sti, part);
350 eina_lock_release(&sti->lock);
354 eina_lock_release(&sti->lock);
359 _store_item_del(void *data __UNUSED__, Evas_Object *obj __UNUSED__)
363 ////// **** WARNING ***********************************************************
364 //// * This function runs inside a thread outside efl mainloop. Be careful! *
365 // ************************************************************************
367 _store_filesystem_sort_cb(void *d1, void *d2)
369 Elm_Store_Item_Info *info1 = d1, *info2 = d2;
370 if ((!info1->sort_id) || (!info2->sort_id)) return 0;
371 return strcoll(info1->sort_id, info2->sort_id);
375 _store_filesystem_list_do(void *data, Ecore_Thread *th __UNUSED__)
377 Elm_Store_Filesystem *st = data;
379 const Eina_File_Direct_Info *finf;
380 Eina_List *sorted = NULL;
381 Elm_Store_Item_Info_Filesystem *info;
383 // FIXME: need a way to abstract the open, list, feed items from list
384 // and maybe get initial sortable key vals etc.
385 it = eina_file_stat_ls(st->dir);
387 EINA_ITERATOR_FOREACH(it, finf)
390 size_t pathsz = finf->path_length + 1;
392 if (finf->path[finf->name_start] == '.') continue ;
394 info = calloc(1, sizeof(Elm_Store_Item_Info_Filesystem) + pathsz);
396 info->path = ((char *)info) + sizeof(Elm_Store_Item_Info_Filesystem);
397 memcpy(info->path, finf->path, pathsz);
399 if (st->base.cb.list.func)
400 ok = st->base.cb.list.func(st->base.cb.list.data, &info->base);
403 if (!st->base.sorted) ecore_thread_feedback(th, info);
404 else sorted = eina_list_append(sorted, info);
408 if (info->base.sort_id) free(info->base.sort_id);
411 if (ecore_thread_check(th)) break;
413 eina_iterator_free(it);
416 sorted = eina_list_sort(sorted, 0,
417 EINA_COMPARE_CB(_store_filesystem_sort_cb));
418 EINA_LIST_FREE(sorted, info)
420 if (!ecore_thread_check(th)) ecore_thread_feedback(th, info);
424 // ************************************************************************
425 //// * End of separate thread function. *
426 ////// ************************************************************************
429 _store_filesystem_list_end(void *data, Ecore_Thread *th)
431 Elm_Store *st = data;
432 if (th == st->list_th) st->list_th = NULL;
436 _store_filesystem_list_cancel(void *data, Ecore_Thread *th)
438 Elm_Store *st = data;
439 if (th == st->list_th) st->list_th = NULL;
443 _store_filesystem_list_update(void *data, Ecore_Thread *th __UNUSED__, void *msg)
445 Elm_Store *st = data;
446 Elm_Store_Item_Filesystem *sti;
447 Elm_Genlist_Item_Class *itc;
448 Elm_Store_Item_Info_Filesystem *info = msg;
450 sti = calloc(1, sizeof(Elm_Store_Item_Filesystem));
452 eina_lock_new(&sti->base.lock);
453 EINA_MAGIC_SET(&(sti->base), ELM_STORE_ITEM_MAGIC);
454 sti->base.store = st;
455 sti->base.data = info->base.data;
456 sti->base.mapping = info->base.mapping;
457 sti->path = eina_stringshare_add(info->path);
459 itc = info->base.item_class;
460 if (!itc) itc = &_store_item_class;
463 itc->func.text_get = _store_item_text_get;
464 itc->func.content_get = _store_item_content_get;
465 itc->func.state_get = NULL; // FIXME: support state gets later
466 itc->func.del = _store_item_del;
469 // FIXME: handle being a parent (tree)
470 sti->base.item = elm_genlist_item_append(st->genlist, itc,
473 ELM_GENLIST_ITEM_NONE,
475 NULL/* func data */);
476 st->items = eina_inlist_append(st->items, (Eina_Inlist *)sti);
478 if (info->base.sort_id) free(info->base.sort_id);
484 _elm_store_new(size_t size)
486 Elm_Store *st = calloc(1, size);
487 EINA_SAFETY_ON_NULL_RETURN_VAL(st, NULL);
489 // TODO: BEGIN - move to elm_store_init()
490 eina_magic_string_set(ELM_STORE_MAGIC, "Elm_Store");
491 eina_magic_string_set(ELM_STORE_FILESYSTEM_MAGIC, "Elm_Store_Filesystem");
492 eina_magic_string_set(ELM_STORE_ITEM_MAGIC, "Elm_Store_Item");
493 // setup default item class (always the same) if list cb doesnt provide one
494 _store_item_class.item_style = "default";
495 _store_item_class.func.text_get = _store_item_text_get;
496 _store_item_class.func.content_get = _store_item_content_get;
497 _store_item_class.func.state_get = NULL; // FIXME: support state gets later
498 _store_item_class.func.del = _store_item_del;
499 // TODO: END - move to elm_store_init()
501 EINA_MAGIC_SET(st, ELM_STORE_MAGIC);
503 st->fetch_thread = EINA_TRUE;
506 #define elm_store_new(type) (type*)_elm_store_new(sizeof(type))
509 _elm_store_filesystem_free(Elm_Store *store)
511 Elm_Store_Filesystem *st = (Elm_Store_Filesystem *)store;
512 eina_stringshare_del(st->dir);
516 _elm_store_filesystem_item_free(Elm_Store_Item *item)
518 Elm_Store_Item_Filesystem *sti = (Elm_Store_Item_Filesystem *)item;
519 eina_stringshare_del(sti->path);
523 elm_store_filesystem_new(void)
525 Elm_Store_Filesystem *st = elm_store_new(Elm_Store_Filesystem);
526 EINA_SAFETY_ON_NULL_RETURN_VAL(st, NULL);
528 EINA_MAGIC_SET(st, ELM_STORE_FILESYSTEM_MAGIC);
529 st->base.free = _elm_store_filesystem_free;
530 st->base.item.free = _elm_store_filesystem_item_free;
536 elm_store_free(Elm_Store *st)
538 void (*item_free)(Elm_Store_Item *);
539 if (!EINA_MAGIC_CHECK(st, ELM_STORE_MAGIC)) return;
542 ecore_thread_cancel(st->list_th);
545 eina_list_free(st->realized);
546 item_free = st->item.free;
549 Elm_Store_Item *sti = (Elm_Store_Item *)st->items;
550 if (sti->eval_job) ecore_job_del(sti->eval_job);
553 ecore_thread_cancel(sti->fetch_th);
554 sti->fetch_th = NULL;
556 if (item_free) item_free(sti);
557 eina_lock_take(&sti->lock);
560 if (st->cb.unfetch.func)
561 st->cb.unfetch.func(st->cb.unfetch.data, sti);
564 eina_lock_release(&sti->lock);
565 eina_lock_free(&sti->lock);
570 evas_object_event_callback_del_full(st->genlist, EVAS_CALLBACK_DEL, _store_genlist_del, st);
571 evas_object_smart_callback_del(st->genlist, "realized", _store_genlist_item_realized);
572 evas_object_smart_callback_del(st->genlist, "unrealized", _store_genlist_item_unrealized);
573 elm_genlist_clear(st->genlist);
576 if (st->free) st->free(st);
581 elm_store_target_genlist_set(Elm_Store *st, Evas_Object *obj)
583 if (!EINA_MAGIC_CHECK(st, ELM_STORE_MAGIC)) return;
584 if (st->genlist == obj) return;
587 evas_object_event_callback_del_full(st->genlist, EVAS_CALLBACK_DEL, _store_genlist_del, st);
588 evas_object_smart_callback_del(st->genlist, "realized", _store_genlist_item_realized);
589 evas_object_smart_callback_del(st->genlist, "unrealized", _store_genlist_item_unrealized);
590 elm_genlist_clear(st->genlist);
593 if (!st->genlist) return;
594 evas_object_smart_callback_add(st->genlist, "realized", _store_genlist_item_realized, st);
595 evas_object_smart_callback_add(st->genlist, "unrealized", _store_genlist_item_unrealized, st);
596 evas_object_event_callback_add(st->genlist, EVAS_CALLBACK_DEL, _store_genlist_del, st);
597 elm_genlist_clear(st->genlist);
601 elm_store_filesystem_directory_set(Elm_Store *store, const char *dir)
603 Elm_Store_Filesystem *st = (Elm_Store_Filesystem *)store;
604 if (!EINA_MAGIC_CHECK(store, ELM_STORE_MAGIC)) return;
605 if (!EINA_MAGIC_CHECK(st, ELM_STORE_FILESYSTEM_MAGIC)) return;
608 ecore_thread_cancel(store->list_th);
609 store->list_th = NULL;
611 if (!eina_stringshare_replace(&st->dir, dir)) return;
612 store->list_th = ecore_thread_feedback_run(_store_filesystem_list_do,
613 _store_filesystem_list_update,
614 _store_filesystem_list_end,
615 _store_filesystem_list_cancel,
620 elm_store_filesystem_directory_get(const Elm_Store *store)
622 const Elm_Store_Filesystem *st = (const Elm_Store_Filesystem *)store;
623 if (!EINA_MAGIC_CHECK(store, ELM_STORE_MAGIC)) return NULL;
624 if (!EINA_MAGIC_CHECK(st, ELM_STORE_FILESYSTEM_MAGIC)) return NULL;
629 elm_store_cache_set(Elm_Store *st, int max)
631 if (!EINA_MAGIC_CHECK(st, ELM_STORE_MAGIC)) return;
632 if (max < 0) max = 0;
634 _store_cache_trim(st);
638 elm_store_cache_get(const Elm_Store *st)
640 if (!EINA_MAGIC_CHECK(st, ELM_STORE_MAGIC)) return 0;
641 return st->cache_max;
645 elm_store_list_func_set(Elm_Store *st, Elm_Store_Item_List_Cb func, const void *data)
647 if (!EINA_MAGIC_CHECK(st, ELM_STORE_MAGIC)) return;
648 st->cb.list.func = func;
649 st->cb.list.data = (void *)data;
653 elm_store_fetch_func_set(Elm_Store *st, Elm_Store_Item_Fetch_Cb func, const void *data)
655 if (!EINA_MAGIC_CHECK(st, ELM_STORE_MAGIC)) return;
656 st->cb.fetch.func = func;
657 st->cb.fetch.data = (void *)data;
661 elm_store_fetch_thread_set(Elm_Store *st, Eina_Bool use_thread)
663 if (!EINA_MAGIC_CHECK(st, ELM_STORE_MAGIC)) return;
664 st->fetch_thread = !!use_thread;
668 elm_store_fetch_thread_get(const Elm_Store *st)
670 if (!EINA_MAGIC_CHECK(st, ELM_STORE_MAGIC)) return EINA_FALSE;
671 return st->fetch_thread;
675 elm_store_unfetch_func_set(Elm_Store *st, Elm_Store_Item_Unfetch_Cb func, const void *data)
677 if (!EINA_MAGIC_CHECK(st, ELM_STORE_MAGIC)) return;
678 st->cb.unfetch.func = func;
679 st->cb.unfetch.data = (void *)data;
683 elm_store_sorted_set(Elm_Store *st, Eina_Bool sorted)
685 if (!EINA_MAGIC_CHECK(st, ELM_STORE_MAGIC)) return;
690 elm_store_sorted_get(const Elm_Store *st)
692 if (!EINA_MAGIC_CHECK(st, ELM_STORE_MAGIC)) return EINA_FALSE;
697 elm_store_item_data_set(Elm_Store_Item *sti, void *data)
699 if (!EINA_MAGIC_CHECK(sti, ELM_STORE_ITEM_MAGIC)) return;
700 //// let fetch/unfetch do the locking
701 // eina_lock_take(&sti->lock);
703 // eina_lock_release(&sti->lock);
707 elm_store_item_data_get(Elm_Store_Item *sti)
709 if (!EINA_MAGIC_CHECK(sti, ELM_STORE_ITEM_MAGIC)) return NULL;
711 //// let fetch/unfetch do the locking
712 // eina_lock_take(&sti->lock);
714 // eina_lock_release(&sti->lock);
718 EAPI const Elm_Store *
719 elm_store_item_store_get(const Elm_Store_Item *sti)
721 if (!EINA_MAGIC_CHECK(sti, ELM_STORE_ITEM_MAGIC)) return NULL;
726 EAPI const Elm_Object_Item *
727 elm_store_item_genlist_item_get(const Elm_Store_Item *sti)
729 if (!EINA_MAGIC_CHECK(sti, ELM_STORE_ITEM_MAGIC)) return NULL;
735 elm_store_item_filesystem_path_get(const Elm_Store_Item *item)
737 Elm_Store_Item_Filesystem *sti = (Elm_Store_Item_Filesystem *)item;
738 Elm_Store_Filesystem *st;
739 if (!EINA_MAGIC_CHECK(item, ELM_STORE_ITEM_MAGIC)) return NULL;
740 if (!EINA_MAGIC_CHECK(item->store, ELM_STORE_MAGIC)) return NULL;
741 /* ensure we're dealing with filesystem item */
742 st = (Elm_Store_Filesystem *)item->store;
743 if (!EINA_MAGIC_CHECK(st, ELM_STORE_FILESYSTEM_MAGIC)) return NULL;