1/*
2 Copyright (C) 2010 Klarälvdalens Datakonsult AB,
3 a KDAB Group company, info@kdab.net,
4 author Stephen Kelly <stephen@kdab.com>
5
6 This library is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Library General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or (at your
9 option) any later version.
10
11 This library is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
14 License for more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301, USA.
20*/
21
22#ifndef KVIEWSTATESAVER_H
23#define KVIEWSTATESAVER_H
24
25#include <QtCore/QObject>
26#include <QtCore/QPair>
27
28#include "kdeui_export.h"
29
30class QAbstractItemView;
31class QItemSelectionModel;
32class QAbstractItemModel;
33class QAbstractScrollArea;
34class QModelIndex;
35class QStringList;
36
37class KConfigGroup;
38
39class KViewStateSaverPrivate;
40
41/**
42 @brief Object for saving and restoring state in QTreeViews and QItemSelectionModels
43
44 Implement the indexFromConfigString and indexToConfigString methods to
45 handle the model in the view whose state is being saved. These implementations can be quite trivial:
46
47 @code
48 QModelIndex DynamicTreeStateSaver::indexFromConfigString(const QAbstractItemModel* model, const QString& key) const
49 {
50 QModelIndexList list = model->match(model->index(0, 0), DynamicTreeModel::DynamicTreeModelId, key.toInt(), 1, Qt::MatchRecursive);
51 if (list.isEmpty())
52 return QModelIndex();
53 return list.first();
54 }
55
56 QString DynamicTreeStateSaver::indexToConfigString(const QModelIndex& index) const
57 {
58 return index.data(DynamicTreeModel::DynamicTreeModelId).toString();
59 }
60 @endcode
61
62 It is possible to restore the state of a QTreeView (that is, the expanded state and selected state of all indexes
63 as well as the horizontal and vertical scroll state) by using setTreeView.
64
65 If there is no tree view state to restore (for example if using QML), the selection state of a QItemSelectionModel
66 can be saved or restored instead.
67
68 The state of any QAbstractScrollArea can also be saved and restored.
69
70 A KViewStateSaver should be created on the stack when saving and on the heap when restoring. The model may be populated dynamically between several
71 event loops, so it may not be immediate for the indexes that should be selected to be in the model. The saver should *not* be persisted as a
72 member. The saver will destroy itself when it has completed the restoration specified in the config group, or a small amount of time has elapsed.
73
74 @code
75 MyWidget::MyWidget(Qobject *parent)
76 : QWidget(parent)
77 {
78 ...
79
80 m_view = new QTreeView(splitter);
81 m_view->setModel(model);
82
83 connect( model, SIGNAL(modelAboutToBeReset()), SLOT(saveState()) );
84 connect( model, SIGNAL(modelReset()), SLOT(restoreState()) );
85 connect( qApp, SIGNAL(aboutToQuit()), SLOT(saveState()) );
86
87 restoreState();
88 }
89
90 void StateSaverWidget::saveState()
91 {
92 ConcreteStateSaver saver;
93 saver.setTreeView(m_view);
94
95 KConfigGroup cfg( KGlobal::config(), "ExampleViewState" );
96 saver.saveState( cfg );
97 cfg.sync();
98 }
99
100 void StateSaverWidget::restoreState()
101 {
102 // Will delete itself.
103 ConcreteTreeStateSaver *saver = new ConcreteStateSaver();
104 saver->setTreeView(m_view);
105 KConfigGroup cfg( KGlobal::config(), "ExampleViewState" );
106 saver->restoreState( cfg );
107 }
108 @endcode
109
110 After creating a saver, the state can be saved using a KConfigGroup.
111
112 It is also possible to save and restore state directly by using the restoreSelection,
113 restoreExpanded etc methods. Note that the implementation of these methods should return
114 strings that the indexFromConfigString implementation can handle.
115
116 @code
117 class DynamicTreeStateSaver : public KViewStateSaver
118 {
119 Q_OBJECT
120 public:
121 // ...
122
123 void selectItems(const QList<qint64> &items)
124 {
125 QStringList itemStrings;
126 foreach(qint64 item, items)
127 itemStrings << QString::number(item);
128 restoreSelection(itemStrings);
129 }
130
131 void expandItems(const QList<qint64> &items)
132 {
133 QStringList itemStrings;
134 foreach(qint64 item, items)
135 itemStrings << QString::number(item);
136 restoreSelection(itemStrings);
137 }
138
139 };
140 @endcode
141
142
143 Note that a single instance of this class should be used with only one widget. That is don't do this:
144
145 @code
146 saver->setTreeView(treeView1);
147 saver->setSelectionModel(treeView2->selectionModel());
148 saver->setScrollArea(treeView3);
149 @endcode
150
151 To save the state of 3 different widgets, use three savers, even if they operate on the same root model.
152
153 @code
154 saver1->setTreeView(treeView1);
155 saver2->setSelectionModel(treeView2->selectionModel());
156 saver3->setScrollArea(treeView3);
157 @endcode
158
159 @note The KViewStateSaver does not take ownership of any widgets set on it.
160
161 It is recommended to restore the state on application startup and after the model has been reset, and to
162 save the state on application close and before the model has been reset.
163
164 @see QAbstractItemModel::modelAboutToBeReset QAbstractItemModel::modelReset
165
166 @author Stephen Kelly <stephen@kdab.com>
167 @since 4.5
168*/
169class KDEUI_EXPORT KViewStateSaver : public QObject
170{
171 Q_OBJECT
172public:
173 /**
174 Constructor
175 @param parent standard parent object, but ignored since this object will delete itself
176 */
177 explicit KViewStateSaver(QObject *parent = 0);
178
179 /**
180 Destructor
181 */
182 ~KViewStateSaver();
183
184 /**
185 * The view whose state is persisted.
186 */
187 QAbstractItemView* view() const;
188
189 /**
190 * Sets the view whose state is persisted.
191 */
192 void setView(QAbstractItemView *view);
193
194 /**
195 The QItemSelectionModel whose state is persisted.
196 */
197 QItemSelectionModel* selectionModel() const;
198
199 /**
200 Sets the QItemSelectionModel whose state is persisted.
201 */
202 void setSelectionModel( QItemSelectionModel *selectionModel );
203
204 /**
205 Saves the state to the @p configGroup
206 */
207 void saveState(KConfigGroup &configGroup);
208
209 /**
210 Restores the state from the @p configGroup
211 */
212 void restoreState(const KConfigGroup &configGroup);
213
214 /**
215 * Returns a QStringList describing the selection in the selectionModel.
216 */
217 QStringList selectionKeys() const;
218
219 /**
220 * Returns a QStringList representing the expanded indexes in the QTreeView.
221 */
222 QStringList expansionKeys() const;
223
224 /**
225 * Returns a QString describing the current index in the selection model.
226 */
227 QString currentIndexKey() const;
228
229 /**
230 * Returns the vertical and horizontal scroll of the QAbstractScrollArea.
231 */
232 QPair<int, int> scrollState() const;
233
234 /**
235 * Select the indexes described by @p indexStrings
236 */
237 void restoreSelection( const QStringList &indexStrings );
238
239 /**
240 * Make the index described by @p indexString the currentIndex in the selectionModel.
241 */
242 void restoreCurrentItem( const QString &indexString );
243
244 /**
245 * Expand the indexes described by @p indexStrings in the QTreeView.
246 */
247 void restoreExpanded( const QStringList &indexStrings );
248
249 /**
250 * Restores the scroll state of the QAbstractScrollArea to the @p verticalScoll
251 * and @p horizontalScroll
252 */
253 void restoreScrollState( int verticalScoll, int horizontalScroll );
254
255protected:
256 /**
257 Reimplement to return an index in the @p model described by the unique key @p key
258 */
259 virtual QModelIndex indexFromConfigString(const QAbstractItemModel *model, const QString &key) const = 0;
260
261 /**
262 Reimplement to return a unique string for the @p index.
263 */
264 virtual QString indexToConfigString(const QModelIndex &index) const = 0;
265
266private:
267 //@cond PRIVATE
268 Q_DECLARE_PRIVATE(KViewStateSaver)
269 KViewStateSaverPrivate * const d_ptr;
270 Q_PRIVATE_SLOT( d_func(), void rowsInserted( const QModelIndex&, int, int ) )
271 Q_PRIVATE_SLOT( d_func(), void restoreScrollBarState() )
272 //@endcond
273};
274
275#endif
276