source: trunk/src/gui/text/qtextcursor.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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