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