1
0
Fork 0
forked from len0rd/rockbox

Initial support for the advanced conditional fully configurable tagcache

browser. Browsing not supported by numeric tags yet, and some features
work currently only when tagcache is loaded in ram.


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@9501 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Miika Pekkarinen 2006-04-04 19:28:13 +00:00
parent 461addf658
commit 4a63c09c86
6 changed files with 385 additions and 202 deletions

View file

@ -61,6 +61,7 @@
#include "database.h" #include "database.h"
#include "dircache.h" #include "dircache.h"
#include "tagcache.h" #include "tagcache.h"
#include "tagtree.h"
#include "lang.h" #include "lang.h"
#include "string.h" #include "string.h"
#include "splash.h" #include "splash.h"
@ -152,6 +153,7 @@ void init_tagcache(void)
#endif #endif
tagcache_init(); tagcache_init();
tagtree_init();
#ifdef HAVE_LCD_BITMAP #ifdef HAVE_LCD_BITMAP
/* Clean the text when we are done. */ /* Clean the text when we are done. */

View file

@ -50,6 +50,7 @@ struct tagcache_search_clause
int tag; int tag;
int type; int type;
bool numeric; bool numeric;
bool input;
long numeric_data; long numeric_data;
char str[32]; char str[32];
}; };

View file

@ -37,9 +37,15 @@
#include "playlist.h" #include "playlist.h"
#include "keyboard.h" #include "keyboard.h"
#include "gui/list.h" #include "gui/list.h"
#include "buffer.h"
#include "atoi.h"
#define FILE_SEARCH_INSTRUCTIONS ROCKBOX_DIR "/tagnavi.config"
static int tagtree_play_folder(struct tree_context* c); static int tagtree_play_folder(struct tree_context* c);
static int tagtree_search(struct tree_context* c, char* string);
static const int numeric_tags[] = { tag_year, tag_length, tag_bitrate, tag_tracknumber };
static const int string_tags[] = { tag_artist, tag_title, tag_album, tag_composer, tag_genre };
static char searchstring[32]; static char searchstring[32];
struct tagentry { struct tagentry {
@ -48,6 +54,241 @@ struct tagentry {
int extraseek; int extraseek;
}; };
#define MAX_TAGS 5
struct search_instruction {
char name[64];
int tagorder[MAX_TAGS];
int tagorder_count;
struct tagcache_search_clause clause[MAX_TAGS][TAGCACHE_MAX_CLAUSES];
int clause_count[MAX_TAGS];
int result_seek[MAX_TAGS];
};
static struct search_instruction *si, *csi;
static int si_count = 0;
static const char *strp;
static int get_token_str(char *buf, int size)
{
/* Find the start. */
while (*strp != '"' && *strp != '\0')
strp++;
if (*strp == '\0' || *(++strp) == '\0')
return -1;
/* Read the data. */
while (*strp != '"' && *strp != '\0' && --size > 0)
*(buf++) = *(strp++);
*buf = '\0';
if (*strp != '"')
return -2;
strp++;
return 0;
}
#define MATCH(tag,str1,str2,settag) \
if (!strcasecmp(str1, str2)) { \
*tag = settag; \
return 1; \
}
static int get_tag(int *tag)
{
char buf[32];
int i;
/* Find the start. */
while (*strp == ' ' && *strp != '\0')
strp++;
if (*strp == '\0')
return 0;
for (i = 0; i < (int)sizeof(buf)-1; i++)
{
if (*strp == '\0' || *strp == ' ')
break ;
buf[i] = *strp;
strp++;
}
buf[i] = '\0';
MATCH(tag, buf, "artist", tag_artist);
MATCH(tag, buf, "song", tag_title);
MATCH(tag, buf, "album", tag_album);
MATCH(tag, buf, "genre", tag_genre);
MATCH(tag, buf, "composer", tag_composer);
MATCH(tag, buf, "year", tag_year);
MATCH(tag, buf, "length", tag_length);
MATCH(tag, buf, "tracknum", tag_tracknumber);
MATCH(tag, buf, "bitrate", tag_bitrate);
logf("NO MATCH: %s\n", buf);
if (buf[0] == '?')
return 0;
return -1;
}
static int get_clause(int *condition)
{
char buf[4];
int i;
/* Find the start. */
while (*strp == ' ' && *strp != '\0')
strp++;
if (*strp == '\0')
return 0;
for (i = 0; i < (int)sizeof(buf)-1; i++)
{
if (*strp == '\0' || *strp == ' ')
break ;
buf[i] = *strp;
strp++;
}
buf[i] = '\0';
MATCH(condition, buf, "=", clause_is);
MATCH(condition, buf, ">", clause_gt);
MATCH(condition, buf, ">=", clause_gteq);
MATCH(condition, buf, "<", clause_lt);
MATCH(condition, buf, "<=", clause_lteq);
MATCH(condition, buf, "~", clause_contains);
MATCH(condition, buf, "^", clause_begins_with);
MATCH(condition, buf, "$", clause_ends_with);
return 0;
}
static bool add_clause(struct search_instruction *inst,
int tag, int type, const char *str)
{
int len = strlen(str);
struct tagcache_search_clause *clause;
if (inst->clause_count[inst->tagorder_count] >= TAGCACHE_MAX_CLAUSES)
{
logf("Too many clauses");
return false;
}
clause = &inst->clause[inst->tagorder_count]
[inst->clause_count[inst->tagorder_count]];
if (len >= (int)sizeof(clause->str) - 1)
{
logf("Too long str in condition");
return false;
}
clause->tag = tag;
clause->type = type;
if (len == 0)
clause->input = true;
else
clause->input = false;
if (tagcache_is_numeric_tag(tag))
{
clause->numeric = true;
clause->numeric_data = atoi(str);
}
else
{
clause->numeric = false;
strcpy(clause->str, str);
}
inst->clause_count[inst->tagorder_count]++;
return true;
}
static int get_condition(struct search_instruction *inst)
{
int tag;
int condition;
char buf[32];
switch (*strp)
{
case '?':
case ' ':
case '&':
strp++;
return 1;
case ':':
strp++;
case '\0':
return 0;
}
if (get_tag(&tag) <= 0)
return -1;
if (get_clause(&condition) <= 0)
return -2;
if (get_token_str(buf, sizeof buf) < 0)
return -3;
logf("got clause: %d/%d [%s]", tag, condition, buf);
add_clause(inst, tag, condition, buf);
return 1;
}
/* example search:
* "Best" artist ? year >= "2000" & title !^ "crap" & genre = "good genre" \
* : album ? year >= "2000" : songs
* ^ begins with
* * contains
* $ ends with
*/
static bool parse_search(struct search_instruction *inst, const char *str)
{
int ret;
memset(inst, 0, sizeof(struct search_instruction));
strp = str;
if (get_token_str(inst->name, sizeof inst->name) < 0)
{
logf("No name found.");
return false;
}
while (inst->tagorder_count < MAX_TAGS)
{
ret = get_tag(&inst->tagorder[inst->tagorder_count]);
if (ret < 0)
{
logf("Parse error #1");
return false;
}
if (ret == 0)
break ;
logf("tag: %d", inst->tagorder[inst->tagorder_count]);
while (get_condition(inst) > 0) ;
inst->tagorder_count++;
}
return true;
}
static struct tagcache_search tcs; static struct tagcache_search tcs;
static int compare(const void *p1, const void *p2) static int compare(const void *p1, const void *p2)
@ -58,6 +299,64 @@ static int compare(const void *p1, const void *p2)
return strncasecmp(e1->name, e2->name, MAX_PATH); return strncasecmp(e1->name, e2->name, MAX_PATH);
} }
void tagtree_init(void)
{
int fd;
char buf[256];
int pos = 0;
si_count = 0;
fd = open(FILE_SEARCH_INSTRUCTIONS, O_RDONLY);
if (fd < 0)
{
logf("Search instruction file not found.");
return ;
}
si = (struct search_instruction *)(((long)audiobuf & ~0x03) + 0x04);
while ( 1 )
{
char *p;
char *next = NULL;
int rc;
rc = read(fd, &buf[pos], sizeof(buf)-pos-1);
if (rc >= 0)
buf[pos+rc] = '\0';
if ( (p = strchr(buf, '\r')) != NULL)
{
*p = '\0';
next = ++p;
}
else
p = buf;
if ( (p = strchr(p, '\n')) != NULL)
{
*p = '\0';
next = ++p;
}
if (!parse_search(si + si_count, buf))
break;
si_count++;
if (next)
{
pos = sizeof(buf) - ((long)next - (long)buf) - 1;
memmove(buf, next, pos);
}
else
break ;
}
close(fd);
audiobuf += sizeof(struct search_instruction) * si_count + 4;
}
int tagtree_load(struct tree_context* c) int tagtree_load(struct tree_context* c)
{ {
int i; int i;
@ -67,8 +366,7 @@ int tagtree_load(struct tree_context* c)
int table = c->currtable; int table = c->currtable;
int extra = c->currextra; int extra = c->currextra;
int extra2 = c->currextra2;
c->dentry_size = sizeof(struct tagentry); c->dentry_size = sizeof(struct tagentry);
if (!table) if (!table)
@ -83,107 +381,34 @@ int tagtree_load(struct tree_context* c)
switch (table) { switch (table) {
case root: { case root: {
static const int tables[] = {allartists, allalbums, allgenres, allsongs, for (i = 0; i < si_count; i++)
search }; {
unsigned char* labels[] = { str(LANG_ID3DB_ARTISTS), dptr->name = (si+i)->name;
str(LANG_ID3DB_ALBUMS), dptr->newtable = navibrowse;
str(LANG_ID3DB_GENRES), dptr->extraseek = i;
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++; dptr++;
} }
c->dirlength = c->filesindir = i; 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++) { return c->dirlength;
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 navibrowse:
case searchalbums: logf("navibrowse...");
case searchsongs: tagcache_search(&tcs, csi->tagorder[extra]);
i = tagtree_search(c, searchstring); for (i = 0; i < extra; i++)
c->dirlength = c->filesindir = i; {
if (c->dirfull) { tagcache_search_add_filter(&tcs, csi->tagorder[i], csi->result_seek[i]);
gui_syncsplash(HZ, true, str(LANG_SHOWDIR_BUFFER_FULL)); sort = true;
c->dirfull = false; }
for (i = 0; i < csi->clause_count[extra]; i++)
{
tagcache_search_add_clause(&tcs, &csi->clause[extra][i]);
sort = true;
} }
else
gui_syncsplash(HZ, true, str(LANG_ID3DB_MATCHES), i);
return i;
case allsongs:
logf("songs..");
tagcache_search(&tcs, tag_title);
break; 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: case chunked_next:
logf("chunked next..."); logf("chunked next...");
break; break;
@ -199,10 +424,10 @@ int tagtree_load(struct tree_context* c)
while (tagcache_get_next(&tcs)) while (tagcache_get_next(&tcs))
{ {
dptr->newtable = tcs.result_seek; dptr->newtable = tcs.result_seek;
if (!tcs.ramsearch || table == songs4album) if (!tcs.ramsearch || csi->tagorder[extra] == tag_title)
{ {
dptr->name = &c->name_buffer[namebufused]; dptr->name = &c->name_buffer[namebufused];
if (table == songs4album) if (csi->tagorder[extra] == tag_title)
{ {
snprintf(dptr->name, c->name_buffer_size - namebufused, "%02d. %s", snprintf(dptr->name, c->name_buffer_size - namebufused, "%02d. %s",
tagcache_get_numeric(&tcs, tag_tracknumber), tagcache_get_numeric(&tcs, tag_tracknumber),
@ -264,54 +489,6 @@ int tagtree_load(struct tree_context* c)
return 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 tagtree_enter(struct tree_context* c)
{ {
int rc = 0; int rc = 0;
@ -343,53 +520,55 @@ int tagtree_enter(struct tree_context* c)
case root: case root:
c->currtable = newextra; c->currtable = newextra;
c->currextra = newextra; c->currextra = newextra;
if (newextra == navibrowse)
{
int i, j;
csi = si+dptr->extraseek;
c->currextra = 0;
/* Read input as necessary. */
for (i = 0; i < csi->tagorder_count; i++)
{
for (j = 0; j < csi->clause_count[i]; j++)
{
if (!csi->clause[i][j].input)
continue;
rc = kbd_input(searchstring, sizeof(searchstring));
if (rc == -1 || !searchstring[0])
{
c->dirlevel--;
break;
}
if (csi->clause[i][j].numeric)
csi->clause[i][j].numeric_data = atoi(searchstring);
else
strncpy(csi->clause[i][j].str, searchstring,
sizeof(csi->clause[i][j].str)-1);
}
}
}
break; break;
case allartists: case navibrowse:
case searchartists: csi->result_seek[c->currextra] = newextra;
c->currtable = albums4artist; if (c->currextra < csi->tagorder_count-1)
c->currextra = newextra; {
break; c->currextra++;
break;
case allgenres: }
c->currtable = artist4genres;
c->currextra = newextra; c->dirlevel--;
if (csi->tagorder[c->currextra] == tag_title)
{
if (tagtree_play_folder(c) >= 0)
rc = 2;
}
break; 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: default:
c->dirlevel--; c->dirlevel--;
break; break;
@ -545,11 +724,11 @@ int tagtree_get_icon(struct tree_context* c)
switch (c->currtable) switch (c->currtable)
{ {
case allsongs: case navibrowse:
case songs4album: if (csi->tagorder[c->currextra] == tag_title)
case songs4artist: icon = Icon_Audio;
case searchsongs: else
icon = Icon_Audio; icon = Icon_Folder;
break; break;
default: default:

View file

@ -22,11 +22,11 @@
#include "tagcache.h" #include "tagcache.h"
#include "tree.h" #include "tree.h"
enum table { invalid, root, allsongs, allalbums, allartists, allgenres, enum table {
albums4artist, songs4album, songs4artist, artist4genres, invalid, root, navibrowse,
search, searchartists, searchalbums, searchsongs, chunked_next };
chunked_next };
void tagtree_init(void);
int tagtree_enter(struct tree_context* c); int tagtree_enter(struct tree_context* c);
void tagtree_exit(struct tree_context* c); void tagtree_exit(struct tree_context* c);
int tagtree_load(struct tree_context* c); int tagtree_load(struct tree_context* c);

View file

@ -767,10 +767,9 @@ static bool dirbrowse(void)
{ {
switch (tc.currtable) switch (tc.currtable)
{ {
case allsongs: case navibrowse:
case songs4album: if (tc.currextra != tag_title)
case songs4artist: break;
case searchsongs:
attr=TREE_ATTR_MPA; attr=TREE_ATTR_MPA;
tagtree_get_filename(&tc, buf, sizeof(buf)); tagtree_get_filename(&tc, buf, sizeof(buf));
break; break;

View file

@ -170,6 +170,8 @@ sub buildzip {
} }
close VIEWERS; close VIEWERS;
`cp $ROOT/apps/tagnavi.config .rockbox/`;
if($notplayer) { if($notplayer) {
`cp $ROOT/apps/plugins/sokoban.levels .rockbox/rocks/`; # sokoban levels `cp $ROOT/apps/plugins/sokoban.levels .rockbox/rocks/`; # sokoban levels
`cp $ROOT/apps/plugins/snake2.levels .rockbox/rocks/`; # snake2 levels `cp $ROOT/apps/plugins/snake2.levels .rockbox/rocks/`; # snake2 levels