|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the QtGui module of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
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 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include "qtextcursor.h" |
|
43 #include "qtextcursor_p.h" |
|
44 #include "qglobal.h" |
|
45 #include "qtextdocumentfragment.h" |
|
46 #include "qtextdocumentfragment_p.h" |
|
47 #include "qtextlist.h" |
|
48 #include "qtexttable.h" |
|
49 #include "qtexttable_p.h" |
|
50 #include "qtextengine_p.h" |
|
51 #include "qabstracttextdocumentlayout.h" |
|
52 |
|
53 #include <qtextlayout.h> |
|
54 #include <qdebug.h> |
|
55 |
|
56 QT_BEGIN_NAMESPACE |
|
57 |
|
58 enum { |
|
59 AdjustPrev = 0x1, |
|
60 AdjustUp = 0x3, |
|
61 AdjustNext = 0x4, |
|
62 AdjustDown = 0x12 |
|
63 }; |
|
64 |
|
65 QTextCursorPrivate::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 |
|
72 QTextCursorPrivate::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 |
|
85 QTextCursorPrivate::~QTextCursorPrivate() |
|
86 { |
|
87 if (priv) |
|
88 priv->removeCursor(this); |
|
89 } |
|
90 |
|
91 QTextCursorPrivate::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 |
|
130 void 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 |
|
148 void 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 |
|
179 void 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 |
|
195 bool 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 |
|
202 void 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 |
|
212 void 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 |
|
296 void 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 |
|
357 bool 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 |
|
649 QTextTable *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 |
|
667 void 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 |
|
695 static 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 |
|
708 void 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 |
|
757 void 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 |
|
800 void 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 |
|
849 QTextLayout *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 */ |
|
1023 QTextCursor::QTextCursor() |
|
1024 : d(0) |
|
1025 { |
|
1026 } |
|
1027 |
|
1028 /*! |
|
1029 Constructs a cursor pointing to the beginning of the \a document. |
|
1030 */ |
|
1031 QTextCursor::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 */ |
|
1039 QTextCursor::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 */ |
|
1049 QTextCursor::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 */ |
|
1059 QTextCursor::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 */ |
|
1070 QTextCursor::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 */ |
|
1079 QTextCursor::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 */ |
|
1090 QTextCursor &QTextCursor::operator=(const QTextCursor &cursor) |
|
1091 { |
|
1092 d = cursor.d; |
|
1093 return *this; |
|
1094 } |
|
1095 |
|
1096 /*! |
|
1097 Destroys the QTextCursor. |
|
1098 */ |
|
1099 QTextCursor::~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 */ |
|
1107 bool 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 */ |
|
1119 void 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 */ |
|
1150 int 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 */ |
|
1165 int 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 */ |
|
1190 bool 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 */ |
|
1242 bool 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 */ |
|
1257 void 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 */ |
|
1279 void 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 */ |
|
1292 void 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 */ |
|
1367 void 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 */ |
|
1391 void 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 */ |
|
1425 void 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 */ |
|
1464 bool 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 */ |
|
1477 bool 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 */ |
|
1493 void 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 */ |
|
1514 void 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 */ |
|
1528 void 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 */ |
|
1545 int 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 */ |
|
1558 int QTextCursor::selectionEnd() const |
|
1559 { |
|
1560 if (!d || !d->priv) |
|
1561 return -1; |
|
1562 return qMax(d->position, d->adjusted_anchor); |
|
1563 } |
|
1564 |
|
1565 static 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 */ |
|
1590 QString 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 */ |
|
1641 QTextDocumentFragment QTextCursor::selection() const |
|
1642 { |
|
1643 return QTextDocumentFragment(*this); |
|
1644 } |
|
1645 |
|
1646 /*! |
|
1647 Returns the block that contains the cursor. |
|
1648 */ |
|
1649 QTextBlock 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 */ |
|
1661 QTextBlockFormat 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 */ |
|
1675 void 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 */ |
|
1690 void 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 */ |
|
1706 QTextCharFormat 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 */ |
|
1720 void 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 */ |
|
1735 void 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 */ |
|
1751 QTextCharFormat 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 */ |
|
1792 void 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 */ |
|
1811 void 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 */ |
|
1831 bool 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 */ |
|
1845 bool 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 */ |
|
1859 bool 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 */ |
|
1875 bool 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 */ |
|
1889 void 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 */ |
|
1902 void 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 */ |
|
1918 void 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 */ |
|
1940 QTextList *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 */ |
|
1955 QTextList *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 */ |
|
1967 QTextList *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 */ |
|
1989 QTextList *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 */ |
|
2002 QTextList *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 */ |
|
2025 QTextTable *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 */ |
|
2042 QTextTable *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 */ |
|
2062 QTextTable *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 */ |
|
2086 QTextFrame *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 */ |
|
2099 QTextFrame *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 */ |
|
2111 void 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 |
|
2138 void 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 */ |
|
2157 void 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 */ |
|
2179 void 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 */ |
|
2192 void 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 */ |
|
2206 void 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 */ |
|
2227 bool 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 */ |
|
2238 bool 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 */ |
|
2258 bool 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 */ |
|
2277 bool 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 */ |
|
2295 bool 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 */ |
|
2314 bool 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 */ |
|
2344 void 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 */ |
|
2366 void 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 |
|
2382 void 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 */ |
|
2397 bool 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 */ |
|
2409 int 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 */ |
|
2421 int 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 */ |
|
2447 QTextDocument *QTextCursor::document() const |
|
2448 { |
|
2449 if (d->priv) |
|
2450 return d->priv->document(); |
|
2451 return 0; // document went away |
|
2452 } |
|
2453 |
|
2454 QT_END_NAMESPACE |