diff options
Diffstat (limited to 'src/corelib/itemmodels/qtransposeproxymodel.cpp')
-rw-r--r-- | src/corelib/itemmodels/qtransposeproxymodel.cpp | 447 |
1 files changed, 447 insertions, 0 deletions
diff --git a/src/corelib/itemmodels/qtransposeproxymodel.cpp b/src/corelib/itemmodels/qtransposeproxymodel.cpp new file mode 100644 index 0000000000..d4f379bc64 --- /dev/null +++ b/src/corelib/itemmodels/qtransposeproxymodel.cpp @@ -0,0 +1,447 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#include "qtransposeproxymodel.h" +#include <private/qtransposeproxymodel_p.h> +#include <QtCore/qvector.h> +#include <QtCore/qmetaobject.h> +#include <QtCore/qsize.h> + +QT_BEGIN_NAMESPACE + +QModelIndex QTransposeProxyModelPrivate::uncheckedMapToSource(const QModelIndex &proxyIndex) const +{ + 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()); +} + +QModelIndex QTransposeProxyModelPrivate::uncheckedMapFromSource(const QModelIndex &sourceIndex) const +{ + if (!model || !sourceIndex.isValid()) + return QModelIndex(); + Q_Q(const QTransposeProxyModel); + return q->createIndex(sourceIndex.column(), sourceIndex.row(), sourceIndex.internalPointer()); +} + +void QTransposeProxyModelPrivate::onLayoutChanged(const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint) +{ + Q_Q(QTransposeProxyModel); + QModelIndexList toList; + toList.reserve(layoutChangePersistentIndexes.size()); + for (const QPersistentModelIndex &persistIdx : qAsConst(layoutChangePersistentIndexes)) + toList << q->mapFromSource(persistIdx); + q->changePersistentIndexList(layoutChangeProxyIndexes, toList); + layoutChangeProxyIndexes.clear(); + layoutChangePersistentIndexes.clear(); + QList<QPersistentModelIndex> proxyParents; + proxyParents.reserve(parents.size()); + for (const QPersistentModelIndex &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->layoutChanged(proxyParents, proxyHint); +} + +void QTransposeProxyModelPrivate::onLayoutAboutToBeChanged(const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint) +{ + Q_Q(QTransposeProxyModel); + const QModelIndexList proxyPersistentIndexes = q->persistentIndexList(); + layoutChangeProxyIndexes.clear(); + layoutChangePersistentIndexes.clear(); + layoutChangeProxyIndexes.reserve(proxyPersistentIndexes.size()); + layoutChangePersistentIndexes.reserve(proxyPersistentIndexes.size()); + for (const QPersistentModelIndex &proxyPersistentIndex : proxyPersistentIndexes) { + layoutChangeProxyIndexes << proxyPersistentIndex; + Q_ASSERT(proxyPersistentIndex.isValid()); + const QPersistentModelIndex srcPersistentIndex = q->mapToSource(proxyPersistentIndex); + Q_ASSERT(srcPersistentIndex.isValid()); + layoutChangePersistentIndexes << srcPersistentIndex; + } + QList<QPersistentModelIndex> proxyParents; + proxyParents.reserve(parents.size()); + for (auto& srcParent : parents) + proxyParents << q->mapFromSource(srcParent); + QAbstractItemModel::LayoutChangeHint proxyHint = QAbstractItemModel::NoLayoutChangeHint; + if (hint == QAbstractItemModel::VerticalSortHint) + proxyHint = QAbstractItemModel::HorizontalSortHint; + else if (hint == QAbstractItemModel::HorizontalSortHint) + proxyHint = QAbstractItemModel::VerticalSortHint; + emit q->layoutAboutToBeChanged(proxyParents, proxyHint); +} + +void QTransposeProxyModelPrivate::onDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>& roles) +{ + Q_Q(QTransposeProxyModel); + emit q->dataChanged(q->mapFromSource(topLeft), q->mapFromSource(bottomRight), roles); +} + +void QTransposeProxyModelPrivate::onHeaderDataChanged(Qt::Orientation orientation, int first, int last) +{ + Q_Q(QTransposeProxyModel); + emit q->headerDataChanged(orientation == Qt::Horizontal ? Qt::Vertical : Qt::Horizontal, first, last); +} + +void QTransposeProxyModelPrivate::onColumnsAboutToBeInserted(const QModelIndex &parent, int first, int last) +{ + Q_Q(QTransposeProxyModel); + q->beginInsertRows(q->mapFromSource(parent), first, last); +} + +void QTransposeProxyModelPrivate::onColumnsAboutToBeRemoved(const QModelIndex &parent, int first, int last) +{ + Q_Q(QTransposeProxyModel); + q->beginRemoveRows(q->mapFromSource(parent), first, last); +} + +void QTransposeProxyModelPrivate::onColumnsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationColumn) +{ + Q_Q(QTransposeProxyModel); + q->beginMoveRows(q->mapFromSource(sourceParent), sourceStart, sourceEnd, q->mapFromSource(destinationParent), destinationColumn); +} + +void QTransposeProxyModelPrivate::onRowsAboutToBeInserted(const QModelIndex &parent, int first, int last) +{ + Q_Q(QTransposeProxyModel); + q->beginInsertColumns(q->mapFromSource(parent), first, last); +} + +void QTransposeProxyModelPrivate::onRowsAboutToBeRemoved(const QModelIndex &parent, int first, int last) +{ + Q_Q(QTransposeProxyModel); + q->beginRemoveColumns(q->mapFromSource(parent), first, last); +} + +void QTransposeProxyModelPrivate::onRowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow) +{ + Q_Q(QTransposeProxyModel); + q->beginMoveColumns(q->mapFromSource(sourceParent), sourceStart, sourceEnd, q->mapFromSource(destinationParent), destinationRow); +} + +/*! + \since 5.13 + \class QTransposeProxyModel + \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. + + If the model is a tree, the parents will be transposed as well. For example, if an index in the source model had parent `index(2,0)`, it will have parent `index(0,2)` in the proxy. +*/ + +/*! + Constructs a new proxy model with the given \a parent. +*/ +QTransposeProxyModel::QTransposeProxyModel(QObject* parent) + : QAbstractProxyModel(*new QTransposeProxyModelPrivate, parent) +{} + +/*! + Destructs the proxy model. +*/ +QTransposeProxyModel::~QTransposeProxyModel() = default; + +/*! + \internal +*/ +QTransposeProxyModel::QTransposeProxyModel(QTransposeProxyModelPrivate &dd, QObject *parent) + : QAbstractProxyModel(dd, parent) +{} + +/*! + \reimp +*/ +void QTransposeProxyModel::setSourceModel(QAbstractItemModel* newSourceModel) +{ + Q_D(QTransposeProxyModel); + if (newSourceModel == d->model) + return; + beginResetModel(); + if (d->model) { + for (const QMetaObject::Connection& discIter : qAsConst(d->sourceConnections)) + disconnect(discIter); + } + d->sourceConnections.clear(); + QAbstractProxyModel::setSourceModel(newSourceModel); + if (d->model) { + using namespace std::placeholders; + d->sourceConnections = QVector<QMetaObject::Connection>{ + connect(d->model, &QAbstractItemModel::modelAboutToBeReset, this, &QTransposeProxyModel::beginResetModel), + connect(d->model, &QAbstractItemModel::modelReset, this, &QTransposeProxyModel::endResetModel), + connect(d->model, &QAbstractItemModel::dataChanged, this, std::bind(&QTransposeProxyModelPrivate::onDataChanged, d, _1, _2, _3)), + connect(d->model, &QAbstractItemModel::headerDataChanged, this, std::bind(&QTransposeProxyModelPrivate::onHeaderDataChanged, d, _1, _2, _3)), + connect(d->model, &QAbstractItemModel::columnsAboutToBeInserted, this, std::bind(&QTransposeProxyModelPrivate::onColumnsAboutToBeInserted, d, _1, _2, _3)), + connect(d->model, &QAbstractItemModel::columnsAboutToBeMoved, this, std::bind(&QTransposeProxyModelPrivate::onColumnsAboutToBeMoved, d, _1, _2, _3, _4, _5)), + connect(d->model, &QAbstractItemModel::columnsAboutToBeRemoved, this, std::bind(&QTransposeProxyModelPrivate::onColumnsAboutToBeRemoved, d, _1, _2, _3)), + connect(d->model, &QAbstractItemModel::columnsInserted, this, &QTransposeProxyModel::endInsertRows), + connect(d->model, &QAbstractItemModel::columnsRemoved, this, &QTransposeProxyModel::endRemoveRows), + connect(d->model, &QAbstractItemModel::columnsMoved, this, &QTransposeProxyModel::endMoveRows), + connect(d->model, &QAbstractItemModel::rowsAboutToBeInserted, this, std::bind(&QTransposeProxyModelPrivate::onRowsAboutToBeInserted, d, _1, _2, _3)), + connect(d->model, &QAbstractItemModel::rowsAboutToBeMoved, this, std::bind(&QTransposeProxyModelPrivate::onRowsAboutToBeMoved, d, _1, _2, _3, _4, _5)), + connect(d->model, &QAbstractItemModel::rowsAboutToBeRemoved, this, std::bind(&QTransposeProxyModelPrivate::onRowsAboutToBeRemoved, d, _1, _2, _3)), + connect(d->model, &QAbstractItemModel::rowsInserted, this, &QTransposeProxyModel::endInsertColumns), + connect(d->model, &QAbstractItemModel::rowsRemoved, this, &QTransposeProxyModel::endRemoveColumns), + connect(d->model, &QAbstractItemModel::rowsMoved, this, &QTransposeProxyModel::endMoveColumns), + connect(d->model, &QAbstractItemModel::layoutAboutToBeChanged, this, std::bind(&QTransposeProxyModelPrivate::onLayoutAboutToBeChanged, d, _1, _2)), + connect(d->model, &QAbstractItemModel::layoutChanged, this, std::bind(&QTransposeProxyModelPrivate::onLayoutChanged, d, _1, _2)) + }; + } + endResetModel(); +} + +/*! + \reimp +*/ +int QTransposeProxyModel::rowCount(const QModelIndex &parent) const +{ + Q_D(const QTransposeProxyModel); + if (!d->model) + return 0; + Q_ASSERT(checkIndex(parent)); + return d->model->columnCount(mapToSource(parent)); +} + +/*! + \reimp +*/ +int QTransposeProxyModel::columnCount(const QModelIndex &parent) const +{ + Q_D(const QTransposeProxyModel); + if (!d->model) + return 0; + Q_ASSERT(checkIndex(parent)); + return d->model->rowCount(mapToSource(parent)); +} + +/*! + \reimp +*/ +QVariant QTransposeProxyModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + Q_D(const QTransposeProxyModel); + if (!d->model) + return QVariant(); + return d->model->headerData(section, orientation == Qt::Horizontal ? Qt::Vertical : Qt::Horizontal, role); +} + +/*! + \reimp +*/ +bool QTransposeProxyModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role) +{ + Q_D(QTransposeProxyModel); + if (!d->model) + return false; + return d->model->setHeaderData(section, orientation == Qt::Horizontal ? Qt::Vertical : Qt::Horizontal, value, role); +} + +/*! + \reimp +*/ +bool QTransposeProxyModel::setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles) +{ + Q_D(QTransposeProxyModel); + Q_ASSERT(checkIndex(index)); + if (!d->model || !index.isValid()) + return false; + return d->model->setItemData(mapToSource(index), roles); +} + +/*! + \reimp +*/ +QSize QTransposeProxyModel::span(const QModelIndex &index) const +{ + Q_D(const QTransposeProxyModel); + Q_ASSERT(checkIndex(index)); + if (!d->model || !index.isValid()) + return QSize(); + return d->model->span(mapToSource(index)).transposed(); +} + +/*! + \reimp +*/ +QMap<int, QVariant> QTransposeProxyModel::itemData(const QModelIndex &index) const +{ + Q_D(const QTransposeProxyModel); + if (!d->model) + return QMap<int, QVariant>(); + Q_ASSERT(checkIndex(index)); + return d->model->itemData(mapToSource(index)); +} + +/*! + \reimp +*/ +QModelIndex QTransposeProxyModel::mapFromSource(const QModelIndex &sourceIndex) const +{ + Q_D(const QTransposeProxyModel); + if (!d->model || !sourceIndex.isValid()) + return QModelIndex(); + Q_ASSERT(d->model->checkIndex(sourceIndex)); + return d->uncheckedMapFromSource(sourceIndex); +} + +/*! + \reimp +*/ +QModelIndex QTransposeProxyModel::mapToSource(const QModelIndex &proxyIndex) const +{ + Q_D(const QTransposeProxyModel); + Q_ASSERT(checkIndex(proxyIndex)); + if (!d->model || !proxyIndex.isValid()) + return QModelIndex(); + return d->uncheckedMapToSource(proxyIndex); +} + +/*! + \reimp +*/ +QModelIndex QTransposeProxyModel::parent(const QModelIndex &index) const +{ + Q_D(const QTransposeProxyModel); + Q_ASSERT(checkIndex(index, CheckIndexOption::DoNotUseParent)); + if (!d->model || !index.isValid()) + return QModelIndex(); + return d->uncheckedMapFromSource(d->uncheckedMapToSource(index).parent()); +} + +/*! + \reimp +*/ +QModelIndex QTransposeProxyModel::index(int row, int column, const QModelIndex &parent) const +{ + Q_D(const QTransposeProxyModel); + Q_ASSERT(checkIndex(parent)); + if (!d->model) + return QModelIndex(); + return mapFromSource(d->model->index(column, row, mapToSource(parent))); +} + +/*! + \reimp +*/ +bool QTransposeProxyModel::insertRows(int row, int count, const QModelIndex &parent) +{ + Q_D(QTransposeProxyModel); + Q_ASSERT(checkIndex(parent)); + if (!d->model) + return false; + return d->model->insertColumns(row, count, mapToSource(parent)); +} + +/*! + \reimp +*/ +bool QTransposeProxyModel::removeRows(int row, int count, const QModelIndex &parent) +{ + Q_D(QTransposeProxyModel); + Q_ASSERT(checkIndex(parent)); + if (!d->model) + return false; + return d->model->removeColumns(row, count, mapToSource(parent)); +} + +/*! + \reimp +*/ +bool QTransposeProxyModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) +{ + Q_D(QTransposeProxyModel); + Q_ASSERT(checkIndex(sourceParent)); + Q_ASSERT(checkIndex(destinationParent)); + if (!d->model) + return false; + return d->model->moveColumns(mapToSource(sourceParent), sourceRow, count, mapToSource(destinationParent), destinationChild); +} + +/*! + \reimp +*/ +bool QTransposeProxyModel::insertColumns(int column, int count, const QModelIndex &parent) +{ + Q_D(QTransposeProxyModel); + Q_ASSERT(checkIndex(parent)); + if (!d->model) + return false; + return d->model->insertRows(column, count, mapToSource(parent)); +} + +/*! + \reimp +*/ +bool QTransposeProxyModel::removeColumns(int column, int count, const QModelIndex &parent) +{ + Q_D(QTransposeProxyModel); + Q_ASSERT(checkIndex(parent)); + if (!d->model) + return false; + return d->model->removeRows(column, count, mapToSource(parent)); +} + +/*! + \reimp +*/ +bool QTransposeProxyModel::moveColumns(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) +{ + Q_D(QTransposeProxyModel); + Q_ASSERT(checkIndex(sourceParent)); + Q_ASSERT(checkIndex(destinationParent)); + if (!d->model) + return false; + return d->model->moveRows(mapToSource(sourceParent), sourceRow, count, mapToSource(destinationParent), destinationChild); +} + +/*! + \reimp + This method will perform no action. Use a QSortFilterProxyModel on top of this one if you require sorting. +*/ +void QTransposeProxyModel::sort(int column, Qt::SortOrder order) +{ + Q_UNUSED(column) + Q_UNUSED(order) + return; +} + +QT_END_NAMESPACE |