remove filtering
This commit is contained in:
@@ -142,7 +142,6 @@
|
|||||||
## The following actions are not bound to any key/command:
|
## The following actions are not bound to any key/command:
|
||||||
##
|
##
|
||||||
## - set_volume
|
## - set_volume
|
||||||
## - filter_playlist_on_priorities
|
|
||||||
##
|
##
|
||||||
#
|
#
|
||||||
#def_key "mouse"
|
#def_key "mouse"
|
||||||
@@ -352,9 +351,6 @@
|
|||||||
#def_key "ctrl_r"
|
#def_key "ctrl_r"
|
||||||
# reverse_playlist
|
# reverse_playlist
|
||||||
#
|
#
|
||||||
#def_key "ctrl_f"
|
|
||||||
# apply_filter
|
|
||||||
#
|
|
||||||
#def_key "/"
|
#def_key "/"
|
||||||
# find
|
# find
|
||||||
#
|
#
|
||||||
|
|||||||
166
src/actions.cpp
166
src/actions.cpp
@@ -800,35 +800,24 @@ void SavePlaylist::run()
|
|||||||
Statusbar::put() << "Save playlist as: ";
|
Statusbar::put() << "Save playlist as: ";
|
||||||
playlist_name = wFooter->prompt();
|
playlist_name = wFooter->prompt();
|
||||||
}
|
}
|
||||||
if (myPlaylist->main().isFiltered())
|
try
|
||||||
{
|
{
|
||||||
Mpd.StartCommandsList();
|
Mpd.SavePlaylist(playlist_name);
|
||||||
for (const auto &item : myPlaylist->main())
|
Statusbar::printf("Playlist saved as \"%1%\"", playlist_name);
|
||||||
Mpd.AddToPlaylist(playlist_name, item.value());
|
|
||||||
Mpd.CommitCommandsList();
|
|
||||||
Statusbar::printf("Filtered items added to playlist \"%1%\"", playlist_name);
|
|
||||||
}
|
}
|
||||||
else
|
catch (MPD::ServerError &e)
|
||||||
{
|
{
|
||||||
try
|
if (e.code() == MPD_SERVER_ERROR_EXIST)
|
||||||
{
|
{
|
||||||
|
confirmAction(
|
||||||
|
boost::format("Playlist \"%1%\" already exists, overwrite?") % playlist_name
|
||||||
|
);
|
||||||
|
Mpd.DeletePlaylist(playlist_name);
|
||||||
Mpd.SavePlaylist(playlist_name);
|
Mpd.SavePlaylist(playlist_name);
|
||||||
Statusbar::printf("Playlist saved as \"%1%\"", playlist_name);
|
Statusbar::print("Playlist overwritten");
|
||||||
}
|
|
||||||
catch (MPD::ServerError &e)
|
|
||||||
{
|
|
||||||
if (e.code() == MPD_SERVER_ERROR_EXIST)
|
|
||||||
{
|
|
||||||
confirmAction(
|
|
||||||
boost::format("Playlist \"%1%\" already exists, overwrite?") % playlist_name
|
|
||||||
);
|
|
||||||
Mpd.DeletePlaylist(playlist_name);
|
|
||||||
Mpd.SavePlaylist(playlist_name);
|
|
||||||
Statusbar::print("Playlist overwritten");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -887,11 +876,9 @@ void MoveSortOrderDown::run()
|
|||||||
bool MoveSelectedItemsUp::canBeRun() const
|
bool MoveSelectedItemsUp::canBeRun() const
|
||||||
{
|
{
|
||||||
return ((myScreen == myPlaylist
|
return ((myScreen == myPlaylist
|
||||||
&& !myPlaylist->main().empty()
|
&& !myPlaylist->main().empty())
|
||||||
&& !myPlaylist->isFiltered())
|
|
||||||
|| (myScreen->isActiveWindow(myPlaylistEditor->Content)
|
|| (myScreen->isActiveWindow(myPlaylistEditor->Content)
|
||||||
&& !myPlaylistEditor->Content.empty()
|
&& !myPlaylistEditor->Content.empty()));
|
||||||
&& !myPlaylistEditor->isContentFiltered()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MoveSelectedItemsUp::run()
|
void MoveSelectedItemsUp::run()
|
||||||
@@ -912,11 +899,9 @@ void MoveSelectedItemsUp::run()
|
|||||||
bool MoveSelectedItemsDown::canBeRun() const
|
bool MoveSelectedItemsDown::canBeRun() const
|
||||||
{
|
{
|
||||||
return ((myScreen == myPlaylist
|
return ((myScreen == myPlaylist
|
||||||
&& !myPlaylist->main().empty()
|
&& !myPlaylist->main().empty())
|
||||||
&& !myPlaylist->isFiltered())
|
|
||||||
|| (myScreen->isActiveWindow(myPlaylistEditor->Content)
|
|| (myScreen->isActiveWindow(myPlaylistEditor->Content)
|
||||||
&& !myPlaylistEditor->Content.empty()
|
&& !myPlaylistEditor->Content.empty()));
|
||||||
&& !myPlaylistEditor->isContentFiltered()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MoveSelectedItemsDown::run()
|
void MoveSelectedItemsDown::run()
|
||||||
@@ -1154,7 +1139,7 @@ void TogglePlayingSongCentering::run()
|
|||||||
Statusbar::printf("Centering playing song: %1%",
|
Statusbar::printf("Centering playing song: %1%",
|
||||||
Config.autocenter_mode ? "on" : "off"
|
Config.autocenter_mode ? "on" : "off"
|
||||||
);
|
);
|
||||||
if (Config.autocenter_mode && !myPlaylist->main().isFiltered())
|
if (Config.autocenter_mode)
|
||||||
{
|
{
|
||||||
auto s = myPlaylist->nowPlayingSong();
|
auto s = myPlaylist->nowPlayingSong();
|
||||||
if (!s.empty())
|
if (!s.empty())
|
||||||
@@ -1176,9 +1161,9 @@ void UpdateDatabase::run()
|
|||||||
|
|
||||||
bool JumpToPlayingSong::canBeRun() const
|
bool JumpToPlayingSong::canBeRun() const
|
||||||
{
|
{
|
||||||
return (myScreen == myPlaylist && !myPlaylist->isFiltered())
|
return myScreen == myPlaylist
|
||||||
|| myScreen == myBrowser
|
|| myScreen == myBrowser
|
||||||
|| myScreen == myLibrary;
|
|| myScreen == myLibrary;
|
||||||
}
|
}
|
||||||
|
|
||||||
void JumpToPlayingSong::run()
|
void JumpToPlayingSong::run()
|
||||||
@@ -1791,11 +1776,8 @@ void ClearMainPlaylist::run()
|
|||||||
{
|
{
|
||||||
if (Config.ask_before_clearing_playlists)
|
if (Config.ask_before_clearing_playlists)
|
||||||
confirmAction("Do you really want to clear main playlist?");
|
confirmAction("Do you really want to clear main playlist?");
|
||||||
auto delete_fun = boost::bind(&MPD::Connection::Delete, _1, _2);
|
Mpd.ClearMainPlaylist();
|
||||||
auto clear_fun = boost::bind(&MPD::Connection::ClearMainPlaylist, _1);
|
Statusbar::print("Playlist cleared");
|
||||||
Statusbar::printf("Deleting items...");
|
|
||||||
clearPlaylist(myPlaylist->main(), delete_fun, clear_fun);
|
|
||||||
Statusbar::printf("Items deleted");
|
|
||||||
myPlaylist->main().reset();
|
myPlaylist->main().reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1811,11 +1793,8 @@ void ClearPlaylist::run()
|
|||||||
std::string playlist = myPlaylistEditor->Playlists.current()->value().path();
|
std::string playlist = myPlaylistEditor->Playlists.current()->value().path();
|
||||||
if (Config.ask_before_clearing_playlists)
|
if (Config.ask_before_clearing_playlists)
|
||||||
confirmAction(boost::format("Do you really want to clear playlist \"%1%\"?") % playlist);
|
confirmAction(boost::format("Do you really want to clear playlist \"%1%\"?") % playlist);
|
||||||
auto delete_fun = boost::bind(&MPD::Connection::PlaylistDelete, _1, playlist, _2);
|
Mpd.ClearPlaylist(playlist);
|
||||||
auto clear_fun = boost::bind(&MPD::Connection::ClearPlaylist, _1, playlist);
|
Statusbar::printf("Playlist \"%1%\" cleared", playlist);
|
||||||
Statusbar::printf("Deleting items from \"%1%\"...", playlist);
|
|
||||||
clearPlaylist(myPlaylistEditor->Content, delete_fun, clear_fun);
|
|
||||||
Statusbar::printf("Items deleted from \"%1%\"", playlist);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SortPlaylist::canBeRun() const
|
bool SortPlaylist::canBeRun() const
|
||||||
@@ -1838,62 +1817,6 @@ void ReversePlaylist::run()
|
|||||||
myPlaylist->Reverse();
|
myPlaylist->Reverse();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ApplyFilter::canBeRun() const
|
|
||||||
{
|
|
||||||
auto w = dynamic_cast<Filterable *>(myScreen);
|
|
||||||
return w && w->allowsFiltering();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ApplyFilter::run()
|
|
||||||
{
|
|
||||||
using Global::wFooter;
|
|
||||||
|
|
||||||
Filterable *f = dynamic_cast<Filterable *>(myScreen);
|
|
||||||
std::string filter = f->currentFilter();
|
|
||||||
// if filter is already here, apply it
|
|
||||||
if (!filter.empty())
|
|
||||||
{
|
|
||||||
f->applyFilter(filter);
|
|
||||||
myScreen->refreshWindow();
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Statusbar::ScopedLock slock;
|
|
||||||
NC::Window::ScopedPromptHook helper(*wFooter,
|
|
||||||
Statusbar::Helpers::ApplyFilterImmediately(f, filter)
|
|
||||||
);
|
|
||||||
Statusbar::put() << NC::Format::Bold << "Apply filter: " << NC::Format::NoBold;
|
|
||||||
wFooter->prompt(filter);
|
|
||||||
}
|
|
||||||
catch (NC::PromptAborted &)
|
|
||||||
{
|
|
||||||
// restore previous filter
|
|
||||||
f->applyFilter(filter);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
filter = f->currentFilter();
|
|
||||||
if (filter.empty())
|
|
||||||
{
|
|
||||||
myPlaylist->main().clearFilterResults();
|
|
||||||
Statusbar::printf("Filtering disabled");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// apply filter here so even if old one wasn't modified
|
|
||||||
// (and callback wasn't invoked), it still gets applied.
|
|
||||||
f->applyFilter(filter);
|
|
||||||
Statusbar::printf("Using filter \"%1%\"", filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
// recalculate total length of songs in playlist as it probably changed.
|
|
||||||
// TODO: check where drawHeader is invoked.
|
|
||||||
if (myScreen == myPlaylist)
|
|
||||||
myPlaylist->reloadTotalLength();
|
|
||||||
listsChangeFinisher();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Find::canBeRun() const
|
bool Find::canBeRun() const
|
||||||
{
|
{
|
||||||
return myScreen == myHelp
|
return myScreen == myHelp
|
||||||
@@ -2119,15 +2042,13 @@ void ToggleBrowserSortMode::run()
|
|||||||
Config.browser_sort_mode = SortMode::Name;
|
Config.browser_sort_mode = SortMode::Name;
|
||||||
Statusbar::print("Sort songs by: name");
|
Statusbar::print("Sort songs by: name");
|
||||||
}
|
}
|
||||||
withUnfilteredMenuReapplyFilter(myBrowser->main(), [] {
|
if (Config.browser_sort_mode != SortMode::NoOp)
|
||||||
if (Config.browser_sort_mode != SortMode::NoOp)
|
{
|
||||||
{
|
size_t sort_offset = myBrowser->inRootDirectory() ? 0 : 1;
|
||||||
size_t sort_offset = myBrowser->inRootDirectory() ? 0 : 1;
|
std::sort(myBrowser->main().begin()+sort_offset, myBrowser->main().end(),
|
||||||
std::sort(myBrowser->main().begin()+sort_offset, myBrowser->main().end(),
|
LocaleBasedItemSorting(std::locale(), Config.ignore_leading_the, Config.browser_sort_mode)
|
||||||
LocaleBasedItemSorting(std::locale(), Config.ignore_leading_the, Config.browser_sort_mode)
|
);
|
||||||
);
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ToggleLibraryTagType::canBeRun() const
|
bool ToggleLibraryTagType::canBeRun() const
|
||||||
@@ -2257,29 +2178,6 @@ void SetVisualizerSampleMultiplier::run()
|
|||||||
# endif // ENABLE_VISUALIZER
|
# endif // ENABLE_VISUALIZER
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FilterPlaylistOnPriorities::canBeRun() const
|
|
||||||
{
|
|
||||||
return myScreen == myPlaylist;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FilterPlaylistOnPriorities::run()
|
|
||||||
{
|
|
||||||
using Global::wFooter;
|
|
||||||
|
|
||||||
unsigned prio;
|
|
||||||
{
|
|
||||||
Statusbar::ScopedLock slock;
|
|
||||||
Statusbar::put() << "Show songs with priority higher than: ";
|
|
||||||
prio = fromString<unsigned>(wFooter->prompt());
|
|
||||||
boundsCheck(prio, 0u, 255u);
|
|
||||||
myPlaylist->main().filter(myPlaylist->main().begin(), myPlaylist->main().end(),
|
|
||||||
[prio](const NC::Menu<MPD::Song>::Item &s) {
|
|
||||||
return s.value().getPrio() > prio;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Statusbar::printf("Playlist filtered (songs with priority higher than %1%)", prio);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShowSongInfo::run()
|
void ShowSongInfo::run()
|
||||||
{
|
{
|
||||||
mySongInfo->switchTo();
|
mySongInfo->switchTo();
|
||||||
@@ -2668,7 +2566,6 @@ void populateActions()
|
|||||||
insert_action(new Actions::ClearPlaylist());
|
insert_action(new Actions::ClearPlaylist());
|
||||||
insert_action(new Actions::SortPlaylist());
|
insert_action(new Actions::SortPlaylist());
|
||||||
insert_action(new Actions::ReversePlaylist());
|
insert_action(new Actions::ReversePlaylist());
|
||||||
insert_action(new Actions::ApplyFilter());
|
|
||||||
insert_action(new Actions::Find());
|
insert_action(new Actions::Find());
|
||||||
insert_action(new Actions::FindItemForward());
|
insert_action(new Actions::FindItemForward());
|
||||||
insert_action(new Actions::FindItemBackward());
|
insert_action(new Actions::FindItemBackward());
|
||||||
@@ -2687,7 +2584,6 @@ void populateActions()
|
|||||||
insert_action(new Actions::RefetchLyrics());
|
insert_action(new Actions::RefetchLyrics());
|
||||||
insert_action(new Actions::SetSelectedItemsPriority());
|
insert_action(new Actions::SetSelectedItemsPriority());
|
||||||
insert_action(new Actions::SetVisualizerSampleMultiplier());
|
insert_action(new Actions::SetVisualizerSampleMultiplier());
|
||||||
insert_action(new Actions::FilterPlaylistOnPriorities());
|
|
||||||
insert_action(new Actions::ShowSongInfo());
|
insert_action(new Actions::ShowSongInfo());
|
||||||
insert_action(new Actions::ShowArtistInfo());
|
insert_action(new Actions::ShowArtistInfo());
|
||||||
insert_action(new Actions::ShowLyrics());
|
insert_action(new Actions::ShowLyrics());
|
||||||
|
|||||||
@@ -47,12 +47,12 @@ enum class Type
|
|||||||
JumpToPlaylistEditor, ToggleScreenLock, JumpToTagEditor, JumpToPositionInSong,
|
JumpToPlaylistEditor, ToggleScreenLock, JumpToTagEditor, JumpToPositionInSong,
|
||||||
ReverseSelection, RemoveSelection, SelectAlbum, AddSelectedItems,
|
ReverseSelection, RemoveSelection, SelectAlbum, AddSelectedItems,
|
||||||
CropMainPlaylist, CropPlaylist, ClearMainPlaylist, ClearPlaylist, SortPlaylist,
|
CropMainPlaylist, CropPlaylist, ClearMainPlaylist, ClearPlaylist, SortPlaylist,
|
||||||
ReversePlaylist, ApplyFilter, Find, FindItemForward, FindItemBackward,
|
ReversePlaylist, Find, FindItemForward, FindItemBackward,
|
||||||
NextFoundItem, PreviousFoundItem, ToggleFindMode, ToggleReplayGainMode,
|
NextFoundItem, PreviousFoundItem, ToggleFindMode, ToggleReplayGainMode,
|
||||||
ToggleSpaceMode, ToggleAddMode, ToggleMouse, ToggleBitrateVisibility,
|
ToggleSpaceMode, ToggleAddMode, ToggleMouse, ToggleBitrateVisibility,
|
||||||
AddRandomItems, ToggleBrowserSortMode, ToggleLibraryTagType,
|
AddRandomItems, ToggleBrowserSortMode, ToggleLibraryTagType,
|
||||||
ToggleMediaLibrarySortMode, RefetchLyrics,
|
ToggleMediaLibrarySortMode, RefetchLyrics,
|
||||||
SetSelectedItemsPriority, SetVisualizerSampleMultiplier, FilterPlaylistOnPriorities,
|
SetSelectedItemsPriority, SetVisualizerSampleMultiplier,
|
||||||
ShowSongInfo, ShowArtistInfo, ShowLyrics, Quit, NextScreen, PreviousScreen,
|
ShowSongInfo, ShowArtistInfo, ShowLyrics, Quit, NextScreen, PreviousScreen,
|
||||||
ShowHelp, ShowPlaylist, ShowBrowser, ChangeBrowseMode, ShowSearchEngine,
|
ShowHelp, ShowPlaylist, ShowBrowser, ChangeBrowseMode, ShowSearchEngine,
|
||||||
ResetSearchEngine, ShowMediaLibrary, ToggleMediaLibraryColumnsMode,
|
ResetSearchEngine, ShowMediaLibrary, ToggleMediaLibraryColumnsMode,
|
||||||
@@ -802,15 +802,6 @@ protected:
|
|||||||
virtual void run();
|
virtual void run();
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ApplyFilter : public BaseAction
|
|
||||||
{
|
|
||||||
ApplyFilter() : BaseAction(Type::ApplyFilter, "apply_filter") { }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual bool canBeRun() const;
|
|
||||||
virtual void run();
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Find : public BaseAction
|
struct Find : public BaseAction
|
||||||
{
|
{
|
||||||
Find() : BaseAction(Type::Find, "find") { }
|
Find() : BaseAction(Type::Find, "find") { }
|
||||||
@@ -969,16 +960,6 @@ protected:
|
|||||||
virtual void run();
|
virtual void run();
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FilterPlaylistOnPriorities : public BaseAction
|
|
||||||
{
|
|
||||||
FilterPlaylistOnPriorities()
|
|
||||||
: BaseAction(Type::FilterPlaylistOnPriorities, "filter_playlist_on_priorities") { }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual bool canBeRun() const;
|
|
||||||
virtual void run();
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ShowSongInfo : public BaseAction
|
struct ShowSongInfo : public BaseAction
|
||||||
{
|
{
|
||||||
ShowSongInfo() : BaseAction(Type::ShowSongInfo, "show_song_info") { }
|
ShowSongInfo() : BaseAction(Type::ShowSongInfo, "show_song_info") { }
|
||||||
|
|||||||
@@ -482,8 +482,6 @@ void BindingsConfiguration::generateDefaults()
|
|||||||
bind(k, Actions::Type::SortPlaylist);
|
bind(k, Actions::Type::SortPlaylist);
|
||||||
if (notBound(k = stringToKey("ctrl_r")))
|
if (notBound(k = stringToKey("ctrl_r")))
|
||||||
bind(k, Actions::Type::ReversePlaylist);
|
bind(k, Actions::Type::ReversePlaylist);
|
||||||
if (notBound(k = stringToKey("ctrl_f")))
|
|
||||||
bind(k, Actions::Type::ApplyFilter);
|
|
||||||
if (notBound(k = stringToKey("/")))
|
if (notBound(k = stringToKey("/")))
|
||||||
{
|
{
|
||||||
bind(k, Actions::Type::Find);
|
bind(k, Actions::Type::Find);
|
||||||
|
|||||||
@@ -248,37 +248,6 @@ void Browser::mouseButtonPressed(MEVENT me)
|
|||||||
|
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
|
|
||||||
bool Browser::allowsFiltering()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Browser::currentFilter()
|
|
||||||
{
|
|
||||||
return RegexFilter<MPD::Item>::currentFilter(w);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Browser::applyFilter(const std::string &filter)
|
|
||||||
{
|
|
||||||
if (filter.empty())
|
|
||||||
{
|
|
||||||
w.clearFilter();
|
|
||||||
w.clearFilterResults();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
w.showAll();
|
|
||||||
auto fun = boost::bind(browserEntryMatcher, _1, _2, true);
|
|
||||||
auto rx = RegexFilter<MPD::Item>(
|
|
||||||
boost::regex(filter, Config.regex_type), fun);
|
|
||||||
w.filter(w.begin(), w.end(), rx);
|
|
||||||
}
|
|
||||||
catch (boost::bad_expression &) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
/***********************************************************************/
|
|
||||||
|
|
||||||
bool Browser::allowsSearching()
|
bool Browser::allowsSearching()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
#include "regex_filter.h"
|
#include "regex_filter.h"
|
||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
|
|
||||||
struct Browser: Screen<NC::Menu<MPD::Item>>, Filterable, HasSongs, Searchable, Tabbable
|
struct Browser: Screen<NC::Menu<MPD::Item>>, HasSongs, Searchable, Tabbable
|
||||||
{
|
{
|
||||||
Browser();
|
Browser();
|
||||||
|
|
||||||
@@ -45,11 +45,6 @@ struct Browser: Screen<NC::Menu<MPD::Item>>, Filterable, HasSongs, Searchable, T
|
|||||||
|
|
||||||
virtual bool isMergable() OVERRIDE { return true; }
|
virtual bool isMergable() OVERRIDE { return true; }
|
||||||
|
|
||||||
// Filterable implementation
|
|
||||||
virtual bool allowsFiltering() OVERRIDE;
|
|
||||||
virtual std::string currentFilter() OVERRIDE;
|
|
||||||
virtual void applyFilter(const std::string &filter) OVERRIDE;
|
|
||||||
|
|
||||||
// Searchable implementation
|
// Searchable implementation
|
||||||
virtual bool allowsSearching() OVERRIDE;
|
virtual bool allowsSearching() OVERRIDE;
|
||||||
virtual bool setSearchConstraint(const std::string &constraint) OVERRIDE;
|
virtual bool setSearchConstraint(const std::string &constraint) OVERRIDE;
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ void setProperties(NC::Menu<T> &menu, const MPD::Song &s, const ProxySongList &p
|
|||||||
is_selected = menu.drawn()->isSelected();
|
is_selected = menu.drawn()->isSelected();
|
||||||
discard_colors = Config.discard_colors_if_item_is_selected && is_selected;
|
discard_colors = Config.discard_colors_if_item_is_selected && is_selected;
|
||||||
|
|
||||||
int song_pos = menu.isFiltered() ? s.getPosition() : drawn_pos;
|
int song_pos = drawn_pos;
|
||||||
is_now_playing = Status::State::player() != MPD::psStop && myPlaylist->isActiveWindow(menu)
|
is_now_playing = Status::State::player() != MPD::psStop && myPlaylist->isActiveWindow(menu)
|
||||||
&& song_pos == Status::State::currentSongPosition();
|
&& song_pos == Status::State::currentSongPosition();
|
||||||
if (is_now_playing)
|
if (is_now_playing)
|
||||||
|
|||||||
@@ -245,7 +245,6 @@ void write_bindings(NC::Scrollpad &w)
|
|||||||
key(w, Type::UpdateDatabase, "Start music database update");
|
key(w, Type::UpdateDatabase, "Start music database update");
|
||||||
w << '\n';
|
w << '\n';
|
||||||
key(w, Type::ExecuteCommand, "Execute command");
|
key(w, Type::ExecuteCommand, "Execute command");
|
||||||
key(w, Type::ApplyFilter, "Apply filter");
|
|
||||||
key(w, Type::FindItemForward, "Find item forward");
|
key(w, Type::FindItemForward, "Find item forward");
|
||||||
key(w, Type::FindItemBackward, "Find item backward");
|
key(w, Type::FindItemBackward, "Find item backward");
|
||||||
key(w, Type::PreviousFoundItem, "Jump to previous found item");
|
key(w, Type::PreviousFoundItem, "Jump to previous found item");
|
||||||
@@ -289,7 +288,6 @@ void write_bindings(NC::Scrollpad &w)
|
|||||||
key(w, Type::SavePlaylist, "Save playlist");
|
key(w, Type::SavePlaylist, "Save playlist");
|
||||||
key(w, Type::SortPlaylist, "Sort playlist");
|
key(w, Type::SortPlaylist, "Sort playlist");
|
||||||
key(w, Type::ReversePlaylist, "Reverse playlist");
|
key(w, Type::ReversePlaylist, "Reverse playlist");
|
||||||
key(w, Type::FilterPlaylistOnPriorities, "Filter playlist on priorities");
|
|
||||||
key(w, Type::JumpToPlayingSong, "Jump to current song");
|
key(w, Type::JumpToPlayingSong, "Jump to current song");
|
||||||
key(w, Type::TogglePlayingSongCentering, "Toggle playing song centering");
|
key(w, Type::TogglePlayingSongCentering, "Toggle playing song centering");
|
||||||
|
|
||||||
|
|||||||
170
src/helpers.h
170
src/helpers.h
@@ -61,7 +61,7 @@ void searchBackward(NC::Menu<ItemT> &m, const PredicateT &pred, bool wrap)
|
|||||||
return;
|
return;
|
||||||
auto it = wrappedSearch(m.rbegin(), m.rcurrent(), m.rend(), pred, wrap);
|
auto it = wrappedSearch(m.rbegin(), m.rcurrent(), m.rend(), pred, wrap);
|
||||||
if (it != m.rend())
|
if (it != m.rend())
|
||||||
m.highlight(m.size()-1-(it-m.rbegin()));
|
m.highlight(it.base()-m.begin()-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline HasColumns *hasColumns(BaseScreen *screen)
|
inline HasColumns *hasColumns(BaseScreen *screen)
|
||||||
@@ -149,54 +149,6 @@ void reverseSelectionHelper(Iterator first, Iterator last)
|
|||||||
first->setSelected(!first->isSelected());
|
first->setSelected(!first->isSelected());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename F>
|
|
||||||
void withUnfilteredMenu(NC::Menu<T> &m, F action)
|
|
||||||
{
|
|
||||||
bool is_filtered = m.isFiltered();
|
|
||||||
auto cleanup = [&]() {
|
|
||||||
if (is_filtered)
|
|
||||||
m.showFiltered();
|
|
||||||
};
|
|
||||||
m.showAll();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
action();
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
cleanup();
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename F>
|
|
||||||
void withUnfilteredMenuReapplyFilter(NC::Menu<T> &m, F action)
|
|
||||||
{
|
|
||||||
auto cleanup = [&]() {
|
|
||||||
if (m.getFilter())
|
|
||||||
{
|
|
||||||
m.applyCurrentFilter(m.begin(), m.end());
|
|
||||||
if (m.empty())
|
|
||||||
{
|
|
||||||
m.clearFilter();
|
|
||||||
m.clearFilterResults();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
m.showAll();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
action();
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
cleanup();
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
void moveSelectedItemsUp(NC::Menu<MPD::Song> &m, F swap_fun)
|
void moveSelectedItemsUp(NC::Menu<MPD::Song> &m, F swap_fun)
|
||||||
{
|
{
|
||||||
@@ -264,53 +216,52 @@ void moveSelectedItemsDown(NC::Menu<MPD::Song> &m, F swap_fun)
|
|||||||
template <typename F>
|
template <typename F>
|
||||||
void moveSelectedItemsTo(NC::Menu<MPD::Song> &m, F move_fun)
|
void moveSelectedItemsTo(NC::Menu<MPD::Song> &m, F move_fun)
|
||||||
{
|
{
|
||||||
|
// FIXME: make it not look like shit
|
||||||
auto cur_ptr = &m.current()->value();
|
auto cur_ptr = &m.current()->value();
|
||||||
withUnfilteredMenu(m, [&]() {
|
// this is kinda shitty, but there is no other way to know
|
||||||
// this is kinda shitty, but there is no other way to know
|
// what position current item has in unfiltered menu.
|
||||||
// what position current item has in unfiltered menu.
|
ptrdiff_t pos = 0;
|
||||||
ptrdiff_t pos = 0;
|
for (auto it = m.begin(); it != m.end(); ++it, ++pos)
|
||||||
for (auto it = m.begin(); it != m.end(); ++it, ++pos)
|
if (&it->value() == cur_ptr)
|
||||||
if (&it->value() == cur_ptr)
|
break;
|
||||||
break;
|
auto begin = m.begin();
|
||||||
auto begin = m.begin();
|
auto list = getSelected(m.begin(), m.end());
|
||||||
auto list = getSelected(m.begin(), m.end());
|
// we move only truly selected items
|
||||||
// we move only truly selected items
|
if (list.empty())
|
||||||
if (list.empty())
|
return;
|
||||||
return;
|
// we can't move to the middle of selected items
|
||||||
// we can't move to the middle of selected items
|
//(this also handles case when list.size() == 1)
|
||||||
//(this also handles case when list.size() == 1)
|
if (pos >= (list.front() - begin) && pos <= (list.back() - begin))
|
||||||
if (pos >= (list.front() - begin) && pos <= (list.back() - begin))
|
return;
|
||||||
return;
|
int diff = pos - (list.front() - begin);
|
||||||
int diff = pos - (list.front() - begin);
|
Mpd.StartCommandsList();
|
||||||
Mpd.StartCommandsList();
|
if (diff > 0) // move down
|
||||||
if (diff > 0) // move down
|
{
|
||||||
|
pos -= list.size();
|
||||||
|
size_t i = list.size()-1;
|
||||||
|
for (auto it = list.rbegin(); it != list.rend(); ++it, --i)
|
||||||
|
move_fun(&Mpd, *it - begin, pos+i);
|
||||||
|
Mpd.CommitCommandsList();
|
||||||
|
i = list.size()-1;
|
||||||
|
for (auto it = list.rbegin(); it != list.rend(); ++it, --i)
|
||||||
{
|
{
|
||||||
pos -= list.size();
|
(*it)->setSelected(false);
|
||||||
size_t i = list.size()-1;
|
m[pos+i].setSelected(true);
|
||||||
for (auto it = list.rbegin(); it != list.rend(); ++it, --i)
|
|
||||||
move_fun(&Mpd, *it - begin, pos+i);
|
|
||||||
Mpd.CommitCommandsList();
|
|
||||||
i = list.size()-1;
|
|
||||||
for (auto it = list.rbegin(); it != list.rend(); ++it, --i)
|
|
||||||
{
|
|
||||||
(*it)->setSelected(false);
|
|
||||||
m[pos+i].setSelected(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (diff < 0) // move up
|
}
|
||||||
|
else if (diff < 0) // move up
|
||||||
|
{
|
||||||
|
size_t i = 0;
|
||||||
|
for (auto it = list.begin(); it != list.end(); ++it, ++i)
|
||||||
|
move_fun(&Mpd, *it - begin, pos+i);
|
||||||
|
Mpd.CommitCommandsList();
|
||||||
|
i = 0;
|
||||||
|
for (auto it = list.begin(); it != list.end(); ++it, ++i)
|
||||||
{
|
{
|
||||||
size_t i = 0;
|
(*it)->setSelected(false);
|
||||||
for (auto it = list.begin(); it != list.end(); ++it, ++i)
|
m[pos+i].setSelected(true);
|
||||||
move_fun(&Mpd, *it - begin, pos+i);
|
|
||||||
Mpd.CommitCommandsList();
|
|
||||||
i = 0;
|
|
||||||
for (auto it = list.begin(); it != list.end(); ++it, ++i)
|
|
||||||
{
|
|
||||||
(*it)->setSelected(false);
|
|
||||||
m[pos+i].setSelected(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
@@ -322,28 +273,14 @@ void deleteSelectedSongs(NC::Menu<MPD::Song> &m, F delete_fun)
|
|||||||
// ignore all songs that are not filtered. we use the fact
|
// ignore all songs that are not filtered. we use the fact
|
||||||
// that both ranges share the same values, ie. we can compare
|
// that both ranges share the same values, ie. we can compare
|
||||||
// pointers to check whether an item belongs to filtered range.
|
// pointers to check whether an item belongs to filtered range.
|
||||||
NC::Menu<MPD::Song>::Iterator begin;
|
auto begin = ++m.begin();
|
||||||
NC::Menu<MPD::Song>::ReverseIterator real_begin, real_end;
|
|
||||||
withUnfilteredMenu(m, [&]() {
|
|
||||||
// obtain iterators for unfiltered range
|
|
||||||
begin = m.begin() + 1; // cancel reverse iterator's offset
|
|
||||||
real_begin = m.rbegin();
|
|
||||||
real_end = m.rend();
|
|
||||||
});
|
|
||||||
// get iterator to filtered range
|
|
||||||
auto cur_filtered = m.rbegin();
|
|
||||||
Mpd.StartCommandsList();
|
Mpd.StartCommandsList();
|
||||||
for (auto it = real_begin; it != real_end; ++it)
|
for (auto it = m.rbegin(); it != m.rend(); ++it)
|
||||||
{
|
{
|
||||||
// current iterator belongs to filtered range, proceed
|
if (it->isSelected())
|
||||||
if (&*it == &*cur_filtered)
|
|
||||||
{
|
{
|
||||||
if (it->isSelected())
|
it->setSelected(false);
|
||||||
{
|
delete_fun(Mpd, it.base() - begin);
|
||||||
it->setSelected(false);
|
|
||||||
delete_fun(Mpd, it.base() - begin);
|
|
||||||
}
|
|
||||||
++cur_filtered;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Mpd.CommitCommandsList();
|
Mpd.CommitCommandsList();
|
||||||
@@ -356,19 +293,6 @@ void cropPlaylist(NC::Menu<MPD::Song> &m, F delete_fun)
|
|||||||
deleteSelectedSongs(m, delete_fun);
|
deleteSelectedSongs(m, delete_fun);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename F, typename G>
|
|
||||||
void clearPlaylist(NC::Menu<MPD::Song> &m, F delete_fun, G clear_fun)
|
|
||||||
{
|
|
||||||
if (m.isFiltered())
|
|
||||||
{
|
|
||||||
for (auto it = m.begin(); it != m.end(); ++it)
|
|
||||||
it->setSelected(true);
|
|
||||||
deleteSelectedSongs(m, delete_fun);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
clear_fun(Mpd);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename ItemT>
|
template <typename ItemT>
|
||||||
std::function<void (ItemT)> vectorMoveInserter(std::vector<ItemT> &v)
|
std::function<void (ItemT)> vectorMoveInserter(std::vector<ItemT> &v)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -27,13 +27,6 @@
|
|||||||
#include "song.h"
|
#include "song.h"
|
||||||
#include "proxy_song_list.h"
|
#include "proxy_song_list.h"
|
||||||
|
|
||||||
struct Filterable
|
|
||||||
{
|
|
||||||
virtual bool allowsFiltering() = 0;
|
|
||||||
virtual std::string currentFilter() = 0;
|
|
||||||
virtual void applyFilter(const std::string &filter) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Searchable
|
struct Searchable
|
||||||
{
|
{
|
||||||
virtual bool allowsSearching() = 0;
|
virtual bool allowsSearching() = 0;
|
||||||
|
|||||||
@@ -253,49 +253,48 @@ void MediaLibrary::update()
|
|||||||
{
|
{
|
||||||
if (hasTwoColumns)
|
if (hasTwoColumns)
|
||||||
{
|
{
|
||||||
if (Albums.reallyEmpty() || m_albums_update_request)
|
if (Albums.empty() || m_albums_update_request)
|
||||||
{
|
{
|
||||||
m_albums_update_request = false;
|
m_albums_update_request = false;
|
||||||
std::map<std::tuple<std::string, std::string, std::string>, time_t> albums;
|
std::map<std::tuple<std::string, std::string, std::string>, time_t> albums;
|
||||||
for (MPD::SongIterator s = Mpd.GetDirectoryRecursive("/"), end; s != end; ++s)
|
for (MPD::SongIterator s = Mpd.GetDirectoryRecursive("/"), end; s != end; ++s)
|
||||||
{
|
{
|
||||||
|
std::string tag;
|
||||||
unsigned idx = 0;
|
unsigned idx = 0;
|
||||||
std::string tag = s->get(Config.media_lib_primary_tag, idx);
|
while (!(tag = s->get(Config.media_lib_primary_tag, idx++)).empty())
|
||||||
do
|
|
||||||
{
|
{
|
||||||
auto key = std::make_tuple(tag, s->getAlbum(), s->getDate());
|
auto key = std::make_tuple(std::move(tag), s->getAlbum(), s->getDate());
|
||||||
auto it = albums.find(key);
|
auto it = albums.find(key);
|
||||||
if (it == albums.end())
|
if (it == albums.end())
|
||||||
albums[key] = s->getMTime();
|
albums[std::move(key)] = s->getMTime();
|
||||||
else
|
else
|
||||||
it->second = s->getMTime();
|
it->second = s->getMTime();
|
||||||
}
|
}
|
||||||
while (!(tag = s->get(Config.media_lib_primary_tag, ++idx)).empty());
|
|
||||||
}
|
}
|
||||||
withUnfilteredMenuReapplyFilter(Albums, [this, &albums]() {
|
size_t idx = 0;
|
||||||
size_t idx = 0;
|
for (const auto &album : albums)
|
||||||
for (auto it = albums.begin(); it != albums.end(); ++it, ++idx)
|
{
|
||||||
{
|
auto entry = AlbumEntry(Album(
|
||||||
auto &&entry = AlbumEntry(Album(
|
std::move(std::get<0>(album.first)),
|
||||||
std::move(std::get<0>(it->first)),
|
std::move(std::get<1>(album.first)),
|
||||||
std::move(std::get<1>(it->first)),
|
std::move(std::get<2>(album.first)),
|
||||||
std::move(std::get<2>(it->first)),
|
album.second)
|
||||||
it->second));
|
);
|
||||||
if (idx < Albums.size())
|
|
||||||
Albums[idx].value() = entry;
|
|
||||||
else
|
|
||||||
Albums.addItem(entry);
|
|
||||||
}
|
|
||||||
if (idx < Albums.size())
|
if (idx < Albums.size())
|
||||||
Albums.resizeList(idx);
|
Albums[idx].value() = std::move(entry);
|
||||||
std::sort(Albums.beginV(), Albums.endV(), SortAlbumEntries());
|
else
|
||||||
});
|
Albums.addItem(std::move(entry));
|
||||||
|
++idx;
|
||||||
|
}
|
||||||
|
if (idx < Albums.size())
|
||||||
|
Albums.resizeList(idx);
|
||||||
|
std::sort(Albums.beginV(), Albums.endV(), SortAlbumEntries());
|
||||||
Albums.refresh();
|
Albums.refresh();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (Tags.reallyEmpty() || m_tags_update_request)
|
if (Tags.empty() || m_tags_update_request)
|
||||||
{
|
{
|
||||||
m_tags_update_request = false;
|
m_tags_update_request = false;
|
||||||
std::map<std::string, time_t> tags;
|
std::map<std::string, time_t> tags;
|
||||||
@@ -303,17 +302,16 @@ void MediaLibrary::update()
|
|||||||
{
|
{
|
||||||
for (MPD::SongIterator s = Mpd.GetDirectoryRecursive("/"), end; s != end; ++s)
|
for (MPD::SongIterator s = Mpd.GetDirectoryRecursive("/"), end; s != end; ++s)
|
||||||
{
|
{
|
||||||
|
std::string tag;
|
||||||
unsigned idx = 0;
|
unsigned idx = 0;
|
||||||
std::string tag = s->get(Config.media_lib_primary_tag, idx);
|
while (!(tag = s->get(Config.media_lib_primary_tag, idx++)).empty())
|
||||||
do
|
|
||||||
{
|
{
|
||||||
auto it = tags.find(tag);
|
auto it = tags.find(tag);
|
||||||
if (it == tags.end())
|
if (it == tags.end())
|
||||||
tags[tag] = s->getMTime();
|
tags[std::move(tag)] = s->getMTime();
|
||||||
else
|
else
|
||||||
it->second = std::max(it->second, s->getMTime());
|
it->second = std::max(it->second, s->getMTime());
|
||||||
}
|
}
|
||||||
while (!(tag = s->get(Config.media_lib_primary_tag, ++idx)).empty());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -322,25 +320,24 @@ void MediaLibrary::update()
|
|||||||
for (; tag != end; ++tag)
|
for (; tag != end; ++tag)
|
||||||
tags[std::move(*tag)] = 0;
|
tags[std::move(*tag)] = 0;
|
||||||
}
|
}
|
||||||
withUnfilteredMenuReapplyFilter(Tags, [this, &tags]() {
|
size_t idx = 0;
|
||||||
size_t idx = 0;
|
for (const auto &tag : tags)
|
||||||
for (auto it = tags.begin(); it != tags.end(); ++it, ++idx)
|
{
|
||||||
{
|
auto ptag = PrimaryTag(std::move(tag.first), tag.second);
|
||||||
auto &&tag = PrimaryTag(std::move(it->first), it->second);
|
|
||||||
if (idx < Tags.size())
|
|
||||||
Tags[idx].value() = tag;
|
|
||||||
else
|
|
||||||
Tags.addItem(tag);
|
|
||||||
}
|
|
||||||
if (idx < Tags.size())
|
if (idx < Tags.size())
|
||||||
Tags.resizeList(idx);
|
Tags[idx].value() = std::move(ptag);
|
||||||
std::sort(Tags.beginV(), Tags.endV(), SortPrimaryTags());
|
else
|
||||||
});
|
Tags.addItem(std::move(ptag));
|
||||||
|
++idx;
|
||||||
|
}
|
||||||
|
if (idx < Tags.size())
|
||||||
|
Tags.resizeList(idx);
|
||||||
|
std::sort(Tags.beginV(), Tags.endV(), SortPrimaryTags());
|
||||||
Tags.refresh();
|
Tags.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Tags.empty()
|
if (!Tags.empty()
|
||||||
&& ((Albums.reallyEmpty() && Global::Timer - m_timer > m_fetching_delay) || m_albums_update_request)
|
&& ((Albums.empty() && Global::Timer - m_timer > m_fetching_delay) || m_albums_update_request)
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
m_albums_update_request = false;
|
m_albums_update_request = false;
|
||||||
@@ -353,42 +350,42 @@ void MediaLibrary::update()
|
|||||||
auto key = std::make_tuple(s->getAlbum(), s->getDate());
|
auto key = std::make_tuple(s->getAlbum(), s->getDate());
|
||||||
auto it = albums.find(key);
|
auto it = albums.find(key);
|
||||||
if (it == albums.end())
|
if (it == albums.end())
|
||||||
albums[key] = s->getMTime();
|
albums[std::move(key)] = s->getMTime();
|
||||||
else
|
else
|
||||||
it->second = s->getMTime();
|
it->second = std::max(it->second, s->getMTime());
|
||||||
};
|
};
|
||||||
withUnfilteredMenuReapplyFilter(Albums, [this, &albums, &primary_tag]() {
|
size_t idx = 0;
|
||||||
size_t idx = 0;
|
for (const auto &album : albums)
|
||||||
for (auto it = albums.begin(); it != albums.end(); ++it, ++idx)
|
{
|
||||||
{
|
auto entry = AlbumEntry(Album(
|
||||||
auto &&entry = AlbumEntry(Album(
|
primary_tag,
|
||||||
primary_tag,
|
std::move(std::get<0>(album.first)),
|
||||||
std::move(std::get<0>(it->first)),
|
std::move(std::get<1>(album.first)),
|
||||||
std::move(std::get<1>(it->first)),
|
album.second)
|
||||||
it->second));
|
);
|
||||||
if (idx < Albums.size())
|
|
||||||
{
|
|
||||||
Albums[idx].value() = entry;
|
|
||||||
Albums[idx].setSeparator(false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
Albums.addItem(entry);
|
|
||||||
}
|
|
||||||
if (idx < Albums.size())
|
if (idx < Albums.size())
|
||||||
Albums.resizeList(idx);
|
|
||||||
std::sort(Albums.beginV(), Albums.endV(), SortAlbumEntries());
|
|
||||||
if (albums.size() > 1)
|
|
||||||
{
|
{
|
||||||
Albums.addSeparator();
|
Albums[idx].value() = std::move(entry);
|
||||||
Albums.addItem(AlbumEntry::mkAllTracksEntry(primary_tag));
|
Albums[idx].setSeparator(false);
|
||||||
}
|
}
|
||||||
});
|
else
|
||||||
|
Albums.addItem(std::move(entry));
|
||||||
|
++idx;
|
||||||
|
}
|
||||||
|
if (idx < Albums.size())
|
||||||
|
Albums.resizeList(idx);
|
||||||
|
std::sort(Albums.beginV(), Albums.endV(), SortAlbumEntries());
|
||||||
|
if (albums.size() > 1)
|
||||||
|
{
|
||||||
|
Albums.addSeparator();
|
||||||
|
Albums.addItem(AlbumEntry::mkAllTracksEntry(primary_tag));
|
||||||
|
}
|
||||||
Albums.refresh();
|
Albums.refresh();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Albums.empty()
|
if (!Albums.empty()
|
||||||
&& ((Songs.reallyEmpty() && Global::Timer - m_timer > m_fetching_delay) || m_songs_update_request)
|
&& ((Songs.empty() && Global::Timer - m_timer > m_fetching_delay) || m_songs_update_request)
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
m_songs_update_request = false;
|
m_songs_update_request = false;
|
||||||
@@ -400,30 +397,28 @@ void MediaLibrary::update()
|
|||||||
Mpd.AddSearch(MPD_TAG_ALBUM, album.entry().album());
|
Mpd.AddSearch(MPD_TAG_ALBUM, album.entry().album());
|
||||||
Mpd.AddSearch(MPD_TAG_DATE, album.entry().date());
|
Mpd.AddSearch(MPD_TAG_DATE, album.entry().date());
|
||||||
}
|
}
|
||||||
withUnfilteredMenuReapplyFilter(Songs, [this, &album]() {
|
size_t idx = 0;
|
||||||
size_t idx = 0;
|
for (MPD::SongIterator s = Mpd.CommitSearchSongs(), end; s != end; ++s, ++idx)
|
||||||
for (MPD::SongIterator s = Mpd.CommitSearchSongs(), end; s != end; ++s, ++idx)
|
{
|
||||||
{
|
bool is_playlist = myPlaylist->checkForSong(*s);
|
||||||
bool is_playlist = myPlaylist->checkForSong(*s);
|
|
||||||
if (idx < Songs.size())
|
|
||||||
{
|
|
||||||
Songs[idx].value() = std::move(*s);
|
|
||||||
Songs[idx].setBold(is_playlist);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
Songs.addItem(std::move(*s), is_playlist);
|
|
||||||
};
|
|
||||||
if (idx < Songs.size())
|
if (idx < Songs.size())
|
||||||
Songs.resizeList(idx);
|
{
|
||||||
std::sort(Songs.begin(), Songs.end(), SortSongs(!album.isAllTracksEntry()));
|
Songs[idx].value() = std::move(*s);
|
||||||
});
|
Songs[idx].setBold(is_playlist);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Songs.addItem(std::move(*s), is_playlist);
|
||||||
|
};
|
||||||
|
if (idx < Songs.size())
|
||||||
|
Songs.resizeList(idx);
|
||||||
|
std::sort(Songs.begin(), Songs.end(), SortSongs(!album.isAllTracksEntry()));
|
||||||
Songs.refresh();
|
Songs.refresh();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int MediaLibrary::windowTimeout()
|
int MediaLibrary::windowTimeout()
|
||||||
{
|
{
|
||||||
if (Albums.reallyEmpty() || Songs.reallyEmpty())
|
if (Albums.empty() || Songs.empty())
|
||||||
return m_window_timeout;
|
return m_window_timeout;
|
||||||
else
|
else
|
||||||
return Screen<WindowType>::windowTimeout();
|
return Screen<WindowType>::windowTimeout();
|
||||||
@@ -562,74 +557,6 @@ void MediaLibrary::mouseButtonPressed(MEVENT me)
|
|||||||
|
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
|
|
||||||
bool MediaLibrary::allowsFiltering()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string MediaLibrary::currentFilter()
|
|
||||||
{
|
|
||||||
std::string filter;
|
|
||||||
if (isActiveWindow(Tags))
|
|
||||||
filter = RegexFilter<PrimaryTag>::currentFilter(Tags);
|
|
||||||
else if (isActiveWindow(Albums))
|
|
||||||
filter = RegexItemFilter<AlbumEntry>::currentFilter(Albums);
|
|
||||||
else if (isActiveWindow(Songs))
|
|
||||||
filter = RegexFilter<MPD::Song>::currentFilter(Songs);
|
|
||||||
return filter;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MediaLibrary::applyFilter(const std::string &filter)
|
|
||||||
{
|
|
||||||
if (filter.empty())
|
|
||||||
{
|
|
||||||
if (isActiveWindow(Tags))
|
|
||||||
{
|
|
||||||
Tags.clearFilter();
|
|
||||||
Tags.clearFilterResults();
|
|
||||||
}
|
|
||||||
else if (isActiveWindow(Albums))
|
|
||||||
{
|
|
||||||
Albums.clearFilter();
|
|
||||||
Albums.clearFilterResults();
|
|
||||||
}
|
|
||||||
else if (isActiveWindow(Songs))
|
|
||||||
{
|
|
||||||
Songs.clearFilter();
|
|
||||||
Songs.clearFilterResults();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (isActiveWindow(Tags))
|
|
||||||
{
|
|
||||||
Tags.showAll();
|
|
||||||
auto rx = RegexFilter<PrimaryTag>(
|
|
||||||
boost::regex(filter, Config.regex_type), TagEntryMatcher);
|
|
||||||
Tags.filter(Tags.begin(), Tags.end(), rx);
|
|
||||||
}
|
|
||||||
else if (isActiveWindow(Albums))
|
|
||||||
{
|
|
||||||
Albums.showAll();
|
|
||||||
auto fun = boost::bind(AlbumEntryMatcher, _1, _2, true);
|
|
||||||
auto rx = RegexItemFilter<AlbumEntry>(
|
|
||||||
boost::regex(filter, Config.regex_type), fun);
|
|
||||||
Albums.filter(Albums.begin(), Albums.end(), rx);
|
|
||||||
}
|
|
||||||
else if (isActiveWindow(Songs))
|
|
||||||
{
|
|
||||||
Songs.showAll();
|
|
||||||
auto rx = RegexFilter<MPD::Song>(
|
|
||||||
boost::regex(filter, Config.regex_type), SongEntryMatcher);
|
|
||||||
Songs.filter(Songs.begin(), Songs.end(), rx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (boost::bad_expression &) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
/***********************************************************************/
|
|
||||||
|
|
||||||
bool MediaLibrary::allowsSearching()
|
bool MediaLibrary::allowsSearching()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
@@ -780,17 +707,13 @@ std::vector<MPD::Song> MediaLibrary::getSelectedSongs()
|
|||||||
}
|
}
|
||||||
// if no item is selected, add songs from right column
|
// if no item is selected, add songs from right column
|
||||||
if (!any_selected && !Albums.empty())
|
if (!any_selected && !Albums.empty())
|
||||||
{
|
result.insert(result.end(), Songs.beginV(), Songs.endV());
|
||||||
withUnfilteredMenu(Songs, [this, &result]() {
|
|
||||||
result.insert(result.end(), Songs.beginV(), Songs.endV());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (isActiveWindow(Songs))
|
else if (isActiveWindow(Songs))
|
||||||
{
|
{
|
||||||
for (auto it = Songs.begin(); it != Songs.end(); ++it)
|
for (const auto &s : Songs)
|
||||||
if (it->isSelected())
|
if (s.isSelected())
|
||||||
result.push_back(it->value());
|
result.push_back(s.value());
|
||||||
// if no item is selected, add current one
|
// if no item is selected, add current one
|
||||||
if (result.empty() && !Songs.empty())
|
if (result.empty() && !Songs.empty())
|
||||||
result.push_back(Songs.current()->value());
|
result.push_back(Songs.current()->value());
|
||||||
@@ -805,12 +728,12 @@ bool MediaLibrary::previousColumnAvailable()
|
|||||||
assert(!hasTwoColumns || !isActiveWindow(Tags));
|
assert(!hasTwoColumns || !isActiveWindow(Tags));
|
||||||
if (isActiveWindow(Songs))
|
if (isActiveWindow(Songs))
|
||||||
{
|
{
|
||||||
if (!Albums.reallyEmpty() && (hasTwoColumns || !Tags.reallyEmpty()))
|
if (!Albums.empty() && (hasTwoColumns || !Tags.empty()))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (isActiveWindow(Albums))
|
else if (isActiveWindow(Albums))
|
||||||
{
|
{
|
||||||
if (!hasTwoColumns && !Tags.reallyEmpty())
|
if (!hasTwoColumns && !Tags.empty())
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -839,12 +762,12 @@ bool MediaLibrary::nextColumnAvailable()
|
|||||||
assert(!hasTwoColumns || !isActiveWindow(Tags));
|
assert(!hasTwoColumns || !isActiveWindow(Tags));
|
||||||
if (isActiveWindow(Tags))
|
if (isActiveWindow(Tags))
|
||||||
{
|
{
|
||||||
if (!Albums.reallyEmpty() && !Songs.reallyEmpty())
|
if (!Albums.empty() && !Songs.empty())
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (isActiveWindow(Albums))
|
else if (isActiveWindow(Albums))
|
||||||
{
|
{
|
||||||
if (!Songs.reallyEmpty())
|
if (!Songs.empty())
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -921,9 +844,7 @@ void MediaLibrary::toggleSortMode()
|
|||||||
Config.media_library_sort_by_mtime ? "modification time" : "name");
|
Config.media_library_sort_by_mtime ? "modification time" : "name");
|
||||||
if (hasTwoColumns)
|
if (hasTwoColumns)
|
||||||
{
|
{
|
||||||
withUnfilteredMenuReapplyFilter(Albums, [this]() {
|
std::sort(Albums.beginV(), Albums.endV(), SortAlbumEntries());
|
||||||
std::sort(Albums.beginV(), Albums.endV(), SortAlbumEntries());
|
|
||||||
});
|
|
||||||
Albums.refresh();
|
Albums.refresh();
|
||||||
Songs.clear();
|
Songs.clear();
|
||||||
if (Config.titles_visibility)
|
if (Config.titles_visibility)
|
||||||
@@ -936,17 +857,14 @@ void MediaLibrary::toggleSortMode()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
withUnfilteredMenuReapplyFilter(Tags, [this]() {
|
// if we already have modification times, just resort. otherwise refetch the list.
|
||||||
// if we already have modification times,
|
if (!Tags.empty() && Tags[0].value().mtime() > 0)
|
||||||
// just resort. otherwise refetch the list.
|
{
|
||||||
if (!Tags.empty() && Tags[0].value().mtime() > 0)
|
std::sort(Tags.beginV(), Tags.endV(), SortPrimaryTags());
|
||||||
{
|
Tags.refresh();
|
||||||
std::sort(Tags.beginV(), Tags.endV(), SortPrimaryTags());
|
}
|
||||||
Tags.refresh();
|
else
|
||||||
}
|
Tags.clear();
|
||||||
else
|
|
||||||
Tags.clear();
|
|
||||||
});
|
|
||||||
Albums.clear();
|
Albums.clear();
|
||||||
Songs.clear();
|
Songs.clear();
|
||||||
}
|
}
|
||||||
@@ -973,10 +891,9 @@ void MediaLibrary::LocateSong(const MPD::Song &s)
|
|||||||
switchTo();
|
switchTo();
|
||||||
Statusbar::put() << "Jumping to song...";
|
Statusbar::put() << "Jumping to song...";
|
||||||
Global::wFooter->refresh();
|
Global::wFooter->refresh();
|
||||||
|
// FIXME: use std::find
|
||||||
if (!hasTwoColumns)
|
if (!hasTwoColumns)
|
||||||
{
|
{
|
||||||
Tags.showAll();
|
|
||||||
if (Tags.empty())
|
if (Tags.empty())
|
||||||
update();
|
update();
|
||||||
if (primary_tag != Tags.current()->value().tag())
|
if (primary_tag != Tags.current()->value().tag())
|
||||||
@@ -994,7 +911,6 @@ void MediaLibrary::LocateSong(const MPD::Song &s)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Albums.showAll();
|
|
||||||
if (Albums.empty())
|
if (Albums.empty())
|
||||||
update();
|
update();
|
||||||
|
|
||||||
@@ -1017,7 +933,6 @@ void MediaLibrary::LocateSong(const MPD::Song &s)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Songs.showAll();
|
|
||||||
if (Songs.empty())
|
if (Songs.empty())
|
||||||
update();
|
update();
|
||||||
|
|
||||||
@@ -1064,10 +979,7 @@ void MediaLibrary::AddToPlaylist(bool add_n_play)
|
|||||||
}
|
}
|
||||||
else if (isActiveWindow(Albums))
|
else if (isActiveWindow(Albums))
|
||||||
{
|
{
|
||||||
bool success;
|
bool success = addSongsToPlaylist(Songs.beginV(), Songs.endV(), add_n_play, -1);
|
||||||
withUnfilteredMenu(Songs, [&]() {
|
|
||||||
success = addSongsToPlaylist(Songs.beginV(), Songs.endV(), add_n_play, -1);
|
|
||||||
});
|
|
||||||
Statusbar::printf("Songs from album \"%1%\" added%2%",
|
Statusbar::printf("Songs from album \"%1%\" added%2%",
|
||||||
Albums.current()->value().entry().album(), withErrors(success)
|
Albums.current()->value().entry().album(), withErrors(success)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
#include "regex_filter.h"
|
#include "regex_filter.h"
|
||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
|
|
||||||
struct MediaLibrary: Screen<NC::Window *>, Filterable, HasColumns, HasSongs, Searchable, Tabbable
|
struct MediaLibrary: Screen<NC::Window *>, HasColumns, HasSongs, Searchable, Tabbable
|
||||||
{
|
{
|
||||||
MediaLibrary();
|
MediaLibrary();
|
||||||
|
|
||||||
@@ -48,11 +48,6 @@ struct MediaLibrary: Screen<NC::Window *>, Filterable, HasColumns, HasSongs, Sea
|
|||||||
|
|
||||||
virtual bool isMergable() OVERRIDE { return true; }
|
virtual bool isMergable() OVERRIDE { return true; }
|
||||||
|
|
||||||
// Filterable implementation
|
|
||||||
virtual bool allowsFiltering() OVERRIDE;
|
|
||||||
virtual std::string currentFilter() OVERRIDE;
|
|
||||||
virtual void applyFilter(const std::string &filter) OVERRIDE;
|
|
||||||
|
|
||||||
// Searchable implementation
|
// Searchable implementation
|
||||||
virtual bool allowsSearching() OVERRIDE;
|
virtual bool allowsSearching() OVERRIDE;
|
||||||
virtual bool setSearchConstraint(const std::string &constraint) OVERRIDE;
|
virtual bool setSearchConstraint(const std::string &constraint) OVERRIDE;
|
||||||
|
|||||||
187
src/menu.h
187
src/menu.h
@@ -130,13 +130,13 @@ public:
|
|||||||
|
|
||||||
/// Resizes the list to given size (adequate to std::vector::resize())
|
/// Resizes the list to given size (adequate to std::vector::resize())
|
||||||
/// @param size requested size
|
/// @param size requested size
|
||||||
void resizeList(size_t size);
|
void resizeList(size_t new_size);
|
||||||
|
|
||||||
/// Adds new option to list
|
/// Adds new option to list
|
||||||
/// @param item object that has to be added
|
/// @param item object that has to be added
|
||||||
/// @param is_bold defines the initial state of bold attribute
|
/// @param is_bold defines the initial state of bold attribute
|
||||||
/// @param is_inactive defines the initial state of static attribute
|
/// @param is_inactive defines the initial state of static attribute
|
||||||
void addItem(ItemT item, bool is_bold = 0, bool is_inactive = 0);
|
void addItem(ItemT item, bool is_bold = false, bool is_inactive = false);
|
||||||
|
|
||||||
/// Adds separator to list
|
/// Adds separator to list
|
||||||
void addSeparator();
|
void addSeparator();
|
||||||
@@ -146,7 +146,7 @@ public:
|
|||||||
/// @param item object that has to be inserted
|
/// @param item object that has to be inserted
|
||||||
/// @param is_bold defines the initial state of bold attribute
|
/// @param is_bold defines the initial state of bold attribute
|
||||||
/// @param is_inactive defines the initial state of static attribute
|
/// @param is_inactive defines the initial state of static attribute
|
||||||
void insertItem(size_t pos, const ItemT &Item, bool is_bold = 0, bool is_inactive = 0);
|
void insertItem(size_t pos, const ItemT &Item, bool is_bold = false, bool is_inactive = false);
|
||||||
|
|
||||||
/// Inserts separator to list at given position
|
/// Inserts separator to list at given position
|
||||||
/// @param pos initial position of inserted separator
|
/// @param pos initial position of inserted separator
|
||||||
@@ -168,27 +168,6 @@ public:
|
|||||||
/// @return currently highlighted position
|
/// @return currently highlighted position
|
||||||
size_t choice() const;
|
size_t choice() const;
|
||||||
|
|
||||||
void filter(ConstIterator first, ConstIterator last, const FilterFunction &f);
|
|
||||||
|
|
||||||
void applyCurrentFilter(ConstIterator first, ConstIterator last);
|
|
||||||
|
|
||||||
/// Clears filter results
|
|
||||||
void clearFilterResults();
|
|
||||||
|
|
||||||
void clearFilter();
|
|
||||||
|
|
||||||
/// @return const reference to currently used filter function
|
|
||||||
const FilterFunction &getFilter() { return m_filter; }
|
|
||||||
|
|
||||||
/// @return true if list is currently filtered, false otherwise
|
|
||||||
bool isFiltered() { return m_options_ptr == &m_filtered_options; }
|
|
||||||
|
|
||||||
/// Turns off filtering
|
|
||||||
void showAll() { m_options_ptr = &m_options; }
|
|
||||||
|
|
||||||
/// Turns on filtering
|
|
||||||
void showFiltered() { m_options_ptr = &m_filtered_options; }
|
|
||||||
|
|
||||||
/// Refreshes the menu window
|
/// Refreshes the menu window
|
||||||
/// @see Window::refresh()
|
/// @see Window::refresh()
|
||||||
virtual void refresh() OVERRIDE;
|
virtual void refresh() OVERRIDE;
|
||||||
@@ -236,17 +215,10 @@ public:
|
|||||||
|
|
||||||
/// Checks if list is empty
|
/// Checks if list is empty
|
||||||
/// @return true if list is empty, false otherwise
|
/// @return true if list is empty, false otherwise
|
||||||
/// @see reallyEmpty()
|
bool empty() const { return m_items.empty(); }
|
||||||
bool empty() const { return m_options_ptr->empty(); }
|
|
||||||
|
|
||||||
/// Checks if list is really empty since Empty() may not
|
|
||||||
/// be accurate if filter is set)
|
|
||||||
/// @return true if list is empty, false otherwise
|
|
||||||
/// @see Empty()
|
|
||||||
bool reallyEmpty() const { return m_options.empty(); }
|
|
||||||
|
|
||||||
/// @return size of the list
|
/// @return size of the list
|
||||||
size_t size() const { return m_options_ptr->size(); }
|
size_t size() const { return m_items.size(); }
|
||||||
|
|
||||||
/// @return currently drawn item. The result is defined only within
|
/// @return currently drawn item. The result is defined only within
|
||||||
/// drawing function that is called by refresh()
|
/// drawing function that is called by refresh()
|
||||||
@@ -256,35 +228,35 @@ public:
|
|||||||
/// @param pos requested position
|
/// @param pos requested position
|
||||||
/// @return reference to item at given position
|
/// @return reference to item at given position
|
||||||
/// @throw std::out_of_range if given position is out of range
|
/// @throw std::out_of_range if given position is out of range
|
||||||
Menu<ItemT>::Item &at(size_t pos) { return *m_options_ptr->at(pos); }
|
Menu<ItemT>::Item &at(size_t pos) { return *m_items.at(pos); }
|
||||||
|
|
||||||
/// @param pos requested position
|
/// @param pos requested position
|
||||||
/// @return const reference to item at given position
|
/// @return const reference to item at given position
|
||||||
/// @throw std::out_of_range if given position is out of range
|
/// @throw std::out_of_range if given position is out of range
|
||||||
const Menu<ItemT>::Item &at(size_t pos) const { return *m_options_ptr->at(pos); }
|
const Menu<ItemT>::Item &at(size_t pos) const { return *m_items.at(pos); }
|
||||||
|
|
||||||
/// @param pos requested position
|
/// @param pos requested position
|
||||||
/// @return const reference to item at given position
|
/// @return const reference to item at given position
|
||||||
const Menu<ItemT>::Item &operator[](size_t pos) const { return *(*m_options_ptr)[pos]; }
|
const Menu<ItemT>::Item &operator[](size_t pos) const { return *m_items[pos]; }
|
||||||
|
|
||||||
/// @param pos requested position
|
/// @param pos requested position
|
||||||
/// @return const reference to item at given position
|
/// @return const reference to item at given position
|
||||||
Menu<ItemT>::Item &operator[](size_t pos) { return *(*m_options_ptr)[pos]; }
|
Menu<ItemT>::Item &operator[](size_t pos) { return *m_items[pos]; }
|
||||||
|
|
||||||
Iterator current() { return Iterator(m_options_ptr->begin() + m_highlight); }
|
Iterator current() { return Iterator(m_items.begin() + m_highlight); }
|
||||||
ConstIterator current() const { return ConstIterator(m_options_ptr->begin() + m_highlight); }
|
ConstIterator current() const { return ConstIterator(m_items.begin() + m_highlight); }
|
||||||
ReverseIterator rcurrent() { return ReverseIterator(++current()); }
|
ReverseIterator rcurrent() { return ReverseIterator(++current()); }
|
||||||
ConstReverseIterator rcurrent() const { return ReverseIterator(++current()); }
|
ConstReverseIterator rcurrent() const { return ReverseIterator(++current()); }
|
||||||
|
|
||||||
ValueIterator currentV() { return ValueIterator(m_options_ptr->begin() + m_highlight); }
|
ValueIterator currentV() { return ValueIterator(m_items.begin() + m_highlight); }
|
||||||
ConstValueIterator currentV() const { return ConstValueIterator(m_options_ptr->begin() + m_highlight); }
|
ConstValueIterator currentV() const { return ConstValueIterator(m_items.begin() + m_highlight); }
|
||||||
ReverseValueIterator rcurrentV() { return ReverseValueIterator(++currentV()); }
|
ReverseValueIterator rcurrentV() { return ReverseValueIterator(++currentV()); }
|
||||||
ConstReverseValueIterator rcurrentV() const { return ConstReverseValueIterator(++currentV()); }
|
ConstReverseValueIterator rcurrentV() const { return ConstReverseValueIterator(++currentV()); }
|
||||||
|
|
||||||
Iterator begin() { return Iterator(m_options_ptr->begin()); }
|
Iterator begin() { return Iterator(m_items.begin()); }
|
||||||
ConstIterator begin() const { return ConstIterator(m_options_ptr->begin()); }
|
ConstIterator begin() const { return ConstIterator(m_items.begin()); }
|
||||||
Iterator end() { return Iterator(m_options_ptr->end()); }
|
Iterator end() { return Iterator(m_items.end()); }
|
||||||
ConstIterator end() const { return ConstIterator(m_options_ptr->end()); }
|
ConstIterator end() const { return ConstIterator(m_items.end()); }
|
||||||
|
|
||||||
ReverseIterator rbegin() { return ReverseIterator(end()); }
|
ReverseIterator rbegin() { return ReverseIterator(end()); }
|
||||||
ConstReverseIterator rbegin() const { return ConstReverseIterator(end()); }
|
ConstReverseIterator rbegin() const { return ConstReverseIterator(end()); }
|
||||||
@@ -320,17 +292,13 @@ private:
|
|||||||
|
|
||||||
bool isHighlightable(size_t pos)
|
bool isHighlightable(size_t pos)
|
||||||
{
|
{
|
||||||
return !(*m_options_ptr)[pos]->isSeparator()
|
return !m_items[pos]->isSeparator()
|
||||||
&& !(*m_options_ptr)[pos]->isInactive();
|
&& !m_items[pos]->isInactive();
|
||||||
}
|
}
|
||||||
|
|
||||||
ItemDisplayer m_item_displayer;
|
ItemDisplayer m_item_displayer;
|
||||||
|
|
||||||
FilterFunction m_filter;
|
std::vector<ItemProxy> m_items;
|
||||||
|
|
||||||
std::vector<ItemProxy> *m_options_ptr;
|
|
||||||
std::vector<ItemProxy> m_options;
|
|
||||||
std::vector<ItemProxy> m_filtered_options;
|
|
||||||
|
|
||||||
size_t m_beginning;
|
size_t m_beginning;
|
||||||
size_t m_highlight;
|
size_t m_highlight;
|
||||||
@@ -357,7 +325,6 @@ Menu<ItemT>::Menu(size_t startx,
|
|||||||
Border border)
|
Border border)
|
||||||
: Window(startx, starty, width, height, title, color, border),
|
: Window(startx, starty, width, height, title, color, border),
|
||||||
m_item_displayer(0),
|
m_item_displayer(0),
|
||||||
m_options_ptr(&m_options),
|
|
||||||
m_beginning(0),
|
m_beginning(0),
|
||||||
m_highlight(0),
|
m_highlight(0),
|
||||||
m_highlight_color(m_base_color),
|
m_highlight_color(m_base_color),
|
||||||
@@ -371,7 +338,6 @@ template <typename ItemT>
|
|||||||
Menu<ItemT>::Menu(const Menu &rhs)
|
Menu<ItemT>::Menu(const Menu &rhs)
|
||||||
: Window(rhs)
|
: Window(rhs)
|
||||||
, m_item_displayer(rhs.m_item_displayer)
|
, m_item_displayer(rhs.m_item_displayer)
|
||||||
, m_filter(rhs.m_filter)
|
|
||||||
, m_beginning(rhs.m_beginning)
|
, m_beginning(rhs.m_beginning)
|
||||||
, m_highlight(rhs.m_highlight)
|
, m_highlight(rhs.m_highlight)
|
||||||
, m_highlight_color(rhs.m_highlight_color)
|
, m_highlight_color(rhs.m_highlight_color)
|
||||||
@@ -384,19 +350,15 @@ Menu<ItemT>::Menu(const Menu &rhs)
|
|||||||
{
|
{
|
||||||
// there is no way to properly fill m_filtered_options
|
// there is no way to properly fill m_filtered_options
|
||||||
// (if rhs is filtered), so we just don't do it.
|
// (if rhs is filtered), so we just don't do it.
|
||||||
m_options.reserve(rhs.m_options.size());
|
m_items.reserve(rhs.m_items.size());
|
||||||
for (auto it = rhs.m_options.begin(); it != rhs.m_options.end(); ++it)
|
std::copy(rhs.begin(), rhs.end(), std::back_inserter(m_items));
|
||||||
m_options.push_back(ItemProxy(**it));
|
|
||||||
m_options_ptr = &m_options;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ItemT>
|
template <typename ItemT>
|
||||||
Menu<ItemT>::Menu(Menu &&rhs)
|
Menu<ItemT>::Menu(Menu &&rhs)
|
||||||
: Window(rhs)
|
: Window(rhs)
|
||||||
, m_item_displayer(rhs.m_item_displayer)
|
, m_item_displayer(rhs.m_item_displayer)
|
||||||
, m_filter(rhs.m_filter)
|
, m_items(std::move(rhs.m_items))
|
||||||
, m_options(std::move(rhs.m_options))
|
|
||||||
, m_filtered_options(std::move(rhs.m_filtered_options))
|
|
||||||
, m_beginning(rhs.m_beginning)
|
, m_beginning(rhs.m_beginning)
|
||||||
, m_highlight(rhs.m_highlight)
|
, m_highlight(rhs.m_highlight)
|
||||||
, m_highlight_color(rhs.m_highlight_color)
|
, m_highlight_color(rhs.m_highlight_color)
|
||||||
@@ -407,10 +369,6 @@ Menu<ItemT>::Menu(Menu &&rhs)
|
|||||||
, m_selected_prefix(std::move(rhs.m_selected_prefix))
|
, m_selected_prefix(std::move(rhs.m_selected_prefix))
|
||||||
, m_selected_suffix(std::move(rhs.m_selected_suffix))
|
, m_selected_suffix(std::move(rhs.m_selected_suffix))
|
||||||
{
|
{
|
||||||
if (rhs.m_options_ptr == &rhs.m_options)
|
|
||||||
m_options_ptr = &m_options;
|
|
||||||
else
|
|
||||||
m_options_ptr = &m_filtered_options;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ItemT>
|
template <typename ItemT>
|
||||||
@@ -418,9 +376,7 @@ Menu<ItemT> &Menu<ItemT>::operator=(Menu rhs)
|
|||||||
{
|
{
|
||||||
std::swap(static_cast<Window &>(*this), static_cast<Window &>(rhs));
|
std::swap(static_cast<Window &>(*this), static_cast<Window &>(rhs));
|
||||||
std::swap(m_item_displayer, rhs.m_item_displayer);
|
std::swap(m_item_displayer, rhs.m_item_displayer);
|
||||||
std::swap(m_filter, rhs.m_filter);
|
std::swap(m_items, rhs.m_items);
|
||||||
std::swap(m_options, rhs.m_options);
|
|
||||||
std::swap(m_filtered_options, rhs.m_filtered_options);
|
|
||||||
std::swap(m_beginning, rhs.m_beginning);
|
std::swap(m_beginning, rhs.m_beginning);
|
||||||
std::swap(m_highlight, rhs.m_highlight);
|
std::swap(m_highlight, rhs.m_highlight);
|
||||||
std::swap(m_highlight_color, rhs.m_highlight_color);
|
std::swap(m_highlight_color, rhs.m_highlight_color);
|
||||||
@@ -430,57 +386,52 @@ Menu<ItemT> &Menu<ItemT>::operator=(Menu rhs)
|
|||||||
std::swap(m_drawn_position, rhs.m_drawn_position);
|
std::swap(m_drawn_position, rhs.m_drawn_position);
|
||||||
std::swap(m_selected_prefix, rhs.m_selected_prefix);
|
std::swap(m_selected_prefix, rhs.m_selected_prefix);
|
||||||
std::swap(m_selected_suffix, rhs.m_selected_suffix);
|
std::swap(m_selected_suffix, rhs.m_selected_suffix);
|
||||||
if (rhs.m_options_ptr == &rhs.m_options)
|
|
||||||
m_options_ptr = &m_options;
|
|
||||||
else
|
|
||||||
m_options_ptr = &m_filtered_options;
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ItemT>
|
template <typename ItemT>
|
||||||
void Menu<ItemT>::resizeList(size_t new_size)
|
void Menu<ItemT>::resizeList(size_t new_size)
|
||||||
{
|
{
|
||||||
if (new_size > m_options.size())
|
if (new_size > m_items.size())
|
||||||
{
|
{
|
||||||
size_t old_size = m_options.size();
|
size_t old_size = m_items.size();
|
||||||
m_options.resize(new_size);
|
m_items.resize(new_size);
|
||||||
for (size_t i = old_size; i < new_size; ++i)
|
for (size_t i = old_size; i < new_size; ++i)
|
||||||
m_options[i] = Item();
|
m_items[i] = Item();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
m_options.resize(new_size);
|
m_items.resize(new_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ItemT>
|
template <typename ItemT>
|
||||||
void Menu<ItemT>::addItem(ItemT item, bool is_bold, bool is_inactive)
|
void Menu<ItemT>::addItem(ItemT item, bool is_bold, bool is_inactive)
|
||||||
{
|
{
|
||||||
m_options.push_back(Item(std::move(item), is_bold, is_inactive));
|
m_items.push_back(Item(std::move(item), is_bold, is_inactive));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ItemT>
|
template <typename ItemT>
|
||||||
void Menu<ItemT>::addSeparator()
|
void Menu<ItemT>::addSeparator()
|
||||||
{
|
{
|
||||||
m_options.push_back(Item::mkSeparator());
|
m_items.push_back(Item::mkSeparator());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ItemT>
|
template <typename ItemT>
|
||||||
void Menu<ItemT>::insertItem(size_t pos, const ItemT &item, bool is_bold, bool is_inactive)
|
void Menu<ItemT>::insertItem(size_t pos, const ItemT &item, bool is_bold, bool is_inactive)
|
||||||
{
|
{
|
||||||
m_options.insert(m_options.begin()+pos, Item(item, is_bold, is_inactive));
|
m_items.insert(m_items.begin()+pos, Item(item, is_bold, is_inactive));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ItemT>
|
template <typename ItemT>
|
||||||
void Menu<ItemT>::insertSeparator(size_t pos)
|
void Menu<ItemT>::insertSeparator(size_t pos)
|
||||||
{
|
{
|
||||||
m_options.insert(m_options.begin()+pos, Item::mkSeparator());
|
m_items.insert(m_items.begin()+pos, Item::mkSeparator());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ItemT>
|
template <typename ItemT>
|
||||||
void Menu<ItemT>::deleteItem(size_t pos)
|
void Menu<ItemT>::deleteItem(size_t pos)
|
||||||
{
|
{
|
||||||
assert(m_options_ptr != &m_filtered_options);
|
assert(pos < m_items.size());
|
||||||
assert(pos < m_options.size());
|
m_items.erase(m_items.begin()+pos);
|
||||||
m_options.erase(m_options.begin()+pos);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ItemT>
|
template <typename ItemT>
|
||||||
@@ -495,7 +446,7 @@ bool Menu<ItemT>::Goto(size_t y)
|
|||||||
template <typename ItemT>
|
template <typename ItemT>
|
||||||
void Menu<ItemT>::refresh()
|
void Menu<ItemT>::refresh()
|
||||||
{
|
{
|
||||||
if (m_options_ptr->empty())
|
if (m_items.empty())
|
||||||
{
|
{
|
||||||
Window::clear();
|
Window::clear();
|
||||||
Window::refresh();
|
Window::refresh();
|
||||||
@@ -503,14 +454,14 @@ void Menu<ItemT>::refresh()
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t max_beginning = 0;
|
size_t max_beginning = 0;
|
||||||
if (m_options_ptr->size() > m_height)
|
if (m_items.size() > m_height)
|
||||||
max_beginning = m_options_ptr->size() - m_height;
|
max_beginning = m_items.size() - m_height;
|
||||||
m_beginning = std::min(m_beginning, max_beginning);
|
m_beginning = std::min(m_beginning, max_beginning);
|
||||||
|
|
||||||
// if highlighted position is off the screen, make it visible
|
// if highlighted position is off the screen, make it visible
|
||||||
m_highlight = std::min(m_highlight, m_beginning+m_height-1);
|
m_highlight = std::min(m_highlight, m_beginning+m_height-1);
|
||||||
// if highlighted position is invalid, correct it
|
// if highlighted position is invalid, correct it
|
||||||
m_highlight = std::min(m_highlight, m_options_ptr->size()-1);
|
m_highlight = std::min(m_highlight, m_items.size()-1);
|
||||||
|
|
||||||
if (!isHighlightable(m_highlight))
|
if (!isHighlightable(m_highlight))
|
||||||
{
|
{
|
||||||
@@ -524,18 +475,18 @@ void Menu<ItemT>::refresh()
|
|||||||
for (size_t &i = m_drawn_position; i < m_beginning+m_height; ++i, ++line)
|
for (size_t &i = m_drawn_position; i < m_beginning+m_height; ++i, ++line)
|
||||||
{
|
{
|
||||||
goToXY(0, line);
|
goToXY(0, line);
|
||||||
if (i >= m_options_ptr->size())
|
if (i >= m_items.size())
|
||||||
{
|
{
|
||||||
for (; line < m_height; ++line)
|
for (; line < m_height; ++line)
|
||||||
mvwhline(m_window, line, 0, KEY_SPACE, m_width);
|
mvwhline(m_window, line, 0, KEY_SPACE, m_width);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if ((*m_options_ptr)[i]->isSeparator())
|
if (m_items[i]->isSeparator())
|
||||||
{
|
{
|
||||||
mvwhline(m_window, line, 0, 0, m_width);
|
mvwhline(m_window, line, 0, 0, m_width);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ((*m_options_ptr)[i]->isBold())
|
if (m_items[i]->isBold())
|
||||||
*this << Format::Bold;
|
*this << Format::Bold;
|
||||||
if (m_highlight_enabled && i == m_highlight)
|
if (m_highlight_enabled && i == m_highlight)
|
||||||
{
|
{
|
||||||
@@ -543,18 +494,18 @@ void Menu<ItemT>::refresh()
|
|||||||
*this << m_highlight_color;
|
*this << m_highlight_color;
|
||||||
}
|
}
|
||||||
mvwhline(m_window, line, 0, KEY_SPACE, m_width);
|
mvwhline(m_window, line, 0, KEY_SPACE, m_width);
|
||||||
if ((*m_options_ptr)[i]->isSelected())
|
if (m_items[i]->isSelected())
|
||||||
*this << m_selected_prefix;
|
*this << m_selected_prefix;
|
||||||
if (m_item_displayer)
|
if (m_item_displayer)
|
||||||
m_item_displayer(*this);
|
m_item_displayer(*this);
|
||||||
if ((*m_options_ptr)[i]->isSelected())
|
if (m_items[i]->isSelected())
|
||||||
*this << m_selected_suffix;
|
*this << m_selected_suffix;
|
||||||
if (m_highlight_enabled && i == m_highlight)
|
if (m_highlight_enabled && i == m_highlight)
|
||||||
{
|
{
|
||||||
*this << Color::End;
|
*this << Color::End;
|
||||||
*this << Format::NoReverse;
|
*this << Format::NoReverse;
|
||||||
}
|
}
|
||||||
if ((*m_options_ptr)[i]->isBold())
|
if (m_items[i]->isBold())
|
||||||
*this << Format::NoBold;
|
*this << Format::NoBold;
|
||||||
}
|
}
|
||||||
Window::refresh();
|
Window::refresh();
|
||||||
@@ -563,10 +514,10 @@ void Menu<ItemT>::refresh()
|
|||||||
template <typename ItemT>
|
template <typename ItemT>
|
||||||
void Menu<ItemT>::scroll(Scroll where)
|
void Menu<ItemT>::scroll(Scroll where)
|
||||||
{
|
{
|
||||||
if (m_options_ptr->empty())
|
if (m_items.empty())
|
||||||
return;
|
return;
|
||||||
size_t max_highlight = m_options_ptr->size()-1;
|
size_t max_highlight = m_items.size()-1;
|
||||||
size_t max_beginning = m_options_ptr->size() < m_height ? 0 : m_options_ptr->size()-m_height;
|
size_t max_beginning = m_items.size() < m_height ? 0 : m_items.size()-m_height;
|
||||||
size_t max_visible_highlight = m_beginning+m_height-1;
|
size_t max_visible_highlight = m_beginning+m_height-1;
|
||||||
switch (where)
|
switch (where)
|
||||||
{
|
{
|
||||||
@@ -661,15 +612,13 @@ void Menu<ItemT>::reset()
|
|||||||
template <typename ItemT>
|
template <typename ItemT>
|
||||||
void Menu<ItemT>::clear()
|
void Menu<ItemT>::clear()
|
||||||
{
|
{
|
||||||
clearFilterResults();
|
m_items.clear();
|
||||||
m_options.clear();
|
|
||||||
m_options_ptr = &m_options;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ItemT>
|
template <typename ItemT>
|
||||||
void Menu<ItemT>::highlight(size_t pos)
|
void Menu<ItemT>::highlight(size_t pos)
|
||||||
{
|
{
|
||||||
assert(pos < m_options_ptr->size());
|
assert(pos < m_items.size());
|
||||||
m_highlight = pos;
|
m_highlight = pos;
|
||||||
size_t half_height = m_height/2;
|
size_t half_height = m_height/2;
|
||||||
if (pos < half_height)
|
if (pos < half_height)
|
||||||
@@ -685,42 +634,6 @@ size_t Menu<ItemT>::choice() const
|
|||||||
return m_highlight;
|
return m_highlight;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ItemT>
|
|
||||||
void Menu<ItemT>::filter(ConstIterator first, ConstIterator last, const FilterFunction &f)
|
|
||||||
{
|
|
||||||
assert(m_options_ptr != &m_filtered_options);
|
|
||||||
clearFilterResults();
|
|
||||||
m_filter = f;
|
|
||||||
for (auto it = first; it != last; ++it)
|
|
||||||
if (m_filter(*it))
|
|
||||||
m_filtered_options.push_back(*it.base());
|
|
||||||
if (m_filtered_options == m_options)
|
|
||||||
m_filtered_options.clear();
|
|
||||||
else
|
|
||||||
m_options_ptr = &m_filtered_options;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename ItemT>
|
|
||||||
void Menu<ItemT>::applyCurrentFilter(ConstIterator first, ConstIterator last)
|
|
||||||
{
|
|
||||||
assert(m_filter);
|
|
||||||
filter(first, last, m_filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template <typename ItemT>
|
|
||||||
void Menu<ItemT>::clearFilterResults()
|
|
||||||
{
|
|
||||||
m_filtered_options.clear();
|
|
||||||
m_options_ptr = &m_options;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename ItemT>
|
|
||||||
void Menu<ItemT>::clearFilter()
|
|
||||||
{
|
|
||||||
m_filter = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // NCMPCPP_MENU_H
|
#endif // NCMPCPP_MENU_H
|
||||||
|
|||||||
@@ -148,36 +148,6 @@ void Playlist::mouseButtonPressed(MEVENT me)
|
|||||||
|
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
|
|
||||||
bool Playlist::allowsFiltering()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Playlist::currentFilter()
|
|
||||||
{
|
|
||||||
return RegexFilter<MPD::Song>::currentFilter(w);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Playlist::applyFilter(const std::string &filter)
|
|
||||||
{
|
|
||||||
if (filter.empty())
|
|
||||||
{
|
|
||||||
w.clearFilter();
|
|
||||||
w.clearFilterResults();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
w.showAll();
|
|
||||||
auto rx = RegexFilter<MPD::Song>(
|
|
||||||
boost::regex(filter, Config.regex_type), playlistEntryMatcher);
|
|
||||||
w.filter(w.begin(), w.end(), rx);
|
|
||||||
}
|
|
||||||
catch (boost::bad_expression &) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
/***********************************************************************/
|
|
||||||
|
|
||||||
bool Playlist::allowsSearching()
|
bool Playlist::allowsSearching()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
@@ -245,22 +215,12 @@ MPD::Song Playlist::nowPlayingSong()
|
|||||||
{
|
{
|
||||||
MPD::Song s;
|
MPD::Song s;
|
||||||
if (Status::State::player() != MPD::psUnknown)
|
if (Status::State::player() != MPD::psUnknown)
|
||||||
withUnfilteredMenu(w, [this, &s]() {
|
|
||||||
auto sp = Status::State::currentSongPosition();
|
|
||||||
if (sp >= 0 && size_t(sp) < w.size())
|
|
||||||
s = w.at(sp).value();
|
|
||||||
});
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Playlist::isFiltered()
|
|
||||||
{
|
|
||||||
if (w.isFiltered())
|
|
||||||
{
|
{
|
||||||
Statusbar::print("Function currently unavailable due to filtered playlist");
|
auto sp = Status::State::currentSongPosition();
|
||||||
return true;
|
if (sp >= 0 && size_t(sp) < w.size())
|
||||||
|
s = w.at(sp).value();
|
||||||
}
|
}
|
||||||
return false;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Playlist::Reverse()
|
void Playlist::Reverse()
|
||||||
@@ -292,7 +252,7 @@ std::string Playlist::getTotalLength()
|
|||||||
m_total_length += s.value().getDuration();
|
m_total_length += s.value().getDuration();
|
||||||
m_reload_total_length = false;
|
m_reload_total_length = false;
|
||||||
}
|
}
|
||||||
if (Config.playlist_show_remaining_time && m_reload_remaining && !w.isFiltered())
|
if (Config.playlist_show_remaining_time && m_reload_remaining)
|
||||||
{
|
{
|
||||||
m_remaining_time = 0;
|
m_remaining_time = 0;
|
||||||
for (size_t i = Status::State::currentSongPosition(); i < w.size(); ++i)
|
for (size_t i = Status::State::currentSongPosition(); i < w.size(); ++i)
|
||||||
@@ -302,21 +262,12 @@ std::string Playlist::getTotalLength()
|
|||||||
|
|
||||||
result << '(' << w.size() << (w.size() == 1 ? " item" : " items");
|
result << '(' << w.size() << (w.size() == 1 ? " item" : " items");
|
||||||
|
|
||||||
if (w.isFiltered())
|
|
||||||
{
|
|
||||||
w.showAll();
|
|
||||||
size_t real_size = w.size();
|
|
||||||
w.showFiltered();
|
|
||||||
if (w.size() != real_size)
|
|
||||||
result << " (out of " << real_size << ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_total_length)
|
if (m_total_length)
|
||||||
{
|
{
|
||||||
result << ", length: ";
|
result << ", length: ";
|
||||||
ShowTime(result, m_total_length, Config.playlist_shorten_total_times);
|
ShowTime(result, m_total_length, Config.playlist_shorten_total_times);
|
||||||
}
|
}
|
||||||
if (Config.playlist_show_remaining_time && m_remaining_time && !w.isFiltered() && w.size() > 1)
|
if (Config.playlist_show_remaining_time && m_remaining_time && w.size() > 1)
|
||||||
{
|
{
|
||||||
result << " :: remaining: ";
|
result << " :: remaining: ";
|
||||||
ShowTime(result, m_remaining_time, Config.playlist_shorten_total_times);
|
ShowTime(result, m_remaining_time, Config.playlist_shorten_total_times);
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
#include "song.h"
|
#include "song.h"
|
||||||
|
|
||||||
struct Playlist: Screen<NC::Menu<MPD::Song>>, Filterable, HasSongs, Searchable, Tabbable
|
struct Playlist: Screen<NC::Menu<MPD::Song>>, HasSongs, Searchable, Tabbable
|
||||||
{
|
{
|
||||||
Playlist();
|
Playlist();
|
||||||
|
|
||||||
@@ -48,11 +48,6 @@ struct Playlist: Screen<NC::Menu<MPD::Song>>, Filterable, HasSongs, Searchable,
|
|||||||
|
|
||||||
virtual bool isMergable() OVERRIDE { return true; }
|
virtual bool isMergable() OVERRIDE { return true; }
|
||||||
|
|
||||||
// Filterable implementation
|
|
||||||
virtual bool allowsFiltering() OVERRIDE;
|
|
||||||
virtual std::string currentFilter() OVERRIDE;
|
|
||||||
virtual void applyFilter(const std::string &filter) OVERRIDE;
|
|
||||||
|
|
||||||
// Searchable implementation
|
// Searchable implementation
|
||||||
virtual bool allowsSearching();
|
virtual bool allowsSearching();
|
||||||
virtual bool setSearchConstraint(const std::string &constraint) OVERRIDE;
|
virtual bool setSearchConstraint(const std::string &constraint) OVERRIDE;
|
||||||
@@ -69,7 +64,6 @@ struct Playlist: Screen<NC::Menu<MPD::Song>>, Filterable, HasSongs, Searchable,
|
|||||||
// private members
|
// private members
|
||||||
MPD::Song nowPlayingSong();
|
MPD::Song nowPlayingSong();
|
||||||
|
|
||||||
bool isFiltered();
|
|
||||||
void Reverse();
|
void Reverse();
|
||||||
|
|
||||||
void EnableHighlighting();
|
void EnableHighlighting();
|
||||||
|
|||||||
@@ -134,27 +134,25 @@ void PlaylistEditor::switchTo()
|
|||||||
|
|
||||||
void PlaylistEditor::update()
|
void PlaylistEditor::update()
|
||||||
{
|
{
|
||||||
if (Playlists.reallyEmpty() || m_playlists_update_requested)
|
if (Playlists.empty() || m_playlists_update_requested)
|
||||||
{
|
{
|
||||||
m_playlists_update_requested = false;
|
m_playlists_update_requested = false;
|
||||||
withUnfilteredMenuReapplyFilter(Playlists, [this]() {
|
size_t idx = 0;
|
||||||
size_t idx = 0;
|
for (MPD::PlaylistIterator it = Mpd.GetPlaylists(), end; it != end; ++it, ++idx)
|
||||||
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));
|
|
||||||
};
|
|
||||||
if (idx < Playlists.size())
|
if (idx < Playlists.size())
|
||||||
Playlists.resizeList(idx);
|
Playlists[idx].value() = std::move(*it);
|
||||||
std::sort(Playlists.beginV(), Playlists.endV(),
|
else
|
||||||
LocaleBasedSorting(std::locale(), Config.ignore_leading_the));
|
Playlists.addItem(std::move(*it));
|
||||||
});
|
};
|
||||||
|
if (idx < Playlists.size())
|
||||||
|
Playlists.resizeList(idx);
|
||||||
|
std::sort(Playlists.beginV(), Playlists.endV(),
|
||||||
|
LocaleBasedSorting(std::locale(), Config.ignore_leading_the));
|
||||||
Playlists.refresh();
|
Playlists.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((Content.reallyEmpty() && Global::Timer - m_timer > m_fetching_delay)
|
if ((Content.empty() && Global::Timer - m_timer > m_fetching_delay)
|
||||||
|| m_content_update_requested)
|
|| m_content_update_requested)
|
||||||
{
|
{
|
||||||
m_content_update_requested = false;
|
m_content_update_requested = false;
|
||||||
@@ -162,49 +160,43 @@ void PlaylistEditor::update()
|
|||||||
Content.clear();
|
Content.clear();
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
withUnfilteredMenuReapplyFilter(Content, [this]() {
|
size_t idx = 0;
|
||||||
size_t idx = 0;
|
MPD::SongIterator s = Mpd.GetPlaylistContent(Playlists.current()->value().path()), end;
|
||||||
MPD::SongIterator s = Mpd.GetPlaylistContent(Playlists.current()->value().path()), end;
|
for (; s != end; ++s, ++idx)
|
||||||
for (; s != end; ++s, ++idx)
|
{
|
||||||
{
|
bool is_bold = myPlaylist->checkForSong(*s);
|
||||||
bool is_bold = myPlaylist->checkForSong(*s);
|
|
||||||
if (idx < Content.size())
|
|
||||||
{
|
|
||||||
Content[idx].setBold(is_bold);
|
|
||||||
Content[idx].value() = std::move(*s);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
Content.addItem(std::move(*s), is_bold);
|
|
||||||
}
|
|
||||||
if (idx < Content.size())
|
if (idx < Content.size())
|
||||||
Content.resizeList(idx);
|
|
||||||
std::string title;
|
|
||||||
if (Config.titles_visibility)
|
|
||||||
{
|
{
|
||||||
title = "Playlist content";
|
Content[idx].setBold(is_bold);
|
||||||
title += " (";
|
Content[idx].value() = std::move(*s);
|
||||||
title += boost::lexical_cast<std::string>(Content.size());
|
|
||||||
title += " item";
|
|
||||||
if (Content.size() == 1)
|
|
||||||
title += ")";
|
|
||||||
else
|
|
||||||
title += "s)";
|
|
||||||
title.resize(Content.getWidth());
|
|
||||||
}
|
}
|
||||||
Content.setTitle(title);
|
else
|
||||||
});
|
Content.addItem(std::move(*s), is_bold);
|
||||||
|
}
|
||||||
|
if (idx < Content.size())
|
||||||
|
Content.resizeList(idx);
|
||||||
|
std::string title;
|
||||||
|
if (Config.titles_visibility)
|
||||||
|
{
|
||||||
|
title = (boost::format("Playlist content (%1%) %2%")
|
||||||
|
% boost::lexical_cast<std::string>(Content.size())
|
||||||
|
% (Content.size() == 1 ? "item" : "items")
|
||||||
|
).str();
|
||||||
|
title.resize(Content.getWidth());
|
||||||
|
}
|
||||||
|
Content.setTitle(title);
|
||||||
}
|
}
|
||||||
Content.display();
|
Content.display();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isActiveWindow(Content) && Content.reallyEmpty())
|
if (isActiveWindow(Content) && Content.empty())
|
||||||
{
|
{
|
||||||
Content.setHighlightColor(Config.main_highlight_color);
|
Content.setHighlightColor(Config.main_highlight_color);
|
||||||
Playlists.setHighlightColor(Config.active_column_color);
|
Playlists.setHighlightColor(Config.active_column_color);
|
||||||
w = &Playlists;
|
w = &Playlists;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Playlists.empty() && Content.reallyEmpty())
|
if (Playlists.empty() && Content.empty())
|
||||||
{
|
{
|
||||||
Content.Window::clear();
|
Content.Window::clear();
|
||||||
Content.Window::display();
|
Content.Window::display();
|
||||||
@@ -213,22 +205,12 @@ void PlaylistEditor::update()
|
|||||||
|
|
||||||
int PlaylistEditor::windowTimeout()
|
int PlaylistEditor::windowTimeout()
|
||||||
{
|
{
|
||||||
if (Content.reallyEmpty())
|
if (Content.empty())
|
||||||
return m_window_timeout;
|
return m_window_timeout;
|
||||||
else
|
else
|
||||||
return Screen<WindowType>::windowTimeout();
|
return Screen<WindowType>::windowTimeout();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PlaylistEditor::isContentFiltered()
|
|
||||||
{
|
|
||||||
if (Content.isFiltered())
|
|
||||||
{
|
|
||||||
Statusbar::print("Function currently unavailable due to filtered playlist content");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ProxySongList PlaylistEditor::contentProxyList()
|
ProxySongList PlaylistEditor::contentProxyList()
|
||||||
{
|
{
|
||||||
return ProxySongList(Content, [](NC::Menu<MPD::Song>::Item &item) {
|
return ProxySongList(Content, [](NC::Menu<MPD::Song>::Item &item) {
|
||||||
@@ -242,10 +224,7 @@ void PlaylistEditor::AddToPlaylist(bool add_n_play)
|
|||||||
|
|
||||||
if (isActiveWindow(Playlists) && !Playlists.empty())
|
if (isActiveWindow(Playlists) && !Playlists.empty())
|
||||||
{
|
{
|
||||||
bool success;
|
bool success = addSongsToPlaylist(Content.beginV(), Content.endV(), add_n_play, -1);
|
||||||
withUnfilteredMenu(Content, [&]() {
|
|
||||||
success = addSongsToPlaylist(Content.beginV(), Content.endV(), add_n_play, -1);
|
|
||||||
});
|
|
||||||
Statusbar::printf("Playlist \"%1%\" loaded%2%",
|
Statusbar::printf("Playlist \"%1%\" loaded%2%",
|
||||||
Playlists.current()->value().path(), withErrors(success)
|
Playlists.current()->value().path(), withErrors(success)
|
||||||
);
|
);
|
||||||
@@ -342,59 +321,6 @@ void PlaylistEditor::mouseButtonPressed(MEVENT me)
|
|||||||
|
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
|
|
||||||
bool PlaylistEditor::allowsFiltering()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string PlaylistEditor::currentFilter()
|
|
||||||
{
|
|
||||||
std::string filter;
|
|
||||||
if (isActiveWindow(Playlists))
|
|
||||||
filter = RegexFilter<MPD::Playlist>::currentFilter(Playlists);
|
|
||||||
else if (isActiveWindow(Content))
|
|
||||||
filter = RegexFilter<MPD::Song>::currentFilter(Content);
|
|
||||||
return filter;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlaylistEditor::applyFilter(const std::string &filter)
|
|
||||||
{
|
|
||||||
if (filter.empty())
|
|
||||||
{
|
|
||||||
if (isActiveWindow(Playlists))
|
|
||||||
{
|
|
||||||
Playlists.clearFilter();
|
|
||||||
Playlists.clearFilterResults();
|
|
||||||
}
|
|
||||||
else if (isActiveWindow(Content))
|
|
||||||
{
|
|
||||||
Content.clearFilter();
|
|
||||||
Content.clearFilterResults();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (isActiveWindow(Playlists))
|
|
||||||
{
|
|
||||||
Playlists.showAll();
|
|
||||||
auto rx = RegexFilter<MPD::Playlist>(
|
|
||||||
boost::regex(filter, Config.regex_type), PlaylistEntryMatcher);
|
|
||||||
Playlists.filter(Playlists.begin(), Playlists.end(), rx);
|
|
||||||
}
|
|
||||||
else if (isActiveWindow(Content))
|
|
||||||
{
|
|
||||||
Content.showAll();
|
|
||||||
auto rx = RegexFilter<MPD::Song>(
|
|
||||||
boost::regex(filter, Config.regex_type), SongEntryMatcher);
|
|
||||||
Content.filter(Content.begin(), Content.end(), rx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (boost::bad_expression &) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
/***********************************************************************/
|
|
||||||
|
|
||||||
bool PlaylistEditor::allowsSearching()
|
bool PlaylistEditor::allowsSearching()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
@@ -489,11 +415,7 @@ std::vector<MPD::Song> PlaylistEditor::getSelectedSongs()
|
|||||||
}
|
}
|
||||||
// if no item is selected, add songs from right column
|
// if no item is selected, add songs from right column
|
||||||
if (!any_selected && !Content.empty())
|
if (!any_selected && !Content.empty())
|
||||||
{
|
std::copy(Content.beginV(), Content.endV(), std::back_inserter(result));
|
||||||
withUnfilteredMenu(Content, [this, &result]() {
|
|
||||||
std::copy(Content.beginV(), Content.endV(), std::back_inserter(result));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (isActiveWindow(Content))
|
else if (isActiveWindow(Content))
|
||||||
{
|
{
|
||||||
@@ -513,7 +435,7 @@ bool PlaylistEditor::previousColumnAvailable()
|
|||||||
{
|
{
|
||||||
if (isActiveWindow(Content))
|
if (isActiveWindow(Content))
|
||||||
{
|
{
|
||||||
if (!Playlists.reallyEmpty())
|
if (!Playlists.empty())
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -534,7 +456,7 @@ bool PlaylistEditor::nextColumnAvailable()
|
|||||||
{
|
{
|
||||||
if (isActiveWindow(Playlists))
|
if (isActiveWindow(Playlists))
|
||||||
{
|
{
|
||||||
if (!Content.reallyEmpty())
|
if (!Content.empty())
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
#include "regex_filter.h"
|
#include "regex_filter.h"
|
||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
|
|
||||||
struct PlaylistEditor: Screen<NC::Window *>, Filterable, HasColumns, HasSongs, Searchable, Tabbable
|
struct PlaylistEditor: Screen<NC::Window *>, HasColumns, HasSongs, Searchable, Tabbable
|
||||||
{
|
{
|
||||||
PlaylistEditor();
|
PlaylistEditor();
|
||||||
|
|
||||||
@@ -48,11 +48,6 @@ struct PlaylistEditor: Screen<NC::Window *>, Filterable, HasColumns, HasSongs, S
|
|||||||
|
|
||||||
virtual bool isMergable() OVERRIDE { return true; }
|
virtual bool isMergable() OVERRIDE { return true; }
|
||||||
|
|
||||||
// Filterable implementation
|
|
||||||
virtual bool allowsFiltering() OVERRIDE;
|
|
||||||
virtual std::string currentFilter() OVERRIDE;
|
|
||||||
virtual void applyFilter(const std::string &filter) OVERRIDE;
|
|
||||||
|
|
||||||
// Searchable implementation
|
// Searchable implementation
|
||||||
virtual bool allowsSearching() OVERRIDE;
|
virtual bool allowsSearching() OVERRIDE;
|
||||||
virtual bool setSearchConstraint(const std::string &constraint) OVERRIDE;
|
virtual bool setSearchConstraint(const std::string &constraint) OVERRIDE;
|
||||||
@@ -80,7 +75,6 @@ struct PlaylistEditor: Screen<NC::Window *>, Filterable, HasColumns, HasSongs, S
|
|||||||
void requestContentsUpdate() { m_content_update_requested = true; }
|
void requestContentsUpdate() { m_content_update_requested = true; }
|
||||||
|
|
||||||
virtual void Locate(const MPD::Playlist &playlist);
|
virtual void Locate(const MPD::Playlist &playlist);
|
||||||
bool isContentFiltered();
|
|
||||||
ProxySongList contentProxyList();
|
ProxySongList contentProxyList();
|
||||||
|
|
||||||
NC::Menu<MPD::Playlist> Playlists;
|
NC::Menu<MPD::Playlist> Playlists;
|
||||||
|
|||||||
@@ -23,7 +23,6 @@
|
|||||||
|
|
||||||
#include <boost/regex.hpp>
|
#include <boost/regex.hpp>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include "menu.h"
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct RegexFilter
|
struct RegexFilter
|
||||||
@@ -51,15 +50,6 @@ struct RegexFilter
|
|||||||
return m_filter.operator bool();
|
return m_filter.operator bool();
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string currentFilter(MenuT &menu)
|
|
||||||
{
|
|
||||||
std::string filter;
|
|
||||||
auto rf = menu.getFilter().template target< RegexFilter<T> >();
|
|
||||||
if (rf)
|
|
||||||
filter = rf->m_rx.str();
|
|
||||||
return filter;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
boost::regex m_rx;
|
boost::regex m_rx;
|
||||||
FilterFunction m_filter;
|
FilterFunction m_filter;
|
||||||
@@ -89,15 +79,6 @@ template <typename T> struct RegexItemFilter
|
|||||||
return m_filter.operator bool();
|
return m_filter.operator bool();
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string currentFilter(MenuT &menu)
|
|
||||||
{
|
|
||||||
std::string filter;
|
|
||||||
auto rf = menu.getFilter().template target< RegexItemFilter<T> >();
|
|
||||||
if (rf)
|
|
||||||
filter = rf->m_rx.str();
|
|
||||||
return filter;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
boost::regex m_rx;
|
boost::regex m_rx;
|
||||||
FilterFunction m_filter;
|
FilterFunction m_filter;
|
||||||
|
|||||||
@@ -176,7 +176,6 @@ void SearchEngine::enterPressed()
|
|||||||
}
|
}
|
||||||
else if (option == SearchButton)
|
else if (option == SearchButton)
|
||||||
{
|
{
|
||||||
w.showAll();
|
|
||||||
Statusbar::print("Searching...");
|
Statusbar::print("Searching...");
|
||||||
if (w.size() > StaticOptions)
|
if (w.size() > StaticOptions)
|
||||||
Prepare();
|
Prepare();
|
||||||
@@ -256,37 +255,6 @@ void SearchEngine::mouseButtonPressed(MEVENT me)
|
|||||||
|
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
|
|
||||||
bool SearchEngine::allowsFiltering()
|
|
||||||
{
|
|
||||||
return w.rbegin()->value().isSong();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string SearchEngine::currentFilter()
|
|
||||||
{
|
|
||||||
return RegexItemFilter<SEItem>::currentFilter(w);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SearchEngine::applyFilter(const std::string &filter)
|
|
||||||
{
|
|
||||||
if (filter.empty())
|
|
||||||
{
|
|
||||||
w.clearFilter();
|
|
||||||
w.clearFilterResults();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
w.showAll();
|
|
||||||
auto fun = boost::bind(SEItemEntryMatcher, _1, _2, true);
|
|
||||||
auto rx = RegexItemFilter<SEItem>(
|
|
||||||
boost::regex(filter, Config.regex_type), fun);
|
|
||||||
w.filter(w.begin(), w.end(), rx);
|
|
||||||
}
|
|
||||||
catch (boost::bad_expression &) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
/***********************************************************************/
|
|
||||||
|
|
||||||
bool SearchEngine::allowsSearching()
|
bool SearchEngine::allowsSearching()
|
||||||
{
|
{
|
||||||
return w.rbegin()->value().isSong();
|
return w.rbegin()->value().isSong();
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ private:
|
|||||||
MPD::Song m_song;
|
MPD::Song m_song;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SearchEngine: Screen<NC::Menu<SEItem>>, Filterable, HasSongs, Searchable, Tabbable
|
struct SearchEngine: Screen<NC::Menu<SEItem>>, HasSongs, Searchable, Tabbable
|
||||||
{
|
{
|
||||||
SearchEngine();
|
SearchEngine();
|
||||||
|
|
||||||
@@ -93,11 +93,6 @@ struct SearchEngine: Screen<NC::Menu<SEItem>>, Filterable, HasSongs, Searchable,
|
|||||||
|
|
||||||
virtual bool isMergable() OVERRIDE { return true; }
|
virtual bool isMergable() OVERRIDE { return true; }
|
||||||
|
|
||||||
// Filterable implementation
|
|
||||||
virtual bool allowsFiltering() OVERRIDE;
|
|
||||||
virtual std::string currentFilter() OVERRIDE;
|
|
||||||
virtual void applyFilter(const std::string &filter) OVERRIDE;
|
|
||||||
|
|
||||||
// Searchable implementation
|
// Searchable implementation
|
||||||
virtual bool allowsSearching() OVERRIDE;
|
virtual bool allowsSearching() OVERRIDE;
|
||||||
virtual bool setSearchConstraint(const std::string &constraint) OVERRIDE;
|
virtual bool setSearchConstraint(const std::string &constraint) OVERRIDE;
|
||||||
|
|||||||
@@ -271,11 +271,9 @@ void SelectedItemsAdder::addAfterCurrentAlbum() const
|
|||||||
return;
|
return;
|
||||||
auto &pl = myPlaylist->main();
|
auto &pl = myPlaylist->main();
|
||||||
size_t pos = Status::State::currentSongPosition();
|
size_t pos = Status::State::currentSongPosition();
|
||||||
withUnfilteredMenu(pl, [&pos, &pl]() {
|
std::string album = pl[pos].value().getAlbum();
|
||||||
std::string album = pl[pos].value().getAlbum();
|
while (pos < pl.size() && pl[pos].value().getAlbum() == album)
|
||||||
while (pos < pl.size() && pl[pos].value().getAlbum() == album)
|
++pos;
|
||||||
++pos;
|
|
||||||
});
|
|
||||||
bool success = addSongsToPlaylist(m_selected_items.begin(), m_selected_items.end(), false, pos);
|
bool success = addSongsToPlaylist(m_selected_items.begin(), m_selected_items.end(), false, pos);
|
||||||
exitSuccessfully(success);
|
exitSuccessfully(success);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -387,32 +387,30 @@ int Status::State::volume()
|
|||||||
|
|
||||||
void Status::Changes::playlist(unsigned previous_version)
|
void Status::Changes::playlist(unsigned previous_version)
|
||||||
{
|
{
|
||||||
withUnfilteredMenuReapplyFilter(myPlaylist->main(), [previous_version]() {
|
if (m_playlist_length < myPlaylist->main().size())
|
||||||
if (m_playlist_length < myPlaylist->main().size())
|
{
|
||||||
|
auto it = myPlaylist->main().begin()+m_playlist_length;
|
||||||
|
auto end = myPlaylist->main().end();
|
||||||
|
for (; it != end; ++it)
|
||||||
|
myPlaylist->unregisterSong(it->value());
|
||||||
|
myPlaylist->main().resizeList(m_playlist_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
MPD::SongIterator s = Mpd.GetPlaylistChanges(previous_version), end;
|
||||||
|
for (; s != end; ++s)
|
||||||
|
{
|
||||||
|
size_t pos = s->getPosition();
|
||||||
|
myPlaylist->registerSong(*s);
|
||||||
|
if (pos < myPlaylist->main().size())
|
||||||
{
|
{
|
||||||
auto it = myPlaylist->main().begin()+m_playlist_length;
|
// if song's already in playlist, replace it with a new one
|
||||||
auto end = myPlaylist->main().end();
|
MPD::Song &old_s = myPlaylist->main()[pos].value();
|
||||||
for (; it != end; ++it)
|
myPlaylist->unregisterSong(old_s);
|
||||||
myPlaylist->unregisterSong(it->value());
|
old_s = std::move(*s);
|
||||||
myPlaylist->main().resizeList(m_playlist_length);
|
|
||||||
}
|
}
|
||||||
|
else // otherwise just add it to playlist
|
||||||
MPD::SongIterator s = Mpd.GetPlaylistChanges(previous_version), end;
|
myPlaylist->main().addItem(std::move(*s));
|
||||||
for (; s != end; ++s)
|
}
|
||||||
{
|
|
||||||
size_t pos = s->getPosition();
|
|
||||||
myPlaylist->registerSong(*s);
|
|
||||||
if (pos < myPlaylist->main().size())
|
|
||||||
{
|
|
||||||
// if song's already in playlist, replace it with a new one
|
|
||||||
MPD::Song &old_s = myPlaylist->main()[pos].value();
|
|
||||||
myPlaylist->unregisterSong(old_s);
|
|
||||||
old_s = std::move(*s);
|
|
||||||
}
|
|
||||||
else // otherwise just add it to playlist
|
|
||||||
myPlaylist->main().addItem(std::move(*s));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
myPlaylist->reloadTotalLength();
|
myPlaylist->reloadTotalLength();
|
||||||
myPlaylist->reloadRemaining();
|
myPlaylist->reloadRemaining();
|
||||||
@@ -536,7 +534,7 @@ void Status::Changes::songID(int song_id)
|
|||||||
|
|
||||||
drawTitle(s);
|
drawTitle(s);
|
||||||
|
|
||||||
if (Config.autocenter_mode && !pl.isFiltered())
|
if (Config.autocenter_mode)
|
||||||
pl.highlight(Status::State::currentSongPosition());
|
pl.highlight(Status::State::currentSongPosition());
|
||||||
|
|
||||||
if (Config.now_playing_lyrics && isVisible(myLyrics) && myLyrics->previousScreen() == myPlaylist)
|
if (Config.now_playing_lyrics && isVisible(myLyrics) && myLyrics->previousScreen() == myPlaylist)
|
||||||
|
|||||||
@@ -220,27 +220,6 @@ bool Statusbar::Helpers::ImmediatelyReturnOneOf::operator()(const char *s) const
|
|||||||
return !isOneOf(s);
|
return !isOneOf(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Statusbar::Helpers::ApplyFilterImmediately::operator()(const char *s)
|
|
||||||
{
|
|
||||||
using Global::myScreen;
|
|
||||||
// if input queue is not empty, we don't want to update filter since next
|
|
||||||
// character will be taken from queue immediately, trigering this function
|
|
||||||
// again and thus making it inefficient, so let's apply filter only if
|
|
||||||
// "real" user input arrived. however, we want to apply filter if ENTER
|
|
||||||
// is next in queue, so its effects will be seen.
|
|
||||||
if (wFooter->inputQueue().empty() || wFooter->inputQueue().front() == KEY_ENTER)
|
|
||||||
{
|
|
||||||
if (m_s != s)
|
|
||||||
{
|
|
||||||
m_s = s;
|
|
||||||
m_f->applyFilter(m_s);
|
|
||||||
myScreen->refreshWindow();
|
|
||||||
}
|
|
||||||
Status::trace();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Statusbar::Helpers::TryExecuteImmediateCommand::operator()(const char *s)
|
bool Statusbar::Helpers::TryExecuteImmediateCommand::operator()(const char *s)
|
||||||
{
|
{
|
||||||
bool continue_ = true;
|
bool continue_ = true;
|
||||||
|
|||||||
@@ -89,20 +89,6 @@ private:
|
|||||||
std::vector<std::string> m_values;
|
std::vector<std::string> m_values;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// called each time user changes current filter (while being inside Window::getString)
|
|
||||||
struct ApplyFilterImmediately
|
|
||||||
{
|
|
||||||
template <typename StringT>
|
|
||||||
ApplyFilterImmediately(Filterable *f, StringT &&filter)
|
|
||||||
: m_f(f), m_s(std::forward<StringT>(filter)) { }
|
|
||||||
|
|
||||||
bool operator()(const char *s);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Filterable *m_f;
|
|
||||||
std::string m_s;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TryExecuteImmediateCommand
|
struct TryExecuteImmediateCommand
|
||||||
{
|
{
|
||||||
bool operator()(const char *s);
|
bool operator()(const char *s);
|
||||||
|
|||||||
@@ -230,7 +230,7 @@ void TagEditor::refresh()
|
|||||||
|
|
||||||
void TagEditor::update()
|
void TagEditor::update()
|
||||||
{
|
{
|
||||||
if (Dirs->reallyEmpty())
|
if (Dirs->empty())
|
||||||
{
|
{
|
||||||
Dirs->Window::clear();
|
Dirs->Window::clear();
|
||||||
Tags->clear();
|
Tags->clear();
|
||||||
@@ -251,7 +251,7 @@ void TagEditor::update()
|
|||||||
Dirs->display();
|
Dirs->display();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Tags->reallyEmpty())
|
if (Tags->empty())
|
||||||
{
|
{
|
||||||
Tags->reset();
|
Tags->reset();
|
||||||
MPD::SongIterator s = Mpd.GetSongs(Dirs->current()->value().second), end;
|
MPD::SongIterator s = Mpd.GetSongs(Dirs->current()->value().second), end;
|
||||||
@@ -716,60 +716,6 @@ void TagEditor::mouseButtonPressed(MEVENT me)
|
|||||||
|
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
|
|
||||||
bool TagEditor::allowsFiltering()
|
|
||||||
{
|
|
||||||
return w == Dirs || w == Tags;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string TagEditor::currentFilter()
|
|
||||||
{
|
|
||||||
std::string filter;
|
|
||||||
if (w == Dirs)
|
|
||||||
filter = RegexFilter< std::pair<std::string, std::string> >::currentFilter(*Dirs);
|
|
||||||
else if (w == Tags)
|
|
||||||
filter = RegexFilter<MPD::MutableSong>::currentFilter(*Tags);
|
|
||||||
return filter;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TagEditor::applyFilter(const std::string &filter)
|
|
||||||
{
|
|
||||||
if (filter.empty())
|
|
||||||
{
|
|
||||||
if (w == Dirs)
|
|
||||||
{
|
|
||||||
Dirs->clearFilter();
|
|
||||||
Dirs->clearFilterResults();
|
|
||||||
}
|
|
||||||
else if (w == Tags)
|
|
||||||
{
|
|
||||||
Tags->clearFilter();
|
|
||||||
Tags->clearFilterResults();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (w == Dirs)
|
|
||||||
{
|
|
||||||
Dirs->showAll();
|
|
||||||
auto fun = boost::bind(DirEntryMatcher, _1, _2, true);
|
|
||||||
auto rx = RegexFilter< std::pair<std::string, std::string> >(
|
|
||||||
boost::regex(filter, Config.regex_type), fun);
|
|
||||||
Dirs->filter(Dirs->begin(), Dirs->end(), rx);
|
|
||||||
}
|
|
||||||
else if (w == Tags)
|
|
||||||
{
|
|
||||||
Tags->showAll();
|
|
||||||
auto rx = RegexFilter<MPD::MutableSong>(
|
|
||||||
boost::regex(filter, Config.regex_type), SongEntryMatcher);
|
|
||||||
Tags->filter(Tags->begin(), Tags->end(), rx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (boost::bad_expression &) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
/***********************************************************************/
|
|
||||||
|
|
||||||
bool TagEditor::allowsSearching()
|
bool TagEditor::allowsSearching()
|
||||||
{
|
{
|
||||||
return w == Dirs || w == Tags;
|
return w == Dirs || w == Tags;
|
||||||
@@ -866,12 +812,12 @@ bool TagEditor::previousColumnAvailable()
|
|||||||
bool result = false;
|
bool result = false;
|
||||||
if (w == Tags)
|
if (w == Tags)
|
||||||
{
|
{
|
||||||
if (!TagTypes->reallyEmpty() && !Dirs->reallyEmpty())
|
if (!TagTypes->empty() && !Dirs->empty())
|
||||||
result = true;
|
result = true;
|
||||||
}
|
}
|
||||||
else if (w == TagTypes)
|
else if (w == TagTypes)
|
||||||
{
|
{
|
||||||
if (!Dirs->reallyEmpty() && isAnyModified(*Tags))
|
if (!Dirs->empty() && isAnyModified(*Tags))
|
||||||
Actions::confirmAction("There are pending changes, are you sure?");
|
Actions::confirmAction("There are pending changes, are you sure?");
|
||||||
result = true;
|
result = true;
|
||||||
}
|
}
|
||||||
@@ -911,12 +857,12 @@ bool TagEditor::nextColumnAvailable()
|
|||||||
bool result = false;
|
bool result = false;
|
||||||
if (w == Dirs)
|
if (w == Dirs)
|
||||||
{
|
{
|
||||||
if (!TagTypes->reallyEmpty() && !Tags->reallyEmpty())
|
if (!TagTypes->empty() && !Tags->empty())
|
||||||
result = true;
|
result = true;
|
||||||
}
|
}
|
||||||
else if (w == TagTypes)
|
else if (w == TagTypes)
|
||||||
{
|
{
|
||||||
if (!Tags->reallyEmpty())
|
if (!Tags->empty())
|
||||||
result = true;
|
result = true;
|
||||||
}
|
}
|
||||||
else if (w == FParser)
|
else if (w == FParser)
|
||||||
@@ -933,7 +879,7 @@ void TagEditor::nextColumn()
|
|||||||
w = TagTypes;
|
w = TagTypes;
|
||||||
TagTypes->setHighlightColor(Config.active_column_color);
|
TagTypes->setHighlightColor(Config.active_column_color);
|
||||||
}
|
}
|
||||||
else if (w == TagTypes && TagTypes->choice() < 13 && !Tags->reallyEmpty())
|
else if (w == TagTypes && TagTypes->choice() < 13 && !Tags->empty())
|
||||||
{
|
{
|
||||||
TagTypes->setHighlightColor(Config.main_highlight_color);
|
TagTypes->setHighlightColor(Config.main_highlight_color);
|
||||||
w->refresh();
|
w->refresh();
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
#include "regex_filter.h"
|
#include "regex_filter.h"
|
||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
|
|
||||||
struct TagEditor: Screen<NC::Window *>, Filterable, HasColumns, HasSongs, Searchable, Tabbable
|
struct TagEditor: Screen<NC::Window *>, HasColumns, HasSongs, Searchable, Tabbable
|
||||||
{
|
{
|
||||||
TagEditor();
|
TagEditor();
|
||||||
|
|
||||||
@@ -51,11 +51,6 @@ struct TagEditor: Screen<NC::Window *>, Filterable, HasColumns, HasSongs, Search
|
|||||||
|
|
||||||
virtual bool isMergable() OVERRIDE { return true; }
|
virtual bool isMergable() OVERRIDE { return true; }
|
||||||
|
|
||||||
// Filterable implementation
|
|
||||||
virtual bool allowsFiltering() OVERRIDE;
|
|
||||||
virtual std::string currentFilter() OVERRIDE;
|
|
||||||
virtual void applyFilter(const std::string &filter) OVERRIDE;
|
|
||||||
|
|
||||||
// Searchable implementation
|
// Searchable implementation
|
||||||
virtual bool allowsSearching() OVERRIDE;
|
virtual bool allowsSearching() OVERRIDE;
|
||||||
virtual bool setSearchConstraint(const std::string &constraint) OVERRIDE;
|
virtual bool setSearchConstraint(const std::string &constraint) OVERRIDE;
|
||||||
|
|||||||
Reference in New Issue
Block a user