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 | |
27 | class QItemSelectionModel; |
28 | |
29 | class 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 | */ |
86 | class KDEUI_EXPORT KSelectionProxyModel : public QAbstractProxyModel |
87 | { |
88 | Q_OBJECT |
89 | public: |
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 (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 | |
260 | Q_SIGNALS: |
261 | #if !defined(Q_MOC_RUN) && !defined(DOXYGEN_SHOULD_SKIP_THIS) && !defined(IN_IDE_PARSER) |
262 | private: // 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 | |
295 | protected: |
296 | QList<QPersistentModelIndex> sourceRootIndexes() const; |
297 | |
298 | private: |
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 | |