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