1/*
2 Copyright (c) 2009 Stephen Kelly <steveire@gmail.com>
3
4 This library is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Library General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or (at your
7 option) any later version.
8
9 This library is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12 License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to the
16 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 02110-1301, USA.
18*/
19
20#ifndef KSELECTIONPROXYMODEL_H
21#define KSELECTIONPROXYMODEL_H
22
23#include <QtGui/QAbstractProxyModel>
24
25#include <kdeui_export.h>
26
27class QItemSelectionModel;
28
29class KSelectionProxyModelPrivate;
30
31/**
32 @brief A Proxy Model which presents a subset of its source model to observers.
33
34 The KSelectionProxyModel is most useful as a convenience for displaying the selection in one view in
35 another view. The selectionModel of the initial view is used to create a proxied model which is filtered
36 based on the configuration of this class.
37
38 For example, when a user clicks a mail folder in one view in an email application, the contained emails
39 should be displayed in another view.
40
41 This takes away the need for the developer to handle the selection between the views, including all the
42 mapToSource, mapFromSource and setRootIndex calls.
43
44 @code
45 MyModel *sourceModel = new MyModel(this);
46 QTreeView *leftView = new QTreeView(this);
47 leftView->setModel(sourceModel);
48
49 KSelectionProxyModel *selectionProxy = new KSelectionProxyModel(leftView->selectionModel(), this);
50 selectionProxy->setSourceModel(sourceModel);
51
52 QTreeView *rightView = new QTreeView(this);
53 rightView->setModel(selectionProxy);
54 @endcode
55
56 \image html selectionproxymodelsimpleselection.png "A Selection in one view creating a model for use with another view."
57
58 The KSelectionProxyModel can handle complex selections.
59
60 \image html selectionproxymodelmultipleselection.png "Non-contiguous selection creating a new simple model in a second view."
61
62 The contents of the secondary view depends on the selection in the primary view, and the configuration of the proxy model.
63 See KSelectionProxyModel::setFilterBehavior for the different possible configurations.
64
65 For example, if the filterBehavior is SubTrees, selecting another item in an already selected subtree has no effect.
66
67 \image html selectionproxymodelmultipleselection-withdescendant.png "Selecting an item and its descendant."
68
69 See the test application in KDE/kdelibs/kdeui/tests/proxymodeltestapp to try out the valid configurations.
70
71 \image html kselectionproxymodel-testapp.png "KSelectionProxyModel test application"
72
73 Obviously, the KSelectionProxyModel may be used in a view, or further processed with other proxy models.
74 See KAddressBook and AkonadiConsole in kdepim for examples which use a further KDescendantsProxyModel
75 and QSortFilterProxyModel on top of a KSelectionProxyModel.
76
77 Additionally, this class can be used to programmatically choose some items from the source model to display in the view. For example,
78 this is how the Favourite Folder View in KMail works, and is also used in unit testing.
79
80 See also: http://doc.trolltech.com/4.5/model-view-proxy-models.html
81
82 @since 4.4
83 @author Stephen Kelly <steveire@gmail.com>
84
85*/
86class KDEUI_EXPORT KSelectionProxyModel : public QAbstractProxyModel
87{
88 Q_OBJECT
89public:
90 /**
91 ctor.
92
93 @p selectionModel The selection model used to filter what is presented by the proxy.
94 */
95
96 explicit KSelectionProxyModel(QItemSelectionModel *selectionModel, QObject *parent = 0);
97
98 /**
99 dtor
100 */
101 virtual ~KSelectionProxyModel();
102
103 /**
104 reimp.
105 */
106 virtual void setSourceModel(QAbstractItemModel * sourceModel);
107
108 QItemSelectionModel *selectionModel() const;
109
110 enum FilterBehavior {
111 SubTrees,
112 SubTreeRoots,
113 SubTreesWithoutRoots,
114 ExactSelection,
115 ChildrenOfExactSelection
116 };
117 Q_ENUMS(FilterBehavior)
118
119 /**
120 Set the filter behaviors of this model.
121 The filter behaviors of the model govern the content of the model based on the selection of the contained QItemSelectionModel.
122
123 See kdeui/proxymodeltestapp to try out the different proxy model behaviors.
124
125 The most useful behaviors are SubTrees, ExactSelection and ChildrenOfExactSelection.
126
127 The default behavior is SubTrees. This means that this proxy model will contain the roots of the items in the source model.
128 Any descendants which are also selected have no additional effect.
129 For example if the source model is like:
130
131 @verbatim
132 (root)
133 - A
134 - B
135 - C
136 - D
137 - E
138 - F
139 - G
140 - H
141 - I
142 - J
143 - K
144 - L
145 @endverbatim
146
147 And A, B, C and D are selected, the proxy will contain:
148
149 @verbatim
150 (root)
151 - A
152 - B
153 - C
154 - D
155 - E
156 - F
157 - G
158 @endverbatim
159
160 That is, selecting 'D' or 'C' if 'B' is also selected has no effect. If 'B' is de-selected, then 'C' amd 'D' become top-level items:
161
162 @verbatim
163 (root)
164 - A
165 - C
166 - D
167 - E
168 - F
169 - G
170 @endverbatim
171
172 This is the behavior used by KJots when rendering books.
173
174 If the behavior is set to SubTreeRoots, then the children of selected indexes are not part of the model. If 'A', 'B' and 'D' are selected,
175
176 @verbatim
177 (root)
178 - A
179 - B
180 @endverbatim
181
182 Note that although 'D' is selected, it is not part of the proxy model, because its parent 'B' is already selected.
183
184 SubTreesWithoutRoots has the effect of not making the selected items part of the model, but making their children part of the model instead. If 'A', 'B' and 'I' are selected:
185
186 @verbatim
187 (root)
188 - C
189 - D
190 - E
191 - F
192 - G
193 - J
194 - K
195 - L
196 @endverbatim
197
198 Note that 'A' has no children, so selecting it has no outward effect on the model.
199
200 ChildrenOfExactSelection causes the proxy model to contain the children of the selected indexes,but further descendants are omitted.
201 Additionally, if descendants of an already selected index are selected, their children are part of the proxy model.
202 For example, if 'A', 'B', 'D' and 'I' are selected:
203
204 @verbatim
205 (root)
206 - C
207 - D
208 - E
209 - G
210 - J
211 - K
212 - L
213 @endverbatim
214
215 This would be useful for example if showing containers (for example maildirs) in one view and their items in another. Sub-maildirs would still appear in the proxy, but
216 could be filtered out using a QSortfilterProxyModel.
217
218 The ExactSelection behavior causes the selected items to be part of the proxy model, even if their ancestors are already selected, but children of selected items are not included.
219
220 Again, if 'A', 'B', 'D' and 'I' are selected:
221
222 @verbatim
223 (root)
224 - A
225 - B
226 - D
227 - I
228 @endverbatim
229
230 This is the behavior used by the Favourite Folder View in KMail.
231
232 */
233 void setFilterBehavior(FilterBehavior behavior);
234 FilterBehavior filterBehavior() const;
235
236 QModelIndex mapFromSource(const QModelIndex & sourceIndex) const;
237 QModelIndex mapToSource(const QModelIndex & proxyIndex) const;
238
239 QItemSelection mapSelectionFromSource(const QItemSelection& selection) const;
240 QItemSelection mapSelectionToSource(const QItemSelection& selection) const;
241
242 virtual Qt::ItemFlags flags(const QModelIndex &index) const;
243 QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
244 virtual int rowCount(const QModelIndex & parent = QModelIndex()) const;
245 virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
246
247 virtual QMimeData* mimeData(const QModelIndexList & indexes) const;
248 virtual QStringList mimeTypes() const;
249 virtual Qt::DropActions supportedDropActions() const;
250 virtual bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent);
251
252 virtual bool hasChildren(const QModelIndex & parent = QModelIndex()) const;
253 virtual QModelIndex index(int, int, const QModelIndex& = QModelIndex()) const;
254 virtual QModelIndex parent(const QModelIndex&) const;
255 virtual int columnCount(const QModelIndex& = QModelIndex()) const;
256
257 virtual QModelIndexList match(const QModelIndex& start, int role, const QVariant& value, int hits = 1,
258 Qt::MatchFlags flags = Qt::MatchFlags(Qt::MatchStartsWith | Qt::MatchWrap)) const;
259
260Q_SIGNALS:
261#if !defined(Q_MOC_RUN) && !defined(DOXYGEN_SHOULD_SKIP_THIS) && !defined(IN_IDE_PARSER)
262private: // Don't allow subclasses to emit these signals.
263#endif
264
265 /**
266 @internal
267 Emitted before @p removeRootIndex, an index in the sourceModel is removed from
268 the root selected indexes. This may be unrelated to rows removed from the model,
269 depending on configuration.
270 */
271 void rootIndexAboutToBeRemoved(const QModelIndex &removeRootIndex);
272
273 /**
274 @internal
275 Emitted when @p newIndex, an index in the sourceModel is added to the root selected
276 indexes. This may be unrelated to rows inserted to the model,
277 depending on configuration.
278 */
279 void rootIndexAdded(const QModelIndex &newIndex);
280
281 /**
282 @internal
283 Emitted before @p selection, a selection in the sourceModel, is removed from
284 the root selection.
285 */
286 void rootSelectionAboutToBeRemoved(const QItemSelection &selection);
287
288 /**
289 @internal
290 Emitted after @p selection, a selection in the sourceModel, is added to
291 the root selection.
292 */
293 void rootSelectionAdded(const QItemSelection &selection);
294
295protected:
296 QList<QPersistentModelIndex> sourceRootIndexes() const;
297
298private:
299 Q_DECLARE_PRIVATE(KSelectionProxyModel)
300 //@cond PRIVATE
301 KSelectionProxyModelPrivate *d_ptr;
302
303 Q_PRIVATE_SLOT(d_func(), void sourceRowsAboutToBeInserted(const QModelIndex &, int, int))
304 Q_PRIVATE_SLOT(d_func(), void sourceRowsInserted(const QModelIndex &, int, int))
305 Q_PRIVATE_SLOT(d_func(), void sourceRowsAboutToBeRemoved(const QModelIndex &, int, int))
306 Q_PRIVATE_SLOT(d_func(), void sourceRowsRemoved(const QModelIndex &, int, int))
307 Q_PRIVATE_SLOT(d_func(), void sourceRowsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int))
308 Q_PRIVATE_SLOT(d_func(), void sourceRowsMoved(const QModelIndex &, int, int, const QModelIndex &, int))
309 Q_PRIVATE_SLOT(d_func(), void sourceModelAboutToBeReset())
310 Q_PRIVATE_SLOT(d_func(), void sourceModelReset())
311 Q_PRIVATE_SLOT(d_func(), void sourceLayoutAboutToBeChanged())
312 Q_PRIVATE_SLOT(d_func(), void sourceLayoutChanged())
313 Q_PRIVATE_SLOT(d_func(), void sourceDataChanged(const QModelIndex &, const QModelIndex &))
314 Q_PRIVATE_SLOT(d_func(), void selectionChanged(const QItemSelection & selected, const QItemSelection & deselected))
315 Q_PRIVATE_SLOT(d_func(), void sourceModelDestroyed())
316
317 //@endcond
318
319};
320
321#endif
322