• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "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 WebCore {
37 
TypeAhead(TypeAheadDataSource * dataSource)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 
stripLeadingWhiteSpace(const String & string)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 
handleEvent(KeyboardEvent * event,MatchModeFlags matchMode)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 WebCore
123