1/*
2 * Copyright (C) 2012 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1.  Redistributions of source code must retain the above copyright
8 *     notice, this list of conditions and the following disclaimer.
9 * 2.  Redistributions in binary form must reproduce the above copyright
10 *     notice, this list of conditions and the following disclaimer in the
11 *     documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
20 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26#include "config.h"
27#if ENABLE(INPUT_MULTIPLE_FIELDS_UI)
28#include "core/html/shadow/DateTimeSymbolicFieldElement.h"
29
30#include "core/events/KeyboardEvent.h"
31#include "platform/fonts/Font.h"
32#include "platform/text/TextBreakIterator.h"
33#include "wtf/text/StringBuilder.h"
34#include "wtf/unicode/Unicode.h"
35
36namespace blink {
37
38static AtomicString makeVisibleEmptyValue(const Vector<String>& symbols)
39{
40    unsigned maximumLength = 0;
41    for (unsigned index = 0; index < symbols.size(); ++index)
42        maximumLength = std::max(maximumLength, numGraphemeClusters(symbols[index]));
43    StringBuilder builder;
44    builder.reserveCapacity(maximumLength);
45    for (unsigned length = 0; length < maximumLength; ++length)
46        builder.append('-');
47    return builder.toAtomicString();
48}
49
50DateTimeSymbolicFieldElement::DateTimeSymbolicFieldElement(Document& document, FieldOwner& fieldOwner, const Vector<String>& symbols, int minimum, int maximum)
51    : DateTimeFieldElement(document, fieldOwner)
52    , m_symbols(symbols)
53    , m_visibleEmptyValue(makeVisibleEmptyValue(symbols))
54    , m_selectedIndex(-1)
55    , m_typeAhead(this)
56    , m_minimumIndex(minimum)
57    , m_maximumIndex(maximum)
58{
59    ASSERT(!symbols.isEmpty());
60    ASSERT(m_minimumIndex >= 0);
61    ASSERT_WITH_SECURITY_IMPLICATION(m_maximumIndex < static_cast<int>(m_symbols.size()));
62    ASSERT(m_minimumIndex <= m_maximumIndex);
63}
64
65float DateTimeSymbolicFieldElement::maximumWidth(const Font& font)
66{
67    float maximumWidth = font.width(visibleEmptyValue());
68    for (unsigned index = 0; index < m_symbols.size(); ++index)
69        maximumWidth = std::max(maximumWidth, font.width(m_symbols[index]));
70    return maximumWidth + DateTimeFieldElement::maximumWidth(font);
71}
72
73void DateTimeSymbolicFieldElement::handleKeyboardEvent(KeyboardEvent* keyboardEvent)
74{
75    if (keyboardEvent->type() != EventTypeNames::keypress)
76        return;
77
78    const UChar charCode = WTF::Unicode::toLower(keyboardEvent->charCode());
79    if (charCode < ' ')
80        return;
81
82    keyboardEvent->setDefaultHandled();
83
84    int index = m_typeAhead.handleEvent(keyboardEvent, TypeAhead::MatchPrefix | TypeAhead::CycleFirstChar | TypeAhead::MatchIndex);
85    if (index < 0)
86        return;
87    setValueAsInteger(index, DispatchEvent);
88}
89
90bool DateTimeSymbolicFieldElement::hasValue() const
91{
92    return m_selectedIndex >= 0;
93}
94
95void DateTimeSymbolicFieldElement::initialize(const AtomicString& pseudo, const String& axHelpText)
96{
97    // The minimum and maximum below are exposed to users, and 1-based numbers
98    // are natural for symbolic fields. For example, the minimum value of a
99    // month field should be 1, not 0.
100    DateTimeFieldElement::initialize(pseudo, axHelpText, m_minimumIndex + 1, m_maximumIndex + 1);
101}
102
103void DateTimeSymbolicFieldElement::setEmptyValue(EventBehavior eventBehavior)
104{
105    if (isDisabled())
106        return;
107    m_selectedIndex = invalidIndex;
108    updateVisibleValue(eventBehavior);
109}
110
111void DateTimeSymbolicFieldElement::setValueAsInteger(int newSelectedIndex, EventBehavior eventBehavior)
112{
113    m_selectedIndex = std::max(0, std::min(newSelectedIndex, static_cast<int>(m_symbols.size() - 1)));
114    updateVisibleValue(eventBehavior);
115}
116
117void DateTimeSymbolicFieldElement::stepDown()
118{
119    if (hasValue()) {
120        if (!indexIsInRange(--m_selectedIndex))
121            m_selectedIndex = m_maximumIndex;
122    } else
123        m_selectedIndex = m_maximumIndex;
124    updateVisibleValue(DispatchEvent);
125}
126
127void DateTimeSymbolicFieldElement::stepUp()
128{
129    if (hasValue()) {
130        if (!indexIsInRange(++m_selectedIndex))
131            m_selectedIndex = m_minimumIndex;
132    } else
133        m_selectedIndex = m_minimumIndex;
134    updateVisibleValue(DispatchEvent);
135}
136
137String DateTimeSymbolicFieldElement::value() const
138{
139    return hasValue() ? m_symbols[m_selectedIndex] : emptyString();
140}
141
142int DateTimeSymbolicFieldElement::valueAsInteger() const
143{
144    return m_selectedIndex;
145}
146
147int DateTimeSymbolicFieldElement::valueForARIAValueNow() const
148{
149    // Synchronize with minimum/maximum adjustment in initialize().
150    return m_selectedIndex + 1;
151}
152
153String DateTimeSymbolicFieldElement::visibleEmptyValue() const
154{
155    return m_visibleEmptyValue;
156}
157
158String DateTimeSymbolicFieldElement::visibleValue() const
159{
160    return hasValue() ? m_symbols[m_selectedIndex] : visibleEmptyValue();
161}
162
163int DateTimeSymbolicFieldElement::indexOfSelectedOption() const
164{
165    return m_selectedIndex;
166}
167
168int DateTimeSymbolicFieldElement::optionCount() const
169{
170    return m_symbols.size();
171}
172
173String DateTimeSymbolicFieldElement::optionAtIndex(int index) const
174{
175    return m_symbols[index];
176}
177
178} // namespace blink
179
180#endif
181