diff --git a/src/actions.cpp b/src/actions.cpp index b021898d..e8bba2b0 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -70,10 +70,25 @@ using Global::myScreen; -namespace {// +namespace { 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< Actions::BaseAction *, static_cast(Actions::Type::_numberOfActions) > AvailableActions; @@ -1942,7 +1957,8 @@ bool NextFoundItem::canBeRun() const void NextFoundItem::run() { Searchable *w = dynamic_cast(myScreen); - w->nextFound(Config.wrapped_search); + assert(w != nullptr); + w->findForward(Config.wrapped_search); listsChangeFinisher(); } @@ -1954,7 +1970,8 @@ bool PreviousFoundItem::canBeRun() const void PreviousFoundItem::run() { Searchable *w = dynamic_cast(myScreen); - w->prevFound(Config.wrapped_search); + assert(w != nullptr); + w->findBackward(Config.wrapped_search); listsChangeFinisher(); } @@ -2798,28 +2815,38 @@ void findItem(const Find direction) using Global::wFooter; Searchable *w = dynamic_cast(myScreen); - assert(w); + assert(w != nullptr); assert(w->allowsSearching()); - std::string token; + std::string constraint; { Statusbar::ScopedLock slock; - Statusbar::put() << "Find " << (direction == Find::Forward ? "forward" : "backward") << ": "; - token = wFooter->prompt(); + Statusbar::put() << "Find " << findToString(direction) << ": "; + constraint = wFooter->prompt(); } - Statusbar::print("Searching..."); - bool success = w->search(token); - - if (success) - Statusbar::print("Searching finished"); - else - Statusbar::printf("Unable to find \"%1%\"", token); - - if (direction == Find::Forward) - w->nextFound(Config.wrapped_search); - else - w->prevFound(Config.wrapped_search); + try + { + bool success = w->setSearchConstraint(constraint); + if (success) + Statusbar::printf("Using constraint \"%1%\"", constraint); + else + Statusbar::printf("Constraint unset"); + } + catch (boost::bad_expression &e) + { + Statusbar::printf("%1%", e.what()); + } + + switch (direction) + { + case Find::Forward: + w->findForward(Config.wrapped_search); + break; + case Find::Backward: + w->findBackward(Config.wrapped_search); + break; + } } void listsChangeFinisher() diff --git a/src/browser.cpp b/src/browser.cpp index bfc82a0f..10e936cf 100644 --- a/src/browser.cpp +++ b/src/browser.cpp @@ -32,7 +32,6 @@ #include "global.h" #include "helpers.h" #include "playlist.h" -#include "regex_filter.h" #include "screen_switcher.h" #include "settings.h" #include "status.h" @@ -285,34 +284,31 @@ bool Browser::allowsSearching() return true; } -bool Browser::search(const std::string &constraint) +bool Browser::setSearchConstraint(const std::string &constraint) { if (constraint.empty()) { - w.clearSearchResults(); + m_search_predicate.clear(); return false; } - try + else { - auto fun = boost::bind(browserEntryMatcher, _1, _2, false); - auto rx = RegexFilter( - boost::regex(constraint, Config.regex_type), fun); - return w.search(w.begin(), w.end(), rx); - } - catch (boost::bad_expression &) - { - return false; + m_search_predicate = RegexFilter( + boost::regex(constraint, Config.regex_type), + boost::bind(browserEntryMatcher, _1, _2, false) + ); + return true; } } -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); } /***********************************************************************/ diff --git a/src/browser.h b/src/browser.h index b76698de..3bb70634 100644 --- a/src/browser.h +++ b/src/browser.h @@ -23,6 +23,7 @@ #include "interfaces.h" #include "mpdpp.h" +#include "regex_filter.h" #include "screen.h" struct Browser: Screen>, Filterable, HasSongs, Searchable, Tabbable @@ -51,9 +52,9 @@ struct Browser: Screen>, Filterable, HasSongs, Searchable, T // Searchable implementation virtual bool allowsSearching() OVERRIDE; - virtual bool search(const std::string &constraint) OVERRIDE; - virtual void nextFound(bool wrap) OVERRIDE; - virtual void prevFound(bool wrap) OVERRIDE; + virtual bool setSearchConstraint(const std::string &constraint) OVERRIDE; + virtual void findForward(bool wrap) OVERRIDE; + virtual void findBackward(bool wrap) OVERRIDE; // HasSongs implementation virtual ProxySongList proxySongList() OVERRIDE; @@ -82,6 +83,7 @@ private: bool m_local_browser; size_t m_scroll_beginning; std::string m_current_directory; + RegexFilter m_search_predicate; }; extern Browser *myBrowser; diff --git a/src/helpers.h b/src/helpers.h index 51a35308..7dc3b500 100644 --- a/src/helpers.h +++ b/src/helpers.h @@ -29,6 +29,41 @@ #include "utility/string.h" #include "utility/wide_string.h" +template +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 +void searchForward(NC::Menu &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 +void searchBackward(NC::Menu &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) { return dynamic_cast(screen); diff --git a/src/interfaces.h b/src/interfaces.h index edf804b3..368a51cf 100644 --- a/src/interfaces.h +++ b/src/interfaces.h @@ -37,9 +37,9 @@ struct Filterable struct Searchable { virtual bool allowsSearching() = 0; - virtual bool search(const std::string &constraint) = 0; - virtual void nextFound(bool wrap) = 0; - virtual void prevFound(bool wrap) = 0; + virtual bool setSearchConstraint(const std::string &constraint) = 0; + virtual void findForward(bool wrap) = 0; + virtual void findBackward(bool wrap) = 0; }; struct HasSongs diff --git a/src/media_library.cpp b/src/media_library.cpp index a095877e..49dd2b40 100644 --- a/src/media_library.cpp +++ b/src/media_library.cpp @@ -29,10 +29,9 @@ #include "display.h" #include "helpers.h" #include "global.h" -#include "media_library.h" #include "mpdpp.h" #include "playlist.h" -#include "regex_filter.h" +#include "media_library.h" #include "status.h" #include "statusbar.h" #include "utility/comparators.h" @@ -256,7 +255,6 @@ void MediaLibrary::update() { if (Albums.reallyEmpty() || m_albums_update_request) { - Albums.clearSearchResults(); m_albums_update_request = false; std::map, time_t> albums; for (MPD::SongIterator s = Mpd.GetDirectoryRecursive("/"), end; s != end; ++s) @@ -299,7 +297,6 @@ void MediaLibrary::update() { if (Tags.reallyEmpty() || m_tags_update_request) { - Tags.clearSearchResults(); m_tags_update_request = false; std::map tags; 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.clearSearchResults(); m_albums_update_request = false; auto &primary_tag = Tags.current().value().tag(); Mpd.StartSearch(true); @@ -395,7 +391,6 @@ void MediaLibrary::update() && ((Songs.reallyEmpty() && Global::Timer - m_timer > m_fetching_delay) || m_songs_update_request) ) { - Songs.clearSearchResults(); m_songs_update_request = false; auto &album = Albums.current().value(); Mpd.StartSearch(true); @@ -640,66 +635,63 @@ bool MediaLibrary::allowsSearching() return true; } -bool MediaLibrary::search(const std::string &constraint) +bool MediaLibrary::setSearchConstraint(const std::string &constraint) { if (constraint.empty()) { if (isActiveWindow(Tags)) - Tags.clearSearchResults(); + m_tags_search_predicate.clear(); else if (isActiveWindow(Albums)) - Albums.clearSearchResults(); + m_albums_search_predicate.clear(); else if (isActiveWindow(Songs)) - Songs.clearSearchResults(); + m_songs_search_predicate.clear(); return false; } - try + else { - bool result = false; if (isActiveWindow(Tags)) { - auto rx = RegexFilter( - boost::regex(constraint, Config.regex_type), TagEntryMatcher); - result = Tags.search(Tags.begin(), Tags.end(), rx); + m_tags_search_predicate = RegexFilter( + boost::regex(constraint, Config.regex_type), + TagEntryMatcher + ); } else if (isActiveWindow(Albums)) { - auto fun = boost::bind(AlbumEntryMatcher, _1, _2, false); - auto rx = RegexItemFilter( - boost::regex(constraint, Config.regex_type), fun); - result = Albums.search(Albums.begin(), Albums.end(), rx); + m_albums_search_predicate = RegexItemFilter( + boost::regex(constraint, Config.regex_type), + boost::bind(AlbumEntryMatcher, _1, _2, false) + ); } else if (isActiveWindow(Songs)) { - auto rx = RegexFilter( - boost::regex(constraint, Config.regex_type), SongEntryMatcher); - result = Songs.search(Songs.begin(), Songs.end(), rx); + m_songs_search_predicate = RegexFilter( + boost::regex(constraint, Config.regex_type), + SongEntryMatcher + ); } - return result; - } - catch (boost::bad_expression &) - { - return false; + return true; } } -void MediaLibrary::nextFound(bool wrap) +void MediaLibrary::findForward(bool wrap) { if (isActiveWindow(Tags)) - Tags.nextFound(wrap); + searchForward(Tags, m_tags_search_predicate, wrap); else if (isActiveWindow(Albums)) - Albums.nextFound(wrap); + searchForward(Albums, m_albums_search_predicate, wrap); 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)) - Tags.prevFound(wrap); + searchBackward(Tags, m_tags_search_predicate, wrap); else if (isActiveWindow(Albums)) - Albums.prevFound(wrap); + searchBackward(Albums, m_albums_search_predicate, wrap); else if (isActiveWindow(Songs)) - Songs.prevFound(wrap); + searchBackward(Songs, m_songs_search_predicate, wrap); } /***********************************************************************/ diff --git a/src/media_library.h b/src/media_library.h index 825a40cd..30514008 100644 --- a/src/media_library.h +++ b/src/media_library.h @@ -24,6 +24,7 @@ #include #include "interfaces.h" +#include "regex_filter.h" #include "screen.h" struct MediaLibrary: Screen, Filterable, HasColumns, HasSongs, Searchable, Tabbable @@ -54,9 +55,9 @@ struct MediaLibrary: Screen, Filterable, HasColumns, HasSongs, Sea // Searchable implementation virtual bool allowsSearching() OVERRIDE; - virtual bool search(const std::string &constraint) OVERRIDE; - virtual void nextFound(bool wrap) OVERRIDE; - virtual void prevFound(bool wrap) OVERRIDE; + virtual bool setSearchConstraint(const std::string &constraint) OVERRIDE; + virtual void findForward(bool wrap) OVERRIDE; + virtual void findBackward(bool wrap) OVERRIDE; // HasSongs implementation virtual ProxySongList proxySongList() OVERRIDE; @@ -153,6 +154,11 @@ private: const int m_window_timeout; const boost::posix_time::time_duration m_fetching_delay; + + RegexFilter m_tags_search_predicate; + RegexItemFilter m_albums_search_predicate; + RegexFilter m_songs_search_predicate; + }; extern MediaLibrary *myLibrary; diff --git a/src/menu.h b/src/menu.h index 13bf7873..9fae6f50 100644 --- a/src/menu.h +++ b/src/menu.h @@ -176,26 +176,11 @@ public: void applyCurrentFilter(ConstIterator first, ConstIterator last); - bool search(ConstIterator first, ConstIterator last, const FilterFunction &f); - /// Clears filter results void clearFilterResults(); 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 const FilterFunction &getFilter() { return m_filter; } @@ -306,8 +291,13 @@ public: Iterator currentI() { return Iterator(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); } 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()); } ConstIterator begin() const { return ConstIterator(m_options_ptr->begin()); } @@ -360,7 +350,6 @@ private: std::vector *m_options_ptr; std::vector m_options; std::vector m_filtered_options; - std::set m_found_positions; size_t m_beginning; size_t m_highlight; @@ -403,7 +392,6 @@ Menu::Menu(const Menu &rhs) , m_item_displayer(rhs.m_item_displayer) , m_filter(rhs.m_filter) , m_searcher(rhs.m_searcher) -, m_found_positions(rhs.m_found_positions) , m_beginning(rhs.m_beginning) , m_highlight(rhs.m_highlight) , m_highlight_color(rhs.m_highlight_color) @@ -430,7 +418,6 @@ Menu::Menu(Menu &&rhs) , m_searcher(rhs.m_searcher) , m_options(std::move(rhs.m_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_highlight(rhs.m_highlight) , m_highlight_color(rhs.m_highlight_color) @@ -456,7 +443,6 @@ Menu &Menu::operator=(Menu rhs) std::swap(m_searcher, rhs.m_searcher); std::swap(m_options, rhs.m_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_highlight, rhs.m_highlight); std::swap(m_highlight_color, rhs.m_highlight_color); @@ -705,7 +691,6 @@ void Menu::clear() { clearFilterResults(); m_options.clear(); - m_found_positions.clear(); m_options_ptr = &m_options; } @@ -764,52 +749,6 @@ void Menu::clearFilter() m_filter = 0; } -template -bool Menu::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 -void Menu::clearSearchResults() -{ - m_found_positions.clear(); -} - -template -void Menu::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 -void Menu::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 diff --git a/src/playlist.cpp b/src/playlist.cpp index 7478183f..167c1c13 100644 --- a/src/playlist.cpp +++ b/src/playlist.cpp @@ -27,7 +27,6 @@ #include "helpers.h" #include "menu.h" #include "playlist.h" -#include "regex_filter.h" #include "screen_switcher.h" #include "song.h" #include "status.h" @@ -184,33 +183,30 @@ bool Playlist::allowsSearching() return true; } -bool Playlist::search(const std::string &constraint) +bool Playlist::setSearchConstraint(const std::string &constraint) { if (constraint.empty()) { - w.clearSearchResults(); + m_search_predicate.clear(); return false; } - try + else { - auto rx = RegexFilter( - boost::regex(constraint, Config.regex_type), playlistEntryMatcher); - return w.search(w.begin(), w.end(), rx); - } - catch (boost::bad_expression &) - { - return false; + m_search_predicate = RegexFilter( + boost::regex(constraint, Config.regex_type), playlistEntryMatcher + ); + return true; } } -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); } /***********************************************************************/ diff --git a/src/playlist.h b/src/playlist.h index 35ba77d4..251f9522 100644 --- a/src/playlist.h +++ b/src/playlist.h @@ -25,6 +25,7 @@ #include #include "interfaces.h" +#include "regex_filter.h" #include "screen.h" #include "song.h" @@ -54,9 +55,9 @@ struct Playlist: Screen>, Filterable, HasSongs, Searchable, // Searchable implementation virtual bool allowsSearching(); - virtual bool search(const std::string &constraint) OVERRIDE; - virtual void nextFound(bool wrap) OVERRIDE; - virtual void prevFound(bool wrap) OVERRIDE; + virtual bool setSearchConstraint(const std::string &constraint) OVERRIDE; + virtual void findForward(bool wrap) OVERRIDE; + virtual void findBackward(bool wrap) OVERRIDE; // HasSongs implementation virtual ProxySongList proxySongList() OVERRIDE; @@ -100,6 +101,8 @@ private: bool m_reload_total_length; bool m_reload_remaining; + + RegexFilter m_search_predicate; }; extern Playlist *myPlaylist; diff --git a/src/playlist_editor.cpp b/src/playlist_editor.cpp index 4846a9b0..b969a24c 100644 --- a/src/playlist_editor.cpp +++ b/src/playlist_editor.cpp @@ -30,7 +30,6 @@ #include "playlist.h" #include "playlist_editor.h" #include "mpdpp.h" -#include "regex_filter.h" #include "status.h" #include "statusbar.h" #include "tag_editor.h" @@ -138,7 +137,6 @@ void PlaylistEditor::update() if (Playlists.reallyEmpty() || m_playlists_update_requested) { m_playlists_update_requested = false; - Playlists.clearSearchResults(); withUnfilteredMenuReapplyFilter(Playlists, [this]() { size_t idx = 0; for (MPD::PlaylistIterator it = Mpd.GetPlaylists(), end; it != end; ++it, ++idx) @@ -164,7 +162,6 @@ void PlaylistEditor::update() Content.clear(); else { - Content.clearSearchResults(); withUnfilteredMenuReapplyFilter(Content, [this]() { size_t idx = 0; MPD::SongIterator s = Mpd.GetPlaylistContent(Playlists.current().value().path()), end; @@ -403,53 +400,50 @@ bool PlaylistEditor::allowsSearching() return true; } -bool PlaylistEditor::search(const std::string &constraint) +bool PlaylistEditor::setSearchConstraint(const std::string &constraint) { if (constraint.empty()) { if (isActiveWindow(Playlists)) - Playlists.clearSearchResults(); + m_playlists_search_predicate.clear(); else if (isActiveWindow(Content)) - Content.clearSearchResults(); + m_content_search_predicate.clear(); return false; } - try + else { - bool result = false; if (isActiveWindow(Playlists)) { - auto rx = RegexFilter( - boost::regex(constraint, Config.regex_type), PlaylistEntryMatcher); - result = Playlists.search(Playlists.begin(), Playlists.end(), rx); + m_playlists_search_predicate = RegexFilter( + boost::regex(constraint, Config.regex_type), + PlaylistEntryMatcher + ); } else if (isActiveWindow(Content)) { - auto rx = RegexFilter( - boost::regex(constraint, Config.regex_type), SongEntryMatcher); - result = Content.search(Content.begin(), Content.end(), rx); + m_content_search_predicate = RegexFilter( + boost::regex(constraint, Config.regex_type), + SongEntryMatcher + ); } - return result; - } - catch (boost::bad_expression &) - { - return false; + return true; } } -void PlaylistEditor::nextFound(bool wrap) +void PlaylistEditor::findForward(bool wrap) { if (isActiveWindow(Playlists)) - Playlists.nextFound(wrap); + searchForward(Playlists, m_playlists_search_predicate, wrap); 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)) - Playlists.prevFound(wrap); + searchBackward(Playlists, m_playlists_search_predicate, wrap); else if (isActiveWindow(Content)) - Content.prevFound(wrap); + searchBackward(Content, m_content_search_predicate, wrap); } /***********************************************************************/ diff --git a/src/playlist_editor.h b/src/playlist_editor.h index e1d4bc10..3a8b6480 100644 --- a/src/playlist_editor.h +++ b/src/playlist_editor.h @@ -24,6 +24,7 @@ #include #include "interfaces.h" +#include "regex_filter.h" #include "screen.h" struct PlaylistEditor: Screen, Filterable, HasColumns, HasSongs, Searchable, Tabbable @@ -54,9 +55,9 @@ struct PlaylistEditor: Screen, Filterable, HasColumns, HasSongs, S // Searchable implementation virtual bool allowsSearching() OVERRIDE; - virtual bool search(const std::string &constraint) OVERRIDE; - virtual void nextFound(bool wrap) OVERRIDE; - virtual void prevFound(bool wrap) OVERRIDE; + virtual bool setSearchConstraint(const std::string &constraint) OVERRIDE; + virtual void findForward(bool wrap) OVERRIDE; + virtual void findBackward(bool wrap) OVERRIDE; // HasSongs implementation virtual ProxySongList proxySongList() OVERRIDE; @@ -98,6 +99,9 @@ private: const int m_window_timeout; const boost::posix_time::time_duration m_fetching_delay; + + RegexFilter m_playlists_search_predicate; + RegexFilter m_content_search_predicate; }; extern PlaylistEditor *myPlaylistEditor; diff --git a/src/regex_filter.h b/src/regex_filter.h index 1970f41e..2409b1a6 100644 --- a/src/regex_filter.h +++ b/src/regex_filter.h @@ -22,21 +22,35 @@ #define NCMPCPP_REGEX_FILTER_H #include +#include #include "menu.h" -template struct RegexFilter +template +struct RegexFilter { typedef NC::Menu MenuT; typedef typename NC::Menu::Item Item; typedef std::function FilterFunction; + RegexFilter() { } RegexFilter(boost::regex rx, FilterFunction filter) - : m_rx(rx), m_filter(filter) { } - - bool operator()(const Item &item) { + : m_rx(std::move(rx)), m_filter(std::move(filter)) { } + + void clear() + { + m_filter = nullptr; + } + + bool operator()(const Item &item) const { + assert(defined()); return m_filter(m_rx, item.value()); } - + + bool defined() const + { + return m_filter.operator bool(); + } + static std::string currentFilter(MenuT &menu) { std::string filter; @@ -57,13 +71,24 @@ template struct RegexItemFilter typedef typename NC::Menu::Item Item; typedef std::function FilterFunction; + RegexItemFilter() { } 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) { return m_filter(m_rx, item); } + bool defined() const + { + return m_filter.operator bool(); + } + static std::string currentFilter(MenuT &menu) { std::string filter; diff --git a/src/search_engine.cpp b/src/search_engine.cpp index 89d2f23f..7d20e9cc 100644 --- a/src/search_engine.cpp +++ b/src/search_engine.cpp @@ -26,7 +26,6 @@ #include "global.h" #include "helpers.h" #include "playlist.h" -#include "regex_filter.h" #include "search_engine.h" #include "settings.h" #include "status.h" @@ -293,34 +292,31 @@ bool SearchEngine::allowsSearching() return w.back().value().isSong(); } -bool SearchEngine::search(const std::string &constraint) +bool SearchEngine::setSearchConstraint(const std::string &constraint) { if (constraint.empty()) { - w.clearSearchResults(); + m_search_predicate.clear(); return false; } - try + else { - auto fun = boost::bind(SEItemEntryMatcher, _1, _2, false); - auto rx = RegexItemFilter( - boost::regex(constraint, Config.regex_type), fun); - return w.search(w.begin(), w.end(), rx); - } - catch (boost::bad_expression &) - { - return false; + m_search_predicate = RegexItemFilter( + boost::regex(constraint, Config.regex_type), + boost::bind(SEItemEntryMatcher, _1, _2, false) + ); + return true; } } -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); } /***********************************************************************/ diff --git a/src/search_engine.h b/src/search_engine.h index ed96c970..52bddbba 100644 --- a/src/search_engine.h +++ b/src/search_engine.h @@ -25,9 +25,56 @@ #include "interfaces.h" #include "mpdpp.h" +#include "regex_filter.h" #include "screen.h" -struct SearchEngine: Screen>, 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>, Filterable, HasSongs, Searchable, Tabbable { SearchEngine(); @@ -53,9 +100,9 @@ struct SearchEngine: Screen>, Filterable, HasSongs, Sear // Searchable implementation virtual bool allowsSearching() OVERRIDE; - virtual bool search(const std::string &constraint) OVERRIDE; - virtual void nextFound(bool wrap) OVERRIDE; - virtual void prevFound(bool wrap) OVERRIDE; + virtual bool setSearchConstraint(const std::string &constraint) OVERRIDE; + virtual void findForward(bool wrap) OVERRIDE; + virtual void findBackward(bool wrap) OVERRIDE; // HasSongs implementation virtual ProxySongList proxySongList() OVERRIDE; @@ -77,6 +124,8 @@ protected: private: void Prepare(); void Search(); + + RegexItemFilter m_search_predicate; const char **SearchMode; @@ -89,52 +138,6 @@ private: 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; #endif // NCMPCPP_SEARCH_ENGINE_H diff --git a/src/status.cpp b/src/status.cpp index 0cfb7a34..e86ce52b 100644 --- a/src/status.cpp +++ b/src/status.cpp @@ -387,7 +387,6 @@ int Status::State::volume() void Status::Changes::playlist(unsigned previous_version) { - myPlaylist->main().clearSearchResults(); withUnfilteredMenuReapplyFilter(myPlaylist->main(), [previous_version]() { if (m_playlist_length < myPlaylist->main().size()) { diff --git a/src/tag_editor.cpp b/src/tag_editor.cpp index 6b94db93..5de9574d 100644 --- a/src/tag_editor.cpp +++ b/src/tag_editor.cpp @@ -775,54 +775,50 @@ bool TagEditor::allowsSearching() return w == Dirs || w == Tags; } -bool TagEditor::search(const std::string &constraint) +bool TagEditor::setSearchConstraint(const std::string &constraint) { if (constraint.empty()) { if (w == Dirs) - Dirs->clearSearchResults(); + m_directories_search_predicate.clear(); else if (w == Tags) - Tags->clearSearchResults(); + m_songs_search_predicate.clear(); return false; } - try + else { - bool result = false; if (w == Dirs) { - auto fun = boost::bind(DirEntryMatcher, _1, _2, false); - auto rx = RegexFilter< std::pair >( - boost::regex(constraint, Config.regex_type), fun); - result = Dirs->search(Dirs->begin(), Dirs->end(), rx); + m_directories_search_predicate = RegexFilter>( + boost::regex(constraint, Config.regex_type), + boost::bind(DirEntryMatcher, _1, _2, false) + ); } else if (w == Tags) { - auto rx = RegexFilter( - boost::regex(constraint, Config.regex_type), SongEntryMatcher); - result = Tags->search(Tags->begin(), Tags->end(), rx); + m_songs_search_predicate = RegexFilter( + boost::regex(constraint, Config.regex_type), + SongEntryMatcher + ); } - return result; - } - catch (boost::bad_expression &) - { - return false; + return true; } } -void TagEditor::nextFound(bool wrap) +void TagEditor::findForward(bool wrap) { if (w == Dirs) - Dirs->nextFound(wrap); + searchForward(*Dirs, m_directories_search_predicate, wrap); 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) - Dirs->prevFound(wrap); + searchBackward(*Dirs, m_directories_search_predicate, wrap); else if (w == Tags) - Tags->prevFound(wrap); + searchBackward(*Tags, m_songs_search_predicate, wrap); } /***********************************************************************/ diff --git a/src/tag_editor.h b/src/tag_editor.h index 8d7fac93..0ffb29d8 100644 --- a/src/tag_editor.h +++ b/src/tag_editor.h @@ -58,9 +58,9 @@ struct TagEditor: Screen, Filterable, HasColumns, HasSongs, Search // Searchable implementation virtual bool allowsSearching() OVERRIDE; - virtual bool search(const std::string &constraint) OVERRIDE; - virtual void nextFound(bool wrap) OVERRIDE; - virtual void prevFound(bool wrap) OVERRIDE; + virtual bool setSearchConstraint(const std::string &constraint) OVERRIDE; + virtual void findForward(bool wrap) OVERRIDE; + virtual void findBackward(bool wrap) OVERRIDE; // HasSongs implementation virtual ProxySongList proxySongList() OVERRIDE; @@ -100,6 +100,9 @@ private: std::string itsBrowsedDir; std::string itsHighlightedDir; + + RegexFilter> m_directories_search_predicate; + RegexFilter m_songs_search_predicate; }; extern TagEditor *myTagEditor;