Skip to content

Commit

Permalink
Add a baseUrl property to Text and TextEdit.
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Andrew den Exter authored and Qt by Nokia committed Jan 19, 2012
1 parent bab2eaf commit de0a5f2
Show file tree
Hide file tree
Showing 12 changed files with 190 additions and 3 deletions.
55 changes: 52 additions & 3 deletions src/quick/items/qquicktext.cpp
Expand Up @@ -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);
Expand Down Expand Up @@ -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()) {
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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()));
}
}
Expand Down Expand Up @@ -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
{
Expand Down
6 changes: 6 additions & 0 deletions src/quick/items/qquicktext_p.h
Expand Up @@ -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);
Expand Down Expand Up @@ -164,6 +165,10 @@ class Q_QUICK_PRIVATE_EXPORT QQuickText : public QQuickImplicitSizeItem
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
Expand Down Expand Up @@ -194,6 +199,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickText : public QQuickImplicitSizeItem
void lineHeightModeChanged(LineHeightMode mode);
void effectiveHorizontalAlignmentChanged();
void lineLaidOut(QQuickTextLine *line);
void baseUrlChanged();

protected:
void mousePressEvent(QMouseEvent *event);
Expand Down
4 changes: 4 additions & 0 deletions src/quick/items/qquicktext_p_p.h
Expand Up @@ -84,6 +84,7 @@ class Q_AUTOTEST_EXPORT QQuickTextPrivate : public QQuickImplicitSizeItemPrivate
bool isLineLaidOutConnected();

QString text;
QUrl baseUrl;
QFont font;
QFont sourceFont;
QColor color;
Expand Down Expand Up @@ -188,6 +189,8 @@ class QQuickTextDocumentWithImageResources : public QTextDocument, public QTextO

QImage image(const QTextImageFormat &format);

void setBaseUrl(const QUrl &url, bool clear = true);

Q_SIGNALS:
void imagesLoaded();

Expand All @@ -201,6 +204,7 @@ private slots:

private:
QHash<QUrl, QDeclarativePixmap *> m_resources;
QUrl m_baseUrl;

int outstanding;
static QSet<QUrl> errors;
Expand Down
39 changes: 39 additions & 0 deletions src/quick/items/qquicktextedit.cpp
Expand Up @@ -685,6 +685,44 @@ qreal QQuickTextEdit::paintedHeight() const
return d->paintedSize.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);
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)
Expand Down Expand Up @@ -1116,6 +1154,7 @@ void QQuickTextEdit::componentComplete()
Q_D(QQuickTextEdit);
QQuickImplicitSizeItem::componentComplete();

d->document->setBaseUrl(baseUrl(), d->richText);
if (d->richText)
d->useImageFallback = qmlEnableImageCache();

Expand Down
2 changes: 2 additions & 0 deletions src/quick/items/qquicktextedit_p.h
Expand Up @@ -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);
Expand Down Expand Up @@ -261,6 +262,7 @@ class Q_AUTOTEST_EXPORT QQuickTextEdit : public QQuickImplicitSizeItem
void canRedoChanged();
void inputMethodComposingChanged();
void effectiveHorizontalAlignmentChanged();
void baseUrlChanged();

public Q_SLOTS:
void selectAll();
Expand Down
1 change: 1 addition & 0 deletions src/quick/items/qquicktextedit_p_p.h
Expand Up @@ -95,6 +95,7 @@ class QQuickTextEditPrivate : public QQuickImplicitSizeItemPrivate
qreal getImplicitWidth() const;

QString text;
QUrl baseUrl;
QFont font;
QFont sourceFont;
QColor color;
Expand Down
@@ -0,0 +1,7 @@
import QtQuick 2.0

Text {
textFormat: Text.RichText
text: "<img src='exists.png'>"
baseUrl: "http/"
}
@@ -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"
}
29 changes: 29 additions & 0 deletions tests/auto/qtquick2/qquicktext/tst_qquicktext.cpp
Expand Up @@ -75,6 +75,7 @@ private slots:
void alignments_data();
void alignments();

void baseUrl();
void embeddedImages_data();
void embeddedImages();

Expand Down Expand Up @@ -1282,16 +1283,44 @@ 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");
QTest::addColumn<QString>("error");
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()
Expand Down
@@ -0,0 +1,7 @@
import QtQuick 2.0

TextEdit {
textFormat: TextEdit.RichText
text: "<img src='exists.png'>"
baseUrl: "http/"
}
@@ -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"
}
29 changes: 29 additions & 0 deletions tests/auto/qtquick2/qquicktextedit/tst_qquicktextedit.cpp
Expand Up @@ -175,6 +175,7 @@ private slots:
void undo_keypressevents_data();
void undo_keypressevents();

void baseUrl();
void embeddedImages();
void embeddedImages_data();

Expand Down Expand Up @@ -3658,16 +3659,44 @@ 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");
QTest::addColumn<QString>("error");
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()
Expand Down

0 comments on commit de0a5f2

Please sign in to comment.