A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 1459 lines 49 kB view raw
1/*************************************************************************** 2 * __________ __ ___. 3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 * \/ \/ \/ \/ \/ 8 * $Id$ 9 * 10 * Copyright (C) 2002 Björn Stenberg 11 * 12 * This program is free software; you can redistribute it and/or 13 * modify it under the terms of the GNU General Public License 14 * as published by the Free Software Foundation; either version 2 15 * of the License, or (at your option) any later version. 16 * 17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 18 * KIND, either express or implied. 19 * 20 ****************************************************************************/ 21#include <errno.h> 22#include <stdio.h> 23#include <string.h> 24#include <stdlib.h> 25#include <stdbool.h> 26 27#include "debug.h" 28#include "lcd.h" 29#include "audio.h" 30#include "menu.h" 31#include "lang.h" 32#include "playlist.h" 33#include "button.h" 34#include "kernel.h" 35#include "keyboard.h" 36#include "mp3data.h" 37#include "metadata.h" 38#include "screens.h" 39#include "tree.h" 40#include "settings.h" 41#include "playlist_viewer.h" 42#include "talk.h" 43#include "onplay.h" 44#include "filetypes.h" 45#include "fileop.h" 46#include "open_plugin.h" 47#include "plugin.h" 48#include "bookmark.h" 49#include "action.h" 50#include "splash.h" 51#include "yesno.h" 52#include "menus/exported_menus.h" 53#include "icons.h" 54#include "sound_menu.h" 55#include "playlist_menu.h" 56#include "playlist_catalog.h" 57#ifdef HAVE_TAGCACHE 58#include "tagtree.h" 59#endif 60#include "cuesheet.h" 61#include "statusbar-skinned.h" 62#include "pitchscreen.h" 63#include "viewport.h" 64#include "pathfuncs.h" 65#include "shortcuts.h" 66#include "misc.h" 67#ifdef HAVE_DISK_STORAGE 68#include "storage.h" 69#endif 70 71static int onplay_result = ONPLAY_OK; 72static bool in_queue_submenu = false; 73 74static bool (*ctx_current_playlist_insert)(int position, bool queue, bool create_new); 75static int (*ctx_add_to_playlist)(const char* playlist, bool new_playlist); 76extern struct menu_item_ex file_menu; /* settings_menu.c */ 77 78/* redefine MAKE_MENU so the MENU_EXITAFTERTHISMENU flag can be added easily */ 79#define MAKE_ONPLAYMENU( name, str, callback, icon, ... ) \ 80 static const struct menu_item_ex *name##_[] = {__VA_ARGS__}; \ 81 static const struct menu_callback_with_desc name##__ = {callback,str,icon};\ 82 static const struct menu_item_ex name = \ 83 {MT_MENU|MENU_HAS_DESC|MENU_EXITAFTERTHISMENU| \ 84 MENU_ITEM_COUNT(sizeof( name##_)/sizeof(*name##_)), \ 85 { (void*)name##_},{.callback_and_desc = & name##__}}; 86 87static struct selected_file 88{ 89 char buf[MAX_PATH]; 90 const char *path; 91 int attr; 92 int context; 93} selected_file; 94 95static struct clipboard 96{ 97 char path[MAX_PATH]; /* Clipped file's path */ 98 unsigned int attr; /* Clipped file's attributes */ 99 unsigned int flags; /* Operation type flags */ 100} clipboard; 101 102/* set selected file (doesn't touch buffer) */ 103static void selected_file_set(int context, const char *path, int attr) 104{ 105 selected_file.path = path; 106 selected_file.attr = attr; 107 selected_file.context = context; 108} 109 110/* Empty the clipboard */ 111static void clipboard_clear_selection(struct clipboard *clip) 112{ 113 clip->path[0] = '\0'; 114 clip->attr = 0; 115 clip->flags = 0; 116} 117 118/* Store the selection in the clipboard */ 119static bool clipboard_clip(struct clipboard *clip, const char *path, 120 unsigned int attr, unsigned int flags) 121{ 122 /* if it fits it clips */ 123 if (strmemccpy(clip->path, path, sizeof (clip->path)) != NULL) 124 { 125 clip->attr = attr; 126 clip->flags = flags; 127 return true; 128 } 129 else { 130 clipboard_clear_selection(clip); 131 return false; 132 } 133} 134 135/* ----------------------------------------------------------------------- */ 136/* Displays the bookmark menu options for the user to decide. This is an */ 137/* interface function. */ 138/* ----------------------------------------------------------------------- */ 139 140 141static int bookmark_load_menu_wrapper(void) 142{ 143 if (get_current_activity() == ACTIVITY_CONTEXTMENU) /* get rid of parent activity */ 144 pop_current_activity_without_refresh(); /* when called from ctxt menu */ 145 146 return bookmark_load_menu(); 147} 148 149static int bookmark_menu_callback(int action, 150 const struct menu_item_ex *this_item, 151 struct gui_synclist *this_list); 152MENUITEM_FUNCTION(bookmark_create_menu_item, 0, 153 ID2P(LANG_BOOKMARK_MENU_CREATE), 154 bookmark_create_menu, 155 bookmark_menu_callback, Icon_Bookmark); 156MENUITEM_FUNCTION(bookmark_load_menu_item, 0, 157 ID2P(LANG_BOOKMARK_MENU_LIST), 158 bookmark_load_menu_wrapper, 159 bookmark_menu_callback, Icon_Bookmark); 160MAKE_ONPLAYMENU(bookmark_menu, ID2P(LANG_BOOKMARK_MENU), 161 bookmark_menu_callback, Icon_Bookmark, 162 &bookmark_create_menu_item, &bookmark_load_menu_item); 163static int bookmark_menu_callback(int action, 164 const struct menu_item_ex *this_item, 165 struct gui_synclist *this_list) 166{ 167 (void) this_list; 168 if (action == ACTION_REQUEST_MENUITEM) 169 { 170 /* hide loading bookmarks menu if no bookmarks exist */ 171 if (this_item == &bookmark_load_menu_item) 172 { 173 if (!bookmark_exists()) 174 return ACTION_EXIT_MENUITEM; 175 } 176 } 177 else if (action == ACTION_EXIT_MENUITEM) 178 settings_save(); 179 180 return action; 181} 182 183/* CONTEXT_WPS playlist options */ 184static bool shuffle_playlist(void) 185{ 186 if (!yesno_pop_confirm(ID2P(LANG_SHUFFLE))) 187 return false; 188 playlist_sort(NULL, true); 189 playlist_randomise(NULL, current_tick, true); 190 playlist_set_modified(NULL, true); 191 192 return false; 193} 194 195static bool save_playlist(void) 196{ 197 /* save_playlist_screen should load the newly saved playlist and resume */ 198 save_playlist_screen(NULL); 199 return false; 200} 201 202static int wps_view_cur_playlist(void) 203{ 204 if (get_current_activity() == ACTIVITY_CONTEXTMENU) /* get rid of parent activity */ 205 pop_current_activity_without_refresh(); /* when called from ctxt menu */ 206 207 playlist_viewer_ex(NULL, NULL); 208 209 return 0; 210} 211 212static void playing_time(void) 213{ 214 plugin_load(PLUGIN_APPS_DIR"/playing_time.rock", NULL); 215} 216 217#ifdef HAVE_ALBUMART 218static void view_album_art(void) 219{ 220 plugin_load(VIEWERS_DIR"/imageviewer.rock", NULL); 221} 222#endif 223 224MENUITEM_FUNCTION(wps_view_cur_playlist_item, 0, ID2P(LANG_VIEW_DYNAMIC_PLAYLIST), 225 wps_view_cur_playlist, NULL, Icon_NOICON); 226MENUITEM_FUNCTION(search_playlist_item, 0, ID2P(LANG_SEARCH_IN_PLAYLIST), 227 search_playlist, NULL, Icon_Playlist); 228MENUITEM_FUNCTION(playlist_save_item, 0, ID2P(LANG_SAVE_DYNAMIC_PLAYLIST), 229 save_playlist, NULL, Icon_Playlist); 230MENUITEM_FUNCTION(reshuffle_item, 0, ID2P(LANG_SHUFFLE_PLAYLIST), 231 shuffle_playlist, NULL, Icon_Playlist); 232MENUITEM_FUNCTION(playing_time_item, 0, ID2P(LANG_PLAYING_TIME), 233 playing_time, NULL, Icon_Playlist); 234MAKE_ONPLAYMENU( wps_playlist_menu, ID2P(LANG_CURRENT_PLAYLIST), 235 NULL, Icon_Playlist, 236 &wps_view_cur_playlist_item, &playlist_save_item, 237 &search_playlist_item, &reshuffle_item, &playing_time_item 238 ); 239 240/* argument for add_to_playlist (for use by menu callbacks) */ 241#define PL_NONE 0x00 242#define PL_QUEUE 0x01 243#define PL_REPLACE 0x02 244struct add_to_pl_param 245{ 246 int8_t position; 247 uint8_t flags; 248}; 249 250static struct add_to_pl_param addtopl_insert = {PLAYLIST_INSERT, PL_NONE}; 251static struct add_to_pl_param addtopl_insert_first = {PLAYLIST_INSERT_FIRST, PL_NONE}; 252static struct add_to_pl_param addtopl_insert_last = {PLAYLIST_INSERT_LAST, PL_NONE}; 253static struct add_to_pl_param addtopl_insert_shuf = {PLAYLIST_INSERT_SHUFFLED, PL_NONE}; 254static struct add_to_pl_param addtopl_insert_last_shuf = {PLAYLIST_INSERT_LAST_SHUFFLED, PL_NONE}; 255 256static struct add_to_pl_param addtopl_queue = {PLAYLIST_INSERT, PL_QUEUE}; 257static struct add_to_pl_param addtopl_queue_first = {PLAYLIST_INSERT_FIRST, PL_QUEUE}; 258static struct add_to_pl_param addtopl_queue_last = {PLAYLIST_INSERT_LAST, PL_QUEUE}; 259static struct add_to_pl_param addtopl_queue_shuf = {PLAYLIST_INSERT_SHUFFLED, PL_QUEUE}; 260static struct add_to_pl_param addtopl_queue_last_shuf = {PLAYLIST_INSERT_LAST_SHUFFLED, PL_QUEUE}; 261 262static struct add_to_pl_param addtopl_replace = {PLAYLIST_INSERT, PL_REPLACE}; 263static struct add_to_pl_param addtopl_replace_shuffled = {PLAYLIST_INSERT_LAST_SHUFFLED, PL_REPLACE}; 264 265static void op_playlist_insert_selected(int position, bool queue) 266{ 267#ifdef HAVE_TAGCACHE 268 if (selected_file.context == CONTEXT_STD && ctx_current_playlist_insert != NULL) 269 { 270 ctx_current_playlist_insert(position, queue, false); 271 return; 272 } 273 else if (selected_file.context == CONTEXT_ID3DB) 274 { 275 tagtree_current_playlist_insert(position, queue); 276 return; 277 } 278#endif 279 if ((selected_file.attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO) 280 playlist_insert_track(NULL, selected_file.path, position, queue, true); 281 else if ((selected_file.attr & FILE_ATTR_MASK) == FILE_ATTR_M3U) 282 playlist_insert_playlist(NULL, selected_file.path, position, queue); 283 else if (selected_file.attr & ATTR_DIRECTORY) 284 { 285 bool recurse = (global_settings.recursive_dir_insert == RECURSE_ON); 286 if (global_settings.recursive_dir_insert == RECURSE_ASK) 287 { 288 289 const char *lines[] = { 290 ID2P(LANG_RECURSE_DIRECTORY_QUESTION), 291 selected_file.path 292 }; 293 const struct text_message message={lines, 2}; 294 /* Ask if user wants to recurse directory */ 295 recurse = (gui_syncyesno_run(&message, NULL, NULL)==YESNO_YES); 296 } 297 298 playlist_insert_directory(NULL, selected_file.path, position, queue, 299 recurse == RECURSE_ON); 300 } 301} 302 303/* CONTEXT_[TREE|ID3DB|STD] playlist options */ 304static int add_to_playlist(void* arg) 305{ 306 struct add_to_pl_param* param = arg; 307 int position = param->position; 308 bool new_playlist = (param->flags & PL_REPLACE) == PL_REPLACE; 309 bool queue = (param->flags & PL_QUEUE) == PL_QUEUE; 310 311 /* warn if replacing the playlist */ 312 if (new_playlist && !warn_on_pl_erase()) 313 return 1; 314 315 splash(0, ID2P(LANG_WAIT)); 316 317 if (new_playlist && global_settings.keep_current_track_on_replace_playlist) 318 { 319 if (audio_status() & AUDIO_STATUS_PLAY) 320 { 321 playlist_remove_all_tracks(NULL); 322 new_playlist = false; 323 } 324 } 325 326 if (new_playlist) 327 playlist_create(NULL, NULL); 328 329 /* always set seed before inserting shuffled */ 330 if (position == PLAYLIST_INSERT_SHUFFLED || 331 position == PLAYLIST_INSERT_LAST_SHUFFLED) 332 { 333 srand(current_tick); 334 if (position == PLAYLIST_INSERT_LAST_SHUFFLED) 335 playlist_set_last_shuffled_start(); 336 } 337 338 op_playlist_insert_selected(position, queue); 339 340 if (new_playlist && (playlist_amount() > 0)) 341 { 342 /* nothing is currently playing so begin playing what we just 343 inserted */ 344 if (global_settings.playlist_shuffle) 345 playlist_shuffle(current_tick, -1); 346 playlist_start(0, 0, 0); 347 onplay_result = ONPLAY_START_PLAY; 348 } 349 350 playlist_set_modified(NULL, true); 351 return 0; 352} 353 354static bool view_playlist(void) 355{ 356 bool result; 357 358 result = playlist_viewer_ex(selected_file.path, NULL); 359 360 if (result == PLAYLIST_VIEWER_OK && 361 onplay_result == ONPLAY_OK) 362 /* playlist was started from viewer */ 363 onplay_result = ONPLAY_START_PLAY; 364 365 return result; 366} 367 368static int treeplaylist_callback(int action, 369 const struct menu_item_ex *this_item, 370 struct gui_synclist *this_list); 371 372/* insert items */ 373MENUITEM_FUNCTION_W_PARAM(i_pl_item, 0, ID2P(LANG_ADD), 374 add_to_playlist, &addtopl_insert, 375 treeplaylist_callback, Icon_Playlist); 376MENUITEM_FUNCTION_W_PARAM(i_first_pl_item, 0, ID2P(LANG_PLAY_NEXT), 377 add_to_playlist, &addtopl_insert_first, 378 treeplaylist_callback, Icon_Playlist); 379MENUITEM_FUNCTION_W_PARAM(i_last_pl_item, 0, ID2P(LANG_PLAY_LAST), 380 add_to_playlist, &addtopl_insert_last, 381 treeplaylist_callback, Icon_Playlist); 382MENUITEM_FUNCTION_W_PARAM(i_shuf_pl_item, 0, ID2P(LANG_ADD_SHUFFLED), 383 add_to_playlist, &addtopl_insert_shuf, 384 treeplaylist_callback, Icon_Playlist); 385MENUITEM_FUNCTION_W_PARAM(i_last_shuf_pl_item, 0, ID2P(LANG_PLAY_LAST_SHUFFLED), 386 add_to_playlist, &addtopl_insert_last_shuf, 387 treeplaylist_callback, Icon_Playlist); 388/* queue items */ 389MENUITEM_FUNCTION_W_PARAM(q_pl_item, 0, ID2P(LANG_QUEUE), 390 add_to_playlist, &addtopl_queue, 391 treeplaylist_callback, Icon_Playlist); 392MENUITEM_FUNCTION_W_PARAM(q_first_pl_item, 0, ID2P(LANG_QUEUE_FIRST), 393 add_to_playlist, &addtopl_queue_first, 394 treeplaylist_callback, Icon_Playlist); 395MENUITEM_FUNCTION_W_PARAM(q_last_pl_item, 0, ID2P(LANG_QUEUE_LAST), 396 add_to_playlist, &addtopl_queue_last, 397 treeplaylist_callback, Icon_Playlist); 398MENUITEM_FUNCTION_W_PARAM(q_shuf_pl_item, 0, ID2P(LANG_QUEUE_SHUFFLED), 399 add_to_playlist, &addtopl_queue_shuf, 400 treeplaylist_callback, Icon_Playlist); 401MENUITEM_FUNCTION_W_PARAM(q_last_shuf_pl_item, 0, ID2P(LANG_QUEUE_LAST_SHUFFLED), 402 add_to_playlist, &addtopl_queue_last_shuf, 403 treeplaylist_callback, Icon_Playlist); 404 405/* queue submenu */ 406MAKE_ONPLAYMENU(queue_menu, ID2P(LANG_QUEUE_MENU), 407 treeplaylist_callback, Icon_Playlist, 408 &q_first_pl_item, 409 &q_pl_item, 410 &q_shuf_pl_item, 411 &q_last_pl_item, 412 &q_last_shuf_pl_item); 413 414/* replace playlist */ 415MENUITEM_FUNCTION_W_PARAM(replace_pl_item, 0, ID2P(LANG_PLAY), 416 add_to_playlist, &addtopl_replace, 417 treeplaylist_callback, Icon_Playlist); 418 419MENUITEM_FUNCTION_W_PARAM(replace_shuf_pl_item, 0, ID2P(LANG_PLAY_SHUFFLED), 420 add_to_playlist, &addtopl_replace_shuffled, 421 treeplaylist_callback, Icon_Playlist); 422 423MAKE_ONPLAYMENU(tree_playlist_menu, ID2P(LANG_PLAYING_NEXT), 424 treeplaylist_callback, Icon_Playlist, 425 426 /* insert */ 427 &i_first_pl_item, 428 &i_pl_item, 429 &i_last_pl_item, 430 &i_shuf_pl_item, 431 &i_last_shuf_pl_item, 432 433 /* queue */ 434 &q_first_pl_item, 435 &q_pl_item, 436 &q_last_pl_item, 437 &q_shuf_pl_item, 438 &q_last_shuf_pl_item, 439 440 /* Queue submenu */ 441 &queue_menu, 442 443 /* replace */ 444 &replace_pl_item, 445 &replace_shuf_pl_item 446 ); 447 448static int treeplaylist_callback(int action, 449 const struct menu_item_ex *this_item, 450 struct gui_synclist *this_list) 451{ 452 (void)this_list; 453 int sel_file_attr = (selected_file.attr & FILE_ATTR_MASK); 454 455 switch (action) 456 { 457 case ACTION_REQUEST_MENUITEM: 458 if (this_item == &tree_playlist_menu) 459 { 460 if (sel_file_attr != FILE_ATTR_AUDIO && 461 sel_file_attr != FILE_ATTR_M3U && 462 (selected_file.attr & ATTR_DIRECTORY) == 0) 463 return ACTION_EXIT_MENUITEM; 464 } 465 else if (this_item == &queue_menu) 466 { 467 if (global_settings.show_queue_options != QUEUE_SHOW_IN_SUBMENU) 468 return ACTION_EXIT_MENUITEM; 469 470 /* queueing options only work during playback */ 471 if (!(audio_status() & AUDIO_STATUS_PLAY)) 472 return ACTION_EXIT_MENUITEM; 473 } 474 else if ((this_item->flags & MENU_TYPE_MASK) == MT_FUNCTION_CALL_W_PARAM && 475 this_item->function_param->function_w_param == add_to_playlist) 476 { 477 struct add_to_pl_param *param = this_item->function_param->param; 478 479 if ((param->flags & PL_QUEUE) == PL_QUEUE) 480 { 481 if (global_settings.show_queue_options != QUEUE_SHOW_AT_TOPLEVEL && 482 !in_queue_submenu) 483 return ACTION_EXIT_MENUITEM; 484 } 485 486 if (param->position == PLAYLIST_INSERT_SHUFFLED || 487 param->position == PLAYLIST_INSERT_LAST_SHUFFLED) 488 { 489 if (!global_settings.show_shuffled_adding_options) 490 return ACTION_EXIT_MENUITEM; 491 492 if (sel_file_attr != FILE_ATTR_M3U && 493 (selected_file.attr & ATTR_DIRECTORY) == 0) 494 return ACTION_EXIT_MENUITEM; 495 } 496 497 if ((param->flags & PL_REPLACE) != PL_REPLACE) 498 { 499 if (!(audio_status() & AUDIO_STATUS_PLAY)) 500 return ACTION_EXIT_MENUITEM; 501 } 502 } 503 504 break; 505 506 case ACTION_ENTER_MENUITEM: 507 in_queue_submenu = this_item == &queue_menu; 508 break; 509 } 510 511 return action; 512} 513 514void onplay_show_playlist_menu(const char* path, int attr, void (*playlist_insert_cb)) 515{ 516 ctx_current_playlist_insert = playlist_insert_cb; 517 selected_file_set(CONTEXT_STD, path, attr); 518 in_queue_submenu = false; 519 do_menu(&tree_playlist_menu, NULL, NULL, false); 520} 521 522/* playlist catalog options */ 523static bool cat_add_to_a_playlist(void) 524{ 525 return catalog_add_to_a_playlist(selected_file.path, selected_file.attr, 526 false, NULL, ctx_add_to_playlist); 527} 528 529static bool cat_add_to_a_new_playlist(void) 530{ 531 return catalog_add_to_a_playlist(selected_file.path, selected_file.attr, 532 true, NULL, ctx_add_to_playlist); 533} 534 535static int cat_playlist_callback(int action, 536 const struct menu_item_ex *this_item, 537 struct gui_synclist *this_list); 538 539MENUITEM_FUNCTION(cat_add_to_list, 0, ID2P(LANG_ADD_TO_EXISTING_PL), 540 cat_add_to_a_playlist, NULL, Icon_Playlist); 541MENUITEM_FUNCTION(cat_add_to_new, 0, ID2P(LANG_CATALOG_ADD_TO_NEW), 542 cat_add_to_a_new_playlist, NULL, Icon_Playlist); 543MAKE_ONPLAYMENU(cat_playlist_menu, ID2P(LANG_ADD_TO_PL), 544 cat_playlist_callback, Icon_Playlist, 545 &cat_add_to_list, &cat_add_to_new); 546 547void onplay_show_playlist_cat_menu(const char* track_name, int attr, void (*add_to_pl_cb)) 548{ 549 ctx_add_to_playlist = add_to_pl_cb; 550 selected_file_set(CONTEXT_STD, track_name, attr); 551 do_menu(&cat_playlist_menu, NULL, NULL, false); 552} 553 554static int cat_playlist_callback(int action, 555 const struct menu_item_ex *this_item, 556 struct gui_synclist *this_list) 557{ 558 (void)this_item; 559 (void)this_list; 560 if (!selected_file.path || 561 (((selected_file.attr & FILE_ATTR_MASK) != FILE_ATTR_AUDIO) && 562 ((selected_file.attr & FILE_ATTR_MASK) != FILE_ATTR_M3U) && 563 ((selected_file.attr & ATTR_DIRECTORY) == 0))) 564 { 565 return ACTION_EXIT_MENUITEM; 566 } 567 568 if (action == ACTION_REQUEST_MENUITEM) 569 { 570 if ((audio_status() & AUDIO_STATUS_PLAY) 571 || selected_file.context != CONTEXT_WPS) 572 { 573 return action; 574 } 575 return ACTION_EXIT_MENUITEM; 576 } 577 return action; 578} 579 580static void splash_cancelled(void) 581{ 582 clear_screen_buffer(true); 583 splash(HZ, ID2P(LANG_CANCEL)); 584} 585 586static void splash_failed(int lang_what, int err) 587{ 588 cond_talk_ids_fq(lang_what, LANG_FAILED); 589 clear_screen_buffer(true); 590 splashf(HZ*2, "%s %s (%d)", str(lang_what), str(LANG_FAILED), err); 591} 592 593static bool clipboard_cut(void) 594{ 595 return clipboard_clip(&clipboard, selected_file.path, selected_file.attr, 596 PASTE_CUT); 597} 598 599static bool clipboard_copy(void) 600{ 601 return clipboard_clip(&clipboard, selected_file.path, selected_file.attr, 602 PASTE_COPY); 603} 604 605/* Paste the clipboard to the current directory */ 606static int clipboard_paste(void) 607{ 608 if (!clipboard.path[0]) 609 return 1; 610 611 int rc = copy_move_fileobject(clipboard.path, getcwd(NULL, 0), clipboard.flags); 612 613 switch (rc) 614 { 615 case FORC_CANCELLED: 616 splash_cancelled(); 617 /* Fallthrough */ 618 case FORC_SUCCESS: 619 onplay_result = ONPLAY_RELOAD_DIR; 620 /* Fallthrough */ 621 case FORC_NOOP: 622 clipboard_clear_selection(&clipboard); 623 /* Fallthrough */ 624 case FORC_NOOVERWRT: 625 break; 626 default: 627 if (rc < FORC_SUCCESS) { 628 splash_failed(LANG_PASTE, rc); 629 onplay_result = ONPLAY_RELOAD_DIR; 630 } 631 } 632 633 return 1; 634} 635 636#ifdef HAVE_TAGCACHE 637static int set_rating_inline(void) 638{ 639 struct mp3entry* id3 = audio_current_track(); 640 if (id3 && id3->tagcache_idx && global_settings.runtimedb) 641 { 642 set_int_ex(str(LANG_MENU_SET_RATING), "", UNIT_INT, (void*)(&id3->rating), 643 NULL, 1, 0, 10, NULL, NULL); 644 tagcache_update_numeric(id3->tagcache_idx-1, tag_rating, id3->rating); 645 } 646 else 647 splash(HZ*2, ID2P(LANG_ID3_NO_INFO)); 648 return 0; 649} 650static int ratingitem_callback(int action, 651 const struct menu_item_ex *this_item, 652 struct gui_synclist *this_list) 653{ 654 (void)this_item; 655 (void)this_list; 656 if (action == ACTION_REQUEST_MENUITEM) 657 { 658 if (!selected_file.path || !global_settings.runtimedb || !tagcache_is_usable()) 659 return ACTION_EXIT_MENUITEM; 660 } 661 return action; 662} 663MENUITEM_FUNCTION(rating_item, 0, ID2P(LANG_MENU_SET_RATING), 664 set_rating_inline, 665 ratingitem_callback, Icon_Questionmark); 666#endif 667MENUITEM_RETURNVALUE(plugin_item, ID2P(LANG_OPEN_PLUGIN), 668 GO_TO_PLUGIN, NULL, Icon_Plugin); 669 670static bool view_cue(void) 671{ 672 struct mp3entry* id3 = audio_current_track(); 673 if (id3 && id3->cuesheet) 674 { 675 browse_cuesheet(id3->cuesheet); 676 } 677 return false; 678} 679static int view_cue_item_callback(int action, 680 const struct menu_item_ex *this_item, 681 struct gui_synclist *this_list) 682{ 683 (void)this_item; 684 (void)this_list; 685 struct mp3entry* id3 = audio_current_track(); 686 if (action == ACTION_REQUEST_MENUITEM) 687 { 688 if (!selected_file.path || !id3 || !id3->cuesheet) 689 return ACTION_EXIT_MENUITEM; 690 } 691 return action; 692} 693MENUITEM_FUNCTION(view_cue_item, 0, ID2P(LANG_BROWSE_CUESHEET), 694 view_cue, view_cue_item_callback, Icon_NOICON); 695 696 697static int browse_id3_wrapper(void) 698{ 699 if (get_current_activity() == ACTIVITY_CONTEXTMENU) /* get rid of parent activity */ 700 pop_current_activity_without_refresh(); /* when called from ctxt menu */ 701 702 if (browse_id3(audio_current_track(), 703 playlist_get_display_index(), 704 playlist_amount(), NULL, 1, NULL)) 705 return GO_TO_ROOT; 706 return GO_TO_PREVIOUS; 707} 708 709/* CONTEXT_WPS items */ 710MENUITEM_FUNCTION(browse_id3_item, MENU_FUNC_CHECK_RETVAL, ID2P(LANG_MENU_SHOW_ID3_INFO), 711 browse_id3_wrapper, NULL, Icon_NOICON); 712 713#ifdef HAVE_PITCHCONTROL 714MENUITEM_FUNCTION(pitch_screen_item, 0, ID2P(LANG_PITCH), 715 gui_syncpitchscreen_run, NULL, Icon_Audio); 716MENUITEM_FUNCTION(pitch_reset_item, 0, ID2P(LANG_RESET_SETTING), 717 reset_pitch, NULL, Icon_Submenu_Entered); 718 719static int pitch_callback(int action, 720 const struct menu_item_ex *this_item, 721 struct gui_synclist *this_list); 722 723/* need special handling so we can toggle the icon */ 724#define MAKE_PITCHMENU( name, str, callback, icon, ... ) \ 725 static const struct menu_item_ex *name##_[] = {__VA_ARGS__}; \ 726 struct menu_callback_with_desc name##__ = {callback,str,icon}; \ 727 static const struct menu_item_ex name = \ 728 {MT_MENU|MENU_HAS_DESC|MENU_EXITAFTERTHISMENU| \ 729 MENU_ITEM_COUNT(sizeof( name##_)/sizeof(*name##_)), \ 730 { (void*)name##_},{.callback_and_desc = & name##__}}; 731 732MAKE_PITCHMENU(pitch_menu, ID2P(LANG_PITCH), 733 pitch_callback, Icon_Audio, 734 &pitch_screen_item, 735 &pitch_reset_item); 736 737static int pitch_callback(int action, 738 const struct menu_item_ex *this_item, 739 struct gui_synclist *this_list) 740{ 741 if (action == ACTION_ENTER_MENUITEM || action == ACTION_REQUEST_MENUITEM) 742 { 743 pitch_menu__.icon_id = Icon_Submenu; /* if setting changed show + */ 744 int32_t ts = dsp_get_timestretch(); 745 if (sound_get_pitch() == PITCH_SPEED_100 && ts == PITCH_SPEED_100) 746 { 747 pitch_menu__.icon_id = Icon_Audio; 748 if (action == ACTION_ENTER_MENUITEM) 749 { /* if default then run pitch screen directly */ 750 gui_syncpitchscreen_run(); 751 action = ACTION_EXIT_MENUITEM; 752 } 753 } 754 } 755 return action; 756 757 (void)this_item; 758 (void)this_list; 759} 760#endif /*def HAVE_PITCHCONTROL*/ 761 762#ifdef HAVE_ALBUMART 763MENUITEM_FUNCTION(view_album_art_item, 0, ID2P(LANG_VIEW_ALBUMART), 764 view_album_art, NULL, Icon_NOICON); 765#endif 766 767static int clipboard_delete_selected_fileobject(void) 768{ 769 int rc = delete_fileobject(selected_file.path); 770 if (rc < FORC_SUCCESS) { 771 splash_failed(LANG_DELETE, rc); 772 } else if (rc == FORC_CANCELLED) { 773 splash_cancelled(); 774 } 775 if (rc != FORC_NOOP) { 776 /* Could have failed after some but not all needed changes; reload */ 777 onplay_result = ONPLAY_RELOAD_DIR; 778 } 779 return 1; 780} 781 782static void show_result(int rc, int lang_what) 783{ 784 if (rc < FORC_SUCCESS) { 785 splash_failed(lang_what, rc); 786 } else if (rc == FORC_CANCELLED) { 787 /* splash_cancelled(); kbd_input() splashes it */ 788 } else if (rc == FORC_SUCCESS) { 789 onplay_result = ONPLAY_RELOAD_DIR; 790 } 791} 792 793static int clipboard_create_dir(void) 794{ 795 int rc = create_dir(); 796 797 show_result(rc, LANG_CREATE_DIR); 798 799 return 1; 800} 801 802static int clipboard_rename_selected_file(void) 803{ 804 int rc = rename_file(selected_file.path); 805 806 show_result(rc, LANG_RENAME); 807 808 return 1; 809} 810 811/* CONTEXT_[TREE|ID3DB] items */ 812static int clipboard_callback(int action, 813 const struct menu_item_ex *this_item, 814 struct gui_synclist *this_list); 815 816MENUITEM_FUNCTION(rename_file_item, 0, ID2P(LANG_RENAME), 817 clipboard_rename_selected_file, clipboard_callback, Icon_NOICON); 818MENUITEM_FUNCTION(clipboard_cut_item, 0, ID2P(LANG_CUT), 819 clipboard_cut, clipboard_callback, Icon_NOICON); 820MENUITEM_FUNCTION(clipboard_copy_item, 0, ID2P(LANG_COPY), 821 clipboard_copy, clipboard_callback, Icon_NOICON); 822MENUITEM_FUNCTION(clipboard_paste_item, 0, ID2P(LANG_PASTE), 823 clipboard_paste, clipboard_callback, Icon_NOICON); 824MENUITEM_FUNCTION(delete_file_item, 0, ID2P(LANG_DELETE), 825 clipboard_delete_selected_fileobject, clipboard_callback, Icon_NOICON); 826MENUITEM_FUNCTION(delete_dir_item, 0, ID2P(LANG_DELETE_DIR), 827 clipboard_delete_selected_fileobject, clipboard_callback, Icon_NOICON); 828MENUITEM_FUNCTION(create_dir_item, 0, ID2P(LANG_CREATE_DIR), 829 clipboard_create_dir, clipboard_callback, Icon_NOICON); 830 831/* other items */ 832static bool list_viewers(void) 833{ 834 int ret = filetype_list_viewers(selected_file.path); 835 if (ret == PLUGIN_USB_CONNECTED) 836 onplay_result = ONPLAY_RELOAD_DIR; 837 return false; 838} 839 840#ifdef HAVE_TAGCACHE 841static bool prepare_database_sel(void *param) 842{ 843 if (selected_file.context == CONTEXT_ID3DB) 844 { 845 if (param && !strcmp(param, "properties") 846 && (selected_file.attr & FILE_ATTR_MASK) != FILE_ATTR_AUDIO) 847 { 848 strmemccpy(selected_file.buf, MAKE_ACT_STR(ACTIVITY_DATABASEBROWSER), 849 sizeof(selected_file.buf)); 850 } 851 else 852 { 853 /* If database is not loaded into RAM, or tagcache_ram is 854 set to "quick", filename needs to be retrieved from disk! */ 855#ifdef HAVE_DISK_STORAGE 856 if ((selected_file.attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO 857 && !storage_disk_is_active() 858#ifdef HAVE_TC_RAMCACHE 859 && (global_settings.tagcache_ram != TAGCACHE_RAM_ON 860 || !tagcache_is_in_ram()) 861#endif 862 ) 863 splash(0, ID2P(LANG_WAIT)); 864#endif 865 if (!tagtree_get_subentry_filename(selected_file.buf, MAX_PATH)) 866 { 867 onplay_result = ONPLAY_RELOAD_DIR; 868 return false; 869 } 870 } 871 872 selected_file.path = selected_file.buf; 873 } 874 return true; 875} 876#endif 877 878static bool onplay_load_plugin(void *param) 879{ 880#ifdef HAVE_TAGCACHE 881 if (!prepare_database_sel(param)) 882 return false; 883#endif 884 885 if (get_current_activity() == ACTIVITY_CONTEXTMENU) /* get rid of parent activity */ 886 pop_current_activity_without_refresh(); /* when called from ctxt menu */ 887 888 int ret = filetype_load_plugin((const char*)param, selected_file.path); 889 if (ret == PLUGIN_USB_CONNECTED) 890 onplay_result = ONPLAY_RELOAD_DIR; 891 else if (ret == PLUGIN_GOTO_PLUGIN) 892 onplay_result = ONPLAY_PLUGIN; 893 else if (ret == PLUGIN_GOTO_WPS) 894 onplay_result = ONPLAY_START_PLAY; 895 return false; 896} 897 898MENUITEM_FUNCTION(list_viewers_item, 0, ID2P(LANG_ONPLAY_OPEN_WITH), 899 list_viewers, clipboard_callback, Icon_NOICON); 900MENUITEM_FUNCTION_W_PARAM(properties_item, 0, ID2P(LANG_PROPERTIES), 901 onplay_load_plugin, (void *)"properties", 902 clipboard_callback, Icon_NOICON); 903MENUITEM_FUNCTION_W_PARAM(track_info_item, 0, ID2P(LANG_MENU_SHOW_ID3_INFO), 904 onplay_load_plugin, (void *)"properties", 905 clipboard_callback, Icon_NOICON); 906#ifdef HAVE_TAGCACHE 907MENUITEM_FUNCTION_W_PARAM(pictureflow_item, 0, ID2P(LANG_ONPLAY_PICTUREFLOW), 908 onplay_load_plugin, (void *)"pictureflow", 909 clipboard_callback, Icon_NOICON); 910#endif 911static bool onplay_add_to_shortcuts(void) 912{ 913 shortcuts_add(SHORTCUT_BROWSER, selected_file.path); 914 return false; 915} 916MENUITEM_FUNCTION(add_to_faves_item, 0, ID2P(LANG_ADD_TO_FAVES), 917 onplay_add_to_shortcuts, 918 clipboard_callback, Icon_NOICON); 919 920static void set_dir_helper(char* dirnamebuf, size_t bufsz) 921{ 922 path_append(dirnamebuf, selected_file.path, PA_SEP_HARD, bufsz); 923 settings_save(); 924} 925 926#if LCD_DEPTH > 1 927 928static void show_updated_backdrop(void) 929{ 930 skin_backdrop_load_setting(); 931 viewportmanager_theme_changed(THEME_STATUSBAR); 932 skin_backdrop_show(sb_get_backdrop(SCREEN_MAIN)); 933} 934 935static bool set_backdrop(void) 936{ 937 char previous_backdrop[sizeof global_settings.backdrop_file]; 938 strcpy(previous_backdrop, global_settings.backdrop_file); 939 940 path_append(global_settings.backdrop_file, selected_file.path, 941 PA_SEP_HARD, sizeof(global_settings.backdrop_file)); 942 943 show_updated_backdrop(); 944 945 if (!yesno_pop(ID2P(LANG_SET_AS_BACKDROP))) { 946 strcpy(global_settings.backdrop_file, previous_backdrop); 947 show_updated_backdrop(); 948 } 949 else 950 settings_save(); 951 952 return true; 953} 954MENUITEM_FUNCTION(set_backdrop_item, 0, ID2P(LANG_SET_AS_BACKDROP), 955 set_backdrop, clipboard_callback, Icon_NOICON); 956#endif 957#ifdef HAVE_RECORDING 958static bool set_recdir(void) 959{ 960 set_dir_helper(global_settings.rec_directory, 961 sizeof(global_settings.rec_directory)); 962 return false; 963} 964MENUITEM_FUNCTION(set_recdir_item, 0, ID2P(LANG_RECORDING_DIR), 965 set_recdir, clipboard_callback, Icon_Recording); 966#endif 967static bool set_startdir(void) 968{ 969 set_dir_helper(global_settings.start_directory, 970 sizeof(global_settings.start_directory)); 971 return false; 972} 973MENUITEM_FUNCTION(set_startdir_item, 0, ID2P(LANG_START_DIR), 974 set_startdir, clipboard_callback, Icon_file_view_menu); 975 976static bool set_catalogdir(void) 977{ 978 catalog_set_directory(selected_file.path); 979 settings_save(); 980 return false; 981} 982MENUITEM_FUNCTION(set_catalogdir_item, 0, ID2P(LANG_PLAYLIST_DIR), 983 set_catalogdir, clipboard_callback, Icon_Playlist); 984 985#ifdef HAVE_TAGCACHE 986static bool set_databasedir(void) 987{ 988 struct tagcache_stat *tc_stat = tagcache_get_stat(); 989 if (strcasecmp(selected_file.path, tc_stat->db_path)) 990 { 991 splash(HZ, ID2P(LANG_PLEASE_REBOOT)); 992 } 993 994 set_dir_helper(global_settings.tagcache_db_path, 995 sizeof(global_settings.tagcache_db_path)); 996 return false; 997} 998MENUITEM_FUNCTION(set_databasedir_item, 0, ID2P(LANG_DATABASE_DIR), 999 set_databasedir, clipboard_callback, Icon_Audio); 1000#endif 1001 1002MAKE_ONPLAYMENU(set_as_dir_menu, ID2P(LANG_SET_AS), 1003 clipboard_callback, Icon_NOICON, 1004 &set_catalogdir_item, 1005#ifdef HAVE_TAGCACHE 1006 &set_databasedir_item, 1007#endif 1008#ifdef HAVE_RECORDING 1009 &set_recdir_item, 1010#endif 1011 &set_startdir_item); 1012 1013static int clipboard_callback(int action, 1014 const struct menu_item_ex *this_item, 1015 struct gui_synclist *this_list) 1016{ 1017 (void)this_list; 1018 switch (action) 1019 { 1020 case ACTION_REQUEST_MENUITEM: 1021#ifdef HAVE_MULTIVOLUME 1022 /* no rename+delete for volumes */ 1023 if ((selected_file.attr & ATTR_VOLUME) && 1024 (this_item == &rename_file_item || 1025 this_item == &delete_dir_item || 1026 this_item == &clipboard_cut_item || 1027 this_item == &list_viewers_item)) 1028 return ACTION_EXIT_MENUITEM; 1029#endif 1030#ifdef HAVE_TAGCACHE 1031 if (selected_file.context == CONTEXT_ID3DB) 1032 { 1033 if (this_item == &track_info_item || 1034 this_item == &pictureflow_item) 1035 return action; 1036 return ACTION_EXIT_MENUITEM; 1037 } 1038#endif 1039 if (this_item == &clipboard_paste_item) 1040 { /* visible if there is something to paste */ 1041 return (clipboard.path[0] != 0) ? 1042 action : ACTION_EXIT_MENUITEM; 1043 } 1044 else if (this_item == &create_dir_item && 1045 *tree_get_context()->dirfilter <= NUM_FILTER_MODES) 1046 { 1047 return action; 1048 } 1049 else if (selected_file.path) 1050 { 1051 /* requires an actual file */ 1052 if (this_item == &clipboard_cut_item || 1053 this_item == &clipboard_copy_item) 1054 { 1055 if (*tree_get_context()->dirfilter != SHOW_M3U) 1056 return action; 1057 } 1058 else if (this_item == &rename_file_item || 1059 (this_item == &track_info_item && 1060 (selected_file.attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO) || 1061 (this_item == &properties_item && 1062 (selected_file.attr & FILE_ATTR_MASK) != FILE_ATTR_AUDIO) || 1063 this_item == &add_to_faves_item) 1064 { 1065 return action; 1066 } 1067 else if ((selected_file.attr & ATTR_DIRECTORY)) 1068 { 1069 /* only for directories */ 1070 if (this_item == &delete_dir_item || 1071 this_item == &set_startdir_item || 1072 this_item == &set_catalogdir_item || 1073#ifdef HAVE_TAGCACHE 1074 this_item == &set_databasedir_item || 1075#endif 1076#ifdef HAVE_RECORDING 1077 this_item == &set_recdir_item || 1078#endif 1079 this_item == &set_as_dir_menu 1080 ) 1081 return action; 1082 } 1083 /* only for files */ 1084 else if (this_item == &list_viewers_item) 1085 { 1086 if (*tree_get_context()->dirfilter != SHOW_M3U) 1087 return action; 1088 } 1089 else if (this_item == &delete_file_item) 1090 return action; 1091#if LCD_DEPTH > 1 1092 else if (this_item == &set_backdrop_item) 1093 { 1094 char *suffix = strrchr(selected_file.path, '.'); 1095 if (suffix) 1096 { 1097 if (strcasecmp(suffix, ".bmp") == 0) 1098 { 1099 return action; 1100 } 1101 } 1102 } 1103#endif 1104 } 1105 return ACTION_EXIT_MENUITEM; 1106 break; 1107 } 1108 return action; 1109} 1110 1111static int onplaymenu_callback(int action, 1112 const struct menu_item_ex *this_item, 1113 struct gui_synclist *this_list); 1114 1115/* used when onplay() is called in the CONTEXT_WPS context */ 1116MAKE_ONPLAYMENU( wps_onplay_menu, ID2P(LANG_ONPLAY_MENU_TITLE), 1117 onplaymenu_callback, Icon_Audio, 1118 &wps_playlist_menu, &cat_playlist_menu, 1119 &sound_settings, &playback_settings, 1120#ifdef HAVE_TAGCACHE 1121 &rating_item, 1122#endif 1123 &bookmark_menu, 1124 &plugin_item, 1125 &browse_id3_item, &list_viewers_item, 1126 &delete_file_item, &view_cue_item, 1127#ifdef HAVE_PITCHCONTROL 1128 &pitch_menu, 1129#endif 1130#ifdef HAVE_ALBUMART 1131 &view_album_art_item, 1132#endif 1133 ); 1134 1135int sort_playlists_callback(int action, 1136 const struct menu_item_ex *this_item, 1137 struct gui_synclist *this_list) 1138{ 1139 (void) this_list; 1140 (void) this_item; 1141 1142 if (action == ACTION_REQUEST_MENUITEM && 1143 *tree_get_context()->dirfilter != SHOW_M3U) 1144 { 1145 return ACTION_EXIT_MENUITEM; 1146 } 1147 return action; 1148} 1149 1150MENUITEM_SETTING(sort_playlists, &global_settings.sort_playlists, sort_playlists_callback); 1151 1152MENUITEM_FUNCTION(view_playlist_item, 0, ID2P(LANG_VIEW), 1153 view_playlist, 1154 onplaymenu_callback, Icon_Playlist); 1155 1156/* used when onplay() is not called in the CONTEXT_WPS context */ 1157MAKE_ONPLAYMENU( tree_onplay_menu, ID2P(LANG_ONPLAY_MENU_TITLE), 1158 onplaymenu_callback, Icon_file_view_menu, 1159 &view_playlist_item, &tree_playlist_menu, &cat_playlist_menu, 1160 &rename_file_item, &clipboard_cut_item, &clipboard_copy_item, 1161 &clipboard_paste_item, &delete_file_item, &delete_dir_item, 1162 &list_viewers_item, &create_dir_item, &properties_item, &track_info_item, 1163#ifdef HAVE_TAGCACHE 1164 &pictureflow_item, 1165#endif 1166#if LCD_DEPTH > 1 1167 &set_backdrop_item, 1168#endif 1169 &add_to_faves_item, &set_as_dir_menu, &file_menu, &sort_playlists, 1170 ); 1171static int onplaymenu_callback(int action, 1172 const struct menu_item_ex *this_item, 1173 struct gui_synclist *this_list) 1174{ 1175 (void)this_list; 1176 switch (action) 1177 { 1178 case ACTION_TREE_STOP: 1179 if (this_item == &wps_onplay_menu) 1180 { 1181 list_stop_handler(); 1182 return ACTION_STD_CANCEL; 1183 } 1184 break; 1185 case ACTION_REQUEST_MENUITEM: 1186 if (this_item == &view_playlist_item) 1187 { 1188 if ((selected_file.attr & FILE_ATTR_MASK) == FILE_ATTR_M3U && 1189 selected_file.context == CONTEXT_TREE) 1190 return action; 1191 } 1192 return ACTION_EXIT_MENUITEM; 1193 break; 1194 case ACTION_EXIT_MENUITEM: 1195 return ACTION_EXIT_AFTER_THIS_MENUITEM; 1196 break; 1197 default: 1198 break; 1199 } 1200 return action; 1201} 1202 1203#ifdef HAVE_HOTKEY 1204/* direct function calls, no need for menu callbacks */ 1205static bool hotkey_delete_item(void) 1206{ 1207#ifdef HAVE_MULTIVOLUME 1208 /* no delete for volumes */ 1209 if (selected_file.attr & ATTR_VOLUME) 1210 return false; 1211#endif 1212 1213#ifdef HAVE_TAGCACHE 1214 if (selected_file.context == CONTEXT_ID3DB && 1215 (selected_file.attr & FILE_ATTR_MASK) != FILE_ATTR_AUDIO) 1216 return false; 1217 1218 if (!prepare_database_sel(NULL)) 1219 return false; 1220#endif 1221 1222 return clipboard_delete_selected_fileobject(); 1223} 1224 1225static bool hotkey_open_with(void) 1226{ 1227 /* only open files */ 1228 if (selected_file.attr & ATTR_DIRECTORY) 1229 return false; 1230#ifdef HAVE_MULTIVOLUME 1231 if (selected_file.attr & ATTR_VOLUME) 1232 return false; 1233#endif 1234#ifdef HAVE_TAGCACHE 1235 if (!prepare_database_sel(NULL)) 1236 return false; 1237#endif 1238 return list_viewers(); 1239} 1240 1241static int hotkey_tree_pl_insert_shuffled(void) 1242{ 1243 if ((audio_status() & AUDIO_STATUS_PLAY) || 1244 (selected_file.attr & ATTR_DIRECTORY) || 1245 ((selected_file.attr & FILE_ATTR_MASK) == FILE_ATTR_M3U)) 1246 { 1247 add_to_playlist(&addtopl_insert_shuf); 1248 } 1249 return ONPLAY_RELOAD_DIR; 1250} 1251 1252static int hotkey_tree_run_plugin(void *param) 1253{ 1254#ifdef HAVE_TAGCACHE 1255 if (!prepare_database_sel(param)) 1256 return ONPLAY_RELOAD_DIR; 1257#endif 1258 if (filetype_load_plugin((const char*)param, selected_file.path) == PLUGIN_GOTO_WPS) 1259 return ONPLAY_START_PLAY; 1260 1261 return ONPLAY_RELOAD_DIR; 1262} 1263 1264static int hotkey_wps_run_plugin(void) 1265{ 1266 open_plugin_run(ID2P(LANG_HOTKEY_WPS)); 1267 return ONPLAY_OK; 1268} 1269#define HOTKEY_FUNC(func, param) {{(void *)func}, param} 1270 1271/* Any desired hotkey functions go here, in the enum in onplay.h, 1272 and in the settings menu in settings_list.c. The order here 1273 is not important. */ 1274static const struct hotkey_assignment hotkey_items[] = { 1275 [0]{ .action = HOTKEY_OFF, 1276 .lang_id = LANG_OFF, 1277 .func = HOTKEY_FUNC(NULL,NULL), 1278 .return_code = ONPLAY_RELOAD_DIR, 1279 .flags = HOTKEY_FLAG_WPS | HOTKEY_FLAG_TREE }, 1280 { .action = HOTKEY_VIEW_PLAYLIST, 1281 .lang_id = LANG_VIEW_DYNAMIC_PLAYLIST, 1282 .func = HOTKEY_FUNC(NULL, NULL), 1283 .return_code = ONPLAY_PLAYLIST, 1284 .flags = HOTKEY_FLAG_WPS }, 1285 { .action = HOTKEY_SHOW_TRACK_INFO, 1286 .lang_id = LANG_MENU_SHOW_ID3_INFO, 1287 .func = HOTKEY_FUNC(browse_id3_wrapper, NULL), 1288 .return_code = ONPLAY_RELOAD_DIR, 1289 .flags = HOTKEY_FLAG_WPS }, 1290#ifdef HAVE_PITCHCONTROL 1291 { .action = HOTKEY_PITCHSCREEN, 1292 .lang_id = LANG_PITCH, 1293 .func = HOTKEY_FUNC(gui_syncpitchscreen_run, NULL), 1294 .return_code = ONPLAY_RELOAD_DIR, 1295 .flags = HOTKEY_FLAG_WPS | HOTKEY_FLAG_NOSBS }, 1296#endif 1297 { .action = HOTKEY_OPEN_WITH, 1298 .lang_id = LANG_ONPLAY_OPEN_WITH, 1299 .func = HOTKEY_FUNC(hotkey_open_with, NULL), 1300 .return_code = ONPLAY_RELOAD_DIR, 1301 .flags = HOTKEY_FLAG_WPS | HOTKEY_FLAG_TREE }, 1302 { .action = HOTKEY_DELETE, 1303 .lang_id = LANG_DELETE, 1304 .func = HOTKEY_FUNC(hotkey_delete_item, NULL), 1305 .return_code = ONPLAY_RELOAD_DIR, 1306 .flags = HOTKEY_FLAG_WPS | HOTKEY_FLAG_TREE }, 1307 { .action = HOTKEY_INSERT, 1308 .lang_id = LANG_ADD, 1309 .func = HOTKEY_FUNC(add_to_playlist, (intptr_t*)&addtopl_insert), 1310 .return_code = ONPLAY_RELOAD_DIR, 1311 .flags = HOTKEY_FLAG_TREE }, 1312 { .action = HOTKEY_INSERT_SHUFFLED, 1313 .lang_id = LANG_ADD_SHUFFLED, 1314 .func = HOTKEY_FUNC(hotkey_tree_pl_insert_shuffled, NULL), 1315 .return_code = ONPLAY_FUNC_RETURN, 1316 .flags = HOTKEY_FLAG_TREE }, 1317 { .action = HOTKEY_PLUGIN, 1318 .lang_id = LANG_OPEN_PLUGIN, 1319 .func = HOTKEY_FUNC(hotkey_wps_run_plugin, NULL), 1320 .return_code = ONPLAY_FUNC_RETURN, 1321 .flags = HOTKEY_FLAG_WPS | HOTKEY_FLAG_NOSBS }, 1322 { .action = HOTKEY_BOOKMARK, 1323 .lang_id = LANG_BOOKMARK_MENU_CREATE, 1324 .func = HOTKEY_FUNC(bookmark_create_menu, NULL), 1325 .return_code = ONPLAY_OK, 1326 .flags = HOTKEY_FLAG_WPS | HOTKEY_FLAG_NOSBS }, 1327 { .action = HOTKEY_BOOKMARK_LIST, 1328 .lang_id = LANG_BOOKMARK_MENU_LIST, 1329 .func = HOTKEY_FUNC(bookmark_load_menu, NULL), 1330 .return_code = ONPLAY_START_PLAY, 1331 .flags = HOTKEY_FLAG_WPS }, 1332 { .action = HOTKEY_PROPERTIES, 1333 .lang_id = LANG_PROPERTIES, 1334 .func = HOTKEY_FUNC(hotkey_tree_run_plugin, (void *)"properties"), 1335 .return_code = ONPLAY_FUNC_RETURN, 1336 .flags = HOTKEY_FLAG_TREE }, 1337#ifdef HAVE_TAGCACHE 1338 { .action = HOTKEY_PICTUREFLOW, 1339 .lang_id = LANG_ONPLAY_PICTUREFLOW, 1340 .func = HOTKEY_FUNC(hotkey_tree_run_plugin, (void *)"pictureflow"), 1341 .return_code = ONPLAY_FUNC_RETURN, 1342 .flags = HOTKEY_FLAG_TREE }, 1343#endif 1344}; 1345 1346const struct hotkey_assignment *get_hotkey(int action) 1347{ 1348 for (size_t i = ARRAYLEN(hotkey_items) - 1; i < ARRAYLEN(hotkey_items); i--) 1349 { 1350 if (hotkey_items[i].action == action) 1351 return &hotkey_items[i]; 1352 } 1353 return &hotkey_items[0]; /* no valid hotkey set, return HOTKEY_OFF*/ 1354} 1355 1356/* Execute the hotkey function, if listed */ 1357static int execute_hotkey(bool is_wps) 1358{ 1359 const int action = (is_wps ? global_settings.hotkey_wps : 1360 global_settings.hotkey_tree); 1361 1362 /* search assignment struct for a match for the hotkey setting */ 1363 const struct hotkey_assignment *this_item = get_hotkey(action); 1364 1365 /* run the associated function (with optional param), if any */ 1366 const struct menu_func_param func = this_item->func; 1367 1368 int func_return = ONPLAY_RELOAD_DIR; 1369 if (func.function != NULL) 1370 { 1371 if (func.param != NULL) 1372 func_return = (*func.function_w_param)(func.param); 1373 else 1374 func_return = (*func.function)(); 1375 } 1376 const int return_code = this_item->return_code; 1377 1378 if (return_code == ONPLAY_FUNC_RETURN) 1379 return func_return; /* Use value returned by function */ 1380 return return_code; /* or return the associated value */ 1381} 1382#endif /* HOTKEY */ 1383 1384int onplay(char* file, int attr, int from_context, bool hotkey, int customaction) 1385{ 1386 const struct menu_item_ex *menu; 1387 onplay_result = ONPLAY_OK; 1388 ctx_current_playlist_insert = NULL; 1389 selected_file_set(from_context, NULL, attr); 1390 1391#ifdef HAVE_TAGCACHE 1392 if (from_context == CONTEXT_ID3DB) 1393 { 1394 ctx_add_to_playlist = tagtree_add_to_playlist; 1395 if (file != NULL) 1396 { 1397 /* add a leading slash so that catalog_add_to_a_playlist 1398 later prefills the name when creating a new playlist */ 1399 snprintf(selected_file.buf, MAX_PATH, "/%s", file); 1400 selected_file.path = selected_file.buf; 1401 } 1402 } 1403 else 1404#endif 1405 { 1406 ctx_add_to_playlist = NULL; 1407 if (file != NULL) 1408 { 1409 strmemccpy(selected_file.buf, file, MAX_PATH); 1410 selected_file.path = selected_file.buf; 1411 } 1412 1413 } 1414 int menu_selection; 1415 1416#ifdef HAVE_HOTKEY 1417 if (hotkey) 1418 return execute_hotkey(from_context == CONTEXT_WPS); 1419#else 1420 (void)hotkey; 1421#endif 1422 if (customaction == ONPLAY_CUSTOMACTION_SHUFFLE_SONGS) 1423 { 1424 int returnCode = add_to_playlist(&addtopl_replace_shuffled); 1425 if (returnCode == 1) 1426 // User did not want to erase his current playlist, so let's show again the database main menu 1427 return ONPLAY_RELOAD_DIR; 1428 return ONPLAY_START_PLAY; 1429 } 1430 1431 push_current_activity(ACTIVITY_CONTEXTMENU); 1432 if (from_context == CONTEXT_WPS) 1433 menu = &wps_onplay_menu; 1434 else 1435 menu = &tree_onplay_menu; 1436 menu_selection = do_menu(menu, NULL, NULL, false); 1437 1438 if (get_current_activity() == ACTIVITY_CONTEXTMENU) /* Activity may have been */ 1439 pop_current_activity(); /* popped already by menu item */ 1440 1441 1442 if (menu_selection == GO_TO_WPS) 1443 return ONPLAY_START_PLAY; 1444 if (menu_selection == GO_TO_ROOT) 1445 return ONPLAY_MAINMENU; 1446 if (menu_selection == GO_TO_MAINMENU) 1447 return ONPLAY_MAINMENU; 1448 if (menu_selection == GO_TO_PLAYLIST_VIEWER) 1449 return ONPLAY_PLAYLIST; 1450 if (menu_selection == GO_TO_PLUGIN) 1451 return ONPLAY_PLUGIN; 1452 1453 return onplay_result; 1454} 1455 1456int get_onplay_context(void) 1457{ 1458 return selected_file.context; 1459}