[Xfce4-commits] [panel-plugins/xfce4-whiskermenu-plugin] 56/473: More improvements to search algorithm.
noreply at xfce.org
noreply at xfce.org
Mon Feb 16 23:53:46 CET 2015
This is an automated email from the git hooks/post-receive script.
gottcode pushed a commit to branch master
in repository panel-plugins/xfce4-whiskermenu-plugin.
commit 8e469ad405b584842e1b4d518847d134ea494fc0
Author: Graeme Gott <graeme at gottcode.org>
Date: Tue Jul 2 15:04:03 2013 -0400
More improvements to search algorithm.
---
src/CMakeLists.txt | 1 +
src/launcher.cpp | 144 +++++++++--------------------------------
src/launcher.hpp | 11 ++--
src/query.cpp | 177 +++++++++++++++++++++++++++++++++++++++++++++++++++
src/query.hpp | 54 ++++++++++++++++
src/search_page.cpp | 23 ++-----
src/search_page.hpp | 3 +-
7 files changed, 278 insertions(+), 135 deletions(-)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 6c45e4f..1951f5f 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -68,6 +68,7 @@ add_library(whiskermenu SHARED
menu.cpp
page.cpp
panel_plugin.cpp
+ query.cpp
recent_page.cpp
register_plugin.c
resizer_widget.cpp
diff --git a/src/launcher.cpp b/src/launcher.cpp
index 6e56194..0483a04 100644
--- a/src/launcher.cpp
+++ b/src/launcher.cpp
@@ -16,7 +16,7 @@
#include "launcher.hpp"
-#include <vector>
+#include "query.hpp"
extern "C"
{
@@ -142,11 +142,25 @@ Launcher::Launcher(GarconMenuItem* item) :
details = generic_name;
}
m_text = g_markup_printf_escaped("<b>%s</b>\n%s", display_name, details);
+
+ // Create search text
+ gchar* normalized = g_utf8_normalize(details, -1, G_NORMALIZE_DEFAULT);
+ gchar* utf8 = g_utf8_casefold(normalized, -1);
+ m_search_comment = utf8;
+ g_free(utf8);
+ g_free(normalized);
}
else
{
m_text = g_markup_printf_escaped("%s", display_name);
}
+
+ // Create search text
+ gchar* normalized = g_utf8_normalize(display_name, -1, G_NORMALIZE_DEFAULT);
+ gchar* utf8 = g_utf8_casefold(normalized, -1);
+ m_search_name = utf8;
+ g_free(utf8);
+ g_free(normalized);
}
//-----------------------------------------------------------------------------
@@ -160,9 +174,9 @@ Launcher::~Launcher()
//-----------------------------------------------------------------------------
-unsigned int Launcher::get_search_results(const std::string& filter_string) const
+unsigned int Launcher::get_search_results(const Query &query) const
{
- std::map<std::string, unsigned int>::const_iterator i = m_searches.find(filter_string);
+ std::map<std::string, unsigned int>::const_iterator i = m_searches.find(query.query());
return (i != m_searches.end()) ? i->second : UINT_MAX;
}
@@ -251,85 +265,34 @@ void Launcher::run(GdkScreen* screen) const
//-----------------------------------------------------------------------------
-void Launcher::search(const std::string& filter_string)
+void Launcher::search(const Query& query)
{
- // Check if search has been done before
- std::map<std::string, unsigned int>::const_iterator i = m_searches.find(filter_string);
- if (i != m_searches.end())
+ if (query.empty())
{
return;
}
- // Check if search will fail because a shorter version has failed before
-
- // Perform search
- unsigned int index = UINT_MAX;
- std::vector<unsigned int> spaces;
-
- const gchar* filter_string_c = filter_string.c_str();
- const gchar* filter_string_ind = filter_string_c;
- size_t filter_len = filter_string.length();
-
- const gchar* search_text = get_search_text();
- size_t len = strlen(search_text);
- for (const gchar* pos = search_text; *pos; pos = g_utf8_next_char(pos))
+ // Check if search has been done or if a shorter version has failed before
+ for (std::map<std::string, unsigned int>::const_iterator i = m_searches.begin(), end = m_searches.end(); i != end; ++i)
{
- gunichar c = g_utf8_get_char(pos);
- len -= (pos - search_text);
- if ((len >= filter_len) && (memcmp(pos, filter_string_c, filter_len) == 0))
- {
- index = pos - search_text;
- break;
- }
- else if (c == g_utf8_get_char(filter_string_ind))
- {
- filter_string_ind = g_utf8_next_char(filter_string_ind);
- }
- else if (g_unichar_isspace(c))
- {
- spaces.push_back(pos - search_text);
- }
- else if ((c == '\n') && (*filter_string_ind != 0))
+ if ( ((i->second == UINT_MAX) && (query.query().find(i->first) == 0))
+ || (i->first == query.query()) )
{
- filter_string_ind = filter_string_c;
+ return;
}
}
- // Check if search text starts with filter string
- if (index == 0)
+ unsigned int match = query.match(m_search_name);
+ if ((match == UINT_MAX) && f_show_description)
{
- // Do nothing
- }
- // Check if search text contains filter string
- else if (index != UINT_MAX)
- {
- // Check if a word in search text starts with filter string
- unsigned int space_index = 0;
- for (std::vector<unsigned int>::const_reverse_iterator i = spaces.rbegin(), end = spaces.rend(); i != end; ++i)
- {
- if (*i < index)
- {
- space_index = *i;
- break;
- }
- }
- unsigned int delta = index - space_index;
- if (delta == 1)
+ match = query.match(m_search_comment);
+ if (match != UINT_MAX)
{
- index += 0x10000000;
- }
- else
- {
- index += 0x20000000 + delta;
+ // Sort matches in comments after matches in names
+ match += 10;
}
}
- // Check if search text contains characters of string
- else if (*filter_string_ind == 0)
- {
- index = UINT_MAX - 1;
- }
-
- m_searches.insert(std::make_pair(filter_string, index));
+ m_searches.insert(std::make_pair(query.query(), match));
}
//-----------------------------------------------------------------------------
@@ -361,48 +324,3 @@ void Launcher::set_show_description(bool show)
}
//-----------------------------------------------------------------------------
-
-const gchar* Launcher::get_search_text()
-{
- if (!m_search_text.empty())
- {
- return m_search_text.c_str();
- }
-
- // Combine name, comment, and generic name into single casefolded string
- const gchar* name = garcon_menu_item_get_name(m_item);
- if (name)
- {
- gchar* normalized = g_utf8_normalize(name, -1, G_NORMALIZE_DEFAULT);
- gchar* utf8 = g_utf8_casefold(normalized, -1);
- m_search_text += utf8;
- g_free(utf8);
- g_free(normalized);
- m_search_text += '\n';
- }
-
- const gchar* generic_name = garcon_menu_item_get_generic_name(m_item);
- if (generic_name)
- {
- gchar* normalized = g_utf8_normalize(generic_name, -1, G_NORMALIZE_DEFAULT);
- gchar* utf8 = g_utf8_casefold(normalized, -1);
- m_search_text += utf8;
- g_free(utf8);
- g_free(normalized);
- m_search_text += '\n';
- }
-
- const gchar* comment = garcon_menu_item_get_comment(m_item);
- if (comment)
- {
- gchar* normalized = g_utf8_normalize(comment, -1, G_NORMALIZE_DEFAULT);
- gchar* utf8 = g_utf8_casefold(normalized, -1);
- m_search_text += utf8;
- g_free(utf8);
- g_free(normalized);
- }
-
- return m_search_text.c_str();
-}
-
-//-----------------------------------------------------------------------------
diff --git a/src/launcher.hpp b/src/launcher.hpp
index cdbae15..39fb921 100644
--- a/src/launcher.hpp
+++ b/src/launcher.hpp
@@ -29,6 +29,8 @@ extern "C"
namespace WhiskerMenu
{
+class Query;
+
class Launcher
{
public:
@@ -50,11 +52,11 @@ public:
return m_item;
}
- unsigned int get_search_results(const std::string& filter_string) const;
+ unsigned int get_search_results(const Query& query) const;
void run(GdkScreen* screen) const;
- void search(const std::string& filter_string);
+ void search(const Query& query);
static bool get_show_name();
static bool get_show_description();
@@ -65,13 +67,12 @@ private:
Launcher(const Launcher& launcher);
Launcher& operator=(const Launcher& launcher);
- const gchar* get_search_text();
-
private:
GarconMenuItem* m_item;
gchar* m_icon;
gchar* m_text;
- std::string m_search_text;
+ std::string m_search_name;
+ std::string m_search_comment;
std::map<std::string, unsigned int> m_searches;
};
diff --git a/src/query.cpp b/src/query.cpp
new file mode 100644
index 0000000..fe91ee3
--- /dev/null
+++ b/src/query.cpp
@@ -0,0 +1,177 @@
+// Copyright (C) 2013 Graeme Gott <graeme at gottcode.org>
+//
+// This library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// at your option) any later version.
+//
+// This library 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+
+#include "query.hpp"
+
+#include <sstream>
+
+#include <climits>
+#include <cstring>
+
+extern "C"
+{
+#include <glib.h>
+}
+
+using namespace WhiskerMenu;
+
+//-----------------------------------------------------------------------------
+
+static inline bool is_start_word(const std::string& string, std::string::size_type pos)
+{
+ return (pos == 0) || g_unichar_isspace(g_utf8_get_char(g_utf8_prev_char(&string.at(pos))));
+}
+
+//-----------------------------------------------------------------------------
+
+Query::Query(const std::string& query)
+{
+ set(query);
+}
+
+//-----------------------------------------------------------------------------
+
+Query::~Query()
+{
+ clear();
+}
+
+//-----------------------------------------------------------------------------
+
+unsigned int Query::match(const std::string& haystack) const
+{
+ // Make sure haystack is longer than query
+ if (m_query.empty() || (m_query.length() > haystack.length()))
+ {
+ return UINT_MAX;
+ }
+
+ // Check if haystack begins with or is query
+ std::string::size_type pos = haystack.find(m_query);
+ if (pos == 0)
+ {
+ return haystack.length() != m_query.length();
+ }
+ // Check if haystack contains query starting at a word boundary
+ else if ((pos != std::string::npos) && is_start_word(haystack, pos))
+ {
+ return 2;
+ }
+
+ if (m_query_words.size() > 1)
+ {
+ // Check if haystack contains query as words
+ std::string::size_type search_pos = 0;
+ for (std::vector<std::string>::const_iterator i = m_query_words.begin(), end = m_query_words.end(); i != end; ++i)
+ {
+ search_pos = haystack.find(*i, search_pos);
+ if ((search_pos == std::string::npos) || !is_start_word(haystack, search_pos))
+ {
+ search_pos = std::string::npos;
+ break;
+ }
+ }
+ if (search_pos != std::string::npos)
+ {
+ return 3;
+ }
+
+ // Check if haystack contains query as words in any order
+ std::vector<std::string>::size_type found_words = 0;
+ for (std::vector<std::string>::const_iterator i = m_query_words.begin(), end = m_query_words.end(); i != end; ++i)
+ {
+ search_pos = haystack.find(*i);
+ if ((search_pos != std::string::npos) && is_start_word(haystack, search_pos))
+ {
+ ++found_words;
+ }
+ else
+ {
+ break;
+ }
+ }
+ if (found_words == m_query_words.size())
+ {
+ return 4;
+ }
+ }
+
+ // Check if haystack contains query
+ if (pos != std::string::npos)
+ {
+ return 5;
+ }
+
+ // Check if haystack contains query as characters
+ bool characters_start_words = true;
+ bool start_word = true;
+ const gchar* query_string = m_query.c_str();
+ for (const gchar* pos = haystack.c_str(); *pos; pos = g_utf8_next_char(pos))
+ {
+ gunichar c = g_utf8_get_char(pos);
+ if (c == g_utf8_get_char(query_string))
+ {
+ characters_start_words &= start_word;
+ query_string = g_utf8_next_char(query_string);
+ start_word = false;
+ }
+ else if (g_unichar_isspace(c))
+ {
+ start_word = true;
+ }
+ else
+ {
+ start_word = false;
+ }
+ }
+ if (*query_string == 0)
+ {
+ return characters_start_words ? 6 : 7;
+ }
+
+ // Failed to find any matches
+ return UINT_MAX;
+}
+
+//-----------------------------------------------------------------------------
+
+void Query::clear()
+{
+ m_query.clear();
+ m_query_words.clear();
+}
+
+//-----------------------------------------------------------------------------
+
+void Query::set(const std::string& query)
+{
+ m_query_words.clear();
+
+ m_query = query;
+ if (m_query.empty())
+ {
+ return;
+ }
+
+ std::string buffer;
+ std::stringstream ss(query);
+ while (ss >> buffer)
+ {
+ m_query_words.push_back(buffer);
+ }
+}
+
+//-----------------------------------------------------------------------------
diff --git a/src/query.hpp b/src/query.hpp
new file mode 100644
index 0000000..4e04b22
--- /dev/null
+++ b/src/query.hpp
@@ -0,0 +1,54 @@
+// Copyright (C) 2013 Graeme Gott <graeme at gottcode.org>
+//
+// This library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// at your option) any later version.
+//
+// This library 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+
+#ifndef WHISKERMENU_QUERY_HPP
+#define WHISKERMENU_QUERY_HPP
+
+#include <string>
+#include <vector>
+
+namespace WhiskerMenu
+{
+
+class Query
+{
+public:
+ Query(const std::string& query = std::string());
+ ~Query();
+
+ bool empty() const
+ {
+ return m_query.empty();
+ }
+
+ unsigned int match(const std::string& haystack) const;
+
+ std::string query() const
+ {
+ return m_query;
+ }
+
+ void clear();
+ void set(const std::string& query);
+
+private:
+ std::string m_query;
+ std::vector<std::string> m_query_words;
+};
+
+}
+
+#endif // WHISKERMENU_QUERY_HPP
diff --git a/src/search_page.cpp b/src/search_page.cpp
index 498eee6..58f646d 100644
--- a/src/search_page.cpp
+++ b/src/search_page.cpp
@@ -52,24 +52,17 @@ SearchPage::~SearchPage()
void SearchPage::set_filter(const gchar* filter)
{
// Store filter string
- std::string old_filter_string = m_filter_string;
- if (filter)
- {
- m_filter_string = filter;
- }
- else
- {
- m_filter_string.clear();
- }
- if (m_filter_string == old_filter_string)
+ std::string query(filter ? filter : "");
+ if (m_query.query() == query)
{
return;
}
+ m_query.set(query);
// Create search results
for (std::vector<Launcher*>::iterator i = m_launchers.begin(), end = m_launchers.end(); i != end; ++i)
{
- (*i)->search(m_filter_string);
+ (*i)->search(m_query);
}
// Apply filter
@@ -128,7 +121,7 @@ void SearchPage::unset_menu_items()
bool SearchPage::on_filter(GtkTreeModel* model, GtkTreeIter* iter)
{
- if (m_filter_string.empty())
+ if (m_query.empty())
{
return false;
}
@@ -136,9 +129,7 @@ bool SearchPage::on_filter(GtkTreeModel* model, GtkTreeIter* iter)
// Check if launcher search string contains text
Launcher* launcher = NULL;
gtk_tree_model_get(model, iter, LauncherModel::COLUMN_LAUNCHER, &launcher, -1);
- unsigned int index = launcher->get_search_results(m_filter_string);
-
- return index != UINT_MAX;
+ return launcher->get_search_results(m_query) != UINT_MAX;
}
//-----------------------------------------------------------------------------
@@ -151,7 +142,7 @@ gint SearchPage::on_sort(GtkTreeModel* model, GtkTreeIter* a, GtkTreeIter* b, Se
Launcher* launcher_b = NULL;
gtk_tree_model_get(model, b, LauncherModel::COLUMN_LAUNCHER, &launcher_b, -1);
- return launcher_a->get_search_results(page->m_filter_string) - launcher_b->get_search_results(page->m_filter_string);
+ return launcher_a->get_search_results(page->m_query) - launcher_b->get_search_results(page->m_query);
}
//-----------------------------------------------------------------------------
diff --git a/src/search_page.hpp b/src/search_page.hpp
index 16f660a..020b1cd 100644
--- a/src/search_page.hpp
+++ b/src/search_page.hpp
@@ -18,6 +18,7 @@
#define WHISKERMENU_SEARCH_PAGE_HPP
#include "filter_page.hpp"
+#include "query.hpp"
#include "slot.hpp"
#include <string>
@@ -49,7 +50,7 @@ private:
void unset_search_model();
private:
- std::string m_filter_string;
+ Query m_query;
GtkTreeModelSort* m_sort_model;
std::vector<Launcher*> m_launchers;
};
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the Xfce4-commits
mailing list