Warning, file /frameworks/kitemmodels/src/core/kconcatenaterowsproxymodel.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
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