KWidgetsAddons

kviewstateserializer.cpp
1 /*
2  SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
3  SPDX-FileContributor: Stephen Kelly <[email protected]>
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 
16 class KViewStateSerializerPrivate
17 {
18 public:
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;
86  QPointer<QAbstractScrollArea> m_scrollArea;
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 {
100  Q_UNUSED(parent);
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 {
122  Q_D(const KViewStateSerializer);
123  return d->m_view;
124 }
125 
127 {
128  Q_D(const KViewStateSerializer);
129  return d->m_selectionModel;
130 }
131 
133 {
135  d->m_selectionModel = selectionModel;
136 }
137 
138 void 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 
158 void 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 
168 QStringList 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 
187 void 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 
220 void 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 
255 void 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 
271 void 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 {
309  Q_D(const KViewStateSerializer);
310  if (!d->m_selectionModel) {
311  return QString();
312  }
313  return indexToConfigString(d->m_selectionModel->currentIndex());
314 }
315 
317 {
318  Q_D(const KViewStateSerializer);
319  if (!d->m_treeView || !d->m_treeView->model()) {
320  return QStringList();
321  }
322 
323  return d->getExpandedItems(QModelIndex());
324 }
325 
327 {
328  Q_D(const KViewStateSerializer);
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 {
345  Q_D(const KViewStateSerializer);
346  return qMakePair(d->m_scrollArea->verticalScrollBar()->value(), d->m_scrollArea->horizontalScrollBar()->value());
347 }
348 
349 void 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"
~KViewStateSerializer() override
Destructor.
virtual QString indexToConfigString(const QModelIndex &index) const =0
Reimplement to return a unique string for the index.
QSet::iterator erase(QSet::iterator pos)
QItemSelectionModel * selectionModel() const const
QStringList expansionKeys() const
Returns a QStringList representing the expanded indexes in the QTreeView.
Object for saving and restoring state in QTreeViews and QItemSelectionModels.
QAbstractItemView * view() const
The view whose state is persisted.
void reserve(int alloc)
void deleteLater()
void restoreScrollState(int verticalScoll, int horizontalScroll)
Restores the scroll state of the QAbstractScrollArea to the verticalScoll and horizontalScroll.
bool isEmpty() const const
QString currentIndexKey() const
Returns a QString describing the current index in the selection model.
QItemSelectionModel * selectionModel() const
The QItemSelectionModel whose state is persisted.
void restoreExpanded(const QStringList &indexStrings)
Expand the indexes described by indexStrings in the QTreeView.
bool isEmpty() const const
QSet::iterator begin()
bool isValid() const const
void restoreSelection(const QStringList &indexStrings)
Select the indexes described by indexStrings.
QPair< int, int > scrollState() const
Returns the vertical and horizontal scroll of the QAbstractScrollArea.
QStringList selectionKeys() const
Returns a QStringList describing the selection in the selectionModel.
KViewStateSerializer(QObject *parent=nullptr)
Constructor.
void rowsInserted(const QModelIndex &parent, int first, int last)
QSet::iterator end()
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const=0
QList::iterator begin()
QList::iterator end()
void setView(QAbstractItemView *view)
Sets the view whose state is persisted.
void restoreCurrentItem(const QString &indexString)
Make the index described by indexString the currentIndex in the selectionModel.
QObject * parent() const const
const QAbstractItemModel * model() const const
void setSelectionModel(QItemSelectionModel *selectionModel)
Sets the QItemSelectionModel whose state is persisted.
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Tue Sep 26 2023 03:59:36 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.