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

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