summaryrefslogtreecommitdiff
path: root/apps/tagtree.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/tagtree.c')
-rw-r--r--apps/tagtree.c536
1 files changed, 536 insertions, 0 deletions
diff --git a/apps/tagtree.c b/apps/tagtree.c
new file mode 100644
index 0000000000..cb4135c204
--- /dev/null
+++ b/apps/tagtree.c
@@ -0,0 +1,536 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 by Miika Pekkarinen
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
20/**
21 * Basic structure on this file was copied from dbtree.c and modified to
22 * support the tag cache interface.
23 */
24#include <stdio.h>
25#include <string.h>
26#include <stdlib.h>
27#include "system.h"
28#include "kernel.h"
29#include "splash.h"
30#include "icons.h"
31#include "tree.h"
32#include "settings.h"
33#include "tagcache.h"
34#include "tagtree.h"
35#include "lang.h"
36#include "logf.h"
37#include "playlist.h"
38#include "keyboard.h"
39#include "gui/list.h"
40
41#define CHUNKED_NEXT -2
42
43static int tagtree_play_folder(struct tree_context* c);
44static int tagtree_search(struct tree_context* c, char* string);
45
46static char searchstring[32];
47struct tagentry {
48 char *name;
49 int newtable;
50 int extraseek;
51};
52
53static struct tagcache_search tcs;
54
55int tagtree_load(struct tree_context* c)
56{
57 int i;
58 int namebufused = 0;
59 struct tagentry *dptr = (struct tagentry *)c->dircache;
60
61 int table = c->currtable;
62 int extra = c->currextra;
63 int extra2 = c->currextra2;
64
65 c->dentry_size = sizeof(struct tagentry);
66
67 if (!table)
68 {
69 c->dirfull = false;
70 table = root;
71 c->currtable = table;
72 }
73
74 if (c->dirfull)
75 table = chunked_next;
76
77 switch (table) {
78 case root: {
79 static const int tables[] = {allartists, allalbums, allgenres, allsongs,
80 search };
81 unsigned char* labels[] = { str(LANG_ID3DB_ARTISTS),
82 str(LANG_ID3DB_ALBUMS),
83 str(LANG_ID3DB_GENRES),
84 str(LANG_ID3DB_SONGS),
85 str(LANG_ID3DB_SEARCH)};
86
87 for (i = 0; i < 5; i++) {
88 dptr->name = &c->name_buffer[namebufused];
89 dptr->newtable = tables[i];
90 strcpy(dptr->name, (char *)labels[i]);
91 namebufused += strlen(dptr->name) + 1;
92 dptr++;
93 }
94 c->dirlength = c->filesindir = i;
95 return i;
96 }
97
98 case search: {
99 static const int tables[] = {searchartists,
100 searchalbums,
101 searchsongs};
102 unsigned char* labels[] = { str(LANG_ID3DB_SEARCH_ARTISTS),
103 str(LANG_ID3DB_SEARCH_ALBUMS),
104 str(LANG_ID3DB_SEARCH_SONGS)};
105
106 for (i = 0; i < 3; i++) {
107 dptr->name = &c->name_buffer[namebufused];
108 dptr->newtable = tables[i];
109 strcpy(dptr->name, (char *)labels[i]);
110 namebufused += strlen(dptr->name) + 1;
111 dptr++;
112 }
113 c->dirlength = c->filesindir = i;
114 return i;
115 }
116
117 case searchartists:
118 case searchalbums:
119 case searchsongs:
120 i = tagtree_search(c, searchstring);
121 c->dirlength = c->filesindir = i;
122 if (c->dirfull) {
123 gui_syncsplash(HZ, true, (unsigned char *)"%s %s",
124 str(LANG_SHOWDIR_ERROR_BUFFER),
125 str(LANG_SHOWDIR_ERROR_FULL));
126 c->dirfull = false;
127 }
128 else
129 gui_syncsplash(HZ, true, str(LANG_ID3DB_MATCHES), i);
130 return i;
131
132 case allsongs:
133 logf("songs..");
134 tagcache_search(&tcs, tag_title);
135 break;
136
137 case allgenres:
138 logf("genres..");
139 tagcache_search(&tcs, tag_genre);
140 break;
141
142 case allalbums:
143 logf("albums..");
144 tagcache_search(&tcs, tag_album);
145 break;
146
147 case allartists:
148 logf("artists..");
149 tagcache_search(&tcs, tag_artist);
150 break;
151
152 case artist4genres:
153 logf("artist4genres..");
154 tagcache_search(&tcs, tag_artist);
155 tagcache_search_add_filter(&tcs, tag_genre, extra);
156 break;
157
158 case albums4artist:
159 logf("albums4artist..");
160 tagcache_search(&tcs, tag_album);
161 tagcache_search_add_filter(&tcs, tag_artist, extra);
162 break;
163
164 case songs4album:
165 logf("songs4album..");
166 tagcache_search(&tcs, tag_title);
167 tagcache_search_add_filter(&tcs, tag_album, extra);
168 if (extra2 > 0)
169 tagcache_search_add_filter(&tcs, tag_artist, extra2);
170 break;
171
172 case songs4artist:
173 logf("songs4artist..");
174 tagcache_search(&tcs, tag_title);
175 tagcache_search_add_filter(&tcs, tag_artist, extra);
176 break;
177
178 case chunked_next:
179 logf("chunked next...");
180 break;
181
182 default:
183 logf("Unsupported table %d\n", table);
184 return -1;
185 }
186
187 i = 0;
188 namebufused = 0;
189 c->dirfull = false;
190 while (tagcache_get_next(&tcs))
191 {
192 dptr->newtable = tcs.result_seek;
193 if (!tcs.ramsearch)
194 {
195 dptr->name = &c->name_buffer[namebufused];
196 namebufused += tcs.result_len;
197 if (namebufused > c->name_buffer_size)
198 {
199 logf("buffer full, 1 entry missed.");
200 c->dirfull = true;
201 break ;
202 }
203 strcpy(dptr->name, tcs.result);
204 }
205 else
206 dptr->name = tcs.result;
207 dptr++;
208 i++;
209
210 /**
211 * Estimate when we are running out of space so we can stop
212 * and enabled chunked browsing without missing entries.
213 */
214 if (i >= global_settings.max_files_in_dir - 1
215 || namebufused + 200 > c->name_buffer_size)
216 {
217 c->dirfull = true;
218 break ;
219 }
220
221 }
222
223 if (c->dirfull)
224 {
225 dptr->name = "===>";
226 dptr->newtable = chunked_next;
227 dptr++;
228 i++;
229 }
230 else
231 tagcache_search_finish(&tcs);
232
233 c->dirlength = c->filesindir = i;
234
235 return i;
236}
237
238static int tagtree_search(struct tree_context* c, char* string)
239{
240 struct tagentry *dptr = (struct tagentry *)c->dircache;
241 int hits = 0;
242 int namebufused = 0;
243
244 switch (c->currtable) {
245 case searchartists:
246 tagcache_search(&tcs, tag_artist);
247 break;
248
249 case searchalbums:
250 tagcache_search(&tcs, tag_album);
251 break;
252
253 case searchsongs:
254 tagcache_search(&tcs, tag_title);
255 break;
256
257 default:
258 logf("Invalid table %d\n", c->currtable);
259 return 0;
260 }
261
262 while (tagcache_get_next(&tcs))
263 {
264 if (!strcasestr(tcs.result, string))
265 continue ;
266
267 if (!tcs.ramsearch)
268 {
269 dptr->name = &c->name_buffer[namebufused];
270 namebufused += tcs.result_len;
271 strcpy(dptr->name, tcs.result);
272 }
273 else
274 dptr->name = tcs.result;
275
276 dptr->newtable = tcs.result_seek;
277 dptr++;
278 hits++;
279 }
280
281 tagcache_search_finish(&tcs);
282
283 return hits;
284}
285
286int tagtree_enter(struct tree_context* c)
287{
288 int rc = 0;
289 struct tagentry *dptr = (struct tagentry *)c->dircache;
290 int newextra;
291
292 dptr += c->selected_item;
293
294 if (dptr->newtable == chunked_next)
295 {
296 c->selected_item=0;
297 gui_synclist_select_item(&tree_lists, c->selected_item);
298 return 0;
299 }
300
301 c->dirfull = false;
302 newextra = dptr->newtable;
303
304 if (c->dirlevel >= MAX_DIR_LEVELS)
305 return 0;
306
307 c->selected_item_history[c->dirlevel]=c->selected_item;
308 c->table_history[c->dirlevel] = c->currtable;
309 c->extra_history[c->dirlevel] = c->currextra;
310 c->pos_history[c->dirlevel] = c->firstpos;
311 c->dirlevel++;
312
313 switch (c->currtable) {
314 case root:
315 c->currtable = newextra;
316 c->currextra = newextra;
317 break;
318
319 case allartists:
320 case searchartists:
321 c->currtable = albums4artist;
322 c->currextra = newextra;
323 break;
324
325 case allgenres:
326 c->currtable = artist4genres;
327 c->currextra = newextra;
328 break;
329
330 case artist4genres:
331 c->currtable = albums4artist;
332 c->currextra = newextra;
333 break;
334
335 case allalbums:
336 c->currtable = songs4album;
337 c->currextra = newextra;
338 c->currextra2 = -1;
339 break;
340 case albums4artist:
341 case searchalbums:
342 c->currtable = songs4album;
343 c->currextra2 = c->currextra;
344 c->currextra = newextra;
345 break;
346
347 case allsongs:
348 case songs4album:
349 case songs4artist:
350 case searchsongs:
351 c->dirlevel--;
352 if (tagtree_play_folder(c) >= 0)
353 rc = 2;
354 break;
355
356 case search:
357 rc = kbd_input(searchstring, sizeof(searchstring));
358 if (rc == -1 || !searchstring[0])
359 c->dirlevel--;
360 else
361 c->currtable = newextra;
362 break;
363
364 default:
365 c->dirlevel--;
366 break;
367 }
368 c->selected_item=0;
369 gui_synclist_select_item(&tree_lists, c->selected_item);
370
371 return rc;
372}
373
374void tagtree_exit(struct tree_context* c)
375{
376 c->dirfull = false;
377 c->dirlevel--;
378 c->selected_item=c->selected_item_history[c->dirlevel];
379 gui_synclist_select_item(&tree_lists, c->selected_item);
380 c->currtable = c->table_history[c->dirlevel];
381 c->currextra = c->extra_history[c->dirlevel];
382 c->firstpos = c->pos_history[c->dirlevel];
383
384 /* Just to be sure when chunked browsing is used. */
385 tagcache_search_finish(&tcs);
386}
387
388int tagtree_get_filename(struct tree_context* c, char *buf, int buflen)
389{
390 struct tagentry *entry = (struct tagentry *)c->dircache;
391
392 entry += c->selected_item;
393
394 tagcache_search(&tcs, tag_filename);
395 tagcache_search_add_filter(&tcs, tag_title, entry->newtable);
396
397 if (!tagcache_get_next(&tcs))
398 {
399 tagcache_search_finish(&tcs);
400 return -1;
401 }
402
403 strncpy(buf, tcs.result, buflen-1);
404 tagcache_search_finish(&tcs);
405
406 return 0;
407}
408
409#if 0
410bool tagtree_rename_tag(struct tree_context *c, const char *newtext)
411{
412 struct tagentry *dptr = (struct tagentry *)c->dircache;
413 int extra, extra2;
414 int type;
415
416 dptr += c->selected_item;
417 extra = dptr->newtable;
418 extra2 = dptr->extraseek;
419
420 switch (c->currtable) {
421 case allgenres:
422 tagcache_search(&tcs, tag_title);
423 tagcache_search_add_filter(&tcs, tag_genre, extra);
424 type = tag_genre;
425 break;
426
427 case allalbums:
428 tagcache_search(&tcs, tag_title);
429 tagcache_search_add_filter(&tcs, tag_album, extra);
430 type = tag_album;
431 break;
432
433 case allartists:
434 tagcache_search(&tcs, tag_title);
435 tagcache_search_add_filter(&tcs, tag_artist, extra);
436 type = tag_artist;
437 break;
438
439 case artist4genres:
440 tagcache_search(&tcs, tag_title);
441 tagcache_search_add_filter(&tcs, tag_genre, extra);
442 type = tag_artist;
443 break;
444
445 case albums4artist:
446 tagcache_search(&tcs, tag_title);
447 tagcache_search_add_filter(&tcs, tag_album, extra);
448 tagcache_search_add_filter(&tcs, tag_artist, extra2);
449 type = tag_album;
450 break;
451
452 default:
453 logf("wrong table");
454 return false;
455 }
456
457 while (tagcache_get_next(&tcs))
458 {
459 // tagcache_modify(&tcs, type, newtext);
460 }
461
462 tagcache_search_finish(&tcs);
463 return true;
464}
465#endif
466
467static int tagtree_play_folder(struct tree_context* c)
468{
469 struct tagentry *entry = (struct tagentry *)c->dircache;
470 int i;
471
472 if (playlist_create(NULL, NULL) < 0) {
473 logf("Failed creating playlist\n");
474 return -1;
475 }
476
477#ifdef HAVE_ADJUSTABLE_CPU_FREQ
478 cpu_boost(true);
479#endif
480
481 for (i=0; i < c->filesindir; i++) {
482 tagcache_search(&tcs, tag_filename);
483 tagcache_search_add_filter(&tcs, tag_title, entry[i].newtable);
484
485 if (!tagcache_get_next(&tcs))
486 {
487 tagcache_search_finish(&tcs);
488 continue ;
489 }
490 playlist_insert_track(NULL, tcs.result, PLAYLIST_INSERT, false);
491 tagcache_search_finish(&tcs);
492 }
493
494#ifdef HAVE_ADJUSTABLE_CPU_FREQ
495 cpu_boost(false);
496#endif
497
498 if (global_settings.playlist_shuffle)
499 c->selected_item = playlist_shuffle(current_tick, c->selected_item);
500 if (!global_settings.play_selected)
501 c->selected_item = 0;
502 gui_synclist_select_item(&tree_lists, c->selected_item);
503
504 playlist_start(c->selected_item,0);
505
506 return 0;
507}
508
509#ifdef HAVE_LCD_BITMAP
510const char* tagtree_get_icon(struct tree_context* c)
511#else
512int tagtree_get_icon(struct tree_context* c)
513#endif
514{
515 int icon;
516
517 switch (c->currtable)
518 {
519 case allsongs:
520 case songs4album:
521 case songs4artist:
522 case searchsongs:
523 icon = Icon_Audio;
524 break;
525
526 default:
527 icon = Icon_Folder;
528 break;
529 }
530
531#ifdef HAVE_LCD_BITMAP
532 return (char *)bitmap_icons_6x8[icon];
533#else
534 return icon;
535#endif
536}