KWidgetsAddons

kviewstateserializer.cpp
1/*
2 SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
3 SPDX-FileContributor: Stephen Kelly <stephen@kdab.com>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#include "kviewstateserializer.h"
9
10#include <QAbstractScrollArea>
11#include <QPointer>
12#include <QScrollBar>
13#include <QTimer>
14#include <QTreeView>
15
16class KViewStateSerializerPrivate
17{
18public:
19 KViewStateSerializerPrivate(KViewStateSerializer *qq)
20 : q_ptr(qq)
21 , m_treeView(nullptr)
22 , m_view(nullptr)
23 , m_selectionModel(nullptr)
24 , m_scrollArea(nullptr)
25 , m_horizontalScrollBarValue(-1)
26 , m_verticalScrollBarValue(-1)
27 {
28 }
29
30 Q_DECLARE_PUBLIC(KViewStateSerializer)
31 KViewStateSerializer *const q_ptr;
32
33 QStringList getExpandedItems(const QModelIndex &index) const;
34
35 void listenToPendingChanges();
36 void processPendingChanges();
37
38 inline void restoreScrollBarState()
39 {
40 if (!m_scrollArea || !m_scrollArea->horizontalScrollBar() || !m_scrollArea->verticalScrollBar()) {
41 return;
42 }
43 if (m_horizontalScrollBarValue >= 0 && m_horizontalScrollBarValue <= m_scrollArea->horizontalScrollBar()->maximum()) {
44 m_scrollArea->horizontalScrollBar()->setValue(m_horizontalScrollBarValue);
45 m_horizontalScrollBarValue = -1;
46 }
47 if (m_verticalScrollBarValue >= 0 && m_verticalScrollBarValue <= m_scrollArea->verticalScrollBar()->maximum()) {
48 m_scrollArea->verticalScrollBar()->setValue(m_verticalScrollBarValue);
49 m_verticalScrollBarValue = -1;
50 }
51 }
52
53 void restoreSelection();
54 void restoreCurrentItem();
55 void restoreExpanded();
56
57 inline bool hasPendingChanges() const
58 {
59 return !m_pendingCurrent.isEmpty() || !m_pendingExpansions.isEmpty() || !m_pendingSelections.isEmpty();
60 }
61
62 const QAbstractItemModel *getModel()
63 {
64 if (m_selectionModel && m_selectionModel->model()) {
65 return m_selectionModel->model();
66 } else if (m_view && m_view->model()) {
67 return m_view->model();
68 }
69 return nullptr;
70 }
71
72 void rowsInserted(const QModelIndex & /*index*/, int /*start*/, int /*end*/)
73 {
75 processPendingChanges();
76
77 if (!hasPendingChanges()) {
78 q->disconnect(m_rowsInsertedConnection);
79 q->deleteLater();
80 }
81 }
82
83 QTreeView *m_treeView;
84 QAbstractItemView *m_view;
85 QItemSelectionModel *m_selectionModel;
87
88 int m_horizontalScrollBarValue;
89 int m_verticalScrollBarValue;
90 QSet<QString> m_pendingSelections;
91 QSet<QString> m_pendingExpansions;
92 QString m_pendingCurrent;
93 QMetaObject::Connection m_rowsInsertedConnection;
94};
95
97 : QObject(nullptr)
98 , d_ptr(new KViewStateSerializerPrivate(this))
99{
101 qRegisterMetaType<QModelIndex>("QModelIndex");
102}
103
105
107{
109 d->m_scrollArea = view;
110 if (view) {
111 d->m_selectionModel = view->selectionModel();
112 d->m_treeView = qobject_cast<QTreeView *>(view);
113 } else {
114 d->m_selectionModel = nullptr;
115 d->m_treeView = nullptr;
116 }
117 d->m_view = view;
118}
119
121{
123 return d->m_view;
124}
125
127{
129 return d->m_selectionModel;
130}
131
133{
135 d->m_selectionModel = selectionModel;
136}
137
138void KViewStateSerializerPrivate::listenToPendingChanges()
139{
141 // watch the model for stuff coming in delayed
142 if (hasPendingChanges()) {
143 const QAbstractItemModel *model = getModel();
144 if (model) {
145 q->disconnect(m_rowsInsertedConnection);
146 m_rowsInsertedConnection = q->connect(model, &QAbstractItemModel::rowsInserted, q, [this](const QModelIndex &parent, int first, int last) {
147 rowsInserted(parent, first, last);
148 });
149 return;
150 } else {
151 q->deleteLater();
152 }
153 } else {
154 q->deleteLater();
155 }
156}
157
158void KViewStateSerializerPrivate::processPendingChanges()
159{
161
162 q->restoreCurrentItem(m_pendingCurrent);
163 q->restoreSelection(m_pendingSelections.values());
164 q->restoreExpanded(m_pendingExpansions.values());
165 q->restoreScrollState(m_verticalScrollBarValue, m_horizontalScrollBarValue);
166}
167
168QStringList KViewStateSerializerPrivate::getExpandedItems(const QModelIndex &index) const
169{
170 Q_Q(const KViewStateSerializer);
171
172 QStringList expansion;
173 for (int i = 0; i < m_treeView->model()->rowCount(index); ++i) {
174 const QModelIndex child = m_treeView->model()->index(i, 0, index);
175
176 // http://bugreports.qt.nokia.com/browse/QTBUG-18039
177 if (m_treeView->model()->hasChildren(child)) {
178 if (m_treeView->isExpanded(child)) {
179 expansion << q->indexToConfigString(child);
180 }
181 expansion << getExpandedItems(child);
182 }
183 }
184 return expansion;
185}
186
187void KViewStateSerializerPrivate::restoreCurrentItem()
188{
190
191 QModelIndex currentIndex = q->indexFromConfigString(m_selectionModel->model(), m_pendingCurrent);
192 if (currentIndex.isValid()) {
193 if (m_treeView) {
194 m_treeView->setCurrentIndex(currentIndex);
195 } else {
196 m_selectionModel->setCurrentIndex(currentIndex, QItemSelectionModel::NoUpdate);
197 }
198 m_pendingCurrent.clear();
199 }
200}
201
203{
205 if (!d->m_selectionModel || !d->m_selectionModel->model()) {
206 return;
207 }
208
209 if (indexString.isEmpty()) {
210 return;
211 }
212 d->m_pendingCurrent = indexString;
213 d->restoreCurrentItem();
214
215 if (d->hasPendingChanges()) {
216 d->listenToPendingChanges();
217 }
218}
219
220void KViewStateSerializerPrivate::restoreExpanded()
221{
223
224 QSet<QString>::iterator it = m_pendingExpansions.begin();
225 for (; it != m_pendingExpansions.end();) {
226 QModelIndex idx = q->indexFromConfigString(m_treeView->model(), *it);
227 if (idx.isValid()) {
228 m_treeView->expand(idx);
229 it = m_pendingExpansions.erase(it);
230 } else {
231 ++it;
232 }
233 }
234}
235
237{
239 if (!d->m_treeView || !d->m_treeView->model()) {
240 return;
241 }
242
243 if (indexStrings.isEmpty()) {
244 return;
245 }
246
247 d->m_pendingExpansions.unite(QSet<QString>(indexStrings.begin(), indexStrings.end()));
248
249 d->restoreExpanded();
250 if (d->hasPendingChanges()) {
251 d->listenToPendingChanges();
252 }
253}
254
255void KViewStateSerializer::restoreScrollState(int verticalScoll, int horizontalScroll)
256{
258
259 if (!d->m_scrollArea) {
260 return;
261 }
262
263 d->m_verticalScrollBarValue = verticalScoll;
264 d->m_horizontalScrollBarValue = horizontalScroll;
265
266 QTimer::singleShot(0, this, [d]() {
267 d->restoreScrollBarState();
268 });
269}
270
271void KViewStateSerializerPrivate::restoreSelection()
272{
274
275 QSet<QString>::iterator it = m_pendingSelections.begin();
276 for (; it != m_pendingSelections.end();) {
277 QModelIndex idx = q->indexFromConfigString(m_selectionModel->model(), *it);
278 if (idx.isValid()) {
279 m_selectionModel->select(idx, QItemSelectionModel::Select);
280 it = m_pendingSelections.erase(it);
281 } else {
282 ++it;
283 }
284 }
285}
286
288{
290
291 if (!d->m_selectionModel || !d->m_selectionModel->model()) {
292 return;
293 }
294
295 if (indexStrings.isEmpty()) {
296 return;
297 }
298
299 d->m_pendingSelections.unite(QSet<QString>(indexStrings.begin(), indexStrings.end()));
300
301 d->restoreSelection();
302 if (d->hasPendingChanges()) {
303 d->listenToPendingChanges();
304 }
305}
306
308{
310 if (!d->m_selectionModel) {
311 return QString();
312 }
313 return indexToConfigString(d->m_selectionModel->currentIndex());
314}
315
317{
319 if (!d->m_treeView || !d->m_treeView->model()) {
320 return QStringList();
321 }
322
323 return d->getExpandedItems(QModelIndex());
324}
325
327{
329 if (!d->m_selectionModel) {
330 return QStringList();
331 }
332
333 const QModelIndexList selectedIndexes = d->m_selectionModel->selectedRows();
334 QStringList selection;
335 selection.reserve(selectedIndexes.count());
336 for (const QModelIndex &index : selectedIndexes) {
337 selection << indexToConfigString(index);
338 }
339
340 return selection;
341}
342
344{
346 return qMakePair(d->m_scrollArea->verticalScrollBar()->value(), d->m_scrollArea->horizontalScrollBar()->value());
347}
348
349void KViewStateSerializer::restoreState()
350{
352 // Delete myself if not finished after 60 seconds
354
355 d->processPendingChanges();
356 if (d->hasPendingChanges()) {
357 d->listenToPendingChanges();
358 }
359}
360
361#include "moc_kviewstateserializer.cpp"
Object for saving and restoring state in QTreeViews and QItemSelectionModels.
QStringList selectionKeys() const
Returns a QStringList describing the selection in the selectionModel.
QPair< int, int > scrollState() const
Returns the vertical and horizontal scroll of the QAbstractScrollArea.
virtual QString indexToConfigString(const QModelIndex &index) const =0
Reimplement to return a unique string for the index.
void restoreSelection(const QStringList &indexStrings)
Select the indexes described by indexStrings.
QString currentIndexKey() const
Returns a QString describing the current index in the selection model.
void restoreCurrentItem(const QString &indexString)
Make the index described by indexString the currentIndex in the selectionModel.
~KViewStateSerializer() override
Destructor.
void restoreExpanded(const QStringList &indexStrings)
Expand the indexes described by indexStrings in the QTreeView.
void restoreScrollState(int verticalScoll, int horizontalScroll)
Restores the scroll state of the QAbstractScrollArea to the verticalScoll and horizontalScroll.
QStringList expansionKeys() const
Returns a QStringList representing the expanded indexes in the QTreeView.
QAbstractItemView * view() const
The view whose state is persisted.
KViewStateSerializer(QObject *parent=nullptr)
Constructor.
QItemSelectionModel * selectionModel() const
The QItemSelectionModel whose state is persisted.
void setSelectionModel(QItemSelectionModel *selectionModel)
Sets the QItemSelectionModel whose state is persisted.
void setView(QAbstractItemView *view)
Sets the view whose state is persisted.
virtual bool hasChildren(const QModelIndex &parent) const const
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const=0
void rowsInserted(const QModelIndex &parent, int first, int last)
QAbstractItemModel * model() const const
QItemSelectionModel * selectionModel() const const
void setCurrentIndex(const QModelIndex &index)
QAbstractItemModel * model()
virtual void select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command)
virtual void setCurrentIndex(const QModelIndex &index, QItemSelectionModel::SelectionFlags command)
void reserve(qsizetype size)
bool isValid() const const
void deleteLater()
QObject * parent() const const
T qobject_cast(QObject *object)
iterator begin()
iterator end()
iterator erase(const_iterator pos)
bool isEmpty() const const
QList< T > values() const const
void clear()
bool isEmpty() const const
void expand(const QModelIndex &index)
bool isExpanded(const QModelIndex &index) const const
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri May 3 2024 11:45:27 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.