Coverage Report

Created: 2026-09-26 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/qt/qtbase/src/gui/text/qtextcursor.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
#include "qtextcursor.h"
5
#include "qtextcursor_p.h"
6
#include "qglobal.h"
7
#include "qtextdocumentfragment.h"
8
#include "qtextdocumentfragment_p.h"
9
#include "qtextlist.h"
10
#include "qtexttable.h"
11
#include "qtexttable_p.h"
12
#include "qtextengine_p.h"
13
#include "qabstracttextdocumentlayout.h"
14
#include "qfontdatabase_p.h"
15
16
#include <qtextlayout.h>
17
#include <qdebug.h>
18
19
QT_BEGIN_NAMESPACE
20
21
enum {
22
    AdjustPrev = 0x1,
23
    AdjustUp = 0x3,
24
    AdjustNext = 0x4,
25
    AdjustDown = 0x12
26
};
27
28
QTextCursorPrivate::QTextCursorPrivate(QTextDocumentPrivate *p)
29
2.39M
    : priv(p), x(0), position(0), anchor(0), adjusted_anchor(0),
30
2.39M
      currentCharFormat(-1), visualNavigation(false), keepPositionOnInsert(false),
31
2.39M
      changed(false)
32
2.39M
{
33
2.39M
    priv->addCursor(this);
34
2.39M
}
35
36
QTextCursorPrivate::QTextCursorPrivate(const QTextCursorPrivate &rhs)
37
0
    : QSharedData(rhs)
38
0
{
39
0
    position = rhs.position;
40
0
    anchor = rhs.anchor;
41
0
    adjusted_anchor = rhs.adjusted_anchor;
42
0
    priv = rhs.priv;
43
0
    x = rhs.x;
44
0
    currentCharFormat = rhs.currentCharFormat;
45
0
    visualNavigation = rhs.visualNavigation;
46
0
    keepPositionOnInsert = rhs.keepPositionOnInsert;
47
0
    changed = rhs.changed;
48
0
    if (priv != nullptr)
49
0
        priv->addCursor(this);
50
0
}
51
52
QTextCursorPrivate::~QTextCursorPrivate()
53
2.39M
{
54
2.39M
    if (priv)
55
2.39M
        priv->removeCursor(this);
56
2.39M
}
57
58
QTextCursorPrivate::AdjustResult QTextCursorPrivate::adjustPosition(int positionOfChange, int charsAddedOrRemoved, QTextUndoCommand::Operation op)
59
87.7M
{
60
87.7M
    QTextCursorPrivate::AdjustResult result = QTextCursorPrivate::CursorMoved;
61
    // not(!) <= , so that inserting text adjusts the cursor correctly
62
87.7M
    if (position < positionOfChange
63
77.1M
        || (position == positionOfChange
64
76.5M
            && (op == QTextUndoCommand::KeepCursor
65
76.5M
                || keepPositionOnInsert)
66
77.1M
            )
67
87.7M
         ) {
68
10.6M
        result = CursorUnchanged;
69
77.1M
    } else {
70
77.1M
        if (charsAddedOrRemoved < 0 && position < positionOfChange - charsAddedOrRemoved)
71
2.37M
            position = positionOfChange;
72
74.7M
        else
73
74.7M
            position += charsAddedOrRemoved;
74
75
77.1M
        currentCharFormat = -1;
76
77.1M
    }
77
78
87.7M
    if (anchor >= positionOfChange
79
77.1M
        && (anchor != positionOfChange || op != QTextUndoCommand::KeepCursor)) {
80
77.1M
        if (charsAddedOrRemoved < 0 && anchor < positionOfChange - charsAddedOrRemoved)
81
2.37M
            anchor = positionOfChange;
82
74.7M
        else
83
74.7M
            anchor += charsAddedOrRemoved;
84
77.1M
    }
85
86
87.7M
    if (adjusted_anchor >= positionOfChange
87
77.1M
        && (adjusted_anchor != positionOfChange || op != QTextUndoCommand::KeepCursor)) {
88
77.1M
        if (charsAddedOrRemoved < 0 && adjusted_anchor < positionOfChange - charsAddedOrRemoved)
89
2.37M
            adjusted_anchor = positionOfChange;
90
74.7M
        else
91
74.7M
            adjusted_anchor += charsAddedOrRemoved;
92
77.1M
    }
93
94
87.7M
    return result;
95
87.7M
}
96
97
void QTextCursorPrivate::setX()
98
37.1M
{
99
37.1M
    if (priv->isInEditBlock() || priv->inContentsChange) {
100
36.5M
        x = -1; // mark dirty
101
36.5M
        return;
102
36.5M
    }
103
104
535k
    QTextBlock block = this->block();
105
535k
    const QTextLayout *layout = blockLayout(block);
106
535k
    int pos = position - block.position();
107
108
535k
    QTextLine line = layout->lineForTextPosition(pos);
109
535k
    if (line.isValid())
110
0
        x = line.cursorToX(pos);
111
535k
    else
112
535k
        x = -1; // delayed init.  Makes movePosition() call setX later on again.
113
535k
}
114
115
void QTextCursorPrivate::remove()
116
3.46M
{
117
3.46M
    if (anchor == position)
118
3.46M
        return;
119
0
    currentCharFormat = -1;
120
0
    int pos1 = position;
121
0
    int pos2 = adjusted_anchor;
122
0
    QTextUndoCommand::Operation op = QTextUndoCommand::KeepCursor;
123
0
    if (pos1 > pos2) {
124
0
        pos1 = adjusted_anchor;
125
0
        pos2 = position;
126
0
        op = QTextUndoCommand::MoveCursor;
127
0
    }
128
129
    // deleting inside table? -> delete only content
130
0
    QTextTable *table = complexSelectionTable();
131
0
    if (table) {
132
0
        priv->beginEditBlock();
133
0
        int startRow, startCol, numRows, numCols;
134
0
        selectedTableCells(&startRow, &numRows, &startCol, &numCols);
135
0
        clearCells(table, startRow, startCol, numRows, numCols, op);
136
0
        adjusted_anchor = anchor = position;
137
0
        priv->endEditBlock();
138
0
    } else {
139
0
        priv->remove(pos1, pos2-pos1, op);
140
0
        adjusted_anchor = anchor = position;
141
0
    }
142
143
0
}
144
145
void QTextCursorPrivate::clearCells(QTextTable *table, int startRow, int startCol, int numRows, int numCols, QTextUndoCommand::Operation op)
146
0
{
147
0
    priv->beginEditBlock();
148
149
0
    for (int row = startRow; row < startRow + numRows; ++row)
150
0
        for (int col = startCol; col < startCol + numCols; ++col) {
151
0
            QTextTableCell cell = table->cellAt(row, col);
152
0
            const int startPos = cell.firstPosition();
153
0
            const int endPos = cell.lastPosition();
154
0
            Q_ASSERT(startPos <= endPos);
155
0
            priv->remove(startPos, endPos - startPos, op);
156
0
        }
157
158
0
    priv->endEditBlock();
159
0
}
160
161
bool QTextCursorPrivate::canDelete(int pos) const
162
0
{
163
0
    QTextDocumentPrivate::FragmentIterator fit = priv->find(pos);
164
0
    QTextCharFormat fmt = priv->formatCollection()->charFormat((*fit)->format);
165
0
    return (fmt.objectIndex() == -1 || fmt.objectType() == QTextFormat::ImageObject);
166
0
}
167
168
void QTextCursorPrivate::insertBlock(const QTextBlockFormat &format, const QTextCharFormat &charFormat)
169
2.60M
{
170
2.60M
    QTextFormatCollection *formats = priv->formatCollection();
171
2.60M
    int idx = formats->indexForFormat(format);
172
2.60M
    Q_ASSERT(formats->format(idx).isBlockFormat());
173
174
2.60M
    priv->insertBlock(position, idx, formats->indexForFormat(charFormat));
175
2.60M
    currentCharFormat = -1;
176
2.60M
}
177
178
void QTextCursorPrivate::adjustCursor(QTextCursor::MoveOperation m)
179
951k
{
180
951k
    adjusted_anchor = anchor;
181
951k
    if (position == anchor)
182
0
        return;
183
184
951k
    QTextFrame *f_position = priv->frameAt(position);
185
951k
    QTextFrame *f_anchor = priv->frameAt(adjusted_anchor);
186
187
951k
    if (f_position != f_anchor) {
188
        // find common parent frame
189
0
        QList<QTextFrame *> positionChain;
190
0
        QList<QTextFrame *> anchorChain;
191
0
        QTextFrame *f = f_position;
192
0
        while (f) {
193
0
            positionChain.prepend(f);
194
0
            f = f->parentFrame();
195
0
        }
196
0
        f = f_anchor;
197
0
        while (f) {
198
0
            anchorChain.prepend(f);
199
0
            f = f->parentFrame();
200
0
        }
201
0
        Q_ASSERT(positionChain.at(0) == anchorChain.at(0));
202
0
        int i = 1;
203
0
        int l = qMin(positionChain.size(), anchorChain.size());
204
0
        for (; i < l; ++i) {
205
0
            if (positionChain.at(i) != anchorChain.at(i))
206
0
                break;
207
0
        }
208
209
0
        if (m <= QTextCursor::WordLeft) {
210
0
            if (i < positionChain.size())
211
0
                position = positionChain.at(i)->firstPosition() - 1;
212
0
        } else {
213
0
            if (i < positionChain.size())
214
0
                position = positionChain.at(i)->lastPosition() + 1;
215
0
        }
216
0
        if (position < adjusted_anchor) {
217
0
            if (i < anchorChain.size())
218
0
                adjusted_anchor = anchorChain.at(i)->lastPosition() + 1;
219
0
        } else {
220
0
            if (i < anchorChain.size())
221
0
                adjusted_anchor = anchorChain.at(i)->firstPosition() - 1;
222
0
        }
223
224
0
        f_position = positionChain.at(i-1);
225
0
    }
226
227
    // same frame, either need to adjust to cell boundaries or return
228
951k
    QTextTable *table = qobject_cast<QTextTable *>(f_position);
229
951k
    if (!table)
230
951k
        return;
231
232
0
    QTextTableCell c_position = table->cellAt(position);
233
0
    QTextTableCell c_anchor = table->cellAt(adjusted_anchor);
234
0
    if (c_position != c_anchor) {
235
0
        position = c_position.firstPosition();
236
0
        if (position < adjusted_anchor)
237
0
            adjusted_anchor = c_anchor.lastPosition();
238
0
        else
239
0
            adjusted_anchor = c_anchor.firstPosition();
240
0
    }
241
0
    currentCharFormat = -1;
242
0
}
243
244
void QTextCursorPrivate::aboutToRemoveCell(int from, int to)
245
0
{
246
0
    Q_ASSERT(from <= to);
247
0
    if (position == anchor)
248
0
        return;
249
250
0
    QTextTable *t = qobject_cast<QTextTable *>(priv->frameAt(position));
251
0
    if (!t)
252
0
        return;
253
0
    QTextTableCell removedCellFrom = t->cellAt(from);
254
0
    QTextTableCell removedCellEnd = t->cellAt(to);
255
0
    if (! removedCellFrom.isValid() || !removedCellEnd.isValid())
256
0
        return;
257
258
0
    int curFrom = position;
259
0
    int curTo = adjusted_anchor;
260
0
    if (curTo < curFrom)
261
0
        qSwap(curFrom, curTo);
262
263
0
    QTextTableCell cellStart = t->cellAt(curFrom);
264
0
    QTextTableCell cellEnd = t->cellAt(curTo);
265
266
0
    if (cellStart.row() >= removedCellFrom.row() && cellEnd.row() <= removedCellEnd.row()
267
0
            && cellStart.column() >= removedCellFrom.column()
268
0
              && cellEnd.column() <= removedCellEnd.column()) { // selection is completely removed
269
        // find a new position, as close as possible to where we were.
270
0
        QTextTableCell cell;
271
0
        if (removedCellFrom.row() == 0 && removedCellEnd.row() == t->rows()-1) // removed n columns
272
0
            cell = t->cellAt(cellStart.row(), removedCellEnd.column()+1);
273
0
        else if (removedCellFrom.column() == 0 && removedCellEnd.column() == t->columns()-1) // removed n rows
274
0
            cell = t->cellAt(removedCellEnd.row() + 1, cellStart.column());
275
276
0
        int newPosition;
277
0
        if (cell.isValid())
278
0
            newPosition = cell.firstPosition();
279
0
        else
280
0
            newPosition = t->lastPosition()+1;
281
282
0
        setPosition(newPosition);
283
0
        anchor = newPosition;
284
0
        adjusted_anchor = newPosition;
285
0
        x = 0;
286
0
    }
287
0
    else if (cellStart.row() >= removedCellFrom.row() && cellStart.row() <= removedCellEnd.row()
288
0
        && cellEnd.row() > removedCellEnd.row()) {
289
0
        int newPosition = t->cellAt(removedCellEnd.row() + 1, cellStart.column()).firstPosition();
290
0
        if (position < anchor)
291
0
            position = newPosition;
292
0
        else
293
0
            anchor = adjusted_anchor = newPosition;
294
0
    }
295
0
    else if (cellStart.column() >= removedCellFrom.column() && cellStart.column() <= removedCellEnd.column()
296
0
        && cellEnd.column() > removedCellEnd.column()) {
297
0
        int newPosition = t->cellAt(cellStart.row(), removedCellEnd.column()+1).firstPosition();
298
0
        if (position < anchor)
299
0
            position = newPosition;
300
0
        else
301
0
            anchor = adjusted_anchor = newPosition;
302
0
    }
303
0
}
304
305
bool QTextCursorPrivate::movePosition(QTextCursor::MoveOperation op, QTextCursor::MoveMode mode)
306
1.40M
{
307
1.40M
    currentCharFormat = -1;
308
1.40M
    bool adjustX = true;
309
1.40M
    QTextBlock blockIt = block();
310
1.40M
    bool visualMovement = priv->defaultCursorMoveStyle == Qt::VisualMoveStyle;
311
312
1.40M
    if (!blockIt.isValid())
313
0
        return false;
314
315
1.40M
    const QTextLayout *layout = blockLayout(blockIt);
316
317
1.40M
    Qt::LayoutDirection blockDirection = layout->textOption().textDirection();
318
1.40M
    if (blockDirection == Qt::LayoutDirectionAuto)
319
1.40M
        blockDirection = blockIt.textDirection();
320
321
1.40M
    if (blockDirection == Qt::RightToLeft) {
322
11.0k
        if (op == QTextCursor::WordLeft)
323
0
            op = QTextCursor::NextWord;
324
11.0k
        else if (op == QTextCursor::WordRight)
325
0
            op = QTextCursor::PreviousWord;
326
327
11.0k
        if (!visualMovement) {
328
11.0k
            if (op == QTextCursor::Left)
329
0
                op = QTextCursor::NextCharacter;
330
11.0k
            else if (op == QTextCursor::Right)
331
0
                op = QTextCursor::PreviousCharacter;
332
11.0k
        }
333
11.0k
    }
334
335
1.40M
    int relativePos = position - blockIt.position();
336
1.40M
    QTextLine line;
337
1.40M
    if (!priv->isInEditBlock())
338
535k
        line = layout->lineForTextPosition(relativePos);
339
340
1.40M
    Q_ASSERT(priv->frameAt(position) == priv->frameAt(adjusted_anchor));
341
342
1.40M
    int newPosition = position;
343
344
1.40M
    if (mode == QTextCursor::KeepAnchor && complexSelectionTable() != nullptr) {
345
0
        if ((op >= QTextCursor::EndOfLine && op <= QTextCursor::NextWord)
346
0
                || (op >= QTextCursor::Right && op <= QTextCursor::WordRight)) {
347
0
            QTextTable *t = qobject_cast<QTextTable *>(priv->frameAt(position));
348
0
            Q_ASSERT(t); // as we have already made sure we have a complex selection
349
0
            QTextTableCell cell_pos = t->cellAt(position);
350
0
            if (cell_pos.column() + cell_pos.columnSpan() != t->columns())
351
0
                op = QTextCursor::NextCell;
352
0
        }
353
0
    }
354
355
1.40M
    if (x == -1 && !priv->isInEditBlock() && (op == QTextCursor::Up || op == QTextCursor::Down))
356
0
        setX();
357
358
1.40M
    switch(op) {
359
0
    case QTextCursor::NoMove:
360
0
        return true;
361
362
0
    case QTextCursor::Start:
363
0
        newPosition = 0;
364
0
        break;
365
0
    case QTextCursor::StartOfLine: {
366
0
        newPosition = blockIt.position();
367
0
        if (line.isValid())
368
0
            newPosition += line.textStart();
369
370
0
        break;
371
0
    }
372
0
    case QTextCursor::StartOfBlock: {
373
0
        newPosition = blockIt.position();
374
0
        break;
375
0
    }
376
0
    case QTextCursor::PreviousBlock: {
377
0
        if (blockIt == priv->blocksBegin())
378
0
            return false;
379
0
        blockIt = blockIt.previous();
380
381
0
        newPosition = blockIt.position();
382
0
        break;
383
0
    }
384
416k
    case QTextCursor::PreviousCharacter:
385
416k
        if (mode == QTextCursor::MoveAnchor && position != adjusted_anchor)
386
0
            newPosition = qMin(position, adjusted_anchor);
387
416k
        else
388
416k
            newPosition = priv->previousCursorPosition(position, QTextLayout::SkipCharacters);
389
416k
        break;
390
0
    case QTextCursor::Left:
391
0
        if (mode == QTextCursor::MoveAnchor && position != adjusted_anchor)
392
0
            newPosition = visualMovement ? qMax(position, adjusted_anchor)
393
0
                                         : qMin(position, adjusted_anchor);
394
0
        else
395
0
            newPosition = visualMovement ? priv->leftCursorPosition(position)
396
0
                                         : priv->previousCursorPosition(position, QTextLayout::SkipCharacters);
397
0
        break;
398
0
    case QTextCursor::StartOfWord: {
399
0
        if (relativePos == 0)
400
0
            break;
401
402
        // skip if already at word start
403
0
        QTextEngine *engine = layout->engine();
404
0
        const QCharAttributes *attributes = engine->attributes();
405
0
        if ((relativePos == blockIt.length() - 1)
406
0
            && (attributes[relativePos - 1].whiteSpace || engine->atWordSeparator(relativePos - 1)))
407
0
            return false;
408
409
0
        if (relativePos < blockIt.length()-1)
410
0
            ++position;
411
412
0
        Q_FALLTHROUGH();
413
0
    }
414
0
    case QTextCursor::PreviousWord:
415
0
    case QTextCursor::WordLeft:
416
0
        newPosition = priv->previousCursorPosition(position, QTextLayout::SkipWords);
417
0
        break;
418
0
    case QTextCursor::Up: {
419
0
        int i = line.lineNumber() - 1;
420
0
        if (i == -1) {
421
0
            if (blockIt == priv->blocksBegin())
422
0
                return false;
423
0
            int blockPosition = blockIt.position();
424
0
            QTextTable *table = qobject_cast<QTextTable *>(priv->frameAt(blockPosition));
425
0
            if (table) {
426
0
                QTextTableCell cell = table->cellAt(blockPosition);
427
0
                if (cell.firstPosition() == blockPosition) {
428
0
                    int row = cell.row() - 1;
429
0
                    if (row >= 0) {
430
0
                        blockPosition = table->cellAt(row, cell.column()).lastPosition();
431
0
                    } else {
432
                        // move to line above the table
433
0
                        blockPosition = table->firstPosition() - 1;
434
0
                    }
435
0
                    blockIt = priv->blocksFind(blockPosition);
436
0
                } else {
437
0
                    blockIt = blockIt.previous();
438
0
                }
439
0
            } else {
440
0
                blockIt = blockIt.previous();
441
0
            }
442
0
            layout = blockLayout(blockIt);
443
0
            i = layout->lineCount()-1;
444
0
        }
445
0
        if (layout->lineCount()) {
446
0
            QTextLine line = layout->lineAt(i);
447
0
            newPosition = line.xToCursor(x) + blockIt.position();
448
0
        } else {
449
0
            newPosition = blockIt.position();
450
0
        }
451
0
        adjustX = false;
452
0
        break;
453
0
    }
454
455
573k
    case QTextCursor::End:
456
573k
        newPosition = priv->length() - 1;
457
573k
        break;
458
0
    case QTextCursor::EndOfLine: {
459
0
        if (!line.isValid() || line.textLength() == 0) {
460
0
            if (blockIt.length() >= 1)
461
                // position right before the block separator
462
0
                newPosition = blockIt.position() + blockIt.length() - 1;
463
0
            break;
464
0
        }
465
0
        newPosition = blockIt.position() + line.textStart() + line.textLength();
466
0
        if (newPosition >= priv->length())
467
0
            newPosition = priv->length() - 1;
468
0
        if (line.lineNumber() < layout->lineCount() - 1) {
469
0
            const QString text = blockIt.text();
470
            // ###### this relies on spaces being the cause for linebreaks.
471
            // this doesn't work with japanese
472
0
            if (text.at(line.textStart() + line.textLength() - 1).isSpace())
473
0
                --newPosition;
474
0
        }
475
0
        break;
476
0
    }
477
0
    case QTextCursor::EndOfWord: {
478
0
        QTextEngine *engine = layout->engine();
479
0
        const QCharAttributes *attributes = engine->attributes();
480
0
        const int len = blockIt.length() - 1;
481
0
        if (relativePos >= len)
482
0
            return false;
483
0
        if (engine->atWordSeparator(relativePos)) {
484
0
            ++relativePos;
485
0
            while (relativePos < len && engine->atWordSeparator(relativePos))
486
0
                ++relativePos;
487
0
        } else {
488
0
            while (relativePos < len && !attributes[relativePos].whiteSpace && !engine->atWordSeparator(relativePos))
489
0
                ++relativePos;
490
0
        }
491
0
        newPosition = blockIt.position() + relativePos;
492
0
        break;
493
0
    }
494
0
    case QTextCursor::EndOfBlock:
495
0
        if (blockIt.length() >= 1)
496
            // position right before the block separator
497
0
            newPosition = blockIt.position() + blockIt.length() - 1;
498
0
        break;
499
0
    case QTextCursor::NextBlock: {
500
0
        blockIt = blockIt.next();
501
0
        if (!blockIt.isValid())
502
0
            return false;
503
504
0
        newPosition = blockIt.position();
505
0
        break;
506
0
    }
507
416k
    case QTextCursor::NextCharacter:
508
416k
        if (mode == QTextCursor::MoveAnchor && position != adjusted_anchor)
509
416k
            newPosition = qMax(position, adjusted_anchor);
510
0
        else
511
0
            newPosition = priv->nextCursorPosition(position, QTextLayout::SkipCharacters);
512
416k
        break;
513
0
    case QTextCursor::Right:
514
0
        if (mode == QTextCursor::MoveAnchor && position != adjusted_anchor)
515
0
            newPosition = visualMovement ? qMin(position, adjusted_anchor)
516
0
                                         : qMax(position, adjusted_anchor);
517
0
        else
518
0
            newPosition = visualMovement ? priv->rightCursorPosition(position)
519
0
                                         : priv->nextCursorPosition(position, QTextLayout::SkipCharacters);
520
0
        break;
521
0
    case QTextCursor::NextWord:
522
0
    case QTextCursor::WordRight:
523
0
        newPosition = priv->nextCursorPosition(position, QTextLayout::SkipWords);
524
0
        break;
525
526
0
    case QTextCursor::Down: {
527
0
        int i = line.lineNumber() + 1;
528
529
0
        if (i >= layout->lineCount()) {
530
0
            int blockPosition = blockIt.position() + blockIt.length() - 1;
531
0
            QTextTable *table = qobject_cast<QTextTable *>(priv->frameAt(blockPosition));
532
0
            if (table) {
533
0
                QTextTableCell cell = table->cellAt(blockPosition);
534
0
                if (cell.lastPosition() == blockPosition) {
535
0
                    int row = cell.row() + cell.rowSpan();
536
0
                    if (row < table->rows()) {
537
0
                        blockPosition = table->cellAt(row, cell.column()).firstPosition();
538
0
                    } else {
539
                        // move to line below the table
540
0
                        blockPosition = table->lastPosition() + 1;
541
0
                    }
542
0
                    blockIt = priv->blocksFind(blockPosition);
543
0
                } else {
544
0
                    blockIt = blockIt.next();
545
0
                }
546
0
            } else {
547
0
                blockIt = blockIt.next();
548
0
            }
549
550
0
            if (blockIt == priv->blocksEnd())
551
0
                return false;
552
0
            layout = blockLayout(blockIt);
553
0
            i = 0;
554
0
        }
555
0
        if (layout->lineCount()) {
556
0
            QTextLine line = layout->lineAt(i);
557
0
            newPosition = line.xToCursor(x) + blockIt.position();
558
0
        } else {
559
0
            newPosition = blockIt.position();
560
0
        }
561
0
        adjustX = false;
562
0
        break;
563
0
    }
564
0
    case QTextCursor::NextCell:
565
0
    case QTextCursor::PreviousCell:
566
0
    case QTextCursor::NextRow:
567
0
    case QTextCursor::PreviousRow: {
568
0
        QTextTable *table = qobject_cast<QTextTable *>(priv->frameAt(position));
569
0
        if (!table)
570
0
            return false;
571
572
0
        QTextTableCell cell = table->cellAt(position);
573
0
        Q_ASSERT(cell.isValid());
574
0
        int column = cell.column();
575
0
        int row = cell.row();
576
0
        const int currentRow = row;
577
0
        if (op == QTextCursor::NextCell || op == QTextCursor::NextRow) {
578
0
            do {
579
0
                column += cell.columnSpan();
580
0
                if (column >= table->columns()) {
581
0
                    column = 0;
582
0
                    ++row;
583
0
                }
584
0
                cell = table->cellAt(row, column);
585
                // note we also continue while we have not reached a cell that's not merged with one above us
586
0
            } while (cell.isValid()
587
0
                    && ((op == QTextCursor::NextRow && currentRow == cell.row())
588
0
                        || cell.row() < row));
589
0
        }
590
0
        else if (op == QTextCursor::PreviousCell || op == QTextCursor::PreviousRow) {
591
0
            do {
592
0
                --column;
593
0
                if (column < 0) {
594
0
                    column = table->columns()-1;
595
0
                    --row;
596
0
                }
597
0
                cell = table->cellAt(row, column);
598
                // note we also continue while we have not reached a cell that's not merged with one above us
599
0
            } while (cell.isValid()
600
0
                    && ((op == QTextCursor::PreviousRow && currentRow == cell.row())
601
0
                        || cell.row() < row));
602
0
        }
603
0
        if (cell.isValid())
604
0
            newPosition = cell.firstPosition();
605
0
        break;
606
0
    }
607
1.40M
    }
608
609
1.40M
    if (mode == QTextCursor::KeepAnchor) {
610
951k
        QTextTable *table = qobject_cast<QTextTable *>(priv->frameAt(position));
611
951k
        if (table && ((op >= QTextCursor::PreviousBlock && op <= QTextCursor::WordLeft)
612
0
                      || (op >= QTextCursor::NextBlock && op <= QTextCursor::WordRight))) {
613
0
            int oldColumn = table->cellAt(position).column();
614
615
0
            const QTextTableCell otherCell = table->cellAt(newPosition);
616
0
            if (!otherCell.isValid())
617
0
                return false;
618
619
0
            int newColumn = otherCell.column();
620
0
            if ((oldColumn > newColumn && op >= QTextCursor::End)
621
0
                || (oldColumn < newColumn && op <= QTextCursor::WordLeft))
622
0
                return false;
623
0
        }
624
951k
    }
625
626
1.40M
    const bool moved = setPosition(newPosition);
627
628
1.40M
    if (mode == QTextCursor::MoveAnchor) {
629
453k
        anchor = position;
630
453k
        adjusted_anchor = position;
631
951k
    } else {
632
951k
        adjustCursor(op);
633
951k
    }
634
635
1.40M
    if (adjustX)
636
1.40M
        setX();
637
638
1.40M
    return moved;
639
1.40M
}
640
641
QTextTable *QTextCursorPrivate::complexSelectionTable() const
642
10.3M
{
643
10.3M
    if (position == anchor)
644
9.41M
        return nullptr;
645
646
951k
    QTextTable *t = qobject_cast<QTextTable *>(priv->frameAt(position));
647
951k
    if (t) {
648
0
        QTextTableCell cell_pos = t->cellAt(position);
649
0
        QTextTableCell cell_anchor = t->cellAt(adjusted_anchor);
650
651
0
        Q_ASSERT(cell_anchor.isValid());
652
653
0
        if (cell_pos == cell_anchor)
654
0
            t = nullptr;
655
0
    }
656
951k
    return t;
657
10.3M
}
658
659
void QTextCursorPrivate::selectedTableCells(int *firstRow, int *numRows, int *firstColumn, int *numColumns) const
660
0
{
661
0
    *firstRow = -1;
662
0
    *firstColumn = -1;
663
0
    *numRows = -1;
664
0
    *numColumns = -1;
665
666
0
    if (position == anchor)
667
0
        return;
668
669
0
    QTextTable *t = qobject_cast<QTextTable *>(priv->frameAt(position));
670
0
    if (!t)
671
0
        return;
672
673
0
    QTextTableCell cell_pos = t->cellAt(position);
674
0
    QTextTableCell cell_anchor = t->cellAt(adjusted_anchor);
675
676
0
    Q_ASSERT(cell_anchor.isValid());
677
678
0
    if (cell_pos == cell_anchor)
679
0
        return;
680
681
0
    *firstRow = qMin(cell_pos.row(), cell_anchor.row());
682
0
    *firstColumn = qMin(cell_pos.column(), cell_anchor.column());
683
0
    *numRows = qMax(cell_pos.row() + cell_pos.rowSpan(), cell_anchor.row() + cell_anchor.rowSpan()) - *firstRow;
684
0
    *numColumns = qMax(cell_pos.column() + cell_pos.columnSpan(), cell_anchor.column() + cell_anchor.columnSpan()) - *firstColumn;
685
0
}
686
687
static void setBlockCharFormatHelper(QTextDocumentPrivate *priv, int pos1, int pos2,
688
                               const QTextCharFormat &format, QTextDocumentPrivate::FormatChangeMode changeMode)
689
386k
{
690
386k
    QTextBlock it = priv->blocksFind(pos1);
691
386k
    QTextBlock end = priv->blocksFind(pos2);
692
386k
    if (end.isValid())
693
386k
        end = end.next();
694
695
772k
    for (; it != end; it = it.next()) {
696
386k
        priv->setCharFormat(it.position() - 1, 1, format, changeMode);
697
386k
    }
698
386k
}
699
700
void QTextCursorPrivate::setBlockCharFormat(const QTextCharFormat &_format,
701
    QTextDocumentPrivate::FormatChangeMode changeMode)
702
386k
{
703
386k
    priv->beginEditBlock();
704
705
386k
    QTextCharFormat format = _format;
706
386k
    format.clearProperty(QTextFormat::ObjectIndex);
707
708
386k
    QTextTable *table = complexSelectionTable();
709
386k
    if (table) {
710
0
        int row_start, col_start, num_rows, num_cols;
711
0
        selectedTableCells(&row_start, &num_rows, &col_start, &num_cols);
712
713
0
        Q_ASSERT(row_start != -1);
714
0
        for (int r = row_start; r < row_start + num_rows; ++r) {
715
0
            for (int c = col_start; c < col_start + num_cols; ++c) {
716
0
                QTextTableCell cell = table->cellAt(r, c);
717
0
                int rspan = cell.rowSpan();
718
0
                int cspan = cell.columnSpan();
719
0
                if (rspan != 1) {
720
0
                    int cr = cell.row();
721
0
                    if (cr != r)
722
0
                        continue;
723
0
                }
724
0
                if (cspan != 1) {
725
0
                    int cc = cell.column();
726
0
                    if (cc != c)
727
0
                        continue;
728
0
                }
729
730
0
                int pos1 = cell.firstPosition();
731
0
                int pos2 = cell.lastPosition();
732
0
                setBlockCharFormatHelper(priv, pos1, pos2, format, changeMode);
733
0
            }
734
0
        }
735
386k
    } else {
736
386k
        int pos1 = position;
737
386k
        int pos2 = adjusted_anchor;
738
386k
        if (pos1 > pos2) {
739
0
            pos1 = adjusted_anchor;
740
0
            pos2 = position;
741
0
        }
742
743
386k
        setBlockCharFormatHelper(priv, pos1, pos2, format, changeMode);
744
386k
    }
745
386k
    priv->endEditBlock();
746
386k
}
747
748
749
void QTextCursorPrivate::setBlockFormat(const QTextBlockFormat &format, QTextDocumentPrivate::FormatChangeMode changeMode)
750
8.08M
{
751
8.08M
    QTextTable *table = complexSelectionTable();
752
8.08M
    if (table) {
753
0
        priv->beginEditBlock();
754
0
        int row_start, col_start, num_rows, num_cols;
755
0
        selectedTableCells(&row_start, &num_rows, &col_start, &num_cols);
756
757
0
        Q_ASSERT(row_start != -1);
758
0
        for (int r = row_start; r < row_start + num_rows; ++r) {
759
0
            for (int c = col_start; c < col_start + num_cols; ++c) {
760
0
                QTextTableCell cell = table->cellAt(r, c);
761
0
                int rspan = cell.rowSpan();
762
0
                int cspan = cell.columnSpan();
763
0
                if (rspan != 1) {
764
0
                    int cr = cell.row();
765
0
                    if (cr != r)
766
0
                        continue;
767
0
                }
768
0
                if (cspan != 1) {
769
0
                    int cc = cell.column();
770
0
                    if (cc != c)
771
0
                        continue;
772
0
                }
773
774
0
                int pos1 = cell.firstPosition();
775
0
                int pos2 = cell.lastPosition();
776
0
                priv->setBlockFormat(priv->blocksFind(pos1), priv->blocksFind(pos2), format, changeMode);
777
0
            }
778
0
        }
779
0
        priv->endEditBlock();
780
8.08M
    } else {
781
8.08M
        int pos1 = position;
782
8.08M
        int pos2 = adjusted_anchor;
783
8.08M
        if (pos1 > pos2) {
784
0
            pos1 = adjusted_anchor;
785
0
            pos2 = position;
786
0
        }
787
788
8.08M
        priv->setBlockFormat(priv->blocksFind(pos1), priv->blocksFind(pos2), format, changeMode);
789
8.08M
    }
790
8.08M
}
791
792
void QTextCursorPrivate::setCharFormat(const QTextCharFormat &_format, QTextDocumentPrivate::FormatChangeMode changeMode)
793
416k
{
794
416k
    Q_ASSERT(position != anchor);
795
796
416k
    QTextCharFormat format = _format;
797
416k
    format.clearProperty(QTextFormat::ObjectIndex);
798
799
416k
    QTextTable *table = complexSelectionTable();
800
416k
    if (table) {
801
0
        priv->beginEditBlock();
802
0
        int row_start, col_start, num_rows, num_cols;
803
0
        selectedTableCells(&row_start, &num_rows, &col_start, &num_cols);
804
805
0
        Q_ASSERT(row_start != -1);
806
0
        for (int r = row_start; r < row_start + num_rows; ++r) {
807
0
            for (int c = col_start; c < col_start + num_cols; ++c) {
808
0
                QTextTableCell cell = table->cellAt(r, c);
809
0
                int rspan = cell.rowSpan();
810
0
                int cspan = cell.columnSpan();
811
0
                if (rspan != 1) {
812
0
                    int cr = cell.row();
813
0
                    if (cr != r)
814
0
                        continue;
815
0
                }
816
0
                if (cspan != 1) {
817
0
                    int cc = cell.column();
818
0
                    if (cc != c)
819
0
                        continue;
820
0
                }
821
822
0
                int pos1 = cell.firstPosition();
823
0
                int pos2 = cell.lastPosition();
824
0
                priv->setCharFormat(pos1, pos2-pos1, format, changeMode);
825
0
            }
826
0
        }
827
0
        priv->endEditBlock();
828
416k
    } else {
829
416k
        int pos1 = position;
830
416k
        int pos2 = adjusted_anchor;
831
416k
        if (pos1 > pos2) {
832
0
            pos1 = adjusted_anchor;
833
0
            pos2 = position;
834
0
        }
835
836
416k
        priv->setCharFormat(pos1, pos2-pos1, format, changeMode);
837
416k
    }
838
416k
}
839
840
841
1.94M
QTextLayout *QTextCursorPrivate::blockLayout(QTextBlock &block) const{
842
1.94M
    QTextLayout *tl = block.layout();
843
1.94M
    if (!tl->lineCount() && priv->layout())
844
0
        priv->layout()->blockBoundingRect(block);
845
1.94M
    return tl;
846
1.94M
}
847
848
/*!
849
    \class QTextCursor
850
    \reentrant
851
    \inmodule QtGui
852
853
    \brief The QTextCursor class offers an API to access and modify QTextDocuments.
854
855
    \ingroup richtext-processing
856
    \ingroup shared
857
858
    Text cursors are objects that are used to access and modify the
859
    contents and underlying structure of text documents via a
860
    programming interface that mimics the behavior of a cursor in a
861
    text editor. QTextCursor contains information about both the
862
    cursor's position within a QTextDocument and any selection that it
863
    has made.
864
865
    QTextCursor is modeled on the way a text cursor behaves in a text
866
    editor, providing a programmatic means of performing standard
867
    actions through the user interface. A document can be thought of
868
    as a single string of characters. The cursor's current position()
869
    then is always either \e between two consecutive characters in the
870
    string, or else \e before the very first character or \e after the
871
    very last character in the string.  Documents can also contain
872
    tables, lists, images, and other objects in addition to text but,
873
    from the developer's point of view, the document can be treated as
874
    one long string.  Some portions of that string can be considered
875
    to lie within particular blocks (e.g. paragraphs), or within a
876
    table's cell, or a list's item, or other structural elements. When
877
    we refer to "current character" we mean the character immediately
878
    \e before the cursor position() in the document. Similarly, the
879
    "current block" is the block that contains the cursor position().
880
881
    A QTextCursor also has an anchor() position. The text that is
882
    between the anchor() and the position() is the selection. If
883
    anchor() == position() there is no selection.
884
885
    The cursor position can be changed programmatically using
886
    setPosition() and movePosition(); the latter can also be used to
887
    select text. For selections see selectionStart(), selectionEnd(),
888
    hasSelection(), clearSelection(), and removeSelectedText().
889
890
    If the position() is at the start of a block, atBlockStart()
891
    returns \c true; and if it is at the end of a block, atBlockEnd() returns
892
    true. The format of the current character is returned by
893
    charFormat(), and the format of the current block is returned by
894
    blockFormat().
895
896
    Formatting can be applied to the current text document using the
897
    setCharFormat(), mergeCharFormat(), setBlockFormat() and
898
    mergeBlockFormat() functions. The 'set' functions will replace the
899
    cursor's current character or block format, while the 'merge'
900
    functions add the given format properties to the cursor's current
901
    format. If the cursor has a selection, the given format is applied
902
    to the current selection. Note that when only a part of a block is
903
    selected, the block format is applied to the entire block. The text
904
    at the current character position can be turned into a list using
905
    createList().
906
907
    Deletions can be achieved using deleteChar(),
908
    deletePreviousChar(), and removeSelectedText().
909
910
    Text strings can be inserted into the document with the insertText()
911
    function, blocks (representing new paragraphs) can be inserted with
912
    insertBlock().
913
914
    Existing fragments of text can be inserted with insertFragment() but,
915
    if you want to insert pieces of text in various formats, it is usually
916
    still easier to use insertText() and supply a character format.
917
918
    Various types of higher-level structure can also be inserted into the
919
    document with the cursor:
920
921
    \list
922
    \li Lists are ordered sequences of block elements that are decorated with
923
       bullet points or symbols. These are inserted in a specified format
924
       with insertList().
925
    \li Tables are inserted with the insertTable() function, and can be
926
       given an optional format. These contain an array of cells that can
927
       be traversed using the cursor.
928
    \li Inline images are inserted with insertImage(). The image to be
929
       used can be specified in an image format, or by name.
930
    \li Frames are inserted by calling insertFrame() with a specified format.
931
    \endlist
932
933
    Actions can be grouped (i.e. treated as a single action for
934
    undo/redo) using beginEditBlock() and endEditBlock().
935
936
    Cursor movements are limited to valid cursor positions. In Latin
937
    writing this is between any two consecutive characters in the
938
    text, before the first character, or after the last character. In
939
    some other writing systems cursor movements are limited to
940
    "clusters" (e.g. a syllable in Devanagari, or a base letter plus
941
    diacritics).  Functions such as movePosition() and deleteChar()
942
    limit cursor movement to these valid positions.
943
944
    \sa {Rich Text Processing}
945
946
*/
947
948
/*!
949
    \enum QTextCursor::MoveOperation
950
951
    \value NoMove Keep the cursor where it is
952
953
    \value Start Move to the start of the document.
954
    \value StartOfLine Move to the start of the current line.
955
    \value StartOfBlock Move to the start of the current block.
956
    \value StartOfWord Move to the start of the current word.
957
    \value PreviousBlock Move to the start of the previous block.
958
    \value PreviousCharacter Move to the previous character.
959
    \value PreviousWord Move to the beginning of the previous word.
960
    \value Up Move up one line.
961
    \value Left Move left one character.
962
    \value WordLeft Move left one word.
963
964
    \value End Move to the end of the document.
965
    \value EndOfLine Move to the end of the current line.
966
    \value EndOfWord Move to the end of the current word.
967
    \value EndOfBlock Move to the end of the current block.
968
    \value NextBlock Move to the beginning of the next block.
969
    \value NextCharacter Move to the next character. If text is selected, the first call
970
    to \c{movePosition(NextCharacter, MoveAnchor)} deselects the text without moving the cursor.
971
    \value NextWord Move to the next word.
972
    \value Down Move down one line.
973
    \value Right Move right one character.
974
    \value WordRight Move right one word.
975
976
    \value NextCell  Move to the beginning of the next table cell inside the
977
           current table. If the current cell is the last cell in the row, the
978
           cursor will move to the first cell in the next row.
979
    \value PreviousCell  Move to the beginning of the previous table cell
980
           inside the current table. If the current cell is the first cell in
981
           the row, the cursor will move to the last cell in the previous row.
982
    \value NextRow  Move to the first new cell of the next row in the current
983
           table.
984
    \value PreviousRow  Move to the last cell of the previous row in the
985
           current table.
986
987
    \sa movePosition()
988
*/
989
990
/*!
991
    \enum QTextCursor::MoveMode
992
993
    \value MoveAnchor Moves the anchor to the same position as the cursor itself.
994
    \value KeepAnchor Keeps the anchor where it is.
995
996
    If the anchor() is kept where it is and the position() is moved,
997
    the text in between will be selected.
998
*/
999
1000
/*!
1001
    \enum QTextCursor::SelectionType
1002
1003
    This enum describes the types of selection that can be applied with the
1004
    select() function.
1005
1006
    \value Document         Selects the entire document.
1007
    \value BlockUnderCursor Selects the block of text under the cursor.
1008
    \value LineUnderCursor  Selects the line of text under the cursor.
1009
    \value WordUnderCursor  Selects the word under the cursor. If the cursor
1010
           is not positioned within a string of selectable characters, no
1011
           text is selected.
1012
*/
1013
1014
/*!
1015
    Constructs a null cursor.
1016
 */
1017
QTextCursor::QTextCursor()
1018
2.31M
    : d(nullptr)
1019
2.31M
{
1020
2.31M
}
1021
1022
/*!
1023
    Constructs a cursor pointing to the beginning of the \a document.
1024
 */
1025
QTextCursor::QTextCursor(QTextDocument *document)
1026
1.45M
    : d(new QTextCursorPrivate(QTextDocumentPrivate::get(document)))
1027
1.45M
{
1028
1.45M
}
1029
1030
/*!
1031
    Constructs a cursor pointing to the beginning of the \a frame.
1032
*/
1033
QTextCursor::QTextCursor(QTextFrame *frame)
1034
0
    : d(new QTextCursorPrivate(QTextDocumentPrivate::get(frame->document())))
1035
0
{
1036
0
    d->adjusted_anchor = d->anchor = d->position = frame->firstPosition();
1037
0
}
1038
1039
1040
/*!
1041
    Constructs a cursor pointing to the beginning of the \a block.
1042
*/
1043
QTextCursor::QTextCursor(const QTextBlock &block)
1044
0
    : d(new QTextCursorPrivate(const_cast<QTextDocumentPrivate *>(QTextDocumentPrivate::get(block))))
1045
0
{
1046
0
    d->adjusted_anchor = d->anchor = d->position = block.position();
1047
0
}
1048
1049
1050
/*!
1051
  \internal
1052
 */
1053
QTextCursor::QTextCursor(QTextDocumentPrivate *p, int pos)
1054
944k
    : d(new QTextCursorPrivate(p))
1055
944k
{
1056
944k
    d->adjusted_anchor = d->anchor = d->position = pos;
1057
1058
944k
    d->setX();
1059
944k
}
1060
1061
/*!
1062
    \internal
1063
*/
1064
QTextCursor::QTextCursor(QTextCursorPrivate *d)
1065
553k
{
1066
553k
    Q_ASSERT(d);
1067
553k
    this->d = d;
1068
553k
}
1069
1070
/*!
1071
    Constructs a new cursor that is a copy of \a cursor.
1072
 */
1073
QTextCursor::QTextCursor(const QTextCursor &cursor)
1074
1.10M
{
1075
1.10M
    d = cursor.d;
1076
1.10M
}
1077
1078
/*!
1079
    Makes a copy of \a cursor and assigns it to this QTextCursor. Note
1080
    that QTextCursor is an \l{Implicitly Shared Classes}{implicitly
1081
    shared} class.
1082
1083
 */
1084
QTextCursor &QTextCursor::operator=(const QTextCursor &cursor)
1085
535k
{
1086
535k
    d = cursor.d;
1087
535k
    return *this;
1088
535k
}
1089
1090
/*!
1091
    \fn void QTextCursor::swap(QTextCursor &other)
1092
    \since 5.0
1093
    \memberswap{text cursor instance}
1094
*/
1095
1096
/*!
1097
    Destroys the QTextCursor.
1098
 */
1099
QTextCursor::~QTextCursor()
1100
6.37M
{
1101
6.37M
}
1102
1103
/*!
1104
    Returns \c true if the cursor is null; otherwise returns \c false. A null
1105
    cursor is created by the default constructor.
1106
 */
1107
bool QTextCursor::isNull() const
1108
535k
{
1109
535k
    return !d || !d->priv;
1110
535k
}
1111
1112
/*!
1113
    Moves the cursor to the absolute position in the document specified by
1114
    \a pos using a \c MoveMode specified by \a m. The cursor is positioned
1115
    between characters.
1116
1117
    \note The "characters" in this case refer to the string of QChar
1118
    objects, i.e. 16-bit Unicode characters, and \a pos is considered
1119
    an index into this string. This does not necessarily correspond to
1120
    individual graphemes in the writing system, as a single grapheme may
1121
    be represented by multiple Unicode characters, such as in the case
1122
    of surrogate pairs, linguistic ligatures or diacritics. For a more
1123
    generic approach to navigating the document, use movePosition(),
1124
    which will respect the actual grapheme boundaries in the text.
1125
1126
    \sa position(), movePosition(), anchor()
1127
*/
1128
void QTextCursor::setPosition(int pos, MoveMode m)
1129
1.08M
{
1130
1.08M
    if (!d || !d->priv)
1131
0
        return;
1132
1133
1.08M
    if (pos < 0 || pos >= d->priv->length()) {
1134
0
        qWarning("QTextCursor::setPosition: Position '%d' out of range", pos);
1135
0
        return;
1136
0
    }
1137
1138
1.08M
    d->setPosition(pos);
1139
1.08M
    if (m == MoveAnchor) {
1140
1.08M
        d->anchor = pos;
1141
1.08M
        d->adjusted_anchor = pos;
1142
1.08M
    } else { // keep anchor
1143
0
        QTextCursor::MoveOperation op;
1144
0
        if (pos < d->anchor)
1145
0
            op = QTextCursor::Left;
1146
0
        else
1147
0
            op = QTextCursor::Right;
1148
0
        d->adjustCursor(op);
1149
0
    }
1150
1.08M
    d->setX();
1151
1.08M
}
1152
1153
/*!
1154
    Returns the absolute position of the cursor within the document.
1155
    The cursor is positioned between characters.
1156
1157
    \note The "characters" in this case refer to the string of QChar
1158
    objects, i.e. 16-bit Unicode characters, and the position is considered
1159
    an index into this string. This does not necessarily correspond to
1160
    individual graphemes in the writing system, as a single grapheme may
1161
    be represented by multiple Unicode characters, such as in the case
1162
    of surrogate pairs, linguistic ligatures or diacritics.
1163
1164
    \sa setPosition(), movePosition(), anchor(), positionInBlock()
1165
*/
1166
int QTextCursor::position() const
1167
13.8M
{
1168
13.8M
    if (!d || !d->priv)
1169
0
        return -1;
1170
13.8M
    return d->position;
1171
13.8M
}
1172
1173
/*!
1174
    \since 4.7
1175
    Returns the relative position of the cursor within the block.
1176
    The cursor is positioned between characters.
1177
1178
    This is equivalent to \c{ position() - block().position()}.
1179
1180
    \note The "characters" in this case refer to the string of QChar
1181
    objects, i.e. 16-bit Unicode characters, and the position is considered
1182
    an index into this string. This does not necessarily correspond to
1183
    individual graphemes in the writing system, as a single grapheme may
1184
    be represented by multiple Unicode characters, such as in the case
1185
    of surrogate pairs, linguistic ligatures or diacritics.
1186
1187
    \sa position()
1188
*/
1189
int QTextCursor::positionInBlock() const
1190
0
{
1191
0
    if (!d || !d->priv)
1192
0
        return 0;
1193
0
    return d->position - d->block().position();
1194
0
}
1195
1196
/*!
1197
    Returns the anchor position; this is the same as position() unless
1198
    there is a selection in which case position() marks one end of the
1199
    selection and anchor() marks the other end. Just like the cursor
1200
    position, the anchor position is between characters.
1201
1202
    \sa position(), setPosition(), movePosition(), selectionStart(), selectionEnd()
1203
*/
1204
int QTextCursor::anchor() const
1205
0
{
1206
0
    if (!d || !d->priv)
1207
0
        return -1;
1208
0
    return d->anchor;
1209
0
}
1210
1211
/*!
1212
    \fn bool QTextCursor::movePosition(MoveOperation operation, MoveMode mode, int n)
1213
1214
    Moves the cursor by performing the given \a operation \a n times, using the specified
1215
    \a mode, and returns \c true if all operations were completed successfully; otherwise
1216
    returns \c false.
1217
1218
    For example, if this function is repeatedly used to seek to the end of the next
1219
    word, it will eventually fail when the end of the document is reached.
1220
1221
    By default, the move operation is performed once (\a n = 1).
1222
1223
    If \a mode is \c KeepAnchor, the cursor selects the text it moves
1224
    over. This is the same effect that the user achieves when they
1225
    hold down the Shift key and move the cursor with the cursor keys.
1226
1227
    \sa setVisualNavigation()
1228
*/
1229
bool QTextCursor::movePosition(MoveOperation op, MoveMode mode, int n)
1230
1.40M
{
1231
1.40M
    if (!d || !d->priv)
1232
0
        return false;
1233
1.40M
    switch (op) {
1234
0
    case Start:
1235
0
    case StartOfLine:
1236
573k
    case End:
1237
573k
    case EndOfLine:
1238
573k
        n = 1;
1239
573k
        break;
1240
832k
    default: break;
1241
1.40M
    }
1242
1243
1.40M
    int previousPosition = d->position;
1244
2.81M
    for (; n > 0; --n) {
1245
1.40M
        if (!d->movePosition(op, mode))
1246
0
            return false;
1247
1.40M
    }
1248
1249
1.40M
    if (d->visualNavigation && !d->block().isVisible()) {
1250
0
        QTextBlock b = d->block();
1251
0
        if (previousPosition < d->position) {
1252
0
            while (!b.next().isVisible())
1253
0
                b = b.next();
1254
0
            d->setPosition(b.position() + b.length() - 1);
1255
0
        } else {
1256
0
            while (!b.previous().isVisible())
1257
0
                b = b.previous();
1258
0
            d->setPosition(b.position());
1259
0
        }
1260
0
        if (mode == QTextCursor::MoveAnchor)
1261
0
            d->anchor = d->position;
1262
0
        while (d->movePosition(op, mode)
1263
0
               && !d->block().isVisible())
1264
0
            ;
1265
1266
0
    }
1267
1.40M
    return true;
1268
1.40M
}
1269
1270
/*!
1271
  \since 4.4
1272
1273
  Returns \c true if the cursor does visual navigation; otherwise
1274
  returns \c false.
1275
1276
  Visual navigation means skipping over hidden text paragraphs. The
1277
  default is false.
1278
1279
  \sa setVisualNavigation(), movePosition()
1280
 */
1281
bool QTextCursor::visualNavigation() const
1282
0
{
1283
0
    return d ? d->visualNavigation : false;
1284
0
}
1285
1286
/*!
1287
  \since 4.4
1288
1289
  Sets visual navigation to \a b.
1290
1291
  Visual navigation means skipping over hidden text paragraphs. The
1292
  default is false.
1293
1294
  \sa visualNavigation(), movePosition()
1295
 */
1296
void QTextCursor::setVisualNavigation(bool b)
1297
0
{
1298
0
    if (d)
1299
0
        d->visualNavigation = b;
1300
0
}
1301
1302
1303
/*!
1304
  \since 4.7
1305
1306
  Sets the visual x position for vertical cursor movements to \a x.
1307
1308
  The vertical movement x position is cleared automatically when the cursor moves horizontally, and kept
1309
  unchanged when the cursor moves vertically. The mechanism allows the cursor to move up and down on a
1310
  visually straight line with proportional fonts, and to gently "jump" over short lines.
1311
1312
  A value of -1 indicates no predefined x position. It will then be set automatically the next time the
1313
  cursor moves up or down.
1314
1315
  \sa verticalMovementX()
1316
  */
1317
void QTextCursor::setVerticalMovementX(int x)
1318
0
{
1319
0
    if (d)
1320
0
        d->x = x;
1321
0
}
1322
1323
/*! \since 4.7
1324
1325
  Returns the visual x position for vertical cursor movements.
1326
1327
  A value of -1 indicates no predefined x position. It will then be set automatically the next time the
1328
  cursor moves up or down.
1329
1330
  \sa setVerticalMovementX()
1331
  */
1332
int QTextCursor::verticalMovementX() const
1333
0
{
1334
0
    return d ? d->x : -1;
1335
0
}
1336
1337
/*!
1338
  \since 4.7
1339
1340
  Returns whether the cursor should keep its current position when text gets inserted at the position of the
1341
  cursor.
1342
1343
  The default is false;
1344
1345
  \sa setKeepPositionOnInsert()
1346
 */
1347
bool QTextCursor::keepPositionOnInsert() const
1348
0
{
1349
0
    return d ? d->keepPositionOnInsert : false;
1350
0
}
1351
1352
/*!
1353
  \since 4.7
1354
1355
  Defines whether the cursor should keep its current position when text gets inserted at the current position of the
1356
  cursor.
1357
1358
  If \a b is true, the cursor keeps its current position when text gets inserted at the positing of the cursor.
1359
  If \a b is false, the cursor moves along with the inserted text.
1360
1361
  The default is false.
1362
1363
  Note that a cursor always moves when text is inserted before the current position of the cursor, and it
1364
  always keeps its position when text is inserted after the current position of the cursor.
1365
1366
  \sa keepPositionOnInsert()
1367
 */
1368
void QTextCursor::setKeepPositionOnInsert(bool b)
1369
0
{
1370
0
    if (d)
1371
0
        d->keepPositionOnInsert = b;
1372
0
}
1373
1374
1375
1376
/*!
1377
    Inserts \a text at the current position, using the current
1378
    character format.
1379
1380
    If there is a selection, the selection is deleted and replaced by
1381
    \a text, for example:
1382
    \snippet code/src_gui_text_qtextcursor.cpp 0
1383
    This clears any existing selection, selects the word at the cursor
1384
    (i.e. from position() forward), and replaces the selection with
1385
    the phrase "Hello World".
1386
1387
    Any ASCII linefeed characters (\\n) in the inserted text are transformed
1388
    into unicode block separators, corresponding to insertBlock() calls.
1389
1390
    \sa charFormat(), hasSelection()
1391
*/
1392
void QTextCursor::insertText(const QString &text)
1393
27.5M
{
1394
27.5M
    QTextCharFormat fmt = charFormat();
1395
27.5M
    fmt.clearProperty(QTextFormat::ObjectType);
1396
27.5M
    insertText(text, fmt);
1397
27.5M
}
1398
1399
/*!
1400
    \fn void QTextCursor::insertText(const QString &text, const QTextCharFormat &format)
1401
    \overload
1402
1403
    Inserts \a text at the current position with the given \a format.
1404
*/
1405
void QTextCursor::insertText(const QString &text, const QTextCharFormat &_format)
1406
30.6M
{
1407
30.6M
    if (!d || !d->priv)
1408
0
        return;
1409
1410
30.6M
    Q_ASSERT(_format.isValid());
1411
1412
30.6M
    QTextCharFormat format = _format;
1413
30.6M
    format.clearProperty(QTextFormat::ObjectIndex);
1414
1415
30.6M
    bool hasEditBlock = false;
1416
1417
30.6M
    if (d->anchor != d->position) {
1418
0
        hasEditBlock = true;
1419
0
        d->priv->beginEditBlock();
1420
0
        d->remove();
1421
0
    }
1422
1423
30.6M
    if (!text.isEmpty()) {
1424
30.6M
        QTextFormatCollection *formats = d->priv->formatCollection();
1425
30.6M
        int formatIdx = formats->indexForFormat(format);
1426
30.6M
        Q_ASSERT(formats->format(formatIdx).isCharFormat());
1427
1428
30.6M
        QTextBlockFormat blockFmt = blockFormat();
1429
1430
1431
30.6M
        int textStart = d->priv->text.size();
1432
30.6M
        int blockStart = 0;
1433
30.6M
        d->priv->text += text;
1434
30.6M
        int textEnd = d->priv->text.size();
1435
1436
267M
        for (int i = 0; i < text.size(); ++i) {
1437
237M
            QChar ch = text.at(i);
1438
1439
237M
            const int blockEnd = i;
1440
1441
237M
            if (ch == u'\r'
1442
1.47k
                && (i + 1) < text.size()
1443
1.09k
                && text.at(i + 1) == u'\n') {
1444
0
                ++i;
1445
0
                ch = text.at(i);
1446
0
            }
1447
1448
237M
            if (ch == u'\n'
1449
237M
                || ch == QChar::ParagraphSeparator
1450
237M
                || ch == QTextBeginningOfFrame
1451
237M
                || ch == QTextEndOfFrame
1452
237M
                || ch == u'\r') {
1453
1454
84.8k
                if (!hasEditBlock) {
1455
78.1k
                    hasEditBlock = true;
1456
78.1k
                    d->priv->beginEditBlock();
1457
78.1k
                }
1458
1459
84.8k
                if (blockEnd > blockStart)
1460
10.1k
                    d->priv->insert(d->position, textStart + blockStart, blockEnd - blockStart, formatIdx);
1461
1462
84.8k
                d->insertBlock(blockFmt, format);
1463
84.8k
                blockStart = i + 1;
1464
84.8k
            }
1465
237M
        }
1466
30.6M
        if (textStart + blockStart < textEnd)
1467
30.5M
            d->priv->insert(d->position, textStart + blockStart, textEnd - textStart - blockStart, formatIdx);
1468
30.6M
    }
1469
30.6M
    if (hasEditBlock)
1470
78.1k
        d->priv->endEditBlock();
1471
30.6M
    d->setX();
1472
30.6M
}
1473
1474
/*!
1475
    If there is no selected text, deletes the character \e at the
1476
    current cursor position; otherwise deletes the selected text.
1477
1478
    \sa deletePreviousChar(), hasSelection(), clearSelection()
1479
*/
1480
void QTextCursor::deleteChar()
1481
0
{
1482
0
    if (!d || !d->priv)
1483
0
        return;
1484
1485
0
    if (d->position != d->anchor) {
1486
0
        removeSelectedText();
1487
0
        return;
1488
0
    }
1489
1490
0
    if (!d->canDelete(d->position))
1491
0
        return;
1492
0
    d->adjusted_anchor = d->anchor =
1493
0
                         d->priv->nextCursorPosition(d->anchor, QTextLayout::SkipCharacters);
1494
0
    d->remove();
1495
0
    d->setX();
1496
0
}
1497
1498
/*!
1499
    If there is no selected text, deletes the character \e before the
1500
    current cursor position; otherwise deletes the selected text.
1501
1502
    \sa deleteChar(), hasSelection(), clearSelection()
1503
*/
1504
void QTextCursor::deletePreviousChar()
1505
0
{
1506
0
    if (!d || !d->priv)
1507
0
        return;
1508
1509
0
    if (d->position != d->anchor) {
1510
0
        removeSelectedText();
1511
0
        return;
1512
0
    }
1513
1514
0
    if (d->anchor < 1 || !d->canDelete(d->anchor-1))
1515
0
        return;
1516
1517
    // For emoji sequences, backspace deletes the entire grapheme cluster,
1518
    // since individual codepoints are meaningless to the user.
1519
    // For other clusters (e.g. Devanagari consonant+vowel), we delete
1520
    // only one codepoint at a time, matching the behavior of other major
1521
    // text editors.
1522
0
    const int clusterStart =
1523
0
            d->priv->previousCursorPosition(d->anchor, QTextLayout::SkipCharacters);
1524
0
    const int clusterCodeUnits = d->anchor - clusterStart;
1525
1526
0
    if (clusterCodeUnits > 1) {
1527
        // Multi-code-unit cluster: use QTextEngine's script analysis
1528
        // to determine if this is an emoji sequence. The emoji segmenter
1529
        // (run during itemization) tags emoji sequences with Script_Emoji.
1530
        // Note: if Qt is built with QT_NO_EMOJISEGMENTER, Script_Emoji is
1531
        // never assigned, and we fall through to the per-codepoint path.
1532
0
        QTextBlock block = d->block();
1533
0
        const int posInBlock = clusterStart - block.position();
1534
0
        Q_ASSERT(posInBlock >= 0 && posInBlock < block.length());
1535
1536
0
        QTextEngine *engine = d->blockLayout(block)->engine();
1537
0
        const int itemIndex = engine->findItem(posInBlock);
1538
1539
0
        if (itemIndex >= 0
1540
0
            && engine->layoutData->items[itemIndex].analysis.script
1541
0
                    == QFontDatabasePrivate::Script_Emoji) {
1542
0
            d->anchor = clusterStart;
1543
0
        } else {
1544
0
            --d->anchor;
1545
            // Step over the high surrogate if we landed on a low surrogate
1546
0
            const QStringView text = engine->layoutData->string;
1547
0
            const int idx = d->anchor - block.position();
1548
0
            if (idx > 0 && text.at(idx).isLowSurrogate()
1549
0
                && text.at(idx - 1).isHighSurrogate()) {
1550
0
                --d->anchor;
1551
0
            }
1552
0
        }
1553
0
    } else {
1554
        // Single code unit (ASCII/BMP): just step back one position
1555
0
        --d->anchor;
1556
0
    }
1557
1558
0
    d->adjusted_anchor = d->anchor;
1559
0
    d->remove();
1560
0
    d->setX();
1561
0
}
1562
1563
/*!
1564
    Selects text in the document according to the given \a selection.
1565
*/
1566
void QTextCursor::select(SelectionType selection)
1567
0
{
1568
0
    if (!d || !d->priv)
1569
0
        return;
1570
1571
0
    clearSelection();
1572
1573
0
    const QTextBlock block = d->block();
1574
1575
0
    switch (selection) {
1576
0
        case LineUnderCursor:
1577
0
            movePosition(StartOfLine);
1578
0
            movePosition(EndOfLine, KeepAnchor);
1579
0
            break;
1580
0
        case WordUnderCursor:
1581
0
            movePosition(StartOfWord);
1582
0
            movePosition(EndOfWord, KeepAnchor);
1583
0
            break;
1584
0
        case BlockUnderCursor:
1585
0
            if (block.length() == 1) // no content
1586
0
                break;
1587
0
            movePosition(StartOfBlock);
1588
            // also select the paragraph separator
1589
0
            if (movePosition(PreviousBlock)) {
1590
0
                movePosition(EndOfBlock);
1591
0
                movePosition(NextBlock, KeepAnchor);
1592
0
            }
1593
0
            movePosition(EndOfBlock, KeepAnchor);
1594
0
            break;
1595
0
        case Document:
1596
0
            movePosition(Start);
1597
0
            movePosition(End, KeepAnchor);
1598
0
            break;
1599
0
    }
1600
0
}
1601
1602
/*!
1603
    Returns \c true if the cursor contains a selection; otherwise returns \c false.
1604
*/
1605
bool QTextCursor::hasSelection() const
1606
876k
{
1607
876k
    return !!d && d->position != d->anchor;
1608
876k
}
1609
1610
1611
/*!
1612
    Returns \c true if the cursor contains a selection that is not simply a
1613
    range from selectionStart() to selectionEnd(); otherwise returns \c false.
1614
1615
    Complex selections are ones that span at least two cells in a table;
1616
    their extent is specified by selectedTableCells().
1617
*/
1618
bool QTextCursor::hasComplexSelection() const
1619
535k
{
1620
535k
    if (!d)
1621
0
        return false;
1622
1623
535k
    return d->complexSelectionTable() != nullptr;
1624
535k
}
1625
1626
/*!
1627
    If the selection spans over table cells, \a firstRow is populated
1628
    with the number of the first row in the selection, \a firstColumn
1629
    with the number of the first column in the selection, and \a
1630
    numRows and \a numColumns with the number of rows and columns in
1631
    the selection. If the selection does not span any table cells the
1632
    results are harmless but undefined.
1633
*/
1634
void QTextCursor::selectedTableCells(int *firstRow, int *numRows, int *firstColumn, int *numColumns) const
1635
0
{
1636
0
    *firstRow = -1;
1637
0
    *firstColumn = -1;
1638
0
    *numRows = -1;
1639
0
    *numColumns = -1;
1640
1641
0
    if (!d || d->position == d->anchor)
1642
0
        return;
1643
1644
0
    d->selectedTableCells(firstRow, numRows, firstColumn, numColumns);
1645
0
}
1646
1647
1648
/*!
1649
    Clears the current selection by setting the anchor to the cursor position.
1650
1651
    Note that it does \b{not} delete the text of the selection.
1652
1653
    \sa removeSelectedText(), hasSelection()
1654
*/
1655
void QTextCursor::clearSelection()
1656
0
{
1657
0
    if (!d)
1658
0
        return;
1659
0
    d->adjusted_anchor = d->anchor = d->position;
1660
0
    d->currentCharFormat = -1;
1661
0
}
1662
1663
/*!
1664
    If there is a selection, its content is deleted; otherwise does
1665
    nothing.
1666
1667
    \sa hasSelection()
1668
*/
1669
void QTextCursor::removeSelectedText()
1670
0
{
1671
0
    if (!d || !d->priv || d->position == d->anchor)
1672
0
        return;
1673
1674
0
    d->priv->beginEditBlock();
1675
0
    d->remove();
1676
0
    d->priv->endEditBlock();
1677
0
    d->setX();
1678
0
}
1679
1680
/*!
1681
    Returns the start of the selection or position() if the
1682
    cursor doesn't have a selection.
1683
1684
    \sa selectionEnd(), position(), anchor()
1685
*/
1686
int QTextCursor::selectionStart() const
1687
535k
{
1688
535k
    if (!d || !d->priv)
1689
0
        return -1;
1690
535k
    return qMin(d->position, d->adjusted_anchor);
1691
535k
}
1692
1693
/*!
1694
    Returns the end of the selection or position() if the cursor
1695
    doesn't have a selection.
1696
1697
    \sa selectionStart(), position(), anchor()
1698
*/
1699
int QTextCursor::selectionEnd() const
1700
535k
{
1701
535k
    if (!d || !d->priv)
1702
0
        return -1;
1703
535k
    return qMax(d->position, d->adjusted_anchor);
1704
535k
}
1705
1706
static void getText(QString &text, QTextDocumentPrivate *priv, const QString &docText, int pos, int end)
1707
0
{
1708
0
    while (pos < end) {
1709
0
        QTextDocumentPrivate::FragmentIterator fragIt = priv->find(pos);
1710
0
        const QTextFragmentData * const frag = fragIt.value();
1711
1712
0
        const int offsetInFragment = qMax(0, pos - fragIt.position());
1713
0
        const int len = qMin(int(frag->size_array[0] - offsetInFragment), end - pos);
1714
1715
0
        text += QStringView(docText.constData() + frag->stringPosition + offsetInFragment, len);
1716
0
        pos += len;
1717
0
    }
1718
0
}
1719
1720
/*!
1721
    Returns the current selection's text (which may be empty). This
1722
    only returns the text, with no rich text formatting information.
1723
    If you want a document fragment (i.e. formatted rich text) use
1724
    selection() instead.
1725
1726
    \note If the selection obtained from an editor spans a line break,
1727
    the text will contain a Unicode U+2029 paragraph separator character
1728
    instead of a newline \c{\n} character. Use QString::replace() to
1729
    replace these characters with newlines.
1730
*/
1731
QString QTextCursor::selectedText() const
1732
0
{
1733
0
    if (!d || !d->priv || d->position == d->anchor)
1734
0
        return QString();
1735
1736
0
    const QString docText = d->priv->buffer();
1737
0
    QString text;
1738
1739
0
    QTextTable *table = d->complexSelectionTable();
1740
0
    if (table) {
1741
0
        int row_start, col_start, num_rows, num_cols;
1742
0
        selectedTableCells(&row_start, &num_rows, &col_start, &num_cols);
1743
1744
0
        Q_ASSERT(row_start != -1);
1745
0
        for (int r = row_start; r < row_start + num_rows; ++r) {
1746
0
            for (int c = col_start; c < col_start + num_cols; ++c) {
1747
0
                QTextTableCell cell = table->cellAt(r, c);
1748
0
                int rspan = cell.rowSpan();
1749
0
                int cspan = cell.columnSpan();
1750
0
                if (rspan != 1) {
1751
0
                    int cr = cell.row();
1752
0
                    if (cr != r)
1753
0
                        continue;
1754
0
                }
1755
0
                if (cspan != 1) {
1756
0
                    int cc = cell.column();
1757
0
                    if (cc != c)
1758
0
                        continue;
1759
0
                }
1760
1761
0
                getText(text, d->priv, docText, cell.firstPosition(), cell.lastPosition());
1762
0
            }
1763
0
        }
1764
0
    } else {
1765
0
        getText(text, d->priv, docText, selectionStart(), selectionEnd());
1766
0
    }
1767
1768
0
    return text;
1769
0
}
1770
1771
/*!
1772
    Returns the current selection (which may be empty) with all its
1773
    formatting information. If you just want the selected text (i.e.
1774
    plain text) use selectedText() instead.
1775
1776
    \note Unlike QTextDocumentFragment::toPlainText(),
1777
    selectedText() may include special unicode characters such as
1778
    QChar::ParagraphSeparator.
1779
1780
    \sa QTextDocumentFragment::toPlainText()
1781
*/
1782
QTextDocumentFragment QTextCursor::selection() const
1783
0
{
1784
0
    return QTextDocumentFragment(*this);
1785
0
}
1786
1787
/*!
1788
    Returns the block that contains the cursor.
1789
*/
1790
QTextBlock QTextCursor::block() const
1791
255k
{
1792
255k
    if (!d || !d->priv)
1793
0
        return QTextBlock();
1794
255k
    return d->block();
1795
255k
}
1796
1797
/*!
1798
    Returns the block format of the block the cursor is in.
1799
1800
    \sa setBlockFormat(), charFormat()
1801
 */
1802
QTextBlockFormat QTextCursor::blockFormat() const
1803
84.2M
{
1804
84.2M
    if (!d || !d->priv)
1805
0
        return QTextBlockFormat();
1806
1807
84.2M
    return d->block().blockFormat();
1808
84.2M
}
1809
1810
/*!
1811
    Sets the block format of the current block (or all blocks that
1812
    are contained in the selection) to \a format.
1813
1814
    \sa blockFormat(), mergeBlockFormat()
1815
*/
1816
void QTextCursor::setBlockFormat(const QTextBlockFormat &format)
1817
6.91M
{
1818
6.91M
    if (!d || !d->priv)
1819
0
        return;
1820
1821
6.91M
    d->setBlockFormat(format, QTextDocumentPrivate::SetFormat);
1822
6.91M
}
1823
1824
/*!
1825
    Modifies the block format of the current block (or all blocks that
1826
    are contained in the selection) with the block format specified by
1827
    \a modifier.
1828
1829
    \sa setBlockFormat(), blockFormat()
1830
*/
1831
void QTextCursor::mergeBlockFormat(const QTextBlockFormat &modifier)
1832
1.16M
{
1833
1.16M
    if (!d || !d->priv)
1834
0
        return;
1835
1836
1.16M
    d->setBlockFormat(modifier, QTextDocumentPrivate::MergeFormat);
1837
1.16M
}
1838
1839
/*!
1840
    Returns the block character format of the block the cursor is in.
1841
1842
    The block char format is the format used when inserting text at the
1843
    beginning of an empty block.
1844
1845
    \sa setBlockCharFormat()
1846
 */
1847
QTextCharFormat QTextCursor::blockCharFormat() const
1848
1.10M
{
1849
1.10M
    if (!d || !d->priv)
1850
0
        return QTextCharFormat();
1851
1852
1.10M
    return d->block().charFormat();
1853
1.10M
}
1854
1855
/*!
1856
    Sets the block char format of the current block (or all blocks that
1857
    are contained in the selection) to \a format.
1858
1859
    \sa blockCharFormat()
1860
*/
1861
void QTextCursor::setBlockCharFormat(const QTextCharFormat &format)
1862
370k
{
1863
370k
    if (!d || !d->priv)
1864
0
        return;
1865
1866
370k
    d->setBlockCharFormat(format, QTextDocumentPrivate::SetFormatAndPreserveObjectIndices);
1867
370k
}
1868
1869
/*!
1870
    Modifies the block char format of the current block (or all blocks that
1871
    are contained in the selection) with the block format specified by
1872
    \a modifier.
1873
1874
    \sa setBlockCharFormat()
1875
*/
1876
void QTextCursor::mergeBlockCharFormat(const QTextCharFormat &modifier)
1877
15.5k
{
1878
15.5k
    if (!d || !d->priv)
1879
0
        return;
1880
1881
15.5k
    d->setBlockCharFormat(modifier, QTextDocumentPrivate::MergeFormat);
1882
15.5k
}
1883
1884
/*!
1885
    Returns the format of the character immediately before the cursor
1886
    position(). If the cursor is positioned at the beginning of a text
1887
    block that is not empty then the format of the character
1888
    immediately after the cursor is returned.
1889
1890
    \sa insertText(), blockFormat()
1891
 */
1892
QTextCharFormat QTextCursor::charFormat() const
1893
28.4M
{
1894
28.4M
    if (!d || !d->priv)
1895
0
        return QTextCharFormat();
1896
1897
28.4M
    int idx = d->currentCharFormat;
1898
28.4M
    if (idx == -1) {
1899
26.7M
        QTextBlock block = d->block();
1900
1901
26.7M
        int pos;
1902
26.7M
        if (d->position == block.position()
1903
941k
            && block.length() > 1)
1904
508
            pos = d->position;
1905
26.7M
        else
1906
26.7M
            pos = d->position - 1;
1907
1908
26.7M
        if (pos == -1) {
1909
5.53k
            idx = d->priv->blockCharFormatIndex(d->priv->blockMap().firstNode());
1910
26.6M
        } else {
1911
26.6M
            Q_ASSERT(pos >= 0 && pos < d->priv->length());
1912
1913
26.6M
            QTextDocumentPrivate::FragmentIterator it = d->priv->find(pos);
1914
26.6M
            Q_ASSERT(!it.atEnd());
1915
26.6M
            idx = it.value()->format;
1916
26.6M
        }
1917
26.7M
    }
1918
1919
28.4M
    QTextCharFormat cfmt = d->priv->formatCollection()->charFormat(idx);
1920
28.4M
    cfmt.clearProperty(QTextFormat::ObjectIndex);
1921
1922
28.4M
    Q_ASSERT(cfmt.isValid());
1923
28.4M
    return cfmt;
1924
28.4M
}
1925
1926
/*!
1927
    Sets the cursor's current character format to the given \a
1928
    format. If the cursor has a selection, the given \a format is
1929
    applied to the current selection.
1930
1931
    \sa hasSelection(), mergeCharFormat()
1932
*/
1933
void QTextCursor::setCharFormat(const QTextCharFormat &format)
1934
5.97M
{
1935
5.97M
    if (!d || !d->priv)
1936
0
        return;
1937
5.97M
    if (d->position == d->anchor) {
1938
5.97M
        d->currentCharFormat = d->priv->formatCollection()->indexForFormat(format);
1939
5.97M
        return;
1940
5.97M
    }
1941
0
    d->setCharFormat(format, QTextDocumentPrivate::SetFormatAndPreserveObjectIndices);
1942
0
}
1943
1944
/*!
1945
    Merges the cursor's current character format with the properties
1946
    described by format \a modifier. If the cursor has a selection,
1947
    this function applies all the properties set in \a modifier to all
1948
    the character formats that are part of the selection.
1949
1950
    \sa hasSelection(), setCharFormat()
1951
*/
1952
void QTextCursor::mergeCharFormat(const QTextCharFormat &modifier)
1953
416k
{
1954
416k
    if (!d || !d->priv)
1955
0
        return;
1956
416k
    if (d->position == d->anchor) {
1957
0
        QTextCharFormat format = charFormat();
1958
0
        format.merge(modifier);
1959
0
        d->currentCharFormat = d->priv->formatCollection()->indexForFormat(format);
1960
0
        return;
1961
0
    }
1962
1963
416k
    d->setCharFormat(modifier, QTextDocumentPrivate::MergeFormat);
1964
416k
}
1965
1966
/*!
1967
    Returns \c true if the cursor is at the start of a block; otherwise
1968
    returns \c false.
1969
1970
    \sa atBlockEnd(), atStart()
1971
*/
1972
bool QTextCursor::atBlockStart() const
1973
0
{
1974
0
    if (!d || !d->priv)
1975
0
        return false;
1976
1977
0
    return d->position == d->block().position();
1978
0
}
1979
1980
/*!
1981
    Returns \c true if the cursor is at the end of a block; otherwise
1982
    returns \c false.
1983
1984
    \sa atBlockStart(), atEnd()
1985
*/
1986
bool QTextCursor::atBlockEnd() const
1987
0
{
1988
0
    if (!d || !d->priv)
1989
0
        return false;
1990
1991
0
    return d->position == d->block().position() + d->block().length() - 1;
1992
0
}
1993
1994
/*!
1995
    Returns \c true if the cursor is at the start of the document;
1996
    otherwise returns \c false.
1997
1998
    \sa atBlockStart(), atEnd()
1999
*/
2000
bool QTextCursor::atStart() const
2001
0
{
2002
0
    if (!d || !d->priv)
2003
0
        return false;
2004
2005
0
    return d->position == 0;
2006
0
}
2007
2008
/*!
2009
    \since 4.6
2010
2011
    Returns \c true if the cursor is at the end of the document;
2012
    otherwise returns \c false.
2013
2014
    \sa atStart(), atBlockEnd()
2015
*/
2016
bool QTextCursor::atEnd() const
2017
0
{
2018
0
    if (!d || !d->priv)
2019
0
        return false;
2020
2021
0
    return d->position == d->priv->length() - 1;
2022
0
}
2023
2024
/*!
2025
    Inserts a new empty block at the cursor position() with the
2026
    current blockFormat() and charFormat().
2027
2028
    \sa setBlockFormat()
2029
*/
2030
void QTextCursor::insertBlock()
2031
291k
{
2032
291k
    insertBlock(blockFormat());
2033
291k
}
2034
2035
/*!
2036
    \overload
2037
2038
    Inserts a new empty block at the cursor position() with block
2039
    format \a format and the current charFormat() as block char format.
2040
2041
    \sa setBlockFormat()
2042
*/
2043
void QTextCursor::insertBlock(const QTextBlockFormat &format)
2044
291k
{
2045
291k
    QTextCharFormat charFmt = charFormat();
2046
291k
    charFmt.clearProperty(QTextFormat::ObjectType);
2047
291k
    insertBlock(format, charFmt);
2048
291k
}
2049
2050
/*!
2051
    \fn void QTextCursor::insertBlock(const QTextBlockFormat &format, const QTextCharFormat &charFormat)
2052
    \overload
2053
2054
    Inserts a new empty block at the cursor position() with block
2055
    format \a format and \a charFormat as block char format.
2056
2057
    \sa setBlockFormat()
2058
*/
2059
void QTextCursor::insertBlock(const QTextBlockFormat &format, const QTextCharFormat &_charFormat)
2060
2.51M
{
2061
2.51M
    if (!d || !d->priv)
2062
0
        return;
2063
2064
2.51M
    QTextCharFormat charFormat = _charFormat;
2065
2.51M
    charFormat.clearProperty(QTextFormat::ObjectIndex);
2066
2067
2.51M
    d->priv->beginEditBlock();
2068
2.51M
    d->remove();
2069
2.51M
    d->insertBlock(format, charFormat);
2070
2.51M
    d->priv->endEditBlock();
2071
2.51M
    d->setX();
2072
2.51M
}
2073
2074
/*!
2075
    Inserts a new block at the current position and makes it the first
2076
    list item of a newly created list with the given \a format. Returns
2077
    the created list.
2078
2079
    \sa currentList(), createList(), insertBlock()
2080
 */
2081
QTextList *QTextCursor::insertList(const QTextListFormat &format)
2082
291k
{
2083
291k
    insertBlock();
2084
291k
    return createList(format);
2085
291k
}
2086
2087
/*!
2088
    \overload
2089
2090
    Inserts a new block at the current position and makes it the first
2091
    list item of a newly created list with the given \a style. Returns
2092
    the created list.
2093
2094
    \sa currentList(), createList(), insertBlock()
2095
 */
2096
QTextList *QTextCursor::insertList(QTextListFormat::Style style)
2097
0
{
2098
0
    insertBlock();
2099
0
    return createList(style);
2100
0
}
2101
2102
/*!
2103
    Creates and returns a new list with the given \a format, and makes the
2104
    current paragraph the cursor is in the first list item.
2105
2106
    \sa insertList(), currentList()
2107
 */
2108
QTextList *QTextCursor::createList(const QTextListFormat &format)
2109
1.04M
{
2110
1.04M
    if (!d || !d->priv)
2111
0
        return nullptr;
2112
2113
1.04M
    QTextList *list = static_cast<QTextList *>(d->priv->createObject(format));
2114
1.04M
    QTextBlockFormat modifier;
2115
1.04M
    modifier.setObjectIndex(list->objectIndex());
2116
1.04M
    mergeBlockFormat(modifier);
2117
1.04M
    return list;
2118
1.04M
}
2119
2120
/*!
2121
    \overload
2122
2123
    Creates and returns a new list with the given \a style, making the
2124
    cursor's current paragraph the first list item.
2125
2126
    The style to be used is defined by the QTextListFormat::Style enum.
2127
2128
    \sa insertList(), currentList()
2129
 */
2130
QTextList *QTextCursor::createList(QTextListFormat::Style style)
2131
0
{
2132
0
    QTextListFormat fmt;
2133
0
    fmt.setStyle(style);
2134
0
    return createList(fmt);
2135
0
}
2136
2137
/*!
2138
    Returns the current list if the cursor position() is inside a
2139
    block that is part of a list; otherwise returns \nullptr.
2140
2141
    \sa insertList(), createList()
2142
 */
2143
QTextList *QTextCursor::currentList() const
2144
46.3M
{
2145
46.3M
    if (!d || !d->priv)
2146
0
        return nullptr;
2147
2148
46.3M
    QTextBlockFormat b = blockFormat();
2149
46.3M
    QTextObject *o = d->priv->objectForFormat(b);
2150
46.3M
    return qobject_cast<QTextList *>(o);
2151
46.3M
}
2152
2153
/*!
2154
    \fn QTextTable *QTextCursor::insertTable(int rows, int columns)
2155
2156
    \overload
2157
2158
    Creates a new table with the given number of \a rows and \a columns,
2159
    inserts it at the current cursor position() in the document, and returns
2160
    the table object. The cursor is moved to the beginning of the first cell.
2161
2162
    There must be at least one row and one column in the table.
2163
2164
    \sa currentTable()
2165
 */
2166
QTextTable *QTextCursor::insertTable(int rows, int cols)
2167
37.3k
{
2168
37.3k
    return insertTable(rows, cols, QTextTableFormat());
2169
37.3k
}
2170
2171
/*!
2172
    \fn QTextTable *QTextCursor::insertTable(int rows, int columns, const QTextTableFormat &format)
2173
2174
    Creates a new table with the given number of \a rows and \a columns
2175
    in the specified \a format, inserts it at the current cursor position()
2176
    in the document, and returns the table object. The cursor is moved to
2177
    the beginning of the first cell.
2178
2179
    There must be at least one row and one column in the table.
2180
2181
    \sa currentTable()
2182
*/
2183
QTextTable *QTextCursor::insertTable(int rows, int cols, const QTextTableFormat &format)
2184
364k
{
2185
364k
    if (!d || !d->priv || rows == 0 || cols == 0)
2186
0
        return nullptr;
2187
2188
364k
    int pos = d->position;
2189
364k
    QTextTable *t = QTextTablePrivate::createTable(d->priv, d->position, rows, cols, format);
2190
364k
    d->setPosition(pos+1);
2191
    // ##### what should we do if we have a selection?
2192
364k
    d->anchor = d->position;
2193
364k
    d->adjusted_anchor = d->anchor;
2194
364k
    return t;
2195
364k
}
2196
2197
/*!
2198
    Returns a pointer to the current table if the cursor position()
2199
    is inside a block that is part of a table; otherwise returns \nullptr.
2200
2201
    \sa insertTable()
2202
*/
2203
QTextTable *QTextCursor::currentTable() const
2204
0
{
2205
0
    if (!d || !d->priv)
2206
0
        return nullptr;
2207
2208
0
    QTextFrame *frame = d->priv->frameAt(d->position);
2209
0
    while (frame) {
2210
0
        QTextTable *table = qobject_cast<QTextTable *>(frame);
2211
0
        if (table)
2212
0
            return table;
2213
0
        frame = frame->parentFrame();
2214
0
    }
2215
0
    return nullptr;
2216
0
}
2217
2218
/*!
2219
    Inserts a frame with the given \a format at the current cursor position(),
2220
    moves the cursor position() inside the frame, and returns the frame.
2221
2222
    If the cursor holds a selection, the whole selection is moved inside the
2223
    frame.
2224
2225
    \sa hasSelection()
2226
*/
2227
QTextFrame *QTextCursor::insertFrame(const QTextFrameFormat &format)
2228
0
{
2229
0
    if (!d || !d->priv)
2230
0
        return nullptr;
2231
2232
0
    return d->priv->insertFrame(selectionStart(), selectionEnd(), format);
2233
0
}
2234
2235
/*!
2236
    Returns a pointer to the current frame. Returns \nullptr if the cursor is invalid.
2237
2238
    \sa insertFrame()
2239
*/
2240
QTextFrame *QTextCursor::currentFrame() const
2241
0
{
2242
0
    if (!d || !d->priv)
2243
0
        return nullptr;
2244
2245
0
    return d->priv->frameAt(d->position);
2246
0
}
2247
2248
2249
/*!
2250
    Inserts the text \a fragment at the current position().
2251
*/
2252
void QTextCursor::insertFragment(const QTextDocumentFragment &fragment)
2253
876k
{
2254
876k
    if (!d || !d->priv || fragment.isEmpty())
2255
341k
        return;
2256
2257
535k
    d->priv->beginEditBlock();
2258
535k
    d->remove();
2259
535k
    fragment.d->insert(*this);
2260
535k
    d->priv->endEditBlock();
2261
535k
    d->setX();
2262
2263
535k
    if (fragment.d && fragment.d->doc)
2264
535k
        d->priv->mergeCachedResources(QTextDocumentPrivate::get(fragment.d->doc));
2265
535k
}
2266
2267
/*!
2268
    \since 4.2
2269
    Inserts the text \a html at the current position(). The text is interpreted as
2270
    HTML.
2271
2272
    \note When using this function with a style sheet, the style sheet will
2273
    only apply to the current block in the document. In order to apply a style
2274
    sheet throughout a document, use QTextDocument::setDefaultStyleSheet()
2275
    instead.
2276
*/
2277
2278
#ifndef QT_NO_TEXTHTMLPARSER
2279
2280
void QTextCursor::insertHtml(const QString &html)
2281
876k
{
2282
876k
    if (!d || !d->priv)
2283
0
        return;
2284
876k
    QTextDocumentFragment fragment = QTextDocumentFragment::fromHtml(html, d->priv->document());
2285
876k
    insertFragment(fragment);
2286
876k
}
2287
2288
#endif // QT_NO_TEXTHTMLPARSER
2289
2290
/*!
2291
    \since 6.4
2292
    Inserts the \a markdown text at the current position(),
2293
    with the specified Markdown \a features. The default is GitHub dialect.
2294
*/
2295
2296
#if QT_CONFIG(textmarkdownreader)
2297
2298
void QTextCursor::insertMarkdown(const QString &markdown, QTextDocument::MarkdownFeatures features)
2299
0
{
2300
0
    if (!d || !d->priv)
2301
0
        return;
2302
0
    QTextDocumentFragment fragment = QTextDocumentFragment::fromMarkdown(markdown, features);
2303
0
    if (markdown.startsWith(QLatin1Char('\n')))
2304
0
        insertBlock(fragment.d->doc->firstBlock().blockFormat());
2305
0
    insertFragment(fragment);
2306
0
    if (!atEnd() && markdown.endsWith(QLatin1Char('\n')))
2307
0
        insertText(QLatin1String("\n"));
2308
0
}
2309
2310
#endif // textmarkdownreader
2311
2312
/*!
2313
    \overload
2314
    \since 4.2
2315
2316
    Inserts the image defined by the given \a format at the cursor's current position
2317
    with the specified \a alignment.
2318
2319
    \sa position()
2320
*/
2321
void QTextCursor::insertImage(const QTextImageFormat &format, QTextFrameFormat::Position alignment)
2322
416k
{
2323
416k
    if (!d || !d->priv)
2324
0
        return;
2325
2326
416k
    QTextFrameFormat ffmt;
2327
416k
    ffmt.setPosition(alignment);
2328
416k
    QTextObject *obj = d->priv->createObject(ffmt);
2329
2330
416k
    QTextImageFormat fmt = format;
2331
416k
    fmt.setObjectIndex(obj->objectIndex());
2332
2333
416k
    d->priv->beginEditBlock();
2334
416k
    d->remove();
2335
416k
    const int idx = d->priv->formatCollection()->indexForFormat(fmt);
2336
416k
    d->priv->insert(d->position, QChar(QChar::ObjectReplacementCharacter), idx);
2337
416k
    d->priv->endEditBlock();
2338
416k
}
2339
2340
/*!
2341
    Inserts the image defined by \a format at the current position().
2342
*/
2343
void QTextCursor::insertImage(const QTextImageFormat &format)
2344
31.9k
{
2345
31.9k
    insertText(QString(QChar::ObjectReplacementCharacter), format);
2346
31.9k
}
2347
2348
/*!
2349
    \overload
2350
2351
    Convenience method for inserting the image with the given \a name at the
2352
    current position().
2353
2354
    \snippet code/src_gui_text_qtextcursor.cpp 1
2355
*/
2356
void QTextCursor::insertImage(const QString &name)
2357
0
{
2358
0
    QTextImageFormat format;
2359
0
    format.setName(name);
2360
0
    insertImage(format);
2361
0
}
2362
2363
/*!
2364
    \since 4.5
2365
    \overload
2366
2367
    Convenience function for inserting the given \a image with an optional
2368
    \a name at the current position().
2369
*/
2370
void QTextCursor::insertImage(const QImage &image, const QString &name)
2371
0
{
2372
0
    if (image.isNull()) {
2373
0
        qWarning("QTextCursor::insertImage: attempt to add an invalid image");
2374
0
        return;
2375
0
    }
2376
0
    QString imageName = name;
2377
0
    if (name.isEmpty())
2378
0
        imageName = QString::number(image.cacheKey());
2379
0
    d->priv->document()->addResource(QTextDocument::ImageResource, QUrl(imageName), image);
2380
0
    QTextImageFormat format;
2381
0
    format.setName(imageName);
2382
0
    insertImage(format);
2383
0
}
2384
2385
/*!
2386
    \fn bool QTextCursor::operator!=(const QTextCursor &other) const
2387
2388
    Returns \c true if the \a other cursor is at a different position in
2389
    the document as this cursor; otherwise returns \c false.
2390
*/
2391
bool QTextCursor::operator!=(const QTextCursor &rhs) const
2392
0
{
2393
0
    return !operator==(rhs);
2394
0
}
2395
2396
/*!
2397
    \fn bool QTextCursor::operator<(const QTextCursor &other) const
2398
2399
    Returns \c true if the \a other cursor is positioned later in the
2400
    document than this cursor; otherwise returns \c false.
2401
*/
2402
bool QTextCursor::operator<(const QTextCursor &rhs) const
2403
0
{
2404
0
    if (!d)
2405
0
        return !!rhs.d;
2406
2407
0
    if (!rhs.d)
2408
0
        return false;
2409
2410
0
    Q_ASSERT_X(d->priv == rhs.d->priv, "QTextCursor::operator<", "cannot compare cursors attached to different documents");
2411
2412
0
    return d->position < rhs.d->position;
2413
0
}
2414
2415
/*!
2416
    \fn bool QTextCursor::operator<=(const QTextCursor &other) const
2417
2418
    Returns \c true if the \a other cursor is positioned later or at the
2419
    same position in the document as this cursor; otherwise returns
2420
    false.
2421
*/
2422
bool QTextCursor::operator<=(const QTextCursor &rhs) const
2423
0
{
2424
0
    if (!d)
2425
0
        return true;
2426
2427
0
    if (!rhs.d)
2428
0
        return false;
2429
2430
0
    Q_ASSERT_X(d->priv == rhs.d->priv, "QTextCursor::operator<=", "cannot compare cursors attached to different documents");
2431
2432
0
    return d->position <= rhs.d->position;
2433
0
}
2434
2435
/*!
2436
    \fn bool QTextCursor::operator==(const QTextCursor &other) const
2437
2438
    Returns \c true if the \a other cursor is at the same position in the
2439
    document as this cursor; otherwise returns \c false.
2440
*/
2441
bool QTextCursor::operator==(const QTextCursor &rhs) const
2442
0
{
2443
0
    if (!d)
2444
0
        return !rhs.d;
2445
2446
0
    if (!rhs.d)
2447
0
        return false;
2448
2449
0
    return d->position == rhs.d->position && d->priv == rhs.d->priv;
2450
0
}
2451
2452
/*!
2453
    \fn bool QTextCursor::operator>=(const QTextCursor &other) const
2454
2455
    Returns \c true if the \a other cursor is positioned earlier or at the
2456
    same position in the document as this cursor; otherwise returns
2457
    false.
2458
*/
2459
bool QTextCursor::operator>=(const QTextCursor &rhs) const
2460
0
{
2461
0
    if (!d)
2462
0
        return false;
2463
2464
0
    if (!rhs.d)
2465
0
        return true;
2466
2467
0
    Q_ASSERT_X(d->priv == rhs.d->priv, "QTextCursor::operator>=", "cannot compare cursors attached to different documents");
2468
2469
0
    return d->position >= rhs.d->position;
2470
0
}
2471
2472
/*!
2473
    \fn bool QTextCursor::operator>(const QTextCursor &other) const
2474
2475
    Returns \c true if the \a other cursor is positioned earlier in the
2476
    document than this cursor; otherwise returns \c false.
2477
*/
2478
bool QTextCursor::operator>(const QTextCursor &rhs) const
2479
0
{
2480
0
    if (!d)
2481
0
        return false;
2482
2483
0
    if (!rhs.d)
2484
0
        return true;
2485
2486
0
    Q_ASSERT_X(d->priv == rhs.d->priv, "QTextCursor::operator>", "cannot compare cursors attached to different documents");
2487
2488
0
    return d->position > rhs.d->position;
2489
0
}
2490
2491
/*!
2492
    Indicates the start of a block of editing operations on the
2493
    document that should appear as a single operation from an
2494
    undo/redo point of view.
2495
2496
    For example:
2497
2498
    \snippet code/src_gui_text_qtextcursor.cpp 2
2499
2500
    The call to undo() will cause both insertions to be undone,
2501
    causing both "World" and "Hello" to be removed.
2502
2503
    It is possible to nest calls to beginEditBlock and endEditBlock. The
2504
    top-most pair will determine the scope of the undo/redo operation.
2505
2506
    \sa endEditBlock()
2507
 */
2508
void QTextCursor::beginEditBlock()
2509
919k
{
2510
919k
    if (!d || !d->priv)
2511
0
        return;
2512
2513
919k
    if (d->priv->editBlock == 0) // we are the initial edit block, store current cursor position for undo
2514
898k
        d->priv->editBlockCursorPosition = d->position;
2515
2516
919k
    d->priv->beginEditBlock();
2517
919k
}
2518
2519
/*!
2520
    Like beginEditBlock() indicates the start of a block of editing operations
2521
    that should appear as a single operation for undo/redo. However unlike
2522
    beginEditBlock() it does not start a new block but reverses the previous call to
2523
    endEditBlock() and therefore makes following operations part of the previous edit block created.
2524
2525
    For example:
2526
2527
    \snippet code/src_gui_text_qtextcursor.cpp 3
2528
2529
    The call to undo() will cause all three insertions to be undone.
2530
2531
    \sa beginEditBlock(), endEditBlock()
2532
 */
2533
void QTextCursor::joinPreviousEditBlock()
2534
0
{
2535
0
    if (!d || !d->priv)
2536
0
        return;
2537
2538
0
    d->priv->joinPreviousEditBlock();
2539
0
}
2540
2541
/*!
2542
    Indicates the end of a block of editing operations on the document
2543
    that should appear as a single operation from an undo/redo point
2544
    of view.
2545
2546
    \sa beginEditBlock()
2547
 */
2548
2549
void QTextCursor::endEditBlock()
2550
919k
{
2551
919k
    if (!d || !d->priv)
2552
0
        return;
2553
2554
919k
    d->priv->endEditBlock();
2555
919k
}
2556
2557
/*!
2558
    Returns \c true if this cursor and \a other are copies of each other, i.e.
2559
    one of them was created as a copy of the other and neither has moved since.
2560
    This is much stricter than equality.
2561
2562
    \sa operator=(), operator==()
2563
*/
2564
bool QTextCursor::isCopyOf(const QTextCursor &other) const
2565
0
{
2566
0
    return d == other.d;
2567
0
}
2568
2569
/*!
2570
    \since 4.2
2571
    Returns the number of the block the cursor is in, or 0 if the cursor is invalid.
2572
2573
    Note that this function only makes sense in documents without complex objects such
2574
    as tables or frames.
2575
*/
2576
int QTextCursor::blockNumber() const
2577
0
{
2578
0
    if (!d || !d->priv)
2579
0
        return 0;
2580
2581
0
    return d->block().blockNumber();
2582
0
}
2583
2584
2585
/*!
2586
    \since 4.2
2587
    Returns the position of the cursor within its containing line.
2588
2589
    Note that this is the column number relative to a wrapped line,
2590
    not relative to the block (i.e. the paragraph).
2591
2592
    You probably want to call positionInBlock() instead.
2593
2594
    \sa positionInBlock()
2595
*/
2596
int QTextCursor::columnNumber() const
2597
0
{
2598
0
    if (!d || !d->priv)
2599
0
        return 0;
2600
2601
0
    QTextBlock block = d->block();
2602
0
    if (!block.isValid())
2603
0
        return 0;
2604
2605
0
    const QTextLayout *layout = d->blockLayout(block);
2606
2607
0
    const int relativePos = d->position - block.position();
2608
2609
0
    if (layout->lineCount() == 0)
2610
0
        return relativePos;
2611
2612
0
    QTextLine line = layout->lineForTextPosition(relativePos);
2613
0
    if (!line.isValid())
2614
0
        return 0;
2615
0
    return relativePos - line.textStart();
2616
0
}
2617
2618
/*!
2619
    \since 4.5
2620
    Returns the document this cursor is associated with.
2621
*/
2622
QTextDocument *QTextCursor::document() const
2623
1.29M
{
2624
1.29M
    if (d->priv)
2625
1.29M
        return d->priv->document();
2626
0
    return nullptr; // document went away
2627
1.29M
}
2628
2629
QT_END_NAMESPACE