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 <qplatformdefs.h>
41 #include <private/qabstractspinbox_p.h>
42 #include <private/qapplication_p.h>
43 #if QT_CONFIG(datetimeparser)
44 #include <private/qdatetimeparser_p.h>
45 #endif
46 #include <private/qlineedit_p.h>
47 #include <qabstractspinbox.h>
48 
49 #include <qapplication.h>
50 #include <qstylehints.h>
51 #include <qclipboard.h>
52 #include <qdatetime.h>
53 #include <qevent.h>
54 #if QT_CONFIG(menu)
55 #include <qmenu.h>
56 #endif
57 #include <qpainter.h>
58 #include <qpalette.h>
59 #include <qstylepainter.h>
60 #include <qdebug.h>
61 #ifndef QT_NO_ACCESSIBILITY
62 # include <qaccessible.h>
63 #endif
64 
65 
66 //#define QABSTRACTSPINBOX_QSBDEBUG
67 #ifdef QABSTRACTSPINBOX_QSBDEBUG
68 #  define QASBDEBUG qDebug
69 #else
70 #  define QASBDEBUG if (false) qDebug
71 #endif
72 
73 QT_BEGIN_NAMESPACE
74 
75 /*!
76     \class QAbstractSpinBox
77     \brief The QAbstractSpinBox class provides a spinbox and a line edit to
78     display values.
79 
80     \ingroup abstractwidgets
81     \inmodule QtWidgets
82 
83     The class is designed as a common super class for widgets like
84     QSpinBox, QDoubleSpinBox and QDateTimeEdit
85 
86     Here are the main properties of the class:
87 
88     \list 1
89 
90     \li \l text: The text that is displayed in the QAbstractSpinBox.
91 
92     \li \l alignment: The alignment of the text in the QAbstractSpinBox.
93 
94     \li \l wrapping: Whether the QAbstractSpinBox wraps from the
95     minimum value to the maximum value and vice versa.
96 
97     \endlist
98 
99     QAbstractSpinBox provides a virtual stepBy() function that is
100     called whenever the user triggers a step. This function takes an
101     integer value to signify how many steps were taken. E.g. Pressing
102     Qt::Key_Down will trigger a call to stepBy(-1).
103 
104     When the user triggers a step whilst holding the Qt::ControlModifier,
105     QAbstractSpinBox steps by 10 instead of making a single step. This
106     step modifier affects wheel events, key events and interaction with
107     the spinbox buttons. Note that on macOS, Control corresponds to the
108     Command key.
109 
110     Since Qt 5.12, QStyle::SH_SpinBox_StepModifier can be used to select
111     which Qt::KeyboardModifier increases the step rate. Qt::NoModifier
112     disables this feature.
113 
114     QAbstractSpinBox also provide a virtual function stepEnabled() to
115     determine whether stepping up/down is allowed at any point. This
116     function returns a bitset of StepEnabled.
117 
118     \sa QAbstractSlider, QSpinBox, QDoubleSpinBox, QDateTimeEdit,
119         {Spin Boxes Example}
120 */
121 
122 /*!
123     \enum QAbstractSpinBox::StepEnabledFlag
124 
125     \value StepNone
126     \value StepUpEnabled
127     \value StepDownEnabled
128 */
129 
130 /*!
131     \enum QAbstractSpinBox::StepType
132 
133     \value DefaultStepType
134     \value AdaptiveDecimalStepType
135 */
136 
137 /*!
138   \fn void QAbstractSpinBox::editingFinished()
139 
140   This signal is emitted editing is finished. This happens when the
141   spinbox loses focus and when enter is pressed.
142 */
143 
144 /*!
145     Constructs an abstract spinbox with the given \a parent with default
146     \l wrapping, and \l alignment properties.
147 */
148 
QAbstractSpinBox(QWidget * parent)149 QAbstractSpinBox::QAbstractSpinBox(QWidget *parent)
150     : QWidget(*new QAbstractSpinBoxPrivate, parent, { })
151 {
152     Q_D(QAbstractSpinBox);
153     d->init();
154 }
155 
156 /*!
157     \internal
158 */
QAbstractSpinBox(QAbstractSpinBoxPrivate & dd,QWidget * parent)159 QAbstractSpinBox::QAbstractSpinBox(QAbstractSpinBoxPrivate &dd, QWidget *parent)
160     : QWidget(dd, parent, { })
161 {
162     Q_D(QAbstractSpinBox);
163     d->init();
164 }
165 
166 /*!
167     Called when the QAbstractSpinBox is destroyed.
168 */
169 
~QAbstractSpinBox()170 QAbstractSpinBox::~QAbstractSpinBox()
171 {
172 }
173 
174 /*!
175     \enum QAbstractSpinBox::ButtonSymbols
176 
177     This enum type describes the symbols that can be displayed on the buttons
178     in a spin box.
179 
180     \inlineimage qspinbox-updown.png
181     \inlineimage qspinbox-plusminus.png
182 
183     \value UpDownArrows Little arrows in the classic style.
184     \value PlusMinus \b{+} and \b{-} symbols.
185     \value NoButtons Don't display buttons.
186 
187     \sa QAbstractSpinBox::buttonSymbols
188 */
189 
190 /*!
191     \property QAbstractSpinBox::buttonSymbols
192 
193     \brief the current button symbol mode
194 
195     The possible values can be either \c UpDownArrows or \c PlusMinus.
196     The default is \c UpDownArrows.
197 
198     Note that some styles might render PlusMinus and UpDownArrows
199     identically.
200 
201     \sa ButtonSymbols
202 */
203 
buttonSymbols() const204 QAbstractSpinBox::ButtonSymbols QAbstractSpinBox::buttonSymbols() const
205 {
206     Q_D(const QAbstractSpinBox);
207     return d->buttonSymbols;
208 }
209 
setButtonSymbols(ButtonSymbols buttonSymbols)210 void QAbstractSpinBox::setButtonSymbols(ButtonSymbols buttonSymbols)
211 {
212     Q_D(QAbstractSpinBox);
213     if (d->buttonSymbols != buttonSymbols) {
214         d->buttonSymbols = buttonSymbols;
215         d->updateEditFieldGeometry();
216         updateGeometry();
217         update();
218     }
219 }
220 
221 /*!
222     \property QAbstractSpinBox::text
223 
224     \brief the spin box's text, including any prefix and suffix
225 
226     There is no default text.
227 */
228 
text() const229 QString QAbstractSpinBox::text() const
230 {
231     return lineEdit()->displayText();
232 }
233 
234 
235 /*!
236     \property QAbstractSpinBox::specialValueText
237     \brief the special-value text
238 
239     If set, the spin box will display this text instead of a numeric
240     value whenever the current value is equal to minimum(). Typical use
241     is to indicate that this choice has a special (default) meaning.
242 
243     For example, if your spin box allows the user to choose a scale factor
244     (or zoom level) for displaying an image, and your application is able
245     to automatically choose one that will enable the image to fit completely
246     within the display window, you can set up the spin box like this:
247 
248     \snippet widgets/spinboxes/window.cpp 3
249 
250     The user will then be able to choose a scale from 1% to 1000%
251     or select "Auto" to leave it up to the application to choose. Your code
252     must then interpret the spin box value of 0 as a request from the user
253     to scale the image to fit inside the window.
254 
255     All values are displayed with the prefix and suffix (if set), \e
256     except for the special value, which only shows the special value
257     text. This special text is passed in the QSpinBox::textChanged()
258     signal that passes a QString.
259 
260     To turn off the special-value text display, call this function
261     with an empty string. The default is no special-value text, i.e.
262     the numeric value is shown as usual.
263 
264     If no special-value text is set, specialValueText() returns an
265     empty string.
266 */
267 
specialValueText() const268 QString QAbstractSpinBox::specialValueText() const
269 {
270     Q_D(const QAbstractSpinBox);
271     return d->specialValueText;
272 }
273 
setSpecialValueText(const QString & specialValueText)274 void QAbstractSpinBox::setSpecialValueText(const QString &specialValueText)
275 {
276     Q_D(QAbstractSpinBox);
277 
278     d->specialValueText = specialValueText;
279     d->cachedSizeHint = QSize(); // minimumSizeHint doesn't care about specialValueText
280     d->clearCache();
281     d->updateEdit();
282 }
283 
284 /*!
285     \property QAbstractSpinBox::wrapping
286 
287     \brief whether the spin box is circular.
288 
289     If wrapping is true stepping up from maximum() value will take you
290     to the minimum() value and vice versa. Wrapping only make sense if
291     you have minimum() and maximum() values set.
292 
293     \snippet code/src_gui_widgets_qabstractspinbox.cpp 0
294 
295     \sa QSpinBox::minimum(), QSpinBox::maximum()
296 */
297 
wrapping() const298 bool QAbstractSpinBox::wrapping() const
299 {
300     Q_D(const QAbstractSpinBox);
301     return d->wrapping;
302 }
303 
setWrapping(bool wrapping)304 void QAbstractSpinBox::setWrapping(bool wrapping)
305 {
306     Q_D(QAbstractSpinBox);
307     d->wrapping = wrapping;
308 }
309 
310 
311 /*!
312     \property QAbstractSpinBox::readOnly
313     \brief whether the spin box is read only.
314 
315     In read-only mode, the user can still copy the text to the
316     clipboard, or drag and drop the text;
317     but cannot edit it.
318 
319     The QLineEdit in the QAbstractSpinBox does not show a cursor in
320     read-only mode.
321 
322     \sa QLineEdit::readOnly
323 */
324 
isReadOnly() const325 bool QAbstractSpinBox::isReadOnly() const
326 {
327     Q_D(const QAbstractSpinBox);
328     return d->readOnly;
329 }
330 
setReadOnly(bool enable)331 void QAbstractSpinBox::setReadOnly(bool enable)
332 {
333     Q_D(QAbstractSpinBox);
334     d->readOnly = enable;
335     d->edit->setReadOnly(enable);
336     QEvent event(QEvent::ReadOnlyChange);
337     QCoreApplication::sendEvent(this, &event);
338     update();
339 }
340 
341 /*!
342     \property QAbstractSpinBox::keyboardTracking
343     \brief whether keyboard tracking is enabled for the spinbox.
344     \since 4.3
345 
346     If keyboard tracking is enabled (the default), the spinbox
347     emits the valueChanged() and textChanged() signals while the
348     new value is being entered from the keyboard.
349 
350     E.g. when the user enters the value 600 by typing 6, 0, and 0,
351     the spinbox emits 3 signals with the values 6, 60, and 600
352     respectively.
353 
354     If keyboard tracking is disabled, the spinbox doesn't emit the
355     valueChanged() and textChanged() signals while typing. It emits
356     the signals later, when the return key is pressed, when keyboard
357     focus is lost, or when other spinbox functionality is used, e.g.
358     pressing an arrow key.
359 */
360 
keyboardTracking() const361 bool QAbstractSpinBox::keyboardTracking() const
362 {
363     Q_D(const QAbstractSpinBox);
364     return d->keyboardTracking;
365 }
366 
setKeyboardTracking(bool enable)367 void QAbstractSpinBox::setKeyboardTracking(bool enable)
368 {
369     Q_D(QAbstractSpinBox);
370     d->keyboardTracking = enable;
371 }
372 
373 /*!
374     \property QAbstractSpinBox::frame
375     \brief whether the spin box draws itself with a frame
376 
377     If enabled (the default) the spin box draws itself inside a frame,
378     otherwise the spin box draws itself without any frame.
379 */
380 
hasFrame() const381 bool QAbstractSpinBox::hasFrame() const
382 {
383     Q_D(const QAbstractSpinBox);
384     return d->frame;
385 }
386 
387 
setFrame(bool enable)388 void QAbstractSpinBox::setFrame(bool enable)
389 {
390     Q_D(QAbstractSpinBox);
391     d->frame = enable;
392     update();
393     d->updateEditFieldGeometry();
394 }
395 
396 /*!
397     \property QAbstractSpinBox::accelerated
398     \brief whether the spin box will accelerate the frequency of the steps when
399     pressing the step Up/Down buttons.
400     \since 4.2
401 
402     If enabled the spin box will increase/decrease the value faster
403     the longer you hold the button down.
404 */
405 
setAccelerated(bool accelerate)406 void QAbstractSpinBox::setAccelerated(bool accelerate)
407 {
408     Q_D(QAbstractSpinBox);
409     d->accelerate = accelerate;
410 
411 }
isAccelerated() const412 bool QAbstractSpinBox::isAccelerated() const
413 {
414     Q_D(const QAbstractSpinBox);
415     return d->accelerate;
416 }
417 
418 /*!
419      \property QAbstractSpinBox::showGroupSeparator
420      \since 5.3
421 
422 
423      This property holds whether a thousands separator is enabled. By default this
424      property is false.
425 */
isGroupSeparatorShown() const426 bool QAbstractSpinBox::isGroupSeparatorShown() const
427 {
428     Q_D(const QAbstractSpinBox);
429     return d->showGroupSeparator;
430 }
431 
setGroupSeparatorShown(bool shown)432 void QAbstractSpinBox::setGroupSeparatorShown(bool shown)
433 {
434     Q_D(QAbstractSpinBox);
435     if (d->showGroupSeparator == shown)
436         return;
437     d->showGroupSeparator = shown;
438     d->setValue(d->value, EmitIfChanged);
439     updateGeometry();
440 }
441 
442 /*!
443     \enum QAbstractSpinBox::CorrectionMode
444 
445     This enum type describes the mode the spinbox will use to correct
446     an \l{QValidator::}{Intermediate} value if editing finishes.
447 
448     \value CorrectToPreviousValue The spinbox will revert to the last
449                                   valid value.
450 
451     \value CorrectToNearestValue The spinbox will revert to the nearest
452                                  valid value.
453 
454     \sa correctionMode
455 */
456 
457 /*!
458     \property QAbstractSpinBox::correctionMode
459     \brief the mode to correct an \l{QValidator::}{Intermediate}
460            value if editing finishes
461     \since 4.2
462 
463     The default mode is QAbstractSpinBox::CorrectToPreviousValue.
464 
465     \sa acceptableInput, validate(), fixup()
466 */
setCorrectionMode(CorrectionMode correctionMode)467 void QAbstractSpinBox::setCorrectionMode(CorrectionMode correctionMode)
468 {
469     Q_D(QAbstractSpinBox);
470     d->correctionMode = correctionMode;
471 
472 }
correctionMode() const473 QAbstractSpinBox::CorrectionMode QAbstractSpinBox::correctionMode() const
474 {
475     Q_D(const QAbstractSpinBox);
476     return d->correctionMode;
477 }
478 
479 
480 /*!
481   \property QAbstractSpinBox::acceptableInput
482   \brief whether the input satisfies the current validation
483   \since 4.2
484 
485   \sa validate(), fixup(), correctionMode
486 */
487 
hasAcceptableInput() const488 bool QAbstractSpinBox::hasAcceptableInput() const
489 {
490     Q_D(const QAbstractSpinBox);
491     return d->edit->hasAcceptableInput();
492 }
493 
494 /*!
495     \property QAbstractSpinBox::alignment
496     \brief the alignment of the spin box
497 
498     Possible Values are Qt::AlignLeft, Qt::AlignRight, and Qt::AlignHCenter.
499 
500     By default, the alignment is Qt::AlignLeft
501 
502     Attempting to set the alignment to an illegal flag combination
503     does nothing.
504 
505     \sa Qt::Alignment
506 */
507 
alignment() const508 Qt::Alignment QAbstractSpinBox::alignment() const
509 {
510     Q_D(const QAbstractSpinBox);
511 
512     return (Qt::Alignment)d->edit->alignment();
513 }
514 
setAlignment(Qt::Alignment flag)515 void QAbstractSpinBox::setAlignment(Qt::Alignment flag)
516 {
517     Q_D(QAbstractSpinBox);
518 
519     d->edit->setAlignment(flag);
520 }
521 
522 /*!
523     Selects all the text in the spinbox except the prefix and suffix.
524 */
525 
selectAll()526 void QAbstractSpinBox::selectAll()
527 {
528     Q_D(QAbstractSpinBox);
529 
530 
531     if (!d->specialValue()) {
532         const int tmp = d->edit->displayText().size() - d->suffix.size();
533         d->edit->setSelection(tmp, -(tmp - d->prefix.size()));
534     } else {
535         d->edit->selectAll();
536     }
537 }
538 
539 /*!
540     Clears the lineedit of all text but prefix and suffix.
541 */
542 
clear()543 void QAbstractSpinBox::clear()
544 {
545     Q_D(QAbstractSpinBox);
546 
547     d->edit->setText(d->prefix + d->suffix);
548     d->edit->setCursorPosition(d->prefix.size());
549     d->cleared = true;
550 }
551 
552 /*!
553     Virtual function that determines whether stepping up and down is
554     legal at any given time.
555 
556     The up arrow will be painted as disabled unless (stepEnabled() &
557     StepUpEnabled) != 0.
558 
559     The default implementation will return (StepUpEnabled|
560     StepDownEnabled) if wrapping is turned on. Else it will return
561     StepDownEnabled if value is > minimum() or'ed with StepUpEnabled if
562     value < maximum().
563 
564     If you subclass QAbstractSpinBox you will need to reimplement this function.
565 
566     \sa QSpinBox::minimum(), QSpinBox::maximum(), wrapping()
567 */
568 
569 
stepEnabled() const570 QAbstractSpinBox::StepEnabled QAbstractSpinBox::stepEnabled() const
571 {
572     Q_D(const QAbstractSpinBox);
573     if (d->readOnly || d->type == QMetaType::UnknownType)
574         return StepNone;
575     if (d->wrapping)
576         return StepEnabled(StepUpEnabled | StepDownEnabled);
577     StepEnabled ret = StepNone;
578     if (QAbstractSpinBoxPrivate::variantCompare(d->value, d->maximum) < 0) {
579         ret |= StepUpEnabled;
580     }
581     if (QAbstractSpinBoxPrivate::variantCompare(d->value, d->minimum) > 0) {
582         ret |= StepDownEnabled;
583     }
584     return ret;
585 }
586 
587 /*!
588    This virtual function is called by the QAbstractSpinBox to
589    determine whether \a input is valid. The \a pos parameter indicates
590    the position in the string. Reimplemented in the various
591    subclasses.
592 */
593 
validate(QString &,int &) const594 QValidator::State QAbstractSpinBox::validate(QString & /* input */, int & /* pos */) const
595 {
596     return QValidator::Acceptable;
597 }
598 
599 /*!
600    This virtual function is called by the QAbstractSpinBox if the
601    \a input is not validated to QValidator::Acceptable when Return is
602    pressed or interpretText() is called. It will try to change the
603    text so it is valid. Reimplemented in the various subclasses.
604 */
605 
fixup(QString &) const606 void QAbstractSpinBox::fixup(QString & /* input */) const
607 {
608 }
609 
610 /*!
611   Steps up by one linestep
612   Calling this slot is analogous to calling stepBy(1);
613   \sa stepBy(), stepDown()
614 */
615 
stepUp()616 void QAbstractSpinBox::stepUp()
617 {
618     stepBy(1);
619 }
620 
621 /*!
622   Steps down by one linestep
623   Calling this slot is analogous to calling stepBy(-1);
624   \sa stepBy(), stepUp()
625 */
626 
stepDown()627 void QAbstractSpinBox::stepDown()
628 {
629     stepBy(-1);
630 }
631 /*!
632     Virtual function that is called whenever the user triggers a step.
633     The \a steps parameter indicates how many steps were taken.
634     For example, pressing \c Qt::Key_Down will trigger a call to \c stepBy(-1),
635     whereas pressing \c Qt::Key_PageUp will trigger a call to \c stepBy(10).
636 
637     If you subclass \c QAbstractSpinBox you must reimplement this
638     function. Note that this function is called even if the resulting
639     value will be outside the bounds of minimum and maximum. It's this
640     function's job to handle these situations.
641 
642     \sa stepUp(), stepDown(), keyPressEvent()
643 */
644 
stepBy(int steps)645 void QAbstractSpinBox::stepBy(int steps)
646 {
647     Q_D(QAbstractSpinBox);
648 
649     const QVariant old = d->value;
650     QString tmp = d->edit->displayText();
651     int cursorPos = d->edit->cursorPosition();
652     bool dontstep = false;
653     EmitPolicy e = EmitIfChanged;
654     if (d->pendingEmit) {
655         dontstep = validate(tmp, cursorPos) != QValidator::Acceptable;
656         d->cleared = false;
657         d->interpret(NeverEmit);
658         if (d->value != old)
659             e = AlwaysEmit;
660     }
661     if (!dontstep) {
662         QVariant singleStep;
663         switch (d->stepType) {
664         case QAbstractSpinBox::StepType::AdaptiveDecimalStepType:
665             singleStep = d->calculateAdaptiveDecimalStep(steps);
666             break;
667         default:
668             singleStep = d->singleStep;
669         }
670         d->setValue(d->bound(d->value + (singleStep * steps), old, steps), e);
671     } else if (e == AlwaysEmit) {
672         d->emitSignals(e, old);
673     }
674     selectAll();
675 }
676 
677 /*!
678     This function returns a pointer to the line edit of the spin box.
679 */
680 
lineEdit() const681 QLineEdit *QAbstractSpinBox::lineEdit() const
682 {
683     Q_D(const QAbstractSpinBox);
684 
685     return d->edit;
686 }
687 
688 
689 /*!
690     \fn void QAbstractSpinBox::setLineEdit(QLineEdit *lineEdit)
691 
692     Sets the line edit of the spinbox to be \a lineEdit instead of the
693     current line edit widget. \a lineEdit cannot be \nullptr.
694 
695     QAbstractSpinBox takes ownership of the new lineEdit
696 
697     If QLineEdit::validator() for the \a lineEdit returns \nullptr, the internal
698     validator of the spinbox will be set on the line edit.
699 */
700 
setLineEdit(QLineEdit * lineEdit)701 void QAbstractSpinBox::setLineEdit(QLineEdit *lineEdit)
702 {
703     Q_D(QAbstractSpinBox);
704 
705     if (!lineEdit) {
706         Q_ASSERT(lineEdit);
707         return;
708     }
709 
710     if (lineEdit == d->edit)
711         return;
712 
713     delete d->edit;
714     d->edit = lineEdit;
715     setProperty("_q_spinbox_lineedit", QVariant::fromValue<QWidget *>(d->edit));
716     if (!d->edit->validator())
717         d->edit->setValidator(d->validator);
718 
719     if (d->edit->parent() != this)
720         d->edit->setParent(this);
721 
722     d->edit->setFrame(!style()->styleHint(QStyle::SH_SpinBox_ButtonsInsideFrame, nullptr, this));
723     d->edit->setFocusProxy(this);
724     d->edit->setAcceptDrops(false);
725 
726     if (d->type != QMetaType::UnknownType) {
727         connect(d->edit, SIGNAL(textChanged(QString)),
728                 this, SLOT(_q_editorTextChanged(QString)));
729         connect(d->edit, SIGNAL(cursorPositionChanged(int,int)),
730                 this, SLOT(_q_editorCursorPositionChanged(int,int)));
731         connect(d->edit, SIGNAL(cursorPositionChanged(int,int)),
732                 this, SLOT(updateMicroFocus()));
733         connect(d->edit->d_func()->control, SIGNAL(updateMicroFocus()),
734                 this, SLOT(updateMicroFocus()));
735     }
736     d->updateEditFieldGeometry();
737     d->edit->setContextMenuPolicy(Qt::NoContextMenu);
738     d->edit->d_func()->control->setAccessibleObject(this);
739 
740     if (isVisible())
741         d->edit->show();
742     if (isVisible())
743         d->updateEdit();
744 }
745 
746 
747 /*!
748     This function interprets the text of the spin box. If the value
749     has changed since last interpretation it will emit signals.
750 */
751 
interpretText()752 void QAbstractSpinBox::interpretText()
753 {
754     Q_D(QAbstractSpinBox);
755     d->interpret(EmitIfChanged);
756 }
757 
758 /*
759     Reimplemented in 4.6, so be careful.
760  */
761 /*!
762     \reimp
763 */
inputMethodQuery(Qt::InputMethodQuery query) const764 QVariant QAbstractSpinBox::inputMethodQuery(Qt::InputMethodQuery query) const
765 {
766     Q_D(const QAbstractSpinBox);
767     const QVariant lineEditValue = d->edit->inputMethodQuery(query);
768     switch (query) {
769     case Qt::ImHints:
770         if (const int hints = inputMethodHints())
771             return QVariant(hints | lineEditValue.toInt());
772         break;
773     default:
774         break;
775     }
776     return lineEditValue;
777 }
778 
779 /*!
780     \reimp
781 */
782 
event(QEvent * event)783 bool QAbstractSpinBox::event(QEvent *event)
784 {
785     Q_D(QAbstractSpinBox);
786     switch (event->type()) {
787     case QEvent::FontChange:
788     case QEvent::StyleChange:
789         d->cachedSizeHint = d->cachedMinimumSizeHint = QSize();
790         break;
791     case QEvent::ApplicationLayoutDirectionChange:
792     case QEvent::LayoutDirectionChange:
793         d->updateEditFieldGeometry();
794         break;
795     case QEvent::HoverEnter:
796     case QEvent::HoverLeave:
797     case QEvent::HoverMove:
798         d->updateHoverControl(static_cast<const QHoverEvent *>(event)->pos());
799         break;
800     case QEvent::ShortcutOverride:
801         if (d->edit->event(event))
802             return true;
803         break;
804 #ifdef QT_KEYPAD_NAVIGATION
805     case QEvent::EnterEditFocus:
806     case QEvent::LeaveEditFocus:
807         if (QApplicationPrivate::keypadNavigationEnabled()) {
808             const bool b = d->edit->event(event);
809             d->edit->setSelection(d->edit->displayText().size() - d->suffix.size(),0);
810             if (event->type() == QEvent::LeaveEditFocus)
811                 emit editingFinished();
812             if (b)
813                 return true;
814         }
815         break;
816 #endif
817     case QEvent::InputMethod:
818         return d->edit->event(event);
819     default:
820         break;
821     }
822     return QWidget::event(event);
823 }
824 
825 /*!
826     \reimp
827 */
828 
showEvent(QShowEvent *)829 void QAbstractSpinBox::showEvent(QShowEvent *)
830 {
831     Q_D(QAbstractSpinBox);
832     d->reset();
833 
834     if (d->ignoreUpdateEdit) {
835         d->ignoreUpdateEdit = false;
836     } else {
837         d->updateEdit();
838     }
839 }
840 
841 /*!
842     \reimp
843 */
844 
changeEvent(QEvent * event)845 void QAbstractSpinBox::changeEvent(QEvent *event)
846 {
847     Q_D(QAbstractSpinBox);
848 
849     switch (event->type()) {
850         case QEvent::StyleChange:
851             d->spinClickTimerInterval = style()->styleHint(QStyle::SH_SpinBox_ClickAutoRepeatRate, nullptr, this);
852             d->spinClickThresholdTimerInterval =
853                 style()->styleHint(QStyle::SH_SpinBox_ClickAutoRepeatThreshold, nullptr, this);
854             if (d->edit)
855                 d->edit->setFrame(!style()->styleHint(QStyle::SH_SpinBox_ButtonsInsideFrame, nullptr, this));
856             d->stepModifier = static_cast<Qt::KeyboardModifier>(style()->styleHint(QStyle::SH_SpinBox_StepModifier, nullptr, this));
857             d->reset();
858             d->updateEditFieldGeometry();
859             break;
860         case QEvent::LocaleChange:
861             d->updateEdit();
862             break;
863         case QEvent::EnabledChange:
864             if (!isEnabled()) {
865                 d->reset();
866             }
867             break;
868         case QEvent::ActivationChange:
869             if (!isActiveWindow()){
870                 d->reset();
871                 if (d->pendingEmit) // pendingEmit can be true even if it hasn't changed.
872                     d->interpret(EmitIfChanged); // E.g. 10 to 10.0
873             }
874             break;
875         default:
876             break;
877     }
878     QWidget::changeEvent(event);
879 }
880 
881 /*!
882     \reimp
883 */
884 
resizeEvent(QResizeEvent * event)885 void QAbstractSpinBox::resizeEvent(QResizeEvent *event)
886 {
887     Q_D(QAbstractSpinBox);
888     QWidget::resizeEvent(event);
889 
890     d->updateEditFieldGeometry();
891     update();
892 }
893 
894 /*!
895     \reimp
896 */
897 
sizeHint() const898 QSize QAbstractSpinBox::sizeHint() const
899 {
900     Q_D(const QAbstractSpinBox);
901     if (d->cachedSizeHint.isEmpty()) {
902         ensurePolished();
903 
904         const QFontMetrics fm(fontMetrics());
905         int h = d->edit->sizeHint().height();
906         int w = 0;
907         QString s;
908         QString fixedContent =  d->prefix + d->suffix + QLatin1Char(' ');
909         s = d->textFromValue(d->minimum);
910         s.truncate(18);
911         s += fixedContent;
912         w = qMax(w, fm.horizontalAdvance(s));
913         s = d->textFromValue(d->maximum);
914         s.truncate(18);
915         s += fixedContent;
916         w = qMax(w, fm.horizontalAdvance(s));
917 
918         if (d->specialValueText.size()) {
919             s = d->specialValueText;
920             w = qMax(w, fm.horizontalAdvance(s));
921         }
922         w += 2; // cursor blinking space
923 
924         QStyleOptionSpinBox opt;
925         initStyleOption(&opt);
926         QSize hint(w, h);
927         d->cachedSizeHint = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this)
928                             .expandedTo(QApplication::globalStrut());
929     }
930     return d->cachedSizeHint;
931 }
932 
933 /*!
934     \reimp
935 */
936 
minimumSizeHint() const937 QSize QAbstractSpinBox::minimumSizeHint() const
938 {
939     Q_D(const QAbstractSpinBox);
940     if (d->cachedMinimumSizeHint.isEmpty()) {
941         //Use the prefix and range to calculate the minimumSizeHint
942         ensurePolished();
943 
944         const QFontMetrics fm(fontMetrics());
945         int h = d->edit->minimumSizeHint().height();
946         int w = 0;
947 
948         QString s;
949         QString fixedContent =  d->prefix + QLatin1Char(' ');
950         s = d->textFromValue(d->minimum);
951         s.truncate(18);
952         s += fixedContent;
953         w = qMax(w, fm.horizontalAdvance(s));
954         s = d->textFromValue(d->maximum);
955         s.truncate(18);
956         s += fixedContent;
957         w = qMax(w, fm.horizontalAdvance(s));
958 
959         if (d->specialValueText.size()) {
960             s = d->specialValueText;
961             w = qMax(w, fm.horizontalAdvance(s));
962         }
963         w += 2; // cursor blinking space
964 
965         QStyleOptionSpinBox opt;
966         initStyleOption(&opt);
967         QSize hint(w, h);
968 
969         d->cachedMinimumSizeHint = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this)
970                                    .expandedTo(QApplication::globalStrut());
971     }
972     return d->cachedMinimumSizeHint;
973 }
974 
975 /*!
976     \reimp
977 */
978 
paintEvent(QPaintEvent *)979 void QAbstractSpinBox::paintEvent(QPaintEvent *)
980 {
981     QStyleOptionSpinBox opt;
982     initStyleOption(&opt);
983     QStylePainter p(this);
984     p.drawComplexControl(QStyle::CC_SpinBox, opt);
985 }
986 
987 /*!
988     \reimp
989 
990     This function handles keyboard input.
991 
992     The following keys are handled specifically:
993     \table
994     \row \li Enter/Return
995          \li This will reinterpret the text and emit a signal even if the value has not changed
996          since last time a signal was emitted.
997     \row \li Up
998          \li This will invoke stepBy(1)
999     \row \li Down
1000          \li This will invoke stepBy(-1)
1001     \row \li Page up
1002          \li This will invoke stepBy(10)
1003     \row \li Page down
1004          \li This will invoke stepBy(-10)
1005     \endtable
1006 
1007     \sa stepBy()
1008 */
1009 
1010 
keyPressEvent(QKeyEvent * event)1011 void QAbstractSpinBox::keyPressEvent(QKeyEvent *event)
1012 {
1013     Q_D(QAbstractSpinBox);
1014 
1015     if (!event->text().isEmpty() && d->edit->cursorPosition() < d->prefix.size())
1016         d->edit->setCursorPosition(d->prefix.size());
1017 
1018     int steps = 1;
1019     bool isPgUpOrDown = false;
1020     switch (event->key()) {
1021     case Qt::Key_PageUp:
1022     case Qt::Key_PageDown:
1023         steps *= 10;
1024         isPgUpOrDown = true;
1025         Q_FALLTHROUGH();
1026     case Qt::Key_Up:
1027     case Qt::Key_Down: {
1028 #ifdef QT_KEYPAD_NAVIGATION
1029         if (QApplicationPrivate::keypadNavigationEnabled()) {
1030             // Reserve up/down for nav - use left/right for edit.
1031             if (!hasEditFocus() && (event->key() == Qt::Key_Up
1032                                     || event->key() == Qt::Key_Down)) {
1033                 event->ignore();
1034                 return;
1035             }
1036         }
1037 #endif
1038         event->accept();
1039         const bool up = (event->key() == Qt::Key_PageUp || event->key() == Qt::Key_Up);
1040         if (!(stepEnabled() & (up ? StepUpEnabled : StepDownEnabled)))
1041             return;
1042         if (!isPgUpOrDown && (event->modifiers() & d->stepModifier))
1043             steps *= 10;
1044         if (!up)
1045             steps *= -1;
1046         if (style()->styleHint(QStyle::SH_SpinBox_AnimateButton, nullptr, this)) {
1047             d->buttonState = (Keyboard | (up ? Up : Down));
1048         }
1049         if (d->spinClickTimerId == -1)
1050             stepBy(steps);
1051         if(event->isAutoRepeat() && !isPgUpOrDown) {
1052             if(d->spinClickThresholdTimerId == -1 && d->spinClickTimerId == -1) {
1053                 d->updateState(up, true);
1054             }
1055         }
1056 #ifndef QT_NO_ACCESSIBILITY
1057         QAccessibleValueChangeEvent event(this, d->value);
1058         QAccessible::updateAccessibility(&event);
1059 #endif
1060         return;
1061     }
1062 #ifdef QT_KEYPAD_NAVIGATION
1063     case Qt::Key_Left:
1064     case Qt::Key_Right:
1065         if (QApplicationPrivate::keypadNavigationEnabled() && !hasEditFocus()) {
1066             event->ignore();
1067             return;
1068         }
1069         break;
1070     case Qt::Key_Back:
1071         if (QApplicationPrivate::keypadNavigationEnabled() && !hasEditFocus()) {
1072             event->ignore();
1073             return;
1074         }
1075         break;
1076 #endif
1077     case Qt::Key_Enter:
1078     case Qt::Key_Return:
1079         d->edit->d_func()->control->clearUndo();
1080         d->interpret(d->keyboardTracking ? AlwaysEmit : EmitIfChanged);
1081         selectAll();
1082         event->ignore();
1083         emit editingFinished();
1084         emit d->edit->returnPressed();
1085         return;
1086 
1087 #ifdef QT_KEYPAD_NAVIGATION
1088     case Qt::Key_Select:
1089         if (QApplicationPrivate::keypadNavigationEnabled()) {
1090             // Toggles between left/right moving cursor and inc/dec.
1091             setEditFocus(!hasEditFocus());
1092         }
1093         return;
1094 #endif
1095 
1096     case Qt::Key_U:
1097         if (event->modifiers() & Qt::ControlModifier
1098             && QGuiApplication::platformName() == QLatin1String("xcb")) { // only X11
1099             event->accept();
1100             if (!isReadOnly())
1101                 clear();
1102             return;
1103         }
1104         break;
1105 
1106     case Qt::Key_End:
1107     case Qt::Key_Home:
1108         if (event->modifiers() & Qt::ShiftModifier) {
1109             int currentPos = d->edit->cursorPosition();
1110             const QString text = d->edit->displayText();
1111             if (event->key() == Qt::Key_End) {
1112                 if ((currentPos == 0 && !d->prefix.isEmpty()) || text.size() - d->suffix.size() <= currentPos) {
1113                     break; // let lineedit handle this
1114                 } else {
1115                     d->edit->setSelection(currentPos, text.size() - d->suffix.size() - currentPos);
1116                 }
1117             } else {
1118                 if ((currentPos == text.size() && !d->suffix.isEmpty()) || currentPos <= d->prefix.size()) {
1119                     break; // let lineedit handle this
1120                 } else {
1121                     d->edit->setSelection(currentPos, d->prefix.size() - currentPos);
1122                 }
1123             }
1124             event->accept();
1125             return;
1126         }
1127         break;
1128 
1129     default:
1130 #ifndef QT_NO_SHORTCUT
1131         if (event == QKeySequence::SelectAll) {
1132             selectAll();
1133             event->accept();
1134             return;
1135         }
1136 #endif
1137         break;
1138     }
1139 
1140     d->edit->event(event);
1141     if (!d->edit->text().isEmpty())
1142         d->cleared = false;
1143     if (!isVisible())
1144         d->ignoreUpdateEdit = true;
1145 }
1146 
1147 /*!
1148     \reimp
1149 */
1150 
keyReleaseEvent(QKeyEvent * event)1151 void QAbstractSpinBox::keyReleaseEvent(QKeyEvent *event)
1152 {
1153     Q_D(QAbstractSpinBox);
1154 
1155     if (d->buttonState & Keyboard && !event->isAutoRepeat())  {
1156         d->reset();
1157     } else {
1158         d->edit->event(event);
1159     }
1160 }
1161 
1162 /*!
1163     \reimp
1164 */
1165 
1166 #if QT_CONFIG(wheelevent)
wheelEvent(QWheelEvent * event)1167 void QAbstractSpinBox::wheelEvent(QWheelEvent *event)
1168 {
1169     Q_D(QAbstractSpinBox);
1170 #ifdef Q_OS_MACOS
1171     // If the event comes from a real mouse wheel, rather than a track pad
1172     // (Qt::MouseEventSynthesizedBySystem), the shift modifier changes the
1173     // scroll orientation to horizontal.
1174     // Convert horizontal events back to vertical whilst shift is held.
1175     if ((event->modifiers() & Qt::ShiftModifier)
1176             && event->source() == Qt::MouseEventNotSynthesized) {
1177         d->wheelDeltaRemainder += event->angleDelta().x();
1178     } else {
1179         d->wheelDeltaRemainder += event->angleDelta().y();
1180     }
1181 #else
1182     d->wheelDeltaRemainder += event->angleDelta().y();
1183 #endif
1184     const int steps = d->wheelDeltaRemainder / 120;
1185     d->wheelDeltaRemainder -= steps * 120;
1186     if (stepEnabled() & (steps > 0 ? StepUpEnabled : StepDownEnabled))
1187         stepBy(event->modifiers() & d->stepModifier ? steps * 10 : steps);
1188     event->accept();
1189 }
1190 #endif
1191 
1192 
1193 /*!
1194     \reimp
1195 */
focusInEvent(QFocusEvent * event)1196 void QAbstractSpinBox::focusInEvent(QFocusEvent *event)
1197 {
1198     Q_D(QAbstractSpinBox);
1199 
1200     d->edit->event(event);
1201     if (event->reason() == Qt::TabFocusReason || event->reason() == Qt::BacktabFocusReason) {
1202         selectAll();
1203     }
1204     QWidget::focusInEvent(event);
1205 }
1206 
1207 /*!
1208     \reimp
1209 */
1210 
focusOutEvent(QFocusEvent * event)1211 void QAbstractSpinBox::focusOutEvent(QFocusEvent *event)
1212 {
1213     Q_D(QAbstractSpinBox);
1214 
1215     if (d->pendingEmit)
1216         d->interpret(EmitIfChanged);
1217 
1218     d->reset();
1219     d->edit->event(event);
1220     d->updateEdit();
1221     QWidget::focusOutEvent(event);
1222 
1223 #ifdef QT_KEYPAD_NAVIGATION
1224     // editingFinished() is already emitted on LeaveEditFocus
1225     if (!QApplicationPrivate::keypadNavigationEnabled())
1226 #endif
1227     emit editingFinished();
1228 }
1229 
1230 /*!
1231     \reimp
1232 */
1233 
closeEvent(QCloseEvent * event)1234 void QAbstractSpinBox::closeEvent(QCloseEvent *event)
1235 {
1236     Q_D(QAbstractSpinBox);
1237 
1238     d->reset();
1239     if (d->pendingEmit)
1240         d->interpret(EmitIfChanged);
1241     QWidget::closeEvent(event);
1242 }
1243 
1244 /*!
1245     \reimp
1246 */
1247 
hideEvent(QHideEvent * event)1248 void QAbstractSpinBox::hideEvent(QHideEvent *event)
1249 {
1250     Q_D(QAbstractSpinBox);
1251     d->reset();
1252     if (d->pendingEmit)
1253         d->interpret(EmitIfChanged);
1254     QWidget::hideEvent(event);
1255 }
1256 
1257 
1258 /*!
1259     \reimp
1260 */
1261 
timerEvent(QTimerEvent * event)1262 void QAbstractSpinBox::timerEvent(QTimerEvent *event)
1263 {
1264     Q_D(QAbstractSpinBox);
1265 
1266     bool doStep = false;
1267     if (event->timerId() == d->spinClickThresholdTimerId) {
1268         killTimer(d->spinClickThresholdTimerId);
1269         d->spinClickThresholdTimerId = -1;
1270         d->effectiveSpinRepeatRate = d->buttonState & Keyboard
1271                                      ? QGuiApplication::styleHints()->keyboardAutoRepeatRate()
1272                                      : d->spinClickTimerInterval;
1273         d->spinClickTimerId = startTimer(d->effectiveSpinRepeatRate);
1274         doStep = true;
1275     } else if (event->timerId() == d->spinClickTimerId) {
1276         if (d->accelerate) {
1277             d->acceleration = d->acceleration + (int)(d->effectiveSpinRepeatRate * 0.05);
1278             if (d->effectiveSpinRepeatRate - d->acceleration >= 10) {
1279                 killTimer(d->spinClickTimerId);
1280                 d->spinClickTimerId = startTimer(d->effectiveSpinRepeatRate - d->acceleration);
1281             }
1282         }
1283         doStep = true;
1284     }
1285 
1286     if (doStep) {
1287         const bool increaseStepRate = QGuiApplication::keyboardModifiers() & d->stepModifier;
1288         const StepEnabled st = stepEnabled();
1289         if (d->buttonState & Up) {
1290             if (!(st & StepUpEnabled)) {
1291                 d->reset();
1292             } else {
1293                 stepBy(increaseStepRate ? 10 : 1);
1294             }
1295         } else if (d->buttonState & Down) {
1296             if (!(st & StepDownEnabled)) {
1297                 d->reset();
1298             } else {
1299                 stepBy(increaseStepRate ? -10 : -1);
1300             }
1301         }
1302         return;
1303     }
1304     QWidget::timerEvent(event);
1305     return;
1306 }
1307 
1308 /*!
1309     \reimp
1310 */
1311 
1312 #if QT_CONFIG(contextmenu)
contextMenuEvent(QContextMenuEvent * event)1313 void QAbstractSpinBox::contextMenuEvent(QContextMenuEvent *event)
1314 {
1315     Q_D(QAbstractSpinBox);
1316 
1317     QPointer<QMenu> menu = d->edit->createStandardContextMenu();
1318     if (!menu)
1319         return;
1320 
1321     d->reset();
1322 
1323     QAction *selAll = new QAction(tr("&Select All"), menu);
1324 #if QT_CONFIG(shortcut)
1325     selAll->setShortcut(QKeySequence::SelectAll);
1326 #endif
1327     menu->insertAction(d->edit->d_func()->selectAllAction,
1328                       selAll);
1329     menu->removeAction(d->edit->d_func()->selectAllAction);
1330     menu->addSeparator();
1331     const uint se = stepEnabled();
1332     QAction *up = menu->addAction(tr("&Step up"));
1333     up->setEnabled(se & StepUpEnabled);
1334     QAction *down = menu->addAction(tr("Step &down"));
1335     down->setEnabled(se & StepDownEnabled);
1336     menu->addSeparator();
1337 
1338     const QPointer<QAbstractSpinBox> that = this;
1339     const QPoint pos = (event->reason() == QContextMenuEvent::Mouse)
1340         ? event->globalPos() : mapToGlobal(QPoint(event->pos().x(), 0)) + QPoint(width() / 2, height() / 2);
1341     const QAction *action = menu->exec(pos);
1342     delete static_cast<QMenu *>(menu);
1343     if (that && action) {
1344         if (action == up) {
1345             stepBy(1);
1346         } else if (action == down) {
1347             stepBy(-1);
1348         } else if (action == selAll) {
1349             selectAll();
1350         }
1351     }
1352     event->accept();
1353 }
1354 #endif // QT_CONFIG(contextmenu)
1355 
1356 /*!
1357     \reimp
1358 */
1359 
mouseMoveEvent(QMouseEvent * event)1360 void QAbstractSpinBox::mouseMoveEvent(QMouseEvent *event)
1361 {
1362     Q_D(QAbstractSpinBox);
1363 
1364     d->updateHoverControl(event->pos());
1365 
1366     // If we have a timer ID, update the state
1367     if (d->spinClickTimerId != -1 && d->buttonSymbols != NoButtons) {
1368         const StepEnabled se = stepEnabled();
1369         if ((se & StepUpEnabled) && d->hoverControl == QStyle::SC_SpinBoxUp)
1370             d->updateState(true);
1371         else if ((se & StepDownEnabled) && d->hoverControl == QStyle::SC_SpinBoxDown)
1372             d->updateState(false);
1373         else
1374             d->reset();
1375         event->accept();
1376     }
1377 }
1378 
1379 /*!
1380     \reimp
1381 */
1382 
mousePressEvent(QMouseEvent * event)1383 void QAbstractSpinBox::mousePressEvent(QMouseEvent *event)
1384 {
1385     Q_D(QAbstractSpinBox);
1386 
1387     if (event->button() != Qt::LeftButton || d->buttonState != None) {
1388         return;
1389     }
1390 
1391     d->updateHoverControl(event->pos());
1392     event->accept();
1393 
1394     const StepEnabled se = (d->buttonSymbols == NoButtons) ? StepEnabled(StepNone) : stepEnabled();
1395     if ((se & StepUpEnabled) && d->hoverControl == QStyle::SC_SpinBoxUp) {
1396         d->updateState(true);
1397     } else if ((se & StepDownEnabled) && d->hoverControl == QStyle::SC_SpinBoxDown) {
1398         d->updateState(false);
1399     } else {
1400         event->ignore();
1401     }
1402 }
1403 
1404 /*!
1405     \reimp
1406 */
mouseReleaseEvent(QMouseEvent * event)1407 void QAbstractSpinBox::mouseReleaseEvent(QMouseEvent *event)
1408 {
1409     Q_D(QAbstractSpinBox);
1410 
1411     if ((d->buttonState & Mouse) != 0)
1412         d->reset();
1413     event->accept();
1414 }
1415 
1416 // --- QAbstractSpinBoxPrivate ---
1417 
1418 /*!
1419     \internal
1420     Constructs a QAbstractSpinBoxPrivate object
1421 */
1422 
QAbstractSpinBoxPrivate()1423 QAbstractSpinBoxPrivate::QAbstractSpinBoxPrivate()
1424     : edit(nullptr), type(QMetaType::UnknownType), spinClickTimerId(-1),
1425       spinClickTimerInterval(100), spinClickThresholdTimerId(-1), spinClickThresholdTimerInterval(-1),
1426       effectiveSpinRepeatRate(1), buttonState(None), cachedText(QLatin1String("\x01")),
1427       cachedState(QValidator::Invalid), pendingEmit(false), readOnly(false), wrapping(false),
1428       ignoreCursorPositionChanged(false), frame(true), accelerate(false), keyboardTracking(true),
1429       cleared(false), ignoreUpdateEdit(false), correctionMode(QAbstractSpinBox::CorrectToPreviousValue),
1430       stepModifier(Qt::ControlModifier), acceleration(0), hoverControl(QStyle::SC_None),
1431       buttonSymbols(QAbstractSpinBox::UpDownArrows), validator(nullptr), showGroupSeparator(0),
1432       wheelDeltaRemainder(0)
1433 {
1434 }
1435 
1436 /*
1437    \internal
1438    Called when the QAbstractSpinBoxPrivate is destroyed
1439 */
~QAbstractSpinBoxPrivate()1440 QAbstractSpinBoxPrivate::~QAbstractSpinBoxPrivate()
1441 {
1442 }
1443 
1444 /*!
1445     \internal
1446     Updates the old and new hover control. Does nothing if the hover
1447     control has not changed.
1448 */
updateHoverControl(const QPoint & pos)1449 bool QAbstractSpinBoxPrivate::updateHoverControl(const QPoint &pos)
1450 {
1451     Q_Q(QAbstractSpinBox);
1452     QRect lastHoverRect = hoverRect;
1453     QStyle::SubControl lastHoverControl = hoverControl;
1454     bool doesHover = q->testAttribute(Qt::WA_Hover);
1455     if (lastHoverControl != newHoverControl(pos) && doesHover) {
1456         q->update(lastHoverRect);
1457         q->update(hoverRect);
1458         return true;
1459     }
1460     return !doesHover;
1461 }
1462 
1463 /*!
1464     \internal
1465     Returns the hover control at \a pos.
1466     This will update the hoverRect and hoverControl.
1467 */
newHoverControl(const QPoint & pos)1468 QStyle::SubControl QAbstractSpinBoxPrivate::newHoverControl(const QPoint &pos)
1469 {
1470     Q_Q(QAbstractSpinBox);
1471 
1472     QStyleOptionSpinBox opt;
1473     q->initStyleOption(&opt);
1474     opt.subControls = QStyle::SC_All;
1475     hoverControl = q->style()->hitTestComplexControl(QStyle::CC_SpinBox, &opt, pos, q);
1476     hoverRect = q->style()->subControlRect(QStyle::CC_SpinBox, &opt, hoverControl, q);
1477     return hoverControl;
1478 }
1479 
1480 /*!
1481     \internal
1482     Strips any prefix/suffix from \a text.
1483 */
1484 
stripped(const QString & t,int * pos) const1485 QString QAbstractSpinBoxPrivate::stripped(const QString &t, int *pos) const
1486 {
1487     QStringRef text(&t);
1488     if (specialValueText.size() == 0 || text != specialValueText) {
1489         int from = 0;
1490         int size = text.size();
1491         bool changed = false;
1492         if (prefix.size() && text.startsWith(prefix)) {
1493             from += prefix.size();
1494             size -= from;
1495             changed = true;
1496         }
1497         if (suffix.size() && text.endsWith(suffix)) {
1498             size -= suffix.size();
1499             changed = true;
1500         }
1501         if (changed)
1502             text = text.mid(from, size);
1503     }
1504 
1505     const int s = text.size();
1506     text = text.trimmed();
1507     if (pos)
1508         (*pos) -= (s - text.size());
1509     return text.toString();
1510 
1511 }
1512 
updateEditFieldGeometry()1513 void QAbstractSpinBoxPrivate::updateEditFieldGeometry()
1514 {
1515     Q_Q(QAbstractSpinBox);
1516     QStyleOptionSpinBox opt;
1517     q->initStyleOption(&opt);
1518     opt.subControls = QStyle::SC_SpinBoxEditField;
1519     edit->setGeometry(q->style()->subControlRect(QStyle::CC_SpinBox, &opt,
1520                                                  QStyle::SC_SpinBoxEditField, q));
1521 }
1522 /*!
1523     \internal
1524     Returns \c true if a specialValueText has been set and the current value is minimum.
1525 */
1526 
specialValue() const1527 bool QAbstractSpinBoxPrivate::specialValue() const
1528 {
1529     return (value == minimum && !specialValueText.isEmpty());
1530 }
1531 
1532 /*!
1533     \internal Virtual function that emits signals when the value
1534     changes. Reimplemented in the different subclasses.
1535 */
1536 
emitSignals(EmitPolicy,const QVariant &)1537 void QAbstractSpinBoxPrivate::emitSignals(EmitPolicy, const QVariant &)
1538 {
1539 }
1540 
1541 /*!
1542     \internal
1543 
1544     Slot connected to the line edit's textChanged(const QString &)
1545     signal.
1546 */
1547 
_q_editorTextChanged(const QString & t)1548 void QAbstractSpinBoxPrivate::_q_editorTextChanged(const QString &t)
1549 {
1550     Q_Q(QAbstractSpinBox);
1551 
1552     if (keyboardTracking) {
1553         QString tmp = t;
1554         int pos = edit->cursorPosition();
1555         QValidator::State state = q->validate(tmp, pos);
1556         if (state == QValidator::Acceptable) {
1557             const QVariant v = valueFromText(tmp);
1558             setValue(v, EmitIfChanged, tmp != t);
1559             pendingEmit = false;
1560         } else {
1561             pendingEmit = true;
1562         }
1563     } else {
1564         pendingEmit = true;
1565     }
1566 }
1567 
1568 /*!
1569     \internal
1570 
1571     Virtual slot connected to the line edit's
1572     cursorPositionChanged(int, int) signal. Will move the cursor to a
1573     valid position if the new one is invalid. E.g. inside the prefix.
1574     Reimplemented in Q[Date|Time|DateTime]EditPrivate to account for
1575     the different sections etc.
1576 */
1577 
_q_editorCursorPositionChanged(int oldpos,int newpos)1578 void QAbstractSpinBoxPrivate::_q_editorCursorPositionChanged(int oldpos, int newpos)
1579 {
1580     if (!edit->hasSelectedText() && !ignoreCursorPositionChanged && !specialValue()) {
1581         ignoreCursorPositionChanged = true;
1582 
1583         bool allowSelection = true;
1584         int pos = -1;
1585         if (newpos < prefix.size() && newpos != 0) {
1586             if (oldpos == 0) {
1587                 allowSelection = false;
1588                 pos = prefix.size();
1589             } else {
1590                 pos = oldpos;
1591             }
1592         } else if (newpos > edit->text().size() - suffix.size()
1593                    && newpos != edit->text().size()) {
1594             if (oldpos == edit->text().size()) {
1595                 pos = edit->text().size() - suffix.size();
1596                 allowSelection = false;
1597             } else {
1598                 pos = edit->text().size();
1599             }
1600         }
1601         if (pos != -1) {
1602             const int selSize = edit->selectionStart() >= 0 && allowSelection
1603                                   ? (edit->selectedText().size()
1604                                      * (newpos < pos ? -1 : 1)) - newpos + pos
1605                                   : 0;
1606 
1607             const QSignalBlocker blocker(edit);
1608             if (selSize != 0) {
1609                 edit->setSelection(pos - selSize, selSize);
1610             } else {
1611                 edit->setCursorPosition(pos);
1612             }
1613         }
1614         ignoreCursorPositionChanged = false;
1615     }
1616 }
1617 
1618 /*!
1619     \internal
1620 
1621     Initialises the QAbstractSpinBoxPrivate object.
1622 */
1623 
init()1624 void QAbstractSpinBoxPrivate::init()
1625 {
1626     Q_Q(QAbstractSpinBox);
1627 
1628     q->setLineEdit(new QLineEdit(q));
1629     edit->setObjectName(QLatin1String("qt_spinbox_lineedit"));
1630     validator = new QSpinBoxValidator(q, this);
1631     edit->setValidator(validator);
1632 
1633     QStyleOptionSpinBox opt;
1634     q->initStyleOption(&opt);
1635     spinClickTimerInterval = q->style()->styleHint(QStyle::SH_SpinBox_ClickAutoRepeatRate, &opt, q);
1636     spinClickThresholdTimerInterval = q->style()->styleHint(QStyle::SH_SpinBox_ClickAutoRepeatThreshold, &opt, q);
1637     q->setFocusPolicy(Qt::WheelFocus);
1638     q->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed, QSizePolicy::SpinBox));
1639     q->setAttribute(Qt::WA_InputMethodEnabled);
1640 
1641     q->setAttribute(Qt::WA_MacShowFocusRect);
1642 }
1643 
1644 /*!
1645     \internal
1646 
1647     Resets the state of the spinbox. E.g. the state is set to
1648     (Keyboard|Up) if Key up is currently pressed.
1649 */
1650 
reset()1651 void QAbstractSpinBoxPrivate::reset()
1652 {
1653     Q_Q(QAbstractSpinBox);
1654 
1655     buttonState = None;
1656     if (q) {
1657         if (spinClickTimerId != -1)
1658             q->killTimer(spinClickTimerId);
1659         if (spinClickThresholdTimerId != -1)
1660             q->killTimer(spinClickThresholdTimerId);
1661         spinClickTimerId = spinClickThresholdTimerId = -1;
1662         acceleration = 0;
1663         q->update();
1664     }
1665 }
1666 
1667 /*!
1668     \internal
1669 
1670     Updates the state of the spinbox.
1671 */
1672 
updateState(bool up,bool fromKeyboard)1673 void QAbstractSpinBoxPrivate::updateState(bool up, bool fromKeyboard /* = false */)
1674 {
1675     Q_Q(QAbstractSpinBox);
1676     if ((up && (buttonState & Up)) || (!up && (buttonState & Down)))
1677         return;
1678     reset();
1679     if (q && (q->stepEnabled() & (up ? QAbstractSpinBox::StepUpEnabled
1680                                   : QAbstractSpinBox::StepDownEnabled))) {
1681         buttonState = (up ? Up : Down) | (fromKeyboard ? Keyboard : Mouse);
1682         int steps = up ? 1 : -1;
1683         if (QGuiApplication::keyboardModifiers() & stepModifier)
1684             steps *= 10;
1685         q->stepBy(steps);
1686         spinClickThresholdTimerId = q->startTimer(spinClickThresholdTimerInterval);
1687 #ifndef QT_NO_ACCESSIBILITY
1688         QAccessibleValueChangeEvent event(q, value);
1689         QAccessible::updateAccessibility(&event);
1690 #endif
1691     }
1692 }
1693 
1694 
1695 /*!
1696     Initialize \a option with the values from this QSpinBox. This method
1697     is useful for subclasses when they need a QStyleOptionSpinBox, but don't want
1698     to fill in all the information themselves.
1699 
1700     \sa QStyleOption::initFrom()
1701 */
initStyleOption(QStyleOptionSpinBox * option) const1702 void QAbstractSpinBox::initStyleOption(QStyleOptionSpinBox *option) const
1703 {
1704     if (!option)
1705         return;
1706 
1707     Q_D(const QAbstractSpinBox);
1708     option->initFrom(this);
1709     option->activeSubControls = QStyle::SC_None;
1710     option->buttonSymbols = d->buttonSymbols;
1711     option->subControls = QStyle::SC_SpinBoxEditField;
1712     if (style()->styleHint(QStyle::SH_SpinBox_ButtonsInsideFrame, nullptr, this))
1713         option->subControls |= QStyle::SC_SpinBoxFrame;
1714     if (d->buttonSymbols != QAbstractSpinBox::NoButtons) {
1715         option->subControls |= QStyle::SC_SpinBoxUp | QStyle::SC_SpinBoxDown;
1716         if (d->buttonState & Up) {
1717             option->activeSubControls = QStyle::SC_SpinBoxUp;
1718         } else if (d->buttonState & Down) {
1719             option->activeSubControls = QStyle::SC_SpinBoxDown;
1720         }
1721     }
1722 
1723     if (d->buttonState) {
1724         option->state |= QStyle::State_Sunken;
1725     } else {
1726         option->activeSubControls = d->hoverControl;
1727     }
1728 
1729     option->stepEnabled = style()->styleHint(QStyle::SH_SpinControls_DisableOnBounds, nullptr, this)
1730                       ? stepEnabled()
1731                       : (QAbstractSpinBox::StepDownEnabled|QAbstractSpinBox::StepUpEnabled);
1732 
1733     option->frame = d->frame;
1734 }
1735 
1736 /*!
1737     \internal
1738 
1739     Bounds \a val to be within minimum and maximum. Also tries to be
1740     clever about setting it at min and max depending on what it was
1741     and what direction it was changed etc.
1742 */
1743 
bound(const QVariant & val,const QVariant & old,int steps) const1744 QVariant QAbstractSpinBoxPrivate::bound(const QVariant &val, const QVariant &old, int steps) const
1745 {
1746     QVariant v = val;
1747     if (!wrapping || steps == 0 || old.isNull()) {
1748         if (variantCompare(v, minimum) < 0) {
1749             v = wrapping ? maximum : minimum;
1750         }
1751         if (variantCompare(v, maximum) > 0) {
1752             v = wrapping ? minimum : maximum;
1753         }
1754     } else {
1755         const bool wasMin = old == minimum;
1756         const bool wasMax = old == maximum;
1757         const int oldcmp = variantCompare(v, old);
1758         const int maxcmp = variantCompare(v, maximum);
1759         const int mincmp = variantCompare(v, minimum);
1760         const bool wrapped = (oldcmp > 0 && steps < 0) || (oldcmp < 0 && steps > 0);
1761         if (maxcmp > 0) {
1762             v = ((wasMax && !wrapped && steps > 0) || (steps < 0 && !wasMin && wrapped))
1763                 ? minimum : maximum;
1764         } else if (wrapped && (maxcmp > 0 || mincmp < 0)) {
1765             v = ((wasMax && steps > 0) || (!wasMin && steps < 0)) ? minimum : maximum;
1766         } else if (mincmp < 0) {
1767             v = (!wasMax && !wasMin ? minimum : maximum);
1768         }
1769     }
1770 
1771     return v;
1772 }
1773 
1774 /*!
1775     \internal
1776 
1777     Sets the value of the spin box to \a val. Depending on the value
1778     of \a ep it will also emit signals.
1779 */
1780 
setValue(const QVariant & val,EmitPolicy ep,bool doUpdate)1781 void QAbstractSpinBoxPrivate::setValue(const QVariant &val, EmitPolicy ep,
1782                                        bool doUpdate)
1783 {
1784     Q_Q(QAbstractSpinBox);
1785     const QVariant old = value;
1786     value = bound(val);
1787     pendingEmit = false;
1788     cleared = false;
1789     if (doUpdate) {
1790         updateEdit();
1791     }
1792     q->update();
1793 
1794     if (ep == AlwaysEmit || (ep == EmitIfChanged && old != value)) {
1795         emitSignals(ep, old);
1796     }
1797 }
1798 
1799 /*!
1800     \internal
1801 
1802     Updates the line edit to reflect the current value of the spin box.
1803 */
1804 
updateEdit()1805 void QAbstractSpinBoxPrivate::updateEdit()
1806 {
1807     Q_Q(QAbstractSpinBox);
1808     if (type == QMetaType::UnknownType)
1809         return;
1810     const QString newText = specialValue() ? specialValueText : prefix + textFromValue(value) + suffix;
1811     if (newText == edit->displayText() || cleared)
1812         return;
1813 
1814     const bool empty = edit->text().isEmpty();
1815     int cursor = edit->cursorPosition();
1816     int selsize = edit->selectedText().size();
1817     const QSignalBlocker blocker(edit);
1818     edit->setText(newText);
1819 
1820     if (!specialValue()) {
1821         cursor = qBound(prefix.size(), cursor, edit->displayText().size() - suffix.size());
1822 
1823         if (selsize > 0) {
1824             edit->setSelection(cursor, selsize);
1825         } else {
1826             edit->setCursorPosition(empty ? prefix.size() : cursor);
1827         }
1828     }
1829     q->update();
1830 }
1831 
1832 /*!
1833     \internal
1834 
1835     Convenience function to set min/max values.
1836 */
1837 
setRange(const QVariant & min,const QVariant & max)1838 void QAbstractSpinBoxPrivate::setRange(const QVariant &min, const QVariant &max)
1839 {
1840     Q_Q(QAbstractSpinBox);
1841 
1842     clearCache();
1843     minimum = min;
1844     maximum = (variantCompare(min, max) < 0 ? max : min);
1845     cachedSizeHint = QSize();
1846     cachedMinimumSizeHint = QSize(); // minimumSizeHint cares about min/max
1847 
1848     reset();
1849     if (!(bound(value) == value)) {
1850         setValue(bound(value), EmitIfChanged);
1851     } else if (value == minimum && !specialValueText.isEmpty()) {
1852         updateEdit();
1853     }
1854 
1855     q->updateGeometry();
1856 }
1857 
1858 /*!
1859     \internal
1860 
1861     Convenience function to get a variant of the right type.
1862 */
1863 
getZeroVariant() const1864 QVariant QAbstractSpinBoxPrivate::getZeroVariant() const
1865 {
1866     QVariant ret;
1867     switch (type) {
1868     case QMetaType::Int: ret = QVariant(0); break;
1869     case QMetaType::Double: ret = QVariant(0.0); break;
1870     default: break;
1871     }
1872     return ret;
1873 }
1874 
1875 /*!
1876     \internal
1877 
1878     Virtual method called that calls the public textFromValue()
1879     functions in the subclasses. Needed to change signature from
1880     QVariant to int/double/QDateTime etc. Used when needing to display
1881     a value textually.
1882 
1883     This method is reimeplemented in the various subclasses.
1884 */
1885 
textFromValue(const QVariant &) const1886 QString QAbstractSpinBoxPrivate::textFromValue(const QVariant &) const
1887 {
1888     return QString();
1889 }
1890 
1891 /*!
1892     \internal
1893 
1894     Virtual method called that calls the public valueFromText()
1895     functions in the subclasses. Needed to change signature from
1896     QVariant to int/double/QDateTime etc. Used when needing to
1897     interpret a string as another type.
1898 
1899     This method is reimeplemented in the various subclasses.
1900 */
1901 
valueFromText(const QString &) const1902 QVariant QAbstractSpinBoxPrivate::valueFromText(const QString &) const
1903 {
1904     return QVariant();
1905 }
1906 /*!
1907     \internal
1908 
1909     Interprets text and emits signals. Called when the spinbox needs
1910     to interpret the text on the lineedit.
1911 */
1912 
interpret(EmitPolicy ep)1913 void QAbstractSpinBoxPrivate::interpret(EmitPolicy ep)
1914 {
1915     Q_Q(QAbstractSpinBox);
1916     if (type == QMetaType::UnknownType || cleared)
1917         return;
1918 
1919     QVariant v = getZeroVariant();
1920     bool doInterpret = true;
1921     QString tmp = edit->displayText();
1922     int pos = edit->cursorPosition();
1923     const int oldpos = pos;
1924 
1925     if (q->validate(tmp, pos) != QValidator::Acceptable) {
1926         const QString copy = tmp;
1927         q->fixup(tmp);
1928         QASBDEBUG() << "QAbstractSpinBoxPrivate::interpret() text '"
1929                     << edit->displayText()
1930                     << "' >> '" << copy << '\''
1931                     << "' >> '" << tmp << '\'';
1932 
1933         doInterpret = tmp != copy && (q->validate(tmp, pos) == QValidator::Acceptable);
1934         if (!doInterpret) {
1935             v = (correctionMode == QAbstractSpinBox::CorrectToNearestValue
1936                  ? variantBound(minimum, v, maximum) : value);
1937         }
1938     }
1939     if (doInterpret) {
1940         v = valueFromText(tmp);
1941     }
1942     clearCache();
1943     setValue(v, ep, true);
1944     if (oldpos != pos)
1945         edit->setCursorPosition(pos);
1946 }
1947 
clearCache() const1948 void QAbstractSpinBoxPrivate::clearCache() const
1949 {
1950     cachedText.clear();
1951     cachedValue.clear();
1952     cachedState = QValidator::Acceptable;
1953 }
1954 
calculateAdaptiveDecimalStep(int steps) const1955 QVariant QAbstractSpinBoxPrivate::calculateAdaptiveDecimalStep(int steps) const
1956 {
1957     Q_UNUSED(steps)
1958     return singleStep;
1959 }
1960 
1961 // --- QSpinBoxValidator ---
1962 
1963 /*!
1964     \internal
1965     Constructs a QSpinBoxValidator object
1966 */
1967 
QSpinBoxValidator(QAbstractSpinBox * qp,QAbstractSpinBoxPrivate * dp)1968 QSpinBoxValidator::QSpinBoxValidator(QAbstractSpinBox *qp, QAbstractSpinBoxPrivate *dp)
1969     : QValidator(qp), qptr(qp), dptr(dp)
1970 {
1971     setObjectName(QLatin1String("qt_spinboxvalidator"));
1972 }
1973 
1974 /*!
1975     \internal
1976 
1977     Checks for specialValueText, prefix, suffix and calls
1978     the virtual QAbstractSpinBox::validate function.
1979 */
1980 
validate(QString & input,int & pos) const1981 QValidator::State QSpinBoxValidator::validate(QString &input, int &pos) const
1982 {
1983     if (dptr->specialValueText.size() > 0 && input == dptr->specialValueText)
1984         return QValidator::Acceptable;
1985 
1986     if (!dptr->prefix.isEmpty() && !input.startsWith(dptr->prefix)) {
1987         input.prepend(dptr->prefix);
1988         pos += dptr->prefix.length();
1989     }
1990 
1991     if (!dptr->suffix.isEmpty() && !input.endsWith(dptr->suffix))
1992         input.append(dptr->suffix);
1993 
1994     return qptr->validate(input, pos);
1995 }
1996 /*!
1997     \internal
1998     Calls the virtual QAbstractSpinBox::fixup function.
1999 */
2000 
fixup(QString & input) const2001 void QSpinBoxValidator::fixup(QString &input) const
2002 {
2003     qptr->fixup(input);
2004 }
2005 
2006 // --- global ---
2007 
2008 /*!
2009     \internal
2010     Adds two variants together and returns the result.
2011 */
2012 
operator +(const QVariant & arg1,const QVariant & arg2)2013 QVariant operator+(const QVariant &arg1, const QVariant &arg2)
2014 {
2015     QVariant ret;
2016     if (Q_UNLIKELY(arg1.userType() != arg2.userType()))
2017         qWarning("QAbstractSpinBox: Internal error: Different types (%s vs %s) (%s:%d)",
2018                  arg1.typeName(), arg2.typeName(), __FILE__, __LINE__);
2019     switch (arg1.userType()) {
2020     case QMetaType::Int: {
2021         const int int1 = arg1.toInt();
2022         const int int2 = arg2.toInt();
2023         if (int1 > 0 && (int2 >= INT_MAX - int1)) {
2024             // The increment overflows
2025             ret = QVariant(INT_MAX);
2026         } else if (int1 < 0 && (int2 <= INT_MIN - int1)) {
2027             // The increment underflows
2028             ret = QVariant(INT_MIN);
2029         } else {
2030             ret = QVariant(int1 + int2);
2031         }
2032         break;
2033     }
2034     case QMetaType::Double: ret = QVariant(arg1.toDouble() + arg2.toDouble()); break;
2035 #if QT_CONFIG(datetimeparser)
2036     case QMetaType::QDateTime: {
2037         QDateTime a2 = arg2.toDateTime();
2038         QDateTime a1 = arg1.toDateTime().addDays(QDATETIMEEDIT_DATE_MIN.daysTo(a2.date()));
2039         a1.setTime(a1.time().addMSecs(a2.time().msecsSinceStartOfDay()));
2040         ret = QVariant(a1);
2041         break;
2042     }
2043 #endif // datetimeparser
2044     default: break;
2045     }
2046     return ret;
2047 }
2048 
2049 
2050 /*!
2051     \internal
2052     Subtracts two variants and returns the result.
2053 */
2054 
operator -(const QVariant & arg1,const QVariant & arg2)2055 QVariant operator-(const QVariant &arg1, const QVariant &arg2)
2056 {
2057     QVariant ret;
2058     if (Q_UNLIKELY(arg1.userType() != arg2.userType()))
2059         qWarning("QAbstractSpinBox: Internal error: Different types (%s vs %s) (%s:%d)",
2060                  arg1.typeName(), arg2.typeName(), __FILE__, __LINE__);
2061     switch (arg1.userType()) {
2062     case QMetaType::Int: ret = QVariant(arg1.toInt() - arg2.toInt()); break;
2063     case QMetaType::Double: ret = QVariant(arg1.toDouble() - arg2.toDouble()); break;
2064     case QMetaType::QDateTime: {
2065         QDateTime a1 = arg1.toDateTime();
2066         QDateTime a2 = arg2.toDateTime();
2067         int days = a2.daysTo(a1);
2068         int secs = a2.secsTo(a1);
2069         int msecs = qMax(0, a1.time().msec() - a2.time().msec());
2070         if (days < 0 || secs < 0 || msecs < 0) {
2071             ret = arg1;
2072         } else {
2073             QDateTime dt = a2.addDays(days).addSecs(secs);
2074             if (msecs > 0)
2075                 dt.setTime(dt.time().addMSecs(msecs));
2076             ret = QVariant(dt);
2077         }
2078     }
2079     default: break;
2080     }
2081     return ret;
2082 }
2083 
2084 /*!
2085     \internal
2086     Multiplies \a arg1 by \a multiplier and returns the result.
2087 */
2088 
operator *(const QVariant & arg1,double multiplier)2089 QVariant operator*(const QVariant &arg1, double multiplier)
2090 {
2091     QVariant ret;
2092 
2093     switch (arg1.userType()) {
2094     case QMetaType::Int:
2095         ret = static_cast<int>(qBound<double>(INT_MIN, arg1.toInt() * multiplier, INT_MAX));
2096         break;
2097     case QMetaType::Double: ret = QVariant(arg1.toDouble() * multiplier); break;
2098 #if QT_CONFIG(datetimeparser)
2099     case QMetaType::QDateTime: {
2100         double days = QDATETIMEEDIT_DATE_MIN.daysTo(arg1.toDateTime().date()) * multiplier;
2101         const qint64 daysInt = qint64(days);
2102         days -= daysInt;
2103         qint64 msecs = qint64(arg1.toDateTime().time().msecsSinceStartOfDay() * multiplier
2104                               + days * (24 * 3600 * 1000));
2105         ret = QDATETIMEEDIT_DATE_MIN.addDays(daysInt).startOfDay().addMSecs(msecs);
2106         break;
2107     }
2108 #endif // datetimeparser
2109     default: ret = arg1; break;
2110     }
2111 
2112     return ret;
2113 }
2114 
2115 
2116 
operator /(const QVariant & arg1,const QVariant & arg2)2117 double operator/(const QVariant &arg1, const QVariant &arg2)
2118 {
2119     double a1 = 0;
2120     double a2 = 0;
2121 
2122     switch (arg1.userType()) {
2123     case QMetaType::Int:
2124         a1 = (double)arg1.toInt();
2125         a2 = (double)arg2.toInt();
2126         break;
2127     case QMetaType::Double:
2128         a1 = arg1.toDouble();
2129         a2 = arg2.toDouble();
2130         break;
2131 #if QT_CONFIG(datetimeparser)
2132     case QMetaType::QDateTime:
2133         a1 = QDATETIMEEDIT_DATE_MIN.daysTo(arg1.toDate());
2134         a2 = QDATETIMEEDIT_DATE_MIN.daysTo(arg2.toDate());
2135         a1 += arg1.toDateTime().time().msecsSinceStartOfDay() / (36e5 * 24);
2136         a2 += arg2.toDateTime().time().msecsSinceStartOfDay() / (36e5 * 24);
2137         break;
2138 #endif // datetimeparser
2139     default: break;
2140     }
2141 
2142     return (a1 != 0 && a2 != 0) ? (a1 / a2) : 0.0;
2143 }
2144 
variantCompare(const QVariant & arg1,const QVariant & arg2)2145 int QAbstractSpinBoxPrivate::variantCompare(const QVariant &arg1, const QVariant &arg2)
2146 {
2147     switch (arg2.userType()) {
2148     case QMetaType::QDate:
2149         Q_ASSERT_X(arg1.userType() == QMetaType::QDate, "QAbstractSpinBoxPrivate::variantCompare",
2150                    qPrintable(QString::fromLatin1("Internal error 1 (%1)").
2151                               arg(QString::fromLatin1(arg1.typeName()))));
2152         if (arg1.toDate() == arg2.toDate()) {
2153             return 0;
2154         } else if (arg1.toDate() < arg2.toDate()) {
2155             return -1;
2156         } else {
2157             return 1;
2158         }
2159     case QMetaType::QTime:
2160         Q_ASSERT_X(arg1.userType() == QMetaType::QTime, "QAbstractSpinBoxPrivate::variantCompare",
2161                    qPrintable(QString::fromLatin1("Internal error 2 (%1)").
2162                               arg(QString::fromLatin1(arg1.typeName()))));
2163         if (arg1.toTime() == arg2.toTime()) {
2164             return 0;
2165         } else if (arg1.toTime() < arg2.toTime()) {
2166             return -1;
2167         } else {
2168             return 1;
2169         }
2170 
2171 
2172     case QMetaType::QDateTime:
2173         if (arg1.toDateTime() == arg2.toDateTime()) {
2174             return 0;
2175         } else if (arg1.toDateTime() < arg2.toDateTime()) {
2176             return -1;
2177         } else {
2178             return 1;
2179         }
2180     case QMetaType::Int:
2181         if (arg1.toInt() == arg2.toInt()) {
2182             return 0;
2183         } else if (arg1.toInt() < arg2.toInt()) {
2184             return -1;
2185         } else {
2186             return 1;
2187         }
2188     case QMetaType::Double:
2189         if (arg1.toDouble() == arg2.toDouble()) {
2190             return 0;
2191         } else if (arg1.toDouble() < arg2.toDouble()) {
2192             return -1;
2193         } else {
2194             return 1;
2195         }
2196     case QMetaType::UnknownType:
2197         if (arg2.userType() == QMetaType::UnknownType)
2198             return 0;
2199         Q_FALLTHROUGH();
2200     default:
2201         Q_ASSERT_X(0, "QAbstractSpinBoxPrivate::variantCompare",
2202                    qPrintable(QString::fromLatin1("Internal error 3 (%1 %2)").
2203                               arg(QString::fromLatin1(arg1.typeName()),
2204                                   QString::fromLatin1(arg2.typeName()))));
2205     }
2206     return -2;
2207 }
2208 
variantBound(const QVariant & min,const QVariant & value,const QVariant & max)2209 QVariant QAbstractSpinBoxPrivate::variantBound(const QVariant &min,
2210                                                const QVariant &value,
2211                                                const QVariant &max)
2212 {
2213     Q_ASSERT(variantCompare(min, max) <= 0);
2214     if (variantCompare(min, value) < 0) {
2215         const int compMax = variantCompare(value, max);
2216         return (compMax < 0 ? value : max);
2217     } else {
2218         return min;
2219     }
2220 }
2221 
2222 
2223 QT_END_NAMESPACE
2224 
2225 #include "moc_qabstractspinbox.cpp"
2226