/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
 * Qwt Widget Library
 * Copyright (C) 1997   Josef Wilgen
 * Copyright (C) 2002   Uwe Rathmann
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the Qwt License, Version 1.0
 *****************************************************************************/

#include "qwt_arrow_button.h"
#include "qwt_math.h"
#include <qpainter.h>
#include <qstyle.h>
#include <qstyleoption.h>
#include <qevent.h>
#include <qapplication.h>

static const int MaxNum = 3;
static const int Margin = 2;
static const int Spacing = 1;

class QwtArrowButton::PrivateData
{
public:
    int num;
    Qt::ArrowType arrowType;
};

static QStyleOptionButton styleOpt( const QwtArrowButton* btn )
{
    QStyleOptionButton option;
    option.init( btn );
    option.features = QStyleOptionButton::None;
    if ( btn->isFlat() )
        option.features |= QStyleOptionButton::Flat;
    if ( btn->menu() )
        option.features |= QStyleOptionButton::HasMenu;
    if ( btn->autoDefault() || btn->isDefault() )
        option.features |= QStyleOptionButton::AutoDefaultButton;
    if ( btn->isDefault() )
        option.features |= QStyleOptionButton::DefaultButton;
    if ( btn->isDown() )
        option.state |= QStyle::State_Sunken;
    if ( !btn->isFlat() && !btn->isDown() )
        option.state |= QStyle::State_Raised;

    return option;
}

/*!
  \param num Number of arrows
  \param arrowType see Qt::ArowType in the Qt docs.
  \param parent Parent widget
*/
QwtArrowButton::QwtArrowButton( int num,
        Qt::ArrowType arrowType, QWidget *parent ):
    QPushButton( parent )
{
    d_data = new PrivateData;
    d_data->num = qBound( 1, num, MaxNum );
    d_data->arrowType = arrowType;

    setAutoRepeat( true );
    setAutoDefault( false );

    switch ( d_data->arrowType )
    {
        case Qt::LeftArrow:
        case Qt::RightArrow:
            setSizePolicy( QSizePolicy::Expanding,
                QSizePolicy::Fixed );
            break;
        default:
            setSizePolicy( QSizePolicy::Fixed,
                QSizePolicy::Expanding );
    }
}

//! Destructor
QwtArrowButton::~QwtArrowButton()
{
    delete d_data;
    d_data = NULL;
}

/*!
  \brief The direction of the arrows
*/
Qt::ArrowType QwtArrowButton::arrowType() const
{
    return d_data->arrowType;
}

/*!
  \brief The number of arrows
*/
int QwtArrowButton::num() const
{
    return d_data->num;
}

/*!
  \return the bounding rect for the label
*/
QRect QwtArrowButton::labelRect() const
{
    const int m = Margin;

    QRect r = rect();
    r.setRect( r.x() + m, r.y() + m,
        r.width() - 2 * m, r.height() - 2 * m );

    if ( isDown() )
    {
        QStyleOptionButton option = styleOpt( this );
        const int ph = style()->pixelMetric(
            QStyle::PM_ButtonShiftHorizontal, &option, this );
        const int pv = style()->pixelMetric(
            QStyle::PM_ButtonShiftVertical, &option, this );

        r.translate( ph, pv );
    }

    return r;
}

/*!
   Paint event handler
   \param event Paint event
*/
void QwtArrowButton::paintEvent( QPaintEvent *event )
{
    QPushButton::paintEvent( event );
    QPainter painter( this );
    drawButtonLabel( &painter );
}

/*!
  \brief Draw the button label

  \param painter Painter
  \sa The Qt Manual on QPushButton
*/
void QwtArrowButton::drawButtonLabel( QPainter *painter )
{
    const bool isVertical = d_data->arrowType == Qt::UpArrow ||
        d_data->arrowType == Qt::DownArrow;

    const QRect r = labelRect();
    QSize boundingSize = labelRect().size();
    if ( isVertical )
        boundingSize.transpose();

    const int w =
        ( boundingSize.width() - ( MaxNum - 1 ) * Spacing ) / MaxNum;

    QSize arrow = arrowSize( Qt::RightArrow,
        QSize( w, boundingSize.height() ) );

    if ( isVertical )
        arrow.transpose();

    QRect contentsSize; // aligned rect where to paint all arrows
    if ( d_data->arrowType == Qt::LeftArrow || d_data->arrowType == Qt::RightArrow )
    {
        contentsSize.setWidth( d_data->num * arrow.width()
            + ( d_data->num - 1 ) * Spacing );
        contentsSize.setHeight( arrow.height() );
    }
    else
    {
        contentsSize.setWidth( arrow.width() );
        contentsSize.setHeight( d_data->num * arrow.height()
            + ( d_data->num - 1 ) * Spacing );
    }

    QRect arrowRect( contentsSize );
    arrowRect.moveCenter( r.center() );
    arrowRect.setSize( arrow );

    painter->save();
    for ( int i = 0; i < d_data->num; i++ )
    {
        drawArrow( painter, arrowRect, d_data->arrowType );

        int dx = 0;
        int dy = 0;

        if ( isVertical )
            dy = arrow.height() + Spacing;
        else
            dx = arrow.width() + Spacing;

        arrowRect.translate( dx, dy );
    }
    painter->restore();

    if ( hasFocus() )
    {
        QStyleOptionFocusRect option;
        option.init( this );
        option.backgroundColor = palette().color( QPalette::Window );

        style()->drawPrimitive( QStyle::PE_FrameFocusRect,
            &option, painter, this );
    }
}

/*!
    Draw an arrow int a bounding rect

    \param painter Painter
    \param r Rectangle where to paint the arrow
    \param arrowType Arrow type
*/
void QwtArrowButton::drawArrow( QPainter *painter,
    const QRect &r, Qt::ArrowType arrowType ) const
{
    QPolygon pa( 3 );

    switch ( arrowType )
    {
        case Qt::UpArrow:
            pa.setPoint( 0, r.bottomLeft() );
            pa.setPoint( 1, r.bottomRight() );
            pa.setPoint( 2, r.center().x(), r.top() );
            break;
        case Qt::DownArrow:
            pa.setPoint( 0, r.topLeft() );
            pa.setPoint( 1, r.topRight() );
            pa.setPoint( 2, r.center().x(), r.bottom() );
            break;
        case Qt::RightArrow:
            pa.setPoint( 0, r.topLeft() );
            pa.setPoint( 1, r.bottomLeft() );
            pa.setPoint( 2, r.right(), r.center().y() );
            break;
        case Qt::LeftArrow:
            pa.setPoint( 0, r.topRight() );
            pa.setPoint( 1, r.bottomRight() );
            pa.setPoint( 2, r.left(), r.center().y() );
            break;
        default:
            break;
    }

    painter->save();

    painter->setPen( palette().color( QPalette::ButtonText ) );
    painter->setBrush( palette().brush( QPalette::ButtonText ) );
    painter->drawPolygon( pa );

    painter->restore();
}

/*!
  \return a size hint
*/
QSize QwtArrowButton::sizeHint() const
{
    const QSize hint = minimumSizeHint();
    return hint.expandedTo( QApplication::globalStrut() );
}

/*!
  \brief Return a minimum size hint
*/
QSize QwtArrowButton::minimumSizeHint() const
{
    const QSize asz = arrowSize( Qt::RightArrow, QSize() );

    QSize sz(
        2 * Margin + ( MaxNum - 1 ) * Spacing + MaxNum * asz.width(),
        2 * Margin + asz.height()
    );

    if ( d_data->arrowType == Qt::UpArrow || d_data->arrowType == Qt::DownArrow )
        sz.transpose();

    QStyleOption styleOption;
    styleOption.init( this );

    sz = style()->sizeFromContents( QStyle::CT_PushButton,
        &styleOption, sz, this );

    return sz;
}

/*!
   Calculate the size for a arrow that fits into a rect of a given size

   \param arrowType Arrow type
   \param boundingSize Bounding size
   \return Size of the arrow
*/
QSize QwtArrowButton::arrowSize( Qt::ArrowType arrowType,
    const QSize &boundingSize ) const
{
    QSize bs = boundingSize;
    if ( arrowType == Qt::UpArrow || arrowType == Qt::DownArrow )
        bs.transpose();

    const int MinLen = 2;
    const QSize sz = bs.expandedTo(
        QSize( MinLen, 2 * MinLen - 1 ) ); // minimum

    int w = sz.width();
    int h = 2 * w - 1;

    if ( h > sz.height() )
    {
        h = sz.height();
        w = ( h + 1 ) / 2;
    }

    QSize arrSize( w, h );
    if ( arrowType == Qt::UpArrow || arrowType == Qt::DownArrow )
        arrSize.transpose();

    return arrSize;
}

/*!
  \brief autoRepeat for the space keys
*/
void QwtArrowButton::keyPressEvent( QKeyEvent *event )
{
    if ( event->isAutoRepeat() && event->key() == Qt::Key_Space )
        Q_EMIT clicked();

    QPushButton::keyPressEvent( event );
}
