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, 0)
151{
152 Q_D(QAbstractSpinBox);
153 d->init();
154}
155
156/*!
157 \internal
158*/
159QAbstractSpinBox::QAbstractSpinBox(QAbstractSpinBoxPrivate &dd, QWidget *parent)
160 : QWidget(dd, parent, 0)
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 == QVariant::Invalid)
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 != QVariant::Invalid) {
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)->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
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, 0, this);
852 d->spinClickThresholdTimerInterval =
853 style()->styleHint(QStyle::SH_SpinBox_ClickAutoRepeatThreshold, 0, 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 .expandedTo(QApplication::globalStrut());
929 }
930 return d->cachedSizeHint;
931}
932
933/*!
934 \reimp
935*/
936
937QSize 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
979void 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
1011void 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, 0, 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 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)
1167void 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*/
1196void 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
1211void 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
1234void 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
1248void 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
1262void 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)
1313void 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 selAll->setShortcut(QKeySequence::SelectAll);
1325 menu->insertAction(d->edit->d_func()->selectAllAction,
1326 selAll);
1327 menu->removeAction(d->edit->d_func()->selectAllAction);
1328 menu->addSeparator();
1329 const uint se = stepEnabled();
1330 QAction *up = menu->addAction(tr("&Step up"));
1331 up->setEnabled(se & StepUpEnabled);
1332 QAction *down = menu->addAction(tr("Step &down"));
1333 down->setEnabled(se & StepDownEnabled);
1334 menu->addSeparator();
1335
1336 const QPointer<QAbstractSpinBox> that = this;
1337 const QPoint pos = (event->reason() == QContextMenuEvent::Mouse)
1338 ? event->globalPos() : mapToGlobal(QPoint(event->pos().x(), 0)) + QPoint(width() / 2, height() / 2);
1339 const QAction *action = menu->exec(pos);
1340 delete static_cast<QMenu *>(menu);
1341 if (that && action) {
1342 if (action == up) {
1343 stepBy(1);
1344 } else if (action == down) {
1345 stepBy(-1);
1346 } else if (action == selAll) {
1347 selectAll();
1348 }
1349 }
1350 event->accept();
1351}
1352#endif // QT_CONFIG(contextmenu)
1353
1354/*!
1355 \reimp
1356*/
1357
1358void QAbstractSpinBox::mouseMoveEvent(QMouseEvent *event)
1359{
1360 Q_D(QAbstractSpinBox);
1361
1362 d->updateHoverControl(event->pos());
1363
1364 // If we have a timer ID, update the state
1365 if (d->spinClickTimerId != -1 && d->buttonSymbols != NoButtons) {
1366 const StepEnabled se = stepEnabled();
1367 if ((se & StepUpEnabled) && d->hoverControl == QStyle::SC_SpinBoxUp)
1368 d->updateState(true);
1369 else if ((se & StepDownEnabled) && d->hoverControl == QStyle::SC_SpinBoxDown)
1370 d->updateState(false);
1371 else
1372 d->reset();
1373 event->accept();
1374 }
1375}
1376
1377/*!
1378 \reimp
1379*/
1380
1381void QAbstractSpinBox::mousePressEvent(QMouseEvent *event)
1382{
1383 Q_D(QAbstractSpinBox);
1384
1385 if (event->button() != Qt::LeftButton || d->buttonState != None) {
1386 return;
1387 }
1388
1389 d->updateHoverControl(event->pos());
1390 event->accept();
1391
1392 const StepEnabled se = (d->buttonSymbols == NoButtons) ? StepEnabled(StepNone) : stepEnabled();
1393 if ((se & StepUpEnabled) && d->hoverControl == QStyle::SC_SpinBoxUp) {
1394 d->updateState(true);
1395 } else if ((se & StepDownEnabled) && d->hoverControl == QStyle::SC_SpinBoxDown) {
1396 d->updateState(false);
1397 } else {
1398 event->ignore();
1399 }
1400}
1401
1402/*!
1403 \reimp
1404*/
1405void QAbstractSpinBox::mouseReleaseEvent(QMouseEvent *event)
1406{
1407 Q_D(QAbstractSpinBox);
1408
1409 if ((d->buttonState & Mouse) != 0)
1410 d->reset();
1411 event->accept();
1412}
1413
1414// --- QAbstractSpinBoxPrivate ---
1415
1416/*!
1417 \internal
1418 Constructs a QAbstractSpinBoxPrivate object
1419*/
1420
1421QAbstractSpinBoxPrivate::QAbstractSpinBoxPrivate()
1422 : edit(0), type(QVariant::Invalid), spinClickTimerId(-1),
1423 spinClickTimerInterval(100), spinClickThresholdTimerId(-1), spinClickThresholdTimerInterval(-1),
1424 effectiveSpinRepeatRate(1), buttonState(None), cachedText(QLatin1String("\x01")),
1425 cachedState(QValidator::Invalid), pendingEmit(false), readOnly(false), wrapping(false),
1426 ignoreCursorPositionChanged(false), frame(true), accelerate(false), keyboardTracking(true),
1427 cleared(false), ignoreUpdateEdit(false), correctionMode(QAbstractSpinBox::CorrectToPreviousValue),
1428 stepModifier(Qt::ControlModifier), acceleration(0), hoverControl(QStyle::SC_None),
1429 buttonSymbols(QAbstractSpinBox::UpDownArrows), validator(0), showGroupSeparator(0),
1430 wheelDeltaRemainder(0)
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 QStringRef 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 q->initStyleOption(&opt);
1633 spinClickTimerInterval = q->style()->styleHint(QStyle::SH_SpinBox_ClickAutoRepeatRate, &opt, q);
1634 spinClickThresholdTimerInterval = q->style()->styleHint(QStyle::SH_SpinBox_ClickAutoRepeatThreshold, &opt, q);
1635 q->setFocusPolicy(Qt::WheelFocus);
1636 q->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed, QSizePolicy::SpinBox));
1637 q->setAttribute(Qt::WA_InputMethodEnabled);
1638
1639 q->setAttribute(Qt::WA_MacShowFocusRect);
1640}
1641
1642/*!
1643 \internal
1644
1645 Resets the state of the spinbox. E.g. the state is set to
1646 (Keyboard|Up) if Key up is currently pressed.
1647*/
1648
1649void QAbstractSpinBoxPrivate::reset()
1650{
1651 Q_Q(QAbstractSpinBox);
1652
1653 buttonState = None;
1654 if (q) {
1655 if (spinClickTimerId != -1)
1656 q->killTimer(spinClickTimerId);
1657 if (spinClickThresholdTimerId != -1)
1658 q->killTimer(spinClickThresholdTimerId);
1659 spinClickTimerId = spinClickThresholdTimerId = -1;
1660 acceleration = 0;
1661 q->update();
1662 }
1663}
1664
1665/*!
1666 \internal
1667
1668 Updates the state of the spinbox.
1669*/
1670
1671void QAbstractSpinBoxPrivate::updateState(bool up, bool fromKeyboard /* = false */)
1672{
1673 Q_Q(QAbstractSpinBox);
1674 if ((up && (buttonState & Up)) || (!up && (buttonState & Down)))
1675 return;
1676 reset();
1677 if (q && (q->stepEnabled() & (up ? QAbstractSpinBox::StepUpEnabled
1678 : QAbstractSpinBox::StepDownEnabled))) {
1679 spinClickThresholdTimerId = q->startTimer(spinClickThresholdTimerInterval);
1680 buttonState = (up ? Up : Down) | (fromKeyboard ? Keyboard : Mouse);
1681 int steps = up ? 1 : -1;
1682 if (QGuiApplication::keyboardModifiers() & stepModifier)
1683 steps *= 10;
1684 q->stepBy(steps);
1685#ifndef QT_NO_ACCESSIBILITY
1686 QAccessibleValueChangeEvent event(q, value);
1687 QAccessible::updateAccessibility(&event);
1688#endif
1689 }
1690}
1691
1692
1693/*!
1694 Initialize \a option with the values from this QSpinBox. This method
1695 is useful for subclasses when they need a QStyleOptionSpinBox, but don't want
1696 to fill in all the information themselves.
1697
1698 \sa QStyleOption::initFrom()
1699*/
1700void QAbstractSpinBox::initStyleOption(QStyleOptionSpinBox *option) const
1701{
1702 if (!option)
1703 return;
1704
1705 Q_D(const QAbstractSpinBox);
1706 option->initFrom(this);
1707 option->activeSubControls = QStyle::SC_None;
1708 option->buttonSymbols = d->buttonSymbols;
1709 option->subControls = QStyle::SC_SpinBoxEditField;
1710 if (style()->styleHint(QStyle::SH_SpinBox_ButtonsInsideFrame, nullptr, this))
1711 option->subControls |= QStyle::SC_SpinBoxFrame;
1712 if (d->buttonSymbols != QAbstractSpinBox::NoButtons) {
1713 option->subControls |= QStyle::SC_SpinBoxUp | QStyle::SC_SpinBoxDown;
1714 if (d->buttonState & Up) {
1715 option->activeSubControls = QStyle::SC_SpinBoxUp;
1716 } else if (d->buttonState & Down) {
1717 option->activeSubControls = QStyle::SC_SpinBoxDown;
1718 }
1719 }
1720
1721 if (d->buttonState) {
1722 option->state |= QStyle::State_Sunken;
1723 } else {
1724 option->activeSubControls = d->hoverControl;
1725 }
1726
1727 option->stepEnabled = style()->styleHint(QStyle::SH_SpinControls_DisableOnBounds)
1728 ? stepEnabled()
1729 : (QAbstractSpinBox::StepDownEnabled|QAbstractSpinBox::StepUpEnabled);
1730
1731 option->frame = d->frame;
1732}
1733
1734/*!
1735 \internal
1736
1737 Bounds \a val to be within minimum and maximum. Also tries to be
1738 clever about setting it at min and max depending on what it was
1739 and what direction it was changed etc.
1740*/
1741
1742QVariant QAbstractSpinBoxPrivate::bound(const QVariant &val, const QVariant &old, int steps) const
1743{
1744 QVariant v = val;
1745 if (!wrapping || steps == 0 || old.isNull()) {
1746 if (variantCompare(v, minimum) < 0) {
1747 v = wrapping ? maximum : minimum;
1748 }
1749 if (variantCompare(v, maximum) > 0) {
1750 v = wrapping ? minimum : maximum;
1751 }
1752 } else {
1753 const bool wasMin = old == minimum;
1754 const bool wasMax = old == maximum;
1755 const int oldcmp = variantCompare(v, old);
1756 const int maxcmp = variantCompare(v, maximum);
1757 const int mincmp = variantCompare(v, minimum);
1758 const bool wrapped = (oldcmp > 0 && steps < 0) || (oldcmp < 0 && steps > 0);
1759 if (maxcmp > 0) {
1760 v = ((wasMax && !wrapped && steps > 0) || (steps < 0 && !wasMin && wrapped))
1761 ? minimum : maximum;
1762 } else if (wrapped && (maxcmp > 0 || mincmp < 0)) {
1763 v = ((wasMax && steps > 0) || (!wasMin && steps < 0)) ? minimum : maximum;
1764 } else if (mincmp < 0) {
1765 v = (!wasMax && !wasMin ? minimum : maximum);
1766 }
1767 }
1768
1769 return v;
1770}
1771
1772/*!
1773 \internal
1774
1775 Sets the value of the spin box to \a val. Depending on the value
1776 of \a ep it will also emit signals.
1777*/
1778
1779void QAbstractSpinBoxPrivate::setValue(const QVariant &val, EmitPolicy ep,
1780 bool doUpdate)
1781{
1782 Q_Q(QAbstractSpinBox);
1783 const QVariant old = value;
1784 value = bound(val);
1785 pendingEmit = false;
1786 cleared = false;
1787 if (doUpdate) {
1788 updateEdit();
1789 }
1790 q->update();
1791
1792 if (ep == AlwaysEmit || (ep == EmitIfChanged && old != value)) {
1793 emitSignals(ep, old);
1794 }
1795}
1796
1797/*!
1798 \internal
1799
1800 Updates the line edit to reflect the current value of the spin box.
1801*/
1802
1803void QAbstractSpinBoxPrivate::updateEdit()
1804{
1805 Q_Q(QAbstractSpinBox);
1806 if (type == QVariant::Invalid)
1807 return;
1808 const QString newText = specialValue() ? specialValueText : prefix + textFromValue(value) + suffix;
1809 if (newText == edit->displayText() || cleared)
1810 return;
1811
1812 const bool empty = edit->text().isEmpty();
1813 int cursor = edit->cursorPosition();
1814 int selsize = edit->selectedText().size();
1815 const QSignalBlocker blocker(edit);
1816 edit->setText(newText);
1817
1818 if (!specialValue()) {
1819 cursor = qBound(prefix.size(), cursor, edit->displayText().size() - suffix.size());
1820
1821 if (selsize > 0) {
1822 edit->setSelection(cursor, selsize);
1823 } else {
1824 edit->setCursorPosition(empty ? prefix.size() : cursor);
1825 }
1826 }
1827 q->update();
1828}
1829
1830/*!
1831 \internal
1832
1833 Convenience function to set min/max values.
1834*/
1835
1836void QAbstractSpinBoxPrivate::setRange(const QVariant &min, const QVariant &max)
1837{
1838 Q_Q(QAbstractSpinBox);
1839
1840 clearCache();
1841 minimum = min;
1842 maximum = (variantCompare(min, max) < 0 ? max : min);
1843 cachedSizeHint = QSize();
1844 cachedMinimumSizeHint = QSize(); // minimumSizeHint cares about min/max
1845
1846 reset();
1847 if (!(bound(value) == value)) {
1848 setValue(bound(value), EmitIfChanged);
1849 } else if (value == minimum && !specialValueText.isEmpty()) {
1850 updateEdit();
1851 }
1852
1853 q->updateGeometry();
1854}
1855
1856/*!
1857 \internal
1858
1859 Convenience function to get a variant of the right type.
1860*/
1861
1862QVariant QAbstractSpinBoxPrivate::getZeroVariant() const
1863{
1864 QVariant ret;
1865 switch (type) {
1866 case QVariant::Int: ret = QVariant((int)0); break;
1867 case QVariant::Double: ret = QVariant((double)0.0); break;
1868 default: break;
1869 }
1870 return ret;
1871}
1872
1873/*!
1874 \internal
1875
1876 Virtual method called that calls the public textFromValue()
1877 functions in the subclasses. Needed to change signature from
1878 QVariant to int/double/QDateTime etc. Used when needing to display
1879 a value textually.
1880
1881 This method is reimeplemented in the various subclasses.
1882*/
1883
1884QString QAbstractSpinBoxPrivate::textFromValue(const QVariant &) const
1885{
1886 return QString();
1887}
1888
1889/*!
1890 \internal
1891
1892 Virtual method called that calls the public valueFromText()
1893 functions in the subclasses. Needed to change signature from
1894 QVariant to int/double/QDateTime etc. Used when needing to
1895 interpret a string as another type.
1896
1897 This method is reimeplemented in the various subclasses.
1898*/
1899
1900QVariant QAbstractSpinBoxPrivate::valueFromText(const QString &) const
1901{
1902 return QVariant();
1903}
1904/*!
1905 \internal
1906
1907 Interprets text and emits signals. Called when the spinbox needs
1908 to interpret the text on the lineedit.
1909*/
1910
1911void QAbstractSpinBoxPrivate::interpret(EmitPolicy ep)
1912{
1913 Q_Q(QAbstractSpinBox);
1914 if (type == QVariant::Invalid || cleared)
1915 return;
1916
1917 QVariant v = getZeroVariant();
1918 bool doInterpret = true;
1919 QString tmp = edit->displayText();
1920 int pos = edit->cursorPosition();
1921 const int oldpos = pos;
1922
1923 if (q->validate(tmp, pos) != QValidator::Acceptable) {
1924 const QString copy = tmp;
1925 q->fixup(tmp);
1926 QASBDEBUG() << "QAbstractSpinBoxPrivate::interpret() text '"
1927 << edit->displayText()
1928 << "' >> '" << copy << '\''
1929 << "' >> '" << tmp << '\'';
1930
1931 doInterpret = tmp != copy && (q->validate(tmp, pos) == QValidator::Acceptable);
1932 if (!doInterpret) {
1933 v = (correctionMode == QAbstractSpinBox::CorrectToNearestValue
1934 ? variantBound(minimum, v, maximum) : value);
1935 }
1936 }
1937 if (doInterpret) {
1938 v = valueFromText(tmp);
1939 }
1940 clearCache();
1941 setValue(v, ep, true);
1942 if (oldpos != pos)
1943 edit->setCursorPosition(pos);
1944}
1945
1946void QAbstractSpinBoxPrivate::clearCache() const
1947{
1948 cachedText.clear();
1949 cachedValue.clear();
1950 cachedState = QValidator::Acceptable;
1951}
1952
1953QVariant QAbstractSpinBoxPrivate::calculateAdaptiveDecimalStep(int steps) const
1954{
1955 Q_UNUSED(steps)
1956 return singleStep;
1957}
1958
1959// --- QSpinBoxValidator ---
1960
1961/*!
1962 \internal
1963 Constructs a QSpinBoxValidator object
1964*/
1965
1966QSpinBoxValidator::QSpinBoxValidator(QAbstractSpinBox *qp, QAbstractSpinBoxPrivate *dp)
1967 : QValidator(qp), qptr(qp), dptr(dp)
1968{
1969 setObjectName(QLatin1String("qt_spinboxvalidator"));
1970}
1971
1972/*!
1973 \internal
1974
1975 Checks for specialValueText, prefix, suffix and calls
1976 the virtual QAbstractSpinBox::validate function.
1977*/
1978
1979QValidator::State QSpinBoxValidator::validate(QString &input, int &pos) const
1980{
1981 if (dptr->specialValueText.size() > 0 && input == dptr->specialValueText)
1982 return QValidator::Acceptable;
1983
1984 if (!dptr->prefix.isEmpty() && !input.startsWith(dptr->prefix)) {
1985 input.prepend(dptr->prefix);
1986 pos += dptr->prefix.length();
1987 }
1988
1989 if (!dptr->suffix.isEmpty() && !input.endsWith(dptr->suffix))
1990 input.append(dptr->suffix);
1991
1992 return qptr->validate(input, pos);
1993}
1994/*!
1995 \internal
1996 Calls the virtual QAbstractSpinBox::fixup function.
1997*/
1998
1999void QSpinBoxValidator::fixup(QString &input) const
2000{
2001 qptr->fixup(input);
2002}
2003
2004// --- global ---
2005
2006/*!
2007 \internal
2008 Adds two variants together and returns the result.
2009*/
2010
2011QVariant operator+(const QVariant &arg1, const QVariant &arg2)
2012{
2013 QVariant ret;
2014 if (Q_UNLIKELY(arg1.type() != arg2.type()))
2015 qWarning("QAbstractSpinBox: Internal error: Different types (%s vs %s) (%s:%d)",
2016 arg1.typeName(), arg2.typeName(), __FILE__, __LINE__);
2017 switch (arg1.type()) {
2018 case QVariant::Int: {
2019 const int int1 = arg1.toInt();
2020 const int int2 = arg2.toInt();
2021 if (int1 > 0 && (int2 >= INT_MAX - int1)) {
2022 // The increment overflows
2023 ret = QVariant(INT_MAX);
2024 } else if (int1 < 0 && (int2 <= INT_MIN - int1)) {
2025 // The increment underflows
2026 ret = QVariant(INT_MIN);
2027 } else {
2028 ret = QVariant(int1 + int2);
2029 }
2030 break;
2031 }
2032 case QVariant::Double: ret = QVariant(arg1.toDouble() + arg2.toDouble()); break;
2033#if QT_CONFIG(datetimeparser)
2034 case QVariant::DateTime: {
2035 QDateTime a2 = arg2.toDateTime();
2036 QDateTime a1 = arg1.toDateTime().addDays(QDATETIMEEDIT_DATE_MIN.daysTo(a2.date()));
2037 a1.setTime(a1.time().addMSecs(a2.time().msecsSinceStartOfDay()));
2038 ret = QVariant(a1);
2039 break;
2040 }
2041#endif // datetimeparser
2042 default: break;
2043 }
2044 return ret;
2045}
2046
2047
2048/*!
2049 \internal
2050 Subtracts two variants and returns the result.
2051*/
2052
2053QVariant operator-(const QVariant &arg1, const QVariant &arg2)
2054{
2055 QVariant ret;
2056 if (Q_UNLIKELY(arg1.type() != arg2.type()))
2057 qWarning("QAbstractSpinBox: Internal error: Different types (%s vs %s) (%s:%d)",
2058 arg1.typeName(), arg2.typeName(), __FILE__, __LINE__);
2059 switch (arg1.type()) {
2060 case QVariant::Int: ret = QVariant(arg1.toInt() - arg2.toInt()); break;
2061 case QVariant::Double: ret = QVariant(arg1.toDouble() - arg2.toDouble()); break;
2062 case QVariant::DateTime: {
2063 QDateTime a1 = arg1.toDateTime();
2064 QDateTime a2 = arg2.toDateTime();
2065 int days = a2.daysTo(a1);
2066 int secs = a2.secsTo(a1);
2067 int msecs = qMax(0, a1.time().msec() - a2.time().msec());
2068 if (days < 0 || secs < 0 || msecs < 0) {
2069 ret = arg1;
2070 } else {
2071 QDateTime dt = a2.addDays(days).addSecs(secs);
2072 if (msecs > 0)
2073 dt.setTime(dt.time().addMSecs(msecs));
2074 ret = QVariant(dt);
2075 }
2076 }
2077 default: break;
2078 }
2079 return ret;
2080}
2081
2082/*!
2083 \internal
2084 Multiplies \a arg1 by \a multiplier and returns the result.
2085*/
2086
2087QVariant operator*(const QVariant &arg1, double multiplier)
2088{
2089 QVariant ret;
2090
2091 switch (arg1.type()) {
2092 case QVariant::Int:
2093 ret = static_cast<int>(qBound<double>(INT_MIN, arg1.toInt() * multiplier, INT_MAX));
2094 break;
2095 case QVariant::Double: ret = QVariant(arg1.toDouble() * multiplier); break;
2096#if QT_CONFIG(datetimeparser)
2097 case QVariant::DateTime: {
2098 double days = QDATETIMEEDIT_DATE_MIN.daysTo(arg1.toDateTime().date()) * multiplier;
2099 const qint64 daysInt = qint64(days);
2100 days -= daysInt;
2101 qint64 msecs = qint64(arg1.toDateTime().time().msecsSinceStartOfDay() * multiplier
2102 + days * (24 * 3600 * 1000));
2103 ret = QDATETIMEEDIT_DATE_MIN.addDays(daysInt).startOfDay().addMSecs(msecs);
2104 break;
2105 }
2106#endif // datetimeparser
2107 default: ret = arg1; break;
2108 }
2109
2110 return ret;
2111}
2112
2113
2114
2115double operator/(const QVariant &arg1, const QVariant &arg2)
2116{
2117 double a1 = 0;
2118 double a2 = 0;
2119
2120 switch (arg1.type()) {
2121 case QVariant::Int:
2122 a1 = (double)arg1.toInt();
2123 a2 = (double)arg2.toInt();
2124 break;
2125 case QVariant::Double:
2126 a1 = arg1.toDouble();
2127 a2 = arg2.toDouble();
2128 break;
2129#if QT_CONFIG(datetimeparser)
2130 case QVariant::DateTime:
2131 a1 = QDATETIMEEDIT_DATE_MIN.daysTo(arg1.toDate());
2132 a2 = QDATETIMEEDIT_DATE_MIN.daysTo(arg2.toDate());
2133 a1 += arg1.toDateTime().time().msecsSinceStartOfDay() / (36e5 * 24);
2134 a2 += arg2.toDateTime().time().msecsSinceStartOfDay() / (36e5 * 24);
2135 break;
2136#endif // datetimeparser
2137 default: break;
2138 }
2139
2140 return (a1 != 0 && a2 != 0) ? (a1 / a2) : 0.0;
2141}
2142
2143int QAbstractSpinBoxPrivate::variantCompare(const QVariant &arg1, const QVariant &arg2)
2144{
2145 switch (arg2.type()) {
2146 case QVariant::Date:
2147 Q_ASSERT_X(arg1.type() == QVariant::Date, "QAbstractSpinBoxPrivate::variantCompare",
2148 qPrintable(QString::fromLatin1("Internal error 1 (%1)").
2149 arg(QString::fromLatin1(arg1.typeName()))));
2150 if (arg1.toDate() == arg2.toDate()) {
2151 return 0;
2152 } else if (arg1.toDate() < arg2.toDate()) {
2153 return -1;
2154 } else {
2155 return 1;
2156 }
2157 case QVariant::Time:
2158 Q_ASSERT_X(arg1.type() == QVariant::Time, "QAbstractSpinBoxPrivate::variantCompare",
2159 qPrintable(QString::fromLatin1("Internal error 2 (%1)").
2160 arg(QString::fromLatin1(arg1.typeName()))));
2161 if (arg1.toTime() == arg2.toTime()) {
2162 return 0;
2163 } else if (arg1.toTime() < arg2.toTime()) {
2164 return -1;
2165 } else {
2166 return 1;
2167 }
2168
2169
2170 case QVariant::DateTime:
2171 if (arg1.toDateTime() == arg2.toDateTime()) {
2172 return 0;
2173 } else if (arg1.toDateTime() < arg2.toDateTime()) {
2174 return -1;
2175 } else {
2176 return 1;
2177 }
2178 case QVariant::Int:
2179 if (arg1.toInt() == arg2.toInt()) {
2180 return 0;
2181 } else if (arg1.toInt() < arg2.toInt()) {
2182 return -1;
2183 } else {
2184 return 1;
2185 }
2186 case QVariant::Double:
2187 if (arg1.toDouble() == arg2.toDouble()) {
2188 return 0;
2189 } else if (arg1.toDouble() < arg2.toDouble()) {
2190 return -1;
2191 } else {
2192 return 1;
2193 }
2194 case QVariant::Invalid:
2195 if (arg2.type() == QVariant::Invalid)
2196 return 0;
2197 Q_FALLTHROUGH();
2198 default:
2199 Q_ASSERT_X(0, "QAbstractSpinBoxPrivate::variantCompare",
2200 qPrintable(QString::fromLatin1("Internal error 3 (%1 %2)").
2201 arg(QString::fromLatin1(arg1.typeName()),
2202 QString::fromLatin1(arg2.typeName()))));
2203 }
2204 return -2;
2205}
2206
2207QVariant QAbstractSpinBoxPrivate::variantBound(const QVariant &min,
2208 const QVariant &value,
2209 const QVariant &max)
2210{
2211 Q_ASSERT(variantCompare(min, max) <= 0);
2212 if (variantCompare(min, value) < 0) {
2213 const int compMax = variantCompare(value, max);
2214 return (compMax < 0 ? value : max);
2215 } else {
2216 return min;
2217 }
2218}
2219
2220
2221QT_END_NAMESPACE
2222
2223#include "moc_qabstractspinbox.cpp"
2224