diff options
author | markt <markt> | 2007-03-03 19:35:34 (GMT) |
---|---|---|
committer | markt <markt> | 2007-03-03 19:35:34 (GMT) |
commit | a233229bd854d2e925ca0f1e86846ff9fde46fcd (patch) | |
tree | a30fffa38994e8ee12096c31a256ba6b3fbfa2c6 /src/FbTk | |
parent | d6a7bd786fd657e16c1ebad5c515d60ba1368d8a (diff) | |
download | fluxbox-a233229bd854d2e925ca0f1e86846ff9fde46fcd.zip fluxbox-a233229bd854d2e925ca0f1e86846ff9fde46fcd.tar.bz2 |
added support for typeahead in menus
Diffstat (limited to 'src/FbTk')
-rw-r--r-- | src/FbTk/ITypeAheadable.hh | 49 | ||||
-rw-r--r-- | src/FbTk/Makefile.am | 1 | ||||
-rw-r--r-- | src/FbTk/Menu.cc | 195 | ||||
-rw-r--r-- | src/FbTk/Menu.hh | 17 | ||||
-rw-r--r-- | src/FbTk/MenuItem.cc | 38 | ||||
-rw-r--r-- | src/FbTk/MenuItem.hh | 15 | ||||
-rw-r--r-- | src/FbTk/MenuTheme.cc | 4 | ||||
-rw-r--r-- | src/FbTk/MenuTheme.hh | 7 | ||||
-rw-r--r-- | src/FbTk/SearchResult.cc | 51 | ||||
-rw-r--r-- | src/FbTk/SearchResult.hh | 53 | ||||
-rw-r--r-- | src/FbTk/TypeAhead.hh | 174 |
11 files changed, 528 insertions, 76 deletions
diff --git a/src/FbTk/ITypeAheadable.hh b/src/FbTk/ITypeAheadable.hh new file mode 100644 index 0000000..4a5b7cc --- /dev/null +++ b/src/FbTk/ITypeAheadable.hh | |||
@@ -0,0 +1,49 @@ | |||
1 | // ITypeAheadable.hh for FbTk - Fluxbox Toolkit | ||
2 | // Copyright (c) 2007 Fluxbox Team (fluxgen at fluxbox dot org) | ||
3 | // | ||
4 | // Permission is hereby granted, free of charge, to any person obtaining a | ||
5 | // copy of this software and associated documentation files (the "Software"), | ||
6 | // to deal in the Software without restriction, including without limitation | ||
7 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
8 | // and/or sell copies of the Software, and to permit persons to whom the | ||
9 | // Software is furnished to do so, subject to the following conditions: | ||
10 | // | ||
11 | // The above copyright notice and this permission notice shall be included in | ||
12 | // all copies or substantial portions of the Software. | ||
13 | // | ||
14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
17 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
19 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
20 | // DEALINGS IN THE SOFTWARE. | ||
21 | |||
22 | #ifndef FBTK_ITYPEAHEADABLE_HH | ||
23 | #define FBTK_ITYPEAHEADABLE_HH | ||
24 | |||
25 | #include <string> | ||
26 | |||
27 | namespace FbTk { | ||
28 | |||
29 | // abstract base class providing access and validation | ||
30 | class ITypeAheadable { | ||
31 | public: | ||
32 | virtual ~ITypeAheadable() { } | ||
33 | |||
34 | virtual const std::string &iTypeString() const = 0; | ||
35 | virtual bool isEnabled() { return true; } | ||
36 | char iTypeChar(size_t i) const { return iTypeString()[i]; } | ||
37 | bool iTypeCheckStringSize(size_t sz) const { | ||
38 | return (iTypeString().size() > sz); | ||
39 | } | ||
40 | bool iTypeCompareChar(char ch, size_t sz) const { | ||
41 | return (bool)iTypeCheckStringSize(sz) && | ||
42 | tolower(iTypeChar(sz)) == tolower(ch); | ||
43 | } | ||
44 | |||
45 | }; | ||
46 | |||
47 | } // end namespace FbTk | ||
48 | |||
49 | #endif // FBTK_ITYPEAHEADABLE_HH | ||
diff --git a/src/FbTk/Makefile.am b/src/FbTk/Makefile.am index 5662370..fadc998 100644 --- a/src/FbTk/Makefile.am +++ b/src/FbTk/Makefile.am | |||
@@ -51,6 +51,7 @@ libFbTk_a_SOURCES = App.hh App.cc Color.cc Color.hh Command.hh \ | |||
51 | MenuSeparator.hh MenuSeparator.cc \ | 51 | MenuSeparator.hh MenuSeparator.cc \ |
52 | MenuIcon.hh MenuIcon.cc \ | 52 | MenuIcon.hh MenuIcon.cc \ |
53 | stringstream.hh \ | 53 | stringstream.hh \ |
54 | TypeAhead.hh SearchResult.hh SearchResult.cc ITypeAheadable.hh \ | ||
54 | Select2nd.hh \ | 55 | Select2nd.hh \ |
55 | CachedPixmap.hh CachedPixmap.cc \ | 56 | CachedPixmap.hh CachedPixmap.cc \ |
56 | ${xpm_SOURCE} \ | 57 | ${xpm_SOURCE} \ |
diff --git a/src/FbTk/Menu.cc b/src/FbTk/Menu.cc index e72b750..c60286a 100644 --- a/src/FbTk/Menu.cc +++ b/src/FbTk/Menu.cc | |||
@@ -110,6 +110,7 @@ Menu::Menu(MenuTheme &tm, ImageControl &imgctrl): | |||
110 | m_visible = false; | 110 | m_visible = false; |
111 | 111 | ||
112 | 112 | ||
113 | m_type_ahead.init(menuitems); | ||
113 | 114 | ||
114 | menu.x_move = | 115 | menu.x_move = |
115 | menu.y_move = 0; | 116 | menu.y_move = 0; |
@@ -205,15 +206,24 @@ int Menu::insert(const FbString &label, Menu *submenu, int pos) { | |||
205 | } | 206 | } |
206 | 207 | ||
207 | int Menu::insert(MenuItem *item, int pos) { | 208 | int Menu::insert(MenuItem *item, int pos) { |
209 | if (item == 0) | ||
210 | return menuitems.size(); | ||
208 | if (pos == -1) { | 211 | if (pos == -1) { |
212 | item->setIndex(menuitems.size()); | ||
209 | menuitems.push_back(item); | 213 | menuitems.push_back(item); |
210 | } else { | 214 | } else { |
211 | menuitems.insert(menuitems.begin() + pos, item); | 215 | menuitems.insert(menuitems.begin() + pos, item); |
216 | fixMenuItemIndices(); | ||
212 | } | 217 | } |
213 | m_need_update = true; // we need to redraw the menu | 218 | m_need_update = true; // we need to redraw the menu |
214 | return menuitems.size(); | 219 | return menuitems.size(); |
215 | } | 220 | } |
216 | 221 | ||
222 | void Menu::fixMenuItemIndices() { | ||
223 | for (size_t i = 0; i < menuitems.size(); i++) | ||
224 | menuitems[i]->setIndex(i); | ||
225 | } | ||
226 | |||
217 | int Menu::remove(unsigned int index) { | 227 | int Menu::remove(unsigned int index) { |
218 | if (index >= menuitems.size()) { | 228 | if (index >= menuitems.size()) { |
219 | #ifdef DEBUG | 229 | #ifdef DEBUG |
@@ -229,6 +239,9 @@ int Menu::remove(unsigned int index) { | |||
229 | 239 | ||
230 | if (item) { | 240 | if (item) { |
231 | menuitems.erase(it); | 241 | menuitems.erase(it); |
242 | // avoid O(n^2) algorithm with removeAll() | ||
243 | if (index != menuitems.size()) | ||
244 | fixMenuItemIndices(); | ||
232 | 245 | ||
233 | if (item->submenu() != 0) { | 246 | if (item->submenu() != 0) { |
234 | Menu *tmp = item->submenu(); | 247 | Menu *tmp = item->submenu(); |
@@ -257,10 +270,8 @@ int Menu::remove(unsigned int index) { | |||
257 | } | 270 | } |
258 | 271 | ||
259 | void Menu::removeAll() { | 272 | void Menu::removeAll() { |
260 | while (!menuitems.empty()) { | 273 | while (!menuitems.empty()) |
261 | remove(0); | 274 | remove(menuitems.size()-1); |
262 | } | ||
263 | m_need_update = true; | ||
264 | } | 275 | } |
265 | 276 | ||
266 | void Menu::raise() { | 277 | void Menu::raise() { |
@@ -271,55 +282,48 @@ void Menu::lower() { | |||
271 | menu.window.lower(); | 282 | menu.window.lower(); |
272 | } | 283 | } |
273 | 284 | ||
274 | void Menu::nextItem(int failsafe) { | 285 | void Menu::cycleItems(bool reverse) { |
275 | if (menuitems.empty()) | 286 | Menuitems vec; |
276 | return; | 287 | if (m_type_ahead.stringSize()) |
277 | 288 | vec = m_matches; | |
278 | if (failsafe == -1) | 289 | else |
279 | failsafe = m_active_index; | 290 | vec = menuitems; |
280 | 291 | ||
281 | int old_active_index = m_active_index; | 292 | if (vec.size() < 1) |
282 | m_active_index += 1; | 293 | return; |
283 | if (!validIndex(m_active_index)) | ||
284 | m_active_index = 0; | ||
285 | 294 | ||
286 | if (validIndex(old_active_index) && | 295 | // find the next item to select |
287 | menuitems[old_active_index] != 0) { | 296 | // this algorithm assumes menuitems are sorted properly |
288 | if (menuitems[old_active_index]->submenu()) { | 297 | int new_index = -1; |
289 | // we need to do this explicitly on the menu.window | 298 | bool passed = !validIndex(m_active_index); |
290 | // since it might hide the parent if we use Menu::hide | 299 | for (size_t i = 0; i < vec.size(); i++) { |
291 | menuitems[old_active_index]->submenu()->internal_hide(); | 300 | if (!isItemSelectable(vec[i]->getIndex()) || |
301 | vec[i]->getIndex() == m_active_index) | ||
302 | continue; | ||
303 | |||
304 | // determine whether or not we've passed the active index | ||
305 | if (!passed && vec[i]->getIndex() > m_active_index) { | ||
306 | if (reverse && new_index != -1) | ||
307 | break; | ||
308 | passed = true; | ||
292 | } | 309 | } |
293 | clearItem(old_active_index); | ||
294 | } | ||
295 | 310 | ||
296 | if (menuitems[m_active_index] == 0) { | 311 | // decide if we want to keep this item |
297 | m_active_index = -1; | 312 | if (passed && !reverse) { |
298 | return; | 313 | new_index = vec[i]->getIndex(); |
299 | } | 314 | break; |
300 | 315 | } else if (reverse || new_index == -1) | |
301 | if (!isItemSelectable(m_active_index) && m_active_index != failsafe) { | 316 | new_index = vec[i]->getIndex(); |
302 | nextItem(failsafe); | ||
303 | return; | ||
304 | } | 317 | } |
305 | 318 | ||
306 | clearItem(m_active_index); | 319 | if (new_index == -1) |
307 | |||
308 | } | ||
309 | |||
310 | void Menu::prevItem(int failsafe) { | ||
311 | if (menuitems.empty()) | ||
312 | return; | 320 | return; |
313 | 321 | ||
314 | if (failsafe == -1) | 322 | // clear the items and close any open submenus |
315 | failsafe = m_active_index; | ||
316 | |||
317 | int old_active_index = m_active_index; | 323 | int old_active_index = m_active_index; |
318 | m_active_index -= 1; | 324 | m_active_index = new_index; |
319 | if (!validIndex(m_active_index)) | 325 | if (validIndex(old_active_index) && |
320 | m_active_index = menuitems.size() - 1; | 326 | menuitems[old_active_index] != 0) { |
321 | |||
322 | if (validIndex(old_active_index)) { | ||
323 | if (menuitems[old_active_index]->submenu()) { | 327 | if (menuitems[old_active_index]->submenu()) { |
324 | // we need to do this explicitly on the menu.window | 328 | // we need to do this explicitly on the menu.window |
325 | // since it might hide the parent if we use Menu::hide | 329 | // since it might hide the parent if we use Menu::hide |
@@ -327,19 +331,7 @@ void Menu::prevItem(int failsafe) { | |||
327 | } | 331 | } |
328 | clearItem(old_active_index); | 332 | clearItem(old_active_index); |
329 | } | 333 | } |
330 | 334 | clearItem(new_index); | |
331 | if (menuitems[m_active_index] == 0) { | ||
332 | m_active_index = -1; | ||
333 | return; | ||
334 | } | ||
335 | |||
336 | if (!isItemSelectable(m_active_index) && m_active_index != failsafe) { | ||
337 | prevItem(failsafe); | ||
338 | return; | ||
339 | } | ||
340 | |||
341 | clearItem(m_active_index); | ||
342 | |||
343 | } | 335 | } |
344 | 336 | ||
345 | void Menu::enterSubmenu() { | 337 | void Menu::enterSubmenu() { |
@@ -356,7 +348,7 @@ void Menu::enterSubmenu() { | |||
356 | drawSubmenu(m_active_index); | 348 | drawSubmenu(m_active_index); |
357 | submenu->grabInputFocus(); | 349 | submenu->grabInputFocus(); |
358 | submenu->m_active_index = -1; // so we land on 0 after nextItem() | 350 | submenu->m_active_index = -1; // so we land on 0 after nextItem() |
359 | submenu->nextItem(); | 351 | submenu->cycleItems(false); |
360 | } | 352 | } |
361 | 353 | ||
362 | void Menu::enterParent() { | 354 | void Menu::enterParent() { |
@@ -1024,34 +1016,59 @@ void Menu::keyPressEvent(XKeyEvent &event) { | |||
1024 | 1016 | ||
1025 | switch (ks) { | 1017 | switch (ks) { |
1026 | case XK_Up: | 1018 | case XK_Up: |
1027 | prevItem(); | 1019 | resetTypeAhead(); |
1020 | cycleItems(true); | ||
1028 | break; | 1021 | break; |
1029 | case XK_Down: | 1022 | case XK_Down: |
1030 | nextItem(); | 1023 | resetTypeAhead(); |
1024 | cycleItems(false); | ||
1031 | break; | 1025 | break; |
1032 | case XK_Left: // enter parent if we have one | 1026 | case XK_Left: // enter parent if we have one |
1027 | resetTypeAhead(); | ||
1033 | enterParent(); | 1028 | enterParent(); |
1034 | break; | 1029 | break; |
1035 | case XK_Right: // enter submenu if we have one | 1030 | case XK_Right: // enter submenu if we have one |
1031 | resetTypeAhead(); | ||
1036 | enterSubmenu(); | 1032 | enterSubmenu(); |
1037 | break; | 1033 | break; |
1038 | case XK_Escape: // close menu | 1034 | case XK_Escape: // close menu |
1035 | m_type_ahead.reset(); | ||
1039 | hide(); | 1036 | hide(); |
1040 | break; | 1037 | break; |
1038 | case XK_BackSpace: | ||
1039 | m_type_ahead.putBackSpace(); | ||
1040 | drawTypeAheadItems(); | ||
1041 | break; | ||
1041 | case XK_KP_Enter: | 1042 | case XK_KP_Enter: |
1042 | case XK_Return: | 1043 | case XK_Return: |
1043 | // send fake button 1 click | 1044 | resetTypeAhead(); |
1044 | if (validIndex(m_active_index) && | 1045 | if (validIndex(m_active_index) && |
1045 | isItemEnabled(m_active_index)) { | 1046 | isItemEnabled(m_active_index)) { |
1046 | if (event.state & ShiftMask) | 1047 | if (menuitems[m_active_index]->submenu() != 0) |
1047 | menuitems[m_active_index]->click(3, event.time); | 1048 | enterSubmenu(); |
1048 | else | 1049 | else { |
1049 | menuitems[m_active_index]->click(1, event.time); | 1050 | // send fake button click |
1050 | m_need_update = true; | 1051 | int button = (event.state & ShiftMask) ? 3 : 1; |
1051 | updateMenu(); | 1052 | find(m_active_index)->click(button, event.time); |
1053 | m_need_update = true; | ||
1054 | updateMenu(); | ||
1055 | } | ||
1052 | } | 1056 | } |
1053 | break; | 1057 | break; |
1058 | case XK_Tab: | ||
1059 | case XK_ISO_Left_Tab: | ||
1060 | m_type_ahead.seek(); | ||
1061 | cycleItems((bool)(event.state & ShiftMask)); | ||
1062 | drawTypeAheadItems(); | ||
1063 | break; | ||
1054 | default: | 1064 | default: |
1065 | m_type_ahead.putCharacter(keychar[0]); | ||
1066 | // if current item doesn't match new search string, find the next one | ||
1067 | drawTypeAheadItems(); | ||
1068 | if (!m_matches.empty() && (!validIndex(m_active_index) || | ||
1069 | std::find(m_matches.begin(), m_matches.end(), | ||
1070 | find(m_active_index)) == m_matches.end())) | ||
1071 | cycleItems(false); | ||
1055 | break; | 1072 | break; |
1056 | } | 1073 | } |
1057 | } | 1074 | } |
@@ -1151,7 +1168,7 @@ void Menu::renderForeground(FbWindow &win, FbDrawable &drawable) { | |||
1151 | // thus sometimes it won't perform the actual clear operation | 1168 | // thus sometimes it won't perform the actual clear operation |
1152 | // nothing in here should be rendered transparently | 1169 | // nothing in here should be rendered transparently |
1153 | // (unless you use a caching pixmap, which I think we should avoid) | 1170 | // (unless you use a caching pixmap, which I think we should avoid) |
1154 | void Menu::clearItem(int index, bool clear) { | 1171 | void Menu::clearItem(int index, bool clear, int search_index) { |
1155 | if (!validIndex(index)) | 1172 | if (!validIndex(index)) |
1156 | return; | 1173 | return; |
1157 | 1174 | ||
@@ -1160,9 +1177,16 @@ void Menu::clearItem(int index, bool clear) { | |||
1160 | int item_x = (sbl * item_w), item_y = (i * item_h); | 1177 | int item_x = (sbl * item_w), item_y = (i * item_h); |
1161 | bool highlight = (index == m_active_index && isItemSelectable(index)); | 1178 | bool highlight = (index == m_active_index && isItemSelectable(index)); |
1162 | 1179 | ||
1180 | if (search_index < 0) | ||
1181 | // find if we need to underline the item | ||
1182 | search_index = std::find(m_matches.begin(), m_matches.end(), | ||
1183 | find(index)) - m_matches.begin(); | ||
1184 | |||
1163 | // don't highlight if moving, doesn't work with alpha on | 1185 | // don't highlight if moving, doesn't work with alpha on |
1164 | if (highlight && !m_moving) { | 1186 | if (highlight && !m_moving) { |
1165 | highlightItem(index); | 1187 | highlightItem(index); |
1188 | if (search_index < (int)m_matches.size()) | ||
1189 | drawLine(index, m_type_ahead.stringSize()); | ||
1166 | return; | 1190 | return; |
1167 | } else if (clear) | 1191 | } else if (clear) |
1168 | menu.frame.clearArea(item_x, item_y, item_w, item_h); | 1192 | menu.frame.clearArea(item_x, item_y, item_w, item_h); |
@@ -1173,6 +1197,9 @@ void Menu::clearItem(int index, bool clear) { | |||
1173 | item->draw(menu.frame, theme(), highlight, | 1197 | item->draw(menu.frame, theme(), highlight, |
1174 | true, false, item_x, item_y, | 1198 | true, false, item_x, item_y, |
1175 | item_w, item_h); | 1199 | item_w, item_h); |
1200 | |||
1201 | if (search_index < (int)m_matches.size()) | ||
1202 | drawLine(index, m_type_ahead.stringSize()); | ||
1176 | } | 1203 | } |
1177 | 1204 | ||
1178 | // Area must have been cleared before calling highlight | 1205 | // Area must have been cleared before calling highlight |
@@ -1206,4 +1233,36 @@ void Menu::highlightItem(int index) { | |||
1206 | 1233 | ||
1207 | } | 1234 | } |
1208 | 1235 | ||
1236 | void Menu::resetTypeAhead() { | ||
1237 | Menuitems vec = m_matches; | ||
1238 | Menuitems::iterator it = vec.begin(); | ||
1239 | m_type_ahead.reset(); | ||
1240 | m_matches.clear(); | ||
1241 | |||
1242 | for (; it != vec.end(); it++) | ||
1243 | clearItem((*it)->getIndex(), true, 1); | ||
1244 | } | ||
1245 | |||
1246 | void Menu::drawTypeAheadItems() { | ||
1247 | // remove underlines from old matches | ||
1248 | for (size_t i = 0; i < m_matches.size(); i++) | ||
1249 | clearItem(m_matches[i]->getIndex(), true, m_matches.size()); | ||
1250 | |||
1251 | m_matches = m_type_ahead.matched(); | ||
1252 | for (size_t j = 0; j < m_matches.size(); j++) | ||
1253 | clearItem(m_matches[j]->getIndex(), false, j); | ||
1254 | } | ||
1255 | |||
1256 | // underline menuitem[index] with respect to matchstringsize size | ||
1257 | void Menu::drawLine(int index, int size){ | ||
1258 | if (!validIndex(index)) | ||
1259 | return; | ||
1260 | |||
1261 | int sbl = index / menu.persub, i = index - (sbl * menu.persub); | ||
1262 | int item_x = (sbl * menu.item_w), item_y = (i * theme().itemHeight()); | ||
1263 | |||
1264 | FbTk::MenuItem *item = find(index); | ||
1265 | item->drawLine(menu.frame, theme(), size, item_x, item_y, menu.item_w); | ||
1266 | } | ||
1267 | |||
1209 | }; // end namespace FbTk | 1268 | }; // end namespace FbTk |
diff --git a/src/FbTk/Menu.hh b/src/FbTk/Menu.hh index 43b77a1..c77b0c0 100644 --- a/src/FbTk/Menu.hh +++ b/src/FbTk/Menu.hh | |||
@@ -41,6 +41,7 @@ | |||
41 | #include "MenuTheme.hh" | 41 | #include "MenuTheme.hh" |
42 | #include "Timer.hh" | 42 | #include "Timer.hh" |
43 | #include "FbString.hh" | 43 | #include "FbString.hh" |
44 | #include "TypeAhead.hh" | ||
44 | 45 | ||
45 | namespace FbTk { | 46 | namespace FbTk { |
46 | 47 | ||
@@ -87,10 +88,8 @@ public: | |||
87 | virtual void raise(); | 88 | virtual void raise(); |
88 | /// lower this window | 89 | /// lower this window |
89 | virtual void lower(); | 90 | virtual void lower(); |
90 | /// select next item | 91 | /// cycle through menuitems |
91 | void nextItem(int failsafe = -1); | 92 | void cycleItems(bool reverse); |
92 | /// select previous item | ||
93 | void prevItem(int failsafe = -1); | ||
94 | void enterSubmenu(); | 93 | void enterSubmenu(); |
95 | void enterParent(); | 94 | void enterParent(); |
96 | 95 | ||
@@ -186,7 +185,7 @@ protected: | |||
186 | int drawItem(FbDrawable &pm, unsigned int index, | 185 | int drawItem(FbDrawable &pm, unsigned int index, |
187 | bool highlight = false, | 186 | bool highlight = false, |
188 | bool exclusive_drawable = false); | 187 | bool exclusive_drawable = false); |
189 | void clearItem(int index, bool clear = true); | 188 | void clearItem(int index, bool clear = true, int search_index = -1); |
190 | void highlightItem(int index); | 189 | void highlightItem(int index); |
191 | virtual void redrawTitle(FbDrawable &pm); | 190 | virtual void redrawTitle(FbDrawable &pm); |
192 | virtual void redrawFrame(FbDrawable &pm); | 191 | virtual void redrawFrame(FbDrawable &pm); |
@@ -209,6 +208,14 @@ private: | |||
209 | ImageControl &m_image_ctrl; | 208 | ImageControl &m_image_ctrl; |
210 | Menuitems menuitems; | 209 | Menuitems menuitems; |
211 | 210 | ||
211 | TypeAhead<Menuitems, MenuItem *> m_type_ahead; | ||
212 | Menuitems m_matches; | ||
213 | |||
214 | void resetTypeAhead(); | ||
215 | void drawTypeAheadItems(); | ||
216 | void drawLine(int index, int size); | ||
217 | void fixMenuItemIndices(); | ||
218 | |||
212 | int m_screen_x, m_screen_y; | 219 | int m_screen_x, m_screen_y; |
213 | unsigned int m_screen_width, m_screen_height; | 220 | unsigned int m_screen_width, m_screen_height; |
214 | bool m_moving; ///< if we're moving/draging or not | 221 | bool m_moving; ///< if we're moving/draging or not |
diff --git a/src/FbTk/MenuItem.cc b/src/FbTk/MenuItem.cc index a2c3fe2..82dd155 100644 --- a/src/FbTk/MenuItem.cc +++ b/src/FbTk/MenuItem.cc | |||
@@ -38,6 +38,44 @@ void MenuItem::click(int button, int time) { | |||
38 | m_command->execute(); | 38 | m_command->execute(); |
39 | } | 39 | } |
40 | 40 | ||
41 | void MenuItem::drawLine(FbDrawable &draw, const MenuTheme &theme, size_t size, | ||
42 | int text_x, int text_y, unsigned int width) const { | ||
43 | |||
44 | unsigned int height = theme.itemHeight(); | ||
45 | int bevelW = theme.bevelWidth(); | ||
46 | |||
47 | int font_top = (height - theme.frameFont().height())/2; | ||
48 | int underline_height = font_top + theme.frameFont().ascent() + 2; | ||
49 | int bottom = height - bevelW - 1; | ||
50 | |||
51 | text_y += bottom > underline_height ? underline_height : bottom; | ||
52 | int text_w = theme.frameFont().textWidth(m_label.c_str(), m_label.size()); | ||
53 | |||
54 | // width of the searchstring | ||
55 | size = size > m_label.length() ? m_label.length() : size; | ||
56 | std::string search_string = m_label.substr(0,size); | ||
57 | int search_string_w = theme.frameFont().textWidth(search_string.c_str(), size); | ||
58 | |||
59 | // pay attention to the text justification | ||
60 | switch(theme.frameFontJustify()) { | ||
61 | case FbTk::LEFT: | ||
62 | text_x += bevelW + height + 1; | ||
63 | break; | ||
64 | case FbTk::RIGHT: | ||
65 | text_x += width - (height + bevelW + text_w); | ||
66 | break; | ||
67 | default: //center | ||
68 | text_x += ((width + 1 - text_w) / 2); | ||
69 | break; | ||
70 | } | ||
71 | |||
72 | // avoid drawing an ugly dot | ||
73 | if (size != 0) | ||
74 | draw.drawLine(theme.frameUnderlineGC().gc(), | ||
75 | text_x, text_y, text_x + search_string_w, text_y); | ||
76 | |||
77 | } | ||
78 | |||
41 | void MenuItem::draw(FbDrawable &draw, | 79 | void MenuItem::draw(FbDrawable &draw, |
42 | const MenuTheme &theme, | 80 | const MenuTheme &theme, |
43 | bool highlight, bool draw_foreground, bool draw_background, | 81 | bool highlight, bool draw_foreground, bool draw_background, |
diff --git a/src/FbTk/MenuItem.hh b/src/FbTk/MenuItem.hh index d73c270..701b624 100644 --- a/src/FbTk/MenuItem.hh +++ b/src/FbTk/MenuItem.hh | |||
@@ -27,6 +27,7 @@ | |||
27 | #include "RefCount.hh" | 27 | #include "RefCount.hh" |
28 | #include "Command.hh" | 28 | #include "Command.hh" |
29 | #include "PixmapWithMask.hh" | 29 | #include "PixmapWithMask.hh" |
30 | #include "ITypeAheadable.hh" | ||
30 | #include "FbString.hh" | 31 | #include "FbString.hh" |
31 | 32 | ||
32 | #include <string> | 33 | #include <string> |
@@ -39,7 +40,7 @@ class MenuTheme; | |||
39 | class FbDrawable; | 40 | class FbDrawable; |
40 | 41 | ||
41 | /// An interface for a menu item in Menu | 42 | /// An interface for a menu item in Menu |
42 | class MenuItem { | 43 | class MenuItem : public FbTk::ITypeAheadable { |
43 | public: | 44 | public: |
44 | MenuItem() | 45 | MenuItem() |
45 | : m_label(""), | 46 | : m_label(""), |
@@ -105,6 +106,17 @@ public: | |||
105 | virtual bool isEnabled() const { return m_enabled; } | 106 | virtual bool isEnabled() const { return m_enabled; } |
106 | virtual bool isSelected() const { return m_selected; } | 107 | virtual bool isSelected() const { return m_selected; } |
107 | virtual bool isToggleItem() const { return m_toggle_item; } | 108 | virtual bool isToggleItem() const { return m_toggle_item; } |
109 | |||
110 | // iType functions | ||
111 | virtual inline void setIndex(int index) { m_index = index; } | ||
112 | virtual inline int getIndex() { return m_index; } | ||
113 | inline const std::string &iTypeString() const { return m_label; } | ||
114 | virtual void drawLine(FbDrawable &draw, | ||
115 | const MenuTheme &theme, | ||
116 | size_t size, | ||
117 | int text_x, int text_y, | ||
118 | unsigned int width) const; | ||
119 | |||
108 | virtual unsigned int width(const MenuTheme &theme) const; | 120 | virtual unsigned int width(const MenuTheme &theme) const; |
109 | virtual unsigned int height(const MenuTheme &theme) const; | 121 | virtual unsigned int height(const MenuTheme &theme) const; |
110 | virtual void draw(FbDrawable &drawable, | 122 | virtual void draw(FbDrawable &drawable, |
@@ -137,6 +149,7 @@ private: | |||
137 | RefCount<Command> m_command; ///< command to be executed | 149 | RefCount<Command> m_command; ///< command to be executed |
138 | bool m_enabled, m_selected; | 150 | bool m_enabled, m_selected; |
139 | bool m_toggle_item; | 151 | bool m_toggle_item; |
152 | int m_index; | ||
140 | 153 | ||
141 | struct Icon { | 154 | struct Icon { |
142 | std::auto_ptr<PixmapWithMask> pixmap; | 155 | std::auto_ptr<PixmapWithMask> pixmap; |
diff --git a/src/FbTk/MenuTheme.cc b/src/FbTk/MenuTheme.cc index c7b6d77..3b03697 100644 --- a/src/FbTk/MenuTheme.cc +++ b/src/FbTk/MenuTheme.cc | |||
@@ -44,6 +44,7 @@ MenuTheme::MenuTheme(int screen_num): | |||
44 | f_text(*this, "menu.frame.textColor", "Menu.Frame.TextColor"), | 44 | f_text(*this, "menu.frame.textColor", "Menu.Frame.TextColor"), |
45 | h_text(*this, "menu.hilite.textColor", "Menu.Hilite.TextColor"), | 45 | h_text(*this, "menu.hilite.textColor", "Menu.Hilite.TextColor"), |
46 | d_text(*this, "menu.frame.disableColor", "Menu.Frame.DisableColor"), | 46 | d_text(*this, "menu.frame.disableColor", "Menu.Frame.DisableColor"), |
47 | u_text(*this, "menu.frame.underlineColor", "Menu.Frame.UnderlineColor"), | ||
47 | title(*this, "menu.title", "Menu.Title"), | 48 | title(*this, "menu.title", "Menu.Title"), |
48 | frame(*this, "menu.frame", "Menu.Frame"), | 49 | frame(*this, "menu.frame", "Menu.Frame"), |
49 | hilite(*this, "menu.hilite", "Menu.Hilite"), | 50 | hilite(*this, "menu.hilite", "Menu.Hilite"), |
@@ -67,6 +68,7 @@ MenuTheme::MenuTheme(int screen_num): | |||
67 | m_display(FbTk::App::instance()->display()), | 68 | m_display(FbTk::App::instance()->display()), |
68 | t_text_gc(RootWindow(m_display, screen_num)), | 69 | t_text_gc(RootWindow(m_display, screen_num)), |
69 | f_text_gc(RootWindow(m_display, screen_num)), | 70 | f_text_gc(RootWindow(m_display, screen_num)), |
71 | u_text_gc(RootWindow(m_display, screen_num)), | ||
70 | h_text_gc(RootWindow(m_display, screen_num)), | 72 | h_text_gc(RootWindow(m_display, screen_num)), |
71 | d_text_gc(RootWindow(m_display, screen_num)), | 73 | d_text_gc(RootWindow(m_display, screen_num)), |
72 | hilite_gc(RootWindow(m_display, screen_num)), | 74 | hilite_gc(RootWindow(m_display, screen_num)), |
@@ -91,6 +93,7 @@ MenuTheme::MenuTheme(int screen_num): | |||
91 | 93 | ||
92 | t_text_gc.setForeground(*t_text); | 94 | t_text_gc.setForeground(*t_text); |
93 | f_text_gc.setForeground(*f_text); | 95 | f_text_gc.setForeground(*f_text); |
96 | u_text_gc.setForeground(*u_text); | ||
94 | h_text_gc.setForeground(*h_text); | 97 | h_text_gc.setForeground(*h_text); |
95 | d_text_gc.setForeground(*d_text); | 98 | d_text_gc.setForeground(*d_text); |
96 | hilite_gc.setForeground(hilite->color()); | 99 | hilite_gc.setForeground(hilite->color()); |
@@ -127,6 +130,7 @@ void MenuTheme::reconfigTheme() { | |||
127 | 130 | ||
128 | t_text_gc.setForeground(*t_text); | 131 | t_text_gc.setForeground(*t_text); |
129 | f_text_gc.setForeground(*f_text); | 132 | f_text_gc.setForeground(*f_text); |
133 | u_text_gc.setForeground(*u_text); | ||
130 | h_text_gc.setForeground(*h_text); | 134 | h_text_gc.setForeground(*h_text); |
131 | d_text_gc.setForeground(*d_text); | 135 | d_text_gc.setForeground(*d_text); |
132 | hilite_gc.setForeground(hilite->color()); | 136 | hilite_gc.setForeground(hilite->color()); |
diff --git a/src/FbTk/MenuTheme.hh b/src/FbTk/MenuTheme.hh index da5b65c..0bb77ef 100644 --- a/src/FbTk/MenuTheme.hh +++ b/src/FbTk/MenuTheme.hh | |||
@@ -56,6 +56,7 @@ public: | |||
56 | ///@{ | 56 | ///@{ |
57 | inline const FbTk::Color &titleTextColor() const { return *t_text; } | 57 | inline const FbTk::Color &titleTextColor() const { return *t_text; } |
58 | inline const FbTk::Color &frameTextColor() const { return *f_text; } | 58 | inline const FbTk::Color &frameTextColor() const { return *f_text; } |
59 | inline const FbTk::Color &frameUnderlineColor() const { return *u_text; } | ||
59 | inline const FbTk::Color &highlightTextColor() const { return *h_text; } | 60 | inline const FbTk::Color &highlightTextColor() const { return *h_text; } |
60 | inline const FbTk::Color &disableTextColor() const { return *d_text; } | 61 | inline const FbTk::Color &disableTextColor() const { return *d_text; } |
61 | ///@} | 62 | ///@} |
@@ -94,11 +95,13 @@ public: | |||
94 | ///@{ | 95 | ///@{ |
95 | inline const GContext &titleTextGC() const { return t_text_gc; } | 96 | inline const GContext &titleTextGC() const { return t_text_gc; } |
96 | inline const GContext &frameTextGC() const { return f_text_gc; } | 97 | inline const GContext &frameTextGC() const { return f_text_gc; } |
98 | inline const GContext &frameUnderlineGC() const { return u_text_gc; } | ||
97 | inline const GContext &hiliteTextGC() const { return h_text_gc; } | 99 | inline const GContext &hiliteTextGC() const { return h_text_gc; } |
98 | inline const GContext &disableTextGC() const { return d_text_gc; } | 100 | inline const GContext &disableTextGC() const { return d_text_gc; } |
99 | inline const GContext &hiliteGC() const { return hilite_gc; } | 101 | inline const GContext &hiliteGC() const { return hilite_gc; } |
100 | inline GContext &titleTextGC() { return t_text_gc; } | 102 | inline GContext &titleTextGC() { return t_text_gc; } |
101 | inline GContext &frameTextGC() { return f_text_gc; } | 103 | inline GContext &frameTextGC() { return f_text_gc; } |
104 | inline GContext &frameUnderlineGC() { return u_text_gc; } | ||
102 | inline GContext &hiliteTextGC() { return h_text_gc; } | 105 | inline GContext &hiliteTextGC() { return h_text_gc; } |
103 | inline GContext &disableTextGC() { return d_text_gc; } | 106 | inline GContext &disableTextGC() { return d_text_gc; } |
104 | inline GContext &hiliteGC() { return hilite_gc; } | 107 | inline GContext &hiliteGC() { return hilite_gc; } |
@@ -139,7 +142,7 @@ public: | |||
139 | } | 142 | } |
140 | 143 | ||
141 | private: | 144 | private: |
142 | FbTk::ThemeItem<FbTk::Color> t_text, f_text, h_text, d_text; | 145 | FbTk::ThemeItem<FbTk::Color> t_text, f_text, h_text, d_text, u_text; |
143 | FbTk::ThemeItem<FbTk::Texture> title, frame, hilite; | 146 | FbTk::ThemeItem<FbTk::Texture> title, frame, hilite; |
144 | FbTk::ThemeItem<FbTk::Font> titlefont, framefont; | 147 | FbTk::ThemeItem<FbTk::Font> titlefont, framefont; |
145 | FbTk::ThemeItem<FbTk::Justify> framefont_justify, titlefont_justify; | 148 | FbTk::ThemeItem<FbTk::Justify> framefont_justify, titlefont_justify; |
@@ -153,7 +156,7 @@ private: | |||
153 | FbTk::ThemeItem<FbTk::PixmapWithMask> m_hl_bullet_pixmap, m_hl_selected_pixmap, m_hl_unselected_pixmap; | 156 | FbTk::ThemeItem<FbTk::PixmapWithMask> m_hl_bullet_pixmap, m_hl_selected_pixmap, m_hl_unselected_pixmap; |
154 | 157 | ||
155 | Display *m_display; | 158 | Display *m_display; |
156 | FbTk::GContext t_text_gc, f_text_gc, h_text_gc, d_text_gc, hilite_gc; | 159 | FbTk::GContext t_text_gc, f_text_gc, u_text_gc, h_text_gc, d_text_gc, hilite_gc; |
157 | 160 | ||
158 | unsigned char m_alpha; | 161 | unsigned char m_alpha; |
159 | MenuMode m_menumode; | 162 | MenuMode m_menumode; |
diff --git a/src/FbTk/SearchResult.cc b/src/FbTk/SearchResult.cc new file mode 100644 index 0000000..3f36edc --- /dev/null +++ b/src/FbTk/SearchResult.cc | |||
@@ -0,0 +1,51 @@ | |||
1 | // SearchResult.cc for FbTk - Fluxbox Toolkit | ||
2 | // Copyright (c) 2007 Fluxbox Team (fluxgen at fluxbox dot org) | ||
3 | // | ||
4 | // Permission is hereby granted, free of charge, to any person obtaining a | ||
5 | // copy of this software and associated documentation files (the "Software"), | ||
6 | // to deal in the Software without restriction, including without limitation | ||
7 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
8 | // and/or sell copies of the Software, and to permit persons to whom the | ||
9 | // Software is furnished to do so, subject to the following conditions: | ||
10 | // | ||
11 | // The above copyright notice and this permission notice shall be included in | ||
12 | // all copies or substantial portions of the Software. | ||
13 | // | ||
14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
17 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
19 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
20 | // DEALINGS IN THE SOFTWARE. | ||
21 | |||
22 | #include "SearchResult.hh" | ||
23 | #include <vector> | ||
24 | |||
25 | namespace FbTk { | ||
26 | |||
27 | void SearchResult::seek() { | ||
28 | switch (m_results.size()) { | ||
29 | case 0: | ||
30 | break; | ||
31 | case 1: | ||
32 | m_seeked_string = m_results[0]->iTypeString(); | ||
33 | break; | ||
34 | default: | ||
35 | bool seekforward = true; | ||
36 | for (size_t i=1; i < m_results.size() && seekforward && | ||
37 | m_results[0]->iTypeCheckStringSize(m_seeked_string.size()); i++) { | ||
38 | if (!m_results[i]->iTypeCompareChar( | ||
39 | m_results[0]->iTypeChar(m_seeked_string.size()), | ||
40 | m_seeked_string.size())) { | ||
41 | seekforward = false; | ||
42 | } else if (i == m_results.size() - 1) { | ||
43 | m_seeked_string += m_results[0]->iTypeChar(m_seeked_string.size()); | ||
44 | i = 0; | ||
45 | } | ||
46 | } | ||
47 | break; | ||
48 | } | ||
49 | } | ||
50 | |||
51 | } // end namespace FbTk | ||
diff --git a/src/FbTk/SearchResult.hh b/src/FbTk/SearchResult.hh new file mode 100644 index 0000000..5e8852b --- /dev/null +++ b/src/FbTk/SearchResult.hh | |||
@@ -0,0 +1,53 @@ | |||
1 | // SearchResult.hh for FbTk - Fluxbox Toolkit | ||
2 | // Copyright (c) 2007 Fluxbox Team (fluxgen at fluxbox dot org) | ||
3 | // | ||
4 | // Permission is hereby granted, free of charge, to any person obtaining a | ||
5 | // copy of this software and associated documentation files (the "Software"), | ||
6 | // to deal in the Software without restriction, including without limitation | ||
7 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
8 | // and/or sell copies of the Software, and to permit persons to whom the | ||
9 | // Software is furnished to do so, subject to the following conditions: | ||
10 | // | ||
11 | // The above copyright notice and this permission notice shall be included in | ||
12 | // all copies or substantial portions of the Software. | ||
13 | // | ||
14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
17 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
19 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
20 | // DEALINGS IN THE SOFTWARE. | ||
21 | |||
22 | #ifndef FBTK_SEARCHRESULT_HH | ||
23 | #define FBTK_SEARCHRESULT_HH | ||
24 | |||
25 | #include <vector> | ||
26 | #include "ITypeAheadable.hh" | ||
27 | |||
28 | namespace FbTk { | ||
29 | |||
30 | class SearchResult { | ||
31 | public: | ||
32 | typedef std::vector < ITypeAheadable* > BaseItems; | ||
33 | typedef BaseItems::iterator BaseItemsIt; | ||
34 | |||
35 | SearchResult(const std::string &to_search_for): | ||
36 | m_seeked_string(to_search_for) { } | ||
37 | |||
38 | void add(ITypeAheadable* item) { m_results.push_back(item); } | ||
39 | size_t size() const { return m_results.size(); } | ||
40 | const BaseItems& result() const { return m_results; } | ||
41 | const std::string& seekedString() const { return m_seeked_string; } | ||
42 | |||
43 | void seek(); | ||
44 | |||
45 | private: | ||
46 | BaseItems m_results; | ||
47 | std::string m_seeked_string; | ||
48 | |||
49 | }; | ||
50 | |||
51 | } // end namespace FbTk | ||
52 | |||
53 | #endif // FBTK_SEARCHRESULT_HH | ||
diff --git a/src/FbTk/TypeAhead.hh b/src/FbTk/TypeAhead.hh new file mode 100644 index 0000000..15246b9 --- /dev/null +++ b/src/FbTk/TypeAhead.hh | |||
@@ -0,0 +1,174 @@ | |||
1 | // TypeAhead.hh for FbTk - Fluxbox Toolkit | ||
2 | // Copyright (c) 2007 Fluxbox Team (fluxgen at fluxbox dot org) | ||
3 | // | ||
4 | // Permission is hereby granted, free of charge, to any person obtaining a | ||
5 | // copy of this software and associated documentation files (the "Software"), | ||
6 | // to deal in the Software without restriction, including without limitation | ||
7 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
8 | // and/or sell copies of the Software, and to permit persons to whom the | ||
9 | // Software is furnished to do so, subject to the following conditions: | ||
10 | // | ||
11 | // The above copyright notice and this permission notice shall be included in | ||
12 | // all copies or substantial portions of the Software. | ||
13 | // | ||
14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
17 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
19 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
20 | // DEALINGS IN THE SOFTWARE. | ||
21 | |||
22 | #ifndef FBTK_TYPEAHEAD_HH | ||
23 | #define FBTK_TYPEAHEAD_HH | ||
24 | |||
25 | #include "ITypeAheadable.hh" | ||
26 | #include <vector> | ||
27 | #include "SearchResult.hh" | ||
28 | |||
29 | namespace FbTk { | ||
30 | |||
31 | template <typename Items, typename Item_Type> | ||
32 | class TypeAhead { | ||
33 | /* | ||
34 | |||
35 | a class template can't be split into separate interface + implementation files, an interface summary is given here: | ||
36 | |||
37 | public: | ||
38 | void init(Items const &items); | ||
39 | |||
40 | // accessors: | ||
41 | inline int stringSize() const { return m_searchstr.size(); } | ||
42 | Items matched() const; | ||
43 | |||
44 | // modifiers: | ||
45 | Items putCharacter(char ch); | ||
46 | void putBackSpace(); | ||
47 | void reset() | ||
48 | |||
49 | private: | ||
50 | SearchResults m_search_results; | ||
51 | std::string m_searchstr; | ||
52 | Items const *m_ref; | ||
53 | |||
54 | // helper | ||
55 | void fillValues(BaseItems const &search, ValueVec &fillin) const; | ||
56 | |||
57 | // reverts to searchstate before current | ||
58 | void revert(); | ||
59 | |||
60 | // search performs iteration and sets state | ||
61 | void search(char char_to_test); | ||
62 | void doSearch(char to_test, | ||
63 | Items const &items, | ||
64 | SearchResult &mySearchResult) const; | ||
65 | void doSearch(char to_test, | ||
66 | BaseItems const &search, | ||
67 | SearchResult &mySearchResult) const; | ||
68 | */ | ||
69 | |||
70 | public: | ||
71 | typedef std::vector < ITypeAheadable* > BaseItems; | ||
72 | typedef BaseItems::const_iterator BaseItemscIt; | ||
73 | typedef std::vector < SearchResult > SearchResults; | ||
74 | typedef typename Items::const_iterator ItemscIt; | ||
75 | |||
76 | void init(Items const &items) { m_ref = &items; } | ||
77 | |||
78 | inline size_t stringSize() const { return m_searchstr.size(); } | ||
79 | |||
80 | void seek() { | ||
81 | if (!m_search_results.empty()) | ||
82 | m_searchstr = m_search_results.back().seekedString(); | ||
83 | } | ||
84 | |||
85 | Items putCharacter(char ch) { | ||
86 | if (isprint(ch)) | ||
87 | search(ch); | ||
88 | return matched(); | ||
89 | } | ||
90 | |||
91 | void putBackSpace() { | ||
92 | if (!m_search_results.empty()) | ||
93 | revert(); | ||
94 | } | ||
95 | |||
96 | void reset() { | ||
97 | m_searchstr.clear(); | ||
98 | m_search_results.clear(); | ||
99 | } | ||
100 | |||
101 | Items matched() const { | ||
102 | Items last_matched; | ||
103 | |||
104 | if (!m_search_results.empty()) | ||
105 | fillValues(m_search_results.back().result(), last_matched); | ||
106 | return last_matched; | ||
107 | } | ||
108 | |||
109 | private: | ||
110 | SearchResults m_search_results; | ||
111 | std::string m_searchstr; | ||
112 | Items const *m_ref; // reference to vector we are operating on | ||
113 | |||
114 | void fillValues(BaseItems const &search, Items &fillin) const { | ||
115 | for (BaseItemscIt it = search.begin(); it != search.end(); it++) { | ||
116 | Item_Type tmp = dynamic_cast<Item_Type>(*it); | ||
117 | if (tmp) | ||
118 | fillin.push_back(tmp); | ||
119 | } | ||
120 | } | ||
121 | |||
122 | void revert() { | ||
123 | m_search_results.pop_back(); | ||
124 | if (m_search_results.empty()) | ||
125 | m_searchstr.clear(); | ||
126 | else | ||
127 | m_searchstr = m_search_results.back().seekedString(); | ||
128 | } | ||
129 | |||
130 | void search(char char_to_test) { | ||
131 | SearchResult mySearchResult(m_searchstr + char_to_test); | ||
132 | size_t num_items = m_ref->size(); | ||
133 | |||
134 | // check if we have already a searched set | ||
135 | if (m_search_results.empty()) | ||
136 | doSearch(char_to_test, *m_ref, mySearchResult); | ||
137 | else { | ||
138 | num_items = m_search_results.back().size(); | ||
139 | doSearch(char_to_test, m_search_results.back().result(), | ||
140 | mySearchResult); | ||
141 | } | ||
142 | |||
143 | if (mySearchResult.size() > 0 ) { | ||
144 | if (mySearchResult.size() < num_items) { | ||
145 | mySearchResult.seek(); | ||
146 | m_search_results.push_back(mySearchResult); | ||
147 | } | ||
148 | m_searchstr += char_to_test; | ||
149 | } | ||
150 | } | ||
151 | |||
152 | // iteration based on original list of items | ||
153 | void doSearch(char to_test, Items const &items, | ||
154 | SearchResult &mySearchResult) const { | ||
155 | for (ItemscIt it = items.begin(); it != items.end(); it++) { | ||
156 | if ((*it)->iTypeCompareChar(to_test, stringSize()) && (*it)->isEnabled()) | ||
157 | mySearchResult.add(*it); | ||
158 | } | ||
159 | } | ||
160 | |||
161 | // iteration based on last SearchResult | ||
162 | void doSearch(char to_test, BaseItems const &search, | ||
163 | SearchResult &mySearchResult) const { | ||
164 | for (BaseItemscIt it = search.begin(); it != search.end(); it++) { | ||
165 | if ((*it)->iTypeCompareChar(to_test, stringSize()) && (*it)->isEnabled()) | ||
166 | mySearchResult.add(*it); | ||
167 | } | ||
168 | } | ||
169 | |||
170 | }; // end Class TypeAhead | ||
171 | |||
172 | } // end namespace FbTk | ||
173 | |||
174 | #endif // FBTK_TYPEAHEAD_HH | ||