diff options
Diffstat (limited to 'src/widgets/qslider.cpp')
-rw-r--r-- | src/widgets/qslider.cpp | 921 |
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 |