summaryrefslogtreecommitdiffstats
path: root/src/widgets/qslider.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/widgets/qslider.cpp')
-rw-r--r--src/widgets/qslider.cpp921
1 files changed, 921 insertions, 0 deletions
diff --git a/src/widgets/qslider.cpp b/src/widgets/qslider.cpp
new file mode 100644
index 0000000..1573294
--- /dev/null
+++ b/src/widgets/qslider.cpp
@@ -0,0 +1,921 @@
+/****************************************************************************
+**
+** Implementation of QSlider class
+**
+** Created : 961019
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the widgets module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "qslider.h"
+#ifndef QT_NO_SLIDER
+#include "qpainter.h"
+#include "qdrawutil.h"
+#include "qtimer.h"
+#include "qbitmap.h"
+#include "qapplication.h"
+#include "qstyle.h"
+#if defined(QT_ACCESSIBILITY_SUPPORT)
+#include "qaccessible.h"
+#endif
+
+static const int thresholdTime = 300;
+static const int repeatTime = 100;
+
+struct QSliderPrivate
+{
+ // ### move these to QSlider in Qt 4.0
+ int sliderStartVal;
+ QSliderPrivate() : sliderStartVal( 0 ) { }
+};
+
+
+/*!
+ \class QSlider
+ \brief The QSlider widget provides a vertical or horizontal slider.
+
+ \ingroup basic
+ \mainclass
+
+ The slider is the classic widget for controlling a bounded value.
+ It lets the user move a slider along a horizontal or vertical
+ groove and translates the slider's position into an integer value
+ within the legal range.
+
+ QSlider inherits QRangeControl, which provides the "integer" side
+ of the slider. setRange() and value() are likely to be used by
+ practically all slider users; see the \l QRangeControl
+ documentation for information about the many other functions that
+ class provides.
+
+ The main functions offered by the slider itself are tickmark and
+ orientation control; you can use setTickmarks() to indicate where
+ you want the tickmarks to be, setTickInterval() to indicate how
+ many of them you want and setOrientation() to indicate whether the
+ slider is to be horizontal or vertical.
+
+ A slider accepts focus on Tab and uses the mouse wheel and a
+ suitable keyboard interface.
+
+ <img src=qslider-m.png> <img src=qslider-w.png>
+
+ \important setRange
+
+ \sa QScrollBar QSpinBox
+ \link guibooks.html#fowler GUI Design Handbook: Slider\endlink
+*/
+
+
+/*!
+ \enum QSlider::TickSetting
+
+ This enum specifies where the tickmarks are to be drawn relative
+ to the slider's groove and the handle the user moves.
+
+ \value NoMarks do not draw any tickmarks.
+ \value Both draw tickmarks on both sides of the groove.
+ \value Above draw tickmarks above the (horizontal) slider
+ \value Below draw tickmarks below the (horizontal) slider
+ \value Left draw tickmarks to the left of the (vertical) slider
+ \value Right draw tickmarks to the right of the (vertical) slider
+*/
+
+
+/*!
+ Constructs a vertical slider.
+
+ The \a parent and \a name arguments are sent on to the QWidget
+ constructor.
+*/
+
+QSlider::QSlider( QWidget *parent, const char *name )
+ : QWidget( parent, name )
+{
+ orient = Vertical;
+ init();
+}
+
+/*!
+ Constructs a slider.
+
+ The \a orientation must be \l Qt::Vertical or \l Qt::Horizontal.
+
+ The \a parent and \a name arguments are sent on to the QWidget
+ constructor.
+*/
+
+QSlider::QSlider( Orientation orientation, QWidget *parent, const char *name )
+ : QWidget( parent, name )
+{
+ orient = orientation;
+ init();
+}
+
+/*!
+ Constructs a slider whose value can never be smaller than \a
+ minValue or greater than \a maxValue, whose page step size is \a
+ pageStep and whose value is initially \a value (which is
+ guaranteed to be in range using bound()).
+
+ If \a orientation is \c Qt::Vertical the slider is vertical and if it
+ is \c Qt::Horizontal the slider is horizontal.
+
+ The \a parent and \a name arguments are sent on to the QWidget
+ constructor.
+*/
+
+QSlider::QSlider( int minValue, int maxValue, int pageStep,
+ int value, Orientation orientation,
+ QWidget *parent, const char *name )
+ : QWidget( parent, name ),
+ QRangeControl( minValue, maxValue, 1, pageStep, value )
+{
+ orient = orientation;
+ init();
+ sliderVal = value;
+}
+
+/*!
+ Destructor.
+*/
+QSlider::~QSlider()
+{
+ delete d;
+}
+
+void QSlider::init()
+{
+ d = new QSliderPrivate;
+ timer = 0;
+ sliderPos = 0;
+ sliderVal = 0;
+ clickOffset = 0;
+ state = Idle;
+ track = TRUE;
+ ticks = NoMarks;
+ tickInt = 0;
+ setFocusPolicy( TabFocus );
+ initTicks();
+
+ QSizePolicy sp( QSizePolicy::Expanding, QSizePolicy::Fixed );
+ if ( orient == Vertical )
+ sp.transpose();
+ setSizePolicy( sp );
+ clearWState( WState_OwnSizePolicy );
+}
+
+
+/*
+ Does what's needed when someone changes the tickmark status.
+*/
+
+void QSlider::initTicks()
+{
+ tickOffset = style().pixelMetric( QStyle::PM_SliderTickmarkOffset, this );
+}
+
+
+/*!
+ \property QSlider::tracking
+ \brief whether slider tracking is enabled
+
+ If tracking is enabled (the default), the slider emits the
+ valueChanged() signal whenever the slider is being dragged. If
+ tracking is disabled, the slider emits the valueChanged() signal
+ when the user releases the mouse button (unless the value happens
+ to be the same as before).
+*/
+
+void QSlider::setTracking( bool enable )
+{
+ track = enable;
+}
+
+
+/*!
+ \fn void QSlider::valueChanged( int value )
+
+ This signal is emitted when the slider value is changed, with the
+ new slider \a value as its argument.
+*/
+
+/*!
+ \fn void QSlider::sliderPressed()
+
+ This signal is emitted when the user presses the slider with the
+ mouse.
+*/
+
+/*!
+ \fn void QSlider::sliderMoved( int value )
+
+ This signal is emitted when the slider is dragged, with the new
+ slider \a value as its argument.
+*/
+
+/*!
+ \fn void QSlider::sliderReleased()
+
+ This signal is emitted when the user releases the slider with the mouse.
+*/
+
+/*
+ Calculates slider position corresponding to value \a v.
+*/
+
+int QSlider::positionFromValue( int v ) const
+{
+ int a = available();
+ int x = QRangeControl::positionFromValue( v, a );
+ if ( orient == Horizontal && QApplication::reverseLayout() )
+ x = a - x;
+ return x;
+}
+
+/*
+ Returns the available space in which the slider can move.
+*/
+
+int QSlider::available() const
+{
+ return style().pixelMetric( QStyle::PM_SliderSpaceAvailable, this );
+}
+
+/*
+ Calculates a value corresponding to slider position \a p.
+*/
+
+int QSlider::valueFromPosition( int p ) const
+{
+ int a = available();
+ int x = QRangeControl::valueFromPosition( p, a );
+ if ( orient == Horizontal && QApplication::reverseLayout() )
+ x = maxValue() + minValue() - x;
+ return x;
+}
+
+/*!
+ Implements the virtual QRangeControl function.
+*/
+
+void QSlider::rangeChange()
+{
+ int newPos = positionFromValue( value() );
+ if ( newPos != sliderPos ) {
+ reallyMoveSlider( newPos );
+ }
+}
+
+/*!
+ Implements the virtual QRangeControl function.
+*/
+
+void QSlider::valueChange()
+{
+ if ( sliderVal != value() ) {
+ int newPos = positionFromValue( value() );
+ sliderVal = value();
+ reallyMoveSlider( newPos );
+ }
+ emit valueChanged(value());
+#if defined(QT_ACCESSIBILITY_SUPPORT)
+ QAccessible::updateAccessibility( this, 0, QAccessible::ValueChanged );
+#endif
+}
+
+
+/*!
+ \reimp
+*/
+void QSlider::resizeEvent( QResizeEvent * )
+{
+ rangeChange();
+ initTicks();
+}
+
+
+/*!
+ Reimplements the virtual function QWidget::setPalette().
+
+ Sets the background color to the mid color for Motif style sliders
+ using palette \a p.
+*/
+
+void QSlider::setPalette( const QPalette &p )
+{
+ QWidget::setPalette( p );
+}
+
+
+
+/*!
+ \property QSlider::orientation
+ \brief the slider's orientation
+
+ The orientation must be \l Qt::Vertical (the default) or \l
+ Qt::Horizontal.
+*/
+
+void QSlider::setOrientation( Orientation orientation )
+{
+ if ( orientation == orient )
+ return;
+
+ if ( !testWState( WState_OwnSizePolicy ) ) {
+ QSizePolicy sp = sizePolicy();
+ sp.transpose();
+ setSizePolicy( sp );
+ clearWState( WState_OwnSizePolicy );
+ }
+
+ orient = orientation;
+
+ rangeChange();
+ update();
+}
+
+/*!
+ \fn int QSlider::sliderStart() const
+
+ Returns the start position of the slider.
+*/
+
+
+/*!
+ Returns the slider handle rectangle. (This is the visual marker
+ that the user can move.)
+*/
+
+QRect QSlider::sliderRect() const
+{
+ return style().querySubControlMetrics( QStyle::CC_Slider, this,
+ QStyle::SC_SliderHandle );
+}
+
+/*
+ Performs the actual moving of the slider.
+*/
+
+void QSlider::reallyMoveSlider( int newPos )
+{
+ QRegion oldR(sliderRect());
+ sliderPos = newPos;
+ QRegion newR(sliderRect());
+
+ /* just the one repaint if no background */
+ if (backgroundMode() == NoBackground)
+ repaint(newR | oldR, FALSE);
+ else {
+ repaint(oldR.subtract(newR));
+ repaint(newR, FALSE);
+ }
+}
+
+
+/*!
+ \reimp
+*/
+void QSlider::paintEvent( QPaintEvent * )
+{
+ QPainter p( this );
+
+ QStyle::SFlags flags = QStyle::Style_Default;
+ if (isEnabled())
+ flags |= QStyle::Style_Enabled;
+ if (hasFocus())
+ flags |= QStyle::Style_HasFocus;
+
+ QStyle::SCFlags sub = QStyle::SC_SliderGroove | QStyle::SC_SliderHandle;
+ if ( tickmarks() != NoMarks )
+ sub |= QStyle::SC_SliderTickmarks;
+
+ style().drawComplexControl( QStyle::CC_Slider, &p, this, rect(), colorGroup(),
+ flags, sub, state == Dragging ? QStyle::SC_SliderHandle : QStyle::SC_None );
+}
+
+
+/*!
+ \reimp
+*/
+void QSlider::mousePressEvent( QMouseEvent *e )
+{
+ int slideLength = style().pixelMetric( QStyle::PM_SliderLength, this );
+ resetState();
+ d->sliderStartVal = sliderVal;
+ QRect r = sliderRect();
+
+ if ( e->button() == RightButton )
+ return;
+
+ if ( r.contains( e->pos() ) ) {
+ state = Dragging;
+ clickOffset = (QCOORD)( goodPart( e->pos() ) - sliderPos );
+ emit sliderPressed();
+ } else if ( e->button() == MidButton ) {
+ int pos = goodPart( e->pos() );
+ moveSlider( pos - slideLength / 2 );
+ state = Dragging;
+ clickOffset = slideLength / 2;
+ } else if ( orient == Horizontal && e->pos().x() < r.left() //### goodPart
+ || orient == Vertical && e->pos().y() < r.top() ) {
+ if ( orient == Horizontal && QApplication::reverseLayout() ) {
+ state = TimingUp;
+ addPage();
+ } else {
+ state = TimingDown;
+ subtractPage();
+ }
+ if ( !timer )
+ timer = new QTimer( this );
+ connect( timer, SIGNAL(timeout()), SLOT(repeatTimeout()) );
+ timer->start( thresholdTime, TRUE );
+ } else if ( orient == Horizontal && e->pos().x() > r.right() //### goodPart
+ || orient == Vertical && e->pos().y() > r.bottom() ) {
+ if ( orient == Horizontal && QApplication::reverseLayout() ) {
+ state = TimingDown;
+ subtractPage();
+ } else {
+ state = TimingUp;
+ addPage();
+ }
+ if ( !timer )
+ timer = new QTimer( this );
+ connect( timer, SIGNAL(timeout()), SLOT(repeatTimeout()) );
+ timer->start( thresholdTime, TRUE );
+ }
+ update( sliderRect() );
+}
+
+/*!
+ \reimp
+*/
+void QSlider::mouseMoveEvent( QMouseEvent *e )
+{
+ if ( state != Dragging )
+ return;
+
+ QRect r = rect();
+ int m = style().pixelMetric( QStyle::PM_MaximumDragDistance,
+ this );
+ if ( m >= 0 ) {
+ if ( orientation() == Horizontal )
+ r.setRect( r.x() - m, r.y() - 2*m/3,
+ r.width() + 2*m, r.height() + 3*m );
+ else
+ r.setRect( r.x() - 2*m/3, r.y() - m,
+ r.width() + 3*m, r.height() + 2*m );
+ if ( !r.contains( e->pos() ) ) {
+ moveSlider( positionFromValue(d->sliderStartVal) );
+ return;
+ }
+ }
+
+ int pos = goodPart( e->pos() );
+ moveSlider( pos - clickOffset );
+}
+
+/*!
+ \reimp
+*/
+#ifndef QT_NO_WHEELEVENT
+void QSlider::wheelEvent( QWheelEvent * e )
+{
+ if ( e->orientation() != orientation() && !rect().contains(e->pos()) )
+ return;
+
+ static float offset = 0;
+ static QSlider* offset_owner = 0;
+ if (offset_owner != this){
+ offset_owner = this;
+ offset = 0;
+ }
+ offset += -e->delta()*QMAX(pageStep(),lineStep())/120;
+ if (QABS(offset)<1)
+ return;
+ setValue( value() + int(offset) );
+ offset -= int(offset);
+ e->accept();
+}
+#endif
+
+/*!
+ \reimp
+*/
+void QSlider::mouseReleaseEvent( QMouseEvent * )
+{
+ resetState();
+ update( sliderRect() );
+}
+
+/*!
+ \reimp
+*/
+void QSlider::focusInEvent( QFocusEvent * e)
+{
+ QWidget::focusInEvent( e );
+}
+
+/*!
+ \reimp
+*/
+void QSlider::focusOutEvent( QFocusEvent * e )
+{
+ QWidget::focusOutEvent( e );
+}
+
+/*!
+ Moves the left (or top) edge of the slider to position \a pos. The
+ slider is actually moved to the step position nearest the given \a
+ pos.
+*/
+
+void QSlider::moveSlider( int pos )
+{
+ int a = available();
+ int newPos = QMIN( a, QMAX( 0, pos ) );
+ int newVal = valueFromPosition( newPos );
+ if (style().styleHint(QStyle::SH_Slider_SnapToValue, this))
+ newPos = positionFromValue( newVal );
+ if ( sliderPos != newPos )
+ reallyMoveSlider( newPos );
+ if ( sliderVal != newVal ) {
+ sliderVal = newVal;
+ emit sliderMoved( sliderVal );
+ }
+ if ( tracking() && sliderVal != value() )
+ setValue( sliderVal );
+
+}
+
+
+/*
+ Resets all state information and stops the timer.
+*/
+
+void QSlider::resetState()
+{
+ if ( timer ) {
+ timer->stop();
+ timer->disconnect();
+ }
+ switch ( state ) {
+ case TimingUp:
+ case TimingDown:
+ break;
+ case Dragging: {
+ setValue( valueFromPosition( sliderPos ) );
+ emit sliderReleased();
+ break;
+ }
+ case Idle:
+ break;
+ default:
+ qWarning("QSlider: (%s) in wrong state", name( "unnamed" ) );
+ }
+ state = Idle;
+}
+
+
+/*!
+ \reimp
+*/
+void QSlider::keyPressEvent( QKeyEvent *e )
+{
+ bool sloppy = bool(style().styleHint(QStyle::SH_Slider_SloppyKeyEvents, this));
+ switch ( e->key() ) {
+ case Key_Left:
+ if ( sloppy || orient == Horizontal ) {
+ if (QApplication::reverseLayout())
+ addLine();
+ else
+ subtractLine();
+ }
+ break;
+ case Key_Right:
+ if ( sloppy || orient == Horizontal ) {
+ if (QApplication::reverseLayout())
+ subtractLine();
+ else
+ addLine();
+ }
+ break;
+ case Key_Up:
+ if ( sloppy || orient == Vertical )
+ subtractLine();
+ break;
+ case Key_Down:
+ if ( sloppy || orient == Vertical )
+ addLine();
+ break;
+ case Key_Prior:
+ subtractPage();
+ break;
+ case Key_Next:
+ addPage();
+ break;
+ case Key_Home:
+ setValue( minValue() );
+ break;
+ case Key_End:
+ setValue( maxValue() );
+ break;
+ default:
+ e->ignore();
+ return;
+ }
+}
+
+void QSlider::setValue( int value )
+{
+ QRangeControl::setValue( value );
+#if defined(QT_ACCESSIBILITY_SUPPORT)
+ QAccessible::updateAccessibility( this, 0, QAccessible::ValueChanged );
+#endif
+}
+
+
+/*! \reimp
+*/
+
+void QSlider::addLine()
+{
+ QRangeControl::addLine();
+}
+
+/*! \reimp
+*/
+
+void QSlider::subtractLine()
+{
+ QRangeControl::subtractLine();
+}
+
+/*!
+ Moves the slider one pageStep() up or right.
+*/
+
+void QSlider::addStep()
+{
+ addPage();
+}
+
+
+/*!
+ Moves the slider one pageStep() down or left.
+*/
+
+void QSlider::subtractStep()
+{
+ subtractPage();
+}
+
+
+/*
+ Waits for autorepeat.
+*/
+
+void QSlider::repeatTimeout()
+{
+ Q_ASSERT( timer );
+ timer->disconnect();
+ if ( state == TimingDown )
+ connect( timer, SIGNAL(timeout()), SLOT(subtractStep()) );
+ else if ( state == TimingUp )
+ connect( timer, SIGNAL(timeout()), SLOT(addStep()) );
+ timer->start( repeatTime, FALSE );
+}
+
+
+/*
+ Returns the relevant dimension of \a p.
+*/
+
+int QSlider::goodPart( const QPoint &p ) const
+{
+ return (orient == Horizontal) ? p.x() : p.y();
+}
+
+/*!
+ \reimp
+*/
+QSize QSlider::sizeHint() const
+{
+ constPolish();
+ const int length = 84, tickSpace = 5;
+ int thick = style().pixelMetric( QStyle::PM_SliderThickness, this );
+ if ( ticks & Above )
+ thick += tickSpace;
+ if ( ticks & Below )
+ thick += tickSpace;
+ int w = thick, h = length;
+ if ( orient == Horizontal ) {
+ w = length;
+ h = thick;
+ }
+ return (style().sizeFromContents(QStyle::CT_Slider, this,
+ QSize(w, h)).expandedTo(QApplication::globalStrut()));
+}
+
+
+
+/*!
+ \reimp
+*/
+
+QSize QSlider::minimumSizeHint() const
+{
+ QSize s = sizeHint();
+ int length = style().pixelMetric(QStyle::PM_SliderLength, this);
+ if ( orient == Horizontal )
+ s.setWidth( length );
+ else
+ s.setHeight( length );
+
+ return s;
+}
+
+/*! \fn void QSlider::setSizePolicy( QSizePolicy::SizeType, QSizePolicy::SizeType, bool )
+ \reimp
+*/
+
+/*! \reimp */
+void QSlider::setSizePolicy( QSizePolicy sp )
+{
+ // ## remove 4.0
+ QWidget::setSizePolicy( sp );
+}
+
+/*! \reimp */
+QSizePolicy QSlider::sizePolicy() const
+{
+ // ### 4.0 remove this reimplementation
+ return QWidget::sizePolicy();
+}
+
+/*!
+ \property QSlider::tickmarks
+ \brief the tickmark settings for this slider
+
+ The valid values are in \l{QSlider::TickSetting}. The default is
+ \c NoMarks.
+
+ \sa tickInterval
+*/
+
+void QSlider::setTickmarks( TickSetting s )
+{
+ ticks = s;
+ initTicks();
+ update();
+}
+
+
+/*!
+ \property QSlider::tickInterval
+ \brief the interval between tickmarks
+
+ This is a value interval, not a pixel interval. If it is 0, the
+ slider will choose between lineStep() and pageStep(). The initial
+ value of tickInterval is 0.
+
+ \sa QRangeControl::lineStep(), QRangeControl::pageStep()
+*/
+
+void QSlider::setTickInterval( int i )
+{
+ tickInt = QMAX( 0, i );
+ update();
+}
+
+
+/*!
+ \reimp
+*/
+void QSlider::styleChange( QStyle& old )
+{
+ QWidget::styleChange( old );
+}
+
+/*!
+ \property QSlider::minValue
+ \brief the current minimum value of the slider
+
+ When setting this property, the \l QSlider::maxValue is adjusted,
+ if necessary, to ensure that the range remains valid.
+
+ \sa setRange()
+*/
+int QSlider::minValue() const
+{
+ return QRangeControl::minValue();
+}
+
+/*!
+ \property QSlider::maxValue
+ \brief the current maximum value of the slider
+
+ When setting this property, the \l QSlider::minValue is adjusted,
+ if necessary, to ensure that the range remains valid.
+
+ \sa setRange()
+*/
+int QSlider::maxValue() const
+{
+ return QRangeControl::maxValue();
+}
+
+void QSlider::setMinValue( int minVal )
+{
+ QRangeControl::setMinValue( minVal );
+}
+
+void QSlider::setMaxValue( int maxVal )
+{
+ QRangeControl::setMaxValue( maxVal );
+}
+
+/*!
+ \property QSlider::lineStep
+ \brief the current line step
+
+ When setting lineStep, the virtual stepChange() function will be
+ called if the new line step is different from the previous
+ setting.
+
+ \sa setSteps() QRangeControl::pageStep() setRange()
+*/
+int QSlider::lineStep() const
+{
+ return QRangeControl::lineStep();
+}
+
+/*!
+ \property QSlider::pageStep
+ \brief the current page step
+
+ When setting pageStep, the virtual stepChange() function will be
+ called if the new page step is different from the previous
+ setting.
+
+ \sa QRangeControl::setSteps() setLineStep() setRange()
+*/
+
+int QSlider::pageStep() const
+{
+ return QRangeControl::pageStep();
+}
+
+void QSlider::setLineStep( int i )
+{
+ setSteps( i, pageStep() );
+}
+
+void QSlider::setPageStep( int i )
+{
+ setSteps( lineStep(), i );
+}
+
+/*!
+ \property QSlider::value
+ \brief the current slider value
+
+ \sa QRangeControl::value() prevValue()
+*/
+
+int QSlider::value() const
+{
+ return QRangeControl::value();
+}
+
+#endif