more meat: local cover arts, custom listings.
this is basically a stub, with covers being fetched from local directories. Further on they must be added to some DB and queried to avoid dir walks every time. Maybe we should also do the lookups in ecore_thread, using a placeholder image while it is fetched. [even more future, but maybe you want to help] listings got customized, with genres -> albums, artists -> albums, and albums show song length and track number. album listings shows covers. I guess this is the way it is gonna be (much like my ipod), so people can start helping with genlist item theme. my future work will be on "nowplaying" mode screen, then theme, then storing covers in db. SVN revision: 52766
This commit is contained in:
parent
eca67db8b8
commit
a881d3d605
|
@ -237,6 +237,7 @@ collections {
|
|||
|
||||
group {
|
||||
name: "page/songs";
|
||||
alias: "page/albums";
|
||||
alias: "page/nameids";
|
||||
|
||||
data.item: "homogeneous" "1";
|
||||
|
@ -374,6 +375,169 @@ collections {
|
|||
}
|
||||
}
|
||||
|
||||
group {
|
||||
name: "page/songs-album";
|
||||
|
||||
data.item: "homogeneous" "1";
|
||||
parts {
|
||||
part {
|
||||
name: "title.bg";
|
||||
type: RECT;
|
||||
mouse_events: 0;
|
||||
description {
|
||||
state: "default" 0.0;
|
||||
color: 128 128 128 255;
|
||||
rel1.to: "ejy.text.title";
|
||||
rel2.to: "ejy.text.title";
|
||||
}
|
||||
}
|
||||
|
||||
part {
|
||||
name: "back";
|
||||
type: RECT;
|
||||
description {
|
||||
state: "default" 0.0;
|
||||
color: 0 255 255 255;
|
||||
rel1 {
|
||||
relative: 0.0 0.0;
|
||||
offset: 10 10;
|
||||
}
|
||||
rel2 {
|
||||
relative: 0.0 0.0;
|
||||
offset: 30 30;
|
||||
}
|
||||
}
|
||||
}
|
||||
programs {
|
||||
program {
|
||||
name: "ejy,back,clicked";
|
||||
signal: "mouse,clicked,1";
|
||||
source: "back";
|
||||
action: SIGNAL_EMIT "ejy,back,clicked" "ejy";
|
||||
}
|
||||
}
|
||||
|
||||
part {
|
||||
name: "songs";
|
||||
type: RECT;
|
||||
description {
|
||||
state: "default" 0.0;
|
||||
color: 255 0 255 0;
|
||||
visible: 0;
|
||||
rel1 {
|
||||
relative: 1.0 0.0;
|
||||
offset: -40 10;
|
||||
}
|
||||
rel2 {
|
||||
relative: 1.0 0.0;
|
||||
offset: -11 30;
|
||||
}
|
||||
}
|
||||
description {
|
||||
state: "visible" 0.0;
|
||||
inherit: "default" 0.0;
|
||||
color: 255 0 255 255;
|
||||
visible: 1;
|
||||
}
|
||||
}
|
||||
programs {
|
||||
program {
|
||||
name: "ejy,songs,clicked";
|
||||
signal: "mouse,clicked,1";
|
||||
source: "songs";
|
||||
action: SIGNAL_EMIT "ejy,songs,clicked" "ejy";
|
||||
}
|
||||
program {
|
||||
name: "ejy,songs,show";
|
||||
signal: "ejy,songs,show";
|
||||
source: "ejy";
|
||||
action: STATE_SET "visible" 0.0;
|
||||
target: "songs";
|
||||
}
|
||||
program {
|
||||
name: "ejy,songs,hide";
|
||||
signal: "ejy,songs,hide";
|
||||
source: "ejy";
|
||||
action: STATE_SET "hidden" 0.0;
|
||||
target: "songs";
|
||||
}
|
||||
}
|
||||
|
||||
part {
|
||||
name: "ejy.text.title";
|
||||
type: TEXT;
|
||||
mouse_events: 0;
|
||||
description {
|
||||
state: "default" 0.0;
|
||||
rel1 {
|
||||
relative: 0.0 0.0;
|
||||
offset: 0 0;
|
||||
}
|
||||
rel2 {
|
||||
relative: 1.0 0.0;
|
||||
offset: -1 100;
|
||||
}
|
||||
text {
|
||||
font: "Sans";
|
||||
size: 10;
|
||||
text: "title";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
part {
|
||||
name: "cover-fallback";
|
||||
type: RECT;
|
||||
description {
|
||||
state: "default" 0.0;
|
||||
color: 255 255 0 255;
|
||||
min: 128 128;
|
||||
max: 128 128;
|
||||
rel1 {
|
||||
relative: 0.0 0.0;
|
||||
offset: 0 110;
|
||||
}
|
||||
rel2 {
|
||||
relative: 1.0 0.0;
|
||||
offset: -1 237;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
part {
|
||||
name: "ejy.swallow.cover";
|
||||
type: SWALLOW;
|
||||
description {
|
||||
state: "default" 0.0;
|
||||
rel1 {
|
||||
relative: 0.0 0.0;
|
||||
offset: 0 110;
|
||||
}
|
||||
rel2 {
|
||||
relative: 1.0 0.0;
|
||||
offset: -1 237;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
part {
|
||||
name: "ejy.swallow.list";
|
||||
type: SWALLOW;
|
||||
description {
|
||||
state: "default" 0.0;
|
||||
rel1 {
|
||||
relative: 0.0 0.0;
|
||||
offset: 0 248;
|
||||
}
|
||||
rel2 {
|
||||
relative: 1.0 1.0;
|
||||
offset: -1 -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
group { name: "elm/genlist/item_compress/song/default";
|
||||
alias: "elm/genlist/item_compress_odd/song/default";
|
||||
data.item: "labels" "ejy.text.title ejy.text.album-artist";
|
||||
|
@ -482,6 +646,172 @@ collections {
|
|||
}
|
||||
}
|
||||
|
||||
group { name: "elm/genlist/item_compress/song-album/default";
|
||||
alias: "elm/genlist/item_compress_odd/song-album/default";
|
||||
data.item: "labels" "ejy.text.title ejy.text.trackno ejy.text.length";
|
||||
parts {
|
||||
part {
|
||||
name: "events";
|
||||
type: RECT;
|
||||
description {
|
||||
state: "default" 0.0;
|
||||
color: 255 255 255 0;
|
||||
}
|
||||
}
|
||||
|
||||
part {
|
||||
name: "ejy.text.trackno";
|
||||
type: TEXT;
|
||||
mouse_events: 0;
|
||||
scale: 1;
|
||||
description {
|
||||
state: "default" 0.0;
|
||||
color: 0 0 0 255;
|
||||
rel1 {
|
||||
relative: 0.0 0.0;
|
||||
offset: 4 2;
|
||||
}
|
||||
rel2 {
|
||||
relative: 0.0 1.0;
|
||||
offset: 40 -3;
|
||||
|
||||
}
|
||||
text {
|
||||
font: "Sans:style=Bold";
|
||||
size: 12;
|
||||
min: 0 1;
|
||||
align: 1.0 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
part {
|
||||
name: "trackno-sep";
|
||||
type: RECT;
|
||||
mouse_events: 0;
|
||||
description {
|
||||
state: "default" 0.0;
|
||||
color: 128 128 128 255;
|
||||
rel1 {
|
||||
relative: 0.0 0.0;
|
||||
offset: 44 0;
|
||||
}
|
||||
rel2 {
|
||||
relative: 0.0 1.0;
|
||||
offset: 44 -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
part {
|
||||
name: "ejy.text.title";
|
||||
type: TEXT;
|
||||
mouse_events: 0;
|
||||
scale: 1;
|
||||
description {
|
||||
state: "default" 0.0;
|
||||
color: 0 0 0 255;
|
||||
rel1 {
|
||||
relative: 0.0 0.0;
|
||||
offset: 48 2;
|
||||
}
|
||||
rel2 {
|
||||
relative: 1.0 1.0;
|
||||
offset: -84 -3;
|
||||
}
|
||||
text {
|
||||
font: "Sans:style=Bold";
|
||||
size: 12;
|
||||
min: 0 1;
|
||||
align: 0.0 0.5;
|
||||
}
|
||||
}
|
||||
description {
|
||||
state: "selected" 0.0;
|
||||
inherit: "default" 0.0;
|
||||
color: 200 20 20 255;
|
||||
}
|
||||
}
|
||||
|
||||
part {
|
||||
name: "length-sep";
|
||||
type: RECT;
|
||||
mouse_events: 0;
|
||||
description {
|
||||
state: "default" 0.0;
|
||||
color: 128 128 128 255;
|
||||
rel1 {
|
||||
relative: 1.0 0.0;
|
||||
offset: -80 0;
|
||||
}
|
||||
rel2 {
|
||||
relative: 1.0 1.0;
|
||||
offset: -80 -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
part {
|
||||
name: "ejy.text.length";
|
||||
type: TEXT;
|
||||
mouse_events: 0;
|
||||
description {
|
||||
state: "default" 0.0;
|
||||
color: 128 128 128 255;
|
||||
rel1 {
|
||||
relative: 1.0 0.0;
|
||||
offset: -76 2;
|
||||
}
|
||||
rel2 {
|
||||
relative: 1.0 1.0;
|
||||
offset: -10 -3;
|
||||
}
|
||||
text {
|
||||
font: "Sans:style=Bold";
|
||||
size: 10;
|
||||
min: 0 1;
|
||||
align: 1.0 0.5;
|
||||
text: "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
part {
|
||||
name: "separator";
|
||||
type: RECT;
|
||||
mouse_events: 0;
|
||||
description {
|
||||
state: "default" 0.0;
|
||||
color: 128 128 128 255;
|
||||
rel1 {
|
||||
relative: 0.0 1.0;
|
||||
offset: 0 -2;
|
||||
}
|
||||
rel2 {
|
||||
relative: 1.0 1.0;
|
||||
offset: -1 -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
programs {
|
||||
program {
|
||||
name: "go_active";
|
||||
signal: "elm,state,selected";
|
||||
source: "elm";
|
||||
action: STATE_SET "selected" 0.0;
|
||||
target: "ejy.text.title";
|
||||
}
|
||||
program {
|
||||
name: "go_passive";
|
||||
signal: "elm,state,unselected";
|
||||
source: "elm";
|
||||
action: STATE_SET "default" 0.0;
|
||||
target: "ejy.text.title";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
group { name: "elm/genlist/item_compress/root/default";
|
||||
alias: "elm/genlist/item_compress_odd/root/default";
|
||||
alias: "elm/genlist/item_compress/nameid/default";
|
||||
|
@ -563,4 +893,236 @@ collections {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
group { name: "elm/genlist/item_compress/album/default";
|
||||
alias: "elm/genlist/item_compress_odd/album/default";
|
||||
data {
|
||||
item: "labels" "ejy.text.album ejy.text.artist";
|
||||
item: "icons" "ejy.swallow.cover";
|
||||
item: "icon_size" "32";
|
||||
}
|
||||
parts {
|
||||
part {
|
||||
name: "events";
|
||||
type: RECT;
|
||||
description {
|
||||
state: "default" 0.0;
|
||||
color: 255 255 255 0;
|
||||
}
|
||||
}
|
||||
|
||||
part {
|
||||
name: "ejy.swallow.cover";
|
||||
type: SWALLOW;
|
||||
mouse_events: 0;
|
||||
description {
|
||||
state: "default" 0.0;
|
||||
min: 32 32;
|
||||
max: 32 32;
|
||||
rel1 {
|
||||
relative: 0.0 0.0;
|
||||
offset: 4 0;
|
||||
}
|
||||
rel2 {
|
||||
relative: 0.0 1.0;
|
||||
offset: 35 -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
part {
|
||||
name: "ejy.text.album";
|
||||
type: TEXT;
|
||||
mouse_events: 0;
|
||||
scale: 1;
|
||||
description {
|
||||
state: "default" 0.0;
|
||||
color: 0 0 0 255;
|
||||
rel1 {
|
||||
relative: 0.0 0.0;
|
||||
offset: 40 2;
|
||||
}
|
||||
rel2 {
|
||||
relative: 1.0 1.0;
|
||||
offset: -5 -3;
|
||||
}
|
||||
text {
|
||||
font: "Sans:style=Bold";
|
||||
size: 12;
|
||||
min: 0 1;
|
||||
align: 0.0 0.5;
|
||||
}
|
||||
}
|
||||
description {
|
||||
state: "selected" 0.0;
|
||||
inherit: "default" 0.0;
|
||||
color: 200 20 20 255;
|
||||
}
|
||||
}
|
||||
|
||||
part {
|
||||
name: "separator";
|
||||
type: RECT;
|
||||
mouse_events: 0;
|
||||
description {
|
||||
state: "default" 0.0;
|
||||
color: 128 128 128 255;
|
||||
rel1 {
|
||||
relative: 0.0 1.0;
|
||||
offset: 0 -2;
|
||||
}
|
||||
rel2 {
|
||||
relative: 1.0 1.0;
|
||||
offset: -1 -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
programs {
|
||||
program {
|
||||
name: "go_active";
|
||||
signal: "elm,state,selected";
|
||||
source: "elm";
|
||||
action: STATE_SET "selected" 0.0;
|
||||
target: "ejy.text.album";
|
||||
}
|
||||
program {
|
||||
name: "go_passive";
|
||||
signal: "elm,state,unselected";
|
||||
source: "elm";
|
||||
action: STATE_SET "default" 0.0;
|
||||
target: "ejy.text.album";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
group { name: "elm/genlist/item_compress/album-artist/default";
|
||||
alias: "elm/genlist/item_compress_odd/album-artist/default";
|
||||
data {
|
||||
item: "labels" "ejy.text.album ejy.text.artist";
|
||||
item: "icons" "ejy.swallow.cover";
|
||||
item: "icon_size" "32";
|
||||
}
|
||||
parts {
|
||||
part {
|
||||
name: "events";
|
||||
type: RECT;
|
||||
description {
|
||||
state: "default" 0.0;
|
||||
color: 255 255 255 0;
|
||||
}
|
||||
}
|
||||
|
||||
part {
|
||||
name: "ejy.swallow.cover";
|
||||
type: SWALLOW;
|
||||
mouse_events: 0;
|
||||
description {
|
||||
state: "default" 0.0;
|
||||
min: 32 32;
|
||||
max: 32 32;
|
||||
rel1 {
|
||||
relative: 0.0 0.0;
|
||||
offset: 4 0;
|
||||
}
|
||||
rel2 {
|
||||
relative: 0.0 1.0;
|
||||
offset: 35 -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
part {
|
||||
name: "ejy.text.album";
|
||||
type: TEXT;
|
||||
mouse_events: 0;
|
||||
scale: 1;
|
||||
description {
|
||||
state: "default" 0.0;
|
||||
color: 0 0 0 255;
|
||||
rel1 {
|
||||
relative: 0.0 0.0;
|
||||
offset: 40 2;
|
||||
}
|
||||
rel2 {
|
||||
relative: 1.0 0.0;
|
||||
offset: -5 -2;
|
||||
to_y: "ejy.text.artist";
|
||||
}
|
||||
text {
|
||||
font: "Sans:style=Bold";
|
||||
size: 12;
|
||||
min: 0 1;
|
||||
align: 0.0 0.5;
|
||||
}
|
||||
}
|
||||
description {
|
||||
state: "selected" 0.0;
|
||||
inherit: "default" 0.0;
|
||||
color: 200 20 20 255;
|
||||
}
|
||||
}
|
||||
|
||||
part {
|
||||
name: "ejy.text.artist";
|
||||
type: TEXT;
|
||||
mouse_events: 0;
|
||||
description {
|
||||
state: "default" 0.0;
|
||||
color: 128 128 128 255;
|
||||
align: 0.0 1.0;
|
||||
fixed: 1 1;
|
||||
rel1 {
|
||||
relative: 0.0 1.0;
|
||||
offset: 40 -3;
|
||||
}
|
||||
rel2 {
|
||||
relative: 1.0 1.0;
|
||||
offset: -5 -3;
|
||||
}
|
||||
text {
|
||||
font: "Sans";
|
||||
size: 8;
|
||||
min: 0 1;
|
||||
align: 0.0 1.0;
|
||||
text: "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
part {
|
||||
name: "separator";
|
||||
type: RECT;
|
||||
mouse_events: 0;
|
||||
description {
|
||||
state: "default" 0.0;
|
||||
color: 128 128 128 255;
|
||||
rel1 {
|
||||
relative: 0.0 1.0;
|
||||
offset: 0 -2;
|
||||
}
|
||||
rel2 {
|
||||
relative: 1.0 1.0;
|
||||
offset: -1 -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
programs {
|
||||
program {
|
||||
name: "go_active";
|
||||
signal: "elm,state,selected";
|
||||
source: "elm";
|
||||
action: STATE_SET "selected" 0.0;
|
||||
target: "ejy.text.album";
|
||||
}
|
||||
program {
|
||||
name: "go_passive";
|
||||
signal: "elm,state,unselected";
|
||||
source: "elm";
|
||||
action: STATE_SET "default" 0.0;
|
||||
target: "ejy.text.album";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ bin_PROGRAMS += enjoy_ql
|
|||
endif
|
||||
|
||||
enjoy_LDADD = @ELEMENTARY_LIBS@ @EMOTION_LIBS@ @LMS_LIBS@ @SQLITE3_LIBS@
|
||||
enjoy_SOURCES = main.c win.c db.c list.c page.c
|
||||
enjoy_SOURCES = main.c win.c db.c list.c page.c cover.c
|
||||
|
||||
if BUILD_QUICKLAUNCH
|
||||
############################################################################
|
||||
|
@ -28,7 +28,7 @@ if BUILD_QUICKLAUNCH
|
|||
############################################################################
|
||||
enjoy_qldir = $(quicklauncher_libdir)
|
||||
enjoy_ql_LTLIBRARIES = enjoy_ql.la
|
||||
enjoy_ql_la_SOURCES = main.c win.c db.c list.c page.c
|
||||
enjoy_ql_la_SOURCES = main.c win.c db.c list.c page.c cover.c
|
||||
enjoy_ql_la_LIBADD = @ELEMENTARY_LIBS@ @EMOTION_LIBS@ @LMS_LIBS@ @SQLITE3_LIBS@
|
||||
enjoy_ql_la_CFLAGS =
|
||||
enjoy_ql_la_LDFLAGS = -module -avoid-version -no-undefined
|
||||
|
|
|
@ -0,0 +1,169 @@
|
|||
#include "private.h"
|
||||
|
||||
static void
|
||||
_cover_album_local_find(Evas *evas, DB *db, Album *album)
|
||||
{
|
||||
Evas_Object *img = evas_object_image_add(evas);
|
||||
Eina_Iterator *it = db_album_songs_get(db, album->id);
|
||||
const Song *song;
|
||||
Eina_List *done_dirs = NULL;
|
||||
|
||||
EINA_ITERATOR_FOREACH(it, song)
|
||||
{
|
||||
DIR *d;
|
||||
struct dirent *de;
|
||||
char dir[PATH_MAX];
|
||||
size_t dir_len = 0, i;
|
||||
const Eina_List *l;
|
||||
const char *s;
|
||||
|
||||
memcpy(dir, song->path, song->len.path + 1);
|
||||
for (i = song->len.path; i > 0; i--)
|
||||
if (dir[i] == '/')
|
||||
{
|
||||
dir[i] = '\0';
|
||||
dir_len = i;
|
||||
break;
|
||||
}
|
||||
if (!dir_len) continue;
|
||||
|
||||
EINA_LIST_FOREACH(done_dirs, l, s)
|
||||
if (strcmp(s, dir) == 0)
|
||||
{
|
||||
dir_len = 0;
|
||||
break;
|
||||
}
|
||||
if (!dir_len) continue;
|
||||
done_dirs = eina_list_append(done_dirs, strdup(dir));
|
||||
|
||||
d = opendir(dir);
|
||||
if (!d)
|
||||
{
|
||||
DBG("could not open directory '%s': %s", dir, strerror(errno));
|
||||
continue;
|
||||
}
|
||||
dir[dir_len] = '/';
|
||||
dir_len++;
|
||||
|
||||
while ((de = readdir(d)) != NULL)
|
||||
{
|
||||
Album_Cover *cover;
|
||||
Evas_Load_Error err;
|
||||
size_t path_len;
|
||||
int w = 0, h = 0;
|
||||
|
||||
if (de->d_name[0] == '.') continue;
|
||||
i = eina_strlcpy(dir + dir_len, de->d_name, PATH_MAX - dir_len);
|
||||
path_len = dir_len + i;
|
||||
|
||||
for (; i > 0; i--)
|
||||
if (dir[dir_len + i] == '.')
|
||||
{
|
||||
const char *ext = dir + dir_len + i + 1;
|
||||
if ((strcasecmp(ext, "jpg") != 0) &&
|
||||
(strcasecmp(ext, "jpeg") != 0) &&
|
||||
(strcasecmp(ext, "png") != 0))
|
||||
i = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == 0) continue;
|
||||
|
||||
evas_object_image_file_set(img, dir, NULL);
|
||||
err = evas_object_image_load_error_get(img);
|
||||
if (err != EVAS_LOAD_ERROR_NONE)
|
||||
{
|
||||
ERR("could not open image %s: %s",
|
||||
dir, evas_load_error_str(err));
|
||||
continue;
|
||||
}
|
||||
evas_object_image_size_get(img, &w, &h);
|
||||
if ((w == 0) || (h == 0))
|
||||
{
|
||||
ERR("could not get image size %s", dir);
|
||||
continue;
|
||||
}
|
||||
|
||||
cover = malloc(sizeof(Album_Cover) + path_len + 1);
|
||||
if (!cover)
|
||||
{
|
||||
ERR("could not allocate memory");
|
||||
continue;
|
||||
}
|
||||
cover->w = w;
|
||||
cover->h = h;
|
||||
cover->path_len = path_len;
|
||||
memcpy(cover->path, dir, path_len + 1);
|
||||
|
||||
album->covers = eina_inlist_append
|
||||
(album->covers, EINA_INLIST_GET(cover));
|
||||
}
|
||||
|
||||
closedir(d);
|
||||
}
|
||||
|
||||
eina_iterator_free(it);
|
||||
evas_object_del(img);
|
||||
|
||||
if (done_dirs)
|
||||
{
|
||||
char *dir;
|
||||
EINA_LIST_FREE(done_dirs, dir) free(dir);
|
||||
}
|
||||
}
|
||||
|
||||
Evas_Object *
|
||||
cover_album_fetch(Evas_Object *parent, DB *db, Album *album, unsigned short size)
|
||||
{
|
||||
Evas_Object *cover;
|
||||
unsigned int min_error = (unsigned int)-1;
|
||||
const Album_Cover *itr, *best_match;
|
||||
unsigned int fetches = 0;
|
||||
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(album, NULL);
|
||||
|
||||
if (!album->flags.fetched_covers) db_album_covers_fetch(db, album);
|
||||
|
||||
fetch_local:
|
||||
fetches++;
|
||||
best_match = NULL;
|
||||
if (!album->covers)
|
||||
_cover_album_local_find(evas_object_evas_get(parent), db, album);
|
||||
|
||||
EINA_INLIST_FOREACH(album->covers, itr)
|
||||
{
|
||||
unsigned int cur_error;
|
||||
unsigned short cur_size = (itr->w > itr->h) ? itr->w : itr->h;
|
||||
|
||||
if (cur_size > size) cur_error = cur_size - size;
|
||||
else cur_error = size - cur_size;
|
||||
|
||||
if (min_error > cur_error)
|
||||
{
|
||||
min_error = cur_error;
|
||||
best_match = itr;
|
||||
if (cur_error == 0) break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!best_match) return NULL;
|
||||
|
||||
cover = elm_icon_add(parent);
|
||||
elm_icon_smooth_set(cover, size >= 32);
|
||||
elm_icon_prescale_set(cover, size);
|
||||
if (!elm_icon_file_set(cover, best_match->path, NULL))
|
||||
{
|
||||
while (album->covers)
|
||||
{
|
||||
void *n = album->covers;
|
||||
album->covers = eina_inlist_remove(album->covers, album->covers);
|
||||
free(n);
|
||||
}
|
||||
db_album_covers_update(db, album);
|
||||
evas_object_del(cover);
|
||||
if (fetches < 2) goto fetch_local;
|
||||
else return NULL;
|
||||
}
|
||||
|
||||
return cover;
|
||||
}
|
326
src/bin/db.c
326
src/bin/db.c
|
@ -157,7 +157,7 @@ Song *db_song_copy(const Song *orig)
|
|||
// TODO: move to mempool to avoid fragmentation!
|
||||
Song *copy;
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(orig, NULL);
|
||||
copy = calloc(1, sizeof(Song));
|
||||
copy = malloc(sizeof(Song));
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(copy, NULL);
|
||||
|
||||
/* Note: cannot use eina_stringshare_ref() as value may be from sqlite
|
||||
|
@ -184,6 +184,8 @@ Song *db_song_copy(const Song *orig)
|
|||
N(length);
|
||||
#undef N
|
||||
|
||||
copy->flags = orig->flags;
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
|
@ -328,14 +330,48 @@ db_songs_get(DB *db)
|
|||
Eina_Bool
|
||||
db_song_rating_set(DB *db, Song *song, int rating)
|
||||
{
|
||||
DBG("TODO");
|
||||
char sql[128], *errmsg = NULL;
|
||||
int r;
|
||||
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(db, EINA_FALSE);
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(song, EINA_FALSE);
|
||||
|
||||
sqlite3_snprintf(sizeof(sql), sql,
|
||||
"UPDATE audios SET playcnt = %d WHERE id = %lld",
|
||||
rating, song->id);
|
||||
|
||||
r = sqlite3_exec(db->handle, sql, NULL, NULL, &errmsg);
|
||||
if (r != SQLITE_OK)
|
||||
{
|
||||
ERR("Could not execute SQL %s: %s", sql, errmsg);
|
||||
sqlite3_free(errmsg);
|
||||
return EINA_FALSE;
|
||||
}
|
||||
song->rating = rating;
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
Eina_Bool
|
||||
db_song_length_set(DB *db, Song *song, int length)
|
||||
{
|
||||
DBG("TODO");
|
||||
char sql[128], *errmsg = NULL;
|
||||
int r;
|
||||
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(db, EINA_FALSE);
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(song, EINA_FALSE);
|
||||
|
||||
sqlite3_snprintf(sizeof(sql), sql,
|
||||
"UPDATE audios SET length = %d WHERE id = %lld",
|
||||
length, song->id);
|
||||
|
||||
r = sqlite3_exec(db->handle, sql, NULL, NULL, &errmsg);
|
||||
if (r != SQLITE_OK)
|
||||
{
|
||||
ERR("Could not execute SQL %s: %s", sql, errmsg);
|
||||
sqlite3_free(errmsg);
|
||||
return EINA_FALSE;
|
||||
}
|
||||
song->length = length;
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
|
@ -585,6 +621,263 @@ db_song_genre_fetch(DB *db, Song *song)
|
|||
return ret;
|
||||
}
|
||||
|
||||
Album *
|
||||
db_album_copy(const Album *orig)
|
||||
{
|
||||
Album *copy;
|
||||
const Album_Cover *orig_cover;
|
||||
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(orig, NULL);
|
||||
copy = malloc(sizeof(Album));
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(copy, NULL);
|
||||
copy->id = orig->id;
|
||||
copy->artist_id = orig->artist_id;
|
||||
copy->name = eina_stringshare_add(orig->name);
|
||||
copy->artist = eina_stringshare_add(orig->artist);
|
||||
copy->len.name = orig->len.name;
|
||||
copy->len.artist = orig->len.artist;
|
||||
copy->flags = orig->flags;
|
||||
|
||||
copy->covers = NULL;
|
||||
EINA_INLIST_FOREACH(orig->covers, orig_cover)
|
||||
{
|
||||
Album_Cover *copy_cover;
|
||||
|
||||
copy_cover = malloc(sizeof(Album_Cover) + orig_cover->path_len + 1);
|
||||
if (!copy_cover) break;
|
||||
copy_cover->w = orig_cover->w;
|
||||
copy_cover->h = orig_cover->h;
|
||||
copy_cover->path_len = orig_cover->path_len;
|
||||
memcpy(copy_cover->path, orig_cover->path, orig_cover->path_len + 1);
|
||||
copy->covers = eina_inlist_append
|
||||
(copy->covers, EINA_INLIST_GET(copy_cover));
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
void
|
||||
db_album_free(Album *album)
|
||||
{
|
||||
while (album->covers)
|
||||
{
|
||||
void *n = album->covers;
|
||||
album->covers = eina_inlist_remove(album->covers, album->covers);
|
||||
free(n);
|
||||
}
|
||||
|
||||
eina_stringshare_del(album->name);
|
||||
eina_stringshare_del(album->artist);
|
||||
free(album);
|
||||
}
|
||||
|
||||
Eina_Bool
|
||||
db_album_artist_fetch(DB *db, Album *album)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
Eina_Bool ret;
|
||||
int err;
|
||||
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(db, EINA_FALSE);
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(album, EINA_FALSE);
|
||||
if (album->flags.fetched_artist) return EINA_TRUE;
|
||||
|
||||
stmt = db->stmt.artist_get;
|
||||
if (!_db_stmt_bind_int64(stmt, 1, album->artist_id))
|
||||
return EINA_FALSE;
|
||||
|
||||
err = sqlite3_step(stmt);
|
||||
if (err == SQLITE_ROW)
|
||||
{
|
||||
eina_stringshare_replace
|
||||
(&album->artist, (const char *)sqlite3_column_text(stmt, 0));
|
||||
album->len.artist = sqlite3_column_bytes(stmt, 0);
|
||||
ret = EINA_TRUE;
|
||||
}
|
||||
else if (err == SQLITE_DONE)
|
||||
{
|
||||
DBG("no artist with id=%lld", (long long)album->artist_id);
|
||||
eina_stringshare_replace(&album->artist, NULL);
|
||||
album->len.artist = 0;
|
||||
ret = EINA_TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERR("could not query artist with id=%lld: %s",
|
||||
(long long)album->artist_id, sqlite3_errmsg(db->handle));
|
||||
ret = EINA_FALSE;
|
||||
}
|
||||
|
||||
_db_stmt_reset(stmt);
|
||||
album->flags.fetched_artist = ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
Eina_Bool
|
||||
db_album_covers_fetch(DB *db, Album *album)
|
||||
{
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(db, EINA_FALSE);
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(album, EINA_FALSE);
|
||||
if (album->flags.fetched_covers) return EINA_TRUE;
|
||||
|
||||
DBG("todo: create and handle 'covers' table");
|
||||
|
||||
album->flags.fetched_covers = EINA_TRUE;
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
Eina_Bool
|
||||
db_album_covers_update(DB *db, const Album *album)
|
||||
{
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(db, EINA_FALSE);
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(album, EINA_FALSE);
|
||||
|
||||
DBG("todo: create and handle 'covers' table");
|
||||
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
|
||||
struct DB_Iterator_Album
|
||||
{
|
||||
struct DB_Iterator base;
|
||||
Album album;
|
||||
};
|
||||
|
||||
static Eina_Bool
|
||||
_db_iterator_album_next(Eina_Iterator *iterator, void **data)
|
||||
{
|
||||
struct DB_Iterator_Album *it = (struct DB_Iterator_Album *)iterator;
|
||||
Album **album = (Album **)data;
|
||||
int r;
|
||||
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(album, EINA_FALSE);
|
||||
*album = NULL;
|
||||
if (!EINA_MAGIC_CHECK(iterator, EINA_MAGIC_ITERATOR))
|
||||
{
|
||||
EINA_MAGIC_FAIL(iterator, EINA_MAGIC_ITERATOR);
|
||||
return EINA_FALSE;
|
||||
}
|
||||
|
||||
r = sqlite3_step(it->base.stmt);
|
||||
if (r == SQLITE_DONE)
|
||||
return EINA_FALSE;
|
||||
if (r != SQLITE_ROW)
|
||||
{
|
||||
ERR("Error executing sql statement: %s",
|
||||
sqlite3_errmsg(it->base.db->handle));
|
||||
return EINA_FALSE;
|
||||
}
|
||||
|
||||
it->album.id = sqlite3_column_int64(it->base.stmt, 0);
|
||||
it->album.artist_id = sqlite3_column_int64(it->base.stmt, 1);
|
||||
it->album.name = (const char *)sqlite3_column_text(it->base.stmt, 2);
|
||||
it->album.len.name = sqlite3_column_bytes(it->base.stmt, 2);
|
||||
|
||||
*album = &it->album;
|
||||
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
Eina_Iterator *
|
||||
db_albums_get(DB *db)
|
||||
{
|
||||
struct DB_Iterator_Album *it;
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(db, NULL);
|
||||
it = calloc(1, sizeof(*it));
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(it, NULL);
|
||||
|
||||
it->base.base.version = EINA_ITERATOR_VERSION;
|
||||
it->base.base.next = _db_iterator_album_next;
|
||||
it->base.base.get_container = _db_iterator_container_get;
|
||||
it->base.base.free = _db_iterator_free;
|
||||
it->base.db = db;
|
||||
it->base.stmt_name = "albums_get";
|
||||
it->base.stmt = _db_stmt_compile
|
||||
(db, it->base.stmt_name,
|
||||
"SELECT id, artist_id, name FROM audio_albums ORDER BY UPPER(name)");
|
||||
if (!it->base.stmt)
|
||||
{
|
||||
free(it);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
EINA_MAGIC_SET(&it->base.base, EINA_MAGIC_ITERATOR);
|
||||
return &it->base.base;
|
||||
}
|
||||
|
||||
Eina_Iterator *
|
||||
db_artist_albums_get(DB *db, int64_t artist_id)
|
||||
{
|
||||
struct DB_Iterator_Album *it;
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(db, NULL);
|
||||
it = calloc(1, sizeof(*it));
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(it, NULL);
|
||||
|
||||
it->base.base.version = EINA_ITERATOR_VERSION;
|
||||
it->base.base.next = _db_iterator_album_next;
|
||||
it->base.base.get_container = _db_iterator_container_get;
|
||||
it->base.base.free = _db_iterator_free;
|
||||
it->base.db = db;
|
||||
it->base.stmt_name = "artist_albums_get";
|
||||
it->base.stmt = _db_stmt_compile
|
||||
(db, it->base.stmt_name,
|
||||
"SELECT id, artist_id, name FROM audio_albums "
|
||||
"WHERE artist_id = ? ORDER BY UPPER(name)");
|
||||
if (!it->base.stmt)
|
||||
{
|
||||
free(it);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!_db_stmt_bind_int64(it->base.stmt, 1, artist_id))
|
||||
{
|
||||
free(it);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
EINA_MAGIC_SET(&it->base.base, EINA_MAGIC_ITERATOR);
|
||||
return &it->base.base;
|
||||
}
|
||||
|
||||
Eina_Iterator *
|
||||
db_genre_albums_get(DB *db, int64_t genre_id)
|
||||
{
|
||||
struct DB_Iterator_Album *it;
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(db, NULL);
|
||||
it = calloc(1, sizeof(*it));
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(it, NULL);
|
||||
|
||||
it->base.base.version = EINA_ITERATOR_VERSION;
|
||||
it->base.base.next = _db_iterator_album_next;
|
||||
it->base.base.get_container = _db_iterator_container_get;
|
||||
it->base.base.free = _db_iterator_free;
|
||||
it->base.db = db;
|
||||
it->base.stmt_name = "genre_albums_get";
|
||||
it->base.stmt = _db_stmt_compile
|
||||
(db, it->base.stmt_name,
|
||||
"SELECT DISTINCT "
|
||||
" audio_albums.id, audio_albums.artist_id, audio_albums.name "
|
||||
"FROM audios, audio_albums "
|
||||
"WHERE audios.album_id = audio_albums.id "
|
||||
"AND audios.genre_id = ? "
|
||||
"ORDER BY UPPER(audio_albums.name)");
|
||||
if (!it->base.stmt)
|
||||
{
|
||||
free(it);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!_db_stmt_bind_int64(it->base.stmt, 1, genre_id))
|
||||
{
|
||||
free(it);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
EINA_MAGIC_SET(&it->base.base, EINA_MAGIC_ITERATOR);
|
||||
return &it->base.base;
|
||||
}
|
||||
|
||||
NameID *
|
||||
db_nameid_copy(const NameID *orig)
|
||||
{
|
||||
|
@ -645,33 +938,6 @@ _db_iterator_nameid_next(Eina_Iterator *iterator, void **data)
|
|||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
Eina_Iterator *
|
||||
db_albums_get(DB *db)
|
||||
{
|
||||
struct DB_Iterator_NameID *it;
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(db, NULL);
|
||||
it = calloc(1, sizeof(*it));
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(it, NULL);
|
||||
|
||||
it->base.base.version = EINA_ITERATOR_VERSION;
|
||||
it->base.base.next = _db_iterator_nameid_next;
|
||||
it->base.base.get_container = _db_iterator_container_get;
|
||||
it->base.base.free = _db_iterator_free;
|
||||
it->base.db = db;
|
||||
it->base.stmt_name = "albums_get";
|
||||
it->base.stmt = _db_stmt_compile
|
||||
(db, it->base.stmt_name,
|
||||
"SELECT id, name FROM audio_albums ORDER BY UPPER(name)");
|
||||
if (!it->base.stmt)
|
||||
{
|
||||
free(it);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
EINA_MAGIC_SET(&it->base.base, EINA_MAGIC_ITERATOR);
|
||||
return &it->base.base;
|
||||
}
|
||||
|
||||
Eina_Iterator *
|
||||
db_artists_get(DB *db)
|
||||
{
|
||||
|
|
|
@ -212,6 +212,14 @@ list_selected_get(const Evas_Object *obj)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
Eina_Bool
|
||||
list_song_updated(Evas_Object *obj)
|
||||
{
|
||||
LIST_GET_OR_RETURN(list, obj, EINA_FALSE);
|
||||
if (list->page.songs) return page_songs_song_updated(list->page.songs);
|
||||
return EINA_FALSE;
|
||||
}
|
||||
|
||||
Eina_Bool
|
||||
list_next_exists(const Evas_Object *obj)
|
||||
{
|
||||
|
|
293
src/bin/page.c
293
src/bin/page.c
|
@ -97,6 +97,7 @@ typedef struct _Page_Class
|
|||
size_t populate_iteration_count;
|
||||
void *(*data_from_itr)(const void *data);
|
||||
size_t data_letter_offset;
|
||||
unsigned short icon_size;
|
||||
} Page_Class;
|
||||
|
||||
typedef struct _Page
|
||||
|
@ -167,7 +168,8 @@ _page_populate(void *data)
|
|||
letter_str = (const char **)(((char *)od) + cls->data_letter_offset);
|
||||
|
||||
letter = toupper((*letter_str)[0]);
|
||||
if (isalpha(letter) && (page->last_index_letter[0] != letter))
|
||||
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);
|
||||
|
@ -229,7 +231,7 @@ _page_songs(void *data, Evas_Object *o __UNUSED__, const char *emission __UNUSED
|
|||
}
|
||||
|
||||
static Evas_Object *
|
||||
_page_add(Evas_Object *parent, Eina_Iterator *it, const char *title, const Page_Class *cls)
|
||||
_page_add(Evas_Object *parent, Eina_Iterator *it, const char *title, Evas_Object *cover, const Page_Class *cls)
|
||||
{
|
||||
Evas_Object *obj;
|
||||
Page *page;
|
||||
|
@ -295,13 +297,22 @@ _page_add(Evas_Object *parent, Eina_Iterator *it, const char *title, const Page_
|
|||
|
||||
elm_layout_content_set(obj, "ejy.swallow.list", page->list);
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
if (edje_object_part_exists(page->edje, "ejy.swallow.cover"))
|
||||
elm_layout_content_set(obj, "ejy.swallow.cover", cover);
|
||||
else
|
||||
evas_object_del(cover);
|
||||
|
||||
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);
|
||||
|
||||
page->populate = ecore_idler_add(_page_populate, page);
|
||||
|
@ -494,9 +505,36 @@ _page_songs_add(Evas_Object *parent, Eina_Iterator *it, const char *title)
|
|||
&song_item_cls,
|
||||
PAGE_SONGS_POPULATE_ITERATION_COUNT,
|
||||
(void *(*)(const void*))db_song_copy,
|
||||
offsetof(Song, title)
|
||||
offsetof(Song, title),
|
||||
0
|
||||
};
|
||||
return _page_add(parent, it, title, &song_cls);
|
||||
return _page_add(parent, it, title, NULL, &song_cls);
|
||||
}
|
||||
|
||||
static Evas_Object *
|
||||
_page_album_songs_add(Evas_Object *parent, Eina_Iterator *it, Evas_Object *cover, const char *title)
|
||||
{
|
||||
static const Elm_Genlist_Item_Class song_item_cls = {
|
||||
"song-album",
|
||||
{
|
||||
_song_item_label_get,
|
||||
NULL,
|
||||
NULL,
|
||||
_song_item_del
|
||||
}
|
||||
};
|
||||
static const Page_Class song_cls = {
|
||||
"song",
|
||||
"_enjoy_page_songs",
|
||||
"page/songs-album",
|
||||
_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, it, title, cover, &song_cls);
|
||||
}
|
||||
|
||||
Song *
|
||||
|
@ -506,6 +544,18 @@ page_songs_selected_get(const Evas_Object *obj)
|
|||
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)
|
||||
{
|
||||
|
@ -561,6 +611,158 @@ page_songs_prev_go(Evas_Object *obj)
|
|||
* 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);
|
||||
DB *db = _page_db_get(page->layout);
|
||||
Eina_Iterator *it = db_album_songs_get(db, album->id);
|
||||
Evas_Object *cover;
|
||||
char buf[128];
|
||||
|
||||
if ((!album->artist) && (!album->flags.fetched_artist))
|
||||
db_album_artist_fetch(db, album);
|
||||
|
||||
if (album->artist)
|
||||
snprintf(buf, sizeof(buf), "Songs of %s by %s",
|
||||
album->name, album->artist);
|
||||
else
|
||||
snprintf(buf, sizeof(buf), "Songs of %s", album->name);
|
||||
|
||||
cover = cover_album_fetch(page->layout, db, album, 256); // TODO: size!
|
||||
Evas_Object *next = _page_album_songs_add(page->layout, it, cover, buf);
|
||||
if (next)
|
||||
evas_object_smart_callback_call(page->layout, "folder-songs", next);
|
||||
else
|
||||
evas_object_del(cover);
|
||||
elm_genlist_item_selected_set(event_info, EINA_FALSE);
|
||||
page->selected = NULL;
|
||||
}
|
||||
|
||||
static Evas_Object *
|
||||
_page_albums_artist_add(Evas_Object *parent, 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",
|
||||
_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, it, title, NULL, &album_cls);
|
||||
}
|
||||
|
||||
static Evas_Object *
|
||||
_page_albums_add(Evas_Object *parent, 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",
|
||||
_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, it, title, NULL, &album_cls);
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
_nameid_item_label_get(void *data, Evas_Object *list __UNUSED__, const char *part __UNUSED__)
|
||||
|
@ -575,48 +777,6 @@ _nameid_item_del(void *data, Evas_Object *list __UNUSED__)
|
|||
db_nameid_free(data);
|
||||
}
|
||||
|
||||
static void
|
||||
_album_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_album_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, 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 Evas_Object *
|
||||
_page_albums_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 = {
|
||||
"album",
|
||||
"_enjoy_page_albums",
|
||||
"page/nameids",
|
||||
_album_item_selected,
|
||||
&nameid_item_cls,
|
||||
PAGE_FOLDERS_POPULATE_ITERATION_COUNT,
|
||||
(void *(*)(const void*))db_nameid_copy,
|
||||
offsetof(NameID, name)
|
||||
};
|
||||
return _page_add(parent, it, title, &nameid_cls);
|
||||
}
|
||||
|
||||
static void
|
||||
_artist_item_selected(void *data, Evas_Object *list __UNUSED__, void *event_info)
|
||||
{
|
||||
|
@ -624,12 +784,12 @@ _artist_item_selected(void *data, Evas_Object *list __UNUSED__, void *event_info
|
|||
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_songs_get(db, nameid->id);
|
||||
Eina_Iterator *it = db_artist_albums_get(db, nameid->id);
|
||||
char buf[128];
|
||||
snprintf(buf, sizeof(buf), "Songs by %s", nameid->name);
|
||||
Evas_Object *next = _page_songs_add(page->layout, it, buf);
|
||||
snprintf(buf, sizeof(buf), "Albums by %s", nameid->name);
|
||||
Evas_Object *next = _page_albums_add(page->layout, it, buf);
|
||||
if (next)
|
||||
evas_object_smart_callback_call(page->layout, "folder-songs", next);
|
||||
evas_object_smart_callback_call(page->layout, "folder", next);
|
||||
elm_genlist_item_selected_set(event_info, EINA_FALSE);
|
||||
page->selected = NULL;
|
||||
}
|
||||
|
@ -654,9 +814,10 @@ _page_artists_add(Evas_Object *parent, Eina_Iterator *it, const char *title)
|
|||
&nameid_item_cls,
|
||||
PAGE_FOLDERS_POPULATE_ITERATION_COUNT,
|
||||
(void *(*)(const void*))db_nameid_copy,
|
||||
offsetof(NameID, name)
|
||||
offsetof(NameID, name),
|
||||
0
|
||||
};
|
||||
return _page_add(parent, it, title, &nameid_cls);
|
||||
return _page_add(parent, it, title, NULL, &nameid_cls);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -666,12 +827,12 @@ _genre_item_selected(void *data, Evas_Object *list __UNUSED__, void *event_info)
|
|||
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_songs_get(db, nameid->id);
|
||||
Eina_Iterator *it = db_genre_albums_get(db, nameid->id);
|
||||
char buf[128];
|
||||
snprintf(buf, sizeof(buf), "Songs of %s", nameid->name);
|
||||
Evas_Object *next = _page_songs_add(page->layout, it, buf);
|
||||
snprintf(buf, sizeof(buf), "Albums of %s", nameid->name);
|
||||
Evas_Object *next = _page_albums_artist_add(page->layout, it, buf);
|
||||
if (next)
|
||||
evas_object_smart_callback_call(page->layout, "folder-songs", next);
|
||||
evas_object_smart_callback_call(page->layout, "folder", next);
|
||||
elm_genlist_item_selected_set(event_info, EINA_FALSE);
|
||||
page->selected = NULL;
|
||||
}
|
||||
|
@ -696,9 +857,10 @@ _page_genres_add(Evas_Object *parent, Eina_Iterator *it, const char *title)
|
|||
&nameid_item_cls,
|
||||
PAGE_FOLDERS_POPULATE_ITERATION_COUNT,
|
||||
(void *(*)(const void*))db_nameid_copy,
|
||||
offsetof(NameID, name)
|
||||
offsetof(NameID, name),
|
||||
0
|
||||
};
|
||||
return _page_add(parent, it, title, &nameid_cls);
|
||||
return _page_add(parent, it, title, NULL, &nameid_cls);
|
||||
}
|
||||
|
||||
typedef struct _Static_Item
|
||||
|
@ -746,7 +908,7 @@ _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_add(parent, it, data);
|
||||
return _page_albums_artist_add(parent, it, data);
|
||||
}
|
||||
|
||||
static Evas_Object *
|
||||
|
@ -781,7 +943,8 @@ page_root_add(Evas_Object *parent)
|
|||
&root_item_cls,
|
||||
PAGE_FOLDERS_POPULATE_ITERATION_COUNT,
|
||||
(void *(*)(const void*))_data_from_itr_passthrough,
|
||||
offsetof(Static_Item, label)
|
||||
offsetof(Static_Item, label),
|
||||
0
|
||||
};
|
||||
static const Static_Item root_items[] = {
|
||||
{"All Songs", _page_all_songs_add, "All Songs", "folder-songs"},
|
||||
|
@ -791,5 +954,5 @@ page_root_add(Evas_Object *parent)
|
|||
};
|
||||
Eina_Iterator *it = _array_iterator_new
|
||||
(root_items, sizeof(Static_Item), ARRAY_SIZE(root_items));
|
||||
return _page_add(parent, it, "Enjoy your music!", &root_cls);
|
||||
return _page_add(parent, it, "Enjoy your music!", NULL, &root_cls);
|
||||
}
|
||||
|
|
|
@ -14,7 +14,8 @@ typedef struct _App App;
|
|||
typedef struct _DB DB;
|
||||
typedef struct _Song Song;
|
||||
typedef struct _NameID NameID;
|
||||
typedef struct _NameID Album;
|
||||
typedef struct _Album_Cover Album_Cover;
|
||||
typedef struct _Album Album;
|
||||
typedef struct _NameID Artist;
|
||||
typedef struct _NameID Genre;
|
||||
|
||||
|
@ -41,6 +42,7 @@ Eina_Bool list_populate(Evas_Object *list, DB *db);
|
|||
Eina_Bool list_songs_show(Evas_Object *obj);
|
||||
Eina_Bool list_songs_exists(const Evas_Object *obj);
|
||||
Song *list_selected_get(const Evas_Object *list);
|
||||
Eina_Bool list_song_updated(Evas_Object *obj);
|
||||
Eina_Bool list_next_exists(const Evas_Object *list);
|
||||
Song *list_next_go(Evas_Object *list);
|
||||
Eina_Bool list_prev_exists(const Evas_Object *list);
|
||||
|
@ -54,11 +56,14 @@ void page_songs_exists_changed(Evas_Object *obj, Eina_Bool exists);
|
|||
Evas_Object *page_root_add(Evas_Object *parent);
|
||||
|
||||
Song *page_songs_selected_get(const Evas_Object *obj);
|
||||
Eina_Bool page_songs_song_updated(Evas_Object *obj);
|
||||
Eina_Bool page_songs_next_exists(const Evas_Object *obj);
|
||||
Song *page_songs_next_go(Evas_Object *obj);
|
||||
Eina_Bool page_songs_prev_exists(const Evas_Object *obj);
|
||||
Song *page_songs_prev_go(Evas_Object *obj);
|
||||
|
||||
Evas_Object *cover_album_fetch(Evas_Object *parent, DB *db, Album *album, unsigned short size);
|
||||
|
||||
|
||||
DB *db_open(const char *path);
|
||||
Eina_Bool db_close(DB *db);
|
||||
|
@ -93,6 +98,32 @@ struct _Song
|
|||
} flags;
|
||||
};
|
||||
|
||||
struct _Album_Cover
|
||||
{
|
||||
EINA_INLIST;
|
||||
unsigned short w;
|
||||
unsigned short h;
|
||||
unsigned short path_len;
|
||||
char path[];
|
||||
};
|
||||
|
||||
struct _Album
|
||||
{
|
||||
int64_t id;
|
||||
int64_t artist_id;
|
||||
const char *name;
|
||||
const char *artist;
|
||||
Eina_Inlist *covers;
|
||||
struct {
|
||||
unsigned int name;
|
||||
unsigned int artist;
|
||||
} len; /* strlen of string fields */
|
||||
struct { /* not from db, for runtime use */
|
||||
Eina_Bool fetched_artist:1;
|
||||
Eina_Bool fetched_covers:1;
|
||||
} flags;
|
||||
};
|
||||
|
||||
struct _NameID
|
||||
{
|
||||
int64_t id;
|
||||
|
@ -109,22 +140,32 @@ Eina_Bool db_song_album_fetch(DB *db, Song *song);
|
|||
Eina_Bool db_song_artist_fetch(DB *db, Song *song);
|
||||
Eina_Bool db_song_genre_fetch(DB *db, Song *song);
|
||||
|
||||
/* walks over 'const Song*' */
|
||||
Eina_Iterator *db_album_songs_get(DB *db, int64_t album_id);
|
||||
Eina_Iterator *db_artist_songs_get(DB *db, int64_t artist_id);
|
||||
Eina_Iterator *db_genre_songs_get(DB *db, int64_t genre_id);
|
||||
|
||||
/* walks over 'const Album*' */
|
||||
Eina_Iterator *db_artist_albums_get(DB *db, int64_t artist_id);
|
||||
Eina_Iterator *db_genre_albums_get(DB *db, int64_t genre_id);
|
||||
|
||||
|
||||
Eina_Iterator *db_albums_get(DB *db); /* walks over 'const Album*' */
|
||||
Eina_Iterator *db_artists_get(DB *db); /* walks over 'const Artist*' */
|
||||
Eina_Iterator *db_genres_get(DB *db); /* walks over 'const Genre*' */
|
||||
|
||||
|
||||
Album *db_album_copy(const Album *album);
|
||||
Eina_Bool db_album_artist_fetch(DB *db, Album *album);
|
||||
Eina_Bool db_album_covers_fetch(DB *db, Album *album);
|
||||
Eina_Bool db_album_covers_update(DB *db, const Album *album);
|
||||
void db_album_free(Album *album);
|
||||
|
||||
NameID *db_nameid_copy(const NameID *nameid);
|
||||
#define db_album_copy(v) db_nameid_copy(v)
|
||||
#define db_artist_copy(v) db_nameid_copy(v)
|
||||
#define db_genre_copy(v) db_nameid_copy(v)
|
||||
|
||||
void db_nameid_free(NameID *nameid);
|
||||
#define db_album_free(v) db_nameid_free(v)
|
||||
#define db_artist_free(v) db_nameid_free(v)
|
||||
#define db_genre_free(v) db_nameid_free(v)
|
||||
|
||||
|
|
|
@ -31,6 +31,9 @@ typedef struct Win
|
|||
struct {
|
||||
Eina_List *add, *del;
|
||||
} scan;
|
||||
struct {
|
||||
Ecore_Timer *play_eval;
|
||||
} timer;
|
||||
struct {
|
||||
Ecore_Job *scan;
|
||||
Ecore_Job *populate;
|
||||
|
@ -99,7 +102,6 @@ _win_scan_job(void *data)
|
|||
static void
|
||||
_win_toolbar_eval(Win *w)
|
||||
{
|
||||
printf("eval toolbar: prev=%hhu, next=%hhu\n", list_prev_exists(w->list), list_next_exists(w->list));
|
||||
if (list_prev_exists(w->list))
|
||||
edje_object_signal_emit(w->edje, "ejy,prev,enable", "ejy");
|
||||
else
|
||||
|
@ -131,6 +133,18 @@ _win_play_eval(Win *w)
|
|||
{
|
||||
Edje_Message_Float_Set *mf;
|
||||
|
||||
if (w->play.length <= 0.0)
|
||||
{
|
||||
w->play.length = emotion_object_play_length_get(w->emotion);
|
||||
if ((w->song) && (w->song->length != (int)w->play.length))
|
||||
{
|
||||
db_song_length_set(w->db, w->song, w->play.length);
|
||||
list_song_updated(w->list);
|
||||
}
|
||||
}
|
||||
|
||||
w->play.position = emotion_object_position_get(w->emotion);
|
||||
|
||||
mf = alloca(sizeof(Edje_Message_Float_Set) + sizeof(double));
|
||||
mf->count = 2;
|
||||
mf->val[0] = w->play.position;
|
||||
|
@ -149,6 +163,13 @@ _win_play_eval(Win *w)
|
|||
}
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
_win_play_eval_timer(void *data)
|
||||
{
|
||||
_win_play_eval(data);
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
_win_song_set(Win *w, Song *s)
|
||||
{
|
||||
|
@ -175,11 +196,20 @@ _win_song_set(Win *w, Song *s)
|
|||
edje_object_message_send(w->edje, EDJE_MESSAGE_INT, MSG_RATING, &mi);
|
||||
|
||||
emotion_object_file_set(w->emotion, s->path);
|
||||
// TODO: emotion_object_audio_volume_set(w->emotion, w->play.volume);
|
||||
w->play.playing = EINA_TRUE;
|
||||
emotion_object_play_set(w->emotion, EINA_TRUE);
|
||||
emotion_object_audio_volume_set(w->emotion, w->play.volume);
|
||||
|
||||
end:
|
||||
if ((!w->play.playing) && (w->timer.play_eval))
|
||||
{
|
||||
ecore_timer_del(w->timer.play_eval);
|
||||
w->timer.play_eval = NULL;
|
||||
}
|
||||
else if ((w->play.playing) && (!w->timer.play_eval))
|
||||
w->timer.play_eval = ecore_timer_loop_add
|
||||
(0.1, _win_play_eval_timer, w);
|
||||
|
||||
_win_play_eval(w);
|
||||
_win_toolbar_eval(w);
|
||||
}
|
||||
|
@ -225,6 +255,7 @@ _win_del(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event_
|
|||
if (w->emotion) evas_object_del(w->emotion);
|
||||
if (w->job.scan) ecore_job_del(w->job.scan);
|
||||
if (w->job.populate) ecore_job_del(w->job.populate);
|
||||
if (w->timer.play_eval) ecore_timer_del(w->timer.play_eval);
|
||||
if (w->thread.scan) ecore_thread_cancel(w->thread.scan);
|
||||
if (w->db_path) eina_stringshare_del(w->db_path);
|
||||
}
|
||||
|
@ -369,6 +400,8 @@ win_new(App *app)
|
|||
|
||||
memset(w, 0, sizeof(*w));
|
||||
|
||||
w->play.volume = 0.8;
|
||||
|
||||
w->win = elm_win_add(NULL, PACKAGE_NAME, ELM_WIN_BASIC);
|
||||
if (!w->win) return NULL;
|
||||
evas_object_data_set(w->win, "_enjoy", &w);
|
||||
|
@ -394,6 +427,8 @@ win_new(App *app)
|
|||
(w->emotion, "position_update", _win_play_pos_update, w);
|
||||
evas_object_smart_callback_add
|
||||
(w->emotion, "length_change", _win_play_pos_update, w);
|
||||
evas_object_smart_callback_add
|
||||
(w->emotion, "frame_decode", _win_play_pos_update, w);
|
||||
evas_object_smart_callback_add
|
||||
(w->emotion, "decode_stop", _win_play_end, w);
|
||||
|
||||
|
@ -477,7 +512,6 @@ win_new(App *app)
|
|||
|
||||
evas_object_show(w->layout);
|
||||
|
||||
printf("initial size: %dx%d\n", iw, ih);
|
||||
evas_object_resize(w->win, iw, ih);
|
||||
evas_object_size_hint_min_set(w->win, w->min.w, w->min.h);
|
||||
elm_win_title_set(w->win, PACKAGE_STRING);
|
||||
|
|
Loading…
Reference in New Issue