summaryrefslogtreecommitdiff
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.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
13misc.c 13misc.c
14onplay.c 14onplay.c
15playlist.c 15playlist.c
16playlist_catalog.c
16playlist_menu.c 17playlist_menu.c
17playlist_viewer.c 18playlist_viewer.c
18plugin.c 19plugin.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 @@
8571 *: "Export modifications" 8571 *: "Export modifications"
8572 </voice> 8572 </voice>
8573</phrase> 8573</phrase>
8574<phrase>
8575 id: LANG_CATALOG
8576 desc: in onplay menu
8577 user:
8578 <source>
8579 *: "Playlist catalog"
8580 </source>
8581 <dest>
8582 *: "Playlist catalog"
8583 </dest>
8584 <voice>
8585 *: "Playlist catalog"
8586 </voice>
8587</phrase>
8588<phrase>
8589 id: LANG_CATALOG_ADD_TO
8590 desc: in onplay playlist catalog submenu
8591 user:
8592 <source>
8593 *: "Add to playlist"
8594 </source>
8595 <dest>
8596 *: "Add to playlist"
8597 </dest>
8598 <voice>
8599 *: "Add to playlist"
8600 </voice>
8601</phrase>
8602<phrase>
8603 id: LANG_CATALOG_ADD_TO_NEW
8604 desc: in onplay playlist catalog submenu
8605 user:
8606 <source>
8607 *: "Add to new playlist"
8608 </source>
8609 <dest>
8610 *: "Add to new playlist"
8611 </dest>
8612 <voice>
8613 *: "Add to new playlist"
8614 </voice>
8615</phrase>
8616<phrase>
8617 id: LANG_CATALOG_VIEW
8618 desc: in onplay playlist catalog submenu
8619 user:
8620 <source>
8621 *: "View catalog"
8622 </source>
8623 <dest>
8624 *: "View catalog"
8625 </dest>
8626 <voice>
8627 *: "View catalog"
8628 </voice>
8629</phrase>
8630<phrase>
8631 id: LANG_CATALOG_NO_DIRECTORY
8632 desc: error message when playlist catalog directory doesn't exist
8633 user:
8634 <source>
8635 *: "%s doesn't exist"
8636 </source>
8637 <dest>
8638 *: "%s doesn't exist"
8639 </dest>
8640 <voice>
8641 *: ""
8642 </voice>
8643</phrase>
8644<phrase>
8645 id: LANG_CATALOG_NO_PLAYLISTS
8646 desc: error message when no playlists for playlist catalog
8647 user:
8648 <source>
8649 *: "No playlists"
8650 </source>
8651 <dest>
8652 *: "No playlists"
8653 </dest>
8654 <voice>
8655 *: ""
8656 </voice>
8657</phrase>
8658
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 @@
63#include "eq_menu.h" 63#include "eq_menu.h"
64#endif 64#endif
65#include "playlist_menu.h" 65#include "playlist_menu.h"
66#include "playlist_catalog.h"
66 67
67static int context; 68static int context;
68static char* selected_file = NULL; 69static char* selected_file = NULL;
@@ -222,6 +223,50 @@ static bool view_playlist(void)
222 return result; 223 return result;
223} 224}
224 225
226bool cat_add_to_a_playlist(void)
227{
228 return catalog_add_to_a_playlist(selected_file, selected_file_attr,
229 false);
230}
231
232bool cat_add_to_a_new_playlist(void)
233{
234 return catalog_add_to_a_playlist(selected_file, selected_file_attr, true);
235}
236
237static bool cat_playlist_options(void)
238{
239 struct menu_item items[3];
240 int m, i=0, result;
241 bool ret = false;
242
243 if ((audio_status() & AUDIO_STATUS_PLAY && context == CONTEXT_WPS) ||
244 context == CONTEXT_TREE)
245 {
246 if (context == CONTEXT_WPS)
247 {
248 items[i].desc = ID2P(LANG_CATALOG_VIEW);
249 items[i].function = catalog_view_playlists;
250 i++;
251 }
252
253 items[i].desc = ID2P(LANG_CATALOG_ADD_TO);
254 items[i].function = cat_add_to_a_playlist;
255 i++;
256 items[i].desc = ID2P(LANG_CATALOG_ADD_TO_NEW);
257 items[i].function = cat_add_to_a_new_playlist;
258 i++;
259 }
260
261 m = menu_init( items, i, NULL, NULL, NULL, NULL );
262 result = menu_show(m);
263 if(result >= 0)
264 ret = items[result].function();
265 menu_exit(m);
266
267 return ret;
268}
269
225/* Sub-menu for playlist options */ 270/* Sub-menu for playlist options */
226static bool playlist_options(void) 271static bool playlist_options(void)
227{ 272{
@@ -773,9 +818,9 @@ static int onplay_callback(int key, int menu)
773int onplay(char* file, int attr, int from) 818int onplay(char* file, int attr, int from)
774{ 819{
775#if CONFIG_CODEC == SWCODEC 820#if CONFIG_CODEC == SWCODEC
776 struct menu_item items[13]; /* increase this if you add entries! */ 821 struct menu_item items[14]; /* increase this if you add entries! */
777#else 822#else
778 struct menu_item items[11]; 823 struct menu_item items[12];
779#endif 824#endif
780 int m, i=0, result; 825 int m, i=0, result;
781#ifdef HAVE_LCD_COLOR 826#ifdef HAVE_LCD_COLOR
@@ -803,6 +848,9 @@ int onplay(char* file, int attr, int from)
803 items[i].desc = ID2P(LANG_PLAYLIST); 848 items[i].desc = ID2P(LANG_PLAYLIST);
804 items[i].function = playlist_options; 849 items[i].function = playlist_options;
805 i++; 850 i++;
851 items[i].desc = ID2P(LANG_CATALOG);
852 items[i].function = cat_playlist_options;
853 i++;
806 } 854 }
807 855
808 if (context == CONTEXT_WPS) 856 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 @@
133 133
134#define PLAYLIST_DISPLAY_COUNT 10 134#define PLAYLIST_DISPLAY_COUNT 10
135 135
136struct directory_search_context {
137 struct playlist_info* playlist;
138 int position;
139 bool queue;
140 int count;
141};
142
136static bool changing_dir = false; 143static bool changing_dir = false;
137 144
138static struct playlist_info current_playlist; 145static struct playlist_info current_playlist;
@@ -151,9 +158,7 @@ static int add_indices_to_playlist(struct playlist_info* playlist,
151static int add_track_to_playlist(struct playlist_info* playlist, 158static int add_track_to_playlist(struct playlist_info* playlist,
152 const char *filename, int position, 159 const char *filename, int position,
153 bool queue, int seek_pos); 160 bool queue, int seek_pos);
154static int add_directory_to_playlist(struct playlist_info* playlist, 161static int directory_search_callback(char* filename, void* context);
155 const char *dirname, int *position,
156 bool queue, int *count, bool recurse);
157static int remove_track_from_playlist(struct playlist_info* playlist, 162static int remove_track_from_playlist(struct playlist_info* playlist,
158 int position, bool write); 163 int position, bool write);
159static int randomise_playlist(struct playlist_info* playlist, 164static int randomise_playlist(struct playlist_info* playlist,
@@ -681,121 +686,46 @@ static int add_track_to_playlist(struct playlist_info* playlist,
681} 686}
682 687
683/* 688/*
684 * Insert directory into playlist. May be called recursively. 689 * Callback for playlist_directory_tracksearch to insert track into
690 * playlist.
685 */ 691 */
686static int add_directory_to_playlist(struct playlist_info* playlist, 692static int directory_search_callback(char* filename, void* context)
687 const char *dirname, int *position,
688 bool queue, int *count, bool recurse)
689{ 693{
690 char buf[MAX_PATH+1]; 694 struct directory_search_context* c =
691 unsigned char *count_str; 695 (struct directory_search_context*) context;
692 int result = 0; 696 int insert_pos;
693 int num_files = 0;
694 int i;
695 struct entry *files;
696 struct tree_context* tc = tree_get_context();
697 int dirfilter = *(tc->dirfilter);
698 697
699 /* use the tree browser dircache to load files */ 698 insert_pos = add_track_to_playlist(c->playlist, filename, c->position,
700 *(tc->dirfilter) = SHOW_ALL; 699 c->queue, -1);
701 700
702 if (ft_load(tc, dirname) < 0) 701 if (insert_pos < 0)
703 {
704 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
705 *(tc->dirfilter) = dirfilter;
706 return -1; 702 return -1;
707 } 703
708 704 (c->count)++;
709 files = (struct entry*) tc->dircache; 705
710 num_files = tc->filesindir; 706 /* Make sure tracks are inserted in correct order if user requests
711 707 INSERT_FIRST */
712 /* we've overwritten the dircache so tree browser will need to be 708 if (c->position == PLAYLIST_INSERT_FIRST || c->position >= 0)
713 reloaded */ 709 c->position = insert_pos + 1;
714 reload_directory(); 710
715 711 if (((c->count)%PLAYLIST_DISPLAY_COUNT) == 0)
716 if (queue)
717 count_str = str(LANG_PLAYLIST_QUEUE_COUNT);
718 else
719 count_str = str(LANG_PLAYLIST_INSERT_COUNT);
720
721 for (i=0; i<num_files; i++)
722 { 712 {
723 /* user abort */ 713 unsigned char* count_str;
724 if (button_get(false) == SETTINGS_CANCEL)
725 {
726 result = -1;
727 break;
728 }
729 714
730 if (files[i].attr & ATTR_DIRECTORY) 715 if (c->queue)
731 { 716 count_str = str(LANG_PLAYLIST_QUEUE_COUNT);
732 if (recurse) 717 else
733 { 718 count_str = str(LANG_PLAYLIST_INSERT_COUNT);
734 /* recursively add directories */
735 snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
736 result = add_directory_to_playlist(playlist, buf, position,
737 queue, count, recurse);
738 if (result < 0)
739 break;
740
741 /* we now need to reload our current directory */
742 if(ft_load(tc, dirname) < 0)
743 {
744 result = -1;
745 break;
746 }
747
748 files = (struct entry*) tc->dircache;
749 num_files = tc->filesindir;
750 if (!num_files)
751 {
752 result = -1;
753 break;
754 }
755 }
756 else
757 continue;
758 }
759 else if ((files[i].attr & TREE_ATTR_MASK) == TREE_ATTR_MPA)
760 {
761 int insert_pos;
762
763 snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
764
765 insert_pos = add_track_to_playlist(playlist, buf, *position,
766 queue, -1);
767 if (insert_pos < 0)
768 {
769 result = -1;
770 break;
771 }
772
773 (*count)++;
774
775 /* Make sure tracks are inserted in correct order if user requests
776 INSERT_FIRST */
777 if (*position == PLAYLIST_INSERT_FIRST || *position >= 0)
778 *position = insert_pos + 1;
779
780 if ((*count%PLAYLIST_DISPLAY_COUNT) == 0)
781 {
782 display_playlist_count(*count, count_str);
783 719
784 if (*count == PLAYLIST_DISPLAY_COUNT && 720 display_playlist_count(c->count, count_str);
785 (audio_status() & AUDIO_STATUS_PLAY) && 721
786 playlist->started) 722 if ((c->count) == PLAYLIST_DISPLAY_COUNT &&
787 audio_flush_and_reload_tracks(); 723 (audio_status() & AUDIO_STATUS_PLAY) &&
788 } 724 c->playlist->started)
789 725 audio_flush_and_reload_tracks();
790 /* let the other threads work */
791 yield();
792 }
793 } 726 }
794 727
795 /* restore dirfilter */ 728 return 0;
796 *(tc->dirfilter) = dirfilter;
797
798 return result;
799} 729}
800 730
801/* 731/*
@@ -2811,9 +2741,9 @@ int playlist_insert_directory(struct playlist_info* playlist,
2811 const char *dirname, int position, bool queue, 2741 const char *dirname, int position, bool queue,
2812 bool recurse) 2742 bool recurse)
2813{ 2743{
2814 int count = 0;
2815 int result; 2744 int result;
2816 unsigned char *count_str; 2745 unsigned char *count_str;
2746 struct directory_search_context context;
2817 2747
2818 if (!playlist) 2748 if (!playlist)
2819 playlist = &current_playlist; 2749 playlist = &current_playlist;
@@ -2829,18 +2759,23 @@ int playlist_insert_directory(struct playlist_info* playlist,
2829 else 2759 else
2830 count_str = str(LANG_PLAYLIST_INSERT_COUNT); 2760 count_str = str(LANG_PLAYLIST_INSERT_COUNT);
2831 2761
2832 display_playlist_count(count, count_str); 2762 display_playlist_count(0, count_str);
2763
2764 context.playlist = playlist;
2765 context.position = position;
2766 context.queue = queue;
2767 context.count = 0;
2833 2768
2834 cpu_boost(true); 2769 cpu_boost(true);
2835 2770
2836 result = add_directory_to_playlist(playlist, dirname, &position, queue, 2771 result = playlist_directory_tracksearch(dirname, recurse,
2837 &count, recurse); 2772 directory_search_callback, &context);
2838 2773
2839 sync_control(playlist, false); 2774 sync_control(playlist, false);
2840 2775
2841 cpu_boost(false); 2776 cpu_boost(false);
2842 2777
2843 display_playlist_count(count, count_str); 2778 display_playlist_count(context.count, count_str);
2844 2779
2845 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started) 2780 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
2846 audio_flush_and_reload_tracks(); 2781 audio_flush_and_reload_tracks();
@@ -3403,3 +3338,98 @@ int playlist_save(struct playlist_info* playlist, char *filename)
3403 3338
3404 return result; 3339 return result;
3405} 3340}
3341
3342/*
3343 * Search specified directory for tracks and notify via callback. May be
3344 * called recursively.
3345 */
3346int playlist_directory_tracksearch(const char* dirname, bool recurse,
3347 int (*callback)(char*, void*),
3348 void* context)
3349{
3350 char buf[MAX_PATH+1];
3351 int result = 0;
3352 int num_files = 0;
3353 int i;
3354 struct entry *files;
3355 struct tree_context* tc = tree_get_context();
3356 int old_dirfilter = *(tc->dirfilter);
3357
3358 if (!callback)
3359 return -1;
3360
3361 /* use the tree browser dircache to load files */
3362 *(tc->dirfilter) = SHOW_ALL;
3363
3364 if (ft_load(tc, dirname) < 0)
3365 {
3366 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
3367 *(tc->dirfilter) = old_dirfilter;
3368 return -1;
3369 }
3370
3371 files = (struct entry*) tc->dircache;
3372 num_files = tc->filesindir;
3373
3374 /* we've overwritten the dircache so tree browser will need to be
3375 reloaded */
3376 reload_directory();
3377
3378 for (i=0; i<num_files; i++)
3379 {
3380 /* user abort */
3381 if (button_get(false) == SETTINGS_CANCEL)
3382 {
3383 result = -1;
3384 break;
3385 }
3386
3387 if (files[i].attr & ATTR_DIRECTORY)
3388 {
3389 if (recurse)
3390 {
3391 /* recursively add directories */
3392 snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
3393 result = playlist_directory_tracksearch(buf, recurse,
3394 callback, context);
3395 if (result < 0)
3396 break;
3397
3398 /* we now need to reload our current directory */
3399 if(ft_load(tc, dirname) < 0)
3400 {
3401 result = -1;
3402 break;
3403 }
3404
3405 files = (struct entry*) tc->dircache;
3406 num_files = tc->filesindir;
3407 if (!num_files)
3408 {
3409 result = -1;
3410 break;
3411 }
3412 }
3413 else
3414 continue;
3415 }
3416 else if ((files[i].attr & TREE_ATTR_MASK) == TREE_ATTR_MPA)
3417 {
3418 snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
3419
3420 if (callback(buf, context) != 0)
3421 {
3422 result = -1;
3423 break;
3424 }
3425
3426 /* let the other threads work */
3427 yield();
3428 }
3429 }
3430
3431 /* restore dirfilter */
3432 *(tc->dirfilter) = old_dirfilter;
3433
3434 return result;
3435}
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,
158int playlist_get_track_info(struct playlist_info* playlist, int index, 158int playlist_get_track_info(struct playlist_info* playlist, int index,
159 struct playlist_track_info* info); 159 struct playlist_track_info* info);
160int playlist_save(struct playlist_info* playlist, char *filename); 160int playlist_save(struct playlist_info* playlist, char *filename);
161int playlist_directory_tracksearch(const char* dirname, bool recurse,
162 int (*callback)(char*, void*),
163 void* context);
161 164
162#endif /* __PLAYLIST_H__ */ 165#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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006 Sebastian Henriksen, Hardeep Sidhu
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
20#include <stdbool.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include "action.h"
25#include "dir.h"
26#include "file.h"
27#include "filetree.h"
28#include "kernel.h"
29#include "keyboard.h"
30#include "lang.h"
31#include "list.h"
32#include "misc.h"
33#include "onplay.h"
34#include "playlist.h"
35#include "settings.h"
36#include "splash.h"
37#include "sprintf.h"
38#include "tree.h"
39#include "yesno.h"
40
41#define PLAYLIST_CATALOG_CFG ROCKBOX_DIR "/playlist_catalog.config"
42#define PLAYLIST_CATALOG_DEFAULT_DIR "/Playlists"
43#define MAX_PLAYLISTS 400
44#define PLAYLIST_DISPLAY_COUNT 10
45
46/* Use for recursive directory search */
47struct add_track_context {
48 int fd;
49 int count;
50};
51
52/* keep track of most recently used playlist */
53static char most_recent_playlist[MAX_PATH];
54
55/* directory where our playlists our stored (configured in
56 PLAYLIST_CATALOG_CFG) */
57static char playlist_dir[MAX_PATH];
58static int playlist_dir_length;
59static bool playlist_dir_exists = false;
60
61/* Retrieve playlist directory from config file and verify it exists */
62static int initialize_catalog(void)
63{
64 static bool initialized = false;
65
66 if (!initialized)
67 {
68 int f;
69 DIR* dir;
70 bool default_dir = true;
71
72 f = open(PLAYLIST_CATALOG_CFG, O_RDONLY);
73 if (f >= 0)
74 {
75 char buf[MAX_PATH+5];
76
77 while (read_line(f, buf, sizeof(buf)))
78 {
79 char* name;
80 char* value;
81
82 /* directory config is of the format: "dir: /path/to/dir" */
83 if (settings_parseline(buf, &name, &value) &&
84 !strncasecmp(name, "dir:", strlen(name)) &&
85 strlen(value) > 0)
86 {
87 strncpy(playlist_dir, value, strlen(value));
88 default_dir = false;
89 }
90 }
91
92 close(f);
93 }
94
95 /* fall back to default directory if no or invalid config */
96 if (default_dir)
97 strncpy(playlist_dir, PLAYLIST_CATALOG_DEFAULT_DIR,
98 sizeof(playlist_dir));
99
100 playlist_dir_length = strlen(playlist_dir);
101
102 dir = opendir(playlist_dir);
103 if (dir)
104 {
105 playlist_dir_exists = true;
106 closedir(dir);
107 memset(most_recent_playlist, 0, sizeof(most_recent_playlist));
108 initialized = true;
109 }
110 }
111
112 if (!playlist_dir_exists)
113 {
114 gui_syncsplash(HZ*2, true, str(LANG_CATALOG_NO_DIRECTORY),
115 playlist_dir);
116 return -1;
117 }
118
119 return 0;
120}
121
122/* Use the filetree functions to retrieve the list of playlists in the
123 directory */
124static int create_playlist_list(char** playlists, int num_items,
125 int* num_playlists)
126{
127 int result = -1;
128 int num_files = 0;
129 int index = 0;
130 int i;
131 bool most_recent = false;
132 struct entry *files;
133 struct tree_context* tc = tree_get_context();
134 int dirfilter = *(tc->dirfilter);
135
136 *num_playlists = 0;
137
138 /* use the tree browser dircache to load only playlists */
139 *(tc->dirfilter) = SHOW_PLAYLIST;
140
141 if (ft_load(tc, playlist_dir) < 0)
142 {
143 gui_syncsplash(HZ*2, true, str(LANG_CATALOG_NO_DIRECTORY),
144 playlist_dir);
145 goto exit;
146 }
147
148 files = (struct entry*) tc->dircache;
149 num_files = tc->filesindir;
150
151 /* we've overwritten the dircache so tree browser will need to be
152 reloaded */
153 reload_directory();
154
155 /* if it exists, most recent playlist will always be index 0 */
156 if (most_recent_playlist[0] != '\0')
157 {
158 index = 1;
159 most_recent = true;
160 }
161
162 for (i=0; i<num_files && index<num_items; i++)
163 {
164 if (files[i].attr & TREE_ATTR_M3U)
165 {
166 if (most_recent && !strncmp(files[i].name, most_recent_playlist,
167 sizeof(most_recent_playlist)))
168 {
169 playlists[0] = files[i].name;
170 most_recent = false;
171 }
172 else
173 {
174 playlists[index] = files[i].name;
175 index++;
176 }
177 }
178 }
179
180 *num_playlists = index;
181
182 /* we couldn't find the most recent playlist, shift all playlists up */
183 if (most_recent)
184 {
185 for (i=0; i<index-1; i++)
186 playlists[i] = playlists[i+1];
187
188 (*num_playlists)--;
189
190 most_recent_playlist[0] = '\0';
191 }
192
193 result = 0;
194
195exit:
196 *(tc->dirfilter) = dirfilter;
197 return result;
198}
199
200/* Callback for gui_synclist */
201static char* playlist_callback_name(int selected_item, void* data,
202 char* buffer)
203{
204 char** playlists = (char**) data;
205
206 strncpy(buffer, playlists[selected_item], MAX_PATH);
207
208 return buffer;
209}
210
211/* Display all playlists in catalog. Selected "playlist" is returned.
212 If "view" mode is set then we're not adding anything into playlist. */
213static int display_playlists(char* playlist, bool view)
214{
215 int result = -1;
216 int num_playlists = 0;
217 int lastbutton = BUTTON_NONE;
218 bool exit = false;
219 char temp_buf[MAX_PATH];
220 char* playlists[MAX_PLAYLISTS];
221 struct gui_synclist playlist_lists;
222
223 if (create_playlist_list(playlists, sizeof(playlists),
224 &num_playlists) != 0)
225 return -1;
226
227 if (num_playlists <= 0)
228 {
229 gui_syncsplash(HZ*2, true, str(LANG_CATALOG_NO_PLAYLISTS));
230 return -1;
231 }
232
233 if (!playlist)
234 playlist = temp_buf;
235
236 gui_synclist_init(&playlist_lists, playlist_callback_name, playlists,
237 false, 1);
238 gui_synclist_set_nb_items(&playlist_lists, num_playlists);
239 gui_synclist_draw(&playlist_lists);
240
241 while (!exit)
242 {
243 int button = button_get_w_tmo(HZ/2);
244 char* sel_file;
245
246 gui_synclist_do_button(&playlist_lists, button);
247
248 sel_file = playlists[gui_synclist_get_sel_pos(&playlist_lists)];
249
250 switch (button)
251 {
252 case TREE_EXIT:
253#ifdef TREE_RC_EXIT
254 case TREE_RC_EXIT:
255#endif
256#ifdef TREE_OFF
257 case TREE_OFF:
258#endif
259 exit = true;
260 break;
261
262#ifdef TREE_ENTER
263 case TREE_ENTER:
264 case TREE_ENTER | BUTTON_REPEAT:
265#endif
266#ifdef TREE_RC_RUN
267 case TREE_RC_RUN:
268#endif
269 case TREE_RUN:
270#ifdef TREE_RUN_PRE
271 if (((button == TREE_RUN)
272#ifdef TREE_RC_RUN_PRE
273 || (button == TREE_RC_RUN))
274 && ((lastbutton != TREE_RC_RUN_PRE)
275#endif
276 && (lastbutton != TREE_RUN_PRE)))
277 break;
278#endif
279
280 if (view)
281 {
282 /* In view mode, selecting a playlist starts playback */
283 if (playlist_create(playlist_dir, sel_file) != -1)
284 {
285 if (global_settings.playlist_shuffle)
286 playlist_shuffle(current_tick, -1);
287 playlist_start(0, 0);
288 }
289 }
290 else
291 {
292 /* we found the playlist we want to add to */
293 snprintf(playlist, MAX_PATH, "%s/%s", playlist_dir,
294 sel_file);
295 }
296
297 result = 0;
298 exit = true;
299 break;
300
301 case TREE_CONTEXT:
302#ifdef TREE_CONTEXT2
303 case TREE_CONTEXT2:
304#endif
305#ifdef TREE_RC_CONTEXT
306 case TREE_RC_CONTEXT:
307#endif
308 /* context menu only available in view mode */
309 if (view)
310 {
311 snprintf(playlist, MAX_PATH, "%s/%s", playlist_dir,
312 sel_file);
313
314 if (onplay(playlist, TREE_ATTR_M3U,
315 CONTEXT_TREE) != ONPLAY_OK)
316 {
317 result = 0;
318 exit = true;
319 }
320 else
321 gui_synclist_draw(&playlist_lists);
322 }
323 break;
324
325 case BUTTON_NONE:
326 gui_syncstatusbar_draw(&statusbars, false);
327 break;
328
329 default:
330 if(default_event_handler(button) == SYS_USB_CONNECTED)
331 {
332 result = -1;
333 exit = true;
334 }
335 break;
336 }
337
338 lastbutton = button;
339 }
340
341 return result;
342}
343
344/* display number of tracks inserted into playlists. Used for directory
345 insert */
346static void display_insert_count(int count)
347{
348 gui_syncsplash(0, true, str(LANG_PLAYLIST_INSERT_COUNT), count,
349#if CONFIG_KEYPAD == PLAYER_PAD
350 str(LANG_STOP_ABORT)
351#else
352 str(LANG_OFF_ABORT)
353#endif
354 );
355}
356
357/* Add specified track into playlist. Callback from directory insert */
358static int add_track_to_playlist(char* filename, void* context)
359{
360 struct add_track_context* c = (struct add_track_context*) context;
361
362 if (fdprintf(c->fd, "%s\n", filename) <= 0)
363 return -1;
364
365 (c->count)++;
366
367 if (((c->count)%PLAYLIST_DISPLAY_COUNT) == 0)
368 display_insert_count(c->count);
369
370 return 0;
371}
372
373/* Add "sel" file into specified "playlist". How to insert depends on type
374 of file */
375static int add_to_playlist(const char* playlist, char* sel, int sel_attr)
376{
377 int fd;
378 int result = -1;
379
380 fd = open(playlist, O_CREAT|O_WRONLY|O_APPEND);
381 if(fd < 0)
382 return result;
383
384 /* In case we're in the playlist directory */
385 reload_directory();
386
387 if ((sel_attr & TREE_ATTR_MASK) == TREE_ATTR_MPA)
388 {
389 /* append the selected file */
390 if (fdprintf(fd, "%s\n", sel) > 0)
391 result = 0;
392 }
393 else if ((sel_attr & TREE_ATTR_MASK) == TREE_ATTR_M3U)
394 {
395 /* append playlist */
396 int f, fs, i;
397 char buf[1024];
398
399 if(strcasecmp(playlist, sel) == 0)
400 goto exit;
401
402 f = open(sel, O_RDONLY);
403 if (f < 0)
404 goto exit;
405
406 fs = filesize(f);
407
408 for (i=0; i<fs;)
409 {
410 int n;
411
412 n = read(f, buf, sizeof(buf));
413 if (n < 0)
414 break;
415
416 if (write(fd, buf, n) < 0)
417 break;
418
419 i += n;
420 }
421
422 if (i >= fs)
423 result = 0;
424
425 close(f);
426 }
427 else if (sel_attr & ATTR_DIRECTORY)
428 {
429 /* search directory for tracks and append to playlist */
430 bool recurse = false;
431 char *lines[] = {
432 (char *)str(LANG_RECURSE_DIRECTORY_QUESTION),
433 sel
434 };
435 struct text_message message={lines, 2};
436 struct add_track_context context;
437
438 if (global_settings.recursive_dir_insert != RECURSE_ASK)
439 recurse = (bool)global_settings.recursive_dir_insert;
440 else
441 {
442 /* Ask if user wants to recurse directory */
443 recurse = (gui_syncyesno_run(&message, NULL, NULL)==YESNO_YES);
444 }
445
446 context.fd = fd;
447 context.count = 0;
448
449 display_insert_count(0);
450
451 result = playlist_directory_tracksearch(sel, recurse,
452 add_track_to_playlist, &context);
453
454 display_insert_count(context.count);
455 }
456
457exit:
458 close(fd);
459 return result;
460}
461
462bool catalog_view_playlists(void)
463{
464 if (initialize_catalog() == -1)
465 return false;
466
467 if (display_playlists(NULL, true) == -1)
468 return false;
469
470 return true;
471}
472
473bool catalog_add_to_a_playlist(char* sel, int sel_attr, bool new_playlist)
474{
475 char playlist[MAX_PATH];
476
477 if (initialize_catalog() == -1)
478 return false;
479
480 if (new_playlist)
481 {
482 snprintf(playlist, MAX_PATH, "%s/", playlist_dir);
483 if (kbd_input(playlist, MAX_PATH))
484 return false;
485
486 if(strlen(playlist) <= 4 ||
487 strcasecmp(&playlist[strlen(playlist)-4], ".m3u"))
488 strcat(playlist, ".m3u");
489 }
490 else
491 {
492 if (display_playlists(playlist, false) == -1)
493 return false;
494 }
495
496 if (add_to_playlist(playlist, sel, sel_attr) == 0)
497 {
498 strncpy(most_recent_playlist, playlist+playlist_dir_length+1,
499 sizeof(most_recent_playlist));
500 return true;
501 }
502 else
503 return false;
504}
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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006 Sebastian Henriksen, Hardeep Sidhu
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19#ifndef _PLAYLIST_CATALOG_H_
20#define _PLAYLIST_CATALOG_H_
21
22/*
23 * View list of playlists in catalog.
24 * ret : true if no error
25 */
26bool catalog_view_playlists(void);
27
28/*
29 * Add something to a playlist (new or select from list of playlists in
30 * catalog).
31 * sel : the path of the music file, playlist or directory to add
32 * sel_attr : the attributes that tell what type of file we're adding
33 * new_playlist : whether we want to create a new playlist or add to an
34 * existing one.
35 * ret : true if the file was successfully added
36 */
37bool catalog_add_to_a_playlist(char* sel, int sel_attr, bool new_playlist);
38
39#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 @@
28#include "playlist_viewer.h" 28#include "playlist_viewer.h"
29#include "talk.h" 29#include "talk.h"
30#include "lang.h" 30#include "lang.h"
31#include "playlist_catalog.h"
31#include "playlist_menu.h" 32#include "playlist_menu.h"
32 33
33static bool save_playlist(void) 34static bool save_playlist(void)
@@ -61,11 +62,12 @@ bool playlist_menu(void)
61 bool result; 62 bool result;
62 63
63 static const struct menu_item items[] = { 64 static const struct menu_item items[] = {
64 { ID2P(LANG_CREATE_PLAYLIST), create_playlist }, 65 { ID2P(LANG_CREATE_PLAYLIST), create_playlist },
65 { ID2P(LANG_VIEW_DYNAMIC_PLAYLIST), playlist_viewer }, 66 { ID2P(LANG_VIEW_DYNAMIC_PLAYLIST), playlist_viewer },
66 { ID2P(LANG_SAVE_DYNAMIC_PLAYLIST), save_playlist }, 67 { ID2P(LANG_SAVE_DYNAMIC_PLAYLIST), save_playlist },
67 { ID2P(LANG_RECURSE_DIRECTORY), recurse_directory }, 68 { ID2P(LANG_CATALOG), catalog_view_playlists },
68 { ID2P(LANG_WARN_ERASEDYNPLAYLIST_MENU), warnon_option}, 69 { ID2P(LANG_RECURSE_DIRECTORY), recurse_directory },
70 { ID2P(LANG_WARN_ERASEDYNPLAYLIST_MENU), warnon_option },
69 }; 71 };
70 72
71 m = menu_init( items, sizeof items / sizeof(struct menu_item), NULL, 73 m = menu_init( items, sizeof items / sizeof(struct menu_item), NULL,