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