File indexing completed on 2025-02-02 14:20:13
0001 /* 0002 SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.net> 0003 SPDX-FileContributor: Stephen Kelly <stephen@kdab.com> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "kviewstateserializer.h" 0009 0010 #include <QAbstractScrollArea> 0011 #include <QPointer> 0012 #include <QScrollBar> 0013 #include <QTimer> 0014 #include <QTreeView> 0015 0016 class KViewStateSerializerPrivate 0017 { 0018 public: 0019 KViewStateSerializerPrivate(KViewStateSerializer *qq) 0020 : q_ptr(qq) 0021 , m_treeView(nullptr) 0022 , m_view(nullptr) 0023 , m_selectionModel(nullptr) 0024 , m_scrollArea(nullptr) 0025 , m_horizontalScrollBarValue(-1) 0026 , m_verticalScrollBarValue(-1) 0027 { 0028 } 0029 0030 Q_DECLARE_PUBLIC(KViewStateSerializer) 0031 KViewStateSerializer *const q_ptr; 0032 0033 QStringList getExpandedItems(const QModelIndex &index) const; 0034 0035 void listenToPendingChanges(); 0036 void processPendingChanges(); 0037 0038 inline void restoreScrollBarState() 0039 { 0040 if (!m_scrollArea || !m_scrollArea->horizontalScrollBar() || !m_scrollArea->verticalScrollBar()) { 0041 return; 0042 } 0043 if (m_horizontalScrollBarValue >= 0 && m_horizontalScrollBarValue <= m_scrollArea->horizontalScrollBar()->maximum()) { 0044 m_scrollArea->horizontalScrollBar()->setValue(m_horizontalScrollBarValue); 0045 m_horizontalScrollBarValue = -1; 0046 } 0047 if (m_verticalScrollBarValue >= 0 && m_verticalScrollBarValue <= m_scrollArea->verticalScrollBar()->maximum()) { 0048 m_scrollArea->verticalScrollBar()->setValue(m_verticalScrollBarValue); 0049 m_verticalScrollBarValue = -1; 0050 } 0051 } 0052 0053 void restoreSelection(); 0054 void restoreCurrentItem(); 0055 void restoreExpanded(); 0056 0057 inline bool hasPendingChanges() const 0058 { 0059 return !m_pendingCurrent.isEmpty() || !m_pendingExpansions.isEmpty() || !m_pendingSelections.isEmpty(); 0060 } 0061 0062 const QAbstractItemModel *getModel() 0063 { 0064 if (m_selectionModel && m_selectionModel->model()) { 0065 return m_selectionModel->model(); 0066 } else if (m_view && m_view->model()) { 0067 return m_view->model(); 0068 } 0069 return nullptr; 0070 } 0071 0072 void rowsInserted(const QModelIndex & /*index*/, int /*start*/, int /*end*/) 0073 { 0074 Q_Q(KViewStateSerializer); 0075 processPendingChanges(); 0076 0077 if (!hasPendingChanges()) { 0078 q->disconnect(m_rowsInsertedConnection); 0079 q->deleteLater(); 0080 } 0081 } 0082 0083 QTreeView *m_treeView; 0084 QAbstractItemView *m_view; 0085 QItemSelectionModel *m_selectionModel; 0086 QPointer<QAbstractScrollArea> m_scrollArea; 0087 0088 int m_horizontalScrollBarValue; 0089 int m_verticalScrollBarValue; 0090 QSet<QString> m_pendingSelections; 0091 QSet<QString> m_pendingExpansions; 0092 QString m_pendingCurrent; 0093 QMetaObject::Connection m_rowsInsertedConnection; 0094 }; 0095 0096 KViewStateSerializer::KViewStateSerializer(QObject *parent) 0097 : QObject(nullptr) 0098 , d_ptr(new KViewStateSerializerPrivate(this)) 0099 { 0100 Q_UNUSED(parent); 0101 qRegisterMetaType<QModelIndex>("QModelIndex"); 0102 } 0103 0104 KViewStateSerializer::~KViewStateSerializer() = default; 0105 0106 void KViewStateSerializer::setView(QAbstractItemView *view) 0107 { 0108 Q_D(KViewStateSerializer); 0109 d->m_scrollArea = view; 0110 if (view) { 0111 d->m_selectionModel = view->selectionModel(); 0112 d->m_treeView = qobject_cast<QTreeView *>(view); 0113 } else { 0114 d->m_selectionModel = nullptr; 0115 d->m_treeView = nullptr; 0116 } 0117 d->m_view = view; 0118 } 0119 0120 QAbstractItemView *KViewStateSerializer::view() const 0121 { 0122 Q_D(const KViewStateSerializer); 0123 return d->m_view; 0124 } 0125 0126 QItemSelectionModel *KViewStateSerializer::selectionModel() const 0127 { 0128 Q_D(const KViewStateSerializer); 0129 return d->m_selectionModel; 0130 } 0131 0132 void KViewStateSerializer::setSelectionModel(QItemSelectionModel *selectionModel) 0133 { 0134 Q_D(KViewStateSerializer); 0135 d->m_selectionModel = selectionModel; 0136 } 0137 0138 void KViewStateSerializerPrivate::listenToPendingChanges() 0139 { 0140 Q_Q(KViewStateSerializer); 0141 // watch the model for stuff coming in delayed 0142 if (hasPendingChanges()) { 0143 const QAbstractItemModel *model = getModel(); 0144 if (model) { 0145 q->disconnect(m_rowsInsertedConnection); 0146 m_rowsInsertedConnection = q->connect(model, &QAbstractItemModel::rowsInserted, q, [this](const QModelIndex &parent, int first, int last) { 0147 rowsInserted(parent, first, last); 0148 }); 0149 return; 0150 } else { 0151 q->deleteLater(); 0152 } 0153 } else { 0154 q->deleteLater(); 0155 } 0156 } 0157 0158 void KViewStateSerializerPrivate::processPendingChanges() 0159 { 0160 Q_Q(KViewStateSerializer); 0161 0162 q->restoreCurrentItem(m_pendingCurrent); 0163 q->restoreSelection(m_pendingSelections.values()); 0164 q->restoreExpanded(m_pendingExpansions.values()); 0165 q->restoreScrollState(m_verticalScrollBarValue, m_horizontalScrollBarValue); 0166 } 0167 0168 QStringList KViewStateSerializerPrivate::getExpandedItems(const QModelIndex &index) const 0169 { 0170 Q_Q(const KViewStateSerializer); 0171 0172 QStringList expansion; 0173 for (int i = 0; i < m_treeView->model()->rowCount(index); ++i) { 0174 const QModelIndex child = m_treeView->model()->index(i, 0, index); 0175 0176 // http://bugreports.qt.nokia.com/browse/QTBUG-18039 0177 if (m_treeView->model()->hasChildren(child)) { 0178 if (m_treeView->isExpanded(child)) { 0179 expansion << q->indexToConfigString(child); 0180 } 0181 expansion << getExpandedItems(child); 0182 } 0183 } 0184 return expansion; 0185 } 0186 0187 void KViewStateSerializerPrivate::restoreCurrentItem() 0188 { 0189 Q_Q(KViewStateSerializer); 0190 0191 QModelIndex currentIndex = q->indexFromConfigString(m_selectionModel->model(), m_pendingCurrent); 0192 if (currentIndex.isValid()) { 0193 if (m_treeView) { 0194 m_treeView->setCurrentIndex(currentIndex); 0195 } else { 0196 m_selectionModel->setCurrentIndex(currentIndex, QItemSelectionModel::NoUpdate); 0197 } 0198 m_pendingCurrent.clear(); 0199 } 0200 } 0201 0202 void KViewStateSerializer::restoreCurrentItem(const QString &indexString) 0203 { 0204 Q_D(KViewStateSerializer); 0205 if (!d->m_selectionModel || !d->m_selectionModel->model()) { 0206 return; 0207 } 0208 0209 if (indexString.isEmpty()) { 0210 return; 0211 } 0212 d->m_pendingCurrent = indexString; 0213 d->restoreCurrentItem(); 0214 0215 if (d->hasPendingChanges()) { 0216 d->listenToPendingChanges(); 0217 } 0218 } 0219 0220 void KViewStateSerializerPrivate::restoreExpanded() 0221 { 0222 Q_Q(KViewStateSerializer); 0223 0224 QSet<QString>::iterator it = m_pendingExpansions.begin(); 0225 for (; it != m_pendingExpansions.end();) { 0226 QModelIndex idx = q->indexFromConfigString(m_treeView->model(), *it); 0227 if (idx.isValid()) { 0228 m_treeView->expand(idx); 0229 it = m_pendingExpansions.erase(it); 0230 } else { 0231 ++it; 0232 } 0233 } 0234 } 0235 0236 void KViewStateSerializer::restoreExpanded(const QStringList &indexStrings) 0237 { 0238 Q_D(KViewStateSerializer); 0239 if (!d->m_treeView || !d->m_treeView->model()) { 0240 return; 0241 } 0242 0243 if (indexStrings.isEmpty()) { 0244 return; 0245 } 0246 0247 d->m_pendingExpansions.unite(QSet<QString>(indexStrings.begin(), indexStrings.end())); 0248 0249 d->restoreExpanded(); 0250 if (d->hasPendingChanges()) { 0251 d->listenToPendingChanges(); 0252 } 0253 } 0254 0255 void KViewStateSerializer::restoreScrollState(int verticalScoll, int horizontalScroll) 0256 { 0257 Q_D(KViewStateSerializer); 0258 0259 if (!d->m_scrollArea) { 0260 return; 0261 } 0262 0263 d->m_verticalScrollBarValue = verticalScoll; 0264 d->m_horizontalScrollBarValue = horizontalScroll; 0265 0266 QTimer::singleShot(0, this, [d]() { 0267 d->restoreScrollBarState(); 0268 }); 0269 } 0270 0271 void KViewStateSerializerPrivate::restoreSelection() 0272 { 0273 Q_Q(KViewStateSerializer); 0274 0275 QSet<QString>::iterator it = m_pendingSelections.begin(); 0276 for (; it != m_pendingSelections.end();) { 0277 QModelIndex idx = q->indexFromConfigString(m_selectionModel->model(), *it); 0278 if (idx.isValid()) { 0279 m_selectionModel->select(idx, QItemSelectionModel::Select); 0280 it = m_pendingSelections.erase(it); 0281 } else { 0282 ++it; 0283 } 0284 } 0285 } 0286 0287 void KViewStateSerializer::restoreSelection(const QStringList &indexStrings) 0288 { 0289 Q_D(KViewStateSerializer); 0290 0291 if (!d->m_selectionModel || !d->m_selectionModel->model()) { 0292 return; 0293 } 0294 0295 if (indexStrings.isEmpty()) { 0296 return; 0297 } 0298 0299 d->m_pendingSelections.unite(QSet<QString>(indexStrings.begin(), indexStrings.end())); 0300 0301 d->restoreSelection(); 0302 if (d->hasPendingChanges()) { 0303 d->listenToPendingChanges(); 0304 } 0305 } 0306 0307 QString KViewStateSerializer::currentIndexKey() const 0308 { 0309 Q_D(const KViewStateSerializer); 0310 if (!d->m_selectionModel) { 0311 return QString(); 0312 } 0313 return indexToConfigString(d->m_selectionModel->currentIndex()); 0314 } 0315 0316 QStringList KViewStateSerializer::expansionKeys() const 0317 { 0318 Q_D(const KViewStateSerializer); 0319 if (!d->m_treeView || !d->m_treeView->model()) { 0320 return QStringList(); 0321 } 0322 0323 return d->getExpandedItems(QModelIndex()); 0324 } 0325 0326 QStringList KViewStateSerializer::selectionKeys() const 0327 { 0328 Q_D(const KViewStateSerializer); 0329 if (!d->m_selectionModel) { 0330 return QStringList(); 0331 } 0332 0333 const QModelIndexList selectedIndexes = d->m_selectionModel->selectedRows(); 0334 QStringList selection; 0335 selection.reserve(selectedIndexes.count()); 0336 for (const QModelIndex &index : selectedIndexes) { 0337 selection << indexToConfigString(index); 0338 } 0339 0340 return selection; 0341 } 0342 0343 QPair<int, int> KViewStateSerializer::scrollState() const 0344 { 0345 Q_D(const KViewStateSerializer); 0346 return qMakePair(d->m_scrollArea->verticalScrollBar()->value(), d->m_scrollArea->horizontalScrollBar()->value()); 0347 } 0348 0349 void KViewStateSerializer::restoreState() 0350 { 0351 Q_D(KViewStateSerializer); 0352 // Delete myself if not finished after 60 seconds 0353 QTimer::singleShot(60000, this, &KViewStateSerializer::deleteLater); 0354 0355 d->processPendingChanges(); 0356 if (d->hasPendingChanges()) { 0357 d->listenToPendingChanges(); 0358 } 0359 } 0360 0361 #include "moc_kviewstateserializer.cpp"