File indexing completed on 2024-05-05 05:29:12
0001 /* 0002 * SPDX-FileCopyrightText: 2014 Aleix Pol Gonzalez <aleixpol@blue-systems.com> 0003 * 0004 * SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "PaginateModel.h" 0008 #include "discover_debug.h" 0009 #include <QtMath> 0010 0011 class PaginateModel::PaginateModelPrivate 0012 { 0013 public: 0014 int m_firstItem = 0; 0015 int m_pageSize = 0; 0016 QAbstractItemModel *m_sourceModel = nullptr; 0017 bool m_hasStaticRowCount = false; 0018 bool m_modelResetInProgress = false; 0019 }; 0020 0021 PaginateModel::PaginateModel(QObject *object) 0022 : QAbstractListModel(object) 0023 , d(new PaginateModelPrivate) 0024 { 0025 } 0026 0027 PaginateModel::~PaginateModel() = default; 0028 0029 int PaginateModel::firstItem() const 0030 { 0031 return d->m_firstItem; 0032 } 0033 0034 void PaginateModel::setFirstItem(int row) 0035 { 0036 Q_ASSERT(row >= 0 && row < d->m_sourceModel->rowCount()); 0037 if (row != d->m_firstItem) { 0038 d->m_modelResetInProgress = true; 0039 beginResetModel(); 0040 d->m_firstItem = row; 0041 endResetModel(); 0042 d->m_modelResetInProgress = false; 0043 Q_EMIT firstItemChanged(); 0044 } 0045 } 0046 0047 int PaginateModel::pageSize() const 0048 { 0049 return d->m_pageSize; 0050 } 0051 0052 void PaginateModel::setPageSize(int count) 0053 { 0054 if (count != d->m_pageSize) { 0055 const int oldSize = rowsByPageSize(d->m_pageSize); 0056 const int newSize = rowsByPageSize(count); 0057 const int difference = newSize - oldSize; 0058 if (difference == 0) { 0059 d->m_pageSize = count; 0060 } else if (difference > 0) { 0061 beginInsertRows(QModelIndex(), d->m_pageSize, d->m_pageSize + difference - 1); 0062 d->m_pageSize = count; 0063 endInsertRows(); 0064 } else { 0065 beginRemoveRows(QModelIndex(), d->m_pageSize + difference, d->m_pageSize - 1); 0066 d->m_pageSize = count; 0067 endRemoveRows(); 0068 } 0069 Q_EMIT pageSizeChanged(); 0070 } 0071 } 0072 0073 QAbstractItemModel *PaginateModel::sourceModel() const 0074 { 0075 return d->m_sourceModel; 0076 } 0077 0078 void PaginateModel::setSourceModel(QAbstractItemModel *model) 0079 { 0080 if (d->m_sourceModel) { 0081 disconnect(d->m_sourceModel, nullptr, this, nullptr); 0082 } 0083 0084 if (model != d->m_sourceModel) { 0085 d->m_modelResetInProgress = true; 0086 beginResetModel(); 0087 d->m_sourceModel = model; 0088 if (model) { 0089 connect(d->m_sourceModel, &QAbstractItemModel::rowsAboutToBeInserted, this, &PaginateModel::_k_sourceRowsAboutToBeInserted); 0090 connect(d->m_sourceModel, &QAbstractItemModel::rowsInserted, this, &PaginateModel::_k_sourceRowsInserted); 0091 connect(d->m_sourceModel, &QAbstractItemModel::rowsAboutToBeRemoved, this, &PaginateModel::_k_sourceRowsAboutToBeRemoved); 0092 connect(d->m_sourceModel, &QAbstractItemModel::rowsRemoved, this, &PaginateModel::_k_sourceRowsRemoved); 0093 connect(d->m_sourceModel, &QAbstractItemModel::rowsAboutToBeMoved, this, &PaginateModel::_k_sourceRowsAboutToBeMoved); 0094 connect(d->m_sourceModel, &QAbstractItemModel::rowsMoved, this, &PaginateModel::_k_sourceRowsMoved); 0095 0096 connect(d->m_sourceModel, &QAbstractItemModel::columnsAboutToBeInserted, this, &PaginateModel::_k_sourceColumnsAboutToBeInserted); 0097 connect(d->m_sourceModel, &QAbstractItemModel::columnsInserted, this, &PaginateModel::_k_sourceColumnsInserted); 0098 connect(d->m_sourceModel, &QAbstractItemModel::columnsAboutToBeRemoved, this, &PaginateModel::_k_sourceColumnsAboutToBeRemoved); 0099 connect(d->m_sourceModel, &QAbstractItemModel::columnsRemoved, this, &PaginateModel::_k_sourceColumnsRemoved); 0100 connect(d->m_sourceModel, &QAbstractItemModel::columnsAboutToBeMoved, this, &PaginateModel::_k_sourceColumnsAboutToBeMoved); 0101 connect(d->m_sourceModel, &QAbstractItemModel::columnsMoved, this, &PaginateModel::_k_sourceColumnsMoved); 0102 0103 connect(d->m_sourceModel, &QAbstractItemModel::dataChanged, this, &PaginateModel::_k_sourceDataChanged); 0104 connect(d->m_sourceModel, &QAbstractItemModel::headerDataChanged, this, &PaginateModel::_k_sourceHeaderDataChanged); 0105 0106 connect(d->m_sourceModel, &QAbstractItemModel::modelAboutToBeReset, this, &PaginateModel::_k_sourceModelAboutToBeReset); 0107 connect(d->m_sourceModel, &QAbstractItemModel::modelReset, this, &PaginateModel::_k_sourceModelReset); 0108 0109 connect(d->m_sourceModel, &QAbstractItemModel::rowsInserted, this, &PaginateModel::pageCountChanged); 0110 connect(d->m_sourceModel, &QAbstractItemModel::rowsRemoved, this, &PaginateModel::pageCountChanged); 0111 connect(d->m_sourceModel, &QAbstractItemModel::modelReset, this, &PaginateModel::pageCountChanged); 0112 } 0113 endResetModel(); 0114 d->m_modelResetInProgress = false; 0115 Q_EMIT sourceModelChanged(); 0116 } 0117 } 0118 0119 QHash<int, QByteArray> PaginateModel::roleNames() const 0120 { 0121 return d->m_sourceModel ? d->m_sourceModel->roleNames() : QAbstractItemModel::roleNames(); 0122 } 0123 0124 int PaginateModel::rowsByPageSize(int size) const 0125 { 0126 return d->m_hasStaticRowCount ? size : !d->m_sourceModel ? 0 : qMin(d->m_sourceModel->rowCount() - d->m_firstItem, size); 0127 } 0128 0129 int PaginateModel::rowCount(const QModelIndex &parent) const 0130 { 0131 return parent.isValid() ? 0 : rowsByPageSize(d->m_pageSize); 0132 } 0133 0134 QModelIndex PaginateModel::mapToSource(const QModelIndex &idx) const 0135 { 0136 if (!d->m_sourceModel) 0137 return QModelIndex(); 0138 return d->m_sourceModel->index(idx.row() + d->m_firstItem, idx.column()); 0139 } 0140 0141 QModelIndex PaginateModel::mapFromSource(const QModelIndex &idx) const 0142 { 0143 Q_ASSERT(idx.model() == d->m_sourceModel); 0144 if (!d->m_sourceModel) 0145 return QModelIndex(); 0146 return index(idx.row() - d->m_firstItem, idx.column()); 0147 } 0148 0149 QVariant PaginateModel::data(const QModelIndex &index, int role) const 0150 { 0151 if (!d->m_sourceModel) 0152 return QVariant(); 0153 QModelIndex idx = mapToSource(index); 0154 return idx.data(role); 0155 } 0156 0157 void PaginateModel::firstPage() 0158 { 0159 setFirstItem(0); 0160 } 0161 0162 void PaginateModel::lastPage() 0163 { 0164 setFirstItem((pageCount() - 1) * d->m_pageSize); 0165 } 0166 0167 void PaginateModel::nextPage() 0168 { 0169 setFirstItem(d->m_firstItem + d->m_pageSize); 0170 } 0171 0172 void PaginateModel::previousPage() 0173 { 0174 setFirstItem(d->m_firstItem - d->m_pageSize); 0175 } 0176 0177 int PaginateModel::currentPage() const 0178 { 0179 if (d->m_pageSize == 0) 0180 return 0; 0181 0182 return d->m_firstItem / d->m_pageSize; 0183 } 0184 0185 int PaginateModel::pageCount() const 0186 { 0187 if (!d->m_sourceModel || d->m_pageSize == 0) 0188 return 0; 0189 const int rc = d->m_sourceModel->rowCount(); 0190 const int r = (rc % d->m_pageSize == 0) ? 1 : 0; 0191 return qMax(qCeil(float(rc) / d->m_pageSize) - r, 1); 0192 } 0193 0194 bool PaginateModel::hasStaticRowCount() const 0195 { 0196 return d->m_hasStaticRowCount; 0197 } 0198 0199 void PaginateModel::setStaticRowCount(bool src) 0200 { 0201 if (src == d->m_hasStaticRowCount) { 0202 return; 0203 } 0204 0205 d->m_modelResetInProgress = true; 0206 beginResetModel(); 0207 d->m_hasStaticRowCount = src; 0208 endResetModel(); 0209 d->m_modelResetInProgress = false; 0210 0211 Q_EMIT staticRowCountChanged(); 0212 } 0213 0214 ////////////////////////////// 0215 0216 void PaginateModel::_k_sourceColumnsAboutToBeInserted(const QModelIndex &parent, int start, int end) 0217 { 0218 Q_UNUSED(end) 0219 if (parent.isValid() || start != 0) { 0220 return; 0221 } 0222 d->m_modelResetInProgress = true; 0223 beginResetModel(); 0224 } 0225 0226 void PaginateModel::_k_sourceColumnsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest) 0227 { 0228 Q_UNUSED(sourceParent) 0229 Q_UNUSED(sourceStart) 0230 Q_UNUSED(sourceEnd) 0231 Q_UNUSED(destParent) 0232 Q_UNUSED(dest) 0233 d->m_modelResetInProgress = true; 0234 beginResetModel(); 0235 } 0236 0237 void PaginateModel::_k_sourceColumnsAboutToBeRemoved(const QModelIndex &parent, int start, int end) 0238 { 0239 Q_UNUSED(end) 0240 if (parent.isValid() || start != 0) { 0241 return; 0242 } 0243 d->m_modelResetInProgress = true; 0244 beginResetModel(); 0245 } 0246 0247 void PaginateModel::_k_sourceColumnsInserted(const QModelIndex &parent, int start, int end) 0248 { 0249 Q_UNUSED(end) 0250 if (parent.isValid() || start != 0) { 0251 return; 0252 } 0253 endResetModel(); 0254 d->m_modelResetInProgress = false; 0255 } 0256 0257 void PaginateModel::_k_sourceColumnsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest) 0258 { 0259 Q_UNUSED(sourceParent) 0260 Q_UNUSED(sourceStart) 0261 Q_UNUSED(sourceEnd) 0262 Q_UNUSED(destParent) 0263 Q_UNUSED(dest) 0264 endResetModel(); 0265 d->m_modelResetInProgress = false; 0266 } 0267 0268 void PaginateModel::_k_sourceColumnsRemoved(const QModelIndex &parent, int start, int end) 0269 { 0270 Q_UNUSED(end) 0271 if (parent.isValid() || start != 0) { 0272 return; 0273 } 0274 endResetModel(); 0275 d->m_modelResetInProgress = false; 0276 } 0277 0278 void PaginateModel::_k_sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles) 0279 { 0280 if (topLeft.parent().isValid() || bottomRight.row() < d->m_firstItem || topLeft.row() > lastItem()) { 0281 return; 0282 } 0283 0284 QModelIndex idxTop = mapFromSource(topLeft); 0285 QModelIndex idxBottom = mapFromSource(bottomRight); 0286 if (!idxTop.isValid()) 0287 idxTop = index(0); 0288 if (!idxBottom.isValid()) 0289 idxBottom = index(rowCount() - 1); 0290 0291 Q_EMIT dataChanged(idxTop, idxBottom, roles); 0292 } 0293 0294 void PaginateModel::_k_sourceHeaderDataChanged(Qt::Orientation orientation, int first, int last) 0295 { 0296 Q_UNUSED(last) 0297 if (first == 0) 0298 Q_EMIT headerDataChanged(orientation, 0, 0); 0299 } 0300 0301 void PaginateModel::_k_sourceModelAboutToBeReset() 0302 { 0303 d->m_modelResetInProgress = true; 0304 beginResetModel(); 0305 } 0306 0307 void PaginateModel::_k_sourceModelReset() 0308 { 0309 endResetModel(); 0310 d->m_modelResetInProgress = false; 0311 } 0312 0313 bool PaginateModel::isIntervalValid(const QModelIndex &parent, int start, int /*end*/) const 0314 { 0315 return !parent.isValid() && start <= lastItem(); 0316 } 0317 0318 bool PaginateModel::canSizeChange() const 0319 { 0320 return !d->m_hasStaticRowCount && currentPage() == pageCount() - 1; 0321 } 0322 0323 void PaginateModel::_k_sourceRowsAboutToBeInserted(const QModelIndex &parent, int start, int end) 0324 { 0325 if (!isIntervalValid(parent, start, end)) { 0326 return; 0327 } 0328 0329 if (canSizeChange()) { 0330 const int newStart = qMax(start - d->m_firstItem, 0); 0331 const int insertedCount = qMin(end - start, pageSize() - newStart - 1); 0332 beginInsertRows(QModelIndex(), newStart, newStart + insertedCount); 0333 } else { 0334 d->m_modelResetInProgress = true; 0335 beginResetModel(); 0336 } 0337 } 0338 0339 void PaginateModel::_k_sourceRowsInserted(const QModelIndex &parent, int start, int end) 0340 { 0341 if (!isIntervalValid(parent, start, end)) { 0342 return; 0343 } 0344 0345 if (d->m_modelResetInProgress) { 0346 endResetModel(); 0347 d->m_modelResetInProgress = false; 0348 } else { 0349 endInsertRows(); 0350 } 0351 } 0352 0353 void PaginateModel::_k_sourceRowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest) 0354 { 0355 Q_UNUSED(sourceParent) 0356 Q_UNUSED(sourceStart) 0357 Q_UNUSED(sourceEnd) 0358 Q_UNUSED(destParent) 0359 Q_UNUSED(dest) 0360 // NOTE could optimize, unsure if it makes sense 0361 d->m_modelResetInProgress = true; 0362 beginResetModel(); 0363 } 0364 0365 void PaginateModel::_k_sourceRowsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest) 0366 { 0367 Q_UNUSED(sourceParent) 0368 Q_UNUSED(sourceStart) 0369 Q_UNUSED(sourceEnd) 0370 Q_UNUSED(destParent) 0371 Q_UNUSED(dest) 0372 endResetModel(); 0373 d->m_modelResetInProgress = false; 0374 } 0375 0376 void PaginateModel::_k_sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) 0377 { 0378 if (!isIntervalValid(parent, start, end)) { 0379 return; 0380 } 0381 0382 if (canSizeChange()) { 0383 const int removedCount = end - start; 0384 const int newStart = qMax(start - d->m_firstItem, 0); 0385 beginRemoveRows(QModelIndex(), newStart, newStart + removedCount); 0386 } else { 0387 d->m_modelResetInProgress = true; 0388 beginResetModel(); 0389 } 0390 } 0391 0392 void PaginateModel::_k_sourceRowsRemoved(const QModelIndex &parent, int start, int end) 0393 { 0394 if (!isIntervalValid(parent, start, end)) { 0395 return; 0396 } 0397 0398 if (canSizeChange()) { 0399 endRemoveRows(); 0400 } else { 0401 d->m_modelResetInProgress = true; 0402 beginResetModel(); 0403 } 0404 } 0405 0406 int PaginateModel::lastItem() const 0407 { 0408 return d->m_firstItem + rowCount(); 0409 }