source: trunk/src/gui/widgets/qabstractspinbox.cpp@ 349

Last change on this file since 349 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

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