menu: remove search related methods

This commit is contained in:
Andrzej Rybczak
2014-11-04 20:53:03 +01:00
parent c01e734e6e
commit 12772c47f9
18 changed files with 306 additions and 290 deletions

View File

@@ -70,10 +70,25 @@
using Global::myScreen; using Global::myScreen;
namespace {// namespace {
enum class Find { Forward, Backward }; enum class Find { Forward, Backward };
std::string findToString(Find find)
{
std::string result;
switch (find)
{
case Find::Forward:
result = "forward";
break;
case Find::Backward:
result = "backward";
break;
}
return result;
}
boost::array< boost::array<
Actions::BaseAction *, static_cast<size_t>(Actions::Type::_numberOfActions) Actions::BaseAction *, static_cast<size_t>(Actions::Type::_numberOfActions)
> AvailableActions; > AvailableActions;
@@ -1942,7 +1957,8 @@ bool NextFoundItem::canBeRun() const
void NextFoundItem::run() void NextFoundItem::run()
{ {
Searchable *w = dynamic_cast<Searchable *>(myScreen); Searchable *w = dynamic_cast<Searchable *>(myScreen);
w->nextFound(Config.wrapped_search); assert(w != nullptr);
w->findForward(Config.wrapped_search);
listsChangeFinisher(); listsChangeFinisher();
} }
@@ -1954,7 +1970,8 @@ bool PreviousFoundItem::canBeRun() const
void PreviousFoundItem::run() void PreviousFoundItem::run()
{ {
Searchable *w = dynamic_cast<Searchable *>(myScreen); Searchable *w = dynamic_cast<Searchable *>(myScreen);
w->prevFound(Config.wrapped_search); assert(w != nullptr);
w->findBackward(Config.wrapped_search);
listsChangeFinisher(); listsChangeFinisher();
} }
@@ -2798,28 +2815,38 @@ void findItem(const Find direction)
using Global::wFooter; using Global::wFooter;
Searchable *w = dynamic_cast<Searchable *>(myScreen); Searchable *w = dynamic_cast<Searchable *>(myScreen);
assert(w); assert(w != nullptr);
assert(w->allowsSearching()); assert(w->allowsSearching());
std::string token; std::string constraint;
{ {
Statusbar::ScopedLock slock; Statusbar::ScopedLock slock;
Statusbar::put() << "Find " << (direction == Find::Forward ? "forward" : "backward") << ": "; Statusbar::put() << "Find " << findToString(direction) << ": ";
token = wFooter->prompt(); constraint = wFooter->prompt();
} }
Statusbar::print("Searching..."); try
bool success = w->search(token); {
bool success = w->setSearchConstraint(constraint);
if (success) if (success)
Statusbar::print("Searching finished"); Statusbar::printf("Using constraint \"%1%\"", constraint);
else else
Statusbar::printf("Unable to find \"%1%\"", token); Statusbar::printf("Constraint unset");
}
if (direction == Find::Forward) catch (boost::bad_expression &e)
w->nextFound(Config.wrapped_search); {
else Statusbar::printf("%1%", e.what());
w->prevFound(Config.wrapped_search); }
switch (direction)
{
case Find::Forward:
w->findForward(Config.wrapped_search);
break;
case Find::Backward:
w->findBackward(Config.wrapped_search);
break;
}
} }
void listsChangeFinisher() void listsChangeFinisher()

View File

@@ -32,7 +32,6 @@
#include "global.h" #include "global.h"
#include "helpers.h" #include "helpers.h"
#include "playlist.h" #include "playlist.h"
#include "regex_filter.h"
#include "screen_switcher.h" #include "screen_switcher.h"
#include "settings.h" #include "settings.h"
#include "status.h" #include "status.h"
@@ -285,34 +284,31 @@ bool Browser::allowsSearching()
return true; return true;
} }
bool Browser::search(const std::string &constraint) bool Browser::setSearchConstraint(const std::string &constraint)
{ {
if (constraint.empty()) if (constraint.empty())
{ {
w.clearSearchResults(); m_search_predicate.clear();
return false; return false;
} }
try else
{ {
auto fun = boost::bind(browserEntryMatcher, _1, _2, false); m_search_predicate = RegexFilter<MPD::Item>(
auto rx = RegexFilter<MPD::Item>( boost::regex(constraint, Config.regex_type),
boost::regex(constraint, Config.regex_type), fun); boost::bind(browserEntryMatcher, _1, _2, false)
return w.search(w.begin(), w.end(), rx); );
} return true;
catch (boost::bad_expression &)
{
return false;
} }
} }
void Browser::nextFound(bool wrap) void Browser::findForward(bool wrap)
{ {
w.nextFound(wrap); searchForward(w, m_search_predicate, wrap);
} }
void Browser::prevFound(bool wrap) void Browser::findBackward(bool wrap)
{ {
w.prevFound(wrap); searchBackward(w, m_search_predicate, wrap);
} }
/***********************************************************************/ /***********************************************************************/

View File

@@ -23,6 +23,7 @@
#include "interfaces.h" #include "interfaces.h"
#include "mpdpp.h" #include "mpdpp.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>>, Filterable, HasSongs, Searchable, Tabbable
@@ -51,9 +52,9 @@ struct Browser: Screen<NC::Menu<MPD::Item>>, Filterable, HasSongs, Searchable, T
// Searchable implementation // Searchable implementation
virtual bool allowsSearching() OVERRIDE; virtual bool allowsSearching() OVERRIDE;
virtual bool search(const std::string &constraint) OVERRIDE; virtual bool setSearchConstraint(const std::string &constraint) OVERRIDE;
virtual void nextFound(bool wrap) OVERRIDE; virtual void findForward(bool wrap) OVERRIDE;
virtual void prevFound(bool wrap) OVERRIDE; virtual void findBackward(bool wrap) OVERRIDE;
// HasSongs implementation // HasSongs implementation
virtual ProxySongList proxySongList() OVERRIDE; virtual ProxySongList proxySongList() OVERRIDE;
@@ -82,6 +83,7 @@ private:
bool m_local_browser; bool m_local_browser;
size_t m_scroll_beginning; size_t m_scroll_beginning;
std::string m_current_directory; std::string m_current_directory;
RegexFilter<MPD::Item> m_search_predicate;
}; };
extern Browser *myBrowser; extern Browser *myBrowser;

View File

@@ -29,6 +29,41 @@
#include "utility/string.h" #include "utility/string.h"
#include "utility/wide_string.h" #include "utility/wide_string.h"
template <typename Iterator, typename PredicateT>
Iterator wrappedSearch(Iterator begin, Iterator current, Iterator end,
const PredicateT &pred, bool wrap)
{
++current;
auto it = std::find_if(current, end, pred);
if (it == end && wrap)
{
it = std::find_if(begin, current, pred);
if (it == current)
it = end;
}
return it;
}
template <typename ItemT, typename PredicateT>
void searchForward(NC::Menu<ItemT> &m, const PredicateT &pred, bool wrap)
{
if (!pred.defined())
return;
auto it = wrappedSearch(m.begin(), m.currentI(), m.end(), pred, wrap);
if (it != m.end())
m.highlight(it-m.begin());
}
template <typename ItemT, typename PredicateT>
void searchBackward(NC::Menu<ItemT> &m, const PredicateT &pred, bool wrap)
{
if (!pred.defined())
return;
auto it = wrappedSearch(m.rbegin(), m.currentRI(), m.rend(), pred, wrap);
if (it != m.rend())
m.highlight(m.size()-1-(it-m.rbegin()));
}
inline HasColumns *hasColumns(BaseScreen *screen) inline HasColumns *hasColumns(BaseScreen *screen)
{ {
return dynamic_cast<HasColumns *>(screen); return dynamic_cast<HasColumns *>(screen);

View File

@@ -37,9 +37,9 @@ struct Filterable
struct Searchable struct Searchable
{ {
virtual bool allowsSearching() = 0; virtual bool allowsSearching() = 0;
virtual bool search(const std::string &constraint) = 0; virtual bool setSearchConstraint(const std::string &constraint) = 0;
virtual void nextFound(bool wrap) = 0; virtual void findForward(bool wrap) = 0;
virtual void prevFound(bool wrap) = 0; virtual void findBackward(bool wrap) = 0;
}; };
struct HasSongs struct HasSongs

View File

@@ -29,10 +29,9 @@
#include "display.h" #include "display.h"
#include "helpers.h" #include "helpers.h"
#include "global.h" #include "global.h"
#include "media_library.h"
#include "mpdpp.h" #include "mpdpp.h"
#include "playlist.h" #include "playlist.h"
#include "regex_filter.h" #include "media_library.h"
#include "status.h" #include "status.h"
#include "statusbar.h" #include "statusbar.h"
#include "utility/comparators.h" #include "utility/comparators.h"
@@ -256,7 +255,6 @@ void MediaLibrary::update()
{ {
if (Albums.reallyEmpty() || m_albums_update_request) if (Albums.reallyEmpty() || m_albums_update_request)
{ {
Albums.clearSearchResults();
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)
@@ -299,7 +297,6 @@ void MediaLibrary::update()
{ {
if (Tags.reallyEmpty() || m_tags_update_request) if (Tags.reallyEmpty() || m_tags_update_request)
{ {
Tags.clearSearchResults();
m_tags_update_request = false; m_tags_update_request = false;
std::map<std::string, time_t> tags; std::map<std::string, time_t> tags;
if (Config.media_library_sort_by_mtime) if (Config.media_library_sort_by_mtime)
@@ -346,7 +343,6 @@ void MediaLibrary::update()
&& ((Albums.reallyEmpty() && Global::Timer - m_timer > m_fetching_delay) || m_albums_update_request) && ((Albums.reallyEmpty() && Global::Timer - m_timer > m_fetching_delay) || m_albums_update_request)
) )
{ {
Albums.clearSearchResults();
m_albums_update_request = false; m_albums_update_request = false;
auto &primary_tag = Tags.current().value().tag(); auto &primary_tag = Tags.current().value().tag();
Mpd.StartSearch(true); Mpd.StartSearch(true);
@@ -395,7 +391,6 @@ void MediaLibrary::update()
&& ((Songs.reallyEmpty() && Global::Timer - m_timer > m_fetching_delay) || m_songs_update_request) && ((Songs.reallyEmpty() && Global::Timer - m_timer > m_fetching_delay) || m_songs_update_request)
) )
{ {
Songs.clearSearchResults();
m_songs_update_request = false; m_songs_update_request = false;
auto &album = Albums.current().value(); auto &album = Albums.current().value();
Mpd.StartSearch(true); Mpd.StartSearch(true);
@@ -640,66 +635,63 @@ bool MediaLibrary::allowsSearching()
return true; return true;
} }
bool MediaLibrary::search(const std::string &constraint) bool MediaLibrary::setSearchConstraint(const std::string &constraint)
{ {
if (constraint.empty()) if (constraint.empty())
{ {
if (isActiveWindow(Tags)) if (isActiveWindow(Tags))
Tags.clearSearchResults(); m_tags_search_predicate.clear();
else if (isActiveWindow(Albums)) else if (isActiveWindow(Albums))
Albums.clearSearchResults(); m_albums_search_predicate.clear();
else if (isActiveWindow(Songs)) else if (isActiveWindow(Songs))
Songs.clearSearchResults(); m_songs_search_predicate.clear();
return false; return false;
} }
try else
{ {
bool result = false;
if (isActiveWindow(Tags)) if (isActiveWindow(Tags))
{ {
auto rx = RegexFilter<PrimaryTag>( m_tags_search_predicate = RegexFilter<PrimaryTag>(
boost::regex(constraint, Config.regex_type), TagEntryMatcher); boost::regex(constraint, Config.regex_type),
result = Tags.search(Tags.begin(), Tags.end(), rx); TagEntryMatcher
);
} }
else if (isActiveWindow(Albums)) else if (isActiveWindow(Albums))
{ {
auto fun = boost::bind(AlbumEntryMatcher, _1, _2, false); m_albums_search_predicate = RegexItemFilter<AlbumEntry>(
auto rx = RegexItemFilter<AlbumEntry>( boost::regex(constraint, Config.regex_type),
boost::regex(constraint, Config.regex_type), fun); boost::bind(AlbumEntryMatcher, _1, _2, false)
result = Albums.search(Albums.begin(), Albums.end(), rx); );
} }
else if (isActiveWindow(Songs)) else if (isActiveWindow(Songs))
{ {
auto rx = RegexFilter<MPD::Song>( m_songs_search_predicate = RegexFilter<MPD::Song>(
boost::regex(constraint, Config.regex_type), SongEntryMatcher); boost::regex(constraint, Config.regex_type),
result = Songs.search(Songs.begin(), Songs.end(), rx); SongEntryMatcher
);
} }
return result; return true;
}
catch (boost::bad_expression &)
{
return false;
} }
} }
void MediaLibrary::nextFound(bool wrap) void MediaLibrary::findForward(bool wrap)
{ {
if (isActiveWindow(Tags)) if (isActiveWindow(Tags))
Tags.nextFound(wrap); searchForward(Tags, m_tags_search_predicate, wrap);
else if (isActiveWindow(Albums)) else if (isActiveWindow(Albums))
Albums.nextFound(wrap); searchForward(Albums, m_albums_search_predicate, wrap);
else if (isActiveWindow(Songs)) else if (isActiveWindow(Songs))
Songs.nextFound(wrap); searchForward(Songs, m_songs_search_predicate, wrap);
} }
void MediaLibrary::prevFound(bool wrap) void MediaLibrary::findBackward(bool wrap)
{ {
if (isActiveWindow(Tags)) if (isActiveWindow(Tags))
Tags.prevFound(wrap); searchBackward(Tags, m_tags_search_predicate, wrap);
else if (isActiveWindow(Albums)) else if (isActiveWindow(Albums))
Albums.prevFound(wrap); searchBackward(Albums, m_albums_search_predicate, wrap);
else if (isActiveWindow(Songs)) else if (isActiveWindow(Songs))
Songs.prevFound(wrap); searchBackward(Songs, m_songs_search_predicate, wrap);
} }
/***********************************************************************/ /***********************************************************************/

View File

@@ -24,6 +24,7 @@
#include <boost/date_time/posix_time/posix_time_types.hpp> #include <boost/date_time/posix_time/posix_time_types.hpp>
#include "interfaces.h" #include "interfaces.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 *>, Filterable, HasColumns, HasSongs, Searchable, Tabbable
@@ -54,9 +55,9 @@ struct MediaLibrary: Screen<NC::Window *>, Filterable, HasColumns, HasSongs, Sea
// Searchable implementation // Searchable implementation
virtual bool allowsSearching() OVERRIDE; virtual bool allowsSearching() OVERRIDE;
virtual bool search(const std::string &constraint) OVERRIDE; virtual bool setSearchConstraint(const std::string &constraint) OVERRIDE;
virtual void nextFound(bool wrap) OVERRIDE; virtual void findForward(bool wrap) OVERRIDE;
virtual void prevFound(bool wrap) OVERRIDE; virtual void findBackward(bool wrap) OVERRIDE;
// HasSongs implementation // HasSongs implementation
virtual ProxySongList proxySongList() OVERRIDE; virtual ProxySongList proxySongList() OVERRIDE;
@@ -153,6 +154,11 @@ private:
const int m_window_timeout; const int m_window_timeout;
const boost::posix_time::time_duration m_fetching_delay; const boost::posix_time::time_duration m_fetching_delay;
RegexFilter<PrimaryTag> m_tags_search_predicate;
RegexItemFilter<AlbumEntry> m_albums_search_predicate;
RegexFilter<MPD::Song> m_songs_search_predicate;
}; };
extern MediaLibrary *myLibrary; extern MediaLibrary *myLibrary;

View File

@@ -176,26 +176,11 @@ public:
void applyCurrentFilter(ConstIterator first, ConstIterator last); void applyCurrentFilter(ConstIterator first, ConstIterator last);
bool search(ConstIterator first, ConstIterator last, const FilterFunction &f);
/// Clears filter results /// Clears filter results
void clearFilterResults(); void clearFilterResults();
void clearFilter(); void clearFilter();
/// Clears search results
void clearSearchResults();
/// Moves current position in the list to the next found one
/// @param wrap if true, this function will go to the first
/// found pos after the last one, otherwise it'll do nothing.
void nextFound(bool wrap);
/// Moves current position in the list to the previous found one
/// @param wrap if true, this function will go to the last
/// found pos after the first one, otherwise it'll do nothing.
void prevFound(bool wrap);
/// @return const reference to currently used filter function /// @return const reference to currently used filter function
const FilterFunction &getFilter() { return m_filter; } const FilterFunction &getFilter() { return m_filter; }
@@ -306,8 +291,13 @@ public:
Iterator currentI() { return Iterator(m_options_ptr->begin() + m_highlight); } Iterator currentI() { return Iterator(m_options_ptr->begin() + m_highlight); }
ConstIterator currentI() const { return ConstIterator(m_options_ptr->begin() + m_highlight); } ConstIterator currentI() const { return ConstIterator(m_options_ptr->begin() + m_highlight); }
ReverseIterator currentRI() { return ReverseIterator(++currentI()); }
ConstReverseIterator currentRI() const { return ReverseIterator(++currentI()); }
ValueIterator currentVI() { return ValueIterator(m_options_ptr->begin() + m_highlight); } ValueIterator currentVI() { return ValueIterator(m_options_ptr->begin() + m_highlight); }
ConstValueIterator currentVI() const { return ConstValueIterator(m_options_ptr->begin() + m_highlight); } ConstValueIterator currentVI() const { return ConstValueIterator(m_options_ptr->begin() + m_highlight); }
ReverseValueIterator currentRVI() { return ReverseValueIterator(++currentVI()); }
ConstReverseValueIterator currentRVI() const { return ConstReverseValueIterator(++currentVI()); }
Iterator begin() { return Iterator(m_options_ptr->begin()); } Iterator begin() { return Iterator(m_options_ptr->begin()); }
ConstIterator begin() const { return ConstIterator(m_options_ptr->begin()); } ConstIterator begin() const { return ConstIterator(m_options_ptr->begin()); }
@@ -360,7 +350,6 @@ private:
std::vector<ItemProxy> *m_options_ptr; std::vector<ItemProxy> *m_options_ptr;
std::vector<ItemProxy> m_options; std::vector<ItemProxy> m_options;
std::vector<ItemProxy> m_filtered_options; std::vector<ItemProxy> m_filtered_options;
std::set<size_t> m_found_positions;
size_t m_beginning; size_t m_beginning;
size_t m_highlight; size_t m_highlight;
@@ -403,7 +392,6 @@ Menu<ItemT>::Menu(const Menu &rhs)
, m_item_displayer(rhs.m_item_displayer) , m_item_displayer(rhs.m_item_displayer)
, m_filter(rhs.m_filter) , m_filter(rhs.m_filter)
, m_searcher(rhs.m_searcher) , m_searcher(rhs.m_searcher)
, m_found_positions(rhs.m_found_positions)
, 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)
@@ -430,7 +418,6 @@ Menu<ItemT>::Menu(Menu &&rhs)
, m_searcher(rhs.m_searcher) , m_searcher(rhs.m_searcher)
, m_options(std::move(rhs.m_options)) , m_options(std::move(rhs.m_options))
, m_filtered_options(std::move(rhs.m_filtered_options)) , m_filtered_options(std::move(rhs.m_filtered_options))
, m_found_positions(std::move(rhs.m_found_positions))
, 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)
@@ -456,7 +443,6 @@ Menu<ItemT> &Menu<ItemT>::operator=(Menu rhs)
std::swap(m_searcher, rhs.m_searcher); std::swap(m_searcher, rhs.m_searcher);
std::swap(m_options, rhs.m_options); std::swap(m_options, rhs.m_options);
std::swap(m_filtered_options, rhs.m_filtered_options); std::swap(m_filtered_options, rhs.m_filtered_options);
std::swap(m_found_positions, rhs.m_found_positions);
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);
@@ -705,7 +691,6 @@ void Menu<ItemT>::clear()
{ {
clearFilterResults(); clearFilterResults();
m_options.clear(); m_options.clear();
m_found_positions.clear();
m_options_ptr = &m_options; m_options_ptr = &m_options;
} }
@@ -764,52 +749,6 @@ void Menu<ItemT>::clearFilter()
m_filter = 0; m_filter = 0;
} }
template <typename ItemT>
bool Menu<ItemT>::search(ConstIterator first, ConstIterator last, const FilterFunction &f)
{
m_found_positions.clear();
m_searcher = f;
for (auto it = first; it != last; ++it)
{
if (m_searcher(*it))
{
size_t pos = it-begin();
m_found_positions.insert(pos);
}
}
return !m_found_positions.empty();
}
template <typename ItemT>
void Menu<ItemT>::clearSearchResults()
{
m_found_positions.clear();
}
template <typename ItemT>
void Menu<ItemT>::nextFound(bool wrap)
{
if (m_found_positions.empty())
return;
auto next = m_found_positions.upper_bound(m_highlight);
if (next != m_found_positions.end())
highlight(*next);
else if (wrap)
highlight(*m_found_positions.begin());
}
template <typename ItemT>
void Menu<ItemT>::prevFound(bool wrap)
{
if (m_found_positions.empty())
return;
auto prev = m_found_positions.lower_bound(m_highlight);
if (prev != m_found_positions.begin())
highlight(*--prev);
else if (wrap)
highlight(*m_found_positions.rbegin());
}
} }
#endif // NCMPCPP_MENU_H #endif // NCMPCPP_MENU_H

View File

@@ -27,7 +27,6 @@
#include "helpers.h" #include "helpers.h"
#include "menu.h" #include "menu.h"
#include "playlist.h" #include "playlist.h"
#include "regex_filter.h"
#include "screen_switcher.h" #include "screen_switcher.h"
#include "song.h" #include "song.h"
#include "status.h" #include "status.h"
@@ -184,33 +183,30 @@ bool Playlist::allowsSearching()
return true; return true;
} }
bool Playlist::search(const std::string &constraint) bool Playlist::setSearchConstraint(const std::string &constraint)
{ {
if (constraint.empty()) if (constraint.empty())
{ {
w.clearSearchResults(); m_search_predicate.clear();
return false; return false;
} }
try else
{ {
auto rx = RegexFilter<MPD::Song>( m_search_predicate = RegexFilter<MPD::Song>(
boost::regex(constraint, Config.regex_type), playlistEntryMatcher); boost::regex(constraint, Config.regex_type), playlistEntryMatcher
return w.search(w.begin(), w.end(), rx); );
} return true;
catch (boost::bad_expression &)
{
return false;
} }
} }
void Playlist::nextFound(bool wrap) void Playlist::findForward(bool wrap)
{ {
w.nextFound(wrap); searchForward(w, m_search_predicate, wrap);
} }
void Playlist::prevFound(bool wrap) void Playlist::findBackward(bool wrap)
{ {
w.prevFound(wrap); searchBackward(w, m_search_predicate, wrap);
} }
/***********************************************************************/ /***********************************************************************/

View File

@@ -25,6 +25,7 @@
#include <unordered_map> #include <unordered_map>
#include "interfaces.h" #include "interfaces.h"
#include "regex_filter.h"
#include "screen.h" #include "screen.h"
#include "song.h" #include "song.h"
@@ -54,9 +55,9 @@ struct Playlist: Screen<NC::Menu<MPD::Song>>, Filterable, HasSongs, Searchable,
// Searchable implementation // Searchable implementation
virtual bool allowsSearching(); virtual bool allowsSearching();
virtual bool search(const std::string &constraint) OVERRIDE; virtual bool setSearchConstraint(const std::string &constraint) OVERRIDE;
virtual void nextFound(bool wrap) OVERRIDE; virtual void findForward(bool wrap) OVERRIDE;
virtual void prevFound(bool wrap) OVERRIDE; virtual void findBackward(bool wrap) OVERRIDE;
// HasSongs implementation // HasSongs implementation
virtual ProxySongList proxySongList() OVERRIDE; virtual ProxySongList proxySongList() OVERRIDE;
@@ -100,6 +101,8 @@ private:
bool m_reload_total_length; bool m_reload_total_length;
bool m_reload_remaining; bool m_reload_remaining;
RegexFilter<MPD::Song> m_search_predicate;
}; };
extern Playlist *myPlaylist; extern Playlist *myPlaylist;

View File

@@ -30,7 +30,6 @@
#include "playlist.h" #include "playlist.h"
#include "playlist_editor.h" #include "playlist_editor.h"
#include "mpdpp.h" #include "mpdpp.h"
#include "regex_filter.h"
#include "status.h" #include "status.h"
#include "statusbar.h" #include "statusbar.h"
#include "tag_editor.h" #include "tag_editor.h"
@@ -138,7 +137,6 @@ void PlaylistEditor::update()
if (Playlists.reallyEmpty() || m_playlists_update_requested) if (Playlists.reallyEmpty() || m_playlists_update_requested)
{ {
m_playlists_update_requested = false; m_playlists_update_requested = false;
Playlists.clearSearchResults();
withUnfilteredMenuReapplyFilter(Playlists, [this]() { 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)
@@ -164,7 +162,6 @@ void PlaylistEditor::update()
Content.clear(); Content.clear();
else else
{ {
Content.clearSearchResults();
withUnfilteredMenuReapplyFilter(Content, [this]() { 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;
@@ -403,53 +400,50 @@ bool PlaylistEditor::allowsSearching()
return true; return true;
} }
bool PlaylistEditor::search(const std::string &constraint) bool PlaylistEditor::setSearchConstraint(const std::string &constraint)
{ {
if (constraint.empty()) if (constraint.empty())
{ {
if (isActiveWindow(Playlists)) if (isActiveWindow(Playlists))
Playlists.clearSearchResults(); m_playlists_search_predicate.clear();
else if (isActiveWindow(Content)) else if (isActiveWindow(Content))
Content.clearSearchResults(); m_content_search_predicate.clear();
return false; return false;
} }
try else
{ {
bool result = false;
if (isActiveWindow(Playlists)) if (isActiveWindow(Playlists))
{ {
auto rx = RegexFilter<MPD::Playlist>( m_playlists_search_predicate = RegexFilter<MPD::Playlist>(
boost::regex(constraint, Config.regex_type), PlaylistEntryMatcher); boost::regex(constraint, Config.regex_type),
result = Playlists.search(Playlists.begin(), Playlists.end(), rx); PlaylistEntryMatcher
);
} }
else if (isActiveWindow(Content)) else if (isActiveWindow(Content))
{ {
auto rx = RegexFilter<MPD::Song>( m_content_search_predicate = RegexFilter<MPD::Song>(
boost::regex(constraint, Config.regex_type), SongEntryMatcher); boost::regex(constraint, Config.regex_type),
result = Content.search(Content.begin(), Content.end(), rx); SongEntryMatcher
);
} }
return result; return true;
}
catch (boost::bad_expression &)
{
return false;
} }
} }
void PlaylistEditor::nextFound(bool wrap) void PlaylistEditor::findForward(bool wrap)
{ {
if (isActiveWindow(Playlists)) if (isActiveWindow(Playlists))
Playlists.nextFound(wrap); searchForward(Playlists, m_playlists_search_predicate, wrap);
else if (isActiveWindow(Content)) else if (isActiveWindow(Content))
Content.nextFound(wrap); searchForward(Content, m_content_search_predicate, wrap);
} }
void PlaylistEditor::prevFound(bool wrap) void PlaylistEditor::findBackward(bool wrap)
{ {
if (isActiveWindow(Playlists)) if (isActiveWindow(Playlists))
Playlists.prevFound(wrap); searchBackward(Playlists, m_playlists_search_predicate, wrap);
else if (isActiveWindow(Content)) else if (isActiveWindow(Content))
Content.prevFound(wrap); searchBackward(Content, m_content_search_predicate, wrap);
} }
/***********************************************************************/ /***********************************************************************/

View File

@@ -24,6 +24,7 @@
#include <boost/date_time/posix_time/posix_time_types.hpp> #include <boost/date_time/posix_time/posix_time_types.hpp>
#include "interfaces.h" #include "interfaces.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 *>, Filterable, HasColumns, HasSongs, Searchable, Tabbable
@@ -54,9 +55,9 @@ struct PlaylistEditor: Screen<NC::Window *>, Filterable, HasColumns, HasSongs, S
// Searchable implementation // Searchable implementation
virtual bool allowsSearching() OVERRIDE; virtual bool allowsSearching() OVERRIDE;
virtual bool search(const std::string &constraint) OVERRIDE; virtual bool setSearchConstraint(const std::string &constraint) OVERRIDE;
virtual void nextFound(bool wrap) OVERRIDE; virtual void findForward(bool wrap) OVERRIDE;
virtual void prevFound(bool wrap) OVERRIDE; virtual void findBackward(bool wrap) OVERRIDE;
// HasSongs implementation // HasSongs implementation
virtual ProxySongList proxySongList() OVERRIDE; virtual ProxySongList proxySongList() OVERRIDE;
@@ -98,6 +99,9 @@ private:
const int m_window_timeout; const int m_window_timeout;
const boost::posix_time::time_duration m_fetching_delay; const boost::posix_time::time_duration m_fetching_delay;
RegexFilter<MPD::Playlist> m_playlists_search_predicate;
RegexFilter<MPD::Song> m_content_search_predicate;
}; };
extern PlaylistEditor *myPlaylistEditor; extern PlaylistEditor *myPlaylistEditor;

View File

@@ -22,21 +22,35 @@
#define NCMPCPP_REGEX_FILTER_H #define NCMPCPP_REGEX_FILTER_H
#include <boost/regex.hpp> #include <boost/regex.hpp>
#include <cassert>
#include "menu.h" #include "menu.h"
template <typename T> struct RegexFilter template <typename T>
struct RegexFilter
{ {
typedef NC::Menu<T> MenuT; typedef NC::Menu<T> MenuT;
typedef typename NC::Menu<T>::Item Item; typedef typename NC::Menu<T>::Item Item;
typedef std::function<bool(const boost::regex &, const T &)> FilterFunction; typedef std::function<bool(const boost::regex &, const T &)> FilterFunction;
RegexFilter() { }
RegexFilter(boost::regex rx, FilterFunction filter) RegexFilter(boost::regex rx, FilterFunction filter)
: m_rx(rx), m_filter(filter) { } : m_rx(std::move(rx)), m_filter(std::move(filter)) { }
bool operator()(const Item &item) { void clear()
{
m_filter = nullptr;
}
bool operator()(const Item &item) const {
assert(defined());
return m_filter(m_rx, item.value()); return m_filter(m_rx, item.value());
} }
bool defined() const
{
return m_filter.operator bool();
}
static std::string currentFilter(MenuT &menu) static std::string currentFilter(MenuT &menu)
{ {
std::string filter; std::string filter;
@@ -57,13 +71,24 @@ template <typename T> struct RegexItemFilter
typedef typename NC::Menu<T>::Item Item; typedef typename NC::Menu<T>::Item Item;
typedef std::function<bool(const boost::regex &, const Item &)> FilterFunction; typedef std::function<bool(const boost::regex &, const Item &)> FilterFunction;
RegexItemFilter() { }
RegexItemFilter(boost::regex rx, FilterFunction filter) RegexItemFilter(boost::regex rx, FilterFunction filter)
: m_rx(rx), m_filter(filter) { } : m_rx(std::move(rx)), m_filter(std::move(filter)) { }
void clear()
{
m_filter = nullptr;
}
bool operator()(const Item &item) { bool operator()(const Item &item) {
return m_filter(m_rx, item); return m_filter(m_rx, item);
} }
bool defined() const
{
return m_filter.operator bool();
}
static std::string currentFilter(MenuT &menu) static std::string currentFilter(MenuT &menu)
{ {
std::string filter; std::string filter;

View File

@@ -26,7 +26,6 @@
#include "global.h" #include "global.h"
#include "helpers.h" #include "helpers.h"
#include "playlist.h" #include "playlist.h"
#include "regex_filter.h"
#include "search_engine.h" #include "search_engine.h"
#include "settings.h" #include "settings.h"
#include "status.h" #include "status.h"
@@ -293,34 +292,31 @@ bool SearchEngine::allowsSearching()
return w.back().value().isSong(); return w.back().value().isSong();
} }
bool SearchEngine::search(const std::string &constraint) bool SearchEngine::setSearchConstraint(const std::string &constraint)
{ {
if (constraint.empty()) if (constraint.empty())
{ {
w.clearSearchResults(); m_search_predicate.clear();
return false; return false;
} }
try else
{ {
auto fun = boost::bind(SEItemEntryMatcher, _1, _2, false); m_search_predicate = RegexItemFilter<SEItem>(
auto rx = RegexItemFilter<SEItem>( boost::regex(constraint, Config.regex_type),
boost::regex(constraint, Config.regex_type), fun); boost::bind(SEItemEntryMatcher, _1, _2, false)
return w.search(w.begin(), w.end(), rx); );
} return true;
catch (boost::bad_expression &)
{
return false;
} }
} }
void SearchEngine::nextFound(bool wrap) void SearchEngine::findForward(bool wrap)
{ {
w.nextFound(wrap); searchForward(w, m_search_predicate, wrap);
} }
void SearchEngine::prevFound(bool wrap) void SearchEngine::findBackward(bool wrap)
{ {
w.prevFound(wrap); searchBackward(w, m_search_predicate, wrap);
} }
/***********************************************************************/ /***********************************************************************/

View File

@@ -25,9 +25,56 @@
#include "interfaces.h" #include "interfaces.h"
#include "mpdpp.h" #include "mpdpp.h"
#include "regex_filter.h"
#include "screen.h" #include "screen.h"
struct SearchEngine: Screen<NC::Menu<struct SEItem>>, Filterable, HasSongs, Searchable, Tabbable struct SEItem
{
SEItem() : m_is_song(false), m_buffer(0) { }
SEItem(NC::Buffer *buf) : m_is_song(false), m_buffer(buf) { }
SEItem(const MPD::Song &s) : m_is_song(true), m_song(s) { }
SEItem(const SEItem &ei) { *this = ei; }
~SEItem() {
if (!m_is_song)
delete m_buffer;
}
NC::Buffer &mkBuffer() {
assert(!m_is_song);
delete m_buffer;
m_buffer = new NC::Buffer();
return *m_buffer;
}
bool isSong() const { return m_is_song; }
NC::Buffer &buffer() { assert(!m_is_song && m_buffer); return *m_buffer; }
MPD::Song &song() { assert(m_is_song); return m_song; }
const NC::Buffer &buffer() const { assert(!m_is_song && m_buffer); return *m_buffer; }
const MPD::Song &song() const { assert(m_is_song); return m_song; }
SEItem &operator=(const SEItem &se) {
if (this == &se)
return *this;
m_is_song = se.m_is_song;
if (se.m_is_song)
m_song = se.m_song;
else if (se.m_buffer)
m_buffer = new NC::Buffer(*se.m_buffer);
else
m_buffer = 0;
return *this;
}
private:
bool m_is_song;
NC::Buffer *m_buffer;
MPD::Song m_song;
};
struct SearchEngine: Screen<NC::Menu<SEItem>>, Filterable, HasSongs, Searchable, Tabbable
{ {
SearchEngine(); SearchEngine();
@@ -53,9 +100,9 @@ struct SearchEngine: Screen<NC::Menu<struct SEItem>>, Filterable, HasSongs, Sear
// Searchable implementation // Searchable implementation
virtual bool allowsSearching() OVERRIDE; virtual bool allowsSearching() OVERRIDE;
virtual bool search(const std::string &constraint) OVERRIDE; virtual bool setSearchConstraint(const std::string &constraint) OVERRIDE;
virtual void nextFound(bool wrap) OVERRIDE; virtual void findForward(bool wrap) OVERRIDE;
virtual void prevFound(bool wrap) OVERRIDE; virtual void findBackward(bool wrap) OVERRIDE;
// HasSongs implementation // HasSongs implementation
virtual ProxySongList proxySongList() OVERRIDE; virtual ProxySongList proxySongList() OVERRIDE;
@@ -77,6 +124,8 @@ protected:
private: private:
void Prepare(); void Prepare();
void Search(); void Search();
RegexItemFilter<SEItem> m_search_predicate;
const char **SearchMode; const char **SearchMode;
@@ -89,52 +138,6 @@ private:
static bool MatchToPattern; static bool MatchToPattern;
}; };
struct SEItem
{
SEItem() : m_is_song(false), m_buffer(0) { }
SEItem(NC::Buffer *buf) : m_is_song(false), m_buffer(buf) { }
SEItem(const MPD::Song &s) : m_is_song(true), m_song(s) { }
SEItem(const SEItem &ei) { *this = ei; }
~SEItem() {
if (!m_is_song)
delete m_buffer;
}
NC::Buffer &mkBuffer() {
assert(!m_is_song);
delete m_buffer;
m_buffer = new NC::Buffer();
return *m_buffer;
}
bool isSong() const { return m_is_song; }
NC::Buffer &buffer() { assert(!m_is_song && m_buffer); return *m_buffer; }
MPD::Song &song() { assert(m_is_song); return m_song; }
const NC::Buffer &buffer() const { assert(!m_is_song && m_buffer); return *m_buffer; }
const MPD::Song &song() const { assert(m_is_song); return m_song; }
SEItem &operator=(const SEItem &se) {
if (this == &se)
return *this;
m_is_song = se.m_is_song;
if (se.m_is_song)
m_song = se.m_song;
else if (se.m_buffer)
m_buffer = new NC::Buffer(*se.m_buffer);
else
m_buffer = 0;
return *this;
}
private:
bool m_is_song;
NC::Buffer *m_buffer;
MPD::Song m_song;
};
extern SearchEngine *mySearcher; extern SearchEngine *mySearcher;
#endif // NCMPCPP_SEARCH_ENGINE_H #endif // NCMPCPP_SEARCH_ENGINE_H

View File

@@ -387,7 +387,6 @@ int Status::State::volume()
void Status::Changes::playlist(unsigned previous_version) void Status::Changes::playlist(unsigned previous_version)
{ {
myPlaylist->main().clearSearchResults();
withUnfilteredMenuReapplyFilter(myPlaylist->main(), [previous_version]() { withUnfilteredMenuReapplyFilter(myPlaylist->main(), [previous_version]() {
if (m_playlist_length < myPlaylist->main().size()) if (m_playlist_length < myPlaylist->main().size())
{ {

View File

@@ -775,54 +775,50 @@ bool TagEditor::allowsSearching()
return w == Dirs || w == Tags; return w == Dirs || w == Tags;
} }
bool TagEditor::search(const std::string &constraint) bool TagEditor::setSearchConstraint(const std::string &constraint)
{ {
if (constraint.empty()) if (constraint.empty())
{ {
if (w == Dirs) if (w == Dirs)
Dirs->clearSearchResults(); m_directories_search_predicate.clear();
else if (w == Tags) else if (w == Tags)
Tags->clearSearchResults(); m_songs_search_predicate.clear();
return false; return false;
} }
try else
{ {
bool result = false;
if (w == Dirs) if (w == Dirs)
{ {
auto fun = boost::bind(DirEntryMatcher, _1, _2, false); m_directories_search_predicate = RegexFilter<std::pair<std::string, std::string>>(
auto rx = RegexFilter< std::pair<std::string, std::string> >( boost::regex(constraint, Config.regex_type),
boost::regex(constraint, Config.regex_type), fun); boost::bind(DirEntryMatcher, _1, _2, false)
result = Dirs->search(Dirs->begin(), Dirs->end(), rx); );
} }
else if (w == Tags) else if (w == Tags)
{ {
auto rx = RegexFilter<MPD::MutableSong>( m_songs_search_predicate = RegexFilter<MPD::MutableSong>(
boost::regex(constraint, Config.regex_type), SongEntryMatcher); boost::regex(constraint, Config.regex_type),
result = Tags->search(Tags->begin(), Tags->end(), rx); SongEntryMatcher
);
} }
return result; return true;
}
catch (boost::bad_expression &)
{
return false;
} }
} }
void TagEditor::nextFound(bool wrap) void TagEditor::findForward(bool wrap)
{ {
if (w == Dirs) if (w == Dirs)
Dirs->nextFound(wrap); searchForward(*Dirs, m_directories_search_predicate, wrap);
else if (w == Tags) else if (w == Tags)
Tags->nextFound(wrap); searchForward(*Tags, m_songs_search_predicate, wrap);
} }
void TagEditor::prevFound(bool wrap) void TagEditor::findBackward(bool wrap)
{ {
if (w == Dirs) if (w == Dirs)
Dirs->prevFound(wrap); searchBackward(*Dirs, m_directories_search_predicate, wrap);
else if (w == Tags) else if (w == Tags)
Tags->prevFound(wrap); searchBackward(*Tags, m_songs_search_predicate, wrap);
} }
/***********************************************************************/ /***********************************************************************/

View File

@@ -58,9 +58,9 @@ struct TagEditor: Screen<NC::Window *>, Filterable, HasColumns, HasSongs, Search
// Searchable implementation // Searchable implementation
virtual bool allowsSearching() OVERRIDE; virtual bool allowsSearching() OVERRIDE;
virtual bool search(const std::string &constraint) OVERRIDE; virtual bool setSearchConstraint(const std::string &constraint) OVERRIDE;
virtual void nextFound(bool wrap) OVERRIDE; virtual void findForward(bool wrap) OVERRIDE;
virtual void prevFound(bool wrap) OVERRIDE; virtual void findBackward(bool wrap) OVERRIDE;
// HasSongs implementation // HasSongs implementation
virtual ProxySongList proxySongList() OVERRIDE; virtual ProxySongList proxySongList() OVERRIDE;
@@ -100,6 +100,9 @@ private:
std::string itsBrowsedDir; std::string itsBrowsedDir;
std::string itsHighlightedDir; std::string itsHighlightedDir;
RegexFilter<std::pair<std::string, std::string>> m_directories_search_predicate;
RegexFilter<MPD::MutableSong> m_songs_search_predicate;
}; };
extern TagEditor *myTagEditor; extern TagEditor *myTagEditor;