1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtWidgets module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include <qapplication.h>
41 #include "qabstractslider.h"
42 #include "qevent.h"
43 #include "qabstractslider_p.h"
44 #include "qdebug.h"
45 #ifndef QT_NO_ACCESSIBILITY
46 #include "qaccessible.h"
47 #endif
48 #include <limits.h>
49 
50 #include <private/qapplication_p.h>
51 
52 QT_BEGIN_NAMESPACE
53 
54 /*!
55     \class QAbstractSlider
56     \brief The QAbstractSlider class provides an integer value within a range.
57 
58     \ingroup abstractwidgets
59     \inmodule QtWidgets
60 
61     The class is designed as a common super class for widgets like
62     QScrollBar, QSlider and QDial.
63 
64     Here are the main properties of the class:
65 
66     \list 1
67 
68     \li \l value: The bounded integer that QAbstractSlider maintains.
69 
70     \li \l minimum: The lowest possible value.
71 
72     \li \l maximum: The highest possible value.
73 
74     \li \l singleStep: The smaller of two natural steps that an
75     abstract sliders provides and typically corresponds to the user
76     pressing an arrow key.
77 
78     \li \l pageStep: The larger of two natural steps that an abstract
79     slider provides and typically corresponds to the user pressing
80     PageUp or PageDown.
81 
82     \li \l tracking: Whether slider tracking is enabled.
83 
84     \li \l sliderPosition: The current position of the slider. If \l
85     tracking is enabled (the default), this is identical to \l value.
86 
87     \endlist
88 
89     Unity (1) may be viewed as a third step size. setValue() lets you
90     set the current value to any integer in the allowed range, not
91     just minimum() + \e n * singleStep() for integer values of \e n.
92     Some widgets may allow the user to set any value at all; others
93     may just provide multiples of singleStep() or pageStep().
94 
95     QAbstractSlider emits a comprehensive set of signals:
96 
97     \table
98     \header \li Signal \li Emitted when
99     \row \li \l valueChanged()
100          \li the value has changed. The \l tracking
101             determines whether this signal is emitted during user
102             interaction.
103     \row \li \l sliderPressed()
104          \li the user starts to drag the slider.
105     \row \li \l sliderMoved()
106          \li the user drags the slider.
107     \row \li \l sliderReleased()
108          \li the user releases the slider.
109     \row \li \l actionTriggered()
110          \li a slider action was triggerd.
111     \row \li \l rangeChanged()
112          \li a the range has changed.
113     \endtable
114 
115     QAbstractSlider provides a virtual sliderChange() function that is
116     well suited for updating the on-screen representation of
117     sliders. By calling triggerAction(), subclasses trigger slider
118     actions. Two helper functions QStyle::sliderPositionFromValue() and
119     QStyle::sliderValueFromPosition() help subclasses and styles to map
120     screen coordinates to logical range values.
121 
122     \sa QAbstractSpinBox, QSlider, QDial, QScrollBar, {Sliders Example}
123 */
124 
125 /*!
126     \enum QAbstractSlider::SliderAction
127 
128     \value SliderNoAction
129     \value SliderSingleStepAdd
130     \value SliderSingleStepSub
131     \value SliderPageStepAdd
132     \value SliderPageStepSub
133     \value SliderToMinimum
134     \value SliderToMaximum
135     \value SliderMove
136 
137 */
138 
139 /*!
140     \fn void QAbstractSlider::valueChanged(int value)
141 
142     This signal is emitted when the slider value has changed, with the
143     new slider \a value as argument.
144 */
145 
146 /*!
147     \fn void QAbstractSlider::sliderPressed()
148 
149     This signal is emitted when the user presses the slider with the
150     mouse, or programmatically when setSliderDown(true) is called.
151 
152     \sa sliderReleased(), sliderMoved(), isSliderDown()
153 */
154 
155 /*!
156     \fn void QAbstractSlider::sliderMoved(int value)
157 
158     This signal is emitted when sliderDown is true and the slider moves. This
159     usually happens when the user is dragging the slider. The \a value
160     is the new slider position.
161 
162     This signal is emitted even when tracking is turned off.
163 
164     \sa setTracking(), valueChanged(), isSliderDown(),
165     sliderPressed(), sliderReleased()
166 */
167 
168 /*!
169     \fn void QAbstractSlider::sliderReleased()
170 
171     This signal is emitted when the user releases the slider with the
172     mouse, or programmatically when setSliderDown(false) is called.
173 
174     \sa sliderPressed(), sliderMoved(), sliderDown
175 */
176 
177 /*!
178     \fn void QAbstractSlider::rangeChanged(int min, int max)
179 
180     This signal is emitted when the slider range has changed, with \a
181     min being the new minimum, and \a max being the new maximum.
182 
183     \sa minimum, maximum
184 */
185 
186 /*!
187     \fn void QAbstractSlider::actionTriggered(int action)
188 
189     This signal is emitted when the slider action \a action is
190     triggered. Actions are \l SliderSingleStepAdd, \l
191     SliderSingleStepSub, \l SliderPageStepAdd, \l SliderPageStepSub,
192     \l SliderToMinimum, \l SliderToMaximum, and \l SliderMove.
193 
194     When the signal is emitted, the \l sliderPosition has been
195     adjusted according to the action, but the \l value has not yet
196     been propagated (meaning the valueChanged() signal was not yet
197     emitted), and the visual display has not been updated. In slots
198     connected to this signal you can thus safely adjust any action by
199     calling setSliderPosition() yourself, based on both the action and
200     the slider's value.
201 
202     \sa triggerAction()
203 */
204 
205 /*!
206     \enum QAbstractSlider::SliderChange
207 
208     \value SliderRangeChange
209     \value SliderOrientationChange
210     \value SliderStepsChange
211     \value SliderValueChange
212 */
213 
QAbstractSliderPrivate()214 QAbstractSliderPrivate::QAbstractSliderPrivate()
215     : minimum(0), maximum(99), pageStep(10), value(0), position(0), pressValue(-1),
216       singleStep(1), singleStepFromItemView(-1), viewMayChangeSingleStep(true), offset_accumulated(0), tracking(true),
217       blocktracking(false), pressed(false),
218       invertedAppearance(false), invertedControls(false),
219       orientation(Qt::Horizontal), repeatAction(QAbstractSlider::SliderNoAction)
220 #ifdef QT_KEYPAD_NAVIGATION
221       , isAutoRepeating(false)
222       , repeatMultiplier(1)
223 {
224     firstRepeat.invalidate();
225 #else
226 {
227 #endif
228 
229 }
230 
231 QAbstractSliderPrivate::~QAbstractSliderPrivate()
232 {
233 }
234 
235 /*!
236     Sets the slider's minimum to \a min and its maximum to \a max.
237 
238     If \a max is smaller than \a min, \a min becomes the only legal
239     value.
240 
241     \sa minimum, maximum
242 */
243 void QAbstractSlider::setRange(int min, int max)
244 {
245     Q_D(QAbstractSlider);
246     int oldMin = d->minimum;
247     int oldMax = d->maximum;
248     d->minimum = min;
249     d->maximum = qMax(min, max);
250     if (oldMin != d->minimum || oldMax != d->maximum) {
251         sliderChange(SliderRangeChange);
252         emit rangeChanged(d->minimum, d->maximum);
253         setValue(d->value); // re-bound
254     }
255 }
256 
257 
258 void QAbstractSliderPrivate::setSteps(int single, int page)
259 {
260     Q_Q(QAbstractSlider);
261     singleStep = qAbs(single);
262     pageStep = qAbs(page);
263     q->sliderChange(QAbstractSlider::SliderStepsChange);
264 }
265 
266 /*!
267     Constructs an abstract slider.
268 
269     The \a parent argument is sent to the QWidget constructor.
270 
271     The \l minimum defaults to 0, the \l maximum to 99, with a \l
272     singleStep size of 1 and a \l pageStep size of 10, and an initial
273     \l value of 0.
274 */
275 QAbstractSlider::QAbstractSlider(QWidget *parent)
276     :QWidget(*new QAbstractSliderPrivate, parent, { })
277 {
278 }
279 
280 /*! \internal */
281 QAbstractSlider::QAbstractSlider(QAbstractSliderPrivate &dd, QWidget *parent)
282     :QWidget(dd, parent, { })
283 {
284 }
285 
286 /*!
287     Destroys the slider.
288 */
289 QAbstractSlider::~QAbstractSlider()
290 {
291 }
292 
293 /*!
294     \property QAbstractSlider::orientation
295     \brief the orientation of the slider
296 
297     The orientation must be \l Qt::Vertical (the default) or \l
298     Qt::Horizontal.
299 */
300 void QAbstractSlider::setOrientation(Qt::Orientation orientation)
301 {
302     Q_D(QAbstractSlider);
303     if (d->orientation == orientation)
304         return;
305 
306     d->orientation = orientation;
307     if (!testAttribute(Qt::WA_WState_OwnSizePolicy)) {
308         setSizePolicy(sizePolicy().transposed());
309         setAttribute(Qt::WA_WState_OwnSizePolicy, false);
310     }
311     update();
312     updateGeometry();
313 }
314 
315 Qt::Orientation QAbstractSlider::orientation() const
316 {
317     Q_D(const QAbstractSlider);
318     return d->orientation;
319 }
320 
321 
322 /*!
323     \property QAbstractSlider::minimum
324     \brief the sliders's minimum value
325 
326     When setting this property, the \l maximum is adjusted if
327     necessary to ensure that the range remains valid. Also the
328     slider's current value is adjusted to be within the new range.
329 
330 */
331 
332 void QAbstractSlider::setMinimum(int min)
333 {
334     Q_D(QAbstractSlider);
335     setRange(min, qMax(d->maximum, min));
336 }
337 
338 int QAbstractSlider::minimum() const
339 {
340     Q_D(const QAbstractSlider);
341     return d->minimum;
342 }
343 
344 
345 /*!
346     \property QAbstractSlider::maximum
347     \brief the slider's maximum value
348 
349     When setting this property, the \l minimum is adjusted if
350     necessary to ensure that the range remains valid.  Also the
351     slider's current value is adjusted to be within the new range.
352 
353 
354 */
355 
356 void QAbstractSlider::setMaximum(int max)
357 {
358     Q_D(QAbstractSlider);
359     setRange(qMin(d->minimum, max), max);
360 }
361 
362 int QAbstractSlider::maximum() const
363 {
364     Q_D(const QAbstractSlider);
365     return d->maximum;
366 }
367 
368 
369 
370 /*!
371     \property QAbstractSlider::singleStep
372     \brief the single step.
373 
374     The smaller of two natural steps that an
375     abstract sliders provides and typically corresponds to the user
376     pressing an arrow key.
377 
378     If the property is modified during an auto repeating key event, behavior
379     is undefined.
380 
381     \sa pageStep
382 */
383 
384 void QAbstractSlider::setSingleStep(int step)
385 {
386     Q_D(QAbstractSlider);
387 
388     d->viewMayChangeSingleStep = (step < 0);
389     if (step < 0 && d->singleStepFromItemView > 0)
390         step = d->singleStepFromItemView;
391 
392     if (step != d->singleStep)
393         d->setSteps(step, d->pageStep);
394 }
395 
396 int QAbstractSlider::singleStep() const
397 {
398     Q_D(const QAbstractSlider);
399     return d->singleStep;
400 }
401 
402 
403 /*!
404     \property QAbstractSlider::pageStep
405     \brief the page step.
406 
407     The larger of two natural steps that an abstract slider provides
408     and typically corresponds to the user pressing PageUp or PageDown.
409 
410     \sa singleStep
411 */
412 
413 void QAbstractSlider::setPageStep(int step)
414 {
415     Q_D(QAbstractSlider);
416     if (step != d->pageStep)
417         d->setSteps(d->singleStep, step);
418 }
419 
420 int QAbstractSlider::pageStep() const
421 {
422     Q_D(const QAbstractSlider);
423     return d->pageStep;
424 }
425 
426 /*!
427     \property QAbstractSlider::tracking
428     \brief whether slider tracking is enabled
429 
430     If tracking is enabled (the default), the slider emits the
431     valueChanged() signal while the slider is being dragged. If
432     tracking is disabled, the slider emits the valueChanged() signal
433     only when the user releases the slider.
434 
435     \sa sliderDown
436 */
437 void QAbstractSlider::setTracking(bool enable)
438 {
439     Q_D(QAbstractSlider);
440     d->tracking = enable;
441 }
442 
443 bool QAbstractSlider::hasTracking() const
444 {
445     Q_D(const QAbstractSlider);
446     return d->tracking;
447 }
448 
449 
450 /*!
451     \property QAbstractSlider::sliderDown
452     \brief whether the slider is pressed down.
453 
454     The property is set by subclasses in order to let the abstract
455     slider know whether or not \l tracking has any effect.
456 
457     Changing the slider down property emits the sliderPressed() and
458     sliderReleased() signals.
459 
460 */
461 void QAbstractSlider::setSliderDown(bool down)
462 {
463     Q_D(QAbstractSlider);
464     bool doEmit = d->pressed != down;
465 
466     d->pressed = down;
467 
468     if (doEmit) {
469         if (down)
470             emit sliderPressed();
471         else
472             emit sliderReleased();
473     }
474 
475     if (!down && d->position != d->value)
476         triggerAction(SliderMove);
477 }
478 
479 bool QAbstractSlider::isSliderDown() const
480 {
481     Q_D(const QAbstractSlider);
482     return d->pressed;
483 }
484 
485 
486 /*!
487     \property QAbstractSlider::sliderPosition
488     \brief the current slider position
489 
490     If \l tracking is enabled (the default), this is identical to \l value.
491 */
492 void QAbstractSlider::setSliderPosition(int position)
493 {
494     Q_D(QAbstractSlider);
495     position = d->bound(position);
496     if (position == d->position)
497         return;
498     d->position = position;
499     if (!d->tracking)
500         update();
501     if (d->pressed)
502         emit sliderMoved(position);
503     if (d->tracking && !d->blocktracking)
504         triggerAction(SliderMove);
505 }
506 
507 int QAbstractSlider::sliderPosition() const
508 {
509     Q_D(const QAbstractSlider);
510     return d->position;
511 }
512 
513 
514 /*!
515     \property QAbstractSlider::value
516     \brief the slider's current value
517 
518     The slider forces the value to be within the legal range: \l
519     minimum <= \c value <= \l maximum.
520 
521     Changing the value also changes the \l sliderPosition.
522 */
523 
524 
525 int QAbstractSlider::value() const
526 {
527     Q_D(const QAbstractSlider);
528     return d->value;
529 }
530 
531 void QAbstractSlider::setValue(int value)
532 {
533     Q_D(QAbstractSlider);
534     value = d->bound(value);
535     if (d->value == value && d->position == value)
536         return;
537     d->value = value;
538     if (d->position != value) {
539         d->position = value;
540         if (d->pressed)
541             emit sliderMoved((d->position = value));
542     }
543 #ifndef QT_NO_ACCESSIBILITY
544     QAccessibleValueChangeEvent event(this, d->value);
545     QAccessible::updateAccessibility(&event);
546 #endif
547     sliderChange(SliderValueChange);
548     emit valueChanged(value);
549 }
550 
551 /*!
552     \property QAbstractSlider::invertedAppearance
553     \brief whether or not a slider shows its values inverted.
554 
555     If this property is \c false (the default), the minimum and maximum will
556     be shown in its classic position for the inherited widget. If the
557     value is true, the minimum and maximum appear at their opposite location.
558 
559     Note: This property makes most sense for sliders and dials. For
560     scroll bars, the visual effect of the scroll bar subcontrols depends on
561     whether or not the styles understand inverted appearance; most styles
562     ignore this property for scroll bars.
563 */
564 
565 bool QAbstractSlider::invertedAppearance() const
566 {
567     Q_D(const QAbstractSlider);
568     return d->invertedAppearance;
569 }
570 
571 void QAbstractSlider::setInvertedAppearance(bool invert)
572 {
573     Q_D(QAbstractSlider);
574     d->invertedAppearance = invert;
575     update();
576 }
577 
578 
579 /*!
580     \property QAbstractSlider::invertedControls
581     \brief whether or not the slider inverts its wheel and key events.
582 
583     If this property is \c false, scrolling the mouse wheel "up" and using keys
584     like page up will increase the slider's value towards its maximum. Otherwise
585     pressing page up will move value towards the slider's minimum.
586 */
587 
588 
589 bool QAbstractSlider::invertedControls() const
590 {
591     Q_D(const QAbstractSlider);
592     return d->invertedControls;
593 }
594 
595 void QAbstractSlider::setInvertedControls(bool invert)
596 {
597     Q_D(QAbstractSlider);
598     d->invertedControls = invert;
599 }
600 
601 /*!  Triggers a slider \a action.  Possible actions are \l
602   SliderSingleStepAdd, \l SliderSingleStepSub, \l SliderPageStepAdd,
603   \l SliderPageStepSub, \l SliderToMinimum, \l SliderToMaximum, and \l
604   SliderMove.
605 
606   \sa actionTriggered()
607  */
608 void QAbstractSlider::triggerAction(SliderAction action)
609 {
610     Q_D(QAbstractSlider);
611     d->blocktracking = true;
612     switch (action) {
613     case SliderSingleStepAdd:
614         setSliderPosition(d->overflowSafeAdd(d->effectiveSingleStep()));
615         break;
616     case SliderSingleStepSub:
617         setSliderPosition(d->overflowSafeAdd(-d->effectiveSingleStep()));
618         break;
619     case SliderPageStepAdd:
620         setSliderPosition(d->overflowSafeAdd(d->pageStep));
621         break;
622     case SliderPageStepSub:
623         setSliderPosition(d->overflowSafeAdd(-d->pageStep));
624         break;
625     case SliderToMinimum:
626         setSliderPosition(d->minimum);
627         break;
628     case SliderToMaximum:
629         setSliderPosition(d->maximum);
630         break;
631     case SliderMove:
632     case SliderNoAction:
633         break;
634     };
635     emit actionTriggered(action);
636     d->blocktracking = false;
637     setValue(d->position);
638 }
639 
640 /*!  Sets action \a action to be triggered repetitively in intervals
641 of \a repeatTime, after an initial delay of \a thresholdTime.
642 
643 \sa triggerAction(), repeatAction()
644  */
645 void QAbstractSlider::setRepeatAction(SliderAction action, int thresholdTime, int repeatTime)
646 {
647     Q_D(QAbstractSlider);
648     if ((d->repeatAction = action) == SliderNoAction) {
649         d->repeatActionTimer.stop();
650     } else {
651         d->repeatActionTime = repeatTime;
652         d->repeatActionTimer.start(thresholdTime, this);
653     }
654 }
655 
656 /*!
657   Returns the current repeat action.
658   \sa setRepeatAction()
659  */
660 QAbstractSlider::SliderAction QAbstractSlider::repeatAction() const
661 {
662     Q_D(const QAbstractSlider);
663     return d->repeatAction;
664 }
665 
666 /*!\reimp
667  */
668 void QAbstractSlider::timerEvent(QTimerEvent *e)
669 {
670     Q_D(QAbstractSlider);
671     if (e->timerId() == d->repeatActionTimer.timerId()) {
672         if (d->repeatActionTime) { // was threshold time, use repeat time next time
673             d->repeatActionTimer.start(d->repeatActionTime, this);
674             d->repeatActionTime = 0;
675         }
676         if (d->repeatAction == SliderPageStepAdd)
677             d->setAdjustedSliderPosition(d->overflowSafeAdd(d->pageStep));
678         else if (d->repeatAction == SliderPageStepSub)
679             d->setAdjustedSliderPosition(d->overflowSafeAdd(-d->pageStep));
680         else
681             triggerAction(d->repeatAction);
682     }
683 }
684 
685 /*!
686     Reimplement this virtual function to track slider changes such as
687     \l SliderRangeChange, \l SliderOrientationChange, \l
688     SliderStepsChange, or \l SliderValueChange. The default
689     implementation only updates the display and ignores the \a change
690     parameter.
691  */
692 void QAbstractSlider::sliderChange(SliderChange)
693 {
694     update();
695 }
696 
697 bool QAbstractSliderPrivate::scrollByDelta(Qt::Orientation orientation, Qt::KeyboardModifiers modifiers, int delta)
698 {
699     Q_Q(QAbstractSlider);
700     int stepsToScroll = 0;
701     // in Qt scrolling to the right gives negative values.
702     if (orientation == Qt::Horizontal)
703         delta = -delta;
704     qreal offset = qreal(delta) / 120;
705 
706     if ((modifiers & Qt::ControlModifier) || (modifiers & Qt::ShiftModifier)) {
707         // Scroll one page regardless of delta:
708         stepsToScroll = qBound(-pageStep, int(offset * pageStep), pageStep);
709         offset_accumulated = 0;
710     } else {
711         // Calculate how many lines to scroll. Depending on what delta is (and
712         // offset), we might end up with a fraction (e.g. scroll 1.3 lines). We can
713         // only scroll whole lines, so we keep the reminder until next event.
714         qreal stepsToScrollF =
715 #if QT_CONFIG(wheelevent)
716                 QApplication::wheelScrollLines() *
717 #endif
718                 offset * effectiveSingleStep();
719         // Check if wheel changed direction since last event:
720         if (offset_accumulated != 0 && (offset / offset_accumulated) < 0)
721             offset_accumulated = 0;
722 
723         offset_accumulated += stepsToScrollF;
724 
725         // Don't scroll more than one page in any case:
726         stepsToScroll = qBound(-pageStep, int(offset_accumulated), pageStep);
727 
728         offset_accumulated -= int(offset_accumulated);
729         if (stepsToScroll == 0) {
730             // We moved less than a line, but might still have accumulated partial scroll,
731             // unless we already are at one of the ends.
732             const float effective_offset = invertedControls ? -offset_accumulated : offset_accumulated;
733             if (effective_offset > 0.f && value < maximum)
734                 return true;
735             if (effective_offset < 0.f && value > minimum)
736                 return true;
737             offset_accumulated = 0;
738             return false;
739         }
740     }
741 
742     if (invertedControls)
743         stepsToScroll = -stepsToScroll;
744 
745     int prevValue = value;
746     position = bound(overflowSafeAdd(stepsToScroll)); // value will be updated by triggerAction()
747     q->triggerAction(QAbstractSlider::SliderMove);
748 
749     if (prevValue == value) {
750         offset_accumulated = 0;
751         return false;
752     }
753     return true;
754 }
755 
756 /*!
757     \reimp
758 */
759 #if QT_CONFIG(wheelevent)
760 void QAbstractSlider::wheelEvent(QWheelEvent * e)
761 {
762     Q_D(QAbstractSlider);
763     e->ignore();
764     bool vertical = bool(e->angleDelta().y());
765     int delta = vertical ? e->angleDelta().y() : e->angleDelta().x();
766     if (e->inverted())
767         delta = -delta;
768     if (d->scrollByDelta(vertical ? Qt::Vertical : Qt::Horizontal, e->modifiers(), delta))
769         e->accept();
770 }
771 
772 #endif
773 
774 /*!
775     \reimp
776 */
777 void QAbstractSlider::keyPressEvent(QKeyEvent *ev)
778 {
779     Q_D(QAbstractSlider);
780     SliderAction action = SliderNoAction;
781 #ifdef QT_KEYPAD_NAVIGATION
782     if (ev->isAutoRepeat()) {
783         if (!d->firstRepeat.isValid())
784             d->firstRepeat.start();
785         else if (1 == d->repeatMultiplier) {
786             // This is the interval in milli seconds which one key repetition
787             // takes.
788             const int repeatMSecs = d->firstRepeat.elapsed();
789 
790             /**
791              * The time it takes to currently navigate the whole slider.
792              */
793             const qreal currentTimeElapse = (qreal(maximum()) / singleStep()) * repeatMSecs;
794 
795             /**
796              * This is an arbitrarily determined constant in msecs that
797              * specifies how long time it should take to navigate from the
798              * start to the end(excluding starting key auto repeat).
799              */
800             const int SliderRepeatElapse = 2500;
801 
802             d->repeatMultiplier = currentTimeElapse / SliderRepeatElapse;
803         }
804 
805     }
806     else if (d->firstRepeat.isValid()) {
807         d->firstRepeat.invalidate();
808         d->repeatMultiplier = 1;
809     }
810 
811 #endif
812 
813     switch (ev->key()) {
814 #ifdef QT_KEYPAD_NAVIGATION
815         case Qt::Key_Select:
816             if (QApplicationPrivate::keypadNavigationEnabled())
817                 setEditFocus(!hasEditFocus());
818             else
819                 ev->ignore();
820             break;
821         case Qt::Key_Back:
822             if (QApplicationPrivate::keypadNavigationEnabled() && hasEditFocus()) {
823                 setValue(d->origValue);
824                 setEditFocus(false);
825             } else
826                 ev->ignore();
827             break;
828 #endif
829 
830         case Qt::Key_Left:
831 #ifdef QT_KEYPAD_NAVIGATION
832             // In QApplication::KeypadNavigationDirectional, we want to change the slider
833             // value if there is no left/right navigation possible and if this slider is not
834             // inside a tab widget.
835             if (QApplicationPrivate::keypadNavigationEnabled()
836                     && (!hasEditFocus() && QApplication::navigationMode() == Qt::NavigationModeKeypadTabOrder
837                     || d->orientation == Qt::Vertical
838                     || !hasEditFocus()
839                     && (QWidgetPrivate::canKeypadNavigate(Qt::Horizontal) || QWidgetPrivate::inTabWidget(this)))) {
840                 ev->ignore();
841                 return;
842             }
843             if (QApplicationPrivate::keypadNavigationEnabled() && d->orientation == Qt::Vertical)
844                 action = d->invertedControls ? SliderSingleStepSub : SliderSingleStepAdd;
845             else
846 #endif
847             if (isRightToLeft())
848                 action = d->invertedControls ? SliderSingleStepSub : SliderSingleStepAdd;
849             else
850                 action = !d->invertedControls ? SliderSingleStepSub : SliderSingleStepAdd;
851             break;
852         case Qt::Key_Right:
853 #ifdef QT_KEYPAD_NAVIGATION
854             // Same logic as in Qt::Key_Left
855             if (QApplicationPrivate::keypadNavigationEnabled()
856                     && (!hasEditFocus() && QApplication::navigationMode() == Qt::NavigationModeKeypadTabOrder
857                     || d->orientation == Qt::Vertical
858                     || !hasEditFocus()
859                     && (QWidgetPrivate::canKeypadNavigate(Qt::Horizontal) || QWidgetPrivate::inTabWidget(this)))) {
860                 ev->ignore();
861                 return;
862             }
863             if (QApplicationPrivate::keypadNavigationEnabled() && d->orientation == Qt::Vertical)
864                 action = d->invertedControls ? SliderSingleStepAdd : SliderSingleStepSub;
865             else
866 #endif
867             if (isRightToLeft())
868                 action = d->invertedControls ? SliderSingleStepAdd : SliderSingleStepSub;
869             else
870                 action = !d->invertedControls ? SliderSingleStepAdd : SliderSingleStepSub;
871             break;
872         case Qt::Key_Up:
873 #ifdef QT_KEYPAD_NAVIGATION
874             // In QApplication::KeypadNavigationDirectional, we want to change the slider
875             // value if there is no up/down navigation possible.
876             if (QApplicationPrivate::keypadNavigationEnabled()
877                     && (QApplication::navigationMode() == Qt::NavigationModeKeypadTabOrder
878                     || d->orientation == Qt::Horizontal
879                     || !hasEditFocus() && QWidgetPrivate::canKeypadNavigate(Qt::Vertical))) {
880                 ev->ignore();
881                 break;
882             }
883 #endif
884             action = d->invertedControls ? SliderSingleStepSub : SliderSingleStepAdd;
885             break;
886         case Qt::Key_Down:
887 #ifdef QT_KEYPAD_NAVIGATION
888             // Same logic as in Qt::Key_Up
889             if (QApplicationPrivate::keypadNavigationEnabled()
890                     && (QApplication::navigationMode() == Qt::NavigationModeKeypadTabOrder
891                     || d->orientation == Qt::Horizontal
892                     || !hasEditFocus() && QWidgetPrivate::canKeypadNavigate(Qt::Vertical))) {
893                 ev->ignore();
894                 break;
895             }
896 #endif
897             action = d->invertedControls ? SliderSingleStepAdd : SliderSingleStepSub;
898             break;
899         case Qt::Key_PageUp:
900             action = d->invertedControls ? SliderPageStepSub : SliderPageStepAdd;
901             break;
902         case Qt::Key_PageDown:
903             action = d->invertedControls ? SliderPageStepAdd : SliderPageStepSub;
904             break;
905         case Qt::Key_Home:
906             action = SliderToMinimum;
907             break;
908         case Qt::Key_End:
909             action = SliderToMaximum;
910             break;
911         default:
912             ev->ignore();
913             break;
914     }
915     if (action)
916         triggerAction(action);
917 }
918 
919 /*!
920     \reimp
921 */
922 void QAbstractSlider::changeEvent(QEvent *ev)
923 {
924     Q_D(QAbstractSlider);
925     switch (ev->type()) {
926     case QEvent::EnabledChange:
927         if (!isEnabled()) {
928             d->repeatActionTimer.stop();
929             setSliderDown(false);
930         }
931         Q_FALLTHROUGH();
932     default:
933         QWidget::changeEvent(ev);
934     }
935 }
936 
937 /*!
938     \reimp
939 */
940 bool QAbstractSlider::event(QEvent *e)
941 {
942 #ifdef QT_KEYPAD_NAVIGATION
943     Q_D(QAbstractSlider);
944     switch (e->type()) {
945     case QEvent::FocusIn:
946         d->origValue = d->value;
947         break;
948     default:
949         break;
950     }
951 #endif
952 
953     return QWidget::event(e);
954 }
955 
956 // This function is called from itemviews when doing scroll per pixel (on updateGeometries())
957 // It will not have any effect if there has been a call to setSingleStep with
958 // a 'reasonable' value (since viewMayChangeSingleStep will be set to false).
959 // (If setSingleStep is called with -1 it will however allow the views to change singleStep.)
960 
961 void QAbstractSliderPrivate::itemviewChangeSingleStep(int step)
962 {
963     singleStepFromItemView = step;
964     if (viewMayChangeSingleStep && singleStep != step)
965         setSteps(step, pageStep);
966 }
967 
968 QT_END_NAMESPACE
969 
970 #include "moc_qabstractslider.cpp"
971