+
+}
+
+void QQuickTextInput::q_updateAlignment()
+{
+ Q_D(QQuickTextInput);
+ if (d->determineHorizontalAlignment()) {
+ d->updateLayout();
+ updateCursorRectangle();
+ }
+}
+
+// ### 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) {
+ str.fill(m_passwordCharacter);
+ if (m_passwordEchoTimer.isActive() && m_cursor > 0 && m_cursor <= m_text.length()) {
+ int cursor = m_cursor - 1;
+ QChar uc = m_text.at(cursor);
+ str[cursor] = uc;
+ if (cursor > 0 && uc.unicode() >= 0xdc00 && uc.unicode() < 0xe000) {
+ // second half of a surrogate, check if we have the first half as well,
+ // if yes restore both at once
+ uc = m_text.at(cursor - 1);
+ if (uc.unicode() >= 0xd800 && uc.unicode() < 0xdc00)
+ str[cursor - 1] = uc;
+ }
+ }
+ } else if (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);
+ }
+
+ if (str != orig || forceUpdate) {
+ m_textLayout.setText(str);
+ updateLayout(); // polish?
+ emit q_func()->displayTextChanged();
+ }
+}
+
+qreal QQuickTextInputPrivate::getImplicitWidth() const
+{
+ Q_Q(const QQuickTextInput);
+ if (!requireImplicitWidth) {
+ QQuickTextInputPrivate *d = const_cast<QQuickTextInputPrivate *>(this);
+ d->requireImplicitWidth = true;
+
+ if (q->isComponentComplete()) {
+ // One time cost, only incurred if implicitWidth is first requested after
+ // componentComplete.
+ QTextLayout layout(m_text);
+
+ QTextOption option = m_textLayout.textOption();
+ option.setTextDirection(m_layoutDirection);
+ option.setFlags(QTextOption::IncludeTrailingSpaces);
+ option.setWrapMode(QTextOption::WrapMode(wrapMode));
+ option.setAlignment(Qt::Alignment(q->effectiveHAlign()));
+ layout.setTextOption(option);
+ layout.setFont(font);
+ layout.setPreeditArea(m_textLayout.preeditAreaPosition(), m_textLayout.preeditAreaText());
+ layout.beginLayout();
+
+ QTextLine line = layout.createLine();
+ line.setLineWidth(INT_MAX);
+ d->implicitWidth = qCeil(line.naturalTextWidth());
+
+ layout.endLayout();
+ }
+ }
+ return implicitWidth;
+}
+
+void QQuickTextInputPrivate::updateLayout()
+{
+ Q_Q(QQuickTextInput);
+
+ if (!q->isComponentComplete())
+ return;
+
+
+ QTextOption option = m_textLayout.textOption();
+ option.setTextDirection(layoutDirection());
+ option.setWrapMode(QTextOption::WrapMode(wrapMode));
+ option.setAlignment(Qt::Alignment(q->effectiveHAlign()));
+ m_textLayout.setTextOption(option);
+ m_textLayout.setFont(font);
+
+ m_textLayout.beginLayout();
+
+ QTextLine line = m_textLayout.createLine();
+ if (requireImplicitWidth) {
+ line.setLineWidth(INT_MAX);
+ const bool wasInLayout = inLayout;
+ inLayout = true;
+ q->setImplicitWidth(qCeil(line.naturalTextWidth()));
+ inLayout = wasInLayout;
+ if (inLayout) // probably the result of a binding loop, but by letting it
+ return; // get this far we'll get a warning to that effect.
+ }
+ qreal lineWidth = q->widthValid() ? q->width() : INT_MAX;
+ qreal height = 0;
+ qreal width = 0;
+ do {
+ line.setLineWidth(lineWidth);
+ line.setPosition(QPointF(0, height));
+
+ height += line.height();
+ width = qMax(width, line.naturalTextWidth());
+
+ line = m_textLayout.createLine();
+ } while (line.isValid());
+ m_textLayout.endLayout();
+
+ option.setWrapMode(QTextOption::NoWrap);
+ m_textLayout.setTextOption(option);
+
+ textLayoutDirty = true;
+
+ const QSizeF previousSize = contentSize;
+ contentSize = QSizeF(width, height);
+
+ updateType = UpdatePaintNode;
+ q->update();
+
+ if (!requireImplicitWidth && !q->widthValid())
+ q->setImplicitSize(width, height);
+ else
+ q->setImplicitHeight(height);
+
+ if (previousSize != contentSize)
+ emit q->contentSizeChanged();
+}
+
+#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
+*/
+void QQuickTextInputPrivate::commitPreedit()
+{
+ Q_Q(QQuickTextInput);
+
+ if (!hasImState)
+ return;
+
+ qApp->inputMethod()->commit();
+
+ if (!hasImState)
+ return;
+
+ QInputMethodEvent ev;
+ QCoreApplication::sendEvent(q, &ev);
+}
+
+void QQuickTextInputPrivate::cancelPreedit()
+{
+ Q_Q(QQuickTextInput);
+
+ if (!hasImState)
+ return;
+
+ qApp->inputMethod()->reset();
+
+ QInputMethodEvent ev;
+ QCoreApplication::sendEvent(q, &ev);
+}
+
+/*!
+ \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 (separateSelection()) {
+ 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 (separateSelection()) {
+ 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;
+ if (separateSelection())
+ removeSelectedText();
+ internalInsert(newText);
+ finishChange(priorState);
+}
+
+/*!
+ \internal
+
+ Clears the line control text.
+*/
+void QQuickTextInputPrivate::clear()
+{
+ int priorState = m_undoState;
+ separateSelection();
+ 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();
+ q->updateInputMethod(Qt::ImCursorRectangle | Qt::ImAnchorPosition
+ | Qt::ImCursorPosition | Qt::ImCurrentSelection);
+}
+
+/*!
+ \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)
+{
+ cancelPasswordEchoTimer();
+ m_passwordEchoEditing = editing;
+ updateDisplayText();
+}
+
+/*!
+ \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);
+ } else {
+ internalDeselect();
+ }
+ m_cursor = pos;
+ if (mark || m_selDirty) {
+ m_selDirty = false;
+ emit q->selectionChanged();
+ }
+ emitCursorPositionChanged();
+ q->updateInputMethod();
+}
+
+/*!
+ \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;
+ m_preeditDirty = event->preeditString() != preeditAreaText();
+
+ if (isGettingInput) {
+ // If any text is being input, remove selected text.
+ priorState = m_undoState;
+ separateSelection();
+ 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();
+ m_selend = qMin(m_selend, m_text.length());
+ 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();
+ hasImState = !event->preeditString().isEmpty();
+ bool cursorVisible = true;
+ QList<QTextLayout::FormatRange> formats;
+ for (int i = 0; i < event->attributes().size(); ++i) {
+ const QInputMethodEvent::Attribute &a = event->attributes().at(i);
+ if (a.type == QInputMethodEvent::Cursor) {
+ hasImState = true;
+ m_preeditCursor = a.start;
+ cursorVisible = a.length != 0;
+ } else if (a.type == QInputMethodEvent::TextFormat) {
+ hasImState = true;
+ QTextCharFormat f = qvariant_cast<QTextFormat>(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())
+ || m_preeditCursor != oldPreeditCursor
+ || isGettingInput) {
+ q->updateCursorRectangle();
+ }
+
+ if (isGettingInput)
+ finishChange(priorState);
+
+ q->setCursorVisible(cursorVisible);
+
+ if (selectionChange) {
+ emit q->selectionChanged();
+ q->updateInputMethod(Qt::ImCursorRectangle | Qt::ImAnchorPosition
+ | Qt::ImCursorPosition | Qt::ImCurrentSelection);
+ }
+}
+
+/*!
+ \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)
+ bool inputMethodAttributesChanged = m_textDirty || m_selDirty;
+ bool alignmentChanged = false;
+
+ if (m_textDirty) {
+ // do validation
+ bool wasValidInput = m_validInput;
+ bool wasAcceptable = m_acceptableInput;
+ m_validInput = true;
+ m_acceptableInput = true;
+#ifndef QT_NO_VALIDATOR
+ if (m_validator) {
+ QString textCopy = m_text;
+ int cursorCopy = m_cursor;
+ QValidator::State state = m_validator->validate(textCopy, cursorCopy);
+ m_validInput = state != QValidator::Invalid;
+ m_acceptableInput = state == QValidator::Acceptable;
+ if (m_validInput) {
+ if (m_text != textCopy) {
+ internalSetText(textCopy, cursorCopy);
+ return true;
+ }
+ m_cursor = cursorCopy;
+ }
+ }
+#endif
+ if (validateFromState >= 0 && wasValidInput && !m_validInput) {
+ if (m_transactions.count())
+ return false;
+ internalUndo(validateFromState);
+ m_history.resize(m_undoState);
+ m_validInput = true;
+ m_acceptableInput = wasAcceptable;
+ m_textDirty = false;
+ }
+
+ if (m_textDirty) {
+ m_textDirty = false;
+ m_preeditDirty = false;
+ alignmentChanged = determineHorizontalAlignment();
+ emit q->textChanged();
+ }
+
+ updateDisplayText(alignmentChanged);
+
+ if (m_acceptableInput != wasAcceptable)
+ emit q->acceptableInputChanged();
+ }
+ if (m_preeditDirty) {
+ m_preeditDirty = false;
+ if (determineHorizontalAlignment()) {
+ alignmentChanged = true;
+ updateLayout();
+ }
+ }
+
+ if (m_selDirty) {
+ m_selDirty = false;
+ emit q->selectionChanged();
+ }
+
+ inputMethodAttributesChanged |= (m_cursor != m_lastCursorPos);
+ if (inputMethodAttributesChanged)
+ q->updateInputMethod();
+ emitUndoRedoChanged();
+
+ if (!emitCursorPositionChanged() && alignmentChanged)
+ q->updateCursorRectangle();
+
+ 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_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) {
+ QAccessibleTextUpdateEvent ev(q, 0, oldText, m_text);
+ QAccessible::updateAccessibility(&ev);
+ }
+#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)
+{
+ Q_Q(QQuickTextInput);
+ if (m_echoMode == QQuickTextInput::Password) {
+ int delay = qGuiApp->styleHints()->passwordMaskDelay();
+ if (delay > 0)
+ m_passwordEchoTimer.start(delay, q);
+ }
+ Q_ASSERT(!hasSelectedText()); // insert(), processInputMethodEvent() call removeSelectedText() first.
+ 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()) {
+ cancelPasswordEchoTimer();
+ Q_ASSERT(!hasSelectedText()); // del(), backspace() call removeSelectedText() first.
+ 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()) {
+ cancelPasswordEchoTimer();
+ int i ;
+ 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
+
+ Adds the current selection to the undo history.
+
+ Returns true if there is a current selection and false otherwise.
+*/
+
+bool QQuickTextInputPrivate::separateSelection()
+{
+ if (hasSelectedText()) {
+ separate();
+ addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend));
+ return true;
+ } else {
+ return false;
+ }
+}
+
+/*!
+ \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<m_inputMask.length(); i++) {
+ c = m_inputMask.at(i);
+ if (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
+*/
+QQuickTextInputPrivate::ValidatorState QQuickTextInputPrivate::hasAcceptableInput(const QString &str) const
+{
+#ifndef QT_NO_VALIDATOR
+ QString textCopy = str;
+ int cursorCopy = m_cursor;
+ if (m_validator) {
+ QValidator::State state = m_validator->validate(textCopy, cursorCopy);
+ if (state != QValidator::Acceptable)
+ return ValidatorState(state);
+ }
+#endif
+
+ if (!m_maskData)
+ return AcceptableInput;
+
+ if (str.length() != m_maxLength)
+ return InvalidInput;
+
+ for (int i=0; i < m_maxLength; ++i) {
+ if (m_maskData[i].separator) {
+ if (str.at(i) != m_maskData[i].maskChar)
+ return InvalidInput;
+ } else {
+ if (!isValidInput(str.at(i), m_maskData[i].maskChar))
+ return InvalidInput;
+ }
+ }
+ return AcceptableInput;
+}
+
+/*!
+ \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;
+ cancelPasswordEchoTimer();
+ 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;
+ }
+ }
+ }
+ separate();
+ m_textDirty = true;
+}
+
+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;
+}
+
+void QQuickTextInputPrivate::emitUndoRedoChanged()
+{
+ Q_Q(QQuickTextInput);
+ const bool previousUndo = canUndo;
+ const bool previousRedo = canRedo;
+
+ canUndo = isUndoAvailable();
+ canRedo = isRedoAvailable();
+
+ if (previousUndo != canUndo)
+ emit q->canUndoChanged();
+ if (previousRedo != canRedo)
+ emit q->canRedoChanged();
+}
+
+/*!
+ \internal
+
+ If the current cursor position differs from the last emitted cursor
+ position, emits cursorPositionChanged().
+*/
+bool QQuickTextInputPrivate::emitCursorPositionChanged()
+{
+ Q_Q(QQuickTextInput);
+ if (m_cursor != m_lastCursorPos) {
+ m_lastCursorPos = m_cursor;
+
+ q->updateCursorRectangle();
+ emit q->cursorPositionChanged();
+
+ 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
+ QAccessibleTextCursorEvent ev(q, m_cursor);
+ QAccessible::updateAccessibility(&ev);
+#endif
+
+ return true;
+ }
+ return false;
+}
+
+
+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) {
+ updateType = UpdatePaintNode;
+ q->update();
+ }
+ }
+ m_blinkPeriod = msec;
+}
+
+void QQuickTextInput::timerEvent(QTimerEvent *event)
+{
+ Q_D(QQuickTextInput);
+ if (event->timerId() == d->m_blinkTimer) {
+ d->m_blinkStatus = !d->m_blinkStatus;
+ d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
+ update();
+ } else if (event->timerId() == d->m_passwordEchoTimer.timerId()) {
+ d->m_passwordEchoTimer.stop();
+ d->updateDisplayText();
+ updateCursorRectangle();
+ }
+}
+
+void QQuickTextInputPrivate::processKeyEvent(QKeyEvent* event)
+{
+ Q_Q(QQuickTextInput);
+
+ if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
+ if (hasAcceptableInput(m_text) || fixup()) {
+ emit q->accepted();
+ }
+ 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) {
+ q->undo();
+ }
+ else if (event == QKeySequence::Redo) {
+ q->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)
+ deleteEndOfLine();
+ }
+#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)
+ deleteEndOfWord();
+ }
+ else if (event == QKeySequence::DeleteStartOfWord) {
+ if (!m_readOnly)
+ deleteStartOfWord();
+ }
+#endif // QT_NO_SHORTCUT
+ else {
+ bool handled = false;
+ if (event->modifiers() & Qt::ControlModifier) {
+ switch (event->key()) {
+ case Qt::Key_Backspace:
+ if (!m_readOnly)
+ deleteStartOfWord();
+ 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();
+}
+
+/*!
+ \internal
+
+ Deletes the portion of the word before the current cursor position.
+*/
+
+void QQuickTextInputPrivate::deleteStartOfWord()
+{
+ int priorState = m_undoState;
+ Command cmd(SetSelection, m_cursor, 0, m_selstart, m_selend);
+ separate();
+ cursorWordBackward(true);
+ addCommand(cmd);
+ removeSelectedText();
+ finishChange(priorState);
+}
+
+/*!
+ \internal
+
+ Deletes the portion of the word after the current cursor position.
+*/
+
+void QQuickTextInputPrivate::deleteEndOfWord()
+{
+ int priorState = m_undoState;
+ Command cmd(SetSelection, m_cursor, 0, m_selstart, m_selend);
+ separate();
+ cursorWordForward(true);
+ // moveCursor (sometimes) calls separate() so we need to add the command after that so the
+ // cursor position and selection are restored in the same undo operation as the remove.
+ addCommand(cmd);
+ removeSelectedText();
+ finishChange(priorState);
+}
+
+/*!
+ \internal
+
+ Deletes all text from the cursor position to the end of the line.
+*/
+
+void QQuickTextInputPrivate::deleteEndOfLine()
+{
+ int priorState = m_undoState;
+ Command cmd(SetSelection, m_cursor, 0, m_selstart, m_selend);
+ separate();
+ setSelection(m_cursor, end());
+ addCommand(cmd);
+ removeSelectedText();
+ finishChange(priorState);