aboutsummaryrefslogtreecommitdiff
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
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.
-rw-r--r--src/FbTk/Makemodule.am2
-rw-r--r--src/FbTk/Menu.cc134
-rw-r--r--src/FbTk/Menu.hh9
-rw-r--r--src/FbTk/MenuSearch.cc221
-rw-r--r--src/FbTk/MenuSearch.hh60
-rw-r--r--src/FbTk/Resource.cc12
-rw-r--r--src/fluxbox.cc7
-rw-r--r--src/fluxbox.hh2
8 files changed, 298 insertions, 149 deletions
diff --git a/src/FbTk/Makemodule.am b/src/FbTk/Makemodule.am
index 57f70f2..3433429 100644
--- a/src/FbTk/Makemodule.am
+++ b/src/FbTk/Makemodule.am
@@ -102,6 +102,8 @@ libFbTk_a_SOURCES = \
102 src/FbTk/Menu.hh \ 102 src/FbTk/Menu.hh \
103 src/FbTk/MenuItem.cc \ 103 src/FbTk/MenuItem.cc \
104 src/FbTk/MenuItem.hh \ 104 src/FbTk/MenuItem.hh \
105 src/FbTk/MenuSearch.hh \
106 src/FbTk/MenuSearch.cc \
105 src/FbTk/MenuSeparator.cc \ 107 src/FbTk/MenuSeparator.cc \
106 src/FbTk/MenuSeparator.hh \ 108 src/FbTk/MenuSeparator.hh \
107 src/FbTk/MenuTheme.cc \ 109 src/FbTk/MenuTheme.cc \
diff --git a/src/FbTk/Menu.cc b/src/FbTk/Menu.cc
index 539c72c..cfbf049 100644
--- a/src/FbTk/Menu.cc
+++ b/src/FbTk/Menu.cc
@@ -69,143 +69,11 @@ void renderMenuPixmap(Pixmap& pm, FbTk::FbWindow* win, int width, int height, co
69 } 69 }
70} 70}
71 71
72
73// finds 'pattern' in 'text', case insensitive.
74// returns position or std::string::npos if not found.
75//
76// implements Boyer–Moore–Horspool
77size_t search_string(const std::string& text, const std::string& pattern) {
78
79 if (pattern.empty()) {
80 return 0;
81 }
82 if (text.empty() || pattern.size() > text.size()) {
83 return std::string::npos;
84 }
85
86 size_t t;
87 size_t tlen = text.size();
88
89 // simple case, no need to be too clever
90 if (pattern.size() == 1) {
91 int b = std::tolower(pattern[0]);
92 for (t = 0; t < tlen; t++) {
93 if (b == std::tolower(text[t])) {
94 return t;
95 }
96 }
97 return std::string::npos;
98 }
99
100
101 size_t plast = pattern.size() - 1;
102 size_t p;
103
104 // prepare skip-table
105 //
106 size_t skip[256];
107 for (p = 0; p < sizeof(skip)/sizeof(skip[0]); p++) {
108 skip[p] = plast + 1;
109 }
110 for (p = 0; p < plast; p++) {
111 skip[std::tolower(pattern[p])] = plast - p;
112 }
113
114 // match
115 for (t = 0; t + plast < tlen; ) {
116 for (p = plast; std::tolower(text[t+p]) == std::tolower(pattern[p]); p--) {
117 if (p == 0) {
118 return t+p;
119 }
120 }
121 t += skip[std::tolower(text[t+p])];
122 }
123
124 return std::string::npos;
125}
126
127} // end of anonymous namespace 72} // end of anonymous namespace
128 73
129 74
130namespace FbTk { 75namespace FbTk {
131 76
132// a small helper which applies search operations on a list of MenuItems*.
133// the former incarnation of this class was FbTk::TypeAhead in combination with
134// the now non-existent FbTk::SearchResults, but the complexity of these
135// are not needed for our use case. as a bonus we have less lose parts
136// flying around.
137class FbTk::Menu::TypeSearch {
138public:
139 TypeSearch(std::vector<FbTk::MenuItem*>& items) : m_items(items) { }
140
141 size_t size() const { return pattern.size(); }
142 void clear() { pattern.clear(); }
143 void add(char c) { pattern.push_back(c); }
144 void backspace() {
145 size_t s = pattern.size();
146 if (s > 0) {
147 pattern.erase(s - 1, 1);
148 }
149 }
150
151 // is 'pattern' matching something?
152 bool has_match() {
153 size_t l = m_items.size();
154 size_t i;
155 for (i = 0; i < l; i++) {
156 if (!m_items[i]->isEnabled())
157 continue;
158 if (search_string(m_items[i]->iTypeString(), pattern) != std::string::npos) {
159 return true;
160 }
161 }
162 return false;
163 }
164
165 // would 'the_pattern' match something?
166 bool would_match(const std::string& the_pattern) {
167 size_t l = m_items.size();
168 size_t i;
169 for (i = 0; i < l; i++) {
170 if (!m_items[i]->isEnabled())
171 continue;
172 if (search_string(m_items[i]->iTypeString(), the_pattern) != std::string::npos) {
173 return true;
174 }
175 }
176 return false;
177 }
178
179 size_t num_matches() {
180 size_t l = m_items.size();
181 size_t i, n;
182 for (i = 0, n = 0; i < l; i++) {
183 if (!m_items[i]->isEnabled())
184 continue;
185 if (search_string(m_items[i]->iTypeString(), pattern) != std::string::npos) {
186 n++;
187 }
188 }
189 return n;
190 }
191
192
193 // returns true if m_text matches against m_items[i] and stores
194 // the position where it matches in the string
195 bool get_match(size_t i, size_t& idx) {
196 if (i > m_items.size()) {
197 return false;
198 }
199 idx = search_string(m_items[i]->iTypeString(), pattern);
200 return idx != std::string::npos;
201 }
202
203 std::string pattern;
204private:
205 const std::vector<FbTk::MenuItem*>& m_items;
206};
207
208
209 77
210Menu* s_shown = 0; // if there's a menu open at all 78Menu* s_shown = 0; // if there's a menu open at all
211Menu* s_focused = 0; // holds currently focused menu 79Menu* s_focused = 0; // holds currently focused menu
@@ -250,7 +118,7 @@ Menu::Menu(FbTk::ThemeProxy<MenuTheme> &tm, ImageControl &imgctrl):
250 m_internal_menu = false; 118 m_internal_menu = false;
251 m_state.moving = m_state.closing = m_state.torn = m_state.visible = false; 119 m_state.moving = m_state.closing = m_state.torn = m_state.visible = false;
252 120
253 m_search.reset(new TypeSearch(m_items)); 121 m_search.reset(new MenuSearch(m_items));
254 122
255 m_x_move = m_y_move = 0; 123 m_x_move = m_y_move = 0;
256 m_which_sub = -1; 124 m_which_sub = -1;
diff --git a/src/FbTk/Menu.hh b/src/FbTk/Menu.hh
index 39440a8..8bb5fdd 100644
--- a/src/FbTk/Menu.hh
+++ b/src/FbTk/Menu.hh
@@ -37,9 +37,10 @@
37namespace FbTk { 37namespace FbTk {
38 38
39template <typename T> class Command; 39template <typename T> class Command;
40template <typename T> class RefCount;
40class MenuItem; 41class MenuItem;
42class MenuSearch;
41class ImageControl; 43class ImageControl;
42template <typename T> class RefCount;
43 44
44/// Base class for menus 45/// Base class for menus
45class Menu: public FbTk::EventHandler, FbTk::FbWindowRenderer { 46class Menu: public FbTk::EventHandler, FbTk::FbWindowRenderer {
@@ -187,10 +188,8 @@ private:
187 188
188 Menu *m_parent; 189 Menu *m_parent;
189 190
190 class TypeSearch; 191 std::vector<MenuItem*> m_items;
191 192 std::auto_ptr<MenuSearch> m_search;
192 std::vector<MenuItem *> m_items;
193 std::auto_ptr<TypeSearch> m_search;
194 193
195 struct State { 194 struct State {
196 bool moving; 195 bool moving;
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}
diff --git a/src/FbTk/MenuSearch.hh b/src/FbTk/MenuSearch.hh
new file mode 100644
index 0000000..d642929
--- /dev/null
+++ b/src/FbTk/MenuSearch.hh
@@ -0,0 +1,60 @@
1#ifndef _MENU_SEARCH_HH_
2#define _MENU_SEARCH_HH_
3
4#include <vector>
5#include <string>
6#include <cstddef>
7
8namespace FbTk {
9
10class MenuItem;
11
12
13// a small helper which applies search operations on a list of MenuItems*.
14// the former incarnation of this class was FbTk::TypeAhead in combination with
15// the now non-existent FbTk::SearchResults, but the complexity of these
16// are not needed for our use case. as a bonus we have less lose parts
17// flying around.
18
19class MenuSearch {
20public:
21
22 enum Mode {
23 NOWHERE,
24 ITEMSTART,
25 SOMEWHERE,
26
27 DEFAULT = ITEMSTART
28 };
29
30 static void setMode(Mode m);
31
32
33 MenuSearch(const std::vector<FbTk::MenuItem*>& items);
34
35 size_t size() const;
36 void clear();
37 void add(char c);
38 void backspace();
39
40 // is 'pattern' matching something?
41 bool has_match();
42
43 // would 'the_pattern' match something?
44 bool would_match(const std::string& the_pattern);
45
46 size_t num_matches();
47
48 // returns true if m_text matches against m_items[i] and stores
49 // the position where it matches in the string
50 bool get_match(size_t i, size_t& idx);
51
52 std::string pattern;
53private:
54 const std::vector<FbTk::MenuItem*>& m_items;
55};
56
57}
58
59
60#endif
diff --git a/src/FbTk/Resource.cc b/src/FbTk/Resource.cc
index 4be85de..af0c4c2 100644
--- a/src/FbTk/Resource.cc
+++ b/src/FbTk/Resource.cc
@@ -25,11 +25,7 @@
25#include "StringUtil.hh" 25#include "StringUtil.hh"
26 26
27#include <iostream> 27#include <iostream>
28#ifdef HAVE_CASSERT 28#include <cassert>
29 #include <cassert>
30#else
31 #include <assert.h>
32#endif
33 29
34using std::cerr; 30using std::cerr;
35using std::endl; 31using std::endl;
@@ -38,9 +34,9 @@ using std::string;
38namespace FbTk { 34namespace FbTk {
39 35
40ResourceManager::ResourceManager(const char *filename, bool lock_db) : 36ResourceManager::ResourceManager(const char *filename, bool lock_db) :
41 m_db_lock(0), 37 m_db_lock(0),
42 m_database(0), 38 m_database(0),
43 m_filename(filename ? filename : "") 39 m_filename(filename ? filename : "")
44{ 40{
45 static bool xrm_initialized = false; 41 static bool xrm_initialized = false;
46 if (!xrm_initialized) { 42 if (!xrm_initialized) {
diff --git a/src/fluxbox.cc b/src/fluxbox.cc
index 8b8b26c..7b73360 100644
--- a/src/fluxbox.cc
+++ b/src/fluxbox.cc
@@ -252,6 +252,7 @@ Fluxbox::Config::Config(FbTk::ResourceManager& rm, const std::string& path) :
252 slit_file(rm, path + "/slitlist", "session.slitlistFile", "Session.SlitlistFile"), 252 slit_file(rm, path + "/slitlist", "session.slitlistFile", "Session.SlitlistFile"),
253 apps_file(rm, path + "/apps", "session.appsFile", "Session.AppsFile"), 253 apps_file(rm, path + "/apps", "session.appsFile", "Session.AppsFile"),
254 tabs_attach_area(rm, ATTACH_AREA_WINDOW, "session.tabsAttachArea", "Session.TabsAttachArea"), 254 tabs_attach_area(rm, ATTACH_AREA_WINDOW, "session.tabsAttachArea", "Session.TabsAttachArea"),
255 menusearch(rm, FbTk::MenuSearch::DEFAULT, "session.menuSearch", "Session.MenuSearch"),
255 cache_life(rm, 5, "session.cacheLife", "Session.CacheLife"), 256 cache_life(rm, 5, "session.cacheLife", "Session.CacheLife"),
256 cache_max(rm, 200, "session.cacheMax", "Session.CacheMax"), 257 cache_max(rm, 200, "session.cacheMax", "Session.CacheMax"),
257 auto_raise_delay(rm, 250, "session.autoRaiseDelay", "Session.AutoRaiseDelay") { 258 auto_raise_delay(rm, 250, "session.autoRaiseDelay", "Session.AutoRaiseDelay") {
@@ -360,7 +361,7 @@ Fluxbox::Fluxbox(int argc, char **argv,
360 // Note: this needs to be done before creating screens 361 // Note: this needs to be done before creating screens
361 m_key.reset(new Keys); 362 m_key.reset(new Keys);
362 m_key->reconfigure(); 363 m_key->reconfigure();
363 364 FbTk::MenuSearch::setMode(*m_config.menusearch);
364 365
365 unsigned int opts = OPT_SLIT|OPT_TOOLBAR; 366 unsigned int opts = OPT_SLIT|OPT_TOOLBAR;
366 vector<int> screens; 367 vector<int> screens;
@@ -541,7 +542,6 @@ void Fluxbox::eventLoop() {
541 } else { 542 } else {
542 FbTk::Timer::updateTimers(ConnectionNumber(disp)); 543 FbTk::Timer::updateTimers(ConnectionNumber(disp));
543 } 544 }
544
545 } 545 }
546} 546}
547 547
@@ -862,7 +862,7 @@ void Fluxbox::handleClientMessage(XClientMessageEvent &ce) {
862 atom = XGetAtomName(FbTk::App::instance()->display(), ce.message_type); 862 atom = XGetAtomName(FbTk::App::instance()->display(), ce.message_type);
863 863
864 fbdbg<<__FILE__<<"("<<__LINE__<<"): ClientMessage. data.l[0]=0x"<<hex<<ce.data.l[0]<< 864 fbdbg<<__FILE__<<"("<<__LINE__<<"): ClientMessage. data.l[0]=0x"<<hex<<ce.data.l[0]<<
865 " message_type=0x"<<ce.message_type<<dec<<" = \""<<atom<<"\""<<endl; 865 " message_type=0x"<<ce.message_type<<dec<<" = \""<<atom<<"\""<<endl;
866 866
867 if (ce.message_type && atom) XFree((char *) atom); 867 if (ce.message_type && atom) XFree((char *) atom);
868#endif // DEBUG 868#endif // DEBUG
@@ -1266,6 +1266,7 @@ void Fluxbox::real_reconfigure() {
1266 STLUtil::forAll(m_screens, mem_fun(&BScreen::reconfigure)); 1266 STLUtil::forAll(m_screens, mem_fun(&BScreen::reconfigure));
1267 m_key->reconfigure(); 1267 m_key->reconfigure();
1268 STLUtil::forAll(m_atomhandler, mem_fun(&AtomHandler::reconfigure)); 1268 STLUtil::forAll(m_atomhandler, mem_fun(&AtomHandler::reconfigure));
1269 FbTk::MenuSearch::setMode(*m_config.menusearch);
1269} 1270}
1270 1271
1271BScreen *Fluxbox::findScreen(int id) { 1272BScreen *Fluxbox::findScreen(int id) {
diff --git a/src/fluxbox.hh b/src/fluxbox.hh
index df0335a..2d2e670 100644
--- a/src/fluxbox.hh
+++ b/src/fluxbox.hh
@@ -29,6 +29,7 @@
29#include "FbTk/Resource.hh" 29#include "FbTk/Resource.hh"
30#include "FbTk/Timer.hh" 30#include "FbTk/Timer.hh"
31#include "FbTk/Signal.hh" 31#include "FbTk/Signal.hh"
32#include "FbTk/MenuSearch.hh"
32 33
33#include "AttentionNoticeHandler.hh" 34#include "AttentionNoticeHandler.hh"
34 35
@@ -251,6 +252,7 @@ private:
251 FbTk::Resource<std::string> apps_file; 252 FbTk::Resource<std::string> apps_file;
252 253
253 FbTk::Resource<TabsAttachArea> tabs_attach_area; 254 FbTk::Resource<TabsAttachArea> tabs_attach_area;
255 FbTk::Resource<FbTk::MenuSearch::Mode> menusearch;
254 FbTk::Resource<unsigned int> cache_life; 256 FbTk::Resource<unsigned int> cache_life;
255 FbTk::Resource<unsigned int> cache_max; 257 FbTk::Resource<unsigned int> cache_max;
256 FbTk::Resource<time_t> auto_raise_delay; 258 FbTk::Resource<time_t> auto_raise_delay;