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