summaryrefslogtreecommitdiffstats
path: root/src/corelib/itemmodels/qabstractproxymodel.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/itemmodels/qabstractproxymodel.cpp')
-rw-r--r--src/corelib/itemmodels/qabstractproxymodel.cpp208
1 files changed, 155 insertions, 53 deletions
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