1/****************************************************************************
2**
3** Copyright (C) 2019 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtWidgets module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qtextedit_p.h"
41#if QT_CONFIG(lineedit)
42#include "qlineedit.h"
43#endif
44#if QT_CONFIG(textbrowser)
45#include "qtextbrowser.h"
46#endif
47
48#include <qfont.h>
49#include <qpainter.h>
50#include <qevent.h>
51#include <qdebug.h>
52#if QT_CONFIG(draganddrop)
53#include <qdrag.h>
54#endif
55#include <qclipboard.h>
56#if QT_CONFIG(menu)
57#include <qmenu.h>
58#endif
59#include <qstyle.h>
60#include <qtimer.h>
61#ifndef QT_NO_ACCESSIBILITY
62#include <qaccessible.h>
63#endif
64#include "private/qtextdocumentlayout_p.h"
65#include "qtextdocument.h"
66#include "private/qtextdocument_p.h"
67#include "qtextlist.h"
68#include "private/qwidgettextcontrol_p.h"
69
70#include <qtextformat.h>
71#include <qdatetime.h>
72#include <qapplication.h>
73#include <private/qapplication_p.h>
74#include <limits.h>
75#include <qtexttable.h>
76#include <qvariant.h>
77
78QT_BEGIN_NAMESPACE
79
80static inline bool shouldEnableInputMethod(QTextEdit *textedit)
81{
82 return !textedit->isReadOnly();
83}
84
85class QTextEditControl : public QWidgetTextControl
86{
87public:
88 inline QTextEditControl(QObject *parent) : QWidgetTextControl(parent) {}
89
90 virtual QMimeData *createMimeDataFromSelection() const override {
91 QTextEdit *ed = qobject_cast<QTextEdit *>(parent());
92 if (!ed)
93 return QWidgetTextControl::createMimeDataFromSelection();
94 return ed->createMimeDataFromSelection();
95 }
96 virtual bool canInsertFromMimeData(const QMimeData *source) const override {
97 QTextEdit *ed = qobject_cast<QTextEdit *>(parent());
98 if (!ed)
99 return QWidgetTextControl::canInsertFromMimeData(source);
100 return ed->canInsertFromMimeData(source);
101 }
102 virtual void insertFromMimeData(const QMimeData *source) override {
103 QTextEdit *ed = qobject_cast<QTextEdit *>(parent());
104 if (!ed)
105 QWidgetTextControl::insertFromMimeData(source);
106 else
107 ed->insertFromMimeData(source);
108 }
109 QVariant loadResource(int type, const QUrl &name) override {
110 auto *ed = qobject_cast<QTextEdit *>(parent());
111 if (!ed)
112 return QWidgetTextControl::loadResource(type, name);
113
114 QUrl resolvedName = ed->d_func()->resolveUrl(name);
115 return ed->loadResource(type, resolvedName);
116 }
117};
118
119QTextEditPrivate::QTextEditPrivate()
120 : control(0),
121 autoFormatting(QTextEdit::AutoNone), tabChangesFocus(false),
122 lineWrap(QTextEdit::WidgetWidth), lineWrapColumnOrWidth(0),
123 wordWrap(QTextOption::WrapAtWordBoundaryOrAnywhere), clickCausedFocus(0),
124 textFormat(Qt::AutoText)
125{
126 ignoreAutomaticScrollbarAdjustment = false;
127 preferRichText = false;
128 showCursorOnInitialShow = true;
129 inDrag = false;
130}
131
132void QTextEditPrivate::createAutoBulletList()
133{
134 QTextCursor cursor = control->textCursor();
135 cursor.beginEditBlock();
136
137 QTextBlockFormat blockFmt = cursor.blockFormat();
138
139 QTextListFormat listFmt;
140 listFmt.setStyle(QTextListFormat::ListDisc);
141 listFmt.setIndent(blockFmt.indent() + 1);
142
143 blockFmt.setIndent(0);
144 cursor.setBlockFormat(blockFmt);
145
146 cursor.createList(listFmt);
147
148 cursor.endEditBlock();
149 control->setTextCursor(cursor);
150}
151
152void QTextEditPrivate::init(const QString &html)
153{
154 Q_Q(QTextEdit);
155 control = new QTextEditControl(q);
156 control->setPalette(q->palette());
157
158 QObject::connect(control, SIGNAL(microFocusChanged()), q, SLOT(updateMicroFocus()));
159 QObject::connect(control, SIGNAL(documentSizeChanged(QSizeF)), q, SLOT(_q_adjustScrollbars()));
160 QObject::connect(control, SIGNAL(updateRequest(QRectF)), q, SLOT(_q_repaintContents(QRectF)));
161 QObject::connect(control, SIGNAL(visibilityRequest(QRectF)), q, SLOT(_q_ensureVisible(QRectF)));
162 QObject::connect(control, SIGNAL(currentCharFormatChanged(QTextCharFormat)),
163 q, SLOT(_q_currentCharFormatChanged(QTextCharFormat)));
164
165 QObject::connect(control, SIGNAL(textChanged()), q, SIGNAL(textChanged()));
166 QObject::connect(control, SIGNAL(undoAvailable(bool)), q, SIGNAL(undoAvailable(bool)));
167 QObject::connect(control, SIGNAL(redoAvailable(bool)), q, SIGNAL(redoAvailable(bool)));
168 QObject::connect(control, SIGNAL(copyAvailable(bool)), q, SIGNAL(copyAvailable(bool)));
169 QObject::connect(control, SIGNAL(selectionChanged()), q, SIGNAL(selectionChanged()));
170 QObject::connect(control, SIGNAL(cursorPositionChanged()), q, SLOT(_q_cursorPositionChanged()));
171#if QT_CONFIG(cursor)
172 QObject::connect(control, SIGNAL(blockMarkerHovered(QTextBlock)), q, SLOT(_q_hoveredBlockWithMarkerChanged(QTextBlock)));
173#endif
174
175 QObject::connect(control, SIGNAL(textChanged()), q, SLOT(updateMicroFocus()));
176
177 QTextDocument *doc = control->document();
178 // set a null page size initially to avoid any relayouting until the textedit
179 // is shown. relayoutDocument() will take care of setting the page size to the
180 // viewport dimensions later.
181 doc->setPageSize(QSize(0, 0));
182 doc->documentLayout()->setPaintDevice(viewport);
183 doc->setDefaultFont(q->font());
184 doc->setUndoRedoEnabled(false); // flush undo buffer.
185 doc->setUndoRedoEnabled(true);
186
187 if (!html.isEmpty())
188 control->setHtml(html);
189
190 hbar->setSingleStep(20);
191 vbar->setSingleStep(20);
192
193 viewport->setBackgroundRole(QPalette::Base);
194 q->setMouseTracking(true);
195 q->setAcceptDrops(true);
196 q->setFocusPolicy(Qt::StrongFocus);
197 q->setAttribute(Qt::WA_KeyCompression);
198 q->setAttribute(Qt::WA_InputMethodEnabled);
199 q->setInputMethodHints(Qt::ImhMultiLine);
200#ifndef QT_NO_CURSOR
201 viewport->setCursor(Qt::IBeamCursor);
202#endif
203#if 0 // Used to be included in Qt4 for Q_WS_WIN
204 setSingleFingerPanEnabled(true);
205#endif
206}
207
208void QTextEditPrivate::_q_repaintContents(const QRectF &contentsRect)
209{
210 if (!contentsRect.isValid()) {
211 viewport->update();
212 return;
213 }
214 const int xOffset = horizontalOffset();
215 const int yOffset = verticalOffset();
216 const QRectF visibleRect(xOffset, yOffset, viewport->width(), viewport->height());
217
218 QRect r = contentsRect.intersected(visibleRect).toAlignedRect();
219 if (r.isEmpty())
220 return;
221
222 r.translate(-xOffset, -yOffset);
223 viewport->update(r);
224}
225
226void QTextEditPrivate::_q_cursorPositionChanged()
227{
228 Q_Q(QTextEdit);
229 emit q->cursorPositionChanged();
230#ifndef QT_NO_ACCESSIBILITY
231 QAccessibleTextCursorEvent event(q, q->textCursor().position());
232 QAccessible::updateAccessibility(&event);
233#endif
234}
235
236#if QT_CONFIG(cursor)
237void QTextEditPrivate::_q_hoveredBlockWithMarkerChanged(const QTextBlock &block)
238{
239 Q_Q(QTextEdit);
240 Qt::CursorShape cursor = cursorToRestoreAfterHover;
241 if (block.isValid() && !q->isReadOnly()) {
242 QTextBlockFormat::MarkerType marker = block.blockFormat().marker();
243 if (marker != QTextBlockFormat::NoMarker) {
244 if (viewport->cursor().shape() != Qt::PointingHandCursor)
245 cursorToRestoreAfterHover = viewport->cursor().shape();
246 cursor = Qt::PointingHandCursor;
247 }
248 }
249 viewport->setCursor(cursor);
250}
251#endif
252
253void QTextEditPrivate::pageUpDown(QTextCursor::MoveOperation op, QTextCursor::MoveMode moveMode)
254{
255 QTextCursor cursor = control->textCursor();
256 bool moved = false;
257 qreal lastY = control->cursorRect(cursor).top();
258 qreal distance = 0;
259 // move using movePosition to keep the cursor's x
260 do {
261 qreal y = control->cursorRect(cursor).top();
262 distance += qAbs(y - lastY);
263 lastY = y;
264 moved = cursor.movePosition(op, moveMode);
265 } while (moved && distance < viewport->height());
266
267 if (moved) {
268 if (op == QTextCursor::Up) {
269 cursor.movePosition(QTextCursor::Down, moveMode);
270 vbar->triggerAction(QAbstractSlider::SliderPageStepSub);
271 } else {
272 cursor.movePosition(QTextCursor::Up, moveMode);
273 vbar->triggerAction(QAbstractSlider::SliderPageStepAdd);
274 }
275 }
276 control->setTextCursor(cursor);
277}
278
279#if QT_CONFIG(scrollbar)
280static QSize documentSize(QWidgetTextControl *control)
281{
282 QTextDocument *doc = control->document();
283 QAbstractTextDocumentLayout *layout = doc->documentLayout();
284
285 QSize docSize;
286
287 if (QTextDocumentLayout *tlayout = qobject_cast<QTextDocumentLayout *>(layout)) {
288 docSize = tlayout->dynamicDocumentSize().toSize();
289 int percentageDone = tlayout->layoutStatus();
290 // extrapolate height
291 if (percentageDone > 0)
292 docSize.setHeight(docSize.height() * 100 / percentageDone);
293 } else {
294 docSize = layout->documentSize().toSize();
295 }
296
297 return docSize;
298}
299
300void QTextEditPrivate::_q_adjustScrollbars()
301{
302 if (ignoreAutomaticScrollbarAdjustment)
303 return;
304 ignoreAutomaticScrollbarAdjustment = true; // avoid recursion, #106108
305
306 QSize viewportSize = viewport->size();
307 QSize docSize = documentSize(control);
308
309 // due to the recursion guard we have to repeat this step a few times,
310 // as adding/removing a scroll bar will cause the document or viewport
311 // size to change
312 // ideally we should loop until the viewport size and doc size stabilize,
313 // but in corner cases they might fluctuate, so we need to limit the
314 // number of iterations
315 for (int i = 0; i < 4; ++i) {
316 hbar->setRange(0, docSize.width() - viewportSize.width());
317 hbar->setPageStep(viewportSize.width());
318
319 vbar->setRange(0, docSize.height() - viewportSize.height());
320 vbar->setPageStep(viewportSize.height());
321
322 // if we are in left-to-right mode widening the document due to
323 // lazy layouting does not require a repaint. If in right-to-left
324 // the scroll bar has the value zero and it visually has the maximum
325 // value (it is visually at the right), then widening the document
326 // keeps it at value zero but visually adjusts it to the new maximum
327 // on the right, hence we need an update.
328 if (q_func()->isRightToLeft())
329 viewport->update();
330
331 _q_showOrHideScrollBars();
332
333 const QSize oldViewportSize = viewportSize;
334 const QSize oldDocSize = docSize;
335
336 // make sure the document is layouted if the viewport width changes
337 viewportSize = viewport->size();
338 if (viewportSize.width() != oldViewportSize.width())
339 relayoutDocument();
340
341 docSize = documentSize(control);
342 if (viewportSize == oldViewportSize && docSize == oldDocSize)
343 break;
344 }
345 ignoreAutomaticScrollbarAdjustment = false;
346}
347#endif
348
349// rect is in content coordinates
350void QTextEditPrivate::_q_ensureVisible(const QRectF &_rect)
351{
352 const QRect rect = _rect.toRect();
353 if ((vbar->isVisible() && vbar->maximum() < rect.bottom())
354 || (hbar->isVisible() && hbar->maximum() < rect.right()))
355 _q_adjustScrollbars();
356 const int visibleWidth = viewport->width();
357 const int visibleHeight = viewport->height();
358 const bool rtl = q_func()->isRightToLeft();
359
360 if (rect.x() < horizontalOffset()) {
361 if (rtl)
362 hbar->setValue(hbar->maximum() - rect.x());
363 else
364 hbar->setValue(rect.x());
365 } else if (rect.x() + rect.width() > horizontalOffset() + visibleWidth) {
366 if (rtl)
367 hbar->setValue(hbar->maximum() - (rect.x() + rect.width() - visibleWidth));
368 else
369 hbar->setValue(rect.x() + rect.width() - visibleWidth);
370 }
371
372 if (rect.y() < verticalOffset())
373 vbar->setValue(rect.y());
374 else if (rect.y() + rect.height() > verticalOffset() + visibleHeight)
375 vbar->setValue(rect.y() + rect.height() - visibleHeight);
376}
377
378/*!
379 \class QTextEdit
380 \brief The QTextEdit class provides a widget that is used to edit and display
381 both plain and rich text.
382
383 \ingroup richtext-processing
384 \inmodule QtWidgets
385
386 \tableofcontents
387
388 \section1 Introduction and Concepts
389
390 QTextEdit is an advanced WYSIWYG viewer/editor supporting rich
391 text formatting using HTML-style tags, or Markdown format. It is optimized
392 to handle large documents and to respond quickly to user input.
393
394 QTextEdit works on paragraphs and characters. A paragraph is a
395 formatted string which is word-wrapped to fit into the width of
396 the widget. By default when reading plain text, one newline
397 signifies a paragraph. A document consists of zero or more
398 paragraphs. The words in the paragraph are aligned in accordance
399 with the paragraph's alignment. Paragraphs are separated by hard
400 line breaks. Each character within a paragraph has its own
401 attributes, for example, font and color.
402
403 QTextEdit can display images, lists and tables. If the text is
404 too large to view within the text edit's viewport, scroll bars will
405 appear. The text edit can load both plain text and rich text files.
406 Rich text can be described using a subset of HTML 4 markup; refer to the
407 \l {Supported HTML Subset} page for more information.
408
409 If you just need to display a small piece of rich text use QLabel.
410
411 The rich text support in Qt is designed to provide a fast, portable and
412 efficient way to add reasonable online help facilities to
413 applications, and to provide a basis for rich text editors. If
414 you find the HTML support insufficient for your needs you may consider
415 the use of Qt WebKit, which provides a full-featured web browser
416 widget.
417
418 The shape of the mouse cursor on a QTextEdit is Qt::IBeamCursor by default.
419 It can be changed through the viewport()'s cursor property.
420
421 \section1 Using QTextEdit as a Display Widget
422
423 QTextEdit can display a large HTML subset, including tables and
424 images.
425
426 The text can be set or replaced using \l setHtml() which deletes any
427 existing text and replaces it with the text passed in the
428 setHtml() call. If you call setHtml() with legacy HTML, and then
429 call toHtml(), the text that is returned may have different markup,
430 but will render the same. The entire text can be deleted with clear().
431
432 Text can also be set or replaced using \l setMarkdown(), and the same
433 caveats apply: if you then call \l toMarkdown(), the text that is returned
434 may be different, but the meaning is preserved as much as possible.
435 Markdown with some embedded HTML can be parsed, with the same limitations
436 that \l setHtml() has; but \l toMarkdown() only writes "pure" Markdown,
437 without any embedded HTML.
438
439 Text itself can be inserted using the QTextCursor class or using the
440 convenience functions insertHtml(), insertPlainText(), append() or
441 paste(). QTextCursor is also able to insert complex objects like tables
442 or lists into the document, and it deals with creating selections
443 and applying changes to selected text.
444
445 By default the text edit wraps words at whitespace to fit within
446 the text edit widget. The setLineWrapMode() function is used to
447 specify the kind of line wrap you want, or \l NoWrap if you don't
448 want any wrapping. Call setLineWrapMode() to set a fixed pixel width
449 \l FixedPixelWidth, or character column (e.g. 80 column) \l
450 FixedColumnWidth with the pixels or columns specified with
451 setLineWrapColumnOrWidth(). If you use word wrap to the widget's width
452 \l WidgetWidth, you can specify whether to break on whitespace or
453 anywhere with setWordWrapMode().
454
455 The find() function can be used to find and select a given string
456 within the text.
457
458 If you want to limit the total number of paragraphs in a QTextEdit,
459 as for example it is often useful in a log viewer, then you can use
460 QTextDocument's maximumBlockCount property for that.
461
462 \section2 Read-only Key Bindings
463
464 When QTextEdit is used read-only the key bindings are limited to
465 navigation, and text may only be selected with the mouse:
466 \table
467 \header \li Keypresses \li Action
468 \row \li Up \li Moves one line up.
469 \row \li Down \li Moves one line down.
470 \row \li Left \li Moves one character to the left.
471 \row \li Right \li Moves one character to the right.
472 \row \li PageUp \li Moves one (viewport) page up.
473 \row \li PageDown \li Moves one (viewport) page down.
474 \row \li Home \li Moves to the beginning of the text.
475 \row \li End \li Moves to the end of the text.
476 \row \li Alt+Wheel
477 \li Scrolls the page horizontally (the Wheel is the mouse wheel).
478 \row \li Ctrl+Wheel \li Zooms the text.
479 \row \li Ctrl+A \li Selects all text.
480 \endtable
481
482 The text edit may be able to provide some meta-information. For
483 example, the documentTitle() function will return the text from
484 within HTML \c{<title>} tags.
485
486 \note Zooming into HTML documents only works if the font-size is not set to a fixed size.
487
488 \section1 Using QTextEdit as an Editor
489
490 All the information about using QTextEdit as a display widget also
491 applies here.
492
493 The current char format's attributes are set with setFontItalic(),
494 setFontWeight(), setFontUnderline(), setFontFamily(),
495 setFontPointSize(), setTextColor() and setCurrentFont(). The current
496 paragraph's alignment is set with setAlignment().
497
498 Selection of text is handled by the QTextCursor class, which provides
499 functionality for creating selections, retrieving the text contents or
500 deleting selections. You can retrieve the object that corresponds with
501 the user-visible cursor using the textCursor() method. If you want to set
502 a selection in QTextEdit just create one on a QTextCursor object and
503 then make that cursor the visible cursor using setTextCursor(). The selection
504 can be copied to the clipboard with copy(), or cut to the clipboard with
505 cut(). The entire text can be selected using selectAll().
506
507 When the cursor is moved and the underlying formatting attributes change,
508 the currentCharFormatChanged() signal is emitted to reflect the new attributes
509 at the new cursor position.
510
511 The textChanged() signal is emitted whenever the text changes (as a result
512 of setText() or through the editor itself).
513
514 QTextEdit holds a QTextDocument object which can be retrieved using the
515 document() method. You can also set your own document object using setDocument().
516
517 QTextDocument provides an \l {QTextDocument::isModified()}{isModified()}
518 function which will return true if the text has been modified since it was
519 either loaded or since the last call to setModified with false as argument.
520 In addition it provides methods for undo and redo.
521
522 \section2 Drag and Drop
523
524 QTextEdit also supports custom drag and drop behavior. By default,
525 QTextEdit will insert plain text, HTML and rich text when the user drops
526 data of these MIME types onto a document. Reimplement
527 canInsertFromMimeData() and insertFromMimeData() to add support for
528 additional MIME types.
529
530 For example, to allow the user to drag and drop an image onto a QTextEdit,
531 you could the implement these functions in the following way:
532
533 \snippet textdocument-imagedrop/textedit.cpp 0
534
535 We add support for image MIME types by returning true. For all other
536 MIME types, we use the default implementation.
537
538 \snippet textdocument-imagedrop/textedit.cpp 1
539
540 We unpack the image from the QVariant held by the MIME source and insert
541 it into the document as a resource.
542
543 \section2 Editing Key Bindings
544
545 The list of key bindings which are implemented for editing:
546 \table
547 \header \li Keypresses \li Action
548 \row \li Backspace \li Deletes the character to the left of the cursor.
549 \row \li Delete \li Deletes the character to the right of the cursor.
550 \row \li Ctrl+C \li Copy the selected text to the clipboard.
551 \row \li Ctrl+Insert \li Copy the selected text to the clipboard.
552 \row \li Ctrl+K \li Deletes to the end of the line.
553 \row \li Ctrl+V \li Pastes the clipboard text into text edit.
554 \row \li Shift+Insert \li Pastes the clipboard text into text edit.
555 \row \li Ctrl+X \li Deletes the selected text and copies it to the clipboard.
556 \row \li Shift+Delete \li Deletes the selected text and copies it to the clipboard.
557 \row \li Ctrl+Z \li Undoes the last operation.
558 \row \li Ctrl+Y \li Redoes the last operation.
559 \row \li Left \li Moves the cursor one character to the left.
560 \row \li Ctrl+Left \li Moves the cursor one word to the left.
561 \row \li Right \li Moves the cursor one character to the right.
562 \row \li Ctrl+Right \li Moves the cursor one word to the right.
563 \row \li Up \li Moves the cursor one line up.
564 \row \li Down \li Moves the cursor one line down.
565 \row \li PageUp \li Moves the cursor one page up.
566 \row \li PageDown \li Moves the cursor one page down.
567 \row \li Home \li Moves the cursor to the beginning of the line.
568 \row \li Ctrl+Home \li Moves the cursor to the beginning of the text.
569 \row \li End \li Moves the cursor to the end of the line.
570 \row \li Ctrl+End \li Moves the cursor to the end of the text.
571 \row \li Alt+Wheel \li Scrolls the page horizontally (the Wheel is the mouse wheel).
572 \endtable
573
574 To select (mark) text hold down the Shift key whilst pressing one
575 of the movement keystrokes, for example, \e{Shift+Right}
576 will select the character to the right, and \e{Shift+Ctrl+Right} will select the word to the right, etc.
577
578 \sa QTextDocument, QTextCursor, {Application Example},
579 {Syntax Highlighter Example}, {Rich Text Processing}
580*/
581
582/*!
583 \property QTextEdit::plainText
584 \since 4.3
585
586 This property gets and sets the text editor's contents as plain
587 text. Previous contents are removed and undo/redo history is reset
588 when the property is set. currentCharFormat() is also reset, unless
589 textCursor() is already at the beginning of the document.
590
591 If the text edit has another content type, it will not be replaced
592 by plain text if you call toPlainText(). The only exception to this
593 is the non-break space, \e{nbsp;}, that will be converted into
594 standard space.
595
596 By default, for an editor with no contents, this property contains
597 an empty string.
598
599 \sa html
600*/
601
602/*!
603 \property QTextEdit::undoRedoEnabled
604 \brief whether undo and redo are enabled
605
606 Users are only able to undo or redo actions if this property is
607 true, and if there is an action that can be undone (or redone).
608*/
609
610/*!
611 \enum QTextEdit::LineWrapMode
612
613 \value NoWrap
614 \value WidgetWidth
615 \value FixedPixelWidth
616 \value FixedColumnWidth
617*/
618
619/*!
620 \enum QTextEdit::AutoFormattingFlag
621
622 \value AutoNone Don't do any automatic formatting.
623 \value AutoBulletList Automatically create bullet lists (e.g. when
624 the user enters an asterisk ('*') in the left most column, or
625 presses Enter in an existing list item.
626 \value AutoAll Apply all automatic formatting. Currently only
627 automatic bullet lists are supported.
628*/
629
630
631/*!
632 Constructs an empty QTextEdit with parent \a
633 parent.
634*/
635QTextEdit::QTextEdit(QWidget *parent)
636 : QAbstractScrollArea(*new QTextEditPrivate, parent)
637{
638 Q_D(QTextEdit);
639 d->init();
640}
641
642/*!
643 \internal
644*/
645QTextEdit::QTextEdit(QTextEditPrivate &dd, QWidget *parent)
646 : QAbstractScrollArea(dd, parent)
647{
648 Q_D(QTextEdit);
649 d->init();
650}
651
652/*!
653 Constructs a QTextEdit with parent \a parent. The text edit will display
654 the text \a text. The text is interpreted as html.
655*/
656QTextEdit::QTextEdit(const QString &text, QWidget *parent)
657 : QAbstractScrollArea(*new QTextEditPrivate, parent)
658{
659 Q_D(QTextEdit);
660 d->init(text);
661}
662
663
664
665/*!
666 Destructor.
667*/
668QTextEdit::~QTextEdit()
669{
670}
671
672/*!
673 Returns the point size of the font of the current format.
674
675 \sa setFontFamily(), setCurrentFont(), setFontPointSize()
676*/
677qreal QTextEdit::fontPointSize() const
678{
679 Q_D(const QTextEdit);
680 return d->control->textCursor().charFormat().fontPointSize();
681}
682
683/*!
684 Returns the font family of the current format.
685
686 \sa setFontFamily(), setCurrentFont(), setFontPointSize()
687*/
688QString QTextEdit::fontFamily() const
689{
690 Q_D(const QTextEdit);
691 return d->control->textCursor().charFormat().fontFamily();
692}
693
694/*!
695 Returns the font weight of the current format.
696
697 \sa setFontWeight(), setCurrentFont(), setFontPointSize(), QFont::Weight
698*/
699int QTextEdit::fontWeight() const
700{
701 Q_D(const QTextEdit);
702 return d->control->textCursor().charFormat().fontWeight();
703}
704
705/*!
706 Returns \c true if the font of the current format is underlined; otherwise returns
707 false.
708
709 \sa setFontUnderline()
710*/
711bool QTextEdit::fontUnderline() const
712{
713 Q_D(const QTextEdit);
714 return d->control->textCursor().charFormat().fontUnderline();
715}
716
717/*!
718 Returns \c true if the font of the current format is italic; otherwise returns
719 false.
720
721 \sa setFontItalic()
722*/
723bool QTextEdit::fontItalic() const
724{
725 Q_D(const QTextEdit);
726 return d->control->textCursor().charFormat().fontItalic();
727}
728
729/*!
730 Returns the text color of the current format.
731
732 \sa setTextColor()
733*/
734QColor QTextEdit::textColor() const
735{
736 Q_D(const QTextEdit);
737 return d->control->textCursor().charFormat().foreground().color();
738}
739
740/*!
741 \since 4.4
742
743 Returns the text background color of the current format.
744
745 \sa setTextBackgroundColor()
746*/
747QColor QTextEdit::textBackgroundColor() const
748{
749 Q_D(const QTextEdit);
750 return d->control->textCursor().charFormat().background().color();
751}
752
753/*!
754 Returns the font of the current format.
755
756 \sa setCurrentFont(), setFontFamily(), setFontPointSize()
757*/
758QFont QTextEdit::currentFont() const
759{
760 Q_D(const QTextEdit);
761 return d->control->textCursor().charFormat().font();
762}
763
764/*!
765 Sets the alignment of the current paragraph to \a a. Valid
766 alignments are Qt::AlignLeft, Qt::AlignRight,
767 Qt::AlignJustify and Qt::AlignCenter (which centers
768 horizontally).
769*/
770void QTextEdit::setAlignment(Qt::Alignment a)
771{
772 Q_D(QTextEdit);
773 QTextBlockFormat fmt;
774 fmt.setAlignment(a);
775 QTextCursor cursor = d->control->textCursor();
776 cursor.mergeBlockFormat(fmt);
777 d->control->setTextCursor(cursor);
778}
779
780/*!
781 Returns the alignment of the current paragraph.
782
783 \sa setAlignment()
784*/
785Qt::Alignment QTextEdit::alignment() const
786{
787 Q_D(const QTextEdit);
788 return d->control->textCursor().blockFormat().alignment();
789}
790
791/*!
792 \property QTextEdit::document
793 \brief the underlying document of the text editor.
794
795 \note The editor \e{does not take ownership of the document} unless it
796 is the document's parent object. The parent object of the provided document
797 remains the owner of the object. If the previously assigned document is a
798 child of the editor then it will be deleted.
799*/
800void QTextEdit::setDocument(QTextDocument *document)
801{
802 Q_D(QTextEdit);
803 d->control->setDocument(document);
804 d->updateDefaultTextOption();
805 d->relayoutDocument();
806}
807
808QTextDocument *QTextEdit::document() const
809{
810 Q_D(const QTextEdit);
811 return d->control->document();
812}
813
814/*!
815 \since 5.2
816
817 \property QTextEdit::placeholderText
818 \brief the editor placeholder text
819
820 Setting this property makes the editor display a grayed-out
821 placeholder text as long as the document() is empty.
822
823 By default, this property contains an empty string.
824
825 \sa document()
826*/
827QString QTextEdit::placeholderText() const
828{
829 Q_D(const QTextEdit);
830 return d->placeholderText;
831}
832
833void QTextEdit::setPlaceholderText(const QString &placeholderText)
834{
835 Q_D(QTextEdit);
836 if (d->placeholderText != placeholderText) {
837 d->placeholderText = placeholderText;
838 if (d->control->document()->isEmpty())
839 d->viewport->update();
840 }
841}
842
843/*!
844 Sets the visible \a cursor.
845*/
846void QTextEdit::setTextCursor(const QTextCursor &cursor)
847{
848 doSetTextCursor(cursor);
849}
850
851/*!
852 \internal
853
854 This provides a hook for subclasses to intercept cursor changes.
855*/
856
857void QTextEdit::doSetTextCursor(const QTextCursor &cursor)
858{
859 Q_D(QTextEdit);
860 d->control->setTextCursor(cursor);
861}
862
863/*!
864 Returns a copy of the QTextCursor that represents the currently visible cursor.
865 Note that changes on the returned cursor do not affect QTextEdit's cursor; use
866 setTextCursor() to update the visible cursor.
867 */
868QTextCursor QTextEdit::textCursor() const
869{
870 Q_D(const QTextEdit);
871 return d->control->textCursor();
872}
873
874/*!
875 Sets the font family of the current format to \a fontFamily.
876
877 \sa fontFamily(), setCurrentFont()
878*/
879void QTextEdit::setFontFamily(const QString &fontFamily)
880{
881 QTextCharFormat fmt;
882 fmt.setFontFamily(fontFamily);
883 mergeCurrentCharFormat(fmt);
884}
885
886/*!
887 Sets the point size of the current format to \a s.
888
889 Note that if \a s is zero or negative, the behavior of this
890 function is not defined.
891
892 \sa fontPointSize(), setCurrentFont(), setFontFamily()
893*/
894void QTextEdit::setFontPointSize(qreal s)
895{
896 QTextCharFormat fmt;
897 fmt.setFontPointSize(s);
898 mergeCurrentCharFormat(fmt);
899}
900
901/*!
902 \fn void QTextEdit::setFontWeight(int weight)
903
904 Sets the font weight of the current format to the given \a weight,
905 where the value used is in the range defined by the QFont::Weight
906 enum.
907
908 \sa fontWeight(), setCurrentFont(), setFontFamily()
909*/
910void QTextEdit::setFontWeight(int w)
911{
912 QTextCharFormat fmt;
913 fmt.setFontWeight(w);
914 mergeCurrentCharFormat(fmt);
915}
916
917/*!
918 If \a underline is true, sets the current format to underline;
919 otherwise sets the current format to non-underline.
920
921 \sa fontUnderline()
922*/
923void QTextEdit::setFontUnderline(bool underline)
924{
925 QTextCharFormat fmt;
926 fmt.setFontUnderline(underline);
927 mergeCurrentCharFormat(fmt);
928}
929
930/*!
931 If \a italic is true, sets the current format to italic;
932 otherwise sets the current format to non-italic.
933
934 \sa fontItalic()
935*/
936void QTextEdit::setFontItalic(bool italic)
937{
938 QTextCharFormat fmt;
939 fmt.setFontItalic(italic);
940 mergeCurrentCharFormat(fmt);
941}
942
943/*!
944 Sets the text color of the current format to \a c.
945
946 \sa textColor()
947*/
948void QTextEdit::setTextColor(const QColor &c)
949{
950 QTextCharFormat fmt;
951 fmt.setForeground(QBrush(c));
952 mergeCurrentCharFormat(fmt);
953}
954
955/*!
956 \since 4.4
957
958 Sets the text background color of the current format to \a c.
959
960 \sa textBackgroundColor()
961*/
962void QTextEdit::setTextBackgroundColor(const QColor &c)
963{
964 QTextCharFormat fmt;
965 fmt.setBackground(QBrush(c));
966 mergeCurrentCharFormat(fmt);
967}
968
969/*!
970 Sets the font of the current format to \a f.
971
972 \sa currentFont(), setFontPointSize(), setFontFamily()
973*/
974void QTextEdit::setCurrentFont(const QFont &f)
975{
976 QTextCharFormat fmt;
977 fmt.setFont(f);
978 mergeCurrentCharFormat(fmt);
979}
980
981/*!
982 \since 4.2
983
984 Undoes the last operation.
985
986 If there is no operation to undo, i.e. there is no undo step in
987 the undo/redo history, nothing happens.
988
989 \sa redo()
990*/
991void QTextEdit::undo()
992{
993 Q_D(QTextEdit);
994 d->control->undo();
995}
996
997void QTextEdit::redo()
998{
999 Q_D(QTextEdit);
1000 d->control->redo();
1001}
1002
1003/*!
1004 \fn void QTextEdit::redo()
1005 \since 4.2
1006
1007 Redoes the last operation.
1008
1009 If there is no operation to redo, i.e. there is no redo step in
1010 the undo/redo history, nothing happens.
1011
1012 \sa undo()
1013*/
1014
1015#ifndef QT_NO_CLIPBOARD
1016/*!
1017 Copies the selected text to the clipboard and deletes it from
1018 the text edit.
1019
1020 If there is no selected text nothing happens.
1021
1022 \sa copy(), paste()
1023*/
1024
1025void QTextEdit::cut()
1026{
1027 Q_D(QTextEdit);
1028 d->control->cut();
1029}
1030
1031/*!
1032 Copies any selected text to the clipboard.
1033
1034 \sa copyAvailable()
1035*/
1036
1037void QTextEdit::copy()
1038{
1039 Q_D(QTextEdit);
1040 d->control->copy();
1041}
1042
1043/*!
1044 Pastes the text from the clipboard into the text edit at the
1045 current cursor position.
1046
1047 If there is no text in the clipboard nothing happens.
1048
1049 To change the behavior of this function, i.e. to modify what
1050 QTextEdit can paste and how it is being pasted, reimplement the
1051 virtual canInsertFromMimeData() and insertFromMimeData()
1052 functions.
1053
1054 \sa cut(), copy()
1055*/
1056
1057void QTextEdit::paste()
1058{
1059 Q_D(QTextEdit);
1060 d->control->paste();
1061}
1062#endif
1063
1064/*!
1065 Deletes all the text in the text edit.
1066
1067 Notes:
1068 \list
1069 \li The undo/redo history is also cleared.
1070 \li currentCharFormat() is reset, unless textCursor()
1071 is already at the beginning of the document.
1072 \endlist
1073
1074 \sa cut(), setPlainText(), setHtml()
1075*/
1076void QTextEdit::clear()
1077{
1078 Q_D(QTextEdit);
1079 // clears and sets empty content
1080 d->control->clear();
1081}
1082
1083
1084/*!
1085 Selects all text.
1086
1087 \sa copy(), cut(), textCursor()
1088 */
1089void QTextEdit::selectAll()
1090{
1091 Q_D(QTextEdit);
1092 d->control->selectAll();
1093}
1094
1095/*! \internal
1096*/
1097bool QTextEdit::event(QEvent *e)
1098{
1099 Q_D(QTextEdit);
1100#ifndef QT_NO_CONTEXTMENU
1101 if (e->type() == QEvent::ContextMenu
1102 && static_cast<QContextMenuEvent *>(e)->reason() == QContextMenuEvent::Keyboard) {
1103 Q_D(QTextEdit);
1104 ensureCursorVisible();
1105 const QPoint cursorPos = cursorRect().center();
1106 QContextMenuEvent ce(QContextMenuEvent::Keyboard, cursorPos, d->viewport->mapToGlobal(cursorPos));
1107 ce.setAccepted(e->isAccepted());
1108 const bool result = QAbstractScrollArea::event(&ce);
1109 e->setAccepted(ce.isAccepted());
1110 return result;
1111 } else if (e->type() == QEvent::ShortcutOverride
1112 || e->type() == QEvent::ToolTip) {
1113 d->sendControlEvent(e);
1114 }
1115#else
1116 Q_UNUSED(d)
1117#endif // QT_NO_CONTEXTMENU
1118#ifdef QT_KEYPAD_NAVIGATION
1119 if (e->type() == QEvent::EnterEditFocus || e->type() == QEvent::LeaveEditFocus) {
1120 if (QApplicationPrivate::keypadNavigationEnabled())
1121 d->sendControlEvent(e);
1122 }
1123#endif
1124 return QAbstractScrollArea::event(e);
1125}
1126
1127/*! \internal
1128*/
1129
1130void QTextEdit::timerEvent(QTimerEvent *e)
1131{
1132 Q_D(QTextEdit);
1133 if (e->timerId() == d->autoScrollTimer.timerId()) {
1134 QRect visible = d->viewport->rect();
1135 QPoint pos;
1136 if (d->inDrag) {
1137 pos = d->autoScrollDragPos;
1138 visible.adjust(qMin(visible.width()/3,20), qMin(visible.height()/3,20),
1139 -qMin(visible.width()/3,20), -qMin(visible.height()/3,20));
1140 } else {
1141 const QPoint globalPos = QCursor::pos();
1142 pos = d->viewport->mapFromGlobal(globalPos);
1143 QMouseEvent ev(QEvent::MouseMove, pos, mapTo(topLevelWidget(), pos), globalPos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
1144 mouseMoveEvent(&ev);
1145 }
1146 int deltaY = qMax(pos.y() - visible.top(), visible.bottom() - pos.y()) - visible.height();
1147 int deltaX = qMax(pos.x() - visible.left(), visible.right() - pos.x()) - visible.width();
1148 int delta = qMax(deltaX, deltaY);
1149 if (delta >= 0) {
1150 if (delta < 7)
1151 delta = 7;
1152 int timeout = 4900 / (delta * delta);
1153 d->autoScrollTimer.start(timeout, this);
1154
1155 if (deltaY > 0)
1156 d->vbar->triggerAction(pos.y() < visible.center().y() ?
1157 QAbstractSlider::SliderSingleStepSub
1158 : QAbstractSlider::SliderSingleStepAdd);
1159 if (deltaX > 0)
1160 d->hbar->triggerAction(pos.x() < visible.center().x() ?
1161 QAbstractSlider::SliderSingleStepSub
1162 : QAbstractSlider::SliderSingleStepAdd);
1163 }
1164 }
1165#ifdef QT_KEYPAD_NAVIGATION
1166 else if (e->timerId() == d->deleteAllTimer.timerId()) {
1167 d->deleteAllTimer.stop();
1168 clear();
1169 }
1170#endif
1171}
1172
1173/*!
1174 Changes the text of the text edit to the string \a text.
1175 Any previous text is removed.
1176
1177 Notes:
1178 \list
1179 \li \a text is interpreted as plain text.
1180 \li The undo/redo history is also cleared.
1181 \li currentCharFormat() is reset, unless textCursor()
1182 is already at the beginning of the document.
1183 \endlist
1184
1185 \sa toPlainText()
1186*/
1187
1188void QTextEdit::setPlainText(const QString &text)
1189{
1190 Q_D(QTextEdit);
1191 d->control->setPlainText(text);
1192 d->preferRichText = false;
1193}
1194
1195/*!
1196 QString QTextEdit::toPlainText() const
1197
1198 Returns the text of the text edit as plain text.
1199
1200 \sa QTextEdit::setPlainText()
1201 */
1202QString QTextEdit::toPlainText() const
1203{
1204 Q_D(const QTextEdit);
1205 return d->control->toPlainText();
1206}
1207
1208/*!
1209 \property QTextEdit::html
1210
1211 This property provides an HTML interface to the text of the text edit.
1212
1213 toHtml() returns the text of the text edit as html.
1214
1215 setHtml() changes the text of the text edit. Any previous text is
1216 removed and the undo/redo history is cleared. The input text is
1217 interpreted as rich text in html format. currentCharFormat() is also
1218 reset, unless textCursor() is already at the beginning of the document.
1219
1220 \note It is the responsibility of the caller to make sure that the
1221 text is correctly decoded when a QString containing HTML is created
1222 and passed to setHtml().
1223
1224 By default, for a newly-created, empty document, this property contains
1225 text to describe an HTML 4.0 document with no body text.
1226
1227 \sa {Supported HTML Subset}, plainText
1228*/
1229
1230#ifndef QT_NO_TEXTHTMLPARSER
1231void QTextEdit::setHtml(const QString &text)
1232{
1233 Q_D(QTextEdit);
1234 d->control->setHtml(text);
1235 d->preferRichText = true;
1236}
1237
1238QString QTextEdit::toHtml() const
1239{
1240 Q_D(const QTextEdit);
1241 return d->control->toHtml();
1242}
1243#endif
1244
1245#if QT_CONFIG(textmarkdownreader) && QT_CONFIG(textmarkdownwriter)
1246/*!
1247 \property QTextEdit::markdown
1248
1249 This property provides a Markdown interface to the text of the text edit.
1250
1251 \c toMarkdown() returns the text of the text edit as "pure" Markdown,
1252 without any embedded HTML formatting. Some features that QTextDocument
1253 supports (such as the use of specific colors and named fonts) cannot be
1254 expressed in "pure" Markdown, and they will be omitted.
1255
1256 \c setMarkdown() changes the text of the text edit. Any previous text is
1257 removed and the undo/redo history is cleared. The input text is
1258 interpreted as rich text in Markdown format.
1259
1260 Parsing of HTML included in the \a markdown string is handled in the same
1261 way as in \l setHtml; however, Markdown formatting inside HTML blocks is
1262 not supported.
1263
1264 Some features of the parser can be enabled or disabled via the \a features
1265 argument:
1266
1267 \value MarkdownNoHTML
1268 Any HTML tags in the Markdown text will be discarded
1269 \value MarkdownDialectCommonMark
1270 The parser supports only the features standardized by CommonMark
1271 \value MarkdownDialectGitHub
1272 The parser supports the GitHub dialect
1273
1274 The default is \c MarkdownDialectGitHub.
1275
1276 \sa plainText, html, QTextDocument::toMarkdown(), QTextDocument::setMarkdown()
1277*/
1278#endif
1279
1280#if QT_CONFIG(textmarkdownreader)
1281void QTextEdit::setMarkdown(const QString &markdown)
1282{
1283 Q_D(const QTextEdit);
1284 d->control->setMarkdown(markdown);
1285}
1286#endif
1287
1288#if QT_CONFIG(textmarkdownwriter)
1289QString QTextEdit::toMarkdown(QTextDocument::MarkdownFeatures features) const
1290{
1291 Q_D(const QTextEdit);
1292 return d->control->toMarkdown(features);
1293}
1294#endif
1295
1296/*! \reimp
1297*/
1298void QTextEdit::keyPressEvent(QKeyEvent *e)
1299{
1300 Q_D(QTextEdit);
1301
1302#ifdef QT_KEYPAD_NAVIGATION
1303 switch (e->key()) {
1304 case Qt::Key_Select:
1305 if (QApplicationPrivate::keypadNavigationEnabled()) {
1306 // code assumes linksaccessible + editable isn't meaningful
1307 if (d->control->textInteractionFlags() & Qt::TextEditable) {
1308 setEditFocus(!hasEditFocus());
1309 } else {
1310 if (!hasEditFocus())
1311 setEditFocus(true);
1312 else {
1313 QTextCursor cursor = d->control->textCursor();
1314 QTextCharFormat charFmt = cursor.charFormat();
1315 if (!(d->control->textInteractionFlags() & Qt::LinksAccessibleByKeyboard)
1316 || !cursor.hasSelection() || charFmt.anchorHref().isEmpty()) {
1317 e->accept();
1318 return;
1319 }
1320 }
1321 }
1322 }
1323 break;
1324 case Qt::Key_Back:
1325 case Qt::Key_No:
1326 if (!QApplicationPrivate::keypadNavigationEnabled()
1327 || (QApplicationPrivate::keypadNavigationEnabled() && !hasEditFocus())) {
1328 e->ignore();
1329 return;
1330 }
1331 break;
1332 default:
1333 if (QApplicationPrivate::keypadNavigationEnabled()) {
1334 if (!hasEditFocus() && !(e->modifiers() & Qt::ControlModifier)) {
1335 if (e->text()[0].isPrint())
1336 setEditFocus(true);
1337 else {
1338 e->ignore();
1339 return;
1340 }
1341 }
1342 }
1343 break;
1344 }
1345#endif
1346#ifndef QT_NO_SHORTCUT
1347
1348 Qt::TextInteractionFlags tif = d->control->textInteractionFlags();
1349
1350 if (tif & Qt::TextSelectableByKeyboard){
1351 if (e == QKeySequence::SelectPreviousPage) {
1352 e->accept();
1353 d->pageUpDown(QTextCursor::Up, QTextCursor::KeepAnchor);
1354 return;
1355 } else if (e ==QKeySequence::SelectNextPage) {
1356 e->accept();
1357 d->pageUpDown(QTextCursor::Down, QTextCursor::KeepAnchor);
1358 return;
1359 }
1360 }
1361 if (tif & (Qt::TextSelectableByKeyboard | Qt::TextEditable)) {
1362 if (e == QKeySequence::MoveToPreviousPage) {
1363 e->accept();
1364 d->pageUpDown(QTextCursor::Up, QTextCursor::MoveAnchor);
1365 return;
1366 } else if (e == QKeySequence::MoveToNextPage) {
1367 e->accept();
1368 d->pageUpDown(QTextCursor::Down, QTextCursor::MoveAnchor);
1369 return;
1370 }
1371 }
1372
1373 if (!(tif & Qt::TextEditable)) {
1374 switch (e->key()) {
1375 case Qt::Key_Space:
1376 e->accept();
1377 if (e->modifiers() & Qt::ShiftModifier)
1378 d->vbar->triggerAction(QAbstractSlider::SliderPageStepSub);
1379 else
1380 d->vbar->triggerAction(QAbstractSlider::SliderPageStepAdd);
1381 break;
1382 default:
1383 d->sendControlEvent(e);
1384 if (!e->isAccepted() && e->modifiers() == Qt::NoModifier) {
1385 if (e->key() == Qt::Key_Home) {
1386 d->vbar->triggerAction(QAbstractSlider::SliderToMinimum);
1387 e->accept();
1388 } else if (e->key() == Qt::Key_End) {
1389 d->vbar->triggerAction(QAbstractSlider::SliderToMaximum);
1390 e->accept();
1391 }
1392 }
1393 if (!e->isAccepted()) {
1394 QAbstractScrollArea::keyPressEvent(e);
1395 }
1396 }
1397 return;
1398 }
1399#endif // QT_NO_SHORTCUT
1400
1401 {
1402 QTextCursor cursor = d->control->textCursor();
1403 const QString text = e->text();
1404 if (cursor.atBlockStart()
1405 && (d->autoFormatting & AutoBulletList)
1406 && (text.length() == 1)
1407 && (text.at(0) == QLatin1Char('-') || text.at(0) == QLatin1Char('*'))
1408 && (!cursor.currentList())) {
1409
1410 d->createAutoBulletList();
1411 e->accept();
1412 return;
1413 }
1414 }
1415
1416 d->sendControlEvent(e);
1417#ifdef QT_KEYPAD_NAVIGATION
1418 if (!e->isAccepted()) {
1419 switch (e->key()) {
1420 case Qt::Key_Up:
1421 case Qt::Key_Down:
1422 if (QApplicationPrivate::keypadNavigationEnabled()) {
1423 // Cursor position didn't change, so we want to leave
1424 // these keys to change focus.
1425 e->ignore();
1426 return;
1427 }
1428 break;
1429 case Qt::Key_Back:
1430 if (!e->isAutoRepeat()) {
1431 if (QApplicationPrivate::keypadNavigationEnabled()) {
1432 if (document()->isEmpty() || !(d->control->textInteractionFlags() & Qt::TextEditable)) {
1433 setEditFocus(false);
1434 e->accept();
1435 } else if (!d->deleteAllTimer.isActive()) {
1436 e->accept();
1437 d->deleteAllTimer.start(750, this);
1438 }
1439 } else {
1440 e->ignore();
1441 return;
1442 }
1443 }
1444 break;
1445 default: break;
1446 }
1447 }
1448#endif
1449}
1450
1451/*! \reimp
1452*/
1453void QTextEdit::keyReleaseEvent(QKeyEvent *e)
1454{
1455#ifdef QT_KEYPAD_NAVIGATION
1456 Q_D(QTextEdit);
1457 if (QApplicationPrivate::keypadNavigationEnabled()) {
1458 if (!e->isAutoRepeat() && e->key() == Qt::Key_Back
1459 && d->deleteAllTimer.isActive()) {
1460 d->deleteAllTimer.stop();
1461 QTextCursor cursor = d->control->textCursor();
1462 QTextBlockFormat blockFmt = cursor.blockFormat();
1463
1464 QTextList *list = cursor.currentList();
1465 if (list && cursor.atBlockStart()) {
1466 list->remove(cursor.block());
1467 } else if (cursor.atBlockStart() && blockFmt.indent() > 0) {
1468 blockFmt.setIndent(blockFmt.indent() - 1);
1469 cursor.setBlockFormat(blockFmt);
1470 } else {
1471 cursor.deletePreviousChar();
1472 }
1473 setTextCursor(cursor);
1474 e->accept();
1475 return;
1476 }
1477 }
1478#endif
1479 e->ignore();
1480}
1481
1482/*!
1483 Loads the resource specified by the given \a type and \a name.
1484
1485 This function is an extension of QTextDocument::loadResource().
1486
1487 \sa QTextDocument::loadResource()
1488*/
1489QVariant QTextEdit::loadResource(int type, const QUrl &name)
1490{
1491 Q_UNUSED(type);
1492 Q_UNUSED(name);
1493 return QVariant();
1494}
1495
1496/*! \reimp
1497*/
1498void QTextEdit::resizeEvent(QResizeEvent *e)
1499{
1500 Q_D(QTextEdit);
1501
1502 if (d->lineWrap == NoWrap) {
1503 QTextDocument *doc = d->control->document();
1504 QVariant alignmentProperty = doc->documentLayout()->property("contentHasAlignment");
1505
1506 if (!doc->pageSize().isNull()
1507 && alignmentProperty.type() == QVariant::Bool
1508 && !alignmentProperty.toBool()) {
1509
1510 d->_q_adjustScrollbars();
1511 return;
1512 }
1513 }
1514
1515 if (d->lineWrap != FixedPixelWidth
1516 && e->oldSize().width() != e->size().width())
1517 d->relayoutDocument();
1518 else
1519 d->_q_adjustScrollbars();
1520}
1521
1522void QTextEditPrivate::relayoutDocument()
1523{
1524 QTextDocument *doc = control->document();
1525 QAbstractTextDocumentLayout *layout = doc->documentLayout();
1526
1527 if (QTextDocumentLayout *tlayout = qobject_cast<QTextDocumentLayout *>(layout)) {
1528 if (lineWrap == QTextEdit::FixedColumnWidth)
1529 tlayout->setFixedColumnWidth(lineWrapColumnOrWidth);
1530 else
1531 tlayout->setFixedColumnWidth(-1);
1532 }
1533
1534 QTextDocumentLayout *tlayout = qobject_cast<QTextDocumentLayout *>(layout);
1535 QSize lastUsedSize;
1536 if (tlayout)
1537 lastUsedSize = tlayout->dynamicDocumentSize().toSize();
1538 else
1539 lastUsedSize = layout->documentSize().toSize();
1540
1541 // ignore calls to _q_adjustScrollbars caused by an emission of the
1542 // usedSizeChanged() signal in the layout, as we're calling it
1543 // later on our own anyway (or deliberately not) .
1544 const bool oldIgnoreScrollbarAdjustment = ignoreAutomaticScrollbarAdjustment;
1545 ignoreAutomaticScrollbarAdjustment = true;
1546
1547 int width = viewport->width();
1548 if (lineWrap == QTextEdit::FixedPixelWidth)
1549 width = lineWrapColumnOrWidth;
1550 else if (lineWrap == QTextEdit::NoWrap) {
1551 QVariant alignmentProperty = doc->documentLayout()->property("contentHasAlignment");
1552 if (alignmentProperty.type() == QVariant::Bool && !alignmentProperty.toBool()) {
1553
1554 width = 0;
1555 }
1556 }
1557
1558 doc->setPageSize(QSize(width, -1));
1559 if (tlayout)
1560 tlayout->ensureLayouted(verticalOffset() + viewport->height());
1561
1562 ignoreAutomaticScrollbarAdjustment = oldIgnoreScrollbarAdjustment;
1563
1564 QSize usedSize;
1565 if (tlayout)
1566 usedSize = tlayout->dynamicDocumentSize().toSize();
1567 else
1568 usedSize = layout->documentSize().toSize();
1569
1570 // this is an obscure situation in the layout that can happen:
1571 // if a character at the end of a line is the tallest one and therefore
1572 // influencing the total height of the line and the line right below it
1573 // is always taller though, then it can happen that if due to line breaking
1574 // that tall character wraps into the lower line the document not only shrinks
1575 // horizontally (causing the character to wrap in the first place) but also
1576 // vertically, because the original line is now smaller and the one below kept
1577 // its size. So a layout with less width _can_ take up less vertical space, too.
1578 // If the wider case causes a vertical scroll bar to appear and the narrower one
1579 // (narrower because the vertical scroll bar takes up horizontal space)) to disappear
1580 // again then we have an endless loop, as _q_adjustScrollBars sets new ranges on the
1581 // scroll bars, the QAbstractScrollArea will find out about it and try to show/hide
1582 // the scroll bars again. That's why we try to detect this case here and break out.
1583 //
1584 // (if you change this please also check the layoutingLoop() testcase in
1585 // QTextEdit's autotests)
1586 if (lastUsedSize.isValid()
1587 && !vbar->isHidden()
1588 && viewport->width() < lastUsedSize.width()
1589 && usedSize.height() < lastUsedSize.height()
1590 && usedSize.height() <= viewport->height())
1591 return;
1592
1593 _q_adjustScrollbars();
1594}
1595
1596void QTextEditPrivate::paint(QPainter *p, QPaintEvent *e)
1597{
1598 const int xOffset = horizontalOffset();
1599 const int yOffset = verticalOffset();
1600
1601 QRect r = e->rect();
1602 p->translate(-xOffset, -yOffset);
1603 r.translate(xOffset, yOffset);
1604
1605 QTextDocument *doc = control->document();
1606 QTextDocumentLayout *layout = qobject_cast<QTextDocumentLayout *>(doc->documentLayout());
1607
1608 // the layout might need to expand the root frame to
1609 // the viewport if NoWrap is set
1610 if (layout)
1611 layout->setViewport(viewport->rect());
1612
1613 control->drawContents(p, r, q_func());
1614
1615 if (layout)
1616 layout->setViewport(QRect());
1617
1618 if (!placeholderText.isEmpty() && doc->isEmpty() && !control->isPreediting()) {
1619 const QColor col = control->palette().placeholderText().color();
1620 p->setPen(col);
1621 const int margin = int(doc->documentMargin());
1622 p->drawText(viewport->rect().adjusted(margin, margin, -margin, -margin), Qt::AlignTop | Qt::TextWordWrap, placeholderText);
1623 }
1624}
1625
1626/*! \fn void QTextEdit::paintEvent(QPaintEvent *event)
1627
1628This event handler can be reimplemented in a subclass to receive paint events passed in \a event.
1629It is usually unnecessary to reimplement this function in a subclass of QTextEdit.
1630
1631\warning The underlying text document must not be modified from within a reimplementation
1632of this function.
1633*/
1634void QTextEdit::paintEvent(QPaintEvent *e)
1635{
1636 Q_D(QTextEdit);
1637 QPainter p(d->viewport);
1638 d->paint(&p, e);
1639}
1640
1641void QTextEditPrivate::_q_currentCharFormatChanged(const QTextCharFormat &fmt)
1642{
1643 Q_Q(QTextEdit);
1644 emit q->currentCharFormatChanged(fmt);
1645}
1646
1647void QTextEditPrivate::updateDefaultTextOption()
1648{
1649 QTextDocument *doc = control->document();
1650
1651 QTextOption opt = doc->defaultTextOption();
1652 QTextOption::WrapMode oldWrapMode = opt.wrapMode();
1653
1654 if (lineWrap == QTextEdit::NoWrap)
1655 opt.setWrapMode(QTextOption::NoWrap);
1656 else
1657 opt.setWrapMode(wordWrap);
1658
1659 if (opt.wrapMode() != oldWrapMode)
1660 doc->setDefaultTextOption(opt);
1661}
1662
1663/*! \reimp
1664*/
1665void QTextEdit::mousePressEvent(QMouseEvent *e)
1666{
1667 Q_D(QTextEdit);
1668#ifdef QT_KEYPAD_NAVIGATION
1669 if (QApplicationPrivate::keypadNavigationEnabled() && !hasEditFocus())
1670 setEditFocus(true);
1671#endif
1672 d->sendControlEvent(e);
1673}
1674
1675/*! \reimp
1676*/
1677void QTextEdit::mouseMoveEvent(QMouseEvent *e)
1678{
1679 Q_D(QTextEdit);
1680 d->inDrag = false; // paranoia
1681 const QPoint pos = e->pos();
1682 d->sendControlEvent(e);
1683 if (!(e->buttons() & Qt::LeftButton))
1684 return;
1685 if (e->source() == Qt::MouseEventNotSynthesized) {
1686 const QRect visible = d->viewport->rect();
1687 if (visible.contains(pos))
1688 d->autoScrollTimer.stop();
1689 else if (!d->autoScrollTimer.isActive())
1690 d->autoScrollTimer.start(100, this);
1691 }
1692}
1693
1694/*! \reimp
1695*/
1696void QTextEdit::mouseReleaseEvent(QMouseEvent *e)
1697{
1698 Q_D(QTextEdit);
1699 d->sendControlEvent(e);
1700 if (e->source() == Qt::MouseEventNotSynthesized && d->autoScrollTimer.isActive()) {
1701 d->autoScrollTimer.stop();
1702 ensureCursorVisible();
1703 }
1704 if (!isReadOnly() && rect().contains(e->pos()))
1705 d->handleSoftwareInputPanel(e->button(), d->clickCausedFocus);
1706 d->clickCausedFocus = 0;
1707}
1708
1709/*! \reimp
1710*/
1711void QTextEdit::mouseDoubleClickEvent(QMouseEvent *e)
1712{
1713 Q_D(QTextEdit);
1714 d->sendControlEvent(e);
1715}
1716
1717/*! \reimp
1718*/
1719bool QTextEdit::focusNextPrevChild(bool next)
1720{
1721 Q_D(const QTextEdit);
1722 if (!d->tabChangesFocus && d->control->textInteractionFlags() & Qt::TextEditable)
1723 return false;
1724 return QAbstractScrollArea::focusNextPrevChild(next);
1725}
1726
1727#ifndef QT_NO_CONTEXTMENU
1728/*!
1729 \fn void QTextEdit::contextMenuEvent(QContextMenuEvent *event)
1730
1731 Shows the standard context menu created with createStandardContextMenu().
1732
1733 If you do not want the text edit to have a context menu, you can set
1734 its \l contextMenuPolicy to Qt::NoContextMenu. If you want to
1735 customize the context menu, reimplement this function. If you want
1736 to extend the standard context menu, reimplement this function, call
1737 createStandardContextMenu() and extend the menu returned.
1738
1739 Information about the event is passed in the \a event object.
1740
1741 \snippet code/src_gui_widgets_qtextedit.cpp 0
1742*/
1743void QTextEdit::contextMenuEvent(QContextMenuEvent *e)
1744{
1745 Q_D(QTextEdit);
1746 d->sendControlEvent(e);
1747}
1748#endif // QT_NO_CONTEXTMENU
1749
1750#if QT_CONFIG(draganddrop)
1751/*! \reimp
1752*/
1753void QTextEdit::dragEnterEvent(QDragEnterEvent *e)
1754{
1755 Q_D(QTextEdit);
1756 d->inDrag = true;
1757 d->sendControlEvent(e);
1758}
1759
1760/*! \reimp
1761*/
1762void QTextEdit::dragLeaveEvent(QDragLeaveEvent *e)
1763{
1764 Q_D(QTextEdit);
1765 d->inDrag = false;
1766 d->autoScrollTimer.stop();
1767 d->sendControlEvent(e);
1768}
1769
1770/*! \reimp
1771*/
1772void QTextEdit::dragMoveEvent(QDragMoveEvent *e)
1773{
1774 Q_D(QTextEdit);
1775 d->autoScrollDragPos = e->pos();
1776 if (!d->autoScrollTimer.isActive())
1777 d->autoScrollTimer.start(100, this);
1778 d->sendControlEvent(e);
1779}
1780
1781/*! \reimp
1782*/
1783void QTextEdit::dropEvent(QDropEvent *e)
1784{
1785 Q_D(QTextEdit);
1786 d->inDrag = false;
1787 d->autoScrollTimer.stop();
1788 d->sendControlEvent(e);
1789}
1790
1791#endif // QT_CONFIG(draganddrop)
1792
1793/*! \reimp
1794 */
1795void QTextEdit::inputMethodEvent(QInputMethodEvent *e)
1796{
1797 Q_D(QTextEdit);
1798#ifdef QT_KEYPAD_NAVIGATION
1799 if (d->control->textInteractionFlags() & Qt::TextEditable
1800 && QApplicationPrivate::keypadNavigationEnabled()
1801 && !hasEditFocus())
1802 setEditFocus(true);
1803#endif
1804 d->sendControlEvent(e);
1805 ensureCursorVisible();
1806}
1807
1808/*!\reimp
1809*/
1810void QTextEdit::scrollContentsBy(int dx, int dy)
1811{
1812 Q_D(QTextEdit);
1813 if (isRightToLeft())
1814 dx = -dx;
1815 d->viewport->scroll(dx, dy);
1816 QGuiApplication::inputMethod()->update(Qt::ImCursorRectangle | Qt::ImAnchorRectangle);
1817}
1818
1819/*!\reimp
1820*/
1821QVariant QTextEdit::inputMethodQuery(Qt::InputMethodQuery property) const
1822{
1823 return inputMethodQuery(property, QVariant());
1824}
1825
1826/*!\internal
1827 */
1828QVariant QTextEdit::inputMethodQuery(Qt::InputMethodQuery query, QVariant argument) const
1829{
1830 Q_D(const QTextEdit);
1831 switch (query) {
1832 case Qt::ImHints:
1833 case Qt::ImInputItemClipRectangle:
1834 return QWidget::inputMethodQuery(query);
1835 default:
1836 break;
1837 }
1838
1839 const QPointF offset(-d->horizontalOffset(), -d->verticalOffset());
1840 switch (argument.type()) {
1841 case QVariant::RectF:
1842 argument = argument.toRectF().translated(-offset);
1843 break;
1844 case QVariant::PointF:
1845 argument = argument.toPointF() - offset;
1846 break;
1847 case QVariant::Rect:
1848 argument = argument.toRect().translated(-offset.toPoint());
1849 break;
1850 case QVariant::Point:
1851 argument = argument.toPoint() - offset;
1852 break;
1853 default:
1854 break;
1855 }
1856
1857 const QVariant v = d->control->inputMethodQuery(query, argument);
1858 switch (v.type()) {
1859 case QVariant::RectF:
1860 return v.toRectF().translated(offset);
1861 case QVariant::PointF:
1862 return v.toPointF() + offset;
1863 case QVariant::Rect:
1864 return v.toRect().translated(offset.toPoint());
1865 case QVariant::Point:
1866 return v.toPoint() + offset.toPoint();
1867 default:
1868 break;
1869 }
1870 return v;
1871}
1872
1873/*! \reimp
1874*/
1875void QTextEdit::focusInEvent(QFocusEvent *e)
1876{
1877 Q_D(QTextEdit);
1878 if (e->reason() == Qt::MouseFocusReason) {
1879 d->clickCausedFocus = 1;
1880 }
1881 QAbstractScrollArea::focusInEvent(e);
1882 d->sendControlEvent(e);
1883}
1884
1885/*! \reimp
1886*/
1887void QTextEdit::focusOutEvent(QFocusEvent *e)
1888{
1889 Q_D(QTextEdit);
1890 QAbstractScrollArea::focusOutEvent(e);
1891 d->sendControlEvent(e);
1892}
1893
1894/*! \reimp
1895*/
1896void QTextEdit::showEvent(QShowEvent *)
1897{
1898 Q_D(QTextEdit);
1899 if (!d->anchorToScrollToWhenVisible.isEmpty()) {
1900 scrollToAnchor(d->anchorToScrollToWhenVisible);
1901 d->anchorToScrollToWhenVisible.clear();
1902 d->showCursorOnInitialShow = false;
1903 } else if (d->showCursorOnInitialShow) {
1904 d->showCursorOnInitialShow = false;
1905 ensureCursorVisible();
1906 }
1907}
1908
1909/*! \reimp
1910*/
1911void QTextEdit::changeEvent(QEvent *e)
1912{
1913 Q_D(QTextEdit);
1914 QAbstractScrollArea::changeEvent(e);
1915 if (e->type() == QEvent::ApplicationFontChange
1916 || e->type() == QEvent::FontChange) {
1917 d->control->document()->setDefaultFont(font());
1918 } else if(e->type() == QEvent::ActivationChange) {
1919 if (!isActiveWindow())
1920 d->autoScrollTimer.stop();
1921 } else if (e->type() == QEvent::EnabledChange) {
1922 e->setAccepted(isEnabled());
1923 d->control->setPalette(palette());
1924 d->sendControlEvent(e);
1925 } else if (e->type() == QEvent::PaletteChange) {
1926 d->control->setPalette(palette());
1927 } else if (e->type() == QEvent::LayoutDirectionChange) {
1928 d->sendControlEvent(e);
1929 }
1930}
1931
1932/*! \reimp
1933*/
1934#if QT_CONFIG(wheelevent)
1935void QTextEdit::wheelEvent(QWheelEvent *e)
1936{
1937 Q_D(QTextEdit);
1938 if (!(d->control->textInteractionFlags() & Qt::TextEditable)) {
1939 if (e->modifiers() & Qt::ControlModifier) {
1940 float delta = e->angleDelta().y() / 120.f;
1941 zoomInF(delta);
1942 return;
1943 }
1944 }
1945 QAbstractScrollArea::wheelEvent(e);
1946 updateMicroFocus();
1947}
1948#endif
1949
1950#ifndef QT_NO_CONTEXTMENU
1951/*! This function creates the standard context menu which is shown
1952 when the user clicks on the text edit with the right mouse
1953 button. It is called from the default contextMenuEvent() handler.
1954 The popup menu's ownership is transferred to the caller.
1955
1956 We recommend that you use the createStandardContextMenu(QPoint) version instead
1957 which will enable the actions that are sensitive to where the user clicked.
1958*/
1959
1960QMenu *QTextEdit::createStandardContextMenu()
1961{
1962 Q_D(QTextEdit);
1963 return d->control->createStandardContextMenu(QPointF(), this);
1964}
1965
1966/*!
1967 \since 4.4
1968 This function creates the standard context menu which is shown
1969 when the user clicks on the text edit with the right mouse
1970 button. It is called from the default contextMenuEvent() handler
1971 and it takes the \a position in document coordinates where the mouse click was.
1972 This can enable actions that are sensitive to the position where the user clicked.
1973 The popup menu's ownership is transferred to the caller.
1974*/
1975
1976QMenu *QTextEdit::createStandardContextMenu(const QPoint &position)
1977{
1978 Q_D(QTextEdit);
1979 return d->control->createStandardContextMenu(position, this);
1980}
1981#endif // QT_NO_CONTEXTMENU
1982
1983/*!
1984 returns a QTextCursor at position \a pos (in viewport coordinates).
1985*/
1986QTextCursor QTextEdit::cursorForPosition(const QPoint &pos) const
1987{
1988 Q_D(const QTextEdit);
1989 return d->control->cursorForPosition(d->mapToContents(pos));
1990}
1991
1992/*!
1993 returns a rectangle (in viewport coordinates) that includes the
1994 \a cursor.
1995 */
1996QRect QTextEdit::cursorRect(const QTextCursor &cursor) const
1997{
1998 Q_D(const QTextEdit);
1999 if (cursor.isNull())
2000 return QRect();
2001
2002 QRect r = d->control->cursorRect(cursor).toRect();
2003 r.translate(-d->horizontalOffset(),-d->verticalOffset());
2004 return r;
2005}
2006
2007/*!
2008 returns a rectangle (in viewport coordinates) that includes the
2009 cursor of the text edit.
2010 */
2011QRect QTextEdit::cursorRect() const
2012{
2013 Q_D(const QTextEdit);
2014 QRect r = d->control->cursorRect().toRect();
2015 r.translate(-d->horizontalOffset(),-d->verticalOffset());
2016 return r;
2017}
2018
2019
2020/*!
2021 Returns the reference of the anchor at position \a pos, or an
2022 empty string if no anchor exists at that point.
2023*/
2024QString QTextEdit::anchorAt(const QPoint& pos) const
2025{
2026 Q_D(const QTextEdit);
2027 return d->control->anchorAt(d->mapToContents(pos));
2028}
2029
2030/*!
2031 \property QTextEdit::overwriteMode
2032 \since 4.1
2033 \brief whether text entered by the user will overwrite existing text
2034
2035 As with many text editors, the text editor widget can be configured
2036 to insert or overwrite existing text with new text entered by the user.
2037
2038 If this property is \c true, existing text is overwritten, character-for-character
2039 by new text; otherwise, text is inserted at the cursor position, displacing
2040 existing text.
2041
2042 By default, this property is \c false (new text does not overwrite existing text).
2043*/
2044
2045bool QTextEdit::overwriteMode() const
2046{
2047 Q_D(const QTextEdit);
2048 return d->control->overwriteMode();
2049}
2050
2051void QTextEdit::setOverwriteMode(bool overwrite)
2052{
2053 Q_D(QTextEdit);
2054 d->control->setOverwriteMode(overwrite);
2055}
2056
2057#if QT_DEPRECATED_SINCE(5, 10)
2058/*!
2059 \property QTextEdit::tabStopWidth
2060 \brief the tab stop width in pixels
2061 \since 4.1
2062 \deprecated in Qt 5.10. Use tabStopDistance instead.
2063
2064 By default, this property contains a value of 80 pixels.
2065*/
2066
2067int QTextEdit::tabStopWidth() const
2068{
2069 return qRound(tabStopDistance());
2070}
2071
2072void QTextEdit::setTabStopWidth(int width)
2073{
2074 setTabStopDistance(width);
2075}
2076#endif
2077
2078/*!
2079 \property QTextEdit::tabStopDistance
2080 \brief the tab stop distance in pixels
2081 \since 5.10
2082
2083 By default, this property contains a value of 80 pixels.
2084*/
2085
2086qreal QTextEdit::tabStopDistance() const
2087{
2088 Q_D(const QTextEdit);
2089 return d->control->document()->defaultTextOption().tabStopDistance();
2090}
2091
2092void QTextEdit::setTabStopDistance(qreal distance)
2093{
2094 Q_D(QTextEdit);
2095 QTextOption opt = d->control->document()->defaultTextOption();
2096 if (opt.tabStopDistance() == distance || distance < 0)
2097 return;
2098 opt.setTabStopDistance(distance);
2099 d->control->document()->setDefaultTextOption(opt);
2100}
2101
2102/*!
2103 \since 4.2
2104 \property QTextEdit::cursorWidth
2105
2106 This property specifies the width of the cursor in pixels. The default value is 1.
2107*/
2108int QTextEdit::cursorWidth() const
2109{
2110 Q_D(const QTextEdit);
2111 return d->control->cursorWidth();
2112}
2113
2114void QTextEdit::setCursorWidth(int width)
2115{
2116 Q_D(QTextEdit);
2117 d->control->setCursorWidth(width);
2118}
2119
2120/*!
2121 \property QTextEdit::acceptRichText
2122 \brief whether the text edit accepts rich text insertions by the user
2123 \since 4.1
2124
2125 When this propery is set to false text edit will accept only
2126 plain text input from the user. For example through clipboard or drag and drop.
2127
2128 This property's default is true.
2129*/
2130
2131bool QTextEdit::acceptRichText() const
2132{
2133 Q_D(const QTextEdit);
2134 return d->control->acceptRichText();
2135}
2136
2137void QTextEdit::setAcceptRichText(bool accept)
2138{
2139 Q_D(QTextEdit);
2140 d->control->setAcceptRichText(accept);
2141}
2142
2143/*!
2144 \class QTextEdit::ExtraSelection
2145 \since 4.2
2146 \inmodule QtWidgets
2147
2148 \brief The QTextEdit::ExtraSelection structure provides a way of specifying a
2149 character format for a given selection in a document.
2150*/
2151
2152/*!
2153 \variable QTextEdit::ExtraSelection::cursor
2154 A cursor that contains a selection in a QTextDocument
2155*/
2156
2157/*!
2158 \variable QTextEdit::ExtraSelection::format
2159 A format that is used to specify a foreground or background brush/color
2160 for the selection.
2161*/
2162
2163/*!
2164 \since 4.2
2165 This function allows temporarily marking certain regions in the document
2166 with a given color, specified as \a selections. This can be useful for
2167 example in a programming editor to mark a whole line of text with a given
2168 background color to indicate the existence of a breakpoint.
2169
2170 \sa QTextEdit::ExtraSelection, extraSelections()
2171*/
2172void QTextEdit::setExtraSelections(const QList<ExtraSelection> &selections)
2173{
2174 Q_D(QTextEdit);
2175 d->control->setExtraSelections(selections);
2176}
2177
2178/*!
2179 \since 4.2
2180 Returns previously set extra selections.
2181
2182 \sa setExtraSelections()
2183*/
2184QList<QTextEdit::ExtraSelection> QTextEdit::extraSelections() const
2185{
2186 Q_D(const QTextEdit);
2187 return d->control->extraSelections();
2188}
2189
2190/*!
2191 This function returns a new MIME data object to represent the contents
2192 of the text edit's current selection. It is called when the selection needs
2193 to be encapsulated into a new QMimeData object; for example, when a drag
2194 and drop operation is started, or when data is copied to the clipboard.
2195
2196 If you reimplement this function, note that the ownership of the returned
2197 QMimeData object is passed to the caller. The selection can be retrieved
2198 by using the textCursor() function.
2199*/
2200QMimeData *QTextEdit::createMimeDataFromSelection() const
2201{
2202 Q_D(const QTextEdit);
2203 return d->control->QWidgetTextControl::createMimeDataFromSelection();
2204}
2205
2206/*!
2207 This function returns \c true if the contents of the MIME data object, specified
2208 by \a source, can be decoded and inserted into the document. It is called
2209 for example when during a drag operation the mouse enters this widget and it
2210 is necessary to determine whether it is possible to accept the drag and drop
2211 operation.
2212
2213 Reimplement this function to enable drag and drop support for additional MIME types.
2214 */
2215bool QTextEdit::canInsertFromMimeData(const QMimeData *source) const
2216{
2217 Q_D(const QTextEdit);
2218 return d->control->QWidgetTextControl::canInsertFromMimeData(source);
2219}
2220
2221/*!
2222 This function inserts the contents of the MIME data object, specified
2223 by \a source, into the text edit at the current cursor position. It is
2224 called whenever text is inserted as the result of a clipboard paste
2225 operation, or when the text edit accepts data from a drag and drop
2226 operation.
2227
2228 Reimplement this function to enable drag and drop support for additional MIME types.
2229 */
2230void QTextEdit::insertFromMimeData(const QMimeData *source)
2231{
2232 Q_D(QTextEdit);
2233 d->control->QWidgetTextControl::insertFromMimeData(source);
2234}
2235
2236/*!
2237 \property QTextEdit::readOnly
2238 \brief whether the text edit is read-only
2239
2240 In a read-only text edit the user can only navigate through the
2241 text and select text; modifying the text is not possible.
2242
2243 This property's default is false.
2244*/
2245
2246bool QTextEdit::isReadOnly() const
2247{
2248 Q_D(const QTextEdit);
2249 return !(d->control->textInteractionFlags() & Qt::TextEditable);
2250}
2251
2252void QTextEdit::setReadOnly(bool ro)
2253{
2254 Q_D(QTextEdit);
2255 Qt::TextInteractionFlags flags = Qt::NoTextInteraction;
2256 if (ro) {
2257 flags = Qt::TextSelectableByMouse;
2258#if QT_CONFIG(textbrowser)
2259 if (qobject_cast<QTextBrowser *>(this))
2260 flags |= Qt::TextBrowserInteraction;
2261#endif
2262 } else {
2263 flags = Qt::TextEditorInteraction;
2264 }
2265 d->control->setTextInteractionFlags(flags);
2266 setAttribute(Qt::WA_InputMethodEnabled, shouldEnableInputMethod(this));
2267 QEvent event(QEvent::ReadOnlyChange);
2268 QCoreApplication::sendEvent(this, &event);
2269}
2270
2271/*!
2272 \property QTextEdit::textInteractionFlags
2273 \since 4.2
2274
2275 Specifies how the widget should interact with user input.
2276
2277 The default value depends on whether the QTextEdit is read-only
2278 or editable, and whether it is a QTextBrowser or not.
2279*/
2280
2281void QTextEdit::setTextInteractionFlags(Qt::TextInteractionFlags flags)
2282{
2283 Q_D(QTextEdit);
2284 d->control->setTextInteractionFlags(flags);
2285}
2286
2287Qt::TextInteractionFlags QTextEdit::textInteractionFlags() const
2288{
2289 Q_D(const QTextEdit);
2290 return d->control->textInteractionFlags();
2291}
2292
2293/*!
2294 Merges the properties specified in \a modifier into the current character
2295 format by calling QTextCursor::mergeCharFormat on the editor's cursor.
2296 If the editor has a selection then the properties of \a modifier are
2297 directly applied to the selection.
2298
2299 \sa QTextCursor::mergeCharFormat()
2300 */
2301void QTextEdit::mergeCurrentCharFormat(const QTextCharFormat &modifier)
2302{
2303 Q_D(QTextEdit);
2304 d->control->mergeCurrentCharFormat(modifier);
2305}
2306
2307/*!
2308 Sets the char format that is be used when inserting new text to \a
2309 format by calling QTextCursor::setCharFormat() on the editor's
2310 cursor. If the editor has a selection then the char format is
2311 directly applied to the selection.
2312 */
2313void QTextEdit::setCurrentCharFormat(const QTextCharFormat &format)
2314{
2315 Q_D(QTextEdit);
2316 d->control->setCurrentCharFormat(format);
2317}
2318
2319/*!
2320 Returns the char format that is used when inserting new text.
2321 */
2322QTextCharFormat QTextEdit::currentCharFormat() const
2323{
2324 Q_D(const QTextEdit);
2325 return d->control->currentCharFormat();
2326}
2327
2328/*!
2329 \property QTextEdit::autoFormatting
2330 \brief the enabled set of auto formatting features
2331
2332 The value can be any combination of the values in the
2333 AutoFormattingFlag enum. The default is AutoNone. Choose
2334 AutoAll to enable all automatic formatting.
2335
2336 Currently, the only automatic formatting feature provided is
2337 AutoBulletList; future versions of Qt may offer more.
2338*/
2339
2340QTextEdit::AutoFormatting QTextEdit::autoFormatting() const
2341{
2342 Q_D(const QTextEdit);
2343 return d->autoFormatting;
2344}
2345
2346void QTextEdit::setAutoFormatting(AutoFormatting features)
2347{
2348 Q_D(QTextEdit);
2349 d->autoFormatting = features;
2350}
2351
2352/*!
2353 Convenience slot that inserts \a text at the current
2354 cursor position.
2355
2356 It is equivalent to
2357
2358 \snippet code/src_gui_widgets_qtextedit.cpp 1
2359 */
2360void QTextEdit::insertPlainText(const QString &text)
2361{
2362 Q_D(QTextEdit);
2363 d->control->insertPlainText(text);
2364}
2365
2366/*!
2367 Convenience slot that inserts \a text which is assumed to be of
2368 html formatting at the current cursor position.
2369
2370 It is equivalent to:
2371
2372 \snippet code/src_gui_widgets_qtextedit.cpp 2
2373
2374 \note When using this function with a style sheet, the style sheet will
2375 only apply to the current block in the document. In order to apply a style
2376 sheet throughout a document, use QTextDocument::setDefaultStyleSheet()
2377 instead.
2378 */
2379#ifndef QT_NO_TEXTHTMLPARSER
2380void QTextEdit::insertHtml(const QString &text)
2381{
2382 Q_D(QTextEdit);
2383 d->control->insertHtml(text);
2384}
2385#endif // QT_NO_TEXTHTMLPARSER
2386
2387/*!
2388 Scrolls the text edit so that the anchor with the given \a name is
2389 visible; does nothing if the \a name is empty, or is already
2390 visible, or isn't found.
2391*/
2392void QTextEdit::scrollToAnchor(const QString &name)
2393{
2394 Q_D(QTextEdit);
2395 if (name.isEmpty())
2396 return;
2397
2398 if (!isVisible()) {
2399 d->anchorToScrollToWhenVisible = name;
2400 return;
2401 }
2402
2403 QPointF p = d->control->anchorPosition(name);
2404 const int newPosition = qRound(p.y());
2405 if ( d->vbar->maximum() < newPosition )
2406 d->_q_adjustScrollbars();
2407 d->vbar->setValue(newPosition);
2408}
2409
2410/*!
2411 Zooms in on the text by making the base font size \a range
2412 points larger and recalculating all font sizes to be the new size.
2413 This does not change the size of any images.
2414
2415 \sa zoomOut()
2416*/
2417void QTextEdit::zoomIn(int range)
2418{
2419 zoomInF(range);
2420}
2421
2422/*!
2423 Zooms out on the text by making the base font size \a range points
2424 smaller and recalculating all font sizes to be the new size. This
2425 does not change the size of any images.
2426
2427 \sa zoomIn()
2428*/
2429void QTextEdit::zoomOut(int range)
2430{
2431 zoomInF(-range);
2432}
2433
2434/*!
2435 \internal
2436*/
2437void QTextEdit::zoomInF(float range)
2438{
2439 if (range == 0.f)
2440 return;
2441 QFont f = font();
2442 const float newSize = f.pointSizeF() + range;
2443 if (newSize <= 0)
2444 return;
2445 f.setPointSizeF(newSize);
2446 setFont(f);
2447}
2448
2449/*!
2450 \since 4.2
2451 Moves the cursor by performing the given \a operation.
2452
2453 If \a mode is QTextCursor::KeepAnchor, the cursor selects the text it moves over.
2454 This is the same effect that the user achieves when they hold down the Shift key
2455 and move the cursor with the cursor keys.
2456
2457 \sa QTextCursor::movePosition()
2458*/
2459void QTextEdit::moveCursor(QTextCursor::MoveOperation operation, QTextCursor::MoveMode mode)
2460{
2461 Q_D(QTextEdit);
2462 d->control->moveCursor(operation, mode);
2463}
2464
2465/*!
2466 \since 4.2
2467 Returns whether text can be pasted from the clipboard into the textedit.
2468*/
2469bool QTextEdit::canPaste() const
2470{
2471 Q_D(const QTextEdit);
2472 return d->control->canPaste();
2473}
2474
2475/*!
2476 \since 4.3
2477 Convenience function to print the text edit's document to the given \a printer. This
2478 is equivalent to calling the print method on the document directly except that this
2479 function also supports QPrinter::Selection as print range.
2480
2481 \sa QTextDocument::print()
2482*/
2483#ifndef QT_NO_PRINTER
2484void QTextEdit::print(QPagedPaintDevice *printer) const
2485{
2486 Q_D(const QTextEdit);
2487 d->control->print(printer);
2488}
2489#endif
2490
2491/*! \property QTextEdit::tabChangesFocus
2492 \brief whether \uicontrol Tab changes focus or is accepted as input
2493
2494 In some occasions text edits should not allow the user to input
2495 tabulators or change indentation using the \uicontrol Tab key, as this breaks
2496 the focus chain. The default is false.
2497
2498*/
2499
2500bool QTextEdit::tabChangesFocus() const
2501{
2502 Q_D(const QTextEdit);
2503 return d->tabChangesFocus;
2504}
2505
2506void QTextEdit::setTabChangesFocus(bool b)
2507{
2508 Q_D(QTextEdit);
2509 d->tabChangesFocus = b;
2510}
2511
2512/*!
2513 \property QTextEdit::documentTitle
2514 \brief the title of the document parsed from the text.
2515
2516 By default, for a newly-created, empty document, this property contains
2517 an empty string.
2518*/
2519
2520/*!
2521 \property QTextEdit::lineWrapMode
2522 \brief the line wrap mode
2523
2524 The default mode is WidgetWidth which causes words to be
2525 wrapped at the right edge of the text edit. Wrapping occurs at
2526 whitespace, keeping whole words intact. If you want wrapping to
2527 occur within words use setWordWrapMode(). If you set a wrap mode of
2528 FixedPixelWidth or FixedColumnWidth you should also call
2529 setLineWrapColumnOrWidth() with the width you want.
2530
2531 \sa lineWrapColumnOrWidth
2532*/
2533
2534QTextEdit::LineWrapMode QTextEdit::lineWrapMode() const
2535{
2536 Q_D(const QTextEdit);
2537 return d->lineWrap;
2538}
2539
2540void QTextEdit::setLineWrapMode(LineWrapMode wrap)
2541{
2542 Q_D(QTextEdit);
2543 if (d->lineWrap == wrap)
2544 return;
2545 d->lineWrap = wrap;
2546 d->updateDefaultTextOption();
2547 d->relayoutDocument();
2548}
2549
2550/*!
2551 \property QTextEdit::lineWrapColumnOrWidth
2552 \brief the position (in pixels or columns depending on the wrap mode) where text will be wrapped
2553
2554 If the wrap mode is FixedPixelWidth, the value is the number of
2555 pixels from the left edge of the text edit at which text should be
2556 wrapped. If the wrap mode is FixedColumnWidth, the value is the
2557 column number (in character columns) from the left edge of the
2558 text edit at which text should be wrapped.
2559
2560 By default, this property contains a value of 0.
2561
2562 \sa lineWrapMode
2563*/
2564
2565int QTextEdit::lineWrapColumnOrWidth() const
2566{
2567 Q_D(const QTextEdit);
2568 return d->lineWrapColumnOrWidth;
2569}
2570
2571void QTextEdit::setLineWrapColumnOrWidth(int w)
2572{
2573 Q_D(QTextEdit);
2574 d->lineWrapColumnOrWidth = w;
2575 d->relayoutDocument();
2576}
2577
2578/*!
2579 \property QTextEdit::wordWrapMode
2580 \brief the mode QTextEdit will use when wrapping text by words
2581
2582 By default, this property is set to QTextOption::WrapAtWordBoundaryOrAnywhere.
2583
2584 \sa QTextOption::WrapMode
2585*/
2586
2587QTextOption::WrapMode QTextEdit::wordWrapMode() const
2588{
2589 Q_D(const QTextEdit);
2590 return d->wordWrap;
2591}
2592
2593void QTextEdit::setWordWrapMode(QTextOption::WrapMode mode)
2594{
2595 Q_D(QTextEdit);
2596 if (mode == d->wordWrap)
2597 return;
2598 d->wordWrap = mode;
2599 d->updateDefaultTextOption();
2600}
2601
2602/*!
2603 Finds the next occurrence of the string, \a exp, using the given
2604 \a options. Returns \c true if \a exp was found and changes the
2605 cursor to select the match; otherwise returns \c false.
2606*/
2607bool QTextEdit::find(const QString &exp, QTextDocument::FindFlags options)
2608{
2609 Q_D(QTextEdit);
2610 return d->control->find(exp, options);
2611}
2612
2613/*!
2614 \fn bool QTextEdit::find(const QRegExp &exp, QTextDocument::FindFlags options)
2615
2616 \since 5.3
2617 \overload
2618
2619 Finds the next occurrence, matching the regular expression, \a exp, using the given
2620 \a options. The QTextDocument::FindCaseSensitively option is ignored for this overload,
2621 use QRegExp::caseSensitivity instead.
2622
2623 Returns \c true if a match was found and changes the cursor to select the match;
2624 otherwise returns \c false.
2625*/
2626#ifndef QT_NO_REGEXP
2627bool QTextEdit::find(const QRegExp &exp, QTextDocument::FindFlags options)
2628{
2629 Q_D(QTextEdit);
2630 return d->control->find(exp, options);
2631}
2632#endif
2633
2634/*!
2635 \fn bool QTextEdit::find(const QRegularExpression &exp, QTextDocument::FindFlags options)
2636
2637 \since 5.13
2638 \overload
2639
2640 Finds the next occurrence, matching the regular expression, \a exp, using the given
2641 \a options. The QTextDocument::FindCaseSensitively option is ignored for this overload,
2642 use QRegularExpression::CaseInsensitiveOption instead.
2643
2644 Returns \c true if a match was found and changes the cursor to select the match;
2645 otherwise returns \c false.
2646*/
2647#if QT_CONFIG(regularexpression)
2648bool QTextEdit::find(const QRegularExpression &exp, QTextDocument::FindFlags options)
2649{
2650 Q_D(QTextEdit);
2651 return d->control->find(exp, options);
2652}
2653#endif
2654
2655/*!
2656 \fn void QTextEdit::copyAvailable(bool yes)
2657
2658 This signal is emitted when text is selected or de-selected in the
2659 text edit.
2660
2661 When text is selected this signal will be emitted with \a yes set
2662 to true. If no text has been selected or if the selected text is
2663 de-selected this signal is emitted with \a yes set to false.
2664
2665 If \a yes is true then copy() can be used to copy the selection to
2666 the clipboard. If \a yes is false then copy() does nothing.
2667
2668 \sa selectionChanged()
2669*/
2670
2671/*!
2672 \fn void QTextEdit::currentCharFormatChanged(const QTextCharFormat &f)
2673
2674 This signal is emitted if the current character format has changed, for
2675 example caused by a change of the cursor position.
2676
2677 The new format is \a f.
2678
2679 \sa setCurrentCharFormat()
2680*/
2681
2682/*!
2683 \fn void QTextEdit::selectionChanged()
2684
2685 This signal is emitted whenever the selection changes.
2686
2687 \sa copyAvailable()
2688*/
2689
2690/*!
2691 \fn void QTextEdit::cursorPositionChanged()
2692
2693 This signal is emitted whenever the position of the
2694 cursor changed.
2695*/
2696
2697/*!
2698 \since 4.2
2699
2700 Sets the text edit's \a text. The text can be plain text or HTML
2701 and the text edit will try to guess the right format.
2702
2703 Use setHtml() or setPlainText() directly to avoid text edit's guessing.
2704
2705 \sa toPlainText(), toHtml()
2706*/
2707void QTextEdit::setText(const QString &text)
2708{
2709 Q_D(QTextEdit);
2710 Qt::TextFormat format = d->textFormat;
2711 if (d->textFormat == Qt::AutoText)
2712 format = Qt::mightBeRichText(text) ? Qt::RichText : Qt::PlainText;
2713#ifndef QT_NO_TEXTHTMLPARSER
2714 if (format == Qt::RichText)
2715 setHtml(text);
2716 else
2717#else
2718 Q_UNUSED(format);
2719#endif
2720 setPlainText(text);
2721}
2722
2723
2724/*!
2725 Appends a new paragraph with \a text to the end of the text edit.
2726
2727 \note The new paragraph appended will have the same character format and
2728 block format as the current paragraph, determined by the position of the cursor.
2729
2730 \sa currentCharFormat(), QTextCursor::blockFormat()
2731*/
2732
2733void QTextEdit::append(const QString &text)
2734{
2735 Q_D(QTextEdit);
2736 const bool atBottom = isReadOnly() ? d->verticalOffset() >= d->vbar->maximum() :
2737 d->control->textCursor().atEnd();
2738 d->control->append(text);
2739 if (atBottom)
2740 d->vbar->setValue(d->vbar->maximum());
2741}
2742
2743/*!
2744 Ensures that the cursor is visible by scrolling the text edit if
2745 necessary.
2746*/
2747void QTextEdit::ensureCursorVisible()
2748{
2749 Q_D(QTextEdit);
2750 d->control->ensureCursorVisible();
2751}
2752
2753/*!
2754 \fn void QTextEdit::textChanged()
2755
2756 This signal is emitted whenever the document's content changes; for
2757 example, when text is inserted or deleted, or when formatting is applied.
2758*/
2759
2760/*!
2761 \fn void QTextEdit::undoAvailable(bool available)
2762
2763 This signal is emitted whenever undo operations become available
2764 (\a available is true) or unavailable (\a available is false).
2765*/
2766
2767/*!
2768 \fn void QTextEdit::redoAvailable(bool available)
2769
2770 This signal is emitted whenever redo operations become available
2771 (\a available is true) or unavailable (\a available is false).
2772*/
2773
2774QT_END_NAMESPACE
2775
2776#include "moc_qtextedit.cpp"
2777