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 
QTextCursorPrivate(QTextDocumentPrivate * p)63 QTextCursorPrivate::QTextCursorPrivate(QTextDocumentPrivate *p)
64     : priv(p), x(0), position(0), anchor(0), adjusted_anchor(0),
65       currentCharFormat(-1), visualNavigation(false), keepPositionOnInsert(false),
66       changed(false)
67 {
68     priv->addCursor(this);
69 }
70 
QTextCursorPrivate(const QTextCursorPrivate & rhs)71 QTextCursorPrivate::QTextCursorPrivate(const QTextCursorPrivate &rhs)
72     : QSharedData(rhs)
73 {
74     position = rhs.position;
75     anchor = rhs.anchor;
76     adjusted_anchor = rhs.adjusted_anchor;
77     priv = rhs.priv;
78     x = rhs.x;
79     currentCharFormat = rhs.currentCharFormat;
80     visualNavigation = rhs.visualNavigation;
81     keepPositionOnInsert = rhs.keepPositionOnInsert;
82     changed = rhs.changed;
83     if (priv != nullptr)
84         priv->addCursor(this);
85 }
86 
~QTextCursorPrivate()87 QTextCursorPrivate::~QTextCursorPrivate()
88 {
89     if (priv)
90         priv->removeCursor(this);
91 }
92 
adjustPosition(int positionOfChange,int charsAddedOrRemoved,QTextUndoCommand::Operation op)93 QTextCursorPrivate::AdjustResult QTextCursorPrivate::adjustPosition(int positionOfChange, int charsAddedOrRemoved, QTextUndoCommand::Operation op)
94 {
95     QTextCursorPrivate::AdjustResult result = QTextCursorPrivate::CursorMoved;
96     // not(!) <= , so that inserting text adjusts the cursor correctly
97     if (position < positionOfChange
98         || (position == positionOfChange
99             && (op == QTextUndoCommand::KeepCursor
100                 || keepPositionOnInsert)
101             )
102          ) {
103         result = CursorUnchanged;
104     } else {
105         if (charsAddedOrRemoved < 0 && position < positionOfChange - charsAddedOrRemoved)
106             position = positionOfChange;
107         else
108             position += charsAddedOrRemoved;
109 
110         currentCharFormat = -1;
111     }
112 
113     if (anchor >= positionOfChange
114         && (anchor != positionOfChange || op != QTextUndoCommand::KeepCursor)) {
115         if (charsAddedOrRemoved < 0 && anchor < positionOfChange - charsAddedOrRemoved)
116             anchor = positionOfChange;
117         else
118             anchor += charsAddedOrRemoved;
119     }
120 
121     if (adjusted_anchor >= positionOfChange
122         && (adjusted_anchor != positionOfChange || op != QTextUndoCommand::KeepCursor)) {
123         if (charsAddedOrRemoved < 0 && adjusted_anchor < positionOfChange - charsAddedOrRemoved)
124             adjusted_anchor = positionOfChange;
125         else
126             adjusted_anchor += charsAddedOrRemoved;
127     }
128 
129     return result;
130 }
131 
setX()132 void QTextCursorPrivate::setX()
133 {
134     if (priv->isInEditBlock() || priv->inContentsChange) {
135         x = -1; // mark dirty
136         return;
137     }
138 
139     QTextBlock block = this->block();
140     const QTextLayout *layout = blockLayout(block);
141     int pos = position - block.position();
142 
143     QTextLine line = layout->lineForTextPosition(pos);
144     if (line.isValid())
145         x = line.cursorToX(pos);
146     else
147         x = -1; // delayed init.  Makes movePosition() call setX later on again.
148 }
149 
remove()150 void QTextCursorPrivate::remove()
151 {
152     if (anchor == position)
153         return;
154     currentCharFormat = -1;
155     int pos1 = position;
156     int pos2 = adjusted_anchor;
157     QTextUndoCommand::Operation op = QTextUndoCommand::KeepCursor;
158     if (pos1 > pos2) {
159         pos1 = adjusted_anchor;
160         pos2 = position;
161         op = QTextUndoCommand::MoveCursor;
162     }
163 
164     // deleting inside table? -> delete only content
165     QTextTable *table = complexSelectionTable();
166     if (table) {
167         priv->beginEditBlock();
168         int startRow, startCol, numRows, numCols;
169         selectedTableCells(&startRow, &numRows, &startCol, &numCols);
170         clearCells(table, startRow, startCol, numRows, numCols, op);
171         adjusted_anchor = anchor = position;
172         priv->endEditBlock();
173     } else {
174         priv->remove(pos1, pos2-pos1, op);
175         adjusted_anchor = anchor = position;
176     }
177 
178 }
179 
clearCells(QTextTable * table,int startRow,int startCol,int numRows,int numCols,QTextUndoCommand::Operation op)180 void QTextCursorPrivate::clearCells(QTextTable *table, int startRow, int startCol, int numRows, int numCols, QTextUndoCommand::Operation op)
181 {
182     priv->beginEditBlock();
183 
184     for (int row = startRow; row < startRow + numRows; ++row)
185         for (int col = startCol; col < startCol + numCols; ++col) {
186             QTextTableCell cell = table->cellAt(row, col);
187             const int startPos = cell.firstPosition();
188             const int endPos = cell.lastPosition();
189             Q_ASSERT(startPos <= endPos);
190             priv->remove(startPos, endPos - startPos, op);
191         }
192 
193     priv->endEditBlock();
194 }
195 
canDelete(int pos) const196 bool QTextCursorPrivate::canDelete(int pos) const
197 {
198     QTextDocumentPrivate::FragmentIterator fit = priv->find(pos);
199     QTextCharFormat fmt = priv->formatCollection()->charFormat((*fit)->format);
200     return (fmt.objectIndex() == -1 || fmt.objectType() == QTextFormat::ImageObject);
201 }
202 
insertBlock(const QTextBlockFormat & format,const QTextCharFormat & charFormat)203 void QTextCursorPrivate::insertBlock(const QTextBlockFormat &format, const QTextCharFormat &charFormat)
204 {
205     QTextFormatCollection *formats = priv->formatCollection();
206     int idx = formats->indexForFormat(format);
207     Q_ASSERT(formats->format(idx).isBlockFormat());
208 
209     priv->insertBlock(position, idx, formats->indexForFormat(charFormat));
210     currentCharFormat = -1;
211 }
212 
adjustCursor(QTextCursor::MoveOperation m)213 void QTextCursorPrivate::adjustCursor(QTextCursor::MoveOperation m)
214 {
215     adjusted_anchor = anchor;
216     if (position == anchor)
217         return;
218 
219     QTextFrame *f_position = priv->frameAt(position);
220     QTextFrame *f_anchor = priv->frameAt(adjusted_anchor);
221 
222     if (f_position != f_anchor) {
223         // find common parent frame
224         QList<QTextFrame *> positionChain;
225         QList<QTextFrame *> anchorChain;
226         QTextFrame *f = f_position;
227         while (f) {
228             positionChain.prepend(f);
229             f = f->parentFrame();
230         }
231         f = f_anchor;
232         while (f) {
233             anchorChain.prepend(f);
234             f = f->parentFrame();
235         }
236         Q_ASSERT(positionChain.at(0) == anchorChain.at(0));
237         int i = 1;
238         int l = qMin(positionChain.size(), anchorChain.size());
239         for (; i < l; ++i) {
240             if (positionChain.at(i) != anchorChain.at(i))
241                 break;
242         }
243 
244         if (m <= QTextCursor::WordLeft) {
245             if (i < positionChain.size())
246                 position = positionChain.at(i)->firstPosition() - 1;
247         } else {
248             if (i < positionChain.size())
249                 position = positionChain.at(i)->lastPosition() + 1;
250         }
251         if (position < adjusted_anchor) {
252             if (i < anchorChain.size())
253                 adjusted_anchor = anchorChain.at(i)->lastPosition() + 1;
254         } else {
255             if (i < anchorChain.size())
256                 adjusted_anchor = anchorChain.at(i)->firstPosition() - 1;
257         }
258 
259         f_position = positionChain.at(i-1);
260     }
261 
262     // same frame, either need to adjust to cell boundaries or return
263     QTextTable *table = qobject_cast<QTextTable *>(f_position);
264     if (!table)
265         return;
266 
267     QTextTableCell c_position = table->cellAt(position);
268     QTextTableCell c_anchor = table->cellAt(adjusted_anchor);
269     if (c_position != c_anchor) {
270         position = c_position.firstPosition();
271         if (position < adjusted_anchor)
272             adjusted_anchor = c_anchor.lastPosition();
273         else
274             adjusted_anchor = c_anchor.firstPosition();
275     }
276     currentCharFormat = -1;
277 }
278 
aboutToRemoveCell(int from,int to)279 void QTextCursorPrivate::aboutToRemoveCell(int from, int to)
280 {
281     Q_ASSERT(from <= to);
282     if (position == anchor)
283         return;
284 
285     QTextTable *t = qobject_cast<QTextTable *>(priv->frameAt(position));
286     if (!t)
287         return;
288     QTextTableCell removedCellFrom = t->cellAt(from);
289     QTextTableCell removedCellEnd = t->cellAt(to);
290     if (! removedCellFrom.isValid() || !removedCellEnd.isValid())
291         return;
292 
293     int curFrom = position;
294     int curTo = adjusted_anchor;
295     if (curTo < curFrom)
296         qSwap(curFrom, curTo);
297 
298     QTextTableCell cellStart = t->cellAt(curFrom);
299     QTextTableCell cellEnd = t->cellAt(curTo);
300 
301     if (cellStart.row() >= removedCellFrom.row() && cellEnd.row() <= removedCellEnd.row()
302             && cellStart.column() >= removedCellFrom.column()
303               && cellEnd.column() <= removedCellEnd.column()) { // selection is completely removed
304         // find a new position, as close as possible to where we were.
305         QTextTableCell cell;
306         if (removedCellFrom.row() == 0 && removedCellEnd.row() == t->rows()-1) // removed n columns
307             cell = t->cellAt(cellStart.row(), removedCellEnd.column()+1);
308         else if (removedCellFrom.column() == 0 && removedCellEnd.column() == t->columns()-1) // removed n rows
309             cell = t->cellAt(removedCellEnd.row() + 1, cellStart.column());
310 
311         int newPosition;
312         if (cell.isValid())
313             newPosition = cell.firstPosition();
314         else
315             newPosition = t->lastPosition()+1;
316 
317         setPosition(newPosition);
318         anchor = newPosition;
319         adjusted_anchor = newPosition;
320         x = 0;
321     }
322     else if (cellStart.row() >= removedCellFrom.row() && cellStart.row() <= removedCellEnd.row()
323         && cellEnd.row() > removedCellEnd.row()) {
324         int newPosition = t->cellAt(removedCellEnd.row() + 1, cellStart.column()).firstPosition();
325         if (position < anchor)
326             position = newPosition;
327         else
328             anchor = adjusted_anchor = newPosition;
329     }
330     else if (cellStart.column() >= removedCellFrom.column() && cellStart.column() <= removedCellEnd.column()
331         && cellEnd.column() > removedCellEnd.column()) {
332         int newPosition = t->cellAt(cellStart.row(), removedCellEnd.column()+1).firstPosition();
333         if (position < anchor)
334             position = newPosition;
335         else
336             anchor = adjusted_anchor = newPosition;
337     }
338 }
339 
movePosition(QTextCursor::MoveOperation op,QTextCursor::MoveMode mode)340 bool QTextCursorPrivate::movePosition(QTextCursor::MoveOperation op, QTextCursor::MoveMode mode)
341 {
342     currentCharFormat = -1;
343     bool adjustX = true;
344     QTextBlock blockIt = block();
345     bool visualMovement = priv->defaultCursorMoveStyle == Qt::VisualMoveStyle;
346 
347     if (!blockIt.isValid())
348         return false;
349 
350     if (blockIt.textDirection() == Qt::RightToLeft) {
351         if (op == QTextCursor::WordLeft)
352             op = QTextCursor::NextWord;
353         else if (op == QTextCursor::WordRight)
354             op = QTextCursor::PreviousWord;
355 
356         if (!visualMovement) {
357             if (op == QTextCursor::Left)
358                 op = QTextCursor::NextCharacter;
359             else if (op == QTextCursor::Right)
360                 op = QTextCursor::PreviousCharacter;
361         }
362     }
363 
364     const QTextLayout *layout = blockLayout(blockIt);
365     int relativePos = position - blockIt.position();
366     QTextLine line;
367     if (!priv->isInEditBlock())
368         line = layout->lineForTextPosition(relativePos);
369 
370     Q_ASSERT(priv->frameAt(position) == priv->frameAt(adjusted_anchor));
371 
372     int newPosition = position;
373 
374     if (mode == QTextCursor::KeepAnchor && complexSelectionTable() != nullptr) {
375         if ((op >= QTextCursor::EndOfLine && op <= QTextCursor::NextWord)
376                 || (op >= QTextCursor::Right && op <= QTextCursor::WordRight)) {
377             QTextTable *t = qobject_cast<QTextTable *>(priv->frameAt(position));
378             Q_ASSERT(t); // as we have already made sure we have a complex selection
379             QTextTableCell cell_pos = t->cellAt(position);
380             if (cell_pos.column() + cell_pos.columnSpan() != t->columns())
381                 op = QTextCursor::NextCell;
382         }
383     }
384 
385     if (x == -1 && !priv->isInEditBlock() && (op == QTextCursor::Up || op == QTextCursor::Down))
386         setX();
387 
388     switch(op) {
389     case QTextCursor::NoMove:
390         return true;
391 
392     case QTextCursor::Start:
393         newPosition = 0;
394         break;
395     case QTextCursor::StartOfLine: {
396         newPosition = blockIt.position();
397         if (line.isValid())
398             newPosition += line.textStart();
399 
400         break;
401     }
402     case QTextCursor::StartOfBlock: {
403         newPosition = blockIt.position();
404         break;
405     }
406     case QTextCursor::PreviousBlock: {
407         if (blockIt == priv->blocksBegin())
408             return false;
409         blockIt = blockIt.previous();
410 
411         newPosition = blockIt.position();
412         break;
413     }
414     case QTextCursor::PreviousCharacter:
415         if (mode == QTextCursor::MoveAnchor && position != adjusted_anchor)
416             newPosition = qMin(position, adjusted_anchor);
417         else
418             newPosition = priv->previousCursorPosition(position, QTextLayout::SkipCharacters);
419         break;
420     case QTextCursor::Left:
421         if (mode == QTextCursor::MoveAnchor && position != adjusted_anchor)
422             newPosition = visualMovement ? qMax(position, adjusted_anchor)
423                                          : qMin(position, adjusted_anchor);
424         else
425             newPosition = visualMovement ? priv->leftCursorPosition(position)
426                                          : priv->previousCursorPosition(position, QTextLayout::SkipCharacters);
427         break;
428     case QTextCursor::StartOfWord: {
429         if (relativePos == 0)
430             break;
431 
432         // skip if already at word start
433         QTextEngine *engine = layout->engine();
434         const QCharAttributes *attributes = engine->attributes();
435         if ((relativePos == blockIt.length() - 1)
436             && (attributes[relativePos - 1].whiteSpace || engine->atWordSeparator(relativePos - 1)))
437             return false;
438 
439         if (relativePos < blockIt.length()-1)
440             ++position;
441 
442         Q_FALLTHROUGH();
443     }
444     case QTextCursor::PreviousWord:
445     case QTextCursor::WordLeft:
446         newPosition = priv->previousCursorPosition(position, QTextLayout::SkipWords);
447         break;
448     case QTextCursor::Up: {
449         int i = line.lineNumber() - 1;
450         if (i == -1) {
451             if (blockIt == priv->blocksBegin())
452                 return false;
453             int blockPosition = blockIt.position();
454             QTextTable *table = qobject_cast<QTextTable *>(priv->frameAt(blockPosition));
455             if (table) {
456                 QTextTableCell cell = table->cellAt(blockPosition);
457                 if (cell.firstPosition() == blockPosition) {
458                     int row = cell.row() - 1;
459                     if (row >= 0) {
460                         blockPosition = table->cellAt(row, cell.column()).lastPosition();
461                     } else {
462                         // move to line above the table
463                         blockPosition = table->firstPosition() - 1;
464                     }
465                     blockIt = priv->blocksFind(blockPosition);
466                 } else {
467                     blockIt = blockIt.previous();
468                 }
469             } else {
470                 blockIt = blockIt.previous();
471             }
472             layout = blockLayout(blockIt);
473             i = layout->lineCount()-1;
474         }
475         if (layout->lineCount()) {
476             QTextLine line = layout->lineAt(i);
477             newPosition = line.xToCursor(x) + blockIt.position();
478         } else {
479             newPosition = blockIt.position();
480         }
481         adjustX = false;
482         break;
483     }
484 
485     case QTextCursor::End:
486         newPosition = priv->length() - 1;
487         break;
488     case QTextCursor::EndOfLine: {
489         if (!line.isValid() || line.textLength() == 0) {
490             if (blockIt.length() >= 1)
491                 // position right before the block separator
492                 newPosition = blockIt.position() + blockIt.length() - 1;
493             break;
494         }
495         newPosition = blockIt.position() + line.textStart() + line.textLength();
496         if (newPosition >= priv->length())
497             newPosition = priv->length() - 1;
498         if (line.lineNumber() < layout->lineCount() - 1) {
499             const QString text = blockIt.text();
500             // ###### this relies on spaces being the cause for linebreaks.
501             // this doesn't work with japanese
502             if (text.at(line.textStart() + line.textLength() - 1).isSpace())
503                 --newPosition;
504         }
505         break;
506     }
507     case QTextCursor::EndOfWord: {
508         QTextEngine *engine = layout->engine();
509         const QCharAttributes *attributes = engine->attributes();
510         const int len = blockIt.length() - 1;
511         if (relativePos >= len)
512             return false;
513         if (engine->atWordSeparator(relativePos)) {
514             ++relativePos;
515             while (relativePos < len && engine->atWordSeparator(relativePos))
516                 ++relativePos;
517         } else {
518             while (relativePos < len && !attributes[relativePos].whiteSpace && !engine->atWordSeparator(relativePos))
519                 ++relativePos;
520         }
521         newPosition = blockIt.position() + relativePos;
522         break;
523     }
524     case QTextCursor::EndOfBlock:
525         if (blockIt.length() >= 1)
526             // position right before the block separator
527             newPosition = blockIt.position() + blockIt.length() - 1;
528         break;
529     case QTextCursor::NextBlock: {
530         blockIt = blockIt.next();
531         if (!blockIt.isValid())
532             return false;
533 
534         newPosition = blockIt.position();
535         break;
536     }
537     case QTextCursor::NextCharacter:
538         if (mode == QTextCursor::MoveAnchor && position != adjusted_anchor)
539             newPosition = qMax(position, adjusted_anchor);
540         else
541             newPosition = priv->nextCursorPosition(position, QTextLayout::SkipCharacters);
542         break;
543     case QTextCursor::Right:
544         if (mode == QTextCursor::MoveAnchor && position != adjusted_anchor)
545             newPosition = visualMovement ? qMin(position, adjusted_anchor)
546                                          : qMax(position, adjusted_anchor);
547         else
548             newPosition = visualMovement ? priv->rightCursorPosition(position)
549                                          : priv->nextCursorPosition(position, QTextLayout::SkipCharacters);
550         break;
551     case QTextCursor::NextWord:
552     case QTextCursor::WordRight:
553         newPosition = priv->nextCursorPosition(position, QTextLayout::SkipWords);
554         break;
555 
556     case QTextCursor::Down: {
557         int i = line.lineNumber() + 1;
558 
559         if (i >= layout->lineCount()) {
560             int blockPosition = blockIt.position() + blockIt.length() - 1;
561             QTextTable *table = qobject_cast<QTextTable *>(priv->frameAt(blockPosition));
562             if (table) {
563                 QTextTableCell cell = table->cellAt(blockPosition);
564                 if (cell.lastPosition() == blockPosition) {
565                     int row = cell.row() + cell.rowSpan();
566                     if (row < table->rows()) {
567                         blockPosition = table->cellAt(row, cell.column()).firstPosition();
568                     } else {
569                         // move to line below the table
570                         blockPosition = table->lastPosition() + 1;
571                     }
572                     blockIt = priv->blocksFind(blockPosition);
573                 } else {
574                     blockIt = blockIt.next();
575                 }
576             } else {
577                 blockIt = blockIt.next();
578             }
579 
580             if (blockIt == priv->blocksEnd())
581                 return false;
582             layout = blockLayout(blockIt);
583             i = 0;
584         }
585         if (layout->lineCount()) {
586             QTextLine line = layout->lineAt(i);
587             newPosition = line.xToCursor(x) + blockIt.position();
588         } else {
589             newPosition = blockIt.position();
590         }
591         adjustX = false;
592         break;
593     }
594     case QTextCursor::NextCell:
595     case QTextCursor::PreviousCell:
596     case QTextCursor::NextRow:
597     case QTextCursor::PreviousRow: {
598         QTextTable *table = qobject_cast<QTextTable *>(priv->frameAt(position));
599         if (!table)
600             return false;
601 
602         QTextTableCell cell = table->cellAt(position);
603         Q_ASSERT(cell.isValid());
604         int column = cell.column();
605         int row = cell.row();
606         const int currentRow = row;
607         if (op == QTextCursor::NextCell || op == QTextCursor::NextRow) {
608             do {
609                 column += cell.columnSpan();
610                 if (column >= table->columns()) {
611                     column = 0;
612                     ++row;
613                 }
614                 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             } while (cell.isValid()
617                     && ((op == QTextCursor::NextRow && currentRow == cell.row())
618                         || cell.row() < row));
619         }
620         else if (op == QTextCursor::PreviousCell || op == QTextCursor::PreviousRow) {
621             do {
622                 --column;
623                 if (column < 0) {
624                     column = table->columns()-1;
625                     --row;
626                 }
627                 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             } while (cell.isValid()
630                     && ((op == QTextCursor::PreviousRow && currentRow == cell.row())
631                         || cell.row() < row));
632         }
633         if (cell.isValid())
634             newPosition = cell.firstPosition();
635         break;
636     }
637     }
638 
639     if (mode == QTextCursor::KeepAnchor) {
640         QTextTable *table = qobject_cast<QTextTable *>(priv->frameAt(position));
641         if (table && ((op >= QTextCursor::PreviousBlock && op <= QTextCursor::WordLeft)
642                       || (op >= QTextCursor::NextBlock && op <= QTextCursor::WordRight))) {
643             int oldColumn = table->cellAt(position).column();
644 
645             const QTextTableCell otherCell = table->cellAt(newPosition);
646             if (!otherCell.isValid())
647                 return false;
648 
649             int newColumn = otherCell.column();
650             if ((oldColumn > newColumn && op >= QTextCursor::End)
651                 || (oldColumn < newColumn && op <= QTextCursor::WordLeft))
652                 return false;
653         }
654     }
655 
656     const bool moved = setPosition(newPosition);
657 
658     if (mode == QTextCursor::MoveAnchor) {
659         anchor = position;
660         adjusted_anchor = position;
661     } else {
662         adjustCursor(op);
663     }
664 
665     if (adjustX)
666         setX();
667 
668     return moved;
669 }
670 
complexSelectionTable() const671 QTextTable *QTextCursorPrivate::complexSelectionTable() const
672 {
673     if (position == anchor)
674         return nullptr;
675 
676     QTextTable *t = qobject_cast<QTextTable *>(priv->frameAt(position));
677     if (t) {
678         QTextTableCell cell_pos = t->cellAt(position);
679         QTextTableCell cell_anchor = t->cellAt(adjusted_anchor);
680 
681         Q_ASSERT(cell_anchor.isValid());
682 
683         if (cell_pos == cell_anchor)
684             t = nullptr;
685     }
686     return t;
687 }
688 
selectedTableCells(int * firstRow,int * numRows,int * firstColumn,int * numColumns) const689 void QTextCursorPrivate::selectedTableCells(int *firstRow, int *numRows, int *firstColumn, int *numColumns) const
690 {
691     *firstRow = -1;
692     *firstColumn = -1;
693     *numRows = -1;
694     *numColumns = -1;
695 
696     if (position == anchor)
697         return;
698 
699     QTextTable *t = qobject_cast<QTextTable *>(priv->frameAt(position));
700     if (!t)
701         return;
702 
703     QTextTableCell cell_pos = t->cellAt(position);
704     QTextTableCell cell_anchor = t->cellAt(adjusted_anchor);
705 
706     Q_ASSERT(cell_anchor.isValid());
707 
708     if (cell_pos == cell_anchor)
709         return;
710 
711     *firstRow = qMin(cell_pos.row(), cell_anchor.row());
712     *firstColumn = qMin(cell_pos.column(), cell_anchor.column());
713     *numRows = qMax(cell_pos.row() + cell_pos.rowSpan(), cell_anchor.row() + cell_anchor.rowSpan()) - *firstRow;
714     *numColumns = qMax(cell_pos.column() + cell_pos.columnSpan(), cell_anchor.column() + cell_anchor.columnSpan()) - *firstColumn;
715 }
716 
setBlockCharFormatHelper(QTextDocumentPrivate * priv,int pos1,int pos2,const QTextCharFormat & format,QTextDocumentPrivate::FormatChangeMode changeMode)717 static void setBlockCharFormatHelper(QTextDocumentPrivate *priv, int pos1, int pos2,
718                                const QTextCharFormat &format, QTextDocumentPrivate::FormatChangeMode changeMode)
719 {
720     QTextBlock it = priv->blocksFind(pos1);
721     QTextBlock end = priv->blocksFind(pos2);
722     if (end.isValid())
723         end = end.next();
724 
725     for (; it != end; it = it.next()) {
726         priv->setCharFormat(it.position() - 1, 1, format, changeMode);
727     }
728 }
729 
setBlockCharFormat(const QTextCharFormat & _format,QTextDocumentPrivate::FormatChangeMode changeMode)730 void QTextCursorPrivate::setBlockCharFormat(const QTextCharFormat &_format,
731     QTextDocumentPrivate::FormatChangeMode changeMode)
732 {
733     priv->beginEditBlock();
734 
735     QTextCharFormat format = _format;
736     format.clearProperty(QTextFormat::ObjectIndex);
737 
738     QTextTable *table = complexSelectionTable();
739     if (table) {
740         int row_start, col_start, num_rows, num_cols;
741         selectedTableCells(&row_start, &num_rows, &col_start, &num_cols);
742 
743         Q_ASSERT(row_start != -1);
744         for (int r = row_start; r < row_start + num_rows; ++r) {
745             for (int c = col_start; c < col_start + num_cols; ++c) {
746                 QTextTableCell cell = table->cellAt(r, c);
747                 int rspan = cell.rowSpan();
748                 int cspan = cell.columnSpan();
749                 if (rspan != 1) {
750                     int cr = cell.row();
751                     if (cr != r)
752                         continue;
753                 }
754                 if (cspan != 1) {
755                     int cc = cell.column();
756                     if (cc != c)
757                         continue;
758                 }
759 
760                 int pos1 = cell.firstPosition();
761                 int pos2 = cell.lastPosition();
762                 setBlockCharFormatHelper(priv, pos1, pos2, format, changeMode);
763             }
764         }
765     } else {
766         int pos1 = position;
767         int pos2 = adjusted_anchor;
768         if (pos1 > pos2) {
769             pos1 = adjusted_anchor;
770             pos2 = position;
771         }
772 
773         setBlockCharFormatHelper(priv, pos1, pos2, format, changeMode);
774     }
775     priv->endEditBlock();
776 }
777 
778 
setBlockFormat(const QTextBlockFormat & format,QTextDocumentPrivate::FormatChangeMode changeMode)779 void QTextCursorPrivate::setBlockFormat(const QTextBlockFormat &format, QTextDocumentPrivate::FormatChangeMode changeMode)
780 {
781     QTextTable *table = complexSelectionTable();
782     if (table) {
783         priv->beginEditBlock();
784         int row_start, col_start, num_rows, num_cols;
785         selectedTableCells(&row_start, &num_rows, &col_start, &num_cols);
786 
787         Q_ASSERT(row_start != -1);
788         for (int r = row_start; r < row_start + num_rows; ++r) {
789             for (int c = col_start; c < col_start + num_cols; ++c) {
790                 QTextTableCell cell = table->cellAt(r, c);
791                 int rspan = cell.rowSpan();
792                 int cspan = cell.columnSpan();
793                 if (rspan != 1) {
794                     int cr = cell.row();
795                     if (cr != r)
796                         continue;
797                 }
798                 if (cspan != 1) {
799                     int cc = cell.column();
800                     if (cc != c)
801                         continue;
802                 }
803 
804                 int pos1 = cell.firstPosition();
805                 int pos2 = cell.lastPosition();
806                 priv->setBlockFormat(priv->blocksFind(pos1), priv->blocksFind(pos2), format, changeMode);
807             }
808         }
809         priv->endEditBlock();
810     } else {
811         int pos1 = position;
812         int pos2 = adjusted_anchor;
813         if (pos1 > pos2) {
814             pos1 = adjusted_anchor;
815             pos2 = position;
816         }
817 
818         priv->setBlockFormat(priv->blocksFind(pos1), priv->blocksFind(pos2), format, changeMode);
819     }
820 }
821 
setCharFormat(const QTextCharFormat & _format,QTextDocumentPrivate::FormatChangeMode changeMode)822 void QTextCursorPrivate::setCharFormat(const QTextCharFormat &_format, QTextDocumentPrivate::FormatChangeMode changeMode)
823 {
824     Q_ASSERT(position != anchor);
825 
826     QTextCharFormat format = _format;
827     format.clearProperty(QTextFormat::ObjectIndex);
828 
829     QTextTable *table = complexSelectionTable();
830     if (table) {
831         priv->beginEditBlock();
832         int row_start, col_start, num_rows, num_cols;
833         selectedTableCells(&row_start, &num_rows, &col_start, &num_cols);
834 
835         Q_ASSERT(row_start != -1);
836         for (int r = row_start; r < row_start + num_rows; ++r) {
837             for (int c = col_start; c < col_start + num_cols; ++c) {
838                 QTextTableCell cell = table->cellAt(r, c);
839                 int rspan = cell.rowSpan();
840                 int cspan = cell.columnSpan();
841                 if (rspan != 1) {
842                     int cr = cell.row();
843                     if (cr != r)
844                         continue;
845                 }
846                 if (cspan != 1) {
847                     int cc = cell.column();
848                     if (cc != c)
849                         continue;
850                 }
851 
852                 int pos1 = cell.firstPosition();
853                 int pos2 = cell.lastPosition();
854                 priv->setCharFormat(pos1, pos2-pos1, format, changeMode);
855             }
856         }
857         priv->endEditBlock();
858     } else {
859         int pos1 = position;
860         int pos2 = adjusted_anchor;
861         if (pos1 > pos2) {
862             pos1 = adjusted_anchor;
863             pos2 = position;
864         }
865 
866         priv->setCharFormat(pos1, pos2-pos1, format, changeMode);
867     }
868 }
869 
870 
blockLayout(QTextBlock & block) const871 QTextLayout *QTextCursorPrivate::blockLayout(QTextBlock &block) const{
872     QTextLayout *tl = block.layout();
873     if (!tl->lineCount() && priv->layout())
874         priv->layout()->blockBoundingRect(block);
875     return tl;
876 }
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  */
QTextCursor()1046 QTextCursor::QTextCursor()
1047     : d(nullptr)
1048 {
1049 }
1050 
1051 /*!
1052     Constructs a cursor pointing to the beginning of the \a document.
1053  */
QTextCursor(QTextDocument * document)1054 QTextCursor::QTextCursor(QTextDocument *document)
1055     : d(new QTextCursorPrivate(document->docHandle()))
1056 {
1057 }
1058 
1059 /*!
1060     Constructs a cursor pointing to the beginning of the \a frame.
1061 */
QTextCursor(QTextFrame * frame)1062 QTextCursor::QTextCursor(QTextFrame *frame)
1063     : d(new QTextCursorPrivate(frame->document()->docHandle()))
1064 {
1065     d->adjusted_anchor = d->anchor = d->position = frame->firstPosition();
1066 }
1067 
1068 
1069 /*!
1070     Constructs a cursor pointing to the beginning of the \a block.
1071 */
QTextCursor(const QTextBlock & block)1072 QTextCursor::QTextCursor(const QTextBlock &block)
1073     : d(new QTextCursorPrivate(block.docHandle()))
1074 {
1075     d->adjusted_anchor = d->anchor = d->position = block.position();
1076 }
1077 
1078 
1079 /*!
1080   \internal
1081  */
QTextCursor(QTextDocumentPrivate * p,int pos)1082 QTextCursor::QTextCursor(QTextDocumentPrivate *p, int pos)
1083     : d(new QTextCursorPrivate(p))
1084 {
1085     d->adjusted_anchor = d->anchor = d->position = pos;
1086 
1087     d->setX();
1088 }
1089 
1090 /*!
1091     \internal
1092 */
QTextCursor(QTextCursorPrivate * d)1093 QTextCursor::QTextCursor(QTextCursorPrivate *d)
1094 {
1095     Q_ASSERT(d);
1096     this->d = d;
1097 }
1098 
1099 /*!
1100     Constructs a new cursor that is a copy of \a cursor.
1101  */
QTextCursor(const QTextCursor & cursor)1102 QTextCursor::QTextCursor(const QTextCursor &cursor)
1103 {
1104     d = cursor.d;
1105 }
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  */
operator =(const QTextCursor & cursor)1113 QTextCursor &QTextCursor::operator=(const QTextCursor &cursor)
1114 {
1115     d = cursor.d;
1116     return *this;
1117 }
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  */
~QTextCursor()1130 QTextCursor::~QTextCursor()
1131 {
1132 }
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  */
isNull() const1138 bool QTextCursor::isNull() const
1139 {
1140     return !d || !d->priv;
1141 }
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 */
setPosition(int pos,MoveMode m)1159 void QTextCursor::setPosition(int pos, MoveMode m)
1160 {
1161     if (!d || !d->priv)
1162         return;
1163 
1164     if (pos < 0 || pos >= d->priv->length()) {
1165         qWarning("QTextCursor::setPosition: Position '%d' out of range", pos);
1166         return;
1167     }
1168 
1169     d->setPosition(pos);
1170     if (m == MoveAnchor) {
1171         d->anchor = pos;
1172         d->adjusted_anchor = pos;
1173     } else { // keep anchor
1174         QTextCursor::MoveOperation op;
1175         if (pos < d->anchor)
1176             op = QTextCursor::Left;
1177         else
1178             op = QTextCursor::Right;
1179         d->adjustCursor(op);
1180     }
1181     d->setX();
1182 }
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 */
position() const1197 int QTextCursor::position() const
1198 {
1199     if (!d || !d->priv)
1200         return -1;
1201     return d->position;
1202 }
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 */
positionInBlock() const1220 int QTextCursor::positionInBlock() const
1221 {
1222     if (!d || !d->priv)
1223         return 0;
1224     return d->position - d->block().position();
1225 }
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 */
anchor() const1235 int QTextCursor::anchor() const
1236 {
1237     if (!d || !d->priv)
1238         return -1;
1239     return d->anchor;
1240 }
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 */
movePosition(MoveOperation op,MoveMode mode,int n)1260 bool QTextCursor::movePosition(MoveOperation op, MoveMode mode, int n)
1261 {
1262     if (!d || !d->priv)
1263         return false;
1264     switch (op) {
1265     case Start:
1266     case StartOfLine:
1267     case End:
1268     case EndOfLine:
1269         n = 1;
1270         break;
1271     default: break;
1272     }
1273 
1274     int previousPosition = d->position;
1275     for (; n > 0; --n) {
1276         if (!d->movePosition(op, mode))
1277             return false;
1278     }
1279 
1280     if (d->visualNavigation && !d->block().isVisible()) {
1281         QTextBlock b = d->block();
1282         if (previousPosition < d->position) {
1283             while (!b.next().isVisible())
1284                 b = b.next();
1285             d->setPosition(b.position() + b.length() - 1);
1286         } else {
1287             while (!b.previous().isVisible())
1288                 b = b.previous();
1289             d->setPosition(b.position());
1290         }
1291         if (mode == QTextCursor::MoveAnchor)
1292             d->anchor = d->position;
1293         while (d->movePosition(op, mode)
1294                && !d->block().isVisible())
1295             ;
1296 
1297     }
1298     return true;
1299 }
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  */
visualNavigation() const1312 bool QTextCursor::visualNavigation() const
1313 {
1314     return d ? d->visualNavigation : false;
1315 }
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  */
setVisualNavigation(bool b)1327 void QTextCursor::setVisualNavigation(bool b)
1328 {
1329     if (d)
1330         d->visualNavigation = b;
1331 }
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   */
setVerticalMovementX(int x)1348 void QTextCursor::setVerticalMovementX(int x)
1349 {
1350     if (d)
1351         d->x = x;
1352 }
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   */
verticalMovementX() const1363 int QTextCursor::verticalMovementX() const
1364 {
1365     return d ? d->x : -1;
1366 }
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  */
keepPositionOnInsert() const1378 bool QTextCursor::keepPositionOnInsert() const
1379 {
1380     return d ? d->keepPositionOnInsert : false;
1381 }
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  */
setKeepPositionOnInsert(bool b)1399 void QTextCursor::setKeepPositionOnInsert(bool b)
1400 {
1401     if (d)
1402         d->keepPositionOnInsert = b;
1403 }
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 */
insertText(const QString & text)1423 void QTextCursor::insertText(const QString &text)
1424 {
1425     QTextCharFormat fmt = charFormat();
1426     fmt.clearProperty(QTextFormat::ObjectType);
1427     insertText(text, fmt);
1428 }
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 */
insertText(const QString & text,const QTextCharFormat & _format)1436 void QTextCursor::insertText(const QString &text, const QTextCharFormat &_format)
1437 {
1438     if (!d || !d->priv)
1439         return;
1440 
1441     Q_ASSERT(_format.isValid());
1442 
1443     QTextCharFormat format = _format;
1444     format.clearProperty(QTextFormat::ObjectIndex);
1445 
1446     bool hasEditBlock = false;
1447 
1448     if (d->anchor != d->position) {
1449         hasEditBlock = true;
1450         d->priv->beginEditBlock();
1451         d->remove();
1452     }
1453 
1454     if (!text.isEmpty()) {
1455         QTextFormatCollection *formats = d->priv->formatCollection();
1456         int formatIdx = formats->indexForFormat(format);
1457         Q_ASSERT(formats->format(formatIdx).isCharFormat());
1458 
1459         QTextBlockFormat blockFmt = blockFormat();
1460 
1461 
1462         int textStart = d->priv->text.length();
1463         int blockStart = 0;
1464         d->priv->text += text;
1465         int textEnd = d->priv->text.length();
1466 
1467         for (int i = 0; i < text.length(); ++i) {
1468             QChar ch = text.at(i);
1469 
1470             const int blockEnd = i;
1471 
1472             if (ch == QLatin1Char('\r')
1473                 && (i + 1) < text.length()
1474                 && text.at(i + 1) == QLatin1Char('\n')) {
1475                 ++i;
1476                 ch = text.at(i);
1477             }
1478 
1479             if (ch == QLatin1Char('\n')
1480                 || ch == QChar::ParagraphSeparator
1481                 || ch == QTextBeginningOfFrame
1482                 || ch == QTextEndOfFrame
1483                 || ch == QLatin1Char('\r')) {
1484 
1485                 if (!hasEditBlock) {
1486                     hasEditBlock = true;
1487                     d->priv->beginEditBlock();
1488                 }
1489 
1490                 if (blockEnd > blockStart)
1491                     d->priv->insert(d->position, textStart + blockStart, blockEnd - blockStart, formatIdx);
1492 
1493                 d->insertBlock(blockFmt, format);
1494                 blockStart = i + 1;
1495             }
1496         }
1497         if (textStart + blockStart < textEnd)
1498             d->priv->insert(d->position, textStart + blockStart, textEnd - textStart - blockStart, formatIdx);
1499     }
1500     if (hasEditBlock)
1501         d->priv->endEditBlock();
1502     d->setX();
1503 }
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 */
deleteChar()1511 void QTextCursor::deleteChar()
1512 {
1513     if (!d || !d->priv)
1514         return;
1515 
1516     if (d->position != d->anchor) {
1517         removeSelectedText();
1518         return;
1519     }
1520 
1521     if (!d->canDelete(d->position))
1522         return;
1523     d->adjusted_anchor = d->anchor =
1524                          d->priv->nextCursorPosition(d->anchor, QTextLayout::SkipCharacters);
1525     d->remove();
1526     d->setX();
1527 }
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 */
deletePreviousChar()1535 void QTextCursor::deletePreviousChar()
1536 {
1537     if (!d || !d->priv)
1538         return;
1539 
1540     if (d->position != d->anchor) {
1541         removeSelectedText();
1542         return;
1543     }
1544 
1545     if (d->anchor < 1 || !d->canDelete(d->anchor-1))
1546         return;
1547     d->anchor--;
1548 
1549     QTextDocumentPrivate::FragmentIterator fragIt = d->priv->find(d->anchor);
1550     const QTextFragmentData * const frag = fragIt.value();
1551     int fpos = fragIt.position();
1552     QChar uc = d->priv->buffer().at(d->anchor - fpos + frag->stringPosition);
1553     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         uc = d->priv->buffer().at(d->anchor - 1 - fpos + frag->stringPosition);
1557         if (uc.isHighSurrogate())
1558             --d->anchor;
1559     }
1560 
1561     d->adjusted_anchor = d->anchor;
1562     d->remove();
1563     d->setX();
1564 }
1565 
1566 /*!
1567     Selects text in the document according to the given \a selection.
1568 */
select(SelectionType selection)1569 void QTextCursor::select(SelectionType selection)
1570 {
1571     if (!d || !d->priv)
1572         return;
1573 
1574     clearSelection();
1575 
1576     const QTextBlock block = d->block();
1577 
1578     switch (selection) {
1579         case LineUnderCursor:
1580             movePosition(StartOfLine);
1581             movePosition(EndOfLine, KeepAnchor);
1582             break;
1583         case WordUnderCursor:
1584             movePosition(StartOfWord);
1585             movePosition(EndOfWord, KeepAnchor);
1586             break;
1587         case BlockUnderCursor:
1588             if (block.length() == 1) // no content
1589                 break;
1590             movePosition(StartOfBlock);
1591             // also select the paragraph separator
1592             if (movePosition(PreviousBlock)) {
1593                 movePosition(EndOfBlock);
1594                 movePosition(NextBlock, KeepAnchor);
1595             }
1596             movePosition(EndOfBlock, KeepAnchor);
1597             break;
1598         case Document:
1599             movePosition(Start);
1600             movePosition(End, KeepAnchor);
1601             break;
1602     }
1603 }
1604 
1605 /*!
1606     Returns \c true if the cursor contains a selection; otherwise returns \c false.
1607 */
hasSelection() const1608 bool QTextCursor::hasSelection() const
1609 {
1610     return !!d && d->position != d->anchor;
1611 }
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 */
hasComplexSelection() const1621 bool QTextCursor::hasComplexSelection() const
1622 {
1623     if (!d)
1624         return false;
1625 
1626     return d->complexSelectionTable() != nullptr;
1627 }
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 */
selectedTableCells(int * firstRow,int * numRows,int * firstColumn,int * numColumns) const1637 void QTextCursor::selectedTableCells(int *firstRow, int *numRows, int *firstColumn, int *numColumns) const
1638 {
1639     *firstRow = -1;
1640     *firstColumn = -1;
1641     *numRows = -1;
1642     *numColumns = -1;
1643 
1644     if (!d || d->position == d->anchor)
1645         return;
1646 
1647     d->selectedTableCells(firstRow, numRows, firstColumn, numColumns);
1648 }
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 */
clearSelection()1658 void QTextCursor::clearSelection()
1659 {
1660     if (!d)
1661         return;
1662     d->adjusted_anchor = d->anchor = d->position;
1663     d->currentCharFormat = -1;
1664 }
1665 
1666 /*!
1667     If there is a selection, its content is deleted; otherwise does
1668     nothing.
1669 
1670     \sa hasSelection()
1671 */
removeSelectedText()1672 void QTextCursor::removeSelectedText()
1673 {
1674     if (!d || !d->priv || d->position == d->anchor)
1675         return;
1676 
1677     d->priv->beginEditBlock();
1678     d->remove();
1679     d->priv->endEditBlock();
1680     d->setX();
1681 }
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 */
selectionStart() const1689 int QTextCursor::selectionStart() const
1690 {
1691     if (!d || !d->priv)
1692         return -1;
1693     return qMin(d->position, d->adjusted_anchor);
1694 }
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 */
selectionEnd() const1702 int QTextCursor::selectionEnd() const
1703 {
1704     if (!d || !d->priv)
1705         return -1;
1706     return qMax(d->position, d->adjusted_anchor);
1707 }
1708 
getText(QString & text,QTextDocumentPrivate * priv,const QString & docText,int pos,int end)1709 static void getText(QString &text, QTextDocumentPrivate *priv, const QString &docText, int pos, int end)
1710 {
1711     while (pos < end) {
1712         QTextDocumentPrivate::FragmentIterator fragIt = priv->find(pos);
1713         const QTextFragmentData * const frag = fragIt.value();
1714 
1715         const int offsetInFragment = qMax(0, pos - fragIt.position());
1716         const int len = qMin(int(frag->size_array[0] - offsetInFragment), end - pos);
1717 
1718         text += QString(docText.constData() + frag->stringPosition + offsetInFragment, len);
1719         pos += len;
1720     }
1721 }
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 */
selectedText() const1734 QString QTextCursor::selectedText() const
1735 {
1736     if (!d || !d->priv || d->position == d->anchor)
1737         return QString();
1738 
1739     const QString docText = d->priv->buffer();
1740     QString text;
1741 
1742     QTextTable *table = d->complexSelectionTable();
1743     if (table) {
1744         int row_start, col_start, num_rows, num_cols;
1745         selectedTableCells(&row_start, &num_rows, &col_start, &num_cols);
1746 
1747         Q_ASSERT(row_start != -1);
1748         for (int r = row_start; r < row_start + num_rows; ++r) {
1749             for (int c = col_start; c < col_start + num_cols; ++c) {
1750                 QTextTableCell cell = table->cellAt(r, c);
1751                 int rspan = cell.rowSpan();
1752                 int cspan = cell.columnSpan();
1753                 if (rspan != 1) {
1754                     int cr = cell.row();
1755                     if (cr != r)
1756                         continue;
1757                 }
1758                 if (cspan != 1) {
1759                     int cc = cell.column();
1760                     if (cc != c)
1761                         continue;
1762                 }
1763 
1764                 getText(text, d->priv, docText, cell.firstPosition(), cell.lastPosition());
1765             }
1766         }
1767     } else {
1768         getText(text, d->priv, docText, selectionStart(), selectionEnd());
1769     }
1770 
1771     return text;
1772 }
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 */
selection() const1785 QTextDocumentFragment QTextCursor::selection() const
1786 {
1787     return QTextDocumentFragment(*this);
1788 }
1789 
1790 /*!
1791     Returns the block that contains the cursor.
1792 */
block() const1793 QTextBlock QTextCursor::block() const
1794 {
1795     if (!d || !d->priv)
1796         return QTextBlock();
1797     return d->block();
1798 }
1799 
1800 /*!
1801     Returns the block format of the block the cursor is in.
1802 
1803     \sa setBlockFormat(), charFormat()
1804  */
blockFormat() const1805 QTextBlockFormat QTextCursor::blockFormat() const
1806 {
1807     if (!d || !d->priv)
1808         return QTextBlockFormat();
1809 
1810     return d->block().blockFormat();
1811 }
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 */
setBlockFormat(const QTextBlockFormat & format)1819 void QTextCursor::setBlockFormat(const QTextBlockFormat &format)
1820 {
1821     if (!d || !d->priv)
1822         return;
1823 
1824     d->setBlockFormat(format, QTextDocumentPrivate::SetFormat);
1825 }
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 */
mergeBlockFormat(const QTextBlockFormat & modifier)1834 void QTextCursor::mergeBlockFormat(const QTextBlockFormat &modifier)
1835 {
1836     if (!d || !d->priv)
1837         return;
1838 
1839     d->setBlockFormat(modifier, QTextDocumentPrivate::MergeFormat);
1840 }
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  */
blockCharFormat() const1850 QTextCharFormat QTextCursor::blockCharFormat() const
1851 {
1852     if (!d || !d->priv)
1853         return QTextCharFormat();
1854 
1855     return d->block().charFormat();
1856 }
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 */
setBlockCharFormat(const QTextCharFormat & format)1864 void QTextCursor::setBlockCharFormat(const QTextCharFormat &format)
1865 {
1866     if (!d || !d->priv)
1867         return;
1868 
1869     d->setBlockCharFormat(format, QTextDocumentPrivate::SetFormatAndPreserveObjectIndices);
1870 }
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 */
mergeBlockCharFormat(const QTextCharFormat & modifier)1879 void QTextCursor::mergeBlockCharFormat(const QTextCharFormat &modifier)
1880 {
1881     if (!d || !d->priv)
1882         return;
1883 
1884     d->setBlockCharFormat(modifier, QTextDocumentPrivate::MergeFormat);
1885 }
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  */
charFormat() const1895 QTextCharFormat QTextCursor::charFormat() const
1896 {
1897     if (!d || !d->priv)
1898         return QTextCharFormat();
1899 
1900     int idx = d->currentCharFormat;
1901     if (idx == -1) {
1902         QTextBlock block = d->block();
1903 
1904         int pos;
1905         if (d->position == block.position()
1906             && block.length() > 1)
1907             pos = d->position;
1908         else
1909             pos = d->position - 1;
1910 
1911         if (pos == -1) {
1912             idx = d->priv->blockCharFormatIndex(d->priv->blockMap().firstNode());
1913         } else {
1914             Q_ASSERT(pos >= 0 && pos < d->priv->length());
1915 
1916             QTextDocumentPrivate::FragmentIterator it = d->priv->find(pos);
1917             Q_ASSERT(!it.atEnd());
1918             idx = it.value()->format;
1919         }
1920     }
1921 
1922     QTextCharFormat cfmt = d->priv->formatCollection()->charFormat(idx);
1923     cfmt.clearProperty(QTextFormat::ObjectIndex);
1924 
1925     Q_ASSERT(cfmt.isValid());
1926     return cfmt;
1927 }
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 */
setCharFormat(const QTextCharFormat & format)1936 void QTextCursor::setCharFormat(const QTextCharFormat &format)
1937 {
1938     if (!d || !d->priv)
1939         return;
1940     if (d->position == d->anchor) {
1941         d->currentCharFormat = d->priv->formatCollection()->indexForFormat(format);
1942         return;
1943     }
1944     d->setCharFormat(format, QTextDocumentPrivate::SetFormatAndPreserveObjectIndices);
1945 }
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 */
mergeCharFormat(const QTextCharFormat & modifier)1955 void QTextCursor::mergeCharFormat(const QTextCharFormat &modifier)
1956 {
1957     if (!d || !d->priv)
1958         return;
1959     if (d->position == d->anchor) {
1960         QTextCharFormat format = charFormat();
1961         format.merge(modifier);
1962         d->currentCharFormat = d->priv->formatCollection()->indexForFormat(format);
1963         return;
1964     }
1965 
1966     d->setCharFormat(modifier, QTextDocumentPrivate::MergeFormat);
1967 }
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 */
atBlockStart() const1975 bool QTextCursor::atBlockStart() const
1976 {
1977     if (!d || !d->priv)
1978         return false;
1979 
1980     return d->position == d->block().position();
1981 }
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 */
atBlockEnd() const1989 bool QTextCursor::atBlockEnd() const
1990 {
1991     if (!d || !d->priv)
1992         return false;
1993 
1994     return d->position == d->block().position() + d->block().length() - 1;
1995 }
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 */
atStart() const2003 bool QTextCursor::atStart() const
2004 {
2005     if (!d || !d->priv)
2006         return false;
2007 
2008     return d->position == 0;
2009 }
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 */
atEnd() const2019 bool QTextCursor::atEnd() const
2020 {
2021     if (!d || !d->priv)
2022         return false;
2023 
2024     return d->position == d->priv->length() - 1;
2025 }
2026 
2027 /*!
2028     Inserts a new empty block at the cursor position() with the
2029     current blockFormat() and charFormat().
2030 
2031     \sa setBlockFormat()
2032 */
insertBlock()2033 void QTextCursor::insertBlock()
2034 {
2035     insertBlock(blockFormat());
2036 }
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 */
insertBlock(const QTextBlockFormat & format)2046 void QTextCursor::insertBlock(const QTextBlockFormat &format)
2047 {
2048     QTextCharFormat charFmt = charFormat();
2049     charFmt.clearProperty(QTextFormat::ObjectType);
2050     insertBlock(format, charFmt);
2051 }
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 */
insertBlock(const QTextBlockFormat & format,const QTextCharFormat & _charFormat)2062 void QTextCursor::insertBlock(const QTextBlockFormat &format, const QTextCharFormat &_charFormat)
2063 {
2064     if (!d || !d->priv)
2065         return;
2066 
2067     QTextCharFormat charFormat = _charFormat;
2068     charFormat.clearProperty(QTextFormat::ObjectIndex);
2069 
2070     d->priv->beginEditBlock();
2071     d->remove();
2072     d->insertBlock(format, charFormat);
2073     d->priv->endEditBlock();
2074     d->setX();
2075 }
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  */
insertList(const QTextListFormat & format)2084 QTextList *QTextCursor::insertList(const QTextListFormat &format)
2085 {
2086     insertBlock();
2087     return createList(format);
2088 }
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  */
insertList(QTextListFormat::Style style)2099 QTextList *QTextCursor::insertList(QTextListFormat::Style style)
2100 {
2101     insertBlock();
2102     return createList(style);
2103 }
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  */
createList(const QTextListFormat & format)2111 QTextList *QTextCursor::createList(const QTextListFormat &format)
2112 {
2113     if (!d || !d->priv)
2114         return nullptr;
2115 
2116     QTextList *list = static_cast<QTextList *>(d->priv->createObject(format));
2117     QTextBlockFormat modifier;
2118     modifier.setObjectIndex(list->objectIndex());
2119     mergeBlockFormat(modifier);
2120     return list;
2121 }
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  */
createList(QTextListFormat::Style style)2133 QTextList *QTextCursor::createList(QTextListFormat::Style style)
2134 {
2135     QTextListFormat fmt;
2136     fmt.setStyle(style);
2137     return createList(fmt);
2138 }
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  */
currentList() const2146 QTextList *QTextCursor::currentList() const
2147 {
2148     if (!d || !d->priv)
2149         return nullptr;
2150 
2151     QTextBlockFormat b = blockFormat();
2152     QTextObject *o = d->priv->objectForFormat(b);
2153     return qobject_cast<QTextList *>(o);
2154 }
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  */
insertTable(int rows,int cols)2169 QTextTable *QTextCursor::insertTable(int rows, int cols)
2170 {
2171     return insertTable(rows, cols, QTextTableFormat());
2172 }
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 */
insertTable(int rows,int cols,const QTextTableFormat & format)2186 QTextTable *QTextCursor::insertTable(int rows, int cols, const QTextTableFormat &format)
2187 {
2188     if(!d || !d->priv || rows == 0 || cols == 0)
2189         return nullptr;
2190 
2191     int pos = d->position;
2192     QTextTable *t = QTextTablePrivate::createTable(d->priv, d->position, rows, cols, format);
2193     d->setPosition(pos+1);
2194     // ##### what should we do if we have a selection?
2195     d->anchor = d->position;
2196     d->adjusted_anchor = d->anchor;
2197     return t;
2198 }
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 */
currentTable() const2206 QTextTable *QTextCursor::currentTable() const
2207 {
2208     if(!d || !d->priv)
2209         return nullptr;
2210 
2211     QTextFrame *frame = d->priv->frameAt(d->position);
2212     while (frame) {
2213         QTextTable *table = qobject_cast<QTextTable *>(frame);
2214         if (table)
2215             return table;
2216         frame = frame->parentFrame();
2217     }
2218     return nullptr;
2219 }
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 */
insertFrame(const QTextFrameFormat & format)2230 QTextFrame *QTextCursor::insertFrame(const QTextFrameFormat &format)
2231 {
2232     if (!d || !d->priv)
2233         return nullptr;
2234 
2235     return d->priv->insertFrame(selectionStart(), selectionEnd(), format);
2236 }
2237 
2238 /*!
2239     Returns a pointer to the current frame. Returns \nullptr if the cursor is invalid.
2240 
2241     \sa insertFrame()
2242 */
currentFrame() const2243 QTextFrame *QTextCursor::currentFrame() const
2244 {
2245     if(!d || !d->priv)
2246         return nullptr;
2247 
2248     return d->priv->frameAt(d->position);
2249 }
2250 
2251 
2252 /*!
2253     Inserts the text \a fragment at the current position().
2254 */
insertFragment(const QTextDocumentFragment & fragment)2255 void QTextCursor::insertFragment(const QTextDocumentFragment &fragment)
2256 {
2257     if (!d || !d->priv || fragment.isEmpty())
2258         return;
2259 
2260     d->priv->beginEditBlock();
2261     d->remove();
2262     fragment.d->insert(*this);
2263     d->priv->endEditBlock();
2264     d->setX();
2265 
2266     if (fragment.d && fragment.d->doc)
2267         d->priv->mergeCachedResources(fragment.d->doc->docHandle());
2268 }
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 
insertHtml(const QString & html)2283 void QTextCursor::insertHtml(const QString &html)
2284 {
2285     if (!d || !d->priv)
2286         return;
2287     QTextDocumentFragment fragment = QTextDocumentFragment::fromHtml(html, d->priv->document());
2288     insertFragment(fragment);
2289 }
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 */
insertImage(const QTextImageFormat & format,QTextFrameFormat::Position alignment)2302 void QTextCursor::insertImage(const QTextImageFormat &format, QTextFrameFormat::Position alignment)
2303 {
2304     if (!d || !d->priv)
2305         return;
2306 
2307     QTextFrameFormat ffmt;
2308     ffmt.setPosition(alignment);
2309     QTextObject *obj = d->priv->createObject(ffmt);
2310 
2311     QTextImageFormat fmt = format;
2312     fmt.setObjectIndex(obj->objectIndex());
2313 
2314     d->priv->beginEditBlock();
2315     d->remove();
2316     const int idx = d->priv->formatCollection()->indexForFormat(fmt);
2317     d->priv->insert(d->position, QString(QChar(QChar::ObjectReplacementCharacter)), idx);
2318     d->priv->endEditBlock();
2319 }
2320 
2321 /*!
2322     Inserts the image defined by \a format at the current position().
2323 */
insertImage(const QTextImageFormat & format)2324 void QTextCursor::insertImage(const QTextImageFormat &format)
2325 {
2326     insertText(QString(QChar::ObjectReplacementCharacter), format);
2327 }
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 */
insertImage(const QString & name)2337 void QTextCursor::insertImage(const QString &name)
2338 {
2339     QTextImageFormat format;
2340     format.setName(name);
2341     insertImage(format);
2342 }
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 */
insertImage(const QImage & image,const QString & name)2351 void QTextCursor::insertImage(const QImage &image, const QString &name)
2352 {
2353     if (image.isNull()) {
2354         qWarning("QTextCursor::insertImage: attempt to add an invalid image");
2355         return;
2356     }
2357     QString imageName = name;
2358     if (name.isEmpty())
2359         imageName = QString::number(image.cacheKey());
2360     d->priv->document()->addResource(QTextDocument::ImageResource, QUrl(imageName), image);
2361     QTextImageFormat format;
2362     format.setName(imageName);
2363     insertImage(format);
2364 }
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 */
operator !=(const QTextCursor & rhs) const2372 bool QTextCursor::operator!=(const QTextCursor &rhs) const
2373 {
2374     return !operator==(rhs);
2375 }
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 */
operator <(const QTextCursor & rhs) const2383 bool QTextCursor::operator<(const QTextCursor &rhs) const
2384 {
2385     if (!d)
2386         return !!rhs.d;
2387 
2388     if (!rhs.d)
2389         return false;
2390 
2391     Q_ASSERT_X(d->priv == rhs.d->priv, "QTextCursor::operator<", "cannot compare cursors attached to different documents");
2392 
2393     return d->position < rhs.d->position;
2394 }
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 */
operator <=(const QTextCursor & rhs) const2403 bool QTextCursor::operator<=(const QTextCursor &rhs) const
2404 {
2405     if (!d)
2406         return true;
2407 
2408     if (!rhs.d)
2409         return false;
2410 
2411     Q_ASSERT_X(d->priv == rhs.d->priv, "QTextCursor::operator<=", "cannot compare cursors attached to different documents");
2412 
2413     return d->position <= rhs.d->position;
2414 }
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 */
operator ==(const QTextCursor & rhs) const2422 bool QTextCursor::operator==(const QTextCursor &rhs) const
2423 {
2424     if (!d)
2425         return !rhs.d;
2426 
2427     if (!rhs.d)
2428         return false;
2429 
2430     return d->position == rhs.d->position && d->priv == rhs.d->priv;
2431 }
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 */
operator >=(const QTextCursor & rhs) const2440 bool QTextCursor::operator>=(const QTextCursor &rhs) const
2441 {
2442     if (!d)
2443         return false;
2444 
2445     if (!rhs.d)
2446         return true;
2447 
2448     Q_ASSERT_X(d->priv == rhs.d->priv, "QTextCursor::operator>=", "cannot compare cursors attached to different documents");
2449 
2450     return d->position >= rhs.d->position;
2451 }
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 */
operator >(const QTextCursor & rhs) const2459 bool QTextCursor::operator>(const QTextCursor &rhs) const
2460 {
2461     if (!d)
2462         return false;
2463 
2464     if (!rhs.d)
2465         return true;
2466 
2467     Q_ASSERT_X(d->priv == rhs.d->priv, "QTextCursor::operator>", "cannot compare cursors attached to different documents");
2468 
2469     return d->position > rhs.d->position;
2470 }
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  */
beginEditBlock()2489 void QTextCursor::beginEditBlock()
2490 {
2491     if (!d || !d->priv)
2492         return;
2493 
2494     if (d->priv->editBlock == 0) // we are the initial edit block, store current cursor position for undo
2495         d->priv->editBlockCursorPosition = d->position;
2496 
2497     d->priv->beginEditBlock();
2498 }
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  */
joinPreviousEditBlock()2514 void QTextCursor::joinPreviousEditBlock()
2515 {
2516     if (!d || !d->priv)
2517         return;
2518 
2519     d->priv->joinPreviousEditBlock();
2520 }
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 
endEditBlock()2530 void QTextCursor::endEditBlock()
2531 {
2532     if (!d || !d->priv)
2533         return;
2534 
2535     d->priv->endEditBlock();
2536 }
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 */
isCopyOf(const QTextCursor & other) const2545 bool QTextCursor::isCopyOf(const QTextCursor &other) const
2546 {
2547     return d == other.d;
2548 }
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 */
blockNumber() const2557 int QTextCursor::blockNumber() const
2558 {
2559     if (!d || !d->priv)
2560         return 0;
2561 
2562     return d->block().blockNumber();
2563 }
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 */
columnNumber() const2577 int QTextCursor::columnNumber() const
2578 {
2579     if (!d || !d->priv)
2580         return 0;
2581 
2582     QTextBlock block = d->block();
2583     if (!block.isValid())
2584         return 0;
2585 
2586     const QTextLayout *layout = d->blockLayout(block);
2587 
2588     const int relativePos = d->position - block.position();
2589 
2590     if (layout->lineCount() == 0)
2591         return relativePos;
2592 
2593     QTextLine line = layout->lineForTextPosition(relativePos);
2594     if (!line.isValid())
2595         return 0;
2596     return relativePos - line.textStart();
2597 }
2598 
2599 /*!
2600     \since 4.5
2601     Returns the document this cursor is associated with.
2602 */
document() const2603 QTextDocument *QTextCursor::document() const
2604 {
2605     if (d->priv)
2606         return d->priv->document();
2607     return nullptr; // document went away
2608 }
2609 
2610 QT_END_NAMESPACE
2611