diff options
Diffstat (limited to 'src/corelib/itemmodels')
22 files changed, 2549 insertions, 2469 deletions
diff --git a/src/corelib/itemmodels/itemmodels.pri b/src/corelib/itemmodels/itemmodels.pri deleted file mode 100644 index ebeac6e211..0000000000 --- a/src/corelib/itemmodels/itemmodels.pri +++ /dev/null @@ -1,63 +0,0 @@ -# Qt itemmodels core module - -!qtConfig(itemmodel): return() - -HEADERS += \ - itemmodels/qabstractitemmodel.h \ - itemmodels/qabstractitemmodel_p.h \ - itemmodels/qitemselectionmodel.h \ - itemmodels/qitemselectionmodel_p.h - -SOURCES += \ - itemmodels/qabstractitemmodel.cpp \ - itemmodels/qitemselectionmodel.cpp - -qtConfig(proxymodel) { - HEADERS += \ - itemmodels/qabstractproxymodel.h \ - itemmodels/qabstractproxymodel_p.h - - SOURCES += \ - itemmodels/qabstractproxymodel.cpp - - qtConfig(concatenatetablesproxymodel) { - HEADERS += \ - itemmodels/qconcatenatetablesproxymodel.h - - SOURCES += \ - itemmodels/qconcatenatetablesproxymodel.cpp - } - - qtConfig(identityproxymodel) { - HEADERS += \ - itemmodels/qidentityproxymodel.h - - SOURCES += \ - itemmodels/qidentityproxymodel.cpp - } - - qtConfig(sortfilterproxymodel) { - HEADERS += \ - itemmodels/qsortfilterproxymodel.h - - SOURCES += \ - itemmodels/qsortfilterproxymodel.cpp - } - - qtConfig(transposeproxymodel) { - HEADERS += \ - itemmodels/qtransposeproxymodel.h \ - itemmodels/qtransposeproxymodel_p.h - - SOURCES += \ - itemmodels/qtransposeproxymodel.cpp - } -} - -qtConfig(stringlistmodel) { - HEADERS += \ - itemmodels/qstringlistmodel.h - - SOURCES += \ - itemmodels/qstringlistmodel.cpp -} diff --git a/src/corelib/itemmodels/qabstractitemmodel.cpp b/src/corelib/itemmodels/qabstractitemmodel.cpp index 46ac703615..cd29f2fcc2 100644 --- a/src/corelib/itemmodels/qabstractitemmodel.cpp +++ b/src/corelib/itemmodels/qabstractitemmodel.cpp @@ -1,41 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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$ -** -****************************************************************************/ +// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qabstractitemmodel.h" #include <private/qabstractitemmodel_p.h> @@ -44,26 +9,32 @@ #include <qsize.h> #include <qmimedata.h> #include <qdebug.h> -#include <qvector.h> -#include <qregexp.h> -#include <qregularexpression.h> +#include <qlist.h> +#if QT_CONFIG(regularexpression) +# include <qregularexpression.h> +#endif #include <qstack.h> +#include <qmap.h> #include <qbitarray.h> #include <qdatetime.h> #include <qloggingcategory.h> +#include <functional> + #include <limits.h> QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcCheckIndex, "qt.core.qabstractitemmodel.checkindex") +QT_IMPL_METATYPE_EXTERN(QModelIndexList) + QPersistentModelIndexData *QPersistentModelIndexData::create(const QModelIndex &index) { Q_ASSERT(index.isValid()); // we will _never_ insert an invalid index in the list QPersistentModelIndexData *d = nullptr; QAbstractItemModel *model = const_cast<QAbstractItemModel *>(index.model()); - QHash<QModelIndex, QPersistentModelIndexData *> &indexes = model->d_func()->persistent.indexes; + QMultiHash<QModelIndex, QPersistentModelIndexData *> &indexes = model->d_func()->persistent.indexes; const auto it = indexes.constFind(index); if (it != indexes.cend()) { d = (*it); @@ -90,6 +61,226 @@ void QPersistentModelIndexData::destroy(QPersistentModelIndexData *data) } /*! + \class QModelRoleData + \inmodule QtCore + \since 6.0 + \ingroup model-view + \brief The QModelRoleData class holds a role and the data associated to that role. + + QModelRoleData objects store an item role (which is a value from the + Qt::ItemDataRole enumeration, or an arbitrary integer for a custom role) + as well as the data associated with that role. + + A QModelRoleData object is typically created by views or delegates, + setting which role they want to fetch the data for. The object + is then passed to models (see QAbstractItemModel::multiData()), + which populate the data corresponding to the role stored. Finally, + the view visualizes the data retrieved from the model. + + \sa {Model/View Programming}, QModelRoleDataSpan +*/ + +/*! + \fn QModelRoleData::QModelRoleData(int role) noexcept + + Constructs a QModelRoleData object for the given \a role. + + \sa Qt::ItemDataRole +*/ + +/*! + \fn int QModelRoleData::role() const noexcept + + Returns the role held by this object. + + \sa Qt::ItemDataRole +*/ + +/*! + \fn const QVariant &QModelRoleData::data() const noexcept + + Returns the data held by this object. + + \sa setData() +*/ + +/*! + \fn QVariant &QModelRoleData::data() noexcept + + Returns the data held by this object as a modifiable reference. + + \sa setData() +*/ + +/*! + \fn template <typename T> void QModelRoleData::setData(T &&value) + + Sets the data held by this object to \a value. + \a value must be of a datatype which can be stored in a QVariant. + + \sa data(), clearData(), Q_DECLARE_METATYPE +*/ + +/*! + \fn void QModelRoleData::clearData() noexcept + + Clears the data held by this object. Note that the role is + unchanged; only the data is cleared. + + \sa data() +*/ + +/*! + \class QModelRoleDataSpan + \inmodule QtCore + \since 6.0 + \ingroup model-view + \brief The QModelRoleDataSpan class provides a span over QModelRoleData objects. + + A QModelRoleDataSpan is used as an abstraction over an array of + QModelRoleData objects. + + Like a view, QModelRoleDataSpan provides a small object (pointer + and size) that can be passed to functions that need to examine the + contents of the array. A QModelRoleDataSpan can be constructed from + any array-like sequence (plain arrays, QVector, std::vector, + QVarLengthArray, and so on). Moreover, it does not own the + sequence, which must therefore be kept alive longer than any + QModelRoleDataSpan objects referencing it. + + Unlike a view, QModelRoleDataSpan is a span, so it allows for + modifications to the underlying elements. + + QModelRoleDataSpan's main use case is making it possible + for a model to return the data corresponding to different roles + in one call. + + In order to draw one element from a model, a view (through its + delegates) will generally request multiple roles for the same index + by calling \c{data()} as many times as needed: + + \snippet code/src_corelib_kernel_qabstractitemmodel.cpp 13 + + QModelRoleDataSpan allows a view to request the same data + using just one function call. + + This is achieved by having the view prepare a suitable array of + QModelRoleData objects, each initialized with the role that should + be fetched. The array is then wrapped in a QModelRoleDataSpan + object, which is then passed to a model's \c{multiData()} function. + + \snippet code/src_corelib_kernel_qabstractitemmodel.cpp 14 + + Views are encouraged to store the array of QModelRoleData objects + (and, possibly, the corresponding span) and re-use it in subsequent + calls to the model. This allows to reduce the memory allocations + related with creating and returning QVariant objects. + + Finally, given a QModelRoleDataSpan object, the model's + responsibility is to fill in the data corresponding to each role in + the span. How this is done depends on the concrete model class. + Here's a sketch of a possible implementation that iterates over the + span and uses \c{setData()} on each element: + + \snippet code/src_corelib_kernel_qabstractitemmodel.cpp 15 + + \sa {Model/View Programming}, QAbstractItemModel::multiData() +*/ + +/*! + \fn QModelRoleDataSpan::QModelRoleDataSpan() noexcept + + Constructs an empty QModelRoleDataSpan. Its data() will be set to + \nullptr, and its length to zero. +*/ + +/*! + \fn QModelRoleDataSpan::QModelRoleDataSpan(QModelRoleData &modelRoleData) noexcept + + Constructs an QModelRoleDataSpan spanning over \a modelRoleData, + seen as a 1-element array. +*/ + +/*! + \fn QModelRoleDataSpan::QModelRoleDataSpan(QModelRoleData *modelRoleData, qsizetype len) + + Constructs an QModelRoleDataSpan spanning over the array beginning + at \a modelRoleData and with length \a len. + + \note The array must be kept alive as long as this object has not + been destructed. +*/ + +/*! + \fn template <typename Container, QModelRoleDataSpan::if_compatible_container<Container> = true> QModelRoleDataSpan::QModelRoleDataSpan(Container &c) noexcept + + Constructs an QModelRoleDataSpan spanning over the container \a c, + which can be any contiguous container of QModelRoleData objects. + For instance, it can be a \c{QVector<QModelRoleData>}, + a \c{std::array<QModelRoleData, 10>} and so on. + + \note The container must be kept alive as long as this object has not + been destructed. +*/ + +/*! + \fn qsizetype QModelRoleDataSpan::size() const noexcept + + Returns the length of the span represented by this object. +*/ + +/*! + \fn qsizetype QModelRoleDataSpan::length() const noexcept + + Returns the length of the span represented by this object. +*/ + +/*! + \fn QModelRoleData *QModelRoleDataSpan::data() const noexcept + + Returns a pointer to the beginning of the span represented by this + object. +*/ + +/*! + \fn QModelRoleData *QModelRoleDataSpan::begin() const noexcept + + Returns a pointer to the beginning of the span represented by this + object. +*/ + +/*! + \fn QModelRoleData *QModelRoleDataSpan::end() const noexcept + + Returns a pointer to the imaginary element one past the end of the + span represented by this object. +*/ + +/*! + \fn QModelRoleData &QModelRoleDataSpan::operator[](qsizetype index) const + + Returns a modifiable reference to the QModelRoleData at position + \a index in the span. + + \note \a index must be a valid index for this span (0 <= \a index < size()). +*/ + +/*! + \fn const QVariant *QModelRoleDataSpan::dataForRole(int role) const + + Returns the data associated with the first QModelRoleData in the + span that has its role equal to \a role. If such a QModelRoleData + object does not exist, the behavior is undefined. + + \note Avoid calling this function from the model's side, as a + model cannot possibly know in advance which roles are in a given + QModelRoleDataSpan. This function is instead suitable for views and + delegates, which have control over the roles in the span. + + \sa QModelRoleData::data() +*/ + +/*! \class QPersistentModelIndex \inmodule QtCore \ingroup shared @@ -108,6 +299,9 @@ void QPersistentModelIndexData::destroy(QPersistentModelIndexData *data) It is good practice to check that persistent model indexes are valid before using them. + \note You cannot store a QStandardItemModel's QPersistentModelIndex + in one of the model's items. + \sa {Model/View Programming}, QModelIndex, QAbstractItemModel */ @@ -188,7 +382,7 @@ QPersistentModelIndex::~QPersistentModelIndex() model index are used when comparing with another persistent model index. */ -bool QPersistentModelIndex::operator==(const QPersistentModelIndex &other) const +bool QPersistentModelIndex::operator==(const QPersistentModelIndex &other) const noexcept { if (d && other.d) return d->index == other.d->index; @@ -205,12 +399,12 @@ bool QPersistentModelIndex::operator==(const QPersistentModelIndex &other) const model index are used when comparing with another persistent model index. */ -bool QPersistentModelIndex::operator<(const QPersistentModelIndex &other) const +bool QPersistentModelIndex::operator<(const QPersistentModelIndex &other) const noexcept { if (d && other.d) return d->index < other.d->index; - return d < other.d; + return std::less<>{}(d, other.d); } /*! @@ -263,17 +457,16 @@ QPersistentModelIndex &QPersistentModelIndex::operator=(const QModelIndex &other } /*! - \fn QPersistentModelIndex::operator const QModelIndex&() const + \fn QPersistentModelIndex::operator QModelIndex() const - Cast operator that returns a const QModelIndex&. + Cast operator that returns a QModelIndex. */ -QPersistentModelIndex::operator const QModelIndex&() const +QPersistentModelIndex::operator QModelIndex() const { - static const QModelIndex invalid; if (d) return d->index; - return invalid; + return QModelIndex(); } /*! @@ -284,7 +477,7 @@ QPersistentModelIndex::operator const QModelIndex&() const model index are used when comparing with another model index. */ -bool QPersistentModelIndex::operator==(const QModelIndex &other) const +bool QPersistentModelIndex::operator==(const QModelIndex &other) const noexcept { if (d) return d->index == other; @@ -298,7 +491,7 @@ bool QPersistentModelIndex::operator==(const QModelIndex &other) const location as the \a other model index; otherwise returns \c{false}. */ -bool QPersistentModelIndex::operator!=(const QModelIndex &other) const +bool QPersistentModelIndex::operator!=(const QModelIndex &other) const noexcept { if (d) return d->index != other; @@ -348,6 +541,22 @@ void *QPersistentModelIndex::internalPointer() const } /*! + \fn const void *QPersistentModelIndex::constInternalPointer() const + \since 6.0 + \internal + + Returns a \c{const void} \c{*} pointer used by the model to + associate the index with the internal data structure. +*/ + +const void *QPersistentModelIndex::constInternalPointer() const +{ + if (d) + return d->index.constInternalPointer(); + return nullptr; +} + +/*! \fn quintptr QPersistentModelIndex::internalId() const \internal @@ -390,37 +599,31 @@ QModelIndex QPersistentModelIndex::sibling(int row, int column) const return QModelIndex(); } -#if QT_DEPRECATED_SINCE(5, 8) /*! - \obsolete - - Use QAbstractItemModel::index() instead. - - Returns the child of the model index that is stored in the given \a row - and \a column. + Returns the data for the given \a role for the item referred to by the + index. - \sa parent(), sibling() + \sa Qt::ItemDataRole, QAbstractItemModel::setData() */ - -QModelIndex QPersistentModelIndex::child(int row, int column) const +QVariant QPersistentModelIndex::data(int role) const { if (d) - return d->index.model()->index(row, column, d->index); - return QModelIndex(); + return d->index.data(role); + return QVariant(); } -#endif + /*! - Returns the data for the given \a role for the item referred to by the + Populates the given \a roleDataSpan for the item referred to by the index. + \since 6.0 \sa Qt::ItemDataRole, QAbstractItemModel::setData() */ -QVariant QPersistentModelIndex::data(int role) const +void QPersistentModelIndex::multiData(QModelRoleDataSpan roleDataSpan) const { if (d) - return d->index.data(role); - return QVariant(); + d->index.multiData(roleDataSpan); } /*! @@ -483,6 +686,7 @@ QDebug operator<<(QDebug dbg, const QPersistentModelIndex &idx) class QEmptyItemModel : public QAbstractItemModel { + Q_OBJECT public: explicit QEmptyItemModel(QObject *parent = nullptr) : QAbstractItemModel(parent) {} QModelIndex index(int, int, const QModelIndex &) const override { return QModelIndex(); } @@ -497,9 +701,7 @@ Q_GLOBAL_STATIC(QEmptyItemModel, qEmptyModel) QAbstractItemModelPrivate::QAbstractItemModelPrivate() - : QObjectPrivate(), - supportedDragActions(-1), - roleNames(defaultRoleNames()) + : QObjectPrivate() { } @@ -514,7 +716,7 @@ QAbstractItemModel *QAbstractItemModelPrivate::staticEmptyModel() void QAbstractItemModelPrivate::invalidatePersistentIndexes() { - for (QPersistentModelIndexData *data : qAsConst(persistent.indexes)) + for (QPersistentModelIndexData *data : std::as_const(persistent.indexes)) data->index = QModelIndex(); persistent.indexes.clear(); } @@ -534,7 +736,7 @@ void QAbstractItemModelPrivate::invalidatePersistentIndex(const QModelIndex &ind } using DefaultRoleNames = QHash<int, QByteArray>; -Q_GLOBAL_STATIC_WITH_ARGS(DefaultRoleNames, qDefaultRoleNames, ( +Q_GLOBAL_STATIC(DefaultRoleNames, qDefaultRoleNames, { { Qt::DisplayRole, "display" }, { Qt::DecorationRole, "decoration" }, @@ -542,7 +744,7 @@ Q_GLOBAL_STATIC_WITH_ARGS(DefaultRoleNames, qDefaultRoleNames, ( { Qt::ToolTipRole, "toolTip" }, { Qt::StatusTipRole, "statusTip" }, { Qt::WhatsThisRole, "whatsThis" }, - })) + }) const QHash<int,QByteArray> &QAbstractItemModelPrivate::defaultRoleNames() { @@ -552,32 +754,32 @@ const QHash<int,QByteArray> &QAbstractItemModelPrivate::defaultRoleNames() bool QAbstractItemModelPrivate::isVariantLessThan(const QVariant &left, const QVariant &right, Qt::CaseSensitivity cs, bool isLocaleAware) { - if (left.userType() == QVariant::Invalid) + if (left.userType() == QMetaType::UnknownType) return false; - if (right.userType() == QVariant::Invalid) + if (right.userType() == QMetaType::UnknownType) return true; switch (left.userType()) { - case QVariant::Int: + case QMetaType::Int: return left.toInt() < right.toInt(); - case QVariant::UInt: + case QMetaType::UInt: return left.toUInt() < right.toUInt(); - case QVariant::LongLong: + case QMetaType::LongLong: return left.toLongLong() < right.toLongLong(); - case QVariant::ULongLong: + case QMetaType::ULongLong: return left.toULongLong() < right.toULongLong(); case QMetaType::Float: return left.toFloat() < right.toFloat(); - case QVariant::Double: + case QMetaType::Double: return left.toDouble() < right.toDouble(); - case QVariant::Char: + case QMetaType::QChar: return left.toChar() < right.toChar(); - case QVariant::Date: + case QMetaType::QDate: return left.toDate() < right.toDate(); - case QVariant::Time: + case QMetaType::QTime: return left.toTime() < right.toTime(); - case QVariant::DateTime: + case QMetaType::QDateTime: return left.toDateTime() < right.toDateTime(); - case QVariant::String: + case QMetaType::QString: default: if (isLocaleAware) return left.toString().localeAwareCompare(right.toString()) < 0; @@ -591,19 +793,19 @@ static uint typeOfVariant(const QVariant &value) { //return 0 for integer, 1 for floating point and 2 for other switch (value.userType()) { - case QVariant::Bool: - case QVariant::Int: - case QVariant::UInt: - case QVariant::LongLong: - case QVariant::ULongLong: - case QVariant::Char: + case QMetaType::Bool: + case QMetaType::Int: + case QMetaType::UInt: + case QMetaType::LongLong: + case QMetaType::ULongLong: + case QMetaType::QChar: case QMetaType::Short: case QMetaType::UShort: case QMetaType::UChar: case QMetaType::ULong: case QMetaType::Long: return 0; - case QVariant::Double: + case QMetaType::Double: case QMetaType::Float: return 1; default: @@ -641,13 +843,13 @@ void QAbstractItemModelPrivate::removePersistentIndexData(QPersistentModelIndexD Q_UNUSED(removed); } // make sure our optimization still works - for (int i = persistent.moved.count() - 1; i >= 0; --i) { + for (int i = persistent.moved.size() - 1; i >= 0; --i) { int idx = persistent.moved.at(i).indexOf(data); if (idx >= 0) persistent.moved[i].remove(idx); } // update the references to invalidated persistent indexes - for (int i = persistent.invalidated.count() - 1; i >= 0; --i) { + for (int i = persistent.invalidated.size() - 1; i >= 0; --i) { int idx = persistent.invalidated.at(i).indexOf(data); if (idx >= 0) persistent.invalidated[i].remove(idx); @@ -660,11 +862,9 @@ void QAbstractItemModelPrivate::rowsAboutToBeInserted(const QModelIndex &parent, { Q_Q(QAbstractItemModel); Q_UNUSED(last); - QVector<QPersistentModelIndexData *> persistent_moved; + QList<QPersistentModelIndexData *> persistent_moved; if (first < q->rowCount(parent)) { - for (QHash<QModelIndex, QPersistentModelIndexData *>::const_iterator it = persistent.indexes.constBegin(); - it != persistent.indexes.constEnd(); ++it) { - QPersistentModelIndexData *data = *it; + for (auto *data : std::as_const(persistent.indexes)) { const QModelIndex &index = data->index; if (index.row() >= first && index.isValid() && index.parent() == parent) { persistent_moved.append(data); @@ -677,11 +877,9 @@ void QAbstractItemModelPrivate::rowsAboutToBeInserted(const QModelIndex &parent, void QAbstractItemModelPrivate::rowsInserted(const QModelIndex &parent, int first, int last) { - QVector<QPersistentModelIndexData *> persistent_moved = persistent.moved.pop(); - int count = (last - first) + 1; // it is important to only use the delta, because the change could be nested - for (QVector<QPersistentModelIndexData *>::const_iterator it = persistent_moved.constBegin(); - it != persistent_moved.constEnd(); ++it) { - QPersistentModelIndexData *data = *it; + const QList<QPersistentModelIndexData *> persistent_moved = persistent.moved.pop(); + const int count = (last - first) + 1; // it is important to only use the delta, because the change could be nested + for (auto *data : persistent_moved) { QModelIndex old = data->index; persistent.indexes.erase(persistent.indexes.constFind(old)); data->index = q_func()->index(old.row() + count, old.column(), parent); @@ -695,19 +893,14 @@ void QAbstractItemModelPrivate::rowsInserted(const QModelIndex &parent, void QAbstractItemModelPrivate::itemsAboutToBeMoved(const QModelIndex &srcParent, int srcFirst, int srcLast, const QModelIndex &destinationParent, int destinationChild, Qt::Orientation orientation) { - QVector<QPersistentModelIndexData *> persistent_moved_explicitly; - QVector<QPersistentModelIndexData *> persistent_moved_in_source; - QVector<QPersistentModelIndexData *> persistent_moved_in_destination; - - QHash<QModelIndex, QPersistentModelIndexData *>::const_iterator it; - const QHash<QModelIndex, QPersistentModelIndexData *>::const_iterator begin = persistent.indexes.constBegin(); - const QHash<QModelIndex, QPersistentModelIndexData *>::const_iterator end = persistent.indexes.constEnd(); + QList<QPersistentModelIndexData *> persistent_moved_explicitly; + QList<QPersistentModelIndexData *> persistent_moved_in_source; + QList<QPersistentModelIndexData *> persistent_moved_in_destination; const bool sameParent = (srcParent == destinationParent); const bool movingUp = (srcFirst > destinationChild); - for ( it = begin; it != end; ++it) { - QPersistentModelIndexData *data = *it; + for (auto *data : std::as_const(persistent.indexes)) { const QModelIndex &index = data->index; const QModelIndex &parent = index.parent(); const bool isSourceIndex = (parent == srcParent); @@ -758,16 +951,10 @@ void QAbstractItemModelPrivate::itemsAboutToBeMoved(const QModelIndex &srcParent column value depending on the value of \a orientation. The indexes may also be moved to a different parent if \a parent differs from the existing parent for the index. */ -void QAbstractItemModelPrivate::movePersistentIndexes(const QVector<QPersistentModelIndexData *> &indexes, int change, const QModelIndex &parent, Qt::Orientation orientation) +void QAbstractItemModelPrivate::movePersistentIndexes(const QList<QPersistentModelIndexData *> &indexes, int change, + const QModelIndex &parent, Qt::Orientation orientation) { - QVector<QPersistentModelIndexData *>::const_iterator it; - const QVector<QPersistentModelIndexData *>::const_iterator begin = indexes.constBegin(); - const QVector<QPersistentModelIndexData *>::const_iterator end = indexes.constEnd(); - - for (it = begin; it != end; ++it) - { - QPersistentModelIndexData *data = *it; - + for (auto *data : indexes) { int row = data->index.row(); int column = data->index.column(); @@ -788,9 +975,9 @@ void QAbstractItemModelPrivate::movePersistentIndexes(const QVector<QPersistentM void QAbstractItemModelPrivate::itemsMoved(const QModelIndex &sourceParent, int sourceFirst, int sourceLast, const QModelIndex &destinationParent, int destinationChild, Qt::Orientation orientation) { - QVector<QPersistentModelIndexData *> moved_in_destination = persistent.moved.pop(); - QVector<QPersistentModelIndexData *> moved_in_source = persistent.moved.pop(); - QVector<QPersistentModelIndexData *> moved_explicitly = persistent.moved.pop(); + const QList<QPersistentModelIndexData *> moved_in_destination = persistent.moved.pop(); + const QList<QPersistentModelIndexData *> moved_in_source = persistent.moved.pop(); + const QList<QPersistentModelIndexData *> moved_explicitly = persistent.moved.pop(); const bool sameParent = (sourceParent == destinationParent); const bool movingUp = (sourceFirst > destinationChild); @@ -807,13 +994,11 @@ void QAbstractItemModelPrivate::itemsMoved(const QModelIndex &sourceParent, int void QAbstractItemModelPrivate::rowsAboutToBeRemoved(const QModelIndex &parent, int first, int last) { - QVector<QPersistentModelIndexData *> persistent_moved; - QVector<QPersistentModelIndexData *> persistent_invalidated; + QList<QPersistentModelIndexData *> persistent_moved; + QList<QPersistentModelIndexData *> persistent_invalidated; // find the persistent indexes that are affected by the change, either by being in the removed subtree // or by being on the same level and below the removed rows - for (QHash<QModelIndex, QPersistentModelIndexData *>::const_iterator it = persistent.indexes.constBegin(); - it != persistent.indexes.constEnd(); ++it) { - QPersistentModelIndexData *data = *it; + for (auto *data : std::as_const(persistent.indexes)) { bool level_changed = false; QModelIndex current = data->index; while (current.isValid()) { @@ -837,11 +1022,9 @@ void QAbstractItemModelPrivate::rowsAboutToBeRemoved(const QModelIndex &parent, void QAbstractItemModelPrivate::rowsRemoved(const QModelIndex &parent, int first, int last) { - QVector<QPersistentModelIndexData *> persistent_moved = persistent.moved.pop(); - int count = (last - first) + 1; // it is important to only use the delta, because the change could be nested - for (QVector<QPersistentModelIndexData *>::const_iterator it = persistent_moved.constBegin(); - it != persistent_moved.constEnd(); ++it) { - QPersistentModelIndexData *data = *it; + const QList<QPersistentModelIndexData *> persistent_moved = persistent.moved.pop(); + const int count = (last - first) + 1; // it is important to only use the delta, because the change could be nested + for (auto *data : persistent_moved) { QModelIndex old = data->index; persistent.indexes.erase(persistent.indexes.constFind(old)); data->index = q_func()->index(old.row() - count, old.column(), parent); @@ -851,11 +1034,11 @@ void QAbstractItemModelPrivate::rowsRemoved(const QModelIndex &parent, qWarning() << "QAbstractItemModel::endRemoveRows: Invalid index (" << old.row() - count << ',' << old.column() << ") in model" << q_func(); } } - QVector<QPersistentModelIndexData *> persistent_invalidated = persistent.invalidated.pop(); - for (QVector<QPersistentModelIndexData *>::const_iterator it = persistent_invalidated.constBegin(); - it != persistent_invalidated.constEnd(); ++it) { - QPersistentModelIndexData *data = *it; - persistent.indexes.erase(persistent.indexes.constFind(data->index)); + const QList<QPersistentModelIndexData *> persistent_invalidated = persistent.invalidated.pop(); + for (auto *data : persistent_invalidated) { + auto pit = persistent.indexes.constFind(data->index); + if (pit != persistent.indexes.cend()) + persistent.indexes.erase(pit); data->index = QModelIndex(); } } @@ -865,11 +1048,9 @@ void QAbstractItemModelPrivate::columnsAboutToBeInserted(const QModelIndex &pare { Q_Q(QAbstractItemModel); Q_UNUSED(last); - QVector<QPersistentModelIndexData *> persistent_moved; + QList<QPersistentModelIndexData *> persistent_moved; if (first < q->columnCount(parent)) { - for (QHash<QModelIndex, QPersistentModelIndexData *>::const_iterator it = persistent.indexes.constBegin(); - it != persistent.indexes.constEnd(); ++it) { - QPersistentModelIndexData *data = *it; + for (auto *data : std::as_const(persistent.indexes)) { const QModelIndex &index = data->index; if (index.column() >= first && index.isValid() && index.parent() == parent) persistent_moved.append(data); @@ -881,11 +1062,9 @@ void QAbstractItemModelPrivate::columnsAboutToBeInserted(const QModelIndex &pare void QAbstractItemModelPrivate::columnsInserted(const QModelIndex &parent, int first, int last) { - QVector<QPersistentModelIndexData *> persistent_moved = persistent.moved.pop(); - int count = (last - first) + 1; // it is important to only use the delta, because the change could be nested - for (QVector<QPersistentModelIndexData *>::const_iterator it = persistent_moved.constBegin(); - it != persistent_moved.constEnd(); ++it) { - QPersistentModelIndexData *data = *it; + const QList<QPersistentModelIndexData *> persistent_moved = persistent.moved.pop(); + const int count = (last - first) + 1; // it is important to only use the delta, because the change could be nested + for (auto *data : persistent_moved) { QModelIndex old = data->index; persistent.indexes.erase(persistent.indexes.constFind(old)); data->index = q_func()->index(old.row(), old.column() + count, parent); @@ -894,19 +1073,17 @@ void QAbstractItemModelPrivate::columnsInserted(const QModelIndex &parent, } else { qWarning() << "QAbstractItemModel::endInsertColumns: Invalid index (" << old.row() << ',' << old.column() + count << ") in model" << q_func(); } - } + } } void QAbstractItemModelPrivate::columnsAboutToBeRemoved(const QModelIndex &parent, int first, int last) { - QVector<QPersistentModelIndexData *> persistent_moved; - QVector<QPersistentModelIndexData *> persistent_invalidated; + QList<QPersistentModelIndexData *> persistent_moved; + QList<QPersistentModelIndexData *> persistent_invalidated; // find the persistent indexes that are affected by the change, either by being in the removed subtree // or by being on the same level and to the right of the removed columns - for (QHash<QModelIndex, QPersistentModelIndexData *>::const_iterator it = persistent.indexes.constBegin(); - it != persistent.indexes.constEnd(); ++it) { - QPersistentModelIndexData *data = *it; + for (auto *data : std::as_const(persistent.indexes)) { bool level_changed = false; QModelIndex current = data->index; while (current.isValid()) { @@ -931,11 +1108,9 @@ void QAbstractItemModelPrivate::columnsAboutToBeRemoved(const QModelIndex &paren void QAbstractItemModelPrivate::columnsRemoved(const QModelIndex &parent, int first, int last) { - QVector<QPersistentModelIndexData *> persistent_moved = persistent.moved.pop(); - int count = (last - first) + 1; // it is important to only use the delta, because the change could be nested - for (QVector<QPersistentModelIndexData *>::const_iterator it = persistent_moved.constBegin(); - it != persistent_moved.constEnd(); ++it) { - QPersistentModelIndexData *data = *it; + const QList<QPersistentModelIndexData *> persistent_moved = persistent.moved.pop(); + const int count = (last - first) + 1; // it is important to only use the delta, because the change could be nested + for (auto *data : persistent_moved) { QModelIndex old = data->index; persistent.indexes.erase(persistent.indexes.constFind(old)); data->index = q_func()->index(old.row(), old.column() - count, parent); @@ -945,11 +1120,11 @@ void QAbstractItemModelPrivate::columnsRemoved(const QModelIndex &parent, qWarning() << "QAbstractItemModel::endRemoveColumns: Invalid index (" << old.row() << ',' << old.column() - count << ") in model" << q_func(); } } - QVector<QPersistentModelIndexData *> persistent_invalidated = persistent.invalidated.pop(); - for (QVector<QPersistentModelIndexData *>::const_iterator it = persistent_invalidated.constBegin(); - it != persistent_invalidated.constEnd(); ++it) { - QPersistentModelIndexData *data = *it; - persistent.indexes.erase(persistent.indexes.constFind(data->index)); + const QList<QPersistentModelIndexData *> persistent_invalidated = persistent.invalidated.pop(); + for (auto *data : persistent_invalidated) { + auto index = persistent.indexes.constFind(data->index); + if (index != persistent.indexes.constEnd()) + persistent.indexes.erase(index); data->index = QModelIndex(); } } @@ -1062,6 +1237,15 @@ void QAbstractItemModel::resetInternalData() */ /*! + \fn const void *QModelIndex::constInternalPointer() const + + Returns a \c{const void} \c{*} pointer used by the model to associate + the index with the internal data structure. + + \sa QAbstractItemModel::createIndex() +*/ + +/*! \fn quintptr QModelIndex::internalId() const Returns a \c{quintptr} used by the model to associate @@ -1122,25 +1306,17 @@ void QAbstractItemModel::resetInternalData() */ /*! - \fn QModelIndex QModelIndex::child(int row, int column) const - - \obsolete - - Use QAbstractItemModel::index() instead. - - Returns the child of the model index that is stored in the given \a row and - \a column. - - \note This function does not work for an invalid model index which is often - used as the root index. + \fn QVariant QModelIndex::data(int role) const - \sa parent(), sibling() + Returns the data for the given \a role for the item referred to by the + index. */ /*! - \fn QVariant QModelIndex::data(int role) const + \fn void QModelIndex::multiData(QModelRoleDataSpan roleDataSpan) const + \since 6.0 - Returns the data for the given \a role for the item referred to by the + Populates the given \a roleDataSpan for the item referred to by the index. */ @@ -1324,10 +1500,12 @@ void QAbstractItemModel::resetInternalData() rows to the model, \l{QAbstractItemModel::}{beginInsertRows()} and \l{QAbstractItemModel::}{endInsertRows()} must be called. + \include models.qdocinc {thread-safety-section1}{QAbstractItemModel} + \sa {Model Classes}, {Model Subclassing Reference}, QModelIndex, QAbstractItemView, {Using drag and drop with item views}, - {Simple DOM Model Example}, {Simple Tree Model Example}, - {Editable Tree Model Example}, {Fetch More Example} + {Simple Tree Model Example}, {Editable Tree Model Example}, + {Fetch More Example} */ /*! @@ -1566,7 +1744,13 @@ QAbstractItemModel::~QAbstractItemModel() For example: - \snippet ../widgets/itemviews/simpledommodel/dommodel.cpp 2 + \code + int MyModel::columnCount(const QModelIndex &parent) const + { + Q_UNUSED(parent); + return 3; + } + \endcode \note When implementing a table based model, columnCount() should return 0 when the parent is valid. @@ -1575,7 +1759,7 @@ QAbstractItemModel::~QAbstractItemModel() */ /*! - \fn void QAbstractItemModel::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles = QVector<int>()) + \fn void QAbstractItemModel::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList<int> &roles = QList<int>()) This signal is emitted whenever the data in an existing item changes. @@ -1651,13 +1835,13 @@ QAbstractItemModel::~QAbstractItemModel() */ /*! - \fn void QAbstractItemModel::rowsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row) + \fn void QAbstractItemModel::rowsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow) \since 4.6 This signal is emitted after rows have been moved within the - model. The items between \a start and \a end - inclusive, under the given \a parent item have been moved to \a destination - starting at the row \a row. + model. The items between \a sourceStart and \a sourceEnd + inclusive, under the given \a sourceParent item have been moved to \a destinationParent + starting at the row \a destinationRow. \b{Note:} Components connected to this signal use it to adapt to changes in the model's dimensions. It can only be emitted by the QAbstractItemModel @@ -1683,13 +1867,13 @@ QAbstractItemModel::~QAbstractItemModel() */ /*! - \fn void QAbstractItemModel::columnsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int column) + \fn void QAbstractItemModel::columnsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationColumn) \since 4.6 This signal is emitted after columns have been moved within the - model. The items between \a start and \a end - inclusive, under the given \a parent item have been moved to \a destination - starting at the column \a column. + model. The items between \a sourceStart and \a sourceEnd + inclusive, under the given \a sourceParent item have been moved to \a destinationParent + starting at the column \a destinationColumn. \b{Note:} Components connected to this signal use it to adapt to changes in the model's dimensions. It can only be emitted by the QAbstractItemModel @@ -1858,7 +2042,6 @@ bool QAbstractItemModel::setData(const QModelIndex &index, const QVariant &value return false; } -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) /*! \since 6.0 Removes the data stored in all the roles for the given \a index. @@ -1873,7 +2056,6 @@ bool QAbstractItemModel::clearItemData(const QModelIndex &index) Q_UNUSED(index); return false; } -#endif /*! \fn QVariant QAbstractItemModel::data(const QModelIndex &index, int role) const = 0 @@ -1882,7 +2064,7 @@ bool QAbstractItemModel::clearItemData(const QModelIndex &index) by the \a index. \note If you do not have a value to return, return an \b invalid - QVariant instead of returning 0. + (default-constructed) QVariant. \sa Qt::ItemDataRole, setData(), headerData() */ @@ -1899,7 +2081,7 @@ bool QAbstractItemModel::clearItemData(const QModelIndex &index) */ bool QAbstractItemModel::setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles) { - // ### Qt 6: Consider change the semantics of this function, + // ### TODO: Consider change the semantics of this function, // or deprecating/removing it altogether. // // For instance, it should try setting *all* the data @@ -1942,14 +2124,14 @@ QStringList QAbstractItemModel::mimeTypes() const mimeTypes(). If you reimplement mimeTypes() in your custom model to return more MIME types, reimplement this function to make use of them. - If the list of \a indexes is empty, or there are no supported MIME types, 0 - is returned rather than a serialized empty list. + If the list of \a indexes is empty, or there are no supported MIME types, + \nullptr is returned rather than a serialized empty list. \sa mimeTypes(), dropMimeData() */ QMimeData *QAbstractItemModel::mimeData(const QModelIndexList &indexes) const { - if (indexes.count() <= 0) + if (indexes.size() <= 0) return nullptr; QStringList types = mimeTypes(); if (types.isEmpty()) @@ -1957,7 +2139,7 @@ QMimeData *QAbstractItemModel::mimeData(const QModelIndexList &indexes) const QMimeData *data = new QMimeData(); QString format = types.at(0); QByteArray encoded; - QDataStream stream(&encoded, QIODevice::WriteOnly); + QDataStream stream(&encoded, QDataStream::WriteOnly); encodeData(indexes, stream); data->setData(format, encoded); return data; @@ -1980,15 +2162,15 @@ bool QAbstractItemModel::canDropMimeData(const QMimeData *data, Qt::DropAction a int row, int column, const QModelIndex &parent) const { - Q_UNUSED(row) - Q_UNUSED(column) - Q_UNUSED(parent) + Q_UNUSED(row); + Q_UNUSED(column); + Q_UNUSED(parent); if (!(action & supportedDropActions())) return false; const QStringList modelTypes = mimeTypes(); - for (int i = 0; i < modelTypes.count(); ++i) { + for (int i = 0; i < modelTypes.size(); ++i) { if (data->hasFormat(modelTypes.at(i))) return true; } @@ -2045,7 +2227,7 @@ bool QAbstractItemModel::dropMimeData(const QMimeData *data, Qt::DropAction acti column = 0; // decode and insert QByteArray encoded = data->data(format); - QDataStream stream(&encoded, QIODevice::ReadOnly); + QDataStream stream(&encoded, QDataStream::ReadOnly); return decodeData(row, column, parent, stream); } @@ -2080,34 +2262,10 @@ Qt::DropActions QAbstractItemModel::supportedDropActions() const */ Qt::DropActions QAbstractItemModel::supportedDragActions() const { - Q_D(const QAbstractItemModel); - if (int(d->supportedDragActions) != -1) - return d->supportedDragActions; return supportedDropActions(); } /*! - \internal - */ -void QAbstractItemModel::doSetSupportedDragActions(Qt::DropActions actions) -{ - Q_D(QAbstractItemModel); - d->supportedDragActions = actions; -} - -/*! - \since 4.2 - \obsolete - \fn void QAbstractItemModel::setSupportedDragActions(Qt::DropActions actions) - - This function is obsolete. Reimplement supportedDragActions() instead. - - Sets the supported drag \a actions for the items in the model. - - \sa supportedDragActions(), {Using drag and drop with item views} -*/ - -/*! \note The base class implementation of this function does nothing and returns \c{false}. @@ -2353,13 +2511,15 @@ QModelIndexList QAbstractItemModel::match(const QModelIndex &start, int role, Qt::MatchFlags flags) const { QModelIndexList result; - uint matchType = flags & 0x0F; + uint matchType = (flags & Qt::MatchTypeMask).toInt(); Qt::CaseSensitivity cs = flags & Qt::MatchCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive; - bool recurse = flags & Qt::MatchRecursive; - bool wrap = flags & Qt::MatchWrap; + bool recurse = flags.testAnyFlag(Qt::MatchRecursive); + bool wrap = flags.testAnyFlag(Qt::MatchWrap); bool allHits = (hits == -1); QString text; // only convert to a string if it is needed +#if QT_CONFIG(regularexpression) QRegularExpression rx; // only create it if needed +#endif const int column = start.column(); QModelIndex p = parent(start); int from = start.row(); @@ -2367,7 +2527,7 @@ QModelIndexList QAbstractItemModel::match(const QModelIndex &start, int role, // iterates twice if wrapping for (int i = 0; (wrap && i < 2) || (!wrap && i < 1); ++i) { - for (int r = from; (r < to) && (allHits || result.count() < hits); ++r) { + for (int r = from; (r < to) && (allHits || result.size() < hits); ++r) { QModelIndex idx = index(r, column, p); if (!idx.isValid()) continue; @@ -2377,9 +2537,10 @@ QModelIndexList QAbstractItemModel::match(const QModelIndex &start, int role, if (value == v) result.append(idx); } else { // QString or regular expression based matching +#if QT_CONFIG(regularexpression) if (matchType == Qt::MatchRegularExpression) { if (rx.pattern().isEmpty()) { - if (value.type() == QVariant::RegularExpression) { + if (value.userType() == QMetaType::QRegularExpression) { rx = value.toRegularExpression(); } else { rx.setPattern(value.toString()); @@ -2388,29 +2549,29 @@ QModelIndexList QAbstractItemModel::match(const QModelIndex &start, int role, } } } else if (matchType == Qt::MatchWildcard) { - if (rx.pattern().isEmpty()) - rx.setPattern(QRegularExpression::wildcardToRegularExpression(value.toString())); + if (rx.pattern().isEmpty()) { + const QString pattern = QRegularExpression::wildcardToRegularExpression(value.toString(), QRegularExpression::NonPathWildcardConversion); + rx.setPattern(pattern); + } if (cs == Qt::CaseInsensitive) rx.setPatternOptions(QRegularExpression::CaseInsensitiveOption); - } else { + } else +#endif + { if (text.isEmpty()) // lazy conversion text = value.toString(); } QString t = v.toString(); switch (matchType) { -#if QT_DEPRECATED_SINCE(5, 15) - case Qt::MatchRegExp: - if (QRegExp(text, cs).exactMatch(t)) - result.append(idx); - break; -#endif +#if QT_CONFIG(regularexpression) case Qt::MatchRegularExpression: Q_FALLTHROUGH(); case Qt::MatchWildcard: if (t.contains(rx)) result.append(idx); break; +#endif case Qt::MatchStartsWith: if (t.startsWith(text, cs)) result.append(idx); @@ -2434,7 +2595,7 @@ QModelIndexList QAbstractItemModel::match(const QModelIndex &start, int role, if (hasChildren(parent)) { // search the hierarchy result += match(index(0, column, parent), role, (text.isEmpty() ? value : text), - (allHits ? -1 : hits - result.count()), flags); + (allHits ? -1 : hits - result.size()), flags); } } } @@ -2457,30 +2618,6 @@ QSize QAbstractItemModel::span(const QModelIndex &) const } /*! - \fn void QAbstractItemModel::setRoleNames(const QHash<int,QByteArray> &roleNames) - \since 4.6 - \obsolete - - This function is obsolete. Reimplement roleNames() instead. - - Sets the model's role names to \a roleNames. - - This function allows mapping of role identifiers to role property names in - scripting languages. - - \sa roleNames() -*/ - -/*! - \internal - */ -void QAbstractItemModel::doSetRoleNames(const QHash<int,QByteArray> &roleNames) -{ - Q_D(QAbstractItemModel); - d->roleNames = roleNames; -} - -/*! \since 4.6 Returns the model's role names. @@ -2513,8 +2650,7 @@ void QAbstractItemModel::doSetRoleNames(const QHash<int,QByteArray> &roleNames) */ QHash<int,QByteArray> QAbstractItemModel::roleNames() const { - Q_D(const QAbstractItemModel); - return d->roleNames; + return QAbstractItemModelPrivate::defaultRoleNames(); } /*! @@ -2585,7 +2721,7 @@ bool QAbstractItemModel::setHeaderData(int section, Qt::Orientation orientation, } /*! - \fn QModelIndex QAbstractItemModel::createIndex(int row, int column, void *ptr) const + \fn QModelIndex QAbstractItemModel::createIndex(int row, int column, const void *ptr) const Creates a model index for the given \a row and \a column with the internal pointer \a ptr. @@ -2615,9 +2751,8 @@ bool QAbstractItemModel::setHeaderData(int section, Qt::Orientation orientation, */ void QAbstractItemModel::encodeData(const QModelIndexList &indexes, QDataStream &stream) const { - QModelIndexList::ConstIterator it = indexes.begin(); - for (; it != indexes.end(); ++it) - stream << (*it).row() << (*it).column() << itemData(*it); + for (const auto &index : indexes) + stream << index.row() << index.column() << itemData(index); } /*! @@ -2630,8 +2765,8 @@ bool QAbstractItemModel::decodeData(int row, int column, const QModelIndex &pare int left = INT_MAX; int bottom = 0; int right = 0; - QVector<int> rows, columns; - QVector<QMap<int, QVariant> > data; + QList<int> rows, columns; + QList<QMap<int, QVariant>> data; while (!stream.atEnd()) { int r, c; @@ -2652,16 +2787,16 @@ bool QAbstractItemModel::decodeData(int row, int column, const QModelIndex &pare int dragColumnCount = right - left + 1; // Compute the number of continuous rows upon insertion and modify the rows to match - QVector<int> rowsToInsert(bottom + 1); - for (int i = 0; i < rows.count(); ++i) + QList<int> rowsToInsert(bottom + 1); + for (int i = 0; i < rows.size(); ++i) rowsToInsert[rows.at(i)] = 1; - for (int i = 0; i < rowsToInsert.count(); ++i) { + for (int i = 0; i < rowsToInsert.size(); ++i) { if (rowsToInsert.at(i) == 1){ rowsToInsert[i] = dragRowCount; ++dragRowCount; } } - for (int i = 0; i < rows.count(); ++i) + for (int i = 0; i < rows.size(); ++i) rows[i] = top + rowsToInsert.at(rows.at(i)); QBitArray isWrittenTo(dragRowCount * dragColumnCount); @@ -2677,7 +2812,7 @@ bool QAbstractItemModel::decodeData(int row, int column, const QModelIndex &pare row = qMax(0, row); column = qMax(0, column); - QVector<QPersistentModelIndex> newIndexes(data.size()); + QList<QPersistentModelIndex> newIndexes(data.size()); // set the data in the table for (int j = 0; j < data.size(); ++j) { int relativeRow = rows.at(j) - top; @@ -2863,6 +2998,13 @@ bool QAbstractItemModelPrivate::allowMove(const QModelIndex &srcParent, int star } /*! + \internal + + see QTBUG-94546 + */ +void QAbstractItemModelPrivate::executePendingOperations() const { } + +/*! \since 4.6 Begins a row move operation. @@ -3190,9 +3332,8 @@ bool QAbstractItemModel::beginMoveColumns(const QModelIndex &sourceParent, int s destinationChange.needsAdjust = destinationParent.isValid() && destinationParent.row() >= sourceLast && destinationParent.parent() == sourceParent; d->changes.push(destinationChange); - d->itemsAboutToBeMoved(sourceParent, sourceFirst, sourceLast, destinationParent, destinationChild, Qt::Horizontal); - emit columnsAboutToBeMoved(sourceParent, sourceFirst, sourceLast, destinationParent, destinationChild, QPrivateSignal()); + d->itemsAboutToBeMoved(sourceParent, sourceFirst, sourceLast, destinationParent, destinationChild, Qt::Horizontal); return true; } @@ -3225,33 +3366,10 @@ void QAbstractItemModel::endMoveColumns() adjustedSource = createIndex(adjustedSource.row(), adjustedSource.column() + numMoved, adjustedSource.internalPointer()); d->itemsMoved(adjustedSource, removeChange.first, removeChange.last, adjustedDestination, insertChange.first, Qt::Horizontal); - emit columnsMoved(adjustedSource, removeChange.first, removeChange.last, adjustedDestination, insertChange.first, QPrivateSignal()); } /*! - \fn void QAbstractItemModel::reset() - \obsolete - - Resets the model to its original state in any attached views. - - This function emits the signals modelAboutToBeReset() and modelReset(). - - \note Use beginResetModel() and endResetModel() instead whenever possible. - Use this method only if there is no way to call beginResetModel() before invalidating the model. - Otherwise it could lead to unexpected behaviour, especially when used with proxy models. - - For example, in this code both signals modelAboutToBeReset() and modelReset() - are emitted \e after the data changes: - - \snippet code/src_corelib_kernel_qabstractitemmodel.cpp 10 - - Instead you should use: - - \snippet code/src_corelib_kernel_qabstractitemmodel.cpp 11 -*/ - -/*! Begins a model reset operation. A reset operation resets the model to its current state in any attached views. @@ -3294,7 +3412,7 @@ void QAbstractItemModel::endResetModel() { Q_D(QAbstractItemModel); d->invalidatePersistentIndexes(); - QMetaObject::invokeMethod(this, "resetInternalData"); + resetInternalData(); emit modelReset(QPrivateSignal()); } @@ -3340,9 +3458,9 @@ void QAbstractItemModel::changePersistentIndexList(const QModelIndexList &from, Q_D(QAbstractItemModel); if (d->persistent.indexes.isEmpty()) return; - QVector<QPersistentModelIndexData *> toBeReinserted; - toBeReinserted.reserve(to.count()); - for (int i = 0; i < from.count(); ++i) { + QList<QPersistentModelIndexData *> toBeReinserted; + toBeReinserted.reserve(to.size()); + for (int i = 0; i < from.size(); ++i) { if (from.at(i) == to.at(i)) continue; const auto it = d->persistent.indexes.constFind(from.at(i)); @@ -3355,11 +3473,8 @@ void QAbstractItemModel::changePersistentIndexList(const QModelIndexList &from, } } - for (QVector<QPersistentModelIndexData *>::const_iterator it = toBeReinserted.constBegin(); - it != toBeReinserted.constEnd() ; ++it) { - QPersistentModelIndexData *data = *it; + for (auto *data : std::as_const(toBeReinserted)) d->persistent.insertMultiAtEnd(data->index, data); - } } /*! @@ -3371,12 +3486,9 @@ QModelIndexList QAbstractItemModel::persistentIndexList() const { Q_D(const QAbstractItemModel); QModelIndexList result; - result.reserve(d->persistent.indexes.count()); - for (QHash<QModelIndex, QPersistentModelIndexData *>::const_iterator it = d->persistent.indexes.constBegin(); - it != d->persistent.indexes.constEnd(); ++it) { - QPersistentModelIndexData *data = *it; + result.reserve(d->persistent.indexes.size()); + for (auto *data : std::as_const(d->persistent.indexes)) result.append(data->index); - } return result; } @@ -3516,6 +3628,61 @@ bool QAbstractItemModel::checkIndex(const QModelIndex &index, CheckIndexOptions } /*! + \since 6.0 + + Fills the \a roleDataSpan with the requested data for the given \a index. + + The default implementation will call simply data() for each role in + the span. A subclass can reimplement this function to provide data + to views more efficiently: + + \snippet code/src_corelib_kernel_qabstractitemmodel.cpp 15 + + In the snippet above, \c{index} is the same for the entire call. + This means that accessing to the necessary data structures in order + to retrieve the information for \c{index} can be done only once + (hoisting the relevant code out of the loop). + + The usage of QModelRoleData::setData(), or similarly + QVariant::setValue(), is encouraged over constructing a QVariant + separately and using a plain assignment operator; this is + because the former allow to re-use the memory already allocated for + the QVariant object stored inside a QModelRoleData, while the latter + always allocates the new variant and then destroys the old one. + + Note that views may call multiData() with spans that have been used + in previous calls, and therefore may already contain some data. + Therefore, it is imperative that if the model cannot return the + data for a given role, then it must clear the data in the + corresponding QModelRoleData object. This can be done by calling + QModelRoleData::clearData(), or similarly by setting a default + constructed QVariant, and so on. Failure to clear the data will + result in the view believing that the "old" data is meant to be + used for the corresponding role. + + Finally, in order to avoid code duplication, a subclass may also + decide to reimplement data() in terms of multiData(), by supplying + a span of just one element: + + \snippet code/src_corelib_kernel_qabstractitemmodel.cpp 16 + + \note Models are not allowed to modify the roles in the span, or + to rearrange the span elements. Doing so results in undefined + behavior. + + \note It is illegal to pass an invalid model index to this function. + + \sa QModelRoleDataSpan, data() +*/ +void QAbstractItemModel::multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const +{ + Q_ASSERT(checkIndex(index, CheckIndexOption::IndexIsValid)); + + for (QModelRoleData &d : roleDataSpan) + d.setData(data(index, d.role())); +} + +/*! \class QAbstractTableModel \inmodule QtCore \brief The QAbstractTableModel class provides an abstract model that can be @@ -3572,10 +3739,9 @@ bool QAbstractItemModel::checkIndex(const QModelIndex &index, CheckIndexOptions \note Some general guidelines for subclassing models are available in the \l{Model Subclassing Reference}. - \note + \include models.qdocinc {thread-safety-section1}{QAbstractTableModel} - \sa {Model Classes}, QAbstractItemModel, QAbstractListModel, - {Pixelator Example} + \sa {Model Classes}, QAbstractItemModel, QAbstractListModel */ /*! @@ -3726,7 +3892,7 @@ Qt::ItemFlags QAbstractTableModel::flags(const QModelIndex &index) const \l{Model Subclassing Reference}. \sa {Model Classes}, {Model Subclassing Reference}, QAbstractItemView, - QAbstractTableModel, {Item Views Puzzle Example} + QAbstractTableModel */ /*! @@ -3845,14 +4011,14 @@ bool QAbstractTableModel::dropMimeData(const QMimeData *data, Qt::DropAction act return false; QByteArray encoded = data->data(format); - QDataStream stream(&encoded, QIODevice::ReadOnly); + QDataStream stream(&encoded, QDataStream::ReadOnly); // if the drop is on an item, replace the data in the items if (parent.isValid() && row == -1 && column == -1) { int top = INT_MAX; int left = INT_MAX; - QVector<int> rows, columns; - QVector<QMap<int, QVariant> > data; + QList<int> rows, columns; + QList<QMap<int, QVariant>> data; while (!stream.atEnd()) { int r, c; @@ -3896,14 +4062,14 @@ bool QAbstractListModel::dropMimeData(const QMimeData *data, Qt::DropAction acti return false; QByteArray encoded = data->data(format); - QDataStream stream(&encoded, QIODevice::ReadOnly); + QDataStream stream(&encoded, QDataStream::ReadOnly); // if the drop is on an item, replace the data in the items if (parent.isValid() && row == -1 && column == -1) { int top = INT_MAX; int left = INT_MAX; - QVector<int> rows, columns; - QVector<QMap<int, QVariant> > data; + QList<int> rows, columns; + QList<QMap<int, QVariant>> data; while (!stream.atEnd()) { int r, c; @@ -3970,7 +4136,7 @@ bool QAbstractListModel::dropMimeData(const QMimeData *data, Qt::DropAction acti */ /*! - \fn uint qHash(const QPersistentModelIndex &index, uint seed = 0) + \fn size_t qHash(const QPersistentModelIndex &index, size_t seed = 0) \since 5.0 \relates QPersistentModelIndex @@ -3991,8 +4157,8 @@ bool QAbstractListModel::dropMimeData(const QMimeData *data, Qt::DropAction acti */ void QAbstractItemModelPrivate::Persistent::insertMultiAtEnd(const QModelIndex& key, QPersistentModelIndexData *data) { - QHash<QModelIndex,QPersistentModelIndexData *>::iterator newIt = indexes.insert(key, data); - QHash<QModelIndex,QPersistentModelIndexData *>::iterator it = newIt; + auto newIt = indexes.insert(key, data); + auto it = newIt; ++it; while (it != indexes.end() && it.key() == key) { qSwap(*newIt,*it); @@ -4004,3 +4170,4 @@ void QAbstractItemModelPrivate::Persistent::insertMultiAtEnd(const QModelIndex& QT_END_NAMESPACE #include "moc_qabstractitemmodel.cpp" +#include "qabstractitemmodel.moc" diff --git a/src/corelib/itemmodels/qabstractitemmodel.h b/src/corelib/itemmodels/qabstractitemmodel.h index 43649cf79b..8f22f14989 100644 --- a/src/corelib/itemmodels/qabstractitemmodel.h +++ b/src/corelib/itemmodels/qabstractitemmodel.h @@ -1,84 +1,150 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QABSTRACTITEMMODEL_H #define QABSTRACTITEMMODEL_H -#include <QtCore/qvariant.h> -#include <QtCore/qobject.h> #include <QtCore/qhash.h> -#include <QtCore/qvector.h> +#include <QtCore/qlist.h> +#include <QtCore/qobject.h> +#include <QtCore/qvariant.h> QT_REQUIRE_CONFIG(itemmodel); QT_BEGIN_NAMESPACE +class QModelRoleData +{ + int m_role; + QVariant m_data; + +public: + explicit QModelRoleData(int role) noexcept + : m_role(role) + {} + + constexpr int role() const noexcept { return m_role; } + constexpr QVariant &data() noexcept { return m_data; } + constexpr const QVariant &data() const noexcept { return m_data; } + + template <typename T> + constexpr void setData(T &&value) noexcept(noexcept(m_data.setValue(std::forward<T>(value)))) + { m_data.setValue(std::forward<T>(value)); } + + void clearData() noexcept { m_data.clear(); } +}; + +Q_DECLARE_TYPEINFO(QModelRoleData, Q_RELOCATABLE_TYPE); + +class QModelRoleDataSpan; + +namespace QtPrivate { +template <typename T, typename Enable = void> +struct IsContainerCompatibleWithModelRoleDataSpan : std::false_type {}; + +template <typename T> +struct IsContainerCompatibleWithModelRoleDataSpan<T, std::enable_if_t<std::conjunction_v< + // lacking concepts and ranges, we accept any T whose std::data yields a suitable pointer ... + std::is_convertible<decltype( std::data(std::declval<T &>()) ), QModelRoleData *>, + // ... and that has a suitable size ... + std::is_convertible<decltype( std::size(std::declval<T &>()) ), qsizetype>, + // ... and it's a range as it defines an iterator-like API + std::is_convertible< + typename std::iterator_traits<decltype( std::begin(std::declval<T &>()) )>::value_type, + QModelRoleData + >, + std::is_convertible< + decltype( std::begin(std::declval<T &>()) != std::end(std::declval<T &>()) ), + bool>, + // Don't make an accidental copy constructor + std::negation<std::is_same<std::decay_t<T>, QModelRoleDataSpan>> + >>> : std::true_type {}; +} // namespace QtPrivate + +class QModelRoleDataSpan +{ + QModelRoleData *m_modelRoleData = nullptr; + qsizetype m_len = 0; + + template <typename T> + using if_compatible_container = std::enable_if_t<QtPrivate::IsContainerCompatibleWithModelRoleDataSpan<T>::value, bool>; + +public: + constexpr QModelRoleDataSpan() noexcept {} + + constexpr QModelRoleDataSpan(QModelRoleData &modelRoleData) noexcept + : m_modelRoleData(&modelRoleData), + m_len(1) + {} + + constexpr QModelRoleDataSpan(QModelRoleData *modelRoleData, qsizetype len) + : m_modelRoleData(modelRoleData), + m_len(len) + {} + + template <typename Container, if_compatible_container<Container> = true> + constexpr QModelRoleDataSpan(Container &c) noexcept(noexcept(std::data(c)) && noexcept(std::size(c))) + : m_modelRoleData(std::data(c)), + m_len(qsizetype(std::size(c))) + {} + + constexpr qsizetype size() const noexcept { return m_len; } + constexpr qsizetype length() const noexcept { return m_len; } + constexpr QModelRoleData *data() const noexcept { return m_modelRoleData; } + constexpr QModelRoleData *begin() const noexcept { return m_modelRoleData; } + constexpr QModelRoleData *end() const noexcept { return m_modelRoleData + m_len; } + constexpr QModelRoleData &operator[](qsizetype index) const { return m_modelRoleData[index]; } + + constexpr QVariant *dataForRole(int role) const + { +#ifdef __cpp_lib_constexpr_algorithms + auto result = std::find_if(begin(), end(), [role](const QModelRoleData &roleData) { + return roleData.role() == role; + }); +#else + auto result = begin(); + const auto e = end(); + for (; result != e; ++result) { + if (result->role() == role) + break; + } +#endif + + return Q_ASSERT(result != end()), &result->data(); + } +}; + +Q_DECLARE_TYPEINFO(QModelRoleDataSpan, Q_RELOCATABLE_TYPE); class QAbstractItemModel; class QPersistentModelIndex; -class Q_CORE_EXPORT QModelIndex +class QModelIndex { friend class QAbstractItemModel; public: - Q_DECL_CONSTEXPR inline QModelIndex() noexcept : r(-1), c(-1), i(0), m(nullptr) {} + constexpr inline QModelIndex() noexcept : r(-1), c(-1), i(0), m(nullptr) {} // compiler-generated copy/move ctors/assignment operators are fine! - Q_DECL_CONSTEXPR inline int row() const noexcept { return r; } - Q_DECL_CONSTEXPR inline int column() const noexcept { return c; } - Q_DECL_CONSTEXPR inline quintptr internalId() const noexcept { return i; } + constexpr inline int row() const noexcept { return r; } + constexpr inline int column() const noexcept { return c; } + constexpr inline quintptr internalId() const noexcept { return i; } inline void *internalPointer() const noexcept { return reinterpret_cast<void*>(i); } + inline const void *constInternalPointer() const noexcept { return reinterpret_cast<const void *>(i); } inline QModelIndex parent() const; inline QModelIndex sibling(int row, int column) const; inline QModelIndex siblingAtColumn(int column) const; inline QModelIndex siblingAtRow(int row) const; -#if QT_DEPRECATED_SINCE(5, 8) - QT_DEPRECATED_X("Use QAbstractItemModel::index") inline QModelIndex child(int row, int column) const; -#endif inline QVariant data(int role = Qt::DisplayRole) const; + inline void multiData(QModelRoleDataSpan roleDataSpan) const; inline Qt::ItemFlags flags() const; - Q_DECL_CONSTEXPR inline const QAbstractItemModel *model() const noexcept { return m; } - Q_DECL_CONSTEXPR inline bool isValid() const noexcept { return (r >= 0) && (c >= 0) && (m != nullptr); } - Q_DECL_CONSTEXPR inline bool operator==(const QModelIndex &other) const noexcept + constexpr inline const QAbstractItemModel *model() const noexcept { return m; } + constexpr inline bool isValid() const noexcept { return (r >= 0) && (c >= 0) && (m != nullptr); } + constexpr inline bool operator==(const QModelIndex &other) const noexcept { return (other.r == r) && (other.i == i) && (other.c == c) && (other.m == m); } - Q_DECL_CONSTEXPR inline bool operator!=(const QModelIndex &other) const noexcept + constexpr inline bool operator!=(const QModelIndex &other) const noexcept { return !(*this == other); } - Q_DECL_CONSTEXPR inline bool operator<(const QModelIndex &other) const noexcept + constexpr inline bool operator<(const QModelIndex &other) const noexcept { return r < other.r || (r == other.r && (c < other.c @@ -86,15 +152,15 @@ public: || (i == other.i && std::less<const QAbstractItemModel *>()(m, other.m)))))); } private: - inline QModelIndex(int arow, int acolumn, void *ptr, const QAbstractItemModel *amodel) noexcept + inline QModelIndex(int arow, int acolumn, const void *ptr, const QAbstractItemModel *amodel) noexcept : r(arow), c(acolumn), i(reinterpret_cast<quintptr>(ptr)), m(amodel) {} - Q_DECL_CONSTEXPR inline QModelIndex(int arow, int acolumn, quintptr id, const QAbstractItemModel *amodel) noexcept + constexpr inline QModelIndex(int arow, int acolumn, quintptr id, const QAbstractItemModel *amodel) noexcept : r(arow), c(acolumn), i(id), m(amodel) {} int r, c; quintptr i; const QAbstractItemModel *m; }; -Q_DECLARE_TYPEINFO(QModelIndex, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(QModelIndex, Q_RELOCATABLE_TYPE); #ifndef QT_NO_DEBUG_STREAM Q_CORE_EXPORT QDebug operator<<(QDebug, const QModelIndex &); @@ -103,7 +169,7 @@ Q_CORE_EXPORT QDebug operator<<(QDebug, const QModelIndex &); class QPersistentModelIndexData; // qHash is a friend, but we can't use default arguments for friends (§8.3.6.4) -uint qHash(const QPersistentModelIndex &index, uint seed = 0) noexcept; +size_t qHash(const QPersistentModelIndex &index, size_t seed = 0) noexcept; class Q_CORE_EXPORT QPersistentModelIndex { @@ -112,43 +178,43 @@ public: QPersistentModelIndex(const QModelIndex &index); QPersistentModelIndex(const QPersistentModelIndex &other); ~QPersistentModelIndex(); - bool operator<(const QPersistentModelIndex &other) const; - bool operator==(const QPersistentModelIndex &other) const; - inline bool operator!=(const QPersistentModelIndex &other) const + bool operator<(const QPersistentModelIndex &other) const noexcept; + bool operator==(const QPersistentModelIndex &other) const noexcept; + inline bool operator!=(const QPersistentModelIndex &other) const noexcept { return !operator==(other); } QPersistentModelIndex &operator=(const QPersistentModelIndex &other); inline QPersistentModelIndex(QPersistentModelIndex &&other) noexcept - : d(other.d) { other.d = nullptr; } - inline QPersistentModelIndex &operator=(QPersistentModelIndex &&other) noexcept - { qSwap(d, other.d); return *this; } - inline void swap(QPersistentModelIndex &other) noexcept { qSwap(d, other.d); } - bool operator==(const QModelIndex &other) const; - bool operator!=(const QModelIndex &other) const; + : d(std::exchange(other.d, nullptr)) {} + QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QPersistentModelIndex) + void swap(QPersistentModelIndex &other) noexcept { qt_ptr_swap(d, other.d); } + bool operator==(const QModelIndex &other) const noexcept; + bool operator!=(const QModelIndex &other) const noexcept; QPersistentModelIndex &operator=(const QModelIndex &other); - operator const QModelIndex&() const; + operator QModelIndex() const; int row() const; int column() const; void *internalPointer() const; + const void *constInternalPointer() const; quintptr internalId() const; QModelIndex parent() const; QModelIndex sibling(int row, int column) const; -#if QT_DEPRECATED_SINCE(5, 8) - QT_DEPRECATED_X("Use QAbstractItemModel::index") QModelIndex child(int row, int column) const; -#endif QVariant data(int role = Qt::DisplayRole) const; + void multiData(QModelRoleDataSpan roleDataSpan) const; Qt::ItemFlags flags() const; const QAbstractItemModel *model() const; bool isValid() const; private: QPersistentModelIndexData *d; - friend uint qHash(const QPersistentModelIndex &, uint seed) noexcept; + friend size_t qHash(const QPersistentModelIndex &, size_t seed) noexcept; + friend bool qHashEquals(const QPersistentModelIndex &a, const QPersistentModelIndex &b) noexcept + { return a.d == b.d; } #ifndef QT_NO_DEBUG_STREAM friend Q_CORE_EXPORT QDebug operator<<(QDebug, const QPersistentModelIndex &); #endif }; Q_DECLARE_SHARED(QPersistentModelIndex) -inline uint qHash(const QPersistentModelIndex &index, uint seed) noexcept +inline size_t qHash(const QPersistentModelIndex &index, size_t seed) noexcept { return qHash(index.d, seed); } @@ -156,7 +222,6 @@ inline uint qHash(const QPersistentModelIndex &index, uint seed) noexcept Q_CORE_EXPORT QDebug operator<<(QDebug, const QPersistentModelIndex &); #endif -template<typename T> class QList; typedef QList<QModelIndex> QModelIndexList; class QMimeData; @@ -171,8 +236,7 @@ class Q_CORE_EXPORT QAbstractItemModel : public QObject friend class QPersistentModelIndexData; friend class QAbstractItemViewPrivate; - friend class QIdentityProxyModel; - friend class QTransposeProxyModelPrivate; + friend class QAbstractProxyModel; public: explicit QAbstractItemModel(QObject *parent = nullptr); @@ -198,9 +262,7 @@ public: virtual QMap<int, QVariant> itemData(const QModelIndex &index) const; virtual bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles); -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) virtual bool clearItemData(const QModelIndex &index); -#endif virtual QStringList mimeTypes() const; virtual QMimeData *mimeData(const QModelIndexList &indexes) const; @@ -209,35 +271,30 @@ public: virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); virtual Qt::DropActions supportedDropActions() const; - virtual Qt::DropActions supportedDragActions() const; -#if QT_DEPRECATED_SINCE(5, 0) - QT_DEPRECATED void setSupportedDragActions(Qt::DropActions actions) - { doSetSupportedDragActions(actions); } -#endif - virtual bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()); - virtual bool insertColumns(int column, int count, const QModelIndex &parent = QModelIndex()); - virtual bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); - virtual bool removeColumns(int column, int count, const QModelIndex &parent = QModelIndex()); - virtual bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, + Q_INVOKABLE Q_REVISION(6, 4) virtual bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()); + Q_INVOKABLE Q_REVISION(6, 4) virtual bool insertColumns(int column, int count, const QModelIndex &parent = QModelIndex()); + Q_INVOKABLE Q_REVISION(6, 4) virtual bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); + Q_INVOKABLE Q_REVISION(6, 4) virtual bool removeColumns(int column, int count, const QModelIndex &parent = QModelIndex()); + Q_INVOKABLE Q_REVISION(6, 4) virtual bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild); - virtual bool moveColumns(const QModelIndex &sourceParent, int sourceColumn, int count, + Q_INVOKABLE Q_REVISION(6, 4) virtual bool moveColumns(const QModelIndex &sourceParent, int sourceColumn, int count, const QModelIndex &destinationParent, int destinationChild); - inline bool insertRow(int row, const QModelIndex &parent = QModelIndex()); - inline bool insertColumn(int column, const QModelIndex &parent = QModelIndex()); - inline bool removeRow(int row, const QModelIndex &parent = QModelIndex()); - inline bool removeColumn(int column, const QModelIndex &parent = QModelIndex()); - inline bool moveRow(const QModelIndex &sourceParent, int sourceRow, + Q_INVOKABLE Q_REVISION(6, 4) inline bool insertRow(int row, const QModelIndex &parent = QModelIndex()); + Q_INVOKABLE Q_REVISION(6, 4) inline bool insertColumn(int column, const QModelIndex &parent = QModelIndex()); + Q_INVOKABLE Q_REVISION(6, 4) inline bool removeRow(int row, const QModelIndex &parent = QModelIndex()); + Q_INVOKABLE Q_REVISION(6, 4) inline bool removeColumn(int column, const QModelIndex &parent = QModelIndex()); + Q_INVOKABLE Q_REVISION(6, 4) inline bool moveRow(const QModelIndex &sourceParent, int sourceRow, const QModelIndex &destinationParent, int destinationChild); - inline bool moveColumn(const QModelIndex &sourceParent, int sourceColumn, + Q_INVOKABLE Q_REVISION(6, 4) inline bool moveColumn(const QModelIndex &sourceParent, int sourceColumn, const QModelIndex &destinationParent, int destinationChild); Q_INVOKABLE virtual void fetchMore(const QModelIndex &parent); Q_INVOKABLE virtual bool canFetchMore(const QModelIndex &parent) const; Q_INVOKABLE virtual Qt::ItemFlags flags(const QModelIndex &index) const; - virtual void sort(int column, Qt::SortOrder order = Qt::AscendingOrder); + Q_INVOKABLE Q_REVISION(6, 4) virtual void sort(int column, Qt::SortOrder order = Qt::AscendingOrder); virtual QModelIndex buddy(const QModelIndex &index) const; Q_INVOKABLE virtual QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits = 1, @@ -266,10 +323,13 @@ public: Q_ENUM(CheckIndexOption) Q_DECLARE_FLAGS(CheckIndexOptions, CheckIndexOption) - Q_REQUIRED_RESULT bool checkIndex(const QModelIndex &index, CheckIndexOptions options = CheckIndexOption::NoOption) const; + [[nodiscard]] bool checkIndex(const QModelIndex &index, CheckIndexOptions options = CheckIndexOption::NoOption) const; + + virtual void multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const; Q_SIGNALS: - void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles = QVector<int>()); + void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, + const QList<int> &roles = QList<int>()); void headerDataChanged(Qt::Orientation orientation, int first, int last); void layoutChanged(const QList<QPersistentModelIndex> &parents = QList<QPersistentModelIndex>(), QAbstractItemModel::LayoutChangeHint hint = QAbstractItemModel::NoLayoutChangeHint); void layoutAboutToBeChanged(const QList<QPersistentModelIndex> &parents = QList<QPersistentModelIndex>(), QAbstractItemModel::LayoutChangeHint hint = QAbstractItemModel::NoLayoutChangeHint); @@ -290,25 +350,22 @@ Q_SIGNALS: void modelReset(QPrivateSignal); void rowsAboutToBeMoved( const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow, QPrivateSignal); - void rowsMoved( const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row, QPrivateSignal); + void rowsMoved( const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow, QPrivateSignal); void columnsAboutToBeMoved( const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationColumn, QPrivateSignal); - void columnsMoved( const QModelIndex &parent, int start, int end, const QModelIndex &destination, int column, QPrivateSignal); + void columnsMoved( const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationColumn, QPrivateSignal); public Q_SLOTS: virtual bool submit(); virtual void revert(); protected Q_SLOTS: -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - virtual -#endif - void resetInternalData(); + virtual void resetInternalData(); protected: QAbstractItemModel(QAbstractItemModelPrivate &dd, QObject *parent = nullptr); - inline QModelIndex createIndex(int row, int column, void *data = nullptr) const; + inline QModelIndex createIndex(int row, int column, const void *data = nullptr) const; inline QModelIndex createIndex(int row, int column, quintptr id) const; void encodeData(const QModelIndexList &indexes, QDataStream &stream) const; @@ -332,15 +389,6 @@ protected: bool beginMoveColumns(const QModelIndex &sourceParent, int sourceFirst, int sourceLast, const QModelIndex &destinationParent, int destinationColumn); void endMoveColumns(); - -#if QT_DEPRECATED_SINCE(5,0) - QT_DEPRECATED void reset() - { - beginResetModel(); - endResetModel(); - } -#endif - void beginResetModel(); void endResetModel(); @@ -348,17 +396,7 @@ protected: void changePersistentIndexList(const QModelIndexList &from, const QModelIndexList &to); QModelIndexList persistentIndexList() const; -#if QT_DEPRECATED_SINCE(5,0) - QT_DEPRECATED void setRoleNames(const QHash<int,QByteArray> &theRoleNames) - { - doSetRoleNames(theRoleNames); - } -#endif - private: - void doSetRoleNames(const QHash<int,QByteArray> &roleNames); - void doSetSupportedDragActions(Qt::DropActions actions); - Q_DECLARE_PRIVATE(QAbstractItemModel) Q_DISABLE_COPY(QAbstractItemModel) }; @@ -379,7 +417,7 @@ inline bool QAbstractItemModel::moveRow(const QModelIndex &sourceParent, int sou inline bool QAbstractItemModel::moveColumn(const QModelIndex &sourceParent, int sourceColumn, const QModelIndex &destinationParent, int destinationChild) { return moveColumns(sourceParent, sourceColumn, 1, destinationParent, destinationChild); } -inline QModelIndex QAbstractItemModel::createIndex(int arow, int acolumn, void *adata) const +inline QModelIndex QAbstractItemModel::createIndex(int arow, int acolumn, const void *adata) const { return QModelIndex(arow, acolumn, adata, this); } inline QModelIndex QAbstractItemModel::createIndex(int arow, int acolumn, quintptr aid) const { return QModelIndex(arow, acolumn, aid, this); } @@ -451,22 +489,26 @@ inline QModelIndex QModelIndex::siblingAtColumn(int acolumn) const inline QModelIndex QModelIndex::siblingAtRow(int arow) const { return m ? (r == arow) ? *this : m->sibling(arow, c, *this) : QModelIndex(); } -#if QT_DEPRECATED_SINCE(5, 8) -inline QModelIndex QModelIndex::child(int arow, int acolumn) const -{ return m ? m->index(arow, acolumn, *this) : QModelIndex(); } -#endif - inline QVariant QModelIndex::data(int arole) const { return m ? m->data(*this, arole) : QVariant(); } +inline void QModelIndex::multiData(QModelRoleDataSpan roleDataSpan) const +{ if (m) m->multiData(*this, roleDataSpan); } + inline Qt::ItemFlags QModelIndex::flags() const { return m ? m->flags(*this) : Qt::ItemFlags(); } -inline uint qHash(const QModelIndex &index) noexcept -{ return uint((uint(index.row()) << 4) + index.column() + index.internalId()); } +inline size_t qHash(const QModelIndex &index, size_t seed = 0) noexcept +{ +#if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0) + return qHashMulti(seed, index.row(), index.column(), index.internalId()); +#else + return size_t((size_t(index.row()) << 4) + size_t(index.column()) + index.internalId()) ^ seed; +#endif +} QT_END_NAMESPACE -Q_DECLARE_METATYPE(QModelIndexList) +QT_DECL_METATYPE_EXTERN(QModelIndexList, Q_CORE_EXPORT) #endif // QABSTRACTITEMMODEL_H diff --git a/src/corelib/itemmodels/qabstractitemmodel_p.h b/src/corelib/itemmodels/qabstractitemmodel_p.h index 92a440a125..e34dc3262c 100644 --- a/src/corelib/itemmodels/qabstractitemmodel_p.h +++ b/src/corelib/itemmodels/qabstractitemmodel_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QABSTRACTITEMMODEL_P_H #define QABSTRACTITEMMODEL_P_H @@ -82,7 +46,8 @@ public: ~QAbstractItemModelPrivate(); void removePersistentIndexData(QPersistentModelIndexData *data); - void movePersistentIndexes(const QVector<QPersistentModelIndexData *> &indexes, int change, const QModelIndex &parent, Qt::Orientation orientation); + void movePersistentIndexes(const QList<QPersistentModelIndexData *> &indexes, int change, const QModelIndex &parent, + Qt::Orientation orientation); void rowsAboutToBeInserted(const QModelIndex &parent, int first, int last); void rowsInserted(const QModelIndex &parent, int first, int last); void rowsAboutToBeRemoved(const QModelIndex &parent, int first, int last); @@ -98,6 +63,9 @@ public: void itemsMoved(const QModelIndex &srcParent, int srcFirst, int srcLast, const QModelIndex &destinationParent, int destinationChild, Qt::Orientation orientation); bool allowMove(const QModelIndex &srcParent, int srcFirst, int srcLast, const QModelIndex &destinationParent, int destinationChild, Qt::Orientation orientation); + // ugly hack for QTreeModel, see QTBUG-94546 + virtual void executePendingOperations() const; + inline QModelIndex createIndex(int row, int column, void *data = nullptr) const { return q_func()->createIndex(row, column, data); } @@ -114,8 +82,8 @@ public: void invalidatePersistentIndex(const QModelIndex &index); struct Change { - Q_DECL_CONSTEXPR Change() : parent(), first(-1), last(-1), needsAdjust(false) {} - Q_DECL_CONSTEXPR Change(const QModelIndex &p, int f, int l) : parent(p), first(f), last(l), needsAdjust(false) {} + constexpr Change() : parent(), first(-1), last(-1), needsAdjust(false) {} + constexpr Change(const QModelIndex &p, int f, int l) : parent(p), first(f), last(l), needsAdjust(false) {} QModelIndex parent; int first, last; @@ -135,26 +103,73 @@ public: // rowsMoved signal. bool needsAdjust; - Q_DECL_CONSTEXPR bool isValid() const { return first >= 0 && last >= 0; } + constexpr bool isValid() const { return first >= 0 && last >= 0; } }; QStack<Change> changes; struct Persistent { Persistent() {} QMultiHash<QModelIndex, QPersistentModelIndexData *> indexes; - QStack<QVector<QPersistentModelIndexData *> > moved; - QStack<QVector<QPersistentModelIndexData *> > invalidated; + QStack<QList<QPersistentModelIndexData *>> moved; + QStack<QList<QPersistentModelIndexData *>> invalidated; void insertMultiAtEnd(const QModelIndex& key, QPersistentModelIndexData *data); } persistent; - Qt::DropActions supportedDragActions; - - QHash<int,QByteArray> roleNames; static const QHash<int,QByteArray> &defaultRoleNames(); static bool isVariantLessThan(const QVariant &left, const QVariant &right, Qt::CaseSensitivity cs = Qt::CaseSensitive, bool isLocaleAware = false); }; -Q_DECLARE_TYPEINFO(QAbstractItemModelPrivate::Change, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(QAbstractItemModelPrivate::Change, Q_RELOCATABLE_TYPE); + +namespace QtPrivate { + +/*! + \internal + This is a workaround for QTBUG-75172. + + Some predefined model roles are supposed to use certain enum/flag + types (e.g. fetching Qt::TextAlignmentRole is supposed to return a + variant containing a Qt::Alignment object). + + For historical reasons, a plain `int` was used sometimes. This is + surprising to end-users and also sloppy on Qt's part; users were + forced to use `int` rather than the correct datatype. + + This function tries both the "right" type and plain `int`, for a + given QVariant. This fixes the problem (using the correct datatype) + but also keeps compatibility with existing code using `int`. + + ### Qt 7: get rid of this. Always use the correct datatype. +*/ +template <typename T> +T legacyEnumValueFromModelData(const QVariant &data) +{ + static_assert(std::is_enum_v<T>); + if (data.userType() == qMetaTypeId<T>()) { + return data.value<T>(); + } else if (std::is_same_v<std::underlying_type_t<T>, int> || + std::is_same_v<std::underlying_type_t<T>, uint>) { + return T(data.toInt()); + } + + return T(); +} + +template <typename T> +T legacyFlagValueFromModelData(const QVariant &data) +{ + if (data.userType() == qMetaTypeId<T>()) { + return data.value<T>(); + } else if (std::is_same_v<std::underlying_type_t<typename T::enum_type>, int> || + std::is_same_v<std::underlying_type_t<typename T::enum_type>, uint>) { + return T::fromInt(data.toInt()); + } + + return T(); +} + +} // namespace QtPrivate + QT_END_NAMESPACE diff --git a/src/corelib/itemmodels/qabstractproxymodel.cpp b/src/corelib/itemmodels/qabstractproxymodel.cpp index 87559cd6b2..abdeefb4da 100644 --- a/src/corelib/itemmodels/qabstractproxymodel.cpp +++ b/src/corelib/itemmodels/qabstractproxymodel.cpp @@ -1,47 +1,12 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qabstractproxymodel.h" #include "qitemselectionmodel.h" #include <private/qabstractproxymodel_p.h> #include <QtCore/QSize> #include <QtCore/QStringList> +#include <QtCore/QMap> QT_BEGIN_NAMESPACE @@ -90,6 +55,87 @@ void QAbstractProxyModelPrivate::_q_sourceModelDestroyed() model = QAbstractItemModelPrivate::staticEmptyModel(); } +void QAbstractProxyModelPrivate::emitHeaderDataChanged() +{ + Q_Q(QAbstractProxyModel); + + if (updateHorizontalHeader) { + if (auto columnCount = q->columnCount(); columnCount > 0) + emit q->headerDataChanged(Qt::Horizontal, 0, columnCount - 1); + } + + if (updateVerticalHeader) { + if (auto rowCount = q->rowCount(); rowCount > 0) + emit q->headerDataChanged(Qt::Vertical, 0, rowCount - 1); + } + + updateHorizontalHeader = false; + updateVerticalHeader = false; +} + +void QAbstractProxyModelPrivate::scheduleHeaderUpdate(Qt::Orientation orientation) +{ + const bool isUpdateScheduled = updateHorizontalHeader || updateVerticalHeader; + + if (orientation == Qt::Horizontal && !updateHorizontalHeader) + updateHorizontalHeader = true; + else if (orientation == Qt::Vertical && !updateVerticalHeader) + updateVerticalHeader = true; + else + return; + + if (!isUpdateScheduled) { + Q_Q(QAbstractProxyModel); + QMetaObject::invokeMethod(q, [this]() { emitHeaderDataChanged(); }, Qt::QueuedConnection); + } +} + +void QAbstractProxyModelPrivate::_q_sourceModelRowsAboutToBeInserted(const QModelIndex &parent, int, int) +{ + if (parent.isValid()) + return; + sourceHadZeroRows = model->rowCount() == 0; +} + +void QAbstractProxyModelPrivate::_q_sourceModelRowsInserted(const QModelIndex &parent, int, int) +{ + if (parent.isValid()) + return; + if (sourceHadZeroRows) + scheduleHeaderUpdate(Qt::Horizontal); +} + +void QAbstractProxyModelPrivate::_q_sourceModelRowsRemoved(const QModelIndex &parent, int, int) +{ + if (parent.isValid()) + return; + if (model->rowCount() == 0) + scheduleHeaderUpdate(Qt::Horizontal); +} + +void QAbstractProxyModelPrivate::_q_sourceModelColumnsAboutToBeInserted(const QModelIndex &parent, int, int) +{ + if (parent.isValid()) + return; + sourceHadZeroColumns = model->columnCount() == 0; +} + +void QAbstractProxyModelPrivate::_q_sourceModelColumnsInserted(const QModelIndex &parent, int, int) +{ + if (parent.isValid()) + return; + if (sourceHadZeroColumns) + scheduleHeaderUpdate(Qt::Vertical); +} + +void QAbstractProxyModelPrivate::_q_sourceModelColumnsRemoved(const QModelIndex &parent, int, int) +{ + if (parent.isValid()) + return; + if (model->columnCount() == 0) + scheduleHeaderUpdate(Qt::Vertical); +} + /*! Constructs a proxy model with the given \a parent. */ @@ -128,31 +174,45 @@ QAbstractProxyModel::~QAbstractProxyModel() void QAbstractProxyModel::setSourceModel(QAbstractItemModel *sourceModel) { Q_D(QAbstractProxyModel); - if (sourceModel != d->model) { - if (d->model) - disconnect(d->model, SIGNAL(destroyed()), this, SLOT(_q_sourceModelDestroyed())); + d->model.removeBindingUnlessInWrapper(); + // Special case to handle nullptr models. Otherwise we will have unwanted + // notifications. + const QAbstractItemModel *currentModel = d->model.valueBypassingBindings(); + if (!sourceModel && currentModel == QAbstractItemModelPrivate::staticEmptyModel()) + return; + static const struct { + const char *signalName; + const char *slotName; + } connectionTable[] = { + // clang-format off + { SIGNAL(destroyed()), SLOT(_q_sourceModelDestroyed()) }, + { SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), SLOT(_q_sourceModelRowsAboutToBeInserted(QModelIndex,int,int)) }, + { SIGNAL(rowsInserted(QModelIndex,int,int)), SLOT(_q_sourceModelRowsInserted(QModelIndex,int,int)) }, + { SIGNAL(rowsRemoved(QModelIndex,int,int)), SLOT(_q_sourceModelRowsRemoved(QModelIndex,int,int)) }, + { SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)), SLOT(_q_sourceModelColumnsAboutToBeInserted(QModelIndex,int,int)) }, + { SIGNAL(columnsInserted(QModelIndex,int,int)), SLOT(_q_sourceModelColumnsInserted(QModelIndex,int,int)) }, + { SIGNAL(columnsRemoved(QModelIndex,int,int)), SLOT(_q_sourceModelColumnsRemoved(QModelIndex,int,int)) } + // clang-format on + }; + + if (sourceModel != currentModel) { + if (currentModel) { + for (const auto &c : connectionTable) + disconnect(currentModel, c.signalName, this, c.slotName); + } if (sourceModel) { - d->model = sourceModel; - connect(d->model, SIGNAL(destroyed()), this, SLOT(_q_sourceModelDestroyed())); + d->model.setValueBypassingBindings(sourceModel); + for (const auto &c : connectionTable) + connect(sourceModel, c.signalName, this, c.slotName); } else { - d->model = QAbstractItemModelPrivate::staticEmptyModel(); + d->model.setValueBypassingBindings(QAbstractItemModelPrivate::staticEmptyModel()); } - d->roleNames = d->model->roleNames(); - emit sourceModelChanged(QPrivateSignal()); + d->model.notify(); } } /*! - Clears the roleNames of this proxy model. -*/ -void QAbstractProxyModel::resetInternalData() -{ - Q_D(QAbstractProxyModel); - d->roleNames = d->model->roleNames(); -} - -/*! Returns the model that contains the data that is available through the proxy model. */ QAbstractItemModel *QAbstractProxyModel::sourceModel() const @@ -163,6 +223,12 @@ QAbstractItemModel *QAbstractProxyModel::sourceModel() const return d->model; } +QBindable<QAbstractItemModel *> QAbstractProxyModel::bindableSourceModel() +{ + Q_D(QAbstractProxyModel); + return QBindable<QAbstractItemModel *>(&d->model); +} + /*! \reimp */ @@ -251,13 +317,17 @@ QVariant QAbstractProxyModel::data(const QModelIndex &proxyIndex, int role) cons QVariant QAbstractProxyModel::headerData(int section, Qt::Orientation orientation, int role) const { Q_D(const QAbstractProxyModel); - int sourceSection; + int sourceSection = section; if (orientation == Qt::Horizontal) { - const QModelIndex proxyIndex = index(0, section); - sourceSection = mapToSource(proxyIndex).column(); + if (rowCount() > 0) { + const QModelIndex proxyIndex = index(0, section); + sourceSection = mapToSource(proxyIndex).column(); + } } else { - const QModelIndex proxyIndex = index(section, 0); - sourceSection = mapToSource(proxyIndex).row(); + if (columnCount() > 0) { + const QModelIndex proxyIndex = index(section, 0); + sourceSection = mapToSource(proxyIndex).row(); + } } return d->model->headerData(sourceSection, orientation, role); } @@ -267,7 +337,8 @@ QVariant QAbstractProxyModel::headerData(int section, Qt::Orientation orientatio */ QMap<int, QVariant> QAbstractProxyModel::itemData(const QModelIndex &proxyIndex) const { - return QAbstractItemModel::itemData(proxyIndex); + Q_D(const QAbstractProxyModel); + return d->model->itemData(mapToSource(proxyIndex)); } /*! @@ -293,7 +364,8 @@ bool QAbstractProxyModel::setData(const QModelIndex &index, const QVariant &valu */ bool QAbstractProxyModel::setItemData(const QModelIndex &index, const QMap< int, QVariant >& roles) { - return QAbstractItemModel::setItemData(index, roles); + Q_D(QAbstractProxyModel); + return d->model->setItemData(mapToSource(index), roles); } /*! @@ -313,7 +385,6 @@ 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 \since 6.0 @@ -323,7 +394,6 @@ bool QAbstractProxyModel::clearItemData(const QModelIndex &index) Q_D(QAbstractProxyModel); return d->model->clearItemData(mapToSource(index)); } -#endif /*! \reimp @@ -394,7 +464,7 @@ QMimeData* QAbstractProxyModel::mimeData(const QModelIndexList &indexes) const { Q_D(const QAbstractProxyModel); QModelIndexList list; - list.reserve(indexes.count()); + list.reserve(indexes.size()); for (const QModelIndex &index : indexes) list << mapToSource(index); return d->model->mimeData(list); @@ -477,6 +547,36 @@ Qt::DropActions QAbstractProxyModel::supportedDropActions() const return d->model->supportedDropActions(); } +/*! + \reimp + */ +QHash<int,QByteArray> QAbstractProxyModel::roleNames() const +{ + Q_D(const QAbstractProxyModel); + return d->model->roleNames(); +} + +/*! + Equivalent to calling createIndex on the source model. + + This method is useful if your proxy model wants to maintain the + parent-child relationship of items in the source model. + When reimplementing mapToSource(), you can call this method to + create an index for row \a row and column \a col of the source model. + + A typical use would be to save the internal pointer coming from the source model + in the proxy index when reimplementing mapFromSource() and use the same internal + pointer as \a internalPtr to recover the original source index when + reimplementing mapToSource(). + \since 6.2 + */ +QModelIndex QAbstractProxyModel::createSourceIndex(int row, int col, void *internalPtr) const +{ + if (sourceModel()) + return sourceModel()->createIndex(row, col, internalPtr); + return QModelIndex(); +} + QT_END_NAMESPACE #include "moc_qabstractproxymodel.cpp" diff --git a/src/corelib/itemmodels/qabstractproxymodel.h b/src/corelib/itemmodels/qabstractproxymodel.h index c9a73b6a31..8652f500df 100644 --- a/src/corelib/itemmodels/qabstractproxymodel.h +++ b/src/corelib/itemmodels/qabstractproxymodel.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QABSTRACTPROXYMODEL_H #define QABSTRACTPROXYMODEL_H @@ -52,7 +16,8 @@ class QItemSelection; class Q_CORE_EXPORT QAbstractProxyModel : public QAbstractItemModel { Q_OBJECT - Q_PROPERTY(QAbstractItemModel* sourceModel READ sourceModel WRITE setSourceModel NOTIFY sourceModelChanged) + Q_PROPERTY(QAbstractItemModel *sourceModel READ sourceModel WRITE setSourceModel + NOTIFY sourceModelChanged BINDABLE bindableSourceModel) public: explicit QAbstractProxyModel(QObject *parent = nullptr); @@ -60,6 +25,7 @@ public: virtual void setSourceModel(QAbstractItemModel *sourceModel); QAbstractItemModel *sourceModel() const; + QBindable<QAbstractItemModel *> bindableSourceModel(); Q_INVOKABLE virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const = 0; Q_INVOKABLE virtual QModelIndex mapFromSource(const QModelIndex &sourceIndex) const = 0; @@ -78,9 +44,7 @@ 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; @@ -98,20 +62,25 @@ public: QStringList mimeTypes() const override; Qt::DropActions supportedDragActions() const override; Qt::DropActions supportedDropActions() const override; + QHash<int, QByteArray> roleNames() const override; Q_SIGNALS: void sourceModelChanged(QPrivateSignal); -protected Q_SLOTS: - void resetInternalData(); - protected: + QModelIndex createSourceIndex(int row, int col, void *internalPtr) const; QAbstractProxyModel(QAbstractProxyModelPrivate &, QObject *parent); private: Q_DECLARE_PRIVATE(QAbstractProxyModel) Q_DISABLE_COPY(QAbstractProxyModel) Q_PRIVATE_SLOT(d_func(), void _q_sourceModelDestroyed()) + Q_PRIVATE_SLOT(d_func(), void _q_sourceModelRowsAboutToBeInserted(QModelIndex, int, int)) + Q_PRIVATE_SLOT(d_func(), void _q_sourceModelRowsInserted(QModelIndex, int, int)) + Q_PRIVATE_SLOT(d_func(), void _q_sourceModelRowsRemoved(QModelIndex, int, int)) + Q_PRIVATE_SLOT(d_func(), void _q_sourceModelColumnsAboutToBeInserted(QModelIndex, int, int)) + Q_PRIVATE_SLOT(d_func(), void _q_sourceModelColumnsInserted(QModelIndex, int, int)) + Q_PRIVATE_SLOT(d_func(), void _q_sourceModelColumnsRemoved(QModelIndex, int, int)) }; QT_END_NAMESPACE diff --git a/src/corelib/itemmodels/qabstractproxymodel_p.h b/src/corelib/itemmodels/qabstractproxymodel_p.h index a95687c970..d33666d00b 100644 --- a/src/corelib/itemmodels/qabstractproxymodel_p.h +++ b/src/corelib/itemmodels/qabstractproxymodel_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QABSTRACTPROXYMODEL_P_H #define QABSTRACTPROXYMODEL_P_H @@ -52,7 +16,9 @@ // // +#include "qabstractproxymodel.h" #include "private/qabstractitemmodel_p.h" +#include "private/qproperty_p.h" QT_REQUIRE_CONFIG(proxymodel); @@ -62,11 +28,45 @@ class Q_CORE_EXPORT QAbstractProxyModelPrivate : public QAbstractItemModelPrivat { Q_DECLARE_PUBLIC(QAbstractProxyModel) public: - QAbstractProxyModelPrivate() : QAbstractItemModelPrivate(), model(nullptr) {} - QAbstractItemModel *model; + QAbstractProxyModelPrivate() + : QAbstractItemModelPrivate(), + sourceHadZeroRows(false), + sourceHadZeroColumns(false), + updateVerticalHeader(false), + updateHorizontalHeader(false) + {} + void setModelForwarder(QAbstractItemModel *sourceModel) + { + q_func()->setSourceModel(sourceModel); + } + void modelChangedForwarder() + { + Q_EMIT q_func()->sourceModelChanged(QAbstractProxyModel::QPrivateSignal()); + } + QAbstractItemModel *getModelForwarder() const { return q_func()->sourceModel(); } + + Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QAbstractProxyModelPrivate, QAbstractItemModel *, model, + &QAbstractProxyModelPrivate::setModelForwarder, + &QAbstractProxyModelPrivate::modelChangedForwarder, + &QAbstractProxyModelPrivate::getModelForwarder, nullptr) virtual void _q_sourceModelDestroyed(); + void _q_sourceModelRowsAboutToBeInserted(const QModelIndex &parent, int first, int last); + void _q_sourceModelRowsInserted(const QModelIndex &parent, int first, int last); + void _q_sourceModelRowsRemoved(const QModelIndex &parent, int first, int last); + void _q_sourceModelColumnsAboutToBeInserted(const QModelIndex &parent, int first, int last); + void _q_sourceModelColumnsInserted(const QModelIndex &parent, int first, int last); + void _q_sourceModelColumnsRemoved(const QModelIndex &parent, int first, int last); + void mapDropCoordinatesToSource(int row, int column, const QModelIndex &parent, int *source_row, int *source_column, QModelIndex *source_parent) const; + + void scheduleHeaderUpdate(Qt::Orientation orientation); + void emitHeaderDataChanged(); + + unsigned int sourceHadZeroRows : 1; + unsigned int sourceHadZeroColumns : 1; + unsigned int updateVerticalHeader : 1; + unsigned int updateHorizontalHeader : 1; }; QT_END_NAMESPACE diff --git a/src/corelib/itemmodels/qconcatenatetablesproxymodel.cpp b/src/corelib/itemmodels/qconcatenatetablesproxymodel.cpp index 3afa132483..3a49d37cff 100644 --- a/src/corelib/itemmodels/qconcatenatetablesproxymodel.cpp +++ b/src/corelib/itemmodels/qconcatenatetablesproxymodel.cpp @@ -1,45 +1,10 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author David Faure <david.faure@kdab.com> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qconcatenatetablesproxymodel.h" #include <private/qabstractitemmodel_p.h> #include "qsize.h" +#include "qmap.h" #include "qdebug.h" QT_BEGIN_NAMESPACE @@ -61,26 +26,43 @@ public: }; 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(); + void slotRowsAboutToBeInserted(const QModelIndex &, int start, int end); + void slotRowsInserted(const QModelIndex &, int start, int end); + void slotRowsAboutToBeRemoved(const QModelIndex &, int start, int end); + void slotRowsRemoved(const QModelIndex &, int start, int end); + void slotColumnsAboutToBeInserted(const QModelIndex &parent, int start, int end); + void slotColumnsInserted(const QModelIndex &parent, int, int); + void slotColumnsAboutToBeRemoved(const QModelIndex &parent, int start, int end); + void slotColumnsRemoved(const QModelIndex &parent, int, int); + void slotDataChanged(const QModelIndex &from, const QModelIndex &to, const QList<int> &roles); + void slotSourceLayoutAboutToBeChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint); + void slotSourceLayoutChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint); + void slotModelAboutToBeReset(); + void 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; + struct ModelInfo { + using ConnArray = std::array<QMetaObject::Connection, 13>; + ModelInfo(QAbstractItemModel *m, ConnArray &&con) + : model(m), connections(std::move(con)) {} + QAbstractItemModel *model = nullptr; + ConnArray connections; + }; + QList<ModelInfo> m_models; + + QList<ModelInfo>::const_iterator findSourceModel(const QAbstractItemModel *m) const + { + auto byModelPtr = [m](const auto &modInfo) { return modInfo.model == m; }; + return std::find_if(m_models.cbegin(), m_models.cend(), byModelPtr); + } + + bool containsSourceModel(const QAbstractItemModel *m) const + { return findSourceModel(m) != m_models.cend(); } + int m_rowCount; // have to maintain it here since we can't compute during model destruction int m_columnCount; @@ -88,8 +70,8 @@ public: int m_newColumnCount; // for layoutAboutToBeChanged/layoutChanged - QVector<QPersistentModelIndex> layoutChangePersistentIndexes; - QVector<QModelIndex> layoutChangeProxyIndexes; + QList<QPersistentModelIndex> layoutChangePersistentIndexes; + QList<QModelIndex> layoutChangeProxyIndexes; }; QConcatenateTablesProxyModelPrivate::QConcatenateTablesProxyModelPrivate() @@ -151,7 +133,7 @@ QModelIndex QConcatenateTablesProxyModel::mapFromSource(const QModelIndex &sourc if (!sourceIndex.isValid()) return QModelIndex(); const QAbstractItemModel *sourceModel = sourceIndex.model(); - if (!d->m_models.contains(const_cast<QAbstractItemModel *>(sourceModel))) { + if (!d->containsSourceModel(sourceModel)) { qWarning("QConcatenateTablesProxyModel: index from wrong model passed to mapFromSource"); Q_ASSERT(!"QConcatenateTablesProxyModel: index from wrong model passed to mapFromSource"); return QModelIndex(); @@ -243,7 +225,7 @@ Qt::ItemFlags QConcatenateTablesProxyModel::flags(const QModelIndex &index) cons return Qt::NoItemFlags; Q_ASSERT(checkIndex(index)); if (!index.isValid()) - return d->m_models.at(0)->flags(index); + return d->m_models.at(0).model->flags(index); const QModelIndex sourceIndex = mapToSource(index); Q_ASSERT(sourceIndex.isValid()); return sourceIndex.model()->flags(sourceIndex); @@ -261,7 +243,7 @@ QVariant QConcatenateTablesProxyModel::headerData(int section, Qt::Orientation o return QVariant(); switch (orientation) { case Qt::Horizontal: - return d->m_models.at(0)->headerData(section, orientation, role); + return d->m_models.at(0).model->headerData(section, orientation, role); case Qt::Vertical: { const auto result = d->sourceModelForRow(section); Q_ASSERT(result.sourceModel); @@ -313,8 +295,8 @@ QModelIndex QConcatenateTablesProxyModel::parent(const QModelIndex &index) const int QConcatenateTablesProxyModel::rowCount(const QModelIndex &parent) const { Q_D(const QConcatenateTablesProxyModel); - Q_ASSERT(checkIndex(parent, QAbstractItemModel::CheckIndexOption::ParentIsInvalid)); // flat model - Q_UNUSED(parent); + if (parent.isValid()) + return 0; // flat model return d->m_rowCount; } @@ -327,7 +309,7 @@ QStringList QConcatenateTablesProxyModel::mimeTypes() const Q_D(const QConcatenateTablesProxyModel); if (d->m_models.isEmpty()) return QStringList(); - return d->m_models.at(0)->mimeTypes(); + return d->m_models.at(0).model->mimeTypes(); } /*! @@ -350,7 +332,7 @@ QMimeData *QConcatenateTablesProxyModel::mimeData(const QModelIndexList &indexes Q_ASSERT(checkIndex(firstIndex, CheckIndexOption::IndexIsValid)); const auto result = d->sourceModelForRow(firstIndex.row()); QModelIndexList sourceIndexes; - sourceIndexes.reserve(indexes.count()); + sourceIndexes.reserve(indexes.size()); for (const QModelIndex &index : indexes) { const QModelIndex sourceIndex = mapToSource(index); Q_ASSERT(sourceIndex.model() == result.sourceModel); // see documentation above @@ -369,7 +351,7 @@ bool QConcatenateTablesProxyModelPrivate::mapDropCoordinatesToSource(int row, in // Drop after the last item if (row == -1 || row == m_rowCount) { *sourceRow = -1; - *sourceModel = m_models.constLast(); + *sourceModel = m_models.constLast().model; return true; } // Drop between toplevel items @@ -448,6 +430,21 @@ QSize QConcatenateTablesProxyModel::span(const QModelIndex &index) const } /*! + Returns a list of models that were added as source models for this proxy model. + + \since 5.15 +*/ +QList<QAbstractItemModel *> QConcatenateTablesProxyModel::sourceModels() const +{ + Q_D(const QConcatenateTablesProxyModel); + QList<QAbstractItemModel *> ret; + ret.reserve(d->m_models.size()); + for (const auto &info : d->m_models) + ret.push_back(info.model); + return ret; +} + +/*! Adds a source model \a sourceModel, below all previously added source models. The ownership of \a sourceModel is not affected by this. @@ -458,30 +455,42 @@ void QConcatenateTablesProxyModel::addSourceModel(QAbstractItemModel *sourceMode { 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())); + Q_ASSERT(!d->containsSourceModel(sourceModel)); 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); + d->m_models.emplace_back(sourceModel, std::array{ + QObjectPrivate::connect(sourceModel, &QAbstractItemModel::dataChanged, + d, &QConcatenateTablesProxyModelPrivate::slotDataChanged), + QObjectPrivate::connect(sourceModel, &QAbstractItemModel::rowsInserted, + d, &QConcatenateTablesProxyModelPrivate::slotRowsInserted), + QObjectPrivate::connect(sourceModel, &QAbstractItemModel::rowsRemoved, + d, &QConcatenateTablesProxyModelPrivate::slotRowsRemoved), + QObjectPrivate::connect(sourceModel, &QAbstractItemModel::rowsAboutToBeInserted, + d, &QConcatenateTablesProxyModelPrivate::slotRowsAboutToBeInserted), + QObjectPrivate::connect(sourceModel, &QAbstractItemModel::rowsAboutToBeRemoved, + d, &QConcatenateTablesProxyModelPrivate::slotRowsAboutToBeRemoved), + + QObjectPrivate::connect(sourceModel, &QAbstractItemModel::columnsInserted, + d, &QConcatenateTablesProxyModelPrivate::slotColumnsInserted), + QObjectPrivate::connect(sourceModel, &QAbstractItemModel::columnsRemoved, + d, &QConcatenateTablesProxyModelPrivate::slotColumnsRemoved), + QObjectPrivate::connect(sourceModel, &QAbstractItemModel::columnsAboutToBeInserted, + d, &QConcatenateTablesProxyModelPrivate::slotColumnsAboutToBeInserted), + QObjectPrivate::connect(sourceModel, &QAbstractItemModel::columnsAboutToBeRemoved, + d, &QConcatenateTablesProxyModelPrivate::slotColumnsAboutToBeRemoved), + + QObjectPrivate::connect(sourceModel, &QAbstractItemModel::layoutAboutToBeChanged, + d, &QConcatenateTablesProxyModelPrivate::slotSourceLayoutAboutToBeChanged), + QObjectPrivate::connect(sourceModel, &QAbstractItemModel::layoutChanged, + d, &QConcatenateTablesProxyModelPrivate::slotSourceLayoutChanged), + QObjectPrivate::connect(sourceModel, &QAbstractItemModel::modelAboutToBeReset, + d, &QConcatenateTablesProxyModelPrivate::slotModelAboutToBeReset), + QObjectPrivate::connect(sourceModel, &QAbstractItemModel::modelReset, + d, &QConcatenateTablesProxyModelPrivate::slotModelReset), + }); if (newRows > 0) endInsertRows(); @@ -496,15 +505,18 @@ void QConcatenateTablesProxyModel::addSourceModel(QAbstractItemModel *sourceMode void QConcatenateTablesProxyModel::removeSourceModel(QAbstractItemModel *sourceModel) { Q_D(QConcatenateTablesProxyModel); - Q_ASSERT(d->m_models.contains(sourceModel)); - disconnect(sourceModel, nullptr, this, nullptr); + + auto it = d->findSourceModel(sourceModel); + Q_ASSERT(it != d->m_models.cend()); + for (auto &c : it->connections) + disconnect(c); 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_models.erase(it); d->m_rowCount -= rowsRemoved; if (rowsRemoved > 0) endRemoveRows(); @@ -512,7 +524,8 @@ void QConcatenateTablesProxyModel::removeSourceModel(QAbstractItemModel *sourceM d->updateColumnCount(); } -void QConcatenateTablesProxyModelPrivate::_q_slotRowsAboutToBeInserted(const QModelIndex &parent, int start, int end) +void QConcatenateTablesProxyModelPrivate::slotRowsAboutToBeInserted(const QModelIndex &parent, + int start, int end) { Q_Q(QConcatenateTablesProxyModel); if (parent.isValid()) // not supported, the proxy is a flat model @@ -522,7 +535,8 @@ void QConcatenateTablesProxyModelPrivate::_q_slotRowsAboutToBeInserted(const QMo q->beginInsertRows(QModelIndex(), rowsPrior + start, rowsPrior + end); } -void QConcatenateTablesProxyModelPrivate::_q_slotRowsInserted(const QModelIndex &parent, int start, int end) +void QConcatenateTablesProxyModelPrivate::slotRowsInserted(const QModelIndex &parent, int start, + int end) { Q_Q(QConcatenateTablesProxyModel); if (parent.isValid()) // flat model @@ -531,7 +545,8 @@ void QConcatenateTablesProxyModelPrivate::_q_slotRowsInserted(const QModelIndex q->endInsertRows(); } -void QConcatenateTablesProxyModelPrivate::_q_slotRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) +void QConcatenateTablesProxyModelPrivate::slotRowsAboutToBeRemoved(const QModelIndex &parent, + int start, int end) { Q_Q(QConcatenateTablesProxyModel); if (parent.isValid()) // flat model @@ -541,7 +556,7 @@ void QConcatenateTablesProxyModelPrivate::_q_slotRowsAboutToBeRemoved(const QMod q->beginRemoveRows(QModelIndex(), rowsPrior + start, rowsPrior + end); } -void QConcatenateTablesProxyModelPrivate::_q_slotRowsRemoved(const QModelIndex &parent, int start, int end) +void QConcatenateTablesProxyModelPrivate::slotRowsRemoved(const QModelIndex &parent, int start, int end) { Q_Q(QConcatenateTablesProxyModel); if (parent.isValid()) // flat model @@ -550,7 +565,8 @@ void QConcatenateTablesProxyModelPrivate::_q_slotRowsRemoved(const QModelIndex & q->endRemoveRows(); } -void QConcatenateTablesProxyModelPrivate::_q_slotColumnsAboutToBeInserted(const QModelIndex &parent, int start, int end) +void QConcatenateTablesProxyModelPrivate::slotColumnsAboutToBeInserted(const QModelIndex &parent, + int start, int end) { Q_Q(QConcatenateTablesProxyModel); if (parent.isValid()) // flat model @@ -566,7 +582,8 @@ void QConcatenateTablesProxyModelPrivate::_q_slotColumnsAboutToBeInserted(const m_newColumnCount = newColCount; } -void QConcatenateTablesProxyModelPrivate::_q_slotColumnsInserted(const QModelIndex &parent, int start, int end) +void QConcatenateTablesProxyModelPrivate::slotColumnsInserted(const QModelIndex &parent, int start, + int end) { Q_UNUSED(start); Q_UNUSED(end); @@ -579,7 +596,8 @@ void QConcatenateTablesProxyModelPrivate::_q_slotColumnsInserted(const QModelInd } } -void QConcatenateTablesProxyModelPrivate::_q_slotColumnsAboutToBeRemoved(const QModelIndex &parent, int start, int end) +void QConcatenateTablesProxyModelPrivate::slotColumnsAboutToBeRemoved(const QModelIndex &parent, + int start, int end) { Q_Q(QConcatenateTablesProxyModel); if (parent.isValid()) // flat model @@ -593,7 +611,8 @@ void QConcatenateTablesProxyModelPrivate::_q_slotColumnsAboutToBeRemoved(const Q m_newColumnCount = newColCount; } -void QConcatenateTablesProxyModelPrivate::_q_slotColumnsRemoved(const QModelIndex &parent, int start, int end) +void QConcatenateTablesProxyModelPrivate::slotColumnsRemoved(const QModelIndex &parent, int start, + int end) { Q_Q(QConcatenateTablesProxyModel); Q_UNUSED(start); @@ -606,19 +625,27 @@ void QConcatenateTablesProxyModelPrivate::_q_slotColumnsRemoved(const QModelInde } } -void QConcatenateTablesProxyModelPrivate::_q_slotDataChanged(const QModelIndex &from, const QModelIndex &to, const QVector<int> &roles) +void QConcatenateTablesProxyModelPrivate::slotDataChanged(const QModelIndex &from, + const QModelIndex &to, + const QList<int> &roles) { Q_Q(QConcatenateTablesProxyModel); Q_ASSERT(from.isValid()); Q_ASSERT(to.isValid()); + if (from.column() >= m_columnCount) + return; + QModelIndex adjustedTo = to; + if (to.column() >= m_columnCount) + adjustedTo = to.siblingAtColumn(m_columnCount - 1); const QModelIndex myFrom = q->mapFromSource(from); Q_ASSERT(q->checkIndex(myFrom, QAbstractItemModel::CheckIndexOption::IndexIsValid)); - const QModelIndex myTo = q->mapFromSource(to); + const QModelIndex myTo = q->mapFromSource(adjustedTo); 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) +void QConcatenateTablesProxyModelPrivate::slotSourceLayoutAboutToBeChanged( + const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint) { Q_Q(QConcatenateTablesProxyModel); @@ -631,7 +658,7 @@ void QConcatenateTablesProxyModelPrivate::_q_slotSourceLayoutAboutToBeChanged(co layoutChangePersistentIndexes.reserve(persistentIndexList.size()); layoutChangeProxyIndexes.reserve(persistentIndexList.size()); - for (const QPersistentModelIndex &proxyPersistentIndex : persistentIndexList) { + for (const QModelIndex &proxyPersistentIndex : persistentIndexList) { layoutChangeProxyIndexes.append(proxyPersistentIndex); Q_ASSERT(proxyPersistentIndex.isValid()); const QPersistentModelIndex srcPersistentIndex = q->mapToSource(proxyPersistentIndex); @@ -640,7 +667,8 @@ void QConcatenateTablesProxyModelPrivate::_q_slotSourceLayoutAboutToBeChanged(co } } -void QConcatenateTablesProxyModelPrivate::_q_slotSourceLayoutChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint) +void QConcatenateTablesProxyModelPrivate::slotSourceLayoutChanged( + const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint) { Q_Q(QConcatenateTablesProxyModel); if (!sourceParents.isEmpty() && !sourceParents.contains(QModelIndex())) @@ -657,20 +685,20 @@ void QConcatenateTablesProxyModelPrivate::_q_slotSourceLayoutChanged(const QList emit q->layoutChanged({}, hint); } -void QConcatenateTablesProxyModelPrivate::_q_slotModelAboutToBeReset() +void QConcatenateTablesProxyModelPrivate::slotModelAboutToBeReset() { Q_Q(QConcatenateTablesProxyModel); - Q_ASSERT(m_models.contains(const_cast<QAbstractItemModel *>(static_cast<const QAbstractItemModel *>(q->sender())))); + Q_ASSERT(containsSourceModel(static_cast<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() +void QConcatenateTablesProxyModelPrivate::slotModelReset() { Q_Q(QConcatenateTablesProxyModel); - Q_ASSERT(m_models.contains(const_cast<QAbstractItemModel *>(static_cast<const QAbstractItemModel *>(q->sender())))); + Q_ASSERT(containsSourceModel(static_cast<QAbstractItemModel *>(q->sender()))); m_columnCount = calculatedColumnCount(); m_rowCount = computeRowsPrior(nullptr); q->endResetModel(); @@ -681,10 +709,11 @@ 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(); + auto byColumnCount = [](const auto &a, const auto &b) { + return a.model->columnCount() < b.model->columnCount(); + }; + const auto it = std::min_element(m_models.begin(), m_models.end(), byColumnCount); + return it->model->columnCount(); } void QConcatenateTablesProxyModelPrivate::updateColumnCount() @@ -707,8 +736,8 @@ void QConcatenateTablesProxyModelPrivate::updateColumnCount() 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); + for (qsizetype i = 0; i < m_models.size(); ++i) { + const QAbstractItemModel *mod = m_models.at(i).model; const int colCount = mod == model ? newCount : mod->columnCount(); if (i == 0) newColumnCount = colCount; @@ -721,7 +750,7 @@ int QConcatenateTablesProxyModelPrivate::columnCountAfterChange(const QAbstractI int QConcatenateTablesProxyModelPrivate::computeRowsPrior(const QAbstractItemModel *sourceModel) const { int rowsPrior = 0; - for (const QAbstractItemModel *model : m_models) { + for (const auto &[model, _] : m_models) { if (model == sourceModel) break; rowsPrior += model->rowCount(); @@ -733,7 +762,7 @@ QConcatenateTablesProxyModelPrivate::SourceModelForRowResult QConcatenateTablesP { QConcatenateTablesProxyModelPrivate::SourceModelForRowResult result; int rowCount = 0; - for (QAbstractItemModel *model : m_models) { + for (const auto &[model, _] : m_models) { const int subRowCount = model->rowCount(); if (rowCount + subRowCount > row) { result.sourceModel = model; diff --git a/src/corelib/itemmodels/qconcatenatetablesproxymodel.h b/src/corelib/itemmodels/qconcatenatetablesproxymodel.h index 69b3a2ba09..9dbebd7b88 100644 --- a/src/corelib/itemmodels/qconcatenatetablesproxymodel.h +++ b/src/corelib/itemmodels/qconcatenatetablesproxymodel.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author David Faure <david.faure@kdab.com> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QCONCATENATEROWSPROXYMODEL_H #define QCONCATENATEROWSPROXYMODEL_H @@ -56,6 +20,7 @@ public: explicit QConcatenateTablesProxyModel(QObject *parent = nullptr); ~QConcatenateTablesProxyModel(); + QList<QAbstractItemModel *> sourceModels() const; Q_SCRIPTABLE void addSourceModel(QAbstractItemModel *sourceModel); Q_SCRIPTABLE void removeSourceModel(QAbstractItemModel *sourceModel); @@ -81,20 +46,6 @@ public: 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 diff --git a/src/corelib/itemmodels/qidentityproxymodel.cpp b/src/corelib/itemmodels/qidentityproxymodel.cpp index f5684c6eda..89fa7e5c07 100644 --- a/src/corelib/itemmodels/qidentityproxymodel.cpp +++ b/src/corelib/itemmodels/qidentityproxymodel.cpp @@ -1,84 +1,13 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Stephen Kelly <stephen.kelly@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$ -** -****************************************************************************/ +// Copyright (C) 2011 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Stephen Kelly <stephen.kelly@kdab.com> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qidentityproxymodel.h" +#include "qidentityproxymodel_p.h" #include "qitemselectionmodel.h" #include <private/qabstractproxymodel_p.h> QT_BEGIN_NAMESPACE -class QIdentityProxyModelPrivate : public QAbstractProxyModelPrivate -{ - QIdentityProxyModelPrivate() - { - - } - - Q_DECLARE_PUBLIC(QIdentityProxyModel) - - QList<QPersistentModelIndex> layoutChangePersistentIndexes; - QModelIndexList proxyIndexes; - - void _q_sourceRowsAboutToBeInserted(const QModelIndex &parent, int start, int end); - void _q_sourceRowsInserted(const QModelIndex &parent, int start, int end); - void _q_sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); - void _q_sourceRowsRemoved(const QModelIndex &parent, int start, int end); - void _q_sourceRowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest); - void _q_sourceRowsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest); - - void _q_sourceColumnsAboutToBeInserted(const QModelIndex &parent, int start, int end); - void _q_sourceColumnsInserted(const QModelIndex &parent, int start, int end); - void _q_sourceColumnsAboutToBeRemoved(const QModelIndex &parent, int start, int end); - void _q_sourceColumnsRemoved(const QModelIndex &parent, int start, int end); - void _q_sourceColumnsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest); - void _q_sourceColumnsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest); - - void _q_sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles); - void _q_sourceHeaderDataChanged(Qt::Orientation orientation, int first, int last); - - void _q_sourceLayoutAboutToBeChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint); - void _q_sourceLayoutChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint); - void _q_sourceModelAboutToBeReset(); - void _q_sourceModelReset(); - -}; - /*! \since 4.8 \class QIdentityProxyModel @@ -215,7 +144,7 @@ QItemSelection QIdentityProxyModel::mapSelectionFromSource(const QItemSelection& QItemSelection::const_iterator it = selection.constBegin(); const QItemSelection::const_iterator end = selection.constEnd(); - proxySelection.reserve(selection.count()); + proxySelection.reserve(selection.size()); for ( ; it != end; ++it) { Q_ASSERT(it->model() == d->model); const QItemSelectionRange range(mapFromSource(it->topLeft()), mapFromSource(it->bottomRight())); @@ -238,7 +167,7 @@ QItemSelection QIdentityProxyModel::mapSelectionToSource(const QItemSelection& s QItemSelection::const_iterator it = selection.constBegin(); const QItemSelection::const_iterator end = selection.constEnd(); - sourceSelection.reserve(selection.count()); + sourceSelection.reserve(selection.size()); for ( ; it != end; ++it) { Q_ASSERT(it->model() == this); const QItemSelectionRange range(mapToSource(it->topLeft()), mapToSource(it->bottomRight())); @@ -257,7 +186,7 @@ QModelIndex QIdentityProxyModel::mapToSource(const QModelIndex& proxyIndex) cons if (!d->model || !proxyIndex.isValid()) return QModelIndex(); Q_ASSERT(proxyIndex.model() == this); - return d->model->createIndex(proxyIndex.row(), proxyIndex.column(), proxyIndex.internalPointer()); + return createSourceIndex(proxyIndex.row(), proxyIndex.column(), proxyIndex.internalPointer()); } /*! @@ -274,7 +203,7 @@ QModelIndexList QIdentityProxyModel::match(const QModelIndex& start, int role, c QModelIndexList::const_iterator it = sourceList.constBegin(); const QModelIndexList::const_iterator end = sourceList.constEnd(); QModelIndexList proxyList; - proxyList.reserve(sourceList.count()); + proxyList.reserve(sourceList.size()); for ( ; it != end; ++it) proxyList.append(mapFromSource(*it)); return proxyList; @@ -361,97 +290,108 @@ void QIdentityProxyModel::setSourceModel(QAbstractItemModel* newSourceModel) { beginResetModel(); - if (sourceModel()) { - disconnect(sourceModel(), SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), - this, SLOT(_q_sourceRowsAboutToBeInserted(QModelIndex,int,int))); - disconnect(sourceModel(), SIGNAL(rowsInserted(QModelIndex,int,int)), - this, SLOT(_q_sourceRowsInserted(QModelIndex,int,int))); - disconnect(sourceModel(), SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), - this, SLOT(_q_sourceRowsAboutToBeRemoved(QModelIndex,int,int))); - disconnect(sourceModel(), SIGNAL(rowsRemoved(QModelIndex,int,int)), - this, SLOT(_q_sourceRowsRemoved(QModelIndex,int,int))); - disconnect(sourceModel(), SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)), - this, SLOT(_q_sourceRowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int))); - disconnect(sourceModel(), SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), - this, SLOT(_q_sourceRowsMoved(QModelIndex,int,int,QModelIndex,int))); - disconnect(sourceModel(), SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)), - this, SLOT(_q_sourceColumnsAboutToBeInserted(QModelIndex,int,int))); - disconnect(sourceModel(), SIGNAL(columnsInserted(QModelIndex,int,int)), - this, SLOT(_q_sourceColumnsInserted(QModelIndex,int,int))); - disconnect(sourceModel(), SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)), - this, SLOT(_q_sourceColumnsAboutToBeRemoved(QModelIndex,int,int))); - disconnect(sourceModel(), SIGNAL(columnsRemoved(QModelIndex,int,int)), - this, SLOT(_q_sourceColumnsRemoved(QModelIndex,int,int))); - disconnect(sourceModel(), SIGNAL(columnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)), - this, SLOT(_q_sourceColumnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int))); - disconnect(sourceModel(), SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)), - this, SLOT(_q_sourceColumnsMoved(QModelIndex,int,int,QModelIndex,int))); - disconnect(sourceModel(), SIGNAL(modelAboutToBeReset()), - this, SLOT(_q_sourceModelAboutToBeReset())); - disconnect(sourceModel(), SIGNAL(modelReset()), - this, SLOT(_q_sourceModelReset())); - disconnect(sourceModel(), SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)), - this, SLOT(_q_sourceDataChanged(QModelIndex,QModelIndex,QVector<int>))); - disconnect(sourceModel(), SIGNAL(headerDataChanged(Qt::Orientation,int,int)), - this, SLOT(_q_sourceHeaderDataChanged(Qt::Orientation,int,int))); - disconnect(sourceModel(), SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), - this, SLOT(_q_sourceLayoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); - disconnect(sourceModel(), SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), - this, SLOT(_q_sourceLayoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); - } + Q_D(QIdentityProxyModel); + + // Call QObject::disconnect() unconditionally, if there is an existing source + // model, it's disconnected, and if there isn't, then calling disconnect() on + // a default-constructed Connection does nothing + for (const auto &c : d->m_sourceModelConnections) + QObject::disconnect(c); QAbstractProxyModel::setSourceModel(newSourceModel); if (sourceModel()) { - connect(sourceModel(), SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), - SLOT(_q_sourceRowsAboutToBeInserted(QModelIndex,int,int))); - connect(sourceModel(), SIGNAL(rowsInserted(QModelIndex,int,int)), - SLOT(_q_sourceRowsInserted(QModelIndex,int,int))); - connect(sourceModel(), SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), - SLOT(_q_sourceRowsAboutToBeRemoved(QModelIndex,int,int))); - connect(sourceModel(), SIGNAL(rowsRemoved(QModelIndex,int,int)), - SLOT(_q_sourceRowsRemoved(QModelIndex,int,int))); - connect(sourceModel(), SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)), - SLOT(_q_sourceRowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int))); - connect(sourceModel(), SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), - SLOT(_q_sourceRowsMoved(QModelIndex,int,int,QModelIndex,int))); - connect(sourceModel(), SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)), - SLOT(_q_sourceColumnsAboutToBeInserted(QModelIndex,int,int))); - connect(sourceModel(), SIGNAL(columnsInserted(QModelIndex,int,int)), - SLOT(_q_sourceColumnsInserted(QModelIndex,int,int))); - connect(sourceModel(), SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)), - SLOT(_q_sourceColumnsAboutToBeRemoved(QModelIndex,int,int))); - connect(sourceModel(), SIGNAL(columnsRemoved(QModelIndex,int,int)), - SLOT(_q_sourceColumnsRemoved(QModelIndex,int,int))); - connect(sourceModel(), SIGNAL(columnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)), - SLOT(_q_sourceColumnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int))); - connect(sourceModel(), SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)), - SLOT(_q_sourceColumnsMoved(QModelIndex,int,int,QModelIndex,int))); - connect(sourceModel(), SIGNAL(modelAboutToBeReset()), - SLOT(_q_sourceModelAboutToBeReset())); - connect(sourceModel(), SIGNAL(modelReset()), - SLOT(_q_sourceModelReset())); - connect(sourceModel(), SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)), - SLOT(_q_sourceDataChanged(QModelIndex,QModelIndex,QVector<int>))); - connect(sourceModel(), SIGNAL(headerDataChanged(Qt::Orientation,int,int)), - SLOT(_q_sourceHeaderDataChanged(Qt::Orientation,int,int))); - connect(sourceModel(), SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), - SLOT(_q_sourceLayoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); - connect(sourceModel(), SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), - SLOT(_q_sourceLayoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); + auto *m = sourceModel(); + d->m_sourceModelConnections = { + QObjectPrivate::connect(m, &QAbstractItemModel::rowsAboutToBeInserted, d, + &QIdentityProxyModelPrivate::sourceRowsAboutToBeInserted), + QObjectPrivate::connect(m, &QAbstractItemModel::rowsInserted, d, + &QIdentityProxyModelPrivate::sourceRowsInserted), + QObjectPrivate::connect(m, &QAbstractItemModel::rowsAboutToBeRemoved, d, + &QIdentityProxyModelPrivate::sourceRowsAboutToBeRemoved), + QObjectPrivate::connect(m, &QAbstractItemModel::rowsRemoved, d, + &QIdentityProxyModelPrivate::sourceRowsRemoved), + QObjectPrivate::connect(m, &QAbstractItemModel::rowsAboutToBeMoved, d, + &QIdentityProxyModelPrivate::sourceRowsAboutToBeMoved), + QObjectPrivate::connect(m, &QAbstractItemModel::rowsMoved, d, + &QIdentityProxyModelPrivate::sourceRowsMoved), + QObjectPrivate::connect(m, &QAbstractItemModel::columnsAboutToBeInserted, d, + &QIdentityProxyModelPrivate::sourceColumnsAboutToBeInserted), + QObjectPrivate::connect(m, &QAbstractItemModel::columnsInserted, d, + &QIdentityProxyModelPrivate::sourceColumnsInserted), + QObjectPrivate::connect(m, &QAbstractItemModel::columnsAboutToBeRemoved, d, + &QIdentityProxyModelPrivate::sourceColumnsAboutToBeRemoved), + QObjectPrivate::connect(m, &QAbstractItemModel::columnsRemoved, d, + &QIdentityProxyModelPrivate::sourceColumnsRemoved), + QObjectPrivate::connect(m, &QAbstractItemModel::columnsAboutToBeMoved, d, + &QIdentityProxyModelPrivate::sourceColumnsAboutToBeMoved), + QObjectPrivate::connect(m, &QAbstractItemModel::columnsMoved, d, + &QIdentityProxyModelPrivate::sourceColumnsMoved), + QObjectPrivate::connect(m, &QAbstractItemModel::modelAboutToBeReset, d, + &QIdentityProxyModelPrivate::sourceModelAboutToBeReset), + QObjectPrivate::connect(m, &QAbstractItemModel::modelReset, d, + &QIdentityProxyModelPrivate::sourceModelReset), + QObjectPrivate::connect(m, &QAbstractItemModel::dataChanged, d, + &QIdentityProxyModelPrivate::sourceDataChanged), + QObjectPrivate::connect(m, &QAbstractItemModel::headerDataChanged, d, + &QIdentityProxyModelPrivate::sourceHeaderDataChanged), + }; + + if (d->m_handleLayoutChanges) { + d->m_sourceModelConnections.emplace_back( + QObjectPrivate::connect(m, &QAbstractItemModel::layoutAboutToBeChanged, d, + &QIdentityProxyModelPrivate::sourceLayoutAboutToBeChanged)); + d->m_sourceModelConnections.emplace_back( + QObjectPrivate::connect(m, &QAbstractItemModel::layoutChanged, d, + &QIdentityProxyModelPrivate::sourceLayoutChanged)); + } } endResetModel(); } -void QIdentityProxyModelPrivate::_q_sourceColumnsAboutToBeInserted(const QModelIndex &parent, int start, int end) +/*! + \since 6.7 + + If \a b is \c true, this proxy model will handle the source model layout + changes (by connecting to \c QAbstractItemModel::layoutAboutToBeChanged + and \c QAbstractItemModel::layoutChanged singals). + + The default is for this proxy model to handle the source model layout + changes. + + In sub-classes of QIdentityProxyModel, it may be useful to set this to + \c false if you need to specially handle the source model layout changes. + + \note Calling this method will only have an effect after calling setSourceModel(). +*/ +void QIdentityProxyModel::setHandleSourceLayoutChanges(bool b) +{ + d_func()->m_handleLayoutChanges = b; +} + +/*! + \since 6.7 + + Returns \c true if this proxy model handles the source model layout + changes, otherwise returns \c false. +*/ +bool QIdentityProxyModel::isHandleSourceLayoutChanges() const +{ + return d_func()->m_handleLayoutChanges; +} + +void QIdentityProxyModelPrivate::sourceColumnsAboutToBeInserted(const QModelIndex &parent, int start, int end) { Q_ASSERT(parent.isValid() ? parent.model() == model : true); Q_Q(QIdentityProxyModel); q->beginInsertColumns(q->mapFromSource(parent), start, end); } -void QIdentityProxyModelPrivate::_q_sourceColumnsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest) +void QIdentityProxyModelPrivate::sourceColumnsAboutToBeMoved(const QModelIndex &sourceParent, + int sourceStart, int sourceEnd, + const QModelIndex &destParent, + int dest) { Q_ASSERT(sourceParent.isValid() ? sourceParent.model() == model : true); Q_ASSERT(destParent.isValid() ? destParent.model() == model : true); @@ -459,47 +399,51 @@ void QIdentityProxyModelPrivate::_q_sourceColumnsAboutToBeMoved(const QModelInde q->beginMoveColumns(q->mapFromSource(sourceParent), sourceStart, sourceEnd, q->mapFromSource(destParent), dest); } -void QIdentityProxyModelPrivate::_q_sourceColumnsAboutToBeRemoved(const QModelIndex &parent, int start, int end) +void QIdentityProxyModelPrivate::sourceColumnsAboutToBeRemoved(const QModelIndex &parent, int start, int end) { Q_ASSERT(parent.isValid() ? parent.model() == model : true); Q_Q(QIdentityProxyModel); q->beginRemoveColumns(q->mapFromSource(parent), start, end); } -void QIdentityProxyModelPrivate::_q_sourceColumnsInserted(const QModelIndex &parent, int start, int end) +void QIdentityProxyModelPrivate::sourceColumnsInserted(const QModelIndex &parent, int start, int end) { Q_ASSERT(parent.isValid() ? parent.model() == model : true); Q_Q(QIdentityProxyModel); - Q_UNUSED(parent) - Q_UNUSED(start) - Q_UNUSED(end) + Q_UNUSED(parent); + Q_UNUSED(start); + Q_UNUSED(end); q->endInsertColumns(); } -void QIdentityProxyModelPrivate::_q_sourceColumnsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest) +void QIdentityProxyModelPrivate::sourceColumnsMoved(const QModelIndex &sourceParent, + int sourceStart, int sourceEnd, + const QModelIndex &destParent, int dest) { Q_ASSERT(sourceParent.isValid() ? sourceParent.model() == model : true); Q_ASSERT(destParent.isValid() ? destParent.model() == model : true); Q_Q(QIdentityProxyModel); - Q_UNUSED(sourceParent) - Q_UNUSED(sourceStart) - Q_UNUSED(sourceEnd) - Q_UNUSED(destParent) - Q_UNUSED(dest) + Q_UNUSED(sourceParent); + Q_UNUSED(sourceStart); + Q_UNUSED(sourceEnd); + Q_UNUSED(destParent); + Q_UNUSED(dest); q->endMoveColumns(); } -void QIdentityProxyModelPrivate::_q_sourceColumnsRemoved(const QModelIndex &parent, int start, int end) +void QIdentityProxyModelPrivate::sourceColumnsRemoved(const QModelIndex &parent, int start, int end) { Q_ASSERT(parent.isValid() ? parent.model() == model : true); Q_Q(QIdentityProxyModel); - Q_UNUSED(parent) - Q_UNUSED(start) - Q_UNUSED(end) + Q_UNUSED(parent); + Q_UNUSED(start); + Q_UNUSED(end); q->endRemoveColumns(); } -void QIdentityProxyModelPrivate::_q_sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles) +void QIdentityProxyModelPrivate::sourceDataChanged(const QModelIndex &topLeft, + const QModelIndex &bottomRight, + const QList<int> &roles) { Q_ASSERT(topLeft.isValid() ? topLeft.model() == model : true); Q_ASSERT(bottomRight.isValid() ? bottomRight.model() == model : true); @@ -507,13 +451,15 @@ void QIdentityProxyModelPrivate::_q_sourceDataChanged(const QModelIndex &topLeft emit q->dataChanged(q->mapFromSource(topLeft), q->mapFromSource(bottomRight), roles); } -void QIdentityProxyModelPrivate::_q_sourceHeaderDataChanged(Qt::Orientation orientation, int first, int last) +void QIdentityProxyModelPrivate::sourceHeaderDataChanged(Qt::Orientation orientation, int first, + int last) { Q_Q(QIdentityProxyModel); emit q->headerDataChanged(orientation, first, last); } -void QIdentityProxyModelPrivate::_q_sourceLayoutAboutToBeChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint) +void QIdentityProxyModelPrivate::sourceLayoutAboutToBeChanged( + const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint) { Q_Q(QIdentityProxyModel); @@ -532,7 +478,7 @@ void QIdentityProxyModelPrivate::_q_sourceLayoutAboutToBeChanged(const QList<QPe emit q->layoutAboutToBeChanged(parents, hint); const auto proxyPersistentIndexes = q->persistentIndexList(); - for (const QPersistentModelIndex &proxyPersistentIndex : proxyPersistentIndexes) { + for (const QModelIndex &proxyPersistentIndex : proxyPersistentIndexes) { proxyIndexes << proxyPersistentIndex; Q_ASSERT(proxyPersistentIndex.isValid()); const QPersistentModelIndex srcPersistentIndex = q->mapToSource(proxyPersistentIndex); @@ -541,7 +487,8 @@ void QIdentityProxyModelPrivate::_q_sourceLayoutAboutToBeChanged(const QList<QPe } } -void QIdentityProxyModelPrivate::_q_sourceLayoutChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint) +void QIdentityProxyModelPrivate::sourceLayoutChanged( + const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint) { Q_Q(QIdentityProxyModel); @@ -567,26 +514,29 @@ void QIdentityProxyModelPrivate::_q_sourceLayoutChanged(const QList<QPersistentM emit q->layoutChanged(parents, hint); } -void QIdentityProxyModelPrivate::_q_sourceModelAboutToBeReset() +void QIdentityProxyModelPrivate::sourceModelAboutToBeReset() { Q_Q(QIdentityProxyModel); q->beginResetModel(); } -void QIdentityProxyModelPrivate::_q_sourceModelReset() +void QIdentityProxyModelPrivate::sourceModelReset() { Q_Q(QIdentityProxyModel); q->endResetModel(); } -void QIdentityProxyModelPrivate::_q_sourceRowsAboutToBeInserted(const QModelIndex &parent, int start, int end) +void QIdentityProxyModelPrivate::sourceRowsAboutToBeInserted(const QModelIndex &parent, int start, + int end) { Q_ASSERT(parent.isValid() ? parent.model() == model : true); Q_Q(QIdentityProxyModel); q->beginInsertRows(q->mapFromSource(parent), start, end); } -void QIdentityProxyModelPrivate::_q_sourceRowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest) +void QIdentityProxyModelPrivate::sourceRowsAboutToBeMoved(const QModelIndex &sourceParent, + int sourceStart, int sourceEnd, + const QModelIndex &destParent, int dest) { Q_ASSERT(sourceParent.isValid() ? sourceParent.model() == model : true); Q_ASSERT(destParent.isValid() ? destParent.model() == model : true); @@ -594,43 +544,46 @@ void QIdentityProxyModelPrivate::_q_sourceRowsAboutToBeMoved(const QModelIndex & q->beginMoveRows(q->mapFromSource(sourceParent), sourceStart, sourceEnd, q->mapFromSource(destParent), dest); } -void QIdentityProxyModelPrivate::_q_sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) +void QIdentityProxyModelPrivate::sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, + int end) { Q_ASSERT(parent.isValid() ? parent.model() == model : true); Q_Q(QIdentityProxyModel); q->beginRemoveRows(q->mapFromSource(parent), start, end); } -void QIdentityProxyModelPrivate::_q_sourceRowsInserted(const QModelIndex &parent, int start, int end) +void QIdentityProxyModelPrivate::sourceRowsInserted(const QModelIndex &parent, int start, int end) { Q_ASSERT(parent.isValid() ? parent.model() == model : true); Q_Q(QIdentityProxyModel); - Q_UNUSED(parent) - Q_UNUSED(start) - Q_UNUSED(end) + Q_UNUSED(parent); + Q_UNUSED(start); + Q_UNUSED(end); q->endInsertRows(); } -void QIdentityProxyModelPrivate::_q_sourceRowsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest) +void QIdentityProxyModelPrivate::sourceRowsMoved(const QModelIndex &sourceParent, int sourceStart, + int sourceEnd, const QModelIndex &destParent, + int dest) { Q_ASSERT(sourceParent.isValid() ? sourceParent.model() == model : true); Q_ASSERT(destParent.isValid() ? destParent.model() == model : true); Q_Q(QIdentityProxyModel); - Q_UNUSED(sourceParent) - Q_UNUSED(sourceStart) - Q_UNUSED(sourceEnd) - Q_UNUSED(destParent) - Q_UNUSED(dest) + Q_UNUSED(sourceParent); + Q_UNUSED(sourceStart); + Q_UNUSED(sourceEnd); + Q_UNUSED(destParent); + Q_UNUSED(dest); q->endMoveRows(); } -void QIdentityProxyModelPrivate::_q_sourceRowsRemoved(const QModelIndex &parent, int start, int end) +void QIdentityProxyModelPrivate::sourceRowsRemoved(const QModelIndex &parent, int start, int end) { Q_ASSERT(parent.isValid() ? parent.model() == model : true); Q_Q(QIdentityProxyModel); - Q_UNUSED(parent) - Q_UNUSED(start) - Q_UNUSED(end) + Q_UNUSED(parent); + Q_UNUSED(start); + Q_UNUSED(end); q->endRemoveRows(); } diff --git a/src/corelib/itemmodels/qidentityproxymodel.h b/src/corelib/itemmodels/qidentityproxymodel.h index 4c14e6176a..c8fc9d21b7 100644 --- a/src/corelib/itemmodels/qidentityproxymodel.h +++ b/src/corelib/itemmodels/qidentityproxymodel.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Stephen Kelly <stephen.kelly@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$ -** -****************************************************************************/ +// Copyright (C) 2011 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Stephen Kelly <stephen.kelly@kdab.com> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QIDENTITYPROXYMODEL_H @@ -80,34 +44,15 @@ public: bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) override; bool moveColumns(const QModelIndex &sourceParent, int sourceColumn, int count, const QModelIndex &destinationParent, int destinationChild) override; + bool isHandleSourceLayoutChanges() const; + protected: QIdentityProxyModel(QIdentityProxyModelPrivate &dd, QObject* parent); + void setHandleSourceLayoutChanges(bool); private: Q_DECLARE_PRIVATE(QIdentityProxyModel) Q_DISABLE_COPY(QIdentityProxyModel) - - Q_PRIVATE_SLOT(d_func(), void _q_sourceRowsAboutToBeInserted(QModelIndex,int,int)) - Q_PRIVATE_SLOT(d_func(), void _q_sourceRowsInserted(QModelIndex,int,int)) - Q_PRIVATE_SLOT(d_func(), void _q_sourceRowsAboutToBeRemoved(QModelIndex,int,int)) - Q_PRIVATE_SLOT(d_func(), void _q_sourceRowsRemoved(QModelIndex,int,int)) - Q_PRIVATE_SLOT(d_func(), void _q_sourceRowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)) - Q_PRIVATE_SLOT(d_func(), void _q_sourceRowsMoved(QModelIndex,int,int,QModelIndex,int)) - - Q_PRIVATE_SLOT(d_func(), void _q_sourceColumnsAboutToBeInserted(QModelIndex,int,int)) - Q_PRIVATE_SLOT(d_func(), void _q_sourceColumnsInserted(QModelIndex,int,int)) - Q_PRIVATE_SLOT(d_func(), void _q_sourceColumnsAboutToBeRemoved(QModelIndex,int,int)) - Q_PRIVATE_SLOT(d_func(), void _q_sourceColumnsRemoved(QModelIndex,int,int)) - Q_PRIVATE_SLOT(d_func(), void _q_sourceColumnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)) - Q_PRIVATE_SLOT(d_func(), void _q_sourceColumnsMoved(QModelIndex,int,int,QModelIndex,int)) - - Q_PRIVATE_SLOT(d_func(), void _q_sourceDataChanged(QModelIndex,QModelIndex,QVector<int>)) - Q_PRIVATE_SLOT(d_func(), void _q_sourceHeaderDataChanged(Qt::Orientation orientation, int first, int last)) - - Q_PRIVATE_SLOT(d_func(), void _q_sourceLayoutAboutToBeChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint)) - Q_PRIVATE_SLOT(d_func(), void _q_sourceLayoutChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint)) - Q_PRIVATE_SLOT(d_func(), void _q_sourceModelAboutToBeReset()) - Q_PRIVATE_SLOT(d_func(), void _q_sourceModelReset()) }; QT_END_NAMESPACE diff --git a/src/corelib/itemmodels/qidentityproxymodel_p.h b/src/corelib/itemmodels/qidentityproxymodel_p.h new file mode 100644 index 0000000000..78e1f5316c --- /dev/null +++ b/src/corelib/itemmodels/qidentityproxymodel_p.h @@ -0,0 +1,72 @@ +// Copyright (C) 2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Stephen Kelly <stephen.kelly@kdab.com> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QIDENTITYPROXYMODEL_P_H +#define QIDENTITYPROXYMODEL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of QAbstractItemModel*. This header file may change from version +// to version without notice, or even be removed. +// +// We mean it. +// +// + +#include <QtCore/private/qabstractproxymodel_p.h> +#include <QtCore/qidentityproxymodel.h> + +QT_BEGIN_NAMESPACE + +class Q_CORE_EXPORT QIdentityProxyModelPrivate : public QAbstractProxyModelPrivate +{ + Q_DECLARE_PUBLIC(QIdentityProxyModel) + +public: + QIdentityProxyModelPrivate() + { + } + + QList<QPersistentModelIndex> layoutChangePersistentIndexes; + QModelIndexList proxyIndexes; + + void sourceRowsAboutToBeInserted(const QModelIndex &parent, int start, int end); + void sourceRowsInserted(const QModelIndex &parent, int start, int end); + void sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); + void sourceRowsRemoved(const QModelIndex &parent, int start, int end); + void sourceRowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, + const QModelIndex &destParent, int dest); + void sourceRowsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, + const QModelIndex &destParent, int dest); + + void sourceColumnsAboutToBeInserted(const QModelIndex &parent, int start, int end); + void sourceColumnsInserted(const QModelIndex &parent, int start, int end); + void sourceColumnsAboutToBeRemoved(const QModelIndex &parent, int start, int end); + void sourceColumnsRemoved(const QModelIndex &parent, int start, int end); + void sourceColumnsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, + int sourceEnd, const QModelIndex &destParent, int dest); + void sourceColumnsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, + const QModelIndex &destParent, int dest); + + void sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, + const QList<int> &roles); + void sourceHeaderDataChanged(Qt::Orientation orientation, int first, int last); + + void sourceLayoutAboutToBeChanged(const QList<QPersistentModelIndex> &sourceParents, + QAbstractItemModel::LayoutChangeHint hint); + void sourceLayoutChanged(const QList<QPersistentModelIndex> &sourceParents, + QAbstractItemModel::LayoutChangeHint hint); + void sourceModelAboutToBeReset(); + void sourceModelReset(); + +private: + bool m_handleLayoutChanges = true; + QVarLengthArray<QMetaObject::Connection, 18> m_sourceModelConnections; +}; + +QT_END_NAMESPACE + +#endif // QIDENTITYPROXYMODEL_P_H diff --git a/src/corelib/itemmodels/qitemselectionmodel.cpp b/src/corelib/itemmodels/qitemselectionmodel.cpp index e4ac5da299..6df60aaf61 100644 --- a/src/corelib/itemmodels/qitemselectionmodel.cpp +++ b/src/corelib/itemmodels/qitemselectionmodel.cpp @@ -1,44 +1,13 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qitemselectionmodel.h" +#include "qitemselectionmodel_p.h" + #include <private/qitemselectionmodel_p.h> +#include <private/qabstractitemmodel_p.h> +#include <private/qduplicatetracker_p.h> +#include <private/qoffsetstringarray_p.h> #include <qdebug.h> #include <algorithm> @@ -46,6 +15,9 @@ QT_BEGIN_NAMESPACE +QT_IMPL_METATYPE_EXTERN(QItemSelectionRange) +QT_IMPL_METATYPE_EXTERN(QItemSelection) + /*! \class QItemSelectionRange \inmodule QtCore @@ -55,6 +27,8 @@ QT_BEGIN_NAMESPACE \ingroup model-view + \compares equality + A QItemSelectionRange contains information about a range of selected items in a model. A range of items is a contiguous array of model items, extending to cover a number of adjacent rows and @@ -86,14 +60,6 @@ QT_BEGIN_NAMESPACE */ /*! - \fn QItemSelectionRange::QItemSelectionRange(const QItemSelectionRange &other) - - Copy constructor. Constructs a new selection range with the same contents - as the \a other range given. - -*/ - -/*! \fn QItemSelectionRange::QItemSelectionRange(const QModelIndex &topLeft, const QModelIndex &bottomRight) Constructs a new selection range containing only the index specified @@ -230,13 +196,6 @@ bool QItemSelectionRange::intersects(const QItemSelectionRange &other) const } /*! - \fn QItemSelectionRange QItemSelectionRange::intersect(const QItemSelectionRange &other) const - \obsolete - - Use intersected(\a other) instead. -*/ - -/*! \fn QItemSelectionRange QItemSelectionRange::intersected(const QItemSelectionRange &other) const \since 4.2 @@ -259,68 +218,29 @@ QItemSelectionRange QItemSelectionRange::intersected(const QItemSelectionRange & } /*! - \fn bool QItemSelectionRange::operator==(const QItemSelectionRange &other) const + \fn bool QItemSelectionRange::operator==(const QItemSelectionRange &lhs, const QItemSelectionRange &rhs) - Returns \c true if the selection range is exactly the same as the \a other + Returns \c true if \a lhs selection range is exactly the same as the \a rhs range given; otherwise returns \c false. */ /*! - \fn bool QItemSelectionRange::operator!=(const QItemSelectionRange &other) const + \fn bool QItemSelectionRange::operator!=(const QItemSelectionRange &lhs, const QItemSelectionRange &rhs) - Returns \c true if the selection range differs from the \a other range given; + Returns \c true if \a lhs selection range differs from the \a rhs range given; otherwise returns \c false. */ /*! - Returns \c true if the selection range is less than the \a other - range given; otherwise returns \c false. - - The less than calculation is not directly useful to developers - the way that ranges - with different parents compare is not defined. This operator only exists so that the - class can be used with QMap. - -*/ -bool QItemSelectionRange::operator<(const QItemSelectionRange &other) const -{ - // ### Qt 6: This is inconsistent with op== and needs to be fixed, nay, - // ### removed, but cannot, because it was inline up to and including 5.9 - - // Comparing parents will compare the models, but if two equivalent ranges - // in two different models have invalid parents, they would appear the same - if (other.tl.model() == tl.model()) { - // parent has to be calculated, so we only do so once. - const QModelIndex topLeftParent = tl.parent(); - const QModelIndex otherTopLeftParent = other.tl.parent(); - if (topLeftParent == otherTopLeftParent) { - if (other.tl.row() == tl.row()) { - if (other.tl.column() == tl.column()) { - if (other.br.row() == br.row()) { - return br.column() < other.br.column(); - } - return br.row() < other.br.row(); - } - return tl.column() < other.tl.column(); - } - return tl.row() < other.tl.row(); - } - return topLeftParent < otherTopLeftParent; - } - - std::less<const QAbstractItemModel *> less; - return less(tl.model(), other.tl.model()); -} - -/*! \fn bool QItemSelectionRange::isValid() const Returns \c true if the selection range is valid; otherwise returns \c false. */ -static void rowLengthsFromRange(const QItemSelectionRange &range, QVector<QPair<QPersistentModelIndex, uint> > &result) +static void rowLengthsFromRange(const QItemSelectionRange &range, QList<std::pair<QPersistentModelIndex, uint>> &result) { if (range.isValid() && range.model()) { const QModelIndex topLeft = range.topLeft(); @@ -331,11 +251,16 @@ static void rowLengthsFromRange(const QItemSelectionRange &range, QVector<QPair< // We don't need to keep track of ItemIsSelectable and ItemIsEnabled here. That is // required in indexesFromRange() because that method is called from public API // which requires the limitation. - result.push_back(qMakePair(QPersistentModelIndex(topLeft.sibling(row, column)), width)); + result.emplace_back(topLeft.sibling(row, column), width); } } } +static bool isSelectableAndEnabled(Qt::ItemFlags flags) +{ + return flags.testFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); +} + template<typename ModelIndexContainer> static void indexesFromRange(const QItemSelectionRange &range, ModelIndexContainer &result) { @@ -347,8 +272,7 @@ static void indexesFromRange(const QItemSelectionRange &range, ModelIndexContain const QModelIndex columnLeader = topLeft.sibling(row, topLeft.column()); for (int column = topLeft.column(); column <= right; ++column) { QModelIndex index = columnLeader.sibling(row, column); - Qt::ItemFlags flags = range.model()->flags(index); - if ((flags & Qt::ItemIsSelectable) && (flags & Qt::ItemIsEnabled)) + if (isSelectableAndEnabled(range.model()->flags(index))) result.push_back(index); } } @@ -365,7 +289,9 @@ static ModelIndexContainer qSelectionIndexes(const QItemSelection &selection) } /*! - Returns \c true if the selection range contains no selectable item + Returns \c true if the selection range contains either no items + or only items which are either disabled or marked as not selectable. + \since 4.7 */ @@ -377,8 +303,7 @@ bool QItemSelectionRange::isEmpty() const for (int column = left(); column <= right(); ++column) { for (int row = top(); row <= bottom(); ++row) { QModelIndex index = model()->index(row, column, parent()); - Qt::ItemFlags flags = model()->flags(index); - if ((flags & Qt::ItemIsSelectable) && (flags & Qt::ItemIsEnabled)) + if (isSelectableAndEnabled(model()->flags(index))) return false; } } @@ -491,7 +416,7 @@ void QItemSelection::select(const QModelIndex &topLeft, const QModelIndex &botto bool QItemSelection::contains(const QModelIndex &index) const { - if (index.flags() & Qt::ItemIsSelectable) { + if (isSelectableAndEnabled(index.flags())) { QList<QItemSelectionRange>::const_iterator it = begin(); for (; it != end(); ++it) if ((*it).contains(index)) @@ -509,9 +434,9 @@ QModelIndexList QItemSelection::indexes() const return qSelectionIndexes<QModelIndexList>(*this); } -static QVector<QPair<QPersistentModelIndex, uint> > qSelectionPersistentRowLengths(const QItemSelection &sel) +static QList<std::pair<QPersistentModelIndex, uint>> qSelectionPersistentRowLengths(const QItemSelection &sel) { - QVector<QPair<QPersistentModelIndex, uint> > result; + QList<std::pair<QPersistentModelIndex, uint>> result; for (const QItemSelectionRange &range : sel) rowLengthsFromRange(range, result); return result; @@ -535,25 +460,23 @@ void QItemSelection::merge(const QItemSelection &other, QItemSelectionModel::Sel command & QItemSelectionModel::Toggle)) return; - QItemSelection newSelection = other; + QItemSelection newSelection; + newSelection.reserve(other.size()); // Collect intersections QItemSelection intersections; - QItemSelection::iterator it = newSelection.begin(); - while (it != newSelection.end()) { - if (!(*it).isValid()) { - it = newSelection.erase(it); + for (const auto &range : other) { + if (!range.isValid()) continue; + newSelection.push_back(range); + for (int t = 0; t < size(); ++t) { + if (range.intersects(at(t))) + intersections.append(at(t).intersected(range)); } - for (int t = 0; t < count(); ++t) { - if ((*it).intersects(at(t))) - intersections.append(at(t).intersected(*it)); - } - ++it; } // Split the old (and new) ranges using the intersections - for (int i = 0; i < intersections.count(); ++i) { // for each intersection - for (int t = 0; t < count();) { // splitt each old range + for (int i = 0; i < intersections.size(); ++i) { // for each intersection + for (int t = 0; t < size();) { // splitt each old range if (at(t).intersects(intersections.at(i))) { split(at(t), intersections.at(i), this); removeAt(t); @@ -562,7 +485,7 @@ void QItemSelection::merge(const QItemSelection &other, QItemSelectionModel::Sel } } // only split newSelection if Toggle is specified - for (int n = 0; (command & QItemSelectionModel::Toggle) && n < newSelection.count();) { + for (int n = 0; (command & QItemSelectionModel::Toggle) && n < newSelection.size();) { if (newSelection.at(n).intersects(intersections.at(i))) { split(newSelection.at(n), intersections.at(i), &newSelection); newSelection.removeAt(n); @@ -629,52 +552,55 @@ void QItemSelection::split(const QItemSelectionRange &range, void QItemSelectionModelPrivate::initModel(QAbstractItemModel *m) { - struct Cx { - const char *signal; - const char *slot; - }; - static const Cx connections[] = { - { SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), - SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int)) }, - { SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)), - SLOT(_q_columnsAboutToBeRemoved(QModelIndex,int,int)) }, - { SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), - SLOT(_q_rowsAboutToBeInserted(QModelIndex,int,int)) }, - { SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)), - SLOT(_q_columnsAboutToBeInserted(QModelIndex,int,int)) }, - { SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)), - SLOT(_q_layoutAboutToBeChanged()) }, - { SIGNAL(columnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)), - SLOT(_q_layoutAboutToBeChanged()) }, - { SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), - SLOT(_q_layoutChanged()) }, - { SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)), - SLOT(_q_layoutChanged()) }, - { SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), - SLOT(_q_layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)) }, - { SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), - SLOT(_q_layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)) }, - { SIGNAL(modelReset()), - SLOT(reset()) }, - { nullptr, nullptr } - }; - - if (model == m) + Q_Q(QItemSelectionModel); + const QAbstractItemModel *oldModel = model.valueBypassingBindings(); + if (oldModel == m) return; - Q_Q(QItemSelectionModel); - if (model) { - for (const Cx *cx = &connections[0]; cx->signal; cx++) - QObject::disconnect(model, cx->signal, q, cx->slot); + if (oldModel) { q->reset(); + disconnectModel(); } - model = m; - if (model) { - for (const Cx *cx = &connections[0]; cx->signal; cx++) - QObject::connect(model, cx->signal, q, cx->slot); + + // Caller has to call notify(), unless calling during construction (the common case). + model.setValueBypassingBindings(m); + + if (m) { + connections = std::array<QMetaObject::Connection, 12> { + QObjectPrivate::connect(m, &QAbstractItemModel::rowsAboutToBeRemoved, + this, &QItemSelectionModelPrivate::rowsAboutToBeRemoved), + QObjectPrivate::connect(m, &QAbstractItemModel::columnsAboutToBeRemoved, + this, &QItemSelectionModelPrivate::columnsAboutToBeRemoved), + QObjectPrivate::connect(m, &QAbstractItemModel::rowsAboutToBeInserted, + this, &QItemSelectionModelPrivate::rowsAboutToBeInserted), + QObjectPrivate::connect(m, &QAbstractItemModel::columnsAboutToBeInserted, + this, &QItemSelectionModelPrivate::columnsAboutToBeInserted), + QObjectPrivate::connect(m, &QAbstractItemModel::rowsAboutToBeMoved, + this, &QItemSelectionModelPrivate::triggerLayoutToBeChanged), + QObjectPrivate::connect(m, &QAbstractItemModel::columnsAboutToBeMoved, + this, &QItemSelectionModelPrivate::triggerLayoutToBeChanged), + QObjectPrivate::connect(m, &QAbstractItemModel::rowsMoved, + this, &QItemSelectionModelPrivate::triggerLayoutChanged), + QObjectPrivate::connect(m, &QAbstractItemModel::columnsMoved, + this, &QItemSelectionModelPrivate::triggerLayoutChanged), + QObjectPrivate::connect(m, &QAbstractItemModel::layoutAboutToBeChanged, + this, &QItemSelectionModelPrivate::layoutAboutToBeChanged), + QObjectPrivate::connect(m, &QAbstractItemModel::layoutChanged, + this, &QItemSelectionModelPrivate::layoutChanged), + QObject::connect(m, &QAbstractItemModel::modelReset, + q, &QItemSelectionModel::reset), + QObjectPrivate::connect(m, &QAbstractItemModel::destroyed, + this, &QItemSelectionModelPrivate::modelDestroyed) + }; } } +void QItemSelectionModelPrivate::disconnectModel() +{ + for (auto &connection : connections) + QObject::disconnect(connection); +} + /*! \internal @@ -692,7 +618,7 @@ QItemSelection QItemSelectionModelPrivate::expandSelection(const QItemSelection QItemSelection expanded; if (command & QItemSelectionModel::Rows) { - for (int i = 0; i < selection.count(); ++i) { + for (int i = 0; i < selection.size(); ++i) { QModelIndex parent = selection.at(i).parent(); int colCount = model->columnCount(parent); QModelIndex tl = model->index(selection.at(i).top(), 0, parent); @@ -702,7 +628,7 @@ QItemSelection QItemSelectionModelPrivate::expandSelection(const QItemSelection } } if (command & QItemSelectionModel::Columns) { - for (int i = 0; i < selection.count(); ++i) { + for (int i = 0; i < selection.size(); ++i) { QModelIndex parent = selection.at(i).parent(); int rowCount = model->rowCount(parent); QModelIndex tl = model->index(0, selection.at(i).left(), parent); @@ -717,22 +643,27 @@ QItemSelection QItemSelectionModelPrivate::expandSelection(const QItemSelection /*! \internal */ -void QItemSelectionModelPrivate::_q_rowsAboutToBeRemoved(const QModelIndex &parent, +void QItemSelectionModelPrivate::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) { Q_Q(QItemSelectionModel); + Q_ASSERT(start <= end); finalize(); // update current index if (currentIndex.isValid() && parent == currentIndex.parent() && currentIndex.row() >= start && currentIndex.row() <= end) { QModelIndex old = currentIndex; - if (start > 0) // there are rows left above the change + if (start > 0) { + // there are rows left above the change currentIndex = model->index(start - 1, old.column(), parent); - else if (model && end < model->rowCount(parent) - 1) // there are rows left below the change + } else if (model.value() && end < model->rowCount(parent) - 1) { + // there are rows left below the change currentIndex = model->index(end + 1, old.column(), parent); - else // there are no rows left in the table + } else { + // there are no rows left in the table currentIndex = QModelIndex(); + } emit q->currentChanged(currentIndex, old); emit q->currentRowChanged(currentIndex, old); if (currentIndex.column() != old.column()) @@ -741,6 +672,7 @@ void QItemSelectionModelPrivate::_q_rowsAboutToBeRemoved(const QModelIndex &pare QItemSelection deselected; QItemSelection newParts; + bool indexesOfSelectionChanged = false; QItemSelection::iterator it = ranges.begin(); while (it != ranges.end()) { if (it->topLeft().parent() != parent) { // Check parents until reaching root or contained in range @@ -752,6 +684,8 @@ void QItemSelectionModelPrivate::_q_rowsAboutToBeRemoved(const QModelIndex &pare deselected.append(*it); it = ranges.erase(it); } else { + if (itParent.isValid() && end < itParent.row()) + indexesOfSelectionChanged = true; ++it; } } else if (start <= it->bottom() && it->bottom() <= end // Full inclusion @@ -776,19 +710,23 @@ void QItemSelectionModelPrivate::_q_rowsAboutToBeRemoved(const QModelIndex &pare deselected.append(removedRange); QItemSelection::split(*it, removedRange, &newParts); it = ranges.erase(it); - } else + } else if (end < it->top()) { // deleted row before selection + indexesOfSelectionChanged = true; + ++it; + } else { ++it; + } } ranges.append(newParts); - if (!deselected.isEmpty()) + if (!deselected.isEmpty() || indexesOfSelectionChanged) emit q->selectionChanged(QItemSelection(), deselected); } /*! \internal */ -void QItemSelectionModelPrivate::_q_columnsAboutToBeRemoved(const QModelIndex &parent, +void QItemSelectionModelPrivate::columnsAboutToBeRemoved(const QModelIndex &parent, int start, int end) { Q_Q(QItemSelectionModel); @@ -797,12 +735,16 @@ void QItemSelectionModelPrivate::_q_columnsAboutToBeRemoved(const QModelIndex &p if (currentIndex.isValid() && parent == currentIndex.parent() && currentIndex.column() >= start && currentIndex.column() <= end) { QModelIndex old = currentIndex; - if (start > 0) // there are columns to the left of the change + if (start > 0) { + // there are columns to the left of the change currentIndex = model->index(old.row(), start - 1, parent); - else if (model && end < model->columnCount() - 1) // there are columns to the right of the change + } else if (model.value() && end < model->columnCount() - 1) { + // there are columns to the right of the change currentIndex = model->index(old.row(), end + 1, parent); - else // there are no columns left in the table + } else { + // there are no columns left in the table currentIndex = QModelIndex(); + } emit q->currentChanged(currentIndex, old); if (currentIndex.row() != old.row()) emit q->currentRowChanged(currentIndex, old); @@ -821,7 +763,7 @@ void QItemSelectionModelPrivate::_q_columnsAboutToBeRemoved(const QModelIndex &p Split selection ranges if columns are about to be inserted in the middle. */ -void QItemSelectionModelPrivate::_q_columnsAboutToBeInserted(const QModelIndex &parent, +void QItemSelectionModelPrivate::columnsAboutToBeInserted(const QModelIndex &parent, int start, int end) { Q_UNUSED(end); @@ -829,11 +771,12 @@ void QItemSelectionModelPrivate::_q_columnsAboutToBeInserted(const QModelIndex & QList<QItemSelectionRange> split; QList<QItemSelectionRange>::iterator it = ranges.begin(); for (; it != ranges.end(); ) { - if ((*it).isValid() && (*it).parent() == parent + const QModelIndex &itParent = it->parent(); + if ((*it).isValid() && itParent == parent && (*it).left() < start && (*it).right() >= start) { - QModelIndex bottomMiddle = model->index((*it).bottom(), start - 1, (*it).parent()); + QModelIndex bottomMiddle = model->index((*it).bottom(), start - 1, itParent); QItemSelectionRange left((*it).topLeft(), bottomMiddle); - QModelIndex topMiddle = model->index((*it).top(), start, (*it).parent()); + QModelIndex topMiddle = model->index((*it).top(), start, itParent); QItemSelectionRange right(topMiddle, (*it).bottomRight()); it = ranges.erase(it); split.append(left); @@ -850,28 +793,38 @@ void QItemSelectionModelPrivate::_q_columnsAboutToBeInserted(const QModelIndex & Split selection ranges if rows are about to be inserted in the middle. */ -void QItemSelectionModelPrivate::_q_rowsAboutToBeInserted(const QModelIndex &parent, +void QItemSelectionModelPrivate::rowsAboutToBeInserted(const QModelIndex &parent, int start, int end) { + Q_Q(QItemSelectionModel); Q_UNUSED(end); finalize(); QList<QItemSelectionRange> split; QList<QItemSelectionRange>::iterator it = ranges.begin(); + bool indexesOfSelectionChanged = false; for (; it != ranges.end(); ) { - if ((*it).isValid() && (*it).parent() == parent + const QModelIndex &itParent = it->parent(); + if ((*it).isValid() && itParent == parent && (*it).top() < start && (*it).bottom() >= start) { - QModelIndex middleRight = model->index(start - 1, (*it).right(), (*it).parent()); + QModelIndex middleRight = model->index(start - 1, (*it).right(), itParent); QItemSelectionRange top((*it).topLeft(), middleRight); - QModelIndex middleLeft = model->index(start, (*it).left(), (*it).parent()); + QModelIndex middleLeft = model->index(start, (*it).left(), itParent); QItemSelectionRange bottom(middleLeft, (*it).bottomRight()); it = ranges.erase(it); split.append(top); split.append(bottom); + } else if ((*it).isValid() && itParent == parent // insertion before selection + && (*it).top() >= start) { + indexesOfSelectionChanged = true; + ++it; } else { ++it; } } ranges += split; + + if (indexesOfSelectionChanged) + emit q->selectionChanged(QItemSelection(), QItemSelection()); } /*! @@ -881,7 +834,8 @@ void QItemSelectionModelPrivate::_q_rowsAboutToBeInserted(const QModelIndex &par preparation for the layoutChanged() signal, where the indexes can be merged again. */ -void QItemSelectionModelPrivate::_q_layoutAboutToBeChanged(const QList<QPersistentModelIndex> &, QAbstractItemModel::LayoutChangeHint hint) +void QItemSelectionModelPrivate::layoutAboutToBeChanged(const QList<QPersistentModelIndex> &, + QAbstractItemModel::LayoutChangeHint hint) { savedPersistentIndexes.clear(); savedPersistentCurrentIndexes.clear(); @@ -890,7 +844,7 @@ void QItemSelectionModelPrivate::_q_layoutAboutToBeChanged(const QList<QPersiste // optimization for when all indexes are selected // (only if there is lots of items (1000) because this is not entirely correct) - if (ranges.isEmpty() && currentSelection.count() == 1) { + if (ranges.isEmpty() && currentSelection.size() == 1) { QItemSelectionRange range = currentSelection.constFirst(); QModelIndex parent = range.parent(); tableRowCount = model->rowCount(parent); @@ -916,21 +870,21 @@ void QItemSelectionModelPrivate::_q_layoutAboutToBeChanged(const QList<QPersiste savedPersistentRowLengths = qSelectionPersistentRowLengths(ranges); savedPersistentCurrentRowLengths = qSelectionPersistentRowLengths(currentSelection); } else { - savedPersistentIndexes = qSelectionIndexes<QVector<QPersistentModelIndex>>(ranges); - savedPersistentCurrentIndexes = qSelectionIndexes<QVector<QPersistentModelIndex>>(currentSelection); + savedPersistentIndexes = qSelectionIndexes<QList<QPersistentModelIndex>>(ranges); + savedPersistentCurrentIndexes = qSelectionIndexes<QList<QPersistentModelIndex>>(currentSelection); } } /*! \internal */ -static QItemSelection mergeRowLengths(const QVector<QPair<QPersistentModelIndex, uint> > &rowLengths) +static QItemSelection mergeRowLengths(const QList<std::pair<QPersistentModelIndex, uint>> &rowLengths) { if (rowLengths.isEmpty()) return QItemSelection(); QItemSelection result; int i = 0; - while (i < rowLengths.count()) { + while (i < rowLengths.size()) { const QPersistentModelIndex &tl = rowLengths.at(i).first; if (!tl.isValid()) { ++i; @@ -938,7 +892,7 @@ static QItemSelection mergeRowLengths(const QVector<QPair<QPersistentModelIndex, } QPersistentModelIndex br = tl; const uint length = rowLengths.at(i).second; - while (++i < rowLengths.count()) { + while (++i < rowLengths.size()) { const QPersistentModelIndex &next = rowLengths.at(i).first; if (!next.isValid()) continue; @@ -963,12 +917,12 @@ static QItemSelection mergeRowLengths(const QVector<QPair<QPersistentModelIndex, Merges \a indexes into an item selection made up of ranges. Assumes that the indexes are sorted. */ -static QItemSelection mergeIndexes(const QVector<QPersistentModelIndex> &indexes) +static QItemSelection mergeIndexes(const QList<QPersistentModelIndex> &indexes) { QItemSelection colSpans; // merge columns int i = 0; - while (i < indexes.count()) { + while (i < indexes.size()) { const QPersistentModelIndex &tl = indexes.at(i); if (!tl.isValid()) { ++i; @@ -978,7 +932,7 @@ static QItemSelection mergeIndexes(const QVector<QPersistentModelIndex> &indexes QModelIndex brParent = br.parent(); int brRow = br.row(); int brColumn = br.column(); - while (++i < indexes.count()) { + while (++i < indexes.size()) { const QPersistentModelIndex &next = indexes.at(i); if (!next.isValid()) continue; @@ -1001,11 +955,11 @@ static QItemSelection mergeIndexes(const QVector<QPersistentModelIndex> &indexes // merge rows QItemSelection rowSpans; i = 0; - while (i < colSpans.count()) { + while (i < colSpans.size()) { QModelIndex tl = colSpans.at(i).topLeft(); QModelIndex br = colSpans.at(i).bottomRight(); QModelIndex prevTl = tl; - while (++i < colSpans.count()) { + while (++i < colSpans.size()) { QModelIndex nextTl = colSpans.at(i).topLeft(); QModelIndex nextBr = colSpans.at(i).bottomRight(); @@ -1028,7 +982,7 @@ static QItemSelection mergeIndexes(const QVector<QPersistentModelIndex> &indexes /*! \internal - Sort predicate function for QItemSelectionModelPrivate::_q_layoutChanged(), + Sort predicate function for QItemSelectionModelPrivate::layoutChanged(), sorting by parent first in addition to operator<(). This is to prevent fragmentation of the selection by grouping indexes with the same row, column of different parents next to each other, which may happen when a selection @@ -1046,7 +1000,7 @@ static bool qt_PersistentModelIndexLessThan(const QPersistentModelIndex &i1, con Merge the selected indexes into selection ranges again. */ -void QItemSelectionModelPrivate::_q_layoutChanged(const QList<QPersistentModelIndex> &, QAbstractItemModel::LayoutChangeHint hint) +void QItemSelectionModelPrivate::layoutChanged(const QList<QPersistentModelIndex> &, QAbstractItemModel::LayoutChangeHint hint) { // special case for when all indexes are selected if (tableSelected && tableColCount == model->columnCount(tableParent) @@ -1104,6 +1058,40 @@ void QItemSelectionModelPrivate::_q_layoutChanged(const QList<QPersistentModelIn } /*! + \internal + + Called when the used model gets destroyed. + + It is impossible to have a correct implementation here. + In the following situation, there are two contradicting rules: + + \code + QProperty<QAbstractItemModel *> leader(mymodel); + QItemSelectionModel myItemSelectionModel; + myItemSelectionModel.bindableModel().setBinding([&](){ return leader.value(); } + delete mymodel; + QAbstractItemModel *returnedModel = myItemSelectionModel.model(); + \endcode + + What should returnedModel be in this situation? + + Rules for bindable properties say that myItemSelectionModel.model() + should return the same as leader.value(), namely the pointer to the now deleted model. + + However, backward compatibility requires myItemSelectionModel.model() to return a + nullptr, because that was done in the past after the model used was deleted. + + We decide to break the new rule, imposed by bindable properties, and not break the old + rule, because that may break existing code. +*/ +void QItemSelectionModelPrivate::modelDestroyed() +{ + model.setValueBypassingBindings(nullptr); + disconnectModel(); + model.notify(); +} + +/*! \class QItemSelectionModel \inmodule QtCore @@ -1139,7 +1127,7 @@ void QItemSelectionModelPrivate::_q_layoutChanged(const QList<QPersistentModelIn \l{QItemSelectionModel::hasSelection()}{hasSelection}, and \l{QItemSelectionModel::currentIndex()}{currentIndex} are meta-object properties. - \sa {Model/View Programming}, QAbstractItemModel, {Chart Example} + \sa {Model/View Programming}, QAbstractItemModel */ /*! @@ -1232,6 +1220,11 @@ void QItemSelectionModel::select(const QModelIndex &index, QItemSelectionModel:: Note the that the current index changes independently from the selection. Also note that this signal will not be emitted when the item model is reset. + Items which stay selected but change their index are not included in + \a selected and \a deselected. Thus, this signal might be emitted with both + \a selected and \a deselected empty, if only the indices of selected items + change. + \sa select(), currentChanged() */ @@ -1273,10 +1266,10 @@ struct IsNotValid { typedef bool result_type; struct is_transparent : std::true_type {}; template <typename T> - Q_DECL_CONSTEXPR bool operator()(T &t) const noexcept(noexcept(t.isValid())) + constexpr bool operator()(T &t) const noexcept(noexcept(t.isValid())) { return !t.isValid(); } template <typename T> - Q_DECL_CONSTEXPR bool operator()(T *t) const noexcept(noexcept(t->isValid())) + constexpr bool operator()(T *t) const noexcept(noexcept(t->isValid())) { return !t->isValid(); } }; } @@ -1291,7 +1284,7 @@ struct IsNotValid { void QItemSelectionModel::select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command) { Q_D(QItemSelectionModel); - if (!d->model) { + if (!d->model.value()) { qWarning("QItemSelectionModel: Selecting when no model has been set will result in a no-op."); return; } @@ -1303,11 +1296,9 @@ void QItemSelectionModel::select(const QItemSelection &selection, QItemSelection // If d->ranges is non-empty when the source model is reset the persistent indexes // it contains will be invalid. We can't clear them in a modelReset slot because that might already // be too late if another model observer is connected to the same modelReset slot and is invoked first - // it might call select() on this selection model before any such QItemSelectionModelPrivate::_q_modelReset() slot + // it might call select() on this selection model before any such QItemSelectionModelPrivate::modelReset() slot // is invoked, so it would not be cleared yet. We clear it invalid ranges in it here. - using namespace QtFunctionObjects; - d->ranges.erase(std::remove_if(d->ranges.begin(), d->ranges.end(), IsNotValid()), - d->ranges.end()); + d->ranges.removeIf(QtFunctionObjects::IsNotValid()); QItemSelection old = d->ranges; old.merge(d->currentSelection, d->currentCommand); @@ -1378,7 +1369,7 @@ void QItemSelectionModel::reset() void QItemSelectionModel::clearSelection() { Q_D(QItemSelectionModel); - if (d->ranges.count() == 0 && d->currentSelection.count() == 0) + if (d->ranges.size() == 0 && d->currentSelection.size() == 0) return; select(QItemSelection(), Clear); @@ -1398,7 +1389,7 @@ void QItemSelectionModel::clearSelection() void QItemSelectionModel::setCurrentIndex(const QModelIndex &index, QItemSelectionModel::SelectionFlags command) { Q_D(QItemSelectionModel); - if (!d->model) { + if (!d->model.value()) { qWarning("QItemSelectionModel: Setting the current index when no model has been set will result in a no-op."); return; } @@ -1449,7 +1440,7 @@ bool QItemSelectionModel::isSelected(const QModelIndex &index) const } // check currentSelection - if (d->currentSelection.count()) { + if (d->currentSelection.size()) { if ((d->currentCommand & Deselect) && selected) selected = !d->currentSelection.contains(index); else if (d->currentCommand & Toggle) @@ -1458,10 +1449,8 @@ bool QItemSelectionModel::isSelected(const QModelIndex &index) const selected = d->currentSelection.contains(index); } - if (selected) { - Qt::ItemFlags flags = d->model->flags(index); - return (flags & Qt::ItemIsSelectable); - } + if (selected) + return isSelectableAndEnabled(d->model->flags(index)); return false; } @@ -1473,18 +1462,21 @@ bool QItemSelectionModel::isSelected(const QModelIndex &index) const Note that this function is usually faster than calling isSelected() on all items in the same row and that unselectable items are ignored. + + \note Since Qt 5.15, the default argument for \a parent is an empty + model index. */ bool QItemSelectionModel::isRowSelected(int row, const QModelIndex &parent) const { Q_D(const QItemSelectionModel); - if (!d->model) + if (!d->model.value()) return false; if (parent.isValid() && d->model != parent.model()) return false; // return false if row exist in currentSelection (Deselect) - if (d->currentCommand & Deselect && d->currentSelection.count()) { - for (int i=0; i<d->currentSelection.count(); ++i) { + if (d->currentCommand & Deselect && d->currentSelection.size()) { + for (int i=0; i<d->currentSelection.size(); ++i) { if (d->currentSelection.at(i).parent() == parent && row >= d->currentSelection.at(i).top() && row <= d->currentSelection.at(i).bottom()) @@ -1493,19 +1485,18 @@ bool QItemSelectionModel::isRowSelected(int row, const QModelIndex &parent) cons } // return false if ranges in both currentSelection and ranges // intersect and have the same row contained - if (d->currentCommand & Toggle && d->currentSelection.count()) { - for (int i=0; i<d->currentSelection.count(); ++i) + if (d->currentCommand & Toggle && d->currentSelection.size()) { + for (int i=0; i<d->currentSelection.size(); ++i) if (d->currentSelection.at(i).top() <= row && d->currentSelection.at(i).bottom() >= row) - for (int j=0; j<d->ranges.count(); ++j) + for (int j=0; j<d->ranges.size(); ++j) if (d->ranges.at(j).top() <= row && d->ranges.at(j).bottom() >= row && d->currentSelection.at(i).intersected(d->ranges.at(j)).isValid()) return false; } auto isSelectable = [&](int row, int column) { - Qt::ItemFlags flags = d->model->index(row, column, parent).flags(); - return (flags & Qt::ItemIsSelectable); + return isSelectableAndEnabled(d->model->index(row, column, parent).flags()); }; const int colCount = d->model->columnCount(parent); @@ -1513,7 +1504,7 @@ bool QItemSelectionModel::isRowSelected(int row, const QModelIndex &parent) cons // add ranges and currentSelection and check through them all QList<QItemSelectionRange>::const_iterator it; QList<QItemSelectionRange> joined = d->ranges; - if (d->currentSelection.count()) + if (d->currentSelection.size()) joined += d->currentSelection; for (int column = 0; column < colCount; ++column) { if (!isSelectable(row, column)) { @@ -1545,18 +1536,21 @@ bool QItemSelectionModel::isRowSelected(int row, const QModelIndex &parent) cons Note that this function is usually faster than calling isSelected() on all items in the same column and that unselectable items are ignored. + + \note Since Qt 5.15, the default argument for \a parent is an empty + model index. */ bool QItemSelectionModel::isColumnSelected(int column, const QModelIndex &parent) const { Q_D(const QItemSelectionModel); - if (!d->model) + if (!d->model.value()) return false; if (parent.isValid() && d->model != parent.model()) return false; // return false if column exist in currentSelection (Deselect) - if (d->currentCommand & Deselect && d->currentSelection.count()) { - for (int i = 0; i < d->currentSelection.count(); ++i) { + if (d->currentCommand & Deselect && d->currentSelection.size()) { + for (int i = 0; i < d->currentSelection.size(); ++i) { if (d->currentSelection.at(i).parent() == parent && column >= d->currentSelection.at(i).left() && column <= d->currentSelection.at(i).right()) @@ -1565,11 +1559,11 @@ bool QItemSelectionModel::isColumnSelected(int column, const QModelIndex &parent } // return false if ranges in both currentSelection and the selection model // intersect and have the same column contained - if (d->currentCommand & Toggle && d->currentSelection.count()) { - for (int i = 0; i < d->currentSelection.count(); ++i) { + if (d->currentCommand & Toggle && d->currentSelection.size()) { + for (int i = 0; i < d->currentSelection.size(); ++i) { if (d->currentSelection.at(i).left() <= column && d->currentSelection.at(i).right() >= column) { - for (int j = 0; j < d->ranges.count(); ++j) { + for (int j = 0; j < d->ranges.size(); ++j) { if (d->ranges.at(j).left() <= column && d->ranges.at(j).right() >= column && d->currentSelection.at(i).intersected(d->ranges.at(j)).isValid()) { return false; @@ -1580,8 +1574,7 @@ bool QItemSelectionModel::isColumnSelected(int column, const QModelIndex &parent } auto isSelectable = [&](int row, int column) { - Qt::ItemFlags flags = d->model->index(row, column, parent).flags(); - return (flags & Qt::ItemIsSelectable); + return isSelectableAndEnabled(d->model->index(row, column, parent).flags()); }; const int rowCount = d->model->rowCount(parent); int unselectable = 0; @@ -1589,7 +1582,7 @@ bool QItemSelectionModel::isColumnSelected(int column, const QModelIndex &parent // add ranges and currentSelection and check through them all QList<QItemSelectionRange>::const_iterator it; QList<QItemSelectionRange> joined = d->ranges; - if (d->currentSelection.count()) + if (d->currentSelection.size()) joined += d->currentSelection; for (int row = 0; row < rowCount; ++row) { if (!isSelectable(row, column)) { @@ -1616,18 +1609,21 @@ bool QItemSelectionModel::isColumnSelected(int column, const QModelIndex &parent /*! Returns \c true if there are any items selected in the \a row with the given \a parent. + + \note Since Qt 5.15, the default argument for \a parent is an empty + model index. */ bool QItemSelectionModel::rowIntersectsSelection(int row, const QModelIndex &parent) const { Q_D(const QItemSelectionModel); - if (!d->model) + if (!d->model.value()) return false; if (parent.isValid() && d->model != parent.model()) return false; QItemSelection sel = d->ranges; sel.merge(d->currentSelection, d->currentCommand); - for (const QItemSelectionRange &range : qAsConst(sel)) { + for (const QItemSelectionRange &range : std::as_const(sel)) { if (range.parent() != parent) return false; int top = range.top(); @@ -1636,8 +1632,7 @@ bool QItemSelectionModel::rowIntersectsSelection(int row, const QModelIndex &par int right = range.right(); if (top <= row && bottom >= row) { for (int j = left; j <= right; j++) { - const Qt::ItemFlags flags = d->model->index(row, j, parent).flags(); - if ((flags & Qt::ItemIsSelectable) && (flags & Qt::ItemIsEnabled)) + if (isSelectableAndEnabled(d->model->index(row, j, parent).flags())) return true; } } @@ -1649,18 +1644,21 @@ bool QItemSelectionModel::rowIntersectsSelection(int row, const QModelIndex &par /*! Returns \c true if there are any items selected in the \a column with the given \a parent. + + \note Since Qt 5.15, the default argument for \a parent is an empty + model index. */ bool QItemSelectionModel::columnIntersectsSelection(int column, const QModelIndex &parent) const { Q_D(const QItemSelectionModel); - if (!d->model) + if (!d->model.value()) return false; if (parent.isValid() && d->model != parent.model()) return false; QItemSelection sel = d->ranges; sel.merge(d->currentSelection, d->currentCommand); - for (const QItemSelectionRange &range : qAsConst(sel)) { + for (const QItemSelectionRange &range : std::as_const(sel)) { if (range.parent() != parent) return false; int top = range.top(); @@ -1669,8 +1667,7 @@ bool QItemSelectionModel::columnIntersectsSelection(int column, const QModelInde int right = range.right(); if (left <= column && right >= column) { for (int j = top; j <= bottom; j++) { - const Qt::ItemFlags flags = d->model->index(j, column, parent).flags(); - if ((flags & Qt::ItemIsSelectable) && (flags & Qt::ItemIsEnabled)) + if (isSelectableAndEnabled(d->model->index(j, column, parent).flags())) return true; } } @@ -1680,20 +1677,47 @@ bool QItemSelectionModel::columnIntersectsSelection(int column, const QModelInde } /*! + \internal + + Check whether the selection is empty. + In contrast to selection.isEmpty(), this takes into account + whether items are enabled and whether they are selectable. +*/ +static bool selectionIsEmpty(const QItemSelection &selection) +{ + return std::all_of(selection.begin(), selection.end(), + [](const QItemSelectionRange &r) { return r.isEmpty(); }); +} + +/*! \since 4.2 - Returns \c true if the selection model contains any selection ranges; + Returns \c true if the selection model contains any selected item, otherwise returns \c false. */ bool QItemSelectionModel::hasSelection() const { Q_D(const QItemSelectionModel); + + // QTreeModel unfortunately sorts itself lazily. + // When it sorts itself, it emits are layoutChanged signal. + // This layoutChanged signal invalidates d->ranges here. + // So QTreeModel must not sort itself while we are iterating over + // d->ranges here. It sorts itself in executePendingOperations, + // thus preventing the sort to happen inside of selectionIsEmpty below. + // Sad story, read more in QTBUG-94546 + const QAbstractItemModel *model = QItemSelectionModel::model(); + if (model != nullptr) { + auto model_p = static_cast<const QAbstractItemModelPrivate *>(QObjectPrivate::get(model)); + model_p->executePendingOperations(); + } + if (d->currentCommand & (Toggle | Deselect)) { QItemSelection sel = d->ranges; sel.merge(d->currentSelection, d->currentCommand); - return !sel.isEmpty(); + return !selectionIsEmpty(sel); } else { - return !(d->ranges.isEmpty() && d->currentSelection.isEmpty()); + return !(selectionIsEmpty(d->ranges) && selectionIsEmpty(d->currentSelection)); } } @@ -1709,6 +1733,25 @@ QModelIndexList QItemSelectionModel::selectedIndexes() const return selected.indexes(); } +struct RowOrColumnDefinition { + QModelIndex parent; + int rowOrColumn; + + friend bool operator==(const RowOrColumnDefinition &lhs, const RowOrColumnDefinition &rhs) noexcept + { return lhs.parent == rhs.parent && lhs.rowOrColumn == rhs.rowOrColumn; } + friend bool operator!=(const RowOrColumnDefinition &lhs, const RowOrColumnDefinition &rhs) noexcept + { return !operator==(lhs, rhs); } +}; +size_t qHash(const RowOrColumnDefinition &key, size_t seed = 0) noexcept +{ + QtPrivate::QHashCombine hash; + seed = hash(seed, key.parent); + seed = hash(seed, key.rowOrColumn); + return seed; +} + +QT_SPECIALIZE_STD_HASH_TO_CALL_QHASH_BY_CREF(RowOrColumnDefinition) + /*! \since 4.2 Returns the indexes in the given \a column for the rows where all columns are selected. @@ -1719,18 +1762,15 @@ QModelIndexList QItemSelectionModel::selectedIndexes() const QModelIndexList QItemSelectionModel::selectedRows(int column) const { QModelIndexList indexes; - //the QSet contains pairs of parent modelIndex - //and row number - QSet< QPair<QModelIndex, int> > rowsSeen; + + QDuplicateTracker<RowOrColumnDefinition> rowsSeen; const QItemSelection ranges = selection(); - for (int i = 0; i < ranges.count(); ++i) { + for (int i = 0; i < ranges.size(); ++i) { const QItemSelectionRange &range = ranges.at(i); QModelIndex parent = range.parent(); for (int row = range.top(); row <= range.bottom(); row++) { - QPair<QModelIndex, int> rowDef = qMakePair(parent, row); - if (!rowsSeen.contains(rowDef)) { - rowsSeen << rowDef; + if (!rowsSeen.hasSeen({parent, row})) { if (isRowSelected(row, parent)) { indexes.append(model()->index(row, column, parent)); } @@ -1751,18 +1791,15 @@ QModelIndexList QItemSelectionModel::selectedRows(int column) const QModelIndexList QItemSelectionModel::selectedColumns(int row) const { QModelIndexList indexes; - //the QSet contains pairs of parent modelIndex - //and column number - QSet< QPair<QModelIndex, int> > columnsSeen; + + QDuplicateTracker<RowOrColumnDefinition> columnsSeen; const QItemSelection ranges = selection(); - for (int i = 0; i < ranges.count(); ++i) { + for (int i = 0; i < ranges.size(); ++i) { const QItemSelectionRange &range = ranges.at(i); QModelIndex parent = range.parent(); for (int column = range.left(); column <= range.right(); column++) { - QPair<QModelIndex, int> columnDef = qMakePair(parent, column); - if (!columnsSeen.contains(columnDef)) { - columnsSeen << columnDef; + if (!columnsSeen.hasSeen({parent, column})) { if (isColumnSelected(column, parent)) { indexes.append(model()->index(row, column, parent)); } @@ -1783,10 +1820,7 @@ const QItemSelection QItemSelectionModel::selection() const selected.merge(d->currentSelection, d->currentCommand); // make sure we have no invalid ranges // ### should probably be handled more generic somewhere else - using namespace QtFunctionObjects; - selected.erase(std::remove_if(selected.begin(), selected.end(), - IsNotValid()), - selected.end()); + selected.removeIf(QtFunctionObjects::IsNotValid()); return selected; } @@ -1827,7 +1861,7 @@ const QItemSelection QItemSelectionModel::selection() const */ QAbstractItemModel *QItemSelectionModel::model() { - return d_func()->model; + return d_func()->model.value(); } /*! @@ -1835,7 +1869,12 @@ QAbstractItemModel *QItemSelectionModel::model() */ const QAbstractItemModel *QItemSelectionModel::model() const { - return d_func()->model; + return d_func()->model.value(); +} + +QBindable<QAbstractItemModel *> QItemSelectionModel::bindableModel() +{ + return &d_func()->model; } /*! @@ -1848,11 +1887,11 @@ const QAbstractItemModel *QItemSelectionModel::model() const void QItemSelectionModel::setModel(QAbstractItemModel *model) { Q_D(QItemSelectionModel); - if (d->model == model) + d->model.removeBindingUnlessInWrapper(); + if (d->model.valueBypassingBindings() == model) return; - d->initModel(model); - emit modelChanged(model); + d->model.notify(); } /*! @@ -1878,9 +1917,9 @@ void QItemSelectionModel::emitSelectionChanged(const QItemSelection &newSelectio // remove equal ranges bool advance; - for (int o = 0; o < deselected.count(); ++o) { + for (int o = 0; o < deselected.size(); ++o) { advance = true; - for (int s = 0; s < selected.count() && o < deselected.count();) { + for (int s = 0; s < selected.size() && o < deselected.size();) { if (deselected.at(o) == selected.at(s)) { deselected.removeAt(o); selected.removeAt(s); @@ -1895,17 +1934,17 @@ void QItemSelectionModel::emitSelectionChanged(const QItemSelection &newSelectio // find intersections QItemSelection intersections; - for (int o = 0; o < deselected.count(); ++o) { - for (int s = 0; s < selected.count(); ++s) { + for (int o = 0; o < deselected.size(); ++o) { + for (int s = 0; s < selected.size(); ++s) { if (deselected.at(o).intersects(selected.at(s))) intersections.append(deselected.at(o).intersected(selected.at(s))); } } // compare remaining ranges with intersections and split them to find deselected and selected - for (int i = 0; i < intersections.count(); ++i) { + for (int i = 0; i < intersections.size(); ++i) { // split deselected - for (int o = 0; o < deselected.count();) { + for (int o = 0; o < deselected.size();) { if (deselected.at(o).intersects(intersections.at(i))) { QItemSelection::split(deselected.at(o), intersections.at(i), &deselected); deselected.removeAt(o); @@ -1914,7 +1953,7 @@ void QItemSelectionModel::emitSelectionChanged(const QItemSelection &newSelectio } } // split selected - for (int s = 0; s < selected.count();) { + for (int s = 0; s < selected.size();) { if (selected.at(s).intersects(intersections.at(i))) { QItemSelection::split(selected.at(s), intersections.at(i), &selected); selected.removeAt(s); diff --git a/src/corelib/itemmodels/qitemselectionmodel.h b/src/corelib/itemmodels/qitemselectionmodel.h index 5820695592..c4b8dadc97 100644 --- a/src/corelib/itemmodels/qitemselectionmodel.h +++ b/src/corelib/itemmodels/qitemselectionmodel.h @@ -1,51 +1,14 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QITEMSELECTIONMODEL_H #define QITEMSELECTIONMODEL_H #include <QtCore/qglobal.h> -#include <QtCore/qset.h> -#include <QtCore/qvector.h> -#include <QtCore/qlist.h> #include <QtCore/qabstractitemmodel.h> +#include <QtCore/qlist.h> +#include <QtCore/qset.h> QT_REQUIRE_CONFIG(itemmodel); @@ -55,25 +18,14 @@ class Q_CORE_EXPORT QItemSelectionRange { public: - inline QItemSelectionRange() : tl(), br() {} -#if QT_VERSION < QT_VERSION_CHECK(6,0,0) - // ### Qt 6: remove them all, the compiler-generated ones are fine - inline QItemSelectionRange(const QItemSelectionRange &other) - : tl(other.tl), br(other.br) {} - QItemSelectionRange(QItemSelectionRange &&other) noexcept - : tl(std::move(other.tl)), br(std::move(other.br)) {} - QItemSelectionRange &operator=(QItemSelectionRange &&other) noexcept - { tl = std::move(other.tl); br = std::move(other.br); return *this; } - QItemSelectionRange &operator=(const QItemSelectionRange &other) - { tl = other.tl; br = other.br; return *this; } -#endif // Qt < 6 + QItemSelectionRange() = default; QItemSelectionRange(const QModelIndex &topL, const QModelIndex &bottomR) : tl(topL), br(bottomR) {} explicit QItemSelectionRange(const QModelIndex &index) : tl(index), br(tl) {} void swap(QItemSelectionRange &other) noexcept { - qSwap(tl, other.tl); - qSwap(br, other.br); + tl.swap(other.tl); + br.swap(other.br); } inline int top() const { return tl.row(); } @@ -103,19 +55,14 @@ public: } bool intersects(const QItemSelectionRange &other) const; -#if QT_DEPRECATED_SINCE(5, 0) - inline QItemSelectionRange intersect(const QItemSelectionRange &other) const - { return intersected(other); } -#endif QItemSelectionRange intersected(const QItemSelectionRange &other) const; - +#if QT_CORE_REMOVED_SINCE(6, 8) inline bool operator==(const QItemSelectionRange &other) const - { return (tl == other.tl && br == other.br); } + { return comparesEqual(*this, other); } inline bool operator!=(const QItemSelectionRange &other) const - { return !operator==(other); } - bool operator<(const QItemSelectionRange &other) const; - + { return !operator==(other); } +#endif inline bool isValid() const { return (tl.isValid() && br.isValid() && tl.parent() == br.parent() @@ -127,9 +74,15 @@ public: QModelIndexList indexes() const; private: + friend bool comparesEqual(const QItemSelectionRange &lhs, + const QItemSelectionRange &rhs) noexcept + { + return (lhs.tl == rhs.tl && lhs.br == rhs.br); + } + Q_DECLARE_EQUALITY_COMPARABLE(QItemSelectionRange) QPersistentModelIndex tl, br; }; -Q_DECLARE_TYPEINFO(QItemSelectionRange, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(QItemSelectionRange, Q_RELOCATABLE_TYPE); class QItemSelection; class QItemSelectionModelPrivate; @@ -137,11 +90,16 @@ class QItemSelectionModelPrivate; class Q_CORE_EXPORT QItemSelectionModel : public QObject { Q_OBJECT - Q_PROPERTY(QAbstractItemModel *model READ model WRITE setModel NOTIFY modelChanged) - Q_PROPERTY(bool hasSelection READ hasSelection NOTIFY selectionChanged STORED false DESIGNABLE false) - Q_PROPERTY(QModelIndex currentIndex READ currentIndex NOTIFY currentChanged STORED false DESIGNABLE false) - Q_PROPERTY(QItemSelection selection READ selection NOTIFY selectionChanged STORED false DESIGNABLE false) - Q_PROPERTY(QModelIndexList selectedIndexes READ selectedIndexes NOTIFY selectionChanged STORED false DESIGNABLE false) + Q_PROPERTY(QAbstractItemModel *model READ model WRITE setModel NOTIFY modelChanged + BINDABLE bindableModel) + Q_PROPERTY(bool hasSelection READ hasSelection NOTIFY selectionChanged STORED false + DESIGNABLE false) + Q_PROPERTY(QModelIndex currentIndex READ currentIndex NOTIFY currentChanged STORED false + DESIGNABLE false) + Q_PROPERTY(QItemSelection selection READ selection NOTIFY selectionChanged STORED false + DESIGNABLE false) + Q_PROPERTY(QModelIndexList selectedIndexes READ selectedIndexes NOTIFY selectionChanged + STORED false DESIGNABLE false) Q_DECLARE_PRIVATE(QItemSelectionModel) @@ -184,9 +142,9 @@ public: Q_INVOKABLE QModelIndexList selectedColumns(int row = 0) const; const QItemSelection selection() const; - // ### Qt 6: Merge these two as "QAbstractItemModel *model() const" const QAbstractItemModel *model() const; QAbstractItemModel *model(); + QBindable<QAbstractItemModel *> bindableModel(); void setModel(QAbstractItemModel *model); @@ -213,54 +171,29 @@ protected: private: Q_DISABLE_COPY(QItemSelectionModel) - Q_PRIVATE_SLOT(d_func(), void _q_columnsAboutToBeRemoved(const QModelIndex&, int, int)) - Q_PRIVATE_SLOT(d_func(), void _q_rowsAboutToBeRemoved(const QModelIndex&, int, int)) - Q_PRIVATE_SLOT(d_func(), void _q_columnsAboutToBeInserted(const QModelIndex&, int, int)) - Q_PRIVATE_SLOT(d_func(), void _q_rowsAboutToBeInserted(const QModelIndex&, int, int)) - Q_PRIVATE_SLOT(d_func(), void _q_layoutAboutToBeChanged(const QList<QPersistentModelIndex> &parents = QList<QPersistentModelIndex>(), QAbstractItemModel::LayoutChangeHint hint = QAbstractItemModel::NoHint)) - Q_PRIVATE_SLOT(d_func(), void _q_layoutChanged(const QList<QPersistentModelIndex> &parents = QList<QPersistentModelIndex>(), QAbstractItemModel::LayoutChangeHint hint = QAbstractItemModel::NoHint)) }; Q_DECLARE_OPERATORS_FOR_FLAGS(QItemSelectionModel::SelectionFlags) -// dummy implentation of qHash() necessary for instantiating QList<QItemSelectionRange>::toSet() with MSVC -inline uint qHash(const QItemSelectionRange &) { return 0; } - -#ifdef Q_CC_MSVC - -/* - ### Qt 6: - ### This needs to be removed for next releases of Qt. It is a workaround for vc++ because - ### Qt exports QItemSelection that inherits QList<QItemSelectionRange>. -*/ - -# ifndef Q_TEMPLATE_EXTERN -# if defined(QT_BUILD_CORE_LIB) -# define Q_TEMPLATE_EXTERN -# else -# define Q_TEMPLATE_EXTERN extern -# endif -# endif -Q_TEMPLATE_EXTERN template class Q_CORE_EXPORT QList<QItemSelectionRange>; -#endif // Q_CC_MSVC - -class Q_CORE_EXPORT QItemSelection : public QList<QItemSelectionRange> +// We export each out-of-line method individually to prevent MSVC from +// exporting the whole QList class. +class QItemSelection : public QList<QItemSelectionRange> { public: - QItemSelection() noexcept : QList<QItemSelectionRange>() {} - QItemSelection(const QModelIndex &topLeft, const QModelIndex &bottomRight); + using QList<QItemSelectionRange>::QList; + Q_CORE_EXPORT QItemSelection(const QModelIndex &topLeft, const QModelIndex &bottomRight); // reusing QList::swap() here is OK! - void select(const QModelIndex &topLeft, const QModelIndex &bottomRight); - bool contains(const QModelIndex &index) const; - QModelIndexList indexes() const; - void merge(const QItemSelection &other, QItemSelectionModel::SelectionFlags command); - static void split(const QItemSelectionRange &range, + Q_CORE_EXPORT void select(const QModelIndex &topLeft, const QModelIndex &bottomRight); + Q_CORE_EXPORT bool contains(const QModelIndex &index) const; + Q_CORE_EXPORT QModelIndexList indexes() const; + Q_CORE_EXPORT void merge(const QItemSelection &other, QItemSelectionModel::SelectionFlags command); + Q_CORE_EXPORT static void split(const QItemSelectionRange &range, const QItemSelectionRange &other, QItemSelection *result); }; -Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QItemSelection) +Q_DECLARE_SHARED(QItemSelection) #ifndef QT_NO_DEBUG_STREAM Q_CORE_EXPORT QDebug operator<<(QDebug, const QItemSelectionRange &); @@ -268,7 +201,7 @@ Q_CORE_EXPORT QDebug operator<<(QDebug, const QItemSelectionRange &); QT_END_NAMESPACE -Q_DECLARE_METATYPE(QItemSelectionRange) -Q_DECLARE_METATYPE(QItemSelection) +QT_DECL_METATYPE_EXTERN(QItemSelectionRange, Q_CORE_EXPORT) +QT_DECL_METATYPE_EXTERN(QItemSelection, Q_CORE_EXPORT) #endif // QITEMSELECTIONMODEL_H diff --git a/src/corelib/itemmodels/qitemselectionmodel_p.h b/src/corelib/itemmodels/qitemselectionmodel_p.h index ba85f22be3..689cd26bd2 100644 --- a/src/corelib/itemmodels/qitemselectionmodel_p.h +++ b/src/corelib/itemmodels/qitemselectionmodel_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QITEMSELECTIONMODEL_P_H #define QITEMSELECTIONMODEL_P_H @@ -51,7 +15,10 @@ // We mean it. // +#include "qitemselectionmodel.h" #include "private/qobject_p.h" +#include "private/qproperty_p.h" +#include <array> QT_REQUIRE_CONFIG(itemmodel); @@ -62,8 +29,7 @@ class QItemSelectionModelPrivate: public QObjectPrivate Q_DECLARE_PUBLIC(QItemSelectionModel) public: QItemSelectionModelPrivate() - : model(nullptr), - currentCommand(QItemSelectionModel::NoUpdate), + : currentCommand(QItemSelectionModel::NoUpdate), tableSelected(false), tableColCount(0), tableRowCount(0) {} QItemSelection expandSelection(const QItemSelection &selection, @@ -71,12 +37,23 @@ public: void initModel(QAbstractItemModel *model); - void _q_rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); - void _q_columnsAboutToBeRemoved(const QModelIndex &parent, int start, int end); - void _q_rowsAboutToBeInserted(const QModelIndex &parent, int start, int end); - void _q_columnsAboutToBeInserted(const QModelIndex &parent, int start, int end); - void _q_layoutAboutToBeChanged(const QList<QPersistentModelIndex> &parents = QList<QPersistentModelIndex>(), QAbstractItemModel::LayoutChangeHint hint = QAbstractItemModel::NoLayoutChangeHint); - void _q_layoutChanged(const QList<QPersistentModelIndex> &parents = QList<QPersistentModelIndex>(), QAbstractItemModel::LayoutChangeHint hint = QAbstractItemModel::NoLayoutChangeHint); + void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); + void columnsAboutToBeRemoved(const QModelIndex &parent, int start, int end); + void rowsAboutToBeInserted(const QModelIndex &parent, int start, int end); + void columnsAboutToBeInserted(const QModelIndex &parent, int start, int end); + void layoutAboutToBeChanged(const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint); + void triggerLayoutToBeChanged() + { + layoutAboutToBeChanged(QList<QPersistentModelIndex>(), QAbstractItemModel::NoLayoutChangeHint); + } + + void layoutChanged(const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint); + void triggerLayoutChanged() + { + layoutChanged(QList<QPersistentModelIndex>(), QAbstractItemModel::NoLayoutChangeHint); + } + + void modelDestroyed(); inline void remove(QList<QItemSelectionRange> &r) { @@ -92,19 +69,26 @@ public: currentSelection.clear(); } - QPointer<QAbstractItemModel> model; + void setModel(QAbstractItemModel *mod) { q_func()->setModel(mod); } + void disconnectModel(); + void modelChanged(QAbstractItemModel *mod) { emit q_func()->modelChanged(mod); } + Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QItemSelectionModelPrivate, QAbstractItemModel *, model, + &QItemSelectionModelPrivate::setModel, + &QItemSelectionModelPrivate::modelChanged, nullptr) + QItemSelection ranges; QItemSelection currentSelection; QPersistentModelIndex currentIndex; QItemSelectionModel::SelectionFlags currentCommand; - QVector<QPersistentModelIndex> savedPersistentIndexes; - QVector<QPersistentModelIndex> savedPersistentCurrentIndexes; - QVector<QPair<QPersistentModelIndex, uint> > savedPersistentRowLengths; - QVector<QPair<QPersistentModelIndex, uint> > savedPersistentCurrentRowLengths; + QList<QPersistentModelIndex> savedPersistentIndexes; + QList<QPersistentModelIndex> savedPersistentCurrentIndexes; + QList<std::pair<QPersistentModelIndex, uint>> savedPersistentRowLengths; + QList<std::pair<QPersistentModelIndex, uint>> savedPersistentCurrentRowLengths; // optimization when all indexes are selected bool tableSelected; QPersistentModelIndex tableParent; int tableColCount, tableRowCount; + std::array<QMetaObject::Connection, 12> connections; }; QT_END_NAMESPACE diff --git a/src/corelib/itemmodels/qsortfilterproxymodel.cpp b/src/corelib/itemmodels/qsortfilterproxymodel.cpp index 21303549ab..a5284dbad4 100644 --- a/src/corelib/itemmodels/qsortfilterproxymodel.cpp +++ b/src/corelib/itemmodels/qsortfilterproxymodel.cpp @@ -1,57 +1,21 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qsortfilterproxymodel.h" #include "qitemselectionmodel.h" #include <qsize.h> #include <qdebug.h> #include <qdatetime.h> -#include <qpair.h> #include <qstringlist.h> #include <private/qabstractitemmodel_p.h> #include <private/qabstractproxymodel_p.h> +#include <private/qproperty_p.h> #include <algorithm> QT_BEGIN_NAMESPACE -typedef QVector<QPair<QModelIndex, QPersistentModelIndex> > QModelIndexPairList; +using QModelIndexPairList = QList<std::pair<QModelIndex, QPersistentModelIndex>>; struct QSortFilterProxyModelDataChanged { @@ -62,7 +26,7 @@ struct QSortFilterProxyModelDataChanged QModelIndex bottomRight; }; -static inline QSet<int> qVectorToSet(const QVector<int> &vector) +static inline QSet<int> qListToSet(const QList<int> &vector) { return {vector.begin(), vector.end()}; } @@ -142,177 +106,165 @@ private: int end; }; -class RegularExpressionData { +class QSortFilterProxyModelPrivate : public QAbstractProxyModelPrivate +{ +public: + Q_DECLARE_PUBLIC(QSortFilterProxyModel) -private: - enum class ExpressionType { - RegExp, -#if QT_CONFIG(regularexpression) - RegularExpression -#endif + enum class Direction { + Rows = 1, + Columns = 2, + All = Rows | Columns }; -public: - RegularExpressionData() : - m_type(ExpressionType::RegExp) - {} + struct Mapping { + QList<int> source_rows; + QList<int> source_columns; + QList<int> proxy_rows; + QList<int> proxy_columns; + QList<QModelIndex> mapped_children; + QModelIndex source_parent; + }; -#if QT_CONFIG(regularexpression) - QRegularExpression regularExpression() const + mutable QHash<QModelIndex, Mapping*> source_index_mapping; + + void setSortCaseSensitivityForwarder(Qt::CaseSensitivity cs) { - if (m_type == ExpressionType::RegularExpression) - return m_regularExpression; - return QRegularExpression(); + q_func()->setSortCaseSensitivity(cs); } - - void setRegularExpression(const QRegularExpression &rx) + void sortCaseSensitivityChangedForwarder(Qt::CaseSensitivity cs) { - m_type = ExpressionType::RegularExpression; - m_regularExpression = rx; - m_regExp = QRegExp(); + emit q_func()->sortCaseSensitivityChanged(cs); } -#endif - QRegExp regExp() const + void setSortRoleForwarder(int role) { q_func()->setSortRole(role); } + void sortRoleChangedForwarder(int role) { emit q_func()->sortRoleChanged(role); } + + void setSortLocaleAwareForwarder(bool on) { q_func()->setSortLocaleAware(on); } + void sortLocaleAwareChangedForwarder(bool on) { emit q_func()->sortLocaleAwareChanged(on); } + + void setFilterKeyColumnForwarder(int column) { q_func()->setFilterKeyColumn(column); } + + void setFilterRoleForwarder(int role) { q_func()->setFilterRole(role); } + void filterRoleChangedForwarder(int role) { emit q_func()->filterRoleChanged(role); } + + void setRecursiveFilteringEnabledForwarder(bool recursive) { - if (m_type == ExpressionType::RegExp) - return m_regExp; - return QRegExp(); + q_func()->setRecursiveFilteringEnabled(recursive); } - - void setRegExp(const QRegExp &rx) + void recursiveFilteringEnabledChangedForwarder(bool recursive) { - m_type = ExpressionType::RegExp; - m_regExp = rx; -#if QT_CONFIG(regularexpression) - m_regularExpression = QRegularExpression(); -#endif - + emit q_func()->recursiveFilteringEnabledChanged(recursive); } - bool isEmpty() const + void setAutoAcceptChildRowsForwarder(bool accept) { q_func()->setAutoAcceptChildRows(accept); } + void autoAcceptChildRowsChangedForwarder(bool accept) { - bool result = true; - switch (m_type) { - case ExpressionType::RegExp: - result = m_regExp.isEmpty(); - break; -#if QT_CONFIG(regularexpression) - case ExpressionType::RegularExpression: - result = m_regularExpression.pattern().isEmpty(); - break; -#endif - } - return result; + emit q_func()->autoAcceptChildRowsChanged(accept); } - Qt::CaseSensitivity caseSensitivity() const + void setDynamicSortFilterForwarder(bool enable) { q_func()->setDynamicSortFilter(enable); } + + void setFilterCaseSensitivityForwarder(Qt::CaseSensitivity cs) { - Qt::CaseSensitivity sensitivity = Qt::CaseInsensitive; - switch (m_type) { - case ExpressionType::RegExp: - sensitivity = m_regExp.caseSensitivity(); - break; -#if QT_CONFIG(regularexpression) - case ExpressionType::RegularExpression: - { - QRegularExpression::PatternOptions options = m_regularExpression.patternOptions(); - if (!(options & QRegularExpression::CaseInsensitiveOption)) - sensitivity = Qt::CaseSensitive; - } - break; -#endif - } - return sensitivity; + q_func()->setFilterCaseSensitivity(cs); } - - void setCaseSensitivity(Qt::CaseSensitivity cs) + void filterCaseSensitivityChangedForwarder(Qt::CaseSensitivity cs) { - switch (m_type) { - case ExpressionType::RegExp: - m_regExp.setCaseSensitivity(cs); - break; -#if QT_CONFIG(regularexpression) - case ExpressionType::RegularExpression: - { - QRegularExpression::PatternOptions options = m_regularExpression.patternOptions(); - options.setFlag(QRegularExpression::CaseInsensitiveOption, cs == Qt::CaseSensitive); - m_regularExpression.setPatternOptions(options); - } - break; -#endif - } + emit q_func()->filterCaseSensitivityChanged(cs); } - bool hasMatch(const QString &str) const + void setFilterRegularExpressionForwarder(const QRegularExpression &re) { - bool result = false; - switch (m_type) { - case ExpressionType::RegExp: - result = str.contains(m_regExp); - break; -#if QT_CONFIG(regularexpression) - case ExpressionType::RegularExpression: - result = str.contains(m_regularExpression); - break; -#endif - } - return result; + q_func()->setFilterRegularExpression(re); } -private: - ExpressionType m_type; - QRegExp m_regExp; -#if QT_CONFIG(regularexpression) - QRegularExpression m_regularExpression; -#endif -}; + int source_sort_column = -1; + int proxy_sort_column = -1; + Qt::SortOrder sort_order = Qt::AscendingOrder; + bool complete_insert = false; + Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS( + QSortFilterProxyModelPrivate, Qt::CaseSensitivity, sort_casesensitivity, + &QSortFilterProxyModelPrivate::setSortCaseSensitivityForwarder, + &QSortFilterProxyModelPrivate::sortCaseSensitivityChangedForwarder, Qt::CaseSensitive) -class QSortFilterProxyModelPrivate : public QAbstractProxyModelPrivate -{ - Q_DECLARE_PUBLIC(QSortFilterProxyModel) + Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QSortFilterProxyModelPrivate, int, sort_role, + &QSortFilterProxyModelPrivate::setSortRoleForwarder, + &QSortFilterProxyModelPrivate::sortRoleChangedForwarder, + Qt::DisplayRole) -public: - struct Mapping { - QVector<int> source_rows; - QVector<int> source_columns; - QVector<int> proxy_rows; - QVector<int> proxy_columns; - QVector<QModelIndex> mapped_children; - QHash<QModelIndex, Mapping *>::const_iterator map_iter; - }; + Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QSortFilterProxyModelPrivate, int, filter_column, + &QSortFilterProxyModelPrivate::setFilterKeyColumnForwarder, + 0) - mutable QHash<QModelIndex, Mapping*> source_index_mapping; + Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QSortFilterProxyModelPrivate, int, filter_role, + &QSortFilterProxyModelPrivate::setFilterRoleForwarder, + &QSortFilterProxyModelPrivate::filterRoleChangedForwarder, + Qt::DisplayRole) - int source_sort_column; - int proxy_sort_column; - Qt::SortOrder sort_order; - Qt::CaseSensitivity sort_casesensitivity; - int sort_role; - bool sort_localeaware; + Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS( + QSortFilterProxyModelPrivate, bool, sort_localeaware, + &QSortFilterProxyModelPrivate::setSortLocaleAwareForwarder, + &QSortFilterProxyModelPrivate::sortLocaleAwareChangedForwarder, false) - int filter_column; - int filter_role; - RegularExpressionData filter_data; - QModelIndex last_top_source; + Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS( + QSortFilterProxyModelPrivate, bool, filter_recursive, + &QSortFilterProxyModelPrivate::setRecursiveFilteringEnabledForwarder, + &QSortFilterProxyModelPrivate::recursiveFilteringEnabledChangedForwarder, false) + + Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS( + QSortFilterProxyModelPrivate, bool, accept_children, + &QSortFilterProxyModelPrivate::setAutoAcceptChildRowsForwarder, + &QSortFilterProxyModelPrivate::autoAcceptChildRowsChangedForwarder, false) + + Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QSortFilterProxyModelPrivate, bool, dynamic_sortfilter, + &QSortFilterProxyModelPrivate::setDynamicSortFilterForwarder, + true) - bool filter_recursive; - bool complete_insert; - bool dynamic_sortfilter; + Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS( + QSortFilterProxyModelPrivate, Qt::CaseSensitivity, filter_casesensitive, + &QSortFilterProxyModelPrivate::setFilterCaseSensitivityForwarder, + &QSortFilterProxyModelPrivate::filterCaseSensitivityChangedForwarder, Qt::CaseSensitive) + + Q_OBJECT_COMPAT_PROPERTY(QSortFilterProxyModelPrivate, QRegularExpression, + filter_regularexpression, + &QSortFilterProxyModelPrivate::setFilterRegularExpressionForwarder) + + QModelIndex last_top_source; QRowsRemoval itemsBeingRemoved; QModelIndexPairList saved_persistent_indexes; QList<QPersistentModelIndex> saved_layoutChange_parents; + std::array<QMetaObject::Connection, 18> sourceConnections; + QHash<QModelIndex, Mapping *>::const_iterator create_mapping( const QModelIndex &source_parent) const; + QHash<QModelIndex, Mapping *>::const_iterator create_mapping_recursive( + const QModelIndex &source_parent) const; QModelIndex proxy_to_source(const QModelIndex &proxyIndex) const; QModelIndex source_to_proxy(const QModelIndex &sourceIndex) const; bool can_create_mapping(const QModelIndex &source_parent) const; void remove_from_mapping(const QModelIndex &source_parent); + /* + * Legacy: changing the pattern through a string does not change the + * case sensitivity. + */ + void set_filter_pattern(const QString &pattern) + { + QRegularExpression re = filter_regularexpression.valueBypassingBindings(); + const auto cs = re.patternOptions() & QRegularExpression::CaseInsensitiveOption; + re.setPattern(pattern); + re.setPatternOptions(cs); + // This is a helper function, which is supposed to be called from a + // more complicated context. Because of that, the caller is responsible + // for calling notify() and removeBindingUnlessInWrapper(), if needed. + filter_regularexpression.setValueBypassingBindings(re); + } + inline QHash<QModelIndex, Mapping *>::const_iterator index_to_iterator( const QModelIndex &proxy_index) const { @@ -321,7 +273,7 @@ public: const void *p = proxy_index.internalPointer(); Q_ASSERT(p); QHash<QModelIndex, Mapping *>::const_iterator it = - static_cast<const Mapping*>(p)->map_iter; + source_index_mapping.constFind(static_cast<const Mapping*>(p)->source_parent); Q_ASSERT(it != source_index_mapping.constEnd()); Q_ASSERT(it.value()); return it; @@ -335,7 +287,7 @@ public: void _q_sourceDataChanged(const QModelIndex &source_top_left, const QModelIndex &source_bottom_right, - const QVector<int> &roles); + const QList<int> &roles); void _q_sourceHeaderDataChanged(Qt::Orientation orientation, int start, int end); void _q_sourceAboutToBeReset(); @@ -378,27 +330,27 @@ public: void sort(); bool update_source_sort_column(); int find_source_sort_column() const; - void sort_source_rows(QVector<int> &source_rows, + void sort_source_rows(QList<int> &source_rows, const QModelIndex &source_parent) const; - QVector<QPair<int, QVector<int > > > proxy_intervals_for_source_items_to_add( - const QVector<int> &proxy_to_source, const QVector<int> &source_items, + QList<std::pair<int, QList<int>>> proxy_intervals_for_source_items_to_add( + const QList<int> &proxy_to_source, const QList<int> &source_items, const QModelIndex &source_parent, Qt::Orientation orient) const; - QVector<QPair<int, int > > proxy_intervals_for_source_items( - const QVector<int> &source_to_proxy, const QVector<int> &source_items) const; + QList<std::pair<int, int>> proxy_intervals_for_source_items( + const QList<int> &source_to_proxy, const QList<int> &source_items) const; void insert_source_items( - QVector<int> &source_to_proxy, QVector<int> &proxy_to_source, - const QVector<int> &source_items, const QModelIndex &source_parent, + QList<int> &source_to_proxy, QList<int> &proxy_to_source, + const QList<int> &source_items, const QModelIndex &source_parent, Qt::Orientation orient, bool emit_signal = true); void remove_source_items( - QVector<int> &source_to_proxy, QVector<int> &proxy_to_source, - const QVector<int> &source_items, const QModelIndex &source_parent, + QList<int> &source_to_proxy, QList<int> &proxy_to_source, + const QList<int> &source_items, const QModelIndex &source_parent, Qt::Orientation orient, bool emit_signal = true); void remove_proxy_interval( - QVector<int> &source_to_proxy, QVector<int> &proxy_to_source, + QList<int> &source_to_proxy, QList<int> &proxy_to_source, int proxy_start, int proxy_end, const QModelIndex &proxy_parent, Qt::Orientation orient, bool emit_signal = true); - void build_source_to_proxy_mapping( - const QVector<int> &proxy_to_source, QVector<int> &source_to_proxy) const; + static inline void build_source_to_proxy_mapping( + const QList<int> &proxy_to_source, QList<int> &source_to_proxy, int start = 0); void source_items_inserted(const QModelIndex &source_parent, int start, int end, Qt::Orientation orient); void source_items_about_to_be_removed(const QModelIndex &source_parent, @@ -406,16 +358,16 @@ public: void source_items_removed(const QModelIndex &source_parent, int start, int end, Qt::Orientation orient); void proxy_item_range( - const QVector<int> &source_to_proxy, const QVector<int> &source_items, + const QList<int> &source_to_proxy, const QList<int> &source_items, int &proxy_low, int &proxy_high) const; QModelIndexPairList store_persistent_indexes() const; void update_persistent_indexes(const QModelIndexPairList &source_indexes); void filter_about_to_be_changed(const QModelIndex &source_parent = QModelIndex()); - void filter_changed(const QModelIndex &source_parent = QModelIndex()); + void filter_changed(Direction dir, const QModelIndex &source_parent = QModelIndex()); QSet<int> handle_filter_changed( - QVector<int> &source_to_proxy, QVector<int> &proxy_to_source, + QList<int> &source_to_proxy, QList<int> &proxy_to_source, const QModelIndex &source_parent, Qt::Orientation orient); void updateChildrenMapping(const QModelIndex &source_parent, Mapping *parent_mapping, @@ -423,14 +375,20 @@ public: void _q_sourceModelDestroyed() override; - bool needsReorder(const QVector<int> &source_rows, const QModelIndex &source_parent) const; + bool needsReorder(const QList<int> &source_rows, const QModelIndex &source_parent) const; bool filterAcceptsRowInternal(int source_row, const QModelIndex &source_parent) const; - bool filterRecursiveAcceptsRow(int source_row, const QModelIndex &source_parent) const; + bool recursiveChildAcceptsRow(int source_row, const QModelIndex &source_parent) const; + bool recursiveParentAcceptsRow(const QModelIndex &source_parent) const; }; typedef QHash<QModelIndex, QSortFilterProxyModelPrivate::Mapping *> IndexMap; +static bool operator&(QSortFilterProxyModelPrivate::Direction a, QSortFilterProxyModelPrivate::Direction b) +{ + return int(a) & int(b); +} + void QSortFilterProxyModelPrivate::_q_sourceModelDestroyed() { QAbstractProxyModelPrivate::_q_sourceModelDestroyed(); @@ -441,23 +399,49 @@ void QSortFilterProxyModelPrivate::_q_sourceModelDestroyed() bool QSortFilterProxyModelPrivate::filterAcceptsRowInternal(int source_row, const QModelIndex &source_parent) const { Q_Q(const QSortFilterProxyModel); - return filter_recursive - ? filterRecursiveAcceptsRow(source_row, source_parent) - : q->filterAcceptsRow(source_row, source_parent); + + if (q->filterAcceptsRow(source_row, source_parent)) + return true; + + // Go up the tree and accept this row if a parent is accepted + if (accept_children && recursiveParentAcceptsRow(source_parent)) + return true; + + // Go down the tree and accept this row if a child is accepted + if (filter_recursive && recursiveChildAcceptsRow(source_row, source_parent)) + return true; + + return false; } -bool QSortFilterProxyModelPrivate::filterRecursiveAcceptsRow(int source_row, const QModelIndex &source_parent) const +bool QSortFilterProxyModelPrivate::recursiveParentAcceptsRow(const QModelIndex &source_parent) const { Q_Q(const QSortFilterProxyModel); - if (q->filterAcceptsRow(source_row, source_parent)) - return true; + if (source_parent.isValid()) { + const QModelIndex index = source_parent.parent(); + + if (q->filterAcceptsRow(source_parent.row(), index)) + return true; + + return recursiveParentAcceptsRow(index); + } + + return false; +} + +bool QSortFilterProxyModelPrivate::recursiveChildAcceptsRow(int source_row, const QModelIndex &source_parent) const +{ + Q_Q(const QSortFilterProxyModel); const QModelIndex index = model->index(source_row, 0, source_parent); const int count = model->rowCount(index); for (int i = 0; i < count; ++i) { - if (filterRecursiveAcceptsRow(i, index)) + if (q->filterAcceptsRow(i, index)) + return true; + + if (recursiveChildAcceptsRow(i, index)) return true; } @@ -467,7 +451,7 @@ bool QSortFilterProxyModelPrivate::filterRecursiveAcceptsRow(int source_row, con void QSortFilterProxyModelPrivate::remove_from_mapping(const QModelIndex &source_parent) { if (Mapping *m = source_index_mapping.take(source_parent)) { - for (const QModelIndex &mappedIdx : qAsConst(m->mapped_children)) + for (const QModelIndex &mappedIdx : std::as_const(m->mapped_children)) remove_from_mapping(mappedIdx); delete m; } @@ -517,8 +501,7 @@ IndexMap::const_iterator QSortFilterProxyModelPrivate::create_mapping( m->proxy_columns.resize(source_cols); build_source_to_proxy_mapping(m->source_columns, m->proxy_columns); - it = IndexMap::const_iterator(source_index_mapping.insert(source_parent, m)); - m->map_iter = it; + m->source_parent = source_parent; if (source_parent.isValid()) { QModelIndex source_grand_parent = source_parent.parent(); @@ -527,12 +510,36 @@ IndexMap::const_iterator QSortFilterProxyModelPrivate::create_mapping( it2.value()->mapped_children.append(source_parent); } + it = IndexMap::const_iterator(source_index_mapping.insert(source_parent, m)); Q_ASSERT(it != source_index_mapping.constEnd()); Q_ASSERT(it.value()); return it; } +// Go up the tree, creating mappings, unless of course the parent is filtered out +IndexMap::const_iterator QSortFilterProxyModelPrivate::create_mapping_recursive(const QModelIndex &source_parent) const +{ + if (source_parent.isValid()) { + const QModelIndex source_grand_parent = source_parent.parent(); + IndexMap::const_iterator it = source_index_mapping.constFind(source_grand_parent); + IndexMap::const_iterator end = source_index_mapping.constEnd(); + if (it == end) { + it = create_mapping_recursive(source_grand_parent); + end = source_index_mapping.constEnd(); + if (it == end) + return end; + } + Mapping *gm = it.value(); + if (gm->proxy_rows.at(source_parent.row()) == -1 || + gm->proxy_columns.at(source_parent.column()) == -1) { + // Can't do, parent is filtered + return end; + } + } + return create_mapping(source_parent); +} + QModelIndex QSortFilterProxyModelPrivate::proxy_to_source(const QModelIndex &proxy_index) const { if (!proxy_index.isValid()) @@ -670,7 +677,7 @@ int QSortFilterProxyModelPrivate::find_source_sort_column() const Sorts the given \a source_rows according to current sort column and order. */ void QSortFilterProxyModelPrivate::sort_source_rows( - QVector<int> &source_rows, const QModelIndex &source_parent) const + QList<int> &source_rows, const QModelIndex &source_parent) const { Q_Q(const QSortFilterProxyModel); if (source_sort_column >= 0) { @@ -681,8 +688,10 @@ void QSortFilterProxyModelPrivate::sort_source_rows( QSortFilterProxyModelGreaterThan gt(source_sort_column, source_parent, model, q); std::stable_sort(source_rows.begin(), source_rows.end(), gt); } - } else { // restore the source model order - std::stable_sort(source_rows.begin(), source_rows.end()); + } else if (sort_order == Qt::AscendingOrder) { + std::stable_sort(source_rows.begin(), source_rows.end(), std::less{}); + } else { + std::stable_sort(source_rows.begin(), source_rows.end(), std::greater{}); } } @@ -697,10 +706,10 @@ void QSortFilterProxyModelPrivate::sort_source_rows( The result is a vector of pairs, where each pair represents a (start, end) tuple, sorted in ascending order. */ -QVector<QPair<int, int > > QSortFilterProxyModelPrivate::proxy_intervals_for_source_items( - const QVector<int> &source_to_proxy, const QVector<int> &source_items) const +QList<std::pair<int, int>> QSortFilterProxyModelPrivate::proxy_intervals_for_source_items( + const QList<int> &source_to_proxy, const QList<int> &source_items) const { - QVector<QPair<int, int> > proxy_intervals; + QList<std::pair<int, int>> proxy_intervals; if (source_items.isEmpty()) return proxy_intervals; @@ -717,22 +726,19 @@ QVector<QPair<int, int > > QSortFilterProxyModelPrivate::proxy_intervals_for_sou ++source_items_index; } // Add interval to result - proxy_intervals.append(QPair<int, int>(first_proxy_item, last_proxy_item)); + proxy_intervals.emplace_back(first_proxy_item, last_proxy_item); } std::stable_sort(proxy_intervals.begin(), proxy_intervals.end()); // Consolidate adjacent intervals for (int i = proxy_intervals.size()-1; i > 0; --i) { - QPair<int, int> &interval = proxy_intervals[i]; - QPair<int, int> &preceeding_interval = proxy_intervals[i - 1]; + std::pair<int, int> &interval = proxy_intervals[i]; + std::pair<int, int> &preceeding_interval = proxy_intervals[i - 1]; if (interval.first == preceeding_interval.second + 1) { preceeding_interval.second = interval.second; interval.first = interval.second = -1; } } - proxy_intervals.erase( - std::remove_if(proxy_intervals.begin(), proxy_intervals.end(), - [](QPair<int, int> &interval) { return interval.first < 0; }), - proxy_intervals.end()); + proxy_intervals.removeIf([](std::pair<int, int> interval) { return interval.first < 0; }); return proxy_intervals; } @@ -745,21 +751,23 @@ QVector<QPair<int, int > > QSortFilterProxyModelPrivate::proxy_intervals_for_sou rows/columnsRemoved(start, end) signals will be generated. */ void QSortFilterProxyModelPrivate::remove_source_items( - QVector<int> &source_to_proxy, QVector<int> &proxy_to_source, - const QVector<int> &source_items, const QModelIndex &source_parent, + QList<int> &source_to_proxy, QList<int> &proxy_to_source, + const QList<int> &source_items, const QModelIndex &source_parent, Qt::Orientation orient, bool emit_signal) { Q_Q(QSortFilterProxyModel); QModelIndex proxy_parent = q->mapFromSource(source_parent); - if (!proxy_parent.isValid() && source_parent.isValid()) + if (!proxy_parent.isValid() && source_parent.isValid()) { + proxy_to_source.clear(); return; // nothing to do (already removed) + } const auto proxy_intervals = proxy_intervals_for_source_items( source_to_proxy, source_items); const auto end = proxy_intervals.rend(); for (auto it = proxy_intervals.rbegin(); it != end; ++it) { - const QPair<int, int> &interval = *it; + const std::pair<int, int> &interval = *it; const int proxy_start = interval.first; const int proxy_end = interval.second; remove_proxy_interval(source_to_proxy, proxy_to_source, proxy_start, proxy_end, @@ -775,7 +783,7 @@ void QSortFilterProxyModelPrivate::remove_source_items( (inclusive) from this proxy model. */ void QSortFilterProxyModelPrivate::remove_proxy_interval( - QVector<int> &source_to_proxy, QVector<int> &proxy_to_source, int proxy_start, int proxy_end, + QList<int> &source_to_proxy, QList<int> &proxy_to_source, int proxy_start, int proxy_end, const QModelIndex &proxy_parent, Qt::Orientation orient, bool emit_signal) { Q_Q(QSortFilterProxyModel); @@ -787,9 +795,11 @@ void QSortFilterProxyModelPrivate::remove_proxy_interval( } // Remove items from proxy-to-source mapping + for (int i = proxy_start; i <= proxy_end; ++i) + source_to_proxy[proxy_to_source.at(i)] = -1; proxy_to_source.remove(proxy_start, proxy_end - proxy_start + 1); - build_source_to_proxy_mapping(proxy_to_source, source_to_proxy); + build_source_to_proxy_mapping(proxy_to_source, source_to_proxy, proxy_start); if (emit_signal) { if (orient == Qt::Vertical) @@ -811,22 +821,21 @@ void QSortFilterProxyModelPrivate::remove_proxy_interval( items), where items is a vector containing the (sorted) source items that should be inserted at that proxy model location. */ -QVector<QPair<int, QVector<int > > > QSortFilterProxyModelPrivate::proxy_intervals_for_source_items_to_add( - const QVector<int> &proxy_to_source, const QVector<int> &source_items, +QList<std::pair<int, QList<int>>> QSortFilterProxyModelPrivate::proxy_intervals_for_source_items_to_add( + const QList<int> &proxy_to_source, const QList<int> &source_items, const QModelIndex &source_parent, Qt::Orientation orient) const { Q_Q(const QSortFilterProxyModel); - QVector<QPair<int, QVector<int> > > proxy_intervals; + QList<std::pair<int, QList<int>>> proxy_intervals; if (source_items.isEmpty()) return proxy_intervals; int proxy_low = 0; int proxy_item = 0; int source_items_index = 0; - QVector<int> source_items_in_interval; bool compare = (orient == Qt::Vertical && source_sort_column >= 0 && dynamic_sortfilter); while (source_items_index < source_items.size()) { - source_items_in_interval.clear(); + QList<int> source_items_in_interval; int first_new_source_item = source_items.at(source_items_index); source_items_in_interval.append(first_new_source_item); ++source_items_index; @@ -872,7 +881,7 @@ QVector<QPair<int, QVector<int > > > QSortFilterProxyModelPrivate::proxy_interva } // Add interval to result - proxy_intervals.append(QPair<int, QVector<int> >(proxy_item, source_items_in_interval)); + proxy_intervals.emplace_back(proxy_item, std::move(source_items_in_interval)); } return proxy_intervals; } @@ -886,8 +895,8 @@ QVector<QPair<int, QVector<int > > > QSortFilterProxyModelPrivate::proxy_interva that the proper rows/columnsInserted(start, end) signals will be generated. */ void QSortFilterProxyModelPrivate::insert_source_items( - QVector<int> &source_to_proxy, QVector<int> &proxy_to_source, - const QVector<int> &source_items, const QModelIndex &source_parent, + QList<int> &source_to_proxy, QList<int> &proxy_to_source, + const QList<int> &source_items, const QModelIndex &source_parent, Qt::Orientation orient, bool emit_signal) { Q_Q(QSortFilterProxyModel); @@ -900,9 +909,9 @@ void QSortFilterProxyModelPrivate::insert_source_items( const auto end = proxy_intervals.rend(); for (auto it = proxy_intervals.rbegin(); it != end; ++it) { - const QPair<int, QVector<int> > &interval = *it; + const std::pair<int, QList<int>> &interval = *it; const int proxy_start = interval.first; - const QVector<int> &source_items = interval.second; + const QList<int> &source_items = interval.second; const int proxy_end = proxy_start + source_items.size() - 1; if (emit_signal) { @@ -912,10 +921,11 @@ void QSortFilterProxyModelPrivate::insert_source_items( q->beginInsertColumns(proxy_parent, proxy_start, proxy_end); } - for (int i = 0; i < source_items.size(); ++i) - proxy_to_source.insert(proxy_start + i, source_items.at(i)); + // TODO: use the range QList::insert() overload once it is implemented (QTBUG-58633). + proxy_to_source.insert(proxy_start, source_items.size(), 0); + std::copy(source_items.cbegin(), source_items.cend(), proxy_to_source.begin() + proxy_start); - build_source_to_proxy_mapping(proxy_to_source, source_to_proxy); + build_source_to_proxy_mapping(proxy_to_source, source_to_proxy, proxy_start); if (emit_signal) { if (orient == Qt::Vertical) @@ -951,20 +961,20 @@ void QSortFilterProxyModelPrivate::source_items_inserted( it = create_mapping(source_parent); Mapping *m = it.value(); QModelIndex proxy_parent = q->mapFromSource(source_parent); - if (m->source_rows.count() > 0) { - q->beginInsertRows(proxy_parent, 0, m->source_rows.count() - 1); + if (m->source_rows.size() > 0) { + q->beginInsertRows(proxy_parent, 0, m->source_rows.size() - 1); q->endInsertRows(); } - if (m->source_columns.count() > 0) { - q->beginInsertColumns(proxy_parent, 0, m->source_columns.count() - 1); + if (m->source_columns.size() > 0) { + q->beginInsertColumns(proxy_parent, 0, m->source_columns.size() - 1); q->endInsertColumns(); } return; } Mapping *m = it.value(); - QVector<int> &source_to_proxy = (orient == Qt::Vertical) ? m->proxy_rows : m->proxy_columns; - QVector<int> &proxy_to_source = (orient == Qt::Vertical) ? m->source_rows : m->source_columns; + QList<int> &source_to_proxy = (orient == Qt::Vertical) ? m->proxy_rows : m->proxy_columns; + QList<int> &proxy_to_source = (orient == Qt::Vertical) ? m->source_rows : m->source_columns; int delta_item_count = end - start + 1; int old_item_count = source_to_proxy.size(); @@ -991,7 +1001,7 @@ void QSortFilterProxyModelPrivate::source_items_inserted( } // Figure out which items to add to mapping based on filter - QVector<int> source_items; + QList<int> source_items; for (int i = start; i <= end; ++i) { if ((orient == Qt::Vertical) ? filterAcceptsRowInternal(i, source_parent) @@ -1005,8 +1015,8 @@ void QSortFilterProxyModelPrivate::source_items_inserted( // If it was new rows make sure to create mappings for columns so that a // valid mapping can be retrieved later and vice-versa. - QVector<int> &orthogonal_proxy_to_source = (orient == Qt::Horizontal) ? m->source_rows : m->source_columns; - QVector<int> &orthogonal_source_to_proxy = (orient == Qt::Horizontal) ? m->proxy_rows : m->proxy_columns; + QList<int> &orthogonal_proxy_to_source = (orient == Qt::Horizontal) ? m->source_rows : m->source_columns; + QList<int> &orthogonal_source_to_proxy = (orient == Qt::Horizontal) ? m->proxy_rows : m->proxy_columns; if (orthogonal_source_to_proxy.isEmpty()) { const int ortho_end = (orient == Qt::Horizontal) ? model->rowCount(source_parent) : model->columnCount(source_parent); @@ -1051,11 +1061,11 @@ void QSortFilterProxyModelPrivate::source_items_about_to_be_removed( } Mapping *m = it.value(); - QVector<int> &source_to_proxy = (orient == Qt::Vertical) ? m->proxy_rows : m->proxy_columns; - QVector<int> &proxy_to_source = (orient == Qt::Vertical) ? m->source_rows : m->source_columns; + QList<int> &source_to_proxy = (orient == Qt::Vertical) ? m->proxy_rows : m->proxy_columns; + QList<int> &proxy_to_source = (orient == Qt::Vertical) ? m->source_rows : m->source_columns; // figure out which items to remove - QVector<int> source_items_to_remove; + QList<int> source_items_to_remove; int proxy_count = proxy_to_source.size(); for (int proxy_item = 0; proxy_item < proxy_count; ++proxy_item) { int source_item = proxy_to_source.at(proxy_item); @@ -1084,8 +1094,8 @@ void QSortFilterProxyModelPrivate::source_items_removed( } Mapping *m = it.value(); - QVector<int> &source_to_proxy = (orient == Qt::Vertical) ? m->proxy_rows : m->proxy_columns; - QVector<int> &proxy_to_source = (orient == Qt::Vertical) ? m->source_rows : m->source_columns; + QList<int> &source_to_proxy = (orient == Qt::Vertical) ? m->proxy_rows : m->proxy_columns; + QList<int> &proxy_to_source = (orient == Qt::Vertical) ? m->source_rows : m->source_columns; if (end >= source_to_proxy.size()) end = source_to_proxy.size() - 1; @@ -1128,8 +1138,8 @@ void QSortFilterProxyModelPrivate::updateChildrenMapping(const QModelIndex &sour Qt::Orientation orient, int start, int end, int delta_item_count, bool remove) { // see if any mapped children should be (re)moved - QVector<QPair<QModelIndex, Mapping*> > moved_source_index_mappings; - QVector<QModelIndex>::iterator it2 = parent_mapping->mapped_children.begin(); + QList<std::pair<QModelIndex, Mapping *>> moved_source_index_mappings; + auto it2 = parent_mapping->mapped_children.begin(); for ( ; it2 != parent_mapping->mapped_children.end();) { const QModelIndex source_child_index = *it2; const int pos = (orient == Qt::Vertical) @@ -1162,14 +1172,14 @@ void QSortFilterProxyModelPrivate::updateChildrenMapping(const QModelIndex &sour Mapping *cm = source_index_mapping.take(source_child_index); Q_ASSERT(cm); // we do not reinsert right away, because the new index might be identical with another, old index - moved_source_index_mappings.append(QPair<QModelIndex, Mapping*>(new_index, cm)); + moved_source_index_mappings.emplace_back(new_index, cm); } } // reinsert moved, mapped indexes - QVector<QPair<QModelIndex, Mapping*> >::iterator it = moved_source_index_mappings.begin(); - for (; it != moved_source_index_mappings.end(); ++it) { - (*it).second->map_iter = QHash<QModelIndex, Mapping *>::const_iterator(source_index_mapping.insert((*it).first, (*it).second)); + for (auto &pair : std::as_const(moved_source_index_mappings)) { + pair.second->source_parent = pair.first; + source_index_mapping.insert(pair.first, pair.second); } } @@ -1177,12 +1187,12 @@ void QSortFilterProxyModelPrivate::updateChildrenMapping(const QModelIndex &sour \internal */ void QSortFilterProxyModelPrivate::proxy_item_range( - const QVector<int> &source_to_proxy, const QVector<int> &source_items, + const QList<int> &source_to_proxy, const QList<int> &source_items, int &proxy_low, int &proxy_high) const { proxy_low = INT_MAX; proxy_high = INT_MIN; - for (int i = 0; i < source_items.count(); ++i) { + for (int i = 0; i < source_items.size(); ++i) { int proxy_item = source_to_proxy.at(source_items.at(i)); Q_ASSERT(proxy_item != -1); if (proxy_item < proxy_low) @@ -1196,11 +1206,12 @@ void QSortFilterProxyModelPrivate::proxy_item_range( \internal */ void QSortFilterProxyModelPrivate::build_source_to_proxy_mapping( - const QVector<int> &proxy_to_source, QVector<int> &source_to_proxy) const + const QList<int> &proxy_to_source, QList<int> &source_to_proxy, int start) { - source_to_proxy.fill(-1); - int proxy_count = proxy_to_source.size(); - for (int i = 0; i < proxy_count; ++i) + if (start == 0) + source_to_proxy.fill(-1); + const int proxy_count = proxy_to_source.size(); + for (int i = start; i < proxy_count; ++i) source_to_proxy[proxy_to_source.at(i)] = i; } @@ -1214,11 +1225,11 @@ QModelIndexPairList QSortFilterProxyModelPrivate::store_persistent_indexes() con { Q_Q(const QSortFilterProxyModel); QModelIndexPairList source_indexes; - source_indexes.reserve(persistent.indexes.count()); - for (const QPersistentModelIndexData *data : qAsConst(persistent.indexes)) { + source_indexes.reserve(persistent.indexes.size()); + for (const QPersistentModelIndexData *data : std::as_const(persistent.indexes)) { const QModelIndex &proxy_index = data->index; QModelIndex source_index = q->mapToSource(proxy_index); - source_indexes.append(qMakePair(proxy_index, QPersistentModelIndex(source_index))); + source_indexes.emplace_back(proxy_index, source_index); } return source_indexes; } @@ -1234,7 +1245,7 @@ void QSortFilterProxyModelPrivate::update_persistent_indexes( { Q_Q(QSortFilterProxyModel); QModelIndexList from, to; - const int numSourceIndexes = source_indexes.count(); + const int numSourceIndexes = source_indexes.size(); from.reserve(numSourceIndexes); to.reserve(numSourceIndexes); for (const auto &indexPair : source_indexes) { @@ -1256,9 +1267,10 @@ void QSortFilterProxyModelPrivate::update_persistent_indexes( */ void QSortFilterProxyModelPrivate::filter_about_to_be_changed(const QModelIndex &source_parent) { - if (!filter_data.isEmpty() && - source_index_mapping.constFind(source_parent) == source_index_mapping.constEnd()) - create_mapping(source_parent); + if (!filter_regularexpression.valueBypassingBindings().pattern().isEmpty() + && source_index_mapping.constFind(source_parent) == source_index_mapping.constEnd()) { + create_mapping(source_parent); + } } @@ -1268,32 +1280,32 @@ void QSortFilterProxyModelPrivate::filter_about_to_be_changed(const QModelIndex Updates the proxy model (adds/removes rows) based on the new filter. */ -void QSortFilterProxyModelPrivate::filter_changed(const QModelIndex &source_parent) +void QSortFilterProxyModelPrivate::filter_changed(Direction dir, const QModelIndex &source_parent) { IndexMap::const_iterator it = source_index_mapping.constFind(source_parent); if (it == source_index_mapping.constEnd()) return; Mapping *m = it.value(); - QSet<int> rows_removed = handle_filter_changed(m->proxy_rows, m->source_rows, source_parent, Qt::Vertical); - QSet<int> columns_removed = handle_filter_changed(m->proxy_columns, m->source_columns, source_parent, Qt::Horizontal); + const QSet<int> rows_removed = (dir & Direction::Rows) ? handle_filter_changed(m->proxy_rows, m->source_rows, source_parent, Qt::Vertical) : QSet<int>(); + const QSet<int> columns_removed = (dir & Direction::Columns) ? handle_filter_changed(m->proxy_columns, m->source_columns, source_parent, Qt::Horizontal) : QSet<int>(); // We need to iterate over a copy of m->mapped_children because otherwise it may be changed by other code, invalidating // the iterator it2. // The m->mapped_children vector can be appended to with indexes which are no longer filtered // out (in create_mapping) when this function recurses for child indexes. - const QVector<QModelIndex> mappedChildren = m->mapped_children; - QVector<int> indexesToRemove; + const QList<QModelIndex> mappedChildren = m->mapped_children; + QList<int> indexesToRemove; for (int i = 0; i < mappedChildren.size(); ++i) { const QModelIndex &source_child_index = mappedChildren.at(i); if (rows_removed.contains(source_child_index.row()) || columns_removed.contains(source_child_index.column())) { indexesToRemove.push_back(i); remove_from_mapping(source_child_index); } else { - filter_changed(source_child_index); + filter_changed(dir, source_child_index); } } - QVector<int>::const_iterator removeIt = indexesToRemove.constEnd(); - const QVector<int>::const_iterator removeBegin = indexesToRemove.constBegin(); + QList<int>::const_iterator removeIt = indexesToRemove.constEnd(); + const QList<int>::const_iterator removeBegin = indexesToRemove.constBegin(); // We can't just remove these items from mappedChildren while iterating above and then // do something like m->mapped_children = mappedChildren, because mapped_children might @@ -1311,13 +1323,13 @@ void QSortFilterProxyModelPrivate::filter_changed(const QModelIndex &source_pare returns the removed items indexes */ QSet<int> QSortFilterProxyModelPrivate::handle_filter_changed( - QVector<int> &source_to_proxy, QVector<int> &proxy_to_source, + QList<int> &source_to_proxy, QList<int> &proxy_to_source, const QModelIndex &source_parent, Qt::Orientation orient) { Q_Q(QSortFilterProxyModel); // Figure out which mapped items to remove - QVector<int> source_items_remove; - for (int i = 0; i < proxy_to_source.count(); ++i) { + QList<int> source_items_remove; + for (int i = 0; i < proxy_to_source.size(); ++i) { const int source_item = proxy_to_source.at(i); if ((orient == Qt::Vertical) ? !filterAcceptsRowInternal(source_item, source_parent) @@ -1327,7 +1339,7 @@ QSet<int> QSortFilterProxyModelPrivate::handle_filter_changed( } } // Figure out which non-mapped items to insert - QVector<int> source_items_insert; + QList<int> source_items_insert; int source_count = source_to_proxy.size(); for (int source_item = 0; source_item < source_count; ++source_item) { if (source_to_proxy.at(source_item) == -1) { @@ -1348,10 +1360,10 @@ QSet<int> QSortFilterProxyModelPrivate::handle_filter_changed( insert_source_items(source_to_proxy, proxy_to_source, source_items_insert, source_parent, orient); } - return qVectorToSet(source_items_remove); + return qListToSet(source_items_remove); } -bool QSortFilterProxyModelPrivate::needsReorder(const QVector<int> &source_rows, const QModelIndex &source_parent) const +bool QSortFilterProxyModelPrivate::needsReorder(const QList<int> &source_rows, const QModelIndex &source_parent) const { Q_Q(const QSortFilterProxyModel); Q_ASSERT(source_sort_column != -1); @@ -1380,7 +1392,7 @@ bool QSortFilterProxyModelPrivate::needsReorder(const QVector<int> &source_rows, void QSortFilterProxyModelPrivate::_q_sourceDataChanged(const QModelIndex &source_top_left, const QModelIndex &source_bottom_right, - const QVector<int> &roles) + const QList<int> &roles) { Q_Q(QSortFilterProxyModel); if (!source_top_left.isValid() || !source_bottom_right.isValid()) @@ -1404,21 +1416,30 @@ void QSortFilterProxyModelPrivate::_q_sourceDataChanged(const QModelIndex &sourc const QModelIndex &source_bottom_right = data_changed.bottomRight; const QModelIndex source_parent = source_top_left.parent(); + bool change_in_unmapped_parent = false; IndexMap::const_iterator it = source_index_mapping.constFind(source_parent); if (it == source_index_mapping.constEnd()) { - // Don't care, since we don't have mapping for this index - continue; + // We don't have mapping for this index, so we cannot know how things + // changed (in case the change affects filtering) in order to forward + // the change correctly. + // But we can at least forward the signal "as is", if the row isn't + // filtered out, this is better than nothing. + it = create_mapping_recursive(source_parent); + if (it == source_index_mapping.constEnd()) + continue; + change_in_unmapped_parent = true; } + Mapping *m = it.value(); // Figure out how the source changes affect us - QVector<int> source_rows_remove; - QVector<int> source_rows_insert; - QVector<int> source_rows_change; - QVector<int> source_rows_resort; - int end = qMin(source_bottom_right.row(), m->proxy_rows.count() - 1); + QList<int> source_rows_remove; + QList<int> source_rows_insert; + QList<int> source_rows_change; + QList<int> source_rows_resort; + int end = qMin(source_bottom_right.row(), m->proxy_rows.size() - 1); for (int source_row = source_top_left.row(); source_row <= end; ++source_row) { - if (dynamic_sortfilter) { + if (dynamic_sortfilter && !change_in_unmapped_parent) { if (m->proxy_rows.at(source_row) != -1) { if (!filterAcceptsRowInternal(source_row, source_parent)) { // This source row no longer satisfies the filter, so it must be removed @@ -1445,8 +1466,8 @@ void QSortFilterProxyModelPrivate::_q_sourceDataChanged(const QModelIndex &sourc if (!source_rows_remove.isEmpty()) { remove_source_items(m->proxy_rows, m->source_rows, source_rows_remove, source_parent, Qt::Vertical); - QSet<int> source_rows_remove_set = qVectorToSet(source_rows_remove); - QVector<QModelIndex>::iterator childIt = m->mapped_children.end(); + QSet<int> source_rows_remove_set = qListToSet(source_rows_remove); + QList<QModelIndex>::iterator childIt = m->mapped_children.end(); while (childIt != m->mapped_children.begin()) { --childIt; const QModelIndex source_child_index = *childIt; @@ -1489,15 +1510,19 @@ void QSortFilterProxyModelPrivate::_q_sourceDataChanged(const QModelIndex &sourc while (source_left_column < source_bottom_right.column() && m->proxy_columns.at(source_left_column) == -1) ++source_left_column; - const QModelIndex proxy_top_left = create_index( - proxy_start_row, m->proxy_columns.at(source_left_column), it); - int source_right_column = source_bottom_right.column(); - while (source_right_column > source_top_left.column() - && m->proxy_columns.at(source_right_column) == -1) - --source_right_column; - const QModelIndex proxy_bottom_right = create_index( - proxy_end_row, m->proxy_columns.at(source_right_column), it); - emit q->dataChanged(proxy_top_left, proxy_bottom_right, roles); + if (m->proxy_columns.at(source_left_column) != -1) { + const QModelIndex proxy_top_left = create_index( + proxy_start_row, m->proxy_columns.at(source_left_column), it); + int source_right_column = source_bottom_right.column(); + while (source_right_column > source_top_left.column() + && m->proxy_columns.at(source_right_column) == -1) + --source_right_column; + if (m->proxy_columns.at(source_right_column) != -1) { + const QModelIndex proxy_bottom_right = create_index( + proxy_end_row, m->proxy_columns.at(source_right_column), it); + emit q->dataChanged(proxy_top_left, proxy_bottom_right, roles); + } + } } } @@ -1517,14 +1542,14 @@ void QSortFilterProxyModelPrivate::_q_sourceHeaderDataChanged(Qt::Orientation or Q_Q(QSortFilterProxyModel); Mapping *m = create_mapping(QModelIndex()).value(); - const QVector<int> &source_to_proxy = (orientation == Qt::Vertical) ? m->proxy_rows : m->proxy_columns; + const QList<int> &source_to_proxy = (orientation == Qt::Vertical) ? m->proxy_rows : m->proxy_columns; - QVector<int> proxy_positions; + QList<int> proxy_positions; proxy_positions.reserve(end - start + 1); { Q_ASSERT(source_to_proxy.size() > end); - QVector<int>::const_iterator it = source_to_proxy.constBegin() + start; - const QVector<int>::const_iterator endIt = source_to_proxy.constBegin() + end + 1; + QList<int>::const_iterator it = source_to_proxy.constBegin() + start; + const QList<int>::const_iterator endIt = source_to_proxy.constBegin() + end + 1; for ( ; it != endIt; ++it) { if (*it != -1) proxy_positions.push_back(*it); @@ -1564,8 +1589,7 @@ void QSortFilterProxyModelPrivate::_q_sourceReset() _q_clearMapping(); // All internal structures are deleted in clear() q->endResetModel(); - update_source_sort_column(); - if (dynamic_sortfilter && update_source_sort_column()) + if (update_source_sort_column() && dynamic_sortfilter) sort(); } @@ -1629,8 +1653,8 @@ void QSortFilterProxyModelPrivate::_q_sourceRowsAboutToBeInserted( const bool toplevel = !source_parent.isValid(); const bool recursive_accepted = filter_recursive && !toplevel && filterAcceptsRowInternal(source_parent.row(), source_parent.parent()); - //Force the creation of a mapping now, even if its empty. - //We need it because the proxy can be acessed at the moment it emits rowsAboutToBeInserted in insert_source_items + //Force the creation of a mapping now, even if it's empty. + //We need it because the proxy can be accessed at the moment it emits rowsAboutToBeInserted in insert_source_items if (!filter_recursive || toplevel || recursive_accepted) { if (can_create_mapping(source_parent)) create_mapping(source_parent); @@ -1678,7 +1702,7 @@ void QSortFilterProxyModelPrivate::_q_sourceRowsInserted( return; // last_top_source should now become visible - _q_sourceDataChanged(last_top_source, last_top_source, QVector<int>()); + _q_sourceDataChanged(last_top_source, last_top_source, QList<int>()); } } @@ -1714,7 +1738,7 @@ void QSortFilterProxyModelPrivate::_q_sourceRowsRemoved( } if (to_hide.isValid()) - _q_sourceDataChanged(to_hide, to_hide, QVector<int>()); + _q_sourceDataChanged(to_hide, to_hide, QList<int>()); } } @@ -1749,8 +1773,8 @@ void QSortFilterProxyModelPrivate::_q_sourceColumnsAboutToBeInserted( { Q_UNUSED(start); Q_UNUSED(end); - //Force the creation of a mapping now, even if its empty. - //We need it because the proxy can be acessed at the moment it emits columnsAboutToBeInserted in insert_source_items + //Force the creation of a mapping now, even if it's empty. + //We need it because the proxy can be accessed at the moment it emits columnsAboutToBeInserted in insert_source_items if (can_create_mapping(source_parent)) create_mapping(source_parent); } @@ -1797,7 +1821,10 @@ void QSortFilterProxyModelPrivate::_q_sourceColumnsRemoved( source_sort_column = -1; } - proxy_sort_column = q->mapFromSource(model->index(0,source_sort_column, source_parent)).column(); + if (source_sort_column >= 0) + proxy_sort_column = q->mapFromSource(model->index(0,source_sort_column, source_parent)).column(); + else + proxy_sort_column = -1; } void QSortFilterProxyModelPrivate::_q_sourceColumnsAboutToBeMoved( @@ -1919,9 +1946,9 @@ void QSortFilterProxyModelPrivate::_q_sourceColumnsMoved( \section1 Filtering In addition to sorting, QSortFilterProxyModel can be used to hide items - that do not match a certain filter. The filter is specified using a QRegExp + that do not match a certain filter. The filter is specified using a QRegularExpression object and is applied to the filterRole() (Qt::DisplayRole by default) of - each item, for a given column. The QRegExp object can be used to match a + each item, for a given column. The QRegularExpression object can be used to match a regular expression, a wildcard pattern, or a fixed string. For example: \snippet qsortfilterproxymodel-details/main.cpp 5 @@ -1949,8 +1976,9 @@ void QSortFilterProxyModelPrivate::_q_sourceColumnsMoved( example.) If you are working with large amounts of filtering and have to invoke - invalidateFilter() repeatedly, using reset() may be more efficient, - depending on the implementation of your model. However, reset() returns the + invalidateFilter() repeatedly, using beginResetModel() / endResetModel() may + be more efficient, depending on the implementation of your model. However, + beginResetModel() / endResetModel() returns the proxy model to its original state, losing selection information, and will cause the proxy model to be repopulated. @@ -1969,21 +1997,6 @@ void QSortFilterProxyModelPrivate::_q_sourceColumnsMoved( \note Some general guidelines for subclassing models are available in the \l{Model Subclassing Reference}. - \note With Qt 5, regular expression support has been improved through the - QRegularExpression class. QSortFilterProxyModel dating back prior to that - class creation, it originally supported only QRegExp. Since Qt 5.12, - QRegularExpression APIs have been added. Therefore, QRegExp APIs should be - considered deprecated and the QRegularExpression version should be used in - place. - - \warning Don't mix calls to the getters and setters of different regexp types - as this will lead to unexpected results. For maximum compatibility, the original - implementation has been kept. Therefore, if, for example, a call to - setFilterRegularExpression is made followed by another one to - setFilterFixedString, the first call will setup a QRegularExpression object - to use as filter while the second will setup a QRegExp in FixedString mode. - However, this is an implementation detail that might change in the future. - \sa QAbstractProxyModel, QAbstractItemModel, {Model/View Programming}, {Basic Sort/Filter Model Example}, {Custom Sort/Filter Model Example}, QIdentityProxyModel */ @@ -1996,17 +2009,8 @@ QSortFilterProxyModel::QSortFilterProxyModel(QObject *parent) : QAbstractProxyModel(*new QSortFilterProxyModelPrivate, parent) { Q_D(QSortFilterProxyModel); - d->proxy_sort_column = d->source_sort_column = -1; - d->sort_order = Qt::AscendingOrder; - d->sort_casesensitivity = Qt::CaseSensitive; - d->sort_role = Qt::DisplayRole; - d->sort_localeaware = false; - d->filter_column = 0; - d->filter_role = Qt::DisplayRole; - d->filter_recursive = false; - d->dynamic_sortfilter = true; - d->complete_insert = false; - connect(this, SIGNAL(modelReset()), this, SLOT(_q_clearMapping())); + QObjectPrivate::connect(this, &QAbstractItemModel::modelReset, d, + &QSortFilterProxyModelPrivate::_q_clearMapping); } /*! @@ -2031,56 +2035,10 @@ void QSortFilterProxyModel::setSourceModel(QAbstractItemModel *sourceModel) beginResetModel(); - disconnect(d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)), - this, SLOT(_q_sourceDataChanged(QModelIndex,QModelIndex,QVector<int>))); - - disconnect(d->model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)), - this, SLOT(_q_sourceHeaderDataChanged(Qt::Orientation,int,int))); - - disconnect(d->model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), - this, SLOT(_q_sourceRowsAboutToBeInserted(QModelIndex,int,int))); - - disconnect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)), - this, SLOT(_q_sourceRowsInserted(QModelIndex,int,int))); - - disconnect(d->model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)), - this, SLOT(_q_sourceColumnsAboutToBeInserted(QModelIndex,int,int))); - - disconnect(d->model, SIGNAL(columnsInserted(QModelIndex,int,int)), - this, SLOT(_q_sourceColumnsInserted(QModelIndex,int,int))); - - disconnect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), - this, SLOT(_q_sourceRowsAboutToBeRemoved(QModelIndex,int,int))); - - disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)), - this, SLOT(_q_sourceRowsRemoved(QModelIndex,int,int))); - - disconnect(d->model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)), - this, SLOT(_q_sourceColumnsAboutToBeRemoved(QModelIndex,int,int))); - - disconnect(d->model, SIGNAL(columnsRemoved(QModelIndex,int,int)), - this, SLOT(_q_sourceColumnsRemoved(QModelIndex,int,int))); - - disconnect(d->model, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)), - this, SLOT(_q_sourceRowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int))); - - disconnect(d->model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), - this, SLOT(_q_sourceRowsMoved(QModelIndex,int,int,QModelIndex,int))); - - disconnect(d->model, SIGNAL(columnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)), - this, SLOT(_q_sourceColumnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int))); - - disconnect(d->model, SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)), - this, SLOT(_q_sourceColumnsMoved(QModelIndex,int,int,QModelIndex,int))); - - disconnect(d->model, SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), - this, SLOT(_q_sourceLayoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); - - disconnect(d->model, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), - this, SLOT(_q_sourceLayoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); - - disconnect(d->model, SIGNAL(modelAboutToBeReset()), this, SLOT(_q_sourceAboutToBeReset())); - disconnect(d->model, SIGNAL(modelReset()), this, SLOT(_q_sourceReset())); + if (d->model) { + for (const QMetaObject::Connection &connection : std::as_const(d->sourceConnections)) + disconnect(connection); + } // same as in _q_sourceReset() d->invalidatePersistentIndexes(); @@ -2088,57 +2046,61 @@ void QSortFilterProxyModel::setSourceModel(QAbstractItemModel *sourceModel) QAbstractProxyModel::setSourceModel(sourceModel); - connect(d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)), - this, SLOT(_q_sourceDataChanged(QModelIndex,QModelIndex,QVector<int>))); + d->sourceConnections = std::array<QMetaObject::Connection, 18>{ + QObjectPrivate::connect(d->model, &QAbstractItemModel::dataChanged, d, + &QSortFilterProxyModelPrivate::_q_sourceDataChanged), - connect(d->model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)), - this, SLOT(_q_sourceHeaderDataChanged(Qt::Orientation,int,int))); + QObjectPrivate::connect(d->model, &QAbstractItemModel::headerDataChanged, d, + &QSortFilterProxyModelPrivate::_q_sourceHeaderDataChanged), - connect(d->model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), - this, SLOT(_q_sourceRowsAboutToBeInserted(QModelIndex,int,int))); + QObjectPrivate::connect(d->model, &QAbstractItemModel::rowsAboutToBeInserted, d, + &QSortFilterProxyModelPrivate::_q_sourceRowsAboutToBeInserted), - connect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)), - this, SLOT(_q_sourceRowsInserted(QModelIndex,int,int))); + QObjectPrivate::connect(d->model, &QAbstractItemModel::rowsInserted, d, + &QSortFilterProxyModelPrivate::_q_sourceRowsInserted), - connect(d->model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)), - this, SLOT(_q_sourceColumnsAboutToBeInserted(QModelIndex,int,int))); + QObjectPrivate::connect(d->model, &QAbstractItemModel::columnsAboutToBeInserted, d, + &QSortFilterProxyModelPrivate::_q_sourceColumnsAboutToBeInserted), - connect(d->model, SIGNAL(columnsInserted(QModelIndex,int,int)), - this, SLOT(_q_sourceColumnsInserted(QModelIndex,int,int))); + QObjectPrivate::connect(d->model, &QAbstractItemModel::columnsInserted, d, + &QSortFilterProxyModelPrivate::_q_sourceColumnsInserted), - connect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), - this, SLOT(_q_sourceRowsAboutToBeRemoved(QModelIndex,int,int))); + QObjectPrivate::connect(d->model, &QAbstractItemModel::rowsAboutToBeRemoved, d, + &QSortFilterProxyModelPrivate::_q_sourceRowsAboutToBeRemoved), - connect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)), - this, SLOT(_q_sourceRowsRemoved(QModelIndex,int,int))); + QObjectPrivate::connect(d->model, &QAbstractItemModel::rowsRemoved, d, + &QSortFilterProxyModelPrivate::_q_sourceRowsRemoved), - connect(d->model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)), - this, SLOT(_q_sourceColumnsAboutToBeRemoved(QModelIndex,int,int))); + QObjectPrivate::connect(d->model, &QAbstractItemModel::columnsAboutToBeRemoved, d, + &QSortFilterProxyModelPrivate::_q_sourceColumnsAboutToBeRemoved), - connect(d->model, SIGNAL(columnsRemoved(QModelIndex,int,int)), - this, SLOT(_q_sourceColumnsRemoved(QModelIndex,int,int))); + QObjectPrivate::connect(d->model, &QAbstractItemModel::columnsRemoved, d, + &QSortFilterProxyModelPrivate::_q_sourceColumnsRemoved), - connect(d->model, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)), - this, SLOT(_q_sourceRowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int))); + QObjectPrivate::connect(d->model, &QAbstractItemModel::rowsAboutToBeMoved, d, + &QSortFilterProxyModelPrivate::_q_sourceRowsAboutToBeMoved), - connect(d->model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), - this, SLOT(_q_sourceRowsMoved(QModelIndex,int,int,QModelIndex,int))); + QObjectPrivate::connect(d->model, &QAbstractItemModel::rowsMoved, d, + &QSortFilterProxyModelPrivate::_q_sourceRowsMoved), - connect(d->model, SIGNAL(columnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)), - this, SLOT(_q_sourceColumnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int))); + QObjectPrivate::connect(d->model, &QAbstractItemModel::columnsAboutToBeMoved, d, + &QSortFilterProxyModelPrivate::_q_sourceColumnsAboutToBeMoved), - connect(d->model, SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)), - this, SLOT(_q_sourceColumnsMoved(QModelIndex,int,int,QModelIndex,int))); + QObjectPrivate::connect(d->model, &QAbstractItemModel::columnsMoved, d, + &QSortFilterProxyModelPrivate::_q_sourceColumnsMoved), - connect(d->model, SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), - this, SLOT(_q_sourceLayoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); + QObjectPrivate::connect(d->model, &QAbstractItemModel::layoutAboutToBeChanged, d, + &QSortFilterProxyModelPrivate::_q_sourceLayoutAboutToBeChanged), - connect(d->model, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), - this, SLOT(_q_sourceLayoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); + QObjectPrivate::connect(d->model, &QAbstractItemModel::layoutChanged, d, + &QSortFilterProxyModelPrivate::_q_sourceLayoutChanged), - connect(d->model, SIGNAL(modelAboutToBeReset()), this, SLOT(_q_sourceAboutToBeReset())); - connect(d->model, SIGNAL(modelReset()), this, SLOT(_q_sourceReset())); + QObjectPrivate::connect(d->model, &QAbstractItemModel::modelAboutToBeReset, d, + &QSortFilterProxyModelPrivate::_q_sourceAboutToBeReset), + QObjectPrivate::connect(d->model, &QAbstractItemModel::modelReset, d, + &QSortFilterProxyModelPrivate::_q_sourceReset) + }; endResetModel(); if (d->update_source_sort_column() && d->dynamic_sortfilter) d->sort(); @@ -2155,7 +2117,7 @@ QModelIndex QSortFilterProxyModel::index(int row, int column, const QModelIndex QModelIndex source_parent = mapToSource(parent); // parent is already mapped at this point IndexMap::const_iterator it = d->create_mapping(source_parent); // but make sure that the children are mapped - if (it.value()->source_rows.count() <= row || it.value()->source_columns.count() <= column) + if (it.value()->source_rows.size() <= row || it.value()->source_columns.size() <= column) return QModelIndex(); return d->create_index(row, column, it); @@ -2186,7 +2148,7 @@ QModelIndex QSortFilterProxyModel::sibling(int row, int column, const QModelInde return QModelIndex(); const IndexMap::const_iterator it = d->index_to_iterator(idx); - if (it.value()->source_rows.count() <= row || it.value()->source_columns.count() <= column) + if (it.value()->source_rows.size() <= row || it.value()->source_columns.size() <= column) return QModelIndex(); return d->create_index(row, column, it); @@ -2202,7 +2164,7 @@ int QSortFilterProxyModel::rowCount(const QModelIndex &parent) const if (parent.isValid() && !source_parent.isValid()) return 0; IndexMap::const_iterator it = d->create_mapping(source_parent); - return it.value()->source_rows.count(); + return it.value()->source_rows.size(); } /*! @@ -2215,7 +2177,7 @@ int QSortFilterProxyModel::columnCount(const QModelIndex &parent) const if (parent.isValid() && !source_parent.isValid()) return 0; IndexMap::const_iterator it = d->create_mapping(source_parent); - return it.value()->source_columns.count(); + return it.value()->source_columns.size(); } /*! @@ -2234,7 +2196,7 @@ bool QSortFilterProxyModel::hasChildren(const QModelIndex &parent) const return true; //we assume we might have children that can be fetched QSortFilterProxyModelPrivate::Mapping *m = d->create_mapping(source_parent).value(); - return m->source_rows.count() != 0 && m->source_columns.count() != 0; + return m->source_rows.size() != 0 && m->source_columns.size() != 0; } /*! @@ -2268,15 +2230,15 @@ QVariant QSortFilterProxyModel::headerData(int section, Qt::Orientation orientat { Q_D(const QSortFilterProxyModel); IndexMap::const_iterator it = d->create_mapping(QModelIndex()); - if (it.value()->source_rows.count() * it.value()->source_columns.count() > 0) + if (it.value()->source_rows.size() * it.value()->source_columns.size() > 0) return QAbstractProxyModel::headerData(section, orientation, role); int source_section; if (orientation == Qt::Vertical) { - if (section < 0 || section >= it.value()->source_rows.count()) + if (section < 0 || section >= it.value()->source_rows.size()) return QVariant(); source_section = it.value()->source_rows.at(section); } else { - if (section < 0 || section >= it.value()->source_columns.count()) + if (section < 0 || section >= it.value()->source_columns.size()) return QVariant(); source_section = it.value()->source_columns.at(section); } @@ -2291,15 +2253,15 @@ bool QSortFilterProxyModel::setHeaderData(int section, Qt::Orientation orientati { Q_D(QSortFilterProxyModel); IndexMap::const_iterator it = d->create_mapping(QModelIndex()); - if (it.value()->source_rows.count() * it.value()->source_columns.count() > 0) + if (it.value()->source_rows.size() * it.value()->source_columns.size() > 0) return QAbstractProxyModel::setHeaderData(section, orientation, value, role); int source_section; if (orientation == Qt::Vertical) { - if (section < 0 || section >= it.value()->source_rows.count()) + if (section < 0 || section >= it.value()->source_rows.size()) return false; source_section = it.value()->source_rows.at(section); } else { - if (section < 0 || section >= it.value()->source_columns.count()) + if (section < 0 || section >= it.value()->source_columns.size()) return false; source_section = it.value()->source_columns.at(section); } @@ -2313,7 +2275,7 @@ QMimeData *QSortFilterProxyModel::mimeData(const QModelIndexList &indexes) const { Q_D(const QSortFilterProxyModel); QModelIndexList source_indexes; - source_indexes.reserve(indexes.count()); + source_indexes.reserve(indexes.size()); for (const QModelIndex &idx : indexes) source_indexes << mapToSource(idx); return d->model->mimeData(source_indexes); @@ -2359,10 +2321,10 @@ bool QSortFilterProxyModel::insertRows(int row, int count, const QModelIndex &pa if (parent.isValid() && !source_parent.isValid()) return false; QSortFilterProxyModelPrivate::Mapping *m = d->create_mapping(source_parent).value(); - if (row > m->source_rows.count()) + if (row > m->source_rows.size()) return false; - int source_row = (row >= m->source_rows.count() - ? m->proxy_rows.count() + int source_row = (row >= m->source_rows.size() + ? m->proxy_rows.size() : m->source_rows.at(row)); return d->model->insertRows(source_row, count, source_parent); } @@ -2379,10 +2341,10 @@ bool QSortFilterProxyModel::insertColumns(int column, int count, const QModelInd if (parent.isValid() && !source_parent.isValid()) return false; QSortFilterProxyModelPrivate::Mapping *m = d->create_mapping(source_parent).value(); - if (column > m->source_columns.count()) + if (column > m->source_columns.size()) return false; - int source_column = (column >= m->source_columns.count() - ? m->proxy_columns.count() + int source_column = (column >= m->source_columns.size() + ? m->proxy_columns.size() : m->source_columns.at(column)); return d->model->insertColumns(source_column, count, source_parent); } @@ -2399,22 +2361,22 @@ bool QSortFilterProxyModel::removeRows(int row, int count, const QModelIndex &pa if (parent.isValid() && !source_parent.isValid()) return false; QSortFilterProxyModelPrivate::Mapping *m = d->create_mapping(source_parent).value(); - if (row + count > m->source_rows.count()) + if (row + count > m->source_rows.size()) return false; if ((count == 1) - || ((d->source_sort_column < 0) && (m->proxy_rows.count() == m->source_rows.count()))) { + || ((d->source_sort_column < 0) && (m->proxy_rows.size() == m->source_rows.size()))) { int source_row = m->source_rows.at(row); return d->model->removeRows(source_row, count, source_parent); } // remove corresponding source intervals // ### if this proves to be slow, we can switch to single-row removal - QVector<int> rows; + QList<int> rows; rows.reserve(count); for (int i = row; i < row + count; ++i) rows.append(m->source_rows.at(i)); std::sort(rows.begin(), rows.end()); - int pos = rows.count() - 1; + int pos = rows.size() - 1; bool ok = true; while (pos >= 0) { const int source_end = rows.at(pos--); @@ -2441,19 +2403,19 @@ bool QSortFilterProxyModel::removeColumns(int column, int count, const QModelInd if (parent.isValid() && !source_parent.isValid()) return false; QSortFilterProxyModelPrivate::Mapping *m = d->create_mapping(source_parent).value(); - if (column + count > m->source_columns.count()) + if (column + count > m->source_columns.size()) return false; - if ((count == 1) || (m->proxy_columns.count() == m->source_columns.count())) { + if ((count == 1) || (m->proxy_columns.size() == m->source_columns.size())) { int source_column = m->source_columns.at(column); return d->model->removeColumns(source_column, count, source_parent); } // remove corresponding source intervals - QVector<int> columns; + QList<int> columns; columns.reserve(count); for (int i = column; i < column + count; ++i) columns.append(m->source_columns.at(i)); - int pos = columns.count() - 1; + int pos = columns.size() - 1; bool ok = true; while (pos >= 0) { const int source_end = columns.at(pos--); @@ -2473,11 +2435,7 @@ bool QSortFilterProxyModel::removeColumns(int column, int count, const QModelInd */ void QSortFilterProxyModel::fetchMore(const QModelIndex &parent) { - Q_D(QSortFilterProxyModel); - QModelIndex source_parent; - if (d->indexValid(parent)) - source_parent = mapToSource(parent); - d->model->fetchMore(source_parent); + QAbstractProxyModel::fetchMore(parent); } /*! @@ -2485,11 +2443,7 @@ void QSortFilterProxyModel::fetchMore(const QModelIndex &parent) */ bool QSortFilterProxyModel::canFetchMore(const QModelIndex &parent) const { - Q_D(const QSortFilterProxyModel); - QModelIndex source_parent; - if (d->indexValid(parent)) - source_parent = mapToSource(parent); - return d->model->canFetchMore(source_parent); + return QAbstractProxyModel::canFetchMore(parent); } /*! @@ -2497,11 +2451,7 @@ bool QSortFilterProxyModel::canFetchMore(const QModelIndex &parent) const */ Qt::ItemFlags QSortFilterProxyModel::flags(const QModelIndex &index) const { - Q_D(const QSortFilterProxyModel); - QModelIndex source_index; - if (d->indexValid(index)) - source_index = mapToSource(index); - return d->model->flags(source_index); + return QAbstractProxyModel::flags(index); } /*! @@ -2542,7 +2492,10 @@ QSize QSortFilterProxyModel::span(const QModelIndex &index) const } /*! - \reimp + \reimp + Sorts the model by \a column in the given \a order. + If the sort \a column is less than zero, the model will be sorted by source model row + in the given \a order. */ void QSortFilterProxyModel::sort(int column, Qt::SortOrder order) { @@ -2557,9 +2510,12 @@ void QSortFilterProxyModel::sort(int column, Qt::SortOrder order) /*! \since 4.5 - \brief the column currently used for sorting + \return the column currently used for sorting + + This returns the most recently used sort column. The default value is -1, + which means that this proxy model does not sort. - This returns the most recently used sort column. + \sa sort() */ int QSortFilterProxyModel::sortColumn() const { @@ -2569,9 +2525,12 @@ int QSortFilterProxyModel::sortColumn() const /*! \since 4.5 - \brief the order currently used for sorting + \return the order currently used for sorting - This returns the most recently used sort order. + This returns the most recently used sort order. The default value is + Qt::AscendingOrder. + + \sa sort() */ Qt::SortOrder QSortFilterProxyModel::sortOrder() const { @@ -2580,61 +2539,61 @@ Qt::SortOrder QSortFilterProxyModel::sortOrder() const } /*! - \property QSortFilterProxyModel::filterRegExp - \brief the QRegExp used to filter the contents of the source model - - Setting this property overwrites the current - \l{QSortFilterProxyModel::filterCaseSensitivity}{filterCaseSensitivity}. - By default, the QRegExp is an empty string matching all contents. - - If no QRegExp or an empty string is set, everything in the source model - will be accepted. - - \sa filterCaseSensitivity, setFilterWildcard(), setFilterFixedString() -*/ -QRegExp QSortFilterProxyModel::filterRegExp() const -{ - Q_D(const QSortFilterProxyModel); - return d->filter_data.regExp(); -} - -void QSortFilterProxyModel::setFilterRegExp(const QRegExp ®Exp) -{ - Q_D(QSortFilterProxyModel); - d->filter_about_to_be_changed(); - d->filter_data.setRegExp(regExp); - d->filter_changed(); -} - -#if QT_CONFIG(regularexpression) -/*! \since 5.12 \property QSortFilterProxyModel::filterRegularExpression \brief the QRegularExpression used to filter the contents of the source model - Setting this property overwrites the current - \l{QSortFilterProxyModel::filterCaseSensitivity}{filterCaseSensitivity}. + Setting this property through the QRegularExpression overload overwrites the + current \l{QSortFilterProxyModel::filterCaseSensitivity}{filterCaseSensitivity}. By default, the QRegularExpression is an empty string matching all contents. If no QRegularExpression or an empty string is set, everything in the source model will be accepted. + \note Setting this property propagates the case sensitivity of the new + regular expression to the \l filterCaseSensitivity property, and so breaks + its binding. Likewise explicitly setting \l filterCaseSensitivity changes + the case sensitivity of the current regular expression, thereby breaking + its binding. + \sa filterCaseSensitivity, setFilterWildcard(), setFilterFixedString() */ QRegularExpression QSortFilterProxyModel::filterRegularExpression() const { Q_D(const QSortFilterProxyModel); - return d->filter_data.regularExpression(); + return d->filter_regularexpression; +} + +QBindable<QRegularExpression> QSortFilterProxyModel::bindableFilterRegularExpression() +{ + Q_D(QSortFilterProxyModel); + return QBindable<QRegularExpression>(&d->filter_regularexpression); } void QSortFilterProxyModel::setFilterRegularExpression(const QRegularExpression ®ularExpression) { Q_D(QSortFilterProxyModel); + const QScopedPropertyUpdateGroup guard; + const bool regExpChanged = + regularExpression != d->filter_regularexpression.valueBypassingBindings(); + d->filter_regularexpression.removeBindingUnlessInWrapper(); + d->filter_casesensitive.removeBindingUnlessInWrapper(); + const Qt::CaseSensitivity cs = d->filter_casesensitive.valueBypassingBindings(); d->filter_about_to_be_changed(); - d->filter_data.setRegularExpression(regularExpression); - d->filter_changed(); + const Qt::CaseSensitivity updatedCs = + regularExpression.patternOptions() & QRegularExpression::CaseInsensitiveOption + ? Qt::CaseInsensitive : Qt::CaseSensitive; + d->filter_regularexpression.setValueBypassingBindings(regularExpression); + if (cs != updatedCs) + d->filter_casesensitive.setValueBypassingBindings(updatedCs); + d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows); + // Do not change the evaluation logic, but notify only if the regular + // expression has actually changed. + if (regExpChanged) + d->filter_regularexpression.notify(); + if (cs != updatedCs) + d->filter_casesensitive.notify(); } -#endif /*! \property QSortFilterProxyModel::filterKeyColumn @@ -2652,37 +2611,82 @@ int QSortFilterProxyModel::filterKeyColumn() const void QSortFilterProxyModel::setFilterKeyColumn(int column) { + // While introducing new bindable properties, we still update the value + // unconditionally (even if it didn't really change), and call the + // filter_about_to_be_changed()/filter_changed() methods, so that we do + // not break any code. However we do notify the observing bindings only + // if the column has actually changed Q_D(QSortFilterProxyModel); + d->filter_column.removeBindingUnlessInWrapper(); d->filter_about_to_be_changed(); - d->filter_column = column; - d->filter_changed(); + const auto oldColumn = d->filter_column.valueBypassingBindings(); + d->filter_column.setValueBypassingBindings(column); + d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows); + if (oldColumn != column) + d->filter_column.notify(); +} + +QBindable<int> QSortFilterProxyModel::bindableFilterKeyColumn() +{ + Q_D(QSortFilterProxyModel); + return QBindable<int>(&d->filter_column); } /*! \property QSortFilterProxyModel::filterCaseSensitivity - \brief the case sensitivity of the QRegExp pattern used to filter the - contents of the source model + \brief the case sensitivity of the QRegularExpression pattern used to filter the + contents of the source model. By default, the filter is case sensitive. - \sa filterRegExp, sortCaseSensitivity + \note Setting this property propagates the new case sensitivity to the + \l filterRegularExpression property, and so breaks its binding. Likewise + explicitly setting \l filterRegularExpression changes the current case + sensitivity, thereby breaking its binding. + + \sa filterRegularExpression, sortCaseSensitivity */ + +/*! + \since 5.15 + \fn void QSortFilterProxyModel::filterCaseSensitivityChanged(Qt::CaseSensitivity filterCaseSensitivity) + \brief This signal is emitted when the case sensitivity of the filter + changes to \a filterCaseSensitivity. + */ Qt::CaseSensitivity QSortFilterProxyModel::filterCaseSensitivity() const { Q_D(const QSortFilterProxyModel); - return d->filter_data.caseSensitivity(); + return d->filter_casesensitive; } void QSortFilterProxyModel::setFilterCaseSensitivity(Qt::CaseSensitivity cs) { Q_D(QSortFilterProxyModel); - if (cs == d->filter_data.caseSensitivity()) + d->filter_casesensitive.removeBindingUnlessInWrapper(); + d->filter_regularexpression.removeBindingUnlessInWrapper(); + if (cs == d->filter_casesensitive) return; + + const QScopedPropertyUpdateGroup guard; + QRegularExpression::PatternOptions options = + d->filter_regularexpression.value().patternOptions(); + options.setFlag(QRegularExpression::CaseInsensitiveOption, cs == Qt::CaseInsensitive); + d->filter_casesensitive.setValueBypassingBindings(cs); + d->filter_about_to_be_changed(); - d->filter_data.setCaseSensitivity(cs); - d->filter_changed(); - emit filterCaseSensitivityChanged(cs); + QRegularExpression re = d->filter_regularexpression; + re.setPatternOptions(options); + d->filter_regularexpression.setValueBypassingBindings(re); + d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows); + d->filter_regularexpression.notify(); + d->filter_casesensitive.notify(); +} + +QBindable<Qt::CaseSensitivity> QSortFilterProxyModel::bindableFilterCaseSensitivity() +{ + Q_D(QSortFilterProxyModel); + return QBindable<Qt::CaseSensitivity>(&d->filter_casesensitive); } /*! @@ -2694,6 +2698,13 @@ void QSortFilterProxyModel::setFilterCaseSensitivity(Qt::CaseSensitivity cs) \sa filterCaseSensitivity, lessThan() */ + +/*! + \since 5.15 + \fn void QSortFilterProxyModel::sortCaseSensitivityChanged(Qt::CaseSensitivity sortCaseSensitivity) + \brief This signal is emitted when the case sensitivity for sorting + changes to \a sortCaseSensitivity. +*/ Qt::CaseSensitivity QSortFilterProxyModel::sortCaseSensitivity() const { Q_D(const QSortFilterProxyModel); @@ -2703,12 +2714,19 @@ Qt::CaseSensitivity QSortFilterProxyModel::sortCaseSensitivity() const void QSortFilterProxyModel::setSortCaseSensitivity(Qt::CaseSensitivity cs) { Q_D(QSortFilterProxyModel); + d->sort_casesensitivity.removeBindingUnlessInWrapper(); if (d->sort_casesensitivity == cs) return; - d->sort_casesensitivity = cs; + d->sort_casesensitivity.setValueBypassingBindings(cs); d->sort(); - emit sortCaseSensitivityChanged(cs); + d->sort_casesensitivity.notify(); // also emits a signal +} + +QBindable<Qt::CaseSensitivity> QSortFilterProxyModel::bindableSortCaseSensitivity() +{ + Q_D(QSortFilterProxyModel); + return QBindable<Qt::CaseSensitivity>(&d->sort_casesensitivity); } /*! @@ -2720,6 +2738,13 @@ void QSortFilterProxyModel::setSortCaseSensitivity(Qt::CaseSensitivity cs) \sa sortCaseSensitivity, lessThan() */ + +/*! + \since 5.15 + \fn void QSortFilterProxyModel::sortLocaleAwareChanged(bool sortLocaleAware) + \brief This signal is emitted when the locale aware setting + changes to \a sortLocaleAware. +*/ bool QSortFilterProxyModel::isSortLocaleAware() const { Q_D(const QSortFilterProxyModel); @@ -2729,33 +2754,21 @@ bool QSortFilterProxyModel::isSortLocaleAware() const void QSortFilterProxyModel::setSortLocaleAware(bool on) { Q_D(QSortFilterProxyModel); + d->sort_localeaware.removeBindingUnlessInWrapper(); if (d->sort_localeaware == on) return; - d->sort_localeaware = on; + d->sort_localeaware.setValueBypassingBindings(on); d->sort(); - emit sortLocaleAwareChanged(on); + d->sort_localeaware.notify(); // also emits a signal } -/*! - \overload - - Sets the regular expression used to filter the contents - of the source model to \a pattern. - - \sa setFilterCaseSensitivity(), setFilterWildcard(), setFilterFixedString(), filterRegExp() -*/ -void QSortFilterProxyModel::setFilterRegExp(const QString &pattern) +QBindable<bool> QSortFilterProxyModel::bindableIsSortLocaleAware() { Q_D(QSortFilterProxyModel); - d->filter_about_to_be_changed(); - QRegExp rx(pattern); - rx.setCaseSensitivity(d->filter_data.caseSensitivity()); - d->filter_data.setRegExp(rx); - d->filter_changed(); + return QBindable<bool>(&d->sort_localeaware); } -#if QT_CONFIG(regularexpression) /*! \since 5.12 @@ -2765,46 +2778,70 @@ void QSortFilterProxyModel::setFilterRegExp(const QString &pattern) This method should be preferred for new code as it will use QRegularExpression internally. + This method will reset the regular expression options + but respect case sensitivity. + + \note Calling this method updates the regular expression, thereby breaking + the binding for \l filterRegularExpression. However it has no effect on the + \l filterCaseSensitivity bindings. + \sa setFilterCaseSensitivity(), setFilterWildcard(), setFilterFixedString(), filterRegularExpression() */ void QSortFilterProxyModel::setFilterRegularExpression(const QString &pattern) { Q_D(QSortFilterProxyModel); + d->filter_regularexpression.removeBindingUnlessInWrapper(); d->filter_about_to_be_changed(); - QRegularExpression rx(pattern); - d->filter_data.setRegularExpression(rx); - d->filter_changed(); + d->set_filter_pattern(pattern); + d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows); + d->filter_regularexpression.notify(); } -#endif /*! Sets the wildcard expression used to filter the contents of the source model to the given \a pattern. - \sa setFilterCaseSensitivity(), setFilterRegExp(), setFilterFixedString(), filterRegExp() + This method will reset the regular expression options + but respect case sensitivity. + + \note Calling this method updates the regular expression, thereby breaking + the binding for \l filterRegularExpression. However it has no effect on the + \l filterCaseSensitivity bindings. + + \sa setFilterCaseSensitivity(), setFilterRegularExpression(), setFilterFixedString(), filterRegularExpression() */ void QSortFilterProxyModel::setFilterWildcard(const QString &pattern) { Q_D(QSortFilterProxyModel); + d->filter_regularexpression.removeBindingUnlessInWrapper(); d->filter_about_to_be_changed(); - QRegExp rx(pattern, d->filter_data.caseSensitivity(), QRegExp::Wildcard); - d->filter_data.setRegExp(rx); - d->filter_changed(); + d->set_filter_pattern(QRegularExpression::wildcardToRegularExpression( + pattern, QRegularExpression::UnanchoredWildcardConversion)); + d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows); + d->filter_regularexpression.notify(); } /*! Sets the fixed string used to filter the contents of the source model to the given \a pattern. - \sa setFilterCaseSensitivity(), setFilterRegExp(), setFilterWildcard(), filterRegExp() + This method will reset the regular expression options + but respect case sensitivity. + + \note Calling this method updates the regular expression, thereby breaking + the binding for \l filterRegularExpression. However it has no effect on the + \l filterCaseSensitivity bindings. + + \sa setFilterCaseSensitivity(), setFilterRegularExpression(), setFilterWildcard(), filterRegularExpression() */ void QSortFilterProxyModel::setFilterFixedString(const QString &pattern) { Q_D(QSortFilterProxyModel); + d->filter_regularexpression.removeBindingUnlessInWrapper(); d->filter_about_to_be_changed(); - QRegExp rx(pattern, d->filter_data.caseSensitivity(), QRegExp::FixedString); - d->filter_data.setRegExp(rx); - d->filter_changed(); + d->set_filter_pattern(QRegularExpression::escape(pattern)); + d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows); + d->filter_regularexpression.notify(); } /*! @@ -2822,6 +2859,8 @@ void QSortFilterProxyModel::setFilterFixedString(const QString &pattern) QComboBox. The default value is true. + + \sa sortColumn() */ bool QSortFilterProxyModel::dynamicSortFilter() const { @@ -2831,21 +2870,43 @@ bool QSortFilterProxyModel::dynamicSortFilter() const void QSortFilterProxyModel::setDynamicSortFilter(bool enable) { + // While introducing new bindable properties, we still update the value + // unconditionally (even if it didn't really change), and call the + // sort() method, so that we do not break any code. + // However we do notify the observing bindings only if the value has + // actually changed. Q_D(QSortFilterProxyModel); - d->dynamic_sortfilter = enable; + d->dynamic_sortfilter.removeBindingUnlessInWrapper(); + const bool valueChanged = d->dynamic_sortfilter.value() != enable; + d->dynamic_sortfilter.setValueBypassingBindings(enable); if (enable) d->sort(); + if (valueChanged) + d->dynamic_sortfilter.notify(); +} + +QBindable<bool> QSortFilterProxyModel::bindableDynamicSortFilter() +{ + Q_D(QSortFilterProxyModel); + return QBindable<bool>(&d->dynamic_sortfilter); } /*! \since 4.2 \property QSortFilterProxyModel::sortRole - \brief the item role that is used to query the source model's data when sorting items + \brief the item role that is used to query the source model's data when + sorting items. The default value is Qt::DisplayRole. \sa lessThan() */ + +/*! + \since 5.15 + \fn void QSortFilterProxyModel::sortRoleChanged(int sortRole) + \brief This signal is emitted when the sort role changes to \a sortRole. +*/ int QSortFilterProxyModel::sortRole() const { Q_D(const QSortFilterProxyModel); @@ -2855,22 +2916,36 @@ int QSortFilterProxyModel::sortRole() const void QSortFilterProxyModel::setSortRole(int role) { Q_D(QSortFilterProxyModel); - if (d->sort_role == role) + d->sort_role.removeBindingUnlessInWrapper(); + if (d->sort_role.valueBypassingBindings() == role) return; - d->sort_role = role; + d->sort_role.setValueBypassingBindings(role); d->sort(); - emit sortRoleChanged(role); + d->sort_role.notify(); // also emits a signal +} + +QBindable<int> QSortFilterProxyModel::bindableSortRole() +{ + Q_D(QSortFilterProxyModel); + return QBindable<int>(&d->sort_role); } /*! \since 4.2 \property QSortFilterProxyModel::filterRole - \brief the item role that is used to query the source model's data when filtering items + \brief the item role that is used to query the source model's data when + filtering items. The default value is Qt::DisplayRole. \sa filterAcceptsRow() */ + +/*! + \since 5.15 + \fn void QSortFilterProxyModel::filterRoleChanged(int filterRole) + \brief This signal is emitted when the filter role changes to \a filterRole. +*/ int QSortFilterProxyModel::filterRole() const { Q_D(const QSortFilterProxyModel); @@ -2880,12 +2955,19 @@ int QSortFilterProxyModel::filterRole() const void QSortFilterProxyModel::setFilterRole(int role) { Q_D(QSortFilterProxyModel); - if (d->filter_role == role) + d->filter_role.removeBindingUnlessInWrapper(); + if (d->filter_role.valueBypassingBindings() == role) return; d->filter_about_to_be_changed(); - d->filter_role = role; - d->filter_changed(); - emit filterRoleChanged(role); + d->filter_role.setValueBypassingBindings(role); + d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows); + d->filter_role.notify(); // also emits a signal +} + +QBindable<int> QSortFilterProxyModel::bindableFilterRole() +{ + Q_D(QSortFilterProxyModel); + return QBindable<int>(&d->filter_role); } /*! @@ -2896,8 +2978,16 @@ void QSortFilterProxyModel::setFilterRole(int role) The default value is false. + \sa autoAcceptChildRows \sa filterAcceptsRow() */ + +/*! + \since 5.15 + \fn void QSortFilterProxyModel::recursiveFilteringEnabledChanged(bool recursiveFilteringEnabled) + \brief This signal is emitted when the recursive filter setting is changed + to \a recursiveFilteringEnabled. +*/ bool QSortFilterProxyModel::isRecursiveFilteringEnabled() const { Q_D(const QSortFilterProxyModel); @@ -2907,25 +2997,66 @@ bool QSortFilterProxyModel::isRecursiveFilteringEnabled() const void QSortFilterProxyModel::setRecursiveFilteringEnabled(bool recursive) { Q_D(QSortFilterProxyModel); + d->filter_recursive.removeBindingUnlessInWrapper(); if (d->filter_recursive == recursive) return; d->filter_about_to_be_changed(); - d->filter_recursive = recursive; - d->filter_changed(); - emit recursiveFilteringEnabledChanged(recursive); + d->filter_recursive.setValueBypassingBindings(recursive); + d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows); + d->filter_recursive.notify(); // also emits a signal } -#if QT_DEPRECATED_SINCE(5, 11) +QBindable<bool> QSortFilterProxyModel::bindableRecursiveFilteringEnabled() +{ + Q_D(QSortFilterProxyModel); + return QBindable<bool>(&d->filter_recursive); +} + +/*! + \since 6.0 + \property QSortFilterProxyModel::autoAcceptChildRows + \brief if true the proxy model will not filter out children of accepted + rows, even if they themselves would be filtered out otherwise. + + The default value is false. + + \sa recursiveFilteringEnabled + \sa filterAcceptsRow() +*/ + /*! - \obsolete + \since 6.0 + \fn void QSortFilterProxyModel::autoAcceptChildRowsChanged(bool autoAcceptChildRows) + + \brief This signals is emitted when the value of the \a autoAcceptChildRows property is changed. - This function is obsolete. Use invalidate() instead. + \sa autoAcceptChildRows */ -void QSortFilterProxyModel::clear() +bool QSortFilterProxyModel::autoAcceptChildRows() const +{ + Q_D(const QSortFilterProxyModel); + return d->accept_children; +} + +void QSortFilterProxyModel::setAutoAcceptChildRows(bool accept) { - invalidate(); + Q_D(QSortFilterProxyModel); + d->accept_children.removeBindingUnlessInWrapper(); + if (d->accept_children == accept) + return; + + d->filter_about_to_be_changed(); + d->accept_children.setValueBypassingBindings(accept); + d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows); + d->accept_children.notify(); // also emits a signal +} + +QBindable<bool> QSortFilterProxyModel::bindableAutoAcceptChildRows() +{ + Q_D(QSortFilterProxyModel); + return QBindable<bool>(&d->accept_children); } -#endif + /*! \since 4.3 @@ -2941,32 +3072,66 @@ void QSortFilterProxyModel::invalidate() emit layoutChanged(); } -#if QT_DEPRECATED_SINCE(5, 11) /*! - \obsolete + \since 4.3 + + Invalidates the current filtering. - This function is obsolete. Use invalidateFilter() instead. + This function should be called if you are implementing custom filtering + (e.g. filterAcceptsRow()), and your filter parameters have changed. + + \sa invalidate() + \sa invalidateColumnsFilter() + \sa invalidateRowsFilter() */ -void QSortFilterProxyModel::filterChanged() +void QSortFilterProxyModel::invalidateFilter() { - invalidateFilter(); + Q_D(QSortFilterProxyModel); + d->filter_changed(QSortFilterProxyModelPrivate::Direction::All); } -#endif /*! - \since 4.3 + \since 6.0 - Invalidates the current filtering. + Invalidates the current filtering for the columns. This function should be called if you are implementing custom filtering - (e.g. filterAcceptsRow()), and your filter parameters have changed. + (by filterAcceptsColumn()), and your filter parameters have changed. + This differs from invalidateFilter() in that it will not invoke + filterAcceptsRow(), but only filterAcceptsColumn(). You can use this + instead of invalidateFilter() if you want to hide or show a column where + the rows don't change. \sa invalidate() + \sa invalidateFilter() + \sa invalidateRowsFilter() */ -void QSortFilterProxyModel::invalidateFilter() +void QSortFilterProxyModel::invalidateColumnsFilter() +{ + Q_D(QSortFilterProxyModel); + d->filter_changed(QSortFilterProxyModelPrivate::Direction::Columns); +} + +/*! + \since 6.0 + + Invalidates the current filtering for the rows. + + This function should be called if you are implementing custom filtering + (by filterAcceptsRow()), and your filter parameters have changed. + This differs from invalidateFilter() in that it will not invoke + filterAcceptsColumn(), but only filterAcceptsRow(). You can use this + instead of invalidateFilter() if you want to hide or show a row where + the columns don't change. + + \sa invalidate() + \sa invalidateFilter() + \sa invalidateColumnsFilter() +*/ +void QSortFilterProxyModel::invalidateRowsFilter() { Q_D(QSortFilterProxyModel); - d->filter_changed(); + d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows); } /*! @@ -3026,43 +3191,41 @@ bool QSortFilterProxyModel::lessThan(const QModelIndex &source_left, const QMode should be accepted or not. This can be changed by setting the \l{QSortFilterProxyModel::filterRole}{filterRole} property. - \sa filterAcceptsColumn(), setFilterFixedString(), setFilterRegExp(), setFilterWildcard() + \sa filterAcceptsColumn(), setFilterFixedString(), setFilterRegularExpression(), setFilterWildcard() */ bool QSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { Q_D(const QSortFilterProxyModel); - if (d->filter_data.isEmpty()) + if (d->filter_regularexpression.value().pattern().isEmpty()) return true; + + int column_count = d->model->columnCount(source_parent); if (d->filter_column == -1) { - int column_count = d->model->columnCount(source_parent); for (int column = 0; column < column_count; ++column) { QModelIndex source_index = d->model->index(source_row, column, source_parent); QString key = d->model->data(source_index, d->filter_role).toString(); - if (d->filter_data.hasMatch(key)) + if (key.contains(d->filter_regularexpression.value())) return true; } return false; } - QModelIndex source_index = d->model->index(source_row, d->filter_column, source_parent); - if (!source_index.isValid()) // the column may not exist + + if (d->filter_column >= column_count) // the column may not exist return true; + QModelIndex source_index = d->model->index(source_row, d->filter_column, source_parent); QString key = d->model->data(source_index, d->filter_role).toString(); - return d->filter_data.hasMatch(key); + return key.contains(d->filter_regularexpression.value()); } /*! Returns \c true if the item in the column indicated by the given \a source_column and \a source_parent should be included in the model; otherwise returns \c false. - The default implementation returns \c true if the value held by the relevant item - matches the filter string, wildcard string or regular expression. - - \note By default, the Qt::DisplayRole is used to determine if the column - should be accepted or not. This can be changed by setting the \l - filterRole property. + \note The default implementation always returns \c true. You must reimplement this + method to get the described behavior. - \sa filterAcceptsRow(), setFilterFixedString(), setFilterRegExp(), setFilterWildcard() + \sa filterAcceptsRow(), setFilterFixedString(), setFilterRegularExpression(), setFilterWildcard() */ bool QSortFilterProxyModel::filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const { diff --git a/src/corelib/itemmodels/qsortfilterproxymodel.h b/src/corelib/itemmodels/qsortfilterproxymodel.h index 91253dd601..9d5b2fac9f 100644 --- a/src/corelib/itemmodels/qsortfilterproxymodel.h +++ b/src/corelib/itemmodels/qsortfilterproxymodel.h @@ -1,51 +1,12 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QSORTFILTERPROXYMODEL_H #define QSORTFILTERPROXYMODEL_H #include <QtCore/qabstractproxymodel.h> -#include <QtCore/qregexp.h> -#if QT_CONFIG(regularexpression) -# include <QtCore/qregularexpression.h> -#endif +#include <QtCore/qregularexpression.h> QT_REQUIRE_CONFIG(sortfilterproxymodel); @@ -62,18 +23,29 @@ class Q_CORE_EXPORT QSortFilterProxyModel : public QAbstractProxyModel friend class QSortFilterProxyModelGreaterThan; Q_OBJECT - Q_PROPERTY(QRegExp filterRegExp READ filterRegExp WRITE setFilterRegExp) -#if QT_CONFIG(regularexpression) - Q_PROPERTY(QRegularExpression filterRegularExpression READ filterRegularExpression WRITE setFilterRegularExpression) -#endif - Q_PROPERTY(int filterKeyColumn READ filterKeyColumn WRITE setFilterKeyColumn) - Q_PROPERTY(bool dynamicSortFilter READ dynamicSortFilter WRITE setDynamicSortFilter) - Q_PROPERTY(Qt::CaseSensitivity filterCaseSensitivity READ filterCaseSensitivity WRITE setFilterCaseSensitivity NOTIFY filterCaseSensitivityChanged) - Q_PROPERTY(Qt::CaseSensitivity sortCaseSensitivity READ sortCaseSensitivity WRITE setSortCaseSensitivity NOTIFY sortCaseSensitivityChanged) - Q_PROPERTY(bool isSortLocaleAware READ isSortLocaleAware WRITE setSortLocaleAware NOTIFY sortLocaleAwareChanged) - Q_PROPERTY(int sortRole READ sortRole WRITE setSortRole NOTIFY sortRoleChanged) - Q_PROPERTY(int filterRole READ filterRole WRITE setFilterRole NOTIFY filterRoleChanged) - Q_PROPERTY(bool recursiveFilteringEnabled READ isRecursiveFilteringEnabled WRITE setRecursiveFilteringEnabled NOTIFY recursiveFilteringEnabledChanged) + Q_PROPERTY(QRegularExpression filterRegularExpression READ filterRegularExpression + WRITE setFilterRegularExpression BINDABLE bindableFilterRegularExpression) + Q_PROPERTY(int filterKeyColumn READ filterKeyColumn WRITE setFilterKeyColumn + BINDABLE bindableFilterKeyColumn) + Q_PROPERTY(bool dynamicSortFilter READ dynamicSortFilter WRITE setDynamicSortFilter + BINDABLE bindableDynamicSortFilter) + Q_PROPERTY(Qt::CaseSensitivity filterCaseSensitivity READ filterCaseSensitivity + WRITE setFilterCaseSensitivity NOTIFY filterCaseSensitivityChanged + BINDABLE bindableFilterCaseSensitivity) + Q_PROPERTY(Qt::CaseSensitivity sortCaseSensitivity READ sortCaseSensitivity + WRITE setSortCaseSensitivity NOTIFY sortCaseSensitivityChanged + BINDABLE bindableSortCaseSensitivity) + Q_PROPERTY(bool isSortLocaleAware READ isSortLocaleAware WRITE setSortLocaleAware + NOTIFY sortLocaleAwareChanged BINDABLE bindableIsSortLocaleAware) + Q_PROPERTY(int sortRole READ sortRole WRITE setSortRole NOTIFY sortRoleChanged + BINDABLE bindableSortRole) + Q_PROPERTY(int filterRole READ filterRole WRITE setFilterRole NOTIFY filterRoleChanged + BINDABLE bindableFilterRole) + Q_PROPERTY(bool recursiveFilteringEnabled READ isRecursiveFilteringEnabled + WRITE setRecursiveFilteringEnabled NOTIFY recursiveFilteringEnabledChanged + BINDABLE bindableRecursiveFilteringEnabled) + Q_PROPERTY(bool autoAcceptChildRows READ autoAcceptChildRows WRITE setAutoAcceptChildRows + NOTIFY autoAcceptChildRowsChanged BINDABLE bindableAutoAcceptChildRows) public: explicit QSortFilterProxyModel(QObject *parent = nullptr); @@ -87,51 +59,53 @@ public: QItemSelection mapSelectionToSource(const QItemSelection &proxySelection) const override; QItemSelection mapSelectionFromSource(const QItemSelection &sourceSelection) const override; - QRegExp filterRegExp() const; - -#if QT_CONFIG(regularexpression) QRegularExpression filterRegularExpression() const; -#endif + QBindable<QRegularExpression> bindableFilterRegularExpression(); int filterKeyColumn() const; void setFilterKeyColumn(int column); + QBindable<int> bindableFilterKeyColumn(); Qt::CaseSensitivity filterCaseSensitivity() const; void setFilterCaseSensitivity(Qt::CaseSensitivity cs); + QBindable<Qt::CaseSensitivity> bindableFilterCaseSensitivity(); Qt::CaseSensitivity sortCaseSensitivity() const; void setSortCaseSensitivity(Qt::CaseSensitivity cs); + QBindable<Qt::CaseSensitivity> bindableSortCaseSensitivity(); bool isSortLocaleAware() const; void setSortLocaleAware(bool on); + QBindable<bool> bindableIsSortLocaleAware(); int sortColumn() const; Qt::SortOrder sortOrder() const; bool dynamicSortFilter() const; void setDynamicSortFilter(bool enable); + QBindable<bool> bindableDynamicSortFilter(); int sortRole() const; void setSortRole(int role); + QBindable<int> bindableSortRole(); int filterRole() const; void setFilterRole(int role); + QBindable<int> bindableFilterRole(); bool isRecursiveFilteringEnabled() const; void setRecursiveFilteringEnabled(bool recursive); + QBindable<bool> bindableRecursiveFilteringEnabled(); + + bool autoAcceptChildRows() const; + void setAutoAcceptChildRows(bool accept); + QBindable<bool> bindableAutoAcceptChildRows(); public Q_SLOTS: - void setFilterRegExp(const QString &pattern); - void setFilterRegExp(const QRegExp ®Exp); -#if QT_CONFIG(regularexpression) void setFilterRegularExpression(const QString &pattern); void setFilterRegularExpression(const QRegularExpression ®ularExpression); -#endif void setFilterWildcard(const QString &pattern); void setFilterFixedString(const QString &pattern); -#if QT_DEPRECATED_SINCE(5, 11) - QT_DEPRECATED_X("Use QSortFilterProxyModel::invalidate") void clear(); -#endif void invalidate(); protected: @@ -139,10 +113,9 @@ protected: virtual bool filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const; virtual bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const; -#if QT_DEPRECATED_SINCE(5, 11) - QT_DEPRECATED_X("Use QSortFilterProxyModel::invalidateFilter") void filterChanged(); -#endif void invalidateFilter(); + void invalidateRowsFilter(); + void invalidateColumnsFilter(); public: using QObject::parent; @@ -194,30 +167,11 @@ Q_SIGNALS: void sortRoleChanged(int sortRole); void filterRoleChanged(int filterRole); void recursiveFilteringEnabledChanged(bool recursiveFilteringEnabled); + void autoAcceptChildRowsChanged(bool autoAcceptChildRows); private: Q_DECLARE_PRIVATE(QSortFilterProxyModel) Q_DISABLE_COPY(QSortFilterProxyModel) - - Q_PRIVATE_SLOT(d_func(), void _q_sourceDataChanged(const QModelIndex &source_top_left, const QModelIndex &source_bottom_right, const QVector<int> &roles)) - Q_PRIVATE_SLOT(d_func(), void _q_sourceHeaderDataChanged(Qt::Orientation orientation, int start, int end)) - Q_PRIVATE_SLOT(d_func(), void _q_sourceAboutToBeReset()) - Q_PRIVATE_SLOT(d_func(), void _q_sourceReset()) - Q_PRIVATE_SLOT(d_func(), void _q_sourceLayoutAboutToBeChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint)) - Q_PRIVATE_SLOT(d_func(), void _q_sourceLayoutChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint)) - Q_PRIVATE_SLOT(d_func(), void _q_sourceRowsAboutToBeInserted(const QModelIndex &source_parent, int start, int end)) - Q_PRIVATE_SLOT(d_func(), void _q_sourceRowsInserted(const QModelIndex &source_parent, int start, int end)) - Q_PRIVATE_SLOT(d_func(), void _q_sourceRowsAboutToBeRemoved(const QModelIndex &source_parent, int start, int end)) - Q_PRIVATE_SLOT(d_func(), void _q_sourceRowsRemoved(const QModelIndex &source_parent, int start, int end)) - Q_PRIVATE_SLOT(d_func(), void _q_sourceRowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)) - Q_PRIVATE_SLOT(d_func(), void _q_sourceRowsMoved(QModelIndex,int,int,QModelIndex,int)) - Q_PRIVATE_SLOT(d_func(), void _q_sourceColumnsAboutToBeInserted(const QModelIndex &source_parent, int start, int end)) - Q_PRIVATE_SLOT(d_func(), void _q_sourceColumnsInserted(const QModelIndex &source_parent, int start, int end)) - Q_PRIVATE_SLOT(d_func(), void _q_sourceColumnsAboutToBeRemoved(const QModelIndex &source_parent, int start, int end)) - Q_PRIVATE_SLOT(d_func(), void _q_sourceColumnsRemoved(const QModelIndex &source_parent, int start, int end)) - Q_PRIVATE_SLOT(d_func(), void _q_sourceColumnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)) - Q_PRIVATE_SLOT(d_func(), void _q_sourceColumnsMoved(QModelIndex,int,int,QModelIndex,int)) - Q_PRIVATE_SLOT(d_func(), void _q_clearMapping()) }; QT_END_NAMESPACE diff --git a/src/corelib/itemmodels/qstringlistmodel.cpp b/src/corelib/itemmodels/qstringlistmodel.cpp index a950783ed8..dfbe72b289 100644 --- a/src/corelib/itemmodels/qstringlistmodel.cpp +++ b/src/corelib/itemmodels/qstringlistmodel.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only /* A simple model that uses a QStringList as its data source. @@ -43,7 +7,8 @@ #include "qstringlistmodel.h" -#include <QtCore/qvector.h> +#include <QtCore/qlist.h> +#include <QtCore/qmap.h> #include <algorithm> @@ -121,7 +86,7 @@ int QStringListModel::rowCount(const QModelIndex &parent) const if (parent.isValid()) return 0; - return lst.count(); + return lst.size(); } /*! @@ -129,7 +94,7 @@ int QStringListModel::rowCount(const QModelIndex &parent) const */ QModelIndex QStringListModel::sibling(int row, int column, const QModelIndex &idx) const { - if (!idx.isValid() || column != 0 || row >= lst.count() || row < 0) + if (!idx.isValid() || column != 0 || row >= lst.size() || row < 0) return QModelIndex(); return createIndex(row, 0); @@ -231,7 +196,6 @@ bool QStringListModel::setData(const QModelIndex &index, const QVariant &value, return false; } -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) /*! \reimp \since 6.0 @@ -240,7 +204,6 @@ 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. @@ -329,12 +292,12 @@ bool QStringListModel::moveRows(const QModelIndex &sourceParent, int sourceRow, return true; } -static bool ascendingLessThan(const QPair<QString, int> &s1, const QPair<QString, int> &s2) +static bool ascendingLessThan(const std::pair<QString, int> &s1, const std::pair<QString, int> &s2) { return s1.first < s2.first; } -static bool decendingLessThan(const QPair<QString, int> &s1, const QPair<QString, int> &s2) +static bool decendingLessThan(const std::pair<QString, int> &s1, const std::pair<QString, int> &s2) { return s1.first > s2.first; } @@ -346,11 +309,11 @@ void QStringListModel::sort(int, Qt::SortOrder order) { emit layoutAboutToBeChanged(QList<QPersistentModelIndex>(), VerticalSortHint); - QVector<QPair<QString, int> > list; - const int lstCount = lst.count(); + QList<std::pair<QString, int>> list; + const int lstCount = lst.size(); list.reserve(lstCount); for (int i = 0; i < lstCount; ++i) - list.append(QPair<QString, int>(lst.at(i), i)); + list.emplace_back(lst.at(i), i); if (order == Qt::AscendingOrder) std::sort(list.begin(), list.end(), ascendingLessThan); @@ -358,7 +321,7 @@ void QStringListModel::sort(int, Qt::SortOrder order) std::sort(list.begin(), list.end(), decendingLessThan); lst.clear(); - QVector<int> forwarding(lstCount); + QList<int> forwarding(lstCount); for (int i = 0; i < lstCount; ++i) { lst.append(list.at(i).first); forwarding[list.at(i).second] = i; @@ -366,7 +329,7 @@ void QStringListModel::sort(int, Qt::SortOrder order) QModelIndexList oldList = persistentIndexList(); QModelIndexList newList; - const int numOldIndexes = oldList.count(); + const int numOldIndexes = oldList.size(); newList.reserve(numOldIndexes); for (int i = 0; i < numOldIndexes; ++i) newList.append(index(forwarding.at(oldList.at(i).row()), 0)); diff --git a/src/corelib/itemmodels/qstringlistmodel.h b/src/corelib/itemmodels/qstringlistmodel.h index 86725ea80b..b93a9c7173 100644 --- a/src/corelib/itemmodels/qstringlistmodel.h +++ b/src/corelib/itemmodels/qstringlistmodel.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QSTRINGLISTMODEL_H #define QSTRINGLISTMODEL_H @@ -59,9 +23,7 @@ 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; diff --git a/src/corelib/itemmodels/qtransposeproxymodel.cpp b/src/corelib/itemmodels/qtransposeproxymodel.cpp index 4853f90632..621b54782e 100644 --- a/src/corelib/itemmodels/qtransposeproxymodel.cpp +++ b/src/corelib/itemmodels/qtransposeproxymodel.cpp @@ -1,47 +1,12 @@ -/**************************************************************************** -** -** Copyright (C) 2018 Luca Beldi <v.ronin@yahoo.it> -** 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$ -** -****************************************************************************/ +// Copyright (C) 2018 Luca Beldi <v.ronin@yahoo.it> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qtransposeproxymodel.h" #include <private/qtransposeproxymodel_p.h> -#include <QtCore/qvector.h> +#include <QtCore/qlist.h> #include <QtCore/qmetaobject.h> #include <QtCore/qsize.h> +#include <QtCore/qmap.h> QT_BEGIN_NAMESPACE @@ -49,9 +14,8 @@ QModelIndex QTransposeProxyModelPrivate::uncheckedMapToSource(const QModelIndex { if (!model || !proxyIndex.isValid()) return QModelIndex(); - if (proxyIndex.internalPointer()) - return model->createIndex(proxyIndex.column(), proxyIndex.row(), proxyIndex.internalPointer()); - return model->index(proxyIndex.column(), proxyIndex.row()); + Q_Q(const QTransposeProxyModel); + return q->createSourceIndex(proxyIndex.column(), proxyIndex.row(), proxyIndex.internalPointer()); } QModelIndex QTransposeProxyModelPrivate::uncheckedMapFromSource(const QModelIndex &sourceIndex) const @@ -65,9 +29,10 @@ QModelIndex QTransposeProxyModelPrivate::uncheckedMapFromSource(const QModelInde void QTransposeProxyModelPrivate::onLayoutChanged(const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint) { Q_Q(QTransposeProxyModel); + Q_ASSERT(layoutChangeProxyIndexes.size() == layoutChangePersistentIndexes.size()); QModelIndexList toList; toList.reserve(layoutChangePersistentIndexes.size()); - for (const QPersistentModelIndex &persistIdx : qAsConst(layoutChangePersistentIndexes)) + for (const QPersistentModelIndex &persistIdx : std::as_const(layoutChangePersistentIndexes)) toList << q->mapFromSource(persistIdx); q->changePersistentIndexList(layoutChangeProxyIndexes, toList); layoutChangeProxyIndexes.clear(); @@ -84,34 +49,42 @@ void QTransposeProxyModelPrivate::onLayoutChanged(const QList<QPersistentModelIn emit q->layoutChanged(proxyParents, proxyHint); } -void QTransposeProxyModelPrivate::onLayoutAboutToBeChanged(const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint) +void QTransposeProxyModelPrivate::onLayoutAboutToBeChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint) { Q_Q(QTransposeProxyModel); + QList<QPersistentModelIndex> proxyParents; + proxyParents.reserve(sourceParents.size()); + for (const QPersistentModelIndex &parent : sourceParents) { + if (!parent.isValid()) { + proxyParents << QPersistentModelIndex(); + continue; + } + const QModelIndex mappedParent = q->mapFromSource(parent); + Q_ASSERT(mappedParent.isValid()); + proxyParents << mappedParent; + } + QAbstractItemModel::LayoutChangeHint proxyHint = QAbstractItemModel::NoLayoutChangeHint; + if (hint == QAbstractItemModel::VerticalSortHint) + proxyHint = QAbstractItemModel::HorizontalSortHint; + else if (hint == QAbstractItemModel::HorizontalSortHint) + proxyHint = QAbstractItemModel::VerticalSortHint; + emit q->layoutAboutToBeChanged(proxyParents, proxyHint); const QModelIndexList proxyPersistentIndexes = q->persistentIndexList(); layoutChangeProxyIndexes.clear(); layoutChangePersistentIndexes.clear(); layoutChangeProxyIndexes.reserve(proxyPersistentIndexes.size()); layoutChangePersistentIndexes.reserve(proxyPersistentIndexes.size()); - for (const QPersistentModelIndex &proxyPersistentIndex : proxyPersistentIndexes) { + for (const QModelIndex &proxyPersistentIndex : proxyPersistentIndexes) { layoutChangeProxyIndexes << proxyPersistentIndex; Q_ASSERT(proxyPersistentIndex.isValid()); const QPersistentModelIndex srcPersistentIndex = q->mapToSource(proxyPersistentIndex); Q_ASSERT(srcPersistentIndex.isValid()); layoutChangePersistentIndexes << srcPersistentIndex; } - QList<QPersistentModelIndex> proxyParents; - proxyParents.reserve(parents.size()); - for (auto& srcParent : parents) - proxyParents << q->mapFromSource(srcParent); - QAbstractItemModel::LayoutChangeHint proxyHint = QAbstractItemModel::NoLayoutChangeHint; - if (hint == QAbstractItemModel::VerticalSortHint) - proxyHint = QAbstractItemModel::HorizontalSortHint; - else if (hint == QAbstractItemModel::HorizontalSortHint) - proxyHint = QAbstractItemModel::VerticalSortHint; - emit q->layoutAboutToBeChanged(proxyParents, proxyHint); } -void QTransposeProxyModelPrivate::onDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>& roles) +void QTransposeProxyModelPrivate::onDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, + const QList<int> &roles) { Q_Q(QTransposeProxyModel); emit q->dataChanged(q->mapFromSource(topLeft), q->mapFromSource(bottomRight), roles); @@ -162,6 +135,7 @@ void QTransposeProxyModelPrivate::onRowsAboutToBeMoved(const QModelIndex &source /*! \since 5.13 \class QTransposeProxyModel + \inmodule QtCore \brief This proxy transposes the source model. This model will make the rows of the source model become columns of the proxy model and vice-versa. @@ -198,14 +172,14 @@ void QTransposeProxyModel::setSourceModel(QAbstractItemModel* newSourceModel) return; beginResetModel(); if (d->model) { - for (const QMetaObject::Connection& discIter : qAsConst(d->sourceConnections)) + for (const QMetaObject::Connection& discIter : std::as_const(d->sourceConnections)) disconnect(discIter); } d->sourceConnections.clear(); QAbstractProxyModel::setSourceModel(newSourceModel); if (d->model) { using namespace std::placeholders; - d->sourceConnections = QVector<QMetaObject::Connection>{ + d->sourceConnections = QList<QMetaObject::Connection>{ connect(d->model, &QAbstractItemModel::modelAboutToBeReset, this, &QTransposeProxyModel::beginResetModel), connect(d->model, &QAbstractItemModel::modelReset, this, &QTransposeProxyModel::endResetModel), connect(d->model, &QAbstractItemModel::dataChanged, this, std::bind(&QTransposeProxyModelPrivate::onDataChanged, d, _1, _2, _3)), @@ -439,8 +413,8 @@ bool QTransposeProxyModel::moveColumns(const QModelIndex &sourceParent, int sour */ void QTransposeProxyModel::sort(int column, Qt::SortOrder order) { - Q_UNUSED(column) - Q_UNUSED(order) + Q_UNUSED(column); + Q_UNUSED(order); return; } diff --git a/src/corelib/itemmodels/qtransposeproxymodel.h b/src/corelib/itemmodels/qtransposeproxymodel.h index 854a547fd4..67ce6f3bef 100644 --- a/src/corelib/itemmodels/qtransposeproxymodel.h +++ b/src/corelib/itemmodels/qtransposeproxymodel.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 Luca Beldi <v.ronin@yahoo.it> -** 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$ -** -****************************************************************************/ +// Copyright (C) 2018 Luca Beldi <v.ronin@yahoo.it> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QTRANSPOSEPROXYMODEL_H #define QTRANSPOSEPROXYMODEL_H diff --git a/src/corelib/itemmodels/qtransposeproxymodel_p.h b/src/corelib/itemmodels/qtransposeproxymodel_p.h index fb5ce5c117..2e0c09b95b 100644 --- a/src/corelib/itemmodels/qtransposeproxymodel_p.h +++ b/src/corelib/itemmodels/qtransposeproxymodel_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 Luca Beldi <v.ronin@yahoo.it> -** 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$ -** -****************************************************************************/ +// Copyright (C) 2018 Luca Beldi <v.ronin@yahoo.it> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QTRANSPOSEPROXYMODEL_P_H #define QTRANSPOSEPROXYMODEL_P_H @@ -62,14 +26,14 @@ class QTransposeProxyModelPrivate : public QAbstractProxyModelPrivate Q_DISABLE_COPY(QTransposeProxyModelPrivate) private: QTransposeProxyModelPrivate() = default; - QVector<QMetaObject::Connection> sourceConnections; - QVector<QPersistentModelIndex> layoutChangePersistentIndexes; + QList<QMetaObject::Connection> sourceConnections; + QList<QPersistentModelIndex> layoutChangePersistentIndexes; QModelIndexList layoutChangeProxyIndexes; QModelIndex uncheckedMapToSource(const QModelIndex &proxyIndex) const; QModelIndex uncheckedMapFromSource(const QModelIndex &sourceIndex) const; void onLayoutChanged(const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint); void onLayoutAboutToBeChanged(const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint); - void onDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>& roles); + void onDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList<int> &roles); void onHeaderDataChanged(Qt::Orientation orientation, int first, int last); void onColumnsAboutToBeInserted(const QModelIndex &parent, int first, int last); void onColumnsAboutToBeRemoved(const QModelIndex &parent, int first, int last); |