Index: apps/tagtree.h =================================================================== --- apps/tagtree.h (revision 24214) +++ apps/tagtree.h (working copy) @@ -28,12 +28,13 @@ #define TAGNAVI_VERSION "#! rockbox/tagbrowser/2.0" #define TAGMENU_MAX_ITEMS 64 #define TAGMENU_MAX_MENUS 32 -#define TAGMENU_MAX_FMTS 32 +#define TAGMENU_MAX_FMTS 48 struct tagentry { char *name; int newtable; int extraseek; + int strip; }; bool tagtree_export(void); Index: apps/tagcache.c =================================================================== --- apps/tagcache.c (revision 24214) +++ apps/tagcache.c (working copy) @@ -928,8 +928,9 @@ str = tfe->tag_data; } } - - if (!check_against_clause(seek, str, clause[i])) + + /* Handle case where str is empty - pass in "" to allow filters to work */ + if (!check_against_clause(seek, ((str[0] == '\0') ? UNTAGGED : str), clause[i])) return false; } } @@ -958,17 +959,14 @@ } read(fd, str, tfe.tag_length); - - /* Check if entry has been deleted. */ - if (str[0] == '\0') - break; - } - - if (!check_against_clause(seek, str, clause[i])) + } + + /* Handle case where str is empty - pass in "" to allow filters to work */ + if (!check_against_clause(seek, ((str[0] == '\0') ? UNTAGGED : str), clause[i])) return false; } } - + return true; } @@ -1273,36 +1271,30 @@ } bool tagcache_search_add_clause(struct tagcache_search *tcs, - struct tagcache_search_clause *clause) + struct tagcache_search_clause *clause, + bool addfullclause) { - int i; - - if (tcs->clause_count >= TAGCACHE_MAX_CLAUSES) - { - logf("Too many clauses"); - return false; - } - - /* Check if there is already a similar filter in present (filters are - * much faster than clauses). - */ - for (i = 0; i < tcs->filter_count; i++) - { - if (tcs->filter_tag[i] == clause->tag) - return true; - } - if (!TAGCACHE_IS_NUMERIC(clause->tag) && tcs->idxfd[clause->tag] < 0) { char buf[MAX_PATH]; - snprintf(buf, sizeof buf, TAGCACHE_FILE_INDEX, clause->tag); + snprintf(buf, sizeof buf, TAGCACHE_FILE_INDEX, clause->tag); tcs->idxfd[clause->tag] = open(buf, O_RDONLY); } - - tcs->clause[tcs->clause_count] = clause; - tcs->clause_count++; - + + /* Only add the full clause if required */ + if (addfullclause) + { + if (tcs->clause_count >= TAGCACHE_MAX_CLAUSES) + { + logf("Too many clauses"); + return false; + } + + tcs->clause[tcs->clause_count] = clause; + tcs->clause_count++; + } + return true; } Index: apps/tagcache.h =================================================================== --- apps/tagcache.h (revision 24214) +++ apps/tagcache.h (working copy) @@ -50,14 +50,14 @@ #define IDX_BUF_DEPTH 64 /* Tag Cache Header version 'TCHxx'. Increment when changing internal structures. */ -#define TAGCACHE_MAGIC 0x5443480d +#define TAGCACHE_MAGIC 0x5443481d /* How much to allocate extra space for ramcache. */ #define TAGCACHE_RESERVE 32768 -/** +/** * Define how long one entry must be at least (longer -> less memory at commit). - * Must be at least 4 bytes in length for correct alignment. + * Must be at least 4 bytes in length for correct alignment. */ #define TAGFILE_ENTRY_CHUNK_LENGTH 8 @@ -75,7 +75,7 @@ /* Idle time before committing events in the command queue. */ #define TAGCACHE_COMMAND_QUEUE_COMMIT_DELAY HZ*2 -#define TAGCACHE_MAX_FILTERS 4 +#define TAGCACHE_MAX_FILTERS 6 #define TAGCACHE_MAX_CLAUSES 32 /* Tag database files. */ @@ -212,9 +212,10 @@ bool tagcache_search_add_filter(struct tagcache_search *tcs, int tag, int seek); bool tagcache_search_add_clause(struct tagcache_search *tcs, - struct tagcache_search_clause *clause); + struct tagcache_search_clause *clause, + bool addfullclause); bool tagcache_get_next(struct tagcache_search *tcs); -bool tagcache_retrieve(struct tagcache_search *tcs, int idxid, +bool tagcache_retrieve(struct tagcache_search *tcs, int idxid, int tag, char *buf, long size); void tagcache_search_finish(struct tagcache_search *tcs); long tagcache_get_numeric(const struct tagcache_search *tcs, int tag); Index: apps/tagtree.c =================================================================== --- apps/tagtree.c (revision 24214) +++ apps/tagtree.c (working copy) @@ -91,24 +91,34 @@ menu_load, }; +/* String presentation of the tags defined in tagcache.h. Must be in correct order! */ +static const char *tags_str[] = { "artist", "album", "genre", "title", + "filename", "composer", "comment", "albumartist", "grouping", "year", + "discnumber", "tracknumber", "bitrate", "length", "playcount", "rating", + "playtime", "lastplayed", "commitid", "mtime" }; + +/* String presentation of the clauses defined in tagcache.h. Must be in correct order! */ +/* static const char *clause_str[] = { "NONE", "=", "!=", ">", ">=", + "<", "<=", "~", "!~", "^", "!^", "$", "!$", "@" }; */ + /* Capacity 10 000 entries (for example 10k different artists) */ #define UNIQBUF_SIZE (64*1024) static long *uniqbuf; -#define MAX_TAGS 5 +#define MAX_TAGS 6 #define MAX_MENU_ID_SIZE 32 static bool sort_inverse; /* * "%3d. %s" autoscore title %sort = "inverse" %limit = "100" - * + * * valid = true * formatstr = "%-3d. %s" * tags[0] = tag_autoscore * tags[1] = tag_title * tag_count = 2 - * + * * limit = 100 * sort_inverse = true */ @@ -1052,7 +1062,7 @@ struct tagcache_search tcs; struct tagentry *dptr = (struct tagentry *)c->dircache; struct display_format *fmt; - int i; + int i, j; int namebufused = 0; int total_count = 0; int special_entry_count = 0; @@ -1060,7 +1070,9 @@ int tag; bool sort = false; int sort_limit; - int strip; + bool strip = false; + bool alltracks = false; + char all_tracks_fmt[MAX_PATH]; /* Show search progress straight away if the disk needs to spin up, otherwise show it after the normal 1/2 second delay */ @@ -1074,7 +1086,9 @@ if (c->currtable == ALLSUBENTRIES) { + alltracks = true; /* cache flag defining whether this is the AllTracks menu */ tag = tag_title; + sort = true; /* default the all subentries to always sort */ level--; } else @@ -1082,109 +1096,161 @@ if (!tagcache_search(&tcs, tag)) return -1; - + /* Prevent duplicate entries in the search list. */ tagcache_search_set_uniqbuf(&tcs, uniqbuf, UNIQBUF_SIZE); - + if (level || csi->clause_count[0] || TAGCACHE_IS_NUMERIC(tag)) sort = true; - + + /* Add filters for each parent level */ for (i = 0; i < level; i++) { if (TAGCACHE_IS_NUMERIC(csi->tagorder[i])) { static struct tagcache_search_clause cc; - + memset(&cc, 0, sizeof(struct tagcache_search_clause)); cc.tag = csi->tagorder[i]; cc.type = clause_is; cc.numeric = true; cc.numeric_data = csi->result_seek[i]; - tagcache_search_add_clause(&tcs, &cc); + tagcache_search_add_clause(&tcs, &cc, true); } else { - tagcache_search_add_filter(&tcs, csi->tagorder[i], + tagcache_search_add_filter(&tcs, csi->tagorder[i], csi->result_seek[i]); } } - + + /* Add clauses for each level in the menu */ for (i = 0; i <= level; i++) { - int j; - for (j = 0; j < csi->clause_count[i]; j++) - tagcache_search_add_clause(&tcs, csi->clause[i][j]); + { + tagcache_search_add_clause(&tcs, csi->clause[i][j], true); + } } - + current_offset = offset; current_entry_count = 0; c->dirfull = false; - - fmt = NULL; - for (i = 0; i < format_count; i++) + + if (alltracks) + { + /* All Tracks format functionality */ + sort = true; + all_tracks_fmt[0] = '\0'; + + /* Construct the All format name by concatenating the menu level tags */ + for (i = 0; i <= level; i++) + { + strcat(all_tracks_fmt, tags_str[csi->tagorder[i]]); + strcat(all_tracks_fmt, "."); + } + strcat(all_tracks_fmt, "All"); + + /* look for any AllTracks format matching the derived format name */ + fmt = NULL; + for (i = 0; i < format_count; i++) + { + if (!strcasecmp(formats[i]->name, all_tracks_fmt)) + { + /* add all possible clauses for all formats */ + fmt = formats[i]; + for (j = 0; j < fmt->clause_count; j++) + { + tagcache_search_add_clause(&tcs, fmt->clause[j], false); + } + } + } + } + else { - if (formats[i]->group_id == csi->format_id[level]) - fmt = formats[i]; + fmt = NULL; + for (i = 0; i < format_count; i++) + { + if (formats[i]->group_id == csi->format_id[level]) + { + /* Ensure that tags from all clauses for the fmt are loaded */ + fmt = formats[i]; + for (j = 0; j < fmt->clause_count; j++) + { + tagcache_search_add_clause(&tcs, fmt->clause[j], false); + } + } + } } + /* The assigning of the sort_inverse and sort_limit fields here is a bit arbitrary since any + track title can match any one of the associated formats, each of which can have a different + set of values for the sort inverse and sort limit attributes. */ if (fmt) { sort_inverse = fmt->sort_inverse; sort_limit = fmt->limit; - strip = fmt->strip; sort = true; } else { + /* No global format */ sort_inverse = false; sort_limit = 0; - strip = 0; } - - if (tag != tag_title && tag != tag_filename) + + if ((tag != tag_title) && (tag != tag_filename)) { if (offset == 0) { + /* Adds the option */ dptr->newtable = ALLSUBENTRIES; dptr->name = str(LANG_TAGNAVI_ALL_TRACKS); dptr++; current_entry_count++; + special_entry_count++; } if (offset <= 1) { + /* Adds the option */ dptr->newtable = NAVIBROWSE; dptr->name = str(LANG_TAGNAVI_RANDOM); dptr->extraseek = -1; dptr++; current_entry_count++; + special_entry_count++; } - special_entry_count+=2; } - + total_count += special_entry_count; - + + /* Now cycle through each entry and apply any formatting */ while (tagcache_get_next(&tcs)) { if (total_count++ < offset) continue; - - dptr->newtable = NAVIBROWSE; - if (tag == tag_title || tag == tag_filename) + + dptr->strip = 0; /* set the default strip amount for this entry */ + if ((tag == tag_title) || (tag == tag_filename)) { dptr->newtable = PLAYTRACK; dptr->extraseek = tcs.idx_id; } else + { + dptr->newtable = NAVIBROWSE; dptr->extraseek = tcs.result_seek; - + } + fmt = NULL; /* Check the format */ for (i = 0; i < format_count; i++) { - if (formats[i]->group_id != csi->format_id[level]) + /* Handle the allsubentries case as well - look for format defined in all_tracks_fmt */ + if (((formats[i]->group_id != csi->format_id[level]) && !alltracks) || + (alltracks && strcasecmp(formats[i]->name, all_tracks_fmt))) continue; - + if (tagcache_check_clauses(&tcs, formats[i]->clause, formats[i]->clause_count)) { @@ -1205,14 +1271,19 @@ tagcache_search_finish(&tcs); return 0; } + + /* Set the strip amount for this track if the title was successfully formatted */ + dptr->strip = fmt->strip; + if (!strip && (fmt->strip > 0)) + strip = true; /* cached strip flag */ } - + dptr->name = &c->name_buffer[namebufused]; if (fmt) - namebufused += strlen(buf)+1; + namebufused += strlen(buf) + 1; else namebufused += tcs.result_len; - + if (namebufused >= c->name_buffer_size) { logf("chunk mode #2: %d", current_entry_count); @@ -1269,51 +1340,58 @@ } total_count++; } - + tagcache_search_finish(&tcs); - + if (!sort && (sort_inverse || sort_limit)) { splashf(HZ*4, ID2P(LANG_SHOWDIR_BUFFER_FULL), total_count); logf("Too small dir buffer"); return 0; } - + if (sort_limit) total_count = MIN(total_count, sort_limit); - + if (strip) { + /* Now perform strip based on the format applied to each track name */ dptr = c->dircache; for (i = 0; i < total_count; i++, dptr++) { - int len = strlen(dptr->name); - - if (len < strip) - continue; - - dptr->name = &dptr->name[strip]; + /* Skip the special entries in the list */ + if (i < special_entry_count) + continue; + + if (dptr->strip > 0) + { + int len = strlen(dptr->name); + + if (len < dptr->strip) + continue; + + dptr->name = &dptr->name[dptr->strip]; + } } } - + return total_count; - } static int load_root(struct tree_context *c) { struct tagentry *dptr = (struct tagentry *)c->dircache; int i; - + tc = c; c->currtable = ROOT; if (c->dirlevel == 0) c->currextra = rootmenu; - + menu = menus[c->currextra]; if (menu == NULL) return 0; - + for (i = 0; i < menu->itemcount; i++) { dptr->name = menu->items[i]->name;