/**************************************************************************** ** ** Implementation of TQSlider class ** ** Created : 961019 ** ** Copyright (C) 2010 Timothy Pearson and (C) 1992-2008 Trolltech ASA. ** ** This file is part of the widgets module of the TQt 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 TQt 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.TQPL ** included in the packaging of this file. Licensees holding valid TQt ** Commercial licenses may use this file in accordance with the TQt ** 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 "tqslider.h" #ifndef TQT_NO_SLIDER #include "tqpainter.h" #include "tqdrawutil.h" #include "tqtimer.h" #include "tqbitmap.h" #include "tqapplication.h" #include "tqstyle.h" #if defined(TQT_ACCESSIBILITY_SUPPORT) #include "tqaccessible.h" #endif static const int thresholdTime = 300; static const int repeatTime = 100; struct TQSliderPrivate { // ### move these to TQSlider in TQt 4.0 int sliderStartVal; TQSliderPrivate() : sliderStartVal( 0 ) { } }; /*! \class TQSlider \brief The TQSlider 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. TQSlider inherits TQRangeControl, which provides the "integer" side of the slider. setRange() and value() are likely to be used by practically all slider users; see the \l TQRangeControl 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. \important setRange \sa TQScrollBar TQSpinBox \link guibooks.html#fowler GUI Design Handbook: Slider\endlink */ /*! \enum TQSlider::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 tqparent and \a name arguments are sent on to the TQWidget constructor. */ TQSlider::TQSlider( TQWidget *tqparent, const char *name ) : TQWidget( tqparent, name ) { orient = Qt::Vertical; init(); } /*! Constructs a slider. The \a orientation must be \l Qt::Vertical or \l Qt::Horizontal. The \a tqparent and \a name arguments are sent on to the TQWidget constructor. */ TQSlider::TQSlider( Qt::Orientation orientation, TQWidget *tqparent, const char *name ) : TQWidget( tqparent, 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 tqparent and \a name arguments are sent on to the TQWidget constructor. */ TQSlider::TQSlider( int minValue, int maxValue, int pageStep, int value, Qt::Orientation orientation, TQWidget *tqparent, const char *name ) : TQWidget( tqparent, name ), TQRangeControl( minValue, maxValue, 1, pageStep, value ) { orient = orientation; init(); sliderVal = value; } /*! Destructor. */ TQSlider::~TQSlider() { delete d; } void TQSlider::init() { d = new TQSliderPrivate; timer = 0; sliderPos = 0; sliderVal = 0; clickOffset = 0; state = Idle; track = TRUE; ticks = NoMarks; tickInt = 0; setFocusPolicy( Qt::TabFocus ); initTicks(); TQSizePolicy sp( TQSizePolicy::Expanding, TQSizePolicy::Fixed ); if ( orient == Qt::Vertical ) sp.transpose(); tqsetSizePolicy( sp ); clearWState( TQt::WState_OwnSizePolicy ); } /* Does what's needed when someone changes the tickmark status. */ void TQSlider::initTicks() { tickOffset = tqstyle().tqpixelMetric( TQStyle::PM_SliderTickmarkOffset, this ); } /*! \property TQSlider::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 TQSlider::setTracking( bool enable ) { track = enable; } /*! \fn void TQSlider::valueChanged( int value ) This signal is emitted when the slider value is changed, with the new slider \a value as its argument. */ /*! \fn void TQSlider::sliderPressed() This signal is emitted when the user presses the slider with the mouse. */ /*! \fn void TQSlider::sliderMoved( int value ) This signal is emitted when the slider is dragged, with the new slider \a value as its argument. */ /*! \fn void TQSlider::sliderReleased() This signal is emitted when the user releases the slider with the mouse. */ /* Calculates slider position corresponding to value \a v. */ int TQSlider::positionFromValue( int v ) const { int a = available(); int x = TQRangeControl::positionFromValue( v, a ); if ( orient == Qt::Horizontal && TQApplication::reverseLayout() ) x = a - x; return x; } /* Returns the available space in which the slider can move. */ int TQSlider::available() const { return tqstyle().tqpixelMetric( TQStyle::PM_SliderSpaceAvailable, this ); } /* Calculates a value corresponding to slider position \a p. */ int TQSlider::valueFromPosition( int p ) const { int a = available(); int x = TQRangeControl::valueFromPosition( p, a ); if ( orient == Qt::Horizontal && TQApplication::reverseLayout() ) x = maxValue() + minValue() - x; return x; } /*! Implements the virtual TQRangeControl function. */ void TQSlider::rangeChange() { int newPos = positionFromValue( value() ); if ( newPos != sliderPos ) { reallyMoveSlider( newPos ); } } /*! Implements the virtual TQRangeControl function. */ void TQSlider::valueChange() { if ( sliderVal != value() ) { int newPos = positionFromValue( value() ); sliderVal = value(); reallyMoveSlider( newPos ); } emit valueChanged(value()); #if defined(TQT_ACCESSIBILITY_SUPPORT) TQAccessible::updateAccessibility( this, 0, TQAccessible::ValueChanged ); #endif } /*! \reimp */ void TQSlider::resizeEvent( TQResizeEvent * ) { rangeChange(); initTicks(); } /*! Reimplements the virtual function TQWidget::setPalette(). Sets the background color to the mid color for Motif style sliders using palette \a p. */ void TQSlider::setPalette( const TQPalette &p ) { TQWidget::setPalette( p ); } /*! \property TQSlider::orientation \brief the slider's orientation The orientation must be \l Qt::Vertical (the default) or \l Qt::Horizontal. */ void TQSlider::setOrientation( Qt::Orientation orientation ) { if ( orientation == orient ) return; if ( !testWState( TQt::WState_OwnSizePolicy ) ) { TQSizePolicy sp = sizePolicy(); sp.transpose(); tqsetSizePolicy( sp ); clearWState( TQt::WState_OwnSizePolicy ); } orient = orientation; rangeChange(); update(); } /*! \fn int TQSlider::sliderStart() const Returns the start position of the slider. */ /*! Returns the slider handle rectangle. (This is the visual marker that the user can move.) */ TQRect TQSlider::sliderRect() const { return tqstyle().querySubControlMetrics( TQStyle::CC_Slider, this, TQStyle::SC_SliderHandle ); } /* Performs the actual moving of the slider. */ void TQSlider::reallyMoveSlider( int newPos ) { TQRegion oldR(sliderRect()); sliderPos = newPos; TQRegion newR(sliderRect()); /* just the one tqrepaint if no background */ if (backgroundMode() == TQt::NoBackground) tqrepaint(newR | oldR, FALSE); else { tqrepaint(oldR.subtract(newR)); tqrepaint(newR, FALSE); } } /*! \reimp */ void TQSlider::paintEvent( TQPaintEvent * ) { TQPainter p( this ); TQStyle::SFlags flags = TQStyle::Style_Default; if (isEnabled()) flags |= TQStyle::Style_Enabled; if (hasFocus()) flags |= TQStyle::Style_HasFocus; TQStyle::SCFlags sub = TQStyle::SC_SliderGroove | TQStyle::SC_SliderHandle; if ( tickmarks() != NoMarks ) sub |= TQStyle::SC_SliderTickmarks; tqstyle().tqdrawComplexControl( TQStyle::CC_Slider, &p, this, rect(), tqcolorGroup(), flags, sub, state == Dragging ? TQStyle::SC_SliderHandle : TQStyle::SC_None ); } /*! \reimp */ void TQSlider::mousePressEvent( TQMouseEvent *e ) { int slideLength = tqstyle().tqpixelMetric( TQStyle::PM_SliderLength, this ); resetState(); d->sliderStartVal = sliderVal; TQRect r = sliderRect(); if ( e->button() == Qt::RightButton ) return; if ( r.contains( e->pos() ) ) { state = Dragging; clickOffset = (TQCOORD)( goodPart( e->pos() ) - sliderPos ); emit sliderPressed(); } else if ( e->button() == Qt::MidButton ) { int pos = goodPart( e->pos() ); moveSlider( pos - slideLength / 2 ); state = Dragging; clickOffset = slideLength / 2; } else if ( (orient == Qt::Horizontal && e->pos().x() < r.left()) //### goodPart || (orient == Qt::Vertical && e->pos().y() < r.top()) ) { if ( orient == Qt::Horizontal && TQApplication::reverseLayout() ) { state = TimingUp; addPage(); } else { state = TimingDown; subtractPage(); } if ( !timer ) timer = new TQTimer( this ); connect( timer, TQT_SIGNAL(timeout()), TQT_SLOT(repeatTimeout()) ); timer->start( thresholdTime, TRUE ); } else if ( (orient == Qt::Horizontal && e->pos().x() > r.right()) //### goodPart || (orient == Qt::Vertical && e->pos().y() > r.bottom()) ) { if ( orient == Qt::Horizontal && TQApplication::reverseLayout() ) { state = TimingDown; subtractPage(); } else { state = TimingUp; addPage(); } if ( !timer ) timer = new TQTimer( this ); connect( timer, TQT_SIGNAL(timeout()), TQT_SLOT(repeatTimeout()) ); timer->start( thresholdTime, TRUE ); } update( sliderRect() ); } /*! \reimp */ void TQSlider::mouseMoveEvent( TQMouseEvent *e ) { if ( state != Dragging ) return; TQRect r = rect(); int m = tqstyle().tqpixelMetric( TQStyle::PM_MaximumDragDistance, this ); if ( m >= 0 ) { if ( orientation() == Qt::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 TQT_NO_WHEELEVENT void TQSlider::wheelEvent( TQWheelEvent * e ) { if ( e->orientation() != orientation() && !TQT_TQRECT_OBJECT(rect()).contains(e->pos()) ) return; static float offset = 0; static TQSlider* offset_owner = 0; if (offset_owner != this){ offset_owner = this; offset = 0; } offset += -e->delta()*TQMAX(pageStep(),lineStep())/120; if (TQABS(offset)<1) return; setValue( value() + int(offset) ); offset -= int(offset); e->accept(); } #endif /*! \reimp */ void TQSlider::mouseReleaseEvent( TQMouseEvent * ) { resetState(); update( sliderRect() ); } /*! \reimp */ void TQSlider::focusInEvent( TQFocusEvent * e) { TQWidget::focusInEvent( e ); } /*! \reimp */ void TQSlider::focusOutEvent( TQFocusEvent * e ) { TQWidget::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 TQSlider::moveSlider( int pos ) { int a = available(); int newPos = TQMIN( a, TQMAX( 0, pos ) ); int newVal = valueFromPosition( newPos ); if (tqstyle().tqstyleHint(TQStyle::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 TQSlider::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("TQSlider: (%s) in wrong state", name( "unnamed" ) ); } state = Idle; } /*! \reimp */ void TQSlider::keyPressEvent( TQKeyEvent *e ) { bool sloppy = bool(tqstyle().tqstyleHint(TQStyle::SH_Slider_SloppyKeyEvents, this)); switch ( e->key() ) { case Qt::Key_Left: if ( sloppy || orient == Qt::Horizontal ) { if (TQApplication::reverseLayout()) addLine(); else subtractLine(); } break; case Qt::Key_Right: if ( sloppy || orient == Qt::Horizontal ) { if (TQApplication::reverseLayout()) subtractLine(); else addLine(); } break; case Qt::Key_Up: if ( sloppy || orient == Qt::Vertical ) subtractLine(); break; case Qt::Key_Down: if ( sloppy || orient == Qt::Vertical ) addLine(); break; case TQt::Key_Prior: subtractPage(); break; case TQt::Key_Next: addPage(); break; case Qt::Key_Home: setValue( minValue() ); break; case Qt::Key_End: setValue( maxValue() ); break; default: e->ignore(); return; } } void TQSlider::setValue( int value ) { TQRangeControl::setValue( value ); #if defined(TQT_ACCESSIBILITY_SUPPORT) TQAccessible::updateAccessibility( this, 0, TQAccessible::ValueChanged ); #endif } /*! \reimp */ void TQSlider::addLine() { TQRangeControl::addLine(); } /*! \reimp */ void TQSlider::subtractLine() { TQRangeControl::subtractLine(); } /*! Moves the slider one pageStep() up or right. */ void TQSlider::addStep() { addPage(); } /*! Moves the slider one pageStep() down or left. */ void TQSlider::subtractStep() { subtractPage(); } /* Waits for autorepeat. */ void TQSlider::repeatTimeout() { TQ_ASSERT( timer ); timer->disconnect(); if ( state == TimingDown ) connect( timer, TQT_SIGNAL(timeout()), TQT_SLOT(subtractStep()) ); else if ( state == TimingUp ) connect( timer, TQT_SIGNAL(timeout()), TQT_SLOT(addStep()) ); timer->start( repeatTime, FALSE ); } /* Returns the relevant dimension of \a p. */ int TQSlider::goodPart( const TQPoint &p ) const { return (orient == Qt::Horizontal) ? p.x() : p.y(); } /*! \reimp */ TQSize TQSlider::tqsizeHint() const { constPolish(); const int length = 84, tickSpace = 5; int thick = tqstyle().tqpixelMetric( TQStyle::PM_SliderThickness, this ); if ( ticks & Above ) thick += tickSpace; if ( ticks & Below ) thick += tickSpace; int w = thick, h = length; if ( orient == Qt::Horizontal ) { w = length; h = thick; } return (tqstyle().tqsizeFromContents(TQStyle::CT_Slider, this, TQSize(w, h)).expandedTo(TQApplication::globalStrut())); } /*! \reimp */ TQSize TQSlider::tqminimumSizeHint() const { TQSize s = tqsizeHint(); int length = tqstyle().tqpixelMetric(TQStyle::PM_SliderLength, this); if ( orient == Qt::Horizontal ) s.setWidth( length ); else s.setHeight( length ); return s; } /*! \fn void TQSlider::tqsetSizePolicy( TQSizePolicy::SizeType, TQSizePolicy::SizeType, bool ) \reimp */ /*! \reimp */ void TQSlider::tqsetSizePolicy( TQSizePolicy sp ) { // ## remove 4.0 TQWidget::tqsetSizePolicy( sp ); } /*! \reimp */ TQSizePolicy TQSlider::sizePolicy() const { // ### 4.0 remove this reimplementation return TQWidget::tqsizePolicy(); } /*! \property TQSlider::tickmarks \brief the tickmark settings for this slider The valid values are in \l{TQSlider::TickSetting}. The default is \c NoMarks. \sa tickInterval */ void TQSlider::setTickmarks( TickSetting s ) { ticks = s; initTicks(); update(); } /*! \property TQSlider::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 TQRangeControl::lineStep(), TQRangeControl::pageStep() */ void TQSlider::setTickInterval( int i ) { tickInt = TQMAX( 0, i ); update(); } /*! \reimp */ void TQSlider::styleChange( TQStyle& old ) { TQWidget::styleChange( old ); } /*! \property TQSlider::minValue \brief the current minimum value of the slider When setting this property, the \l TQSlider::maxValue is adjusted, if necessary, to ensure that the range remains valid. \sa setRange() */ int TQSlider::minValue() const { return TQRangeControl::minValue(); } /*! \property TQSlider::maxValue \brief the current maximum value of the slider When setting this property, the \l TQSlider::minValue is adjusted, if necessary, to ensure that the range remains valid. \sa setRange() */ int TQSlider::maxValue() const { return TQRangeControl::maxValue(); } void TQSlider::setMinValue( int minVal ) { TQRangeControl::setMinValue( minVal ); } void TQSlider::setMaxValue( int maxVal ) { TQRangeControl::setMaxValue( maxVal ); } /*! \property TQSlider::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() TQRangeControl::pageStep() setRange() */ int TQSlider::lineStep() const { return TQRangeControl::lineStep(); } /*! \property TQSlider::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 TQRangeControl::setSteps() setLineStep() setRange() */ int TQSlider::pageStep() const { return TQRangeControl::pageStep(); } void TQSlider::setLineStep( int i ) { setSteps( i, pageStep() ); } void TQSlider::setPageStep( int i ) { setSteps( lineStep(), i ); } /*! \property TQSlider::value \brief the current slider value \sa TQRangeControl::value() prevValue() */ int TQSlider::value() const { return TQRangeControl::value(); } #endif