#include "MenuSearch.hh" #include "MenuItem.hh" #include "StringUtil.hh" #include "Resource.hh" namespace { size_t search_str_nowhere(const std::string& text, const std::string& pattern) { return std::string::npos; } // finds 'pattern' at beginning of 'text' size_t search_str_textstart(const std::string& text, const std::string& pattern) { size_t l = std::min(text.size(), pattern.size()); if (l == 0) { return std::string::npos; } size_t i; for (i = l; i > 0; i--) { if (std::tolower(text[i-1]) != std::tolower(pattern[i-1])) { return std::string::npos; } } return i; } // finds 'pattern' in 'text', case insensitive. // returns position or std::string::npos if not found. // // implements Boyer–Moore–Horspool size_t search_str_bmh(const std::string& text, const std::string& pattern) { if (pattern.empty()) { return 0; } if (text.empty() || pattern.size() > text.size()) { return std::string::npos; } size_t t; size_t tlen = text.size(); // simple case, no need to be too clever if (pattern.size() == 1) { int b = std::tolower(pattern[0]); for (t = 0; t < tlen; t++) { if (b == std::tolower(text[t])) { return t; } } return std::string::npos; } size_t plast = pattern.size() - 1; size_t p; // prepare skip-table // size_t skip[256]; for (p = 0; p < sizeof(skip)/sizeof(skip[0]); p++) { skip[p] = plast + 1; } for (p = 0; p < plast; p++) { skip[std::tolower(pattern[p])] = plast - p; } // match for (t = 0; t + plast < tlen; ) { for (p = plast; std::tolower(text[t+p]) == std::tolower(pattern[p]); p--) { if (p == 0) { return t+p; } } t += skip[std::tolower(text[t+p])]; } return std::string::npos; } // actually search function, depends on Mode size_t (*search_str)(const std::string&, const std::string&) = search_str_textstart; } // anonymous namespace FbTk { void MenuSearch::setMode(MenuSearch::Mode m) { if (m == NOWHERE) { search_str = search_str_nowhere; } else if (m == SOMEWHERE) { search_str = search_str_bmh; } else { search_str = search_str_textstart; } } MenuSearch::MenuSearch(const std::vector& items) : m_items(items) { } size_t MenuSearch::size() const { return pattern.size(); } void MenuSearch::clear() { pattern.clear(); } void MenuSearch::add(char c) { pattern.push_back(c); } void MenuSearch::backspace() { size_t s = pattern.size(); if (s > 0) { pattern.erase(s - 1, 1); } } // is 'pattern' matching something? bool MenuSearch::has_match() { size_t l = m_items.size(); size_t i; for (i = 0; i < l; i++) { if (!m_items[i]->isEnabled()) continue; if (search_str(m_items[i]->iTypeString(), pattern) != std::string::npos) { return true; } } return false; } // would 'the_pattern' match something? bool MenuSearch::would_match(const std::string& the_pattern) { size_t l = m_items.size(); size_t i; for (i = 0; i < l; i++) { if (!m_items[i]->isEnabled()) continue; if (search_str(m_items[i]->iTypeString(), the_pattern) != std::string::npos) { return true; } } return false; } size_t MenuSearch::num_matches() { size_t l = m_items.size(); size_t i, n; for (i = 0, n = 0; i < l; i++) { if (!m_items[i]->isEnabled()) continue; if (search_str(m_items[i]->iTypeString(), pattern) != std::string::npos) { n++; } } return n; } // returns true if m_text matches against m_items[i] and stores // the position where it matches in the string. an empty // 'pattern' always matches bool MenuSearch::get_match(size_t i, size_t& idx) { if (i > m_items.size()) { return false; } if (pattern.empty()) return true; idx = search_str(m_items[i]->iTypeString(), pattern); return idx != std::string::npos; } // // resource-implementation related template<> std::string FbTk::Resource::getString() const { switch (m_value) { case FbTk::MenuSearch::NOWHERE: return "nowhere"; case FbTk::MenuSearch::SOMEWHERE: return "somewhere"; default: return "itemstart"; }; } template<> void FbTk::Resource::setFromString(const char *strval) { std::string val = FbTk::StringUtil::toLower(strval); if (val == "nowhere") { m_value = FbTk::MenuSearch::NOWHERE; } else if (val == "somewhere") { m_value = FbTk::MenuSearch::SOMEWHERE; } else { setDefaultValue(); } } }