File indexing completed on 2024-05-12 17:07:45
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 return d->m_firstItem / d->m_pageSize; 0174 } 0175 0176 int PaginateModel::pageCount() const 0177 { 0178 if (!d->m_sourceModel) 0179 return 0; 0180 const int rc = d->m_sourceModel->rowCount(); 0181 const int r = (rc % d->m_pageSize == 0) ? 1 : 0; 0182 return qMax(qCeil(float(rc) / d->m_pageSize) - r, 1); 0183 } 0184 0185 bool PaginateModel::hasStaticRowCount() const 0186 { 0187 return d->m_hasStaticRowCount; 0188 } 0189 0190 void PaginateModel::setStaticRowCount(bool src) 0191 { 0192 if (src == d->m_hasStaticRowCount) { 0193 return; 0194 } 0195 0196 beginResetModel(); 0197 d->m_hasStaticRowCount = src; 0198 endResetModel(); 0199 0200 Q_EMIT staticRowCountChanged(); 0201 } 0202 0203 ////////////////////////////// 0204 0205 void PaginateModel::_k_sourceColumnsAboutToBeInserted(const QModelIndex &parent, int start, int end) 0206 { 0207 Q_UNUSED(end) 0208 if (parent.isValid() || start != 0) { 0209 return; 0210 } 0211 beginResetModel(); 0212 } 0213 0214 void PaginateModel::_k_sourceColumnsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest) 0215 { 0216 Q_UNUSED(sourceParent) 0217 Q_UNUSED(sourceStart) 0218 Q_UNUSED(sourceEnd) 0219 Q_UNUSED(destParent) 0220 Q_UNUSED(dest) 0221 beginResetModel(); 0222 } 0223 0224 void PaginateModel::_k_sourceColumnsAboutToBeRemoved(const QModelIndex &parent, int start, int end) 0225 { 0226 Q_UNUSED(end) 0227 if (parent.isValid() || start != 0) { 0228 return; 0229 } 0230 beginResetModel(); 0231 } 0232 0233 void PaginateModel::_k_sourceColumnsInserted(const QModelIndex &parent, int start, int end) 0234 { 0235 Q_UNUSED(end) 0236 if (parent.isValid() || start != 0) { 0237 return; 0238 } 0239 endResetModel(); 0240 } 0241 0242 void PaginateModel::_k_sourceColumnsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest) 0243 { 0244 Q_UNUSED(sourceParent) 0245 Q_UNUSED(sourceStart) 0246 Q_UNUSED(sourceEnd) 0247 Q_UNUSED(destParent) 0248 Q_UNUSED(dest) 0249 endResetModel(); 0250 } 0251 0252 void PaginateModel::_k_sourceColumnsRemoved(const QModelIndex &parent, int start, int end) 0253 { 0254 Q_UNUSED(end) 0255 if (parent.isValid() || start != 0) { 0256 return; 0257 } 0258 endResetModel(); 0259 } 0260 0261 void PaginateModel::_k_sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles) 0262 { 0263 if (topLeft.parent().isValid() || bottomRight.row() < d->m_firstItem || topLeft.row() > lastItem()) { 0264 return; 0265 } 0266 0267 QModelIndex idxTop = mapFromSource(topLeft); 0268 QModelIndex idxBottom = mapFromSource(bottomRight); 0269 if (!idxTop.isValid()) 0270 idxTop = index(0); 0271 if (!idxBottom.isValid()) 0272 idxBottom = index(rowCount() - 1); 0273 0274 Q_EMIT dataChanged(idxTop, idxBottom, roles); 0275 } 0276 0277 void PaginateModel::_k_sourceHeaderDataChanged(Qt::Orientation orientation, int first, int last) 0278 { 0279 Q_UNUSED(last) 0280 if (first == 0) 0281 Q_EMIT headerDataChanged(orientation, 0, 0); 0282 } 0283 0284 void PaginateModel::_k_sourceModelAboutToBeReset() 0285 { 0286 beginResetModel(); 0287 } 0288 0289 void PaginateModel::_k_sourceModelReset() 0290 { 0291 endResetModel(); 0292 } 0293 0294 bool PaginateModel::isIntervalValid(const QModelIndex &parent, int start, int /*end*/) const 0295 { 0296 return !parent.isValid() && start <= lastItem(); 0297 } 0298 0299 bool PaginateModel::canSizeChange() const 0300 { 0301 return !d->m_hasStaticRowCount && currentPage() == pageCount() - 1; 0302 } 0303 0304 void PaginateModel::_k_sourceRowsAboutToBeInserted(const QModelIndex &parent, int start, int end) 0305 { 0306 if (!isIntervalValid(parent, start, end)) { 0307 return; 0308 } 0309 0310 if (canSizeChange()) { 0311 const int newStart = qMax(start - d->m_firstItem, 0); 0312 const int insertedCount = qMin(end - start, pageSize() - newStart - 1); 0313 beginInsertRows(QModelIndex(), newStart, newStart + insertedCount); 0314 } else { 0315 beginResetModel(); 0316 } 0317 } 0318 0319 void PaginateModel::_k_sourceRowsInserted(const QModelIndex &parent, int start, int end) 0320 { 0321 if (!isIntervalValid(parent, start, end)) { 0322 return; 0323 } 0324 0325 if (canSizeChange()) { 0326 endInsertRows(); 0327 } else { 0328 endResetModel(); 0329 } 0330 } 0331 0332 void PaginateModel::_k_sourceRowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest) 0333 { 0334 Q_UNUSED(sourceParent) 0335 Q_UNUSED(sourceStart) 0336 Q_UNUSED(sourceEnd) 0337 Q_UNUSED(destParent) 0338 Q_UNUSED(dest) 0339 // NOTE could optimize, unsure if it makes sense 0340 beginResetModel(); 0341 } 0342 0343 void PaginateModel::_k_sourceRowsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest) 0344 { 0345 Q_UNUSED(sourceParent) 0346 Q_UNUSED(sourceStart) 0347 Q_UNUSED(sourceEnd) 0348 Q_UNUSED(destParent) 0349 Q_UNUSED(dest) 0350 endResetModel(); 0351 } 0352 0353 void PaginateModel::_k_sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) 0354 { 0355 if (!isIntervalValid(parent, start, end)) { 0356 return; 0357 } 0358 0359 if (canSizeChange()) { 0360 const int removedCount = end - start; 0361 const int newStart = qMax(start - d->m_firstItem, 0); 0362 beginRemoveRows(QModelIndex(), newStart, newStart + removedCount); 0363 } else { 0364 beginResetModel(); 0365 } 0366 } 0367 0368 void PaginateModel::_k_sourceRowsRemoved(const QModelIndex &parent, int start, int end) 0369 { 0370 if (!isIntervalValid(parent, start, end)) { 0371 return; 0372 } 0373 0374 if (canSizeChange()) { 0375 endRemoveRows(); 0376 } else { 0377 beginResetModel(); 0378 } 0379 } 0380 0381 int PaginateModel::lastItem() const 0382 { 0383 return d->m_firstItem + rowCount(); 0384 }