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