diff -r 000000000000 -r 1918ee327afb tests/auto/qtextdocument/tst_qtextdocument.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/auto/qtextdocument/tst_qtextdocument.cpp Mon Jan 11 14:00:40 2010 +0000 @@ -0,0 +1,2656 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** 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 Technology Preview License Agreement accompanying +** this package. +** +** 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.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include + + +#include +#include + +#include +#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 task240325(); + + void stylesheetFont_data(); + void stylesheetFont(); + + 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 revisionWithUndoCompressionAndUndo(); + + 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::task240325() +{ + doc->setHtml("Foobar Foobar Foobar Foobar"); + + QImage img(1000, 7000, QImage::Format_ARGB32_Premultiplied); + QPainter p(&img); + QFontMetrics fm(p.font()); + + // Set page size to contain image and one "Foobar" + doc->setPageSize(QSize(100 + fm.width("Foobar")*2, 1000)); + + // Force layout + doc->drawContents(&p); + + QCOMPARE(doc->blockCount(), 1); + for (QTextBlock block = doc->begin() ; block!=doc->end() ; block = block.next()) { + QTextLayout *layout = block.layout(); + QCOMPARE(layout->lineCount(), 4); + for (int lineIdx=0;lineIdxlineCount();++lineIdx) { + QTextLine line = layout->lineAt(lineIdx); + + QString text = block.text().mid(line.textStart(), line.textLength()).trimmed(); + + // Remove start token + if (lineIdx == 0) + text = text.mid(1); + + QCOMPARE(text, QString::fromLatin1("Foobar")); + } + } +} + +void tst_QTextDocument::stylesheetFont_data() +{ + QTest::addColumn("stylesheet"); + QTest::addColumn("font"); + + { + QFont font; + font.setBold(true); + font.setPixelSize(64); + + QTest::newRow("Regular font specification") + << "font-size: 64px; font-weight: bold;" + << font; + } + + + { + QFont font; + font.setBold(true); + font.setPixelSize(64); + + QTest::newRow("Shorthand font specification") + << "font: normal bold 64px Arial;" + << font; + } + +} + +void tst_QTextDocument::stylesheetFont() +{ + QFETCH(QString, stylesheet); + QFETCH(QFont, font); + + QString html = QString::fromLatin1("" + "" + "
" + "Foobar" + "
" + "" + "").arg(stylesheet); + + qDebug() << html; + doc->setHtml(html); + QCOMPARE(doc->blockCount(), 1); + + // First and only block + QTextBlock block = doc->firstBlock(); + + QString text = block.text(); + QCOMPARE(text, QString::fromLatin1("Foobar")); + + QFont actualFont = block.charFormat().font(); + + QCOMPARE(actualFont.bold(), font.bold()); + QCOMPARE(actualFont.pixelSize(), font.pixelSize()); +} + +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
"); + } + + { + CREATE_DOC_AND_CURSOR(); + + QTextListFormat listFmt; + listFmt.setStyle(QTextListFormat::ListDisc); + + cursor.insertList(listFmt); + cursor.insertText("Blah"); + + QTest::newRow("list-ul-margin") << QTextDocumentFragment(&doc) + << QString("EMPTYBLOCK") + + QString("
  • Blah
"); + } +} + +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); + + // doc has one QTextCursor stored in the + // cursor member variable, thus the signal + // gets emitted once. + + doc->setPlainText("Foo\nBar\nBaz\nBlub\nBlah"); + + QCOMPARE(spy.calls, 1); + + spy.calls = 0; + doc->setHtml("

Foo

Bar

Baz

Blah"); + + 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::revisionWithUndoCompressionAndUndo() +{ + QTextDocument doc; + QTextCursor cursor(&doc); + cursor.insertText("This is the beginning of it all."); + QCOMPARE(doc.firstBlock().revision(), 1); + QCOMPARE(doc.revision(), 1); + cursor.insertBlock(); + QCOMPARE(doc.revision(), 2); + cursor.insertText("this"); + QCOMPARE(doc.revision(), 3); + cursor.insertText("is"); + QCOMPARE(doc.revision(), 4); + cursor.insertText("compressed"); + QCOMPARE(doc.revision(), 5); + doc.undo(); + QCOMPARE(doc.revision(), 6); + QCOMPARE(doc.toPlainText(), QString("This is the beginning of it all.\n")) ; + cursor.setPosition(0); + QCOMPARE(doc.firstBlock().revision(), 1); + cursor.insertText("Very beginnig"); + QCOMPARE(doc.firstBlock().revision(), 7); + doc.undo(); + QCOMPARE(doc.revision(), 8); + QCOMPARE(doc.firstBlock().revision(), 1); + + cursor.beginEditBlock(); + cursor.insertText("Hello"); + cursor.insertBlock(); + cursor.insertText("world"); + cursor.endEditBlock(); + QCOMPARE(doc.revision(), 9); + doc.undo(); + QCOMPARE(doc.revision(), 10); + + +} + +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")); + cursor.insertText("One\nTwo\nThree"); + cursor.insertText("Trailing text"); + doc->undo(); + QCOMPARE(doc->toPlainText(), QString("Hello WorldOne\nTwo\nThree")); + doc->undo(); + QCOMPARE(doc->toPlainText(), QString("Hello World")); + doc->undo(); + QCOMPARE(doc->toPlainText(), QString("")); + + cursor.insertText("quod"); + cursor.beginEditBlock(); + cursor.insertText(" erat"); + cursor.endEditBlock(); + cursor.insertText(" demonstrandum"); + QCOMPARE(doc->toPlainText(), QString("quod erat demonstrandum")); + doc->undo(); + QCOMPARE(doc->toPlainText(), QString("quod erat")); + doc->undo(); + QCOMPARE(doc->toPlainText(), QString("quod")); + 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"