aboutsummaryrefslogtreecommitdiff
path: root/src/FbTk/MenuSearch.cc
diff options
context:
space:
mode:
authorMathias Gumz <akira@fluxbox.org>2015-02-05 20:30:44 (GMT)
committerMathias Gumz <akira@fluxbox.org>2015-02-05 20:30:44 (GMT)
commit0da4be2a0114d4419ceb70a4c6b6342f8fd79852 (patch)
tree371575758618de52415c823f756346cb3e1a5bb4 /src/FbTk/MenuSearch.cc
parente79228cc08ee1d0d20d7ef27103a5d167fb8f133 (diff)
downloadfluxbox-0da4be2a0114d4419ceb70a4c6b6342f8fd79852.zip
fluxbox-0da4be2a0114d4419ceb70a4c6b6342f8fd79852.tar.bz2
Feature: different MenuSearch modes
Fluxbox now supports three MenuSearch modes: * NoWhere - essentially "disabling" the menu search. * Somewhere - the search string matches somewhere. * ItemStart - the search string matches at the start of a menu item. The default value is "ItemStart", just in the good old times. As long as this feature is not configurable via the menu it would irritate users with distinct muscle memory who type without thinking OR checking the visual feedback: they would trigger items they did not intent to trigger after years of the old behavior. Once this feature get's an entry in the config menu the default value might change.
Diffstat (limited to 'src/FbTk/MenuSearch.cc')
-rw-r--r--src/FbTk/MenuSearch.cc221
1 files changed, 221 insertions, 0 deletions
diff --git a/src/FbTk/MenuSearch.cc b/src/FbTk/MenuSearch.cc
new file mode 100644
index 0000000..da903f4
--- /dev/null
+++ b/src/FbTk/MenuSearch.cc
@@ -0,0 +1,221 @@
1#include "MenuSearch.hh"
2#include "MenuItem.hh"
3#include "StringUtil.hh"
4#include "Resource.hh"
5
6namespace {
7
8size_t search_str_nowhere(const std::string& text, const std::string& pattern) {
9 return std::string::npos;
10}
11
12// finds 'pattern' at beginning of 'text'
13size_t search_str_textstart(const std::string& text, const std::string& pattern) {
14
15 size_t l = std::min(text.size(), pattern.size());
16 if (l == 0) {
17 return std::string::npos;
18 }
19
20 size_t i;
21 for (i = l; i > 0; i--) {
22 if (std::tolower(text[i-1]) != std::tolower(pattern[i-1])) {
23 return std::string::npos;
24 }
25 }
26
27 return i;
28}
29
30// finds 'pattern' in 'text', case insensitive.
31// returns position or std::string::npos if not found.
32//
33// implements Boyer–Moore–Horspool
34size_t search_str_bmh(const std::string& text, const std::string& pattern) {
35
36 if (pattern.empty()) {
37 return 0;
38 }
39 if (text.empty() || pattern.size() > text.size()) {
40 return std::string::npos;
41 }
42
43 size_t t;
44 size_t tlen = text.size();
45
46 // simple case, no need to be too clever
47 if (pattern.size() == 1) {
48 int b = std::tolower(pattern[0]);
49 for (t = 0; t < tlen; t++) {
50 if (b == std::tolower(text[t])) {
51 return t;
52 }
53 }
54 return std::string::npos;
55 }
56
57
58 size_t plast = pattern.size() - 1;
59 size_t p;
60
61 // prepare skip-table
62 //
63 size_t skip[256];
64 for (p = 0; p < sizeof(skip)/sizeof(skip[0]); p++) {
65 skip[p] = plast + 1;
66 }
67 for (p = 0; p < plast; p++) {
68 skip[std::tolower(pattern[p])] = plast - p;
69 }
70
71 // match
72 for (t = 0; t + plast < tlen; ) {
73 for (p = plast; std::tolower(text[t+p]) == std::tolower(pattern[p]); p--) {
74 if (p == 0) {
75 return t+p;
76 }
77 }
78 t += skip[std::tolower(text[t+p])];
79 }
80
81 return std::string::npos;
82}
83
84
85// actually search function, depends on Mode
86size_t (*search_str)(const std::string&, const std::string&) = search_str_textstart;
87
88} // anonymous
89
90
91
92namespace FbTk {
93
94
95void MenuSearch::setMode(MenuSearch::Mode m) {
96 if (m == NOWHERE) {
97 search_str = search_str_nowhere;
98 } else if (m == SOMEWHERE) {
99 search_str = search_str_bmh;
100 } else {
101 search_str = search_str_textstart;
102 }
103}
104
105
106
107MenuSearch::MenuSearch(const std::vector<FbTk::MenuItem*>& items) :
108 m_items(items) {
109}
110
111size_t MenuSearch::size() const {
112 return pattern.size();
113}
114
115void MenuSearch::clear() {
116 pattern.clear();
117}
118
119void MenuSearch::add(char c) {
120 pattern.push_back(c);
121}
122
123void MenuSearch::backspace() {
124 size_t s = pattern.size();
125 if (s > 0) {
126 pattern.erase(s - 1, 1);
127 }
128}
129
130// is 'pattern' matching something?
131bool MenuSearch::has_match() {
132 size_t l = m_items.size();
133 size_t i;
134 for (i = 0; i < l; i++) {
135 if (!m_items[i]->isEnabled())
136 continue;
137 if (search_str(m_items[i]->iTypeString(), pattern) != std::string::npos) {
138 return true;
139 }
140 }
141 return false;
142}
143
144// would 'the_pattern' match something?
145bool MenuSearch::would_match(const std::string& the_pattern) {
146 size_t l = m_items.size();
147 size_t i;
148 for (i = 0; i < l; i++) {
149 if (!m_items[i]->isEnabled())
150 continue;
151 if (search_str(m_items[i]->iTypeString(), the_pattern) != std::string::npos) {
152 return true;
153 }
154 }
155 return false;
156}
157
158size_t MenuSearch::num_matches() {
159 size_t l = m_items.size();
160 size_t i, n;
161 for (i = 0, n = 0; i < l; i++) {
162 if (!m_items[i]->isEnabled())
163 continue;
164 if (search_str(m_items[i]->iTypeString(), pattern) != std::string::npos) {
165 n++;
166 }
167 }
168 return n;
169}
170
171
172// returns true if m_text matches against m_items[i] and stores
173// the position where it matches in the string. an empty
174// 'pattern' always matches
175bool MenuSearch::get_match(size_t i, size_t& idx) {
176 if (i > m_items.size()) {
177 return false;
178 }
179
180 if (pattern.empty())
181 return true;
182
183 idx = search_str(m_items[i]->iTypeString(), pattern);
184 return idx != std::string::npos;
185}
186
187
188
189
190//
191// resource-implementation related
192
193template<>
194std::string FbTk::Resource<FbTk::MenuSearch::Mode>::getString() const {
195
196 switch (m_value) {
197 case FbTk::MenuSearch::NOWHERE:
198 return "nowhere";
199 case FbTk::MenuSearch::SOMEWHERE:
200 return "somewhere";
201 default:
202 return "itemstart";
203 };
204}
205
206template<>
207void FbTk::Resource<FbTk::MenuSearch::Mode>::setFromString(const char *strval) {
208
209 std::string val = FbTk::StringUtil::toLower(strval);
210 if (val == "nowhere") {
211 m_value = FbTk::MenuSearch::NOWHERE;
212 } else if (val == "somewhere") {
213 m_value = FbTk::MenuSearch::SOMEWHERE;
214 } else {
215 setDefaultValue();
216 }
217
218 std::cerr << "** " << val << " " << m_value << "\n";
219}
220
221}