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/events/KeyboardEvent.h" 33#include "platform/fonts/Font.h" 34#include "platform/text/PlatformLocale.h" 35 36using namespace WTF::Unicode; 37 38namespace WebCore { 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::didBlur() 95{ 96 int value = typeAheadValue(); 97 m_typeAheadBuffer.clear(); 98 if (value >= 0) 99 setValueAsInteger(value, DispatchEvent); 100 DateTimeFieldElement::didBlur(); 101} 102 103String DateTimeNumericFieldElement::formatValue(int value) const 104{ 105 Locale& locale = localeForOwner(); 106 if (m_hardLimits.maximum > 999) 107 return locale.convertToLocalizedNumber(String::format("%04d", value)); 108 if (m_hardLimits.maximum > 99) 109 return locale.convertToLocalizedNumber(String::format("%03d", value)); 110 return locale.convertToLocalizedNumber(String::format("%02d", value)); 111} 112 113void DateTimeNumericFieldElement::handleKeyboardEvent(KeyboardEvent* keyboardEvent) 114{ 115 ASSERT(!isDisabled()); 116 if (keyboardEvent->type() != EventTypeNames::keypress) 117 return; 118 119 UChar charCode = static_cast<UChar>(keyboardEvent->charCode()); 120 String number = localeForOwner().convertFromLocalizedNumber(String(&charCode, 1)); 121 const int digit = number[0] - '0'; 122 if (digit < 0 || digit > 9) 123 return; 124 125 m_typeAheadBuffer.append(number); 126 int newValue = typeAheadValue(); 127 if (newValue >= m_hardLimits.minimum) 128 setValueAsInteger(newValue, DispatchEvent); 129 else { 130 m_hasValue = false; 131 updateVisibleValue(DispatchEvent); 132 } 133 134 if (m_typeAheadBuffer.length() >= DateTimeNumericFieldElement::formatValue(m_range.maximum).length() || newValue * 10 > m_range.maximum) 135 focusOnNextField(); 136 137 keyboardEvent->setDefaultHandled(); 138} 139 140bool DateTimeNumericFieldElement::hasValue() const 141{ 142 return m_hasValue; 143} 144 145void DateTimeNumericFieldElement::initialize(const AtomicString& pseudo, const String& axHelpText) 146{ 147 DateTimeFieldElement::initialize(pseudo, axHelpText, m_range.minimum, m_range.maximum); 148} 149 150int DateTimeNumericFieldElement::maximum() const 151{ 152 return m_range.maximum; 153} 154 155void DateTimeNumericFieldElement::setEmptyValue(EventBehavior eventBehavior) 156{ 157 if (isDisabled()) 158 return; 159 160 m_hasValue = false; 161 m_value = 0; 162 m_typeAheadBuffer.clear(); 163 updateVisibleValue(eventBehavior); 164} 165 166void DateTimeNumericFieldElement::setValueAsInteger(int value, EventBehavior eventBehavior) 167{ 168 m_value = m_hardLimits.clampValue(value); 169 m_hasValue = true; 170 updateVisibleValue(eventBehavior); 171} 172 173void DateTimeNumericFieldElement::stepDown() 174{ 175 int newValue = roundDown(m_hasValue ? m_value - 1 : defaultValueForStepDown()); 176 if (!m_range.isInRange(newValue)) 177 newValue = roundDown(m_range.maximum); 178 m_typeAheadBuffer.clear(); 179 setValueAsInteger(newValue, DispatchEvent); 180} 181 182void DateTimeNumericFieldElement::stepUp() 183{ 184 int newValue = roundUp(m_hasValue ? m_value + 1 : defaultValueForStepUp()); 185 if (!m_range.isInRange(newValue)) 186 newValue = roundUp(m_range.minimum); 187 m_typeAheadBuffer.clear(); 188 setValueAsInteger(newValue, DispatchEvent); 189} 190 191String DateTimeNumericFieldElement::value() const 192{ 193 return m_hasValue ? formatValue(m_value) : emptyString(); 194} 195 196int DateTimeNumericFieldElement::valueAsInteger() const 197{ 198 return m_hasValue ? m_value : -1; 199} 200 201int DateTimeNumericFieldElement::typeAheadValue() const 202{ 203 if (m_typeAheadBuffer.length()) 204 return m_typeAheadBuffer.toString().toInt(); 205 return -1; 206} 207 208String DateTimeNumericFieldElement::visibleValue() const 209{ 210 if (m_typeAheadBuffer.length()) 211 return formatValue(typeAheadValue()); 212 return m_hasValue ? value() : m_placeholder; 213} 214 215int DateTimeNumericFieldElement::roundDown(int n) const 216{ 217 n -= m_step.stepBase; 218 if (n >= 0) 219 n = n / m_step.step * m_step.step; 220 else 221 n = -((-n + m_step.step - 1) / m_step.step * m_step.step); 222 return n + m_step.stepBase; 223} 224 225int DateTimeNumericFieldElement::roundUp(int n) const 226{ 227 n -= m_step.stepBase; 228 if (n >= 0) 229 n = (n + m_step.step - 1) / m_step.step * m_step.step; 230 else 231 n = -(-n / m_step.step * m_step.step); 232 return n + m_step.stepBase; 233} 234 235} // namespace WebCore 236 237#endif 238