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