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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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