summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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