commit 7df629fa23ac2be9fec50a2c47057a532e03aa05
parent cfff6ea8fa9f2b1e319ec0ba2d2a77b245aad869
Author: cfillion <cfillion@users.noreply.github.com>
Date: Fri, 8 Apr 2016 23:22:53 -0400
implement smarter filtering in the browser
Diffstat:
5 files changed, 190 insertions(+), 5 deletions(-)
diff --git a/src/browser.cpp b/src/browser.cpp
@@ -311,8 +311,8 @@ void Browser::checkFilter()
const string &filter = from_autostring(wideFilter);
- if(filter != m_filter) {
- m_filter = filter;
+ if(filter != m_filter.get()) {
+ m_filter.set(filter);
fillList();
}
}
@@ -578,8 +578,7 @@ bool Browser::match(const Entry &entry) const
const string &category = getValue(CategoryColumn, entry);
const string &author = getValue(AuthorColumn, entry);
- return icontains(name, m_filter) || icontains(category, m_filter) ||
- icontains(author, m_filter);
+ return m_filter.match(name) || m_filter.match(category) || m_filter.match(author);
}
auto Browser::getEntry(const int listIndex) const -> const Entry *
diff --git a/src/browser.hpp b/src/browser.hpp
@@ -20,6 +20,7 @@
#include "dialog.hpp"
+#include "filter.hpp"
#include "listview.hpp"
#include "registry.hpp"
@@ -121,7 +122,7 @@ private:
int m_currentIndex;
int m_filterTimer;
- std::string m_filter;
+ Filter m_filter;
std::vector<Entry> m_entries;
std::vector<size_t> m_visibleEntries;
std::map<const Entry *, const Version *> m_actions;
diff --git a/src/filter.cpp b/src/filter.cpp
@@ -0,0 +1,73 @@
+/* ReaPack: Package manager for REAPER
+ * Copyright (C) 2015-2016 Christian Fillion
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "filter.hpp"
+
+#include <boost/algorithm/string.hpp>
+
+using namespace std;
+
+void Filter::set(const string &input)
+{
+ enum State { Default, DoubleQuote, SingleQuote };
+
+ string buf;
+ State state = Default;
+ m_input = input;
+
+ m_words.clear();
+
+ auto push = [&] {
+ state = Default;
+
+ if(!buf.empty()) {
+ m_words.push_back(buf);
+ buf.clear();
+ }
+ };
+
+ for(const char c : input) {
+ if(c == '"' && state != SingleQuote) {
+ if(state == DoubleQuote)
+ push();
+ else
+ state = DoubleQuote;
+ }
+ else if(c == '\'' && state != DoubleQuote) {
+ if(state == SingleQuote)
+ push();
+ else
+ state = SingleQuote;
+ }
+ else if(c == '\x20' && state == Default)
+ push();
+ else
+ buf += c;
+ }
+
+ push();
+}
+
+bool Filter::match(const string &str) const
+{
+ for(const string &word : m_words) {
+ if(!boost::icontains(str, word))
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/filter.hpp b/src/filter.hpp
@@ -0,0 +1,35 @@
+/* ReaPack: Package manager for REAPER
+ * Copyright (C) 2015-2016 Christian Fillion
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef REAPACK_FILTER_HPP
+#define REAPACK_FILTER_HPP
+
+#include <string>
+#include <vector>
+
+class Filter {
+public:
+ const std::string get() const { return m_input; }
+ void set(const std::string &);
+ bool match(const std::string &) const;
+
+private:
+ std::string m_input;
+ std::vector<std::string> m_words;
+};
+
+#endif
diff --git a/test/filter.cpp b/test/filter.cpp
@@ -0,0 +1,77 @@
+#include <catch.hpp>
+
+#include <filter.hpp>
+
+using namespace std;
+
+static const char *M = "[filter]";
+
+TEST_CASE("basic matching", M) {
+ Filter f;
+ REQUIRE(f.match("world"));
+
+ f.set("hello");
+
+ REQUIRE(f.match("hello"));
+ REQUIRE(f.match("HELLO"));
+ REQUIRE_FALSE(f.match("world"));
+}
+
+TEST_CASE("get/set filter", M) {
+ Filter f;
+ REQUIRE(f.get().empty());
+
+ f.set("hello");
+ REQUIRE(f.get() == "hello");
+ REQUIRE(f.match("hello"));
+
+ f.set("world");
+ REQUIRE(f.get() == "world");
+ REQUIRE(f.match("world"));
+}
+
+TEST_CASE("word matching", M) {
+ Filter f;
+ f.set("hello world");
+
+ REQUIRE_FALSE(f.match("hello"));
+ REQUIRE(f.match("hello world"));
+ REQUIRE(f.match("helloworld"));
+ REQUIRE(f.match("hello test world"));
+}
+
+TEST_CASE("double quote matching", M) {
+ Filter f;
+ f.set("\"hello world\"");
+
+ REQUIRE(f.match("hello world"));
+ REQUIRE_FALSE(f.match("helloworld"));
+ REQUIRE_FALSE(f.match("hello test world"));
+}
+
+TEST_CASE("single quote matching", M) {
+ Filter f;
+ f.set("'hello world'");
+
+ REQUIRE(f.match("hello world"));
+ REQUIRE_FALSE(f.match("helloworld"));
+ REQUIRE_FALSE(f.match("hello test world"));
+}
+
+TEST_CASE("mixing quotes", M) {
+ Filter f;
+
+ SECTION("double in single") {
+ f.set("'hello \"world\"'");
+
+ REQUIRE(f.match("hello \"world\""));
+ REQUIRE_FALSE(f.match("hello world"));
+ }
+
+ SECTION("single in double") {
+ f.set("\"hello 'world'\"");
+
+ REQUIRE(f.match("hello 'world'"));
+ REQUIRE_FALSE(f.match("hello world"));
+ }
+}