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#include <qtextdocument.h>
33#include <qtextdocumentfragment.h>
34#include <qtextlist.h>
35#include <qabstracttextdocumentlayout.h>
36#include <qtextcursor.h>
37#include "../qtextdocument/common.h"
38
39class tst_QTextList : public QObject
40{
41 Q_OBJECT
42
43private slots:
44 void init();
45 void cleanup();
46 void item();
47 void autoNumbering();
48 void autoNumberingRTL();
49 void autoNumberingPrefixAndSuffix();
50 void autoNumberingPrefixAndSuffixRTL();
51 void autoNumberingPrefixAndSuffixHtmlExportImport();
52 void romanNumbering();
53 void romanNumberingLimit();
54 void formatChange();
55 void cursorNavigation();
56 void partialRemoval();
57 void formatReferenceChange();
58 void ensureItemOrder();
59 void add();
60 void defaultIndent();
61 void blockUpdate();
62 void numbering_data();
63 void numbering();
64
65private:
66 QTextDocument *doc;
67 QTextCursor cursor;
68 QTestDocumentLayout *layout;
69};
70
71void tst_QTextList::init()
72{
73 doc = new QTextDocument();
74 layout = new QTestDocumentLayout(doc);
75 doc->setDocumentLayout(layout);
76 cursor = QTextCursor(doc);
77}
78
79void tst_QTextList::cleanup()
80{
81 cursor = QTextCursor();
82 delete doc;
83 doc = 0;
84}
85
86void tst_QTextList::item()
87{
88 // this is basically a test for the key() + 1 in QTextList::item.
89 QTextList *list = cursor.createList(format: QTextListFormat());
90 QVERIFY(list->item(0).blockFormat().objectIndex() != -1);
91}
92
93void tst_QTextList::autoNumbering()
94{
95 QTextListFormat fmt;
96 fmt.setStyle(QTextListFormat::ListLowerAlpha);
97 QTextList *list = cursor.createList(format: fmt);
98 QVERIFY(list);
99
100 for (int i = 0; i < 27; ++i)
101 cursor.insertBlock();
102
103 QCOMPARE(list->count(), 28);
104
105 QVERIFY(cursor.currentList());
106 QCOMPARE(cursor.currentList()->itemNumber(cursor.block()), 27);
107 QCOMPARE(cursor.currentList()->itemText(cursor.block()), QLatin1String("ab."));
108}
109
110void tst_QTextList::autoNumberingPrefixAndSuffix()
111{
112 QTextListFormat fmt;
113 fmt.setStyle(QTextListFormat::ListLowerAlpha);
114 fmt.setNumberPrefix("-");
115 fmt.setNumberSuffix(")");
116 QTextList *list = cursor.createList(format: fmt);
117 QVERIFY(list);
118
119 for (int i = 0; i < 27; ++i)
120 cursor.insertBlock();
121
122 QCOMPARE(list->count(), 28);
123
124 QVERIFY(cursor.currentList());
125 QCOMPARE(cursor.currentList()->itemNumber(cursor.block()), 27);
126 QCOMPARE(cursor.currentList()->itemText(cursor.block()), QLatin1String("-ab)"));
127}
128
129void tst_QTextList::autoNumberingPrefixAndSuffixRTL()
130{
131 QTextBlockFormat bfmt;
132 bfmt.setLayoutDirection(Qt::RightToLeft);
133 cursor.setBlockFormat(bfmt);
134
135 QTextListFormat fmt;
136 fmt.setStyle(QTextListFormat::ListUpperAlpha);
137 fmt.setNumberPrefix("-");
138 fmt.setNumberSuffix("*");
139 QTextList *list = cursor.createList(format: fmt);
140 QVERIFY(list);
141
142 cursor.insertBlock();
143
144 QCOMPARE(list->count(), 2);
145
146 QCOMPARE(cursor.currentList()->itemText(cursor.block()), QLatin1String("*B-"));
147}
148
149void tst_QTextList::autoNumberingPrefixAndSuffixHtmlExportImport()
150{
151 QTextListFormat fmt;
152 fmt.setStyle(QTextListFormat::ListLowerAlpha);
153 fmt.setNumberPrefix("\"");
154 fmt.setNumberSuffix("#");
155 fmt.setIndent(10);
156 // FIXME: Would like to test "'" but there's a problem in the css parser (Scanner::preprocess
157 // is called before the values are being parsed), so the quoting does not work.
158 QTextList *list = cursor.createList(format: fmt);
159 QVERIFY(list);
160
161 for (int i = 0; i < 27; ++i)
162 cursor.insertBlock();
163
164 QCOMPARE(list->count(), 28);
165
166 QString htmlExport = doc->toHtml();
167 QTextDocument importDoc;
168 importDoc.setHtml(htmlExport);
169
170 QTextCursor importCursor(&importDoc);
171 for (int i = 0; i < 27; ++i)
172 importCursor.movePosition(op: QTextCursor::NextBlock);
173
174 QVERIFY(importCursor.currentList());
175 QCOMPARE(importCursor.currentList()->itemNumber(importCursor.block()), 27);
176 QCOMPARE(importCursor.currentList()->itemText(importCursor.block()), QLatin1String("\"ab#"));
177 QCOMPARE(importCursor.currentList()->format().indent(), 10);
178}
179
180void tst_QTextList::autoNumberingRTL()
181{
182 QTextBlockFormat bfmt;
183 bfmt.setLayoutDirection(Qt::RightToLeft);
184 cursor.setBlockFormat(bfmt);
185
186 QTextListFormat fmt;
187 fmt.setStyle(QTextListFormat::ListUpperAlpha);
188 QTextList *list = cursor.createList(format: fmt);
189 QVERIFY(list);
190
191 cursor.insertBlock();
192
193 QCOMPARE(list->count(), 2);
194
195 QCOMPARE(cursor.currentList()->itemText(cursor.block()), QLatin1String(".B"));
196}
197
198void tst_QTextList::romanNumbering()
199{
200 QTextListFormat fmt;
201 fmt.setStyle(QTextListFormat::ListUpperRoman);
202 QTextList *list = cursor.createList(format: fmt);
203 QVERIFY(list);
204
205 for (int i = 0; i < 4998; ++i)
206 cursor.insertBlock();
207
208 QCOMPARE(list->count(), 4999);
209
210 QVERIFY(cursor.currentList());
211 QCOMPARE(cursor.currentList()->itemNumber(cursor.block()), 4998);
212 QCOMPARE(cursor.currentList()->itemText(cursor.block()), QLatin1String("MMMMCMXCIX."));
213}
214
215void tst_QTextList::romanNumberingLimit()
216{
217 QTextListFormat fmt;
218 fmt.setStyle(QTextListFormat::ListLowerRoman);
219 QTextList *list = cursor.createList(format: fmt);
220 QVERIFY(list);
221
222 for (int i = 0; i < 4999; ++i)
223 cursor.insertBlock();
224
225 QCOMPARE(list->count(), 5000);
226
227 QVERIFY(cursor.currentList());
228 QCOMPARE(cursor.currentList()->itemNumber(cursor.block()), 4999);
229 QCOMPARE(cursor.currentList()->itemText(cursor.block()), QLatin1String("?."));
230}
231
232void tst_QTextList::formatChange()
233{
234 // testing the formatChanged slot in QTextListManager
235
236 /* <initial block>
237 * 1.
238 * 2.
239 */
240 QTextList *list = cursor.insertList(style: QTextListFormat::ListDecimal);
241 QTextList *firstList = list;
242 cursor.insertBlock();
243
244 QVERIFY(list && list->count() == 2);
245
246 QTextBlockFormat bfmt = cursor.blockFormat();
247// QCOMPARE(bfmt.object(), list);
248
249 bfmt.setObjectIndex(-1);
250 cursor.setBlockFormat(bfmt);
251
252 QCOMPARE(firstList->count(), 1);
253}
254
255void tst_QTextList::cursorNavigation()
256{
257 // testing some cursor list methods
258
259 /* <initial block>
260 * 1.
261 * 2.
262 */
263 cursor.insertList(style: QTextListFormat::ListDecimal);
264 cursor.insertBlock();
265
266 cursor.movePosition(op: QTextCursor::Start);
267 cursor.movePosition(op: QTextCursor::NextBlock);
268 cursor.movePosition(op: QTextCursor::NextBlock);
269 QVERIFY(cursor.currentList());
270 cursor.movePosition(op: QTextCursor::PreviousBlock);
271 QVERIFY(cursor.currentList());
272 QCOMPARE(cursor.currentList()->itemNumber(cursor.block()), 0);
273}
274
275void tst_QTextList::partialRemoval()
276{
277 /* this is essentially a test for PieceTable::removeBlock to not miss any
278 blocks with the blockChanged signal emission that actually get removed.
279
280 It creates two lists, like this:
281
282 1. Hello World
283 a. Foobar
284
285 and then removes from within the 'Hello World' into the 'Foobar' .
286 There used to be no emission for the removal of the second (a.) block,
287 causing list inconsistencies.
288
289 */
290
291 QTextList *firstList = cursor.insertList(style: QTextListFormat::ListDecimal);
292
293 QTextCursor selStart = cursor;
294 selStart.movePosition(op: QTextCursor::PreviousCharacter);
295
296 cursor.insertText(text: "Hello World");
297
298 // position it well into the 'hello world' text.
299 selStart.movePosition(op: QTextCursor::NextCharacter);
300 selStart.movePosition(op: QTextCursor::NextCharacter);
301 selStart.clearSelection();
302
303 QPointer<QTextList> secondList = cursor.insertList(style: QTextListFormat::ListCircle);
304 cursor.insertText(text: "Foobar");
305
306 // position it into the 'foo bar' text.
307 cursor.movePosition(op: QTextCursor::PreviousCharacter);
308 QTextCursor selEnd = cursor;
309
310 // this creates a selection that includes parts of both text-fragments and also the list item of the second list.
311 QTextCursor selection = selStart;
312 selection.setPosition(pos: selEnd.position(), mode: QTextCursor::KeepAnchor);
313
314 selection.deleteChar(); // deletes the second list
315
316 QVERIFY(!secondList);
317 QVERIFY(firstList->count() > 0);
318
319 doc->undo();
320}
321
322void tst_QTextList::formatReferenceChange()
323{
324 QTextList *list = cursor.insertList(style: QTextListFormat::ListDecimal);
325 cursor.insertText(text: "Some Content...");
326 cursor.insertBlock(format: QTextBlockFormat());
327
328 cursor.setPosition(pos: list->item(i: 0).position());
329 int listItemStartPos = cursor.position();
330 cursor.movePosition(op: QTextCursor::NextBlock);
331 int listItemLen = cursor.position() - listItemStartPos;
332 layout->expect(from: listItemStartPos, oldLength: listItemLen, length: listItemLen);
333
334 QTextListFormat fmt = list->format();
335 fmt.setStyle(QTextListFormat::ListCircle);
336 list->setFormat(fmt);
337
338 QVERIFY(layout->called);
339 QVERIFY(!layout->error);
340}
341
342void tst_QTextList::ensureItemOrder()
343{
344 /*
345 * Insert a new list item before the first one and verify the blocks
346 * are sorted after that.
347 */
348 QTextList *list = cursor.insertList(style: QTextListFormat::ListDecimal);
349
350 QTextBlockFormat fmt = cursor.blockFormat();
351 cursor.movePosition(op: QTextCursor::Start);
352 cursor.insertBlock(format: fmt);
353
354 QCOMPARE(list->item(0).position(), 1);
355 QCOMPARE(list->item(1).position(), 2);
356}
357
358void tst_QTextList::add()
359{
360 QTextList *list = cursor.insertList(style: QTextListFormat::ListDecimal);
361 cursor.insertBlock(format: QTextBlockFormat());
362 QCOMPARE(list->count(), 1);
363 cursor.insertBlock(format: QTextBlockFormat());
364 list->add(block: cursor.block());
365 QCOMPARE(list->count(), 2);
366}
367
368// Task #72036
369void tst_QTextList::defaultIndent()
370{
371 QTextListFormat fmt;
372 QCOMPARE(fmt.indent(), 1);
373}
374
375void tst_QTextList::blockUpdate()
376{
377 // three items
378 QTextList *list = cursor.insertList(style: QTextListFormat::ListDecimal);
379 cursor.insertBlock();
380 cursor.insertBlock();
381
382 // remove second, needs also update on the third
383 // since the numbering might have changed
384 const int len = cursor.position() + cursor.block().length() - 1;
385 layout->expect(from: 1, oldLength: len, length: len);
386 list->remove(list->item(i: 1));
387 QVERIFY(!layout->error);
388}
389
390void tst_QTextList::numbering_data()
391{
392 QTest::addColumn<int>(name: "format");
393 QTest::addColumn<int>(name: "number");
394 QTest::addColumn<QString>(name: "result");
395
396 QTest::newRow(dataTag: "E.") << int(QTextListFormat::ListUpperAlpha) << 5 << "E.";
397 QTest::newRow(dataTag: "abc.") << int(QTextListFormat::ListLowerAlpha) << (26 + 2) * 26 + 3 << "abc.";
398 QTest::newRow(dataTag: "12.") << int(QTextListFormat::ListDecimal) << 12 << "12.";
399 QTest::newRow(dataTag: "XXIV.") << int(QTextListFormat::ListUpperRoman) << 24 << "XXIV.";
400 QTest::newRow(dataTag: "VIII.") << int(QTextListFormat::ListUpperRoman) << 8 << "VIII.";
401 QTest::newRow(dataTag: "xxx.") << int(QTextListFormat::ListLowerRoman) << 30 << "xxx.";
402 QTest::newRow(dataTag: "xxix.") << int(QTextListFormat::ListLowerRoman) << 29 << "xxix.";
403// QTest::newRow("xxx. alpha") << int(QTextListFormat::ListLowerAlpha) << (24 * 26 + 24) * 26 + 24 << "xxx."; //Too slow
404}
405
406void tst_QTextList::numbering()
407{
408 QFETCH(int, format);
409 QFETCH(int, number);
410 QFETCH(QString, result);
411
412
413 QTextListFormat fmt;
414 fmt.setStyle(QTextListFormat::Style(format));
415 QTextList *list = cursor.createList(format: fmt);
416 QVERIFY(list);
417
418 for (int i = 1; i < number; ++i)
419 cursor.insertBlock();
420
421 QCOMPARE(list->count(), number);
422
423 QVERIFY(cursor.currentList());
424 QCOMPARE(cursor.currentList()->itemNumber(cursor.block()), number - 1);
425 QCOMPARE(cursor.currentList()->itemText(cursor.block()), result);
426}
427
428QTEST_MAIN(tst_QTextList)
429#include "tst_qtextlist.moc"
430

source code of qtbase/tests/auto/gui/text/qtextlist/tst_qtextlist.cpp