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