Coverage Report

Created: 2025-07-12 07:23

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