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