diff options
Diffstat (limited to 'src/corelib/itemmodels')
21 files changed, 1586 insertions, 1591 deletions
diff --git a/src/corelib/itemmodels/qabstractitemmodel.cpp b/src/corelib/itemmodels/qabstractitemmodel.cpp index 8da33e0c4e..cd29f2fcc2 100644 --- a/src/corelib/itemmodels/qabstractitemmodel.cpp +++ b/src/corelib/itemmodels/qabstractitemmodel.cpp @@ -1,42 +1,6 @@ -/**************************************************************************** -** -** 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> -** 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> @@ -50,16 +14,21 @@ # 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 @@ -243,7 +212,7 @@ void QPersistentModelIndexData::destroy(QPersistentModelIndexData *data) */ /*! - \fn template <typename Container> QModelRoleDataSpan::QModelRoleDataSpan(Container &c) noexcept + \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. @@ -413,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; @@ -430,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); } /*! @@ -508,7 +477,7 @@ QPersistentModelIndex::operator 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; @@ -522,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; @@ -717,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(); } @@ -746,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(); } @@ -766,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" }, @@ -774,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() { @@ -873,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); @@ -894,7 +864,7 @@ void QAbstractItemModelPrivate::rowsAboutToBeInserted(const QModelIndex &parent, Q_UNUSED(last); QList<QPersistentModelIndexData *> persistent_moved; if (first < q->rowCount(parent)) { - for (auto *data : qAsConst(persistent.indexes)) { + 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); @@ -930,7 +900,7 @@ void QAbstractItemModelPrivate::itemsAboutToBeMoved(const QModelIndex &srcParent const bool sameParent = (srcParent == destinationParent); const bool movingUp = (srcFirst > destinationChild); - for (auto *data : qAsConst(persistent.indexes)) { + for (auto *data : std::as_const(persistent.indexes)) { const QModelIndex &index = data->index; const QModelIndex &parent = index.parent(); const bool isSourceIndex = (parent == srcParent); @@ -1028,7 +998,7 @@ void QAbstractItemModelPrivate::rowsAboutToBeRemoved(const QModelIndex &parent, 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 (auto *data : qAsConst(persistent.indexes)) { + for (auto *data : std::as_const(persistent.indexes)) { bool level_changed = false; QModelIndex current = data->index; while (current.isValid()) { @@ -1080,7 +1050,7 @@ void QAbstractItemModelPrivate::columnsAboutToBeInserted(const QModelIndex &pare Q_UNUSED(last); QList<QPersistentModelIndexData *> persistent_moved; if (first < q->columnCount(parent)) { - for (auto *data : qAsConst(persistent.indexes)) { + 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); @@ -1113,7 +1083,7 @@ void QAbstractItemModelPrivate::columnsAboutToBeRemoved(const QModelIndex &paren 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 (auto *data : qAsConst(persistent.indexes)) { + for (auto *data : std::as_const(persistent.indexes)) { bool level_changed = false; QModelIndex current = data->index; while (current.isValid()) { @@ -1530,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} */ /*! @@ -1772,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. @@ -1857,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 @@ -1889,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 @@ -2086,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() */ @@ -2153,7 +2131,7 @@ QStringList QAbstractItemModel::mimeTypes() const */ QMimeData *QAbstractItemModel::mimeData(const QModelIndexList &indexes) const { - if (indexes.count() <= 0) + if (indexes.size() <= 0) return nullptr; QStringList types = mimeTypes(); if (types.isEmpty()) @@ -2192,7 +2170,7 @@ bool QAbstractItemModel::canDropMimeData(const QMimeData *data, Qt::DropAction a 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; } @@ -2533,10 +2511,10 @@ 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) @@ -2549,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; @@ -2571,8 +2549,10 @@ 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 @@ -2615,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); } } } @@ -2808,15 +2788,15 @@ bool QAbstractItemModel::decodeData(int row, int column, const QModelIndex &pare // Compute the number of continuous rows upon insertion and modify the rows to match QList<int> rowsToInsert(bottom + 1); - for (int i = 0; i < rows.count(); ++i) + 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); @@ -3018,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. @@ -3345,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; } @@ -3380,7 +3366,6 @@ 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()); } @@ -3474,8 +3459,8 @@ void QAbstractItemModel::changePersistentIndexList(const QModelIndexList &from, if (d->persistent.indexes.isEmpty()) return; QList<QPersistentModelIndexData *> toBeReinserted; - toBeReinserted.reserve(to.count()); - for (int i = 0; i < from.count(); ++i) { + 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)); @@ -3488,7 +3473,7 @@ void QAbstractItemModel::changePersistentIndexList(const QModelIndexList &from, } } - for (auto *data : qAsConst(toBeReinserted)) + for (auto *data : std::as_const(toBeReinserted)) d->persistent.insertMultiAtEnd(data->index, data); } @@ -3501,8 +3486,8 @@ QModelIndexList QAbstractItemModel::persistentIndexList() const { Q_D(const QAbstractItemModel); QModelIndexList result; - result.reserve(d->persistent.indexes.count()); - for (auto *data : qAsConst(d->persistent.indexes)) + result.reserve(d->persistent.indexes.size()); + for (auto *data : std::as_const(d->persistent.indexes)) result.append(data->index); return result; } @@ -3754,10 +3739,9 @@ void QAbstractItemModel::multiData(const QModelIndex &index, QModelRoleDataSpan \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 */ /*! @@ -3908,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 */ /*! @@ -4186,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 789ed60ce4..8f22f14989 100644 --- a/src/corelib/itemmodels/qabstractitemmodel.h +++ b/src/corelib/itemmodels/qabstractitemmodel.h @@ -1,42 +1,6 @@ -/**************************************************************************** -** -** 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> -** 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 @@ -214,17 +178,17 @@ 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(qExchange(other.d, nullptr)) {} + : d(std::exchange(other.d, nullptr)) {} QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QPersistentModelIndex) - inline void swap(QPersistentModelIndex &other) noexcept { qSwap(d, other.d); } - bool operator==(const QModelIndex &other) const; - bool operator!=(const QModelIndex &other) const; + 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 QModelIndex() const; int row() const; @@ -272,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); @@ -310,28 +273,28 @@ public: virtual Qt::DropActions supportedDropActions() const; virtual Qt::DropActions supportedDragActions() const; - 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, @@ -387,10 +350,10 @@ 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(); @@ -536,10 +499,16 @@ inline Qt::ItemFlags QModelIndex::flags() const { return m ? m->flags(*this) : Qt::ItemFlags(); } inline size_t qHash(const QModelIndex &index, size_t seed = 0) noexcept -{ return size_t((size_t(index.row()) << 4) + size_t(index.column()) + index.internalId()) ^ seed; } +{ +#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 dbab493c9c..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 @@ -99,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); } @@ -154,6 +121,56 @@ public: }; 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 #endif // QABSTRACTITEMMODEL_P_H diff --git a/src/corelib/itemmodels/qabstractproxymodel.cpp b/src/corelib/itemmodels/qabstractproxymodel.cpp index e5d8c68e72..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,17 +174,41 @@ 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()); } - emit sourceModelChanged(QPrivateSignal()); + d->model.notify(); } } @@ -153,6 +223,12 @@ QAbstractItemModel *QAbstractProxyModel::sourceModel() const return d->model; } +QBindable<QAbstractItemModel *> QAbstractProxyModel::bindableSourceModel() +{ + Q_D(QAbstractProxyModel); + return QBindable<QAbstractItemModel *>(&d->model); +} + /*! \reimp */ @@ -241,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); } @@ -257,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)); } /*! @@ -283,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); } /*! @@ -382,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); @@ -474,6 +556,26 @@ QHash<int,QByteArray> QAbstractProxyModel::roleNames() const 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 diff --git a/src/corelib/itemmodels/qabstractproxymodel.h b/src/corelib/itemmodels/qabstractproxymodel.h index ef4feb5564..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; @@ -102,12 +68,19 @@ Q_SIGNALS: void sourceModelChanged(QPrivateSignal); 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 09deee4429..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 QList<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; - QList<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; @@ -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); @@ -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 @@ -455,7 +437,11 @@ QSize QConcatenateTablesProxyModel::span(const QModelIndex &index) const QList<QAbstractItemModel *> QConcatenateTablesProxyModel::sourceModels() const { Q_D(const QConcatenateTablesProxyModel); - return d->m_models.toList(); + QList<QAbstractItemModel *> ret; + ret.reserve(d->m_models.size()); + for (const auto &info : d->m_models) + ret.push_back(info.model); + return ret; } /*! @@ -469,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,QList<int>)), this, SLOT(_q_slotDataChanged(QModelIndex,QModelIndex,QList<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(); @@ -507,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(); @@ -523,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 @@ -533,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 @@ -542,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 @@ -552,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 @@ -561,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 @@ -577,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); @@ -590,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 @@ -604,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); @@ -617,19 +625,27 @@ void QConcatenateTablesProxyModelPrivate::_q_slotColumnsRemoved(const QModelInde } } -void QConcatenateTablesProxyModelPrivate::_q_slotDataChanged(const QModelIndex &from, const QModelIndex &to, const QList<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); @@ -651,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())) @@ -668,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(); @@ -692,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() @@ -718,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; @@ -732,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(); @@ -744,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 a119bab098..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 @@ -82,21 +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 QList<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 8634ddd520..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 QList<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,QList<int>)), - this, SLOT(_q_sourceDataChanged(QModelIndex,QModelIndex,QList<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,QList<int>)), - SLOT(_q_sourceDataChanged(QModelIndex,QModelIndex,QList<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,14 +399,14 @@ 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); @@ -476,7 +416,9 @@ void QIdentityProxyModelPrivate::_q_sourceColumnsInserted(const QModelIndex &par 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); @@ -489,7 +431,7 @@ void QIdentityProxyModelPrivate::_q_sourceColumnsMoved(const QModelIndex &source 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); @@ -499,7 +441,9 @@ void QIdentityProxyModelPrivate::_q_sourceColumnsRemoved(const QModelIndex &pare q->endRemoveColumns(); } -void QIdentityProxyModelPrivate::_q_sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList<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); @@ -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,14 +544,15 @@ 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); @@ -611,7 +562,9 @@ void QIdentityProxyModelPrivate::_q_sourceRowsInserted(const QModelIndex &parent 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); @@ -624,7 +577,7 @@ void QIdentityProxyModelPrivate::_q_sourceRowsMoved(const QModelIndex &sourcePar 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); diff --git a/src/corelib/itemmodels/qidentityproxymodel.h b/src/corelib/itemmodels/qidentityproxymodel.h index ce2ac68f89..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, QList<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 b12cfc471b..6df60aaf61 100644 --- a/src/corelib/itemmodels/qitemselectionmodel.cpp +++ b/src/corelib/itemmodels/qitemselectionmodel.cpp @@ -1,45 +1,13 @@ -/**************************************************************************** -** -** Copyright (C) 2020 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> @@ -47,6 +15,9 @@ QT_BEGIN_NAMESPACE +QT_IMPL_METATYPE_EXTERN(QItemSelectionRange) +QT_IMPL_METATYPE_EXTERN(QItemSelection) + /*! \class QItemSelectionRange \inmodule QtCore @@ -56,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 @@ -245,17 +218,17 @@ 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. */ @@ -267,7 +240,7 @@ QItemSelectionRange QItemSelectionRange::intersected(const QItemSelectionRange & */ -static void rowLengthsFromRange(const QItemSelectionRange &range, QList<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(); @@ -278,11 +251,16 @@ static void rowLengthsFromRange(const QItemSelectionRange &range, QList<QPair<QP // 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) { @@ -294,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); } } @@ -312,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 */ @@ -324,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; } } @@ -438,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)) @@ -456,9 +434,9 @@ QModelIndexList QItemSelection::indexes() const return qSelectionIndexes<QModelIndexList>(*this); } -static QList<QPair<QPersistentModelIndex, uint>> qSelectionPersistentRowLengths(const QItemSelection &sel) +static QList<std::pair<QPersistentModelIndex, uint>> qSelectionPersistentRowLengths(const QItemSelection &sel) { - QList<QPair<QPersistentModelIndex, uint>> result; + QList<std::pair<QPersistentModelIndex, uint>> result; for (const QItemSelectionRange &range : sel) rowLengthsFromRange(range, result); return result; @@ -482,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); @@ -509,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); @@ -576,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 @@ -639,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); @@ -649,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); @@ -664,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()) @@ -688,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 @@ -699,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 @@ -723,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); @@ -744,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); @@ -768,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); @@ -776,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); @@ -797,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()); } /*! @@ -828,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(); @@ -837,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); @@ -870,14 +877,14 @@ void QItemSelectionModelPrivate::_q_layoutAboutToBeChanged(const QList<QPersiste /*! \internal */ -static QItemSelection mergeRowLengths(const QList<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; @@ -885,7 +892,7 @@ static QItemSelection mergeRowLengths(const QList<QPair<QPersistentModelIndex, u } 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; @@ -915,7 +922,7 @@ 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; @@ -925,7 +932,7 @@ static QItemSelection mergeIndexes(const QList<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; @@ -948,11 +955,11 @@ static QItemSelection mergeIndexes(const QList<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(); @@ -975,7 +982,7 @@ static QItemSelection mergeIndexes(const QList<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 @@ -993,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) @@ -1051,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 @@ -1086,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 */ /*! @@ -1179,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() */ @@ -1238,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; } @@ -1250,7 +1296,7 @@ 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. d->ranges.removeIf(QtFunctionObjects::IsNotValid()); @@ -1323,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); @@ -1343,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; } @@ -1394,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) @@ -1403,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; } @@ -1425,14 +1469,14 @@ bool QItemSelectionModel::isSelected(const QModelIndex &index) const 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()) @@ -1441,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); @@ -1461,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)) { @@ -1500,14 +1543,14 @@ bool QItemSelectionModel::isRowSelected(int row, const QModelIndex &parent) cons 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()) @@ -1516,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; @@ -1531,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; @@ -1540,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)) { @@ -1574,14 +1616,14 @@ bool QItemSelectionModel::isColumnSelected(int column, const QModelIndex &parent 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(); @@ -1590,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; } } @@ -1610,14 +1651,14 @@ bool QItemSelectionModel::rowIntersectsSelection(int row, const QModelIndex &par 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(); @@ -1626,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; } } @@ -1637,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)); } } @@ -1699,7 +1766,7 @@ QModelIndexList QItemSelectionModel::selectedRows(int column) const 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++) { @@ -1728,7 +1795,7 @@ QModelIndexList QItemSelectionModel::selectedColumns(int row) const 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++) { @@ -1794,7 +1861,7 @@ const QItemSelection QItemSelectionModel::selection() const */ QAbstractItemModel *QItemSelectionModel::model() { - return d_func()->model; + return d_func()->model.value(); } /*! @@ -1802,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; } /*! @@ -1815,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(); } /*! @@ -1845,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); @@ -1862,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); @@ -1881,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 cf8fadde1b..c4b8dadc97 100644 --- a/src/corelib/itemmodels/qitemselectionmodel.h +++ b/src/corelib/itemmodels/qitemselectionmodel.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 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 @@ -60,8 +24,8 @@ public: 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(); } @@ -93,12 +57,12 @@ public: bool intersects(const QItemSelectionRange &other) const; 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); } - + { return !operator==(other); } +#endif inline bool isValid() const { return (tl.isValid() && br.isValid() && tl.parent() == br.parent() @@ -110,6 +74,12 @@ 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_RELOCATABLE_TYPE); @@ -120,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) @@ -169,6 +144,7 @@ public: const QAbstractItemModel *model() const; QAbstractItemModel *model(); + QBindable<QAbstractItemModel *> bindableModel(); void setModel(QAbstractItemModel *model); @@ -195,17 +171,11 @@ 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) -// We export each out-of-line method invidually to prevent MSVC from +// We export each out-of-line method individually to prevent MSVC from // exporting the whole QList class. class QItemSelection : public QList<QItemSelectionRange> { @@ -231,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 c520e50517..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; QList<QPersistentModelIndex> savedPersistentIndexes; QList<QPersistentModelIndex> savedPersistentCurrentIndexes; - QList<QPair<QPersistentModelIndex, uint>> savedPersistentRowLengths; - QList<QPair<QPersistentModelIndex, uint>> savedPersistentCurrentRowLengths; + 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 38417bbba6..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 QList<QPair<QModelIndex, QPersistentModelIndex>> QModelIndexPairList; +using QModelIndexPairList = QList<std::pair<QModelIndex, QPersistentModelIndex>>; struct QSortFilterProxyModelDataChanged { @@ -144,9 +108,9 @@ private: class QSortFilterProxyModelPrivate : public QAbstractProxyModelPrivate { +public: Q_DECLARE_PUBLIC(QSortFilterProxyModel) -public: enum class Direction { Rows = 1, Columns = 2, @@ -164,35 +128,143 @@ public: mutable QHash<QModelIndex, Mapping*> source_index_mapping; - int source_sort_column; - int proxy_sort_column; - Qt::SortOrder sort_order; - Qt::CaseSensitivity sort_casesensitivity; - int sort_role; - bool sort_localeaware; + void setSortCaseSensitivityForwarder(Qt::CaseSensitivity cs) + { + q_func()->setSortCaseSensitivity(cs); + } + void sortCaseSensitivityChangedForwarder(Qt::CaseSensitivity cs) + { + emit q_func()->sortCaseSensitivityChanged(cs); + } - int filter_column; - int filter_role; - QRegularExpression filter_data; - QModelIndex last_top_source; + 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) + { + q_func()->setRecursiveFilteringEnabled(recursive); + } + void recursiveFilteringEnabledChangedForwarder(bool recursive) + { + emit q_func()->recursiveFilteringEnabledChanged(recursive); + } + + void setAutoAcceptChildRowsForwarder(bool accept) { q_func()->setAutoAcceptChildRows(accept); } + void autoAcceptChildRowsChangedForwarder(bool accept) + { + emit q_func()->autoAcceptChildRowsChanged(accept); + } + + void setDynamicSortFilterForwarder(bool enable) { q_func()->setDynamicSortFilter(enable); } + + void setFilterCaseSensitivityForwarder(Qt::CaseSensitivity cs) + { + q_func()->setFilterCaseSensitivity(cs); + } + void filterCaseSensitivityChangedForwarder(Qt::CaseSensitivity cs) + { + emit q_func()->filterCaseSensitivityChanged(cs); + } + + void setFilterRegularExpressionForwarder(const QRegularExpression &re) + { + q_func()->setFilterRegularExpression(re); + } + + 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) + + Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QSortFilterProxyModelPrivate, int, sort_role, + &QSortFilterProxyModelPrivate::setSortRoleForwarder, + &QSortFilterProxyModelPrivate::sortRoleChangedForwarder, + Qt::DisplayRole) + + Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QSortFilterProxyModelPrivate, int, filter_column, + &QSortFilterProxyModelPrivate::setFilterKeyColumnForwarder, + 0) + + Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QSortFilterProxyModelPrivate, int, filter_role, + &QSortFilterProxyModelPrivate::setFilterRoleForwarder, + &QSortFilterProxyModelPrivate::filterRoleChangedForwarder, + Qt::DisplayRole) + + Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS( + QSortFilterProxyModelPrivate, bool, sort_localeaware, + &QSortFilterProxyModelPrivate::setSortLocaleAwareForwarder, + &QSortFilterProxyModelPrivate::sortLocaleAwareChangedForwarder, false) + + 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 accept_children; - 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 { @@ -260,10 +332,10 @@ public: int find_source_sort_column() const; void sort_source_rows(QList<int> &source_rows, const QModelIndex &source_parent) const; - QList<QPair<int, QList<int>>> proxy_intervals_for_source_items_to_add( + 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; - QList<QPair<int, int>> proxy_intervals_for_source_items( + 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( QList<int> &source_to_proxy, QList<int> &proxy_to_source, @@ -379,7 +451,7 @@ bool QSortFilterProxyModelPrivate::recursiveChildAcceptsRow(int source_row, cons 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; } @@ -445,6 +517,29 @@ IndexMap::const_iterator QSortFilterProxyModelPrivate::create_mapping( 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()) @@ -593,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{}); } } @@ -609,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. */ -QList<QPair<int, int>> QSortFilterProxyModelPrivate::proxy_intervals_for_source_items( +QList<std::pair<int, int>> QSortFilterProxyModelPrivate::proxy_intervals_for_source_items( const QList<int> &source_to_proxy, const QList<int> &source_items) const { - QList<QPair<int, int>> proxy_intervals; + QList<std::pair<int, int>> proxy_intervals; if (source_items.isEmpty()) return proxy_intervals; @@ -629,19 +726,19 @@ QList<QPair<int, int>> QSortFilterProxyModelPrivate::proxy_intervals_for_source_ ++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.removeIf([](QPair<int, int> interval) { return interval.first < 0; }); + proxy_intervals.removeIf([](std::pair<int, int> interval) { return interval.first < 0; }); return proxy_intervals; } @@ -660,15 +757,17 @@ void QSortFilterProxyModelPrivate::remove_source_items( { 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, @@ -722,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. */ -QList<QPair<int, QList<int>>> QSortFilterProxyModelPrivate::proxy_intervals_for_source_items_to_add( +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); - QList<QPair<int, QList<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; - QList<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; @@ -783,7 +881,7 @@ QList<QPair<int, QList<int>>> QSortFilterProxyModelPrivate::proxy_intervals_for_ } // Add interval to result - proxy_intervals.append(QPair<int, QList<int>>(proxy_item, source_items_in_interval)); + proxy_intervals.emplace_back(proxy_item, std::move(source_items_in_interval)); } return proxy_intervals; } @@ -811,7 +909,7 @@ void QSortFilterProxyModelPrivate::insert_source_items( const auto end = proxy_intervals.rend(); for (auto it = proxy_intervals.rbegin(); it != end; ++it) { - const QPair<int, QList<int>> &interval = *it; + const std::pair<int, QList<int>> &interval = *it; const int proxy_start = interval.first; const QList<int> &source_items = interval.second; const int proxy_end = proxy_start + source_items.size() - 1; @@ -823,8 +921,9 @@ 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, proxy_start); @@ -862,12 +961,12 @@ 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; @@ -1039,7 +1138,7 @@ 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 - QList<QPair<QModelIndex, Mapping *>> moved_source_index_mappings; + 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; @@ -1073,12 +1172,12 @@ 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 - for (auto &pair : qAsConst(moved_source_index_mappings)) { + for (auto &pair : std::as_const(moved_source_index_mappings)) { pair.second->source_parent = pair.first; source_index_mapping.insert(pair.first, pair.second); } @@ -1093,7 +1192,7 @@ void QSortFilterProxyModelPrivate::proxy_item_range( { 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) @@ -1126,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; } @@ -1146,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) { @@ -1168,9 +1267,10 @@ void QSortFilterProxyModelPrivate::update_persistent_indexes( */ void QSortFilterProxyModelPrivate::filter_about_to_be_changed(const QModelIndex &source_parent) { - if (!filter_data.pattern().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); + } } @@ -1229,7 +1329,7 @@ QSet<int> QSortFilterProxyModelPrivate::handle_filter_changed( Q_Q(QSortFilterProxyModel); // Figure out which mapped items to remove QList<int> source_items_remove; - for (int i = 0; i < proxy_to_source.count(); ++i) { + 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) @@ -1316,11 +1416,20 @@ 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 @@ -1328,9 +1437,9 @@ void QSortFilterProxyModelPrivate::_q_sourceDataChanged(const QModelIndex &sourc 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.count() - 1); + 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 @@ -1480,9 +1589,7 @@ void QSortFilterProxyModelPrivate::_q_sourceReset() _q_clearMapping(); // All internal structures are deleted in clear() q->endResetModel(); - create_mapping(QModelIndex()); - update_source_sort_column(); - if (dynamic_sortfilter && update_source_sort_column()) + if (update_source_sort_column() && dynamic_sortfilter) sort(); } @@ -1546,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); @@ -1666,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); } @@ -1714,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( @@ -1899,18 +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->accept_children = 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); } /*! @@ -1935,56 +2035,10 @@ void QSortFilterProxyModel::setSourceModel(QAbstractItemModel *sourceModel) beginResetModel(); - disconnect(d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QList<int>)), - this, SLOT(_q_sourceDataChanged(QModelIndex,QModelIndex,QList<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(); @@ -1992,59 +2046,62 @@ void QSortFilterProxyModel::setSourceModel(QAbstractItemModel *sourceModel) QAbstractProxyModel::setSourceModel(sourceModel); - connect(d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QList<int>)), - this, SLOT(_q_sourceDataChanged(QModelIndex,QModelIndex,QList<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(); - d->create_mapping(QModelIndex()); if (d->update_source_sort_column() && d->dynamic_sortfilter) d->sort(); } @@ -2060,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); @@ -2091,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); @@ -2107,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(); } /*! @@ -2120,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(); } /*! @@ -2139,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; } /*! @@ -2173,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); } @@ -2196,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); } @@ -2218,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); @@ -2264,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); } @@ -2284,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); } @@ -2304,10 +2361,10 @@ 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); } @@ -2319,7 +2376,7 @@ bool QSortFilterProxyModel::removeRows(int row, int count, const QModelIndex &pa 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--); @@ -2346,9 +2403,9 @@ 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); } @@ -2358,7 +2415,7 @@ bool QSortFilterProxyModel::removeColumns(int column, int count, const QModelInd 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--); @@ -2378,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); } /*! @@ -2390,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); } /*! @@ -2402,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); } /*! @@ -2447,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) { @@ -2462,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. + This returns the most recently used sort column. The default value is -1, + which means that this proxy model does not sort. + + \sa sort() */ int QSortFilterProxyModel::sortColumn() const { @@ -2474,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. The default value is + Qt::AscendingOrder. - This returns the most recently used sort order. + \sa sort() */ Qt::SortOrder QSortFilterProxyModel::sortOrder() const { @@ -2484,35 +2538,62 @@ Qt::SortOrder QSortFilterProxyModel::sortOrder() const return d->sort_order; } -#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; + 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 = regularExpression; + 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 @@ -2530,10 +2611,25 @@ 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; + 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); } /*! @@ -2544,6 +2640,11 @@ void QSortFilterProxyModel::setFilterKeyColumn(int column) By default, the filter is case sensitive. + \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 */ @@ -2556,22 +2657,36 @@ void QSortFilterProxyModel::setFilterKeyColumn(int column) Qt::CaseSensitivity QSortFilterProxyModel::filterCaseSensitivity() const { Q_D(const QSortFilterProxyModel); - return d->filter_data.patternOptions() & QRegularExpression::CaseInsensitiveOption ? - Qt::CaseInsensitive : Qt::CaseSensitive; + return d->filter_casesensitive; } void QSortFilterProxyModel::setFilterCaseSensitivity(Qt::CaseSensitivity cs) { Q_D(QSortFilterProxyModel); - QRegularExpression::PatternOptions o = QRegularExpression::NoPatternOption; - if (cs == Qt::CaseInsensitive) - o = QRegularExpression::CaseInsensitiveOption; - if (o == d->filter_data.patternOptions()) + 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.setPatternOptions(o); + QRegularExpression re = d->filter_regularexpression; + re.setPatternOptions(options); + d->filter_regularexpression.setValueBypassingBindings(re); d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows); - emit filterCaseSensitivityChanged(cs); + d->filter_regularexpression.notify(); + d->filter_casesensitive.notify(); +} + +QBindable<Qt::CaseSensitivity> QSortFilterProxyModel::bindableFilterCaseSensitivity() +{ + Q_D(QSortFilterProxyModel); + return QBindable<Qt::CaseSensitivity>(&d->filter_casesensitive); } /*! @@ -2599,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); } /*! @@ -2632,15 +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 +} + +QBindable<bool> QSortFilterProxyModel::bindableIsSortLocaleAware() +{ + Q_D(QSortFilterProxyModel); + return QBindable<bool>(&d->sort_localeaware); } -#if QT_CONFIG(regularexpression) /*! \since 5.12 @@ -2650,46 +2778,70 @@ void QSortFilterProxyModel::setSortLocaleAware(bool on) 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.patternOptions() & QRegularExpression::CaseInsensitiveOption); - d->filter_data.setPattern(pattern); + 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. + 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(); - QString p = QRegularExpression::wildcardToRegularExpression(pattern, QRegularExpression::UnanchoredWildcardConversion); - d->filter_data.setPattern(p); + 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. + 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(); - d->filter_data.setPattern(QRegularExpression::escape(pattern)); + d->set_filter_pattern(QRegularExpression::escape(pattern)); d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows); + d->filter_regularexpression.notify(); } /*! @@ -2707,6 +2859,8 @@ void QSortFilterProxyModel::setFilterFixedString(const QString &pattern) QComboBox. The default value is true. + + \sa sortColumn() */ bool QSortFilterProxyModel::dynamicSortFilter() const { @@ -2716,10 +2870,25 @@ 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); } /*! @@ -2747,11 +2916,18 @@ 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); } /*! @@ -2779,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_role.setValueBypassingBindings(role); d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows); - emit filterRoleChanged(role); + d->filter_role.notify(); // also emits a signal +} + +QBindable<int> QSortFilterProxyModel::bindableFilterRole() +{ + Q_D(QSortFilterProxyModel); + return QBindable<int>(&d->filter_role); } /*! @@ -2814,12 +2997,19 @@ 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_recursive.setValueBypassingBindings(recursive); d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows); - emit recursiveFilteringEnabledChanged(recursive); + d->filter_recursive.notify(); // also emits a signal +} + +QBindable<bool> QSortFilterProxyModel::bindableRecursiveFilteringEnabled() +{ + Q_D(QSortFilterProxyModel); + return QBindable<bool>(&d->filter_recursive); } /*! @@ -2851,13 +3041,20 @@ bool QSortFilterProxyModel::autoAcceptChildRows() const void QSortFilterProxyModel::setAutoAcceptChildRows(bool accept) { Q_D(QSortFilterProxyModel); + d->accept_children.removeBindingUnlessInWrapper(); if (d->accept_children == accept) return; d->filter_about_to_be_changed(); - d->accept_children = accept; + d->accept_children.setValueBypassingBindings(accept); d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows); - emit autoAcceptChildRowsChanged(accept); + d->accept_children.notify(); // also emits a signal +} + +QBindable<bool> QSortFilterProxyModel::bindableAutoAcceptChildRows() +{ + Q_D(QSortFilterProxyModel); + return QBindable<bool>(&d->accept_children); } /*! @@ -3000,23 +3197,25 @@ bool QSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex & { Q_D(const QSortFilterProxyModel); - if (d->filter_data.pattern().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.match(key).hasMatch()) + 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.match(key).hasMatch(); + return key.contains(d->filter_regularexpression.value()); } /*! diff --git a/src/corelib/itemmodels/qsortfilterproxymodel.h b/src/corelib/itemmodels/qsortfilterproxymodel.h index d2c5054351..9d5b2fac9f 100644 --- a/src/corelib/itemmodels/qsortfilterproxymodel.h +++ b/src/corelib/itemmodels/qsortfilterproxymodel.h @@ -1,50 +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> -#if QT_CONFIG(regularexpression) -# include <QtCore/qregularexpression.h> -#endif +#include <QtCore/qregularexpression.h> QT_REQUIRE_CONFIG(sortfilterproxymodel); @@ -61,18 +23,29 @@ class Q_CORE_EXPORT QSortFilterProxyModel : public QAbstractProxyModel friend class QSortFilterProxyModelGreaterThan; Q_OBJECT -#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(bool autoAcceptChildRows READ autoAcceptChildRows WRITE setAutoAcceptChildRows NOTIFY autoAcceptChildRowsChanged) + 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); @@ -86,45 +59,51 @@ public: QItemSelection mapSelectionToSource(const QItemSelection &proxySelection) const override; QItemSelection mapSelectionFromSource(const QItemSelection &sourceSelection) const override; -#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: -#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); void invalidate(); @@ -193,28 +172,6 @@ Q_SIGNALS: 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 QList<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 829d4cf3ed..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. @@ -44,6 +8,7 @@ #include "qstringlistmodel.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); @@ -327,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; } @@ -344,11 +309,11 @@ void QStringListModel::sort(int, Qt::SortOrder order) { emit layoutAboutToBeChanged(QList<QPersistentModelIndex>(), VerticalSortHint); - QList<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); @@ -364,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 fb53c19f09..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 diff --git a/src/corelib/itemmodels/qtransposeproxymodel.cpp b/src/corelib/itemmodels/qtransposeproxymodel.cpp index 83a86d1a97..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/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,9 +49,26 @@ 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(); @@ -99,16 +81,6 @@ void QTransposeProxyModelPrivate::onLayoutAboutToBeChanged(const QList<QPersiste 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, @@ -163,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. @@ -199,7 +172,7 @@ 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(); 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 8cee3d2288..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 |