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

Last change on this file since 1147 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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