Generate A-Z menus in the tagtree

this adds a new command %byfirstletter

%byfirstletter "custom_track" "Track A to Z" "title"
^ command       ^menu name     ^menu title   ^subitem

need a better name for subitem btw..

this patch also allows us to tell when we are in the BFL menu
by checking customaction == ONPLAY_CUSTOMACTION_FIRSTLETTER
we then enable spelling of the letters in the menu
it spells Numeric too but that shouldn't matter with the upcoming voice patch

Change-Id: I59815f697a4ef84a8cb540783b620d15f6670e00
This commit is contained in:
William Wilgus 2024-09-23 03:23:36 -04:00
parent 67ad6589fb
commit 06986d27f0
4 changed files with 93 additions and 99 deletions

View file

@ -28,6 +28,7 @@
enum {
ONPLAY_NO_CUSTOMACTION,
ONPLAY_CUSTOMACTION_SHUFFLE_SONGS,
ONPLAY_CUSTOMACTION_FIRSTLETTER,
};
int onplay(char* file, int attr, int from_context, bool hotkey, int customaction);

View file

@ -30,97 +30,14 @@
#
# Define the A to Z Artist sub menu
%menu_start "custom_artist" "Artist A to Z"
"Numeric" -> canonicalartist ? canonicalartist < "A" -> album -> title = "fmt_title"
"A" -> canonicalartist ? canonicalartist ^ "A" -> album -> title = "fmt_title"
"B" -> canonicalartist ? canonicalartist ^ "B" -> album -> title = "fmt_title"
"C" -> canonicalartist ? canonicalartist ^ "C" -> album -> title = "fmt_title"
"D" -> canonicalartist ? canonicalartist ^ "D" -> album -> title = "fmt_title"
"E" -> canonicalartist ? canonicalartist ^ "E" -> album -> title = "fmt_title"
"F" -> canonicalartist ? canonicalartist ^ "F" -> album -> title = "fmt_title"
"G" -> canonicalartist ? canonicalartist ^ "G" -> album -> title = "fmt_title"
"H" -> canonicalartist ? canonicalartist ^ "H" -> album -> title = "fmt_title"
"I" -> canonicalartist ? canonicalartist ^ "I" -> album -> title = "fmt_title"
"J" -> canonicalartist ? canonicalartist ^ "J" -> album -> title = "fmt_title"
"K" -> canonicalartist ? canonicalartist ^ "K" -> album -> title = "fmt_title"
"L" -> canonicalartist ? canonicalartist ^ "L" -> album -> title = "fmt_title"
"M" -> canonicalartist ? canonicalartist ^ "M" -> album -> title = "fmt_title"
"N" -> canonicalartist ? canonicalartist ^ "N" -> album -> title = "fmt_title"
"O" -> canonicalartist ? canonicalartist ^ "O" -> album -> title = "fmt_title"
"P" -> canonicalartist ? canonicalartist ^ "P" -> album -> title = "fmt_title"
"Q" -> canonicalartist ? canonicalartist ^ "Q" -> album -> title = "fmt_title"
"R" -> canonicalartist ? canonicalartist ^ "R" -> album -> title = "fmt_title"
"S" -> canonicalartist ? canonicalartist ^ "S" -> album -> title = "fmt_title"
"T" -> canonicalartist ? canonicalartist ^ "T" -> album -> title = "fmt_title"
"U" -> canonicalartist ? canonicalartist ^ "U" -> album -> title = "fmt_title"
"V" -> canonicalartist ? canonicalartist ^ "V" -> album -> title = "fmt_title"
"W" -> canonicalartist ? canonicalartist ^ "W" -> album -> title = "fmt_title"
"X" -> canonicalartist ? canonicalartist ^ "X" -> album -> title = "fmt_title"
"Y" -> canonicalartist ? canonicalartist ^ "Y" -> album -> title = "fmt_title"
"Z" -> canonicalartist ? canonicalartist ^ "Z" -> album -> title = "fmt_title"
%byfirstletter "custom_artist" "Artist A to Z" "canonicalartist"
# Define the A to Z album sub menu
%byfirstletter "custom_album" "Album A to Z" "album"
# Define the A to Z track sub menu
%byfirstletter "custom_track" "Track A to Z" "title"
# ^ An empy line ends the menu
# Define the A to Z album sub menu
%menu_start "custom_album" "Album A to Z"
"Numeric" -> album ? album < "A" -> title = "fmt_title"
"A" -> album ? album ^ "A" -> title = "fmt_title"
"B" -> album ? album ^ "B" -> title = "fmt_title"
"C" -> album ? album ^ "C" -> title = "fmt_title"
"D" -> album ? album ^ "D" -> title = "fmt_title"
"E" -> album ? album ^ "E" -> title = "fmt_title"
"F" -> album ? album ^ "F" -> title = "fmt_title"
"G" -> album ? album ^ "G" -> title = "fmt_title"
"H" -> album ? album ^ "H" -> title = "fmt_title"
"I" -> album ? album ^ "I" -> title = "fmt_title"
"J" -> album ? album ^ "J" -> title = "fmt_title"
"K" -> album ? album ^ "K" -> title = "fmt_title"
"L" -> album ? album ^ "L" -> title = "fmt_title"
"M" -> album ? album ^ "M" -> title = "fmt_title"
"N" -> album ? album ^ "N" -> title = "fmt_title"
"O" -> album ? album ^ "O" -> title = "fmt_title"
"P" -> album ? album ^ "P" -> title = "fmt_title"
"Q" -> album ? album ^ "Q" -> title = "fmt_title"
"R" -> album ? album ^ "R" -> title = "fmt_title"
"S" -> album ? album ^ "S" -> title = "fmt_title"
"T" -> album ? album ^ "T" -> title = "fmt_title"
"U" -> album ? album ^ "U" -> title = "fmt_title"
"V" -> album ? album ^ "V" -> title = "fmt_title"
"W" -> album ? album ^ "W" -> title = "fmt_title"
"X" -> album ? album ^ "X" -> title = "fmt_title"
"Y" -> album ? album ^ "Y" -> title = "fmt_title"
"Z" -> album ? album ^ "Z" -> title = "fmt_title"
# Define the A to Z track sub menu
%menu_start "custom_track" "Track A to Z"
"Numeric" -> title ? title < "A" -> title = "fmt_title"
"A" -> title ? title ^ "A" -> title = "fmt_title"
"B" -> title ? title ^ "B" -> title = "fmt_title"
"C" -> title ? title ^ "C" -> title = "fmt_title"
"D" -> title ? title ^ "D" -> title = "fmt_title"
"E" -> title ? title ^ "E" -> title = "fmt_title"
"F" -> title ? title ^ "F" -> title = "fmt_title"
"G" -> title ? title ^ "G" -> title = "fmt_title"
"H" -> title ? title ^ "H" -> title = "fmt_title"
"I" -> title ? title ^ "I" -> title = "fmt_title"
"J" -> title ? title ^ "J" -> title = "fmt_title"
"K" -> title ? title ^ "K" -> title = "fmt_title"
"L" -> title ? title ^ "L" -> title = "fmt_title"
"M" -> title ? title ^ "M" -> title = "fmt_title"
"N" -> title ? title ^ "N" -> title = "fmt_title"
"O" -> title ? title ^ "O" -> title = "fmt_title"
"P" -> title ? title ^ "P" -> title = "fmt_title"
"Q" -> title ? title ^ "Q" -> title = "fmt_title"
"R" -> title ? title ^ "R" -> title = "fmt_title"
"S" -> title ? title ^ "S" -> title = "fmt_title"
"T" -> title ? title ^ "T" -> title = "fmt_title"
"U" -> title ? title ^ "U" -> title = "fmt_title"
"V" -> title ? title ^ "V" -> title = "fmt_title"
"W" -> title ? title ^ "W" -> title = "fmt_title"
"X" -> title ? title ^ "X" -> title = "fmt_title"
"Y" -> title ? title ^ "Y" -> title = "fmt_title"
"Z" -> title ? title ^ "Z" -> title = "fmt_title"
# Define the A to Z sub menu
%menu_start "a2z" "A to Z..."
"Artists" ==> "custom_artist"

View file

@ -112,6 +112,7 @@ enum variables {
var_include,
var_rootmenu,
var_format,
menu_byfirstletter,
menu_next,
menu_load,
menu_reload,
@ -173,11 +174,12 @@ struct display_format {
static struct display_format *formats[TAGMENU_MAX_FMTS];
static int format_count;
#define MENUENTRY_MAX_NAME 64
struct menu_entry {
char name[64];
char name[MENUENTRY_MAX_NAME];
int type;
struct search_instruction {
char name[64];
char name[MENUENTRY_MAX_NAME];
int tagorder[MAX_TAGS];
int tagorder_count;
struct tagcache_search_clause *clause[MAX_TAGS][TAGCACHE_MAX_CLAUSES];
@ -381,6 +383,7 @@ static int get_tag(int *tag)
TAG_MATCH("comment", tag_comment),
TAG_MATCH("discnum", tag_discnumber),
TAG_MATCH("%format", var_format),
TAG_MATCH("%byfirstletter", menu_byfirstletter),
TAG_MATCH("%reload", menu_reload),
TAG_MATCH("filename", tag_filename),
@ -1058,6 +1061,63 @@ int tagtree_import(void)
static bool parse_menu(const char *filename);
static bool alloc_menu_item(void)
{
/* Allocate */
if (menu->items[menu->itemcount] == NULL)
menu->items[menu->itemcount] = tagtree_alloc0(sizeof(struct menu_entry));
if (!menu->items[menu->itemcount])
{
logf("tagtree failed to allocate %s", "menu items");
return false;
}
return true;
}
static void firstletter_parse_buf(char *buf)
{
core_pin(tagtree_handle);
if (parse_search(menu->items[menu->itemcount], buf))
{
menu->items[menu->itemcount]->type = menu_byfirstletter;
menu->itemcount++;
}
core_unpin(tagtree_handle);;
}
static void build_firstletter_menu(char *buf, size_t bufsz)
{
const char *subitem = buf;
size_t l = strlen(buf) + 1;
buf+=l;
bufsz-=l;
const char *showalbum = "";
const char * const fmt ="\"%s\" -> %s ? %s %c \"%c\" -> %s title = \"fmt_title\"";
if (!alloc_menu_item())
return;
if (strcasestr(subitem, "artist") != NULL)
showalbum = "album ->"; /* album subitem for canonicalartist */
/* Numeric ex: "Numeric" -> album ? album < "A" -> title = "fmt_title" */
snprintf(buf, bufsz, fmt,
str(LANG_DISPLAY_NUMERIC), subitem, subitem,'<', 'A', showalbum);
firstletter_parse_buf(buf);
for (int i = 0; i < 26; i++)
{
if (!alloc_menu_item())
return;
snprintf(buf, bufsz, fmt, "#", subitem, subitem,'^', 'A' + i, showalbum);
buf[1] = 'A' + i; /* overwrite the placeholder # with the current letter */
/* ex: "A" -> title ? title ^ "A" -> title = "fmt_title" */
firstletter_parse_buf(buf);
}
}
static int parse_line(int n, char *buf, void *parameters)
{
char data[256];
@ -1127,7 +1187,7 @@ static int parse_line(int n, char *buf, void *parameters)
logf("Load menu fail: %s", data);
}
break;
case menu_byfirstletter: /* Fallthrough */
case var_menu_start:
if (menu_count >= TAGMENU_MAX_MENUS)
{
@ -1169,6 +1229,19 @@ static int parse_line(int n, char *buf, void *parameters)
return 0;
}
logf("menu: %s", menu->title);
if (variable == menu_byfirstletter)
{
if (get_token_str(data, sizeof(data)) < 0)
{
logf("%%firstletter_menu has no subitem"); /*artist,album*/
return 0;
}
logf("A-Z Menu subitem: %s", data);
read_menu = false;
build_firstletter_menu(data, sizeof(data));
break;
}
read_menu = true;
break;
@ -1202,14 +1275,8 @@ static int parse_line(int n, char *buf, void *parameters)
return 0;
}
/* Allocate */
if (menu->items[menu->itemcount] == NULL)
menu->items[menu->itemcount] = tagtree_alloc0(sizeof(struct menu_entry));
if (!menu->items[menu->itemcount])
{
logf("tagtree failed to allocate %s", "menu items");
if (!alloc_menu_item())
return -2;
}
core_pin(tagtree_handle);
if (parse_search(menu->items[menu->itemcount], buf))
menu->itemcount++;
@ -1449,6 +1516,7 @@ static void tcs_get_basename(struct tagcache_search *tcs, bool is_basename)
static int retrieve_entries(struct tree_context *c, int offset, bool init)
{
logf( "%s", __func__);
char tcs_buf[TAGCACHE_BUFSZ];
const long tcs_bufsz = sizeof(tcs_buf);
struct tagcache_search tcs;
@ -1818,6 +1886,12 @@ static int load_root(struct tree_context *c)
dptr->extraseek = i;
dptr->customaction = ONPLAY_CUSTOMACTION_SHUFFLE_SONGS;
break;
case menu_byfirstletter:
dptr->newtable = TABLE_NAVIBROWSE;
dptr->extraseek = i;
dptr->customaction = ONPLAY_CUSTOMACTION_FIRSTLETTER;
break;
}
dptr++;

View file

@ -205,6 +205,7 @@ static int tree_voice_cb(int selected_item, void * data)
struct tree_context * local_tc=(struct tree_context *)data;
char *name;
int attr=0;
int customaction = ONPLAY_NO_CUSTOMACTION;
#ifdef HAVE_TAGCACHE
bool id3db = *(local_tc->dirfilter) == SHOW_ID3DB;
char buf[AVERAGE_FILENAME_LENGTH*2];
@ -213,6 +214,7 @@ static int tree_voice_cb(int selected_item, void * data)
{
attr = tagtree_get_attr(local_tc);
name = tagtree_get_entry_name(local_tc, selected_item, buf, sizeof(buf));
customaction = tagtree_get_custom_action(local_tc);
}
else
#endif
@ -245,7 +247,7 @@ static int tree_voice_cb(int selected_item, void * data)
did_clip = false;
}
}
bool spell_name = false;
bool spell_name = (customaction == ONPLAY_CUSTOMACTION_FIRSTLETTER);
if(!did_clip)
{
/* say the number or spell if required or as a fallback */