Updated Dutch (Nederlands) translation
[kugel-rb.git] / apps / tagtree.c
blob7ef8e89d4ff46349373ad8a6e12faa98ab7c8c4e
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2005 by Miika Pekkarinen
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.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
20 /**
21 * Basic structure on this file was copied from dbtree.c and modified to
22 * support the tag cache interface.
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 "action.h"
33 #include "settings.h"
34 #include "tagcache.h"
35 #include "tagtree.h"
36 #include "lang.h"
37 #include "logf.h"
38 #include "playlist.h"
39 #include "keyboard.h"
40 #include "gui/list.h"
41 #include "buffer.h"
42 #include "atoi.h"
43 #include "playback.h"
44 #include "yesno.h"
46 #define FILE_SEARCH_INSTRUCTIONS ROCKBOX_DIR "/tagnavi.config"
48 static int tagtree_play_folder(struct tree_context* c);
50 static char searchstring[32];
52 enum variables {
53 var_sorttype = 100,
54 var_limit
57 /* Capacity 10 000 entries (for example 10k different artists) */
58 #define UNIQBUF_SIZE (64*1024)
59 static long *uniqbuf;
61 #define MAX_TAGS 5
63 static struct tagcache_search tcs, tcs2;
64 static bool sort_inverse;
67 * "%3d. %s" autoscore title %sort = "inverse" %limit = "100"
69 * valid = true
70 * formatstr = "%-3d. %s"
71 * tags[0] = tag_autoscore
72 * tags[1] = tag_title
73 * tag_count = 2
75 * limit = 100
76 * sort_inverse = true
78 struct display_format {
79 bool valid;
80 char formatstr[64];
81 int tags[MAX_TAGS];
82 int tag_count;
84 int limit;
85 bool sort_inverse;
88 struct search_instruction {
89 char name[64];
90 int tagorder[MAX_TAGS];
91 int tagorder_count;
92 struct tagcache_search_clause clause[MAX_TAGS][TAGCACHE_MAX_CLAUSES];
93 struct display_format format[MAX_TAGS];
94 int clause_count[MAX_TAGS];
95 int result_seek[MAX_TAGS];
98 static struct search_instruction *si, *csi;
99 static int si_count = 0;
100 static const char *strp;
102 static int current_offset;
103 static int current_entry_count;
105 static struct tree_context *tc;
107 static int get_token_str(char *buf, int size)
109 /* Find the start. */
110 while (*strp != '"' && *strp != '\0')
111 strp++;
113 if (*strp == '\0' || *(++strp) == '\0')
114 return -1;
116 /* Read the data. */
117 while (*strp != '"' && *strp != '\0' && --size > 0)
118 *(buf++) = *(strp++);
120 *buf = '\0';
121 if (*strp != '"')
122 return -2;
124 strp++;
126 return 0;
129 #define MATCH(tag,str1,str2,settag) \
130 if (!strcasecmp(str1, str2)) { \
131 *tag = settag; \
132 return 1; \
135 static int get_tag(int *tag)
137 char buf[32];
138 int i;
140 /* Find the start. */
141 while (*strp == ' ' && *strp != '\0')
142 strp++;
144 if (*strp == '\0' || *strp == '?' || *strp == ':')
145 return 0;
147 for (i = 0; i < (int)sizeof(buf)-1; i++)
149 if (*strp == '\0' || *strp == ' ')
150 break ;
151 buf[i] = *strp;
152 strp++;
154 buf[i] = '\0';
156 MATCH(tag, buf, "album", tag_album);
157 MATCH(tag, buf, "artist", tag_artist);
158 MATCH(tag, buf, "bitrate", tag_bitrate);
159 MATCH(tag, buf, "composer", tag_composer);
160 MATCH(tag, buf, "genre", tag_genre);
161 MATCH(tag, buf, "length", tag_length);
162 MATCH(tag, buf, "title", tag_title);
163 MATCH(tag, buf, "filename", tag_filename);
164 MATCH(tag, buf, "tracknum", tag_tracknumber);
165 MATCH(tag, buf, "year", tag_year);
166 MATCH(tag, buf, "playcount", tag_playcount);
167 MATCH(tag, buf, "autoscore", tag_virt_autoscore);
168 MATCH(tag, buf, "%sort", var_sorttype);
169 MATCH(tag, buf, "%limit", var_limit);
171 logf("NO MATCH: %s\n", buf);
172 if (buf[0] == '?')
173 return 0;
175 return -1;
178 static int get_clause(int *condition)
180 char buf[4];
181 int i;
183 /* Find the start. */
184 while (*strp == ' ' && *strp != '\0')
185 strp++;
187 if (*strp == '\0')
188 return 0;
190 for (i = 0; i < (int)sizeof(buf)-1; i++)
192 if (*strp == '\0' || *strp == ' ')
193 break ;
194 buf[i] = *strp;
195 strp++;
197 buf[i] = '\0';
199 MATCH(condition, buf, "=", clause_is);
200 MATCH(condition, buf, "==", clause_is);
201 MATCH(condition, buf, "!=", clause_is_not);
202 MATCH(condition, buf, ">", clause_gt);
203 MATCH(condition, buf, ">=", clause_gteq);
204 MATCH(condition, buf, "<", clause_lt);
205 MATCH(condition, buf, "<=", clause_lteq);
206 MATCH(condition, buf, "~", clause_contains);
207 MATCH(condition, buf, "!~", clause_not_contains);
208 MATCH(condition, buf, "^", clause_begins_with);
209 MATCH(condition, buf, "!^", clause_not_begins_with);
210 MATCH(condition, buf, "$", clause_ends_with);
211 MATCH(condition, buf, "!$", clause_not_ends_with);
213 return 0;
216 static bool add_clause(struct search_instruction *inst,
217 int tag, int type, const char *str)
219 int len = strlen(str);
220 struct tagcache_search_clause *clause;
222 if (inst->clause_count[inst->tagorder_count] >= TAGCACHE_MAX_CLAUSES)
224 logf("Too many clauses");
225 return false;
228 clause = &inst->clause[inst->tagorder_count]
229 [inst->clause_count[inst->tagorder_count]];
230 if (len >= (int)sizeof(clause->str) - 1)
232 logf("Too long str in condition");
233 return false;
236 clause->tag = tag;
237 clause->type = type;
238 if (len == 0)
239 clause->input = true;
240 else
241 clause->input = false;
243 if (tagcache_is_numeric_tag(tag))
245 clause->numeric = true;
246 clause->numeric_data = atoi(str);
248 else
250 clause->numeric = false;
251 strcpy(clause->str, str);
254 inst->clause_count[inst->tagorder_count]++;
256 return true;
259 static bool read_variable(char *buf, int size)
261 int condition;
263 if (!get_clause(&condition))
264 return false;
266 if (condition != clause_is)
267 return false;
269 if (get_token_str(buf, size) < 0)
270 return false;
272 return true;
275 /* "%3d. %s" autoscore title %sort = "inverse" %limit = "100" */
276 static int get_format_str(struct display_format *fmt)
278 int ret;
279 char buf[32];
281 memset(fmt, 0, sizeof(struct display_format));
283 if (get_token_str(fmt->formatstr, sizeof fmt->formatstr) < 0)
284 return -10;
286 while (fmt->tag_count < MAX_TAGS)
288 ret = get_tag(&fmt->tags[fmt->tag_count]);
289 if (ret < 0)
290 return -11;
292 if (ret == 0)
293 break;
295 switch (fmt->tags[fmt->tag_count]) {
296 case var_sorttype:
297 if (!read_variable(buf, sizeof buf))
298 return -12;
299 if (!strcasecmp("inverse", buf))
300 fmt->sort_inverse = true;
301 break;
303 case var_limit:
304 if (!read_variable(buf, sizeof buf))
305 return -13;
306 fmt->limit = atoi(buf);
307 break;
309 default:
310 fmt->tag_count++;
314 fmt->valid = true;
316 return 1;
319 static int get_condition(struct search_instruction *inst)
321 int tag;
322 int condition;
323 char buf[32];
325 switch (*strp)
327 case '=':
328 if (get_format_str(&inst->format[inst->tagorder_count]) < 0)
330 logf("get_format_str() parser failed!");
331 return -4;
333 return 1;
335 case '?':
336 case ' ':
337 case '&':
338 strp++;
339 return 1;
340 case ':':
341 strp++;
342 case '\0':
343 return 0;
346 if (get_tag(&tag) <= 0)
347 return -1;
349 if (get_clause(&condition) <= 0)
350 return -2;
352 if (get_token_str(buf, sizeof buf) < 0)
353 return -3;
355 logf("got clause: %d/%d [%s]", tag, condition, buf);
356 add_clause(inst, tag, condition, buf);
358 return 1;
361 /* example search:
362 * "Best" artist ? year >= "2000" & title !^ "crap" & genre = "good genre" \
363 * : album ? year >= "2000" : songs
364 * ^ begins with
365 * * contains
366 * $ ends with
369 static bool parse_search(struct search_instruction *inst, const char *str)
371 int ret;
373 memset(inst, 0, sizeof(struct search_instruction));
374 strp = str;
376 if (get_token_str(inst->name, sizeof inst->name) < 0)
378 logf("No name found.");
379 return false;
382 while (inst->tagorder_count < MAX_TAGS)
384 ret = get_tag(&inst->tagorder[inst->tagorder_count]);
385 if (ret < 0)
387 logf("Parse error #1");
388 return false;
391 if (ret == 0)
392 break ;
394 logf("tag: %d", inst->tagorder[inst->tagorder_count]);
396 while (get_condition(inst) > 0) ;
398 inst->tagorder_count++;
401 return true;
404 static int compare(const void *p1, const void *p2)
406 struct tagentry *e1 = (struct tagentry *)p1;
407 struct tagentry *e2 = (struct tagentry *)p2;
409 if (sort_inverse)
410 return strncasecmp(e2->name, e1->name, MAX_PATH);
412 return strncasecmp(e1->name, e2->name, MAX_PATH);
415 static void tagtree_buffer_event(struct mp3entry *id3, bool last_track)
417 (void)id3;
418 (void)last_track;
420 /* Do not gather data unless proper setting has been enabled. */
421 if (!global_settings.runtimedb)
422 return;
424 logf("be:%d%s", last_track, id3->path);
426 if (!tagcache_find_index(&tcs, id3->path))
428 logf("tc stat: not found: %s", id3->path);
429 return;
432 id3->playcount = tagcache_get_numeric(&tcs, tag_playcount);
433 id3->lastplayed = tagcache_get_numeric(&tcs, tag_lastplayed);
434 id3->rating = tagcache_get_numeric(&tcs, tag_virt_autoscore) / 10;
436 tagcache_search_finish(&tcs);
439 static void tagtree_unbuffer_event(struct mp3entry *id3, bool last_track)
441 (void)last_track;
442 long playcount;
443 long playtime;
444 long lastplayed;
446 /* Do not gather data unless proper setting has been enabled. */
447 if (!global_settings.runtimedb)
449 logf("runtimedb gathering not enabled");
450 return;
453 /* Don't process unplayed tracks. */
454 if (id3->elapsed == 0)
456 logf("not logging unplayed track");
457 return;
460 if (!tagcache_find_index(&tcs, id3->path))
462 logf("tc stat: not found: %s", id3->path);
463 return;
466 playcount = tagcache_get_numeric(&tcs, tag_playcount);
467 playtime = tagcache_get_numeric(&tcs, tag_playtime);
468 lastplayed = tagcache_get_numeric(&tcs, tag_lastplayed);
470 playcount++;
472 lastplayed = tagcache_increase_serial();
473 if (lastplayed < 0)
475 logf("incorrect tc serial:%d", lastplayed);
476 tagcache_search_finish(&tcs);
477 return;
480 /* Ignore the last 15s (crossfade etc.) */
481 playtime += MIN(id3->length, id3->elapsed + 15 * 1000);
483 logf("ube:%s", id3->path);
484 logf("-> %d/%d/%d", last_track, playcount, playtime);
485 logf("-> %d/%d/%d", id3->elapsed, id3->length, MIN(id3->length, id3->elapsed + 15 * 1000));
487 /* lastplayed not yet supported. */
489 if (!tagcache_modify_numeric_entry(&tcs, tag_playcount, playcount)
490 || !tagcache_modify_numeric_entry(&tcs, tag_playtime, playtime)
491 || !tagcache_modify_numeric_entry(&tcs, tag_lastplayed, lastplayed))
493 logf("tc stat: modify failed!");
494 tagcache_search_finish(&tcs);
495 return;
498 tagcache_search_finish(&tcs);
501 bool tagtree_export(void)
503 gui_syncsplash(0, true, str(LANG_CREATING));
504 if (!tagcache_create_changelog(&tcs))
506 gui_syncsplash(HZ*2, true, str(LANG_FAILED));
509 return false;
512 bool tagtree_import(void)
514 gui_syncsplash(0, true, str(LANG_WAIT));
515 if (!tagcache_import_changelog())
517 gui_syncsplash(HZ*2, true, str(LANG_FAILED));
520 return false;
523 void tagtree_init(void)
525 int fd;
526 char buf[256];
527 int rc;
528 int line_count;
530 fd = open(FILE_SEARCH_INSTRUCTIONS, O_RDONLY);
531 if (fd < 0)
533 logf("Search instruction file not found.");
534 return ;
537 /* Pre-pass search instructions file to count how many entries */
538 line_count = 0;
539 while ( 1 )
541 rc = read_line(fd, buf, sizeof(buf)-1);
542 if (rc <= 0)
543 break;
544 line_count++;
547 /* Allocate memory for searches */
548 si = (struct search_instruction *) buffer_alloc(sizeof(struct search_instruction) * line_count + 4);
550 /* Now read file for real, parsing into si */
551 lseek(fd, 0L, SEEK_SET);
552 while ( 1 )
554 rc = read_line(fd, buf, sizeof(buf)-1);
555 if (rc <= 0)
556 break;
557 if (!parse_search(si + si_count, buf))
558 break;
559 si_count++;
561 close(fd);
563 uniqbuf = buffer_alloc(UNIQBUF_SIZE);
564 audio_set_track_buffer_event(tagtree_buffer_event);
565 audio_set_track_unbuffer_event(tagtree_unbuffer_event);
568 bool show_search_progress(bool init, int count)
570 static int last_tick = 0;
572 if (init)
574 last_tick = current_tick;
575 return true;
578 if (current_tick - last_tick > HZ/4)
580 gui_syncsplash(0, true, str(LANG_PLAYLIST_SEARCH_MSG), count,
581 #if CONFIG_KEYPAD == PLAYER_PAD
582 str(LANG_STOP_ABORT)
583 #else
584 str(LANG_OFF_ABORT)
585 #endif
587 if (action_userabort(TIMEOUT_NOBLOCK))
588 return false;
589 last_tick = current_tick;
590 yield();
593 return true;
596 int retrieve_entries(struct tree_context *c, struct tagcache_search *tcs,
597 int offset, bool init)
599 struct tagentry *dptr = (struct tagentry *)c->dircache;
600 struct display_format *fmt;
601 int i;
602 int namebufused = 0;
603 int total_count = 0;
604 int special_entry_count = 0;
605 int level = c->currextra;
606 int tag;
607 bool sort = false;
608 int sort_limit = 0;
610 if (init
611 #ifdef HAVE_TC_RAMCACHE
612 && !tagcache_is_ramcache()
613 #endif
616 show_search_progress(true, 0);
617 gui_syncsplash(0, true, str(LANG_PLAYLIST_SEARCH_MSG),
618 0, csi->name);
621 if (c->currtable == allsubentries)
623 tag = tag_title;
624 level--;
626 else
627 tag = csi->tagorder[level];
629 if (!tagcache_search(tcs, tag))
630 return -1;
632 /* Prevent duplicate entries in the search list. */
633 tagcache_search_set_uniqbuf(tcs, uniqbuf, UNIQBUF_SIZE);
635 if (level || csi->clause_count[0] || tagcache_is_numeric_tag(tag))
636 sort = true;
638 for (i = 0; i < level; i++)
640 if (tagcache_is_numeric_tag(csi->tagorder[i]))
642 static struct tagcache_search_clause cc;
644 memset(&cc, 0, sizeof(struct tagcache_search_clause));
645 cc.tag = csi->tagorder[i];
646 cc.type = clause_is;
647 cc.numeric = true;
648 cc.numeric_data = csi->result_seek[i];
649 tagcache_search_add_clause(tcs, &cc);
651 else
653 tagcache_search_add_filter(tcs, csi->tagorder[i],
654 csi->result_seek[i]);
658 for (i = 0; i <= level; i++)
660 int j;
662 for (j = 0; j < csi->clause_count[i]; j++)
663 tagcache_search_add_clause(tcs, &csi->clause[i][j]);
666 current_offset = offset;
667 current_entry_count = 0;
668 c->dirfull = false;
669 fmt = &csi->format[level];
670 if (fmt->valid)
672 sort_inverse = fmt->sort_inverse;
673 sort_limit = fmt->limit;
675 else
677 sort_inverse = false;
678 sort_limit = 0;
681 if (tag != tag_title && tag != tag_filename)
683 if (offset == 0)
685 dptr->newtable = allsubentries;
686 dptr->name = str(LANG_TAGNAVI_ALL_TRACKS);
687 dptr++;
688 current_entry_count++;
690 special_entry_count++;
693 total_count += special_entry_count;
695 while (tagcache_get_next(tcs))
697 if (total_count++ < offset)
698 continue;
700 dptr->newtable = navibrowse;
701 if (tag == tag_title || tag == tag_filename)
703 dptr->newtable = playtrack;
704 dptr->extraseek = tcs->idx_id;
706 else
707 dptr->extraseek = tcs->result_seek;
709 if (!tcs->ramresult || fmt->valid)
711 char buf[MAX_PATH];
712 int buf_pos = 0;
714 if (fmt->valid)
716 char fmtbuf[8];
717 bool read_format = false;
718 int fmtbuf_pos = 0;
719 int parpos = 0;
721 memset(buf, 0, sizeof buf);
722 for (i = 0; fmt->formatstr[i] != '\0'; i++)
724 if (fmt->formatstr[i] == '%')
726 read_format = true;
727 fmtbuf_pos = 0;
728 if (parpos >= fmt->tag_count)
730 logf("too many format tags");
731 return 0;
735 if (read_format)
737 fmtbuf[fmtbuf_pos++] = fmt->formatstr[i];
738 if (fmtbuf_pos >= (long)sizeof(fmtbuf))
740 logf("format parse error");
741 return 0;
744 if (fmt->formatstr[i] == 's')
746 fmtbuf[fmtbuf_pos] = '\0';
747 read_format = false;
748 snprintf(&buf[buf_pos], MAX_PATH - buf_pos, fmtbuf, tcs->result);
749 buf_pos += strlen(&buf[buf_pos]);
750 parpos++;
752 else if (fmt->formatstr[i] == 'd')
754 fmtbuf[fmtbuf_pos] = '\0';
755 read_format = false;
756 snprintf(&buf[buf_pos], MAX_PATH - buf_pos, fmtbuf,
757 tagcache_get_numeric(tcs, fmt->tags[parpos]));
758 buf_pos += strlen(&buf[buf_pos]);
759 parpos++;
761 continue;
764 buf[buf_pos++] = fmt->formatstr[i];
766 if (buf_pos - 1 >= (long)sizeof(buf))
768 logf("buffer overflow");
769 return 0;
773 buf[buf_pos++] = '\0';
776 dptr->name = &c->name_buffer[namebufused];
777 if (fmt->valid)
778 namebufused += buf_pos;
779 else
780 namebufused += tcs->result_len;
782 if (namebufused >= c->name_buffer_size)
784 logf("chunk mode #2: %d", current_entry_count);
785 c->dirfull = true;
786 sort = false;
787 break ;
789 if (fmt->valid)
790 strcpy(dptr->name, buf);
791 else
792 strcpy(dptr->name, tcs->result);
794 else
795 dptr->name = tcs->result;
797 dptr++;
798 current_entry_count++;
800 if (current_entry_count >= global_settings.max_files_in_dir)
802 logf("chunk mode #3: %d", current_entry_count);
803 c->dirfull = true;
804 sort = false;
805 break ;
808 if (init && !tcs->ramsearch)
810 if (!show_search_progress(false, i))
812 tagcache_search_finish(tcs);
813 return current_entry_count;
818 if (sort)
819 qsort(c->dircache + special_entry_count * c->dentry_size,
820 current_entry_count - special_entry_count,
821 c->dentry_size, compare);
823 if (!init)
825 tagcache_search_finish(tcs);
826 return current_entry_count;
829 while (tagcache_get_next(tcs))
831 if (!tcs->ramsearch)
833 if (!show_search_progress(false, total_count))
834 break;
836 total_count++;
839 tagcache_search_finish(tcs);
841 if (!sort && (sort_inverse || sort_limit))
843 gui_syncsplash(HZ*4, true, str(LANG_SHOWDIR_BUFFER_FULL), total_count);
844 logf("Too small dir buffer");
845 return 0;
848 if (sort_limit)
849 total_count = MIN(total_count, sort_limit);
851 return total_count;
854 static int load_root(struct tree_context *c)
856 struct tagentry *dptr = (struct tagentry *)c->dircache;
857 int i;
859 tc = c;
860 c->currtable = root;
861 for (i = 0; i < si_count; i++)
863 dptr->name = (si+i)->name;
864 dptr->newtable = navibrowse;
865 dptr->extraseek = i;
866 dptr++;
869 current_offset = 0;
870 current_entry_count = i;
872 return i;
875 int tagtree_load(struct tree_context* c)
877 int count;
878 int table = c->currtable;
880 c->dentry_size = sizeof(struct tagentry);
882 if (!table)
884 c->dirfull = false;
885 table = root;
886 c->currtable = table;
889 switch (table)
891 case root:
892 count = load_root(c);
893 c->dirlevel = 0;
894 break;
896 case allsubentries:
897 case navibrowse:
898 logf("navibrowse...");
899 cpu_boost(true);
900 count = retrieve_entries(c, &tcs, 0, true);
901 cpu_boost(false);
902 break;
904 default:
905 logf("Unsupported table %d\n", table);
906 return -1;
909 if (count < 0)
911 c->dirlevel = 0;
912 count = load_root(c);
913 gui_syncsplash(HZ, true, str(LANG_TAGCACHE_BUSY));
916 /* The _total_ numer of entries available. */
917 c->dirlength = c->filesindir = count;
919 return count;
922 int tagtree_enter(struct tree_context* c)
924 int rc = 0;
925 struct tagentry *dptr;
926 int newextra;
927 int seek;
929 dptr = tagtree_get_entry(c, c->selected_item);
931 c->dirfull = false;
932 newextra = dptr->newtable;
933 seek = dptr->extraseek;
935 if (c->dirlevel >= MAX_DIR_LEVELS)
936 return 0;
938 c->selected_item_history[c->dirlevel]=c->selected_item;
939 c->table_history[c->dirlevel] = c->currtable;
940 c->extra_history[c->dirlevel] = c->currextra;
941 c->pos_history[c->dirlevel] = c->firstpos;
942 c->dirlevel++;
944 switch (c->currtable) {
945 case root:
946 c->currextra = newextra;
948 if (newextra == navibrowse)
950 int i, j;
952 csi = si+seek;
953 c->currextra = 0;
955 /* Read input as necessary. */
956 for (i = 0; i < csi->tagorder_count; i++)
958 for (j = 0; j < csi->clause_count[i]; j++)
960 if (!csi->clause[i][j].input)
961 continue;
963 rc = kbd_input(searchstring, sizeof(searchstring));
964 if (rc == -1 || !searchstring[0])
966 c->dirlevel--;
967 return 0;
970 if (csi->clause[i][j].numeric)
971 csi->clause[i][j].numeric_data = atoi(searchstring);
972 else
973 strncpy(csi->clause[i][j].str, searchstring,
974 sizeof(csi->clause[i][j].str)-1);
978 c->currtable = newextra;
980 break;
982 case navibrowse:
983 case allsubentries:
984 if (newextra == playtrack)
986 c->dirlevel--;
987 /* about to create a new current playlist...
988 allow user to cancel the operation */
989 if (global_settings.warnon_erase_dynplaylist &&
990 !global_settings.party_mode &&
991 playlist_modified(NULL))
993 char *lines[]={str(LANG_WARN_ERASEDYNPLAYLIST_PROMPT)};
994 struct text_message message={lines, 1};
996 if (gui_syncyesno_run(&message, NULL, NULL) != YESNO_YES)
997 break;
1000 if (tagtree_play_folder(c) >= 0)
1001 rc = 2;
1002 break;
1005 c->currtable = newextra;
1006 csi->result_seek[c->currextra] = seek;
1007 if (c->currextra < csi->tagorder_count-1)
1008 c->currextra++;
1009 else
1010 c->dirlevel--;
1011 break;
1013 default:
1014 c->dirlevel--;
1015 break;
1018 c->selected_item=0;
1019 gui_synclist_select_item(&tree_lists, c->selected_item);
1021 return rc;
1024 void tagtree_exit(struct tree_context* c)
1026 c->dirfull = false;
1027 c->dirlevel--;
1028 c->selected_item=c->selected_item_history[c->dirlevel];
1029 gui_synclist_select_item(&tree_lists, c->selected_item);
1030 c->currtable = c->table_history[c->dirlevel];
1031 c->currextra = c->extra_history[c->dirlevel];
1032 c->firstpos = c->pos_history[c->dirlevel];
1035 int tagtree_get_filename(struct tree_context* c, char *buf, int buflen)
1037 struct tagentry *entry;
1039 entry = tagtree_get_entry(c, c->selected_item);
1041 if (!tagcache_search(&tcs, tag_filename))
1042 return -1;
1044 if (!tagcache_retrieve(&tcs, entry->extraseek, buf, buflen))
1046 tagcache_search_finish(&tcs);
1047 return -2;
1050 tagcache_search_finish(&tcs);
1052 return 0;
1055 bool insert_all_playlist(struct tree_context *c, int position, bool queue)
1057 int i;
1058 char buf[MAX_PATH];
1059 int from, to, direction;
1061 cpu_boost(true);
1062 if (!tagcache_search(&tcs, tag_filename))
1064 gui_syncsplash(HZ, true, str(LANG_TAGCACHE_BUSY));
1065 cpu_boost(false);
1066 return false;
1069 if (position == PLAYLIST_INSERT_FIRST)
1071 from = c->filesindir - 1;
1072 to = -1;
1073 direction = -1;
1075 else
1077 from = 0;
1078 to = c->filesindir;
1079 direction = 1;
1082 for (i = from; i != to; i += direction)
1084 if (!show_search_progress(false, i))
1085 break;
1087 if (!tagcache_retrieve(&tcs, tagtree_get_entry(c, i)->extraseek,
1088 buf, sizeof buf))
1090 continue;
1093 if (playlist_insert_track(NULL, buf, position, queue, false) < 0)
1095 logf("playlist_insert_track failed");
1096 break;
1098 yield();
1100 playlist_sync(NULL);
1101 tagcache_search_finish(&tcs);
1102 cpu_boost(false);
1104 return true;
1107 bool tagtree_insert_selection_playlist(int position, bool queue)
1109 struct tagentry *dptr;
1110 char buf[MAX_PATH];
1111 int dirlevel = tc->dirlevel;
1113 /* We need to set the table to allsubentries. */
1114 show_search_progress(true, 0);
1116 dptr = tagtree_get_entry(tc, tc->selected_item);
1118 /* Insert a single track? */
1119 if (dptr->newtable == playtrack)
1121 if (tagtree_get_filename(tc, buf, sizeof buf) < 0)
1123 logf("tagtree_get_filename failed");
1124 return false;
1126 playlist_insert_track(NULL, buf, position, queue, true);
1128 return true;
1131 if (dptr->newtable == navibrowse)
1133 tagtree_enter(tc);
1134 tagtree_load(tc);
1135 dptr = tagtree_get_entry(tc, tc->selected_item);
1137 else if (dptr->newtable != allsubentries)
1139 logf("unsupported table: %d", dptr->newtable);
1140 return false;
1143 /* Now the current table should be allsubentries. */
1144 if (dptr->newtable != playtrack)
1146 tagtree_enter(tc);
1147 tagtree_load(tc);
1148 dptr = tagtree_get_entry(tc, tc->selected_item);
1150 /* And now the newtable should be playtrack. */
1151 if (dptr->newtable != playtrack)
1153 logf("newtable: %d !!", dptr->newtable);
1154 tc->dirlevel = dirlevel;
1155 return false;
1159 if (tc->filesindir <= 0)
1160 gui_syncsplash(HZ, true, str(LANG_END_PLAYLIST_PLAYER));
1161 else
1163 logf("insert_all_playlist");
1164 if (!insert_all_playlist(tc, position, queue))
1165 gui_syncsplash(HZ*2, true, str(LANG_FAILED));
1168 /* Finally return the dirlevel to its original value. */
1169 while (tc->dirlevel > dirlevel)
1170 tagtree_exit(tc);
1171 tagtree_load(tc);
1173 return true;
1176 static int tagtree_play_folder(struct tree_context* c)
1178 if (playlist_create(NULL, NULL) < 0)
1180 logf("Failed creating playlist\n");
1181 return -1;
1184 if (!insert_all_playlist(c, PLAYLIST_INSERT, false))
1185 return -2;
1187 if (global_settings.playlist_shuffle)
1188 c->selected_item = playlist_shuffle(current_tick, c->selected_item);
1189 if (!global_settings.play_selected)
1190 c->selected_item = 0;
1191 gui_synclist_select_item(&tree_lists, c->selected_item);
1193 playlist_start(c->selected_item,0);
1195 return 0;
1198 struct tagentry* tagtree_get_entry(struct tree_context *c, int id)
1200 struct tagentry *entry = (struct tagentry *)c->dircache;
1201 int realid = id - current_offset;
1203 /* Load the next chunk if necessary. */
1204 if (realid >= current_entry_count || realid < 0)
1206 cpu_boost(true);
1207 if (retrieve_entries(c, &tcs2, MAX(0, id - (current_entry_count / 2)),
1208 false) < 0)
1210 logf("retrieve failed");
1211 cpu_boost(false);
1212 return NULL;
1214 realid = id - current_offset;
1215 cpu_boost(false);
1218 return &entry[realid];
1221 int tagtree_get_attr(struct tree_context* c)
1223 int attr = -1;
1224 switch (c->currtable)
1226 case navibrowse:
1227 if (csi->tagorder[c->currextra] == tag_title)
1228 attr = TREE_ATTR_MPA;
1229 else
1230 attr = ATTR_DIRECTORY;
1231 break;
1233 case allsubentries:
1234 attr = TREE_ATTR_MPA;
1235 break;
1237 default:
1238 attr = ATTR_DIRECTORY;
1239 break;
1242 return attr;
1245 #ifdef HAVE_LCD_BITMAP
1246 const unsigned char* tagtree_get_icon(struct tree_context* c)
1247 #else
1248 int tagtree_get_icon(struct tree_context* c)
1249 #endif
1251 int icon = Icon_Folder;
1253 if (tagtree_get_attr(c) == TREE_ATTR_MPA)
1254 icon = Icon_Audio;
1256 #ifdef HAVE_LCD_BITMAP
1257 return bitmap_icons_6x8[icon];
1258 #else
1259 return icon;
1260 #endif