Add a baseUrl property to Text and TextEdit.
authorAndrew den Exter <andrew.den-exter@nokia.com>
Tue, 17 Jan 2012 00:42:26 +0000 (10:42 +1000)
committerQt by Nokia <qt-info@nokia.com>
Thu, 19 Jan 2012 10:11:02 +0000 (11:11 +0100)
Specifies the base URL which embedded links in rich text are resolved
against.  By default this is the URL of the item.

Task-number: QTBUG-23655
Change-Id: Ib51b8503a18d9ac4e1801c77b77b3595d8f4912a
Reviewed-by: Martin Jones <martin.jones@nokia.com>
12 files changed:
src/quick/items/qquicktext.cpp
src/quick/items/qquicktext_p.h
src/quick/items/qquicktext_p_p.h
src/quick/items/qquicktextedit.cpp
src/quick/items/qquicktextedit_p.h
src/quick/items/qquicktextedit_p_p.h
tests/auto/qtquick2/qquicktext/data/embeddedImagesLocalRelative.qml [new file with mode: 0644]
tests/auto/qtquick2/qquicktext/data/embeddedImagesRemoteRelative.qml [new file with mode: 0644]
tests/auto/qtquick2/qquicktext/tst_qquicktext.cpp
tests/auto/qtquick2/qquicktextedit/data/embeddedImagesLocalRelative.qml [new file with mode: 0644]
tests/auto/qtquick2/qquicktextedit/data/embeddedImagesRemoteRelative.qml [new file with mode: 0644]
tests/auto/qtquick2/qquicktextedit/tst_qquicktextedit.cpp

index fb90220..c599943 100644 (file)
@@ -119,7 +119,7 @@ QQuickTextDocumentWithImageResources::~QQuickTextDocumentWithImageResources()
 QVariant QQuickTextDocumentWithImageResources::loadResource(int type, const QUrl &name)
 {
     QDeclarativeContext *context = qmlContext(parent());
-    QUrl url = context->resolvedUrl(name);
+    QUrl url = m_baseUrl.resolved(name);
 
     if (type == QTextDocument::ImageResource) {
         QDeclarativePixmap *p = loadPixmap(context, url);
@@ -160,7 +160,7 @@ QSizeF QQuickTextDocumentWithImageResources::intrinsicSize(
         QSizeF size(width, height);
         if (!hasWidth || !hasHeight) {
             QDeclarativeContext *context = qmlContext(parent());
-            QUrl url = context->resolvedUrl(QUrl(imageFormat.name()));
+            QUrl url = m_baseUrl.resolved(QUrl(imageFormat.name()));
 
             QDeclarativePixmap *p = loadPixmap(context, url);
             if (!p->isReady()) {
@@ -198,12 +198,21 @@ void QQuickTextDocumentWithImageResources::drawObject(
 QImage QQuickTextDocumentWithImageResources::image(const QTextImageFormat &format)
 {
     QDeclarativeContext *context = qmlContext(parent());
-    QUrl url = context->resolvedUrl(QUrl(format.name()));
+    QUrl url = m_baseUrl.resolved(QUrl(format.name()));
 
     QDeclarativePixmap *p = loadPixmap(context, url);
     return p->image();
 }
 
+void QQuickTextDocumentWithImageResources::setBaseUrl(const QUrl &url, bool clear)
+{
+    m_baseUrl = url;
+    if (clear) {
+        clearResources();
+        markContentsDirty(0, characterCount());
+    }
+}
+
 QDeclarativePixmap *QQuickTextDocumentWithImageResources::loadPixmap(
         QDeclarativeContext *context, const QUrl &url)
 {
@@ -901,6 +910,7 @@ void QQuickTextPrivate::ensureDoc()
         Q_Q(QQuickText);
         doc = new QQuickTextDocumentWithImageResources(q);
         doc->setDocumentMargin(0);
+        doc->setBaseUrl(q->baseUrl());
         FAST_CONNECT(doc, SIGNAL(imagesLoaded()), q, SLOT(q_imagesLoaded()));
     }
 }
@@ -1717,6 +1727,45 @@ void QQuickText::setElideMode(QQuickText::TextElideMode mode)
     emit elideModeChanged(d->elideMode);
 }
 
+/*!
+    \qmlproperty url QtQuick2::Text::baseUrl
+
+    This property specifies a base URL which is used to resolve relative URLs
+    within the text.
+
+    By default is the url of the Text element.
+*/
+
+QUrl QQuickText::baseUrl() const
+{
+    Q_D(const QQuickText);
+    if (d->baseUrl.isEmpty()) {
+        if (QDeclarativeContext *context = qmlContext(this))
+            const_cast<QQuickTextPrivate *>(d)->baseUrl = context->baseUrl();
+    }
+    return d->baseUrl;
+}
+
+void QQuickText::setBaseUrl(const QUrl &url)
+{
+    Q_D(QQuickText);
+    if (baseUrl() != url) {
+        d->baseUrl = url;
+
+        if (d->doc)
+            d->doc->setBaseUrl(url);
+        emit baseUrlChanged();
+    }
+}
+
+void QQuickText::resetBaseUrl()
+{
+    if (QDeclarativeContext *context = qmlContext(this))
+        setBaseUrl(context->baseUrl());
+    else
+        setBaseUrl(QUrl());
+}
+
 /*! \internal */
 QRectF QQuickText::boundingRect() const
 {
index ad38953..002169d 100644 (file)
@@ -85,6 +85,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickText : public QQuickImplicitSizeItem
     Q_PROPERTY(qreal paintedHeight READ paintedHeight NOTIFY paintedSizeChanged)
     Q_PROPERTY(qreal lineHeight READ lineHeight WRITE setLineHeight NOTIFY lineHeightChanged)
     Q_PROPERTY(LineHeightMode lineHeightMode READ lineHeightMode WRITE setLineHeightMode NOTIFY lineHeightModeChanged)
+    Q_PROPERTY(QUrl baseUrl READ baseUrl WRITE setBaseUrl RESET resetBaseUrl NOTIFY baseUrlChanged)
 
 public:
     QQuickText(QQuickItem *parent=0);
@@ -164,6 +165,10 @@ public:
     LineHeightMode lineHeightMode() const;
     void setLineHeightMode(LineHeightMode);
 
+    QUrl baseUrl() const;
+    void setBaseUrl(const QUrl &url);
+    void resetBaseUrl();
+
     virtual void componentComplete();
 
     int resourcesLoading() const; // mainly for testing
@@ -194,6 +199,7 @@ Q_SIGNALS:
     void lineHeightModeChanged(LineHeightMode mode);
     void effectiveHorizontalAlignmentChanged();
     void lineLaidOut(QQuickTextLine *line);
+    void baseUrlChanged();
 
 protected:
     void mousePressEvent(QMouseEvent *event);
index cf95fc6..b461dc5 100644 (file)
@@ -84,6 +84,7 @@ public:
     bool isLineLaidOutConnected();
 
     QString text;
+    QUrl baseUrl;
     QFont font;
     QFont sourceFont;
     QColor  color;
@@ -188,6 +189,8 @@ public:
 
     QImage image(const QTextImageFormat &format);
 
+    void setBaseUrl(const QUrl &url, bool clear = true);
+
 Q_SIGNALS:
     void imagesLoaded();
 
@@ -201,6 +204,7 @@ private slots:
 
 private:
     QHash<QUrl, QDeclarativePixmap *> m_resources;
+    QUrl m_baseUrl;
 
     int outstanding;
     static QSet<QUrl> errors;
index 0ecaf4c..7ca2b50 100644 (file)
@@ -686,6 +686,44 @@ qreal QQuickTextEdit::paintedHeight() const
 }
 
 /*!
+    \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);
+    if (d->baseUrl.isEmpty()) {
+        if (QDeclarativeContext *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 (QDeclarativeContext *context = qmlContext(this))
+        setBaseUrl(context->baseUrl());
+    else
+        setBaseUrl(QUrl());
+}
+
+/*!
     \qmlmethod rectangle QtQuick2::TextEdit::positionToRectangle(position)
 
     Returns the rectangle at the given \a position in the text. The x, y,
@@ -1116,6 +1154,7 @@ void QQuickTextEdit::componentComplete()
     Q_D(QQuickTextEdit);
     QQuickImplicitSizeItem::componentComplete();
 
+    d->document->setBaseUrl(baseUrl(), d->richText);
     if (d->richText)
         d->useImageFallback = qmlEnableImageCache();
 
index 8cdd984..9a591c9 100644 (file)
@@ -93,6 +93,7 @@ class Q_AUTOTEST_EXPORT QQuickTextEdit : public QQuickImplicitSizeItem
     Q_PROPERTY(bool canUndo READ canUndo NOTIFY canUndoChanged)
     Q_PROPERTY(bool canRedo READ canRedo NOTIFY canRedoChanged)
     Q_PROPERTY(bool inputMethodComposing READ isInputMethodComposing NOTIFY inputMethodComposingChanged)
+    Q_PROPERTY(QUrl baseUrl READ baseUrl WRITE setBaseUrl RESET resetBaseUrl NOTIFY baseUrlChanged)
 
 public:
     QQuickTextEdit(QQuickItem *parent=0);
@@ -261,6 +262,7 @@ Q_SIGNALS:
     void canRedoChanged();
     void inputMethodComposingChanged();
     void effectiveHorizontalAlignmentChanged();
+    void baseUrlChanged();
 
 public Q_SLOTS:
     void selectAll();
index 801bca0..3a7291b 100644 (file)
@@ -95,6 +95,7 @@ public:
     qreal getImplicitWidth() const;
 
     QString text;
+    QUrl baseUrl;
     QFont font;
     QFont sourceFont;
     QColor  color;
diff --git a/tests/auto/qtquick2/qquicktext/data/embeddedImagesLocalRelative.qml b/tests/auto/qtquick2/qquicktext/data/embeddedImagesLocalRelative.qml
new file mode 100644 (file)
index 0000000..8de7364
--- /dev/null
@@ -0,0 +1,7 @@
+import QtQuick 2.0
+
+Text {
+    textFormat: Text.RichText
+    text: "<img src='exists.png'>"
+    baseUrl: "http/"
+}
diff --git a/tests/auto/qtquick2/qquicktext/data/embeddedImagesRemoteRelative.qml b/tests/auto/qtquick2/qquicktext/data/embeddedImagesRemoteRelative.qml
new file mode 100644 (file)
index 0000000..cee1974
--- /dev/null
@@ -0,0 +1,7 @@
+import QtQuick 2.0
+
+Text {
+    textFormat: Text.RichText
+    text: "<img src='exists.png'>"
+    baseUrl: "http://127.0.0.1:14453/text.html"
+}
index fa9549a..60dab33 100644 (file)
@@ -75,6 +75,7 @@ private slots:
     void alignments_data();
     void alignments();
 
+    void baseUrl();
     void embeddedImages_data();
     void embeddedImages();
 
@@ -1282,6 +1283,32 @@ void tst_qquicktext::clickLink()
     }
 }
 
+void tst_qquicktext::baseUrl()
+{
+    QUrl localUrl("file:///tests/text.qml");
+    QUrl remoteUrl("http://qt.nokia.com/test.qml");
+
+    QDeclarativeComponent textComponent(&engine);
+    textComponent.setData("import QtQuick 2.0\n Text {}", localUrl);
+    QQuickText *textObject = qobject_cast<QQuickText *>(textComponent.create());
+
+    QCOMPARE(textObject->baseUrl(), localUrl);
+
+    QSignalSpy spy(textObject, SIGNAL(baseUrlChanged()));
+
+    textObject->setBaseUrl(localUrl);
+    QCOMPARE(textObject->baseUrl(), localUrl);
+    QCOMPARE(spy.count(), 0);
+
+    textObject->setBaseUrl(remoteUrl);
+    QCOMPARE(textObject->baseUrl(), remoteUrl);
+    QCOMPARE(spy.count(), 1);
+
+    textObject->resetBaseUrl();
+    QCOMPARE(textObject->baseUrl(), localUrl);
+    QCOMPARE(spy.count(), 2);
+}
+
 void tst_qquicktext::embeddedImages_data()
 {
     QTest::addColumn<QUrl>("qmlfile");
@@ -1289,9 +1316,11 @@ void tst_qquicktext::embeddedImages_data()
     QTest::newRow("local") << testFileUrl("embeddedImagesLocal.qml") << "";
     QTest::newRow("local-error") << testFileUrl("embeddedImagesLocalError.qml")
         << testFileUrl("embeddedImagesLocalError.qml").toString()+":3:1: QML Text: Cannot open: " + testFileUrl("http/notexists.png").toString();
+    QTest::newRow("local") << testFileUrl("embeddedImagesLocalRelative.qml") << "";
     QTest::newRow("remote") << testFileUrl("embeddedImagesRemote.qml") << "";
     QTest::newRow("remote-error") << testFileUrl("embeddedImagesRemoteError.qml")
         << testFileUrl("embeddedImagesRemoteError.qml").toString()+":3:1: QML Text: Error downloading http://127.0.0.1:14453/notexists.png - server replied: Not found";
+    QTest::newRow("remote") << testFileUrl("embeddedImagesRemoteRelative.qml") << "";
 }
 
 void tst_qquicktext::embeddedImages()
diff --git a/tests/auto/qtquick2/qquicktextedit/data/embeddedImagesLocalRelative.qml b/tests/auto/qtquick2/qquicktextedit/data/embeddedImagesLocalRelative.qml
new file mode 100644 (file)
index 0000000..200ded1
--- /dev/null
@@ -0,0 +1,7 @@
+import QtQuick 2.0
+
+TextEdit {
+    textFormat: TextEdit.RichText
+    text: "<img src='exists.png'>"
+    baseUrl: "http/"
+}
diff --git a/tests/auto/qtquick2/qquicktextedit/data/embeddedImagesRemoteRelative.qml b/tests/auto/qtquick2/qquicktextedit/data/embeddedImagesRemoteRelative.qml
new file mode 100644 (file)
index 0000000..ee39e08
--- /dev/null
@@ -0,0 +1,7 @@
+import QtQuick 2.0
+
+TextEdit {
+    textFormat: TextEdit.RichText
+    text: "<img src='exists.png'>"
+    baseUrl: "http://127.0.0.1:42332/text.html"
+}
index 272ec19..4a5bb01 100644 (file)
@@ -175,6 +175,7 @@ private slots:
     void undo_keypressevents_data();
     void undo_keypressevents();
 
+    void baseUrl();
     void embeddedImages();
     void embeddedImages_data();
 
@@ -3658,6 +3659,32 @@ void tst_qquicktextedit::undo_keypressevents()
     QVERIFY(textEdit->text().isEmpty());
 }
 
+void tst_qquicktextedit::baseUrl()
+{
+    QUrl localUrl("file:///tests/text.qml");
+    QUrl remoteUrl("http://qt.nokia.com/test.qml");
+
+    QDeclarativeComponent textComponent(&engine);
+    textComponent.setData("import QtQuick 2.0\n TextEdit {}", localUrl);
+    QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit *>(textComponent.create());
+
+    QCOMPARE(textObject->baseUrl(), localUrl);
+
+    QSignalSpy spy(textObject, SIGNAL(baseUrlChanged()));
+
+    textObject->setBaseUrl(localUrl);
+    QCOMPARE(textObject->baseUrl(), localUrl);
+    QCOMPARE(spy.count(), 0);
+
+    textObject->setBaseUrl(remoteUrl);
+    QCOMPARE(textObject->baseUrl(), remoteUrl);
+    QCOMPARE(spy.count(), 1);
+
+    textObject->resetBaseUrl();
+    QCOMPARE(textObject->baseUrl(), localUrl);
+    QCOMPARE(spy.count(), 2);
+}
+
 void tst_qquicktextedit::embeddedImages_data()
 {
     QTest::addColumn<QUrl>("qmlfile");
@@ -3665,9 +3692,11 @@ void tst_qquicktextedit::embeddedImages_data()
     QTest::newRow("local") << testFileUrl("embeddedImagesLocal.qml") << "";
     QTest::newRow("local-error") << testFileUrl("embeddedImagesLocalError.qml")
         << testFileUrl("embeddedImagesLocalError.qml").toString()+":3:1: QML TextEdit: Cannot open: " + testFileUrl("http/notexists.png").toString();
+    QTest::newRow("local") << testFileUrl("embeddedImagesLocalRelative.qml") << "";
     QTest::newRow("remote") << testFileUrl("embeddedImagesRemote.qml") << "";
     QTest::newRow("remote-error") << testFileUrl("embeddedImagesRemoteError.qml")
         << testFileUrl("embeddedImagesRemoteError.qml").toString()+":3:1: QML TextEdit: Error downloading http://127.0.0.1:42332/notexists.png - server replied: Not found";
+    QTest::newRow("remote") << testFileUrl("embeddedImagesRemoteRelative.qml") << "";
 }
 
 void tst_qquicktextedit::embeddedImages()