From 1b6cb65f3a371e40794e92176f013b1a720432e1 Mon Sep 17 00:00:00 2001 From: Andrzej Rybczak Date: Sat, 23 May 2015 22:55:04 +0200 Subject: [PATCH] actions: add support for range selection and make a few actions work on ranges --- NEWS | 1 + doc/bindings | 3 +++ src/actions.cpp | 59 ++++++++++++++++++++++++++++++++++++++----- src/actions.h | 22 +++++++++++++++- src/bindings.cpp | 2 ++ src/help.cpp | 7 ++--- src/helpers.h | 41 ++++++++++++++++++++++-------- src/playlist.cpp | 12 --------- src/playlist.h | 3 +-- src/sort_playlist.cpp | 7 ++--- 10 files changed, 119 insertions(+), 38 deletions(-) diff --git a/NEWS b/NEWS index dc94e3e3..31294783 100644 --- a/NEWS +++ b/NEWS @@ -26,6 +26,7 @@ ncmpcpp-0.7 (????-??-??) * Searching in text fields now respects regular expression configuration. * Monolithic 'press_space' action was split into 'add_item_to_playlist', 'toggle_lyrics_update_on_song_change' and 'toggle_visualization_type'. * Sorting actions were rebound to Ctrl-S. +* Support for range selection was added (bound to Ctrl-V by default). In addition, sorting, reversing and shuffling items in playlist now works on ranges. ncmpcpp-0.6.4 (2015-05-02) diff --git a/doc/bindings b/doc/bindings index fe7e69f5..2c75e4bc 100644 --- a/doc/bindings +++ b/doc/bindings @@ -415,6 +415,9 @@ #def_key "l" # show_lyrics # +#def_key "ctrl_v" +# select_range +# #def_key "v" # reverse_selection # diff --git a/src/actions.cpp b/src/actions.cpp index 0c65304e..6c26f226 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -89,6 +89,15 @@ void seek(); void findItem(const SearchDirection direction); void listsChangeFinisher(); +template +bool findSelectedRangeAndPrintInfoIfNot(Iterator &first, Iterator &last) +{ + bool success = findSelectedRange(first, last); + if (!success) + Statusbar::print("No range selected"); + return success; +} + } namespace Actions { @@ -1173,11 +1182,19 @@ void ToggleRepeat::run() Mpd.SetRepeat(!Status::State::repeat()); } +bool Shuffle::canBeRun() +{ + if (myScreen != myPlaylist) + return false; + m_begin = myPlaylist->main().begin(); + m_end = myPlaylist->main().end(); + return findSelectedRangeAndPrintInfoIfNot(m_begin, m_end); +} + void Shuffle::run() { - auto begin = myPlaylist->main().begin(), end = myPlaylist->main().end(); - auto range = getSelectedRange(begin, end); - Mpd.ShuffleRange(range.first-begin, range.second-begin); + auto begin = myPlaylist->main().begin(); + Mpd.ShuffleRange(m_begin-begin, m_end-begin); Statusbar::print("Range shuffled"); } @@ -1654,6 +1671,23 @@ void SelectItem::run() current->setSelected(!current->isSelected()); } +bool SelectRange::canBeRun() +{ + m_list = dynamic_cast(myScreen->activeWindow()); + if (m_list == nullptr) + return false; + m_begin = m_list->beginP(); + m_end = m_list->endP(); + return findRange(m_begin, m_end); +} + +void SelectRange::run() +{ + for (; m_begin != m_end; ++m_begin) + m_begin->setSelected(true); + Statusbar::print("Range selected"); +} + bool ReverseSelection::canBeRun() { m_list = dynamic_cast(myScreen->activeWindow()); @@ -1793,7 +1827,10 @@ void ClearPlaylist::run() bool SortPlaylist::canBeRun() { - return myScreen == myPlaylist; + if (myScreen != myPlaylist) + return false; + auto first = myPlaylist->main().begin(), last = myPlaylist->main().end(); + return findSelectedRangeAndPrintInfoIfNot(first, last); } void SortPlaylist::run() @@ -1803,12 +1840,21 @@ void SortPlaylist::run() bool ReversePlaylist::canBeRun() { - return myScreen == myPlaylist; + if (myScreen != myPlaylist) + return false; + m_begin = myPlaylist->main().begin(); + m_end = myPlaylist->main().end(); + return findSelectedRangeAndPrintInfoIfNot(m_begin, m_end); } void ReversePlaylist::run() { - myPlaylist->Reverse(); + Statusbar::print("Reversing range..."); + Mpd.StartCommandsList(); + for (--m_end; m_begin < m_end; ++m_begin, --m_end) + Mpd.Swap(m_begin->value().getPosition(), m_end->value().getPosition()); + Mpd.CommitCommandsList(); + Statusbar::print("Range reversed"); } bool Find::canBeRun() @@ -2512,6 +2558,7 @@ void populateActions() insert_action(new Actions::JumpToParentDirectory()); insert_action(new Actions::PressEnter()); insert_action(new Actions::SelectItem()); + insert_action(new Actions::SelectRange()); insert_action(new Actions::PreviousColumn()); insert_action(new Actions::NextColumn()); insert_action(new Actions::MasterScreen()); diff --git a/src/actions.h b/src/actions.h index db8ee5d1..262a03f9 100644 --- a/src/actions.h +++ b/src/actions.h @@ -49,7 +49,7 @@ enum class Type SetCrossfade, SetVolume, EditSong, EditLibraryTag, EditLibraryAlbum, EditDirectoryName, EditPlaylistName, EditLyrics, JumpToBrowser, JumpToMediaLibrary, JumpToPlaylistEditor, ToggleScreenLock, JumpToTagEditor, JumpToPositionInSong, - SelectItem, ReverseSelection, RemoveSelection, SelectAlbum, AddSelectedItems, + SelectItem, SelectRange, ReverseSelection, RemoveSelection, SelectAlbum, AddSelectedItems, CropMainPlaylist, CropPlaylist, ClearMainPlaylist, ClearPlaylist, SortPlaylist, ReversePlaylist, Find, FindItemForward, FindItemBackward, NextFoundItem, PreviousFoundItem, ToggleFindMode, ToggleReplayGainMode, @@ -581,7 +581,11 @@ struct Shuffle: BaseAction Shuffle(): BaseAction(Type::Shuffle, "shuffle") { } private: + virtual bool canBeRun() OVERRIDE; virtual void run() OVERRIDE; + + NC::Menu::ConstIterator m_begin; + NC::Menu::ConstIterator m_end; }; struct ToggleRandom: BaseAction @@ -776,6 +780,19 @@ private: NC::List *m_list; }; +struct SelectRange: BaseAction +{ + SelectRange(): BaseAction(Type::SelectRange, "select_range") { } + +private: + virtual bool canBeRun() OVERRIDE; + virtual void run() OVERRIDE; + + NC::List *m_list; + NC::List::Iterator m_begin; + NC::List::Iterator m_end; +}; + struct ReverseSelection: BaseAction { ReverseSelection(): BaseAction(Type::ReverseSelection, "reverse_selection") { } @@ -869,6 +886,9 @@ struct ReversePlaylist: BaseAction private: virtual bool canBeRun() OVERRIDE; virtual void run() OVERRIDE; + + NC::Menu::ConstIterator m_begin; + NC::Menu::ConstIterator m_end; }; struct Find: BaseAction diff --git a/src/bindings.cpp b/src/bindings.cpp index a0e19099..71ee1d42 100644 --- a/src/bindings.cpp +++ b/src/bindings.cpp @@ -630,6 +630,8 @@ void BindingsConfiguration::generateDefaults() bind(k, Actions::Type::JumpToPositionInSong); if (notBound(k = stringToKey("l"))) bind(k, Actions::Type::ShowLyrics); + if (notBound(k = stringToKey("ctrl_v"))) + bind(k, Actions::Type::SelectRange); if (notBound(k = stringToKey("v"))) bind(k, Actions::Type::ReverseSelection); if (notBound(k = stringToKey("V"))) diff --git a/src/help.cpp b/src/help.cpp index c0f98b0c..98e40558 100644 --- a/src/help.cpp +++ b/src/help.cpp @@ -178,6 +178,7 @@ void write_bindings(NC::Scrollpad &w) w << '\n'; key(w, Type::ToggleAddMode, "Toggle add mode (add or remove/always add)"); key(w, Type::ToggleMouse, "Toggle mouse support"); + key(w, Type::SelectRange, "Select range"); key(w, Type::ReverseSelection, "Reverse selection"); key(w, Type::RemoveSelection, "Remove selection"); key(w, Type::SelectItem, "Select current item"); @@ -191,7 +192,6 @@ void write_bindings(NC::Scrollpad &w) key(w, Type::ToggleConsume, "Toggle consume mode"); key(w, Type::ToggleReplayGainMode, "Toggle replay gain mode"); key(w, Type::ToggleBitrateVisibility, "Toggle bitrate visibility"); - key(w, Type::Shuffle, "Shuffle selected range in playlist"); key(w, Type::ToggleCrossfade, "Toggle crossfade mode"); key(w, Type::SetCrossfade, "Set crossfade"); key(w, Type::SetVolume, "Set volume"); @@ -239,8 +239,9 @@ void write_bindings(NC::Scrollpad &w) key(w, Type::EditSong, "Edit song"); # endif // HAVE_TAGLIB_H key(w, Type::SavePlaylist, "Save playlist"); - key(w, Type::SortPlaylist, "Sort playlist"); - key(w, Type::ReversePlaylist, "Reverse playlist"); + key(w, Type::Shuffle, "Shuffle range"); + key(w, Type::SortPlaylist, "Sort range"); + key(w, Type::ReversePlaylist, "Reverse range"); key(w, Type::JumpToPlayingSong, "Jump to current song"); key(w, Type::TogglePlayingSongCentering, "Toggle playing song centering"); diff --git a/src/helpers.h b/src/helpers.h index 7488c34d..79dd6c64 100644 --- a/src/helpers.h +++ b/src/helpers.h @@ -113,21 +113,40 @@ std::vector getSelected(Iterator first, Iterator last) return result; } -/// @return selected range within given range or original range if no item is selected +/// @return true if range that begins and ends with selected items was found template -std::pair getSelectedRange(Iterator first, Iterator second) +bool findRange(Iterator &first, Iterator &last) { - auto result = std::make_pair(first, second); - if (hasSelected(first, second)) + for (; first != last; ++first) { - while (!result.first->isSelected()) - ++result.first; - do - --result.second; - while (!result.second->isSelected()); - ++result.second; + if (first->isSelected()) + break; } - return result; + if (first == last) + return false; + --last; + for (; first != last; --last) + { + if (last->isSelected()) + break; + } + ++last; + return true; +} + +/// @return true if fully selected range was found, false otherwise. +template +bool findSelectedRange(Iterator &first, Iterator &last) +{ + if (!findRange(first, last)) + return false; + // we have range, now check if it's filled with selected items + for (auto it = first; it != last; ++it) + { + if (!it->isSelected()) + return false; + } + return true; } template diff --git a/src/playlist.cpp b/src/playlist.cpp index d85e6a20..78e26491 100644 --- a/src/playlist.cpp +++ b/src/playlist.cpp @@ -197,18 +197,6 @@ MPD::Song Playlist::nowPlayingSong() return s; } -void Playlist::Reverse() -{ - Statusbar::print("Reversing playlist order..."); - auto begin = w.begin(), end = w.end(); - std::tie(begin, end) = getSelectedRange(begin, end); - Mpd.StartCommandsList(); - for (--end; begin < end; ++begin, --end) - Mpd.Swap(begin->value().getPosition(), end->value().getPosition()); - Mpd.CommitCommandsList(); - Statusbar::print("Playlist reversed"); -} - void Playlist::EnableHighlighting() { w.setHighlighting(true); diff --git a/src/playlist.h b/src/playlist.h index c94c060b..fd1f7813 100644 --- a/src/playlist.h +++ b/src/playlist.h @@ -62,8 +62,7 @@ struct Playlist: Screen, HasSongs, Searchable, Tabbable // private members MPD::Song nowPlayingSong(); - void Reverse(); - + void EnableHighlighting(); void SetSelectedItemsPriority(int prio); diff --git a/src/sort_playlist.cpp b/src/sort_playlist.cpp index 58eba3f3..c21ca71a 100644 --- a/src/sort_playlist.cpp +++ b/src/sort_playlist.cpp @@ -156,8 +156,9 @@ void SortPlaylistDialog::sort() const { auto &pl = myPlaylist->main(); auto begin = pl.begin(), end = pl.end(); - std::tie(begin, end) = getSelectedRange(begin, end); - + if (!findSelectedRange(begin, end)) + return; + size_t start_pos = begin - pl.begin(); std::vector playlist; playlist.reserve(end - begin); @@ -203,7 +204,7 @@ void SortPlaylistDialog::sort() const Mpd.StartCommandsList(); quick_sort(playlist.begin(), playlist.end()); Mpd.CommitCommandsList(); - Statusbar::print("Playlist sorted"); + Statusbar::print("Range sorted"); switchToPreviousScreen(); }