1 /* 2 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). 3 * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org) 4 * (C) 1999 Antti Koivisto (koivisto (at) kde.org) 5 * (C) 2001 Dirk Mueller (mueller (at) kde.org) 6 * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010, 2011 Apple Inc. All rights reserved. 7 * (C) 2006 Alexey Proskuryakov (ap (at) 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 "core/html/forms/TypeAhead.h" 30 31 #include "core/events/KeyboardEvent.h" 32 #include "wtf/unicode/CharacterNames.h" 33 34 using namespace WTF::Unicode; 35 36 namespace blink { 37 38 TypeAhead::TypeAhead(TypeAheadDataSource* dataSource) 39 : m_dataSource(dataSource) 40 , m_lastTypeTime(0) 41 , m_repeatingChar(0) 42 { 43 } 44 45 static const DOMTimeStamp typeAheadTimeout = 1000; 46 47 static String stripLeadingWhiteSpace(const String& string) 48 { 49 unsigned length = string.length(); 50 51 unsigned i; 52 for (i = 0; i < length; ++i) { 53 if (string[i] != noBreakSpace && !isSpaceOrNewline(string[i])) 54 break; 55 } 56 57 return string.substring(i, length - i); 58 } 59 60 int TypeAhead::handleEvent(KeyboardEvent* event, MatchModeFlags matchMode) 61 { 62 if (event->timeStamp() < m_lastTypeTime) 63 return -1; 64 65 int optionCount = m_dataSource->optionCount(); 66 DOMTimeStamp delta = event->timeStamp() - m_lastTypeTime; 67 m_lastTypeTime = event->timeStamp(); 68 69 UChar c = event->charCode(); 70 71 if (delta > typeAheadTimeout) 72 m_buffer.clear(); 73 m_buffer.append(c); 74 75 if (optionCount < 1) 76 return -1; 77 78 int searchStartOffset = 1; 79 String prefix; 80 if (matchMode & CycleFirstChar && c == m_repeatingChar) { 81 // The user is likely trying to cycle through all the items starting 82 // with this character, so just search on the character. 83 prefix = String(&c, 1); 84 m_repeatingChar = c; 85 } else if (matchMode & MatchPrefix) { 86 prefix = m_buffer.toString(); 87 if (m_buffer.length() > 1) { 88 m_repeatingChar = 0; 89 searchStartOffset = 0; 90 } else { 91 m_repeatingChar = c; 92 } 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 blink 123