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