diff options
Diffstat (limited to 'src/gui/util/qvalidator.cpp')
-rw-r--r-- | src/gui/util/qvalidator.cpp | 153 |
1 files changed, 88 insertions, 65 deletions
diff --git a/src/gui/util/qvalidator.cpp b/src/gui/util/qvalidator.cpp index f71a66c98c..2a81006657 100644 --- a/src/gui/util/qvalidator.cpp +++ b/src/gui/util/qvalidator.cpp @@ -331,12 +331,12 @@ QIntValidator::~QIntValidator() or is a prefix of an integer in the valid range, returns \l Intermediate. Otherwise, returns \l Invalid. - If the valid range consists of just positive integers (e.g., 32 to 100) - and \a input is a negative integer, then Invalid is returned. (On the other - hand, if the range consists of negative integers (e.g., -100 to -32) and - \a input is a positive integer, then Intermediate is returned, because - the user might be just about to type the minus (especially for right-to-left - languages). + If the valid range consists of just positive integers (e.g., 32 to 100) and + \a input is a negative integer, then Invalid is returned. (On the other + hand, if the range consists of negative integers (e.g., -100 to -32) and \a + input is a positive integer without leading plus sign, then Intermediate is + returned, because the user might be just about to type the minus (especially + for right-to-left languages). Similarly, if the valid range is between 46 and 53, then 41 and 59 will be evaluated as \l Intermediate, as otherwise the user wouldn't be able to @@ -362,34 +362,45 @@ static qlonglong pow10(int exp) return result; } -QValidator::State QIntValidator::validate(QString & input, int&) const +template <typename T> static inline +std::optional<QValidator::State> initialResultCheck(T min, T max, const ParsingResult &result) { - QByteArray buff; - if (!locale().d->m_data->validateChars(input, QLocaleData::IntegerMode, &buff, -1, - locale().numberOptions())) { - return Invalid; - } + if (result.state == ParsingResult::Invalid) + return QValidator::Invalid; + const CharBuff &buff = result.buff; if (buff.isEmpty()) - return Intermediate; + return QValidator::Intermediate; - const bool startsWithMinus(buff[0] == '-'); - if (b >= 0 && startsWithMinus) - return Invalid; + char ch = buff[0]; + const bool signConflicts = (min >= 0 && ch == '-') || (max < 0 && ch == '+'); + if (signConflicts) + return QValidator::Invalid; - const bool startsWithPlus(buff[0] == '+'); - if (t < 0 && startsWithPlus) - return Invalid; + if (result.state == ParsingResult::Intermediate) + return QValidator::Intermediate; - if (buff.size() == 1 && (startsWithPlus || startsWithMinus)) - return Intermediate; + return std::nullopt; +} - bool ok; - qlonglong entered = QLocaleData::bytearrayToLongLong(buff, 10, &ok); - if (!ok) +QValidator::State QIntValidator::validate(QString & input, int&) const +{ + ParsingResult result = + locale().d->m_data->validateChars(input, QLocaleData::IntegerMode, -1, + locale().numberOptions()); + + std::optional<State> opt = initialResultCheck(b, t, result); + if (opt) + return *opt; + + const CharBuff &buff = result.buff; + QSimpleParsedNumber r = QLocaleData::bytearrayToLongLong(buff, 10); + if (!r.ok()) return Invalid; + qint64 entered = r.result; if (entered >= b && entered <= t) { + bool ok = false; locale().toInt(input, &ok); return ok ? Acceptable : Intermediate; } @@ -401,7 +412,7 @@ QValidator::State QIntValidator::validate(QString & input, int&) const // of a number of digits equal to or less than the max value as intermediate. int buffLength = buff.size(); - if (startsWithPlus) + if (buff[0] == '+') buffLength--; const int tLength = t != 0 ? static_cast<int>(std::log10(qAbs(t))) + 1 : 1; @@ -414,15 +425,15 @@ QValidator::State QIntValidator::validate(QString & input, int&) const /*! \reimp */ void QIntValidator::fixup(QString &input) const { - QByteArray buff; - if (!locale().d->m_data->validateChars(input, QLocaleData::IntegerMode, &buff, -1, - locale().numberOptions())) { + auto [parseState, buff] = + locale().d->m_data->validateChars(input, QLocaleData::IntegerMode, -1, + locale().numberOptions()); + if (parseState == ParsingResult::Invalid) return; - } - bool ok; - qlonglong entered = QLocaleData::bytearrayToLongLong(buff, 10, &ok); - if (ok) - input = locale().toString(entered); + + QSimpleParsedNumber r = QLocaleData::bytearrayToLongLong(buff, 10); + if (r.ok()) + input = locale().toString(r.result); } /*! @@ -531,10 +542,11 @@ public: in the German locale, "1,234" will be accepted as the fractional number 1.234. In Arabic locales, QDoubleValidator will accept Arabic digits. - \note The QLocale::NumberOptions set on the locale() also affect the - way the number is interpreted. For example, since QLocale::RejectGroupSeparator - is not set by default, the validator will accept group separators. It is thus - recommended to use QLocale::toDouble() to obtain the numeric value. + \note The QLocale::NumberOptions set on the locale() also affect the way the + number is interpreted. For example, since QLocale::RejectGroupSeparator is + not set by default (except on the \c "C" locale), the validator will accept + group separators. If the string passes validation, pass it to + locale().toDouble() to obtain its numeric value. \sa QIntValidator, QRegularExpressionValidator, QLocale::toDouble(), {Line Edits Example} */ @@ -544,10 +556,23 @@ public: \since 4.3 This enum defines the allowed notations for entering a double. - \value StandardNotation The string is written as a standard number - (i.e. 0.015). - \value ScientificNotation The string is written in scientific - form. It may have an exponent part(i.e. 1.5E-2). + \value StandardNotation The string is written in the standard format, a + whole number part optionally followed by a separator + and fractional part, for example \c{"0.015"}. + + \value ScientificNotation The string is written in scientific form, which + optionally appends an exponent part to the + standard format, for example \c{"1.5E-2"}. + + The whole number part may, as usual, include a sign. This, along with the + separators for fractional part, exponent and any digit-grouping, depend on + locale. QDoubleValidator doesn't check the placement (which would also + depend on locale) of any digit-grouping separators it finds, but it will + reject input that contains them if \l QLocale::RejectGroupSeparator is set + in \c locale().numberOptions(). + + \sa QLocale::numberOptions(), QLocale::decimalPoint(), + QLocale::exponential(), QLocale::negativeSign() */ /*! @@ -589,14 +614,14 @@ QDoubleValidator::~QDoubleValidator() /*! \fn QValidator::State QDoubleValidator::validate(QString &input, int &pos) const - Returns \l Acceptable if the string \a input contains a double - that is within the valid range and is in the correct format. + Returns \l Acceptable if the string \a input is in the correct format and + contains a double within the valid range. - Returns \l Intermediate if \a input contains a double that is - outside the range or is in the wrong format; e.g. is empty. + Returns \l Intermediate if \a input is in the wrong format or contains a + double outside the range. - Returns \l Invalid if the \a input is not a double or with too many - digits after the decimal point. + Returns \l Invalid if the \a input doesn't represent a double or has too + many digits after the decimal point. Note: If the valid range consists of just positive doubles (e.g. 0.0 to 100.0) and \a input is a negative double then \l Invalid is returned. If notation() @@ -632,19 +657,12 @@ QValidator::State QDoubleValidator::validate(QString & input, int &) const QValidator::State QDoubleValidatorPrivate::validateWithLocale(QString &input, QLocaleData::NumberMode numMode, const QLocale &locale) const { Q_Q(const QDoubleValidator); - QByteArray buff; - if (!locale.d->m_data->validateChars(input, numMode, &buff, q->dec, locale.numberOptions())) { - return QValidator::Invalid; - } - - if (buff.isEmpty()) - return QValidator::Intermediate; - - if (q->b >= 0 && buff.startsWith('-')) - return QValidator::Invalid; + ParsingResult result = + locale.d->m_data->validateChars(input, numMode, q->dec, locale.numberOptions()); - if (q->t < 0 && buff.startsWith('+')) - return QValidator::Invalid; + std::optional<QValidator::State> opt = initialResultCheck(q->b, q->t, result); + if (opt) + return *opt; bool ok = false; double i = locale.toDouble(input, &ok); // returns 0.0 if !ok @@ -658,7 +676,11 @@ QValidator::State QDoubleValidatorPrivate::validateWithLocale(QString &input, QL if (notation == QDoubleValidator::StandardNotation) { double max = qMax(qAbs(q->b), qAbs(q->t)); qlonglong v; - if (convertDoubleTo(max, &v)) { + // Need a whole number to pass to convertDoubleTo() or it fails. Use + // floor, as max is positive so this has the same number of digits + // before the decimal point, where qCeil() might take us up to a power + // of ten, adding a digit. + if (convertDoubleTo(qFloor(max), &v)) { qlonglong n = pow10(numDigits(v)); // In order to get the highest possible number in the intermediate // range we need to get 10 to the power of the number of digits @@ -719,15 +741,16 @@ void QDoubleValidatorPrivate::fixupWithLocale(QString &input, QLocaleData::Numbe const QLocale &locale) const { Q_Q(const QDoubleValidator); - QByteArray buff; // Passing -1 as the number of decimals, because fixup() exists to improve // an Intermediate value, if it can. - if (!locale.d->m_data->validateChars(input, numMode, &buff, -1, locale.numberOptions())) + auto [parseState, buff] = + locale.d->m_data->validateChars(input, numMode, -1, locale.numberOptions()); + if (parseState == ParsingResult::Invalid) return; - // buff now contains data in C locale. + // buff contains data in C locale. bool ok = false; - const double entered = buff.toDouble(&ok); + const double entered = QByteArrayView(buff).toDouble(&ok); if (ok) { // Here we need to adjust the output format accordingly char mode; @@ -751,7 +774,7 @@ void QDoubleValidatorPrivate::fixupWithLocale(QString &input, QLocaleData::Numbe if (eIndex < 0) eIndex = buff.size(); precision = eIndex - (buff.contains('.') ? 1 : 0) - - (buff.startsWith('-') || buff.startsWith('+') ? 1 : 0); + - (buff[0] == '-' || buff[0] == '+' ? 1 : 0); } // Use q->dec to limit the number of decimals, because we want the // fixup() result to pass validate(). |