File indexing completed on 2024-04-14 03:54:01

0001 /*
0002     This file is part of the KDE project
0003     SPDX-FileCopyrightText: 2007, 2009 Rafael Fernández López <ereslibre@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #ifndef KCATEGORIZEDVIEW_H
0009 #define KCATEGORIZEDVIEW_H
0010 
0011 #include <QListView>
0012 #include <memory>
0013 
0014 #include <kitemviews_export.h>
0015 
0016 class KCategoryDrawer;
0017 
0018 /**
0019  * @class KCategorizedView kcategorizedview.h KCategorizedView
0020  *
0021  * @short Item view for listing items in a categorized fashion optionally
0022  *
0023  * KCategorizedView basically has the same functionality as QListView, only that it also lets you
0024  * layout items in a way that they are categorized visually.
0025  *
0026  * For it to work you will need to set a KCategorizedSortFilterProxyModel and a KCategoryDrawer
0027  * with methods setModel() and setCategoryDrawer() respectively. Also, the model will need to be
0028  * flagged as categorized with KCategorizedSortFilterProxyModel::setCategorizedModel(true).
0029  *
0030  * The way it works (if categorization enabled):
0031  *
0032  *     - When sorting, it does more things than QListView does. It will ask the model for the
0033  *       special role CategorySortRole (@see KCategorizedSortFilterProxyModel). This can return
0034  *       a QString or an int in order to tell the view the order of categories. In this sense, for
0035  *       instance, if we are sorting by name ascending, "A" would be before than "B". If we are
0036  *       sorting by size ascending, 512 bytes would be before 1024 bytes. This way categories are
0037  *       also sorted.
0038  *
0039  *     - When the view has to paint, it will ask the model with the role CategoryDisplayRole
0040  *       (@see KCategorizedSortFilterProxyModel). It will for instance return "F" for "foo.pdf" if
0041  *       we are sorting by name ascending, or "Small" if a certain item has 100 bytes, for example.
0042  *
0043  * For drawing categories, KCategoryDrawer will be used. You can inherit this class to do your own
0044  * drawing.
0045  *
0046  * @note All examples cited before talk about filesystems and such, but have present that this
0047  *       is a completely generic class, and it can be used for whatever your purpose is. For
0048  *       instance when talking about animals, you can separate them by "Mammal" and "Oviparous". In
0049  *       this very case, for example, the CategorySortRole and the CategoryDisplayRole could be the
0050  *       same ("Mammal" and "Oviparous").
0051  *
0052  * @note There is a really performance boost if CategorySortRole returns an int instead of a QString.
0053  *       Have present that this role is asked (n * log n) times when sorting and compared. Comparing
0054  *       ints is always faster than comparing strings, without mattering how fast the string
0055  *       comparison is. Consider thinking of a way of returning ints instead of QStrings if your
0056  *       model can contain a high number of items.
0057  *
0058  * @warning Note that for really drawing items in blocks you will need some things to be done:
0059  *             - The model set to this view has to be (or inherit if you want to do special stuff
0060  *               in it) KCategorizedSortFilterProxyModel.
0061  *             - This model needs to be set setCategorizedModel to true.
0062  *             - Set a category drawer by calling setCategoryDrawer.
0063  *
0064  * @see KCategorizedSortFilterProxyModel, KCategoryDrawer
0065  *
0066  * @author Rafael Fernández López <ereslibre@kde.org>
0067  */
0068 class KITEMVIEWS_EXPORT KCategorizedView : public QListView
0069 {
0070     Q_OBJECT
0071     Q_PROPERTY(int categorySpacing READ categorySpacing WRITE setCategorySpacing NOTIFY categorySpacingChanged)
0072     Q_PROPERTY(bool alternatingBlockColors READ alternatingBlockColors WRITE setAlternatingBlockColors NOTIFY alternatingBlockColorsChanged)
0073     Q_PROPERTY(bool collapsibleBlocks READ collapsibleBlocks WRITE setCollapsibleBlocks NOTIFY collapsibleBlocksChanged)
0074 
0075 public:
0076     KCategorizedView(QWidget *parent = nullptr);
0077 
0078     ~KCategorizedView() override;
0079 
0080     /**
0081      * Reimplemented from QAbstractItemView.
0082      */
0083     void setModel(QAbstractItemModel *model) override;
0084 
0085     /**
0086      * Calls to setGridSizeOwn().
0087      */
0088     void setGridSize(const QSize &size);
0089 
0090     /**
0091      * @warning note that setGridSize is not virtual in the base class (QListView), so if you are
0092      *          calling to this method, make sure you have a KCategorizedView pointer around. This
0093      *          means that something like:
0094      * @code
0095      *     QListView *lv = new KCategorizedView();
0096      *     lv->setGridSize(mySize);
0097      * @endcode
0098      *
0099      * will not call to the expected setGridSize method. Instead do something like this:
0100      *
0101      * @code
0102      *     QListView *lv;
0103      *     ...
0104      *     KCategorizedView *cv = qobject_cast<KCategorizedView*>(lv);
0105      *     if (cv) {
0106      *         cv->setGridSizeOwn(mySize);
0107      *     } else {
0108      *         lv->setGridSize(mySize);
0109      *     }
0110      * @endcode
0111      *
0112      * @note this method will call to QListView::setGridSize among other operations.
0113      *
0114      * @since 4.4
0115      */
0116     void setGridSizeOwn(const QSize &size);
0117 
0118     /**
0119      * Reimplemented from QAbstractItemView.
0120      */
0121     QRect visualRect(const QModelIndex &index) const override;
0122 
0123     /**
0124      * Returns the current category drawer.
0125      */
0126     KCategoryDrawer *categoryDrawer() const;
0127 
0128     /**
0129      * The category drawer that will be used for drawing categories.
0130      */
0131     void setCategoryDrawer(KCategoryDrawer *categoryDrawer);
0132 
0133     /**
0134      * @return Category spacing. The spacing between categories.
0135      *
0136      * @since 4.4
0137      */
0138     int categorySpacing() const;
0139 
0140     /**
0141      * Stablishes the category spacing. This is the spacing between categories.
0142      *
0143      * @since 4.4
0144      */
0145     void setCategorySpacing(int categorySpacing);
0146 
0147     /**
0148      * @return Whether blocks should be drawn with alternating colors.
0149      *
0150      * @since 4.4
0151      */
0152     bool alternatingBlockColors() const;
0153 
0154     /**
0155      * Sets whether blocks should be drawn with alternating colors.
0156      *
0157      * @since 4.4
0158      */
0159     void setAlternatingBlockColors(bool enable);
0160 
0161     /**
0162      * @return Whether blocks can be collapsed or not.
0163      *
0164      * @since 4.4
0165      */
0166     bool collapsibleBlocks() const;
0167 
0168     /**
0169      * Sets whether blocks can be collapsed or not.
0170      *
0171      * @since 4.4
0172      */
0173     void setCollapsibleBlocks(bool enable);
0174 
0175     /**
0176      * @return Block of indexes that are into @p category.
0177      *
0178      * @since 4.5
0179      */
0180     QModelIndexList block(const QString &category);
0181 
0182     /**
0183      * @return Block of indexes that are represented by @p representative.
0184      *
0185      * @since 4.5
0186      */
0187     QModelIndexList block(const QModelIndex &representative);
0188 
0189     /**
0190      * Reimplemented from QAbstractItemView.
0191      */
0192     QModelIndex indexAt(const QPoint &point) const override;
0193 
0194     /**
0195      * Reimplemented from QAbstractItemView.
0196      */
0197     void reset() override;
0198 
0199 Q_SIGNALS:
0200     void categorySpacingChanged(int spacing);
0201     void alternatingBlockColorsChanged(bool enable);
0202     void collapsibleBlocksChanged(bool enable);
0203 
0204 protected:
0205     /**
0206      * Reimplemented from QWidget.
0207      */
0208     void paintEvent(QPaintEvent *event) override;
0209 
0210     /**
0211      * Reimplemented from QWidget.
0212      */
0213     void resizeEvent(QResizeEvent *event) override;
0214 
0215     /**
0216      * Reimplemented from QAbstractItemView.
0217      */
0218     virtual void setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags flags) override;
0219 
0220     /**
0221      * Reimplemented from QWidget.
0222      */
0223     void mouseMoveEvent(QMouseEvent *event) override;
0224 
0225     /**
0226      * Reimplemented from QWidget.
0227      */
0228     void mousePressEvent(QMouseEvent *event) override;
0229 
0230     /**
0231      * Reimplemented from QWidget.
0232      */
0233     void mouseReleaseEvent(QMouseEvent *event) override;
0234 
0235     /**
0236      * Reimplemented from QWidget.
0237      */
0238     void leaveEvent(QEvent *event) override;
0239 
0240     /**
0241      * Reimplemented from QAbstractItemView.
0242      */
0243     void startDrag(Qt::DropActions supportedActions) override;
0244 
0245     /**
0246      * Reimplemented from QAbstractItemView.
0247      */
0248     void dragMoveEvent(QDragMoveEvent *event) override;
0249 
0250     /**
0251      * Reimplemented from QAbstractItemView.
0252      */
0253     void dragEnterEvent(QDragEnterEvent *event) override;
0254 
0255     /**
0256      * Reimplemented from QAbstractItemView.
0257      */
0258     void dragLeaveEvent(QDragLeaveEvent *event) override;
0259 
0260     /**
0261      * Reimplemented from QAbstractItemView.
0262      */
0263     void dropEvent(QDropEvent *event) override;
0264 
0265     /**
0266      * Reimplemented from QAbstractItemView.
0267      */
0268     virtual QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) override;
0269 
0270     /**
0271      * Reimplemented from QAbstractItemView.
0272      */
0273     virtual void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) override;
0274 
0275     /**
0276      * Reimplemented from QAbstractItemView.
0277      */
0278     void updateGeometries() override;
0279 
0280     /**
0281      * Reimplemented from QAbstractItemView.
0282      */
0283     virtual void currentChanged(const QModelIndex &current, const QModelIndex &previous) override;
0284 
0285     /**
0286      * Reimplemented from QAbstractItemView.
0287      */
0288     virtual void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList<int> &roles = QList<int>()) override;
0289 
0290     /**
0291      * Reimplemented from QAbstractItemView.
0292      */
0293     virtual void rowsInserted(const QModelIndex &parent, int start, int end) override;
0294 
0295 protected Q_SLOTS:
0296     /**
0297      * @internal
0298      * Reposition items as needed.
0299      */
0300     virtual void slotLayoutChanged();
0301 
0302 private:
0303     friend class KCategorizedViewPrivate;
0304     std::unique_ptr<class KCategorizedViewPrivate> const d;
0305 
0306     Q_PRIVATE_SLOT(d, void _k_slotCollapseOrExpandClicked(QModelIndex))
0307 };
0308 
0309 #endif // KCATEGORIZEDVIEW_H