File indexing completed on 2024-05-12 15:42:57

0001 /*
0002     SPDX-FileCopyrightText: 2015 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
0003     SPDX-FileContributor: David Faure <david.faure@kdab.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "kconcatenaterowsproxymodel.h"
0009 
0010 #if KITEMMODELS_BUILD_DEPRECATED_SINCE(5, 80)
0011 
0012 class KConcatenateRowsProxyModelPrivate
0013 {
0014 public:
0015     KConcatenateRowsProxyModelPrivate(KConcatenateRowsProxyModel *model)
0016         : q(model)
0017     {
0018     }
0019 
0020     int computeRowsPrior(const QAbstractItemModel *sourceModel) const;
0021     QAbstractItemModel *sourceModelForRow(int row, int *sourceRow) const;
0022 
0023     void slotRowsAboutToBeInserted(const QModelIndex &, int start, int end);
0024     void slotRowsInserted(const QModelIndex &, int start, int end);
0025     void slotRowsAboutToBeRemoved(const QModelIndex &, int start, int end);
0026     void slotRowsRemoved(const QModelIndex &, int start, int end);
0027     void slotColumnsAboutToBeInserted(const QModelIndex &parent, int start, int end);
0028     void slotColumnsInserted(const QModelIndex &parent, int, int);
0029     void slotColumnsAboutToBeRemoved(const QModelIndex &parent, int start, int end);
0030     void slotColumnsRemoved(const QModelIndex &parent, int, int);
0031     void slotDataChanged(const QModelIndex &from, const QModelIndex &to, const QVector<int> &roles);
0032     void slotSourceLayoutAboutToBeChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint);
0033     void slotSourceLayoutChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint);
0034     void slotModelAboutToBeReset();
0035     void slotModelReset();
0036 
0037     KConcatenateRowsProxyModel *q;
0038     QList<QAbstractItemModel *> m_models;
0039     int m_rowCount = 0; // have to maintain it here since we can't compute during model destruction
0040 
0041     // for layoutAboutToBeChanged/layoutChanged
0042     QVector<QPersistentModelIndex> layoutChangePersistentIndexes;
0043     QModelIndexList proxyIndexes;
0044 };
0045 
0046 KConcatenateRowsProxyModel::KConcatenateRowsProxyModel(QObject *parent)
0047     : QAbstractItemModel(parent)
0048     , d(new KConcatenateRowsProxyModelPrivate(this))
0049 {
0050 }
0051 
0052 KConcatenateRowsProxyModel::~KConcatenateRowsProxyModel()
0053 {
0054 }
0055 
0056 QModelIndex KConcatenateRowsProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
0057 {
0058     const QAbstractItemModel *sourceModel = sourceIndex.model();
0059     if (!sourceModel) {
0060         return {};
0061     }
0062     int rowsPrior = d->computeRowsPrior(sourceModel);
0063     return createIndex(rowsPrior + sourceIndex.row(), sourceIndex.column());
0064 }
0065 
0066 QModelIndex KConcatenateRowsProxyModel::mapToSource(const QModelIndex &proxyIndex) const
0067 {
0068     if (!proxyIndex.isValid()) {
0069         return QModelIndex();
0070     }
0071     const int row = proxyIndex.row();
0072     int sourceRow;
0073     QAbstractItemModel *sourceModel = d->sourceModelForRow(row, &sourceRow);
0074     if (!sourceModel) {
0075         return QModelIndex();
0076     }
0077     return sourceModel->index(sourceRow, proxyIndex.column());
0078 }
0079 
0080 QVariant KConcatenateRowsProxyModel::data(const QModelIndex &index, int role) const
0081 {
0082     const QModelIndex sourceIndex = mapToSource(index);
0083     if (!sourceIndex.isValid()) {
0084         return QVariant();
0085     }
0086     return sourceIndex.model()->data(sourceIndex, role);
0087 }
0088 
0089 bool KConcatenateRowsProxyModel::setData(const QModelIndex &index, const QVariant &value, int role)
0090 {
0091     const QModelIndex sourceIndex = mapToSource(index);
0092     if (!sourceIndex.isValid()) {
0093         return false;
0094     }
0095     QAbstractItemModel *sourceModel = const_cast<QAbstractItemModel *>(sourceIndex.model());
0096     return sourceModel->setData(sourceIndex, value, role);
0097 }
0098 
0099 QMap<int, QVariant> KConcatenateRowsProxyModel::itemData(const QModelIndex &proxyIndex) const
0100 {
0101     const QModelIndex sourceIndex = mapToSource(proxyIndex);
0102     if (!sourceIndex.isValid()) {
0103         return {};
0104     }
0105     return sourceIndex.model()->itemData(sourceIndex);
0106 }
0107 
0108 Qt::ItemFlags KConcatenateRowsProxyModel::flags(const QModelIndex &index) const
0109 {
0110     const QModelIndex sourceIndex = mapToSource(index);
0111     return sourceIndex.isValid() ? sourceIndex.model()->flags(sourceIndex) : Qt::ItemFlags();
0112 }
0113 
0114 QVariant KConcatenateRowsProxyModel::headerData(int section, Qt::Orientation orientation, int role) const
0115 {
0116     if (d->m_models.isEmpty()) {
0117         return QVariant();
0118     }
0119     if (orientation == Qt::Horizontal) {
0120         return d->m_models.at(0)->headerData(section, orientation, role);
0121     } else {
0122         int sourceRow;
0123         QAbstractItemModel *sourceModel = d->sourceModelForRow(section, &sourceRow);
0124         if (!sourceModel) {
0125             return QVariant();
0126         }
0127         return sourceModel->headerData(sourceRow, orientation, role);
0128     }
0129 }
0130 
0131 int KConcatenateRowsProxyModel::columnCount(const QModelIndex &parent) const
0132 {
0133     if (d->m_models.isEmpty()) {
0134         return 0;
0135     }
0136     if (parent.isValid()) {
0137         return 0; // flat model;
0138     }
0139     return d->m_models.at(0)->columnCount(QModelIndex());
0140 }
0141 
0142 QHash<int, QByteArray> KConcatenateRowsProxyModel::roleNames() const
0143 {
0144     if (d->m_models.isEmpty()) {
0145         return {};
0146     }
0147     return d->m_models.at(0)->roleNames();
0148 }
0149 
0150 QModelIndex KConcatenateRowsProxyModel::index(int row, int column, const QModelIndex &parent) const
0151 {
0152     if (row < 0) {
0153         return {};
0154     }
0155     if (column < 0) {
0156         return {};
0157     }
0158     int sourceRow;
0159     QAbstractItemModel *sourceModel = d->sourceModelForRow(row, &sourceRow);
0160     if (!sourceModel) {
0161         return QModelIndex();
0162     }
0163     return mapFromSource(sourceModel->index(sourceRow, column, parent));
0164 }
0165 
0166 QModelIndex KConcatenateRowsProxyModel::parent(const QModelIndex &) const
0167 {
0168     return QModelIndex(); // we are flat, no hierarchy
0169 }
0170 
0171 int KConcatenateRowsProxyModel::rowCount(const QModelIndex &parent) const
0172 {
0173     if (parent.isValid()) {
0174         return 0; // flat model
0175     }
0176 
0177     return d->m_rowCount;
0178 }
0179 
0180 void KConcatenateRowsProxyModel::addSourceModel(QAbstractItemModel *sourceModel)
0181 {
0182     Q_ASSERT(sourceModel);
0183     Q_ASSERT(!d->m_models.contains(sourceModel));
0184     connect(sourceModel, SIGNAL(dataChanged(QModelIndex, QModelIndex, QVector<int>)), this, SLOT(slotDataChanged(QModelIndex, QModelIndex, QVector<int>)));
0185     connect(sourceModel, SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(slotRowsInserted(QModelIndex, int, int)));
0186     connect(sourceModel, SIGNAL(rowsRemoved(QModelIndex, int, int)), this, SLOT(slotRowsRemoved(QModelIndex, int, int)));
0187     connect(sourceModel, SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int)), this, SLOT(slotRowsAboutToBeInserted(QModelIndex, int, int)));
0188     connect(sourceModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)), this, SLOT(slotRowsAboutToBeRemoved(QModelIndex, int, int)));
0189 
0190     connect(sourceModel, SIGNAL(columnsInserted(QModelIndex, int, int)), this, SLOT(slotColumnsInserted(QModelIndex, int, int)));
0191     connect(sourceModel, SIGNAL(columnsRemoved(QModelIndex, int, int)), this, SLOT(slotColumnsRemoved(QModelIndex, int, int)));
0192     connect(sourceModel, SIGNAL(columnsAboutToBeInserted(QModelIndex, int, int)), this, SLOT(slotColumnsAboutToBeInserted(QModelIndex, int, int)));
0193     connect(sourceModel, SIGNAL(columnsAboutToBeRemoved(QModelIndex, int, int)), this, SLOT(slotColumnsAboutToBeRemoved(QModelIndex, int, int)));
0194 
0195     connect(sourceModel,
0196             SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>, QAbstractItemModel::LayoutChangeHint)),
0197             this,
0198             SLOT(slotSourceLayoutAboutToBeChanged(QList<QPersistentModelIndex>, QAbstractItemModel::LayoutChangeHint)));
0199     connect(sourceModel,
0200             SIGNAL(layoutChanged(QList<QPersistentModelIndex>, QAbstractItemModel::LayoutChangeHint)),
0201             this,
0202             SLOT(slotSourceLayoutChanged(QList<QPersistentModelIndex>, QAbstractItemModel::LayoutChangeHint)));
0203     connect(sourceModel, SIGNAL(modelAboutToBeReset()), this, SLOT(slotModelAboutToBeReset()));
0204     connect(sourceModel, SIGNAL(modelReset()), this, SLOT(slotModelReset()));
0205 
0206     const int newRows = sourceModel->rowCount();
0207     if (newRows > 0) {
0208         beginInsertRows(QModelIndex(), d->m_rowCount, d->m_rowCount + newRows - 1);
0209     }
0210     d->m_rowCount += newRows;
0211     d->m_models.append(sourceModel);
0212     if (newRows > 0) {
0213         endInsertRows();
0214     }
0215 }
0216 
0217 QList<QAbstractItemModel *> KConcatenateRowsProxyModel::sources() const
0218 {
0219     return d->m_models;
0220 }
0221 
0222 void KConcatenateRowsProxyModel::removeSourceModel(QAbstractItemModel *sourceModel)
0223 {
0224     Q_ASSERT(d->m_models.contains(sourceModel));
0225     disconnect(sourceModel, nullptr, this, nullptr);
0226 
0227     const int rowsRemoved = sourceModel->rowCount();
0228     const int rowsPrior = d->computeRowsPrior(sourceModel); // location of removed section
0229 
0230     if (rowsRemoved > 0) {
0231         beginRemoveRows(QModelIndex(), rowsPrior, rowsPrior + rowsRemoved - 1);
0232     }
0233     d->m_models.removeOne(sourceModel);
0234     d->m_rowCount -= rowsRemoved;
0235     if (rowsRemoved > 0) {
0236         endRemoveRows();
0237     }
0238 }
0239 
0240 void KConcatenateRowsProxyModelPrivate::slotRowsAboutToBeInserted(const QModelIndex &, int start, int end)
0241 {
0242     const QAbstractItemModel *model = qobject_cast<QAbstractItemModel *>(q->sender());
0243     const int rowsPrior = computeRowsPrior(model);
0244     q->beginInsertRows(QModelIndex(), rowsPrior + start, rowsPrior + end);
0245 }
0246 
0247 void KConcatenateRowsProxyModelPrivate::slotRowsInserted(const QModelIndex &, int start, int end)
0248 {
0249     m_rowCount += end - start + 1;
0250     q->endInsertRows();
0251 }
0252 
0253 void KConcatenateRowsProxyModelPrivate::slotRowsAboutToBeRemoved(const QModelIndex &, int start, int end)
0254 {
0255     const QAbstractItemModel *model = qobject_cast<QAbstractItemModel *>(q->sender());
0256     const int rowsPrior = computeRowsPrior(model);
0257     q->beginRemoveRows(QModelIndex(), rowsPrior + start, rowsPrior + end);
0258 }
0259 
0260 void KConcatenateRowsProxyModelPrivate::slotRowsRemoved(const QModelIndex &, int start, int end)
0261 {
0262     m_rowCount -= end - start + 1;
0263     q->endRemoveRows();
0264 }
0265 
0266 void KConcatenateRowsProxyModelPrivate::slotColumnsAboutToBeInserted(const QModelIndex &parent, int start, int end)
0267 {
0268     if (parent.isValid()) { // we are flat
0269         return;
0270     }
0271     const QAbstractItemModel *model = qobject_cast<QAbstractItemModel *>(q->sender());
0272     if (m_models.at(0) == model) {
0273         q->beginInsertColumns(QModelIndex(), start, end);
0274     }
0275 }
0276 
0277 void KConcatenateRowsProxyModelPrivate::slotColumnsInserted(const QModelIndex &parent, int, int)
0278 {
0279     if (parent.isValid()) { // we are flat
0280         return;
0281     }
0282     const QAbstractItemModel *model = qobject_cast<QAbstractItemModel *>(q->sender());
0283     if (m_models.at(0) == model) {
0284         q->endInsertColumns();
0285     }
0286 }
0287 
0288 void KConcatenateRowsProxyModelPrivate::slotColumnsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
0289 {
0290     if (parent.isValid()) { // we are flat
0291         return;
0292     }
0293     const QAbstractItemModel *model = qobject_cast<QAbstractItemModel *>(q->sender());
0294     if (m_models.at(0) == model) {
0295         q->beginRemoveColumns(QModelIndex(), start, end);
0296     }
0297 }
0298 
0299 void KConcatenateRowsProxyModelPrivate::slotColumnsRemoved(const QModelIndex &parent, int, int)
0300 {
0301     if (parent.isValid()) { // we are flat
0302         return;
0303     }
0304     const QAbstractItemModel *model = qobject_cast<QAbstractItemModel *>(q->sender());
0305     if (m_models.at(0) == model) {
0306         q->endRemoveColumns();
0307     }
0308 }
0309 
0310 void KConcatenateRowsProxyModelPrivate::slotDataChanged(const QModelIndex &from, const QModelIndex &to, const QVector<int> &roles)
0311 {
0312     if (!from.isValid()) { // QSFPM bug, it emits dataChanged(invalid, invalid) if a cell in a hidden column changes
0313         return;
0314     }
0315     const QModelIndex myFrom = q->mapFromSource(from);
0316     const QModelIndex myTo = q->mapFromSource(to);
0317     Q_EMIT q->dataChanged(myFrom, myTo, roles);
0318 }
0319 
0320 void KConcatenateRowsProxyModelPrivate::slotSourceLayoutAboutToBeChanged(const QList<QPersistentModelIndex> &sourceParents,
0321                                                                          QAbstractItemModel::LayoutChangeHint hint)
0322 {
0323     QList<QPersistentModelIndex> parents;
0324     parents.reserve(sourceParents.size());
0325     for (const QPersistentModelIndex &parent : sourceParents) {
0326         if (!parent.isValid()) {
0327             parents << QPersistentModelIndex();
0328             continue;
0329         }
0330         const QModelIndex mappedParent = q->mapFromSource(parent);
0331         Q_ASSERT(mappedParent.isValid());
0332         parents << mappedParent;
0333     }
0334 
0335     Q_EMIT q->layoutAboutToBeChanged(parents, hint);
0336 
0337     const QModelIndexList persistentIndexList = q->persistentIndexList();
0338     layoutChangePersistentIndexes.reserve(persistentIndexList.size());
0339 
0340     for (const QModelIndex &proxyPersistentIndex : persistentIndexList) {
0341         proxyIndexes << proxyPersistentIndex;
0342         Q_ASSERT(proxyPersistentIndex.isValid());
0343         const QPersistentModelIndex srcPersistentIndex = q->mapToSource(proxyPersistentIndex);
0344         Q_ASSERT(srcPersistentIndex.isValid());
0345         layoutChangePersistentIndexes << srcPersistentIndex;
0346     }
0347 }
0348 
0349 void KConcatenateRowsProxyModelPrivate::slotSourceLayoutChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint)
0350 {
0351     for (int i = 0; i < proxyIndexes.size(); ++i) {
0352         const QModelIndex proxyIdx = proxyIndexes.at(i);
0353         QModelIndex newProxyIdx = q->mapFromSource(layoutChangePersistentIndexes.at(i));
0354         q->changePersistentIndex(proxyIdx, newProxyIdx);
0355     }
0356 
0357     layoutChangePersistentIndexes.clear();
0358     proxyIndexes.clear();
0359 
0360     QList<QPersistentModelIndex> parents;
0361     parents.reserve(sourceParents.size());
0362     for (const QPersistentModelIndex &parent : sourceParents) {
0363         if (!parent.isValid()) {
0364             parents << QPersistentModelIndex();
0365             continue;
0366         }
0367         const QModelIndex mappedParent = q->mapFromSource(parent);
0368         Q_ASSERT(mappedParent.isValid());
0369         parents << mappedParent;
0370     }
0371     Q_EMIT q->layoutChanged(parents, hint);
0372 }
0373 
0374 void KConcatenateRowsProxyModelPrivate::slotModelAboutToBeReset()
0375 {
0376     const QAbstractItemModel *sourceModel = qobject_cast<const QAbstractItemModel *>(q->sender());
0377     Q_ASSERT(m_models.contains(const_cast<QAbstractItemModel *>(sourceModel)));
0378     const int oldRows = sourceModel->rowCount();
0379     if (oldRows > 0) {
0380         slotRowsAboutToBeRemoved(QModelIndex(), 0, oldRows - 1);
0381         slotRowsRemoved(QModelIndex(), 0, oldRows - 1);
0382     }
0383     if (m_models.at(0) == sourceModel) {
0384         q->beginResetModel();
0385     }
0386 }
0387 
0388 void KConcatenateRowsProxyModelPrivate::slotModelReset()
0389 {
0390     const QAbstractItemModel *sourceModel = qobject_cast<const QAbstractItemModel *>(q->sender());
0391     Q_ASSERT(m_models.contains(const_cast<QAbstractItemModel *>(sourceModel)));
0392     if (m_models.at(0) == sourceModel) {
0393         q->endResetModel();
0394     }
0395     const int newRows = sourceModel->rowCount();
0396     if (newRows > 0) {
0397         slotRowsAboutToBeInserted(QModelIndex(), 0, newRows - 1);
0398         slotRowsInserted(QModelIndex(), 0, newRows - 1);
0399     }
0400 }
0401 
0402 int KConcatenateRowsProxyModelPrivate::computeRowsPrior(const QAbstractItemModel *sourceModel) const
0403 {
0404     int rowsPrior = 0;
0405     for (const QAbstractItemModel *model : std::as_const(m_models)) {
0406         if (model == sourceModel) {
0407             break;
0408         }
0409         rowsPrior += model->rowCount();
0410     }
0411     return rowsPrior;
0412 }
0413 
0414 QAbstractItemModel *KConcatenateRowsProxyModelPrivate::sourceModelForRow(int row, int *sourceRow) const
0415 {
0416     int rowCount = 0;
0417     QAbstractItemModel *selection = nullptr;
0418     for (QAbstractItemModel *model : std::as_const(m_models)) {
0419         const int subRowCount = model->rowCount();
0420         if (rowCount + subRowCount > row) {
0421             selection = model;
0422             break;
0423         }
0424         rowCount += subRowCount;
0425     }
0426     *sourceRow = row - rowCount;
0427     return selection;
0428 }
0429 
0430 #include "moc_kconcatenaterowsproxymodel.cpp"
0431 
0432 #endif