/**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions ** contained in the either Technology Preview License Agreement or the ** Beta Release License Agreement. ** ** GNU Lesser General Public License Usage ** 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 version 1.0, 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. ** ** If you are unsure which license is appropriate for your use, please ** contact the sales department at http://www.qtsoftware.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" QT_FORWARD_DECLARE_CLASS(QTextDocument) //TESTED_CLASS= //TESTED_FILES= class tst_QTextDocument : public QObject { Q_OBJECT public: tst_QTextDocument(); virtual ~tst_QTextDocument(); public slots: void init(); void cleanup(); private slots: void getSetCheck(); void isEmpty(); void find_data(); void find(); void find2(); void findWithRegExp_data(); void findWithRegExp(); void findMultiple(); void basicIsModifiedChecks(); void moreIsModified(); void isModified2(); void isModified3(); void isModified4(); void noundo_basicIsModifiedChecks(); void noundo_moreIsModified(); void noundo_isModified2(); void noundo_isModified3(); void mightBeRichText(); void toHtml_data(); void toHtml(); void toHtml2(); void setFragmentMarkersInHtmlExport(); void toHtmlBodyBgColor(); void toHtmlRootFrameProperties(); void capitalizationHtmlInExport(); void wordspacingHtmlExport(); void cursorPositionChanged(); void cursorPositionChangedOnSetText(); void textFrameIterator(); void codecForHtml(); void markContentsDirty(); void clonePreservesMetaInformation(); void clonePreservesPageSize(); void clonePreservesPageBreakPolicies(); void clonePreservesDefaultFont(); void clonePreservesRootFrameFormat(); void clonePreservesResources(); void clonePreservesUserStates(); void clonePreservesIndentWidth(); void blockCount(); void defaultStyleSheet(); void resolvedFontInEmptyFormat(); void defaultRootFrameMargin(); void clearResources(); void setPlainText(); void toPlainText(); void deleteTextObjectsOnClear(); void maximumBlockCount(); void adjustSize(); void initialUserData(); void html_defaultFont(); void blockCountChanged(); void nonZeroDocumentLengthOnClear(); void setTextPreservesUndoRedoEnabled(); void firstLast(); void backgroundImage_toHtml(); void backgroundImage_toHtml2(); void backgroundImage_clone(); void backgroundImage_copy(); void documentCleanup(); void characterAt(); void revisions(); void testUndoCommandAdded(); void testUndoBlocks(); void receiveCursorPositionChangedAfterContentsChange(); private: void backgroundImage_checkExpectedHtml(const QTextDocument &doc); QTextDocument *doc; QTextCursor cursor; QFont defaultFont; QString htmlHead; QString htmlTail; }; class MyAbstractTextDocumentLayout : public QAbstractTextDocumentLayout { public: MyAbstractTextDocumentLayout(QTextDocument *doc) : QAbstractTextDocumentLayout(doc) {} void draw(QPainter *, const PaintContext &) {} int hitTest(const QPointF &, Qt::HitTestAccuracy) const { return 0; } int pageCount() const { return 0; } QSizeF documentSize() const { return QSizeF(); } QRectF frameBoundingRect(QTextFrame *) const { return QRectF(); } QRectF blockBoundingRect(const QTextBlock &) const { return QRectF(); } void documentChanged(int, int, int) {} }; // Testing get/set functions void tst_QTextDocument::getSetCheck() { QTextDocument obj1; // QAbstractTextDocumentLayout * QTextDocument::documentLayout() // void QTextDocument::setDocumentLayout(QAbstractTextDocumentLayout *) QPointer var1 = new MyAbstractTextDocumentLayout(0); obj1.setDocumentLayout(var1); QCOMPARE(static_cast(var1), obj1.documentLayout()); obj1.setDocumentLayout((QAbstractTextDocumentLayout *)0); QVERIFY(var1.isNull()); QVERIFY(obj1.documentLayout()); // bool QTextDocument::useDesignMetrics() // void QTextDocument::setUseDesignMetrics(bool) obj1.setUseDesignMetrics(false); QCOMPARE(false, obj1.useDesignMetrics()); obj1.setUseDesignMetrics(true); QCOMPARE(true, obj1.useDesignMetrics()); } tst_QTextDocument::tst_QTextDocument() { QImage img(16, 16, QImage::Format_ARGB32_Premultiplied); img.save("foo.png"); } tst_QTextDocument::~tst_QTextDocument() { QFile::remove(QLatin1String("foo.png")); } void tst_QTextDocument::init() { doc = new QTextDocument; cursor = QTextCursor(doc); defaultFont = QFont(); htmlHead = QString("\n" "" "\n"); htmlHead = htmlHead.arg(defaultFont.family()).arg(defaultFont.pointSizeF()).arg(defaultFont.weight() * 8).arg((defaultFont.italic() ? "italic" : "normal")); htmlTail = QString(""); } void tst_QTextDocument::cleanup() { cursor = QTextCursor(); delete doc; doc = 0; } void tst_QTextDocument::isEmpty() { QVERIFY(doc->isEmpty()); } void tst_QTextDocument::find_data() { QTest::addColumn("haystack"); QTest::addColumn("needle"); QTest::addColumn("flags"); QTest::addColumn("from"); QTest::addColumn("anchor"); QTest::addColumn("position"); QTest::newRow("1") << "Hello World" << "World" << int(QTextDocument::FindCaseSensitively) << 0 << 6 << 11; QTest::newRow("2") << QString::fromAscii("Hello") + QString(QChar::ParagraphSeparator) + QString::fromAscii("World") << "World" << int(QTextDocument::FindCaseSensitively) << 1 << 6 << 11; QTest::newRow("3") << QString::fromAscii("Hello") + QString(QChar::ParagraphSeparator) + QString::fromAscii("World") << "Hello" << int(QTextDocument::FindCaseSensitively | QTextDocument::FindBackward) << 10 << 0 << 5; QTest::newRow("4wholewords") << QString::fromAscii("Hello Blah World") << "Blah" << int(QTextDocument::FindWholeWords) << 0 << 6 << 10; QTest::newRow("5wholewords") << QString::fromAscii("HelloBlahWorld") << "Blah" << int(QTextDocument::FindWholeWords) << 0 << -1 << -1; QTest::newRow("6wholewords") << QString::fromAscii("HelloBlahWorld Blah Hah") << "Blah" << int(QTextDocument::FindWholeWords) << 0 << 15 << 19; QTest::newRow("7wholewords") << QString::fromAscii("HelloBlahWorld Blah Hah") << "Blah" << int(QTextDocument::FindWholeWords | QTextDocument::FindBackward) << 23 << 15 << 19; QTest::newRow("8wholewords") << QString::fromAscii("Hello: World\n") << "orld" << int(QTextDocument::FindWholeWords) << 0 << -1 << -1; QTest::newRow("across-paragraphs") << QString::fromAscii("First Parag\nSecond Parag with a lot more text") << "Parag" << int(QTextDocument::FindBackward) << 15 << 6 << 11; QTest::newRow("nbsp") << "Hello" + QString(QChar(QChar::Nbsp)) +"World" << " " << int(QTextDocument::FindCaseSensitively) << 0 << 5 << 6; } void tst_QTextDocument::find() { QFETCH(QString, haystack); QFETCH(QString, needle); QFETCH(int, flags); QFETCH(int, from); QFETCH(int, anchor); QFETCH(int, position); cursor.insertText(haystack); cursor = doc->find(needle, from, QTextDocument::FindFlags(flags)); if (anchor != -1) { QCOMPARE(cursor.anchor(), anchor); QCOMPARE(cursor.position(), position); } else { QVERIFY(cursor.isNull()); } //search using a regular expression QRegExp expr(needle); expr.setPatternSyntax(QRegExp::FixedString); QTextDocument::FindFlags flg(flags); expr.setCaseSensitivity((flg & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive); cursor = doc->find(expr, from, flg); if (anchor != -1) { QCOMPARE(cursor.anchor(), anchor); QCOMPARE(cursor.position(), position); } else { QVERIFY(cursor.isNull()); } } void tst_QTextDocument::findWithRegExp_data() { QTest::addColumn("haystack"); QTest::addColumn("needle"); QTest::addColumn("flags"); QTest::addColumn("from"); QTest::addColumn("anchor"); QTest::addColumn("position"); // match integers 0 to 99 QTest::newRow("1") << "23" << "^\\d\\d?$" << int(QTextDocument::FindCaseSensitively) << 0 << 0 << 2; // match ampersands but not & QTest::newRow("2") << "His & hers & theirs" << "&(?!amp;)"<< int(QTextDocument::FindCaseSensitively) << 0 << 15 << 16; //backward search QTest::newRow("3") << QString::fromAscii("HelloBlahWorld Blah Hah") << "h" << int(QTextDocument::FindBackward) << 18 << 8 << 9; } void tst_QTextDocument::findWithRegExp() { QFETCH(QString, haystack); QFETCH(QString, needle); QFETCH(int, flags); QFETCH(int, from); QFETCH(int, anchor); QFETCH(int, position); cursor.insertText(haystack); //search using a regular expression QRegExp expr(needle); QTextDocument::FindFlags flg(flags); expr.setCaseSensitivity((flg & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive); cursor = doc->find(expr, from, flg); if (anchor != -1) { QCOMPARE(cursor.anchor(), anchor); QCOMPARE(cursor.position(), position); } else { QVERIFY(cursor.isNull()); } } void tst_QTextDocument::find2() { doc->setPlainText("aaa"); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor); QTextCursor hit = doc->find("a", cursor); QCOMPARE(hit.position(), 2); QCOMPARE(hit.anchor(), 1); } void tst_QTextDocument::findMultiple() { const QString text("foo bar baz foo bar baz"); doc->setPlainText(text); cursor.movePosition(QTextCursor::Start); cursor = doc->find("bar", cursor); QCOMPARE(cursor.selectionStart(), text.indexOf("bar")); QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3); cursor = doc->find("bar", cursor); QCOMPARE(cursor.selectionStart(), text.lastIndexOf("bar")); QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3); cursor.movePosition(QTextCursor::End); cursor = doc->find("bar", cursor, QTextDocument::FindBackward); QCOMPARE(cursor.selectionStart(), text.lastIndexOf("bar")); QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3); cursor = doc->find("bar", cursor, QTextDocument::FindBackward); QCOMPARE(cursor.selectionStart(), text.indexOf("bar")); QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3); QRegExp expr("bar"); expr.setPatternSyntax(QRegExp::FixedString); cursor.movePosition(QTextCursor::End); cursor = doc->find(expr, cursor, QTextDocument::FindBackward); QCOMPARE(cursor.selectionStart(), text.lastIndexOf("bar")); QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3); cursor = doc->find(expr, cursor, QTextDocument::FindBackward); QCOMPARE(cursor.selectionStart(), text.indexOf("bar")); QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3); cursor.movePosition(QTextCursor::Start); cursor = doc->find(expr, cursor); QCOMPARE(cursor.selectionStart(), text.indexOf("bar")); QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3); cursor = doc->find(expr, cursor); QCOMPARE(cursor.selectionStart(), text.lastIndexOf("bar")); QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3); } void tst_QTextDocument::basicIsModifiedChecks() { QSignalSpy spy(doc, SIGNAL(modificationChanged(bool))); QVERIFY(!doc->isModified()); cursor.insertText("Hello World"); QVERIFY(doc->isModified()); QCOMPARE(spy.count(), 1); QVERIFY(spy.takeFirst().at(0).toBool()); doc->undo(); QVERIFY(!doc->isModified()); QCOMPARE(spy.count(), 1); QVERIFY(!spy.takeFirst().at(0).toBool()); doc->redo(); QVERIFY(doc->isModified()); QCOMPARE(spy.count(), 1); QVERIFY(spy.takeFirst().at(0).toBool()); } void tst_QTextDocument::moreIsModified() { QVERIFY(!doc->isModified()); cursor.insertText("Hello"); QVERIFY(doc->isModified()); doc->undo(); QVERIFY(!doc->isModified()); cursor.insertText("Hello"); doc->undo(); QVERIFY(!doc->isModified()); } void tst_QTextDocument::isModified2() { // reported on qt4-preview-feedback QVERIFY(!doc->isModified()); cursor.insertText("Hello"); QVERIFY(doc->isModified()); doc->setModified(false); QVERIFY(!doc->isModified()); cursor.insertText("Hello"); QVERIFY(doc->isModified()); } void tst_QTextDocument::isModified3() { QVERIFY(!doc->isModified()); doc->setUndoRedoEnabled(false); doc->setUndoRedoEnabled(true); cursor.insertText("Hello"); QVERIFY(doc->isModified()); doc->undo(); QVERIFY(!doc->isModified()); } void tst_QTextDocument::isModified4() { QVERIFY(!doc->isModified()); cursor.insertText("Hello"); cursor.insertText("World"); doc->setModified(false); QVERIFY(!doc->isModified()); cursor.insertText("Again"); QVERIFY(doc->isModified()); doc->undo(); QVERIFY(!doc->isModified()); doc->undo(); QVERIFY(doc->isModified()); doc->redo(); QVERIFY(!doc->isModified()); doc->redo(); QVERIFY(doc->isModified()); doc->undo(); QVERIFY(!doc->isModified()); doc->undo(); QVERIFY(doc->isModified()); //task 197769 cursor.insertText("Hello"); QVERIFY(doc->isModified()); } void tst_QTextDocument::noundo_basicIsModifiedChecks() { doc->setUndoRedoEnabled(false); QSignalSpy spy(doc, SIGNAL(modificationChanged(bool))); QVERIFY(!doc->isModified()); cursor.insertText("Hello World"); QVERIFY(doc->isModified()); QCOMPARE(spy.count(), 1); QVERIFY(spy.takeFirst().at(0).toBool()); doc->undo(); QVERIFY(doc->isModified()); QCOMPARE(spy.count(), 0); doc->redo(); QVERIFY(doc->isModified()); QCOMPARE(spy.count(), 0); } void tst_QTextDocument::noundo_moreIsModified() { doc->setUndoRedoEnabled(false); QVERIFY(!doc->isModified()); cursor.insertText("Hello"); QVERIFY(doc->isModified()); doc->undo(); QVERIFY(doc->isModified()); cursor.insertText("Hello"); doc->undo(); QVERIFY(doc->isModified()); } void tst_QTextDocument::noundo_isModified2() { // reported on qt4-preview-feedback QVERIFY(!doc->isModified()); cursor.insertText("Hello"); QVERIFY(doc->isModified()); doc->setModified(false); QVERIFY(!doc->isModified()); cursor.insertText("Hello"); QVERIFY(doc->isModified()); } void tst_QTextDocument::noundo_isModified3() { doc->setUndoRedoEnabled(false); QVERIFY(!doc->isModified()); cursor.insertText("Hello"); QVERIFY(doc->isModified()); doc->undo(); QVERIFY(doc->isModified()); } void tst_QTextDocument::mightBeRichText() { const char qtDocuHeader[] = "\n" "\n" ""; QVERIFY(Qt::mightBeRichText(QString::fromLatin1(qtDocuHeader))); } Q_DECLARE_METATYPE(QTextDocumentFragment) #define CREATE_DOC_AND_CURSOR() \ QTextDocument doc; \ doc.setDefaultFont(defaultFont); \ QTextCursor cursor(&doc); void tst_QTextDocument::toHtml_data() { QTest::addColumn("input"); QTest::addColumn("expectedOutput"); { CREATE_DOC_AND_CURSOR(); cursor.insertText("Blah"); QTest::newRow("simple") << QTextDocumentFragment(&doc) << QString("

Blah

"); } { CREATE_DOC_AND_CURSOR(); cursor.insertText("&<>"); QTest::newRow("entities") << QTextDocumentFragment(&doc) << QString("

&<>

"); } { CREATE_DOC_AND_CURSOR(); QTextCharFormat fmt; fmt.setFontFamily("Times"); cursor.insertText("Blah", fmt); QTest::newRow("font-family") << QTextDocumentFragment(&doc) << QString("

Blah

"); } { CREATE_DOC_AND_CURSOR(); QTextCharFormat fmt; fmt.setFontFamily("Foo's Family"); cursor.insertText("Blah", fmt); QTest::newRow("font-family-with-quotes1") << QTextDocumentFragment(&doc) << QString("

Blah

"); } { CREATE_DOC_AND_CURSOR(); QTextCharFormat fmt; fmt.setFontFamily("Foo\"s Family"); cursor.insertText("Blah", fmt); QTest::newRow("font-family-with-quotes2") << QTextDocumentFragment(&doc) << QString("

Blah

"); } { CREATE_DOC_AND_CURSOR(); QTextBlockFormat fmt; fmt.setNonBreakableLines(true); cursor.insertBlock(fmt); cursor.insertText("Blah"); QTest::newRow("pre") << QTextDocumentFragment(&doc) << QString("EMPTYBLOCK") + QString("
Blah
"); } { CREATE_DOC_AND_CURSOR(); QTextCharFormat fmt; fmt.setFontPointSize(40); cursor.insertText("Blah", fmt); QTest::newRow("font-size") << QTextDocumentFragment(&doc) << QString("

Blah

"); } { CREATE_DOC_AND_CURSOR(); QTextCharFormat fmt; fmt.setProperty(QTextFormat::FontSizeIncrement, 2); cursor.insertText("Blah", fmt); QTest::newRow("logical-font-size") << QTextDocumentFragment(&doc) << QString("

Blah

"); } { CREATE_DOC_AND_CURSOR(); cursor.insertText("Foo"); QTextCharFormat fmt; fmt.setFontPointSize(40); cursor.insertBlock(QTextBlockFormat(), fmt); fmt.clearProperty(QTextFormat::FontPointSize); cursor.insertText("Blub", fmt); QTest::newRow("no-font-size") << QTextDocumentFragment(&doc) << QString("

Foo

\n

Blub

"); } { CREATE_DOC_AND_CURSOR(); QTextBlockFormat fmt; fmt.setLayoutDirection(Qt::RightToLeft); cursor.insertBlock(fmt); cursor.insertText("Blah"); QTest::newRow("rtl") << QTextDocumentFragment(&doc) << QString("EMPTYBLOCK") + QString("

Blah

"); } { CREATE_DOC_AND_CURSOR(); QTextBlockFormat fmt; fmt.setAlignment(Qt::AlignJustify); cursor.insertBlock(fmt); cursor.insertText("Blah"); QTest::newRow("blockalign") << QTextDocumentFragment(&doc) << QString("EMPTYBLOCK") + QString("

Blah

"); } { CREATE_DOC_AND_CURSOR(); QTextBlockFormat fmt; fmt.setAlignment(Qt::AlignCenter); cursor.insertBlock(fmt); cursor.insertText("Blah"); QTest::newRow("blockalign2") << QTextDocumentFragment(&doc) << QString("EMPTYBLOCK") + QString("

Blah

"); } { CREATE_DOC_AND_CURSOR(); QTextBlockFormat fmt; fmt.setAlignment(Qt::AlignRight | Qt::AlignAbsolute); cursor.insertBlock(fmt); cursor.insertText("Blah"); QTest::newRow("blockalign3") << QTextDocumentFragment(&doc) << QString("EMPTYBLOCK") + QString("

Blah

"); } { CREATE_DOC_AND_CURSOR(); QTextBlockFormat fmt; fmt.setBackground(QColor("#0000ff")); cursor.insertBlock(fmt); cursor.insertText("Blah"); QTest::newRow("bgcolor") << QTextDocumentFragment(&doc) << QString("EMPTYBLOCK") + QString("

Blah

"); } { CREATE_DOC_AND_CURSOR(); QTextCharFormat fmt; fmt.setFontWeight(40); cursor.insertText("Blah", fmt); QTest::newRow("font-weight") << QTextDocumentFragment(&doc) << QString("

Blah

"); } { CREATE_DOC_AND_CURSOR(); QTextCharFormat fmt; fmt.setFontItalic(true); cursor.insertText("Blah", fmt); QTest::newRow("font-italic") << QTextDocumentFragment(&doc) << QString("

Blah

"); } { CREATE_DOC_AND_CURSOR(); QTextCharFormat fmt; fmt.setFontUnderline(true); fmt.setFontOverline(false); cursor.insertText("Blah", fmt); QTest::newRow("text-decoration-1") << QTextDocumentFragment(&doc) << QString("

Blah

"); } { CREATE_DOC_AND_CURSOR(); QTextCharFormat fmt; fmt.setForeground(QColor("#00ff00")); cursor.insertText("Blah", fmt); QTest::newRow("color") << QTextDocumentFragment(&doc) << QString("

Blah

"); } { CREATE_DOC_AND_CURSOR(); QTextCharFormat fmt; fmt.setBackground(QColor("#00ff00")); cursor.insertText("Blah", fmt); QTest::newRow("span-bgcolor") << QTextDocumentFragment(&doc) << QString("

Blah

"); } { CREATE_DOC_AND_CURSOR(); QTextCharFormat fmt; fmt.setVerticalAlignment(QTextCharFormat::AlignSubScript); cursor.insertText("Blah", fmt); QTest::newRow("valign-sub") << QTextDocumentFragment(&doc) << QString("

Blah

"); } { CREATE_DOC_AND_CURSOR(); QTextCharFormat fmt; fmt.setVerticalAlignment(QTextCharFormat::AlignSuperScript); cursor.insertText("Blah", fmt); QTest::newRow("valign-super") << QTextDocumentFragment(&doc) << QString("

Blah

"); } { CREATE_DOC_AND_CURSOR(); QTextCharFormat fmt; fmt.setAnchor(true); fmt.setAnchorName("blub"); cursor.insertText("Blah", fmt); QTest::newRow("named anchor") << QTextDocumentFragment(&doc) << QString("

Blah

"); } { CREATE_DOC_AND_CURSOR(); QTextCharFormat fmt; fmt.setAnchor(true); fmt.setAnchorHref("http://www.kde.org/"); cursor.insertText("Blah", fmt); QTest::newRow("href anchor") << QTextDocumentFragment(&doc) << QString("

Blah

"); } { CREATE_DOC_AND_CURSOR(); cursor.insertTable(2, 2); QTest::newRow("simpletable") << QTextDocumentFragment(&doc) << QString("" "\n\n\n" "\n\n\n" "
"); } { CREATE_DOC_AND_CURSOR(); QTextTable *table = cursor.insertTable(1, 4); table->mergeCells(0, 0, 1, 2); table->mergeCells(0, 2, 1, 2); QTest::newRow("tablespans") << QTextDocumentFragment(&doc) << QString("" "\n\n\n" "
"); } { CREATE_DOC_AND_CURSOR(); QTextTableFormat fmt; fmt.setBorder(1); fmt.setCellSpacing(3); fmt.setCellPadding(3); fmt.setBackground(QColor("#ff00ff")); fmt.setWidth(QTextLength(QTextLength::PercentageLength, 50)); fmt.setAlignment(Qt::AlignHCenter); fmt.setPosition(QTextFrameFormat::FloatRight); cursor.insertTable(2, 2, fmt); QTest::newRow("tableattrs") << QTextDocumentFragment(&doc) << QString("" "\n\n\n" "\n\n\n" "
"); } { CREATE_DOC_AND_CURSOR(); QTextTableFormat fmt; fmt.setBorder(1); fmt.setCellSpacing(3); fmt.setCellPadding(3); fmt.setBackground(QColor("#ff00ff")); fmt.setWidth(QTextLength(QTextLength::PercentageLength, 50)); fmt.setAlignment(Qt::AlignHCenter); fmt.setPosition(QTextFrameFormat::FloatRight); fmt.setLeftMargin(25); fmt.setBottomMargin(35); cursor.insertTable(2, 2, fmt); QTest::newRow("tableattrs2") << QTextDocumentFragment(&doc) << QString("" "\n\n\n" "\n\n\n" "
"); } { CREATE_DOC_AND_CURSOR(); QTextTableFormat fmt; fmt.setHeaderRowCount(2); cursor.insertTable(4, 2, fmt); QTest::newRow("tableheader") << QTextDocumentFragment(&doc) << QString("" "\n\n\n" "\n\n\n" "\n\n\n" "\n\n\n" "
"); } { CREATE_DOC_AND_CURSOR(); QTextTable *table = cursor.insertTable(2, 2); QTextTable *subTable = table->cellAt(0, 1).firstCursorPosition().insertTable(1, 1); subTable->cellAt(0, 0).firstCursorPosition().insertText("Hey"); QTest::newRow("nestedtable") << QTextDocumentFragment(&doc) << QString("" "\n\n\n" "\n\n\n" "
\n\n\n
\n

Hey

"); } { CREATE_DOC_AND_CURSOR(); QTextTableFormat fmt; QVector widths; widths.append(QTextLength()); widths.append(QTextLength(QTextLength::PercentageLength, 30)); widths.append(QTextLength(QTextLength::FixedLength, 40)); fmt.setColumnWidthConstraints(widths); cursor.insertTable(1, 3, fmt); QTest::newRow("colwidths") << QTextDocumentFragment(&doc) << QString("" "\n\n\n\n" "
"); } // ### rowspan/colspan tests, once texttable api for that is back again // { CREATE_DOC_AND_CURSOR(); QTextTable *table = cursor.insertTable(1, 1); QTextCursor cellCurs = table->cellAt(0, 0).firstCursorPosition(); QTextCharFormat fmt; fmt.setBackground(QColor("#ffffff")); cellCurs.mergeBlockCharFormat(fmt); QTest::newRow("cellproperties") << QTextDocumentFragment(&doc) << QString("" "\n\n" "
"); } { CREATE_DOC_AND_CURSOR(); // ### fixme: use programmatic api as soon as we can create floats through it const char html[] = "BlahBlubb"; QTest::newRow("image") << QTextDocumentFragment::fromHtml(QString::fromLatin1(html)) << QString("

BlahBlubb

"); } { CREATE_DOC_AND_CURSOR(); QTextImageFormat fmt; fmt.setName("foo"); fmt.setVerticalAlignment(QTextCharFormat::AlignMiddle); cursor.insertImage(fmt); QTest::newRow("image-malign") << QTextDocumentFragment(&doc) << QString("

"); } { CREATE_DOC_AND_CURSOR(); QTextImageFormat fmt; fmt.setName("foo"); fmt.setVerticalAlignment(QTextCharFormat::AlignTop); cursor.insertImage(fmt); QTest::newRow("image-malign") << QTextDocumentFragment(&doc) << QString("

"); } { CREATE_DOC_AND_CURSOR(); QTextImageFormat fmt; fmt.setName("foo"); cursor.insertImage(fmt); cursor.insertImage(fmt); QTest::newRow("2images") << QTextDocumentFragment(&doc) << QString("

"); } { CREATE_DOC_AND_CURSOR(); QString txt = QLatin1String("Blah"); txt += QChar::LineSeparator; txt += QLatin1String("Bar"); cursor.insertText(txt); QTest::newRow("linebreaks") << QTextDocumentFragment(&doc) << QString("

Blah
Bar

"); } { CREATE_DOC_AND_CURSOR(); QTextBlockFormat fmt; fmt.setTopMargin(10); fmt.setBottomMargin(20); fmt.setLeftMargin(30); fmt.setRightMargin(40); cursor.insertBlock(fmt); cursor.insertText("Blah"); QTest::newRow("blockmargins") << QTextDocumentFragment(&doc) << QString("EMPTYBLOCK") + QString("

Blah

"); } { CREATE_DOC_AND_CURSOR(); QTextList *list = cursor.insertList(QTextListFormat::ListDisc); cursor.insertText("Blubb"); cursor.insertBlock(); cursor.insertText("Blah"); QCOMPARE(list->count(), 2); QTest::newRow("lists") << QTextDocumentFragment(&doc) << QString("EMPTYBLOCK") + QString("
  • Blubb
  • \n
  • Blah
"); } { CREATE_DOC_AND_CURSOR(); QTextList *list = cursor.insertList(QTextListFormat::ListDisc); cursor.insertText("Blubb"); cursor.insertBlock(); QTextCharFormat blockCharFmt; blockCharFmt.setForeground(QColor("#0000ff")); cursor.mergeBlockCharFormat(blockCharFmt); QTextCharFormat fmt; fmt.setForeground(QColor("#ff0000")); cursor.insertText("Blah", fmt); QCOMPARE(list->count(), 2); QTest::newRow("charfmt-for-list-item") << QTextDocumentFragment(&doc) << QString("EMPTYBLOCK") + QString("
  • Blubb
  • \n
  • Blah
"); } { CREATE_DOC_AND_CURSOR(); QTextBlockFormat fmt; fmt.setIndent(3); fmt.setTextIndent(30); cursor.insertBlock(fmt); cursor.insertText("Test"); QTest::newRow("block-indent") << QTextDocumentFragment(&doc) << QString("EMPTYBLOCK") + QString("

Test

"); } { CREATE_DOC_AND_CURSOR(); QTextListFormat fmt; fmt.setStyle(QTextListFormat::ListDisc); fmt.setIndent(4); cursor.insertList(fmt); cursor.insertText("Blah"); QTest::newRow("list-indent") << QTextDocumentFragment(&doc) << QString("EMPTYBLOCK") + QString("
  • Blah
"); } { CREATE_DOC_AND_CURSOR(); cursor.insertBlock(); QTest::newRow("emptyblock") << QTextDocumentFragment(&doc) // after insertBlock() we /do/ have two blocks in the document, so also expect // these in the html output << QString("EMPTYBLOCK") + QString("EMPTYBLOCK"); } { CREATE_DOC_AND_CURSOR(); // if you press enter twice in an empty textedit and then insert 'Test' // you actually get three visible paragraphs, two empty leading ones and // a third with the actual text. the corresponding html representation // therefore should also contain three paragraphs. cursor.insertBlock(); QTextCharFormat fmt; fmt.setForeground(QColor("#00ff00")); fmt.setProperty(QTextFormat::FontSizeIncrement, 1); cursor.mergeBlockCharFormat(fmt); fmt.setProperty(QTextFormat::FontSizeIncrement, 2); cursor.insertText("Test", fmt); QTest::newRow("blockcharfmt") << QTextDocumentFragment(&doc) << QString("EMPTYBLOCK

Test

"); } { CREATE_DOC_AND_CURSOR(); QTextCharFormat fmt; fmt.setForeground(QColor("#00ff00")); cursor.setBlockCharFormat(fmt); fmt.setForeground(QColor("#0000ff")); cursor.insertText("Test", fmt); QTest::newRow("blockcharfmt2") << QTextDocumentFragment(&doc) << QString("

Test

"); } { QTest::newRow("horizontal-ruler") << QTextDocumentFragment::fromHtml("
") << QString("EMPTYBLOCK") + QString("
"); } { QTest::newRow("horizontal-ruler-with-width") << QTextDocumentFragment::fromHtml("
") << QString("EMPTYBLOCK") + QString("
"); } { CREATE_DOC_AND_CURSOR(); QTextFrame *mainFrame = cursor.currentFrame(); QTextFrameFormat ffmt; ffmt.setBorder(1); ffmt.setPosition(QTextFrameFormat::FloatRight); ffmt.setMargin(2); ffmt.setWidth(100); ffmt.setHeight(50); ffmt.setBackground(QColor("#00ff00")); cursor.insertFrame(ffmt); cursor.insertText("Hello World"); cursor = mainFrame->lastCursorPosition(); QTest::newRow("frame") << QTextDocumentFragment(&doc) << QString("\n\n
\n

Hello World

"); } { CREATE_DOC_AND_CURSOR(); QTextCharFormat fmt; fmt.setForeground(QColor("#00ff00")); // fmt.setBackground(QColor("#0000ff")); cursor.setBlockCharFormat(fmt); fmt.setForeground(QBrush()); // fmt.setBackground(QBrush()); cursor.insertText("Test", fmt); // QTest::newRow("nostylebrush") << QTextDocumentFragment(&doc) << QString("

Test

"); QTest::newRow("nostylebrush") << QTextDocumentFragment(&doc) << QString("

Test

"); } { CREATE_DOC_AND_CURSOR(); QTextTable *table = cursor.insertTable(2, 2); table->mergeCells(0, 0, 1, 2); QTextTableFormat fmt = table->format(); QVector widths; widths.append(QTextLength(QTextLength::FixedLength, 20)); widths.append(QTextLength(QTextLength::FixedLength, 40)); fmt.setColumnWidthConstraints(widths); table->setFormat(fmt); QTest::newRow("mergedtablecolwidths") << QTextDocumentFragment(&doc) << QString("" "\n\n" "\n\n\n" "
"); } { CREATE_DOC_AND_CURSOR(); QTextCharFormat fmt; cursor.insertText("Blah\nGreen yellow green"); cursor.setPosition(0); cursor.setPosition(23, QTextCursor::KeepAnchor); fmt.setBackground(Qt::green); cursor.mergeCharFormat(fmt); cursor.clearSelection(); cursor.setPosition(11); cursor.setPosition(17, QTextCursor::KeepAnchor); fmt.setBackground(Qt::yellow); cursor.mergeCharFormat(fmt); cursor.clearSelection(); QTest::newRow("multiparagraph-bgcolor") << QTextDocumentFragment(&doc) << QString("

Blah

\n" "

Green " "yellow" " green

"); } { CREATE_DOC_AND_CURSOR(); QTextBlockFormat fmt; fmt.setBackground(QColor("#0000ff")); cursor.insertBlock(fmt); QTextCharFormat charfmt; charfmt.setBackground(QColor("#0000ff")); cursor.insertText("Blah", charfmt); QTest::newRow("nospan-bgcolor") << QTextDocumentFragment(&doc) << QString("EMPTYBLOCK") + QString("

Blah

"); } { CREATE_DOC_AND_CURSOR(); QTextTable *table = cursor.insertTable(2, 2); QTextCharFormat fmt = table->cellAt(0, 0).format(); fmt.setVerticalAlignment(QTextCharFormat::AlignMiddle); table->cellAt(0, 0).setFormat(fmt); fmt = table->cellAt(0, 1).format(); fmt.setVerticalAlignment(QTextCharFormat::AlignTop); table->cellAt(0, 1).setFormat(fmt); fmt = table->cellAt(1, 0).format(); fmt.setVerticalAlignment(QTextCharFormat::AlignBottom); table->cellAt(1, 0).setFormat(fmt); table->cellAt(0, 0).firstCursorPosition().insertText("Blah"); QTest::newRow("table-vertical-alignment") << QTextDocumentFragment(&doc) << QString("" "\n\n" "\n" "\n\n" "\n" "
\n" "

Blah

"); } { CREATE_DOC_AND_CURSOR(); QTextTable *table = cursor.insertTable(2, 2); QTextTableCellFormat fmt = table->cellAt(0, 0).format().toTableCellFormat(); fmt.setLeftPadding(1); table->cellAt(0, 0).setFormat(fmt); fmt = table->cellAt(0, 1).format().toTableCellFormat(); fmt.setRightPadding(1); table->cellAt(0, 1).setFormat(fmt); fmt = table->cellAt(1, 0).format().toTableCellFormat(); fmt.setTopPadding(1); table->cellAt(1, 0).setFormat(fmt); fmt = table->cellAt(1, 1).format().toTableCellFormat(); fmt.setBottomPadding(1); table->cellAt(1, 1).setFormat(fmt); table->cellAt(0, 0).firstCursorPosition().insertText("Blah"); QTest::newRow("table-cell-paddings") << QTextDocumentFragment(&doc) << QString("" "\n\n" "\n" "\n\n" "\n" "
\n" "

Blah

"); } { CREATE_DOC_AND_CURSOR(); QTextTableFormat fmt; fmt.setBorderBrush(QColor("#0000ff")); fmt.setBorderStyle(QTextFrameFormat::BorderStyle_Solid); cursor.insertTable(2, 2, fmt); QTest::newRow("tableborder") << QTextDocumentFragment(&doc) << QString("" "\n\n\n" "\n\n\n" "
"); } { CREATE_DOC_AND_CURSOR(); cursor.insertBlock(); cursor.insertText("Foo"); cursor.block().setUserState(42); QTest::newRow("userstate") << QTextDocumentFragment(&doc) << QString("EMPTYBLOCK") + QString("

Foo

"); } { CREATE_DOC_AND_CURSOR(); QTextBlockFormat blockFmt; blockFmt.setPageBreakPolicy(QTextFormat::PageBreak_AlwaysBefore); cursor.insertBlock(blockFmt); cursor.insertText("Foo"); blockFmt.setPageBreakPolicy(QTextFormat::PageBreak_AlwaysBefore | QTextFormat::PageBreak_AlwaysAfter); cursor.insertBlock(blockFmt); cursor.insertText("Bar"); QTextTableFormat tableFmt; tableFmt.setPageBreakPolicy(QTextFormat::PageBreak_AlwaysAfter); cursor.insertTable(1, 1, tableFmt); QTest::newRow("pagebreak") << QTextDocumentFragment(&doc) << QString("EMPTYBLOCK") + QString("

Foo

" "\n

Bar

" "\n\n\n
"); } } void tst_QTextDocument::toHtml() { QFETCH(QTextDocumentFragment, input); QFETCH(QString, expectedOutput); cursor.insertFragment(input); expectedOutput.prepend(htmlHead); expectedOutput.replace("OPENDEFAULTBLOCKSTYLE", "style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"); expectedOutput.replace("DEFAULTBLOCKSTYLE", "style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\""); expectedOutput.replace("EMPTYBLOCK", "

\n"); if (expectedOutput.endsWith(QLatin1Char('\n'))) expectedOutput.chop(1); expectedOutput.append(htmlTail); QString output = doc->toHtml(); QCOMPARE(output, expectedOutput); } void tst_QTextDocument::toHtml2() { QTextDocument doc; doc.setHtml("

text text

"); // 4 spaces before the second 'text' QTextBlock block = doc.firstBlock(); QTextBlock::Iterator iter = block.begin(); QTextFragment f = iter.fragment(); QVERIFY(f.isValid()); QCOMPARE(f.position(), 0); QCOMPARE(f.length(), 5); //qDebug() << block.text().mid(f.position(), f.length()); iter++; f = iter.fragment(); QVERIFY(f.isValid()); QCOMPARE(f.position(), 5); QCOMPARE(f.length(), 1); //qDebug() << block.text().mid(f.position(), f.length()); iter++; f = iter.fragment(); //qDebug() << block.text().mid(f.position(), f.length()); QVERIFY(f.isValid()); QCOMPARE(f.position(), 6); QCOMPARE(f.length(), 5); // 1 space should be preserved. QCOMPARE(block.text().mid(f.position(), f.length()), QString(" text")); doc.setHtml("
foo
text"); // 4 spaces before the second 'text' block = doc.firstBlock().next(); //qDebug() << block.text(); QCOMPARE(block.text(), QString("foo")); block = block.next(); //qDebug() << block.text(); QCOMPARE(block.text(), QString("text")); } void tst_QTextDocument::setFragmentMarkersInHtmlExport() { { CREATE_DOC_AND_CURSOR(); cursor.insertText("Leadin"); const int startPos = cursor.position(); cursor.insertText("Test"); QTextCharFormat fmt; fmt.setForeground(QColor("#00ff00")); cursor.insertText("Blah", fmt); const int endPos = cursor.position(); cursor.insertText("Leadout", QTextCharFormat()); cursor.setPosition(startPos); cursor.setPosition(endPos, QTextCursor::KeepAnchor); QTextDocumentFragment fragment(cursor); QString expected = htmlHead; expected.replace(QRegExp(""), QString("")); expected += QString("

TestBlah

") + htmlTail; QCOMPARE(fragment.toHtml(), expected); } { CREATE_DOC_AND_CURSOR(); cursor.insertText("Leadin"); const int startPos = cursor.position(); cursor.insertText("Test"); const int endPos = cursor.position(); cursor.insertText("Leadout", QTextCharFormat()); cursor.setPosition(startPos); cursor.setPosition(endPos, QTextCursor::KeepAnchor); QTextDocumentFragment fragment(cursor); QString expected = htmlHead; expected.replace(QRegExp(""), QString("")); expected += QString("

Test

") + htmlTail; QCOMPARE(fragment.toHtml(), expected); } } void tst_QTextDocument::toHtmlBodyBgColor() { CREATE_DOC_AND_CURSOR(); cursor.insertText("Blah"); QTextFrameFormat fmt = doc.rootFrame()->frameFormat(); fmt.setBackground(QColor("#0000ff")); doc.rootFrame()->setFrameFormat(fmt); QString expectedHtml("\n" "" "\n" "

Blah

" ""); expectedHtml = expectedHtml.arg(defaultFont.family()).arg(defaultFont.pointSizeF()).arg(defaultFont.weight() * 8).arg((defaultFont.italic() ? "italic" : "normal")); QCOMPARE(doc.toHtml(), expectedHtml); } void tst_QTextDocument::toHtmlRootFrameProperties() { CREATE_DOC_AND_CURSOR(); QTextFrameFormat fmt = doc.rootFrame()->frameFormat(); fmt.setTopMargin(10); fmt.setLeftMargin(10); fmt.setBorder(2); doc.rootFrame()->setFrameFormat(fmt); cursor.insertText("Blah"); QString expectedOutput("\n" "\n
\n" "

Blah

"); expectedOutput.prepend(htmlHead); expectedOutput.replace("DEFAULTBLOCKSTYLE", "style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\""); expectedOutput.append(htmlTail); QCOMPARE(doc.toHtml(), expectedOutput); } void tst_QTextDocument::capitalizationHtmlInExport() { doc->setPlainText("Test"); QRegExp re(".*span style=\"(.*)\">Test.*"); QVERIFY(re.exactMatch(doc->toHtml()) == false); // no span QTextCursor cursor(doc); cursor.setPosition(4, QTextCursor::KeepAnchor); QTextCharFormat cf; cf.setFontCapitalization(QFont::SmallCaps); cursor.mergeCharFormat(cf); const QString smallcaps = doc->toHtml(); QVERIFY(re.exactMatch(doc->toHtml())); QCOMPARE(re.numCaptures(), 1); QCOMPARE(re.cap(1).trimmed(), QString("font-variant:small-caps;")); cf.setFontCapitalization(QFont::AllUppercase); cursor.mergeCharFormat(cf); const QString uppercase = doc->toHtml(); QVERIFY(re.exactMatch(doc->toHtml())); QCOMPARE(re.numCaptures(), 1); QCOMPARE(re.cap(1).trimmed(), QString("text-transform:uppercase;")); cf.setFontCapitalization(QFont::AllLowercase); cursor.mergeCharFormat(cf); const QString lowercase = doc->toHtml(); QVERIFY(re.exactMatch(doc->toHtml())); QCOMPARE(re.numCaptures(), 1); QCOMPARE(re.cap(1).trimmed(), QString("text-transform:lowercase;")); doc->setHtml(smallcaps); cursor.setPosition(1); QCOMPARE(cursor.charFormat().fontCapitalization(), QFont::SmallCaps); doc->setHtml(uppercase); QCOMPARE(cursor.charFormat().fontCapitalization(), QFont::AllUppercase); doc->setHtml(lowercase); QCOMPARE(cursor.charFormat().fontCapitalization(), QFont::AllLowercase); } void tst_QTextDocument::wordspacingHtmlExport() { doc->setPlainText("Test"); QRegExp re(".*span style=\"(.*)\">Test.*"); QVERIFY(re.exactMatch(doc->toHtml()) == false); // no span QTextCursor cursor(doc); cursor.setPosition(4, QTextCursor::KeepAnchor); QTextCharFormat cf; cf.setFontWordSpacing(4); cursor.mergeCharFormat(cf); QVERIFY(re.exactMatch(doc->toHtml())); QCOMPARE(re.numCaptures(), 1); QCOMPARE(re.cap(1).trimmed(), QString("word-spacing:4px;")); cf.setFontWordSpacing(-8.5); cursor.mergeCharFormat(cf); QVERIFY(re.exactMatch(doc->toHtml())); QCOMPARE(re.numCaptures(), 1); QCOMPARE(re.cap(1).trimmed(), QString("word-spacing:-8.5px;")); } class CursorPosSignalSpy : public QObject { Q_OBJECT public: CursorPosSignalSpy(QTextDocument *doc) { calls = 0; connect(doc, SIGNAL(cursorPositionChanged(const QTextCursor &)), this, SLOT(cursorPositionChanged(const QTextCursor &))); } int calls; private slots: void cursorPositionChanged(const QTextCursor &) { ++calls; } }; void tst_QTextDocument::cursorPositionChanged() { CursorPosSignalSpy spy(doc); cursor.insertText("Test"); QCOMPARE(spy.calls, 1); spy.calls = 0; QTextCursor unrelatedCursor(doc); unrelatedCursor.insertText("Blah"); QCOMPARE(spy.calls, 2); spy.calls = 0; cursor.insertText("Blah"); QCOMPARE(spy.calls, 1); spy.calls = 0; cursor.movePosition(QTextCursor::PreviousCharacter); QCOMPARE(spy.calls, 0); } void tst_QTextDocument::cursorPositionChangedOnSetText() { CursorPosSignalSpy spy(doc); cursor = QTextCursor(); doc->setPlainText("Foo\nBar\nBaz\nBlub\nBlah"); // the signal should still be emitted once for the QTextCursor that // QTextDocument::setPlainText creates temporarily. But the signal // should not be emitted more often. QCOMPARE(spy.calls, 1); spy.calls = 0; doc->setHtml("

Foo

Bar

Baz

Blah"); // the signal should still be emitted once for the QTextCursor that // QTextDocument::setPlainText creates temporarily. But the signal // should not be emitted more often. QCOMPARE(spy.calls, 1); } void tst_QTextDocument::textFrameIterator() { cursor.insertTable(1, 1); int blockCount = 0; int frameCount = 0; for (QTextFrame::Iterator frameIt = doc->rootFrame()->begin(); !frameIt.atEnd(); ++frameIt) { if (frameIt.currentFrame()) ++frameCount; else if (frameIt.currentBlock().isValid()) ++blockCount; } QEXPECT_FAIL("", "This is currently worked around in the html export but needs fixing!", Continue); QCOMPARE(blockCount, 0); QCOMPARE(frameCount, 1); } void tst_QTextDocument::codecForHtml() { const QByteArray header(""); QTextCodec *c = Qt::codecForHtml(header); QVERIFY(c); QCOMPARE(c->name(), QByteArray("UTF-16")); } class TestSyntaxHighlighter : public QObject { Q_OBJECT public: inline TestSyntaxHighlighter(QTextDocument *doc) : QObject(doc), ok(false) {} bool ok; private slots: inline void markBlockDirty(int from, int charsRemoved, int charsAdded) { Q_UNUSED(charsRemoved); Q_UNUSED(charsAdded); QTextDocument *doc = static_cast(parent()); QTextBlock block = doc->findBlock(from); QTestDocumentLayout *lout = qobject_cast(doc->documentLayout()); lout->called = false; doc->markContentsDirty(block.position(), block.length()); ok = (lout->called == false); } inline void modifyBlockAgain(int from, int charsRemoved, int charsAdded) { Q_UNUSED(charsRemoved); Q_UNUSED(charsAdded); QTextDocument *doc = static_cast(parent()); QTextBlock block = doc->findBlock(from); QTextCursor cursor(block); QTestDocumentLayout *lout = qobject_cast(doc->documentLayout()); lout->called = false; cursor.insertText("Foo"); ok = (lout->called == true); } }; void tst_QTextDocument::markContentsDirty() { QTestDocumentLayout *lout = new QTestDocumentLayout(doc); doc->setDocumentLayout(lout); TestSyntaxHighlighter *highlighter = new TestSyntaxHighlighter(doc); connect(doc, SIGNAL(contentsChange(int, int, int)), highlighter, SLOT(markBlockDirty(int, int, int))); highlighter->ok = false; cursor.insertText("Some dummy text blah blah"); QVERIFY(highlighter->ok); disconnect(doc, SIGNAL(contentsChange(int, int, int)), highlighter, SLOT(markBlockDirty(int, int, int))); connect(doc, SIGNAL(contentsChange(int, int, int)), highlighter, SLOT(modifyBlockAgain(int, int, int))); highlighter->ok = false; cursor.insertText("FooBar"); QVERIFY(highlighter->ok); lout->called = false; doc->markContentsDirty(1, 4); QVERIFY(lout->called); } void tst_QTextDocument::clonePreservesMetaInformation() { const QString title("Foobar"); const QString url("about:blank"); doc->setHtml("" + title + "Hrm"); doc->setMetaInformation(QTextDocument::DocumentUrl, url); QCOMPARE(doc->metaInformation(QTextDocument::DocumentTitle), title); QCOMPARE(doc->metaInformation(QTextDocument::DocumentUrl), url); QTextDocument *clone = doc->clone(); QCOMPARE(clone->metaInformation(QTextDocument::DocumentTitle), title); QCOMPARE(clone->metaInformation(QTextDocument::DocumentUrl), url); delete clone; } void tst_QTextDocument::clonePreservesPageSize() { QSizeF sz(100., 100.); doc->setPageSize(sz); QTextDocument *clone = doc->clone(); QCOMPARE(clone->pageSize(), sz); delete clone; } void tst_QTextDocument::clonePreservesPageBreakPolicies() { QTextTableFormat tableFmt; tableFmt.setPageBreakPolicy(QTextFormat::PageBreak_AlwaysAfter); QTextBlockFormat blockFmt; blockFmt.setPageBreakPolicy(QTextFormat::PageBreak_AlwaysBefore); QTextCursor cursor(doc); cursor.setBlockFormat(blockFmt); cursor.insertText("foo"); cursor.insertTable(2, 2, tableFmt); QTextDocument *clone = doc->clone(); QCOMPARE(clone->begin().blockFormat().pageBreakPolicy(), QTextFormat::PageBreak_AlwaysBefore); QVERIFY(!clone->rootFrame()->childFrames().isEmpty()); QCOMPARE(clone->rootFrame()->childFrames().first()->frameFormat().pageBreakPolicy(), QTextFormat::PageBreak_AlwaysAfter); delete clone; } void tst_QTextDocument::clonePreservesDefaultFont() { QFont f = doc->defaultFont(); QVERIFY(f.pointSize() != 100); f.setPointSize(100); doc->setDefaultFont(f); QTextDocument *clone = doc->clone(); QCOMPARE(clone->defaultFont(), f); delete clone; } void tst_QTextDocument::clonePreservesResources() { QUrl testUrl(":/foobar"); QVariant testResource("hello world"); doc->addResource(QTextDocument::ImageResource, testUrl, testResource); QTextDocument *clone = doc->clone(); QVERIFY(clone->resource(QTextDocument::ImageResource, testUrl) == testResource); delete clone; } void tst_QTextDocument::clonePreservesUserStates() { QTextCursor cursor(doc); cursor.insertText("bla bla bla"); cursor.block().setUserState(1); cursor.insertBlock(); cursor.insertText("foo bar"); cursor.block().setUserState(2); cursor.insertBlock(); cursor.insertText("no user state"); QTextDocument *clone = doc->clone(); QTextBlock b1 = doc->begin(), b2 = clone->begin(); while (b1 != doc->end()) { b1 = b1.next(); b2 = b2.next(); QCOMPARE(b1.userState(), b2.userState()); } QVERIFY(b2 == clone->end()); delete clone; } void tst_QTextDocument::clonePreservesRootFrameFormat() { doc->setPlainText("Hello"); QTextFrameFormat fmt = doc->rootFrame()->frameFormat(); fmt.setMargin(200); doc->rootFrame()->setFrameFormat(fmt); QCOMPARE(doc->rootFrame()->frameFormat().margin(), qreal(200)); QTextDocument *copy = doc->clone(); QCOMPARE(copy->rootFrame()->frameFormat().margin(), qreal(200)); delete copy; } void tst_QTextDocument::clonePreservesIndentWidth() { doc->setIndentWidth(42); QTextDocument *clone = doc->clone(); QCOMPARE(clone->indentWidth(), qreal(42)); } void tst_QTextDocument::blockCount() { QCOMPARE(doc->blockCount(), 1); cursor.insertBlock(); QCOMPARE(doc->blockCount(), 2); cursor.insertBlock(); QCOMPARE(doc->blockCount(), 3); cursor.insertText("blah blah"); QCOMPARE(doc->blockCount(), 3); doc->undo(); doc->undo(); QCOMPARE(doc->blockCount(), 2); doc->undo(); QCOMPARE(doc->blockCount(), 1); } void tst_QTextDocument::resolvedFontInEmptyFormat() { QFont font; font.setPointSize(42); doc->setDefaultFont(font); QTextCharFormat fmt = doc->begin().charFormat(); QVERIFY(fmt.properties().isEmpty()); QVERIFY(fmt.font() == font); } void tst_QTextDocument::defaultRootFrameMargin() { QCOMPARE(doc->rootFrame()->frameFormat().margin(), 4.0); } class TestDocument : public QTextDocument { public: inline TestDocument(const QUrl &testUrl, const QString &testString) : url(testUrl), string(testString), resourceLoaded(false) {} bool hasResourceCached(); protected: virtual QVariant loadResource(int type, const QUrl &name); private: QUrl url; QString string; bool resourceLoaded; }; bool TestDocument::hasResourceCached() { resourceLoaded = false; resource(QTextDocument::ImageResource, url); return !resourceLoaded; } QVariant TestDocument::loadResource(int type, const QUrl &name) { if (type == QTextDocument::ImageResource && name == url) { resourceLoaded = true; return string; } return QTextDocument::loadResource(type, name); } void tst_QTextDocument::clearResources() { // regular resource for QTextDocument QUrl testUrl(":/foobar"); QVariant testResource("hello world"); // implicitly cached resource, initially loaded through TestDocument::loadResource() QUrl cacheUrl(":/blub"); QString cacheResource("mah"); TestDocument doc(cacheUrl, cacheResource); doc.addResource(QTextDocument::ImageResource, testUrl, testResource); QVERIFY(doc.resource(QTextDocument::ImageResource, testUrl) == testResource); doc.setPlainText("Hah"); QVERIFY(doc.resource(QTextDocument::ImageResource, testUrl) == testResource); doc.setHtml("Mooo"); QVERIFY(doc.resource(QTextDocument::ImageResource, testUrl) == testResource); QVERIFY(doc.resource(QTextDocument::ImageResource, cacheUrl) == cacheResource); doc.clear(); QVERIFY(!doc.resource(QTextDocument::ImageResource, testUrl).isValid()); QVERIFY(!doc.hasResourceCached()); doc.clear(); doc.setHtml("Mooo"); QVERIFY(doc.resource(QTextDocument::ImageResource, cacheUrl) == cacheResource); doc.setPlainText("Foob"); QVERIFY(!doc.hasResourceCached()); } void tst_QTextDocument::setPlainText() { doc->setPlainText("Hello World"); QString s(""); doc->setPlainText(s); QCOMPARE(doc->toPlainText(), s); } void tst_QTextDocument::toPlainText() { doc->setHtml("Hello World"); QCOMPARE(doc->toPlainText(), QLatin1String("Hello World")); } void tst_QTextDocument::deleteTextObjectsOnClear() { QPointer table = cursor.insertTable(2, 2); QVERIFY(!table.isNull()); doc->clear(); QVERIFY(table.isNull()); } void tst_QTextDocument::defaultStyleSheet() { const QString sheet("p { background-color: green; }"); QVERIFY(doc->defaultStyleSheet().isEmpty()); doc->setDefaultStyleSheet(sheet); QCOMPARE(doc->defaultStyleSheet(), sheet); cursor.insertHtml("

test"); QTextBlockFormat fmt = doc->begin().blockFormat(); QVERIFY(fmt.background().color() == QColor("green")); doc->clear(); cursor.insertHtml("

test"); fmt = doc->begin().blockFormat(); QVERIFY(fmt.background().color() == QColor("green")); QTextDocument *clone = doc->clone(); QCOMPARE(clone->defaultStyleSheet(), sheet); cursor = QTextCursor(clone); cursor.insertHtml("

test"); fmt = clone->begin().blockFormat(); QVERIFY(fmt.background().color() == QColor("green")); delete clone; cursor = QTextCursor(doc); cursor.insertHtml("

test"); fmt = doc->begin().blockFormat(); QVERIFY(fmt.background().color() == QColor("green")); doc->clear(); cursor.insertHtml("

test"); fmt = doc->begin().blockFormat(); QVERIFY(fmt.background().color() == QColor("red")); doc->clear(); doc->setDefaultStyleSheet("invalid style sheet...."); cursor.insertHtml("

test"); fmt = doc->begin().blockFormat(); QVERIFY(fmt.background().color() != QColor("green")); } void tst_QTextDocument::maximumBlockCount() { QCOMPARE(doc->maximumBlockCount(), 0); QVERIFY(doc->isUndoRedoEnabled()); cursor.insertBlock(); cursor.insertText("Blah"); cursor.insertBlock(); cursor.insertText("Foo"); QCOMPARE(doc->blockCount(), 3); QCOMPARE(doc->toPlainText(), QString("\nBlah\nFoo")); doc->setMaximumBlockCount(1); QVERIFY(!doc->isUndoRedoEnabled()); QCOMPARE(doc->blockCount(), 1); QCOMPARE(doc->toPlainText(), QString("Foo")); cursor.insertBlock(); cursor.insertText("Hello"); doc->setMaximumBlockCount(1); QCOMPARE(doc->blockCount(), 1); QCOMPARE(doc->toPlainText(), QString("Hello")); doc->setMaximumBlockCount(100); for (int i = 0; i < 1000; ++i) { cursor.insertBlock(); cursor.insertText("Blah)"); QVERIFY(doc->blockCount() <= 100); } cursor.movePosition(QTextCursor::End); QCOMPARE(cursor.blockNumber(), 99); QTextCharFormat fmt; fmt.setFontItalic(true); cursor.setBlockCharFormat(fmt); cursor.movePosition(QTextCursor::Start); QVERIFY(!cursor.blockCharFormat().fontItalic()); doc->setMaximumBlockCount(1); QVERIFY(cursor.blockCharFormat().fontItalic()); cursor.insertTable(2, 2); QCOMPARE(doc->blockCount(), 6); cursor.insertBlock(); QCOMPARE(doc->blockCount(), 1); } void tst_QTextDocument::adjustSize() { // avoid ugly tooltips like in task 125583 QString text("Test Text"); doc->setPlainText(text); doc->rootFrame()->setFrameFormat(QTextFrameFormat()); doc->adjustSize(); QCOMPARE(doc->size().width(), doc->idealWidth()); } void tst_QTextDocument::initialUserData() { doc->setPlainText("Hello"); QTextBlock block = doc->begin(); block.setUserData(new QTextBlockUserData); QVERIFY(block.userData()); doc->documentLayout(); QVERIFY(block.userData()); doc->setDocumentLayout(new QTestDocumentLayout(doc)); QVERIFY(!block.userData()); } void tst_QTextDocument::html_defaultFont() { QFont f; f.setItalic(true); f.setWeight(QFont::Bold); doc->setDefaultFont(f); doc->setPlainText("Test"); QString bodyPart = QString::fromLatin1("") .arg(f.family()).arg(f.pointSizeF()).arg(f.weight() * 8); QString html = doc->toHtml(); if (!html.contains(bodyPart)) { qDebug() << "html:" << html; qDebug() << "expected body:" << bodyPart; QVERIFY(html.contains(bodyPart)); } if (html.contains("span")) qDebug() << "html:" << html; QVERIFY(!html.contains("setPlainText("Foo"); QCOMPARE(doc->blockCount(), 1); QCOMPARE(spy.count(), 0); spy.clear(); doc->setPlainText("Foo\nBar"); QCOMPARE(doc->blockCount(), 2); QCOMPARE(spy.count(), 1); QCOMPARE(spy.at(0).value(0).toInt(), 2); spy.clear(); cursor.movePosition(QTextCursor::End); cursor.insertText("Blahblah"); QCOMPARE(spy.count(), 0); cursor.insertBlock(); QCOMPARE(spy.count(), 1); QCOMPARE(spy.at(0).value(0).toInt(), 3); spy.clear(); doc->undo(); QCOMPARE(spy.count(), 1); QCOMPARE(spy.at(0).value(0).toInt(), 2); } void tst_QTextDocument::nonZeroDocumentLengthOnClear() { QTestDocumentLayout *lout = new QTestDocumentLayout(doc); doc->setDocumentLayout(lout); doc->clear(); QVERIFY(lout->called); QVERIFY(!lout->lastDocumentLengths.contains(0)); } void tst_QTextDocument::setTextPreservesUndoRedoEnabled() { QVERIFY(doc->isUndoRedoEnabled()); doc->setPlainText("Test"); QVERIFY(doc->isUndoRedoEnabled()); doc->setUndoRedoEnabled(false); QVERIFY(!doc->isUndoRedoEnabled()); doc->setPlainText("Test2"); QVERIFY(!doc->isUndoRedoEnabled()); doc->setHtml("

hello"); QVERIFY(!doc->isUndoRedoEnabled()); } void tst_QTextDocument::firstLast() { QCOMPARE(doc->blockCount(), 1); QVERIFY(doc->firstBlock() == doc->lastBlock()); doc->setPlainText("Hello\nTest\nWorld"); QCOMPARE(doc->blockCount(), 3); QVERIFY(doc->firstBlock() != doc->lastBlock()); QCOMPARE(doc->firstBlock().text(), QString("Hello")); QCOMPARE(doc->lastBlock().text(), QString("World")); // manual forward loop QTextBlock block = doc->firstBlock(); QVERIFY(block.isValid()); QCOMPARE(block.text(), QString("Hello")); block = block.next(); QVERIFY(block.isValid()); QCOMPARE(block.text(), QString("Test")); block = block.next(); QVERIFY(block.isValid()); QCOMPARE(block.text(), QString("World")); block = block.next(); QVERIFY(!block.isValid()); // manual backward loop block = doc->lastBlock(); QVERIFY(block.isValid()); QCOMPARE(block.text(), QString("World")); block = block.previous(); QVERIFY(block.isValid()); QCOMPARE(block.text(), QString("Test")); block = block.previous(); QVERIFY(block.isValid()); QCOMPARE(block.text(), QString("Hello")); block = block.previous(); QVERIFY(!block.isValid()); } const QString backgroundImage_html("
Blah
"); void tst_QTextDocument::backgroundImage_checkExpectedHtml(const QTextDocument &doc) { QString expectedHtml("\n" "" "\n" "" "\n\n
" "\n

Blah

" "
"); expectedHtml = expectedHtml.arg(defaultFont.family()).arg(defaultFont.pointSizeF()).arg(defaultFont.weight() * 8).arg((defaultFont.italic() ? "italic" : "normal")); QCOMPARE(doc.toHtml(), expectedHtml); } void tst_QTextDocument::backgroundImage_toHtml() { CREATE_DOC_AND_CURSOR(); doc.setHtml(backgroundImage_html); backgroundImage_checkExpectedHtml(doc); } void tst_QTextDocument::backgroundImage_toHtml2() { CREATE_DOC_AND_CURSOR(); cursor.insertHtml(backgroundImage_html); backgroundImage_checkExpectedHtml(doc); } void tst_QTextDocument::backgroundImage_clone() { CREATE_DOC_AND_CURSOR(); doc.setHtml(backgroundImage_html); QTextDocument *clone = doc.clone(); backgroundImage_checkExpectedHtml(*clone); delete clone; } void tst_QTextDocument::backgroundImage_copy() { CREATE_DOC_AND_CURSOR(); doc.setHtml(backgroundImage_html); QTextDocumentFragment fragment(&doc); { CREATE_DOC_AND_CURSOR(); cursor.insertFragment(fragment); backgroundImage_checkExpectedHtml(doc); } } void tst_QTextDocument::documentCleanup() { QTextDocument doc; QTextCursor cursor(&doc); cursor.insertText("d\nfoo\nbar\n"); doc.documentLayout(); // forces relayout // remove char 1 cursor.setPosition(0); QSizeF size = doc.documentLayout()->documentSize(); cursor.deleteChar(); // the size should be unchanged. QCOMPARE(doc.documentLayout()->documentSize(), size); } void tst_QTextDocument::characterAt() { QTextDocument doc; QTextCursor cursor(&doc); QString text("12345\n67890"); cursor.insertText(text); int length = doc.characterCount(); QCOMPARE(length, text.length() + 1); QCOMPARE(doc.characterAt(length-1), QChar(QChar::ParagraphSeparator)); QCOMPARE(doc.characterAt(-1), QChar()); QCOMPARE(doc.characterAt(length), QChar()); QCOMPARE(doc.characterAt(length + 1), QChar()); for (int i = 0; i < text.length(); ++i) { QChar c = text.at(i); if (c == QLatin1Char('\n')) c = QChar(QChar::ParagraphSeparator); QCOMPARE(doc.characterAt(i), c); } } void tst_QTextDocument::revisions() { QTextDocument doc; QTextCursor cursor(&doc); QString text("Hello World"); QCOMPARE(doc.firstBlock().revision(), 0); cursor.insertText(text); QCOMPARE(doc.firstBlock().revision(), 1); cursor.setPosition(6); cursor.insertBlock(); QCOMPARE(cursor.block().previous().revision(), 2); QCOMPARE(cursor.block().revision(), 2); cursor.insertText("candle"); QCOMPARE(cursor.block().revision(), 3); cursor.movePosition(QTextCursor::EndOfBlock); cursor.insertBlock(); // we are at the block end QCOMPARE(cursor.block().previous().revision(), 3); QCOMPARE(cursor.block().revision(), 4); cursor.insertText("lightbulb"); QCOMPARE(cursor.block().revision(), 5); cursor.movePosition(QTextCursor::StartOfBlock); cursor.insertBlock(); // we are the block start QCOMPARE(cursor.block().previous().revision(), 6); QCOMPARE(cursor.block().revision(), 5); } void tst_QTextDocument::testUndoCommandAdded() { QVERIFY(doc); QSignalSpy spy(doc, SIGNAL(undoCommandAdded())); QVERIFY(spy.isValid()); QVERIFY(spy.isEmpty()); cursor.insertText("a"); QCOMPARE(spy.count(), 1); cursor.insertText("b"); // should be merged QCOMPARE(spy.count(), 1); cursor.insertText("c"); // should be merged QCOMPARE(spy.count(), 1); QCOMPARE(doc->toPlainText(), QString("abc")); doc->undo(); QCOMPARE(doc->toPlainText(), QString("")); doc->clear(); spy.clear(); cursor.insertText("aaa"); QCOMPARE(spy.count(), 1); spy.clear(); cursor.insertText("aaaa\nbcd"); QCOMPARE(spy.count(), 1); spy.clear(); cursor.beginEditBlock(); cursor.insertText("aa"); cursor.insertText("bbb\n"); cursor.setCharFormat(QTextCharFormat()); cursor.insertText("\nccc"); QVERIFY(spy.isEmpty()); cursor.endEditBlock(); QCOMPARE(spy.count(), 1); spy.clear(); cursor.insertBlock(); QCOMPARE(spy.count(), 1); spy.clear(); cursor.setPosition(5); QVERIFY(spy.isEmpty()); cursor.setCharFormat(QTextCharFormat()); QVERIFY(spy.isEmpty()); cursor.setPosition(10, QTextCursor::KeepAnchor); QVERIFY(spy.isEmpty()); QTextCharFormat cf; cf.setFontItalic(true); cursor.mergeCharFormat(cf); QCOMPARE(spy.count(), 1); } void tst_QTextDocument::testUndoBlocks() { QVERIFY(doc); cursor.insertText("Hello World"); cursor.insertText("period"); doc->undo(); QCOMPARE(doc->toPlainText(), QString("")); cursor.insertText("Hello World"); cursor.insertText("One\nTwo\nThree"); QCOMPARE(doc->toPlainText(), QString("Hello WorldOne\nTwo\nThree")); doc->undo(); QCOMPARE(doc->toPlainText(), QString("Hello World")); doc->undo(); QCOMPARE(doc->toPlainText(), QString("")); } class Receiver : public QObject { Q_OBJECT public: QString first; public slots: void cursorPositionChanged() { if (first.isEmpty()) first = QLatin1String("cursorPositionChanged"); } void contentsChange() { if (first.isEmpty()) first = QLatin1String("contentsChanged"); } }; void tst_QTextDocument::receiveCursorPositionChangedAfterContentsChange() { QVERIFY(doc); doc->setDocumentLayout(new MyAbstractTextDocumentLayout(doc)); Receiver rec; connect(doc, SIGNAL(cursorPositionChanged(QTextCursor)), &rec, SLOT(cursorPositionChanged())); connect(doc, SIGNAL(contentsChange(int,int,int)), &rec, SLOT(contentsChange())); cursor.insertText("Hello World"); QCOMPARE(rec.first, QString("contentsChanged")); } QTEST_MAIN(tst_QTextDocument) #include "tst_qtextdocument.moc"