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:
Gustavo Sverzut Barbieri 2010-09-25 23:34:58 +00:00
parent eca67db8b8
commit a881d3d605
8 changed files with 1346 additions and 103 deletions

View File

@ -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";
}
}
}
}

View File

@ -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

169
src/bin/cover.c Normal file
View File

@ -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;
}

View File

@ -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)
{

View File

@ -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)
{

View File

@ -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);
}

View File

@ -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)

View File

@ -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);