/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <qplatformdefs.h>
#include <private/qabstractspinbox_p.h>
#include <private/qdatetime_p.h>
#include <private/qlineedit_p.h>
#include <qabstractspinbox.h>
#ifndef QT_NO_SPINBOX
#include <qapplication.h>
#include <qclipboard.h>
#include <qdatetime.h>
#include <qdatetimeedit.h>
#include <qevent.h>
#include <qmenu.h>
#include <qpainter.h>
#include <qpalette.h>
#include <qstylepainter.h>
#include <qdebug.h>
#ifndef QT_NO_ACCESSIBILITY
# include <qaccessible.h>
#endif
#if defined(Q_WS_X11)
#include <limits.h>
#endif
#if defined(Q_OS_SYMBIAN)
#include <w32std.h>
#include <private/qt_s60_p.h>
#endif
//#define QABSTRACTSPINBOX_QSBDEBUG
#ifdef QABSTRACTSPINBOX_QSBDEBUG
# define QASBDEBUG qDebug
#else
# define QASBDEBUG if (false) qDebug
#endif
QT_BEGIN_NAMESPACE
/*!
\class QAbstractSpinBox
\brief The QAbstractSpinBox class provides a spinbox and a line edit to
display values.
\ingroup abstractwidgets
The class is designed as a common super class for widgets like
QSpinBox, QDoubleSpinBox and QDateTimeEdit
Here are the main properties of the class:
\list 1
\i \l text: The text that is displayed in the QAbstractSpinBox.
\i \l alignment: The alignment of the text in the QAbstractSpinBox.
\i \l wrapping: Whether the QAbstractSpinBox wraps from the
minimum value to the maximum value and vica versa.
\endlist
QAbstractSpinBox provides a virtual stepBy() function that is
called whenever the user triggers a step. This function takes an
integer value to signify how many steps were taken. E.g. Pressing
Qt::Key_Down will trigger a call to stepBy(-1).
QAbstractSpinBox also provide a virtual function stepEnabled() to
determine whether stepping up/down is allowed at any point. This
function returns a bitset of StepEnabled.
\sa QAbstractSlider, QSpinBox, QDoubleSpinBox, QDateTimeEdit,
{Spin Boxes Example}
*/
/*!
\enum QAbstractSpinBox::StepEnabledFlag
\value StepNone
\value StepUpEnabled
\value StepDownEnabled
*/
/*!
\fn void QAbstractSpinBox::editingFinished()
This signal is emitted editing is finished. This happens when the
spinbox loses focus and when enter is pressed.
*/
/*!
Constructs an abstract spinbox with the given \a parent with default
\l wrapping, and \l alignment properties.
*/
QAbstractSpinBox::QAbstractSpinBox(QWidget *parent)
: QWidget(*new QAbstractSpinBoxPrivate, parent, 0)
{
Q_D(QAbstractSpinBox);
d->init();
}
/*!
\internal
*/
QAbstractSpinBox::QAbstractSpinBox(QAbstractSpinBoxPrivate &dd, QWidget *parent)
: QWidget(dd, parent, 0)
{
Q_D(QAbstractSpinBox);
d->init();
}
/*!
Called when the QAbstractSpinBox is destroyed.
*/
QAbstractSpinBox::~QAbstractSpinBox()
{
}
/*!
\enum QAbstractSpinBox::ButtonSymbols
This enum type describes the symbols that can be displayed on the buttons
in a spin box.
\inlineimage qspinbox-updown.png
\inlineimage qspinbox-plusminus.png
\value UpDownArrows Little arrows in the classic style.
\value PlusMinus \bold{+} and \bold{-} symbols.
\value NoButtons Don't display buttons.
\sa QAbstractSpinBox::buttonSymbols
*/
/*!
\property QAbstractSpinBox::buttonSymbols
\brief the current button symbol mode
The possible values can be either \c UpDownArrows or \c PlusMinus.
The default is \c UpDownArrows.
Note that some styles might render PlusMinus and UpDownArrows
identically.
\sa ButtonSymbols
*/
QAbstractSpinBox::ButtonSymbols QAbstractSpinBox::buttonSymbols() const
{
Q_D(const QAbstractSpinBox);
return d->buttonSymbols;
}
void QAbstractSpinBox::setButtonSymbols(ButtonSymbols buttonSymbols)
{
Q_D(QAbstractSpinBox);
if (d->buttonSymbols != buttonSymbols) {
d->buttonSymbols = buttonSymbols;
d->updateEditFieldGeometry();
update();
}
}
/*!
\property QAbstractSpinBox::text
\brief the spin box's text, including any prefix and suffix
There is no default text.
*/
QString QAbstractSpinBox::text() const
{
return lineEdit()->displayText();
}
/*!
\property QAbstractSpinBox::specialValueText
\brief the special-value text
If set, the spin box will display this text instead of a numeric
value whenever the current value is equal to minimum(). Typical use
is to indicate that this choice has a special (default) meaning.
For example, if your spin box allows the user to choose a scale factor
(or zoom level) for displaying an image, and your application is able
to automatically choose one that will enable the image to fit completely
within the display window, you can set up the spin box like this:
\snippet examples/widgets/spinboxes/window.cpp 3
The user will then be able to choose a scale from 1% to 1000%
or select "Auto" to leave it up to the application to choose. Your code
must then interpret the spin box value of 0 as a request from the user
to scale the image to fit inside the window.
All values are displayed with the prefix and suffix (if set), \e
except for the special value, which only shows the special value
text. This special text is passed in the QSpinBox::valueChanged()
signal that passes a QString.
To turn off the special-value text display, call this function
with an empty string. The default is no special-value text, i.e.
the numeric value is shown as usual.
If no special-value text is set, specialValueText() returns an
empty string.
*/
QString QAbstractSpinBox::specialValueText() const
{
Q_D(const QAbstractSpinBox);
return d->specialValueText;
}
void QAbstractSpinBox::setSpecialValueText(const QString &specialValueText)
{
Q_D(QAbstractSpinBox);
d->specialValueText = specialValueText;
d->cachedSizeHint = QSize(); // minimumSizeHint doesn't care about specialValueText
d->clearCache();
d->updateEdit();
}
/*!
\property QAbstractSpinBox::wrapping
\brief whether the spin box is circular.
If wrapping is true stepping up from maximum() value will take you
to the minimum() value and vica versa. Wrapping only make sense if
you have minimum() and maximum() values set.
\snippet doc/src/snippets/code/src_gui_widgets_qabstractspinbox.cpp 0
\sa QSpinBox::minimum(), QSpinBox::maximum()
*/
bool QAbstractSpinBox::wrapping() const
{
Q_D(const QAbstractSpinBox);
return d->wrapping;
}
void QAbstractSpinBox::setWrapping(bool wrapping)
{
Q_D(QAbstractSpinBox);
d->wrapping = wrapping;
}
/*!
\property QAbstractSpinBox::readOnly
\brief whether the spin box is read only.
In read-only mode, the user can still copy the text to the
clipboard, or drag and drop the text;
but cannot edit it.
The QLineEdit in the QAbstractSpinBox does not show a cursor in
read-only mode.
\sa QLineEdit::readOnly
*/
bool QAbstractSpinBox::isReadOnly() const
{
Q_D(const QAbstractSpinBox);
return d->readOnly;
}
void QAbstractSpinBox::setReadOnly(bool enable)
{
Q_D(QAbstractSpinBox);
d->readOnly = enable;
d->edit->setReadOnly(enable);
update();
}
/*!
\property QAbstractSpinBox::keyboardTracking
\brief whether keyboard tracking is enabled for the spinbox.
\since 4.3
If keyboard tracking is enabled (the default), the spinbox
emits the valueChanged() signal while the new value is being
entered from the keyboard.
E.g. when the user enters the value 600 by typing 6, 0, and 0,
the spinbox emits 3 signals with the values 6, 60, and 600
respectively.
If keyboard tracking is disabled, the spinbox doesn't emit the
valueChanged() signal while typing. It emits the signal later,
when the return key is pressed, when keyboard focus is lost, or
when other spinbox functionality is used, e.g. pressing an arrow
key.
*/
bool QAbstractSpinBox::keyboardTracking() const
{
Q_D(const QAbstractSpinBox);
return d->keyboardTracking;
}
void QAbstractSpinBox::setKeyboardTracking(bool enable)
{
Q_D(QAbstractSpinBox);
d->keyboardTracking = enable;
}
/*!
\property QAbstractSpinBox::frame
\brief whether the spin box draws itself with a frame
If enabled (the default) the spin box draws itself inside a frame,
otherwise the spin box draws itself without any frame.
*/
bool QAbstractSpinBox::hasFrame() const
{
Q_D(const QAbstractSpinBox);
return d->frame;
}
void QAbstractSpinBox::setFrame(bool enable)
{
Q_D(QAbstractSpinBox);
d->frame = enable;
update();
d->updateEditFieldGeometry();
}
/*!
\property QAbstractSpinBox::accelerated
\brief whether the spin box will accelerate the frequency of the steps when
pressing the step Up/Down buttons.
\since 4.2
If enabled the spin box will increase/decrease the value faster
the longer you hold the button down.
*/
void QAbstractSpinBox::setAccelerated(bool accelerate)
{
Q_D(QAbstractSpinBox);
d->accelerate = accelerate;
}
bool QAbstractSpinBox::isAccelerated() const
{
Q_D(const QAbstractSpinBox);
return d->accelerate;
}
/*!
\enum QAbstractSpinBox::CorrectionMode
This enum type describes the mode the spinbox will use to correct
an \l{QValidator::}{Intermediate} value if editing finishes.
\value CorrectToPreviousValue The spinbox will revert to the last
valid value.
\value CorrectToNearestValue The spinbox will revert to the nearest
valid value.
\sa correctionMode
*/
/*!
\property QAbstractSpinBox::correctionMode
\brief the mode to correct an \l{QValidator::}{Intermediate}
value if editing finishes
\since 4.2
The default mode is QAbstractSpinBox::CorrectToPreviousValue.
\sa acceptableInput, validate(), fixup()
*/
void QAbstractSpinBox::setCorrectionMode(CorrectionMode correctionMode)
{
Q_D(QAbstractSpinBox);
d->correctionMode = correctionMode;
}
QAbstractSpinBox::CorrectionMode QAbstractSpinBox::correctionMode() const
{
Q_D(const QAbstractSpinBox);
return d->correctionMode;
}
/*!
\property QAbstractSpinBox::acceptableInput
\brief whether the input satisfies the current validation
\since 4.2
\sa validate(), fixup(), correctionMode
*/
bool QAbstractSpinBox::hasAcceptableInput() const
{
Q_D(const QAbstractSpinBox);
return d->edit->hasAcceptableInput();
}
/*!
\property QAbstractSpinBox::alignment
\brief the alignment of the spin box
Possible Values are Qt::AlignLeft, Qt::AlignRight, and Qt::AlignHCenter.
By default, the alignment is Qt::AlignLeft
Attempting to set the alignment to an illegal flag combination
does nothing.
\sa Qt::Alignment
*/
Qt::Alignment QAbstractSpinBox::alignment() const
{
Q_D(const QAbstractSpinBox);
return (Qt::Alignment)d->edit->alignment();
}
void QAbstractSpinBox::setAlignment(Qt::Alignment flag)
{
Q_D(QAbstractSpinBox);
d->edit->setAlignment(flag);
}
/*!
Selects all the text in the spinbox except the prefix and suffix.
*/
void QAbstractSpinBox::selectAll()
{
Q_D(QAbstractSpinBox);
if (!d->specialValue()) {
const int tmp = d->edit->displayText().size() - d->suffix.size();
d->edit->setSelection(tmp, -(tmp - d->prefix.size()));
} else {
d->edit->selectAll();
}
}
/*!
Clears the lineedit of all text but prefix and suffix.
*/
void QAbstractSpinBox::clear()
{
Q_D(QAbstractSpinBox);
d->edit->setText(d->prefix + d->suffix);
d->edit->setCursorPosition(d->prefix.size());
d->cleared = true;
}
/*!
Virtual function that determines whether stepping up and down is
legal at any given time.
The up arrow will be painted as disabled unless (stepEnabled() &
StepUpEnabled) != 0.
The default implementation will return (StepUpEnabled|
StepDownEnabled) if wrapping is turned on. Else it will return
StepDownEnabled if value is > minimum() or'ed with StepUpEnabled if
value < maximum().
If you subclass QAbstractSpinBox you will need to reimplement this function.
\sa QSpinBox::minimum(), QSpinBox::maximum(), wrapping()
*/
QAbstractSpinBox::StepEnabled QAbstractSpinBox::stepEnabled() const
{
Q_D(const QAbstractSpinBox);
if (d->readOnly || d->type == QVariant::Invalid)
return StepNone;
if (d->wrapping)
return StepEnabled(StepUpEnabled | StepDownEnabled);
StepEnabled ret = StepNone;
if (d->variantCompare(d->value, d->maximum) < 0) {
ret |= StepUpEnabled;
}
if (d->variantCompare(d->value, d->minimum) > 0) {
ret |= StepDownEnabled;
}
return ret;
}
/*!
This virtual function is called by the QAbstractSpinBox to
determine whether \a input is valid. The \a pos parameter indicates
the position in the string. Reimplemented in the various
subclasses.
*/
QValidator::State QAbstractSpinBox::validate(QString & /* input */, int & /* pos */) const
{
return QValidator::Acceptable;
}
/*!
This virtual function is called by the QAbstractSpinBox if the
\a input is not validated to QValidator::Acceptable when Return is
pressed or interpretText() is called. It will try to change the
text so it is valid. Reimplemented in the various subclasses.
*/
void QAbstractSpinBox::fixup(QString & /* input */) const
{
}
/*!
Steps up by one linestep
Calling this slot is analogous to calling stepBy(1);
\sa stepBy(), stepDown()
*/
void QAbstractSpinBox::stepUp()
{
stepBy(1);
}
/*!
Steps down by one linestep
Calling this slot is analogous to calling stepBy(-1);
\sa stepBy(), stepUp()
*/
void QAbstractSpinBox::stepDown()
{
stepBy(-1);
}
/*!
Virtual function that is called whenever the user triggers a step.
The \a steps parameter indicates how many steps were taken, e.g.
Pressing Qt::Key_Down will trigger a call to stepBy(-1),
whereas pressing Qt::Key_Prior will trigger a call to
stepBy(10).
If you subclass QAbstractSpinBox you must reimplement this
function. Note that this function is called even if the resulting
value will be outside the bounds of minimum and maximum. It's this
function's job to handle these situations.
*/
void QAbstractSpinBox::stepBy(int steps)
{
Q_D(QAbstractSpinBox);
const QVariant old = d->value;
QString tmp = d->edit->displayText();
int cursorPos = d->edit->cursorPosition();
bool dontstep = false;
EmitPolicy e = EmitIfChanged;
if (d->pendingEmit) {
dontstep = validate(tmp, cursorPos) != QValidator::Acceptable;
d->cleared = false;
d->interpret(NeverEmit);
if (d->value != old)
e = AlwaysEmit;
}
if (!dontstep) {
d->setValue(d->bound(d->value + (d->singleStep * steps), old, steps), e);
} else if (e == AlwaysEmit) {
d->emitSignals(e, old);
}
selectAll();
}
/*!
This function returns a pointer to the line edit of the spin box.
*/
QLineEdit *QAbstractSpinBox::lineEdit() const
{
Q_D(const QAbstractSpinBox);
return d->edit;
}
/*!
\fn void QAbstractSpinBox::setLineEdit(QLineEdit *lineEdit)
Sets the line edit of the spinbox to be \a lineEdit instead of the
current line edit widget. \a lineEdit can not be 0.
QAbstractSpinBox takes ownership of the new lineEdit
If QLineEdit::validator() for the \a lineEdit returns 0, the internal
validator of the spinbox will be set on the line edit.
*/
void QAbstractSpinBox::setLineEdit(QLineEdit *lineEdit)
{
Q_D(QAbstractSpinBox);
if (!lineEdit) {
Q_ASSERT(lineEdit);
return;
}
delete d->edit;
d->edit = lineEdit;
if (!d->edit->validator())
d->edit->setValidator(d->validator);
if (d->edit->parent() != this)
d->edit->setParent(this);
d->edit->setFrame(false);
d->edit->setFocusProxy(this);
d->edit->setAcceptDrops(false);
if (d->type != QVariant::Invalid) {
connect(d->edit, SIGNAL(textChanged(QString)),
this, SLOT(_q_editorTextChanged(QString)));
connect(d->edit, SIGNAL(cursorPositionChanged(int,int)),
this, SLOT(_q_editorCursorPositionChanged(int,int)));
}
d->updateEditFieldGeometry();
d->edit->setContextMenuPolicy(Qt::NoContextMenu);
if (isVisible())
d->edit->show();
if (isVisible())
d->updateEdit();
}
/*!
This function interprets the text of the spin box. If the value
has changed since last interpretation it will emit signals.
*/
void QAbstractSpinBox::interpretText()
{
Q_D(QAbstractSpinBox);
d->interpret(EmitIfChanged);
}
/*
Reimplemented in 4.6, so be careful.
*/
/*!
\reimp
*/
QVariant QAbstractSpinBox::inputMethodQuery(Qt::InputMethodQuery query) const
{
Q_D(const QAbstractSpinBox);
return d->edit->inputMethodQuery(query);
}
/*!
\reimp
*/
bool QAbstractSpinBox::event(QEvent *event)
{
Q_D(QAbstractSpinBox);
switch (event->type()) {
case QEvent::FontChange:
case QEvent::StyleChange:
d->cachedSizeHint = d->cachedMinimumSizeHint = QSize();
break;
case QEvent::ApplicationLayoutDirectionChange:
case QEvent::LayoutDirectionChange:
d->updateEditFieldGeometry();
break;
case QEvent::HoverEnter:
case QEvent::HoverLeave:
case QEvent::HoverMove:
if (const QHoverEvent *he = static_cast<const QHoverEvent *>(event))
d->updateHoverControl(he->pos());
break;
case QEvent::ShortcutOverride:
if (d->edit->event(event))
return true;
break;
#ifdef QT_KEYPAD_NAVIGATION
case QEvent::EnterEditFocus:
case QEvent::LeaveEditFocus:
if (QApplication::keypadNavigationEnabled()) {
const bool b = d->edit->event(event);
d->edit->setSelection(d->edit->displayText().size() - d->suffix.size(),0);
if (event->type() == QEvent::LeaveEditFocus)
emit editingFinished();
if (b)
return true;
}
break;
#endif
case QEvent::InputMethod:
return d->edit->event(event);
default:
break;
}
return QWidget::event(event);
}
/*!
\reimp
*/
void QAbstractSpinBox::showEvent(QShowEvent *)
{
Q_D(QAbstractSpinBox);
d->reset();
if (d->ignoreUpdateEdit) {
d->ignoreUpdateEdit = false;
} else {
d->updateEdit();
}
}
/*!
\reimp
*/
void QAbstractSpinBox::changeEvent(QEvent *event)
{
Q_D(QAbstractSpinBox);
switch (event->type()) {
case QEvent::StyleChange:
d->spinClickTimerInterval = style()->styleHint(QStyle::SH_SpinBox_ClickAutoRepeatRate, 0, this);
d->spinClickThresholdTimerInterval =
style()->styleHint(QStyle::SH_SpinBox_ClickAutoRepeatThreshold, 0, this);
d->reset();
d->updateEditFieldGeometry();
break;
case QEvent::EnabledChange:
if (!isEnabled()) {
d->reset();
}
break;
case QEvent::ActivationChange:
if (!isActiveWindow()){
d->reset();
if (d->pendingEmit) // pendingEmit can be true even if it hasn't changed.
d->interpret(EmitIfChanged); // E.g. 10 to 10.0
}
break;
default:
break;
}
QWidget::changeEvent(event);
}
/*!
\reimp
*/
void QAbstractSpinBox::resizeEvent(QResizeEvent *event)
{
Q_D(QAbstractSpinBox);
QWidget::resizeEvent(event);
d->updateEditFieldGeometry();
update();
}
/*!
\reimp
*/
QSize QAbstractSpinBox::sizeHint() const
{
Q_D(const QAbstractSpinBox);
if (d->cachedSizeHint.isEmpty()) {
ensurePolished();
const QFontMetrics fm(fontMetrics());
int h = d->edit->sizeHint().height();
int w = 0;
QString s;
s = d->prefix + d->textFromValue(d->minimum) + d->suffix + QLatin1Char(' ');
s.truncate(18);
w = qMax(w, fm.width(s));
s = d->prefix + d->textFromValue(d->maximum) + d->suffix + QLatin1Char(' ');
s.truncate(18);
w = qMax(w, fm.width(s));
if (d->specialValueText.size()) {
s = d->specialValueText;
w = qMax(w, fm.width(s));
}
w += 2; // cursor blinking space
QStyleOptionSpinBox opt;
initStyleOption(&opt);
QSize hint(w, h);
QSize extra(35, 6);
opt.rect.setSize(hint + extra);
extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt,
QStyle::SC_SpinBoxEditField, this).size();
// get closer to final result by repeating the calculation
opt.rect.setSize(hint + extra);
extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt,
QStyle::SC_SpinBoxEditField, this).size();
hint += extra;
opt.rect = rect();
d->cachedSizeHint = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this)
.expandedTo(QApplication::globalStrut());
}
return d->cachedSizeHint;
}
/*!
\reimp
*/
QSize QAbstractSpinBox::minimumSizeHint() const
{
Q_D(const QAbstractSpinBox);
if (d->cachedMinimumSizeHint.isEmpty()) {
ensurePolished();
const QFontMetrics fm(fontMetrics());
int h = d->edit->minimumSizeHint().height();
int w = fm.width(QLatin1String("1000"));
w += 2; // cursor blinking space
QStyleOptionSpinBox opt;
initStyleOption(&opt);
QSize hint(w, h);
QSize extra(35, 6);
opt.rect.setSize(hint + extra);
extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt,
QStyle::SC_SpinBoxEditField, this).size();
// get closer to final result by repeating the calculation
opt.rect.setSize(hint + extra);
extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt,
QStyle::SC_SpinBoxEditField, this).size();
hint += extra;
opt.rect = rect();
d->cachedMinimumSizeHint = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this)
.expandedTo(QApplication::globalStrut());
}
return d->cachedMinimumSizeHint;
}
/*!
\reimp
*/
void QAbstractSpinBox::paintEvent(QPaintEvent *)
{
QStyleOptionSpinBox opt;
initStyleOption(&opt);
QStylePainter p(this);
p.drawComplexControl(QStyle::CC_SpinBox, opt);
}
/*!
\reimp
This function handles keyboard input.
The following keys are handled specifically:
\table
\row \i Enter/Return
\i This will reinterpret the text and emit a signal even if the value has not changed
since last time a signal was emitted.
\row \i Up
\i This will invoke stepBy(1)
\row \i Down
\i This will invoke stepBy(-1)
\row \i Page up
\i This will invoke stepBy(10)
\row \i Page down
\i This will invoke stepBy(-10)
\endtable
*/
void QAbstractSpinBox::keyPressEvent(QKeyEvent *event)
{
Q_D(QAbstractSpinBox);
if (!event->text().isEmpty() && d->edit->cursorPosition() < d->prefix.size())
d->edit->setCursorPosition(d->prefix.size());
int steps = 1;
bool isPgUpOrDown = false;
switch (event->key()) {
case Qt::Key_PageUp:
case Qt::Key_PageDown:
steps *= 10;
isPgUpOrDown = true;
case Qt::Key_Up:
case Qt::Key_Down: {
#ifdef QT_KEYPAD_NAVIGATION
if (QApplication::keypadNavigationEnabled()) {
// Reserve up/down for nav - use left/right for edit.
if (!hasEditFocus() && (event->key() == Qt::Key_Up
|| event->key() == Qt::Key_Down)) {
event->ignore();
return;
}
}
#endif
event->accept();
const bool up = (event->key() == Qt::Key_PageUp || event->key() == Qt::Key_Up);
if (!(stepEnabled() & (up ? StepUpEnabled : StepDownEnabled)))
return;
if (!up)
steps *= -1;
if (style()->styleHint(QStyle::SH_SpinBox_AnimateButton, 0, this)) {
d->buttonState = (Keyboard | (up ? Up : Down));
}
if (d->spinClickTimerId == -1)
stepBy(steps);
if(event->isAutoRepeat() && !isPgUpOrDown) {
if(d->spinClickThresholdTimerId == -1 && d->spinClickTimerId == -1) {
d->updateState(up, true);
}
}
#ifndef QT_NO_ACCESSIBILITY
QAccessible::updateAccessibility(this, 0, QAccessible::ValueChanged);
#endif
return;
}
#ifdef QT_KEYPAD_NAVIGATION
case Qt::Key_Left:
case Qt::Key_Right:
if (QApplication::keypadNavigationEnabled() && !hasEditFocus()) {
event->ignore();
return;
}
break;
case Qt::Key_Back:
if (QApplication::keypadNavigationEnabled() && !hasEditFocus()) {
event->ignore();
return;
}
break;
#endif
case Qt::Key_Enter:
case Qt::Key_Return:
d->edit->d_func()->control->clearUndo();
d->interpret(d->keyboardTracking ? AlwaysEmit : EmitIfChanged);
selectAll();
event->ignore();
emit editingFinished();
return;
#ifdef QT_KEYPAD_NAVIGATION
case Qt::Key_Select:
if (QApplication::keypadNavigationEnabled()) {
// Toggles between left/right moving cursor and inc/dec.
setEditFocus(!hasEditFocus());
}
return;
#endif
#ifdef Q_WS_X11 // only X11
case Qt::Key_U:
if (event->modifiers() & Qt::ControlModifier) {
event->accept();
if (!isReadOnly())
clear();
return;
}
break;
#endif
case Qt::Key_End:
case Qt::Key_Home:
if (event->modifiers() & Qt::ShiftModifier) {
int currentPos = d->edit->cursorPosition();
const QString text = d->edit->displayText();
if (event->key() == Qt::Key_End) {
if ((currentPos == 0 && !d->prefix.isEmpty()) || text.size() - d->suffix.size() <= currentPos) {
break; // let lineedit handle this
} else {
d->edit->setSelection(currentPos, text.size() - d->suffix.size() - currentPos);
}
} else {
if ((currentPos == text.size() && !d->suffix.isEmpty()) || currentPos <= d->prefix.size()) {
break; // let lineedit handle this
} else {
d->edit->setSelection(currentPos, d->prefix.size() - currentPos);
}
}
event->accept();
return;
}
break;
default:
#ifndef QT_NO_SHORTCUT
if (event == QKeySequence::SelectAll) {
selectAll();
event->accept();
return;
}
#endif
break;
}
d->edit->event(event);
if (!isVisible())
d->ignoreUpdateEdit = true;
}
/*!
\reimp
*/
void QAbstractSpinBox::keyReleaseEvent(QKeyEvent *event)
{
Q_D(QAbstractSpinBox);
if (d->buttonState & Keyboard && !event->isAutoRepeat()) {
d->reset();
} else {
d->edit->event(event);
}
}
/*!
\reimp
*/
#ifndef QT_NO_WHEELEVENT
void QAbstractSpinBox::wheelEvent(QWheelEvent *event)
{
const int steps = (event->delta() > 0 ? 1 : -1);
if (stepEnabled() & (steps > 0 ? StepUpEnabled : StepDownEnabled))
stepBy(event->modifiers() & Qt::ControlModifier ? steps * 10 : steps);
event->accept();
}
#endif
/*!
\reimp
*/
void QAbstractSpinBox::focusInEvent(QFocusEvent *event)
{
Q_D(QAbstractSpinBox);
d->edit->event(event);
if (event->reason() == Qt::TabFocusReason || event->reason() == Qt::BacktabFocusReason) {
selectAll();
}
QWidget::focusInEvent(event);
}
/*!
\reimp
*/
void QAbstractSpinBox::focusOutEvent(QFocusEvent *event)
{
Q_D(QAbstractSpinBox);
if (d->pendingEmit)
d->interpret(EmitIfChanged);
d->reset();
d->edit->event(event);
d->updateEdit();
QWidget::focusOutEvent(event);
#ifdef QT_KEYPAD_NAVIGATION
// editingFinished() is already emitted on LeaveEditFocus
if (!QApplication::keypadNavigationEnabled())
#endif
emit editingFinished();
}
/*!
\reimp
*/
void QAbstractSpinBox::closeEvent(QCloseEvent *event)
{
Q_D(QAbstractSpinBox);
d->reset();
if (d->pendingEmit)
d->interpret(EmitIfChanged);
QWidget::closeEvent(event);
}
/*!
\reimp
*/
void QAbstractSpinBox::hideEvent(QHideEvent *event)
{
Q_D(QAbstractSpinBox);
d->reset();
if (d->pendingEmit)
d->interpret(EmitIfChanged);
QWidget::hideEvent(event);
}
/*!
\internal
Used when acceleration is turned on. We need to get the
keyboard auto repeat rate from OS. This value is used as
argument when starting acceleration related timers.
Every platform should, either, use native calls to obtain
the value or hard code some reasonable rate.
Remember that time value should be given in msecs.
*/
static int getKeyboardAutoRepeatRate() {
int ret = 30;
#if defined(Q_OS_SYMBIAN)
TTimeIntervalMicroSeconds32 initialTime;
TTimeIntervalMicroSeconds32 time;
S60->wsSession().GetKeyboardRepeatRate(initialTime, time);
ret = time.Int() / 1000; // msecs
#elif defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
DWORD time;
if (SystemParametersInfo(SPI_GETKEYBOARDSPEED, 0, &time, 0) != FALSE)
ret = static_cast<int>(1000 / static_cast<int>(time)); // msecs
#endif
return ret; // msecs
}
/*!
\reimp
*/
void QAbstractSpinBox::timerEvent(QTimerEvent *event)
{
Q_D(QAbstractSpinBox);
bool doStep = false;
if (event->timerId() == d->spinClickThresholdTimerId) {
killTimer(d->spinClickThresholdTimerId);
d->spinClickThresholdTimerId = -1;
d->effectiveSpinRepeatRate = d->buttonState & Keyboard
? getKeyboardAutoRepeatRate()
: d->spinClickTimerInterval;
d->spinClickTimerId = startTimer(d->effectiveSpinRepeatRate);
doStep = true;
} else if (event->timerId() == d->spinClickTimerId) {
if (d->accelerate) {
d->acceleration = d->acceleration + (int)(d->effectiveSpinRepeatRate * 0.05);
if (d->effectiveSpinRepeatRate - d->acceleration >= 10) {
killTimer(d->spinClickTimerId);
d->spinClickTimerId = startTimer(d->effectiveSpinRepeatRate - d->acceleration);
}
}
doStep = true;
}
if (doStep) {
const StepEnabled st = stepEnabled();
if (d->buttonState & Up) {
if (!(st & StepUpEnabled)) {
d->reset();
} else {
stepBy(1);
}
} else if (d->buttonState & Down) {
if (!(st & StepDownEnabled)) {
d->reset();
} else {
stepBy(-1);
}
}
return;
}
QWidget::timerEvent(event);
return;
}
/*!
\reimp
*/
void QAbstractSpinBox::contextMenuEvent(QContextMenuEvent *event)
{
#ifdef QT_NO_CONTEXTMENU
Q_UNUSED(event);
#else
Q_D(QAbstractSpinBox);
d->reset();
QPointer<QMenu> menu = d->edit->createStandardContextMenu();
QAction *selAll = new QAction(tr("&Select All"), menu);
menu->insertAction(d->edit->d_func()->selectAllAction,
selAll);
menu->removeAction(d->edit->d_func()->selectAllAction);
menu->addSeparator();
const uint se = stepEnabled();
QAction *up = menu->addAction(tr("&Step up"));
up->setEnabled(se & StepUpEnabled);
QAction *down = menu->addAction(tr("Step &down"));
down->setEnabled(se & StepDownEnabled);
menu->addSeparator();
const QPointer<QAbstractSpinBox> that = this;
const QPoint pos = (event->reason() == QContextMenuEvent::Mouse)
? event->globalPos() : mapToGlobal(QPoint(event->pos().x(), 0)) + QPoint(width() / 2, height() / 2);
const QAction *action = menu->exec(pos);
delete static_cast<QMenu *>(menu);
if (that && action) {
if (action == up) {
stepBy(1);
} else if (action == down) {
stepBy(-1);
} else if (action == selAll) {
selectAll();
}
}
event->accept();
#endif // QT_NO_CONTEXTMENU
}
/*!
\reimp
*/
void QAbstractSpinBox::mouseMoveEvent(QMouseEvent *event)
{
Q_D(QAbstractSpinBox);
d->updateHoverControl(event->pos());
// If we have a timer ID, update the state
if (d->spinClickTimerId != -1 && d->buttonSymbols != NoButtons) {
const StepEnabled se = stepEnabled();
if ((se & StepUpEnabled) && d->hoverControl == QStyle::SC_SpinBoxUp)
d->updateState(true);
else if ((se & StepDownEnabled) && d->hoverControl == QStyle::SC_SpinBoxDown)
d->updateState(false);
else
d->reset();
event->accept();
}
}
/*!
\reimp
*/
void QAbstractSpinBox::mousePressEvent(QMouseEvent *event)
{
Q_D(QAbstractSpinBox);
if (event->button() != Qt::LeftButton || d->buttonState != None) {
return;
}
d->updateHoverControl(event->pos());
event->accept();
const StepEnabled se = (d->buttonSymbols == NoButtons) ? StepEnabled(StepNone) : stepEnabled();
if ((se & StepUpEnabled) && d->hoverControl == QStyle::SC_SpinBoxUp) {
d->updateState(true);
} else if ((se & StepDownEnabled) && d->hoverControl == QStyle::SC_SpinBoxDown) {
d->updateState(false);
} else {
event->ignore();
}
}
/*!
\reimp
*/
void QAbstractSpinBox::mouseReleaseEvent(QMouseEvent *event)
{
Q_D(QAbstractSpinBox);
if ((d->buttonState & Mouse) != 0)
d->reset();
event->accept();
}
// --- QAbstractSpinBoxPrivate ---
/*!
\internal
Constructs a QAbstractSpinBoxPrivate object
*/
QAbstractSpinBoxPrivate::QAbstractSpinBoxPrivate()
: edit(0), type(QVariant::Invalid), spinClickTimerId(-1),
spinClickTimerInterval(100), spinClickThresholdTimerId(-1), spinClickThresholdTimerInterval(-1),
effectiveSpinRepeatRate(1), buttonState(None), cachedText(QLatin1String("\x01")),
cachedState(QValidator::Invalid), pendingEmit(false), readOnly(false), wrapping(false),
ignoreCursorPositionChanged(false), frame(true), accelerate(false), keyboardTracking(true),
cleared(false), ignoreUpdateEdit(false), correctionMode(QAbstractSpinBox::CorrectToPreviousValue),
acceleration(0), hoverControl(QStyle::SC_None), buttonSymbols(QAbstractSpinBox::UpDownArrows), validator(0)
{
}
/*
\internal
Called when the QAbstractSpinBoxPrivate is destroyed
*/
QAbstractSpinBoxPrivate::~QAbstractSpinBoxPrivate()
{
}
/*!
\internal
Updates the old and new hover control. Does nothing if the hover
control has not changed.
*/
bool QAbstractSpinBoxPrivate::updateHoverControl(const QPoint &pos)
{
Q_Q(QAbstractSpinBox);
QRect lastHoverRect = hoverRect;
QStyle::SubControl lastHoverControl = hoverControl;
bool doesHover = q->testAttribute(Qt::WA_Hover);
if (lastHoverControl != newHoverControl(pos) && doesHover) {
q->update(lastHoverRect);
q->update(hoverRect);
return true;
}
return !doesHover;
}
/*!
\internal
Returns the hover control at \a pos.
This will update the hoverRect and hoverControl.
*/
QStyle::SubControl QAbstractSpinBoxPrivate::newHoverControl(const QPoint &pos)
{
Q_Q(QAbstractSpinBox);
QStyleOptionSpinBox opt;
q->initStyleOption(&opt);
opt.subControls = QStyle::SC_All;
hoverControl = q->style()->hitTestComplexControl(QStyle::CC_SpinBox, &opt, pos, q);
hoverRect = q->style()->subControlRect(QStyle::CC_SpinBox, &opt, hoverControl, q);
return hoverControl;
}
/*!
\internal
Strips any prefix/suffix from \a text.
*/
QString QAbstractSpinBoxPrivate::stripped(const QString &t, int *pos) const
{
QString text = t;
if (specialValueText.size() == 0 || text != specialValueText) {
int from = 0;
int size = text.size();
bool changed = false;
if (prefix.size() && text.startsWith(prefix)) {
from += prefix.size();
size -= from;
changed = true;
}
if (suffix.size() && text.endsWith(suffix)) {
size -= suffix.size();
changed = true;
}
if (changed)
text = text.mid(from, size);
}
const int s = text.size();
text = text.trimmed();
if (pos)
(*pos) -= (s - text.size());
return text;
}
void QAbstractSpinBoxPrivate::updateEditFieldGeometry()
{
Q_Q(QAbstractSpinBox);
QStyleOptionSpinBox opt;
q->initStyleOption(&opt);
opt.subControls = QStyle::SC_SpinBoxEditField;
edit->setGeometry(q->style()->subControlRect(QStyle::CC_SpinBox, &opt,
QStyle::SC_SpinBoxEditField, q));
}
/*!
\internal
Returns true if a specialValueText has been set and the current value is minimum.
*/
bool QAbstractSpinBoxPrivate::specialValue() const
{
return (value == minimum && !specialValueText.isEmpty());
}
/*!
\internal Virtual function that emits signals when the value
changes. Reimplemented in the different subclasses.
*/
void QAbstractSpinBoxPrivate::emitSignals(EmitPolicy, const QVariant &)
{
}
/*!
\internal
Slot connected to the line edit's textChanged(const QString &)
signal.
*/
void QAbstractSpinBoxPrivate::_q_editorTextChanged(const QString &t)
{
Q_Q(QAbstractSpinBox);
if (keyboardTracking) {
QString tmp = t;
int pos = edit->cursorPosition();
QValidator::State state = q->validate(tmp, pos);
if (state == QValidator::Acceptable) {
const QVariant v = valueFromText(tmp);
setValue(v, EmitIfChanged, tmp != t);
pendingEmit = false;
} else {
pendingEmit = true;
}
} else {
pendingEmit = true;
}
}
/*!
\internal
Virtual slot connected to the line edit's
cursorPositionChanged(int, int) signal. Will move the cursor to a
valid position if the new one is invalid. E.g. inside the prefix.
Reimplemented in Q[Date|Time|DateTime]EditPrivate to account for
the different sections etc.
*/
void QAbstractSpinBoxPrivate::_q_editorCursorPositionChanged(int oldpos, int newpos)
{
if (!edit->hasSelectedText() && !ignoreCursorPositionChanged && !specialValue()) {
ignoreCursorPositionChanged = true;
bool allowSelection = true;
int pos = -1;
if (newpos < prefix.size() && newpos != 0) {
if (oldpos == 0) {
allowSelection = false;
pos = prefix.size();
} else {
pos = oldpos;
}
} else if (newpos > edit->text().size() - suffix.size()
&& newpos != edit->text().size()) {
if (oldpos == edit->text().size()) {
pos = edit->text().size() - suffix.size();
allowSelection = false;
} else {
pos = edit->text().size();
}
}
if (pos != -1) {
const int selSize = edit->selectionStart() >= 0 && allowSelection
? (edit->selectedText().size()
* (newpos < pos ? -1 : 1)) - newpos + pos
: 0;
const bool wasBlocked = edit->blockSignals(true);
if (selSize != 0) {
edit->setSelection(pos - selSize, selSize);
} else {
edit->setCursorPosition(pos);
}
edit->blockSignals(wasBlocked);
}
ignoreCursorPositionChanged = false;
}
}
/*!
\internal
Initialises the QAbstractSpinBoxPrivate object.
*/
void QAbstractSpinBoxPrivate::init()
{
Q_Q(QAbstractSpinBox);
q->setLineEdit(new QLineEdit(q));
edit->setObjectName(QLatin1String("qt_spinbox_lineedit"));
validator = new QSpinBoxValidator(q, this);
edit->setValidator(validator);
QStyleOptionSpinBox opt;
q->initStyleOption(&opt);
spinClickTimerInterval = q->style()->styleHint(QStyle::SH_SpinBox_ClickAutoRepeatRate, &opt, q);
spinClickThresholdTimerInterval = q->style()->styleHint(QStyle::SH_SpinBox_ClickAutoRepeatThreshold, &opt, q);
q->setFocusPolicy(Qt::WheelFocus);
q->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed, QSizePolicy::SpinBox));
q->setAttribute(Qt::WA_InputMethodEnabled);
q->setAttribute(Qt::WA_MacShowFocusRect);
}
/*!
\internal
Resets the state of the spinbox. E.g. the state is set to
(Keyboard|Up) if Key up is currently pressed.
*/
void QAbstractSpinBoxPrivate::reset()
{
Q_Q(QAbstractSpinBox);
buttonState = None;
if (q) {
if (spinClickTimerId != -1)
q->killTimer(spinClickTimerId);
if (spinClickThresholdTimerId != -1)
q->killTimer(spinClickThresholdTimerId);
spinClickTimerId = spinClickThresholdTimerId = -1;
acceleration = 0;
q->update();
}
}
/*!
\internal
Updates the state of the spinbox.
*/
void QAbstractSpinBoxPrivate::updateState(bool up, bool fromKeyboard /* = false */)
{
Q_Q(QAbstractSpinBox);
if ((up && (buttonState & Up)) || (!up && (buttonState & Down)))
return;
reset();
if (q && (q->stepEnabled() & (up ? QAbstractSpinBox::StepUpEnabled
: QAbstractSpinBox::StepDownEnabled))) {
spinClickThresholdTimerId = q->startTimer(spinClickThresholdTimerInterval);
buttonState = (up ? Up : Down) | (fromKeyboard ? Keyboard : Mouse);
q->stepBy(up ? 1 : -1);
#ifndef QT_NO_ACCESSIBILITY
QAccessible::updateAccessibility(q, 0, QAccessible::ValueChanged);
#endif
}
}
/*!
Initialize \a option with the values from this QSpinBox. This method
is useful for subclasses when they need a QStyleOptionSpinBox, but don't want
to fill in all the information themselves.
\sa QStyleOption::initFrom()
*/
void QAbstractSpinBox::initStyleOption(QStyleOptionSpinBox *option) const
{
if (!option)
return;
Q_D(const QAbstractSpinBox);
option->initFrom(this);
option->activeSubControls = QStyle::SC_None;
option->buttonSymbols = d->buttonSymbols;
option->subControls = QStyle::SC_SpinBoxFrame | QStyle::SC_SpinBoxEditField;
if (d->buttonSymbols != QAbstractSpinBox::NoButtons) {
option->subControls |= QStyle::SC_SpinBoxUp | QStyle::SC_SpinBoxDown;
if (d->buttonState & Up) {
option->activeSubControls = QStyle::SC_SpinBoxUp;
} else if (d->buttonState & Down) {
option->activeSubControls = QStyle::SC_SpinBoxDown;
}
}
if (d->buttonState) {
option->state |= QStyle::State_Sunken;
} else {
option->activeSubControls = d->hoverControl;
}
option->stepEnabled = style()->styleHint(QStyle::SH_SpinControls_DisableOnBounds)
? stepEnabled()
: (QAbstractSpinBox::StepDownEnabled|QAbstractSpinBox::StepUpEnabled);
option->frame = d->frame;
}
/*!
\internal
Bounds \a val to be within minimum and maximum. Also tries to be
clever about setting it at min and max depending on what it was
and what direction it was changed etc.
*/
QVariant QAbstractSpinBoxPrivate::bound(const QVariant &val, const QVariant &old, int steps) const
{
QVariant v = val;
if (!wrapping || steps == 0 || old.isNull()) {
if (variantCompare(v, minimum) < 0) {
v = wrapping ? maximum : minimum;
}
if (variantCompare(v, maximum) > 0) {
v = wrapping ? minimum : maximum;
}
} else {
const bool wasMin = old == minimum;
const bool wasMax = old == maximum;
const int oldcmp = variantCompare(v, old);
const int maxcmp = variantCompare(v, maximum);
const int mincmp = variantCompare(v, minimum);
const bool wrapped = (oldcmp > 0 && steps < 0) || (oldcmp < 0 && steps > 0);
if (maxcmp > 0) {
v = ((wasMax && !wrapped && steps > 0) || (steps < 0 && !wasMin && wrapped))
? minimum : maximum;
} else if (wrapped && (maxcmp > 0 || mincmp < 0)) {
v = ((wasMax && steps > 0) || (!wasMin && steps < 0)) ? minimum : maximum;
} else if (mincmp < 0) {
v = (!wasMax && !wasMin ? minimum : maximum);
}
}
return v;
}
/*!
\internal
Sets the value of the spin box to \a val. Depending on the value
of \a ep it will also emit signals.
*/
void QAbstractSpinBoxPrivate::setValue(const QVariant &val, EmitPolicy ep,
bool doUpdate)
{
Q_Q(QAbstractSpinBox);
const QVariant old = value;
value = bound(val);
pendingEmit = false;
cleared = false;
if (doUpdate) {
updateEdit();
}
q->update();
if (ep == AlwaysEmit || (ep == EmitIfChanged && old != value)) {
emitSignals(ep, old);
}
}
/*!
\internal
Updates the line edit to reflect the current value of the spin box.
*/
void QAbstractSpinBoxPrivate::updateEdit()
{
Q_Q(QAbstractSpinBox);
if (type == QVariant::Invalid)
return;
const QString newText = specialValue() ? specialValueText : prefix + textFromValue(value) + suffix;
if (newText == edit->displayText() || cleared)
return;
const bool empty = edit->text().isEmpty();
int cursor = edit->cursorPosition();
int selsize = edit->selectedText().size();
const bool sb = edit->blockSignals(true);
edit->setText(newText);
if (!specialValue()) {
cursor = qBound(prefix.size(), cursor, edit->displayText().size() - suffix.size());
if (selsize > 0) {
edit->setSelection(cursor, selsize);
} else {
edit->setCursorPosition(empty ? prefix.size() : cursor);
}
}
edit->blockSignals(sb);
q->update();
}
/*!
\internal
Convenience function to set min/max values.
*/
void QAbstractSpinBoxPrivate::setRange(const QVariant &min, const QVariant &max)
{
Q_Q(QAbstractSpinBox);
clearCache();
minimum = min;
maximum = (variantCompare(min, max) < 0 ? max : min);
cachedSizeHint = QSize(); // minimumSizeHint doesn't care about min/max
reset();
if (!(bound(value) == value)) {
setValue(bound(value), EmitIfChanged);
} else if (value == minimum && !specialValueText.isEmpty()) {
updateEdit();
}
q->updateGeometry();
}
/*!
\internal
Convenience function to get a variant of the right type.
*/
QVariant QAbstractSpinBoxPrivate::getZeroVariant() const
{
QVariant ret;
switch (type) {
case QVariant::Int: ret = QVariant((int)0); break;
case QVariant::Double: ret = QVariant((double)0.0); break;
default: break;
}
return ret;
}
/*!
\internal
Virtual method called that calls the public textFromValue()
functions in the subclasses. Needed to change signature from
QVariant to int/double/QDateTime etc. Used when needing to display
a value textually.
This method is reimeplemented in the various subclasses.
*/
QString QAbstractSpinBoxPrivate::textFromValue(const QVariant &) const
{
return QString();
}
/*!
\internal
Virtual method called that calls the public valueFromText()
functions in the subclasses. Needed to change signature from
QVariant to int/double/QDateTime etc. Used when needing to
interpret a string as another type.
This method is reimeplemented in the various subclasses.
*/
QVariant QAbstractSpinBoxPrivate::valueFromText(const QString &) const
{
return QVariant();
}
/*!
\internal
Interprets text and emits signals. Called when the spinbox needs
to interpret the text on the lineedit.
*/
void QAbstractSpinBoxPrivate::interpret(EmitPolicy ep)
{
Q_Q(QAbstractSpinBox);
if (type == QVariant::Invalid || cleared)
return;
QVariant v = getZeroVariant();
bool doInterpret = true;
QString tmp = edit->displayText();
int pos = edit->cursorPosition();
const int oldpos = pos;
if (q->validate(tmp, pos) != QValidator::Acceptable) {
const QString copy = tmp;
q->fixup(tmp);
QASBDEBUG() << "QAbstractSpinBoxPrivate::interpret() text '"
<< edit->displayText()
<< "' >> '" << copy << '\''
<< "' >> '" << tmp << '\'';
doInterpret = tmp != copy && (q->validate(tmp, pos) == QValidator::Acceptable);
if (!doInterpret) {
v = (correctionMode == QAbstractSpinBox::CorrectToNearestValue
? variantBound(minimum, v, maximum) : value);
}
}
if (doInterpret) {
v = valueFromText(tmp);
}
clearCache();
setValue(v, ep, true);
if (oldpos != pos)
edit->setCursorPosition(pos);
}
void QAbstractSpinBoxPrivate::clearCache() const
{
cachedText.clear();
cachedValue.clear();
cachedState = QValidator::Acceptable;
}
// --- QSpinBoxValidator ---
/*!
\internal
Constructs a QSpinBoxValidator object
*/
QSpinBoxValidator::QSpinBoxValidator(QAbstractSpinBox *qp, QAbstractSpinBoxPrivate *dp)
: QValidator(qp), qptr(qp), dptr(dp)
{
setObjectName(QLatin1String("qt_spinboxvalidator"));
}
/*!
\internal
Checks for specialValueText, prefix, suffix and calls
the virtual QAbstractSpinBox::validate function.
*/
QValidator::State QSpinBoxValidator::validate(QString &input, int &pos) const
{
if (dptr->specialValueText.size() > 0 && input == dptr->specialValueText)
return QValidator::Acceptable;
if (!dptr->prefix.isEmpty() && !input.startsWith(dptr->prefix)) {
input.prepend(dptr->prefix);
pos += dptr->prefix.length();
}
if (!dptr->suffix.isEmpty() && !input.endsWith(dptr->suffix))
input.append(dptr->suffix);
return qptr->validate(input, pos);
}
/*!
\internal
Calls the virtual QAbstractSpinBox::fixup function.
*/
void QSpinBoxValidator::fixup(QString &input) const
{
qptr->fixup(input);
}
// --- global ---
/*!
\internal
Adds two variants together and returns the result.
*/
QVariant operator+(const QVariant &arg1, const QVariant &arg2)
{
QVariant ret;
if (arg1.type() != arg2.type())
qWarning("QAbstractSpinBox: Internal error: Different types (%s vs %s) (%s:%d)",
arg1.typeName(), arg2.typeName(), __FILE__, __LINE__);
switch (arg1.type()) {
case QVariant::Int: ret = QVariant(arg1.toInt() + arg2.toInt()); break;
case QVariant::Double: ret = QVariant(arg1.toDouble() + arg2.toDouble()); break;
case QVariant::DateTime: {
QDateTime a2 = arg2.toDateTime();
QDateTime a1 = arg1.toDateTime().addDays(QDATETIMEEDIT_DATETIME_MIN.daysTo(a2));
a1.setTime(a1.time().addMSecs(QTime().msecsTo(a2.time())));
ret = QVariant(a1);
}
default: break;
}
return ret;
}
/*!
\internal
Subtracts two variants and returns the result.
*/
QVariant operator-(const QVariant &arg1, const QVariant &arg2)
{
QVariant ret;
if (arg1.type() != arg2.type())
qWarning("QAbstractSpinBox: Internal error: Different types (%s vs %s) (%s:%d)",
arg1.typeName(), arg2.typeName(), __FILE__, __LINE__);
switch (arg1.type()) {
case QVariant::Int: ret = QVariant(arg1.toInt() - arg2.toInt()); break;
case QVariant::Double: ret = QVariant(arg1.toDouble() - arg2.toDouble()); break;
case QVariant::DateTime: {
QDateTime a1 = arg1.toDateTime();
QDateTime a2 = arg2.toDateTime();
int days = a2.daysTo(a1);
int secs = a2.secsTo(a1);
int msecs = qMax(0, a1.time().msec() - a2.time().msec());
if (days < 0 || secs < 0 || msecs < 0) {
ret = arg1;
} else {
QDateTime dt = a2.addDays(days).addSecs(secs);
if (msecs > 0)
dt.setTime(dt.time().addMSecs(msecs));
ret = QVariant(dt);
}
}
default: break;
}
return ret;
}
/*!
\internal
Multiplies \a arg1 by \a multiplier and returns the result.
*/
QVariant operator*(const QVariant &arg1, double multiplier)
{
QVariant ret;
switch (arg1.type()) {
case QVariant::Int: ret = QVariant((int)(arg1.toInt() * multiplier)); break;
case QVariant::Double: ret = QVariant(arg1.toDouble() * multiplier); break;
case QVariant::DateTime: {
double days = QDATETIMEEDIT_DATE_MIN.daysTo(arg1.toDateTime().date()) * multiplier;
int daysInt = (int)days;
days -= daysInt;
long msecs = (long)((QDATETIMEEDIT_TIME_MIN.msecsTo(arg1.toDateTime().time()) * multiplier)
+ (days * (24 * 3600 * 1000)));
ret = QDateTime(QDate().addDays(int(days)), QTime().addMSecs(msecs));
break;
}
default: ret = arg1; break;
}
return ret;
}
double operator/(const QVariant &arg1, const QVariant &arg2)
{
double a1 = 0;
double a2 = 0;
switch (arg1.type()) {
case QVariant::Int:
a1 = (double)arg1.toInt();
a2 = (double)arg2.toInt();
break;
case QVariant::Double:
a1 = arg1.toDouble();
a2 = arg2.toDouble();
break;
case QVariant::DateTime:
a1 = QDATETIMEEDIT_DATE_MIN.daysTo(arg1.toDate());
a2 = QDATETIMEEDIT_DATE_MIN.daysTo(arg2.toDate());
a1 += (double)QDATETIMEEDIT_TIME_MIN.msecsTo(arg1.toDateTime().time()) / (long)(3600 * 24 * 1000);
a2 += (double)QDATETIMEEDIT_TIME_MIN.msecsTo(arg2.toDateTime().time()) / (long)(3600 * 24 * 1000);
default: break;
}
return (a1 != 0 && a2 != 0) ? (a1 / a2) : 0.0;
}
int QAbstractSpinBoxPrivate::variantCompare(const QVariant &arg1, const QVariant &arg2)
{
switch (arg2.type()) {
case QVariant::Date:
Q_ASSERT_X(arg1.type() == QVariant::Date, "QAbstractSpinBoxPrivate::variantCompare",
qPrintable(QString::fromAscii("Internal error 1 (%1)").
arg(QString::fromAscii(arg1.typeName()))));
if (arg1.toDate() == arg2.toDate()) {
return 0;
} else if (arg1.toDate() < arg2.toDate()) {
return -1;
} else {
return 1;
}
case QVariant::Time:
Q_ASSERT_X(arg1.type() == QVariant::Time, "QAbstractSpinBoxPrivate::variantCompare",
qPrintable(QString::fromAscii("Internal error 2 (%1)").
arg(QString::fromAscii(arg1.typeName()))));
if (arg1.toTime() == arg2.toTime()) {
return 0;
} else if (arg1.toTime() < arg2.toTime()) {
return -1;
} else {
return 1;
}
case QVariant::DateTime:
if (arg1.toDateTime() == arg2.toDateTime()) {
return 0;
} else if (arg1.toDateTime() < arg2.toDateTime()) {
return -1;
} else {
return 1;
}
case QVariant::Int:
if (arg1.toInt() == arg2.toInt()) {
return 0;
} else if (arg1.toInt() < arg2.toInt()) {
return -1;
} else {
return 1;
}
case QVariant::Double:
if (arg1.toDouble() == arg2.toDouble()) {
return 0;
} else if (arg1.toDouble() < arg2.toDouble()) {
return -1;
} else {
return 1;
}
case QVariant::Invalid:
if (arg2.type() == QVariant::Invalid)
return 0;
default:
Q_ASSERT_X(0, "QAbstractSpinBoxPrivate::variantCompare",
qPrintable(QString::fromAscii("Internal error 3 (%1 %2)").
arg(QString::fromAscii(arg1.typeName())).
arg(QString::fromAscii(arg2.typeName()))));
}
return -2;
}
QVariant QAbstractSpinBoxPrivate::variantBound(const QVariant &min,
const QVariant &value,
const QVariant &max)
{
Q_ASSERT(variantCompare(min, max) <= 0);
if (variantCompare(min, value) < 0) {
const int compMax = variantCompare(value, max);
return (compMax < 0 ? value : max);
} else {
return min;
}
}
QT_END_NAMESPACE
#include "moc_qabstractspinbox.cpp"
#endif // QT_NO_SPINBOX