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

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

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

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