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

Last change on this file since 651 was 651, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.2 sources.

File size: 57.6 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 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 d->reset();
1252 QPointer<QMenu> menu = d->edit->createStandardContextMenu();
1253
1254 QAction *selAll = new QAction(tr("&Select All"), menu);
1255 menu->insertAction(d->edit->d_func()->selectAllAction,
1256 selAll);
1257 menu->removeAction(d->edit->d_func()->selectAllAction);
1258 menu->addSeparator();
1259 const uint se = stepEnabled();
1260 QAction *up = menu->addAction(tr("&Step up"));
1261 up->setEnabled(se & StepUpEnabled);
1262 QAction *down = menu->addAction(tr("Step &down"));
1263 down->setEnabled(se & StepDownEnabled);
1264 menu->addSeparator();
1265
1266 const QPointer<QAbstractSpinBox> that = this;
1267 const QPoint pos = (event->reason() == QContextMenuEvent::Mouse)
1268 ? event->globalPos() : mapToGlobal(QPoint(event->pos().x(), 0)) + QPoint(width() / 2, height() / 2);
1269 const QAction *action = menu->exec(pos);
1270 delete static_cast<QMenu *>(menu);
1271 if (that && action) {
1272 if (action == up) {
1273 stepBy(1);
1274 } else if (action == down) {
1275 stepBy(-1);
1276 } else if (action == selAll) {
1277 selectAll();
1278 }
1279 }
1280 event->accept();
1281#endif // QT_NO_CONTEXTMENU
1282}
1283
1284/*!
1285 \reimp
1286*/
1287
1288void QAbstractSpinBox::mouseMoveEvent(QMouseEvent *event)
1289{
1290 Q_D(QAbstractSpinBox);
1291
1292 d->updateHoverControl(event->pos());
1293
1294 // If we have a timer ID, update the state
1295 if (d->spinClickTimerId != -1 && d->buttonSymbols != NoButtons) {
1296 const StepEnabled se = stepEnabled();
1297 if ((se & StepUpEnabled) && d->hoverControl == QStyle::SC_SpinBoxUp)
1298 d->updateState(true);
1299 else if ((se & StepDownEnabled) && d->hoverControl == QStyle::SC_SpinBoxDown)
1300 d->updateState(false);
1301 else
1302 d->reset();
1303 event->accept();
1304 }
1305}
1306
1307/*!
1308 \reimp
1309*/
1310
1311void QAbstractSpinBox::mousePressEvent(QMouseEvent *event)
1312{
1313 Q_D(QAbstractSpinBox);
1314
1315 if (event->button() != Qt::LeftButton || d->buttonState != None) {
1316 return;
1317 }
1318
1319 d->updateHoverControl(event->pos());
1320 event->accept();
1321
1322 const StepEnabled se = (d->buttonSymbols == NoButtons) ? StepEnabled(StepNone) : stepEnabled();
1323 if ((se & StepUpEnabled) && d->hoverControl == QStyle::SC_SpinBoxUp) {
1324 d->updateState(true);
1325 } else if ((se & StepDownEnabled) && d->hoverControl == QStyle::SC_SpinBoxDown) {
1326 d->updateState(false);
1327 } else {
1328 event->ignore();
1329 }
1330}
1331
1332/*!
1333 \reimp
1334*/
1335void QAbstractSpinBox::mouseReleaseEvent(QMouseEvent *event)
1336{
1337 Q_D(QAbstractSpinBox);
1338
1339 if ((d->buttonState & Mouse) != 0)
1340 d->reset();
1341 event->accept();
1342}
1343
1344// --- QAbstractSpinBoxPrivate ---
1345
1346/*!
1347 \internal
1348 Constructs a QAbstractSpinBoxPrivate object
1349*/
1350
1351QAbstractSpinBoxPrivate::QAbstractSpinBoxPrivate()
1352 : edit(0), type(QVariant::Invalid), spinClickTimerId(-1),
1353 spinClickTimerInterval(100), spinClickThresholdTimerId(-1), spinClickThresholdTimerInterval(-1),
1354 effectiveSpinRepeatRate(1), buttonState(None), cachedText(QLatin1String("\x01")),
1355 cachedState(QValidator::Invalid), pendingEmit(false), readOnly(false), wrapping(false),
1356 ignoreCursorPositionChanged(false), frame(true), accelerate(false), keyboardTracking(true),
1357 cleared(false), ignoreUpdateEdit(false), correctionMode(QAbstractSpinBox::CorrectToPreviousValue),
1358 acceleration(0), hoverControl(QStyle::SC_None), buttonSymbols(QAbstractSpinBox::UpDownArrows), validator(0)
1359{
1360}
1361
1362/*
1363 \internal
1364 Called when the QAbstractSpinBoxPrivate is destroyed
1365*/
1366QAbstractSpinBoxPrivate::~QAbstractSpinBoxPrivate()
1367{
1368}
1369
1370/*!
1371 \internal
1372 Updates the old and new hover control. Does nothing if the hover
1373 control has not changed.
1374*/
1375bool QAbstractSpinBoxPrivate::updateHoverControl(const QPoint &pos)
1376{
1377 Q_Q(QAbstractSpinBox);
1378 QRect lastHoverRect = hoverRect;
1379 QStyle::SubControl lastHoverControl = hoverControl;
1380 bool doesHover = q->testAttribute(Qt::WA_Hover);
1381 if (lastHoverControl != newHoverControl(pos) && doesHover) {
1382 q->update(lastHoverRect);
1383 q->update(hoverRect);
1384 return true;
1385 }
1386 return !doesHover;
1387}
1388
1389/*!
1390 \internal
1391 Returns the hover control at \a pos.
1392 This will update the hoverRect and hoverControl.
1393*/
1394QStyle::SubControl QAbstractSpinBoxPrivate::newHoverControl(const QPoint &pos)
1395{
1396 Q_Q(QAbstractSpinBox);
1397
1398 QStyleOptionSpinBox opt;
1399 q->initStyleOption(&opt);
1400 opt.subControls = QStyle::SC_All;
1401 hoverControl = q->style()->hitTestComplexControl(QStyle::CC_SpinBox, &opt, pos, q);
1402 hoverRect = q->style()->subControlRect(QStyle::CC_SpinBox, &opt, hoverControl, q);
1403 return hoverControl;
1404}
1405
1406/*!
1407 \internal
1408 Strips any prefix/suffix from \a text.
1409*/
1410
1411QString QAbstractSpinBoxPrivate::stripped(const QString &t, int *pos) const
1412{
1413 QString text = t;
1414 if (specialValueText.size() == 0 || text != specialValueText) {
1415 int from = 0;
1416 int size = text.size();
1417 bool changed = false;
1418 if (prefix.size() && text.startsWith(prefix)) {
1419 from += prefix.size();
1420 size -= from;
1421 changed = true;
1422 }
1423 if (suffix.size() && text.endsWith(suffix)) {
1424 size -= suffix.size();
1425 changed = true;
1426 }
1427 if (changed)
1428 text = text.mid(from, size);
1429 }
1430
1431 const int s = text.size();
1432 text = text.trimmed();
1433 if (pos)
1434 (*pos) -= (s - text.size());
1435 return text;
1436
1437}
1438
1439void QAbstractSpinBoxPrivate::updateEditFieldGeometry()
1440{
1441 Q_Q(QAbstractSpinBox);
1442 QStyleOptionSpinBox opt;
1443 q->initStyleOption(&opt);
1444 opt.subControls = QStyle::SC_SpinBoxEditField;
1445 edit->setGeometry(q->style()->subControlRect(QStyle::CC_SpinBox, &opt,
1446 QStyle::SC_SpinBoxEditField, q));
1447}
1448/*!
1449 \internal
1450 Returns true if a specialValueText has been set and the current value is minimum.
1451*/
1452
1453bool QAbstractSpinBoxPrivate::specialValue() const
1454{
1455 return (value == minimum && !specialValueText.isEmpty());
1456}
1457
1458/*!
1459 \internal Virtual function that emits signals when the value
1460 changes. Reimplemented in the different subclasses.
1461*/
1462
1463void QAbstractSpinBoxPrivate::emitSignals(EmitPolicy, const QVariant &)
1464{
1465}
1466
1467/*!
1468 \internal
1469
1470 Slot connected to the line edit's textChanged(const QString &)
1471 signal.
1472*/
1473
1474void QAbstractSpinBoxPrivate::_q_editorTextChanged(const QString &t)
1475{
1476 Q_Q(QAbstractSpinBox);
1477
1478 if (keyboardTracking) {
1479 QString tmp = t;
1480 int pos = edit->cursorPosition();
1481 QValidator::State state = q->validate(tmp, pos);
1482 if (state == QValidator::Acceptable) {
1483 const QVariant v = valueFromText(tmp);
1484 setValue(v, EmitIfChanged, tmp != t);
1485 pendingEmit = false;
1486 } else {
1487 pendingEmit = true;
1488 }
1489 } else {
1490 pendingEmit = true;
1491 }
1492}
1493
1494/*!
1495 \internal
1496
1497 Virtual slot connected to the line edit's
1498 cursorPositionChanged(int, int) signal. Will move the cursor to a
1499 valid position if the new one is invalid. E.g. inside the prefix.
1500 Reimplemented in Q[Date|Time|DateTime]EditPrivate to account for
1501 the different sections etc.
1502*/
1503
1504void QAbstractSpinBoxPrivate::_q_editorCursorPositionChanged(int oldpos, int newpos)
1505{
1506 if (!edit->hasSelectedText() && !ignoreCursorPositionChanged && !specialValue()) {
1507 ignoreCursorPositionChanged = true;
1508
1509 bool allowSelection = true;
1510 int pos = -1;
1511 if (newpos < prefix.size() && newpos != 0) {
1512 if (oldpos == 0) {
1513 allowSelection = false;
1514 pos = prefix.size();
1515 } else {
1516 pos = oldpos;
1517 }
1518 } else if (newpos > edit->text().size() - suffix.size()
1519 && newpos != edit->text().size()) {
1520 if (oldpos == edit->text().size()) {
1521 pos = edit->text().size() - suffix.size();
1522 allowSelection = false;
1523 } else {
1524 pos = edit->text().size();
1525 }
1526 }
1527 if (pos != -1) {
1528 const int selSize = edit->selectionStart() >= 0 && allowSelection
1529 ? (edit->selectedText().size()
1530 * (newpos < pos ? -1 : 1)) - newpos + pos
1531 : 0;
1532
1533 const bool wasBlocked = edit->blockSignals(true);
1534 if (selSize != 0) {
1535 edit->setSelection(pos - selSize, selSize);
1536 } else {
1537 edit->setCursorPosition(pos);
1538 }
1539 edit->blockSignals(wasBlocked);
1540 }
1541 ignoreCursorPositionChanged = false;
1542 }
1543}
1544
1545/*!
1546 \internal
1547
1548 Initialises the QAbstractSpinBoxPrivate object.
1549*/
1550
1551void QAbstractSpinBoxPrivate::init()
1552{
1553 Q_Q(QAbstractSpinBox);
1554
1555 q->setLineEdit(new QLineEdit(q));
1556 edit->setObjectName(QLatin1String("qt_spinbox_lineedit"));
1557 validator = new QSpinBoxValidator(q, this);
1558 edit->setValidator(validator);
1559
1560 QStyleOptionSpinBox opt;
1561 q->initStyleOption(&opt);
1562 spinClickTimerInterval = q->style()->styleHint(QStyle::SH_SpinBox_ClickAutoRepeatRate, &opt, q);
1563 spinClickThresholdTimerInterval = q->style()->styleHint(QStyle::SH_SpinBox_ClickAutoRepeatThreshold, &opt, q);
1564 q->setFocusPolicy(Qt::WheelFocus);
1565 q->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed, QSizePolicy::SpinBox));
1566 q->setAttribute(Qt::WA_InputMethodEnabled);
1567
1568 q->setAttribute(Qt::WA_MacShowFocusRect);
1569}
1570
1571/*!
1572 \internal
1573
1574 Resets the state of the spinbox. E.g. the state is set to
1575 (Keyboard|Up) if Key up is currently pressed.
1576*/
1577
1578void QAbstractSpinBoxPrivate::reset()
1579{
1580 Q_Q(QAbstractSpinBox);
1581
1582 buttonState = None;
1583 if (q) {
1584 if (spinClickTimerId != -1)
1585 q->killTimer(spinClickTimerId);
1586 if (spinClickThresholdTimerId != -1)
1587 q->killTimer(spinClickThresholdTimerId);
1588 spinClickTimerId = spinClickThresholdTimerId = -1;
1589 acceleration = 0;
1590 q->update();
1591 }
1592}
1593
1594/*!
1595 \internal
1596
1597 Updates the state of the spinbox.
1598*/
1599
1600void QAbstractSpinBoxPrivate::updateState(bool up, bool fromKeyboard /* = false */)
1601{
1602 Q_Q(QAbstractSpinBox);
1603 if ((up && (buttonState & Up)) || (!up && (buttonState & Down)))
1604 return;
1605 reset();
1606 if (q && (q->stepEnabled() & (up ? QAbstractSpinBox::StepUpEnabled
1607 : QAbstractSpinBox::StepDownEnabled))) {
1608 spinClickThresholdTimerId = q->startTimer(spinClickThresholdTimerInterval);
1609 buttonState = (up ? Up : Down) | (fromKeyboard ? Keyboard : Mouse);
1610 q->stepBy(up ? 1 : -1);
1611#ifndef QT_NO_ACCESSIBILITY
1612 QAccessible::updateAccessibility(q, 0, QAccessible::ValueChanged);
1613#endif
1614 }
1615}
1616
1617
1618/*!
1619 Initialize \a option with the values from this QSpinBox. This method
1620 is useful for subclasses when they need a QStyleOptionSpinBox, but don't want
1621 to fill in all the information themselves.
1622
1623 \sa QStyleOption::initFrom()
1624*/
1625void QAbstractSpinBox::initStyleOption(QStyleOptionSpinBox *option) const
1626{
1627 if (!option)
1628 return;
1629
1630 Q_D(const QAbstractSpinBox);
1631 option->initFrom(this);
1632 option->activeSubControls = QStyle::SC_None;
1633 option->buttonSymbols = d->buttonSymbols;
1634 option->subControls = QStyle::SC_SpinBoxFrame | QStyle::SC_SpinBoxEditField;
1635 if (d->buttonSymbols != QAbstractSpinBox::NoButtons) {
1636 option->subControls |= QStyle::SC_SpinBoxUp | QStyle::SC_SpinBoxDown;
1637 if (d->buttonState & Up) {
1638 option->activeSubControls = QStyle::SC_SpinBoxUp;
1639 } else if (d->buttonState & Down) {
1640 option->activeSubControls = QStyle::SC_SpinBoxDown;
1641 }
1642 }
1643
1644 if (d->buttonState) {
1645 option->state |= QStyle::State_Sunken;
1646 } else {
1647 option->activeSubControls = d->hoverControl;
1648 }
1649
1650 option->stepEnabled = style()->styleHint(QStyle::SH_SpinControls_DisableOnBounds)
1651 ? stepEnabled()
1652 : (QAbstractSpinBox::StepDownEnabled|QAbstractSpinBox::StepUpEnabled);
1653
1654 option->frame = d->frame;
1655}
1656
1657/*!
1658 \internal
1659
1660 Bounds \a val to be within minimum and maximum. Also tries to be
1661 clever about setting it at min and max depending on what it was
1662 and what direction it was changed etc.
1663*/
1664
1665QVariant QAbstractSpinBoxPrivate::bound(const QVariant &val, const QVariant &old, int steps) const
1666{
1667 QVariant v = val;
1668 if (!wrapping || steps == 0 || old.isNull()) {
1669 if (variantCompare(v, minimum) < 0) {
1670 v = wrapping ? maximum : minimum;
1671 }
1672 if (variantCompare(v, maximum) > 0) {
1673 v = wrapping ? minimum : maximum;
1674 }
1675 } else {
1676 const bool wasMin = old == minimum;
1677 const bool wasMax = old == maximum;
1678 const int oldcmp = variantCompare(v, old);
1679 const int maxcmp = variantCompare(v, maximum);
1680 const int mincmp = variantCompare(v, minimum);
1681 const bool wrapped = (oldcmp > 0 && steps < 0) || (oldcmp < 0 && steps > 0);
1682 if (maxcmp > 0) {
1683 v = ((wasMax && !wrapped && steps > 0) || (steps < 0 && !wasMin && wrapped))
1684 ? minimum : maximum;
1685 } else if (wrapped && (maxcmp > 0 || mincmp < 0)) {
1686 v = ((wasMax && steps > 0) || (!wasMin && steps < 0)) ? minimum : maximum;
1687 } else if (mincmp < 0) {
1688 v = (!wasMax && !wasMin ? minimum : maximum);
1689 }
1690 }
1691
1692 return v;
1693}
1694
1695/*!
1696 \internal
1697
1698 Sets the value of the spin box to \a val. Depending on the value
1699 of \a ep it will also emit signals.
1700*/
1701
1702void QAbstractSpinBoxPrivate::setValue(const QVariant &val, EmitPolicy ep,
1703 bool doUpdate)
1704{
1705 Q_Q(QAbstractSpinBox);
1706 const QVariant old = value;
1707 value = bound(val);
1708 pendingEmit = false;
1709 cleared = false;
1710 if (doUpdate) {
1711 updateEdit();
1712 }
1713 q->update();
1714
1715 if (ep == AlwaysEmit || (ep == EmitIfChanged && old != value)) {
1716 emitSignals(ep, old);
1717 }
1718}
1719
1720/*!
1721 \internal
1722
1723 Updates the line edit to reflect the current value of the spin box.
1724*/
1725
1726void QAbstractSpinBoxPrivate::updateEdit()
1727{
1728 Q_Q(QAbstractSpinBox);
1729 if (type == QVariant::Invalid)
1730 return;
1731 const QString newText = specialValue() ? specialValueText : prefix + textFromValue(value) + suffix;
1732 if (newText == edit->displayText() || cleared)
1733 return;
1734
1735 const bool empty = edit->text().isEmpty();
1736 int cursor = edit->cursorPosition();
1737 int selsize = edit->selectedText().size();
1738 const bool sb = edit->blockSignals(true);
1739 edit->setText(newText);
1740
1741 if (!specialValue()) {
1742 cursor = qBound(prefix.size(), cursor, edit->displayText().size() - suffix.size());
1743
1744 if (selsize > 0) {
1745 edit->setSelection(cursor, selsize);
1746 } else {
1747 edit->setCursorPosition(empty ? prefix.size() : cursor);
1748 }
1749 }
1750 edit->blockSignals(sb);
1751 q->update();
1752}
1753
1754/*!
1755 \internal
1756
1757 Convenience function to set min/max values.
1758*/
1759
1760void QAbstractSpinBoxPrivate::setRange(const QVariant &min, const QVariant &max)
1761{
1762 Q_Q(QAbstractSpinBox);
1763
1764 clearCache();
1765 minimum = min;
1766 maximum = (variantCompare(min, max) < 0 ? max : min);
1767 cachedSizeHint = QSize(); // minimumSizeHint doesn't care about min/max
1768
1769 reset();
1770 if (!(bound(value) == value)) {
1771 setValue(bound(value), EmitIfChanged);
1772 } else if (value == minimum && !specialValueText.isEmpty()) {
1773 updateEdit();
1774 }
1775
1776 q->updateGeometry();
1777}
1778
1779/*!
1780 \internal
1781
1782 Convenience function to get a variant of the right type.
1783*/
1784
1785QVariant QAbstractSpinBoxPrivate::getZeroVariant() const
1786{
1787 QVariant ret;
1788 switch (type) {
1789 case QVariant::Int: ret = QVariant((int)0); break;
1790 case QVariant::Double: ret = QVariant((double)0.0); break;
1791 default: break;
1792 }
1793 return ret;
1794}
1795
1796/*!
1797 \internal
1798
1799 Virtual method called that calls the public textFromValue()
1800 functions in the subclasses. Needed to change signature from
1801 QVariant to int/double/QDateTime etc. Used when needing to display
1802 a value textually.
1803
1804 This method is reimeplemented in the various subclasses.
1805*/
1806
1807QString QAbstractSpinBoxPrivate::textFromValue(const QVariant &) const
1808{
1809 return QString();
1810}
1811
1812/*!
1813 \internal
1814
1815 Virtual method called that calls the public valueFromText()
1816 functions in the subclasses. Needed to change signature from
1817 QVariant to int/double/QDateTime etc. Used when needing to
1818 interpret a string as another type.
1819
1820 This method is reimeplemented in the various subclasses.
1821*/
1822
1823QVariant QAbstractSpinBoxPrivate::valueFromText(const QString &) const
1824{
1825 return QVariant();
1826}
1827/*!
1828 \internal
1829
1830 Interprets text and emits signals. Called when the spinbox needs
1831 to interpret the text on the lineedit.
1832*/
1833
1834void QAbstractSpinBoxPrivate::interpret(EmitPolicy ep)
1835{
1836 Q_Q(QAbstractSpinBox);
1837 if (type == QVariant::Invalid || cleared)
1838 return;
1839
1840 QVariant v = getZeroVariant();
1841 bool doInterpret = true;
1842 QString tmp = edit->displayText();
1843 int pos = edit->cursorPosition();
1844 const int oldpos = pos;
1845
1846 if (q->validate(tmp, pos) != QValidator::Acceptable) {
1847 const QString copy = tmp;
1848 q->fixup(tmp);
1849 QASBDEBUG() << "QAbstractSpinBoxPrivate::interpret() text '"
1850 << edit->displayText()
1851 << "' >> '" << copy << '\''
1852 << "' >> '" << tmp << '\'';
1853
1854 doInterpret = tmp != copy && (q->validate(tmp, pos) == QValidator::Acceptable);
1855 if (!doInterpret) {
1856 v = (correctionMode == QAbstractSpinBox::CorrectToNearestValue
1857 ? variantBound(minimum, v, maximum) : value);
1858 }
1859 }
1860 if (doInterpret) {
1861 v = valueFromText(tmp);
1862 }
1863 clearCache();
1864 setValue(v, ep, true);
1865 if (oldpos != pos)
1866 edit->setCursorPosition(pos);
1867}
1868
1869void QAbstractSpinBoxPrivate::clearCache() const
1870{
1871 cachedText.clear();
1872 cachedValue.clear();
1873 cachedState = QValidator::Acceptable;
1874}
1875
1876
1877// --- QSpinBoxValidator ---
1878
1879/*!
1880 \internal
1881 Constructs a QSpinBoxValidator object
1882*/
1883
1884QSpinBoxValidator::QSpinBoxValidator(QAbstractSpinBox *qp, QAbstractSpinBoxPrivate *dp)
1885 : QValidator(qp), qptr(qp), dptr(dp)
1886{
1887 setObjectName(QLatin1String("qt_spinboxvalidator"));
1888}
1889
1890/*!
1891 \internal
1892
1893 Checks for specialValueText, prefix, suffix and calls
1894 the virtual QAbstractSpinBox::validate function.
1895*/
1896
1897QValidator::State QSpinBoxValidator::validate(QString &input, int &pos) const
1898{
1899 if (dptr->specialValueText.size() > 0 && input == dptr->specialValueText)
1900 return QValidator::Acceptable;
1901
1902 if (!dptr->prefix.isEmpty() && !input.startsWith(dptr->prefix)) {
1903 input.prepend(dptr->prefix);
1904 pos += dptr->prefix.length();
1905 }
1906
1907 if (!dptr->suffix.isEmpty() && !input.endsWith(dptr->suffix))
1908 input.append(dptr->suffix);
1909
1910 return qptr->validate(input, pos);
1911}
1912/*!
1913 \internal
1914 Calls the virtual QAbstractSpinBox::fixup function.
1915*/
1916
1917void QSpinBoxValidator::fixup(QString &input) const
1918{
1919 qptr->fixup(input);
1920}
1921
1922// --- global ---
1923
1924/*!
1925 \internal
1926 Adds two variants together and returns the result.
1927*/
1928
1929QVariant operator+(const QVariant &arg1, const QVariant &arg2)
1930{
1931 QVariant ret;
1932 if (arg1.type() != arg2.type())
1933 qWarning("QAbstractSpinBox: Internal error: Different types (%s vs %s) (%s:%d)",
1934 arg1.typeName(), arg2.typeName(), __FILE__, __LINE__);
1935 switch (arg1.type()) {
1936 case QVariant::Int: ret = QVariant(arg1.toInt() + arg2.toInt()); break;
1937 case QVariant::Double: ret = QVariant(arg1.toDouble() + arg2.toDouble()); break;
1938 case QVariant::DateTime: {
1939 QDateTime a2 = arg2.toDateTime();
1940 QDateTime a1 = arg1.toDateTime().addDays(QDATETIMEEDIT_DATETIME_MIN.daysTo(a2));
1941 a1.setTime(a1.time().addMSecs(QTime().msecsTo(a2.time())));
1942 ret = QVariant(a1);
1943 }
1944 default: break;
1945 }
1946 return ret;
1947}
1948
1949
1950/*!
1951 \internal
1952 Subtracts two variants and returns the result.
1953*/
1954
1955QVariant operator-(const QVariant &arg1, const QVariant &arg2)
1956{
1957 QVariant ret;
1958 if (arg1.type() != arg2.type())
1959 qWarning("QAbstractSpinBox: Internal error: Different types (%s vs %s) (%s:%d)",
1960 arg1.typeName(), arg2.typeName(), __FILE__, __LINE__);
1961 switch (arg1.type()) {
1962 case QVariant::Int: ret = QVariant(arg1.toInt() - arg2.toInt()); break;
1963 case QVariant::Double: ret = QVariant(arg1.toDouble() - arg2.toDouble()); break;
1964 case QVariant::DateTime: {
1965 QDateTime a1 = arg1.toDateTime();
1966 QDateTime a2 = arg2.toDateTime();
1967 int days = a2.daysTo(a1);
1968 int secs = a2.secsTo(a1);
1969 int msecs = qMax(0, a1.time().msec() - a2.time().msec());
1970 if (days < 0 || secs < 0 || msecs < 0) {
1971 ret = arg1;
1972 } else {
1973 QDateTime dt = a2.addDays(days).addSecs(secs);
1974 if (msecs > 0)
1975 dt.setTime(dt.time().addMSecs(msecs));
1976 ret = QVariant(dt);
1977 }
1978 }
1979 default: break;
1980 }
1981 return ret;
1982}
1983
1984/*!
1985 \internal
1986 Multiplies \a arg1 by \a multiplier and returns the result.
1987*/
1988
1989QVariant operator*(const QVariant &arg1, double multiplier)
1990{
1991 QVariant ret;
1992
1993 switch (arg1.type()) {
1994 case QVariant::Int: ret = QVariant((int)(arg1.toInt() * multiplier)); break;
1995 case QVariant::Double: ret = QVariant(arg1.toDouble() * multiplier); break;
1996 case QVariant::DateTime: {
1997 double days = QDATETIMEEDIT_DATE_MIN.daysTo(arg1.toDateTime().date()) * multiplier;
1998 int daysInt = (int)days;
1999 days -= daysInt;
2000 long msecs = (long)((QDATETIMEEDIT_TIME_MIN.msecsTo(arg1.toDateTime().time()) * multiplier)
2001 + (days * (24 * 3600 * 1000)));
2002 ret = QDateTime(QDate().addDays(int(days)), QTime().addMSecs(msecs));
2003 break;
2004 }
2005 default: ret = arg1; break;
2006 }
2007
2008 return ret;
2009}
2010
2011
2012
2013double operator/(const QVariant &arg1, const QVariant &arg2)
2014{
2015 double a1 = 0;
2016 double a2 = 0;
2017
2018 switch (arg1.type()) {
2019 case QVariant::Int:
2020 a1 = (double)arg1.toInt();
2021 a2 = (double)arg2.toInt();
2022 break;
2023 case QVariant::Double:
2024 a1 = arg1.toDouble();
2025 a2 = arg2.toDouble();
2026 break;
2027 case QVariant::DateTime:
2028 a1 = QDATETIMEEDIT_DATE_MIN.daysTo(arg1.toDate());
2029 a2 = QDATETIMEEDIT_DATE_MIN.daysTo(arg2.toDate());
2030 a1 += (double)QDATETIMEEDIT_TIME_MIN.msecsTo(arg1.toDateTime().time()) / (long)(3600 * 24 * 1000);
2031 a2 += (double)QDATETIMEEDIT_TIME_MIN.msecsTo(arg2.toDateTime().time()) / (long)(3600 * 24 * 1000);
2032 default: break;
2033 }
2034
2035 return (a1 != 0 && a2 != 0) ? (a1 / a2) : 0.0;
2036}
2037
2038int QAbstractSpinBoxPrivate::variantCompare(const QVariant &arg1, const QVariant &arg2)
2039{
2040 switch (arg2.type()) {
2041 case QVariant::Date:
2042 Q_ASSERT_X(arg1.type() == QVariant::Date, "QAbstractSpinBoxPrivate::variantCompare",
2043 qPrintable(QString::fromAscii("Internal error 1 (%1)").
2044 arg(QString::fromAscii(arg1.typeName()))));
2045 if (arg1.toDate() == arg2.toDate()) {
2046 return 0;
2047 } else if (arg1.toDate() < arg2.toDate()) {
2048 return -1;
2049 } else {
2050 return 1;
2051 }
2052 case QVariant::Time:
2053 Q_ASSERT_X(arg1.type() == QVariant::Time, "QAbstractSpinBoxPrivate::variantCompare",
2054 qPrintable(QString::fromAscii("Internal error 2 (%1)").
2055 arg(QString::fromAscii(arg1.typeName()))));
2056 if (arg1.toTime() == arg2.toTime()) {
2057 return 0;
2058 } else if (arg1.toTime() < arg2.toTime()) {
2059 return -1;
2060 } else {
2061 return 1;
2062 }
2063
2064
2065 case QVariant::DateTime:
2066 if (arg1.toDateTime() == arg2.toDateTime()) {
2067 return 0;
2068 } else if (arg1.toDateTime() < arg2.toDateTime()) {
2069 return -1;
2070 } else {
2071 return 1;
2072 }
2073 case QVariant::Int:
2074 if (arg1.toInt() == arg2.toInt()) {
2075 return 0;
2076 } else if (arg1.toInt() < arg2.toInt()) {
2077 return -1;
2078 } else {
2079 return 1;
2080 }
2081 case QVariant::Double:
2082 if (arg1.toDouble() == arg2.toDouble()) {
2083 return 0;
2084 } else if (arg1.toDouble() < arg2.toDouble()) {
2085 return -1;
2086 } else {
2087 return 1;
2088 }
2089 case QVariant::Invalid:
2090 if (arg2.type() == QVariant::Invalid)
2091 return 0;
2092 default:
2093 Q_ASSERT_X(0, "QAbstractSpinBoxPrivate::variantCompare",
2094 qPrintable(QString::fromAscii("Internal error 3 (%1 %2)").
2095 arg(QString::fromAscii(arg1.typeName())).
2096 arg(QString::fromAscii(arg2.typeName()))));
2097 }
2098 return -2;
2099}
2100
2101QVariant QAbstractSpinBoxPrivate::variantBound(const QVariant &min,
2102 const QVariant &value,
2103 const QVariant &max)
2104{
2105 Q_ASSERT(variantCompare(min, max) <= 0);
2106 if (variantCompare(min, value) < 0) {
2107 const int compMax = variantCompare(value, max);
2108 return (compMax < 0 ? value : max);
2109 } else {
2110 return min;
2111 }
2112}
2113
2114
2115QT_END_NAMESPACE
2116
2117#include "moc_qabstractspinbox.cpp"
2118
2119#endif // QT_NO_SPINBOX
Note: See TracBrowser for help on using the repository browser.