Qwt User's Guide 6.3.0
Loading...
Searching...
No Matches
qwt_abstract_slider.cpp
1/******************************************************************************
2 * Qwt Widget Library
3 * Copyright (C) 1997 Josef Wilgen
4 * Copyright (C) 2002 Uwe Rathmann
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the Qwt License, Version 1.0
8 *****************************************************************************/
9
10#include "qwt_abstract_slider.h"
11#include "qwt_scale_map.h"
12#include "qwt_scale_div.h"
13#include "qwt_math.h"
14
15#include <qevent.h>
16
17static double qwtAlignToScaleDiv(
18 const QwtAbstractSlider* slider, double value )
19{
20 const QwtScaleDiv& sd = slider->scaleDiv();
21
22 const int tValue = slider->transform( value );
23
24 if ( tValue == slider->transform( sd.lowerBound() ) )
25 return sd.lowerBound();
26
27 if ( tValue == slider->transform( sd.upperBound() ) )
28 return sd.upperBound();
29
30 for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
31 {
32 const QList< double > ticks = sd.ticks( i );
33 for ( int j = 0; j < ticks.size(); j++ )
34 {
35 if ( slider->transform( ticks[ j ] ) == tValue )
36 return ticks[ j ];
37 }
38 }
39
40 return value;
41}
42
43class QwtAbstractSlider::PrivateData
44{
45 public:
46 PrivateData()
47 : isScrolling( false )
48 , isTracking( true )
49 , pendingValueChanged( false )
50 , readOnly( false )
51 , totalSteps( 100 )
52 , singleSteps( 1 )
53 , pageSteps( 10 )
54 , stepAlignment( true )
55 , isValid( false )
56 , value( 0.0 )
57 , wrapping( false )
58 , invertedControls( false )
59 {
60 }
61
62 bool isScrolling;
63 bool isTracking;
64 bool pendingValueChanged;
65
66 bool readOnly;
67
68 uint totalSteps;
69 uint singleSteps;
70 uint pageSteps;
71 bool stepAlignment;
72
73 bool isValid;
74 double value;
75
76 bool wrapping;
77 bool invertedControls;
78};
79
92 : QwtAbstractScale( parent )
93{
94 m_data = new QwtAbstractSlider::PrivateData;
95
96 setScale( 0.0, 100.0 );
97 setFocusPolicy( Qt::StrongFocus );
98}
99
102{
103 delete m_data;
104}
105
114{
115 if ( on != m_data->isValid )
116 {
117 m_data->isValid = on;
118 sliderChange();
119
120 Q_EMIT valueChanged( m_data->value );
121 }
122}
123
126{
127 return m_data->isValid;
128}
129
142{
143 if ( m_data->readOnly != on )
144 {
145 m_data->readOnly = on;
146 setFocusPolicy( on ? Qt::StrongFocus : Qt::NoFocus );
147
148 update();
149 }
150}
151
160{
161 return m_data->readOnly;
162}
163
178{
179 m_data->isTracking = on;
180}
181
187{
188 return m_data->isTracking;
189}
190
195void QwtAbstractSlider::mousePressEvent( QMouseEvent* event )
196{
197 if ( isReadOnly() )
198 {
199 event->ignore();
200 return;
201 }
202
203 if ( !m_data->isValid || lowerBound() == upperBound() )
204 return;
205
206 m_data->isScrolling = isScrollPosition( event->pos() );
207
208 if ( m_data->isScrolling )
209 {
210 m_data->pendingValueChanged = false;
211
212 Q_EMIT sliderPressed();
213 }
214}
215
220void QwtAbstractSlider::mouseMoveEvent( QMouseEvent* event )
221{
222 if ( isReadOnly() )
223 {
224 event->ignore();
225 return;
226 }
227
228 if ( m_data->isValid && m_data->isScrolling )
229 {
230 double value = scrolledTo( event->pos() );
231 if ( value != m_data->value )
232 {
233 value = boundedValue( value );
234
235 if ( m_data->stepAlignment )
236 {
237 value = alignedValue( value );
238 }
239 else
240 {
241 value = qwtAlignToScaleDiv( this, value );
242 }
243
244 if ( value != m_data->value )
245 {
246 m_data->value = value;
247
248 sliderChange();
249
250 Q_EMIT sliderMoved( m_data->value );
251
252 if ( m_data->isTracking )
253 Q_EMIT valueChanged( m_data->value );
254 else
255 m_data->pendingValueChanged = true;
256 }
257 }
258 }
259}
260
265void QwtAbstractSlider::mouseReleaseEvent( QMouseEvent* event )
266{
267 if ( isReadOnly() )
268 {
269 event->ignore();
270 return;
271 }
272
273 if ( m_data->isScrolling && m_data->isValid )
274 {
275 m_data->isScrolling = false;
276
277 if ( m_data->pendingValueChanged )
278 Q_EMIT valueChanged( m_data->value );
279
280 Q_EMIT sliderReleased();
281 }
282}
283
296void QwtAbstractSlider::wheelEvent( QWheelEvent* event )
297{
298 if ( isReadOnly() )
299 {
300 event->ignore();
301 return;
302 }
303
304 if ( !m_data->isValid || m_data->isScrolling )
305 return;
306
307#if QT_VERSION < 0x050000
308 const int wheelDelta = event->delta();
309#else
310 const QPoint delta = event->angleDelta();
311 const int wheelDelta = ( qAbs( delta.x() ) > qAbs( delta.y() ) )
312 ? delta.x() : delta.y();
313#endif
314
315 int numSteps = 0;
316
317 if ( ( event->modifiers() & Qt::ControlModifier ) ||
318 ( event->modifiers() & Qt::ShiftModifier ) )
319 {
320 // one page regardless of delta
321 numSteps = m_data->pageSteps;
322 if ( wheelDelta < 0 )
323 numSteps = -numSteps;
324 }
325 else
326 {
327 const int numTurns = ( wheelDelta / 120 );
328 numSteps = numTurns * m_data->singleSteps;
329 }
330
331 if ( m_data->invertedControls )
332 numSteps = -numSteps;
333
334 const double value = incrementedValue( m_data->value, numSteps );
335 if ( value != m_data->value )
336 {
337 m_data->value = value;
338 sliderChange();
339
340 Q_EMIT sliderMoved( m_data->value );
341 Q_EMIT valueChanged( m_data->value );
342 }
343}
344
370void QwtAbstractSlider::keyPressEvent( QKeyEvent* event )
371{
372 if ( isReadOnly() )
373 {
374 event->ignore();
375 return;
376 }
377
378 if ( !m_data->isValid || m_data->isScrolling )
379 return;
380
381 int numSteps = 0;
382 double value = m_data->value;
383
384 switch ( event->key() )
385 {
386 case Qt::Key_Left:
387 {
388 numSteps = -static_cast< int >( m_data->singleSteps );
389 if ( isInverted() )
390 numSteps = -numSteps;
391
392 break;
393 }
394 case Qt::Key_Right:
395 {
396 numSteps = m_data->singleSteps;
397 if ( isInverted() )
398 numSteps = -numSteps;
399
400 break;
401 }
402 case Qt::Key_Down:
403 {
404 numSteps = -static_cast< int >( m_data->singleSteps );
405 if ( m_data->invertedControls )
406 numSteps = -numSteps;
407 break;
408 }
409 case Qt::Key_Up:
410 {
411 numSteps = m_data->singleSteps;
412 if ( m_data->invertedControls )
413 numSteps = -numSteps;
414
415 break;
416 }
417 case Qt::Key_PageUp:
418 {
419 numSteps = m_data->pageSteps;
420 if ( m_data->invertedControls )
421 numSteps = -numSteps;
422 break;
423 }
424 case Qt::Key_PageDown:
425 {
426 numSteps = -static_cast< int >( m_data->pageSteps );
427 if ( m_data->invertedControls )
428 numSteps = -numSteps;
429 break;
430 }
431 case Qt::Key_Home:
432 {
433 value = minimum();
434 break;
435 }
436 case Qt::Key_End:
437 {
438 value = maximum();
439 break;
440 }
441 default:
442 {
443 event->ignore();
444 }
445 }
446
447 if ( numSteps != 0 )
448 {
449 value = incrementedValue( m_data->value, numSteps );
450 }
451
452 if ( value != m_data->value )
453 {
454 m_data->value = value;
455 sliderChange();
456
457 Q_EMIT sliderMoved( m_data->value );
458 Q_EMIT valueChanged( m_data->value );
459 }
460}
461
475{
476 m_data->totalSteps = stepCount;
477}
478
484{
485 return m_data->totalSteps;
486}
487
500{
501 m_data->singleSteps = stepCount;
502}
503
509{
510 return m_data->singleSteps;
511}
512
525{
526 m_data->pageSteps = stepCount;
527}
528
534{
535 return m_data->pageSteps;
536}
537
548{
549 if ( on != m_data->stepAlignment )
550 {
551 m_data->stepAlignment = on;
552 }
553}
554
560{
561 return m_data->stepAlignment;
562}
563
570void QwtAbstractSlider::setValue( double value )
571{
572 value = qBound( minimum(), value, maximum() );
573
574 const bool changed = ( m_data->value != value ) || !m_data->isValid;
575
576 m_data->value = value;
577 m_data->isValid = true;
578
579 if ( changed )
580 {
581 sliderChange();
582 Q_EMIT valueChanged( m_data->value );
583 }
584}
585
588{
589 return m_data->value;
590}
591
600{
601 m_data->wrapping = on;
602}
603
609{
610 return m_data->wrapping;
611}
612
629{
630 m_data->invertedControls = on;
631}
632
638{
639 return m_data->invertedControls;
640}
641
651{
652 const double value = incrementedValue(
653 m_data->value, stepCount );
654
655 if ( value != m_data->value )
656 {
657 m_data->value = value;
658 sliderChange();
659 }
660}
661
671 double value, int stepCount ) const
672{
673 if ( m_data->totalSteps == 0 )
674 return value;
675
676 const QwtTransform* transformation =
678
679 if ( transformation == NULL )
680 {
681 const double range = maximum() - minimum();
682 value += stepCount * range / m_data->totalSteps;
683 }
684 else
685 {
686 QwtScaleMap map = scaleMap();
687 map.setPaintInterval( 0, m_data->totalSteps );
688
689 // we need equidistant steps according to
690 // paint device coordinates
691 const double range = transformation->transform( maximum() )
692 - transformation->transform( minimum() );
693
694 const double stepSize = range / m_data->totalSteps;
695
696 double v = transformation->transform( value );
697
698 v = qRound( v / stepSize ) * stepSize;
699 v += stepCount * range / m_data->totalSteps;
700
701 value = transformation->invTransform( v );
702 }
703
704 value = boundedValue( value );
705
706 if ( m_data->stepAlignment )
707 value = alignedValue( value );
708
709 return value;
710}
711
712double QwtAbstractSlider::boundedValue( double value ) const
713{
714 const double vmin = minimum();
715 const double vmax = maximum();
716
717 if ( m_data->wrapping && vmin != vmax )
718 {
719 if ( qFuzzyCompare( scaleMap().pDist(), 360.0 ) )
720 {
721 // full circle scales: min and max are the same
722
723 if ( qFuzzyCompare( value, vmax ) )
724 {
725 value = vmin;
726 }
727 else
728 {
729 const double range = vmax - vmin;
730
731 if ( value < vmin )
732 {
733 value += std::ceil( ( vmin - value ) / range ) * range;
734 }
735 else if ( value > vmax )
736 {
737 value -= std::ceil( ( value - vmax ) / range ) * range;
738 }
739 }
740 }
741 else
742 {
743 if ( value < vmin )
744 value = vmax;
745 else if ( value > vmax )
746 value = vmin;
747 }
748 }
749 else
750 {
751 value = qBound( vmin, value, vmax );
752 }
753
754 return value;
755}
756
757double QwtAbstractSlider::alignedValue( double value ) const
758{
759 if ( m_data->totalSteps == 0 )
760 return value;
761
762 double stepSize;
763
764 if ( scaleMap().transformation() == NULL )
765 {
766 stepSize = ( maximum() - minimum() ) / m_data->totalSteps;
767 if ( stepSize > 0.0 )
768 {
769 value = lowerBound() +
770 qRound( ( value - lowerBound() ) / stepSize ) * stepSize;
771 }
772 }
773 else
774 {
775 stepSize = ( scaleMap().p2() - scaleMap().p1() ) / m_data->totalSteps;
776
777 if ( stepSize > 0.0 )
778 {
779 double v = scaleMap().transform( value );
780
781 v = scaleMap().p1() +
782 qRound( ( v - scaleMap().p1() ) / stepSize ) * stepSize;
783
784 value = scaleMap().invTransform( v );
785 }
786 }
787
788 if ( qAbs( stepSize ) > 1e-12 )
789 {
790 if ( qFuzzyCompare( value + 1.0, 1.0 ) )
791 {
792 // correct rounding error if value = 0
793 value = 0.0;
794 }
795 else
796 {
797 // correct rounding error at the border
798 if ( qFuzzyCompare( value, upperBound() ) )
799 value = upperBound();
800 else if ( qFuzzyCompare( value, lowerBound() ) )
801 value = lowerBound();
802 }
803 }
804
805 return value;
806}
807
812{
813 const double value = qBound( minimum(), m_data->value, maximum() );
814
815 const bool changed = ( value != m_data->value );
816 if ( changed )
817 {
818 m_data->value = value;
819 }
820
821 if ( m_data->isValid || changed )
822 Q_EMIT valueChanged( m_data->value );
823
824 updateGeometry();
825 update();
826}
827
830{
831 update();
832}
833
834#include "moc_qwt_abstract_slider.cpp"
An abstract base class for widgets having a scale.
const QwtScaleMap & scaleMap() const
double lowerBound() const
const QwtScaleDiv & scaleDiv() const
int transform(double) const
double upperBound() const
void setScale(double lowerBound, double upperBound)
Specify a scale.
An abstract base class for slider widgets with a scale.
virtual void scaleChange() override
virtual void mouseReleaseEvent(QMouseEvent *) override
virtual ~QwtAbstractSlider()
Destructor.
virtual void sliderChange()
Calling update()
virtual void keyPressEvent(QKeyEvent *) override
void setTracking(bool)
Enables or disables tracking.
virtual void mousePressEvent(QMouseEvent *) override
void setTotalSteps(uint)
Set the number of steps.
void valueChanged(double value)
Notify a change of value.
void setValue(double value)
double incrementedValue(double value, int stepCount) const
void setStepAlignment(bool)
Enable step alignment.
void incrementValue(int stepCount)
virtual void mouseMoveEvent(QMouseEvent *) override
virtual void wheelEvent(QWheelEvent *) override
void setSingleSteps(uint)
Set the number of steps for a single increment.
double value() const
Returns the current value.
void setPageSteps(uint)
Set the number of steps for a page increment.
void sliderMoved(double value)
virtual bool isScrollPosition(const QPoint &pos) const =0
Determine what to do when the user presses a mouse button.
virtual double scrolledTo(const QPoint &pos) const =0
Determine the value for a new position of the movable part of the slider.
QwtAbstractSlider(QWidget *parent=NULL)
Constructor.
A class representing a scale division.
double lowerBound() const
double upperBound() const
QList< double > ticks(int tickType) const
@ NTickTypes
Number of valid tick types.
A scale map.
double p1() const
double transform(double s) const
const QwtTransform * transformation() const
Get the transformation.
void setPaintInterval(double p1, double p2)
Specify the borders of the paint device interval.
double invTransform(double p) const
double p2() const
A transformation between coordinate systems.
virtual double transform(double value) const =0
virtual double invTransform(double value) const =0