summaryrefslogtreecommitdiff
path: root/apps/onplay.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/onplay.c')
-rw-r--r--apps/onplay.c995
1 files changed, 249 insertions, 746 deletions
diff --git a/apps/onplay.c b/apps/onplay.c
index 572138e583..d468c0a545 100644
--- a/apps/onplay.c
+++ b/apps/onplay.c
@@ -26,7 +26,6 @@
26 26
27#include "debug.h" 27#include "debug.h"
28#include "lcd.h" 28#include "lcd.h"
29#include "file.h"
30#include "audio.h" 29#include "audio.h"
31#include "menu.h" 30#include "menu.h"
32#include "lang.h" 31#include "lang.h"
@@ -43,6 +42,7 @@
43#include "talk.h" 42#include "talk.h"
44#include "onplay.h" 43#include "onplay.h"
45#include "filetypes.h" 44#include "filetypes.h"
45#include "fileop.h"
46#include "open_plugin.h" 46#include "open_plugin.h"
47#include "plugin.h" 47#include "plugin.h"
48#include "bookmark.h" 48#include "bookmark.h"
@@ -65,12 +65,9 @@
65#include "shortcuts.h" 65#include "shortcuts.h"
66#include "misc.h" 66#include "misc.h"
67 67
68static int context;
69static const char *selected_file = NULL;
70static char selected_file_path[MAX_PATH];
71static int selected_file_attr = 0;
72static int onplay_result = ONPLAY_OK; 68static int onplay_result = ONPLAY_OK;
73static bool in_queue_submenu = false; 69static bool in_queue_submenu = false;
70
74static bool (*ctx_current_playlist_insert)(int position, bool queue, bool create_new); 71static bool (*ctx_current_playlist_insert)(int position, bool queue, bool create_new);
75static int (*ctx_add_to_playlist)(const char* playlist, bool new_playlist); 72static int (*ctx_add_to_playlist)(const char* playlist, bool new_playlist);
76extern struct menu_item_ex file_menu; /* settings_menu.c */ 73extern struct menu_item_ex file_menu; /* settings_menu.c */
@@ -84,30 +81,13 @@ extern struct menu_item_ex file_menu; /* settings_menu.c */
84 MENU_ITEM_COUNT(sizeof( name##_)/sizeof(*name##_)), \ 81 MENU_ITEM_COUNT(sizeof( name##_)/sizeof(*name##_)), \
85 { (void*)name##_},{.callback_and_desc = & name##__}}; 82 { (void*)name##_},{.callback_and_desc = & name##__}};
86 83
87/* Used for directory move, copy and delete */ 84static struct selected_file
88struct dirrecurse_params
89{
90 char path[MAX_PATH]; /* Buffer for full path */
91 size_t append; /* Append position in 'path' for stack push */
92};
93
94enum clipboard_op_flags
95{
96 PASTE_CUT = 0x00, /* Is a move (cut) operation (default) */
97 PASTE_COPY = 0x01, /* Is a copy operation */
98 PASTE_OVERWRITE = 0x02, /* Overwrite destination */
99 PASTE_EXDEV = 0x04, /* Actually copy/move across volumes */
100};
101
102/* result codec of various onplay operations */
103enum onplay_result_code
104{ 85{
105 /* Anything < 0 is failure */ 86 char buf[MAX_PATH];
106 OPRC_SUCCESS = 0, /* All operations completed successfully */ 87 const char *path;
107 OPRC_NOOP = 1, /* Operation didn't need to do anything */ 88 int attr;
108 OPRC_CANCELLED = 2, /* Operation was cancelled by user */ 89 int context;
109 OPRC_NOOVERWRT = 3, 90} selected_file;
110};
111 91
112static struct clipboard 92static struct clipboard
113{ 93{
@@ -116,6 +96,14 @@ static struct clipboard
116 unsigned int flags; /* Operation type flags */ 96 unsigned int flags; /* Operation type flags */
117} clipboard; 97} clipboard;
118 98
99/* set selected file (doesn't touch buffer) */
100static void selected_file_set(int context, const char *path, int attr)
101{
102 selected_file.path = path;
103 selected_file.attr = attr;
104 selected_file.context = context;
105}
106
119/* Empty the clipboard */ 107/* Empty the clipboard */
120static void clipboard_clear_selection(struct clipboard *clip) 108static void clipboard_clear_selection(struct clipboard *clip)
121{ 109{
@@ -174,25 +162,21 @@ static int bookmark_menu_callback(int action,
174 struct gui_synclist *this_list) 162 struct gui_synclist *this_list)
175{ 163{
176 (void) this_list; 164 (void) this_list;
177 switch (action) 165 if (action == ACTION_REQUEST_MENUITEM)
178 { 166 {
179 case ACTION_REQUEST_MENUITEM: 167 /* hide loading bookmarks menu if no bookmarks exist */
180 /* hide loading bookmarks menu if no bookmarks exist */ 168 if (this_item == &bookmark_load_menu_item)
181 if (this_item == &bookmark_load_menu_item) 169 {
182 { 170 if (!bookmark_exists())
183 if (!bookmark_exists()) 171 return ACTION_EXIT_MENUITEM;
184 return ACTION_EXIT_MENUITEM; 172 }
185 }
186 break;
187 case ACTION_EXIT_MENUITEM:
188 settings_save();
189 break;
190 } 173 }
174 else if (action == ACTION_EXIT_MENUITEM)
175 settings_save();
176
191 return action; 177 return action;
192} 178}
193 179
194
195
196/* CONTEXT_WPS playlist options */ 180/* CONTEXT_WPS playlist options */
197static bool shuffle_playlist(void) 181static bool shuffle_playlist(void)
198{ 182{
@@ -244,46 +228,82 @@ MAKE_ONPLAYMENU( wps_playlist_menu, ID2P(LANG_CURRENT_PLAYLIST),
244 ); 228 );
245 229
246/* argument for add_to_playlist (for use by menu callbacks) */ 230/* argument for add_to_playlist (for use by menu callbacks) */
231#define PL_NONE 0x00
232#define PL_QUEUE 0x01
233#define PL_REPLACE 0x02
247struct add_to_pl_param 234struct add_to_pl_param
248{ 235{
249 int8_t position; 236 int8_t position;
250 unsigned int queue: 1; 237 uint8_t flags;
251 unsigned int replace: 1;
252}; 238};
253 239
254static struct add_to_pl_param addtopl_insert = {PLAYLIST_INSERT, 0, 0}; 240static struct add_to_pl_param addtopl_insert = {PLAYLIST_INSERT, PL_NONE};
255static struct add_to_pl_param addtopl_insert_first = {PLAYLIST_INSERT_FIRST, 0, 0}; 241static struct add_to_pl_param addtopl_insert_first = {PLAYLIST_INSERT_FIRST, PL_NONE};
256static struct add_to_pl_param addtopl_insert_last = {PLAYLIST_INSERT_LAST, 0, 0}; 242static struct add_to_pl_param addtopl_insert_last = {PLAYLIST_INSERT_LAST, PL_NONE};
257static struct add_to_pl_param addtopl_insert_shuf = {PLAYLIST_INSERT_SHUFFLED, 0, 0}; 243static struct add_to_pl_param addtopl_insert_shuf = {PLAYLIST_INSERT_SHUFFLED, PL_NONE};
258static struct add_to_pl_param addtopl_insert_last_shuf = {PLAYLIST_INSERT_LAST_SHUFFLED, 0, 0}; 244static struct add_to_pl_param addtopl_insert_last_shuf = {PLAYLIST_INSERT_LAST_SHUFFLED, PL_NONE};
245
246static struct add_to_pl_param addtopl_queue = {PLAYLIST_INSERT, PL_QUEUE};
247static struct add_to_pl_param addtopl_queue_first = {PLAYLIST_INSERT_FIRST, PL_QUEUE};
248static struct add_to_pl_param addtopl_queue_last = {PLAYLIST_INSERT_LAST, PL_QUEUE};
249static struct add_to_pl_param addtopl_queue_shuf = {PLAYLIST_INSERT_SHUFFLED, PL_QUEUE};
250static struct add_to_pl_param addtopl_queue_last_shuf = {PLAYLIST_INSERT_LAST_SHUFFLED, PL_QUEUE};
251
252static struct add_to_pl_param addtopl_replace = {PLAYLIST_INSERT, PL_REPLACE};
253static struct add_to_pl_param addtopl_replace_shuffled = {PLAYLIST_INSERT_LAST_SHUFFLED, PL_REPLACE};
254
255static void op_playlist_insert_selected(int position, bool queue)
256{
257#ifdef HAVE_TAGCACHE
258 if (selected_file.context == CONTEXT_STD && ctx_current_playlist_insert != NULL)
259 {
260 ctx_current_playlist_insert(position, queue, false);
261 return;
262 }
263#endif
264 if ((selected_file.attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
265 playlist_insert_track(NULL, selected_file.path, position, queue, true);
266 else if ((selected_file.attr & FILE_ATTR_MASK) == FILE_ATTR_M3U)
267 playlist_insert_playlist(NULL, selected_file.path, position, queue);
268 else if (selected_file.attr & ATTR_DIRECTORY)
269 {
270#ifdef HAVE_TAGCACHE
271 if (selected_file.context == CONTEXT_ID3DB)
272 {
273 tagtree_current_playlist_insert(position, queue);
274 return;
275 }
276#endif
277 bool recurse = (global_settings.recursive_dir_insert == RECURSE_ON);
278 if (global_settings.recursive_dir_insert == RECURSE_ASK)
279 {
259 280
260static struct add_to_pl_param addtopl_queue = {PLAYLIST_INSERT, 1, 0}; 281 const char *lines[] = {
261static struct add_to_pl_param addtopl_queue_first = {PLAYLIST_INSERT_FIRST, 1, 0}; 282 ID2P(LANG_RECURSE_DIRECTORY_QUESTION),
262static struct add_to_pl_param addtopl_queue_last = {PLAYLIST_INSERT_LAST, 1, 0}; 283 selected_file.path
263static struct add_to_pl_param addtopl_queue_shuf = {PLAYLIST_INSERT_SHUFFLED, 1, 0}; 284 };
264static struct add_to_pl_param addtopl_queue_last_shuf = {PLAYLIST_INSERT_LAST_SHUFFLED, 1, 0}; 285 const struct text_message message={lines, 2};
286 /* Ask if user wants to recurse directory */
287 recurse = (gui_syncyesno_run(&message, NULL, NULL)==YESNO_YES);
288 }
265 289
266static struct add_to_pl_param addtopl_replace = {PLAYLIST_INSERT, 0, 1}; 290 playlist_insert_directory(NULL, selected_file.path, position, queue,
267static struct add_to_pl_param addtopl_replace_shuffled = {PLAYLIST_INSERT_LAST_SHUFFLED, 0, 1}; 291 recurse == RECURSE_ON);
292 }
293}
268 294
269/* CONTEXT_[TREE|ID3DB|STD] playlist options */ 295/* CONTEXT_[TREE|ID3DB|STD] playlist options */
270static int add_to_playlist(void* arg) 296static int add_to_playlist(void* arg)
271{ 297{
272 struct add_to_pl_param* param = arg; 298 struct add_to_pl_param* param = arg;
273 int position = param->position; 299 int position = param->position;
274 bool new_playlist = !!param->replace; 300 bool new_playlist = (param->flags & PL_REPLACE) == PL_REPLACE;
275 bool queue = !!param->queue; 301 bool queue = (param->flags & PL_QUEUE) == PL_QUEUE;
276 302
277 /* warn if replacing the playlist */ 303 /* warn if replacing the playlist */
278 if (new_playlist && !warn_on_pl_erase()) 304 if (new_playlist && !warn_on_pl_erase())
279 return 0; 305 return 0;
280 306
281 const char *lines[] = {
282 ID2P(LANG_RECURSE_DIRECTORY_QUESTION),
283 selected_file
284 };
285 const struct text_message message={lines, 2};
286
287 splash(0, ID2P(LANG_WAIT)); 307 splash(0, ID2P(LANG_WAIT));
288 308
289 if (new_playlist && global_settings.keep_current_track_on_replace_playlist) 309 if (new_playlist && global_settings.keep_current_track_on_replace_playlist)
@@ -307,38 +327,7 @@ static int add_to_playlist(void* arg)
307 playlist_set_last_shuffled_start(); 327 playlist_set_last_shuffled_start();
308 } 328 }
309 329
310#ifdef HAVE_TAGCACHE 330 op_playlist_insert_selected(position, queue);
311 if ((context == CONTEXT_ID3DB) && (selected_file_attr & ATTR_DIRECTORY))
312 {
313 tagtree_current_playlist_insert(position, queue);
314 }
315 else if (context == CONTEXT_STD && ctx_current_playlist_insert != NULL)
316 {
317 ctx_current_playlist_insert(position, queue, false);
318 }
319 else
320#endif
321 {
322 if ((selected_file_attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
323 playlist_insert_track(NULL, selected_file, position, queue, true);
324 else if (selected_file_attr & ATTR_DIRECTORY)
325 {
326 bool recurse = false;
327
328 if (global_settings.recursive_dir_insert != RECURSE_ASK)
329 recurse = (bool)global_settings.recursive_dir_insert;
330 else
331 {
332 /* Ask if user wants to recurse directory */
333 recurse = (gui_syncyesno_run(&message, NULL, NULL)==YESNO_YES);
334 }
335
336 playlist_insert_directory(NULL, selected_file, position, queue,
337 recurse);
338 }
339 else if ((selected_file_attr & FILE_ATTR_MASK) == FILE_ATTR_M3U)
340 playlist_insert_playlist(NULL, selected_file, position, queue);
341 }
342 331
343 if (new_playlist && (playlist_amount() > 0)) 332 if (new_playlist && (playlist_amount() > 0))
344 { 333 {
@@ -358,7 +347,7 @@ static bool view_playlist(void)
358{ 347{
359 bool result; 348 bool result;
360 349
361 result = playlist_viewer_ex(selected_file, NULL); 350 result = playlist_viewer_ex(selected_file.path, NULL);
362 351
363 if (result == PLAYLIST_VIEWER_OK && 352 if (result == PLAYLIST_VIEWER_OK &&
364 onplay_result == ONPLAY_OK) 353 onplay_result == ONPLAY_OK)
@@ -447,19 +436,22 @@ MAKE_ONPLAYMENU(tree_playlist_menu, ID2P(LANG_PLAYING_NEXT),
447 &replace_pl_item, 436 &replace_pl_item,
448 &replace_shuf_pl_item 437 &replace_shuf_pl_item
449 ); 438 );
439
450static int treeplaylist_callback(int action, 440static int treeplaylist_callback(int action,
451 const struct menu_item_ex *this_item, 441 const struct menu_item_ex *this_item,
452 struct gui_synclist *this_list) 442 struct gui_synclist *this_list)
453{ 443{
454 (void)this_list; 444 (void)this_list;
445 int sel_file_attr = (selected_file.attr & FILE_ATTR_MASK);
446
455 switch (action) 447 switch (action)
456 { 448 {
457 case ACTION_REQUEST_MENUITEM: 449 case ACTION_REQUEST_MENUITEM:
458 if (this_item == &tree_playlist_menu) 450 if (this_item == &tree_playlist_menu)
459 { 451 {
460 if ((selected_file_attr & FILE_ATTR_MASK) != FILE_ATTR_AUDIO && 452 if (sel_file_attr != FILE_ATTR_AUDIO &&
461 (selected_file_attr & FILE_ATTR_MASK) != FILE_ATTR_M3U && 453 sel_file_attr != FILE_ATTR_M3U &&
462 (selected_file_attr & ATTR_DIRECTORY) == 0) 454 (selected_file.attr & ATTR_DIRECTORY) == 0)
463 return ACTION_EXIT_MENUITEM; 455 return ACTION_EXIT_MENUITEM;
464 } 456 }
465 else if (this_item == &queue_menu) 457 else if (this_item == &queue_menu)
@@ -476,7 +468,7 @@ static int treeplaylist_callback(int action,
476 { 468 {
477 struct add_to_pl_param *param = this_item->function_param->param; 469 struct add_to_pl_param *param = this_item->function_param->param;
478 470
479 if (param->queue) 471 if ((param->flags & PL_QUEUE) == PL_QUEUE)
480 { 472 {
481 if (global_settings.show_queue_options != QUEUE_SHOW_AT_TOPLEVEL && 473 if (global_settings.show_queue_options != QUEUE_SHOW_AT_TOPLEVEL &&
482 !in_queue_submenu) 474 !in_queue_submenu)
@@ -489,12 +481,12 @@ static int treeplaylist_callback(int action,
489 if (!global_settings.show_shuffled_adding_options) 481 if (!global_settings.show_shuffled_adding_options)
490 return ACTION_EXIT_MENUITEM; 482 return ACTION_EXIT_MENUITEM;
491 483
492 if ((selected_file_attr & FILE_ATTR_MASK) != FILE_ATTR_M3U && 484 if (sel_file_attr != FILE_ATTR_M3U &&
493 (selected_file_attr & ATTR_DIRECTORY) == 0) 485 (selected_file.attr & ATTR_DIRECTORY) == 0)
494 return ACTION_EXIT_MENUITEM; 486 return ACTION_EXIT_MENUITEM;
495 } 487 }
496 488
497 if (!param->replace) 489 if ((param->flags & PL_REPLACE) != PL_REPLACE)
498 { 490 {
499 if (!(audio_status() & AUDIO_STATUS_PLAY)) 491 if (!(audio_status() & AUDIO_STATUS_PLAY))
500 return ACTION_EXIT_MENUITEM; 492 return ACTION_EXIT_MENUITEM;
@@ -513,10 +505,8 @@ static int treeplaylist_callback(int action,
513 505
514void onplay_show_playlist_menu(const char* path, int attr, void (*playlist_insert_cb)) 506void onplay_show_playlist_menu(const char* path, int attr, void (*playlist_insert_cb))
515{ 507{
516 context = CONTEXT_STD;
517 ctx_current_playlist_insert = playlist_insert_cb; 508 ctx_current_playlist_insert = playlist_insert_cb;
518 selected_file = path; 509 selected_file_set(CONTEXT_STD, path, attr);
519 selected_file_attr = attr;
520 in_queue_submenu = false; 510 in_queue_submenu = false;
521 do_menu(&tree_playlist_menu, NULL, NULL, false); 511 do_menu(&tree_playlist_menu, NULL, NULL, false);
522} 512}
@@ -524,13 +514,13 @@ void onplay_show_playlist_menu(const char* path, int attr, void (*playlist_inser
524/* playlist catalog options */ 514/* playlist catalog options */
525static bool cat_add_to_a_playlist(void) 515static bool cat_add_to_a_playlist(void)
526{ 516{
527 return catalog_add_to_a_playlist(selected_file, selected_file_attr, 517 return catalog_add_to_a_playlist(selected_file.path, selected_file.attr,
528 false, NULL, ctx_add_to_playlist); 518 false, NULL, ctx_add_to_playlist);
529} 519}
530 520
531static bool cat_add_to_a_new_playlist(void) 521static bool cat_add_to_a_new_playlist(void)
532{ 522{
533 return catalog_add_to_a_playlist(selected_file, selected_file_attr, 523 return catalog_add_to_a_playlist(selected_file.path, selected_file.attr,
534 true, NULL, ctx_add_to_playlist); 524 true, NULL, ctx_add_to_playlist);
535} 525}
536 526
@@ -548,10 +538,8 @@ MAKE_ONPLAYMENU(cat_playlist_menu, ID2P(LANG_ADD_TO_PL),
548 538
549void onplay_show_playlist_cat_menu(const char* track_name, int attr, void (*add_to_pl_cb)) 539void onplay_show_playlist_cat_menu(const char* track_name, int attr, void (*add_to_pl_cb))
550{ 540{
551 context = CONTEXT_STD;
552 ctx_add_to_playlist = add_to_pl_cb; 541 ctx_add_to_playlist = add_to_pl_cb;
553 selected_file = track_name; 542 selected_file_set(CONTEXT_STD, track_name, attr);
554 selected_file_attr = attr;
555 do_menu(&cat_playlist_menu, NULL, NULL, false); 543 do_menu(&cat_playlist_menu, NULL, NULL, false);
556} 544}
557 545
@@ -561,515 +549,48 @@ static int cat_playlist_callback(int action,
561{ 549{
562 (void)this_item; 550 (void)this_item;
563 (void)this_list; 551 (void)this_list;
564 if (!selected_file || 552 if (!selected_file.path ||
565 (((selected_file_attr & FILE_ATTR_MASK) != FILE_ATTR_AUDIO) && 553 (((selected_file.attr & FILE_ATTR_MASK) != FILE_ATTR_AUDIO) &&
566 ((selected_file_attr & FILE_ATTR_MASK) != FILE_ATTR_M3U) && 554 ((selected_file.attr & FILE_ATTR_MASK) != FILE_ATTR_M3U) &&
567 ((selected_file_attr & ATTR_DIRECTORY) == 0))) 555 ((selected_file.attr & ATTR_DIRECTORY) == 0)))
568 { 556 {
569 return ACTION_EXIT_MENUITEM; 557 return ACTION_EXIT_MENUITEM;
570 } 558 }
571 559
572 switch (action) 560 if (action == ACTION_REQUEST_MENUITEM)
573 {
574 case ACTION_REQUEST_MENUITEM:
575 if ((audio_status() & AUDIO_STATUS_PLAY) || context != CONTEXT_WPS)
576 {
577 return action;
578 }
579 else
580 return ACTION_EXIT_MENUITEM;
581 break;
582 }
583 return action;
584}
585
586static void draw_slider(void)
587{
588 struct viewport *last_vp;
589 FOR_NB_SCREENS(i)
590 {
591 struct viewport vp;
592 int slider_height = 2*screens[i].getcharheight();
593 viewport_set_defaults(&vp, i);
594 last_vp = screens[i].set_viewport(&vp);
595 show_busy_slider(&screens[i], 1, vp.height - slider_height,
596 vp.width-2, slider_height-1);
597 screens[i].update_viewport();
598 screens[i].set_viewport(last_vp);
599 }
600}
601
602static void clear_display(bool update)
603{
604 struct viewport vp;
605 struct viewport *last_vp;
606 FOR_NB_SCREENS(i)
607 { 561 {
608 struct screen * screen = &screens[i]; 562 if ((audio_status() & AUDIO_STATUS_PLAY)
609 viewport_set_defaults(&vp, screen->screen_type); 563 || selected_file.context != CONTEXT_WPS)
610 last_vp = screen->set_viewport(&vp); 564 {
611 screen->clear_viewport(); 565 return action;
612 if (update) {
613 screen->update_viewport();
614 } 566 }
615 screen->set_viewport(last_vp); 567 return ACTION_EXIT_MENUITEM;
616 } 568 }
617} 569 return action;
618
619static void splash_path(const char *path)
620{
621 clear_display(false);
622 path_basename(path, &path);
623 splash(0, path);
624 draw_slider();
625}
626
627/* Splashes the path and checks the keys */
628static bool poll_cancel_action(const char *path)
629{
630 splash_path(path);
631 return ACTION_STD_CANCEL == get_action(CONTEXT_STD, TIMEOUT_NOBLOCK);
632}
633
634static bool check_new_name(const char *basename)
635{
636 /* at least prevent escapes out of the base directory from keyboard-
637 entered filenames; the file code should reject other invalidities */
638 return *basename != '\0' && !strchr(basename, PATH_SEPCH) &&
639 !is_dotdir_name(basename);
640} 570}
641 571
642static void splash_cancelled(void) 572static void splash_cancelled(void)
643{ 573{
644 clear_display(true); 574 clear_screen_buffer(true);
645 splash(HZ, ID2P(LANG_CANCEL)); 575 splash(HZ, ID2P(LANG_CANCEL));
646} 576}
647 577
648static void splash_failed(int lang_what) 578static void splash_failed(int lang_what, int err)
649{ 579{
650 cond_talk_ids_fq(lang_what, LANG_FAILED); 580 cond_talk_ids_fq(lang_what, LANG_FAILED);
651 clear_display(true); 581 clear_screen_buffer(true);
652 splashf(HZ*2, "%s %s", str(lang_what), str(LANG_FAILED)); 582 splashf(HZ*2, "%s %s (%d)", str(lang_what), str(LANG_FAILED), err);
653}
654
655/* helper function to remove a non-empty directory */
656static int remove_dir(struct dirrecurse_params *parm)
657{
658 DIR *dir = opendir(parm->path);
659 if (!dir) {
660 return -1; /* open error */
661 }
662
663 size_t append = parm->append;
664 int rc = OPRC_SUCCESS;
665
666 /* walk through the directory content */
667 while (rc == OPRC_SUCCESS) {
668 errno = 0; /* distinguish failure from eod */
669 struct dirent *entry = readdir(dir);
670 if (!entry) {
671 if (errno) {
672 rc = -1;
673 }
674 break;
675 }
676
677 struct dirinfo info = dir_get_info(dir, entry);
678 if ((info.attribute & ATTR_DIRECTORY) &&
679 is_dotdir_name(entry->d_name)) {
680 continue; /* skip these */
681 }
682
683 /* append name to current directory */
684 parm->append = append + path_append(&parm->path[append],
685 PA_SEP_HARD, entry->d_name,
686 sizeof (parm->path) - append);
687 if (parm->append >= sizeof (parm->path)) {
688 rc = -1;
689 break; /* no space left in buffer */
690 }
691
692 if (info.attribute & ATTR_DIRECTORY) {
693 /* remove a subdirectory */
694 rc = remove_dir(parm);
695 } else {
696 /* remove a file */
697 if (poll_cancel_action(parm->path)) {
698 rc = OPRC_CANCELLED;
699 break;
700 }
701
702 rc = remove(parm->path);
703 }
704
705 /* Remove basename we added above */
706 parm->path[append] = '\0';
707 }
708
709 closedir(dir);
710
711 if (rc == 0) {
712 /* remove the now empty directory */
713 if (poll_cancel_action(parm->path)) {
714 rc = OPRC_CANCELLED;
715 } else {
716 rc = rmdir(parm->path);
717 }
718 }
719
720 return rc;
721}
722
723/* share code for file and directory deletion, saves space */
724static int delete_file_dir(void)
725{
726 const char *to_delete=selected_file;
727 const int to_delete_attr=selected_file_attr;
728 if (confirm_delete_yesno(to_delete) != YESNO_YES) {
729 return 1;
730 }
731
732 clear_display(true);
733 splash(HZ/2, ID2P(LANG_DELETING));
734
735 int rc = -1;
736
737 if (to_delete_attr & ATTR_DIRECTORY) { /* true if directory */
738 struct dirrecurse_params parm;
739 parm.append = strlcpy(parm.path, to_delete, sizeof (parm.path));
740
741 if (parm.append < sizeof (parm.path)) {
742 cpu_boost(true);
743 rc = remove_dir(&parm);
744 cpu_boost(false);
745 }
746 } else {
747 rc = remove(to_delete);
748 }
749
750 if (rc < OPRC_SUCCESS) {
751 splash_failed(LANG_DELETE);
752 } else if (rc == OPRC_CANCELLED) {
753 splash_cancelled();
754 }
755
756 if (rc != OPRC_NOOP) {
757 /* Could have failed after some but not all needed changes; reload */
758 onplay_result = ONPLAY_RELOAD_DIR;
759 }
760
761 return 1;
762}
763
764static int rename_file(void)
765{
766 int rc = -1;
767 char newname[MAX_PATH];
768 const char *oldbase, *selection = selected_file;
769
770 path_basename(selection, &oldbase);
771 size_t pathlen = oldbase - selection;
772 char *newbase = newname + pathlen;
773
774 if (strmemccpy(newname, selection, sizeof (newname)) == NULL) {
775 /* Too long */
776 } else if (kbd_input(newbase, sizeof (newname) - pathlen, NULL) < 0) {
777 rc = OPRC_CANCELLED;
778 } else if (!strcmp(oldbase, newbase)) {
779 rc = OPRC_NOOP; /* No change at all */
780 } else if (check_new_name(newbase)) {
781 switch (relate(selection, newname))
782 {
783 case RELATE_DIFFERENT:
784 if (file_exists(newname)) {
785 break; /* don't overwrite */
786 }
787 /* Fall-through */
788 case RELATE_SAME:
789 rc = rename(selection, newname);
790 break;
791 case RELATE_PREFIX:
792 default:
793 break;
794 }
795 }
796
797 if (rc < OPRC_SUCCESS) {
798 splash_failed(LANG_RENAME);
799 } else if (rc == OPRC_CANCELLED) {
800 /* splash_cancelled(); kbd_input() splashes it */
801 } else if (rc == OPRC_SUCCESS) {
802 onplay_result = ONPLAY_RELOAD_DIR;
803 }
804
805 return 1;
806}
807
808static int create_dir(void)
809{
810 int rc = -1;
811 char dirname[MAX_PATH];
812 size_t pathlen = path_append(dirname, getcwd(NULL, 0), PA_SEP_HARD,
813 sizeof (dirname));
814 char *basename = dirname + pathlen;
815
816 if (pathlen >= sizeof (dirname)) {
817 /* Too long */
818 } else if (kbd_input(basename, sizeof (dirname) - pathlen, NULL) < 0) {
819 rc = OPRC_CANCELLED;
820 } else if (check_new_name(basename)) {
821 rc = mkdir(dirname);
822 }
823
824 if (rc < OPRC_SUCCESS) {
825 splash_failed(LANG_CREATE_DIR);
826 } else if (rc == OPRC_CANCELLED) {
827 /* splash_cancelled(); kbd_input() splashes it */
828 } else if (rc == OPRC_SUCCESS) {
829 onplay_result = ONPLAY_RELOAD_DIR;
830 }
831
832 return 1;
833}
834
835/* Paste a file */
836static int clipboard_pastefile(const char *src, const char *target,
837 unsigned int flags)
838{
839 int rc = -1;
840
841 while (!(flags & (PASTE_COPY | PASTE_EXDEV))) {
842 if ((flags & PASTE_OVERWRITE) || !file_exists(target)) {
843 /* Rename and possibly overwrite the file */
844 if (poll_cancel_action(src)) {
845 rc = OPRC_CANCELLED;
846 } else {
847 rc = rename(src, target);
848 }
849
850 #ifdef HAVE_MULTIVOLUME
851 if (rc < 0 && errno == EXDEV) {
852 /* Failed because cross volume rename doesn't work; force
853 a move instead */
854 flags |= PASTE_EXDEV;
855 break;
856 }
857 #endif /* HAVE_MULTIVOLUME */
858 }
859
860 return rc;
861 }
862
863 /* See if we can get the plugin buffer for the file copy buffer */
864 size_t buffersize;
865 char *buffer = (char *) plugin_get_buffer(&buffersize);
866 if (buffer == NULL || buffersize < 512) {
867 /* Not large enough, try for a disk sector worth of stack
868 instead */
869 buffersize = 512;
870 buffer = (char *)alloca(buffersize);
871 }
872
873 if (buffer == NULL) {
874 return -1;
875 }
876
877 buffersize &= ~0x1ff; /* Round buffer size to multiple of sector
878 size */
879
880 int src_fd = open(src, O_RDONLY);
881 if (src_fd >= 0) {
882 int oflag = O_WRONLY|O_CREAT;
883
884 if (!(flags & PASTE_OVERWRITE)) {
885 oflag |= O_EXCL;
886 }
887
888 int target_fd = open(target, oflag, 0666);
889 if (target_fd >= 0) {
890 off_t total_size = 0;
891 off_t next_cancel_test = 0; /* No excessive button polling */
892
893 rc = OPRC_SUCCESS;
894
895 while (rc == OPRC_SUCCESS) {
896 if (total_size >= next_cancel_test) {
897 next_cancel_test = total_size + 0x10000;
898 if (poll_cancel_action(src)) {
899 rc = OPRC_CANCELLED;
900 break;
901 }
902 }
903
904 ssize_t bytesread = read(src_fd, buffer, buffersize);
905 if (bytesread <= 0) {
906 if (bytesread < 0) {
907 rc = -1;
908 }
909 /* else eof on buffer boundary; nothing to write */
910 break;
911 }
912
913 ssize_t byteswritten = write(target_fd, buffer, bytesread);
914 if (byteswritten < bytesread) {
915 /* Some I/O error */
916 rc = -1;
917 break;
918 }
919
920 total_size += byteswritten;
921
922 if (bytesread < (ssize_t)buffersize) {
923 /* EOF with trailing bytes */
924 break;
925 }
926 }
927
928 if (rc == OPRC_SUCCESS) {
929 /* If overwriting, set the correct length if original was
930 longer */
931 rc = ftruncate(target_fd, total_size);
932 }
933
934 close(target_fd);
935
936 if (rc != OPRC_SUCCESS) {
937 /* Copy failed. Cleanup. */
938 remove(target);
939 }
940 }
941
942 close(src_fd);
943 }
944
945 if (rc == OPRC_SUCCESS && !(flags & PASTE_COPY)) {
946 /* Remove the source file */
947 rc = remove(src);
948 }
949
950 return rc;
951}
952
953/* Paste a directory */
954static int clipboard_pastedirectory(struct dirrecurse_params *src,
955 struct dirrecurse_params *target,
956 unsigned int flags)
957{
958 int rc = -1;
959
960 while (!(flags & (PASTE_COPY | PASTE_EXDEV))) {
961 if ((flags & PASTE_OVERWRITE) || !file_exists(target->path)) {
962 /* Just try to move the directory */
963 if (poll_cancel_action(src->path)) {
964 rc = OPRC_CANCELLED;
965 } else {
966 rc = rename(src->path, target->path);
967 }
968
969 if (rc < 0) {
970 int errnum = errno;
971 if (errnum == ENOTEMPTY && (flags & PASTE_OVERWRITE)) {
972 /* Directory is not empty thus rename() will not do a quick
973 overwrite */
974 break;
975 }
976 #ifdef HAVE_MULTIVOLUME
977 else if (errnum == EXDEV) {
978 /* Failed because cross volume rename doesn't work; force
979 a move instead */
980 flags |= PASTE_EXDEV;
981 break;
982 }
983 #endif /* HAVE_MULTIVOLUME */
984 }
985 }
986
987 return rc;
988 }
989
990 DIR *srcdir = opendir(src->path);
991
992 if (srcdir) {
993 /* Make a directory to copy things to */
994 rc = mkdir(target->path);
995 if (rc < 0 && errno == EEXIST && (flags & PASTE_OVERWRITE)) {
996 /* Exists and overwrite was approved */
997 rc = OPRC_SUCCESS;
998 }
999 }
1000
1001 size_t srcap = src->append, targetap = target->append;
1002
1003 /* Walk through the directory content; this loop will exit as soon as
1004 there's a problem */
1005 while (rc == OPRC_SUCCESS) {
1006 errno = 0; /* Distinguish failure from eod */
1007 struct dirent *entry = readdir(srcdir);
1008 if (!entry) {
1009 if (errno) {
1010 rc = -1;
1011 }
1012 break;
1013 }
1014
1015 struct dirinfo info = dir_get_info(srcdir, entry);
1016 if ((info.attribute & ATTR_DIRECTORY) &&
1017 is_dotdir_name(entry->d_name)) {
1018 continue; /* Skip these */
1019 }
1020
1021 /* Append names to current directories */
1022 src->append = srcap +
1023 path_append(&src->path[srcap], PA_SEP_HARD, entry->d_name,
1024 sizeof(src->path) - srcap);
1025
1026 target->append = targetap +
1027 path_append(&target->path[targetap], PA_SEP_HARD, entry->d_name,
1028 sizeof (target->path) - targetap);
1029
1030 if (src->append >= sizeof (src->path) ||
1031 target->append >= sizeof (target->path)) {
1032 rc = -1; /* No space left in buffer */
1033 break;
1034 }
1035
1036 if (poll_cancel_action(src->path)) {
1037 rc = OPRC_CANCELLED;
1038 break;
1039 }
1040
1041 DEBUGF("Copy %s to %s\n", src->path, target->path);
1042
1043 if (info.attribute & ATTR_DIRECTORY) {
1044 /* Copy/move a subdirectory */
1045 rc = clipboard_pastedirectory(src, target, flags); /* recursion */
1046 } else {
1047 /* Copy/move a file */
1048 rc = clipboard_pastefile(src->path, target->path, flags);
1049 }
1050
1051 /* Remove basenames we added above */
1052 src->path[srcap] = target->path[targetap] = '\0';
1053 }
1054
1055 if (rc == OPRC_SUCCESS && !(flags & PASTE_COPY)) {
1056 /* Remove the now empty directory */
1057 rc = rmdir(src->path);
1058 }
1059
1060 closedir(srcdir);
1061 return rc;
1062} 583}
1063 584
1064static bool clipboard_cut(void) 585static bool clipboard_cut(void)
1065{ 586{
1066 return clipboard_clip(&clipboard, selected_file, selected_file_attr, 587 return clipboard_clip(&clipboard, selected_file.path, selected_file.attr,
1067 PASTE_CUT); 588 PASTE_CUT);
1068} 589}
1069 590
1070static bool clipboard_copy(void) 591static bool clipboard_copy(void)
1071{ 592{
1072 return clipboard_clip(&clipboard, selected_file, selected_file_attr, 593 return clipboard_clip(&clipboard, selected_file.path, selected_file.attr,
1073 PASTE_COPY); 594 PASTE_COPY);
1074} 595}
1075 596
@@ -1079,82 +600,24 @@ static int clipboard_paste(void)
1079 if (!clipboard.path[0]) 600 if (!clipboard.path[0])
1080 return 1; 601 return 1;
1081 602
1082 int rc = -1; 603 int rc = copy_move_fileobject(clipboard.path, getcwd(NULL, 0), clipboard.flags);
1083
1084 struct dirrecurse_params src, target;
1085 unsigned int flags = clipboard.flags;
1086
1087 /* Figure out the name of the selection */
1088 const char *nameptr;
1089 path_basename(clipboard.path, &nameptr);
1090
1091 /* Final target is current directory plus name of selection */
1092 target.append = path_append(target.path, getcwd(NULL, 0),
1093 nameptr, sizeof (target.path));
1094
1095 switch (target.append < sizeof (target.path) ?
1096 relate(clipboard.path, target.path) : -1)
1097 {
1098 case RELATE_SAME:
1099 rc = OPRC_NOOP;
1100 break;
1101
1102 case RELATE_DIFFERENT:
1103 if (file_exists(target.path)) {
1104 /* If user chooses not to overwrite, cancel */
1105 if (confirm_overwrite_yesno() == YESNO_NO) {
1106 rc = OPRC_NOOVERWRT;
1107 break;
1108 }
1109
1110 flags |= PASTE_OVERWRITE;
1111 }
1112
1113 clear_display(true);
1114 splash(HZ/2, (flags & PASTE_COPY) ? ID2P(LANG_COPYING) :
1115 ID2P(LANG_MOVING));
1116
1117 /* Now figure out what we're doing */
1118 cpu_boost(true);
1119
1120 if (clipboard.attr & ATTR_DIRECTORY) {
1121 /* Copy or move a subdirectory */
1122 src.append = strlcpy(src.path, clipboard.path,
1123 sizeof (src.path));
1124 if (src.append < sizeof (src.path)) {
1125 rc = clipboard_pastedirectory(&src, &target, flags);
1126 }
1127 } else {
1128 /* Copy or move a file */
1129 rc = clipboard_pastefile(clipboard.path, target.path, flags);
1130 }
1131
1132 cpu_boost(false);
1133 break;
1134
1135 case RELATE_PREFIX:
1136 default: /* Some other relation / failure */
1137 break;
1138 }
1139
1140 clear_display(true);
1141 604
1142 switch (rc) 605 switch (rc)
1143 { 606 {
1144 case OPRC_CANCELLED: 607 case FORC_CANCELLED:
1145 splash_cancelled(); 608 splash_cancelled();
1146 /* Fallthrough */ 609 /* Fallthrough */
1147 case OPRC_SUCCESS: 610 case FORC_SUCCESS:
1148 onplay_result = ONPLAY_RELOAD_DIR; 611 onplay_result = ONPLAY_RELOAD_DIR;
1149 /* Fallthrough */ 612 /* Fallthrough */
1150 case OPRC_NOOP: 613 case FORC_NOOP:
1151 clipboard_clear_selection(&clipboard); 614 clipboard_clear_selection(&clipboard);
1152 /* Fallthrough */ 615 /* Fallthrough */
1153 case OPRC_NOOVERWRT: 616 case FORC_NOOVERWRT:
1154 break; 617 break;
1155 default: 618 default:
1156 if (rc < OPRC_SUCCESS) { 619 if (rc < FORC_SUCCESS) {
1157 splash_failed(LANG_PASTE); 620 splash_failed(LANG_PASTE, rc);
1158 onplay_result = ONPLAY_RELOAD_DIR; 621 onplay_result = ONPLAY_RELOAD_DIR;
1159 } 622 }
1160 } 623 }
@@ -1182,13 +645,10 @@ static int ratingitem_callback(int action,
1182{ 645{
1183 (void)this_item; 646 (void)this_item;
1184 (void)this_list; 647 (void)this_list;
1185 switch (action) 648 if (action == ACTION_REQUEST_MENUITEM)
1186 { 649 {
1187 case ACTION_REQUEST_MENUITEM: 650 if (!selected_file.path || !global_settings.runtimedb || !tagcache_is_usable())
1188 if (!selected_file || !global_settings.runtimedb || 651 return ACTION_EXIT_MENUITEM;
1189 !tagcache_is_usable())
1190 return ACTION_EXIT_MENUITEM;
1191 break;
1192 } 652 }
1193 return action; 653 return action;
1194} 654}
@@ -1215,13 +675,10 @@ static int view_cue_item_callback(int action,
1215 (void)this_item; 675 (void)this_item;
1216 (void)this_list; 676 (void)this_list;
1217 struct mp3entry* id3 = audio_current_track(); 677 struct mp3entry* id3 = audio_current_track();
1218 switch (action) 678 if (action == ACTION_REQUEST_MENUITEM)
1219 { 679 {
1220 case ACTION_REQUEST_MENUITEM: 680 if (!selected_file.path || !id3 || !id3->cuesheet)
1221 if (!selected_file 681 return ACTION_EXIT_MENUITEM;
1222 || !id3 || !id3->cuesheet)
1223 return ACTION_EXIT_MENUITEM;
1224 break;
1225 } 682 }
1226 return action; 683 return action;
1227} 684}
@@ -1249,13 +706,57 @@ MENUITEM_FUNCTION(pitch_screen_item, 0, ID2P(LANG_PITCH),
1249 gui_syncpitchscreen_run, NULL, Icon_Audio); 706 gui_syncpitchscreen_run, NULL, Icon_Audio);
1250#endif 707#endif
1251 708
709static int clipboard_delete_selected_fileobject(void)
710{
711 int rc = delete_fileobject(selected_file.path);
712 if (rc < FORC_SUCCESS) {
713 splash_failed(LANG_DELETE, rc);
714 } else if (rc == FORC_CANCELLED) {
715 splash_cancelled();
716 }
717 if (rc != FORC_NOOP) {
718 /* Could have failed after some but not all needed changes; reload */
719 onplay_result = ONPLAY_RELOAD_DIR;
720 }
721 return 1;
722}
723
724static void show_result(int rc, int lang_what)
725{
726 if (rc < FORC_SUCCESS) {
727 splash_failed(lang_what, rc);
728 } else if (rc == FORC_CANCELLED) {
729 /* splash_cancelled(); kbd_input() splashes it */
730 } else if (rc == FORC_SUCCESS) {
731 onplay_result = ONPLAY_RELOAD_DIR;
732 }
733}
734
735static int clipboard_create_dir(void)
736{
737 int rc = create_dir();
738
739 show_result(rc, LANG_CREATE_DIR);
740
741 return 1;
742}
743
744static int clipboard_rename_selected_file(void)
745{
746 int rc = rename_file(selected_file.path);
747
748 show_result(rc, LANG_RENAME);
749
750 return 1;
751}
752
1252/* CONTEXT_[TREE|ID3DB] items */ 753/* CONTEXT_[TREE|ID3DB] items */
1253static int clipboard_callback(int action, 754static int clipboard_callback(int action,
1254 const struct menu_item_ex *this_item, 755 const struct menu_item_ex *this_item,
1255 struct gui_synclist *this_list); 756 struct gui_synclist *this_list);
1256 757
1257MENUITEM_FUNCTION(rename_file_item, 0, ID2P(LANG_RENAME), 758MENUITEM_FUNCTION(rename_file_item, 0, ID2P(LANG_RENAME),
1258 rename_file, clipboard_callback, Icon_NOICON); 759 clipboard_rename_selected_file, clipboard_callback, Icon_NOICON);
1259MENUITEM_FUNCTION(clipboard_cut_item, 0, ID2P(LANG_CUT), 760MENUITEM_FUNCTION(clipboard_cut_item, 0, ID2P(LANG_CUT),
1260 clipboard_cut, clipboard_callback, Icon_NOICON); 761 clipboard_cut, clipboard_callback, Icon_NOICON);
1261MENUITEM_FUNCTION(clipboard_copy_item, 0, ID2P(LANG_COPY), 762MENUITEM_FUNCTION(clipboard_copy_item, 0, ID2P(LANG_COPY),
@@ -1263,16 +764,16 @@ MENUITEM_FUNCTION(clipboard_copy_item, 0, ID2P(LANG_COPY),
1263MENUITEM_FUNCTION(clipboard_paste_item, 0, ID2P(LANG_PASTE), 764MENUITEM_FUNCTION(clipboard_paste_item, 0, ID2P(LANG_PASTE),
1264 clipboard_paste, clipboard_callback, Icon_NOICON); 765 clipboard_paste, clipboard_callback, Icon_NOICON);
1265MENUITEM_FUNCTION(delete_file_item, 0, ID2P(LANG_DELETE), 766MENUITEM_FUNCTION(delete_file_item, 0, ID2P(LANG_DELETE),
1266 delete_file_dir, clipboard_callback, Icon_NOICON); 767 clipboard_delete_selected_fileobject, clipboard_callback, Icon_NOICON);
1267MENUITEM_FUNCTION(delete_dir_item, 0, ID2P(LANG_DELETE_DIR), 768MENUITEM_FUNCTION(delete_dir_item, 0, ID2P(LANG_DELETE_DIR),
1268 delete_file_dir, clipboard_callback, Icon_NOICON); 769 clipboard_delete_selected_fileobject, clipboard_callback, Icon_NOICON);
1269MENUITEM_FUNCTION(create_dir_item, 0, ID2P(LANG_CREATE_DIR), 770MENUITEM_FUNCTION(create_dir_item, 0, ID2P(LANG_CREATE_DIR),
1270 create_dir, clipboard_callback, Icon_NOICON); 771 clipboard_create_dir, clipboard_callback, Icon_NOICON);
1271 772
1272/* other items */ 773/* other items */
1273static bool list_viewers(void) 774static bool list_viewers(void)
1274{ 775{
1275 int ret = filetype_list_viewers(selected_file); 776 int ret = filetype_list_viewers(selected_file.path);
1276 if (ret == PLUGIN_USB_CONNECTED) 777 if (ret == PLUGIN_USB_CONNECTED)
1277 onplay_result = ONPLAY_RELOAD_DIR; 778 onplay_result = ONPLAY_RELOAD_DIR;
1278 return false; 779 return false;
@@ -1281,19 +782,19 @@ static bool list_viewers(void)
1281#ifdef HAVE_TAGCACHE 782#ifdef HAVE_TAGCACHE
1282static bool prepare_database_sel(void *param) 783static bool prepare_database_sel(void *param)
1283{ 784{
1284 if (context == CONTEXT_ID3DB && 785 if (selected_file.context == CONTEXT_ID3DB &&
1285 (selected_file_attr & FILE_ATTR_MASK) != FILE_ATTR_AUDIO) 786 (selected_file.attr & FILE_ATTR_MASK) != FILE_ATTR_AUDIO)
1286 { 787 {
1287 if (!strcmp(param, "properties")) 788 if (!strcmp(param, "properties"))
1288 strmemccpy(selected_file_path, MAKE_ACT_STR(ACTIVITY_DATABASEBROWSER), 789 strmemccpy(selected_file.buf, MAKE_ACT_STR(ACTIVITY_DATABASEBROWSER),
1289 sizeof(selected_file_path)); 790 sizeof(selected_file.buf));
1290 else if (!tagtree_get_subentry_filename(selected_file_path, MAX_PATH)) 791 else if (!tagtree_get_subentry_filename(selected_file.buf, MAX_PATH))
1291 { 792 {
1292 onplay_result = ONPLAY_RELOAD_DIR; 793 onplay_result = ONPLAY_RELOAD_DIR;
1293 return false; 794 return false;
1294 } 795 }
1295 796
1296 selected_file = selected_file_path; 797 selected_file.path = selected_file.buf;
1297 } 798 }
1298 return true; 799 return true;
1299} 800}
@@ -1305,7 +806,7 @@ static bool onplay_load_plugin(void *param)
1305 if (!prepare_database_sel(param)) 806 if (!prepare_database_sel(param))
1306 return false; 807 return false;
1307#endif 808#endif
1308 int ret = filetype_load_plugin((const char*)param, selected_file); 809 int ret = filetype_load_plugin((const char*)param, selected_file.path);
1309 if (ret == PLUGIN_USB_CONNECTED) 810 if (ret == PLUGIN_USB_CONNECTED)
1310 onplay_result = ONPLAY_RELOAD_DIR; 811 onplay_result = ONPLAY_RELOAD_DIR;
1311 else if (ret == PLUGIN_GOTO_PLUGIN) 812 else if (ret == PLUGIN_GOTO_PLUGIN)
@@ -1330,19 +831,25 @@ MENUITEM_FUNCTION_W_PARAM(pictureflow_item, 0, ID2P(LANG_ONPLAY_PICTUREFLOW),
1330#endif 831#endif
1331static bool onplay_add_to_shortcuts(void) 832static bool onplay_add_to_shortcuts(void)
1332{ 833{
1333 shortcuts_add(SHORTCUT_BROWSER, selected_file); 834 shortcuts_add(SHORTCUT_BROWSER, selected_file.path);
1334 return false; 835 return false;
1335} 836}
1336MENUITEM_FUNCTION(add_to_faves_item, 0, ID2P(LANG_ADD_TO_FAVES), 837MENUITEM_FUNCTION(add_to_faves_item, 0, ID2P(LANG_ADD_TO_FAVES),
1337 onplay_add_to_shortcuts, 838 onplay_add_to_shortcuts,
1338 clipboard_callback, Icon_NOICON); 839 clipboard_callback, Icon_NOICON);
1339 840
841static void set_dir_helper(char* dirnamebuf, size_t bufsz)
842{
843 path_append(dirnamebuf, selected_file.path, PA_SEP_HARD, bufsz);
844 settings_save();
845}
846
1340#if LCD_DEPTH > 1 847#if LCD_DEPTH > 1
1341static bool set_backdrop(void) 848static bool set_backdrop(void)
1342{ 849{
1343 path_append(global_settings.backdrop_file, selected_file, 850 set_dir_helper(global_settings.backdrop_file,
1344 PA_SEP_HARD, sizeof(global_settings.backdrop_file)); 851 sizeof(global_settings.backdrop_file));
1345 settings_save(); 852
1346 skin_backdrop_load_setting(); 853 skin_backdrop_load_setting();
1347 skin_backdrop_show(sb_get_backdrop(SCREEN_MAIN)); 854 skin_backdrop_show(sb_get_backdrop(SCREEN_MAIN));
1348 return true; 855 return true;
@@ -1353,9 +860,8 @@ MENUITEM_FUNCTION(set_backdrop_item, 0, ID2P(LANG_SET_AS_BACKDROP),
1353#ifdef HAVE_RECORDING 860#ifdef HAVE_RECORDING
1354static bool set_recdir(void) 861static bool set_recdir(void)
1355{ 862{
1356 path_append(global_settings.rec_directory, selected_file, 863 set_dir_helper(global_settings.rec_directory,
1357 PA_SEP_HARD, sizeof(global_settings.rec_directory)); 864 sizeof(global_settings.rec_directory));
1358 settings_save();
1359 return false; 865 return false;
1360} 866}
1361MENUITEM_FUNCTION(set_recdir_item, 0, ID2P(LANG_RECORDING_DIR), 867MENUITEM_FUNCTION(set_recdir_item, 0, ID2P(LANG_RECORDING_DIR),
@@ -1363,10 +869,8 @@ MENUITEM_FUNCTION(set_recdir_item, 0, ID2P(LANG_RECORDING_DIR),
1363#endif 869#endif
1364static bool set_startdir(void) 870static bool set_startdir(void)
1365{ 871{
1366 path_append(global_settings.start_directory, selected_file, 872 set_dir_helper(global_settings.start_directory,
1367 PA_SEP_HARD, sizeof(global_settings.start_directory)); 873 sizeof(global_settings.start_directory));
1368
1369 settings_save();
1370 return false; 874 return false;
1371} 875}
1372MENUITEM_FUNCTION(set_startdir_item, 0, ID2P(LANG_START_DIR), 876MENUITEM_FUNCTION(set_startdir_item, 0, ID2P(LANG_START_DIR),
@@ -1374,7 +878,7 @@ MENUITEM_FUNCTION(set_startdir_item, 0, ID2P(LANG_START_DIR),
1374 878
1375static bool set_catalogdir(void) 879static bool set_catalogdir(void)
1376{ 880{
1377 catalog_set_directory(selected_file); 881 catalog_set_directory(selected_file.path);
1378 settings_save(); 882 settings_save();
1379 return false; 883 return false;
1380} 884}
@@ -1384,16 +888,14 @@ MENUITEM_FUNCTION(set_catalogdir_item, 0, ID2P(LANG_PLAYLIST_DIR),
1384#ifdef HAVE_TAGCACHE 888#ifdef HAVE_TAGCACHE
1385static bool set_databasedir(void) 889static bool set_databasedir(void)
1386{ 890{
1387 path_append(global_settings.tagcache_db_path, selected_file,
1388 PA_SEP_SOFT, sizeof(global_settings.tagcache_db_path));
1389
1390 struct tagcache_stat *tc_stat = tagcache_get_stat(); 891 struct tagcache_stat *tc_stat = tagcache_get_stat();
1391 if (strcasecmp(global_settings.tagcache_db_path, tc_stat->db_path)) 892 if (strcasecmp(selected_file.path, tc_stat->db_path))
1392 { 893 {
1393 splash(HZ, ID2P(LANG_PLEASE_REBOOT)); 894 splash(HZ, ID2P(LANG_PLEASE_REBOOT));
1394 } 895 }
1395 896
1396 settings_save(); 897 set_dir_helper(global_settings.tagcache_db_path,
898 sizeof(global_settings.tagcache_db_path));
1397 return false; 899 return false;
1398} 900}
1399MENUITEM_FUNCTION(set_databasedir_item, 0, ID2P(LANG_DATABASE_DIR), 901MENUITEM_FUNCTION(set_databasedir_item, 0, ID2P(LANG_DATABASE_DIR),
@@ -1421,7 +923,7 @@ static int clipboard_callback(int action,
1421 case ACTION_REQUEST_MENUITEM: 923 case ACTION_REQUEST_MENUITEM:
1422#ifdef HAVE_MULTIVOLUME 924#ifdef HAVE_MULTIVOLUME
1423 /* no rename+delete for volumes */ 925 /* no rename+delete for volumes */
1424 if ((selected_file_attr & ATTR_VOLUME) && 926 if ((selected_file.attr & ATTR_VOLUME) &&
1425 (this_item == &rename_file_item || 927 (this_item == &rename_file_item ||
1426 this_item == &delete_dir_item || 928 this_item == &delete_dir_item ||
1427 this_item == &clipboard_cut_item || 929 this_item == &clipboard_cut_item ||
@@ -1429,7 +931,7 @@ static int clipboard_callback(int action,
1429 return ACTION_EXIT_MENUITEM; 931 return ACTION_EXIT_MENUITEM;
1430#endif 932#endif
1431#ifdef HAVE_TAGCACHE 933#ifdef HAVE_TAGCACHE
1432 if (context == CONTEXT_ID3DB) 934 if (selected_file.context == CONTEXT_ID3DB)
1433 { 935 {
1434 if (this_item == &track_info_item || 936 if (this_item == &track_info_item ||
1435 this_item == &pictureflow_item) 937 this_item == &pictureflow_item)
@@ -1447,21 +949,21 @@ static int clipboard_callback(int action,
1447 { 949 {
1448 return action; 950 return action;
1449 } 951 }
1450 else if (selected_file) 952 else if (selected_file.path)
1451 { 953 {
1452 /* requires an actual file */ 954 /* requires an actual file */
1453 if (this_item == &rename_file_item || 955 if (this_item == &rename_file_item ||
1454 this_item == &clipboard_cut_item || 956 this_item == &clipboard_cut_item ||
1455 this_item == &clipboard_copy_item || 957 this_item == &clipboard_copy_item ||
1456 (this_item == &track_info_item && 958 (this_item == &track_info_item &&
1457 (selected_file_attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO) || 959 (selected_file.attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO) ||
1458 (this_item == &properties_item && 960 (this_item == &properties_item &&
1459 (selected_file_attr & FILE_ATTR_MASK) != FILE_ATTR_AUDIO) || 961 (selected_file.attr & FILE_ATTR_MASK) != FILE_ATTR_AUDIO) ||
1460 this_item == &add_to_faves_item) 962 this_item == &add_to_faves_item)
1461 { 963 {
1462 return action; 964 return action;
1463 } 965 }
1464 else if ((selected_file_attr & ATTR_DIRECTORY)) 966 else if ((selected_file.attr & ATTR_DIRECTORY))
1465 { 967 {
1466 /* only for directories */ 968 /* only for directories */
1467 if (this_item == &delete_dir_item || 969 if (this_item == &delete_dir_item ||
@@ -1486,7 +988,7 @@ static int clipboard_callback(int action,
1486#if LCD_DEPTH > 1 988#if LCD_DEPTH > 1
1487 else if (this_item == &set_backdrop_item) 989 else if (this_item == &set_backdrop_item)
1488 { 990 {
1489 char *suffix = strrchr(selected_file, '.'); 991 char *suffix = strrchr(selected_file.path, '.');
1490 if (suffix) 992 if (suffix)
1491 { 993 {
1492 if (strcasecmp(suffix, ".bmp") == 0) 994 if (strcasecmp(suffix, ".bmp") == 0)
@@ -1560,8 +1062,8 @@ static int onplaymenu_callback(int action,
1560 case ACTION_REQUEST_MENUITEM: 1062 case ACTION_REQUEST_MENUITEM:
1561 if (this_item == &view_playlist_item) 1063 if (this_item == &view_playlist_item)
1562 { 1064 {
1563 if ((selected_file_attr & FILE_ATTR_MASK) == FILE_ATTR_M3U && 1065 if ((selected_file.attr & FILE_ATTR_MASK) == FILE_ATTR_M3U &&
1564 context == CONTEXT_TREE) 1066 selected_file.context == CONTEXT_TREE)
1565 return action; 1067 return action;
1566 } 1068 }
1567 return ACTION_EXIT_MENUITEM; 1069 return ACTION_EXIT_MENUITEM;
@@ -1569,6 +1071,8 @@ static int onplaymenu_callback(int action,
1569 case ACTION_EXIT_MENUITEM: 1071 case ACTION_EXIT_MENUITEM:
1570 return ACTION_EXIT_AFTER_THIS_MENUITEM; 1072 return ACTION_EXIT_AFTER_THIS_MENUITEM;
1571 break; 1073 break;
1074 default:
1075 break;
1572 } 1076 }
1573 return action; 1077 return action;
1574} 1078}
@@ -1579,26 +1083,26 @@ static bool hotkey_delete_item(void)
1579{ 1083{
1580#ifdef HAVE_MULTIVOLUME 1084#ifdef HAVE_MULTIVOLUME
1581 /* no delete for volumes */ 1085 /* no delete for volumes */
1582 if (selected_file_attr & ATTR_VOLUME) 1086 if (selected_file.attr & ATTR_VOLUME)
1583 return false; 1087 return false;
1584#endif 1088#endif
1585 1089
1586#ifdef HAVE_TAGCACHE 1090#ifdef HAVE_TAGCACHE
1587 if (context == CONTEXT_ID3DB && 1091 if (selected_file.context == CONTEXT_ID3DB &&
1588 (selected_file_attr & FILE_ATTR_MASK) != FILE_ATTR_AUDIO) 1092 (selected_file.attr & FILE_ATTR_MASK) != FILE_ATTR_AUDIO)
1589 return false; 1093 return false;
1590#endif 1094#endif
1591 1095
1592 return delete_file_dir(); 1096 return clipboard_delete_selected_fileobject();
1593} 1097}
1594 1098
1595static bool hotkey_open_with(void) 1099static bool hotkey_open_with(void)
1596{ 1100{
1597 /* only open files */ 1101 /* only open files */
1598 if (selected_file_attr & ATTR_DIRECTORY) 1102 if (selected_file.attr & ATTR_DIRECTORY)
1599 return false; 1103 return false;
1600#ifdef HAVE_MULTIVOLUME 1104#ifdef HAVE_MULTIVOLUME
1601 if (selected_file_attr & ATTR_VOLUME) 1105 if (selected_file.attr & ATTR_VOLUME)
1602 return false; 1106 return false;
1603#endif 1107#endif
1604 return list_viewers(); 1108 return list_viewers();
@@ -1607,8 +1111,8 @@ static bool hotkey_open_with(void)
1607static int hotkey_tree_pl_insert_shuffled(void) 1111static int hotkey_tree_pl_insert_shuffled(void)
1608{ 1112{
1609 if ((audio_status() & AUDIO_STATUS_PLAY) || 1113 if ((audio_status() & AUDIO_STATUS_PLAY) ||
1610 (selected_file_attr & ATTR_DIRECTORY) || 1114 (selected_file.attr & ATTR_DIRECTORY) ||
1611 ((selected_file_attr & FILE_ATTR_MASK) == FILE_ATTR_M3U)) 1115 ((selected_file.attr & FILE_ATTR_MASK) == FILE_ATTR_M3U))
1612 { 1116 {
1613 add_to_playlist(&addtopl_insert_shuf); 1117 add_to_playlist(&addtopl_insert_shuf);
1614 } 1118 }
@@ -1621,7 +1125,7 @@ static int hotkey_tree_run_plugin(void *param)
1621 if (!prepare_database_sel(param)) 1125 if (!prepare_database_sel(param))
1622 return ONPLAY_RELOAD_DIR; 1126 return ONPLAY_RELOAD_DIR;
1623#endif 1127#endif
1624 if (filetype_load_plugin((const char*)param, selected_file) == PLUGIN_GOTO_WPS) 1128 if (filetype_load_plugin((const char*)param, selected_file.path) == PLUGIN_GOTO_WPS)
1625 return ONPLAY_START_PLAY; 1129 return ONPLAY_START_PLAY;
1626 1130
1627 return ONPLAY_RELOAD_DIR; 1131 return ONPLAY_RELOAD_DIR;
@@ -1747,15 +1251,15 @@ static int execute_hotkey(bool is_wps)
1747} 1251}
1748#endif /* HOTKEY */ 1252#endif /* HOTKEY */
1749 1253
1750int onplay(char* file, int attr, int from, bool hotkey) 1254int onplay(char* file, int attr, int from_context, bool hotkey)
1751{ 1255{
1752 const struct menu_item_ex *menu; 1256 const struct menu_item_ex *menu;
1753 onplay_result = ONPLAY_OK; 1257 onplay_result = ONPLAY_OK;
1754 context = from;
1755 ctx_current_playlist_insert = NULL; 1258 ctx_current_playlist_insert = NULL;
1756 selected_file = NULL; 1259 selected_file_set(from_context, NULL, attr);
1260
1757#ifdef HAVE_TAGCACHE 1261#ifdef HAVE_TAGCACHE
1758 if (context == CONTEXT_ID3DB && 1262 if (from_context == CONTEXT_ID3DB &&
1759 (attr & FILE_ATTR_MASK) != FILE_ATTR_AUDIO) 1263 (attr & FILE_ATTR_MASK) != FILE_ATTR_AUDIO)
1760 { 1264 {
1761 ctx_add_to_playlist = tagtree_add_to_playlist; 1265 ctx_add_to_playlist = tagtree_add_to_playlist;
@@ -1763,8 +1267,8 @@ int onplay(char* file, int attr, int from, bool hotkey)
1763 { 1267 {
1764 /* add a leading slash so that catalog_add_to_a_playlist 1268 /* add a leading slash so that catalog_add_to_a_playlist
1765 later prefills the name when creating a new playlist */ 1269 later prefills the name when creating a new playlist */
1766 snprintf(selected_file_path, MAX_PATH, "/%s", file); 1270 snprintf(selected_file.buf, MAX_PATH, "/%s", file);
1767 selected_file = selected_file_path; 1271 selected_file.path = selected_file.buf;
1768 } 1272 }
1769 } 1273 }
1770 else 1274 else
@@ -1773,22 +1277,22 @@ int onplay(char* file, int attr, int from, bool hotkey)
1773 ctx_add_to_playlist = NULL; 1277 ctx_add_to_playlist = NULL;
1774 if (file != NULL) 1278 if (file != NULL)
1775 { 1279 {
1776 strmemccpy(selected_file_path, file, MAX_PATH); 1280 strmemccpy(selected_file.buf, file, MAX_PATH);
1777 selected_file = selected_file_path; 1281 selected_file.path = selected_file.buf;
1778 } 1282 }
1779 1283
1780 } 1284 }
1781 selected_file_attr = attr;
1782 int menu_selection; 1285 int menu_selection;
1286
1783#ifdef HAVE_HOTKEY 1287#ifdef HAVE_HOTKEY
1784 if (hotkey) 1288 if (hotkey)
1785 return execute_hotkey(context == CONTEXT_WPS); 1289 return execute_hotkey(from_context == CONTEXT_WPS);
1786#else 1290#else
1787 (void)hotkey; 1291 (void)hotkey;
1788#endif 1292#endif
1789 1293
1790 push_current_activity(ACTIVITY_CONTEXTMENU); 1294 push_current_activity(ACTIVITY_CONTEXTMENU);
1791 if (context == CONTEXT_WPS) 1295 if (from_context == CONTEXT_WPS)
1792 menu = &wps_onplay_menu; 1296 menu = &wps_onplay_menu;
1793 else 1297 else
1794 menu = &tree_onplay_menu; 1298 menu = &tree_onplay_menu;
@@ -1797,23 +1301,22 @@ int onplay(char* file, int attr, int from, bool hotkey)
1797 if (get_current_activity() == ACTIVITY_CONTEXTMENU) /* Activity may have been */ 1301 if (get_current_activity() == ACTIVITY_CONTEXTMENU) /* Activity may have been */
1798 pop_current_activity(); /* popped already by menu item */ 1302 pop_current_activity(); /* popped already by menu item */
1799 1303
1800 switch (menu_selection) 1304
1801 { 1305 if (menu_selection == GO_TO_WPS)
1802 case GO_TO_WPS: 1306 return ONPLAY_START_PLAY;
1803 return ONPLAY_START_PLAY; 1307 if (menu_selection == GO_TO_ROOT)
1804 case GO_TO_ROOT: 1308 return ONPLAY_MAINMENU;
1805 case GO_TO_MAINMENU: 1309 if (menu_selection == GO_TO_MAINMENU)
1806 return ONPLAY_MAINMENU; 1310 return ONPLAY_MAINMENU;
1807 case GO_TO_PLAYLIST_VIEWER: 1311 if (menu_selection == GO_TO_PLAYLIST_VIEWER)
1808 return ONPLAY_PLAYLIST; 1312 return ONPLAY_PLAYLIST;
1809 case GO_TO_PLUGIN: 1313 if (menu_selection == GO_TO_PLUGIN)
1810 return ONPLAY_PLUGIN; 1314 return ONPLAY_PLUGIN;
1811 default: 1315
1812 return onplay_result; 1316 return onplay_result;
1813 }
1814} 1317}
1815 1318
1816int get_onplay_context(void) 1319int get_onplay_context(void)
1817{ 1320{
1818 return context; 1321 return selected_file.context;
1819} 1322}