actions: make find forward/backward incremental

This commit is contained in:
Andrzej Rybczak
2014-11-06 23:22:55 +01:00
parent 7c71df8dc7
commit 6a5f46a458
21 changed files with 236 additions and 229 deletions

2
NEWS
View File

@@ -6,6 +6,8 @@ ncmpcpp-0.7 (????-??-??)
* Directories and playlists in browser can now be sorted by modification time.
* ~ is now expanded to home directory in mpd_host configuration variable.
* It is now possible to define startup slave screen using -S/--slave-screen command line option or startup_slave_screen configuration variable.
* List filtering has been removed due to the major part of its functionality overlapping with find forward/backward.
* Find backward/forward function is now incremental.
ncmpcpp-0.6.1 (2014-11-06)

View File

@@ -72,23 +72,6 @@ using Global::myScreen;
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<size_t>(Actions::Type::_numberOfActions)
> AvailableActions;
@@ -96,7 +79,7 @@ boost::array<
void populateActions();
void seek();
void findItem(const Find direction);
void findItem(const SearchDirection direction);
void listsChangeFinisher();
}
@@ -1860,7 +1843,7 @@ bool FindItemBackward::canBeRun() const
void FindItemForward::run()
{
findItem(::Find::Forward);
findItem(SearchDirection::Forward);
listsChangeFinisher();
}
@@ -1872,7 +1855,7 @@ bool FindItemForward::canBeRun() const
void FindItemBackward::run()
{
findItem(::Find::Backward);
findItem(SearchDirection::Backward);
listsChangeFinisher();
}
@@ -1885,7 +1868,7 @@ void NextFoundItem::run()
{
Searchable *w = dynamic_cast<Searchable *>(myScreen);
assert(w != nullptr);
w->findForward(Config.wrapped_search);
w->find(SearchDirection::Forward, Config.wrapped_search, true);
listsChangeFinisher();
}
@@ -1898,7 +1881,7 @@ void PreviousFoundItem::run()
{
Searchable *w = dynamic_cast<Searchable *>(myScreen);
assert(w != nullptr);
w->findBackward(Config.wrapped_search);
w->find(SearchDirection::Backward, Config.wrapped_search, true);
listsChangeFinisher();
}
@@ -2710,7 +2693,7 @@ void seek()
wFooter->setTimeout(old_timeout);
}
void findItem(const Find direction)
void findItem(const SearchDirection direction)
{
using Global::wFooter;
@@ -2721,32 +2704,30 @@ void findItem(const Find direction)
std::string constraint;
{
Statusbar::ScopedLock slock;
Statusbar::put() << "Find " << findToString(direction) << ": ";
NC::Window::ScopedPromptHook prompt_hook(*wFooter,
Statusbar::Helpers::FindImmediately(w, direction)
);
Statusbar::put() << (boost::format("Find %1%: ") % direction).str();
constraint = wFooter->prompt();
}
try
{
bool success = w->setSearchConstraint(constraint);
if (success)
Statusbar::printf("Using constraint \"%1%\"", constraint);
else
if (constraint.empty())
{
Statusbar::printf("Constraint unset");
w->clearConstraint();
}
else
{
w->setSearchConstraint(constraint);
Statusbar::printf("Using constraint \"%1%\"", constraint);
}
}
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()

View File

@@ -26,7 +26,7 @@
#include <string>
#include "window.h"
namespace Actions {//
namespace Actions {
enum class Type
{

View File

@@ -248,31 +248,22 @@ bool Browser::allowsSearching()
return true;
}
bool Browser::setSearchConstraint(const std::string &constraint)
void Browser::setSearchConstraint(const std::string &constraint)
{
if (constraint.empty())
{
m_search_predicate.clear();
return false;
}
else
{
m_search_predicate = RegexFilter<MPD::Item>(
boost::regex(constraint, Config.regex_type),
boost::bind(browserEntryMatcher, _1, _2, false)
);
return true;
}
m_search_predicate = RegexFilter<MPD::Item>(
boost::regex(constraint, Config.regex_type),
boost::bind(browserEntryMatcher, _1, _2, false)
);
}
void Browser::findForward(bool wrap)
void Browser::clearConstraint()
{
searchForward(w, m_search_predicate, wrap);
m_search_predicate.clear();
}
void Browser::findBackward(bool wrap)
bool Browser::find(SearchDirection direction, bool wrap, bool skip_current)
{
searchBackward(w, m_search_predicate, wrap);
return search(w, m_search_predicate, direction, wrap, skip_current);
}
/***********************************************************************/

View File

@@ -47,9 +47,9 @@ struct Browser: Screen<NC::Menu<MPD::Item>>, HasSongs, Searchable, Tabbable
// Searchable implementation
virtual bool allowsSearching() OVERRIDE;
virtual bool setSearchConstraint(const std::string &constraint) OVERRIDE;
virtual void findForward(bool wrap) OVERRIDE;
virtual void findBackward(bool wrap) OVERRIDE;
virtual void setSearchConstraint(const std::string &constraint) OVERRIDE;
virtual void clearConstraint() OVERRIDE;
virtual bool find(SearchDirection direction, bool wrap, bool skip_current) OVERRIDE;
// HasSongs implementation
virtual ProxySongList proxySongList() OVERRIDE;

View File

@@ -20,6 +20,33 @@
#include "enums.h"
std::ostream &operator<<(std::ostream &os, SearchDirection sd)
{
switch (sd)
{
case SearchDirection::Backward:
os << "backward";
break;
case SearchDirection::Forward:
os << "forward";
break;
}
return os;
}
std::istream &operator>>(std::istream &is, SearchDirection &sd)
{
std::string ssd;
is >> ssd;
if (ssd == "backward")
sd = SearchDirection::Backward;
else if (ssd == "forward")
sd = SearchDirection::Forward;
else
is.setstate(std::ios::failbit);
return is;
}
std::ostream &operator<<(std::ostream &os, SpaceAddMode sam)
{
switch (sam)

View File

@@ -24,6 +24,10 @@
#include "config.h"
#include <iostream>
enum class SearchDirection { Backward, Forward };
std::ostream &operator<<(std::ostream &os, SearchDirection sd);
std::istream &operator>>(std::istream &is, SearchDirection &sd);
enum class SpaceAddMode { AddRemove, AlwaysAdd };
std::ostream &operator<<(std::ostream &os, SpaceAddMode sam);
std::istream &operator>>(std::istream &is, SpaceAddMode &sam);

View File

@@ -31,9 +31,10 @@
template <typename Iterator, typename PredicateT>
Iterator wrappedSearch(Iterator begin, Iterator current, Iterator end,
const PredicateT &pred, bool wrap)
const PredicateT &pred, bool wrap, bool skip_current)
{
++current;
if (skip_current)
++current;
auto it = std::find_if(current, end, pred);
if (it == end && wrap)
{
@@ -45,23 +46,40 @@ Iterator wrappedSearch(Iterator begin, Iterator current, Iterator end,
}
template <typename ItemT, typename PredicateT>
void searchForward(NC::Menu<ItemT> &m, const PredicateT &pred, bool wrap)
bool search(NC::Menu<ItemT> &m, const PredicateT &pred,
SearchDirection direction, bool wrap, bool skip_current)
{
if (!pred.defined())
return;
auto it = wrappedSearch(m.begin(), m.current(), 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.rcurrent(), m.rend(), pred, wrap);
if (it != m.rend())
m.highlight(it.base()-m.begin()-1);
bool result = false;
if (pred.defined())
{
switch (direction)
{
case SearchDirection::Backward:
{
auto it = wrappedSearch(m.rbegin(), m.rcurrent(), m.rend(),
pred, wrap, skip_current
);
if (it != m.rend())
{
m.highlight(it.base()-m.begin()-1);
result = true;
}
break;
}
case SearchDirection::Forward:
{
auto it = wrappedSearch(m.begin(), m.current(), m.end(),
pred, wrap, skip_current
);
if (it != m.end())
{
m.highlight(it-m.begin());
result = true;
}
}
}
}
return result;
}
inline HasColumns *hasColumns(BaseScreen *screen)

View File

@@ -22,6 +22,7 @@
#define NCMPCPP_INTERFACES_H
#include <string>
#include "enums.h"
#include "gcc.h"
#include "screen.h"
#include "song.h"
@@ -30,9 +31,9 @@
struct Searchable
{
virtual bool allowsSearching() = 0;
virtual bool setSearchConstraint(const std::string &constraint) = 0;
virtual void findForward(bool wrap) = 0;
virtual void findBackward(bool wrap) = 0;
virtual void setSearchConstraint(const std::string &constraint) = 0;
virtual void clearConstraint() = 0;
virtual bool find(SearchDirection direction, bool wrap, bool skip_current) = 0;
};
struct HasSongs

View File

@@ -574,63 +574,51 @@ bool MediaLibrary::allowsSearching()
return true;
}
bool MediaLibrary::setSearchConstraint(const std::string &constraint)
void MediaLibrary::setSearchConstraint(const std::string &constraint)
{
if (constraint.empty())
if (isActiveWindow(Tags))
{
if (isActiveWindow(Tags))
m_tags_search_predicate.clear();
else if (isActiveWindow(Albums))
m_albums_search_predicate.clear();
else if (isActiveWindow(Songs))
m_songs_search_predicate.clear();
return false;
m_tags_search_predicate = RegexFilter<PrimaryTag>(
boost::regex(constraint, Config.regex_type),
TagEntryMatcher
);
}
else
else if (isActiveWindow(Albums))
{
if (isActiveWindow(Tags))
{
m_tags_search_predicate = RegexFilter<PrimaryTag>(
boost::regex(constraint, Config.regex_type),
TagEntryMatcher
);
}
else if (isActiveWindow(Albums))
{
m_albums_search_predicate = RegexItemFilter<AlbumEntry>(
boost::regex(constraint, Config.regex_type),
boost::bind(AlbumEntryMatcher, _1, _2, false)
);
}
else if (isActiveWindow(Songs))
{
m_songs_search_predicate = RegexFilter<MPD::Song>(
boost::regex(constraint, Config.regex_type),
SongEntryMatcher
);
}
return true;
m_albums_search_predicate = RegexItemFilter<AlbumEntry>(
boost::regex(constraint, Config.regex_type),
boost::bind(AlbumEntryMatcher, _1, _2, false)
);
}
else if (isActiveWindow(Songs))
{
m_songs_search_predicate = RegexFilter<MPD::Song>(
boost::regex(constraint, Config.regex_type),
SongEntryMatcher
);
}
}
void MediaLibrary::findForward(bool wrap)
void MediaLibrary::clearConstraint()
{
if (isActiveWindow(Tags))
searchForward(Tags, m_tags_search_predicate, wrap);
m_tags_search_predicate.clear();
else if (isActiveWindow(Albums))
searchForward(Albums, m_albums_search_predicate, wrap);
m_albums_search_predicate.clear();
else if (isActiveWindow(Songs))
searchForward(Songs, m_songs_search_predicate, wrap);
m_songs_search_predicate.clear();
}
void MediaLibrary::findBackward(bool wrap)
bool MediaLibrary::find(SearchDirection direction, bool wrap, bool skip_current)
{
bool result = false;
if (isActiveWindow(Tags))
searchBackward(Tags, m_tags_search_predicate, wrap);
result = search(Tags, m_tags_search_predicate, direction, wrap, skip_current);
else if (isActiveWindow(Albums))
searchBackward(Albums, m_albums_search_predicate, wrap);
result = search(Albums, m_albums_search_predicate, direction, wrap, skip_current);
else if (isActiveWindow(Songs))
searchBackward(Songs, m_songs_search_predicate, wrap);
result = search(Songs, m_songs_search_predicate, direction, wrap, skip_current);
return result;
}
/***********************************************************************/

View File

@@ -50,9 +50,9 @@ struct MediaLibrary: Screen<NC::Window *>, HasColumns, HasSongs, Searchable, Tab
// Searchable implementation
virtual bool allowsSearching() OVERRIDE;
virtual bool setSearchConstraint(const std::string &constraint) OVERRIDE;
virtual void findForward(bool wrap) OVERRIDE;
virtual void findBackward(bool wrap) OVERRIDE;
virtual void setSearchConstraint(const std::string &constraint) OVERRIDE;
virtual void clearConstraint() OVERRIDE;
virtual bool find(SearchDirection direction, bool wrap, bool skip_current) OVERRIDE;
// HasSongs implementation
virtual ProxySongList proxySongList() OVERRIDE;

View File

@@ -155,30 +155,21 @@ bool Playlist::allowsSearching()
return true;
}
bool Playlist::setSearchConstraint(const std::string &constraint)
void Playlist::setSearchConstraint(const std::string &constraint)
{
if (constraint.empty())
{
m_search_predicate.clear();
return false;
}
else
{
m_search_predicate = RegexFilter<MPD::Song>(
boost::regex(constraint, Config.regex_type), playlistEntryMatcher
);
return true;
}
m_search_predicate = RegexFilter<MPD::Song>(
boost::regex(constraint, Config.regex_type), playlistEntryMatcher
);
}
void Playlist::findForward(bool wrap)
void Playlist::clearConstraint()
{
searchForward(w, m_search_predicate, wrap);
m_search_predicate.clear();
}
void Playlist::findBackward(bool wrap)
bool Playlist::find(SearchDirection direction, bool wrap, bool skip_current)
{
searchBackward(w, m_search_predicate, wrap);
return search(w, m_search_predicate, direction, wrap, skip_current);
}
/***********************************************************************/

View File

@@ -50,9 +50,9 @@ struct Playlist: Screen<NC::Menu<MPD::Song>>, HasSongs, Searchable, Tabbable
// Searchable implementation
virtual bool allowsSearching();
virtual bool setSearchConstraint(const std::string &constraint) OVERRIDE;
virtual void findForward(bool wrap) OVERRIDE;
virtual void findBackward(bool wrap) OVERRIDE;
virtual void setSearchConstraint(const std::string &constraint) OVERRIDE;
virtual void clearConstraint() OVERRIDE;
virtual bool find(SearchDirection direction, bool wrap, bool skip_current) OVERRIDE;
// HasSongs implementation
virtual ProxySongList proxySongList() OVERRIDE;

View File

@@ -328,50 +328,40 @@ bool PlaylistEditor::allowsSearching()
return true;
}
bool PlaylistEditor::setSearchConstraint(const std::string &constraint)
void PlaylistEditor::setSearchConstraint(const std::string &constraint)
{
if (constraint.empty())
if (isActiveWindow(Playlists))
{
if (isActiveWindow(Playlists))
m_playlists_search_predicate.clear();
else if (isActiveWindow(Content))
m_content_search_predicate.clear();
return false;
m_playlists_search_predicate = RegexFilter<MPD::Playlist>(
boost::regex(constraint, Config.regex_type),
PlaylistEntryMatcher
);
}
else
else if (isActiveWindow(Content))
{
if (isActiveWindow(Playlists))
{
m_playlists_search_predicate = RegexFilter<MPD::Playlist>(
boost::regex(constraint, Config.regex_type),
PlaylistEntryMatcher
);
}
else if (isActiveWindow(Content))
{
m_content_search_predicate = RegexFilter<MPD::Song>(
boost::regex(constraint, Config.regex_type),
SongEntryMatcher
);
}
return true;
m_content_search_predicate = RegexFilter<MPD::Song>(
boost::regex(constraint, Config.regex_type),
SongEntryMatcher
);
}
}
void PlaylistEditor::findForward(bool wrap)
void PlaylistEditor::clearConstraint()
{
if (isActiveWindow(Playlists))
searchForward(Playlists, m_playlists_search_predicate, wrap);
m_playlists_search_predicate.clear();
else if (isActiveWindow(Content))
searchForward(Content, m_content_search_predicate, wrap);
m_content_search_predicate.clear();
}
void PlaylistEditor::findBackward(bool wrap)
bool PlaylistEditor::find(SearchDirection direction, bool wrap, bool skip_current)
{
bool result = false;
if (isActiveWindow(Playlists))
searchBackward(Playlists, m_playlists_search_predicate, wrap);
result = search(Playlists, m_playlists_search_predicate, direction, wrap, skip_current);
else if (isActiveWindow(Content))
searchBackward(Content, m_content_search_predicate, wrap);
result = search(Content, m_content_search_predicate, direction, wrap, skip_current);
return result;
}
/***********************************************************************/

View File

@@ -50,9 +50,9 @@ struct PlaylistEditor: Screen<NC::Window *>, HasColumns, HasSongs, Searchable, T
// Searchable implementation
virtual bool allowsSearching() OVERRIDE;
virtual bool setSearchConstraint(const std::string &constraint) OVERRIDE;
virtual void findForward(bool wrap) OVERRIDE;
virtual void findBackward(bool wrap) OVERRIDE;
virtual void setSearchConstraint(const std::string &constraint) OVERRIDE;
virtual void clearConstraint() OVERRIDE;
virtual bool find(SearchDirection direction, bool wrap, bool skip_current) OVERRIDE;
// HasSongs implementation
virtual ProxySongList proxySongList() OVERRIDE;

View File

@@ -260,31 +260,22 @@ bool SearchEngine::allowsSearching()
return w.rbegin()->value().isSong();
}
bool SearchEngine::setSearchConstraint(const std::string &constraint)
void SearchEngine::setSearchConstraint(const std::string &constraint)
{
if (constraint.empty())
{
m_search_predicate.clear();
return false;
}
else
{
m_search_predicate = RegexItemFilter<SEItem>(
boost::regex(constraint, Config.regex_type),
boost::bind(SEItemEntryMatcher, _1, _2, false)
);
return true;
}
m_search_predicate = RegexItemFilter<SEItem>(
boost::regex(constraint, Config.regex_type),
boost::bind(SEItemEntryMatcher, _1, _2, false)
);
}
void SearchEngine::findForward(bool wrap)
void SearchEngine::clearConstraint()
{
searchForward(w, m_search_predicate, wrap);
m_search_predicate.clear();
}
void SearchEngine::findBackward(bool wrap)
bool SearchEngine::find(SearchDirection direction, bool wrap, bool skip_current)
{
searchBackward(w, m_search_predicate, wrap);
return search(w, m_search_predicate, direction, wrap, skip_current);
}
/***********************************************************************/

View File

@@ -95,9 +95,9 @@ struct SearchEngine: Screen<NC::Menu<SEItem>>, HasSongs, Searchable, Tabbable
// Searchable implementation
virtual bool allowsSearching() OVERRIDE;
virtual bool setSearchConstraint(const std::string &constraint) OVERRIDE;
virtual void findForward(bool wrap) OVERRIDE;
virtual void findBackward(bool wrap) OVERRIDE;
virtual void setSearchConstraint(const std::string &constraint) OVERRIDE;
virtual void clearConstraint() OVERRIDE;
virtual bool find(SearchDirection direction, bool wrap, bool skip_current) OVERRIDE;
// HasSongs implementation
virtual ProxySongList proxySongList() OVERRIDE;

View File

@@ -220,6 +220,24 @@ bool Statusbar::Helpers::ImmediatelyReturnOneOf::operator()(const char *s) const
return !isOneOf(s);
}
bool Statusbar::Helpers::FindImmediately::operator()(const char *s)
{
using Global::myScreen;
Status::trace();
try {
if (m_w->allowsSearching() && m_s != s)
{
m_w->setSearchConstraint(s);
m_found = m_w->find(m_direction, Config.wrapped_search, false);
if (myScreen == myPlaylist)
myPlaylist->EnableHighlighting();
myScreen->refreshWindow();
m_s = s;
}
} catch (boost::bad_expression &) { }
return true;
}
bool Statusbar::Helpers::TryExecuteImmediateCommand::operator()(const char *s)
{
bool continue_ = true;

View File

@@ -89,6 +89,21 @@ private:
std::vector<std::string> m_values;
};
struct FindImmediately
{
FindImmediately(Searchable *w, SearchDirection direction)
: m_w(w), m_direction(direction), m_found(true)
{ }
bool operator()(const char *s);
private:
Searchable *m_w;
const SearchDirection m_direction;
std::string m_s;
bool m_found;
};
struct TryExecuteImmediateCommand
{
bool operator()(const char *s);

View File

@@ -721,50 +721,40 @@ bool TagEditor::allowsSearching()
return w == Dirs || w == Tags;
}
bool TagEditor::setSearchConstraint(const std::string &constraint)
void TagEditor::setSearchConstraint(const std::string &constraint)
{
if (constraint.empty())
if (w == Dirs)
{
if (w == Dirs)
m_directories_search_predicate.clear();
else if (w == Tags)
m_songs_search_predicate.clear();
return false;
m_directories_search_predicate = RegexFilter<std::pair<std::string, std::string>>(
boost::regex(constraint, Config.regex_type),
boost::bind(DirEntryMatcher, _1, _2, false)
);
}
else
else if (w == Tags)
{
if (w == Dirs)
{
m_directories_search_predicate = RegexFilter<std::pair<std::string, std::string>>(
boost::regex(constraint, Config.regex_type),
boost::bind(DirEntryMatcher, _1, _2, false)
);
}
else if (w == Tags)
{
m_songs_search_predicate = RegexFilter<MPD::MutableSong>(
boost::regex(constraint, Config.regex_type),
SongEntryMatcher
);
}
return true;
m_songs_search_predicate = RegexFilter<MPD::MutableSong>(
boost::regex(constraint, Config.regex_type),
SongEntryMatcher
);
}
}
void TagEditor::findForward(bool wrap)
void TagEditor::clearConstraint()
{
if (w == Dirs)
searchForward(*Dirs, m_directories_search_predicate, wrap);
m_directories_search_predicate.clear();
else if (w == Tags)
searchForward(*Tags, m_songs_search_predicate, wrap);
m_songs_search_predicate.clear();
}
void TagEditor::findBackward(bool wrap)
bool TagEditor::find(SearchDirection direction, bool wrap, bool skip_current)
{
bool result = false;
if (w == Dirs)
searchBackward(*Dirs, m_directories_search_predicate, wrap);
result = search(*Dirs, m_directories_search_predicate, direction, wrap, skip_current);
else if (w == Tags)
searchBackward(*Tags, m_songs_search_predicate, wrap);
result = search(*Tags, m_songs_search_predicate, direction, wrap, skip_current);
return result;
}
/***********************************************************************/

View File

@@ -53,9 +53,9 @@ struct TagEditor: Screen<NC::Window *>, HasColumns, HasSongs, Searchable, Tabbab
// Searchable implementation
virtual bool allowsSearching() OVERRIDE;
virtual bool setSearchConstraint(const std::string &constraint) OVERRIDE;
virtual void findForward(bool wrap) OVERRIDE;
virtual void findBackward(bool wrap) OVERRIDE;
virtual void setSearchConstraint(const std::string &constraint) OVERRIDE;
virtual void clearConstraint() OVERRIDE;
virtual bool find(SearchDirection direction, bool wrap, bool skip_current) OVERRIDE;
// HasSongs implementation
virtual ProxySongList proxySongList() OVERRIDE;