1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4
5#include "qtextlist.h"
6#include "qtextobject_p.h"
7#include "qtextcursor.h"
8#include "qtextdocument_p.h"
9#include <qdebug.h>
10
11QT_BEGIN_NAMESPACE
12
13using namespace Qt::StringLiterals;
14
15class QTextListPrivate : public QTextBlockGroupPrivate
16{
17public:
18 QTextListPrivate(QTextDocument *doc)
19 : QTextBlockGroupPrivate(doc)
20 {
21 }
22};
23
24/*!
25 \class QTextList
26 \reentrant
27
28 \brief The QTextList class provides a decorated list of items in a QTextDocument.
29 \inmodule QtGui
30
31 \ingroup richtext-processing
32
33 A list contains a sequence of text blocks, each of which is marked with a
34 bullet point or other symbol. Multiple levels of lists can be used, and
35 the automatic numbering feature provides support for ordered numeric and
36 alphabetical lists.
37
38 Lists are created by using a text cursor to insert an empty list at the
39 current position or by moving existing text into a new list.
40 The \l{QTextCursor::insertList()} function inserts an empty block into the
41 document at the cursor position, and makes it the first item in a list.
42
43 \snippet textdocument-lists/mainwindow.cpp 0
44
45 The \l{QTextCursor::createList()} function takes the contents of the
46 cursor's current block and turns it into the first item of a new list.
47
48 The cursor's current list is found with \l{QTextCursor::currentList()}.
49
50 The number of items in a list is given by count(). Each item can be
51 obtained by its index in the list with the item() function. Similarly,
52 the index of a given item can be found with itemNumber(). The text of
53 each item can be found with the itemText() function.
54
55 Note that the items in the list may not be adjacent elements in the
56 document. For example, the top-level items in a multi-level list will
57 be separated by the items in lower levels of the list.
58
59 List items can be deleted by index with the removeItem() function.
60 remove() deletes the specified item in the list.
61
62 The list's format is set with setFormat() and read with format().
63 The format describes the decoration of the list itself, and not the
64 individual items.
65
66 \sa QTextBlock, QTextListFormat, QTextCursor
67*/
68
69/*! \internal
70 */
71QTextList::QTextList(QTextDocument *doc)
72 : QTextBlockGroup(*new QTextListPrivate(doc), doc)
73{
74}
75
76/*!
77 \internal
78*/
79QTextList::~QTextList()
80{
81}
82
83/*!
84 Returns the number of items in the list.
85*/
86int QTextList::count() const
87{
88 Q_D(const QTextList);
89 return d->blocks.size();
90}
91
92/*!
93 Returns the \a{i}-th text block in the list.
94
95 \sa count(), itemText()
96*/
97QTextBlock QTextList::item(int i) const
98{
99 Q_D(const QTextList);
100 if (i < 0 || i >= d->blocks.size())
101 return QTextBlock();
102 return d->blocks.at(i);
103}
104
105/*!
106 \fn void QTextList::setFormat(const QTextListFormat &format)
107
108 Sets the list's format to \a format.
109*/
110
111/*!
112 \fn QTextListFormat QTextList::format() const
113
114 Returns the list's format.
115*/
116
117/*!
118 \fn int QTextList::itemNumber(const QTextBlock &block) const
119
120 Returns the index of the list item that corresponds to the given \a block.
121 Returns -1 if the block was not present in the list.
122*/
123int QTextList::itemNumber(const QTextBlock &blockIt) const
124{
125 Q_D(const QTextList);
126 return d->blocks.indexOf(t: blockIt);
127}
128
129/*!
130 \fn QString QTextList::itemText(const QTextBlock &block) const
131
132 Returns the text of the list item that corresponds to the given \a block.
133*/
134QString QTextList::itemText(const QTextBlock &blockIt) const
135{
136 Q_D(const QTextList);
137 int item = d->blocks.indexOf(t: blockIt) + 1;
138 if (item <= 0)
139 return QString();
140
141 QTextBlock block = d->blocks.at(i: item-1);
142 QTextBlockFormat blockFormat = block.blockFormat();
143
144 QString result;
145
146 const int style = format().style();
147 QString numberPrefix;
148 QString numberSuffix = u"."_s;
149
150 // the number of the item might be offset by start, which defaults to 1
151 const int itemNumber = item + format().start() - 1;
152
153 if (format().hasProperty(propertyId: QTextFormat::ListNumberPrefix))
154 numberPrefix = format().numberPrefix();
155 if (format().hasProperty(propertyId: QTextFormat::ListNumberSuffix))
156 numberSuffix = format().numberSuffix();
157
158 switch (style) {
159 case QTextListFormat::ListDecimal:
160 result = QString::number(itemNumber);
161 break;
162 // from the old richtext
163 case QTextListFormat::ListLowerAlpha:
164 case QTextListFormat::ListUpperAlpha:
165 {
166 // match the html default behavior of falling back to decimal numbers
167 if (itemNumber < 1) {
168 result = QString::number(itemNumber);
169 break;
170 }
171
172 const char baseChar = style == QTextListFormat::ListUpperAlpha ? 'A' : 'a';
173
174 int c = itemNumber;
175 while (c > 0) {
176 c--;
177 result.prepend(c: QChar::fromUcs2(c: baseChar + (c % 26)));
178 c /= 26;
179 }
180 }
181 break;
182 case QTextListFormat::ListLowerRoman:
183 case QTextListFormat::ListUpperRoman:
184 {
185 // match the html default behavior of falling back to decimal numbers
186 if (itemNumber < 1) {
187 result = QString::number(itemNumber);
188 } else if (itemNumber < 5000) {
189 QByteArray romanNumeral;
190
191 // works for up to 4999 items
192 static const char romanSymbolsLower[] = "iiivixxxlxcccdcmmmm";
193 static const char romanSymbolsUpper[] = "IIIVIXXXLXCCCDCMMMM";
194 QByteArray romanSymbols; // wrap to have "mid"
195 if (style == QTextListFormat::ListLowerRoman)
196 romanSymbols = QByteArray::fromRawData(data: romanSymbolsLower, size: sizeof(romanSymbolsLower));
197 else
198 romanSymbols = QByteArray::fromRawData(data: romanSymbolsUpper, size: sizeof(romanSymbolsUpper));
199
200 int c[] = { 1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000 };
201 int n = itemNumber;
202 for (int i = 12; i >= 0; n %= c[i], i--) {
203 int q = n / c[i];
204 if (q > 0) {
205 int startDigit = i + (i+3)/4;
206 int numDigits;
207 if (i % 4) {
208 // c[i] == 4|5|9|40|50|90|400|500|900
209 if ((i-2) % 4) {
210 // c[i] == 4|9|40|90|400|900 => with subtraction (IV, IX, XL, XC, ...)
211 numDigits = 2;
212 }
213 else {
214 // c[i] == 5|50|500 (V, L, D)
215 numDigits = 1;
216 }
217 }
218 else {
219 // c[i] == 1|10|100|1000 (I, II, III, X, XX, ...)
220 numDigits = q;
221 }
222
223 romanNumeral.append(a: romanSymbols.mid(index: startDigit, len: numDigits));
224 }
225 }
226 result = QString::fromLatin1(ba: romanNumeral);
227 } else {
228 result = u"?"_s;
229 }
230
231 }
232 break;
233 default:
234 Q_ASSERT(false);
235 }
236 if (blockIt.textDirection() == Qt::RightToLeft)
237 return numberSuffix + result + numberPrefix;
238 else
239 return numberPrefix + result + numberSuffix;
240}
241
242/*!
243 Removes the item at item position \a i from the list. When the last item in the
244 list is removed, the list is automatically deleted by the QTextDocument that owns
245 it.
246
247 \sa add(), remove()
248*/
249void QTextList::removeItem(int i)
250{
251 Q_D(QTextList);
252 if (i < 0 || i >= d->blocks.size())
253 return;
254
255 QTextBlock block = d->blocks.at(i);
256 remove(block);
257}
258
259
260/*!
261 Removes the given \a block from the list.
262
263 \sa add(), removeItem()
264*/
265void QTextList::remove(const QTextBlock &block)
266{
267 QTextBlockFormat fmt = block.blockFormat();
268 fmt.setIndent(fmt.indent() + format().indent());
269 fmt.setObjectIndex(-1);
270 const_cast<QTextDocumentPrivate *>(QTextDocumentPrivate::get(block))->setBlockFormat(from: block, to: block, newFormat: fmt, mode: QTextDocumentPrivate::SetFormat);
271}
272
273/*!
274 Makes the given \a block part of the list.
275
276 \sa remove(), removeItem()
277*/
278void QTextList::add(const QTextBlock &block)
279{
280 QTextBlockFormat fmt = block.blockFormat();
281 fmt.setObjectIndex(objectIndex());
282 const_cast<QTextDocumentPrivate *>(QTextDocumentPrivate::get(block))->setBlockFormat(from: block, to: block, newFormat: fmt, mode: QTextDocumentPrivate::SetFormat);
283}
284
285QT_END_NAMESPACE
286
287#include "moc_qtextlist.cpp"
288

source code of qtbase/src/gui/text/qtextlist.cpp