diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2009-03-23 09:18:55 (GMT) |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2009-03-23 09:18:55 (GMT) |
commit | e5fcad302d86d316390c6b0f62759a067313e8a9 (patch) | |
tree | c2afbf6f1066b6ce261f14341cf6d310e5595bc1 /src/gui/widgets/qlineedit.cpp | |
download | Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.zip Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.gz Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.bz2 |
Long live Qt 4.5!
Diffstat (limited to 'src/gui/widgets/qlineedit.cpp')
-rw-r--r-- | src/gui/widgets/qlineedit.cpp | 3696 |
1 files changed, 3696 insertions, 0 deletions
diff --git a/src/gui/widgets/qlineedit.cpp b/src/gui/widgets/qlineedit.cpp new file mode 100644 index 0000000..b03df9e --- /dev/null +++ b/src/gui/widgets/qlineedit.cpp @@ -0,0 +1,3696 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qlineedit.h" +#include "qlineedit_p.h" + +#ifndef QT_NO_LINEEDIT +#include "qaction.h" +#include "qapplication.h" +#include "qclipboard.h" +#include "qdrag.h" +#include "qdrawutil.h" +#include "qevent.h" +#include "qfontmetrics.h" +#include "qmenu.h" +#include "qpainter.h" +#include "qpixmap.h" +#include "qpointer.h" +#include "qstringlist.h" +#include "qstyle.h" +#include "qstyleoption.h" +#include "qtimer.h" +#include "qvalidator.h" +#include "qvariant.h" +#include "qvector.h" +#include "qwhatsthis.h" +#include "qdebug.h" +#include "qtextedit.h" +#include <private/qtextedit_p.h> +#ifndef QT_NO_ACCESSIBILITY +#include "qaccessible.h" +#endif +#ifndef QT_NO_IM +#include "qinputcontext.h" +#include "qlist.h" +#endif +#include "qabstractitemview.h" +#include "private/qstylesheetstyle_p.h" + +#ifndef QT_NO_SHORTCUT +#include "private/qapplication_p.h" +#include "private/qshortcutmap_p.h" +#include "qkeysequence.h" +#define ACCEL_KEY(k) (!qApp->d_func()->shortcutMap.hasShortcutForKeySequence(k) ? QLatin1String("\t") + QString(QKeySequence(k)) : QString()) +#else +#define ACCEL_KEY(k) QString() +#endif + +#include <limits.h> + +#define verticalMargin 1 +#define horizontalMargin 2 + +QT_BEGIN_NAMESPACE + +#ifdef Q_WS_MAC +extern void qt_mac_secure_keyboard(bool); //qapplication_mac.cpp +#endif + +static inline bool shouldEnableInputMethod(QLineEdit *lineedit) +{ + const QLineEdit::EchoMode mode = lineedit->echoMode(); + return !lineedit->isReadOnly() && (mode == QLineEdit::Normal || mode == QLineEdit::PasswordEchoOnEdit); +} + +/*! + Initialize \a option with the values from this QLineEdit. This method + is useful for subclasses when they need a QStyleOptionFrame or QStyleOptionFrameV2, but don't want + to fill in all the information themselves. This function will check the version + of the QStyleOptionFrame and fill in the additional values for a + QStyleOptionFrameV2. + + \sa QStyleOption::initFrom() +*/ +void QLineEdit::initStyleOption(QStyleOptionFrame *option) const +{ + if (!option) + return; + + Q_D(const QLineEdit); + option->initFrom(this); + option->rect = contentsRect(); + option->lineWidth = d->frame ? style()->pixelMetric(QStyle::PM_DefaultFrameWidth, option, this) + : 0; + option->midLineWidth = 0; + option->state |= QStyle::State_Sunken; + if (d->readOnly) + option->state |= QStyle::State_ReadOnly; +#ifdef QT_KEYPAD_NAVIGATION + if (hasEditFocus()) + option->state |= QStyle::State_HasEditFocus; +#endif + if (QStyleOptionFrameV2 *optionV2 = qstyleoption_cast<QStyleOptionFrameV2 *>(option)) + optionV2->features = QStyleOptionFrameV2::None; +} + +/*! + \class QLineEdit + \brief The QLineEdit widget is a one-line text editor. + + \ingroup basicwidgets + \mainclass + + A line edit allows the user to enter and edit a single line of + plain text with a useful collection of editing functions, + including undo and redo, cut and paste, and drag and drop. + + By changing the echoMode() of a line edit, it can also be used as + a "write-only" field, for inputs such as passwords. + + The length of the text can be constrained to maxLength(). The text + can be arbitrarily constrained using a validator() or an + inputMask(), or both. + + A related class is QTextEdit which allows multi-line, rich text + editing. + + You can change the text with setText() or insert(). The text is + retrieved with text(); the displayed text (which may be different, + see \l{EchoMode}) is retrieved with displayText(). Text can be + selected with setSelection() or selectAll(), and the selection can + be cut(), copy()ied and paste()d. The text can be aligned with + setAlignment(). + + When the text changes the textChanged() signal is emitted; when + the text changes other than by calling setText() the textEdited() + signal is emitted; when the cursor is moved the + cursorPositionChanged() signal is emitted; and when the Return or + Enter key is pressed the returnPressed() signal is emitted. + + When editing is finished, either because the line edit lost focus + or Return/Enter is pressed the editingFinished() signal is + emitted. + + Note that if there is a validator set on the line edit, the + returnPressed()/editingFinished() signals will only be emitted if + the validator returns QValidator::Acceptable. + + By default, QLineEdits have a frame as specified by the Windows + and Motif style guides; you can turn it off by calling + setFrame(false). + + The default key bindings are described below. The line edit also + provides a context menu (usually invoked by a right mouse click) + that presents some of these editing options. + \target desc + \table + \header \i Keypress \i Action + \row \i Left Arrow \i Moves the cursor one character to the left. + \row \i Shift+Left Arrow \i Moves and selects text one character to the left. + \row \i Right Arrow \i Moves the cursor one character to the right. + \row \i Shift+Right Arrow \i Moves and selects text one character to the right. + \row \i Home \i Moves the cursor to the beginning of the line. + \row \i End \i Moves the cursor to the end of the line. + \row \i Backspace \i Deletes the character to the left of the cursor. + \row \i Ctrl+Backspace \i Deletes the word to the left of the cursor. + \row \i Delete \i Deletes the character to the right of the cursor. + \row \i Ctrl+Delete \i Deletes the word to the right of the cursor. + \row \i Ctrl+A \i Select all. + \row \i Ctrl+C \i Copies the selected text to the clipboard. + \row \i Ctrl+Insert \i Copies the selected text to the clipboard. + \row \i Ctrl+K \i Deletes to the end of the line. + \row \i Ctrl+V \i Pastes the clipboard text into line edit. + \row \i Shift+Insert \i Pastes the clipboard text into line edit. + \row \i Ctrl+X \i Deletes the selected text and copies it to the clipboard. + \row \i Shift+Delete \i Deletes the selected text and copies it to the clipboard. + \row \i Ctrl+Z \i Undoes the last operation. + \row \i Ctrl+Y \i Redoes the last undone operation. + \endtable + + Any other key sequence that represents a valid character, will + cause the character to be inserted into the line edit. + + \table 100% + \row \o \inlineimage macintosh-lineedit.png Screenshot of a Macintosh style line edit + \o A line edit shown in the \l{Macintosh Style Widget Gallery}{Macintosh widget style}. + \row \o \inlineimage windows-lineedit.png Screenshot of a Windows XP style line edit + \o A line edit shown in the \l{Windows XP Style Widget Gallery}{Windows XP widget style}. + \row \o \inlineimage plastique-lineedit.png Screenshot of a Plastique style line edit + \o A line edit shown in the \l{Plastique Style Widget Gallery}{Plastique widget style}. + \endtable + + \sa QTextEdit, QLabel, QComboBox, {fowler}{GUI Design Handbook: Field, Entry}, {Line Edits Example} +*/ + + +/*! + \fn void QLineEdit::textChanged(const QString &text) + + This signal is emitted whenever the text changes. The \a text + argument is the new text. + + Unlike textEdited(), this signal is also emitted when the text is + changed programmatically, for example, by calling setText(). +*/ + +/*! + \fn void QLineEdit::textEdited(const QString &text) + + This signal is emitted whenever the text is edited. The \a text + argument is the next text. + + Unlike textChanged(), this signal is not emitted when the text is + changed programmatically, for example, by calling setText(). +*/ + +/*! + \fn void QLineEdit::cursorPositionChanged(int old, int new) + + This signal is emitted whenever the cursor moves. The previous + position is given by \a old, and the new position by \a new. + + \sa setCursorPosition(), cursorPosition() +*/ + +/*! + \fn void QLineEdit::selectionChanged() + + This signal is emitted whenever the selection changes. + + \sa hasSelectedText(), selectedText() +*/ + +/*! + Constructs a line edit with no text. + + The maximum text length is set to 32767 characters. + + The \a parent argument is sent to the QWidget constructor. + + \sa setText(), setMaxLength() +*/ +QLineEdit::QLineEdit(QWidget* parent) + : QWidget(*new QLineEditPrivate, parent,0) +{ + Q_D(QLineEdit); + d->init(QString()); +} + +/*! + Constructs a line edit containing the text \a contents. + + The cursor position is set to the end of the line and the maximum + text length to 32767 characters. + + The \a parent and argument is sent to the QWidget + constructor. + + \sa text(), setMaxLength() +*/ +QLineEdit::QLineEdit(const QString& contents, QWidget* parent) + : QWidget(*new QLineEditPrivate, parent, 0) +{ + Q_D(QLineEdit); + d->init(contents); +} + + +#ifdef QT3_SUPPORT +/*! + Constructs a line edit with no text. + + The maximum text length is set to 32767 characters. + + The \a parent and \a name arguments are sent to the QWidget constructor. + + \sa setText(), setMaxLength() +*/ +QLineEdit::QLineEdit(QWidget* parent, const char* name) + : QWidget(*new QLineEditPrivate, parent,0) +{ + Q_D(QLineEdit); + setObjectName(QString::fromAscii(name)); + d->init(QString()); +} + +/*! + Constructs a line edit containing the text \a contents. + + The cursor position is set to the end of the line and the maximum + text length to 32767 characters. + + The \a parent and \a name arguments are sent to the QWidget + constructor. + + \sa text(), setMaxLength() +*/ + +QLineEdit::QLineEdit(const QString& contents, QWidget* parent, const char* name) + : QWidget(*new QLineEditPrivate, parent, 0) +{ + Q_D(QLineEdit); + setObjectName(QString::fromAscii(name)); + d->init(contents); +} + +/*! + Constructs a line edit with an input \a inputMask and the text \a + contents. + + The cursor position is set to the end of the line and the maximum + text length is set to the length of the mask (the number of mask + characters and separators). + + The \a parent and \a name arguments are sent to the QWidget + constructor. + + \sa setMask() text() +*/ +QLineEdit::QLineEdit(const QString& contents, const QString &inputMask, QWidget* parent, const char* name) + : QWidget(*new QLineEditPrivate, parent, 0) +{ + Q_D(QLineEdit); + setObjectName(QString::fromAscii(name)); + d->parseInputMask(inputMask); + if (d->maskData) { + QString ms = d->maskString(0, contents); + d->init(ms + d->clearString(ms.length(), d->maxLength - ms.length())); + d->cursor = d->nextMaskBlank(ms.length()); + } else { + d->init(contents); + } +} +#endif + +/*! + Destroys the line edit. +*/ + +QLineEdit::~QLineEdit() +{ +} + + +/*! + \property QLineEdit::text + \brief the line edit's text + + Setting this property clears the selection, clears the undo/redo + history, moves the cursor to the end of the line and resets the + \l modified property to false. The text is not validated when + inserted with setText(). + + The text is truncated to maxLength() length. + + By default, this property contains an empty string. + + \sa insert(), clear() +*/ +QString QLineEdit::text() const +{ + Q_D(const QLineEdit); + QString res = d->text; + if (d->maskData) + res = d->stripString(d->text); + return (res.isNull() ? QString::fromLatin1("") : res); +} + +void QLineEdit::setText(const QString& text) +{ + Q_D(QLineEdit); + d->setText(text, -1, false); +#ifdef QT_KEYPAD_NAVIGATION + d->origText = d->text; +#endif +} + + +/*! + \property QLineEdit::displayText + \brief the displayed text + + If \l echoMode is \l Normal this returns the same as text(); if + \l EchoMode is \l Password or \l PasswordEchoOnEdit it returns a string of asterisks + text().length() characters long, e.g. "******"; if \l EchoMode is + \l NoEcho returns an empty string, "". + + By default, this property contains an empty string. + + \sa setEchoMode() text() EchoMode +*/ + +QString QLineEdit::displayText() const +{ + Q_D(const QLineEdit); + if (d->echoMode == NoEcho) + return QString::fromLatin1(""); + QString res = d->text; + + if (d->echoMode == Password || (d->echoMode == PasswordEchoOnEdit + && !d->passwordEchoEditing)) { + QStyleOptionFrameV2 opt; + initStyleOption(&opt); + res.fill(style()->styleHint(QStyle::SH_LineEdit_PasswordCharacter, &opt, this)); + } + return (res.isNull() ? QString::fromLatin1("") : res); +} + + +/*! + \property QLineEdit::maxLength + \brief the maximum permitted length of the text + + If the text is too long, it is truncated at the limit. + + If truncation occurs any selected text will be unselected, the + cursor position is set to 0 and the first part of the string is + shown. + + If the line edit has an input mask, the mask defines the maximum + string length. + + By default, this property contains a value of 32767. + + \sa inputMask +*/ + +int QLineEdit::maxLength() const +{ + Q_D(const QLineEdit); + return d->maxLength; +} + +void QLineEdit::setMaxLength(int maxLength) +{ + Q_D(QLineEdit); + if (d->maskData) + return; + d->maxLength = maxLength; + setText(d->text); +} + + + +/*! + \property QLineEdit::frame + \brief whether the line edit draws itself with a frame + + If enabled (the default) the line edit draws itself inside a + frame, otherwise the line edit draws itself without any frame. +*/ +bool QLineEdit::hasFrame() const +{ + Q_D(const QLineEdit); + return d->frame; +} + + +void QLineEdit::setFrame(bool enable) +{ + Q_D(QLineEdit); + d->frame = enable; + update(); + updateGeometry(); +} + + +/*! + \enum QLineEdit::EchoMode + + This enum type describes how a line edit should display its + contents. + + \value Normal Display characters as they are entered. This is the + default. + \value NoEcho Do not display anything. This may be appropriate + for passwords where even the length of the + password should be kept secret. + \value Password Display asterisks instead of the characters + actually entered. + \value PasswordEchoOnEdit Display characters as they are entered + while editing otherwise display asterisks. + + \sa setEchoMode() echoMode() +*/ + + +/*! + \property QLineEdit::echoMode + \brief the line edit's echo mode + + The echo mode determines how the text entered in the line edit is + displayed (or echoed) to the user. + + The most common setting is \l Normal, in which the text entered by the + user is displayed verbatim, but QLineEdit also supports modes that allow + the entered text to be suppressed or obscured: these include \l NoEcho, + \l Password and \l PasswordEchoOnEdit. + + The widget's display and the ability to copy or drag the text is + affected by this setting. + + By default, this property is set to \l Normal. + + \sa EchoMode displayText() +*/ + +QLineEdit::EchoMode QLineEdit::echoMode() const +{ + Q_D(const QLineEdit); + return (EchoMode) d->echoMode; +} + +void QLineEdit::setEchoMode(EchoMode mode) +{ + Q_D(QLineEdit); + if (mode == (EchoMode)d->echoMode) + return; + setAttribute(Qt::WA_InputMethodEnabled, shouldEnableInputMethod(this)); + d->echoMode = mode; + d->passwordEchoEditing = false; + d->updateTextLayout(); + update(); +#ifdef Q_WS_MAC + if (hasFocus()) + qt_mac_secure_keyboard(d->echoMode == Password || d->echoMode == NoEcho); +#endif +} + + +#ifndef QT_NO_VALIDATOR +/*! + Returns a pointer to the current input validator, or 0 if no + validator has been set. + + \sa setValidator() +*/ + +const QValidator * QLineEdit::validator() const +{ + Q_D(const QLineEdit); + return d->validator; +} + +/*! + Sets this line edit to only accept input that the validator, \a v, + will accept. This allows you to place any arbitrary constraints on + the text which may be entered. + + If \a v == 0, setValidator() removes the current input validator. + The initial setting is to have no input validator (i.e. any input + is accepted up to maxLength()). + + \sa validator() QIntValidator QDoubleValidator QRegExpValidator +*/ + +void QLineEdit::setValidator(const QValidator *v) +{ + Q_D(QLineEdit); + d->validator = const_cast<QValidator*>(v); +} +#endif // QT_NO_VALIDATOR + +#ifndef QT_NO_COMPLETER +/*! + \since 4.2 + + Sets this line edit to provide auto completions from the completer, \a c. + The completion mode is set using QCompleter::setCompletionMode(). + + To use a QCompleter with a QValidator or QLineEdit::inputMask, you need to + ensure that the model provided to QCompleter contains valid entries. You can + use the QSortFilterProxyModel to ensure that the QCompleter's model contains + only valid entries. + + If \a c == 0, setCompleter() removes the current completer, effectively + disabling auto completion. + + \sa QCompleter +*/ +void QLineEdit::setCompleter(QCompleter *c) +{ + Q_D(QLineEdit); + if (c == d->completer) + return; + if (d->completer) { + disconnect(d->completer, 0, this, 0); + d->completer->setWidget(0); + if (d->completer->parent() == this) + delete d->completer; + } + d->completer = c; + if (!c) + return; + if (c->widget() == 0) + c->setWidget(this); + if (hasFocus()) { + QObject::connect(d->completer, SIGNAL(activated(QString)), + this, SLOT(setText(QString))); + QObject::connect(d->completer, SIGNAL(highlighted(QString)), + this, SLOT(_q_completionHighlighted(QString))); + } +} + +/*! + \since 4.2 + + Returns the current QCompleter that provides completions. +*/ +QCompleter *QLineEdit::completer() const +{ + Q_D(const QLineEdit); + return d->completer; +} + +// looks for an enabled item iterating forward(dir=1)/backward(dir=-1) from the +// current row based. dir=0 indicates a new completion prefix was set. +bool QLineEditPrivate::advanceToEnabledItem(int dir) +{ + int start = completer->currentRow(); + if (start == -1) + return false; + int i = start + dir; + if (dir == 0) dir = 1; + do { + if (!completer->setCurrentRow(i)) { + if (!completer->wrapAround()) + break; + i = i > 0 ? 0 : completer->completionCount() - 1; + } else { + QModelIndex currentIndex = completer->currentIndex(); + if (completer->completionModel()->flags(currentIndex) & Qt::ItemIsEnabled) + return true; + i += dir; + } + } while (i != start); + + completer->setCurrentRow(start); // restore + return false; +} + +void QLineEditPrivate::complete(int key) +{ + if (!completer || readOnly || echoMode != QLineEdit::Normal) + return; + + if (completer->completionMode() == QCompleter::InlineCompletion) { + if (key == Qt::Key_Backspace) + return; + int n = 0; + if (key == Qt::Key_Up || key == Qt::Key_Down) { + if (selend != 0 && selend != text.length()) + return; + QString prefix = hasSelectedText() ? text.left(selstart) : text; + if (text.compare(completer->currentCompletion(), completer->caseSensitivity()) != 0 + || prefix.compare(completer->completionPrefix(), completer->caseSensitivity()) != 0) { + completer->setCompletionPrefix(prefix); + } else { + n = (key == Qt::Key_Up) ? -1 : +1; + } + } else { + completer->setCompletionPrefix(text); + } + if (!advanceToEnabledItem(n)) + return; + } else { +#ifndef QT_KEYPAD_NAVIGATION + if (text.isEmpty()) { + completer->popup()->hide(); + return; + } +#endif + completer->setCompletionPrefix(text); + } + + completer->complete(); +} + +void QLineEditPrivate::_q_completionHighlighted(QString newText) +{ + Q_Q(QLineEdit); + if (completer->completionMode() != QCompleter::InlineCompletion) + q->setText(newText); + else { + int c = cursor; + q->setText(text.left(c) + newText.mid(c)); + q->setSelection(text.length(), c - newText.length()); + } +} +#endif // QT_NO_COMPLETER + +/*! + Returns a recommended size for the widget. + + The width returned, in pixels, is usually enough for about 15 to + 20 characters. +*/ + +QSize QLineEdit::sizeHint() const +{ + Q_D(const QLineEdit); + ensurePolished(); + QFontMetrics fm(font()); + int h = qMax(fm.lineSpacing(), 14) + 2*verticalMargin + + d->topTextMargin + d->bottomTextMargin + + d->topmargin + d->bottommargin; + int w = fm.width(QLatin1Char('x')) * 17 + 2*horizontalMargin + + d->leftTextMargin + d->rightTextMargin + + d->leftmargin + d->rightmargin; // "some" + QStyleOptionFrameV2 opt; + initStyleOption(&opt); + return (style()->sizeFromContents(QStyle::CT_LineEdit, &opt, QSize(w, h). + expandedTo(QApplication::globalStrut()), this)); +} + + +/*! + Returns a minimum size for the line edit. + + The width returned is enough for at least one character. +*/ + +QSize QLineEdit::minimumSizeHint() const +{ + Q_D(const QLineEdit); + ensurePolished(); + QFontMetrics fm = fontMetrics(); + int h = fm.height() + qMax(2*verticalMargin, fm.leading()) + + d->topmargin + d->bottommargin; + int w = fm.maxWidth() + d->leftmargin + d->rightmargin; + QStyleOptionFrameV2 opt; + initStyleOption(&opt); + return (style()->sizeFromContents(QStyle::CT_LineEdit, &opt, QSize(w, h). + expandedTo(QApplication::globalStrut()), this)); +} + + +/*! + \property QLineEdit::cursorPosition + \brief the current cursor position for this line edit + + Setting the cursor position causes a repaint when appropriate. + + By default, this property contains a value of 0. +*/ + +int QLineEdit::cursorPosition() const +{ + Q_D(const QLineEdit); + return d->cursor; +} + +void QLineEdit::setCursorPosition(int pos) +{ + Q_D(QLineEdit); + if (pos < 0) + pos = 0; + + if (pos <= d->text.length()) + d->moveCursor(pos); +} + +/*! + Returns the cursor position under the point \a pos. +*/ +// ### What should this do if the point is outside of contentsRect? Currently returns 0. +int QLineEdit::cursorPositionAt(const QPoint &pos) +{ + Q_D(QLineEdit); + return d->xToPos(pos.x()); +} + + +#ifdef QT3_SUPPORT +/*! \obsolete + + Use setText(), setCursorPosition() and setSelection() instead. +*/ +bool QLineEdit::validateAndSet(const QString &newText, int newPos, + int newMarkAnchor, int newMarkDrag) +{ + Q_D(QLineEdit); + int priorState = d->undoState; + d->selstart = 0; + d->selend = d->text.length(); + d->removeSelectedText(); + d->insert(newText); + d->finishChange(priorState); + if (d->undoState > priorState) { + d->cursor = newPos; + d->selstart = qMin(newMarkAnchor, newMarkDrag); + d->selend = qMax(newMarkAnchor, newMarkDrag); + update(); + d->emitCursorPositionChanged(); + return true; + } + return false; +} +#endif //QT3_SUPPORT + +/*! + \property QLineEdit::alignment + \brief the alignment of the line edit + + Both horizontal and vertical alignment is allowed here, Qt::AlignJustify + will map to Qt::AlignLeft. + + By default, this property contains a combination of Qt::AlignLeft and Qt::AlignVCenter. + + \sa Qt::Alignment +*/ + +Qt::Alignment QLineEdit::alignment() const +{ + Q_D(const QLineEdit); + return QFlag(d->alignment); +} + +void QLineEdit::setAlignment(Qt::Alignment alignment) +{ + Q_D(QLineEdit); + d->alignment = alignment; + update(); +} + + +/*! + Moves the cursor forward \a steps characters. If \a mark is true + each character moved over is added to the selection; if \a mark is + false the selection is cleared. + + \sa cursorBackward() +*/ + +void QLineEdit::cursorForward(bool mark, int steps) +{ + Q_D(QLineEdit); + int cursor = d->cursor; + if (steps > 0) { + while(steps--) + cursor = d->textLayout.nextCursorPosition(cursor); + } else if (steps < 0) { + while (steps++) + cursor = d->textLayout.previousCursorPosition(cursor); + } + d->moveCursor(cursor, mark); +} + + +/*! + Moves the cursor back \a steps characters. If \a mark is true each + character moved over is added to the selection; if \a mark is + false the selection is cleared. + + \sa cursorForward() +*/ +void QLineEdit::cursorBackward(bool mark, int steps) +{ + cursorForward(mark, -steps); +} + +/*! + Moves the cursor one word forward. If \a mark is true, the word is + also selected. + + \sa cursorWordBackward() +*/ +void QLineEdit::cursorWordForward(bool mark) +{ + Q_D(QLineEdit); + d->moveCursor(d->textLayout.nextCursorPosition(d->cursor, QTextLayout::SkipWords), mark); +} + +/*! + Moves the cursor one word backward. If \a mark is true, the word + is also selected. + + \sa cursorWordForward() +*/ + +void QLineEdit::cursorWordBackward(bool mark) +{ + Q_D(QLineEdit); + d->moveCursor(d->textLayout.previousCursorPosition(d->cursor, QTextLayout::SkipWords), mark); +} + + +/*! + If no text is selected, deletes the character to the left of the + text cursor and moves the cursor one position to the left. If any + text is selected, the cursor is moved to the beginning of the + selected text and the selected text is deleted. + + \sa del() +*/ +void QLineEdit::backspace() +{ + Q_D(QLineEdit); + int priorState = d->undoState; + if (d->hasSelectedText()) { + d->removeSelectedText(); + } else if (d->cursor) { + --d->cursor; + if (d->maskData) + d->cursor = d->prevMaskBlank(d->cursor); + QChar uc = d->text.at(d->cursor); + if (d->cursor > 0 && uc.unicode() >= 0xdc00 && uc.unicode() < 0xe000) { + // second half of a surrogate, check if we have the first half as well, + // if yes delete both at once + uc = d->text.at(d->cursor - 1); + if (uc.unicode() >= 0xd800 && uc.unicode() < 0xdc00) { + d->del(true); + --d->cursor; + } + } + d->del(true); + } + d->finishChange(priorState); +} + +/*! + If no text is selected, deletes the character to the right of the + text cursor. If any text is selected, the cursor is moved to the + beginning of the selected text and the selected text is deleted. + + \sa backspace() +*/ + +void QLineEdit::del() +{ + Q_D(QLineEdit); + int priorState = d->undoState; + if (d->hasSelectedText()) { + d->removeSelectedText(); + } else { + int n = d->textLayout.nextCursorPosition(d->cursor) - d->cursor; + while (n--) + d->del(); + } + d->finishChange(priorState); +} + +/*! + Moves the text cursor to the beginning of the line unless it is + already there. If \a mark is true, text is selected towards the + first position; otherwise, any selected text is unselected if the + cursor is moved. + + \sa end() +*/ + +void QLineEdit::home(bool mark) +{ + Q_D(QLineEdit); + d->moveCursor(0, mark); +} + +/*! + Moves the text cursor to the end of the line unless it is already + there. If \a mark is true, text is selected towards the last + position; otherwise, any selected text is unselected if the cursor + is moved. + + \sa home() +*/ + +void QLineEdit::end(bool mark) +{ + Q_D(QLineEdit); + d->moveCursor(d->text.length(), mark); +} + + +/*! + \property QLineEdit::modified + \brief whether the line edit's contents has been modified by the user + + The modified flag is never read by QLineEdit; it has a default value + of false and is changed to true whenever the user changes the line + edit's contents. + + This is useful for things that need to provide a default value but + do not start out knowing what the default should be (perhaps it + depends on other fields on the form). Start the line edit without + the best default, and when the default is known, if modified() + returns false (the user hasn't entered any text), insert the + default value. + + Calling setText() resets the modified flag to false. +*/ + +bool QLineEdit::isModified() const +{ + Q_D(const QLineEdit); + return d->modifiedState != d->undoState; +} + +void QLineEdit::setModified(bool modified) +{ + Q_D(QLineEdit); + if (modified) + d->modifiedState = -1; + else + d->modifiedState = d->undoState; +} + + +/*!\fn QLineEdit::clearModified() + +Use setModified(false) instead. + + \sa isModified() +*/ + + +/*! + \property QLineEdit::hasSelectedText + \brief whether there is any text selected + + hasSelectedText() returns true if some or all of the text has been + selected by the user; otherwise returns false. + + By default, this property is false. + + \sa selectedText() +*/ + + +bool QLineEdit::hasSelectedText() const +{ + Q_D(const QLineEdit); + return d->hasSelectedText(); +} + +/*! + \property QLineEdit::selectedText + \brief the selected text + + If there is no selected text this property's value is + an empty string. + + By default, this property contains an empty string. + + \sa hasSelectedText() +*/ + +QString QLineEdit::selectedText() const +{ + Q_D(const QLineEdit); + if (d->hasSelectedText()) + return d->text.mid(d->selstart, d->selend - d->selstart); + return QString(); +} + +/*! + selectionStart() returns the index of the first selected character in the + line edit or -1 if no text is selected. + + \sa selectedText() +*/ + +int QLineEdit::selectionStart() const +{ + Q_D(const QLineEdit); + return d->hasSelectedText() ? d->selstart : -1; +} + + +#ifdef QT3_SUPPORT + +/*! + \fn void QLineEdit::lostFocus() + + This signal is emitted when the line edit has lost focus. + + Use editingFinished() instead + \sa editingFinished(), returnPressed() +*/ + +/*! + Use isModified() instead. +*/ +bool QLineEdit::edited() const { return isModified(); } +/*! + Use setModified() or setText(). +*/ +void QLineEdit::setEdited(bool on) { setModified(on); } + +/*! + There exists no equivalent functionality in Qt 4. +*/ +int QLineEdit::characterAt(int xpos, QChar *chr) const +{ + Q_D(const QLineEdit); + int pos = d->xToPos(xpos + contentsRect().x() - d->hscroll + horizontalMargin); + if (chr && pos < (int) d->text.length()) + *chr = d->text.at(pos); + return pos; + +} + +/*! + Use selectedText() and selectionStart() instead. +*/ +bool QLineEdit::getSelection(int *start, int *end) +{ + Q_D(QLineEdit); + if (d->hasSelectedText() && start && end) { + *start = d->selstart; + *end = d->selend; + return true; + } + return false; +} +#endif + + +/*! + Selects text from position \a start and for \a length characters. + Negative lengths are allowed. + + \sa deselect() selectAll() selectedText() +*/ + +void QLineEdit::setSelection(int start, int length) +{ + Q_D(QLineEdit); + if (start < 0 || start > (int)d->text.length()) { + qWarning("QLineEdit::setSelection: Invalid start position (%d)", start); + return; + } else { + if (length > 0) { + d->selstart = start; + d->selend = qMin(start + length, (int)d->text.length()); + d->cursor = d->selend; + } else { + d->selstart = qMax(start + length, 0); + d->selend = start; + d->cursor = d->selstart; + } + } + + if (d->hasSelectedText()){ + QStyleOptionFrameV2 opt; + initStyleOption(&opt); + if (!style()->styleHint(QStyle::SH_BlinkCursorWhenTextSelected, &opt, this)) + d->setCursorVisible(false); + } + + update(); + d->emitCursorPositionChanged(); +} + + +/*! + \property QLineEdit::undoAvailable + \brief whether undo is available + + Undo becomes available once the user has modified the text in the line edit. + + By default, this property is false. +*/ + +bool QLineEdit::isUndoAvailable() const +{ + Q_D(const QLineEdit); + return d->isUndoAvailable(); +} + +/*! + \property QLineEdit::redoAvailable + \brief whether redo is available + + Redo becomes available once the user has performed one or more undo operations + on text in the line edit. + + By default, this property is false. +*/ + +bool QLineEdit::isRedoAvailable() const +{ + Q_D(const QLineEdit); + return d->isRedoAvailable(); +} + +/*! + \property QLineEdit::dragEnabled + \brief whether the lineedit starts a drag if the user presses and + moves the mouse on some selected text + + Dragging is disabled by default. +*/ + +bool QLineEdit::dragEnabled() const +{ + Q_D(const QLineEdit); + return d->dragEnabled; +} + +void QLineEdit::setDragEnabled(bool b) +{ + Q_D(QLineEdit); + d->dragEnabled = b; +} + + +/*! + \property QLineEdit::acceptableInput + \brief whether the input satisfies the inputMask and the + validator. + + By default, this property is true. + + \sa setInputMask(), setValidator() +*/ +bool QLineEdit::hasAcceptableInput() const +{ + Q_D(const QLineEdit); + return d->hasAcceptableInput(d->text); +} + +/*! + Sets the margins around the text inside the frame to have the + sizes \a left, \a top, \a right, and \a bottom. + \since 4.5 + + See also getTextMargins(). +*/ +void QLineEdit::setTextMargins(int left, int top, int right, int bottom) +{ + Q_D(QLineEdit); + d->leftTextMargin = left; + d->topTextMargin = top; + d->rightTextMargin = right; + d->bottomTextMargin = bottom; + updateGeometry(); + update(); +} + +/*! + Returns the widget's text margins for \a left, \a top, \a right, and \a bottom. + \since 4.5 + + \sa setTextMargins() +*/ +void QLineEdit::getTextMargins(int *left, int *top, int *right, int *bottom) const +{ + Q_D(const QLineEdit); + if (left) + *left = d->leftTextMargin; + if (top) + *top = d->topTextMargin; + if (right) + *right = d->rightTextMargin; + if (bottom) + *bottom = d->bottomTextMargin; +} + +/*! + \property QLineEdit::inputMask + \brief The validation input mask + + If no mask is set, inputMask() returns an empty string. + + Sets the QLineEdit's validation mask. Validators can be used + instead of, or in conjunction with masks; see setValidator(). + + Unset the mask and return to normal QLineEdit operation by passing + an empty string ("") or just calling setInputMask() with no + arguments. + + The table below shows the characters that can be used in an input mask. + A space character, the default character for a blank, is needed for cases + where a character is \e{permitted but not required}. + + \table + \header \i Character \i Meaning + \row \i \c A \i ASCII alphabetic character required. A-Z, a-z. + \row \i \c a \i ASCII alphabetic character permitted but not required. + \row \i \c N \i ASCII alphanumeric character required. A-Z, a-z, 0-9. + \row \i \c n \i ASCII alphanumeric character permitted but not required. + \row \i \c X \i Any character required. + \row \i \c x \i Any character permitted but not required. + \row \i \c 9 \i ASCII digit required. 0-9. + \row \i \c 0 \i ASCII digit permitted but not required. + \row \i \c D \i ASCII digit required. 1-9. + \row \i \c d \i ASCII digit permitted but not required (1-9). + \row \i \c # \i ASCII digit or plus/minus sign permitted but not required. + \row \i \c H \i Hexadecimal character required. A-F, a-f, 0-9. + \row \i \c h \i Hexadecimal character permitted but not required. + \row \i \c B \i Binary character required. 0-1. + \row \i \c b \i Binary character permitted but not required. + \row \i \c > \i All following alphabetic characters are uppercased. + \row \i \c < \i All following alphabetic characters are lowercased. + \row \i \c ! \i Switch off case conversion. + \row \i \tt{\\} \i Use \tt{\\} to escape the special + characters listed above to use them as + separators. + \endtable + + The mask consists of a string of mask characters and separators, + optionally followed by a semicolon and the character used for + blanks. The blank characters are always removed from the text + after editing. + + Examples: + \table + \header \i Mask \i Notes + \row \i \c 000.000.000.000;_ \i IP address; blanks are \c{_}. + \row \i \c HH:HH:HH:HH:HH:HH;_ \i MAC address + \row \i \c 0000-00-00 \i ISO Date; blanks are \c space + \row \i \c >AAAAA-AAAAA-AAAAA-AAAAA-AAAAA;# \i License number; + blanks are \c - and all (alphabetic) characters are converted to + uppercase. + \endtable + + To get range control (e.g., for an IP address) use masks together + with \link setValidator() validators\endlink. + + \sa maxLength +*/ +QString QLineEdit::inputMask() const +{ + Q_D(const QLineEdit); + return (d->maskData ? d->inputMask + QLatin1Char(';') + d->blank : QString()); +} + +void QLineEdit::setInputMask(const QString &inputMask) +{ + Q_D(QLineEdit); + d->parseInputMask(inputMask); + if (d->maskData) + d->moveCursor(d->nextMaskBlank(0)); +} + +/*! + Selects all the text (i.e. highlights it) and moves the cursor to + the end. This is useful when a default value has been inserted + because if the user types before clicking on the widget, the + selected text will be deleted. + + \sa setSelection() deselect() +*/ + +void QLineEdit::selectAll() +{ + Q_D(QLineEdit); + d->selstart = d->selend = d->cursor = 0; + d->moveCursor(d->text.length(), true); +} + +/*! + Deselects any selected text. + + \sa setSelection() selectAll() +*/ + +void QLineEdit::deselect() +{ + Q_D(QLineEdit); + d->deselect(); + d->finishChange(); +} + + +/*! + Deletes any selected text, inserts \a newText, and validates the + result. If it is valid, it sets it as the new contents of the line + edit. + + \sa setText(), clear() +*/ +void QLineEdit::insert(const QString &newText) +{ +// q->resetInputContext(); //#### FIX ME IN QT + Q_D(QLineEdit); + int priorState = d->undoState; + d->removeSelectedText(); + d->insert(newText); + d->finishChange(priorState); +} + +/*! + Clears the contents of the line edit. + + \sa setText(), insert() +*/ +void QLineEdit::clear() +{ + Q_D(QLineEdit); + int priorState = d->undoState; + resetInputContext(); + d->selstart = 0; + d->selend = d->text.length(); + d->removeSelectedText(); + d->separate(); + d->finishChange(priorState, /*update*/false, /*edited*/false); +} + +/*! + Undoes the last operation if undo is \link + QLineEdit::undoAvailable available\endlink. Deselects any current + selection, and updates the selection start to the current cursor + position. +*/ +void QLineEdit::undo() +{ + Q_D(QLineEdit); + resetInputContext(); + d->undo(); + d->finishChange(-1, true); +} + +/*! + Redoes the last operation if redo is \link + QLineEdit::redoAvailable available\endlink. +*/ +void QLineEdit::redo() +{ + Q_D(QLineEdit); + resetInputContext(); + d->redo(); + d->finishChange(); +} + + +/*! + \property QLineEdit::readOnly + \brief whether the line edit is read only. + + In read-only mode, the user can still copy the text to the + clipboard, or drag and drop the text (if echoMode() is \l Normal), + but cannot edit it. + + QLineEdit does not show a cursor in read-only mode. + + By default, this property is false. + + \sa setEnabled() +*/ + +bool QLineEdit::isReadOnly() const +{ + Q_D(const QLineEdit); + return d->readOnly; +} + +void QLineEdit::setReadOnly(bool enable) +{ + Q_D(QLineEdit); + if (d->readOnly != enable) { + d->readOnly = enable; + setAttribute(Qt::WA_MacShowFocusRect, !d->readOnly); + setAttribute(Qt::WA_InputMethodEnabled, shouldEnableInputMethod(this)); +#ifndef QT_NO_CURSOR + setCursor(enable ? Qt::ArrowCursor : Qt::IBeamCursor); +#endif + update(); + } +} + + +#ifndef QT_NO_CLIPBOARD +/*! + Copies the selected text to the clipboard and deletes it, if there + is any, and if echoMode() is \l Normal. + + If the current validator disallows deleting the selected text, + cut() will copy without deleting. + + \sa copy() paste() setValidator() +*/ + +void QLineEdit::cut() +{ + if (hasSelectedText()) { + copy(); + del(); + } +} + + +/*! + Copies the selected text to the clipboard, if there is any, and if + echoMode() is \l Normal. + + \sa cut() paste() +*/ + +void QLineEdit::copy() const +{ + Q_D(const QLineEdit); + d->copy(); +} + +/*! + Inserts the clipboard's text at the cursor position, deleting any + selected text, providing the line edit is not \link + QLineEdit::readOnly read-only\endlink. + + If the end result would not be acceptable to the current + \link setValidator() validator\endlink, nothing happens. + + \sa copy() cut() +*/ + +void QLineEdit::paste() +{ + Q_D(QLineEdit); + if (echoMode() == PasswordEchoOnEdit && !d->passwordEchoEditing) { + // Clear the edit and reset to normal echo mode when pasting; the echo + // mode switches back when the edit loses focus. ### changes a public + // property, resets current content + d->updatePasswordEchoEditing(true); + clear(); + } + insert(QApplication::clipboard()->text(QClipboard::Clipboard)); +} + +void QLineEditPrivate::copy(bool clipboard) const +{ + Q_Q(const QLineEdit); + QString t = q->selectedText(); + if (!t.isEmpty() && echoMode == QLineEdit::Normal) { + q->disconnect(QApplication::clipboard(), SIGNAL(selectionChanged()), q, 0); + QApplication::clipboard()->setText(t, clipboard ? QClipboard::Clipboard : QClipboard::Selection); + q->connect(QApplication::clipboard(), SIGNAL(selectionChanged()), + q, SLOT(_q_clipboardChanged())); + } +} + +#endif // !QT_NO_CLIPBOARD + +/*! \reimp +*/ +bool QLineEdit::event(QEvent * e) +{ + Q_D(QLineEdit); +#ifndef QT_NO_SHORTCUT + if (e->type() == QEvent::ShortcutOverride && !d->readOnly) { + QKeyEvent* ke = (QKeyEvent*) e; + if (ke == QKeySequence::Copy + || ke == QKeySequence::Paste + || ke == QKeySequence::Cut + || ke == QKeySequence::Redo + || ke == QKeySequence::Undo + || ke == QKeySequence::MoveToNextWord + || ke == QKeySequence::MoveToPreviousWord + || ke == QKeySequence::MoveToStartOfDocument + || ke == QKeySequence::MoveToEndOfDocument + || ke == QKeySequence::SelectNextWord + || ke == QKeySequence::SelectPreviousWord + || ke == QKeySequence::SelectStartOfLine + || ke == QKeySequence::SelectEndOfLine + || ke == QKeySequence::SelectStartOfBlock + || ke == QKeySequence::SelectEndOfBlock + || ke == QKeySequence::SelectStartOfDocument + || ke == QKeySequence::SelectAll + || ke == QKeySequence::SelectEndOfDocument) { + ke->accept(); + } else if (ke->modifiers() == Qt::NoModifier || ke->modifiers() == Qt::ShiftModifier + || ke->modifiers() == Qt::KeypadModifier) { + if (ke->key() < Qt::Key_Escape) { + ke->accept(); + } else { + switch (ke->key()) { + case Qt::Key_Delete: + case Qt::Key_Home: + case Qt::Key_End: + case Qt::Key_Backspace: + case Qt::Key_Left: + case Qt::Key_Right: + ke->accept(); + default: + break; + } + } + } + } else +#endif + if (e->type() == QEvent::Timer) { + // should be timerEvent, is here for binary compatibility + int timerId = ((QTimerEvent*)e)->timerId(); + if (timerId == d->cursorTimer) { + QStyleOptionFrameV2 opt; + initStyleOption(&opt); + if(!hasSelectedText() + || style()->styleHint(QStyle::SH_BlinkCursorWhenTextSelected, &opt, this)) + d->setCursorVisible(!d->cursorVisible); +#ifndef QT_NO_DRAGANDDROP + } else if (timerId == d->dndTimer.timerId()) { + d->drag(); +#endif + } + else if (timerId == d->tripleClickTimer.timerId()) + d->tripleClickTimer.stop(); +#ifdef QT_KEYPAD_NAVIGATION + else if (timerId == d->deleteAllTimer.timerId()) { + d->deleteAllTimer.stop(); + clear(); + } +#endif + } else if (e->type() == QEvent::ContextMenu) { +#ifndef QT_NO_IM + if (d->composeMode()) + return true; +#endif + d->separate(); + } else if (e->type() == QEvent::WindowActivate) { + QTimer::singleShot(0, this, SLOT(_q_handleWindowActivate())); + } +#ifdef QT_KEYPAD_NAVIGATION + if (QApplication::keypadNavigationEnabled()) { + if ((e->type() == QEvent::KeyPress) || (e->type() == QEvent::KeyRelease)) { + QKeyEvent *ke = (QKeyEvent *)e; + if (ke->key() == Qt::Key_Back) { + if (ke->isAutoRepeat()) { + // Swallow it. We don't want back keys running amok. + ke->accept(); + return true; + } + if ((e->type() == QEvent::KeyRelease) + && !isReadOnly() + && d->deleteAllTimer.isActive()) { + d->deleteAllTimer.stop(); + backspace(); + ke->accept(); + return true; + } + } + } else if (e->type() == QEvent::EnterEditFocus) { + end(false); + if (!d->cursorTimer) { + int cft = QApplication::cursorFlashTime(); + d->cursorTimer = cft ? startTimer(cft/2) : -1; + } + } else if (e->type() == QEvent::LeaveEditFocus) { + d->setCursorVisible(false); + if (d->cursorTimer > 0) + killTimer(d->cursorTimer); + d->cursorTimer = 0; + + if (!d->emitingEditingFinished) { + if (hasAcceptableInput() || d->fixup()) { + d->emitingEditingFinished = true; + emit editingFinished(); + d->emitingEditingFinished = false; + } + } + } + } +#endif + return QWidget::event(e); +} + +/*! \reimp +*/ +void QLineEdit::mousePressEvent(QMouseEvent* e) +{ + Q_D(QLineEdit); + if (d->sendMouseEventToInputContext(e)) + return; + if (e->button() == Qt::RightButton) + return; +#ifdef QT_KEYPAD_NAVIGATION + if (QApplication::keypadNavigationEnabled() && !hasEditFocus()) { + setEditFocus(true); + // Get the completion list to pop up. + if (d->completer) + d->completer->complete(); + } +#endif + if (d->tripleClickTimer.isActive() && (e->pos() - d->tripleClick).manhattanLength() < + QApplication::startDragDistance()) { + selectAll(); + return; + } + bool mark = e->modifiers() & Qt::ShiftModifier; + int cursor = d->xToPos(e->pos().x()); +#ifndef QT_NO_DRAGANDDROP + if (!mark && d->dragEnabled && d->echoMode == Normal && + e->button() == Qt::LeftButton && d->inSelection(e->pos().x())) { + d->cursor = cursor; + update(); + d->dndPos = e->pos(); + if (!d->dndTimer.isActive()) + d->dndTimer.start(QApplication::startDragTime(), this); + d->emitCursorPositionChanged(); + } else +#endif + { + d->moveCursor(cursor, mark); + } +} + +/*! \reimp +*/ +void QLineEdit::mouseMoveEvent(QMouseEvent * e) +{ + Q_D(QLineEdit); + if (d->sendMouseEventToInputContext(e)) + return; + + if (e->buttons() & Qt::LeftButton) { +#ifndef QT_NO_DRAGANDDROP + if (d->dndTimer.isActive()) { + if ((d->dndPos - e->pos()).manhattanLength() > QApplication::startDragDistance()) + d->drag(); + } else +#endif + { + d->moveCursor(d->xToPos(e->pos().x()), true); + } + } +} + +/*! \reimp +*/ +void QLineEdit::mouseReleaseEvent(QMouseEvent* e) +{ + Q_D(QLineEdit); + if (d->sendMouseEventToInputContext(e)) + return; +#ifndef QT_NO_DRAGANDDROP + if (e->button() == Qt::LeftButton) { + if (d->dndTimer.isActive()) { + d->dndTimer.stop(); + deselect(); + return; + } + } +#endif +#ifndef QT_NO_CLIPBOARD + if (QApplication::clipboard()->supportsSelection()) { + if (e->button() == Qt::LeftButton) { + d->copy(false); + } else if (!d->readOnly && e->button() == Qt::MidButton) { + d->deselect(); + insert(QApplication::clipboard()->text(QClipboard::Selection)); + } + } +#endif +} + +/*! \reimp +*/ +void QLineEdit::mouseDoubleClickEvent(QMouseEvent* e) +{ + Q_D(QLineEdit); + if (d->sendMouseEventToInputContext(e)) + return; + if (e->button() == Qt::LeftButton) { + deselect(); + d->cursor = d->xToPos(e->pos().x()); + d->cursor = d->textLayout.previousCursorPosition(d->cursor, QTextLayout::SkipWords); + // ## text layout should support end of words. + int end = d->textLayout.nextCursorPosition(d->cursor, QTextLayout::SkipWords); + while (end > d->cursor && d->text[end-1].isSpace()) + --end; + d->moveCursor(end, true); + d->tripleClickTimer.start(QApplication::doubleClickInterval(), this); + d->tripleClick = e->pos(); + } +} + +/*! + \fn void QLineEdit::returnPressed() + + This signal is emitted when the Return or Enter key is pressed. + Note that if there is a validator() or inputMask() set on the line + edit, the returnPressed() signal will only be emitted if the input + follows the inputMask() and the validator() returns + QValidator::Acceptable. +*/ + +/*! + \fn void QLineEdit::editingFinished() + + This signal is emitted when the Return or Enter key is pressed or + the line edit loses focus. Note that if there is a validator() or + inputMask() set on the line edit and enter/return is pressed, the + editingFinished() signal will only be emitted if the input follows + the inputMask() and the validator() returns QValidator::Acceptable. +*/ + +/*! + Converts the given key press \a event into a line edit action. + + If Return or Enter is pressed and the current text is valid (or + can be \link QValidator::fixup() made valid\endlink by the + validator), the signal returnPressed() is emitted. + + The default key bindings are listed in the class's detailed + description. +*/ + +void QLineEdit::keyPressEvent(QKeyEvent *event) +{ + Q_D(QLineEdit); + + bool inlineCompletionAccepted = false; + +#ifndef QT_NO_COMPLETER + if (d->completer) { + QCompleter::CompletionMode completionMode = d->completer->completionMode(); + if ((completionMode == QCompleter::PopupCompletion + || completionMode == QCompleter::UnfilteredPopupCompletion) + &&d->completer->popup() + && d->completer->popup()->isVisible()) { + // The following keys are forwarded by the completer to the widget + // Ignoring the events lets the completer provide suitable default behavior + switch (event->key()) { + case Qt::Key_Escape: + event->ignore(); + return; + case Qt::Key_Enter: + case Qt::Key_Return: + case Qt::Key_F4: +#ifdef QT_KEYPAD_NAVIGATION + case Qt::Key_Select: + if (!QApplication::keypadNavigationEnabled()) + break; +#endif + d->completer->popup()->hide(); // just hide. will end up propagating to parent + default: + break; // normal key processing + } + } else if (completionMode == QCompleter::InlineCompletion) { + switch (event->key()) { + case Qt::Key_Enter: + case Qt::Key_Return: + case Qt::Key_F4: +#ifdef QT_KEYPAD_NAVIGATION + case Qt::Key_Select: + if (!QApplication::keypadNavigationEnabled()) + break; +#endif + if (!d->completer->currentCompletion().isEmpty() && d->selend > d->selstart + && d->selend == d->text.length()) { + setText(d->completer->currentCompletion()); + inlineCompletionAccepted = true; + } + default: + break; // normal key processing + } + } + } +#endif // QT_NO_COMPLETER + +#ifdef QT_KEYPAD_NAVIGATION + bool select = false; + switch (event->key()) { + case Qt::Key_Select: + if (QApplication::keypadNavigationEnabled()) { + if (hasEditFocus()) { + setEditFocus(false); + if (d->completer && d->completer->popup()->isVisible()) + d->completer->popup()->hide(); + select = true; + } + } + break; + case Qt::Key_Back: + case Qt::Key_No: + if (!QApplication::keypadNavigationEnabled() || !hasEditFocus()) { + event->ignore(); + return; + } + break; + default: + if (QApplication::keypadNavigationEnabled()) { + if (!hasEditFocus() && !(event->modifiers() & Qt::ControlModifier)) { + if (!event->text().isEmpty() && event->text().at(0).isPrint() + && !isReadOnly()) + { + setEditFocus(true); + clear(); + } else { + event->ignore(); + return; + } + } + } + } + + + + if (QApplication::keypadNavigationEnabled() && !select && !hasEditFocus()) { + setEditFocus(true); + if (event->key() == Qt::Key_Select) + return; // Just start. No action. + } +#endif + + if (echoMode() == PasswordEchoOnEdit + && !d->passwordEchoEditing + && !isReadOnly() + && !event->text().isEmpty() +#ifdef QT_KEYPAD_NAVIGATION + && event->key() != Qt::Key_Select + && event->key() != Qt::Key_Up + && event->key() != Qt::Key_Down + && event->key() != Qt::Key_Back +#endif + && !(event->modifiers() & Qt::ControlModifier)) { + // Clear the edit and reset to normal echo mode while editing; the + // echo mode switches back when the edit loses focus. ### changes a + // public property, resets current content. dubious code; you can + // navigate with keys up, down, back, and select(?), but if you press + // "left" or "right" it clears? + d->updatePasswordEchoEditing(true); + clear(); + } + + d->setCursorVisible(true); + if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) { + if (hasAcceptableInput() || d->fixup()) { + emit returnPressed(); + d->emitingEditingFinished = true; + emit editingFinished(); + d->emitingEditingFinished = false; + } + if (inlineCompletionAccepted) + event->accept(); + else + event->ignore(); + return; + } + bool unknown = false; + + if (false) { + } +#ifndef QT_NO_SHORTCUT + else if (event == QKeySequence::Undo) { + if (!d->readOnly) + undo(); + } + else if (event == QKeySequence::Redo) { + if (!d->readOnly) + redo(); + } + else if (event == QKeySequence::SelectAll) { + selectAll(); + } +#ifndef QT_NO_CLIPBOARD + else if (event == QKeySequence::Copy) { + copy(); + } + else if (event == QKeySequence::Paste) { + if (!d->readOnly) + paste(); + } + else if (event == QKeySequence::Cut) { + if (!d->readOnly) { + cut(); + } + } + else if (event == QKeySequence::DeleteEndOfLine) { + if (!d->readOnly) { + setSelection(d->cursor, d->text.size()); + copy(); + del(); + } + } +#endif //QT_NO_CLIPBOARD + else if (event == QKeySequence::MoveToStartOfLine) { + home(0); + } + else if (event == QKeySequence::MoveToEndOfLine) { + end(0); + } + else if (event == QKeySequence::SelectStartOfLine) { + home(1); + } + else if (event == QKeySequence::SelectEndOfLine) { + end(1); + } + else if (event == QKeySequence::MoveToNextChar) { +#if !defined(Q_WS_WIN) || defined(QT_NO_COMPLETER) + if (d->hasSelectedText()) { +#else + if (d->hasSelectedText() && d->completer + && d->completer->completionMode() == QCompleter::InlineCompletion) { +#endif + d->moveCursor(d->selend, false); + } else { + cursorForward(0, layoutDirection() == Qt::LeftToRight ? 1 : -1); + } + } + else if (event == QKeySequence::SelectNextChar) { + cursorForward(1, layoutDirection() == Qt::LeftToRight ? 1 : -1); + } + else if (event == QKeySequence::MoveToPreviousChar) { +#if !defined(Q_WS_WIN) || defined(QT_NO_COMPLETER) + if (d->hasSelectedText()) { +#else + if (d->hasSelectedText() && d->completer + && d->completer->completionMode() == QCompleter::InlineCompletion) { +#endif + d->moveCursor(d->selstart, false); + } else { + cursorBackward(0, layoutDirection() == Qt::LeftToRight ? 1 : -1); + } + } + else if (event == QKeySequence::SelectPreviousChar) { + cursorBackward(1, layoutDirection() == Qt::LeftToRight ? 1 : -1); + } + else if (event == QKeySequence::MoveToNextWord) { + if (echoMode() == Normal) + layoutDirection() == Qt::LeftToRight ? cursorWordForward(0) : cursorWordBackward(0); + else + layoutDirection() == Qt::LeftToRight ? end(0) : home(0); + } + else if (event == QKeySequence::MoveToPreviousWord) { + if (echoMode() == Normal) + layoutDirection() == Qt::LeftToRight ? cursorWordBackward(0) : cursorWordForward(0); + else if (!d->readOnly) { + layoutDirection() == Qt::LeftToRight ? home(0) : end(0); + } + } + else if (event == QKeySequence::SelectNextWord) { + if (echoMode() == Normal) + layoutDirection() == Qt::LeftToRight ? cursorWordForward(1) : cursorWordBackward(1); + else + layoutDirection() == Qt::LeftToRight ? end(1) : home(1); + } + else if (event == QKeySequence::SelectPreviousWord) { + if (echoMode() == Normal) + layoutDirection() == Qt::LeftToRight ? cursorWordBackward(1) : cursorWordForward(1); + else + layoutDirection() == Qt::LeftToRight ? home(1) : end(1); + } + else if (event == QKeySequence::Delete) { + if (!d->readOnly) + del(); + } + else if (event == QKeySequence::DeleteEndOfWord) { + if (!d->readOnly) { + cursorWordForward(true); + del(); + } + } + else if (event == QKeySequence::DeleteStartOfWord) { + if (!d->readOnly) { + cursorWordBackward(true); + del(); + } + } +#endif // QT_NO_SHORTCUT + else { +#ifdef Q_WS_MAC + if (event->key() == Qt::Key_Up || event->key() == Qt::Key_Down) { + Qt::KeyboardModifiers myModifiers = (event->modifiers() & ~Qt::KeypadModifier); + if (myModifiers & Qt::ShiftModifier) { + if (myModifiers == (Qt::ControlModifier|Qt::ShiftModifier) + || myModifiers == (Qt::AltModifier|Qt::ShiftModifier) + || myModifiers == Qt::ShiftModifier) { + + event->key() == Qt::Key_Up ? home(1) : end(1); + } + } else { + if ((myModifiers == Qt::ControlModifier + || myModifiers == Qt::AltModifier + || myModifiers == Qt::NoModifier)) { + event->key() == Qt::Key_Up ? home(0) : end(0); + } + } + } +#endif + if (event->modifiers() & Qt::ControlModifier) { + switch (event->key()) { + case Qt::Key_Backspace: + if (!d->readOnly) { + cursorWordBackward(true); + del(); + } + break; +#ifndef QT_NO_COMPLETER + case Qt::Key_Up: + case Qt::Key_Down: + d->complete(event->key()); + break; +#endif +#if defined(Q_WS_X11) + case Qt::Key_E: + end(0); + break; + + case Qt::Key_U: + if (!d->readOnly) { + setSelection(0, d->text.size()); +#ifndef QT_NO_CLIPBOARD + copy(); +#endif + del(); + } + break; +#endif + default: + unknown = true; + } + } else { // ### check for *no* modifier + switch (event->key()) { + case Qt::Key_Backspace: + if (!d->readOnly) { + backspace(); +#ifndef QT_NO_COMPLETER + d->complete(Qt::Key_Backspace); +#endif + } + break; +#ifdef QT_KEYPAD_NAVIGATION + case Qt::Key_Back: + if (QApplication::keypadNavigationEnabled() && !event->isAutoRepeat() + && !isReadOnly()) { + if (text().length() == 0) { + setText(d->origText); + + if (d->passwordEchoEditing) + d->updatePasswordEchoEditing(false); + + setEditFocus(false); + } else if (!d->deleteAllTimer.isActive()) { + d->deleteAllTimer.start(750, this); + } + } else { + unknown = true; + } + break; +#endif + + default: + unknown = true; + } + } + } + + if (event->key() == Qt::Key_Direction_L || event->key() == Qt::Key_Direction_R) { + setLayoutDirection((event->key() == Qt::Key_Direction_L) ? Qt::LeftToRight : Qt::RightToLeft); + d->updateTextLayout(); + update(); + unknown = false; + } + + if (unknown && !d->readOnly) { + QString t = event->text(); + if (!t.isEmpty() && t.at(0).isPrint()) { + insert(t); +#ifndef QT_NO_COMPLETER + d->complete(event->key()); +#endif + event->accept(); + return; + } + } + + if (unknown) + event->ignore(); + else + event->accept(); +} + +/*! + \since 4.4 + + Returns a rectangle that includes the lineedit cursor. +*/ +QRect QLineEdit::cursorRect() const +{ + Q_D(const QLineEdit); + return d->cursorRect(); +} + +/*! + This function is not intended as polymorphic usage. Just a shared code + fragment that calls QInputContext::mouseHandler for this + class. +*/ +bool QLineEditPrivate::sendMouseEventToInputContext( QMouseEvent *e ) +{ +#if !defined QT_NO_IM + Q_Q(QLineEdit); + if ( composeMode() ) { + int tmp_cursor = xToPos(e->pos().x()); + int mousePos = tmp_cursor - cursor; + if ( mousePos < 0 || mousePos > textLayout.preeditAreaText().length() ) { + mousePos = -1; + // don't send move events outside the preedit area + if ( e->type() == QEvent::MouseMove ) + return true; + } + + QInputContext *qic = q->inputContext(); + if ( qic ) + // may be causing reset() in some input methods + qic->mouseHandler(mousePos, e); + if (!textLayout.preeditAreaText().isEmpty()) + return true; + } +#else + Q_UNUSED(e); +#endif + + return false; +} + +/*! \reimp + */ +void QLineEdit::inputMethodEvent(QInputMethodEvent *e) +{ + Q_D(QLineEdit); + if (d->readOnly) { + e->ignore(); + return; + } + + if (echoMode() == PasswordEchoOnEdit && !d->passwordEchoEditing) { + // Clear the edit and reset to normal echo mode while entering input + // method data; the echo mode switches back when the edit loses focus. + // ### changes a public property, resets current content. + d->updatePasswordEchoEditing(true); + clear(); + } + +#ifdef QT_KEYPAD_NAVIGATION + // Focus in if currently in navigation focus on the widget + // Only focus in on preedits, to allow input methods to + // commit text as they focus out without interfering with focus + if (QApplication::keypadNavigationEnabled() + && hasFocus() && !hasEditFocus() + && !e->preeditString().isEmpty()) { + setEditFocus(true); + selectAll(); // so text is replaced rather than appended to + } +#endif + + int priorState = d->undoState; + d->removeSelectedText(); + + int c = d->cursor; // cursor position after insertion of commit string + if (e->replacementStart() <= 0) + c += e->commitString().length() + qMin(-e->replacementStart(), e->replacementLength()); + + d->cursor += e->replacementStart(); + + // insert commit string + if (e->replacementLength()) { + d->selstart = d->cursor; + d->selend = d->selstart + e->replacementLength(); + d->removeSelectedText(); + } + if (!e->commitString().isEmpty()) + d->insert(e->commitString()); + + d->cursor = qMin(c, d->text.length()); + + d->textLayout.setPreeditArea(d->cursor, e->preeditString()); + d->preeditCursor = e->preeditString().length(); + d->hideCursor = false; + QList<QTextLayout::FormatRange> formats; + for (int i = 0; i < e->attributes().size(); ++i) { + const QInputMethodEvent::Attribute &a = e->attributes().at(i); + if (a.type == QInputMethodEvent::Cursor) { + d->preeditCursor = a.start; + d->hideCursor = !a.length; + } else if (a.type == QInputMethodEvent::TextFormat) { + QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat(); + if (f.isValid()) { + QTextLayout::FormatRange o; + o.start = a.start + d->cursor; + o.length = a.length; + o.format = f; + formats.append(o); + } + } + } + d->textLayout.setAdditionalFormats(formats); + d->updateTextLayout(); + update(); + if (!e->commitString().isEmpty()) + d->emitCursorPositionChanged(); + d->finishChange(priorState); +#ifndef QT_NO_COMPLETER + if (!e->commitString().isEmpty()) + d->complete(Qt::Key_unknown); +#endif +} + +/*!\reimp +*/ +QVariant QLineEdit::inputMethodQuery(Qt::InputMethodQuery property) const +{ + Q_D(const QLineEdit); + switch(property) { + case Qt::ImMicroFocus: + return d->cursorRect(); + case Qt::ImFont: + return font(); + case Qt::ImCursorPosition: + return QVariant((d->selend - d->selstart == 0) ? d->cursor : d->selend); + case Qt::ImSurroundingText: + return QVariant(d->text); + case Qt::ImCurrentSelection: + return QVariant(selectedText()); + default: + return QVariant(); + } +} + +/*!\reimp +*/ + +void QLineEdit::focusInEvent(QFocusEvent *e) +{ + Q_D(QLineEdit); + if (e->reason() == Qt::TabFocusReason || + e->reason() == Qt::BacktabFocusReason || + e->reason() == Qt::ShortcutFocusReason) { + if (d->maskData) + d->moveCursor(d->nextMaskBlank(0)); + else if (!d->hasSelectedText()) + selectAll(); + } +#ifdef QT_KEYPAD_NAVIGATION + if (!QApplication::keypadNavigationEnabled() || (hasEditFocus() && e->reason() == Qt::PopupFocusReason)) +#endif + if (!d->cursorTimer) { + int cft = QApplication::cursorFlashTime(); + d->cursorTimer = cft ? startTimer(cft/2) : -1; + } + QStyleOptionFrameV2 opt; + initStyleOption(&opt); + if((!hasSelectedText() && d->textLayout.preeditAreaText().isEmpty()) + || style()->styleHint(QStyle::SH_BlinkCursorWhenTextSelected, &opt, this)) + d->setCursorVisible(true); +#ifdef Q_WS_MAC + if (d->echoMode == Password || d->echoMode == NoEcho) + qt_mac_secure_keyboard(true); +#endif +#ifdef QT_KEYPAD_NAVIGATION + d->origText = d->text; +#endif +#ifndef QT_NO_COMPLETER + if (d->completer) { + d->completer->setWidget(this); + QObject::connect(d->completer, SIGNAL(activated(QString)), + this, SLOT(setText(QString))); + QObject::connect(d->completer, SIGNAL(highlighted(QString)), + this, SLOT(_q_completionHighlighted(QString))); + } +#endif + update(); +} + +/*!\reimp +*/ + +void QLineEdit::focusOutEvent(QFocusEvent *e) +{ + Q_D(QLineEdit); + if (d->passwordEchoEditing) { + // Reset the echomode back to PasswordEchoOnEdit when the widget loses + // focus. + d->updatePasswordEchoEditing(false); + } + + Qt::FocusReason reason = e->reason(); + if (reason != Qt::ActiveWindowFocusReason && + reason != Qt::PopupFocusReason) + deselect(); + + d->setCursorVisible(false); + if (d->cursorTimer > 0) + killTimer(d->cursorTimer); + d->cursorTimer = 0; + +#ifdef QT_KEYPAD_NAVIGATION + // editingFinished() is already emitted on LeaveEditFocus + if (!QApplication::keypadNavigationEnabled()) +#endif + if (reason != Qt::PopupFocusReason + || !(QApplication::activePopupWidget() && QApplication::activePopupWidget()->parentWidget() == this)) { + if (!d->emitingEditingFinished) { + if (hasAcceptableInput() || d->fixup()) { + d->emitingEditingFinished = true; + emit editingFinished(); + d->emitingEditingFinished = false; + } + } +#ifdef QT3_SUPPORT + emit lostFocus(); +#endif + } +#ifdef Q_WS_MAC + if (d->echoMode == Password || d->echoMode == NoEcho) + qt_mac_secure_keyboard(false); +#endif +#ifdef QT_KEYPAD_NAVIGATION + d->origText = QString(); +#endif +#ifndef QT_NO_COMPLETER + if (d->completer) { + QObject::disconnect(d->completer, 0, this, 0); + } +#endif + update(); +} + +/*!\reimp +*/ +void QLineEdit::paintEvent(QPaintEvent *) +{ + Q_D(QLineEdit); + QPainter p(this); + + QRect r = rect(); + QPalette pal = palette(); + + QStyleOptionFrameV2 panel; + initStyleOption(&panel); + style()->drawPrimitive(QStyle::PE_PanelLineEdit, &panel, &p, this); + r = style()->subElementRect(QStyle::SE_LineEditContents, &panel, this); + r.setX(r.x() + d->leftTextMargin); + r.setY(r.y() + d->topTextMargin); + r.setRight(r.right() - d->rightTextMargin); + r.setBottom(r.bottom() - d->bottomTextMargin); + p.setClipRect(r); + + QFontMetrics fm = fontMetrics(); + Qt::Alignment va = QStyle::visualAlignment(layoutDirection(), QFlag(d->alignment)); + switch (va & Qt::AlignVertical_Mask) { + case Qt::AlignBottom: + d->vscroll = r.y() + r.height() - fm.height() - verticalMargin; + break; + case Qt::AlignTop: + d->vscroll = r.y() + verticalMargin; + break; + default: + //center + d->vscroll = r.y() + (r.height() - fm.height() + 1) / 2; + break; + } + QRect lineRect(r.x() + horizontalMargin, d->vscroll, r.width() - 2*horizontalMargin, fm.height()); + QTextLine line = d->textLayout.lineAt(0); + + int cursor = d->cursor; + if (d->preeditCursor != -1) + cursor += d->preeditCursor; + // locate cursor position + int cix = qRound(line.cursorToX(cursor)); + + // horizontal scrolling. d->hscroll is the left indent from the beginning + // of the text line to the left edge of lineRect. we update this value + // depending on the delta from the last paint event; in effect this means + // the below code handles all scrolling based on the textline (widthUsed, + // minLB, minRB), the line edit rect (lineRect) and the cursor position + // (cix). + int minLB = qMax(0, -fm.minLeftBearing()); + int minRB = qMax(0, -fm.minRightBearing()); + int widthUsed = qRound(line.naturalTextWidth()) + 1 + minRB; + if ((minLB + widthUsed) <= lineRect.width()) { + // text fits in lineRect; use hscroll for alignment + switch (va & ~(Qt::AlignAbsolute|Qt::AlignVertical_Mask)) { + case Qt::AlignRight: + d->hscroll = widthUsed - lineRect.width() + 1; + break; + case Qt::AlignHCenter: + d->hscroll = (widthUsed - lineRect.width()) / 2; + break; + default: + // Left + d->hscroll = 0; + break; + } + d->hscroll -= minLB; + } else if (cix - d->hscroll >= lineRect.width()) { + // text doesn't fit, cursor is to the right of lineRect (scroll right) + d->hscroll = cix - lineRect.width() + 1; + } else if (cix - d->hscroll < 0 && d->hscroll < widthUsed) { + // text doesn't fit, cursor is to the left of lineRect (scroll left) + d->hscroll = cix; + } else if (widthUsed - d->hscroll < lineRect.width()) { + // text doesn't fit, text document is to the left of lineRect; align + // right + d->hscroll = widthUsed - lineRect.width() + 1; + } + // the y offset is there to keep the baseline constant in case we have script changes in the text. + QPoint topLeft = lineRect.topLeft() - QPoint(d->hscroll, d->ascent - fm.ascent()); + + // draw text, selections and cursors +#ifndef QT_NO_STYLE_STYLESHEET + if (QStyleSheetStyle* cssStyle = qobject_cast<QStyleSheetStyle*>(style())) { + cssStyle->focusPalette(this, &panel, &pal); + } +#endif + p.setPen(pal.text().color()); + + QVector<QTextLayout::FormatRange> selections; +#ifdef QT_KEYPAD_NAVIGATION + if (!QApplication::keypadNavigationEnabled() || hasEditFocus()) +#endif + if (d->selstart < d->selend || (d->cursorVisible && d->maskData && !d->readOnly)) { + QTextLayout::FormatRange o; + if (d->selstart < d->selend) { + o.start = d->selstart; + o.length = d->selend - d->selstart; + o.format.setBackground(pal.brush(QPalette::Highlight)); + o.format.setForeground(pal.brush(QPalette::HighlightedText)); + } else { + // mask selection + o.start = d->cursor; + o.length = 1; + o.format.setBackground(pal.brush(QPalette::Text)); + o.format.setForeground(pal.brush(QPalette::Window)); + } + selections.append(o); + } + + // Asian users see an IM selection text as cursor on candidate + // selection phase of input method, so the ordinary cursor should be + // invisible if we have a preedit string. + d->textLayout.draw(&p, topLeft, selections, r); + if (d->cursorVisible && !d->readOnly && !d->hideCursor) + d->textLayout.drawCursor(&p, topLeft, cursor, style()->pixelMetric(QStyle::PM_TextCursorWidth)); +} + + +#ifndef QT_NO_DRAGANDDROP +/*!\reimp +*/ +void QLineEdit::dragMoveEvent(QDragMoveEvent *e) +{ + Q_D(QLineEdit); + if (!d->readOnly && e->mimeData()->hasFormat(QLatin1String("text/plain"))) { + e->acceptProposedAction(); + d->cursor = d->xToPos(e->pos().x()); + d->cursorVisible = true; + update(); + d->emitCursorPositionChanged(); + } +} + +/*!\reimp */ +void QLineEdit::dragEnterEvent(QDragEnterEvent * e) +{ + QLineEdit::dragMoveEvent(e); +} + +/*!\reimp */ +void QLineEdit::dragLeaveEvent(QDragLeaveEvent *) +{ + Q_D(QLineEdit); + if (d->cursorVisible) { + d->cursorVisible = false; + update(); + } +} + +/*!\reimp */ +void QLineEdit::dropEvent(QDropEvent* e) +{ + Q_D(QLineEdit); + QString str = e->mimeData()->text(); + + if (!str.isNull() && !d->readOnly) { + if (e->source() == this && e->dropAction() == Qt::CopyAction) + deselect(); + d->cursor =d->xToPos(e->pos().x()); + int selStart = d->cursor; + int oldSelStart = d->selstart; + int oldSelEnd = d->selend; + d->cursorVisible = false; + e->acceptProposedAction(); + insert(str); + if (e->source() == this) { + if (e->dropAction() == Qt::MoveAction) { + if (selStart > oldSelStart && selStart <= oldSelEnd) + setSelection(oldSelStart, str.length()); + else if (selStart > oldSelEnd) + setSelection(selStart - str.length(), str.length()); + else + setSelection(selStart, str.length()); + } else { + setSelection(selStart, str.length()); + } + } + } else { + e->ignore(); + update(); + } +} + +void QLineEditPrivate::drag() +{ + Q_Q(QLineEdit); + dndTimer.stop(); + QMimeData *data = new QMimeData; + data->setText(q->selectedText()); + QDrag *drag = new QDrag(q); + drag->setMimeData(data); + Qt::DropAction action = drag->start(); + if (action == Qt::MoveAction && !readOnly && drag->target() != q) { + int priorState = undoState; + removeSelectedText(); + finishChange(priorState); + } +} + +#endif // QT_NO_DRAGANDDROP + +#ifndef QT_NO_CONTEXTMENU +/*! + Shows the standard context menu created with + createStandardContextMenu(). + + If you do not want the line edit to have a context menu, you can set + its \l contextMenuPolicy to Qt::NoContextMenu. If you want to + customize the context menu, reimplement this function. If you want + to extend the standard context menu, reimplement this function, call + createStandardContextMenu() and extend the menu returned. + + \snippet doc/src/snippets/code/src_gui_widgets_qlineedit.cpp 0 + + The \a event parameter is used to obtain the position where + the mouse cursor was when the event was generated. + + \sa setContextMenuPolicy() +*/ +void QLineEdit::contextMenuEvent(QContextMenuEvent *event) +{ + QPointer<QMenu> menu = createStandardContextMenu(); + menu->exec(event->globalPos()); + delete menu; +} + +#if defined(Q_WS_WIN) + extern bool qt_use_rtl_extensions; +#endif + +/*! This function creates the standard context menu which is shown + when the user clicks on the line edit with the right mouse + button. It is called from the default contextMenuEvent() handler. + The popup menu's ownership is transferred to the caller. +*/ + +QMenu *QLineEdit::createStandardContextMenu() +{ + Q_D(QLineEdit); + QMenu *popup = new QMenu(this); + popup->setObjectName(QLatin1String("qt_edit_menu")); + + QAction *action = popup->addAction(QLineEdit::tr("&Undo") + ACCEL_KEY(QKeySequence::Undo)); + action->setEnabled(d->isUndoAvailable()); + connect(action, SIGNAL(triggered()), SLOT(undo())); + + action = popup->addAction(QLineEdit::tr("&Redo") + ACCEL_KEY(QKeySequence::Redo)); + action->setEnabled(d->isRedoAvailable()); + connect(action, SIGNAL(triggered()), SLOT(redo())); + + popup->addSeparator(); + +#ifndef QT_NO_CLIPBOARD + action = popup->addAction(QLineEdit::tr("Cu&t") + ACCEL_KEY(QKeySequence::Cut)); + action->setEnabled(!d->readOnly && d->hasSelectedText()); + connect(action, SIGNAL(triggered()), SLOT(cut())); + + action = popup->addAction(QLineEdit::tr("&Copy") + ACCEL_KEY(QKeySequence::Copy)); + action->setEnabled(d->hasSelectedText()); + connect(action, SIGNAL(triggered()), SLOT(copy())); + + action = popup->addAction(QLineEdit::tr("&Paste") + ACCEL_KEY(QKeySequence::Paste)); + action->setEnabled(!d->readOnly && !QApplication::clipboard()->text().isEmpty()); + connect(action, SIGNAL(triggered()), SLOT(paste())); +#endif + + action = popup->addAction(QLineEdit::tr("Delete")); + action->setEnabled(!d->readOnly && !d->text.isEmpty() && d->hasSelectedText()); + connect(action, SIGNAL(triggered()), SLOT(_q_deleteSelected())); + + popup->addSeparator(); + + action = popup->addAction(QLineEdit::tr("Select All") + ACCEL_KEY(QKeySequence::SelectAll)); + action->setEnabled(!d->text.isEmpty() && !d->allSelected()); + d->selectAllAction = action; + connect(action, SIGNAL(triggered()), SLOT(selectAll())); + +#if !defined(QT_NO_IM) + QInputContext *qic = inputContext(); + if (qic) { + QList<QAction *> imActions = qic->actions(); + for (int i = 0; i < imActions.size(); ++i) + popup->addAction(imActions.at(i)); + } +#endif + +#if defined(Q_WS_WIN) + if (!d->readOnly && qt_use_rtl_extensions) { +#else + if (!d->readOnly) { +#endif + popup->addSeparator(); + QUnicodeControlCharacterMenu *ctrlCharacterMenu = new QUnicodeControlCharacterMenu(this, popup); + popup->addMenu(ctrlCharacterMenu); + } + return popup; +} +#endif // QT_NO_CONTEXTMENU + +/*! \reimp */ +void QLineEdit::changeEvent(QEvent *ev) +{ + Q_D(QLineEdit); + if(ev->type() == QEvent::ActivationChange) { + if (!palette().isEqual(QPalette::Active, QPalette::Inactive)) + update(); + } else if (ev->type() == QEvent::FontChange + || ev->type() == QEvent::StyleChange + || ev->type() == QEvent::LayoutDirectionChange) { + d->updateTextLayout(); + } + QWidget::changeEvent(ev); +} + +void QLineEditPrivate::_q_clipboardChanged() +{ +} + +void QLineEditPrivate::_q_handleWindowActivate() +{ + Q_Q(QLineEdit); + if (!q->hasFocus() && q->hasSelectedText()) + q->deselect(); +} + +void QLineEditPrivate::_q_deleteSelected() +{ + Q_Q(QLineEdit); + if (!hasSelectedText()) + return; + + int priorState = undoState; + q->resetInputContext(); + removeSelectedText(); + separate(); + finishChange(priorState); +} + +void QLineEditPrivate::init(const QString& txt) +{ + Q_Q(QLineEdit); +#ifndef QT_NO_CURSOR + q->setCursor(Qt::IBeamCursor); +#endif + q->setFocusPolicy(Qt::StrongFocus); + q->setAttribute(Qt::WA_InputMethodEnabled); + // Specifies that this widget can use more, but is able to survive on + // less, horizontal space; and is fixed vertically. + q->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed, QSizePolicy::LineEdit)); + q->setBackgroundRole(QPalette::Base); + q->setAttribute(Qt::WA_KeyCompression); + q->setMouseTracking(true); + q->setAcceptDrops(true); + text = txt; + updateTextLayout(); + cursor = text.length(); + + q->setAttribute(Qt::WA_MacShowFocusRect); +} + +void QLineEditPrivate::updatePasswordEchoEditing(bool editing) +{ + Q_Q(QLineEdit); + passwordEchoEditing = editing; + q->setAttribute(Qt::WA_InputMethodEnabled, shouldEnableInputMethod(q)); + updateTextLayout(); + q->update(); +} + +void QLineEditPrivate::updateTextLayout() +{ + // replace certain non-printable characters with spaces (to avoid + // drawing boxes when using fonts that don't have glyphs for such + // characters) + Q_Q(QLineEdit); + QString str = q->displayText(); + QChar* uc = str.data(); + for (int i = 0; i < (int)str.length(); ++i) { + if ((uc[i] < 0x20 && uc[i] != 0x09) + || uc[i] == QChar::LineSeparator + || uc[i] == QChar::ParagraphSeparator + || uc[i] == QChar::ObjectReplacementCharacter) + uc[i] = QChar(0x0020); + } + textLayout.setFont(q->font()); + textLayout.setText(str); + QTextOption option; + option.setTextDirection(q->layoutDirection()); + option.setFlags(QTextOption::IncludeTrailingSpaces); + textLayout.setTextOption(option); + + textLayout.beginLayout(); + QTextLine l = textLayout.createLine(); + textLayout.endLayout(); + ascent = qRound(l.ascent()); +} + +int QLineEditPrivate::xToPos(int x, QTextLine::CursorPosition betweenOrOn) const +{ + QRect cr = adjustedContentsRect(); + x-= cr.x() - hscroll + horizontalMargin; + QTextLine l = textLayout.lineAt(0); + return l.xToCursor(x, betweenOrOn); +} + +QRect QLineEditPrivate::cursorRect() const +{ + Q_Q(const QLineEdit); + QRect cr = adjustedContentsRect(); + int cix = cr.x() - hscroll + horizontalMargin; + QTextLine l = textLayout.lineAt(0); + int c = cursor; + if (preeditCursor != -1) + c += preeditCursor; + cix += qRound(l.cursorToX(c)); + int ch = qMin(cr.height(), q->fontMetrics().height() + 1); + int w = q->style()->pixelMetric(QStyle::PM_TextCursorWidth); + return QRect(cix-5, vscroll, w + 9, ch); +} + +QRect QLineEditPrivate::adjustedContentsRect() const +{ + Q_Q(const QLineEdit); + QStyleOptionFrameV2 opt; + q->initStyleOption(&opt); + QRect r = q->style()->subElementRect(QStyle::SE_LineEditContents, &opt, q); + r.setX(r.x() + leftTextMargin); + r.setY(r.y() + topTextMargin); + r.setRight(r.right() - rightTextMargin); + r.setBottom(r.bottom() - bottomTextMargin); + return r; +} + +bool QLineEditPrivate::fixup() // this function assumes that validate currently returns != Acceptable +{ +#ifndef QT_NO_VALIDATOR + if (validator) { + QString textCopy = text; + int cursorCopy = cursor; + validator->fixup(textCopy); + if (validator->validate(textCopy, cursorCopy) == QValidator::Acceptable) { + if (textCopy != text || cursorCopy != cursor) + setText(textCopy, cursorCopy); + return true; + } + } +#endif + return false; +} + +void QLineEditPrivate::moveCursor(int pos, bool mark) +{ + Q_Q(QLineEdit); + if (pos != cursor) { + separate(); + if (maskData) + pos = pos > cursor ? nextMaskBlank(pos) : prevMaskBlank(pos); + } + bool fullUpdate = mark || hasSelectedText(); + if (mark) { + int anchor; + if (selend > selstart && cursor == selstart) + anchor = selend; + else if (selend > selstart && cursor == selend) + anchor = selstart; + else + anchor = cursor; + selstart = qMin(anchor, pos); + selend = qMax(anchor, pos); + updateTextLayout(); + } else { + deselect(); + } + if (fullUpdate) { + cursor = pos; + q->update(); + } else { + setCursorVisible(false); + cursor = pos; + setCursorVisible(true); + if (!adjustedContentsRect().contains(cursorRect())) + q->update(); + } + QStyleOptionFrameV2 opt; + q->initStyleOption(&opt); + if (mark && !q->style()->styleHint(QStyle::SH_BlinkCursorWhenTextSelected, &opt, q)) + setCursorVisible(false); + if (mark || selDirty) { + selDirty = false; + emit q->selectionChanged(); + } + emitCursorPositionChanged(); +} + +void QLineEditPrivate::finishChange(int validateFromState, bool update, bool edited) +{ + Q_Q(QLineEdit); + bool lineDirty = selDirty; + if (textDirty) { + // do validation + bool wasValidInput = validInput; + validInput = true; +#ifndef QT_NO_VALIDATOR + if (validator) { + validInput = false; + QString textCopy = text; + int cursorCopy = cursor; + validInput = (validator->validate(textCopy, cursorCopy) != QValidator::Invalid); + if (validInput) { + if (text != textCopy) { + setText(textCopy, cursorCopy); + return; + } + cursor = cursorCopy; + } + } +#endif + if (validateFromState >= 0 && wasValidInput && !validInput) { + undo(validateFromState); + history.resize(undoState); + if (modifiedState > undoState) + modifiedState = -1; + validInput = true; + textDirty = false; + } + updateTextLayout(); + lineDirty |= textDirty; + if (textDirty) { + textDirty = false; + QString actualText = maskData ? stripString(text) : text; + if (edited) + emit q->textEdited(actualText); + q->updateMicroFocus(); +#ifndef QT_NO_COMPLETER + if (edited && completer && completer->completionMode() != QCompleter::InlineCompletion) + complete(-1); // update the popup on cut/paste/del +#endif + emit q->textChanged(actualText); + } +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(q, 0, QAccessible::ValueChanged); +#endif + } + if (selDirty) { + selDirty = false; + emit q->selectionChanged(); + } + if (lineDirty || update) + q->update(); + emitCursorPositionChanged(); +} + +void QLineEditPrivate::emitCursorPositionChanged() +{ + Q_Q(QLineEdit); + if (cursor != lastCursorPos) { + const int oldLast = lastCursorPos; + lastCursorPos = cursor; + emit q->cursorPositionChanged(oldLast, cursor); + } +} + +void QLineEditPrivate::setText(const QString& txt, int pos, bool edited) +{ + Q_Q(QLineEdit); + q->resetInputContext(); + deselect(); + QString oldText = text; + if (maskData) { + text = maskString(0, txt, true); + text += clearString(text.length(), maxLength - text.length()); + } else { + text = txt.isEmpty() ? txt : txt.left(maxLength); + } + history.clear(); + modifiedState = undoState = 0; + cursor = (pos < 0 || pos > text.length()) ? text.length() : pos; + textDirty = (oldText != text); + finishChange(-1, true, edited); +} + + +void QLineEditPrivate::setCursorVisible(bool visible) +{ + Q_Q(QLineEdit); + if ((bool)cursorVisible == visible) + return; + if (cursorTimer) + cursorVisible = visible; + QRect r = cursorRect(); + if (maskData) + q->update(); + else + q->update(r); +} + +void QLineEditPrivate::addCommand(const Command& cmd) +{ + if (separator && undoState && history[undoState-1].type != Separator) { + history.resize(undoState + 2); + history[undoState++] = Command(Separator, cursor, 0, selstart, selend); + } else { + history.resize(undoState + 1); + } + separator = false; + history[undoState++] = cmd; +} + +void QLineEditPrivate::insert(const QString& s) +{ + if (hasSelectedText()) + addCommand(Command(SetSelection, cursor, 0, selstart, selend)); + if (maskData) { + QString ms = maskString(cursor, s); + for (int i = 0; i < (int) ms.length(); ++i) { + addCommand (Command(DeleteSelection, cursor+i, text.at(cursor+i), -1, -1)); + addCommand(Command(Insert, cursor+i, ms.at(i), -1, -1)); + } + text.replace(cursor, ms.length(), ms); + cursor += ms.length(); + cursor = nextMaskBlank(cursor); + textDirty = true; + } else { + int remaining = maxLength - text.length(); + if (remaining != 0) { + text.insert(cursor, s.left(remaining)); + for (int i = 0; i < (int) s.left(remaining).length(); ++i) + addCommand(Command(Insert, cursor++, s.at(i), -1, -1)); + textDirty = true; + } + } +} + +void QLineEditPrivate::del(bool wasBackspace) +{ + if (cursor < (int) text.length()) { + if (hasSelectedText()) + addCommand(Command(SetSelection, cursor, 0, selstart, selend)); + addCommand (Command((CommandType)((maskData?2:0)+(wasBackspace?Remove:Delete)), cursor, text.at(cursor), -1, -1)); + if (maskData) { + text.replace(cursor, 1, clearString(cursor, 1)); + addCommand(Command(Insert, cursor, text.at(cursor), -1, -1)); + } else { + text.remove(cursor, 1); + } + textDirty = true; + } +} + +void QLineEditPrivate::removeSelectedText() +{ + if (selstart < selend && selend <= (int) text.length()) { + separate(); + int i ; + addCommand(Command(SetSelection, cursor, 0, selstart, selend)); + if (selstart <= cursor && cursor < selend) { + // cursor is within the selection. Split up the commands + // to be able to restore the correct cursor position + for (i = cursor; i >= selstart; --i) + addCommand (Command(DeleteSelection, i, text.at(i), -1, 1)); + for (i = selend - 1; i > cursor; --i) + addCommand (Command(DeleteSelection, i - cursor + selstart - 1, text.at(i), -1, -1)); + } else { + for (i = selend-1; i >= selstart; --i) + addCommand (Command(RemoveSelection, i, text.at(i), -1, -1)); + } + if (maskData) { + text.replace(selstart, selend - selstart, clearString(selstart, selend - selstart)); + for (int i = 0; i < selend - selstart; ++i) + addCommand(Command(Insert, selstart + i, text.at(selstart + i), -1, -1)); + } else { + text.remove(selstart, selend - selstart); + } + if (cursor > selstart) + cursor -= qMin(cursor, selend) - selstart; + deselect(); + textDirty = true; + + // adjust hscroll to avoid gap + const int minRB = qMax(0, -q_func()->fontMetrics().minRightBearing()); + updateTextLayout(); + const QTextLine line = textLayout.lineAt(0); + const int widthUsed = qRound(line.naturalTextWidth()) + 1 + minRB; + hscroll = qMin(hscroll, widthUsed); + } +} + +void QLineEditPrivate::parseInputMask(const QString &maskFields) +{ + int delimiter = maskFields.indexOf(QLatin1Char(';')); + if (maskFields.isEmpty() || delimiter == 0) { + if (maskData) { + delete [] maskData; + maskData = 0; + maxLength = 32767; + setText(QString()); + } + return; + } + + if (delimiter == -1) { + blank = QLatin1Char(' '); + inputMask = maskFields; + } else { + inputMask = maskFields.left(delimiter); + blank = (delimiter + 1 < maskFields.length()) ? maskFields[delimiter + 1] : QLatin1Char(' '); + } + + // calculate maxLength / maskData length + maxLength = 0; + QChar c = 0; + for (int i=0; i<inputMask.length(); i++) { + c = inputMask.at(i); + if (i > 0 && inputMask.at(i-1) == QLatin1Char('\\')) { + maxLength++; + continue; + } + if (c != QLatin1Char('\\') && c != QLatin1Char('!') && + c != QLatin1Char('<') && c != QLatin1Char('>') && + c != QLatin1Char('{') && c != QLatin1Char('}') && + c != QLatin1Char('[') && c != QLatin1Char(']')) + maxLength++; + } + + delete [] maskData; + maskData = new MaskInputData[maxLength]; + + MaskInputData::Casemode m = MaskInputData::NoCaseMode; + c = 0; + bool s; + bool escape = false; + int index = 0; + for (int i = 0; i < inputMask.length(); i++) { + c = inputMask.at(i); + if (escape) { + s = true; + maskData[index].maskChar = c; + maskData[index].separator = s; + maskData[index].caseMode = m; + index++; + escape = false; + } else if (c == QLatin1Char('<')) { + m = MaskInputData::Lower; + } else if (c == QLatin1Char('>')) { + m = MaskInputData::Upper; + } else if (c == QLatin1Char('!')) { + m = MaskInputData::NoCaseMode; + } else if (c != QLatin1Char('{') && c != QLatin1Char('}') && c != QLatin1Char('[') && c != QLatin1Char(']')) { + switch (c.unicode()) { + case 'A': + case 'a': + case 'N': + case 'n': + case 'X': + case 'x': + case '9': + case '0': + case 'D': + case 'd': + case '#': + case 'H': + case 'h': + case 'B': + case 'b': + s = false; + break; + case '\\': + escape = true; + default: + s = true; + break; + } + + if (!escape) { + maskData[index].maskChar = c; + maskData[index].separator = s; + maskData[index].caseMode = m; + index++; + } + } + } + setText(text); +} + + +/* checks if the key is valid compared to the inputMask */ +bool QLineEditPrivate::isValidInput(QChar key, QChar mask) const +{ + switch (mask.unicode()) { + case 'A': + if (key.isLetter()) + return true; + break; + case 'a': + if (key.isLetter() || key == blank) + return true; + break; + case 'N': + if (key.isLetterOrNumber()) + return true; + break; + case 'n': + if (key.isLetterOrNumber() || key == blank) + return true; + break; + case 'X': + if (key.isPrint()) + return true; + break; + case 'x': + if (key.isPrint() || key == blank) + return true; + break; + case '9': + if (key.isNumber()) + return true; + break; + case '0': + if (key.isNumber() || key == blank) + return true; + break; + case 'D': + if (key.isNumber() && key.digitValue() > 0) + return true; + break; + case 'd': + if ((key.isNumber() && key.digitValue() > 0) || key == blank) + return true; + break; + case '#': + if (key.isNumber() || key == QLatin1Char('+') || key == QLatin1Char('-') || key == blank) + return true; + break; + case 'B': + if (key == QLatin1Char('0') || key == QLatin1Char('1')) + return true; + break; + case 'b': + if (key == QLatin1Char('0') || key == QLatin1Char('1') || key == blank) + return true; + break; + case 'H': + if (key.isNumber() || (key >= QLatin1Char('a') && key <= QLatin1Char('f')) || (key >= QLatin1Char('A') && key <= QLatin1Char('F'))) + return true; + break; + case 'h': + if (key.isNumber() || (key >= QLatin1Char('a') && key <= QLatin1Char('f')) || (key >= QLatin1Char('A') && key <= QLatin1Char('F')) || key == blank) + return true; + break; + default: + break; + } + return false; +} + +bool QLineEditPrivate::hasAcceptableInput(const QString &str) const +{ +#ifndef QT_NO_VALIDATOR + QString textCopy = str; + int cursorCopy = cursor; + if (validator && validator->validate(textCopy, cursorCopy) + != QValidator::Acceptable) + return false; +#endif + + if (!maskData) + return true; + + if (str.length() != maxLength) + return false; + + for (int i=0; i < maxLength; ++i) { + if (maskData[i].separator) { + if (str.at(i) != maskData[i].maskChar) + return false; + } else { + if (!isValidInput(str.at(i), maskData[i].maskChar)) + return false; + } + } + return true; +} + +/* + Applies the inputMask on \a str starting from position \a pos in the mask. \a clear + specifies from where characters should be gotten when a separator is met in \a str - true means + that blanks will be used, false that previous input is used. + Calling this when no inputMask is set is undefined. +*/ +QString QLineEditPrivate::maskString(uint pos, const QString &str, bool clear) const +{ + if (pos >= (uint)maxLength) + return QString::fromLatin1(""); + + QString fill; + fill = clear ? clearString(0, maxLength) : text; + + int strIndex = 0; + QString s = QString::fromLatin1(""); + int i = pos; + while (i < maxLength) { + if (strIndex < str.length()) { + if (maskData[i].separator) { + s += maskData[i].maskChar; + if (str[(int)strIndex] == maskData[i].maskChar) + strIndex++; + ++i; + } else { + if (isValidInput(str[(int)strIndex], maskData[i].maskChar)) { + switch (maskData[i].caseMode) { + case MaskInputData::Upper: + s += str[(int)strIndex].toUpper(); + break; + case MaskInputData::Lower: + s += str[(int)strIndex].toLower(); + break; + default: + s += str[(int)strIndex]; + } + ++i; + } else { + // search for separator first + int n = findInMask(i, true, true, str[(int)strIndex]); + if (n != -1) { + if (str.length() != 1 || i == 0 || (i > 0 && (!maskData[i-1].separator || maskData[i-1].maskChar != str[(int)strIndex]))) { + s += fill.mid(i, n-i+1); + i = n + 1; // update i to find + 1 + } + } else { + // search for valid blank if not + n = findInMask(i, true, false, str[(int)strIndex]); + if (n != -1) { + s += fill.mid(i, n-i); + switch (maskData[n].caseMode) { + case MaskInputData::Upper: + s += str[(int)strIndex].toUpper(); + break; + case MaskInputData::Lower: + s += str[(int)strIndex].toLower(); + break; + default: + s += str[(int)strIndex]; + } + i = n + 1; // updates i to find + 1 + } + } + } + strIndex++; + } + } else + break; + } + + return s; +} + + + +/* + Returns a "cleared" string with only separators and blank chars. + Calling this when no inputMask is set is undefined. +*/ +QString QLineEditPrivate::clearString(uint pos, uint len) const +{ + if (pos >= (uint)maxLength) + return QString(); + + QString s; + int end = qMin((uint)maxLength, pos + len); + for (int i=pos; i<end; i++) + if (maskData[i].separator) + s += maskData[i].maskChar; + else + s += blank; + + return s; +} + +/* + Strips blank parts of the input in a QLineEdit when an inputMask is set, + separators are still included. Typically "127.0__.0__.1__" becomes "127.0.0.1". +*/ +QString QLineEditPrivate::stripString(const QString &str) const +{ + if (!maskData) + return str; + + QString s; + int end = qMin(maxLength, (int)str.length()); + for (int i=0; i < end; i++) + if (maskData[i].separator) + s += maskData[i].maskChar; + else + if (str[i] != blank) + s += str[i]; + + return s; +} + +/* searches forward/backward in maskData for either a separator or a blank */ +int QLineEditPrivate::findInMask(int pos, bool forward, bool findSeparator, QChar searchChar) const +{ + if (pos >= maxLength || pos < 0) + return -1; + + int end = forward ? maxLength : -1; + int step = forward ? 1 : -1; + int i = pos; + + while (i != end) { + if (findSeparator) { + if (maskData[i].separator && maskData[i].maskChar == searchChar) + return i; + } else { + if (!maskData[i].separator) { + if (searchChar.isNull()) + return i; + else if (isValidInput(searchChar, maskData[i].maskChar)) + return i; + } + } + i += step; + } + return -1; +} + +void QLineEditPrivate::undo(int until) +{ + if (!isUndoAvailable()) + return; + deselect(); + while (undoState && undoState > until) { + Command& cmd = history[--undoState]; + switch (cmd.type) { + case Insert: + text.remove(cmd.pos, 1); + cursor = cmd.pos; + break; + case SetSelection: + selstart = cmd.selStart; + selend = cmd.selEnd; + cursor = cmd.pos; + break; + case Remove: + case RemoveSelection: + text.insert(cmd.pos, cmd.uc); + cursor = cmd.pos + 1; + break; + case Delete: + case DeleteSelection: + text.insert(cmd.pos, cmd.uc); + cursor = cmd.pos; + break; + case Separator: + continue; + } + if (until < 0 && undoState) { + Command& next = history[undoState-1]; + if (next.type != cmd.type && next.type < RemoveSelection + && (cmd.type < RemoveSelection || next.type == Separator)) + break; + } + } + textDirty = true; + emitCursorPositionChanged(); +} + +void QLineEditPrivate::redo() { + if (!isRedoAvailable()) + return; + deselect(); + while (undoState < (int)history.size()) { + Command& cmd = history[undoState++]; + switch (cmd.type) { + case Insert: + text.insert(cmd.pos, cmd.uc); + cursor = cmd.pos + 1; + break; + case SetSelection: + selstart = cmd.selStart; + selend = cmd.selEnd; + cursor = cmd.pos; + break; + case Remove: + case Delete: + case RemoveSelection: + case DeleteSelection: + text.remove(cmd.pos, 1); + cursor = cmd.pos; + break; + case Separator: + selstart = cmd.selStart; + selend = cmd.selEnd; + cursor = cmd.pos; + break; + } + if (undoState < (int)history.size()) { + Command& next = history[undoState]; + if (next.type != cmd.type && cmd.type < RemoveSelection && next.type != Separator + && (next.type < RemoveSelection || cmd.type == Separator)) + break; + } + } + textDirty = true; + emitCursorPositionChanged(); +} + +/*! + \fn void QLineEdit::repaintArea(int a, int b) + + Use update() instead. +*/ + +/*! + \fn void QLineEdit::cursorLeft(bool mark, int steps) + + Use cursorForward() with a negative number of steps instead. For + example, cursorForward(mark, -steps). +*/ + +/*! + \fn void QLineEdit::cursorRight(bool mark, int steps) + + Use cursorForward() instead. +*/ + +/*! + \fn bool QLineEdit::frame() const + + Use hasFrame() instead. +*/ + +/*! + \fn void QLineEdit::clearValidator() + + Use setValidator(0) instead. +*/ + +/*! + \fn bool QLineEdit::hasMarkedText() const + + Use hasSelectedText() instead. +*/ + +/*! + \fn QString QLineEdit::markedText() const + + Use selectedText() instead. +*/ + +/*! + \fn void QLineEdit::setFrameRect(QRect) + \internal +*/ + +/*! + \fn QRect QLineEdit::frameRect() const + \internal +*/ +/*! + \enum QLineEdit::DummyFrame + \internal + + \value Box + \value Sunken + \value Plain + \value Raised + \value MShadow + \value NoFrame + \value Panel + \value StyledPanel + \value HLine + \value VLine + \value GroupBoxPanel + \value WinPanel + \value ToolBarPanel + \value MenuBarPanel + \value PopupPanel + \value LineEditPanel + \value TabWidgetPanel + \value MShape +*/ + +/*! + \fn void QLineEdit::setFrameShadow(DummyFrame) + \internal +*/ + +/*! + \fn DummyFrame QLineEdit::frameShadow() const + \internal +*/ + +/*! + \fn void QLineEdit::setFrameShape(DummyFrame) + \internal +*/ + +/*! + \fn DummyFrame QLineEdit::frameShape() const + \internal +*/ + +/*! + \fn void QLineEdit::setFrameStyle(int) + \internal +*/ + +/*! + \fn int QLineEdit::frameStyle() const + \internal +*/ + +/*! + \fn int QLineEdit::frameWidth() const + \internal +*/ + +/*! + \fn void QLineEdit::setLineWidth(int) + \internal +*/ + +/*! + \fn int QLineEdit::lineWidth() const + \internal +*/ + +/*! + \fn void QLineEdit::setMargin(int margin) + Sets the width of the margin around the contents of the widget to \a margin. + + Use QWidget::setContentsMargins() instead. + \sa margin(), QWidget::setContentsMargins() +*/ + +/*! + \fn int QLineEdit::margin() const + Returns the with of the the margin around the contents of the widget. + + Use QWidget::getContentsMargins() instead. + \sa setMargin(), QWidget::getContentsMargins() +*/ + +/*! + \fn void QLineEdit::setMidLineWidth(int) + \internal +*/ + +/*! + \fn int QLineEdit::midLineWidth() const + \internal +*/ + +QT_END_NAMESPACE + +#include "moc_qlineedit.cpp" + +#endif // QT_NO_LINEEDIT |