Coverage Report

Created: 2026-08-14 08:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/qtbase/src/gui/text/qtextlist.cpp
Line
Count
Source
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
11
QT_BEGIN_NAMESPACE
12
13
using namespace Qt::StringLiterals;
14
15
class QTextListPrivate : public QTextBlockGroupPrivate
16
{
17
public:
18
    QTextListPrivate(QTextDocument *doc)
19
0
        : QTextBlockGroupPrivate(doc)
20
0
    {
21
0
    }
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
 */
71
QTextList::QTextList(QTextDocument *doc)
72
0
    : QTextBlockGroup(*new QTextListPrivate(doc), doc)
73
0
{
74
0
}
75
76
/*!
77
  \internal
78
*/
79
QTextList::~QTextList()
80
0
{
81
0
}
82
83
/*!
84
    Returns the number of items in the list.
85
*/
86
int QTextList::count() const
87
0
{
88
0
    Q_D(const QTextList);
89
0
    return d->blocks.size();
90
0
}
91
92
/*!
93
    Returns the \a{i}-th text block in the list.
94
95
    \sa count(), itemText()
96
*/
97
QTextBlock QTextList::item(int i) const
98
0
{
99
0
    Q_D(const QTextList);
100
0
    if (i < 0 || i >= d->blocks.size())
101
0
        return QTextBlock();
102
0
    return d->blocks.at(i);
103
0
}
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
*/
123
int QTextList::itemNumber(const QTextBlock &blockIt) const
124
0
{
125
0
    Q_D(const QTextList);
126
0
    return d->blocks.indexOf(blockIt);
127
0
}
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
*/
134
QString QTextList::itemText(const QTextBlock &blockIt) const
135
0
{
136
0
    Q_D(const QTextList);
137
0
    int item = d->blocks.indexOf(blockIt) + 1;
138
0
    if (item <= 0)
139
0
        return QString();
140
141
0
    QTextBlock block = d->blocks.at(item-1);
142
0
    QTextBlockFormat blockFormat = block.blockFormat();
143
144
0
    QString result;
145
146
0
    const int style = format().style();
147
0
    QString numberPrefix;
148
0
    QString numberSuffix = u"."_s;
149
150
    // the number of the item might be offset by start, which defaults to 1
151
0
    const int itemNumber = item + format().start() - 1;
152
153
0
    if (format().hasProperty(QTextFormat::ListNumberPrefix))
154
0
        numberPrefix = format().numberPrefix();
155
0
    if (format().hasProperty(QTextFormat::ListNumberSuffix))
156
0
        numberSuffix = format().numberSuffix();
157
158
0
    switch (style) {
159
0
        case QTextListFormat::ListDecimal:
160
0
            result = QString::number(itemNumber);
161
0
            break;
162
            // from the old richtext
163
0
        case QTextListFormat::ListLowerAlpha:
164
0
        case QTextListFormat::ListUpperAlpha:
165
0
            {
166
                // match the html default behavior of falling back to decimal numbers
167
0
                if (itemNumber < 1) {
168
0
                    result = QString::number(itemNumber);
169
0
                    break;
170
0
                }
171
172
0
                const char baseChar = style == QTextListFormat::ListUpperAlpha ? 'A' : 'a';
173
174
0
                int c = itemNumber;
175
0
                while (c > 0) {
176
0
                    c--;
177
0
                    result.prepend(QChar::fromUcs2(baseChar + (c % 26)));
178
0
                    c /= 26;
179
0
                }
180
0
            }
181
0
            break;
182
0
        case QTextListFormat::ListLowerRoman:
183
0
        case QTextListFormat::ListUpperRoman:
184
0
            {
185
                // match the html default behavior of falling back to decimal numbers
186
0
                if (itemNumber < 1) {
187
0
                    result = QString::number(itemNumber);
188
0
                } else if (itemNumber < 5000) {
189
0
                    QString romanNumeral;
190
191
                    // works for up to 4999 items
192
0
                    QLatin1StringView romanSymbols;
193
0
                    if (style == QTextListFormat::ListLowerRoman)
194
0
                        romanSymbols = "iiivixxxlxcccdcmmmm"_L1;
195
0
                    else
196
0
                        romanSymbols = "IIIVIXXXLXCCCDCMMMM"_L1;
197
198
0
                    int c[] = { 1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000 };
199
0
                    int n = itemNumber;
200
0
                    for (int i = 12; i >= 0; n %= c[i], i--) {
201
0
                        int q = n / c[i];
202
0
                        if (q > 0) {
203
0
                            int startDigit = i + (i+3)/4;
204
0
                            int numDigits;
205
0
                            if (i % 4) {
206
                                // c[i] == 4|5|9|40|50|90|400|500|900
207
0
                                if ((i-2) % 4) {
208
                                    // c[i] == 4|9|40|90|400|900 => with subtraction (IV, IX, XL, XC, ...)
209
0
                                    numDigits = 2;
210
0
                                }
211
0
                                else {
212
                                    // c[i] == 5|50|500 (V, L, D)
213
0
                                    numDigits = 1;
214
0
                                }
215
0
                            }
216
0
                            else {
217
                                // c[i] == 1|10|100|1000 (I, II, III, X, XX, ...)
218
0
                                numDigits = q;
219
0
                            }
220
221
0
                            romanNumeral.append(romanSymbols.sliced(startDigit, numDigits));
222
0
                        }
223
0
                    }
224
0
                    result = std::move(romanNumeral);
225
0
                } else {
226
0
                    result = u"?"_s;
227
0
                }
228
229
0
            }
230
0
            break;
231
0
        default:
232
0
            Q_ASSERT(false);
233
0
    }
234
0
    if (blockIt.textDirection() == Qt::RightToLeft)
235
0
        return numberSuffix + result + numberPrefix;
236
0
    else
237
0
        return numberPrefix + result + numberSuffix;
238
0
}
239
240
/*!
241
    Removes the item at item position \a i from the list. When the last item in the
242
    list is removed, the list is automatically deleted by the QTextDocument that owns
243
    it.
244
245
    \sa add(), remove()
246
*/
247
void QTextList::removeItem(int i)
248
0
{
249
0
    Q_D(QTextList);
250
0
    if (i < 0 || i >= d->blocks.size())
251
0
        return;
252
253
0
    QTextBlock block = d->blocks.at(i);
254
0
    remove(block);
255
0
}
256
257
258
/*!
259
    Removes the given \a block from the list.
260
261
    \sa add(), removeItem()
262
*/
263
void QTextList::remove(const QTextBlock &block)
264
0
{
265
0
    QTextBlockFormat fmt = block.blockFormat();
266
0
    fmt.setIndent(fmt.indent() + format().indent());
267
0
    fmt.setObjectIndex(-1);
268
0
    const_cast<QTextDocumentPrivate *>(QTextDocumentPrivate::get(block))->setBlockFormat(block, block, fmt, QTextDocumentPrivate::SetFormat);
269
0
}
270
271
/*!
272
    Makes the given \a block part of the list.
273
274
    \sa remove(), removeItem()
275
*/
276
void QTextList::add(const QTextBlock &block)
277
0
{
278
0
    QTextBlockFormat fmt = block.blockFormat();
279
0
    fmt.setObjectIndex(objectIndex());
280
0
    const_cast<QTextDocumentPrivate *>(QTextDocumentPrivate::get(block))->setBlockFormat(block, block, fmt, QTextDocumentPrivate::SetFormat);
281
0
}
282
283
QT_END_NAMESPACE
284
285
#include "moc_qtextlist.cpp"