summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/tagnavi.config61
-rw-r--r--apps/tagtree.c263
-rw-r--r--apps/tree.c2
3 files changed, 271 insertions, 55 deletions
diff --git a/apps/tagnavi.config b/apps/tagnavi.config
index 55418718bf..9821c30faf 100644
--- a/apps/tagnavi.config
+++ b/apps/tagnavi.config
@@ -1,15 +1,46 @@
1"Artist" artist : album : title = "%02d. %s" tracknum title 1#! rockbox/tagbrowser/2.0
2"Album" album : title = "%02d. %s" tracknum title 2# ^ Version header must be the first line of every file
3"Genre" genre : artist : album : title = "%02d. %s" tracknum title 3
4"Composer" composer : album : title = "%02d. %s" tracknum title 4# Tag Browser configuration file, do not edit as changes will be lost!
5"Track" title 5# Instead, you can modify "/.rockbox/tagnavi_custom.config" which will never
6"Year" year ? year > "1000" & year < "2008" : artist : album : title = "%02d. %s" tracknum title 6# get overwritten automatically.
7"Search by artist" artist ? artist ~ "" : album : title = "%02d. %s" tracknum title 7
8"Search by album" album ? album ~ "" : title = "%02d. %s" tracknum title 8# Include our custom menu
9"Search by title" title ? title ~ "" 9%include "/.rockbox/tagnavi_custom.config"
10"Search by filename" filename ? filename ~ "" 10
11"Search by score" title = "(%3d) %s" autoscore title ? autoscore > "" 11#
12"Most played tracks" title = "(%3d) %s" playcount title %sort = "inverse" %limit = "100" ? playcount > "0" 12# === Begin of "Search by..." sub menu
13"Never played tracks" artist ? playcount == "0" : album : title = "%02d. %s" tracknum title 13#
14"Best tracks" artist ? playcount > "1" & autoscore > "85" : album : title = "%02d. %s (%3d)" tracknum title autoscore 14
15"List played tracks" title = "(%3d/%d) %s" autoscore playcount title ? playcount > "0" 15# Define the search sub menu
16%menu_start "search" "Search by..."
17"Artist" -> artist ? artist ~ "" -> album -> title = "%02d. %s" tracknum title
18"Artist -> (score > 85)" artist ? artist ~ "" & autoscore >= "85" -> album -> title = "%02d. %s" tracknum title
19"Album" -> album ? album ~ "" -> title = "%02d. %s" tracknum title
20"Title" -> title ? title ~ ""
21"Filename" -> filename ? filename ~ ""
22"Score" -> title = "(%3d) %s" autoscore title ? autoscore > ""
23
24# ^ An empy line ends the menu
25
26#
27# === Begin of main menu
28#
29
30# Define the title of the main menu
31%menu_start "main" "Browse by..."
32"Artist" -> artist -> album -> title = "%02d. %s" tracknum title
33"Album" -> album -> title = "%02d. %s" tracknum title
34"Genre" -> genre -> artist -> album -> title = "%02d. %s" tracknum title
35"Composer" -> composer -> album -> title = "%02d. %s" tracknum title
36"Track" -> title
37"Year" -> year ? year > "1000" & year < "2008" -> artist -> album -> title = "%02d. %s" tracknum title
38"Search..." ==> "search"
39"Most played tracks" -> title = "(%3d) %s" playcount title %sort = "inverse" %limit = "100" ? playcount > "0"
40"Never played tracks" -> artist ? playcount == "0" -> album -> title = "%02d. %s" tracknum title
41"Best tracks" -> artist ? playcount > "1" & autoscore > "85" -> album -> title = "%02d. %s (%3d)" tracknum title autoscore
42"List played tracks" -> title = "(%3d/%d) %s" autoscore playcount title ? playcount > "0"
43"Custom view..." ==> "custom"
44
45# And finally set main menu as our root menu
46%root_menu "main"
diff --git a/apps/tagtree.c b/apps/tagtree.c
index c674cf4ebd..7a710c6fc3 100644
--- a/apps/tagtree.c
+++ b/apps/tagtree.c
@@ -51,7 +51,12 @@ static char searchstring[32];
51 51
52enum variables { 52enum variables {
53 var_sorttype = 100, 53 var_sorttype = 100,
54 var_limit 54 var_limit,
55 var_menu_start,
56 var_include,
57 var_rootmenu,
58 menu_next,
59 menu_load,
55}; 60};
56 61
57/* Capacity 10 000 entries (for example 10k different artists) */ 62/* Capacity 10 000 entries (for example 10k different artists) */
@@ -95,9 +100,30 @@ struct search_instruction {
95 int result_seek[MAX_TAGS]; 100 int result_seek[MAX_TAGS];
96}; 101};
97 102
98static struct search_instruction *si, *csi; 103struct menu_entry {
99static int si_count = 0; 104 char name[64];
105 int type;
106 struct search_instruction *si;
107 int link;
108};
109
110#define TAGNAVI_VERSION "#! rockbox/tagbrowser/2.0"
111#define TAGMENU_MAX_ITEMS 32
112#define TAGMENU_MAX_MENUS 8
113struct root_menu {
114 char title[64];
115 char id[32];
116 int itemcount;
117 struct root_menu *parent;
118 struct menu_entry *items[TAGMENU_MAX_ITEMS];
119};
120
121static struct root_menu menus[TAGMENU_MAX_MENUS];
122static struct root_menu *menu;
123static struct search_instruction *csi;
100static const char *strp; 124static const char *strp;
125static int menu_count;
126static int root_menu;
101 127
102static int current_offset; 128static int current_offset;
103static int current_entry_count; 129static int current_entry_count;
@@ -138,10 +164,10 @@ static int get_tag(int *tag)
138 int i; 164 int i;
139 165
140 /* Find the start. */ 166 /* Find the start. */
141 while (*strp == ' ' && *strp != '\0') 167 while ((*strp == ' ' || *strp == '>') && *strp != '\0')
142 strp++; 168 strp++;
143 169
144 if (*strp == '\0' || *strp == '?' || *strp == ':') 170 if (*strp == '\0' || *strp == '?')
145 return 0; 171 return 0;
146 172
147 for (i = 0; i < (int)sizeof(buf)-1; i++) 173 for (i = 0; i < (int)sizeof(buf)-1; i++)
@@ -167,6 +193,11 @@ static int get_tag(int *tag)
167 MATCH(tag, buf, "autoscore", tag_virt_autoscore); 193 MATCH(tag, buf, "autoscore", tag_virt_autoscore);
168 MATCH(tag, buf, "%sort", var_sorttype); 194 MATCH(tag, buf, "%sort", var_sorttype);
169 MATCH(tag, buf, "%limit", var_limit); 195 MATCH(tag, buf, "%limit", var_limit);
196 MATCH(tag, buf, "%menu_start", var_menu_start);
197 MATCH(tag, buf, "%include", var_include);
198 MATCH(tag, buf, "%root_menu", var_rootmenu);
199 MATCH(tag, buf, "->", menu_next);
200 MATCH(tag, buf, "==>", menu_load);
170 201
171 logf("NO MATCH: %s\n", buf); 202 logf("NO MATCH: %s\n", buf);
172 if (buf[0] == '?') 203 if (buf[0] == '?')
@@ -337,8 +368,7 @@ static int get_condition(struct search_instruction *inst)
337 case '&': 368 case '&':
338 strp++; 369 strp++;
339 return 1; 370 return 1;
340 case ':': 371 case '-':
341 strp++;
342 case '\0': 372 case '\0':
343 return 0; 373 return 0;
344 } 374 }
@@ -366,25 +396,56 @@ static int get_condition(struct search_instruction *inst)
366 * $ ends with 396 * $ ends with
367 */ 397 */
368 398
369static bool parse_search(struct search_instruction *inst, const char *str) 399static bool parse_search(struct menu_entry *entry, const char *str)
370{ 400{
371 int ret; 401 int ret;
402 int type;
403 struct search_instruction *inst = entry->si;
404 char buf[MAX_PATH];
405 int i;
372 406
373 memset(inst, 0, sizeof(struct search_instruction));
374 strp = str; 407 strp = str;
375 408
376 if (get_token_str(inst->name, sizeof inst->name) < 0) 409 /* Parse entry name */
410 if (get_token_str(entry->name, sizeof entry->name) < 0)
377 { 411 {
378 logf("No name found."); 412 logf("No name found.");
379 return false; 413 return false;
380 } 414 }
381 415
416 /* Parse entry type */
417 if (get_tag(&entry->type) <= 0)
418 return false;
419
420 if (entry->type == menu_load)
421 {
422 if (get_token_str(buf, sizeof buf) < 0)
423 return false;
424
425 /* Find the matching root menu or "create" it */
426 for (i = 0; i < menu_count; i++)
427 {
428 if (!strcasecmp(menus[i].id, buf))
429 {
430 entry->link = i;
431 menus[i].parent = menu;
432 return true;
433 }
434 }
435
436 return false;
437 }
438
439 if (entry->type != menu_next)
440 return false;
441
382 while (inst->tagorder_count < MAX_TAGS) 442 while (inst->tagorder_count < MAX_TAGS)
383 { 443 {
384 ret = get_tag(&inst->tagorder[inst->tagorder_count]); 444 ret = get_tag(&inst->tagorder[inst->tagorder_count]);
385 if (ret < 0) 445 if (ret < 0)
386 { 446 {
387 logf("Parse error #1"); 447 logf("Parse error #1");
448 logf("%s", strp);
388 return false; 449 return false;
389 } 450 }
390 451
@@ -396,6 +457,9 @@ static bool parse_search(struct search_instruction *inst, const char *str)
396 while (get_condition(inst) > 0) ; 457 while (get_condition(inst) > 0) ;
397 458
398 inst->tagorder_count++; 459 inst->tagorder_count++;
460
461 if (get_tag(&type) <= 0 || type != menu_next)
462 break;
399 } 463 }
400 464
401 return true; 465 return true;
@@ -520,46 +584,142 @@ bool tagtree_import(void)
520 return false; 584 return false;
521} 585}
522 586
523void tagtree_init(void) 587static bool parse_menu(const char *filename)
524{ 588{
525 int fd; 589 int fd;
526 char buf[256]; 590 char buf[256];
591 char data[256];
592 int variable;
527 int rc; 593 int rc;
528 int line_count; 594 bool first = true;
595 bool read_menu = false;
596 int i;
597
598 if (menu_count >= TAGMENU_MAX_MENUS)
599 return false;
529 600
530 fd = open(FILE_SEARCH_INSTRUCTIONS, O_RDONLY); 601 fd = open(filename, O_RDONLY);
531 if (fd < 0) 602 if (fd < 0)
532 { 603 {
533 logf("Search instruction file not found."); 604 logf("Search instruction file not found.");
534 return ; 605 return false;
535 } 606 }
536 607
537 /* Pre-pass search instructions file to count how many entries */
538 line_count = 0;
539 while ( 1 )
540 {
541 rc = read_line(fd, buf, sizeof(buf)-1);
542 if (rc <= 0)
543 break;
544 line_count++;
545 }
546
547 /* Allocate memory for searches */
548 si = (struct search_instruction *) buffer_alloc(sizeof(struct search_instruction) * line_count + 4);
549
550 /* Now read file for real, parsing into si */ 608 /* Now read file for real, parsing into si */
551 lseek(fd, 0L, SEEK_SET);
552 while ( 1 ) 609 while ( 1 )
553 { 610 {
554 rc = read_line(fd, buf, sizeof(buf)-1); 611 rc = read_line(fd, buf, sizeof(buf)-1);
555 if (rc <= 0) 612 if (rc <= 0)
556 break;
557 if (!parse_search(si + si_count, buf))
558 break; 613 break;
559 si_count++; 614
615 if (first)
616 {
617 if (strcasecmp(TAGNAVI_VERSION, buf))
618 {
619 logf("Version mismatch");
620 break;
621 }
622 first = false;
623 }
624
625 if (buf[0] == '#')
626 continue;
627
628 if (buf[0] == '\0')
629 {
630 if (read_menu)
631 {
632 /* End the menu */
633 menu_count++;
634 menu = &menus[menu_count];
635 read_menu = false;
636 }
637 continue;
638 }
639
640 if (!read_menu)
641 {
642 strp = buf;
643 if (get_tag(&variable) <= 0)
644 continue;
645
646 switch (variable)
647 {
648 case var_include:
649 if (get_token_str(data, sizeof(data)) < 0)
650 {
651 logf("%include empty");
652 return false;
653 }
654
655 if (!parse_menu(data))
656 {
657 logf("Load menu fail: %s", data);
658 }
659 break;
660
661 case var_menu_start:
662 if (get_token_str(menu->id, sizeof(menu->id)) < 0)
663 {
664 logf("%menu_start id empty");
665 return false;
666 }
667 if (get_token_str(menu->title, sizeof(menu->title)) < 0)
668 {
669 logf("%menu_start title empty");
670 return false;
671 }
672 menu->itemcount = 0;
673 read_menu = true;
674 break;
675
676 case var_rootmenu:
677 if (get_token_str(data, sizeof(data)) < 0)
678 {
679 logf("%root_menu empty");
680 return false;
681 }
682
683 for (i = 0; i < menu_count; i++)
684 {
685 if (!strcasecmp(menus[i].id, data))
686 {
687 root_menu = i;
688 }
689 }
690 break;
691 }
692
693 continue;
694 }
695
696 /* Allocate */
697 if (menu->items[menu->itemcount] == NULL)
698 {
699 menu->items[menu->itemcount] = buffer_alloc(sizeof(struct menu_entry));
700 memset(menu->items[menu->itemcount], 0, sizeof(struct menu_entry));
701 menu->items[menu->itemcount]->si = buffer_alloc(sizeof(struct search_instruction));
702 memset(menu->items[menu->itemcount]->si, 0, sizeof(struct search_instruction));
703 }
704
705 if (!parse_search(menu->items[menu->itemcount], buf))
706 continue;
707
708 menu->itemcount++;
560 } 709 }
561 close(fd); 710 close(fd);
562 711
712 return true;
713}
714
715void tagtree_init(void)
716{
717 memset(menus, 0, sizeof menus);
718 menu_count = 0;
719 menu = &menus[0];
720 root_menu = 0;
721 parse_menu(FILE_SEARCH_INSTRUCTIONS);
722
563 uniqbuf = buffer_alloc(UNIQBUF_SIZE); 723 uniqbuf = buffer_alloc(UNIQBUF_SIZE);
564 audio_set_track_buffer_event(tagtree_buffer_event); 724 audio_set_track_buffer_event(tagtree_buffer_event);
565 audio_set_track_unbuffer_event(tagtree_unbuffer_event); 725 audio_set_track_unbuffer_event(tagtree_unbuffer_event);
@@ -858,11 +1018,27 @@ static int load_root(struct tree_context *c)
858 1018
859 tc = c; 1019 tc = c;
860 c->currtable = root; 1020 c->currtable = root;
861 for (i = 0; i < si_count; i++) 1021 if (c->dirlevel == 0)
1022 c->currextra = root_menu;
1023
1024 menu = &menus[c->currextra];
1025
1026 for (i = 0; i < menu->itemcount; i++)
862 { 1027 {
863 dptr->name = (si+i)->name; 1028 dptr->name = menu->items[i]->name;
864 dptr->newtable = navibrowse; 1029 switch (menu->items[i]->type)
865 dptr->extraseek = i; 1030 {
1031 case menu_next:
1032 dptr->newtable = navibrowse;
1033 dptr->extraseek = i;
1034 break;
1035
1036 case menu_load:
1037 dptr->newtable = root;
1038 dptr->extraseek = menu->items[i]->link;
1039 break;
1040 }
1041
866 dptr++; 1042 dptr++;
867 } 1043 }
868 1044
@@ -884,13 +1060,13 @@ int tagtree_load(struct tree_context* c)
884 c->dirfull = false; 1060 c->dirfull = false;
885 table = root; 1061 table = root;
886 c->currtable = table; 1062 c->currtable = table;
1063 c->currextra = root_menu;
887 } 1064 }
888 1065
889 switch (table) 1066 switch (table)
890 { 1067 {
891 case root: 1068 case root:
892 count = load_root(c); 1069 count = load_root(c);
893 c->dirlevel = 0;
894 break; 1070 break;
895 1071
896 case allsubentries: 1072 case allsubentries:
@@ -945,11 +1121,17 @@ int tagtree_enter(struct tree_context* c)
945 case root: 1121 case root:
946 c->currextra = newextra; 1122 c->currextra = newextra;
947 1123
948 if (newextra == navibrowse) 1124 if (newextra == root)
1125 {
1126 menu = &menus[seek];
1127 c->currextra = seek;
1128 }
1129
1130 else if (newextra == navibrowse)
949 { 1131 {
950 int i, j; 1132 int i, j;
951 1133
952 csi = si+seek; 1134 csi = menu->items[seek]->si;
953 c->currextra = 0; 1135 c->currextra = 0;
954 1136
955 /* Read input as necessary. */ 1137 /* Read input as necessary. */
@@ -963,7 +1145,7 @@ int tagtree_enter(struct tree_context* c)
963 rc = kbd_input(searchstring, sizeof(searchstring)); 1145 rc = kbd_input(searchstring, sizeof(searchstring));
964 if (rc == -1 || !searchstring[0]) 1146 if (rc == -1 || !searchstring[0])
965 { 1147 {
966 c->dirlevel--; 1148 tagtree_exit(c);
967 return 0; 1149 return 0;
968 } 1150 }
969 1151
@@ -1024,7 +1206,8 @@ int tagtree_enter(struct tree_context* c)
1024void tagtree_exit(struct tree_context* c) 1206void tagtree_exit(struct tree_context* c)
1025{ 1207{
1026 c->dirfull = false; 1208 c->dirfull = false;
1027 c->dirlevel--; 1209 if (c->dirlevel > 0)
1210 c->dirlevel--;
1028 c->selected_item=c->selected_item_history[c->dirlevel]; 1211 c->selected_item=c->selected_item_history[c->dirlevel];
1029 gui_synclist_select_item(&tree_lists, c->selected_item); 1212 gui_synclist_select_item(&tree_lists, c->selected_item);
1030 c->currtable = c->table_history[c->dirlevel]; 1213 c->currtable = c->table_history[c->dirlevel];
diff --git a/apps/tree.c b/apps/tree.c
index 1f68a8560b..584aaa8006 100644
--- a/apps/tree.c
+++ b/apps/tree.c
@@ -532,10 +532,12 @@ static bool check_changed_id3mode(bool currmode)
532 currmode = global_settings.dirfilter == SHOW_ID3DB; 532 currmode = global_settings.dirfilter == SHOW_ID3DB;
533 if (currmode) { 533 if (currmode) {
534 curr_context=CONTEXT_ID3DB; 534 curr_context=CONTEXT_ID3DB;
535 tc.dirlevel = 0;
535 tagtree_load(&tc); 536 tagtree_load(&tc);
536 } 537 }
537 else 538 else
538 { 539 {
540 tc.dirlevel = 0;
539 curr_context=CONTEXT_TREE; 541 curr_context=CONTEXT_TREE;
540 ft_load(&tc, NULL); 542 ft_load(&tc, NULL);
541 reload_dir = true; 543 reload_dir = true;