/*
 * SPDX-FileCopyrightText: 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved.
 *
 * This file is part of the KGantt library.
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#ifndef KGANTTGRAPHICSVIEW_H
#define KGANTTGRAPHICSVIEW_H

#include <QGraphicsView>

#include "kganttglobal.h"

QT_BEGIN_NAMESPACE
class QPrinter;
class QModelIndex;
class QAbstractItemModel;
class QAbstractProxyModel;
class QItemSelectionModel;
QT_END_NAMESPACE

namespace KGantt
{
class AbstractRowController;
class AbstractGrid;
class GraphicsItem;
class ConstraintModel;
class ItemDelegate;
class PrintingContext;

/*!\class KGantt::GraphicsView kganttgraphicsview.h KGanttGraphicsView
 * \ingroup KGantt
 * \brief The GraphicsView class provides a model/view implementation of a gantt chart.
 */
class KGANTT_EXPORT GraphicsView : public QGraphicsView
{
    Q_OBJECT
    KGANTT_DECLARE_PRIVATE_BASE_POLYMORPHIC(GraphicsView)

    Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly)

    Q_PRIVATE_SLOT(d, void slotGridChanged())
    Q_PRIVATE_SLOT(d, void slotHorizontalScrollValueChanged(int))

    Q_PRIVATE_SLOT(d, void slotHeaderContextMenuRequested(const QPoint &))
    /* slots for QAbstractItemModel signals */
    Q_PRIVATE_SLOT(d, void slotColumnsInserted(const QModelIndex &parent, int start, int end))
    Q_PRIVATE_SLOT(d, void slotColumnsRemoved(const QModelIndex &parent, int start, int end))
    Q_PRIVATE_SLOT(d, void slotDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight))
    Q_PRIVATE_SLOT(d, void slotLayoutChanged())
    Q_PRIVATE_SLOT(d, void slotModelReset())
    Q_PRIVATE_SLOT(d, void slotRowsInserted(const QModelIndex &parent, int start, int end))
    Q_PRIVATE_SLOT(d, void slotRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end))
    Q_PRIVATE_SLOT(d, void slotRowsRemoved(const QModelIndex &parent, int start, int end))

    Q_PRIVATE_SLOT(d, void slotItemClicked(const QModelIndex &idx))
    Q_PRIVATE_SLOT(d, void slotItemDoubleClicked(const QModelIndex &idx))
public:
    /*! Constructor. Creates a new KGantt::GraphicsView with parent
     * \a parent.
     */
    explicit GraphicsView(QWidget *parent = nullptr);

    /*! Destroys this view. */
    ~GraphicsView() override;

    /*! \returns the current model displayed by this view
     *
     * Note: The returned model is not the model set with \a setModel()
     *
     * \see GraphicsView::setModel
     */
    QAbstractItemModel *model() const;

    /*! \returns the KGantt::SummaryHandlingProxyModel used by this view.
     */
    QAbstractProxyModel *summaryHandlingModel() const;

    /*! \returns the KGantt::ConstraintModel displayed by this view.
     */
    ConstraintModel *constraintModel() const;

    /*! \returns the rootindex for this view.
     */
    QModelIndex rootIndex() const;

    /*! \returns the QItemSelectionModel used by this view
     */
    QItemSelectionModel *selectionModel() const;

    /*! \returns the AbstractRowController
     * for this view. \see setRowController
     */
    AbstractRowController *rowController() const;

    /*! \returns the AbstractGrid used by this view.
     */
    AbstractGrid *grid() const;

    /*! \returns the AbstractGrid used by this view.
     */
    AbstractGrid *takeGrid();

    /*! \returns the ItemDelegate used by this view to render items
     */
    ItemDelegate *itemDelegate() const;

    /*!\returns true if the view is in read-only mode
     */
    bool isReadOnly() const;

    /*! Sets the context menu policy for the header. The default value
     * Qt::DefaultContextMenu results in a standard context menu on the header
     * that allows the user to set the scale and zoom.
     *
     * Setting this to Qt::CustomContextMenu will cause the signal
     * headerContextMenuRequested(const QPoint& pt) to be emitted instead.
     *
     * \see QWidget::setContextMenuPolicy( Qt::ContextMenuPolicy )
     */
    void setHeaderContextMenuPolicy(Qt::ContextMenuPolicy);

    /*! \returns the context menu policy for the header
     */
    Qt::ContextMenuPolicy headerContextMenuPolicy() const;

    /*!\returns The QModelIndex for the item located at
     * position \a pos in the view or an invalid index
     * if no item was present at that position.
     *
     * This is useful for for example contextmenus.
     */
    QModelIndex indexAt(const QPoint &pos) const;

    /*! Adds a constraint from \a from to \a to. \a modifiers are the
     * keyboard modifiers pressed by the user when the action is invoked.
     *
     * Override this to control how constraints are added. The default
     * implementation adds a soft constraint unless the Shift key is pressed,
     * in that case it adds a hard constraint. If a constraint is already
     * present, it is removed and nothing is added.
     */
    virtual void addConstraint(const QModelIndex &from, const QModelIndex &to, Qt::KeyboardModifiers modifiers);

    /*! \internal */
    void updateRow(const QModelIndex &);

    /*! \internal
     * Resets the state of the view.
     */
    void updateScene();

#if 0
        TODO: For 3.0


        /*! Creates a new GraphicsItem
         * Re-iplement to create your own flavour of GraphicsItem
         */
        virtual GraphicsItem* createItem( ItemType type ) const;
#endif

public Q_SLOTS:
    /*! \internal
     * Adjusts the bounding rectangle of the scene.
     */
    void updateSceneRect();

public:
    /*! \internal */
    void deleteSubtree(const QModelIndex &);

    /*! Print the Gantt chart using \a printer. If \a drawRowLabels
     * is true (the default), each row will have it's label printed
     * on the left side. If \a drawColumnLabels is true (the
     * default), each column will have it's label printed at the
     * top side.
     *
     * This version of print() will print multiple pages.
     */
    void print(QPrinter *printer, bool drawRowLabels = true, bool drawColumnLabels = true);

    /*! Print part of the Gantt chart from \a start to \a end using \a printer.
     * If \a drawRowLabels is true (the default), each row will have it's
     * label printed on the left side. If \a drawColumnLabels is true (the
     * default), each column will have it's label printed at the
     * top side.
     *
     * This version of print() will print multiple pages.
     *
     * To print a certain range of a chart with a DateTimeGrid, use
     * qreal DateTimeGrid::mapFromDateTime( const QDateTime& dt) const
     * to figure out the values for \a start and \a end.
     */
    void print(QPrinter *printer, qreal start, qreal end, bool drawRowLabels = true, bool drawColumnLabels = true);

    /*! Render the GanttView inside the rectangle \a target using the painter \a painter.
     * If \a drawRowLabels is true (the default), each row will have it's
     * label printed on the left side. If \a drawColumnLabels is true (the
     * default), each column will have it's label printed at the
     * top side.
     */
    void print(QPainter *painter, const QRectF &target = QRectF(), bool drawRowLabels = true, bool drawColumnLabels = true);

    /*! Render the GanttView inside the rectangle \a target using the painter \a painter.
     * If \a drawRowLabels is true (the default), each row will have it's
     * label printed on the left side. If \a drawColumnLabels is true (the
     * default), each column will have it's label printed at the
     * top side.
     *
     * To print a certain range of a chart with a DateTimeGrid, use
     * qreal DateTimeGrid::mapFromDateTime( const QDateTime& dt) const
     * to figure out the values for \a start and \a end.
     */
    void print(QPainter *painter, qreal start, qreal end, const QRectF &target = QRectF(), bool drawRowLabels = true, bool drawColumnLabels = true);

    /*! Print the Gantt chart on the \a printer in accordance with the PrintingContext \a context
     *
     * \see PrintingContext
     *
     * \since 2.8.0
     */
    void printDiagram(QPrinter *printer, const PrintingContext &context);

public Q_SLOTS:
    /*! Sets the model to be displayed in this view to
     * \a model. The view does not take ownership of the model.
     *
     * To make a model work well with GraphicsView it must
     * have a certain layout. Whether the model is flat or has a
     * treestrucure is not important, as long as an
     * AbstractRowController is provided that can navigate the
     * model.
     *
     * GraphicsView operates per row in the model. The data is always
     * taken from the _last_ item in the row. The ItemRoles used are
     * Qt::DisplayRole and the roles defined in KGantt::ItemDataRole.
     *
     * Note: This model is not returned by \a model()
     *
     * \see GraphicsView::model
     */
    void setModel(QAbstractItemModel *);
    void setSummaryHandlingModel(QAbstractProxyModel *model);

    /*! Sets the constraintmodel displayed by this view.
     * \see KGantt::ConstraintModel.
     */
    void setConstraintModel(KGantt::ConstraintModel *);

    /*! Sets the root index of the model displayed by this view.
     * Similar to QAbstractItemView::setRootIndex, default is QModelIndex().
     */
    void setRootIndex(const QModelIndex &);

    /*! Sets the QItemSelectionModel used by this view to manage
     * selections. Similar to QAbstractItemView::setSelectionModel
     */
    void setSelectionModel(QItemSelectionModel *);

    /*! Sets the AbstractRowController used by this view. The
     * AbstractRowController deals with the height and position
     * of each row and with which parts of the model are
     * displayed. \see AbstractRowController
     */
    void setRowController(KGantt::AbstractRowController *);

    /*! Sets the AbstractGrid for this view. The grid is an
     * object that controls how QModelIndexes are mapped
     * to and from the view and how the background and header
     * is rendered. \see AbstractGrid and DateTimeGrid.
     */
    void setGrid(KGantt::AbstractGrid *);

    /*! Sets the KGantt::ItemDelegate used for rendering items on this
     * view. \see ItemDelegate and QAbstractItemDelegate.
     */
    void setItemDelegate(KGantt::ItemDelegate *delegate);

    /*! Sets the view to read-only mode if \a to is true. The default is
     * read/write if the model permits it.
     */
    void setReadOnly(bool);

Q_SIGNALS:
    /*! \fn void GraphicsView::activated( const QModelIndex & index ) */
    void activated(const QModelIndex &index);

    /*! \fn void GraphicsView::clicked( const QModelIndex & index ); */
    void clicked(const QModelIndex &index);

    /*! \fn void GraphicsView::qrealClicked( const QModelIndex & index ); */
    void qrealClicked(const QModelIndex &index);

    /*! \fn void GraphicsView::entered( const QModelIndex & index ); */
    void entered(const QModelIndex &index);

    /*! \fn void GraphicsView::pressed( const QModelIndex & index ); */
    void pressed(const QModelIndex &index);

    /*! \fn void GraphicsView::headerContextMenuRequested( const QPoint& pt )
     * This signal is emitted when the header has contextMenuPolicy Qt::CustomContextMenu
     * and the widget wants to show a context menu for the header. Unlike in
     * QWidget::customContextMenuRequested() signal, \a pt is here in global coordinates.
     */
    void headerContextMenuRequested(const QPoint &pt);

protected:
    /*! \internal */
    void clearItems();
    /*reimp*/ void resizeEvent(QResizeEvent *) override;

private:
    friend class View;
};
}

#endif /* KGANTTGRAPHICSVIEW_H */
