Skip to content
qwt_slider.cpp 23 KiB
Newer Older
pixhawk's avatar
pixhawk committed
/* -*- 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
 *****************************************************************************/

Bryant's avatar
Bryant committed
#include "qwt_slider.h"
pixhawk's avatar
pixhawk committed
#include "qwt_painter.h"
#include "qwt_scale_draw.h"
#include "qwt_scale_map.h"
Bryant's avatar
Bryant committed
#include <qevent.h>
#include <qdrawutil.h>
#include <qpainter.h>
#include <qalgorithms.h>
#include <qmath.h>
#include <qstyle.h>
#include <qstyleoption.h>
#include <qapplication.h>

static QSize qwtHandleSize( const QSize &size, 
    Qt::Orientation orientation, bool hasTrough )
{
    QSize handleSize = size;

    if ( handleSize.isEmpty() )
    {
        const int handleThickness = 16;
        handleSize.setWidth( 2 * handleThickness );
        handleSize.setHeight( handleThickness );

        if ( !hasTrough )
            handleSize.transpose();

        if ( orientation == Qt::Vertical )
            handleSize.transpose();
    }

    return handleSize;
}

static QwtScaleDraw::Alignment qwtScaleDrawAlignment( 
    Qt::Orientation orientation, QwtSlider::ScalePosition scalePos )
{
    QwtScaleDraw::Alignment align;

    if ( orientation == Qt::Vertical )
    {
        // NoScale lays out like Left
        if ( scalePos == QwtSlider::LeadingScale )
            align = QwtScaleDraw::RightScale;
        else
            align = QwtScaleDraw::LeftScale;
    }
    else
    {
        // NoScale lays out like Bottom
        if ( scalePos == QwtSlider::TrailingScale )
            align = QwtScaleDraw::TopScale;
        else
            align = QwtScaleDraw::BottomScale;
    }

    return align;
}
pixhawk's avatar
pixhawk committed

class QwtSlider::PrivateData
{
public:
Bryant's avatar
Bryant committed
    PrivateData():
        repeatTimerId( 0 ),
        updateInterval( 150 ),
        stepsIncrement( 0 ),
        pendingValueChange( false ),
        borderWidth( 2 ),
        spacing( 4 ),
        scalePosition( QwtSlider::TrailingScale ),
        hasTrough( true ),
        hasGroove( false ),
        mouseOffset( 0 )
    {
    }

    int repeatTimerId;
    bool timerTick;
    int updateInterval;
    int stepsIncrement;
    bool pendingValueChange;

pixhawk's avatar
pixhawk committed
    QRect sliderRect;

Bryant's avatar
Bryant committed
    QSize handleSize;
pixhawk's avatar
pixhawk committed
    int borderWidth;
Bryant's avatar
Bryant committed
    int spacing;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    Qt::Orientation orientation;
    QwtSlider::ScalePosition scalePosition;

    bool hasTrough;
    bool hasGroove;

    int mouseOffset;
pixhawk's avatar
pixhawk committed

    mutable QSize sizeHintCache;
};
/*!
Bryant's avatar
Bryant committed
  Construct vertical slider in QwtSlider::Trough style
  with a scale to the left. 

  The scale is initialized to [0.0, 100.0] and the value set to 0.0.

  \param parent Parent widget

  \sa setOrientation(), setScalePosition(), setBackgroundStyle()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
QwtSlider::QwtSlider( QWidget *parent ):
    QwtAbstractSlider( parent )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    initSlider( Qt::Vertical );
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
  Construct a slider in QwtSlider::Trough style

  When orientation is Qt::Vertical the scale will be aligned to
  the left - otherwise at the the top of the slider.
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  The scale is initialized to [0.0, 100.0] and the value set to 0.0.
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  \param parent Parent widget
  \param orientation Orientation of the slider. 
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
QwtSlider::QwtSlider( Qt::Orientation orientation, QWidget *parent ):
    QwtAbstractSlider( parent )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    initSlider( orientation );
pixhawk's avatar
pixhawk committed
}

Bryant's avatar
Bryant committed
//! Destructor
QwtSlider::~QwtSlider()
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    delete d_data;
}
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
void QwtSlider::initSlider( Qt::Orientation orientation )
{
    if ( orientation == Qt::Vertical )
        setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Expanding );
    else
        setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    setAttribute( Qt::WA_WState_OwnSizePolicy, false );
pixhawk's avatar
pixhawk committed

    d_data = new QwtSlider::PrivateData;

Bryant's avatar
Bryant committed
    d_data->orientation = orientation;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    scaleDraw()->setAlignment( 
        qwtScaleDrawAlignment( orientation, d_data->scalePosition ) );
    scaleDraw()->setLength( 100 );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    setScale( 0.0, 100.0 );
    setValue( 0.0 );
pixhawk's avatar
pixhawk committed
}

/*!
  \brief Set the orientation.
Bryant's avatar
Bryant committed
  \param orientation Allowed values are Qt::Horizontal and Qt::Vertical.
Bryant's avatar
Bryant committed
  \sa orientation(), scalePosition()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtSlider::setOrientation( Qt::Orientation orientation )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( orientation == d_data->orientation )
pixhawk's avatar
pixhawk committed
        return;

Bryant's avatar
Bryant committed
    d_data->orientation = orientation;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    scaleDraw()->setAlignment( 
        qwtScaleDrawAlignment( orientation, d_data->scalePosition ) );

    if ( !testAttribute( Qt::WA_WState_OwnSizePolicy ) )
pixhawk's avatar
pixhawk committed
    {
        QSizePolicy sp = sizePolicy();
        sp.transpose();
Bryant's avatar
Bryant committed
        setSizePolicy( sp );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        setAttribute( Qt::WA_WState_OwnSizePolicy, false );
pixhawk's avatar
pixhawk committed
    }

Bryant's avatar
Bryant committed
    if ( testAttribute( Qt::WA_WState_Polished ) )
        layoutSlider( true );
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
  \return Orientation
  \sa setOrientation()
*/
Qt::Orientation QwtSlider::orientation() const
{
    return d_data->orientation;
}
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
/*!
  \brief Change the position of the scale
  \param scalePosition Position of the scale.
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  \sa ScalePosition, scalePosition()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtSlider::setScalePosition( ScalePosition scalePosition )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( d_data->scalePosition == scalePosition )
pixhawk's avatar
pixhawk committed
        return;

Bryant's avatar
Bryant committed
    d_data->scalePosition = scalePosition;
    scaleDraw()->setAlignment( 
        qwtScaleDrawAlignment( d_data->orientation, scalePosition ) );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    if ( testAttribute( Qt::WA_WState_Polished ) )
        layoutSlider( true );
pixhawk's avatar
pixhawk committed
}

Bryant's avatar
Bryant committed
/*! 
  \return Position of the scale
  \sa setScalePosition()
 */
QwtSlider::ScalePosition QwtSlider::scalePosition() const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    return d_data->scalePosition;
pixhawk's avatar
pixhawk committed
}

/*!
  \brief Change the slider's border width
Bryant's avatar
Bryant committed

  The border width is used for drawing the slider handle and the
  trough.

  \param width Border width
  \sa borderWidth()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtSlider::setBorderWidth( int width )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( width < 0 )
        width = 0;

    if ( width != d_data->borderWidth )
    {
        d_data->borderWidth = width;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        if ( testAttribute( Qt::WA_WState_Polished ) )
            layoutSlider( true );
pixhawk's avatar
pixhawk committed
    }
}

/*!
Bryant's avatar
Bryant committed
  \return the border width.
  \sa setBorderWidth()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
int QwtSlider::borderWidth() const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    return d_data->borderWidth;
}

/*!
  \brief Change the spacing between trough and scale

  A spacing of 0 means, that the backbone of the scale is covered
  by the trough.

  The default setting is 4 pixels.
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  \param spacing Number of pixels
  \sa spacing();
*/
void QwtSlider::setSpacing( int spacing )
{
    if ( spacing <= 0 )
        spacing = 0;

    if ( spacing != d_data->spacing  )
    {
        d_data->spacing = spacing;

        if ( testAttribute( Qt::WA_WState_Polished ) )
            layoutSlider( true );
pixhawk's avatar
pixhawk committed
    }
}

/*!
Bryant's avatar
Bryant committed
  \return Number of pixels between slider and scale
  \sa setSpacing()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
int QwtSlider::spacing() const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    return d_data->spacing;
}

/*!
  \brief Set the slider's handle size

  When the size is empty the slider handle will be painted with a
  default size depending on its orientation() and backgroundStyle().

  \param size New size
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  \sa handleSize()
*/
void QwtSlider::setHandleSize( const QSize &size )
{
    if ( size != d_data->handleSize )
    {
        d_data->handleSize = size;

        if ( testAttribute( Qt::WA_WState_Polished ) )
            layoutSlider( true );
pixhawk's avatar
pixhawk committed
    }
}

Bryant's avatar
Bryant committed
/*!
  \return Size of the handle.
  \sa setHandleSize()
*/
QSize QwtSlider::handleSize() const
{
    return d_data->handleSize;
}

pixhawk's avatar
pixhawk committed
/*!
  \brief Set a scale draw

  For changing the labels of the scales, it
  is necessary to derive from QwtScaleDraw and
  overload QwtScaleDraw::label().

  \param scaleDraw ScaleDraw object, that has to be created with
Bryant's avatar
Bryant committed
                   new and will be deleted in ~QwtSlider() or the next
pixhawk's avatar
pixhawk committed
                   call of setScaleDraw().
Bryant's avatar
Bryant committed

  \sa scaleDraw()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtSlider::setScaleDraw( QwtScaleDraw *scaleDraw )
pixhawk's avatar
pixhawk committed
{
    const QwtScaleDraw *previousScaleDraw = this->scaleDraw();
    if ( scaleDraw == NULL || scaleDraw == previousScaleDraw )
        return;

    if ( previousScaleDraw )
Bryant's avatar
Bryant committed
        scaleDraw->setAlignment( previousScaleDraw->alignment() );

    setAbstractScaleDraw( scaleDraw );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    if ( testAttribute( Qt::WA_WState_Polished ) )
        layoutSlider( true );
pixhawk's avatar
pixhawk committed
}

/*!
  \return the scale draw of the slider
  \sa setScaleDraw()
*/
const QwtScaleDraw *QwtSlider::scaleDraw() const
{
Bryant's avatar
Bryant committed
    return static_cast<const QwtScaleDraw *>( abstractScaleDraw() );
pixhawk's avatar
pixhawk committed
}

/*!
  \return the scale draw of the slider
  \sa setScaleDraw()
*/
QwtScaleDraw *QwtSlider::scaleDraw()
{
Bryant's avatar
Bryant committed
    return static_cast<QwtScaleDraw *>( abstractScaleDraw() );
pixhawk's avatar
pixhawk committed
}

//! Notify changed scale
void QwtSlider::scaleChange()
{
Bryant's avatar
Bryant committed
    QwtAbstractSlider::scaleChange();

    if ( testAttribute( Qt::WA_WState_Polished ) )
        layoutSlider( true );
}

/*!
  \brief Specify the update interval for automatic scrolling

  The minimal accepted value is 50 ms.

  \param interval Update interval in milliseconds

  \sa setUpdateInterval()
*/
void QwtSlider::setUpdateInterval( int interval )
{
    d_data->updateInterval = qMax( interval, 50 );
}

/*!
  \return Update interval in milliseconds for automatic scrolling
  \sa setUpdateInterval()
 */
int QwtSlider::updateInterval() const
{
    return d_data->updateInterval;
}

/*!
   Draw the slider into the specified rectangle.

   \param painter Painter
   \param sliderRect Bounding rectangle of the slider
*/
void QwtSlider::drawSlider( 
    QPainter *painter, const QRect &sliderRect ) const
{
    QRect innerRect( sliderRect );

    if ( d_data->hasTrough )
    {
        const int bw = d_data->borderWidth;
        innerRect = sliderRect.adjusted( bw, bw, -bw, -bw );

        painter->fillRect( innerRect, palette().brush( QPalette::Mid ) );
        qDrawShadePanel( painter, sliderRect, palette(), true, bw, NULL );
    }

    const QSize handleSize = qwtHandleSize( d_data->handleSize,
        d_data->orientation, d_data->hasTrough );

    if ( d_data->hasGroove )
    {
        const int slotExtent = 4;
        const int slotMargin = 4;

        QRect slotRect; 
        if ( orientation() == Qt::Horizontal )
        {
            int slotOffset = qMax( 1, handleSize.width() / 2 - slotMargin );
            int slotHeight = slotExtent + ( innerRect.height() % 2 );

            slotRect.setWidth( innerRect.width() - 2 * slotOffset );
            slotRect.setHeight( slotHeight );
pixhawk's avatar
pixhawk committed
        }
Bryant's avatar
Bryant committed
        else
        {
            int slotOffset = qMax( 1, handleSize.height() / 2 - slotMargin );
            int slotWidth = slotExtent + ( innerRect.width() % 2 );

            slotRect.setWidth( slotWidth );
            slotRect.setHeight( innerRect.height() - 2 * slotOffset );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        }

        slotRect.moveCenter( innerRect.center() );

        QBrush brush = palette().brush( QPalette::Dark );
        qDrawShadePanel( painter, slotRect, palette(), true, 1 , &brush );
pixhawk's avatar
pixhawk committed
    }

    if ( isValid() )
Bryant's avatar
Bryant committed
        drawHandle( painter, handleRect(), transform( value() ) );
pixhawk's avatar
pixhawk committed
}

Bryant's avatar
Bryant committed
/*!
  Draw the thumb at a position

  \param painter Painter
  \param handleRect Bounding rectangle of the handle
  \param pos Position of the handle marker in widget coordinates
*/
void QwtSlider::drawHandle( QPainter *painter, 
    const QRect &handleRect, int pos ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    const int bw = d_data->borderWidth;

    qDrawShadePanel( painter, 
        handleRect, palette(), false, bw,
        &palette().brush( QPalette::Button ) );

pixhawk's avatar
pixhawk committed
    pos++; // shade line points one pixel below
Bryant's avatar
Bryant committed
    if ( orientation() == Qt::Horizontal )
    {
        qDrawShadeLine( painter, pos, handleRect.top() + bw,
            pos, handleRect.bottom() - bw, palette(), true, 1 );
    }
    else // Vertical
    {
        qDrawShadeLine( painter, handleRect.left() + bw, pos,
            handleRect.right() - bw, pos, palette(), true, 1 );
    }
}

/*!
  \brief Determine what to do when the user presses a mouse button.

  \param pos Mouse position

  \retval True, when handleRect() contains pos 
  \sa scrolledTo()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
bool QwtSlider::isScrollPosition( const QPoint &pos ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( handleRect().contains( pos ) )
    {
        const double v = ( orientation() == Qt::Horizontal ) 
            ? pos.x() : pos.y();

        d_data->mouseOffset = v - transform( value() );
        return true;
pixhawk's avatar
pixhawk committed
    }

Bryant's avatar
Bryant committed
    return false;
}

/*!
  \brief Determine the value for a new position of the
         slider handle.

  \param pos Mouse position

  \return Value for the mouse position
  \sa isScrollPosition()
*/
double QwtSlider::scrolledTo( const QPoint &pos ) const
{
    int p = ( orientation() == Qt::Horizontal ) 
        ? pos.x() : pos.y();

    p -= d_data->mouseOffset;

    int min = transform( lowerBound() );
    int max = transform( upperBound() );
    if ( min > max )
        qSwap( min, max );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    p = qBound( min, p, max );

    return invTransform( p );
}

/*!
   Mouse press event handler
   \param event Mouse event
*/
void QwtSlider::mousePressEvent( QMouseEvent *event )
{
    if ( isReadOnly() )
    {
        event->ignore();
pixhawk's avatar
pixhawk committed
        return;
    }

Bryant's avatar
Bryant committed
    const QPoint pos = event->pos();

    if ( isValid() && d_data->sliderRect.contains( pos ) )
    {
        if ( !handleRect().contains( pos ) )
        {
            const int markerPos = transform( value() );

            d_data->stepsIncrement = pageSteps();

            if ( d_data->orientation == Qt::Horizontal )
            {
                if ( pos.x() < markerPos )
                    d_data->stepsIncrement = -d_data->stepsIncrement;
            }
            else
            {
                if ( pos.y() < markerPos )
                    d_data->stepsIncrement = -d_data->stepsIncrement;
            }

            if ( isInverted() )
                d_data->stepsIncrement = -d_data->stepsIncrement;

            d_data->timerTick = false;
            d_data->repeatTimerId = startTimer( qMax( 250, 2 * updateInterval() ) );

            return;
        }
    }

    QwtAbstractSlider::mousePressEvent( event );
}

/*!
   Mouse release event handler
   \param event Mouse event
*/
void QwtSlider::mouseReleaseEvent( QMouseEvent *event )
{
    if ( d_data->repeatTimerId > 0 )
    {
        killTimer( d_data->repeatTimerId );
        d_data->repeatTimerId = 0;
        d_data->timerTick = false;
        d_data->stepsIncrement = 0;
    }

    if ( d_data->pendingValueChange )
    {
        d_data->pendingValueChange = false;
        Q_EMIT valueChanged( value() );
    }
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    QwtAbstractSlider::mouseReleaseEvent( event );
pixhawk's avatar
pixhawk committed
}

Bryant's avatar
Bryant committed
/*!
   Timer event handler

   Handles the timer, when the mouse stays pressed
   inside the sliderRect().

   \param event Mouse event
*/  
void QwtSlider::timerEvent( QTimerEvent *event )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( event->timerId() != d_data->repeatTimerId )
    {
        QwtAbstractSlider::timerEvent( event );
        return;
    }

    if ( !isValid() )
    {
        killTimer( d_data->repeatTimerId );
        d_data->repeatTimerId = 0;
        return;
    }

    const double v = value();
    incrementValue( d_data->stepsIncrement );

    if ( v != value() )
    {
        if ( isTracking() )
            Q_EMIT valueChanged( value() );
        else
            d_data->pendingValueChange = true;

        Q_EMIT sliderMoved( value() );
pixhawk's avatar
pixhawk committed
    }
Bryant's avatar
Bryant committed

    if ( !d_data->timerTick )
    {
        // restart the timer with a shorter interval
        killTimer( d_data->repeatTimerId );
        d_data->repeatTimerId = startTimer( updateInterval() );
        
        d_data->timerTick = true;
    }   
pixhawk's avatar
pixhawk committed
}

Bryant's avatar
Bryant committed
/*!
   Qt paint event handler
   \param event Paint event
*/
void QwtSlider::paintEvent( QPaintEvent *event )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    QPainter painter( this );
    painter.setClipRegion( event->region() );

    QStyleOption opt;
    opt.init(this);
    style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);

    if ( d_data->scalePosition != QwtSlider::NoScale )
    {
        if ( !d_data->sliderRect.contains( event->rect() ) )
            scaleDraw()->draw( &painter, palette() );
pixhawk's avatar
pixhawk committed
    }

Bryant's avatar
Bryant committed
    drawSlider( &painter, d_data->sliderRect );
pixhawk's avatar
pixhawk committed

    if ( hasFocus() )
Bryant's avatar
Bryant committed
        QwtPainter::drawFocusRect( &painter, this, d_data->sliderRect );
pixhawk's avatar
pixhawk committed
}

Bryant's avatar
Bryant committed
/*!
   Qt resize event handler
   \param event Resize event
*/
void QwtSlider::resizeEvent( QResizeEvent *event )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    Q_UNUSED( event );

pixhawk's avatar
pixhawk committed
    layoutSlider( false );
}

Bryant's avatar
Bryant committed
/*!
   Handles QEvent::StyleChange and QEvent::FontChange events
   \param event Change event
*/
void QwtSlider::changeEvent( QEvent *event )
{
    if ( event->type() == QEvent::StyleChange || 
        event->type() == QEvent::FontChange )
    {
        if ( testAttribute( Qt::WA_WState_Polished ) )
            layoutSlider( true );
    }

    QwtAbstractSlider::changeEvent( event );
}

pixhawk's avatar
pixhawk committed
/*!
  Recalculate the slider's geometry and layout based on
Bryant's avatar
Bryant committed
  the current geometry and fonts.

pixhawk's avatar
pixhawk committed
  \param update_geometry  notify the layout system and call update
         to redraw the scale
*/
void QwtSlider::layoutSlider( bool update_geometry )
{
Bryant's avatar
Bryant committed
    int bw = 0;
    if ( d_data->hasTrough )
        bw = d_data->borderWidth;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    const QSize handleSize = qwtHandleSize( d_data->handleSize,
        d_data->orientation, d_data->hasTrough );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    QRect sliderRect = contentsRect();
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    /*
       The marker line of the handle needs to be aligned to
       the scale. But the marker is in the center 
       and we need space enough to display the rest of the handle.
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
       But the scale itself usually needs margins for displaying
       the tick labels, that also might needs space beyond the
       backbone.
Bryant's avatar
Bryant committed
       Now it depends on what needs more margins. If it is the
       slider the scale gets shrunk, otherwise the slider.
     */
Bryant's avatar
Bryant committed
    int scaleMargin = 0;
    if ( d_data->scalePosition != QwtSlider::NoScale )
    {
        int d1, d2;
        scaleDraw()->getBorderDistHint( font(), d1, d2 );

        scaleMargin = qMax( d1, d2 ) - bw;
    }
Bryant's avatar
Bryant committed
    int scaleX, scaleY, scaleLength;
Bryant's avatar
Bryant committed
    if ( d_data->orientation == Qt::Horizontal )
    {
        const int handleMargin = handleSize.width() / 2 - 1;
        if ( scaleMargin > handleMargin )
        {
            int off = scaleMargin - handleMargin;
            sliderRect.adjust( off, 0, -off, 0 );
        }
Bryant's avatar
Bryant committed
        scaleX = sliderRect.left() + bw + handleSize.width() / 2 - 1;
        scaleLength = sliderRect.width() - handleSize.width();
    }
    else
    {
        int handleMargin = handleSize.height() / 2 - 1;
        if ( scaleMargin > handleMargin )
        {
            int off = scaleMargin - handleMargin;
            sliderRect.adjust( 0, off, 0, -off );
Bryant's avatar
Bryant committed
        scaleY = sliderRect.top() + bw + handleSize.height() / 2 - 1;
        scaleLength = sliderRect.height() - handleSize.height();
    }

    scaleLength -= 2 * bw;
Bryant's avatar
Bryant committed
    // now align slider and scale according to the ScalePosition
Bryant's avatar
Bryant committed
    if ( d_data->orientation == Qt::Horizontal )
    {
        const int h = handleSize.height() + 2 * bw;

        if ( d_data->scalePosition == QwtSlider::TrailingScale )
        {
            sliderRect.setTop( sliderRect.bottom() + 1 - h );
            scaleY = sliderRect.top() - d_data->spacing;
Bryant's avatar
Bryant committed
        else
        {
            sliderRect.setHeight( h );
            scaleY = sliderRect.bottom() + 1 + d_data->spacing;
pixhawk's avatar
pixhawk committed
        }
Bryant's avatar
Bryant committed
    }
    else // Qt::Vertical
    {
        const int w = handleSize.width() + 2 * bw;

        if ( d_data->scalePosition == QwtSlider::LeadingScale )
        {
            sliderRect.setWidth( w );
            scaleX = sliderRect.right() + 1 + d_data->spacing;
        }
        else
        {
            sliderRect.setLeft( sliderRect.right() + 1 - w );
            scaleX = sliderRect.left() - d_data->spacing;
pixhawk's avatar
pixhawk committed
        }
    }

Bryant's avatar
Bryant committed
    d_data->sliderRect = sliderRect;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    scaleDraw()->move( scaleX, scaleY );
    scaleDraw()->setLength( scaleLength );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    if ( update_geometry )
    {
pixhawk's avatar
pixhawk committed
        d_data->sizeHintCache = QSize(); // invalidate
        updateGeometry();
        update();
    }
}

Bryant's avatar
Bryant committed
/*!
  En/Disable the trough
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  The slider can be cutomized by showing a trough for the
  handle.
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  \param on When true, the groove is visible
  \sa hasTrough(), setGroove()
 */
void QwtSlider::setTrough( bool on )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( d_data->hasTrough != on )
    {
        d_data->hasTrough = on;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        if ( testAttribute( Qt::WA_WState_Polished ) )
            layoutSlider( true );
pixhawk's avatar
pixhawk committed
    }
}

/*!
Bryant's avatar
Bryant committed
  \return True, when the trough is visisble
  \sa setTrough(), hasGroove()
 */
bool QwtSlider::hasTrough() const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    return d_data->hasTrough;
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
  En/Disable the groove
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  The slider can be cutomized by showing a groove for the
  handle.
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  \param on When true, the groove is visible
  \sa hasGroove(), setThrough()
 */
void QwtSlider::setGroove( bool on )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( d_data->hasGroove != on )
    {
        d_data->hasGroove = on;
        
        if ( testAttribute( Qt::WA_WState_Polished ) )
            layoutSlider( true );
    }
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
  \return True, when the groove is visisble
  \sa setGroove(), hasTrough()
 */
bool QwtSlider::hasGroove() const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    return d_data->hasGroove;
} 
pixhawk's avatar
pixhawk committed

/*!
Bryant's avatar
Bryant committed
  \return minimumSizeHint()
pixhawk's avatar
pixhawk committed
*/
QSize QwtSlider::sizeHint() const
{
Bryant's avatar
Bryant committed
    const QSize hint = minimumSizeHint();
    return hint.expandedTo( QApplication::globalStrut() );
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
  \return Minimum size hint
  \sa sizeHint()
pixhawk's avatar
pixhawk committed
*/
QSize QwtSlider::minimumSizeHint() const
{
Bryant's avatar
Bryant committed
    if ( !d_data->sizeHintCache.isEmpty() )
pixhawk's avatar
pixhawk committed
        return d_data->sizeHintCache;

Bryant's avatar
Bryant committed
    const QSize handleSize = qwtHandleSize( d_data->handleSize,
        d_data->orientation, d_data->hasTrough );

    int bw = 0;
    if ( d_data->hasTrough )
        bw = d_data->borderWidth;

    int sliderLength = 0; 
    int scaleExtent = 0;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    if ( d_data->scalePosition != QwtSlider::NoScale )
    {
pixhawk's avatar
pixhawk committed
        int d1, d2;
Bryant's avatar
Bryant committed
        scaleDraw()->getBorderDistHint( font(), d1, d2 );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        const int scaleBorderDist = 2 * ( qMax( d1, d2 ) - bw );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        int handleBorderDist;
        if ( d_data->orientation == Qt::Horizontal )
            handleBorderDist = handleSize.width();
        else
            handleBorderDist = handleSize.height();
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        sliderLength = scaleDraw()->minLength( font() );
        if ( handleBorderDist > scaleBorderDist )
        {
            // We need additional space for the overlapping handle
            sliderLength += handleBorderDist - scaleBorderDist;
        }
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        scaleExtent += d_data->spacing;
        scaleExtent += qCeil( scaleDraw()->extent( font() ) );
pixhawk's avatar
pixhawk committed
    }

Bryant's avatar
Bryant committed
    sliderLength = qMax( sliderLength, 84 ); // from QSlider
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    int w = 0;
    int h = 0;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    if ( d_data->orientation == Qt::Horizontal )
    {
        w = sliderLength;
        h = handleSize.height() + 2 * bw + scaleExtent;
    }
    else
    {
        w = handleSize.width() + 2 * bw + scaleExtent;
        h = sliderLength;
    }

    // finally add margins
    int left, right, top, bottom;
    getContentsMargins( &left, &top, &right, &bottom );

    w += left + right;
    h += top + bottom;

    d_data->sizeHintCache = QSize( w, h );
pixhawk's avatar
pixhawk committed
    return d_data->sizeHintCache;
}
Bryant's avatar
Bryant committed

/*!
   \return Bounding rectangle of the slider handle
 */
QRect QwtSlider::handleRect() const
{
    if ( !isValid() )
        return QRect();

    const int markerPos = transform( value() );

    QPoint center = d_data->sliderRect.center();
    if ( d_data->orientation == Qt::Horizontal )
        center.setX( markerPos );
    else
        center.setY( markerPos );

    QRect rect;
    rect.setSize( qwtHandleSize( d_data->handleSize,
        d_data->orientation, d_data->hasTrough ) );
    rect.moveCenter( center );

    return rect;
}

/*!
 \return Bounding rectangle of the slider - without the scale
 */
QRect QwtSlider::sliderRect() const
{
    return d_data->sliderRect;
}