67de1d3d9b
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@9398 a1c6a512-1295-4272-9138-f99709370657
565 lines
15 KiB
C
565 lines
15 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2005 by Miika Pekkarinen
|
|
*
|
|
* All files in this archive are subject to the GNU General Public License.
|
|
* See the file COPYING in the source tree root for full license agreement.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/**
|
|
* Basic structure on this file was copied from dbtree.c and modified to
|
|
* support the tag cache interface.
|
|
*/
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include "system.h"
|
|
#include "kernel.h"
|
|
#include "splash.h"
|
|
#include "icons.h"
|
|
#include "tree.h"
|
|
#include "settings.h"
|
|
#include "tagcache.h"
|
|
#include "tagtree.h"
|
|
#include "lang.h"
|
|
#include "logf.h"
|
|
#include "playlist.h"
|
|
#include "keyboard.h"
|
|
#include "gui/list.h"
|
|
|
|
static int tagtree_play_folder(struct tree_context* c);
|
|
static int tagtree_search(struct tree_context* c, char* string);
|
|
|
|
static char searchstring[32];
|
|
struct tagentry {
|
|
char *name;
|
|
int newtable;
|
|
int extraseek;
|
|
};
|
|
|
|
static struct tagcache_search tcs;
|
|
|
|
static int compare(const void *p1, const void *p2)
|
|
{
|
|
struct tagentry *e1 = (struct tagentry *)p1;
|
|
struct tagentry *e2 = (struct tagentry *)p2;
|
|
|
|
return strncasecmp(e1->name, e2->name, MAX_PATH);
|
|
}
|
|
|
|
int tagtree_load(struct tree_context* c)
|
|
{
|
|
int i;
|
|
int namebufused = 0;
|
|
struct tagentry *dptr = (struct tagentry *)c->dircache;
|
|
bool sort = false;
|
|
|
|
int table = c->currtable;
|
|
int extra = c->currextra;
|
|
int extra2 = c->currextra2;
|
|
|
|
c->dentry_size = sizeof(struct tagentry);
|
|
|
|
if (!table)
|
|
{
|
|
c->dirfull = false;
|
|
table = root;
|
|
c->currtable = table;
|
|
}
|
|
|
|
if (c->dirfull)
|
|
table = chunked_next;
|
|
|
|
switch (table) {
|
|
case root: {
|
|
static const int tables[] = {allartists, allalbums, allgenres, allsongs,
|
|
search };
|
|
unsigned char* labels[] = { str(LANG_ID3DB_ARTISTS),
|
|
str(LANG_ID3DB_ALBUMS),
|
|
str(LANG_ID3DB_GENRES),
|
|
str(LANG_ID3DB_SONGS),
|
|
str(LANG_ID3DB_SEARCH)};
|
|
|
|
for (i = 0; i < 5; i++) {
|
|
dptr->name = &c->name_buffer[namebufused];
|
|
dptr->newtable = tables[i];
|
|
strcpy(dptr->name, (char *)labels[i]);
|
|
namebufused += strlen(dptr->name) + 1;
|
|
dptr++;
|
|
}
|
|
c->dirlength = c->filesindir = i;
|
|
return i;
|
|
}
|
|
|
|
case search: {
|
|
static const int tables[] = {searchartists,
|
|
searchalbums,
|
|
searchsongs};
|
|
unsigned char* labels[] = { str(LANG_ID3DB_SEARCH_ARTISTS),
|
|
str(LANG_ID3DB_SEARCH_ALBUMS),
|
|
str(LANG_ID3DB_SEARCH_SONGS)};
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
dptr->name = &c->name_buffer[namebufused];
|
|
dptr->newtable = tables[i];
|
|
strcpy(dptr->name, (char *)labels[i]);
|
|
namebufused += strlen(dptr->name) + 1;
|
|
dptr++;
|
|
}
|
|
c->dirlength = c->filesindir = i;
|
|
return i;
|
|
}
|
|
|
|
case searchartists:
|
|
case searchalbums:
|
|
case searchsongs:
|
|
i = tagtree_search(c, searchstring);
|
|
c->dirlength = c->filesindir = i;
|
|
if (c->dirfull) {
|
|
gui_syncsplash(HZ, true, str(LANG_SHOWDIR_BUFFER_FULL));
|
|
c->dirfull = false;
|
|
}
|
|
else
|
|
gui_syncsplash(HZ, true, str(LANG_ID3DB_MATCHES), i);
|
|
return i;
|
|
|
|
case allsongs:
|
|
logf("songs..");
|
|
tagcache_search(&tcs, tag_title);
|
|
break;
|
|
|
|
case allgenres:
|
|
logf("genres..");
|
|
tagcache_search(&tcs, tag_genre);
|
|
break;
|
|
|
|
case allalbums:
|
|
logf("albums..");
|
|
tagcache_search(&tcs, tag_album);
|
|
break;
|
|
|
|
case allartists:
|
|
logf("artists..");
|
|
tagcache_search(&tcs, tag_artist);
|
|
break;
|
|
|
|
case artist4genres:
|
|
logf("artist4genres..");
|
|
tagcache_search(&tcs, tag_artist);
|
|
tagcache_search_add_filter(&tcs, tag_genre, extra);
|
|
sort = true;
|
|
break;
|
|
|
|
case albums4artist:
|
|
logf("albums4artist..");
|
|
tagcache_search(&tcs, tag_album);
|
|
tagcache_search_add_filter(&tcs, tag_artist, extra);
|
|
sort = true;
|
|
break;
|
|
|
|
case songs4album:
|
|
logf("songs4album..");
|
|
tagcache_search(&tcs, tag_title);
|
|
tagcache_search_add_filter(&tcs, tag_album, extra);
|
|
sort = true;
|
|
if (extra2 > 0)
|
|
tagcache_search_add_filter(&tcs, tag_artist, extra2);
|
|
break;
|
|
|
|
case songs4artist:
|
|
logf("songs4artist..");
|
|
tagcache_search(&tcs, tag_title);
|
|
tagcache_search_add_filter(&tcs, tag_artist, extra);
|
|
sort = true;
|
|
break;
|
|
|
|
case chunked_next:
|
|
logf("chunked next...");
|
|
break;
|
|
|
|
default:
|
|
logf("Unsupported table %d\n", table);
|
|
return -1;
|
|
}
|
|
|
|
i = 0;
|
|
namebufused = 0;
|
|
c->dirfull = false;
|
|
while (tagcache_get_next(&tcs))
|
|
{
|
|
dptr->newtable = tcs.result_seek;
|
|
if (!tcs.ramsearch || table == songs4album)
|
|
{
|
|
dptr->name = &c->name_buffer[namebufused];
|
|
if (table == songs4album)
|
|
{
|
|
snprintf(dptr->name, c->name_buffer_size - namebufused, "%02d. %s",
|
|
tagcache_get_numeric(&tcs, tag_tracknumber),
|
|
tcs.result);
|
|
namebufused += strlen(dptr->name) + 1;
|
|
if (namebufused >= c->name_buffer_size)
|
|
{
|
|
logf("buffer full, 1 entry missed.");
|
|
c->dirfull = true;
|
|
break ;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
namebufused += tcs.result_len;
|
|
if (namebufused >= c->name_buffer_size)
|
|
{
|
|
logf("buffer full, 1 entry missed.");
|
|
c->dirfull = true;
|
|
break ;
|
|
}
|
|
strcpy(dptr->name, tcs.result);
|
|
}
|
|
}
|
|
else
|
|
dptr->name = tcs.result;
|
|
|
|
dptr++;
|
|
i++;
|
|
|
|
/**
|
|
* Estimate when we are running out of space so we can stop
|
|
* and enabled chunked browsing without missing entries.
|
|
*/
|
|
if (i >= global_settings.max_files_in_dir - 1
|
|
|| namebufused + 200 > c->name_buffer_size)
|
|
{
|
|
c->dirfull = true;
|
|
break ;
|
|
}
|
|
|
|
}
|
|
|
|
if (sort)
|
|
qsort(c->dircache, i, c->dentry_size, compare);
|
|
|
|
if (c->dirfull)
|
|
{
|
|
dptr->name = "===>";
|
|
dptr->newtable = chunked_next;
|
|
dptr++;
|
|
i++;
|
|
}
|
|
else
|
|
tagcache_search_finish(&tcs);
|
|
|
|
c->dirlength = c->filesindir = i;
|
|
|
|
return i;
|
|
}
|
|
|
|
static int tagtree_search(struct tree_context* c, char* string)
|
|
{
|
|
struct tagentry *dptr = (struct tagentry *)c->dircache;
|
|
int hits = 0;
|
|
int namebufused = 0;
|
|
|
|
switch (c->currtable) {
|
|
case searchartists:
|
|
tagcache_search(&tcs, tag_artist);
|
|
break;
|
|
|
|
case searchalbums:
|
|
tagcache_search(&tcs, tag_album);
|
|
break;
|
|
|
|
case searchsongs:
|
|
tagcache_search(&tcs, tag_title);
|
|
break;
|
|
|
|
default:
|
|
logf("Invalid table %d\n", c->currtable);
|
|
return 0;
|
|
}
|
|
|
|
while (tagcache_get_next(&tcs))
|
|
{
|
|
if (!strcasestr(tcs.result, string))
|
|
continue ;
|
|
|
|
if (!tcs.ramsearch)
|
|
{
|
|
dptr->name = &c->name_buffer[namebufused];
|
|
namebufused += tcs.result_len;
|
|
strcpy(dptr->name, tcs.result);
|
|
}
|
|
else
|
|
dptr->name = tcs.result;
|
|
|
|
dptr->newtable = tcs.result_seek;
|
|
dptr++;
|
|
hits++;
|
|
}
|
|
|
|
tagcache_search_finish(&tcs);
|
|
|
|
return hits;
|
|
}
|
|
|
|
int tagtree_enter(struct tree_context* c)
|
|
{
|
|
int rc = 0;
|
|
struct tagentry *dptr = (struct tagentry *)c->dircache;
|
|
int newextra;
|
|
|
|
dptr += c->selected_item;
|
|
|
|
if (dptr->newtable == chunked_next)
|
|
{
|
|
c->selected_item=0;
|
|
gui_synclist_select_item(&tree_lists, c->selected_item);
|
|
return 0;
|
|
}
|
|
|
|
c->dirfull = false;
|
|
newextra = dptr->newtable;
|
|
|
|
if (c->dirlevel >= MAX_DIR_LEVELS)
|
|
return 0;
|
|
|
|
c->selected_item_history[c->dirlevel]=c->selected_item;
|
|
c->table_history[c->dirlevel] = c->currtable;
|
|
c->extra_history[c->dirlevel] = c->currextra;
|
|
c->pos_history[c->dirlevel] = c->firstpos;
|
|
c->dirlevel++;
|
|
|
|
switch (c->currtable) {
|
|
case root:
|
|
c->currtable = newextra;
|
|
c->currextra = newextra;
|
|
break;
|
|
|
|
case allartists:
|
|
case searchartists:
|
|
c->currtable = albums4artist;
|
|
c->currextra = newextra;
|
|
break;
|
|
|
|
case allgenres:
|
|
c->currtable = artist4genres;
|
|
c->currextra = newextra;
|
|
break;
|
|
|
|
case artist4genres:
|
|
c->currtable = albums4artist;
|
|
c->currextra = newextra;
|
|
break;
|
|
|
|
case allalbums:
|
|
c->currtable = songs4album;
|
|
c->currextra = newextra;
|
|
c->currextra2 = -1;
|
|
break;
|
|
case albums4artist:
|
|
case searchalbums:
|
|
c->currtable = songs4album;
|
|
c->currextra2 = c->currextra;
|
|
c->currextra = newextra;
|
|
break;
|
|
|
|
case allsongs:
|
|
case songs4album:
|
|
case songs4artist:
|
|
case searchsongs:
|
|
c->dirlevel--;
|
|
if (tagtree_play_folder(c) >= 0)
|
|
rc = 2;
|
|
break;
|
|
|
|
case search:
|
|
rc = kbd_input(searchstring, sizeof(searchstring));
|
|
if (rc == -1 || !searchstring[0])
|
|
c->dirlevel--;
|
|
else
|
|
c->currtable = newextra;
|
|
break;
|
|
|
|
default:
|
|
c->dirlevel--;
|
|
break;
|
|
}
|
|
c->selected_item=0;
|
|
gui_synclist_select_item(&tree_lists, c->selected_item);
|
|
|
|
return rc;
|
|
}
|
|
|
|
void tagtree_exit(struct tree_context* c)
|
|
{
|
|
c->dirfull = false;
|
|
c->dirlevel--;
|
|
c->selected_item=c->selected_item_history[c->dirlevel];
|
|
gui_synclist_select_item(&tree_lists, c->selected_item);
|
|
c->currtable = c->table_history[c->dirlevel];
|
|
c->currextra = c->extra_history[c->dirlevel];
|
|
c->firstpos = c->pos_history[c->dirlevel];
|
|
|
|
/* Just to be sure when chunked browsing is used. */
|
|
tagcache_search_finish(&tcs);
|
|
}
|
|
|
|
int tagtree_get_filename(struct tree_context* c, char *buf, int buflen)
|
|
{
|
|
struct tagentry *entry = (struct tagentry *)c->dircache;
|
|
|
|
entry += c->selected_item;
|
|
|
|
tagcache_search(&tcs, tag_filename);
|
|
tagcache_search_add_filter(&tcs, tag_title, entry->newtable);
|
|
|
|
if (!tagcache_get_next(&tcs))
|
|
{
|
|
tagcache_search_finish(&tcs);
|
|
return -1;
|
|
}
|
|
|
|
strncpy(buf, tcs.result, buflen-1);
|
|
tagcache_search_finish(&tcs);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if 0
|
|
bool tagtree_rename_tag(struct tree_context *c, const char *newtext)
|
|
{
|
|
struct tagentry *dptr = (struct tagentry *)c->dircache;
|
|
int extra, extra2;
|
|
int type;
|
|
|
|
dptr += c->selected_item;
|
|
extra = dptr->newtable;
|
|
extra2 = dptr->extraseek;
|
|
|
|
switch (c->currtable) {
|
|
case allgenres:
|
|
tagcache_search(&tcs, tag_title);
|
|
tagcache_search_add_filter(&tcs, tag_genre, extra);
|
|
type = tag_genre;
|
|
break;
|
|
|
|
case allalbums:
|
|
tagcache_search(&tcs, tag_title);
|
|
tagcache_search_add_filter(&tcs, tag_album, extra);
|
|
type = tag_album;
|
|
break;
|
|
|
|
case allartists:
|
|
tagcache_search(&tcs, tag_title);
|
|
tagcache_search_add_filter(&tcs, tag_artist, extra);
|
|
type = tag_artist;
|
|
break;
|
|
|
|
case artist4genres:
|
|
tagcache_search(&tcs, tag_title);
|
|
tagcache_search_add_filter(&tcs, tag_genre, extra);
|
|
type = tag_artist;
|
|
break;
|
|
|
|
case albums4artist:
|
|
tagcache_search(&tcs, tag_title);
|
|
tagcache_search_add_filter(&tcs, tag_album, extra);
|
|
tagcache_search_add_filter(&tcs, tag_artist, extra2);
|
|
type = tag_album;
|
|
break;
|
|
|
|
default:
|
|
logf("wrong table");
|
|
return false;
|
|
}
|
|
|
|
while (tagcache_get_next(&tcs))
|
|
{
|
|
// tagcache_modify(&tcs, type, newtext);
|
|
}
|
|
|
|
tagcache_search_finish(&tcs);
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
static int tagtree_play_folder(struct tree_context* c)
|
|
{
|
|
struct tagentry *entry = (struct tagentry *)c->dircache;
|
|
int i;
|
|
|
|
if (playlist_create(NULL, NULL) < 0) {
|
|
logf("Failed creating playlist\n");
|
|
return -1;
|
|
}
|
|
|
|
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
|
|
cpu_boost(true);
|
|
#endif
|
|
|
|
for (i=0; i < c->filesindir; i++) {
|
|
tagcache_search(&tcs, tag_filename);
|
|
tagcache_search_add_filter(&tcs, tag_title, entry[i].newtable);
|
|
|
|
if (!tagcache_get_next(&tcs))
|
|
{
|
|
tagcache_search_finish(&tcs);
|
|
continue ;
|
|
}
|
|
playlist_insert_track(NULL, tcs.result, PLAYLIST_INSERT, false);
|
|
tagcache_search_finish(&tcs);
|
|
}
|
|
|
|
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
|
|
cpu_boost(false);
|
|
#endif
|
|
|
|
if (global_settings.playlist_shuffle)
|
|
c->selected_item = playlist_shuffle(current_tick, c->selected_item);
|
|
if (!global_settings.play_selected)
|
|
c->selected_item = 0;
|
|
gui_synclist_select_item(&tree_lists, c->selected_item);
|
|
|
|
playlist_start(c->selected_item,0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef HAVE_LCD_BITMAP
|
|
const char* tagtree_get_icon(struct tree_context* c)
|
|
#else
|
|
int tagtree_get_icon(struct tree_context* c)
|
|
#endif
|
|
{
|
|
int icon;
|
|
|
|
switch (c->currtable)
|
|
{
|
|
case allsongs:
|
|
case songs4album:
|
|
case songs4artist:
|
|
case searchsongs:
|
|
icon = Icon_Audio;
|
|
break;
|
|
|
|
default:
|
|
icon = Icon_Folder;
|
|
break;
|
|
}
|
|
|
|
#ifdef HAVE_LCD_BITMAP
|
|
return (char *)bitmap_icons_6x8[icon];
|
|
#else
|
|
return icon;
|
|
#endif
|
|
}
|