author | Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com> |
Fri, 19 Feb 2010 23:40:16 +0200 | |
branch | RCL_3 |
changeset 4 | 3b1da2848fc7 |
parent 0 | 1918ee327afb |
permissions | -rw-r--r-- |
0 | 1 |
/**************************************************************************** |
2 |
** |
|
4
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
3 |
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
0 | 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 |
||
4
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
865 |
Text cursors are objects that are used to access and modify the |
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
866 |
contents and underlying structure of text documents via a |
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
867 |
programming interface that mimics the behavior of a cursor in a |
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
868 |
text editor. QTextCursor contains information about both the |
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
869 |
cursor's position within a QTextDocument and any selection that it |
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
870 |
has made. |
0 | 871 |
|
872 |
QTextCursor is modeled on the way a text cursor behaves in a text |
|
4
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
873 |
editor, providing a programmatic means of performing standard |
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
874 |
actions through the user interface. A document can be thought of |
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
875 |
as a single string of characters. The cursor's current position() |
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
876 |
then is always either \e between two consecutive characters in the |
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
877 |
string, or else \e before the very first character or \e after the |
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
878 |
very last character in the string. Documents can also contain |
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
879 |
tables, lists, images, and other objects in addition to text but, |
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
880 |
from the developer's point of view, the document can be treated as |
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
881 |
one long string. Some portions of that string can be considered |
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
882 |
to lie within particular blocks (e.g. paragraphs), or within a |
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
883 |
table's cell, or a list's item, or other structural elements. When |
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
884 |
we refer to "current character" we mean the character immediately |
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
885 |
\e before the cursor position() in the document. Similarly, the |
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
886 |
"current block" is the block that contains the cursor position(). |
0 | 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 |
|
4
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
944 |
writing this is between any two consecutive characters in the |
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
945 |
text, before the first character, or after the last character. In |
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
946 |
some other writing systems cursor movements are limited to |
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
947 |
"clusters" (e.g. a syllable in Devanagari, or a base letter plus |
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
948 |
diacritics). Functions such as movePosition() and deleteChar() |
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
949 |
limit cursor movement to these valid positions. |
0 | 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 |
/*! |
|
4
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1744 |
Returns the format of the character immediately before the cursor |
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1745 |
position(). If the cursor is positioned at the beginning of a text |
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1746 |
block that is not empty then the format of the character |
0 | 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 |