Home | History | Annotate | Download | only in shadow
      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/DateTimeNumericFieldElement.h"
     29 
     30 #include "CSSPropertyNames.h"
     31 #include "CSSValueKeywords.h"
     32 #include "core/dom/KeyboardEvent.h"
     33 #include "core/platform/graphics/Font.h"
     34 #include "core/platform/text/PlatformLocale.h"
     35 #include "wtf/text/StringBuilder.h"
     36 
     37 using namespace WTF::Unicode;
     38 
     39 namespace WebCore {
     40 
     41 static const DOMTimeStamp typeAheadTimeout = 1000;
     42 
     43 int DateTimeNumericFieldElement::Range::clampValue(int value) const
     44 {
     45     return std::min(std::max(value, minimum), maximum);
     46 }
     47 
     48 bool DateTimeNumericFieldElement::Range::isInRange(int value) const
     49 {
     50     return value >= minimum && value <= maximum;
     51 }
     52 
     53 // ----------------------------
     54 
     55 DateTimeNumericFieldElement::DateTimeNumericFieldElement(Document* document, FieldOwner& fieldOwner, const Range& range, const Range& hardLimits, const String& placeholder, const DateTimeNumericFieldElement::Step& step)
     56     : DateTimeFieldElement(document, fieldOwner)
     57     , m_lastDigitCharTime(0)
     58     , m_placeholder(placeholder)
     59     , m_range(range)
     60     , m_hardLimits(hardLimits)
     61     , m_step(step)
     62     , m_value(0)
     63     , m_hasValue(false)
     64 {
     65     ASSERT(m_step.step);
     66     ASSERT(m_range.minimum <= m_range.maximum);
     67     ASSERT(m_hardLimits.minimum <= m_hardLimits.maximum);
     68 
     69     // We show a direction-neutral string such as "--" as a placeholder. It
     70     // should follow the direction of numeric values.
     71     if (localeForOwner().isRTL()) {
     72         Direction dir = direction(formatValue(this->maximum())[0]);
     73         if (dir == LeftToRight || dir == EuropeanNumber || dir == ArabicNumber) {
     74             setInlineStyleProperty(CSSPropertyUnicodeBidi, CSSValueBidiOverride);
     75             setInlineStyleProperty(CSSPropertyDirection, CSSValueLtr);
     76         }
     77     }
     78 }
     79 
     80 float DateTimeNumericFieldElement::maximumWidth(const Font& font)
     81 {
     82     float maximumWidth = font.width(m_placeholder);
     83     maximumWidth = std::max(maximumWidth, font.width(formatValue(maximum())));
     84     maximumWidth = std::max(maximumWidth, font.width(value()));
     85     return maximumWidth + DateTimeFieldElement::maximumWidth(font);
     86 }
     87 
     88 int DateTimeNumericFieldElement::defaultValueForStepDown() const
     89 {
     90     return m_range.maximum;
     91 }
     92 
     93 int DateTimeNumericFieldElement::defaultValueForStepUp() const
     94 {
     95     return m_range.minimum;
     96 }
     97 
     98 void DateTimeNumericFieldElement::didBlur()
     99 {
    100     int value = typeAheadValue();
    101     m_typeAheadBuffer.clear();
    102     if (value >= 0)
    103         setValueAsInteger(value, DispatchEvent);
    104     DateTimeFieldElement::didBlur();
    105 }
    106 
    107 String DateTimeNumericFieldElement::formatValue(int value) const
    108 {
    109     Locale& locale = localeForOwner();
    110     if (m_hardLimits.maximum > 999)
    111         return locale.convertToLocalizedNumber(String::format("%04d", value));
    112     if (m_hardLimits.maximum > 99)
    113         return locale.convertToLocalizedNumber(String::format("%03d", value));
    114     return locale.convertToLocalizedNumber(String::format("%02d", value));
    115 }
    116 
    117 void DateTimeNumericFieldElement::handleKeyboardEvent(KeyboardEvent* keyboardEvent)
    118 {
    119     ASSERT(!isDisabled());
    120     if (keyboardEvent->type() != eventNames().keypressEvent)
    121         return;
    122 
    123     UChar charCode = static_cast<UChar>(keyboardEvent->charCode());
    124     String number = localeForOwner().convertFromLocalizedNumber(String(&charCode, 1));
    125     const int digit = number[0] - '0';
    126     if (digit < 0 || digit > 9)
    127         return;
    128 
    129     DOMTimeStamp delta = keyboardEvent->timeStamp() - m_lastDigitCharTime;
    130     m_lastDigitCharTime = keyboardEvent->timeStamp();
    131 
    132     if (delta > typeAheadTimeout)
    133         m_typeAheadBuffer.clear();
    134     m_typeAheadBuffer.append(number);
    135 
    136     int newValue = typeAheadValue();
    137     if (newValue >= m_hardLimits.minimum)
    138         setValueAsInteger(newValue, DispatchEvent);
    139     else {
    140         m_hasValue = false;
    141         updateVisibleValue(DispatchEvent);
    142     }
    143 
    144     if (m_typeAheadBuffer.length() >= DateTimeNumericFieldElement::formatValue(m_range.maximum).length() || newValue * 10 > m_range.maximum)
    145         focusOnNextField();
    146 
    147     keyboardEvent->setDefaultHandled();
    148 }
    149 
    150 bool DateTimeNumericFieldElement::hasValue() const
    151 {
    152     return m_hasValue;
    153 }
    154 
    155 void DateTimeNumericFieldElement::initialize(const AtomicString& pseudo, const String& axHelpText)
    156 {
    157     DateTimeFieldElement::initialize(pseudo, axHelpText, m_range.minimum, m_range.maximum);
    158 }
    159 
    160 int DateTimeNumericFieldElement::maximum() const
    161 {
    162     return m_range.maximum;
    163 }
    164 
    165 void DateTimeNumericFieldElement::setEmptyValue(EventBehavior eventBehavior)
    166 {
    167     if (isDisabled())
    168         return;
    169 
    170     m_hasValue = false;
    171     m_value = 0;
    172     m_typeAheadBuffer.clear();
    173     updateVisibleValue(eventBehavior);
    174 }
    175 
    176 void DateTimeNumericFieldElement::setValueAsInteger(int value, EventBehavior eventBehavior)
    177 {
    178     m_value = m_hardLimits.clampValue(value);
    179     m_hasValue = true;
    180     updateVisibleValue(eventBehavior);
    181 }
    182 
    183 void DateTimeNumericFieldElement::stepDown()
    184 {
    185     int newValue = roundDown(m_hasValue ? m_value - 1 : defaultValueForStepDown());
    186     if (!m_range.isInRange(newValue))
    187         newValue = roundDown(m_range.maximum);
    188     m_typeAheadBuffer.clear();
    189     setValueAsInteger(newValue, DispatchEvent);
    190 }
    191 
    192 void DateTimeNumericFieldElement::stepUp()
    193 {
    194     int newValue = roundUp(m_hasValue ? m_value + 1 : defaultValueForStepUp());
    195     if (!m_range.isInRange(newValue))
    196         newValue = roundUp(m_range.minimum);
    197     m_typeAheadBuffer.clear();
    198     setValueAsInteger(newValue, DispatchEvent);
    199 }
    200 
    201 String DateTimeNumericFieldElement::value() const
    202 {
    203     return m_hasValue ? formatValue(m_value) : emptyString();
    204 }
    205 
    206 int DateTimeNumericFieldElement::valueAsInteger() const
    207 {
    208     return m_hasValue ? m_value : -1;
    209 }
    210 
    211 int DateTimeNumericFieldElement::typeAheadValue() const
    212 {
    213     if (m_typeAheadBuffer.length())
    214         return m_typeAheadBuffer.toString().toInt();
    215     return -1;
    216 }
    217 
    218 String DateTimeNumericFieldElement::visibleValue() const
    219 {
    220     if (m_typeAheadBuffer.length())
    221         return formatValue(typeAheadValue());
    222     return m_hasValue ? value() : m_placeholder;
    223 }
    224 
    225 int DateTimeNumericFieldElement::roundDown(int n) const
    226 {
    227     n -= m_step.stepBase;
    228     if (n >= 0)
    229         n = n / m_step.step * m_step.step;
    230     else
    231         n = -((-n + m_step.step - 1) / m_step.step * m_step.step);
    232     return n + m_step.stepBase;
    233 }
    234 
    235 int DateTimeNumericFieldElement::roundUp(int n) const
    236 {
    237     n -= m_step.stepBase;
    238     if (n >= 0)
    239         n = (n + m_step.step - 1) / m_step.step * m_step.step;
    240     else
    241         n = -(-n / m_step.step * m_step.step);
    242     return n + m_step.stepBase;
    243 }
    244 
    245 } // namespace WebCore
    246 
    247 #endif
    248