actions: add support for range selection and make a few actions work on ranges

master
Andrzej Rybczak 11 years ago
parent 274c075ffd
commit 1b6cb65f3a
  1. 1
      NEWS
  2. 3
      doc/bindings
  3. 59
      src/actions.cpp
  4. 22
      src/actions.h
  5. 2
      src/bindings.cpp
  6. 7
      src/help.cpp
  7. 41
      src/helpers.h
  8. 12
      src/playlist.cpp
  9. 1
      src/playlist.h
  10. 5
      src/sort_playlist.cpp

@ -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)

@ -415,6 +415,9 @@
#def_key "l"
# show_lyrics
#
#def_key "ctrl_v"
# select_range
#
#def_key "v"
# reverse_selection
#

@ -89,6 +89,15 @@ void seek();
void findItem(const SearchDirection direction);
void listsChangeFinisher();
template <typename Iterator>
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<NC::List *>(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<NC::List *>(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());

@ -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<MPD::Song>::ConstIterator m_begin;
NC::Menu<MPD::Song>::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<MPD::Song>::ConstIterator m_begin;
NC::Menu<MPD::Song>::ConstIterator m_end;
};
struct Find: BaseAction

@ -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")))

@ -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");

@ -113,21 +113,40 @@ std::vector<Iterator> 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 <typename Iterator>
std::pair<Iterator, Iterator> 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 <typename Iterator>
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 <typename T>

@ -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);

@ -62,7 +62,6 @@ struct Playlist: Screen<SongMenu>, HasSongs, Searchable, Tabbable
// private members
MPD::Song nowPlayingSong();
void Reverse();
void EnableHighlighting();

@ -156,7 +156,8 @@ 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<MPD::Song> playlist;
@ -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();
}

Loading…
Cancel
Save