added support for typeahead in menus
This commit is contained in:
parent
d6a7bd786f
commit
a233229bd8
12 changed files with 538 additions and 79 deletions
|
@ -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: <color> 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:
|
||||
|
|
49
src/FbTk/ITypeAheadable.hh
Normal file
49
src/FbTk/ITypeAheadable.hh
Normal file
|
@ -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 <string>
|
||||
|
||||
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
|
|
@ -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} \
|
||||
|
|
201
src/FbTk/Menu.cc
201
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,18 +282,46 @@ void Menu::lower() {
|
|||
menu.window.lower();
|
||||
}
|
||||
|
||||
void Menu::nextItem(int failsafe) {
|
||||
if (menuitems.empty())
|
||||
void Menu::cycleItems(bool reverse) {
|
||||
Menuitems vec;
|
||||
if (m_type_ahead.stringSize())
|
||||
vec = m_matches;
|
||||
else
|
||||
vec = menuitems;
|
||||
|
||||
if (vec.size() < 1)
|
||||
return;
|
||||
|
||||
if (failsafe == -1)
|
||||
failsafe = m_active_index;
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
if (new_index == -1)
|
||||
return;
|
||||
|
||||
// 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 = 0;
|
||||
|
||||
m_active_index = new_index;
|
||||
if (validIndex(old_active_index) &&
|
||||
menuitems[old_active_index] != 0) {
|
||||
if (menuitems[old_active_index]->submenu()) {
|
||||
|
@ -292,54 +331,7 @@ void Menu::nextItem(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) {
|
||||
nextItem(failsafe);
|
||||
return;
|
||||
}
|
||||
|
||||
clearItem(m_active_index);
|
||||
|
||||
}
|
||||
|
||||
void Menu::prevItem(int failsafe) {
|
||||
if (menuitems.empty())
|
||||
return;
|
||||
|
||||
if (failsafe == -1)
|
||||
failsafe = m_active_index;
|
||||
|
||||
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)) {
|
||||
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();
|
||||
}
|
||||
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
|
||||
|
|
|
@ -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<Menuitems, MenuItem *> 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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "RefCount.hh"
|
||||
#include "Command.hh"
|
||||
#include "PixmapWithMask.hh"
|
||||
#include "ITypeAheadable.hh"
|
||||
#include "FbString.hh"
|
||||
|
||||
#include <string>
|
||||
|
@ -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<Command> m_command; ///< command to be executed
|
||||
bool m_enabled, m_selected;
|
||||
bool m_toggle_item;
|
||||
int m_index;
|
||||
|
||||
struct Icon {
|
||||
std::auto_ptr<PixmapWithMask> pixmap;
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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<FbTk::Color> t_text, f_text, h_text, d_text;
|
||||
FbTk::ThemeItem<FbTk::Color> t_text, f_text, h_text, d_text, u_text;
|
||||
FbTk::ThemeItem<FbTk::Texture> title, frame, hilite;
|
||||
FbTk::ThemeItem<FbTk::Font> titlefont, framefont;
|
||||
FbTk::ThemeItem<FbTk::Justify> framefont_justify, titlefont_justify;
|
||||
|
@ -153,7 +156,7 @@ private:
|
|||
FbTk::ThemeItem<FbTk::PixmapWithMask> 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;
|
||||
|
|
51
src/FbTk/SearchResult.cc
Normal file
51
src/FbTk/SearchResult.cc
Normal file
|
@ -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 <vector>
|
||||
|
||||
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
|
53
src/FbTk/SearchResult.hh
Normal file
53
src/FbTk/SearchResult.hh
Normal file
|
@ -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 <vector>
|
||||
#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
|
174
src/FbTk/TypeAhead.hh
Normal file
174
src/FbTk/TypeAhead.hh
Normal file
|
@ -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 <vector>
|
||||
#include "SearchResult.hh"
|
||||
|
||||
namespace FbTk {
|
||||
|
||||
template <typename Items, typename Item_Type>
|
||||
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<Item_Type>(*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
|
Loading…
Reference in a new issue