summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
Diffstat (limited to 'apps')
-rw-r--r--apps/gui/wps.c4
-rw-r--r--apps/lang/english.lang14
-rw-r--r--apps/lang/francais.lang14
-rw-r--r--apps/onplay.c13
-rw-r--r--apps/onplay.h7
-rw-r--r--apps/playlist_viewer.c2
-rw-r--r--apps/settings_list.c8
-rw-r--r--apps/tagnavi.config15
-rw-r--r--apps/tagtree.c228
-rw-r--r--apps/tagtree.h1
-rw-r--r--apps/tree.c27
-rw-r--r--apps/tree.h3
12 files changed, 248 insertions, 88 deletions
diff --git a/apps/gui/wps.c b/apps/gui/wps.c
index 0de805bd02..82b0394a53 100644
--- a/apps/gui/wps.c
+++ b/apps/gui/wps.c
@@ -806,7 +806,7 @@ long gui_wps_show(void)
806 theme_enabled = false; 806 theme_enabled = false;
807 gwps_leave_wps(theme_enabled); 807 gwps_leave_wps(theme_enabled);
808 onplay(state->id3->path, 808 onplay(state->id3->path,
809 FILE_ATTR_AUDIO, CONTEXT_WPS, hotkey); 809 FILE_ATTR_AUDIO, CONTEXT_WPS, hotkey, ONPLAY_NO_CUSTOMACTION);
810 if (!audio_status()) 810 if (!audio_status())
811 { 811 {
812 /* re-enable theme since we're returning to SBS */ 812 /* re-enable theme since we're returning to SBS */
@@ -823,7 +823,7 @@ long gui_wps_show(void)
823 { 823 {
824 gwps_leave_wps(true); 824 gwps_leave_wps(true);
825 int retval = onplay(state->id3->path, 825 int retval = onplay(state->id3->path,
826 FILE_ATTR_AUDIO, CONTEXT_WPS, hotkey); 826 FILE_ATTR_AUDIO, CONTEXT_WPS, hotkey, ONPLAY_NO_CUSTOMACTION);
827 /* if music is stopped in the context menu we want to exit the wps */ 827 /* if music is stopped in the context menu we want to exit the wps */
828 if (retval == ONPLAY_MAINMENU 828 if (retval == ONPLAY_MAINMENU
829 || !audio_status()) 829 || !audio_status())
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index b0c7aed8f7..348f339239 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -2054,6 +2054,20 @@
2054 </voice> 2054 </voice>
2055</phrase> 2055</phrase>
2056<phrase> 2056<phrase>
2057 id: LANG_RANDOM_SHUFFLE_RANDOM_SELECTIVE_SONGS_SUMMARY
2058 desc: a summary splash screen that appear on the database browser when you try to create a playlist from the database browser that exceeds your system limit
2059 user: core
2060 <source>
2061 *: "Selection too big, %d random tracks will be picked from it"
2062 </source>
2063 <dest>
2064 *: "Selection too big, %d random tracks will be picked from it"
2065 </dest>
2066 <voice>
2067 *: "Selection too big, fewer random tracks will be picked from it"
2068 </voice>
2069</phrase>
2070<phrase>
2057 id: LANG_TAGCACHE_RAM 2071 id: LANG_TAGCACHE_RAM
2058 desc: in tag cache settings 2072 desc: in tag cache settings
2059 user: core 2073 user: core
diff --git a/apps/lang/francais.lang b/apps/lang/francais.lang
index ce907372b0..518b48fb96 100644
--- a/apps/lang/francais.lang
+++ b/apps/lang/francais.lang
@@ -2028,6 +2028,20 @@
2028 </voice> 2028 </voice>
2029</phrase> 2029</phrase>
2030<phrase> 2030<phrase>
2031 id: LANG_RANDOM_SHUFFLE_RANDOM_SELECTIVE_SONGS_SUMMARY
2032 desc: a summary splash screen that appear on the database browser when you try to create a playlist from the database browser that exceeds your system limit
2033 user: core
2034 <source>
2035 *: "Selection too big, %d random tracks will be picked from it"
2036 </source>
2037 <dest>
2038 *: "Selection trop grande, %d pistes seront sélectionnées aléatoirement depuis celle-ci"
2039 </dest>
2040 <voice>
2041 *: "Selection trop grande, donc des pistes seront sélectionnées aléatoirement depuis celle-ci"
2042 </voice>
2043</phrase>
2044<phrase>
2031 id: LANG_TAGCACHE_RAM 2045 id: LANG_TAGCACHE_RAM
2032 desc: in tag cache settings 2046 desc: in tag cache settings
2033 user: core 2047 user: core
diff --git a/apps/onplay.c b/apps/onplay.c
index 4f748204df..045af275bc 100644
--- a/apps/onplay.c
+++ b/apps/onplay.c
@@ -302,7 +302,7 @@ static int add_to_playlist(void* arg)
302 302
303 /* warn if replacing the playlist */ 303 /* warn if replacing the playlist */
304 if (new_playlist && !warn_on_pl_erase()) 304 if (new_playlist && !warn_on_pl_erase())
305 return 0; 305 return 1;
306 306
307 splash(0, ID2P(LANG_WAIT)); 307 splash(0, ID2P(LANG_WAIT));
308 308
@@ -340,7 +340,7 @@ static int add_to_playlist(void* arg)
340 } 340 }
341 341
342 playlist_set_modified(NULL, true); 342 playlist_set_modified(NULL, true);
343 return false; 343 return 0;
344} 344}
345 345
346static bool view_playlist(void) 346static bool view_playlist(void)
@@ -1255,7 +1255,7 @@ static int execute_hotkey(bool is_wps)
1255} 1255}
1256#endif /* HOTKEY */ 1256#endif /* HOTKEY */
1257 1257
1258int onplay(char* file, int attr, int from_context, bool hotkey) 1258int onplay(char* file, int attr, int from_context, bool hotkey, int customaction)
1259{ 1259{
1260 const struct menu_item_ex *menu; 1260 const struct menu_item_ex *menu;
1261 onplay_result = ONPLAY_OK; 1261 onplay_result = ONPLAY_OK;
@@ -1294,6 +1294,13 @@ int onplay(char* file, int attr, int from_context, bool hotkey)
1294#else 1294#else
1295 (void)hotkey; 1295 (void)hotkey;
1296#endif 1296#endif
1297 if (customaction == ONPLAY_CUSTOMACTION_SHUFFLE_SONGS) {
1298 int returnCode = add_to_playlist(&addtopl_replace_shuffled);
1299 if (returnCode == 1)
1300 // User did not want to erase his current playlist, so let's show again the database main menu
1301 return ONPLAY_RELOAD_DIR;
1302 return ONPLAY_START_PLAY;
1303 }
1297 1304
1298 push_current_activity(ACTIVITY_CONTEXTMENU); 1305 push_current_activity(ACTIVITY_CONTEXTMENU);
1299 if (from_context == CONTEXT_WPS) 1306 if (from_context == CONTEXT_WPS)
diff --git a/apps/onplay.h b/apps/onplay.h
index 74dc045db3..03861e9cf6 100644
--- a/apps/onplay.h
+++ b/apps/onplay.h
@@ -25,7 +25,12 @@
25#include "menu.h" 25#include "menu.h"
26#endif 26#endif
27 27
28int onplay(char* file, int attr, int from_context, bool hotkey); 28enum {
29 ONPLAY_NO_CUSTOMACTION,
30 ONPLAY_CUSTOMACTION_SHUFFLE_SONGS,
31};
32
33int onplay(char* file, int attr, int from_context, bool hotkey, int customaction);
29int get_onplay_context(void); 34int get_onplay_context(void);
30 35
31enum { 36enum {
diff --git a/apps/playlist_viewer.c b/apps/playlist_viewer.c
index 70072f59f5..5bf547a3fc 100644
--- a/apps/playlist_viewer.c
+++ b/apps/playlist_viewer.c
@@ -1107,7 +1107,7 @@ enum playlist_viewer_result playlist_viewer_ex(const char* filename,
1107 } 1107 }
1108 } 1108 }
1109 else 1109 else
1110 onplay(current_track->name, FILE_ATTR_AUDIO, CONTEXT_STD, true); 1110 onplay(current_track->name, FILE_ATTR_AUDIO, CONTEXT_STD, true, ONPLAY_NO_CUSTOMACTION);
1111 break; 1111 break;
1112 } 1112 }
1113#endif /* HAVE_HOTKEY */ 1113#endif /* HAVE_HOTKEY */
diff --git a/apps/settings_list.c b/apps/settings_list.c
index 8ec434bd9b..3b29703fe9 100644
--- a/apps/settings_list.c
+++ b/apps/settings_list.c
@@ -1120,7 +1120,11 @@ const struct settings_list settings[] = {
1120 SYSTEM_SETTING(NVRAM(4), topruntime, 0), 1120 SYSTEM_SETTING(NVRAM(4), topruntime, 0),
1121 INT_SETTING(F_BANFROMQS, max_files_in_playlist, 1121 INT_SETTING(F_BANFROMQS, max_files_in_playlist,
1122 LANG_MAX_FILES_IN_PLAYLIST, 1122 LANG_MAX_FILES_IN_PLAYLIST,
1123#if MEMORYSIZE > 1 1123#if CONFIG_CPU == PP5002 || CONFIG_CPU == PP5020 || CONFIG_CPU == PP5022
1124 /** Slow CPU benefits greatly from building smaller playlists
1125 On the iPod Mini 2nd gen, creating a playlist of 2000 entries takes around 10 seconds */
1126 2000,
1127#elif MEMORYSIZE > 1
1124 10000, 1128 10000,
1125#else 1129#else
1126 400, 1130 400,
@@ -1854,7 +1858,7 @@ const struct settings_list settings[] = {
1854 true, "warn when erasing dynamic playlist",NULL), 1858 true, "warn when erasing dynamic playlist",NULL),
1855 OFFON_SETTING(0, keep_current_track_on_replace_playlist, LANG_KEEP_CURRENT_TRACK_ON_REPLACE, 1859 OFFON_SETTING(0, keep_current_track_on_replace_playlist, LANG_KEEP_CURRENT_TRACK_ON_REPLACE,
1856 true, "keep current track when replacing playlist",NULL), 1860 true, "keep current track when replacing playlist",NULL),
1857 OFFON_SETTING(0, show_shuffled_adding_options, LANG_SHOW_SHUFFLED_ADDING_OPTIONS, false, 1861 OFFON_SETTING(0, show_shuffled_adding_options, LANG_SHOW_SHUFFLED_ADDING_OPTIONS, true,
1858 "show shuffled adding options", NULL), 1862 "show shuffled adding options", NULL),
1859 CHOICE_SETTING(0, show_queue_options, LANG_SHOW_QUEUE_OPTIONS, 0, 1863 CHOICE_SETTING(0, show_queue_options, LANG_SHOW_QUEUE_OPTIONS, 0,
1860 "show queue options", "off,on,in submenu", 1864 "show queue options", "off,on,in submenu",
diff --git a/apps/tagnavi.config b/apps/tagnavi.config
index 6eda05ec44..6baa6e1328 100644
--- a/apps/tagnavi.config
+++ b/apps/tagnavi.config
@@ -176,20 +176,21 @@
176 176
177# Define the title of the main menu 177# Define the title of the main menu
178%menu_start "main" "Database" 178%menu_start "main" "Database"
179"Artist" -> canonicalartist -> album -> title = "fmt_title"
180"Album Artist" -> albumartist -> album -> title = "fmt_title" 179"Album Artist" -> albumartist -> album -> title = "fmt_title"
180"Artist" -> canonicalartist -> album -> title = "fmt_title"
181"Album" -> album -> title = "fmt_title" 181"Album" -> album -> title = "fmt_title"
182"Genre" -> genre -> canonicalartist -> album -> title = "fmt_title" 182"Genre" -> genre -> canonicalartist -> album -> title = "fmt_title"
183"Year" -> year ? year > "0" -> canonicalartist -> album -> title = "fmt_title"
183"Composer" -> composer -> album -> title = "fmt_title" 184"Composer" -> composer -> album -> title = "fmt_title"
185"A to Z" ==> "a2z"
184"Track" ==> "track" 186"Track" ==> "track"
185"Year" -> year ? year > "0" -> canonicalartist -> album -> title = "fmt_title" 187"Shuffle Songs" ~> title = "fmt_title"
188"Search" ==> "search"
186"User Rating" -> rating -> title = "fmt_title" 189"User Rating" -> rating -> title = "fmt_title"
187"Recently Added" -> album ? entryage < "4" & commitid > "0" -> title = "fmt_title" 190"Recently Added" -> album ? entryage < "4" & commitid > "0" -> title = "fmt_title"
188"A to Z..." ==> "a2z" 191"History" ==> "runtime"
189"History..." ==> "runtime" 192"Same as current" ==> "same"
190"Same as current..." ==> "same" 193"Custom view" ==> "custom"
191"Search..." ==> "search"
192"Custom view..." ==> "custom"
193 194
194# And finally set main menu as our root menu 195# And finally set main menu as our root menu
195%root_menu "main" 196%root_menu "main"
diff --git a/apps/tagtree.c b/apps/tagtree.c
index d2e27a3e58..4a0bff32bd 100644
--- a/apps/tagtree.c
+++ b/apps/tagtree.c
@@ -56,6 +56,7 @@
56#include "playback.h" 56#include "playback.h"
57#include "strnatcmp.h" 57#include "strnatcmp.h"
58#include "panic.h" 58#include "panic.h"
59#include "onplay.h"
59 60
60#define str_or_empty(x) (x ? x : "(NULL)") 61#define str_or_empty(x) (x ? x : "(NULL)")
61 62
@@ -71,6 +72,7 @@ struct tagentry {
71 char* name; 72 char* name;
72 int newtable; 73 int newtable;
73 int extraseek; 74 int extraseek;
75 int customaction;
74}; 76};
75 77
76static struct tagentry* tagtree_get_entry(struct tree_context *c, int id); 78static struct tagentry* tagtree_get_entry(struct tree_context *c, int id);
@@ -78,10 +80,10 @@ static struct tagentry* tagtree_get_entry(struct tree_context *c, int id);
78#define SEARCHSTR_SIZE 256 80#define SEARCHSTR_SIZE 256
79 81
80enum table { 82enum table {
81 ROOT = 1, 83 TABLE_ROOT = 1,
82 NAVIBROWSE, 84 TABLE_NAVIBROWSE,
83 ALLSUBENTRIES, 85 TABLE_ALLSUBENTRIES,
84 PLAYTRACK, 86 TABLE_PLAYTRACK,
85}; 87};
86 88
87static const struct id3_to_search_mapping { 89static const struct id3_to_search_mapping {
@@ -108,12 +110,21 @@ enum variables {
108 menu_next, 110 menu_next,
109 menu_load, 111 menu_load,
110 menu_reload, 112 menu_reload,
113 menu_shuffle_songs,
111}; 114};
112 115
113/* Capacity 10 000 entries (for example 10k different artists) */ 116/* Capacity 10 000 entries (for example 10k different artists) */
114#define UNIQBUF_SIZE (64*1024) 117#define UNIQBUF_SIZE (64*1024)
115static uint32_t uniqbuf[UNIQBUF_SIZE / sizeof(uint32_t)]; 118static uint32_t uniqbuf[UNIQBUF_SIZE / sizeof(uint32_t)];
116 119
120#if MEMORYSIZE > 2
121 #define INSERT_ALL_PLAYLIST_MAX_SEGMENT_SIZE (1024)
122#else
123 /* Lower quality randomness for low-ram devices using smaller segments */
124 #define INSERT_ALL_PLAYLIST_MAX_SEGMENT_SIZE (128)
125#endif
126static bool selective_random_playlist_indexes[INSERT_ALL_PLAYLIST_MAX_SEGMENT_SIZE];
127
117#define MAX_TAGS 5 128#define MAX_TAGS 5
118#define MAX_MENU_ID_SIZE 32 129#define MAX_MENU_ID_SIZE 32
119 130
@@ -338,6 +349,7 @@ static int get_tag(int *tag)
338 TAG_MATCH("Pm", tag_virt_playtime_min), 349 TAG_MATCH("Pm", tag_virt_playtime_min),
339 TAG_MATCH("Ps", tag_virt_playtime_sec), 350 TAG_MATCH("Ps", tag_virt_playtime_sec),
340 TAG_MATCH("->", menu_next), 351 TAG_MATCH("->", menu_next),
352 TAG_MATCH("~>", menu_shuffle_songs),
341 353
342 TAG_MATCH("==>", menu_load), 354 TAG_MATCH("==>", menu_load),
343 355
@@ -820,7 +832,7 @@ static bool parse_search(struct menu_entry *entry, const char *str)
820 return true; 832 return true;
821 } 833 }
822 834
823 if (entry->type != menu_next) 835 if (entry->type != menu_next && entry->type != menu_shuffle_songs)
824 return false; 836 return false;
825 837
826 while (inst->tagorder_count < MAX_TAGS) 838 while (inst->tagorder_count < MAX_TAGS)
@@ -847,7 +859,7 @@ static bool parse_search(struct menu_entry *entry, const char *str)
847 859
848 inst->tagorder_count++; 860 inst->tagorder_count++;
849 861
850 if (get_tag(&type) <= 0 || type != menu_next) 862 if (get_tag(&type) <= 0 || (type != menu_next && type != menu_shuffle_songs))
851 break; 863 break;
852 } 864 }
853 865
@@ -1245,6 +1257,7 @@ static void tagtree_unload(struct tree_context *c)
1245 dptr->name = NULL; 1257 dptr->name = NULL;
1246 dptr->newtable = 0; 1258 dptr->newtable = 0;
1247 dptr->extraseek = 0; 1259 dptr->extraseek = 0;
1260 dptr->customaction = ONPLAY_NO_CUSTOMACTION;
1248 dptr++; 1261 dptr++;
1249 } 1262 }
1250 } 1263 }
@@ -1454,7 +1467,7 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
1454#endif 1467#endif
1455 , 0, 0, 0); 1468 , 0, 0, 0);
1456 1469
1457 if (c->currtable == ALLSUBENTRIES) 1470 if (c->currtable == TABLE_ALLSUBENTRIES)
1458 { 1471 {
1459 tag = tag_title; 1472 tag = tag_title;
1460 level--; 1473 level--;
@@ -1544,17 +1557,19 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
1544 { 1557 {
1545 if (offset == 0) 1558 if (offset == 0)
1546 { 1559 {
1547 dptr->newtable = ALLSUBENTRIES; 1560 dptr->newtable = TABLE_ALLSUBENTRIES;
1548 dptr->name = str(LANG_TAGNAVI_ALL_TRACKS); 1561 dptr->name = str(LANG_TAGNAVI_ALL_TRACKS);
1562 dptr->customaction = ONPLAY_NO_CUSTOMACTION;
1549 dptr++; 1563 dptr++;
1550 current_entry_count++; 1564 current_entry_count++;
1551 special_entry_count++; 1565 special_entry_count++;
1552 } 1566 }
1553 if (offset <= 1) 1567 if (offset <= 1)
1554 { 1568 {
1555 dptr->newtable = NAVIBROWSE; 1569 dptr->newtable = TABLE_NAVIBROWSE;
1556 dptr->name = str(LANG_TAGNAVI_RANDOM); 1570 dptr->name = str(LANG_TAGNAVI_RANDOM);
1557 dptr->extraseek = -1; 1571 dptr->extraseek = -1;
1572 dptr->customaction = ONPLAY_NO_CUSTOMACTION;
1558 dptr++; 1573 dptr++;
1559 current_entry_count++; 1574 current_entry_count++;
1560 special_entry_count++; 1575 special_entry_count++;
@@ -1568,14 +1583,15 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
1568 if (total_count++ < offset) 1583 if (total_count++ < offset)
1569 continue; 1584 continue;
1570 1585
1571 dptr->newtable = NAVIBROWSE; 1586 dptr->newtable = TABLE_NAVIBROWSE;
1572 if (tag == tag_title || tag == tag_filename) 1587 if (tag == tag_title || tag == tag_filename)
1573 { 1588 {
1574 dptr->newtable = PLAYTRACK; 1589 dptr->newtable = TABLE_PLAYTRACK;
1575 dptr->extraseek = tcs.idx_id; 1590 dptr->extraseek = tcs.idx_id;
1576 } 1591 }
1577 else 1592 else
1578 dptr->extraseek = tcs.result_seek; 1593 dptr->extraseek = tcs.result_seek;
1594 dptr->customaction = ONPLAY_NO_CUSTOMACTION;
1579 1595
1580 fmt = NULL; 1596 fmt = NULL;
1581 /* Check the format */ 1597 /* Check the format */
@@ -1758,7 +1774,7 @@ static int load_root(struct tree_context *c)
1758 int i; 1774 int i;
1759 1775
1760 tc = c; 1776 tc = c;
1761 c->currtable = ROOT; 1777 c->currtable = TABLE_ROOT;
1762 if (c->dirlevel == 0) 1778 if (c->dirlevel == 0)
1763 c->currextra = rootmenu; 1779 c->currextra = rootmenu;
1764 1780
@@ -1775,13 +1791,21 @@ static int load_root(struct tree_context *c)
1775 switch (menu->items[i]->type) 1791 switch (menu->items[i]->type)
1776 { 1792 {
1777 case menu_next: 1793 case menu_next:
1778 dptr->newtable = NAVIBROWSE; 1794 dptr->newtable = TABLE_NAVIBROWSE;
1779 dptr->extraseek = i; 1795 dptr->extraseek = i;
1796 dptr->customaction = ONPLAY_NO_CUSTOMACTION;
1780 break; 1797 break;
1781 1798
1782 case menu_load: 1799 case menu_load:
1783 dptr->newtable = ROOT; 1800 dptr->newtable = TABLE_ROOT;
1784 dptr->extraseek = menu->items[i]->link; 1801 dptr->extraseek = menu->items[i]->link;
1802 dptr->customaction = ONPLAY_NO_CUSTOMACTION;
1803 break;
1804
1805 case menu_shuffle_songs:
1806 dptr->newtable = TABLE_NAVIBROWSE;
1807 dptr->extraseek = i;
1808 dptr->customaction = ONPLAY_CUSTOMACTION_SHUFFLE_SONGS;
1785 break; 1809 break;
1786 } 1810 }
1787 1811
@@ -1804,19 +1828,19 @@ int tagtree_load(struct tree_context* c)
1804 if (!table) 1828 if (!table)
1805 { 1829 {
1806 c->dirfull = false; 1830 c->dirfull = false;
1807 table = ROOT; 1831 table = TABLE_ROOT;
1808 c->currtable = table; 1832 c->currtable = table;
1809 c->currextra = rootmenu; 1833 c->currextra = rootmenu;
1810 } 1834 }
1811 1835
1812 switch (table) 1836 switch (table)
1813 { 1837 {
1814 case ROOT: 1838 case TABLE_ROOT:
1815 count = load_root(c); 1839 count = load_root(c);
1816 break; 1840 break;
1817 1841
1818 case ALLSUBENTRIES: 1842 case TABLE_ALLSUBENTRIES:
1819 case NAVIBROWSE: 1843 case TABLE_NAVIBROWSE:
1820 logf("navibrowse..."); 1844 logf("navibrowse...");
1821 cpu_boost(true); 1845 cpu_boost(true);
1822 count = retrieve_entries(c, 0, true); 1846 count = retrieve_entries(c, 0, true);
@@ -1921,16 +1945,16 @@ int tagtree_enter(struct tree_context* c, bool is_visible)
1921 core_pin(tagtree_handle); 1945 core_pin(tagtree_handle);
1922 1946
1923 switch (c->currtable) { 1947 switch (c->currtable) {
1924 case ROOT: 1948 case TABLE_ROOT:
1925 c->currextra = newextra; 1949 c->currextra = newextra;
1926 1950
1927 if (newextra == ROOT) 1951 if (newextra == TABLE_ROOT)
1928 { 1952 {
1929 menu = menus[seek]; 1953 menu = menus[seek];
1930 c->currextra = seek; 1954 c->currextra = seek;
1931 } 1955 }
1932 1956
1933 else if (newextra == NAVIBROWSE) 1957 else if (newextra == TABLE_NAVIBROWSE)
1934 { 1958 {
1935 int i, j; 1959 int i, j;
1936 1960
@@ -2005,9 +2029,9 @@ int tagtree_enter(struct tree_context* c, bool is_visible)
2005 2029
2006 break; 2030 break;
2007 2031
2008 case NAVIBROWSE: 2032 case TABLE_NAVIBROWSE:
2009 case ALLSUBENTRIES: 2033 case TABLE_ALLSUBENTRIES:
2010 if (newextra == PLAYTRACK) 2034 if (newextra == TABLE_PLAYTRACK)
2011 { 2035 {
2012 adjust_selection = false; 2036 adjust_selection = false;
2013 2037
@@ -2102,13 +2126,46 @@ int tagtree_get_filename(struct tree_context* c, char *buf, int buflen)
2102 return 0; 2126 return 0;
2103} 2127}
2104 2128
2129int tagtree_get_custom_action(struct tree_context* c)
2130{
2131 return tagtree_get_entry(c, c->selected_item)->customaction;
2132}
2133
2134static void swap_array_bool(bool *a, bool *b) {
2135 bool temp = *a;
2136 *a = *b;
2137 *b = temp;
2138}
2139
2140/**
2141 * Randomly shuffle an array using the Fisher-Yates algorithm : https://en.wikipedia.org/wiki/Random_permutation
2142 * This algorithm has a linear complexity. Don't forget to srand before call to use it with a relevant seed.
2143 */
2144static void shuffle_bool_array(bool array[], int size) {
2145 for (int i = size - 1; i > 0; i--) {
2146 int j = rand() % (i + 1);
2147 swap_array_bool(&array[i], &array[j]);
2148 }
2149}
2150
2151static bool fill_selective_random_playlist_indexes(int current_segment_n, int current_segment_max_available_space) {
2152 if (current_segment_n == 0 || current_segment_max_available_space == 0)
2153 return false;
2154 if (current_segment_max_available_space > current_segment_n)
2155 current_segment_max_available_space = current_segment_n;
2156 for (int i = 0; i < current_segment_n; i++)
2157 selective_random_playlist_indexes[i] = i < current_segment_max_available_space;
2158 srand(current_tick);
2159 shuffle_bool_array(selective_random_playlist_indexes, current_segment_n);
2160 return true;
2161}
2105 2162
2106static bool insert_all_playlist(struct tree_context *c, 2163static bool insert_all_playlist(struct tree_context *c,
2107 const char* playlist, bool new_playlist, 2164 const char* playlist, bool new_playlist,
2108 int position, bool queue) 2165 int position, bool queue)
2109{ 2166{
2110 struct tagcache_search tcs; 2167 struct tagcache_search tcs;
2111 int i, n; 2168 int n;
2112 int fd = -1; 2169 int fd = -1;
2113 unsigned long last_tick; 2170 unsigned long last_tick;
2114 char buf[MAX_PATH]; 2171 char buf[MAX_PATH];
@@ -2144,44 +2201,77 @@ static bool insert_all_playlist(struct tree_context *c,
2144 return false; 2201 return false;
2145 } 2202 }
2146 } 2203 }
2147
2148 last_tick = current_tick + HZ/2; /* Show splash after 0.5 seconds have passed */ 2204 last_tick = current_tick + HZ/2; /* Show splash after 0.5 seconds have passed */
2149 splash_progress_set_delay(HZ / 2); /* wait 1/2 sec before progress */ 2205 splash_progress_set_delay(HZ / 2); /* wait 1/2 sec before progress */
2150 n = c->filesindir; 2206 n = c->filesindir;
2151 for (i = 0; i < n; i++) 2207 int segment_size = INSERT_ALL_PLAYLIST_MAX_SEGMENT_SIZE;
2152 { 2208 int segments_count = n / segment_size;
2153 2209 int leftovers_segment_size = n % segment_size;
2154 splash_progress(i, n, "%s (%s)", str(LANG_WAIT), str(LANG_OFF_ABORT)); 2210 bool fill_randomly = false;
2155 if (TIME_AFTER(current_tick, last_tick + HZ/4)) 2211 if (playlist == NULL) {
2156 { 2212 bool will_exceed = n > playlist_get_current()->max_playlist_size;
2157 if (action_userabort(TIMEOUT_NOBLOCK)) 2213 fill_randomly = will_exceed;
2158 break; 2214 }
2159 last_tick = current_tick; 2215 if (leftovers_segment_size > 0 && fill_randomly) {
2216 // We need to re-balance the segments so the randomness will be coherent and balanced the same through all segments
2217 while (leftovers_segment_size + segments_count < segment_size) {
2218 segment_size--; // -1 to all other segments
2219 leftovers_segment_size += segments_count;
2160 } 2220 }
2161 2221 }
2162 if (!tagcache_retrieve(&tcs, tagtree_get_entry(c, i)->extraseek, 2222 if (leftovers_segment_size > 0)
2163 tcs.type, buf, sizeof buf)) 2223 segments_count += 1;
2164 { 2224 int max_available_space = playlist_get_current()->max_playlist_size - playlist_get_current()->amount;
2165 continue; 2225 int max_available_space_per_segment = max_available_space / segments_count;
2226 if (fill_randomly) {
2227 talk_id(LANG_RANDOM_SHUFFLE_RANDOM_SELECTIVE_SONGS_SUMMARY, true);
2228 splashf(HZ * 3, str(LANG_RANDOM_SHUFFLE_RANDOM_SELECTIVE_SONGS_SUMMARY), max_available_space_per_segment * segments_count);
2229 //splashf(HZ * 5, "sz=%d lsz=%d sc=%d rcps=%d", segment_size, leftovers_segment_size, segments_count, max_available_space_per_segment);
2230 }
2231 for (int i = 0; i < segments_count; i++) {
2232 bool is_leftovers_segment = leftovers_segment_size > 0 && i + 1 >= segments_count;
2233 if (fill_randomly) {
2234 if (is_leftovers_segment)
2235 fill_randomly = fill_selective_random_playlist_indexes(leftovers_segment_size, max_available_space_per_segment);
2236 else
2237 fill_randomly = fill_selective_random_playlist_indexes(segment_size, max_available_space_per_segment);
2166 } 2238 }
2167 2239 bool exit_loop_now = false;
2168 if (playlist == NULL) 2240 int cur_segment_start = i * segment_size;
2169 { 2241 int cur_segment_end;
2170 if (playlist_insert_track(NULL, buf, position, queue, false) < 0) 2242 if (is_leftovers_segment)
2171 { 2243 cur_segment_end = cur_segment_start + leftovers_segment_size;
2172 logf("playlist_insert_track failed"); 2244 else
2173 break; 2245 cur_segment_end = cur_segment_start + segment_size;
2246 for (int j = cur_segment_start; j < cur_segment_end && !exit_loop_now; j++) {
2247 if (fill_randomly && !selective_random_playlist_indexes[j % segment_size])
2248 continue;
2249 splash_progress(j, n, "%s (%s)", str(LANG_WAIT), str(LANG_OFF_ABORT));
2250 if (TIME_AFTER(current_tick, last_tick + HZ/4)) {
2251 if (action_userabort(TIMEOUT_NOBLOCK)) {
2252 exit_loop_now = true;
2253 break;
2254 }
2255 last_tick = current_tick;
2174 } 2256 }
2175 } 2257 if (!tagcache_retrieve(&tcs, tagtree_get_entry(c, j)->extraseek, tcs.type, buf, sizeof buf))
2176 else if (fdprintf(fd, "%s\n", buf) <= 0) 2258 continue;
2259 if (playlist == NULL) {
2260 if (playlist_insert_track(NULL, buf, position, queue, false) < 0) {
2261 logf("playlist_insert_track failed");
2262 exit_loop_now = true;
2263 break;
2264 }
2265 } else if (fdprintf(fd, "%s\n", buf) <= 0) {
2266 exit_loop_now = true;
2177 break; 2267 break;
2178 2268 }
2179 yield(); 2269 yield();
2180 2270 if (playlist == NULL && position == PLAYLIST_INSERT_FIRST)
2181 if (playlist == NULL && position == PLAYLIST_INSERT_FIRST) 2271 position = PLAYLIST_INSERT;
2182 {
2183 position = PLAYLIST_INSERT;
2184 } 2272 }
2273 if (exit_loop_now)
2274 break;
2185 } 2275 }
2186 if (playlist == NULL) 2276 if (playlist == NULL)
2187 playlist_sync(NULL); 2277 playlist_sync(NULL);
@@ -2196,14 +2286,14 @@ static bool insert_all_playlist(struct tree_context *c,
2196static bool goto_allsubentries(int newtable) 2286static bool goto_allsubentries(int newtable)
2197{ 2287{
2198 int i = 0; 2288 int i = 0;
2199 while (i < 2 && (newtable == NAVIBROWSE || newtable == ALLSUBENTRIES)) 2289 while (i < 2 && (newtable == TABLE_NAVIBROWSE || newtable == TABLE_ALLSUBENTRIES))
2200 { 2290 {
2201 tagtree_enter(tc, false); 2291 tagtree_enter(tc, false);
2202 tagtree_load(tc); 2292 tagtree_load(tc);
2203 newtable = tagtree_get_entry(tc, tc->selected_item)->newtable; 2293 newtable = tagtree_get_entry(tc, tc->selected_item)->newtable;
2204 i++; 2294 i++;
2205 } 2295 }
2206 return (newtable == PLAYTRACK); 2296 return (newtable == TABLE_PLAYTRACK);
2207} 2297}
2208 2298
2209static void reset_tc_to_prev(int dirlevel, int selected_item) 2299static void reset_tc_to_prev(int dirlevel, int selected_item)
@@ -2233,7 +2323,7 @@ static bool tagtree_insert_selection(int position, bool queue,
2233 2323
2234 newtable = tagtree_get_entry(tc, tc->selected_item)->newtable; 2324 newtable = tagtree_get_entry(tc, tc->selected_item)->newtable;
2235 2325
2236 if (newtable == PLAYTRACK) /* Insert a single track? */ 2326 if (newtable == TABLE_PLAYTRACK) /* Insert a single track? */
2237 { 2327 {
2238 if (tagtree_get_filename(tc, buf, sizeof buf) < 0) 2328 if (tagtree_get_filename(tc, buf, sizeof buf) < 0)
2239 return false; 2329 return false;
@@ -2353,9 +2443,19 @@ static int tagtree_play_folder(struct tree_context* c)
2353 if (!insert_all_playlist(c, NULL, false, PLAYLIST_INSERT_LAST, false)) 2443 if (!insert_all_playlist(c, NULL, false, PLAYLIST_INSERT_LAST, false))
2354 return -2; 2444 return -2;
2355 2445
2446 int n = c->filesindir;
2447 bool has_playlist_been_randomized = n > playlist_get_current()->max_playlist_size;
2448 if (has_playlist_been_randomized) {
2449 /* We need to recalculate the start index based on a percentage to put the user
2450 around its desired start position and avoid out of bounds */
2451
2452 int percentage_start_index = 100 * start_index / n;
2453 start_index = percentage_start_index * playlist_get_current()->amount / 100;
2454 }
2455
2356 if (global_settings.playlist_shuffle) 2456 if (global_settings.playlist_shuffle)
2357 { 2457 {
2358 start_index = playlist_shuffle(current_tick, c->selected_item); 2458 start_index = playlist_shuffle(current_tick, start_index);
2359 if (!global_settings.play_selected) 2459 if (!global_settings.play_selected)
2360 start_index = 0; 2460 start_index = 0;
2361 } 2461 }
@@ -2403,11 +2503,11 @@ char *tagtree_get_title(struct tree_context* c)
2403{ 2503{
2404 switch (c->currtable) 2504 switch (c->currtable)
2405 { 2505 {
2406 case ROOT: 2506 case TABLE_ROOT:
2407 return menu->title; 2507 return menu->title;
2408 2508
2409 case NAVIBROWSE: 2509 case TABLE_NAVIBROWSE:
2410 case ALLSUBENTRIES: 2510 case TABLE_ALLSUBENTRIES:
2411 return current_title[c->currextra]; 2511 return current_title[c->currextra];
2412 } 2512 }
2413 2513
@@ -2419,7 +2519,7 @@ int tagtree_get_attr(struct tree_context* c)
2419 int attr = -1; 2519 int attr = -1;
2420 switch (c->currtable) 2520 switch (c->currtable)
2421 { 2521 {
2422 case NAVIBROWSE: 2522 case TABLE_NAVIBROWSE:
2423 if (csi->tagorder[c->currextra] == tag_title 2523 if (csi->tagorder[c->currextra] == tag_title
2424 || csi->tagorder[c->currextra] == tag_virt_basename) 2524 || csi->tagorder[c->currextra] == tag_virt_basename)
2425 attr = FILE_ATTR_AUDIO; 2525 attr = FILE_ATTR_AUDIO;
@@ -2427,7 +2527,7 @@ int tagtree_get_attr(struct tree_context* c)
2427 attr = ATTR_DIRECTORY; 2527 attr = ATTR_DIRECTORY;
2428 break; 2528 break;
2429 2529
2430 case ALLSUBENTRIES: 2530 case TABLE_ALLSUBENTRIES:
2431 attr = FILE_ATTR_AUDIO; 2531 attr = FILE_ATTR_AUDIO;
2432 break; 2532 break;
2433 2533
diff --git a/apps/tagtree.h b/apps/tagtree.h
index 39ab545bd0..a57a5c2f80 100644
--- a/apps/tagtree.h
+++ b/apps/tagtree.h
@@ -45,6 +45,7 @@ char *tagtree_get_title(struct tree_context* c);
45int tagtree_get_attr(struct tree_context* c); 45int tagtree_get_attr(struct tree_context* c);
46int tagtree_get_icon(struct tree_context* c); 46int tagtree_get_icon(struct tree_context* c);
47int tagtree_get_filename(struct tree_context* c, char *buf, int buflen); 47int tagtree_get_filename(struct tree_context* c, char *buf, int buflen);
48int tagtree_get_custom_action(struct tree_context* c);
48bool tagtree_get_subentry_filename(char *buf, size_t bufsize); 49bool tagtree_get_subentry_filename(char *buf, size_t bufsize);
49bool tagtree_subentries_do_action(bool (*action_cb)(const char *file_name)); 50bool tagtree_subentries_do_action(bool (*action_cb)(const char *file_name));
50 51
diff --git a/apps/tree.c b/apps/tree.c
index 71a7ee3f62..b4cd9d77b0 100644
--- a/apps/tree.c
+++ b/apps/tree.c
@@ -735,6 +735,17 @@ static int dirbrowse(void)
735 oldbutton = button; 735 oldbutton = button;
736 gui_synclist_do_button(&tree_lists, &button); 736 gui_synclist_do_button(&tree_lists, &button);
737 tc.selected_item = gui_synclist_get_sel_pos(&tree_lists); 737 tc.selected_item = gui_synclist_get_sel_pos(&tree_lists);
738 int customaction = ONPLAY_NO_CUSTOMACTION;
739 bool do_restore_display = true;
740 #ifdef HAVE_TAGCACHE
741 if (id3db && (button == ACTION_STD_OK || button == ACTION_STD_CONTEXT)) {
742 customaction = tagtree_get_custom_action(&tc);
743 if (customaction == ONPLAY_CUSTOMACTION_SHUFFLE_SONGS) {
744 button = ACTION_STD_CONTEXT; /** The code to insert shuffled is on the context branch of the switch so we always go here */
745 do_restore_display = false;
746 }
747 }
748 #endif
738 switch ( button ) { 749 switch ( button ) {
739 case ACTION_STD_OK: 750 case ACTION_STD_OK:
740 /* nothing to do if no files to display */ 751 /* nothing to do if no files to display */
@@ -773,7 +784,7 @@ static int dirbrowse(void)
773 default: 784 default:
774 break; 785 break;
775 } 786 }
776 restore = true; 787 restore = do_restore_display;
777 break; 788 break;
778 789
779 case ACTION_STD_CANCEL: 790 case ACTION_STD_CANCEL:
@@ -798,12 +809,12 @@ static int dirbrowse(void)
798 if (ft_exit(&tc) == 3) 809 if (ft_exit(&tc) == 3)
799 exit_func = true; 810 exit_func = true;
800 811
801 restore = true; 812 restore = do_restore_display;
802 break; 813 break;
803 814
804 case ACTION_TREE_STOP: 815 case ACTION_TREE_STOP:
805 if (list_stop_handler()) 816 if (list_stop_handler())
806 restore = true; 817 restore = do_restore_display;
807 break; 818 break;
808 819
809 case ACTION_STD_MENU: 820 case ACTION_STD_MENU:
@@ -851,7 +862,7 @@ static int dirbrowse(void)
851 skin_update(CUSTOM_STATUSBAR, i, SKIN_REFRESH_ALL); 862 skin_update(CUSTOM_STATUSBAR, i, SKIN_REFRESH_ALL);
852 } 863 }
853 864
854 restore = true; 865 restore = do_restore_display;
855 break; 866 break;
856 } 867 }
857#endif 868#endif
@@ -872,7 +883,7 @@ static int dirbrowse(void)
872 break; 883 break;
873 884
874 if(!numentries) 885 if(!numentries)
875 onplay_result = onplay(NULL, 0, curr_context, hotkey); 886 onplay_result = onplay(NULL, 0, curr_context, hotkey, customaction);
876 else { 887 else {
877#ifdef HAVE_TAGCACHE 888#ifdef HAVE_TAGCACHE
878 if (id3db) 889 if (id3db)
@@ -902,7 +913,7 @@ static int dirbrowse(void)
902 ft_assemble_path(buf, sizeof(buf), currdir, entry->name); 913 ft_assemble_path(buf, sizeof(buf), currdir, entry->name);
903 914
904 } 915 }
905 onplay_result = onplay(buf, attr, curr_context, hotkey); 916 onplay_result = onplay(buf, attr, curr_context, hotkey, customaction);
906 } 917 }
907 switch (onplay_result) 918 switch (onplay_result)
908 { 919 {
@@ -911,7 +922,7 @@ static int dirbrowse(void)
911 break; 922 break;
912 923
913 case ONPLAY_OK: 924 case ONPLAY_OK:
914 restore = true; 925 restore = do_restore_display;
915 break; 926 break;
916 927
917 case ONPLAY_RELOAD_DIR: 928 case ONPLAY_RELOAD_DIR:
@@ -988,7 +999,7 @@ static int dirbrowse(void)
988 999
989 lastfilter = *tc.dirfilter; 1000 lastfilter = *tc.dirfilter;
990 lastsortcase = global_settings.sort_case; 1001 lastsortcase = global_settings.sort_case;
991 restore = true; 1002 restore = do_restore_display;
992 } 1003 }
993 1004
994 if (exit_func) 1005 if (exit_func)
diff --git a/apps/tree.h b/apps/tree.h
index d13c75d434..e958bbf109 100644
--- a/apps/tree.h
+++ b/apps/tree.h
@@ -33,6 +33,9 @@ struct entry {
33 char *name; 33 char *name;
34 int attr; /* FAT attributes + file type flags */ 34 int attr; /* FAT attributes + file type flags */
35 unsigned time_write; /* Last write time */ 35 unsigned time_write; /* Last write time */
36 #ifdef HAVE_TAGCACHE
37 int customaction; /* db use */
38 #endif
36}; 39};
37 40
38#define BROWSE_SELECTONLY 0x0001 /* exit on selecting a file */ 41#define BROWSE_SELECTONLY 0x0001 /* exit on selecting a file */