1 #include <Elementary.h>
2 #include <Elementary_Cursor.h>
5 #define ELM_STORE_MAGIC 0x3f89ea56
6 #define ELM_STORE_FILESYSTEM_MAGIC 0x3f89ea57
7 #define ELM_STORE_ITEM_MAGIC 0x5afe8c1d
12 void (*free)(Elm_Store *store);
14 void (*free)(Elm_Store_Item *item);
17 Ecore_Thread *list_th;
24 Elm_Store_Item_List_Cb func;
28 Elm_Store_Item_Fetch_Cb func;
32 Elm_Store_Item_Unfetch_Cb func;
37 Eina_Bool fetch_thread : 1;
40 struct _Elm_Store_Item
45 Elm_Object_Item *item;
46 Ecore_Thread *fetch_th;
48 const Elm_Store_Item_Mapping *mapping;
52 Eina_Bool was_live : 1;
53 Eina_Bool realized : 1;
54 Eina_Bool fetched : 1;
57 struct _Elm_Store_Filesystem
64 struct _Elm_Store_Item_Filesystem
70 static Elm_Genlist_Item_Class _store_item_class;
73 _store_cache_trim(Elm_Store *st)
75 while ((st->realized ) &&
76 (((int)eina_list_count(st->realized) - st->realized_count)
79 Elm_Store_Item *sti = st->realized->data;
82 st->realized = eina_list_remove_list(st->realized, st->realized);
83 sti->realized = EINA_FALSE;
85 eina_lock_take(&sti->lock);
88 eina_lock_release(&sti->lock);
91 ecore_thread_cancel(sti->fetch_th);
94 eina_lock_take(&sti->lock);
96 sti->fetched = EINA_FALSE;
97 eina_lock_release(&sti->lock);
98 if (st->cb.unfetch.func)
99 st->cb.unfetch.func(st->cb.unfetch.data, sti);
100 eina_lock_take(&sti->lock);
102 eina_lock_release(&sti->lock);
107 _store_genlist_del(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
109 Elm_Store *st = data;
113 ecore_thread_cancel(st->list_th);
116 eina_list_free(st->realized);
119 Elm_Store_Item *sti = (Elm_Store_Item *)st->items;
120 if (sti->eval_job) ecore_job_del(sti->eval_job);
123 ecore_thread_cancel(sti->fetch_th);
124 sti->fetch_th = NULL;
126 if (sti->store->item.free) sti->store->item.free(sti);
129 if (st->cb.unfetch.func)
130 st->cb.unfetch.func(st->cb.unfetch.data, sti);
133 eina_lock_free(&sti->lock);
137 // FIXME: kill threads and more
140 ////// **** WARNING ***********************************************************
141 //// * This function runs inside a thread outside efl mainloop. Be careful! *
142 // ************************************************************************
143 /* TODO: refactor lock part into core? this does not depend on filesystm part */
145 _store_filesystem_fetch_do(void *data, Ecore_Thread *th __UNUSED__)
147 Elm_Store_Item *sti = data;
148 eina_lock_take(&sti->lock);
151 eina_lock_release(&sti->lock);
156 eina_lock_release(&sti->lock);
157 if (sti->store->cb.fetch.func)
158 sti->store->cb.fetch.func(sti->store->cb.fetch.data, sti);
159 eina_lock_take(&sti->lock);
160 sti->fetched = EINA_TRUE;
162 eina_lock_release(&sti->lock);
164 // ************************************************************************
165 //// * End of separate thread function. *
166 ////// ************************************************************************
167 /* TODO: refactor lock part into core? this does not depend on filesystm part */
169 _store_filesystem_fetch_end(void *data, Ecore_Thread *th)
171 Elm_Store_Item *sti = data;
172 eina_lock_take(&sti->lock);
173 if (sti->data) elm_genlist_item_update(sti->item);
174 eina_lock_release(&sti->lock);
175 if (th == sti->fetch_th) sti->fetch_th = NULL;
178 /* TODO: refactor lock part into core? this does not depend on filesystm part */
180 _store_filesystem_fetch_cancel(void *data, Ecore_Thread *th)
182 Elm_Store_Item *sti = data;
183 eina_lock_take(&sti->lock);
184 if (th == sti->fetch_th) sti->fetch_th = NULL;
185 if (sti->data) elm_genlist_item_update(sti->item);
186 eina_lock_release(&sti->lock);
190 _store_item_eval(void *data)
192 Elm_Store_Item *sti = data;
193 sti->eval_job = NULL;
194 if (sti->live == sti->was_live) return;
195 sti->was_live = sti->live;
198 _store_cache_trim(sti->store);
200 sti->store->realized = eina_list_remove(sti->store->realized, sti);
201 sti->store->realized = eina_list_append(sti->store->realized, sti);
202 sti->realized = EINA_TRUE;
203 if ((sti->store->fetch_thread) && (!sti->fetch_th))
204 sti->fetch_th = ecore_thread_run(_store_filesystem_fetch_do,
205 _store_filesystem_fetch_end,
206 _store_filesystem_fetch_cancel,
208 else if ((!sti->store->fetch_thread))
210 _store_filesystem_fetch_do(sti, NULL);
211 _store_filesystem_fetch_end(sti, NULL);
218 ecore_thread_cancel(sti->fetch_th);
219 sti->fetch_th = NULL;
221 _store_cache_trim(sti->store);
226 _store_genlist_item_realized(void *data, Evas_Object *obj __UNUSED__, void *event_info)
228 Elm_Store *st = data;
229 Elm_Object_Item *gli = event_info;
230 Elm_Store_Item *sti = elm_object_item_data_get(gli);
232 st->realized_count++;
233 sti->live = EINA_TRUE;
234 if (sti->eval_job) ecore_job_del(sti->eval_job);
235 sti->eval_job = ecore_job_add(_store_item_eval, sti);
239 _store_genlist_item_unrealized(void *data, Evas_Object *obj __UNUSED__, void *event_info)
241 Elm_Store *st = data;
242 Elm_Object_Item *gli = event_info;
243 Elm_Store_Item *sti = elm_object_item_data_get(gli);
245 st->realized_count--;
246 sti->live = EINA_FALSE;
247 if (sti->eval_job) ecore_job_del(sti->eval_job);
248 sti->eval_job = ecore_job_add(_store_item_eval, sti);
251 static const Elm_Store_Item_Mapping *
252 _store_item_mapping_find(Elm_Store_Item *sti, const char *part)
254 const Elm_Store_Item_Mapping *m;
256 for (m = sti->mapping; m; m ++)
258 if (m->type == ELM_STORE_ITEM_MAPPING_NONE) break;
259 if (!strcmp(part, m->part)) return m;
265 _store_item_text_get(void *data, Evas_Object *obj __UNUSED__, const char *part)
267 Elm_Store_Item *sti = data;
269 eina_lock_take(&sti->lock);
272 const Elm_Store_Item_Mapping *m = _store_item_mapping_find(sti, part);
277 case ELM_STORE_ITEM_MAPPING_LABEL:
278 s = *(char **)(((unsigned char *)sti->data) + m->offset);
280 case ELM_STORE_ITEM_MAPPING_CUSTOM:
281 if (m->details.custom.func)
282 s = m->details.custom.func(sti->data, sti, part);
289 eina_lock_release(&sti->lock);
290 return s ? strdup(s) : NULL;
294 _store_item_content_get(void *data, Evas_Object *obj, const char *part)
296 Elm_Store_Item *sti = data;
297 eina_lock_take(&sti->lock);
300 const Elm_Store_Item_Mapping *m = _store_item_mapping_find(sti, part);
303 Evas_Object *ic = NULL;
304 const char *s = NULL;
308 case ELM_STORE_ITEM_MAPPING_ICON:
309 ic = elm_icon_add(obj);
310 s = *(char **)(((unsigned char *)sti->data) + m->offset);
311 elm_icon_order_lookup_set(ic, m->details.icon.lookup_order);
312 evas_object_size_hint_aspect_set(ic,
313 EVAS_ASPECT_CONTROL_VERTICAL,
316 elm_icon_smooth_set(ic, m->details.icon.smooth);
317 elm_icon_no_scale_set(ic, m->details.icon.no_scale);
318 elm_icon_resizable_set(ic,
319 m->details.icon.scale_up,
320 m->details.icon.scale_down);
323 if (m->details.icon.standard_name)
324 elm_icon_standard_set(ic, s);
326 elm_icon_file_set(ic, s, NULL);
329 case ELM_STORE_ITEM_MAPPING_PHOTO:
330 ic = elm_icon_add(obj);
331 s = *(char **)(((unsigned char *)sti->data) + m->offset);
332 elm_photo_size_set(ic, m->details.photo.size);
334 elm_photo_file_set(ic, s);
336 case ELM_STORE_ITEM_MAPPING_CUSTOM:
337 if (m->details.custom.func)
338 ic = m->details.custom.func(sti->data, sti, part);
343 eina_lock_release(&sti->lock);
347 eina_lock_release(&sti->lock);
352 _store_item_del(void *data __UNUSED__, Evas_Object *obj __UNUSED__)
356 ////// **** WARNING ***********************************************************
357 //// * This function runs inside a thread outside efl mainloop. Be careful! *
358 // ************************************************************************
360 _store_filesystem_sort_cb(void *d1, void *d2)
362 Elm_Store_Item_Info *info1 = d1, *info2 = d2;
363 if ((!info1->sort_id) || (!info2->sort_id)) return 0;
364 return strcoll(info1->sort_id, info2->sort_id);
368 _store_filesystem_list_do(void *data, Ecore_Thread *th __UNUSED__)
370 Elm_Store_Filesystem *st = data;
372 const Eina_File_Direct_Info *finf;
373 Eina_List *sorted = NULL;
374 Elm_Store_Item_Info_Filesystem *info;
376 // FIXME: need a way to abstract the open, list, feed items from list
377 // and maybe get initial sortable key vals etc.
378 it = eina_file_stat_ls(st->dir);
380 EINA_ITERATOR_FOREACH(it, finf)
383 size_t pathsz = finf->path_length + 1;
385 if (finf->path[finf->name_start] == '.') continue ;
387 info = calloc(1, sizeof(Elm_Store_Item_Info_Filesystem) + pathsz);
389 info->path = ((char *)info) + sizeof(Elm_Store_Item_Info_Filesystem);
390 memcpy(info->path, finf->path, pathsz);
392 if (st->base.cb.list.func)
393 ok = st->base.cb.list.func(st->base.cb.list.data, &info->base);
396 if (!st->base.sorted) ecore_thread_feedback(th, info);
397 else sorted = eina_list_append(sorted, info);
401 if (info->base.sort_id) free(info->base.sort_id);
404 if (ecore_thread_check(th)) break;
406 eina_iterator_free(it);
409 sorted = eina_list_sort(sorted, 0,
410 EINA_COMPARE_CB(_store_filesystem_sort_cb));
411 EINA_LIST_FREE(sorted, info)
413 if (!ecore_thread_check(th)) ecore_thread_feedback(th, info);
417 // ************************************************************************
418 //// * End of separate thread function. *
419 ////// ************************************************************************
422 _store_filesystem_list_end(void *data, Ecore_Thread *th)
424 Elm_Store *st = data;
425 if (th == st->list_th) st->list_th = NULL;
429 _store_filesystem_list_cancel(void *data, Ecore_Thread *th)
431 Elm_Store *st = data;
432 if (th == st->list_th) st->list_th = NULL;
436 _store_filesystem_list_update(void *data, Ecore_Thread *th __UNUSED__, void *msg)
438 Elm_Store *st = data;
439 Elm_Store_Item_Filesystem *sti;
440 Elm_Genlist_Item_Class *itc;
441 Elm_Store_Item_Info_Filesystem *info = msg;
443 sti = calloc(1, sizeof(Elm_Store_Item_Filesystem));
445 eina_lock_new(&sti->base.lock);
446 EINA_MAGIC_SET(&(sti->base), ELM_STORE_ITEM_MAGIC);
447 sti->base.store = st;
448 sti->base.data = info->base.data;
449 sti->base.mapping = info->base.mapping;
450 sti->path = eina_stringshare_add(info->path);
452 itc = info->base.item_class;
453 if (!itc) itc = &_store_item_class;
456 itc->func.text_get = _store_item_text_get;
457 itc->func.content_get = _store_item_content_get;
458 itc->func.state_get = NULL; // FIXME: support state gets later
459 itc->func.del = _store_item_del;
462 // FIXME: handle being a parent (tree)
463 sti->base.item = elm_genlist_item_append(st->genlist, itc,
466 ELM_GENLIST_ITEM_NONE,
468 NULL/* func data */);
469 st->items = eina_inlist_append(st->items, (Eina_Inlist *)sti);
471 if (info->base.sort_id) free(info->base.sort_id);
477 _elm_store_new(size_t size)
479 Elm_Store *st = calloc(1, size);
480 EINA_SAFETY_ON_NULL_RETURN_VAL(st, NULL);
482 // TODO: BEGIN - move to elm_store_init()
483 eina_magic_string_set(ELM_STORE_MAGIC, "Elm_Store");
484 eina_magic_string_set(ELM_STORE_FILESYSTEM_MAGIC, "Elm_Store_Filesystem");
485 eina_magic_string_set(ELM_STORE_ITEM_MAGIC, "Elm_Store_Item");
486 // setup default item class (always the same) if list cb doesnt provide one
487 _store_item_class.item_style = "default";
488 _store_item_class.func.text_get = _store_item_text_get;
489 _store_item_class.func.content_get = _store_item_content_get;
490 _store_item_class.func.state_get = NULL; // FIXME: support state gets later
491 _store_item_class.func.del = _store_item_del;
492 // TODO: END - move to elm_store_init()
494 EINA_MAGIC_SET(st, ELM_STORE_MAGIC);
496 st->fetch_thread = EINA_TRUE;
499 #define elm_store_new(type) (type*)_elm_store_new(sizeof(type))
502 _elm_store_filesystem_free(Elm_Store *store)
504 Elm_Store_Filesystem *st = (Elm_Store_Filesystem *)store;
505 eina_stringshare_del(st->dir);
509 _elm_store_filesystem_item_free(Elm_Store_Item *item)
511 Elm_Store_Item_Filesystem *sti = (Elm_Store_Item_Filesystem *)item;
512 eina_stringshare_del(sti->path);
516 elm_store_filesystem_new(void)
518 Elm_Store_Filesystem *st = elm_store_new(Elm_Store_Filesystem);
519 EINA_SAFETY_ON_NULL_RETURN_VAL(st, NULL);
521 EINA_MAGIC_SET(st, ELM_STORE_FILESYSTEM_MAGIC);
522 st->base.free = _elm_store_filesystem_free;
523 st->base.item.free = _elm_store_filesystem_item_free;
529 elm_store_free(Elm_Store *st)
531 void (*item_free)(Elm_Store_Item *);
532 if (!EINA_MAGIC_CHECK(st, ELM_STORE_MAGIC)) return;
535 ecore_thread_cancel(st->list_th);
538 eina_list_free(st->realized);
539 item_free = st->item.free;
542 Elm_Store_Item *sti = (Elm_Store_Item *)st->items;
543 if (sti->eval_job) ecore_job_del(sti->eval_job);
546 ecore_thread_cancel(sti->fetch_th);
547 sti->fetch_th = NULL;
549 if (item_free) item_free(sti);
552 if (st->cb.unfetch.func)
553 st->cb.unfetch.func(st->cb.unfetch.data, sti);
556 eina_lock_free(&sti->lock);
561 evas_object_event_callback_del_full(st->genlist, EVAS_CALLBACK_DEL, _store_genlist_del, st);
562 evas_object_smart_callback_del(st->genlist, "realized", _store_genlist_item_realized);
563 evas_object_smart_callback_del(st->genlist, "unrealized", _store_genlist_item_unrealized);
564 elm_genlist_clear(st->genlist);
567 if (st->free) st->free(st);
572 elm_store_target_genlist_set(Elm_Store *st, Evas_Object *obj)
574 if (!EINA_MAGIC_CHECK(st, ELM_STORE_MAGIC)) return;
575 if (st->genlist == obj) return;
578 evas_object_event_callback_del_full(st->genlist, EVAS_CALLBACK_DEL, _store_genlist_del, st);
579 evas_object_smart_callback_del(st->genlist, "realized", _store_genlist_item_realized);
580 evas_object_smart_callback_del(st->genlist, "unrealized", _store_genlist_item_unrealized);
581 elm_genlist_clear(st->genlist);
584 if (!st->genlist) return;
585 evas_object_smart_callback_add(st->genlist, "realized", _store_genlist_item_realized, st);
586 evas_object_smart_callback_add(st->genlist, "unrealized", _store_genlist_item_unrealized, st);
587 evas_object_event_callback_add(st->genlist, EVAS_CALLBACK_DEL, _store_genlist_del, st);
588 elm_genlist_clear(st->genlist);
592 elm_store_filesystem_directory_set(Elm_Store *store, const char *dir)
594 Elm_Store_Filesystem *st = (Elm_Store_Filesystem *)store;
595 if (!EINA_MAGIC_CHECK(store, ELM_STORE_MAGIC)) return;
596 if (!EINA_MAGIC_CHECK(st, ELM_STORE_FILESYSTEM_MAGIC)) return;
599 ecore_thread_cancel(store->list_th);
600 store->list_th = NULL;
602 if (!eina_stringshare_replace(&st->dir, dir)) return;
603 store->list_th = ecore_thread_feedback_run(_store_filesystem_list_do,
604 _store_filesystem_list_update,
605 _store_filesystem_list_end,
606 _store_filesystem_list_cancel,
611 elm_store_filesystem_directory_get(const Elm_Store *store)
613 const Elm_Store_Filesystem *st = (const Elm_Store_Filesystem *)store;
614 if (!EINA_MAGIC_CHECK(store, ELM_STORE_MAGIC)) return NULL;
615 if (!EINA_MAGIC_CHECK(st, ELM_STORE_FILESYSTEM_MAGIC)) return NULL;
620 elm_store_cache_set(Elm_Store *st, int max)
622 if (!EINA_MAGIC_CHECK(st, ELM_STORE_MAGIC)) return;
623 if (max < 0) max = 0;
625 _store_cache_trim(st);
629 elm_store_cache_get(const Elm_Store *st)
631 if (!EINA_MAGIC_CHECK(st, ELM_STORE_MAGIC)) return 0;
632 return st->cache_max;
636 elm_store_list_func_set(Elm_Store *st, Elm_Store_Item_List_Cb func, const void *data)
638 if (!EINA_MAGIC_CHECK(st, ELM_STORE_MAGIC)) return;
639 st->cb.list.func = func;
640 st->cb.list.data = (void *)data;
644 elm_store_fetch_func_set(Elm_Store *st, Elm_Store_Item_Fetch_Cb func, const void *data)
646 if (!EINA_MAGIC_CHECK(st, ELM_STORE_MAGIC)) return;
647 st->cb.fetch.func = func;
648 st->cb.fetch.data = (void *)data;
652 elm_store_fetch_thread_set(Elm_Store *st, Eina_Bool use_thread)
654 if (!EINA_MAGIC_CHECK(st, ELM_STORE_MAGIC)) return;
655 st->fetch_thread = !!use_thread;
659 elm_store_fetch_thread_get(const Elm_Store *st)
661 if (!EINA_MAGIC_CHECK(st, ELM_STORE_MAGIC)) return EINA_FALSE;
662 return st->fetch_thread;
666 elm_store_unfetch_func_set(Elm_Store *st, Elm_Store_Item_Unfetch_Cb func, const void *data)
668 if (!EINA_MAGIC_CHECK(st, ELM_STORE_MAGIC)) return;
669 st->cb.unfetch.func = func;
670 st->cb.unfetch.data = (void *)data;
674 elm_store_sorted_set(Elm_Store *st, Eina_Bool sorted)
676 if (!EINA_MAGIC_CHECK(st, ELM_STORE_MAGIC)) return;
681 elm_store_sorted_get(const Elm_Store *st)
683 if (!EINA_MAGIC_CHECK(st, ELM_STORE_MAGIC)) return EINA_FALSE;
688 elm_store_item_data_set(Elm_Store_Item *sti, void *data)
690 if (!EINA_MAGIC_CHECK(sti, ELM_STORE_ITEM_MAGIC)) return;
691 eina_lock_take(&sti->lock);
693 eina_lock_release(&sti->lock);
697 elm_store_item_data_get(Elm_Store_Item *sti)
699 if (!EINA_MAGIC_CHECK(sti, ELM_STORE_ITEM_MAGIC)) return NULL;
701 eina_lock_take(&sti->lock);
703 eina_lock_release(&sti->lock);
707 EAPI const Elm_Store *
708 elm_store_item_store_get(const Elm_Store_Item *sti)
710 if (!EINA_MAGIC_CHECK(sti, ELM_STORE_ITEM_MAGIC)) return NULL;
715 EAPI const Elm_Object_Item *
716 elm_store_item_genlist_item_get(const Elm_Store_Item *sti)
718 if (!EINA_MAGIC_CHECK(sti, ELM_STORE_ITEM_MAGIC)) return NULL;
724 elm_store_item_filesystem_path_get(const Elm_Store_Item *item)
726 Elm_Store_Item_Filesystem *sti = (Elm_Store_Item_Filesystem *)item;
727 Elm_Store_Filesystem *st;
728 if (!EINA_MAGIC_CHECK(item, ELM_STORE_ITEM_MAGIC)) return NULL;
729 if (!EINA_MAGIC_CHECK(item->store, ELM_STORE_MAGIC)) return NULL;
730 /* ensure we're dealing with filesystem item */
731 st = (Elm_Store_Filesystem *)item->store;
732 if (!EINA_MAGIC_CHECK(st, ELM_STORE_FILESYSTEM_MAGIC)) return NULL;