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