From e7fb84adb5d5d9cc5c61db3bbd025f206854bcb4 Mon Sep 17 00:00:00 2001 From: Andrew den Exter Date: Fri, 11 Nov 2011 13:47:59 +1000 Subject: [PATCH] Merge QQuickLineControl into QQuickTextInput. There's no clear separation of responsibilty between these classes and keeping them in sync and forwarding signals is a unnecessary overhead that can be avoided by combining them. Task-number: QTBUG-22627 Change-Id: I4350eb3c612b10d4ed34886374889ae893b8183a Reviewed-by: Martin Jones --- src/quick/items/items.pri | 2 - src/quick/items/qquicklinecontrol.cpp | 1517 ----------------- src/quick/items/qquicklinecontrol_p.h | 449 ----- src/quick/items/qquicktextinput.cpp | 1791 +++++++++++++++++--- src/quick/items/qquicktextinput_p.h | 2 +- src/quick/items/qquicktextinput_p_p.h | 341 +++- .../qquicktextinput/tst_qquicktextinput.cpp | 6 +- 7 files changed, 1898 insertions(+), 2210 deletions(-) delete mode 100644 src/quick/items/qquicklinecontrol.cpp delete mode 100644 src/quick/items/qquicklinecontrol_p.h diff --git a/src/quick/items/items.pri b/src/quick/items/items.pri index ec1de05..85ef591 100644 --- a/src/quick/items/items.pri +++ b/src/quick/items/items.pri @@ -16,7 +16,6 @@ HEADERS += \ $$PWD/qquicktext_p.h \ $$PWD/qquicktext_p_p.h \ $$PWD/qquicktextnode_p.h \ - $$PWD/qquicklinecontrol_p.h \ $$PWD/qquicktextinput_p.h \ $$PWD/qquicktextinput_p_p.h \ $$PWD/qquicktextcontrol_p.h \ @@ -85,7 +84,6 @@ SOURCES += \ $$PWD/qquickpainteditem.cpp \ $$PWD/qquicktext.cpp \ $$PWD/qquicktextnode.cpp \ - $$PWD/qquicklinecontrol.cpp \ $$PWD/qquicktextinput.cpp \ $$PWD/qquicktextcontrol.cpp \ $$PWD/qquicktextedit.cpp \ diff --git a/src/quick/items/qquicklinecontrol.cpp b/src/quick/items/qquicklinecontrol.cpp deleted file mode 100644 index 6daf4b1..0000000 --- a/src/quick/items/qquicklinecontrol.cpp +++ /dev/null @@ -1,1517 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** This file may be used under the terms of the GNU Lesser General Public -** License version 2.1 as published by the Free Software Foundation and -** appearing in the file LICENSE.LGPL included in the packaging of this -** file. Please review the following information to ensure the GNU Lesser -** General Public License version 2.1 requirements will be met: -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -// ### -#define QT_NO_ACCESSIBILITY - -#include "qquicklinecontrol_p.h" - -#ifndef QT_NO_LINEEDIT - -#include "qclipboard.h" -#ifndef QT_NO_ACCESSIBILITY -#include "qaccessible.h" -#endif -#include "qguiapplication.h" -#include "qstylehints.h" - -QT_BEGIN_NAMESPACE - -// ### these should come from QStyleHints -const int textCursorWidth = 1; -const bool fullWidthSelection = true; - -/*! - \internal - - Updates the display text based of the current edit text - If the text has changed will emit displayTextChanged() -*/ -void QQuickLineControl::updateDisplayText(bool forceUpdate) -{ - QString orig = m_textLayout.text(); - QString str; - if (m_echoMode == NoEcho) - str = QString::fromLatin1(""); - else - str = m_text; - - if (m_echoMode == Password || (m_echoMode == PasswordEchoOnEdit - && !m_passwordEchoEditing)) - str.fill(m_passwordCharacter); - - // replace certain non-printable characters with spaces (to avoid - // drawing boxes when using fonts that don't have glyphs for such - // characters) - 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); - } - - m_textLayout.setText(str); - - QTextOption option = m_textLayout.textOption(); - option.setTextDirection(m_layoutDirection); - option.setFlags(QTextOption::IncludeTrailingSpaces); - m_textLayout.setTextOption(option); - - m_textLayout.beginLayout(); - QTextLine l = m_textLayout.createLine(); - m_textLayout.endLayout(); - m_ascent = qRound(l.ascent()); - - if (str != orig || forceUpdate) - emit displayTextChanged(str); -} - -#ifndef QT_NO_CLIPBOARD -/*! - \internal - - Copies the currently selected text into the clipboard using the given - \a mode. - - \note If the echo mode is set to a mode other than Normal then copy - will not work. This is to prevent using copy as a method of bypassing - password features of the line control. -*/ -void QQuickLineControl::copy(QClipboard::Mode mode) const -{ - QString t = selectedText(); - if (!t.isEmpty() && m_echoMode == Normal) { - disconnect(QGuiApplication::clipboard(), SIGNAL(selectionChanged()), this, 0); - QGuiApplication::clipboard()->setText(t, mode); - connect(QGuiApplication::clipboard(), SIGNAL(selectionChanged()), - this, SLOT(_q_clipboardChanged())); - } -} - -/*! - \internal - - Inserts the text stored in the application clipboard into the line - control. - - \sa insert() -*/ -void QQuickLineControl::paste(QClipboard::Mode clipboardMode) -{ - QString clip = QGuiApplication::clipboard()->text(clipboardMode); - if (!clip.isEmpty() || hasSelectedText()) { - separate(); //make it a separate undo/redo command - insert(clip); - separate(); - } -} - -#endif // !QT_NO_CLIPBOARD - -/*! - \internal - - Exits preedit mode and commits parts marked as tentative commit -*/ -void QQuickLineControl::commitPreedit() -{ - if (!composeMode()) - return; - - qApp->inputPanel()->reset(); - - if (!m_tentativeCommit.isEmpty()) { - internalInsert(m_tentativeCommit); - m_tentativeCommit.clear(); - finishChange(-1, true/*not used, not documented*/, false); - } - - m_preeditCursor = 0; - setPreeditArea(-1, QString()); - m_textLayout.clearAdditionalFormats(); - updateDisplayText(/*force*/ true); -} - -/*! - \internal - - Handles the behavior for the backspace key or function. - Removes the current selection if there is a selection, otherwise - removes the character prior to the cursor position. - - \sa del() -*/ -void QQuickLineControl::backspace() -{ - int priorState = m_undoState; - if (hasSelectedText()) { - removeSelectedText(); - } else if (m_cursor) { - --m_cursor; - if (m_maskData) - m_cursor = prevMaskBlank(m_cursor); - QChar uc = m_text.at(m_cursor); - if (m_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 = m_text.at(m_cursor - 1); - if (uc.unicode() >= 0xd800 && uc.unicode() < 0xdc00) { - internalDelete(true); - --m_cursor; - } - } - internalDelete(true); - } - finishChange(priorState); -} - -/*! - \internal - - Handles the behavior for the delete key or function. - Removes the current selection if there is a selection, otherwise - removes the character after the cursor position. - - \sa del() -*/ -void QQuickLineControl::del() -{ - int priorState = m_undoState; - if (hasSelectedText()) { - removeSelectedText(); - } else { - int n = m_textLayout.nextCursorPosition(m_cursor) - m_cursor; - while (n--) - internalDelete(); - } - finishChange(priorState); -} - -/*! - \internal - - Inserts the given \a newText at the current cursor position. - If there is any selected text it is removed prior to insertion of - the new text. -*/ -void QQuickLineControl::insert(const QString &newText) -{ - int priorState = m_undoState; - removeSelectedText(); - internalInsert(newText); - finishChange(priorState); -} - -/*! - \internal - - Clears the line control text. -*/ -void QQuickLineControl::clear() -{ - int priorState = m_undoState; - m_selstart = 0; - m_selend = m_text.length(); - removeSelectedText(); - separate(); - finishChange(priorState, /*update*/false, /*edited*/false); -} - -/*! - \internal - - Sets \a length characters from the given \a start position as selected. - The given \a start position must be within the current text for - the line control. If \a length characters cannot be selected, then - the selection will extend to the end of the current text. -*/ -void QQuickLineControl::setSelection(int start, int length) -{ - commitPreedit(); - - if (start < 0 || start > (int)m_text.length()){ - qWarning("QQuickLineControl::setSelection: Invalid start position"); - return; - } - - if (length > 0) { - if (start == m_selstart && start + length == m_selend && m_cursor == m_selend) - return; - m_selstart = start; - m_selend = qMin(start + length, (int)m_text.length()); - m_cursor = m_selend; - } else if (length < 0){ - if (start == m_selend && start + length == m_selstart && m_cursor == m_selstart) - return; - m_selstart = qMax(start + length, 0); - m_selend = start; - m_cursor = m_selstart; - } else if (m_selstart != m_selend) { - m_selstart = 0; - m_selend = 0; - m_cursor = start; - } else { - m_cursor = start; - emitCursorPositionChanged(); - return; - } - emit selectionChanged(); - emitCursorPositionChanged(); -} - -void QQuickLineControl::_q_clipboardChanged() -{ -} - -void QQuickLineControl::_q_deleteSelected() -{ - if (!hasSelectedText()) - return; - - int priorState = m_undoState; - emit resetInputContext(); - removeSelectedText(); - separate(); - finishChange(priorState); -} - -/*! - \internal - - Initializes the line control with a starting text value of \a txt. -*/ -void QQuickLineControl::init(const QString &txt) -{ - m_text = txt; - - updateDisplayText(); - m_cursor = m_text.length(); -} - -/*! - \internal - - Sets the password echo editing to \a editing. If password echo editing - is true, then the text of the password is displayed even if the echo - mode is set to QLineEdit::PasswordEchoOnEdit. Password echoing editing - does not affect other echo modes. -*/ -void QQuickLineControl::updatePasswordEchoEditing(bool editing) -{ - m_passwordEchoEditing = editing; - updateDisplayText(); -} - -/*! - \internal - - Returns the cursor position of the given \a x pixel value in relation - to the displayed text. The given \a betweenOrOn specified what kind - of cursor position is requested. -*/ -int QQuickLineControl::xToPos(int x, QTextLine::CursorPosition betweenOrOn) const -{ - return m_textLayout.lineAt(0).xToCursor(x, betweenOrOn); -} - -/*! - \internal - - Returns the bounds of the current cursor, as defined as a - between characters cursor. -*/ -QRect QQuickLineControl::cursorRect() const -{ - QTextLine l = m_textLayout.lineAt(0); - int c = m_cursor; - if (m_preeditCursor != -1) - c += m_preeditCursor; - int cix = qRound(l.cursorToX(c)); - int w = m_cursorWidth; - int ch = l.height() + 1; - - return QRect(cix-5, 0, w+9, ch); -} - -QString QQuickLineControl::text() const -{ - QString content = m_text; - if (!m_tentativeCommit.isEmpty()) - content.insert(m_cursor, m_tentativeCommit); - QString res = m_maskData ? stripString(content) : content; - return (res.isNull() ? QString::fromLatin1("") : res); -} - -// like text() but doesn't include preedit -QString QQuickLineControl::realText() const -{ - QString res = m_maskData ? stripString(m_text) : m_text; - return (res.isNull() ? QString::fromLatin1("") : res); -} - -void QQuickLineControl::setText(const QString &txt) -{ - if (composeMode()) - qApp->inputPanel()->reset(); - m_tentativeCommit.clear(); - internalSetText(txt, -1, false); -} - -/*! - \internal - - Fixes the current text so that it is valid given any set validators. - - Returns true if the text was changed. Otherwise returns false. -*/ -bool QQuickLineControl::fixup() // this function assumes that validate currently returns != Acceptable -{ -#ifndef QT_NO_VALIDATOR - if (m_validator) { - QString textCopy = m_text; - int cursorCopy = m_cursor; - m_validator->fixup(textCopy); - if (m_validator->validate(textCopy, cursorCopy) == QValidator::Acceptable) { - if (textCopy != m_text || cursorCopy != m_cursor) - internalSetText(textCopy, cursorCopy); - return true; - } - } -#endif - return false; -} - -/*! - \internal - - Moves the cursor to the given position \a pos. If \a mark is true will - adjust the currently selected text. -*/ -void QQuickLineControl::moveCursor(int pos, bool mark) -{ - commitPreedit(); - - if (pos != m_cursor) { - separate(); - if (m_maskData) - pos = pos > m_cursor ? nextMaskBlank(pos) : prevMaskBlank(pos); - } - if (mark) { - int anchor; - if (m_selend > m_selstart && m_cursor == m_selstart) - anchor = m_selend; - else if (m_selend > m_selstart && m_cursor == m_selend) - anchor = m_selstart; - else - anchor = m_cursor; - m_selstart = qMin(anchor, pos); - m_selend = qMax(anchor, pos); - updateDisplayText(); - } else { - internalDeselect(); - } - m_cursor = pos; - if (mark || m_selDirty) { - m_selDirty = false; - emit selectionChanged(); - } - emitCursorPositionChanged(); -} - -/*! - \internal - - Applies the given input method event \a event to the text of the line - control -*/ -void QQuickLineControl::processInputMethodEvent(QInputMethodEvent *event) -{ - int priorState = -1; - bool isGettingInput = !event->commitString().isEmpty() - || event->preeditString() != preeditAreaText() - || event->replacementLength() > 0; - bool cursorPositionChanged = false; - bool selectionChange = false; - - if (isGettingInput) { - // If any text is being input, remove selected text. - priorState = m_undoState; - if (echoMode() == PasswordEchoOnEdit && !passwordEchoEditing()) { - updatePasswordEchoEditing(true); - m_selstart = 0; - m_selend = m_text.length(); - } - removeSelectedText(); - } - - int c = m_cursor; // cursor position after insertion of commit string - if (event->replacementStart() <= 0) - c += event->commitString().length() - qMin(-event->replacementStart(), event->replacementLength()); - - m_cursor += event->replacementStart(); - if (m_cursor < 0) - m_cursor = 0; - - // insert commit string - if (event->replacementLength()) { - m_selstart = m_cursor; - m_selend = m_selstart + event->replacementLength(); - removeSelectedText(); - } - if (!event->commitString().isEmpty()) { - internalInsert(event->commitString()); - cursorPositionChanged = true; - } - - m_cursor = qBound(0, c, m_text.length()); - - for (int i = 0; i < event->attributes().size(); ++i) { - const QInputMethodEvent::Attribute &a = event->attributes().at(i); - if (a.type == QInputMethodEvent::Selection) { - m_cursor = qBound(0, a.start + a.length, m_text.length()); - if (a.length) { - m_selstart = qMax(0, qMin(a.start, m_text.length())); - m_selend = m_cursor; - if (m_selend < m_selstart) { - qSwap(m_selstart, m_selend); - } - selectionChange = true; - } else { - m_selstart = m_selend = 0; - } - cursorPositionChanged = true; - } - } -#ifndef QT_NO_IM - setPreeditArea(m_cursor, event->preeditString()); -#endif //QT_NO_IM - const int oldPreeditCursor = m_preeditCursor; - m_preeditCursor = event->preeditString().length(); - m_hideCursor = false; - QList formats; - for (int i = 0; i < event->attributes().size(); ++i) { - const QInputMethodEvent::Attribute &a = event->attributes().at(i); - if (a.type == QInputMethodEvent::Cursor) { - m_preeditCursor = a.start; - m_hideCursor = !a.length; - } else if (a.type == QInputMethodEvent::TextFormat) { - QTextCharFormat f = qvariant_cast(a.value).toCharFormat(); - if (f.isValid()) { - QTextLayout::FormatRange o; - o.start = a.start + m_cursor; - o.length = a.length; - o.format = f; - formats.append(o); - } - } - } - m_textLayout.setAdditionalFormats(formats); - updateDisplayText(/*force*/ true); - if (cursorPositionChanged) - emitCursorPositionChanged(); - else if (m_preeditCursor != oldPreeditCursor) - emit updateMicroFocus(); - - bool tentativeCommitChanged = (m_tentativeCommit != event->tentativeCommitString()); - - if (tentativeCommitChanged) { - m_textDirty = true; - m_tentativeCommit = event->tentativeCommitString(); - } - - if (isGettingInput || tentativeCommitChanged) - finishChange(priorState); - - if (selectionChange) - emit selectionChanged(); -} - -/*! - \internal - - Sets the selection to cover the word at the given cursor position. - The word boundaries are defined by the behavior of QTextLayout::SkipWords - cursor mode. -*/ -void QQuickLineControl::selectWordAtPos(int cursor) -{ - int next = cursor + 1; - if (next > end()) - --next; - int c = m_textLayout.previousCursorPosition(next, QTextLayout::SkipWords); - moveCursor(c, false); - // ## text layout should support end of words. - int end = m_textLayout.nextCursorPosition(c, QTextLayout::SkipWords); - while (end > cursor && m_text[end-1].isSpace()) - --end; - moveCursor(end, true); -} - -/*! - \internal - - Completes a change to the line control text. If the change is not valid - will undo the line control state back to the given \a validateFromState. - - If \a edited is true and the change is valid, will emit textEdited() in - addition to textChanged(). Otherwise only emits textChanged() on a valid - change. - - The \a update value is currently unused. -*/ -bool QQuickLineControl::finishChange(int validateFromState, bool update, bool edited) -{ - Q_UNUSED(update) - - if (m_textDirty) { - // do validation - bool wasValidInput = m_validInput; - m_validInput = true; -#ifndef QT_NO_VALIDATOR - if (m_validator) { - QString textCopy = m_text; - int cursorCopy = m_cursor; - m_validInput = (m_validator->validate(textCopy, cursorCopy) != QValidator::Invalid); - if (m_validInput) { - if (m_text != textCopy) { - internalSetText(textCopy, cursorCopy); - return true; - } - m_cursor = cursorCopy; - - if (!m_tentativeCommit.isEmpty()) { - textCopy.insert(m_cursor, m_tentativeCommit); - bool validInput = (m_validator->validate(textCopy, cursorCopy) != QValidator::Invalid); - if (!validInput) - m_tentativeCommit.clear(); - } - } else { - m_tentativeCommit.clear(); - } - } -#endif - if (validateFromState >= 0 && wasValidInput && !m_validInput) { - if (m_transactions.count()) - return false; - internalUndo(validateFromState); - m_history.resize(m_undoState); - if (m_modifiedState > m_undoState) - m_modifiedState = -1; - m_validInput = true; - m_textDirty = false; - } - updateDisplayText(); - - if (m_textDirty) { - m_textDirty = false; - QString actualText = text(); - if (edited) - emit textEdited(actualText); - emit textChanged(actualText); - } - } - if (m_selDirty) { - m_selDirty = false; - emit selectionChanged(); - } - emitCursorPositionChanged(); - return true; -} - -/*! - \internal - - An internal function for setting the text of the line control. -*/ -void QQuickLineControl::internalSetText(const QString &txt, int pos, bool edited) -{ - internalDeselect(); - emit resetInputContext(); - QString oldText = m_text; - if (m_maskData) { - m_text = maskString(0, txt, true); - m_text += clearString(m_text.length(), m_maxLength - m_text.length()); - } else { - m_text = txt.isEmpty() ? txt : txt.left(m_maxLength); - } - m_history.clear(); - m_modifiedState = m_undoState = 0; - m_cursor = (pos < 0 || pos > m_text.length()) ? m_text.length() : pos; - m_textDirty = (oldText != m_text); - - bool changed = finishChange(-1, true, edited); -#ifdef QT_NO_ACCESSIBILITY - Q_UNUSED(changed) -#else - if (changed) - QAccessible::updateAccessibility(parent(), 0, QAccessible::TextUpdated); -#endif -} - - -/*! - \internal - - Adds the given \a command to the undo history - of the line control. Does not apply the command. -*/ -void QQuickLineControl::addCommand(const Command &cmd) -{ - if (m_separator && m_undoState && m_history[m_undoState - 1].type != Separator) { - m_history.resize(m_undoState + 2); - m_history[m_undoState++] = Command(Separator, m_cursor, 0, m_selstart, m_selend); - } else { - m_history.resize(m_undoState + 1); - } - m_separator = false; - m_history[m_undoState++] = cmd; -} - -/*! - \internal - - Inserts the given string \a s into the line - control. - - Also adds the appropriate commands into the undo history. - This function does not call finishChange(), and may leave the text - in an invalid state. -*/ -void QQuickLineControl::internalInsert(const QString &s) -{ - if (hasSelectedText()) - addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend)); - if (m_maskData) { - QString ms = maskString(m_cursor, s); - for (int i = 0; i < (int) ms.length(); ++i) { - addCommand (Command(DeleteSelection, m_cursor + i, m_text.at(m_cursor + i), -1, -1)); - addCommand(Command(Insert, m_cursor + i, ms.at(i), -1, -1)); - } - m_text.replace(m_cursor, ms.length(), ms); - m_cursor += ms.length(); - m_cursor = nextMaskBlank(m_cursor); - m_textDirty = true; - } else { - int remaining = m_maxLength - m_text.length(); - if (remaining != 0) { - m_text.insert(m_cursor, s.left(remaining)); - for (int i = 0; i < (int) s.left(remaining).length(); ++i) - addCommand(Command(Insert, m_cursor++, s.at(i), -1, -1)); - m_textDirty = true; - } - } -} - -/*! - \internal - - deletes a single character from the current text. If \a wasBackspace, - the character prior to the cursor is removed. Otherwise the character - after the cursor is removed. - - Also adds the appropriate commands into the undo history. - This function does not call finishChange(), and may leave the text - in an invalid state. -*/ -void QQuickLineControl::internalDelete(bool wasBackspace) -{ - if (m_cursor < (int) m_text.length()) { - if (hasSelectedText()) - addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend)); - addCommand(Command((CommandType)((m_maskData ? 2 : 0) + (wasBackspace ? Remove : Delete)), - m_cursor, m_text.at(m_cursor), -1, -1)); - if (m_maskData) { - m_text.replace(m_cursor, 1, clearString(m_cursor, 1)); - addCommand(Command(Insert, m_cursor, m_text.at(m_cursor), -1, -1)); - } else { - m_text.remove(m_cursor, 1); - } - m_textDirty = true; - } -} - -/*! - \internal - - removes the currently selected text from the line control. - - Also adds the appropriate commands into the undo history. - This function does not call finishChange(), and may leave the text - in an invalid state. -*/ -void QQuickLineControl::removeSelectedText() -{ - if (m_selstart < m_selend && m_selend <= (int) m_text.length()) { - separate(); - int i ; - addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend)); - if (m_selstart <= m_cursor && m_cursor < m_selend) { - // cursor is within the selection. Split up the commands - // to be able to restore the correct cursor position - for (i = m_cursor; i >= m_selstart; --i) - addCommand (Command(DeleteSelection, i, m_text.at(i), -1, 1)); - for (i = m_selend - 1; i > m_cursor; --i) - addCommand (Command(DeleteSelection, i - m_cursor + m_selstart - 1, m_text.at(i), -1, -1)); - } else { - for (i = m_selend-1; i >= m_selstart; --i) - addCommand (Command(RemoveSelection, i, m_text.at(i), -1, -1)); - } - if (m_maskData) { - m_text.replace(m_selstart, m_selend - m_selstart, clearString(m_selstart, m_selend - m_selstart)); - for (int i = 0; i < m_selend - m_selstart; ++i) - addCommand(Command(Insert, m_selstart + i, m_text.at(m_selstart + i), -1, -1)); - } else { - m_text.remove(m_selstart, m_selend - m_selstart); - } - if (m_cursor > m_selstart) - m_cursor -= qMin(m_cursor, m_selend) - m_selstart; - internalDeselect(); - m_textDirty = true; - } -} - -/*! - \internal - - Parses the input mask specified by \a maskFields to generate - the mask data used to handle input masks. -*/ -void QQuickLineControl::parseInputMask(const QString &maskFields) -{ - int delimiter = maskFields.indexOf(QLatin1Char(';')); - if (maskFields.isEmpty() || delimiter == 0) { - if (m_maskData) { - delete [] m_maskData; - m_maskData = 0; - m_maxLength = 32767; - internalSetText(QString()); - } - return; - } - - if (delimiter == -1) { - m_blank = QLatin1Char(' '); - m_inputMask = maskFields; - } else { - m_inputMask = maskFields.left(delimiter); - m_blank = (delimiter + 1 < maskFields.length()) ? maskFields[delimiter + 1] : QLatin1Char(' '); - } - - // calculate m_maxLength / m_maskData length - m_maxLength = 0; - QChar c = 0; - for (int i=0; i 0 && m_inputMask.at(i-1) == QLatin1Char('\\')) { - m_maxLength++; - continue; - } - if (c != QLatin1Char('\\') && c != QLatin1Char('!') && - c != QLatin1Char('<') && c != QLatin1Char('>') && - c != QLatin1Char('{') && c != QLatin1Char('}') && - c != QLatin1Char('[') && c != QLatin1Char(']')) - m_maxLength++; - } - - delete [] m_maskData; - m_maskData = new MaskInputData[m_maxLength]; - - MaskInputData::Casemode m = MaskInputData::NoCaseMode; - c = 0; - bool s; - bool escape = false; - int index = 0; - for (int i = 0; i < m_inputMask.length(); i++) { - c = m_inputMask.at(i); - if (escape) { - s = true; - m_maskData[index].maskChar = c; - m_maskData[index].separator = s; - m_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) { - m_maskData[index].maskChar = c; - m_maskData[index].separator = s; - m_maskData[index].caseMode = m; - index++; - } - } - } - internalSetText(m_text); -} - - -/*! - \internal - - checks if the key is valid compared to the inputMask -*/ -bool QQuickLineControl::isValidInput(QChar key, QChar mask) const -{ - switch (mask.unicode()) { - case 'A': - if (key.isLetter()) - return true; - break; - case 'a': - if (key.isLetter() || key == m_blank) - return true; - break; - case 'N': - if (key.isLetterOrNumber()) - return true; - break; - case 'n': - if (key.isLetterOrNumber() || key == m_blank) - return true; - break; - case 'X': - if (key.isPrint()) - return true; - break; - case 'x': - if (key.isPrint() || key == m_blank) - return true; - break; - case '9': - if (key.isNumber()) - return true; - break; - case '0': - if (key.isNumber() || key == m_blank) - return true; - break; - case 'D': - if (key.isNumber() && key.digitValue() > 0) - return true; - break; - case 'd': - if ((key.isNumber() && key.digitValue() > 0) || key == m_blank) - return true; - break; - case '#': - if (key.isNumber() || key == QLatin1Char('+') || key == QLatin1Char('-') || key == m_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 == m_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 == m_blank) - return true; - break; - default: - break; - } - return false; -} - -/*! - \internal - - Returns true if the given text \a str is valid for any - validator or input mask set for the line control. - - Otherwise returns false -*/ -bool QQuickLineControl::hasAcceptableInput(const QString &str) const -{ -#ifndef QT_NO_VALIDATOR - QString textCopy = str; - int cursorCopy = m_cursor; - if (m_validator && m_validator->validate(textCopy, cursorCopy) - != QValidator::Acceptable) - return false; -#endif - - if (!m_maskData) - return true; - - if (str.length() != m_maxLength) - return false; - - for (int i=0; i < m_maxLength; ++i) { - if (m_maskData[i].separator) { - if (str.at(i) != m_maskData[i].maskChar) - return false; - } else { - if (!isValidInput(str.at(i), m_maskData[i].maskChar)) - return false; - } - } - return true; -} - -/*! - \internal - - 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 QQuickLineControl::maskString(uint pos, const QString &str, bool clear) const -{ - if (pos >= (uint)m_maxLength) - return QString::fromLatin1(""); - - QString fill; - fill = clear ? clearString(0, m_maxLength) : m_text; - - int strIndex = 0; - QString s = QString::fromLatin1(""); - int i = pos; - while (i < m_maxLength) { - if (strIndex < str.length()) { - if (m_maskData[i].separator) { - s += m_maskData[i].maskChar; - if (str[(int)strIndex] == m_maskData[i].maskChar) - strIndex++; - ++i; - } else { - if (isValidInput(str[(int)strIndex], m_maskData[i].maskChar)) { - switch (m_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 && (!m_maskData[i-1].separator || m_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 m_blank if not - n = findInMask(i, true, false, str[(int)strIndex]); - if (n != -1) { - s += fill.mid(i, n-i); - switch (m_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; -} - - - -/*! - \internal - - Returns a "cleared" string with only separators and blank chars. - Calling this when no inputMask is set is undefined. -*/ -QString QQuickLineControl::clearString(uint pos, uint len) const -{ - if (pos >= (uint)m_maxLength) - return QString(); - - QString s; - int end = qMin((uint)m_maxLength, pos + len); - for (int i = pos; i < end; ++i) - if (m_maskData[i].separator) - s += m_maskData[i].maskChar; - else - s += m_blank; - - return s; -} - -/*! - \internal - - Strips blank parts of the input in a QQuickLineControl when an inputMask is set, - separators are still included. Typically "127.0__.0__.1__" becomes "127.0.0.1". -*/ -QString QQuickLineControl::stripString(const QString &str) const -{ - if (!m_maskData) - return str; - - QString s; - int end = qMin(m_maxLength, (int)str.length()); - for (int i = 0; i < end; ++i) - if (m_maskData[i].separator) - s += m_maskData[i].maskChar; - else - if (str[i] != m_blank) - s += str[i]; - - return s; -} - -/*! - \internal - searches forward/backward in m_maskData for either a separator or a m_blank -*/ -int QQuickLineControl::findInMask(int pos, bool forward, bool findSeparator, QChar searchChar) const -{ - if (pos >= m_maxLength || pos < 0) - return -1; - - int end = forward ? m_maxLength : -1; - int step = forward ? 1 : -1; - int i = pos; - - while (i != end) { - if (findSeparator) { - if (m_maskData[i].separator && m_maskData[i].maskChar == searchChar) - return i; - } else { - if (!m_maskData[i].separator) { - if (searchChar.isNull()) - return i; - else if (isValidInput(searchChar, m_maskData[i].maskChar)) - return i; - } - } - i += step; - } - return -1; -} - -void QQuickLineControl::internalUndo(int until) -{ - if (!isUndoAvailable()) - return; - internalDeselect(); - while (m_undoState && m_undoState > until) { - Command& cmd = m_history[--m_undoState]; - switch (cmd.type) { - case Insert: - m_text.remove(cmd.pos, 1); - m_cursor = cmd.pos; - break; - case SetSelection: - m_selstart = cmd.selStart; - m_selend = cmd.selEnd; - m_cursor = cmd.pos; - break; - case Remove: - case RemoveSelection: - m_text.insert(cmd.pos, cmd.uc); - m_cursor = cmd.pos + 1; - break; - case Delete: - case DeleteSelection: - m_text.insert(cmd.pos, cmd.uc); - m_cursor = cmd.pos; - break; - case Separator: - continue; - } - if (until < 0 && m_undoState) { - Command& next = m_history[m_undoState-1]; - if (next.type != cmd.type && next.type < RemoveSelection - && (cmd.type < RemoveSelection || next.type == Separator)) - break; - } - } - m_textDirty = true; - emitCursorPositionChanged(); -} - -void QQuickLineControl::internalRedo() -{ - if (!isRedoAvailable()) - return; - internalDeselect(); - while (m_undoState < (int)m_history.size()) { - Command& cmd = m_history[m_undoState++]; - switch (cmd.type) { - case Insert: - m_text.insert(cmd.pos, cmd.uc); - m_cursor = cmd.pos + 1; - break; - case SetSelection: - m_selstart = cmd.selStart; - m_selend = cmd.selEnd; - m_cursor = cmd.pos; - break; - case Remove: - case Delete: - case RemoveSelection: - case DeleteSelection: - m_text.remove(cmd.pos, 1); - m_selstart = cmd.selStart; - m_selend = cmd.selEnd; - m_cursor = cmd.pos; - break; - case Separator: - m_selstart = cmd.selStart; - m_selend = cmd.selEnd; - m_cursor = cmd.pos; - break; - } - if (m_undoState < (int)m_history.size()) { - Command& next = m_history[m_undoState]; - if (next.type != cmd.type && cmd.type < RemoveSelection && next.type != Separator - && (next.type < RemoveSelection || cmd.type == Separator)) - break; - } - } - m_textDirty = true; - emitCursorPositionChanged(); -} - -/*! - \internal - - If the current cursor position differs from the last emitted cursor - position, emits cursorPositionChanged(). -*/ -void QQuickLineControl::emitCursorPositionChanged() -{ - if (m_cursor != m_lastCursorPos) { - const int oldLast = m_lastCursorPos; - m_lastCursorPos = m_cursor; - cursorPositionChanged(oldLast, m_cursor); -#ifndef QT_NO_ACCESSIBILITY - QAccessible::updateAccessibility(parent(), 0, QAccessible::TextCaretMoved); -#endif - } -} - - -void QQuickLineControl::setCursorBlinkPeriod(int msec) -{ - if (msec == m_blinkPeriod) - return; - if (m_blinkTimer) { - killTimer(m_blinkTimer); - } - if (msec) { - m_blinkTimer = startTimer(msec / 2); - m_blinkStatus = 1; - } else { - m_blinkTimer = 0; - if (m_blinkStatus == 1) - emit updateNeeded(inputMask().isEmpty() ? cursorRect() : QRect()); - } - m_blinkPeriod = msec; -} - -void QQuickLineControl::resetCursorBlinkTimer() -{ - if (m_blinkPeriod == 0 || m_blinkTimer == 0) - return; - killTimer(m_blinkTimer); - m_blinkTimer = startTimer(m_blinkPeriod / 2); - m_blinkStatus = 1; -} - -void QQuickLineControl::timerEvent(QTimerEvent *event) -{ - if (event->timerId() == m_blinkTimer) { - m_blinkStatus = !m_blinkStatus; - emit updateNeeded(inputMask().isEmpty() ? cursorRect() : QRect()); - } else if (event->timerId() == m_deleteAllTimer) { - killTimer(m_deleteAllTimer); - m_deleteAllTimer = 0; - clear(); - } -} - -void QQuickLineControl::processKeyEvent(QKeyEvent* event) -{ - bool inlineCompletionAccepted = false; - - if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) { - if (hasAcceptableInput() || fixup()) { - emit accepted(); - emit editingFinished(); - } - if (inlineCompletionAccepted) - event->accept(); - else - event->ignore(); - return; - } - - if (echoMode() == PasswordEchoOnEdit - && !passwordEchoEditing() - && !isReadOnly() - && !event->text().isEmpty() - && !(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 - // ### resets current content. dubious code; you can - // navigate with keys up, down, back, and select(?), but if you press - // "left" or "right" it clears? - updatePasswordEchoEditing(true); - clear(); - } - - bool unknown = false; - bool visual = cursorMoveStyle() == Qt::VisualMoveStyle; - - if (false) { - } -#ifndef QT_NO_SHORTCUT - else if (event == QKeySequence::Undo) { - if (!isReadOnly()) - undo(); - } - else if (event == QKeySequence::Redo) { - if (!isReadOnly()) - redo(); - } - else if (event == QKeySequence::SelectAll) { - selectAll(); - } -#ifndef QT_NO_CLIPBOARD - else if (event == QKeySequence::Copy) { - copy(); - } - else if (event == QKeySequence::Paste) { - if (!isReadOnly()) { - QClipboard::Mode mode = QClipboard::Clipboard; - paste(mode); - } - } - else if (event == QKeySequence::Cut) { - if (!isReadOnly()) { - copy(); - del(); - } - } - else if (event == QKeySequence::DeleteEndOfLine) { - if (!isReadOnly()) { - setSelection(cursor(), end()); - copy(); - del(); - } - } -#endif //QT_NO_CLIPBOARD - else if (event == QKeySequence::MoveToStartOfLine || event == QKeySequence::MoveToStartOfBlock) { - home(0); - } - else if (event == QKeySequence::MoveToEndOfLine || event == QKeySequence::MoveToEndOfBlock) { - end(0); - } - else if (event == QKeySequence::SelectStartOfLine || event == QKeySequence::SelectStartOfBlock) { - home(1); - } - else if (event == QKeySequence::SelectEndOfLine || event == QKeySequence::SelectEndOfBlock) { - end(1); - } - else if (event == QKeySequence::MoveToNextChar) { - if (hasSelectedText()) { - moveCursor(selectionEnd(), false); - } else { - cursorForward(0, visual ? 1 : (layoutDirection() == Qt::LeftToRight ? 1 : -1)); - } - } - else if (event == QKeySequence::SelectNextChar) { - cursorForward(1, visual ? 1 : (layoutDirection() == Qt::LeftToRight ? 1 : -1)); - } - else if (event == QKeySequence::MoveToPreviousChar) { - if (hasSelectedText()) { - moveCursor(selectionStart(), false); - } else { - cursorForward(0, visual ? -1 : (layoutDirection() == Qt::LeftToRight ? -1 : 1)); - } - } - else if (event == QKeySequence::SelectPreviousChar) { - cursorForward(1, visual ? -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 (!isReadOnly()) { - 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 (!isReadOnly()) - del(); - } - else if (event == QKeySequence::DeleteEndOfWord) { - if (!isReadOnly()) { - cursorWordForward(true); - del(); - } - } - else if (event == QKeySequence::DeleteStartOfWord) { - if (!isReadOnly()) { - cursorWordBackward(true); - del(); - } - } -#endif // QT_NO_SHORTCUT - else { - bool handled = false; - if (event->modifiers() & Qt::ControlModifier) { - switch (event->key()) { - case Qt::Key_Backspace: - if (!isReadOnly()) { - cursorWordBackward(true); - del(); - } - break; - default: - if (!handled) - unknown = true; - } - } else { // ### check for *no* modifier - switch (event->key()) { - case Qt::Key_Backspace: - if (!isReadOnly()) { - backspace(); - } - break; - default: - if (!handled) - 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); - unknown = false; - } - - if (unknown && !isReadOnly()) { - QString t = event->text(); - if (!t.isEmpty() && t.at(0).isPrint()) { - insert(t); - event->accept(); - return; - } - } - - if (unknown) - event->ignore(); - else - event->accept(); -} - - -QT_END_NAMESPACE - -#endif diff --git a/src/quick/items/qquicklinecontrol_p.h b/src/quick/items/qquicklinecontrol_p.h deleted file mode 100644 index e80fad7..0000000 --- a/src/quick/items/qquicklinecontrol_p.h +++ /dev/null @@ -1,449 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** This file may be used under the terms of the GNU Lesser General Public -** License version 2.1 as published by the Free Software Foundation and -** appearing in the file LICENSE.LGPL included in the packaging of this -** file. Please review the following information to ensure the GNU Lesser -** General Public License version 2.1 requirements will be met: -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKLINECONTROL_P_H -#define QQUICKLINECONTROL_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "QtCore/qglobal.h" - -#include "QtGui/qtextlayout.h" -#include "QtCore/qpointer.h" -#include "QtGui/qclipboard.h" -#include "QtGui/qvalidator.h" -#include "QtGui/qpalette.h" -#include "QtGui/qguiapplication.h" -#include "QtGui/qinputpanel.h" -#include "QtCore/qpoint.h" - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Gui) - -class Q_AUTOTEST_EXPORT QQuickLineControl : public QObject -{ - Q_OBJECT - -public: - QQuickLineControl(const QString &txt = QString()) - : m_cursor(0), m_preeditCursor(0), m_cursorWidth(0), m_layoutDirection(Qt::LayoutDirectionAuto), - m_hideCursor(false), m_separator(0), m_readOnly(0), - m_echoMode(Normal), m_textDirty(0), m_selDirty(0), - m_validInput(1), m_blinkStatus(0), m_blinkPeriod(0), m_blinkTimer(0), m_deleteAllTimer(0), - m_ascent(0), m_maxLength(32767), m_lastCursorPos(-1), - m_maskData(0), m_modifiedState(0), m_undoState(0), - m_selstart(0), m_selend(0), m_passwordEchoEditing(false) - { - init(txt); - } - - enum EchoMode { - Normal, - NoEcho, - Password, - PasswordEchoOnEdit - }; - - - ~QQuickLineControl() - { - delete [] m_maskData; - } - - int nextMaskBlank(int pos) - { - int c = findInMask(pos, true, false); - m_separator |= (c != pos); - return (c != -1 ? c : m_maxLength); - } - - int prevMaskBlank(int pos) - { - int c = findInMask(pos, false, false); - m_separator |= (c != pos); - return (c != -1 ? c : 0); - } - - bool isUndoAvailable() const { return !m_readOnly && m_undoState; } - bool isRedoAvailable() const { return !m_readOnly && m_undoState < (int)m_history.size(); } - void clearUndo() { m_history.clear(); m_modifiedState = m_undoState = 0; } - - bool isModified() const { return m_modifiedState != m_undoState; } - void setModified(bool modified) { m_modifiedState = modified ? -1 : m_undoState; } - - bool allSelected() const { return !m_text.isEmpty() && m_selstart == 0 && m_selend == (int)m_text.length(); } - bool hasSelectedText() const { return !m_text.isEmpty() && m_selend > m_selstart; } - - int width() const { return qRound(m_textLayout.lineAt(0).width()) + 1; } - int height() const { return qRound(m_textLayout.lineAt(0).height()) + 1; } - int ascent() const { return m_ascent; } - qreal naturalTextWidth() const { return m_textLayout.lineAt(0).naturalTextWidth(); } - - void setSelection(int start, int length); - - inline QString selectedText() const { return hasSelectedText() ? m_text.mid(m_selstart, m_selend - m_selstart) : QString(); } - QString textBeforeSelection() const { return hasSelectedText() ? m_text.left(m_selstart) : QString(); } - QString textAfterSelection() const { return hasSelectedText() ? m_text.mid(m_selend) : QString(); } - - int selectionStart() const { return hasSelectedText() ? m_selstart : -1; } - int selectionEnd() const { return hasSelectedText() ? m_selend : -1; } - bool inSelection(int x) const - { - if (m_selstart >= m_selend) - return false; - int pos = xToPos(x, QTextLine::CursorOnCharacter); - return pos >= m_selstart && pos < m_selend; - } - - void removeSelection() - { - int priorState = m_undoState; - removeSelectedText(); - finishChange(priorState); - } - - int start() const { return 0; } - int end() const { return m_text.length(); } - -#ifndef QT_NO_CLIPBOARD - void copy(QClipboard::Mode mode = QClipboard::Clipboard) const; - void paste(QClipboard::Mode mode = QClipboard::Clipboard); -#endif - - int cursor() const{ return m_cursor; } - int preeditCursor() const { return m_preeditCursor; } - - int cursorWidth() const { return m_cursorWidth; } - void setCursorWidth(int value) { m_cursorWidth = value; } - - Qt::CursorMoveStyle cursorMoveStyle() const { return m_textLayout.cursorMoveStyle(); } - void setCursorMoveStyle(Qt::CursorMoveStyle style) { m_textLayout.setCursorMoveStyle(style); } - - void moveCursor(int pos, bool mark = false); - void cursorForward(bool mark, int steps) - { - int c = m_cursor; - if (steps > 0) { - while (steps--) - c = cursorMoveStyle() == Qt::VisualMoveStyle ? m_textLayout.rightCursorPosition(c) - : m_textLayout.nextCursorPosition(c); - } else if (steps < 0) { - while (steps++) - c = cursorMoveStyle() == Qt::VisualMoveStyle ? m_textLayout.leftCursorPosition(c) - : m_textLayout.previousCursorPosition(c); - } - moveCursor(c, mark); - } - - void cursorWordForward(bool mark) { moveCursor(m_textLayout.nextCursorPosition(m_cursor, QTextLayout::SkipWords), mark); } - void cursorWordBackward(bool mark) { moveCursor(m_textLayout.previousCursorPosition(m_cursor, QTextLayout::SkipWords), mark); } - - void home(bool mark) { moveCursor(0, mark); } - void end(bool mark) { moveCursor(text().length(), mark); } - - int xToPos(int x, QTextLine::CursorPosition = QTextLine::CursorBetweenCharacters) const; - QRect cursorRect() const; - - qreal cursorToX(int cursor) const { return m_textLayout.lineAt(0).cursorToX(cursor); } - qreal cursorToX() const - { - int cursor = m_cursor; - if (m_preeditCursor != -1) - cursor += m_preeditCursor; - return cursorToX(cursor); - } - - bool isReadOnly() const { return m_readOnly; } - void setReadOnly(bool enable) { m_readOnly = enable; } - - QString text() const; - QString realText() const; - void setText(const QString &txt); - void commitPreedit(); - - QString displayText() const { return m_textLayout.text(); } - - void backspace(); - void del(); - void deselect() { internalDeselect(); finishChange(); } - void selectAll() { m_selstart = m_selend = m_cursor = 0; moveCursor(m_text.length(), true); } - - void insert(const QString &); - void clear(); - void undo() { internalUndo(); finishChange(-1, true); } - void redo() { internalRedo(); finishChange(); } - void selectWordAtPos(int); - - EchoMode echoMode() const { return EchoMode(m_echoMode); } - void setEchoMode(EchoMode mode) - { - m_echoMode = mode; - m_passwordEchoEditing = false; - updateDisplayText(); - } - - int maxLength() const { return m_maxLength; } - void setMaxLength(int maxLength) - { - if (m_maskData) - return; - m_maxLength = maxLength; - setText(m_text); - } - -#ifndef QT_NO_VALIDATOR - const QValidator *validator() const { return m_validator; } - void setValidator(const QValidator *v) { m_validator = const_cast(v); } -#endif - - int cursorPosition() const { return m_cursor; } - void setCursorPosition(int pos) { if (pos <= m_text.length()) moveCursor(qMax(0, pos)); } - - bool hasAcceptableInput() const { return hasAcceptableInput(m_text); } - bool fixup(); - - QString inputMask() const { return m_maskData ? m_inputMask + QLatin1Char(';') + m_blank : QString(); } - void setInputMask(const QString &mask) - { - parseInputMask(mask); - if (m_maskData) - moveCursor(nextMaskBlank(0)); - } - - // input methods -#ifndef QT_NO_IM - bool composeMode() const { return !m_textLayout.preeditAreaText().isEmpty(); } - void setPreeditArea(int cursor, const QString &text) { m_textLayout.setPreeditArea(cursor, text); } -#endif - - QString preeditAreaText() const { return m_textLayout.preeditAreaText(); } - - void updatePasswordEchoEditing(bool editing); - bool passwordEchoEditing() const { return m_passwordEchoEditing; } - - QChar passwordCharacter() const { return m_passwordCharacter; } - void setPasswordCharacter(const QChar &character) { m_passwordCharacter = character; updateDisplayText(); } - - Qt::LayoutDirection layoutDirection() const { - if (m_layoutDirection == Qt::LayoutDirectionAuto) { - if (m_text.isEmpty()) - return QGuiApplication::keyboardInputDirection(); - return m_text.isRightToLeft() ? Qt::RightToLeft : Qt::LeftToRight; - } - return m_layoutDirection; - } - void setLayoutDirection(Qt::LayoutDirection direction) - { - if (direction != m_layoutDirection) { - m_layoutDirection = direction; - updateDisplayText(); - } - } - - void setFont(const QFont &font) { m_textLayout.setFont(font); updateDisplayText(); } - - void processInputMethodEvent(QInputMethodEvent *event); - void processMouseEvent(QMouseEvent* ev); - void processKeyEvent(QKeyEvent* ev); - - int cursorBlinkPeriod() const { return m_blinkPeriod; } - void setCursorBlinkPeriod(int msec); - void resetCursorBlinkTimer(); - - bool cursorBlinkStatus() const { return m_blinkStatus; } - - QString cancelText() const { return m_cancelText; } - void setCancelText(const QString &text) { m_cancelText = text; } - - const QPalette &palette() const { return m_palette; } - void setPalette(const QPalette &p) { m_palette = p; } - - enum DrawFlags { - DrawText = 0x01, - DrawSelections = 0x02, - DrawCursor = 0x04, - DrawAll = DrawText | DrawSelections | DrawCursor - }; - - bool processEvent(QEvent *ev); - - QTextLayout *textLayout() - { - return &m_textLayout; - } - -private: - void init(const QString &txt); - void removeSelectedText(); - void internalSetText(const QString &txt, int pos = -1, bool edited = true); - void updateDisplayText(bool forceUpdate = false); - - void internalInsert(const QString &s); - void internalDelete(bool wasBackspace = false); - void internalRemove(int pos); - - inline void internalDeselect() - { - m_selDirty |= (m_selend > m_selstart); - m_selstart = m_selend = 0; - } - - void internalUndo(int until = -1); - void internalRedo(); - - QString m_text; - QPalette m_palette; - int m_cursor; - int m_preeditCursor; - int m_cursorWidth; - QString m_tentativeCommit; - Qt::LayoutDirection m_layoutDirection; - uint m_hideCursor : 1; // used to hide the m_cursor inside preedit areas - uint m_separator : 1; - uint m_readOnly : 1; - uint m_echoMode : 2; - uint m_textDirty : 1; - uint m_selDirty : 1; - uint m_validInput : 1; - uint m_blinkStatus : 1; - int m_blinkPeriod; // 0 for non-blinking cursor - int m_blinkTimer; - int m_deleteAllTimer; - int m_ascent; - int m_maxLength; - int m_lastCursorPos; - QList m_transactions; - QString m_cancelText; - - void emitCursorPositionChanged(); - - bool finishChange(int validateFromState = -1, bool update = false, bool edited = true); - -#ifndef QT_NO_VALIDATOR - QPointer m_validator; -#endif - - struct MaskInputData { - enum Casemode { NoCaseMode, Upper, Lower }; - QChar maskChar; // either the separator char or the inputmask - bool separator; - Casemode caseMode; - }; - QString m_inputMask; - QChar m_blank; - MaskInputData *m_maskData; - - // undo/redo handling - enum CommandType { Separator, Insert, Remove, Delete, RemoveSelection, DeleteSelection, SetSelection }; - struct Command { - inline Command() {} - inline Command(CommandType t, int p, QChar c, int ss, int se) : type(t),uc(c),pos(p),selStart(ss),selEnd(se) {} - uint type : 4; - QChar uc; - int pos, selStart, selEnd; - }; - int m_modifiedState; - int m_undoState; - QVector m_history; - void addCommand(const Command& cmd); - - inline void separate() { m_separator = true; } - - // selection - int m_selstart; - int m_selend; - - // masking - void parseInputMask(const QString &maskFields); - bool isValidInput(QChar key, QChar mask) const; - bool hasAcceptableInput(const QString &text) const; - QString maskString(uint pos, const QString &str, bool clear = false) const; - QString clearString(uint pos, uint len) const; - QString stripString(const QString &str) const; - int findInMask(int pos, bool forward, bool findSeparator, QChar searchChar = QChar()) const; - - // complex text layout - QTextLayout m_textLayout; - - bool m_passwordEchoEditing; - QChar m_passwordCharacter; - -Q_SIGNALS: - void cursorPositionChanged(int, int); - void selectionChanged(); - - void displayTextChanged(const QString &); - void textChanged(const QString &); - void textEdited(const QString &); - - void resetInputContext(); - void updateMicroFocus(); - - void accepted(); - void editingFinished(); - void updateNeeded(const QRect &); - -protected: - virtual void timerEvent(QTimerEvent *event); - -private Q_SLOTS: - void _q_clipboardChanged(); - void _q_deleteSelected(); - -}; - -QT_END_NAMESPACE - -QT_END_HEADER - -#endif // QQuickLineControl_P_H diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp index 3752523..649e29d 100644 --- a/src/quick/items/qquicktextinput.cpp +++ b/src/quick/items/qquicktextinput.cpp @@ -54,6 +54,10 @@ #include #include +#ifndef QT_NO_ACCESSIBILITY +#include "qaccessible.h" +#endif + QT_BEGIN_NAMESPACE DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD) @@ -96,7 +100,12 @@ QQuickTextInput::~QQuickTextInput() QString QQuickTextInput::text() const { Q_D(const QQuickTextInput); - return d->control->text(); + + QString content = d->m_text; + if (!d->m_tentativeCommit.isEmpty()) + content.insert(d->m_cursor, d->m_tentativeCommit); + QString res = d->m_maskData ? d->stripString(content) : content; + return (res.isNull() ? QString::fromLatin1("") : res); } void QQuickTextInput::setText(const QString &s) @@ -104,7 +113,16 @@ void QQuickTextInput::setText(const QString &s) Q_D(QQuickTextInput); if (s == text()) return; - d->control->setText(s); + if (d->composeMode()) + qApp->inputPanel()->reset(); + d->m_tentativeCommit.clear(); + d->internalSetText(s, -1, false); +} + +QString QQuickTextInputPrivate::realText() const +{ + QString res = m_maskData ? stripString(m_text) : m_text; + return (res.isNull() ? QString::fromLatin1("") : res); } /*! @@ -233,7 +251,7 @@ void QQuickTextInput::setFont(const QFont &font) d->font.setPointSizeF(size/2.0); } if (oldFont != d->font) { - d->control->setFont(d->font); + d->updateDisplayText(); updateSize(); updateCursorRectangle(); if (d->cursorItem) { @@ -284,10 +302,8 @@ void QQuickTextInput::setSelectionColor(const QColor &color) return; d->selectionColor = color; - QPalette p = d->control->palette(); - p.setColor(QPalette::Highlight, d->selectionColor); - d->control->setPalette(p); - if (d->control->hasSelectedText()) { + d->m_palette.setColor(QPalette::Highlight, d->selectionColor); + if (d->hasSelectedText()) { d->textLayoutDirty = true; update(); } @@ -311,10 +327,8 @@ void QQuickTextInput::setSelectedTextColor(const QColor &color) return; d->selectedTextColor = color; - QPalette p = d->control->palette(); - p.setColor(QPalette::HighlightedText, d->selectedTextColor); - d->control->setPalette(p); - if (d->control->hasSelectedText()) { + d->m_palette.setColor(QPalette::HighlightedText, d->selectedTextColor); + if (d->hasSelectedText()) { d->textLayoutDirty = true; update(); } @@ -406,9 +420,9 @@ bool QQuickTextInputPrivate::determineHorizontalAlignment() { if (hAlignImplicit) { // if no explicit alignment has been set, follow the natural layout direction of the text - QString text = control->text(); + QString text = q_func()->text(); if (text.isEmpty()) - text = control->preeditAreaText(); + text = m_textLayout.preeditAreaText(); bool isRightToLeft = text.isEmpty() ? QGuiApplication::keyboardInputDirection() == Qt::RightToLeft : text.isRightToLeft(); return setHAlign(isRightToLeft ? QQuickTextInput::AlignRight : QQuickTextInput::AlignLeft); } @@ -438,19 +452,19 @@ void QQuickTextInputPrivate::mirrorChange() bool QQuickTextInput::isReadOnly() const { Q_D(const QQuickTextInput); - return d->control->isReadOnly(); + return d->m_readOnly; } void QQuickTextInput::setReadOnly(bool ro) { Q_D(QQuickTextInput); - if (d->control->isReadOnly() == ro) + if (d->m_readOnly == ro) return; setFlag(QQuickItem::ItemAcceptsInputMethod, !ro); - d->control->setReadOnly(ro); + d->m_readOnly = ro; if (!ro) - d->control->setCursorPosition(d->control->end()); + d->setCursorPosition(d->end()); emit readOnlyChanged(ro); } @@ -466,16 +480,17 @@ void QQuickTextInput::setReadOnly(bool ro) int QQuickTextInput::maxLength() const { Q_D(const QQuickTextInput); - return d->control->maxLength(); + return d->m_maxLength; } void QQuickTextInput::setMaxLength(int ml) { Q_D(QQuickTextInput); - if (d->control->maxLength() == ml) + if (d->m_maxLength == ml || d->m_maskData) return; - d->control->setMaxLength(ml); + d->m_maxLength = ml; + d->internalSetText(d->m_text, -1, false); emit maximumLengthChanged(ml); } @@ -518,9 +533,9 @@ void QQuickTextInput::setCursorVisible(bool on) if (d->cursorVisible == on) return; d->cursorVisible = on; - d->control->setCursorBlinkPeriod(on ? qApp->styleHints()->cursorFlashTime() : 0); - QRect r = d->control->cursorRect(); - if (d->control->inputMask().isEmpty()) + d->setCursorBlinkPeriod(on ? qApp->styleHints()->cursorFlashTime() : 0); + QRect r = cursorRectangle(); + if (d->inputMask().isEmpty()) updateRect(r); else updateRect(); @@ -534,14 +549,15 @@ void QQuickTextInput::setCursorVisible(bool on) int QQuickTextInput::cursorPosition() const { Q_D(const QQuickTextInput); - return d->control->cursor(); + return d->m_cursor; } + void QQuickTextInput::setCursorPosition(int cp) { Q_D(QQuickTextInput); - if (cp < 0 || cp > d->control->text().length()) + if (cp < 0 || cp > text().length()) return; - d->control->moveCursor(cp); + d->moveCursor(cp); } /*! @@ -551,12 +567,11 @@ void QQuickTextInput::setCursorPosition(int cp) QRect QQuickTextInput::cursorRectangle() const { Q_D(const QQuickTextInput); - QRect r = d->control->cursorRect(); - // Scroll and make consistent with TextEdit - // QQuickLineControl inexplicably adds 1 to the height and horizontal padding - // for unicode direction markers. - r.adjust(5 - d->hscroll, 0, -4 - d->hscroll, -1); - return r; + QTextLine l = d->m_textLayout.lineAt(0); + int c = d->m_cursor; + if (d->m_preeditCursor != -1) + c += d->m_preeditCursor; + return QRect(qRound(l.cursorToX(c)) - d->hscroll, 0, d->m_cursorWidth, l.height()); } /*! \qmlproperty int QtQuick2::TextInput::selectionStart @@ -604,9 +619,9 @@ int QQuickTextInput::selectionEnd() const void QQuickTextInput::select(int start, int end) { Q_D(QQuickTextInput); - if (start < 0 || end < 0 || start > d->control->text().length() || end > d->control->text().length()) + if (start < 0 || end < 0 || start > text().length() || end > text().length()) return; - d->control->setSelection(start, end-start); + d->setSelection(start, end-start); } /*! @@ -626,7 +641,7 @@ void QQuickTextInput::select(int start, int end) QString QQuickTextInput::selectedText() const { Q_D(const QQuickTextInput); - return d->control->selectedText(); + return d->selectedText(); } /*! @@ -788,18 +803,17 @@ void QQuickTextInput::setAutoScroll(bool b) QValidator* QQuickTextInput::validator() const { Q_D(const QQuickTextInput); - //###const cast isn't good, but needed for property system? - return const_cast(d->control->validator()); + return d->m_validator; } void QQuickTextInput::setValidator(QValidator* v) { Q_D(QQuickTextInput); - if (d->control->validator() == v) + if (d->m_validator == v) return; - d->control->setValidator(v); - if (!d->control->hasAcceptableInput()) { + d->m_validator = v; + if (!d->hasAcceptableInput(d->m_text)) { d->oldValidity = false; emit acceptableInputChanged(); } @@ -820,17 +834,17 @@ void QQuickTextInput::setValidator(QValidator* v) QString QQuickTextInput::inputMask() const { Q_D(const QQuickTextInput); - return d->control->inputMask(); + return d->inputMask(); } void QQuickTextInput::setInputMask(const QString &im) { Q_D(QQuickTextInput); - if (d->control->inputMask() == im) + if (d->inputMask() == im) return; - d->control->setInputMask(im); - emit inputMaskChanged(d->control->inputMask()); + d->setInputMask(im); + emit inputMaskChanged(d->inputMask()); } /*! @@ -844,7 +858,7 @@ void QQuickTextInput::setInputMask(const QString &im) bool QQuickTextInput::hasAcceptableInput() const { Q_D(const QQuickTextInput); - return d->control->hasAcceptableInput(); + return d->hasAcceptableInput(d->m_text); } /*! @@ -860,12 +874,11 @@ void QQuickTextInputPrivate::updateInputMethodHints() { Q_Q(QQuickTextInput); Qt::InputMethodHints hints = inputMethodHints; - uint echo = control->echoMode(); - if (echo == QQuickTextInput::Password || echo == QQuickTextInput::NoEcho) + if (m_echoMode == QQuickTextInput::Password || m_echoMode == QQuickTextInput::NoEcho) hints |= Qt::ImhHiddenText; - else if (echo == QQuickTextInput::PasswordEchoOnEdit) + else if (m_echoMode == QQuickTextInput::PasswordEchoOnEdit) hints &= ~Qt::ImhHiddenText; - if (echo != QQuickTextInput::Normal) + if (m_echoMode != QQuickTextInput::Normal) hints |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText); q->setInputMethodHints(hints); } @@ -884,7 +897,7 @@ void QQuickTextInputPrivate::updateInputMethodHints() QQuickTextInput::EchoMode QQuickTextInput::echoMode() const { Q_D(const QQuickTextInput); - return (QQuickTextInput::EchoMode)d->control->echoMode(); + return QQuickTextInput::EchoMode(d->m_echoMode); } void QQuickTextInput::setEchoMode(QQuickTextInput::EchoMode echo) @@ -892,9 +905,13 @@ void QQuickTextInput::setEchoMode(QQuickTextInput::EchoMode echo) Q_D(QQuickTextInput); if (echoMode() == echo) return; - d->control->setEchoMode((QQuickLineControl::EchoMode)echo); + d->m_echoMode = echo; + d->m_passwordEchoEditing = false; d->updateInputMethodHints(); + + d->updateDisplayText(); q_textChanged(); + emit echoModeChanged(echoMode()); } @@ -986,8 +1003,8 @@ void QQuickTextInput::createCursor() QDeclarative_setParent_noEvent(d->cursorItem, this); d->cursorItem->setParentItem(this); - d->cursorItem->setX(d->control->cursorToX()); - d->cursorItem->setHeight(d->control->height()-1); // -1 to counter QQuickLineControl's +1 which is not consistent with Text. + d->cursorItem->setX(d->cursorToX()); + d->cursorItem->setHeight(d->calculateTextHeight()); } /*! @@ -1002,12 +1019,10 @@ void QQuickTextInput::createCursor() QRectF QQuickTextInput::positionToRectangle(int pos) const { Q_D(const QQuickTextInput); - if (pos > d->control->cursorPosition()) - pos += d->control->preeditAreaText().length(); - return QRectF(d->control->cursorToX(pos)-d->hscroll, - 0.0, - d->control->cursorWidth(), - cursorRectangle().height()); + if (pos > d->m_cursor) + pos += d->preeditAreaText().length(); + QTextLine l = d->m_textLayout.lineAt(0); + return QRectF( l.cursorToX(pos) - d->hscroll, 0.0, d->m_cursorWidth, l.height()); } /*! @@ -1036,10 +1051,10 @@ int QQuickTextInput::positionAt(int x) const int QQuickTextInput::positionAt(int x, CursorPosition position) const { Q_D(const QQuickTextInput); - int pos = d->control->xToPos(x + d->hscroll, QTextLine::CursorPosition(position)); - const int cursor = d->control->cursor(); + int pos = d->m_textLayout.lineAt(0).xToCursor(x + d->hscroll, QTextLine::CursorPosition(position)); + const int cursor = d->m_cursor; if (pos > cursor) { - const int preeditLength = d->control->preeditAreaText().length(); + const int preeditLength = d->preeditAreaText().length(); pos = pos > cursor + preeditLength ? pos - preeditLength : cursor; @@ -1055,16 +1070,16 @@ void QQuickTextInput::keyPressEvent(QKeyEvent* ev) if (!ignore && (d->lastSelectionStart == d->lastSelectionEnd) && (ev->key() == Qt::Key_Right || ev->key() == Qt::Key_Left)) { // Ignore when moving off the end unless there is a selection, // because then moving will do something (deselect). - int cursorPosition = d->control->cursor(); + int cursorPosition = d->m_cursor; if (cursorPosition == 0) - ignore = ev->key() == (d->control->layoutDirection() == Qt::LeftToRight ? Qt::Key_Left : Qt::Key_Right); - if (cursorPosition == d->control->text().length()) - ignore = ev->key() == (d->control->layoutDirection() == Qt::LeftToRight ? Qt::Key_Right : Qt::Key_Left); + ignore = ev->key() == (d->layoutDirection() == Qt::LeftToRight ? Qt::Key_Left : Qt::Key_Right); + if (cursorPosition == text().length()) + ignore = ev->key() == (d->layoutDirection() == Qt::LeftToRight ? Qt::Key_Right : Qt::Key_Left); } if (ignore) { ev->ignore(); } else { - d->control->processKeyEvent(ev); + d->processKeyEvent(ev); } if (!ev->isAccepted()) QQuickImplicitSizeItem::keyPressEvent(ev); @@ -1073,16 +1088,16 @@ void QQuickTextInput::keyPressEvent(QKeyEvent* ev) void QQuickTextInput::inputMethodEvent(QInputMethodEvent *ev) { Q_D(QQuickTextInput); - const bool wasComposing = d->control->preeditAreaText().length() > 0; - if (d->control->isReadOnly()) { + const bool wasComposing = d->preeditAreaText().length() > 0; + if (d->m_readOnly) { ev->ignore(); } else { - d->control->processInputMethodEvent(ev); + d->processInputMethodEvent(ev); } if (!ev->isAccepted()) QQuickImplicitSizeItem::inputMethodEvent(ev); - if (wasComposing != (d->control->preeditAreaText().length() > 0)) + if (wasComposing != (d->m_textLayout.preeditAreaText().length() > 0)) emit inputMethodComposingChanged(); } @@ -1091,9 +1106,9 @@ void QQuickTextInput::mouseDoubleClickEvent(QMouseEvent *event) Q_D(QQuickTextInput); if (d->selectByMouse && event->button() == Qt::LeftButton) { - d->control->commitPreedit(); + d->commitPreedit(); int cursor = d->xToPos(event->localPos().x()); - d->control->selectWordAtPos(cursor); + d->selectWordAtPos(cursor); event->setAccepted(true); if (!d->hasPendingTripleClick()) { d->tripleClickStartPoint = event->localPos().toPoint(); @@ -1116,7 +1131,7 @@ void QQuickTextInput::mousePressEvent(QMouseEvent *event) bool hadActiveFocus = hasActiveFocus(); forceActiveFocus(); // re-open input panel on press if already focused - if (hasActiveFocus() && hadActiveFocus && !isReadOnly()) + if (hasActiveFocus() && hadActiveFocus && !d->m_readOnly) openSoftwareInputPanel(); } if (d->selectByMouse) { @@ -1136,7 +1151,7 @@ void QQuickTextInput::mousePressEvent(QMouseEvent *event) bool mark = (event->modifiers() & Qt::ShiftModifier) && d->selectByMouse; int cursor = d->xToPos(event->localPos().x()); - d->control->moveCursor(cursor, mark); + d->moveCursor(cursor, mark); event->setAccepted(true); } @@ -1148,12 +1163,12 @@ void QQuickTextInput::mouseMoveEvent(QMouseEvent *event) if (qAbs(int(event->localPos().x() - d->pressPos.x())) > qApp->styleHints()->startDragDistance()) setKeepMouseGrab(true); - if (d->control->composeMode()) { + if (d->composeMode()) { // start selection int startPos = d->xToPos(d->pressPos.x()); int currentPos = d->xToPos(event->localPos().x()); if (startPos != currentPos) - d->control->setSelection(startPos, currentPos - startPos); + d->setSelection(startPos, currentPos - startPos); } else { moveCursorSelection(d->xToPos(event->localPos().x()), d->mouseSelectionMode); } @@ -1175,10 +1190,10 @@ void QQuickTextInput::mouseReleaseEvent(QMouseEvent *event) #ifndef QT_NO_CLIPBOARD if (QGuiApplication::clipboard()->supportsSelection()) { if (event->button() == Qt::LeftButton) { - d->control->copy(QClipboard::Selection); - } else if (!isReadOnly() && event->button() == Qt::MidButton) { - d->control->deselect(); - d->control->insert(QGuiApplication::clipboard()->text(QClipboard::Selection)); + d->copy(QClipboard::Selection); + } else if (!d->m_readOnly && event->button() == Qt::MidButton) { + d->deselect(); + d->insert(QGuiApplication::clipboard()->text(QClipboard::Selection)); } } #endif @@ -1189,10 +1204,10 @@ void QQuickTextInput::mouseReleaseEvent(QMouseEvent *event) bool QQuickTextInputPrivate::sendMouseEventToInputContext(QMouseEvent *event) { #if !defined QT_NO_IM - if (control->composeMode()) { + if (composeMode()) { int tmp_cursor = xToPos(event->localPos().x()); - int mousePos = tmp_cursor - control->cursor(); - if (mousePos >= 0 && mousePos <= control->preeditAreaText().length()) { + int mousePos = tmp_cursor - m_cursor; + if (mousePos >= 0 && mousePos <= m_textLayout.preeditAreaText().length()) { if (event->type() == QEvent::MouseButtonRelease) { qApp->inputPanel()->invokeAction(QInputPanel::Click, mousePos); } @@ -1217,8 +1232,9 @@ void QQuickTextInput::mouseUngrabEvent() bool QQuickTextInput::event(QEvent* ev) { #ifndef QT_NO_SHORTCUT + Q_D(QQuickTextInput); if (ev->type() == QEvent::ShortcutOverride) { - if (isReadOnly()) + if (d->m_readOnly) return false; QKeyEvent* ke = static_cast(ev); if (ke == QKeySequence::Copy @@ -1275,15 +1291,10 @@ void QQuickTextInput::geometryChanged(const QRectF &newGeometry, QQuickImplicitSizeItem::geometryChanged(newGeometry, oldGeometry); } -int QQuickTextInputPrivate::calculateTextWidth() -{ - return qRound(control->naturalTextWidth()); -} - void QQuickTextInputPrivate::updateHorizontalScroll() { Q_Q(QQuickTextInput); - const int preeditLength = control->preeditAreaText().length(); + const int preeditLength = m_textLayout.preeditAreaText().length(); const int width = q->width(); int widthUsed = calculateTextWidth(); @@ -1303,7 +1314,7 @@ void QQuickTextInputPrivate::updateHorizontalScroll() break; } } else { - int cix = qRound(control->cursorToX(control->cursor() + preeditLength)); + int cix = qRound(cursorToX(m_cursor + preeditLength)); if (cix - hscroll >= width) { // text doesn't fit, cursor is to the right of br (scroll right) hscroll = cix - width; @@ -1318,8 +1329,8 @@ void QQuickTextInputPrivate::updateHorizontalScroll() if (preeditLength > 0) { // check to ensure long pre-edit text doesn't push the cursor // off to the left - cix = qRound(control->cursorToX( - control->cursor() + qMax(0, control->preeditCursor() - 1))); + cix = qRound(cursorToX( + m_cursor + qMax(0, m_preeditCursor - 1))); if (cix < hscroll) hscroll = cix; } @@ -1341,8 +1352,7 @@ QSGNode *QQuickTextInput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData if (cursorNode != 0 && !isReadOnly()) { cursorNode->setRect(cursorRectangle()); - if (!d->cursorVisible - || (!d->control->cursorBlinkStatus() && d->control->cursorBlinkPeriod() > 0)) { + if (!d->cursorVisible || (!d->m_blinkStatus && d->m_blinkPeriod > 0)) { d->hideCursor(); } else { d->showCursor(); @@ -1357,25 +1367,23 @@ QSGNode *QQuickTextInput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData QRect br(boundingRect().toRect()); if (d->autoScroll) { // the y offset is there to keep the baseline constant in case we have script changes in the text. - offset = br.topLeft() - QPoint(d->hscroll, d->control->ascent() - fm.ascent()); + offset = br.topLeft() - QPoint(d->hscroll, d->ascent() - fm.ascent()); } else { offset = QPoint(d->hscroll, 0); } - QTextLayout *textLayout = d->control->textLayout(); - if (!textLayout->text().isEmpty()) { - node->addTextLayout(offset, textLayout, d->color, + if (!d->m_textLayout.text().isEmpty()) { + node->addTextLayout(offset, &d->m_textLayout, d->color, QQuickText::Normal, QColor(), d->selectionColor, d->selectedTextColor, - d->control->selectionStart(), - d->control->selectionEnd() - 1); // selectionEnd() returns first char after + d->selectionStart(), + d->selectionEnd() - 1); // selectionEnd() returns first char after // selection } if (!isReadOnly() && d->cursorItem == 0) { node->setCursor(cursorRectangle(), d->color); - if (!d->cursorVisible - || (!d->control->cursorBlinkStatus() && d->control->cursorBlinkPeriod() > 0)) { + if (!d->cursorVisible || (!d->m_blinkStatus && d->m_blinkPeriod > 0)) { d->hideCursor(); } else { d->showCursor(); @@ -1401,25 +1409,24 @@ QVariant QQuickTextInput::inputMethodQuery(Qt::InputMethodQuery property) const case Qt::ImFont: return font(); case Qt::ImCursorPosition: - return QVariant(d->control->cursor()); + return QVariant(d->m_cursor); case Qt::ImSurroundingText: - if (d->control->echoMode() == QQuickLineControl::PasswordEchoOnEdit - && !d->control->passwordEchoEditing()) { + if (d->m_echoMode == PasswordEchoOnEdit && !d->m_passwordEchoEditing) { return QVariant(displayText()); } else { - return QVariant(d->control->realText()); + return QVariant(d->realText()); } case Qt::ImCurrentSelection: return QVariant(selectedText()); case Qt::ImMaximumTextLength: return QVariant(maxLength()); case Qt::ImAnchorPosition: - if (d->control->selectionStart() == d->control->selectionEnd()) - return QVariant(d->control->cursor()); - else if (d->control->selectionStart() == d->control->cursor()) - return QVariant(d->control->selectionEnd()); + if (d->selectionStart() == d->selectionEnd()) + return QVariant(d->m_cursor); + else if (d->selectionStart() == d->m_cursor) + return QVariant(d->selectionEnd()); else - return QVariant(d->control->selectionStart()); + return QVariant(d->selectionStart()); default: return QVariant(); } @@ -1433,7 +1440,7 @@ QVariant QQuickTextInput::inputMethodQuery(Qt::InputMethodQuery property) const void QQuickTextInput::deselect() { Q_D(QQuickTextInput); - d->control->deselect(); + d->deselect(); } /*! @@ -1444,7 +1451,7 @@ void QQuickTextInput::deselect() void QQuickTextInput::selectAll() { Q_D(QQuickTextInput); - d->control->setSelection(0, d->control->text().length()); + d->setSelection(0, text().length()); } /*! @@ -1455,12 +1462,11 @@ void QQuickTextInput::selectAll() */ bool QQuickTextInput::isRightToLeft(int start, int end) { - Q_D(QQuickTextInput); if (start > end) { qmlInfo(this) << "isRightToLeft(start, end) called with the end property being smaller than the start."; return false; } else { - return d->control->text().mid(start, end - start).isRightToLeft(); + return text().mid(start, end - start).isRightToLeft(); } } @@ -1473,8 +1479,8 @@ bool QQuickTextInput::isRightToLeft(int start, int end) void QQuickTextInput::cut() { Q_D(QQuickTextInput); - d->control->copy(); - d->control->del(); + d->copy(); + d->del(); } /*! @@ -1485,7 +1491,7 @@ void QQuickTextInput::cut() void QQuickTextInput::copy() { Q_D(QQuickTextInput); - d->control->copy(); + d->copy(); } /*! @@ -1496,8 +1502,8 @@ void QQuickTextInput::copy() void QQuickTextInput::paste() { Q_D(QQuickTextInput); - if (!d->control->isReadOnly()) - d->control->paste(); + if (!d->m_readOnly) + d->paste(); } #endif // QT_NO_CLIPBOARD @@ -1509,7 +1515,7 @@ void QQuickTextInput::paste() void QQuickTextInput::selectWord() { Q_D(QQuickTextInput); - d->control->selectWordAtPos(d->control->cursor()); + d->selectWordAtPos(d->m_cursor); } /*! @@ -1539,7 +1545,7 @@ void QQuickTextInput::selectWord() QString QQuickTextInput::passwordCharacter() const { Q_D(const QQuickTextInput); - return QString(d->control->passwordCharacter()); + return QString(d->m_passwordCharacter); } void QQuickTextInput::setPasswordCharacter(const QString &str) @@ -1547,9 +1553,9 @@ void QQuickTextInput::setPasswordCharacter(const QString &str) Q_D(QQuickTextInput); if (str.length() < 1) return; - d->control->setPasswordCharacter(str.constData()[0]); - EchoMode echoMode_ = echoMode(); - if (echoMode_ == Password || echoMode_ == PasswordEchoOnEdit) { + d->m_passwordCharacter = str.constData()[0]; + d->updateDisplayText(); + if (d->m_echoMode == Password || d->m_echoMode == PasswordEchoOnEdit) { updateSize(); } emit passwordCharacterChanged(); @@ -1568,7 +1574,7 @@ void QQuickTextInput::setPasswordCharacter(const QString &str) QString QQuickTextInput::displayText() const { Q_D(const QQuickTextInput); - return d->control->displayText(); + return d->m_textLayout.text(); } /*! @@ -1639,7 +1645,7 @@ bool QQuickTextInput::canPaste() const void QQuickTextInput::moveCursorSelection(int position) { Q_D(QQuickTextInput); - d->control->moveCursor(position, true); + d->moveCursor(position, true); } /*! @@ -1684,19 +1690,19 @@ void QQuickTextInput::moveCursorSelection(int pos, SelectionMode mode) Q_D(QQuickTextInput); if (mode == SelectCharacters) { - d->control->moveCursor(pos, true); - } else if (pos != d->control->cursor()){ - const int cursor = d->control->cursor(); + d->moveCursor(pos, true); + } else if (pos != d->m_cursor){ + const int cursor = d->m_cursor; int anchor; - if (!d->control->hasSelectedText()) - anchor = d->control->cursor(); - else if (d->control->selectionStart() == d->control->cursor()) - anchor = d->control->selectionEnd(); + if (!d->hasSelectedText()) + anchor = d->m_cursor; + else if (d->selectionStart() == d->m_cursor) + anchor = d->selectionEnd(); else - anchor = d->control->selectionStart(); + anchor = d->selectionStart(); if (anchor < pos || (anchor == pos && cursor < pos)) { - const QString text = d->control->text(); + const QString text = this->text(); QTextBoundaryFinder finder(QTextBoundaryFinder::Word, text); finder.setPosition(anchor); @@ -1712,9 +1718,9 @@ void QQuickTextInput::moveCursorSelection(int pos, SelectionMode mode) finder.toNextBoundary(); const int cursor = finder.position() != -1 ? finder.position() : text.length(); - d->control->setSelection(anchor, cursor - anchor); + d->setSelection(anchor, cursor - anchor); } else if (anchor > pos || (anchor == pos && cursor > pos)) { - const QString text = d->control->text(); + const QString text = this->text(); QTextBoundaryFinder finder(QTextBoundaryFinder::Word, text); finder.setPosition(anchor); @@ -1731,7 +1737,7 @@ void QQuickTextInput::moveCursorSelection(int pos, SelectionMode mode) finder.toPreviousBoundary(); const int cursor = finder.position() != -1 ? finder.position() : 0; - d->control->setSelection(anchor, cursor - anchor); + d->setSelection(anchor, cursor - anchor); } } } @@ -1825,7 +1831,7 @@ void QQuickTextInput::closeSoftwareInputPanel() void QQuickTextInput::focusInEvent(QFocusEvent *event) { Q_D(const QQuickTextInput); - if (d->focusOnPress && !isReadOnly()) + if (d->focusOnPress && !d->m_readOnly) openSoftwareInputPanel(); QQuickImplicitSizeItem::focusInEvent(event); } @@ -1838,9 +1844,9 @@ void QQuickTextInput::itemChange(ItemChange change, const ItemChangeData &value) d->focused = hasFocus; setCursorVisible(hasFocus); // ### refactor: && d->canvas && d->canvas->hasFocus() if (echoMode() == QQuickTextInput::PasswordEchoOnEdit && !hasFocus) - d->control->updatePasswordEchoEditing(false);//QQuickLineControl sets it on key events, but doesn't deal with focus events + d->updatePasswordEchoEditing(false);//QQuickTextInputPrivate sets it on key events, but doesn't deal with focus events if (!hasFocus) - d->control->deselect(); + d->deselect(); } QQuickItem::itemChange(change, value); } @@ -1860,76 +1866,37 @@ void QQuickTextInput::itemChange(ItemChange change, const ItemChangeData &value) bool QQuickTextInput::isInputMethodComposing() const { Q_D(const QQuickTextInput); - return d->control->preeditAreaText().length() > 0; + return d->preeditAreaText().length() > 0; } void QQuickTextInputPrivate::init() { Q_Q(QQuickTextInput); - control->setParent(q);//Now mandatory due to accessibility changes - control->setCursorWidth(1); - control->setPasswordCharacter(QLatin1Char('*')); q->setSmooth(smooth); q->setAcceptedMouseButtons(Qt::LeftButton); q->setFlag(QQuickItem::ItemAcceptsInputMethod); q->setFlag(QQuickItem::ItemHasContents); - q->connect(control, SIGNAL(cursorPositionChanged(int,int)), - q, SLOT(cursorPosChanged())); - q->connect(control, SIGNAL(selectionChanged()), - q, SLOT(selectionChanged())); - q->connect(control, SIGNAL(textChanged(QString)), - q, SLOT(q_textChanged())); - q->connect(control, SIGNAL(accepted()), - q, SIGNAL(accepted())); - q->connect(control, SIGNAL(updateNeeded(QRect)), - q, SLOT(updateRect(QRect))); #ifndef QT_NO_CLIPBOARD q->connect(q, SIGNAL(readOnlyChanged(bool)), q, SLOT(q_canPasteChanged())); q->connect(QGuiApplication::clipboard(), SIGNAL(dataChanged()), q, SLOT(q_canPasteChanged())); - canPaste = !control->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0; + canPaste = !m_readOnly && QGuiApplication::clipboard()->text().length() != 0; #endif // QT_NO_CLIPBOARD - q->connect(control, SIGNAL(updateMicroFocus()), - q, SLOT(updateCursorRectangle())); - q->connect(control, SIGNAL(displayTextChanged(QString)), - q, SLOT(updateRect())); + updateDisplayText(); q->updateSize(); imHints &= ~Qt::ImhMultiLine; - oldValidity = control->hasAcceptableInput(); + oldValidity = hasAcceptableInput(m_text); lastSelectionStart = 0; lastSelectionEnd = 0; - QPalette p = control->palette(); - selectedTextColor = p.color(QPalette::HighlightedText); - selectionColor = p.color(QPalette::Highlight); + selectedTextColor = m_palette.color(QPalette::HighlightedText); + selectionColor = m_palette.color(QPalette::Highlight); determineHorizontalAlignment(); if (!qmlDisableDistanceField()) { - QTextOption option = control->textLayout()->textOption(); + QTextOption option = m_textLayout.textOption(); option.setUseDesignMetrics(true); - control->textLayout()->setTextOption(option); - } -} - -void QQuickTextInput::cursorPosChanged() -{ - Q_D(QQuickTextInput); - updateCursorRectangle(); - emit cursorPositionChanged(); - // XXX todo - not in 4.8? -#if 0 - d->control->resetCursorBlinkTimer(); -#endif - - if (!d->control->hasSelectedText()) { - if (d->lastSelectionStart != d->control->cursor()) { - d->lastSelectionStart = d->control->cursor(); - emit selectionStartChanged(); - } - if (d->lastSelectionEnd != d->control->cursor()) { - d->lastSelectionEnd = d->control->cursor(); - emit selectionEndChanged(); - } + m_textLayout.setTextOption(option); } } @@ -1942,7 +1909,7 @@ void QQuickTextInput::updateCursorRectangle() updateMicroFocus(); emit cursorRectangleChanged(); if (d->cursorItem) - d->cursorItem->setX(d->control->cursorToX() - d->hscroll); + d->cursorItem->setX(d->cursorToX() - d->hscroll); } void QQuickTextInput::selectionChanged() @@ -1951,16 +1918,16 @@ void QQuickTextInput::selectionChanged() updateRect();//TODO: Only update rect in selection emit selectedTextChanged(); - if (d->lastSelectionStart != d->control->selectionStart()) { - d->lastSelectionStart = d->control->selectionStart(); + if (d->lastSelectionStart != d->selectionStart()) { + d->lastSelectionStart = d->selectionStart(); if (d->lastSelectionStart == -1) - d->lastSelectionStart = d->control->cursor(); + d->lastSelectionStart = d->m_cursor; emit selectionStartChanged(); } - if (d->lastSelectionEnd != d->control->selectionEnd()) { - d->lastSelectionEnd = d->control->selectionEnd(); + if (d->lastSelectionEnd != d->selectionEnd()) { + d->lastSelectionEnd = d->selectionEnd(); if (d->lastSelectionEnd == -1) - d->lastSelectionEnd = d->control->cursor(); + d->lastSelectionEnd = d->m_cursor; emit selectionEndChanged(); } } @@ -2010,7 +1977,7 @@ QRectF QQuickTextInput::boundingRect() const Q_D(const QQuickTextInput); QRectF r = QQuickImplicitSizeItem::boundingRect(); - int cursorWidth = d->cursorItem ? d->cursorItem->width() : d->control->cursorWidth(); + int cursorWidth = d->cursorItem ? d->cursorItem->width() : d->m_cursorWidth; // Could include font max left/right bearings to either side of rectangle. @@ -2023,7 +1990,7 @@ void QQuickTextInput::updateSize(bool needsRedraw) Q_D(QQuickTextInput); int w = width(); int h = height(); - setImplicitSize(d->calculateTextWidth(), d->control->height()-1); // -1 to counter QQuickLineControl's +1 which is not consistent with Text. + setImplicitSize(d->calculateTextWidth(), d->calculateTextHeight()); if (w==width() && h==height() && needsRedraw) update(); } @@ -2033,11 +2000,1433 @@ void QQuickTextInput::q_canPasteChanged() Q_D(QQuickTextInput); bool old = d->canPaste; #ifndef QT_NO_CLIPBOARD - d->canPaste = !d->control->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0; + d->canPaste = !d->m_readOnly && QGuiApplication::clipboard()->text().length() != 0; #endif if (d->canPaste != old) emit canPasteChanged(); } +// ### these should come from QStyleHints +const int textCursorWidth = 1; +const bool fullWidthSelection = true; + +/*! + \internal + + Updates the display text based of the current edit text + If the text has changed will emit displayTextChanged() +*/ +void QQuickTextInputPrivate::updateDisplayText(bool forceUpdate) +{ + QString orig = m_textLayout.text(); + QString str; + if (m_echoMode == QQuickTextInput::NoEcho) + str = QString::fromLatin1(""); + else + str = m_text; + + if (m_echoMode == QQuickTextInput::Password + || (m_echoMode == QQuickTextInput::PasswordEchoOnEdit && !m_passwordEchoEditing)) + str.fill(m_passwordCharacter); + + // replace certain non-printable characters with spaces (to avoid + // drawing boxes when using fonts that don't have glyphs for such + // characters) + 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); + } + + m_textLayout.setText(str); + + QTextOption option = m_textLayout.textOption(); + option.setTextDirection(m_layoutDirection); + option.setFlags(QTextOption::IncludeTrailingSpaces); + m_textLayout.setTextOption(option); + + m_textLayout.beginLayout(); + QTextLine l = m_textLayout.createLine(); + m_textLayout.endLayout(); + m_ascent = qRound(l.ascent()); + + if (str != orig || forceUpdate) + emit q_func()->displayTextChanged(); +} + +#ifndef QT_NO_CLIPBOARD +/*! + \internal + + Copies the currently selected text into the clipboard using the given + \a mode. + + \note If the echo mode is set to a mode other than Normal then copy + will not work. This is to prevent using copy as a method of bypassing + password features of the line control. +*/ +void QQuickTextInputPrivate::copy(QClipboard::Mode mode) const +{ + QString t = selectedText(); + if (!t.isEmpty() && m_echoMode == QQuickTextInput::Normal) { + QGuiApplication::clipboard()->setText(t, mode); + } +} + +/*! + \internal + + Inserts the text stored in the application clipboard into the line + control. + + \sa insert() +*/ +void QQuickTextInputPrivate::paste(QClipboard::Mode clipboardMode) +{ + QString clip = QGuiApplication::clipboard()->text(clipboardMode); + if (!clip.isEmpty() || hasSelectedText()) { + separate(); //make it a separate undo/redo command + insert(clip); + separate(); + } +} + +#endif // !QT_NO_CLIPBOARD + +/*! + \internal + + Exits preedit mode and commits parts marked as tentative commit +*/ +void QQuickTextInputPrivate::commitPreedit() +{ + if (!composeMode()) + return; + + qApp->inputPanel()->reset(); + + if (!m_tentativeCommit.isEmpty()) { + internalInsert(m_tentativeCommit); + m_tentativeCommit.clear(); + finishChange(-1, true/*not used, not documented*/, false); + } + + m_preeditCursor = 0; + m_textLayout.setPreeditArea(-1, QString()); + m_textLayout.clearAdditionalFormats(); + updateDisplayText(/*force*/ true); +} + +/*! + \internal + + Handles the behavior for the backspace key or function. + Removes the current selection if there is a selection, otherwise + removes the character prior to the cursor position. + + \sa del() +*/ +void QQuickTextInputPrivate::backspace() +{ + int priorState = m_undoState; + if (hasSelectedText()) { + removeSelectedText(); + } else if (m_cursor) { + --m_cursor; + if (m_maskData) + m_cursor = prevMaskBlank(m_cursor); + QChar uc = m_text.at(m_cursor); + if (m_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 = m_text.at(m_cursor - 1); + if (uc.unicode() >= 0xd800 && uc.unicode() < 0xdc00) { + internalDelete(true); + --m_cursor; + } + } + internalDelete(true); + } + finishChange(priorState); +} + +/*! + \internal + + Handles the behavior for the delete key or function. + Removes the current selection if there is a selection, otherwise + removes the character after the cursor position. + + \sa del() +*/ +void QQuickTextInputPrivate::del() +{ + int priorState = m_undoState; + if (hasSelectedText()) { + removeSelectedText(); + } else { + int n = m_textLayout.nextCursorPosition(m_cursor) - m_cursor; + while (n--) + internalDelete(); + } + finishChange(priorState); +} + +/*! + \internal + + Inserts the given \a newText at the current cursor position. + If there is any selected text it is removed prior to insertion of + the new text. +*/ +void QQuickTextInputPrivate::insert(const QString &newText) +{ + int priorState = m_undoState; + removeSelectedText(); + internalInsert(newText); + finishChange(priorState); +} + +/*! + \internal + + Clears the line control text. +*/ +void QQuickTextInputPrivate::clear() +{ + int priorState = m_undoState; + m_selstart = 0; + m_selend = m_text.length(); + removeSelectedText(); + separate(); + finishChange(priorState, /*update*/false, /*edited*/false); +} + +/*! + \internal + + Sets \a length characters from the given \a start position as selected. + The given \a start position must be within the current text for + the line control. If \a length characters cannot be selected, then + the selection will extend to the end of the current text. +*/ +void QQuickTextInputPrivate::setSelection(int start, int length) +{ + Q_Q(QQuickTextInput); + commitPreedit(); + + if (start < 0 || start > (int)m_text.length()){ + qWarning("QQuickTextInputPrivate::setSelection: Invalid start position"); + return; + } + + if (length > 0) { + if (start == m_selstart && start + length == m_selend && m_cursor == m_selend) + return; + m_selstart = start; + m_selend = qMin(start + length, (int)m_text.length()); + m_cursor = m_selend; + } else if (length < 0){ + if (start == m_selend && start + length == m_selstart && m_cursor == m_selstart) + return; + m_selstart = qMax(start + length, 0); + m_selend = start; + m_cursor = m_selstart; + } else if (m_selstart != m_selend) { + m_selstart = 0; + m_selend = 0; + m_cursor = start; + } else { + m_cursor = start; + emitCursorPositionChanged(); + return; + } + emit q->selectionChanged(); + emitCursorPositionChanged(); +} + +/*! + \internal + + Initializes the line control with a starting text value of \a txt. +*/ +void QQuickTextInputPrivate::init(const QString &txt) +{ + m_text = txt; + + updateDisplayText(); + m_cursor = m_text.length(); +} + +/*! + \internal + + Sets the password echo editing to \a editing. If password echo editing + is true, then the text of the password is displayed even if the echo + mode is set to QLineEdit::PasswordEchoOnEdit. Password echoing editing + does not affect other echo modes. +*/ +void QQuickTextInputPrivate::updatePasswordEchoEditing(bool editing) +{ + m_passwordEchoEditing = editing; + updateDisplayText(); +} + +/*! + \internal + + Returns the cursor position of the given \a x pixel value in relation + to the displayed text. The given \a betweenOrOn specified what kind + of cursor position is requested. +*/ +int QQuickTextInputPrivate::xToPos(int x, QTextLine::CursorPosition betweenOrOn) const +{ + Q_Q(const QQuickTextInput); + QRect cr = q->boundingRect().toRect(); + x-= cr.x() - hscroll; + return m_textLayout.lineAt(0).xToCursor(x, betweenOrOn); +} + +/*! + \internal + + Fixes the current text so that it is valid given any set validators. + + Returns true if the text was changed. Otherwise returns false. +*/ +bool QQuickTextInputPrivate::fixup() // this function assumes that validate currently returns != Acceptable +{ +#ifndef QT_NO_VALIDATOR + if (m_validator) { + QString textCopy = m_text; + int cursorCopy = m_cursor; + m_validator->fixup(textCopy); + if (m_validator->validate(textCopy, cursorCopy) == QValidator::Acceptable) { + if (textCopy != m_text || cursorCopy != m_cursor) + internalSetText(textCopy, cursorCopy); + return true; + } + } +#endif + return false; +} + +/*! + \internal + + Moves the cursor to the given position \a pos. If \a mark is true will + adjust the currently selected text. +*/ +void QQuickTextInputPrivate::moveCursor(int pos, bool mark) +{ + Q_Q(QQuickTextInput); + commitPreedit(); + + if (pos != m_cursor) { + separate(); + if (m_maskData) + pos = pos > m_cursor ? nextMaskBlank(pos) : prevMaskBlank(pos); + } + if (mark) { + int anchor; + if (m_selend > m_selstart && m_cursor == m_selstart) + anchor = m_selend; + else if (m_selend > m_selstart && m_cursor == m_selend) + anchor = m_selstart; + else + anchor = m_cursor; + m_selstart = qMin(anchor, pos); + m_selend = qMax(anchor, pos); + updateDisplayText(); + } else { + internalDeselect(); + } + m_cursor = pos; + if (mark || m_selDirty) { + m_selDirty = false; + emit q->selectionChanged(); + } + emitCursorPositionChanged(); +} + +/*! + \internal + + Applies the given input method event \a event to the text of the line + control +*/ +void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event) +{ + Q_Q(QQuickTextInput); + + int priorState = -1; + bool isGettingInput = !event->commitString().isEmpty() + || event->preeditString() != preeditAreaText() + || event->replacementLength() > 0; + bool cursorPositionChanged = false; + bool selectionChange = false; + + if (isGettingInput) { + // If any text is being input, remove selected text. + priorState = m_undoState; + if (m_echoMode == QQuickTextInput::PasswordEchoOnEdit && !m_passwordEchoEditing) { + updatePasswordEchoEditing(true); + m_selstart = 0; + m_selend = m_text.length(); + } + removeSelectedText(); + } + + int c = m_cursor; // cursor position after insertion of commit string + if (event->replacementStart() <= 0) + c += event->commitString().length() - qMin(-event->replacementStart(), event->replacementLength()); + + m_cursor += event->replacementStart(); + if (m_cursor < 0) + m_cursor = 0; + + // insert commit string + if (event->replacementLength()) { + m_selstart = m_cursor; + m_selend = m_selstart + event->replacementLength(); + removeSelectedText(); + } + if (!event->commitString().isEmpty()) { + internalInsert(event->commitString()); + cursorPositionChanged = true; + } + + m_cursor = qBound(0, c, m_text.length()); + + for (int i = 0; i < event->attributes().size(); ++i) { + const QInputMethodEvent::Attribute &a = event->attributes().at(i); + if (a.type == QInputMethodEvent::Selection) { + m_cursor = qBound(0, a.start + a.length, m_text.length()); + if (a.length) { + m_selstart = qMax(0, qMin(a.start, m_text.length())); + m_selend = m_cursor; + if (m_selend < m_selstart) { + qSwap(m_selstart, m_selend); + } + selectionChange = true; + } else { + m_selstart = m_selend = 0; + } + cursorPositionChanged = true; + } + } +#ifndef QT_NO_IM + m_textLayout.setPreeditArea(m_cursor, event->preeditString()); +#endif //QT_NO_IM + const int oldPreeditCursor = m_preeditCursor; + m_preeditCursor = event->preeditString().length(); + m_hideCursor = false; + QList formats; + for (int i = 0; i < event->attributes().size(); ++i) { + const QInputMethodEvent::Attribute &a = event->attributes().at(i); + if (a.type == QInputMethodEvent::Cursor) { + m_preeditCursor = a.start; + m_hideCursor = !a.length; + } else if (a.type == QInputMethodEvent::TextFormat) { + QTextCharFormat f = qvariant_cast(a.value).toCharFormat(); + if (f.isValid()) { + QTextLayout::FormatRange o; + o.start = a.start + m_cursor; + o.length = a.length; + o.format = f; + formats.append(o); + } + } + } + m_textLayout.setAdditionalFormats(formats); + updateDisplayText(/*force*/ true); + if (cursorPositionChanged) + emitCursorPositionChanged(); + else if (m_preeditCursor != oldPreeditCursor) + q->updateCursorRectangle(); + + bool tentativeCommitChanged = m_tentativeCommit != event->tentativeCommitString(); + + if (tentativeCommitChanged) { + m_textDirty = true; + m_tentativeCommit = event->tentativeCommitString(); + } + + if (isGettingInput || tentativeCommitChanged) + finishChange(priorState); + + if (selectionChange) + emit q->selectionChanged(); +} + +/*! + \internal + + Sets the selection to cover the word at the given cursor position. + The word boundaries are defined by the behavior of QTextLayout::SkipWords + cursor mode. +*/ +void QQuickTextInputPrivate::selectWordAtPos(int cursor) +{ + int next = cursor + 1; + if (next > end()) + --next; + int c = m_textLayout.previousCursorPosition(next, QTextLayout::SkipWords); + moveCursor(c, false); + // ## text layout should support end of words. + int end = m_textLayout.nextCursorPosition(c, QTextLayout::SkipWords); + while (end > cursor && m_text[end-1].isSpace()) + --end; + moveCursor(end, true); +} + +/*! + \internal + + Completes a change to the line control text. If the change is not valid + will undo the line control state back to the given \a validateFromState. + + If \a edited is true and the change is valid, will emit textEdited() in + addition to textChanged(). Otherwise only emits textChanged() on a valid + change. + + The \a update value is currently unused. +*/ +bool QQuickTextInputPrivate::finishChange(int validateFromState, bool update, bool /*edited*/) +{ + Q_Q(QQuickTextInput); + + Q_UNUSED(update) + + if (m_textDirty) { + // do validation + bool wasValidInput = m_validInput; + m_validInput = true; +#ifndef QT_NO_VALIDATOR + if (m_validator) { + QString textCopy = m_text; + int cursorCopy = m_cursor; + m_validInput = (m_validator->validate(textCopy, cursorCopy) != QValidator::Invalid); + if (m_validInput) { + if (m_text != textCopy) { + internalSetText(textCopy, cursorCopy); + return true; + } + m_cursor = cursorCopy; + + if (!m_tentativeCommit.isEmpty()) { + textCopy.insert(m_cursor, m_tentativeCommit); + bool validInput = m_validator->validate(textCopy, cursorCopy) != QValidator::Invalid; + if (!validInput) + m_tentativeCommit.clear(); + } + } else { + m_tentativeCommit.clear(); + } + } +#endif + if (validateFromState >= 0 && wasValidInput && !m_validInput) { + if (m_transactions.count()) + return false; + internalUndo(validateFromState); + m_history.resize(m_undoState); + if (m_modifiedState > m_undoState) + m_modifiedState = -1; + m_validInput = true; + m_textDirty = false; + } + updateDisplayText(); + + if (m_textDirty) { + m_textDirty = false; + q_func()->q_textChanged(); + } + } + if (m_selDirty) { + m_selDirty = false; + emit q->selectionChanged(); + } + emitCursorPositionChanged(); + return true; +} + +/*! + \internal + + An internal function for setting the text of the line control. +*/ +void QQuickTextInputPrivate::internalSetText(const QString &txt, int pos, bool edited) +{ + Q_Q(QQuickTextInput); + internalDeselect(); + QString oldText = m_text; + if (m_maskData) { + m_text = maskString(0, txt, true); + m_text += clearString(m_text.length(), m_maxLength - m_text.length()); + } else { + m_text = txt.isEmpty() ? txt : txt.left(m_maxLength); + } + m_history.clear(); + m_modifiedState = m_undoState = 0; + m_cursor = (pos < 0 || pos > m_text.length()) ? m_text.length() : pos; + m_textDirty = (oldText != m_text); + + bool changed = finishChange(-1, true, edited); +#ifdef QT_NO_ACCESSIBILITY + Q_UNUSED(changed) +#else + if (changed) + QAccessible::updateAccessibility(q, 0, QAccessible::TextUpdated); +#endif +} + + +/*! + \internal + + Adds the given \a command to the undo history + of the line control. Does not apply the command. +*/ +void QQuickTextInputPrivate::addCommand(const Command &cmd) +{ + if (m_separator && m_undoState && m_history[m_undoState - 1].type != Separator) { + m_history.resize(m_undoState + 2); + m_history[m_undoState++] = Command(Separator, m_cursor, 0, m_selstart, m_selend); + } else { + m_history.resize(m_undoState + 1); + } + m_separator = false; + m_history[m_undoState++] = cmd; +} + +/*! + \internal + + Inserts the given string \a s into the line + control. + + Also adds the appropriate commands into the undo history. + This function does not call finishChange(), and may leave the text + in an invalid state. +*/ +void QQuickTextInputPrivate::internalInsert(const QString &s) +{ + if (hasSelectedText()) + addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend)); + if (m_maskData) { + QString ms = maskString(m_cursor, s); + for (int i = 0; i < (int) ms.length(); ++i) { + addCommand (Command(DeleteSelection, m_cursor + i, m_text.at(m_cursor + i), -1, -1)); + addCommand(Command(Insert, m_cursor + i, ms.at(i), -1, -1)); + } + m_text.replace(m_cursor, ms.length(), ms); + m_cursor += ms.length(); + m_cursor = nextMaskBlank(m_cursor); + m_textDirty = true; + } else { + int remaining = m_maxLength - m_text.length(); + if (remaining != 0) { + m_text.insert(m_cursor, s.left(remaining)); + for (int i = 0; i < (int) s.left(remaining).length(); ++i) + addCommand(Command(Insert, m_cursor++, s.at(i), -1, -1)); + m_textDirty = true; + } + } +} + +/*! + \internal + + deletes a single character from the current text. If \a wasBackspace, + the character prior to the cursor is removed. Otherwise the character + after the cursor is removed. + + Also adds the appropriate commands into the undo history. + This function does not call finishChange(), and may leave the text + in an invalid state. +*/ +void QQuickTextInputPrivate::internalDelete(bool wasBackspace) +{ + if (m_cursor < (int) m_text.length()) { + if (hasSelectedText()) + addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend)); + addCommand(Command((CommandType)((m_maskData ? 2 : 0) + (wasBackspace ? Remove : Delete)), + m_cursor, m_text.at(m_cursor), -1, -1)); + if (m_maskData) { + m_text.replace(m_cursor, 1, clearString(m_cursor, 1)); + addCommand(Command(Insert, m_cursor, m_text.at(m_cursor), -1, -1)); + } else { + m_text.remove(m_cursor, 1); + } + m_textDirty = true; + } +} + +/*! + \internal + + removes the currently selected text from the line control. + + Also adds the appropriate commands into the undo history. + This function does not call finishChange(), and may leave the text + in an invalid state. +*/ +void QQuickTextInputPrivate::removeSelectedText() +{ + if (m_selstart < m_selend && m_selend <= (int) m_text.length()) { + separate(); + int i ; + addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend)); + if (m_selstart <= m_cursor && m_cursor < m_selend) { + // cursor is within the selection. Split up the commands + // to be able to restore the correct cursor position + for (i = m_cursor; i >= m_selstart; --i) + addCommand (Command(DeleteSelection, i, m_text.at(i), -1, 1)); + for (i = m_selend - 1; i > m_cursor; --i) + addCommand (Command(DeleteSelection, i - m_cursor + m_selstart - 1, m_text.at(i), -1, -1)); + } else { + for (i = m_selend-1; i >= m_selstart; --i) + addCommand (Command(RemoveSelection, i, m_text.at(i), -1, -1)); + } + if (m_maskData) { + m_text.replace(m_selstart, m_selend - m_selstart, clearString(m_selstart, m_selend - m_selstart)); + for (int i = 0; i < m_selend - m_selstart; ++i) + addCommand(Command(Insert, m_selstart + i, m_text.at(m_selstart + i), -1, -1)); + } else { + m_text.remove(m_selstart, m_selend - m_selstart); + } + if (m_cursor > m_selstart) + m_cursor -= qMin(m_cursor, m_selend) - m_selstart; + internalDeselect(); + m_textDirty = true; + } +} + +/*! + \internal + + Parses the input mask specified by \a maskFields to generate + the mask data used to handle input masks. +*/ +void QQuickTextInputPrivate::parseInputMask(const QString &maskFields) +{ + int delimiter = maskFields.indexOf(QLatin1Char(';')); + if (maskFields.isEmpty() || delimiter == 0) { + if (m_maskData) { + delete [] m_maskData; + m_maskData = 0; + m_maxLength = 32767; + internalSetText(QString()); + } + return; + } + + if (delimiter == -1) { + m_blank = QLatin1Char(' '); + m_inputMask = maskFields; + } else { + m_inputMask = maskFields.left(delimiter); + m_blank = (delimiter + 1 < maskFields.length()) ? maskFields[delimiter + 1] : QLatin1Char(' '); + } + + // calculate m_maxLength / m_maskData length + m_maxLength = 0; + QChar c = 0; + for (int i=0; i 0 && m_inputMask.at(i-1) == QLatin1Char('\\')) { + m_maxLength++; + continue; + } + if (c != QLatin1Char('\\') && c != QLatin1Char('!') && + c != QLatin1Char('<') && c != QLatin1Char('>') && + c != QLatin1Char('{') && c != QLatin1Char('}') && + c != QLatin1Char('[') && c != QLatin1Char(']')) + m_maxLength++; + } + + delete [] m_maskData; + m_maskData = new MaskInputData[m_maxLength]; + + MaskInputData::Casemode m = MaskInputData::NoCaseMode; + c = 0; + bool s; + bool escape = false; + int index = 0; + for (int i = 0; i < m_inputMask.length(); i++) { + c = m_inputMask.at(i); + if (escape) { + s = true; + m_maskData[index].maskChar = c; + m_maskData[index].separator = s; + m_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) { + m_maskData[index].maskChar = c; + m_maskData[index].separator = s; + m_maskData[index].caseMode = m; + index++; + } + } + } + internalSetText(m_text); +} + + +/*! + \internal + + checks if the key is valid compared to the inputMask +*/ +bool QQuickTextInputPrivate::isValidInput(QChar key, QChar mask) const +{ + switch (mask.unicode()) { + case 'A': + if (key.isLetter()) + return true; + break; + case 'a': + if (key.isLetter() || key == m_blank) + return true; + break; + case 'N': + if (key.isLetterOrNumber()) + return true; + break; + case 'n': + if (key.isLetterOrNumber() || key == m_blank) + return true; + break; + case 'X': + if (key.isPrint()) + return true; + break; + case 'x': + if (key.isPrint() || key == m_blank) + return true; + break; + case '9': + if (key.isNumber()) + return true; + break; + case '0': + if (key.isNumber() || key == m_blank) + return true; + break; + case 'D': + if (key.isNumber() && key.digitValue() > 0) + return true; + break; + case 'd': + if ((key.isNumber() && key.digitValue() > 0) || key == m_blank) + return true; + break; + case '#': + if (key.isNumber() || key == QLatin1Char('+') || key == QLatin1Char('-') || key == m_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 == m_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 == m_blank) + return true; + break; + default: + break; + } + return false; +} + +/*! + \internal + + Returns true if the given text \a str is valid for any + validator or input mask set for the line control. + + Otherwise returns false +*/ +bool QQuickTextInputPrivate::hasAcceptableInput(const QString &str) const +{ +#ifndef QT_NO_VALIDATOR + QString textCopy = str; + int cursorCopy = m_cursor; + if (m_validator && m_validator->validate(textCopy, cursorCopy) + != QValidator::Acceptable) + return false; +#endif + + if (!m_maskData) + return true; + + if (str.length() != m_maxLength) + return false; + + for (int i=0; i < m_maxLength; ++i) { + if (m_maskData[i].separator) { + if (str.at(i) != m_maskData[i].maskChar) + return false; + } else { + if (!isValidInput(str.at(i), m_maskData[i].maskChar)) + return false; + } + } + return true; +} + +/*! + \internal + + 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 QQuickTextInputPrivate::maskString(uint pos, const QString &str, bool clear) const +{ + if (pos >= (uint)m_maxLength) + return QString::fromLatin1(""); + + QString fill; + fill = clear ? clearString(0, m_maxLength) : m_text; + + int strIndex = 0; + QString s = QString::fromLatin1(""); + int i = pos; + while (i < m_maxLength) { + if (strIndex < str.length()) { + if (m_maskData[i].separator) { + s += m_maskData[i].maskChar; + if (str[(int)strIndex] == m_maskData[i].maskChar) + strIndex++; + ++i; + } else { + if (isValidInput(str[(int)strIndex], m_maskData[i].maskChar)) { + switch (m_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 && (!m_maskData[i-1].separator || m_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 m_blank if not + n = findInMask(i, true, false, str[(int)strIndex]); + if (n != -1) { + s += fill.mid(i, n-i); + switch (m_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; +} + + + +/*! + \internal + + Returns a "cleared" string with only separators and blank chars. + Calling this when no inputMask is set is undefined. +*/ +QString QQuickTextInputPrivate::clearString(uint pos, uint len) const +{ + if (pos >= (uint)m_maxLength) + return QString(); + + QString s; + int end = qMin((uint)m_maxLength, pos + len); + for (int i = pos; i < end; ++i) + if (m_maskData[i].separator) + s += m_maskData[i].maskChar; + else + s += m_blank; + + return s; +} + +/*! + \internal + + Strips blank parts of the input in a QQuickTextInputPrivate when an inputMask is set, + separators are still included. Typically "127.0__.0__.1__" becomes "127.0.0.1". +*/ +QString QQuickTextInputPrivate::stripString(const QString &str) const +{ + if (!m_maskData) + return str; + + QString s; + int end = qMin(m_maxLength, (int)str.length()); + for (int i = 0; i < end; ++i) + if (m_maskData[i].separator) + s += m_maskData[i].maskChar; + else + if (str[i] != m_blank) + s += str[i]; + + return s; +} + +/*! + \internal + searches forward/backward in m_maskData for either a separator or a m_blank +*/ +int QQuickTextInputPrivate::findInMask(int pos, bool forward, bool findSeparator, QChar searchChar) const +{ + if (pos >= m_maxLength || pos < 0) + return -1; + + int end = forward ? m_maxLength : -1; + int step = forward ? 1 : -1; + int i = pos; + + while (i != end) { + if (findSeparator) { + if (m_maskData[i].separator && m_maskData[i].maskChar == searchChar) + return i; + } else { + if (!m_maskData[i].separator) { + if (searchChar.isNull()) + return i; + else if (isValidInput(searchChar, m_maskData[i].maskChar)) + return i; + } + } + i += step; + } + return -1; +} + +void QQuickTextInputPrivate::internalUndo(int until) +{ + if (!isUndoAvailable()) + return; + internalDeselect(); + while (m_undoState && m_undoState > until) { + Command& cmd = m_history[--m_undoState]; + switch (cmd.type) { + case Insert: + m_text.remove(cmd.pos, 1); + m_cursor = cmd.pos; + break; + case SetSelection: + m_selstart = cmd.selStart; + m_selend = cmd.selEnd; + m_cursor = cmd.pos; + break; + case Remove: + case RemoveSelection: + m_text.insert(cmd.pos, cmd.uc); + m_cursor = cmd.pos + 1; + break; + case Delete: + case DeleteSelection: + m_text.insert(cmd.pos, cmd.uc); + m_cursor = cmd.pos; + break; + case Separator: + continue; + } + if (until < 0 && m_undoState) { + Command& next = m_history[m_undoState-1]; + if (next.type != cmd.type && next.type < RemoveSelection + && (cmd.type < RemoveSelection || next.type == Separator)) + break; + } + } + m_textDirty = true; + emitCursorPositionChanged(); +} + +void QQuickTextInputPrivate::internalRedo() +{ + if (!isRedoAvailable()) + return; + internalDeselect(); + while (m_undoState < (int)m_history.size()) { + Command& cmd = m_history[m_undoState++]; + switch (cmd.type) { + case Insert: + m_text.insert(cmd.pos, cmd.uc); + m_cursor = cmd.pos + 1; + break; + case SetSelection: + m_selstart = cmd.selStart; + m_selend = cmd.selEnd; + m_cursor = cmd.pos; + break; + case Remove: + case Delete: + case RemoveSelection: + case DeleteSelection: + m_text.remove(cmd.pos, 1); + m_selstart = cmd.selStart; + m_selend = cmd.selEnd; + m_cursor = cmd.pos; + break; + case Separator: + m_selstart = cmd.selStart; + m_selend = cmd.selEnd; + m_cursor = cmd.pos; + break; + } + if (m_undoState < (int)m_history.size()) { + Command& next = m_history[m_undoState]; + if (next.type != cmd.type && cmd.type < RemoveSelection && next.type != Separator + && (next.type < RemoveSelection || cmd.type == Separator)) + break; + } + } + m_textDirty = true; + emitCursorPositionChanged(); +} + +/*! + \internal + + If the current cursor position differs from the last emitted cursor + position, emits cursorPositionChanged(). +*/ +void QQuickTextInputPrivate::emitCursorPositionChanged() +{ + Q_Q(QQuickTextInput); + if (m_cursor != m_lastCursorPos) { + m_lastCursorPos = m_cursor; + + q->updateCursorRectangle(); + emit q->cursorPositionChanged(); + // XXX todo - not in 4.8? + #if 0 + resetCursorBlinkTimer(); + #endif + + if (!hasSelectedText()) { + if (lastSelectionStart != m_cursor) { + lastSelectionStart = m_cursor; + emit q->selectionStartChanged(); + } + if (lastSelectionEnd != m_cursor) { + lastSelectionEnd = m_cursor; + emit q->selectionEndChanged(); + } + } + +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(q, 0, QAccessible::TextCaretMoved); +#endif + } +} + + +void QQuickTextInputPrivate::setCursorBlinkPeriod(int msec) +{ + Q_Q(QQuickTextInput); + if (msec == m_blinkPeriod) + return; + if (m_blinkTimer) { + q->killTimer(m_blinkTimer); + } + if (msec) { + m_blinkTimer = q->startTimer(msec / 2); + m_blinkStatus = 1; + } else { + m_blinkTimer = 0; + if (m_blinkStatus == 1) + emit q->updateRect(inputMask().isEmpty() ? q->cursorRectangle() : QRect()); + } + m_blinkPeriod = msec; +} + +void QQuickTextInputPrivate::resetCursorBlinkTimer() +{ + Q_Q(QQuickTextInput); + if (m_blinkPeriod == 0 || m_blinkTimer == 0) + return; + q->killTimer(m_blinkTimer); + m_blinkTimer = q->startTimer(m_blinkPeriod / 2); + m_blinkStatus = 1; +} + +void QQuickTextInput::timerEvent(QTimerEvent *event) +{ + Q_D(QQuickTextInput); + if (event->timerId() == d->m_blinkTimer) { + d->m_blinkStatus = !d->m_blinkStatus; + updateRect(inputMask().isEmpty() ? cursorRectangle() : QRect()); + } else if (event->timerId() == d->m_deleteAllTimer) { + killTimer(d->m_deleteAllTimer); + d->m_deleteAllTimer = 0; + d->clear(); + } +} + +void QQuickTextInputPrivate::processKeyEvent(QKeyEvent* event) +{ + Q_Q(QQuickTextInput); + bool inlineCompletionAccepted = false; + + if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) { + if (hasAcceptableInput(m_text) || fixup()) { + emit q->accepted(); + } + if (inlineCompletionAccepted) + event->accept(); + else + event->ignore(); + return; + } + + if (m_echoMode == QQuickTextInput::PasswordEchoOnEdit + && !m_passwordEchoEditing + && !m_readOnly + && !event->text().isEmpty() + && !(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 + // ### resets current content. dubious code; you can + // navigate with keys up, down, back, and select(?), but if you press + // "left" or "right" it clears? + updatePasswordEchoEditing(true); + clear(); + } + + bool unknown = false; + bool visual = cursorMoveStyle() == Qt::VisualMoveStyle; + + if (false) { + } +#ifndef QT_NO_SHORTCUT + else if (event == QKeySequence::Undo) { + if (!m_readOnly) + undo(); + } + else if (event == QKeySequence::Redo) { + if (!m_readOnly) + redo(); + } + else if (event == QKeySequence::SelectAll) { + selectAll(); + } +#ifndef QT_NO_CLIPBOARD + else if (event == QKeySequence::Copy) { + copy(); + } + else if (event == QKeySequence::Paste) { + if (!m_readOnly) { + QClipboard::Mode mode = QClipboard::Clipboard; + paste(mode); + } + } + else if (event == QKeySequence::Cut) { + if (!m_readOnly) { + copy(); + del(); + } + } + else if (event == QKeySequence::DeleteEndOfLine) { + if (!m_readOnly) { + setSelection(m_cursor, end()); + copy(); + del(); + } + } +#endif //QT_NO_CLIPBOARD + else if (event == QKeySequence::MoveToStartOfLine || event == QKeySequence::MoveToStartOfBlock) { + home(0); + } + else if (event == QKeySequence::MoveToEndOfLine || event == QKeySequence::MoveToEndOfBlock) { + end(0); + } + else if (event == QKeySequence::SelectStartOfLine || event == QKeySequence::SelectStartOfBlock) { + home(1); + } + else if (event == QKeySequence::SelectEndOfLine || event == QKeySequence::SelectEndOfBlock) { + end(1); + } + else if (event == QKeySequence::MoveToNextChar) { + if (hasSelectedText()) { + moveCursor(selectionEnd(), false); + } else { + cursorForward(0, visual ? 1 : (layoutDirection() == Qt::LeftToRight ? 1 : -1)); + } + } + else if (event == QKeySequence::SelectNextChar) { + cursorForward(1, visual ? 1 : (layoutDirection() == Qt::LeftToRight ? 1 : -1)); + } + else if (event == QKeySequence::MoveToPreviousChar) { + if (hasSelectedText()) { + moveCursor(selectionStart(), false); + } else { + cursorForward(0, visual ? -1 : (layoutDirection() == Qt::LeftToRight ? -1 : 1)); + } + } + else if (event == QKeySequence::SelectPreviousChar) { + cursorForward(1, visual ? -1 : (layoutDirection() == Qt::LeftToRight ? -1 : 1)); + } + else if (event == QKeySequence::MoveToNextWord) { + if (m_echoMode == QQuickTextInput::Normal) + layoutDirection() == Qt::LeftToRight ? cursorWordForward(0) : cursorWordBackward(0); + else + layoutDirection() == Qt::LeftToRight ? end(0) : home(0); + } + else if (event == QKeySequence::MoveToPreviousWord) { + if (m_echoMode == QQuickTextInput::Normal) + layoutDirection() == Qt::LeftToRight ? cursorWordBackward(0) : cursorWordForward(0); + else if (!m_readOnly) { + layoutDirection() == Qt::LeftToRight ? home(0) : end(0); + } + } + else if (event == QKeySequence::SelectNextWord) { + if (m_echoMode == QQuickTextInput::Normal) + layoutDirection() == Qt::LeftToRight ? cursorWordForward(1) : cursorWordBackward(1); + else + layoutDirection() == Qt::LeftToRight ? end(1) : home(1); + } + else if (event == QKeySequence::SelectPreviousWord) { + if (m_echoMode == QQuickTextInput::Normal) + layoutDirection() == Qt::LeftToRight ? cursorWordBackward(1) : cursorWordForward(1); + else + layoutDirection() == Qt::LeftToRight ? home(1) : end(1); + } + else if (event == QKeySequence::Delete) { + if (!m_readOnly) + del(); + } + else if (event == QKeySequence::DeleteEndOfWord) { + if (!m_readOnly) { + cursorWordForward(true); + del(); + } + } + else if (event == QKeySequence::DeleteStartOfWord) { + if (!m_readOnly) { + cursorWordBackward(true); + del(); + } + } +#endif // QT_NO_SHORTCUT + else { + bool handled = false; + if (event->modifiers() & Qt::ControlModifier) { + switch (event->key()) { + case Qt::Key_Backspace: + if (!m_readOnly) { + cursorWordBackward(true); + del(); + } + break; + default: + if (!handled) + unknown = true; + } + } else { // ### check for *no* modifier + switch (event->key()) { + case Qt::Key_Backspace: + if (!m_readOnly) { + backspace(); + } + break; + default: + if (!handled) + 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); + unknown = false; + } + + if (unknown && !m_readOnly) { + QString t = event->text(); + if (!t.isEmpty() && t.at(0).isPrint()) { + insert(t); + event->accept(); + return; + } + } + + if (unknown) + event->ignore(); + else + event->accept(); +} + + QT_END_NAMESPACE diff --git a/src/quick/items/qquicktextinput_p.h b/src/quick/items/qquicktextinput_p.h index 3ea8647..7a07de6 100644 --- a/src/quick/items/qquicktextinput_p.h +++ b/src/quick/items/qquicktextinput_p.h @@ -256,6 +256,7 @@ protected: void mouseUngrabEvent(); bool event(QEvent *e); void focusInEvent(QFocusEvent *event); + void timerEvent(QTimerEvent *event); virtual void itemChange(ItemChange, const ItemChangeData &); QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data); @@ -276,7 +277,6 @@ private Q_SLOTS: void q_textChanged(); void selectionChanged(); void createCursor(); - void cursorPosChanged(); void updateCursorRectangle(); void updateRect(const QRect &r = QRect()); void q_canPasteChanged(); diff --git a/src/quick/items/qquicktextinput_p_p.h b/src/quick/items/qquicktextinput_p_p.h index 119df5f..b410bfd 100644 --- a/src/quick/items/qquicktextinput_p_p.h +++ b/src/quick/items/qquicktextinput_p_p.h @@ -47,12 +47,13 @@ #include "qquicktext_p.h" #include "qquickimplicitsizeitem_p_p.h" -#include "qquicklinecontrol_p.h" - #include #include #include +#include #include +#include +#include #include @@ -75,26 +76,50 @@ class Q_AUTOTEST_EXPORT QQuickTextInputPrivate : public QQuickImplicitSizeItemPr Q_DECLARE_PUBLIC(QQuickTextInput) public: QQuickTextInputPrivate() - : control(new QQuickLineControl(QString())) - , color((QRgb)0) - , style(QQuickText::Normal) - , styleColor((QRgb)0) - , hAlign(QQuickTextInput::AlignLeft) - , mouseSelectionMode(QQuickTextInput::SelectCharacters) - , inputMethodHints(Qt::ImhNone) - , textNode(0) - , hscroll(0) - , oldScroll(0) - , oldValidity(false) - , focused(false) - , focusOnPress(true) - , cursorVisible(false) - , autoScroll(true) - , selectByMouse(false) - , canPaste(false) - , hAlignImplicit(true) - , selectPressed(false) - , textLayoutDirty(true) + : color((QRgb)0) + , styleColor((QRgb)0) + , textNode(0) + , m_maskData(0) + , hscroll(0) + , oldScroll(0) + , m_cursor(0) + , m_preeditCursor(0) + , m_cursorWidth(1) + , m_blinkPeriod(0) + , m_blinkTimer(0) + , m_deleteAllTimer(0) + , m_ascent(0) + , m_maxLength(32767) + , m_lastCursorPos(-1) + , m_modifiedState(0) + , m_undoState(0) + , m_selstart(0) + , m_selend(0) + , style(QQuickText::Normal) + , hAlign(QQuickTextInput::AlignLeft) + , mouseSelectionMode(QQuickTextInput::SelectCharacters) + , inputMethodHints(Qt::ImhNone) + , m_layoutDirection(Qt::LayoutDirectionAuto) + , m_passwordCharacter(QLatin1Char('*')) + , oldValidity(false) + , focused(false) + , focusOnPress(true) + , cursorVisible(false) + , autoScroll(true) + , selectByMouse(false) + , canPaste(false) + , hAlignImplicit(true) + , selectPressed(false) + , textLayoutDirty(true) + , m_hideCursor(false) + , m_separator(0) + , m_readOnly(0) + , m_echoMode(QQuickTextInput::Normal) + , m_textDirty(0) + , m_selDirty(0) + , m_validInput(1) + , m_blinkStatus(0) + , m_passwordEchoEditing(false) { } @@ -102,44 +127,65 @@ public: { } - int xToPos(int x, QTextLine::CursorPosition betweenOrOn = QTextLine::CursorBetweenCharacters) const - { - Q_Q(const QQuickTextInput); - QRect cr = q->boundingRect().toRect(); - x-= cr.x() - hscroll; - return control->xToPos(x, betweenOrOn); - } - void init(); void startCreatingCursor(); void updateHorizontalScroll(); bool determineHorizontalAlignment(); bool setHAlign(QQuickTextInput::HAlignment, bool forceAlign = false); void mirrorChange(); - int calculateTextWidth(); bool sendMouseEventToInputContext(QMouseEvent *event); void updateInputMethodHints(); void hideCursor(); void showCursor(); - QQuickLineControl* control; + struct MaskInputData { + enum Casemode { NoCaseMode, Upper, Lower }; + QChar maskChar; // either the separator char or the inputmask + bool separator; + Casemode caseMode; + }; + + // undo/redo handling + enum CommandType { Separator, Insert, Remove, Delete, RemoveSelection, DeleteSelection, SetSelection }; + struct Command { + inline Command() {} + inline Command(CommandType t, int p, QChar c, int ss, int se) : type(t),uc(c),pos(p),selStart(ss),selEnd(se) {} + uint type : 4; + QChar uc; + int pos, selStart, selEnd; + }; + enum DrawFlags { + DrawText = 0x01, + DrawSelections = 0x02, + DrawCursor = 0x04, + DrawAll = DrawText | DrawSelections | DrawCursor + }; + + QTextLayout m_textLayout; + QString m_text; + QString m_inputMask; + QString m_cancelText; + QString m_tentativeCommit; + QPalette m_palette; QFont font; QFont sourceFont; QColor color; QColor selectionColor; QColor selectedTextColor; - QQuickText::TextStyle style; QColor styleColor; - QQuickTextInput::HAlignment hAlign; - QQuickTextInput::SelectionMode mouseSelectionMode; - Qt::InputMethodHints inputMethodHints; QPointer cursorComponent; QPointer cursorItem; +#ifndef QT_NO_VALIDATOR + QPointer m_validator; +#endif QPointF pressPos; QQuickTextNode *textNode; + MaskInputData *m_maskData; QElapsedTimer tripleClickTimer; QPoint tripleClickStartPoint; + QList m_transactions; + QVector m_history; int lastSelectionStart; int lastSelectionEnd; @@ -147,6 +193,28 @@ public: int oldWidth; int hscroll; int oldScroll; + int m_cursor; + int m_preeditCursor; + int m_cursorWidth; + int m_blinkPeriod; // 0 for non-blinking cursor + int m_blinkTimer; + int m_deleteAllTimer; + int m_ascent; + int m_maxLength; + int m_lastCursorPos; + int m_modifiedState; + int m_undoState; + int m_selstart; + int m_selend; + + QQuickText::TextStyle style; + QQuickTextInput::HAlignment hAlign; + QQuickTextInput::SelectionMode mouseSelectionMode; + Qt::InputMethodHints inputMethodHints; + Qt::LayoutDirection m_layoutDirection; + + QChar m_blank; + QChar m_passwordCharacter; bool oldValidity:1; bool focused:1; @@ -159,12 +227,213 @@ public: bool selectPressed:1; bool textLayoutDirty:1; + uint m_hideCursor : 1; // used to hide the m_cursor inside preedit areas + uint m_separator : 1; + uint m_readOnly : 1; + uint m_echoMode : 2; + uint m_textDirty : 1; + uint m_selDirty : 1; + uint m_validInput : 1; + uint m_blinkStatus : 1; + uint m_passwordEchoEditing; + static inline QQuickTextInputPrivate *get(QQuickTextInput *t) { return t->d_func(); } bool hasPendingTripleClick() const { return !tripleClickTimer.hasExpired(qApp->styleHints()->mouseDoubleClickInterval()); } + + + int nextMaskBlank(int pos) + { + int c = findInMask(pos, true, false); + m_separator |= (c != pos); + return (c != -1 ? c : m_maxLength); + } + + int prevMaskBlank(int pos) + { + int c = findInMask(pos, false, false); + m_separator |= (c != pos); + return (c != -1 ? c : 0); + } + + bool isUndoAvailable() const { return !m_readOnly && m_undoState; } + bool isRedoAvailable() const { return !m_readOnly && m_undoState < (int)m_history.size(); } + void clearUndo() { m_history.clear(); m_modifiedState = m_undoState = 0; } + + bool isModified() const { return m_modifiedState != m_undoState; } + void setModified(bool modified) { m_modifiedState = modified ? -1 : m_undoState; } + + bool allSelected() const { return !m_text.isEmpty() && m_selstart == 0 && m_selend == (int)m_text.length(); } + bool hasSelectedText() const { return !m_text.isEmpty() && m_selend > m_selstart; } + + int calculateTextHeight() const { return qRound(m_textLayout.lineAt(0).height()); } + int calculateTextWidth() const { return qRound(m_textLayout.lineAt(0).naturalTextWidth()); } + int ascent() const { return m_ascent; } + + void setSelection(int start, int length); + + inline QString selectedText() const { return hasSelectedText() ? m_text.mid(m_selstart, m_selend - m_selstart) : QString(); } + QString textBeforeSelection() const { return hasSelectedText() ? m_text.left(m_selstart) : QString(); } + QString textAfterSelection() const { return hasSelectedText() ? m_text.mid(m_selend) : QString(); } + + int selectionStart() const { return hasSelectedText() ? m_selstart : -1; } + int selectionEnd() const { return hasSelectedText() ? m_selend : -1; } + bool inSelection(int x) const + { + if (m_selstart >= m_selend) + return false; + int pos = xToPos(x, QTextLine::CursorOnCharacter); + return pos >= m_selstart && pos < m_selend; + } + + void removeSelection() + { + int priorState = m_undoState; + removeSelectedText(); + finishChange(priorState); + } + + int start() const { return 0; } + int end() const { return m_text.length(); } + + QString realText() const; + +#ifndef QT_NO_CLIPBOARD + void copy(QClipboard::Mode mode = QClipboard::Clipboard) const; + void paste(QClipboard::Mode mode = QClipboard::Clipboard); +#endif + + void commitPreedit(); + + Qt::CursorMoveStyle cursorMoveStyle() const { return m_textLayout.cursorMoveStyle(); } + void setCursorMoveStyle(Qt::CursorMoveStyle style) { m_textLayout.setCursorMoveStyle(style); } + + void moveCursor(int pos, bool mark = false); + void cursorForward(bool mark, int steps) + { + int c = m_cursor; + if (steps > 0) { + while (steps--) + c = cursorMoveStyle() == Qt::VisualMoveStyle ? m_textLayout.rightCursorPosition(c) + : m_textLayout.nextCursorPosition(c); + } else if (steps < 0) { + while (steps++) + c = cursorMoveStyle() == Qt::VisualMoveStyle ? m_textLayout.leftCursorPosition(c) + : m_textLayout.previousCursorPosition(c); + } + moveCursor(c, mark); + } + + void cursorWordForward(bool mark) { moveCursor(m_textLayout.nextCursorPosition(m_cursor, QTextLayout::SkipWords), mark); } + void cursorWordBackward(bool mark) { moveCursor(m_textLayout.previousCursorPosition(m_cursor, QTextLayout::SkipWords), mark); } + + void home(bool mark) { moveCursor(0, mark); } + void end(bool mark) { moveCursor(q_func()->text().length(), mark); } + + int xToPos(int x, QTextLine::CursorPosition = QTextLine::CursorBetweenCharacters) const; + + qreal cursorToX(int cursor) const { return m_textLayout.lineAt(0).cursorToX(cursor); } + qreal cursorToX() const + { + int cursor = m_cursor; + if (m_preeditCursor != -1) + cursor += m_preeditCursor; + return cursorToX(cursor); + } + + void backspace(); + void del(); + void deselect() { internalDeselect(); finishChange(); } + void selectAll() { m_selstart = m_selend = m_cursor = 0; moveCursor(m_text.length(), true); } + + void insert(const QString &); + void clear(); + void undo() { internalUndo(); finishChange(-1, true); } + void redo() { internalRedo(); finishChange(); } + void selectWordAtPos(int); + + void setCursorPosition(int pos) { if (pos <= m_text.length()) moveCursor(qMax(0, pos)); } + + bool fixup(); + + QString inputMask() const { return m_maskData ? m_inputMask + QLatin1Char(';') + m_blank : QString(); } + void setInputMask(const QString &mask) + { + parseInputMask(mask); + if (m_maskData) + moveCursor(nextMaskBlank(0)); + } + + // input methods +#ifndef QT_NO_IM + bool composeMode() const { return !m_textLayout.preeditAreaText().isEmpty(); } +#endif + + QString preeditAreaText() const { return m_textLayout.preeditAreaText(); } + + void updatePasswordEchoEditing(bool editing); + + Qt::LayoutDirection layoutDirection() const { + if (m_layoutDirection == Qt::LayoutDirectionAuto) { + if (m_text.isEmpty()) + return QGuiApplication::keyboardInputDirection(); + return m_text.isRightToLeft() ? Qt::RightToLeft : Qt::LeftToRight; + } + return m_layoutDirection; + } + void setLayoutDirection(Qt::LayoutDirection direction) + { + if (direction != m_layoutDirection) { + m_layoutDirection = direction; + updateDisplayText(); + } + } + + void processInputMethodEvent(QInputMethodEvent *event); + void processKeyEvent(QKeyEvent* ev); + + void setCursorBlinkPeriod(int msec); + void resetCursorBlinkTimer(); + +private: + void init(const QString &txt); + void removeSelectedText(); + void internalSetText(const QString &txt, int pos = -1, bool edited = true); + void updateDisplayText(bool forceUpdate = false); + + void internalInsert(const QString &s); + void internalDelete(bool wasBackspace = false); + void internalRemove(int pos); + + inline void internalDeselect() + { + m_selDirty |= (m_selend > m_selstart); + m_selstart = m_selend = 0; + } + + void internalUndo(int until = -1); + void internalRedo(); + + void emitCursorPositionChanged(); + + bool finishChange(int validateFromState = -1, bool update = false, bool edited = true); + + void addCommand(const Command& cmd); + + inline void separate() { m_separator = true; } + + + // masking + void parseInputMask(const QString &maskFields); + bool isValidInput(QChar key, QChar mask) const; + bool hasAcceptableInput(const QString &text) const; + QString maskString(uint pos, const QString &str, bool clear = false) const; + QString clearString(uint pos, uint len) const; + QString stripString(const QString &str) const; + int findInMask(int pos, bool forward, bool findSeparator, QChar searchChar = QChar()) const; }; QT_END_NAMESPACE diff --git a/tests/auto/qtquick2/qquicktextinput/tst_qquicktextinput.cpp b/tests/auto/qtquick2/qquicktextinput/tst_qquicktextinput.cpp index e4ea569..6b6fd73 100644 --- a/tests/auto/qtquick2/qquicktextinput/tst_qquicktextinput.cpp +++ b/tests/auto/qtquick2/qquicktextinput/tst_qquicktextinput.cpp @@ -1823,8 +1823,7 @@ void tst_qquicktextinput::canPasteEmpty() { QQuickTextInput *textInput = qobject_cast(textInputComponent.create()); QVERIFY(textInput != 0); - QQuickLineControl lc; - bool cp = !lc.isReadOnly() && QGuiApplication::clipboard()->text().length() != 0; + bool cp = !textInput->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0; QCOMPARE(textInput->canPaste(), cp); #endif @@ -1841,8 +1840,7 @@ void tst_qquicktextinput::canPaste() { QQuickTextInput *textInput = qobject_cast(textInputComponent.create()); QVERIFY(textInput != 0); - QQuickLineControl lc; - bool cp = !lc.isReadOnly() && QGuiApplication::clipboard()->text().length() != 0; + bool cp = !textInput->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0; QCOMPARE(textInput->canPaste(), cp); #endif -- 2.7.4