summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiika Pekkarinen <miipekk@ihme.org>2006-04-04 19:28:13 +0000
committerMiika Pekkarinen <miipekk@ihme.org>2006-04-04 19:28:13 +0000
commit4a63c09c8633cd7305af01d1f270125f7bd12a1e (patch)
treef9e763dc95bb8add9f9624d42792e1f6a6e176ba
parent461addf6582e6951374e23233ba3964feb23630b (diff)
downloadrockbox-4a63c09c8633cd7305af01d1f270125f7bd12a1e.tar.gz
rockbox-4a63c09c8633cd7305af01d1f270125f7bd12a1e.zip
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
-rw-r--r--apps/main.c2
-rw-r--r--apps/tagcache.h1
-rw-r--r--apps/tagtree.c565
-rw-r--r--apps/tagtree.h8
-rw-r--r--apps/tree.c7
-rwxr-xr-xtools/buildzip.pl2
6 files changed, 384 insertions, 201 deletions
diff --git a/apps/main.c b/apps/main.c
index e7a01c899a..596d76223d 100644
--- a/apps/main.c
+++ b/apps/main.c
@@ -61,6 +61,7 @@
61#include "database.h" 61#include "database.h"
62#include "dircache.h" 62#include "dircache.h"
63#include "tagcache.h" 63#include "tagcache.h"
64#include "tagtree.h"
64#include "lang.h" 65#include "lang.h"
65#include "string.h" 66#include "string.h"
66#include "splash.h" 67#include "splash.h"
@@ -152,6 +153,7 @@ void init_tagcache(void)
152#endif 153#endif
153 154
154 tagcache_init(); 155 tagcache_init();
156 tagtree_init();
155 157
156#ifdef HAVE_LCD_BITMAP 158#ifdef HAVE_LCD_BITMAP
157 /* Clean the text when we are done. */ 159 /* Clean the text when we are done. */
diff --git a/apps/tagcache.h b/apps/tagcache.h
index 052d71b42b..82d475811f 100644
--- a/apps/tagcache.h
+++ b/apps/tagcache.h
@@ -50,6 +50,7 @@ struct tagcache_search_clause
50 int tag; 50 int tag;
51 int type; 51 int type;
52 bool numeric; 52 bool numeric;
53 bool input;
53 long numeric_data; 54 long numeric_data;
54 char str[32]; 55 char str[32];
55}; 56};
diff --git a/apps/tagtree.c b/apps/tagtree.c
index 81998228ee..7cae86a953 100644
--- a/apps/tagtree.c
+++ b/apps/tagtree.c
@@ -37,9 +37,15 @@
37#include "playlist.h" 37#include "playlist.h"
38#include "keyboard.h" 38#include "keyboard.h"
39#include "gui/list.h" 39#include "gui/list.h"
40#include "buffer.h"
41#include "atoi.h"
42
43#define FILE_SEARCH_INSTRUCTIONS ROCKBOX_DIR "/tagnavi.config"
40 44
41static int tagtree_play_folder(struct tree_context* c); 45static int tagtree_play_folder(struct tree_context* c);
42static int tagtree_search(struct tree_context* c, char* string); 46
47static const int numeric_tags[] = { tag_year, tag_length, tag_bitrate, tag_tracknumber };
48static const int string_tags[] = { tag_artist, tag_title, tag_album, tag_composer, tag_genre };
43 49
44static char searchstring[32]; 50static char searchstring[32];
45struct tagentry { 51struct tagentry {
@@ -48,6 +54,241 @@ struct tagentry {
48 int extraseek; 54 int extraseek;
49}; 55};
50 56
57#define MAX_TAGS 5
58
59struct search_instruction {
60 char name[64];
61 int tagorder[MAX_TAGS];
62 int tagorder_count;
63 struct tagcache_search_clause clause[MAX_TAGS][TAGCACHE_MAX_CLAUSES];
64 int clause_count[MAX_TAGS];
65 int result_seek[MAX_TAGS];
66};
67
68static struct search_instruction *si, *csi;
69static int si_count = 0;
70static const char *strp;
71
72static int get_token_str(char *buf, int size)
73{
74 /* Find the start. */
75 while (*strp != '"' && *strp != '\0')
76 strp++;
77
78 if (*strp == '\0' || *(++strp) == '\0')
79 return -1;
80
81 /* Read the data. */
82 while (*strp != '"' && *strp != '\0' && --size > 0)
83 *(buf++) = *(strp++);
84
85 *buf = '\0';
86 if (*strp != '"')
87 return -2;
88
89 strp++;
90
91 return 0;
92}
93
94#define MATCH(tag,str1,str2,settag) \
95 if (!strcasecmp(str1, str2)) { \
96 *tag = settag; \
97 return 1; \
98 }
99
100static int get_tag(int *tag)
101{
102 char buf[32];
103 int i;
104
105 /* Find the start. */
106 while (*strp == ' ' && *strp != '\0')
107 strp++;
108
109 if (*strp == '\0')
110 return 0;
111
112 for (i = 0; i < (int)sizeof(buf)-1; i++)
113 {
114 if (*strp == '\0' || *strp == ' ')
115 break ;
116 buf[i] = *strp;
117 strp++;
118 }
119 buf[i] = '\0';
120
121 MATCH(tag, buf, "artist", tag_artist);
122 MATCH(tag, buf, "song", tag_title);
123 MATCH(tag, buf, "album", tag_album);
124 MATCH(tag, buf, "genre", tag_genre);
125 MATCH(tag, buf, "composer", tag_composer);
126 MATCH(tag, buf, "year", tag_year);
127 MATCH(tag, buf, "length", tag_length);
128 MATCH(tag, buf, "tracknum", tag_tracknumber);
129 MATCH(tag, buf, "bitrate", tag_bitrate);
130
131 logf("NO MATCH: %s\n", buf);
132 if (buf[0] == '?')
133 return 0;
134
135 return -1;
136}
137
138static int get_clause(int *condition)
139{
140 char buf[4];
141 int i;
142
143 /* Find the start. */
144 while (*strp == ' ' && *strp != '\0')
145 strp++;
146
147 if (*strp == '\0')
148 return 0;
149
150 for (i = 0; i < (int)sizeof(buf)-1; i++)
151 {
152 if (*strp == '\0' || *strp == ' ')
153 break ;
154 buf[i] = *strp;
155 strp++;
156 }
157 buf[i] = '\0';
158
159 MATCH(condition, buf, "=", clause_is);
160 MATCH(condition, buf, ">", clause_gt);
161 MATCH(condition, buf, ">=", clause_gteq);
162 MATCH(condition, buf, "<", clause_lt);
163 MATCH(condition, buf, "<=", clause_lteq);
164 MATCH(condition, buf, "~", clause_contains);
165 MATCH(condition, buf, "^", clause_begins_with);
166 MATCH(condition, buf, "$", clause_ends_with);
167
168 return 0;
169}
170
171static bool add_clause(struct search_instruction *inst,
172 int tag, int type, const char *str)
173{
174 int len = strlen(str);
175 struct tagcache_search_clause *clause;
176
177 if (inst->clause_count[inst->tagorder_count] >= TAGCACHE_MAX_CLAUSES)
178 {
179 logf("Too many clauses");
180 return false;
181 }
182
183 clause = &inst->clause[inst->tagorder_count]
184 [inst->clause_count[inst->tagorder_count]];
185 if (len >= (int)sizeof(clause->str) - 1)
186 {
187 logf("Too long str in condition");
188 return false;
189 }
190
191 clause->tag = tag;
192 clause->type = type;
193 if (len == 0)
194 clause->input = true;
195 else
196 clause->input = false;
197
198 if (tagcache_is_numeric_tag(tag))
199 {
200 clause->numeric = true;
201 clause->numeric_data = atoi(str);
202 }
203 else
204 {
205 clause->numeric = false;
206 strcpy(clause->str, str);
207 }
208
209 inst->clause_count[inst->tagorder_count]++;
210
211 return true;
212}
213
214static int get_condition(struct search_instruction *inst)
215{
216 int tag;
217 int condition;
218 char buf[32];
219
220 switch (*strp)
221 {
222 case '?':
223 case ' ':
224 case '&':
225 strp++;
226 return 1;
227 case ':':
228 strp++;
229 case '\0':
230 return 0;
231 }
232
233 if (get_tag(&tag) <= 0)
234 return -1;
235
236 if (get_clause(&condition) <= 0)
237 return -2;
238
239 if (get_token_str(buf, sizeof buf) < 0)
240 return -3;
241
242 logf("got clause: %d/%d [%s]", tag, condition, buf);
243 add_clause(inst, tag, condition, buf);
244
245 return 1;
246}
247
248/* example search:
249 * "Best" artist ? year >= "2000" & title !^ "crap" & genre = "good genre" \
250 * : album ? year >= "2000" : songs
251 * ^ begins with
252 * * contains
253 * $ ends with
254 */
255
256static bool parse_search(struct search_instruction *inst, const char *str)
257{
258 int ret;
259
260 memset(inst, 0, sizeof(struct search_instruction));
261 strp = str;
262
263 if (get_token_str(inst->name, sizeof inst->name) < 0)
264 {
265 logf("No name found.");
266 return false;
267 }
268
269 while (inst->tagorder_count < MAX_TAGS)
270 {
271 ret = get_tag(&inst->tagorder[inst->tagorder_count]);
272 if (ret < 0)
273 {
274 logf("Parse error #1");
275 return false;
276 }
277
278 if (ret == 0)
279 break ;
280
281 logf("tag: %d", inst->tagorder[inst->tagorder_count]);
282
283 while (get_condition(inst) > 0) ;
284
285 inst->tagorder_count++;
286 }
287
288 return true;
289}
290
291
51static struct tagcache_search tcs; 292static struct tagcache_search tcs;
52 293
53static int compare(const void *p1, const void *p2) 294static int compare(const void *p1, const void *p2)
@@ -58,6 +299,64 @@ static int compare(const void *p1, const void *p2)
58 return strncasecmp(e1->name, e2->name, MAX_PATH); 299 return strncasecmp(e1->name, e2->name, MAX_PATH);
59} 300}
60 301
302void tagtree_init(void)
303{
304 int fd;
305 char buf[256];
306 int pos = 0;
307
308 si_count = 0;
309
310 fd = open(FILE_SEARCH_INSTRUCTIONS, O_RDONLY);
311 if (fd < 0)
312 {
313 logf("Search instruction file not found.");
314 return ;
315 }
316
317 si = (struct search_instruction *)(((long)audiobuf & ~0x03) + 0x04);
318
319 while ( 1 )
320 {
321 char *p;
322 char *next = NULL;
323 int rc;
324
325 rc = read(fd, &buf[pos], sizeof(buf)-pos-1);
326 if (rc >= 0)
327 buf[pos+rc] = '\0';
328
329 if ( (p = strchr(buf, '\r')) != NULL)
330 {
331 *p = '\0';
332 next = ++p;
333 }
334 else
335 p = buf;
336
337 if ( (p = strchr(p, '\n')) != NULL)
338 {
339 *p = '\0';
340 next = ++p;
341 }
342
343 if (!parse_search(si + si_count, buf))
344 break;
345 si_count++;
346
347 if (next)
348 {
349 pos = sizeof(buf) - ((long)next - (long)buf) - 1;
350 memmove(buf, next, pos);
351 }
352 else
353 break ;
354 }
355 close(fd);
356
357 audiobuf += sizeof(struct search_instruction) * si_count + 4;
358}
359
61int tagtree_load(struct tree_context* c) 360int tagtree_load(struct tree_context* c)
62{ 361{
63 int i; 362 int i;
@@ -67,8 +366,7 @@ int tagtree_load(struct tree_context* c)
67 366
68 int table = c->currtable; 367 int table = c->currtable;
69 int extra = c->currextra; 368 int extra = c->currextra;
70 int extra2 = c->currextra2; 369
71
72 c->dentry_size = sizeof(struct tagentry); 370 c->dentry_size = sizeof(struct tagentry);
73 371
74 if (!table) 372 if (!table)
@@ -83,107 +381,34 @@ int tagtree_load(struct tree_context* c)
83 381
84 switch (table) { 382 switch (table) {
85 case root: { 383 case root: {
86 static const int tables[] = {allartists, allalbums, allgenres, allsongs, 384 for (i = 0; i < si_count; i++)
87 search }; 385 {
88 unsigned char* labels[] = { str(LANG_ID3DB_ARTISTS), 386 dptr->name = (si+i)->name;
89 str(LANG_ID3DB_ALBUMS), 387 dptr->newtable = navibrowse;
90 str(LANG_ID3DB_GENRES), 388 dptr->extraseek = i;
91 str(LANG_ID3DB_SONGS),
92 str(LANG_ID3DB_SEARCH)};
93
94 for (i = 0; i < 5; i++) {
95 dptr->name = &c->name_buffer[namebufused];
96 dptr->newtable = tables[i];
97 strcpy(dptr->name, (char *)labels[i]);
98 namebufused += strlen(dptr->name) + 1;
99 dptr++; 389 dptr++;
100 } 390 }
101 c->dirlength = c->filesindir = i; 391 c->dirlength = c->filesindir = i;
102 return i;
103 }
104
105 case search: {
106 static const int tables[] = {searchartists,
107 searchalbums,
108 searchsongs};
109 unsigned char* labels[] = { str(LANG_ID3DB_SEARCH_ARTISTS),
110 str(LANG_ID3DB_SEARCH_ALBUMS),
111 str(LANG_ID3DB_SEARCH_SONGS)};
112 392
113 for (i = 0; i < 3; i++) { 393 return c->dirlength;
114 dptr->name = &c->name_buffer[namebufused];
115 dptr->newtable = tables[i];
116 strcpy(dptr->name, (char *)labels[i]);
117 namebufused += strlen(dptr->name) + 1;
118 dptr++;
119 }
120 c->dirlength = c->filesindir = i;
121 return i;
122 } 394 }
123 395
124 case searchartists: 396 case navibrowse:
125 case searchalbums: 397 logf("navibrowse...");
126 case searchsongs: 398 tagcache_search(&tcs, csi->tagorder[extra]);
127 i = tagtree_search(c, searchstring); 399 for (i = 0; i < extra; i++)
128 c->dirlength = c->filesindir = i; 400 {
129 if (c->dirfull) { 401 tagcache_search_add_filter(&tcs, csi->tagorder[i], csi->result_seek[i]);
130 gui_syncsplash(HZ, true, str(LANG_SHOWDIR_BUFFER_FULL)); 402 sort = true;
131 c->dirfull = false; 403 }
404
405 for (i = 0; i < csi->clause_count[extra]; i++)
406 {
407 tagcache_search_add_clause(&tcs, &csi->clause[extra][i]);
408 sort = true;
132 } 409 }
133 else
134 gui_syncsplash(HZ, true, str(LANG_ID3DB_MATCHES), i);
135 return i;
136
137 case allsongs:
138 logf("songs..");
139 tagcache_search(&tcs, tag_title);
140 break;
141
142 case allgenres:
143 logf("genres..");
144 tagcache_search(&tcs, tag_genre);
145 break;
146
147 case allalbums:
148 logf("albums..");
149 tagcache_search(&tcs, tag_album);
150 break;
151
152 case allartists:
153 logf("artists..");
154 tagcache_search(&tcs, tag_artist);
155 break;
156
157 case artist4genres:
158 logf("artist4genres..");
159 tagcache_search(&tcs, tag_artist);
160 tagcache_search_add_filter(&tcs, tag_genre, extra);
161 sort = true;
162 break;
163
164 case albums4artist:
165 logf("albums4artist..");
166 tagcache_search(&tcs, tag_album);
167 tagcache_search_add_filter(&tcs, tag_artist, extra);
168 sort = true;
169 break;
170
171 case songs4album:
172 logf("songs4album..");
173 tagcache_search(&tcs, tag_title);
174 tagcache_search_add_filter(&tcs, tag_album, extra);
175 sort = true;
176 if (extra2 > 0)
177 tagcache_search_add_filter(&tcs, tag_artist, extra2);
178 break;
179
180 case songs4artist:
181 logf("songs4artist..");
182 tagcache_search(&tcs, tag_title);
183 tagcache_search_add_filter(&tcs, tag_artist, extra);
184 sort = true;
185 break; 410 break;
186 411
187 case chunked_next: 412 case chunked_next:
188 logf("chunked next..."); 413 logf("chunked next...");
189 break; 414 break;
@@ -199,10 +424,10 @@ int tagtree_load(struct tree_context* c)
199 while (tagcache_get_next(&tcs)) 424 while (tagcache_get_next(&tcs))
200 { 425 {
201 dptr->newtable = tcs.result_seek; 426 dptr->newtable = tcs.result_seek;
202 if (!tcs.ramsearch || table == songs4album) 427 if (!tcs.ramsearch || csi->tagorder[extra] == tag_title)
203 { 428 {
204 dptr->name = &c->name_buffer[namebufused]; 429 dptr->name = &c->name_buffer[namebufused];
205 if (table == songs4album) 430 if (csi->tagorder[extra] == tag_title)
206 { 431 {
207 snprintf(dptr->name, c->name_buffer_size - namebufused, "%02d. %s", 432 snprintf(dptr->name, c->name_buffer_size - namebufused, "%02d. %s",
208 tagcache_get_numeric(&tcs, tag_tracknumber), 433 tagcache_get_numeric(&tcs, tag_tracknumber),
@@ -264,54 +489,6 @@ int tagtree_load(struct tree_context* c)
264 return i; 489 return i;
265} 490}
266 491
267static int tagtree_search(struct tree_context* c, char* string)
268{
269 struct tagentry *dptr = (struct tagentry *)c->dircache;
270 int hits = 0;
271 int namebufused = 0;
272
273 switch (c->currtable) {
274 case searchartists:
275 tagcache_search(&tcs, tag_artist);
276 break;
277
278 case searchalbums:
279 tagcache_search(&tcs, tag_album);
280 break;
281
282 case searchsongs:
283 tagcache_search(&tcs, tag_title);
284 break;
285
286 default:
287 logf("Invalid table %d\n", c->currtable);
288 return 0;
289 }
290
291 while (tagcache_get_next(&tcs))
292 {
293 if (!strcasestr(tcs.result, string))
294 continue ;
295
296 if (!tcs.ramsearch)
297 {
298 dptr->name = &c->name_buffer[namebufused];
299 namebufused += tcs.result_len;
300 strcpy(dptr->name, tcs.result);
301 }
302 else
303 dptr->name = tcs.result;
304
305 dptr->newtable = tcs.result_seek;
306 dptr++;
307 hits++;
308 }
309
310 tagcache_search_finish(&tcs);
311
312 return hits;
313}
314
315int tagtree_enter(struct tree_context* c) 492int tagtree_enter(struct tree_context* c)
316{ 493{
317 int rc = 0; 494 int rc = 0;
@@ -343,53 +520,55 @@ int tagtree_enter(struct tree_context* c)
343 case root: 520 case root:
344 c->currtable = newextra; 521 c->currtable = newextra;
345 c->currextra = newextra; 522 c->currextra = newextra;
523 if (newextra == navibrowse)
524 {
525 int i, j;
526
527 csi = si+dptr->extraseek;
528 c->currextra = 0;
529
530 /* Read input as necessary. */
531 for (i = 0; i < csi->tagorder_count; i++)
532 {
533 for (j = 0; j < csi->clause_count[i]; j++)
534 {
535 if (!csi->clause[i][j].input)
536 continue;
537
538 rc = kbd_input(searchstring, sizeof(searchstring));
539 if (rc == -1 || !searchstring[0])
540 {
541 c->dirlevel--;
542 break;
543 }
544
545 if (csi->clause[i][j].numeric)
546 csi->clause[i][j].numeric_data = atoi(searchstring);
547 else
548 strncpy(csi->clause[i][j].str, searchstring,
549 sizeof(csi->clause[i][j].str)-1);
550 }
551 }
552
553 }
346 break; 554 break;
347 555
348 case allartists: 556 case navibrowse:
349 case searchartists: 557 csi->result_seek[c->currextra] = newextra;
350 c->currtable = albums4artist; 558 if (c->currextra < csi->tagorder_count-1)
351 c->currextra = newextra; 559 {
352 break; 560 c->currextra++;
353 561 break;
354 case allgenres: 562 }
355 c->currtable = artist4genres;
356 c->currextra = newextra;
357 break;
358 563
359 case artist4genres:
360 c->currtable = albums4artist;
361 c->currextra = newextra;
362 break;
363
364 case allalbums:
365 c->currtable = songs4album;
366 c->currextra = newextra;
367 c->currextra2 = -1;
368 break;
369 case albums4artist:
370 case searchalbums:
371 c->currtable = songs4album;
372 c->currextra2 = c->currextra;
373 c->currextra = newextra;
374 break;
375
376 case allsongs:
377 case songs4album:
378 case songs4artist:
379 case searchsongs:
380 c->dirlevel--; 564 c->dirlevel--;
381 if (tagtree_play_folder(c) >= 0) 565 if (csi->tagorder[c->currextra] == tag_title)
382 rc = 2; 566 {
383 break; 567 if (tagtree_play_folder(c) >= 0)
384 568 rc = 2;
385 case search: 569 }
386 rc = kbd_input(searchstring, sizeof(searchstring));
387 if (rc == -1 || !searchstring[0])
388 c->dirlevel--;
389 else
390 c->currtable = newextra;
391 break; 570 break;
392 571
393 default: 572 default:
394 c->dirlevel--; 573 c->dirlevel--;
395 break; 574 break;
@@ -545,11 +724,11 @@ int tagtree_get_icon(struct tree_context* c)
545 724
546 switch (c->currtable) 725 switch (c->currtable)
547 { 726 {
548 case allsongs: 727 case navibrowse:
549 case songs4album: 728 if (csi->tagorder[c->currextra] == tag_title)
550 case songs4artist: 729 icon = Icon_Audio;
551 case searchsongs: 730 else
552 icon = Icon_Audio; 731 icon = Icon_Folder;
553 break; 732 break;
554 733
555 default: 734 default:
diff --git a/apps/tagtree.h b/apps/tagtree.h
index 7eafb7061d..2a275c89f8 100644
--- a/apps/tagtree.h
+++ b/apps/tagtree.h
@@ -22,11 +22,11 @@
22#include "tagcache.h" 22#include "tagcache.h"
23#include "tree.h" 23#include "tree.h"
24 24
25enum table { invalid, root, allsongs, allalbums, allartists, allgenres, 25enum table {
26 albums4artist, songs4album, songs4artist, artist4genres, 26 invalid, root, navibrowse,
27 search, searchartists, searchalbums, searchsongs, 27 chunked_next };
28 chunked_next };
29 28
29void tagtree_init(void);
30int tagtree_enter(struct tree_context* c); 30int tagtree_enter(struct tree_context* c);
31void tagtree_exit(struct tree_context* c); 31void tagtree_exit(struct tree_context* c);
32int tagtree_load(struct tree_context* c); 32int tagtree_load(struct tree_context* c);
diff --git a/apps/tree.c b/apps/tree.c
index 3a0a34b621..f523b1c5f2 100644
--- a/apps/tree.c
+++ b/apps/tree.c
@@ -767,10 +767,9 @@ static bool dirbrowse(void)
767 { 767 {
768 switch (tc.currtable) 768 switch (tc.currtable)
769 { 769 {
770 case allsongs: 770 case navibrowse:
771 case songs4album: 771 if (tc.currextra != tag_title)
772 case songs4artist: 772 break;
773 case searchsongs:
774 attr=TREE_ATTR_MPA; 773 attr=TREE_ATTR_MPA;
775 tagtree_get_filename(&tc, buf, sizeof(buf)); 774 tagtree_get_filename(&tc, buf, sizeof(buf));
776 break; 775 break;
diff --git a/tools/buildzip.pl b/tools/buildzip.pl
index 81010def07..353318770e 100755
--- a/tools/buildzip.pl
+++ b/tools/buildzip.pl
@@ -170,6 +170,8 @@ sub buildzip {
170 } 170 }
171 close VIEWERS; 171 close VIEWERS;
172 172
173 `cp $ROOT/apps/tagnavi.config .rockbox/`;
174
173 if($notplayer) { 175 if($notplayer) {
174 `cp $ROOT/apps/plugins/sokoban.levels .rockbox/rocks/`; # sokoban levels 176 `cp $ROOT/apps/plugins/sokoban.levels .rockbox/rocks/`; # sokoban levels
175 `cp $ROOT/apps/plugins/snake2.levels .rockbox/rocks/`; # snake2 levels 177 `cp $ROOT/apps/plugins/snake2.levels .rockbox/rocks/`; # snake2 levels