From a233229bd854d2e925ca0f1e86846ff9fde46fcd Mon Sep 17 00:00:00 2001 From: markt Date: Sat, 3 Mar 2007 19:35:34 +0000 Subject: added support for typeahead in menus --- ChangeLog | 7 ++ src/FbTk/ITypeAheadable.hh | 49 ++++++++++++ src/FbTk/Makefile.am | 1 + src/FbTk/Menu.cc | 195 +++++++++++++++++++++++++++++---------------- src/FbTk/Menu.hh | 17 ++-- src/FbTk/MenuItem.cc | 38 +++++++++ src/FbTk/MenuItem.hh | 15 +++- src/FbTk/MenuTheme.cc | 4 + src/FbTk/MenuTheme.hh | 7 +- src/FbTk/SearchResult.cc | 51 ++++++++++++ src/FbTk/SearchResult.hh | 53 ++++++++++++ src/FbTk/TypeAhead.hh | 174 ++++++++++++++++++++++++++++++++++++++++ 12 files changed, 535 insertions(+), 76 deletions(-) create mode 100644 src/FbTk/ITypeAheadable.hh create mode 100644 src/FbTk/SearchResult.cc create mode 100644 src/FbTk/SearchResult.hh create mode 100644 src/FbTk/TypeAhead.hh diff --git a/ChangeLog b/ChangeLog index 113c751..202ef00 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,12 @@ (Format: Year/Month/Day) Changes for 1.0rc3: +*07/03/03 + * Added typeahead support to menus (Mathias, Mark + thanks Matteo Galiazzo) + - Added new style item menu.frame.underlineColor: for displaying + matching items + FbTk/Menu.cc/hh MenuItem.cc/hh MenuTheme.cc/hh Makefile.am and added + FbTk/ITypeAheadable.hh TypeAhead.hh SearchResult.cc/hh +*07/03/02 * Added support for keypad enter key in menu (Mark) FbTk/Menu.cc *07/02/28: 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 @@ +// ITypeAheadable.hh for FbTk - Fluxbox Toolkit +// Copyright (c) 2007 Fluxbox Team (fluxgen at fluxbox dot org) +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +#ifndef FBTK_ITYPEAHEADABLE_HH +#define FBTK_ITYPEAHEADABLE_HH + +#include + +namespace FbTk { + +// abstract base class providing access and validation +class ITypeAheadable { +public: + virtual ~ITypeAheadable() { } + + virtual const std::string &iTypeString() const = 0; + virtual bool isEnabled() { return true; } + char iTypeChar(size_t i) const { return iTypeString()[i]; } + bool iTypeCheckStringSize(size_t sz) const { + return (iTypeString().size() > sz); + } + bool iTypeCompareChar(char ch, size_t sz) const { + return (bool)iTypeCheckStringSize(sz) && + tolower(iTypeChar(sz)) == tolower(ch); + } + +}; + +} // end namespace FbTk + +#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 \ MenuSeparator.hh MenuSeparator.cc \ MenuIcon.hh MenuIcon.cc \ stringstream.hh \ + TypeAhead.hh SearchResult.hh SearchResult.cc ITypeAheadable.hh \ Select2nd.hh \ CachedPixmap.hh CachedPixmap.cc \ ${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): m_visible = false; + m_type_ahead.init(menuitems); menu.x_move = menu.y_move = 0; @@ -205,15 +206,24 @@ int Menu::insert(const FbString &label, Menu *submenu, int pos) { } int Menu::insert(MenuItem *item, int pos) { + if (item == 0) + return menuitems.size(); if (pos == -1) { + item->setIndex(menuitems.size()); menuitems.push_back(item); } else { menuitems.insert(menuitems.begin() + pos, item); + fixMenuItemIndices(); } m_need_update = true; // we need to redraw the menu return menuitems.size(); } +void Menu::fixMenuItemIndices() { + for (size_t i = 0; i < menuitems.size(); i++) + menuitems[i]->setIndex(i); +} + int Menu::remove(unsigned int index) { if (index >= menuitems.size()) { #ifdef DEBUG @@ -229,6 +239,9 @@ int Menu::remove(unsigned int index) { if (item) { menuitems.erase(it); + // avoid O(n^2) algorithm with removeAll() + if (index != menuitems.size()) + fixMenuItemIndices(); if (item->submenu() != 0) { Menu *tmp = item->submenu(); @@ -257,10 +270,8 @@ int Menu::remove(unsigned int index) { } void Menu::removeAll() { - while (!menuitems.empty()) { - remove(0); - } - m_need_update = true; + while (!menuitems.empty()) + remove(menuitems.size()-1); } void Menu::raise() { @@ -271,55 +282,48 @@ void Menu::lower() { menu.window.lower(); } -void Menu::nextItem(int failsafe) { - if (menuitems.empty()) - return; - - if (failsafe == -1) - failsafe = m_active_index; +void Menu::cycleItems(bool reverse) { + Menuitems vec; + if (m_type_ahead.stringSize()) + vec = m_matches; + else + vec = menuitems; - int old_active_index = m_active_index; - m_active_index += 1; - if (!validIndex(m_active_index)) - m_active_index = 0; + if (vec.size() < 1) + return; - if (validIndex(old_active_index) && - menuitems[old_active_index] != 0) { - if (menuitems[old_active_index]->submenu()) { - // we need to do this explicitly on the menu.window - // since it might hide the parent if we use Menu::hide - menuitems[old_active_index]->submenu()->internal_hide(); + // find the next item to select + // this algorithm assumes menuitems are sorted properly + int new_index = -1; + bool passed = !validIndex(m_active_index); + for (size_t i = 0; i < vec.size(); i++) { + if (!isItemSelectable(vec[i]->getIndex()) || + vec[i]->getIndex() == m_active_index) + continue; + + // determine whether or not we've passed the active index + if (!passed && vec[i]->getIndex() > m_active_index) { + if (reverse && new_index != -1) + break; + passed = true; } - clearItem(old_active_index); - } - if (menuitems[m_active_index] == 0) { - m_active_index = -1; - return; - } - - if (!isItemSelectable(m_active_index) && m_active_index != failsafe) { - nextItem(failsafe); - return; + // decide if we want to keep this item + if (passed && !reverse) { + new_index = vec[i]->getIndex(); + break; + } else if (reverse || new_index == -1) + new_index = vec[i]->getIndex(); } - clearItem(m_active_index); - -} - -void Menu::prevItem(int failsafe) { - if (menuitems.empty()) + if (new_index == -1) return; - if (failsafe == -1) - failsafe = m_active_index; - + // clear the items and close any open submenus int old_active_index = m_active_index; - m_active_index -= 1; - if (!validIndex(m_active_index)) - m_active_index = menuitems.size() - 1; - - if (validIndex(old_active_index)) { + m_active_index = new_index; + if (validIndex(old_active_index) && + menuitems[old_active_index] != 0) { if (menuitems[old_active_index]->submenu()) { // we need to do this explicitly on the menu.window // since it might hide the parent if we use Menu::hide @@ -327,19 +331,7 @@ void Menu::prevItem(int failsafe) { } clearItem(old_active_index); } - - if (menuitems[m_active_index] == 0) { - m_active_index = -1; - return; - } - - if (!isItemSelectable(m_active_index) && m_active_index != failsafe) { - prevItem(failsafe); - return; - } - - clearItem(m_active_index); - + clearItem(new_index); } void Menu::enterSubmenu() { @@ -356,7 +348,7 @@ void Menu::enterSubmenu() { drawSubmenu(m_active_index); submenu->grabInputFocus(); submenu->m_active_index = -1; // so we land on 0 after nextItem() - submenu->nextItem(); + submenu->cycleItems(false); } void Menu::enterParent() { @@ -1024,34 +1016,59 @@ void Menu::keyPressEvent(XKeyEvent &event) { switch (ks) { case XK_Up: - prevItem(); + resetTypeAhead(); + cycleItems(true); break; case XK_Down: - nextItem(); + resetTypeAhead(); + cycleItems(false); break; case XK_Left: // enter parent if we have one + resetTypeAhead(); enterParent(); break; case XK_Right: // enter submenu if we have one + resetTypeAhead(); enterSubmenu(); break; case XK_Escape: // close menu + m_type_ahead.reset(); hide(); break; + case XK_BackSpace: + m_type_ahead.putBackSpace(); + drawTypeAheadItems(); + break; case XK_KP_Enter: case XK_Return: - // send fake button 1 click + resetTypeAhead(); if (validIndex(m_active_index) && isItemEnabled(m_active_index)) { - if (event.state & ShiftMask) - menuitems[m_active_index]->click(3, event.time); - else - menuitems[m_active_index]->click(1, event.time); - m_need_update = true; - updateMenu(); + if (menuitems[m_active_index]->submenu() != 0) + enterSubmenu(); + else { + // send fake button click + int button = (event.state & ShiftMask) ? 3 : 1; + find(m_active_index)->click(button, event.time); + m_need_update = true; + updateMenu(); + } } break; + case XK_Tab: + case XK_ISO_Left_Tab: + m_type_ahead.seek(); + cycleItems((bool)(event.state & ShiftMask)); + drawTypeAheadItems(); + break; default: + m_type_ahead.putCharacter(keychar[0]); + // if current item doesn't match new search string, find the next one + drawTypeAheadItems(); + if (!m_matches.empty() && (!validIndex(m_active_index) || + std::find(m_matches.begin(), m_matches.end(), + find(m_active_index)) == m_matches.end())) + cycleItems(false); break; } } @@ -1151,7 +1168,7 @@ void Menu::renderForeground(FbWindow &win, FbDrawable &drawable) { // thus sometimes it won't perform the actual clear operation // nothing in here should be rendered transparently // (unless you use a caching pixmap, which I think we should avoid) -void Menu::clearItem(int index, bool clear) { +void Menu::clearItem(int index, bool clear, int search_index) { if (!validIndex(index)) return; @@ -1160,9 +1177,16 @@ void Menu::clearItem(int index, bool clear) { int item_x = (sbl * item_w), item_y = (i * item_h); bool highlight = (index == m_active_index && isItemSelectable(index)); + if (search_index < 0) + // find if we need to underline the item + search_index = std::find(m_matches.begin(), m_matches.end(), + find(index)) - m_matches.begin(); + // don't highlight if moving, doesn't work with alpha on if (highlight && !m_moving) { highlightItem(index); + if (search_index < (int)m_matches.size()) + drawLine(index, m_type_ahead.stringSize()); return; } else if (clear) menu.frame.clearArea(item_x, item_y, item_w, item_h); @@ -1173,6 +1197,9 @@ void Menu::clearItem(int index, bool clear) { item->draw(menu.frame, theme(), highlight, true, false, item_x, item_y, item_w, item_h); + + if (search_index < (int)m_matches.size()) + drawLine(index, m_type_ahead.stringSize()); } // Area must have been cleared before calling highlight @@ -1206,4 +1233,36 @@ void Menu::highlightItem(int index) { } +void Menu::resetTypeAhead() { + Menuitems vec = m_matches; + Menuitems::iterator it = vec.begin(); + m_type_ahead.reset(); + m_matches.clear(); + + for (; it != vec.end(); it++) + clearItem((*it)->getIndex(), true, 1); +} + +void Menu::drawTypeAheadItems() { + // remove underlines from old matches + for (size_t i = 0; i < m_matches.size(); i++) + clearItem(m_matches[i]->getIndex(), true, m_matches.size()); + + m_matches = m_type_ahead.matched(); + for (size_t j = 0; j < m_matches.size(); j++) + clearItem(m_matches[j]->getIndex(), false, j); +} + +// underline menuitem[index] with respect to matchstringsize size +void Menu::drawLine(int index, int size){ + if (!validIndex(index)) + return; + + int sbl = index / menu.persub, i = index - (sbl * menu.persub); + int item_x = (sbl * menu.item_w), item_y = (i * theme().itemHeight()); + + FbTk::MenuItem *item = find(index); + item->drawLine(menu.frame, theme(), size, item_x, item_y, menu.item_w); +} + }; // 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 @@ #include "MenuTheme.hh" #include "Timer.hh" #include "FbString.hh" +#include "TypeAhead.hh" namespace FbTk { @@ -87,10 +88,8 @@ public: virtual void raise(); /// lower this window virtual void lower(); - /// select next item - void nextItem(int failsafe = -1); - /// select previous item - void prevItem(int failsafe = -1); + /// cycle through menuitems + void cycleItems(bool reverse); void enterSubmenu(); void enterParent(); @@ -186,7 +185,7 @@ protected: int drawItem(FbDrawable &pm, unsigned int index, bool highlight = false, bool exclusive_drawable = false); - void clearItem(int index, bool clear = true); + void clearItem(int index, bool clear = true, int search_index = -1); void highlightItem(int index); virtual void redrawTitle(FbDrawable &pm); virtual void redrawFrame(FbDrawable &pm); @@ -209,6 +208,14 @@ private: ImageControl &m_image_ctrl; Menuitems menuitems; + TypeAhead m_type_ahead; + Menuitems m_matches; + + void resetTypeAhead(); + void drawTypeAheadItems(); + void drawLine(int index, int size); + void fixMenuItemIndices(); + int m_screen_x, m_screen_y; unsigned int m_screen_width, m_screen_height; 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) { m_command->execute(); } +void MenuItem::drawLine(FbDrawable &draw, const MenuTheme &theme, size_t size, + int text_x, int text_y, unsigned int width) const { + + unsigned int height = theme.itemHeight(); + int bevelW = theme.bevelWidth(); + + int font_top = (height - theme.frameFont().height())/2; + int underline_height = font_top + theme.frameFont().ascent() + 2; + int bottom = height - bevelW - 1; + + text_y += bottom > underline_height ? underline_height : bottom; + int text_w = theme.frameFont().textWidth(m_label.c_str(), m_label.size()); + + // width of the searchstring + size = size > m_label.length() ? m_label.length() : size; + std::string search_string = m_label.substr(0,size); + int search_string_w = theme.frameFont().textWidth(search_string.c_str(), size); + + // pay attention to the text justification + switch(theme.frameFontJustify()) { + case FbTk::LEFT: + text_x += bevelW + height + 1; + break; + case FbTk::RIGHT: + text_x += width - (height + bevelW + text_w); + break; + default: //center + text_x += ((width + 1 - text_w) / 2); + break; + } + + // avoid drawing an ugly dot + if (size != 0) + draw.drawLine(theme.frameUnderlineGC().gc(), + text_x, text_y, text_x + search_string_w, text_y); + +} + void MenuItem::draw(FbDrawable &draw, const MenuTheme &theme, 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 @@ #include "RefCount.hh" #include "Command.hh" #include "PixmapWithMask.hh" +#include "ITypeAheadable.hh" #include "FbString.hh" #include @@ -39,7 +40,7 @@ class MenuTheme; class FbDrawable; /// An interface for a menu item in Menu -class MenuItem { +class MenuItem : public FbTk::ITypeAheadable { public: MenuItem() : m_label(""), @@ -105,6 +106,17 @@ public: virtual bool isEnabled() const { return m_enabled; } virtual bool isSelected() const { return m_selected; } virtual bool isToggleItem() const { return m_toggle_item; } + + // iType functions + virtual inline void setIndex(int index) { m_index = index; } + virtual inline int getIndex() { return m_index; } + inline const std::string &iTypeString() const { return m_label; } + virtual void drawLine(FbDrawable &draw, + const MenuTheme &theme, + size_t size, + int text_x, int text_y, + unsigned int width) const; + virtual unsigned int width(const MenuTheme &theme) const; virtual unsigned int height(const MenuTheme &theme) const; virtual void draw(FbDrawable &drawable, @@ -137,6 +149,7 @@ private: RefCount m_command; ///< command to be executed bool m_enabled, m_selected; bool m_toggle_item; + int m_index; struct Icon { std::auto_ptr 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): f_text(*this, "menu.frame.textColor", "Menu.Frame.TextColor"), h_text(*this, "menu.hilite.textColor", "Menu.Hilite.TextColor"), d_text(*this, "menu.frame.disableColor", "Menu.Frame.DisableColor"), + u_text(*this, "menu.frame.underlineColor", "Menu.Frame.UnderlineColor"), title(*this, "menu.title", "Menu.Title"), frame(*this, "menu.frame", "Menu.Frame"), hilite(*this, "menu.hilite", "Menu.Hilite"), @@ -67,6 +68,7 @@ MenuTheme::MenuTheme(int screen_num): m_display(FbTk::App::instance()->display()), t_text_gc(RootWindow(m_display, screen_num)), f_text_gc(RootWindow(m_display, screen_num)), + u_text_gc(RootWindow(m_display, screen_num)), h_text_gc(RootWindow(m_display, screen_num)), d_text_gc(RootWindow(m_display, screen_num)), hilite_gc(RootWindow(m_display, screen_num)), @@ -91,6 +93,7 @@ MenuTheme::MenuTheme(int screen_num): t_text_gc.setForeground(*t_text); f_text_gc.setForeground(*f_text); + u_text_gc.setForeground(*u_text); h_text_gc.setForeground(*h_text); d_text_gc.setForeground(*d_text); hilite_gc.setForeground(hilite->color()); @@ -127,6 +130,7 @@ void MenuTheme::reconfigTheme() { t_text_gc.setForeground(*t_text); f_text_gc.setForeground(*f_text); + u_text_gc.setForeground(*u_text); h_text_gc.setForeground(*h_text); d_text_gc.setForeground(*d_text); 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: ///@{ inline const FbTk::Color &titleTextColor() const { return *t_text; } inline const FbTk::Color &frameTextColor() const { return *f_text; } + inline const FbTk::Color &frameUnderlineColor() const { return *u_text; } inline const FbTk::Color &highlightTextColor() const { return *h_text; } inline const FbTk::Color &disableTextColor() const { return *d_text; } ///@} @@ -94,11 +95,13 @@ public: ///@{ inline const GContext &titleTextGC() const { return t_text_gc; } inline const GContext &frameTextGC() const { return f_text_gc; } + inline const GContext &frameUnderlineGC() const { return u_text_gc; } inline const GContext &hiliteTextGC() const { return h_text_gc; } inline const GContext &disableTextGC() const { return d_text_gc; } inline const GContext &hiliteGC() const { return hilite_gc; } inline GContext &titleTextGC() { return t_text_gc; } inline GContext &frameTextGC() { return f_text_gc; } + inline GContext &frameUnderlineGC() { return u_text_gc; } inline GContext &hiliteTextGC() { return h_text_gc; } inline GContext &disableTextGC() { return d_text_gc; } inline GContext &hiliteGC() { return hilite_gc; } @@ -139,7 +142,7 @@ public: } private: - FbTk::ThemeItem t_text, f_text, h_text, d_text; + FbTk::ThemeItem t_text, f_text, h_text, d_text, u_text; FbTk::ThemeItem title, frame, hilite; FbTk::ThemeItem titlefont, framefont; FbTk::ThemeItem framefont_justify, titlefont_justify; @@ -153,7 +156,7 @@ private: FbTk::ThemeItem m_hl_bullet_pixmap, m_hl_selected_pixmap, m_hl_unselected_pixmap; Display *m_display; - FbTk::GContext t_text_gc, f_text_gc, h_text_gc, d_text_gc, hilite_gc; + FbTk::GContext t_text_gc, f_text_gc, u_text_gc, h_text_gc, d_text_gc, hilite_gc; unsigned char m_alpha; 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 @@ +// SearchResult.cc for FbTk - Fluxbox Toolkit +// Copyright (c) 2007 Fluxbox Team (fluxgen at fluxbox dot org) +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +#include "SearchResult.hh" +#include + +namespace FbTk { + +void SearchResult::seek() { + switch (m_results.size()) { + case 0: + break; + case 1: + m_seeked_string = m_results[0]->iTypeString(); + break; + default: + bool seekforward = true; + for (size_t i=1; i < m_results.size() && seekforward && + m_results[0]->iTypeCheckStringSize(m_seeked_string.size()); i++) { + if (!m_results[i]->iTypeCompareChar( + m_results[0]->iTypeChar(m_seeked_string.size()), + m_seeked_string.size())) { + seekforward = false; + } else if (i == m_results.size() - 1) { + m_seeked_string += m_results[0]->iTypeChar(m_seeked_string.size()); + i = 0; + } + } + break; + } +} + +} // 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 @@ +// SearchResult.hh for FbTk - Fluxbox Toolkit +// Copyright (c) 2007 Fluxbox Team (fluxgen at fluxbox dot org) +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +#ifndef FBTK_SEARCHRESULT_HH +#define FBTK_SEARCHRESULT_HH + +#include +#include "ITypeAheadable.hh" + +namespace FbTk { + +class SearchResult { +public: + typedef std::vector < ITypeAheadable* > BaseItems; + typedef BaseItems::iterator BaseItemsIt; + + SearchResult(const std::string &to_search_for): + m_seeked_string(to_search_for) { } + + void add(ITypeAheadable* item) { m_results.push_back(item); } + size_t size() const { return m_results.size(); } + const BaseItems& result() const { return m_results; } + const std::string& seekedString() const { return m_seeked_string; } + + void seek(); + +private: + BaseItems m_results; + std::string m_seeked_string; + +}; + +} // end namespace FbTk + +#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 @@ +// TypeAhead.hh for FbTk - Fluxbox Toolkit +// Copyright (c) 2007 Fluxbox Team (fluxgen at fluxbox dot org) +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +#ifndef FBTK_TYPEAHEAD_HH +#define FBTK_TYPEAHEAD_HH + +#include "ITypeAheadable.hh" +#include +#include "SearchResult.hh" + +namespace FbTk { + +template +class TypeAhead { +/* + +a class template can't be split into separate interface + implementation files, an interface summary is given here: + +public: + void init(Items const &items); + +// accessors: + inline int stringSize() const { return m_searchstr.size(); } + Items matched() const; + +// modifiers: + Items putCharacter(char ch); + void putBackSpace(); + void reset() + +private: + SearchResults m_search_results; + std::string m_searchstr; + Items const *m_ref; + +// helper + void fillValues(BaseItems const &search, ValueVec &fillin) const; + +// reverts to searchstate before current + void revert(); + +// search performs iteration and sets state + void search(char char_to_test); + void doSearch(char to_test, + Items const &items, + SearchResult &mySearchResult) const; + void doSearch(char to_test, + BaseItems const &search, + SearchResult &mySearchResult) const; +*/ + +public: + typedef std::vector < ITypeAheadable* > BaseItems; + typedef BaseItems::const_iterator BaseItemscIt; + typedef std::vector < SearchResult > SearchResults; + typedef typename Items::const_iterator ItemscIt; + + void init(Items const &items) { m_ref = &items; } + + inline size_t stringSize() const { return m_searchstr.size(); } + + void seek() { + if (!m_search_results.empty()) + m_searchstr = m_search_results.back().seekedString(); + } + + Items putCharacter(char ch) { + if (isprint(ch)) + search(ch); + return matched(); + } + + void putBackSpace() { + if (!m_search_results.empty()) + revert(); + } + + void reset() { + m_searchstr.clear(); + m_search_results.clear(); + } + + Items matched() const { + Items last_matched; + + if (!m_search_results.empty()) + fillValues(m_search_results.back().result(), last_matched); + return last_matched; + } + +private: + SearchResults m_search_results; + std::string m_searchstr; + Items const *m_ref; // reference to vector we are operating on + + void fillValues(BaseItems const &search, Items &fillin) const { + for (BaseItemscIt it = search.begin(); it != search.end(); it++) { + Item_Type tmp = dynamic_cast(*it); + if (tmp) + fillin.push_back(tmp); + } + } + + void revert() { + m_search_results.pop_back(); + if (m_search_results.empty()) + m_searchstr.clear(); + else + m_searchstr = m_search_results.back().seekedString(); + } + + void search(char char_to_test) { + SearchResult mySearchResult(m_searchstr + char_to_test); + size_t num_items = m_ref->size(); + + // check if we have already a searched set + if (m_search_results.empty()) + doSearch(char_to_test, *m_ref, mySearchResult); + else { + num_items = m_search_results.back().size(); + doSearch(char_to_test, m_search_results.back().result(), + mySearchResult); + } + + if (mySearchResult.size() > 0 ) { + if (mySearchResult.size() < num_items) { + mySearchResult.seek(); + m_search_results.push_back(mySearchResult); + } + m_searchstr += char_to_test; + } + } + + // iteration based on original list of items + void doSearch(char to_test, Items const &items, + SearchResult &mySearchResult) const { + for (ItemscIt it = items.begin(); it != items.end(); it++) { + if ((*it)->iTypeCompareChar(to_test, stringSize()) && (*it)->isEnabled()) + mySearchResult.add(*it); + } + } + + // iteration based on last SearchResult + void doSearch(char to_test, BaseItems const &search, + SearchResult &mySearchResult) const { + for (BaseItemscIt it = search.begin(); it != search.end(); it++) { + if ((*it)->iTypeCompareChar(to_test, stringSize()) && (*it)->isEnabled()) + mySearchResult.add(*it); + } + } + +}; // end Class TypeAhead + +} // end namespace FbTk + +#endif // FBTK_TYPEAHEAD_HH -- cgit v0.11.2