/* -*- 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_wheel.h" #include "qwt_math.h" #include "qwt_painter.h" #include #include #include #include #include #include #include #if QT_VERSION < 0x040601 #define qFabs(x) ::fabs(x) #define qFastSin(x) ::sin(x) #define qExp(x) ::exp(x) #endif class QwtWheel::PrivateData { public: PrivateData(): orientation( Qt::Horizontal ), viewAngle( 175.0 ), totalAngle( 360.0 ), tickCount( 10 ), wheelBorderWidth( 2 ), borderWidth( 2 ), wheelWidth( 20 ), isScrolling( false ), mouseOffset( 0.0 ), tracking( true ), pendingValueChanged( false ), updateInterval( 50 ), mass( 0.0 ), timerId( 0 ), speed( 0.0 ), mouseValue( 0.0 ), flyingValue( 0.0 ), minimum( 0.0 ), maximum( 100.0 ), singleStep( 1.0 ), pageStepCount( 1 ), stepAlignment( true ), value( 0.0 ), inverted( false ), wrapping( false ) { } Qt::Orientation orientation; double viewAngle; double totalAngle; int tickCount; int wheelBorderWidth; int borderWidth; int wheelWidth; bool isScrolling; double mouseOffset; bool tracking; bool pendingValueChanged; // when not tracking int updateInterval; double mass; // for the flying wheel effect int timerId; QTime time; double speed; double mouseValue; double flyingValue; double minimum; double maximum; double singleStep; int pageStepCount; bool stepAlignment; double value; bool inverted; bool wrapping; }; //! Constructor QwtWheel::QwtWheel( QWidget *parent ): QWidget( parent ) { d_data = new PrivateData; setFocusPolicy( Qt::StrongFocus ); setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed ); setAttribute( Qt::WA_WState_OwnSizePolicy, false ); } //! Destructor QwtWheel::~QwtWheel() { delete d_data; } /*! \brief En/Disable tracking If tracking is enabled (the default), the wheel emits the valueChanged() signal while the wheel is moving. If tracking is disabled, the wheel emits the valueChanged() signal only when the wheel movement is terminated. The wheelMoved() signal is emitted regardless id tracking is enabled or not. \param enable On/Off \sa isTracking() */ void QwtWheel::setTracking( bool enable ) { d_data->tracking = enable; } /*! \return True, when tracking is enabled \sa setTracking(), valueChanged(), wheelMoved() */ bool QwtWheel::isTracking() const { return d_data->tracking; } /*! \brief Specify the update interval when the wheel is flying Default and minimum value is 50 ms. \param interval Interval in milliseconds \sa updateInterval(), setMass(), setTracking() */ void QwtWheel::setUpdateInterval( int interval ) { d_data->updateInterval = qMax( interval, 50 ); } /*! \return Update interval when the wheel is flying \sa setUpdateInterval(), mass(), isTracking() */ int QwtWheel::updateInterval() const { return d_data->updateInterval; } /*! \brief Mouse press event handler Start movement of the wheel. \param event Mouse event */ void QwtWheel::mousePressEvent( QMouseEvent *event ) { stopFlying(); d_data->isScrolling = wheelRect().contains( event->pos() ); if ( d_data->isScrolling ) { d_data->time.start(); d_data->speed = 0.0; d_data->mouseValue = valueAt( event->pos() ); d_data->mouseOffset = d_data->mouseValue - d_data->value; d_data->pendingValueChanged = false; Q_EMIT wheelPressed(); } } /*! \brief Mouse Move Event handler Turn the wheel according to the mouse position \param event Mouse event */ void QwtWheel::mouseMoveEvent( QMouseEvent *event ) { if ( !d_data->isScrolling ) return; double mouseValue = valueAt( event->pos() ); if ( d_data->mass > 0.0 ) { double ms = d_data->time.restart(); // the interval when mouse move events are posted are somehow // random. To avoid unrealistic speed values we limit ms ms = qMax( ms, 5.0 ); d_data->speed = ( mouseValue - d_data->mouseValue ) / ms; } d_data->mouseValue = mouseValue; double value = boundedValue( mouseValue - d_data->mouseOffset ); if ( d_data->stepAlignment ) value = alignedValue( value ); if ( value != d_data->value ) { d_data->value = value; update(); Q_EMIT wheelMoved( d_data->value ); if ( d_data->tracking ) Q_EMIT valueChanged( d_data->value ); else d_data->pendingValueChanged = true; } } /*! \brief Mouse Release Event handler When the wheel has no mass the movement of the wheel stops, otherwise it starts flying. \param event Mouse event */ void QwtWheel::mouseReleaseEvent( QMouseEvent *event ) { Q_UNUSED( event ); if ( !d_data->isScrolling ) return; d_data->isScrolling = false; bool startFlying = false; if ( d_data->mass > 0.0 ) { const int ms = d_data->time.elapsed(); if ( ( qFabs( d_data->speed ) > 0.0 ) && ( ms < 50 ) ) startFlying = true; } if ( startFlying ) { d_data->flyingValue = boundedValue( d_data->mouseValue - d_data->mouseOffset ); d_data->timerId = startTimer( d_data->updateInterval ); } else { if ( d_data->pendingValueChanged ) Q_EMIT valueChanged( d_data->value ); } d_data->pendingValueChanged = false; d_data->mouseOffset = 0.0; Q_EMIT wheelReleased(); } /*! \brief Qt timer event The flying wheel effect is implemented using a timer \param event Timer event \sa updateInterval() */ void QwtWheel::timerEvent( QTimerEvent *event ) { if ( event->timerId() != d_data->timerId ) { QWidget::timerEvent( event ); return; } d_data->speed *= qExp( -d_data->updateInterval * 0.001 / d_data->mass ); d_data->flyingValue += d_data->speed * d_data->updateInterval; d_data->flyingValue = boundedValue( d_data->flyingValue ); double value = d_data->flyingValue; if ( d_data->stepAlignment ) value = alignedValue( value ); if ( qFabs( d_data->speed ) < 0.001 * d_data->singleStep ) { // stop if d_data->speed < one step per second stopFlying(); } if ( value != d_data->value ) { d_data->value = value; update(); if ( d_data->tracking || d_data->timerId == 0 ) Q_EMIT valueChanged( d_data->value ); } } /*! \brief Handle wheel events In/Decrement the value \param event Wheel event */ void QwtWheel::wheelEvent( QWheelEvent *event ) { if ( !wheelRect().contains( event->pos() ) ) { event->ignore(); return; } if ( d_data->isScrolling ) return; stopFlying(); double increment = 0.0; if ( ( event->modifiers() & Qt::ControlModifier) || ( event->modifiers() & Qt::ShiftModifier ) ) { // one page regardless of delta increment = d_data->singleStep * d_data->pageStepCount; if ( event->delta() < 0 ) increment = -increment; } else { const int numSteps = event->delta() / 120; increment = d_data->singleStep * numSteps; } if ( d_data->orientation == Qt::Vertical && d_data->inverted ) increment = -increment; double value = boundedValue( d_data->value + increment ); if ( d_data->stepAlignment ) value = alignedValue( value ); if ( value != d_data->value ) { d_data->value = value; update(); Q_EMIT valueChanged( d_data->value ); Q_EMIT wheelMoved( d_data->value ); } } /*! Handle key events - Qt::Key_Home\n Step to minimum() - Qt::Key_End\n Step to maximum() - Qt::Key_Up\n In case of a horizontal or not inverted vertical wheel the value will be incremented by the step size. For an inverted vertical wheel the value will be decremented by the step size. - Qt::Key_Down\n In case of a horizontal or not inverted vertical wheel the value will be decremented by the step size. For an inverted vertical wheel the value will be incremented by the step size. - Qt::Key_PageUp\n The value will be incremented by pageStepSize() * singleStepSize(). - Qt::Key_PageDown\n The value will be decremented by pageStepSize() * singleStepSize(). \param event Key event */ void QwtWheel::keyPressEvent( QKeyEvent *event ) { if ( d_data->isScrolling ) { // don't interfere mouse scrolling return; } double value = d_data->value; double increment = 0.0; switch ( event->key() ) { case Qt::Key_Down: { if ( d_data->orientation == Qt::Vertical && d_data->inverted ) increment = d_data->singleStep; else increment = -d_data->singleStep; break; } case Qt::Key_Up: { if ( d_data->orientation == Qt::Vertical && d_data->inverted ) increment = -d_data->singleStep; else increment = d_data->singleStep; break; } case Qt::Key_Left: { if ( d_data->orientation == Qt::Horizontal ) { if ( d_data->inverted ) increment = d_data->singleStep; else increment = -d_data->singleStep; } break; } case Qt::Key_Right: { if ( d_data->orientation == Qt::Horizontal ) { if ( d_data->inverted ) increment = -d_data->singleStep; else increment = d_data->singleStep; } break; } case Qt::Key_PageUp: { increment = d_data->pageStepCount * d_data->singleStep; break; } case Qt::Key_PageDown: { increment = -d_data->pageStepCount * d_data->singleStep; break; } case Qt::Key_Home: { value = d_data->minimum; break; } case Qt::Key_End: { value = d_data->maximum; break; } default:; { event->ignore(); } } if ( event->isAccepted() ) stopFlying(); if ( increment != 0.0 ) { value = boundedValue( d_data->value + increment ); if ( d_data->stepAlignment ) value = alignedValue( value ); } if ( value != d_data->value ) { d_data->value = value; update(); Q_EMIT valueChanged( d_data->value ); Q_EMIT wheelMoved( d_data->value ); } } /*! \brief Adjust the number of grooves in the wheel's surface. The number of grooves is limited to 6 <= count <= 50. Values outside this range will be clipped. The default value is 10. \param count Number of grooves per 360 degrees \sa tickCount() */ void QwtWheel::setTickCount( int count ) { count = qBound( 6, count, 50 ); if ( count != d_data->tickCount ) { d_data->tickCount = qBound( 6, count, 50 ); update(); } } /*! \return Number of grooves in the wheel's surface. \sa setTickCnt() */ int QwtWheel::tickCount() const { return d_data->tickCount; } /*! \brief Set the wheel border width of the wheel. The wheel border must not be smaller than 1 and is limited in dependence on the wheel's size. Values outside the allowed range will be clipped. The wheel border defaults to 2. \param borderWidth Border width \sa internalBorder() */ void QwtWheel::setWheelBorderWidth( int borderWidth ) { const int d = qMin( width(), height() ) / 3; borderWidth = qMin( borderWidth, d ); d_data->wheelBorderWidth = qMax( borderWidth, 1 ); update(); } /*! \return Wheel border width \sa setWheelBorderWidth() */ int QwtWheel::wheelBorderWidth() const { return d_data->wheelBorderWidth; } /*! \brief Set the border width The border defaults to 2. \param width Border width \sa borderWidth() */ void QwtWheel::setBorderWidth( int width ) { d_data->borderWidth = qMax( width, 0 ); update(); } /*! \return Border width \sa setBorderWidth() */ int QwtWheel::borderWidth() const { return d_data->borderWidth; } /*! \return Rectangle of the wheel without the outer border */ QRect QwtWheel::wheelRect() const { const int bw = d_data->borderWidth; return contentsRect().adjusted( bw, bw, -bw, -bw ); } /*! \brief Set the total angle which the wheel can be turned. One full turn of the wheel corresponds to an angle of 360 degrees. A total angle of n*360 degrees means that the wheel has to be turned n times around its axis to get from the minimum value to the maximum value. The default setting of the total angle is 360 degrees. \param angle total angle in degrees \sa totalAngle() */ void QwtWheel::setTotalAngle( double angle ) { if ( angle < 0.0 ) angle = 0.0; d_data->totalAngle = angle; update(); } /*! \return Total angle which the wheel can be turned. \sa setTotalAngle() */ double QwtWheel::totalAngle() const { return d_data->totalAngle; } /*! \brief Set the wheel's orientation. The default orientation is Qt::Horizontal. \param orientation Qt::Horizontal or Qt::Vertical. \sa orientation() */ void QwtWheel::setOrientation( Qt::Orientation orientation ) { if ( d_data->orientation == orientation ) return; if ( !testAttribute( Qt::WA_WState_OwnSizePolicy ) ) { QSizePolicy sp = sizePolicy(); sp.transpose(); setSizePolicy( sp ); setAttribute( Qt::WA_WState_OwnSizePolicy, false ); } d_data->orientation = orientation; update(); } /*! \return Orientation \sa setOrientation() */ Qt::Orientation QwtWheel::orientation() const { return d_data->orientation; } /*! \brief Specify the visible portion of the wheel. You may use this function for fine-tuning the appearance of the wheel. The default value is 175 degrees. The value is limited from 10 to 175 degrees. \param angle Visible angle in degrees \sa viewAngle(), setTotalAngle() */ void QwtWheel::setViewAngle( double angle ) { d_data->viewAngle = qBound( 10.0, angle, 175.0 ); update(); } /*! \return Visible portion of the wheel \sa setViewAngle(), totalAngle() */ double QwtWheel::viewAngle() const { return d_data->viewAngle; } /*! Determine the value corresponding to a specified point \param pos Position \return Value corresponding to pos */ double QwtWheel::valueAt( const QPoint &pos ) const { const QRectF rect = wheelRect(); double w, dx; if ( d_data->orientation == Qt::Vertical ) { w = rect.height(); dx = rect.top() - pos.y(); } else { w = rect.width(); dx = pos.x() - rect.left(); } if ( w == 0.0 ) return 0.0; if ( d_data->inverted ) { dx = w - dx; } // w pixels is an arc of viewAngle degrees, // so we convert change in pixels to change in angle const double ang = dx * d_data->viewAngle / w; // value range maps to totalAngle degrees, // so convert the change in angle to a change in value const double val = ang * ( maximum() - minimum() ) / d_data->totalAngle; return val; } /*! \brief Qt Paint Event \param event Paint event */ void QwtWheel::paintEvent( QPaintEvent *event ) { QPainter painter( this ); painter.setClipRegion( event->region() ); QStyleOption opt; opt.init(this); style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this); qDrawShadePanel( &painter, contentsRect(), palette(), true, d_data->borderWidth ); drawWheelBackground( &painter, wheelRect() ); drawTicks( &painter, wheelRect() ); if ( hasFocus() ) QwtPainter::drawFocusRect( &painter, this ); } /*! Draw the Wheel's background gradient \param painter Painter \param rect Geometry for the wheel */ void QwtWheel::drawWheelBackground( QPainter *painter, const QRectF &rect ) { painter->save(); QPalette pal = palette(); // draw shaded background QLinearGradient gradient( rect.topLeft(), ( d_data->orientation == Qt::Horizontal ) ? rect.topRight() : rect.bottomLeft() ); gradient.setColorAt( 0.0, pal.color( QPalette::Button ) ); gradient.setColorAt( 0.2, pal.color( QPalette::Midlight ) ); gradient.setColorAt( 0.7, pal.color( QPalette::Mid ) ); gradient.setColorAt( 1.0, pal.color( QPalette::Dark ) ); painter->fillRect( rect, gradient ); // draw internal border const QPen lightPen( palette().color( QPalette::Light ), d_data->wheelBorderWidth, Qt::SolidLine, Qt::FlatCap ); const QPen darkPen( pal.color( QPalette::Dark ), d_data->wheelBorderWidth, Qt::SolidLine, Qt::FlatCap ); const double bw2 = 0.5 * d_data->wheelBorderWidth; if ( d_data->orientation == Qt::Horizontal ) { painter->setPen( lightPen ); painter->drawLine( QPointF( rect.left(), rect.top() + bw2 ), QPointF( rect.right(), rect.top() + bw2 ) ); painter->setPen( darkPen ); painter->drawLine( QPointF( rect.left(), rect.bottom() - bw2 ), QPointF( rect.right(), rect.bottom() - bw2 ) ); } else // Qt::Vertical { painter->setPen( lightPen ); painter->drawLine( QPointF( rect.left() + bw2, rect.top() ), QPointF( rect.left() + bw2, rect.bottom() ) ); painter->setPen( darkPen ); painter->drawLine( QPointF( rect.right() - bw2, rect.top() ), QPointF( rect.right() - bw2, rect.bottom() ) ); } painter->restore(); } /*! Draw the Wheel's ticks \param painter Painter \param rect Geometry for the wheel */ void QwtWheel::drawTicks( QPainter *painter, const QRectF &rect ) { const double range = d_data->maximum - d_data->minimum; if ( range == 0.0 || d_data->totalAngle == 0.0 ) { return; } const QPen lightPen( palette().color( QPalette::Light ), 0, Qt::SolidLine, Qt::FlatCap ); const QPen darkPen( palette().color( QPalette::Dark ), 0, Qt::SolidLine, Qt::FlatCap ); const double cnvFactor = qAbs( d_data->totalAngle / range ); const double halfIntv = 0.5 * d_data->viewAngle / cnvFactor; const double loValue = value() - halfIntv; const double hiValue = value() + halfIntv; const double tickWidth = 360.0 / double( d_data->tickCount ) / cnvFactor; const double sinArc = qFastSin( d_data->viewAngle * M_PI / 360.0 ); if ( d_data->orientation == Qt::Horizontal ) { const double radius = rect.width() * 0.5; double l1 = rect.top() + d_data->wheelBorderWidth; double l2 = rect.bottom() - d_data->wheelBorderWidth - 1; // draw one point over the border if border > 1 if ( d_data->wheelBorderWidth > 1 ) { l1--; l2++; } const double maxpos = rect.right() - 2; const double minpos = rect.left() + 2; // draw tick marks for ( double tickValue = ::ceil( loValue / tickWidth ) * tickWidth; tickValue < hiValue; tickValue += tickWidth ) { const double angle = qwtRadians( tickValue - value() ); const double s = qFastSin( angle * cnvFactor ); const double off = radius * ( sinArc + s ) / sinArc; double tickPos; if ( d_data->inverted ) tickPos = rect.left() + off; else tickPos = rect.right() - off; if ( ( tickPos <= maxpos ) && ( tickPos > minpos ) ) { painter->setPen( darkPen ); painter->drawLine( QPointF( tickPos - 1 , l1 ), QPointF( tickPos - 1, l2 ) ); painter->setPen( lightPen ); painter->drawLine( QPointF( tickPos, l1 ), QPointF( tickPos, l2 ) ); } } } else // Qt::Vertical { const double radius = rect.height() * 0.5; double l1 = rect.left() + d_data->wheelBorderWidth; double l2 = rect.right() - d_data->wheelBorderWidth - 1; if ( d_data->wheelBorderWidth > 1 ) { l1--; l2++; } const double maxpos = rect.bottom() - 2; const double minpos = rect.top() + 2; for ( double tickValue = ::ceil( loValue / tickWidth ) * tickWidth; tickValue < hiValue; tickValue += tickWidth ) { const double angle = qwtRadians( tickValue - value() ); const double s = qFastSin( angle * cnvFactor ); const double off = radius * ( sinArc + s ) / sinArc; double tickPos; if ( d_data->inverted ) tickPos = rect.bottom() - off; else tickPos = rect.top() + off; if ( ( tickPos <= maxpos ) && ( tickPos > minpos ) ) { painter->setPen( darkPen ); painter->drawLine( QPointF( l1, tickPos - 1 ), QPointF( l2, tickPos - 1 ) ); painter->setPen( lightPen ); painter->drawLine( QPointF( l1, tickPos ), QPointF( l2, tickPos ) ); } } } } /*! \brief Set the width of the wheel Corresponds to the wheel height for horizontal orientation, and the wheel width for vertical orientation. \param width the wheel's width \sa wheelWidth() */ void QwtWheel::setWheelWidth( int width ) { d_data->wheelWidth = width; update(); } /*! \return Width of the wheel \sa setWheelWidth() */ int QwtWheel::wheelWidth() const { return d_data->wheelWidth; } /*! \return a size hint */ QSize QwtWheel::sizeHint() const { const QSize hint = minimumSizeHint(); return hint.expandedTo( QApplication::globalStrut() ); } /*! \return Minimum size hint \warning The return value is based on the wheel width. */ QSize QwtWheel::minimumSizeHint() const { QSize sz( 3 * d_data->wheelWidth + 2 * d_data->borderWidth, d_data->wheelWidth + 2 * d_data->borderWidth ); if ( d_data->orientation != Qt::Horizontal ) sz.transpose(); return sz; } /*! \brief Set the step size of the counter A value <= 0.0 disables stepping \param stepSize Single step size \sa singleStep(), setPageStepCount() */ void QwtWheel::setSingleStep( double stepSize ) { d_data->singleStep = qMax( stepSize, 0.0 ); } /*! \return Single step size \sa setSingleStep() */ double QwtWheel::singleStep() const { return d_data->singleStep; } /*! \brief En/Disable step alignment When step alignment is enabled value changes initiated by user input ( mouse, keyboard, wheel ) are aligned to the multiples of the single step. \param on On/Off \sa stepAlignment(), setSingleStep() */ void QwtWheel::setStepAlignment( bool on ) { if ( on != d_data->stepAlignment ) { d_data->stepAlignment = on; } } /*! \return True, when the step alignment is enabled \sa setStepAlignment(), singleStep() */ bool QwtWheel::stepAlignment() const { return d_data->stepAlignment; } /*! \brief Set the page step count pageStepCount is a multiplicator for the single step size that typically corresponds to the user pressing PageUp or PageDown. A value of 0 disables page stepping. The default value is 1. \param count Multiplicator for the single step size \sa pageStepCount(), setSingleStep() */ void QwtWheel::setPageStepCount( int count ) { d_data->pageStepCount = qMax( 0, count ); } /*! \return Page step count \sa setPageStepCount(), singleStep() */ int QwtWheel::pageStepCount() const { return d_data->pageStepCount; } /*! \brief Set the minimum and maximum values The maximum is adjusted if necessary to ensure that the range remains valid. The value might be modified to be inside of the range. \param min Minimum value \param max Maximum value \sa minimum(), maximum() */ void QwtWheel::setRange( double min, double max ) { max = qMax( min, max ); if ( d_data->minimum == min && d_data->maximum == max ) return; d_data->minimum = min; d_data->maximum = max; if ( d_data->value < min || d_data->value > max ) { d_data->value = qBound( min, d_data->value, max ); update(); Q_EMIT valueChanged( d_data->value ); } } /*! Set the minimum value of the range \param value Minimum value \sa setRange(), setMaximum(), minimum() \note The maximum is adjusted if necessary to ensure that the range remains valid. */ void QwtWheel::setMinimum( double value ) { setRange( value, maximum() ); } /*! \return The minimum of the range \sa setRange(), setMinimum(), maximum() */ double QwtWheel::minimum() const { return d_data->minimum; } /*! Set the maximum value of the range \param value Maximum value \sa setRange(), setMinimum(), maximum() */ void QwtWheel::setMaximum( double value ) { setRange( minimum(), value ); } /*! \return The maximum of the range \sa setRange(), setMaximum(), minimum() */ double QwtWheel::maximum() const { return d_data->maximum; } /*! \brief Set a new value without adjusting to the step raster \param value New value \sa value(), valueChanged() \warning The value is clipped when it lies outside the range. */ void QwtWheel::setValue( double value ) { stopFlying(); d_data->isScrolling = false; value = qBound( d_data->minimum, value, d_data->maximum ); if ( d_data->value != value ) { d_data->value = value; update(); Q_EMIT valueChanged( d_data->value ); } } /*! \return Current value of the wheel \sa setValue(), valueChanged() */ double QwtWheel::value() const { return d_data->value; } /*! \brief En/Disable inverted appearance An inverted wheel increases its values in the opposite direction. The direction of an inverted horizontal wheel will be from right to left an inverted vertical wheel will increase from bottom to top. \param on En/Disable inverted appearance \sa isInverted() */ void QwtWheel::setInverted( bool on ) { if ( d_data->inverted != on ) { d_data->inverted = on; update(); } } /*! \return True, when the wheel is inverted \sa setInverted() */ bool QwtWheel::isInverted() const { return d_data->inverted; } /*! \brief En/Disable wrapping If wrapping is true stepping up from maximum() value will take you to the minimum() value and vice versa. \param on En/Disable wrapping \sa wrapping() */ void QwtWheel::setWrapping( bool on ) { d_data->wrapping = on; } /*! \return True, when wrapping is set \sa setWrapping() */ bool QwtWheel::wrapping() const { return d_data->wrapping; } /*! \brief Set the slider's mass for flywheel effect. If the slider's mass is greater then 0, it will continue to move after the mouse button has been released. Its speed decreases with time at a rate depending on the slider's mass. A large mass means that it will continue to move for a long time. Derived widgets may overload this function to make it public. \param mass New mass in kg \bug If the mass is smaller than 1g, it is set to zero. The maximal mass is limited to 100kg. \sa mass() */ void QwtWheel::setMass( double mass ) { if ( mass < 0.001 ) { d_data->mass = 0.0; } else { d_data->mass = qMin( 100.0, mass ); } if ( d_data->mass <= 0.0 ) stopFlying(); } /*! \return mass \sa setMass() */ double QwtWheel::mass() const { return d_data->mass; } //! Stop the flying movement of the wheel void QwtWheel::stopFlying() { if ( d_data->timerId != 0 ) { killTimer( d_data->timerId ); d_data->timerId = 0; d_data->speed = 0.0; } } double QwtWheel::boundedValue( double value ) const { const double range = d_data->maximum - d_data->minimum; if ( d_data->wrapping && range >= 0.0 ) { if ( value < d_data->minimum ) { value += ::ceil( ( d_data->minimum - value ) / range ) * range; } else if ( value > d_data->maximum ) { value -= ::ceil( ( value - d_data->maximum ) / range ) * range; } } else { value = qBound( d_data->minimum, value, d_data->maximum ); } return value; } double QwtWheel::alignedValue( double value ) const { const double stepSize = d_data->singleStep; if ( stepSize > 0.0 ) { value = d_data->minimum + qRound( ( value - d_data->minimum ) / stepSize ) * stepSize; if ( stepSize > 1e-12 ) { if ( qFuzzyCompare( value + 1.0, 1.0 ) ) { // correct rounding error if value = 0 value = 0.0; } else if ( qFuzzyCompare( value, d_data->maximum ) ) { // correct rounding error at the border value = d_data->maximum; } } } return value; }