Implement filtering in playlist editor

This commit is contained in:
Andrzej Rybczak
2016-11-13 05:52:48 +01:00
parent de2513a36c
commit 0477e2e750
6 changed files with 182 additions and 143 deletions

View File

@@ -878,10 +878,11 @@ bool MoveSelectedItemsUp::canBeRun()
void MoveSelectedItemsUp::run()
{
const char *filteredMsg = "Moving items up is disabled in filtered playlist";
if (myScreen == myPlaylist)
{
if (myPlaylist->main().isFiltered())
Statusbar::print("Moving items up is disabled in filtered playlist");
Statusbar::print(filteredMsg);
else
moveSelectedItemsUp(
myPlaylist->main(),
@@ -889,10 +890,15 @@ void MoveSelectedItemsUp::run()
}
else if (myScreen == myPlaylistEditor)
{
assert(!myPlaylistEditor->Playlists.empty());
std::string playlist = myPlaylistEditor->Playlists.current()->value().path();
auto move_fun = std::bind(&MPD::Connection::PlaylistMove, ph::_1, playlist, ph::_2, ph::_3);
moveSelectedItemsUp(myPlaylistEditor->Content, move_fun);
if (myPlaylistEditor->Content.isFiltered())
Statusbar::print(filteredMsg);
else
{
auto playlist = myPlaylistEditor->Playlists.current()->value().path();
moveSelectedItemsUp(
myPlaylistEditor->Content,
std::bind(&MPD::Connection::PlaylistMove, ph::_1, playlist, ph::_2, ph::_3));
}
}
}
@@ -906,10 +912,11 @@ bool MoveSelectedItemsDown::canBeRun()
void MoveSelectedItemsDown::run()
{
const char *filteredMsg = "Moving items down is disabled in filtered playlist";
if (myScreen == myPlaylist)
{
if (myPlaylist->main().isFiltered())
Statusbar::print("Moving items down is disabled in filtered playlist");
Statusbar::print(filteredMsg);
else
moveSelectedItemsDown(
myPlaylist->main(),
@@ -917,10 +924,15 @@ void MoveSelectedItemsDown::run()
}
else if (myScreen == myPlaylistEditor)
{
assert(!myPlaylistEditor->Playlists.empty());
std::string playlist = myPlaylistEditor->Playlists.current()->value().path();
auto move_fun = std::bind(&MPD::Connection::PlaylistMove, ph::_1, playlist, ph::_2, ph::_3);
moveSelectedItemsDown(myPlaylistEditor->Content, move_fun);
if (myPlaylistEditor->Content.isFiltered())
Statusbar::print(filteredMsg);
else
{
auto playlist = myPlaylistEditor->Playlists.current()->value().path();
moveSelectedItemsDown(
myPlaylistEditor->Content,
std::bind(&MPD::Connection::PlaylistMove, ph::_1, playlist, ph::_2, ph::_3));
}
}
}
@@ -1622,7 +1634,7 @@ bool JumpToPlaylistEditor::canBeRun()
void JumpToPlaylistEditor::run()
{
myPlaylistEditor->Locate(myBrowser->main().current()->value().playlist());
myPlaylistEditor->locatePlaylist(myBrowser->main().current()->value().playlist());
}
void ToggleScreenLock::run()

View File

@@ -63,56 +63,6 @@ MPD::SongIterator getDatabaseIterator(MPD::Connection &mpd)
return result;
}
typedef std::vector<MPD::Song>::const_iterator VectorSongIterator;
bool addSongsToPlaylist(VectorSongIterator first, VectorSongIterator last, bool play, int position)
{
bool result = true;
auto addSongNoError = [&](VectorSongIterator song) -> int {
try
{
return Mpd.AddSong(*song, position);
}
catch (MPD::ServerError &e)
{
Status::handleServerError(e);
result = false;
return -1;
}
};
if (last-first >= 1)
{
int id;
while (true)
{
id = addSongNoError(first);
if (id >= 0)
break;
++first;
if (first == last)
return result;
}
if (position == -1)
{
++first;
for(; first != last; ++first)
addSongNoError(first);
}
else
{
++position;
--last;
for (; first != last; --last)
addSongNoError(last);
}
if (play)
Mpd.PlayID(id);
}
return result;
}
void removeSongFromPlaylist(const SongMenu &playlist, const MPD::Song &s)
{
Mpd.StartCommandsList();

View File

@@ -369,7 +369,8 @@ void cropPlaylist(NC::Menu<MPD::Song> &m, F delete_fun)
deleteSelectedSongs(m, delete_fun);
}
template <typename Iterator> std::string getSharedDirectory(Iterator first, Iterator last)
template <typename Iterator>
std::string getSharedDirectory(Iterator first, Iterator last)
{
assert(first != last);
std::string result = first->getDirectory();
@@ -395,6 +396,56 @@ void markSongsInPlaylist(ListT &list)
}
}
template <typename Iterator>
bool addSongsToPlaylist(Iterator first, Iterator last, bool play, int position)
{
bool result = true;
auto addSongNoError = [&](Iterator it) -> int {
try
{
return Mpd.AddSong(*it, position);
}
catch (MPD::ServerError &e)
{
Status::handleServerError(e);
result = false;
return -1;
}
};
if (last-first >= 1)
{
int id;
while (true)
{
id = addSongNoError(first);
if (id >= 0)
break;
++first;
if (first == last)
return result;
}
if (position == -1)
{
++first;
for(; first != last; ++first)
addSongNoError(first);
}
else
{
++position;
--last;
for (; first != last; --last)
addSongNoError(last);
}
if (play)
Mpd.PlayID(id);
}
return result;
}
template <typename T> void ShowTime(T &buf, size_t length, bool short_names)
{
const unsigned MINUTE = 60;
@@ -451,11 +502,6 @@ inline const char *withErrors(bool success)
return success ? "" : " " "(with errors)";
}
bool addSongsToPlaylist(std::vector<MPD::Song>::const_iterator first,
std::vector<MPD::Song>::const_iterator last,
bool play, int position);
bool addSongToPlaylist(const MPD::Song &s, bool play, int position = -1);
const MPD::Song *currentSong(const BaseScreen *screen);

View File

@@ -79,7 +79,7 @@ PlaylistEditor::PlaylistEditor()
menu << Charset::utf8ToLocale(menu.drawn()->value().path());
});
Content = NC::Menu<MPD::Song>(RightColumnStartX, MainStartY, RightColumnWidth, MainHeight, Config.titles_visibility ? "Playlist content" : "", Config.main_color, NC::Border());
Content = NC::Menu<MPD::Song>(RightColumnStartX, MainStartY, RightColumnWidth, MainHeight, Config.titles_visibility ? "Content" : "", Config.main_color, NC::Border());
Content.setHighlightColor(Config.main_highlight_color);
Content.cyclicScrolling(Config.use_cyclic_scrolling);
Content.centeredCursor(Config.centered_cursor);
@@ -143,42 +143,45 @@ void PlaylistEditor::switchTo()
void PlaylistEditor::update()
{
if (Playlists.empty() || m_playlists_update_requested)
{
m_playlists_update_requested = false;
size_t idx = 0;
try
ScopedUnfilteredMenu<MPD::Playlist> sunfilter_playlists(ReapplyFilter::No, Playlists);
if (Playlists.empty() || m_playlists_update_requested)
{
for (MPD::PlaylistIterator it = Mpd.GetPlaylists(), end; it != end; ++it, ++idx)
m_playlists_update_requested = false;
sunfilter_playlists.set(ReapplyFilter::Yes, true);
size_t idx = 0;
try
{
if (idx < Playlists.size())
Playlists[idx].value() = std::move(*it);
for (MPD::PlaylistIterator it = Mpd.GetPlaylists(), end; it != end; ++it, ++idx)
{
if (idx < Playlists.size())
Playlists[idx].value() = std::move(*it);
else
Playlists.addItem(std::move(*it));
};
}
catch (MPD::ServerError &e)
{
if (e.code() == MPD_SERVER_ERROR_SYSTEM) // no playlists directory
Statusbar::print(e.what());
else
Playlists.addItem(std::move(*it));
};
throw;
}
if (idx < Playlists.size())
Playlists.resizeList(idx);
std::sort(Playlists.beginV(), Playlists.endV(),
LocaleBasedSorting(std::locale(), Config.ignore_leading_the));
}
catch (MPD::ServerError &e)
{
if (e.code() == MPD_SERVER_ERROR_SYSTEM) // no playlists directory
Statusbar::print(e.what());
else
throw;
}
if (idx < Playlists.size())
Playlists.resizeList(idx);
std::sort(Playlists.beginV(), Playlists.endV(),
LocaleBasedSorting(std::locale(), Config.ignore_leading_the));
Playlists.refresh();
}
if ((Content.empty() && Global::Timer - m_timer > m_fetching_delay)
|| m_content_update_requested)
{
m_content_update_requested = false;
if (Playlists.empty())
Content.clear();
else
ScopedUnfilteredMenu<MPD::Song> sunfilter_content(ReapplyFilter::No, Content);
if (!Playlists.empty()
&& ((Content.empty() && Global::Timer - m_timer > m_fetching_delay)
|| m_content_update_requested))
{
m_content_update_requested = false;
sunfilter_content.set(ReapplyFilter::Yes, true);
size_t idx = 0;
MPD::SongIterator s = Mpd.GetPlaylistContent(Playlists.current()->value().path()), end;
for (; s != end; ++s, ++idx)
@@ -202,33 +205,20 @@ void PlaylistEditor::update()
std::string wtitle;
if (Config.titles_visibility)
{
wtitle = (boost::format("Playlist content (%1%) %2%")
% boost::lexical_cast<std::string>(Content.size())
% (Content.size() == 1 ? "item" : "items")
).str();
wtitle = (boost::format("Content (%1% %2%)")
% boost::lexical_cast<std::string>(Content.size())
% (Content.size() == 1 ? "item" : "items")).str();
wtitle.resize(Content.getWidth());
}
Content.setTitle(wtitle);
Content.refreshBorder();
}
Content.display();
}
if (isActiveWindow(Content) && Content.empty())
{
Content.setHighlightColor(Config.main_highlight_color);
Playlists.setHighlightColor(Config.active_column_color);
w = &Playlists;
}
if (Playlists.empty() && Content.empty())
{
Content.Window::clear();
Content.Window::display();
}
}
int PlaylistEditor::windowTimeout()
{
ScopedUnfilteredMenu<MPD::Song> sunfilter_content(ReapplyFilter::No, Content);
if (Content.empty())
return m_window_timeout;
else
@@ -328,6 +318,51 @@ bool PlaylistEditor::search(SearchDirection direction, bool wrap, bool skip_curr
return result;
}
std::string PlaylistEditor::currentFilter()
{
std::string result;
if (isActiveWindow(Playlists))
{
if (auto pred = Playlists.filterPredicate<Regex::Filter<MPD::Playlist>>())
result = pred->constraint();
}
else if (isActiveWindow(Content))
{
if (auto pred = Content.filterPredicate<Regex::Filter<MPD::Song>>())
result = pred->constraint();
}
return result;
}
void PlaylistEditor::applyFilter(const std::string &constraint)
{
if (isActiveWindow(Playlists))
{
if (!constraint.empty())
{
Playlists.applyFilter(Regex::Filter<MPD::Playlist>(
constraint,
Config.regex_type,
PlaylistEntryMatcher));
}
else
Playlists.clearFilter();
}
else if (isActiveWindow(Content))
{
if (!constraint.empty())
{
Content.applyFilter(Regex::Filter<MPD::Song>(
constraint,
Config.regex_type,
SongEntryMatcher));
}
else
Content.clearFilter();
}
}
/***********************************************************************/
bool PlaylistEditor::itemAvailable()
@@ -344,14 +379,10 @@ bool PlaylistEditor::addItemToPlaylist(bool play)
bool result = false;
if (isActiveWindow(Playlists))
{
std::vector<MPD::Song> list(
std::make_move_iterator(Mpd.GetPlaylistContent(Playlists.current()->value().path())),
std::make_move_iterator(MPD::SongIterator())
);
result = addSongsToPlaylist(list.begin(), list.end(), play, -1);
ScopedUnfilteredMenu<MPD::Song> sunfilter_content(ReapplyFilter::No, Content);
result = addSongsToPlaylist(Content.beginV(), Content.endV(), play, -1);
Statusbar::printf("Playlist \"%1%\" loaded%2%",
Playlists.current()->value().path(), withErrors(result)
);
Playlists.current()->value().path(), withErrors(result));
}
else if (isActiveWindow(Content))
result = addSongToPlaylist(Content.current()->value(), play);
@@ -372,19 +403,13 @@ std::vector<MPD::Song> PlaylistEditor::getSelectedSongs()
std::copy(
std::make_move_iterator(Mpd.GetPlaylistContent(e.value().path())),
std::make_move_iterator(MPD::SongIterator()),
std::back_inserter(result)
);
std::back_inserter(result));
}
}
// if no item is selected, add songs from right column
ScopedUnfilteredMenu<MPD::Song> sunfilter_content(ReapplyFilter::No, Content);
if (!any_selected && !Playlists.empty())
{
std::copy(
std::make_move_iterator(Mpd.GetPlaylistContent(Playlists.current()->value().path())),
std::make_move_iterator(MPD::SongIterator()),
std::back_inserter(result)
);
}
std::copy(Content.beginV(), Content.endV(), std::back_inserter(result));
}
else if (isActiveWindow(Content))
result = Content.getSelectedSongs();
@@ -397,6 +422,7 @@ bool PlaylistEditor::previousColumnAvailable()
{
if (isActiveWindow(Content))
{
ScopedUnfilteredMenu<MPD::Playlist> sunfilter_playlists(ReapplyFilter::No, Playlists);
if (!Playlists.empty())
return true;
}
@@ -418,6 +444,7 @@ bool PlaylistEditor::nextColumnAvailable()
{
if (isActiveWindow(Playlists))
{
ScopedUnfilteredMenu<MPD::Song> sunfilter_content(ReapplyFilter::No, Content);
if (!Content.empty())
return true;
}
@@ -442,15 +469,17 @@ void PlaylistEditor::updateTimer()
m_timer = Global::Timer;
}
void PlaylistEditor::Locate(const MPD::Playlist &playlist)
void PlaylistEditor::locatePlaylist(const MPD::Playlist &playlist)
{
update();
auto begin = Playlists.beginV(), end = Playlists.endV();
auto it = std::find(begin, end, playlist);
if (it != end)
Playlists.clearFilter();
auto first = Playlists.beginV(), last = Playlists.endV();
auto it = std::find(first, last, playlist);
if (it != last)
{
Playlists.highlight(it-begin);
Playlists.highlight(it - first);
Content.clear();
Content.clearFilter();
switchTo();
}
}

View File

@@ -54,7 +54,10 @@ struct PlaylistEditor: Screen<NC::Window *>, HasColumns, HasSongs, Searchable, T
virtual void setSearchConstraint(const std::string &constraint) OVERRIDE;
virtual void clearSearchConstraint() OVERRIDE;
virtual bool search(SearchDirection direction, bool wrap, bool skip_current) OVERRIDE;
virtual std::string currentFilter() OVERRIDE;
virtual void applyFilter(const std::string &filter) OVERRIDE;
// HasSongs implementation
virtual bool itemAvailable() OVERRIDE;
virtual bool addItemToPlaylist(bool play) OVERRIDE;
@@ -73,7 +76,7 @@ struct PlaylistEditor: Screen<NC::Window *>, HasColumns, HasSongs, Searchable, T
void requestPlaylistsUpdate() { m_playlists_update_requested = true; }
void requestContentsUpdate() { m_content_update_requested = true; }
virtual void Locate(const MPD::Playlist &playlist);
void locatePlaylist(const MPD::Playlist &playlist);
NC::Menu<MPD::Playlist> Playlists;
SongMenu Content;

View File

@@ -371,11 +371,14 @@ struct Window
/// Refreshed whole window and its border
/// @see refresh()
void display();
/// Refreshes window's border
void refreshBorder() const;
/// Refreshes whole window, but not the border
/// @see display()
virtual void refresh();
/// Moves the window to new coordinates
/// @param new_x new X position of left upper corner of window
/// @param new_y new Y position of left upper corner of window
@@ -437,10 +440,6 @@ protected:
///
void setColor(Color c);
/// Refreshes window's border
///
void refreshBorder() const;
/// Changes dimensions of window, called from resize()
/// @param width new window's width
/// @param height new window's height