|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the QtGui module of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include "qplaintextedit_p.h" |
|
43 |
|
44 |
|
45 #include <qfont.h> |
|
46 #include <qpainter.h> |
|
47 #include <qevent.h> |
|
48 #include <qdebug.h> |
|
49 #include <qmime.h> |
|
50 #include <qdrag.h> |
|
51 #include <qclipboard.h> |
|
52 #include <qmenu.h> |
|
53 #include <qstyle.h> |
|
54 #include <qtimer.h> |
|
55 #include "private/qtextdocumentlayout_p.h" |
|
56 #include "private/qabstracttextdocumentlayout_p.h" |
|
57 #include "qtextdocument.h" |
|
58 #include "private/qtextdocument_p.h" |
|
59 #include "qtextlist.h" |
|
60 #include "private/qtextcontrol_p.h" |
|
61 |
|
62 #include <qtextformat.h> |
|
63 #include <qdatetime.h> |
|
64 #include <qapplication.h> |
|
65 #include <limits.h> |
|
66 #include <qtexttable.h> |
|
67 #include <qvariant.h> |
|
68 #include <qinputcontext.h> |
|
69 |
|
70 #ifndef QT_NO_TEXTEDIT |
|
71 |
|
72 QT_BEGIN_NAMESPACE |
|
73 |
|
74 static inline bool shouldEnableInputMethod(QPlainTextEdit *plaintextedit) |
|
75 { |
|
76 return !plaintextedit->isReadOnly(); |
|
77 } |
|
78 |
|
79 class QPlainTextDocumentLayoutPrivate : public QAbstractTextDocumentLayoutPrivate |
|
80 { |
|
81 Q_DECLARE_PUBLIC(QPlainTextDocumentLayout) |
|
82 public: |
|
83 QPlainTextDocumentLayoutPrivate() { |
|
84 mainViewPrivate = 0; |
|
85 width = 0; |
|
86 maximumWidth = 0; |
|
87 maximumWidthBlockNumber = 0; |
|
88 blockCount = 1; |
|
89 blockUpdate = blockDocumentSizeChanged = false; |
|
90 cursorWidth = 1; |
|
91 textLayoutFlags = 0; |
|
92 } |
|
93 |
|
94 qreal width; |
|
95 qreal maximumWidth; |
|
96 int maximumWidthBlockNumber; |
|
97 int blockCount; |
|
98 QPlainTextEditPrivate *mainViewPrivate; |
|
99 bool blockUpdate; |
|
100 bool blockDocumentSizeChanged; |
|
101 int cursorWidth; |
|
102 int textLayoutFlags; |
|
103 |
|
104 void layoutBlock(const QTextBlock &block); |
|
105 qreal blockWidth(const QTextBlock &block); |
|
106 |
|
107 void relayout(); |
|
108 }; |
|
109 |
|
110 |
|
111 |
|
112 /*! \class QPlainTextDocumentLayout |
|
113 \since 4.4 |
|
114 \brief The QPlainTextDocumentLayout class implements a plain text layout for QTextDocument |
|
115 |
|
116 \ingroup richtext-processing |
|
117 |
|
118 A QPlainTextDocumentLayout is required for text documents that can |
|
119 be display or edited in a QPlainTextEdit. See |
|
120 QTextDocument::setDocumentLayout(). |
|
121 |
|
122 QPlainTextDocumentLayout uses the QAbstractTextDocumentLayout API |
|
123 that QTextDocument requires, but redefines it partially in order to |
|
124 support plain text better. For instances, it does not operate on |
|
125 vertical pixels, but on paragraphs (called blocks) instead. The |
|
126 height of a document is identical to the number of paragraphs it |
|
127 contains. The layout also doesn't support tables or nested frames, |
|
128 or any sort of advanced text layout that goes beyond a list of |
|
129 paragraphs with syntax highlighting. |
|
130 |
|
131 */ |
|
132 |
|
133 |
|
134 |
|
135 /*! |
|
136 Constructs a plain text document layout for the text \a document. |
|
137 */ |
|
138 QPlainTextDocumentLayout::QPlainTextDocumentLayout(QTextDocument *document) |
|
139 :QAbstractTextDocumentLayout(* new QPlainTextDocumentLayoutPrivate, document) { |
|
140 } |
|
141 /*! |
|
142 Destructs a plain text document layout. |
|
143 */ |
|
144 QPlainTextDocumentLayout::~QPlainTextDocumentLayout() {} |
|
145 |
|
146 |
|
147 /*! |
|
148 \reimp |
|
149 */ |
|
150 void QPlainTextDocumentLayout::draw(QPainter *, const PaintContext &) |
|
151 { |
|
152 } |
|
153 |
|
154 /*! |
|
155 \reimp |
|
156 */ |
|
157 int QPlainTextDocumentLayout::hitTest(const QPointF &, Qt::HitTestAccuracy ) const |
|
158 { |
|
159 // this function is used from |
|
160 // QAbstractTextDocumentLayout::anchorAt(), but is not |
|
161 // implementable in a plain text document layout, because the |
|
162 // layout depends on the top block and top line which depends on |
|
163 // the view |
|
164 return -1; |
|
165 } |
|
166 |
|
167 /*! |
|
168 \reimp |
|
169 */ |
|
170 int QPlainTextDocumentLayout::pageCount() const |
|
171 { return 1; } |
|
172 |
|
173 /*! |
|
174 \reimp |
|
175 */ |
|
176 QSizeF QPlainTextDocumentLayout::documentSize() const |
|
177 { |
|
178 Q_D(const QPlainTextDocumentLayout); |
|
179 return QSizeF(d->maximumWidth, document()->lineCount()); |
|
180 } |
|
181 |
|
182 /*! |
|
183 \reimp |
|
184 */ |
|
185 QRectF QPlainTextDocumentLayout::frameBoundingRect(QTextFrame *) const |
|
186 { |
|
187 Q_D(const QPlainTextDocumentLayout); |
|
188 return QRectF(0, 0, qMax(d->width, d->maximumWidth), qreal(INT_MAX)); |
|
189 } |
|
190 |
|
191 /*! |
|
192 \reimp |
|
193 */ |
|
194 QRectF QPlainTextDocumentLayout::blockBoundingRect(const QTextBlock &block) const |
|
195 { |
|
196 if (!block.isValid()) { return QRectF(); } |
|
197 QTextLayout *tl = block.layout(); |
|
198 if (!tl->lineCount()) |
|
199 const_cast<QPlainTextDocumentLayout*>(this)->layoutBlock(block); |
|
200 QRectF br; |
|
201 if (block.isVisible()) { |
|
202 br = QRectF(QPointF(0, 0), tl->boundingRect().bottomRight()); |
|
203 if (tl->lineCount() == 1) |
|
204 br.setWidth(qMax(br.width(), tl->lineAt(0).naturalTextWidth())); |
|
205 qreal margin = document()->documentMargin(); |
|
206 br.adjust(0, 0, margin, 0); |
|
207 if (!block.next().isValid()) |
|
208 br.adjust(0, 0, 0, margin); |
|
209 } |
|
210 return br; |
|
211 |
|
212 } |
|
213 |
|
214 /*! |
|
215 Ensures that \a block has a valid layout |
|
216 */ |
|
217 void QPlainTextDocumentLayout::ensureBlockLayout(const QTextBlock &block) const |
|
218 { |
|
219 if (!block.isValid()) |
|
220 return; |
|
221 QTextLayout *tl = block.layout(); |
|
222 if (!tl->lineCount()) |
|
223 const_cast<QPlainTextDocumentLayout*>(this)->layoutBlock(block); |
|
224 } |
|
225 |
|
226 |
|
227 /*! \property QPlainTextDocumentLayout::cursorWidth |
|
228 |
|
229 This property specifies the width of the cursor in pixels. The default value is 1. |
|
230 */ |
|
231 void QPlainTextDocumentLayout::setCursorWidth(int width) |
|
232 { |
|
233 Q_D(QPlainTextDocumentLayout); |
|
234 d->cursorWidth = width; |
|
235 } |
|
236 |
|
237 int QPlainTextDocumentLayout::cursorWidth() const |
|
238 { |
|
239 Q_D(const QPlainTextDocumentLayout); |
|
240 return d->cursorWidth; |
|
241 } |
|
242 |
|
243 QPlainTextDocumentLayoutPrivate *QPlainTextDocumentLayout::priv() const |
|
244 { |
|
245 Q_D(const QPlainTextDocumentLayout); |
|
246 return const_cast<QPlainTextDocumentLayoutPrivate*>(d); |
|
247 } |
|
248 |
|
249 |
|
250 /*! |
|
251 |
|
252 Requests a complete update on all views. |
|
253 */ |
|
254 void QPlainTextDocumentLayout::requestUpdate() |
|
255 { |
|
256 emit update(QRectF(0., -document()->documentMargin(), 1000000000., 1000000000.)); |
|
257 } |
|
258 |
|
259 |
|
260 void QPlainTextDocumentLayout::setTextWidth(qreal newWidth) |
|
261 { |
|
262 Q_D(QPlainTextDocumentLayout); |
|
263 d->width = d->maximumWidth = newWidth; |
|
264 d->relayout(); |
|
265 } |
|
266 |
|
267 qreal QPlainTextDocumentLayout::textWidth() const |
|
268 { |
|
269 Q_D(const QPlainTextDocumentLayout); |
|
270 return d->width; |
|
271 } |
|
272 |
|
273 void QPlainTextDocumentLayoutPrivate::relayout() |
|
274 { |
|
275 Q_Q(QPlainTextDocumentLayout); |
|
276 QTextBlock block = q->document()->firstBlock(); |
|
277 while (block.isValid()) { |
|
278 block.layout()->clearLayout(); |
|
279 block.setLineCount(block.isVisible() ? 1 : 0); |
|
280 block = block.next(); |
|
281 } |
|
282 emit q->update(); |
|
283 } |
|
284 |
|
285 |
|
286 /*! \reimp |
|
287 */ |
|
288 void QPlainTextDocumentLayout::documentChanged(int from, int /*charsRemoved*/, int charsAdded) |
|
289 { |
|
290 Q_D(QPlainTextDocumentLayout); |
|
291 QTextDocument *doc = document(); |
|
292 int newBlockCount = doc->blockCount(); |
|
293 |
|
294 QTextBlock changeStartBlock = doc->findBlock(from); |
|
295 QTextBlock changeEndBlock = doc->findBlock(qMax(0, from + charsAdded - 1)); |
|
296 |
|
297 if (changeStartBlock == changeEndBlock && newBlockCount == d->blockCount) { |
|
298 QTextBlock block = changeStartBlock; |
|
299 int blockLineCount = block.layout()->lineCount(); |
|
300 if (block.isValid() && blockLineCount) { |
|
301 QRectF oldBr = blockBoundingRect(block); |
|
302 layoutBlock(block); |
|
303 QRectF newBr = blockBoundingRect(block); |
|
304 if (newBr.height() == oldBr.height()) { |
|
305 if (!d->blockUpdate) |
|
306 emit updateBlock(block); |
|
307 return; |
|
308 } |
|
309 } |
|
310 } else { |
|
311 QTextBlock block = changeStartBlock; |
|
312 do { |
|
313 block.clearLayout(); |
|
314 if (block == changeEndBlock) |
|
315 break; |
|
316 block = block.next(); |
|
317 } while(block.isValid()); |
|
318 } |
|
319 |
|
320 if (newBlockCount != d->blockCount) { |
|
321 |
|
322 int changeEnd = changeEndBlock.blockNumber(); |
|
323 int blockDiff = newBlockCount - d->blockCount; |
|
324 int oldChangeEnd = changeEnd - blockDiff; |
|
325 |
|
326 if (d->maximumWidthBlockNumber > oldChangeEnd) |
|
327 d->maximumWidthBlockNumber += blockDiff; |
|
328 |
|
329 d->blockCount = newBlockCount; |
|
330 if (d->blockCount == 1) |
|
331 d->maximumWidth = blockWidth(doc->firstBlock()); |
|
332 |
|
333 if (!d->blockDocumentSizeChanged) |
|
334 emit documentSizeChanged(documentSize()); |
|
335 |
|
336 if (blockDiff == 1 && changeEnd == newBlockCount -1 ) { |
|
337 if (!d->blockUpdate) { |
|
338 QTextBlock b = changeStartBlock; |
|
339 for(;;) { |
|
340 emit updateBlock(b); |
|
341 if (b == changeEndBlock) |
|
342 break; |
|
343 b = b.next(); |
|
344 } |
|
345 } |
|
346 return; |
|
347 } |
|
348 } |
|
349 |
|
350 if (!d->blockUpdate) |
|
351 emit update(QRectF(0., -doc->documentMargin(), 1000000000., 1000000000.)); // optimization potential |
|
352 } |
|
353 |
|
354 |
|
355 void QPlainTextDocumentLayout::layoutBlock(const QTextBlock &block) |
|
356 { |
|
357 Q_D(QPlainTextDocumentLayout); |
|
358 QTextDocument *doc = document(); |
|
359 qreal margin = doc->documentMargin(); |
|
360 qreal blockMaximumWidth = 0; |
|
361 |
|
362 qreal height = 0; |
|
363 QTextLayout *tl = block.layout(); |
|
364 QTextOption option = doc->defaultTextOption(); |
|
365 tl->setTextOption(option); |
|
366 |
|
367 int extraMargin = 0; |
|
368 if (option.flags() & QTextOption::AddSpaceForLineAndParagraphSeparators) { |
|
369 QFontMetrics fm(block.charFormat().font()); |
|
370 extraMargin += fm.width(QChar(0x21B5)); |
|
371 } |
|
372 tl->beginLayout(); |
|
373 qreal availableWidth = d->width; |
|
374 if (availableWidth <= 0) { |
|
375 availableWidth = qreal(INT_MAX); // similar to text edit with pageSize.width == 0 |
|
376 } |
|
377 availableWidth -= 2*margin + extraMargin; |
|
378 while (1) { |
|
379 QTextLine line = tl->createLine(); |
|
380 if (!line.isValid()) |
|
381 break; |
|
382 line.setLeadingIncluded(true); |
|
383 line.setLineWidth(availableWidth); |
|
384 line.setPosition(QPointF(margin, height)); |
|
385 height += line.height(); |
|
386 blockMaximumWidth = qMax(blockMaximumWidth, line.naturalTextWidth() + 2*margin); |
|
387 } |
|
388 tl->endLayout(); |
|
389 |
|
390 int previousLineCount = doc->lineCount(); |
|
391 const_cast<QTextBlock&>(block).setLineCount(block.isVisible() ? tl->lineCount() : 0); |
|
392 int lineCount = doc->lineCount(); |
|
393 |
|
394 bool emitDocumentSizeChanged = previousLineCount != lineCount; |
|
395 if (blockMaximumWidth > d->maximumWidth) { |
|
396 // new longest line |
|
397 d->maximumWidth = blockMaximumWidth; |
|
398 d->maximumWidthBlockNumber = block.blockNumber(); |
|
399 emitDocumentSizeChanged = true; |
|
400 } else if (block.blockNumber() == d->maximumWidthBlockNumber && blockMaximumWidth < d->maximumWidth) { |
|
401 // longest line shrinking |
|
402 QTextBlock b = doc->firstBlock(); |
|
403 d->maximumWidth = 0; |
|
404 QTextBlock maximumBlock; |
|
405 while (b.isValid()) { |
|
406 qreal blockMaximumWidth = blockWidth(b); |
|
407 if (blockMaximumWidth > d->maximumWidth) { |
|
408 d->maximumWidth = blockMaximumWidth; |
|
409 maximumBlock = b; |
|
410 } |
|
411 b = b.next(); |
|
412 } |
|
413 if (maximumBlock.isValid()) { |
|
414 d->maximumWidthBlockNumber = maximumBlock.blockNumber(); |
|
415 emitDocumentSizeChanged = true; |
|
416 } |
|
417 } |
|
418 if (emitDocumentSizeChanged && !d->blockDocumentSizeChanged) |
|
419 emit documentSizeChanged(documentSize()); |
|
420 } |
|
421 |
|
422 qreal QPlainTextDocumentLayout::blockWidth(const QTextBlock &block) |
|
423 { |
|
424 QTextLayout *layout = block.layout(); |
|
425 if (!layout->lineCount()) |
|
426 return 0; // only for layouted blocks |
|
427 qreal blockWidth = 0; |
|
428 for (int i = 0; i < layout->lineCount(); ++i) { |
|
429 QTextLine line = layout->lineAt(i); |
|
430 blockWidth = qMax(line.naturalTextWidth() + 8, blockWidth); |
|
431 } |
|
432 return blockWidth; |
|
433 } |
|
434 |
|
435 |
|
436 QPlainTextEditControl::QPlainTextEditControl(QPlainTextEdit *parent) |
|
437 : QTextControl(parent), textEdit(parent), |
|
438 topBlock(0) |
|
439 { |
|
440 setAcceptRichText(false); |
|
441 } |
|
442 |
|
443 void QPlainTextEditPrivate::_q_cursorPositionChanged() |
|
444 { |
|
445 pageUpDownLastCursorYIsValid = false; |
|
446 } |
|
447 |
|
448 void QPlainTextEditPrivate::_q_verticalScrollbarActionTriggered(int action) { |
|
449 if (action == QAbstractSlider::SliderPageStepAdd) { |
|
450 pageUpDown(QTextCursor::Down, QTextCursor::MoveAnchor, false); |
|
451 } else if (action == QAbstractSlider::SliderPageStepSub) { |
|
452 pageUpDown(QTextCursor::Up, QTextCursor::MoveAnchor, false); |
|
453 } |
|
454 } |
|
455 |
|
456 QMimeData *QPlainTextEditControl::createMimeDataFromSelection() const { |
|
457 QPlainTextEdit *ed = qobject_cast<QPlainTextEdit *>(parent()); |
|
458 if (!ed) |
|
459 return QTextControl::createMimeDataFromSelection(); |
|
460 return ed->createMimeDataFromSelection(); |
|
461 } |
|
462 bool QPlainTextEditControl::canInsertFromMimeData(const QMimeData *source) const { |
|
463 QPlainTextEdit *ed = qobject_cast<QPlainTextEdit *>(parent()); |
|
464 if (!ed) |
|
465 return QTextControl::canInsertFromMimeData(source); |
|
466 return ed->canInsertFromMimeData(source); |
|
467 } |
|
468 void QPlainTextEditControl::insertFromMimeData(const QMimeData *source) { |
|
469 QPlainTextEdit *ed = qobject_cast<QPlainTextEdit *>(parent()); |
|
470 if (!ed) |
|
471 QTextControl::insertFromMimeData(source); |
|
472 else |
|
473 ed->insertFromMimeData(source); |
|
474 } |
|
475 |
|
476 int QPlainTextEditPrivate::verticalOffset(int topBlock, int topLine) const |
|
477 { |
|
478 qreal offset = 0; |
|
479 QTextDocument *doc = control->document(); |
|
480 |
|
481 if (topLine) { |
|
482 QTextBlock currentBlock = doc->findBlockByNumber(topBlock); |
|
483 QPlainTextDocumentLayout *documentLayout = qobject_cast<QPlainTextDocumentLayout*>(doc->documentLayout()); |
|
484 Q_ASSERT(documentLayout); |
|
485 QRectF r = documentLayout->blockBoundingRect(currentBlock); |
|
486 QTextLayout *layout = currentBlock.layout(); |
|
487 if (layout && topLine <= layout->lineCount()) { |
|
488 QTextLine line = layout->lineAt(topLine - 1); |
|
489 const QRectF lr = line.naturalTextRect(); |
|
490 offset = lr.bottom(); |
|
491 } |
|
492 } |
|
493 if (topBlock == 0 && topLine == 0) |
|
494 offset -= doc->documentMargin(); // top margin |
|
495 return (int)offset; |
|
496 } |
|
497 |
|
498 |
|
499 int QPlainTextEditPrivate::verticalOffset() const { |
|
500 return verticalOffset(control->topBlock, topLine); |
|
501 } |
|
502 |
|
503 |
|
504 QTextBlock QPlainTextEditControl::firstVisibleBlock() const |
|
505 { |
|
506 return document()->findBlockByNumber(topBlock); |
|
507 } |
|
508 |
|
509 |
|
510 |
|
511 int QPlainTextEditControl::hitTest(const QPointF &point, Qt::HitTestAccuracy ) const { |
|
512 int currentBlockNumber = topBlock; |
|
513 QTextBlock currentBlock = document()->findBlockByNumber(currentBlockNumber); |
|
514 if (!currentBlock.isValid()) |
|
515 return -1; |
|
516 |
|
517 QPlainTextDocumentLayout *documentLayout = qobject_cast<QPlainTextDocumentLayout*>(document()->documentLayout()); |
|
518 Q_ASSERT(documentLayout); |
|
519 |
|
520 QPointF offset; |
|
521 QRectF r = documentLayout->blockBoundingRect(currentBlock); |
|
522 while (currentBlock.next().isValid() && r.bottom() + offset.y() <= point.y()) { |
|
523 offset.ry() += r.height(); |
|
524 currentBlock = currentBlock.next(); |
|
525 ++currentBlockNumber; |
|
526 r = documentLayout->blockBoundingRect(currentBlock); |
|
527 } |
|
528 while (currentBlock.previous().isValid() && r.top() + offset.y() > point.y()) { |
|
529 offset.ry() -= r.height(); |
|
530 currentBlock = currentBlock.previous(); |
|
531 --currentBlockNumber; |
|
532 r = documentLayout->blockBoundingRect(currentBlock); |
|
533 } |
|
534 |
|
535 |
|
536 if (!currentBlock.isValid()) |
|
537 return -1; |
|
538 QTextLayout *layout = currentBlock.layout(); |
|
539 int off = 0; |
|
540 QPointF pos = point - offset; |
|
541 for (int i = 0; i < layout->lineCount(); ++i) { |
|
542 QTextLine line = layout->lineAt(i); |
|
543 const QRectF lr = line.naturalTextRect(); |
|
544 if (lr.top() > pos.y()) { |
|
545 off = qMin(off, line.textStart()); |
|
546 } else if (lr.bottom() <= pos.y()) { |
|
547 off = qMax(off, line.textStart() + line.textLength()); |
|
548 } else { |
|
549 off = line.xToCursor(pos.x(), overwriteMode() ? |
|
550 QTextLine::CursorOnCharacter : QTextLine::CursorBetweenCharacters); |
|
551 break; |
|
552 } |
|
553 } |
|
554 |
|
555 return currentBlock.position() + off; |
|
556 } |
|
557 |
|
558 QRectF QPlainTextEditControl::blockBoundingRect(const QTextBlock &block) const { |
|
559 int currentBlockNumber = topBlock; |
|
560 int blockNumber = block.blockNumber(); |
|
561 QTextBlock currentBlock = document()->findBlockByNumber(currentBlockNumber); |
|
562 if (!currentBlock.isValid()) |
|
563 return QRectF(); |
|
564 Q_ASSERT(currentBlock.blockNumber() == currentBlockNumber); |
|
565 QTextDocument *doc = document(); |
|
566 QPlainTextDocumentLayout *documentLayout = qobject_cast<QPlainTextDocumentLayout*>(doc->documentLayout()); |
|
567 Q_ASSERT(documentLayout); |
|
568 |
|
569 QPointF offset; |
|
570 if (!block.isValid()) |
|
571 return QRectF(); |
|
572 QRectF r = documentLayout->blockBoundingRect(currentBlock); |
|
573 int maxVerticalOffset = r.height(); |
|
574 while (currentBlockNumber < blockNumber && offset.y() - maxVerticalOffset <= 2* textEdit->viewport()->height()) { |
|
575 offset.ry() += r.height(); |
|
576 currentBlock = currentBlock.next(); |
|
577 ++currentBlockNumber; |
|
578 if (!currentBlock.isVisible()) { |
|
579 currentBlock = doc->findBlockByLineNumber(currentBlock.firstLineNumber()); |
|
580 currentBlockNumber = currentBlock.blockNumber(); |
|
581 } |
|
582 r = documentLayout->blockBoundingRect(currentBlock); |
|
583 } |
|
584 while (currentBlockNumber > blockNumber && offset.y() + maxVerticalOffset >= -textEdit->viewport()->height()) { |
|
585 currentBlock = currentBlock.previous(); |
|
586 --currentBlockNumber; |
|
587 while (!currentBlock.isVisible()) { |
|
588 currentBlock = currentBlock.previous(); |
|
589 --currentBlockNumber; |
|
590 } |
|
591 if (!currentBlock.isValid()) |
|
592 break; |
|
593 |
|
594 r = documentLayout->blockBoundingRect(currentBlock); |
|
595 offset.ry() -= r.height(); |
|
596 } |
|
597 |
|
598 if (currentBlockNumber != blockNumber) { |
|
599 // fallback for blocks out of reach. Give it some geometry at |
|
600 // least, and ensure the layout is up to date. |
|
601 r = documentLayout->blockBoundingRect(block); |
|
602 if (currentBlockNumber > blockNumber) |
|
603 offset.ry() -= r.height(); |
|
604 } |
|
605 r.translate(offset); |
|
606 return r; |
|
607 } |
|
608 |
|
609 |
|
610 void QPlainTextEditPrivate::setTopLine(int visualTopLine, int dx) |
|
611 { |
|
612 QTextDocument *doc = control->document(); |
|
613 QTextBlock block = doc->findBlockByLineNumber(visualTopLine); |
|
614 int blockNumber = block.blockNumber(); |
|
615 int lineNumber = visualTopLine - block.firstLineNumber(); |
|
616 setTopBlock(blockNumber, lineNumber, dx); |
|
617 } |
|
618 |
|
619 void QPlainTextEditPrivate::setTopBlock(int blockNumber, int lineNumber, int dx) |
|
620 { |
|
621 Q_Q(QPlainTextEdit); |
|
622 blockNumber = qMax(0, blockNumber); |
|
623 lineNumber = qMax(0, lineNumber); |
|
624 QTextDocument *doc = control->document(); |
|
625 QTextBlock block = doc->findBlockByNumber(blockNumber); |
|
626 |
|
627 int newTopLine = block.firstLineNumber() + lineNumber; |
|
628 int maxTopLine = vbar->maximum(); |
|
629 |
|
630 if (newTopLine > maxTopLine) { |
|
631 block = doc->findBlockByLineNumber(maxTopLine); |
|
632 blockNumber = block.blockNumber(); |
|
633 lineNumber = maxTopLine - block.firstLineNumber(); |
|
634 } |
|
635 |
|
636 bool vbarSignalsBlocked = vbar->blockSignals(true); |
|
637 vbar->setValue(newTopLine); |
|
638 vbar->blockSignals(vbarSignalsBlocked); |
|
639 |
|
640 if (!dx && blockNumber == control->topBlock && lineNumber == topLine) |
|
641 return; |
|
642 |
|
643 if (viewport->updatesEnabled() && viewport->isVisible()) { |
|
644 int dy = 0; |
|
645 if (doc->findBlockByNumber(control->topBlock).isValid()) { |
|
646 dy = (int)(-q->blockBoundingGeometry(block).y()) |
|
647 + verticalOffset() - verticalOffset(blockNumber, lineNumber); |
|
648 } |
|
649 control->topBlock = blockNumber; |
|
650 topLine = lineNumber; |
|
651 if (dx || dy) |
|
652 viewport->scroll(q->isRightToLeft() ? -dx : dx, dy); |
|
653 else |
|
654 viewport->update(); |
|
655 emit q->updateRequest(viewport->rect(), dy); |
|
656 } else { |
|
657 control->topBlock = blockNumber; |
|
658 topLine = lineNumber; |
|
659 } |
|
660 |
|
661 } |
|
662 |
|
663 |
|
664 |
|
665 void QPlainTextEditPrivate::ensureVisible(int position, bool center, bool forceCenter) { |
|
666 Q_Q(QPlainTextEdit); |
|
667 QRectF visible = QRectF(viewport->rect()).translated(-q->contentOffset()); |
|
668 QTextBlock block = control->document()->findBlock(position); |
|
669 if (!block.isValid()) |
|
670 return; |
|
671 QRectF br = control->blockBoundingRect(block); |
|
672 if (!br.isValid()) |
|
673 return; |
|
674 QRectF lr = br; |
|
675 QTextLine line = block.layout()->lineForTextPosition(position - block.position()); |
|
676 Q_ASSERT(line.isValid()); |
|
677 lr = line.naturalTextRect().translated(br.topLeft()); |
|
678 |
|
679 if (lr.bottom() >= visible.bottom() || (center && lr.top() < visible.top()) || forceCenter){ |
|
680 |
|
681 qreal height = visible.height(); |
|
682 if (center) |
|
683 height /= 2; |
|
684 |
|
685 qreal h = center ? line.naturalTextRect().center().y() : line.naturalTextRect().bottom(); |
|
686 |
|
687 QTextBlock previousVisibleBlock = block; |
|
688 while (h < height && block.previous().isValid()) { |
|
689 previousVisibleBlock = block; |
|
690 do { |
|
691 block = block.previous(); |
|
692 } while (!block.isVisible() && block.previous().isValid()); |
|
693 h += q->blockBoundingRect(block).height(); |
|
694 } |
|
695 |
|
696 int l = 0; |
|
697 int lineCount = block.layout()->lineCount(); |
|
698 int voffset = verticalOffset(block.blockNumber(), 0); |
|
699 while (l < lineCount) { |
|
700 QRectF lineRect = block.layout()->lineAt(l).naturalTextRect(); |
|
701 if (h - voffset - lineRect.top() <= height) |
|
702 break; |
|
703 ++l; |
|
704 } |
|
705 |
|
706 if (l >= lineCount) { |
|
707 block = previousVisibleBlock; |
|
708 l = 0; |
|
709 } |
|
710 setTopBlock(block.blockNumber(), l); |
|
711 } else if (lr.top() < visible.top()) { |
|
712 setTopBlock(block.blockNumber(), line.lineNumber()); |
|
713 } |
|
714 |
|
715 } |
|
716 |
|
717 |
|
718 void QPlainTextEditPrivate::updateViewport() |
|
719 { |
|
720 Q_Q(QPlainTextEdit); |
|
721 viewport->update(); |
|
722 emit q->updateRequest(viewport->rect(), 0); |
|
723 } |
|
724 |
|
725 QPlainTextEditPrivate::QPlainTextEditPrivate() |
|
726 : control(0), |
|
727 tabChangesFocus(false), |
|
728 lineWrap(QPlainTextEdit::WidgetWidth), |
|
729 wordWrap(QTextOption::WrapAtWordBoundaryOrAnywhere), |
|
730 clickCausedFocus(0),topLine(0), |
|
731 pageUpDownLastCursorYIsValid(false) |
|
732 { |
|
733 showCursorOnInitialShow = true; |
|
734 backgroundVisible = false; |
|
735 centerOnScroll = false; |
|
736 inDrag = false; |
|
737 } |
|
738 |
|
739 |
|
740 void QPlainTextEditPrivate::init(const QString &txt) |
|
741 { |
|
742 Q_Q(QPlainTextEdit); |
|
743 control = new QPlainTextEditControl(q); |
|
744 |
|
745 QTextDocument *doc = new QTextDocument(control); |
|
746 QAbstractTextDocumentLayout *layout = new QPlainTextDocumentLayout(doc); |
|
747 doc->setDocumentLayout(layout); |
|
748 control->setDocument(doc); |
|
749 |
|
750 control->setPalette(q->palette()); |
|
751 |
|
752 QObject::connect(vbar, SIGNAL(actionTriggered(int)), q, SLOT(_q_verticalScrollbarActionTriggered(int))); |
|
753 |
|
754 QObject::connect(control, SIGNAL(microFocusChanged()), q, SLOT(updateMicroFocus())); |
|
755 QObject::connect(control, SIGNAL(documentSizeChanged(QSizeF)), q, SLOT(_q_adjustScrollbars())); |
|
756 QObject::connect(control, SIGNAL(blockCountChanged(int)), q, SIGNAL(blockCountChanged(int))); |
|
757 QObject::connect(control, SIGNAL(updateRequest(QRectF)), q, SLOT(_q_repaintContents(QRectF))); |
|
758 QObject::connect(control, SIGNAL(modificationChanged(bool)), q, SIGNAL(modificationChanged(bool))); |
|
759 |
|
760 QObject::connect(control, SIGNAL(textChanged()), q, SIGNAL(textChanged())); |
|
761 QObject::connect(control, SIGNAL(undoAvailable(bool)), q, SIGNAL(undoAvailable(bool))); |
|
762 QObject::connect(control, SIGNAL(redoAvailable(bool)), q, SIGNAL(redoAvailable(bool))); |
|
763 QObject::connect(control, SIGNAL(copyAvailable(bool)), q, SIGNAL(copyAvailable(bool))); |
|
764 QObject::connect(control, SIGNAL(selectionChanged()), q, SIGNAL(selectionChanged())); |
|
765 QObject::connect(control, SIGNAL(cursorPositionChanged()), q, SLOT(_q_cursorPositionChanged())); |
|
766 QObject::connect(control, SIGNAL(cursorPositionChanged()), q, SIGNAL(cursorPositionChanged())); |
|
767 |
|
768 QObject::connect(control, SIGNAL(textChanged()), q, SLOT(updateMicroFocus())); |
|
769 |
|
770 // set a null page size initially to avoid any relayouting until the textedit |
|
771 // is shown. relayoutDocument() will take care of setting the page size to the |
|
772 // viewport dimensions later. |
|
773 doc->setTextWidth(-1); |
|
774 doc->documentLayout()->setPaintDevice(viewport); |
|
775 doc->setDefaultFont(q->font()); |
|
776 |
|
777 |
|
778 if (!txt.isEmpty()) |
|
779 control->setPlainText(txt); |
|
780 |
|
781 hbar->setSingleStep(20); |
|
782 vbar->setSingleStep(1); |
|
783 |
|
784 viewport->setBackgroundRole(QPalette::Base); |
|
785 q->setAcceptDrops(true); |
|
786 q->setFocusPolicy(Qt::WheelFocus); |
|
787 q->setAttribute(Qt::WA_KeyCompression); |
|
788 q->setAttribute(Qt::WA_InputMethodEnabled); |
|
789 |
|
790 #ifndef QT_NO_CURSOR |
|
791 viewport->setCursor(Qt::IBeamCursor); |
|
792 #endif |
|
793 originalOffsetY = 0; |
|
794 #ifdef Q_WS_WIN |
|
795 setSingleFingerPanEnabled(true); |
|
796 #endif |
|
797 } |
|
798 |
|
799 void QPlainTextEditPrivate::_q_repaintContents(const QRectF &contentsRect) |
|
800 { |
|
801 Q_Q(QPlainTextEdit); |
|
802 if (!contentsRect.isValid()) { |
|
803 updateViewport(); |
|
804 return; |
|
805 } |
|
806 const int xOffset = horizontalOffset(); |
|
807 const int yOffset = verticalOffset(); |
|
808 const QRect visibleRect(xOffset, yOffset, viewport->width(), viewport->height()); |
|
809 |
|
810 QRect r = contentsRect.adjusted(-1, -1, 1, 1).intersected(visibleRect).toAlignedRect(); |
|
811 if (r.isEmpty()) |
|
812 return; |
|
813 |
|
814 r.translate(-xOffset, -yOffset); |
|
815 viewport->update(r); |
|
816 emit q->updateRequest(r, 0); |
|
817 } |
|
818 |
|
819 void QPlainTextEditPrivate::pageUpDown(QTextCursor::MoveOperation op, QTextCursor::MoveMode moveMode, bool moveCursor) |
|
820 { |
|
821 |
|
822 Q_Q(QPlainTextEdit); |
|
823 |
|
824 QTextCursor cursor = control->textCursor(); |
|
825 if (moveCursor) { |
|
826 ensureCursorVisible(); |
|
827 if (!pageUpDownLastCursorYIsValid) |
|
828 pageUpDownLastCursorY = control->cursorRect(cursor).top() - verticalOffset(); |
|
829 } |
|
830 |
|
831 qreal lastY = pageUpDownLastCursorY; |
|
832 |
|
833 |
|
834 if (op == QTextCursor::Down) { |
|
835 QRectF visible = QRectF(viewport->rect()).translated(-q->contentOffset()); |
|
836 QTextBlock firstVisibleBlock = q->firstVisibleBlock(); |
|
837 QTextBlock block = firstVisibleBlock; |
|
838 QRectF br = q->blockBoundingRect(block); |
|
839 qreal h = 0; |
|
840 int atEnd = false; |
|
841 while (h + br.height() <= visible.bottom()) { |
|
842 if (!block.next().isValid()) { |
|
843 atEnd = true; |
|
844 lastY = visible.bottom(); // set cursor to last line |
|
845 break; |
|
846 } |
|
847 h += br.height(); |
|
848 block = block.next(); |
|
849 br = q->blockBoundingRect(block); |
|
850 } |
|
851 |
|
852 if (!atEnd) { |
|
853 int line = 0; |
|
854 qreal diff = visible.bottom() - h; |
|
855 int lineCount = block.layout()->lineCount(); |
|
856 while (line < lineCount - 1) { |
|
857 if (block.layout()->lineAt(line).naturalTextRect().bottom() > diff) { |
|
858 // the first line that did not completely fit the screen |
|
859 break; |
|
860 } |
|
861 ++line; |
|
862 } |
|
863 setTopBlock(block.blockNumber(), line); |
|
864 } |
|
865 |
|
866 if (moveCursor) { |
|
867 // move using movePosition to keep the cursor's x |
|
868 lastY += verticalOffset(); |
|
869 bool moved = false; |
|
870 do { |
|
871 moved = cursor.movePosition(op, moveMode); |
|
872 } while (moved && control->cursorRect(cursor).top() < lastY); |
|
873 } |
|
874 |
|
875 } else if (op == QTextCursor::Up) { |
|
876 |
|
877 QRectF visible = QRectF(viewport->rect()).translated(-q->contentOffset()); |
|
878 visible.translate(0, -visible.height()); // previous page |
|
879 QTextBlock block = q->firstVisibleBlock(); |
|
880 qreal h = 0; |
|
881 while (h >= visible.top()) { |
|
882 if (!block.previous().isValid()) { |
|
883 if (control->topBlock == 0 && topLine == 0) { |
|
884 lastY = 0; // set cursor to first line |
|
885 } |
|
886 break; |
|
887 } |
|
888 block = block.previous(); |
|
889 QRectF br = q->blockBoundingRect(block); |
|
890 h -= br.height(); |
|
891 } |
|
892 |
|
893 int line = 0; |
|
894 if (block.isValid()) { |
|
895 qreal diff = visible.top() - h; |
|
896 int lineCount = block.layout()->lineCount(); |
|
897 while (line < lineCount) { |
|
898 if (block.layout()->lineAt(line).naturalTextRect().top() >= diff) |
|
899 break; |
|
900 ++line; |
|
901 } |
|
902 if (line == lineCount) { |
|
903 if (block.next().isValid() && block.next() != q->firstVisibleBlock()) { |
|
904 block = block.next(); |
|
905 line = 0; |
|
906 } else { |
|
907 --line; |
|
908 } |
|
909 } |
|
910 } |
|
911 setTopBlock(block.blockNumber(), line); |
|
912 |
|
913 if (moveCursor) { |
|
914 // move using movePosition to keep the cursor's x |
|
915 lastY += verticalOffset(); |
|
916 bool moved = false; |
|
917 do { |
|
918 moved = cursor.movePosition(op, moveMode); |
|
919 } while (moved && control->cursorRect(cursor).top() > lastY); |
|
920 } |
|
921 } |
|
922 |
|
923 if (moveCursor) { |
|
924 control->setTextCursor(cursor); |
|
925 pageUpDownLastCursorYIsValid = true; |
|
926 } |
|
927 } |
|
928 |
|
929 #ifndef QT_NO_SCROLLBAR |
|
930 |
|
931 void QPlainTextEditPrivate::_q_adjustScrollbars() |
|
932 { |
|
933 Q_Q(QPlainTextEdit); |
|
934 QTextDocument *doc = control->document(); |
|
935 QPlainTextDocumentLayout *documentLayout = qobject_cast<QPlainTextDocumentLayout*>(doc->documentLayout()); |
|
936 Q_ASSERT(documentLayout); |
|
937 bool documentSizeChangedBlocked = documentLayout->priv()->blockDocumentSizeChanged; |
|
938 documentLayout->priv()->blockDocumentSizeChanged = true; |
|
939 qreal margin = doc->documentMargin(); |
|
940 |
|
941 int vmax = 0; |
|
942 |
|
943 int vSliderLength = 0; |
|
944 if (!centerOnScroll && q->isVisible()) { |
|
945 QTextBlock block = doc->lastBlock(); |
|
946 const int visible = static_cast<int>(viewport->rect().height() - margin - 1); |
|
947 int y = 0; |
|
948 int visibleFromBottom = 0; |
|
949 |
|
950 while (block.isValid()) { |
|
951 if (!block.isVisible()) { |
|
952 block = block.previous(); |
|
953 continue; |
|
954 } |
|
955 y += int(documentLayout->blockBoundingRect(block).height()); |
|
956 |
|
957 QTextLayout *layout = block.layout(); |
|
958 int layoutLineCount = layout->lineCount(); |
|
959 if (y > visible) { |
|
960 int lineNumber = 0; |
|
961 while (lineNumber < layoutLineCount) { |
|
962 QTextLine line = layout->lineAt(lineNumber); |
|
963 const QRectF lr = line.naturalTextRect(); |
|
964 if (int(lr.top()) >= y - visible) |
|
965 break; |
|
966 ++lineNumber; |
|
967 } |
|
968 if (lineNumber < layoutLineCount) |
|
969 visibleFromBottom += (layoutLineCount - lineNumber - 1); |
|
970 break; |
|
971 |
|
972 } |
|
973 visibleFromBottom += layoutLineCount; |
|
974 block = block.previous(); |
|
975 } |
|
976 vmax = qMax(0, doc->lineCount() - visibleFromBottom); |
|
977 vSliderLength = visibleFromBottom; |
|
978 |
|
979 } else { |
|
980 vmax = qMax(0, doc->lineCount() - 1); |
|
981 vSliderLength = viewport->height() / q->fontMetrics().lineSpacing(); |
|
982 } |
|
983 |
|
984 |
|
985 |
|
986 QSizeF documentSize = documentLayout->documentSize(); |
|
987 vbar->setRange(0, qMax(0, vmax)); |
|
988 vbar->setPageStep(vSliderLength); |
|
989 int visualTopLine = vmax; |
|
990 QTextBlock firstVisibleBlock = q->firstVisibleBlock(); |
|
991 if (firstVisibleBlock.isValid()) |
|
992 visualTopLine = firstVisibleBlock.firstLineNumber() + topLine; |
|
993 bool vbarSignalsBlocked = vbar->blockSignals(true); |
|
994 vbar->setValue(visualTopLine); |
|
995 vbar->blockSignals(vbarSignalsBlocked); |
|
996 |
|
997 hbar->setRange(0, (int)documentSize.width() - viewport->width()); |
|
998 hbar->setPageStep(viewport->width()); |
|
999 documentLayout->priv()->blockDocumentSizeChanged = documentSizeChangedBlocked; |
|
1000 setTopLine(vbar->value()); |
|
1001 } |
|
1002 |
|
1003 #endif |
|
1004 |
|
1005 |
|
1006 void QPlainTextEditPrivate::ensureViewportLayouted() |
|
1007 { |
|
1008 } |
|
1009 |
|
1010 /*! |
|
1011 \class QPlainTextEdit |
|
1012 \since 4.4 |
|
1013 \brief The QPlainTextEdit class provides a widget that is used to edit and display |
|
1014 plain text. |
|
1015 |
|
1016 \ingroup richtext-processing |
|
1017 |
|
1018 |
|
1019 \tableofcontents |
|
1020 |
|
1021 \section1 Introduction and Concepts |
|
1022 |
|
1023 QPlainTextEdit is an advanced viewer/editor supporting plain |
|
1024 text. It is optimized to handle large documents and to respond |
|
1025 quickly to user input. |
|
1026 |
|
1027 QPlainText uses very much the same technology and concepts as |
|
1028 QTextEdit, but is optimized for plain text handling. |
|
1029 |
|
1030 QPlainTextEdit works on paragraphs and characters. A paragraph is |
|
1031 a formatted string which is word-wrapped to fit into the width of |
|
1032 the widget. By default when reading plain text, one newline |
|
1033 signifies a paragraph. A document consists of zero or more |
|
1034 paragraphs. Paragraphs are separated by hard line breaks. Each |
|
1035 character within a paragraph has its own attributes, for example, |
|
1036 font and color. |
|
1037 |
|
1038 The shape of the mouse cursor on a QPlainTextEdit is |
|
1039 Qt::IBeamCursor by default. It can be changed through the |
|
1040 viewport()'s cursor property. |
|
1041 |
|
1042 \section1 Using QPlainTextEdit as a Display Widget |
|
1043 |
|
1044 The text is set or replaced using setPlainText() which deletes the |
|
1045 existing text and replaces it with the text passed to setPlainText(). |
|
1046 |
|
1047 Text can be inserted using the QTextCursor class or using the |
|
1048 convenience functions insertPlainText(), appendPlainText() or |
|
1049 paste(). |
|
1050 |
|
1051 By default, the text edit wraps words at whitespace to fit within |
|
1052 the text edit widget. The setLineWrapMode() function is used to |
|
1053 specify the kind of line wrap you want, \l WidgetWidth or \l |
|
1054 NoWrap if you don't want any wrapping. If you use word wrap to |
|
1055 the widget's width \l WidgetWidth, you can specify whether to |
|
1056 break on whitespace or anywhere with setWordWrapMode(). |
|
1057 |
|
1058 The find() function can be used to find and select a given string |
|
1059 within the text. |
|
1060 |
|
1061 If you want to limit the total number of paragraphs in a |
|
1062 QPlainTextEdit, as it is for example useful in a log viewer, then |
|
1063 you can use the maximumBlockCount property. The combination of |
|
1064 setMaximumBlockCount() and appendPlainText() turns QPlainTextEdit |
|
1065 into an efficient viewer for log text. The scrolling can be |
|
1066 reduced with the centerOnScroll() property, making the log viewer |
|
1067 even faster. Text can be formatted in a limited way, either using |
|
1068 a syntax highlighter (see below), or by appending html-formatted |
|
1069 text with appendHtml(). While QPlainTextEdit does not support |
|
1070 complex rich text rendering with tables and floats, it does |
|
1071 support limited paragraph-based formatting that you may need in a |
|
1072 log viewer. |
|
1073 |
|
1074 \section2 Read-only Key Bindings |
|
1075 |
|
1076 When QPlainTextEdit is used read-only the key bindings are limited to |
|
1077 navigation, and text may only be selected with the mouse: |
|
1078 \table |
|
1079 \header \i Keypresses \i Action |
|
1080 \row \i Qt::UpArrow \i Moves one line up. |
|
1081 \row \i Qt::DownArrow \i Moves one line down. |
|
1082 \row \i Qt::LeftArrow \i Moves one character to the left. |
|
1083 \row \i Qt::RightArrow \i Moves one character to the right. |
|
1084 \row \i PageUp \i Moves one (viewport) page up. |
|
1085 \row \i PageDown \i Moves one (viewport) page down. |
|
1086 \row \i Home \i Moves to the beginning of the text. |
|
1087 \row \i End \i Moves to the end of the text. |
|
1088 \row \i Alt+Wheel |
|
1089 \i Scrolls the page horizontally (the Wheel is the mouse wheel). |
|
1090 \row \i Ctrl+Wheel \i Zooms the text. |
|
1091 \row \i Ctrl+A \i Selects all text. |
|
1092 \endtable |
|
1093 |
|
1094 |
|
1095 \section1 Using QPlainTextEdit as an Editor |
|
1096 |
|
1097 All the information about using QPlainTextEdit as a display widget also |
|
1098 applies here. |
|
1099 |
|
1100 Selection of text is handled by the QTextCursor class, which provides |
|
1101 functionality for creating selections, retrieving the text contents or |
|
1102 deleting selections. You can retrieve the object that corresponds with |
|
1103 the user-visible cursor using the textCursor() method. If you want to set |
|
1104 a selection in QPlainTextEdit just create one on a QTextCursor object and |
|
1105 then make that cursor the visible cursor using setCursor(). The selection |
|
1106 can be copied to the clipboard with copy(), or cut to the clipboard with |
|
1107 cut(). The entire text can be selected using selectAll(). |
|
1108 |
|
1109 QPlainTextEdit holds a QTextDocument object which can be retrieved using the |
|
1110 document() method. You can also set your own document object using setDocument(). |
|
1111 QTextDocument emits a textChanged() signal if the text changes and it also |
|
1112 provides a isModified() function which will return true if the text has been |
|
1113 modified since it was either loaded or since the last call to setModified |
|
1114 with false as argument. In addition it provides methods for undo and redo. |
|
1115 |
|
1116 \section2 Syntax Highlighting |
|
1117 |
|
1118 Just like QTextEdit, QPlainTextEdit works together with |
|
1119 QSyntaxHighlighter. |
|
1120 |
|
1121 \section2 Editing Key Bindings |
|
1122 |
|
1123 The list of key bindings which are implemented for editing: |
|
1124 \table |
|
1125 \header \i Keypresses \i Action |
|
1126 \row \i Backspace \i Deletes the character to the left of the cursor. |
|
1127 \row \i Delete \i Deletes the character to the right of the cursor. |
|
1128 \row \i Ctrl+C \i Copy the selected text to the clipboard. |
|
1129 \row \i Ctrl+Insert \i Copy the selected text to the clipboard. |
|
1130 \row \i Ctrl+K \i Deletes to the end of the line. |
|
1131 \row \i Ctrl+V \i Pastes the clipboard text into text edit. |
|
1132 \row \i Shift+Insert \i Pastes the clipboard text into text edit. |
|
1133 \row \i Ctrl+X \i Deletes the selected text and copies it to the clipboard. |
|
1134 \row \i Shift+Delete \i Deletes the selected text and copies it to the clipboard. |
|
1135 \row \i Ctrl+Z \i Undoes the last operation. |
|
1136 \row \i Ctrl+Y \i Redoes the last operation. |
|
1137 \row \i LeftArrow \i Moves the cursor one character to the left. |
|
1138 \row \i Ctrl+LeftArrow \i Moves the cursor one word to the left. |
|
1139 \row \i RightArrow \i Moves the cursor one character to the right. |
|
1140 \row \i Ctrl+RightArrow \i Moves the cursor one word to the right. |
|
1141 \row \i UpArrow \i Moves the cursor one line up. |
|
1142 \row \i Ctrl+UpArrow \i Moves the cursor one word up. |
|
1143 \row \i DownArrow \i Moves the cursor one line down. |
|
1144 \row \i Ctrl+Down Arrow \i Moves the cursor one word down. |
|
1145 \row \i PageUp \i Moves the cursor one page up. |
|
1146 \row \i PageDown \i Moves the cursor one page down. |
|
1147 \row \i Home \i Moves the cursor to the beginning of the line. |
|
1148 \row \i Ctrl+Home \i Moves the cursor to the beginning of the text. |
|
1149 \row \i End \i Moves the cursor to the end of the line. |
|
1150 \row \i Ctrl+End \i Moves the cursor to the end of the text. |
|
1151 \row \i Alt+Wheel \i Scrolls the page horizontally (the Wheel is the mouse wheel). |
|
1152 \row \i Ctrl+Wheel \i Zooms the text. |
|
1153 \endtable |
|
1154 |
|
1155 To select (mark) text hold down the Shift key whilst pressing one |
|
1156 of the movement keystrokes, for example, \e{Shift+Right Arrow} |
|
1157 will select the character to the right, and \e{Shift+Ctrl+Right |
|
1158 Arrow} will select the word to the right, etc. |
|
1159 |
|
1160 \section1 Differences to QTextEdit |
|
1161 |
|
1162 QPlainTextEdit is a thin class, implemented by using most of the |
|
1163 technology that is behind QTextEdit and QTextDocument. Its |
|
1164 performance benefits over QTextEdit stem mostly from using a |
|
1165 different and simplified text layout called |
|
1166 QPlainTextDocumentLayout on the text document (see |
|
1167 QTextDocument::setDocumentLayout()). The plain text document layout |
|
1168 does not support tables nor embedded frames, and \e{replaces a |
|
1169 pixel-exact height calculation with a line-by-line respectively |
|
1170 paragraph-by-paragraph scrolling approach}. This makes it possible |
|
1171 to handle significantly larger documents, and still resize the |
|
1172 editor with line wrap enabled in real time. It also makes for a |
|
1173 fast log viewer (see setMaximumBlockCount()). |
|
1174 |
|
1175 |
|
1176 \sa QTextDocument, QTextCursor, {Application Example}, |
|
1177 {Code Editor Example}, {Syntax Highlighter Example}, |
|
1178 {Rich Text Processing} |
|
1179 |
|
1180 */ |
|
1181 |
|
1182 /*! |
|
1183 \property QPlainTextEdit::plainText |
|
1184 |
|
1185 This property gets and sets the plain text editor's contents. The previous |
|
1186 contents are removed and undo/redo history is reset when this property is set. |
|
1187 |
|
1188 By default, for an editor with no contents, this property contains an empty string. |
|
1189 */ |
|
1190 |
|
1191 /*! |
|
1192 \property QPlainTextEdit::undoRedoEnabled |
|
1193 \brief whether undo and redo are enabled |
|
1194 |
|
1195 Users are only able to undo or redo actions if this property is |
|
1196 true, and if there is an action that can be undone (or redone). |
|
1197 |
|
1198 By default, this property is true. |
|
1199 */ |
|
1200 |
|
1201 /*! |
|
1202 \enum QPlainTextEdit::LineWrapMode |
|
1203 |
|
1204 \value NoWrap |
|
1205 \value WidgetWidth |
|
1206 */ |
|
1207 |
|
1208 |
|
1209 /*! |
|
1210 Constructs an empty QPlainTextEdit with parent \a |
|
1211 parent. |
|
1212 */ |
|
1213 QPlainTextEdit::QPlainTextEdit(QWidget *parent) |
|
1214 : QAbstractScrollArea(*new QPlainTextEditPrivate, parent) |
|
1215 { |
|
1216 Q_D(QPlainTextEdit); |
|
1217 d->init(); |
|
1218 } |
|
1219 |
|
1220 /*! |
|
1221 \internal |
|
1222 */ |
|
1223 QPlainTextEdit::QPlainTextEdit(QPlainTextEditPrivate &dd, QWidget *parent) |
|
1224 : QAbstractScrollArea(dd, parent) |
|
1225 { |
|
1226 Q_D(QPlainTextEdit); |
|
1227 d->init(); |
|
1228 } |
|
1229 |
|
1230 /*! |
|
1231 Constructs a QPlainTextEdit with parent \a parent. The text edit will display |
|
1232 the plain text \a text. |
|
1233 */ |
|
1234 QPlainTextEdit::QPlainTextEdit(const QString &text, QWidget *parent) |
|
1235 : QAbstractScrollArea(*new QPlainTextEditPrivate, parent) |
|
1236 { |
|
1237 Q_D(QPlainTextEdit); |
|
1238 d->init(text); |
|
1239 } |
|
1240 |
|
1241 |
|
1242 /*! |
|
1243 Destructor. |
|
1244 */ |
|
1245 QPlainTextEdit::~QPlainTextEdit() |
|
1246 { |
|
1247 Q_D(QPlainTextEdit); |
|
1248 if (d->documentLayoutPtr) { |
|
1249 if (d->documentLayoutPtr->priv()->mainViewPrivate == d) |
|
1250 d->documentLayoutPtr->priv()->mainViewPrivate = 0; |
|
1251 } |
|
1252 } |
|
1253 |
|
1254 /*! |
|
1255 Makes \a document the new document of the text editor. |
|
1256 |
|
1257 The parent QObject of the provided document remains the owner |
|
1258 of the object. If the current document is a child of the text |
|
1259 editor, then it is deleted. |
|
1260 |
|
1261 The document must have a document layout that inherits |
|
1262 QPlainTextDocumentLayout (see QTextDocument::setDocumentLayout()). |
|
1263 |
|
1264 \sa document() |
|
1265 */ |
|
1266 void QPlainTextEdit::setDocument(QTextDocument *document) |
|
1267 { |
|
1268 Q_D(QPlainTextEdit); |
|
1269 QPlainTextDocumentLayout *documentLayout = 0; |
|
1270 |
|
1271 if (!document) { |
|
1272 document = new QTextDocument(d->control); |
|
1273 documentLayout = new QPlainTextDocumentLayout(document); |
|
1274 document->setDocumentLayout(documentLayout); |
|
1275 } else { |
|
1276 documentLayout = qobject_cast<QPlainTextDocumentLayout*>(document->documentLayout()); |
|
1277 if (!documentLayout) { |
|
1278 qWarning("QPlainTextEdit::setDocument: Document set does not support QPlainTextDocumentLayout"); |
|
1279 return; |
|
1280 } |
|
1281 } |
|
1282 d->control->setDocument(document); |
|
1283 if (!documentLayout->priv()->mainViewPrivate) |
|
1284 documentLayout->priv()->mainViewPrivate = d; |
|
1285 d->documentLayoutPtr = documentLayout; |
|
1286 d->updateDefaultTextOption(); |
|
1287 d->relayoutDocument(); |
|
1288 d->_q_adjustScrollbars(); |
|
1289 } |
|
1290 |
|
1291 /*! |
|
1292 Returns a pointer to the underlying document. |
|
1293 |
|
1294 \sa setDocument() |
|
1295 */ |
|
1296 QTextDocument *QPlainTextEdit::document() const |
|
1297 { |
|
1298 Q_D(const QPlainTextEdit); |
|
1299 return d->control->document(); |
|
1300 } |
|
1301 |
|
1302 /*! |
|
1303 Sets the visible \a cursor. |
|
1304 */ |
|
1305 void QPlainTextEdit::setTextCursor(const QTextCursor &cursor) |
|
1306 { |
|
1307 Q_D(QPlainTextEdit); |
|
1308 d->control->setTextCursor(cursor); |
|
1309 } |
|
1310 |
|
1311 /*! |
|
1312 Returns a copy of the QTextCursor that represents the currently visible cursor. |
|
1313 Note that changes on the returned cursor do not affect QPlainTextEdit's cursor; use |
|
1314 setTextCursor() to update the visible cursor. |
|
1315 */ |
|
1316 QTextCursor QPlainTextEdit::textCursor() const |
|
1317 { |
|
1318 Q_D(const QPlainTextEdit); |
|
1319 return d->control->textCursor(); |
|
1320 } |
|
1321 |
|
1322 |
|
1323 /*! |
|
1324 Undoes the last operation. |
|
1325 |
|
1326 If there is no operation to undo, i.e. there is no undo step in |
|
1327 the undo/redo history, nothing happens. |
|
1328 |
|
1329 \sa redo() |
|
1330 */ |
|
1331 void QPlainTextEdit::undo() |
|
1332 { |
|
1333 Q_D(QPlainTextEdit); |
|
1334 d->control->undo(); |
|
1335 } |
|
1336 |
|
1337 void QPlainTextEdit::redo() |
|
1338 { |
|
1339 Q_D(QPlainTextEdit); |
|
1340 d->control->redo(); |
|
1341 } |
|
1342 |
|
1343 /*! |
|
1344 \fn void QPlainTextEdit::redo() |
|
1345 |
|
1346 Redoes the last operation. |
|
1347 |
|
1348 If there is no operation to redo, i.e. there is no redo step in |
|
1349 the undo/redo history, nothing happens. |
|
1350 |
|
1351 \sa undo() |
|
1352 */ |
|
1353 |
|
1354 #ifndef QT_NO_CLIPBOARD |
|
1355 /*! |
|
1356 Copies the selected text to the clipboard and deletes it from |
|
1357 the text edit. |
|
1358 |
|
1359 If there is no selected text nothing happens. |
|
1360 |
|
1361 \sa copy() paste() |
|
1362 */ |
|
1363 |
|
1364 void QPlainTextEdit::cut() |
|
1365 { |
|
1366 Q_D(QPlainTextEdit); |
|
1367 d->control->cut(); |
|
1368 } |
|
1369 |
|
1370 /*! |
|
1371 Copies any selected text to the clipboard. |
|
1372 |
|
1373 \sa copyAvailable() |
|
1374 */ |
|
1375 |
|
1376 void QPlainTextEdit::copy() |
|
1377 { |
|
1378 Q_D(QPlainTextEdit); |
|
1379 d->control->copy(); |
|
1380 } |
|
1381 |
|
1382 /*! |
|
1383 Pastes the text from the clipboard into the text edit at the |
|
1384 current cursor position. |
|
1385 |
|
1386 If there is no text in the clipboard nothing happens. |
|
1387 |
|
1388 To change the behavior of this function, i.e. to modify what |
|
1389 QPlainTextEdit can paste and how it is being pasted, reimplement the |
|
1390 virtual canInsertFromMimeData() and insertFromMimeData() |
|
1391 functions. |
|
1392 |
|
1393 \sa cut() copy() |
|
1394 */ |
|
1395 |
|
1396 void QPlainTextEdit::paste() |
|
1397 { |
|
1398 Q_D(QPlainTextEdit); |
|
1399 d->control->paste(); |
|
1400 } |
|
1401 #endif |
|
1402 |
|
1403 /*! |
|
1404 Deletes all the text in the text edit. |
|
1405 |
|
1406 Note that the undo/redo history is cleared by this function. |
|
1407 |
|
1408 \sa cut() setPlainText() |
|
1409 */ |
|
1410 void QPlainTextEdit::clear() |
|
1411 { |
|
1412 Q_D(QPlainTextEdit); |
|
1413 // clears and sets empty content |
|
1414 d->control->topBlock = d->topLine = 0; |
|
1415 d->control->clear(); |
|
1416 } |
|
1417 |
|
1418 |
|
1419 /*! |
|
1420 Selects all text. |
|
1421 |
|
1422 \sa copy() cut() textCursor() |
|
1423 */ |
|
1424 void QPlainTextEdit::selectAll() |
|
1425 { |
|
1426 Q_D(QPlainTextEdit); |
|
1427 d->control->selectAll(); |
|
1428 } |
|
1429 |
|
1430 /*! \internal |
|
1431 */ |
|
1432 bool QPlainTextEdit::event(QEvent *e) |
|
1433 { |
|
1434 Q_D(QPlainTextEdit); |
|
1435 |
|
1436 #ifndef QT_NO_CONTEXTMENU |
|
1437 if (e->type() == QEvent::ContextMenu |
|
1438 && static_cast<QContextMenuEvent *>(e)->reason() == QContextMenuEvent::Keyboard) { |
|
1439 ensureCursorVisible(); |
|
1440 const QPoint cursorPos = cursorRect().center(); |
|
1441 QContextMenuEvent ce(QContextMenuEvent::Keyboard, cursorPos, d->viewport->mapToGlobal(cursorPos)); |
|
1442 ce.setAccepted(e->isAccepted()); |
|
1443 const bool result = QAbstractScrollArea::event(&ce); |
|
1444 e->setAccepted(ce.isAccepted()); |
|
1445 return result; |
|
1446 } |
|
1447 #endif // QT_NO_CONTEXTMENU |
|
1448 if (e->type() == QEvent::ShortcutOverride |
|
1449 || e->type() == QEvent::ToolTip) { |
|
1450 d->sendControlEvent(e); |
|
1451 } |
|
1452 #ifdef QT_KEYPAD_NAVIGATION |
|
1453 else if (e->type() == QEvent::EnterEditFocus || e->type() == QEvent::LeaveEditFocus) { |
|
1454 if (QApplication::keypadNavigationEnabled()) |
|
1455 d->sendControlEvent(e); |
|
1456 } |
|
1457 #endif |
|
1458 else if (e->type() == QEvent::Gesture) { |
|
1459 QGestureEvent *ge = static_cast<QGestureEvent *>(e); |
|
1460 QPanGesture *g = static_cast<QPanGesture *>(ge->gesture(Qt::PanGesture)); |
|
1461 if (g) { |
|
1462 QScrollBar *hBar = horizontalScrollBar(); |
|
1463 QScrollBar *vBar = verticalScrollBar(); |
|
1464 if (g->state() == Qt::GestureStarted) |
|
1465 d->originalOffsetY = vBar->value(); |
|
1466 QPointF offset = g->offset(); |
|
1467 if (!offset.isNull()) { |
|
1468 if (QApplication::isRightToLeft()) |
|
1469 offset.rx() *= -1; |
|
1470 // QPlainTextEdit scrolls by lines only in vertical direction |
|
1471 QFontMetrics fm(document()->defaultFont()); |
|
1472 int lineHeight = fm.height(); |
|
1473 int newX = hBar->value() - g->delta().x(); |
|
1474 int newY = d->originalOffsetY - offset.y()/lineHeight; |
|
1475 hBar->setValue(newX); |
|
1476 vBar->setValue(newY); |
|
1477 } |
|
1478 } |
|
1479 return true; |
|
1480 } |
|
1481 return QAbstractScrollArea::event(e); |
|
1482 } |
|
1483 |
|
1484 /*! \internal |
|
1485 */ |
|
1486 |
|
1487 void QPlainTextEdit::timerEvent(QTimerEvent *e) |
|
1488 { |
|
1489 Q_D(QPlainTextEdit); |
|
1490 if (e->timerId() == d->autoScrollTimer.timerId()) { |
|
1491 QRect visible = d->viewport->rect(); |
|
1492 QPoint pos; |
|
1493 if (d->inDrag) { |
|
1494 pos = d->autoScrollDragPos; |
|
1495 visible.adjust(qMin(visible.width()/3,20), qMin(visible.height()/3,20), |
|
1496 -qMin(visible.width()/3,20), -qMin(visible.height()/3,20)); |
|
1497 } else { |
|
1498 const QPoint globalPos = QCursor::pos(); |
|
1499 pos = d->viewport->mapFromGlobal(globalPos); |
|
1500 QMouseEvent ev(QEvent::MouseMove, pos, globalPos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); |
|
1501 mouseMoveEvent(&ev); |
|
1502 } |
|
1503 int deltaY = qMax(pos.y() - visible.top(), visible.bottom() - pos.y()) - visible.height(); |
|
1504 int deltaX = qMax(pos.x() - visible.left(), visible.right() - pos.x()) - visible.width(); |
|
1505 int delta = qMax(deltaX, deltaY); |
|
1506 if (delta >= 0) { |
|
1507 if (delta < 7) |
|
1508 delta = 7; |
|
1509 int timeout = 4900 / (delta * delta); |
|
1510 d->autoScrollTimer.start(timeout, this); |
|
1511 |
|
1512 if (deltaY > 0) |
|
1513 d->vbar->triggerAction(pos.y() < visible.center().y() ? |
|
1514 QAbstractSlider::SliderSingleStepSub |
|
1515 : QAbstractSlider::SliderSingleStepAdd); |
|
1516 if (deltaX > 0) |
|
1517 d->hbar->triggerAction(pos.x() < visible.center().x() ? |
|
1518 QAbstractSlider::SliderSingleStepSub |
|
1519 : QAbstractSlider::SliderSingleStepAdd); |
|
1520 } |
|
1521 } |
|
1522 #ifdef QT_KEYPAD_NAVIGATION |
|
1523 else if (e->timerId() == d->deleteAllTimer.timerId()) { |
|
1524 d->deleteAllTimer.stop(); |
|
1525 clear(); |
|
1526 } |
|
1527 #endif |
|
1528 } |
|
1529 |
|
1530 /*! |
|
1531 Changes the text of the text edit to the string \a text. |
|
1532 Any previous text is removed. |
|
1533 |
|
1534 \a text is interpreted as plain text. |
|
1535 |
|
1536 Note that the undo/redo history is cleared by this function. |
|
1537 |
|
1538 \sa toText() |
|
1539 */ |
|
1540 |
|
1541 void QPlainTextEdit::setPlainText(const QString &text) |
|
1542 { |
|
1543 Q_D(QPlainTextEdit); |
|
1544 d->control->setPlainText(text); |
|
1545 } |
|
1546 |
|
1547 /*! |
|
1548 \fn QString QPlainTextEdit::toPlainText() const |
|
1549 |
|
1550 Returns the text of the text edit as plain text. |
|
1551 |
|
1552 \sa QPlainTextEdit::setPlainText() |
|
1553 */ |
|
1554 |
|
1555 /*! \reimp |
|
1556 */ |
|
1557 void QPlainTextEdit::keyPressEvent(QKeyEvent *e) |
|
1558 { |
|
1559 Q_D(QPlainTextEdit); |
|
1560 |
|
1561 #ifdef QT_KEYPAD_NAVIGATION |
|
1562 switch (e->key()) { |
|
1563 case Qt::Key_Select: |
|
1564 if (QApplication::keypadNavigationEnabled()) { |
|
1565 if (!(d->control->textInteractionFlags() & Qt::LinksAccessibleByKeyboard)) |
|
1566 setEditFocus(!hasEditFocus()); |
|
1567 else { |
|
1568 if (!hasEditFocus()) |
|
1569 setEditFocus(true); |
|
1570 else { |
|
1571 QTextCursor cursor = d->control->textCursor(); |
|
1572 QTextCharFormat charFmt = cursor.charFormat(); |
|
1573 if (!cursor.hasSelection() || charFmt.anchorHref().isEmpty()) { |
|
1574 setEditFocus(false); |
|
1575 } |
|
1576 } |
|
1577 } |
|
1578 } |
|
1579 break; |
|
1580 case Qt::Key_Back: |
|
1581 case Qt::Key_No: |
|
1582 if (!QApplication::keypadNavigationEnabled() |
|
1583 || (QApplication::keypadNavigationEnabled() && !hasEditFocus())) { |
|
1584 e->ignore(); |
|
1585 return; |
|
1586 } |
|
1587 break; |
|
1588 default: |
|
1589 if (QApplication::keypadNavigationEnabled()) { |
|
1590 if (!hasEditFocus() && !(e->modifiers() & Qt::ControlModifier)) { |
|
1591 if (e->text()[0].isPrint()) { |
|
1592 setEditFocus(true); |
|
1593 clear(); |
|
1594 } else { |
|
1595 e->ignore(); |
|
1596 return; |
|
1597 } |
|
1598 } |
|
1599 } |
|
1600 break; |
|
1601 } |
|
1602 #endif |
|
1603 |
|
1604 #ifndef QT_NO_SHORTCUT |
|
1605 |
|
1606 Qt::TextInteractionFlags tif = d->control->textInteractionFlags(); |
|
1607 |
|
1608 if (tif & Qt::TextSelectableByKeyboard){ |
|
1609 if (e == QKeySequence::SelectPreviousPage) { |
|
1610 e->accept(); |
|
1611 d->pageUpDown(QTextCursor::Up, QTextCursor::KeepAnchor); |
|
1612 return; |
|
1613 } else if (e ==QKeySequence::SelectNextPage) { |
|
1614 e->accept(); |
|
1615 d->pageUpDown(QTextCursor::Down, QTextCursor::KeepAnchor); |
|
1616 return; |
|
1617 } |
|
1618 } |
|
1619 if (tif & (Qt::TextSelectableByKeyboard | Qt::TextEditable)) { |
|
1620 if (e == QKeySequence::MoveToPreviousPage) { |
|
1621 e->accept(); |
|
1622 d->pageUpDown(QTextCursor::Up, QTextCursor::MoveAnchor); |
|
1623 return; |
|
1624 } else if (e == QKeySequence::MoveToNextPage) { |
|
1625 e->accept(); |
|
1626 d->pageUpDown(QTextCursor::Down, QTextCursor::MoveAnchor); |
|
1627 return; |
|
1628 } |
|
1629 } |
|
1630 |
|
1631 if (!(tif & Qt::TextEditable)) { |
|
1632 switch (e->key()) { |
|
1633 case Qt::Key_Space: |
|
1634 e->accept(); |
|
1635 if (e->modifiers() & Qt::ShiftModifier) |
|
1636 d->vbar->triggerAction(QAbstractSlider::SliderPageStepSub); |
|
1637 else |
|
1638 d->vbar->triggerAction(QAbstractSlider::SliderPageStepAdd); |
|
1639 break; |
|
1640 default: |
|
1641 d->sendControlEvent(e); |
|
1642 if (!e->isAccepted() && e->modifiers() == Qt::NoModifier) { |
|
1643 if (e->key() == Qt::Key_Home) { |
|
1644 d->vbar->triggerAction(QAbstractSlider::SliderToMinimum); |
|
1645 e->accept(); |
|
1646 } else if (e->key() == Qt::Key_End) { |
|
1647 d->vbar->triggerAction(QAbstractSlider::SliderToMaximum); |
|
1648 e->accept(); |
|
1649 } |
|
1650 } |
|
1651 if (!e->isAccepted()) { |
|
1652 QAbstractScrollArea::keyPressEvent(e); |
|
1653 } |
|
1654 } |
|
1655 return; |
|
1656 } |
|
1657 #endif // QT_NO_SHORTCUT |
|
1658 |
|
1659 d->sendControlEvent(e); |
|
1660 #ifdef QT_KEYPAD_NAVIGATION |
|
1661 if (!e->isAccepted()) { |
|
1662 switch (e->key()) { |
|
1663 case Qt::Key_Up: |
|
1664 case Qt::Key_Down: |
|
1665 if (QApplication::keypadNavigationEnabled()) { |
|
1666 // Cursor position didn't change, so we want to leave |
|
1667 // these keys to change focus. |
|
1668 e->ignore(); |
|
1669 return; |
|
1670 } |
|
1671 break; |
|
1672 case Qt::Key_Left: |
|
1673 case Qt::Key_Right: |
|
1674 if (QApplication::keypadNavigationEnabled() |
|
1675 && QApplication::navigationMode() == Qt::NavigationModeKeypadDirectional) { |
|
1676 // Same as for Key_Up and Key_Down. |
|
1677 e->ignore(); |
|
1678 return; |
|
1679 } |
|
1680 break; |
|
1681 case Qt::Key_Back: |
|
1682 if (!e->isAutoRepeat()) { |
|
1683 if (QApplication::keypadNavigationEnabled()) { |
|
1684 if (document()->isEmpty()) { |
|
1685 setEditFocus(false); |
|
1686 e->accept(); |
|
1687 } else if (!d->deleteAllTimer.isActive()) { |
|
1688 e->accept(); |
|
1689 d->deleteAllTimer.start(750, this); |
|
1690 } |
|
1691 } else { |
|
1692 e->ignore(); |
|
1693 return; |
|
1694 } |
|
1695 } |
|
1696 break; |
|
1697 default: break; |
|
1698 } |
|
1699 } |
|
1700 #endif |
|
1701 } |
|
1702 |
|
1703 /*! \reimp |
|
1704 */ |
|
1705 void QPlainTextEdit::keyReleaseEvent(QKeyEvent *e) |
|
1706 { |
|
1707 #ifdef QT_KEYPAD_NAVIGATION |
|
1708 Q_D(QPlainTextEdit); |
|
1709 if (QApplication::keypadNavigationEnabled()) { |
|
1710 if (!e->isAutoRepeat() && e->key() == Qt::Key_Back |
|
1711 && d->deleteAllTimer.isActive()) { |
|
1712 d->deleteAllTimer.stop(); |
|
1713 QTextCursor cursor = d->control->textCursor(); |
|
1714 QTextBlockFormat blockFmt = cursor.blockFormat(); |
|
1715 |
|
1716 QTextList *list = cursor.currentList(); |
|
1717 if (list && cursor.atBlockStart()) { |
|
1718 list->remove(cursor.block()); |
|
1719 } else if (cursor.atBlockStart() && blockFmt.indent() > 0) { |
|
1720 blockFmt.setIndent(blockFmt.indent() - 1); |
|
1721 cursor.setBlockFormat(blockFmt); |
|
1722 } else { |
|
1723 cursor.deletePreviousChar(); |
|
1724 } |
|
1725 setTextCursor(cursor); |
|
1726 } |
|
1727 } |
|
1728 #else |
|
1729 Q_UNUSED(e); |
|
1730 #endif |
|
1731 } |
|
1732 |
|
1733 /*! |
|
1734 Loads the resource specified by the given \a type and \a name. |
|
1735 |
|
1736 This function is an extension of QTextDocument::loadResource(). |
|
1737 |
|
1738 \sa QTextDocument::loadResource() |
|
1739 */ |
|
1740 QVariant QPlainTextEdit::loadResource(int type, const QUrl &name) |
|
1741 { |
|
1742 Q_UNUSED(type); |
|
1743 Q_UNUSED(name); |
|
1744 return QVariant(); |
|
1745 } |
|
1746 |
|
1747 /*! \reimp |
|
1748 */ |
|
1749 void QPlainTextEdit::resizeEvent(QResizeEvent *e) |
|
1750 { |
|
1751 Q_D(QPlainTextEdit); |
|
1752 if (e->oldSize().width() != e->size().width()) |
|
1753 d->relayoutDocument(); |
|
1754 d->_q_adjustScrollbars(); |
|
1755 } |
|
1756 |
|
1757 void QPlainTextEditPrivate::relayoutDocument() |
|
1758 { |
|
1759 QTextDocument *doc = control->document(); |
|
1760 QPlainTextDocumentLayout *documentLayout = qobject_cast<QPlainTextDocumentLayout*>(doc->documentLayout()); |
|
1761 Q_ASSERT(documentLayout); |
|
1762 documentLayoutPtr = documentLayout; |
|
1763 |
|
1764 int width = viewport->width(); |
|
1765 |
|
1766 if (documentLayout->priv()->mainViewPrivate == 0 |
|
1767 || documentLayout->priv()->mainViewPrivate == this |
|
1768 || width > documentLayout->textWidth()) { |
|
1769 documentLayout->priv()->mainViewPrivate = this; |
|
1770 documentLayout->setTextWidth(width); |
|
1771 } |
|
1772 } |
|
1773 |
|
1774 static void fillBackground(QPainter *p, const QRectF &rect, QBrush brush, QRectF gradientRect = QRectF()) |
|
1775 { |
|
1776 p->save(); |
|
1777 if (brush.style() >= Qt::LinearGradientPattern && brush.style() <= Qt::ConicalGradientPattern) { |
|
1778 if (!gradientRect.isNull()) { |
|
1779 QTransform m = QTransform::fromTranslate(gradientRect.left(), gradientRect.top()); |
|
1780 m.scale(gradientRect.width(), gradientRect.height()); |
|
1781 brush.setTransform(m); |
|
1782 const_cast<QGradient *>(brush.gradient())->setCoordinateMode(QGradient::LogicalMode); |
|
1783 } |
|
1784 } else { |
|
1785 p->setBrushOrigin(rect.topLeft()); |
|
1786 } |
|
1787 p->fillRect(rect, brush); |
|
1788 p->restore(); |
|
1789 } |
|
1790 |
|
1791 |
|
1792 |
|
1793 /*! \reimp |
|
1794 */ |
|
1795 void QPlainTextEdit::paintEvent(QPaintEvent *e) |
|
1796 { |
|
1797 QPainter painter(viewport()); |
|
1798 Q_ASSERT(qobject_cast<QPlainTextDocumentLayout*>(document()->documentLayout())); |
|
1799 |
|
1800 QPointF offset(contentOffset()); |
|
1801 |
|
1802 QRect er = e->rect(); |
|
1803 QRect viewportRect = viewport()->rect(); |
|
1804 |
|
1805 bool editable = !isReadOnly(); |
|
1806 |
|
1807 QTextBlock block = firstVisibleBlock(); |
|
1808 qreal maximumWidth = document()->documentLayout()->documentSize().width(); |
|
1809 |
|
1810 // Set a brush origin so that the WaveUnderline knows where the wave started |
|
1811 painter.setBrushOrigin(offset); |
|
1812 |
|
1813 // keep right margin clean from full-width selection |
|
1814 int maxX = offset.x() + qMax((qreal)viewportRect.width(), maximumWidth) |
|
1815 - document()->documentMargin(); |
|
1816 er.setRight(qMin(er.right(), maxX)); |
|
1817 painter.setClipRect(er); |
|
1818 |
|
1819 |
|
1820 QAbstractTextDocumentLayout::PaintContext context = getPaintContext(); |
|
1821 |
|
1822 while (block.isValid()) { |
|
1823 |
|
1824 QRectF r = blockBoundingRect(block).translated(offset); |
|
1825 QTextLayout *layout = block.layout(); |
|
1826 |
|
1827 if (!block.isVisible()) { |
|
1828 offset.ry() += r.height(); |
|
1829 block = block.next(); |
|
1830 continue; |
|
1831 } |
|
1832 |
|
1833 if (r.bottom() >= er.top() && r.top() <= er.bottom()) { |
|
1834 |
|
1835 QTextBlockFormat blockFormat = block.blockFormat(); |
|
1836 |
|
1837 QBrush bg = blockFormat.background(); |
|
1838 if (bg != Qt::NoBrush) { |
|
1839 QRectF contentsRect = r; |
|
1840 contentsRect.setWidth(qMax(r.width(), maximumWidth)); |
|
1841 fillBackground(&painter, contentsRect, bg); |
|
1842 } |
|
1843 |
|
1844 |
|
1845 QVector<QTextLayout::FormatRange> selections; |
|
1846 int blpos = block.position(); |
|
1847 int bllen = block.length(); |
|
1848 for (int i = 0; i < context.selections.size(); ++i) { |
|
1849 const QAbstractTextDocumentLayout::Selection &range = context.selections.at(i); |
|
1850 const int selStart = range.cursor.selectionStart() - blpos; |
|
1851 const int selEnd = range.cursor.selectionEnd() - blpos; |
|
1852 if (selStart < bllen && selEnd > 0 |
|
1853 && selEnd > selStart) { |
|
1854 QTextLayout::FormatRange o; |
|
1855 o.start = selStart; |
|
1856 o.length = selEnd - selStart; |
|
1857 o.format = range.format; |
|
1858 selections.append(o); |
|
1859 } else if (!range.cursor.hasSelection() && range.format.hasProperty(QTextFormat::FullWidthSelection) |
|
1860 && block.contains(range.cursor.position())) { |
|
1861 // for full width selections we don't require an actual selection, just |
|
1862 // a position to specify the line. that's more convenience in usage. |
|
1863 QTextLayout::FormatRange o; |
|
1864 QTextLine l = layout->lineForTextPosition(range.cursor.position() - blpos); |
|
1865 o.start = l.textStart(); |
|
1866 o.length = l.textLength(); |
|
1867 if (o.start + o.length == bllen - 1) |
|
1868 ++o.length; // include newline |
|
1869 o.format = range.format; |
|
1870 selections.append(o); |
|
1871 } |
|
1872 } |
|
1873 |
|
1874 bool drawCursor = (editable |
|
1875 && context.cursorPosition >= blpos |
|
1876 && context.cursorPosition < blpos + bllen); |
|
1877 |
|
1878 bool drawCursorAsBlock = drawCursor && overwriteMode() ; |
|
1879 |
|
1880 if (drawCursorAsBlock) { |
|
1881 if (context.cursorPosition == blpos + bllen - 1) { |
|
1882 drawCursorAsBlock = false; |
|
1883 } else { |
|
1884 QTextLayout::FormatRange o; |
|
1885 o.start = context.cursorPosition - blpos; |
|
1886 o.length = 1; |
|
1887 o.format.setForeground(palette().base()); |
|
1888 o.format.setBackground(palette().text()); |
|
1889 selections.append(o); |
|
1890 } |
|
1891 } |
|
1892 |
|
1893 |
|
1894 layout->draw(&painter, offset, selections, er); |
|
1895 if ((drawCursor && !drawCursorAsBlock) |
|
1896 || (editable && context.cursorPosition < -1 |
|
1897 && !layout->preeditAreaText().isEmpty())) { |
|
1898 int cpos = context.cursorPosition; |
|
1899 if (cpos < -1) |
|
1900 cpos = layout->preeditAreaPosition() - (cpos + 2); |
|
1901 else |
|
1902 cpos -= blpos; |
|
1903 layout->drawCursor(&painter, offset, cpos, cursorWidth()); |
|
1904 } |
|
1905 } |
|
1906 |
|
1907 offset.ry() += r.height(); |
|
1908 if (offset.y() > viewportRect.height()) |
|
1909 break; |
|
1910 block = block.next(); |
|
1911 } |
|
1912 |
|
1913 if (backgroundVisible() && !block.isValid() && offset.y() <= er.bottom() |
|
1914 && (centerOnScroll() || verticalScrollBar()->maximum() == verticalScrollBar()->minimum())) { |
|
1915 painter.fillRect(QRect(QPoint((int)er.left(), (int)offset.y()), er.bottomRight()), palette().background()); |
|
1916 } |
|
1917 } |
|
1918 |
|
1919 |
|
1920 void QPlainTextEditPrivate::updateDefaultTextOption() |
|
1921 { |
|
1922 QTextDocument *doc = control->document(); |
|
1923 |
|
1924 QTextOption opt = doc->defaultTextOption(); |
|
1925 QTextOption::WrapMode oldWrapMode = opt.wrapMode(); |
|
1926 |
|
1927 if (lineWrap == QPlainTextEdit::NoWrap) |
|
1928 opt.setWrapMode(QTextOption::NoWrap); |
|
1929 else |
|
1930 opt.setWrapMode(wordWrap); |
|
1931 |
|
1932 if (opt.wrapMode() != oldWrapMode) |
|
1933 doc->setDefaultTextOption(opt); |
|
1934 } |
|
1935 |
|
1936 |
|
1937 /*! \reimp |
|
1938 */ |
|
1939 void QPlainTextEdit::mousePressEvent(QMouseEvent *e) |
|
1940 { |
|
1941 Q_D(QPlainTextEdit); |
|
1942 #ifdef QT_KEYPAD_NAVIGATION |
|
1943 if (QApplication::keypadNavigationEnabled() && !hasEditFocus()) |
|
1944 setEditFocus(true); |
|
1945 #endif |
|
1946 d->sendControlEvent(e); |
|
1947 } |
|
1948 |
|
1949 /*! \reimp |
|
1950 */ |
|
1951 void QPlainTextEdit::mouseMoveEvent(QMouseEvent *e) |
|
1952 { |
|
1953 Q_D(QPlainTextEdit); |
|
1954 d->inDrag = false; // paranoia |
|
1955 const QPoint pos = e->pos(); |
|
1956 d->sendControlEvent(e); |
|
1957 if (!(e->buttons() & Qt::LeftButton)) |
|
1958 return; |
|
1959 QRect visible = d->viewport->rect(); |
|
1960 if (visible.contains(pos)) |
|
1961 d->autoScrollTimer.stop(); |
|
1962 else if (!d->autoScrollTimer.isActive()) |
|
1963 d->autoScrollTimer.start(100, this); |
|
1964 } |
|
1965 |
|
1966 /*! \reimp |
|
1967 */ |
|
1968 void QPlainTextEdit::mouseReleaseEvent(QMouseEvent *e) |
|
1969 { |
|
1970 Q_D(QPlainTextEdit); |
|
1971 d->sendControlEvent(e); |
|
1972 if (d->autoScrollTimer.isActive()) { |
|
1973 d->autoScrollTimer.stop(); |
|
1974 d->ensureCursorVisible(); |
|
1975 } |
|
1976 |
|
1977 if (!isReadOnly() && rect().contains(e->pos())) |
|
1978 d->handleSoftwareInputPanel(e->button(), d->clickCausedFocus); |
|
1979 d->clickCausedFocus = 0; |
|
1980 } |
|
1981 |
|
1982 /*! \reimp |
|
1983 */ |
|
1984 void QPlainTextEdit::mouseDoubleClickEvent(QMouseEvent *e) |
|
1985 { |
|
1986 Q_D(QPlainTextEdit); |
|
1987 d->sendControlEvent(e); |
|
1988 } |
|
1989 |
|
1990 /*! \reimp |
|
1991 */ |
|
1992 bool QPlainTextEdit::focusNextPrevChild(bool next) |
|
1993 { |
|
1994 Q_D(const QPlainTextEdit); |
|
1995 if (!d->tabChangesFocus && d->control->textInteractionFlags() & Qt::TextEditable) |
|
1996 return false; |
|
1997 return QAbstractScrollArea::focusNextPrevChild(next); |
|
1998 } |
|
1999 |
|
2000 #ifndef QT_NO_CONTEXTMENU |
|
2001 /*! |
|
2002 \fn void QPlainTextEdit::contextMenuEvent(QContextMenuEvent *event) |
|
2003 |
|
2004 Shows the standard context menu created with createStandardContextMenu(). |
|
2005 |
|
2006 If you do not want the text edit to have a context menu, you can set |
|
2007 its \l contextMenuPolicy to Qt::NoContextMenu. If you want to |
|
2008 customize the context menu, reimplement this function. If you want |
|
2009 to extend the standard context menu, reimplement this function, call |
|
2010 createStandardContextMenu() and extend the menu returned. |
|
2011 |
|
2012 Information about the event is passed in the \a event object. |
|
2013 |
|
2014 \snippet doc/src/snippets/code/src_gui_widgets_qplaintextedit.cpp 0 |
|
2015 */ |
|
2016 void QPlainTextEdit::contextMenuEvent(QContextMenuEvent *e) |
|
2017 { |
|
2018 Q_D(QPlainTextEdit); |
|
2019 d->sendControlEvent(e); |
|
2020 } |
|
2021 #endif // QT_NO_CONTEXTMENU |
|
2022 |
|
2023 #ifndef QT_NO_DRAGANDDROP |
|
2024 /*! \reimp |
|
2025 */ |
|
2026 void QPlainTextEdit::dragEnterEvent(QDragEnterEvent *e) |
|
2027 { |
|
2028 Q_D(QPlainTextEdit); |
|
2029 d->inDrag = true; |
|
2030 d->sendControlEvent(e); |
|
2031 } |
|
2032 |
|
2033 /*! \reimp |
|
2034 */ |
|
2035 void QPlainTextEdit::dragLeaveEvent(QDragLeaveEvent *e) |
|
2036 { |
|
2037 Q_D(QPlainTextEdit); |
|
2038 d->inDrag = false; |
|
2039 d->autoScrollTimer.stop(); |
|
2040 d->sendControlEvent(e); |
|
2041 } |
|
2042 |
|
2043 /*! \reimp |
|
2044 */ |
|
2045 void QPlainTextEdit::dragMoveEvent(QDragMoveEvent *e) |
|
2046 { |
|
2047 Q_D(QPlainTextEdit); |
|
2048 d->autoScrollDragPos = e->pos(); |
|
2049 if (!d->autoScrollTimer.isActive()) |
|
2050 d->autoScrollTimer.start(100, this); |
|
2051 d->sendControlEvent(e); |
|
2052 } |
|
2053 |
|
2054 /*! \reimp |
|
2055 */ |
|
2056 void QPlainTextEdit::dropEvent(QDropEvent *e) |
|
2057 { |
|
2058 Q_D(QPlainTextEdit); |
|
2059 d->inDrag = false; |
|
2060 d->autoScrollTimer.stop(); |
|
2061 d->sendControlEvent(e); |
|
2062 } |
|
2063 |
|
2064 #endif // QT_NO_DRAGANDDROP |
|
2065 |
|
2066 /*! \reimp |
|
2067 */ |
|
2068 void QPlainTextEdit::inputMethodEvent(QInputMethodEvent *e) |
|
2069 { |
|
2070 Q_D(QPlainTextEdit); |
|
2071 #ifdef QT_KEYPAD_NAVIGATION |
|
2072 if (d->control->textInteractionFlags() & Qt::TextEditable |
|
2073 && QApplication::keypadNavigationEnabled() |
|
2074 && !hasEditFocus()) { |
|
2075 setEditFocus(true); |
|
2076 selectAll(); // so text is replaced rather than appended to |
|
2077 } |
|
2078 #endif |
|
2079 d->sendControlEvent(e); |
|
2080 ensureCursorVisible(); |
|
2081 } |
|
2082 |
|
2083 /*!\reimp |
|
2084 */ |
|
2085 void QPlainTextEdit::scrollContentsBy(int dx, int /*dy*/) |
|
2086 { |
|
2087 Q_D(QPlainTextEdit); |
|
2088 d->setTopLine(d->vbar->value(), dx); |
|
2089 } |
|
2090 |
|
2091 /*!\reimp |
|
2092 */ |
|
2093 QVariant QPlainTextEdit::inputMethodQuery(Qt::InputMethodQuery property) const |
|
2094 { |
|
2095 Q_D(const QPlainTextEdit); |
|
2096 QVariant v = d->control->inputMethodQuery(property); |
|
2097 const QPoint offset(-d->horizontalOffset(), -0); |
|
2098 if (v.type() == QVariant::RectF) |
|
2099 v = v.toRectF().toRect().translated(offset); |
|
2100 else if (v.type() == QVariant::PointF) |
|
2101 v = v.toPointF().toPoint() + offset; |
|
2102 else if (v.type() == QVariant::Rect) |
|
2103 v = v.toRect().translated(offset); |
|
2104 else if (v.type() == QVariant::Point) |
|
2105 v = v.toPoint() + offset; |
|
2106 return v; |
|
2107 } |
|
2108 |
|
2109 /*! \reimp |
|
2110 */ |
|
2111 void QPlainTextEdit::focusInEvent(QFocusEvent *e) |
|
2112 { |
|
2113 Q_D(QPlainTextEdit); |
|
2114 if (e->reason() == Qt::MouseFocusReason) { |
|
2115 d->clickCausedFocus = 1; |
|
2116 } |
|
2117 QAbstractScrollArea::focusInEvent(e); |
|
2118 d->sendControlEvent(e); |
|
2119 } |
|
2120 |
|
2121 /*! \reimp |
|
2122 */ |
|
2123 void QPlainTextEdit::focusOutEvent(QFocusEvent *e) |
|
2124 { |
|
2125 Q_D(QPlainTextEdit); |
|
2126 QAbstractScrollArea::focusOutEvent(e); |
|
2127 d->sendControlEvent(e); |
|
2128 } |
|
2129 |
|
2130 /*! \reimp |
|
2131 */ |
|
2132 void QPlainTextEdit::showEvent(QShowEvent *) |
|
2133 { |
|
2134 Q_D(QPlainTextEdit); |
|
2135 if (d->showCursorOnInitialShow) { |
|
2136 d->showCursorOnInitialShow = false; |
|
2137 ensureCursorVisible(); |
|
2138 } |
|
2139 } |
|
2140 |
|
2141 /*! \reimp |
|
2142 */ |
|
2143 void QPlainTextEdit::changeEvent(QEvent *e) |
|
2144 { |
|
2145 Q_D(QPlainTextEdit); |
|
2146 QAbstractScrollArea::changeEvent(e); |
|
2147 if (e->type() == QEvent::ApplicationFontChange |
|
2148 || e->type() == QEvent::FontChange) { |
|
2149 d->control->document()->setDefaultFont(font()); |
|
2150 } else if(e->type() == QEvent::ActivationChange) { |
|
2151 if (!isActiveWindow()) |
|
2152 d->autoScrollTimer.stop(); |
|
2153 } else if (e->type() == QEvent::EnabledChange) { |
|
2154 e->setAccepted(isEnabled()); |
|
2155 d->sendControlEvent(e); |
|
2156 } else if (e->type() == QEvent::PaletteChange) { |
|
2157 d->control->setPalette(palette()); |
|
2158 } else if (e->type() == QEvent::LayoutDirectionChange) { |
|
2159 d->sendControlEvent(e); |
|
2160 } |
|
2161 } |
|
2162 |
|
2163 /*! \reimp |
|
2164 */ |
|
2165 #ifndef QT_NO_WHEELEVENT |
|
2166 void QPlainTextEdit::wheelEvent(QWheelEvent *e) |
|
2167 { |
|
2168 QAbstractScrollArea::wheelEvent(e); |
|
2169 updateMicroFocus(); |
|
2170 } |
|
2171 #endif |
|
2172 |
|
2173 #ifndef QT_NO_CONTEXTMENU |
|
2174 /*! This function creates the standard context menu which is shown |
|
2175 when the user clicks on the line edit with the right mouse |
|
2176 button. It is called from the default contextMenuEvent() handler. |
|
2177 The popup menu's ownership is transferred to the caller. |
|
2178 */ |
|
2179 |
|
2180 QMenu *QPlainTextEdit::createStandardContextMenu() |
|
2181 { |
|
2182 Q_D(QPlainTextEdit); |
|
2183 return d->control->createStandardContextMenu(QPointF(), this); |
|
2184 } |
|
2185 #endif // QT_NO_CONTEXTMENU |
|
2186 |
|
2187 /*! |
|
2188 returns a QTextCursor at position \a pos (in viewport coordinates). |
|
2189 */ |
|
2190 QTextCursor QPlainTextEdit::cursorForPosition(const QPoint &pos) const |
|
2191 { |
|
2192 Q_D(const QPlainTextEdit); |
|
2193 return d->control->cursorForPosition(d->mapToContents(pos)); |
|
2194 } |
|
2195 |
|
2196 /*! |
|
2197 returns a rectangle (in viewport coordinates) that includes the |
|
2198 \a cursor. |
|
2199 */ |
|
2200 QRect QPlainTextEdit::cursorRect(const QTextCursor &cursor) const |
|
2201 { |
|
2202 Q_D(const QPlainTextEdit); |
|
2203 if (cursor.isNull()) |
|
2204 return QRect(); |
|
2205 |
|
2206 QRect r = d->control->cursorRect(cursor).toRect(); |
|
2207 r.translate(-d->horizontalOffset(),-d->verticalOffset()); |
|
2208 return r; |
|
2209 } |
|
2210 |
|
2211 /*! |
|
2212 returns a rectangle (in viewport coordinates) that includes the |
|
2213 cursor of the text edit. |
|
2214 */ |
|
2215 QRect QPlainTextEdit::cursorRect() const |
|
2216 { |
|
2217 Q_D(const QPlainTextEdit); |
|
2218 QRect r = d->control->cursorRect().toRect(); |
|
2219 r.translate(-d->horizontalOffset(),-d->verticalOffset()); |
|
2220 return r; |
|
2221 } |
|
2222 |
|
2223 |
|
2224 /*! |
|
2225 \property QPlainTextEdit::overwriteMode |
|
2226 \brief whether text entered by the user will overwrite existing text |
|
2227 |
|
2228 As with many text editors, the plain text editor widget can be configured |
|
2229 to insert or overwrite existing text with new text entered by the user. |
|
2230 |
|
2231 If this property is true, existing text is overwritten, character-for-character |
|
2232 by new text; otherwise, text is inserted at the cursor position, displacing |
|
2233 existing text. |
|
2234 |
|
2235 By default, this property is false (new text does not overwrite existing text). |
|
2236 */ |
|
2237 |
|
2238 bool QPlainTextEdit::overwriteMode() const |
|
2239 { |
|
2240 Q_D(const QPlainTextEdit); |
|
2241 return d->control->overwriteMode(); |
|
2242 } |
|
2243 |
|
2244 void QPlainTextEdit::setOverwriteMode(bool overwrite) |
|
2245 { |
|
2246 Q_D(QPlainTextEdit); |
|
2247 d->control->setOverwriteMode(overwrite); |
|
2248 } |
|
2249 |
|
2250 /*! |
|
2251 \property QPlainTextEdit::tabStopWidth |
|
2252 \brief the tab stop width in pixels |
|
2253 |
|
2254 By default, this property contains a value of 80. |
|
2255 */ |
|
2256 |
|
2257 int QPlainTextEdit::tabStopWidth() const |
|
2258 { |
|
2259 Q_D(const QPlainTextEdit); |
|
2260 return qRound(d->control->document()->defaultTextOption().tabStop()); |
|
2261 } |
|
2262 |
|
2263 void QPlainTextEdit::setTabStopWidth(int width) |
|
2264 { |
|
2265 Q_D(QPlainTextEdit); |
|
2266 QTextOption opt = d->control->document()->defaultTextOption(); |
|
2267 if (opt.tabStop() == width || width < 0) |
|
2268 return; |
|
2269 opt.setTabStop(width); |
|
2270 d->control->document()->setDefaultTextOption(opt); |
|
2271 } |
|
2272 |
|
2273 /*! |
|
2274 \property QPlainTextEdit::cursorWidth |
|
2275 |
|
2276 This property specifies the width of the cursor in pixels. The default value is 1. |
|
2277 */ |
|
2278 int QPlainTextEdit::cursorWidth() const |
|
2279 { |
|
2280 Q_D(const QPlainTextEdit); |
|
2281 return d->control->cursorWidth(); |
|
2282 } |
|
2283 |
|
2284 void QPlainTextEdit::setCursorWidth(int width) |
|
2285 { |
|
2286 Q_D(QPlainTextEdit); |
|
2287 d->control->setCursorWidth(width); |
|
2288 } |
|
2289 |
|
2290 |
|
2291 |
|
2292 /*! |
|
2293 This function allows temporarily marking certain regions in the document |
|
2294 with a given color, specified as \a selections. This can be useful for |
|
2295 example in a programming editor to mark a whole line of text with a given |
|
2296 background color to indicate the existence of a breakpoint. |
|
2297 |
|
2298 \sa QTextEdit::ExtraSelection, extraSelections() |
|
2299 */ |
|
2300 void QPlainTextEdit::setExtraSelections(const QList<QTextEdit::ExtraSelection> &selections) |
|
2301 { |
|
2302 Q_D(QPlainTextEdit); |
|
2303 d->control->setExtraSelections(selections); |
|
2304 } |
|
2305 |
|
2306 /*! |
|
2307 Returns previously set extra selections. |
|
2308 |
|
2309 \sa setExtraSelections() |
|
2310 */ |
|
2311 QList<QTextEdit::ExtraSelection> QPlainTextEdit::extraSelections() const |
|
2312 { |
|
2313 Q_D(const QPlainTextEdit); |
|
2314 return d->control->extraSelections(); |
|
2315 } |
|
2316 |
|
2317 /*! |
|
2318 This function returns a new MIME data object to represent the contents |
|
2319 of the text edit's current selection. It is called when the selection needs |
|
2320 to be encapsulated into a new QMimeData object; for example, when a drag |
|
2321 and drop operation is started, or when data is copied to the clipboard. |
|
2322 |
|
2323 If you reimplement this function, note that the ownership of the returned |
|
2324 QMimeData object is passed to the caller. The selection can be retrieved |
|
2325 by using the textCursor() function. |
|
2326 */ |
|
2327 QMimeData *QPlainTextEdit::createMimeDataFromSelection() const |
|
2328 { |
|
2329 Q_D(const QPlainTextEdit); |
|
2330 return d->control->QTextControl::createMimeDataFromSelection(); |
|
2331 } |
|
2332 |
|
2333 /*! |
|
2334 This function returns true if the contents of the MIME data object, specified |
|
2335 by \a source, can be decoded and inserted into the document. It is called |
|
2336 for example when during a drag operation the mouse enters this widget and it |
|
2337 is necessary to determine whether it is possible to accept the drag. |
|
2338 */ |
|
2339 bool QPlainTextEdit::canInsertFromMimeData(const QMimeData *source) const |
|
2340 { |
|
2341 Q_D(const QPlainTextEdit); |
|
2342 return d->control->QTextControl::canInsertFromMimeData(source); |
|
2343 } |
|
2344 |
|
2345 /*! |
|
2346 This function inserts the contents of the MIME data object, specified |
|
2347 by \a source, into the text edit at the current cursor position. It is |
|
2348 called whenever text is inserted as the result of a clipboard paste |
|
2349 operation, or when the text edit accepts data from a drag and drop |
|
2350 operation. |
|
2351 */ |
|
2352 void QPlainTextEdit::insertFromMimeData(const QMimeData *source) |
|
2353 { |
|
2354 Q_D(QPlainTextEdit); |
|
2355 d->control->QTextControl::insertFromMimeData(source); |
|
2356 } |
|
2357 |
|
2358 /*! |
|
2359 \property QPlainTextEdit::readOnly |
|
2360 \brief whether the text edit is read-only |
|
2361 |
|
2362 In a read-only text edit the user can only navigate through the |
|
2363 text and select text; modifying the text is not possible. |
|
2364 |
|
2365 This property's default is false. |
|
2366 */ |
|
2367 |
|
2368 bool QPlainTextEdit::isReadOnly() const |
|
2369 { |
|
2370 Q_D(const QPlainTextEdit); |
|
2371 return !(d->control->textInteractionFlags() & Qt::TextEditable); |
|
2372 } |
|
2373 |
|
2374 void QPlainTextEdit::setReadOnly(bool ro) |
|
2375 { |
|
2376 Q_D(QPlainTextEdit); |
|
2377 Qt::TextInteractionFlags flags = Qt::NoTextInteraction; |
|
2378 if (ro) { |
|
2379 flags = Qt::TextSelectableByMouse; |
|
2380 } else { |
|
2381 flags = Qt::TextEditorInteraction; |
|
2382 } |
|
2383 setAttribute(Qt::WA_InputMethodEnabled, shouldEnableInputMethod(this)); |
|
2384 d->control->setTextInteractionFlags(flags); |
|
2385 } |
|
2386 |
|
2387 /*! |
|
2388 \property QPlainTextEdit::textInteractionFlags |
|
2389 |
|
2390 Specifies how the label should interact with user input if it displays text. |
|
2391 |
|
2392 If the flags contain either Qt::LinksAccessibleByKeyboard or Qt::TextSelectableByKeyboard |
|
2393 then the focus policy is also automatically set to Qt::ClickFocus. |
|
2394 |
|
2395 The default value depends on whether the QPlainTextEdit is read-only |
|
2396 or editable, and whether it is a QTextBrowser or not. |
|
2397 */ |
|
2398 |
|
2399 void QPlainTextEdit::setTextInteractionFlags(Qt::TextInteractionFlags flags) |
|
2400 { |
|
2401 Q_D(QPlainTextEdit); |
|
2402 d->control->setTextInteractionFlags(flags); |
|
2403 } |
|
2404 |
|
2405 Qt::TextInteractionFlags QPlainTextEdit::textInteractionFlags() const |
|
2406 { |
|
2407 Q_D(const QPlainTextEdit); |
|
2408 return d->control->textInteractionFlags(); |
|
2409 } |
|
2410 |
|
2411 /*! |
|
2412 Merges the properties specified in \a modifier into the current character |
|
2413 format by calling QTextCursor::mergeCharFormat on the editor's cursor. |
|
2414 If the editor has a selection then the properties of \a modifier are |
|
2415 directly applied to the selection. |
|
2416 |
|
2417 \sa QTextCursor::mergeCharFormat() |
|
2418 */ |
|
2419 void QPlainTextEdit::mergeCurrentCharFormat(const QTextCharFormat &modifier) |
|
2420 { |
|
2421 Q_D(QPlainTextEdit); |
|
2422 d->control->mergeCurrentCharFormat(modifier); |
|
2423 } |
|
2424 |
|
2425 /*! |
|
2426 Sets the char format that is be used when inserting new text to \a |
|
2427 format by calling QTextCursor::setCharFormat() on the editor's |
|
2428 cursor. If the editor has a selection then the char format is |
|
2429 directly applied to the selection. |
|
2430 */ |
|
2431 void QPlainTextEdit::setCurrentCharFormat(const QTextCharFormat &format) |
|
2432 { |
|
2433 Q_D(QPlainTextEdit); |
|
2434 d->control->setCurrentCharFormat(format); |
|
2435 } |
|
2436 |
|
2437 /*! |
|
2438 Returns the char format that is used when inserting new text. |
|
2439 */ |
|
2440 QTextCharFormat QPlainTextEdit::currentCharFormat() const |
|
2441 { |
|
2442 Q_D(const QPlainTextEdit); |
|
2443 return d->control->currentCharFormat(); |
|
2444 } |
|
2445 |
|
2446 |
|
2447 |
|
2448 /*! |
|
2449 Convenience slot that inserts \a text at the current |
|
2450 cursor position. |
|
2451 |
|
2452 It is equivalent to |
|
2453 |
|
2454 \snippet doc/src/snippets/code/src_gui_widgets_qplaintextedit.cpp 1 |
|
2455 */ |
|
2456 void QPlainTextEdit::insertPlainText(const QString &text) |
|
2457 { |
|
2458 Q_D(QPlainTextEdit); |
|
2459 d->control->insertPlainText(text); |
|
2460 } |
|
2461 |
|
2462 |
|
2463 /*! |
|
2464 Moves the cursor by performing the given \a operation. |
|
2465 |
|
2466 If \a mode is QTextCursor::KeepAnchor, the cursor selects the text it moves over. |
|
2467 This is the same effect that the user achieves when they hold down the Shift key |
|
2468 and move the cursor with the cursor keys. |
|
2469 |
|
2470 \sa QTextCursor::movePosition() |
|
2471 */ |
|
2472 void QPlainTextEdit::moveCursor(QTextCursor::MoveOperation operation, QTextCursor::MoveMode mode) |
|
2473 { |
|
2474 Q_D(QPlainTextEdit); |
|
2475 d->control->moveCursor(operation, mode); |
|
2476 } |
|
2477 |
|
2478 /*! |
|
2479 Returns whether text can be pasted from the clipboard into the textedit. |
|
2480 */ |
|
2481 bool QPlainTextEdit::canPaste() const |
|
2482 { |
|
2483 Q_D(const QPlainTextEdit); |
|
2484 return d->control->canPaste(); |
|
2485 } |
|
2486 |
|
2487 #ifndef QT_NO_PRINTER |
|
2488 /*! |
|
2489 Convenience function to print the text edit's document to the given \a printer. This |
|
2490 is equivalent to calling the print method on the document directly except that this |
|
2491 function also supports QPrinter::Selection as print range. |
|
2492 |
|
2493 \sa QTextDocument::print() |
|
2494 */ |
|
2495 void QPlainTextEdit::print(QPrinter *printer) const |
|
2496 { |
|
2497 Q_D(const QPlainTextEdit); |
|
2498 d->control->print(printer); |
|
2499 } |
|
2500 #endif // QT _NO_PRINTER |
|
2501 |
|
2502 /*! \property QPlainTextEdit::tabChangesFocus |
|
2503 \brief whether \gui Tab changes focus or is accepted as input |
|
2504 |
|
2505 In some occasions text edits should not allow the user to input |
|
2506 tabulators or change indentation using the \gui Tab key, as this breaks |
|
2507 the focus chain. The default is false. |
|
2508 |
|
2509 */ |
|
2510 |
|
2511 bool QPlainTextEdit::tabChangesFocus() const |
|
2512 { |
|
2513 Q_D(const QPlainTextEdit); |
|
2514 return d->tabChangesFocus; |
|
2515 } |
|
2516 |
|
2517 void QPlainTextEdit::setTabChangesFocus(bool b) |
|
2518 { |
|
2519 Q_D(QPlainTextEdit); |
|
2520 d->tabChangesFocus = b; |
|
2521 } |
|
2522 |
|
2523 /*! |
|
2524 \property QPlainTextEdit::documentTitle |
|
2525 \brief the title of the document parsed from the text. |
|
2526 |
|
2527 By default, this property contains an empty string. |
|
2528 */ |
|
2529 |
|
2530 /*! |
|
2531 \property QPlainTextEdit::lineWrapMode |
|
2532 \brief the line wrap mode |
|
2533 |
|
2534 The default mode is WidgetWidth which causes words to be |
|
2535 wrapped at the right edge of the text edit. Wrapping occurs at |
|
2536 whitespace, keeping whole words intact. If you want wrapping to |
|
2537 occur within words use setWordWrapMode(). |
|
2538 */ |
|
2539 |
|
2540 QPlainTextEdit::LineWrapMode QPlainTextEdit::lineWrapMode() const |
|
2541 { |
|
2542 Q_D(const QPlainTextEdit); |
|
2543 return d->lineWrap; |
|
2544 } |
|
2545 |
|
2546 void QPlainTextEdit::setLineWrapMode(LineWrapMode wrap) |
|
2547 { |
|
2548 Q_D(QPlainTextEdit); |
|
2549 if (d->lineWrap == wrap) |
|
2550 return; |
|
2551 d->lineWrap = wrap; |
|
2552 d->updateDefaultTextOption(); |
|
2553 d->relayoutDocument(); |
|
2554 d->_q_adjustScrollbars(); |
|
2555 ensureCursorVisible(); |
|
2556 } |
|
2557 |
|
2558 /*! |
|
2559 \property QPlainTextEdit::wordWrapMode |
|
2560 \brief the mode QPlainTextEdit will use when wrapping text by words |
|
2561 |
|
2562 By default, this property is set to QTextOption::WrapAtWordBoundaryOrAnywhere. |
|
2563 |
|
2564 \sa QTextOption::WrapMode |
|
2565 */ |
|
2566 |
|
2567 QTextOption::WrapMode QPlainTextEdit::wordWrapMode() const |
|
2568 { |
|
2569 Q_D(const QPlainTextEdit); |
|
2570 return d->wordWrap; |
|
2571 } |
|
2572 |
|
2573 void QPlainTextEdit::setWordWrapMode(QTextOption::WrapMode mode) |
|
2574 { |
|
2575 Q_D(QPlainTextEdit); |
|
2576 if (mode == d->wordWrap) |
|
2577 return; |
|
2578 d->wordWrap = mode; |
|
2579 d->updateDefaultTextOption(); |
|
2580 } |
|
2581 |
|
2582 /*! |
|
2583 \property QPlainTextEdit::backgroundVisible |
|
2584 \brief whether the palette background is visible outside the document area |
|
2585 |
|
2586 If set to true, the plain text edit paints the palette background |
|
2587 on the viewport area not covered by the text document. Otherwise, |
|
2588 if set to false, it won't. The feature makes it possible for |
|
2589 the user to visually distinguish between the area of the document, |
|
2590 painted with the base color of the palette, and the empty |
|
2591 area not covered by any document. |
|
2592 |
|
2593 The default is false. |
|
2594 */ |
|
2595 |
|
2596 bool QPlainTextEdit::backgroundVisible() const |
|
2597 { |
|
2598 Q_D(const QPlainTextEdit); |
|
2599 return d->backgroundVisible; |
|
2600 } |
|
2601 |
|
2602 void QPlainTextEdit::setBackgroundVisible(bool visible) |
|
2603 { |
|
2604 Q_D(QPlainTextEdit); |
|
2605 if (visible == d->backgroundVisible) |
|
2606 return; |
|
2607 d->backgroundVisible = visible; |
|
2608 d->updateViewport(); |
|
2609 } |
|
2610 |
|
2611 /*! |
|
2612 \property QPlainTextEdit::centerOnScroll |
|
2613 \brief whether the cursor should be centered on screen |
|
2614 |
|
2615 If set to true, the plain text edit scrolls the document |
|
2616 vertically to make the cursor visible at the center of the |
|
2617 viewport. This also allows the text edit to scroll below the end |
|
2618 of the document. Otherwise, if set to false, the plain text edit |
|
2619 scrolls the smallest amount possible to ensure the cursor is |
|
2620 visible. The same algorithm is applied to any new line appended |
|
2621 through appendPlainText(). |
|
2622 |
|
2623 The default is false. |
|
2624 |
|
2625 \sa centerCursor(), ensureCursorVisible() |
|
2626 */ |
|
2627 |
|
2628 bool QPlainTextEdit::centerOnScroll() const |
|
2629 { |
|
2630 Q_D(const QPlainTextEdit); |
|
2631 return d->centerOnScroll; |
|
2632 } |
|
2633 |
|
2634 void QPlainTextEdit::setCenterOnScroll(bool enabled) |
|
2635 { |
|
2636 Q_D(QPlainTextEdit); |
|
2637 if (enabled == d->centerOnScroll) |
|
2638 return; |
|
2639 d->centerOnScroll = enabled; |
|
2640 } |
|
2641 |
|
2642 |
|
2643 |
|
2644 /*! |
|
2645 Finds the next occurrence of the string, \a exp, using the given |
|
2646 \a options. Returns true if \a exp was found and changes the |
|
2647 cursor to select the match; otherwise returns false. |
|
2648 */ |
|
2649 bool QPlainTextEdit::find(const QString &exp, QTextDocument::FindFlags options) |
|
2650 { |
|
2651 Q_D(QPlainTextEdit); |
|
2652 return d->control->find(exp, options); |
|
2653 } |
|
2654 |
|
2655 /*! |
|
2656 \fn void QPlainTextEdit::copyAvailable(bool yes) |
|
2657 |
|
2658 This signal is emitted when text is selected or de-selected in the |
|
2659 text edit. |
|
2660 |
|
2661 When text is selected this signal will be emitted with \a yes set |
|
2662 to true. If no text has been selected or if the selected text is |
|
2663 de-selected this signal is emitted with \a yes set to false. |
|
2664 |
|
2665 If \a yes is true then copy() can be used to copy the selection to |
|
2666 the clipboard. If \a yes is false then copy() does nothing. |
|
2667 |
|
2668 \sa selectionChanged() |
|
2669 */ |
|
2670 |
|
2671 |
|
2672 /*! |
|
2673 \fn void QPlainTextEdit::selectionChanged() |
|
2674 |
|
2675 This signal is emitted whenever the selection changes. |
|
2676 |
|
2677 \sa copyAvailable() |
|
2678 */ |
|
2679 |
|
2680 /*! |
|
2681 \fn void QPlainTextEdit::cursorPositionChanged() |
|
2682 |
|
2683 This signal is emitted whenever the position of the |
|
2684 cursor changed. |
|
2685 */ |
|
2686 |
|
2687 |
|
2688 |
|
2689 /*! |
|
2690 \fn void QPlainTextEdit::updateRequest(const QRect &rect, int dy) |
|
2691 |
|
2692 This signal is emitted when the text document needs an update of |
|
2693 the specified \a rect. If the text is scrolled, \a rect will cover |
|
2694 the entire viewport area. If the text is scrolled vertically, \a |
|
2695 dy carries the amount of pixels the viewport was scrolled. |
|
2696 |
|
2697 The purpose of the signal is to support extra widgets in plain |
|
2698 text edit subclasses that e.g. show line numbers, breakpoints, or |
|
2699 other extra information. |
|
2700 */ |
|
2701 |
|
2702 /*! \fn void QPlainTextEdit::blockCountChanged(int newBlockCount); |
|
2703 |
|
2704 This signal is emitted whenever the block count changes. The new |
|
2705 block count is passed in \a newBlockCount. |
|
2706 */ |
|
2707 |
|
2708 /*! \fn void QPlainTextEdit::modificationChanged(bool changed); |
|
2709 |
|
2710 This signal is emitted whenever the content of the document |
|
2711 changes in a way that affects the modification state. If \a |
|
2712 changed is true, the document has been modified; otherwise it is |
|
2713 false. |
|
2714 |
|
2715 For example, calling setModified(false) on a document and then |
|
2716 inserting text causes the signal to get emitted. If you undo that |
|
2717 operation, causing the document to return to its original |
|
2718 unmodified state, the signal will get emitted again. |
|
2719 */ |
|
2720 |
|
2721 |
|
2722 |
|
2723 |
|
2724 void QPlainTextEditPrivate::append(const QString &text, Qt::TextFormat format) |
|
2725 { |
|
2726 Q_Q(QPlainTextEdit); |
|
2727 |
|
2728 QTextDocument *document = control->document(); |
|
2729 QPlainTextDocumentLayout *documentLayout = qobject_cast<QPlainTextDocumentLayout*>(document->documentLayout()); |
|
2730 Q_ASSERT(documentLayout); |
|
2731 |
|
2732 int maximumBlockCount = document->maximumBlockCount(); |
|
2733 if (maximumBlockCount) |
|
2734 document->setMaximumBlockCount(0); |
|
2735 |
|
2736 const bool atBottom = q->isVisible() |
|
2737 && (control->blockBoundingRect(document->lastBlock()).bottom() - verticalOffset() |
|
2738 <= viewport->rect().bottom()); |
|
2739 |
|
2740 if (!q->isVisible()) |
|
2741 showCursorOnInitialShow = true; |
|
2742 |
|
2743 bool documentSizeChangedBlocked = documentLayout->priv()->blockDocumentSizeChanged; |
|
2744 documentLayout->priv()->blockDocumentSizeChanged = true; |
|
2745 |
|
2746 if (format == Qt::RichText) |
|
2747 control->appendHtml(text); |
|
2748 else if (format == Qt::PlainText) |
|
2749 control->appendPlainText(text); |
|
2750 else |
|
2751 control->append(text); |
|
2752 |
|
2753 if (maximumBlockCount > 0) { |
|
2754 if (document->blockCount() > maximumBlockCount) { |
|
2755 bool blockUpdate = false; |
|
2756 if (control->topBlock) { |
|
2757 control->topBlock--; |
|
2758 blockUpdate = true; |
|
2759 emit q->updateRequest(viewport->rect(), 0); |
|
2760 } |
|
2761 |
|
2762 bool updatesBlocked = documentLayout->priv()->blockUpdate; |
|
2763 documentLayout->priv()->blockUpdate = blockUpdate; |
|
2764 QTextCursor cursor(document); |
|
2765 cursor.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor); |
|
2766 cursor.removeSelectedText(); |
|
2767 documentLayout->priv()->blockUpdate = updatesBlocked; |
|
2768 } |
|
2769 document->setMaximumBlockCount(maximumBlockCount); |
|
2770 } |
|
2771 |
|
2772 documentLayout->priv()->blockDocumentSizeChanged = documentSizeChangedBlocked; |
|
2773 _q_adjustScrollbars(); |
|
2774 |
|
2775 |
|
2776 if (atBottom) { |
|
2777 const bool needScroll = !centerOnScroll |
|
2778 || control->blockBoundingRect(document->lastBlock()).bottom() - verticalOffset() |
|
2779 > viewport->rect().bottom(); |
|
2780 if (needScroll) |
|
2781 vbar->setValue(vbar->maximum()); |
|
2782 } |
|
2783 } |
|
2784 |
|
2785 |
|
2786 /*! |
|
2787 Appends a new paragraph with \a text to the end of the text edit. |
|
2788 |
|
2789 \sa appendHtml() |
|
2790 */ |
|
2791 |
|
2792 void QPlainTextEdit::appendPlainText(const QString &text) |
|
2793 { |
|
2794 Q_D(QPlainTextEdit); |
|
2795 d->append(text, Qt::PlainText); |
|
2796 } |
|
2797 |
|
2798 /*! |
|
2799 Appends a new paragraph with \a html to the end of the text edit. |
|
2800 |
|
2801 appendPlainText() |
|
2802 */ |
|
2803 |
|
2804 void QPlainTextEdit::appendHtml(const QString &html) |
|
2805 { |
|
2806 Q_D(QPlainTextEdit); |
|
2807 d->append(html, Qt::RichText); |
|
2808 } |
|
2809 |
|
2810 void QPlainTextEditPrivate::ensureCursorVisible(bool center) |
|
2811 { |
|
2812 Q_Q(QPlainTextEdit); |
|
2813 QRect visible = viewport->rect(); |
|
2814 QRect cr = q->cursorRect(); |
|
2815 if (cr.top() < visible.top() || cr.bottom() > visible.bottom()) { |
|
2816 ensureVisible(control->textCursor().position(), center); |
|
2817 } |
|
2818 |
|
2819 const bool rtl = q->isRightToLeft(); |
|
2820 if (cr.left() < visible.left() || cr.right() > visible.right()) { |
|
2821 int x = cr.center().x() + horizontalOffset() - visible.width()/2; |
|
2822 hbar->setValue(rtl ? hbar->maximum() - x : x); |
|
2823 } |
|
2824 } |
|
2825 |
|
2826 /*! |
|
2827 Ensures that the cursor is visible by scrolling the text edit if |
|
2828 necessary. |
|
2829 |
|
2830 \sa centerCursor(), centerOnScroll |
|
2831 */ |
|
2832 void QPlainTextEdit::ensureCursorVisible() |
|
2833 { |
|
2834 Q_D(QPlainTextEdit); |
|
2835 d->ensureCursorVisible(d->centerOnScroll); |
|
2836 } |
|
2837 |
|
2838 |
|
2839 /*! Scrolls the document in order to center the cursor vertically. |
|
2840 |
|
2841 \sa ensureCursorVisible(), centerOnScroll |
|
2842 */ |
|
2843 void QPlainTextEdit::centerCursor() |
|
2844 { |
|
2845 Q_D(QPlainTextEdit); |
|
2846 d->ensureVisible(textCursor().position(), true, true); |
|
2847 } |
|
2848 |
|
2849 /*! |
|
2850 Returns the first visible block. |
|
2851 |
|
2852 \sa blockBoundingRect() |
|
2853 */ |
|
2854 QTextBlock QPlainTextEdit::firstVisibleBlock() const |
|
2855 { |
|
2856 Q_D(const QPlainTextEdit); |
|
2857 return d->control->firstVisibleBlock(); |
|
2858 } |
|
2859 |
|
2860 /*! Returns the content's origin in viewport coordinates. |
|
2861 |
|
2862 The origin of the content of a plain text edit is always the top |
|
2863 left corner of the first visible text block. The content offset |
|
2864 is different from (0,0) when the text has been scrolled |
|
2865 horizontally, or when the first visible block has been scrolled |
|
2866 partially off the screen, i.e. the visible text does not start |
|
2867 with the first line of the first visible block, or when the first |
|
2868 visible block is the very first block and the editor displays a |
|
2869 margin. |
|
2870 |
|
2871 \sa firstVisibleBlock(), horizontalScrollBar(), verticalScrollBar() |
|
2872 */ |
|
2873 QPointF QPlainTextEdit::contentOffset() const |
|
2874 { |
|
2875 Q_D(const QPlainTextEdit); |
|
2876 return QPointF(-d->horizontalOffset(), -d->verticalOffset()); |
|
2877 } |
|
2878 |
|
2879 |
|
2880 /*! Returns the bounding rectangle of the text \a block in content |
|
2881 coordinates. Translate the rectangle with the contentOffset() to get |
|
2882 visual coordinates on the viewport. |
|
2883 |
|
2884 \sa firstVisibleBlock(), blockBoundingRect() |
|
2885 */ |
|
2886 QRectF QPlainTextEdit::blockBoundingGeometry(const QTextBlock &block) const |
|
2887 { |
|
2888 Q_D(const QPlainTextEdit); |
|
2889 return d->control->blockBoundingRect(block); |
|
2890 } |
|
2891 |
|
2892 /*! |
|
2893 Returns the bounding rectangle of the text \a block in the block's own coordinates. |
|
2894 |
|
2895 \sa blockBoundingGeometry() |
|
2896 */ |
|
2897 QRectF QPlainTextEdit::blockBoundingRect(const QTextBlock &block) const |
|
2898 { |
|
2899 QPlainTextDocumentLayout *documentLayout = qobject_cast<QPlainTextDocumentLayout*>(document()->documentLayout()); |
|
2900 Q_ASSERT(documentLayout); |
|
2901 return documentLayout->blockBoundingRect(block); |
|
2902 } |
|
2903 |
|
2904 /*! |
|
2905 \property QPlainTextEdit::blockCount |
|
2906 \brief the number of text blocks in the document. |
|
2907 |
|
2908 By default, in an empty document, this property contains a value of 1. |
|
2909 */ |
|
2910 int QPlainTextEdit::blockCount() const |
|
2911 { |
|
2912 return document()->blockCount(); |
|
2913 } |
|
2914 |
|
2915 /*! Returns the paint context for the viewport(), useful only when |
|
2916 reimplementing paintEvent(). |
|
2917 */ |
|
2918 QAbstractTextDocumentLayout::PaintContext QPlainTextEdit::getPaintContext() const |
|
2919 { |
|
2920 Q_D(const QPlainTextEdit); |
|
2921 return d->control->getPaintContext(d->viewport); |
|
2922 } |
|
2923 |
|
2924 /*! |
|
2925 \property QPlainTextEdit::maximumBlockCount |
|
2926 \brief the limit for blocks in the document. |
|
2927 |
|
2928 Specifies the maximum number of blocks the document may have. If there are |
|
2929 more blocks in the document that specified with this property blocks are removed |
|
2930 from the beginning of the document. |
|
2931 |
|
2932 A negative or zero value specifies that the document may contain an unlimited |
|
2933 amount of blocks. |
|
2934 |
|
2935 The default value is 0. |
|
2936 |
|
2937 Note that setting this property will apply the limit immediately to the document |
|
2938 contents. Setting this property also disables the undo redo history. |
|
2939 |
|
2940 */ |
|
2941 |
|
2942 |
|
2943 /*! |
|
2944 \fn void QPlainTextEdit::textChanged() |
|
2945 |
|
2946 This signal is emitted whenever the document's content changes; for |
|
2947 example, when text is inserted or deleted, or when formatting is applied. |
|
2948 */ |
|
2949 |
|
2950 /*! |
|
2951 \fn void QPlainTextEdit::undoAvailable(bool available) |
|
2952 |
|
2953 This signal is emitted whenever undo operations become available |
|
2954 (\a available is true) or unavailable (\a available is false). |
|
2955 */ |
|
2956 |
|
2957 /*! |
|
2958 \fn void QPlainTextEdit::redoAvailable(bool available) |
|
2959 |
|
2960 This signal is emitted whenever redo operations become available |
|
2961 (\a available is true) or unavailable (\a available is false). |
|
2962 */ |
|
2963 |
|
2964 QT_END_NAMESPACE |
|
2965 |
|
2966 #include "moc_qplaintextedit.cpp" |
|
2967 #include "moc_qplaintextedit_p.cpp" |
|
2968 |
|
2969 #endif // QT_NO_TEXTEDIT |