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