1/*
2 This file is part of the KDE project
3 SPDX-FileCopyrightText: 2007, 2009 Rafael Fernández López <ereslibre@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#ifndef KCATEGORIZEDVIEW_H
9#define KCATEGORIZEDVIEW_H
10
11#include <QListView>
12#include <memory>
13
14#include <kitemviews_export.h>
15
16class KCategoryDrawer;
17
18/**
19 * @class KCategorizedView kcategorizedview.h KCategorizedView
20 *
21 * @short Item view for listing items in a categorized fashion optionally
22 *
23 * KCategorizedView basically has the same functionality as QListView, only that it also lets you
24 * layout items in a way that they are categorized visually.
25 *
26 * For it to work you will need to set a KCategorizedSortFilterProxyModel and a KCategoryDrawer
27 * with methods setModel() and setCategoryDrawer() respectively. Also, the model will need to be
28 * flagged as categorized with KCategorizedSortFilterProxyModel::setCategorizedModel(true).
29 *
30 * The way it works (if categorization enabled):
31 *
32 * - When sorting, it does more things than QListView does. It will ask the model for the
33 * special role CategorySortRole (@see KCategorizedSortFilterProxyModel). This can return
34 * a QString or an int in order to tell the view the order of categories. In this sense, for
35 * instance, if we are sorting by name ascending, "A" would be before than "B". If we are
36 * sorting by size ascending, 512 bytes would be before 1024 bytes. This way categories are
37 * also sorted.
38 *
39 * - When the view has to paint, it will ask the model with the role CategoryDisplayRole
40 * (@see KCategorizedSortFilterProxyModel). It will for instance return "F" for "foo.pdf" if
41 * we are sorting by name ascending, or "Small" if a certain item has 100 bytes, for example.
42 *
43 * For drawing categories, KCategoryDrawer will be used. You can inherit this class to do your own
44 * drawing.
45 *
46 * @note All examples cited before talk about filesystems and such, but have present that this
47 * is a completely generic class, and it can be used for whatever your purpose is. For
48 * instance when talking about animals, you can separate them by "Mammal" and "Oviparous". In
49 * this very case, for example, the CategorySortRole and the CategoryDisplayRole could be the
50 * same ("Mammal" and "Oviparous").
51 *
52 * @note There is a really performance boost if CategorySortRole returns an int instead of a QString.
53 * Have present that this role is asked (n * log n) times when sorting and compared. Comparing
54 * ints is always faster than comparing strings, without mattering how fast the string
55 * comparison is. Consider thinking of a way of returning ints instead of QStrings if your
56 * model can contain a high number of items.
57 *
58 * @warning Note that for really drawing items in blocks you will need some things to be done:
59 * - The model set to this view has to be (or inherit if you want to do special stuff
60 * in it) KCategorizedSortFilterProxyModel.
61 * - This model needs to be set setCategorizedModel to true.
62 * - Set a category drawer by calling setCategoryDrawer.
63 *
64 * @see KCategorizedSortFilterProxyModel, KCategoryDrawer
65 *
66 * @author Rafael Fernández López <ereslibre@kde.org>
67 */
68class KITEMVIEWS_EXPORT KCategorizedView : public QListView
69{
70 Q_OBJECT
71 Q_PROPERTY(int categorySpacing READ categorySpacing WRITE setCategorySpacing NOTIFY categorySpacingChanged)
72 Q_PROPERTY(bool alternatingBlockColors READ alternatingBlockColors WRITE setAlternatingBlockColors NOTIFY alternatingBlockColorsChanged)
73 Q_PROPERTY(bool collapsibleBlocks READ collapsibleBlocks WRITE setCollapsibleBlocks NOTIFY collapsibleBlocksChanged)
74
75public:
76 KCategorizedView(QWidget *parent = nullptr);
77
78 ~KCategorizedView() override;
79
80 /**
81 * Reimplemented from QAbstractItemView.
82 */
83 void setModel(QAbstractItemModel *model) override;
84
85 /**
86 * Calls to setGridSizeOwn().
87 */
88 void setGridSize(const QSize &size);
89
90 /**
91 * @warning note that setGridSize is not virtual in the base class (QListView), so if you are
92 * calling to this method, make sure you have a KCategorizedView pointer around. This
93 * means that something like:
94 * @code
95 * QListView *lv = new KCategorizedView();
96 * lv->setGridSize(mySize);
97 * @endcode
98 *
99 * will not call to the expected setGridSize method. Instead do something like this:
100 *
101 * @code
102 * QListView *lv;
103 * ...
104 * KCategorizedView *cv = qobject_cast<KCategorizedView*>(lv);
105 * if (cv) {
106 * cv->setGridSizeOwn(mySize);
107 * } else {
108 * lv->setGridSize(mySize);
109 * }
110 * @endcode
111 *
112 * @note this method will call to QListView::setGridSize among other operations.
113 *
114 * @since 4.4
115 */
116 void setGridSizeOwn(const QSize &size);
117
118 /**
119 * Reimplemented from QAbstractItemView.
120 */
121 QRect visualRect(const QModelIndex &index) const override;
122
123 /**
124 * Returns the current category drawer.
125 */
126 KCategoryDrawer *categoryDrawer() const;
127
128 /**
129 * The category drawer that will be used for drawing categories.
130 */
131 void setCategoryDrawer(KCategoryDrawer *categoryDrawer);
132
133 /**
134 * @return Category spacing. The spacing between categories.
135 *
136 * @since 4.4
137 */
138 int categorySpacing() const;
139
140 /**
141 * Stablishes the category spacing. This is the spacing between categories.
142 *
143 * @since 4.4
144 */
145 void setCategorySpacing(int categorySpacing);
146
147 /**
148 * @return Whether blocks should be drawn with alternating colors.
149 *
150 * @since 4.4
151 */
152 bool alternatingBlockColors() const;
153
154 /**
155 * Sets whether blocks should be drawn with alternating colors.
156 *
157 * @since 4.4
158 */
159 void setAlternatingBlockColors(bool enable);
160
161 /**
162 * @return Whether blocks can be collapsed or not.
163 *
164 * @since 4.4
165 */
166 bool collapsibleBlocks() const;
167
168 /**
169 * Sets whether blocks can be collapsed or not.
170 *
171 * @since 4.4
172 */
173 void setCollapsibleBlocks(bool enable);
174
175 /**
176 * @return Block of indexes that are into @p category.
177 *
178 * @since 4.5
179 */
180 QModelIndexList block(const QString &category);
181
182 /**
183 * @return Block of indexes that are represented by @p representative.
184 *
185 * @since 4.5
186 */
187 QModelIndexList block(const QModelIndex &representative);
188
189 /**
190 * Reimplemented from QAbstractItemView.
191 */
192 QModelIndex indexAt(const QPoint &point) const override;
193
194 /**
195 * Reimplemented from QAbstractItemView.
196 */
197 void reset() override;
198
199Q_SIGNALS:
200 void categorySpacingChanged(int spacing);
201 void alternatingBlockColorsChanged(bool enable);
202 void collapsibleBlocksChanged(bool enable);
203
204protected:
205 /**
206 * Reimplemented from QWidget.
207 */
208 void paintEvent(QPaintEvent *event) override;
209
210 /**
211 * Reimplemented from QWidget.
212 */
213 void resizeEvent(QResizeEvent *event) override;
214
215 /**
216 * Reimplemented from QAbstractItemView.
217 */
218 virtual void setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags flags) override;
219
220 /**
221 * Reimplemented from QWidget.
222 */
223 void mouseMoveEvent(QMouseEvent *event) override;
224
225 /**
226 * Reimplemented from QWidget.
227 */
228 void mousePressEvent(QMouseEvent *event) override;
229
230 /**
231 * Reimplemented from QWidget.
232 */
233 void mouseReleaseEvent(QMouseEvent *event) override;
234
235 /**
236 * Reimplemented from QWidget.
237 */
238 void leaveEvent(QEvent *event) override;
239
240 /**
241 * Reimplemented from QAbstractItemView.
242 */
243 void startDrag(Qt::DropActions supportedActions) override;
244
245 /**
246 * Reimplemented from QAbstractItemView.
247 */
248 void dragMoveEvent(QDragMoveEvent *event) override;
249
250 /**
251 * Reimplemented from QAbstractItemView.
252 */
253 void dragEnterEvent(QDragEnterEvent *event) override;
254
255 /**
256 * Reimplemented from QAbstractItemView.
257 */
258 void dragLeaveEvent(QDragLeaveEvent *event) override;
259
260 /**
261 * Reimplemented from QAbstractItemView.
262 */
263 void dropEvent(QDropEvent *event) override;
264
265 /**
266 * Reimplemented from QAbstractItemView.
267 */
268 virtual QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) override;
269
270 /**
271 * Reimplemented from QAbstractItemView.
272 */
273 virtual void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) override;
274
275 /**
276 * Reimplemented from QAbstractItemView.
277 */
278 void updateGeometries() override;
279
280 /**
281 * Reimplemented from QAbstractItemView.
282 */
283 virtual void currentChanged(const QModelIndex &current, const QModelIndex &previous) override;
284
285 /**
286 * Reimplemented from QAbstractItemView.
287 */
288 virtual void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList<int> &roles = QList<int>()) override;
289
290 /**
291 * Reimplemented from QAbstractItemView.
292 */
293 virtual void rowsInserted(const QModelIndex &parent, int start, int end) override;
294
295protected Q_SLOTS:
296 /**
297 * @internal
298 * Reposition items as needed.
299 */
300 virtual void slotLayoutChanged();
301
302private:
303 friend class KCategorizedViewPrivate;
304 std::unique_ptr<class KCategorizedViewPrivate> const d;
305
306 Q_PRIVATE_SLOT(d, void _k_slotCollapseOrExpandClicked(QModelIndex))
307};
308
309#endif // KCATEGORIZEDVIEW_H
310

source code of kitemviews/src/kcategorizedview.h