diff options
94 files changed, 4776 insertions, 1272 deletions
diff --git a/doc/global/qt-html-templates-offline.qdocconf b/doc/global/qt-html-templates-offline.qdocconf index d5780a35da..f9baab9d19 100644 --- a/doc/global/qt-html-templates-offline.qdocconf +++ b/doc/global/qt-html-templates-offline.qdocconf @@ -3,6 +3,8 @@ include(html-config.qdocconf) include(html-header-offline.qdocconf) include(html-footer.qdocconf) +defines += offlinedocs + #uncomment if navigation bar is not wanted #HTML.nonavigationbar = "true" diff --git a/doc/global/qt-html-templates-online.qdocconf b/doc/global/qt-html-templates-online.qdocconf index 69c399d05a..f1437a2d9c 100644 --- a/doc/global/qt-html-templates-online.qdocconf +++ b/doc/global/qt-html-templates-online.qdocconf @@ -4,6 +4,8 @@ HTML.tocdepth = 2 include(html-header-online.qdocconf) include(html-footer-online.qdocconf) +defines += onlinedocs + #uncomment if navigation bar is not wanted #HTML.nonavigationbar = "true" diff --git a/mkspecs/linux-clang-32/qmake.conf b/mkspecs/linux-clang-32/qmake.conf new file mode 100644 index 0000000000..de872e7431 --- /dev/null +++ b/mkspecs/linux-clang-32/qmake.conf @@ -0,0 +1,20 @@ +# +# qmake configuration for linux-clang +# + +MAKEFILE_GENERATOR = UNIX +CONFIG += incremental + +QMAKE_INCREMENTAL_STYLE = sublib + +include(../common/linux.conf) + +QMAKE_CFLAGS = -m32 +QMAKE_LFLAGS = -m32 + +include(../common/gcc-base-unix.conf) +include(../common/clang.conf) + +QMAKE_LFLAGS += -ccc-gcc-name g++ + +load(qt_config) diff --git a/mkspecs/linux-clang-32/qplatformdefs.h b/mkspecs/linux-clang-32/qplatformdefs.h new file mode 100644 index 0000000000..8f5ce17858 --- /dev/null +++ b/mkspecs/linux-clang-32/qplatformdefs.h @@ -0,0 +1,40 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the qmake spec of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "../linux-clang/qplatformdefs.h" diff --git a/mkspecs/linux-clang-libc++-32/qmake.conf b/mkspecs/linux-clang-libc++-32/qmake.conf new file mode 100644 index 0000000000..f4c006d383 --- /dev/null +++ b/mkspecs/linux-clang-libc++-32/qmake.conf @@ -0,0 +1,10 @@ +# +# qmake configuration for linux-clang and libc++ +# + +include(../linux-clang-32/qmake.conf) + +QMAKE_CXXFLAGS += -stdlib=libc++ +QMAKE_LFLAGS += -stdlib=libc++ + +load(qt_config) diff --git a/mkspecs/linux-clang-libc++-32/qplatformdefs.h b/mkspecs/linux-clang-libc++-32/qplatformdefs.h new file mode 100644 index 0000000000..8f5ce17858 --- /dev/null +++ b/mkspecs/linux-clang-libc++-32/qplatformdefs.h @@ -0,0 +1,40 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the qmake spec of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "../linux-clang/qplatformdefs.h" diff --git a/src/corelib/configure.json b/src/corelib/configure.json index 183eb3a13e..f09ef6c1dd 100644 --- a/src/corelib/configure.json +++ b/src/corelib/configure.json @@ -793,6 +793,13 @@ "condition": "features.proxymodel", "output": [ "publicFeature", "feature" ] }, + "concatenatetablesproxymodel": { + "label": "QConcatenateTablesProxyModel", + "purpose": "Supports concatenating source models.", + "section": "ItemViews", + "condition": "features.proxymodel", + "output": [ "publicFeature", "feature" ] + }, "stringlistmodel": { "label": "QStringListModel", "purpose": "Provides a model that supplies strings to views.", diff --git a/src/corelib/global/qglobal.cpp b/src/corelib/global/qglobal.cpp index a369bbe490..6c608dab74 100644 --- a/src/corelib/global/qglobal.cpp +++ b/src/corelib/global/qglobal.cpp @@ -2142,11 +2142,20 @@ struct QUnixOSVersion static QString unquote(const char *begin, const char *end) { + // man os-release says: + // Variable assignment values must be enclosed in double + // or single quotes if they include spaces, semicolons or + // other special characters outside of A–Z, a–z, 0–9. Shell + // special characters ("$", quotes, backslash, backtick) + // must be escaped with backslashes, following shell style. + // All strings should be in UTF-8 format, and non-printable + // characters should not be used. It is not supported to + // concatenate multiple individually quoted strings. if (*begin == '"') { Q_ASSERT(end[-1] == '"'); - return QString::fromLatin1(begin + 1, end - begin - 2); + return QString::fromUtf8(begin + 1, end - begin - 2); } - return QString::fromLatin1(begin, end - begin); + return QString::fromUtf8(begin, end - begin); } static QByteArray getEtcFileContent(const char *filename) { diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h index b608489576..f56629faa1 100644 --- a/src/corelib/global/qglobal.h +++ b/src/corelib/global/qglobal.h @@ -591,11 +591,11 @@ Q_DECL_CONSTEXPR inline qint64 qRound64(float d) { return d >= 0.0f ? qint64(d + 0.5f) : qint64(d - float(qint64(d-1)) + 0.5f) + qint64(d-1); } template <typename T> -Q_DECL_CONSTEXPR inline const T &qMin(const T &a, const T &b) { return (a < b) ? a : b; } +constexpr inline const T &qMin(const T &a, const T &b) { return (a < b) ? a : b; } template <typename T> -Q_DECL_CONSTEXPR inline const T &qMax(const T &a, const T &b) { return (a < b) ? b : a; } +constexpr inline const T &qMax(const T &a, const T &b) { return (a < b) ? b : a; } template <typename T> -Q_DECL_CONSTEXPR inline const T &qBound(const T &min, const T &val, const T &max) +constexpr inline const T &qBound(const T &min, const T &val, const T &max) { return qMax(min, qMin(max, val)); } #ifndef Q_FORWARD_DECLARE_OBJC_CLASS @@ -1186,9 +1186,6 @@ namespace QtPrivate { //like std::enable_if template <bool B, typename T = void> struct QEnableIf; template <typename T> struct QEnableIf<true, T> { typedef T Type; }; - -template <bool B, typename T, typename F> struct QConditional { typedef T Type; }; -template <typename T, typename F> struct QConditional<false, T, F> { typedef F Type; }; } QT_END_NAMESPACE diff --git a/src/corelib/io/qdir.cpp b/src/corelib/io/qdir.cpp index 39422c3401..fb3a2f0aea 100644 --- a/src/corelib/io/qdir.cpp +++ b/src/corelib/io/qdir.cpp @@ -1040,7 +1040,7 @@ QStringList QDir::nameFilters() const list of filters specified by \a nameFilters. Each name filter is a wildcard (globbing) filter that understands - \c{*} and \c{?} wildcards. (See \l{QRegularExpression wildcard matching}.) + \c{*} and \c{?} wildcards. (See \l{Wildcard matching}.) For example, the following code sets three name filters on a QDir to ensure that only files with extensions typically used for C++ @@ -2120,7 +2120,7 @@ QString QDir::rootPath() patterns in the list of \a filters; otherwise returns \c false. The matching is case insensitive. - \sa {QRegularExpression Wildcard matching}, QRegularExpression::wildcardToRegularExpression(), + \sa {Wildcard matching}, QRegularExpression::wildcardToRegularExpression(), entryList(), entryInfoList() */ bool QDir::match(const QStringList &filters, const QString &fileName) @@ -2143,7 +2143,7 @@ bool QDir::match(const QStringList &filters, const QString &fileName) contain multiple patterns separated by spaces or semicolons. The matching is case insensitive. - \sa {QRegularExpression wildcard matching}, QRegularExpression::wildcardToRegularExpression, + \sa {Wildcard matching}, QRegularExpression::wildcardToRegularExpression, entryList(), entryInfoList() */ bool QDir::match(const QString &filter, const QString &fileName) diff --git a/src/corelib/itemmodels/itemmodels.pri b/src/corelib/itemmodels/itemmodels.pri index 068a8c4b3a..5a977c6623 100644 --- a/src/corelib/itemmodels/itemmodels.pri +++ b/src/corelib/itemmodels/itemmodels.pri @@ -20,6 +20,14 @@ qtConfig(proxymodel) { SOURCES += \ itemmodels/qabstractproxymodel.cpp + qtConfig(concatenatetablesproxymodel) { + HEADERS += \ + itemmodels/qconcatenatetablesproxymodel.h + + SOURCES += \ + itemmodels/qconcatenatetablesproxymodel.cpp + } + qtConfig(identityproxymodel) { HEADERS += \ itemmodels/qidentityproxymodel.h diff --git a/src/corelib/itemmodels/qabstractproxymodel.cpp b/src/corelib/itemmodels/qabstractproxymodel.cpp index b7c49a53e4..095ab6fc71 100644 --- a/src/corelib/itemmodels/qabstractproxymodel.cpp +++ b/src/corelib/itemmodels/qabstractproxymodel.cpp @@ -313,6 +313,17 @@ bool QAbstractProxyModel::setHeaderData(int section, Qt::Orientation orientation return d->model->setHeaderData(sourceSection, orientation, value, role); } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +/*! + \reimp + */ +bool QAbstractProxyModel::clearItemData(const QModelIndex &index) +{ + Q_D(QAbstractProxyModel); + return d->model->clearItemData(mapToSource(index)); +} +#endif + /*! \reimp */ diff --git a/src/corelib/itemmodels/qabstractproxymodel.h b/src/corelib/itemmodels/qabstractproxymodel.h index c4e5d67908..6d9daee75a 100644 --- a/src/corelib/itemmodels/qabstractproxymodel.h +++ b/src/corelib/itemmodels/qabstractproxymodel.h @@ -78,6 +78,9 @@ public: bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; bool setItemData(const QModelIndex& index, const QMap<int, QVariant> &roles) override; bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role = Qt::EditRole) override; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + bool clearItemData(const QModelIndex &index) override; +#endif QModelIndex buddy(const QModelIndex &index) const override; bool canFetchMore(const QModelIndex &parent) const override; diff --git a/src/corelib/itemmodels/qconcatenatetablesproxymodel.cpp b/src/corelib/itemmodels/qconcatenatetablesproxymodel.cpp new file mode 100644 index 0000000000..bbfe2dce16 --- /dev/null +++ b/src/corelib/itemmodels/qconcatenatetablesproxymodel.cpp @@ -0,0 +1,750 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author David Faure <david.faure@kdab.com> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qconcatenatetablesproxymodel.h" +#include <private/qabstractitemmodel_p.h> +#include "qsize.h" +#include "qdebug.h" + +QT_BEGIN_NAMESPACE + +class QConcatenateTablesProxyModelPrivate : public QAbstractItemModelPrivate +{ + Q_DECLARE_PUBLIC(QConcatenateTablesProxyModel); + +public: + QConcatenateTablesProxyModelPrivate(); + + int computeRowsPrior(const QAbstractItemModel *sourceModel) const; + + struct SourceModelForRowResult + { + SourceModelForRowResult() : sourceModel(Q_NULLPTR), sourceRow(-1) {} + QAbstractItemModel *sourceModel; + int sourceRow; + }; + SourceModelForRowResult sourceModelForRow(int row) const; + + void _q_slotRowsAboutToBeInserted(const QModelIndex &, int start, int end); + void _q_slotRowsInserted(const QModelIndex &, int start, int end); + void _q_slotRowsAboutToBeRemoved(const QModelIndex &, int start, int end); + void _q_slotRowsRemoved(const QModelIndex &, int start, int end); + void _q_slotColumnsAboutToBeInserted(const QModelIndex &parent, int start, int end); + void _q_slotColumnsInserted(const QModelIndex &parent, int, int); + void _q_slotColumnsAboutToBeRemoved(const QModelIndex &parent, int start, int end); + void _q_slotColumnsRemoved(const QModelIndex &parent, int, int); + void _q_slotDataChanged(const QModelIndex &from, const QModelIndex &to, const QVector<int> &roles); + void _q_slotSourceLayoutAboutToBeChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint); + void _q_slotSourceLayoutChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint); + void _q_slotModelAboutToBeReset(); + void _q_slotModelReset(); + int columnCountAfterChange(const QAbstractItemModel *model, int newCount) const; + int calculatedColumnCount() const; + void updateColumnCount(); + bool mapDropCoordinatesToSource(int row, int column, const QModelIndex &parent, + int *sourceRow, int *sourceColumn, QModelIndex *sourceParent, QAbstractItemModel **sourceModel) const; + + QVector<QAbstractItemModel *> m_models; + int m_rowCount; // have to maintain it here since we can't compute during model destruction + int m_columnCount; + + // for columns{AboutToBe,}{Inserted,Removed} + int m_newColumnCount; + + // for layoutAboutToBeChanged/layoutChanged + QVector<QPersistentModelIndex> layoutChangePersistentIndexes; + QVector<QModelIndex> layoutChangeProxyIndexes; +}; + +QConcatenateTablesProxyModelPrivate::QConcatenateTablesProxyModelPrivate() + : m_rowCount(0), + m_columnCount(0), + m_newColumnCount(0) +{ +} + +/*! + \since 5.13 + \class QConcatenateTablesProxyModel + \inmodule QtCore + \brief The QConcatenateTablesProxyModel class proxies multiple source models, concatenating their rows + + \ingroup model-view + + QConcatenateTablesProxyModel takes multiple source models and concatenates their rows. + + In other words, the proxy will have all rows of the first source model, + followed by all rows of the second source model, and so on. + + If the source models don't have the same number of columns, the proxy will only + have as many columns as the source model with the smallest number of columns. + Additional columns in other source models will simply be ignored. + + Source models can be added and removed at runtime, and the column count is adjusted accordingly. + + This proxy does not inherit from QAbstractProxyModel because it uses multiple source + models, rather than a single one. + + Only flat models (lists and tables) are supported, tree models are not. + + \sa QAbstractProxyModel, {Model/View Programming}, QIdentityProxyModel, QAbstractItemModel + */ + + +/*! + Constructs a concatenate-rows proxy model with the given \a parent. +*/ +QConcatenateTablesProxyModel::QConcatenateTablesProxyModel(QObject *parent) + : QAbstractItemModel(*new QConcatenateTablesProxyModelPrivate, parent) +{ +} + +/*! + Destroys this proxy model. +*/ +QConcatenateTablesProxyModel::~QConcatenateTablesProxyModel() +{ +} + +/*! + Returns the proxy index for a given \a sourceIndex, which can be from any of the source models. +*/ +QModelIndex QConcatenateTablesProxyModel::mapFromSource(const QModelIndex &sourceIndex) const +{ + Q_D(const QConcatenateTablesProxyModel); + if (!sourceIndex.isValid()) + return QModelIndex(); + const QAbstractItemModel *sourceModel = sourceIndex.model(); + if (!d->m_models.contains(const_cast<QAbstractItemModel *>(sourceModel))) { + qWarning("QConcatenateTablesProxyModel: index from wrong model passed to mapFromSource"); + Q_ASSERT(!"QConcatenateTablesProxyModel: index from wrong model passed to mapFromSource"); + return QModelIndex(); + } + if (sourceIndex.column() >= d->m_columnCount) + return QModelIndex(); + int rowsPrior = d_func()->computeRowsPrior(sourceModel); + return createIndex(rowsPrior + sourceIndex.row(), sourceIndex.column(), sourceIndex.internalPointer()); +} + +/*! + Returns the source index for a given proxy index. +*/ +QModelIndex QConcatenateTablesProxyModel::mapToSource(const QModelIndex &proxyIndex) const +{ + Q_D(const QConcatenateTablesProxyModel); + Q_ASSERT(checkIndex(proxyIndex)); + if (!proxyIndex.isValid()) + return QModelIndex(); + if (proxyIndex.model() != this) { + qWarning("QConcatenateTablesProxyModel: index from wrong model passed to mapToSource"); + Q_ASSERT(!"QConcatenateTablesProxyModel: index from wrong model passed to mapToSource"); + return QModelIndex(); + } + const int row = proxyIndex.row(); + const auto result = d->sourceModelForRow(row); + if (!result.sourceModel) + return QModelIndex(); + return result.sourceModel->index(result.sourceRow, proxyIndex.column()); +} + +/*! + \reimp +*/ +QVariant QConcatenateTablesProxyModel::data(const QModelIndex &index, int role) const +{ + const QModelIndex sourceIndex = mapToSource(index); + Q_ASSERT(checkIndex(index, CheckIndexOption::IndexIsValid)); + if (!sourceIndex.isValid()) + return QVariant(); + return sourceIndex.data(role); +} + +/*! + \reimp +*/ +bool QConcatenateTablesProxyModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + Q_ASSERT(checkIndex(index, CheckIndexOption::IndexIsValid)); + const QModelIndex sourceIndex = mapToSource(index); + Q_ASSERT(sourceIndex.isValid()); + const auto sourceModel = const_cast<QAbstractItemModel *>(sourceIndex.model()); + return sourceModel->setData(sourceIndex, value, role); +} + +/*! + \reimp +*/ +QMap<int, QVariant> QConcatenateTablesProxyModel::itemData(const QModelIndex &proxyIndex) const +{ + Q_ASSERT(checkIndex(proxyIndex)); + const QModelIndex sourceIndex = mapToSource(proxyIndex); + Q_ASSERT(sourceIndex.isValid()); + return sourceIndex.model()->itemData(sourceIndex); +} + +/*! + \reimp +*/ +bool QConcatenateTablesProxyModel::setItemData(const QModelIndex &proxyIndex, const QMap<int, QVariant> &roles) +{ + Q_ASSERT(checkIndex(proxyIndex)); + const QModelIndex sourceIndex = mapToSource(proxyIndex); + Q_ASSERT(sourceIndex.isValid()); + const auto sourceModel = const_cast<QAbstractItemModel *>(sourceIndex.model()); + return sourceModel->setItemData(sourceIndex, roles); +} + +/*! + Returns the flags for the given index. + If the index is valid, the flags come from the source model for this index. + If the index is invalid (as used to determine if dropping onto an empty area + in the view is allowed, for instance), the flags from the first model are returned. +*/ +Qt::ItemFlags QConcatenateTablesProxyModel::flags(const QModelIndex &index) const +{ + Q_D(const QConcatenateTablesProxyModel); + if (d->m_models.isEmpty()) + return Qt::NoItemFlags; + Q_ASSERT(checkIndex(index)); + if (!index.isValid()) + return d->m_models.at(0)->flags(index); + const QModelIndex sourceIndex = mapToSource(index); + Q_ASSERT(sourceIndex.isValid()); + return sourceIndex.model()->flags(sourceIndex); +} + +/*! + This method returns the horizontal header data for the first source model, + and the vertical header data for the source model corresponding to each row. + \reimp +*/ +QVariant QConcatenateTablesProxyModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + Q_D(const QConcatenateTablesProxyModel); + if (d->m_models.isEmpty()) + return QVariant(); + switch (orientation) { + case Qt::Horizontal: + return d->m_models.at(0)->headerData(section, orientation, role); + case Qt::Vertical: { + const auto result = d->sourceModelForRow(section); + Q_ASSERT(result.sourceModel); + return result.sourceModel->headerData(result.sourceRow, orientation, role); + } + } + return QVariant(); +} + +/*! + This method returns the column count of the source model with the smallest number of columns. + \reimp +*/ +int QConcatenateTablesProxyModel::columnCount(const QModelIndex &parent) const +{ + Q_D(const QConcatenateTablesProxyModel); + if (parent.isValid()) + return 0; // flat model + return d->m_columnCount; +} + +/*! + \reimp +*/ +QModelIndex QConcatenateTablesProxyModel::index(int row, int column, const QModelIndex &parent) const +{ + Q_D(const QConcatenateTablesProxyModel); + Q_ASSERT(hasIndex(row, column, parent)); + if (!hasIndex(row, column, parent)) + return QModelIndex(); + Q_ASSERT(checkIndex(parent, QAbstractItemModel::CheckIndexOption::ParentIsInvalid)); // flat model + const auto result = d->sourceModelForRow(row); + Q_ASSERT(result.sourceModel); + return mapFromSource(result.sourceModel->index(result.sourceRow, column)); +} + +/*! + \reimp +*/ +QModelIndex QConcatenateTablesProxyModel::parent(const QModelIndex &index) const +{ + Q_UNUSED(index); + return QModelIndex(); // flat model, no hierarchy +} + +/*! + \reimp +*/ +int QConcatenateTablesProxyModel::rowCount(const QModelIndex &parent) const +{ + Q_D(const QConcatenateTablesProxyModel); + Q_ASSERT(checkIndex(parent, QAbstractItemModel::CheckIndexOption::ParentIsInvalid)); // flat model + Q_UNUSED(parent); + return d->m_rowCount; +} + +/*! + This method returns the mime types for the first source model. + \reimp +*/ +QStringList QConcatenateTablesProxyModel::mimeTypes() const +{ + Q_D(const QConcatenateTablesProxyModel); + if (d->m_models.isEmpty()) + return QStringList(); + return d->m_models.at(0)->mimeTypes(); +} + +/*! + The call is forwarded to the source model of the first index in the list of \a indexes. + + Important: please note that this proxy only supports dragging a single row. + It will assert if called with indexes from multiple rows, because dragging rows that + might come from different source models cannot be implemented generically by this proxy model. + Each piece of data in the QMimeData needs to be merged, which is data-type-specific. + Reimplement this method in a subclass if you want to support dragging multiple rows. + + \reimp +*/ +QMimeData *QConcatenateTablesProxyModel::mimeData(const QModelIndexList &indexes) const +{ + Q_D(const QConcatenateTablesProxyModel); + if (indexes.isEmpty()) + return nullptr; + const QModelIndex firstIndex = indexes.first(); + Q_ASSERT(checkIndex(firstIndex, CheckIndexOption::IndexIsValid)); + const auto result = d->sourceModelForRow(firstIndex.row()); + QModelIndexList sourceIndexes; + sourceIndexes.reserve(indexes.count()); + for (const QModelIndex &index : indexes) { + const QModelIndex sourceIndex = mapToSource(index); + Q_ASSERT(sourceIndex.model() == result.sourceModel); // see documentation above + sourceIndexes.append(sourceIndex); + } + return result.sourceModel->mimeData(sourceIndexes); +} + + +bool QConcatenateTablesProxyModelPrivate::mapDropCoordinatesToSource(int row, int column, const QModelIndex &parent, + int *sourceRow, int *sourceColumn, QModelIndex *sourceParent, QAbstractItemModel **sourceModel) const +{ + Q_Q(const QConcatenateTablesProxyModel); + *sourceColumn = column; + if (!parent.isValid()) { + // Drop after the last item + if (row == -1 || row == m_rowCount) { + *sourceRow = -1; + *sourceModel = m_models.constLast(); + return true; + } + // Drop between toplevel items + const auto result = sourceModelForRow(row); + Q_ASSERT(result.sourceModel); + *sourceRow = result.sourceRow; + *sourceModel = result.sourceModel; + return true; + } else { + if (row > -1) + return false; // flat model, no dropping as new children of items + // Drop onto item + const int targetRow = parent.row(); + const auto result = sourceModelForRow(targetRow); + Q_ASSERT(result.sourceModel); + const QModelIndex sourceIndex = q->mapToSource(parent); + *sourceRow = -1; + *sourceParent = sourceIndex; + *sourceModel = result.sourceModel; + return true; + } +} + +/*! + \reimp +*/ +bool QConcatenateTablesProxyModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const +{ + Q_D(const QConcatenateTablesProxyModel); + if (d->m_models.isEmpty()) + return false; + + int sourceRow, sourceColumn; + QModelIndex sourceParent; + QAbstractItemModel *sourceModel; + if (!d->mapDropCoordinatesToSource(row, column, parent, &sourceRow, &sourceColumn, &sourceParent, &sourceModel)) + return false; + return sourceModel->canDropMimeData(data, action, sourceRow, sourceColumn, sourceParent); +} + +/*! + QConcatenateTablesProxyModel handles dropping onto an item, between items, and after the last item. + In all cases the call is forwarded to the underlying source model. + When dropping onto an item, the source model for this item is called. + When dropping between items, the source model immediately below the drop position is called. + When dropping after the last item, the last source model is called. + + \reimp +*/ +bool QConcatenateTablesProxyModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) +{ + Q_D(const QConcatenateTablesProxyModel); + if (d->m_models.isEmpty()) + return false; + int sourceRow, sourceColumn; + QModelIndex sourceParent; + QAbstractItemModel *sourceModel; + if (!d->mapDropCoordinatesToSource(row, column, parent, &sourceRow, &sourceColumn, &sourceParent, &sourceModel)) + return false; + + return sourceModel->dropMimeData(data, action, sourceRow, sourceColumn, sourceParent); +} + +/*! + \reimp +*/ +QSize QConcatenateTablesProxyModel::span(const QModelIndex &index) const +{ + Q_D(const QConcatenateTablesProxyModel); + Q_ASSERT(checkIndex(index)); + if (d->m_models.isEmpty() || !index.isValid()) + return QSize(); + const QModelIndex sourceIndex = mapToSource(index); + Q_ASSERT(sourceIndex.isValid()); + return sourceIndex.model()->span(sourceIndex); +} + +/*! + Adds a source model \a sourceModel, below all previously added source models. + + The ownership of \a sourceModel is not affected by this. + + The same source model cannot be added more than once. + */ +void QConcatenateTablesProxyModel::addSourceModel(QAbstractItemModel *sourceModel) +{ + Q_D(QConcatenateTablesProxyModel); + Q_ASSERT(sourceModel); + Q_ASSERT(!d->m_models.contains(sourceModel)); + connect(sourceModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)), this, SLOT(_q_slotDataChanged(QModelIndex,QModelIndex,QVector<int>))); + connect(sourceModel, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(_q_slotRowsInserted(QModelIndex,int,int))); + connect(sourceModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(_q_slotRowsRemoved(QModelIndex,int,int))); + connect(sourceModel, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), this, SLOT(_q_slotRowsAboutToBeInserted(QModelIndex,int,int))); + connect(sourceModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(_q_slotRowsAboutToBeRemoved(QModelIndex,int,int))); + + connect(sourceModel, SIGNAL(columnsInserted(QModelIndex,int,int)), this, SLOT(_q_slotColumnsInserted(QModelIndex,int,int))); + connect(sourceModel, SIGNAL(columnsRemoved(QModelIndex,int,int)), this, SLOT(_q_slotColumnsRemoved(QModelIndex,int,int))); + connect(sourceModel, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)), this, SLOT(_q_slotColumnsAboutToBeInserted(QModelIndex,int,int))); + connect(sourceModel, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(_q_slotColumnsAboutToBeRemoved(QModelIndex,int,int))); + + connect(sourceModel, SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>, QAbstractItemModel::LayoutChangeHint)), + this, SLOT(_q_slotSourceLayoutAboutToBeChanged(QList<QPersistentModelIndex>, QAbstractItemModel::LayoutChangeHint))); + connect(sourceModel, SIGNAL(layoutChanged(QList<QPersistentModelIndex>, QAbstractItemModel::LayoutChangeHint)), + this, SLOT(_q_slotSourceLayoutChanged(QList<QPersistentModelIndex>, QAbstractItemModel::LayoutChangeHint))); + connect(sourceModel, SIGNAL(modelAboutToBeReset()), this, SLOT(_q_slotModelAboutToBeReset())); + connect(sourceModel, SIGNAL(modelReset()), this, SLOT(_q_slotModelReset())); + + const int newRows = sourceModel->rowCount(); + if (newRows > 0) + beginInsertRows(QModelIndex(), d->m_rowCount, d->m_rowCount + newRows - 1); + d->m_rowCount += newRows; + d->m_models.append(sourceModel); + if (newRows > 0) + endInsertRows(); + + d->updateColumnCount(); +} + +/*! + Removes the source model \a sourceModel, which was previously added to this proxy. + + The ownership of \a sourceModel is not affected by this. +*/ +void QConcatenateTablesProxyModel::removeSourceModel(QAbstractItemModel *sourceModel) +{ + Q_D(QConcatenateTablesProxyModel); + Q_ASSERT(d->m_models.contains(sourceModel)); + disconnect(sourceModel, 0, this, 0); + + const int rowsRemoved = sourceModel->rowCount(); + const int rowsPrior = d->computeRowsPrior(sourceModel); // location of removed section + + if (rowsRemoved > 0) + beginRemoveRows(QModelIndex(), rowsPrior, rowsPrior + rowsRemoved - 1); + d->m_models.removeOne(sourceModel); + d->m_rowCount -= rowsRemoved; + if (rowsRemoved > 0) + endRemoveRows(); + + d->updateColumnCount(); +} + +void QConcatenateTablesProxyModelPrivate::_q_slotRowsAboutToBeInserted(const QModelIndex &parent, int start, int end) +{ + Q_Q(QConcatenateTablesProxyModel); + if (parent.isValid()) // not supported, the proxy is a flat model + return; + const QAbstractItemModel * const model = static_cast<QAbstractItemModel *>(q->sender()); + const int rowsPrior = computeRowsPrior(model); + q->beginInsertRows(QModelIndex(), rowsPrior + start, rowsPrior + end); +} + +void QConcatenateTablesProxyModelPrivate::_q_slotRowsInserted(const QModelIndex &parent, int start, int end) +{ + Q_Q(QConcatenateTablesProxyModel); + if (parent.isValid()) // flat model + return; + m_rowCount += end - start + 1; + q->endInsertRows(); +} + +void QConcatenateTablesProxyModelPrivate::_q_slotRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) +{ + Q_Q(QConcatenateTablesProxyModel); + if (parent.isValid()) // flat model + return; + const QAbstractItemModel * const model = static_cast<QAbstractItemModel *>(q->sender()); + const int rowsPrior = computeRowsPrior(model); + q->beginRemoveRows(QModelIndex(), rowsPrior + start, rowsPrior + end); +} + +void QConcatenateTablesProxyModelPrivate::_q_slotRowsRemoved(const QModelIndex &parent, int start, int end) +{ + Q_Q(QConcatenateTablesProxyModel); + if (parent.isValid()) // flat model + return; + m_rowCount -= end - start + 1; + q->endRemoveRows(); +} + +void QConcatenateTablesProxyModelPrivate::_q_slotColumnsAboutToBeInserted(const QModelIndex &parent, int start, int end) +{ + Q_Q(QConcatenateTablesProxyModel); + if (parent.isValid()) // flat model + return; + const QAbstractItemModel * const model = static_cast<QAbstractItemModel *>(q->sender()); + const int oldColCount = model->columnCount(); + const int newColCount = columnCountAfterChange(model, oldColCount + end - start + 1); + Q_ASSERT(newColCount >= oldColCount); + if (newColCount > oldColCount) + // If the underlying models have a different number of columns (example: 2 and 3), inserting 2 columns in + // the first model leads to inserting only one column in the proxy, since qMin(2+2,3) == 3. + q->beginInsertColumns(QModelIndex(), start, qMin(end, start + newColCount - oldColCount - 1)); + m_newColumnCount = newColCount; +} + +void QConcatenateTablesProxyModelPrivate::_q_slotColumnsInserted(const QModelIndex &parent, int start, int end) +{ + Q_UNUSED(start); + Q_UNUSED(end); + Q_Q(QConcatenateTablesProxyModel); + if (parent.isValid()) // flat model + return; + if (m_newColumnCount != m_columnCount) { + m_columnCount = m_newColumnCount; + q->endInsertColumns(); + } +} + +void QConcatenateTablesProxyModelPrivate::_q_slotColumnsAboutToBeRemoved(const QModelIndex &parent, int start, int end) +{ + Q_Q(QConcatenateTablesProxyModel); + if (parent.isValid()) // flat model + return; + const QAbstractItemModel * const model = static_cast<QAbstractItemModel *>(q->sender()); + const int oldColCount = model->columnCount(); + const int newColCount = columnCountAfterChange(model, oldColCount - (end - start + 1)); + Q_ASSERT(newColCount <= oldColCount); + if (newColCount < oldColCount) + q->beginRemoveColumns(QModelIndex(), start, qMax(end, start + oldColCount - newColCount - 1)); + m_newColumnCount = newColCount; +} + +void QConcatenateTablesProxyModelPrivate::_q_slotColumnsRemoved(const QModelIndex &parent, int start, int end) +{ + Q_Q(QConcatenateTablesProxyModel); + Q_UNUSED(start); + Q_UNUSED(end); + if (parent.isValid()) // flat model + return; + if (m_newColumnCount != m_columnCount) { + m_columnCount = m_newColumnCount; + q->endRemoveColumns(); + } +} + +void QConcatenateTablesProxyModelPrivate::_q_slotDataChanged(const QModelIndex &from, const QModelIndex &to, const QVector<int> &roles) +{ + Q_Q(QConcatenateTablesProxyModel); + Q_ASSERT(from.isValid()); + Q_ASSERT(to.isValid()); + const QModelIndex myFrom = q->mapFromSource(from); + Q_ASSERT(q->checkIndex(myFrom, QAbstractItemModel::CheckIndexOption::IndexIsValid)); + const QModelIndex myTo = q->mapFromSource(to); + Q_ASSERT(q->checkIndex(myTo, QAbstractItemModel::CheckIndexOption::IndexIsValid)); + emit q->dataChanged(myFrom, myTo, roles); +} + +void QConcatenateTablesProxyModelPrivate::_q_slotSourceLayoutAboutToBeChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint) +{ + Q_Q(QConcatenateTablesProxyModel); + + if (!sourceParents.isEmpty() && !sourceParents.contains(QModelIndex())) + return; + + emit q->layoutAboutToBeChanged({}, hint); + + const QModelIndexList persistentIndexList = q->persistentIndexList(); + layoutChangePersistentIndexes.reserve(persistentIndexList.size()); + layoutChangeProxyIndexes.reserve(persistentIndexList.size()); + + for (const QPersistentModelIndex &proxyPersistentIndex : persistentIndexList) { + layoutChangeProxyIndexes.append(proxyPersistentIndex); + Q_ASSERT(proxyPersistentIndex.isValid()); + const QPersistentModelIndex srcPersistentIndex = q->mapToSource(proxyPersistentIndex); + Q_ASSERT(srcPersistentIndex.isValid()); + layoutChangePersistentIndexes << srcPersistentIndex; + } +} + +void QConcatenateTablesProxyModelPrivate::_q_slotSourceLayoutChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint) +{ + Q_Q(QConcatenateTablesProxyModel); + if (!sourceParents.isEmpty() && !sourceParents.contains(QModelIndex())) + return; + for (int i = 0; i < layoutChangeProxyIndexes.size(); ++i) { + const QModelIndex proxyIdx = layoutChangeProxyIndexes.at(i); + const QModelIndex newProxyIdx = q->mapFromSource(layoutChangePersistentIndexes.at(i)); + q->changePersistentIndex(proxyIdx, newProxyIdx); + } + + layoutChangePersistentIndexes.clear(); + layoutChangeProxyIndexes.clear(); + + emit q->layoutChanged({}, hint); +} + +void QConcatenateTablesProxyModelPrivate::_q_slotModelAboutToBeReset() +{ + Q_Q(QConcatenateTablesProxyModel); + Q_ASSERT(m_models.contains(const_cast<QAbstractItemModel *>(static_cast<const QAbstractItemModel *>(q->sender())))); + q->beginResetModel(); + // A reset might reduce both rowCount and columnCount, and we can't notify of both at the same time, + // and notifying of one after the other leaves an intermediary invalid situation. + // So the only safe choice is to forward it as a full reset. +} + +void QConcatenateTablesProxyModelPrivate::_q_slotModelReset() +{ + Q_Q(QConcatenateTablesProxyModel); + Q_ASSERT(m_models.contains(const_cast<QAbstractItemModel *>(static_cast<const QAbstractItemModel *>(q->sender())))); + m_columnCount = calculatedColumnCount(); + m_rowCount = computeRowsPrior(nullptr); + q->endResetModel(); +} + +int QConcatenateTablesProxyModelPrivate::calculatedColumnCount() const +{ + if (m_models.isEmpty()) + return 0; + + const auto it = std::min_element(m_models.begin(), m_models.end(), [](const QAbstractItemModel* model1, const QAbstractItemModel* model2) { + return model1->columnCount() < model2->columnCount(); + }); + return (*it)->columnCount(); +} + +void QConcatenateTablesProxyModelPrivate::updateColumnCount() +{ + Q_Q(QConcatenateTablesProxyModel); + const int newColumnCount = calculatedColumnCount(); + const int columnDiff = newColumnCount - m_columnCount; + if (columnDiff > 0) { + q->beginInsertColumns(QModelIndex(), m_columnCount, m_columnCount + columnDiff - 1); + m_columnCount = newColumnCount; + q->endInsertColumns(); + } else if (columnDiff < 0) { + const int lastColumn = m_columnCount - 1; + q->beginRemoveColumns(QModelIndex(), lastColumn + columnDiff + 1, lastColumn); + m_columnCount = newColumnCount; + q->endRemoveColumns(); + } +} + +int QConcatenateTablesProxyModelPrivate::columnCountAfterChange(const QAbstractItemModel *model, int newCount) const +{ + int newColumnCount = 0; + for (int i = 0; i < m_models.count(); ++i) { + const QAbstractItemModel *mod = m_models.at(i); + const int colCount = mod == model ? newCount : mod->columnCount(); + if (i == 0) + newColumnCount = colCount; + else + newColumnCount = qMin(colCount, newColumnCount); + } + return newColumnCount; +} + +int QConcatenateTablesProxyModelPrivate::computeRowsPrior(const QAbstractItemModel *sourceModel) const +{ + int rowsPrior = 0; + for (const QAbstractItemModel *model : m_models) { + if (model == sourceModel) + break; + rowsPrior += model->rowCount(); + } + return rowsPrior; +} + +QConcatenateTablesProxyModelPrivate::SourceModelForRowResult QConcatenateTablesProxyModelPrivate::sourceModelForRow(int row) const +{ + QConcatenateTablesProxyModelPrivate::SourceModelForRowResult result; + int rowCount = 0; + for (QAbstractItemModel *model : m_models) { + const int subRowCount = model->rowCount(); + if (rowCount + subRowCount > row) { + result.sourceModel = model; + break; + } + rowCount += subRowCount; + } + result.sourceRow = row - rowCount; + return result; +} + +QT_END_NAMESPACE + +#include "moc_qconcatenatetablesproxymodel.cpp" diff --git a/src/corelib/itemmodels/qconcatenatetablesproxymodel.h b/src/corelib/itemmodels/qconcatenatetablesproxymodel.h new file mode 100644 index 0000000000..85fc6a9c72 --- /dev/null +++ b/src/corelib/itemmodels/qconcatenatetablesproxymodel.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author David Faure <david.faure@kdab.com> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCONCATENATEROWSPROXYMODEL_H +#define QCONCATENATEROWSPROXYMODEL_H + +#include <QtCore/qabstractitemmodel.h> + +QT_BEGIN_NAMESPACE + +class QConcatenateTablesProxyModelPrivate; + +class Q_CORE_EXPORT QConcatenateTablesProxyModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + explicit QConcatenateTablesProxyModel(QObject *parent = nullptr); + ~QConcatenateTablesProxyModel(); + + Q_SCRIPTABLE void addSourceModel(QAbstractItemModel *sourceModel); + Q_SCRIPTABLE void removeSourceModel(QAbstractItemModel *sourceModel); + + QModelIndex mapFromSource(const QModelIndex &sourceIndex) const; + QModelIndex mapToSource(const QModelIndex &proxyIndex) const; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + QMap<int, QVariant> itemData(const QModelIndex &proxyIndex) const override; + bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles) override; + Qt::ItemFlags flags(const QModelIndex &index) const override; + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex &index) const override; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + QStringList mimeTypes() const override; + QMimeData *mimeData(const QModelIndexList &indexes) const override; + bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const override; + bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override; + QSize span(const QModelIndex &index) const override; + +private: + Q_DECLARE_PRIVATE(QConcatenateTablesProxyModel) + Q_DISABLE_COPY(QConcatenateTablesProxyModel) + + Q_PRIVATE_SLOT(d_func(), void _q_slotRowsAboutToBeInserted(const QModelIndex &, int start, int end)) + Q_PRIVATE_SLOT(d_func(), void _q_slotRowsInserted(const QModelIndex &, int start, int end)) + Q_PRIVATE_SLOT(d_func(), void _q_slotRowsAboutToBeRemoved(const QModelIndex &, int start, int end)) + Q_PRIVATE_SLOT(d_func(), void _q_slotRowsRemoved(const QModelIndex &, int start, int end)) + Q_PRIVATE_SLOT(d_func(), void _q_slotColumnsAboutToBeInserted(const QModelIndex &parent, int start, int end)) + Q_PRIVATE_SLOT(d_func(), void _q_slotColumnsInserted(const QModelIndex &parent, int, int)) + Q_PRIVATE_SLOT(d_func(), void _q_slotColumnsAboutToBeRemoved(const QModelIndex &parent, int start, int end)) + Q_PRIVATE_SLOT(d_func(), void _q_slotColumnsRemoved(const QModelIndex &parent, int, int)) + Q_PRIVATE_SLOT(d_func(), void _q_slotDataChanged(const QModelIndex &from, const QModelIndex &to, const QVector<int> &roles)) + Q_PRIVATE_SLOT(d_func(), void _q_slotSourceLayoutAboutToBeChanged(QList<QPersistentModelIndex>, QAbstractItemModel::LayoutChangeHint)) + Q_PRIVATE_SLOT(d_func(), void _q_slotSourceLayoutChanged(const QList<QPersistentModelIndex> &, QAbstractItemModel::LayoutChangeHint)) + Q_PRIVATE_SLOT(d_func(), void _q_slotModelAboutToBeReset()) + Q_PRIVATE_SLOT(d_func(), void _q_slotModelReset()) +}; + +QT_END_NAMESPACE + +#endif // QCONCATENATEROWSPROXYMODEL_H diff --git a/src/corelib/itemmodels/qstringlistmodel.cpp b/src/corelib/itemmodels/qstringlistmodel.cpp index 567e6fa35e..3cc0bee8ef 100644 --- a/src/corelib/itemmodels/qstringlistmodel.cpp +++ b/src/corelib/itemmodels/qstringlistmodel.cpp @@ -136,6 +136,42 @@ QModelIndex QStringListModel::sibling(int row, int column, const QModelIndex &id } /*! + \reimp + \since 5.13 +*/ +QMap<int, QVariant> QStringListModel::itemData(const QModelIndex &index) const +{ + if (!checkIndex(index, CheckIndexOption::IndexIsValid | CheckIndexOption::ParentIsInvalid)) + return QMap<int, QVariant>{}; + const QVariant displayData = lst.at(index.row()); + return QMap<int, QVariant>{{ + std::make_pair<int>(Qt::DisplayRole, displayData), + std::make_pair<int>(Qt::EditRole, displayData) + }}; +} + +/*! + \reimp + \since 5.13 + If \a roles contains both Qt::DisplayRole and Qt::EditRole, the latter will take precedence +*/ +bool QStringListModel::setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles) +{ + if (roles.isEmpty()) + return false; + if (std::any_of(roles.keyBegin(), roles.keyEnd(), [](int role) -> bool { + return role != Qt::DisplayRole && role != Qt::EditRole; + })) { + return false; + } + auto roleIter = roles.constFind(Qt::EditRole); + if (roleIter == roles.constEnd()) + roleIter = roles.constFind(Qt::DisplayRole); + Q_ASSERT(roleIter != roles.constEnd()); + return setData(index, roleIter.value(), roleIter.key()); +} + +/*! Returns data for the specified \a role, from the item with the given \a index. @@ -185,18 +221,22 @@ bool QStringListModel::setData(const QModelIndex &index, const QVariant &value, if (index.row() >= 0 && index.row() < lst.size() && (role == Qt::EditRole || role == Qt::DisplayRole)) { lst.replace(index.row(), value.toString()); - QVector<int> roles; - roles.reserve(2); - roles.append(Qt::DisplayRole); - roles.append(Qt::EditRole); - emit dataChanged(index, index, roles); - // once Q_COMPILER_UNIFORM_INIT can be used, change to: - // emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole}); + emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole}); return true; } return false; } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +/*! + \reimp + */ +bool QStringListModel::clearItemData(const QModelIndex &index) +{ + return setData(index, QVariant(), Qt::EditRole); +} +#endif + /*! Inserts \a count rows into the model, beginning at the given \a row. @@ -249,6 +289,38 @@ bool QStringListModel::removeRows(int row, int count, const QModelIndex &parent) return true; } +/*! + \since 5.13 + \reimp +*/ +bool QStringListModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) +{ + if (sourceRow < 0 + || sourceRow + count - 1 >= rowCount(sourceParent) + || destinationChild <= 0 + || destinationChild > rowCount(destinationParent) + || sourceRow == destinationChild - 1 + || count <= 0) { + return false; + } + if (!beginMoveRows(QModelIndex(), sourceRow, sourceRow + count - 1, QModelIndex(), destinationChild)) + return false; + /* + QList::move assumes that the second argument is the index where the item will end up to + i.e. the valid range for that argument is from 0 to QList::size()-1 + QAbstractItemModel::moveRows when source and destinations have the same parent assumes that + the item will end up being in the row BEFORE the one indicated by destinationChild + i.e. the valid range for that argument is from 1 to QList::size() + For this reason we remove 1 from destinationChild when using it inside QList + */ + destinationChild--; + const int fromRow = destinationChild < sourceRow ? (sourceRow + count - 1) : sourceRow; + while (count--) + lst.move(fromRow, destinationChild); + endMoveRows(); + return true; +} + static bool ascendingLessThan(const QPair<QString, int> &s1, const QPair<QString, int> &s2) { return s1.first < s2.first; diff --git a/src/corelib/itemmodels/qstringlistmodel.h b/src/corelib/itemmodels/qstringlistmodel.h index a40c13ae40..6c83917054 100644 --- a/src/corelib/itemmodels/qstringlistmodel.h +++ b/src/corelib/itemmodels/qstringlistmodel.h @@ -59,11 +59,18 @@ public: QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + bool clearItemData(const QModelIndex &index) override; +#endif Qt::ItemFlags flags(const QModelIndex &index) const override; bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; + bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) override; + + QMap<int, QVariant> itemData(const QModelIndex &index) const override; + bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles) override; void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; diff --git a/src/corelib/kernel/qeventdispatcher_glib.cpp b/src/corelib/kernel/qeventdispatcher_glib.cpp index 4c780a9294..d2201df41b 100644 --- a/src/corelib/kernel/qeventdispatcher_glib.cpp +++ b/src/corelib/kernel/qeventdispatcher_glib.cpp @@ -86,9 +86,10 @@ static gboolean socketNotifierSourceCheck(GSource *source) p->pollfd.fd, t[int(p->socketNotifier->type())]); // ### note, modifies src->pollfds! p->socketNotifier->setEnabled(false); + i--; + } else { + pending = pending || ((p->pollfd.revents & p->pollfd.events) != 0); } - - pending = ((p->pollfd.revents & p->pollfd.events) != 0); } return pending; diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp index eb67544f21..af838ab090 100644 --- a/src/corelib/kernel/qmetatype.cpp +++ b/src/corelib/kernel/qmetatype.cpp @@ -290,6 +290,10 @@ struct DefinedTypesFilter { \value QJsonObject QJsonObject \value QJsonArray QJsonArray \value QJsonDocument QJsonDocument + \value QCborValue QCborValue + \value QCborArray QCborArray + \value QCborMap QCborMap + \value QCborSimpleType QCborSimpleType \value QModelIndex QModelIndex \value QPersistentModelIndex QPersistentModelIndex (since 5.5) \value QUuid QUuid diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 6254330d25..66afa52617 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -2142,7 +2142,8 @@ void QObject::removeEventFilter(QObject *obj) \fn void QObject::destroyed(QObject *obj) This signal is emitted immediately before the object \a obj is - destroyed, and can not be blocked. + destroyed, after any instances of QPointer have been notified, + and can not be blocked. All the objects's children are destroyed immediately after this signal is emitted. diff --git a/src/corelib/plugin/qplugin.h b/src/corelib/plugin/qplugin.h index 5aca22497a..676b5047d6 100644 --- a/src/corelib/plugin/qplugin.h +++ b/src/corelib/plugin/qplugin.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -71,13 +72,27 @@ inline constexpr unsigned char qPluginArchRequirements() } typedef QObject *(*QtPluginInstanceFunction)(); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) typedef const char *(*QtPluginMetaDataFunction)(); +#else +typedef QPair<const uchar *, size_t> (*QtPluginMetaDataFunction)(); +#endif + struct Q_CORE_EXPORT QStaticPlugin { +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +public: + constexpr QStaticPlugin(QtPluginInstanceFunction i, QtPluginMetaDataFunction m) + : instance(i), rawMetaData(m().first), rawMetaDataSize(m().second) + QtPluginInstanceFunction instance; +private: + // ### Qt 6: revise, as this is not standard-layout + const void *rawMetaData; + qsizetype rawMetaDataSize +#elif !defined(Q_QDOC) // Note: This struct is initialized using an initializer list. // As such, it cannot have any new constructors or variables. -#ifndef Q_QDOC QtPluginInstanceFunction instance; QtPluginMetaDataFunction rawMetaData; #else @@ -148,6 +163,15 @@ void Q_CORE_EXPORT qRegisterStaticPluginFunction(QStaticPlugin staticPlugin); return plugin; \ } +#elif QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + +# define QT_MOC_EXPORT_PLUGIN(PLUGINCLASS, PLUGINCLASSNAME) \ + Q_EXTERN_C Q_DECL_EXPORT \ + auto qt_plugin_query_metadata() \ + { return qMakePair<const void *, size_t>(qt_pluginMetaData, sizeof qt_pluginMetaData); } \ + Q_EXTERN_C Q_DECL_EXPORT QT_PREPEND_NAMESPACE(QObject) *qt_plugin_instance() \ + Q_PLUGIN_INSTANCE(PLUGINCLASS) + #else # define QT_MOC_EXPORT_PLUGIN(PLUGINCLASS, PLUGINCLASSNAME) \ diff --git a/src/corelib/tools/qbytearray.cpp b/src/corelib/tools/qbytearray.cpp index b53a60b9bf..1e591d6d69 100644 --- a/src/corelib/tools/qbytearray.cpp +++ b/src/corelib/tools/qbytearray.cpp @@ -3885,9 +3885,7 @@ static qulonglong toIntegral_helper(const char *data, bool *ok, int base, qulong template <typename T> static inline T toIntegral_helper(const char *data, bool *ok, int base) { - // ### Qt6: use std::conditional<std::is_unsigned<T>::value, qulonglong, qlonglong>::type - const bool isUnsigned = T(0) < T(-1); - typedef typename QtPrivate::QConditional<isUnsigned, qulonglong, qlonglong>::Type Int64; + using Int64 = typename std::conditional<std::is_unsigned<T>::value, qulonglong, qlonglong>::type; #if defined(QT_CHECK_RANGE) if (base != 0 && (base < 2 || base > 36)) { diff --git a/src/corelib/tools/qlocale.cpp b/src/corelib/tools/qlocale.cpp index b285e58779..0081b5d566 100644 --- a/src/corelib/tools/qlocale.cpp +++ b/src/corelib/tools/qlocale.cpp @@ -620,13 +620,18 @@ static QLocalePrivate *c_private() */ /*! - Constructs a QSystemLocale object. The constructor will automatically - install this object as the system locale and remove any earlier installed - system locales. + Constructs a QSystemLocale object. + + The constructor will automatically install this object as the system locale, + if there's not one active. It also resets the flag that'll prompt + QLocale::system() to re-initialize its data, so that instantiating a + QSystemLocale transiently (doesn't install the transient as system locale if + there was one already and) triggers an update to the system locale's data. */ QSystemLocale::QSystemLocale() { - _systemLocale = this; + if (!_systemLocale) + _systemLocale = this; globalLocaleData.m_language_id = 0; } @@ -656,15 +661,17 @@ static const QSystemLocale *systemLocale() return QSystemLocale_globalSystemLocale(); } -void QLocalePrivate::updateSystemPrivate() +static void updateSystemPrivate() { - // this function is NOT thread-safe! + // This function is NOT thread-safe! + // It *should not* be called by anything but systemData() const QSystemLocale *sys_locale = systemLocale(); // tell the object that the system locale has changed. sys_locale->query(QSystemLocale::LocaleChanged, QVariant()); - globalLocaleData = *sys_locale->fallbackUiLocale().d->m_data; + // Populate global with fallback as basis: + globalLocaleData = *sys_locale->fallbackUiLocaleData(); QVariant res = sys_locale->query(QSystemLocale::LanguageId, QVariant()); if (!res.isNull()) { @@ -715,7 +722,7 @@ static const QLocaleData *systemData() static QBasicMutex systemDataMutex; systemDataMutex.lock(); if (globalLocaleData.m_language_id == 0) - QLocalePrivate::updateSystemPrivate(); + updateSystemPrivate(); systemDataMutex.unlock(); } @@ -1180,9 +1187,7 @@ static qulonglong toIntegral_helper(const QLocaleData *d, QStringView str, bool template <typename T> static inline T toIntegral_helper(const QLocalePrivate *d, QStringView str, bool *ok) { - // ### Qt6: use std::conditional<std::is_unsigned<T>::value, qulonglong, qlonglong>::type - const bool isUnsigned = T(0) < T(-1); - typedef typename QtPrivate::QConditional<isUnsigned, qulonglong, qlonglong>::Type Int64; + using Int64 = typename std::conditional<std::is_unsigned<T>::value, qulonglong, qlonglong>::type; // we select the right overload by the last, unused parameter Int64 val = toIntegral_helper(d->m_data, str, ok, d->m_numberOptions, Int64()); @@ -2353,7 +2358,6 @@ QString QLocale::toString(double i, char f, int prec) const QLocale QLocale::system() { - // this function is NOT thread-safe! QT_PREPEND_NAMESPACE(systemData)(); // trigger updating of the system data if necessary return QLocale(*systemLocalePrivate->data()); } diff --git a/src/corelib/tools/qlocale.h b/src/corelib/tools/qlocale.h index 8e6c5c503b..2c7229d28b 100644 --- a/src/corelib/tools/qlocale.h +++ b/src/corelib/tools/qlocale.h @@ -436,6 +436,11 @@ public: Cantonese = 357, Osage = 358, Tangut = 359, + Ido = 360, + Lojban = 361, + Sicilian = 362, + SouthernKurdish = 363, + WesternBalochi = 364, Afan = Oromo, Bhutani = Dzongkha, @@ -452,7 +457,7 @@ public: Twi = Akan, Uigur = Uighur, - LastLanguage = Tangut + LastLanguage = WesternBalochi }; enum Script { @@ -1091,6 +1096,7 @@ public: private: QLocale(QLocalePrivate &dd); friend class QLocalePrivate; + friend class QSystemLocale; friend Q_CORE_EXPORT uint qHash(const QLocale &key, uint seed) Q_DECL_NOTHROW; QSharedDataPointer<QLocalePrivate> d; diff --git a/src/corelib/tools/qlocale.qdoc b/src/corelib/tools/qlocale.qdoc index 5c0a9b63e5..0f83a765a5 100644 --- a/src/corelib/tools/qlocale.qdoc +++ b/src/corelib/tools/qlocale.qdoc @@ -105,42 +105,80 @@ \value AnyLanguage \value C The "C" locale is identical in behavior to English/UnitedStates. + \value Abkhazian - \value Oromo \value Afan Obsolete, please use Oromo \value Afar \value Afrikaans + \value Aghem + \value Ahom Since Qt 5.7 + \value Akan + \value Akkadian Since Qt 5.1 \value Akoose Since Qt 5.3 \value Albanian + \value AmericanSignLanguage Since Qt 5.7 \value Amharic + \value AncientEgyptian Since Qt 5.1 + \value AncientGreek Since Qt 5.1 + \value AncientNorthArabian Since Qt 5.5 \value Arabic + \value Aragonese Since Qt 5.1 + \value Aramaic Since Qt 5.1 + \value ArdhamagadhiPrakrit Since Qt 5.7 \value Armenian \value Assamese + \value Asturian + \value Asu + \value Atsam \value Avaric + \value Avestan \value Aymara \value Azerbaijani + \value Bafia + \value Balinese Since Qt 5.1 + \value Bambara + \value Bamun Since Qt 5.1 + \value Basaa \value Bashkir \value Basque + \value Bassa Since Qt 5.5 + \value BatakToba Since Qt 5.1 + \value Belarusian + \value Bemba + \value Bena \value Bengali - \value Dzongkha + \value Bhojpuri Since Qt 5.7 \value Bhutani Obsolete, please use Dzongkha \value Bihari \value Bislama - \value Bosnian + \value Blin \value Bodo + \value Bosnian \value Breton + \value Buginese Since Qt 5.1 + \value Buhid Since Qt 5.1 \value Bulgarian \value Burmese - \value Belarusian \value Byelorussian Obsolete, please use Belarusian - \value Khmer \value Cambodian Obsolete, please use Khmer + \value Cantonese Since Qt 5.7 + \value Carian Since Qt 5.1 \value Catalan + \value CentralKurdish Since Qt 5.5 + \value CentralMoroccoTamazight + \value Chakma Since Qt 5.1 \value Chamorro \value Chechen + \value Cherokee + \value Chewa Obsolete, please use Nyanja + \value Chiga \value Chinese \value Church \value Chuvash + \value ClassicalMandaic Since Qt 5.1 + \value Colognian + \value CongoSwahili + \value Coptic Since Qt 5.1 \value Cornish \value Corsican \value Cree @@ -148,33 +186,58 @@ \value Czech \value Danish \value Divehi + \value Dogri Since Qt 5.1 + \value Duala \value Dutch + \value Dzongkha + \value EasternCham Since Qt 5.1 + \value EasternKayah Since Qt 5.1 + \value Embu \value English \value Esperanto \value Estonian + \value Etruscan Since Qt 5.1 + \value Ewe + \value Ewondo \value Faroese \value Fijian + \value Filipino \value Finnish \value French - \value WesternFrisian same as Frisian \value Frisian same as WesternFrisian + \value Friulian + \value Fulah + \value Ga \value Gaelic \value Galician + \value Ganda + \value Geez \value Georgian \value German + \value Gothic Since Qt 5.1 \value Greek \value Greenlandic \value Guarani \value Gujarati + \value Gusii \value Haitian + \value Hanunoo Since Qt 5.1 \value Hausa + \value Hawaiian \value Hebrew \value Herero + \value HieroglyphicLuwian Since Qt 5.7 \value Hindi \value HiriMotu + \value HmongNjua Since Qt 5.5 + \value Ho Since Qt 5.5 \value Hungarian \value Icelandic + \value Ido Since Qt 5.12 + \value Igbo + \value InariSami Since Qt 5.5 \value Indonesian + \value Ingush Since Qt 5.1 \value Interlingua \value Interlingue \value Inuktitut @@ -183,306 +246,244 @@ \value Italian \value Japanese \value Javanese + \value Jju + \value JolaFonyi + \value Kabuverdianu + \value Kabyle + \value Kako + \value Kalenjin + \value Kamba \value Kannada \value Kanuri \value Kashmiri \value Kazakh + \value Kenyang Since Qt 5.5 + \value Khmer + \value Kiche Since Qt 5.5 + \value Kikuyu \value Kinyarwanda \value Kirghiz \value Komi \value Kongo + \value Konkani \value Korean + \value Koro + \value KoyraboroSenni + \value KoyraChiini + \value Kpelle \value Kurdish - \value Rundi \value Kurundi Obsolete, please use Rundi \value Kwanyama + \value Kwasio \value Lakota Since Qt 5.3 + \value Langi \value Lao + \value LargeFloweryMiao Since Qt 5.1 \value Latin \value Latvian + \value Lepcha Since Qt 5.1 + \value Lezghian Since Qt 5.5 \value Limburgish + \value Limbu Since Qt 5.1 + \value LinearA Since Qt 5.5 \value Lingala + \value Lisu Since Qt 5.1 + \value LiteraryChinese Since Qt 5.7 \value Lithuanian + \value Lojban Since Qt 5.12 + \value LowerSorbian Since Qt 5.5 + \value LowGerman \value LubaKatanga + \value LuleSami Since Qt 5.5 + \value Luo + \value Lu Since Qt 5.1 \value Luxembourgish - \value Marshallese + \value Luyia + \value Lycian Since Qt 5.1 + \value Lydian Since Qt 5.1 \value Macedonian + \value Machame + \value Maithili Since Qt 5.5 + \value MakhuwaMeetto + \value Makonde \value Malagasy \value Malay \value Malayalam \value Maltese + \value Mandingo Since Qt 5.1 + \value ManichaeanMiddlePersian Since Qt 5.5 + \value Manipuri Since Qt 5.1 \value Manx \value Maori + \value Mapuche Since Qt 5.5 \value Marathi + \value Marshallese + \value Masai + \value Mazanderani Since Qt 5.7 + \value Mende Since Qt 5.5 + \value Meroitic Since Qt 5.1 + \value Meru + \value Meta + \value Mohawk Since Qt 5.5 \value Moldavian Obsolete, please use Romanian \value Mongolian + \value Mono Since Qt 5.5 + \value Morisyen + \value Mru Since Qt 5.7 + \value Mundang + \value Nama \value NauruLanguage \value Navaho \value Ndonga \value Nepali - \value Norwegian same as NorwegianBokmal + \value Newari Since Qt 5.7 + \value Ngiemboon + \value Ngomba + \value Nko Since Qt 5.5 + \value NorthernLuri Since Qt 5.7 + \value NorthernSami + \value NorthernSotho + \value NorthernThai Since Qt 5.1 + \value NorthNdebele \value NorwegianBokmal same as Norwegian \value NorwegianNynorsk + \value Norwegian same as NorwegianBokmal + \value Nuer + \value Nyanja + \value Nyankole \value Occitan \value Ojibwa + \value OldIrish Since Qt 5.1 + \value OldNorse Since Qt 5.1 + \value OldPersian Since Qt 5.1 + \value OldTurkish Since Qt 5.1 \value Oriya + \value Oromo + \value Osage Since Qt 5.7 \value Ossetic + \value Pahlavi Since Qt 5.1 + \value Palauan Since Qt 5.7 \value Pali + \value Papiamento Since Qt 5.7 + \value Parthian Since Qt 5.1 \value Pashto \value Persian + \value Phoenician Since Qt 5.1 \value Polish \value Portuguese + \value PrakritLanguage Since Qt 5.1 + \value Prussian Since Qt 5.5 \value Punjabi \value Quechua - \value Romansh + \value Rejang Since Qt 5.1 \value RhaetoRomance Obsolete, please use Romansh \value Romanian + \value Romansh + \value Rombo + \value Rundi \value Russian + \value Rwa + \value Sabaean Since Qt 5.1 + \value Saho + \value Sakha + \value Samaritan Since Qt 5.1 + \value Samburu \value Samoan \value Sango + \value Sangu \value Sanskrit + \value Santali Since Qt 5.1 + \value Saraiki Since Qt 5.7 \value Sardinian + \value Saurashtra Since Qt 5.1 + \value Sena \value Serbian \value SerboCroatian Obsolete, please use Serbian - \value SouthernSotho - \value Tswana + \value Shambala \value Shona + \value SichuanYi + \value Sicilian Since Qt 5.12 + \value Sidamo \value Sindhi \value Sinhala - \value Swati + \value SkoltSami Since Qt 5.5 \value Slovak \value Slovenian + \value Soga \value Somali + \value Sora Since Qt 5.1 + \value SouthernKurdish Since Qt 5.12 + \value SouthernSami Since Qt 5.5 + \value SouthernSotho + \value SouthNdebele \value Spanish \value StandardMoroccanTamazight Since Qt 5.3 \value Sundanese \value Swahili + \value Swati \value Swedish + \value SwissGerman + \value Sylheti Since Qt 5.1 + \value Syriac + \value Tachelhit \value Tagalog Obsolete, please use Filipino + \value Tagbanwa Since Qt 5.1 \value Tahitian + \value TaiDam Since Qt 5.1 + \value TaiNua Since Qt 5.1 + \value Taita \value Tajik \value Tamil + \value Tangut Since Qt 5.7 + \value Taroko + \value Tasawaq \value Tatar + \value TedimChin Since Qt 5.5 \value Telugu + \value Teso \value Thai \value Tibetan + \value Tigre \value Tigrinya + \value TokelauLanguage Since Qt 5.7 + \value TokPisin Since Qt 5.7 \value Tongan \value Tsonga + \value Tswana \value Turkish \value Turkmen + \value TuvaluLanguage Since Qt 5.7 \value Twi Obsolete, please use Akan + \value Tyap + \value Ugaritic Since Qt 5.1 \value Uighur \value Uigur Obsolete, please use Uighur \value Ukrainian + \value UncodedLanguages Since Qt 5.7 + \value UpperSorbian Since Qt 5.5 \value Urdu \value Uzbek + \value Vai + \value Venda \value Vietnamese \value Volapuk + \value Vunjo + \value Walamo \value Walloon + \value Walser + \value Warlpiri Since Qt 5.5 \value Welsh + \value WesternBalochi Since Qt 5.12 + \value WesternFrisian same as Frisian \value Wolof \value Xhosa + \value Yangben \value Yiddish \value Yoruba + \value Zarma \value Zhuang \value Zulu - \value Bosnian - \value Divehi - \value Manx - \value Cornish - \value Akan - \value Konkani - \value Ga - \value Igbo - \value Kamba - \value Syriac - \value Blin - \value Geez - \value Koro - \value Sidamo - \value Atsam - \value Tigre - \value Jju - \value Friulian - \value Venda - \value Ewe - \value Walamo - \value Hawaiian - \value Tyap - \value Nyanja - \value Chewa Obsolete, please use Nyanja - \value Filipino - \value SwissGerman - \value SichuanYi - \value Kpelle - \value LowGerman - \value SouthNdebele - \value NorthernSotho - \value NorthernSami - \value Taroko - \value Gusii - \value Taita - \value Fulah - \value Kikuyu - \value Samburu - \value Sena - \value NorthNdebele - \value Rombo - \value Tachelhit - \value Kabyle - \value Nyankole - \value Bena - \value Vunjo - \value Bambara - \value Embu - \value Cherokee - \value Morisyen - \value Makonde - \value Langi - \value Ganda - \value Bemba - \value Kabuverdianu - \value Meru - \value Kalenjin - \value Nama - \value Machame - \value Colognian - \value Masai - \value Soga - \value Luyia - \value Asu - \value Teso - \value Saho - \value KoyraChiini - \value Rwa - \value Luo - \value Chiga - \value CentralMoroccoTamazight - \value KoyraboroSenni - \value Shambala - \value Aghem - \value Basaa - \value Zarma - \value Duala - \value JolaFonyi - \value Ewondo - \value Bafia - \value MakhuwaMeetto - \value Mundang - \value Kwasio - \value Nuer - \value Sakha - \value Sangu - \value CongoSwahili - \value Tasawaq - \value Vai - \value Walser - \value Yangben - \value Avestan - \value Asturian - \value Ngomba - \value Kako - \value Meta - \value Ngiemboon - \value Aragonese - \value Akkadian - \value AncientEgyptian - \value AncientGreek - \value Aramaic - \value Balinese - \value Bamun - \value BatakToba - \value Buginese - \value Buhid - \value Carian - \value Chakma - \value ClassicalMandaic - \value Coptic - \value Dogri - \value EasternCham - \value EasternKayah - \value Etruscan - \value Gothic - \value Hanunoo - \value Ingush - \value LargeFloweryMiao - \value Lepcha - \value Limbu - \value Lisu - \value Lu - \value Lycian - \value Lydian - \value Mandingo - \value Manipuri - \value Meroitic - \value NorthernThai - \value OldIrish - \value OldNorse - \value OldPersian - \value OldTurkish - \value Pahlavi - \value Parthian - \value Phoenician - \value PrakritLanguage - \value Rejang - \value Sabaean - \value Samaritan - \value Santali - \value Saurashtra - \value Sora - \value Sylheti - \value Tagbanwa - \value TaiDam - \value TaiNua - \value Ugaritic - \value Mapuche Since Qt 5.5 - \value CentralKurdish Since Qt 5.5 - \value LowerSorbian Since Qt 5.5 - \value UpperSorbian Since Qt 5.5 - \value Kenyang Since Qt 5.5 - \value Mohawk Since Qt 5.5 - \value Nko Since Qt 5.5 - \value Prussian Since Qt 5.5 - \value Kiche Since Qt 5.5 - \value SouthernSami Since Qt 5.5 - \value LuleSami Since Qt 5.5 - \value InariSami Since Qt 5.5 - \value SkoltSami Since Qt 5.5 - \value Warlpiri Since Qt 5.5 - \value ManichaeanMiddlePersian Since Qt 5.5 - \value Mende Since Qt 5.5 - \value AncientNorthArabian Since Qt 5.5 - \value LinearA Since Qt 5.5 - \value HmongNjua Since Qt 5.5 - \value Ho Since Qt 5.5 - \value Lezghian Since Qt 5.5 - \value Bassa Since Qt 5.5 - \value Mono Since Qt 5.5 - \value TedimChin Since Qt 5.5 - \value Maithili Since Qt 5.5 - \value LowerSorbian Since Qt 5.5 - \value UpperSorbian Since Qt 5.5 - \value Ahom Since Qt 5.7 - \value AmericanSignLanguage Since Qt 5.7 - \value ArdhamagadhiPrakrit Since Qt 5.7 - \value Bhojpuri Since Qt 5.7 - \value Cantonese Since Qt 5.7 - \value HieroglyphicLuwian Since Qt 5.7 - \value LiteraryChinese Since Qt 5.7 - \value Mazanderani Since Qt 5.7 - \value Mru Since Qt 5.7 - \value Newari Since Qt 5.7 - \value NorthernLuri Since Qt 5.7 - \value Osage Since Qt 5.7 - \value Palauan Since Qt 5.7 - \value Papiamento Since Qt 5.7 - \value Saraiki Since Qt 5.7 - \value Tangut Since Qt 5.7 - \value TokelauLanguage Since Qt 5.7 - \value TokPisin Since Qt 5.7 - \value TuvaluLanguage Since Qt 5.7 - \value UncodedLanguages Since Qt 5.7 + \omitvalue LastLanguage \sa language(), languageToString() @@ -494,7 +495,9 @@ This enumerated type is used to specify a country. \value AnyCountry + \value Afghanistan + \value AlandIslands \value Albania \value Algeria \value AmericanSamoa @@ -506,6 +509,7 @@ \value Argentina \value Armenia \value Aruba + \value AscensionIsland \value Australia \value Austria \value Azerbaijan @@ -520,11 +524,13 @@ \value Bermuda \value Bhutan \value Bolivia + \value Bonaire \value BosniaAndHerzegowina \value Botswana \value BouvetIsland \value Brazil \value BritishIndianOceanTerritory + \value BritishVirginIslands \value Brunei \value Bulgaria \value BurkinaFaso @@ -536,6 +542,7 @@ \value CapeVerde \value CaymanIslands \value CentralAfricanRepublic + \value CeutaAndMelilla \value Chad \value Chile \value China @@ -544,18 +551,19 @@ \value CocosIslands \value Colombia \value Comoros - \value CongoKinshasa \value CongoBrazzaville - \value DemocraticRepublicOfCongo Obsolete, please use CongoKinshasa - \value PeoplesRepublicOfCongo Obsolete, please use CongoBrazzaville + \value CongoKinshasa \value CookIslands \value CostaRica - \value IvoryCoast \value Croatia \value Cuba + \value CuraSao \value Cyprus \value CzechRepublic + \value DemocraticRepublicOfCongo Obsolete, please use CongoKinshasa + \value DemocraticRepublicOfKorea Obsolete, please use NorthKorea \value Denmark + \value DiegoGarcia \value Djibouti \value Dominica \value DominicanRepublic @@ -604,19 +612,18 @@ \value Iran \value Iraq \value Ireland + \value IsleOfMan \value Israel \value Italy + \value IvoryCoast \value Jamaica \value Japan + \value Jersey \value Jordan \value Kazakhstan \value Kenya \value Kiribati - \value NorthKorea - \value SouthKorea - \value DemocraticRepublicOfKorea Obsolete, please use NorthKorea - \value RepublicOfKorea Obsolete, please use SouthKorea - \value Kosovo + \value Kosovo Since Qt 5.2 \value Kuwait \value Kyrgyzstan \value Laos @@ -648,6 +655,7 @@ \value Moldova \value Monaco \value Mongolia + \value Montenegro \value Montserrat \value Morocco \value Mozambique @@ -664,6 +672,7 @@ \value Niue \value NorfolkIsland \value NorthernMarianaIslands + \value NorthKorea \value Norway \value Oman \value OutlyingOceania Since Qt 5.7 @@ -673,6 +682,7 @@ \value Panama \value PapuaNewGuinea \value Paraguay + \value PeoplesRepublicOfCongo Obsolete, please use CongoBrazzaville \value Peru \value Philippines \value Pitcairn @@ -680,32 +690,39 @@ \value Portugal \value PuertoRico \value Qatar + \value RepublicOfKorea Obsolete, please use SouthKorea \value Reunion \value Romania - \value Russia same as RussianFederation \value RussianFederation same as Russia + \value Russia same as RussianFederation \value Rwanda + \value SaintBarthelemy + \value SaintHelena \value SaintKittsAndNevis \value SaintLucia + \value SaintMartin + \value SaintPierreAndMiquelon \value SaintVincentAndTheGrenadines \value Samoa \value SanMarino \value SaoTomeAndPrincipe \value SaudiArabia \value Senegal + \value Serbia \value Seychelles \value SierraLeone \value Singapore + \value SintMaarten \value Slovakia \value Slovenia \value SolomonIslands \value Somalia \value SouthAfrica \value SouthGeorgiaAndTheSouthSandwichIslands + \value SouthKorea + \value SouthSudan \value Spain \value SriLanka - \value SaintHelena - \value SaintPierreAndMiquelon \value Sudan \value Suriname \value SvalbardAndJanMayenIslands @@ -723,6 +740,7 @@ \value Tokelau Obsolete, please use TokelauCountry \value Tonga \value TrinidadAndTobago + \value TristanDaCunha \value Tunisia \value Turkey \value Turkmenistan @@ -735,35 +753,20 @@ \value UnitedKingdom \value UnitedStates \value UnitedStatesMinorOutlyingIslands + \value UnitedStatesVirginIslands \value Uruguay \value Uzbekistan \value Vanuatu \value VaticanCityState \value Venezuela \value Vietnam - \value BritishVirginIslands - \value UnitedStatesVirginIslands \value WallisAndFutunaIslands \value WesternSahara \value World Since Qt 5.12 \value Yemen \value Zambia \value Zimbabwe - \value Montenegro - \value Serbia - \value SaintBarthelemy - \value SaintMartin - \value AscensionIsland - \value AlandIslands - \value DiegoGarcia - \value CeutaAndMelilla - \value IsleOfMan - \value Jersey - \value TristanDaCunha - \value SouthSudan - \value CuraSao - \value Bonaire - \value SintMaarten + \omitvalue LastCountry \sa country(), countryToString() @@ -775,135 +778,136 @@ This enumerated type is used to specify a script. \value AnyScript + \value AdlamScript Since Qt 5.7 \value AhomScript Since Qt 5.7 \value AnatolianHieroglyphsScript Since Qt 5.7 \value ArabicScript \value ArmenianScript - \value AvestanScript - \value BalineseScript - \value BamumScript + \value AvestanScript Since Qt 5.1 + \value BalineseScript Since Qt 5.1 + \value BamumScript Since Qt 5.1 \value BassaVahScript Since Qt 5.5 - \value BatakScript + \value BatakScript Since Qt 5.1 \value BengaliScript \value BhaiksukiScript Since Qt 5.7 - \value BopomofoScript - \value BrahmiScript - \value BrailleScript - \value BugineseScript - \value BuhidScript - \value CanadianAboriginalScript - \value CarianScript + \value BopomofoScript Since Qt 5.1 + \value BrahmiScript Since Qt 5.1 + \value BrailleScript Since Qt 5.1 + \value BugineseScript Since Qt 5.1 + \value BuhidScript Since Qt 5.1 + \value CanadianAboriginalScript Since Qt 5.1 + \value CarianScript Since Qt 5.1 \value CaucasianAlbanianScript Since Qt 5.5 - \value ChakmaScript - \value ChamScript + \value ChakmaScript Since Qt 5.1 + \value ChamScript Since Qt 5.1 \value CherokeeScript - \value CopticScript - \value CypriotScript + \value CopticScript Since Qt 5.1 + \value CuneiformScript Since Qt 5.1 + \value CypriotScript Since Qt 5.1 \value CyrillicScript - \value DeseretScript + \value DeseretScript Since Qt 5.1 \value DevanagariScript \value DuployanScript Since Qt 5.5 - \value EgyptianHieroglyphsScript + \value EgyptianHieroglyphsScript Since Qt 5.1 \value ElbasanScript Since Qt 5.5 \value EthiopicScript - \value FraserScript + \value FraserScript Since Qt 5.1 \value GeorgianScript - \value GlagoliticScript - \value GothicScript + \value GlagoliticScript Since Qt 5.1 + \value GothicScript Since Qt 5.1 \value GranthaScript Since Qt 5.5 \value GreekScript \value GujaratiScript \value GurmukhiScript - \value HanScript - \value HangulScript - \value HanunooScript + \value HangulScript Since Qt 5.1 + \value HanScript Since Qt 5.1 + \value HanunooScript Since Qt 5.1 \value HanWithBopomofoScript Since Qt 5.7 \value HatranScript Since Qt 5.7 \value HebrewScript - \value HiraganaScript - \value ImperialAramaicScript - \value InscriptionalPahlaviScript - \value InscriptionalParthianScript + \value HiraganaScript Since Qt 5.1 + \value ImperialAramaicScript Since Qt 5.1 + \value InscriptionalPahlaviScript Since Qt 5.1 + \value InscriptionalParthianScript Since Qt 5.1 \value JamoScript Since Qt 5.7 \value JapaneseScript - \value JavaneseScript - \value KaithiScript + \value JavaneseScript Since Qt 5.1 + \value KaithiScript Since Qt 5.1 \value KannadaScript - \value KatakanaScript - \value KayahLiScript - \value KharoshthiScript - \value KhmerScript + \value KatakanaScript Since Qt 5.1 + \value KayahLiScript Since Qt 5.1 + \value KharoshthiScript Since Qt 5.1 + \value KhmerScript Since Qt 5.1 \value KhojkiScript Since Qt 5.5 \value KhudawadiScript Since Qt 5.5 \value KoreanScript - \value LannaScript + \value LannaScript Since Qt 5.1 \value LaoScript \value LatinScript - \value LepchaScript - \value LimbuScript + \value LepchaScript Since Qt 5.1 + \value LimbuScript Since Qt 5.1 \value LinearAScript Since Qt 5.5 - \value LinearBScript - \value LycianScript - \value LydianScript + \value LinearBScript Since Qt 5.1 + \value LycianScript Since Qt 5.1 + \value LydianScript Since Qt 5.1 \value MahajaniScript Since Qt 5.5 \value MalayalamScript - \value MandaeanScript + \value MandaeanScript Since Qt 5.1 \value ManichaeanScript Since Qt 5.5 \value MarchenScript Since Qt 5.7 - \value MeiteiMayekScript + \value MeiteiMayekScript Since Qt 5.1 \value MendeKikakuiScript Since Qt 5.5 - \value MeroiticScript - \value MeroiticCursiveScript + \value MeroiticCursiveScript Since Qt 5.1 + \value MeroiticScript Since Qt 5.1 \value ModiScript Since Qt 5.5 \value MongolianScript \value MroScript Since Qt 5.5 \value MultaniScript Since Qt 5.7 \value MyanmarScript \value NabataeanScript Since Qt 5.5 - \value NkoScript \value NewaScript Since Qt 5.7 - \value NewTaiLueScript - \value OghamScript - \value OlChikiScript - \value OldItalicScript + \value NewTaiLueScript Since Qt 5.1 + \value NkoScript Since Qt 5.1 + \value OghamScript Since Qt 5.1 + \value OlChikiScript Since Qt 5.1 \value OldHungarianScript Since Qt 5.7 + \value OldItalicScript Since Qt 5.1 \value OldNorthArabianScript Since Qt 5.5 \value OldPermicScript Since Qt 5.5 - \value OldPersianScript - \value OldSouthArabianScript + \value OldPersianScript Since Qt 5.1 + \value OldSouthArabianScript Since Qt 5.1 \value OriyaScript - \value OrkhonScript + \value OrkhonScript Since Qt 5.1 \value OsageScript Since Qt 5.7 - \value OsmanyaScript + \value OsmanyaScript Since Qt 5.1 \value PahawhHmongScript Since Qt 5.5 \value PalmyreneScript Since Qt 5.5 \value PauCinHauScript Since Qt 5.5 - \value PhagsPaScript - \value PhoenicianScript - \value PollardPhoneticScript + \value PhagsPaScript Since Qt 5.1 + \value PhoenicianScript Since Qt 5.1 + \value PollardPhoneticScript Since Qt 5.1 \value PsalterPahlaviScript Since Qt 5.5 - \value RejangScript - \value RunicScript - \value SamaritanScript - \value SaurashtraScript - \value SharadaScript - \value ShavianScript + \value RejangScript Since Qt 5.1 + \value RunicScript Since Qt 5.1 + \value SamaritanScript Since Qt 5.1 + \value SaurashtraScript Since Qt 5.1 + \value SharadaScript Since Qt 5.1 + \value ShavianScript Since Qt 5.1 \value SiddhamScript Since Qt 5.5 \value SignWritingScript Since Qt 5.7 - \value SimplifiedHanScript same as SimplifiedChineseScript \value SimplifiedChineseScript same as SimplifiedHanScript + \value SimplifiedHanScript same as SimplifiedChineseScript \value SinhalaScript - \value SoraSompengScript - \value CuneiformScript - \value SundaneseScript - \value SylotiNagriScript + \value SoraSompengScript Since Qt 5.1 + \value SundaneseScript Since Qt 5.1 + \value SylotiNagriScript Since Qt 5.1 \value SyriacScript - \value TagalogScript - \value TagbanwaScript - \value TaiLeScript - \value TaiVietScript - \value TakriScript + \value TagalogScript Since Qt 5.1 + \value TagbanwaScript Since Qt 5.1 + \value TaiLeScript Since Qt 5.1 + \value TaiVietScript Since Qt 5.1 + \value TakriScript Since Qt 5.1 \value TamilScript \value TangutScript Since Qt 5.7 \value TeluguScript @@ -912,12 +916,13 @@ \value TibetanScript \value TifinaghScript \value TirhutaScript Since Qt 5.5 - \value TraditionalHanScript same as TraditionalChineseScript \value TraditionalChineseScript same as TraditionalHanScript - \value UgariticScript + \value TraditionalHanScript same as TraditionalChineseScript + \value UgariticScript Since Qt 5.1 \value VaiScript \value VarangKshitiScript Since Qt 5.5 \value YiScript + \omitvalue LastScript \sa script(), scriptToString(), languageToString() diff --git a/src/corelib/tools/qlocale_data_p.h b/src/corelib/tools/qlocale_data_p.h index aeeec2b085..fcff04011f 100644 --- a/src/corelib/tools/qlocale_data_p.h +++ b/src/corelib/tools/qlocale_data_p.h @@ -77,7 +77,7 @@ static const int ImperialMeasurementSystemsCount = // GENERATED PART STARTS HERE /* - This part of the file was generated on 2018-08-13 from the + This part of the file was generated on 2018-08-15 from the Common Locale Data Repository v33.1 http://www.unicode.org/cldr/ @@ -122,6 +122,7 @@ static const QLocaleId likely_subtags[] = { { 195, 0, 0 }, { 195, 7, 239 }, // bem -> bem_Latn_ZM { 186, 0, 0 }, { 186, 7, 210 }, // bez -> bez_Latn_TZ { 20, 0, 0 }, { 20, 2, 33 }, // bg -> bg_Cyrl_BG + { 364, 0, 0 }, { 364, 1, 163 }, // bgn -> bgn_Arab_PK { 343, 0, 0 }, { 343, 13, 100 }, // bho -> bho_Deva_IN { 18, 0, 0 }, { 18, 7, 229 }, // bi -> bi_Latn_VU { 270, 0, 0 }, { 270, 7, 170 }, // bku -> bku_Latn_PH @@ -224,10 +225,12 @@ static const QLocaleId likely_subtags[] = { { 168, 0, 0 }, { 168, 34, 44 }, // ii -> ii_Yiii_CN { 56, 0, 0 }, { 56, 7, 225 }, // ik -> ik_Latn_US { 281, 0, 0 }, { 281, 2, 178 }, // inh -> inh_Cyrl_RU + { 360, 0, 0 }, { 360, 7, 260 }, // io -> io_Latn_001 { 51, 0, 0 }, { 51, 7, 99 }, // is -> is_Latn_IS { 58, 0, 0 }, { 58, 7, 106 }, // it -> it_Latn_IT { 55, 0, 0 }, { 55, 44, 38 }, // iu -> iu_Cans_CA { 59, 0, 0 }, { 59, 19, 108 }, // ja -> ja_Jpan_JP + { 361, 0, 0 }, { 361, 7, 260 }, // jbo -> jbo_Latn_001 { 257, 0, 0 }, { 257, 7, 37 }, // jgo -> jgo_Latn_CM { 200, 0, 0 }, { 200, 7, 210 }, // jmc -> jmc_Latn_TZ { 60, 0, 0 }, { 60, 7, 101 }, // jv -> jv_Latn_ID @@ -385,10 +388,12 @@ static const QLocaleId likely_subtags[] = { { 305, 0, 0 }, { 305, 90, 100 }, // saz -> saz_Saur_IN { 249, 0, 0 }, { 249, 7, 210 }, // sbp -> sbp_Latn_TZ { 115, 0, 0 }, { 115, 7, 106 }, // sc -> sc_Latn_IT + { 362, 0, 0 }, { 362, 7, 106 }, // scn -> scn_Latn_IT { 105, 0, 0 }, { 105, 1, 163 }, // sd -> sd_Arab_PK { 105, 13, 0 }, { 105, 13, 100 }, // sd_Deva -> sd_Deva_IN { 105, 111, 0 }, { 105, 111, 100 }, // sd_Khoj -> sd_Khoj_IN { 105, 125, 0 }, { 105, 125, 100 }, // sd_Sind -> sd_Sind_IN + { 363, 0, 0 }, { 363, 1, 102 }, // sdh -> sdh_Arab_IR { 173, 0, 0 }, { 173, 7, 161 }, // se -> se_Latn_NO { 180, 0, 0 }, { 180, 7, 146 }, // seh -> seh_Latn_MZ { 213, 0, 0 }, { 213, 7, 132 }, // ses -> ses_Latn_ML @@ -1252,6 +1257,11 @@ static const quint16 locale_index[] = { 564, // Cantonese 0, // Osage 0, // Tangut + 566, // Ido + 567, // Lojban + 568, // Sicilian + 569, // Southern Kurdish + 570, // Western Balochi 0 // trailing 0 }; @@ -1823,6 +1833,11 @@ static const QLocaleData locale_data[] = { { 349, 1, 103, 1643, 1644, 1563, 1642, 1776, 45, 43, 101, 8220, 8221, 8216, 8217, 0,6 , 0,6 , 0,6 , 0,6 , 53,10 , 63,17 , 18,7 , 25,12 , 38181,77 , 38181,77 , 158,27 , 38181,77 , 38181,77 , 158,27 , 0,28 , 0,28 , 85,14 , 0,28 , 0,28 , 85,14 , 0,2 , 0,2 , 45,4 , 5,17 , 22,23 , {73,81,68}, 44,5 , 0,7 , 8,5 , 4,0 , 5731,11 , 0,0 , 0, 0, 6, 5, 6 }, // Northern Luri/Arabic/Iraq { 357, 6, 97, 46, 44, 59, 37, 48, 45, 43, 101, 12300, 12301, 12302, 12303, 170,5 , 170,5 , 951,5 , 951,5 , 412,8 , 441,14 , 198,6 , 215,13 , 4671,39 , 4671,39 , 158,27 , 4671,39 , 4671,39 , 158,27 , 2023,28 , 2023,28 , 2051,14 , 2023,28 , 2023,28 , 2051,14 , 60,2 , 57,2 , 45,4 , 5,17 , 22,23 , {72,75,68}, 130,3 , 17057,11 , 4,4 , 4,0 , 5742,2 , 5744,14 , 2, 1, 7, 6, 7 }, // Cantonese/Traditional Han/Hong Kong { 357, 5, 44, 46, 44, 59, 37, 48, 45, 43, 101, 8220, 8221, 8216, 8217, 170,5 , 170,5 , 951,5 , 951,5 , 412,8 , 420,13 , 198,6 , 204,11 , 4671,39 , 4710,38 , 158,27 , 4671,39 , 4710,38 , 158,27 , 2002,21 , 2023,28 , 2051,14 , 2002,21 , 2023,28 , 2051,14 , 60,2 , 57,2 , 45,4 , 5,17 , 22,23 , {67,78,89}, 129,1 , 3122,13 , 4,4 , 4,0 , 5758,2 , 5760,7 , 2, 1, 7, 6, 7 }, // Cantonese/Simplified Han/China + { 360, 7, 260, 46, 44, 59, 37, 48, 45, 43, 101, 8220, 8221, 8216, 8217, 0,6 , 0,6 , 0,6 , 0,6 , 53,10 , 63,17 , 37,5 , 8,10 , 368,48 , 368,48 , 158,27 , 368,48 , 368,48 , 158,27 , 0,28 , 0,28 , 85,14 , 0,28 , 0,28 , 85,14 , 0,2 , 0,2 , 45,4 , 5,17 , 22,23 , {0,0,0}, 0,0 , 2586,0 , 8,5 , 4,0 , 0,0 , 0,0 , 2, 1, 1, 6, 7 }, // Ido/Latin/World + { 361, 7, 260, 46, 44, 59, 37, 48, 45, 43, 101, 8220, 8221, 8216, 8217, 0,6 , 0,6 , 0,6 , 0,6 , 53,10 , 63,17 , 37,5 , 8,10 , 368,48 , 368,48 , 158,27 , 368,48 , 368,48 , 158,27 , 0,28 , 0,28 , 85,14 , 0,28 , 0,28 , 85,14 , 0,2 , 0,2 , 45,4 , 5,17 , 22,23 , {0,0,0}, 0,0 , 2586,0 , 8,5 , 4,0 , 0,0 , 0,0 , 2, 1, 1, 6, 7 }, // Lojban/Latin/World + { 362, 7, 106, 46, 44, 59, 37, 48, 45, 43, 101, 8220, 8221, 8216, 8217, 0,6 , 0,6 , 0,6 , 0,6 , 53,10 , 63,17 , 37,5 , 8,10 , 368,48 , 368,48 , 158,27 , 368,48 , 368,48 , 158,27 , 0,28 , 0,28 , 85,14 , 0,28 , 0,28 , 85,14 , 0,2 , 0,2 , 45,4 , 5,17 , 22,23 , {69,85,82}, 14,1 , 0,7 , 8,5 , 4,0 , 0,0 , 0,0 , 2, 1, 1, 6, 7 }, // Sicilian/Latin/Italy + { 363, 1, 102, 46, 44, 59, 37, 48, 45, 43, 101, 8220, 8221, 8216, 8217, 0,6 , 0,6 , 0,6 , 0,6 , 53,10 , 63,17 , 37,5 , 8,10 , 368,48 , 368,48 , 158,27 , 368,48 , 368,48 , 158,27 , 0,28 , 0,28 , 85,14 , 0,28 , 0,28 , 85,14 , 0,2 , 0,2 , 45,4 , 5,17 , 22,23 , {73,82,82}, 0,0 , 0,7 , 8,5 , 4,0 , 0,0 , 0,0 , 0, 0, 6, 5, 5 }, // Southern Kurdish/Arabic/Iran + { 364, 1, 163, 46, 44, 59, 37, 48, 45, 43, 101, 8220, 8221, 8216, 8217, 0,6 , 0,6 , 0,6 , 0,6 , 53,10 , 63,17 , 37,5 , 8,10 , 368,48 , 368,48 , 158,27 , 368,48 , 368,48 , 158,27 , 0,28 , 0,28 , 85,14 , 0,28 , 0,28 , 85,14 , 0,2 , 0,2 , 45,4 , 5,17 , 22,23 , {80,75,82}, 172,2 , 0,7 , 8,5 , 4,0 , 0,0 , 0,0 , 0, 0, 7, 6, 7 }, // Western Balochi/Arabic/Pakistan { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, {0,0,0}, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0, 0, 0, 0, 0 } // trailing 0s }; @@ -6667,6 +6682,11 @@ static const char language_name_list[] = "Cantonese\0" "Osage\0" "Tangut\0" +"Ido\0" +"Lojban\0" +"Sicilian\0" +"Southern Kurdish\0" +"Western Balochi\0" ; static const quint16 language_name_index[] = { @@ -7030,6 +7050,11 @@ static const quint16 language_name_index[] = { 3050, // Cantonese 3060, // Osage 3066, // Tangut + 3073, // Ido + 3077, // Lojban + 3084, // Sicilian + 3093, // Southern Kurdish + 3110, // Western Balochi }; static const char script_name_list[] = @@ -8213,6 +8238,11 @@ static const unsigned char language_code_list[] = "yue" // Cantonese "osa" // Osage "txg" // Tangut +"io\0" // Ido +"jbo" // Lojban +"scn" // Sicilian +"sdh" // Southern Kurdish +"bgn" // Western Balochi ; static const unsigned char script_code_list[] = diff --git a/src/corelib/tools/qlocale_p.h b/src/corelib/tools/qlocale_p.h index f2e11499c8..f7dbb3a815 100644 --- a/src/corelib/tools/qlocale_p.h +++ b/src/corelib/tools/qlocale_p.h @@ -66,6 +66,7 @@ QT_BEGIN_NAMESPACE #ifndef QT_NO_SYSTEMLOCALE +struct QLocaleData; class Q_CORE_EXPORT QSystemLocale { public: @@ -126,6 +127,7 @@ public: virtual QVariant query(QueryType type, QVariant in) const; virtual QLocale fallbackUiLocale() const; + inline const QLocaleData *fallbackUiLocaleData() const; private: QSystemLocale(bool); friend class QSystemLocaleSingleton; @@ -364,8 +366,6 @@ public: QLocale::MeasurementSystem measurementSystem() const; - static void updateSystemPrivate(); - QString dateTimeToString(QStringView format, const QDateTime &datetime, const QDate &dateOnly, const QTime &timeOnly, const QLocale *q) const; @@ -375,6 +375,10 @@ public: QLocale::NumberOptions m_numberOptions; }; +#ifndef QT_NO_SYSTEMLOCALE +const QLocaleData *QSystemLocale::fallbackUiLocaleData() const { return fallbackUiLocale().d->m_data; } +#endif + template <> inline QLocalePrivate *QSharedDataPointer<QLocalePrivate>::clone() { diff --git a/src/corelib/tools/qmakearray_p.h b/src/corelib/tools/qmakearray_p.h index ae4d7f07c6..71441c2c27 100644 --- a/src/corelib/tools/qmakearray_p.h +++ b/src/corelib/tools/qmakearray_p.h @@ -111,10 +111,10 @@ struct QuickSortFilter<Predicate, QuickSortData<Head, Tail...>> using TailFilteredData = typename QuickSortFilter< Predicate, QuickSortData<Tail...>>::Type; - using Type = typename QConditional< + using Type = typename std::conditional< Predicate<Head>::value, decltype(quickSortConcat(QuickSortData<Head> {}, TailFilteredData{})), - TailFilteredData>::Type; + TailFilteredData>::type; }; template <template <typename> class Predicate> diff --git a/src/corelib/tools/qoffsetstringarray_p.h b/src/corelib/tools/qoffsetstringarray_p.h new file mode 100644 index 0000000000..076316396b --- /dev/null +++ b/src/corelib/tools/qoffsetstringarray_p.h @@ -0,0 +1,197 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QOFFSETSTRINGARRAY_P_H +#define QOFFSETSTRINGARRAY_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "private/qglobal_p.h" + +#include <tuple> +#include <array> + +QT_BEGIN_NAMESPACE + +namespace QtPrivate { +template<int N, int O, int I, int ... Idx> +struct OffsetSequenceHelper : OffsetSequenceHelper<N - 1, O + I, Idx..., O> { }; + +template<int Last, int I, int S, int ... Idx> +struct OffsetSequenceHelper<1, Last, I, S, Idx...> : IndexesList<Last + I, Idx..., Last> +{ + static const constexpr auto Length = Last + I; + using Type = typename std::conditional< + Last <= std::numeric_limits<quint8>::max(), + quint8, + typename std::conditional< + Last <= std::numeric_limits<quint16>::max(), + quint16, + int>::type + >::type; +}; + +template<int ... Idx> +struct OffsetSequence : OffsetSequenceHelper<sizeof ... (Idx), 0, Idx..., 0> { }; + +template<int N> +struct StaticString +{ + const char data[N]; + + constexpr StaticString(const StaticString<N> &other) noexcept = default; +}; + + +template<> +struct StaticString<0> +{ + static constexpr int size() noexcept + { + return 0; + } +}; + +template<typename, typename> +struct StaticStringBuilder; + +template<int ... I1, int ... I2> +struct StaticStringBuilder<IndexesList<I1...>, IndexesList<I2...>> +{ + +QT_WARNING_PUSH +QT_WARNING_DISABLE_MSVC(4100) // The formal parameter is not referenced in the body of the function. + // The unreferenced parameter is ignored. + // It happens when 'rs' is StaticString<0> + template<int N1, int N2> + static constexpr StaticString<N1 + N2> concatenate( + const char (&ls)[N1], const StaticString<N2> &rs) noexcept + { + return StaticString<N1 + N2>{ls[I1]..., rs.data[I2]...}; + } +QT_WARNING_POP +}; + +template<int Sum> +constexpr StaticString<0> staticString() noexcept +{ + return StaticString<0>{}; +} + +template<int Sum, int I, int ... Ix> +constexpr StaticString<Sum> staticString(const char (&s)[I], const char (&...sx)[Ix]) noexcept +{ + return StaticStringBuilder< + makeIndexSequence<I>, + makeIndexSequence<Sum - I>>::concatenate(s, staticString<Sum - I>(sx...)); +} +} // namespace QtPrivate + +template<typename T, int SizeString, int SizeOffsets> +class QOffsetStringArray +{ +public: + using Type = T; + + template<int ... Ox> + constexpr QOffsetStringArray(const QtPrivate::StaticString<SizeString> &str, + QtPrivate::IndexesList<SizeString, Ox...>) noexcept + : m_string(str), + m_offsets{Ox...} + { } + + constexpr inline const char *operator[](const int index) const noexcept + { + return m_string.data + m_offsets[qBound(int(0), index, SizeOffsets - 1)]; + } + + constexpr inline const char *at(const int index) const noexcept + { + return m_string.data + m_offsets[index]; + } + + constexpr inline const char *str() const { return m_string.data; } + constexpr inline const T *offsets() const { return m_offsets; } + constexpr inline int count() const { return SizeOffsets; }; + + static constexpr const auto sizeString = SizeString; + static constexpr const auto sizeOffsets = SizeOffsets; + +private: + QtPrivate::StaticString<SizeString> m_string; + const T m_offsets[SizeOffsets]; +}; + +template<typename T, int N, int ... Ox> +constexpr QOffsetStringArray<T, N, sizeof ... (Ox)> qOffsetStringArray( + const QtPrivate::StaticString<N> &string, + QtPrivate::IndexesList<N, Ox...> offsets) noexcept +{ + return QOffsetStringArray<T, N, sizeof ... (Ox)>( + string, + offsets); +} + +template<int ... Nx> +struct QOffsetStringArrayRet +{ + using Offsets = QtPrivate::OffsetSequence<Nx...>; + using Type = QOffsetStringArray<typename Offsets::Type, Offsets::Length, sizeof ... (Nx)>; +}; + +template<int ... Nx> +constexpr auto qOffsetStringArray(const char (&...strings)[Nx]) noexcept -> typename QOffsetStringArrayRet<Nx...>::Type +{ + using Offsets = QtPrivate::OffsetSequence<Nx...>; + return qOffsetStringArray<typename Offsets::Type>( + QtPrivate::staticString<Offsets::Length>(strings...), Offsets{}); +} + +QT_END_NAMESPACE + +#endif // QOFFSETSTRINGARRAY_P_H diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h index 6be3dcdbe1..da76601e88 100644 --- a/src/corelib/tools/qstring.h +++ b/src/corelib/tools/qstring.h @@ -899,10 +899,8 @@ private: template <typename T> static T toIntegral_helper(const QChar *data, int len, bool *ok, int base) { - // ### Qt6: use std::conditional<std::is_unsigned<T>::value, qulonglong, qlonglong>::type - const bool isUnsigned = T(0) < T(-1); - typedef typename QtPrivate::QConditional<isUnsigned, qulonglong, qlonglong>::Type Int64; - typedef typename QtPrivate::QConditional<isUnsigned, uint, int>::Type Int32; + using Int64 = typename std::conditional<std::is_unsigned<T>::value, qulonglong, qlonglong>::type; + using Int32 = typename std::conditional<std::is_unsigned<T>::value, uint, int>::type; // we select the right overload by casting size() to int or uint Int64 val = toIntegral_helper(data, Int32(len), ok, base); diff --git a/src/corelib/tools/tools.pri b/src/corelib/tools/tools.pri index dc28e0e0a2..995bab694e 100644 --- a/src/corelib/tools/tools.pri +++ b/src/corelib/tools/tools.pri @@ -39,6 +39,7 @@ HEADERS += \ tools/qmargins.h \ tools/qmessageauthenticationcode.h \ tools/qcontiguouscache.h \ + tools/qoffsetstringarray_p.h \ tools/qpair.h \ tools/qpoint.h \ tools/qqueue.h \ diff --git a/src/gui/itemmodels/qstandarditemmodel.cpp b/src/gui/itemmodels/qstandarditemmodel.cpp index 8ec88ebc60..aa640f4d7e 100644 --- a/src/gui/itemmodels/qstandarditemmodel.cpp +++ b/src/gui/itemmodels/qstandarditemmodel.cpp @@ -3010,11 +3010,17 @@ bool QStandardItemModel::setData(const QModelIndex &index, const QVariant &value return true; } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +/*! + \reimp + */ +#else /*! \since 5.12 Removes the data stored in all the roles for the given \a index. \sa setData(), data() */ +#endif bool QStandardItemModel::clearItemData(const QModelIndex &index) { if (!checkIndex(index, CheckIndexOption::IndexIsValid)) diff --git a/src/network/access/qnetworkcookiejar.cpp b/src/network/access/qnetworkcookiejar.cpp index 072a7f249d..af5b126953 100644 --- a/src/network/access/qnetworkcookiejar.cpp +++ b/src/network/access/qnetworkcookiejar.cpp @@ -45,6 +45,14 @@ #include "QtCore/qdatetime.h" #if QT_CONFIG(topleveldomain) #include "private/qtldurl_p.h" +#else +QT_BEGIN_NAMESPACE +static bool qIsEffectiveTLD(QString domain) +{ + // provide minimal checking by not accepting cookies on real TLDs + return !domain.contains(QLatin1Char('.')); +} +QT_END_NAMESPACE #endif QT_BEGIN_NAMESPACE @@ -356,19 +364,12 @@ bool QNetworkCookieJar::validateCookie(const QNetworkCookie &cookie, const QUrl // https://tools.ietf.org/html/rfc6265#section-5.3 step 5 if (host == domain) return true; -#if QT_CONFIG(topleveldomain) // the check for effective TLDs makes the "embedded dot" rule from RFC 2109 section 4.3.2 // redundant; the "leading dot" rule has been relaxed anyway, see QNetworkCookie::normalize() // we remove the leading dot for this check if it's present - if (qIsEffectiveTLD(domain)) - return false; // not accepted -#else - // provide minimal checking by not accepting cookies on real TLDs - if (!domain.contains(QLatin1Char('.'))) - return false; -#endif - - return true; + // Normally defined in qtldurl_p.h, but uses fall-back in this file when topleveldomain isn't + // configured: + return !qIsEffectiveTLD(domain); } QT_END_NAMESPACE diff --git a/src/network/kernel/qauthenticator.cpp b/src/network/kernel/qauthenticator.cpp index 34db5b4b31..47ce9ab0c6 100644 --- a/src/network/kernel/qauthenticator.cpp +++ b/src/network/kernel/qauthenticator.cpp @@ -465,27 +465,12 @@ QByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMet methodString = ""; phase = Done; break; - case QAuthenticatorPrivate::Plain: - response = '\0' + user.toUtf8() + '\0' + password.toUtf8(); - phase = Done; - break; case QAuthenticatorPrivate::Basic: methodString = "Basic "; response = user.toLatin1() + ':' + password.toLatin1(); response = response.toBase64(); phase = Done; break; - case QAuthenticatorPrivate::Login: - if (challenge.contains("VXNlciBOYW1lAA==")) { - response = user.toUtf8().toBase64(); - phase = Phase2; - } else if (challenge.contains("UGFzc3dvcmQA")) { - response = password.toUtf8().toBase64(); - phase = Done; - } - break; - case QAuthenticatorPrivate::CramMd5: - break; case QAuthenticatorPrivate::DigestMd5: methodString = "Digest "; response = digestMd5Response(challenge, requestMethod, path); diff --git a/src/network/kernel/qauthenticator_p.h b/src/network/kernel/qauthenticator_p.h index 8a1ee0ebe6..265cb7afe2 100644 --- a/src/network/kernel/qauthenticator_p.h +++ b/src/network/kernel/qauthenticator_p.h @@ -68,7 +68,7 @@ class QNtlmWindowsHandles; class Q_AUTOTEST_EXPORT QAuthenticatorPrivate { public: - enum Method { None, Basic, Plain, Login, Ntlm, CramMd5, DigestMd5 }; + enum Method { None, Basic, Ntlm, DigestMd5 }; QAuthenticatorPrivate(); ~QAuthenticatorPrivate(); diff --git a/src/sql/models/qsqltablemodel.cpp b/src/sql/models/qsqltablemodel.cpp index a33f76838f..98d6ddf882 100644 --- a/src/sql/models/qsqltablemodel.cpp +++ b/src/sql/models/qsqltablemodel.cpp @@ -607,6 +607,16 @@ bool QSqlTableModel::setData(const QModelIndex &index, const QVariant &value, in return true; } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +/*! + \reimp + */ +bool QStringListModel::clearItemData(const QModelIndex &index) +{ + return setData(index, QVariant(), Qt::EditRole); +} +#endif + /*! This function simply calls QSqlQueryModel::setQuery(\a query). You should normally not call it on a QSqlTableModel. Instead, use diff --git a/src/sql/models/qsqltablemodel.h b/src/sql/models/qsqltablemodel.h index 7acc7dc94d..eba27e60ec 100644 --- a/src/sql/models/qsqltablemodel.h +++ b/src/sql/models/qsqltablemodel.h @@ -74,6 +74,9 @@ public: QSqlRecord record(int row) const; QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + bool clearItemData(const QModelIndex &index) override; +#endif QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; diff --git a/src/widgets/itemviews/qheaderview.cpp b/src/widgets/itemviews/qheaderview.cpp index 3396a91dc5..2f276a7a8c 100644 --- a/src/widgets/itemviews/qheaderview.cpp +++ b/src/widgets/itemviews/qheaderview.cpp @@ -3877,9 +3877,9 @@ void QHeaderViewPrivate::updateDefaultSectionSizeFromStyle() void QHeaderViewPrivate::recalcSectionStartPos() const // linear (but fast) { int pixelpos = 0; - for (QVector<SectionItem>::const_iterator i = sectionItems.constBegin(); i != sectionItems.constEnd(); ++i) { - i->calculated_startpos = pixelpos; // write into const mutable - pixelpos += i->size; + for (const SectionItem &i : sectionItems) { + i.calculated_startpos = pixelpos; // write into const mutable + pixelpos += i.size; } sectionStartposRecalc = false; } diff --git a/src/widgets/itemviews/qitemdelegate.cpp b/src/widgets/itemviews/qitemdelegate.cpp index dff4cc4593..9c65d5fddd 100644 --- a/src/widgets/itemviews/qitemdelegate.cpp +++ b/src/widgets/itemviews/qitemdelegate.cpp @@ -1089,7 +1089,7 @@ QRect QItemDelegate::doCheck(const QStyleOptionViewItem &option, opt.rect = bounding; const QWidget *widget = d->widget(option); // cast QStyle *style = widget ? widget->style() : QApplication::style(); - return style->subElementRect(QStyle::SE_ViewItemCheckIndicator, &opt, widget); + return style->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &opt, widget); } return QRect(); } diff --git a/src/widgets/itemviews/qlistview.cpp b/src/widgets/itemviews/qlistview.cpp index e5769940d4..fdac332367 100644 --- a/src/widgets/itemviews/qlistview.cpp +++ b/src/widgets/itemviews/qlistview.cpp @@ -2246,8 +2246,8 @@ int QListModeViewBase::verticalScrollToValue(int index, QListView::ScrollHint hi } else { int scrollBarValue = verticalScrollBar()->value(); int numHidden = 0; - for (int i = 0; i < flowPositions.count() - 1 && i <= scrollBarValue; ++i) - if (isHidden(i)) + for (const auto &idx : qAsConst(dd->hiddenRows)) + if (idx.row() <= scrollBarValue) ++numHidden; value = qBound(0, scrollValueMap.at(verticalScrollBar()->value()) - numHidden, flowPositions.count() - 1); } @@ -2687,21 +2687,24 @@ int QListModeViewBase::perItemScrollToValue(int index, int scrollValue, int view return scrollValue; itemExtent += spacing(); - QVector<int> visibleFlowPositions; - visibleFlowPositions.reserve(flowPositions.count() - 1); - for (int i = 0; i < flowPositions.count() - 1; i++) { // flowPositions count is +1 larger than actual row count - if (!isHidden(i)) - visibleFlowPositions.append(flowPositions.at(i)); - } - + QVector<int> hiddenRows = dd->hiddenRowIds(); + std::sort(hiddenRows.begin(), hiddenRows.end()); + int hiddenRowsBefore = 0; + for (int i = 0; i < hiddenRows.size() - 1; ++i) + if (hiddenRows.at(i) > index + hiddenRowsBefore) + break; + else + ++hiddenRowsBefore; if (!wrap) { int topIndex = index; const int bottomIndex = topIndex; - const int bottomCoordinate = visibleFlowPositions.at(index); - + const int bottomCoordinate = flowPositions.at(index + hiddenRowsBefore); while (topIndex > 0 && - (bottomCoordinate - visibleFlowPositions.at(topIndex - 1) + itemExtent) <= (viewportSize)) { + (bottomCoordinate - flowPositions.at(topIndex + hiddenRowsBefore - 1) + itemExtent) <= (viewportSize)) { topIndex--; + // will the next one be a hidden row -> skip + while (hiddenRowsBefore > 0 && hiddenRows.at(hiddenRowsBefore - 1) >= topIndex + hiddenRowsBefore - 1) + hiddenRowsBefore--; } const int itemCount = bottomIndex - topIndex + 1; @@ -2720,7 +2723,7 @@ int QListModeViewBase::perItemScrollToValue(int index, int scrollValue, int view ? Qt::Horizontal : Qt::Vertical); if (flowOrientation == orientation) { // scrolling in the "flow" direction // ### wrapped scrolling in the flow direction - return visibleFlowPositions.at(index); // ### always pixel based for now + return flowPositions.at(index + hiddenRowsBefore); // ### always pixel based for now } else if (!segmentStartRows.isEmpty()) { // we are scrolling in the "segment" direction int segment = qBinarySearch<int>(segmentStartRows, index, 0, segmentStartRows.count() - 1); int leftSegment = segment; @@ -3354,9 +3357,9 @@ int QListView::visualIndex(const QModelIndex &index) const d->executePostedLayout(); QListViewItem itm = d->indexToListViewItem(index); int visualIndex = d->commonListView->itemIndex(itm); - for (int row = 0; row <= index.row() && visualIndex >= 0; row++) { - if (d->isHidden(row)) - visualIndex--; + for (const auto &idx : qAsConst(d->hiddenRows)) { + if (idx.row() <= index.row()) + --visualIndex; } return visualIndex; } diff --git a/src/widgets/itemviews/qlistview_p.h b/src/widgets/itemviews/qlistview_p.h index 181386d4d0..3f997ef7e3 100644 --- a/src/widgets/itemviews/qlistview_p.h +++ b/src/widgets/itemviews/qlistview_p.h @@ -377,6 +377,14 @@ public: QModelIndex idx = model->index(row, 0, root); return isPersistent(idx) && hiddenRows.contains(idx); } + // helper to avoid checking for isPersistent and creating persistent indexes as above in isHidden + QVector<int> hiddenRowIds() const { + QVector<int> rowIds; + rowIds.reserve(hiddenRows.size()); + for (const auto &idx : hiddenRows) + rowIds += idx.row(); + return rowIds; + } inline bool isHiddenOrDisabled(int row) const { return isHidden(row) || !isIndexEnabled(modelIndex(row)); } void removeCurrentAndDisabled(QVector<QModelIndex> *indexes, const QModelIndex ¤t) const; diff --git a/src/widgets/itemviews/qlistwidget.cpp b/src/widgets/itemviews/qlistwidget.cpp index 72e0a67a64..895622616e 100644 --- a/src/widgets/itemviews/qlistwidget.cpp +++ b/src/widgets/itemviews/qlistwidget.cpp @@ -227,6 +227,22 @@ bool QListModel::setData(const QModelIndex &index, const QVariant &value, int ro return true; } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +bool QListModel::clearItemData(const QModelIndex &index) +{ + if (!checkIndex(index, CheckIndexOption::IndexIsValid)) + return false; + QListWidgetItem *item = items.at(index.row()); + const auto beginIter = item->d->values.cbegin(); + const auto endIter = item->d->values.cend(); + if (std::all_of(beginIter, endIter, [](const QWidgetItemData& data) -> bool { return !data.value.isValid(); })) + return true; //it's already cleared + item->d->values.clear(); + emit dataChanged(index, index, QVector<int>{}); + return true; +} +#endif + QMap<int, QVariant> QListModel::itemData(const QModelIndex &index) const { QMap<int, QVariant> roles; @@ -277,6 +293,30 @@ bool QListModel::removeRows(int row, int count, const QModelIndex &parent) return true; } +/*! + \since 5.13 + \reimp +*/ +bool QListModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) +{ + if (sourceRow < 0 + || sourceRow + count - 1 >= rowCount(sourceParent) + || destinationChild <= 0 + || destinationChild > rowCount(destinationParent) + || sourceRow == destinationChild - 1 + || count <= 0) { + return false; + } + if (!beginMoveRows(QModelIndex(), sourceRow, sourceRow + count - 1, QModelIndex(), destinationChild)) + return false; + destinationChild--; + const int fromRow = destinationChild < sourceRow ? (sourceRow + count - 1) : sourceRow; + while (count--) + items.move(fromRow, destinationChild); + endMoveRows(); + return true; +} + Qt::ItemFlags QListModel::flags(const QModelIndex &index) const { if (!index.isValid() || index.row() >= items.count() || index.model() != this) @@ -289,7 +329,7 @@ void QListModel::sort(int column, Qt::SortOrder order) if (column != 0) return; - emit layoutAboutToBeChanged(); + emit layoutAboutToBeChanged({}, QAbstractItemModel::VerticalSortHint); QVector < QPair<QListWidgetItem*,int> > sorting(items.count()); for (int i = 0; i < items.count(); ++i) { @@ -313,7 +353,7 @@ void QListModel::sort(int column, Qt::SortOrder order) } changePersistentIndexList(fromIndexes, toIndexes); - emit layoutChanged(); + emit layoutChanged({}, QAbstractItemModel::VerticalSortHint); } /** diff --git a/src/widgets/itemviews/qlistwidget_p.h b/src/widgets/itemviews/qlistwidget_p.h index 9cb3d5966b..65a7124322 100644 --- a/src/widgets/itemviews/qlistwidget_p.h +++ b/src/widgets/itemviews/qlistwidget_p.h @@ -100,11 +100,15 @@ public: QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex &index, const QVariant &value, int role) override; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + bool clearItemData(const QModelIndex &index) override; +#endif QMap<int, QVariant> itemData(const QModelIndex &index) const override; bool insertRows(int row, int count = 1, const QModelIndex &parent = QModelIndex()) override; bool removeRows(int row, int count = 1, const QModelIndex &parent = QModelIndex()) override; + bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) override; Qt::ItemFlags flags(const QModelIndex &index) const override; diff --git a/src/widgets/itemviews/qtableview.cpp b/src/widgets/itemviews/qtableview.cpp index 9725a768de..e7edd08d2a 100644 --- a/src/widgets/itemviews/qtableview.cpp +++ b/src/widgets/itemviews/qtableview.cpp @@ -1383,12 +1383,12 @@ void QTableView::paintEvent(QPaintEvent *event) //firstVisualRow is the visual index of the first visible row. lastVisualRow is the visual index of the last visible Row. //same goes for ...VisualColumn int firstVisualRow = qMax(verticalHeader->visualIndexAt(0),0); - int lastVisualRow = verticalHeader->visualIndexAt(verticalHeader->viewport()->height()); + int lastVisualRow = verticalHeader->visualIndexAt(verticalHeader->height()); if (lastVisualRow == -1) lastVisualRow = d->model->rowCount(d->root) - 1; int firstVisualColumn = horizontalHeader->visualIndexAt(0); - int lastVisualColumn = horizontalHeader->visualIndexAt(horizontalHeader->viewport()->width()); + int lastVisualColumn = horizontalHeader->visualIndexAt(horizontalHeader->width()); if (rightToLeft) qSwap(firstVisualColumn, lastVisualColumn); if (firstVisualColumn == -1) diff --git a/src/widgets/itemviews/qtablewidget.cpp b/src/widgets/itemviews/qtablewidget.cpp index 11925af7a0..169cc5a17c 100644 --- a/src/widgets/itemviews/qtablewidget.cpp +++ b/src/widgets/itemviews/qtablewidget.cpp @@ -189,7 +189,7 @@ void QTableModel::setItem(int row, int column, QTableWidgetItem *item) sortedRow = qMax((int)(it - colItems.begin()), 0); } if (sortedRow != row) { - emit layoutAboutToBeChanged(); + emit layoutAboutToBeChanged({}, QAbstractItemModel::VerticalSortHint); // move the items @ row to sortedRow int cc = columnCount(); QVector<QTableWidgetItem*> rowItems(cc); @@ -209,7 +209,7 @@ void QTableModel::setItem(int row, int column, QTableWidgetItem *item) changePersistentIndexList(oldPersistentIndexes, newPersistentIndexes); - emit layoutChanged(); + emit layoutChanged({}, QAbstractItemModel::VerticalSortHint); return; } } @@ -480,6 +480,24 @@ bool QTableModel::setItemData(const QModelIndex &index, const QMap<int, QVariant return true; } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +bool QTableModel::clearItemData(const QModelIndex &index) +{ + if (!checkIndex(index, CheckIndexOption::IndexIsValid)) + return false; + QTableWidgetItem *itm = item(index); + if (!itm) + return false; + const auto beginIter = itm->values.cbegin(); + const auto endIter = itm->values.cend(); + if (std::all_of(beginIter, endIter, [](const QWidgetItemData& data) -> bool { return !data.value.isValid(); })) + return true; //it's already cleared + itm->values.clear(); + emit dataChanged(index, index, QVector<int>{}); + return true; +} +#endif + Qt::ItemFlags QTableModel::flags(const QModelIndex &index) const { if (!index.isValid()) @@ -530,12 +548,12 @@ void QTableModel::sort(int column, Qt::SortOrder order) } } - emit layoutAboutToBeChanged(); + emit layoutAboutToBeChanged({}, QAbstractItemModel::VerticalSortHint); tableItems = sorted_table; changePersistentIndexList(from, to); // ### slow - emit layoutChanged(); + emit layoutChanged({}, QAbstractItemModel::VerticalSortHint); } /* @@ -580,7 +598,7 @@ void QTableModel::ensureSorted(int column, Qt::SortOrder order, vit = colItems.insert(vit, item); if (newRow != oldRow) { if (!changed) { - emit layoutAboutToBeChanged(); + emit layoutAboutToBeChanged({}, QAbstractItemModel::VerticalSortHint); oldPersistentIndexes = persistentIndexList(); newPersistentIndexes = oldPersistentIndexes; changed = true; @@ -615,7 +633,7 @@ void QTableModel::ensureSorted(int column, Qt::SortOrder order, verticalHeaderItems = newVertical; changePersistentIndexList(oldPersistentIndexes, newPersistentIndexes); - emit layoutChanged(); + emit layoutChanged({}, QAbstractItemModel::VerticalSortHint); } } diff --git a/src/widgets/itemviews/qtablewidget_p.h b/src/widgets/itemviews/qtablewidget_p.h index 9899272fce..d88326f129 100644 --- a/src/widgets/itemviews/qtablewidget_p.h +++ b/src/widgets/itemviews/qtablewidget_p.h @@ -129,6 +129,9 @@ public: QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex &index, const QVariant &value, int role) override; bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles) override; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + bool clearItemData(const QModelIndex &index) override; +#endif QMap<int, QVariant> itemData(const QModelIndex &index) const override; diff --git a/src/widgets/itemviews/qtreewidget.cpp b/src/widgets/itemviews/qtreewidget.cpp index a0af27115d..4768869843 100644 --- a/src/widgets/itemviews/qtreewidget.cpp +++ b/src/widgets/itemviews/qtreewidget.cpp @@ -390,6 +390,27 @@ bool QTreeModel::setData(const QModelIndex &index, const QVariant &value, int ro return false; } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +bool QTreeModel::clearItemData(const QModelIndex &index) +{ + if (!checkIndex(index, CheckIndexOption::IndexIsValid)) + return false; + QTreeWidgetItem *itm = item(index); + if (!itm) + return false; + const auto beginIter = itm->values.at(index.column()).cbegin(); + const auto endIter = itm->values.at(index.column()).cend(); + if (std::all_of(beginIter, endIter, [](const QWidgetItemData& data) -> bool { return !data.value.isValid(); }) + && !itm->d->display.at(index.column()).isValid()) { + return true; //it's already cleared + } + itm->d->display[index.column()] = QVariant(); + itm->values[index.column()].clear(); + emit dataChanged(index, index, QVector<int>{}); + return true; +} +#endif + QMap<int, QVariant> QTreeModel::itemData(const QModelIndex &index) const { QMap<int, QVariant> roles; @@ -635,7 +656,7 @@ void QTreeModel::ensureSorted(int column, Qt::SortOrder order, // we are going to change the persistent indexes, so we need to prepare if (!changed) { // this will only happen once changed = true; - emit layoutAboutToBeChanged(); // the selection model needs to know + emit layoutAboutToBeChanged({parent}, QAbstractItemModel::VerticalSortHint); // the selection model needs to know oldPersistentIndexes = persistentIndexList(); newPersistentIndexes = oldPersistentIndexes; } @@ -668,7 +689,7 @@ void QTreeModel::ensureSorted(int column, Qt::SortOrder order, if (changed) { itm->children = lst; changePersistentIndexList(oldPersistentIndexes, newPersistentIndexes); - emit layoutChanged(); + emit layoutChanged({parent}, QAbstractItemModel::VerticalSortHint); } } @@ -2164,9 +2185,9 @@ void QTreeWidgetItem::sortChildren(int column, Qt::SortOrder order, bool climb) QTreeModel::SkipSorting skipSorting(model); int oldSortColumn = view->d_func()->explicitSortColumn; view->d_func()->explicitSortColumn = column; - emit model->layoutAboutToBeChanged(); + emit model->layoutAboutToBeChanged({}, QAbstractItemModel::VerticalSortHint); d->sortChildren(column, order, climb); - emit model->layoutChanged(); + emit model->layoutChanged({}, QAbstractItemModel::VerticalSortHint); view->d_func()->explicitSortColumn = oldSortColumn; } diff --git a/src/widgets/itemviews/qtreewidget_p.h b/src/widgets/itemviews/qtreewidget_p.h index adc2c2c421..ee4a633468 100644 --- a/src/widgets/itemviews/qtreewidget_p.h +++ b/src/widgets/itemviews/qtreewidget_p.h @@ -99,7 +99,9 @@ public: QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex &index, const QVariant &value, int role) override; - +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + bool clearItemData(const QModelIndex &index) override; +#endif QMap<int, QVariant> itemData(const QModelIndex &index) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; diff --git a/src/widgets/styles/qcommonstyle.cpp b/src/widgets/styles/qcommonstyle.cpp index 3ee3e856bc..87d233f024 100644 --- a/src/widgets/styles/qcommonstyle.cpp +++ b/src/widgets/styles/qcommonstyle.cpp @@ -3067,7 +3067,7 @@ QRect QCommonStyle::subElementRect(SubElement sr, const QStyleOption *opt, } d->cachedOption = new QStyleOptionViewItem(*vopt); } - if (sr == SE_ViewItemCheckIndicator) + if (sr == SE_ItemViewItemCheckIndicator) r = d->checkRect; else if (sr == SE_ItemViewItemDecoration) r = d->decorationRect; diff --git a/src/widgets/styles/qstyle.h b/src/widgets/styles/qstyle.h index 9192dae864..8256f908db 100644 --- a/src/widgets/styles/qstyle.h +++ b/src/widgets/styles/qstyle.h @@ -308,8 +308,8 @@ public: SE_TabWidgetLeftCorner, SE_TabWidgetRightCorner, - SE_ViewItemCheckIndicator, // ### Qt 6: remove - SE_ItemViewItemCheckIndicator = SE_ViewItemCheckIndicator, + SE_ItemViewItemCheckIndicator, + SE_ViewItemCheckIndicator = SE_ItemViewItemCheckIndicator, // ### Qt 6: remove SE_TabBarTearIndicator, SE_TabBarTearIndicatorLeft = SE_TabBarTearIndicator, diff --git a/src/widgets/styles/qstylesheetstyle.cpp b/src/widgets/styles/qstylesheetstyle.cpp index c2ffcc82b1..5c9d19a49d 100644 --- a/src/widgets/styles/qstylesheetstyle.cpp +++ b/src/widgets/styles/qstylesheetstyle.cpp @@ -44,6 +44,7 @@ #include "private/qcssutil_p.h" #include <qdebug.h> +#include <qdir.h> #include <qapplication.h> #if QT_CONFIG(menu) #include <qmenu.h> @@ -952,8 +953,12 @@ QRenderRule::QRenderRule(const QVector<Declaration> &declarations, const QObject Attachment attachment = Attachment_Scroll; origin = Origin_Padding; Origin clip = Origin_Border; - if (v.extractBackground(&brush, &uri, &repeat, &alignment, &origin, &attachment, &clip)) - bg = new QStyleSheetBackgroundData(brush, QPixmap(uri), repeat, alignment, origin, attachment, clip); + if (v.extractBackground(&brush, &uri, &repeat, &alignment, &origin, &attachment, &clip)) { + QPixmap pixmap(uri); + if (!uri.isEmpty() && pixmap.isNull()) + qWarning("Could not create pixmap from %s", qPrintable(QDir::toNativeSeparators(uri))); + bg = new QStyleSheetBackgroundData(brush, pixmap, repeat, alignment, origin, attachment, clip); + } QBrush sfg, fg; QBrush sbg, abg; @@ -5818,7 +5823,7 @@ QRect QStyleSheetStyle::subElementRect(SubElement se, const QStyleOption *opt, c return ParentStyle::subElementRect(se, opt, w); #if QT_CONFIG(itemviews) - case SE_ViewItemCheckIndicator: + case SE_ItemViewItemCheckIndicator: if (!qstyleoption_cast<const QStyleOptionViewItem *>(opt)) { return subElementRect(SE_CheckBoxIndicator, opt, w); } diff --git a/tests/auto/corelib/itemmodels/itemmodels.pro b/tests/auto/corelib/itemmodels/itemmodels.pro index bcb6e604f8..0346341be6 100644 --- a/tests/auto/corelib/itemmodels/itemmodels.pro +++ b/tests/auto/corelib/itemmodels/itemmodels.pro @@ -5,6 +5,7 @@ SUBDIRS = qabstractitemmodel \ qtHaveModule(gui): SUBDIRS += \ qabstractproxymodel \ + qconcatenatetablesproxymodel \ qidentityproxymodel \ qitemselectionmodel \ qsortfilterproxymodel_recursive \ diff --git a/tests/auto/corelib/itemmodels/qconcatenatetablesproxymodel/qconcatenatetablesproxymodel.pro b/tests/auto/corelib/itemmodels/qconcatenatetablesproxymodel/qconcatenatetablesproxymodel.pro new file mode 100644 index 0000000000..ee4ea28b5b --- /dev/null +++ b/tests/auto/corelib/itemmodels/qconcatenatetablesproxymodel/qconcatenatetablesproxymodel.pro @@ -0,0 +1,5 @@ +CONFIG += testcase +TARGET = tst_qconcatenatetablesproxymodel +QT = core gui testlib + +SOURCES = tst_qconcatenatetablesproxymodel.cpp diff --git a/tests/auto/corelib/itemmodels/qconcatenatetablesproxymodel/tst_qconcatenatetablesproxymodel.cpp b/tests/auto/corelib/itemmodels/qconcatenatetablesproxymodel/tst_qconcatenatetablesproxymodel.cpp new file mode 100644 index 0000000000..40617c1f7d --- /dev/null +++ b/tests/auto/corelib/itemmodels/qconcatenatetablesproxymodel/tst_qconcatenatetablesproxymodel.cpp @@ -0,0 +1,823 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author David Faure <david.faure@kdab.com> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QSignalSpy> +#include <QSortFilterProxyModel> +#include <QTest> +#include <QStandardItemModel> +#include <QIdentityProxyModel> +#include <QItemSelectionModel> +#include <QMimeData> +#include <QStringListModel> +#include <QAbstractItemModelTester> + +#include <qconcatenatetablesproxymodel.h> + +Q_DECLARE_METATYPE(QModelIndex) + +// Extracts a full row from a model as a string +// Works best if every cell contains only one character +static QString extractRowTexts(QAbstractItemModel *model, int row, const QModelIndex &parent = QModelIndex()) +{ + QString result; + const int colCount = model->columnCount(); + for (int col = 0; col < colCount; ++col) { + const QString txt = model->index(row, col, parent).data().toString(); + result += txt.isEmpty() ? QStringLiteral(" ") : txt; + } + return result; +} + +// Extracts a full column from a model as a string +// Works best if every cell contains only one character +static QString extractColumnTexts(QAbstractItemModel *model, int column, const QModelIndex &parent = QModelIndex()) +{ + QString result; + const int rowCount = model->rowCount(); + for (int row = 0; row < rowCount; ++row) { + const QString txt = model->index(row, column, parent).data().toString(); + result += txt.isEmpty() ? QStringLiteral(" ") : txt; + } + return result; +} + +static QString rowSpyToText(const QSignalSpy &spy) +{ + if (!spy.isValid()) + return QStringLiteral("THE SIGNALSPY IS INVALID!"); + QString str; + for (int i = 0; i < spy.count(); ++i) { + str += spy.at(i).at(1).toString() + QLatin1Char(',') + spy.at(i).at(2).toString(); + if (i + 1 < spy.count()) + str += QLatin1Char(';'); + } + return str; +} + +class tst_QConcatenateTablesProxyModel : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void init(); + void shouldAggregateTwoModelsCorrectly(); + void shouldAggregateThenRemoveTwoEmptyModelsCorrectly(); + void shouldAggregateTwoEmptyModelsWhichThenGetFilled(); + void shouldHandleDataChanged(); + void shouldHandleSetData(); + void shouldHandleSetItemData(); + void shouldHandleRowInsertionAndRemoval(); + void shouldAggregateAnotherModelThenRemoveModels(); + void shouldUseSmallestColumnCount(); + void shouldIncreaseColumnCountWhenRemovingFirstModel(); + void shouldHandleColumnInsertionAndRemoval(); + void shouldPropagateLayoutChanged(); + void shouldReactToModelReset(); + void shouldUpdateColumnsOnModelReset(); + void shouldPropagateDropOnItem_data(); + void shouldPropagateDropOnItem(); + void shouldPropagateDropBetweenItems(); + void shouldPropagateDropBetweenItemsAtModelBoundary(); + void shouldPropagateDropAfterLastRow_data(); + void shouldPropagateDropAfterLastRow(); + +private: + QStandardItemModel mod; + QStandardItemModel mod2; + QStandardItemModel mod3; +}; + +void tst_QConcatenateTablesProxyModel::init() +{ + // Prepare some source models to use later on + mod.clear(); + mod.appendRow({ new QStandardItem(QStringLiteral("A")), new QStandardItem(QStringLiteral("B")), new QStandardItem(QStringLiteral("C")) }); + mod.setHorizontalHeaderLabels(QStringList() << QStringLiteral("H1") << QStringLiteral("H2") << QStringLiteral("H3")); + mod.setVerticalHeaderLabels(QStringList() << QStringLiteral("One")); + + mod2.clear(); + mod2.appendRow({ new QStandardItem(QStringLiteral("D")), new QStandardItem(QStringLiteral("E")), new QStandardItem(QStringLiteral("F")) }); + mod2.setHorizontalHeaderLabels(QStringList() << QStringLiteral("H1") << QStringLiteral("H2") << QStringLiteral("H3")); + mod2.setVerticalHeaderLabels(QStringList() << QStringLiteral("Two")); + + mod3.clear(); + mod3.appendRow({ new QStandardItem(QStringLiteral("1")), new QStandardItem(QStringLiteral("2")), new QStandardItem(QStringLiteral("3")) }); + mod3.appendRow({ new QStandardItem(QStringLiteral("4")), new QStandardItem(QStringLiteral("5")), new QStandardItem(QStringLiteral("6")) }); +} + +void tst_QConcatenateTablesProxyModel::shouldAggregateTwoModelsCorrectly() +{ + // Given a combining proxy + QConcatenateTablesProxyModel pm; + + // When adding two source models + pm.addSourceModel(&mod); + pm.addSourceModel(&mod2); + QAbstractItemModelTester modelTest(&pm, this); + + // Then the proxy should show 2 rows + QCOMPARE(pm.rowCount(), 2); + QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC")); + QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("DEF")); + + // ... and correct headers + QCOMPARE(pm.headerData(0, Qt::Horizontal).toString(), QStringLiteral("H1")); + QCOMPARE(pm.headerData(1, Qt::Horizontal).toString(), QStringLiteral("H2")); + QCOMPARE(pm.headerData(2, Qt::Horizontal).toString(), QStringLiteral("H3")); + QCOMPARE(pm.headerData(0, Qt::Vertical).toString(), QStringLiteral("One")); + QCOMPARE(pm.headerData(1, Qt::Vertical).toString(), QStringLiteral("Two")); + + QVERIFY(!pm.canFetchMore(QModelIndex())); +} + +void tst_QConcatenateTablesProxyModel::shouldAggregateThenRemoveTwoEmptyModelsCorrectly() +{ + // Given a combining proxy + QConcatenateTablesProxyModel pm; + + // When adding two empty models + QSignalSpy rowATBISpy(&pm, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int))); + QSignalSpy rowInsertedSpy(&pm, SIGNAL(rowsInserted(QModelIndex,int,int))); + QSignalSpy rowATBRSpy(&pm, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int))); + QSignalSpy rowRemovedSpy(&pm, SIGNAL(rowsRemoved(QModelIndex,int,int))); + QIdentityProxyModel i1, i2; + pm.addSourceModel(&i1); + pm.addSourceModel(&i2); + + // Then the proxy should still be empty (and no signals emitted) + QCOMPARE(pm.rowCount(), 0); + QCOMPARE(pm.columnCount(), 0); + QCOMPARE(rowATBISpy.count(), 0); + QCOMPARE(rowInsertedSpy.count(), 0); + + // When removing the empty models + pm.removeSourceModel(&i1); + pm.removeSourceModel(&i2); + + // Then the proxy should still be empty (and no signals emitted) + QCOMPARE(pm.rowCount(), 0); + QCOMPARE(pm.columnCount(), 0); + QCOMPARE(rowATBRSpy.count(), 0); + QCOMPARE(rowRemovedSpy.count(), 0); +} + +void tst_QConcatenateTablesProxyModel::shouldAggregateTwoEmptyModelsWhichThenGetFilled() +{ + // Given a combining proxy with two empty models + QConcatenateTablesProxyModel pm; + QIdentityProxyModel i1, i2; + pm.addSourceModel(&i1); + pm.addSourceModel(&i2); + + // When filling them afterwards + i1.setSourceModel(&mod); + i2.setSourceModel(&mod2); + QAbstractItemModelTester modelTest(&pm, this); + + // Then the proxy should show 2 rows + QCOMPARE(pm.rowCount(), 2); + QCOMPARE(pm.columnCount(), 3); + QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC")); + QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("DEF")); + + // ... and correct headers + QCOMPARE(pm.headerData(0, Qt::Horizontal).toString(), QStringLiteral("H1")); + QCOMPARE(pm.headerData(1, Qt::Horizontal).toString(), QStringLiteral("H2")); + QCOMPARE(pm.headerData(2, Qt::Horizontal).toString(), QStringLiteral("H3")); + QCOMPARE(pm.headerData(0, Qt::Vertical).toString(), QStringLiteral("One")); + QCOMPARE(pm.headerData(1, Qt::Vertical).toString(), QStringLiteral("Two")); + + QVERIFY(!pm.canFetchMore(QModelIndex())); +} + +void tst_QConcatenateTablesProxyModel::shouldHandleDataChanged() +{ + // Given two models combined + QConcatenateTablesProxyModel pm; + pm.addSourceModel(&mod); + pm.addSourceModel(&mod2); + QAbstractItemModelTester modelTest(&pm, this); + QSignalSpy dataChangedSpy(&pm, SIGNAL(dataChanged(QModelIndex,QModelIndex))); + + // When a cell in a source model changes + mod.item(0, 0)->setData("a", Qt::EditRole); + + // Then the change should be notified to the proxy + QCOMPARE(dataChangedSpy.count(), 1); + QCOMPARE(dataChangedSpy.at(0).at(0).toModelIndex(), pm.index(0, 0)); + QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("aBC")); + + // Same test with the other model + mod2.item(0, 2)->setData("f", Qt::EditRole); + + QCOMPARE(dataChangedSpy.count(), 2); + QCOMPARE(dataChangedSpy.at(1).at(0).toModelIndex(), pm.index(1, 2)); + QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("DEf")); +} + +void tst_QConcatenateTablesProxyModel::shouldHandleSetData() +{ + // Given two models combined + QConcatenateTablesProxyModel pm; + pm.addSourceModel(&mod); + pm.addSourceModel(&mod2); + QAbstractItemModelTester modelTest(&pm, this); + QSignalSpy dataChangedSpy(&pm, SIGNAL(dataChanged(QModelIndex,QModelIndex))); + + // When changing a cell using setData + pm.setData(pm.index(0, 0), "a"); + + // Then the change should be notified to the proxy + QCOMPARE(dataChangedSpy.count(), 1); + QCOMPARE(dataChangedSpy.at(0).at(0).toModelIndex(), pm.index(0, 0)); + QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("aBC")); + + // Same test with the other model + pm.setData(pm.index(1, 2), "f"); + + QCOMPARE(dataChangedSpy.count(), 2); + QCOMPARE(dataChangedSpy.at(1).at(0).toModelIndex(), pm.index(1, 2)); + QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("DEf")); +} + +void tst_QConcatenateTablesProxyModel::shouldHandleSetItemData() +{ + // Given two models combined + QConcatenateTablesProxyModel pm; + pm.addSourceModel(&mod); + pm.addSourceModel(&mod2); + QAbstractItemModelTester modelTest(&pm, this); + QSignalSpy dataChangedSpy(&pm, SIGNAL(dataChanged(QModelIndex,QModelIndex))); + + // When changing a cell using setData + pm.setItemData(pm.index(0, 0), QMap<int, QVariant>{ std::make_pair<int, QVariant>(Qt::DisplayRole, QStringLiteral("X")), + std::make_pair<int, QVariant>(Qt::UserRole, 88) }); + + // Then the change should be notified to the proxy + QCOMPARE(dataChangedSpy.count(), 1); + QCOMPARE(dataChangedSpy.at(0).at(0).toModelIndex(), pm.index(0, 0)); + QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("XBC")); + QCOMPARE(pm.index(0, 0).data(Qt::UserRole).toInt(), 88); + + // Same test with the other model + pm.setItemData(pm.index(1, 2), QMap<int, QVariant>{ std::make_pair<int, QVariant>(Qt::DisplayRole, QStringLiteral("Y")), + std::make_pair<int, QVariant>(Qt::UserRole, 89) }); + + QCOMPARE(dataChangedSpy.count(), 2); + QCOMPARE(dataChangedSpy.at(1).at(0).toModelIndex(), pm.index(1, 2)); + QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("DEY")); + QCOMPARE(pm.index(1, 2).data(Qt::UserRole).toInt(), 89); +} + +void tst_QConcatenateTablesProxyModel::shouldHandleRowInsertionAndRemoval() +{ + // Given two models combined + QConcatenateTablesProxyModel pm; + pm.addSourceModel(&mod); + pm.addSourceModel(&mod2); + QAbstractItemModelTester modelTest(&pm, this); + QSignalSpy rowATBISpy(&pm, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int))); + QSignalSpy rowInsertedSpy(&pm, SIGNAL(rowsInserted(QModelIndex,int,int))); + + // When a source model inserts a new row + QList<QStandardItem *> row; + row.append(new QStandardItem(QStringLiteral("1"))); + row.append(new QStandardItem(QStringLiteral("2"))); + row.append(new QStandardItem(QStringLiteral("3"))); + mod2.insertRow(0, row); + + // Then the proxy should notify its users and show changes + QCOMPARE(rowSpyToText(rowATBISpy), QStringLiteral("1,1")); + QCOMPARE(rowSpyToText(rowInsertedSpy), QStringLiteral("1,1")); + QCOMPARE(pm.rowCount(), 3); + QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC")); + QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("123")); + QCOMPARE(extractRowTexts(&pm, 2), QStringLiteral("DEF")); + + // When removing that row + QSignalSpy rowATBRSpy(&pm, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int))); + QSignalSpy rowRemovedSpy(&pm, SIGNAL(rowsRemoved(QModelIndex,int,int))); + mod2.removeRow(0); + + // Then the proxy should notify its users and show changes + QCOMPARE(rowATBRSpy.count(), 1); + QCOMPARE(rowATBRSpy.at(0).at(1).toInt(), 1); + QCOMPARE(rowATBRSpy.at(0).at(2).toInt(), 1); + QCOMPARE(rowRemovedSpy.count(), 1); + QCOMPARE(rowRemovedSpy.at(0).at(1).toInt(), 1); + QCOMPARE(rowRemovedSpy.at(0).at(2).toInt(), 1); + QCOMPARE(pm.rowCount(), 2); + QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC")); + QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("DEF")); + + // When removing the last row from mod2 + rowATBRSpy.clear(); + rowRemovedSpy.clear(); + mod2.removeRow(0); + + // Then the proxy should notify its users and show changes + QCOMPARE(rowATBRSpy.count(), 1); + QCOMPARE(rowATBRSpy.at(0).at(1).toInt(), 1); + QCOMPARE(rowATBRSpy.at(0).at(2).toInt(), 1); + QCOMPARE(rowRemovedSpy.count(), 1); + QCOMPARE(rowRemovedSpy.at(0).at(1).toInt(), 1); + QCOMPARE(rowRemovedSpy.at(0).at(2).toInt(), 1); + QCOMPARE(pm.rowCount(), 1); + QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC")); +} + +void tst_QConcatenateTablesProxyModel::shouldAggregateAnotherModelThenRemoveModels() +{ + // Given two models combined, and a third model + QConcatenateTablesProxyModel pm; + pm.addSourceModel(&mod); + pm.addSourceModel(&mod2); + QAbstractItemModelTester modelTest(&pm, this); + + QSignalSpy rowATBISpy(&pm, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int))); + QSignalSpy rowInsertedSpy(&pm, SIGNAL(rowsInserted(QModelIndex,int,int))); + + // When adding the new source model + pm.addSourceModel(&mod3); + + // Then the proxy should notify its users about the two rows inserted + QCOMPARE(rowSpyToText(rowATBISpy), QStringLiteral("2,3")); + QCOMPARE(rowSpyToText(rowInsertedSpy), QStringLiteral("2,3")); + QCOMPARE(pm.rowCount(), 4); + QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC")); + QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("DEF")); + QCOMPARE(extractRowTexts(&pm, 2), QStringLiteral("123")); + QCOMPARE(extractRowTexts(&pm, 3), QStringLiteral("456")); + + // When removing that source model again + QSignalSpy rowATBRSpy(&pm, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int))); + QSignalSpy rowRemovedSpy(&pm, SIGNAL(rowsRemoved(QModelIndex,int,int))); + pm.removeSourceModel(&mod3); + + // Then the proxy should notify its users about the row removed + QCOMPARE(rowATBRSpy.count(), 1); + QCOMPARE(rowATBRSpy.at(0).at(1).toInt(), 2); + QCOMPARE(rowATBRSpy.at(0).at(2).toInt(), 3); + QCOMPARE(rowRemovedSpy.count(), 1); + QCOMPARE(rowRemovedSpy.at(0).at(1).toInt(), 2); + QCOMPARE(rowRemovedSpy.at(0).at(2).toInt(), 3); + QCOMPARE(pm.rowCount(), 2); + QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC")); + QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("DEF")); + + // When removing model 2 + rowATBRSpy.clear(); + rowRemovedSpy.clear(); + pm.removeSourceModel(&mod2); + QCOMPARE(rowATBRSpy.count(), 1); + QCOMPARE(rowATBRSpy.at(0).at(1).toInt(), 1); + QCOMPARE(rowATBRSpy.at(0).at(2).toInt(), 1); + QCOMPARE(rowRemovedSpy.count(), 1); + QCOMPARE(rowRemovedSpy.at(0).at(1).toInt(), 1); + QCOMPARE(rowRemovedSpy.at(0).at(2).toInt(), 1); + QCOMPARE(pm.rowCount(), 1); + QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC")); + + // When removing model 1 + rowATBRSpy.clear(); + rowRemovedSpy.clear(); + pm.removeSourceModel(&mod); + QCOMPARE(rowATBRSpy.count(), 1); + QCOMPARE(rowATBRSpy.at(0).at(1).toInt(), 0); + QCOMPARE(rowATBRSpy.at(0).at(2).toInt(), 0); + QCOMPARE(rowRemovedSpy.count(), 1); + QCOMPARE(rowRemovedSpy.at(0).at(1).toInt(), 0); + QCOMPARE(rowRemovedSpy.at(0).at(2).toInt(), 0); + QCOMPARE(pm.rowCount(), 0); +} + +void tst_QConcatenateTablesProxyModel::shouldUseSmallestColumnCount() +{ + QConcatenateTablesProxyModel pm; + pm.addSourceModel(&mod); + pm.addSourceModel(&mod2); + mod2.setColumnCount(1); + pm.addSourceModel(&mod3); + QAbstractItemModelTester modelTest(&pm, this); + + QCOMPARE(pm.rowCount(), 4); + QCOMPARE(pm.columnCount(), 1); + QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("A")); + QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("D")); + QCOMPARE(extractRowTexts(&pm, 2), QStringLiteral("1")); + QCOMPARE(extractRowTexts(&pm, 3), QStringLiteral("4")); + + const QModelIndex indexA = pm.mapFromSource(mod.index(0, 0)); + QVERIFY(indexA.isValid()); + QCOMPARE(indexA, pm.index(0, 0)); + + const QModelIndex indexB = pm.mapFromSource(mod.index(0, 1)); + QVERIFY(!indexB.isValid()); + + const QModelIndex indexD = pm.mapFromSource(mod2.index(0, 0)); + QVERIFY(indexD.isValid()); + QCOMPARE(indexD, pm.index(1, 0)); +} + +void tst_QConcatenateTablesProxyModel::shouldIncreaseColumnCountWhenRemovingFirstModel() +{ + // Given a model with 2 columns and one with 3 columns + QConcatenateTablesProxyModel pm; + pm.addSourceModel(&mod); + QAbstractItemModelTester modelTest(&pm, this); + mod.setColumnCount(2); + pm.addSourceModel(&mod2); + QCOMPARE(pm.rowCount(), 2); + QCOMPARE(pm.columnCount(), 2); + + QSignalSpy colATBISpy(&pm, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int))); + QSignalSpy colInsertedSpy(&pm, SIGNAL(columnsInserted(QModelIndex,int,int))); + QSignalSpy rowATBRSpy(&pm, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int))); + QSignalSpy rowRemovedSpy(&pm, SIGNAL(rowsRemoved(QModelIndex,int,int))); + + // When removing the first source model + pm.removeSourceModel(&mod); + + // Then the proxy should notify its users about the row removed, and the column added + QCOMPARE(pm.rowCount(), 1); + QCOMPARE(pm.columnCount(), 3); + QCOMPARE(rowSpyToText(rowATBRSpy), QStringLiteral("0,0")); + QCOMPARE(rowSpyToText(rowRemovedSpy), QStringLiteral("0,0")); + QCOMPARE(rowSpyToText(colATBISpy), QStringLiteral("2,2")); + QCOMPARE(rowSpyToText(colInsertedSpy), QStringLiteral("2,2")); + QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("DEF")); +} + +void tst_QConcatenateTablesProxyModel::shouldHandleColumnInsertionAndRemoval() +{ + // Given two models combined, one with 2 columns and one with 3 + QConcatenateTablesProxyModel pm; + pm.addSourceModel(&mod); + QAbstractItemModelTester modelTest(&pm, this); + mod.setColumnCount(2); + pm.addSourceModel(&mod2); + QSignalSpy colATBISpy(&pm, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int))); + QSignalSpy colInsertedSpy(&pm, SIGNAL(columnsInserted(QModelIndex,int,int))); + QSignalSpy colATBRSpy(&pm, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int))); + QSignalSpy colRemovedSpy(&pm, SIGNAL(columnsRemoved(QModelIndex,int,int))); + + // When the first source model inserts a new column + QCOMPARE(mod.columnCount(), 2); + mod.setColumnCount(3); + + // Then the proxy should notify its users and show changes + QCOMPARE(rowSpyToText(colATBISpy), QStringLiteral("2,2")); + QCOMPARE(rowSpyToText(colInsertedSpy), QStringLiteral("2,2")); + QCOMPARE(pm.rowCount(), 2); + QCOMPARE(pm.columnCount(), 3); + QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("AB ")); + QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("DEF")); + + // And when removing two columns + mod.setColumnCount(1); + + // Then the proxy should notify its users and show changes + QCOMPARE(rowSpyToText(colATBRSpy), QStringLiteral("1,2")); + QCOMPARE(rowSpyToText(colRemovedSpy), QStringLiteral("1,2")); + QCOMPARE(pm.rowCount(), 2); + QCOMPARE(pm.columnCount(), 1); + QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("A")); + QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("D")); +} + +void tst_QConcatenateTablesProxyModel::shouldPropagateLayoutChanged() +{ + // Given two source models, the second one being a QSFPM + QConcatenateTablesProxyModel pm; + pm.addSourceModel(&mod); + QAbstractItemModelTester modelTest(&pm, this); + + QSortFilterProxyModel qsfpm; + qsfpm.setSourceModel(&mod3); + pm.addSourceModel(&qsfpm); + + QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC")); + QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("123")); + QCOMPARE(extractRowTexts(&pm, 2), QStringLiteral("456")); + + // And a selection (row 1) + QItemSelectionModel selection(&pm); + selection.select(pm.index(1, 0), QItemSelectionModel::Select | QItemSelectionModel::Rows); + const QModelIndexList lst = selection.selectedIndexes(); + QCOMPARE(lst.count(), 3); + for (int col = 0; col < lst.count(); ++col) { + QCOMPARE(lst.at(col).row(), 1); + QCOMPARE(lst.at(col).column(), col); + } + + QSignalSpy layoutATBCSpy(&pm, SIGNAL(layoutAboutToBeChanged())); + QSignalSpy layoutChangedSpy(&pm, SIGNAL(layoutChanged())); + + // When changing the sorting in the QSFPM + qsfpm.sort(0, Qt::DescendingOrder); + + // Then the proxy should emit the layoutChanged signals, and show re-sorted data + QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC")); + QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("456")); + QCOMPARE(extractRowTexts(&pm, 2), QStringLiteral("123")); + QCOMPARE(layoutATBCSpy.count(), 1); + QCOMPARE(layoutChangedSpy.count(), 1); + + // And the selection should be updated accordingly (it became row 2) + const QModelIndexList lstAfter = selection.selectedIndexes(); + QCOMPARE(lstAfter.count(), 3); + for (int col = 0; col < lstAfter.count(); ++col) { + QCOMPARE(lstAfter.at(col).row(), 2); + QCOMPARE(lstAfter.at(col).column(), col); + } +} + +void tst_QConcatenateTablesProxyModel::shouldReactToModelReset() +{ + // Given two source models, the second one being a QSFPM + QConcatenateTablesProxyModel pm; + pm.addSourceModel(&mod); + QAbstractItemModelTester modelTest(&pm, this); + + QSortFilterProxyModel qsfpm; + qsfpm.setSourceModel(&mod3); + pm.addSourceModel(&qsfpm); + + QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC")); + QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("123")); + QCOMPARE(extractRowTexts(&pm, 2), QStringLiteral("456")); + QSignalSpy rowATBRSpy(&pm, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int))); + QSignalSpy rowRemovedSpy(&pm, SIGNAL(rowsRemoved(QModelIndex,int,int))); + QSignalSpy rowATBISpy(&pm, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int))); + QSignalSpy rowInsertedSpy(&pm, SIGNAL(rowsInserted(QModelIndex,int,int))); + QSignalSpy colATBRSpy(&pm, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int))); + QSignalSpy colRemovedSpy(&pm, SIGNAL(columnsRemoved(QModelIndex,int,int))); + QSignalSpy modelATBResetSpy(&pm, SIGNAL(modelAboutToBeReset())); + QSignalSpy modelResetSpy(&pm, SIGNAL(modelReset())); + + // When changing the source model of the QSFPM + qsfpm.setSourceModel(&mod2); + + // Then the proxy should emit the reset signals, and show the new data + QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC")); + QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("DEF")); + QCOMPARE(rowATBRSpy.count(), 0); + QCOMPARE(rowRemovedSpy.count(), 0); + QCOMPARE(rowATBISpy.count(), 0); + QCOMPARE(rowInsertedSpy.count(), 0); + QCOMPARE(colATBRSpy.count(), 0); + QCOMPARE(colRemovedSpy.count(), 0); + QCOMPARE(modelATBResetSpy.count(), 1); + QCOMPARE(modelResetSpy.count(), 1); +} + +void tst_QConcatenateTablesProxyModel::shouldUpdateColumnsOnModelReset() +{ + // Given two source models, the first one being a QSFPM + QConcatenateTablesProxyModel pm; + + QSortFilterProxyModel qsfpm; + qsfpm.setSourceModel(&mod3); + pm.addSourceModel(&qsfpm); + pm.addSourceModel(&mod); + QAbstractItemModelTester modelTest(&pm, this); + + QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("123")); + QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("456")); + QCOMPARE(extractRowTexts(&pm, 2), QStringLiteral("ABC")); + + // ... and a model with only 2 columns + QStandardItemModel mod2Columns; + mod2Columns.appendRow({ new QStandardItem(QStringLiteral("W")), new QStandardItem(QStringLiteral("X")) }); + + QSignalSpy rowATBRSpy(&pm, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int))); + QSignalSpy rowRemovedSpy(&pm, SIGNAL(rowsRemoved(QModelIndex,int,int))); + QSignalSpy rowATBISpy(&pm, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int))); + QSignalSpy rowInsertedSpy(&pm, SIGNAL(rowsInserted(QModelIndex,int,int))); + QSignalSpy colATBRSpy(&pm, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int))); + QSignalSpy colRemovedSpy(&pm, SIGNAL(columnsRemoved(QModelIndex,int,int))); + QSignalSpy modelATBResetSpy(&pm, SIGNAL(modelAboutToBeReset())); + QSignalSpy modelResetSpy(&pm, SIGNAL(modelReset())); + + // When changing the source model of the QSFPM + qsfpm.setSourceModel(&mod2Columns); + + // Then the proxy should reset, and show the new data + QCOMPARE(modelATBResetSpy.count(), 1); + QCOMPARE(modelResetSpy.count(), 1); + QCOMPARE(rowATBRSpy.count(), 0); + QCOMPARE(rowRemovedSpy.count(), 0); + QCOMPARE(rowATBISpy.count(), 0); + QCOMPARE(rowInsertedSpy.count(), 0); + QCOMPARE(colATBRSpy.count(), 0); + QCOMPARE(colRemovedSpy.count(), 0); + + QCOMPARE(pm.rowCount(), 2); + QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("WX")); + QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("AB")); +} + +void tst_QConcatenateTablesProxyModel::shouldPropagateDropOnItem_data() +{ + QTest::addColumn<int>("sourceRow"); + QTest::addColumn<int>("destRow"); + QTest::addColumn<QString>("expectedResult"); + + QTest::newRow("0-3") << 0 << 3 << QStringLiteral("ABCA"); + QTest::newRow("1-2") << 1 << 2 << QStringLiteral("ABBD"); + QTest::newRow("2-1") << 2 << 1 << QStringLiteral("ACCD"); + QTest::newRow("3-0") << 3 << 0 << QStringLiteral("DBCD"); + +} + +void tst_QConcatenateTablesProxyModel::shouldPropagateDropOnItem() +{ + // Given two source models who handle drops + + // Note: QStandardItemModel handles drop onto items by inserting child rows, + // which is good for QTreeView but not for QTableView or QConcatenateTablesProxyModel. + // So we use QStringListModel here instead. + QConcatenateTablesProxyModel pm; + QStringListModel model1({QStringLiteral("A"), QStringLiteral("B")}); + QStringListModel model2({QStringLiteral("C"), QStringLiteral("D")}); + pm.addSourceModel(&model1); + pm.addSourceModel(&model2); + QAbstractItemModelTester modelTest(&pm, this); + QCOMPARE(extractColumnTexts(&pm, 0), QStringLiteral("ABCD")); + + // When dragging one item + QFETCH(int, sourceRow); + QMimeData* mimeData = pm.mimeData({pm.index(sourceRow, 0)}); + QVERIFY(mimeData); + + // and dropping onto another item + QFETCH(int, destRow); + QVERIFY(pm.canDropMimeData(mimeData, Qt::CopyAction, -1, -1, pm.index(destRow, 0))); + QVERIFY(pm.dropMimeData(mimeData, Qt::CopyAction, -1, -1, pm.index(destRow, 0))); + delete mimeData; + + // Then the result should be as expected + QFETCH(QString, expectedResult); + QCOMPARE(extractColumnTexts(&pm, 0), expectedResult); +} + +void tst_QConcatenateTablesProxyModel::shouldPropagateDropBetweenItems() +{ + // Given two models combined + QConcatenateTablesProxyModel pm; + pm.addSourceModel(&mod3); + pm.addSourceModel(&mod2); + QAbstractItemModelTester modelTest(&pm, this); + QCOMPARE(pm.rowCount(), 3); + QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("123")); + QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("456")); + QCOMPARE(extractRowTexts(&pm, 2), QStringLiteral("DEF")); + + // When dragging the last row + QModelIndexList indexes; + indexes.reserve(pm.columnCount()); + for (int col = 0; col < pm.columnCount(); ++col) { + indexes.append(pm.index(2, col)); + } + QMimeData* mimeData = pm.mimeData(indexes); + QVERIFY(mimeData); + + // and dropping it before row 1 + const int destRow = 1; + QVERIFY(pm.canDropMimeData(mimeData, Qt::CopyAction, destRow, 0, QModelIndex())); + QVERIFY(pm.dropMimeData(mimeData, Qt::CopyAction, destRow, 0, QModelIndex())); + delete mimeData; + + // Then a new row should be inserted + QCOMPARE(pm.rowCount(), 4); + QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("123")); + QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("DEF")); + QCOMPARE(extractRowTexts(&pm, 2), QStringLiteral("456")); + QCOMPARE(extractRowTexts(&pm, 3), QStringLiteral("DEF")); +} + +void tst_QConcatenateTablesProxyModel::shouldPropagateDropBetweenItemsAtModelBoundary() +{ + // Given two models combined + QConcatenateTablesProxyModel pm; + pm.addSourceModel(&mod3); + pm.addSourceModel(&mod2); + QAbstractItemModelTester modelTest(&pm, this); + QCOMPARE(pm.rowCount(), 3); + QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("123")); + QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("456")); + QCOMPARE(extractRowTexts(&pm, 2), QStringLiteral("DEF")); + + // When dragging the first row + QModelIndexList indexes; + indexes.reserve(pm.columnCount()); + for (int col = 0; col < pm.columnCount(); ++col) { + indexes.append(pm.index(0, col)); + } + QMimeData* mimeData = pm.mimeData(indexes); + QVERIFY(mimeData); + + // and dropping it before row 2 + const int destRow = 2; + QVERIFY(pm.canDropMimeData(mimeData, Qt::CopyAction, destRow, 0, QModelIndex())); + QVERIFY(pm.dropMimeData(mimeData, Qt::CopyAction, destRow, 0, QModelIndex())); + delete mimeData; + + // Then a new row should be inserted + QCOMPARE(pm.rowCount(), 4); + QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("123")); + QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("456")); + QCOMPARE(extractRowTexts(&pm, 2), QStringLiteral("123")); + QCOMPARE(extractRowTexts(&pm, 3), QStringLiteral("DEF")); + + // and it should be part of the second model + QCOMPARE(mod2.rowCount(), 2); +} + +void tst_QConcatenateTablesProxyModel::shouldPropagateDropAfterLastRow_data() +{ + QTest::addColumn<int>("destRow"); + + // Dropping after the last row is documented to be done with destRow == -1. + QTest::newRow("-1") << -1; + // However, sometimes QTreeView calls dropMimeData with destRow == rowCount... + // Not sure if that's a bug or not, but let's support it in the model, just in case. + QTest::newRow("3") << 3; +} + +void tst_QConcatenateTablesProxyModel::shouldPropagateDropAfterLastRow() +{ + QFETCH(int, destRow); + + // Given two models combined + QConcatenateTablesProxyModel pm; + pm.addSourceModel(&mod3); + pm.addSourceModel(&mod2); + QAbstractItemModelTester modelTest(&pm, this); + QCOMPARE(pm.rowCount(), 3); + QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("123")); + QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("456")); + QCOMPARE(extractRowTexts(&pm, 2), QStringLiteral("DEF")); + + // When dragging the second row + QModelIndexList indexes; + indexes.reserve(pm.columnCount()); + for (int col = 0; col < pm.columnCount(); ++col) { + indexes.append(pm.index(1, col)); + } + QMimeData* mimeData = pm.mimeData(indexes); + QVERIFY(mimeData); + + // and dropping it after the last row + QVERIFY(pm.canDropMimeData(mimeData, Qt::CopyAction, destRow, 0, QModelIndex())); + QVERIFY(pm.dropMimeData(mimeData, Qt::CopyAction, destRow, 0, QModelIndex())); + delete mimeData; + + // Then a new row should be inserted at the end + QCOMPARE(pm.rowCount(), 4); + QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("123")); + QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("456")); + QCOMPARE(extractRowTexts(&pm, 2), QStringLiteral("DEF")); + QCOMPARE(extractRowTexts(&pm, 3), QStringLiteral("456")); + +} + +QTEST_GUILESS_MAIN(tst_QConcatenateTablesProxyModel) + +#include "tst_qconcatenatetablesproxymodel.moc" diff --git a/tests/auto/corelib/itemmodels/qstringlistmodel/tst_qstringlistmodel.cpp b/tests/auto/corelib/itemmodels/qstringlistmodel/tst_qstringlistmodel.cpp index 9a54c0a70d..16e5170a47 100644 --- a/tests/auto/corelib/itemmodels/qstringlistmodel/tst_qstringlistmodel.cpp +++ b/tests/auto/corelib/itemmodels/qstringlistmodel/tst_qstringlistmodel.cpp @@ -82,8 +82,116 @@ private slots: void setData_emits_both_roles(); void supportedDragDropActions(); + + void moveRows_data(); + void moveRows(); + void moveRowsInvalid_data(); + void moveRowsInvalid(); + + void itemData(); + void setItemData(); }; +void tst_QStringListModel::moveRowsInvalid_data() +{ + QTest::addColumn<QStringListModel*>("baseModel"); + QTest::addColumn<QModelIndex>("startParent"); + QTest::addColumn<int>("startRow"); + QTest::addColumn<int>("count"); + QTest::addColumn<QModelIndex>("destinationParent"); + QTest::addColumn<int>("destination"); + + QStringListModel* tempModel = new QStringListModel(QStringList{"A", "B", "C", "D", "E", "F"}, this); + QTest::addRow("destination_equal_source") << tempModel << QModelIndex() << 0 << 1 << QModelIndex() << 1; + tempModel = new QStringListModel(QStringList{"A", "B", "C", "D", "E", "F"}, this); + QTest::addRow("count_equal_0") << tempModel << QModelIndex() << 0 << 0 << QModelIndex() << 2; + tempModel = new QStringListModel(QStringList{"A", "B", "C", "D", "E", "F"}, this); + QTest::addRow("move_child") << tempModel << tempModel->index(0, 0) << 0 << 1 << QModelIndex() << 2; + tempModel = new QStringListModel(QStringList{"A", "B", "C", "D", "E", "F"}, this); + QTest::addRow("move_to_child") << tempModel << QModelIndex() << 0 << 1 << tempModel->index(0, 0) << 2; + tempModel = new QStringListModel(QStringList{"A", "B", "C", "D", "E", "F"}, this); + QTest::addRow("negative_count") << tempModel << QModelIndex() << 0 << -1 << QModelIndex() << 2; + tempModel = new QStringListModel(QStringList{"A", "B", "C", "D", "E", "F"}, this); + QTest::addRow("negative_source_row") << tempModel << QModelIndex() << -1 << 1 << QModelIndex() << 2; + tempModel = new QStringListModel(QStringList{"A", "B", "C", "D", "E", "F"}, this); + QTest::addRow("negative_destination_row") << tempModel << QModelIndex() << 0 << 1 << QModelIndex() << -1; + tempModel = new QStringListModel(QStringList{"A", "B", "C", "D", "E", "F"}, this); + QTest::addRow("source_row_equal_rowCount") << tempModel << QModelIndex() << tempModel->rowCount() << 1 << QModelIndex() << 1; + tempModel = new QStringListModel(QStringList{"A", "B", "C", "D", "E", "F"}, this); + QTest::addRow("destination_row_greater_rowCount") << tempModel << QModelIndex() << 0 << 1 << QModelIndex() << tempModel->rowCount() + 1; + tempModel = new QStringListModel(QStringList{"A", "B", "C", "D", "E", "F"}, this); + QTest::addRow("move_row_within_source_range") << tempModel << QModelIndex() << 0 << 3 << QModelIndex() << 2; + tempModel = new QStringListModel(QStringList{"A", "B", "C", "D", "E", "F"}, this); + QTest::addRow("destination_row_before_0") << tempModel << QModelIndex() << 1 << 1 << QModelIndex() << 0; +} + +void tst_QStringListModel::moveRowsInvalid() +{ + QFETCH(QStringListModel* const, baseModel); + QFETCH(const QModelIndex, startParent); + QFETCH(const int, startRow); + QFETCH(const int, count); + QFETCH(const QModelIndex, destinationParent); + QFETCH(const int, destination); + + QSignalSpy rowMovedSpy(baseModel, &QAbstractItemModel::rowsMoved); + QSignalSpy rowAboutMovedSpy(baseModel, &QAbstractItemModel::rowsAboutToBeMoved); + QVERIFY(rowMovedSpy.isValid()); + QVERIFY(rowAboutMovedSpy.isValid()); + QVERIFY(!baseModel->moveRows(startParent, startRow, count, destinationParent, destination)); + QCOMPARE(rowMovedSpy.size(), 0); + QCOMPARE(rowAboutMovedSpy.size(), 0); + delete baseModel; +} + +void tst_QStringListModel::moveRows_data() +{ + QTest::addColumn<int>("startRow"); + QTest::addColumn<int>("count"); + QTest::addColumn<int>("destination"); + QTest::addColumn<QStringList>("expected"); + + QTest::newRow("1_Item_from_top_to_middle") << 0 << 1 << 3 << QStringList{"B", "C", "A", "D", "E", "F"}; + QTest::newRow("1_Item_from_top_to_bottom") << 0 << 1 << 6 << QStringList{"B", "C", "D", "E", "F", "A"}; + QTest::newRow("1_Item_from_middle_to_top") << 2 << 1 << 1 << QStringList{"C", "A", "B", "D", "E", "F"}; + QTest::newRow("1_Item_from_bottom_to_middle") << 5 << 1 << 3 << QStringList{"A", "B", "F", "C", "D", "E"}; + QTest::newRow("1_Item_from_bottom to_top") << 5 << 1 << 1 << QStringList{"F", "A", "B", "C", "D", "E"}; + QTest::newRow("1_Item_from_middle_to_bottom") << 2 << 1 << 6 << QStringList{"A", "B", "D", "E", "F", "C"}; + QTest::newRow("1_Item_from_middle_to_middle_before") << 2 << 1 << 1 << QStringList{"C", "A", "B", "D", "E", "F"}; + QTest::newRow("1_Item_from_middle_to_middle_after") << 2 << 1 << 4 << QStringList{"A", "B", "D", "C", "E", "F"}; + + QTest::newRow("2_Items_from_top_to_middle") << 0 << 2 << 3 << QStringList{"C", "A", "B", "D", "E", "F"}; + QTest::newRow("2_Items_from_top_to_bottom") << 0 << 2 << 6 << QStringList{"C", "D", "E", "F", "A", "B"}; + QTest::newRow("2_Items_from_middle_to_top") << 2 << 2 << 1 << QStringList{"C", "D", "A", "B", "E", "F"}; + QTest::newRow("2_Items_from_bottom_to_middle") << 4 << 2 << 3 << QStringList{"A", "B", "E", "F", "C", "D"}; + QTest::newRow("2_Items_from_bottom_to_top") << 4 << 2 << 1 << QStringList{"E", "F", "A", "B", "C", "D"}; + QTest::newRow("2_Items_from_middle_to_bottom") << 2 << 2 << 6 << QStringList{"A", "B", "E", "F", "C", "D"}; + QTest::newRow("2_Items_from_middle_to_middle_before") << 3 << 2 << 2 << QStringList{"A", "D", "E", "B", "C", "F"}; + QTest::newRow("2_Items_from_middle_to_middle_after") << 1 << 2 << 5 << QStringList{"A", "D", "E", "B", "C", "F"}; +} + +void tst_QStringListModel::moveRows() +{ + QFETCH(const int, startRow); + QFETCH(const int, count); + QFETCH(const int, destination); + QFETCH(const QStringList, expected); + QStringListModel baseModel(QStringList{"A", "B", "C", "D", "E", "F"}); + QSignalSpy rowMovedSpy(&baseModel, &QAbstractItemModel::rowsMoved); + QSignalSpy rowAboutMovedSpy(&baseModel, &QAbstractItemModel::rowsAboutToBeMoved); + QVERIFY(baseModel.moveRows(QModelIndex(), startRow, count, QModelIndex(), destination)); + QCOMPARE(baseModel.stringList(), expected); + QCOMPARE(rowMovedSpy.size(), 1); + QCOMPARE(rowAboutMovedSpy.size(), 1); + for (const QList<QVariant> &signalArgs : {rowMovedSpy.first(), rowAboutMovedSpy.first()}){ + QVERIFY(!signalArgs.at(0).value<QModelIndex>().isValid()); + QCOMPARE(signalArgs.at(1).toInt(), startRow); + QCOMPARE(signalArgs.at(2).toInt(), startRow + count - 1); + QVERIFY(!signalArgs.at(3).value<QModelIndex>().isValid()); + QCOMPARE(signalArgs.at(4).toInt(), destination); + } +} + void tst_QStringListModel::rowsAboutToBeRemoved_rowsRemoved_data() { QTest::addColumn<QStringList>("input"); @@ -246,6 +354,74 @@ void tst_QStringListModel::setData_emits_both_roles() expected); } +void tst_QStringListModel::itemData() +{ + QStringListModel testModel{ QStringList { + QStringLiteral("One"), + QStringLiteral("Two"), + QStringLiteral("Three"), + QStringLiteral("Four"), + QStringLiteral("Five") + }}; + QMap<int, QVariant> compareMap; + QCOMPARE(testModel.itemData(QModelIndex()), compareMap); + compareMap.insert(Qt::DisplayRole, QStringLiteral("Two")); + compareMap.insert(Qt::EditRole, QStringLiteral("Two")); + QCOMPARE(testModel.itemData(testModel.index(1, 0)), compareMap); +} + +void tst_QStringListModel::setItemData() +{ + QStringListModel testModel{ QStringList { + QStringLiteral("One"), + QStringLiteral("Two"), + QStringLiteral("Three"), + QStringLiteral("Four"), + QStringLiteral("Five") + }}; + QSignalSpy dataChangedSpy(&testModel, &QAbstractItemModel::dataChanged); + QModelIndex changeIndex = testModel.index(1, 0); + const QVector<int> changeRoles{Qt::DisplayRole, Qt::EditRole}; + const QString changedString("Changed"); + QMap<int, QVariant> newItemData{std::make_pair<int>(Qt::DisplayRole, changedString)}; + // invalid index does nothing and returns false + QVERIFY(!testModel.setItemData(QModelIndex(), newItemData)); + // valid data is set, return value is true and dataChanged is emitted once + QVERIFY(testModel.setItemData(changeIndex, newItemData)); + QCOMPARE(changeIndex.data(Qt::DisplayRole).toString(), changedString); + QCOMPARE(changeIndex.data(Qt::EditRole).toString(), changedString); + QCOMPARE(dataChangedSpy.size(), 1); + QVariantList dataChangedArguments = dataChangedSpy.takeFirst(); + QCOMPARE(dataChangedArguments.at(0).value<QModelIndex>(), changeIndex); + QCOMPARE(dataChangedArguments.at(1).value<QModelIndex>(), changeIndex); + QCOMPARE(dataChangedArguments.at(2).value<QVector<int> >(), changeRoles); + // Unsupported roles do nothing return false + newItemData.clear(); + newItemData.insert(Qt::UserRole, changedString); + QVERIFY(!testModel.setItemData(changeIndex, newItemData)); + QCOMPARE(dataChangedSpy.size(), 0); + // If some but not all the roles are supported it returns false and does nothing + newItemData.insert(Qt::EditRole, changedString); + changeIndex = testModel.index(2, 0); + QVERIFY(!testModel.setItemData(changeIndex, newItemData)); + QCOMPARE(changeIndex.data(Qt::DisplayRole).toString(), QStringLiteral("Three")); + QCOMPARE(changeIndex.data(Qt::EditRole).toString(), QStringLiteral("Three")); + QCOMPARE(dataChangedSpy.size(), 0); + // Qt::EditRole and Qt::DisplayRole are both set, Qt::EditRole takes precedence + newItemData.clear(); + newItemData.insert(Qt::EditRole, changedString); + newItemData.insert(Qt::DisplayRole, QStringLiteral("Ignored")); + changeIndex = testModel.index(3, 0); + QVERIFY(testModel.setItemData(changeIndex, newItemData)); + QCOMPARE(changeIndex.data(Qt::DisplayRole).toString(), changedString); + QCOMPARE(changeIndex.data(Qt::EditRole).toString(), changedString); + QCOMPARE(dataChangedSpy.size(), 1); + dataChangedArguments = dataChangedSpy.takeFirst(); + QCOMPARE(dataChangedArguments.at(0).value<QModelIndex>(), changeIndex); + QCOMPARE(dataChangedArguments.at(1).value<QModelIndex>(), changeIndex); + QCOMPARE(dataChangedArguments.at(2).value<QVector<int> >(), changeRoles); +} + void tst_QStringListModel::supportedDragDropActions() { QStringListModel model; diff --git a/tests/auto/corelib/plugin/qpluginloader/qpluginloader.pro b/tests/auto/corelib/plugin/qpluginloader/qpluginloader.pro index 541e73636c..5efe68f4af 100644 --- a/tests/auto/corelib/plugin/qpluginloader/qpluginloader.pro +++ b/tests/auto/corelib/plugin/qpluginloader/qpluginloader.pro @@ -3,6 +3,7 @@ TEMPLATE = subdirs tst.depends = lib theplugin SUBDIRS = lib \ + staticplugin \ theplugin \ tst !android:!win32:!darwin { diff --git a/tests/auto/corelib/plugin/qpluginloader/staticplugin/.gitignore b/tests/auto/corelib/plugin/qpluginloader/staticplugin/.gitignore new file mode 100644 index 0000000000..26f7ecd506 --- /dev/null +++ b/tests/auto/corelib/plugin/qpluginloader/staticplugin/.gitignore @@ -0,0 +1,3 @@ +*staticplugin.prl +libstaticplugin.a +staticplugin.lib diff --git a/tests/auto/corelib/plugin/qpluginloader/staticplugin/main.cpp b/tests/auto/corelib/plugin/qpluginloader/staticplugin/main.cpp new file mode 100644 index 0000000000..d891839b1e --- /dev/null +++ b/tests/auto/corelib/plugin/qpluginloader/staticplugin/main.cpp @@ -0,0 +1,39 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Intel Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <QtPlugin> +#include <QObject> + +class StaticPlugin : public QObject +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "SomeIID") +public: + StaticPlugin() {} +}; + +#include "main.moc" diff --git a/tests/auto/corelib/plugin/qpluginloader/staticplugin/staticplugin.pro b/tests/auto/corelib/plugin/qpluginloader/staticplugin/staticplugin.pro new file mode 100644 index 0000000000..ff65ab728c --- /dev/null +++ b/tests/auto/corelib/plugin/qpluginloader/staticplugin/staticplugin.pro @@ -0,0 +1,7 @@ +TEMPLATE = lib +CONFIG += plugin static +SOURCES = main.cpp +QT = core + +# Add extra metadata to the plugin +QMAKE_MOC_OPTIONS += -M ExtraMetaData=StaticPlugin -M ExtraMetaData=foo diff --git a/tests/auto/corelib/plugin/qpluginloader/tst/tst.pro b/tests/auto/corelib/plugin/qpluginloader/tst/tst.pro index c20e56ba4c..a3885f4134 100644 --- a/tests/auto/corelib/plugin/qpluginloader/tst/tst.pro +++ b/tests/auto/corelib/plugin/qpluginloader/tst/tst.pro @@ -8,9 +8,14 @@ HEADERS = ../theplugin/plugininterface.h win32 { CONFIG(debug, debug|release) { TARGET = ../../debug/tst_qpluginloader + LIBS += -L../staticplugin/debug } else { TARGET = ../../release/tst_qpluginloader + LIBS += -L../staticplugin/release } +} else { + LIBS += -L../staticplugin } +LIBS += -lstaticplugin TESTDATA += ../elftest ../machtest diff --git a/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp b/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp index c517c0809a..4316ea14ea 100644 --- a/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp +++ b/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2016 Intel Corporation. +** Copyright (C) 2018 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. @@ -121,8 +121,11 @@ private slots: void reloadPlugin(); void preloadedPlugin_data(); void preloadedPlugin(); + void staticPlugins(); }; +Q_IMPORT_PLUGIN(StaticPlugin) + void tst_QPluginLoader::cleanup() { // check if the library/plugin was leaked @@ -520,5 +523,37 @@ void tst_QPluginLoader::preloadedPlugin() QVERIFY(lib.unload()); } +void tst_QPluginLoader::staticPlugins() +{ + const QObjectList instances = QPluginLoader::staticInstances(); + QVERIFY(instances.size()); + + bool found = false; + for (QObject *obj : instances) { + found = obj->metaObject()->className() == QLatin1String("StaticPlugin"); + if (found) + break; + } + QVERIFY(found); + + const auto plugins = QPluginLoader::staticPlugins(); + QCOMPARE(plugins.size(), instances.size()); + + // find the metadata + QJsonObject metaData; + for (const auto &p : plugins) { + metaData = p.metaData(); + found = metaData.value("className").toString() == QLatin1String("StaticPlugin"); + if (found) + break; + } + QVERIFY(found); + + QCOMPARE(metaData.value("version").toInt(), QT_VERSION); + QCOMPARE(metaData.value("IID").toString(), "SomeIID"); + QCOMPARE(metaData.value("ExtraMetaData"), QJsonArray({ "StaticPlugin", "foo" })); +} + + QTEST_MAIN(tst_QPluginLoader) #include "tst_qpluginloader.moc" diff --git a/tests/auto/corelib/serialization/qtextstream/tst_qtextstream.cpp b/tests/auto/corelib/serialization/qtextstream/tst_qtextstream.cpp index edea4713a1..1c1631760b 100644 --- a/tests/auto/corelib/serialization/qtextstream/tst_qtextstream.cpp +++ b/tests/auto/corelib/serialization/qtextstream/tst_qtextstream.cpp @@ -2662,28 +2662,30 @@ void tst_QTextStream::useCase2() // ------------------------------------------------------------------------------ void tst_QTextStream::manipulators_data() { - QTest::addColumn<int>("flags"); + QTest::addColumn<int>("base"); + QTest::addColumn<int>("alignFlag"); + QTest::addColumn<int>("numberFlag"); QTest::addColumn<int>("width"); QTest::addColumn<double>("realNumber"); QTest::addColumn<int>("intNumber"); QTest::addColumn<QString>("textData"); QTest::addColumn<QByteArray>("result"); - QTest::newRow("no flags") << 0 << 0 << 5.0 << 5 << QString("five") << QByteArray("55five"); - QTest::newRow("rightadjust") << 0 << 10 << 5.0 << 5 << QString("five") << QByteArray(" 5 5 five"); - - // ### FIX -// QTest::newRow("leftadjust") << int(QTextStream::left) << 10 << 5.0 << 5 << QString("five") << QByteArray("5 5 five "); -// QTest::newRow("showpos") << int(QTextStream::showpos) << 10 << 5.0 << 5 << QString("five") << QByteArray(" +5 +5 five"); -// QTest::newRow("showpos2") << int(QTextStream::showpos) << 5 << 3.14 << -5 << QString("five") << QByteArray("+3.14 -5 five"); -// QTest::newRow("hex") << int(QTextStream::hex | QTextStream::showbase) << 5 << 3.14 << -5 << QString("five") << QByteArray(" 3.14 -0x5 five"); -// QTest::newRow("hex uppercase") << int(QTextStream::hex | QTextStream::uppercase | QTextStream::showbase) << 5 << 3.14 << -5 << QString("five") << QByteArray(" 3.14 -0X5 five"); + QTest::newRow("no flags") << 10 << 0 << 0 << 0 << 5.0 << 5 << QString("five") << QByteArray("55five"); + QTest::newRow("rightadjust") << 10 << int(QTextStream::AlignRight) << 0 << 10 << 5.0 << 5 << QString("five") << QByteArray(" 5 5 five"); + QTest::newRow("leftadjust") << 10 << int(QTextStream::AlignLeft) << 0 << 10 << 5.0 << 5 << QString("five") << QByteArray("5 5 five "); + QTest::newRow("showpos") << 10 << int(QTextStream::AlignRight) << int(QTextStream::ForceSign) << 10 << 5.0 << 5 << QString("five") << QByteArray(" +5 +5 five"); + QTest::newRow("showpos2") << 10 << int(QTextStream::AlignRight) << int(QTextStream::ForceSign) << 5 << 3.14 << -5 << QString("five") << QByteArray("+3.14 -5 five"); + QTest::newRow("hex") << 16 << int(QTextStream::AlignRight) << int(QTextStream::ShowBase) << 5 << 3.14 << -5 << QString("five") << QByteArray(" 3.14 -0x5 five"); + QTest::newRow("hex") << 16 << int(QTextStream::AlignRight) << int(QTextStream::ShowBase | QTextStream::UppercaseBase) << 5 << 3.14 << -5 << QString("five") << QByteArray(" 3.14 -0X5 five"); } // ------------------------------------------------------------------------------ void tst_QTextStream::manipulators() { -// QFETCH(int, flags); + QFETCH(int, base); + QFETCH(int, alignFlag); + QFETCH(int, numberFlag); QFETCH(int, width); QFETCH(double, realNumber); QFETCH(int, intNumber); @@ -2697,14 +2699,16 @@ void tst_QTextStream::manipulators() stream.setCodec(QTextCodec::codecForName("ISO-8859-1")); stream.setAutoDetectUnicode(true); -// stream.setFlags(flags); + stream.setIntegerBase(base); + stream.setFieldAlignment(QTextStream::FieldAlignment(alignFlag)); + stream.setNumberFlags(QTextStream::NumberFlag(numberFlag)); stream.setFieldWidth(width); stream << realNumber; stream << intNumber; stream << textData; stream.flush(); - QCOMPARE(buffer.data().constData(), result.constData()); + QCOMPARE(buffer.data(), result); } void tst_QTextStream::generateBOM() diff --git a/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp b/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp index 261689d401..ff36beadcb 100644 --- a/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp +++ b/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp @@ -1869,9 +1869,11 @@ static void setWinLocaleInfo(LCTYPE type, const QString &value) # define LOCALE_SSHORTTIME 0x00000079 #endif -class RestoreLocaleHelper { +class RestoreLocaleHelper +{ public: - RestoreLocaleHelper() { + RestoreLocaleHelper() + { m_decimal = getWinLocaleInfo(LOCALE_SDECIMAL); m_thousand = getWinLocaleInfo(LOCALE_STHOUSAND); m_sdate = getWinLocaleInfo(LOCALE_SSHORTDATE); @@ -1879,7 +1881,8 @@ public: m_time = getWinLocaleInfo(LOCALE_SSHORTTIME); } - ~RestoreLocaleHelper() { + ~RestoreLocaleHelper() + { // restore these, or the user will get a surprise setWinLocaleInfo(LOCALE_SDECIMAL, m_decimal); setWinLocaleInfo(LOCALE_STHOUSAND, m_thousand); @@ -1887,12 +1890,10 @@ public: setWinLocaleInfo(LOCALE_SLONGDATE, m_ldate); setWinLocaleInfo(LOCALE_SSHORTTIME, m_time); - // make sure QLocale::system() gets updated - QLocalePrivate::updateSystemPrivate(); + QSystemLocale dummy; // to provoke a refresh of the system locale } QString m_decimal, m_thousand, m_sdate, m_ldate, m_time; - }; void tst_QLocale::windowsDefaultLocale() @@ -1908,8 +1909,7 @@ void tst_QLocale::windowsDefaultLocale() const QString shortTimeFormat = QStringLiteral("h^m^s"); setWinLocaleInfo(LOCALE_SSHORTTIME, shortTimeFormat); - // make sure QLocale::system() gets updated - QLocalePrivate::updateSystemPrivate(); + QSystemLocale dummy; // to provoke a refresh of the system locale QLocale locale = QLocale::system(); // make sure we are seeing the system's format strings @@ -2650,9 +2650,11 @@ void tst_QLocale::textDirection_data() case QLocale::Sabaean: case QLocale::Samaritan: case QLocale::Sindhi: + case QLocale::SouthernKurdish: case QLocale::Syriac: case QLocale::Uighur: case QLocale::Urdu: + case QLocale::WesternBalochi: case QLocale::Yiddish: // false if there is no locale data for language: rightToLeft = (QLocale(QLocale::Language(language)).language() diff --git a/tests/auto/corelib/tools/qoffsetstringarray/qoffsetstringarray.pro b/tests/auto/corelib/tools/qoffsetstringarray/qoffsetstringarray.pro new file mode 100644 index 0000000000..c8e6a8e05a --- /dev/null +++ b/tests/auto/corelib/tools/qoffsetstringarray/qoffsetstringarray.pro @@ -0,0 +1,6 @@ +CONFIG += testcase +TARGET = tst_qoffsetstringarray +QT = core testlib core-private +CONFIG += c++11 +CONFIG += strict_c++ +SOURCES = $$PWD/tst_qoffsetstringarray.cpp diff --git a/tests/auto/corelib/tools/qoffsetstringarray/tst_qoffsetstringarray.cpp b/tests/auto/corelib/tools/qoffsetstringarray/tst_qoffsetstringarray.cpp new file mode 100644 index 0000000000..dfa0450b18 --- /dev/null +++ b/tests/auto/corelib/tools/qoffsetstringarray/tst_qoffsetstringarray.cpp @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> + +#include <private/qoffsetstringarray_p.h> + + +class tst_QOffsetStringArray : public QObject +{ + Q_OBJECT + +private slots: + void init(); + void access(); +}; + + +constexpr const auto messages = qOffsetStringArray( + "level - 0", + "level - 1", + "level - 2", + "level - 3", + "level - 4", + "" +); + +constexpr const auto messages257 = qOffsetStringArray( + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "end" +); + +constexpr const auto messagesBigOffsets = qOffsetStringArray( + " 10 20 30 40 50 60 70 80 90", + " 10 20 30 40 50 60 70 80 90", + " 10 20 30 40 50 60 70 80 90", + " 10 20 30 40 50 60 70 80 90" +); + +void tst_QOffsetStringArray::init() +{ + static_assert(messages.sizeString == 51, "message.sizeString"); + static_assert(messages.sizeOffsets == 6, "message.sizeOffsets"); + static_assert(std::is_same<decltype(messages)::Type, quint8>::value, "messages::Type != quint8"); + + static_assert(messages257.sizeOffsets == 257, "messages257.sizeOffsets"); + static_assert(messages257.sizeString == 260, "messages257.sizeString"); + static_assert(std::is_same<decltype(messages257)::Type, quint16>::value, + "messages257::Type != quint16"); + + static_assert(messagesBigOffsets.sizeOffsets == 4, "messagesBigOffsets.sizeOffsets"); + static_assert(messagesBigOffsets.sizeString == 364, "messagesBigOffsets.sizeString"); + static_assert(std::is_same<decltype(messagesBigOffsets)::Type, quint16>::value, + "messagesBigOffsets::Type != quint16"); +} + +void tst_QOffsetStringArray::access() +{ + QCOMPARE(messages[0], "level - 0"); + QCOMPARE(messages[1], "level - 1"); + QCOMPARE(messages[2], "level - 2"); + QCOMPARE(messages[3], "level - 3"); + QCOMPARE(messages[4], "level - 4"); + QCOMPARE(messages[5], ""); + QCOMPARE(messages[6], ""); +} + + +QTEST_APPLESS_MAIN(tst_QOffsetStringArray) +#include "tst_qoffsetstringarray.moc" diff --git a/tests/auto/corelib/tools/tools.pro b/tests/auto/corelib/tools/tools.pro index f28cf21b8b..2a975e67d1 100644 --- a/tests/auto/corelib/tools/tools.pro +++ b/tests/auto/corelib/tools/tools.pro @@ -35,6 +35,7 @@ SUBDIRS=\ qmap_strictiterators \ qmargins \ qmessageauthenticationcode \ + qoffsetstringarray \ qpair \ qpoint \ qpointf \ diff --git a/tests/auto/network/access/qnetworkcookiejar/tst_qnetworkcookiejar.cpp b/tests/auto/network/access/qnetworkcookiejar/tst_qnetworkcookiejar.cpp index 8b49679042..6c3443a735 100644 --- a/tests/auto/network/access/qnetworkcookiejar/tst_qnetworkcookiejar.cpp +++ b/tests/auto/network/access/qnetworkcookiejar/tst_qnetworkcookiejar.cpp @@ -35,7 +35,9 @@ #include <QtNetwork/QNetworkCookieJar> #include <QtNetwork/QNetworkCookie> #include <QtNetwork/QNetworkRequest> +#if QT_CONFIG(topleveldomain) #include "private/qtldurl_p.h" +#endif class tst_QNetworkCookieJar: public QObject { @@ -47,7 +49,7 @@ private slots: void setCookiesFromUrl(); void cookiesForUrl_data(); void cookiesForUrl(); -#ifdef QT_BUILD_INTERNAL +#if defined(QT_BUILD_INTERNAL) && QT_CONFIG(topleveldomain) void effectiveTLDs_data(); void effectiveTLDs(); #endif @@ -398,7 +400,7 @@ void tst_QNetworkCookieJar::cookiesForUrl() } // This test requires private API. -#ifdef QT_BUILD_INTERNAL +#if defined(QT_BUILD_INTERNAL) && QT_CONFIG(topleveldomain) void tst_QNetworkCookieJar::effectiveTLDs_data() { QTest::addColumn<QString>("domain"); diff --git a/tests/auto/tools/rcc/data/sizes/data/data-0.txt b/tests/auto/tools/rcc/data/sizes/data/data-0.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/auto/tools/rcc/data/sizes/data/data-0.txt diff --git a/tests/auto/tools/rcc/data/sizes/data/data-1.txt b/tests/auto/tools/rcc/data/sizes/data/data-1.txt new file mode 100644 index 0000000000..b516b2c489 --- /dev/null +++ b/tests/auto/tools/rcc/data/sizes/data/data-1.txt @@ -0,0 +1 @@ +@
\ No newline at end of file diff --git a/tests/auto/tools/rcc/data/sizes/data/data-2.txt b/tests/auto/tools/rcc/data/sizes/data/data-2.txt new file mode 100644 index 0000000000..a616ad491b --- /dev/null +++ b/tests/auto/tools/rcc/data/sizes/data/data-2.txt @@ -0,0 +1 @@ +01
\ No newline at end of file diff --git a/tests/auto/tools/rcc/data/sizes/data/data-35.txt b/tests/auto/tools/rcc/data/sizes/data/data-35.txt new file mode 100644 index 0000000000..19a8036a15 --- /dev/null +++ b/tests/auto/tools/rcc/data/sizes/data/data-35.txt @@ -0,0 +1 @@ +0123456789 0123456789 0123456789 12
\ No newline at end of file diff --git a/tests/auto/tools/rcc/data/sizes/size-0.expected b/tests/auto/tools/rcc/data/sizes/size-0.expected new file mode 100644 index 0000000000..e862310153 --- /dev/null +++ b/tests/auto/tools/rcc/data/sizes/size-0.expected @@ -0,0 +1,88 @@ +/**************************************************************************** +** Resource object code +** +IGNORE: ** Created by: The Resource Compiler for Qt version 5.11.2 +** +** WARNING! All changes made in this file will be lost! +*****************************************************************************/ + +static const unsigned char qt_resource_data[] = { +IGNORE: // /data/dev/qt-5/qtbase/tests/auto/tools/rcc/data/sizes/data/data-0.txt + 0x0,0x0,0x0,0x0, + + +}; + +static const unsigned char qt_resource_name[] = { + // data + 0x0,0x4, + 0x0,0x6,0xa8,0xa1, + 0x0,0x64, + 0x0,0x61,0x0,0x74,0x0,0x61, + // data-0.txt + 0x0,0xa, + 0x4,0xe,0xa,0xb4, + 0x0,0x64, + 0x0,0x61,0x0,0x74,0x0,0x61,0x0,0x2d,0x0,0x30,0x0,0x2e,0x0,0x74,0x0,0x78,0x0,0x74, + +}; + +static const unsigned char qt_resource_struct[] = { + // : + 0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1, +0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + // :/data + 0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2, +0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + // :/data/data-0.txt + 0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0, +TIMESTAMP:data/data-0.txt + +}; + +#ifdef QT_NAMESPACE +# define QT_RCC_PREPEND_NAMESPACE(name) ::QT_NAMESPACE::name +# define QT_RCC_MANGLE_NAMESPACE0(x) x +# define QT_RCC_MANGLE_NAMESPACE1(a, b) a##_##b +# define QT_RCC_MANGLE_NAMESPACE2(a, b) QT_RCC_MANGLE_NAMESPACE1(a,b) +# define QT_RCC_MANGLE_NAMESPACE(name) QT_RCC_MANGLE_NAMESPACE2( \ + QT_RCC_MANGLE_NAMESPACE0(name), QT_RCC_MANGLE_NAMESPACE0(QT_NAMESPACE)) +#else +# define QT_RCC_PREPEND_NAMESPACE(name) name +# define QT_RCC_MANGLE_NAMESPACE(name) name +#endif + +#ifdef QT_NAMESPACE +namespace QT_NAMESPACE { +#endif + +bool qRegisterResourceData(int, const unsigned char *, const unsigned char *, const unsigned char *); + +bool qUnregisterResourceData(int, const unsigned char *, const unsigned char *, const unsigned char *); + +#ifdef QT_NAMESPACE +} +#endif + +int QT_RCC_MANGLE_NAMESPACE(qInitResources)(); +int QT_RCC_MANGLE_NAMESPACE(qInitResources)() +{ + QT_RCC_PREPEND_NAMESPACE(qRegisterResourceData) + (0x2, qt_resource_struct, qt_resource_name, qt_resource_data); + return 1; +} + +int QT_RCC_MANGLE_NAMESPACE(qCleanupResources)(); +int QT_RCC_MANGLE_NAMESPACE(qCleanupResources)() +{ + QT_RCC_PREPEND_NAMESPACE(qUnregisterResourceData) + (0x2, qt_resource_struct, qt_resource_name, qt_resource_data); + return 1; +} + +namespace { + struct initializer { + initializer() { QT_RCC_MANGLE_NAMESPACE(qInitResources)(); } + ~initializer() { QT_RCC_MANGLE_NAMESPACE(qCleanupResources)(); } + } dummy; +} diff --git a/tests/auto/tools/rcc/data/sizes/size-0.qrc b/tests/auto/tools/rcc/data/sizes/size-0.qrc new file mode 100644 index 0000000000..9f47732fe2 --- /dev/null +++ b/tests/auto/tools/rcc/data/sizes/size-0.qrc @@ -0,0 +1,5 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>data/data-0.txt</file> +</qresource> +</RCC> diff --git a/tests/auto/tools/rcc/data/sizes/size-1.expected b/tests/auto/tools/rcc/data/sizes/size-1.expected new file mode 100644 index 0000000000..9e5161272a --- /dev/null +++ b/tests/auto/tools/rcc/data/sizes/size-1.expected @@ -0,0 +1,89 @@ +/**************************************************************************** +** Resource object code +** +IGNORE:** Created by: The Resource Compiler for Qt version 5.11.2 +** +** WARNING! All changes made in this file will be lost! +*****************************************************************************/ + +static const unsigned char qt_resource_data[] = { +IGNORE: // /data/dev/qt-5/qtbase/tests/auto/tools/rcc/data/sizes/data/data-1.txt + 0x0,0x0,0x0,0x1, + 0x40, + + +}; + +static const unsigned char qt_resource_name[] = { + // data + 0x0,0x4, + 0x0,0x6,0xa8,0xa1, + 0x0,0x64, + 0x0,0x61,0x0,0x74,0x0,0x61, + // data-1.txt + 0x0,0xa, + 0x4,0x11,0xa,0xb4, + 0x0,0x64, + 0x0,0x61,0x0,0x74,0x0,0x61,0x0,0x2d,0x0,0x31,0x0,0x2e,0x0,0x74,0x0,0x78,0x0,0x74, + +}; + +static const unsigned char qt_resource_struct[] = { + // : + 0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1, +0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + // :/data + 0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2, +0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + // :/data/data-1.txt + 0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0, +TIMESTAMP:data/data-1.txt + +}; + +#ifdef QT_NAMESPACE +# define QT_RCC_PREPEND_NAMESPACE(name) ::QT_NAMESPACE::name +# define QT_RCC_MANGLE_NAMESPACE0(x) x +# define QT_RCC_MANGLE_NAMESPACE1(a, b) a##_##b +# define QT_RCC_MANGLE_NAMESPACE2(a, b) QT_RCC_MANGLE_NAMESPACE1(a,b) +# define QT_RCC_MANGLE_NAMESPACE(name) QT_RCC_MANGLE_NAMESPACE2( \ + QT_RCC_MANGLE_NAMESPACE0(name), QT_RCC_MANGLE_NAMESPACE0(QT_NAMESPACE)) +#else +# define QT_RCC_PREPEND_NAMESPACE(name) name +# define QT_RCC_MANGLE_NAMESPACE(name) name +#endif + +#ifdef QT_NAMESPACE +namespace QT_NAMESPACE { +#endif + +bool qRegisterResourceData(int, const unsigned char *, const unsigned char *, const unsigned char *); + +bool qUnregisterResourceData(int, const unsigned char *, const unsigned char *, const unsigned char *); + +#ifdef QT_NAMESPACE +} +#endif + +int QT_RCC_MANGLE_NAMESPACE(qInitResources)(); +int QT_RCC_MANGLE_NAMESPACE(qInitResources)() +{ + QT_RCC_PREPEND_NAMESPACE(qRegisterResourceData) + (0x2, qt_resource_struct, qt_resource_name, qt_resource_data); + return 1; +} + +int QT_RCC_MANGLE_NAMESPACE(qCleanupResources)(); +int QT_RCC_MANGLE_NAMESPACE(qCleanupResources)() +{ + QT_RCC_PREPEND_NAMESPACE(qUnregisterResourceData) + (0x2, qt_resource_struct, qt_resource_name, qt_resource_data); + return 1; +} + +namespace { + struct initializer { + initializer() { QT_RCC_MANGLE_NAMESPACE(qInitResources)(); } + ~initializer() { QT_RCC_MANGLE_NAMESPACE(qCleanupResources)(); } + } dummy; +} diff --git a/tests/auto/tools/rcc/data/sizes/size-1.qrc b/tests/auto/tools/rcc/data/sizes/size-1.qrc new file mode 100644 index 0000000000..9fde9a1722 --- /dev/null +++ b/tests/auto/tools/rcc/data/sizes/size-1.qrc @@ -0,0 +1,5 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>data/data-1.txt</file> +</qresource> +</RCC> diff --git a/tests/auto/tools/rcc/data/sizes/size-2-0-35-1.expected b/tests/auto/tools/rcc/data/sizes/size-2-0-35-1.expected new file mode 100644 index 0000000000..0fc953351a --- /dev/null +++ b/tests/auto/tools/rcc/data/sizes/size-2-0-35-1.expected @@ -0,0 +1,126 @@ +/**************************************************************************** +** Resource object code +** +IGNORE: ** Created by: The Resource Compiler for Qt version 5.11.2 +** +** WARNING! All changes made in this file will be lost! +*****************************************************************************/ + +static const unsigned char qt_resource_data[] = { +IGNORE: // /data/dev/qt-5/qtbase/tests/auto/tools/rcc/data/sizes/data/data-2.txt + 0x0,0x0,0x0,0x2, + 0x30, + 0x31, +IGNORE: // /data/dev/qt-5/qtbase/tests/auto/tools/rcc/data/sizes/data/data-35.txt + 0x0,0x0,0x0,0x23, + 0x30, + 0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x20,0x30,0x31,0x32,0x33,0x34,0x35, + 0x36,0x37,0x38,0x39,0x20,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x20, + 0x31,0x32, +IGNORE: // /data/dev/qt-5/qtbase/tests/auto/tools/rcc/data/sizes/data/data-1.txt + 0x0,0x0,0x0,0x1, + 0x40, + +IGNORE: // /data/dev/qt-5/qtbase/tests/auto/tools/rcc/data/sizes/data/data-0.txt + 0x0,0x0,0x0,0x0, + + +}; + +static const unsigned char qt_resource_name[] = { + // data + 0x0,0x4, + 0x0,0x6,0xa8,0xa1, + 0x0,0x64, + 0x0,0x61,0x0,0x74,0x0,0x61, + // data-2.txt + 0x0,0xa, + 0x4,0x8,0xa,0xb4, + 0x0,0x64, + 0x0,0x61,0x0,0x74,0x0,0x61,0x0,0x2d,0x0,0x32,0x0,0x2e,0x0,0x74,0x0,0x78,0x0,0x74, + // data-35.txt + 0x0,0xb, + 0x0,0xb5,0x4f,0x74, + 0x0,0x64, + 0x0,0x61,0x0,0x74,0x0,0x61,0x0,0x2d,0x0,0x33,0x0,0x35,0x0,0x2e,0x0,0x74,0x0,0x78,0x0,0x74, + // data-1.txt + 0x0,0xa, + 0x4,0x11,0xa,0xb4, + 0x0,0x64, + 0x0,0x61,0x0,0x74,0x0,0x61,0x0,0x2d,0x0,0x31,0x0,0x2e,0x0,0x74,0x0,0x78,0x0,0x74, + // data-0.txt + 0x0,0xa, + 0x4,0xe,0xa,0xb4, + 0x0,0x64, + 0x0,0x61,0x0,0x74,0x0,0x61,0x0,0x2d,0x0,0x30,0x0,0x2e,0x0,0x74,0x0,0x78,0x0,0x74, + +}; + +static const unsigned char qt_resource_struct[] = { + // : + 0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1, +0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + // :/data + 0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x2, +0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + // :/data/data-35.txt + 0x0,0x0,0x0,0x28,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6, +TIMESTAMP:data/data-35.txt + // :/data/data-2.txt + 0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0, +TIMESTAMP:data/data-2.txt + // :/data/data-0.txt + 0x0,0x0,0x0,0x5e,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x32, +TIMESTAMP:data/data-0.txt + // :/data/data-1.txt + 0x0,0x0,0x0,0x44,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2d, +TIMESTAMP:data/data-1.txt + +}; + +#ifdef QT_NAMESPACE +# define QT_RCC_PREPEND_NAMESPACE(name) ::QT_NAMESPACE::name +# define QT_RCC_MANGLE_NAMESPACE0(x) x +# define QT_RCC_MANGLE_NAMESPACE1(a, b) a##_##b +# define QT_RCC_MANGLE_NAMESPACE2(a, b) QT_RCC_MANGLE_NAMESPACE1(a,b) +# define QT_RCC_MANGLE_NAMESPACE(name) QT_RCC_MANGLE_NAMESPACE2( \ + QT_RCC_MANGLE_NAMESPACE0(name), QT_RCC_MANGLE_NAMESPACE0(QT_NAMESPACE)) +#else +# define QT_RCC_PREPEND_NAMESPACE(name) name +# define QT_RCC_MANGLE_NAMESPACE(name) name +#endif + +#ifdef QT_NAMESPACE +namespace QT_NAMESPACE { +#endif + +bool qRegisterResourceData(int, const unsigned char *, const unsigned char *, const unsigned char *); + +bool qUnregisterResourceData(int, const unsigned char *, const unsigned char *, const unsigned char *); + +#ifdef QT_NAMESPACE +} +#endif + +int QT_RCC_MANGLE_NAMESPACE(qInitResources)(); +int QT_RCC_MANGLE_NAMESPACE(qInitResources)() +{ + QT_RCC_PREPEND_NAMESPACE(qRegisterResourceData) + (0x2, qt_resource_struct, qt_resource_name, qt_resource_data); + return 1; +} + +int QT_RCC_MANGLE_NAMESPACE(qCleanupResources)(); +int QT_RCC_MANGLE_NAMESPACE(qCleanupResources)() +{ + QT_RCC_PREPEND_NAMESPACE(qUnregisterResourceData) + (0x2, qt_resource_struct, qt_resource_name, qt_resource_data); + return 1; +} + +namespace { + struct initializer { + initializer() { QT_RCC_MANGLE_NAMESPACE(qInitResources)(); } + ~initializer() { QT_RCC_MANGLE_NAMESPACE(qCleanupResources)(); } + } dummy; +} diff --git a/tests/auto/tools/rcc/data/sizes/size-2-0-35-1.qrc b/tests/auto/tools/rcc/data/sizes/size-2-0-35-1.qrc new file mode 100644 index 0000000000..039c9203ff --- /dev/null +++ b/tests/auto/tools/rcc/data/sizes/size-2-0-35-1.qrc @@ -0,0 +1,8 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>data/data-2.txt</file> + <file>data/data-0.txt</file> + <file>data/data-35.txt</file> + <file>data/data-1.txt</file> +</qresource> +</RCC> diff --git a/tests/auto/tools/rcc/rcc.pro b/tests/auto/tools/rcc/rcc.pro index 264b8ecc66..3fdc4132a7 100644 --- a/tests/auto/tools/rcc/rcc.pro +++ b/tests/auto/tools/rcc/rcc.pro @@ -3,3 +3,10 @@ QT = core testlib TARGET = tst_rcc SOURCES += tst_rcc.cpp + +RESOURCES += \ + $$PWD/data/images/images.qrc \ + $$PWD/data/sizes/size-0.qrc \ + $$PWD/data/sizes/size-2-0-35-1.qrc \ + $$PWD/data/sizes/size-1.qrc + diff --git a/tests/auto/tools/rcc/tst_rcc.cpp b/tests/auto/tools/rcc/tst_rcc.cpp index 54a2854ede..0876af6759 100644 --- a/tests/auto/tools/rcc/tst_rcc.cpp +++ b/tests/auto/tools/rcc/tst_rcc.cpp @@ -55,9 +55,13 @@ private slots: void rcc_data(); void rcc(); + void binary_data(); void binary(); + void readback_data(); + void readback(); + void cleanupTestCase(); private: @@ -126,6 +130,13 @@ void tst_rcc::rcc_data() if (dataPath.isEmpty()) QFAIL("data path not found"); QTest::newRow("images") << dataPath << "images.qrc" << "images.expected"; + + QString sizesPath = QFINDTESTDATA("data/sizes/"); + if (sizesPath.isEmpty()) + QFAIL("data path not found"); + QTest::newRow("size-0") << sizesPath << "size-0.qrc" << "size-0.expected"; + QTest::newRow("size-1") << sizesPath << "size-1.qrc" << "size-1.expected"; + QTest::newRow("size-2-0-35-1") << sizesPath << "size-2-0-35-1.qrc" << "size-2-0-35-1.expected"; } void tst_rcc::rcc() @@ -353,6 +364,42 @@ void tst_rcc::binary() QLocale::setDefault(oldDefaultLocale); } +void tst_rcc::readback_data() +{ + QTest::addColumn<QString>("resourceName"); + QTest::addColumn<QString>("fileSystemName"); + + QTest::newRow("data-0") << ":data/data-0.txt" << "sizes/data/data-0.txt"; + QTest::newRow("data-1") << ":data/data-1.txt" << "sizes/data/data-1.txt"; + QTest::newRow("data-2") << ":data/data-2.txt" << "sizes/data/data-2.txt"; + QTest::newRow("data-35") << ":data/data-35.txt" << "sizes/data/data-35.txt"; + QTest::newRow("circle") << ":images/circle.png" << "images/images/circle.png"; + QTest::newRow("square") << ":images/square.png" << "images/images/square.png"; + QTest::newRow("triangle") << ":images/subdir/triangle.png" + << "images/images/subdir/triangle.png"; +} + +void tst_rcc::readback() +{ + QFETCH(QString, resourceName); + QFETCH(QString, fileSystemName); + + QString dataPath = QFINDTESTDATA("data/"); + if (dataPath.isEmpty()) + QFAIL("data path not found"); + + QFile resourceFile(resourceName); + QVERIFY(resourceFile.open(QIODevice::ReadOnly)); + QByteArray resourceData = resourceFile.readAll(); + resourceFile.close(); + + QFile fileSystemFile(dataPath + fileSystemName); + QVERIFY(fileSystemFile.open(QIODevice::ReadOnly)); + QByteArray fileSystemData = fileSystemFile.readAll(); + fileSystemFile.close(); + + QCOMPARE(resourceData, fileSystemData); +} void tst_rcc::cleanupTestCase() { diff --git a/tests/auto/widgets/itemviews/qitemdelegate/tst_qitemdelegate.cpp b/tests/auto/widgets/itemviews/qitemdelegate/tst_qitemdelegate.cpp index a8f6906056..adedace8b2 100644 --- a/tests/auto/widgets/itemviews/qitemdelegate/tst_qitemdelegate.cpp +++ b/tests/auto/widgets/itemviews/qitemdelegate/tst_qitemdelegate.cpp @@ -1215,7 +1215,7 @@ void tst_QItemDelegate::editorEvent() option.checkState = Qt::CheckState(checkState); const int checkMargin = qApp->style()->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, 0) + 1; - QPoint pos = inCheck ? qApp->style()->subElementRect(QStyle::SE_ViewItemCheckIndicator, &option, 0).center() + QPoint(checkMargin, 0) : QPoint(200,200); + QPoint pos = inCheck ? qApp->style()->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &option, 0).center() + QPoint(checkMargin, 0) : QPoint(200,200); QEvent *event = new QMouseEvent((QEvent::Type)type, pos, @@ -1371,7 +1371,7 @@ void tst_QItemDelegate::QTBUG4435_keepSelectionOnCheck() option.features = QStyleOptionViewItem::HasDisplay | QStyleOptionViewItem::HasCheckIndicator; option.checkState = Qt::CheckState(model.index(0, 0).data(Qt::CheckStateRole).toInt()); const int checkMargin = qApp->style()->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, 0) + 1; - QPoint pos = qApp->style()->subElementRect(QStyle::SE_ViewItemCheckIndicator, &option, 0).center() + QPoint pos = qApp->style()->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &option, 0).center() + QPoint(checkMargin, 0); QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ControlModifier, pos); QTRY_VERIFY(view.selectionModel()->isColumnSelected(0, QModelIndex())); diff --git a/tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp b/tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp index 5227db64ec..433b9e55e1 100644 --- a/tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp +++ b/tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp @@ -873,6 +873,10 @@ void tst_QListView::setCurrentIndex() } } } + while (model.rowCount()) { + view.setCurrentIndex(model.index(model.rowCount() - 1, 0)); + model.removeRow(model.rowCount() - 1); + } } class PublicListView : public QListView diff --git a/tests/auto/widgets/itemviews/qlistwidget/tst_qlistwidget.cpp b/tests/auto/widgets/itemviews/qlistwidget/tst_qlistwidget.cpp index 98b44fe8aa..d8e14df353 100644 --- a/tests/auto/widgets/itemviews/qlistwidget/tst_qlistwidget.cpp +++ b/tests/auto/widgets/itemviews/qlistwidget/tst_qlistwidget.cpp @@ -31,6 +31,7 @@ #include <QtGui/QtGui> #include <QtWidgets/QtWidgets> #include <qlist.h> +#include <QSignalSpy> #include <qlistwidget.h> #include <private/qlistwidget_p.h> @@ -116,6 +117,14 @@ private slots: void QTBUG14363_completerWithAnyKeyPressedEditTriggers(); void mimeData(); void QTBUG50891_ensureSelectionModelSignalConnectionsAreSet(); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + void clearItemData(); +#endif + + void moveRows_data(); + void moveRows(); + void moveRowsInvalid_data(); + void moveRowsInvalid(); protected slots: void rowsAboutToBeInserted(const QModelIndex &parent, int first, int last) @@ -148,6 +157,108 @@ private: }; +void tst_QListWidget::moveRowsInvalid_data() +{ + QTest::addColumn<QListWidget*>("baseWidget"); + QTest::addColumn<QModelIndex>("startParent"); + QTest::addColumn<int>("startRow"); + QTest::addColumn<int>("count"); + QTest::addColumn<QModelIndex>("destinationParent"); + QTest::addColumn<int>("destination"); + + const auto createWidget = []() -> QListWidget* { + QListWidget* result = new QListWidget; + result->addItems(QStringList{"A", "B", "C", "D", "E", "F"}); + return result; + }; + + QTest::addRow("destination_equal_source") << createWidget() << QModelIndex() << 0 << 1 << QModelIndex() << 1; + QTest::addRow("count_equal_0") << createWidget() << QModelIndex() << 0 << 0 << QModelIndex() << 2; + QListWidget* tempWidget = createWidget(); + QTest::addRow("move_child") << tempWidget << tempWidget->model()->index(0, 0) << 0 << 1 << QModelIndex() << 2; + tempWidget = createWidget(); + QTest::addRow("move_to_child") << tempWidget << QModelIndex() << 0 << 1 << tempWidget->model()->index(0, 0) << 2; + QTest::addRow("negative_count") << createWidget() << QModelIndex() << 0 << -1 << QModelIndex() << 2; + QTest::addRow("negative_source_row") << createWidget() << QModelIndex() << -1 << 1 << QModelIndex() << 2; + QTest::addRow("negative_destination_row") << createWidget() << QModelIndex() << 0 << 1 << QModelIndex() << -1; + QTest::addRow("source_row_equal_rowCount") << createWidget() << QModelIndex() << 6 << 1 << QModelIndex() << 1; + QTest::addRow("destination_row_greater_rowCount") << createWidget() << QModelIndex() << 0 << 1 << QModelIndex() << 6 + 1; + QTest::addRow("move_row_within_source_range") << createWidget() << QModelIndex() << 0 << 3 << QModelIndex() << 2; + QTest::addRow("destination_row_before_0") << createWidget() << QModelIndex() << 1 << 1 << QModelIndex() << 0; +} + +void tst_QListWidget::moveRowsInvalid() +{ + QFETCH(QListWidget* const, baseWidget); + QFETCH(const QModelIndex, startParent); + QFETCH(const int, startRow); + QFETCH(const int, count); + QFETCH(const QModelIndex, destinationParent); + QFETCH(const int, destination); + QAbstractItemModel *baseModel = baseWidget->model(); + QSignalSpy rowMovedSpy(baseModel, &QAbstractItemModel::rowsMoved); + QSignalSpy rowAboutMovedSpy(baseModel, &QAbstractItemModel::rowsAboutToBeMoved); + QVERIFY(rowMovedSpy.isValid()); + QVERIFY(rowAboutMovedSpy.isValid()); + QVERIFY(!baseModel->moveRows(startParent, startRow, count, destinationParent, destination)); + QCOMPARE(rowMovedSpy.size(), 0); + QCOMPARE(rowAboutMovedSpy.size(), 0); + delete baseWidget; +} + +void tst_QListWidget::moveRows_data() +{ + QTest::addColumn<int>("startRow"); + QTest::addColumn<int>("count"); + QTest::addColumn<int>("destination"); + QTest::addColumn<QStringList>("expected"); + + QTest::newRow("1_Item_from_top_to_middle") << 0 << 1 << 3 << QStringList{"B", "C", "A", "D", "E", "F"}; + QTest::newRow("1_Item_from_top_to_bottom") << 0 << 1 << 6 << QStringList{"B", "C", "D", "E", "F", "A"}; + QTest::newRow("1_Item_from_middle_to_top") << 2 << 1 << 1 << QStringList{"C", "A", "B", "D", "E", "F"}; + QTest::newRow("1_Item_from_bottom_to_middle") << 5 << 1 << 3 << QStringList{"A", "B", "F", "C", "D", "E"}; + QTest::newRow("1_Item_from_bottom to_top") << 5 << 1 << 1 << QStringList{"F", "A", "B", "C", "D", "E"}; + QTest::newRow("1_Item_from_middle_to_bottom") << 2 << 1 << 6 << QStringList{"A", "B", "D", "E", "F", "C"}; + QTest::newRow("1_Item_from_middle_to_middle_before") << 2 << 1 << 1 << QStringList{"C", "A", "B", "D", "E", "F"}; + QTest::newRow("1_Item_from_middle_to_middle_after") << 2 << 1 << 4 << QStringList{"A", "B", "D", "C", "E", "F"}; + + QTest::newRow("2_Items_from_top_to_middle") << 0 << 2 << 3 << QStringList{"C", "A", "B", "D", "E", "F"}; + QTest::newRow("2_Items_from_top_to_bottom") << 0 << 2 << 6 << QStringList{"C", "D", "E", "F", "A", "B"}; + QTest::newRow("2_Items_from_middle_to_top") << 2 << 2 << 1 << QStringList{"C", "D", "A", "B", "E", "F"}; + QTest::newRow("2_Items_from_bottom_to_middle") << 4 << 2 << 3 << QStringList{"A", "B", "E", "F", "C", "D"}; + QTest::newRow("2_Items_from_bottom_to_top") << 4 << 2 << 1 << QStringList{"E", "F", "A", "B", "C", "D"}; + QTest::newRow("2_Items_from_middle_to_bottom") << 2 << 2 << 6 << QStringList{"A", "B", "E", "F", "C", "D"}; + QTest::newRow("2_Items_from_middle_to_middle_before") << 3 << 2 << 2 << QStringList{"A", "D", "E", "B", "C", "F"}; + QTest::newRow("2_Items_from_middle_to_middle_after") << 1 << 2 << 5 << QStringList{"A", "D", "E", "B", "C", "F"}; +} + +void tst_QListWidget::moveRows() +{ + QFETCH(const int, startRow); + QFETCH(const int, count); + QFETCH(const int, destination); + QFETCH(const QStringList, expected); + QListWidget baseWidget; + baseWidget.addItems(QStringList{"A", "B", "C", "D", "E", "F"}); + QAbstractItemModel *baseModel = baseWidget.model(); + QSignalSpy rowMovedSpy(baseModel, &QAbstractItemModel::rowsMoved); + QSignalSpy rowAboutMovedSpy(baseModel, &QAbstractItemModel::rowsAboutToBeMoved); + QVERIFY(baseModel->moveRows(QModelIndex(), startRow, count, QModelIndex(), destination)); + QCOMPARE(baseModel->rowCount(), expected.size()); + for (int i = 0; i < expected.size(); ++i) + QCOMPARE(baseModel->index(i, 0).data().toString(), expected.at(i)); + QCOMPARE(rowMovedSpy.size(), 1); + QCOMPARE(rowAboutMovedSpy.size(), 1); + for (const QList<QVariant> &signalArgs : {rowMovedSpy.first(), rowAboutMovedSpy.first()}){ + QVERIFY(!signalArgs.at(0).value<QModelIndex>().isValid()); + QCOMPARE(signalArgs.at(1).toInt(), startRow); + QCOMPARE(signalArgs.at(2).toInt(), startRow + count - 1); + QVERIFY(!signalArgs.at(3).value<QModelIndex>().isValid()); + QCOMPARE(signalArgs.at(4).toInt(), destination); + } +} + + typedef QList<int> IntList; tst_QListWidget::tst_QListWidget(): testWidget(0), rcParent(8), rcFirst(8,0), rcLast(8,0) @@ -1743,5 +1854,27 @@ void tst_QListWidget::QTBUG50891_ensureSelectionModelSignalConnectionsAreSet() } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +void tst_QListWidget::clearItemData() +{ + QListWidget list; + for (int i = 0 ; i < 4; ++i) + new QListWidgetItem(QString::number(i), &list); + QSignalSpy dataChangeSpy(list.model(), &QAbstractItemModel::dataChanged); + QVERIFY(dataChangeSpy.isValid()); + QVERIFY(!list.model()->clearItemData(QModelIndex())); + QCOMPARE(dataChangeSpy.size(), 0); + QVERIFY(list.model()->clearItemData(list.model()->index(0, 0))); + QVERIFY(!list.model()->index(0, 0).data().isValid()); + QCOMPARE(dataChangeSpy.size(), 1); + const QList<QVariant> dataChangeArgs = dataChangeSpy.takeFirst(); + QCOMPARE(dataChangeArgs.at(0).value<QModelIndex>(), list.model()->index(0, 0)); + QCOMPARE(dataChangeArgs.at(1).value<QModelIndex>(), list.model()->index(0, 0)); + QVERIFY(dataChangeArgs.at(2).value<QVector<int>>().isEmpty()); + QVERIFY(list.model()->clearItemData(list.model()->index(0, 0))); + QCOMPARE(dataChangeSpy.size(), 0); +} +#endif + QTEST_MAIN(tst_QListWidget) #include "tst_qlistwidget.moc" diff --git a/tests/auto/widgets/itemviews/qtablewidget/tst_qtablewidget.cpp b/tests/auto/widgets/itemviews/qtablewidget/tst_qtablewidget.cpp index 208ce27c8f..510fe5659d 100644 --- a/tests/auto/widgets/itemviews/qtablewidget/tst_qtablewidget.cpp +++ b/tests/auto/widgets/itemviews/qtablewidget/tst_qtablewidget.cpp @@ -89,6 +89,9 @@ private slots: void itemWithHeaderItems(); void mimeData(); void selectedRowAfterSorting(); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + void clearItemData(); +#endif private: QTableWidget *testWidget; @@ -1610,5 +1613,28 @@ void tst_QTableWidget::selectedRowAfterSorting() } } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +void tst_QTableWidget::clearItemData() +{ + QTableWidget table(3,3); + for (int r = 0; r < 3; r++) + for (int c = 0; c < 3; c++) + table.setItem(r,c,new QTableWidgetItem(QStringLiteral("0"))); + QSignalSpy dataChangeSpy(table.model(), &QAbstractItemModel::dataChanged); + QVERIFY(dataChangeSpy.isValid()); + QVERIFY(!table.model()->clearItemData(QModelIndex())); + QCOMPARE(dataChangeSpy.size(), 0); + QVERIFY(table.model()->clearItemData(table.model()->index(0, 0))); + QVERIFY(!table.model()->index(0, 0).data().isValid()); + QCOMPARE(dataChangeSpy.size(), 1); + const QList<QVariant> dataChangeArgs = dataChangeSpy.takeFirst(); + QCOMPARE(dataChangeArgs.at(0).value<QModelIndex>(), table.model()->index(0, 0)); + QCOMPARE(dataChangeArgs.at(1).value<QModelIndex>(), table.model()->index(0, 0)); + QVERIFY(dataChangeArgs.at(2).value<QVector<int>>().isEmpty()); + QVERIFY(table.model()->clearItemData(table.model()->index(0, 0))); + QCOMPARE(dataChangeSpy.size(), 0); +} +#endif + QTEST_MAIN(tst_QTableWidget) #include "tst_qtablewidget.moc" diff --git a/tests/auto/widgets/itemviews/qtreewidget/tst_qtreewidget.cpp b/tests/auto/widgets/itemviews/qtreewidget/tst_qtreewidget.cpp index 8c93df9073..4a6244826e 100644 --- a/tests/auto/widgets/itemviews/qtreewidget/tst_qtreewidget.cpp +++ b/tests/auto/widgets/itemviews/qtreewidget/tst_qtreewidget.cpp @@ -161,6 +161,9 @@ private slots: void getMimeDataWithInvalidItem(); void testVisualItemRect(); void reparentHiddenItem(); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + void clearItemData(); +#endif public slots: void itemSelectionChanged(); @@ -3004,7 +3007,7 @@ void tst_QTreeWidget::task191552_rtl() opt.features = QStyleOptionViewItem::HasDisplay | QStyleOptionViewItem::HasCheckIndicator; opt.checkState = Qt::Checked; opt.widget = &tw; - const QRect checkRect = tw.style()->subElementRect(QStyle::SE_ViewItemCheckIndicator, &opt, &tw); + const QRect checkRect = tw.style()->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &opt, &tw); QTest::mouseClick(tw.viewport(), Qt::LeftButton, Qt::NoModifier, checkRect.center()); QCOMPARE(item->checkState(0), Qt::Unchecked); @@ -3218,7 +3221,7 @@ void tst_QTreeWidget::nonEditableTristate() option.checkState = item->checkState(0); const int checkMargin = qApp->style()->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, 0) + 1; - QPoint pos = qApp->style()->subElementRect(QStyle::SE_ViewItemCheckIndicator, &option, 0).center() + QPoint(checkMargin, 0); + QPoint pos = qApp->style()->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &option, 0).center() + QPoint(checkMargin, 0); QTest::mouseClick(tree->viewport(), Qt::LeftButton, Qt::NoModifier, pos); QCOMPARE(int(item->checkState(0)), int(Qt::Checked)); @@ -3529,5 +3532,43 @@ void tst_QTreeWidget::reparentHiddenItem() QVERIFY(grandChild->isHidden()); } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +void tst_QTreeWidget::clearItemData() +{ + QTreeWidget tree; + QAbstractItemModel* model = tree.model(); + QVERIFY(model->insertColumn(0)); + QVERIFY(model->insertRow(0)); + const QModelIndex parentIdx = model->index(0, 0); + QVERIFY(model->insertColumn(0, parentIdx)); + QVERIFY(model->insertRow(0, parentIdx)); + const QModelIndex childIdx = model->index(0, 0, parentIdx); + model->setData(parentIdx, QStringLiteral("parent")); + model->setData(parentIdx, QStringLiteral("parent"), Qt::UserRole); + model->setData(childIdx, QStringLiteral("child")); + QSignalSpy dataChangeSpy(model, &QAbstractItemModel::dataChanged); + QVERIFY(dataChangeSpy.isValid()); + QVERIFY(!model->clearItemData(QModelIndex())); + QCOMPARE(dataChangeSpy.size(), 0); + QVERIFY(model->clearItemData(parentIdx)); + QVERIFY(!model->data(parentIdx).isValid()); + QVERIFY(!model->data(parentIdx, Qt::UserRole).isValid()); + QCOMPARE(dataChangeSpy.size(), 1); + QList<QVariant> dataChangeArgs = dataChangeSpy.takeFirst(); + QCOMPARE(dataChangeArgs.at(0).value<QModelIndex>(), parentIdx); + QCOMPARE(dataChangeArgs.at(1).value<QModelIndex>(), parentIdx); + QVERIFY(dataChangeArgs.at(2).value<QVector<int>>().isEmpty()); + QVERIFY(model->clearItemData(parentIdx)); + QCOMPARE(dataChangeSpy.size(), 0); + QVERIFY(model->clearItemData(childIdx)); + QVERIFY(!model->data(childIdx).isValid()); + QCOMPARE(dataChangeSpy.size(), 1); + dataChangeArgs = dataChangeSpy.takeFirst(); + QCOMPARE(dataChangeArgs.at(0).value<QModelIndex>(), childIdx); + QCOMPARE(dataChangeArgs.at(1).value<QModelIndex>(), childIdx); + QVERIFY(dataChangeArgs.at(2).value<QVector<int>>().isEmpty()); +} +#endif + QTEST_MAIN(tst_QTreeWidget) #include "tst_qtreewidget.moc" diff --git a/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp b/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp index a88fd8d19c..634e258250 100644 --- a/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp +++ b/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp @@ -1143,8 +1143,16 @@ void tst_QMenu::QTBUG7411_submenus_activate() QTRY_VERIFY(sub1.isVisible()); } +static bool isPlatformWayland() +{ + return !QGuiApplication::platformName().compare(QLatin1String("wayland"), Qt::CaseInsensitive); +} + void tst_QMenu::QTBUG30595_rtl_submenu() { + if (isPlatformWayland()) + QSKIP("Creating xdg_popups on Wayland requires real input events. Positions would be off."); + QMenu menu("Test Menu"); menu.setLayoutDirection(Qt::RightToLeft); QMenu sub("&sub"); @@ -1179,6 +1187,9 @@ void tst_QMenu::QTBUG20403_nested_popup_on_shortcut_trigger() #ifndef Q_OS_MACOS void tst_QMenu::click_while_dismissing_submenu() { + if (isPlatformWayland()) + QSKIP("Wayland: Creating (grabbing) popups requires real mouse events."); + QMenu menu("Test Menu"); QAction *action = menu.addAction("action"); QMenu sub("&sub"); diff --git a/tests/benchmarks/widgets/itemviews/itemviews.pro b/tests/benchmarks/widgets/itemviews/itemviews.pro index a23cdf7b97..0c3256f307 100644 --- a/tests/benchmarks/widgets/itemviews/itemviews.pro +++ b/tests/benchmarks/widgets/itemviews/itemviews.pro @@ -1,4 +1,5 @@ TEMPLATE = subdirs SUBDIRS = \ qtableview \ - qheaderview + qheaderview \ + qlistview diff --git a/tests/benchmarks/widgets/itemviews/qlistview/qlistview.pro b/tests/benchmarks/widgets/itemviews/qlistview/qlistview.pro new file mode 100644 index 0000000000..68537d09ea --- /dev/null +++ b/tests/benchmarks/widgets/itemviews/qlistview/qlistview.pro @@ -0,0 +1,7 @@ +QT += widgets testlib + +TEMPLATE = app +TARGET = tst_bench_qlistview + +SOURCES += tst_qlistview.cpp + diff --git a/tests/benchmarks/widgets/itemviews/qlistview/tst_qlistview.cpp b/tests/benchmarks/widgets/itemviews/qlistview/tst_qlistview.cpp new file mode 100644 index 0000000000..01496743c2 --- /dev/null +++ b/tests/benchmarks/widgets/itemviews/qlistview/tst_qlistview.cpp @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qtest.h> +#include <QListView> +#include <QStandardItemModel> + + +class tst_QListView : public QObject +{ + Q_OBJECT + +public: + tst_QListView() = default; + virtual ~tst_QListView() = default; + +private slots: + void benchSetCurrentIndex(); +}; + +void tst_QListView::benchSetCurrentIndex() +{ + QStandardItemModel sm(50000, 1); + QListView lv; + lv.setModel(&sm); + const int rc = lv.model()->rowCount(); + for (int i = 0; i < rc; i+= 100) + lv.setRowHidden(i, true); + lv.setCurrentIndex(lv.model()->index(0, 0, QModelIndex())); + lv.show(); + QVERIFY(QTest::qWaitForWindowExposed(&lv)); + + QBENCHMARK_ONCE { + while (lv.currentIndex().row() < rc - 20) + lv.setCurrentIndex(lv.model()->index(lv.currentIndex().row() + 17, + lv.currentIndex().column(), + QModelIndex())); + } +} + + +QTEST_MAIN(tst_QListView) +#include "tst_qlistview.moc" diff --git a/tests/manual/widgets/itemviews/itemviews.pro b/tests/manual/widgets/itemviews/itemviews.pro index 53f658d54d..8884cc3aae 100644 --- a/tests/manual/widgets/itemviews/itemviews.pro +++ b/tests/manual/widgets/itemviews/itemviews.pro @@ -1,2 +1,8 @@ TEMPLATE = subdirs -SUBDIRS = delegate qheaderview qtreeview qtreewidget tableview-span-navigation +SUBDIRS = delegate \ + qconcatenatetablesproxymodel \ + qheaderview \ + qtreeview \ + qtreewidget \ + tableview-span-navigation \ + diff --git a/tests/manual/widgets/itemviews/qconcatenatetablesproxymodel/main.cpp b/tests/manual/widgets/itemviews/qconcatenatetablesproxymodel/main.cpp new file mode 100644 index 0000000000..2c1825f29f --- /dev/null +++ b/tests/manual/widgets/itemviews/qconcatenatetablesproxymodel/main.cpp @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author David Faure <david.faure@kdab.com> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QApplication> +#include <QConcatenateTablesProxyModel> +#include <QStandardItemModel> +#include <QTableView> +#include <QTreeView> + +static void prepareModel(const QString &prefix, QStandardItemModel *model) +{ + for (int row = 0; row < model->rowCount(); ++row) { + for (int column = 0; column < model->columnCount(); ++column) { + QStandardItem *item = new QStandardItem(prefix + QString(" %1,%2").arg(row).arg(column)); + item->setDragEnabled(true); + item->setDropEnabled(true); + model->setItem(row, column, item); + } + } +} + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + QStandardItemModel firstModel(4, 4); + prepareModel("First", &firstModel); + QStandardItemModel secondModel(2, 2); + + QConcatenateTablesProxyModel proxy; + proxy.addSourceModel(&firstModel); + proxy.addSourceModel(&secondModel); + + prepareModel("Second", &secondModel); + + QTableView tableView; + tableView.setWindowTitle("concat proxy, in QTableView"); + tableView.setDragDropMode(QAbstractItemView::DragDrop); + tableView.setModel(&proxy); + tableView.show(); + + QTreeView treeView; + treeView.setWindowTitle("concat proxy, in QTreeView"); + treeView.setDragDropMode(QAbstractItemView::DragDrop); + treeView.setModel(&proxy); + treeView.show(); + + // For comparison, views on top on QStandardItemModel + + QTableView tableViewTest; + tableViewTest.setWindowTitle("first model, in QTableView"); + tableViewTest.setDragDropMode(QAbstractItemView::DragDrop); + tableViewTest.setModel(&firstModel); + tableViewTest.show(); + + QTreeView treeViewTest; + treeViewTest.setWindowTitle("first model, in QTreeView"); + treeViewTest.setDragDropMode(QAbstractItemView::DragDrop); + treeViewTest.setModel(&firstModel); + treeViewTest.show(); + + return app.exec(); +} diff --git a/tests/manual/widgets/itemviews/qconcatenatetablesproxymodel/qconcatenatetablesproxymodel.pro b/tests/manual/widgets/itemviews/qconcatenatetablesproxymodel/qconcatenatetablesproxymodel.pro new file mode 100644 index 0000000000..19904212a7 --- /dev/null +++ b/tests/manual/widgets/itemviews/qconcatenatetablesproxymodel/qconcatenatetablesproxymodel.pro @@ -0,0 +1,8 @@ + +TEMPLATE = app +TARGET = qconcatenatetablesproxymodel +INCLUDEPATH += . + +QT += widgets + +SOURCES += main.cpp diff --git a/util/local_database/cldr2qlocalexml.py b/util/local_database/cldr2qlocalexml.py index ce45f631a6..bc999e1b65 100755 --- a/util/local_database/cldr2qlocalexml.py +++ b/util/local_database/cldr2qlocalexml.py @@ -51,6 +51,7 @@ order. import os import sys import re +import textwrap import enumdata import xpathlite @@ -59,6 +60,10 @@ from dateconverter import convert_date from localexml import Locale findEntryInFile = xpathlite._findEntryInFile +def wrappedwarn(prefix, tokens): + return sys.stderr.write( + '\n'.join(textwrap.wrap(prefix + ', '.join(tokens), + subsequent_indent=' ', width=80)) + '\n') def parse_number_format(patterns, data): # this is a very limited parsing of the number format for currency only. @@ -164,9 +169,9 @@ def getNumberSystems(cache={}): entry = dict(ns[1]) name = entry[u'id'] if u'digits' in entry and ord(entry[u'digits'][0]) > 0xffff: - # FIXME: make this redundant: + # FIXME, QTBUG-69324: make this redundant: # omit number system if zero doesn't fit in single-char16 UTF-16 :-( - sys.stderr.write('skipping number system "%s" [can\'t represent its zero, U+%X, QTBUG-69324]\n' + sys.stderr.write('skipping number system "%s" [can\'t represent its zero, U+%X]\n' % (name, ord(entry[u'digits'][0]))) else: cache[name] = entry @@ -243,7 +248,7 @@ def _generateLocaleInfo(path, language_code, script_code, country_code, variant_ numbering_system = None try: numbering_system = findEntry(path, "numbers/defaultNumberingSystem") - except: + except xpathlite.Error: pass def findEntryDef(path, xpath, value=''): try: @@ -438,6 +443,38 @@ def integrateWeekData(filePath): else: locale.weekendEnd = weekendEndByCountryCode["001"] +def splitLocale(name): + """Split name into (language, script, territory) triple as generator. + + Ignores any trailing fields (with a warning), leaves script (a capitalised + four-letter token) or territory (either a number or an all-uppercase token) + empty if unspecified, returns a single-entry generator if name is a single + tag (i.e. contains no underscores). Always yields 1 or 3 values, never 2.""" + tags = iter(name.split('_')) + yield tags.next() # Language + tag = tags.next() + + # Script is always four letters, always capitalised: + if len(tag) == 4 and tag[0].isupper() and tag[1:].islower(): + yield tag + try: + tag = tags.next() + except StopIteration: + tag = '' + else: + yield '' + + # Territory is upper-case or numeric: + if tag and tag.isupper() or tag.isdigit(): + yield tag + tag = '' + else: + yield '' + + # If nothing is left, StopIteration will avoid the warning: + tag = (tag if tag else tags.next(),) + sys.stderr.write('Ignoring unparsed cruft %s in %s\n' % ('_'.join(tag + tuple(tags)), name)) + if len(sys.argv) != 2: usage() @@ -451,34 +488,30 @@ cldr_files = os.listdir(cldr_dir) locale_database = {} # see http://www.unicode.org/reports/tr35/tr35-info.html#Default_Content -defaultContent_locales = {} +defaultContent_locales = [] for ns in findTagsInFile(os.path.join(cldr_dir, '..', 'supplemental', 'supplementalMetadata.xml'), 'metadata/defaultContent'): for data in ns[1:][0]: if data[0] == u"locales": - defaultContent_locales = data[1].split() + defaultContent_locales += data[1].split() +skips = [] for file in defaultContent_locales: - items = file.split("_") - if len(items) == 3: - language_code = items[0] - script_code = items[1] - country_code = items[2] - else: - if len(items) != 2: - sys.stderr.write('skipping defaultContent locale "' + file + '" [neither lang_script_country nor lang_country]\n') - continue - language_code = items[0] - script_code = "" - country_code = items[1] - if len(country_code) == 4: - sys.stderr.write('skipping defaultContent locale "' + file + '" [long country code]\n') - continue + try: + language_code, script_code, country_code = splitLocale(file) + except ValueError: + sys.stderr.write('skipping defaultContent locale "' + file + '" [neither two nor three tags]\n') + continue + + if not (script_code or country_code): + sys.stderr.write('skipping defaultContent locale "' + file + '" [second tag is neither script nor territory]\n') + continue + try: l = _generateLocaleInfo(cldr_dir + "/" + file + ".xml", language_code, script_code, country_code) if not l: - sys.stderr.write('skipping defaultContent locale "' + file + '" [no locale info generated]\n') + skips.append(file) continue except xpathlite.Error as e: sys.stderr.write('skipping defaultContent locale "%s" (%s)\n' % (file, str(e))) @@ -486,11 +519,15 @@ for file in defaultContent_locales: locale_database[(l.language_id, l.script_id, l.country_id, l.variant_code)] = l +if skips: + wrappedwarn('skipping defaultContent locales [no locale info generated]: ', skips) + skips = [] + for file in cldr_files: try: l = generateLocaleInfo(cldr_dir + "/" + file) if not l: - sys.stderr.write('skipping file "' + file + '" [no locale info generated]\n') + skips.append(file) continue except xpathlite.Error as e: sys.stderr.write('skipping file "%s" (%s)\n' % (file, str(e))) @@ -498,6 +535,9 @@ for file in cldr_files: locale_database[(l.language_id, l.script_id, l.country_id, l.variant_code)] = l +if skips: + wrappedwarn('skipping files [no locale info generated]: ', skips) + integrateWeekData(cldr_dir+"/../supplemental/supplementalData.xml") locale_keys = locale_database.keys() locale_keys.sort() @@ -548,34 +588,35 @@ def _parseLocale(l): if l == "und": raise xpathlite.Error("we are treating unknown locale like C") - items = l.split("_") - language_code = items[0] + parsed = splitLocale(l) + language_code = parsed.next() + script_code = country_code = '' + try: + script_code, country_code = parsed + except ValueError: + pass + if language_code != "und": language_id = enumdata.languageCodeToId(language_code) if language_id == -1: raise xpathlite.Error('unknown language code "%s"' % language_code) language = enumdata.language_list[language_id][0] - if len(items) > 1: - script_code = items[1] - country_code = "" - if len(items) > 2: - country_code = items[2] - if len(script_code) == 4: - script_id = enumdata.scriptCodeToId(script_code) - if script_id == -1: - raise xpathlite.Error('unknown script code "%s"' % script_code) - script = enumdata.script_list[script_id][0] - else: - country_code = script_code - if country_code: - country_id = enumdata.countryCodeToId(country_code) - if country_id == -1: - raise xpathlite.Error('unknown country code "%s"' % country_code) - country = enumdata.country_list[country_id][0] + if script_code: + script_id = enumdata.scriptCodeToId(script_code) + if script_id == -1: + raise xpathlite.Error('unknown script code "%s"' % script_code) + script = enumdata.script_list[script_id][0] + + if country_code: + country_id = enumdata.countryCodeToId(country_code) + if country_id == -1: + raise xpathlite.Error('unknown country code "%s"' % country_code) + country = enumdata.country_list[country_id][0] return (language, script, country) +skips = [] print " <likelySubtags>" for ns in findTagsInFile(cldr_dir + "/../supplemental/likelySubtags.xml", "likelySubtags"): tmp = {} @@ -583,14 +624,13 @@ for ns in findTagsInFile(cldr_dir + "/../supplemental/likelySubtags.xml", "likel tmp[data[0]] = data[1] try: - (from_language, from_script, from_country) = _parseLocale(tmp[u"from"]) + from_language, from_script, from_country = _parseLocale(tmp[u"from"]) + to_language, to_script, to_country = _parseLocale(tmp[u"to"]) except xpathlite.Error as e: - sys.stderr.write('skipping likelySubtag "%s" -> "%s" (%s)\n' % (tmp[u"from"], tmp[u"to"], str(e))) - continue - try: - (to_language, to_script, to_country) = _parseLocale(tmp[u"to"]) - except xpathlite.Error as e: - sys.stderr.write('skipping likelySubtag "%s" -> "%s" (%s)\n' % (tmp[u"from"], tmp[u"to"], str(e))) + if tmp[u'to'].startswith(tmp[u'from']) and str(e) == 'unknown language code "%s"' % tmp[u'from']: + skips.append(tmp[u'to']) + else: + sys.stderr.write('skipping likelySubtag "%s" -> "%s" (%s)\n' % (tmp[u"from"], tmp[u"to"], str(e))) continue # substitute according to http://www.unicode.org/reports/tr35/#Likely_Subtags if to_country == "AnyCountry" and from_country != to_country: @@ -611,7 +651,8 @@ for ns in findTagsInFile(cldr_dir + "/../supplemental/likelySubtags.xml", "likel print " </to>" print " </likelySubtag>" print " </likelySubtags>" - +if skips: + wrappedwarn('skipping likelySubtags (for unknown language codes): ', skips) print " <localeList>" Locale.C().toXml() diff --git a/util/local_database/enumdata.py b/util/local_database/enumdata.py index e24ac02b07..26bb74d1fe 100644 --- a/util/local_database/enumdata.py +++ b/util/local_database/enumdata.py @@ -37,366 +37,371 @@ ### Qt 6: restore alphabetic order in each list. language_list = { - 0 : ["AnyLanguage", " "], - 1 : ["C", " "], - 2 : ["Abkhazian", "ab"], - 3 : ["Oromo", "om"], # macrolanguage - 4 : ["Afar", "aa"], - 5 : ["Afrikaans", "af"], - 6 : ["Albanian", "sq"], # macrolanguage - 7 : ["Amharic", "am"], - 8 : ["Arabic", "ar"], # macrolanguage - 9 : ["Armenian", "hy"], - 10 : ["Assamese", "as"], - 11 : ["Aymara", "ay"], # macrolanguage - 12 : ["Azerbaijani", "az"], # macrolanguage - 13 : ["Bashkir", "ba"], - 14 : ["Basque", "eu"], - 15 : ["Bengali", "bn"], - 16 : ["Dzongkha", "dz"], - 17 : ["Bihari", "bh"], - 18 : ["Bislama", "bi"], - 19 : ["Breton", "br"], - 20 : ["Bulgarian", "bg"], - 21 : ["Burmese", "my"], - 22 : ["Belarusian", "be"], - 23 : ["Khmer", "km"], - 24 : ["Catalan", "ca"], - 25 : ["Chinese", "zh"], # macrolanguage - 26 : ["Corsican", "co"], - 27 : ["Croatian", "hr"], - 28 : ["Czech", "cs"], - 29 : ["Danish", "da"], - 30 : ["Dutch", "nl"], - 31 : ["English", "en"], - 32 : ["Esperanto", "eo"], - 33 : ["Estonian", "et"], # macrolanguage - 34 : ["Faroese", "fo"], - 35 : ["Fijian", "fj"], - 36 : ["Finnish", "fi"], - 37 : ["French", "fr"], - 38 : ["Western Frisian", "fy"], - 39 : ["Gaelic", "gd"], - 40 : ["Galician", "gl"], - 41 : ["Georgian", "ka"], - 42 : ["German", "de"], - 43 : ["Greek", "el"], - 44 : ["Greenlandic", "kl"], - 45 : ["Guarani", "gn"], # macrolanguage - 46 : ["Gujarati", "gu"], - 47 : ["Hausa", "ha"], - 48 : ["Hebrew", "he"], - 49 : ["Hindi", "hi"], - 50 : ["Hungarian", "hu"], - 51 : ["Icelandic", "is"], - 52 : ["Indonesian", "id"], - 53 : ["Interlingua", "ia"], - 54 : ["Interlingue", "ie"], - 55 : ["Inuktitut", "iu"], # macrolanguage - 56 : ["Inupiak", "ik"], # macrolanguage - 57 : ["Irish", "ga"], - 58 : ["Italian", "it"], - 59 : ["Japanese", "ja"], - 60 : ["Javanese", "jv"], - 61 : ["Kannada", "kn"], - 62 : ["Kashmiri", "ks"], - 63 : ["Kazakh", "kk"], - 64 : ["Kinyarwanda", "rw"], - 65 : ["Kirghiz", "ky"], - 66 : ["Korean", "ko"], - 67 : ["Kurdish", "ku"], # macrolanguage - 68 : ["Rundi", "rn"], - 69 : ["Lao", "lo"], - 70 : ["Latin", "la"], - 71 : ["Latvian", "lv"], # macrolanguage - 72 : ["Lingala", "ln"], - 73 : ["Lithuanian", "lt"], - 74 : ["Macedonian", "mk"], - 75 : ["Malagasy", "mg"], # macrolanguage - 76 : ["Malay", "ms"], # macrolanguage - 77 : ["Malayalam", "ml"], - 78 : ["Maltese", "mt"], - 79 : ["Maori", "mi"], - 80 : ["Marathi", "mr"], - 81 : ["Marshallese", "mh"], - 82 : ["Mongolian", "mn"], # macrolanguage - 83 : ["Nauru", "na"], - 84 : ["Nepali", "ne"], # macrolanguage - 85 : ["Norwegian Bokmal", "nb"], - 86 : ["Occitan", "oc"], - 87 : ["Oriya", "or"], # macrolanguage - 88 : ["Pashto", "ps"], # macrolanguage - 89 : ["Persian", "fa"], # macrolanguage - 90 : ["Polish", "pl"], - 91 : ["Portuguese", "pt"], - 92 : ["Punjabi", "pa"], - 93 : ["Quechua", "qu"], # macrolanguage - 94 : ["Romansh", "rm"], - 95 : ["Romanian", "ro"], - 96 : ["Russian", "ru"], - 97 : ["Samoan", "sm"], - 98 : ["Sango", "sg"], - 99 : ["Sanskrit", "sa"], - 100 : ["Serbian", "sr"], - 101 : ["Ossetic", "os"], - 102 : ["Southern Sotho", "st"], - 103 : ["Tswana", "tn"], - 104 : ["Shona", "sn"], - 105 : ["Sindhi", "sd"], - 106 : ["Sinhala", "si"], - 107 : ["Swati", "ss"], - 108 : ["Slovak", "sk"], - 109 : ["Slovenian", "sl"], - 110 : ["Somali", "so"], - 111 : ["Spanish", "es"], - 112 : ["Sundanese", "su"], - 113 : ["Swahili", "sw"], # macrolanguage - 114 : ["Swedish", "sv"], - 115 : ["Sardinian", "sc"], # macrolanguage - 116 : ["Tajik", "tg"], - 117 : ["Tamil", "ta"], - 118 : ["Tatar", "tt"], - 119 : ["Telugu", "te"], - 120 : ["Thai", "th"], - 121 : ["Tibetan", "bo"], - 122 : ["Tigrinya", "ti"], - 123 : ["Tongan", "to"], - 124 : ["Tsonga", "ts"], - 125 : ["Turkish", "tr"], - 126 : ["Turkmen", "tk"], - 127 : ["Tahitian", "ty"], - 128 : ["Uighur", "ug"], - 129 : ["Ukrainian", "uk"], - 130 : ["Urdu", "ur"], - 131 : ["Uzbek", "uz"], # macrolanguage - 132 : ["Vietnamese", "vi"], - 133 : ["Volapuk", "vo"], - 134 : ["Welsh", "cy"], - 135 : ["Wolof", "wo"], - 136 : ["Xhosa", "xh"], - 137 : ["Yiddish", "yi"], # macrolanguage - 138 : ["Yoruba", "yo"], - 139 : ["Zhuang", "za"], # macrolanguage - 140 : ["Zulu", "zu"], - 141 : ["Norwegian Nynorsk", "nn"], - 142 : ["Bosnian", "bs"], - 143 : ["Divehi", "dv"], - 144 : ["Manx", "gv"], - 145 : ["Cornish", "kw"], - 146 : ["Akan", "ak"], # macrolanguage - 147 : ["Konkani", "kok"], - 148 : ["Ga", "gaa"], - 149 : ["Igbo", "ig" ], - 150 : ["Kamba", "kam"], - 151 : ["Syriac", "syr"], - 152 : ["Blin", "byn"], - 153 : ["Geez", "gez"], - 154 : ["Koro", "kfo"], - 155 : ["Sidamo", "sid"], - 156 : ["Atsam", "cch"], - 157 : ["Tigre", "tig"], - 158 : ["Jju", "kaj"], - 159 : ["Friulian", "fur"], - 160 : ["Venda", "ve" ], - 161 : ["Ewe", "ee" ], - 162 : ["Walamo", "wal"], - 163 : ["Hawaiian", "haw"], - 164 : ["Tyap", "kcg"], - 165 : ["Nyanja", "ny" ], - 166 : ["Filipino", "fil"], - 167 : ["Swiss German", "gsw"], - 168 : ["Sichuan Yi", "ii" ], - 169 : ["Kpelle", "kpe"], - 170 : ["Low German", "nds"], - 171 : ["South Ndebele", "nr" ], - 172 : ["Northern Sotho", "nso"], - 173 : ["Northern Sami", "se" ], - 174 : ["Taroko", "trv"], - 175 : ["Gusii", "guz"], - 176 : ["Taita", "dav"], - 177 : ["Fulah", "ff"], # macrolanguage - 178 : ["Kikuyu", "ki"], - 179 : ["Samburu", "saq"], - 180 : ["Sena", "seh"], - 181 : ["North Ndebele", "nd"], - 182 : ["Rombo", "rof"], - 183 : ["Tachelhit", "shi"], - 184 : ["Kabyle", "kab"], - 185 : ["Nyankole", "nyn"], - 186 : ["Bena", "bez"], - 187 : ["Vunjo", "vun"], - 188 : ["Bambara", "bm"], - 189 : ["Embu", "ebu"], - 190 : ["Cherokee", "chr"], - 191 : ["Morisyen", "mfe"], - 192 : ["Makonde", "kde"], - 193 : ["Langi", "lag"], - 194 : ["Ganda", "lg"], - 195 : ["Bemba", "bem"], - 196 : ["Kabuverdianu", "kea"], - 197 : ["Meru", "mer"], - 198 : ["Kalenjin", "kln"], - 199 : ["Nama", "naq"], - 200 : ["Machame", "jmc"], - 201 : ["Colognian", "ksh"], - 202 : ["Masai", "mas"], - 203 : ["Soga", "xog"], - 204 : ["Luyia", "luy"], - 205 : ["Asu", "asa"], - 206 : ["Teso", "teo"], - 207 : ["Saho", "ssy"], - 208 : ["Koyra Chiini", "khq"], - 209 : ["Rwa", "rwk"], - 210 : ["Luo", "luo"], - 211 : ["Chiga", "cgg"], - 212 : ["Central Morocco Tamazight", "tzm"], - 213 : ["Koyraboro Senni", "ses"], - 214 : ["Shambala", "ksb"], - 215 : ["Bodo", "brx"], - 216 : ["Avaric", "av"], - 217 : ["Chamorro", "ch"], - 218 : ["Chechen", "ce"], - 219 : ["Church", "cu"], # macrolanguage - 220 : ["Chuvash", "cv"], - 221 : ["Cree", "cr"], # macrolanguage - 222 : ["Haitian", "ht"], - 223 : ["Herero", "hz"], - 224 : ["Hiri Motu", "ho"], - 225 : ["Kanuri", "kr"], # macrolanguage - 226 : ["Komi", "kv"], # macrolanguage - 227 : ["Kongo", "kg"], # macrolanguage - 228 : ["Kwanyama", "kj"], - 229 : ["Limburgish", "li"], - 230 : ["Luba Katanga", "lu"], - 231 : ["Luxembourgish", "lb"], - 232 : ["Navaho", "nv"], - 233 : ["Ndonga", "ng"], - 234 : ["Ojibwa", "oj"], # macrolanguage - 235 : ["Pali", "pi"], # macrolanguage - 236 : ["Walloon", "wa"], - 237 : ["Aghem", "agq"], - 238 : ["Basaa", "bas"], - 239 : ["Zarma", "dje"], - 240 : ["Duala", "dua"], - 241 : ["Jola Fonyi", "dyo"], - 242 : ["Ewondo", "ewo"], - 243 : ["Bafia", "ksf"], - 244 : ["Makhuwa Meetto", "mgh"], - 245 : ["Mundang", "mua"], - 246 : ["Kwasio", "nmg"], - 247 : ["Nuer", "nus"], - 248 : ["Sakha", "sah"], - 249 : ["Sangu", "sbp"], - 250 : ["Congo Swahili", "swc"], - 251 : ["Tasawaq", "twq"], - 252 : ["Vai", "vai"], - 253 : ["Walser", "wae"], - 254 : ["Yangben", "yav"], - 255 : ["Avestan", "ae"], - 256 : ["Asturian", "ast"], - 257 : ["Ngomba", "jgo"], - 258 : ["Kako", "kkj"], - 259 : ["Meta", "mgo"], - 260 : ["Ngiemboon", "nnh"], - 261 : ["Aragonese", "an"], - 262 : ["Akkadian", "akk"], - 263 : ["Ancient Egyptian", "egy"], - 264 : ["Ancient Greek", "grc"], - 265 : ["Aramaic", "arc"], - 266 : ["Balinese", "ban"], - 267 : ["Bamun", "bax"], - 268 : ["Batak Toba", "bbc"], - 269 : ["Buginese", "bug"], - 270 : ["Buhid", "bku"], - 271 : ["Carian", "xcr"], - 272 : ["Chakma", "ccp"], - 273 : ["Classical Mandaic", "myz"], - 274 : ["Coptic", "cop"], - 275 : ["Dogri", "doi"], # macrolanguage - 276 : ["Eastern Cham", "cjm"], - 277 : ["Eastern Kayah", "eky"], - 278 : ["Etruscan", "ett"], - 279 : ["Gothic", "got"], - 280 : ["Hanunoo", "hnn"], - 281 : ["Ingush", "inh"], - 282 : ["Large Flowery Miao", "hmd"], - 283 : ["Lepcha", "lep"], - 284 : ["Limbu", "lif"], - 285 : ["Lisu", "lis"], - 286 : ["Lu", "khb"], - 287 : ["Lycian", "xlc"], - 288 : ["Lydian", "xld"], - 289 : ["Mandingo", "man"], # macrolanguage - 290 : ["Manipuri", "mni"], - 291 : ["Meroitic", "xmr"], - 292 : ["Northern Thai", "nod"], - 293 : ["Old Irish", "sga"], - 294 : ["Old Norse", "non"], - 295 : ["Old Persian", "peo"], - 296 : ["Old Turkish", "otk"], - 297 : ["Pahlavi", "pal"], - 298 : ["Parthian", "xpr"], - 299 : ["Phoenician", "phn"], - 300 : ["Prakrit Language", "pra"], - 301 : ["Rejang", "rej"], - 302 : ["Sabaean", "xsa"], - 303 : ["Samaritan", "smp"], - 304 : ["Santali", "sat"], - 305 : ["Saurashtra", "saz"], - 306 : ["Sora", "srb"], - 307 : ["Sylheti", "syl"], - 308 : ["Tagbanwa", "tbw"], - 309 : ["Tai Dam", "blt"], - 310 : ["Tai Nua", "tdd"], - 311 : ["Ugaritic", "uga"], - 312 : ["Akoose", "bss"], - 313 : ["Lakota", "lkt"], - 314 : ["Standard Moroccan Tamazight", "zgh"], - 315 : ["Mapuche", "arn"], - 316 : ["Central Kurdish", "ckb"], - 317 : ["Lower Sorbian", "dsb"], - 318 : ["Upper Sorbian", "hsb"], - 319 : ["Kenyang", "ken"], - 320 : ["Mohawk", "moh"], - 321 : ["Nko", "nqo"], - 322 : ["Prussian", "prg"], - 323 : ["Kiche", "quc"], - 324 : ["Southern Sami", "sma"], - 325 : ["Lule Sami", "smj"], - 326 : ["Inari Sami", "smn"], - 327 : ["Skolt Sami", "sms"], - 328 : ["Warlpiri", "wbp"], - 329 : ["Manichaean Middle Persian", "xmn"], - 330 : ["Mende", "men"], - 331 : ["Ancient North Arabian", "xna"], - 332 : ["Linear A", "lab"], - 333 : ["Hmong Njua", "hnj"], - 334 : ["Ho", "hoc"], - 335 : ["Lezghian", "lez"], - 336 : ["Bassa", "bsq"], - 337 : ["Mono", "mru"], - 338 : ["Tedim Chin", "ctd"], - 339 : ["Maithili", "mai"], - 340 : ["Ahom", "aho"], - 341 : ["American Sign Language", "ase"], - 342 : ["Ardhamagadhi Prakrit", "pka"], - 343 : ["Bhojpuri", "bho"], - 344 : ["Hieroglyphic Luwian", "hlu"], - 345 : ["Literary Chinese", "lzh"], - 346 : ["Mazanderani", "mzn"], - 347 : ["Mru", "mro"], - 348 : ["Newari", "new"], - 349 : ["Northern Luri", "lrc"], - 350 : ["Palauan", "pau"], - 351 : ["Papiamento", "pap"], - 352 : ["Saraiki", "skr"], - 353 : ["Tokelau", "tkl"], - 354 : ["Tok Pisin", "tpi"], - 355 : ["Tuvalu", "tvl"], - 356 : ["Uncoded Languages", "mis"], - 357 : ["Cantonese", "yue"], - 358 : ["Osage", "osa"], - 359 : ["Tangut", "txg"] + 0: ["AnyLanguage", " "], + 1: ["C", " "], + 2: ["Abkhazian", "ab"], + 3: ["Oromo", "om"], # macrolanguage + 4: ["Afar", "aa"], + 5: ["Afrikaans", "af"], + 6: ["Albanian", "sq"], # macrolanguage + 7: ["Amharic", "am"], + 8: ["Arabic", "ar"], # macrolanguage + 9: ["Armenian", "hy"], + 10: ["Assamese", "as"], + 11: ["Aymara", "ay"], # macrolanguage + 12: ["Azerbaijani", "az"], # macrolanguage + 13: ["Bashkir", "ba"], + 14: ["Basque", "eu"], + 15: ["Bengali", "bn"], + 16: ["Dzongkha", "dz"], + 17: ["Bihari", "bh"], + 18: ["Bislama", "bi"], + 19: ["Breton", "br"], + 20: ["Bulgarian", "bg"], + 21: ["Burmese", "my"], + 22: ["Belarusian", "be"], + 23: ["Khmer", "km"], + 24: ["Catalan", "ca"], + 25: ["Chinese", "zh"], # macrolanguage + 26: ["Corsican", "co"], + 27: ["Croatian", "hr"], + 28: ["Czech", "cs"], + 29: ["Danish", "da"], + 30: ["Dutch", "nl"], + 31: ["English", "en"], + 32: ["Esperanto", "eo"], + 33: ["Estonian", "et"], # macrolanguage + 34: ["Faroese", "fo"], + 35: ["Fijian", "fj"], + 36: ["Finnish", "fi"], + 37: ["French", "fr"], + 38: ["Western Frisian", "fy"], + 39: ["Gaelic", "gd"], + 40: ["Galician", "gl"], + 41: ["Georgian", "ka"], + 42: ["German", "de"], + 43: ["Greek", "el"], + 44: ["Greenlandic", "kl"], + 45: ["Guarani", "gn"], # macrolanguage + 46: ["Gujarati", "gu"], + 47: ["Hausa", "ha"], + 48: ["Hebrew", "he"], + 49: ["Hindi", "hi"], + 50: ["Hungarian", "hu"], + 51: ["Icelandic", "is"], + 52: ["Indonesian", "id"], + 53: ["Interlingua", "ia"], + 54: ["Interlingue", "ie"], + 55: ["Inuktitut", "iu"], # macrolanguage + 56: ["Inupiak", "ik"], # macrolanguage + 57: ["Irish", "ga"], + 58: ["Italian", "it"], + 59: ["Japanese", "ja"], + 60: ["Javanese", "jv"], + 61: ["Kannada", "kn"], + 62: ["Kashmiri", "ks"], + 63: ["Kazakh", "kk"], + 64: ["Kinyarwanda", "rw"], + 65: ["Kirghiz", "ky"], + 66: ["Korean", "ko"], + 67: ["Kurdish", "ku"], # macrolanguage + 68: ["Rundi", "rn"], + 69: ["Lao", "lo"], + 70: ["Latin", "la"], + 71: ["Latvian", "lv"], # macrolanguage + 72: ["Lingala", "ln"], + 73: ["Lithuanian", "lt"], + 74: ["Macedonian", "mk"], + 75: ["Malagasy", "mg"], # macrolanguage + 76: ["Malay", "ms"], # macrolanguage + 77: ["Malayalam", "ml"], + 78: ["Maltese", "mt"], + 79: ["Maori", "mi"], + 80: ["Marathi", "mr"], + 81: ["Marshallese", "mh"], + 82: ["Mongolian", "mn"], # macrolanguage + 83: ["Nauru", "na"], + 84: ["Nepali", "ne"], # macrolanguage + 85: ["Norwegian Bokmal", "nb"], + 86: ["Occitan", "oc"], + 87: ["Oriya", "or"], # macrolanguage + 88: ["Pashto", "ps"], # macrolanguage + 89: ["Persian", "fa"], # macrolanguage + 90: ["Polish", "pl"], + 91: ["Portuguese", "pt"], + 92: ["Punjabi", "pa"], + 93: ["Quechua", "qu"], # macrolanguage + 94: ["Romansh", "rm"], + 95: ["Romanian", "ro"], + 96: ["Russian", "ru"], + 97: ["Samoan", "sm"], + 98: ["Sango", "sg"], + 99: ["Sanskrit", "sa"], + 100: ["Serbian", "sr"], + 101: ["Ossetic", "os"], + 102: ["Southern Sotho", "st"], + 103: ["Tswana", "tn"], + 104: ["Shona", "sn"], + 105: ["Sindhi", "sd"], + 106: ["Sinhala", "si"], + 107: ["Swati", "ss"], + 108: ["Slovak", "sk"], + 109: ["Slovenian", "sl"], + 110: ["Somali", "so"], + 111: ["Spanish", "es"], + 112: ["Sundanese", "su"], + 113: ["Swahili", "sw"], # macrolanguage + 114: ["Swedish", "sv"], + 115: ["Sardinian", "sc"], # macrolanguage + 116: ["Tajik", "tg"], + 117: ["Tamil", "ta"], + 118: ["Tatar", "tt"], + 119: ["Telugu", "te"], + 120: ["Thai", "th"], + 121: ["Tibetan", "bo"], + 122: ["Tigrinya", "ti"], + 123: ["Tongan", "to"], + 124: ["Tsonga", "ts"], + 125: ["Turkish", "tr"], + 126: ["Turkmen", "tk"], + 127: ["Tahitian", "ty"], + 128: ["Uighur", "ug"], + 129: ["Ukrainian", "uk"], + 130: ["Urdu", "ur"], + 131: ["Uzbek", "uz"], # macrolanguage + 132: ["Vietnamese", "vi"], + 133: ["Volapuk", "vo"], + 134: ["Welsh", "cy"], + 135: ["Wolof", "wo"], + 136: ["Xhosa", "xh"], + 137: ["Yiddish", "yi"], # macrolanguage + 138: ["Yoruba", "yo"], + 139: ["Zhuang", "za"], # macrolanguage + 140: ["Zulu", "zu"], + 141: ["Norwegian Nynorsk", "nn"], + 142: ["Bosnian", "bs"], + 143: ["Divehi", "dv"], + 144: ["Manx", "gv"], + 145: ["Cornish", "kw"], + 146: ["Akan", "ak"], # macrolanguage + 147: ["Konkani", "kok"], + 148: ["Ga", "gaa"], + 149: ["Igbo", "ig" ], + 150: ["Kamba", "kam"], + 151: ["Syriac", "syr"], + 152: ["Blin", "byn"], + 153: ["Geez", "gez"], + 154: ["Koro", "kfo"], + 155: ["Sidamo", "sid"], + 156: ["Atsam", "cch"], + 157: ["Tigre", "tig"], + 158: ["Jju", "kaj"], + 159: ["Friulian", "fur"], + 160: ["Venda", "ve" ], + 161: ["Ewe", "ee" ], + 162: ["Walamo", "wal"], + 163: ["Hawaiian", "haw"], + 164: ["Tyap", "kcg"], + 165: ["Nyanja", "ny" ], + 166: ["Filipino", "fil"], + 167: ["Swiss German", "gsw"], + 168: ["Sichuan Yi", "ii" ], + 169: ["Kpelle", "kpe"], + 170: ["Low German", "nds"], + 171: ["South Ndebele", "nr" ], + 172: ["Northern Sotho", "nso"], + 173: ["Northern Sami", "se" ], + 174: ["Taroko", "trv"], + 175: ["Gusii", "guz"], + 176: ["Taita", "dav"], + 177: ["Fulah", "ff"], # macrolanguage + 178: ["Kikuyu", "ki"], + 179: ["Samburu", "saq"], + 180: ["Sena", "seh"], + 181: ["North Ndebele", "nd"], + 182: ["Rombo", "rof"], + 183: ["Tachelhit", "shi"], + 184: ["Kabyle", "kab"], + 185: ["Nyankole", "nyn"], + 186: ["Bena", "bez"], + 187: ["Vunjo", "vun"], + 188: ["Bambara", "bm"], + 189: ["Embu", "ebu"], + 190: ["Cherokee", "chr"], + 191: ["Morisyen", "mfe"], + 192: ["Makonde", "kde"], + 193: ["Langi", "lag"], + 194: ["Ganda", "lg"], + 195: ["Bemba", "bem"], + 196: ["Kabuverdianu", "kea"], + 197: ["Meru", "mer"], + 198: ["Kalenjin", "kln"], + 199: ["Nama", "naq"], + 200: ["Machame", "jmc"], + 201: ["Colognian", "ksh"], + 202: ["Masai", "mas"], + 203: ["Soga", "xog"], + 204: ["Luyia", "luy"], + 205: ["Asu", "asa"], + 206: ["Teso", "teo"], + 207: ["Saho", "ssy"], + 208: ["Koyra Chiini", "khq"], + 209: ["Rwa", "rwk"], + 210: ["Luo", "luo"], + 211: ["Chiga", "cgg"], + 212: ["Central Morocco Tamazight", "tzm"], + 213: ["Koyraboro Senni", "ses"], + 214: ["Shambala", "ksb"], + 215: ["Bodo", "brx"], + 216: ["Avaric", "av"], + 217: ["Chamorro", "ch"], + 218: ["Chechen", "ce"], + 219: ["Church", "cu"], # macrolanguage + 220: ["Chuvash", "cv"], + 221: ["Cree", "cr"], # macrolanguage + 222: ["Haitian", "ht"], + 223: ["Herero", "hz"], + 224: ["Hiri Motu", "ho"], + 225: ["Kanuri", "kr"], # macrolanguage + 226: ["Komi", "kv"], # macrolanguage + 227: ["Kongo", "kg"], # macrolanguage + 228: ["Kwanyama", "kj"], + 229: ["Limburgish", "li"], + 230: ["Luba Katanga", "lu"], + 231: ["Luxembourgish", "lb"], + 232: ["Navaho", "nv"], + 233: ["Ndonga", "ng"], + 234: ["Ojibwa", "oj"], # macrolanguage + 235: ["Pali", "pi"], # macrolanguage + 236: ["Walloon", "wa"], + 237: ["Aghem", "agq"], + 238: ["Basaa", "bas"], + 239: ["Zarma", "dje"], + 240: ["Duala", "dua"], + 241: ["Jola Fonyi", "dyo"], + 242: ["Ewondo", "ewo"], + 243: ["Bafia", "ksf"], + 244: ["Makhuwa Meetto", "mgh"], + 245: ["Mundang", "mua"], + 246: ["Kwasio", "nmg"], + 247: ["Nuer", "nus"], + 248: ["Sakha", "sah"], + 249: ["Sangu", "sbp"], + 250: ["Congo Swahili", "swc"], + 251: ["Tasawaq", "twq"], + 252: ["Vai", "vai"], + 253: ["Walser", "wae"], + 254: ["Yangben", "yav"], + 255: ["Avestan", "ae"], + 256: ["Asturian", "ast"], + 257: ["Ngomba", "jgo"], + 258: ["Kako", "kkj"], + 259: ["Meta", "mgo"], + 260: ["Ngiemboon", "nnh"], + 261: ["Aragonese", "an"], + 262: ["Akkadian", "akk"], + 263: ["Ancient Egyptian", "egy"], + 264: ["Ancient Greek", "grc"], + 265: ["Aramaic", "arc"], + 266: ["Balinese", "ban"], + 267: ["Bamun", "bax"], + 268: ["Batak Toba", "bbc"], + 269: ["Buginese", "bug"], + 270: ["Buhid", "bku"], + 271: ["Carian", "xcr"], + 272: ["Chakma", "ccp"], + 273: ["Classical Mandaic", "myz"], + 274: ["Coptic", "cop"], + 275: ["Dogri", "doi"], # macrolanguage + 276: ["Eastern Cham", "cjm"], + 277: ["Eastern Kayah", "eky"], + 278: ["Etruscan", "ett"], + 279: ["Gothic", "got"], + 280: ["Hanunoo", "hnn"], + 281: ["Ingush", "inh"], + 282: ["Large Flowery Miao", "hmd"], + 283: ["Lepcha", "lep"], + 284: ["Limbu", "lif"], + 285: ["Lisu", "lis"], + 286: ["Lu", "khb"], + 287: ["Lycian", "xlc"], + 288: ["Lydian", "xld"], + 289: ["Mandingo", "man"], # macrolanguage + 290: ["Manipuri", "mni"], + 291: ["Meroitic", "xmr"], + 292: ["Northern Thai", "nod"], + 293: ["Old Irish", "sga"], + 294: ["Old Norse", "non"], + 295: ["Old Persian", "peo"], + 296: ["Old Turkish", "otk"], + 297: ["Pahlavi", "pal"], + 298: ["Parthian", "xpr"], + 299: ["Phoenician", "phn"], + 300: ["Prakrit Language", "pra"], + 301: ["Rejang", "rej"], + 302: ["Sabaean", "xsa"], + 303: ["Samaritan", "smp"], + 304: ["Santali", "sat"], + 305: ["Saurashtra", "saz"], + 306: ["Sora", "srb"], + 307: ["Sylheti", "syl"], + 308: ["Tagbanwa", "tbw"], + 309: ["Tai Dam", "blt"], + 310: ["Tai Nua", "tdd"], + 311: ["Ugaritic", "uga"], + 312: ["Akoose", "bss"], + 313: ["Lakota", "lkt"], + 314: ["Standard Moroccan Tamazight", "zgh"], + 315: ["Mapuche", "arn"], + 316: ["Central Kurdish", "ckb"], + 317: ["Lower Sorbian", "dsb"], + 318: ["Upper Sorbian", "hsb"], + 319: ["Kenyang", "ken"], + 320: ["Mohawk", "moh"], + 321: ["Nko", "nqo"], + 322: ["Prussian", "prg"], + 323: ["Kiche", "quc"], + 324: ["Southern Sami", "sma"], + 325: ["Lule Sami", "smj"], + 326: ["Inari Sami", "smn"], + 327: ["Skolt Sami", "sms"], + 328: ["Warlpiri", "wbp"], + 329: ["Manichaean Middle Persian", "xmn"], + 330: ["Mende", "men"], + 331: ["Ancient North Arabian", "xna"], + 332: ["Linear A", "lab"], + 333: ["Hmong Njua", "hnj"], + 334: ["Ho", "hoc"], + 335: ["Lezghian", "lez"], + 336: ["Bassa", "bsq"], + 337: ["Mono", "mru"], + 338: ["Tedim Chin", "ctd"], + 339: ["Maithili", "mai"], + 340: ["Ahom", "aho"], + 341: ["American Sign Language", "ase"], + 342: ["Ardhamagadhi Prakrit", "pka"], + 343: ["Bhojpuri", "bho"], + 344: ["Hieroglyphic Luwian", "hlu"], + 345: ["Literary Chinese", "lzh"], + 346: ["Mazanderani", "mzn"], + 347: ["Mru", "mro"], + 348: ["Newari", "new"], + 349: ["Northern Luri", "lrc"], + 350: ["Palauan", "pau"], + 351: ["Papiamento", "pap"], + 352: ["Saraiki", "skr"], + 353: ["Tokelau", "tkl"], + 354: ["Tok Pisin", "tpi"], + 355: ["Tuvalu", "tvl"], + 356: ["Uncoded Languages", "mis"], + 357: ["Cantonese", "yue"], + 358: ["Osage", "osa"], + 359: ["Tangut", "txg"], + 360: ["Ido", "io"], + 361: ["Lojban", "jbo"], + 362: ["Sicilian", "scn"], + 363: ["Southern Kurdish", "sdh"], + 364: ["Western Balochi", "bgn"], } language_aliases = { @@ -419,268 +424,268 @@ language_aliases = { } country_list = { - 0 : ["AnyCountry", "ZZ"], - 1 : ["Afghanistan", "AF"], - 2 : ["Albania", "AL"], - 3 : ["Algeria", "DZ"], - 4 : ["American Samoa", "AS"], - 5 : ["Andorra", "AD"], - 6 : ["Angola", "AO"], - 7 : ["Anguilla", "AI"], - 8 : ["Antarctica", "AQ"], - 9 : ["Antigua And Barbuda", "AG"], - 10 : ["Argentina", "AR"], - 11 : ["Armenia", "AM"], - 12 : ["Aruba", "AW"], - 13 : ["Australia", "AU"], - 14 : ["Austria", "AT"], - 15 : ["Azerbaijan", "AZ"], - 16 : ["Bahamas", "BS"], - 17 : ["Bahrain", "BH"], - 18 : ["Bangladesh", "BD"], - 19 : ["Barbados", "BB"], - 20 : ["Belarus", "BY"], - 21 : ["Belgium", "BE"], - 22 : ["Belize", "BZ"], - 23 : ["Benin", "BJ"], - 24 : ["Bermuda", "BM"], - 25 : ["Bhutan", "BT"], - 26 : ["Bolivia", "BO"], - 27 : ["Bosnia And Herzegowina", "BA"], - 28 : ["Botswana", "BW"], - 29 : ["Bouvet Island", "BV"], - 30 : ["Brazil", "BR"], - 31 : ["British Indian Ocean Territory", "IO"], - 32 : ["Brunei", "BN"], - 33 : ["Bulgaria", "BG"], - 34 : ["Burkina Faso", "BF"], - 35 : ["Burundi", "BI"], - 36 : ["Cambodia", "KH"], - 37 : ["Cameroon", "CM"], - 38 : ["Canada", "CA"], - 39 : ["Cape Verde", "CV"], - 40 : ["Cayman Islands", "KY"], - 41 : ["Central African Republic", "CF"], - 42 : ["Chad", "TD"], - 43 : ["Chile", "CL"], - 44 : ["China", "CN"], - 45 : ["Christmas Island", "CX"], - 46 : ["Cocos Islands", "CC"], - 47 : ["Colombia", "CO"], - 48 : ["Comoros", "KM"], - 49 : ["Congo Kinshasa", "CD"], - 50 : ["Congo Brazzaville", "CG"], - 51 : ["Cook Islands", "CK"], - 52 : ["Costa Rica", "CR"], - 53 : ["Ivory Coast", "CI"], - 54 : ["Croatia", "HR"], - 55 : ["Cuba", "CU"], - 56 : ["Cyprus", "CY"], - 57 : ["Czech Republic", "CZ"], - 58 : ["Denmark", "DK"], - 59 : ["Djibouti", "DJ"], - 60 : ["Dominica", "DM"], - 61 : ["Dominican Republic", "DO"], - 62 : ["East Timor", "TL"], - 63 : ["Ecuador", "EC"], - 64 : ["Egypt", "EG"], - 65 : ["El Salvador", "SV"], - 66 : ["Equatorial Guinea", "GQ"], - 67 : ["Eritrea", "ER"], - 68 : ["Estonia", "EE"], - 69 : ["Ethiopia", "ET"], - 70 : ["Falkland Islands", "FK"], - 71 : ["Faroe Islands", "FO"], - 72 : ["Fiji", "FJ"], - 73 : ["Finland", "FI"], - 74 : ["France", "FR"], - 75 : ["Guernsey", "GG"], - 76 : ["French Guiana", "GF"], - 77 : ["French Polynesia", "PF"], - 78 : ["French Southern Territories", "TF"], - 79 : ["Gabon", "GA"], - 80 : ["Gambia", "GM"], - 81 : ["Georgia", "GE"], - 82 : ["Germany", "DE"], - 83 : ["Ghana", "GH"], - 84 : ["Gibraltar", "GI"], - 85 : ["Greece", "GR"], - 86 : ["Greenland", "GL"], - 87 : ["Grenada", "GD"], - 88 : ["Guadeloupe", "GP"], - 89 : ["Guam", "GU"], - 90 : ["Guatemala", "GT"], - 91 : ["Guinea", "GN"], - 92 : ["Guinea Bissau", "GW"], - 93 : ["Guyana", "GY"], - 94 : ["Haiti", "HT"], - 95 : ["Heard And McDonald Islands", "HM"], - 96 : ["Honduras", "HN"], - 97 : ["Hong Kong", "HK"], - 98 : ["Hungary", "HU"], - 99 : ["Iceland", "IS"], - 100 : ["India", "IN"], - 101 : ["Indonesia", "ID"], - 102 : ["Iran", "IR"], - 103 : ["Iraq", "IQ"], - 104 : ["Ireland", "IE"], - 105 : ["Israel", "IL"], - 106 : ["Italy", "IT"], - 107 : ["Jamaica", "JM"], - 108 : ["Japan", "JP"], - 109 : ["Jordan", "JO"], - 110 : ["Kazakhstan", "KZ"], - 111 : ["Kenya", "KE"], - 112 : ["Kiribati", "KI"], - 113 : ["North Korea", "KP"], - 114 : ["South Korea", "KR"], - 115 : ["Kuwait", "KW"], - 116 : ["Kyrgyzstan", "KG"], - 117 : ["Laos", "LA"], - 118 : ["Latvia", "LV"], - 119 : ["Lebanon", "LB"], - 120 : ["Lesotho", "LS"], - 121 : ["Liberia", "LR"], - 122 : ["Libya", "LY"], - 123 : ["Liechtenstein", "LI"], - 124 : ["Lithuania", "LT"], - 125 : ["Luxembourg", "LU"], - 126 : ["Macau", "MO"], - 127 : ["Macedonia", "MK"], - 128 : ["Madagascar", "MG"], - 129 : ["Malawi", "MW"], - 130 : ["Malaysia", "MY"], - 131 : ["Maldives", "MV"], - 132 : ["Mali", "ML"], - 133 : ["Malta", "MT"], - 134 : ["Marshall Islands", "MH"], - 135 : ["Martinique", "MQ"], - 136 : ["Mauritania", "MR"], - 137 : ["Mauritius", "MU"], - 138 : ["Mayotte", "YT"], - 139 : ["Mexico", "MX"], - 140 : ["Micronesia", "FM"], - 141 : ["Moldova", "MD"], - 142 : ["Monaco", "MC"], - 143 : ["Mongolia", "MN"], - 144 : ["Montserrat", "MS"], - 145 : ["Morocco", "MA"], - 146 : ["Mozambique", "MZ"], - 147 : ["Myanmar", "MM"], - 148 : ["Namibia", "NA"], - 149 : ["Nauru", "NR"], - 150 : ["Nepal", "NP"], - 151 : ["Netherlands", "NL"], - 152 : ["Cura Sao", "CW"], - 153 : ["New Caledonia", "NC"], - 154 : ["New Zealand", "NZ"], - 155 : ["Nicaragua", "NI"], - 156 : ["Niger", "NE"], - 157 : ["Nigeria", "NG"], - 158 : ["Niue", "NU"], - 159 : ["Norfolk Island", "NF"], - 160 : ["Northern Mariana Islands", "MP"], - 161 : ["Norway", "NO"], - 162 : ["Oman", "OM"], - 163 : ["Pakistan", "PK"], - 164 : ["Palau", "PW"], - 165 : ["Palestinian Territories", "PS"], - 166 : ["Panama", "PA"], - 167 : ["Papua New Guinea", "PG"], - 168 : ["Paraguay", "PY"], - 169 : ["Peru", "PE"], - 170 : ["Philippines", "PH"], - 171 : ["Pitcairn", "PN"], - 172 : ["Poland", "PL"], - 173 : ["Portugal", "PT"], - 174 : ["Puerto Rico", "PR"], - 175 : ["Qatar", "QA"], - 176 : ["Reunion", "RE"], - 177 : ["Romania", "RO"], - 178 : ["Russia", "RU"], - 179 : ["Rwanda", "RW"], - 180 : ["Saint Kitts And Nevis", "KN"], - 181 : ["Saint Lucia", "LC"], - 182 : ["Saint Vincent And The Grenadines", "VC"], - 183 : ["Samoa", "WS"], - 184 : ["San Marino", "SM"], - 185 : ["Sao Tome And Principe", "ST"], - 186 : ["Saudi Arabia", "SA"], - 187 : ["Senegal", "SN"], - 188 : ["Seychelles", "SC"], - 189 : ["Sierra Leone", "SL"], - 190 : ["Singapore", "SG"], - 191 : ["Slovakia", "SK"], - 192 : ["Slovenia", "SI"], - 193 : ["Solomon Islands", "SB"], - 194 : ["Somalia", "SO"], - 195 : ["South Africa", "ZA"], - 196 : ["South Georgia And The South Sandwich Islands", "GS"], - 197 : ["Spain", "ES"], - 198 : ["Sri Lanka", "LK"], - 199 : ["Saint Helena", "SH"], - 200 : ["Saint Pierre And Miquelon", "PM"], - 201 : ["Sudan", "SD"], - 202 : ["Suriname", "SR"], - 203 : ["Svalbard And Jan Mayen Islands", "SJ"], - 204 : ["Swaziland", "SZ"], - 205 : ["Sweden", "SE"], - 206 : ["Switzerland", "CH"], - 207 : ["Syria", "SY"], - 208 : ["Taiwan", "TW"], - 209 : ["Tajikistan", "TJ"], - 210 : ["Tanzania", "TZ"], - 211 : ["Thailand", "TH"], - 212 : ["Togo", "TG"], - 213 : ["Tokelau", "TK"], - 214 : ["Tonga", "TO"], - 215 : ["Trinidad And Tobago", "TT"], - 216 : ["Tunisia", "TN"], - 217 : ["Turkey", "TR"], - 218 : ["Turkmenistan", "TM"], - 219 : ["Turks And Caicos Islands", "TC"], - 220 : ["Tuvalu", "TV"], - 221 : ["Uganda", "UG"], - 222 : ["Ukraine", "UA"], - 223 : ["United Arab Emirates", "AE"], - 224 : ["United Kingdom", "GB"], - 225 : ["United States", "US"], - 226 : ["United States Minor Outlying Islands", "UM"], - 227 : ["Uruguay", "UY"], - 228 : ["Uzbekistan", "UZ"], - 229 : ["Vanuatu", "VU"], - 230 : ["Vatican City State", "VA"], - 231 : ["Venezuela", "VE"], - 232 : ["Vietnam", "VN"], - 233 : ["British Virgin Islands", "VG"], - 234 : ["United States Virgin Islands", "VI"], - 235 : ["Wallis And Futuna Islands", "WF"], - 236 : ["Western Sahara", "EH"], - 237 : ["Yemen", "YE"], - 238 : ["Canary Islands", "IC"], - 239 : ["Zambia", "ZM"], - 240 : ["Zimbabwe", "ZW"], - 241 : ["Clipperton Island", "CP"], - 242 : ["Montenegro", "ME"], - 243 : ["Serbia", "RS"], - 244 : ["Saint Barthelemy", "BL"], - 245 : ["Saint Martin", "MF"], - 246 : ["Latin America", "419"], - 247 : ["Ascension Island", "AC"], - 248 : ["Aland Islands", "AX"], - 249 : ["Diego Garcia", "DG"], - 250 : ["Ceuta And Melilla", "EA"], - 251 : ["Isle Of Man", "IM"], - 252 : ["Jersey", "JE"], - 253 : ["Tristan Da Cunha", "TA"], - 254 : ["South Sudan", "SS"], - 255 : ["Bonaire", "BQ"], - 256 : ["Sint Maarten", "SX"], - 257 : ["Kosovo", "XK"], - 258 : ["European Union", "EU"], - 259 : ["Outlying Oceania", "QO"], - 260 : ["World", "001"], - 261 : ["Europe", "150"] + 0: ["AnyCountry", "ZZ"], + 1: ["Afghanistan", "AF"], + 2: ["Albania", "AL"], + 3: ["Algeria", "DZ"], + 4: ["American Samoa", "AS"], + 5: ["Andorra", "AD"], + 6: ["Angola", "AO"], + 7: ["Anguilla", "AI"], + 8: ["Antarctica", "AQ"], + 9: ["Antigua And Barbuda", "AG"], + 10: ["Argentina", "AR"], + 11: ["Armenia", "AM"], + 12: ["Aruba", "AW"], + 13: ["Australia", "AU"], + 14: ["Austria", "AT"], + 15: ["Azerbaijan", "AZ"], + 16: ["Bahamas", "BS"], + 17: ["Bahrain", "BH"], + 18: ["Bangladesh", "BD"], + 19: ["Barbados", "BB"], + 20: ["Belarus", "BY"], + 21: ["Belgium", "BE"], + 22: ["Belize", "BZ"], + 23: ["Benin", "BJ"], + 24: ["Bermuda", "BM"], + 25: ["Bhutan", "BT"], + 26: ["Bolivia", "BO"], + 27: ["Bosnia And Herzegowina", "BA"], + 28: ["Botswana", "BW"], + 29: ["Bouvet Island", "BV"], + 30: ["Brazil", "BR"], + 31: ["British Indian Ocean Territory", "IO"], + 32: ["Brunei", "BN"], + 33: ["Bulgaria", "BG"], + 34: ["Burkina Faso", "BF"], + 35: ["Burundi", "BI"], + 36: ["Cambodia", "KH"], + 37: ["Cameroon", "CM"], + 38: ["Canada", "CA"], + 39: ["Cape Verde", "CV"], + 40: ["Cayman Islands", "KY"], + 41: ["Central African Republic", "CF"], + 42: ["Chad", "TD"], + 43: ["Chile", "CL"], + 44: ["China", "CN"], + 45: ["Christmas Island", "CX"], + 46: ["Cocos Islands", "CC"], + 47: ["Colombia", "CO"], + 48: ["Comoros", "KM"], + 49: ["Congo Kinshasa", "CD"], + 50: ["Congo Brazzaville", "CG"], + 51: ["Cook Islands", "CK"], + 52: ["Costa Rica", "CR"], + 53: ["Ivory Coast", "CI"], + 54: ["Croatia", "HR"], + 55: ["Cuba", "CU"], + 56: ["Cyprus", "CY"], + 57: ["Czech Republic", "CZ"], + 58: ["Denmark", "DK"], + 59: ["Djibouti", "DJ"], + 60: ["Dominica", "DM"], + 61: ["Dominican Republic", "DO"], + 62: ["East Timor", "TL"], + 63: ["Ecuador", "EC"], + 64: ["Egypt", "EG"], + 65: ["El Salvador", "SV"], + 66: ["Equatorial Guinea", "GQ"], + 67: ["Eritrea", "ER"], + 68: ["Estonia", "EE"], + 69: ["Ethiopia", "ET"], + 70: ["Falkland Islands", "FK"], + 71: ["Faroe Islands", "FO"], + 72: ["Fiji", "FJ"], + 73: ["Finland", "FI"], + 74: ["France", "FR"], + 75: ["Guernsey", "GG"], + 76: ["French Guiana", "GF"], + 77: ["French Polynesia", "PF"], + 78: ["French Southern Territories", "TF"], + 79: ["Gabon", "GA"], + 80: ["Gambia", "GM"], + 81: ["Georgia", "GE"], + 82: ["Germany", "DE"], + 83: ["Ghana", "GH"], + 84: ["Gibraltar", "GI"], + 85: ["Greece", "GR"], + 86: ["Greenland", "GL"], + 87: ["Grenada", "GD"], + 88: ["Guadeloupe", "GP"], + 89: ["Guam", "GU"], + 90: ["Guatemala", "GT"], + 91: ["Guinea", "GN"], + 92: ["Guinea Bissau", "GW"], + 93: ["Guyana", "GY"], + 94: ["Haiti", "HT"], + 95: ["Heard And McDonald Islands", "HM"], + 96: ["Honduras", "HN"], + 97: ["Hong Kong", "HK"], + 98: ["Hungary", "HU"], + 99: ["Iceland", "IS"], + 100: ["India", "IN"], + 101: ["Indonesia", "ID"], + 102: ["Iran", "IR"], + 103: ["Iraq", "IQ"], + 104: ["Ireland", "IE"], + 105: ["Israel", "IL"], + 106: ["Italy", "IT"], + 107: ["Jamaica", "JM"], + 108: ["Japan", "JP"], + 109: ["Jordan", "JO"], + 110: ["Kazakhstan", "KZ"], + 111: ["Kenya", "KE"], + 112: ["Kiribati", "KI"], + 113: ["North Korea", "KP"], + 114: ["South Korea", "KR"], + 115: ["Kuwait", "KW"], + 116: ["Kyrgyzstan", "KG"], + 117: ["Laos", "LA"], + 118: ["Latvia", "LV"], + 119: ["Lebanon", "LB"], + 120: ["Lesotho", "LS"], + 121: ["Liberia", "LR"], + 122: ["Libya", "LY"], + 123: ["Liechtenstein", "LI"], + 124: ["Lithuania", "LT"], + 125: ["Luxembourg", "LU"], + 126: ["Macau", "MO"], + 127: ["Macedonia", "MK"], + 128: ["Madagascar", "MG"], + 129: ["Malawi", "MW"], + 130: ["Malaysia", "MY"], + 131: ["Maldives", "MV"], + 132: ["Mali", "ML"], + 133: ["Malta", "MT"], + 134: ["Marshall Islands", "MH"], + 135: ["Martinique", "MQ"], + 136: ["Mauritania", "MR"], + 137: ["Mauritius", "MU"], + 138: ["Mayotte", "YT"], + 139: ["Mexico", "MX"], + 140: ["Micronesia", "FM"], + 141: ["Moldova", "MD"], + 142: ["Monaco", "MC"], + 143: ["Mongolia", "MN"], + 144: ["Montserrat", "MS"], + 145: ["Morocco", "MA"], + 146: ["Mozambique", "MZ"], + 147: ["Myanmar", "MM"], + 148: ["Namibia", "NA"], + 149: ["Nauru", "NR"], + 150: ["Nepal", "NP"], + 151: ["Netherlands", "NL"], + 152: ["Cura Sao", "CW"], + 153: ["New Caledonia", "NC"], + 154: ["New Zealand", "NZ"], + 155: ["Nicaragua", "NI"], + 156: ["Niger", "NE"], + 157: ["Nigeria", "NG"], + 158: ["Niue", "NU"], + 159: ["Norfolk Island", "NF"], + 160: ["Northern Mariana Islands", "MP"], + 161: ["Norway", "NO"], + 162: ["Oman", "OM"], + 163: ["Pakistan", "PK"], + 164: ["Palau", "PW"], + 165: ["Palestinian Territories", "PS"], + 166: ["Panama", "PA"], + 167: ["Papua New Guinea", "PG"], + 168: ["Paraguay", "PY"], + 169: ["Peru", "PE"], + 170: ["Philippines", "PH"], + 171: ["Pitcairn", "PN"], + 172: ["Poland", "PL"], + 173: ["Portugal", "PT"], + 174: ["Puerto Rico", "PR"], + 175: ["Qatar", "QA"], + 176: ["Reunion", "RE"], + 177: ["Romania", "RO"], + 178: ["Russia", "RU"], + 179: ["Rwanda", "RW"], + 180: ["Saint Kitts And Nevis", "KN"], + 181: ["Saint Lucia", "LC"], + 182: ["Saint Vincent And The Grenadines", "VC"], + 183: ["Samoa", "WS"], + 184: ["San Marino", "SM"], + 185: ["Sao Tome And Principe", "ST"], + 186: ["Saudi Arabia", "SA"], + 187: ["Senegal", "SN"], + 188: ["Seychelles", "SC"], + 189: ["Sierra Leone", "SL"], + 190: ["Singapore", "SG"], + 191: ["Slovakia", "SK"], + 192: ["Slovenia", "SI"], + 193: ["Solomon Islands", "SB"], + 194: ["Somalia", "SO"], + 195: ["South Africa", "ZA"], + 196: ["South Georgia And The South Sandwich Islands", "GS"], + 197: ["Spain", "ES"], + 198: ["Sri Lanka", "LK"], + 199: ["Saint Helena", "SH"], + 200: ["Saint Pierre And Miquelon", "PM"], + 201: ["Sudan", "SD"], + 202: ["Suriname", "SR"], + 203: ["Svalbard And Jan Mayen Islands", "SJ"], + 204: ["Swaziland", "SZ"], + 205: ["Sweden", "SE"], + 206: ["Switzerland", "CH"], + 207: ["Syria", "SY"], + 208: ["Taiwan", "TW"], + 209: ["Tajikistan", "TJ"], + 210: ["Tanzania", "TZ"], + 211: ["Thailand", "TH"], + 212: ["Togo", "TG"], + 213: ["Tokelau", "TK"], + 214: ["Tonga", "TO"], + 215: ["Trinidad And Tobago", "TT"], + 216: ["Tunisia", "TN"], + 217: ["Turkey", "TR"], + 218: ["Turkmenistan", "TM"], + 219: ["Turks And Caicos Islands", "TC"], + 220: ["Tuvalu", "TV"], + 221: ["Uganda", "UG"], + 222: ["Ukraine", "UA"], + 223: ["United Arab Emirates", "AE"], + 224: ["United Kingdom", "GB"], + 225: ["United States", "US"], + 226: ["United States Minor Outlying Islands", "UM"], + 227: ["Uruguay", "UY"], + 228: ["Uzbekistan", "UZ"], + 229: ["Vanuatu", "VU"], + 230: ["Vatican City State", "VA"], + 231: ["Venezuela", "VE"], + 232: ["Vietnam", "VN"], + 233: ["British Virgin Islands", "VG"], + 234: ["United States Virgin Islands", "VI"], + 235: ["Wallis And Futuna Islands", "WF"], + 236: ["Western Sahara", "EH"], + 237: ["Yemen", "YE"], + 238: ["Canary Islands", "IC"], + 239: ["Zambia", "ZM"], + 240: ["Zimbabwe", "ZW"], + 241: ["Clipperton Island", "CP"], + 242: ["Montenegro", "ME"], + 243: ["Serbia", "RS"], + 244: ["Saint Barthelemy", "BL"], + 245: ["Saint Martin", "MF"], + 246: ["Latin America", "419"], + 247: ["Ascension Island", "AC"], + 248: ["Aland Islands", "AX"], + 249: ["Diego Garcia", "DG"], + 250: ["Ceuta And Melilla", "EA"], + 251: ["Isle Of Man", "IM"], + 252: ["Jersey", "JE"], + 253: ["Tristan Da Cunha", "TA"], + 254: ["South Sudan", "SS"], + 255: ["Bonaire", "BQ"], + 256: ["Sint Maarten", "SX"], + 257: ["Kosovo", "XK"], + 258: ["European Union", "EU"], + 259: ["Outlying Oceania", "QO"], + 260: ["World", "001"], + 261: ["Europe", "150"], } country_aliases = { @@ -698,148 +703,148 @@ country_aliases = { } script_list = { - 0 : ["AnyScript", "Zzzz"], - 1 : ["Arabic", "Arab"], - 2 : ["Cyrillic", "Cyrl"], - 3 : ["Deseret", "Dsrt"], - 4 : ["Gurmukhi", "Guru"], - 5 : ["Simplified Han", "Hans"], - 6 : ["Traditional Han", "Hant"], - 7 : ["Latin", "Latn"], - 8 : ["Mongolian", "Mong"], - 9 : ["Tifinagh", "Tfng"], - 10 : ["Armenian", "Armn"], - 11 : ["Bengali", "Beng"], - 12 : ["Cherokee", "Cher"], - 13 : ["Devanagari", "Deva"], - 14 : ["Ethiopic", "Ethi"], - 15 : ["Georgian", "Geor"], - 16 : ["Greek", "Grek"], - 17 : ["Gujarati", "Gujr"], - 18 : ["Hebrew", "Hebr"], - 19 : ["Japanese", "Jpan"], - 20 : ["Khmer", "Khmr"], - 21 : ["Kannada", "Knda"], - 22 : ["Korean", "Kore"], - 23 : ["Lao", "Laoo"], - 24 : ["Malayalam", "Mlym"], - 25 : ["Myanmar", "Mymr"], - 26 : ["Oriya", "Orya"], - 27 : ["Tamil", "Taml"], - 28 : ["Telugu", "Telu"], - 29 : ["Thaana", "Thaa"], - 30 : ["Thai", "Thai"], - 31 : ["Tibetan", "Tibt"], - 32 : ["Sinhala", "Sinh"], - 33 : ["Syriac", "Syrc"], - 34 : ["Yi", "Yiii"], - 35 : ["Vai", "Vaii"], - 36 : ["Avestan", "Avst"], - 37 : ["Balinese", "Bali"], - 38 : ["Bamum", "Bamu"], - 39 : ["Batak", "Batk"], - 40 : ["Bopomofo", "Bopo"], - 41 : ["Brahmi", "Brah"], - 42 : ["Buginese", "Bugi"], - 43 : ["Buhid", "Buhd"], - 44 : ["Canadian Aboriginal", "Cans"], - 45 : ["Carian", "Cari"], - 46 : ["Chakma", "Cakm"], - 47 : ["Cham", "Cham"], - 48 : ["Coptic", "Copt"], - 49 : ["Cypriot", "Cprt"], - 50 : ["Egyptian Hieroglyphs", "Egyp"], - 51 : ["Fraser", "Lisu"], - 52 : ["Glagolitic", "Glag"], - 53 : ["Gothic", "Goth"], - 54 : ["Han", "Hani"], - 55 : ["Hangul", "Hang"], - 56 : ["Hanunoo", "Hano"], - 57 : ["Imperial Aramaic", "Armi"], - 58 : ["Inscriptional Pahlavi", "Phli"], - 59 : ["Inscriptional Parthian", "Prti"], - 60 : ["Javanese", "Java"], - 61 : ["Kaithi", "Kthi"], - 62 : ["Katakana", "Kana"], - 63 : ["Kayah Li", "Kali"], - 64 : ["Kharoshthi", "Khar"], - 65 : ["Lanna", "Lana"], - 66 : ["Lepcha", "Lepc"], - 67 : ["Limbu", "Limb"], - 68 : ["Linear B", "Linb"], - 69 : ["Lycian", "Lyci"], - 70 : ["Lydian", "Lydi"], - 71 : ["Mandaean", "Mand"], - 72 : ["Meitei Mayek", "Mtei"], - 73 : ["Meroitic", "Mero"], - 74 : ["Meroitic Cursive", "Merc"], - 75 : ["Nko", "Nkoo"], - 76 : ["New Tai Lue", "Talu"], - 77 : ["Ogham", "Ogam"], - 78 : ["Ol Chiki", "Olck"], - 79 : ["Old Italic", "Ital"], - 80 : ["Old Persian", "Xpeo"], - 81 : ["Old South Arabian", "Sarb"], - 82 : ["Orkhon", "Orkh"], - 83 : ["Osmanya", "Osma"], - 84 : ["Phags Pa", "Phag"], - 85 : ["Phoenician", "Phnx"], - 86 : ["Pollard Phonetic", "Plrd"], - 87 : ["Rejang", "Rjng"], - 88 : ["Runic", "Runr"], - 89 : ["Samaritan", "Samr"], - 90 : ["Saurashtra", "Saur"], - 91 : ["Sharada", "Shrd"], - 92 : ["Shavian", "Shaw"], - 93 : ["Sora Sompeng", "Sora"], - 94 : ["Cuneiform", "Xsux"], - 95 : ["Sundanese", "Sund"], - 96 : ["Syloti Nagri", "Sylo"], - 97 : ["Tagalog", "Tglg"], - 98 : ["Tagbanwa", "Tagb"], - 99 : ["Tai Le", "Tale"], - 100 : ["Tai Viet", "Tavt"], - 101 : ["Takri", "Takr"], - 102 : ["Ugaritic", "Ugar"], - 103 : ["Braille", "Brai"], - 104 : ["Hiragana", "Hira"], - 105 : ["Caucasian Albanian", "Aghb"], - 106 : ["Bassa Vah", "Bass"], - 107 : ["Duployan", "Dupl"], - 108 : ["Elbasan", "Elba"], - 109 : ["Grantha", "Gran"], - 110 : ["Pahawh Hmong", "Hmng"], - 111 : ["Khojki", "Khoj"], - 112 : ["Linear A", "Lina"], - 113 : ["Mahajani", "Mahj"], - 114 : ["Manichaean", "Mani"], - 115 : ["Mende Kikakui", "Mend"], - 116 : ["Modi", "Modi"], - 117 : ["Mro", "Mroo"], - 118 : ["Old North Arabian", "Narb"], - 119 : ["Nabataean", "Nbat"], - 120 : ["Palmyrene", "Palm"], - 121 : ["Pau Cin Hau", "Pauc"], - 122 : ["Old Permic", "Perm"], - 123 : ["Psalter Pahlavi", "Phlp"], - 124 : ["Siddham", "Sidd"], - 125 : ["Khudawadi", "Sind"], - 126 : ["Tirhuta", "Tirh"], - 127 : ["Varang Kshiti", "Wara"], - 128 : ["Ahom", "Ahom"], - 129 : ["Anatolian Hieroglyphs", "Hluw"], - 130 : ["Hatran", "Hatr"], - 131 : ["Multani", "Mult"], - 132 : ["Old Hungarian", "Hung"], - 133 : ["Sign Writing", "Sgnw"], - 134 : ["Adlam", "Adlm"], - 135 : ["Bhaiksuki", "Bhks"], - 136 : ["Marchen", "Marc"], - 137 : ["Newa", "Newa"], - 138 : ["Osage", "Osge"], - 139 : ["Tangut", "Tang"], - 140 : ["Han with Bopomofo", "Hanb"], - 141 : ["Jamo", "Jamo"] + 0: ["AnyScript", "Zzzz"], + 1: ["Arabic", "Arab"], + 2: ["Cyrillic", "Cyrl"], + 3: ["Deseret", "Dsrt"], + 4: ["Gurmukhi", "Guru"], + 5: ["Simplified Han", "Hans"], + 6: ["Traditional Han", "Hant"], + 7: ["Latin", "Latn"], + 8: ["Mongolian", "Mong"], + 9: ["Tifinagh", "Tfng"], + 10: ["Armenian", "Armn"], + 11: ["Bengali", "Beng"], + 12: ["Cherokee", "Cher"], + 13: ["Devanagari", "Deva"], + 14: ["Ethiopic", "Ethi"], + 15: ["Georgian", "Geor"], + 16: ["Greek", "Grek"], + 17: ["Gujarati", "Gujr"], + 18: ["Hebrew", "Hebr"], + 19: ["Japanese", "Jpan"], + 20: ["Khmer", "Khmr"], + 21: ["Kannada", "Knda"], + 22: ["Korean", "Kore"], + 23: ["Lao", "Laoo"], + 24: ["Malayalam", "Mlym"], + 25: ["Myanmar", "Mymr"], + 26: ["Oriya", "Orya"], + 27: ["Tamil", "Taml"], + 28: ["Telugu", "Telu"], + 29: ["Thaana", "Thaa"], + 30: ["Thai", "Thai"], + 31: ["Tibetan", "Tibt"], + 32: ["Sinhala", "Sinh"], + 33: ["Syriac", "Syrc"], + 34: ["Yi", "Yiii"], + 35: ["Vai", "Vaii"], + 36: ["Avestan", "Avst"], + 37: ["Balinese", "Bali"], + 38: ["Bamum", "Bamu"], + 39: ["Batak", "Batk"], + 40: ["Bopomofo", "Bopo"], + 41: ["Brahmi", "Brah"], + 42: ["Buginese", "Bugi"], + 43: ["Buhid", "Buhd"], + 44: ["Canadian Aboriginal", "Cans"], + 45: ["Carian", "Cari"], + 46: ["Chakma", "Cakm"], + 47: ["Cham", "Cham"], + 48: ["Coptic", "Copt"], + 49: ["Cypriot", "Cprt"], + 50: ["Egyptian Hieroglyphs", "Egyp"], + 51: ["Fraser", "Lisu"], + 52: ["Glagolitic", "Glag"], + 53: ["Gothic", "Goth"], + 54: ["Han", "Hani"], + 55: ["Hangul", "Hang"], + 56: ["Hanunoo", "Hano"], + 57: ["Imperial Aramaic", "Armi"], + 58: ["Inscriptional Pahlavi", "Phli"], + 59: ["Inscriptional Parthian", "Prti"], + 60: ["Javanese", "Java"], + 61: ["Kaithi", "Kthi"], + 62: ["Katakana", "Kana"], + 63: ["Kayah Li", "Kali"], + 64: ["Kharoshthi", "Khar"], + 65: ["Lanna", "Lana"], + 66: ["Lepcha", "Lepc"], + 67: ["Limbu", "Limb"], + 68: ["Linear B", "Linb"], + 69: ["Lycian", "Lyci"], + 70: ["Lydian", "Lydi"], + 71: ["Mandaean", "Mand"], + 72: ["Meitei Mayek", "Mtei"], + 73: ["Meroitic", "Mero"], + 74: ["Meroitic Cursive", "Merc"], + 75: ["Nko", "Nkoo"], + 76: ["New Tai Lue", "Talu"], + 77: ["Ogham", "Ogam"], + 78: ["Ol Chiki", "Olck"], + 79: ["Old Italic", "Ital"], + 80: ["Old Persian", "Xpeo"], + 81: ["Old South Arabian", "Sarb"], + 82: ["Orkhon", "Orkh"], + 83: ["Osmanya", "Osma"], + 84: ["Phags Pa", "Phag"], + 85: ["Phoenician", "Phnx"], + 86: ["Pollard Phonetic", "Plrd"], + 87: ["Rejang", "Rjng"], + 88: ["Runic", "Runr"], + 89: ["Samaritan", "Samr"], + 90: ["Saurashtra", "Saur"], + 91: ["Sharada", "Shrd"], + 92: ["Shavian", "Shaw"], + 93: ["Sora Sompeng", "Sora"], + 94: ["Cuneiform", "Xsux"], + 95: ["Sundanese", "Sund"], + 96: ["Syloti Nagri", "Sylo"], + 97: ["Tagalog", "Tglg"], + 98: ["Tagbanwa", "Tagb"], + 99: ["Tai Le", "Tale"], + 100: ["Tai Viet", "Tavt"], + 101: ["Takri", "Takr"], + 102: ["Ugaritic", "Ugar"], + 103: ["Braille", "Brai"], + 104: ["Hiragana", "Hira"], + 105: ["Caucasian Albanian", "Aghb"], + 106: ["Bassa Vah", "Bass"], + 107: ["Duployan", "Dupl"], + 108: ["Elbasan", "Elba"], + 109: ["Grantha", "Gran"], + 110: ["Pahawh Hmong", "Hmng"], + 111: ["Khojki", "Khoj"], + 112: ["Linear A", "Lina"], + 113: ["Mahajani", "Mahj"], + 114: ["Manichaean", "Mani"], + 115: ["Mende Kikakui", "Mend"], + 116: ["Modi", "Modi"], + 117: ["Mro", "Mroo"], + 118: ["Old North Arabian", "Narb"], + 119: ["Nabataean", "Nbat"], + 120: ["Palmyrene", "Palm"], + 121: ["Pau Cin Hau", "Pauc"], + 122: ["Old Permic", "Perm"], + 123: ["Psalter Pahlavi", "Phlp"], + 124: ["Siddham", "Sidd"], + 125: ["Khudawadi", "Sind"], + 126: ["Tirhuta", "Tirh"], + 127: ["Varang Kshiti", "Wara"], + 128: ["Ahom", "Ahom"], + 129: ["Anatolian Hieroglyphs", "Hluw"], + 130: ["Hatran", "Hatr"], + 131: ["Multani", "Mult"], + 132: ["Old Hungarian", "Hung"], + 133: ["Sign Writing", "Sgnw"], + 134: ["Adlam", "Adlm"], + 135: ["Bhaiksuki", "Bhks"], + 136: ["Marchen", "Marc"], + 137: ["Newa", "Newa"], + 138: ["Osage", "Osge"], + 139: ["Tangut", "Tang"], + 140: ["Han with Bopomofo", "Hanb"], + 141: ["Jamo", "Jamo"], } script_aliases = { diff --git a/util/local_database/xpathlite.py b/util/local_database/xpathlite.py index a1ed42812d..b3f8325569 100644 --- a/util/local_database/xpathlite.py +++ b/util/local_database/xpathlite.py @@ -213,12 +213,8 @@ def _fixedLookupChain(dirname, name): return items def _findEntry(base, path, draft=None, attribute=None): - file = base if base.endswith(".xml"): - filename = base base = base[:-4] - else: - file = base + ".xml" (dirname, filename) = os.path.split(base) items = _fixedLookupChain(dirname, filename) |