Fix QQuickTextInput::moveCursorSelection()
[profile/ivi/qtdeclarative.git] / src / quick / items / qquicktextinput.cpp
index e219934..22551c9 100644 (file)
@@ -1,38 +1,38 @@
 /****************************************************************************
 **
 /****************************************************************************
 **
-** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
 **
 ** This file is part of the QtQml module of the Qt Toolkit.
 **
 ** $QT_BEGIN_LICENSE:LGPL$
 **
 ** This file is part of the QtQml module of the Qt Toolkit.
 **
 ** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
 ** GNU Lesser General Public License Usage
 ** 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.
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 **
 **
-** In addition, as a special exception, Nokia gives you certain additional
-** rights. These rights are described in the Nokia Qt LGPL Exception
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
 **
 ** GNU General Public License Usage
 ** 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.
-**
-**
-**
-**
+** 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.
 **
 **
 ** $QT_END_LICENSE$
 **
 **
 ** $QT_END_LICENSE$
 
 #include "qquicktextinput_p.h"
 #include "qquicktextinput_p_p.h"
 
 #include "qquicktextinput_p.h"
 #include "qquicktextinput_p_p.h"
-#include "qquickcanvas.h"
+#include "qquickwindow.h"
+#include "qquicktextutil_p.h"
 
 #include <private/qqmlglobal_p.h>
 
 
 #include <private/qqmlglobal_p.h>
 
+
 #include <QtCore/qcoreapplication.h>
 #include <QtQml/qqmlinfo.h>
 #include <QtGui/qevent.h>
 #include <QtCore/qcoreapplication.h>
 #include <QtQml/qqmlinfo.h>
 #include <QtGui/qevent.h>
@@ -64,13 +66,15 @@ QT_BEGIN_NAMESPACE
 DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
 
 /*!
 DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
 
 /*!
-    \qmlclass TextInput QQuickTextInput
+    \qmltype TextInput
+    \instantiates QQuickTextInput
     \inqmlmodule QtQuick 2
     \inqmlmodule QtQuick 2
-    \ingroup qml-basic-visual-elements
-    \brief The TextInput item displays an editable line of text.
+    \ingroup qtquick-visual
+    \ingroup qtquick-input
     \inherits Item
     \inherits Item
+    \brief Displays an editable line of text
 
 
-    The TextInput element displays a single line of editable plain text.
+    The TextInput type displays a single line of editable plain text.
 
     TextInput is used to accept a line of text input. Input constraints
     can be placed on a TextInput item (for example, through a \l validator or \l inputMask),
 
     TextInput is used to accept a line of text input. Input constraints
     can be placed on a TextInput item (for example, through a \l validator or \l inputMask),
@@ -102,8 +106,8 @@ void QQuickTextInput::componentComplete()
     d->checkIsValid();
     d->updateLayout();
     updateCursorRectangle();
     d->checkIsValid();
     d->updateLayout();
     updateCursorRectangle();
-    if (d->cursorComponent && d->cursorComponent->isReady())
-        createCursor();
+    if (d->cursorComponent && isCursorVisible())
+        QQuickTextUtil::createCursor(d);
 }
 
 /*!
 }
 
 /*!
@@ -130,6 +134,42 @@ void QQuickTextInput::setText(const QString &s)
     d->internalSetText(s, -1, false);
 }
 
     d->internalSetText(s, -1, false);
 }
 
+
+/*!
+    \qmlproperty enumeration QtQuick2::TextInput::renderType
+
+    Override the default rendering type for this component.
+
+    Supported render types are:
+    \list
+    \li Text.QtRendering - the default
+    \li Text.NativeRendering
+    \endlist
+
+    Select Text.NativeRendering if you prefer text to look native on the target platform and do
+    not require advanced features such as transformation of the text. Using such features in
+    combination with the NativeRendering render type will lend poor and sometimes pixelated
+    results.
+*/
+QQuickTextInput::RenderType QQuickTextInput::renderType() const
+{
+    Q_D(const QQuickTextInput);
+    return d->renderType;
+}
+
+void QQuickTextInput::setRenderType(QQuickTextInput::RenderType renderType)
+{
+    Q_D(QQuickTextInput);
+    if (d->renderType == renderType)
+        return;
+
+    d->renderType = renderType;
+    emit renderTypeChanged();
+
+    if (isComponentComplete())
+        d->updateLayout();
+}
+
 /*!
     \qmlproperty int QtQuick2::TextInput::length
 
 /*!
     \qmlproperty int QtQuick2::TextInput::length
 
@@ -644,7 +684,7 @@ void QQuickTextInput::setMaxLength(int ml)
     forward keys to it and you desire it to look active when this happens
     (but without actually giving it active focus).
 
     forward keys to it and you desire it to look active when this happens
     (but without actually giving it active focus).
 
-    It should not be set directly on the element, like in the below QML,
+    It should not be set directly on the item, like in the below QML,
     as the specified value will be overridden an lost on focus changes.
 
     \code
     as the specified value will be overridden an lost on focus changes.
 
     \code
@@ -669,9 +709,13 @@ void QQuickTextInput::setCursorVisible(bool on)
     if (d->cursorVisible == on)
         return;
     d->cursorVisible = on;
     if (d->cursorVisible == on)
         return;
     d->cursorVisible = on;
-    d->setCursorBlinkPeriod(on ? qApp->styleHints()->cursorFlashTime() : 0);
-    d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
-    update();
+    if (on && isComponentComplete())
+        QQuickTextUtil::createCursor(d);
+    if (!d->cursorItem) {
+        d->setCursorBlinkPeriod(on ? qApp->styleHints()->cursorFlashTime() : 0);
+        d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
+        update();
+    }
     emit cursorVisibleChanged(d->cursorVisible);
 }
 
     emit cursorVisibleChanged(d->cursorVisible);
 }
 
@@ -747,7 +791,7 @@ int QQuickTextInput::selectionEnd() const
     return d->lastSelectionEnd;
 }
 /*!
     return d->lastSelectionEnd;
 }
 /*!
-    \qmlmethod void QtQuick2::TextInput::select(int start, int end)
+    \qmlmethod QtQuick2::TextInput::select(int start, int end)
 
     Causes the text from \a start to \a end to be selected.
 
 
     Causes the text from \a start to \a end to be selected.
 
@@ -836,11 +880,13 @@ void QQuickTextInput::setAutoScroll(bool b)
 #ifndef QT_NO_VALIDATOR
 
 /*!
 #ifndef QT_NO_VALIDATOR
 
 /*!
-    \qmlclass IntValidator QIntValidator
+    \qmltype IntValidator
+    \instantiates QIntValidator
     \inqmlmodule QtQuick 2
     \inqmlmodule QtQuick 2
-    \ingroup qml-basic-visual-elements
+    \ingroup qtquick-text-utility
+    \brief Defines a validator for integer values
 
 
-    This element provides a validator for integer values.
+    The IntValidator type provides a validator for integer values.
 
     If no \l locale is set IntValidator uses the \l {QLocale::setDefault()}{default locale} to
     interpret the number and will accept locale specific digits, group separators, and positive
 
     If no \l locale is set IntValidator uses the \l {QLocale::setDefault()}{default locale} to
     interpret the number and will accept locale specific digits, group separators, and positive
@@ -898,11 +944,13 @@ void QQuickIntValidator::resetLocaleName()
 */
 
 /*!
 */
 
 /*!
-    \qmlclass DoubleValidator QDoubleValidator
+    \qmltype DoubleValidator
+    \instantiates QDoubleValidator
     \inqmlmodule QtQuick 2
     \inqmlmodule QtQuick 2
-    \ingroup qml-basic-visual-elements
+    \ingroup qtquick-text-utility
+    \brief Defines a validator for non-integer numbers
 
 
-    This element provides a validator for non-integer numbers.
+    The DoubleValidator type provides a validator for non-integer numbers.
 
     Input is accepted if it contains a double that is within the valid range
     and is in the  correct format.
 
     Input is accepted if it contains a double that is within the valid range
     and is in the  correct format.
@@ -990,11 +1038,13 @@ void QQuickDoubleValidator::resetLocaleName()
 */
 
 /*!
 */
 
 /*!
-    \qmlclass RegExpValidator QRegExpValidator
+    \qmltype RegExpValidator
+    \instantiates QRegExpValidator
     \inqmlmodule QtQuick 2
     \inqmlmodule QtQuick 2
-    \ingroup qml-basic-visual-elements
+    \ingroup qtquick-text-utility
+    \brief Provides a string validator
 
 
-    This element provides a validator, which counts as valid any string which
+    The RegExpValidator type provides a validator, which counts as valid any string which
     matches a specified regular expression.
 */
 /*!
     matches a specified regular expression.
 */
 /*!
@@ -1043,14 +1093,32 @@ void QQuickTextInput::setValidator(QValidator* v)
     if (d->m_validator == v)
         return;
 
     if (d->m_validator == v)
         return;
 
+    if (d->m_validator) {
+        qmlobject_disconnect(
+                d->m_validator, QValidator, SIGNAL(changed()),
+                this, QQuickTextInput, SLOT(q_validatorChanged()));
+    }
+
     d->m_validator = v;
 
     d->m_validator = v;
 
+    if (d->m_validator) {
+        qmlobject_connect(
+                d->m_validator, QValidator, SIGNAL(changed()),
+                this, QQuickTextInput, SLOT(q_validatorChanged()));
+    }
+
     if (isComponentComplete())
         d->checkIsValid();
 
     emit validatorChanged();
 }
 
     if (isComponentComplete())
         d->checkIsValid();
 
     emit validatorChanged();
 }
 
+void QQuickTextInput::q_validatorChanged()
+{
+    Q_D(QQuickTextInput);
+    d->checkIsValid();
+}
+
 #endif // QT_NO_VALIDATOR
 
 void QQuickTextInputPrivate::checkIsValid()
 #endif // QT_NO_VALIDATOR
 
 void QQuickTextInputPrivate::checkIsValid()
@@ -1247,65 +1315,14 @@ QQmlComponent* QQuickTextInput::cursorDelegate() const
 void QQuickTextInput::setCursorDelegate(QQmlComponent* c)
 {
     Q_D(QQuickTextInput);
 void QQuickTextInput::setCursorDelegate(QQmlComponent* c)
 {
     Q_D(QQuickTextInput);
-    if (d->cursorComponent == c)
-        return;
-
-    d->cursorComponent = c;
-    if (!c) {
-        //note that the components are owned by something else
-        delete d->cursorItem;
-        d->cursorItem = 0;
-    } else {
-        d->startCreatingCursor();
-    }
-
-    emit cursorDelegateChanged();
-}
-
-void QQuickTextInputPrivate::startCreatingCursor()
-{
-    Q_Q(QQuickTextInput);
-    if (cursorComponent->isReady()) {
-        q->createCursor();
-    } else if (cursorComponent->isLoading()) {
-        q->connect(cursorComponent, SIGNAL(statusChanged(int)),
-                q, SLOT(createCursor()));
-    } else { // isError
-        qmlInfo(q, cursorComponent->errors()) << QQuickTextInput::tr("Could not load cursor delegate");
-    }
+    QQuickTextUtil::setCursorDelegate(d, c);
 }
 
 void QQuickTextInput::createCursor()
 {
     Q_D(QQuickTextInput);
 }
 
 void QQuickTextInput::createCursor()
 {
     Q_D(QQuickTextInput);
-    if (!isComponentComplete())
-        return;
-
-    if (d->cursorComponent->isError()) {
-        qmlInfo(this, d->cursorComponent->errors()) << tr("Could not load cursor delegate");
-        return;
-    }
-
-    if (!d->cursorComponent->isReady())
-        return;
-
-    if (d->cursorItem)
-        delete d->cursorItem;
-    QQmlContext *creationContext = d->cursorComponent->creationContext();
-    QObject *object = d->cursorComponent->create(creationContext ? creationContext : qmlContext(this));
-    d->cursorItem = qobject_cast<QQuickItem*>(object);
-    if (!d->cursorItem) {
-        delete object;
-        qmlInfo(this, d->cursorComponent->errors()) << tr("Could not instantiate cursor delegate");
-        return;
-    }
-
-    QRectF r = cursorRectangle();
-
-    QQml_setParent_noEvent(d->cursorItem, this);
-    d->cursorItem->setParentItem(this);
-    d->cursorItem->setPos(r.topLeft());
-    d->cursorItem->setHeight(r.height());
+    d->cursorPending = true;
+    QQuickTextUtil::createCursor(d);
 }
 
 /*!
 }
 
 /*!
@@ -1492,7 +1509,7 @@ void QQuickTextInput::mousePressEvent(QMouseEvent *event)
         forceActiveFocus();
         // re-open input panel on press if already focused
         if (hasActiveFocus() && hadActiveFocus && !d->m_readOnly)
         forceActiveFocus();
         // re-open input panel on press if already focused
         if (hasActiveFocus() && hadActiveFocus && !d->m_readOnly)
-            openSoftwareInputPanel();
+            qGuiApp->inputMethod()->show();
     }
 
     event->setAccepted(true);
     }
 
     event->setAccepted(true);
@@ -1629,7 +1646,7 @@ void QQuickTextInput::geometryChanged(const QRectF &newGeometry,
 {
     Q_D(QQuickTextInput);
     if (!d->inLayout) {
 {
     Q_D(QQuickTextInput);
     if (!d->inLayout) {
-        if (newGeometry.width() != oldGeometry.width() && d->wrapMode != NoWrap)
+        if (newGeometry.width() != oldGeometry.width())
             d->updateLayout();
         updateCursorRectangle();
     }
             d->updateLayout();
         updateCursorRectangle();
     }
@@ -1692,18 +1709,8 @@ void QQuickTextInputPrivate::updateVerticalScroll()
 
     if (!autoScroll || heightUsed <=  height) {
         // text fits in br; use vscroll for alignment
 
     if (!autoScroll || heightUsed <=  height) {
         // text fits in br; use vscroll for alignment
-        switch (vAlign & ~(Qt::AlignAbsolute|Qt::AlignHorizontal_Mask)) {
-        case Qt::AlignBottom:
-            vscroll = heightUsed - height;
-            break;
-        case Qt::AlignVCenter:
-            vscroll = (heightUsed - height) / 2;
-            break;
-        default:
-            // Top
-            vscroll = 0;
-            break;
-        }
+        vscroll = -QQuickTextUtil::alignedY(
+                    heightUsed, height, vAlign & ~(Qt::AlignAbsolute|Qt::AlignHorizontal_Mask));
     } else {
         QTextLine currentLine = m_textLayout.lineForTextPosition(m_cursor + preeditLength);
         QRectF r = currentLine.isValid() ? currentLine.rect() : QRectF();
     } else {
         QTextLine currentLine = m_textLayout.lineForTextPosition(m_cursor + preeditLength);
         QRectF r = currentLine.isValid() ? currentLine.rect() : QRectF();
@@ -1765,13 +1772,14 @@ QSGNode *QQuickTextInput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData
         if (cursorNode != 0 && !isReadOnly()) {
             cursorNode->setRect(cursorRectangle());
 
         if (cursorNode != 0 && !isReadOnly()) {
             cursorNode->setRect(cursorRectangle());
 
-            if (!d->cursorVisible || (!d->m_blinkStatus && d->m_blinkPeriod > 0)) {
+            if (!d->cursorVisible || d->cursorItem || (!d->m_blinkStatus && d->m_blinkPeriod > 0)) {
                 d->hideCursor();
             } else {
                 d->showCursor();
             }
         }
     } else {
                 d->hideCursor();
             } else {
                 d->showCursor();
             }
         }
     } else {
+        node->setUseNativeRenderer(d->renderType == QQuickTextInput::NativeRendering);
         node->deleteContent();
         node->setMatrix(QMatrix4x4());
 
         node->deleteContent();
         node->setMatrix(QMatrix4x4());
 
@@ -1845,7 +1853,7 @@ QVariant QQuickTextInput::inputMethodQuery(Qt::InputMethodQuery property) const
 }
 
 /*!
 }
 
 /*!
-    \qmlmethod void QtQuick2::TextInput::deselect()
+    \qmlmethod QtQuick2::TextInput::deselect()
 
     Removes active text selection.
 */
 
     Removes active text selection.
 */
@@ -1856,7 +1864,7 @@ void QQuickTextInput::deselect()
 }
 
 /*!
 }
 
 /*!
-    \qmlmethod void QtQuick2::TextInput::selectAll()
+    \qmlmethod QtQuick2::TextInput::selectAll()
 
     Causes all text to be selected.
 */
 
     Causes all text to be selected.
 */
@@ -1867,7 +1875,7 @@ void QQuickTextInput::selectAll()
 }
 
 /*!
 }
 
 /*!
-    \qmlmethod void QtQuick2::TextInput::isRightToLeft(int start, int end)
+    \qmlmethod QtQuick2::TextInput::isRightToLeft(int start, int end)
 
     Returns true if the natural reading direction of the editor text
     found between positions \a start and \a end is right to left.
 
     Returns true if the natural reading direction of the editor text
     found between positions \a start and \a end is right to left.
@@ -1891,8 +1899,10 @@ bool QQuickTextInput::isRightToLeft(int start, int end)
 void QQuickTextInput::cut()
 {
     Q_D(QQuickTextInput);
 void QQuickTextInput::cut()
 {
     Q_D(QQuickTextInput);
-    d->copy();
-    d->del();
+    if (!d->m_readOnly) {
+        d->copy();
+        d->del();
+    }
 }
 
 /*!
 }
 
 /*!
@@ -1920,6 +1930,8 @@ void QQuickTextInput::paste()
 #endif // QT_NO_CLIPBOARD
 
 /*!
 #endif // QT_NO_CLIPBOARD
 
 /*!
+    \qmlmethod QtQuick2::TextInput::undo()
+
     Undoes the last operation if undo is \l {canUndo}{available}. Deselects any
     current selection, and updates the selection start to the current cursor
     position.
     Undoes the last operation if undo is \l {canUndo}{available}. Deselects any
     current selection, and updates the selection start to the current cursor
     position.
@@ -1935,6 +1947,8 @@ void QQuickTextInput::undo()
 }
 
 /*!
 }
 
 /*!
+    \qmlmethod QtQuick2::TextInput::redo()
+
     Redoes the last operation if redo is \l {canRedo}{available}.
 */
 
     Redoes the last operation if redo is \l {canRedo}{available}.
 */
 
@@ -1948,7 +1962,7 @@ void QQuickTextInput::redo()
 }
 
 /*!
 }
 
 /*!
-    \qmlmethod void QtQuick2::TextInput::insert(int position, string text)
+    \qmlmethod QtQuick2::TextInput::insert(int position, string text)
 
     Inserts \a text into the TextInput at position.
 */
 
     Inserts \a text into the TextInput at position.
 */
@@ -2100,7 +2114,7 @@ void QQuickTextInput::remove(int start, int end)
 
 
 /*!
 
 
 /*!
-    \qmlmethod void QtQuick2::TextInput::selectWord()
+    \qmlmethod QtQuick2::TextInput::selectWord()
 
     Causes the word closest to the current cursor position to be selected.
 */
 
     Causes the word closest to the current cursor position to be selected.
 */
@@ -2111,20 +2125,6 @@ void QQuickTextInput::selectWord()
 }
 
 /*!
 }
 
 /*!
-    \qmlproperty bool QtQuick2::TextInput::smooth
-
-    This property holds whether the text is smoothly scaled or transformed.
-
-    Smooth filtering gives better visual quality, but is slower.  If
-    the item is displayed at its natural size, this property has no visual or
-    performance effect.
-
-    \note Generally scaling artifacts are only visible if the item is stationary on
-    the screen.  A common pattern when animating an item is to disable smooth
-    filtering at the beginning of the animation and reenable it at the conclusion.
-*/
-
-/*!
    \qmlproperty string QtQuick2::TextInput::passwordCharacter
 
    This is the character displayed when echoMode is set to Password or
    \qmlproperty string QtQuick2::TextInput::passwordCharacter
 
    This is the character displayed when echoMode is set to Password or
@@ -2193,7 +2193,7 @@ void QQuickTextInput::setSelectByMouse(bool on)
 }
 
 /*!
 }
 
 /*!
-    \qmlproperty enum QtQuick2::TextInput::mouseSelectionMode
+    \qmlproperty enumeration QtQuick2::TextInput::mouseSelectionMode
 
     Specifies how text should be selected using a mouse.
 
 
     Specifies how text should be selected using a mouse.
 
@@ -2320,7 +2320,7 @@ void QQuickTextInput::moveCursorSelection(int position)
 }
 
 /*!
 }
 
 /*!
-    \qmlmethod void QtQuick2::TextInput::moveCursorSelection(int position, SelectionMode mode = TextInput.SelectCharacters)
+    \qmlmethod QtQuick2::TextInput::moveCursorSelection(int position, SelectionMode mode = TextInput.SelectCharacters)
 
     Moves the cursor to \a position and updates the selection according to the optional \a mode
     parameter.  (To only move the cursor, set the \l cursorPosition property.)
 
     Moves the cursor to \a position and updates the selection according to the optional \a mode
     parameter.  (To only move the cursor, set the \l cursorPosition property.)
@@ -2378,8 +2378,8 @@ void QQuickTextInput::moveCursorSelection(int pos, SelectionMode mode)
             finder.setPosition(anchor);
 
             const QTextBoundaryFinder::BoundaryReasons reasons = finder.boundaryReasons();
             finder.setPosition(anchor);
 
             const QTextBoundaryFinder::BoundaryReasons reasons = finder.boundaryReasons();
-            if (anchor < text.length() && (!(reasons & QTextBoundaryFinder::StartWord)
-                    || ((reasons & QTextBoundaryFinder::EndWord) && anchor > cursor))) {
+            if (anchor < text.length() && (reasons == QTextBoundaryFinder::NotAtBoundary
+                                           || (reasons & QTextBoundaryFinder::EndOfItem))) {
                 finder.toPreviousBoundary();
             }
             anchor = finder.position() != -1 ? finder.position() : 0;
                 finder.toPreviousBoundary();
             }
             anchor = finder.position() != -1 ? finder.position() : 0;
@@ -2396,11 +2396,10 @@ void QQuickTextInput::moveCursorSelection(int pos, SelectionMode mode)
             finder.setPosition(anchor);
 
             const QTextBoundaryFinder::BoundaryReasons reasons = finder.boundaryReasons();
             finder.setPosition(anchor);
 
             const QTextBoundaryFinder::BoundaryReasons reasons = finder.boundaryReasons();
-            if (anchor > 0 && (!(reasons & QTextBoundaryFinder::EndWord)
-                    || ((reasons & QTextBoundaryFinder::StartWord) && anchor < cursor))) {
+            if (anchor > 0 && (reasons == QTextBoundaryFinder::NotAtBoundary
+                               || (reasons & QTextBoundaryFinder::StartOfItem))) {
                 finder.toNextBoundary();
             }
                 finder.toNextBoundary();
             }
-
             anchor = finder.position() != -1 ? finder.position() : text.length();
 
             finder.setPosition(pos);
             anchor = finder.position() != -1 ? finder.position() : text.length();
 
             finder.setPosition(pos);
@@ -2413,97 +2412,11 @@ void QQuickTextInput::moveCursorSelection(int pos, SelectionMode mode)
     }
 }
 
     }
 }
 
-/*!
-    \qmlmethod void QtQuick2::TextInput::openSoftwareInputPanel()
-
-    Opens software input panels like virtual keyboards for typing, useful for
-    customizing when you want the input keyboard to be shown and hidden in
-    your application.
-
-    By default the opening of input panels follows the platform style. Input panels are
-    always closed if no editor has active focus.
-
-    You can disable the automatic behavior by setting the property \c activeFocusOnPress to false
-    and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement
-    the behavior you want.
-
-    Only relevant on platforms, which provide virtual keyboards.
-
-    \qml
-        import QtQuick 2.0
-        TextInput {
-            id: textInput
-            text: "Hello world!"
-            activeFocusOnPress: false
-            MouseArea {
-                anchors.fill: parent
-                onClicked: {
-                    if (!textInput.activeFocus) {
-                        textInput.forceActiveFocus()
-                        textInput.openSoftwareInputPanel();
-                    } else {
-                        textInput.focus = false;
-                    }
-                }
-                onPressAndHold: textInput.closeSoftwareInputPanel();
-            }
-        }
-    \endqml
-*/
-void QQuickTextInput::openSoftwareInputPanel()
-{
-    if (qGuiApp)
-        qGuiApp->inputMethod()->show();
-}
-
-/*!
-    \qmlmethod void QtQuick2::TextInput::closeSoftwareInputPanel()
-
-    Closes a software input panel like a virtual keyboard shown on the screen, useful
-    for customizing when you want the input keyboard to be shown and hidden in
-    your application.
-
-    By default the opening of input panels follows the platform style. Input panels are
-    always closed if no editor has active focus.
-
-    You can disable the automatic behavior by setting the property \c activeFocusOnPress to false
-    and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement
-    the behavior you want.
-
-    Only relevant on platforms, which provide virtual keyboards.
-
-    \qml
-        import QtQuick 2.0
-        TextInput {
-            id: textInput
-            text: "Hello world!"
-            activeFocusOnPress: false
-            MouseArea {
-                anchors.fill: parent
-                onClicked: {
-                    if (!textInput.activeFocus) {
-                        textInput.forceActiveFocus();
-                        textInput.openSoftwareInputPanel();
-                    } else {
-                        textInput.focus = false;
-                    }
-                }
-                onPressAndHold: textInput.closeSoftwareInputPanel();
-            }
-        }
-    \endqml
-*/
-void QQuickTextInput::closeSoftwareInputPanel()
-{
-    if (qGuiApp)
-        qGuiApp->inputMethod()->hide();
-}
-
 void QQuickTextInput::focusInEvent(QFocusEvent *event)
 {
     Q_D(const QQuickTextInput);
     if (d->focusOnPress && !d->m_readOnly)
 void QQuickTextInput::focusInEvent(QFocusEvent *event)
 {
     Q_D(const QQuickTextInput);
     if (d->focusOnPress && !d->m_readOnly)
-        openSoftwareInputPanel();
+        qGuiApp->inputMethod()->show();
     QQuickImplicitSizeItem::focusInEvent(event);
 }
 
     QQuickImplicitSizeItem::focusInEvent(event);
 }
 
@@ -2512,7 +2425,7 @@ void QQuickTextInput::itemChange(ItemChange change, const ItemChangeData &value)
     Q_D(QQuickTextInput);
     if (change == ItemActiveFocusHasChanged) {
         bool hasFocus = value.boolValue;
     Q_D(QQuickTextInput);
     if (change == ItemActiveFocusHasChanged) {
         bool hasFocus = value.boolValue;
-        setCursorVisible(hasFocus); // ### refactor:  && d->canvas && d->canvas->hasFocus()
+        setCursorVisible(hasFocus);
         if (!hasFocus && (d->m_passwordEchoEditing || d->m_passwordEchoTimer.isActive())) {
             d->updatePasswordEchoEditing(false);//QQuickTextInputPrivate sets it on key events, but doesn't deal with focus events
         }
         if (!hasFocus && (d->m_passwordEchoEditing || d->m_passwordEchoTimer.isActive())) {
             d->updatePasswordEchoEditing(false);//QQuickTextInputPrivate sets it on key events, but doesn't deal with focus events
         }
@@ -2552,8 +2465,13 @@ bool QQuickTextInput::isInputMethodComposing() const
 void QQuickTextInputPrivate::init()
 {
     Q_Q(QQuickTextInput);
 void QQuickTextInputPrivate::init()
 {
     Q_Q(QQuickTextInput);
-    q->setSmooth(smooth);
-    q->setAcceptedMouseButtons(Qt::LeftButton);
+#ifndef QT_NO_CLIPBOARD
+    if (QGuiApplication::clipboard()->supportsSelection())
+        q->setAcceptedMouseButtons(Qt::LeftButton | Qt::MiddleButton);
+    else
+#endif
+        q->setAcceptedMouseButtons(Qt::LeftButton);
+
     q->setFlag(QQuickItem::ItemAcceptsInputMethod);
     q->setFlag(QQuickItem::ItemHasContents);
 #ifndef QT_NO_CLIPBOARD
     q->setFlag(QQuickItem::ItemAcceptsInputMethod);
     q->setFlag(QQuickItem::ItemHasContents);
 #ifndef QT_NO_CLIPBOARD
@@ -2567,7 +2485,7 @@ void QQuickTextInputPrivate::init()
 
     if (!qmlDisableDistanceField()) {
         QTextOption option = m_textLayout.textOption();
 
     if (!qmlDisableDistanceField()) {
         QTextOption option = m_textLayout.textOption();
-        option.setUseDesignMetrics(true);
+        option.setUseDesignMetrics(renderType != QQuickTextInput::NativeRendering);
         m_textLayout.setTextOption(option);
     }
 }
         m_textLayout.setTextOption(option);
     }
 }
@@ -2632,18 +2550,8 @@ QRectF QQuickTextInput::boundingRect() const
     int cursorWidth = d->cursorItem ? 0 : 1;
 
     qreal hscroll = d->hscroll;
     int cursorWidth = d->cursorItem ? 0 : 1;
 
     qreal hscroll = d->hscroll;
-    if (!d->autoScroll || d->contentSize.width() < width()) {
-        switch (effectiveHAlign()) {
-        case AlignLeft:
-            break;
-        case AlignRight:
-            hscroll += d->contentSize.width() - width();
-            break;
-        case AlignHCenter:
-            hscroll += (d->contentSize.width() - width()) / 2;
-            break;
-        }
-    }
+    if (!d->autoScroll || d->contentSize.width() < width())
+        hscroll -= QQuickTextUtil::alignedX(d->contentSize.width(), width(), effectiveHAlign());
 
     // Could include font max left/right bearings to either side of rectangle.
     QRectF r(-hscroll, -d->vscroll, d->contentSize.width(), d->contentSize.height());
 
     // Could include font max left/right bearings to either side of rectangle.
     QRectF r(-hscroll, -d->vscroll, d->contentSize.width(), d->contentSize.height());
@@ -2922,7 +2830,7 @@ void QQuickTextInputPrivate::cancelPreedit()
 void QQuickTextInputPrivate::backspace()
 {
     int priorState = m_undoState;
 void QQuickTextInputPrivate::backspace()
 {
     int priorState = m_undoState;
-    if (hasSelectedText()) {
+    if (separateSelection()) {
         removeSelectedText();
     } else if (m_cursor) {
             --m_cursor;
         removeSelectedText();
     } else if (m_cursor) {
             --m_cursor;
@@ -2955,7 +2863,7 @@ void QQuickTextInputPrivate::backspace()
 void QQuickTextInputPrivate::del()
 {
     int priorState = m_undoState;
 void QQuickTextInputPrivate::del()
 {
     int priorState = m_undoState;
-    if (hasSelectedText()) {
+    if (separateSelection()) {
         removeSelectedText();
     } else {
         int n = m_textLayout.nextCursorPosition(m_cursor) - m_cursor;
         removeSelectedText();
     } else {
         int n = m_textLayout.nextCursorPosition(m_cursor) - m_cursor;
@@ -2975,7 +2883,8 @@ void QQuickTextInputPrivate::del()
 void QQuickTextInputPrivate::insert(const QString &newText)
 {
     int priorState = m_undoState;
 void QQuickTextInputPrivate::insert(const QString &newText)
 {
     int priorState = m_undoState;
-    removeSelectedText();
+    if (separateSelection())
+        removeSelectedText();
     internalInsert(newText);
     finishChange(priorState);
 }
     internalInsert(newText);
     finishChange(priorState);
 }
@@ -2988,6 +2897,7 @@ void QQuickTextInputPrivate::insert(const QString &newText)
 void QQuickTextInputPrivate::clear()
 {
     int priorState = m_undoState;
 void QQuickTextInputPrivate::clear()
 {
     int priorState = m_undoState;
+    separateSelection();
     m_selstart = 0;
     m_selend = m_text.length();
     removeSelectedText();
     m_selstart = 0;
     m_selend = m_text.length();
     removeSelectedText();
@@ -3138,6 +3048,7 @@ void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event)
     if (isGettingInput) {
         // If any text is being input, remove selected text.
         priorState = m_undoState;
     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;
         if (m_echoMode == QQuickTextInput::PasswordEchoOnEdit && !m_passwordEchoEditing) {
             updatePasswordEchoEditing(true);
             m_selstart = 0;
@@ -3189,10 +3100,9 @@ void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event)
     m_textLayout.setPreeditArea(m_cursor, event->preeditString());
 #endif //QT_NO_IM
     const int oldPreeditCursor = m_preeditCursor;
     m_textLayout.setPreeditArea(m_cursor, event->preeditString());
 #endif //QT_NO_IM
     const int oldPreeditCursor = m_preeditCursor;
-    const bool oldCursorVisible = cursorVisible;
     m_preeditCursor = event->preeditString().length();
     hasImState = !event->preeditString().isEmpty();
     m_preeditCursor = event->preeditString().length();
     hasImState = !event->preeditString().isEmpty();
-    cursorVisible = true;
+    bool cursorVisible = true;
     QList<QTextLayout::FormatRange> formats;
     for (int i = 0; i < event->attributes().size(); ++i) {
         const QInputMethodEvent::Attribute &a = event->attributes().at(i);
     QList<QTextLayout::FormatRange> formats;
     for (int i = 0; i < event->attributes().size(); ++i) {
         const QInputMethodEvent::Attribute &a = event->attributes().at(i);
@@ -3215,17 +3125,16 @@ void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event)
     m_textLayout.setAdditionalFormats(formats);
 
     updateDisplayText(/*force*/ true);
     m_textLayout.setAdditionalFormats(formats);
 
     updateDisplayText(/*force*/ true);
-    if (cursorPositionChanged) {
-        emitCursorPositionChanged();
-    } else if (m_preeditCursor != oldPreeditCursor || isGettingInput) {
+    if ((cursorPositionChanged && !emitCursorPositionChanged())
+            || m_preeditCursor != oldPreeditCursor
+            || isGettingInput) {
         q->updateCursorRectangle();
     }
 
     if (isGettingInput)
         finishChange(priorState);
 
         q->updateCursorRectangle();
     }
 
     if (isGettingInput)
         finishChange(priorState);
 
-    if (cursorVisible != oldCursorVisible)
-        emit q->cursorVisibleChanged(cursorVisible);
+    q->setCursorVisible(cursorVisible);
 
     if (selectionChange) {
         emit q->selectionChanged();
 
     if (selectionChange) {
         emit q->selectionChanged();
@@ -3412,8 +3321,7 @@ void QQuickTextInputPrivate::internalInsert(const QString &s)
         if (delay > 0)
             m_passwordEchoTimer.start(delay, q);
     }
         if (delay > 0)
             m_passwordEchoTimer.start(delay, q);
     }
-    if (hasSelectedText())
-        addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend));
+    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) {
     if (m_maskData) {
         QString ms = maskString(m_cursor, s);
         for (int i = 0; i < (int) ms.length(); ++i) {
@@ -3450,8 +3358,7 @@ void QQuickTextInputPrivate::internalDelete(bool wasBackspace)
 {
     if (m_cursor < (int) m_text.length()) {
         cancelPasswordEchoTimer();
 {
     if (m_cursor < (int) m_text.length()) {
         cancelPasswordEchoTimer();
-        if (hasSelectedText())
-            addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend));
+        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) {
         addCommand(Command((CommandType)((m_maskData ? 2 : 0) + (wasBackspace ? Remove : Delete)),
                    m_cursor, m_text.at(m_cursor), -1, -1));
         if (m_maskData) {
@@ -3477,9 +3384,7 @@ void QQuickTextInputPrivate::removeSelectedText()
 {
     if (m_selstart < m_selend && m_selend <= (int) m_text.length()) {
         cancelPasswordEchoTimer();
 {
     if (m_selstart < m_selend && m_selend <= (int) m_text.length()) {
         cancelPasswordEchoTimer();
-        separate();
         int i ;
         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
         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
@@ -3508,6 +3413,25 @@ void QQuickTextInputPrivate::removeSelectedText()
 /*!
     \internal
 
 /*!
     \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.
 */
     Parses the input mask specified by \a maskFields to generate
     the mask data used to handle input masks.
 */
@@ -3906,11 +3830,14 @@ void QQuickTextInputPrivate::internalUndo(int until)
         }
         if (until < 0 && m_undoState) {
             Command& next = m_history[m_undoState-1];
         }
         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))
+            if (next.type != cmd.type
+                    && next.type < RemoveSelection
+                    && (cmd.type < RemoveSelection || next.type == Separator)) {
                 break;
                 break;
+            }
         }
     }
         }
     }
+    separate();
     m_textDirty = true;
 }
 
     m_textDirty = true;
 }
 
@@ -3948,9 +3875,12 @@ void QQuickTextInputPrivate::internalRedo()
         }
         if (m_undoState < (int)m_history.size()) {
             Command& next = m_history[m_undoState];
         }
         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))
+            if (next.type != cmd.type
+                    && cmd.type < RemoveSelection
+                    && next.type != Separator
+                    && (next.type < RemoveSelection || cmd.type == Separator)) {
                 break;
                 break;
+            }
         }
     }
     m_textDirty = true;
         }
     }
     m_textDirty = true;
@@ -4039,22 +3969,19 @@ void QQuickTextInput::timerEvent(QTimerEvent *event)
     } else if (event->timerId() == d->m_passwordEchoTimer.timerId()) {
         d->m_passwordEchoTimer.stop();
         d->updateDisplayText();
     } else if (event->timerId() == d->m_passwordEchoTimer.timerId()) {
         d->m_passwordEchoTimer.stop();
         d->updateDisplayText();
+        updateCursorRectangle();
     }
 }
 
 void QQuickTextInputPrivate::processKeyEvent(QKeyEvent* event)
 {
     Q_Q(QQuickTextInput);
     }
 }
 
 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 (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();
+        event->ignore();
         return;
     }
 
         return;
     }
 
@@ -4104,11 +4031,8 @@ void QQuickTextInputPrivate::processKeyEvent(QKeyEvent* event)
         }
     }
     else if (event == QKeySequence::DeleteEndOfLine) {
         }
     }
     else if (event == QKeySequence::DeleteEndOfLine) {
-        if (!m_readOnly) {
-            setSelection(m_cursor, end());
-            copy();
-            del();
-        }
+        if (!m_readOnly)
+            deleteEndOfLine();
     }
 #endif //QT_NO_CLIPBOARD
     else if (event == QKeySequence::MoveToStartOfLine || event == QKeySequence::MoveToStartOfBlock) {
     }
 #endif //QT_NO_CLIPBOARD
     else if (event == QKeySequence::MoveToStartOfLine || event == QKeySequence::MoveToStartOfBlock) {
@@ -4173,16 +4097,12 @@ void QQuickTextInputPrivate::processKeyEvent(QKeyEvent* event)
             del();
     }
     else if (event == QKeySequence::DeleteEndOfWord) {
             del();
     }
     else if (event == QKeySequence::DeleteEndOfWord) {
-        if (!m_readOnly) {
-            cursorWordForward(true);
-            del();
-        }
+        if (!m_readOnly)
+            deleteEndOfWord();
     }
     else if (event == QKeySequence::DeleteStartOfWord) {
     }
     else if (event == QKeySequence::DeleteStartOfWord) {
-        if (!m_readOnly) {
-            cursorWordBackward(true);
-            del();
-        }
+        if (!m_readOnly)
+            deleteStartOfWord();
     }
 #endif // QT_NO_SHORTCUT
     else {
     }
 #endif // QT_NO_SHORTCUT
     else {
@@ -4190,10 +4110,8 @@ void QQuickTextInputPrivate::processKeyEvent(QKeyEvent* event)
         if (event->modifiers() & Qt::ControlModifier) {
             switch (event->key()) {
             case Qt::Key_Backspace:
         if (event->modifiers() & Qt::ControlModifier) {
             switch (event->key()) {
             case Qt::Key_Backspace:
-                if (!m_readOnly) {
-                    cursorWordBackward(true);
-                    del();
-                }
+                if (!m_readOnly)
+                    deleteStartOfWord();
                 break;
             default:
                 if (!handled)
                 break;
             default:
                 if (!handled)
@@ -4233,6 +4151,58 @@ void QQuickTextInputPrivate::processKeyEvent(QKeyEvent* event)
         event->accept();
 }
 
         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);
+}
 
 QT_END_NAMESPACE
 
 
 QT_END_NAMESPACE