/* This file is part of the KDE libraries
    Copyright (C) 1997 Martin Jones (mjones@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.
*/

#include "kselector.h"

#include <QImage>
#include <QPainter>
#include <QPaintEvent>
#include <QPixmap>
#include <QStyle>
#include <QStyleOption>

#include "kcolorhelpers_p.h"

using KDEPrivate::fillOpaqueRect;

//-----------------------------------------------------------------------------
/*
 * 1D value selector with contents drawn by derived class.
 * See KColorDialog for example.
 */

#define ARROWSIZE 5

class KSelector::Private
{
public:
    Private()
    {
        arrowPE = QStyle::PE_IndicatorArrowLeft;
        m_indent = true;
    }

    bool m_indent;
    QStyle::PrimitiveElement arrowPE;
};

class KGradientSelector::KGradientSelectorPrivate
{
public:
  KGradientSelectorPrivate(KGradientSelector *q): q(q) {}

  KGradientSelector *q;
  QLinearGradient gradient;
  QString text1;
  QString text2;
};

KSelector::KSelector( QWidget *parent )
  : QAbstractSlider( parent )
 , d(new Private)
{
    setOrientation(Qt::Horizontal);
}

KSelector::KSelector( Qt::Orientation o, QWidget *parent )
  : QAbstractSlider( parent )
 , d(new Private)
{
    setOrientation(o);
    if(o == Qt::Horizontal)
        setArrowDirection(Qt::UpArrow);
}

KSelector::~KSelector()
{
    delete d;
}

void KSelector::setIndent( bool i )
{
    d->m_indent = i;
}

bool KSelector::indent() const
{
    return d->m_indent;
}

QRect KSelector::contentsRect() const
{
    int w = indent() ? style()->pixelMetric( QStyle::PM_DefaultFrameWidth ) : 0;
    //TODO: is the height:width ratio of an indicator arrow always 2:1? hm.
    int iw = (w < ARROWSIZE) ? ARROWSIZE : w;

    if ( orientation() == Qt::Vertical ) {
        if ( arrowDirection() == Qt::RightArrow ) {
            return QRect( w + ARROWSIZE, iw,
                          width() - w*2 - ARROWSIZE,
                          height() - iw*2 );
        } else {
            return QRect( w, iw,
                          width() - w*2 - ARROWSIZE,
                          height() - iw*2 );
        }
    } else { // Qt::Horizontal
        if ( arrowDirection() == Qt::UpArrow ) {
            return QRect( iw, w,
                          width() - 2*iw,
                          height() - w*2 - ARROWSIZE );
        } else {
            return QRect( iw, w + ARROWSIZE,
                          width() - 2*iw,
                          height() - w*2 - ARROWSIZE );
        }
    }
}

void KSelector::paintEvent( QPaintEvent * )
{
  QPainter painter;
  int w = style()->pixelMetric( QStyle::PM_DefaultFrameWidth );
  int iw = (w < ARROWSIZE) ? ARROWSIZE : w;

  painter.begin( this );

  drawContents( &painter );

  QBrush brush;

  QPoint pos = calcArrowPos( value() );
  drawArrow( &painter, pos );

  if ( indent() )
  {
    QStyleOptionFrame opt;
    opt.initFrom( this );
    opt.state = QStyle::State_Sunken;
    if ( orientation() == Qt::Vertical )
      opt.rect.adjust( 0, iw - w, -5, w - iw );
    else
      opt.rect.adjust(iw - w, 0, w - iw, -5);
    QBrush oldBrush = painter.brush();
    painter.setBrush( Qt::NoBrush );
    style()->drawPrimitive( QStyle::PE_Frame, &opt, &painter, this );
    painter.setBrush( oldBrush );
  }


  painter.end();
}

void KSelector::mousePressEvent( QMouseEvent *e )
{
    setSliderDown(true);
    moveArrow( e->pos() );
}

void KSelector::mouseMoveEvent( QMouseEvent *e )
{
  moveArrow( e->pos() );
}

void KSelector::mouseReleaseEvent( QMouseEvent *e )
{
    moveArrow( e->pos() );
    setSliderDown(false);
}

void KSelector::wheelEvent( QWheelEvent *e )
{
    int val = value() + e->delta()/120;
    setSliderDown(true);
    setValue( val );
    setSliderDown(false);
}

void KSelector::moveArrow( const QPoint &pos )
{
    int val;
    int w = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
    int iw = (w < ARROWSIZE) ? ARROWSIZE : w;

    if ( orientation() == Qt::Vertical )
        val = ( maximum() - minimum() ) * (height() - pos.y() - iw)
            / (height() - iw * 2) + minimum();
    else
        val = ( maximum() - minimum() ) * ( pos.x() - iw)
            / (width() - iw * 2) + minimum();

    setValue( val );
    update();
}

QPoint KSelector::calcArrowPos( int val )
{
    QPoint p;
    int w = style()->pixelMetric( QStyle::PM_DefaultFrameWidth );
    int iw = ( w < ARROWSIZE ) ? ARROWSIZE : w;

    if ( orientation() == Qt::Vertical )
    {
        p.setY( height() - iw - 1 - (height() - 2 * iw - 1) * val  / ( maximum() - minimum() ) );

        if ( d->arrowPE == QStyle::PE_IndicatorArrowRight ) {
            p.setX( 0 );
        } else {
            p.setX( width() - 5 );
        }
    }
    else
    {
        p.setX( iw + (width() - 2 * iw - 1) * val  / ( maximum() - minimum() ) );

        if ( d->arrowPE == QStyle::PE_IndicatorArrowDown ) {
            p.setY( 0 );
        } else {
            p.setY( height() - 5 );
        }
    }

    return p;
}

void KSelector::setArrowDirection( Qt::ArrowType direction )
{
    switch ( direction ) {
        case Qt::UpArrow:
            if ( orientation() == Qt::Horizontal ) {
                d->arrowPE = QStyle::PE_IndicatorArrowUp;
            } else {
                d->arrowPE = QStyle::PE_IndicatorArrowLeft;
            }
            break;
        case Qt::DownArrow:
            if ( orientation() == Qt::Horizontal ) {
                d->arrowPE = QStyle::PE_IndicatorArrowDown;
            } else {
                d->arrowPE = QStyle::PE_IndicatorArrowRight;
            }
            break;
        case Qt::LeftArrow:
            if ( orientation() == Qt::Vertical ) {
                d->arrowPE = QStyle::PE_IndicatorArrowLeft;
            } else {
                d->arrowPE = QStyle::PE_IndicatorArrowDown;
            }
            break;
        case Qt::RightArrow:
            if ( orientation() == Qt::Vertical ) {
                d->arrowPE = QStyle::PE_IndicatorArrowRight;
            } else {
                d->arrowPE = QStyle::PE_IndicatorArrowUp;
            }
            break;

        case Qt::NoArrow:
            break;
    }
}

Qt::ArrowType KSelector::arrowDirection() const
{
    switch ( d->arrowPE ) {
        case QStyle::PE_IndicatorArrowUp:
            return Qt::UpArrow;
            break;
        case QStyle::PE_IndicatorArrowDown:
            return Qt::DownArrow;
            break;
        case QStyle::PE_IndicatorArrowRight:
            return Qt::RightArrow;
            break;
        case QStyle::PE_IndicatorArrowLeft:
        default:
            return Qt::LeftArrow;
            break;
    }
}

void KSelector::drawContents( QPainter * )
{}

void KSelector::drawArrow( QPainter *painter, const QPoint &pos )
{
    painter->setPen( QPen() );
    painter->setBrush( QBrush( palette().color(QPalette::ButtonText) ) );

    QStyleOption o;

    if ( orientation() == Qt::Vertical ) {
        o.rect = QRect( pos.x(), pos.y() - ARROWSIZE / 2,
                        ARROWSIZE, ARROWSIZE );
    } else {
        o.rect = QRect( pos.x() - ARROWSIZE / 2, pos.y(),
                        ARROWSIZE, ARROWSIZE );

    }
    style()->drawPrimitive( d->arrowPE, &o, painter, this );
}

//----------------------------------------------------------------------------

KGradientSelector::KGradientSelector( QWidget *parent )
    : KSelector( parent ), d(new KGradientSelectorPrivate(this))
{
}


KGradientSelector::KGradientSelector( Qt::Orientation o, QWidget *parent )
    : KSelector( o, parent ), d(new KGradientSelectorPrivate(this))
{
}


KGradientSelector::~KGradientSelector()
{
    delete d;
}


void KGradientSelector::drawContents( QPainter *painter )
{
  d->gradient.setStart(contentsRect().topLeft());
  if (orientation() == Qt::Vertical) {
      d->gradient.setFinalStop(contentsRect().bottomLeft());
  } else {
      d->gradient.setFinalStop(contentsRect().topRight());
  }
  QBrush gradientBrush(d->gradient);

  fillOpaqueRect(painter, contentsRect(), gradientBrush);

  if ( orientation() == Qt::Vertical )
  {
    int yPos = contentsRect().top() + painter->fontMetrics().ascent() + 2;
    int xPos = contentsRect().left() + (contentsRect().width() -
       painter->fontMetrics().width( d->text2 )) / 2;
    QPen pen( qGray(firstColor().rgb()) > 180 ? Qt::black : Qt::white );
    painter->setPen( pen );
    painter->drawText( xPos, yPos, d->text2 );

    yPos = contentsRect().bottom() - painter->fontMetrics().descent() - 2;
    xPos = contentsRect().left() + (contentsRect().width() -
      painter->fontMetrics().width( d->text1 )) / 2;
    pen.setColor( qGray(secondColor().rgb()) > 180 ? Qt::black : Qt::white );
    painter->setPen( pen );
    painter->drawText( xPos, yPos, d->text1 );
  }
  else
  {
    int yPos = contentsRect().bottom()-painter->fontMetrics().descent()-2;

    QPen pen( qGray(firstColor().rgb()) > 180 ? Qt::black : Qt::white );
    painter->setPen( pen );
    painter->drawText( contentsRect().left() + 2, yPos, d->text1 );

    pen.setColor( qGray(secondColor().rgb()) > 180 ? Qt::black : Qt::white );
    painter->setPen( pen );
    painter->drawText( contentsRect().right() -
       painter->fontMetrics().width( d->text2 ) - 2, yPos, d->text2 );
  }
}

QSize KGradientSelector::minimumSize() const
{
    return sizeHint();
}

void KGradientSelector::setStops( const QGradientStops &stops )
{
    d->gradient.setStops(stops);
    update();
}

QGradientStops KGradientSelector::stops() const
{
    return d->gradient.stops();
}

void KGradientSelector::setColors( const QColor &col1, const QColor &col2 )
{
  d->gradient.setColorAt(0.0, col1);
  d->gradient.setColorAt(1.0, col2);
  update();
}

void KGradientSelector::setText( const QString &t1, const QString &t2 )
{
  d->text1 = t1;
  d->text2 = t2;
  update();
}

void KGradientSelector::setFirstColor( const QColor &col )
{
  d->gradient.setColorAt(0.0, col);
  update();
}

void KGradientSelector::setSecondColor( const QColor &col )
{
  d->gradient.setColorAt(1.0, col);
  update();
}

void KGradientSelector::setFirstText( const QString &t )
{
  d->text1 = t;
  update();
}

void KGradientSelector::setSecondText( const QString &t )
{
  d->text2 = t;
  update();
}

QColor KGradientSelector::firstColor() const
{
  return d->gradient.stops().first().second;
}

QColor KGradientSelector::secondColor() const
{
  return d->gradient.stops().last().second;
}

QString KGradientSelector::firstText() const
{
  return d->text1;
}

QString KGradientSelector::secondText() const
{
  return d->text2;
}

#include "kselector.moc"
