diff --git a/NEWS b/NEWS index 9faa8541..582b2576 100644 --- a/NEWS +++ b/NEWS @@ -25,6 +25,7 @@ ncmpcpp-0.8 (????-??-??) * Fixed fetching lyrics from justsomelyrics.com. * Added support for fetching lyrics from jah-lyrics.com and plyrics.com. * Seek immediately after invoking appropriate action once. +* Added support for locating current song in playlist editor. ncmpcpp-0.7.7 (2016-10-31) * Fixed compilation on 32bit platforms. diff --git a/src/actions.cpp b/src/actions.cpp index f183541f..86b49a7b 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -1207,6 +1207,7 @@ bool JumpToPlayingSong::canBeRun() m_song = myPlaylist->nowPlayingSong(); return !m_song.empty() && (myScreen == myPlaylist + || myScreen == myPlaylistEditor || myScreen == myBrowser || myScreen == myLibrary); } @@ -1217,6 +1218,10 @@ void JumpToPlayingSong::run() { myPlaylist->locateSong(m_song); } + else if (myScreen == myPlaylistEditor) + { + myPlaylistEditor->locateSong(m_song); + } else if (myScreen == myBrowser) { myBrowser->locateSong(m_song); diff --git a/src/screens/help.cpp b/src/screens/help.cpp index 7e11b90f..5e7ea626 100644 --- a/src/screens/help.cpp +++ b/src/screens/help.cpp @@ -268,7 +268,7 @@ void write_bindings(NC::Scrollpad &w) key(w, Type::EditPlaylistName, "Edit playlist name"); key(w, Type::ChangeBrowseMode, "Browse MPD database/local filesystem"); key(w, Type::ToggleBrowserSortMode, "Toggle sort mode"); - key(w, Type::JumpToPlayingSong, "Locate playing song"); + key(w, Type::JumpToPlayingSong, "Locate current song"); key(w, Type::JumpToParentDirectory, "Jump to parent directory"); key(w, Type::DeleteBrowserItems, "Delete selected items from disk"); key(w, Type::JumpToPlaylistEditor, "Jump to playlist editor (playlists only)"); @@ -289,6 +289,7 @@ void write_bindings(NC::Scrollpad &w) key(w, Type::NextColumn, "Next column"); key(w, Type::PlayItem, "Add item to playlist and play it"); key(w, Type::AddItemToPlaylist, "Add item to playlist"); + key(w, Type::JumpToPlayingSong, "Locate current song"); # ifdef HAVE_TAGLIB_H key(w, Type::EditSong, "Edit song"); # endif // HAVE_TAGLIB_H @@ -301,6 +302,7 @@ void write_bindings(NC::Scrollpad &w) key(w, Type::NextColumn, "Next column"); key(w, Type::PlayItem, "Add item to playlist and play it"); key(w, Type::AddItemToPlaylist, "Add item to playlist"); + key(w, Type::JumpToPlayingSong, "Locate current song"); # ifdef HAVE_TAGLIB_H key(w, Type::EditSong, "Edit song"); # endif // HAVE_TAGLIB_H diff --git a/src/screens/playlist_editor.cpp b/src/screens/playlist_editor.cpp index 8f00e165..bf36d623 100644 --- a/src/screens/playlist_editor.cpp +++ b/src/screens/playlist_editor.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -57,7 +58,7 @@ size_t RightColumnWidth; std::string SongToString(const MPD::Song &s); bool PlaylistEntryMatcher(const Regex::Regex &rx, const MPD::Playlist &playlist); bool SongEntryMatcher(const Regex::Regex &rx, const MPD::Song &s); - +boost::optional GetSongIndexInPlaylist(MPD::Playlist playlist, const MPD::Song &song); } PlaylistEditor::PlaylistEditor() @@ -481,6 +482,81 @@ void PlaylistEditor::locatePlaylist(const MPD::Playlist &playlist) } } +void PlaylistEditor::gotoSong(size_t playlist_index, size_t song_index) +{ + Playlists.highlight(playlist_index); + Content.clear(); + update(); + Content.highlight(song_index); + + if (isActiveWindow(Playlists)) + nextColumn(); + else + Playlists.refresh(); +} + +void PlaylistEditor::locateSong(const MPD::Song &s) +{ + Content.clearFilter(); + Playlists.clearFilter(); + + if (!Content.empty()) + { + auto song_it = std::find(Content.currentV() + 1, Content.endV(), s); + if (song_it != Content.endV()) + { + Content.highlight(song_it - Content.beginV()); + nextColumn(); + return; + } + } + + if (!Playlists.empty()) + { + Statusbar::print("Jumping to song..."); + auto locate_and_switch_playlist = [this, &s](auto pl_it) -> bool { + if (auto song_index = GetSongIndexInPlaylist(*pl_it, s)) + { + this->gotoSong(pl_it - Playlists.beginV(), *song_index); + return true; + } + return false; + }; + + for (auto pl_it = Playlists.currentV() + 1; pl_it != Playlists.endV(); ++pl_it) + if (locate_and_switch_playlist(pl_it)) + return; + for (auto pl_it = Playlists.beginV(); pl_it != Playlists.currentV(); ++pl_it) + if (locate_and_switch_playlist(pl_it)) + return; + } + + if (!Content.empty()) + { + auto song_it = std::find(Content.beginV(), Content.currentV(), s); + if (song_it != Content.currentV()) + { + Content.highlight(song_it - Content.beginV()); + nextColumn(); + return; + } + } + + // Wrap back to the beginning of current playlist + if (Content.size() != 0) + { + auto song_it = std::find(Content.beginV(), Content.currentV(), s); + if (song_it != Content.currentV()) + { + Content.highlight(song_it - Content.beginV()); + nextColumn(); + return; + } + } + + Statusbar::print("Song is not from playlists"); +} + namespace { std::string SongToString(const MPD::Song &s) @@ -508,4 +584,20 @@ bool SongEntryMatcher(const Regex::Regex &rx, const MPD::Song &s) return Regex::search(SongToString(s), rx); } +boost::optional GetSongIndexInPlaylist(MPD::Playlist playlist, const MPD::Song &song) +{ + size_t index = 0; + MPD::SongIterator it = Mpd.GetPlaylistContentNoInfo(playlist.path()), end; + + for (;;) + { + if (it == end) + return boost::none; + if (*it == song) + return index; + + ++it, ++index; + } +} + } diff --git a/src/screens/playlist_editor.h b/src/screens/playlist_editor.h index 3d6efb75..c3398e1c 100644 --- a/src/screens/playlist_editor.h +++ b/src/screens/playlist_editor.h @@ -79,6 +79,8 @@ struct PlaylistEditor: Screen, Filterable, HasColumns, HasSongs, S void requestContentsUpdate() { m_content_update_requested = true; } void locatePlaylist(const MPD::Playlist &playlist); + void locateSong(const MPD::Song &s); + void gotoSong(size_t playlist_index, size_t song_index); NC::Menu Playlists; SongMenu Content;