1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2002 Björn Stenberg
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.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
43 #include "statusbar.h"
44 #include "playlist_viewer.h"
47 #include "filetypes.h"
56 #ifdef HAVE_LCD_BITMAP
59 #include "main_menu.h"
60 #include "sound_menu.h"
62 #if CONFIG_CODEC == SWCODEC
65 #include "playlist_menu.h"
66 #include "playlist_catalog.h"
72 static char* selected_file
= NULL
;
73 static int selected_file_attr
= 0;
74 static int onplay_result
= ONPLAY_OK
;
75 static char clipboard_selection
[MAX_PATH
];
76 static int clipboard_selection_attr
= 0;
77 static bool clipboard_is_copy
= false;
79 /* For playlist options */
80 struct playlist_args
{
85 /* ----------------------------------------------------------------------- */
86 /* Displays the bookmark menu options for the user to decide. This is an */
87 /* interface function. */
88 /* ----------------------------------------------------------------------- */
89 bool bookmark_menu(void)
93 struct menu_item items
[3];
97 if ((audio_status() & AUDIO_STATUS_PLAY
))
99 items
[i
].desc
= ID2P(LANG_BOOKMARK_MENU_CREATE
);
100 items
[i
].function
= bookmark_create_menu
;
103 if (bookmark_exist())
105 items
[i
].desc
= ID2P(LANG_BOOKMARK_MENU_LIST
);
106 items
[i
].function
= bookmark_load_menu
;
111 m
=menu_init( items
, i
, NULL
, NULL
, NULL
, NULL
);
113 #ifdef HAVE_LCD_CHARCELLS
114 status_set_param(true);
116 result
= menu_run(m
);
117 #ifdef HAVE_LCD_CHARCELLS
118 status_set_param(false);
127 static bool list_viewers(void)
129 struct menu_item menu
[16];
133 i
=filetype_load_menu(menu
,sizeof(menu
)/sizeof(*menu
));
136 m
= menu_init( menu
, i
, NULL
, NULL
, NULL
, NULL
);
137 result
= menu_show(m
);
140 ret
= filetype_load_plugin((char *)menu
[result
].desc
,selected_file
);
144 /* FIX: translation! */
145 gui_syncsplash(HZ
*2, true, (unsigned char *)"No viewers found");
148 if (ret
== PLUGIN_USB_CONNECTED
)
149 onplay_result
= ONPLAY_RELOAD_DIR
;
154 static bool shuffle_playlist(void)
156 playlist_sort(NULL
, true);
157 playlist_randomise(NULL
, current_tick
, true);
162 static bool save_playlist(void)
164 save_playlist_screen(NULL
);
168 static bool add_to_playlist(int position
, bool queue
)
170 bool new_playlist
= !(audio_status() & AUDIO_STATUS_PLAY
);
172 (char *)str(LANG_RECURSE_DIRECTORY_QUESTION
),
175 struct text_message message
={lines
, 2};
177 gui_syncsplash(0, true, str(LANG_WAIT
));
180 playlist_create(NULL
, NULL
);
182 /* always set seed before inserting shuffled */
183 if (position
== PLAYLIST_INSERT_SHUFFLED
)
187 if (context
== CONTEXT_ID3DB
)
189 tagtree_insert_selection_playlist(position
, queue
);
194 if ((selected_file_attr
& TREE_ATTR_MASK
) == TREE_ATTR_MPA
)
195 playlist_insert_track(NULL
, selected_file
, position
, queue
, true);
196 else if (selected_file_attr
& ATTR_DIRECTORY
)
198 bool recurse
= false;
200 if (global_settings
.recursive_dir_insert
!= RECURSE_ASK
)
201 recurse
= (bool)global_settings
.recursive_dir_insert
;
204 /* Ask if user wants to recurse directory */
205 recurse
= (gui_syncyesno_run(&message
, NULL
, NULL
)==YESNO_YES
);
208 playlist_insert_directory(NULL
, selected_file
, position
, queue
,
211 else if ((selected_file_attr
& TREE_ATTR_MASK
) == TREE_ATTR_M3U
)
212 playlist_insert_playlist(NULL
, selected_file
, position
, queue
);
215 if (new_playlist
&& (playlist_amount() > 0))
217 /* nothing is currently playing so begin playing what we just
219 if (global_settings
.playlist_shuffle
)
220 playlist_shuffle(current_tick
, -1);
222 gui_syncstatusbar_draw(&statusbars
, false);
223 onplay_result
= ONPLAY_START_PLAY
;
229 static bool view_playlist(void)
231 bool was_playing
= audio_status() & AUDIO_STATUS_PLAY
;
234 result
= playlist_viewer_ex(selected_file
);
236 if (!was_playing
&& (audio_status() & AUDIO_STATUS_PLAY
) &&
237 onplay_result
== ONPLAY_OK
)
238 /* playlist was started from viewer */
239 onplay_result
= ONPLAY_START_PLAY
;
244 bool cat_add_to_a_playlist(void)
246 return catalog_add_to_a_playlist(selected_file
, selected_file_attr
,
250 bool cat_add_to_a_new_playlist(void)
252 return catalog_add_to_a_playlist(selected_file
, selected_file_attr
, true);
255 static bool cat_playlist_options(void)
257 struct menu_item items
[3];
261 if ((audio_status() & AUDIO_STATUS_PLAY
&& context
== CONTEXT_WPS
) ||
262 context
== CONTEXT_TREE
)
264 if (context
== CONTEXT_WPS
)
266 items
[i
].desc
= ID2P(LANG_CATALOG_VIEW
);
267 items
[i
].function
= catalog_view_playlists
;
271 items
[i
].desc
= ID2P(LANG_CATALOG_ADD_TO
);
272 items
[i
].function
= cat_add_to_a_playlist
;
274 items
[i
].desc
= ID2P(LANG_CATALOG_ADD_TO_NEW
);
275 items
[i
].function
= cat_add_to_a_new_playlist
;
279 m
= menu_init( items
, i
, NULL
, NULL
, NULL
, NULL
);
280 result
= menu_show(m
);
282 ret
= items
[result
].function();
288 /* Sub-menu for playlist options */
289 static bool playlist_options(void)
291 struct menu_item items
[13];
292 struct playlist_args args
[13]; /* increase these 2 if you add entries! */
293 int m
, i
=0, pstart
=0, result
;
296 if ((selected_file_attr
& TREE_ATTR_MASK
) == TREE_ATTR_M3U
&&
297 context
== CONTEXT_TREE
)
299 items
[i
].desc
= ID2P(LANG_VIEW
);
300 items
[i
].function
= view_playlist
;
305 if (audio_status() & AUDIO_STATUS_PLAY
&&
306 context
== CONTEXT_WPS
)
308 items
[i
].desc
= ID2P(LANG_VIEW_DYNAMIC_PLAYLIST
);
309 items
[i
].function
= playlist_viewer
;
313 items
[i
].desc
= ID2P(LANG_SEARCH_IN_PLAYLIST
);
314 items
[i
].function
= search_playlist
;
318 items
[i
].desc
= ID2P(LANG_SAVE_DYNAMIC_PLAYLIST
);
319 items
[i
].function
= save_playlist
;
323 items
[i
].desc
= ID2P(LANG_SHUFFLE_PLAYLIST
);
324 items
[i
].function
= shuffle_playlist
;
329 if (context
== CONTEXT_TREE
|| context
== CONTEXT_ID3DB
)
331 if (audio_status() & AUDIO_STATUS_PLAY
)
333 items
[i
].desc
= ID2P(LANG_INSERT
);
334 args
[i
].position
= PLAYLIST_INSERT
;
335 args
[i
].queue
= false;
338 items
[i
].desc
= ID2P(LANG_INSERT_FIRST
);
339 args
[i
].position
= PLAYLIST_INSERT_FIRST
;
340 args
[i
].queue
= false;
343 items
[i
].desc
= ID2P(LANG_INSERT_LAST
);
344 args
[i
].position
= PLAYLIST_INSERT_LAST
;
345 args
[i
].queue
= false;
348 items
[i
].desc
= ID2P(LANG_INSERT_SHUFFLED
);
349 args
[i
].position
= PLAYLIST_INSERT_SHUFFLED
;
350 args
[i
].queue
= false;
353 items
[i
].desc
= ID2P(LANG_QUEUE
);
354 args
[i
].position
= PLAYLIST_INSERT
;
355 args
[i
].queue
= true;
358 items
[i
].desc
= ID2P(LANG_QUEUE_FIRST
);
359 args
[i
].position
= PLAYLIST_INSERT_FIRST
;
360 args
[i
].queue
= true;
363 items
[i
].desc
= ID2P(LANG_QUEUE_LAST
);
364 args
[i
].position
= PLAYLIST_INSERT_LAST
;
365 args
[i
].queue
= true;
368 items
[i
].desc
= ID2P(LANG_QUEUE_SHUFFLED
);
369 args
[i
].position
= PLAYLIST_INSERT_SHUFFLED
;
370 args
[i
].queue
= true;
373 else if (((selected_file_attr
& TREE_ATTR_MASK
) == TREE_ATTR_MPA
) ||
374 (selected_file_attr
& ATTR_DIRECTORY
))
376 items
[i
].desc
= ID2P(LANG_INSERT
);
377 args
[i
].position
= PLAYLIST_INSERT_LAST
;
378 args
[i
].queue
= false;
381 if (selected_file_attr
& ATTR_DIRECTORY
)
383 items
[i
].desc
= ID2P(LANG_INSERT_SHUFFLED
);
384 args
[i
].position
= PLAYLIST_INSERT_SHUFFLED
;
385 args
[i
].queue
= false;
391 m
= menu_init( items
, i
, NULL
, NULL
, NULL
, NULL
);
392 result
= menu_show(m
);
393 if (result
>= 0 && result
< pstart
)
394 ret
= items
[result
].function();
395 else if (result
>= pstart
)
396 ret
= add_to_playlist(args
[result
].position
, args
[result
].queue
);
403 /* helper function to remove a non-empty directory */
404 static int remove_dir(char* dirname
, int len
)
408 int dirlen
= strlen(dirname
);
410 dir
= opendir(dirname
);
412 return -1; /* open error */
416 struct dirent
* entry
;
417 /* walk through the directory content */
418 entry
= readdir(dir
);
422 /* append name to current directory */
423 snprintf(dirname
+dirlen
, len
-dirlen
, "/%s", entry
->d_name
);
425 if (entry
->attribute
& ATTR_DIRECTORY
)
426 { /* remove a subdirectory */
427 if (!strcmp((char *)entry
->d_name
, ".") ||
428 !strcmp((char *)entry
->d_name
, ".."))
429 continue; /* skip these */
431 result
= remove_dir(dirname
, len
); /* recursion */
433 break; /* or better continue, delete what we can? */
436 { /* remove a file */
437 result
= remove(dirname
);
443 { /* remove the now empty directory */
444 dirname
[dirlen
] = '\0'; /* terminate to original length */
446 result
= rmdir(dirname
);
453 /* share code for file and directory deletion, saves space */
454 static bool delete_handler(bool is_dir
)
457 (char *)str(LANG_REALLY_DELETE
),
461 (char *)str(LANG_DELETED
),
465 struct text_message message
={lines
, 2};
466 struct text_message yes_message
={yes_lines
, 2};
467 if(gui_syncyesno_run(&message
, &yes_message
, NULL
)!=YESNO_YES
)
472 char pathname
[MAX_PATH
]; /* space to go deep */
473 strncpy(pathname
, selected_file
, sizeof pathname
);
474 res
= remove_dir(pathname
, sizeof(pathname
));
477 res
= remove(selected_file
);
480 onplay_result
= ONPLAY_RELOAD_DIR
;
486 static bool delete_file(void)
488 return delete_handler(false);
491 static bool delete_dir(void)
493 return delete_handler(true);
496 #ifdef HAVE_LCD_COLOR
497 static bool set_backdrop(void)
500 if(load_main_backdrop(selected_file
)) {
501 gui_syncsplash(HZ
, true, str(LANG_BACKDROP_LOADED
));
502 set_file(selected_file
, (char *)global_settings
.backdrop_file
, MAX_FILENAME
);
503 show_main_backdrop();
506 gui_syncsplash(HZ
, true, str(LANG_BACKDROP_FAILED
));
512 static bool rename_file(void)
514 char newname
[MAX_PATH
];
515 char* ptr
= strrchr(selected_file
, '/') + 1;
516 int pathlen
= (ptr
- selected_file
);
517 strncpy(newname
, selected_file
, sizeof newname
);
518 if (!kbd_input(newname
+ pathlen
, (sizeof newname
)-pathlen
)) {
519 if (!strlen(newname
+ pathlen
) ||
520 (rename(selected_file
, newname
) < 0)) {
522 lcd_puts(0,0,str(LANG_RENAME
));
523 lcd_puts(0,1,str(LANG_FAILED
));
528 onplay_result
= ONPLAY_RELOAD_DIR
;
534 bool create_dir(void)
536 char dirname
[MAX_PATH
];
541 cwd
= getcwd(NULL
, 0);
542 memset(dirname
, 0, sizeof dirname
);
544 snprintf(dirname
, sizeof dirname
, "%s/",
547 pathlen
= strlen(dirname
);
548 rc
= kbd_input(dirname
+ pathlen
, (sizeof dirname
)-pathlen
);
552 rc
= mkdir(dirname
, 0);
554 gui_syncsplash(HZ
, true, (unsigned char *)"%s %s",
555 str(LANG_CREATE_DIR
), str(LANG_FAILED
));
557 onplay_result
= ONPLAY_RELOAD_DIR
;
563 /* Store the current selection in the clipboard */
564 static bool clipboard_clip(bool copy
)
566 clipboard_selection
[0] = 0;
567 strncpy(clipboard_selection
, selected_file
, MAX_PATH
);
568 clipboard_selection_attr
= selected_file_attr
;
569 clipboard_is_copy
= copy
;
574 static bool clipboard_cut(void)
576 return clipboard_clip(false);
579 static bool clipboard_copy(void)
581 return clipboard_clip(true);
584 /* Paste a file to a new directory. Will overwrite always. */
585 static bool clipboard_pastefile(const char *src
, const char *target
, bool copy
)
587 int src_fd
, target_fd
, buffersize
, size
, bytesread
, byteswritten
;
592 /* See if we can get the plugin buffer for the file copy buffer */
593 buffer
= (char *) plugin_get_buffer(&buffersize
);
594 if (buffer
== NULL
|| buffersize
< 512) {
595 /* Not large enough, try for a disk sector worth of stack instead */
597 buffer
= (char *) __builtin_alloca(buffersize
);
600 if (buffer
== NULL
) {
604 buffersize
&= ~0x1ff; /* Round buffer size to multiple of sector size */
606 src_fd
= open(src
, O_RDONLY
);
609 target_fd
= creat(target
, O_WRONLY
);
611 if (target_fd
>= 0) {
614 size
= filesize(src_fd
);
621 bytesread
= read(src_fd
, buffer
, buffersize
);
623 if (bytesread
== -1) {
630 while(bytesread
> 0) {
631 byteswritten
= write(target_fd
, buffer
, bytesread
);
633 if (byteswritten
== -1) {
639 bytesread
-= byteswritten
;
645 /* Copy failed. Cleanup. */
654 result
= rename(src
, target
) == 0;
655 #ifdef HAVE_MULTIVOLUME
657 if (errno
== EXDEV
) {
658 /* Failed because cross volume rename doesn't work. Copy instead */
659 result
= clipboard_pastefile(src
, target
, true);
662 result
= remove(src
);
672 /* Paste a directory to a new location. Designed to be called by clipboard_paste */
673 static bool clipboard_pastedirectory(char *src
, int srclen
, char *target
, int targetlen
, bool copy
)
676 int srcdirlen
= strlen(src
);
677 int targetdirlen
= strlen(target
);
681 /* Check if the target exists */
682 fd
= open(target
, O_RDONLY
);
687 /* Just move the directory */
688 result
= rename(src
, target
) == 0;
690 #ifdef HAVE_MULTIVOLUME
691 if (!result
&& errno
== EXDEV
) {
692 /* Try a copy as we're going across devices */
693 result
= clipboard_pastedirectory(src
, srclen
, target
, targetlen
, true);
695 /* If it worked, remove the source directory */
697 remove_dir(src
, srclen
);
703 /* Make a directory to copy things to */
704 result
= mkdir(target
, 0) == 0;
708 /* Check if something went wrong already */
713 srcdir
= opendir(src
);
718 /* This loop will exit as soon as there's a problem */
721 struct dirent
* entry
;
722 /* walk through the directory content */
723 entry
= readdir(srcdir
);
727 /* append name to current directory */
728 snprintf(src
+srcdirlen
, srclen
-srcdirlen
, "/%s", entry
->d_name
);
729 snprintf(target
+targetdirlen
, targetlen
-targetdirlen
, "/%s", entry
->d_name
);
731 DEBUGF("Copy %s to %s\n", src
, target
);
733 if (entry
->attribute
& ATTR_DIRECTORY
)
734 { /* copy/move a subdirectory */
735 if (!strcmp((char *)entry
->d_name
, ".") ||
736 !strcmp((char *)entry
->d_name
, ".."))
737 continue; /* skip these */
739 result
= clipboard_pastedirectory(src
, srclen
, target
, targetlen
, copy
); /* recursion */
742 { /* copy/move a file */
743 result
= clipboard_pastefile(src
, target
, copy
);
750 src
[srcdirlen
] = '\0'; /* terminate to original length */
751 target
[targetdirlen
] = '\0'; /* terminate to original length */
757 /* Paste the clipboard to the current directory */
758 static bool clipboard_paste(void)
760 char target
[MAX_PATH
];
765 unsigned char *lines
[]={str(LANG_REALLY_OVERWRITE
)};
766 struct text_message message
={(char **)lines
, 1};
768 /* Get the name of the current directory */
769 cwd
= getcwd(NULL
, 0);
770 snprintf(target
, sizeof target
, "%s", cwd
[1] ? cwd
: "");
772 /* Figure out the name of the selection */
773 nameptr
= strrchr(clipboard_selection
, '/');
775 /* Paste the name on to the current directory to give us our final target */
776 strcat(target
, nameptr
);
778 /* Check if we're going to overwrite */
779 target_fd
= open(target
, O_RDONLY
);
782 /* If the target existed but they choose not to overwite, exit */
783 if (target_fd
>= 0 &&
784 (gui_syncyesno_run(&message
, NULL
, NULL
) == YESNO_NO
)) {
788 /* Now figure out what we're doing */
789 if (clipboard_selection_attr
& ATTR_DIRECTORY
) {
790 /* Recursion. Set up external stack */
791 char srcpath
[MAX_PATH
];
792 char targetpath
[MAX_PATH
];
794 strncpy(srcpath
, clipboard_selection
, sizeof srcpath
);
795 strncpy(targetpath
, target
, sizeof targetpath
);
797 success
= clipboard_pastedirectory(srcpath
, sizeof(srcpath
), target
, sizeof(targetpath
), clipboard_is_copy
);
799 success
= clipboard_pastefile(clipboard_selection
, target
, clipboard_is_copy
);
804 /* Reset everything */
805 clipboard_selection
[0] = 0;
806 clipboard_selection_attr
= 0;
807 clipboard_is_copy
= false;
809 /* Force reload of the current directory */
810 onplay_result
= ONPLAY_RELOAD_DIR
;
812 gui_syncsplash(HZ
, true, (unsigned char *)"%s %s",
813 str(LANG_PASTE
), str(LANG_FAILED
));
819 static bool exit_to_main
;
821 /* catch MENU_EXIT_MENU within context menu to call the main menu afterwards */
822 static int onplay_callback(int key
, int menu
)
826 if (key
== ACTION_STD_MENU
)
832 int onplay(char* file
, int attr
, int from
)
834 #if CONFIG_CODEC == SWCODEC
835 struct menu_item items
[14]; /* increase this if you add entries! */
837 struct menu_item items
[12];
840 #ifdef HAVE_LCD_COLOR
844 onplay_result
= ONPLAY_OK
;
846 exit_to_main
= false;
847 selected_file
= file
;
848 selected_file_attr
= attr
;
850 if (context
== CONTEXT_WPS
)
852 items
[i
].desc
= ID2P(LANG_SOUND_SETTINGS
);
853 items
[i
].function
= sound_menu
;
857 if (context
== CONTEXT_WPS
||
858 context
== CONTEXT_TREE
||
859 context
== CONTEXT_ID3DB
)
861 items
[i
].desc
= ID2P(LANG_PLAYLIST
);
862 items
[i
].function
= playlist_options
;
864 items
[i
].desc
= ID2P(LANG_CATALOG
);
865 items
[i
].function
= cat_playlist_options
;
869 if (context
== CONTEXT_WPS
)
871 items
[i
].desc
= ID2P(LANG_BOOKMARK_MENU
);
872 items
[i
].function
= bookmark_menu
;
878 if (context
== CONTEXT_WPS
)
880 items
[i
].desc
= ID2P(LANG_MENU_SHOW_ID3_INFO
);
881 items
[i
].function
= browse_id3
;
883 /* if(rundb_initialized)
885 items[i].desc = ID2P(LANG_MENU_SET_RATING);
886 items[i].function = set_rating;
891 #ifdef HAVE_MULTIVOLUME
892 if (!(attr
& ATTR_VOLUME
)) /* no rename+delete for volumes */
895 if (context
== CONTEXT_TREE
)
897 items
[i
].desc
= ID2P(LANG_RENAME
);
898 items
[i
].function
= rename_file
;
901 items
[i
].desc
= ID2P(LANG_CUT
);
902 items
[i
].function
= clipboard_cut
;
905 items
[i
].desc
= ID2P(LANG_COPY
);
906 items
[i
].function
= clipboard_copy
;
909 if (clipboard_selection
[0] != 0) /* Something in the clipboard? */
911 items
[i
].desc
= ID2P(LANG_PASTE
);
912 items
[i
].function
= clipboard_paste
;
917 if (!(attr
& ATTR_DIRECTORY
) && context
== CONTEXT_TREE
)
919 items
[i
].desc
= ID2P(LANG_DELETE
);
920 items
[i
].function
= delete_file
;
923 #ifdef HAVE_LCD_COLOR
924 suffix
= strrchr(file
, '.');
927 if (strcasecmp(suffix
, ".bmp") == 0)
929 items
[i
].desc
= ID2P(LANG_SET_AS_BACKDROP
);
930 items
[i
].function
= set_backdrop
;
938 if (context
== CONTEXT_TREE
)
940 items
[i
].desc
= ID2P(LANG_DELETE_DIR
);
941 items
[i
].function
= delete_dir
;
947 if (!(attr
& ATTR_DIRECTORY
))
949 items
[i
].desc
= ID2P(LANG_ONPLAY_OPEN_WITH
);
950 items
[i
].function
= list_viewers
;
956 if (strlen(clipboard_selection
) != 0)
958 items
[i
].desc
= ID2P(LANG_PASTE
);
959 items
[i
].function
= clipboard_paste
;
964 if (context
== CONTEXT_TREE
)
966 items
[i
].desc
= ID2P(LANG_CREATE_DIR
);
967 items
[i
].function
= create_dir
;
970 if (context
== CONTEXT_WPS
)
972 #ifdef HAVE_PITCHSCREEN
973 /* Pitch screen access */
974 items
[i
].desc
= ID2P(LANG_PITCH
);
975 items
[i
].function
= pitch_screen
;
978 #if CONFIG_CODEC == SWCODEC
979 /* Equalizer menu items */
980 items
[i
].desc
= ID2P(LANG_EQUALIZER_GRAPHICAL
);
981 items
[i
].function
= eq_menu_graphical
;
983 items
[i
].desc
= ID2P(LANG_EQUALIZER_BROWSE
);
984 items
[i
].function
= eq_browse_presets
;
989 /* DIY menu handling, since we want to exit after selection */
992 m
= menu_init( items
, i
, onplay_callback
, NULL
, NULL
, NULL
);
993 result
= menu_show(m
);
995 items
[result
].function();
999 result
= main_menu();
1001 #ifdef HAVE_LCD_BITMAP
1002 if (global_settings
.statusbar
)
1003 lcd_setmargins(0, STATUSBAR_HEIGHT
);
1005 lcd_setmargins(0, 0);
1009 return onplay_result
;