Implement filtering in playlist editor
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
11
src/window.h
11
src/window.h
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user