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