#include "private.h" #include "mpris.h" #include #include /* * TODO: * - create page_folder that contains something other than songs, click one * should create a new page, stack it into the parent list and recurse. * - add suffle action for page_songs */ /* number of songs to populate at once before going back to mainloop */ #define PAGE_SONGS_POPULATE_ITERATION_COUNT (64) /* number of folders to populate at once before going back to mainloop */ #define PAGE_FOLDERS_POPULATE_ITERATION_COUNT (64) #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) struct Array_Iterator { Eina_Iterator base; const void *container; size_t current, count, item_size; }; static void * _array_iterator_container_get(Eina_Iterator *iterator) { struct Array_Iterator *it = (struct Array_Iterator *)iterator; if (!EINA_MAGIC_CHECK(iterator, EINA_MAGIC_ITERATOR)) { EINA_MAGIC_FAIL(iterator, EINA_MAGIC_ITERATOR); return NULL; } return (void *)it->container; } static void _array_iterator_free(Eina_Iterator *iterator) { struct Array_Iterator *it = (struct Array_Iterator *)iterator; if (!EINA_MAGIC_CHECK(iterator, EINA_MAGIC_ITERATOR)) { EINA_MAGIC_FAIL(iterator, EINA_MAGIC_ITERATOR); return; } EINA_MAGIC_SET(&it->base, EINA_MAGIC_NONE); free(it); } static Eina_Bool _array_iterator_next(Eina_Iterator *iterator, void **data) { struct Array_Iterator *it = (struct Array_Iterator *)iterator; if (!EINA_MAGIC_CHECK(iterator, EINA_MAGIC_ITERATOR)) { EINA_MAGIC_FAIL(iterator, EINA_MAGIC_ITERATOR); return EINA_FALSE; } EINA_SAFETY_ON_NULL_RETURN_VAL(data, EINA_FALSE); *data = NULL; if (it->current >= it->count) return EINA_FALSE; *data = ((char *)it->container) + it->item_size * it->current; it->current++; return EINA_TRUE; } static Eina_Iterator * _array_iterator_new(const void *array, size_t item_size, size_t count) { struct Array_Iterator *it = calloc(1, sizeof(*it)); EINA_SAFETY_ON_NULL_RETURN_VAL(it, NULL); it->base.get_container = _array_iterator_container_get; it->base.free = _array_iterator_free; it->base.next = _array_iterator_next; it->container = array; it->item_size = item_size; it->count = count; EINA_MAGIC_SET(&it->base, EINA_MAGIC_ITERATOR); return &it->base; } static void * _data_from_itr_passthrough(const void *data) { return (void *)data; } typedef struct _Page Page; typedef struct _Page_Class { const char *name; const char *key; const char *layout; Eina_Bool (*init)(Page *page); /* optional extra initializer */ void (*after_populate)(Page *page); /* called after page has been populated */ Evas_Smart_Cb selected; const Elm_Genlist_Item_Class *item_cls; size_t populate_iteration_count; void *(*data_from_itr)(const void *data); size_t data_letter_offset; unsigned short icon_size; } Page_Class; struct _Page { const Page_Class *cls; Evas_Object *layout; Evas_Object *edje; Evas_Object *list; Evas_Object *index; Evas_Object *parent; size_t num_elements; const char *title; void *container; void *model; Elm_Genlist_Item *selected; Elm_Genlist_Item *first; Eina_Iterator *iterator; Ecore_Idler *populate; char last_index_letter[2]; struct { void *data; void (*free)(void *data); } item; }; #define PAGE_GET_OR_RETURN(page, obj, ...) \ Page *page = evas_object_data_get(obj, "_enjoy_page"); \ do \ { \ if (!page) \ { \ CRITICAL("Not a page: "#obj": %p", obj); \ return __VA_ARGS__; \ } \ } \ while (0) static DB * _page_db_get(const Evas_Object *obj) { Page *page, *ppage; page = evas_object_data_get(obj, "_enjoy_page"); EINA_SAFETY_ON_NULL_RETURN_VAL(page, NULL); ppage = evas_object_data_get(page->parent, "_enjoy_page"); if (ppage) return _page_db_get(page->parent); return list_db_get(page->parent); } static Eina_Bool _page_populate(void *data) { Page *page = data; const Page_Class *cls = page->cls; unsigned int count; for (count = 0; count < cls->populate_iteration_count; count++) { Elm_Genlist_Item *it; char letter; void *id, *od; const char **letter_str; if (!eina_iterator_next(page->iterator, &id)) goto end; // TODO: evaluate if we should keep a full copy or just store // fields of interest such as id, title, artist and album od = cls->data_from_itr(id); if (!od) goto end; it = elm_genlist_item_append (page->list, cls->item_cls, od, NULL, ELM_GENLIST_ITEM_NONE, NULL, NULL); letter_str = (const char **)(((char *)od) + cls->data_letter_offset); letter = toupper((*letter_str)[0]); if ((page->index) && (isalpha(letter) && (page->last_index_letter[0] != letter))) { if ((page->first) && (!page->last_index_letter[0])) elm_index_item_append(page->index, "Special", page->first); page->last_index_letter[0] = letter; elm_index_item_append(page->index, page->last_index_letter, it); } if (!page->first) page->first = it; page->num_elements++; } return EINA_TRUE; end: if (cls->after_populate) cls->after_populate(page); page->populate = NULL; return EINA_FALSE; } static void _page_selected(void *data, Evas_Object *o, void *event_info) { Page *page = data; Elm_Genlist_Item *it = event_info; if (page->selected == it) return; page->selected = it; page->cls->selected(data, o, event_info); } static void _page_index_changed(void *data __UNUSED__, Evas_Object *o __UNUSED__, void *event_info) { Elm_Genlist_Item *it = event_info; elm_genlist_item_top_bring_in(it); } static void _page_del(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event_info __UNUSED__) { Page *page = data; eina_stringshare_del(page->title); if (page->iterator) eina_iterator_free(page->iterator); if (page->populate) ecore_idler_del(page->populate); if (page->item.free) page->item.free(page->item.data); free(page); } static void _page_back(void *data, Evas_Object *o __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__) { Page *page = data; evas_object_smart_callback_call(page->layout, "back", NULL); } static void _page_songs(void *data, Evas_Object *o __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__) { Page *page = data; evas_object_smart_callback_call(page->layout, "songs", NULL); } static Evas_Object * _page_add(Evas_Object *parent, void *model, Eina_Iterator *it, const char *title, const Page_Class *cls) { Evas_Object *obj; Page *page; const char *s; EINA_SAFETY_ON_NULL_RETURN_VAL(it, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(cls, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(cls->name, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(cls->key, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(cls->layout, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(cls->selected, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(cls->item_cls, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(cls->data_from_itr, NULL); DBG("creating page %s with key %s, item style %s", cls->name, cls->key, cls->item_cls->item_style); obj = elm_layout_add(parent); if (!obj) { eina_iterator_free(it); return NULL; } page = calloc(1, sizeof(*page)); if (!page) { CRITICAL("could not allocate page memory!"); eina_iterator_free(it); goto error; } evas_object_data_set(obj, "_enjoy_page", page); evas_object_data_set(obj, cls->key, page); evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL, _page_del, page); page->layout = obj; page->model = model; page->iterator = it; page->cls = cls; page->parent = parent; if (!elm_layout_file_set(obj, PACKAGE_DATA_DIR "/default.edj", cls->layout)) { CRITICAL("no theme for '%s' at %s", cls->layout, PACKAGE_DATA_DIR "/default.edj"); goto error; } page->title = eina_stringshare_add(title); page->edje = elm_layout_edje_get(obj); edje_object_part_text_set(page->edje, "ejy.text.title", page->title); edje_object_signal_callback_add (page->edje, "ejy,back,clicked", "ejy", _page_back, page); edje_object_signal_callback_add (page->edje, "ejy,songs,clicked", "ejy", _page_songs, page); page->list = elm_genlist_add(obj); elm_genlist_bounce_set(page->list, EINA_FALSE, EINA_TRUE); elm_genlist_horizontal_mode_set(page->list, ELM_LIST_COMPRESS); elm_genlist_compress_mode_set(page->list, EINA_TRUE); elm_object_style_set(page->list, "enjoy"); s = edje_object_data_get(page->edje, "homogeneous"); elm_genlist_homogeneous_set(page->list, s ? !!atoi(s) : EINA_FALSE); elm_layout_content_set(obj, "ejy.swallow.list", page->list); if (edje_object_part_exists(page->edje, "ejy.swallow.index")) { page->index = elm_index_add(obj); evas_object_smart_callback_add (page->index, "delay,changed", _page_index_changed, page); elm_layout_content_set(obj, "ejy.swallow.index", page->index); } page->container = eina_iterator_container_get(it); evas_object_data_set(page->list, "_enjoy_container", page->container); evas_object_data_set(page->list, "_enjoy_page", page); evas_object_smart_callback_add(page->list, "selected", _page_selected, page); if (cls->init) { if (!cls->init(page)) goto error; } page->populate = ecore_idler_add(_page_populate, page); return obj; error: evas_object_del(obj); /* should delete everything */ return NULL; } void page_songs_exists_changed(Evas_Object *obj, Eina_Bool exists) { PAGE_GET_OR_RETURN(page, obj); if (exists) edje_object_signal_emit(page->edje, "ejy,songs,show", "ejy"); else edje_object_signal_emit(page->edje, "ejy,songs,hide", "ejy"); } const char * page_title_get(const Evas_Object *obj) { PAGE_GET_OR_RETURN(page, obj, NULL); return page->title; } /*********************************************************************** * SONGS ***********************************************************************/ #define PAGE_SONGS_GET_OR_RETURN(page, obj, ...) \ Page *page = evas_object_data_get(obj, "_enjoy_page_songs"); \ do \ { \ if (!page) \ { \ CRITICAL("Not a page_song: "#obj": %p", obj); \ return __VA_ARGS__; \ } \ } \ while (0) static char * _song_item_label_get(void *data, Evas_Object *list, const char *part) { Song *song = data; /* check if matches protocol */ if (strncmp(part, "ejy.text.", sizeof("ejy.text.") - 1) != 0) return NULL; part += sizeof("ejy.text.") - 1; if (!strcmp(part, "title")) return strdup(song->title); else if (!strcmp(part, "trackno-title")) { char *str; if (song->trackno < 1) return strdup(song->title); if (asprintf(&str, "%d - %s", song->trackno, song->title) > 0) return str; return NULL; } else if (!strcmp(part, "album-artist")) { char *str; if ((!song->flags.fetched_album) || (!song->flags.fetched_artist)) { DB *db = evas_object_data_get(list, "_enjoy_container"); db_song_album_fetch(db, song); db_song_artist_fetch(db, song); } if ((!song->album) && (!song->artist)) return NULL; else if (!song->album) return strdup(song->artist); else if (!song->artist) return strdup(song->album); if (asprintf(&str, "%s - %s", song->album, song->artist) > 0) return str; return NULL; } else if (!strcmp(part, "album")) { if (!song->flags.fetched_album) { DB *db = evas_object_data_get(list, "_enjoy_container"); db_song_album_fetch(db, song); } return song->album ? strdup(song->album) : NULL; } else if (!strcmp(part, "artist")) { if (!song->flags.fetched_artist) { DB *db = evas_object_data_get(list, "_enjoy_container"); db_song_artist_fetch(db, song); } return song->artist ? strdup(song->artist) : NULL; } else if (!strcmp(part, "genre")) { if (!song->flags.fetched_genre) { DB *db = evas_object_data_get(list, "_enjoy_container"); db_song_genre_fetch(db, song); } return song->genre ? strdup(song->genre) : NULL; } else if (!strcmp(part, "trackno")) { char *str; if (song->trackno < 1) return NULL; if (asprintf(&str, "%d", song->trackno) > 0) return str; return NULL; } else if (!strcmp(part, "playcnt")) { char *str; if (song->playcnt < 1) return NULL; if (asprintf(&str, "%d", song->playcnt) > 0) return str; return NULL; } else if (!strcmp(part, "rating")) { char *str; if (song->rating < 1) return NULL; if (asprintf(&str, "%d", song->rating) > 0) return str; return NULL; } else if (!strcmp(part, "length")) { char *str; int len; if (song->length < 1) return NULL; if (song->length < 60) len = asprintf(&str, "%d", song->length); else if (song->length < 60 * 60) len = asprintf(&str, "%d:%02d", song->length / 60, song->length % 60); else len = asprintf(&str, "%d:%02d:%02d", song->length / (60 * 60), (song->length / 60) % 60, song->length % 60); if (len > 0) return str; return NULL; } return NULL; } static Eina_Bool _song_item_state_get(void *data, Evas_Object *list, const char *part) { Song *song = data; /* check if matches protocol */ if (strncmp(part, "ejy.state.", sizeof("ejy.state.") - 1) != 0) return EINA_FALSE; part += sizeof("ejy.state.") - 1; if (!strcmp(part, "title")) return EINA_TRUE; else if (!strcmp(part, "trackno")) return song->trackno > 0; else if (!strcmp(part, "playcnt")) return song->playcnt > 0; else if (!strcmp(part, "rating")) return song->rating > 0; else if (!strcmp(part, "length")) return song->length > 0; else if (!strcmp(part, "artist")) { if (!song->flags.fetched_artist) { DB *db = evas_object_data_get(list, "_enjoy_container"); db_song_artist_fetch(db, song); } return !!song->artist; } else if (!strcmp(part, "album")) { if (!song->flags.fetched_album) { DB *db = evas_object_data_get(list, "_enjoy_container"); db_song_album_fetch(db, song); } return !!song->album; } else if (!strcmp(part, "genre")) { if (!song->flags.fetched_genre) { DB *db = evas_object_data_get(list, "_enjoy_container"); db_song_genre_fetch(db, song); } return !!song->genre; } return EINA_FALSE; } static void _song_item_del(void *data, Evas_Object *list __UNUSED__) { db_song_free(data); } static void _song_item_selected(void *data, Evas_Object *list __UNUSED__, void *event_info) { Page *page = data; Song *song = elm_genlist_item_data_get(event_info); if (song) evas_object_smart_callback_call(page->layout, "song", song); } static void _page_songs_after_populate(Page *page) { mpris_signal_tracklist_tracklist_change(page->num_elements); } static Evas_Object * _page_songs_add(Evas_Object *parent, NameID *nameid, Eina_Iterator *it, const char *title) { static const Elm_Genlist_Item_Class song_item_cls = { "song", { _song_item_label_get, NULL, _song_item_state_get, _song_item_del } }; static const Page_Class song_cls = { "song", "_enjoy_page_songs", "page/songs", NULL, _page_songs_after_populate, _song_item_selected, &song_item_cls, PAGE_SONGS_POPULATE_ITERATION_COUNT, (void *(*)(const void*))db_song_copy, offsetof(Song, title), 0 }; return _page_add(parent, nameid, it, title, &song_cls); } static void _song_album_cover_size_changed(void *data, Evas *e __UNUSED__, Evas_Object *part, void *event_info __UNUSED__) { Page *page = data; Evas_Coord size, w, h; Evas_Object *cover; evas_object_geometry_get(part, NULL, NULL, &w, &h); if (w < 32) w = 32; if (h < 32) h = 32; size = (w < h) ? w : h; DBG("cover view changed size to %dx%d, query cover size %d", w, h, size); cover = cover_album_fetch(page->layout, page->container, page->model, size); elm_layout_content_set(page->layout, "ejy.swallow.cover", cover); } static Eina_Bool _song_album_init(Page *page) { Album *album = page->model; if (edje_object_part_exists(page->edje, "ejy.swallow.cover")) { DB *db = _page_db_get(page->layout); Evas_Object *cover, *part; const char *s; int size = 0; s = edje_object_data_get(page->edje, "cover_size"); if (s) size = atoi(s); if (size < 32) size = 32; cover = cover_album_fetch(page->layout, db, album, size); elm_layout_content_set(page->layout, "ejy.swallow.cover", cover); part = (Evas_Object *) edje_object_part_object_get(page->edje, "ejy.swallow.cover"); evas_object_event_callback_add (part, EVAS_CALLBACK_RESIZE, _song_album_cover_size_changed, page); } if (album->name) edje_object_part_text_set(page->edje, "ejy.text.album", album->name); else edje_object_part_text_set(page->edje, "ejy.text.album", ""); if (album->artist) edje_object_part_text_set(page->edje, "ejy.text.artist", album->artist); else edje_object_part_text_set(page->edje, "ejy.text.artist", ""); return EINA_TRUE; } static Evas_Object * _page_album_songs_add(Evas_Object *parent, Album *album) { DB *db = _page_db_get(parent); Eina_Iterator *it; static const Elm_Genlist_Item_Class song_item_cls = { "song-album", { _song_item_label_get, NULL, _song_item_state_get, _song_item_del } }; static const Page_Class song_cls = { "song", "_enjoy_page_songs", "page/songs-album", _song_album_init, _page_songs_after_populate, _song_item_selected, &song_item_cls, PAGE_SONGS_POPULATE_ITERATION_COUNT, (void *(*)(const void*))db_song_copy, offsetof(Song, title), 0 }; it = db_album_songs_get(db, album->id); if ((!album->artist) && (!album->flags.fetched_artist)) db_album_artist_fetch(db, album); return _page_add(parent, album, it, "Album Songs", &song_cls); } Song * page_songs_selected_get(const Evas_Object *obj) { PAGE_SONGS_GET_OR_RETURN(page, obj, NULL); return page->selected ? elm_genlist_item_data_get(page->selected) : NULL; } Eina_Bool page_songs_song_updated(Evas_Object *obj) { PAGE_SONGS_GET_OR_RETURN(page, obj, EINA_FALSE); if (page->selected) { elm_genlist_item_update(page->selected); return EINA_TRUE; } return EINA_FALSE; } Eina_Bool page_songs_next_exists(const Evas_Object *obj) { PAGE_SONGS_GET_OR_RETURN(page, obj, EINA_FALSE); Elm_Genlist_Item *it = page->selected; if (!it) return EINA_FALSE; it = elm_genlist_item_next_get(it); return !!it; } Song * page_songs_next_go(Evas_Object *obj) { PAGE_SONGS_GET_OR_RETURN(page, obj, NULL); Elm_Genlist_Item *it = page->selected; Song *song; if (!it) return NULL; it = elm_genlist_item_next_get(it); if (!it) return NULL; song = elm_genlist_item_data_get(it); page->selected = it; elm_genlist_item_selected_set(it, EINA_TRUE); elm_genlist_item_bring_in(it); mpris_signal_player_track_change(song); return song; } int32_t page_songs_selected_n_get(Evas_Object *obj) { PAGE_SONGS_GET_OR_RETURN(page, obj, 0); Elm_Genlist_Item *it; int n; for (n = 0, it = page->first; it && it != page->selected; n++, it = elm_genlist_item_next_get(it)); return (it) ? n : 0; } Song * page_songs_nth_get(Evas_Object *obj, int32_t n) { PAGE_SONGS_GET_OR_RETURN(page, obj, NULL); Elm_Genlist_Item *it = page->first; while (it && n--) it = elm_genlist_item_next_get(it); if (!it) return NULL; return elm_genlist_item_data_get(it); } Song * page_songs_random_go(Evas_Object *obj) { Song *song; int count, song_num; PAGE_SONGS_GET_OR_RETURN(page, obj, NULL); Elm_Genlist_Item *it = page->first; if (!it) return NULL; song_num = (rand() % page->num_elements); for (count = 0; (it) && (count < song_num); count++) it = elm_genlist_item_next_get(it); if (!it) return NULL; song = elm_genlist_item_data_get(it); page->selected = it; elm_genlist_item_selected_set(it, EINA_TRUE); elm_genlist_item_bring_in(it); mpris_signal_player_track_change(song); return song; } Eina_Bool page_songs_prev_exists(const Evas_Object *obj) { PAGE_SONGS_GET_OR_RETURN(page, obj, EINA_FALSE); Elm_Genlist_Item *it = page->selected; if (!it) return EINA_FALSE; it = elm_genlist_item_prev_get(it); return !!it; } Song * page_songs_prev_go(Evas_Object *obj) { PAGE_SONGS_GET_OR_RETURN(page, obj, NULL); Elm_Genlist_Item *it = page->selected; Song *song; if (!it) return NULL; it = elm_genlist_item_prev_get(it); if (!it) return NULL; song = elm_genlist_item_data_get(it); page->selected = it; elm_genlist_item_selected_set(it, EINA_TRUE); elm_genlist_item_bring_in(it); mpris_signal_player_track_change(song); return song; } /*********************************************************************** * FOLDERS **********************************************************************/ static char * _album_item_label_get(void *data, Evas_Object *list, const char *part) { Album *album = data; if (strcmp(part, "ejy.text.artist") == 0) { if (!album->flags.fetched_artist) { DB *db = evas_object_data_get(list, "_enjoy_container"); db_album_artist_fetch(db, album); } if (album->artist) return strdup(album->artist); else return NULL; } return strdup(album->name); } static Evas_Object * _album_item_icon_get(void *data, Evas_Object *list, const char *part __UNUSED__) { Page *page = evas_object_data_get(list, "_enjoy_page"); Album *album = data; return cover_album_fetch(list, page->container, album, page->cls->icon_size); } static void _album_item_del(void *data, Evas_Object *list __UNUSED__) { db_album_free(data); } static void _album_item_selected(void *data, Evas_Object *list __UNUSED__, void *event_info) { Page *page = data; Album *album = elm_genlist_item_data_get(event_info); EINA_SAFETY_ON_NULL_RETURN(album); Evas_Object *next = _page_album_songs_add(page->layout, album); if (next) evas_object_smart_callback_call(page->layout, "folder-songs", next); elm_genlist_item_selected_set(event_info, EINA_FALSE); page->selected = NULL; } static Evas_Object * _page_albums_artist_add(Evas_Object *parent, NameID *nameid, Eina_Iterator *it, const char *title) { static const Elm_Genlist_Item_Class album_item_cls = { "album-artist", { _album_item_label_get, _album_item_icon_get, NULL, _album_item_del } }; static Page_Class album_cls = { "album", "_enjoy_page_albums", "page/albums", NULL, NULL, _album_item_selected, &album_item_cls, PAGE_FOLDERS_POPULATE_ITERATION_COUNT, (void *(*)(const void*))db_album_copy, offsetof(Album, name), 0 }; if (!album_cls.icon_size) { Evas_Object *ed = edje_object_add(evas_object_evas_get(parent)); if (edje_object_file_set (ed, PACKAGE_DATA_DIR"/default.edj", "elm/genlist/item_compress/album-artist/default")) { const char *s = edje_object_data_get(ed, "icon_size"); if (s) album_cls.icon_size = atoi(s); } evas_object_del(ed); if (!album_cls.icon_size) { ERR("Could not get icon_size! assume 32"); album_cls.icon_size = 32; } } return _page_add(parent, nameid, it, title, &album_cls); } static Evas_Object * _page_albums_add(Evas_Object *parent, NameID *nameid, Eina_Iterator *it, const char *title) { static const Elm_Genlist_Item_Class album_item_cls = { "album", { _album_item_label_get, _album_item_icon_get, NULL, _album_item_del } }; static Page_Class album_cls = { "album", "_enjoy_page_albums", "page/albums", NULL, NULL, _album_item_selected, &album_item_cls, PAGE_FOLDERS_POPULATE_ITERATION_COUNT, (void *(*)(const void*))db_album_copy, offsetof(Album, name), 0 }; if (!album_cls.icon_size) { Evas_Object *ed = edje_object_add(evas_object_evas_get(parent)); if (edje_object_file_set (ed, PACKAGE_DATA_DIR"/default.edj", "elm/genlist/item_compress/album/default")) { const char *s = edje_object_data_get(ed, "icon_size"); if (s) album_cls.icon_size = atoi(s); } evas_object_del(ed); if (!album_cls.icon_size) { ERR("Could not get icon_size! assume 32"); album_cls.icon_size = 32; } } return _page_add(parent, nameid, it, title, &album_cls); } static char * _item_all_songs_label_get(void *data __UNUSED__, Evas_Object *list __UNUSED__, const char *part __UNUSED__) { if (strcmp(part, "ejy.text.artist") == 0) return NULL; return strdup("All Songs"); } static Evas_Object * _item_all_songs_icon_get(void *data __UNUSED__, Evas_Object *list __UNUSED__, const char *part __UNUSED__) { Page *page = evas_object_data_get(list, "_enjoy_page"); return cover_allsongs_fetch(list, page->cls->icon_size); } static const Elm_Genlist_Item_Class _item_all_songs_cls = { "all-song", { _item_all_songs_label_get, _item_all_songs_icon_get, NULL, NULL } }; static char * _nameid_item_label_get(void *data, Evas_Object *list __UNUSED__, const char *part __UNUSED__) { NameID *nameid = data; return strdup(nameid->name); } static void _nameid_item_del(void *data, Evas_Object *list __UNUSED__) { db_nameid_free(data); } static void _artist_item_all_songs_selected(void *data, Evas_Object *list __UNUSED__, void *event_info) { Page *page = data; NameID *nameid = page->item.data; DB *db = _page_db_get(page->layout); Eina_Iterator *it = db_artist_songs_get(db, nameid->id); char buf[128]; snprintf(buf, sizeof(buf), "Songs by %s", nameid->name); Evas_Object *next = _page_songs_add(page->layout, nameid, it, buf); if (next) evas_object_smart_callback_call(page->layout, "folder-songs", next); elm_genlist_item_selected_set(event_info, EINA_FALSE); page->selected = NULL; } static void _artist_item_selected(void *data, Evas_Object *list __UNUSED__, void *event_info) { Page *page = data; NameID *nameid = elm_genlist_item_data_get(event_info); EINA_SAFETY_ON_NULL_RETURN(nameid); DB *db = _page_db_get(page->layout); Eina_Iterator *it = db_artist_albums_get(db, nameid->id); char buf[128]; snprintf(buf, sizeof(buf), "Albums by %s", nameid->name); Evas_Object *next = _page_albums_add(page->layout, nameid, it, buf); if (next) { Page *next_page = evas_object_data_get(next, "_enjoy_page"); next_page->item.data = db_nameid_copy(nameid); next_page->item.free = (void (*)(void *))db_nameid_free; elm_genlist_item_append (next_page->list, &_item_all_songs_cls, NULL, NULL, ELM_GENLIST_ITEM_NONE, _artist_item_all_songs_selected, next_page); evas_object_smart_callback_call(page->layout, "folder", next); } elm_genlist_item_selected_set(event_info, EINA_FALSE); page->selected = NULL; } static Evas_Object * _page_artists_add(Evas_Object *parent, NameID *nameid, Eina_Iterator *it, const char *title) { static const Elm_Genlist_Item_Class nameid_item_cls = { "nameid", { _nameid_item_label_get, NULL, NULL, _nameid_item_del } }; static const Page_Class nameid_cls = { "artist", "_enjoy_page_artists", "page/nameids", NULL, NULL, _artist_item_selected, &nameid_item_cls, PAGE_FOLDERS_POPULATE_ITERATION_COUNT, (void *(*)(const void*))db_nameid_copy, offsetof(NameID, name), 0 }; return _page_add(parent, nameid, it, title, &nameid_cls); } static void _genre_item_all_songs_selected(void *data, Evas_Object *list __UNUSED__, void *event_info) { Page *page = data; NameID *nameid = page->item.data; DB *db = _page_db_get(page->layout); Eina_Iterator *it = db_genre_songs_get(db, nameid->id); char buf[128]; snprintf(buf, sizeof(buf), "Songs of %s", nameid->name); Evas_Object *next = _page_songs_add(page->layout, nameid, it, buf); if (next) evas_object_smart_callback_call(page->layout, "folder-songs", next); elm_genlist_item_selected_set(event_info, EINA_FALSE); page->selected = NULL; } static void _genre_item_selected(void *data, Evas_Object *list __UNUSED__, void *event_info) { Page *page = data; NameID *nameid = elm_genlist_item_data_get(event_info); EINA_SAFETY_ON_NULL_RETURN(nameid); DB *db = _page_db_get(page->layout); Eina_Iterator *it = db_genre_albums_get(db, nameid->id); char buf[128]; snprintf(buf, sizeof(buf), "Albums of %s", nameid->name); Evas_Object *next = _page_albums_artist_add(page->layout, nameid, it, buf); if (next) { Page *next_page = evas_object_data_get(next, "_enjoy_page"); next_page->item.data = db_nameid_copy(nameid); next_page->item.free = (void (*)(void *))db_nameid_free; elm_genlist_item_append (next_page->list, &_item_all_songs_cls, NULL, NULL, ELM_GENLIST_ITEM_NONE, _genre_item_all_songs_selected, next_page); evas_object_smart_callback_call(page->layout, "folder", next); } elm_genlist_item_selected_set(event_info, EINA_FALSE); page->selected = NULL; } static Evas_Object * _page_genres_add(Evas_Object *parent, Eina_Iterator *it, const char *title) { static const Elm_Genlist_Item_Class nameid_item_cls = { "nameid", { _nameid_item_label_get, NULL, NULL, _nameid_item_del } }; static const Page_Class nameid_cls = { "genre", "_enjoy_page_genres", "page/nameids", NULL, NULL, _genre_item_selected, &nameid_item_cls, PAGE_FOLDERS_POPULATE_ITERATION_COUNT, (void *(*)(const void*))db_nameid_copy, offsetof(NameID, name), 0 }; return _page_add(parent, NULL, it, title, &nameid_cls); } typedef struct _Static_Item { const char *label; Evas_Object *(*action)(Evas_Object *parent, void *data); const void *data; const char *signal; } Static_Item; static char * _static_item_label_get(void *data, Evas_Object *list __UNUSED__, const char *part __UNUSED__) { Static_Item *root = data; return strdup(root->label); } static void _static_item_selected(void *data, Evas_Object *list __UNUSED__, void *event_info) { Page *page = data; const Static_Item *si = elm_genlist_item_data_get(event_info); Evas_Object *next; EINA_SAFETY_ON_NULL_RETURN(si); EINA_SAFETY_ON_NULL_RETURN(si->action); next = si->action(page->layout, (void *)si->data); if (next) evas_object_smart_callback_call(page->layout, si->signal, next); elm_genlist_item_selected_set(event_info, EINA_FALSE); page->selected = NULL; } static Evas_Object * _page_all_songs_add(Evas_Object *parent, void *data) { PAGE_GET_OR_RETURN(page, parent, NULL); DB *db = _page_db_get(parent); Eina_Iterator *it = db_songs_get(db); return _page_songs_add(parent, NULL, it, data); } static Evas_Object * _page_all_albums_add(Evas_Object *parent, void *data) { PAGE_GET_OR_RETURN(page, parent, NULL); DB *db = _page_db_get(parent); Eina_Iterator *it = db_albums_get(db); return _page_albums_artist_add(parent, NULL, it, data); } static Evas_Object * _page_all_artists_add(Evas_Object *parent, void *data) { PAGE_GET_OR_RETURN(page, parent, NULL); DB *db = _page_db_get(parent); Eina_Iterator *it = db_artists_get(db); return _page_artists_add(parent, NULL, it, data); } static Evas_Object * _page_all_genres_add(Evas_Object *parent, void *data) { PAGE_GET_OR_RETURN(page, parent, NULL); DB *db = _page_db_get(parent); Eina_Iterator *it = db_genres_get(db); return _page_genres_add(parent, it, data); } Evas_Object * page_root_add(Evas_Object *parent) { static const Elm_Genlist_Item_Class root_item_cls = { "root", { _static_item_label_get, NULL, NULL, NULL } }; static const Page_Class root_cls = { "root", "_enjoy_page_roots", "page/roots", NULL, NULL, _static_item_selected, &root_item_cls, PAGE_FOLDERS_POPULATE_ITERATION_COUNT, (void *(*)(const void*))_data_from_itr_passthrough, offsetof(Static_Item, label), 0 }; static const Static_Item root_items[] = { {"All Songs", _page_all_songs_add, "All Songs", "folder-songs"}, {"Albums", _page_all_albums_add, "All Albums", "folder"}, {"Artists", _page_all_artists_add, "All Artists", "folder"}, {"Genres", _page_all_genres_add, "All Genres", "folder"}, }; Eina_Iterator *it = _array_iterator_new (root_items, sizeof(Static_Item), ARRAY_SIZE(root_items)); return _page_add(parent, NULL, it, "Enjoy your music!", &root_cls); }