source: ntrip/trunk/BNC/qwt/qwt_abstract_slider.cpp@ 8639

Last change on this file since 8639 was 8127, checked in by stoecker, 8 years ago

update qwt and qwtpolar, many QT5 fixes (unfinished)

File size: 18.0 KB
Line 
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
20static 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.lowerBound() ) )
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
46class QwtAbstractSlider::PrivateData
47{
48public:
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*/
94QwtAbstractSlider::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
104QwtAbstractSlider::~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*/
116void 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
128bool 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*/
144void 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*/
162bool 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*/
180void 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*/
189bool QwtAbstractSlider::isTracking() const
190{
191 return d_data->isTracking;
192}
193
194/*!
195 Mouse press event handler
196 \param event Mouse event
197*/
198void 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*/
223void 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*/
268void 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*/
299void 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*/
365void 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 */
469void QwtAbstractSlider::setTotalSteps( uint stepCount )
470{
471 d_data->totalSteps = stepCount;
472}
473
474/*!
475 \return Number of steps
476 \sa setTotalSteps(), singleSteps(), pageSteps()
477 */
478uint 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
494void QwtAbstractSlider::setSingleSteps( uint stepCount )
495{
496 d_data->singleSteps = stepCount;
497}
498
499/*!
500 \return Number of steps
501 \sa setSingleSteps(), totalSteps(), pageSteps()
502 */
503uint 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
519void QwtAbstractSlider::setPageSteps( uint stepCount )
520{
521 d_data->pageSteps = stepCount;
522}
523
524/*!
525 \return Number of steps
526 \sa setPageSteps(), totalSteps(), singleSteps()
527 */
528uint 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*/
542void 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 */
554bool 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*/
565void 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.
582double 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*/
594void QwtAbstractSlider::setWrapping( bool on )
595{
596 d_data->wrapping = on;
597}
598
599/*!
600 \return True, when wrapping is set
601 \sa setWrapping()
602 */
603bool 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 */
623void 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 */
632bool 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 */
645void 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 */
665double 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
707double 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
747double 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 */
801void 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()
819void QwtAbstractSlider::sliderChange()
820{
821 update();
822}
Note: See TracBrowser for help on using the repository browser.