From 08e5bae295204ac0cadc1dda6c7a01599f66a2bb Mon Sep 17 00:00:00 2001 From: Andreas Aardal Hanssen Date: Wed, 28 Oct 2009 09:42:14 +0100 Subject: Let QGraphicsItem set QObjectPrivate::wasDeleted when appropriate. This change allows children of QGraphicsObject-derived classes to check if the parent is being deleted by checking the private flag wasDeleted. Reverts 37b16d5cbb4e7bc534f690ebf50434d228b5ecfc, p4 change 9681, to allow QGraphicsItem to set QObjectPrivate's wasDeleted member before entering QObjectPrivate's destructor. The original code was in there to let the user know, via the console output, that QObject was double-deleted (e.g., when placing QObject on the stack, and also giving it a parent, so that if the parent is deleted first, bang). Reviewed-by: Aaron Kennedy Reviewed-by: Bradley T. Hughes --- src/corelib/kernel/qobject.cpp | 7 ---- src/gui/graphicsview/qgraphicsitem.cpp | 2 ++ tests/auto/qgraphicsobject/tst_qgraphicsobject.cpp | 42 ++++++++++++++++++++++ 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 7be19b3..5303975 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -835,14 +835,7 @@ QObject::QObject(QObjectPrivate &dd, QObject *parent) QObject::~QObject() { Q_D(QObject); - if (d->wasDeleted) { -#if defined(QT_DEBUG) - qWarning("QObject: Double deletion detected"); -#endif - return; - } d->wasDeleted = true; - d->blockSig = 0; // unblock signals so we always emit destroyed() if (!d->isWidget) { diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index ad672a3..2e68d3e 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -1286,6 +1286,8 @@ QGraphicsItem::QGraphicsItem(QGraphicsItemPrivate &dd, QGraphicsItem *parent, */ QGraphicsItem::~QGraphicsItem() { + if (d_ptr->isObject) + QObjectPrivate::get(static_cast(this))->wasDeleted = true; d_ptr->inDestructor = 1; d_ptr->removeExtraItemCache(); diff --git a/tests/auto/qgraphicsobject/tst_qgraphicsobject.cpp b/tests/auto/qgraphicsobject/tst_qgraphicsobject.cpp index a9fd55a..194665d 100644 --- a/tests/auto/qgraphicsobject/tst_qgraphicsobject.cpp +++ b/tests/auto/qgraphicsobject/tst_qgraphicsobject.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include "../../shared/util.h" class tst_QGraphicsObject : public QObject { @@ -65,6 +66,7 @@ private slots: void opacity(); void enabled(); void visible(); + void deleted(); }; @@ -249,6 +251,46 @@ void tst_QGraphicsObject::visible() QVERIFY(object.property("visible") == true); } +class DeleteTester : public QGraphicsObject +{ +public: + DeleteTester(bool *w, bool *pw, QGraphicsItem *parent = 0) + : QGraphicsObject(parent), wasDeleted(w), parentWasDeleted(pw) + { } + + ~DeleteTester() + { + *wasDeleted = QObjectPrivate::get(this)->wasDeleted; + if (QGraphicsItem *p = parentItem()) { + if (QGraphicsObject *o = p->toGraphicsObject()) + *parentWasDeleted = QObjectPrivate::get(o)->wasDeleted; + } + } + + void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget * = 0) + { } + QRectF boundingRect() const + { return QRectF(); } + + bool *wasDeleted; + bool *parentWasDeleted; +}; + +void tst_QGraphicsObject::deleted() +{ + bool item1_parentWasDeleted = false; + bool item1_wasDeleted = false; + bool item2_parentWasDeleted = false; + bool item2_wasDeleted = false; + DeleteTester *item1 = new DeleteTester(&item1_wasDeleted, &item1_parentWasDeleted); + DeleteTester *item2 = new DeleteTester(&item2_wasDeleted, &item2_parentWasDeleted, item1); + delete item1; + + QVERIFY(!item1_wasDeleted); // destructor not called yet + QVERIFY(!item1_parentWasDeleted); // no parent + QVERIFY(!item2_wasDeleted); // destructor not called yet + QVERIFY(item2_parentWasDeleted); +} QTEST_MAIN(tst_QGraphicsObject) #include "tst_qgraphicsobject.moc" -- cgit v0.12 From de7de35082ec21d506a90e68cb30888540ec64cc Mon Sep 17 00:00:00 2001 From: Thorvald Natvig Date: Tue, 20 Oct 2009 13:31:02 +0200 Subject: Fix XML entities in QTextDocument::toHtml() If a QTextDocument contains an anchor with a & in it, this & is not escaped in the toHtml() function, meaning the resulting document can't be parsed as XML as it will contain invalid entities. Reviewed-by: Olivier Goffart Merge-request: 1753 --- src/gui/text/qtextdocument.cpp | 33 ++++++++++++++++++++------ tests/auto/qtextdocument/qtextdocument.pro | 1 + tests/auto/qtextdocument/tst_qtextdocument.cpp | 32 +++++++++++++++++++++++-- 3 files changed, 57 insertions(+), 9 deletions(-) diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp index a8956b8..17981e3 100644 --- a/src/gui/text/qtextdocument.cpp +++ b/src/gui/text/qtextdocument.cpp @@ -168,6 +168,25 @@ QString Qt::escape(const QString& plain) return rich; } +static QString escapeXml(const QString &plain) +{ + QString rich; + rich.reserve(int(plain.length() * 1.1)); + for (int i = 0; i < plain.length(); ++i) { + if (plain.at(i) == QLatin1Char('<')) + rich += QLatin1String("<"); + else if (plain.at(i) == QLatin1Char('>')) + rich += QLatin1String(">"); + else if (plain.at(i) == QLatin1Char('&')) + rich += QLatin1String("&"); + else if (plain.at(i) == QLatin1Char('"')) + rich += QLatin1String("""); + else + rich += plain.at(i); + } + return rich; +} + /*! \fn QString Qt::convertFromPlainText(const QString &plain, WhiteSpaceMode mode) @@ -2038,7 +2057,7 @@ void QTextHtmlExporter::emitAttribute(const char *attribute, const QString &valu html += QLatin1Char(' '); html += QLatin1String(attribute); html += QLatin1String("=\""); - html += value; + html += escapeXml(value); html += QLatin1Char('"'); } @@ -2302,12 +2321,12 @@ void QTextHtmlExporter::emitFontFamily(const QString &family) { html += QLatin1String(" font-family:"); - QLatin1Char quote('\''); - if (family.contains(quote)) - quote = QLatin1Char('\"'); + QLatin1String quote("\'"); + if (family.contains(QLatin1Char('\''))) + quote = QLatin1String("""); html += quote; - html += family; + html += escapeXml(family); html += quote; html += QLatin1Char(';'); } @@ -2341,13 +2360,13 @@ void QTextHtmlExporter::emitFragment(const QTextFragment &fragment) const QString name = format.anchorName(); if (!name.isEmpty()) { html += QLatin1String(""); } const QString href = format.anchorHref(); if (!href.isEmpty()) { html += QLatin1String(""); closeAnchor = true; } diff --git a/tests/auto/qtextdocument/qtextdocument.pro b/tests/auto/qtextdocument/qtextdocument.pro index d3ec45d..1e44a9c 100644 --- a/tests/auto/qtextdocument/qtextdocument.pro +++ b/tests/auto/qtextdocument/qtextdocument.pro @@ -1,4 +1,5 @@ load(qttest_p4) +QT += xml HEADERS += common.h SOURCES += tst_qtextdocument.cpp diff --git a/tests/auto/qtextdocument/tst_qtextdocument.cpp b/tests/auto/qtextdocument/tst_qtextdocument.cpp index f393393..452b683 100644 --- a/tests/auto/qtextdocument/tst_qtextdocument.cpp +++ b/tests/auto/qtextdocument/tst_qtextdocument.cpp @@ -59,6 +59,7 @@ #include #include #include +#include #include "common.h" @@ -732,7 +733,7 @@ void tst_QTextDocument::toHtml_data() cursor.insertText("Blah", fmt); QTest::newRow("font-family-with-quotes1") << QTextDocumentFragment(&doc) - << QString("

Blah

"); + << QString("

Blah

"); } { @@ -743,7 +744,7 @@ void tst_QTextDocument::toHtml_data() cursor.insertText("Blah", fmt); QTest::newRow("font-family-with-quotes2") << QTextDocumentFragment(&doc) - << QString("

Blah

"); + << QString("

Blah

"); } { @@ -974,6 +975,30 @@ void tst_QTextDocument::toHtml_data() { CREATE_DOC_AND_CURSOR(); + QTextCharFormat fmt; + fmt.setAnchor(true); + fmt.setAnchorHref("http://www.kde.org/?a=1&b=2"); + cursor.insertText("Blah", fmt); + + QTest::newRow("href anchor with &") << QTextDocumentFragment(&doc) + << QString("

Blah

"); + } + + { + CREATE_DOC_AND_CURSOR(); + + QTextCharFormat fmt; + fmt.setAnchor(true); + fmt.setAnchorHref("http://www.kde.org/?a='&b=\""); + cursor.insertText("Blah", fmt); + + QTest::newRow("href anchor with ' and \"") << QTextDocumentFragment(&doc) + << QString("

Blah

"); + } + + { + CREATE_DOC_AND_CURSOR(); + cursor.insertTable(2, 2); QTest::newRow("simpletable") << QTextDocumentFragment(&doc) @@ -1541,6 +1566,9 @@ void tst_QTextDocument::toHtml() QString output = doc->toHtml(); QCOMPARE(output, expectedOutput); + + QDomDocument document; + QVERIFY2(document.setContent(output), "Output was not valid XML"); } void tst_QTextDocument::toHtml2() -- cgit v0.12 From 1f3218e086778a0e324542a00937cd44e8e10c79 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Tue, 27 Oct 2009 20:37:27 +0100 Subject: Qt::escape(): also escape the quote (") This amend previous commit by removing the duplicate function. The quote need to be escaped in attributes. Reviewed-by: Thomas Zander --- dist/changes-4.6.0 | 3 +++ src/gui/text/qtextdocument.cpp | 27 +++++--------------------- tests/auto/qtextdocument/tst_qtextdocument.cpp | 26 +++++++++++++++++++++++-- 3 files changed, 32 insertions(+), 24 deletions(-) diff --git a/dist/changes-4.6.0 b/dist/changes-4.6.0 index 7f723da..ba2b051 100644 --- a/dist/changes-4.6.0 +++ b/dist/changes-4.6.0 @@ -35,6 +35,9 @@ information about a particular change. * Added QVariant::toFloat() and QVariant::toReal() * Added QVariant(float) constructor + - Qt::escape + * now escape the double quote (") + **************************************************************************** * Platform Specific Changes * **************************************************************************** diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp index 17981e3..6978b6c 100644 --- a/src/gui/text/qtextdocument.cpp +++ b/src/gui/text/qtextdocument.cpp @@ -140,7 +140,7 @@ bool Qt::mightBeRichText(const QString& text) /*! Converts the plain text string \a plain to a HTML string with - HTML metacharacters \c{<}, \c{>}, and \c{&} replaced by HTML + HTML metacharacters \c{<}, \c{>}, \c{&}, and \c{"} replaced by HTML entities. Example: @@ -162,23 +162,6 @@ QString Qt::escape(const QString& plain) rich += QLatin1String(">"); else if (plain.at(i) == QLatin1Char('&')) rich += QLatin1String("&"); - else - rich += plain.at(i); - } - return rich; -} - -static QString escapeXml(const QString &plain) -{ - QString rich; - rich.reserve(int(plain.length() * 1.1)); - for (int i = 0; i < plain.length(); ++i) { - if (plain.at(i) == QLatin1Char('<')) - rich += QLatin1String("<"); - else if (plain.at(i) == QLatin1Char('>')) - rich += QLatin1String(">"); - else if (plain.at(i) == QLatin1Char('&')) - rich += QLatin1String("&"); else if (plain.at(i) == QLatin1Char('"')) rich += QLatin1String("""); else @@ -2057,7 +2040,7 @@ void QTextHtmlExporter::emitAttribute(const char *attribute, const QString &valu html += QLatin1Char(' '); html += QLatin1String(attribute); html += QLatin1String("=\""); - html += escapeXml(value); + html += Qt::escape(value); html += QLatin1Char('"'); } @@ -2326,7 +2309,7 @@ void QTextHtmlExporter::emitFontFamily(const QString &family) quote = QLatin1String("""); html += quote; - html += escapeXml(family); + html += Qt::escape(family); html += quote; html += QLatin1Char(';'); } @@ -2360,13 +2343,13 @@ void QTextHtmlExporter::emitFragment(const QTextFragment &fragment) const QString name = format.anchorName(); if (!name.isEmpty()) { html += QLatin1String(""); } const QString href = format.anchorHref(); if (!href.isEmpty()) { html += QLatin1String(""); closeAnchor = true; } diff --git a/tests/auto/qtextdocument/tst_qtextdocument.cpp b/tests/auto/qtextdocument/tst_qtextdocument.cpp index 452b683..5237438 100644 --- a/tests/auto/qtextdocument/tst_qtextdocument.cpp +++ b/tests/auto/qtextdocument/tst_qtextdocument.cpp @@ -176,6 +176,8 @@ private slots: void testUndoBlocks(); void receiveCursorPositionChangedAfterContentsChange(); + void escape_data(); + void escape(); private: void backgroundImage_checkExpectedHtml(const QTextDocument &doc); @@ -577,7 +579,7 @@ void tst_QTextDocument::task240325() } void tst_QTextDocument::stylesheetFont_data() -{ +{ QTest::addColumn("stylesheet"); QTest::addColumn("font"); @@ -1566,7 +1568,7 @@ void tst_QTextDocument::toHtml() QString output = doc->toHtml(); QCOMPARE(output, expectedOutput); - + QDomDocument document; QVERIFY2(document.setContent(output), "Output was not valid XML"); } @@ -2680,5 +2682,25 @@ void tst_QTextDocument::receiveCursorPositionChangedAfterContentsChange() QCOMPARE(rec.first, QString("contentsChanged")); } +void tst_QTextDocument::escape_data() +{ + QTest::addColumn("original"); + QTest::addColumn("expected"); + + QTest::newRow("1") << "Hello World\n" << "Hello World\n"; + QTest::newRow("2") << "#include " << "#include <QtCore>"; + QTest::newRow("3") << "

plop -->

" + << "<p class="cool"><a href="http://example.com/?foo=bar&amp;bar=foo">plop --&gt; </a></p>"; + QTest::newRow("4") << QString::fromUtf8("<\320\222\321\201>") << QString::fromUtf8("<\320\222\321\201>"); +} + +void tst_QTextDocument::escape() +{ + QFETCH(QString, original); + QFETCH(QString, expected); + + QCOMPARE(Qt::escape(original), expected); +} + QTEST_MAIN(tst_QTextDocument) #include "tst_qtextdocument.moc" -- cgit v0.12 From a925ba1ab6316a155f2ac61f898c52f07a1340b4 Mon Sep 17 00:00:00 2001 From: Gabriel de Dietrich Date: Wed, 28 Oct 2009 09:58:18 +0100 Subject: QSplitter would not show previously collapsed widgets. After deleting or hiding the last non collapsed item in a QSplitter, none would be visible. We now check wether there is any non-hidden, collapsed widget, and set it to non-collapsed. Auto-test included. Task-number: QTBUG-4101 Reviewed-by: Olivier --- src/gui/widgets/qsplitter.cpp | 17 +++++++++++++++-- tests/auto/qsplitter/tst_qsplitter.cpp | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/gui/widgets/qsplitter.cpp b/src/gui/widgets/qsplitter.cpp index e3121ae..520a802 100644 --- a/src/gui/widgets/qsplitter.cpp +++ b/src/gui/widgets/qsplitter.cpp @@ -360,13 +360,26 @@ void QSplitterPrivate::recalc(bool update) before a hidden widget must be hidden. */ bool first = true; + bool allInvisible = n != 0; for (int i = 0; i < n ; ++i) { QSplitterLayoutStruct *s = list.at(i); - s->handle->setHidden(first || s->widget->isHidden()); - if (!s->widget->isHidden()) + bool widgetHidden = s->widget->isHidden(); + if (allInvisible && !widgetHidden && !s->collapsed) + allInvisible = false; + s->handle->setHidden(first || widgetHidden); + if (!widgetHidden) first = false; } + if (allInvisible) + for (int i = 0; i < n ; ++i) { + QSplitterLayoutStruct *s = list.at(i); + if (!s->widget->isHidden()) { + s->collapsed = false; + break; + } + } + int fi = 2 * q->frameWidth(); int maxl = fi; int minl = fi; diff --git a/tests/auto/qsplitter/tst_qsplitter.cpp b/tests/auto/qsplitter/tst_qsplitter.cpp index cf16421..b832f3a 100644 --- a/tests/auto/qsplitter/tst_qsplitter.cpp +++ b/tests/auto/qsplitter/tst_qsplitter.cpp @@ -102,6 +102,8 @@ private slots: void task187373_addAbstractScrollAreas(); void task187373_addAbstractScrollAreas_data(); void task169702_sizes(); + void taskQTBUG_4101_ensureOneNonCollapsedWidget_data(); + void taskQTBUG_4101_ensureOneNonCollapsedWidget(); private: void removeThirdWidget(); @@ -1281,6 +1283,8 @@ class MyFriendlySplitter : public QSplitter public: MyFriendlySplitter(QWidget *parent = 0) : QSplitter(parent) {} void setRubberBand(int pos) { QSplitter::setRubberBand(pos); } + + friend class tst_QSplitter; }; void tst_QSplitter::rubberBandNotInSplitter() @@ -1403,5 +1407,35 @@ void tst_QSplitter::task169702_sizes() QCOMPARE(testW->size().height(), testW->minimumSizeHint().height()); } +void tst_QSplitter::taskQTBUG_4101_ensureOneNonCollapsedWidget_data() +{ + QTest::addColumn("testingHide"); + + QTest::newRow("last non collapsed hidden") << true; + QTest::newRow("last non collapsed deleted") << false; +} + +void tst_QSplitter::taskQTBUG_4101_ensureOneNonCollapsedWidget() +{ + QFETCH(bool, testingHide); + + MyFriendlySplitter s; + QLabel *l; + for (int i = 0; i < 5; ++i) { + l = new QLabel(QString("Label ") + QChar('A' + i)); + l->setAlignment(Qt::AlignCenter); + s.addWidget(l); + s.moveSplitter(0, i); // Collapse all the labels except the last one. + } + + s.show(); + if (testingHide) + l->hide(); + else + delete l; + QTest::qWait(100); + QVERIFY(s.sizes().at(0) > 0); +} + QTEST_MAIN(tst_QSplitter) #include "tst_qsplitter.moc" -- cgit v0.12