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

source code of qtbase/src/widgets/widgets/qtextedit.cpp