diff options
Diffstat (limited to 'src/gui/util')
-rw-r--r-- | src/gui/util/qastchandler.cpp | 2 | ||||
-rw-r--r-- | src/gui/util/qdesktopservices.cpp | 7 | ||||
-rw-r--r-- | src/gui/util/qedidparser.cpp | 21 | ||||
-rw-r--r-- | src/gui/util/qgridlayoutengine.cpp | 28 | ||||
-rw-r--r-- | src/gui/util/qktxhandler.cpp | 12 | ||||
-rw-r--r-- | src/gui/util/qpkmhandler.cpp | 6 | ||||
-rw-r--r-- | src/gui/util/qtexturefiledata.cpp | 70 | ||||
-rw-r--r-- | src/gui/util/qtexturefiledata_p.h | 6 | ||||
-rw-r--r-- | src/gui/util/qundostack.cpp | 12 | ||||
-rw-r--r-- | src/gui/util/qvalidator.cpp | 60 |
10 files changed, 147 insertions, 77 deletions
diff --git a/src/gui/util/qastchandler.cpp b/src/gui/util/qastchandler.cpp index 6c05e0e248..f5c1d84f91 100644 --- a/src/gui/util/qastchandler.cpp +++ b/src/gui/util/qastchandler.cpp @@ -37,7 +37,7 @@ quint32 QAstcHandler::astcGLFormat(quint8 xBlockDim, quint8 yBlockDim) const static const quint32 glFormatRGBABase = 0x93B0; // GL_COMPRESSED_RGBA_ASTC_4x4_KHR static const quint32 glFormatSRGBBase = 0x93D0; // GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR - static QSize dims[14] = { + Q_CONSTINIT static QSize dims[14] = { { 4, 4 }, // GL_COMPRESSED_xxx_ASTC_4x4_KHR { 5, 4 }, // GL_COMPRESSED_xxx_ASTC_5x4_KHR { 5, 5 }, // GL_COMPRESSED_xxx_ASTC_5x5_KHR diff --git a/src/gui/util/qdesktopservices.cpp b/src/gui/util/qdesktopservices.cpp index ae452885fc..4a12f6db6f 100644 --- a/src/gui/util/qdesktopservices.cpp +++ b/src/gui/util/qdesktopservices.cpp @@ -162,6 +162,13 @@ void QOpenUrlHandlerRegistry::handlerDestroyed(QObject *handler) \snippet code/src_gui_util_qdesktopservices.cpp 3 + \note For Android Nougat (SDK 24) and above, URLs with a \c file scheme + are opened using \l {Android: FileProvider}{FileProvider} which tries to obtain + a shareable \c content scheme URI first. For that reason, Qt for Android defines + a file provider with the authority \c ${applicationId}.qtprovider, with \c applicationId + being the app's package name to avoid name conflicts. For more information, also see + \l {Android: Setting up file sharing}{Setting up file sharing}. + \sa setUrlHandler() */ bool QDesktopServices::openUrl(const QUrl &url) diff --git a/src/gui/util/qedidparser.cpp b/src/gui/util/qedidparser.cpp index b989503ec2..4dae151e6a 100644 --- a/src/gui/util/qedidparser.cpp +++ b/src/gui/util/qedidparser.cpp @@ -12,6 +12,7 @@ #define EDID_DESCRIPTOR_PRODUCT_NAME 0xfc #define EDID_DESCRIPTOR_SERIAL_NUMBER 0xff +#define EDID_DATA_BLOCK_COUNT 4 #define EDID_OFFSET_DATA_BLOCKS 0x36 #define EDID_OFFSET_LAST_BLOCK 0x6c #define EDID_OFFSET_PNP_ID 0x08 @@ -71,7 +72,7 @@ static QString lookupVendorIdInSystemDatabase(QByteArrayView id) bool QEdidParser::parse(const QByteArray &blob) { const quint8 *data = reinterpret_cast<const quint8 *>(blob.constData()); - const size_t length = blob.length(); + const size_t length = blob.size(); // Verify header if (length < 128) @@ -104,7 +105,7 @@ bool QEdidParser::parse(const QByteArray &blob) serialNumber = QString(); // Parse EDID data - for (int i = 0; i < 5; ++i) { + for (int i = 0; i < EDID_DATA_BLOCK_COUNT; ++i) { const uint offset = EDID_OFFSET_DATA_BLOCKS + i * 18; if (data[offset] != 0 || data[offset + 1] != 0 || data[offset + 2] != 0) @@ -234,16 +235,22 @@ QString QEdidParser::parseEdidString(const quint8 *data) { QByteArray buffer(reinterpret_cast<const char *>(data), 13); - // Erase carriage return and line feed - buffer = buffer.replace('\r', '\0').replace('\n', '\0'); - - // Replace non-printable characters with dash for (int i = 0; i < buffer.size(); ++i) { + // If there are less than 13 characters in the string, the string + // is terminated with the ASCII code ‘0Ah’ (line feed) and padded + // with ASCII code ‘20h’ (space). See EDID 1.4, sections 3.10.3.1, + // 3.10.3.2, and 3.10.3.4. + if (buffer[i] == '\n') { + buffer.truncate(i); + break; + } + + // Replace non-printable characters with dash if (buffer[i] < '\040' || buffer[i] > '\176') buffer[i] = '-'; } - return QString::fromLatin1(buffer.trimmed()); + return QString::fromLatin1(buffer); } QT_END_NAMESPACE diff --git a/src/gui/util/qgridlayoutengine.cpp b/src/gui/util/qgridlayoutengine.cpp index 25c02cba51..1cc42d3260 100644 --- a/src/gui/util/qgridlayoutengine.cpp +++ b/src/gui/util/qgridlayoutengine.cpp @@ -16,7 +16,7 @@ using namespace Qt::StringLiterals; template<typename T> static void insertOrRemoveItems(QList<T> &items, int index, int delta) { - int count = items.count(); + int count = items.size(); if (index < count) { if (delta > 0) { items.insert(index, delta, T()); @@ -773,7 +773,7 @@ int QGridLayoutEngine::columnCount(Qt::Orientation orientation) const int QGridLayoutEngine::itemCount() const { - return q_items.count(); + return q_items.size(); } QGridLayoutItem *QGridLayoutEngine::itemAt(int index) const @@ -818,7 +818,7 @@ void QGridLayoutEngine::setRowSpacing(int row, qreal spacing, Qt::Orientation or Q_ASSERT(row >= 0); QGridLayoutRowInfo &rowInfo = q_infos[orientation]; - if (row >= rowInfo.spacings.count()) + if (row >= rowInfo.spacings.size()) rowInfo.spacings.resize(row + 1); if (spacing >= 0) rowInfo.spacings[row].setUserValue(spacing); @@ -843,7 +843,7 @@ void QGridLayoutEngine::setRowStretchFactor(int row, int stretch, Qt::Orientatio maybeExpandGrid(row, -1, orientation); QGridLayoutRowInfo &rowInfo = q_infos[orientation]; - if (row >= rowInfo.stretches.count()) + if (row >= rowInfo.stretches.size()) rowInfo.stretches.resize(row + 1); rowInfo.stretches[row].setUserValue(stretch); } @@ -865,7 +865,7 @@ void QGridLayoutEngine::setRowSizeHint(Qt::SizeHint which, int row, qreal size, maybeExpandGrid(row, -1, orientation); QGridLayoutRowInfo &rowInfo = q_infos[orientation]; - if (row >= rowInfo.boxes.count()) + if (row >= rowInfo.boxes.size()) rowInfo.boxes.resize(row + 1); rowInfo.boxes[row].q_sizes(which) = size; } @@ -883,7 +883,7 @@ void QGridLayoutEngine::setRowAlignment(int row, Qt::Alignment alignment, maybeExpandGrid(row, -1, orientation); QGridLayoutRowInfo &rowInfo = q_infos[orientation]; - if (row >= rowInfo.alignments.count()) + if (row >= rowInfo.alignments.size()) rowInfo.alignments.resize(row + 1); rowInfo.alignments[row] = alignment; } @@ -992,7 +992,7 @@ void QGridLayoutEngine::setGeometries(const QRectF &contentsGeometry, const QAbs ensureGeometries(contentsGeometry.size(), styleInfo); - for (int i = q_items.count() - 1; i >= 0; --i) { + for (int i = q_items.size() - 1; i >= 0; --i) { QGridLayoutItem *item = q_items.at(i); qreal x = q_xx.at(item->firstColumn()); @@ -1121,7 +1121,7 @@ void QGridLayoutEngine::transpose() { invalidate(); - for (int i = q_items.count() - 1; i >= 0; --i) + for (int i = q_items.size() - 1; i >= 0; --i) q_items.at(i)->transpose(); q_defaultSpacings.transpose(); @@ -1210,7 +1210,7 @@ void QGridLayoutEngine::maybeExpandGrid(int row, int column, Qt::Orientation ori int newGridColumnCount = internalGridColumnCount(); int newGridSize = newGridRowCount * newGridColumnCount; - if (newGridSize != q_grid.count()) { + if (newGridSize != q_grid.size()) { q_grid.resize(newGridSize); if (newGridColumnCount != oldGridColumnCount) { @@ -1232,7 +1232,7 @@ void QGridLayoutEngine::regenerateGrid() { q_grid.fill(nullptr); - for (int i = q_items.count() - 1; i >= 0; --i) { + for (int i = q_items.size() - 1; i >= 0; --i) { QGridLayoutItem *item = q_items.at(i); for (int j = item->firstRow(); j <= item->lastRow(); ++j) { @@ -1265,7 +1265,7 @@ void QGridLayoutEngine::insertOrRemoveRows(int row, int delta, Qt::Orientation o q_infos[orientation].insertOrRemoveRows(row, delta); - for (int i = q_items.count() - 1; i >= 0; --i) + for (int i = q_items.size() - 1; i >= 0; --i) q_items.at(i)->insertOrRemoveRows(row, delta, orientation); q_grid.resize(internalGridRowCount() * internalGridColumnCount()); @@ -1406,7 +1406,7 @@ void QGridLayoutEngine::fillRowData(QGridLayoutRowData *rowData, } } } - if (row < rowInfo.boxes.count()) { + if (row < rowInfo.boxes.size()) { QGridLayoutBox rowBoxInfo = rowInfo.boxes.at(row); rowBoxInfo.normalize(); rowBox.q_minimumSize = qMax(rowBox.q_minimumSize, rowBoxInfo.q_minimumSize); @@ -1510,7 +1510,7 @@ void QGridLayoutEngine::ensureEffectiveFirstAndLastRows() const q_cachedEffectiveFirstRows = {columnCount, rowCount}; q_cachedEffectiveLastRows = {-1, -1}; - for (int i = q_items.count() - 1; i >= 0; --i) { + for (int i = q_items.size() - 1; i >= 0; --i) { const QGridLayoutItem *item = q_items.at(i); for (Qt::Orientation o : {Qt::Horizontal, Qt::Vertical}) { @@ -1556,7 +1556,7 @@ void QGridLayoutEngine::ensureColumnAndRowData(QGridLayoutRowData *rowData, QGri bool QGridLayoutEngine::ensureDynamicConstraint() const { if (q_cachedConstraintOrientation == UnknownConstraint) { - for (int i = q_items.count() - 1; i >= 0; --i) { + for (int i = q_items.size() - 1; i >= 0; --i) { QGridLayoutItem *item = q_items.at(i); if (item->hasDynamicConstraint()) { Qt::Orientation itemConstraintOrientation = item->dynamicConstraintOrientation(); diff --git a/src/gui/util/qktxhandler.cpp b/src/gui/util/qktxhandler.cpp index 85e3428bf0..f04da929c3 100644 --- a/src/gui/util/qktxhandler.cpp +++ b/src/gui/util/qktxhandler.cpp @@ -41,7 +41,7 @@ struct KTXHeader { quint32 bytesOfKeyValueData; }; -static const quint32 headerSize = sizeof(KTXHeader); +static const quint32 qktxh_headerSize = sizeof(KTXHeader); // Currently unused, declared for future reference struct KTXKeyValuePairItem { @@ -94,7 +94,7 @@ QTextureFileData QKtxHandler::read() const QByteArray buf = device()->readAll(); const quint32 dataSize = quint32(buf.size()); - if (dataSize < headerSize || !canRead(QByteArray(), buf)) { + if (dataSize < qktxh_headerSize || !canRead(QByteArray(), buf)) { qCDebug(lcQtGuiTextureIO, "Invalid KTX file %s", logName().constData()); return QTextureFileData(); } @@ -117,10 +117,10 @@ QTextureFileData QKtxHandler::read() texData.setNumFaces(decode(header->numberOfFaces)); const quint32 bytesOfKeyValueData = decode(header->bytesOfKeyValueData); - if (headerSize + bytesOfKeyValueData < quint64(buf.length())) // oob check - texData.setKeyValueMetadata( - decodeKeyValues(QByteArrayView(buf.data() + headerSize, bytesOfKeyValueData))); - quint32 offset = headerSize + bytesOfKeyValueData; + if (qktxh_headerSize + bytesOfKeyValueData < quint64(buf.size())) // oob check + texData.setKeyValueMetadata(decodeKeyValues( + QByteArrayView(buf.data() + qktxh_headerSize, bytesOfKeyValueData))); + quint32 offset = qktxh_headerSize + bytesOfKeyValueData; constexpr int MAX_ITERATIONS = 32; // cap iterations in case of corrupt data diff --git a/src/gui/util/qpkmhandler.cpp b/src/gui/util/qpkmhandler.cpp index 7b0fb020df..d84ce2ce7f 100644 --- a/src/gui/util/qpkmhandler.cpp +++ b/src/gui/util/qpkmhandler.cpp @@ -13,7 +13,7 @@ QT_BEGIN_NAMESPACE -static const int headerSize = 16; +static const int qpkmh_headerSize = 16; struct PkmType { @@ -46,7 +46,7 @@ QTextureFileData QPkmHandler::read() return texData; QByteArray fileData = device()->readAll(); - if (fileData.size() < headerSize || !canRead(QByteArray(), fileData)) { + if (fileData.size() < qpkmh_headerSize || !canRead(QByteArray(), fileData)) { qCDebug(lcQtGuiTextureIO, "Invalid PKM file %s", logName().constData()); return QTextureFileData(); } @@ -75,7 +75,7 @@ QTextureFileData QPkmHandler::read() QSize texSize(qFromBigEndian<quint16>(rawData + 12), qFromBigEndian<quint16>(rawData + 14)); texData.setSize(texSize); - texData.setDataOffset(headerSize); + texData.setDataOffset(qpkmh_headerSize); if (!texData.isValid()) { qCDebug(lcQtGuiTextureIO, "Invalid values in header of PKM file %s", logName().constData()); diff --git a/src/gui/util/qtexturefiledata.cpp b/src/gui/util/qtexturefiledata.cpp index 4b9bcb0a33..e1fa900b84 100644 --- a/src/gui/util/qtexturefiledata.cpp +++ b/src/gui/util/qtexturefiledata.cpp @@ -1,6 +1,7 @@ // Copyright (C) 2018 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +#include "QtGui/qimage.h" #include "qtexturefiledata_p.h" #include <QtCore/qsize.h> #include <QtCore/qvarlengtharray.h> @@ -21,14 +22,17 @@ public: QTextureFileDataPrivate(const QTextureFileDataPrivate &other) : QSharedData(other), + mode(other.mode), logName(other.logName), data(other.data), offsets(other.offsets), lengths(other.lengths), + images(other.images), size(other.size), format(other.format), numFaces(other.numFaces), - numLevels(other.numLevels) + numLevels(other.numLevels), + keyValues(other.keyValues) { } @@ -40,13 +44,18 @@ public: { numLevels = force ? levels : qMax(numLevels, levels); numFaces = force ? faces : qMax(numFaces, faces); - - offsets.resize(numFaces); - lengths.resize(numFaces); - - for (auto faceList : { &offsets, &lengths }) - for (auto &levelList : *faceList) + if (mode == QTextureFileData::ByteArrayMode) { + offsets.resize(numFaces); + lengths.resize(numFaces); + + for (auto faceList : { &offsets, &lengths }) + for (auto &levelList : *faceList) + levelList.resize(numLevels); + } else { + images.resize(numFaces); + for (auto &levelList : images) levelList.resize(numLevels); + } } bool isValid(int level, int face) const { return level < numLevels && face < numFaces; } @@ -56,10 +65,12 @@ public: int getLength(int level, int face) const { return lengths[face][level]; } void setLength(int value, int level, int face) { lengths[face][level] = value; } + QTextureFileData::Mode mode = QTextureFileData::ByteArrayMode; QByteArray logName; QByteArray data; QVarLengthArray<QList<int>, MAX_FACES> offsets; // [Face][Level] = offset QVarLengthArray<QList<int>, MAX_FACES> lengths; // [Face][Level] = length + QVarLengthArray<QList<QImage>, MAX_FACES> images; // [Face][Level] = length QSize size; quint32 format = 0; quint32 internalFormat = 0; @@ -69,8 +80,10 @@ public: QMap<QByteArray, QByteArray> keyValues; }; -QTextureFileData::QTextureFileData() +QTextureFileData::QTextureFileData(Mode mode) { + d = new QTextureFileDataPrivate; + d->mode = mode; } QTextureFileData::QTextureFileData(const QTextureFileData &other) @@ -98,11 +111,14 @@ bool QTextureFileData::isValid() const if (!d) return false; + if (d->mode == ImageMode) + return true; // Manually populated: the caller needs to do verification at that time. + if (d->data.isEmpty() || d->size.isEmpty() || (!d->format && !d->internalFormat)) return false; - const int numFacesOffset = d->offsets.length(); - const int numFacesLength = d->lengths.length(); + const int numFacesOffset = d->offsets.size(); + const int numFacesLength = d->lengths.size(); if (numFacesOffset == 0 || numFacesLength == 0 || d->numFaces != numFacesOffset || d->numFaces != numFacesLength) return false; @@ -139,19 +155,26 @@ QByteArray QTextureFileData::data() const void QTextureFileData::setData(const QByteArray &data) { - if (!d.constData()) //### uh think about this design, this is the only way to create; should be constructor instead at least - d = new QTextureFileDataPrivate; - + Q_ASSERT(d->mode == ByteArrayMode); d->data = data; } +void QTextureFileData::setData(const QImage &image, int level, int face) +{ + Q_ASSERT(d->mode == ImageMode); + d->ensureSize(level + 1, face + 1); + d->images[face][level] = image; +} + int QTextureFileData::dataOffset(int level, int face) const { + Q_ASSERT(d->mode == ByteArrayMode); return (d && d->isValid(level, face)) ? d->getOffset(level, face) : 0; } void QTextureFileData::setDataOffset(int offset, int level, int face) { + Q_ASSERT(d->mode == ByteArrayMode); if (d.constData() && level >= 0) { d->ensureSize(level + 1, face + 1); d->setOffset(offset, level, face); @@ -160,22 +183,31 @@ void QTextureFileData::setDataOffset(int offset, int level, int face) int QTextureFileData::dataLength(int level, int face) const { + Q_ASSERT(d->mode == ByteArrayMode); return (d && d->isValid(level, face)) ? d->getLength(level, face) : 0; } QByteArrayView QTextureFileData::getDataView(int level, int face) const { - const int dataLength = this->dataLength(level, face); - const int dataOffset = this->dataOffset(level, face); + if (d->mode == ByteArrayMode) { + const int dataLength = this->dataLength(level, face); + const int dataOffset = this->dataOffset(level, face); - if (d == nullptr || dataLength == 0) - return QByteArrayView(); + if (d == nullptr || dataLength == 0) + return QByteArrayView(); - return QByteArrayView(d->data.constData() + dataOffset, dataLength); + return QByteArrayView(d->data.constData() + dataOffset, dataLength); + } else { + if (!d->isValid(level, face)) + return QByteArrayView(); + const QImage &img = d->images[face][level]; + return img.isNull() ? QByteArrayView() : QByteArrayView(img.constBits(), img.sizeInBytes()); + } } void QTextureFileData::setDataLength(int length, int level, int face) { + Q_ASSERT(d->mode == ByteArrayMode); if (d.constData() && level >= 0) { d->ensureSize(level + 1, face + 1); d->setLength(length, level, face); @@ -286,9 +318,11 @@ QDebug operator<<(QDebug dbg, const QTextureFileData &d) dbg << "glInternalFormat:" << glFormatName(d.glInternalFormat()); dbg << "glBaseInternalFormat:" << glFormatName(d.glBaseInternalFormat()); dbg.nospace() << "Levels: " << d.numLevels(); + dbg.nospace() << "Faces: " << d.numFaces(); if (!d.isValid()) dbg << " {Invalid}"; dbg << ")"; + dbg << (d.d->mode ? "[bytearray-based]" : "[image-based]"); } else { dbg << "null)"; } diff --git a/src/gui/util/qtexturefiledata_p.h b/src/gui/util/qtexturefiledata_p.h index 189a590979..cd0dbe171c 100644 --- a/src/gui/util/qtexturefiledata_p.h +++ b/src/gui/util/qtexturefiledata_p.h @@ -30,7 +30,9 @@ class QTextureFileDataPrivate; class Q_GUI_EXPORT QTextureFileData { public: - QTextureFileData(); + enum Mode { ByteArrayMode, ImageMode }; + + QTextureFileData(Mode mode = ByteArrayMode); QTextureFileData(const QTextureFileData &other); QTextureFileData &operator=(const QTextureFileData &other); ~QTextureFileData(); @@ -42,6 +44,7 @@ public: QByteArray data() const; void setData(const QByteArray &data); + void setData(const QImage &image, int level = 0, int face = 0); int dataOffset(int level = 0, int face = 0) const; void setDataOffset(int offset, int level = 0, int face = 0); @@ -77,6 +80,7 @@ public: private: QSharedDataPointer<QTextureFileDataPrivate> d; + friend Q_GUI_EXPORT QDebug operator<<(QDebug dbg, const QTextureFileData &d); }; Q_DECLARE_TYPEINFO(QTextureFileData, Q_RELOCATABLE_TYPE); diff --git a/src/gui/util/qundostack.cpp b/src/gui/util/qundostack.cpp index 94d97216c4..403833d421 100644 --- a/src/gui/util/qundostack.cpp +++ b/src/gui/util/qundostack.cpp @@ -286,7 +286,7 @@ void QUndoCommand::setText(const QString &text) int QUndoCommand::childCount() const { - return d->child_list.count(); + return d->child_list.size(); } /*! @@ -299,7 +299,7 @@ int QUndoCommand::childCount() const const QUndoCommand *QUndoCommand::child(int index) const { - if (index < 0 || index >= d->child_list.count()) + if (index < 0 || index >= d->child_list.size()) return nullptr; return d->child_list.at(index); } @@ -444,10 +444,10 @@ void QUndoStackPrivate::setIndex(int idx, bool clean) bool QUndoStackPrivate::checkUndoLimit() { - if (undo_limit <= 0 || !macro_stack.isEmpty() || undo_limit >= command_list.count()) + if (undo_limit <= 0 || !macro_stack.isEmpty() || undo_limit >= command_list.size()) return false; - int del_count = command_list.count() - undo_limit; + int del_count = command_list.size() - undo_limit; for (int i = 0; i < del_count; ++i) delete command_list.takeFirst(); @@ -1142,7 +1142,7 @@ void QUndoStack::beginMacro(const QString &text) } d->macro_stack.append(cmd); - if (d->macro_stack.count() == 1) { + if (d->macro_stack.size() == 1) { emit canUndoChanged(false); emit undoTextChanged(QString()); emit canRedoChanged(false); @@ -1191,7 +1191,7 @@ const QUndoCommand *QUndoStack::command(int index) const { Q_D(const QUndoStack); - if (index < 0 || index >= d->command_list.count()) + if (index < 0 || index >= d->command_list.size()) return nullptr; return d->command_list.at(index); } diff --git a/src/gui/util/qvalidator.cpp b/src/gui/util/qvalidator.cpp index f71a66c98c..8b0f9ac321 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 @@ -531,10 +531,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 +545,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 +603,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() @@ -658,7 +672,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 |