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
73QT_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
149QAbstractSpinBox::QAbstractSpinBox(QWidget *parent)
150 : QWidget(*new QAbstractSpinBoxPrivate, parent, { })
151{
152 Q_D(QAbstractSpinBox);
153 d->init();
154}
155
156/*!
157 \internal
158*/
159QAbstractSpinBox::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
170QAbstractSpinBox::~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
204QAbstractSpinBox::ButtonSymbols QAbstractSpinBox::buttonSymbols() const
205{
206 Q_D(const QAbstractSpinBox);
207 return d->buttonSymbols;
208}
209
210void 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
229QString 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
268QString QAbstractSpinBox::specialValueText() const
269{
270 Q_D(const QAbstractSpinBox);
271 return d->specialValueText;
272}
273
274void 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
298bool QAbstractSpinBox::wrapping() const
299{
300 Q_D(const QAbstractSpinBox);
301 return d->wrapping;
302}
303
304void 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
325bool QAbstractSpinBox::isReadOnly() const
326{
327 Q_D(const QAbstractSpinBox);
328 return d->readOnly;
329}
330
331void 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
361bool QAbstractSpinBox::keyboardTracking() const
362{
363 Q_D(const QAbstractSpinBox);
364 return d->keyboardTracking;
365}
366
367void 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
381bool QAbstractSpinBox::hasFrame() const
382{
383 Q_D(const QAbstractSpinBox);
384 return d->frame;
385}
386
387
388void 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
406void QAbstractSpinBox::setAccelerated(bool accelerate)
407{
408 Q_D(QAbstractSpinBox);
409 d->accelerate = accelerate;
410
411}
412bool 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*/
426bool QAbstractSpinBox::isGroupSeparatorShown() const
427{
428 Q_D(const QAbstractSpinBox);
429 return d->showGroupSeparator;
430}
431
432void 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*/
467void QAbstractSpinBox::setCorrectionMode(CorrectionMode correctionMode)
468{
469 Q_D(QAbstractSpinBox);
470 d->correctionMode = correctionMode;
471
472}
473QAbstractSpinBox::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
488bool 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
508Qt::Alignment QAbstractSpinBox::alignment() const
509{
510 Q_D(const QAbstractSpinBox);
511
512 return (Qt::Alignment)d->edit->alignment();
513}
514
515void 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
526void 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
543void 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
570QAbstractSpinBox::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
594QValidator::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
606void 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
616void 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
627void 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
645void 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
681QLineEdit *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
701void 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
752void 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*/
764QVariant 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
783bool 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)->position().toPoint());
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
829void 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
845void 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
885void 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
898QSize 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 }
929 return d->cachedSizeHint;
930}
931
932/*!
933 \reimp
934*/
935
936QSize QAbstractSpinBox::minimumSizeHint() const
937{
938 Q_D(const QAbstractSpinBox);
939 if (d->cachedMinimumSizeHint.isEmpty()) {
940 //Use the prefix and range to calculate the minimumSizeHint
941 ensurePolished();
942
943 const QFontMetrics fm(fontMetrics());
944 int h = d->edit->minimumSizeHint().height();
945 int w = 0;
946
947 QString s;
948 QString fixedContent = d->prefix + QLatin1Char(' ');
949 s = d->textFromValue(d->minimum);
950 s.truncate(18);
951 s += fixedContent;
952 w = qMax(w, fm.horizontalAdvance(s));
953 s = d->textFromValue(d->maximum);
954 s.truncate(18);
955 s += fixedContent;
956 w = qMax(w, fm.horizontalAdvance(s));
957
958 if (d->specialValueText.size()) {
959 s = d->specialValueText;
960 w = qMax(w, fm.horizontalAdvance(s));
961 }
962 w += 2; // cursor blinking space
963
964 QStyleOptionSpinBox opt;
965 initStyleOption(&opt);
966 QSize hint(w, h);
967
968 d->cachedMinimumSizeHint = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this);
969 }
970 return d->cachedMinimumSizeHint;
971}
972
973/*!
974 \reimp
975*/
976
977void QAbstractSpinBox::paintEvent(QPaintEvent *)
978{
979 QStyleOptionSpinBox opt;
980 initStyleOption(&opt);
981 QStylePainter p(this);
982 p.drawComplexControl(QStyle::CC_SpinBox, opt);
983}
984
985/*!
986 \reimp
987
988 This function handles keyboard input.
989
990 The following keys are handled specifically:
991 \table
992 \row \li Enter/Return
993 \li This will reinterpret the text and emit a signal even if the value has not changed
994 since last time a signal was emitted.
995 \row \li Up
996 \li This will invoke stepBy(1)
997 \row \li Down
998 \li This will invoke stepBy(-1)
999 \row \li Page up
1000 \li This will invoke stepBy(10)
1001 \row \li Page down
1002 \li This will invoke stepBy(-10)
1003 \endtable
1004
1005 \sa stepBy()
1006*/
1007
1008
1009void QAbstractSpinBox::keyPressEvent(QKeyEvent *event)
1010{
1011 Q_D(QAbstractSpinBox);
1012
1013 d->keyboardModifiers = event->modifiers();
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
1151void QAbstractSpinBox::keyReleaseEvent(QKeyEvent *event)
1152{
1153 Q_D(QAbstractSpinBox);
1154
1155 d->keyboardModifiers = event->modifiers();
1156 if (d->buttonState & Keyboard && !event->isAutoRepeat()) {
1157 d->reset();
1158 } else {
1159 d->edit->event(event);
1160 }
1161}
1162
1163/*!
1164 \reimp
1165*/
1166
1167#if QT_CONFIG(wheelevent)
1168void QAbstractSpinBox::wheelEvent(QWheelEvent *event)
1169{
1170 Q_D(QAbstractSpinBox);
1171#ifdef Q_OS_MACOS
1172 // If the event comes from a real mouse wheel, rather than a track pad
1173 // (Qt::MouseEventSynthesizedBySystem), the shift modifier changes the
1174 // scroll orientation to horizontal.
1175 // Convert horizontal events back to vertical whilst shift is held.
1176 if ((event->modifiers() & Qt::ShiftModifier)
1177 && event->source() == Qt::MouseEventNotSynthesized) {
1178 d->wheelDeltaRemainder += event->angleDelta().x();
1179 } else {
1180 d->wheelDeltaRemainder += event->angleDelta().y();
1181 }
1182#else
1183 d->wheelDeltaRemainder += event->angleDelta().y();
1184#endif
1185 const int steps = d->wheelDeltaRemainder / 120;
1186 d->wheelDeltaRemainder -= steps * 120;
1187 if (stepEnabled() & (steps > 0 ? StepUpEnabled : StepDownEnabled))
1188 stepBy(event->modifiers() & d->stepModifier ? steps * 10 : steps);
1189 event->accept();
1190}
1191#endif
1192
1193
1194/*!
1195 \reimp
1196*/
1197void QAbstractSpinBox::focusInEvent(QFocusEvent *event)
1198{
1199 Q_D(QAbstractSpinBox);
1200
1201 d->edit->event(event);
1202 if (event->reason() == Qt::TabFocusReason || event->reason() == Qt::BacktabFocusReason) {
1203 selectAll();
1204 }
1205 QWidget::focusInEvent(event);
1206}
1207
1208/*!
1209 \reimp
1210*/
1211
1212void QAbstractSpinBox::focusOutEvent(QFocusEvent *event)
1213{
1214 Q_D(QAbstractSpinBox);
1215
1216 if (d->pendingEmit)
1217 d->interpret(EmitIfChanged);
1218
1219 d->reset();
1220 d->edit->event(event);
1221 d->updateEdit();
1222 QWidget::focusOutEvent(event);
1223
1224#ifdef QT_KEYPAD_NAVIGATION
1225 // editingFinished() is already emitted on LeaveEditFocus
1226 if (!QApplicationPrivate::keypadNavigationEnabled())
1227#endif
1228 emit editingFinished();
1229}
1230
1231/*!
1232 \reimp
1233*/
1234
1235void QAbstractSpinBox::closeEvent(QCloseEvent *event)
1236{
1237 Q_D(QAbstractSpinBox);
1238
1239 d->reset();
1240 if (d->pendingEmit)
1241 d->interpret(EmitIfChanged);
1242 QWidget::closeEvent(event);
1243}
1244
1245/*!
1246 \reimp
1247*/
1248
1249void QAbstractSpinBox::hideEvent(QHideEvent *event)
1250{
1251 Q_D(QAbstractSpinBox);
1252 d->reset();
1253 if (d->pendingEmit)
1254 d->interpret(EmitIfChanged);
1255 QWidget::hideEvent(event);
1256}
1257
1258
1259/*!
1260 \reimp
1261*/
1262
1263void QAbstractSpinBox::timerEvent(QTimerEvent *event)
1264{
1265 Q_D(QAbstractSpinBox);
1266
1267 bool doStep = false;
1268 if (event->timerId() == d->spinClickThresholdTimerId) {
1269 killTimer(d->spinClickThresholdTimerId);
1270 d->spinClickThresholdTimerId = -1;
1271 d->effectiveSpinRepeatRate = d->buttonState & Keyboard
1272 ? QGuiApplication::styleHints()->keyboardAutoRepeatRate()
1273 : d->spinClickTimerInterval;
1274 d->spinClickTimerId = startTimer(d->effectiveSpinRepeatRate);
1275 doStep = true;
1276 } else if (event->timerId() == d->spinClickTimerId) {
1277 if (d->accelerate) {
1278 d->acceleration = d->acceleration + (int)(d->effectiveSpinRepeatRate * 0.05);
1279 if (d->effectiveSpinRepeatRate - d->acceleration >= 10) {
1280 killTimer(d->spinClickTimerId);
1281 d->spinClickTimerId = startTimer(d->effectiveSpinRepeatRate - d->acceleration);
1282 }
1283 }
1284 doStep = true;
1285 }
1286
1287 if (doStep) {
1288 const bool increaseStepRate = d->keyboardModifiers & d->stepModifier;
1289 const StepEnabled st = stepEnabled();
1290 if (d->buttonState & Up) {
1291 if (!(st & StepUpEnabled)) {
1292 d->reset();
1293 } else {
1294 stepBy(increaseStepRate ? 10 : 1);
1295 }
1296 } else if (d->buttonState & Down) {
1297 if (!(st & StepDownEnabled)) {
1298 d->reset();
1299 } else {
1300 stepBy(increaseStepRate ? -10 : -1);
1301 }
1302 }
1303 return;
1304 }
1305 QWidget::timerEvent(event);
1306 return;
1307}
1308
1309/*!
1310 \reimp
1311*/
1312
1313#if QT_CONFIG(contextmenu)
1314void QAbstractSpinBox::contextMenuEvent(QContextMenuEvent *event)
1315{
1316 Q_D(QAbstractSpinBox);
1317
1318 QPointer<QMenu> menu = d->edit->createStandardContextMenu();
1319 if (!menu)
1320 return;
1321
1322 d->reset();
1323
1324 QAction *selAll = new QAction(tr("&Select All"), menu);
1325#if QT_CONFIG(shortcut)
1326 selAll->setShortcut(QKeySequence::SelectAll);
1327#endif
1328 menu->insertAction(d->edit->d_func()->selectAllAction,
1329 selAll);
1330 menu->removeAction(d->edit->d_func()->selectAllAction);
1331 menu->addSeparator();
1332 const uint se = stepEnabled();
1333 QAction *up = menu->addAction(tr("&Step up"));
1334 up->setEnabled(se & StepUpEnabled);
1335 QAction *down = menu->addAction(tr("Step &down"));
1336 down->setEnabled(se & StepDownEnabled);
1337 menu->addSeparator();
1338
1339 const QPointer<QAbstractSpinBox> that = this;
1340 const QPoint pos = (event->reason() == QContextMenuEvent::Mouse)
1341 ? event->globalPos() : mapToGlobal(QPoint(event->pos().x(), 0)) + QPoint(width() / 2, height() / 2);
1342 const QAction *action = menu->exec(pos);
1343 delete static_cast<QMenu *>(menu);
1344 if (that && action) {
1345 if (action == up) {
1346 stepBy(1);
1347 } else if (action == down) {
1348 stepBy(-1);
1349 } else if (action == selAll) {
1350 selectAll();
1351 }
1352 }
1353 event->accept();
1354}
1355#endif // QT_CONFIG(contextmenu)
1356
1357/*!
1358 \reimp
1359*/
1360
1361void QAbstractSpinBox::mouseMoveEvent(QMouseEvent *event)
1362{
1363 Q_D(QAbstractSpinBox);
1364
1365 d->keyboardModifiers = event->modifiers();
1366 d->updateHoverControl(event->position().toPoint());
1367
1368 // If we have a timer ID, update the state
1369 if (d->spinClickTimerId != -1 && d->buttonSymbols != NoButtons) {
1370 const StepEnabled se = stepEnabled();
1371 if ((se & StepUpEnabled) && d->hoverControl == QStyle::SC_SpinBoxUp)
1372 d->updateState(true);
1373 else if ((se & StepDownEnabled) && d->hoverControl == QStyle::SC_SpinBoxDown)
1374 d->updateState(false);
1375 else
1376 d->reset();
1377 event->accept();
1378 }
1379}
1380
1381/*!
1382 \reimp
1383*/
1384
1385void QAbstractSpinBox::mousePressEvent(QMouseEvent *event)
1386{
1387 Q_D(QAbstractSpinBox);
1388
1389 d->keyboardModifiers = event->modifiers();
1390 if (event->button() != Qt::LeftButton || d->buttonState != None) {
1391 return;
1392 }
1393
1394 d->updateHoverControl(event->position().toPoint());
1395 event->accept();
1396
1397 const StepEnabled se = (d->buttonSymbols == NoButtons) ? StepEnabled(StepNone) : stepEnabled();
1398 if ((se & StepUpEnabled) && d->hoverControl == QStyle::SC_SpinBoxUp) {
1399 d->updateState(true);
1400 } else if ((se & StepDownEnabled) && d->hoverControl == QStyle::SC_SpinBoxDown) {
1401 d->updateState(false);
1402 } else {
1403 event->ignore();
1404 }
1405}
1406
1407/*!
1408 \reimp
1409*/
1410void QAbstractSpinBox::mouseReleaseEvent(QMouseEvent *event)
1411{
1412 Q_D(QAbstractSpinBox);
1413
1414 d->keyboardModifiers = event->modifiers();
1415 if ((d->buttonState & Mouse) != 0)
1416 d->reset();
1417 event->accept();
1418}
1419
1420// --- QAbstractSpinBoxPrivate ---
1421
1422/*!
1423 \internal
1424 Constructs a QAbstractSpinBoxPrivate object
1425*/
1426
1427QAbstractSpinBoxPrivate::QAbstractSpinBoxPrivate()
1428 : pendingEmit(false), readOnly(false), wrapping(false),
1429 ignoreCursorPositionChanged(false), frame(true), accelerate(false), keyboardTracking(true),
1430 cleared(false), ignoreUpdateEdit(false), showGroupSeparator(false)
1431{
1432}
1433
1434/*
1435 \internal
1436 Called when the QAbstractSpinBoxPrivate is destroyed
1437*/
1438QAbstractSpinBoxPrivate::~QAbstractSpinBoxPrivate()
1439{
1440}
1441
1442/*!
1443 \internal
1444 Updates the old and new hover control. Does nothing if the hover
1445 control has not changed.
1446*/
1447bool QAbstractSpinBoxPrivate::updateHoverControl(const QPoint &pos)
1448{
1449 Q_Q(QAbstractSpinBox);
1450 QRect lastHoverRect = hoverRect;
1451 QStyle::SubControl lastHoverControl = hoverControl;
1452 bool doesHover = q->testAttribute(Qt::WA_Hover);
1453 if (lastHoverControl != newHoverControl(pos) && doesHover) {
1454 q->update(lastHoverRect);
1455 q->update(hoverRect);
1456 return true;
1457 }
1458 return !doesHover;
1459}
1460
1461/*!
1462 \internal
1463 Returns the hover control at \a pos.
1464 This will update the hoverRect and hoverControl.
1465*/
1466QStyle::SubControl QAbstractSpinBoxPrivate::newHoverControl(const QPoint &pos)
1467{
1468 Q_Q(QAbstractSpinBox);
1469
1470 QStyleOptionSpinBox opt;
1471 q->initStyleOption(&opt);
1472 opt.subControls = QStyle::SC_All;
1473 hoverControl = q->style()->hitTestComplexControl(QStyle::CC_SpinBox, &opt, pos, q);
1474 hoverRect = q->style()->subControlRect(QStyle::CC_SpinBox, &opt, hoverControl, q);
1475 return hoverControl;
1476}
1477
1478/*!
1479 \internal
1480 Strips any prefix/suffix from \a text.
1481*/
1482
1483QString QAbstractSpinBoxPrivate::stripped(const QString &t, int *pos) const
1484{
1485 QStringView text(t);
1486 if (specialValueText.size() == 0 || text != specialValueText) {
1487 int from = 0;
1488 int size = text.size();
1489 bool changed = false;
1490 if (prefix.size() && text.startsWith(prefix)) {
1491 from += prefix.size();
1492 size -= from;
1493 changed = true;
1494 }
1495 if (suffix.size() && text.endsWith(suffix)) {
1496 size -= suffix.size();
1497 changed = true;
1498 }
1499 if (changed)
1500 text = text.mid(from, size);
1501 }
1502
1503 const int s = text.size();
1504 text = text.trimmed();
1505 if (pos)
1506 (*pos) -= (s - text.size());
1507 return text.toString();
1508
1509}
1510
1511void QAbstractSpinBoxPrivate::updateEditFieldGeometry()
1512{
1513 Q_Q(QAbstractSpinBox);
1514 QStyleOptionSpinBox opt;
1515 q->initStyleOption(&opt);
1516 opt.subControls = QStyle::SC_SpinBoxEditField;
1517 edit->setGeometry(q->style()->subControlRect(QStyle::CC_SpinBox, &opt,
1518 QStyle::SC_SpinBoxEditField, q));
1519}
1520/*!
1521 \internal
1522 Returns \c true if a specialValueText has been set and the current value is minimum.
1523*/
1524
1525bool QAbstractSpinBoxPrivate::specialValue() const
1526{
1527 return (value == minimum && !specialValueText.isEmpty());
1528}
1529
1530/*!
1531 \internal Virtual function that emits signals when the value
1532 changes. Reimplemented in the different subclasses.
1533*/
1534
1535void QAbstractSpinBoxPrivate::emitSignals(EmitPolicy, const QVariant &)
1536{
1537}
1538
1539/*!
1540 \internal
1541
1542 Slot connected to the line edit's textChanged(const QString &)
1543 signal.
1544*/
1545
1546void QAbstractSpinBoxPrivate::_q_editorTextChanged(const QString &t)
1547{
1548 Q_Q(QAbstractSpinBox);
1549
1550 if (keyboardTracking) {
1551 QString tmp = t;
1552 int pos = edit->cursorPosition();
1553 QValidator::State state = q->validate(tmp, pos);
1554 if (state == QValidator::Acceptable) {
1555 const QVariant v = valueFromText(tmp);
1556 setValue(v, EmitIfChanged, tmp != t);
1557 pendingEmit = false;
1558 } else {
1559 pendingEmit = true;
1560 }
1561 } else {
1562 pendingEmit = true;
1563 }
1564}
1565
1566/*!
1567 \internal
1568
1569 Virtual slot connected to the line edit's
1570 cursorPositionChanged(int, int) signal. Will move the cursor to a
1571 valid position if the new one is invalid. E.g. inside the prefix.
1572 Reimplemented in Q[Date|Time|DateTime]EditPrivate to account for
1573 the different sections etc.
1574*/
1575
1576void QAbstractSpinBoxPrivate::_q_editorCursorPositionChanged(int oldpos, int newpos)
1577{
1578 if (!edit->hasSelectedText() && !ignoreCursorPositionChanged && !specialValue()) {
1579 ignoreCursorPositionChanged = true;
1580
1581 bool allowSelection = true;
1582 int pos = -1;
1583 if (newpos < prefix.size() && newpos != 0) {
1584 if (oldpos == 0) {
1585 allowSelection = false;
1586 pos = prefix.size();
1587 } else {
1588 pos = oldpos;
1589 }
1590 } else if (newpos > edit->text().size() - suffix.size()
1591 && newpos != edit->text().size()) {
1592 if (oldpos == edit->text().size()) {
1593 pos = edit->text().size() - suffix.size();
1594 allowSelection = false;
1595 } else {
1596 pos = edit->text().size();
1597 }
1598 }
1599 if (pos != -1) {
1600 const int selSize = edit->selectionStart() >= 0 && allowSelection
1601 ? (edit->selectedText().size()
1602 * (newpos < pos ? -1 : 1)) - newpos + pos
1603 : 0;
1604
1605 const QSignalBlocker blocker(edit);
1606 if (selSize != 0) {
1607 edit->setSelection(pos - selSize, selSize);
1608 } else {
1609 edit->setCursorPosition(pos);
1610 }
1611 }
1612 ignoreCursorPositionChanged = false;
1613 }
1614}
1615
1616/*!
1617 \internal
1618
1619 Initialises the QAbstractSpinBoxPrivate object.
1620*/
1621
1622void QAbstractSpinBoxPrivate::init()
1623{
1624 Q_Q(QAbstractSpinBox);
1625
1626 q->setLineEdit(new QLineEdit(q));
1627 edit->setObjectName(QLatin1String("qt_spinbox_lineedit"));
1628 validator = new QSpinBoxValidator(q, this);
1629 edit->setValidator(validator);
1630
1631 QStyleOptionSpinBox opt;
1632 // ### This is called from the ctor and thus we shouldn't call initStyleOption yet
1633 // ### as we only call the base class implementation of initStyleOption called.
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
1651void 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
1673void 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 (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*/
1702void 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
1744QVariant 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
1781void 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
1805void 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
1838void 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
1864QVariant 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
1886QString 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
1902QVariant 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
1913void 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
1948void QAbstractSpinBoxPrivate::clearCache() const
1949{
1950 cachedText.clear();
1951 cachedValue.clear();
1952 cachedState = QValidator::Acceptable;
1953}
1954
1955QVariant 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
1968QSpinBoxValidator::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
1981QValidator::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
2001void 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
2013QVariant 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
2055QVariant 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
2089QVariant 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
2117double 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
2145int 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
2209QVariant 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
2223QT_END_NAMESPACE
2224
2225#include "moc_qabstractspinbox.cpp"
2226