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
34
35namespace WebCore {
36using namespace WTF::Unicode;
37
38TypeAhead::TypeAhead(TypeAheadDataSource* dataSource)
39 : m_dataSource(dataSource)
40 , m_repeatingChar(0)
41{
42}
43
44static const Seconds typeAheadTimeout { 1_s };
45
46static String stripLeadingWhiteSpace(const String& string)
47{
48 unsigned length = string.length();
49 unsigned i;
50 for (i = 0; i < length; ++i) {
51 if (string[i] != noBreakSpace && !isSpaceOrNewline(string[i]))
52 break;
53 }
54 return string.substring(i, length - i);
55}
56
57int TypeAhead::handleEvent(KeyboardEvent* event, MatchModeFlags matchMode)
58{
59 if (event->timeStamp() < m_lastTypeTime)
60 return -1;
61
62 int optionCount = m_dataSource->optionCount();
63 Seconds delta = event->timeStamp() - m_lastTypeTime;
64 m_lastTypeTime = event->timeStamp();
65
66 UChar c = event->charCode();
67
68 if (delta > typeAheadTimeout)
69 m_buffer.clear();
70 m_buffer.append(c);
71
72 if (optionCount < 1)
73 return -1;
74
75 int searchStartOffset = 1;
76 String prefix;
77 if (matchMode & CycleFirstChar && c == m_repeatingChar) {
78 // The user is likely trying to cycle through all the items starting
79 // with this character, so just search on the character.
80 prefix = String(&c, 1);
81 m_repeatingChar = c;
82 } else if (matchMode & MatchPrefix) {
83 prefix = m_buffer.toString();
84 if (m_buffer.length() > 1) {
85 m_repeatingChar = 0;
86 searchStartOffset = 0;
87 } else
88 m_repeatingChar = c;
89 }
90
91 if (!prefix.isEmpty()) {
92 int selected = m_dataSource->indexOfSelectedOption();
93 int index = (selected < 0 ? 0 : selected) + searchStartOffset;
94 index %= optionCount;
95
96 String prefixWithCaseFolded(prefix.foldCase());
97 for (int i = 0; i < optionCount; ++i, index = (index + 1) % optionCount) {
98 // Fold the option string and check if its prefix is equal to the folded prefix.
99 String text = m_dataSource->optionAtIndex(index);
100 if (stripLeadingWhiteSpace(text).foldCase().startsWith(prefixWithCaseFolded))
101 return index;
102 }
103 }
104
105 if (matchMode & MatchIndex) {
106 bool ok = false;
107 int index = m_buffer.toString().toInt(&ok);
108 if (index > 0 && index <= optionCount)
109 return index - 1;
110 }
111 return -1;
112}
113
114} // namespace WebCore
115