1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
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 https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29
30#include <QtTest/QtTest>
31
32#define protected public
33
34#include <qtextdocument.h>
35#undef protected
36#include <private/qtextdocument_p.h>
37#include <qabstracttextdocumentlayout.h>
38#include <qtextobject.h>
39#include <qdebug.h>
40#include <stdlib.h>
41#include <qtextcursor.h>
42#include "../qtextdocument/common.h"
43
44class tst_QTextPieceTable : public QObject
45{
46 Q_OBJECT
47
48public:
49 tst_QTextPieceTable();
50
51
52public slots:
53 void init();
54 void cleanup();
55private slots:
56 void insertion1();
57 void insertion2();
58 void insertion3();
59 void insertion4();
60 void insertion5();
61
62 void removal1();
63 void removal2();
64 void removal3();
65 void removal4();
66
67 void undoRedo1();
68 void undoRedo2();
69 void undoRedo3();
70 void undoRedo4();
71 void undoRedo5();
72 void undoRedo6();
73 void undoRedo7();
74 void undoRedo8();
75 void undoRedo9();
76 void undoRedo10();
77 void undoRedo11();
78
79 void checkDocumentChanged();
80 void checkDocumentChanged2();
81 void setBlockFormat();
82
83 void blockInsertion();
84 void blockInsertion2();
85
86 void blockRemoval1();
87 void blockRemoval2();
88 void blockRemoval3();
89 void blockRemoval4();
90 void blockRemoval5();
91
92 void checkBlockSeparation();
93
94 void checkFrames1();
95 void removeFrameDirect();
96 void removeWithChildFrame();
97 void clearWithFrames();
98
99private:
100 QTextDocument *doc;
101 QTextDocumentPrivate *table;
102 int blockFormatIndex;
103 int charFormatIndex;
104};
105
106tst_QTextPieceTable::tst_QTextPieceTable()
107{ doc = 0; table = 0; }
108
109
110void tst_QTextPieceTable::init()
111{
112 doc = new QTextDocument(0);
113 table = doc->docHandle();
114 blockFormatIndex = table->formatCollection()->indexForFormat(f: QTextBlockFormat());
115 charFormatIndex = table->formatCollection()->indexForFormat(f: QTextCharFormat());
116}
117
118void tst_QTextPieceTable::cleanup()
119{
120 delete doc;
121 doc = 0;
122}
123
124void tst_QTextPieceTable::insertion1()
125{
126 table->insert(pos: 0, text: "aacc", format: charFormatIndex);
127 QCOMPARE(table->plainText(), QString("aacc"));
128 table->insert(pos: 2, text: "bb", format: charFormatIndex);
129 QCOMPARE(table->plainText(), QString("aabbcc"));
130 table->insert(pos: 1, text: "1", format: charFormatIndex);
131 QCOMPARE(table->plainText(), QString("a1abbcc"));
132 table->insert(pos: 6, text: "d", format: charFormatIndex);
133 QCOMPARE(table->plainText(), QString("a1abbcdc"));
134 table->insert(pos: 8, text: "z", format: charFormatIndex);
135 QCOMPARE(table->plainText(), QString("a1abbcdcz"));
136}
137
138void tst_QTextPieceTable::insertion2()
139{
140 table->insert(pos: 0, text: "bb", format: charFormatIndex);
141 QCOMPARE(table->plainText(), QString("bb"));
142}
143
144void tst_QTextPieceTable::insertion3()
145{
146 QString compare;
147 for (int i = 0; i < 20000; ++i) {
148 int pos = QRandomGenerator::global()->bounded(highest: i+1);
149 QChar c((unsigned short)(i & 0xff) + 1);
150 QString str;
151 str += c;
152 table->insert(pos, text: str, format: charFormatIndex);
153 compare.insert(i: pos, s: str);
154 }
155 QCOMPARE(table->plainText(), compare);
156}
157
158void tst_QTextPieceTable::insertion4()
159{
160 QString compare;
161 for (int i = 0; i < 20000; ++i) {
162 int pos = QRandomGenerator::global()->generate() % (i+1);
163 QChar c((unsigned short)((i % 26) + (i>25?'A':'a')));
164 QString str;
165 str += c;
166 str += c;
167 table->insert(pos, text: str, format: charFormatIndex);
168 compare.insert(i: pos, s: str);
169// if (table->text() != compare) {
170// qDebug("compare failed: i=%d (current char=%c) insert at %d\nexpected '%s'\ngot '%s'", i, (i % 26) + (i>25?'A':'a'), pos, compare.latin1(), table->text().latin1());
171// exit(12);
172// }
173 }
174 QCOMPARE(table->plainText(), compare);
175}
176
177void tst_QTextPieceTable::insertion5()
178{
179 QString compare;
180 for (int i = 0; i < 20000; ++i) {
181 int pos = QRandomGenerator::global()->generate() % (i+1);
182 QChar c((unsigned short)((i % 26) + (i>25?'A':'a')));
183 QString str;
184 str += c;
185 str += c;
186 if (c == 'a') {
187 table->insertBlock(pos, blockFormat: blockFormatIndex, charFormat: charFormatIndex);
188 str = QChar(QChar::ParagraphSeparator);
189 } else {
190 table->insert(pos, text: str, format: charFormatIndex);
191 }
192 compare.insert(i: pos, s: str);
193 }
194 QCOMPARE(table->plainText(), compare);
195 for (QTextBlock it = table->blocksBegin(); it != table->blocksEnd(); it = it.next()) {
196 QTextDocumentPrivate::FragmentIterator fit = table->find(pos: it.position());
197 QCOMPARE(fit.position(), it.position());
198 }
199}
200
201void tst_QTextPieceTable::removal1()
202{
203 table->insert(pos: 0, text: "abbccc", format: charFormatIndex);
204 QCOMPARE(table->plainText(), QString("abbccc"));
205 table->remove(pos: 1, length: 2);
206 QCOMPARE(table->plainText(), QString("accc"));
207 table->insert(pos: 1, text: "1", format: charFormatIndex);
208 QCOMPARE(table->plainText(), QString("a1ccc"));
209 table->remove(pos: 4, length: 1);
210 QCOMPARE(table->plainText(), QString("a1cc"));
211 table->insert(pos: 4, text: "z", format: charFormatIndex);
212 QCOMPARE(table->plainText(), QString("a1ccz"));
213}
214
215void tst_QTextPieceTable::removal2()
216{
217 table->insert(pos: 0, text: "bb", format: charFormatIndex);
218 QCOMPARE(table->plainText(), QString("bb"));
219 table->remove(pos: 0, length: 2);
220 QCOMPARE(table->plainText(), QString(""));
221 table->insertBlock(pos: 0, blockFormat: blockFormatIndex, charFormat: charFormatIndex);
222 QCOMPARE(table->plainText(), QString(QChar(QChar::ParagraphSeparator)));
223 table->remove(pos: 0, length: 1);
224 QCOMPARE(table->plainText(), QString(""));
225
226 table->insert(pos: 0, text: "bb", format: charFormatIndex);
227 QCOMPARE(table->plainText(), QString("bb"));
228 table->insertBlock(pos: 1, blockFormat: blockFormatIndex, charFormat: charFormatIndex);
229 QCOMPARE(table->plainText(), QString("b") + QString(QChar(QChar::ParagraphSeparator)) + QString("b"));
230 table->remove(pos: 1, length: 1);
231 QCOMPARE(table->plainText(), QString("bb"));
232}
233
234void tst_QTextPieceTable::removal3()
235{
236 QString compare;
237 int l = 0;
238 for (int i = 0; i < 20000; ++i) {
239 bool remove = l && (QRandomGenerator::global()->bounded(highest: 2));
240 int pos = QRandomGenerator::global()->bounded(highest: remove ? l : (l+1));
241 QChar c((unsigned short)((i % 26) + (i>25?'A':'a')));
242 QString str;
243 str += c;
244 str += c;
245 if (remove && pos < table->length()) {
246 compare.remove(i: pos, len: 1);
247 table->remove(pos, length: 1);
248 } else {
249 compare.insert(i: pos, s: str);
250 table->insert(pos, text: str, format: charFormatIndex);
251 }
252 l += remove ? -1 : 2;
253// if (table->text() != compare) {
254// qDebug("compare failed: i=%d (current char=%c) insert at %d\nexpected '%s'\ngot '%s'", i, (i % 26) + (i>25?'A':'a'), pos, compare.latin1(), table->text().latin1());
255// exit(12);
256// }
257 }
258 QCOMPARE(table->plainText(), compare);
259}
260
261void tst_QTextPieceTable::removal4()
262{
263 QString compare;
264 int l = 0;
265 for (int i = 0; i < 20000; ++i) {
266 bool remove = l && (QRandomGenerator::global()->bounded(highest: 2));
267 int pos = (l > 1) ? QRandomGenerator::global()->bounded(highest: remove ? l-1 : l) : 0;
268 QChar c((unsigned short)((i % 26) + (i>25?'A':'a')));
269 QString str;
270 if (c != 'a') {
271 str += c;
272 str += c;
273 } else {
274 str = QChar(QChar::ParagraphSeparator);
275 }
276 if (remove && pos < table->length() - 1) {
277 compare.remove(i: pos, len: 1);
278 table->remove(pos, length: 1);
279 } else {
280 if (str[0] == QChar(QChar::ParagraphSeparator))
281 table->insertBlock(pos, blockFormat: blockFormatIndex, charFormat: charFormatIndex);
282 else
283 table->insert(pos, text: str, format: charFormatIndex);
284 compare.insert(i: pos, s: str);
285 }
286 l += remove ? -1 : 2;
287// if (table->plainText() != compare) {
288// qDebug("compare failed: i=%d (current char=%c) insert at %d\nexpected '%s'\ngot '%s'", i, (i % 26) + (i>25?'A':'a'), pos, compare.latin1(), table->plainText().latin1());
289// exit(12);
290// }
291 }
292 QCOMPARE(table->plainText(), compare);
293}
294
295void tst_QTextPieceTable::undoRedo1()
296{
297 table->insert(pos: 0, text: "01234567", format: charFormatIndex);
298 table->insert(pos: 0, text: "a", format: charFormatIndex);
299 table->insert(pos: 1, text: "b", format: charFormatIndex);
300 QCOMPARE(table->plainText(), QString("ab01234567"));
301 table->undo();
302 QCOMPARE(table->plainText(), QString("01234567"));
303 table->redo();
304 QCOMPARE(table->plainText(), QString("ab01234567"));
305 table->undo();
306 table->insert(pos: 1, text: "c", format: charFormatIndex);
307 QCOMPARE(table->plainText(), QString("0c1234567"));
308 table->undo();
309 QCOMPARE(table->plainText(), QString("01234567"));
310 table->undo();
311 QVERIFY(table->plainText().isEmpty());
312}
313
314void tst_QTextPieceTable::undoRedo2()
315{
316 table->insert(pos: 0, text: "01", format: charFormatIndex);
317 table->insert(pos: 1, text: "a", format: charFormatIndex);
318 QCOMPARE(table->plainText(), QString("0a1"));
319 table->undo();
320 QCOMPARE(table->plainText(), QString("01"));
321 table->undo();
322 QCOMPARE(table->plainText(), QString(""));
323 table->redo();
324 QCOMPARE(table->plainText(), QString("01"));
325 table->redo();
326 QCOMPARE(table->plainText(), QString("0a1"));
327}
328
329void tst_QTextPieceTable::undoRedo3()
330{
331 table->insert(pos: 0, text: "01", format: charFormatIndex);
332 table->insert(pos: 2, text: "ab", format: charFormatIndex);
333 table->remove(pos: 2, length: 1);
334 QCOMPARE(table->plainText(), QString("01b"));
335 table->undo();
336 QCOMPARE(table->plainText(), QString("01ab"));
337 table->undo();
338 QVERIFY(table->plainText().isEmpty());
339 table->redo();
340 QCOMPARE(table->plainText(), QString("01ab"));
341 table->redo();
342 QCOMPARE(table->plainText(), QString("01b"));
343}
344
345void tst_QTextPieceTable::undoRedo4()
346{
347 table->insert(pos: 0, text: "01", format: charFormatIndex);
348 table->insert(pos: 0, text: "ab", format: charFormatIndex);
349 table->remove(pos: 0, length: 1);
350 QCOMPARE(table->plainText(), QString("b01"));
351 table->undo();
352 QCOMPARE(table->plainText(), QString("ab01"));
353 table->undo();
354 QCOMPARE(table->plainText(), QString("01"));
355 table->undo();
356 QCOMPARE(table->plainText(), QString(""));
357 table->redo();
358 QCOMPARE(table->plainText(), QString("01"));
359 table->redo();
360 QCOMPARE(table->plainText(), QString("ab01"));
361 table->redo();
362 QCOMPARE(table->plainText(), QString("b01"));
363}
364
365void tst_QTextPieceTable::undoRedo5()
366{
367 table->beginEditBlock();
368 table->insert(pos: 0, text: "01", format: charFormatIndex);
369 table->remove(pos: 1, length: 1);
370 table->endEditBlock();
371 QCOMPARE(table->plainText(), QString("0"));
372 table->undo();
373 QCOMPARE(table->plainText(), QString(""));
374}
375
376void tst_QTextPieceTable::undoRedo6()
377{
378 // this is essentially a test for the undoStack[undoPosition - 1].block = false in PieceTable::endUndoBlock()
379 QTextDocument doc;
380 QTextCursor cursor(&doc);
381 cursor.insertText(text: "Hello World");
382
383 cursor.insertBlock();
384 cursor.insertText(text: "Hello World2");
385
386 cursor.movePosition(op: QTextCursor::Start);
387 QTextBlockFormat bfmt;
388 bfmt.setAlignment(Qt::AlignHCenter);
389 cursor.setBlockFormat(bfmt);
390 QCOMPARE(cursor.blockFormat().alignment(), Qt::AlignHCenter);
391
392 QTextCursor range = cursor;
393 range.clearSelection();
394 range.movePosition(op: QTextCursor::Start);
395 range.movePosition(op: QTextCursor::End, QTextCursor::KeepAnchor);
396
397 QTextCharFormat modifier;
398 modifier.setFontItalic(true);
399 range.mergeCharFormat(modifier);
400
401 cursor.movePosition(op: QTextCursor::Start);
402 QCOMPARE(cursor.blockFormat().alignment(), Qt::AlignHCenter);
403
404 doc.undo();
405
406 QCOMPARE(cursor.blockFormat().alignment(), Qt::AlignHCenter);
407}
408
409void tst_QTextPieceTable::undoRedo7()
410{
411 table->insert(pos: 0, text: "a", format: charFormatIndex);
412 table->insert(pos: 1, text: "b", format: charFormatIndex);
413 QCOMPARE(table->plainText(), QString("ab"));
414
415 table->undo();
416 QVERIFY(table->plainText().isEmpty());
417}
418
419void tst_QTextPieceTable::undoRedo8()
420{
421 table->insert(pos: 0, text: "a", format: charFormatIndex);
422 table->insert(pos: 1, text: "b", format: charFormatIndex);
423 QCOMPARE(table->plainText(), QString("ab"));
424
425 table->remove(pos: 0, length: 1);
426 table->remove(pos: 0, length: 1);
427
428 QVERIFY(table->plainText().isEmpty());
429 table->undo();
430 QCOMPARE(table->plainText(), QString("ab"));
431}
432
433void tst_QTextPieceTable::undoRedo9()
434{
435 table->insert(pos: 0, text: "a", format: charFormatIndex);
436 table->insert(pos: 1, text: "b", format: charFormatIndex);
437 QCOMPARE(table->plainText(), QString("ab"));
438
439 table->remove(pos: 1, length: 1);
440 table->remove(pos: 0, length: 1);
441
442 QVERIFY(table->plainText().isEmpty());
443 table->undo();
444 QCOMPARE(table->plainText(), QString("ab"));
445}
446
447void tst_QTextPieceTable::undoRedo10()
448{
449 // testcase for the beginUndoBlock/endUndoBlock calls being surrounded by an if (undoEnabled)
450 QTextCharFormat cf;
451 cf.setForeground(Qt::blue);
452 int cfIdx = table->formatCollection()->indexForFormat(f: cf);
453
454 QTextBlockFormat f;
455 int idx = table->formatCollection()->indexForFormat(f);
456
457 table->insert(pos: 0, text: "a", format: cfIdx);
458 table->insertBlock(pos: 1, blockFormat: idx, charFormat: cfIdx);
459 table->insert(pos: 1, text: "b", format: cfIdx);
460
461 cf.setForeground(Qt::red);
462 int newCfIdx = table->formatCollection()->indexForFormat(f: cf);
463
464 table->setCharFormat(pos: 0, length: 3, newFormat: cf, mode: QTextDocumentPrivate::MergeFormat);
465
466 QCOMPARE(table->find(0).value()->format, newCfIdx);
467
468 table->undo();
469
470 QCOMPARE(table->find(0).value()->format, cfIdx);
471}
472
473void tst_QTextPieceTable::undoRedo11()
474{
475 const int loops = 20;
476 QString compare;
477 int l = 0;
478 for (int i = 0; i < loops; ++i) {
479 bool remove = l && (QRandomGenerator::global()->bounded(highest: 2));
480 int pos = (l > 1) ? QRandomGenerator::global()->bounded(highest: remove ? l-1 : l) : 0;
481 QChar c((unsigned short)((i % 26) + (i>25?'A':'a')));
482 QString str;
483 str += c;
484 str += c;
485 if (remove) {
486 compare.remove(i: pos, len: 1);
487 table->remove(pos, length: 1);
488 } else {
489 compare.insert(i: pos, s: str);
490 table->insert(pos, text: str, format: charFormatIndex);
491 }
492 l += remove ? -1 : 2;
493 }
494 QCOMPARE(table->plainText(), compare);
495 for (int i = 0; i < loops; ++i)
496 table->undo();
497 QCOMPARE(table->plainText(), QString(""));
498 for (int i = 0; i < loops; ++i)
499 table->redo();
500 QCOMPARE(table->plainText(), compare);
501}
502
503
504void tst_QTextPieceTable::checkDocumentChanged()
505{
506 table->enableUndoRedo(enable: false);
507 QTestDocumentLayout *layout = new QTestDocumentLayout(doc);
508 doc->setDocumentLayout(layout);
509
510 // single insert
511 layout->expect(from: 0, oldLength: 0, length: 15);
512 table->insert(pos: 0, text: "012345678901234", format: charFormatIndex);
513 QVERIFY(!layout->error);
514
515 // single remove
516 layout->expect(from: 0, oldLength: 5, length: 0);
517 table->remove(pos: 0, length: 5);
518 QVERIFY(!layout->error);
519
520 // symmetric insert/remove
521 layout->expect(from: 0, oldLength: 0, length: 0);
522 table->beginEditBlock();
523 table->insert(pos: 0, text: "01234", format: charFormatIndex);
524 table->remove(pos: 0, length: 5);
525 table->endEditBlock();
526 QVERIFY(!layout->error);
527
528 layout->expect(from: 0, oldLength: 5, length: 5);
529 table->beginEditBlock();
530 table->remove(pos: 0, length: 5);
531 table->insert(pos: 0, text: "01234", format: charFormatIndex);
532 table->endEditBlock();
533 QVERIFY(!layout->error);
534
535 // replace
536 layout->expect(from: 0, oldLength: 3, length: 5);
537 table->beginEditBlock();
538 table->remove(pos: 0, length: 3);
539 table->insert(pos: 0, text: "01234", format: charFormatIndex);
540 table->endEditBlock();
541 QVERIFY(!layout->error);
542
543 layout->expect(from: 0, oldLength: 0, length: 2);
544 table->beginEditBlock();
545 table->insert(pos: 0, text: "01234", format: charFormatIndex);
546 table->remove(pos: 0, length: 3);
547 table->endEditBlock();
548 QVERIFY(!layout->error);
549
550 // insert + remove inside insert block
551 layout->expect(from: 0, oldLength: 0, length: 2);
552 table->beginEditBlock();
553 table->insert(pos: 0, text: "01234", format: charFormatIndex);
554 table->remove(pos: 1, length: 3);
555 table->endEditBlock();
556 QVERIFY(!layout->error);
557
558 layout->expect(from: 0, oldLength: 0, length: 2);
559 table->beginEditBlock();
560 table->insert(pos: 0, text: "01234", format: charFormatIndex);
561 table->remove(pos: 2, length: 3);
562 table->endEditBlock();
563 QVERIFY(!layout->error);
564
565 // insert + remove partly outside
566 layout->expect(from: 0, oldLength: 1, length: 0);
567 table->beginEditBlock();
568 table->insert(pos: 1, text: "0", format: charFormatIndex);
569 table->remove(pos: 0, length: 2);
570 table->endEditBlock();
571 QVERIFY(!layout->error);
572
573 layout->expect(from: 0, oldLength: 1, length: 1);
574 table->beginEditBlock();
575 table->insert(pos: 1, text: "01", format: charFormatIndex);
576 table->remove(pos: 0, length: 2);
577 table->endEditBlock();
578 QVERIFY(!layout->error);
579
580 layout->expect(from: 0, oldLength: 1, length: 2);
581 table->beginEditBlock();
582 table->insert(pos: 1, text: "012", format: charFormatIndex);
583 table->remove(pos: 0, length: 2);
584 table->endEditBlock();
585 QVERIFY(!layout->error);
586
587 layout->expect(from: 1, oldLength: 1, length: 0);
588 table->beginEditBlock();
589 table->insert(pos: 1, text: "0", format: charFormatIndex);
590 table->remove(pos: 1, length: 2);
591 table->endEditBlock();
592 QVERIFY(!layout->error);
593
594 layout->expect(from: 1, oldLength: 1, length: 1);
595 table->beginEditBlock();
596 table->insert(pos: 1, text: "01", format: charFormatIndex);
597 table->remove(pos: 2, length: 2);
598 table->endEditBlock();
599 QVERIFY(!layout->error);
600
601 layout->expect(from: 1, oldLength: 1, length: 2);
602 table->beginEditBlock();
603 table->insert(pos: 1, text: "012", format: charFormatIndex);
604 table->remove(pos: 3, length: 2);
605 table->endEditBlock();
606 QVERIFY(!layout->error);
607
608 // insert + remove non overlapping
609 layout->expect(from: 0, oldLength: 1, length: 1);
610 table->beginEditBlock();
611 table->insert(pos: 1, text: "0", format: charFormatIndex);
612 table->remove(pos: 0, length: 1);
613 table->endEditBlock();
614 QVERIFY(!layout->error);
615
616 layout->expect(from: 0, oldLength: 2, length: 2);
617 table->beginEditBlock();
618 table->insert(pos: 2, text: "1", format: charFormatIndex);
619 table->remove(pos: 0, length: 1);
620 table->endEditBlock();
621 QVERIFY(!layout->error);
622
623 layout->expect(from: 0, oldLength: 2, length: 2);
624 table->beginEditBlock();
625 table->remove(pos: 0, length: 1);
626 table->insert(pos: 1, text: "0", format: charFormatIndex);
627 table->endEditBlock();
628 QVERIFY(!layout->error);
629
630 layout->expect(from: 0, oldLength: 3, length: 3);
631 table->beginEditBlock();
632 table->remove(pos: 0, length: 1);
633 table->insert(pos: 2, text: "1", format: charFormatIndex);
634 table->endEditBlock();
635
636
637 layout->expect(from: 0, oldLength: 3, length: 3);
638 QTextCharFormat fmt;
639 fmt.setForeground(Qt::blue);
640 table->beginEditBlock();
641 table->setCharFormat(pos: 0, length: 1, newFormat: fmt);
642 table->setCharFormat(pos: 2, length: 1, newFormat: fmt);
643 table->endEditBlock();
644 QVERIFY(!layout->error);
645}
646
647void tst_QTextPieceTable::checkDocumentChanged2()
648{
649 QTestDocumentLayout *layout = new QTestDocumentLayout(doc);
650 doc->setDocumentLayout(layout);
651
652 QTextCharFormat fmt;
653 fmt.setForeground(Qt::blue);
654 int anotherCharFormatIndex = table->formatCollection()->indexForFormat(f: fmt);
655
656 layout->expect(from: 0, oldLength: 0, length: 12);
657 table->beginEditBlock();
658 table->insert(pos: 0, text: "0123", format: charFormatIndex);
659 table->insert(pos: 4, text: "4567", format: anotherCharFormatIndex);
660 table->insert(pos: 8, text: "8901", format: charFormatIndex);
661 table->endEditBlock();
662 QVERIFY(!layout->error);
663
664 fmt.setFontItalic(true);
665
666 layout->expect(from: 1, oldLength: 10, length: 10);
667 table->beginEditBlock();
668 table->setCharFormat(pos: 8, length: 3, newFormat: fmt);
669 table->setCharFormat(pos: 4, length: 4, newFormat: fmt);
670 table->setCharFormat(pos: 1, length: 3, newFormat: fmt);
671 table->endEditBlock();
672 QVERIFY(!layout->error);
673}
674
675void tst_QTextPieceTable::setBlockFormat()
676{
677 QTextBlockFormat bfmt;
678 int index = table->formatCollection()->indexForFormat(f: bfmt);
679
680 table->insertBlock(pos: 0, blockFormat: index, charFormat: charFormatIndex);
681 table->insertBlock(pos: 0, blockFormat: index, charFormat: charFormatIndex);
682 table->insertBlock(pos: 0, blockFormat: index, charFormat: charFormatIndex);
683
684 QTextBlockFormat newbfmt = bfmt;
685 newbfmt.setAlignment(Qt::AlignRight);
686 index = table->formatCollection()->indexForFormat(f: bfmt);
687 QTextBlock b = table->blocksFind(pos: 1);
688 table->setBlockFormat(from: b, to: b, newFormat: newbfmt);
689
690 QCOMPARE(table->blocksFind(0).blockFormat(), bfmt);
691 QCOMPARE(table->blocksFind(1).blockFormat(), newbfmt);
692 QCOMPARE(table->blocksFind(2).blockFormat(), bfmt);
693}
694
695
696void tst_QTextPieceTable::blockInsertion()
697{
698 QTextBlockFormat fmt;
699 fmt.setTopMargin(100);
700 int idx = table->formatCollection()->indexForFormat(f: fmt);
701 int charFormat = table->formatCollection()->indexForFormat(f: QTextCharFormat());
702 QCOMPARE(table->blocksFind(0).blockFormat(), QTextBlockFormat());
703
704 table->insertBlock(pos: 0, blockFormat: idx, charFormat);
705 QCOMPARE(table->blocksFind(0).blockFormat(), QTextBlockFormat());
706 QCOMPARE(table->blocksFind(1).blockFormat(), fmt);
707
708 table->undo();
709 QCOMPARE(table->blockMap().length(), 1);
710 QCOMPARE(table->blocksFind(0).blockFormat(), QTextBlockFormat());
711
712 table->redo();
713 QCOMPARE(table->blocksFind(0).blockFormat(), QTextBlockFormat());
714 QCOMPARE(table->blocksFind(1).blockFormat(), fmt);
715}
716
717void tst_QTextPieceTable::blockInsertion2()
718{
719 // caused evil failing assertion in fragmentmap
720 int pos = 0;
721 table->insertBlock(pos, blockFormat: blockFormatIndex, charFormat: charFormatIndex);
722 pos += 1;
723 table->insert(pos, text: "a", format: charFormatIndex);
724 pos += 1;
725
726 pos -= 1;
727 table->insertBlock(pos, blockFormat: blockFormatIndex, charFormat: charFormatIndex);
728 QCOMPARE(table->blocksFind(0).position(), 0);
729 QCOMPARE(table->blocksFind(1).position(), 1);
730 QCOMPARE(table->blocksFind(2).position(), 2);
731}
732
733/*
734 Tests correct removal behaviour when deleting over block boundaries or complete blocks.
735*/
736
737void tst_QTextPieceTable::blockRemoval1()
738{
739 QTextBlockFormat fmt1;
740 fmt1.setTopMargin(100);
741 QTextBlockFormat fmt2;
742 fmt2.setAlignment(Qt::AlignRight);
743 int idx1 = table->formatCollection()->indexForFormat(f: fmt1);
744 int idx2 = table->formatCollection()->indexForFormat(f: fmt2);
745
746 table->insert(pos: 0, text: "0123", format: charFormatIndex);
747 table->insertBlock(pos: 4, blockFormat: idx1, charFormat: charFormatIndex);
748 table->insert(pos: 5, text: "5678", format: charFormatIndex);
749 table->insertBlock(pos: 9, blockFormat: idx2, charFormat: charFormatIndex);
750 table->insert(pos: 10, text: "0123", format: charFormatIndex);
751
752 QCOMPARE(table->blocksFind(0).blockFormat(), QTextBlockFormat());
753 QCOMPARE(table->blocksFind(4).blockFormat(), QTextBlockFormat());
754 QCOMPARE(table->blocksFind(5).blockFormat(), fmt1);
755 QCOMPARE(table->blocksFind(10).blockFormat(), fmt2);
756 QCOMPARE(table->blocksFind(1).position(), 0);
757 QCOMPARE(table->blocksFind(6).position(), 5);
758 QCOMPARE(table->blocksFind(11).position(), 10);
759
760 table->beginEditBlock();
761 table->remove(pos: 5, length: 5);
762 table->endEditBlock();
763 QCOMPARE(table->blocksFind(4).blockFormat(), QTextBlockFormat());
764 QCOMPARE(table->blocksFind(5).blockFormat(), fmt2);
765 QCOMPARE(table->blocksFind(4).position(), 0);
766 QCOMPARE(table->blocksFind(5).position(), 5);
767
768 table->undo();
769
770 QCOMPARE(table->blocksFind(0).blockFormat(), QTextBlockFormat());
771 QCOMPARE(table->blocksFind(4).blockFormat(), QTextBlockFormat());
772 QCOMPARE(table->blocksFind(5).blockFormat(), fmt1);
773 QCOMPARE(table->blocksFind(10).blockFormat(), fmt2);
774 QCOMPARE(table->blocksFind(1).position(), 0);
775 QCOMPARE(table->blocksFind(6).position(), 5);
776 QCOMPARE(table->blocksFind(11).position(), 10);
777
778 table->redo();
779 QCOMPARE(table->blocksFind(4).blockFormat(), QTextBlockFormat());
780 QCOMPARE(table->blocksFind(5).blockFormat(), fmt2);
781 QCOMPARE(table->blocksFind(4).position(), 0);
782 QCOMPARE(table->blocksFind(5).position(), 5);
783}
784
785void tst_QTextPieceTable::blockRemoval2()
786{
787 QTextBlockFormat fmt1;
788 fmt1.setTopMargin(100);
789 QTextBlockFormat fmt2;
790 fmt2.setAlignment(Qt::AlignRight);
791 int idx1 = table->formatCollection()->indexForFormat(f: fmt1);
792 int idx2 = table->formatCollection()->indexForFormat(f: fmt2);
793
794 table->insert(pos: 0, text: "0123", format: charFormatIndex);
795 table->insertBlock(pos: 4, blockFormat: idx1, charFormat: charFormatIndex);
796 table->insert(pos: 5, text: "5678", format: charFormatIndex);
797 table->insertBlock(pos: 9, blockFormat: idx2, charFormat: charFormatIndex);
798 table->insert(pos: 10, text: "0123", format: charFormatIndex);
799
800 QCOMPARE(table->blocksFind(0).blockFormat(), QTextBlockFormat());
801 QCOMPARE(table->blocksFind(4).blockFormat(), QTextBlockFormat());
802 QCOMPARE(table->blocksFind(5).blockFormat(), fmt1);
803 QCOMPARE(table->blocksFind(10).blockFormat(), fmt2);
804 QCOMPARE(table->blocksFind(1).position(), 0);
805 QCOMPARE(table->blocksFind(6).position(), 5);
806 QCOMPARE(table->blocksFind(11).position(), 10);
807
808 table->remove(pos: 4, length: 1);
809 QCOMPARE(table->blocksFind(4).blockFormat(), QTextBlockFormat());
810 QCOMPARE(table->blocksFind(6).blockFormat(), QTextBlockFormat());
811 QCOMPARE(table->blocksFind(4).position(), 0);
812 QCOMPARE(table->blocksFind(6).position(), 0);
813
814 table->undo();
815
816 QCOMPARE(table->blocksFind(0).blockFormat(), QTextBlockFormat());
817 QCOMPARE(table->blocksFind(4).blockFormat(), QTextBlockFormat());
818 QCOMPARE(table->blocksFind(5).blockFormat(), fmt1);
819 QCOMPARE(table->blocksFind(10).blockFormat(), fmt2);
820 QCOMPARE(table->blocksFind(1).position(), 0);
821 QCOMPARE(table->blocksFind(6).position(), 5);
822 QCOMPARE(table->blocksFind(11).position(), 10);
823
824 table->redo();
825 QCOMPARE(table->blocksFind(4).blockFormat(), QTextBlockFormat());
826 QCOMPARE(table->blocksFind(6).blockFormat(), QTextBlockFormat());
827 QCOMPARE(table->blocksFind(4).position(), 0);
828 QCOMPARE(table->blocksFind(6).position(), 0);
829}
830
831void tst_QTextPieceTable::blockRemoval3()
832{
833 QTextBlockFormat fmt1;
834 fmt1.setTopMargin(100);
835 QTextBlockFormat fmt2;
836 fmt2.setAlignment(Qt::AlignRight);
837 int idx1 = table->formatCollection()->indexForFormat(f: fmt1);
838 int idx2 = table->formatCollection()->indexForFormat(f: fmt2);
839
840 table->insert(pos: 0, text: "0123", format: charFormatIndex);
841 table->insertBlock(pos: 4, blockFormat: idx1, charFormat: charFormatIndex);
842 table->insert(pos: 5, text: "5678", format: charFormatIndex);
843 table->insertBlock(pos: 9, blockFormat: idx2, charFormat: charFormatIndex);
844 table->insert(pos: 10, text: "0123", format: charFormatIndex);
845
846 QCOMPARE(table->blocksFind(0).blockFormat(), QTextBlockFormat());
847 QCOMPARE(table->blocksFind(4).blockFormat(), QTextBlockFormat());
848 QCOMPARE(table->blocksFind(5).blockFormat(), fmt1);
849 QCOMPARE(table->blocksFind(10).blockFormat(), fmt2);
850 QCOMPARE(table->blocksFind(1).position(), 0);
851 QCOMPARE(table->blocksFind(6).position(), 5);
852 QCOMPARE(table->blocksFind(11).position(), 10);
853
854 table->beginEditBlock();
855 table->remove(pos: 3, length: 4);
856 table->endEditBlock();
857
858 QCOMPARE(table->blocksFind(1).blockFormat(), QTextBlockFormat());
859 QCOMPARE(table->blocksFind(5).blockFormat(), QTextBlockFormat());
860 QCOMPARE(table->blocksFind(1).position(), 0);
861 QCOMPARE(table->blocksFind(5).position(), 0);
862
863 table->undo();
864
865 QCOMPARE(table->blocksFind(0).blockFormat(), QTextBlockFormat());
866 QCOMPARE(table->blocksFind(4).blockFormat(), QTextBlockFormat());
867 QCOMPARE(table->blocksFind(5).blockFormat(), fmt1);
868 QCOMPARE(table->blocksFind(10).blockFormat(), fmt2);
869 QCOMPARE(table->blocksFind(1).position(), 0);
870 QCOMPARE(table->blocksFind(6).position(), 5);
871 QCOMPARE(table->blocksFind(11).position(), 10);
872
873 table->redo();
874 QCOMPARE(table->blocksFind(1).blockFormat(), QTextBlockFormat());
875 QCOMPARE(table->blocksFind(5).blockFormat(), QTextBlockFormat());
876 QCOMPARE(table->blocksFind(1).position(), 0);
877 QCOMPARE(table->blocksFind(5).position(), 0);
878}
879
880void tst_QTextPieceTable::blockRemoval4()
881{
882#if 0
883 QTextBlockFormat fmt1;
884 fmt1.setTopMargin(100);
885 QTextBlockFormat fmt2;
886 fmt2.setAlignment(Qt::AlignRight);
887 int idx1 = table->formatCollection()->indexForFormat(fmt1);
888 int idx2 = table->formatCollection()->indexForFormat(fmt2);
889
890 table->insert(0, "0123", charFormatIndex);
891 table->insertBlock(4, idx1, charFormatIndex);
892 table->insert(5, "5678", charFormatIndex);
893 table->insertBlock(9, idx2, charFormatIndex);
894 table->insert(10, "0123", charFormatIndex);
895
896 QCOMPARE(table->blocksFind(0).blockFormat(), QTextBlockFormat());
897 QCOMPARE(table->blocksFind(4).blockFormat(), QTextBlockFormat());
898 QCOMPARE(table->blocksFind(5).blockFormat(), fmt1);
899 QCOMPARE(table->blocksFind(10).blockFormat(), fmt2);
900 QCOMPARE(table->blocksFind(1).position(), 0);
901 QCOMPARE(table->blocksFind(6).position(), 5);
902 QCOMPARE(table->blocksFind(11).position(), 10);
903
904 table->remove(3, 7);
905 QCOMPARE(table->blocksFind(1).position(), 0);
906 QCOMPARE(table->blocksFind(5).position(), 0);
907 QCOMPARE(table->blocksFind(1).blockFormat(), QTextBlockFormat());
908 QCOMPARE(table->blocksFind(5).blockFormat(), QTextBlockFormat());
909
910 table->undo();
911
912 QCOMPARE(table->blocksFind(0).blockFormat(), QTextBlockFormat());
913 QCOMPARE(table->blocksFind(4).blockFormat(), QTextBlockFormat());
914 QCOMPARE(table->blocksFind(5).blockFormat(), fmt1);
915 QCOMPARE(table->blocksFind(10).blockFormat(), fmt2);
916 QCOMPARE(table->blocksFind(1).position(), 0);
917 QCOMPARE(table->blocksFind(6).position(), 5);
918 QCOMPARE(table->blocksFind(11).position(), 10);
919
920 table->redo();
921 QCOMPARE(table->blocksFind(1).position(), 0);
922 QCOMPARE(table->blocksFind(5).position(), 0);
923 QCOMPARE(table->blocksFind(1).blockFormat(), QTextBlockFormat());
924 QCOMPARE(table->blocksFind(5).blockFormat(), QTextBlockFormat());
925#endif
926}
927
928void tst_QTextPieceTable::blockRemoval5()
929{
930 QTextBlockFormat fmt1;
931 fmt1.setTopMargin(100);
932 QTextBlockFormat fmt2;
933 fmt2.setAlignment(Qt::AlignRight);
934 int idx1 = table->formatCollection()->indexForFormat(f: fmt1);
935 int idx2 = table->formatCollection()->indexForFormat(f: fmt2);
936
937 table->insert(pos: 0, text: "0123", format: charFormatIndex);
938 table->insertBlock(pos: 4, blockFormat: idx1, charFormat: charFormatIndex);
939 table->insert(pos: 5, text: "5678", format: charFormatIndex);
940 table->insertBlock(pos: 9, blockFormat: idx2, charFormat: charFormatIndex);
941 table->insert(pos: 10, text: "0123", format: charFormatIndex);
942
943 QCOMPARE(table->blocksFind(0).blockFormat(), QTextBlockFormat());
944 QCOMPARE(table->blocksFind(4).blockFormat(), QTextBlockFormat());
945 QCOMPARE(table->blocksFind(5).blockFormat(), fmt1);
946 QCOMPARE(table->blocksFind(10).blockFormat(), fmt2);
947 QCOMPARE(table->blocksFind(1).position(), 0);
948 QCOMPARE(table->blocksFind(6).position(), 5);
949 QCOMPARE(table->blocksFind(11).position(), 10);
950
951 table->beginEditBlock();
952 table->remove(pos: 3, length: 8);
953 table->endEditBlock();
954
955 QCOMPARE(table->blocksFind(0).blockFormat(), QTextBlockFormat());
956 QCOMPARE(table->blocksFind(5).blockFormat(), QTextBlockFormat());
957 QCOMPARE(table->blocksFind(1).position(), 0);
958 QCOMPARE(table->blocksFind(5).position(), 0);
959
960 table->undo();
961
962 QCOMPARE(table->blocksFind(0).blockFormat(), QTextBlockFormat());
963 QCOMPARE(table->blocksFind(4).blockFormat(), QTextBlockFormat());
964 QCOMPARE(table->blocksFind(5).blockFormat(), fmt1);
965 QCOMPARE(table->blocksFind(10).blockFormat(), fmt2);
966 QCOMPARE(table->blocksFind(1).position(), 0);
967 QCOMPARE(table->blocksFind(6).position(), 5);
968 QCOMPARE(table->blocksFind(11).position(), 10);
969
970 table->redo();
971 QCOMPARE(table->blocksFind(0).blockFormat(), QTextBlockFormat());
972 QCOMPARE(table->blocksFind(5).blockFormat(), QTextBlockFormat());
973 QCOMPARE(table->blocksFind(1).position(), 0);
974 QCOMPARE(table->blocksFind(5).position(), 0);
975}
976
977
978void tst_QTextPieceTable::checkBlockSeparation()
979{
980 table->insertBlock(pos: 0, blockFormat: blockFormatIndex, charFormat: charFormatIndex);
981 table->insertBlock(pos: 1, blockFormat: blockFormatIndex, charFormat: charFormatIndex);
982
983 QVERIFY(table->find(0) != table->find(1));
984}
985
986void tst_QTextPieceTable::checkFrames1()
987{
988 QTextFrameFormat ffmt;
989 table->insert(pos: 0, text: "Hello", format: charFormatIndex);
990 QPointer<QTextFrame> frame = table->insertFrame(start: 1, end: 3, format: ffmt);
991 QTextFrame *root = table->rootFrame();
992
993 QCOMPARE(root, frame->parentFrame());
994
995 QVERIFY(root);
996 QVERIFY(!root->parentFrame());
997
998 QCOMPARE(root->childFrames().count(), 1);
999 QVERIFY(frame->format() == ffmt);
1000 QCOMPARE(frame->firstPosition(), 2);
1001 QCOMPARE(frame->lastPosition(), 4);
1002
1003
1004 QPointer<QTextFrame> frame2 = table->insertFrame(start: 2, end: 3, format: ffmt);
1005
1006 QCOMPARE(root->childFrames().count(), 1);
1007 QCOMPARE(root->childFrames().at(0), frame.data());
1008 QCOMPARE(frame->childFrames().count(), 1);
1009 QCOMPARE(frame2->childFrames().count(), 0);
1010 QCOMPARE(frame2->parentFrame(), frame.data());
1011 QCOMPARE(frame2->firstPosition(), 3);
1012 QCOMPARE(frame2->lastPosition(), 4);
1013
1014 QVERIFY(frame->format() == ffmt);
1015 QCOMPARE(frame->firstPosition(), 2);
1016 QCOMPARE(frame->lastPosition(), 6);
1017
1018 table->removeFrame(frame);
1019
1020 QCOMPARE(root->childFrames().count(), 1);
1021 QCOMPARE(root->childFrames().at(0), frame2.data());
1022 QVERIFY(!frame);
1023 QCOMPARE(frame2->childFrames().count(), 0);
1024 QCOMPARE(frame2->parentFrame(), root);
1025 QCOMPARE(frame2->firstPosition(), 2);
1026 QCOMPARE(frame2->lastPosition(), 3);
1027
1028 table->undo();
1029
1030 frame = table->frameAt(pos: 2);
1031
1032 QCOMPARE(root->childFrames().count(), 1);
1033 QCOMPARE(root->childFrames().at(0), frame.data());
1034 QCOMPARE(frame->childFrames().count(), 1);
1035 QCOMPARE(frame->childFrames().at(0), frame2.data());
1036 QCOMPARE(frame2->childFrames().count(), 0);
1037 QCOMPARE(frame2->parentFrame(), frame.data());
1038 QCOMPARE(frame2->firstPosition(), 3);
1039 QCOMPARE(frame2->lastPosition(), 4);
1040
1041 QCOMPARE(frame->firstPosition(), 2);
1042 QCOMPARE(frame->lastPosition(), 6);
1043
1044 table->undo();
1045
1046 QCOMPARE(root->childFrames().count(), 1);
1047 QCOMPARE(root->childFrames().at(0), frame.data());
1048 QCOMPARE(frame->childFrames().count(), 0);
1049 QVERIFY(!frame2);
1050
1051 QCOMPARE(frame->firstPosition(), 2);
1052 QCOMPARE(frame->lastPosition(), 4);
1053}
1054
1055void tst_QTextPieceTable::removeFrameDirect()
1056{
1057 QTextFrameFormat ffmt;
1058 table->insert(pos: 0, text: "Hello", format: charFormatIndex);
1059
1060 QTextFrame *frame = table->insertFrame(start: 1, end: 5, format: ffmt);
1061
1062 QCOMPARE(frame->parentFrame(), table->rootFrame());
1063
1064 const int start = frame->firstPosition() - 1;
1065 const int end = frame->lastPosition();
1066 const int length = end - start + 1;
1067
1068 table->remove(pos: start, length);
1069}
1070
1071void tst_QTextPieceTable::removeWithChildFrame()
1072{
1073 /*
1074 The piecetable layout is:
1075
1076 ...
1077 1 BeginningOfFrame(first frame)
1078 2 text
1079 3 BeginningOfFrame(second frame)
1080 4 text
1081 5 text
1082 6 EndOfFrame(second frame)
1083 7 text
1084 8 text
1085 9 EndOfFrame(first frame)
1086 ...
1087
1088 The idea is to remove from [2] until [6], basically some trailing text and the second frame.
1089 In this case frameAt(2) != frameAt(6), so the assertion in remove() needed an adjustement.
1090 */
1091 QTextFrameFormat ffmt;
1092 table->insert(pos: 0, text: "Hello World", format: charFormatIndex);
1093
1094 QTextFrame *frame = table->insertFrame(start: 1, end: 6, format: ffmt);
1095 QTextFrame *childFrame = table->insertFrame(start: 3, end: 5, format: ffmt);
1096 Q_UNUSED(frame);
1097 Q_UNUSED(childFrame);
1098
1099 // used to give a failing assertion
1100 table->remove(pos: 2, length: 5);
1101 QVERIFY(true);
1102}
1103
1104void tst_QTextPieceTable::clearWithFrames()
1105{
1106 /*
1107 The piecetable layout is:
1108
1109 ...
1110 1 BeginningOfFrame(first frame)
1111 2 text
1112 3 EndOfFrame(first frame)
1113 4 BeginningOfFrame(second frame)
1114 5 text
1115 6 text
1116 7 EndOfFrame(second frame)
1117 ...
1118
1119 The idea is to remove from [1] until [7].
1120 */
1121 QTextFrameFormat ffmt;
1122 table->insert(pos: 0, text: "Hello World", format: charFormatIndex);
1123
1124 QTextFrame *firstFrame = table->insertFrame(start: 1, end: 2, format: ffmt);
1125 QTextFrame *secondFrame = table->insertFrame(start: 4, end: 6, format: ffmt);
1126
1127 const int start = firstFrame->firstPosition() - 1;
1128 const int end = secondFrame->lastPosition();
1129 const int length = end - start + 1;
1130 // used to give a failing assertion
1131 table->remove(pos: start, length);
1132 QVERIFY(true);
1133}
1134
1135QTEST_MAIN(tst_QTextPieceTable)
1136
1137
1138#include "tst_qtextpiecetable.moc"
1139
1140

source code of qtbase/tests/auto/gui/text/qtextpiecetable/tst_qtextpiecetable.cpp