Merge master <-> api_changes
[profile/ivi/qtdeclarative.git] / src / quick / items / qquicktextedit.cpp
index 91b0b0d..a9bff73 100644 (file)
@@ -1,10 +1,9 @@
 /****************************************************************************
 **
-** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
-** Contact: Nokia Corporation (qt-info@nokia.com)
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
 **
-** This file is part of the QtDeclarative module of the Qt Toolkit.
+** This file is part of the QtQml module of the Qt Toolkit.
 **
 ** $QT_BEGIN_LICENSE:LGPL$
 ** GNU Lesser General Public License Usage
@@ -35,6 +34,7 @@
 **
 **
 **
+**
 ** $QT_END_LICENSE$
 **
 ****************************************************************************/
 #include "qquicktextnode_p.h"
 #include <QtQuick/qsgsimplerectnode.h>
 
-#include <QtDeclarative/qdeclarativeinfo.h>
+#include <QtQml/qqmlinfo.h>
 #include <QtGui/qguiapplication.h>
 #include <QtGui/qevent.h>
 #include <QtGui/qpainter.h>
 #include <QtGui/qtextobject.h>
 #include <QtCore/qmath.h>
 
-#include <private/qdeclarativeglobal_p.h>
+#include <private/qqmlglobal_p.h>
+#include <private/qqmlproperty_p.h>
 #include <private/qtextengine_p.h>
-#include <QtQuick/private/qsgtexture_p.h>
 #include <private/qsgadaptationlayer_p.h>
 
 QT_BEGIN_NAMESPACE
 
-DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
-DEFINE_BOOL_CONFIG_OPTION(qmlEnableImageCache, QML_ENABLE_TEXT_IMAGE_CACHE)
-
 /*!
     \qmlclass TextEdit QQuickTextEdit
     \inqmlmodule QtQuick 2
@@ -126,13 +123,17 @@ QQuickTextEdit::QQuickTextEdit(QQuickItem *parent)
 QString QQuickTextEdit::text() const
 {
     Q_D(const QQuickTextEdit);
-
+    if (!d->textCached) {
+        QQuickTextEditPrivate *d = const_cast<QQuickTextEditPrivate *>(d_func());
 #ifndef QT_NO_TEXTHTMLPARSER
-    if (d->richText)
-        return d->document->toHtml();
-    else
+        if (d->richText)
+            d->text = d->control->toHtml();
+        else
 #endif
-        return d->document->toPlainText();
+            d->text = d->control->toPlainText();
+        d->textCached = true;
+    }
+    return d->text;
 }
 
 /*!
@@ -262,11 +263,9 @@ void QQuickTextEdit::setText(const QString &text)
 #else
         d->control->setPlainText(text);
 #endif
-        d->useImageFallback = qmlEnableImageCache();
     } else {
         d->control->setPlainText(text);
     }
-    q_textChanged();
 }
 
 /*!
@@ -280,7 +279,7 @@ void QQuickTextEdit::setText(const QString &text)
     \o TextEdit.RichText
     \endlist
 
-    The default is TextEdit.AutoText.  If the text format is TextEdit.AutoText the text edit
+    The default is TextEdit.PlainText.  If the text format is TextEdit.AutoText the text edit
     will automatically determine whether the text should be treated as
     rich text.  This determination is made using Qt::mightBeRichText().
 
@@ -319,21 +318,20 @@ void QQuickTextEdit::setTextFormat(TextFormat format)
     Q_D(QQuickTextEdit);
     if (format == d->format)
         return;
+
     bool wasRich = d->richText;
-    d->richText = format == RichText || (format == AutoText && Qt::mightBeRichText(d->text));
+    d->richText = format == RichText || (format == AutoText && (wasRich || Qt::mightBeRichText(text())));
 
+#ifndef QT_NO_TEXTHTMLPARSER
     if (wasRich && !d->richText) {
-        d->control->setPlainText(d->text);
+        d->control->setPlainText(!d->textCached ? d->control->toHtml() : d->text);
         updateSize();
     } else if (!wasRich && d->richText) {
-#ifndef QT_NO_TEXTHTMLPARSER
-        d->control->setHtml(d->text);
-#else
-        d->control->setPlainText(d->text);
-#endif
+        d->control->setHtml(!d->textCached ? d->control->toPlainText() : d->text);
         updateSize();
-        d->useImageFallback = qmlEnableImageCache();
     }
+#endif
+
     d->format = format;
     d->control->setAcceptRichText(d->format != PlainText);
     emit textFormatChanged(d->format);
@@ -368,6 +366,7 @@ void QQuickTextEdit::setFont(const QFont &font)
         }
         updateSize();
         updateDocument();
+        updateInputMethod(Qt::ImCursorRectangle | Qt::ImFont);
     }
     emit fontChanged(d->sourceFont);
 }
@@ -400,9 +399,6 @@ void QQuickTextEdit::setColor(const QColor &color)
         return;
 
     d->color = color;
-    QPalette pal = d->control->palette();
-    pal.setColor(QPalette::Text, color);
-    d->control->setPalette(pal);
     updateDocument();
     emit colorChanged(d->color);
 }
@@ -425,9 +421,6 @@ void QQuickTextEdit::setSelectionColor(const QColor &color)
         return;
 
     d->selectionColor = color;
-    QPalette pal = d->control->palette();
-    pal.setColor(QPalette::Highlight, color);
-    d->control->setPalette(pal);
     updateDocument();
     emit selectionColorChanged(d->selectionColor);
 }
@@ -450,9 +443,6 @@ void QQuickTextEdit::setSelectedTextColor(const QColor &color)
         return;
 
     d->selectedTextColor = color;
-    QPalette pal = d->control->palette();
-    pal.setColor(QPalette::HighlightedText, color);
-    d->control->setPalette(pal);
     updateDocument();
     emit selectedTextColorChanged(d->selectedTextColor);
 }
@@ -552,10 +542,10 @@ bool QQuickTextEditPrivate::determineHorizontalAlignment()
     Q_Q(QQuickTextEdit);
     if (hAlignImplicit && q->isComponentComplete()) {
         bool alignToRight;
-        if (text.isEmpty()) {
+        if (document->isEmpty()) {
             const QString preeditText = control->textCursor().block().layout()->preeditAreaText();
             alignToRight = preeditText.isEmpty()
-                    ? QGuiApplication::keyboardInputDirection() == Qt::RightToLeft
+                    ? qApp->inputMethod()->inputDirection() == Qt::RightToLeft
                     : preeditText.isRightToLeft();
         } else {
             alignToRight = rightToLeftText;
@@ -657,27 +647,65 @@ int QQuickTextEdit::length() const
 }
 
 /*!
-    \qmlproperty real QtQuick2::TextEdit::paintedWidth
+    \qmlproperty real QtQuick2::TextEdit::contentWidth
 
     Returns the width of the text, including the width past the width
     which is covered due to insufficient wrapping if \l wrapMode is set.
 */
-qreal QQuickTextEdit::paintedWidth() const
+qreal QQuickTextEdit::contentWidth() const
 {
     Q_D(const QQuickTextEdit);
-    return d->paintedSize.width();
+    return d->contentSize.width();
 }
 
 /*!
-    \qmlproperty real QtQuick2::TextEdit::paintedHeight
+    \qmlproperty real QtQuick2::TextEdit::contentHeight
 
     Returns the height of the text, including the height past the height
     that is covered if the text does not fit within the set height.
 */
-qreal QQuickTextEdit::paintedHeight() const
+qreal QQuickTextEdit::contentHeight() const
+{
+    Q_D(const QQuickTextEdit);
+    return d->contentSize.height();
+}
+
+/*!
+    \qmlproperty url QtQuick2::TextEdit::baseUrl
+
+    This property specifies a base URL which is used to resolve relative URLs
+    within the text.
+
+    By default is the url of the TextEdit element.
+*/
+
+QUrl QQuickTextEdit::baseUrl() const
 {
     Q_D(const QQuickTextEdit);
-    return d->paintedSize.height();
+    if (d->baseUrl.isEmpty()) {
+        if (QQmlContext *context = qmlContext(this))
+            const_cast<QQuickTextEditPrivate *>(d)->baseUrl = context->baseUrl();
+    }
+    return d->baseUrl;
+}
+
+void QQuickTextEdit::setBaseUrl(const QUrl &url)
+{
+    Q_D(QQuickTextEdit);
+    if (baseUrl() != url) {
+        d->baseUrl = url;
+
+        d->document->setBaseUrl(url, d->richText);
+        emit baseUrlChanged();
+    }
+}
+
+void QQuickTextEdit::resetBaseUrl()
+{
+    if (QQmlContext *context = qmlContext(this))
+        setBaseUrl(context->baseUrl());
+    else
+        setBaseUrl(QUrl());
 }
 
 /*!
@@ -692,7 +720,7 @@ QRectF QQuickTextEdit::positionToRectangle(int pos) const
     Q_D(const QQuickTextEdit);
     QTextCursor c(d->document);
     c.setPosition(pos);
-    return d->control->cursorRect(c);
+    return d->control->cursorRect(c).translated(0, d->yoff);
 
 }
 
@@ -704,10 +732,10 @@ QRectF QQuickTextEdit::positionToRectangle(int pos) const
     Position 0 is before the first character, position 1 is after the first character
     but before the second, and so on until position \l {text}.length, which is after all characters.
 */
-int QQuickTextEdit::positionAt(int x, int y) const
+int QQuickTextEdit::positionAt(qreal x, qreal y) const
 {
     Q_D(const QQuickTextEdit);
-    int r = d->document->documentLayout()->hitTest(QPoint(x,y-d->yoff), Qt::FuzzyHit);
+    int r = d->document->documentLayout()->hitTest(QPointF(x,y-d->yoff), Qt::FuzzyHit);
     QTextCursor cursor = d->control->textCursor();
     if (r > cursor.position()) {
         // The cursor position includes positions within the preedit text, but only positions in the
@@ -863,7 +891,7 @@ int QQuickTextEdit::cursorPosition() const
 void QQuickTextEdit::setCursorPosition(int pos)
 {
     Q_D(QQuickTextEdit);
-    if (pos < 0 || pos > d->text.length())
+    if (pos < 0 || pos >= d->document->characterCount()) // characterCount includes the terminating null.
         return;
     QTextCursor cursor = d->control->textCursor();
     if (cursor.position() == pos && cursor.anchor() == pos)
@@ -882,16 +910,16 @@ void QQuickTextEdit::setCursorPosition(int pos)
     needed, and the x and y properties of delegate instance will be set so as
     to be one pixel before the top left of the current character.
 
-    Note that the root item of the delegate component must be a QDeclarativeItem or
-    QDeclarativeItem derived item.
+    Note that the root item of the delegate component must be a QQuickItem or
+    QQuickItem derived item.
 */
-QDeclarativeComponent* QQuickTextEdit::cursorDelegate() const
+QQmlComponent* QQuickTextEdit::cursorDelegate() const
 {
     Q_D(const QQuickTextEdit);
     return d->cursorComponent;
 }
 
-void QQuickTextEdit::setCursorDelegate(QDeclarativeComponent* c)
+void QQuickTextEdit::setCursorDelegate(QQmlComponent* c)
 {
     Q_D(QQuickTextEdit);
     if (d->cursorComponent) {
@@ -917,15 +945,15 @@ void QQuickTextEdit::setCursorDelegate(QDeclarativeComponent* c)
 void QQuickTextEdit::loadCursorDelegate()
 {
     Q_D(QQuickTextEdit);
-    if (d->cursorComponent->isLoading())
+    if (d->cursorComponent->isLoading() || !isComponentComplete())
         return;
-    QDeclarativeContext *creationContext = d->cursorComponent->creationContext();
+    QQmlContext *creationContext = d->cursorComponent->creationContext();
     QObject *object = d->cursorComponent->create(creationContext ? creationContext : qmlContext(this));
     d->cursor = qobject_cast<QQuickItem*>(object);
     if (d->cursor) {
         d->control->setCursorWidth(0);
         updateCursor();
-        QDeclarative_setParent_noEvent(d->cursor, this);
+        QQml_setParent_noEvent(d->cursor, this);
         d->cursor->setParentItem(this);
         d->cursor->setHeight(QFontMetrics(d->font).height());
         moveCursorDelegate();
@@ -1029,7 +1057,7 @@ void QQuickTextEdit::setPersistentSelection(bool on)
     emit persistentSelectionChanged(d->persistentSelection);
 }
 
-/*
+/*!
    \qmlproperty real QtQuick2::TextEdit::textMargin
 
    The margin, in pixels, around the text in the TextEdit.
@@ -1050,6 +1078,68 @@ void QQuickTextEdit::setTextMargin(qreal margin)
     emit textMarginChanged(d->textMargin);
 }
 
+/*!
+    \qmlproperty enumeration QtQuick2::TextEdit::inputMethodHints
+
+    Provides hints to the input method about the expected content of the text edit and how it
+    should operate.
+
+    The value is a bit-wise combination of flags or Qt.ImhNone if no hints are set.
+
+    Flags that alter behaviour are:
+
+    \list
+    \o Qt.ImhHiddenText - Characters should be hidden, as is typically used when entering passwords.
+    \o Qt.ImhSensitiveData - Typed text should not be stored by the active input method
+            in any persistent storage like predictive user dictionary.
+    \o Qt.ImhNoAutoUppercase - The input method should not try to automatically switch to upper case
+            when a sentence ends.
+    \o Qt.ImhPreferNumbers - Numbers are preferred (but not required).
+    \o Qt.ImhPreferUppercase - Upper case letters are preferred (but not required).
+    \o Qt.ImhPreferLowercase - Lower case letters are preferred (but not required).
+    \o Qt.ImhNoPredictiveText - Do not use predictive text (i.e. dictionary lookup) while typing.
+
+    \o Qt.ImhDate - The text editor functions as a date field.
+    \o Qt.ImhTime - The text editor functions as a time field.
+    \endlist
+
+    Flags that restrict input (exclusive flags) are:
+
+    \list
+    \o Qt.ImhDigitsOnly - Only digits are allowed.
+    \o Qt.ImhFormattedNumbersOnly - Only number input is allowed. This includes decimal point and minus sign.
+    \o Qt.ImhUppercaseOnly - Only upper case letter input is allowed.
+    \o Qt.ImhLowercaseOnly - Only lower case letter input is allowed.
+    \o Qt.ImhDialableCharactersOnly - Only characters suitable for phone dialing are allowed.
+    \o Qt.ImhEmailCharactersOnly - Only characters suitable for email addresses are allowed.
+    \o Qt.ImhUrlCharactersOnly - Only characters suitable for URLs are allowed.
+    \endlist
+
+    Masks:
+
+    \list
+    \o Qt.ImhExclusiveInputMask - This mask yields nonzero if any of the exclusive flags are used.
+    \endlist
+*/
+
+Qt::InputMethodHints QQuickTextEdit::inputMethodHints() const
+{
+    Q_D(const QQuickTextEdit);
+    return d->inputMethodHints;
+}
+
+void QQuickTextEdit::setInputMethodHints(Qt::InputMethodHints hints)
+{
+    Q_D(QQuickTextEdit);
+
+    if (hints == d->inputMethodHints)
+        return;
+
+    d->inputMethodHints = hints;
+    updateInputMethod(Qt::ImHints);
+    emit inputMethodHintsChanged();
+}
+
 void QQuickTextEdit::geometryChanged(const QRectF &newGeometry,
                                   const QRectF &oldGeometry)
 {
@@ -1067,16 +1157,15 @@ void QQuickTextEdit::componentComplete()
     Q_D(QQuickTextEdit);
     QQuickImplicitSizeItem::componentComplete();
 
-    if (d->richText)
-        d->useImageFallback = qmlEnableImageCache();
-
+    d->document->setBaseUrl(baseUrl(), d->richText);
     if (d->dirty) {
         d->determineHorizontalAlignment();
         d->updateDefaultTextOption();
         updateSize();
         d->dirty = false;
     }
-
+    if (d->cursorComponent && d->cursorComponent->isReady())
+        loadCursorDelegate();
 }
 /*!
     \qmlproperty bool QtQuick2::TextEdit::selectByMouse
@@ -1160,6 +1249,8 @@ void QQuickTextEdit::setReadOnly(bool r)
     if (!r)
         d->control->moveCursor(QTextCursor::End);
 
+    updateInputMethod(Qt::ImEnabled);
+    q_canPasteChanged();
     emit readOnlyChanged(r);
 }
 
@@ -1192,13 +1283,17 @@ Qt::TextInteractionFlags QQuickTextEdit::textInteractionFlags() const
 /*!
     \qmlproperty rectangle QtQuick2::TextEdit::cursorRectangle
 
-    The rectangle where the text cursor is rendered
+    The rectangle where the standard text cursor is rendered
     within the text edit. Read-only.
+
+    The position and height of a custom cursorDelegate are updated to follow the cursorRectangle
+    automatically when it changes.  The width of the delegate is unaffected by changes in the
+    cursor rectangle.
 */
-QRect QQuickTextEdit::cursorRectangle() const
+QRectF QQuickTextEdit::cursorRectangle() const
 {
     Q_D(const QQuickTextEdit);
-    return d->control->cursorRect().toRect().translated(0,d->yoff);
+    return d->control->cursorRect().translated(0, d->yoff);
 }
 
 bool QQuickTextEdit::event(QEvent *event)
@@ -1288,7 +1383,7 @@ void QQuickTextEdit::selectWord()
 void QQuickTextEdit::select(int start, int end)
 {
     Q_D(QQuickTextEdit);
-    if (start < 0 || end < 0 || start > d->text.length() || end > d->text.length())
+    if (start < 0 || end < 0 || start >= d->document->characterCount() || end >= d->document->characterCount())
         return;
     QTextCursor cursor = d->control->textCursor();
     cursor.beginEditBlock();
@@ -1309,12 +1404,11 @@ void QQuickTextEdit::select(int start, int end)
 */
 bool QQuickTextEdit::isRightToLeft(int start, int end)
 {
-    Q_D(QQuickTextEdit);
     if (start > end) {
         qmlInfo(this) << "isRightToLeft(start, end) called with the end property being smaller than the start.";
         return false;
     } else {
-        return d->text.mid(start, end - start).isRightToLeft();
+        return getText(start, end).isRightToLeft();
     }
 }
 
@@ -1353,6 +1447,29 @@ void QQuickTextEdit::paste()
 }
 #endif // QT_NO_CLIPBOARD
 
+
+/*!
+    Undoes the last operation if undo is \l {canUndo}{available}. Deselects any
+    current selection, and updates the selection start to the current cursor
+    position.
+*/
+
+void QQuickTextEdit::undo()
+{
+    Q_D(QQuickTextEdit);
+    d->control->undo();
+}
+
+/*!
+    Redoes the last operation if redo is \l {canRedo}{available}.
+*/
+
+void QQuickTextEdit::redo()
+{
+    Q_D(QQuickTextEdit);
+    d->control->redo();
+}
+
 /*!
 \overload
 Handles the given mouse \a event.
@@ -1426,6 +1543,15 @@ void QQuickTextEdit::itemChange(ItemChange change, const ItemChangeData &value)
 {
     if (change == ItemActiveFocusHasChanged) {
         setCursorVisible(value.boolValue); // ### refactor: focus handling && d->canvas && d->canvas->hasFocus());
+
+        if (value.boolValue) {
+            q_updateAlignment();
+            connect(qApp->inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)),
+                    this, SLOT(q_updateAlignment()));
+        } else {
+            disconnect(qApp->inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)),
+                       this, SLOT(q_updateAlignment()));
+        }
     }
     QQuickItem::itemChange(change, value);
 }
@@ -1454,35 +1580,12 @@ QVariant QQuickTextEdit::inputMethodQuery(Qt::InputMethodQuery property) const
 
 }
 
-void QQuickTextEdit::updateImageCache(const QRectF &)
+void QQuickTextEdit::triggerPreprocess()
 {
     Q_D(QQuickTextEdit);
-
-    // Do we really need the image cache?
-    if (!d->richText || !d->useImageFallback) {
-        if (!d->pixmapCache.isNull())
-            d->pixmapCache = QPixmap();
-        return;
-    }
-
-    if (width() != d->pixmapCache.width() || height() != d->pixmapCache.height())
-        d->pixmapCache = QPixmap(width(), height());
-
-    if (d->pixmapCache.isNull())
-        return;
-
-    // ### Use supplied rect, clear area and update only this part (for cursor updates)
-    QRectF bounds = QRectF(0, 0, width(), height());
-    d->pixmapCache.fill(Qt::transparent);
-    {
-        QPainter painter(&d->pixmapCache);
-
-        painter.setRenderHint(QPainter::TextAntialiasing);
-        painter.translate(0, d->yoff);
-
-        d->control->drawContents(&painter, bounds);
-    }
-
+    if (d->updateType == QQuickTextEditPrivate::UpdateNone)
+        d->updateType = QQuickTextEditPrivate::UpdateOnlyPreprocess;
+    update();
 }
 
 QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData)
@@ -1490,31 +1593,16 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
     Q_UNUSED(updatePaintNodeData);
     Q_D(QQuickTextEdit);
 
-    QSGNode *currentNode = oldNode;
-    if (d->richText && d->useImageFallback) {
-        QSGImageNode *node = 0;
-        if (oldNode == 0 || d->nodeType != QQuickTextEditPrivate::NodeIsTexture) {
-            delete oldNode;
-            node = QQuickItemPrivate::get(this)->sceneGraphContext()->createImageNode();
-            d->texture = new QSGPlainTexture();
-            d->nodeType = QQuickTextEditPrivate::NodeIsTexture;
-            currentNode = node;
-        } else {
-            node = static_cast<QSGImageNode *>(oldNode);
-        }
-
-        qobject_cast<QSGPlainTexture *>(d->texture)->setImage(d->pixmapCache.toImage());
-        node->setTexture(0);
-        node->setTexture(d->texture);
+    if (d->updateType != QQuickTextEditPrivate::UpdatePaintNode && oldNode != 0) {
+        // Update done in preprocess() in the nodes
+        d->updateType = QQuickTextEditPrivate::UpdateNone;
+        return oldNode;
+    }
 
-        node->setTargetRect(QRectF(0, 0, d->pixmapCache.width(), d->pixmapCache.height()));
-        node->setSourceRect(QRectF(0, 0, 1, 1));
-        node->setHorizontalWrapMode(QSGTexture::ClampToEdge);
-        node->setVerticalWrapMode(QSGTexture::ClampToEdge);
-        node->setFiltering(QSGTexture::Linear); // Nonsmooth text just ugly, so don't do that..
-        node->update();
+    d->updateType = QQuickTextEditPrivate::UpdateNone;
 
-    } else if (oldNode == 0 || d->documentDirty) {
+    QSGNode *currentNode = oldNode;
+    if (oldNode == 0 || d->documentDirty) {
         d->documentDirty = false;
 
 #if defined(Q_OS_MAC)
@@ -1525,10 +1613,8 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
 #endif
 
         QQuickTextNode *node = 0;
-        if (oldNode == 0 || d->nodeType != QQuickTextEditPrivate::NodeIsText) {
-            delete oldNode;
-            node = new QQuickTextNode(QQuickItemPrivate::get(this)->sceneGraphContext());
-            d->nodeType = QQuickTextEditPrivate::NodeIsText;
+        if (oldNode == 0) {
+            node = new QQuickTextNode(QQuickItemPrivate::get(this)->sceneGraphContext(), this);
             currentNode = node;
         } else {
             node = static_cast<QQuickTextNode *>(oldNode);
@@ -1539,10 +1625,8 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
 
         QRectF bounds = boundingRect();
 
-        QColor selectionColor = d->control->palette().color(QPalette::Highlight);
-        QColor selectedTextColor = d->control->palette().color(QPalette::HighlightedText);
         node->addTextDocument(bounds.topLeft(), d->document, d->color, QQuickText::Normal, QColor(),
-                              selectionColor, selectedTextColor, selectionStart(),
+                              d->selectionColor, d->selectedTextColor, selectionStart(),
                               selectionEnd() - 1);  // selectionEnd() returns first char after
                                                     // selection
 
@@ -1554,7 +1638,7 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
 #endif
     }
 
-    if (d->nodeType == QQuickTextEditPrivate::NodeIsText && d->cursorComponent == 0 && !isReadOnly()) {
+    if (d->cursorComponent == 0 && !isReadOnly()) {
         QQuickTextNode *node = static_cast<QQuickTextNode *>(currentNode);
 
         QColor color = (!d->cursorVisible || !d->control->cursorOn())
@@ -1596,10 +1680,40 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
 bool QQuickTextEdit::canPaste() const
 {
     Q_D(const QQuickTextEdit);
+    if (!d->canPasteValid) {
+        const_cast<QQuickTextEditPrivate *>(d)->canPaste = d->control->canPaste();
+        const_cast<QQuickTextEditPrivate *>(d)->canPasteValid = true;
+    }
     return d->canPaste;
 }
 
 /*!
+    \qmlproperty bool QtQuick2::TextEdit::canUndo
+
+    Returns true if the TextEdit is writable and there are previous operations
+    that can be undone.
+*/
+
+bool QQuickTextEdit::canUndo() const
+{
+    Q_D(const QQuickTextEdit);
+    return d->document->isUndoAvailable();
+}
+
+/*!
+    \qmlproperty bool QtQuick2::TextEdit::canRedo
+
+    Returns true if the TextEdit is writable and there are \l {undo}{undone}
+    operations that can be redone.
+*/
+
+bool QQuickTextEdit::canRedo() const
+{
+    Q_D(const QQuickTextEdit);
+    return d->document->isRedoAvailable();
+}
+
+/*!
     \qmlproperty bool QtQuick2::TextEdit::inputMethodComposing
 
 
@@ -1631,43 +1745,25 @@ void QQuickTextEditPrivate::init()
     document = new QQuickTextDocumentWithImageResources(q);
 
     control = new QQuickTextControl(document, q);
-    control->setView(q);
-    control->setIgnoreUnusedNavigationEvents(true);
     control->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::TextSelectableByKeyboard | Qt::TextEditable);
-    control->setDragEnabled(false);
-
-    // By default, QQuickTextControl will issue both a updateCursorRequest() and an updateRequest()
-    // when the cursor needs to be repainted. We need the signals to be separate to be able to
-    // distinguish the cursor updates so that we can avoid updating the whole subtree when the
-    // cursor blinks.
-    if (!QObject::disconnect(control, SIGNAL(updateCursorRequest(QRectF)),
-                             control, SIGNAL(updateRequest(QRectF)))) {
-        qWarning("QQuickTextEditPrivate::init: Failed to disconnect updateCursorRequest and updateRequest");
-    }
-
-    // QQuickTextControl follows the default text color
-    // defined by the platform, declarative text
-    // should be black by default
-    QPalette pal = control->palette();
-    if (pal.color(QPalette::Text) != color) {
-        pal.setColor(QPalette::Text, color);
-        control->setPalette(pal);
-    }
-
-    QObject::connect(control, SIGNAL(updateRequest(QRectF)), q, SLOT(updateDocument()));
-    QObject::connect(control, SIGNAL(updateCursorRequest()), q, SLOT(updateCursor()));
-    QObject::connect(control, SIGNAL(textChanged()), q, SLOT(q_textChanged()));
-    QObject::connect(control, SIGNAL(selectionChanged()), q, SIGNAL(selectionChanged()));
-    QObject::connect(control, SIGNAL(selectionChanged()), q, SLOT(updateSelectionMarkers()));
-    QObject::connect(control, SIGNAL(cursorPositionChanged()), q, SLOT(updateSelectionMarkers()));
-    QObject::connect(control, SIGNAL(cursorPositionChanged()), q, SIGNAL(cursorPositionChanged()));
-    QObject::connect(control, SIGNAL(microFocusChanged()), q, SLOT(moveCursorDelegate()));
-    QObject::connect(control, SIGNAL(linkActivated(QString)), q, SIGNAL(linkActivated(QString)));
+    control->setAcceptRichText(false);
+    control->setCursorIsFocusIndicator(true);
+
+    FAST_CONNECT(control, SIGNAL(updateRequest(QRectF)), q, SLOT(updateDocument()));
+    FAST_CONNECT(control, SIGNAL(updateCursorRequest()), q, SLOT(updateCursor()));
+    FAST_CONNECT(control, SIGNAL(textChanged()), q, SLOT(q_textChanged()));
+    FAST_CONNECT(control, SIGNAL(selectionChanged()), q, SIGNAL(selectionChanged()));
+    FAST_CONNECT(control, SIGNAL(selectionChanged()), q, SLOT(updateSelectionMarkers()));
+    FAST_CONNECT(control, SIGNAL(cursorPositionChanged()), q, SLOT(updateSelectionMarkers()));
+    FAST_CONNECT(control, SIGNAL(cursorPositionChanged()), q, SIGNAL(cursorPositionChanged()));
+    FAST_CONNECT(control, SIGNAL(cursorRectangleChanged()), q, SLOT(moveCursorDelegate()));
+    FAST_CONNECT(control, SIGNAL(linkActivated(QString)), q, SIGNAL(linkActivated(QString)));
 #ifndef QT_NO_CLIPBOARD
-    QObject::connect(q, SIGNAL(readOnlyChanged(bool)), q, SLOT(q_canPasteChanged()));
-    QObject::connect(QGuiApplication::clipboard(), SIGNAL(dataChanged()), q, SLOT(q_canPasteChanged()));
-    canPaste = control->canPaste();
+    FAST_CONNECT(QGuiApplication::clipboard(), SIGNAL(dataChanged()), q, SLOT(q_canPasteChanged()));
 #endif
+    FAST_CONNECT(document, SIGNAL(undoAvailable(bool)), q, SIGNAL(canUndoChanged()));
+    FAST_CONNECT(document, SIGNAL(redoAvailable(bool)), q, SIGNAL(canRedoChanged()));
+    FAST_CONNECT(document, SIGNAL(imagesLoaded()), q, SLOT(updateSize()));
 
     document->setDefaultFont(font);
     document->setDocumentMargin(textMargin);
@@ -1679,20 +1775,20 @@ void QQuickTextEditPrivate::init()
 void QQuickTextEdit::q_textChanged()
 {
     Q_D(QQuickTextEdit);
-    d->text = text();
+    d->textCached = false;
     d->rightToLeftText = d->document->begin().layout()->engine()->isRightToLeft();
     d->determineHorizontalAlignment();
     d->updateDefaultTextOption();
     updateSize();
     updateTotalLines();
-    emit textChanged(d->text);
+    emit textChanged();
 }
 
 void QQuickTextEdit::moveCursorDelegate()
 {
     Q_D(QQuickTextEdit);
     d->determineHorizontalAlignment();
-    updateMicroFocus();
+    updateInputMethod();
     emit cursorRectangleChanged();
     if (!d->cursor)
         return;
@@ -1767,11 +1863,11 @@ void QQuickTextEdit::updateSize()
         } else {
             d->document->setTextWidth(-1);
         }
-        QFontMetrics fm = QFontMetrics(d->font);
-        int dy = height();
-        dy -= (int)d->document->size().height();
+        QFontMetricsF fm(d->font);
+        qreal dy = height();
+        dy -= d->document->size().height();
 
-        int nyoff;
+        qreal nyoff;
         if (heightValid()) {
             if (d->vAlign == AlignBottom)
                 nyoff = dy;
@@ -1787,7 +1883,7 @@ void QQuickTextEdit::updateSize()
         setBaselineOffset(fm.ascent() + d->yoff + d->textMargin);
 
         //### need to comfirm cost of always setting these
-        int newWidth = qCeil(d->document->idealWidth());
+        qreal newWidth = d->document->idealWidth();
         if (!widthValid() && d->document->textWidth() != newWidth)
             d->document->setTextWidth(newWidth); // ### Text does not align if width is not set (QTextDoc bug)
         // ### Setting the implicitWidth triggers another updateSize(), and unless there are bindings nothing has changed.
@@ -1796,14 +1892,17 @@ void QQuickTextEdit::updateSize()
             iWidth = newWidth;
         else if (d->requireImplicitWidth)
             iWidth = naturalWidth;
-        qreal newHeight = d->document->isEmpty() ? fm.height() : (int)d->document->size().height();
+        qreal newHeight = d->document->isEmpty() ? fm.height() : d->document->size().height();
         if (iWidth > -1)
             setImplicitSize(iWidth, newHeight);
         else
             setImplicitHeight(newHeight);
 
-        d->paintedSize = QSize(newWidth, newHeight);
-        emit paintedSizeChanged();
+        QSizeF size(newWidth, newHeight);
+        if (d->contentSize != size) {
+            d->contentSize = size;
+            emit contentSizeChanged();
+        }
     } else {
         d->dirty = true;
     }
@@ -1816,7 +1915,7 @@ void QQuickTextEdit::updateDocument()
     d->documentDirty = true;
 
     if (isComponentComplete()) {
-        updateImageCache();
+        d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
         update();
     }
 }
@@ -1825,11 +1924,20 @@ void QQuickTextEdit::updateCursor()
 {
     Q_D(QQuickTextEdit);
     if (isComponentComplete()) {
-        updateImageCache(d->control->cursorRect());
+        d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
         update();
     }
 }
 
+void QQuickTextEdit::q_updateAlignment()
+{
+    Q_D(QQuickTextEdit);
+    if (d->determineHorizontalAlignment()) {
+        d->updateDefaultTextOption();
+        moveCursorDelegate();
+    }
+}
+
 void QQuickTextEdit::updateTotalLines()
 {
     Q_D(QQuickTextEdit);
@@ -1867,16 +1975,11 @@ void QQuickTextEditPrivate::updateDefaultTextOption()
 
     QTextOption::WrapMode oldWrapMode = opt.wrapMode();
     opt.setWrapMode(QTextOption::WrapMode(wrapMode));
+    opt.setUseDesignMetrics(true);
 
-    bool oldUseDesignMetrics = opt.useDesignMetrics();
-    bool useDesignMetrics = !qmlDisableDistanceField();
-    opt.setUseDesignMetrics(useDesignMetrics);
-
-    if (oldWrapMode == opt.wrapMode()
-            && oldAlignment == opt.alignment()
-            && oldUseDesignMetrics == useDesignMetrics) {
+    if (oldWrapMode == opt.wrapMode() && oldAlignment == opt.alignment())
         return;
-    }
+
     document->setDefaultTextOption(opt);
 }
 
@@ -1899,7 +2002,7 @@ void QQuickTextEditPrivate::updateDefaultTextOption()
     Only relevant on platforms, which provide virtual keyboards.
 
     \code
-        import QtQuick 1.0
+        import QtQuick 2.0
         TextEdit {
             id: textEdit
             text: "Hello world!"
@@ -1922,7 +2025,7 @@ void QQuickTextEditPrivate::updateDefaultTextOption()
 void QQuickTextEdit::openSoftwareInputPanel()
 {
     if (qGuiApp)
-        qGuiApp->inputPanel()->show();
+        qGuiApp->inputMethod()->show();
 }
 
 /*!
@@ -1942,7 +2045,7 @@ void QQuickTextEdit::openSoftwareInputPanel()
     Only relevant on platforms, which provide virtual keyboards.
 
     \code
-        import QtQuick 1.0
+        import QtQuick 2.0
         TextEdit {
             id: textEdit
             text: "Hello world!"
@@ -1965,7 +2068,7 @@ void QQuickTextEdit::openSoftwareInputPanel()
 void QQuickTextEdit::closeSoftwareInputPanel()
 {
     if (qGuiApp)
-        qGuiApp->inputPanel()->hide();
+        qGuiApp->inputMethod()->hide();
 }
 
 void QQuickTextEdit::focusInEvent(QFocusEvent *event)
@@ -1981,7 +2084,9 @@ void QQuickTextEdit::q_canPasteChanged()
     Q_D(QQuickTextEdit);
     bool old = d->canPaste;
     d->canPaste = d->control->canPaste();
-    if (old!=d->canPaste)
+    bool changed = old!=d->canPaste || !d->canPasteValid;
+    d->canPasteValid = true;
+    if (changed)
         emit canPasteChanged();
 }