|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 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 |
|
866 Text cursors are objects that are used to access and modify the contents |
|
867 and underlying structure of text documents via a programming interface |
|
868 that mimics the behavior of a cursor in a text editor. QTextCursor contains |
|
869 information about both the cursor's position within a QTextDocument and any |
|
870 selection that it 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 actions |
|
874 through the user interface. A document can be thought of as a |
|
875 single string of characters with the cursor's position() being \e |
|
876 between any two characters (or at the very beginning or very end |
|
877 of the document). Documents can also contain tables, lists, |
|
878 images, and other objects in addition to text but, from the developer's |
|
879 point of view, the document can be treated as one long string. |
|
880 Some portions of that string can be considered to lie within particular |
|
881 blocks (e.g. paragraphs), or within a table's cell, or a list's item, |
|
882 or other structural elements. When we refer to "current character" we |
|
883 mean the character immediately after the cursor position() in the |
|
884 document; similarly the "current block" is the block that contains the |
|
885 cursor position(). |
|
886 |
|
887 A QTextCursor also has an anchor() position. The text that is |
|
888 between the anchor() and the position() is the selection. If |
|
889 anchor() == position() there is no selection. |
|
890 |
|
891 The cursor position can be changed programmatically using |
|
892 setPosition() and movePosition(); the latter can also be used to |
|
893 select text. For selections see selectionStart(), selectionEnd(), |
|
894 hasSelection(), clearSelection(), and removeSelectedText(). |
|
895 |
|
896 If the position() is at the start of a block atBlockStart() |
|
897 returns true; and if it is at the end of a block atBlockEnd() returns |
|
898 true. The format of the current character is returned by |
|
899 charFormat(), and the format of the current block is returned by |
|
900 blockFormat(). |
|
901 |
|
902 Formatting can be applied to the current text document using the |
|
903 setCharFormat(), mergeCharFormat(), setBlockFormat() and |
|
904 mergeBlockFormat() functions. The 'set' functions will replace the |
|
905 cursor's current character or block format, while the 'merge' |
|
906 functions add the given format properties to the cursor's current |
|
907 format. If the cursor has a selection the given format is applied |
|
908 to the current selection. Note that when only parts of a block is |
|
909 selected the block format is applied to the entire block. The text |
|
910 at the current character position can be turned into a list using |
|
911 createList(). |
|
912 |
|
913 Deletions can be achieved using deleteChar(), |
|
914 deletePreviousChar(), and removeSelectedText(). |
|
915 |
|
916 Text strings can be inserted into the document with the insertText() |
|
917 function, blocks (representing new paragraphs) can be inserted with |
|
918 insertBlock(). |
|
919 |
|
920 Existing fragments of text can be inserted with insertFragment() but, |
|
921 if you want to insert pieces of text in various formats, it is usually |
|
922 still easier to use insertText() and supply a character format. |
|
923 |
|
924 Various types of higher-level structure can also be inserted into the |
|
925 document with the cursor: |
|
926 |
|
927 \list |
|
928 \i Lists are ordered sequences of block elements that are decorated with |
|
929 bullet points or symbols. These are inserted in a specified format |
|
930 with insertList(). |
|
931 \i Tables are inserted with the insertTable() function, and can be |
|
932 given an optional format. These contain an array of cells that can |
|
933 be traversed using the cursor. |
|
934 \i Inline images are inserted with insertImage(). The image to be |
|
935 used can be specified in an image format, or by name. |
|
936 \i Frames are inserted by calling insertFrame() with a specified format. |
|
937 \endlist |
|
938 |
|
939 Actions can be grouped (i.e. treated as a single action for |
|
940 undo/redo) using beginEditBlock() and endEditBlock(). |
|
941 |
|
942 Cursor movements are limited to valid cursor positions. In Latin |
|
943 writing this is usually after every character in the text. In some |
|
944 other writing systems cursor movements are limited to "clusters" |
|
945 (e.g. a syllable in Devanagari, or a base letter plus diacritics). |
|
946 Functions such as movePosition() and deleteChar() limit cursor |
|
947 movement to these valid positions. |
|
948 |
|
949 \sa \link richtext.html Rich Text Processing\endlink |
|
950 |
|
951 */ |
|
952 |
|
953 /*! |
|
954 \enum QTextCursor::MoveOperation |
|
955 |
|
956 \value NoMove Keep the cursor where it is |
|
957 |
|
958 \value Start Move to the start of the document. |
|
959 \value StartOfLine Move to the start of the current line. |
|
960 \value StartOfBlock Move to the start of the current block. |
|
961 \value StartOfWord Move to the start of the current word. |
|
962 \value PreviousBlock Move to the start of the previous block. |
|
963 \value PreviousCharacter Move to the previous character. |
|
964 \value PreviousWord Move to the beginning of the previous word. |
|
965 \value Up Move up one line. |
|
966 \value Left Move left one character. |
|
967 \value WordLeft Move left one word. |
|
968 |
|
969 \value End Move to the end of the document. |
|
970 \value EndOfLine Move to the end of the current line. |
|
971 \value EndOfWord Move to the end of the current word. |
|
972 \value EndOfBlock Move to the end of the current block. |
|
973 \value NextBlock Move to the beginning of the next block. |
|
974 \value NextCharacter Move to the next character. |
|
975 \value NextWord Move to the next word. |
|
976 \value Down Move down one line. |
|
977 \value Right Move right one character. |
|
978 \value WordRight Move right one word. |
|
979 |
|
980 \value NextCell Move to the beginning of the next table cell inside the |
|
981 current table. If the current cell is the last cell in the row, the |
|
982 cursor will move to the first cell in the next row. |
|
983 \value PreviousCell Move to the beginning of the previous table cell |
|
984 inside the current table. If the current cell is the first cell in |
|
985 the row, the cursor will move to the last cell in the previous row. |
|
986 \value NextRow Move to the first new cell of the next row in the current |
|
987 table. |
|
988 \value PreviousRow Move to the last cell of the previous row in the |
|
989 current table. |
|
990 |
|
991 \sa movePosition() |
|
992 */ |
|
993 |
|
994 /*! |
|
995 \enum QTextCursor::MoveMode |
|
996 |
|
997 \value MoveAnchor Moves the anchor to the same position as the cursor itself. |
|
998 \value KeepAnchor Keeps the anchor where it is. |
|
999 |
|
1000 If the anchor() is kept where it is and the position() is moved, |
|
1001 the text in between will be selected. |
|
1002 */ |
|
1003 |
|
1004 /*! |
|
1005 \enum QTextCursor::SelectionType |
|
1006 |
|
1007 This enum describes the types of selection that can be applied with the |
|
1008 select() function. |
|
1009 |
|
1010 \value Document Selects the entire document. |
|
1011 \value BlockUnderCursor Selects the block of text under the cursor. |
|
1012 \value LineUnderCursor Selects the line of text under the cursor. |
|
1013 \value WordUnderCursor Selects the word under the cursor. If the cursor |
|
1014 is not positioned within a string of selectable characters, no |
|
1015 text is selected. |
|
1016 */ |
|
1017 |
|
1018 /*! |
|
1019 Constructs a null cursor. |
|
1020 */ |
|
1021 QTextCursor::QTextCursor() |
|
1022 : d(0) |
|
1023 { |
|
1024 } |
|
1025 |
|
1026 /*! |
|
1027 Constructs a cursor pointing to the beginning of the \a document. |
|
1028 */ |
|
1029 QTextCursor::QTextCursor(QTextDocument *document) |
|
1030 : d(new QTextCursorPrivate(document->docHandle())) |
|
1031 { |
|
1032 } |
|
1033 |
|
1034 /*! |
|
1035 Constructs a cursor pointing to the beginning of the \a frame. |
|
1036 */ |
|
1037 QTextCursor::QTextCursor(QTextFrame *frame) |
|
1038 : d(new QTextCursorPrivate(frame->document()->docHandle())) |
|
1039 { |
|
1040 d->adjusted_anchor = d->anchor = d->position = frame->firstPosition(); |
|
1041 } |
|
1042 |
|
1043 |
|
1044 /*! |
|
1045 Constructs a cursor pointing to the beginning of the \a block. |
|
1046 */ |
|
1047 QTextCursor::QTextCursor(const QTextBlock &block) |
|
1048 : d(new QTextCursorPrivate(block.docHandle())) |
|
1049 { |
|
1050 d->adjusted_anchor = d->anchor = d->position = block.position(); |
|
1051 } |
|
1052 |
|
1053 |
|
1054 /*! |
|
1055 \internal |
|
1056 */ |
|
1057 QTextCursor::QTextCursor(QTextDocumentPrivate *p, int pos) |
|
1058 : d(new QTextCursorPrivate(p)) |
|
1059 { |
|
1060 d->adjusted_anchor = d->anchor = d->position = pos; |
|
1061 |
|
1062 d->setX(); |
|
1063 } |
|
1064 |
|
1065 /*! |
|
1066 \internal |
|
1067 */ |
|
1068 QTextCursor::QTextCursor(QTextCursorPrivate *d) |
|
1069 { |
|
1070 Q_ASSERT(d); |
|
1071 this->d = d; |
|
1072 } |
|
1073 |
|
1074 /*! |
|
1075 Constructs a new cursor that is a copy of \a cursor. |
|
1076 */ |
|
1077 QTextCursor::QTextCursor(const QTextCursor &cursor) |
|
1078 { |
|
1079 d = cursor.d; |
|
1080 } |
|
1081 |
|
1082 /*! |
|
1083 Makes a copy of \a cursor and assigns it to this QTextCursor. Note |
|
1084 that QTextCursor is an \l{Implicitly Shared Classes}{implicitly |
|
1085 shared} class. |
|
1086 |
|
1087 */ |
|
1088 QTextCursor &QTextCursor::operator=(const QTextCursor &cursor) |
|
1089 { |
|
1090 d = cursor.d; |
|
1091 return *this; |
|
1092 } |
|
1093 |
|
1094 /*! |
|
1095 Destroys the QTextCursor. |
|
1096 */ |
|
1097 QTextCursor::~QTextCursor() |
|
1098 { |
|
1099 } |
|
1100 |
|
1101 /*! |
|
1102 Returns true if the cursor is null; otherwise returns false. A null |
|
1103 cursor is created by the default constructor. |
|
1104 */ |
|
1105 bool QTextCursor::isNull() const |
|
1106 { |
|
1107 return !d || !d->priv; |
|
1108 } |
|
1109 |
|
1110 /*! |
|
1111 Moves the cursor to the absolute position in the document specified by |
|
1112 \a pos using a \c MoveMode specified by \a m. The cursor is positioned |
|
1113 between characters. |
|
1114 |
|
1115 \sa position() movePosition() anchor() |
|
1116 */ |
|
1117 void QTextCursor::setPosition(int pos, MoveMode m) |
|
1118 { |
|
1119 if (!d || !d->priv) |
|
1120 return; |
|
1121 |
|
1122 if (pos < 0 || pos >= d->priv->length()) { |
|
1123 qWarning("QTextCursor::setPosition: Position '%d' out of range", pos); |
|
1124 return; |
|
1125 } |
|
1126 |
|
1127 d->setPosition(pos); |
|
1128 if (m == MoveAnchor) { |
|
1129 d->anchor = pos; |
|
1130 d->adjusted_anchor = pos; |
|
1131 } else { // keep anchor |
|
1132 QTextCursor::MoveOperation op; |
|
1133 if (pos < d->anchor) |
|
1134 op = QTextCursor::Left; |
|
1135 else |
|
1136 op = QTextCursor::Right; |
|
1137 d->adjustCursor(op); |
|
1138 } |
|
1139 d->setX(); |
|
1140 } |
|
1141 |
|
1142 /*! |
|
1143 Returns the absolute position of the cursor within the document. |
|
1144 The cursor is positioned between characters. |
|
1145 |
|
1146 \sa setPosition() movePosition() anchor() |
|
1147 */ |
|
1148 int QTextCursor::position() const |
|
1149 { |
|
1150 if (!d || !d->priv) |
|
1151 return -1; |
|
1152 return d->position; |
|
1153 } |
|
1154 |
|
1155 /*! |
|
1156 Returns the anchor position; this is the same as position() unless |
|
1157 there is a selection in which case position() marks one end of the |
|
1158 selection and anchor() marks the other end. Just like the cursor |
|
1159 position, the anchor position is between characters. |
|
1160 |
|
1161 \sa position() setPosition() movePosition() selectionStart() selectionEnd() |
|
1162 */ |
|
1163 int QTextCursor::anchor() const |
|
1164 { |
|
1165 if (!d || !d->priv) |
|
1166 return -1; |
|
1167 return d->anchor; |
|
1168 } |
|
1169 |
|
1170 /*! |
|
1171 \fn bool QTextCursor::movePosition(MoveOperation operation, MoveMode mode, int n) |
|
1172 |
|
1173 Moves the cursor by performing the given \a operation \a n times, using the specified |
|
1174 \a mode, and returns true if all operations were completed successfully; otherwise |
|
1175 returns false. |
|
1176 |
|
1177 For example, if this function is repeatedly used to seek to the end of the next |
|
1178 word, it will eventually fail when the end of the document is reached. |
|
1179 |
|
1180 By default, the move operation is performed once (\a n = 1). |
|
1181 |
|
1182 If \a mode is \c KeepAnchor, the cursor selects the text it moves |
|
1183 over. This is the same effect that the user achieves when they |
|
1184 hold down the Shift key and move the cursor with the cursor keys. |
|
1185 |
|
1186 \sa setVisualNavigation() |
|
1187 */ |
|
1188 bool QTextCursor::movePosition(MoveOperation op, MoveMode mode, int n) |
|
1189 { |
|
1190 if (!d || !d->priv) |
|
1191 return false; |
|
1192 switch (op) { |
|
1193 case Start: |
|
1194 case StartOfLine: |
|
1195 case End: |
|
1196 case EndOfLine: |
|
1197 n = 1; |
|
1198 break; |
|
1199 default: break; |
|
1200 } |
|
1201 |
|
1202 int previousPosition = d->position; |
|
1203 for (; n > 0; --n) { |
|
1204 if (!d->movePosition(op, mode)) |
|
1205 return false; |
|
1206 } |
|
1207 |
|
1208 if (d->visualNavigation && !d->block().isVisible()) { |
|
1209 QTextBlock b = d->block(); |
|
1210 if (previousPosition < d->position) { |
|
1211 while (!b.next().isVisible()) |
|
1212 b = b.next(); |
|
1213 d->setPosition(b.position() + b.length() - 1); |
|
1214 } else { |
|
1215 while (!b.previous().isVisible()) |
|
1216 b = b.previous(); |
|
1217 d->setPosition(b.position()); |
|
1218 } |
|
1219 if (mode == QTextCursor::MoveAnchor) |
|
1220 d->anchor = d->position; |
|
1221 while (d->movePosition(op, mode) |
|
1222 && !d->block().isVisible()) |
|
1223 ; |
|
1224 |
|
1225 } |
|
1226 return true; |
|
1227 } |
|
1228 |
|
1229 /*! |
|
1230 \since 4.4 |
|
1231 |
|
1232 Returns true if the cursor does visual navigation; otherwise |
|
1233 returns false. |
|
1234 |
|
1235 Visual navigation means skipping over hidden text pragraphs. The |
|
1236 default is false. |
|
1237 |
|
1238 \sa setVisualNavigation(), movePosition() |
|
1239 */ |
|
1240 bool QTextCursor::visualNavigation() const |
|
1241 { |
|
1242 return d ? d->visualNavigation : false; |
|
1243 } |
|
1244 |
|
1245 /*! |
|
1246 \since 4.4 |
|
1247 |
|
1248 Sets visual navigation to \a b. |
|
1249 |
|
1250 Visual navigation means skipping over hidden text pragraphs. The |
|
1251 default is false. |
|
1252 |
|
1253 \sa visualNavigation(), movePosition() |
|
1254 */ |
|
1255 void QTextCursor::setVisualNavigation(bool b) |
|
1256 { |
|
1257 if (d) |
|
1258 d->visualNavigation = b; |
|
1259 } |
|
1260 |
|
1261 /*! |
|
1262 Inserts \a text at the current position, using the current |
|
1263 character format. |
|
1264 |
|
1265 If there is a selection, the selection is deleted and replaced by |
|
1266 \a text, for example: |
|
1267 \snippet doc/src/snippets/code/src_gui_text_qtextcursor.cpp 0 |
|
1268 This clears any existing selection, selects the word at the cursor |
|
1269 (i.e. from position() forward), and replaces the selection with |
|
1270 the phrase "Hello World". |
|
1271 |
|
1272 Any ASCII linefeed characters (\\n) in the inserted text are transformed |
|
1273 into unicode block separators, corresponding to insertBlock() calls. |
|
1274 |
|
1275 \sa charFormat() hasSelection() |
|
1276 */ |
|
1277 void QTextCursor::insertText(const QString &text) |
|
1278 { |
|
1279 QTextCharFormat fmt = charFormat(); |
|
1280 fmt.clearProperty(QTextFormat::ObjectType); |
|
1281 insertText(text, fmt); |
|
1282 } |
|
1283 |
|
1284 /*! |
|
1285 \fn void QTextCursor::insertText(const QString &text, const QTextCharFormat &format) |
|
1286 \overload |
|
1287 |
|
1288 Inserts \a text at the current position with the given \a format. |
|
1289 */ |
|
1290 void QTextCursor::insertText(const QString &text, const QTextCharFormat &_format) |
|
1291 { |
|
1292 if (!d || !d->priv) |
|
1293 return; |
|
1294 |
|
1295 Q_ASSERT(_format.isValid()); |
|
1296 |
|
1297 QTextCharFormat format = _format; |
|
1298 format.clearProperty(QTextFormat::ObjectIndex); |
|
1299 |
|
1300 bool hasEditBlock = false; |
|
1301 |
|
1302 if (d->anchor != d->position) { |
|
1303 hasEditBlock = true; |
|
1304 d->priv->beginEditBlock(); |
|
1305 d->remove(); |
|
1306 } |
|
1307 |
|
1308 if (!text.isEmpty()) { |
|
1309 QTextFormatCollection *formats = d->priv->formatCollection(); |
|
1310 int formatIdx = formats->indexForFormat(format); |
|
1311 Q_ASSERT(formats->format(formatIdx).isCharFormat()); |
|
1312 |
|
1313 QTextBlockFormat blockFmt = blockFormat(); |
|
1314 |
|
1315 |
|
1316 int textStart = d->priv->text.length(); |
|
1317 int blockStart = 0; |
|
1318 d->priv->text += text; |
|
1319 int textEnd = d->priv->text.length(); |
|
1320 |
|
1321 for (int i = 0; i < text.length(); ++i) { |
|
1322 QChar ch = text.at(i); |
|
1323 |
|
1324 const int blockEnd = i; |
|
1325 |
|
1326 if (ch == QLatin1Char('\r') |
|
1327 && (i + 1) < text.length() |
|
1328 && text.at(i + 1) == QLatin1Char('\n')) { |
|
1329 ++i; |
|
1330 ch = text.at(i); |
|
1331 } |
|
1332 |
|
1333 if (ch == QLatin1Char('\n') |
|
1334 || ch == QChar::ParagraphSeparator |
|
1335 || ch == QTextBeginningOfFrame |
|
1336 || ch == QTextEndOfFrame |
|
1337 || ch == QLatin1Char('\r')) { |
|
1338 |
|
1339 if (!hasEditBlock) { |
|
1340 hasEditBlock = true; |
|
1341 d->priv->beginEditBlock(); |
|
1342 } |
|
1343 |
|
1344 if (blockEnd > blockStart) |
|
1345 d->priv->insert(d->position, textStart + blockStart, blockEnd - blockStart, formatIdx); |
|
1346 |
|
1347 d->insertBlock(blockFmt, format); |
|
1348 blockStart = i + 1; |
|
1349 } |
|
1350 } |
|
1351 if (textStart + blockStart < textEnd) |
|
1352 d->priv->insert(d->position, textStart + blockStart, textEnd - textStart - blockStart, formatIdx); |
|
1353 } |
|
1354 if (hasEditBlock) |
|
1355 d->priv->endEditBlock(); |
|
1356 d->setX(); |
|
1357 } |
|
1358 |
|
1359 /*! |
|
1360 If there is no selected text, deletes the character \e at the |
|
1361 current cursor position; otherwise deletes the selected text. |
|
1362 |
|
1363 \sa deletePreviousChar() hasSelection() clearSelection() |
|
1364 */ |
|
1365 void QTextCursor::deleteChar() |
|
1366 { |
|
1367 if (!d || !d->priv) |
|
1368 return; |
|
1369 |
|
1370 if (d->position != d->anchor) { |
|
1371 removeSelectedText(); |
|
1372 return; |
|
1373 } |
|
1374 |
|
1375 if (!d->canDelete(d->position)) |
|
1376 return; |
|
1377 d->adjusted_anchor = d->anchor = |
|
1378 d->priv->nextCursorPosition(d->anchor, QTextLayout::SkipCharacters); |
|
1379 d->remove(); |
|
1380 d->setX(); |
|
1381 } |
|
1382 |
|
1383 /*! |
|
1384 If there is no selected text, deletes the character \e before the |
|
1385 current cursor position; otherwise deletes the selected text. |
|
1386 |
|
1387 \sa deleteChar() hasSelection() clearSelection() |
|
1388 */ |
|
1389 void QTextCursor::deletePreviousChar() |
|
1390 { |
|
1391 if (!d || !d->priv) |
|
1392 return; |
|
1393 |
|
1394 if (d->position != d->anchor) { |
|
1395 removeSelectedText(); |
|
1396 return; |
|
1397 } |
|
1398 |
|
1399 if (d->anchor < 1 || !d->canDelete(d->anchor-1)) |
|
1400 return; |
|
1401 d->anchor--; |
|
1402 |
|
1403 QTextDocumentPrivate::FragmentIterator fragIt = d->priv->find(d->anchor); |
|
1404 const QTextFragmentData * const frag = fragIt.value(); |
|
1405 int fpos = fragIt.position(); |
|
1406 QChar uc = d->priv->buffer().at(d->anchor - fpos + frag->stringPosition); |
|
1407 if (d->anchor > fpos && uc.unicode() >= 0xdc00 && uc.unicode() < 0xe000) { |
|
1408 // second half of a surrogate, check if we have the first half as well, |
|
1409 // if yes delete both at once |
|
1410 uc = d->priv->buffer().at(d->anchor - 1 - fpos + frag->stringPosition); |
|
1411 if (uc.unicode() >= 0xd800 && uc.unicode() < 0xdc00) |
|
1412 --d->anchor; |
|
1413 } |
|
1414 |
|
1415 d->adjusted_anchor = d->anchor; |
|
1416 d->remove(); |
|
1417 d->setX(); |
|
1418 } |
|
1419 |
|
1420 /*! |
|
1421 Selects text in the document according to the given \a selection. |
|
1422 */ |
|
1423 void QTextCursor::select(SelectionType selection) |
|
1424 { |
|
1425 if (!d || !d->priv) |
|
1426 return; |
|
1427 |
|
1428 clearSelection(); |
|
1429 |
|
1430 const QTextBlock block = d->block(); |
|
1431 |
|
1432 switch (selection) { |
|
1433 case LineUnderCursor: |
|
1434 movePosition(StartOfLine); |
|
1435 movePosition(EndOfLine, KeepAnchor); |
|
1436 break; |
|
1437 case WordUnderCursor: |
|
1438 movePosition(StartOfWord); |
|
1439 movePosition(EndOfWord, KeepAnchor); |
|
1440 break; |
|
1441 case BlockUnderCursor: |
|
1442 if (block.length() == 1) // no content |
|
1443 break; |
|
1444 movePosition(StartOfBlock); |
|
1445 // also select the paragraph separator |
|
1446 if (movePosition(PreviousBlock)) { |
|
1447 movePosition(EndOfBlock); |
|
1448 movePosition(NextBlock, KeepAnchor); |
|
1449 } |
|
1450 movePosition(EndOfBlock, KeepAnchor); |
|
1451 break; |
|
1452 case Document: |
|
1453 movePosition(Start); |
|
1454 movePosition(End, KeepAnchor); |
|
1455 break; |
|
1456 } |
|
1457 } |
|
1458 |
|
1459 /*! |
|
1460 Returns true if the cursor contains a selection; otherwise returns false. |
|
1461 */ |
|
1462 bool QTextCursor::hasSelection() const |
|
1463 { |
|
1464 return !!d && d->position != d->anchor; |
|
1465 } |
|
1466 |
|
1467 |
|
1468 /*! |
|
1469 Returns true if the cursor contains a selection that is not simply a |
|
1470 range from selectionStart() to selectionEnd(); otherwise returns false. |
|
1471 |
|
1472 Complex selections are ones that span at least two cells in a table; |
|
1473 their extent is specified by selectedTableCells(). |
|
1474 */ |
|
1475 bool QTextCursor::hasComplexSelection() const |
|
1476 { |
|
1477 if (!d) |
|
1478 return false; |
|
1479 |
|
1480 return d->complexSelectionTable() != 0; |
|
1481 } |
|
1482 |
|
1483 /*! |
|
1484 If the selection spans over table cells, \a firstRow is populated |
|
1485 with the number of the first row in the selection, \a firstColumn |
|
1486 with the number of the first column in the selection, and \a |
|
1487 numRows and \a numColumns with the number of rows and columns in |
|
1488 the selection. If the selection does not span any table cells the |
|
1489 results are harmless but undefined. |
|
1490 */ |
|
1491 void QTextCursor::selectedTableCells(int *firstRow, int *numRows, int *firstColumn, int *numColumns) const |
|
1492 { |
|
1493 *firstRow = -1; |
|
1494 *firstColumn = -1; |
|
1495 *numRows = -1; |
|
1496 *numColumns = -1; |
|
1497 |
|
1498 if (!d || d->position == d->anchor) |
|
1499 return; |
|
1500 |
|
1501 d->selectedTableCells(firstRow, numRows, firstColumn, numColumns); |
|
1502 } |
|
1503 |
|
1504 |
|
1505 /*! |
|
1506 Clears the current selection by setting the anchor to the cursor position. |
|
1507 |
|
1508 Note that it does \bold{not} delete the text of the selection. |
|
1509 |
|
1510 \sa removeSelectedText() hasSelection() |
|
1511 */ |
|
1512 void QTextCursor::clearSelection() |
|
1513 { |
|
1514 if (!d) |
|
1515 return; |
|
1516 d->adjusted_anchor = d->anchor = d->position; |
|
1517 d->currentCharFormat = -1; |
|
1518 } |
|
1519 |
|
1520 /*! |
|
1521 If there is a selection, its content is deleted; otherwise does |
|
1522 nothing. |
|
1523 |
|
1524 \sa hasSelection() |
|
1525 */ |
|
1526 void QTextCursor::removeSelectedText() |
|
1527 { |
|
1528 if (!d || !d->priv || d->position == d->anchor) |
|
1529 return; |
|
1530 |
|
1531 d->priv->beginEditBlock(); |
|
1532 d->remove(); |
|
1533 d->priv->endEditBlock(); |
|
1534 d->setX(); |
|
1535 } |
|
1536 |
|
1537 /*! |
|
1538 Returns the start of the selection or position() if the |
|
1539 cursor doesn't have a selection. |
|
1540 |
|
1541 \sa selectionEnd() position() anchor() |
|
1542 */ |
|
1543 int QTextCursor::selectionStart() const |
|
1544 { |
|
1545 if (!d || !d->priv) |
|
1546 return -1; |
|
1547 return qMin(d->position, d->adjusted_anchor); |
|
1548 } |
|
1549 |
|
1550 /*! |
|
1551 Returns the end of the selection or position() if the cursor |
|
1552 doesn't have a selection. |
|
1553 |
|
1554 \sa selectionStart() position() anchor() |
|
1555 */ |
|
1556 int QTextCursor::selectionEnd() const |
|
1557 { |
|
1558 if (!d || !d->priv) |
|
1559 return -1; |
|
1560 return qMax(d->position, d->adjusted_anchor); |
|
1561 } |
|
1562 |
|
1563 static void getText(QString &text, QTextDocumentPrivate *priv, const QString &docText, int pos, int end) |
|
1564 { |
|
1565 while (pos < end) { |
|
1566 QTextDocumentPrivate::FragmentIterator fragIt = priv->find(pos); |
|
1567 const QTextFragmentData * const frag = fragIt.value(); |
|
1568 |
|
1569 const int offsetInFragment = qMax(0, pos - fragIt.position()); |
|
1570 const int len = qMin(int(frag->size_array[0] - offsetInFragment), end - pos); |
|
1571 |
|
1572 text += QString(docText.constData() + frag->stringPosition + offsetInFragment, len); |
|
1573 pos += len; |
|
1574 } |
|
1575 } |
|
1576 |
|
1577 /*! |
|
1578 Returns the current selection's text (which may be empty). This |
|
1579 only returns the text, with no rich text formatting information. |
|
1580 If you want a document fragment (i.e. formatted rich text) use |
|
1581 selection() instead. |
|
1582 |
|
1583 \note If the selection obtained from an editor spans a line break, |
|
1584 the text will contain a Unicode U+2029 paragraph separator character |
|
1585 instead of a newline \c{\n} character. Use QString::replace() to |
|
1586 replace these characters with newlines. |
|
1587 */ |
|
1588 QString QTextCursor::selectedText() const |
|
1589 { |
|
1590 if (!d || !d->priv || d->position == d->anchor) |
|
1591 return QString(); |
|
1592 |
|
1593 const QString docText = d->priv->buffer(); |
|
1594 QString text; |
|
1595 |
|
1596 QTextTable *table = d->complexSelectionTable(); |
|
1597 if (table) { |
|
1598 int row_start, col_start, num_rows, num_cols; |
|
1599 selectedTableCells(&row_start, &num_rows, &col_start, &num_cols); |
|
1600 |
|
1601 Q_ASSERT(row_start != -1); |
|
1602 for (int r = row_start; r < row_start + num_rows; ++r) { |
|
1603 for (int c = col_start; c < col_start + num_cols; ++c) { |
|
1604 QTextTableCell cell = table->cellAt(r, c); |
|
1605 int rspan = cell.rowSpan(); |
|
1606 int cspan = cell.columnSpan(); |
|
1607 if (rspan != 1) { |
|
1608 int cr = cell.row(); |
|
1609 if (cr != r) |
|
1610 continue; |
|
1611 } |
|
1612 if (cspan != 1) { |
|
1613 int cc = cell.column(); |
|
1614 if (cc != c) |
|
1615 continue; |
|
1616 } |
|
1617 |
|
1618 getText(text, d->priv, docText, cell.firstPosition(), cell.lastPosition()); |
|
1619 } |
|
1620 } |
|
1621 } else { |
|
1622 getText(text, d->priv, docText, selectionStart(), selectionEnd()); |
|
1623 } |
|
1624 |
|
1625 return text; |
|
1626 } |
|
1627 |
|
1628 /*! |
|
1629 Returns the current selection (which may be empty) with all its |
|
1630 formatting information. If you just want the selected text (i.e. |
|
1631 plain text) use selectedText() instead. |
|
1632 |
|
1633 \note Unlike QTextDocumentFragment::toPlainText(), |
|
1634 selectedText() may include special unicode characters such as |
|
1635 QChar::ParagraphSeparator. |
|
1636 |
|
1637 \sa QTextDocumentFragment::toPlainText() |
|
1638 */ |
|
1639 QTextDocumentFragment QTextCursor::selection() const |
|
1640 { |
|
1641 return QTextDocumentFragment(*this); |
|
1642 } |
|
1643 |
|
1644 /*! |
|
1645 Returns the block that contains the cursor. |
|
1646 */ |
|
1647 QTextBlock QTextCursor::block() const |
|
1648 { |
|
1649 if (!d || !d->priv) |
|
1650 return QTextBlock(); |
|
1651 return d->block(); |
|
1652 } |
|
1653 |
|
1654 /*! |
|
1655 Returns the block format of the block the cursor is in. |
|
1656 |
|
1657 \sa setBlockFormat() charFormat() |
|
1658 */ |
|
1659 QTextBlockFormat QTextCursor::blockFormat() const |
|
1660 { |
|
1661 if (!d || !d->priv) |
|
1662 return QTextBlockFormat(); |
|
1663 |
|
1664 return d->block().blockFormat(); |
|
1665 } |
|
1666 |
|
1667 /*! |
|
1668 Sets the block format of the current block (or all blocks that |
|
1669 are contained in the selection) to \a format. |
|
1670 |
|
1671 \sa blockFormat(), mergeBlockFormat() |
|
1672 */ |
|
1673 void QTextCursor::setBlockFormat(const QTextBlockFormat &format) |
|
1674 { |
|
1675 if (!d || !d->priv) |
|
1676 return; |
|
1677 |
|
1678 d->setBlockFormat(format, QTextDocumentPrivate::SetFormat); |
|
1679 } |
|
1680 |
|
1681 /*! |
|
1682 Modifies the block format of the current block (or all blocks that |
|
1683 are contained in the selection) with the block format specified by |
|
1684 \a modifier. |
|
1685 |
|
1686 \sa setBlockFormat(), blockFormat() |
|
1687 */ |
|
1688 void QTextCursor::mergeBlockFormat(const QTextBlockFormat &modifier) |
|
1689 { |
|
1690 if (!d || !d->priv) |
|
1691 return; |
|
1692 |
|
1693 d->setBlockFormat(modifier, QTextDocumentPrivate::MergeFormat); |
|
1694 } |
|
1695 |
|
1696 /*! |
|
1697 Returns the block character format of the block the cursor is in. |
|
1698 |
|
1699 The block char format is the format used when inserting text at the |
|
1700 beginning of an empty block. |
|
1701 |
|
1702 \sa setBlockCharFormat() |
|
1703 */ |
|
1704 QTextCharFormat QTextCursor::blockCharFormat() const |
|
1705 { |
|
1706 if (!d || !d->priv) |
|
1707 return QTextCharFormat(); |
|
1708 |
|
1709 return d->block().charFormat(); |
|
1710 } |
|
1711 |
|
1712 /*! |
|
1713 Sets the block char format of the current block (or all blocks that |
|
1714 are contained in the selection) to \a format. |
|
1715 |
|
1716 \sa blockCharFormat() |
|
1717 */ |
|
1718 void QTextCursor::setBlockCharFormat(const QTextCharFormat &format) |
|
1719 { |
|
1720 if (!d || !d->priv) |
|
1721 return; |
|
1722 |
|
1723 d->setBlockCharFormat(format, QTextDocumentPrivate::SetFormatAndPreserveObjectIndices); |
|
1724 } |
|
1725 |
|
1726 /*! |
|
1727 Modifies the block char format of the current block (or all blocks that |
|
1728 are contained in the selection) with the block format specified by |
|
1729 \a modifier. |
|
1730 |
|
1731 \sa setBlockCharFormat() |
|
1732 */ |
|
1733 void QTextCursor::mergeBlockCharFormat(const QTextCharFormat &modifier) |
|
1734 { |
|
1735 if (!d || !d->priv) |
|
1736 return; |
|
1737 |
|
1738 d->setBlockCharFormat(modifier, QTextDocumentPrivate::MergeFormat); |
|
1739 } |
|
1740 |
|
1741 /*! |
|
1742 Returns the format of the character immediately before the cursor position(). If the cursor is |
|
1743 positioned at the beginning of a text block that is not empty then the format of the character |
|
1744 immediately after the cursor is returned. |
|
1745 |
|
1746 \sa insertText(), blockFormat() |
|
1747 */ |
|
1748 QTextCharFormat QTextCursor::charFormat() const |
|
1749 { |
|
1750 if (!d || !d->priv) |
|
1751 return QTextCharFormat(); |
|
1752 |
|
1753 int idx = d->currentCharFormat; |
|
1754 if (idx == -1) { |
|
1755 QTextBlock block = d->block(); |
|
1756 |
|
1757 int pos; |
|
1758 if (d->position == block.position() |
|
1759 && block.length() > 1) |
|
1760 pos = d->position; |
|
1761 else |
|
1762 pos = d->position - 1; |
|
1763 |
|
1764 if (pos == -1) { |
|
1765 idx = d->priv->blockCharFormatIndex(d->priv->blockMap().firstNode()); |
|
1766 } else { |
|
1767 Q_ASSERT(pos >= 0 && pos < d->priv->length()); |
|
1768 |
|
1769 QTextDocumentPrivate::FragmentIterator it = d->priv->find(pos); |
|
1770 Q_ASSERT(!it.atEnd()); |
|
1771 idx = it.value()->format; |
|
1772 } |
|
1773 } |
|
1774 |
|
1775 QTextCharFormat cfmt = d->priv->formatCollection()->charFormat(idx); |
|
1776 cfmt.clearProperty(QTextFormat::ObjectIndex); |
|
1777 |
|
1778 Q_ASSERT(cfmt.isValid()); |
|
1779 return cfmt; |
|
1780 } |
|
1781 |
|
1782 /*! |
|
1783 Sets the cursor's current character format to the given \a |
|
1784 format. If the cursor has a selection, the given \a format is |
|
1785 applied to the current selection. |
|
1786 |
|
1787 \sa hasSelection(), mergeCharFormat() |
|
1788 */ |
|
1789 void QTextCursor::setCharFormat(const QTextCharFormat &format) |
|
1790 { |
|
1791 if (!d || !d->priv) |
|
1792 return; |
|
1793 if (d->position == d->anchor) { |
|
1794 d->currentCharFormat = d->priv->formatCollection()->indexForFormat(format); |
|
1795 return; |
|
1796 } |
|
1797 d->setCharFormat(format, QTextDocumentPrivate::SetFormatAndPreserveObjectIndices); |
|
1798 } |
|
1799 |
|
1800 /*! |
|
1801 Merges the cursor's current character format with the properties |
|
1802 described by format \a modifier. If the cursor has a selection, |
|
1803 this function applies all the properties set in \a modifier to all |
|
1804 the character formats that are part of the selection. |
|
1805 |
|
1806 \sa hasSelection(), setCharFormat() |
|
1807 */ |
|
1808 void QTextCursor::mergeCharFormat(const QTextCharFormat &modifier) |
|
1809 { |
|
1810 if (!d || !d->priv) |
|
1811 return; |
|
1812 if (d->position == d->anchor) { |
|
1813 QTextCharFormat format = charFormat(); |
|
1814 format.merge(modifier); |
|
1815 d->currentCharFormat = d->priv->formatCollection()->indexForFormat(format); |
|
1816 return; |
|
1817 } |
|
1818 |
|
1819 d->setCharFormat(modifier, QTextDocumentPrivate::MergeFormat); |
|
1820 } |
|
1821 |
|
1822 /*! |
|
1823 Returns true if the cursor is at the start of a block; otherwise |
|
1824 returns false. |
|
1825 |
|
1826 \sa atBlockEnd(), atStart() |
|
1827 */ |
|
1828 bool QTextCursor::atBlockStart() const |
|
1829 { |
|
1830 if (!d || !d->priv) |
|
1831 return false; |
|
1832 |
|
1833 return d->position == d->block().position(); |
|
1834 } |
|
1835 |
|
1836 /*! |
|
1837 Returns true if the cursor is at the end of a block; otherwise |
|
1838 returns false. |
|
1839 |
|
1840 \sa atBlockStart(), atEnd() |
|
1841 */ |
|
1842 bool QTextCursor::atBlockEnd() const |
|
1843 { |
|
1844 if (!d || !d->priv) |
|
1845 return false; |
|
1846 |
|
1847 return d->position == d->block().position() + d->block().length() - 1; |
|
1848 } |
|
1849 |
|
1850 /*! |
|
1851 Returns true if the cursor is at the start of the document; |
|
1852 otherwise returns false. |
|
1853 |
|
1854 \sa atBlockStart(), atEnd() |
|
1855 */ |
|
1856 bool QTextCursor::atStart() const |
|
1857 { |
|
1858 if (!d || !d->priv) |
|
1859 return false; |
|
1860 |
|
1861 return d->position == 0; |
|
1862 } |
|
1863 |
|
1864 /*! |
|
1865 \since 4.6 |
|
1866 |
|
1867 Returns true if the cursor is at the end of the document; |
|
1868 otherwise returns false. |
|
1869 |
|
1870 \sa atStart(), atBlockEnd() |
|
1871 */ |
|
1872 bool QTextCursor::atEnd() const |
|
1873 { |
|
1874 if (!d || !d->priv) |
|
1875 return false; |
|
1876 |
|
1877 return d->position == d->priv->length() - 1; |
|
1878 } |
|
1879 |
|
1880 /*! |
|
1881 Inserts a new empty block at the cursor position() with the |
|
1882 current blockFormat() and charFormat(). |
|
1883 |
|
1884 \sa setBlockFormat() |
|
1885 */ |
|
1886 void QTextCursor::insertBlock() |
|
1887 { |
|
1888 insertBlock(blockFormat()); |
|
1889 } |
|
1890 |
|
1891 /*! |
|
1892 \overload |
|
1893 |
|
1894 Inserts a new empty block at the cursor position() with block |
|
1895 format \a format and the current charFormat() as block char format. |
|
1896 |
|
1897 \sa setBlockFormat() |
|
1898 */ |
|
1899 void QTextCursor::insertBlock(const QTextBlockFormat &format) |
|
1900 { |
|
1901 QTextCharFormat charFmt = charFormat(); |
|
1902 charFmt.clearProperty(QTextFormat::ObjectType); |
|
1903 insertBlock(format, charFmt); |
|
1904 } |
|
1905 |
|
1906 /*! |
|
1907 \fn void QTextCursor::insertBlock(const QTextBlockFormat &format, const QTextCharFormat &charFormat) |
|
1908 \overload |
|
1909 |
|
1910 Inserts a new empty block at the cursor position() with block |
|
1911 format \a format and \a charFormat as block char format. |
|
1912 |
|
1913 \sa setBlockFormat() |
|
1914 */ |
|
1915 void QTextCursor::insertBlock(const QTextBlockFormat &format, const QTextCharFormat &_charFormat) |
|
1916 { |
|
1917 if (!d || !d->priv) |
|
1918 return; |
|
1919 |
|
1920 QTextCharFormat charFormat = _charFormat; |
|
1921 charFormat.clearProperty(QTextFormat::ObjectIndex); |
|
1922 |
|
1923 d->priv->beginEditBlock(); |
|
1924 d->remove(); |
|
1925 d->insertBlock(format, charFormat); |
|
1926 d->priv->endEditBlock(); |
|
1927 d->setX(); |
|
1928 } |
|
1929 |
|
1930 /*! |
|
1931 Inserts a new block at the current position and makes it the first |
|
1932 list item of a newly created list with the given \a format. Returns |
|
1933 the created list. |
|
1934 |
|
1935 \sa currentList() createList() insertBlock() |
|
1936 */ |
|
1937 QTextList *QTextCursor::insertList(const QTextListFormat &format) |
|
1938 { |
|
1939 insertBlock(); |
|
1940 return createList(format); |
|
1941 } |
|
1942 |
|
1943 /*! |
|
1944 \overload |
|
1945 |
|
1946 Inserts a new block at the current position and makes it the first |
|
1947 list item of a newly created list with the given \a style. Returns |
|
1948 the created list. |
|
1949 |
|
1950 \sa currentList(), createList(), insertBlock() |
|
1951 */ |
|
1952 QTextList *QTextCursor::insertList(QTextListFormat::Style style) |
|
1953 { |
|
1954 insertBlock(); |
|
1955 return createList(style); |
|
1956 } |
|
1957 |
|
1958 /*! |
|
1959 Creates and returns a new list with the given \a format, and makes the |
|
1960 current paragraph the cursor is in the first list item. |
|
1961 |
|
1962 \sa insertList() currentList() |
|
1963 */ |
|
1964 QTextList *QTextCursor::createList(const QTextListFormat &format) |
|
1965 { |
|
1966 if (!d || !d->priv) |
|
1967 return 0; |
|
1968 |
|
1969 QTextList *list = static_cast<QTextList *>(d->priv->createObject(format)); |
|
1970 QTextBlockFormat modifier; |
|
1971 modifier.setObjectIndex(list->objectIndex()); |
|
1972 mergeBlockFormat(modifier); |
|
1973 return list; |
|
1974 } |
|
1975 |
|
1976 /*! |
|
1977 \overload |
|
1978 |
|
1979 Creates and returns a new list with the given \a style, making the |
|
1980 cursor's current paragraph the first list item. |
|
1981 |
|
1982 The style to be used is defined by the QTextListFormat::Style enum. |
|
1983 |
|
1984 \sa insertList() currentList() |
|
1985 */ |
|
1986 QTextList *QTextCursor::createList(QTextListFormat::Style style) |
|
1987 { |
|
1988 QTextListFormat fmt; |
|
1989 fmt.setStyle(style); |
|
1990 return createList(fmt); |
|
1991 } |
|
1992 |
|
1993 /*! |
|
1994 Returns the current list if the cursor position() is inside a |
|
1995 block that is part of a list; otherwise returns 0. |
|
1996 |
|
1997 \sa insertList() createList() |
|
1998 */ |
|
1999 QTextList *QTextCursor::currentList() const |
|
2000 { |
|
2001 if (!d || !d->priv) |
|
2002 return 0; |
|
2003 |
|
2004 QTextBlockFormat b = blockFormat(); |
|
2005 QTextObject *o = d->priv->objectForFormat(b); |
|
2006 return qobject_cast<QTextList *>(o); |
|
2007 } |
|
2008 |
|
2009 /*! |
|
2010 \fn QTextTable *QTextCursor::insertTable(int rows, int columns) |
|
2011 |
|
2012 \overload |
|
2013 |
|
2014 Creates a new table with the given number of \a rows and \a columns, |
|
2015 inserts it at the current cursor position() in the document, and returns |
|
2016 the table object. The cursor is moved to the beginning of the first cell. |
|
2017 |
|
2018 There must be at least one row and one column in the table. |
|
2019 |
|
2020 \sa currentTable() |
|
2021 */ |
|
2022 QTextTable *QTextCursor::insertTable(int rows, int cols) |
|
2023 { |
|
2024 return insertTable(rows, cols, QTextTableFormat()); |
|
2025 } |
|
2026 |
|
2027 /*! |
|
2028 \fn QTextTable *QTextCursor::insertTable(int rows, int columns, const QTextTableFormat &format) |
|
2029 |
|
2030 Creates a new table with the given number of \a rows and \a columns |
|
2031 in the specified \a format, inserts it at the current cursor position() |
|
2032 in the document, and returns the table object. The cursor is moved to |
|
2033 the beginning of the first cell. |
|
2034 |
|
2035 There must be at least one row and one column in the table. |
|
2036 |
|
2037 \sa currentTable() |
|
2038 */ |
|
2039 QTextTable *QTextCursor::insertTable(int rows, int cols, const QTextTableFormat &format) |
|
2040 { |
|
2041 if(!d || !d->priv || rows == 0 || cols == 0) |
|
2042 return 0; |
|
2043 |
|
2044 int pos = d->position; |
|
2045 QTextTable *t = QTextTablePrivate::createTable(d->priv, d->position, rows, cols, format); |
|
2046 d->setPosition(pos+1); |
|
2047 // ##### what should we do if we have a selection? |
|
2048 d->anchor = d->position; |
|
2049 d->adjusted_anchor = d->anchor; |
|
2050 return t; |
|
2051 } |
|
2052 |
|
2053 /*! |
|
2054 Returns a pointer to the current table if the cursor position() |
|
2055 is inside a block that is part of a table; otherwise returns 0. |
|
2056 |
|
2057 \sa insertTable() |
|
2058 */ |
|
2059 QTextTable *QTextCursor::currentTable() const |
|
2060 { |
|
2061 if(!d || !d->priv) |
|
2062 return 0; |
|
2063 |
|
2064 QTextFrame *frame = d->priv->frameAt(d->position); |
|
2065 while (frame) { |
|
2066 QTextTable *table = qobject_cast<QTextTable *>(frame); |
|
2067 if (table) |
|
2068 return table; |
|
2069 frame = frame->parentFrame(); |
|
2070 } |
|
2071 return 0; |
|
2072 } |
|
2073 |
|
2074 /*! |
|
2075 Inserts a frame with the given \a format at the current cursor position(), |
|
2076 moves the cursor position() inside the frame, and returns the frame. |
|
2077 |
|
2078 If the cursor holds a selection, the whole selection is moved inside the |
|
2079 frame. |
|
2080 |
|
2081 \sa hasSelection() |
|
2082 */ |
|
2083 QTextFrame *QTextCursor::insertFrame(const QTextFrameFormat &format) |
|
2084 { |
|
2085 if (!d || !d->priv) |
|
2086 return 0; |
|
2087 |
|
2088 return d->priv->insertFrame(selectionStart(), selectionEnd(), format); |
|
2089 } |
|
2090 |
|
2091 /*! |
|
2092 Returns a pointer to the current frame. Returns 0 if the cursor is invalid. |
|
2093 |
|
2094 \sa insertFrame() |
|
2095 */ |
|
2096 QTextFrame *QTextCursor::currentFrame() const |
|
2097 { |
|
2098 if(!d || !d->priv) |
|
2099 return 0; |
|
2100 |
|
2101 return d->priv->frameAt(d->position); |
|
2102 } |
|
2103 |
|
2104 |
|
2105 /*! |
|
2106 Inserts the text \a fragment at the current position(). |
|
2107 */ |
|
2108 void QTextCursor::insertFragment(const QTextDocumentFragment &fragment) |
|
2109 { |
|
2110 if (!d || !d->priv || fragment.isEmpty()) |
|
2111 return; |
|
2112 |
|
2113 d->priv->beginEditBlock(); |
|
2114 d->remove(); |
|
2115 fragment.d->insert(*this); |
|
2116 d->priv->endEditBlock(); |
|
2117 |
|
2118 if (fragment.d && fragment.d->doc) |
|
2119 d->priv->mergeCachedResources(fragment.d->doc->docHandle()); |
|
2120 } |
|
2121 |
|
2122 /*! |
|
2123 \since 4.2 |
|
2124 Inserts the text \a html at the current position(). The text is interpreted as |
|
2125 HTML. |
|
2126 |
|
2127 \note When using this function with a style sheet, the style sheet will |
|
2128 only apply to the current block in the document. In order to apply a style |
|
2129 sheet throughout a document, use QTextDocument::setDefaultStyleSheet() |
|
2130 instead. |
|
2131 */ |
|
2132 |
|
2133 #ifndef QT_NO_TEXTHTMLPARSER |
|
2134 |
|
2135 void QTextCursor::insertHtml(const QString &html) |
|
2136 { |
|
2137 if (!d || !d->priv) |
|
2138 return; |
|
2139 QTextDocumentFragment fragment = QTextDocumentFragment::fromHtml(html, d->priv->document()); |
|
2140 insertFragment(fragment); |
|
2141 } |
|
2142 |
|
2143 #endif // QT_NO_TEXTHTMLPARSER |
|
2144 |
|
2145 /*! |
|
2146 \overload |
|
2147 \since 4.2 |
|
2148 |
|
2149 Inserts the image defined by the given \a format at the cursor's current position |
|
2150 with the specified \a alignment. |
|
2151 |
|
2152 \sa position() |
|
2153 */ |
|
2154 void QTextCursor::insertImage(const QTextImageFormat &format, QTextFrameFormat::Position alignment) |
|
2155 { |
|
2156 if (!d || !d->priv) |
|
2157 return; |
|
2158 |
|
2159 QTextFrameFormat ffmt; |
|
2160 ffmt.setPosition(alignment); |
|
2161 QTextObject *obj = d->priv->createObject(ffmt); |
|
2162 |
|
2163 QTextImageFormat fmt = format; |
|
2164 fmt.setObjectIndex(obj->objectIndex()); |
|
2165 |
|
2166 d->priv->beginEditBlock(); |
|
2167 d->remove(); |
|
2168 const int idx = d->priv->formatCollection()->indexForFormat(fmt); |
|
2169 d->priv->insert(d->position, QString(QChar(QChar::ObjectReplacementCharacter)), idx); |
|
2170 d->priv->endEditBlock(); |
|
2171 } |
|
2172 |
|
2173 /*! |
|
2174 Inserts the image defined by \a format at the current position(). |
|
2175 */ |
|
2176 void QTextCursor::insertImage(const QTextImageFormat &format) |
|
2177 { |
|
2178 insertText(QString(QChar::ObjectReplacementCharacter), format); |
|
2179 } |
|
2180 |
|
2181 /*! |
|
2182 \overload |
|
2183 |
|
2184 Convenience method for inserting the image with the given \a name at the |
|
2185 current position(). |
|
2186 |
|
2187 \snippet doc/src/snippets/code/src_gui_text_qtextcursor.cpp 1 |
|
2188 */ |
|
2189 void QTextCursor::insertImage(const QString &name) |
|
2190 { |
|
2191 QTextImageFormat format; |
|
2192 format.setName(name); |
|
2193 insertImage(format); |
|
2194 } |
|
2195 |
|
2196 /*! |
|
2197 \since 4.5 |
|
2198 \overload |
|
2199 |
|
2200 Convenience function for inserting the given \a image with an optional |
|
2201 \a name at the current position(). |
|
2202 */ |
|
2203 void QTextCursor::insertImage(const QImage &image, const QString &name) |
|
2204 { |
|
2205 if (image.isNull()) { |
|
2206 qWarning("QTextCursor::insertImage: attempt to add an invalid image"); |
|
2207 return; |
|
2208 } |
|
2209 QString imageName = name; |
|
2210 if (name.isEmpty()) |
|
2211 imageName = QString::number(image.serialNumber()); |
|
2212 d->priv->document()->addResource(QTextDocument::ImageResource, QUrl(imageName), image); |
|
2213 QTextImageFormat format; |
|
2214 format.setName(imageName); |
|
2215 insertImage(format); |
|
2216 } |
|
2217 |
|
2218 /*! |
|
2219 \fn bool QTextCursor::operator!=(const QTextCursor &other) const |
|
2220 |
|
2221 Returns true if the \a other cursor is at a different position in |
|
2222 the document as this cursor; otherwise returns false. |
|
2223 */ |
|
2224 bool QTextCursor::operator!=(const QTextCursor &rhs) const |
|
2225 { |
|
2226 return !operator==(rhs); |
|
2227 } |
|
2228 |
|
2229 /*! |
|
2230 \fn bool QTextCursor::operator<(const QTextCursor &other) const |
|
2231 |
|
2232 Returns true if the \a other cursor is positioned later in the |
|
2233 document than this cursor; otherwise returns false. |
|
2234 */ |
|
2235 bool QTextCursor::operator<(const QTextCursor &rhs) const |
|
2236 { |
|
2237 if (!d) |
|
2238 return !!rhs.d; |
|
2239 |
|
2240 if (!rhs.d) |
|
2241 return false; |
|
2242 |
|
2243 Q_ASSERT_X(d->priv == rhs.d->priv, "QTextCursor::operator<", "cannot compare cursors attached to different documents"); |
|
2244 |
|
2245 return d->position < rhs.d->position; |
|
2246 } |
|
2247 |
|
2248 /*! |
|
2249 \fn bool QTextCursor::operator<=(const QTextCursor &other) const |
|
2250 |
|
2251 Returns true if the \a other cursor is positioned later or at the |
|
2252 same position in the document as this cursor; otherwise returns |
|
2253 false. |
|
2254 */ |
|
2255 bool QTextCursor::operator<=(const QTextCursor &rhs) const |
|
2256 { |
|
2257 if (!d) |
|
2258 return true; |
|
2259 |
|
2260 if (!rhs.d) |
|
2261 return false; |
|
2262 |
|
2263 Q_ASSERT_X(d->priv == rhs.d->priv, "QTextCursor::operator<=", "cannot compare cursors attached to different documents"); |
|
2264 |
|
2265 return d->position <= rhs.d->position; |
|
2266 } |
|
2267 |
|
2268 /*! |
|
2269 \fn bool QTextCursor::operator==(const QTextCursor &other) const |
|
2270 |
|
2271 Returns true if the \a other cursor is at the same position in the |
|
2272 document as this cursor; otherwise returns false. |
|
2273 */ |
|
2274 bool QTextCursor::operator==(const QTextCursor &rhs) const |
|
2275 { |
|
2276 if (!d) |
|
2277 return !rhs.d; |
|
2278 |
|
2279 if (!rhs.d) |
|
2280 return false; |
|
2281 |
|
2282 return d->position == rhs.d->position && d->priv == rhs.d->priv; |
|
2283 } |
|
2284 |
|
2285 /*! |
|
2286 \fn bool QTextCursor::operator>=(const QTextCursor &other) const |
|
2287 |
|
2288 Returns true if the \a other cursor is positioned earlier or at the |
|
2289 same position in the document as this cursor; otherwise returns |
|
2290 false. |
|
2291 */ |
|
2292 bool QTextCursor::operator>=(const QTextCursor &rhs) const |
|
2293 { |
|
2294 if (!d) |
|
2295 return false; |
|
2296 |
|
2297 if (!rhs.d) |
|
2298 return true; |
|
2299 |
|
2300 Q_ASSERT_X(d->priv == rhs.d->priv, "QTextCursor::operator>=", "cannot compare cursors attached to different documents"); |
|
2301 |
|
2302 return d->position >= rhs.d->position; |
|
2303 } |
|
2304 |
|
2305 /*! |
|
2306 \fn bool QTextCursor::operator>(const QTextCursor &other) const |
|
2307 |
|
2308 Returns true if the \a other cursor is positioned earlier in the |
|
2309 document than this cursor; otherwise returns false. |
|
2310 */ |
|
2311 bool QTextCursor::operator>(const QTextCursor &rhs) const |
|
2312 { |
|
2313 if (!d) |
|
2314 return false; |
|
2315 |
|
2316 if (!rhs.d) |
|
2317 return true; |
|
2318 |
|
2319 Q_ASSERT_X(d->priv == rhs.d->priv, "QTextCursor::operator>", "cannot compare cursors attached to different documents"); |
|
2320 |
|
2321 return d->position > rhs.d->position; |
|
2322 } |
|
2323 |
|
2324 /*! |
|
2325 Indicates the start of a block of editing operations on the |
|
2326 document that should appear as a single operation from an |
|
2327 undo/redo point of view. |
|
2328 |
|
2329 For example: |
|
2330 |
|
2331 \snippet doc/src/snippets/code/src_gui_text_qtextcursor.cpp 2 |
|
2332 |
|
2333 The call to undo() will cause both insertions to be undone, |
|
2334 causing both "World" and "Hello" to be removed. |
|
2335 |
|
2336 It is possible to nest calls to beginEditBlock and endEditBlock. The |
|
2337 top-most pair will determine the scope of the undo/redo operation. |
|
2338 |
|
2339 \sa endEditBlock() |
|
2340 */ |
|
2341 void QTextCursor::beginEditBlock() |
|
2342 { |
|
2343 if (!d || !d->priv) |
|
2344 return; |
|
2345 |
|
2346 d->priv->beginEditBlock(); |
|
2347 } |
|
2348 |
|
2349 /*! |
|
2350 Like beginEditBlock() indicates the start of a block of editing operations |
|
2351 that should appear as a single operation for undo/redo. However unlike |
|
2352 beginEditBlock() it does not start a new block but reverses the previous call to |
|
2353 endEditBlock() and therefore makes following operations part of the previous edit block created. |
|
2354 |
|
2355 For example: |
|
2356 |
|
2357 \snippet doc/src/snippets/code/src_gui_text_qtextcursor.cpp 3 |
|
2358 |
|
2359 The call to undo() will cause all three insertions to be undone. |
|
2360 |
|
2361 \sa beginEditBlock(), endEditBlock() |
|
2362 */ |
|
2363 void QTextCursor::joinPreviousEditBlock() |
|
2364 { |
|
2365 if (!d || !d->priv) |
|
2366 return; |
|
2367 |
|
2368 d->priv->joinPreviousEditBlock(); |
|
2369 } |
|
2370 |
|
2371 /*! |
|
2372 Indicates the end of a block of editing operations on the document |
|
2373 that should appear as a single operation from an undo/redo point |
|
2374 of view. |
|
2375 |
|
2376 \sa beginEditBlock() |
|
2377 */ |
|
2378 |
|
2379 void QTextCursor::endEditBlock() |
|
2380 { |
|
2381 if (!d || !d->priv) |
|
2382 return; |
|
2383 |
|
2384 d->priv->endEditBlock(); |
|
2385 } |
|
2386 |
|
2387 /*! |
|
2388 Returns true if this cursor and \a other are copies of each other, i.e. |
|
2389 one of them was created as a copy of the other and neither has moved since. |
|
2390 This is much stricter than equality. |
|
2391 |
|
2392 \sa operator=() operator==() |
|
2393 */ |
|
2394 bool QTextCursor::isCopyOf(const QTextCursor &other) const |
|
2395 { |
|
2396 return d == other.d; |
|
2397 } |
|
2398 |
|
2399 /*! |
|
2400 \since 4.2 |
|
2401 Returns the number of the block the cursor is in, or 0 if the cursor is invalid. |
|
2402 |
|
2403 Note that this function only makes sense in documents without complex objects such |
|
2404 as tables or frames. |
|
2405 */ |
|
2406 int QTextCursor::blockNumber() const |
|
2407 { |
|
2408 if (!d || !d->priv) |
|
2409 return 0; |
|
2410 |
|
2411 return d->block().blockNumber(); |
|
2412 } |
|
2413 |
|
2414 /*! |
|
2415 \since 4.2 |
|
2416 Returns the position of the cursor within its containing line. |
|
2417 */ |
|
2418 int QTextCursor::columnNumber() const |
|
2419 { |
|
2420 if (!d || !d->priv) |
|
2421 return 0; |
|
2422 |
|
2423 QTextBlock block = d->block(); |
|
2424 if (!block.isValid()) |
|
2425 return 0; |
|
2426 |
|
2427 const QTextLayout *layout = d->blockLayout(block); |
|
2428 |
|
2429 const int relativePos = d->position - block.position(); |
|
2430 |
|
2431 if (layout->lineCount() == 0) |
|
2432 return relativePos; |
|
2433 |
|
2434 QTextLine line = layout->lineForTextPosition(relativePos); |
|
2435 if (!line.isValid()) |
|
2436 return 0; |
|
2437 return relativePos - line.textStart(); |
|
2438 } |
|
2439 |
|
2440 /*! |
|
2441 \since 4.5 |
|
2442 Returns the document this cursor is associated with. |
|
2443 */ |
|
2444 QTextDocument *QTextCursor::document() const |
|
2445 { |
|
2446 if (d->priv) |
|
2447 return d->priv->document(); |
|
2448 return 0; // document went away |
|
2449 } |
|
2450 |
|
2451 QT_END_NAMESPACE |