/**
  * This file is part of the KDE project
  * Copyright (C) 2007, 2009 Rafael Fernández López <ereslibre@kde.org>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
  * License as published by the Free Software Foundation; either
  * version 2 of the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Library General Public License for more details.
  *
  * You should have received a copy of the GNU Library General Public License
  * along with this library; see the file COPYING.LIB.  If not, write to
  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  * Boston, MA 02110-1301, USA.
  */

#ifndef KCATEGORIZEDVIEW_H
#define KCATEGORIZEDVIEW_H

#include <QListView>

//#include <kitemviews_export.h>

class KCategoryDrawer;

/**
  * @class KCategorizedView kcategorizedview.h KCategorizedView
  *
  * @short Item view for listing items in a categorized fashion optionally
  *
  * KCategorizedView basically has the same functionality as QListView, only that it also lets you
  * layout items in a way that they are categorized visually.
  *
  * For it to work you will need to set a KCategorizedSortFilterProxyModel and a KCategoryDrawer
  * with methods setModel() and setCategoryDrawer() respectively. Also, the model will need to be
  * flagged as categorized with KCategorizedSortFilterProxyModel::setCategorizedModel(true).
  *
  * The way it works (if categorization enabled):
  *
  *     - When sorting, it does more things than QListView does. It will ask the model for the
  *       special role CategorySortRole (@see KCategorizedSortFilterProxyModel). This can return
  *       a QString or an int in order to tell the view the order of categories. In this sense, for
  *       instance, if we are sorting by name ascending, "A" would be before than "B". If we are
  *       sorting by size ascending, 512 bytes would be before 1024 bytes. This way categories are
  *       also sorted.
  *
  *     - When the view has to paint, it will ask the model with the role CategoryDisplayRole
  *       (@see KCategorizedSortFilterProxyModel). It will for instance return "F" for "foo.pdf" if
  *       we are sorting by name ascending, or "Small" if a certain item has 100 bytes, for example.
  *
  * For drawing categories, KCategoryDrawer will be used. You can inherit this class to do your own
  * drawing.
  *
  * @note All examples cited before talk about filesystems and such, but have present that this
  *       is a completely generic class, and it can be used for whatever your purpose is. For
  *       instance when talking about animals, you can separate them by "Mammal" and "Oviparous". In
  *       this very case, for example, the CategorySortRole and the CategoryDisplayRole could be the
  *       same ("Mammal" and "Oviparous").
  *
  * @note There is a really performance boost if CategorySortRole returns an int instead of a QString.
  *       Have present that this role is asked (n * log n) times when sorting and compared. Comparing
  *       ints is always faster than comparing strings, whithout mattering how fast the string
  *       comparison is. Consider thinking of a way of returning ints instead of QStrings if your
  *       model can contain a high number of items.
  *
  * @warning Note that for really drawing items in blocks you will need some things to be done:
  *             - The model set to this view has to be (or inherit if you want to do special stuff
  *               in it) KCategorizedSortFilterProxyModel.
  *             - This model needs to be set setCategorizedModel to true.
  *             - Set a category drawer by calling setCategoryDrawer.
  *
  * @see KCategorizedSortFilterProxyModel, KCategoryDrawer
  *
  * @author Rafael Fernández López <ereslibre@kde.org>
  */
class /*KITEMVIEWS_EXPORT*/ KCategorizedView
	: public QListView {
	Q_OBJECT
	Q_PROPERTY(int categorySpacing READ categorySpacing WRITE setCategorySpacing)
	Q_PROPERTY(bool alternatingBlockColors READ alternatingBlockColors WRITE setAlternatingBlockColors)
	Q_PROPERTY(bool collapsibleBlocks READ collapsibleBlocks WRITE setCollapsibleBlocks)

public:
	KCategorizedView(QWidget* parent = nullptr);

	~KCategorizedView();

	/**
      * Reimplemented from QAbstractItemView.
      */
	void setModel(QAbstractItemModel* model) Q_DECL_OVERRIDE;

	/**
      * Calls to setGridSizeOwn().
      */
	void setGridSize(const QSize& size);

	/**
      * @warning note that setGridSize is not virtual in the base class (QListView), so if you are
      *          calling to this method, make sure you have a KCategorizedView pointer around. This
      *          means that something like:
      * @code
      *     QListView *lv = new KCategorizedView();
      *     lv->setGridSize(mySize);
      * @endcode
      *
      * will not call to the expected setGridSize method. Instead do something like this:
      *
      * @code
      *     QListView *lv;
      *     ...
      *     KCategorizedView *cv = qobject_cast<KCategorizedView*>(lv);
      *     if (cv) {
      *         cv->setGridSizeOwn(mySize);
      *     } else {
      *         lv->setGridSize(mySize);
      *     }
      * @endcode
      *
      * @note this method will call to QListView::setGridSize among other operations.
      *
      * @since 4.4
      */
	void setGridSizeOwn(const QSize& size);

	/**
      * Reimplemented from QAbstractItemView.
      */
	QRect visualRect(const QModelIndex& index) const Q_DECL_OVERRIDE;

	/**
      * Returns the current category drawer.
      */
	KCategoryDrawer* categoryDrawer() const;

	/**
      * The category drawer that will be used for drawing categories.
      */
	void setCategoryDrawer(KCategoryDrawer* categoryDrawer);

	/**
      * @return Category spacing. The spacing between categories.
      *
      * @since 4.4
      */
	int categorySpacing() const;

	/**
      * Stablishes the category spacing. This is the spacing between categories.
      *
      * @since 4.4
      */
	void setCategorySpacing(int categorySpacing);

	/**
      * @return Whether blocks should be drawn with alternating colors.
      *
      * @since 4.4
      */
	bool alternatingBlockColors() const;

	/**
      * Sets whether blocks should be drawn with alternating colors.
      *
      * @since 4.4
      */
	void setAlternatingBlockColors(bool enable);

	/**
      * @return Whether blocks can be collapsed or not.
      *
      * @since 4.4
      */
	bool collapsibleBlocks() const;

	/**
      * Sets whether blocks can be collapsed or not.
      *
      * @since 4.4
      */
	void setCollapsibleBlocks(bool enable);

	/**
      * @return Block of indexes that are into @p category.
      *
      * @since 4.5
      */
	QModelIndexList block(const QString& category);

	/**
      * @return Block of indexes that are represented by @p representative.
      *
      * @since 4.5
      */
	QModelIndexList block(const QModelIndex& representative);

	/**
      * Reimplemented from QAbstractItemView.
      */
	QModelIndex indexAt(const QPoint& point) const Q_DECL_OVERRIDE;

	/**
      * Reimplemented from QAbstractItemView.
      */
	void reset() Q_DECL_OVERRIDE;

protected:
	/**
      * Reimplemented from QWidget.
      */
	void paintEvent(QPaintEvent* event) Q_DECL_OVERRIDE;

	/**
      * Reimplemented from QWidget.
      */
	void resizeEvent(QResizeEvent* event) Q_DECL_OVERRIDE;

	/**
      * Reimplemented from QAbstractItemView.
      */
	virtual void setSelection(const QRect& rect,
	                          QItemSelectionModel::SelectionFlags flags) Q_DECL_OVERRIDE;

	/**
      * Reimplemented from QWidget.
      */
	void mouseMoveEvent(QMouseEvent* event) Q_DECL_OVERRIDE;

	/**
      * Reimplemented from QWidget.
      */
	void mousePressEvent(QMouseEvent* event) Q_DECL_OVERRIDE;

	/**
      * Reimplemented from QWidget.
      */
	void mouseReleaseEvent(QMouseEvent* event) Q_DECL_OVERRIDE;

	/**
      * Reimplemented from QWidget.
      */
	void leaveEvent(QEvent* event) Q_DECL_OVERRIDE;

	/**
      * Reimplemented from QAbstractItemView.
      */
	void startDrag(Qt::DropActions supportedActions) Q_DECL_OVERRIDE;

	/**
      * Reimplemented from QAbstractItemView.
      */
	void dragMoveEvent(QDragMoveEvent* event) Q_DECL_OVERRIDE;

	/**
      * Reimplemented from QAbstractItemView.
      */
	void dragEnterEvent(QDragEnterEvent* event) Q_DECL_OVERRIDE;

	/**
      * Reimplemented from QAbstractItemView.
      */
	void dragLeaveEvent(QDragLeaveEvent* event) Q_DECL_OVERRIDE;

	/**
      * Reimplemented from QAbstractItemView.
      */
	void dropEvent(QDropEvent* event) Q_DECL_OVERRIDE;

	/**
      * Reimplemented from QAbstractItemView.
      */
	virtual QModelIndex moveCursor(CursorAction cursorAction,
	                               Qt::KeyboardModifiers modifiers) Q_DECL_OVERRIDE;

	/**
      * Reimplemented from QAbstractItemView.
      */
	virtual void rowsAboutToBeRemoved(const QModelIndex& parent,
	                                  int start,
	                                  int end) Q_DECL_OVERRIDE;

	/**
      * Reimplemented from QAbstractItemView.
      */
	void updateGeometries() Q_DECL_OVERRIDE;

	/**
      * Reimplemented from QAbstractItemView.
      */
	virtual void currentChanged(const QModelIndex& current,
	                            const QModelIndex& previous) Q_DECL_OVERRIDE;

	/**
      * Reimplemented from QAbstractItemView.
      */
	virtual void dataChanged(const QModelIndex& topLeft,
	                         const QModelIndex& bottomRight,
	                         const QVector<int>& roles = QVector<int>()) Q_DECL_OVERRIDE;

	/**
      * Reimplemented from QAbstractItemView.
      */
	virtual void rowsInserted(const QModelIndex& parent,
	                          int start,
	                          int end) Q_DECL_OVERRIDE;

protected Q_SLOTS:
	/**
      * @internal
      * @warning Deprecated since 4.4.
      */
	/*
#ifndef KITEMVIEWS_NO_DEPRECATED
    virtual KITEMVIEWS_DEPRECATED void rowsInsertedArtifficial(const QModelIndex &parent,
            int start,
            int end);
#endif
*/

	/**
      * @internal
      * @warning Deprecated since 4.4.
      */
	/*
#ifndef KITEMVIEWS_NO_DEPRECATED
    virtual KITEMVIEWS_DEPRECATED void rowsRemoved(const QModelIndex &parent,
            int start,
            int end);
#endif
*/
	/**
      * @internal
      * Reposition items as needed.
      */
	virtual void slotLayoutChanged();

private:
	class Private;
	Private* const d;

	Q_PRIVATE_SLOT(d, void _k_slotCollapseOrExpandClicked(QModelIndex))
};

#endif// KCATEGORIZEDVIEW_H
