summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Nielsen Feltzing <linus@haxx.se>2006-07-18 13:54:12 +0000
committerLinus Nielsen Feltzing <linus@haxx.se>2006-07-18 13:54:12 +0000
commitda0525f54f0479f545bfe120f180ca14c256e28f (patch)
tree61ed91bef7e27f8c06b78e3d817249c3f63fdf9b
parentd4100c4cb1747e1a20be53b411abec4f00b05c88 (diff)
downloadrockbox-da0525f54f0479f545bfe120f180ca14c256e28f.tar.gz
rockbox-da0525f54f0479f545bfe120f180ca14c256e28f.tar.bz2
rockbox-da0525f54f0479f545bfe120f180ca14c256e28f.zip
Patch #5179 by Sebastian Henriksen and Hardeep Sidhu - Playlist catalog
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@10232 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/SOURCES1
-rw-r--r--apps/lang/english.lang85
-rw-r--r--apps/onplay.c52
-rw-r--r--apps/playlist.c256
-rw-r--r--apps/playlist.h3
-rw-r--r--apps/playlist_catalog.c504
-rw-r--r--apps/playlist_catalog.h39
-rw-r--r--apps/playlist_menu.c12
8 files changed, 832 insertions, 120 deletions
diff --git a/apps/SOURCES b/apps/SOURCES
index 4c479b7a60..7580c5e3f6 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -13,6 +13,7 @@ menu.c
misc.c
onplay.c
playlist.c
+playlist_catalog.c
playlist_menu.c
playlist_viewer.c
plugin.c
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index 0bf10055e6..78e4ab6147 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -8571,3 +8571,88 @@
*: "Export modifications"
</voice>
</phrase>
+<phrase>
+ id: LANG_CATALOG
+ desc: in onplay menu
+ user:
+ <source>
+ *: "Playlist catalog"
+ </source>
+ <dest>
+ *: "Playlist catalog"
+ </dest>
+ <voice>
+ *: "Playlist catalog"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CATALOG_ADD_TO
+ desc: in onplay playlist catalog submenu
+ user:
+ <source>
+ *: "Add to playlist"
+ </source>
+ <dest>
+ *: "Add to playlist"
+ </dest>
+ <voice>
+ *: "Add to playlist"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CATALOG_ADD_TO_NEW
+ desc: in onplay playlist catalog submenu
+ user:
+ <source>
+ *: "Add to new playlist"
+ </source>
+ <dest>
+ *: "Add to new playlist"
+ </dest>
+ <voice>
+ *: "Add to new playlist"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CATALOG_VIEW
+ desc: in onplay playlist catalog submenu
+ user:
+ <source>
+ *: "View catalog"
+ </source>
+ <dest>
+ *: "View catalog"
+ </dest>
+ <voice>
+ *: "View catalog"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CATALOG_NO_DIRECTORY
+ desc: error message when playlist catalog directory doesn't exist
+ user:
+ <source>
+ *: "%s doesn't exist"
+ </source>
+ <dest>
+ *: "%s doesn't exist"
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CATALOG_NO_PLAYLISTS
+ desc: error message when no playlists for playlist catalog
+ user:
+ <source>
+ *: "No playlists"
+ </source>
+ <dest>
+ *: "No playlists"
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+
diff --git a/apps/onplay.c b/apps/onplay.c
index bc486d1a38..d552e41826 100644
--- a/apps/onplay.c
+++ b/apps/onplay.c
@@ -63,6 +63,7 @@
#include "eq_menu.h"
#endif
#include "playlist_menu.h"
+#include "playlist_catalog.h"
static int context;
static char* selected_file = NULL;
@@ -222,6 +223,50 @@ static bool view_playlist(void)
return result;
}
+bool cat_add_to_a_playlist(void)
+{
+ return catalog_add_to_a_playlist(selected_file, selected_file_attr,
+ false);
+}
+
+bool cat_add_to_a_new_playlist(void)
+{
+ return catalog_add_to_a_playlist(selected_file, selected_file_attr, true);
+}
+
+static bool cat_playlist_options(void)
+{
+ struct menu_item items[3];
+ int m, i=0, result;
+ bool ret = false;
+
+ if ((audio_status() & AUDIO_STATUS_PLAY && context == CONTEXT_WPS) ||
+ context == CONTEXT_TREE)
+ {
+ if (context == CONTEXT_WPS)
+ {
+ items[i].desc = ID2P(LANG_CATALOG_VIEW);
+ items[i].function = catalog_view_playlists;
+ i++;
+ }
+
+ items[i].desc = ID2P(LANG_CATALOG_ADD_TO);
+ items[i].function = cat_add_to_a_playlist;
+ i++;
+ items[i].desc = ID2P(LANG_CATALOG_ADD_TO_NEW);
+ items[i].function = cat_add_to_a_new_playlist;
+ i++;
+ }
+
+ m = menu_init( items, i, NULL, NULL, NULL, NULL );
+ result = menu_show(m);
+ if(result >= 0)
+ ret = items[result].function();
+ menu_exit(m);
+
+ return ret;
+}
+
/* Sub-menu for playlist options */
static bool playlist_options(void)
{
@@ -773,9 +818,9 @@ static int onplay_callback(int key, int menu)
int onplay(char* file, int attr, int from)
{
#if CONFIG_CODEC == SWCODEC
- struct menu_item items[13]; /* increase this if you add entries! */
+ struct menu_item items[14]; /* increase this if you add entries! */
#else
- struct menu_item items[11];
+ struct menu_item items[12];
#endif
int m, i=0, result;
#ifdef HAVE_LCD_COLOR
@@ -803,6 +848,9 @@ int onplay(char* file, int attr, int from)
items[i].desc = ID2P(LANG_PLAYLIST);
items[i].function = playlist_options;
i++;
+ items[i].desc = ID2P(LANG_CATALOG);
+ items[i].function = cat_playlist_options;
+ i++;
}
if (context == CONTEXT_WPS)
diff --git a/apps/playlist.c b/apps/playlist.c
index 93293d75ed..a08ecdfe34 100644
--- a/apps/playlist.c
+++ b/apps/playlist.c
@@ -133,6 +133,13 @@
#define PLAYLIST_DISPLAY_COUNT 10
+struct directory_search_context {
+ struct playlist_info* playlist;
+ int position;
+ bool queue;
+ int count;
+};
+
static bool changing_dir = false;
static struct playlist_info current_playlist;
@@ -151,9 +158,7 @@ static int add_indices_to_playlist(struct playlist_info* playlist,
static int add_track_to_playlist(struct playlist_info* playlist,
const char *filename, int position,
bool queue, int seek_pos);
-static int add_directory_to_playlist(struct playlist_info* playlist,
- const char *dirname, int *position,
- bool queue, int *count, bool recurse);
+static int directory_search_callback(char* filename, void* context);
static int remove_track_from_playlist(struct playlist_info* playlist,
int position, bool write);
static int randomise_playlist(struct playlist_info* playlist,
@@ -681,121 +686,46 @@ static int add_track_to_playlist(struct playlist_info* playlist,
}
/*
- * Insert directory into playlist. May be called recursively.
+ * Callback for playlist_directory_tracksearch to insert track into
+ * playlist.
*/
-static int add_directory_to_playlist(struct playlist_info* playlist,
- const char *dirname, int *position,
- bool queue, int *count, bool recurse)
+static int directory_search_callback(char* filename, void* context)
{
- char buf[MAX_PATH+1];
- unsigned char *count_str;
- int result = 0;
- int num_files = 0;
- int i;
- struct entry *files;
- struct tree_context* tc = tree_get_context();
- int dirfilter = *(tc->dirfilter);
+ struct directory_search_context* c =
+ (struct directory_search_context*) context;
+ int insert_pos;
- /* use the tree browser dircache to load files */
- *(tc->dirfilter) = SHOW_ALL;
+ insert_pos = add_track_to_playlist(c->playlist, filename, c->position,
+ c->queue, -1);
- if (ft_load(tc, dirname) < 0)
- {
- gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
- *(tc->dirfilter) = dirfilter;
+ if (insert_pos < 0)
return -1;
- }
-
- files = (struct entry*) tc->dircache;
- num_files = tc->filesindir;
-
- /* we've overwritten the dircache so tree browser will need to be
- reloaded */
- reload_directory();
-
- if (queue)
- count_str = str(LANG_PLAYLIST_QUEUE_COUNT);
- else
- count_str = str(LANG_PLAYLIST_INSERT_COUNT);
-
- for (i=0; i<num_files; i++)
+
+ (c->count)++;
+
+ /* Make sure tracks are inserted in correct order if user requests
+ INSERT_FIRST */
+ if (c->position == PLAYLIST_INSERT_FIRST || c->position >= 0)
+ c->position = insert_pos + 1;
+
+ if (((c->count)%PLAYLIST_DISPLAY_COUNT) == 0)
{
- /* user abort */
- if (button_get(false) == SETTINGS_CANCEL)
- {
- result = -1;
- break;
- }
+ unsigned char* count_str;
- if (files[i].attr & ATTR_DIRECTORY)
- {
- if (recurse)
- {
- /* recursively add directories */
- snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
- result = add_directory_to_playlist(playlist, buf, position,
- queue, count, recurse);
- if (result < 0)
- break;
-
- /* we now need to reload our current directory */
- if(ft_load(tc, dirname) < 0)
- {
- result = -1;
- break;
- }
-
- files = (struct entry*) tc->dircache;
- num_files = tc->filesindir;
- if (!num_files)
- {
- result = -1;
- break;
- }
- }
- else
- continue;
- }
- else if ((files[i].attr & TREE_ATTR_MASK) == TREE_ATTR_MPA)
- {
- int insert_pos;
-
- snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
-
- insert_pos = add_track_to_playlist(playlist, buf, *position,
- queue, -1);
- if (insert_pos < 0)
- {
- result = -1;
- break;
- }
-
- (*count)++;
-
- /* Make sure tracks are inserted in correct order if user requests
- INSERT_FIRST */
- if (*position == PLAYLIST_INSERT_FIRST || *position >= 0)
- *position = insert_pos + 1;
-
- if ((*count%PLAYLIST_DISPLAY_COUNT) == 0)
- {
- display_playlist_count(*count, count_str);
+ if (c->queue)
+ count_str = str(LANG_PLAYLIST_QUEUE_COUNT);
+ else
+ count_str = str(LANG_PLAYLIST_INSERT_COUNT);
- if (*count == PLAYLIST_DISPLAY_COUNT &&
- (audio_status() & AUDIO_STATUS_PLAY) &&
- playlist->started)
- audio_flush_and_reload_tracks();
- }
-
- /* let the other threads work */
- yield();
- }
+ display_playlist_count(c->count, count_str);
+
+ if ((c->count) == PLAYLIST_DISPLAY_COUNT &&
+ (audio_status() & AUDIO_STATUS_PLAY) &&
+ c->playlist->started)
+ audio_flush_and_reload_tracks();
}
- /* restore dirfilter */
- *(tc->dirfilter) = dirfilter;
-
- return result;
+ return 0;
}
/*
@@ -2811,9 +2741,9 @@ int playlist_insert_directory(struct playlist_info* playlist,
const char *dirname, int position, bool queue,
bool recurse)
{
- int count = 0;
int result;
unsigned char *count_str;
+ struct directory_search_context context;
if (!playlist)
playlist = &current_playlist;
@@ -2829,18 +2759,23 @@ int playlist_insert_directory(struct playlist_info* playlist,
else
count_str = str(LANG_PLAYLIST_INSERT_COUNT);
- display_playlist_count(count, count_str);
+ display_playlist_count(0, count_str);
+
+ context.playlist = playlist;
+ context.position = position;
+ context.queue = queue;
+ context.count = 0;
cpu_boost(true);
- result = add_directory_to_playlist(playlist, dirname, &position, queue,
- &count, recurse);
+ result = playlist_directory_tracksearch(dirname, recurse,
+ directory_search_callback, &context);
sync_control(playlist, false);
cpu_boost(false);
- display_playlist_count(count, count_str);
+ display_playlist_count(context.count, count_str);
if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
audio_flush_and_reload_tracks();
@@ -3403,3 +3338,98 @@ int playlist_save(struct playlist_info* playlist, char *filename)
return result;
}
+
+/*
+ * Search specified directory for tracks and notify via callback. May be
+ * called recursively.
+ */
+int playlist_directory_tracksearch(const char* dirname, bool recurse,
+ int (*callback)(char*, void*),
+ void* context)
+{
+ char buf[MAX_PATH+1];
+ int result = 0;
+ int num_files = 0;
+ int i;
+ struct entry *files;
+ struct tree_context* tc = tree_get_context();
+ int old_dirfilter = *(tc->dirfilter);
+
+ if (!callback)
+ return -1;
+
+ /* use the tree browser dircache to load files */
+ *(tc->dirfilter) = SHOW_ALL;
+
+ if (ft_load(tc, dirname) < 0)
+ {
+ gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
+ *(tc->dirfilter) = old_dirfilter;
+ return -1;
+ }
+
+ files = (struct entry*) tc->dircache;
+ num_files = tc->filesindir;
+
+ /* we've overwritten the dircache so tree browser will need to be
+ reloaded */
+ reload_directory();
+
+ for (i=0; i<num_files; i++)
+ {
+ /* user abort */
+ if (button_get(false) == SETTINGS_CANCEL)
+ {
+ result = -1;
+ break;
+ }
+
+ if (files[i].attr & ATTR_DIRECTORY)
+ {
+ if (recurse)
+ {
+ /* recursively add directories */
+ snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
+ result = playlist_directory_tracksearch(buf, recurse,
+ callback, context);
+ if (result < 0)
+ break;
+
+ /* we now need to reload our current directory */
+ if(ft_load(tc, dirname) < 0)
+ {
+ result = -1;
+ break;
+ }
+
+ files = (struct entry*) tc->dircache;
+ num_files = tc->filesindir;
+ if (!num_files)
+ {
+ result = -1;
+ break;
+ }
+ }
+ else
+ continue;
+ }
+ else if ((files[i].attr & TREE_ATTR_MASK) == TREE_ATTR_MPA)
+ {
+ snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
+
+ if (callback(buf, context) != 0)
+ {
+ result = -1;
+ break;
+ }
+
+ /* let the other threads work */
+ yield();
+ }
+ }
+
+ /* restore dirfilter */
+ *(tc->dirfilter) = old_dirfilter;
+
+ return result;
+}
diff --git a/apps/playlist.h b/apps/playlist.h
index 1a5bb3d33c..c5449a219e 100644
--- a/apps/playlist.h
+++ b/apps/playlist.h
@@ -158,5 +158,8 @@ char *playlist_get_name(const struct playlist_info* playlist, char *buf,
int playlist_get_track_info(struct playlist_info* playlist, int index,
struct playlist_track_info* info);
int playlist_save(struct playlist_info* playlist, char *filename);
+int playlist_directory_tracksearch(const char* dirname, bool recurse,
+ int (*callback)(char*, void*),
+ void* context);
#endif /* __PLAYLIST_H__ */
diff --git a/apps/playlist_catalog.c b/apps/playlist_catalog.c
new file mode 100644
index 0000000000..a1a9bd8ea1
--- /dev/null
+++ b/apps/playlist_catalog.c
@@ -0,0 +1,504 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2006 Sebastian Henriksen, Hardeep Sidhu
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "action.h"
+#include "dir.h"
+#include "file.h"
+#include "filetree.h"
+#include "kernel.h"
+#include "keyboard.h"
+#include "lang.h"
+#include "list.h"
+#include "misc.h"
+#include "onplay.h"
+#include "playlist.h"
+#include "settings.h"
+#include "splash.h"
+#include "sprintf.h"
+#include "tree.h"
+#include "yesno.h"
+
+#define PLAYLIST_CATALOG_CFG ROCKBOX_DIR "/playlist_catalog.config"
+#define PLAYLIST_CATALOG_DEFAULT_DIR "/Playlists"
+#define MAX_PLAYLISTS 400
+#define PLAYLIST_DISPLAY_COUNT 10
+
+/* Use for recursive directory search */
+struct add_track_context {
+ int fd;
+ int count;
+};
+
+/* keep track of most recently used playlist */
+static char most_recent_playlist[MAX_PATH];
+
+/* directory where our playlists our stored (configured in
+ PLAYLIST_CATALOG_CFG) */
+static char playlist_dir[MAX_PATH];
+static int playlist_dir_length;
+static bool playlist_dir_exists = false;
+
+/* Retrieve playlist directory from config file and verify it exists */
+static int initialize_catalog(void)
+{
+ static bool initialized = false;
+
+ if (!initialized)
+ {
+ int f;
+ DIR* dir;
+ bool default_dir = true;
+
+ f = open(PLAYLIST_CATALOG_CFG, O_RDONLY);
+ if (f >= 0)
+ {
+ char buf[MAX_PATH+5];
+
+ while (read_line(f, buf, sizeof(buf)))
+ {
+ char* name;
+ char* value;
+
+ /* directory config is of the format: "dir: /path/to/dir" */
+ if (settings_parseline(buf, &name, &value) &&
+ !strncasecmp(name, "dir:", strlen(name)) &&
+ strlen(value) > 0)
+ {
+ strncpy(playlist_dir, value, strlen(value));
+ default_dir = false;
+ }
+ }
+
+ close(f);
+ }
+
+ /* fall back to default directory if no or invalid config */
+ if (default_dir)
+ strncpy(playlist_dir, PLAYLIST_CATALOG_DEFAULT_DIR,
+ sizeof(playlist_dir));
+
+ playlist_dir_length = strlen(playlist_dir);
+
+ dir = opendir(playlist_dir);
+ if (dir)
+ {
+ playlist_dir_exists = true;
+ closedir(dir);
+ memset(most_recent_playlist, 0, sizeof(most_recent_playlist));
+ initialized = true;
+ }
+ }
+
+ if (!playlist_dir_exists)
+ {
+ gui_syncsplash(HZ*2, true, str(LANG_CATALOG_NO_DIRECTORY),
+ playlist_dir);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Use the filetree functions to retrieve the list of playlists in the
+ directory */
+static int create_playlist_list(char** playlists, int num_items,
+ int* num_playlists)
+{
+ int result = -1;
+ int num_files = 0;
+ int index = 0;
+ int i;
+ bool most_recent = false;
+ struct entry *files;
+ struct tree_context* tc = tree_get_context();
+ int dirfilter = *(tc->dirfilter);
+
+ *num_playlists = 0;
+
+ /* use the tree browser dircache to load only playlists */
+ *(tc->dirfilter) = SHOW_PLAYLIST;
+
+ if (ft_load(tc, playlist_dir) < 0)
+ {
+ gui_syncsplash(HZ*2, true, str(LANG_CATALOG_NO_DIRECTORY),
+ playlist_dir);
+ goto exit;
+ }
+
+ files = (struct entry*) tc->dircache;
+ num_files = tc->filesindir;
+
+ /* we've overwritten the dircache so tree browser will need to be
+ reloaded */
+ reload_directory();
+
+ /* if it exists, most recent playlist will always be index 0 */
+ if (most_recent_playlist[0] != '\0')
+ {
+ index = 1;
+ most_recent = true;
+ }
+
+ for (i=0; i<num_files && index<num_items; i++)
+ {
+ if (files[i].attr & TREE_ATTR_M3U)
+ {
+ if (most_recent && !strncmp(files[i].name, most_recent_playlist,
+ sizeof(most_recent_playlist)))
+ {
+ playlists[0] = files[i].name;
+ most_recent = false;
+ }
+ else
+ {
+ playlists[index] = files[i].name;
+ index++;
+ }
+ }
+ }
+
+ *num_playlists = index;
+
+ /* we couldn't find the most recent playlist, shift all playlists up */
+ if (most_recent)
+ {
+ for (i=0; i<index-1; i++)
+ playlists[i] = playlists[i+1];
+
+ (*num_playlists)--;
+
+ most_recent_playlist[0] = '\0';
+ }
+
+ result = 0;
+
+exit:
+ *(tc->dirfilter) = dirfilter;
+ return result;
+}
+
+/* Callback for gui_synclist */
+static char* playlist_callback_name(int selected_item, void* data,
+ char* buffer)
+{
+ char** playlists = (char**) data;
+
+ strncpy(buffer, playlists[selected_item], MAX_PATH);
+
+ return buffer;
+}
+
+/* Display all playlists in catalog. Selected "playlist" is returned.
+ If "view" mode is set then we're not adding anything into playlist. */
+static int display_playlists(char* playlist, bool view)
+{
+ int result = -1;
+ int num_playlists = 0;
+ int lastbutton = BUTTON_NONE;
+ bool exit = false;
+ char temp_buf[MAX_PATH];
+ char* playlists[MAX_PLAYLISTS];
+ struct gui_synclist playlist_lists;
+
+ if (create_playlist_list(playlists, sizeof(playlists),
+ &num_playlists) != 0)
+ return -1;
+
+ if (num_playlists <= 0)
+ {
+ gui_syncsplash(HZ*2, true, str(LANG_CATALOG_NO_PLAYLISTS));
+ return -1;
+ }
+
+ if (!playlist)
+ playlist = temp_buf;
+
+ gui_synclist_init(&playlist_lists, playlist_callback_name, playlists,
+ false, 1);
+ gui_synclist_set_nb_items(&playlist_lists, num_playlists);
+ gui_synclist_draw(&playlist_lists);
+
+ while (!exit)
+ {
+ int button = button_get_w_tmo(HZ/2);
+ char* sel_file;
+
+ gui_synclist_do_button(&playlist_lists, button);
+
+ sel_file = playlists[gui_synclist_get_sel_pos(&playlist_lists)];
+
+ switch (button)
+ {
+ case TREE_EXIT:
+#ifdef TREE_RC_EXIT
+ case TREE_RC_EXIT:
+#endif
+#ifdef TREE_OFF
+ case TREE_OFF:
+#endif
+ exit = true;
+ break;
+
+#ifdef TREE_ENTER
+ case TREE_ENTER:
+ case TREE_ENTER | BUTTON_REPEAT:
+#endif
+#ifdef TREE_RC_RUN
+ case TREE_RC_RUN:
+#endif
+ case TREE_RUN:
+#ifdef TREE_RUN_PRE
+ if (((button == TREE_RUN)
+#ifdef TREE_RC_RUN_PRE
+ || (button == TREE_RC_RUN))
+ && ((lastbutton != TREE_RC_RUN_PRE)
+#endif
+ && (lastbutton != TREE_RUN_PRE)))
+ break;
+#endif
+
+ if (view)
+ {
+ /* In view mode, selecting a playlist starts playback */
+ if (playlist_create(playlist_dir, sel_file) != -1)
+ {
+ if (global_settings.playlist_shuffle)
+ playlist_shuffle(current_tick, -1);
+ playlist_start(0, 0);
+ }
+ }
+ else
+ {
+ /* we found the playlist we want to add to */
+ snprintf(playlist, MAX_PATH, "%s/%s", playlist_dir,
+ sel_file);
+ }
+
+ result = 0;
+ exit = true;
+ break;
+
+ case TREE_CONTEXT:
+#ifdef TREE_CONTEXT2
+ case TREE_CONTEXT2:
+#endif
+#ifdef TREE_RC_CONTEXT
+ case TREE_RC_CONTEXT:
+#endif
+ /* context menu only available in view mode */
+ if (view)
+ {
+ snprintf(playlist, MAX_PATH, "%s/%s", playlist_dir,
+ sel_file);
+
+ if (onplay(playlist, TREE_ATTR_M3U,
+ CONTEXT_TREE) != ONPLAY_OK)
+ {
+ result = 0;
+ exit = true;
+ }
+ else
+ gui_synclist_draw(&playlist_lists);
+ }
+ break;
+
+ case BUTTON_NONE:
+ gui_syncstatusbar_draw(&statusbars, false);
+ break;
+
+ default:
+ if(default_event_handler(button) == SYS_USB_CONNECTED)
+ {
+ result = -1;
+ exit = true;
+ }
+ break;
+ }
+
+ lastbutton = button;
+ }
+
+ return result;
+}
+
+/* display number of tracks inserted into playlists. Used for directory
+ insert */
+static void display_insert_count(int count)
+{
+ gui_syncsplash(0, true, str(LANG_PLAYLIST_INSERT_COUNT), count,
+#if CONFIG_KEYPAD == PLAYER_PAD
+ str(LANG_STOP_ABORT)
+#else
+ str(LANG_OFF_ABORT)
+#endif
+ );
+}
+
+/* Add specified track into playlist. Callback from directory insert */
+static int add_track_to_playlist(char* filename, void* context)
+{
+ struct add_track_context* c = (struct add_track_context*) context;
+
+ if (fdprintf(c->fd, "%s\n", filename) <= 0)
+ return -1;
+
+ (c->count)++;
+
+ if (((c->count)%PLAYLIST_DISPLAY_COUNT) == 0)
+ display_insert_count(c->count);
+
+ return 0;
+}
+
+/* Add "sel" file into specified "playlist". How to insert depends on type
+ of file */
+static int add_to_playlist(const char* playlist, char* sel, int sel_attr)
+{
+ int fd;
+ int result = -1;
+
+ fd = open(playlist, O_CREAT|O_WRONLY|O_APPEND);
+ if(fd < 0)
+ return result;
+
+ /* In case we're in the playlist directory */
+ reload_directory();
+
+ if ((sel_attr & TREE_ATTR_MASK) == TREE_ATTR_MPA)
+ {
+ /* append the selected file */
+ if (fdprintf(fd, "%s\n", sel) > 0)
+ result = 0;
+ }
+ else if ((sel_attr & TREE_ATTR_MASK) == TREE_ATTR_M3U)
+ {
+ /* append playlist */
+ int f, fs, i;
+ char buf[1024];
+
+ if(strcasecmp(playlist, sel) == 0)
+ goto exit;
+
+ f = open(sel, O_RDONLY);
+ if (f < 0)
+ goto exit;
+
+ fs = filesize(f);
+
+ for (i=0; i<fs;)
+ {
+ int n;
+
+ n = read(f, buf, sizeof(buf));
+ if (n < 0)
+ break;
+
+ if (write(fd, buf, n) < 0)
+ break;
+
+ i += n;
+ }
+
+ if (i >= fs)
+ result = 0;
+
+ close(f);
+ }
+ else if (sel_attr & ATTR_DIRECTORY)
+ {
+ /* search directory for tracks and append to playlist */
+ bool recurse = false;
+ char *lines[] = {
+ (char *)str(LANG_RECURSE_DIRECTORY_QUESTION),
+ sel
+ };
+ struct text_message message={lines, 2};
+ struct add_track_context context;
+
+ if (global_settings.recursive_dir_insert != RECURSE_ASK)
+ recurse = (bool)global_settings.recursive_dir_insert;
+ else
+ {
+ /* Ask if user wants to recurse directory */
+ recurse = (gui_syncyesno_run(&message, NULL, NULL)==YESNO_YES);
+ }
+
+ context.fd = fd;
+ context.count = 0;
+
+ display_insert_count(0);
+
+ result = playlist_directory_tracksearch(sel, recurse,
+ add_track_to_playlist, &context);
+
+ display_insert_count(context.count);
+ }
+
+exit:
+ close(fd);
+ return result;
+}
+
+bool catalog_view_playlists(void)
+{
+ if (initialize_catalog() == -1)
+ return false;
+
+ if (display_playlists(NULL, true) == -1)
+ return false;
+
+ return true;
+}
+
+bool catalog_add_to_a_playlist(char* sel, int sel_attr, bool new_playlist)
+{
+ char playlist[MAX_PATH];
+
+ if (initialize_catalog() == -1)
+ return false;
+
+ if (new_playlist)
+ {
+ snprintf(playlist, MAX_PATH, "%s/", playlist_dir);
+ if (kbd_input(playlist, MAX_PATH))
+ return false;
+
+ if(strlen(playlist) <= 4 ||
+ strcasecmp(&playlist[strlen(playlist)-4], ".m3u"))
+ strcat(playlist, ".m3u");
+ }
+ else
+ {
+ if (display_playlists(playlist, false) == -1)
+ return false;
+ }
+
+ if (add_to_playlist(playlist, sel, sel_attr) == 0)
+ {
+ strncpy(most_recent_playlist, playlist+playlist_dir_length+1,
+ sizeof(most_recent_playlist));
+ return true;
+ }
+ else
+ return false;
+}
diff --git a/apps/playlist_catalog.h b/apps/playlist_catalog.h
new file mode 100644
index 0000000000..591906e64f
--- /dev/null
+++ b/apps/playlist_catalog.h
@@ -0,0 +1,39 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2006 Sebastian Henriksen, Hardeep Sidhu
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef _PLAYLIST_CATALOG_H_
+#define _PLAYLIST_CATALOG_H_
+
+/*
+ * View list of playlists in catalog.
+ * ret : true if no error
+ */
+bool catalog_view_playlists(void);
+
+/*
+ * Add something to a playlist (new or select from list of playlists in
+ * catalog).
+ * sel : the path of the music file, playlist or directory to add
+ * sel_attr : the attributes that tell what type of file we're adding
+ * new_playlist : whether we want to create a new playlist or add to an
+ * existing one.
+ * ret : true if the file was successfully added
+ */
+bool catalog_add_to_a_playlist(char* sel, int sel_attr, bool new_playlist);
+
+#endif
diff --git a/apps/playlist_menu.c b/apps/playlist_menu.c
index ba4a4fe0d2..e23722160f 100644
--- a/apps/playlist_menu.c
+++ b/apps/playlist_menu.c
@@ -28,6 +28,7 @@
#include "playlist_viewer.h"
#include "talk.h"
#include "lang.h"
+#include "playlist_catalog.h"
#include "playlist_menu.h"
static bool save_playlist(void)
@@ -61,11 +62,12 @@ bool playlist_menu(void)
bool result;
static const struct menu_item items[] = {
- { ID2P(LANG_CREATE_PLAYLIST), create_playlist },
- { ID2P(LANG_VIEW_DYNAMIC_PLAYLIST), playlist_viewer },
- { ID2P(LANG_SAVE_DYNAMIC_PLAYLIST), save_playlist },
- { ID2P(LANG_RECURSE_DIRECTORY), recurse_directory },
- { ID2P(LANG_WARN_ERASEDYNPLAYLIST_MENU), warnon_option},
+ { ID2P(LANG_CREATE_PLAYLIST), create_playlist },
+ { ID2P(LANG_VIEW_DYNAMIC_PLAYLIST), playlist_viewer },
+ { ID2P(LANG_SAVE_DYNAMIC_PLAYLIST), save_playlist },
+ { ID2P(LANG_CATALOG), catalog_view_playlists },
+ { ID2P(LANG_RECURSE_DIRECTORY), recurse_directory },
+ { ID2P(LANG_WARN_ERASEDYNPLAYLIST_MENU), warnon_option },
};
m = menu_init( items, sizeof items / sizeof(struct menu_item), NULL,