1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42
43 #include <QtTest/QtTest>
44
45
46 #include <qtextdocument.h>
47 #include <qdebug.h>
48
49 #include <qtextcursor.h>
50 #include <qtextdocumentfragment.h>
51 #include <qtextformat.h>
52 #include <qtextobject.h>
53 #include <qtexttable.h>
54 #include <qabstracttextdocumentlayout.h>
55 #include <qtextlist.h>
56 #include <qtextcodec.h>
57 #include <qurl.h>
58 #include <qpainter.h>
59 #include <qfontmetrics.h>
60 #include <qimage.h>
61 #include <qtextlayout.h>
62 #include <QDomDocument>
63 #include "common.h"
64
65
66 QT_FORWARD_DECLARE_CLASS(QTextDocument)
67
68 //TESTED_CLASS=
69 //TESTED_FILES=
70
71 class tst_QTextDocument : public QObject
72 {
73 Q_OBJECT
74
75 public:
76 tst_QTextDocument();
77 virtual ~tst_QTextDocument();
78
79 public slots:
80 void init();
81 void cleanup();
82 private slots:
83 void getSetCheck();
84 void isEmpty();
85 void find_data();
86 void find();
87 void find2();
88 void findWithRegExp_data();
89 void findWithRegExp();
90 void findMultiple();
91 void basicIsModifiedChecks();
92 void moreIsModified();
93 void isModified2();
94 void isModified3();
95 void isModified4();
96 void noundo_basicIsModifiedChecks();
97 void noundo_moreIsModified();
98 void noundo_isModified2();
99 void noundo_isModified3();
100 void mightBeRichText();
101 void mightBeRichText_data();
102
103 void task240325();
104
105 void stylesheetFont_data();
106 void stylesheetFont();
107
108 void toHtml_data();
109 void toHtml();
110 void toHtml2();
111
112 void setFragmentMarkersInHtmlExport();
113
114 void toHtmlBodyBgColor();
115 void toHtmlRootFrameProperties();
116 void capitalizationHtmlInExport();
117 void wordspacingHtmlExport();
118
119 void cursorPositionChanged();
120 void cursorPositionChangedOnSetText();
121
122 void textFrameIterator();
123
124 void codecForHtml();
125
126 void markContentsDirty();
127
128 void clonePreservesMetaInformation();
129 void clonePreservesPageSize();
130 void clonePreservesPageBreakPolicies();
131 void clonePreservesDefaultFont();
132 void clonePreservesRootFrameFormat();
133 void clonePreservesResources();
134 void clonePreservesUserStates();
135 void clonePreservesIndentWidth();
136 void blockCount();
137 void defaultStyleSheet();
138
139 void resolvedFontInEmptyFormat();
140
141 void defaultRootFrameMargin();
142
143 void clearResources();
144
145 void setPlainText();
146 void toPlainText();
147
148 void deleteTextObjectsOnClear();
149
150 void maximumBlockCount();
151 void adjustSize();
152 void initialUserData();
153
154 void html_defaultFont();
155
156 void blockCountChanged();
157
158 void nonZeroDocumentLengthOnClear();
159
160 void setTextPreservesUndoRedoEnabled();
161
162 void firstLast();
163
164 void backgroundImage_toHtml();
165 void backgroundImage_toHtml2();
166 void backgroundImage_clone();
167 void backgroundImage_copy();
168
169 void documentCleanup();
170
171 void characterAt();
172 void revisions();
173 void revisionWithUndoCompressionAndUndo();
174
175 void testUndoCommandAdded();
176
177 void testUndoBlocks();
178
179 void receiveCursorPositionChangedAfterContentsChange();
180 void escape_data();
181 void escape();
182
183 void copiedFontSize();
184 void QTBUG25778_pixelSizeFromHtml();
185
186 void htmlExportImportBlockCount();
187
188 private:
189 void backgroundImage_checkExpectedHtml(const QTextDocument &doc);
190
191 QTextDocument *doc;
192 QTextCursor cursor;
193 QFont defaultFont;
194 QString htmlHead;
195 QString htmlTail;
196 };
197
198 class MyAbstractTextDocumentLayout : public QAbstractTextDocumentLayout
199 {
200 public:
MyAbstractTextDocumentLayout(QTextDocument * doc)201 MyAbstractTextDocumentLayout(QTextDocument *doc) : QAbstractTextDocumentLayout(doc) {}
draw(QPainter *,const PaintContext &)202 void draw(QPainter *, const PaintContext &) {}
hitTest(const QPointF &,Qt::HitTestAccuracy) const203 int hitTest(const QPointF &, Qt::HitTestAccuracy) const { return 0; }
pageCount() const204 int pageCount() const { return 0; }
documentSize() const205 QSizeF documentSize() const { return QSizeF(); }
frameBoundingRect(QTextFrame *) const206 QRectF frameBoundingRect(QTextFrame *) const { return QRectF(); }
blockBoundingRect(const QTextBlock &) const207 QRectF blockBoundingRect(const QTextBlock &) const { return QRectF(); }
documentChanged(int,int,int)208 void documentChanged(int, int, int) {}
209 };
210
211 // Testing get/set functions
getSetCheck()212 void tst_QTextDocument::getSetCheck()
213 {
214 QTextDocument obj1;
215 // QAbstractTextDocumentLayout * QTextDocument::documentLayout()
216 // void QTextDocument::setDocumentLayout(QAbstractTextDocumentLayout *)
217 QPointer<MyAbstractTextDocumentLayout> var1 = new MyAbstractTextDocumentLayout(0);
218 obj1.setDocumentLayout(var1);
219 QCOMPARE(static_cast<QAbstractTextDocumentLayout *>(var1), obj1.documentLayout());
220 obj1.setDocumentLayout((QAbstractTextDocumentLayout *)0);
221 QVERIFY(var1.isNull());
222 QVERIFY(obj1.documentLayout());
223
224 // bool QTextDocument::useDesignMetrics()
225 // void QTextDocument::setUseDesignMetrics(bool)
226 obj1.setUseDesignMetrics(false);
227 QCOMPARE(false, obj1.useDesignMetrics());
228 obj1.setUseDesignMetrics(true);
229 QCOMPARE(true, obj1.useDesignMetrics());
230 }
231
tst_QTextDocument()232 tst_QTextDocument::tst_QTextDocument()
233 {
234 QImage img(16, 16, QImage::Format_ARGB32_Premultiplied);
235 img.save("foo.png");
236 }
237
~tst_QTextDocument()238 tst_QTextDocument::~tst_QTextDocument()
239 {
240 QFile::remove(QLatin1String("foo.png"));
241 }
242
init()243 void tst_QTextDocument::init()
244 {
245 doc = new QTextDocument;
246 cursor = QTextCursor(doc);
247 defaultFont = QFont();
248
249 htmlHead = QString("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" "
250 "\"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
251 "<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
252 "p, li { white-space: pre-wrap; }\n"
253 "</style></head>"
254 "<body style=\" font-family:'%1'; font-size:%2pt; font-weight:%3; font-style:%4;\">\n");
255 htmlHead = htmlHead.arg(defaultFont.family()).arg(defaultFont.pointSizeF()).arg(defaultFont.weight() * 8).arg((defaultFont.italic() ? "italic" : "normal"));
256
257 htmlTail = QString("</body></html>");
258 }
259
cleanup()260 void tst_QTextDocument::cleanup()
261 {
262 cursor = QTextCursor();
263 delete doc;
264 doc = 0;
265 }
266
isEmpty()267 void tst_QTextDocument::isEmpty()
268 {
269 QVERIFY(doc->isEmpty());
270 }
271
find_data()272 void tst_QTextDocument::find_data()
273 {
274 QTest::addColumn<QString>("haystack");
275 QTest::addColumn<QString>("needle");
276 QTest::addColumn<int>("flags");
277 QTest::addColumn<int>("from");
278 QTest::addColumn<int>("anchor");
279 QTest::addColumn<int>("position");
280
281 QTest::newRow("1") << "Hello World" << "World" << int(QTextDocument::FindCaseSensitively) << 0 << 6 << 11;
282
283 QTest::newRow("2") << QString::fromAscii("Hello") + QString(QChar::ParagraphSeparator) + QString::fromAscii("World")
284 << "World" << int(QTextDocument::FindCaseSensitively) << 1 << 6 << 11;
285
286 QTest::newRow("3") << QString::fromAscii("Hello") + QString(QChar::ParagraphSeparator) + QString::fromAscii("World")
287 << "Hello" << int(QTextDocument::FindCaseSensitively | QTextDocument::FindBackward) << 10 << 0 << 5;
288 QTest::newRow("4wholewords") << QString::fromAscii("Hello Blah World")
289 << "Blah" << int(QTextDocument::FindWholeWords) << 0 << 6 << 10;
290 QTest::newRow("5wholewords") << QString::fromAscii("HelloBlahWorld")
291 << "Blah" << int(QTextDocument::FindWholeWords) << 0 << -1 << -1;
292 QTest::newRow("6wholewords") << QString::fromAscii("HelloBlahWorld Blah Hah")
293 << "Blah" << int(QTextDocument::FindWholeWords) << 0 << 15 << 19;
294 QTest::newRow("7wholewords") << QString::fromAscii("HelloBlahWorld Blah Hah")
295 << "Blah" << int(QTextDocument::FindWholeWords | QTextDocument::FindBackward) << 23 << 15 << 19;
296 QTest::newRow("8wholewords") << QString::fromAscii("Hello: World\n")
297 << "orld" << int(QTextDocument::FindWholeWords) << 0 << -1 << -1;
298
299 QTest::newRow("across-paragraphs") << QString::fromAscii("First Parag\nSecond Parag with a lot more text")
300 << "Parag" << int(QTextDocument::FindBackward)
301 << 15 << 6 << 11;
302
303 QTest::newRow("nbsp") << "Hello" + QString(QChar(QChar::Nbsp)) +"World" << " " << int(QTextDocument::FindCaseSensitively) << 0 << 5 << 6;
304 }
305
find()306 void tst_QTextDocument::find()
307 {
308 QFETCH(QString, haystack);
309 QFETCH(QString, needle);
310 QFETCH(int, flags);
311 QFETCH(int, from);
312 QFETCH(int, anchor);
313 QFETCH(int, position);
314
315 cursor.insertText(haystack);
316 cursor = doc->find(needle, from, QTextDocument::FindFlags(flags));
317
318 if (anchor != -1) {
319 QCOMPARE(cursor.anchor(), anchor);
320 QCOMPARE(cursor.position(), position);
321 } else {
322 QVERIFY(cursor.isNull());
323 }
324
325 //search using a regular expression
326 QRegExp expr(needle);
327 expr.setPatternSyntax(QRegExp::FixedString);
328 QTextDocument::FindFlags flg(flags);
329 expr.setCaseSensitivity((flg & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive);
330 cursor = doc->find(expr, from, flg);
331
332 if (anchor != -1) {
333 QCOMPARE(cursor.anchor(), anchor);
334 QCOMPARE(cursor.position(), position);
335 } else {
336 QVERIFY(cursor.isNull());
337 }
338 }
339
findWithRegExp_data()340 void tst_QTextDocument::findWithRegExp_data()
341 {
342 QTest::addColumn<QString>("haystack");
343 QTest::addColumn<QString>("needle");
344 QTest::addColumn<int>("flags");
345 QTest::addColumn<int>("from");
346 QTest::addColumn<int>("anchor");
347 QTest::addColumn<int>("position");
348
349 // match integers 0 to 99
350 QTest::newRow("1") << "23" << "^\\d\\d?$" << int(QTextDocument::FindCaseSensitively) << 0 << 0 << 2;
351 // match ampersands but not &
352 QTest::newRow("2") << "His & hers & theirs" << "&(?!amp;)"<< int(QTextDocument::FindCaseSensitively) << 0 << 15 << 16;
353 //backward search
354 QTest::newRow("3") << QString::fromAscii("HelloBlahWorld Blah Hah")
355 << "h" << int(QTextDocument::FindBackward) << 18 << 8 << 9;
356
357 }
358
findWithRegExp()359 void tst_QTextDocument::findWithRegExp()
360 {
361 QFETCH(QString, haystack);
362 QFETCH(QString, needle);
363 QFETCH(int, flags);
364 QFETCH(int, from);
365 QFETCH(int, anchor);
366 QFETCH(int, position);
367
368 cursor.insertText(haystack);
369 //search using a regular expression
370 QRegExp expr(needle);
371 QTextDocument::FindFlags flg(flags);
372 expr.setCaseSensitivity((flg & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive);
373 cursor = doc->find(expr, from, flg);
374
375 if (anchor != -1) {
376 QCOMPARE(cursor.anchor(), anchor);
377 QCOMPARE(cursor.position(), position);
378 } else {
379 QVERIFY(cursor.isNull());
380 }
381 }
382
find2()383 void tst_QTextDocument::find2()
384 {
385 doc->setPlainText("aaa");
386 cursor.movePosition(QTextCursor::Start);
387 cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
388 QTextCursor hit = doc->find("a", cursor);
389 QCOMPARE(hit.position(), 2);
390 QCOMPARE(hit.anchor(), 1);
391 }
392
findMultiple()393 void tst_QTextDocument::findMultiple()
394 {
395 const QString text("foo bar baz foo bar baz");
396 doc->setPlainText(text);
397
398 cursor.movePosition(QTextCursor::Start);
399 cursor = doc->find("bar", cursor);
400 QCOMPARE(cursor.selectionStart(), text.indexOf("bar"));
401 QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3);
402 cursor = doc->find("bar", cursor);
403 QCOMPARE(cursor.selectionStart(), text.lastIndexOf("bar"));
404 QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3);
405
406 cursor.movePosition(QTextCursor::End);
407 cursor = doc->find("bar", cursor, QTextDocument::FindBackward);
408 QCOMPARE(cursor.selectionStart(), text.lastIndexOf("bar"));
409 QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3);
410 cursor = doc->find("bar", cursor, QTextDocument::FindBackward);
411 QCOMPARE(cursor.selectionStart(), text.indexOf("bar"));
412 QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3);
413
414
415 QRegExp expr("bar");
416 expr.setPatternSyntax(QRegExp::FixedString);
417
418 cursor.movePosition(QTextCursor::End);
419 cursor = doc->find(expr, cursor, QTextDocument::FindBackward);
420 QCOMPARE(cursor.selectionStart(), text.lastIndexOf("bar"));
421 QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3);
422 cursor = doc->find(expr, cursor, QTextDocument::FindBackward);
423 QCOMPARE(cursor.selectionStart(), text.indexOf("bar"));
424 QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3);
425
426 cursor.movePosition(QTextCursor::Start);
427 cursor = doc->find(expr, cursor);
428 QCOMPARE(cursor.selectionStart(), text.indexOf("bar"));
429 QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3);
430 cursor = doc->find(expr, cursor);
431 QCOMPARE(cursor.selectionStart(), text.lastIndexOf("bar"));
432 QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3);
433 }
434
basicIsModifiedChecks()435 void tst_QTextDocument::basicIsModifiedChecks()
436 {
437 QSignalSpy spy(doc, SIGNAL(modificationChanged(bool)));
438
439 QVERIFY(!doc->isModified());
440 cursor.insertText("Hello World");
441 QVERIFY(doc->isModified());
442 QCOMPARE(spy.count(), 1);
443 QVERIFY(spy.takeFirst().at(0).toBool());
444
445 doc->undo();
446 QVERIFY(!doc->isModified());
447 QCOMPARE(spy.count(), 1);
448 QVERIFY(!spy.takeFirst().at(0).toBool());
449
450 doc->redo();
451 QVERIFY(doc->isModified());
452 QCOMPARE(spy.count(), 1);
453 QVERIFY(spy.takeFirst().at(0).toBool());
454 }
455
moreIsModified()456 void tst_QTextDocument::moreIsModified()
457 {
458 QVERIFY(!doc->isModified());
459
460 cursor.insertText("Hello");
461 QVERIFY(doc->isModified());
462
463 doc->undo();
464 QVERIFY(!doc->isModified());
465
466 cursor.insertText("Hello");
467
468 doc->undo();
469 QVERIFY(!doc->isModified());
470 }
471
isModified2()472 void tst_QTextDocument::isModified2()
473 {
474 // reported on qt4-preview-feedback
475 QVERIFY(!doc->isModified());
476
477 cursor.insertText("Hello");
478 QVERIFY(doc->isModified());
479
480 doc->setModified(false);
481 QVERIFY(!doc->isModified());
482
483 cursor.insertText("Hello");
484 QVERIFY(doc->isModified());
485 }
486
isModified3()487 void tst_QTextDocument::isModified3()
488 {
489 QVERIFY(!doc->isModified());
490
491 doc->setUndoRedoEnabled(false);
492 doc->setUndoRedoEnabled(true);
493
494 cursor.insertText("Hello");
495
496 QVERIFY(doc->isModified());
497 doc->undo();
498 QVERIFY(!doc->isModified());
499 }
500
isModified4()501 void tst_QTextDocument::isModified4()
502 {
503 QVERIFY(!doc->isModified());
504
505 cursor.insertText("Hello");
506 cursor.insertText("World");
507
508 doc->setModified(false);
509
510 QVERIFY(!doc->isModified());
511
512 cursor.insertText("Again");
513 QVERIFY(doc->isModified());
514
515 doc->undo();
516 QVERIFY(!doc->isModified());
517 doc->undo();
518 QVERIFY(doc->isModified());
519
520 doc->redo();
521 QVERIFY(!doc->isModified());
522 doc->redo();
523 QVERIFY(doc->isModified());
524
525 doc->undo();
526 QVERIFY(!doc->isModified());
527 doc->undo();
528 QVERIFY(doc->isModified());
529
530 //task 197769
531 cursor.insertText("Hello");
532 QVERIFY(doc->isModified());
533 }
534
noundo_basicIsModifiedChecks()535 void tst_QTextDocument::noundo_basicIsModifiedChecks()
536 {
537 doc->setUndoRedoEnabled(false);
538 QSignalSpy spy(doc, SIGNAL(modificationChanged(bool)));
539
540 QVERIFY(!doc->isModified());
541 cursor.insertText("Hello World");
542 QVERIFY(doc->isModified());
543 QCOMPARE(spy.count(), 1);
544 QVERIFY(spy.takeFirst().at(0).toBool());
545
546 doc->undo();
547 QVERIFY(doc->isModified());
548 QCOMPARE(spy.count(), 0);
549
550 doc->redo();
551 QVERIFY(doc->isModified());
552 QCOMPARE(spy.count(), 0);
553 }
554
task240325()555 void tst_QTextDocument::task240325()
556 {
557 doc->setHtml("<html><img width=\"100\" height=\"100\" align=\"right\"/>Foobar Foobar Foobar Foobar</html>");
558
559 QImage img(1000, 7000, QImage::Format_ARGB32_Premultiplied);
560 QPainter p(&img);
561 QFontMetrics fm(p.font());
562
563 // Set page size to contain image and one "Foobar"
564 doc->setPageSize(QSize(100 + fm.width("Foobar")*2, 1000));
565
566 // Force layout
567 doc->drawContents(&p);
568
569 QCOMPARE(doc->blockCount(), 1);
570 for (QTextBlock block = doc->begin() ; block!=doc->end() ; block = block.next()) {
571 QTextLayout *layout = block.layout();
572 QCOMPARE(layout->lineCount(), 4);
573 for (int lineIdx=0;lineIdx<layout->lineCount();++lineIdx) {
574 QTextLine line = layout->lineAt(lineIdx);
575
576 QString text = block.text().mid(line.textStart(), line.textLength()).trimmed();
577
578 // Remove start token
579 if (lineIdx == 0)
580 text = text.mid(1);
581
582 QCOMPARE(text, QString::fromLatin1("Foobar"));
583 }
584 }
585 }
586
stylesheetFont_data()587 void tst_QTextDocument::stylesheetFont_data()
588 {
589 QTest::addColumn<QString>("stylesheet");
590 QTest::addColumn<QFont>("font");
591
592 {
593 QFont font;
594 font.setBold(true);
595 font.setPixelSize(64);
596
597 QTest::newRow("Regular font specification")
598 << "font-size: 64px; font-weight: bold;"
599 << font;
600 }
601
602
603 {
604 QFont font;
605 font.setBold(true);
606 font.setPixelSize(64);
607
608 QTest::newRow("Shorthand font specification")
609 << "font: normal bold 64px Arial;"
610 << font;
611 }
612
613 }
614
stylesheetFont()615 void tst_QTextDocument::stylesheetFont()
616 {
617 QFETCH(QString, stylesheet);
618 QFETCH(QFont, font);
619
620 QString html = QString::fromLatin1("<html>"
621 "<body>"
622 "<div style=\"%1\" >"
623 "Foobar"
624 "</div>"
625 "</body>"
626 "</html>").arg(stylesheet);
627
628 qDebug() << html;
629 doc->setHtml(html);
630 QCOMPARE(doc->blockCount(), 1);
631
632 // First and only block
633 QTextBlock block = doc->firstBlock();
634
635 QString text = block.text();
636 QCOMPARE(text, QString::fromLatin1("Foobar"));
637
638 QFont actualFont = block.charFormat().font();
639
640 QCOMPARE(actualFont.bold(), font.bold());
641 QCOMPARE(actualFont.pixelSize(), font.pixelSize());
642 }
643
noundo_moreIsModified()644 void tst_QTextDocument::noundo_moreIsModified()
645 {
646 doc->setUndoRedoEnabled(false);
647 QVERIFY(!doc->isModified());
648
649 cursor.insertText("Hello");
650 QVERIFY(doc->isModified());
651
652 doc->undo();
653 QVERIFY(doc->isModified());
654
655 cursor.insertText("Hello");
656
657 doc->undo();
658 QVERIFY(doc->isModified());
659 }
660
noundo_isModified2()661 void tst_QTextDocument::noundo_isModified2()
662 {
663 // reported on qt4-preview-feedback
664 QVERIFY(!doc->isModified());
665
666 cursor.insertText("Hello");
667 QVERIFY(doc->isModified());
668
669 doc->setModified(false);
670 QVERIFY(!doc->isModified());
671
672 cursor.insertText("Hello");
673 QVERIFY(doc->isModified());
674 }
675
noundo_isModified3()676 void tst_QTextDocument::noundo_isModified3()
677 {
678 doc->setUndoRedoEnabled(false);
679 QVERIFY(!doc->isModified());
680
681 cursor.insertText("Hello");
682
683 QVERIFY(doc->isModified());
684 doc->undo();
685 QVERIFY(doc->isModified());
686 }
687
mightBeRichText_data()688 void tst_QTextDocument::mightBeRichText_data()
689 {
690 const char qtDocuHeader[] = "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"
691 "<!DOCTYPE html\n"
692 " PUBLIC ""-//W3C//DTD XHTML 1.0 Strict//EN\" \"DTD/xhtml1-strict.dtd\">\n"
693 "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">";
694 QVERIFY(Qt::mightBeRichText(QString::fromLatin1(qtDocuHeader)));
695 QTest::addColumn<QString>("input");
696 QTest::addColumn<bool>("result");
697
698 QTest::newRow("documentation-header") << QString("<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"
699 "<!DOCTYPE html\n"
700 " PUBLIC ""-//W3C//DTD XHTML 1.0 Strict//EN\" \"DTD/xhtml1-strict.dtd\">\n"
701 "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">")
702 << true;
703 QTest::newRow("br-nospace") << QString("Test <br/> new line") << true;
704 QTest::newRow("br-space") << QString("Test <br /> new line") << true;
705 QTest::newRow("br-invalidspace") << QString("Test <br/ > new line") << false;
706 QTest::newRow("invalid closing tag") << QString("Test <br/ line") << false;
707 }
708
mightBeRichText()709 void tst_QTextDocument::mightBeRichText()
710 {
711 QFETCH(QString, input);
712 QFETCH(bool, result);
713 QVERIFY(result == Qt::mightBeRichText(input));
714 }
715
Q_DECLARE_METATYPE(QTextDocumentFragment)716 Q_DECLARE_METATYPE(QTextDocumentFragment)
717
718 #define CREATE_DOC_AND_CURSOR() \
719 QTextDocument doc; \
720 doc.setDefaultFont(defaultFont); \
721 QTextCursor cursor(&doc);
722
723 void tst_QTextDocument::toHtml_data()
724 {
725 QTest::addColumn<QTextDocumentFragment>("input");
726 QTest::addColumn<QString>("expectedOutput");
727
728 {
729 CREATE_DOC_AND_CURSOR();
730
731 cursor.insertText("Blah");
732
733 QTest::newRow("simple") << QTextDocumentFragment(&doc) << QString("<p DEFAULTBLOCKSTYLE>Blah</p>");
734 }
735
736 {
737 CREATE_DOC_AND_CURSOR();
738
739 cursor.insertText("&<>");
740
741 QTest::newRow("entities") << QTextDocumentFragment(&doc) << QString("<p DEFAULTBLOCKSTYLE>&<></p>");
742 }
743
744 {
745 CREATE_DOC_AND_CURSOR();
746
747 QTextCharFormat fmt;
748 fmt.setFontFamily("Times");
749 cursor.insertText("Blah", fmt);
750
751 QTest::newRow("font-family") << QTextDocumentFragment(&doc)
752 << QString("<p DEFAULTBLOCKSTYLE><span style=\" font-family:'Times';\">Blah</span></p>");
753 }
754
755 {
756 CREATE_DOC_AND_CURSOR();
757
758 QTextCharFormat fmt;
759 fmt.setFontFamily("Foo's Family");
760 cursor.insertText("Blah", fmt);
761
762 QTest::newRow("font-family-with-quotes1") << QTextDocumentFragment(&doc)
763 << QString("<p DEFAULTBLOCKSTYLE><span style=\" font-family:"Foo's Family";\">Blah</span></p>");
764 }
765
766 {
767 CREATE_DOC_AND_CURSOR();
768
769 QTextCharFormat fmt;
770 fmt.setFontFamily("Foo\"s Family");
771 cursor.insertText("Blah", fmt);
772
773 QTest::newRow("font-family-with-quotes2") << QTextDocumentFragment(&doc)
774 << QString("<p DEFAULTBLOCKSTYLE><span style=\" font-family:'Foo"s Family';\">Blah</span></p>");
775 }
776
777 {
778 CREATE_DOC_AND_CURSOR();
779
780 QTextBlockFormat fmt;
781 fmt.setNonBreakableLines(true);
782 cursor.insertBlock(fmt);
783 cursor.insertText("Blah");
784
785 QTest::newRow("pre") << QTextDocumentFragment(&doc)
786 <<
787 QString("EMPTYBLOCK") +
788 QString("<pre DEFAULTBLOCKSTYLE>Blah</pre>");
789 }
790
791 {
792 CREATE_DOC_AND_CURSOR();
793
794 QTextCharFormat fmt;
795 fmt.setFontPointSize(40);
796 cursor.insertText("Blah", fmt);
797
798 QTest::newRow("font-size") << QTextDocumentFragment(&doc)
799 << QString("<p DEFAULTBLOCKSTYLE><span style=\" font-size:40pt;\">Blah</span></p>");
800 }
801
802 {
803 CREATE_DOC_AND_CURSOR();
804
805 QTextCharFormat fmt;
806 fmt.setProperty(QTextFormat::FontSizeIncrement, 2);
807 cursor.insertText("Blah", fmt);
808
809 QTest::newRow("logical-font-size") << QTextDocumentFragment(&doc)
810 << QString("<p DEFAULTBLOCKSTYLE><span style=\" font-size:x-large;\">Blah</span></p>");
811 }
812
813 {
814 CREATE_DOC_AND_CURSOR();
815
816 cursor.insertText("Foo");
817
818 QTextCharFormat fmt;
819 fmt.setFontPointSize(40);
820 cursor.insertBlock(QTextBlockFormat(), fmt);
821
822 fmt.clearProperty(QTextFormat::FontPointSize);
823 cursor.insertText("Blub", fmt);
824
825 QTest::newRow("no-font-size") << QTextDocumentFragment(&doc)
826 << QString("<p DEFAULTBLOCKSTYLE>Foo</p>\n<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">Blub</p>");
827 }
828
829 {
830 CREATE_DOC_AND_CURSOR();
831
832 QTextBlockFormat fmt;
833 fmt.setLayoutDirection(Qt::RightToLeft);
834 cursor.insertBlock(fmt);
835 cursor.insertText("Blah");
836
837 QTest::newRow("rtl") << QTextDocumentFragment(&doc)
838 <<
839 QString("EMPTYBLOCK") +
840 QString("<p dir='rtl' DEFAULTBLOCKSTYLE>Blah</p>");
841 }
842
843 {
844 CREATE_DOC_AND_CURSOR();
845
846 QTextBlockFormat fmt;
847 fmt.setAlignment(Qt::AlignJustify);
848 cursor.insertBlock(fmt);
849 cursor.insertText("Blah");
850
851 QTest::newRow("blockalign") << QTextDocumentFragment(&doc)
852 <<
853 QString("EMPTYBLOCK") +
854 QString("<p align=\"justify\" DEFAULTBLOCKSTYLE>Blah</p>");
855 }
856
857 {
858 CREATE_DOC_AND_CURSOR();
859
860 QTextBlockFormat fmt;
861 fmt.setAlignment(Qt::AlignCenter);
862 cursor.insertBlock(fmt);
863 cursor.insertText("Blah");
864
865 QTest::newRow("blockalign2") << QTextDocumentFragment(&doc)
866 <<
867 QString("EMPTYBLOCK") +
868 QString("<p align=\"center\" DEFAULTBLOCKSTYLE>Blah</p>");
869 }
870
871 {
872 CREATE_DOC_AND_CURSOR();
873
874 QTextBlockFormat fmt;
875 fmt.setAlignment(Qt::AlignRight | Qt::AlignAbsolute);
876 cursor.insertBlock(fmt);
877 cursor.insertText("Blah");
878
879 QTest::newRow("blockalign3") << QTextDocumentFragment(&doc)
880 <<
881 QString("EMPTYBLOCK") +
882 QString("<p align=\"right\" DEFAULTBLOCKSTYLE>Blah</p>");
883 }
884
885 {
886 CREATE_DOC_AND_CURSOR();
887
888 QTextBlockFormat fmt;
889 fmt.setBackground(QColor("#0000ff"));
890 cursor.insertBlock(fmt);
891 cursor.insertText("Blah");
892
893 QTest::newRow("bgcolor") << QTextDocumentFragment(&doc)
894 << QString("EMPTYBLOCK") +
895 QString("<p OPENDEFAULTBLOCKSTYLE background-color:#0000ff;\">Blah</p>");
896 }
897
898 {
899 CREATE_DOC_AND_CURSOR();
900
901 QTextCharFormat fmt;
902 fmt.setFontWeight(40);
903 cursor.insertText("Blah", fmt);
904
905 QTest::newRow("font-weight") << QTextDocumentFragment(&doc)
906 << QString("<p DEFAULTBLOCKSTYLE><span style=\" font-weight:320;\">Blah</span></p>");
907 }
908
909 {
910 CREATE_DOC_AND_CURSOR();
911
912 QTextCharFormat fmt;
913 fmt.setFontItalic(true);
914 cursor.insertText("Blah", fmt);
915
916 QTest::newRow("font-italic") << QTextDocumentFragment(&doc)
917 << QString("<p DEFAULTBLOCKSTYLE><span style=\" font-style:italic;\">Blah</span></p>");
918 }
919
920 {
921 CREATE_DOC_AND_CURSOR();
922
923 QTextCharFormat fmt;
924 fmt.setFontUnderline(true);
925 fmt.setFontOverline(false);
926 cursor.insertText("Blah", fmt);
927
928 QTest::newRow("text-decoration-1") << QTextDocumentFragment(&doc)
929 << QString("<p DEFAULTBLOCKSTYLE><span style=\" text-decoration: underline;\">Blah</span></p>");
930 }
931
932 {
933 CREATE_DOC_AND_CURSOR();
934
935 QTextCharFormat fmt;
936 fmt.setForeground(QColor("#00ff00"));
937 cursor.insertText("Blah", fmt);
938
939 QTest::newRow("color") << QTextDocumentFragment(&doc)
940 << QString("<p DEFAULTBLOCKSTYLE><span style=\" color:#00ff00;\">Blah</span></p>");
941 }
942
943 {
944 CREATE_DOC_AND_CURSOR();
945
946 QTextCharFormat fmt;
947 fmt.setBackground(QColor("#00ff00"));
948 cursor.insertText("Blah", fmt);
949
950 QTest::newRow("span-bgcolor") << QTextDocumentFragment(&doc)
951 << QString("<p DEFAULTBLOCKSTYLE><span style=\" background-color:#00ff00;\">Blah</span></p>");
952 }
953
954 {
955 CREATE_DOC_AND_CURSOR();
956
957 QTextCharFormat fmt;
958 fmt.setVerticalAlignment(QTextCharFormat::AlignSubScript);
959 cursor.insertText("Blah", fmt);
960
961 QTest::newRow("valign-sub") << QTextDocumentFragment(&doc)
962 << QString("<p DEFAULTBLOCKSTYLE><span style=\" vertical-align:sub;\">Blah</span></p>");
963
964 }
965
966 {
967 CREATE_DOC_AND_CURSOR();
968
969 QTextCharFormat fmt;
970 fmt.setVerticalAlignment(QTextCharFormat::AlignSuperScript);
971 cursor.insertText("Blah", fmt);
972
973 QTest::newRow("valign-super") << QTextDocumentFragment(&doc)
974 << QString("<p DEFAULTBLOCKSTYLE><span style=\" vertical-align:super;\">Blah</span></p>");
975
976 }
977
978 {
979 CREATE_DOC_AND_CURSOR();
980
981 QTextCharFormat fmt;
982 fmt.setAnchor(true);
983 fmt.setAnchorName("blub");
984 cursor.insertText("Blah", fmt);
985
986 QTest::newRow("named anchor") << QTextDocumentFragment(&doc)
987 << QString("<p DEFAULTBLOCKSTYLE><a name=\"blub\"></a>Blah</p>");
988 }
989
990 {
991 CREATE_DOC_AND_CURSOR();
992
993 QTextCharFormat fmt;
994 fmt.setAnchor(true);
995 fmt.setAnchorHref("http://www.kde.org/");
996 cursor.insertText("Blah", fmt);
997
998 QTest::newRow("href anchor") << QTextDocumentFragment(&doc)
999 << QString("<p DEFAULTBLOCKSTYLE><a href=\"http://www.kde.org/\">Blah</a></p>");
1000 }
1001
1002 {
1003 CREATE_DOC_AND_CURSOR();
1004
1005 QTextCharFormat fmt;
1006 fmt.setAnchor(true);
1007 fmt.setAnchorHref("http://www.kde.org/?a=1&b=2");
1008 cursor.insertText("Blah", fmt);
1009
1010 QTest::newRow("href anchor with &") << QTextDocumentFragment(&doc)
1011 << QString("<p DEFAULTBLOCKSTYLE><a href=\"http://www.kde.org/?a=1&b=2\">Blah</a></p>");
1012 }
1013
1014 {
1015 CREATE_DOC_AND_CURSOR();
1016
1017 QTextCharFormat fmt;
1018 fmt.setAnchor(true);
1019 fmt.setAnchorHref("http://www.kde.org/?a='&b=\"");
1020 cursor.insertText("Blah", fmt);
1021
1022 QTest::newRow("href anchor with ' and \"") << QTextDocumentFragment(&doc)
1023 << QString("<p DEFAULTBLOCKSTYLE><a href=\"http://www.kde.org/?a='&b="\">Blah</a></p>");
1024 }
1025
1026 {
1027 CREATE_DOC_AND_CURSOR();
1028
1029 cursor.insertTable(2, 2);
1030
1031 QTest::newRow("simpletable") << QTextDocumentFragment(&doc)
1032 << QString("<table border=\"1\" cellspacing=\"2\">"
1033 "\n<tr>\n<td></td>\n<td></td></tr>"
1034 "\n<tr>\n<td></td>\n<td></td></tr>"
1035 "</table>");
1036 }
1037
1038 {
1039 CREATE_DOC_AND_CURSOR();
1040
1041 QTextTable *table = cursor.insertTable(1, 4);
1042 table->mergeCells(0, 0, 1, 2);
1043 table->mergeCells(0, 2, 1, 2);
1044
1045 QTest::newRow("tablespans") << QTextDocumentFragment(&doc)
1046 << QString("<table border=\"1\" cellspacing=\"2\">"
1047 "\n<tr>\n<td colspan=\"2\"></td>\n<td colspan=\"2\"></td></tr>"
1048 "</table>");
1049 }
1050
1051 {
1052 CREATE_DOC_AND_CURSOR();
1053
1054 QTextTableFormat fmt;
1055 fmt.setBorder(1);
1056 fmt.setCellSpacing(3);
1057 fmt.setCellPadding(3);
1058 fmt.setBackground(QColor("#ff00ff"));
1059 fmt.setWidth(QTextLength(QTextLength::PercentageLength, 50));
1060 fmt.setAlignment(Qt::AlignHCenter);
1061 fmt.setPosition(QTextFrameFormat::FloatRight);
1062 cursor.insertTable(2, 2, fmt);
1063
1064 QTest::newRow("tableattrs") << QTextDocumentFragment(&doc)
1065 << QString("<table border=\"1\" style=\" float: right;\" align=\"center\" width=\"50%\" cellspacing=\"3\" cellpadding=\"3\" bgcolor=\"#ff00ff\">"
1066 "\n<tr>\n<td></td>\n<td></td></tr>"
1067 "\n<tr>\n<td></td>\n<td></td></tr>"
1068 "</table>");
1069 }
1070
1071 {
1072 CREATE_DOC_AND_CURSOR();
1073
1074 QTextTableFormat fmt;
1075 fmt.setBorder(1);
1076 fmt.setCellSpacing(3);
1077 fmt.setCellPadding(3);
1078 fmt.setBackground(QColor("#ff00ff"));
1079 fmt.setWidth(QTextLength(QTextLength::PercentageLength, 50));
1080 fmt.setAlignment(Qt::AlignHCenter);
1081 fmt.setPosition(QTextFrameFormat::FloatRight);
1082 fmt.setLeftMargin(25);
1083 fmt.setBottomMargin(35);
1084 cursor.insertTable(2, 2, fmt);
1085
1086 QTest::newRow("tableattrs2") << QTextDocumentFragment(&doc)
1087 << QString("<table border=\"1\" style=\" float: right; margin-top:0px; margin-bottom:35px; margin-left:25px; margin-right:0px;\" align=\"center\" width=\"50%\" cellspacing=\"3\" cellpadding=\"3\" bgcolor=\"#ff00ff\">"
1088 "\n<tr>\n<td></td>\n<td></td></tr>"
1089 "\n<tr>\n<td></td>\n<td></td></tr>"
1090 "</table>");
1091 }
1092
1093 {
1094 CREATE_DOC_AND_CURSOR();
1095
1096 QTextTableFormat fmt;
1097 fmt.setHeaderRowCount(2);
1098 cursor.insertTable(4, 2, fmt);
1099
1100 QTest::newRow("tableheader") << QTextDocumentFragment(&doc)
1101 << QString("<table border=\"1\" cellspacing=\"2\">"
1102 "<thead>\n<tr>\n<td></td>\n<td></td></tr>"
1103 "\n<tr>\n<td></td>\n<td></td></tr></thead>"
1104 "\n<tr>\n<td></td>\n<td></td></tr>"
1105 "\n<tr>\n<td></td>\n<td></td></tr>"
1106 "</table>");
1107 }
1108
1109 {
1110 CREATE_DOC_AND_CURSOR();
1111
1112 QTextTable *table = cursor.insertTable(2, 2);
1113 QTextTable *subTable = table->cellAt(0, 1).firstCursorPosition().insertTable(1, 1);
1114 subTable->cellAt(0, 0).firstCursorPosition().insertText("Hey");
1115
1116 QTest::newRow("nestedtable") << QTextDocumentFragment(&doc)
1117 << QString("<table border=\"1\" cellspacing=\"2\">"
1118 "\n<tr>\n<td></td>\n<td>\n<table border=\"1\" cellspacing=\"2\">\n<tr>\n<td>\n<p DEFAULTBLOCKSTYLE>Hey</p></td></tr></table></td></tr>"
1119 "\n<tr>\n<td></td>\n<td></td></tr>"
1120 "</table>");
1121 }
1122
1123 {
1124 CREATE_DOC_AND_CURSOR();
1125
1126 QTextTableFormat fmt;
1127 QVector<QTextLength> widths;
1128 widths.append(QTextLength());
1129 widths.append(QTextLength(QTextLength::PercentageLength, 30));
1130 widths.append(QTextLength(QTextLength::FixedLength, 40));
1131 fmt.setColumnWidthConstraints(widths);
1132 cursor.insertTable(1, 3, fmt);
1133
1134 QTest::newRow("colwidths") << QTextDocumentFragment(&doc)
1135 << QString("<table border=\"1\" cellspacing=\"2\">"
1136 "\n<tr>\n<td></td>\n<td width=\"30%\"></td>\n<td width=\"40\"></td></tr>"
1137 "</table>");
1138 }
1139
1140 // ### rowspan/colspan tests, once texttable api for that is back again
1141 //
1142 {
1143 CREATE_DOC_AND_CURSOR();
1144
1145 QTextTable *table = cursor.insertTable(1, 1);
1146 QTextCursor cellCurs = table->cellAt(0, 0).firstCursorPosition();
1147 QTextCharFormat fmt;
1148 fmt.setBackground(QColor("#ffffff"));
1149 cellCurs.mergeBlockCharFormat(fmt);
1150
1151 QTest::newRow("cellproperties") << QTextDocumentFragment(&doc)
1152 << QString("<table border=\"1\" cellspacing=\"2\">"
1153 "\n<tr>\n<td bgcolor=\"#ffffff\"></td></tr>"
1154 "</table>");
1155 }
1156
1157 {
1158 CREATE_DOC_AND_CURSOR();
1159
1160 // ### fixme: use programmatic api as soon as we can create floats through it
1161 const char html[] = "<html><body>Blah<img src=\"image.png\" width=\"10\" height=\"20\" style=\"float: right;\" />Blubb</body></html>";
1162
1163 QTest::newRow("image") << QTextDocumentFragment::fromHtml(QString::fromLatin1(html))
1164 << QString("<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">Blah<img src=\"image.png\" width=\"10\" height=\"20\" style=\"float: right;\" />Blubb</p>");
1165 }
1166
1167 {
1168 CREATE_DOC_AND_CURSOR();
1169
1170 QTextImageFormat fmt;
1171 fmt.setName("foo");
1172 fmt.setVerticalAlignment(QTextCharFormat::AlignMiddle);
1173 cursor.insertImage(fmt);
1174
1175 QTest::newRow("image-malign") << QTextDocumentFragment(&doc)
1176 << QString("<p DEFAULTBLOCKSTYLE><img src=\"foo\" style=\"vertical-align: middle;\" /></p>");
1177 }
1178
1179 {
1180 CREATE_DOC_AND_CURSOR();
1181
1182 QTextImageFormat fmt;
1183 fmt.setName("foo");
1184 fmt.setVerticalAlignment(QTextCharFormat::AlignTop);
1185 cursor.insertImage(fmt);
1186
1187 QTest::newRow("image-malign") << QTextDocumentFragment(&doc)
1188 << QString("<p DEFAULTBLOCKSTYLE><img src=\"foo\" style=\"vertical-align: top;\" /></p>");
1189 }
1190
1191 {
1192 CREATE_DOC_AND_CURSOR();
1193
1194 QTextImageFormat fmt;
1195 fmt.setName("foo");
1196 cursor.insertImage(fmt);
1197 cursor.insertImage(fmt);
1198
1199 QTest::newRow("2images") << QTextDocumentFragment(&doc)
1200 << QString("<p DEFAULTBLOCKSTYLE><img src=\"foo\" /><img src=\"foo\" /></p>");
1201 }
1202
1203 {
1204 CREATE_DOC_AND_CURSOR();
1205
1206 QString txt = QLatin1String("Blah");
1207 txt += QChar::LineSeparator;
1208 txt += QLatin1String("Bar");
1209 cursor.insertText(txt);
1210
1211 QTest::newRow("linebreaks") << QTextDocumentFragment(&doc)
1212 << QString("<p DEFAULTBLOCKSTYLE>Blah<br />Bar</p>");
1213 }
1214
1215 {
1216 CREATE_DOC_AND_CURSOR();
1217
1218 QTextBlockFormat fmt;
1219 fmt.setTopMargin(10);
1220 fmt.setBottomMargin(20);
1221 fmt.setLeftMargin(30);
1222 fmt.setRightMargin(40);
1223 cursor.insertBlock(fmt);
1224 cursor.insertText("Blah");
1225
1226 QTest::newRow("blockmargins") << QTextDocumentFragment(&doc)
1227 <<
1228 QString("EMPTYBLOCK") +
1229 QString("<p style=\" margin-top:10px; margin-bottom:20px; margin-left:30px; margin-right:40px; -qt-block-indent:0; text-indent:0px;\">Blah</p>");
1230 }
1231
1232 {
1233 CREATE_DOC_AND_CURSOR();
1234
1235 QTextList *list = cursor.insertList(QTextListFormat::ListDisc);
1236 cursor.insertText("Blubb");
1237 cursor.insertBlock();
1238 cursor.insertText("Blah");
1239 QCOMPARE(list->count(), 2);
1240
1241 QTest::newRow("lists") << QTextDocumentFragment(&doc)
1242 <<
1243 QString("EMPTYBLOCK") +
1244 QString("<ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;\"><li DEFAULTBLOCKSTYLE>Blubb</li>\n<li DEFAULTBLOCKSTYLE>Blah</li></ul>");
1245 }
1246
1247 {
1248 CREATE_DOC_AND_CURSOR();
1249
1250 QTextList *list = cursor.insertList(QTextListFormat::ListDisc);
1251 cursor.insertText("Blubb");
1252
1253 cursor.insertBlock();
1254
1255 QTextCharFormat blockCharFmt;
1256 blockCharFmt.setForeground(QColor("#0000ff"));
1257 cursor.mergeBlockCharFormat(blockCharFmt);
1258
1259 QTextCharFormat fmt;
1260 fmt.setForeground(QColor("#ff0000"));
1261 cursor.insertText("Blah", fmt);
1262 QCOMPARE(list->count(), 2);
1263
1264 QTest::newRow("charfmt-for-list-item") << QTextDocumentFragment(&doc)
1265 <<
1266 QString("EMPTYBLOCK") +
1267 QString("<ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;\"><li DEFAULTBLOCKSTYLE>Blubb</li>\n<li style=\" color:#0000ff;\" DEFAULTBLOCKSTYLE><span style=\" color:#ff0000;\">Blah</span></li></ul>");
1268 }
1269
1270 {
1271 CREATE_DOC_AND_CURSOR();
1272
1273 QTextBlockFormat fmt;
1274 fmt.setIndent(3);
1275 fmt.setTextIndent(30);
1276 cursor.insertBlock(fmt);
1277 cursor.insertText("Test");
1278
1279 QTest::newRow("block-indent") << QTextDocumentFragment(&doc)
1280 <<
1281 QString("EMPTYBLOCK") +
1282 QString("<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:3; text-indent:30px;\">Test</p>");
1283 }
1284
1285 {
1286 CREATE_DOC_AND_CURSOR();
1287
1288 QTextListFormat fmt;
1289 fmt.setStyle(QTextListFormat::ListDisc);
1290 fmt.setIndent(4);
1291 cursor.insertList(fmt);
1292 cursor.insertText("Blah");
1293
1294 QTest::newRow("list-indent") << QTextDocumentFragment(&doc)
1295 <<
1296 QString("EMPTYBLOCK") +
1297 QString("<ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 4;\"><li DEFAULTBLOCKSTYLE>Blah</li></ul>");
1298 }
1299
1300 {
1301 CREATE_DOC_AND_CURSOR();
1302
1303 cursor.insertBlock();
1304
1305
1306 QTest::newRow("emptyblock") << QTextDocumentFragment(&doc)
1307 // after insertBlock() we /do/ have two blocks in the document, so also expect
1308 // these in the html output
1309 << QString("EMPTYBLOCK") + QString("EMPTYBLOCK");
1310 }
1311
1312 {
1313 CREATE_DOC_AND_CURSOR();
1314
1315 // if you press enter twice in an empty textedit and then insert 'Test'
1316 // you actually get three visible paragraphs, two empty leading ones and
1317 // a third with the actual text. the corresponding html representation
1318 // therefore should also contain three paragraphs.
1319
1320 cursor.insertBlock();
1321 QTextCharFormat fmt;
1322 fmt.setForeground(QColor("#00ff00"));
1323 fmt.setProperty(QTextFormat::FontSizeIncrement, 1);
1324 cursor.mergeBlockCharFormat(fmt);
1325
1326 fmt.setProperty(QTextFormat::FontSizeIncrement, 2);
1327 cursor.insertText("Test", fmt);
1328
1329 QTest::newRow("blockcharfmt") << QTextDocumentFragment(&doc)
1330 << QString("EMPTYBLOCK<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:x-large; color:#00ff00;\">Test</span></p>");
1331 }
1332
1333 {
1334 CREATE_DOC_AND_CURSOR();
1335
1336 QTextCharFormat fmt;
1337 fmt.setForeground(QColor("#00ff00"));
1338 cursor.setBlockCharFormat(fmt);
1339 fmt.setForeground(QColor("#0000ff"));
1340 cursor.insertText("Test", fmt);
1341
1342 QTest::newRow("blockcharfmt2") << QTextDocumentFragment(&doc)
1343 << QString("<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" color:#0000ff;\">Test</span></p>");
1344 }
1345
1346 {
1347 QTest::newRow("horizontal-ruler") << QTextDocumentFragment::fromHtml("<hr />")
1348 <<
1349 QString("EMPTYBLOCK") +
1350 QString("<hr />");
1351 }
1352 {
1353 QTest::newRow("horizontal-ruler-with-width") << QTextDocumentFragment::fromHtml("<hr width=\"50%\"/>")
1354 <<
1355 QString("EMPTYBLOCK") +
1356 QString("<hr width=\"50%\"/>");
1357 }
1358 {
1359 CREATE_DOC_AND_CURSOR();
1360
1361 QTextFrame *mainFrame = cursor.currentFrame();
1362
1363 QTextFrameFormat ffmt;
1364 ffmt.setBorder(1);
1365 ffmt.setPosition(QTextFrameFormat::FloatRight);
1366 ffmt.setMargin(2);
1367 ffmt.setWidth(100);
1368 ffmt.setHeight(50);
1369 ffmt.setBackground(QColor("#00ff00"));
1370 cursor.insertFrame(ffmt);
1371 cursor.insertText("Hello World");
1372 cursor = mainFrame->lastCursorPosition();
1373
1374 QTest::newRow("frame") << QTextDocumentFragment(&doc)
1375 << QString("<table border=\"1\" style=\"-qt-table-type: frame; float: right; margin-top:2px; margin-bottom:2px; margin-left:2px; margin-right:2px;\" width=\"100\" height=\"50\" bgcolor=\"#00ff00\">\n<tr>\n<td style=\"border: none;\">\n<p DEFAULTBLOCKSTYLE>Hello World</p></td></tr></table>");
1376 }
1377
1378 {
1379 CREATE_DOC_AND_CURSOR();
1380
1381 QTextCharFormat fmt;
1382 fmt.setForeground(QColor("#00ff00"));
1383 // fmt.setBackground(QColor("#0000ff"));
1384 cursor.setBlockCharFormat(fmt);
1385
1386 fmt.setForeground(QBrush());
1387 // fmt.setBackground(QBrush());
1388 cursor.insertText("Test", fmt);
1389
1390 // QTest::newRow("nostylebrush") << QTextDocumentFragment(&doc) << QString("<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; color:#00ff00; -qt-blockcharfmt-background-color:#0000ff;\">Test</p>");
1391 QTest::newRow("nostylebrush") << QTextDocumentFragment(&doc) << QString("<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">Test</p>");
1392 }
1393
1394 {
1395 CREATE_DOC_AND_CURSOR();
1396
1397 QTextTable *table = cursor.insertTable(2, 2);
1398 table->mergeCells(0, 0, 1, 2);
1399 QTextTableFormat fmt = table->format();
1400 QVector<QTextLength> widths;
1401 widths.append(QTextLength(QTextLength::FixedLength, 20));
1402 widths.append(QTextLength(QTextLength::FixedLength, 40));
1403 fmt.setColumnWidthConstraints(widths);
1404 table->setFormat(fmt);
1405
1406 QTest::newRow("mergedtablecolwidths") << QTextDocumentFragment(&doc)
1407 << QString("<table border=\"1\" cellspacing=\"2\">"
1408 "\n<tr>\n<td colspan=\"2\"></td></tr>"
1409 "\n<tr>\n<td width=\"20\"></td>\n<td width=\"40\"></td></tr>"
1410 "</table>");
1411 }
1412
1413 {
1414 CREATE_DOC_AND_CURSOR();
1415
1416 QTextCharFormat fmt;
1417
1418 cursor.insertText("Blah\nGreen yellow green");
1419 cursor.setPosition(0);
1420 cursor.setPosition(23, QTextCursor::KeepAnchor);
1421 fmt.setBackground(Qt::green);
1422 cursor.mergeCharFormat(fmt);
1423 cursor.clearSelection();
1424 cursor.setPosition(11);
1425 cursor.setPosition(17, QTextCursor::KeepAnchor);
1426 fmt.setBackground(Qt::yellow);
1427 cursor.mergeCharFormat(fmt);
1428 cursor.clearSelection();
1429
1430 QTest::newRow("multiparagraph-bgcolor") << QTextDocumentFragment(&doc)
1431 << QString("<p DEFAULTBLOCKSTYLE><span style=\" background-color:#00ff00;\">Blah</span></p>\n"
1432 "<p DEFAULTBLOCKSTYLE><span style=\" background-color:#00ff00;\">Green </span>"
1433 "<span style=\" background-color:#ffff00;\">yellow</span>"
1434 "<span style=\" background-color:#00ff00;\"> green</span></p>");
1435 }
1436
1437 {
1438 CREATE_DOC_AND_CURSOR();
1439
1440 QTextBlockFormat fmt;
1441 fmt.setBackground(QColor("#0000ff"));
1442 cursor.insertBlock(fmt);
1443
1444 QTextCharFormat charfmt;
1445 charfmt.setBackground(QColor("#0000ff"));
1446 cursor.insertText("Blah", charfmt);
1447
1448 QTest::newRow("nospan-bgcolor") << QTextDocumentFragment(&doc)
1449 << QString("EMPTYBLOCK") +
1450 QString("<p OPENDEFAULTBLOCKSTYLE background-color:#0000ff;\"><span style=\" background-color:#0000ff;\">Blah</span></p>");
1451 }
1452
1453 {
1454 CREATE_DOC_AND_CURSOR();
1455
1456 QTextTable *table = cursor.insertTable(2, 2);
1457 QTextCharFormat fmt = table->cellAt(0, 0).format();
1458 fmt.setVerticalAlignment(QTextCharFormat::AlignMiddle);
1459 table->cellAt(0, 0).setFormat(fmt);
1460 fmt = table->cellAt(0, 1).format();
1461 fmt.setVerticalAlignment(QTextCharFormat::AlignTop);
1462 table->cellAt(0, 1).setFormat(fmt);
1463 fmt = table->cellAt(1, 0).format();
1464 fmt.setVerticalAlignment(QTextCharFormat::AlignBottom);
1465 table->cellAt(1, 0).setFormat(fmt);
1466
1467 table->cellAt(0, 0).firstCursorPosition().insertText("Blah");
1468
1469 QTest::newRow("table-vertical-alignment") << QTextDocumentFragment(&doc)
1470 << QString("<table border=\"1\" cellspacing=\"2\">"
1471 "\n<tr>\n<td style=\" vertical-align:middle;\">\n"
1472 "<p DEFAULTBLOCKSTYLE>Blah</p></td>"
1473 "\n<td style=\" vertical-align:top;\"></td></tr>"
1474 "\n<tr>\n<td style=\" vertical-align:bottom;\"></td>"
1475 "\n<td></td></tr>"
1476 "</table>");
1477 }
1478
1479 {
1480 CREATE_DOC_AND_CURSOR();
1481
1482 QTextTable *table = cursor.insertTable(2, 2);
1483 QTextTableCellFormat fmt = table->cellAt(0, 0).format().toTableCellFormat();
1484 fmt.setLeftPadding(1);
1485 table->cellAt(0, 0).setFormat(fmt);
1486 fmt = table->cellAt(0, 1).format().toTableCellFormat();
1487 fmt.setRightPadding(1);
1488 table->cellAt(0, 1).setFormat(fmt);
1489 fmt = table->cellAt(1, 0).format().toTableCellFormat();
1490 fmt.setTopPadding(1);
1491 table->cellAt(1, 0).setFormat(fmt);
1492 fmt = table->cellAt(1, 1).format().toTableCellFormat();
1493 fmt.setBottomPadding(1);
1494 table->cellAt(1, 1).setFormat(fmt);
1495
1496 table->cellAt(0, 0).firstCursorPosition().insertText("Blah");
1497
1498 QTest::newRow("table-cell-paddings") << QTextDocumentFragment(&doc)
1499 << QString("<table border=\"1\" cellspacing=\"2\">"
1500 "\n<tr>\n<td style=\" padding-left:1;\">\n"
1501 "<p DEFAULTBLOCKSTYLE>Blah</p></td>"
1502 "\n<td style=\" padding-right:1;\"></td></tr>"
1503 "\n<tr>\n<td style=\" padding-top:1;\"></td>"
1504 "\n<td style=\" padding-bottom:1;\"></td></tr>"
1505 "</table>");
1506 }
1507
1508 {
1509 CREATE_DOC_AND_CURSOR();
1510
1511 QTextTableFormat fmt;
1512 fmt.setBorderBrush(QColor("#0000ff"));
1513 fmt.setBorderStyle(QTextFrameFormat::BorderStyle_Solid);
1514 cursor.insertTable(2, 2, fmt);
1515
1516 QTest::newRow("tableborder") << QTextDocumentFragment(&doc)
1517 << QString("<table border=\"1\" style=\" border-color:#0000ff; border-style:solid;\" cellspacing=\"2\">"
1518 "\n<tr>\n<td></td>\n<td></td></tr>"
1519 "\n<tr>\n<td></td>\n<td></td></tr>"
1520 "</table>");
1521 }
1522
1523 {
1524 CREATE_DOC_AND_CURSOR();
1525
1526 cursor.insertBlock();
1527 cursor.insertText("Foo");
1528
1529 cursor.block().setUserState(42);
1530
1531 QTest::newRow("userstate") << QTextDocumentFragment(&doc)
1532 << QString("EMPTYBLOCK") +
1533 QString("<p OPENDEFAULTBLOCKSTYLE -qt-user-state:42;\">Foo</p>");
1534 }
1535
1536 {
1537 CREATE_DOC_AND_CURSOR();
1538
1539 QTextBlockFormat blockFmt;
1540 blockFmt.setPageBreakPolicy(QTextFormat::PageBreak_AlwaysBefore);
1541
1542 cursor.insertBlock(blockFmt);
1543 cursor.insertText("Foo");
1544
1545 blockFmt.setPageBreakPolicy(QTextFormat::PageBreak_AlwaysBefore | QTextFormat::PageBreak_AlwaysAfter);
1546
1547 cursor.insertBlock(blockFmt);
1548 cursor.insertText("Bar");
1549
1550 QTextTableFormat tableFmt;
1551 tableFmt.setPageBreakPolicy(QTextFormat::PageBreak_AlwaysAfter);
1552
1553 cursor.insertTable(1, 1, tableFmt);
1554
1555 QTest::newRow("pagebreak") << QTextDocumentFragment(&doc)
1556 << QString("EMPTYBLOCK") +
1557 QString("<p OPENDEFAULTBLOCKSTYLE page-break-before:always;\">Foo</p>"
1558 "\n<p OPENDEFAULTBLOCKSTYLE page-break-before:always; page-break-after:always;\">Bar</p>"
1559 "\n<table border=\"1\" style=\" page-break-after:always;\" cellspacing=\"2\">\n<tr>\n<td></td></tr></table>");
1560 }
1561
1562 {
1563 CREATE_DOC_AND_CURSOR();
1564
1565 QTextListFormat listFmt;
1566 listFmt.setStyle(QTextListFormat::ListDisc);
1567
1568 cursor.insertList(listFmt);
1569 cursor.insertText("Blah");
1570
1571 QTest::newRow("list-ul-margin") << QTextDocumentFragment(&doc)
1572 << QString("EMPTYBLOCK") +
1573 QString("<ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;\"><li DEFAULTBLOCKSTYLE>Blah</li></ul>");
1574 }
1575 }
1576
toHtml()1577 void tst_QTextDocument::toHtml()
1578 {
1579 QFETCH(QTextDocumentFragment, input);
1580 QFETCH(QString, expectedOutput);
1581
1582 cursor.insertFragment(input);
1583
1584 expectedOutput.prepend(htmlHead);
1585
1586 expectedOutput.replace("OPENDEFAULTBLOCKSTYLE", "style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;");
1587 expectedOutput.replace("DEFAULTBLOCKSTYLE", "style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"");
1588 expectedOutput.replace("EMPTYBLOCK", "<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><br /></p>\n");
1589 if (expectedOutput.endsWith(QLatin1Char('\n')))
1590 expectedOutput.chop(1);
1591 expectedOutput.append(htmlTail);
1592
1593 QString output = doc->toHtml();
1594
1595 QCOMPARE(output, expectedOutput);
1596
1597 QDomDocument document;
1598 QVERIFY2(document.setContent(output), "Output was not valid XML");
1599 }
1600
toHtml2()1601 void tst_QTextDocument::toHtml2()
1602 {
1603 QTextDocument doc;
1604 doc.setHtml("<p>text <img src=\"\"> text</p>"); // 4 spaces before the second 'text'
1605 QTextBlock block = doc.firstBlock();
1606 QTextBlock::Iterator iter = block.begin();
1607 QTextFragment f = iter.fragment();
1608 QVERIFY(f.isValid());
1609 QCOMPARE(f.position(), 0);
1610 QCOMPARE(f.length(), 5);
1611 //qDebug() << block.text().mid(f.position(), f.length());
1612
1613 iter++;
1614 f = iter.fragment();
1615 QVERIFY(f.isValid());
1616 QCOMPARE(f.position(), 5);
1617 QCOMPARE(f.length(), 1);
1618 //qDebug() << block.text().mid(f.position(), f.length());
1619
1620 iter++;
1621 f = iter.fragment();
1622 //qDebug() << block.text().mid(f.position(), f.length());
1623 QVERIFY(f.isValid());
1624 QCOMPARE(f.position(), 6);
1625 QCOMPARE(f.length(), 5); // 1 space should be preserved.
1626 QCOMPARE(block.text().mid(f.position(), f.length()), QString(" text"));
1627
1628 doc.setHtml("<table><tr><td> foo</td></tr></table> text"); // 4 spaces before the second 'text'
1629 block = doc.firstBlock().next();
1630 //qDebug() << block.text();
1631 QCOMPARE(block.text(), QString("foo"));
1632
1633 block = block.next();
1634 //qDebug() << block.text();
1635 QCOMPARE(block.text(), QString("text"));
1636 }
1637
setFragmentMarkersInHtmlExport()1638 void tst_QTextDocument::setFragmentMarkersInHtmlExport()
1639 {
1640 {
1641 CREATE_DOC_AND_CURSOR();
1642
1643 cursor.insertText("Leadin");
1644 const int startPos = cursor.position();
1645
1646 cursor.insertText("Test");
1647 QTextCharFormat fmt;
1648 fmt.setForeground(QColor("#00ff00"));
1649 cursor.insertText("Blah", fmt);
1650
1651 const int endPos = cursor.position();
1652 cursor.insertText("Leadout", QTextCharFormat());
1653
1654 cursor.setPosition(startPos);
1655 cursor.setPosition(endPos, QTextCursor::KeepAnchor);
1656 QTextDocumentFragment fragment(cursor);
1657
1658 QString expected = htmlHead;
1659 expected.replace(QRegExp("<body.*>"), QString("<body>"));
1660 expected += QString("<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><!--StartFragment-->Test<span style=\" color:#00ff00;\">Blah</span><!--EndFragment--></p>") + htmlTail;
1661 QCOMPARE(fragment.toHtml(), expected);
1662 }
1663 {
1664 CREATE_DOC_AND_CURSOR();
1665
1666 cursor.insertText("Leadin");
1667 const int startPos = cursor.position();
1668
1669 cursor.insertText("Test");
1670
1671 const int endPos = cursor.position();
1672 cursor.insertText("Leadout", QTextCharFormat());
1673
1674 cursor.setPosition(startPos);
1675 cursor.setPosition(endPos, QTextCursor::KeepAnchor);
1676 QTextDocumentFragment fragment(cursor);
1677
1678 QString expected = htmlHead;
1679 expected.replace(QRegExp("<body.*>"), QString("<body>"));
1680 expected += QString("<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><!--StartFragment-->Test<!--EndFragment--></p>") + htmlTail;
1681 QCOMPARE(fragment.toHtml(), expected);
1682 }
1683 }
1684
toHtmlBodyBgColor()1685 void tst_QTextDocument::toHtmlBodyBgColor()
1686 {
1687 CREATE_DOC_AND_CURSOR();
1688
1689 cursor.insertText("Blah");
1690
1691 QTextFrameFormat fmt = doc.rootFrame()->frameFormat();
1692 fmt.setBackground(QColor("#0000ff"));
1693 doc.rootFrame()->setFrameFormat(fmt);
1694
1695 QString expectedHtml("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" "
1696 "\"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
1697 "<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
1698 "p, li { white-space: pre-wrap; }\n"
1699 "</style></head>"
1700 "<body style=\" font-family:'%1'; font-size:%2pt; font-weight:%3; font-style:%4;\""
1701 " bgcolor=\"#0000ff\">\n"
1702 "<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">Blah</p>"
1703 "</body></html>");
1704
1705 expectedHtml = expectedHtml.arg(defaultFont.family()).arg(defaultFont.pointSizeF()).arg(defaultFont.weight() * 8).arg((defaultFont.italic() ? "italic" : "normal"));
1706
1707 QCOMPARE(doc.toHtml(), expectedHtml);
1708 }
1709
toHtmlRootFrameProperties()1710 void tst_QTextDocument::toHtmlRootFrameProperties()
1711 {
1712 CREATE_DOC_AND_CURSOR();
1713
1714 QTextFrameFormat fmt = doc.rootFrame()->frameFormat();
1715 fmt.setTopMargin(10);
1716 fmt.setLeftMargin(10);
1717 fmt.setBorder(2);
1718 doc.rootFrame()->setFrameFormat(fmt);
1719
1720 cursor.insertText("Blah");
1721
1722 QString expectedOutput("<table border=\"2\" style=\"-qt-table-type: root; margin-top:10px; "
1723 "margin-bottom:4px; margin-left:10px; margin-right:4px;\">\n"
1724 "<tr>\n<td style=\"border: none;\">\n"
1725 "<p DEFAULTBLOCKSTYLE>Blah</p></td></tr></table>");
1726
1727 expectedOutput.prepend(htmlHead);
1728 expectedOutput.replace("DEFAULTBLOCKSTYLE", "style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"");
1729 expectedOutput.append(htmlTail);
1730
1731 QCOMPARE(doc.toHtml(), expectedOutput);
1732 }
1733
capitalizationHtmlInExport()1734 void tst_QTextDocument::capitalizationHtmlInExport()
1735 {
1736 doc->setPlainText("Test");
1737
1738 QRegExp re(".*span style=\"(.*)\">Test.*");
1739 QVERIFY(re.exactMatch(doc->toHtml()) == false); // no span
1740
1741 QTextCursor cursor(doc);
1742 cursor.setPosition(4, QTextCursor::KeepAnchor);
1743 QTextCharFormat cf;
1744 cf.setFontCapitalization(QFont::SmallCaps);
1745 cursor.mergeCharFormat(cf);
1746
1747 const QString smallcaps = doc->toHtml();
1748 QVERIFY(re.exactMatch(doc->toHtml()));
1749 QCOMPARE(re.captureCount(), 1);
1750 QCOMPARE(re.cap(1).trimmed(), QString("font-variant:small-caps;"));
1751
1752 cf.setFontCapitalization(QFont::AllUppercase);
1753 cursor.mergeCharFormat(cf);
1754 const QString uppercase = doc->toHtml();
1755 QVERIFY(re.exactMatch(doc->toHtml()));
1756 QCOMPARE(re.captureCount(), 1);
1757 QCOMPARE(re.cap(1).trimmed(), QString("text-transform:uppercase;"));
1758
1759 cf.setFontCapitalization(QFont::AllLowercase);
1760 cursor.mergeCharFormat(cf);
1761 const QString lowercase = doc->toHtml();
1762 QVERIFY(re.exactMatch(doc->toHtml()));
1763 QCOMPARE(re.captureCount(), 1);
1764 QCOMPARE(re.cap(1).trimmed(), QString("text-transform:lowercase;"));
1765
1766 doc->setHtml(smallcaps);
1767 cursor.setPosition(1);
1768 QCOMPARE(cursor.charFormat().fontCapitalization(), QFont::SmallCaps);
1769 doc->setHtml(uppercase);
1770 QCOMPARE(cursor.charFormat().fontCapitalization(), QFont::AllUppercase);
1771 doc->setHtml(lowercase);
1772 QCOMPARE(cursor.charFormat().fontCapitalization(), QFont::AllLowercase);
1773 }
1774
wordspacingHtmlExport()1775 void tst_QTextDocument::wordspacingHtmlExport()
1776 {
1777 doc->setPlainText("Test");
1778
1779 QRegExp re(".*span style=\"(.*)\">Test.*");
1780 QVERIFY(re.exactMatch(doc->toHtml()) == false); // no span
1781
1782 QTextCursor cursor(doc);
1783 cursor.setPosition(4, QTextCursor::KeepAnchor);
1784 QTextCharFormat cf;
1785 cf.setFontWordSpacing(4);
1786 cursor.mergeCharFormat(cf);
1787
1788 QVERIFY(re.exactMatch(doc->toHtml()));
1789 QCOMPARE(re.captureCount(), 1);
1790 QCOMPARE(re.cap(1).trimmed(), QString("word-spacing:4px;"));
1791
1792 cf.setFontWordSpacing(-8.5);
1793 cursor.mergeCharFormat(cf);
1794
1795 QVERIFY(re.exactMatch(doc->toHtml()));
1796 QCOMPARE(re.captureCount(), 1);
1797 QCOMPARE(re.cap(1).trimmed(), QString("word-spacing:-8.5px;"));
1798 }
1799
1800 class CursorPosSignalSpy : public QObject
1801 {
1802 Q_OBJECT
1803 public:
CursorPosSignalSpy(QTextDocument * doc)1804 CursorPosSignalSpy(QTextDocument *doc)
1805 {
1806 calls = 0;
1807 connect(doc, SIGNAL(cursorPositionChanged(const QTextCursor &)),
1808 this, SLOT(cursorPositionChanged(const QTextCursor &)));
1809 }
1810
1811 int calls;
1812
1813 private slots:
cursorPositionChanged(const QTextCursor &)1814 void cursorPositionChanged(const QTextCursor &)
1815 {
1816 ++calls;
1817 }
1818 };
1819
cursorPositionChanged()1820 void tst_QTextDocument::cursorPositionChanged()
1821 {
1822 CursorPosSignalSpy spy(doc);
1823
1824 cursor.insertText("Test");
1825 QCOMPARE(spy.calls, 1);
1826
1827 spy.calls = 0;
1828 QTextCursor unrelatedCursor(doc);
1829 unrelatedCursor.insertText("Blah");
1830 QCOMPARE(spy.calls, 2);
1831
1832 spy.calls = 0;
1833 cursor.insertText("Blah");
1834 QCOMPARE(spy.calls, 1);
1835
1836 spy.calls = 0;
1837 cursor.movePosition(QTextCursor::PreviousCharacter);
1838 QCOMPARE(spy.calls, 0);
1839 }
1840
cursorPositionChangedOnSetText()1841 void tst_QTextDocument::cursorPositionChangedOnSetText()
1842 {
1843 CursorPosSignalSpy spy(doc);
1844
1845 // doc has one QTextCursor stored in the
1846 // cursor member variable, thus the signal
1847 // gets emitted once.
1848
1849 doc->setPlainText("Foo\nBar\nBaz\nBlub\nBlah");
1850
1851 QCOMPARE(spy.calls, 1);
1852
1853 spy.calls = 0;
1854 doc->setHtml("<p>Foo<p>Bar<p>Baz<p>Blah");
1855
1856 QCOMPARE(spy.calls, 1);
1857 }
1858
textFrameIterator()1859 void tst_QTextDocument::textFrameIterator()
1860 {
1861 cursor.insertTable(1, 1);
1862
1863 int blockCount = 0;
1864 int frameCount = 0;
1865
1866 for (QTextFrame::Iterator frameIt = doc->rootFrame()->begin();
1867 !frameIt.atEnd(); ++frameIt) {
1868 if (frameIt.currentFrame())
1869 ++frameCount;
1870 else if (frameIt.currentBlock().isValid())
1871 ++blockCount;
1872
1873 }
1874
1875 QEXPECT_FAIL("", "This is currently worked around in the html export but needs fixing!", Continue);
1876 QCOMPARE(blockCount, 0);
1877 QCOMPARE(frameCount, 1);
1878 }
1879
codecForHtml()1880 void tst_QTextDocument::codecForHtml()
1881 {
1882 const QByteArray header("<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html;charset=utf-16\">");
1883 QTextCodec *c = Qt::codecForHtml(header);
1884 QVERIFY(c);
1885 QCOMPARE(c->name(), QByteArray("UTF-16"));
1886 }
1887
1888 class TestSyntaxHighlighter : public QObject
1889 {
1890 Q_OBJECT
1891 public:
TestSyntaxHighlighter(QTextDocument * doc)1892 inline TestSyntaxHighlighter(QTextDocument *doc) : QObject(doc), ok(false) {}
1893
1894 bool ok;
1895
1896 private slots:
markBlockDirty(int from,int charsRemoved,int charsAdded)1897 inline void markBlockDirty(int from, int charsRemoved, int charsAdded)
1898 {
1899 Q_UNUSED(charsRemoved);
1900 Q_UNUSED(charsAdded);
1901 QTextDocument *doc = static_cast<QTextDocument *>(parent());
1902 QTextBlock block = doc->findBlock(from);
1903
1904 QTestDocumentLayout *lout = qobject_cast<QTestDocumentLayout *>(doc->documentLayout());
1905 lout->called = false;
1906
1907 doc->markContentsDirty(block.position(), block.length());
1908
1909 ok = (lout->called == false);
1910 }
1911
modifyBlockAgain(int from,int charsRemoved,int charsAdded)1912 inline void modifyBlockAgain(int from, int charsRemoved, int charsAdded)
1913 {
1914 Q_UNUSED(charsRemoved);
1915 Q_UNUSED(charsAdded);
1916 QTextDocument *doc = static_cast<QTextDocument *>(parent());
1917 QTextBlock block = doc->findBlock(from);
1918 QTextCursor cursor(block);
1919
1920 QTestDocumentLayout *lout = qobject_cast<QTestDocumentLayout *>(doc->documentLayout());
1921 lout->called = false;
1922
1923 cursor.insertText("Foo");
1924
1925 ok = (lout->called == true);
1926 }
1927 };
1928
markContentsDirty()1929 void tst_QTextDocument::markContentsDirty()
1930 {
1931 QTestDocumentLayout *lout = new QTestDocumentLayout(doc);
1932 doc->setDocumentLayout(lout);
1933 TestSyntaxHighlighter *highlighter = new TestSyntaxHighlighter(doc);
1934 connect(doc, SIGNAL(contentsChange(int, int, int)),
1935 highlighter, SLOT(markBlockDirty(int, int, int)));
1936
1937 highlighter->ok = false;
1938 cursor.insertText("Some dummy text blah blah");
1939 QVERIFY(highlighter->ok);
1940
1941 disconnect(doc, SIGNAL(contentsChange(int, int, int)),
1942 highlighter, SLOT(markBlockDirty(int, int, int)));
1943 connect(doc, SIGNAL(contentsChange(int, int, int)),
1944 highlighter, SLOT(modifyBlockAgain(int, int, int)));
1945 highlighter->ok = false;
1946 cursor.insertText("FooBar");
1947 QVERIFY(highlighter->ok);
1948
1949 lout->called = false;
1950
1951 doc->markContentsDirty(1, 4);
1952
1953 QVERIFY(lout->called);
1954 }
1955
clonePreservesMetaInformation()1956 void tst_QTextDocument::clonePreservesMetaInformation()
1957 {
1958 const QString title("Foobar");
1959 const QString url("about:blank");
1960 doc->setHtml("<html><head><title>" + title + "</title></head><body>Hrm</body></html>");
1961 doc->setMetaInformation(QTextDocument::DocumentUrl, url);
1962 QCOMPARE(doc->metaInformation(QTextDocument::DocumentTitle), title);
1963 QCOMPARE(doc->metaInformation(QTextDocument::DocumentUrl), url);
1964
1965 QTextDocument *clone = doc->clone();
1966 QCOMPARE(clone->metaInformation(QTextDocument::DocumentTitle), title);
1967 QCOMPARE(clone->metaInformation(QTextDocument::DocumentUrl), url);
1968 delete clone;
1969 }
1970
clonePreservesPageSize()1971 void tst_QTextDocument::clonePreservesPageSize()
1972 {
1973 QSizeF sz(100., 100.);
1974 doc->setPageSize(sz);
1975 QTextDocument *clone = doc->clone();
1976 QCOMPARE(clone->pageSize(), sz);
1977 delete clone;
1978 }
1979
clonePreservesPageBreakPolicies()1980 void tst_QTextDocument::clonePreservesPageBreakPolicies()
1981 {
1982 QTextTableFormat tableFmt;
1983 tableFmt.setPageBreakPolicy(QTextFormat::PageBreak_AlwaysAfter);
1984
1985 QTextBlockFormat blockFmt;
1986 blockFmt.setPageBreakPolicy(QTextFormat::PageBreak_AlwaysBefore);
1987
1988 QTextCursor cursor(doc);
1989
1990 cursor.setBlockFormat(blockFmt);
1991 cursor.insertText("foo");
1992 cursor.insertTable(2, 2, tableFmt);
1993
1994 QTextDocument *clone = doc->clone();
1995 QCOMPARE(clone->begin().blockFormat().pageBreakPolicy(), QTextFormat::PageBreak_AlwaysBefore);
1996 QVERIFY(!clone->rootFrame()->childFrames().isEmpty());
1997 QCOMPARE(clone->rootFrame()->childFrames().first()->frameFormat().pageBreakPolicy(), QTextFormat::PageBreak_AlwaysAfter);
1998 delete clone;
1999 }
2000
clonePreservesDefaultFont()2001 void tst_QTextDocument::clonePreservesDefaultFont()
2002 {
2003 QFont f = doc->defaultFont();
2004 QVERIFY(f.pointSize() != 100);
2005 f.setPointSize(100);
2006 doc->setDefaultFont(f);
2007 QTextDocument *clone = doc->clone();
2008 QCOMPARE(clone->defaultFont(), f);
2009 delete clone;
2010 }
2011
clonePreservesResources()2012 void tst_QTextDocument::clonePreservesResources()
2013 {
2014 QUrl testUrl(":/foobar");
2015 QVariant testResource("hello world");
2016
2017 doc->addResource(QTextDocument::ImageResource, testUrl, testResource);
2018 QTextDocument *clone = doc->clone();
2019 QVERIFY(clone->resource(QTextDocument::ImageResource, testUrl) == testResource);
2020 delete clone;
2021 }
2022
clonePreservesUserStates()2023 void tst_QTextDocument::clonePreservesUserStates()
2024 {
2025 QTextCursor cursor(doc);
2026 cursor.insertText("bla bla bla");
2027 cursor.block().setUserState(1);
2028 cursor.insertBlock();
2029 cursor.insertText("foo bar");
2030 cursor.block().setUserState(2);
2031 cursor.insertBlock();
2032 cursor.insertText("no user state");
2033
2034 QTextDocument *clone = doc->clone();
2035 QTextBlock b1 = doc->begin(), b2 = clone->begin();
2036 while (b1 != doc->end()) {
2037 b1 = b1.next();
2038 b2 = b2.next();
2039 QCOMPARE(b1.userState(), b2.userState());
2040 }
2041 QVERIFY(b2 == clone->end());
2042 delete clone;
2043 }
2044
clonePreservesRootFrameFormat()2045 void tst_QTextDocument::clonePreservesRootFrameFormat()
2046 {
2047 doc->setPlainText("Hello");
2048 QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
2049 fmt.setMargin(200);
2050 doc->rootFrame()->setFrameFormat(fmt);
2051 QCOMPARE(doc->rootFrame()->frameFormat().margin(), qreal(200));
2052 QTextDocument *copy = doc->clone();
2053 QCOMPARE(copy->rootFrame()->frameFormat().margin(), qreal(200));
2054 delete copy;
2055 }
2056
clonePreservesIndentWidth()2057 void tst_QTextDocument::clonePreservesIndentWidth()
2058 {
2059 doc->setIndentWidth(42);
2060 QTextDocument *clone = doc->clone();
2061 QCOMPARE(clone->indentWidth(), qreal(42));
2062 delete clone;
2063 }
2064
blockCount()2065 void tst_QTextDocument::blockCount()
2066 {
2067 QCOMPARE(doc->blockCount(), 1);
2068 cursor.insertBlock();
2069 QCOMPARE(doc->blockCount(), 2);
2070 cursor.insertBlock();
2071 QCOMPARE(doc->blockCount(), 3);
2072 cursor.insertText("blah blah");
2073 QCOMPARE(doc->blockCount(), 3);
2074 doc->undo();
2075 doc->undo();
2076 QCOMPARE(doc->blockCount(), 2);
2077 doc->undo();
2078 QCOMPARE(doc->blockCount(), 1);
2079 }
2080
resolvedFontInEmptyFormat()2081 void tst_QTextDocument::resolvedFontInEmptyFormat()
2082 {
2083 QFont font;
2084 font.setPointSize(42);
2085 doc->setDefaultFont(font);
2086 QTextCharFormat fmt = doc->begin().charFormat();
2087 QVERIFY(fmt.properties().isEmpty());
2088 QVERIFY(fmt.font() == font);
2089 }
2090
defaultRootFrameMargin()2091 void tst_QTextDocument::defaultRootFrameMargin()
2092 {
2093 QCOMPARE(doc->rootFrame()->frameFormat().margin(), 4.0);
2094 }
2095
2096 class TestDocument : public QTextDocument
2097 {
2098 public:
TestDocument(const QUrl & testUrl,const QString & testString)2099 inline TestDocument(const QUrl &testUrl, const QString &testString)
2100 : url(testUrl), string(testString), resourceLoaded(false) {}
2101
2102 bool hasResourceCached();
2103
2104 protected:
2105 virtual QVariant loadResource(int type, const QUrl &name);
2106
2107 private:
2108 QUrl url;
2109 QString string;
2110 bool resourceLoaded;
2111 };
2112
hasResourceCached()2113 bool TestDocument::hasResourceCached()
2114 {
2115 resourceLoaded = false;
2116 resource(QTextDocument::ImageResource, url);
2117 return !resourceLoaded;
2118 }
2119
loadResource(int type,const QUrl & name)2120 QVariant TestDocument::loadResource(int type, const QUrl &name)
2121 {
2122 if (type == QTextDocument::ImageResource
2123 && name == url) {
2124 resourceLoaded = true;
2125 return string;
2126 }
2127 return QTextDocument::loadResource(type, name);
2128 }
2129
clearResources()2130 void tst_QTextDocument::clearResources()
2131 {
2132 // regular resource for QTextDocument
2133 QUrl testUrl(":/foobar");
2134 QVariant testResource("hello world");
2135
2136 // implicitly cached resource, initially loaded through TestDocument::loadResource()
2137 QUrl cacheUrl(":/blub");
2138 QString cacheResource("mah");
2139
2140 TestDocument doc(cacheUrl, cacheResource);
2141 doc.addResource(QTextDocument::ImageResource, testUrl, testResource);
2142
2143 QVERIFY(doc.resource(QTextDocument::ImageResource, testUrl) == testResource);
2144
2145 doc.setPlainText("Hah");
2146 QVERIFY(doc.resource(QTextDocument::ImageResource, testUrl) == testResource);
2147
2148 doc.setHtml("<b>Mooo</b><img src=\":/blub\"/>");
2149 QVERIFY(doc.resource(QTextDocument::ImageResource, testUrl) == testResource);
2150 QVERIFY(doc.resource(QTextDocument::ImageResource, cacheUrl) == cacheResource);
2151
2152 doc.clear();
2153 QVERIFY(!doc.resource(QTextDocument::ImageResource, testUrl).isValid());
2154 QVERIFY(!doc.hasResourceCached());
2155 doc.clear();
2156
2157 doc.setHtml("<b>Mooo</b><img src=\":/blub\"/>");
2158 QVERIFY(doc.resource(QTextDocument::ImageResource, cacheUrl) == cacheResource);
2159
2160 doc.setPlainText("Foob");
2161 QVERIFY(!doc.hasResourceCached());
2162 }
2163
setPlainText()2164 void tst_QTextDocument::setPlainText()
2165 {
2166 doc->setPlainText("Hello World");
2167 QString s("");
2168 doc->setPlainText(s);
2169 QCOMPARE(doc->toPlainText(), s);
2170 }
2171
toPlainText()2172 void tst_QTextDocument::toPlainText()
2173 {
2174 doc->setHtml("Hello World");
2175 QCOMPARE(doc->toPlainText(), QLatin1String("Hello World"));
2176 }
2177
deleteTextObjectsOnClear()2178 void tst_QTextDocument::deleteTextObjectsOnClear()
2179 {
2180 QPointer<QTextTable> table = cursor.insertTable(2, 2);
2181 QVERIFY(!table.isNull());
2182 doc->clear();
2183 QVERIFY(table.isNull());
2184 }
2185
defaultStyleSheet()2186 void tst_QTextDocument::defaultStyleSheet()
2187 {
2188 const QString sheet("p { background-color: green; }");
2189 QVERIFY(doc->defaultStyleSheet().isEmpty());
2190 doc->setDefaultStyleSheet(sheet);
2191 QCOMPARE(doc->defaultStyleSheet(), sheet);
2192
2193 cursor.insertHtml("<p>test");
2194 QTextBlockFormat fmt = doc->begin().blockFormat();
2195 QVERIFY(fmt.background().color() == QColor("green"));
2196
2197 doc->clear();
2198 cursor.insertHtml("<p>test");
2199 fmt = doc->begin().blockFormat();
2200 QVERIFY(fmt.background().color() == QColor("green"));
2201
2202 QTextDocument *clone = doc->clone();
2203 QCOMPARE(clone->defaultStyleSheet(), sheet);
2204 cursor = QTextCursor(clone);
2205 cursor.insertHtml("<p>test");
2206 fmt = clone->begin().blockFormat();
2207 QVERIFY(fmt.background().color() == QColor("green"));
2208 delete clone;
2209
2210 cursor = QTextCursor(doc);
2211 cursor.insertHtml("<p>test");
2212 fmt = doc->begin().blockFormat();
2213 QVERIFY(fmt.background().color() == QColor("green"));
2214
2215 doc->clear();
2216 cursor.insertHtml("<style>p { background-color: red; }</style><p>test");
2217 fmt = doc->begin().blockFormat();
2218 QVERIFY(fmt.background().color() == QColor("red"));
2219
2220 doc->clear();
2221 doc->setDefaultStyleSheet("invalid style sheet....");
2222 cursor.insertHtml("<p>test");
2223 fmt = doc->begin().blockFormat();
2224 QVERIFY(fmt.background().color() != QColor("green"));
2225 }
2226
maximumBlockCount()2227 void tst_QTextDocument::maximumBlockCount()
2228 {
2229 QCOMPARE(doc->maximumBlockCount(), 0);
2230 QVERIFY(doc->isUndoRedoEnabled());
2231
2232 cursor.insertBlock();
2233 cursor.insertText("Blah");
2234 cursor.insertBlock();
2235 cursor.insertText("Foo");
2236 QCOMPARE(doc->blockCount(), 3);
2237 QCOMPARE(doc->toPlainText(), QString("\nBlah\nFoo"));
2238
2239 doc->setMaximumBlockCount(1);
2240 QVERIFY(!doc->isUndoRedoEnabled());
2241
2242 QCOMPARE(doc->blockCount(), 1);
2243 QCOMPARE(doc->toPlainText(), QString("Foo"));
2244
2245 cursor.insertBlock();
2246 cursor.insertText("Hello");
2247 doc->setMaximumBlockCount(1);
2248 QCOMPARE(doc->blockCount(), 1);
2249 QCOMPARE(doc->toPlainText(), QString("Hello"));
2250
2251 doc->setMaximumBlockCount(100);
2252 for (int i = 0; i < 1000; ++i) {
2253 cursor.insertBlock();
2254 cursor.insertText("Blah)");
2255 QVERIFY(doc->blockCount() <= 100);
2256 }
2257
2258 cursor.movePosition(QTextCursor::End);
2259 QCOMPARE(cursor.blockNumber(), 99);
2260 QTextCharFormat fmt;
2261 fmt.setFontItalic(true);
2262 cursor.setBlockCharFormat(fmt);
2263 cursor.movePosition(QTextCursor::Start);
2264 QVERIFY(!cursor.blockCharFormat().fontItalic());
2265
2266 doc->setMaximumBlockCount(1);
2267 QVERIFY(cursor.blockCharFormat().fontItalic());
2268
2269 cursor.insertTable(2, 2);
2270 QCOMPARE(doc->blockCount(), 6);
2271 cursor.insertBlock();
2272 QCOMPARE(doc->blockCount(), 1);
2273 }
2274
adjustSize()2275 void tst_QTextDocument::adjustSize()
2276 {
2277 // avoid ugly tooltips like in task 125583
2278 QString text("Test Text");
2279 doc->setPlainText(text);
2280 doc->rootFrame()->setFrameFormat(QTextFrameFormat());
2281 doc->adjustSize();
2282 QCOMPARE(doc->size().width(), doc->idealWidth());
2283 }
2284
initialUserData()2285 void tst_QTextDocument::initialUserData()
2286 {
2287 doc->setPlainText("Hello");
2288 QTextBlock block = doc->begin();
2289 block.setUserData(new QTextBlockUserData);
2290 QVERIFY(block.userData());
2291 doc->documentLayout();
2292 QVERIFY(block.userData());
2293 doc->setDocumentLayout(new QTestDocumentLayout(doc));
2294 QVERIFY(!block.userData());
2295 }
2296
html_defaultFont()2297 void tst_QTextDocument::html_defaultFont()
2298 {
2299 QFont f;
2300 f.setItalic(true);
2301 f.setWeight(QFont::Bold);
2302 doc->setDefaultFont(f);
2303 doc->setPlainText("Test");
2304
2305 QString bodyPart = QString::fromLatin1("<body style=\" font-family:'%1'; font-size:%2pt; font-weight:%3; font-style:italic;\">")
2306 .arg(f.family()).arg(f.pointSizeF()).arg(f.weight() * 8);
2307
2308 QString html = doc->toHtml();
2309 if (!html.contains(bodyPart)) {
2310 qDebug() << "html:" << html;
2311 qDebug() << "expected body:" << bodyPart;
2312 QVERIFY(html.contains(bodyPart));
2313 }
2314
2315 if (html.contains("span"))
2316 qDebug() << "html:" << html;
2317 QVERIFY(!html.contains("<span style"));
2318 }
2319
blockCountChanged()2320 void tst_QTextDocument::blockCountChanged()
2321 {
2322 QSignalSpy spy(doc, SIGNAL(blockCountChanged(int)));
2323
2324 doc->setPlainText("Foo");
2325
2326 QCOMPARE(doc->blockCount(), 1);
2327 QCOMPARE(spy.count(), 0);
2328
2329 spy.clear();
2330
2331 doc->setPlainText("Foo\nBar");
2332 QCOMPARE(doc->blockCount(), 2);
2333 QCOMPARE(spy.count(), 1);
2334 QCOMPARE(spy.at(0).value(0).toInt(), 2);
2335
2336 spy.clear();
2337
2338 cursor.movePosition(QTextCursor::End);
2339 cursor.insertText("Blahblah");
2340
2341 QCOMPARE(spy.count(), 0);
2342
2343 cursor.insertBlock();
2344 QCOMPARE(spy.count(), 1);
2345 QCOMPARE(spy.at(0).value(0).toInt(), 3);
2346
2347 spy.clear();
2348 doc->undo();
2349
2350 QCOMPARE(spy.count(), 1);
2351 QCOMPARE(spy.at(0).value(0).toInt(), 2);
2352 }
2353
nonZeroDocumentLengthOnClear()2354 void tst_QTextDocument::nonZeroDocumentLengthOnClear()
2355 {
2356 QTestDocumentLayout *lout = new QTestDocumentLayout(doc);
2357 doc->setDocumentLayout(lout);
2358
2359 doc->clear();
2360 QVERIFY(lout->called);
2361 QVERIFY(!lout->lastDocumentLengths.contains(0));
2362 }
2363
setTextPreservesUndoRedoEnabled()2364 void tst_QTextDocument::setTextPreservesUndoRedoEnabled()
2365 {
2366 QVERIFY(doc->isUndoRedoEnabled());
2367
2368 doc->setPlainText("Test");
2369
2370 QVERIFY(doc->isUndoRedoEnabled());
2371
2372 doc->setUndoRedoEnabled(false);
2373 QVERIFY(!doc->isUndoRedoEnabled());
2374 doc->setPlainText("Test2");
2375 QVERIFY(!doc->isUndoRedoEnabled());
2376
2377 doc->setHtml("<p>hello");
2378 QVERIFY(!doc->isUndoRedoEnabled());
2379 }
2380
firstLast()2381 void tst_QTextDocument::firstLast()
2382 {
2383 QCOMPARE(doc->blockCount(), 1);
2384 QVERIFY(doc->firstBlock() == doc->lastBlock());
2385
2386 doc->setPlainText("Hello\nTest\nWorld");
2387
2388 QCOMPARE(doc->blockCount(), 3);
2389 QVERIFY(doc->firstBlock() != doc->lastBlock());
2390
2391 QCOMPARE(doc->firstBlock().text(), QString("Hello"));
2392 QCOMPARE(doc->lastBlock().text(), QString("World"));
2393
2394 // manual forward loop
2395 QTextBlock block = doc->firstBlock();
2396
2397 QVERIFY(block.isValid());
2398 QCOMPARE(block.text(), QString("Hello"));
2399
2400 block = block.next();
2401
2402 QVERIFY(block.isValid());
2403 QCOMPARE(block.text(), QString("Test"));
2404
2405 block = block.next();
2406
2407 QVERIFY(block.isValid());
2408 QCOMPARE(block.text(), QString("World"));
2409
2410 block = block.next();
2411 QVERIFY(!block.isValid());
2412
2413 // manual backward loop
2414 block = doc->lastBlock();
2415
2416 QVERIFY(block.isValid());
2417 QCOMPARE(block.text(), QString("World"));
2418
2419 block = block.previous();
2420
2421 QVERIFY(block.isValid());
2422 QCOMPARE(block.text(), QString("Test"));
2423
2424 block = block.previous();
2425
2426 QVERIFY(block.isValid());
2427 QCOMPARE(block.text(), QString("Hello"));
2428
2429 block = block.previous();
2430 QVERIFY(!block.isValid());
2431 }
2432
2433 const QString backgroundImage_html("<body><table><tr><td background=\"foo.png\">Blah</td></tr></table></body>");
2434
backgroundImage_checkExpectedHtml(const QTextDocument & doc)2435 void tst_QTextDocument::backgroundImage_checkExpectedHtml(const QTextDocument &doc)
2436 {
2437 QString expectedHtml("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" "
2438 "\"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
2439 "<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
2440 "p, li { white-space: pre-wrap; }\n"
2441 "</style></head>"
2442 "<body style=\" font-family:'%1'; font-size:%2pt; font-weight:%3; font-style:%4;\">\n"
2443 "<table border=\"0\" style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;\" cellspacing=\"2\" cellpadding=\"0\">"
2444 "\n<tr>\n<td background=\"foo.png\">"
2445 "\n<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">Blah</p>"
2446 "</td></tr></table></body></html>");
2447
2448 expectedHtml = expectedHtml.arg(defaultFont.family()).arg(defaultFont.pointSizeF()).arg(defaultFont.weight() * 8).arg((defaultFont.italic() ? "italic" : "normal"));
2449
2450 QCOMPARE(doc.toHtml(), expectedHtml);
2451 }
2452
backgroundImage_toHtml()2453 void tst_QTextDocument::backgroundImage_toHtml()
2454 {
2455 CREATE_DOC_AND_CURSOR();
2456
2457 doc.setHtml(backgroundImage_html);
2458 backgroundImage_checkExpectedHtml(doc);
2459 }
2460
backgroundImage_toHtml2()2461 void tst_QTextDocument::backgroundImage_toHtml2()
2462 {
2463 CREATE_DOC_AND_CURSOR();
2464
2465 cursor.insertHtml(backgroundImage_html);
2466 backgroundImage_checkExpectedHtml(doc);
2467 }
2468
backgroundImage_clone()2469 void tst_QTextDocument::backgroundImage_clone()
2470 {
2471 CREATE_DOC_AND_CURSOR();
2472
2473 doc.setHtml(backgroundImage_html);
2474 QTextDocument *clone = doc.clone();
2475 backgroundImage_checkExpectedHtml(*clone);
2476 delete clone;
2477 }
2478
backgroundImage_copy()2479 void tst_QTextDocument::backgroundImage_copy()
2480 {
2481 CREATE_DOC_AND_CURSOR();
2482
2483 doc.setHtml(backgroundImage_html);
2484 QTextDocumentFragment fragment(&doc);
2485
2486 {
2487 CREATE_DOC_AND_CURSOR();
2488
2489 cursor.insertFragment(fragment);
2490 backgroundImage_checkExpectedHtml(doc);
2491 }
2492 }
2493
documentCleanup()2494 void tst_QTextDocument::documentCleanup()
2495 {
2496 QTextDocument doc;
2497 QTextCursor cursor(&doc);
2498 cursor.insertText("d\nfoo\nbar\n");
2499 doc.documentLayout(); // forces relayout
2500
2501 // remove char 1
2502 cursor.setPosition(0);
2503 QSizeF size = doc.documentLayout()->documentSize();
2504 cursor.deleteChar();
2505 // the size should be unchanged.
2506 QCOMPARE(doc.documentLayout()->documentSize(), size);
2507 }
2508
characterAt()2509 void tst_QTextDocument::characterAt()
2510 {
2511 QTextDocument doc;
2512 QTextCursor cursor(&doc);
2513 QString text("12345\n67890");
2514 cursor.insertText(text);
2515 int length = doc.characterCount();
2516 QCOMPARE(length, text.length() + 1);
2517 QCOMPARE(doc.characterAt(length-1), QChar(QChar::ParagraphSeparator));
2518 QCOMPARE(doc.characterAt(-1), QChar());
2519 QCOMPARE(doc.characterAt(length), QChar());
2520 QCOMPARE(doc.characterAt(length + 1), QChar());
2521 for (int i = 0; i < text.length(); ++i) {
2522 QChar c = text.at(i);
2523 if (c == QLatin1Char('\n'))
2524 c = QChar(QChar::ParagraphSeparator);
2525 QCOMPARE(doc.characterAt(i), c);
2526 }
2527 }
2528
revisions()2529 void tst_QTextDocument::revisions()
2530 {
2531 QTextDocument doc;
2532 QTextCursor cursor(&doc);
2533 QString text("Hello World");
2534 QCOMPARE(doc.firstBlock().revision(), 0);
2535 cursor.insertText(text);
2536 QCOMPARE(doc.firstBlock().revision(), 1);
2537 cursor.setPosition(6);
2538 cursor.insertBlock();
2539 QCOMPARE(cursor.block().previous().revision(), 2);
2540 QCOMPARE(cursor.block().revision(), 2);
2541 cursor.insertText("candle");
2542 QCOMPARE(cursor.block().revision(), 3);
2543 cursor.movePosition(QTextCursor::EndOfBlock);
2544 cursor.insertBlock(); // we are at the block end
2545 QCOMPARE(cursor.block().previous().revision(), 3);
2546 QCOMPARE(cursor.block().revision(), 4);
2547 cursor.insertText("lightbulb");
2548 QCOMPARE(cursor.block().revision(), 5);
2549 cursor.movePosition(QTextCursor::StartOfBlock);
2550 cursor.insertBlock(); // we are the block start
2551 QCOMPARE(cursor.block().previous().revision(), 6);
2552 QCOMPARE(cursor.block().revision(), 5);
2553 }
2554
revisionWithUndoCompressionAndUndo()2555 void tst_QTextDocument::revisionWithUndoCompressionAndUndo()
2556 {
2557 QTextDocument doc;
2558 QTextCursor cursor(&doc);
2559 cursor.insertText("This is the beginning of it all.");
2560 QCOMPARE(doc.firstBlock().revision(), 1);
2561 QCOMPARE(doc.revision(), 1);
2562 cursor.insertBlock();
2563 QCOMPARE(doc.revision(), 2);
2564 cursor.insertText("this");
2565 QCOMPARE(doc.revision(), 3);
2566 cursor.insertText("is");
2567 QCOMPARE(doc.revision(), 4);
2568 cursor.insertText("compressed");
2569 QCOMPARE(doc.revision(), 5);
2570 doc.undo();
2571 QCOMPARE(doc.revision(), 6);
2572 QCOMPARE(doc.toPlainText(), QString("This is the beginning of it all.\n")) ;
2573 cursor.setPosition(0);
2574 QCOMPARE(doc.firstBlock().revision(), 1);
2575 cursor.insertText("Very beginnig");
2576 QCOMPARE(doc.firstBlock().revision(), 7);
2577 doc.undo();
2578 QCOMPARE(doc.revision(), 8);
2579 QCOMPARE(doc.firstBlock().revision(), 1);
2580
2581 cursor.beginEditBlock();
2582 cursor.insertText("Hello");
2583 cursor.insertBlock();
2584 cursor.insertText("world");
2585 cursor.endEditBlock();
2586 QCOMPARE(doc.revision(), 9);
2587 doc.undo();
2588 QCOMPARE(doc.revision(), 10);
2589
2590
2591 }
2592
testUndoCommandAdded()2593 void tst_QTextDocument::testUndoCommandAdded()
2594 {
2595 QVERIFY(doc);
2596 QSignalSpy spy(doc, SIGNAL(undoCommandAdded()));
2597 QVERIFY(spy.isValid());
2598 QVERIFY(spy.isEmpty());
2599
2600 cursor.insertText("a");
2601 QCOMPARE(spy.count(), 1);
2602 cursor.insertText("b"); // should be merged
2603 QCOMPARE(spy.count(), 1);
2604 cursor.insertText("c"); // should be merged
2605 QCOMPARE(spy.count(), 1);
2606 QCOMPARE(doc->toPlainText(), QString("abc"));
2607 doc->undo();
2608 QCOMPARE(doc->toPlainText(), QString(""));
2609
2610 doc->clear();
2611 spy.clear();
2612 cursor.insertText("aaa");
2613 QCOMPARE(spy.count(), 1);
2614
2615 spy.clear();
2616 cursor.insertText("aaaa\nbcd");
2617 QCOMPARE(spy.count(), 1);
2618
2619 spy.clear();
2620 cursor.beginEditBlock();
2621 cursor.insertText("aa");
2622 cursor.insertText("bbb\n");
2623 cursor.setCharFormat(QTextCharFormat());
2624 cursor.insertText("\nccc");
2625 QVERIFY(spy.isEmpty());
2626 cursor.endEditBlock();
2627 QCOMPARE(spy.count(), 1);
2628
2629 spy.clear();
2630 cursor.insertBlock();
2631 QCOMPARE(spy.count(), 1);
2632
2633 spy.clear();
2634 cursor.setPosition(5);
2635 QVERIFY(spy.isEmpty());
2636 cursor.setCharFormat(QTextCharFormat());
2637 QVERIFY(spy.isEmpty());
2638 cursor.setPosition(10, QTextCursor::KeepAnchor);
2639 QVERIFY(spy.isEmpty());
2640 QTextCharFormat cf;
2641 cf.setFontItalic(true);
2642 cursor.mergeCharFormat(cf);
2643 QCOMPARE(spy.count(), 1);
2644
2645 spy.clear();
2646 doc->undo();
2647 QCOMPARE(spy.count(), 0);
2648 doc->undo();
2649 QCOMPARE(spy.count(), 0);
2650 spy.clear();
2651 doc->redo();
2652 QCOMPARE(spy.count(), 0);
2653 doc->redo();
2654 QCOMPARE(spy.count(), 0);
2655 }
2656
testUndoBlocks()2657 void tst_QTextDocument::testUndoBlocks()
2658 {
2659 QVERIFY(doc);
2660 cursor.insertText("Hello World");
2661 cursor.insertText("period");
2662 doc->undo();
2663 QCOMPARE(doc->toPlainText(), QString(""));
2664 cursor.insertText("Hello World");
2665 cursor.insertText("One\nTwo\nThree");
2666 QCOMPARE(doc->toPlainText(), QString("Hello WorldOne\nTwo\nThree"));
2667 doc->undo();
2668 QCOMPARE(doc->toPlainText(), QString("Hello World"));
2669 cursor.insertText("One\nTwo\nThree");
2670 cursor.insertText("Trailing text");
2671 doc->undo();
2672 QCOMPARE(doc->toPlainText(), QString("Hello WorldOne\nTwo\nThree"));
2673 doc->undo();
2674 QCOMPARE(doc->toPlainText(), QString("Hello World"));
2675 doc->undo();
2676 QCOMPARE(doc->toPlainText(), QString(""));
2677
2678 cursor.insertText("quod");
2679 cursor.beginEditBlock();
2680 cursor.insertText(" erat");
2681 cursor.endEditBlock();
2682 cursor.insertText(" demonstrandum");
2683 QCOMPARE(doc->toPlainText(), QString("quod erat demonstrandum"));
2684 doc->undo();
2685 QCOMPARE(doc->toPlainText(), QString("quod erat"));
2686 doc->undo();
2687 QCOMPARE(doc->toPlainText(), QString("quod"));
2688 doc->undo();
2689 QCOMPARE(doc->toPlainText(), QString(""));
2690 }
2691
2692 class Receiver : public QObject
2693 {
2694 Q_OBJECT
2695 public:
2696 QString first;
2697 public slots:
cursorPositionChanged()2698 void cursorPositionChanged() {
2699 if (first.isEmpty())
2700 first = QLatin1String("cursorPositionChanged");
2701 }
2702
contentsChange()2703 void contentsChange() {
2704 if (first.isEmpty())
2705 first = QLatin1String("contentsChanged");
2706 }
2707 };
2708
receiveCursorPositionChangedAfterContentsChange()2709 void tst_QTextDocument::receiveCursorPositionChangedAfterContentsChange()
2710 {
2711 QVERIFY(doc);
2712 doc->setDocumentLayout(new MyAbstractTextDocumentLayout(doc));
2713 Receiver rec;
2714 connect(doc, SIGNAL(cursorPositionChanged(QTextCursor)),
2715 &rec, SLOT(cursorPositionChanged()));
2716 connect(doc, SIGNAL(contentsChange(int,int,int)),
2717 &rec, SLOT(contentsChange()));
2718 cursor.insertText("Hello World");
2719 QCOMPARE(rec.first, QString("contentsChanged"));
2720 }
2721
escape_data()2722 void tst_QTextDocument::escape_data()
2723 {
2724 QTest::addColumn<QString>("original");
2725 QTest::addColumn<QString>("expected");
2726
2727 QTest::newRow("1") << "Hello World\n" << "Hello World\n";
2728 QTest::newRow("2") << "#include <QtCore>" << "#include <QtCore>";
2729 QTest::newRow("3") << "<p class=\"cool\"><a href=\"http://example.com/?foo=bar&bar=foo\">plop --> </a></p>"
2730 << "<p class="cool"><a href="http://example.com/?foo=bar&amp;bar=foo">plop --&gt; </a></p>";
2731 QTest::newRow("4") << QString::fromUtf8("<\320\222\321\201>") << QString::fromUtf8("<\320\222\321\201>");
2732 }
2733
escape()2734 void tst_QTextDocument::escape()
2735 {
2736 QFETCH(QString, original);
2737 QFETCH(QString, expected);
2738
2739 QCOMPARE(Qt::escape(original), expected);
2740 }
2741
QTBUG25778_pixelSizeFromHtml()2742 void tst_QTextDocument::QTBUG25778_pixelSizeFromHtml()
2743 {
2744 QTextDocument document1;
2745 QTextDocument document2;
2746
2747 document1.setHtml("<span style=\"font-size: 24px\">Foobar</span>");
2748 document2.setHtml(document1.toHtml());
2749
2750 QTextCursor cursor(&document2);
2751 QCOMPARE(cursor.charFormat().font().pixelSize(), 24);
2752 }
2753
copiedFontSize()2754 void tst_QTextDocument::copiedFontSize()
2755 {
2756 QTextDocument documentInput;
2757 QTextDocument documentOutput;
2758
2759 QFont fontInput;
2760 fontInput.setPixelSize(24);
2761
2762 QTextCursor cursorInput(&documentInput);
2763 QTextCharFormat formatInput = cursorInput.charFormat();
2764 formatInput.setFont(fontInput);
2765 cursorInput.insertText("Should be the same font", formatInput);
2766 cursorInput.select(QTextCursor::Document);
2767
2768 QTextDocumentFragment fragmentInput(cursorInput);
2769 QString html = fragmentInput.toHtml();
2770
2771 QTextCursor cursorOutput(&documentOutput);
2772 QTextDocumentFragment fragmentOutput = QTextDocumentFragment::fromHtml(html);
2773 cursorOutput.insertFragment(fragmentOutput);
2774
2775 QCOMPARE(cursorOutput.charFormat().font().pixelSize(), 24);
2776 }
2777
htmlExportImportBlockCount()2778 void tst_QTextDocument::htmlExportImportBlockCount()
2779 {
2780 QTextDocument document;
2781 {
2782 QTextCursor cursor(&document);
2783 cursor.insertText("Foo");
2784 cursor.insertBlock();
2785 cursor.insertBlock();
2786 cursor.insertBlock();
2787 cursor.insertBlock();
2788 cursor.insertText("Bar");
2789 }
2790
2791 QCOMPARE(document.blockCount(), 5);
2792 QString html = document.toHtml();
2793
2794 document.clear();
2795 document.setHtml(html);
2796
2797 QCOMPARE(document.blockCount(), 5);
2798 }
2799
2800 QTEST_MAIN(tst_QTextDocument)
2801 #include "tst_qtextdocument.moc"
2802