Newer
Older
/* -*- 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_abstract_slider.h"
static double qwtAlignToScaleDiv(
const QwtAbstractSlider *slider, double value )
{
const QwtScaleDiv &sd = slider->scaleDiv();
const int tValue = slider->transform( value );
if ( tValue == slider->transform( sd.lowerBound() ) )
return sd.lowerBound();
if ( tValue == slider->transform( sd.lowerBound() ) )
return sd.upperBound();
for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
{
const QList<double> ticks = sd.ticks( i );
for ( int j = 0; j < ticks.size(); j++ )
{
if ( slider->transform( ticks[ j ] ) == tValue )
return ticks[ j ];
}
}
return value;
}
class QwtAbstractSlider::PrivateData
{
public:
PrivateData():
isScrolling( false ),
isTracking( true ),
pendingValueChanged( false ),
readOnly( false ),
totalSteps( 100 ),
singleSteps( 1 ),
pageSteps( 10 ),
stepAlignment( true ),
isValid( false ),
value( 0.0 ),
wrapping( false ),
invertedControls( false )
{
bool isScrolling;
bool isTracking;
bool pendingValueChanged;
uint totalSteps;
uint singleSteps;
uint pageSteps;
bool stepAlignment;
bool isValid;
double value;
bool wrapping;
bool invertedControls;
\brief Constructor
The scale is initialized to [0.0, 100.0], the
number of steps is set to 100 with 1 and 10 and single
an page step sizes. Step alignment is enabled.
The initial value is invalid.
QwtAbstractSlider::QwtAbstractSlider( QWidget *parent ):
QwtAbstractScale( parent )
setScale( 0.0, 100.0 );
setFocusPolicy( Qt::StrongFocus );
}
//! Destructor
QwtAbstractSlider::~QwtAbstractSlider()
{
delete d_data;
}
/*!
Set the value to be valid/invalid
\param on When true, the value is invalidated
\sa setValue()
*/
void QwtAbstractSlider::setValid( bool on )
{
if ( on != d_data->isValid )
{
d_data->isValid = on;
sliderChange();
Q_EMIT valueChanged( d_data->value );
}
}
//! \return True, when the value is invalid
bool QwtAbstractSlider::isValid() const
{
return d_data->isValid;
}
/*!
En/Disable read only mode
In read only mode the slider can't be controlled by mouse
or keyboard.
\warning The focus policy is set to Qt::StrongFocus or Qt::NoFocus
if ( d_data->readOnly != on )
{
d_data->readOnly = on;
setFocusPolicy( on ? Qt::StrongFocus : Qt::NoFocus );
update();
}
}
/*!
In read only mode the slider can't be controlled by mouse
or keyboard.
\return true if read only
\sa setReadOnly()
*/
bool QwtAbstractSlider::isReadOnly() const
{
return d_data->readOnly;
}
/*!
If tracking is enabled, the slider emits the valueChanged()
signal while the movable part of the slider is being dragged.
If tracking is disabled, the slider emits the valueChanged() signal
only when the user releases the slider.
Tracking is enabled by default.
\param on \c true (enable) or \c false (disable) tracking.
\sa isTracking(), sliderMoved()
*/
void QwtAbstractSlider::setTracking( bool on )
\return True, when tracking has been enabled
\sa setTracking()
/*!
Mouse press event handler
\param event Mouse event
*/
void QwtAbstractSlider::mousePressEvent( QMouseEvent *event )
if ( !d_data->isValid || lowerBound() == upperBound() )
return;
if ( d_data->isScrolling )
{
d_data->pendingValueChanged = false;
/*!
Mouse Move Event handler
\param event Mouse event
*/
void QwtAbstractSlider::mouseMoveEvent( QMouseEvent *event )
if ( d_data->isValid && d_data->isScrolling )
{
double value = scrolledTo( event->pos() );
if ( value != d_data->value )
{
value = boundedValue( value );
if ( d_data->stepAlignment )
{
value = alignedValue( value );
}
else
{
value = qwtAlignToScaleDiv( this, value );
}
if ( value != d_data->value )
{
d_data->value = value;
if ( d_data->isTracking )
Q_EMIT valueChanged( d_data->value );
else
d_data->pendingValueChanged = true;
}
}
void QwtAbstractSlider::mouseReleaseEvent( QMouseEvent *event )
if ( isReadOnly() )
{
event->ignore();
return;
}
if ( d_data->isScrolling && d_data->isValid )
{
d_data->isScrolling = false;
if ( d_data->pendingValueChanged )
Q_EMIT valueChanged( d_data->value );
Wheel Event handler
In/decreases the value by s number of steps. The direction
depends on the invertedControls() property.
When the control or shift modifier is pressed the wheel delta
( divided by 120 ) is mapped to an increment according to
pageSteps(). Otherwise it is mapped to singleSteps().
\param event Wheel event
if ( ( event->modifiers() & Qt::ControlModifier) ||
( event->modifiers() & Qt::ShiftModifier ) )
{
// one page regardless of delta
numSteps = d_data->pageSteps;
if ( event->delta() < 0 )
numSteps = -numSteps;
}
else
{
const int numTurns = ( event->delta() / 120 );
numSteps = numTurns * d_data->singleSteps;
const double value = incrementedValue( d_data->value, numSteps );
if ( value != d_data->value )
{
d_data->value = value;
sliderChange();
Q_EMIT sliderMoved( d_data->value );
Q_EMIT valueChanged( d_data->value );
QwtAbstractSlider handles the following keys:
- Qt::Key_Left\n
Add/Subtract singleSteps() in direction to lowerBound();
- Qt::Key_Right\n
Add/Subtract singleSteps() in direction to upperBound();
- Qt::Key_Down\n
Subtract singleSteps(), when invertedControls() is false
- Qt::Key_Up\n
Add singleSteps(), when invertedControls() is false
- Qt::Key_PageDown\n
Subtract pageSteps(), when invertedControls() is false
- Qt::Key_PageUp\n
Add pageSteps(), when invertedControls() is false
- Qt::Key_Home\n
Set the value to the minimum()
- Qt::Key_End\n
Set the value to the maximum()
\param event Key event
switch ( event->key() )
{
case Qt::Key_Left:
{
numSteps = -static_cast<int>( d_data->singleSteps );
if ( isInverted() )
numSteps = -numSteps;
break;
}
case Qt::Key_Right:
{
numSteps = d_data->singleSteps;
if ( isInverted() )
numSteps = -numSteps;
break;
}
case Qt::Key_Down:
{
numSteps = -static_cast<int>( d_data->singleSteps );
if ( d_data->invertedControls )
numSteps = -numSteps;
break;
}
case Qt::Key_Up:
{
numSteps = d_data->singleSteps;
if ( d_data->invertedControls )
numSteps = -numSteps;
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
case Qt::Key_PageUp:
{
numSteps = d_data->pageSteps;
if ( d_data->invertedControls )
numSteps = -numSteps;
break;
}
case Qt::Key_PageDown:
{
numSteps = -static_cast<int>( d_data->pageSteps );
if ( d_data->invertedControls )
numSteps = -numSteps;
break;
}
case Qt::Key_Home:
{
value = minimum();
break;
}
case Qt::Key_End:
{
value = maximum();
break;
}
default:;
{
event->ignore();
if ( numSteps != 0 )
{
value = incrementedValue( d_data->value, numSteps );
if ( value != d_data->value )
{
d_data->value = value;
sliderChange();
Q_EMIT sliderMoved( d_data->value );
Q_EMIT valueChanged( d_data->value );
/*!
\brief Set the number of steps
The range of the slider is divided into a number of steps from
which the value increments according to user inputs depend.
The default setting is 100.
\param stepCount Number of steps
\sa totalSteps(), setSingleSteps(), setPageSteps()
*/
void QwtAbstractSlider::setTotalSteps( uint stepCount )
{
d_data->totalSteps = stepCount;
/*!
\return Number of steps
\sa setTotalSteps(), singleSteps(), pageSteps()
*/
uint QwtAbstractSlider::totalSteps() const
{
return d_data->totalSteps;
}
The range of the slider is divided into a number of steps from
which the value increments according to user inputs depend.
\param stepCount Number of steps
\sa singleSteps(), setTotalSteps(), setPageSteps()
*/
void QwtAbstractSlider::setSingleSteps( uint stepCount )
\return Number of steps
\sa setSingleSteps(), totalSteps(), pageSteps()
*/
uint QwtAbstractSlider::singleSteps() const
{
return d_data->singleSteps;
}
/*!
\brief Set the number of steps for a page increment
The range of the slider is divided into a number of steps from
which the value increments according to user inputs depend.
\param stepCount Number of steps
\sa pageSteps(), setTotalSteps(), setSingleSteps()
*/
void QwtAbstractSlider::setPageSteps( uint stepCount )
{
d_data->pageSteps = stepCount;
}
/*!
\return Number of steps
\sa setPageSteps(), totalSteps(), singleSteps()
*/
uint QwtAbstractSlider::pageSteps() const
{
return d_data->pageSteps;
}
When step alignment is enabled values resulting from slider
movements are aligned to the step size.
\param on Enable step alignment when true
\sa stepAlignment()
void QwtAbstractSlider::setStepAlignment( bool on )
{
if ( on != d_data->stepAlignment )
{
d_data->stepAlignment = on;
}
}
/*!
\return True, when step alignment is enabled
\sa setStepAlignment()
*/
bool QwtAbstractSlider::stepAlignment() const
Set the slider to the specified value
\param value New value
\sa setValid(), sliderChange(), valueChanged()
value = qBound( minimum(), value, maximum() );
const bool changed = ( d_data->value != value ) || !d_data->isValid;
d_data->value = value;
d_data->isValid = true;
if ( changed )
{
sliderChange();
Q_EMIT valueChanged( d_data->value );
}
//! Returns the current value.
double QwtAbstractSlider::value() const
{
return d_data->value;
}
If wrapping is true stepping up from upperBound() value will
take you to the minimum() value and vice versa.
void QwtAbstractSlider::setWrapping( bool on )
{
d_data->wrapping = on;
}
/*!
\return True, when wrapping is set
\sa setWrapping()
*/
bool QwtAbstractSlider::wrapping() const
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
/*!
Invert wheel and key events
Usually scrolling the mouse wheel "up" and using keys like page
up will increase the slider's value towards its maximum.
When invertedControls() is enabled the value is scrolled
towards its minimum.
Inverting the controls might be f.e. useful for a vertical slider
with an inverted scale ( decreasing from top to bottom ).
\param on Invert controls, when true
\sa invertedControls(), keyEvent(), wheelEvent()
*/
void QwtAbstractSlider::setInvertedControls( bool on )
{
d_data->invertedControls = on;
}
/*!
\return True, when the controls are inverted
\sa setInvertedControls()
*/
bool QwtAbstractSlider::invertedControls() const
{
return d_data->invertedControls;
}
The step size depends on the number of totalSteps()
\param stepCount Number of steps
\sa setTotalSteps(), incrementedValue()
*/
void QwtAbstractSlider::incrementValue( int stepCount )
const double value = incrementedValue(
d_data->value, stepCount );
if ( value != d_data->value )
{
d_data->value = value;
sliderChange();
}
Increment a value
\param value Value
\param stepCount Number of steps
\return Incremented value
*/
double QwtAbstractSlider::incrementedValue(
double value, int stepCount ) const
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
if ( d_data->totalSteps == 0 )
return value;
const QwtTransform *transformation =
scaleMap().transformation();
if ( transformation == NULL )
{
const double range = maximum() - minimum();
value += stepCount * range / d_data->totalSteps;
}
else
{
QwtScaleMap map = scaleMap();
map.setPaintInterval( 0, d_data->totalSteps );
// we need equidant steps according to
// paint device coordinates
const double range = transformation->transform( maximum() )
- transformation->transform( minimum() );
const double stepSize = range / d_data->totalSteps;
double v = transformation->transform( value );
v = qRound( v / stepSize ) * stepSize;
v += stepCount * range / d_data->totalSteps;
value = transformation->invTransform( v );
}
value = boundedValue( value );
if ( d_data->stepAlignment )
value = alignedValue( value );
return value;
}
double QwtAbstractSlider::boundedValue( double value ) const
{
const double vmin = minimum();
const double vmax = maximum();
if ( d_data->wrapping && vmin != vmax )
{
const int fullCircle = 360 * 16;
const double pd = scaleMap().pDist();
if ( int( pd / fullCircle ) * fullCircle == pd )
{
// full circle scales: min and max are the same
const double range = vmax - vmin;
if ( value < vmin )
{
value += ::ceil( ( vmin - value ) / range ) * range;
}
else if ( value > vmax )
{
value -= ::ceil( ( value - vmax ) / range ) * range;
}
}
else
{
if ( value < vmin )
value = vmax;
else if ( value > vmax )
value = vmin;
}
}
else
{
value = qBound( vmin, value, vmax );
}
return value;
double QwtAbstractSlider::alignedValue( double value ) const
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
if ( d_data->totalSteps == 0 )
return value;
double stepSize;
if ( scaleMap().transformation() == NULL )
{
stepSize = ( maximum() - minimum() ) / d_data->totalSteps;
if ( stepSize > 0.0 )
{
value = lowerBound() +
qRound( ( value - lowerBound() ) / stepSize ) * stepSize;
}
}
else
{
stepSize = ( scaleMap().p2() - scaleMap().p1() ) / d_data->totalSteps;
if ( stepSize > 0.0 )
{
double v = scaleMap().transform( value );
v = scaleMap().p1() +
qRound( ( v - scaleMap().p1() ) / stepSize ) * stepSize;
value = scaleMap().invTransform( v );
}
}
if ( qAbs( stepSize ) > 1e-12 )
{
if ( qFuzzyCompare( value + 1.0, 1.0 ) )
{
// correct rounding error if value = 0
value = 0.0;
}
else
{
// correct rounding error at the border
if ( qFuzzyCompare( value, upperBound() ) )
value = upperBound();
else if ( qFuzzyCompare( value, lowerBound() ) )
value = lowerBound();
}
}
return value;
/*!
Update the slider according to modifications of the scale
*/
void QwtAbstractSlider::scaleChange()
const double value = qBound( minimum(), d_data->value, maximum() );
const bool changed = ( value != d_data->value );
if ( changed )
{
d_data->value = value;
}
if ( d_data->isValid || changed )
Q_EMIT valueChanged( d_data->value );
updateGeometry();
update();
//! Calling update()
void QwtAbstractSlider::sliderChange()