diff options
Diffstat (limited to 'apps/playlist_viewer.c')
-rw-r--r-- | apps/playlist_viewer.c | 445 |
1 files changed, 335 insertions, 110 deletions
diff --git a/apps/playlist_viewer.c b/apps/playlist_viewer.c index 21b61d4a67..1d8b1b1b03 100644 --- a/apps/playlist_viewer.c +++ b/apps/playlist_viewer.c @@ -73,6 +73,17 @@ enum direction BACKWARD }; +enum pv_onplay_result { + PV_ONPLAY_USB, + PV_ONPLAY_USB_CLOSED, + PV_ONPLAY_WPS_CLOSED, + PV_ONPLAY_CLOSED, + PV_ONPLAY_ITEM_REMOVED, + PV_ONPLAY_CHANGED, + PV_ONPLAY_UNCHANGED, + PV_ONPLAY_SAVED, +}; + struct playlist_buffer { char *name_buffer; /* Buffer used to store track names */ @@ -92,8 +103,10 @@ struct playlist_buffer /* Global playlist viewer settings */ struct playlist_viewer { - struct playlist_info* playlist; /* playlist being viewed */ + const char *title; /* Playlist Viewer list title */ + struct playlist_info* playlist; /* Playlist being viewed */ int num_tracks; /* Number of tracks in playlist */ + int *initial_selection; /* The initially selected track */ int current_playing_track; /* Index of current playing track */ int selected_track; /* The selected track, relative (first is 0) */ int moving_track; /* The track to move, relative (first is 0) @@ -107,6 +120,7 @@ static struct playlist_viewer viewer; /* Used when viewing playlists on disk */ static struct playlist_info temp_playlist; +static bool temp_playlist_init = false; static void playlist_buffer_init(struct playlist_buffer *pb, char *names_buffer, int names_buffer_size); @@ -119,14 +133,17 @@ static struct playlist_entry * playlist_buffer_get_track(struct playlist_buffer int index); static bool playlist_viewer_init(struct playlist_viewer * viewer, - const char* filename, bool reload); + const char* filename, bool reload, + int *most_recent_selection); static void format_name(char* dest, const char* src); static void format_line(const struct playlist_entry* track, char* str, int len); static bool update_playlist(bool force); -static int onplay_menu(int index); +static enum pv_onplay_result onplay_menu(int index); + +static void close_playlist_viewer(void); static void playlist_buffer_init(struct playlist_buffer *pb, char *names_buffer, int names_buffer_size) @@ -306,7 +323,8 @@ static struct playlist_entry * playlist_buffer_get_track(struct playlist_buffer /* Initialize the playlist viewer. */ static bool playlist_viewer_init(struct playlist_viewer * viewer, - const char* filename, bool reload) + const char* filename, bool reload, + int *most_recent_selection) { char* buffer; size_t buffer_size; @@ -335,7 +353,10 @@ static bool playlist_viewer_init(struct playlist_viewer * viewer, return false; if (!filename) + { viewer->playlist = NULL; + viewer->title = (char *) str(LANG_PLAYLIST); + } else { /* Viewing playlist on disk */ @@ -344,6 +365,14 @@ static bool playlist_viewer_init(struct playlist_viewer * viewer, char *index_buffer = NULL; ssize_t index_buffer_size = 0; + /* Initialize temp playlist + * TODO - move this to playlist.c */ + if (!temp_playlist_init) + { + mutex_init(&temp_playlist.mutex); + temp_playlist_init = true; + } + viewer->playlist = &temp_playlist; /* Separate directory from filename */ @@ -359,6 +388,7 @@ static bool playlist_viewer_init(struct playlist_viewer * viewer, dir = "/"; file = filename+1; } + viewer->title = file; if (is_playing) { @@ -387,11 +417,12 @@ static bool playlist_viewer_init(struct playlist_viewer * viewer, viewer->moving_track = -1; viewer->moving_playlist_index = -1; + viewer->initial_selection = most_recent_selection; if (!reload) { if (viewer->playlist) - viewer->selected_track = 0; + viewer->selected_track = most_recent_selection ? *most_recent_selection : 0; else viewer->selected_track = playlist_get_display_index() - 1; } @@ -479,25 +510,118 @@ static bool update_playlist(bool force) return true; } -/* Menu of playlist commands. Invoked via ON+PLAY on main viewer screen. - Returns -1 if USB attached, 0 if no playlist change, 1 if playlist - changed, 2 if a track was removed from the playlist */ -static int onplay_menu(int index) +static enum pv_onplay_result show_track_info(const struct playlist_entry *current_track) +{ + struct mp3entry id3; + bool id3_retrieval_successful = false; + + int fd = open(current_track->name, O_RDONLY); + if (fd >= 0) + { + if (get_metadata(&id3, fd, current_track->name)) + id3_retrieval_successful = true; + close(fd); + } + + return id3_retrieval_successful && + browse_id3_ex(&id3, viewer.playlist, current_track->display_index, + viewer.num_tracks, NULL, 1) ? PV_ONPLAY_USB : PV_ONPLAY_UNCHANGED; +} + + +#ifdef HAVE_HOTKEY +static enum pv_onplay_result open_with(const struct playlist_entry *current_track) +{ + char selected_track[MAX_PATH]; + close_playlist_viewer(); /* don't pop activity yet – relevant for plugin_load */ + + strmemccpy(selected_track, current_track->name, sizeof(selected_track)); + + int plugin_return = filetype_list_viewers(selected_track); + pop_current_activity_without_refresh(); + + switch (plugin_return) + { + case PLUGIN_USB_CONNECTED: + return PV_ONPLAY_USB_CLOSED; + case PLUGIN_GOTO_WPS: + return PV_ONPLAY_WPS_CLOSED; + default: + return PV_ONPLAY_CLOSED; + } +} +#endif /* HAVE_HOTKEY */ + +#ifdef HAVE_TAGCACHE +static enum pv_onplay_result open_pictureflow(const struct playlist_entry *current_track) +{ + char selected_track[MAX_PATH]; + close_playlist_viewer(); /* don't pop activity yet – relevant for plugin_load */ + + strmemccpy(selected_track, current_track->name, sizeof(selected_track)); + int plugin_return = filetype_load_plugin((void *)"pictureflow", selected_track); + pop_current_activity_without_refresh(); + + switch (plugin_return) + { + case PLUGIN_USB_CONNECTED: + return PV_ONPLAY_USB_CLOSED; + case PLUGIN_GOTO_WPS: + return PV_ONPLAY_WPS_CLOSED; + default: + return PV_ONPLAY_CLOSED; + } +} +#endif + +static enum pv_onplay_result delete_track(int current_track_index, + int index, bool current_was_playing) +{ + playlist_delete(viewer.playlist, current_track_index); + if (current_was_playing) + { + if (playlist_amount_ex(viewer.playlist) <= 0) + audio_stop(); + else + { + /* Start playing new track except if it's the lasttrack + track in the playlist and repeat mode is disabled */ + struct playlist_entry *current_track = + playlist_buffer_get_track(&viewer.buffer, index); + if (current_track->display_index != viewer.num_tracks || + global_settings.repeat_mode == REPEAT_ALL) + { + audio_play(0, 0); + viewer.current_playing_track = -1; + } + } + } + return PV_ONPLAY_ITEM_REMOVED; +} + +/* Menu of playlist commands. Invoked via ON+PLAY on main viewer screen. */ +static enum pv_onplay_result onplay_menu(int index) { - int result, ret = 0; + int result, ret = PV_ONPLAY_UNCHANGED; struct playlist_entry * current_track = playlist_buffer_get_track(&viewer.buffer, index); MENUITEM_STRINGLIST(menu_items, ID2P(LANG_PLAYLIST), NULL, - ID2P(LANG_CURRENT_PLAYLIST), ID2P(LANG_CATALOG), - ID2P(LANG_REMOVE), ID2P(LANG_MOVE), ID2P(LANG_SHUFFLE), + ID2P(LANG_PLAYING_NEXT), ID2P(LANG_ADD_TO_PL), + ID2P(LANG_REMOVE), ID2P(LANG_MOVE), ID2P(LANG_MENU_SHOW_ID3_INFO), + ID2P(LANG_SHUFFLE), ID2P(LANG_SAVE), - ID2P(LANG_PLAYLISTVIEWER_SETTINGS)); - bool current = (current_track->index == viewer.current_playing_track); + ID2P(LANG_PLAYLISTVIEWER_SETTINGS) +#ifdef HAVE_TAGCACHE + ,ID2P(LANG_ONPLAY_PICTUREFLOW) +#endif + ); + + bool current_was_playing = (current_track->index == viewer.current_playing_track); result = do_menu(&menu_items, NULL, NULL, false); if (result == MENU_ATTACHED_USB) { - ret = -1; + ret = PV_ONPLAY_USB; } else if (result >= 0) { @@ -509,58 +633,48 @@ static int onplay_menu(int index) { case 0: /* playlist */ - onplay_show_playlist_menu(current_track->name); - ret = 0; + onplay_show_playlist_menu(current_track->name, FILE_ATTR_AUDIO, NULL); + ret = PV_ONPLAY_UNCHANGED; break; case 1: /* add to catalog */ - onplay_show_playlist_cat_menu(current_track->name); - ret = 0; + onplay_show_playlist_cat_menu(current_track->name, FILE_ATTR_AUDIO, NULL); + ret = PV_ONPLAY_UNCHANGED; break; case 2: - /* delete track */ - playlist_delete(viewer.playlist, current_track->index); - if (current) - { - if (playlist_amount_ex(viewer.playlist) <= 0) - audio_stop(); - else - { - /* Start playing new track except if it's the lasttrack - track in the playlist and repeat mode is disabled */ - current_track = - playlist_buffer_get_track(&viewer.buffer, index); - if (current_track->display_index!=viewer.num_tracks || - global_settings.repeat_mode == REPEAT_ALL) - { - audio_play(0, 0); - viewer.current_playing_track = -1; - } - } - } - ret = 2; + ret = delete_track(current_track->index, index, current_was_playing); break; case 3: /* move track */ viewer.moving_track = index; viewer.moving_playlist_index = current_track->index; - ret = 0; + ret = PV_ONPLAY_UNCHANGED; break; case 4: - /* shuffle */ - playlist_randomise(viewer.playlist, current_tick, false); - ret = 1; + ret = show_track_info(current_track); break; case 5: - /* save playlist */ - save_playlist_screen(viewer.playlist); - ret = 0; + /* shuffle */ + playlist_sort(viewer.playlist, !viewer.playlist); + playlist_randomise(viewer.playlist, current_tick, !viewer.playlist); + viewer.selected_track = 0; + ret = PV_ONPLAY_CHANGED; break; case 6: + save_playlist_screen(viewer.playlist); + /* playlist indices of current playlist may have changed */ + ret = viewer.playlist ? PV_ONPLAY_UNCHANGED : PV_ONPLAY_SAVED; + break; + case 7: /* playlist viewer settings */ result = do_menu(&viewer_settings_menu, NULL, NULL, false); - ret = (result == MENU_ATTACHED_USB) ? -1 : 0; + ret = (result == MENU_ATTACHED_USB) ? PV_ONPLAY_USB : PV_ONPLAY_UNCHANGED; break; +#ifdef HAVE_TAGCACHE + case 8: + ret = open_pictureflow(current_track); + break; +#endif } } return ret; @@ -569,7 +683,7 @@ static int onplay_menu(int index) /* View current playlist */ enum playlist_viewer_result playlist_viewer(void) { - return playlist_viewer_ex(NULL); + return playlist_viewer_ex(NULL, NULL); } static int get_track_num(struct playlist_viewer *local_viewer, @@ -669,32 +783,82 @@ static int playlist_callback_voice(int selected_item, void *data) return 0; } +static void update_lists(struct gui_synclist * playlist_lists, bool init) +{ + if (init) + gui_synclist_init(playlist_lists, playlist_callback_name, + &viewer, false, 1, NULL); + gui_synclist_set_nb_items(playlist_lists, viewer.num_tracks); + gui_synclist_set_voice_callback(playlist_lists, + global_settings.talk_file? + &playlist_callback_voice:NULL); + gui_synclist_set_icon_callback(playlist_lists, + global_settings.playlist_viewer_icons? + &playlist_callback_icons:NULL); + gui_synclist_set_title(playlist_lists, viewer.title, Icon_Playlist); + gui_synclist_select_item(playlist_lists, viewer.selected_track); + gui_synclist_draw(playlist_lists); + gui_synclist_speak_item(playlist_lists); +} + +static bool update_viewer_with_changes(struct gui_synclist *playlist_lists, enum pv_onplay_result res) +{ + bool exit = false; + if (res == PV_ONPLAY_CHANGED || + res == PV_ONPLAY_SAVED || + res == PV_ONPLAY_ITEM_REMOVED) + { + if (res != PV_ONPLAY_SAVED) + playlist_set_modified(viewer.playlist, true); + + if (res == PV_ONPLAY_ITEM_REMOVED) + gui_synclist_del_item(playlist_lists); + + update_playlist(true); + + if (viewer.num_tracks <= 0) + exit = true; + + if (viewer.selected_track >= viewer.num_tracks) + viewer.selected_track = viewer.num_tracks-1; + } + + /* the show_icons option in the playlist viewer settings + * menu might have changed */ + update_lists(playlist_lists, false); + return exit; +} + +static bool open_playlist_viewer(const char* filename, + struct gui_synclist *playlist_lists, + bool reload, int *most_recent_selection) +{ + push_current_activity(ACTIVITY_PLAYLISTVIEWER); + + if (!playlist_viewer_init(&viewer, filename, reload, most_recent_selection)) + return false; + + update_lists(playlist_lists, true); + + return true; +} + /* Main viewer function. Filename identifies playlist to be viewed. If NULL, view current playlist. */ -enum playlist_viewer_result playlist_viewer_ex(const char* filename) +enum playlist_viewer_result playlist_viewer_ex(const char* filename, + int* most_recent_selection) { enum playlist_viewer_result ret = PLAYLIST_VIEWER_OK; bool exit = false; /* exit viewer */ int button; - bool dirty = false; struct gui_synclist playlist_lists; - if (!playlist_viewer_init(&viewer, filename, false)) + + if (!open_playlist_viewer(filename, &playlist_lists, false, most_recent_selection)) + { + ret = PLAYLIST_VIEWER_CANCEL; goto exit; + } - push_current_activity(ACTIVITY_PLAYLISTVIEWER); - gui_synclist_init(&playlist_lists, playlist_callback_name, - &viewer, false, 1, NULL); - gui_synclist_set_voice_callback(&playlist_lists, - global_settings.talk_file? - &playlist_callback_voice:NULL); - gui_synclist_set_icon_callback(&playlist_lists, - global_settings.playlist_viewer_icons? - &playlist_callback_icons:NULL); - gui_synclist_set_nb_items(&playlist_lists, viewer.num_tracks); - gui_synclist_set_title(&playlist_lists, str(LANG_PLAYLIST), Icon_Playlist); - gui_synclist_select_item(&playlist_lists, viewer.selected_track); - gui_synclist_draw(&playlist_lists); - gui_synclist_speak_item(&playlist_lists); while (!exit) { int track; @@ -720,8 +884,7 @@ enum playlist_viewer_result playlist_viewer_ex(const char* filename) } /* Timeout so we can determine if play status has changed */ - bool res = list_do_action(CONTEXT_TREE, HZ/2, - &playlist_lists, &button, LIST_WRAP_UNLESS_HELD); + bool res = list_do_action(CONTEXT_TREE, HZ/2, &playlist_lists, &button); /* during moving, another redraw is going to be needed, * since viewer.selected_track is updated too late (after the first draw) * drawing the moving item needs it */ @@ -754,7 +917,8 @@ enum playlist_viewer_result playlist_viewer_ex(const char* filename) else { exit = true; - ret = PLAYLIST_VIEWER_CANCEL; + ret = button == ACTION_TREE_WPS ? + PLAYLIST_VIEWER_OK : PLAYLIST_VIEWER_CANCEL; } break; } @@ -778,10 +942,12 @@ enum playlist_viewer_result playlist_viewer_ex(const char* filename) splashf(HZ, (unsigned char *)"%s %s", str(LANG_MOVE), str(LANG_FAILED)); } + + playlist_set_modified(viewer.playlist, true); + update_playlist(true); viewer.moving_track = -1; viewer.moving_playlist_index = -1; - dirty = true; } else if (!viewer.playlist) { @@ -797,6 +963,7 @@ enum playlist_viewer_result playlist_viewer_ex(const char* filename) int start_index = current_track->index; if (!warn_on_pl_erase()) { + gui_synclist_set_title(&playlist_lists, playlist_lists.title, playlist_lists.title_icon); gui_synclist_draw(&playlist_lists); break; } @@ -807,8 +974,11 @@ enum playlist_viewer_result playlist_viewer_ex(const char* filename) start_index = playlist_shuffle(current_tick, start_index); playlist_start(start_index, 0, 0); + if (viewer.initial_selection) + *(viewer.initial_selection) = viewer.selected_track; + /* Our playlist is now the current list */ - if (!playlist_viewer_init(&viewer, NULL, true)) + if (!playlist_viewer_init(&viewer, NULL, true, NULL)) goto exit; exit = true; } @@ -819,35 +989,31 @@ enum playlist_viewer_result playlist_viewer_ex(const char* filename) } case ACTION_STD_CONTEXT: { - /* ON+PLAY menu */ - int ret_val; - - ret_val = onplay_menu(viewer.selected_track); + int pv_onplay_result = onplay_menu(viewer.selected_track); - if (ret_val < 0) + if (pv_onplay_result == PV_ONPLAY_USB) { ret = PLAYLIST_VIEWER_USB; goto exit; } - else if (ret_val > 0) + else if (pv_onplay_result == PV_ONPLAY_USB_CLOSED) + return PLAYLIST_VIEWER_USB; + else if (pv_onplay_result == PV_ONPLAY_WPS_CLOSED) + return PLAYLIST_VIEWER_OK; + else if (pv_onplay_result == PV_ONPLAY_CLOSED) { - /* Playlist changed */ - if (ret_val == 2) - gui_synclist_del_item(&playlist_lists); - update_playlist(true); - if (viewer.num_tracks <= 0) - exit = true; - if (viewer.selected_track >= viewer.num_tracks) - viewer.selected_track = viewer.num_tracks-1; - dirty = true; + if (!open_playlist_viewer(filename, &playlist_lists, true, NULL)) + { + ret = PLAYLIST_VIEWER_CANCEL; + goto exit; + } + break; + } + if (update_viewer_with_changes(&playlist_lists, pv_onplay_result)) + { + exit = true; + ret = PLAYLIST_VIEWER_CANCEL; } - /* the show_icons option in the playlist viewer settings - * menu might have changed */ - gui_synclist_set_icon_callback(&playlist_lists, - global_settings.playlist_viewer_icons? - &playlist_callback_icons:NULL); - gui_synclist_draw(&playlist_lists); - gui_synclist_speak_item(&playlist_lists); break; } case ACTION_STD_MENU: @@ -857,20 +1023,72 @@ enum playlist_viewer_result playlist_viewer_ex(const char* filename) case ACTION_STD_QUICKSCREEN: if (!global_settings.shortcuts_replaces_qs) { - quick_screen_quick(button); + if (quick_screen_quick(button) == + QUICKSCREEN_GOTO_SHORTCUTS_MENU) /* currently disabled */ + { + /* QuickScreen defers skin updates when popping its activity + to switch to Shortcuts Menu, so make up for that here: */ + FOR_NB_SCREENS(i) + skin_update(CUSTOM_STATUSBAR, i, SKIN_REFRESH_ALL); + } update_playlist(true); - gui_synclist_set_voice_callback(&playlist_lists, - global_settings.talk_file? - &playlist_callback_voice:NULL); - gui_synclist_set_icon_callback(&playlist_lists, - global_settings.playlist_viewer_icons? - &playlist_callback_icons:NULL); - gui_synclist_set_title(&playlist_lists, str(LANG_PLAYLIST), Icon_Playlist); - gui_synclist_draw(&playlist_lists); - gui_synclist_speak_item(&playlist_lists); - break; + update_lists(&playlist_lists, true); } + break; +#endif +#ifdef HAVE_HOTKEY + case ACTION_TREE_HOTKEY: + { + struct playlist_entry *current_track = playlist_buffer_get_track( + &viewer.buffer, + viewer.selected_track); + enum pv_onplay_result (*do_plugin)(const struct playlist_entry *) = NULL; +#ifdef HAVE_TAGCACHE + if (global_settings.hotkey_tree == HOTKEY_PICTUREFLOW) + do_plugin = &open_pictureflow; #endif + if (global_settings.hotkey_tree == HOTKEY_OPEN_WITH) + do_plugin = &open_with; + + if (do_plugin != NULL) + { + int plugin_result = do_plugin(current_track); + + if (plugin_result == PV_ONPLAY_USB_CLOSED) + return PLAYLIST_VIEWER_USB; + else if (plugin_result == PV_ONPLAY_WPS_CLOSED) + return PLAYLIST_VIEWER_OK; + else if (!open_playlist_viewer(filename, &playlist_lists, true, NULL)) + { + ret = PLAYLIST_VIEWER_CANCEL; + goto exit; + } + } + else if (global_settings.hotkey_tree == HOTKEY_PROPERTIES) + { + if (show_track_info(current_track) == PV_ONPLAY_USB) + { + ret = PLAYLIST_VIEWER_USB; + goto exit; + } + update_lists(&playlist_lists, false); + } + else if (global_settings.hotkey_tree == HOTKEY_DELETE) + { + if (update_viewer_with_changes(&playlist_lists, + delete_track(current_track->index, + viewer.selected_track, + (current_track->index == viewer.current_playing_track)))) + { + ret = PLAYLIST_VIEWER_CANCEL; + exit = true; + } + } + else + onplay(current_track->name, FILE_ATTR_AUDIO, CONTEXT_STD, true); + break; + } +#endif /* HAVE_HOTKEY */ default: if(default_event_handler(button) == SYS_USB_CONNECTED) { @@ -882,15 +1100,23 @@ enum playlist_viewer_result playlist_viewer_ex(const char* filename) } exit: + pop_current_activity_without_refresh(); + close_playlist_viewer(); + return ret; +} + +static void close_playlist_viewer(void) +{ talk_shutup(); - pop_current_activity(); if (viewer.playlist) { - if(dirty && yesno_pop(ID2P(LANG_SAVE_CHANGES))) + if (viewer.initial_selection) + *(viewer.initial_selection) = viewer.selected_track; + + if(playlist_modified(viewer.playlist) && yesno_pop(ID2P(LANG_SAVE_CHANGES))) save_playlist_screen(viewer.playlist); playlist_close(viewer.playlist); } - return ret; } static const char* playlist_search_callback_name(int selected_item, void * data, @@ -926,7 +1152,7 @@ bool search_playlist(void) struct gui_synclist playlist_lists; struct playlist_track_info track; - if (!playlist_viewer_init(&viewer, 0, false)) + if (!playlist_viewer_init(&viewer, 0, false, NULL)) return ret; if (kbd_input(search_str, sizeof(search_str), NULL) < 0) return ret; @@ -981,8 +1207,7 @@ bool search_playlist(void) gui_synclist_speak_item(&playlist_lists); while (!exit) { - if (list_do_action(CONTEXT_LIST, HZ/4, - &playlist_lists, &button, LIST_WRAP_UNLESS_HELD)) + if (list_do_action(CONTEXT_LIST, HZ/4, &playlist_lists, &button)) continue; switch (button) { |