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

Last change on this file since 829 was 651, checked in by Dmitry A. Kuminov, 16 years ago

trunk: Merged in qt 4.6.2 sources.

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