1 is the correct value here. Not that it makes any difference... :)
[kugel-rb.git] / apps / tagtree.c
blob8b7182cc8f0deca540ab21a84fef8ce6ec27af70
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 /* Capacity 10 000 entries (for example 10k different artists) */
53 #define UNIQBUF_SIZE (64*1024)
54 static long *uniqbuf;
56 #define MAX_TAGS 5
59 * "%3d. %s" autoscore title
61 * valid = true
62 * formatstr = "%-3d. %s"
63 * tags[0] = tag_autoscore
64 * tags[1] = tag_title
65 * tag_count = 2
67 struct display_format {
68 bool valid;
69 char formatstr[64];
70 int tags[MAX_TAGS];
71 int tag_count;
74 struct search_instruction {
75 char name[64];
76 int tagorder[MAX_TAGS];
77 int tagorder_count;
78 struct tagcache_search_clause clause[MAX_TAGS][TAGCACHE_MAX_CLAUSES];
79 struct display_format format[MAX_TAGS];
80 int clause_count[MAX_TAGS];
81 int result_seek[MAX_TAGS];
84 static struct search_instruction *si, *csi;
85 static int si_count = 0;
86 static const char *strp;
88 static int current_offset;
89 static int current_entry_count;
91 static struct tree_context *tc;
93 static int get_token_str(char *buf, int size)
95 /* Find the start. */
96 while (*strp != '"' && *strp != '\0')
97 strp++;
99 if (*strp == '\0' || *(++strp) == '\0')
100 return -1;
102 /* Read the data. */
103 while (*strp != '"' && *strp != '\0' && --size > 0)
104 *(buf++) = *(strp++);
106 *buf = '\0';
107 if (*strp != '"')
108 return -2;
110 strp++;
112 return 0;
115 #define MATCH(tag,str1,str2,settag) \
116 if (!strcasecmp(str1, str2)) { \
117 *tag = settag; \
118 return 1; \
121 static int get_tag(int *tag)
123 char buf[32];
124 int i;
126 /* Find the start. */
127 while (*strp == ' ' && *strp != '\0')
128 strp++;
130 if (*strp == '\0')
131 return 0;
133 for (i = 0; i < (int)sizeof(buf)-1; i++)
135 if (*strp == '\0' || *strp == ' ')
136 break ;
137 buf[i] = *strp;
138 strp++;
140 buf[i] = '\0';
142 MATCH(tag, buf, "album", tag_album);
143 MATCH(tag, buf, "artist", tag_artist);
144 MATCH(tag, buf, "bitrate", tag_bitrate);
145 MATCH(tag, buf, "composer", tag_composer);
146 MATCH(tag, buf, "genre", tag_genre);
147 MATCH(tag, buf, "length", tag_length);
148 MATCH(tag, buf, "title", tag_title);
149 MATCH(tag, buf, "filename", tag_filename);
150 MATCH(tag, buf, "tracknum", tag_tracknumber);
151 MATCH(tag, buf, "year", tag_year);
152 MATCH(tag, buf, "playcount", tag_playcount);
153 MATCH(tag, buf, "autoscore", tag_virt_autoscore);
155 logf("NO MATCH: %s\n", buf);
156 if (buf[0] == '?')
157 return 0;
159 return -1;
162 static int get_clause(int *condition)
164 char buf[4];
165 int i;
167 /* Find the start. */
168 while (*strp == ' ' && *strp != '\0')
169 strp++;
171 if (*strp == '\0')
172 return 0;
174 for (i = 0; i < (int)sizeof(buf)-1; i++)
176 if (*strp == '\0' || *strp == ' ')
177 break ;
178 buf[i] = *strp;
179 strp++;
181 buf[i] = '\0';
183 MATCH(condition, buf, "=", clause_is);
184 MATCH(condition, buf, "==", clause_is);
185 MATCH(condition, buf, "!=", clause_is_not);
186 MATCH(condition, buf, ">", clause_gt);
187 MATCH(condition, buf, ">=", clause_gteq);
188 MATCH(condition, buf, "<", clause_lt);
189 MATCH(condition, buf, "<=", clause_lteq);
190 MATCH(condition, buf, "~", clause_contains);
191 MATCH(condition, buf, "!~", clause_not_contains);
192 MATCH(condition, buf, "^", clause_begins_with);
193 MATCH(condition, buf, "!^", clause_not_begins_with);
194 MATCH(condition, buf, "$", clause_ends_with);
195 MATCH(condition, buf, "!$", clause_not_ends_with);
197 return 0;
200 static bool add_clause(struct search_instruction *inst,
201 int tag, int type, const char *str)
203 int len = strlen(str);
204 struct tagcache_search_clause *clause;
206 if (inst->clause_count[inst->tagorder_count] >= TAGCACHE_MAX_CLAUSES)
208 logf("Too many clauses");
209 return false;
212 clause = &inst->clause[inst->tagorder_count]
213 [inst->clause_count[inst->tagorder_count]];
214 if (len >= (int)sizeof(clause->str) - 1)
216 logf("Too long str in condition");
217 return false;
220 clause->tag = tag;
221 clause->type = type;
222 if (len == 0)
223 clause->input = true;
224 else
225 clause->input = false;
227 if (tagcache_is_numeric_tag(tag))
229 clause->numeric = true;
230 clause->numeric_data = atoi(str);
232 else
234 clause->numeric = false;
235 strcpy(clause->str, str);
238 inst->clause_count[inst->tagorder_count]++;
240 return true;
243 static int get_format_str(struct display_format *fmt)
245 int ret;
247 memset(fmt, 0, sizeof(struct display_format));
249 if (get_token_str(fmt->formatstr, sizeof fmt->formatstr) < 0)
250 return -10;
252 while (fmt->tag_count < MAX_TAGS)
254 ret = get_tag(&fmt->tags[fmt->tag_count]);
255 if (ret < 0)
256 return -11;
258 if (ret == 0)
259 break;
261 fmt->tag_count++;
264 fmt->valid = true;
266 return 1;
269 static int get_condition(struct search_instruction *inst)
271 struct display_format format;
272 struct display_format *fmt = NULL;
273 int tag;
274 int condition;
275 char buf[32];
277 switch (*strp)
279 case '=':
280 if (get_format_str(&format) < 0)
282 logf("get_format_str() parser failed!");
283 return -4;
285 fmt = &format;
286 break;
288 case '?':
289 case ' ':
290 case '&':
291 strp++;
292 return 1;
293 case ':':
294 strp++;
295 case '\0':
296 return 0;
299 if (fmt)
301 memcpy(&inst->format[inst->tagorder_count], fmt,
302 sizeof(struct display_format));
304 else
305 inst->format[inst->tagorder_count].valid = false;
307 if (get_tag(&tag) <= 0)
308 return -1;
310 if (get_clause(&condition) <= 0)
311 return -2;
313 if (get_token_str(buf, sizeof buf) < 0)
314 return -3;
316 logf("got clause: %d/%d [%s]", tag, condition, buf);
317 add_clause(inst, tag, condition, buf);
319 return 1;
322 /* example search:
323 * "Best" artist ? year >= "2000" & title !^ "crap" & genre = "good genre" \
324 * : album ? year >= "2000" : songs
325 * ^ begins with
326 * * contains
327 * $ ends with
330 static bool parse_search(struct search_instruction *inst, const char *str)
332 int ret;
334 memset(inst, 0, sizeof(struct search_instruction));
335 strp = str;
337 if (get_token_str(inst->name, sizeof inst->name) < 0)
339 logf("No name found.");
340 return false;
343 while (inst->tagorder_count < MAX_TAGS)
345 ret = get_tag(&inst->tagorder[inst->tagorder_count]);
346 if (ret < 0)
348 logf("Parse error #1");
349 return false;
352 if (ret == 0)
353 break ;
355 logf("tag: %d", inst->tagorder[inst->tagorder_count]);
357 while (get_condition(inst) > 0) ;
359 inst->tagorder_count++;
362 return true;
366 static struct tagcache_search tcs, tcs2;
368 static int compare(const void *p1, const void *p2)
370 struct tagentry *e1 = (struct tagentry *)p1;
371 struct tagentry *e2 = (struct tagentry *)p2;
373 return strncasecmp(e1->name, e2->name, MAX_PATH);
376 static void tagtree_buffer_event(struct mp3entry *id3, bool last_track)
378 (void)id3;
379 (void)last_track;
381 logf("be:%d%s", last_track, id3->path);
384 static void tagtree_unbuffer_event(struct mp3entry *id3, bool last_track)
386 (void)last_track;
387 long playcount;
388 long playtime;
389 long lastplayed;
391 /* Do not gather data unless proper setting has been enabled. */
392 if (!global_settings.runtimedb)
394 logf("runtimedb gathering not enabled");
395 return;
398 /* Don't process unplayed tracks. */
399 if (id3->elapsed == 0)
401 logf("not logging unplayed track");
402 return;
405 if (!tagcache_find_index(&tcs, id3->path))
407 logf("tc stat: not found: %s", id3->path);
408 return;
411 playcount = tagcache_get_numeric(&tcs, tag_playcount);
412 playtime = tagcache_get_numeric(&tcs, tag_playtime);
413 lastplayed = tagcache_get_numeric(&tcs, tag_lastplayed);
415 playcount++;
417 lastplayed = tagcache_increase_serial();
418 if (lastplayed < 0)
420 logf("incorrect tc serial:%d", lastplayed);
421 tagcache_search_finish(&tcs);
422 return;
425 /* Ignore the last 15s (crossfade etc.) */
426 playtime += MIN(id3->length, id3->elapsed + 15 * 1000);
428 logf("ube:%s", id3->path);
429 logf("-> %d/%d/%d", last_track, playcount, playtime);
430 logf("-> %d/%d/%d", id3->elapsed, id3->length, MIN(id3->length, id3->elapsed + 15 * 1000));
432 /* lastplayed not yet supported. */
434 if (!tagcache_modify_numeric_entry(&tcs, tag_playcount, playcount)
435 || !tagcache_modify_numeric_entry(&tcs, tag_playtime, playtime)
436 || !tagcache_modify_numeric_entry(&tcs, tag_lastplayed, lastplayed))
438 logf("tc stat: modify failed!");
439 tagcache_search_finish(&tcs);
440 return;
443 tagcache_search_finish(&tcs);
446 bool tagtree_export(void)
448 gui_syncsplash(0, true, str(LANG_CREATING));
449 if (!tagcache_create_changelog(&tcs))
451 gui_syncsplash(HZ*2, true, str(LANG_FAILED));
454 return false;
457 bool tagtree_import(void)
459 gui_syncsplash(0, true, str(LANG_WAIT));
460 if (!tagcache_import_changelog())
462 gui_syncsplash(HZ*2, true, str(LANG_FAILED));
465 return false;
468 void tagtree_init(void)
470 int fd;
471 char buf[256];
472 int rc;
473 int line_count;
475 fd = open(FILE_SEARCH_INSTRUCTIONS, O_RDONLY);
476 if (fd < 0)
478 logf("Search instruction file not found.");
479 return ;
482 /* Pre-pass search instructions file to count how many entries */
483 line_count = 0;
484 while ( 1 )
486 rc = read_line(fd, buf, sizeof(buf)-1);
487 if (rc <= 0)
488 break;
489 line_count++;
492 /* Allocate memory for searches */
493 si = (struct search_instruction *) buffer_alloc(sizeof(struct search_instruction) * line_count + 4);
495 /* Now read file for real, parsing into si */
496 lseek(fd, 0L, SEEK_SET);
497 while ( 1 )
499 rc = read_line(fd, buf, sizeof(buf)-1);
500 if (rc <= 0)
501 break;
502 if (!parse_search(si + si_count, buf))
503 break;
504 si_count++;
506 close(fd);
508 uniqbuf = buffer_alloc(UNIQBUF_SIZE);
509 audio_set_track_buffer_event(tagtree_buffer_event);
510 audio_set_track_unbuffer_event(tagtree_unbuffer_event);
513 bool show_search_progress(bool init, int count)
515 static int last_tick = 0;
517 if (init)
519 last_tick = current_tick;
520 return true;
523 if (current_tick - last_tick > HZ/4)
525 gui_syncsplash(0, true, str(LANG_PLAYLIST_SEARCH_MSG), count,
526 #if CONFIG_KEYPAD == PLAYER_PAD
527 str(LANG_STOP_ABORT)
528 #else
529 str(LANG_OFF_ABORT)
530 #endif
532 if (action_userabort(TIMEOUT_NOBLOCK))
533 return false;
534 last_tick = current_tick;
535 yield();
538 return true;
541 int retrieve_entries(struct tree_context *c, struct tagcache_search *tcs,
542 int offset, bool init)
544 struct tagentry *dptr = (struct tagentry *)c->dircache;
545 int i;
546 int namebufused = 0;
547 int total_count = 0;
548 int special_entry_count = 0;
549 int level = c->currextra;
550 int tag;
551 bool sort = false;
553 if (init
554 #ifdef HAVE_TC_RAMCACHE
555 && !tagcache_is_ramcache()
556 #endif
559 show_search_progress(true, 0);
560 gui_syncsplash(0, true, str(LANG_PLAYLIST_SEARCH_MSG),
561 0, csi->name);
564 if (c->currtable == allsubentries)
566 tag = tag_title;
567 level--;
569 else
570 tag = csi->tagorder[level];
572 if (!tagcache_search(tcs, tag))
573 return -1;
575 /* Prevent duplicate entries in the search list. */
576 tagcache_search_set_uniqbuf(tcs, uniqbuf, UNIQBUF_SIZE);
578 if (level || csi->clause_count[0] || tagcache_is_numeric_tag(tag))
579 sort = true;
581 for (i = 0; i < level; i++)
583 if (tagcache_is_numeric_tag(csi->tagorder[i]))
585 static struct tagcache_search_clause cc;
587 memset(&cc, 0, sizeof(struct tagcache_search_clause));
588 cc.tag = csi->tagorder[i];
589 cc.type = clause_is;
590 cc.numeric = true;
591 cc.numeric_data = csi->result_seek[i];
592 tagcache_search_add_clause(tcs, &cc);
594 else
596 tagcache_search_add_filter(tcs, csi->tagorder[i],
597 csi->result_seek[i]);
601 for (i = 0; i <= level; i++)
603 int j;
605 for (j = 0; j < csi->clause_count[i]; j++)
606 tagcache_search_add_clause(tcs, &csi->clause[i][j]);
609 current_offset = offset;
610 current_entry_count = 0;
611 c->dirfull = false;
613 if (tag != tag_title && tag != tag_filename)
615 if (offset == 0)
617 dptr->newtable = allsubentries;
618 dptr->name = str(LANG_TAGNAVI_ALL_TRACKS);
619 dptr++;
620 current_entry_count++;
622 special_entry_count++;
625 total_count += special_entry_count;
627 while (tagcache_get_next(tcs))
629 struct display_format *fmt = &csi->format[level];
631 if (total_count++ < offset)
632 continue;
634 dptr->newtable = navibrowse;
635 dptr->extraseek = tcs->result_seek;
636 if (tag == tag_title || tag == tag_filename)
637 dptr->newtable = playtrack;
639 if (!tcs->ramsearch || fmt->valid
640 || tagcache_is_numeric_tag(tag))
642 char buf[MAX_PATH];
643 int buf_pos = 0;
645 if (fmt->valid)
647 char fmtbuf[8];
648 bool read_format = false;
649 int fmtbuf_pos = 0;
650 int parpos = 0;
652 memset(buf, 0, sizeof buf);
653 for (i = 0; fmt->formatstr[i] != '\0'; i++)
655 if (fmt->formatstr[i] == '%')
657 read_format = true;
658 fmtbuf_pos = 0;
659 if (parpos >= fmt->tag_count)
661 logf("too many format tags");
662 return 0;
666 if (read_format)
668 fmtbuf[fmtbuf_pos++] = fmt->formatstr[i];
669 if (fmtbuf_pos >= (long)sizeof(fmtbuf))
671 logf("format parse error");
672 return 0;
675 if (fmt->formatstr[i] == 's')
677 fmtbuf[fmtbuf_pos] = '\0';
678 read_format = false;
679 snprintf(&buf[buf_pos], MAX_PATH - buf_pos, fmtbuf, tcs->result);
680 buf_pos += strlen(&buf[buf_pos]);
681 parpos++;
683 else if (fmt->formatstr[i] == 'd')
685 fmtbuf[fmtbuf_pos] = '\0';
686 read_format = false;
687 snprintf(&buf[buf_pos], MAX_PATH - buf_pos, fmtbuf,
688 tagcache_get_numeric(tcs, fmt->tags[parpos]));
689 buf_pos += strlen(&buf[buf_pos]);
690 parpos++;
692 continue;
695 buf[buf_pos++] = fmt->formatstr[i];
697 if (buf_pos - 1 >= (long)sizeof(buf))
699 logf("buffer overflow");
700 return 0;
704 buf[buf_pos++] = '\0';
707 dptr->name = &c->name_buffer[namebufused];
708 if (fmt->valid)
709 namebufused += buf_pos;
710 else
711 namebufused += tcs->result_len;
713 if (namebufused >= c->name_buffer_size)
715 logf("chunk mode #2: %d", current_entry_count);
716 c->dirfull = true;
717 sort = false;
718 break ;
720 if (fmt->valid)
721 strcpy(dptr->name, buf);
722 else
723 strcpy(dptr->name, tcs->result);
725 else
726 dptr->name = tcs->result;
728 dptr++;
729 current_entry_count++;
731 if (current_entry_count >= global_settings.max_files_in_dir)
733 logf("chunk mode #3: %d", current_entry_count);
734 c->dirfull = true;
735 sort = false;
736 break ;
739 if (init && !tcs->ramsearch)
741 if (!show_search_progress(false, i))
743 tagcache_search_finish(tcs);
744 return current_entry_count;
749 if (sort)
750 qsort(c->dircache + special_entry_count * c->dentry_size,
751 current_entry_count - special_entry_count,
752 c->dentry_size, compare);
754 if (!init)
756 tagcache_search_finish(tcs);
757 return current_entry_count;
760 while (tagcache_get_next(tcs))
762 if (!tcs->ramsearch)
764 if (!show_search_progress(false, total_count))
765 break;
767 total_count++;
770 tagcache_search_finish(tcs);
771 return total_count;
774 static int load_root(struct tree_context *c)
776 struct tagentry *dptr = (struct tagentry *)c->dircache;
777 int i;
779 tc = c;
780 c->currtable = root;
781 for (i = 0; i < si_count; i++)
783 dptr->name = (si+i)->name;
784 dptr->newtable = navibrowse;
785 dptr->extraseek = i;
786 dptr++;
789 current_offset = 0;
790 current_entry_count = i;
792 return i;
795 int tagtree_load(struct tree_context* c)
797 int count;
798 int table = c->currtable;
800 c->dentry_size = sizeof(struct tagentry);
802 if (!table)
804 c->dirfull = false;
805 table = root;
806 c->currtable = table;
809 switch (table)
811 case root:
812 count = load_root(c);
813 c->dirlevel = 0;
814 break;
816 case allsubentries:
817 case navibrowse:
818 logf("navibrowse...");
819 cpu_boost(true);
820 count = retrieve_entries(c, &tcs, 0, true);
821 cpu_boost(false);
822 break;
824 default:
825 logf("Unsupported table %d\n", table);
826 return -1;
829 if (count < 0)
831 c->dirlevel = 0;
832 count = load_root(c);
833 gui_syncsplash(HZ, true, str(LANG_TAGCACHE_BUSY));
836 /* The _total_ numer of entries available. */
837 c->dirlength = c->filesindir = count;
839 return count;
842 int tagtree_enter(struct tree_context* c)
844 int rc = 0;
845 struct tagentry *dptr;
846 int newextra;
847 int seek;
849 dptr = tagtree_get_entry(c, c->selected_item);
851 c->dirfull = false;
852 newextra = dptr->newtable;
853 seek = dptr->extraseek;
855 if (c->dirlevel >= MAX_DIR_LEVELS)
856 return 0;
858 c->selected_item_history[c->dirlevel]=c->selected_item;
859 c->table_history[c->dirlevel] = c->currtable;
860 c->extra_history[c->dirlevel] = c->currextra;
861 c->pos_history[c->dirlevel] = c->firstpos;
862 c->dirlevel++;
864 switch (c->currtable) {
865 case root:
866 c->currextra = newextra;
868 if (newextra == navibrowse)
870 int i, j;
872 csi = si+seek;
873 c->currextra = 0;
875 /* Read input as necessary. */
876 for (i = 0; i < csi->tagorder_count; i++)
878 for (j = 0; j < csi->clause_count[i]; j++)
880 if (!csi->clause[i][j].input)
881 continue;
883 rc = kbd_input(searchstring, sizeof(searchstring));
884 if (rc == -1 || !searchstring[0])
886 c->dirlevel--;
887 return 0;
890 if (csi->clause[i][j].numeric)
891 csi->clause[i][j].numeric_data = atoi(searchstring);
892 else
893 strncpy(csi->clause[i][j].str, searchstring,
894 sizeof(csi->clause[i][j].str)-1);
898 c->currtable = newextra;
900 break;
902 case navibrowse:
903 case allsubentries:
904 if (newextra == playtrack)
906 c->dirlevel--;
907 /* about to create a new current playlist...
908 allow user to cancel the operation */
909 if (global_settings.warnon_erase_dynplaylist &&
910 !global_settings.party_mode &&
911 playlist_modified(NULL))
913 char *lines[]={str(LANG_WARN_ERASEDYNPLAYLIST_PROMPT)};
914 struct text_message message={lines, 1};
916 if (gui_syncyesno_run(&message, NULL, NULL) != YESNO_YES)
917 break;
920 if (tagtree_play_folder(c) >= 0)
921 rc = 2;
922 break;
925 c->currtable = newextra;
926 csi->result_seek[c->currextra] = seek;
927 if (c->currextra < csi->tagorder_count-1)
928 c->currextra++;
929 else
930 c->dirlevel--;
931 break;
933 default:
934 c->dirlevel--;
935 break;
938 c->selected_item=0;
939 gui_synclist_select_item(&tree_lists, c->selected_item);
941 return rc;
944 void tagtree_exit(struct tree_context* c)
946 c->dirfull = false;
947 c->dirlevel--;
948 c->selected_item=c->selected_item_history[c->dirlevel];
949 gui_synclist_select_item(&tree_lists, c->selected_item);
950 c->currtable = c->table_history[c->dirlevel];
951 c->currextra = c->extra_history[c->dirlevel];
952 c->firstpos = c->pos_history[c->dirlevel];
955 int tagtree_get_filename(struct tree_context* c, char *buf, int buflen)
957 struct tagentry *entry;
959 entry = tagtree_get_entry(c, c->selected_item);
961 if (!tagcache_search(&tcs, tag_filename))
962 return -1;
964 if (!tagcache_retrieve(&tcs, entry->extraseek, buf, buflen))
966 tagcache_search_finish(&tcs);
967 return -2;
970 tagcache_search_finish(&tcs);
972 return 0;
975 bool insert_all_playlist(struct tree_context *c, int position, bool queue)
977 int i;
978 char buf[MAX_PATH];
979 int from, to, direction;
981 cpu_boost(true);
982 if (!tagcache_search(&tcs, tag_filename))
984 gui_syncsplash(HZ, true, str(LANG_TAGCACHE_BUSY));
985 return false;
988 if (position == PLAYLIST_INSERT_FIRST)
990 from = c->filesindir - 1;
991 to = -1;
992 direction = -1;
994 else
996 from = 0;
997 to = c->filesindir;
998 direction = 1;
1001 for (i = from; i != to; i += direction)
1003 if (!show_search_progress(false, i))
1004 break;
1006 if (!tagcache_retrieve(&tcs, tagtree_get_entry(c, i)->extraseek,
1007 buf, sizeof buf))
1009 continue;
1012 if (playlist_insert_track(NULL, buf, position, queue, false) < 0)
1014 logf("playlist_insert_track failed");
1015 break;
1017 yield();
1019 playlist_sync(NULL);
1020 tagcache_search_finish(&tcs);
1021 cpu_boost(false);
1023 return true;
1026 bool tagtree_insert_selection_playlist(int position, bool queue)
1028 struct tagentry *dptr;
1029 char buf[MAX_PATH];
1030 int dirlevel = tc->dirlevel;
1032 /* We need to set the table to allsubentries. */
1033 show_search_progress(true, 0);
1035 dptr = tagtree_get_entry(tc, tc->selected_item);
1037 /* Insert a single track? */
1038 if (dptr->newtable == playtrack)
1040 if (tagtree_get_filename(tc, buf, sizeof buf) < 0)
1042 logf("tagtree_get_filename failed");
1043 return false;
1045 playlist_insert_track(NULL, buf, position, queue, true);
1047 return true;
1050 if (dptr->newtable == navibrowse)
1052 tagtree_enter(tc);
1053 tagtree_load(tc);
1054 dptr = tagtree_get_entry(tc, tc->selected_item);
1056 else if (dptr->newtable != allsubentries)
1058 logf("unsupported table: %d", dptr->newtable);
1059 return false;
1062 /* Now the current table should be allsubentries. */
1063 if (dptr->newtable != playtrack)
1065 tagtree_enter(tc);
1066 tagtree_load(tc);
1067 dptr = tagtree_get_entry(tc, tc->selected_item);
1069 /* And now the newtable should be playtrack. */
1070 if (dptr->newtable != playtrack)
1072 logf("newtable: %d !!", dptr->newtable);
1073 tc->dirlevel = dirlevel;
1074 return false;
1078 if (tc->filesindir <= 0)
1079 gui_syncsplash(HZ, true, str(LANG_END_PLAYLIST_PLAYER));
1080 else
1082 logf("insert_all_playlist");
1083 if (!insert_all_playlist(tc, position, queue))
1084 gui_syncsplash(HZ*2, true, str(LANG_FAILED));
1087 /* Finally return the dirlevel to its original value. */
1088 while (tc->dirlevel > dirlevel)
1089 tagtree_exit(tc);
1090 tagtree_load(tc);
1092 return true;
1095 static int tagtree_play_folder(struct tree_context* c)
1097 if (playlist_create(NULL, NULL) < 0)
1099 logf("Failed creating playlist\n");
1100 return -1;
1103 if (!insert_all_playlist(c, PLAYLIST_INSERT, false))
1104 return -2;
1106 if (global_settings.playlist_shuffle)
1107 c->selected_item = playlist_shuffle(current_tick, c->selected_item);
1108 if (!global_settings.play_selected)
1109 c->selected_item = 0;
1110 gui_synclist_select_item(&tree_lists, c->selected_item);
1112 playlist_start(c->selected_item,0);
1114 return 0;
1117 struct tagentry* tagtree_get_entry(struct tree_context *c, int id)
1119 struct tagentry *entry = (struct tagentry *)c->dircache;
1120 int realid = id - current_offset;
1122 /* Load the next chunk if necessary. */
1123 if (realid >= current_entry_count || realid < 0)
1125 cpu_boost(true);
1126 if (retrieve_entries(c, &tcs2, MAX(0, id - (current_entry_count / 2)),
1127 false) < 0)
1129 logf("retrieve failed");
1130 return NULL;
1132 realid = id - current_offset;
1133 cpu_boost(false);
1136 return &entry[realid];
1139 int tagtree_get_attr(struct tree_context* c)
1141 int attr = -1;
1142 switch (c->currtable)
1144 case navibrowse:
1145 if (csi->tagorder[c->currextra] == tag_title)
1146 attr = TREE_ATTR_MPA;
1147 else
1148 attr = ATTR_DIRECTORY;
1149 break;
1151 case allsubentries:
1152 attr = TREE_ATTR_MPA;
1153 break;
1155 default:
1156 attr = ATTR_DIRECTORY;
1157 break;
1160 return attr;
1163 #ifdef HAVE_LCD_BITMAP
1164 const unsigned char* tagtree_get_icon(struct tree_context* c)
1165 #else
1166 int tagtree_get_icon(struct tree_context* c)
1167 #endif
1169 int icon = Icon_Folder;
1171 if (tagtree_get_attr(c) == TREE_ATTR_MPA)
1172 icon = Icon_Audio;
1174 #ifdef HAVE_LCD_BITMAP
1175 return bitmap_icons_6x8[icon];
1176 #else
1177 return icon;
1178 #endif