1/* 2 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). 3 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) 4 * (C) 1999 Antti Koivisto (koivisto@kde.org) 5 * (C) 2001 Dirk Mueller (mueller@kde.org) 6 * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010, 2011 Apple Inc. All rights reserved. 7 * (C) 2006 Alexey Proskuryakov (ap@nypop.com) 8 * Copyright (C) 2010 Google Inc. All rights reserved. 9 * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) 10 * 11 * This library is free software; you can redistribute it and/or 12 * modify it under the terms of the GNU Library General Public 13 * License as published by the Free Software Foundation; either 14 * version 2 of the License, or (at your option) any later version. 15 * 16 * This library is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 * Library General Public License for more details. 20 * 21 * You should have received a copy of the GNU Library General Public License 22 * along with this library; see the file COPYING.LIB. If not, write to 23 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 24 * Boston, MA 02110-1301, USA. 25 * 26 */ 27 28#include "config.h" 29#include "TypeAhead.h" 30 31#include "KeyboardEvent.h" 32#include <wtf/unicode/CharacterNames.h> 33#include <wtf/unicode/Unicode.h> 34 35using namespace WTF::Unicode; 36 37namespace WebCore { 38 39TypeAhead::TypeAhead(TypeAheadDataSource* dataSource) 40 : m_dataSource(dataSource) 41 , m_lastTypeTime(0) 42 , m_repeatingChar(0) 43{ 44} 45 46static const DOMTimeStamp typeAheadTimeout = 1000; 47 48static String stripLeadingWhiteSpace(const String& string) 49{ 50 unsigned length = string.length(); 51 52 unsigned i; 53 for (i = 0; i < length; ++i) { 54 if (string[i] != noBreakSpace && (string[i] <= 0x7F ? !isASCIISpace(string[i]) : (direction(string[i]) != WhiteSpaceNeutral))) 55 break; 56 } 57 58 return string.substring(i, length - i); 59} 60 61int TypeAhead::handleEvent(KeyboardEvent* event, MatchModeFlags matchMode) 62{ 63 if (event->timeStamp() < m_lastTypeTime) 64 return -1; 65 66 int optionCount = m_dataSource->optionCount(); 67 DOMTimeStamp delta = event->timeStamp() - m_lastTypeTime; 68 m_lastTypeTime = event->timeStamp(); 69 70 UChar c = event->charCode(); 71 72 if (delta > typeAheadTimeout) 73 m_buffer.clear(); 74 m_buffer.append(c); 75 76 if (optionCount < 1) 77 return -1; 78 79 int searchStartOffset = 1; 80 String prefix; 81 if (matchMode & CycleFirstChar && c == m_repeatingChar) { 82 // The user is likely trying to cycle through all the items starting 83 // with this character, so just search on the character. 84 prefix = String(&c, 1); 85 m_repeatingChar = c; 86 } else if (matchMode & MatchPrefix) { 87 prefix = m_buffer.toString(); 88 if (m_buffer.length() > 1) { 89 m_repeatingChar = 0; 90 searchStartOffset = 0; 91 } else 92 m_repeatingChar = c; 93 } 94 95 if (!prefix.isEmpty()) { 96 int selected = m_dataSource->indexOfSelectedOption(); 97 int index = (selected < 0 ? 0 : selected) + searchStartOffset; 98 index %= optionCount; 99 100 // Compute a case-folded copy of the prefix string before beginning the search for 101 // a matching element. This code uses foldCase to work around the fact that 102 // String::startWith does not fold non-ASCII characters. This code can be changed 103 // to use startWith once that is fixed. 104 String prefixWithCaseFolded(prefix.foldCase()); 105 for (int i = 0; i < optionCount; ++i, index = (index + 1) % optionCount) { 106 // Fold the option string and check if its prefix is equal to the folded prefix. 107 String text = m_dataSource->optionAtIndex(index); 108 if (stripLeadingWhiteSpace(text).foldCase().startsWith(prefixWithCaseFolded)) 109 return index; 110 } 111 } 112 113 if (matchMode & MatchIndex) { 114 bool ok = false; 115 int index = m_buffer.toString().toInt(&ok); 116 if (index > 0 && index <= optionCount) 117 return index - 1; 118 } 119 return -1; 120} 121 122} // namespace WebCore 123