1 /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
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_abstract_scale_draw.h"
12 #include "qwt_math.h"
13 #include "qwt_scale_map.h"
14 #include <qevent.h>
15 
16 #if QT_VERSION < 0x040601
17 #define qFabs(x) ::fabs(x)
18 #endif
19 
qwtAlignToScaleDiv(const QwtAbstractSlider * slider,double value)20 static double qwtAlignToScaleDiv(
21     const QwtAbstractSlider *slider, double value )
22 {
23     const QwtScaleDiv &sd = slider->scaleDiv();
24 
25     const int tValue = slider->transform( value );
26 
27     if ( tValue == slider->transform( sd.lowerBound() ) )
28         return sd.lowerBound();
29 
30     if ( tValue == slider->transform( sd.upperBound() ) )
31         return sd.upperBound();
32 
33     for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
34     {
35         const QList<double> ticks = sd.ticks( i );
36         for ( int j = 0; j < ticks.size(); j++ )
37         {
38             if ( slider->transform( ticks[ j ] ) == tValue )
39                 return ticks[ j ];
40         }
41     }
42 
43     return value;
44 }
45 
46 class QwtAbstractSlider::PrivateData
47 {
48 public:
PrivateData()49     PrivateData():
50         isScrolling( false ),
51         isTracking( true ),
52         pendingValueChanged( false ),
53         readOnly( false ),
54         totalSteps( 100 ),
55         singleSteps( 1 ),
56         pageSteps( 10 ),
57         stepAlignment( true ),
58         isValid( false ),
59         value( 0.0 ),
60         wrapping( false ),
61         invertedControls( false )
62     {
63     }
64 
65     bool isScrolling;
66     bool isTracking;
67     bool pendingValueChanged;
68 
69     bool readOnly;
70 
71     uint totalSteps;
72     uint singleSteps;
73     uint pageSteps;
74     bool stepAlignment;
75 
76     bool isValid;
77     double value;
78 
79     bool wrapping;
80     bool invertedControls;
81 };
82 
83 /*!
84   \brief Constructor
85 
86   The scale is initialized to [0.0, 100.0], the
87   number of steps is set to 100 with 1 and 10 and single
88   an page step sizes. Step alignment is enabled.
89 
90   The initial value is invalid.
91 
92   \param parent Parent widget
93 */
QwtAbstractSlider(QWidget * parent)94 QwtAbstractSlider::QwtAbstractSlider( QWidget *parent ):
95     QwtAbstractScale( parent )
96 {
97     d_data = new QwtAbstractSlider::PrivateData;
98 
99     setScale( 0.0, 100.0 );
100     setFocusPolicy( Qt::StrongFocus );
101 }
102 
103 //! Destructor
~QwtAbstractSlider()104 QwtAbstractSlider::~QwtAbstractSlider()
105 {
106     delete d_data;
107 }
108 
109 /*!
110   Set the value to be valid/invalid
111 
112   \param on When true, the value is invalidated
113 
114   \sa setValue()
115 */
setValid(bool on)116 void QwtAbstractSlider::setValid( bool on )
117 {
118     if ( on != d_data->isValid )
119     {
120         d_data->isValid = on;
121         sliderChange();
122 
123         Q_EMIT valueChanged( d_data->value );
124     }
125 }
126 
127 //! \return True, when the value is invalid
isValid() const128 bool QwtAbstractSlider::isValid() const
129 {
130     return d_data->isValid;
131 }
132 
133 /*!
134   En/Disable read only mode
135 
136   In read only mode the slider can't be controlled by mouse
137   or keyboard.
138 
139   \param on Enables in case of true
140   \sa isReadOnly()
141 
142   \warning The focus policy is set to Qt::StrongFocus or Qt::NoFocus
143 */
setReadOnly(bool on)144 void QwtAbstractSlider::setReadOnly( bool on )
145 {
146     if ( d_data->readOnly != on )
147     {
148         d_data->readOnly = on;
149         setFocusPolicy( on ? Qt::StrongFocus : Qt::NoFocus );
150 
151         update();
152     }
153 }
154 
155 /*!
156   In read only mode the slider can't be controlled by mouse
157   or keyboard.
158 
159   \return true if read only
160   \sa setReadOnly()
161 */
isReadOnly() const162 bool QwtAbstractSlider::isReadOnly() const
163 {
164     return d_data->readOnly;
165 }
166 
167 /*!
168   \brief Enables or disables tracking.
169 
170   If tracking is enabled, the slider emits the valueChanged()
171   signal while the movable part of the slider is being dragged.
172   If tracking is disabled, the slider emits the valueChanged() signal
173   only when the user releases the slider.
174 
175   Tracking is enabled by default.
176   \param on \c true (enable) or \c false (disable) tracking.
177 
178   \sa isTracking(), sliderMoved()
179 */
setTracking(bool on)180 void QwtAbstractSlider::setTracking( bool on )
181 {
182     d_data->isTracking = on;
183 }
184 
185 /*!
186   \return True, when tracking has been enabled
187   \sa setTracking()
188 */
isTracking() const189 bool QwtAbstractSlider::isTracking() const
190 {
191     return d_data->isTracking;
192 }
193 
194 /*!
195    Mouse press event handler
196    \param event Mouse event
197 */
mousePressEvent(QMouseEvent * event)198 void QwtAbstractSlider::mousePressEvent( QMouseEvent *event )
199 {
200     if ( isReadOnly() )
201     {
202         event->ignore();
203         return;
204     }
205 
206     if ( !d_data->isValid || lowerBound() == upperBound() )
207         return;
208 
209     d_data->isScrolling = isScrollPosition( event->pos() );
210 
211     if ( d_data->isScrolling )
212     {
213         d_data->pendingValueChanged = false;
214 
215         Q_EMIT sliderPressed();
216     }
217 }
218 
219 /*!
220    Mouse Move Event handler
221    \param event Mouse event
222 */
mouseMoveEvent(QMouseEvent * event)223 void QwtAbstractSlider::mouseMoveEvent( QMouseEvent *event )
224 {
225     if ( isReadOnly() )
226     {
227         event->ignore();
228         return;
229     }
230 
231     if ( d_data->isValid && d_data->isScrolling )
232     {
233         double value = scrolledTo( event->pos() );
234         if ( value != d_data->value )
235         {
236             value = boundedValue( value );
237 
238             if ( d_data->stepAlignment )
239             {
240                 value = alignedValue( value );
241             }
242             else
243             {
244                 value = qwtAlignToScaleDiv( this, value );
245             }
246 
247             if ( value != d_data->value )
248             {
249                 d_data->value = value;
250 
251                 sliderChange();
252 
253                 Q_EMIT sliderMoved( d_data->value );
254 
255                 if ( d_data->isTracking )
256                     Q_EMIT valueChanged( d_data->value );
257                 else
258                     d_data->pendingValueChanged = true;
259             }
260         }
261     }
262 }
263 
264 /*!
265    Mouse Release Event handler
266    \param event Mouse event
267 */
mouseReleaseEvent(QMouseEvent * event)268 void QwtAbstractSlider::mouseReleaseEvent( QMouseEvent *event )
269 {
270     if ( isReadOnly() )
271     {
272         event->ignore();
273         return;
274     }
275 
276     if ( d_data->isScrolling && d_data->isValid )
277     {
278         d_data->isScrolling = false;
279 
280         if ( d_data->pendingValueChanged )
281             Q_EMIT valueChanged( d_data->value );
282 
283         Q_EMIT sliderReleased();
284     }
285 }
286 
287 /*!
288    Wheel Event handler
289 
290    In/decreases the value by s number of steps. The direction
291    depends on the invertedControls() property.
292 
293    When the control or shift modifier is pressed the wheel delta
294    ( divided by 120 ) is mapped to an increment according to
295    pageSteps(). Otherwise it is mapped to singleSteps().
296 
297    \param event Wheel event
298 */
wheelEvent(QWheelEvent * event)299 void QwtAbstractSlider::wheelEvent( QWheelEvent *event )
300 {
301     if ( isReadOnly() )
302     {
303         event->ignore();
304         return;
305     }
306 
307     if ( !d_data->isValid || d_data->isScrolling )
308         return;
309 
310     int numSteps = 0;
311 
312     if ( ( event->modifiers() & Qt::ControlModifier) ||
313         ( event->modifiers() & Qt::ShiftModifier ) )
314     {
315         // one page regardless of delta
316         numSteps = d_data->pageSteps;
317         if ( event->delta() < 0 )
318             numSteps = -numSteps;
319     }
320     else
321     {
322         const int numTurns = ( event->delta() / 120 );
323         numSteps = numTurns * d_data->singleSteps;
324     }
325 
326     if ( d_data->invertedControls )
327         numSteps = -numSteps;
328 
329     const double value = incrementedValue( d_data->value, numSteps );
330     if ( value != d_data->value )
331     {
332         d_data->value = value;
333         sliderChange();
334 
335         Q_EMIT sliderMoved( d_data->value );
336         Q_EMIT valueChanged( d_data->value );
337     }
338 }
339 
340 /*!
341   Handles key events
342 
343   QwtAbstractSlider handles the following keys:
344 
345   - Qt::Key_Left\n
346     Add/Subtract singleSteps() in direction to lowerBound();
347   - Qt::Key_Right\n
348     Add/Subtract singleSteps() in direction to upperBound();
349   - Qt::Key_Down\n
350     Subtract singleSteps(), when invertedControls() is false
351   - Qt::Key_Up\n
352     Add singleSteps(), when invertedControls() is false
353   - Qt::Key_PageDown\n
354     Subtract pageSteps(), when invertedControls() is false
355   - Qt::Key_PageUp\n
356     Add pageSteps(), when invertedControls() is false
357   - Qt::Key_Home\n
358     Set the value to the minimum()
359   - Qt::Key_End\n
360     Set the value to the maximum()
361 
362   \param event Key event
363   \sa isReadOnly()
364 */
keyPressEvent(QKeyEvent * event)365 void QwtAbstractSlider::keyPressEvent( QKeyEvent *event )
366 {
367     if ( isReadOnly() )
368     {
369         event->ignore();
370         return;
371     }
372 
373     if ( !d_data->isValid || d_data->isScrolling )
374         return;
375 
376     int numSteps = 0;
377     double value = d_data->value;
378 
379     switch ( event->key() )
380     {
381         case Qt::Key_Left:
382         {
383             numSteps = -static_cast<int>( d_data->singleSteps );
384             if ( isInverted() )
385                 numSteps = -numSteps;
386 
387             break;
388         }
389         case Qt::Key_Right:
390         {
391             numSteps = d_data->singleSteps;
392             if ( isInverted() )
393                 numSteps = -numSteps;
394 
395             break;
396         }
397         case Qt::Key_Down:
398         {
399             numSteps = -static_cast<int>( d_data->singleSteps );
400             if ( d_data->invertedControls )
401                 numSteps = -numSteps;
402             break;
403         }
404         case Qt::Key_Up:
405         {
406             numSteps = d_data->singleSteps;
407             if ( d_data->invertedControls )
408                 numSteps = -numSteps;
409 
410             break;
411         }
412         case Qt::Key_PageUp:
413         {
414             numSteps = d_data->pageSteps;
415             if ( d_data->invertedControls )
416                 numSteps = -numSteps;
417             break;
418         }
419         case Qt::Key_PageDown:
420         {
421             numSteps = -static_cast<int>( d_data->pageSteps );
422             if ( d_data->invertedControls )
423                 numSteps = -numSteps;
424             break;
425         }
426         case Qt::Key_Home:
427         {
428             value = minimum();
429             break;
430         }
431         case Qt::Key_End:
432         {
433             value = maximum();
434             break;
435         }
436         default:;
437         {
438             event->ignore();
439         }
440     }
441 
442     if ( numSteps != 0 )
443     {
444         value = incrementedValue( d_data->value, numSteps );
445     }
446 
447     if ( value != d_data->value )
448     {
449         d_data->value = value;
450         sliderChange();
451 
452         Q_EMIT sliderMoved( d_data->value );
453         Q_EMIT valueChanged( d_data->value );
454     }
455 }
456 
457 /*!
458   \brief Set the number of steps
459 
460   The range of the slider is divided into a number of steps from
461   which the value increments according to user inputs depend.
462 
463   The default setting is 100.
464 
465   \param stepCount Number of steps
466 
467   \sa totalSteps(), setSingleSteps(), setPageSteps()
468  */
setTotalSteps(uint stepCount)469 void QwtAbstractSlider::setTotalSteps( uint stepCount )
470 {
471     d_data->totalSteps = stepCount;
472 }
473 
474 /*!
475   \return Number of steps
476   \sa setTotalSteps(), singleSteps(), pageSteps()
477  */
totalSteps() const478 uint QwtAbstractSlider::totalSteps() const
479 {
480     return d_data->totalSteps;
481 }
482 
483 /*!
484   \brief Set the number of steps for a single increment
485 
486   The range of the slider is divided into a number of steps from
487   which the value increments according to user inputs depend.
488 
489   \param stepCount Number of steps
490 
491   \sa singleSteps(), setTotalSteps(), setPageSteps()
492  */
493 
setSingleSteps(uint stepCount)494 void QwtAbstractSlider::setSingleSteps( uint stepCount )
495 {
496     d_data->singleSteps = stepCount;
497 }
498 
499 /*!
500   \return Number of steps
501   \sa setSingleSteps(), totalSteps(), pageSteps()
502  */
singleSteps() const503 uint QwtAbstractSlider::singleSteps() const
504 {
505     return d_data->singleSteps;
506 }
507 
508 /*!
509   \brief Set the number of steps for a page increment
510 
511   The range of the slider is divided into a number of steps from
512   which the value increments according to user inputs depend.
513 
514   \param stepCount Number of steps
515 
516   \sa pageSteps(), setTotalSteps(), setSingleSteps()
517  */
518 
setPageSteps(uint stepCount)519 void QwtAbstractSlider::setPageSteps( uint stepCount )
520 {
521     d_data->pageSteps = stepCount;
522 }
523 
524 /*!
525   \return Number of steps
526   \sa setPageSteps(), totalSteps(), singleSteps()
527  */
pageSteps() const528 uint QwtAbstractSlider::pageSteps() const
529 {
530     return d_data->pageSteps;
531 }
532 
533 /*!
534   \brief Enable step alignment
535 
536   When step alignment is enabled values resulting from slider
537   movements are aligned to the step size.
538 
539   \param on Enable step alignment when true
540   \sa stepAlignment()
541 */
setStepAlignment(bool on)542 void QwtAbstractSlider::setStepAlignment( bool on )
543 {
544     if ( on != d_data->stepAlignment )
545     {
546         d_data->stepAlignment = on;
547     }
548 }
549 
550 /*!
551   \return True, when step alignment is enabled
552   \sa setStepAlignment()
553  */
stepAlignment() const554 bool QwtAbstractSlider::stepAlignment() const
555 {
556     return d_data->stepAlignment;
557 }
558 
559 /*!
560   Set the slider to the specified value
561 
562   \param value New value
563   \sa setValid(), sliderChange(), valueChanged()
564 */
setValue(double value)565 void QwtAbstractSlider::setValue( double value )
566 {
567     value = qBound( minimum(), value, maximum() );
568 
569     const bool changed = ( d_data->value != value ) || !d_data->isValid;
570 
571     d_data->value = value;
572     d_data->isValid = true;
573 
574     if ( changed )
575     {
576         sliderChange();
577         Q_EMIT valueChanged( d_data->value );
578     }
579 }
580 
581 //! Returns the current value.
value() const582 double QwtAbstractSlider::value() const
583 {
584     return d_data->value;
585 }
586 
587 /*!
588   If wrapping is true stepping up from upperBound() value will
589   take you to the minimum() value and vice versa.
590 
591   \param on En/Disable wrapping
592   \sa wrapping()
593 */
setWrapping(bool on)594 void QwtAbstractSlider::setWrapping( bool on )
595 {
596     d_data->wrapping = on;
597 }
598 
599 /*!
600   \return True, when wrapping is set
601   \sa setWrapping()
602  */
wrapping() const603 bool QwtAbstractSlider::wrapping() const
604 {
605     return d_data->wrapping;
606 }
607 
608 /*!
609   Invert wheel and key events
610 
611   Usually scrolling the mouse wheel "up" and using keys like page
612   up will increase the slider's value towards its maximum.
613   When invertedControls() is enabled the value is scrolled
614   towards its minimum.
615 
616   Inverting the controls might be f.e. useful for a vertical slider
617   with an inverted scale ( decreasing from top to bottom ).
618 
619   \param on Invert controls, when true
620 
621   \sa invertedControls(), keyEvent(), wheelEvent()
622  */
setInvertedControls(bool on)623 void QwtAbstractSlider::setInvertedControls( bool on )
624 {
625     d_data->invertedControls = on;
626 }
627 
628 /*!
629  \return True, when the controls are inverted
630  \sa setInvertedControls()
631  */
invertedControls() const632 bool QwtAbstractSlider::invertedControls() const
633 {
634     return d_data->invertedControls;
635 }
636 
637 /*!
638   Increment the slider
639 
640   The step size depends on the number of totalSteps()
641 
642   \param stepCount Number of steps
643   \sa setTotalSteps(), incrementedValue()
644  */
incrementValue(int stepCount)645 void QwtAbstractSlider::incrementValue( int stepCount )
646 {
647     const double value = incrementedValue(
648         d_data->value, stepCount );
649 
650     if ( value != d_data->value )
651     {
652         d_data->value = value;
653         sliderChange();
654     }
655 }
656 
657 /*!
658   Increment a value
659 
660   \param value Value
661   \param stepCount Number of steps
662 
663   \return Incremented value
664  */
incrementedValue(double value,int stepCount) const665 double QwtAbstractSlider::incrementedValue(
666     double value, int stepCount ) const
667 {
668     if ( d_data->totalSteps == 0 )
669         return value;
670 
671     const QwtTransform *transformation =
672         scaleMap().transformation();
673 
674     if ( transformation == NULL )
675     {
676         const double range = maximum() - minimum();
677         value += stepCount * range / d_data->totalSteps;
678     }
679     else
680     {
681         QwtScaleMap map = scaleMap();
682         map.setPaintInterval( 0, d_data->totalSteps );
683 
684         // we need equidant steps according to
685         // paint device coordinates
686         const double range = transformation->transform( maximum() )
687             - transformation->transform( minimum() );
688 
689         const double stepSize = range / d_data->totalSteps;
690 
691         double v = transformation->transform( value );
692 
693         v = qRound( v / stepSize ) * stepSize;
694         v += stepCount * range / d_data->totalSteps;
695 
696         value = transformation->invTransform( v );
697     }
698 
699     value = boundedValue( value );
700 
701     if ( d_data->stepAlignment )
702         value = alignedValue( value );
703 
704     return value;
705 }
706 
boundedValue(double value) const707 double QwtAbstractSlider::boundedValue( double value ) const
708 {
709     const double vmin = minimum();
710     const double vmax = maximum();
711 
712     if ( d_data->wrapping && vmin != vmax )
713     {
714         const int fullCircle = 360 * 16;
715 
716         const double pd = scaleMap().pDist();
717         if ( int( pd / fullCircle ) * fullCircle == pd )
718         {
719             // full circle scales: min and max are the same
720             const double range = vmax - vmin;
721 
722             if ( value < vmin )
723             {
724                 value += ::ceil( ( vmin - value ) / range ) * range;
725             }
726             else if ( value > vmax )
727             {
728                 value -= ::ceil( ( value - vmax ) / range ) * range;
729             }
730         }
731         else
732         {
733             if ( value < vmin )
734                 value = vmax;
735             else if ( value > vmax )
736                 value = vmin;
737         }
738     }
739     else
740     {
741         value = qBound( vmin, value, vmax );
742     }
743 
744     return value;
745 }
746 
alignedValue(double value) const747 double QwtAbstractSlider::alignedValue( double value ) const
748 {
749     if ( d_data->totalSteps == 0 )
750         return value;
751 
752     double stepSize;
753 
754     if ( scaleMap().transformation() == NULL )
755     {
756         stepSize = ( maximum() - minimum() ) / d_data->totalSteps;
757         if ( stepSize > 0.0 )
758         {
759             value = lowerBound() +
760                 qRound( ( value - lowerBound() ) / stepSize ) * stepSize;
761         }
762     }
763     else
764     {
765         stepSize = ( scaleMap().p2() - scaleMap().p1() ) / d_data->totalSteps;
766 
767         if ( stepSize > 0.0 )
768         {
769             double v = scaleMap().transform( value );
770 
771             v = scaleMap().p1() +
772                 qRound( ( v - scaleMap().p1() ) / stepSize ) * stepSize;
773 
774             value = scaleMap().invTransform( v );
775         }
776     }
777 
778     if ( qAbs( stepSize ) > 1e-12 )
779     {
780         if ( qFuzzyCompare( value + 1.0, 1.0 ) )
781         {
782             // correct rounding error if value = 0
783             value = 0.0;
784         }
785         else
786         {
787             // correct rounding error at the border
788             if ( qFuzzyCompare( value, upperBound() ) )
789                 value = upperBound();
790             else if ( qFuzzyCompare( value, lowerBound() ) )
791                 value = lowerBound();
792         }
793     }
794 
795     return value;
796 }
797 
798 /*!
799   Update the slider according to modifications of the scale
800  */
scaleChange()801 void QwtAbstractSlider::scaleChange()
802 {
803     const double value = qBound( minimum(), d_data->value, maximum() );
804 
805     const bool changed = ( value != d_data->value );
806     if ( changed )
807     {
808         d_data->value = value;
809     }
810 
811     if ( d_data->isValid || changed )
812         Q_EMIT valueChanged( d_data->value );
813 
814     updateGeometry();
815     update();
816 }
817 
818 //! Calling update()
sliderChange()819 void QwtAbstractSlider::sliderChange()
820 {
821     update();
822 }
823