1/****************************************************************************
2**
3** Copyright (C) 2019 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtWidgets module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qwidgettextcontrol_p.h"
41#include "qwidgettextcontrol_p_p.h"
42
43#ifndef QT_NO_TEXTCONTROL
44
45#include <qfont.h>
46#include <qpainter.h>
47#include <qevent.h>
48#include <qdebug.h>
49#if QT_CONFIG(draganddrop)
50#include <qdrag.h>
51#endif
52#include <qclipboard.h>
53#if QT_CONFIG(menu)
54#include <qmenu.h>
55#include "private/qmenu_p.h"
56#endif
57#include <qstyle.h>
58#include <qtimer.h>
59#include "private/qapplication_p.h"
60#include "private/qtextdocumentlayout_p.h"
61#include "private/qabstracttextdocumentlayout_p.h"
62#include "qtextdocument.h"
63#include "private/qtextdocument_p.h"
64#include "qtextlist.h"
65#include "private/qwidgettextcontrol_p.h"
66#if QT_CONFIG(style_stylesheet)
67# include "private/qstylesheetstyle_p.h"
68#endif
69#if QT_CONFIG(graphicsview)
70#include "qgraphicssceneevent.h"
71#endif
72#include "qpagedpaintdevice.h"
73#include "private/qpagedpaintdevice_p.h"
74#include "qtextdocumentwriter.h"
75#include "qstylehints.h"
76#include "private/qtextcursor_p.h"
77
78#include <qtextformat.h>
79#include <qdatetime.h>
80#include <qbuffer.h>
81#include <qapplication.h>
82#include <limits.h>
83#include <qtexttable.h>
84#include <qvariant.h>
85#include <qurl.h>
86#include <qdesktopservices.h>
87#include <qinputmethod.h>
88#include <qtooltip.h>
89#include <qstyleoption.h>
90#if QT_CONFIG(lineedit)
91#include <QtWidgets/qlineedit.h>
92#endif
93#include <QtGui/qaccessible.h>
94#include <QtCore/qmetaobject.h>
95
96#ifndef QT_NO_SHORTCUT
97#include "private/qapplication_p.h"
98#include "private/qshortcutmap_p.h"
99#include <qkeysequence.h>
100#define ACCEL_KEY(k) (!QCoreApplication::testAttribute(Qt::AA_DontShowShortcutsInContextMenus) \
101 && !QGuiApplicationPrivate::instance()->shortcutMap.hasShortcutForKeySequence(k) ? \
102 QLatin1Char('\t') + QKeySequence(k).toString(QKeySequence::NativeText) : QString())
103
104#else
105#define ACCEL_KEY(k) QString()
106#endif
107
108#include <algorithm>
109
110QT_BEGIN_NAMESPACE
111
112// could go into QTextCursor...
113static QTextLine currentTextLine(const QTextCursor &cursor)
114{
115 const QTextBlock block = cursor.block();
116 if (!block.isValid())
117 return QTextLine();
118
119 const QTextLayout *layout = block.layout();
120 if (!layout)
121 return QTextLine();
122
123 const int relativePos = cursor.position() - block.position();
124 return layout->lineForTextPosition(pos: relativePos);
125}
126
127QWidgetTextControlPrivate::QWidgetTextControlPrivate()
128 : doc(nullptr), cursorOn(false), cursorVisible(false), cursorIsFocusIndicator(false),
129#ifndef Q_OS_ANDROID
130 interactionFlags(Qt::TextEditorInteraction),
131#else
132 interactionFlags(Qt::TextEditable | Qt::TextSelectableByKeyboard),
133#endif
134 dragEnabled(true),
135#if QT_CONFIG(draganddrop)
136 mousePressed(false), mightStartDrag(false),
137#endif
138 lastSelectionPosition(0), lastSelectionAnchor(0),
139 ignoreAutomaticScrollbarAdjustement(false),
140 overwriteMode(false),
141 acceptRichText(true),
142 preeditCursor(0), hideCursor(false),
143 hasFocus(false),
144#ifdef QT_KEYPAD_NAVIGATION
145 hasEditFocus(false),
146#endif
147 isEnabled(true),
148 hadSelectionOnMousePress(false),
149 ignoreUnusedNavigationEvents(false),
150 openExternalLinks(false),
151 wordSelectionEnabled(false)
152{}
153
154bool QWidgetTextControlPrivate::cursorMoveKeyEvent(QKeyEvent *e)
155{
156#ifdef QT_NO_SHORTCUT
157 Q_UNUSED(e);
158#endif
159
160 Q_Q(QWidgetTextControl);
161 if (cursor.isNull())
162 return false;
163
164 const QTextCursor oldSelection = cursor;
165 const int oldCursorPos = cursor.position();
166
167 QTextCursor::MoveMode mode = QTextCursor::MoveAnchor;
168 QTextCursor::MoveOperation op = QTextCursor::NoMove;
169
170 if (false) {
171 }
172#ifndef QT_NO_SHORTCUT
173 if (e == QKeySequence::MoveToNextChar) {
174 op = QTextCursor::Right;
175 }
176 else if (e == QKeySequence::MoveToPreviousChar) {
177 op = QTextCursor::Left;
178 }
179 else if (e == QKeySequence::SelectNextChar) {
180 op = QTextCursor::Right;
181 mode = QTextCursor::KeepAnchor;
182 }
183 else if (e == QKeySequence::SelectPreviousChar) {
184 op = QTextCursor::Left;
185 mode = QTextCursor::KeepAnchor;
186 }
187 else if (e == QKeySequence::SelectNextWord) {
188 op = QTextCursor::WordRight;
189 mode = QTextCursor::KeepAnchor;
190 }
191 else if (e == QKeySequence::SelectPreviousWord) {
192 op = QTextCursor::WordLeft;
193 mode = QTextCursor::KeepAnchor;
194 }
195 else if (e == QKeySequence::SelectStartOfLine) {
196 op = QTextCursor::StartOfLine;
197 mode = QTextCursor::KeepAnchor;
198 }
199 else if (e == QKeySequence::SelectEndOfLine) {
200 op = QTextCursor::EndOfLine;
201 mode = QTextCursor::KeepAnchor;
202 }
203 else if (e == QKeySequence::SelectStartOfBlock) {
204 op = QTextCursor::StartOfBlock;
205 mode = QTextCursor::KeepAnchor;
206 }
207 else if (e == QKeySequence::SelectEndOfBlock) {
208 op = QTextCursor::EndOfBlock;
209 mode = QTextCursor::KeepAnchor;
210 }
211 else if (e == QKeySequence::SelectStartOfDocument) {
212 op = QTextCursor::Start;
213 mode = QTextCursor::KeepAnchor;
214 }
215 else if (e == QKeySequence::SelectEndOfDocument) {
216 op = QTextCursor::End;
217 mode = QTextCursor::KeepAnchor;
218 }
219 else if (e == QKeySequence::SelectPreviousLine) {
220 op = QTextCursor::Up;
221 mode = QTextCursor::KeepAnchor;
222 {
223 QTextBlock block = cursor.block();
224 QTextLine line = currentTextLine(cursor);
225 if (!block.previous().isValid()
226 && line.isValid()
227 && line.lineNumber() == 0)
228 op = QTextCursor::Start;
229 }
230 }
231 else if (e == QKeySequence::SelectNextLine) {
232 op = QTextCursor::Down;
233 mode = QTextCursor::KeepAnchor;
234 {
235 QTextBlock block = cursor.block();
236 QTextLine line = currentTextLine(cursor);
237 if (!block.next().isValid()
238 && line.isValid()
239 && line.lineNumber() == block.layout()->lineCount() - 1)
240 op = QTextCursor::End;
241 }
242 }
243 else if (e == QKeySequence::MoveToNextWord) {
244 op = QTextCursor::WordRight;
245 }
246 else if (e == QKeySequence::MoveToPreviousWord) {
247 op = QTextCursor::WordLeft;
248 }
249 else if (e == QKeySequence::MoveToEndOfBlock) {
250 op = QTextCursor::EndOfBlock;
251 }
252 else if (e == QKeySequence::MoveToStartOfBlock) {
253 op = QTextCursor::StartOfBlock;
254 }
255 else if (e == QKeySequence::MoveToNextLine) {
256 op = QTextCursor::Down;
257 }
258 else if (e == QKeySequence::MoveToPreviousLine) {
259 op = QTextCursor::Up;
260 }
261 else if (e == QKeySequence::MoveToStartOfLine) {
262 op = QTextCursor::StartOfLine;
263 }
264 else if (e == QKeySequence::MoveToEndOfLine) {
265 op = QTextCursor::EndOfLine;
266 }
267 else if (e == QKeySequence::MoveToStartOfDocument) {
268 op = QTextCursor::Start;
269 }
270 else if (e == QKeySequence::MoveToEndOfDocument) {
271 op = QTextCursor::End;
272 }
273#endif // QT_NO_SHORTCUT
274 else {
275 return false;
276 }
277
278// Except for pageup and pagedown, OS X has very different behavior, we don't do it all, but
279// here's the breakdown:
280// Shift still works as an anchor, but only one of the other keys can be down Ctrl (Command),
281// Alt (Option), or Meta (Control).
282// Command/Control + Left/Right -- Move to left or right of the line
283// + Up/Down -- Move to top bottom of the file. (Control doesn't move the cursor)
284// Option + Left/Right -- Move one word Left/right.
285// + Up/Down -- Begin/End of Paragraph.
286// Home/End Top/Bottom of file. (usually don't move the cursor, but will select)
287
288 bool visualNavigation = cursor.visualNavigation();
289 cursor.setVisualNavigation(true);
290 const bool moved = cursor.movePosition(op, mode);
291 cursor.setVisualNavigation(visualNavigation);
292 q->ensureCursorVisible();
293
294 bool ignoreNavigationEvents = ignoreUnusedNavigationEvents;
295 bool isNavigationEvent = e->key() == Qt::Key_Up || e->key() == Qt::Key_Down;
296
297#ifdef QT_KEYPAD_NAVIGATION
298 ignoreNavigationEvents = ignoreNavigationEvents || QApplicationPrivate::keypadNavigationEnabled();
299 isNavigationEvent = isNavigationEvent ||
300 (QApplication::navigationMode() == Qt::NavigationModeKeypadDirectional
301 && (e->key() == Qt::Key_Left || e->key() == Qt::Key_Right));
302#else
303 isNavigationEvent = isNavigationEvent || e->key() == Qt::Key_Left || e->key() == Qt::Key_Right;
304#endif
305
306 if (moved) {
307 if (cursor.position() != oldCursorPos)
308 emit q->cursorPositionChanged();
309 emit q->microFocusChanged();
310 } else if (ignoreNavigationEvents && isNavigationEvent && oldSelection.anchor() == cursor.anchor()) {
311 return false;
312 }
313
314 selectionChanged(/*forceEmitSelectionChanged =*/(mode == QTextCursor::KeepAnchor));
315
316 repaintOldAndNewSelection(oldSelection);
317
318 return true;
319}
320
321void QWidgetTextControlPrivate::updateCurrentCharFormat()
322{
323 Q_Q(QWidgetTextControl);
324
325 QTextCharFormat fmt = cursor.charFormat();
326 if (fmt == lastCharFormat)
327 return;
328 lastCharFormat = fmt;
329
330 emit q->currentCharFormatChanged(format: fmt);
331 emit q->microFocusChanged();
332}
333
334void QWidgetTextControlPrivate::indent()
335{
336 QTextBlockFormat blockFmt = cursor.blockFormat();
337
338 QTextList *list = cursor.currentList();
339 if (!list) {
340 QTextBlockFormat modifier;
341 modifier.setIndent(blockFmt.indent() + 1);
342 cursor.mergeBlockFormat(modifier);
343 } else {
344 QTextListFormat format = list->format();
345 format.setIndent(format.indent() + 1);
346
347 if (list->itemNumber(cursor.block()) == 1)
348 list->setFormat(format);
349 else
350 cursor.createList(format);
351 }
352}
353
354void QWidgetTextControlPrivate::outdent()
355{
356 QTextBlockFormat blockFmt = cursor.blockFormat();
357
358 QTextList *list = cursor.currentList();
359
360 if (!list) {
361 QTextBlockFormat modifier;
362 modifier.setIndent(blockFmt.indent() - 1);
363 cursor.mergeBlockFormat(modifier);
364 } else {
365 QTextListFormat listFmt = list->format();
366 listFmt.setIndent(listFmt.indent() - 1);
367 list->setFormat(listFmt);
368 }
369}
370
371void QWidgetTextControlPrivate::gotoNextTableCell()
372{
373 QTextTable *table = cursor.currentTable();
374 QTextTableCell cell = table->cellAt(c: cursor);
375
376 int newColumn = cell.column() + cell.columnSpan();
377 int newRow = cell.row();
378
379 if (newColumn >= table->columns()) {
380 newColumn = 0;
381 ++newRow;
382 if (newRow >= table->rows())
383 table->insertRows(pos: table->rows(), num: 1);
384 }
385
386 cell = table->cellAt(row: newRow, col: newColumn);
387 cursor = cell.firstCursorPosition();
388}
389
390void QWidgetTextControlPrivate::gotoPreviousTableCell()
391{
392 QTextTable *table = cursor.currentTable();
393 QTextTableCell cell = table->cellAt(c: cursor);
394
395 int newColumn = cell.column() - 1;
396 int newRow = cell.row();
397
398 if (newColumn < 0) {
399 newColumn = table->columns() - 1;
400 --newRow;
401 if (newRow < 0)
402 return;
403 }
404
405 cell = table->cellAt(row: newRow, col: newColumn);
406 cursor = cell.firstCursorPosition();
407}
408
409void QWidgetTextControlPrivate::createAutoBulletList()
410{
411 cursor.beginEditBlock();
412
413 QTextBlockFormat blockFmt = cursor.blockFormat();
414
415 QTextListFormat listFmt;
416 listFmt.setStyle(QTextListFormat::ListDisc);
417 listFmt.setIndent(blockFmt.indent() + 1);
418
419 blockFmt.setIndent(0);
420 cursor.setBlockFormat(blockFmt);
421
422 cursor.createList(format: listFmt);
423
424 cursor.endEditBlock();
425}
426
427void QWidgetTextControlPrivate::init(Qt::TextFormat format, const QString &text, QTextDocument *document)
428{
429 Q_Q(QWidgetTextControl);
430 setContent(format, text, document);
431
432 doc->setUndoRedoEnabled(interactionFlags & Qt::TextEditable);
433 q->setCursorWidth(-1);
434}
435
436void QWidgetTextControlPrivate::setContent(Qt::TextFormat format, const QString &text, QTextDocument *document)
437{
438 Q_Q(QWidgetTextControl);
439
440 // for use when called from setPlainText. we may want to re-use the currently
441 // set char format then.
442 const QTextCharFormat charFormatForInsertion = cursor.charFormat();
443
444 bool clearDocument = true;
445 if (!doc) {
446 if (document) {
447 doc = document;
448 clearDocument = false;
449 } else {
450 palette = QApplication::palette(className: "QWidgetTextControl");
451 doc = new QTextDocument(q);
452 }
453 _q_documentLayoutChanged();
454 cursor = QTextCursor(doc);
455
456// #### doc->documentLayout()->setPaintDevice(viewport);
457
458 QObject::connect(sender: doc, SIGNAL(contentsChanged()), receiver: q, SLOT(_q_updateCurrentCharFormatAndSelection()));
459 QObject::connect(sender: doc, SIGNAL(cursorPositionChanged(QTextCursor)), receiver: q, SLOT(_q_emitCursorPosChanged(QTextCursor)));
460 QObject::connect(sender: doc, SIGNAL(documentLayoutChanged()), receiver: q, SLOT(_q_documentLayoutChanged()));
461
462 // convenience signal forwards
463 QObject::connect(sender: doc, SIGNAL(undoAvailable(bool)), receiver: q, SIGNAL(undoAvailable(bool)));
464 QObject::connect(sender: doc, SIGNAL(redoAvailable(bool)), receiver: q, SIGNAL(redoAvailable(bool)));
465 QObject::connect(sender: doc, SIGNAL(modificationChanged(bool)), receiver: q, SIGNAL(modificationChanged(bool)));
466 QObject::connect(sender: doc, SIGNAL(blockCountChanged(int)), receiver: q, SIGNAL(blockCountChanged(int)));
467 }
468
469 bool previousUndoRedoState = doc->isUndoRedoEnabled();
470 if (!document)
471 doc->setUndoRedoEnabled(false);
472
473 //Saving the index save some time.
474 static int contentsChangedIndex = QMetaMethod::fromSignal(signal: &QTextDocument::contentsChanged).methodIndex();
475 static int textChangedIndex = QMetaMethod::fromSignal(signal: &QWidgetTextControl::textChanged).methodIndex();
476 // avoid multiple textChanged() signals being emitted
477 QMetaObject::disconnect(sender: doc, signal_index: contentsChangedIndex, receiver: q, method_index: textChangedIndex);
478
479 if (!text.isEmpty()) {
480 // clear 'our' cursor for insertion to prevent
481 // the emission of the cursorPositionChanged() signal.
482 // instead we emit it only once at the end instead of
483 // at the end of the document after loading and when
484 // positioning the cursor again to the start of the
485 // document.
486 cursor = QTextCursor();
487 if (format == Qt::PlainText) {
488 QTextCursor formatCursor(doc);
489 // put the setPlainText and the setCharFormat into one edit block,
490 // so that the syntax highlight triggers only /once/ for the entire
491 // document, not twice.
492 formatCursor.beginEditBlock();
493 doc->setPlainText(text);
494 doc->setUndoRedoEnabled(false);
495 formatCursor.select(selection: QTextCursor::Document);
496 formatCursor.setCharFormat(charFormatForInsertion);
497 formatCursor.endEditBlock();
498#if QT_CONFIG(textmarkdownreader)
499 } else if (format == Qt::MarkdownText) {
500 doc->setMarkdown(markdown: text);
501 doc->setUndoRedoEnabled(false);
502#endif
503 } else {
504#ifndef QT_NO_TEXTHTMLPARSER
505 doc->setHtml(text);
506#else
507 doc->setPlainText(text);
508#endif
509 doc->setUndoRedoEnabled(false);
510 }
511 cursor = QTextCursor(doc);
512 } else if (clearDocument) {
513 doc->clear();
514 }
515 cursor.setCharFormat(charFormatForInsertion);
516
517 QMetaObject::connect(sender: doc, signal_index: contentsChangedIndex, receiver: q, method_index: textChangedIndex);
518 emit q->textChanged();
519 if (!document)
520 doc->setUndoRedoEnabled(previousUndoRedoState);
521 _q_updateCurrentCharFormatAndSelection();
522 if (!document)
523 doc->setModified(false);
524
525 q->ensureCursorVisible();
526 emit q->cursorPositionChanged();
527
528 QObject::connect(sender: doc, SIGNAL(contentsChange(int,int,int)), receiver: q, SLOT(_q_contentsChanged(int,int,int)), Qt::UniqueConnection);
529}
530
531void QWidgetTextControlPrivate::startDrag()
532{
533#if QT_CONFIG(draganddrop)
534 Q_Q(QWidgetTextControl);
535 mousePressed = false;
536 if (!contextWidget)
537 return;
538 QMimeData *data = q->createMimeDataFromSelection();
539
540 QDrag *drag = new QDrag(contextWidget);
541 drag->setMimeData(data);
542
543 Qt::DropActions actions = Qt::CopyAction;
544 Qt::DropAction action;
545 if (interactionFlags & Qt::TextEditable) {
546 actions |= Qt::MoveAction;
547 action = drag->exec(supportedActions: actions, defaultAction: Qt::MoveAction);
548 } else {
549 action = drag->exec(supportedActions: actions, defaultAction: Qt::CopyAction);
550 }
551
552 if (action == Qt::MoveAction && drag->target() != contextWidget)
553 cursor.removeSelectedText();
554#endif
555}
556
557void QWidgetTextControlPrivate::setCursorPosition(const QPointF &pos)
558{
559 Q_Q(QWidgetTextControl);
560 const int cursorPos = q->hitTest(point: pos, accuracy: Qt::FuzzyHit);
561 if (cursorPos == -1)
562 return;
563 cursor.setPosition(pos: cursorPos);
564}
565
566void QWidgetTextControlPrivate::setCursorPosition(int pos, QTextCursor::MoveMode mode)
567{
568 cursor.setPosition(pos, mode);
569
570 if (mode != QTextCursor::KeepAnchor) {
571 selectedWordOnDoubleClick = QTextCursor();
572 selectedBlockOnTrippleClick = QTextCursor();
573 }
574}
575
576void QWidgetTextControlPrivate::repaintCursor()
577{
578 Q_Q(QWidgetTextControl);
579 emit q->updateRequest(rect: cursorRectPlusUnicodeDirectionMarkers(cursor));
580}
581
582void QWidgetTextControlPrivate::repaintOldAndNewSelection(const QTextCursor &oldSelection)
583{
584 Q_Q(QWidgetTextControl);
585 if (cursor.hasSelection()
586 && oldSelection.hasSelection()
587 && cursor.currentFrame() == oldSelection.currentFrame()
588 && !cursor.hasComplexSelection()
589 && !oldSelection.hasComplexSelection()
590 && cursor.anchor() == oldSelection.anchor()
591 ) {
592 QTextCursor differenceSelection(doc);
593 differenceSelection.setPosition(pos: oldSelection.position());
594 differenceSelection.setPosition(pos: cursor.position(), mode: QTextCursor::KeepAnchor);
595 emit q->updateRequest(rect: q->selectionRect(cursor: differenceSelection));
596 } else {
597 if (!oldSelection.isNull())
598 emit q->updateRequest(rect: q->selectionRect(cursor: oldSelection) | cursorRectPlusUnicodeDirectionMarkers(cursor: oldSelection));
599 emit q->updateRequest(rect: q->selectionRect() | cursorRectPlusUnicodeDirectionMarkers(cursor));
600 }
601}
602
603void QWidgetTextControlPrivate::selectionChanged(bool forceEmitSelectionChanged /*=false*/)
604{
605 Q_Q(QWidgetTextControl);
606 if (forceEmitSelectionChanged) {
607 emit q->selectionChanged();
608#ifndef QT_NO_ACCESSIBILITY
609 if (q->parent() && q->parent()->isWidgetType()) {
610 QAccessibleTextSelectionEvent ev(q->parent(), cursor.anchor(), cursor.position());
611 QAccessible::updateAccessibility(event: &ev);
612 }
613#endif
614 }
615
616 if (cursor.position() == lastSelectionPosition
617 && cursor.anchor() == lastSelectionAnchor)
618 return;
619
620 bool selectionStateChange = (cursor.hasSelection()
621 != (lastSelectionPosition != lastSelectionAnchor));
622 if (selectionStateChange)
623 emit q->copyAvailable(b: cursor.hasSelection());
624
625 if (!forceEmitSelectionChanged
626 && (selectionStateChange
627 || (cursor.hasSelection()
628 && (cursor.position() != lastSelectionPosition
629 || cursor.anchor() != lastSelectionAnchor)))) {
630 emit q->selectionChanged();
631#ifndef QT_NO_ACCESSIBILITY
632 if (q->parent() && q->parent()->isWidgetType()) {
633 QAccessibleTextSelectionEvent ev(q->parent(), cursor.anchor(), cursor.position());
634 QAccessible::updateAccessibility(event: &ev);
635 }
636#endif
637 }
638 emit q->microFocusChanged();
639 lastSelectionPosition = cursor.position();
640 lastSelectionAnchor = cursor.anchor();
641}
642
643void QWidgetTextControlPrivate::_q_updateCurrentCharFormatAndSelection()
644{
645 updateCurrentCharFormat();
646 selectionChanged();
647}
648
649#ifndef QT_NO_CLIPBOARD
650void QWidgetTextControlPrivate::setClipboardSelection()
651{
652 QClipboard *clipboard = QGuiApplication::clipboard();
653 if (!cursor.hasSelection() || !clipboard->supportsSelection())
654 return;
655 Q_Q(QWidgetTextControl);
656 QMimeData *data = q->createMimeDataFromSelection();
657 clipboard->setMimeData(data, mode: QClipboard::Selection);
658}
659#endif
660
661void QWidgetTextControlPrivate::_q_emitCursorPosChanged(const QTextCursor &someCursor)
662{
663 Q_Q(QWidgetTextControl);
664 if (someCursor.isCopyOf(other: cursor)) {
665 emit q->cursorPositionChanged();
666 emit q->microFocusChanged();
667 }
668}
669
670void QWidgetTextControlPrivate::_q_contentsChanged(int from, int charsRemoved, int charsAdded)
671{
672#ifndef QT_NO_ACCESSIBILITY
673 Q_Q(QWidgetTextControl);
674
675 if (QAccessible::isActive() && q->parent() && q->parent()->isWidgetType()) {
676 QTextCursor tmp(doc);
677 tmp.setPosition(pos: from);
678 // when setting a new text document the length is off
679 // QTBUG-32583 - characterCount is off by 1 requires the -1
680 tmp.setPosition(pos: qMin(a: doc->characterCount() - 1, b: from + charsAdded), mode: QTextCursor::KeepAnchor);
681 QString newText = tmp.selectedText();
682
683 // always report the right number of removed chars, but in lack of the real string use spaces
684 QString oldText = QString(charsRemoved, QLatin1Char(' '));
685
686 QAccessibleEvent *ev = nullptr;
687 if (charsRemoved == 0) {
688 ev = new QAccessibleTextInsertEvent(q->parent(), from, newText);
689 } else if (charsAdded == 0) {
690 ev = new QAccessibleTextRemoveEvent(q->parent(), from, oldText);
691 } else {
692 ev = new QAccessibleTextUpdateEvent(q->parent(), from, oldText, newText);
693 }
694 QAccessible::updateAccessibility(event: ev);
695 delete ev;
696 }
697#else
698 Q_UNUSED(from)
699 Q_UNUSED(charsRemoved)
700 Q_UNUSED(charsAdded)
701#endif
702}
703
704void QWidgetTextControlPrivate::_q_documentLayoutChanged()
705{
706 Q_Q(QWidgetTextControl);
707 QAbstractTextDocumentLayout *layout = doc->documentLayout();
708 QObject::connect(sender: layout, SIGNAL(update(QRectF)), receiver: q, SIGNAL(updateRequest(QRectF)));
709 QObject::connect(sender: layout, SIGNAL(updateBlock(QTextBlock)), receiver: q, SLOT(_q_updateBlock(QTextBlock)));
710 QObject::connect(sender: layout, SIGNAL(documentSizeChanged(QSizeF)), receiver: q, SIGNAL(documentSizeChanged(QSizeF)));
711
712}
713
714void QWidgetTextControlPrivate::setCursorVisible(bool visible)
715{
716 if (cursorVisible == visible)
717 return;
718
719 cursorVisible = visible;
720 updateCursorBlinking();
721
722 if (cursorVisible)
723 connect(sender: QGuiApplication::styleHints(), signal: &QStyleHints::cursorFlashTimeChanged, receiverPrivate: this, slot: &QWidgetTextControlPrivate::updateCursorBlinking);
724 else
725 disconnect(sender: QGuiApplication::styleHints(), signal: &QStyleHints::cursorFlashTimeChanged, receiverPrivate: this, slot: &QWidgetTextControlPrivate::updateCursorBlinking);
726}
727
728void QWidgetTextControlPrivate::updateCursorBlinking()
729{
730 cursorBlinkTimer.stop();
731 if (cursorVisible) {
732 int flashTime = QGuiApplication::styleHints()->cursorFlashTime();
733 if (flashTime >= 2)
734 cursorBlinkTimer.start(msec: flashTime / 2, obj: q_func());
735 }
736
737 cursorOn = cursorVisible;
738 repaintCursor();
739}
740
741void QWidgetTextControlPrivate::extendWordwiseSelection(int suggestedNewPosition, qreal mouseXPosition)
742{
743 Q_Q(QWidgetTextControl);
744
745 // if inside the initial selected word keep that
746 if (suggestedNewPosition >= selectedWordOnDoubleClick.selectionStart()
747 && suggestedNewPosition <= selectedWordOnDoubleClick.selectionEnd()) {
748 q->setTextCursor(cursor: selectedWordOnDoubleClick);
749 return;
750 }
751
752 QTextCursor curs = selectedWordOnDoubleClick;
753 curs.setPosition(pos: suggestedNewPosition, mode: QTextCursor::KeepAnchor);
754
755 if (!curs.movePosition(op: QTextCursor::StartOfWord))
756 return;
757 const int wordStartPos = curs.position();
758
759 const int blockPos = curs.block().position();
760 const QPointF blockCoordinates = q->blockBoundingRect(block: curs.block()).topLeft();
761
762 QTextLine line = currentTextLine(cursor: curs);
763 if (!line.isValid())
764 return;
765
766 const qreal wordStartX = line.cursorToX(cursorPos: curs.position() - blockPos) + blockCoordinates.x();
767
768 if (!curs.movePosition(op: QTextCursor::EndOfWord))
769 return;
770 const int wordEndPos = curs.position();
771
772 const QTextLine otherLine = currentTextLine(cursor: curs);
773 if (otherLine.textStart() != line.textStart()
774 || wordEndPos == wordStartPos)
775 return;
776
777 const qreal wordEndX = line.cursorToX(cursorPos: curs.position() - blockPos) + blockCoordinates.x();
778
779 if (!wordSelectionEnabled && (mouseXPosition < wordStartX || mouseXPosition > wordEndX))
780 return;
781
782 if (wordSelectionEnabled) {
783 if (suggestedNewPosition < selectedWordOnDoubleClick.position()) {
784 cursor.setPosition(pos: selectedWordOnDoubleClick.selectionEnd());
785 setCursorPosition(pos: wordStartPos, mode: QTextCursor::KeepAnchor);
786 } else {
787 cursor.setPosition(pos: selectedWordOnDoubleClick.selectionStart());
788 setCursorPosition(pos: wordEndPos, mode: QTextCursor::KeepAnchor);
789 }
790 } else {
791 // keep the already selected word even when moving to the left
792 // (#39164)
793 if (suggestedNewPosition < selectedWordOnDoubleClick.position())
794 cursor.setPosition(pos: selectedWordOnDoubleClick.selectionEnd());
795 else
796 cursor.setPosition(pos: selectedWordOnDoubleClick.selectionStart());
797
798 const qreal differenceToStart = mouseXPosition - wordStartX;
799 const qreal differenceToEnd = wordEndX - mouseXPosition;
800
801 if (differenceToStart < differenceToEnd)
802 setCursorPosition(pos: wordStartPos, mode: QTextCursor::KeepAnchor);
803 else
804 setCursorPosition(pos: wordEndPos, mode: QTextCursor::KeepAnchor);
805 }
806
807 if (interactionFlags & Qt::TextSelectableByMouse) {
808#ifndef QT_NO_CLIPBOARD
809 setClipboardSelection();
810#endif
811 selectionChanged(forceEmitSelectionChanged: true);
812 }
813}
814
815void QWidgetTextControlPrivate::extendBlockwiseSelection(int suggestedNewPosition)
816{
817 Q_Q(QWidgetTextControl);
818
819 // if inside the initial selected line keep that
820 if (suggestedNewPosition >= selectedBlockOnTrippleClick.selectionStart()
821 && suggestedNewPosition <= selectedBlockOnTrippleClick.selectionEnd()) {
822 q->setTextCursor(cursor: selectedBlockOnTrippleClick);
823 return;
824 }
825
826 if (suggestedNewPosition < selectedBlockOnTrippleClick.position()) {
827 cursor.setPosition(pos: selectedBlockOnTrippleClick.selectionEnd());
828 cursor.setPosition(pos: suggestedNewPosition, mode: QTextCursor::KeepAnchor);
829 cursor.movePosition(op: QTextCursor::StartOfBlock, QTextCursor::KeepAnchor);
830 } else {
831 cursor.setPosition(pos: selectedBlockOnTrippleClick.selectionStart());
832 cursor.setPosition(pos: suggestedNewPosition, mode: QTextCursor::KeepAnchor);
833 cursor.movePosition(op: QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
834 cursor.movePosition(op: QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
835 }
836
837 if (interactionFlags & Qt::TextSelectableByMouse) {
838#ifndef QT_NO_CLIPBOARD
839 setClipboardSelection();
840#endif
841 selectionChanged(forceEmitSelectionChanged: true);
842 }
843}
844
845void QWidgetTextControlPrivate::_q_deleteSelected()
846{
847 if (!(interactionFlags & Qt::TextEditable) || !cursor.hasSelection())
848 return;
849 cursor.removeSelectedText();
850}
851
852void QWidgetTextControl::undo()
853{
854 Q_D(QWidgetTextControl);
855 d->repaintSelection();
856 const int oldCursorPos = d->cursor.position();
857 d->doc->undo(cursor: &d->cursor);
858 if (d->cursor.position() != oldCursorPos)
859 emit cursorPositionChanged();
860 emit microFocusChanged();
861 ensureCursorVisible();
862}
863
864void QWidgetTextControl::redo()
865{
866 Q_D(QWidgetTextControl);
867 d->repaintSelection();
868 const int oldCursorPos = d->cursor.position();
869 d->doc->redo(cursor: &d->cursor);
870 if (d->cursor.position() != oldCursorPos)
871 emit cursorPositionChanged();
872 emit microFocusChanged();
873 ensureCursorVisible();
874}
875
876QWidgetTextControl::QWidgetTextControl(QObject *parent)
877 : QInputControl(QInputControl::TextEdit, *new QWidgetTextControlPrivate, parent)
878{
879 Q_D(QWidgetTextControl);
880 d->init();
881}
882
883QWidgetTextControl::QWidgetTextControl(const QString &text, QObject *parent)
884 : QInputControl(QInputControl::TextEdit, *new QWidgetTextControlPrivate, parent)
885{
886 Q_D(QWidgetTextControl);
887 d->init(format: Qt::RichText, text);
888}
889
890QWidgetTextControl::QWidgetTextControl(QTextDocument *doc, QObject *parent)
891 : QInputControl(QInputControl::TextEdit, *new QWidgetTextControlPrivate, parent)
892{
893 Q_D(QWidgetTextControl);
894 d->init(format: Qt::RichText, text: QString(), document: doc);
895}
896
897QWidgetTextControl::~QWidgetTextControl()
898{
899}
900
901void QWidgetTextControl::setDocument(QTextDocument *document)
902{
903 Q_D(QWidgetTextControl);
904 if (d->doc == document)
905 return;
906
907 d->doc->disconnect(receiver: this);
908 d->doc->documentLayout()->disconnect(receiver: this);
909 d->doc->documentLayout()->setPaintDevice(nullptr);
910
911 if (d->doc->parent() == this)
912 delete d->doc;
913
914 d->doc = nullptr;
915 d->setContent(format: Qt::RichText, text: QString(), document);
916}
917
918QTextDocument *QWidgetTextControl::document() const
919{
920 Q_D(const QWidgetTextControl);
921 return d->doc;
922}
923
924void QWidgetTextControl::setTextCursor(const QTextCursor &cursor, bool selectionClipboard)
925{
926 Q_D(QWidgetTextControl);
927 d->cursorIsFocusIndicator = false;
928 const bool posChanged = cursor.position() != d->cursor.position();
929 const QTextCursor oldSelection = d->cursor;
930 d->cursor = cursor;
931 d->cursorOn = d->hasFocus
932 && (d->interactionFlags & (Qt::TextSelectableByKeyboard | Qt::TextEditable));
933 d->_q_updateCurrentCharFormatAndSelection();
934 ensureCursorVisible();
935 d->repaintOldAndNewSelection(oldSelection);
936 if (posChanged)
937 emit cursorPositionChanged();
938
939#ifndef QT_NO_CLIPBOARD
940 if (selectionClipboard)
941 d->setClipboardSelection();
942#endif
943}
944
945QTextCursor QWidgetTextControl::textCursor() const
946{
947 Q_D(const QWidgetTextControl);
948 return d->cursor;
949}
950
951#ifndef QT_NO_CLIPBOARD
952
953void QWidgetTextControl::cut()
954{
955 Q_D(QWidgetTextControl);
956 if (!(d->interactionFlags & Qt::TextEditable) || !d->cursor.hasSelection())
957 return;
958 copy();
959 d->cursor.removeSelectedText();
960}
961
962void QWidgetTextControl::copy()
963{
964 Q_D(QWidgetTextControl);
965 if (!d->cursor.hasSelection())
966 return;
967 QMimeData *data = createMimeDataFromSelection();
968 QGuiApplication::clipboard()->setMimeData(data);
969}
970
971void QWidgetTextControl::paste(QClipboard::Mode mode)
972{
973 const QMimeData *md = QGuiApplication::clipboard()->mimeData(mode);
974 if (md)
975 insertFromMimeData(source: md);
976}
977#endif
978
979void QWidgetTextControl::clear()
980{
981 Q_D(QWidgetTextControl);
982 // clears and sets empty content
983 d->extraSelections.clear();
984 d->setContent();
985}
986
987
988void QWidgetTextControl::selectAll()
989{
990 Q_D(QWidgetTextControl);
991 const int selectionLength = qAbs(t: d->cursor.position() - d->cursor.anchor());
992 d->cursor.select(selection: QTextCursor::Document);
993 d->selectionChanged(forceEmitSelectionChanged: selectionLength != qAbs(t: d->cursor.position() - d->cursor.anchor()));
994 d->cursorIsFocusIndicator = false;
995 emit updateRequest();
996}
997
998void QWidgetTextControl::processEvent(QEvent *e, const QPointF &coordinateOffset, QWidget *contextWidget)
999{
1000 QTransform t;
1001 t.translate(dx: coordinateOffset.x(), dy: coordinateOffset.y());
1002 processEvent(e, transform: t, contextWidget);
1003}
1004
1005void QWidgetTextControl::processEvent(QEvent *e, const QTransform &transform, QWidget *contextWidget)
1006{
1007 Q_D(QWidgetTextControl);
1008 if (d->interactionFlags == Qt::NoTextInteraction) {
1009 e->ignore();
1010 return;
1011 }
1012
1013 d->contextWidget = contextWidget;
1014
1015 if (!d->contextWidget) {
1016 switch (e->type()) {
1017#if QT_CONFIG(graphicsview)
1018 case QEvent::GraphicsSceneMouseMove:
1019 case QEvent::GraphicsSceneMousePress:
1020 case QEvent::GraphicsSceneMouseRelease:
1021 case QEvent::GraphicsSceneMouseDoubleClick:
1022 case QEvent::GraphicsSceneContextMenu:
1023 case QEvent::GraphicsSceneHoverEnter:
1024 case QEvent::GraphicsSceneHoverMove:
1025 case QEvent::GraphicsSceneHoverLeave:
1026 case QEvent::GraphicsSceneHelp:
1027 case QEvent::GraphicsSceneDragEnter:
1028 case QEvent::GraphicsSceneDragMove:
1029 case QEvent::GraphicsSceneDragLeave:
1030 case QEvent::GraphicsSceneDrop: {
1031 QGraphicsSceneEvent *ev = static_cast<QGraphicsSceneEvent *>(e);
1032 d->contextWidget = ev->widget();
1033 break;
1034 }
1035#endif // QT_CONFIG(graphicsview)
1036 default: break;
1037 };
1038 }
1039
1040 switch (e->type()) {
1041 case QEvent::KeyPress:
1042 d->keyPressEvent(e: static_cast<QKeyEvent *>(e));
1043 break;
1044 case QEvent::MouseButtonPress: {
1045 QMouseEvent *ev = static_cast<QMouseEvent *>(e);
1046 d->mousePressEvent(e: ev, button: ev->button(), pos: transform.map(p: ev->pos()), modifiers: ev->modifiers(),
1047 buttons: ev->buttons(), globalPos: ev->globalPos());
1048 break; }
1049 case QEvent::MouseMove: {
1050 QMouseEvent *ev = static_cast<QMouseEvent *>(e);
1051 d->mouseMoveEvent(e: ev, button: ev->button(), pos: transform.map(p: ev->pos()), modifiers: ev->modifiers(),
1052 buttons: ev->buttons(), globalPos: ev->globalPos());
1053 break; }
1054 case QEvent::MouseButtonRelease: {
1055 QMouseEvent *ev = static_cast<QMouseEvent *>(e);
1056 d->mouseReleaseEvent(e: ev, button: ev->button(), pos: transform.map(p: ev->pos()), modifiers: ev->modifiers(),
1057 buttons: ev->buttons(), globalPos: ev->globalPos());
1058 break; }
1059 case QEvent::MouseButtonDblClick: {
1060 QMouseEvent *ev = static_cast<QMouseEvent *>(e);
1061 d->mouseDoubleClickEvent(e: ev, button: ev->button(), pos: transform.map(p: ev->pos()), modifiers: ev->modifiers(),
1062 buttons: ev->buttons(), globalPos: ev->globalPos());
1063 break; }
1064 case QEvent::InputMethod:
1065 d->inputMethodEvent(static_cast<QInputMethodEvent *>(e));
1066 break;
1067#ifndef QT_NO_CONTEXTMENU
1068 case QEvent::ContextMenu: {
1069 QContextMenuEvent *ev = static_cast<QContextMenuEvent *>(e);
1070 d->contextMenuEvent(screenPos: ev->globalPos(), docPos: transform.map(p: ev->pos()), contextWidget);
1071 break; }
1072#endif // QT_NO_CONTEXTMENU
1073 case QEvent::FocusIn:
1074 case QEvent::FocusOut:
1075 d->focusEvent(e: static_cast<QFocusEvent *>(e));
1076 break;
1077
1078 case QEvent::EnabledChange:
1079 d->isEnabled = e->isAccepted();
1080 break;
1081
1082#ifndef QT_NO_TOOLTIP
1083 case QEvent::ToolTip: {
1084 QHelpEvent *ev = static_cast<QHelpEvent *>(e);
1085 d->showToolTip(globalPos: ev->globalPos(), pos: transform.map(p: ev->pos()), contextWidget);
1086 break;
1087 }
1088#endif // QT_NO_TOOLTIP
1089
1090#if QT_CONFIG(draganddrop)
1091 case QEvent::DragEnter: {
1092 QDragEnterEvent *ev = static_cast<QDragEnterEvent *>(e);
1093 if (d->dragEnterEvent(e, mimeData: ev->mimeData()))
1094 ev->acceptProposedAction();
1095 break;
1096 }
1097 case QEvent::DragLeave:
1098 d->dragLeaveEvent();
1099 break;
1100 case QEvent::DragMove: {
1101 QDragMoveEvent *ev = static_cast<QDragMoveEvent *>(e);
1102 if (d->dragMoveEvent(e, mimeData: ev->mimeData(), pos: transform.map(p: ev->pos())))
1103 ev->acceptProposedAction();
1104 break;
1105 }
1106 case QEvent::Drop: {
1107 QDropEvent *ev = static_cast<QDropEvent *>(e);
1108 if (d->dropEvent(mimeData: ev->mimeData(), pos: transform.map(p: ev->pos()), dropAction: ev->dropAction(), source: ev->source()))
1109 ev->acceptProposedAction();
1110 break;
1111 }
1112#endif
1113
1114#if QT_CONFIG(graphicsview)
1115 case QEvent::GraphicsSceneMousePress: {
1116 QGraphicsSceneMouseEvent *ev = static_cast<QGraphicsSceneMouseEvent *>(e);
1117 d->mousePressEvent(e: ev, button: ev->button(), pos: transform.map(p: ev->pos()), modifiers: ev->modifiers(), buttons: ev->buttons(),
1118 globalPos: ev->screenPos());
1119 break; }
1120 case QEvent::GraphicsSceneMouseMove: {
1121 QGraphicsSceneMouseEvent *ev = static_cast<QGraphicsSceneMouseEvent *>(e);
1122 d->mouseMoveEvent(e: ev, button: ev->button(), pos: transform.map(p: ev->pos()), modifiers: ev->modifiers(), buttons: ev->buttons(),
1123 globalPos: ev->screenPos());
1124 break; }
1125 case QEvent::GraphicsSceneMouseRelease: {
1126 QGraphicsSceneMouseEvent *ev = static_cast<QGraphicsSceneMouseEvent *>(e);
1127 d->mouseReleaseEvent(e: ev, button: ev->button(), pos: transform.map(p: ev->pos()), modifiers: ev->modifiers(), buttons: ev->buttons(),
1128 globalPos: ev->screenPos());
1129 break; }
1130 case QEvent::GraphicsSceneMouseDoubleClick: {
1131 QGraphicsSceneMouseEvent *ev = static_cast<QGraphicsSceneMouseEvent *>(e);
1132 d->mouseDoubleClickEvent(e: ev, button: ev->button(), pos: transform.map(p: ev->pos()), modifiers: ev->modifiers(), buttons: ev->buttons(),
1133 globalPos: ev->screenPos());
1134 break; }
1135 case QEvent::GraphicsSceneContextMenu: {
1136 QGraphicsSceneContextMenuEvent *ev = static_cast<QGraphicsSceneContextMenuEvent *>(e);
1137 d->contextMenuEvent(screenPos: ev->screenPos(), docPos: transform.map(p: ev->pos()), contextWidget);
1138 break; }
1139
1140 case QEvent::GraphicsSceneHoverMove: {
1141 QGraphicsSceneHoverEvent *ev = static_cast<QGraphicsSceneHoverEvent *>(e);
1142 d->mouseMoveEvent(e: ev, button: Qt::NoButton, pos: transform.map(p: ev->pos()), modifiers: ev->modifiers(),buttons: Qt::NoButton,
1143 globalPos: ev->screenPos());
1144 break; }
1145
1146 case QEvent::GraphicsSceneDragEnter: {
1147 QGraphicsSceneDragDropEvent *ev = static_cast<QGraphicsSceneDragDropEvent *>(e);
1148 if (d->dragEnterEvent(e, mimeData: ev->mimeData()))
1149 ev->acceptProposedAction();
1150 break; }
1151 case QEvent::GraphicsSceneDragLeave:
1152 d->dragLeaveEvent();
1153 break;
1154 case QEvent::GraphicsSceneDragMove: {
1155 QGraphicsSceneDragDropEvent *ev = static_cast<QGraphicsSceneDragDropEvent *>(e);
1156 if (d->dragMoveEvent(e, mimeData: ev->mimeData(), pos: transform.map(p: ev->pos())))
1157 ev->acceptProposedAction();
1158 break; }
1159 case QEvent::GraphicsSceneDrop: {
1160 QGraphicsSceneDragDropEvent *ev = static_cast<QGraphicsSceneDragDropEvent *>(e);
1161 if (d->dropEvent(mimeData: ev->mimeData(), pos: transform.map(p: ev->pos()), dropAction: ev->dropAction(), source: ev->source()))
1162 ev->accept();
1163 break; }
1164#endif // QT_CONFIG(graphicsview)
1165#ifdef QT_KEYPAD_NAVIGATION
1166 case QEvent::EnterEditFocus:
1167 case QEvent::LeaveEditFocus:
1168 if (QApplicationPrivate::keypadNavigationEnabled())
1169 d->editFocusEvent(e);
1170 break;
1171#endif
1172 case QEvent::ShortcutOverride:
1173 if (d->interactionFlags & Qt::TextEditable) {
1174 QKeyEvent* ke = static_cast<QKeyEvent *>(e);
1175 if (isCommonTextEditShortcut(ke))
1176 ke->accept();
1177 }
1178 break;
1179 default:
1180 break;
1181 }
1182}
1183
1184bool QWidgetTextControl::event(QEvent *e)
1185{
1186 return QObject::event(event: e);
1187}
1188
1189void QWidgetTextControl::timerEvent(QTimerEvent *e)
1190{
1191 Q_D(QWidgetTextControl);
1192 if (e->timerId() == d->cursorBlinkTimer.timerId()) {
1193 d->cursorOn = !d->cursorOn;
1194
1195 if (d->cursor.hasSelection())
1196 d->cursorOn &= (QApplication::style()->styleHint(stylehint: QStyle::SH_BlinkCursorWhenTextSelected)
1197 != 0);
1198
1199 d->repaintCursor();
1200 } else if (e->timerId() == d->trippleClickTimer.timerId()) {
1201 d->trippleClickTimer.stop();
1202 }
1203}
1204
1205void QWidgetTextControl::setPlainText(const QString &text)
1206{
1207 Q_D(QWidgetTextControl);
1208 d->setContent(format: Qt::PlainText, text);
1209}
1210
1211#if QT_CONFIG(textmarkdownreader)
1212void QWidgetTextControl::setMarkdown(const QString &text)
1213{
1214 Q_D(QWidgetTextControl);
1215 d->setContent(format: Qt::MarkdownText, text);
1216}
1217#endif
1218
1219void QWidgetTextControl::setHtml(const QString &text)
1220{
1221 Q_D(QWidgetTextControl);
1222 d->setContent(format: Qt::RichText, text);
1223}
1224
1225void QWidgetTextControlPrivate::keyPressEvent(QKeyEvent *e)
1226{
1227 Q_Q(QWidgetTextControl);
1228#ifndef QT_NO_SHORTCUT
1229 if (e == QKeySequence::SelectAll) {
1230 e->accept();
1231 q->selectAll();
1232#ifndef QT_NO_CLIPBOARD
1233 setClipboardSelection();
1234#endif
1235 return;
1236 }
1237#ifndef QT_NO_CLIPBOARD
1238 else if (e == QKeySequence::Copy) {
1239 e->accept();
1240 q->copy();
1241 return;
1242 }
1243#endif
1244#endif // QT_NO_SHORTCUT
1245
1246 if (interactionFlags & Qt::TextSelectableByKeyboard
1247 && cursorMoveKeyEvent(e))
1248 goto accept;
1249
1250 if (interactionFlags & Qt::LinksAccessibleByKeyboard) {
1251 if ((e->key() == Qt::Key_Return
1252 || e->key() == Qt::Key_Enter
1253#ifdef QT_KEYPAD_NAVIGATION
1254 || e->key() == Qt::Key_Select
1255#endif
1256 )
1257 && cursor.hasSelection()) {
1258
1259 e->accept();
1260 activateLinkUnderCursor();
1261 return;
1262 }
1263 }
1264
1265 if (!(interactionFlags & Qt::TextEditable)) {
1266 e->ignore();
1267 return;
1268 }
1269
1270 if (e->key() == Qt::Key_Direction_L || e->key() == Qt::Key_Direction_R) {
1271 QTextBlockFormat fmt;
1272 fmt.setLayoutDirection((e->key() == Qt::Key_Direction_L) ? Qt::LeftToRight : Qt::RightToLeft);
1273 cursor.mergeBlockFormat(modifier: fmt);
1274 goto accept;
1275 }
1276
1277 // schedule a repaint of the region of the cursor, as when we move it we
1278 // want to make sure the old cursor disappears (not noticeable when moving
1279 // only a few pixels but noticeable when jumping between cells in tables for
1280 // example)
1281 repaintSelection();
1282
1283 if (e->key() == Qt::Key_Backspace && !(e->modifiers() & ~(Qt::ShiftModifier | Qt::GroupSwitchModifier))) {
1284 QTextBlockFormat blockFmt = cursor.blockFormat();
1285 QTextList *list = cursor.currentList();
1286 if (list && cursor.atBlockStart() && !cursor.hasSelection()) {
1287 list->remove(cursor.block());
1288 } else if (cursor.atBlockStart() && blockFmt.indent() > 0) {
1289 blockFmt.setIndent(blockFmt.indent() - 1);
1290 cursor.setBlockFormat(blockFmt);
1291 } else {
1292 QTextCursor localCursor = cursor;
1293 localCursor.deletePreviousChar();
1294 if (cursor.d)
1295 cursor.d->setX();
1296 }
1297 goto accept;
1298 }
1299#ifndef QT_NO_SHORTCUT
1300 else if (e == QKeySequence::InsertParagraphSeparator) {
1301 cursor.insertBlock();
1302 e->accept();
1303 goto accept;
1304 } else if (e == QKeySequence::InsertLineSeparator) {
1305 cursor.insertText(text: QString(QChar::LineSeparator));
1306 e->accept();
1307 goto accept;
1308 }
1309#endif
1310 if (false) {
1311 }
1312#ifndef QT_NO_SHORTCUT
1313 else if (e == QKeySequence::Undo) {
1314 q->undo();
1315 }
1316 else if (e == QKeySequence::Redo) {
1317 q->redo();
1318 }
1319#ifndef QT_NO_CLIPBOARD
1320 else if (e == QKeySequence::Cut) {
1321 q->cut();
1322 }
1323 else if (e == QKeySequence::Paste) {
1324 QClipboard::Mode mode = QClipboard::Clipboard;
1325 if (QGuiApplication::clipboard()->supportsSelection()) {
1326 if (e->modifiers() == (Qt::CTRL | Qt::SHIFT) && e->key() == Qt::Key_Insert)
1327 mode = QClipboard::Selection;
1328 }
1329 q->paste(mode);
1330 }
1331#endif
1332 else if (e == QKeySequence::Delete) {
1333 QTextCursor localCursor = cursor;
1334 localCursor.deleteChar();
1335 if (cursor.d)
1336 cursor.d->setX();
1337 } else if (e == QKeySequence::Backspace) {
1338 QTextCursor localCursor = cursor;
1339 localCursor.deletePreviousChar();
1340 if (cursor.d)
1341 cursor.d->setX();
1342 }else if (e == QKeySequence::DeleteEndOfWord) {
1343 if (!cursor.hasSelection())
1344 cursor.movePosition(op: QTextCursor::NextWord, QTextCursor::KeepAnchor);
1345 cursor.removeSelectedText();
1346 }
1347 else if (e == QKeySequence::DeleteStartOfWord) {
1348 if (!cursor.hasSelection())
1349 cursor.movePosition(op: QTextCursor::PreviousWord, QTextCursor::KeepAnchor);
1350 cursor.removeSelectedText();
1351 }
1352 else if (e == QKeySequence::DeleteEndOfLine) {
1353 QTextBlock block = cursor.block();
1354 if (cursor.position() == block.position() + block.length() - 2)
1355 cursor.movePosition(op: QTextCursor::Right, QTextCursor::KeepAnchor);
1356 else
1357 cursor.movePosition(op: QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
1358 cursor.removeSelectedText();
1359 }
1360#endif // QT_NO_SHORTCUT
1361 else {
1362 goto process;
1363 }
1364 goto accept;
1365
1366process:
1367 {
1368 if (q->isAcceptableInput(event: e)) {
1369 if (overwriteMode
1370 // no need to call deleteChar() if we have a selection, insertText
1371 // does it already
1372 && !cursor.hasSelection()
1373 && !cursor.atBlockEnd())
1374 cursor.deleteChar();
1375
1376 cursor.insertText(text: e->text());
1377 selectionChanged();
1378 } else {
1379 e->ignore();
1380 return;
1381 }
1382 }
1383
1384 accept:
1385
1386#ifndef QT_NO_CLIPBOARD
1387 setClipboardSelection();
1388#endif
1389
1390 e->accept();
1391 cursorOn = true;
1392
1393 q->ensureCursorVisible();
1394
1395 updateCurrentCharFormat();
1396}
1397
1398QVariant QWidgetTextControl::loadResource(int type, const QUrl &name)
1399{
1400 Q_UNUSED(type);
1401 Q_UNUSED(name);
1402 return QVariant();
1403}
1404
1405void QWidgetTextControlPrivate::_q_updateBlock(const QTextBlock &block)
1406{
1407 Q_Q(QWidgetTextControl);
1408 QRectF br = q->blockBoundingRect(block);
1409 br.setRight(qreal(INT_MAX)); // the block might have shrunk
1410 emit q->updateRequest(rect: br);
1411}
1412
1413QRectF QWidgetTextControlPrivate::rectForPosition(int position) const
1414{
1415 Q_Q(const QWidgetTextControl);
1416 const QTextBlock block = doc->findBlock(pos: position);
1417 if (!block.isValid())
1418 return QRectF();
1419 const QAbstractTextDocumentLayout *docLayout = doc->documentLayout();
1420 const QTextLayout *layout = block.layout();
1421 const QPointF layoutPos = q->blockBoundingRect(block).topLeft();
1422 int relativePos = position - block.position();
1423 if (preeditCursor != 0) {
1424 int preeditPos = layout->preeditAreaPosition();
1425 if (relativePos == preeditPos)
1426 relativePos += preeditCursor;
1427 else if (relativePos > preeditPos)
1428 relativePos += layout->preeditAreaText().length();
1429 }
1430 QTextLine line = layout->lineForTextPosition(pos: relativePos);
1431
1432 int cursorWidth;
1433 {
1434 bool ok = false;
1435#ifndef QT_NO_PROPERTIES
1436 cursorWidth = docLayout->property(name: "cursorWidth").toInt(ok: &ok);
1437#endif
1438 if (!ok)
1439 cursorWidth = 1;
1440 }
1441
1442 QRectF r;
1443
1444 if (line.isValid()) {
1445 qreal x = line.cursorToX(cursorPos: relativePos);
1446 qreal w = 0;
1447 if (overwriteMode) {
1448 if (relativePos < line.textLength() - line.textStart())
1449 w = line.cursorToX(cursorPos: relativePos + 1) - x;
1450 else
1451 w = QFontMetrics(block.layout()->font()).horizontalAdvance(QLatin1Char(' ')); // in sync with QTextLine::draw()
1452 }
1453 r = QRectF(layoutPos.x() + x, layoutPos.y() + line.y(),
1454 cursorWidth + w, line.height());
1455 } else {
1456 r = QRectF(layoutPos.x(), layoutPos.y(), cursorWidth, 10); // #### correct height
1457 }
1458
1459 return r;
1460}
1461
1462namespace {
1463struct QTextFrameComparator {
1464 bool operator()(QTextFrame *frame, int position) { return frame->firstPosition() < position; }
1465 bool operator()(int position, QTextFrame *frame) { return position < frame->firstPosition(); }
1466};
1467}
1468
1469static QRectF boundingRectOfFloatsInSelection(const QTextCursor &cursor)
1470{
1471 QRectF r;
1472 QTextFrame *frame = cursor.currentFrame();
1473 const QList<QTextFrame *> children = frame->childFrames();
1474
1475 const QList<QTextFrame *>::ConstIterator firstFrame = std::lower_bound(first: children.constBegin(), last: children.constEnd(),
1476 val: cursor.selectionStart(), comp: QTextFrameComparator());
1477 const QList<QTextFrame *>::ConstIterator lastFrame = std::upper_bound(first: children.constBegin(), last: children.constEnd(),
1478 val: cursor.selectionEnd(), comp: QTextFrameComparator());
1479 for (QList<QTextFrame *>::ConstIterator it = firstFrame; it != lastFrame; ++it) {
1480 if ((*it)->frameFormat().position() != QTextFrameFormat::InFlow)
1481 r |= frame->document()->documentLayout()->frameBoundingRect(frame: *it);
1482 }
1483 return r;
1484}
1485
1486QRectF QWidgetTextControl::selectionRect(const QTextCursor &cursor) const
1487{
1488 Q_D(const QWidgetTextControl);
1489
1490 QRectF r = d->rectForPosition(position: cursor.selectionStart());
1491
1492 if (cursor.hasComplexSelection() && cursor.currentTable()) {
1493 QTextTable *table = cursor.currentTable();
1494
1495 r = d->doc->documentLayout()->frameBoundingRect(frame: table);
1496 /*
1497 int firstRow, numRows, firstColumn, numColumns;
1498 cursor.selectedTableCells(&firstRow, &numRows, &firstColumn, &numColumns);
1499
1500 const QTextTableCell firstCell = table->cellAt(firstRow, firstColumn);
1501 const QTextTableCell lastCell = table->cellAt(firstRow + numRows - 1, firstColumn + numColumns - 1);
1502
1503 const QAbstractTextDocumentLayout * const layout = doc->documentLayout();
1504
1505 QRectF tableSelRect = layout->blockBoundingRect(firstCell.firstCursorPosition().block());
1506
1507 for (int col = firstColumn; col < firstColumn + numColumns; ++col) {
1508 const QTextTableCell cell = table->cellAt(firstRow, col);
1509 const qreal y = layout->blockBoundingRect(cell.firstCursorPosition().block()).top();
1510
1511 tableSelRect.setTop(qMin(tableSelRect.top(), y));
1512 }
1513
1514 for (int row = firstRow; row < firstRow + numRows; ++row) {
1515 const QTextTableCell cell = table->cellAt(row, firstColumn);
1516 const qreal x = layout->blockBoundingRect(cell.firstCursorPosition().block()).left();
1517
1518 tableSelRect.setLeft(qMin(tableSelRect.left(), x));
1519 }
1520
1521 for (int col = firstColumn; col < firstColumn + numColumns; ++col) {
1522 const QTextTableCell cell = table->cellAt(firstRow + numRows - 1, col);
1523 const qreal y = layout->blockBoundingRect(cell.lastCursorPosition().block()).bottom();
1524
1525 tableSelRect.setBottom(qMax(tableSelRect.bottom(), y));
1526 }
1527
1528 for (int row = firstRow; row < firstRow + numRows; ++row) {
1529 const QTextTableCell cell = table->cellAt(row, firstColumn + numColumns - 1);
1530 const qreal x = layout->blockBoundingRect(cell.lastCursorPosition().block()).right();
1531
1532 tableSelRect.setRight(qMax(tableSelRect.right(), x));
1533 }
1534
1535 r = tableSelRect.toRect();
1536 */
1537 } else if (cursor.hasSelection()) {
1538 const int position = cursor.selectionStart();
1539 const int anchor = cursor.selectionEnd();
1540 const QTextBlock posBlock = d->doc->findBlock(pos: position);
1541 const QTextBlock anchorBlock = d->doc->findBlock(pos: anchor);
1542 if (posBlock == anchorBlock && posBlock.isValid() && posBlock.layout()->lineCount()) {
1543 const QTextLine posLine = posBlock.layout()->lineForTextPosition(pos: position - posBlock.position());
1544 const QTextLine anchorLine = anchorBlock.layout()->lineForTextPosition(pos: anchor - anchorBlock.position());
1545
1546 const int firstLine = qMin(a: posLine.lineNumber(), b: anchorLine.lineNumber());
1547 const int lastLine = qMax(a: posLine.lineNumber(), b: anchorLine.lineNumber());
1548 const QTextLayout *layout = posBlock.layout();
1549 r = QRectF();
1550 for (int i = firstLine; i <= lastLine; ++i) {
1551 r |= layout->lineAt(i).rect();
1552 r |= layout->lineAt(i).naturalTextRect(); // might be bigger in the case of wrap not enabled
1553 }
1554 r.translate(p: blockBoundingRect(block: posBlock).topLeft());
1555 } else {
1556 QRectF anchorRect = d->rectForPosition(position: cursor.selectionEnd());
1557 r |= anchorRect;
1558 r |= boundingRectOfFloatsInSelection(cursor);
1559 QRectF frameRect(d->doc->documentLayout()->frameBoundingRect(frame: cursor.currentFrame()));
1560 r.setLeft(frameRect.left());
1561 r.setRight(frameRect.right());
1562 }
1563 if (r.isValid())
1564 r.adjust(xp1: -1, yp1: -1, xp2: 1, yp2: 1);
1565 }
1566
1567 return r;
1568}
1569
1570QRectF QWidgetTextControl::selectionRect() const
1571{
1572 Q_D(const QWidgetTextControl);
1573 return selectionRect(cursor: d->cursor);
1574}
1575
1576void QWidgetTextControlPrivate::mousePressEvent(QEvent *e, Qt::MouseButton button, const QPointF &pos, Qt::KeyboardModifiers modifiers,
1577 Qt::MouseButtons buttons, const QPoint &globalPos)
1578{
1579 Q_Q(QWidgetTextControl);
1580
1581 mousePressPos = pos.toPoint();
1582
1583#if QT_CONFIG(draganddrop)
1584 mightStartDrag = false;
1585#endif
1586
1587 if (sendMouseEventToInputContext(
1588 e, eventType: QEvent::MouseButtonPress, button, pos, modifiers, buttons, globalPos)) {
1589 return;
1590 }
1591
1592 if (interactionFlags & Qt::LinksAccessibleByMouse) {
1593 anchorOnMousePress = q->anchorAt(pos);
1594
1595 if (cursorIsFocusIndicator) {
1596 cursorIsFocusIndicator = false;
1597 repaintSelection();
1598 cursor.clearSelection();
1599 }
1600 }
1601 if (!(button & Qt::LeftButton) ||
1602 !((interactionFlags & Qt::TextSelectableByMouse) || (interactionFlags & Qt::TextEditable))) {
1603 e->ignore();
1604 return;
1605 }
1606 bool wasValid = blockWithMarkerUnderMouse.isValid();
1607 blockWithMarkerUnderMouse = q->blockWithMarkerAt(pos);
1608 if (wasValid != blockWithMarkerUnderMouse.isValid())
1609 emit q->blockMarkerHovered(block: blockWithMarkerUnderMouse);
1610
1611
1612 cursorIsFocusIndicator = false;
1613 const QTextCursor oldSelection = cursor;
1614 const int oldCursorPos = cursor.position();
1615
1616 mousePressed = (interactionFlags & Qt::TextSelectableByMouse);
1617
1618 commitPreedit();
1619
1620 if (trippleClickTimer.isActive()
1621 && ((pos - trippleClickPoint).toPoint().manhattanLength() < QApplication::startDragDistance())) {
1622
1623 cursor.movePosition(op: QTextCursor::StartOfBlock);
1624 cursor.movePosition(op: QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
1625 cursor.movePosition(op: QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
1626 selectedBlockOnTrippleClick = cursor;
1627
1628 anchorOnMousePress = QString();
1629 blockWithMarkerUnderMouse = QTextBlock();
1630 emit q->blockMarkerHovered(block: blockWithMarkerUnderMouse);
1631
1632 trippleClickTimer.stop();
1633 } else {
1634 int cursorPos = q->hitTest(point: pos, accuracy: Qt::FuzzyHit);
1635 if (cursorPos == -1) {
1636 e->ignore();
1637 return;
1638 }
1639
1640 if (modifiers == Qt::ShiftModifier && (interactionFlags & Qt::TextSelectableByMouse)) {
1641 if (wordSelectionEnabled && !selectedWordOnDoubleClick.hasSelection()) {
1642 selectedWordOnDoubleClick = cursor;
1643 selectedWordOnDoubleClick.select(selection: QTextCursor::WordUnderCursor);
1644 }
1645
1646 if (selectedBlockOnTrippleClick.hasSelection())
1647 extendBlockwiseSelection(suggestedNewPosition: cursorPos);
1648 else if (selectedWordOnDoubleClick.hasSelection())
1649 extendWordwiseSelection(suggestedNewPosition: cursorPos, mouseXPosition: pos.x());
1650 else if (!wordSelectionEnabled)
1651 setCursorPosition(pos: cursorPos, mode: QTextCursor::KeepAnchor);
1652 } else {
1653
1654 if (dragEnabled
1655 && cursor.hasSelection()
1656 && !cursorIsFocusIndicator
1657 && cursorPos >= cursor.selectionStart()
1658 && cursorPos <= cursor.selectionEnd()
1659 && q->hitTest(point: pos, accuracy: Qt::ExactHit) != -1) {
1660#if QT_CONFIG(draganddrop)
1661 mightStartDrag = true;
1662#endif
1663 return;
1664 }
1665
1666 setCursorPosition(pos: cursorPos);
1667 }
1668 }
1669
1670 if (interactionFlags & Qt::TextEditable) {
1671 q->ensureCursorVisible();
1672 if (cursor.position() != oldCursorPos)
1673 emit q->cursorPositionChanged();
1674 _q_updateCurrentCharFormatAndSelection();
1675 } else {
1676 if (cursor.position() != oldCursorPos) {
1677 emit q->cursorPositionChanged();
1678 emit q->microFocusChanged();
1679 }
1680 selectionChanged();
1681 }
1682 repaintOldAndNewSelection(oldSelection);
1683 hadSelectionOnMousePress = cursor.hasSelection();
1684}
1685
1686void QWidgetTextControlPrivate::mouseMoveEvent(QEvent *e, Qt::MouseButton button, const QPointF &mousePos, Qt::KeyboardModifiers modifiers,
1687 Qt::MouseButtons buttons, const QPoint &globalPos)
1688{
1689 Q_Q(QWidgetTextControl);
1690
1691 if (interactionFlags & Qt::LinksAccessibleByMouse) {
1692 QString anchor = q->anchorAt(pos: mousePos);
1693 if (anchor != highlightedAnchor) {
1694 highlightedAnchor = anchor;
1695 emit q->linkHovered(anchor);
1696 }
1697 }
1698
1699 if (buttons & Qt::LeftButton) {
1700 const bool editable = interactionFlags & Qt::TextEditable;
1701
1702 if (!(mousePressed
1703 || editable
1704 || mightStartDrag
1705 || selectedWordOnDoubleClick.hasSelection()
1706 || selectedBlockOnTrippleClick.hasSelection()))
1707 return;
1708
1709 const QTextCursor oldSelection = cursor;
1710 const int oldCursorPos = cursor.position();
1711
1712 if (mightStartDrag) {
1713 if ((mousePos.toPoint() - mousePressPos).manhattanLength() > QApplication::startDragDistance())
1714 startDrag();
1715 return;
1716 }
1717
1718 const qreal mouseX = qreal(mousePos.x());
1719
1720 int newCursorPos = q->hitTest(point: mousePos, accuracy: Qt::FuzzyHit);
1721
1722 if (isPreediting()) {
1723 // note: oldCursorPos not including preedit
1724 int selectionStartPos = q->hitTest(point: mousePressPos, accuracy: Qt::FuzzyHit);
1725
1726 if (newCursorPos != selectionStartPos) {
1727 commitPreedit();
1728 // commit invalidates positions
1729 newCursorPos = q->hitTest(point: mousePos, accuracy: Qt::FuzzyHit);
1730 selectionStartPos = q->hitTest(point: mousePressPos, accuracy: Qt::FuzzyHit);
1731 setCursorPosition(pos: selectionStartPos);
1732 }
1733 }
1734
1735 if (newCursorPos == -1)
1736 return;
1737
1738 if (mousePressed && wordSelectionEnabled && !selectedWordOnDoubleClick.hasSelection()) {
1739 selectedWordOnDoubleClick = cursor;
1740 selectedWordOnDoubleClick.select(selection: QTextCursor::WordUnderCursor);
1741 }
1742
1743 if (selectedBlockOnTrippleClick.hasSelection())
1744 extendBlockwiseSelection(suggestedNewPosition: newCursorPos);
1745 else if (selectedWordOnDoubleClick.hasSelection())
1746 extendWordwiseSelection(suggestedNewPosition: newCursorPos, mouseXPosition: mouseX);
1747 else if (mousePressed && !isPreediting())
1748 setCursorPosition(pos: newCursorPos, mode: QTextCursor::KeepAnchor);
1749
1750 if (interactionFlags & Qt::TextEditable) {
1751 // don't call ensureVisible for the visible cursor to avoid jumping
1752 // scrollbars. the autoscrolling ensures smooth scrolling if necessary.
1753 //q->ensureCursorVisible();
1754 if (cursor.position() != oldCursorPos)
1755 emit q->cursorPositionChanged();
1756 _q_updateCurrentCharFormatAndSelection();
1757#ifndef QT_NO_IM
1758 if (contextWidget)
1759 QGuiApplication::inputMethod()->update(queries: Qt::ImQueryInput);
1760#endif //QT_NO_IM
1761 } else {
1762 //emit q->visibilityRequest(QRectF(mousePos, QSizeF(1, 1)));
1763 if (cursor.position() != oldCursorPos) {
1764 emit q->cursorPositionChanged();
1765 emit q->microFocusChanged();
1766 }
1767 }
1768 selectionChanged(forceEmitSelectionChanged: true);
1769 repaintOldAndNewSelection(oldSelection);
1770 } else {
1771 bool wasValid = blockWithMarkerUnderMouse.isValid();
1772 blockWithMarkerUnderMouse = q->blockWithMarkerAt(pos: mousePos);
1773 if (wasValid != blockWithMarkerUnderMouse.isValid())
1774 emit q->blockMarkerHovered(block: blockWithMarkerUnderMouse);
1775 }
1776
1777 sendMouseEventToInputContext(e, eventType: QEvent::MouseMove, button, pos: mousePos, modifiers, buttons, globalPos);
1778}
1779
1780void QWidgetTextControlPrivate::mouseReleaseEvent(QEvent *e, Qt::MouseButton button, const QPointF &pos, Qt::KeyboardModifiers modifiers,
1781 Qt::MouseButtons buttons, const QPoint &globalPos)
1782{
1783 Q_Q(QWidgetTextControl);
1784
1785 const QTextCursor oldSelection = cursor;
1786 if (sendMouseEventToInputContext(
1787 e, eventType: QEvent::MouseButtonRelease, button, pos, modifiers, buttons, globalPos)) {
1788 repaintOldAndNewSelection(oldSelection);
1789 return;
1790 }
1791
1792 const int oldCursorPos = cursor.position();
1793
1794#if QT_CONFIG(draganddrop)
1795 if (mightStartDrag && (button & Qt::LeftButton)) {
1796 mousePressed = false;
1797 setCursorPosition(pos);
1798 cursor.clearSelection();
1799 selectionChanged();
1800 }
1801#endif
1802 if (mousePressed) {
1803 mousePressed = false;
1804#ifndef QT_NO_CLIPBOARD
1805 setClipboardSelection();
1806 selectionChanged(forceEmitSelectionChanged: true);
1807 } else if (button == Qt::MiddleButton
1808 && (interactionFlags & Qt::TextEditable)
1809 && QGuiApplication::clipboard()->supportsSelection()) {
1810 setCursorPosition(pos);
1811 const QMimeData *md = QGuiApplication::clipboard()->mimeData(mode: QClipboard::Selection);
1812 if (md)
1813 q->insertFromMimeData(source: md);
1814#endif
1815 }
1816
1817 repaintOldAndNewSelection(oldSelection);
1818
1819 if (cursor.position() != oldCursorPos) {
1820 emit q->cursorPositionChanged();
1821 emit q->microFocusChanged();
1822 }
1823
1824 // toggle any checkbox that the user clicks
1825 if ((interactionFlags & Qt::TextEditable) && (button & Qt::LeftButton) &&
1826 (blockWithMarkerUnderMouse.isValid()) && !cursor.hasSelection()) {
1827 QTextBlock markerBlock = q->blockWithMarkerAt(pos);
1828 if (markerBlock == blockWithMarkerUnderMouse) {
1829 auto fmt = blockWithMarkerUnderMouse.blockFormat();
1830 switch (fmt.marker()) {
1831 case QTextBlockFormat::MarkerType::Unchecked :
1832 fmt.setMarker(QTextBlockFormat::MarkerType::Checked);
1833 break;
1834 case QTextBlockFormat::MarkerType::Checked:
1835 fmt.setMarker(QTextBlockFormat::MarkerType::Unchecked);
1836 break;
1837 default:
1838 break;
1839 }
1840 cursor.setBlockFormat(fmt);
1841 }
1842 }
1843
1844 if (interactionFlags & Qt::LinksAccessibleByMouse) {
1845 if (!(button & Qt::LeftButton))
1846 return;
1847
1848 const QString anchor = q->anchorAt(pos);
1849
1850 if (anchor.isEmpty())
1851 return;
1852
1853 if (!cursor.hasSelection()
1854 || (anchor == anchorOnMousePress && hadSelectionOnMousePress)) {
1855
1856 const int anchorPos = q->hitTest(point: pos, accuracy: Qt::ExactHit);
1857 if (anchorPos != -1) {
1858 cursor.setPosition(pos: anchorPos);
1859
1860 QString anchor = anchorOnMousePress;
1861 anchorOnMousePress = QString();
1862 activateLinkUnderCursor(href: anchor);
1863 }
1864 }
1865 }
1866}
1867
1868void QWidgetTextControlPrivate::mouseDoubleClickEvent(QEvent *e, Qt::MouseButton button, const QPointF &pos,
1869 Qt::KeyboardModifiers modifiers, Qt::MouseButtons buttons,
1870 const QPoint &globalPos)
1871{
1872 Q_Q(QWidgetTextControl);
1873
1874 if (button == Qt::LeftButton
1875 && (interactionFlags & Qt::TextSelectableByMouse)) {
1876
1877#if QT_CONFIG(draganddrop)
1878 mightStartDrag = false;
1879#endif
1880 commitPreedit();
1881
1882 const QTextCursor oldSelection = cursor;
1883 setCursorPosition(pos);
1884 QTextLine line = currentTextLine(cursor);
1885 bool doEmit = false;
1886 if (line.isValid() && line.textLength()) {
1887 cursor.select(selection: QTextCursor::WordUnderCursor);
1888 doEmit = true;
1889 }
1890 repaintOldAndNewSelection(oldSelection);
1891
1892 cursorIsFocusIndicator = false;
1893 selectedWordOnDoubleClick = cursor;
1894
1895 trippleClickPoint = pos;
1896 trippleClickTimer.start(msec: QApplication::doubleClickInterval(), obj: q);
1897 if (doEmit) {
1898 selectionChanged();
1899#ifndef QT_NO_CLIPBOARD
1900 setClipboardSelection();
1901#endif
1902 emit q->cursorPositionChanged();
1903 }
1904 } else if (!sendMouseEventToInputContext(e, eventType: QEvent::MouseButtonDblClick, button, pos,
1905 modifiers, buttons, globalPos)) {
1906 e->ignore();
1907 }
1908}
1909
1910bool QWidgetTextControlPrivate::sendMouseEventToInputContext(
1911 QEvent *e, QEvent::Type eventType, Qt::MouseButton button, const QPointF &pos,
1912 Qt::KeyboardModifiers modifiers, Qt::MouseButtons buttons, const QPoint &globalPos)
1913{
1914 Q_UNUSED(eventType);
1915 Q_UNUSED(button);
1916 Q_UNUSED(pos);
1917 Q_UNUSED(modifiers);
1918 Q_UNUSED(buttons);
1919 Q_UNUSED(globalPos);
1920#if !defined(QT_NO_IM)
1921 Q_Q(QWidgetTextControl);
1922
1923 if (isPreediting()) {
1924 QTextLayout *layout = cursor.block().layout();
1925 int cursorPos = q->hitTest(point: pos, accuracy: Qt::FuzzyHit) - cursor.position();
1926
1927 if (cursorPos < 0 || cursorPos > layout->preeditAreaText().length())
1928 cursorPos = -1;
1929
1930 if (cursorPos >= 0) {
1931 if (eventType == QEvent::MouseButtonRelease)
1932 QGuiApplication::inputMethod()->invokeAction(a: QInputMethod::Click, cursorPosition: cursorPos);
1933
1934 e->setAccepted(true);
1935 return true;
1936 }
1937 }
1938#else
1939 Q_UNUSED(e);
1940#endif
1941 return false;
1942}
1943
1944void QWidgetTextControlPrivate::contextMenuEvent(const QPoint &screenPos, const QPointF &docPos, QWidget *contextWidget)
1945{
1946#ifdef QT_NO_CONTEXTMENU
1947 Q_UNUSED(screenPos);
1948 Q_UNUSED(docPos);
1949 Q_UNUSED(contextWidget);
1950#else
1951 Q_Q(QWidgetTextControl);
1952 QMenu *menu = q->createStandardContextMenu(pos: docPos, parent: contextWidget);
1953 if (!menu)
1954 return;
1955 menu->setAttribute(Qt::WA_DeleteOnClose);
1956
1957 if (auto *widget = qobject_cast<QWidget *>(o: parent)) {
1958 if (auto *window = widget->window()->windowHandle()) {
1959 QMenuPrivate::get(m: menu)->topData()->initialScreenIndex =
1960 QGuiApplication::screens().indexOf(t: window->screen());
1961 }
1962 }
1963
1964 menu->popup(pos: screenPos);
1965#endif
1966}
1967
1968bool QWidgetTextControlPrivate::dragEnterEvent(QEvent *e, const QMimeData *mimeData)
1969{
1970 Q_Q(QWidgetTextControl);
1971 if (!(interactionFlags & Qt::TextEditable) || !q->canInsertFromMimeData(source: mimeData)) {
1972 e->ignore();
1973 return false;
1974 }
1975
1976 dndFeedbackCursor = QTextCursor();
1977
1978 return true; // accept proposed action
1979}
1980
1981void QWidgetTextControlPrivate::dragLeaveEvent()
1982{
1983 Q_Q(QWidgetTextControl);
1984
1985 const QRectF crect = q->cursorRect(cursor: dndFeedbackCursor);
1986 dndFeedbackCursor = QTextCursor();
1987
1988 if (crect.isValid())
1989 emit q->updateRequest(rect: crect);
1990}
1991
1992bool QWidgetTextControlPrivate::dragMoveEvent(QEvent *e, const QMimeData *mimeData, const QPointF &pos)
1993{
1994 Q_Q(QWidgetTextControl);
1995 if (!(interactionFlags & Qt::TextEditable) || !q->canInsertFromMimeData(source: mimeData)) {
1996 e->ignore();
1997 return false;
1998 }
1999
2000 const int cursorPos = q->hitTest(point: pos, accuracy: Qt::FuzzyHit);
2001 if (cursorPos != -1) {
2002 QRectF crect = q->cursorRect(cursor: dndFeedbackCursor);
2003 if (crect.isValid())
2004 emit q->updateRequest(rect: crect);
2005
2006 dndFeedbackCursor = cursor;
2007 dndFeedbackCursor.setPosition(pos: cursorPos);
2008
2009 crect = q->cursorRect(cursor: dndFeedbackCursor);
2010 emit q->updateRequest(rect: crect);
2011 }
2012
2013 return true; // accept proposed action
2014}
2015
2016bool QWidgetTextControlPrivate::dropEvent(const QMimeData *mimeData, const QPointF &pos, Qt::DropAction dropAction, QObject *source)
2017{
2018 Q_Q(QWidgetTextControl);
2019 dndFeedbackCursor = QTextCursor();
2020
2021 if (!(interactionFlags & Qt::TextEditable) || !q->canInsertFromMimeData(source: mimeData))
2022 return false;
2023
2024 repaintSelection();
2025
2026 QTextCursor insertionCursor = q->cursorForPosition(pos);
2027 insertionCursor.beginEditBlock();
2028
2029 if (dropAction == Qt::MoveAction && source == contextWidget)
2030 cursor.removeSelectedText();
2031
2032 cursor = insertionCursor;
2033 q->insertFromMimeData(source: mimeData);
2034 insertionCursor.endEditBlock();
2035 q->ensureCursorVisible();
2036 return true; // accept proposed action
2037}
2038
2039void QWidgetTextControlPrivate::inputMethodEvent(QInputMethodEvent *e)
2040{
2041 Q_Q(QWidgetTextControl);
2042 if (!(interactionFlags & Qt::TextEditable) || cursor.isNull()) {
2043 e->ignore();
2044 return;
2045 }
2046 bool isGettingInput = !e->commitString().isEmpty()
2047 || e->preeditString() != cursor.block().layout()->preeditAreaText()
2048 || e->replacementLength() > 0;
2049
2050 int oldCursorPos = cursor.position();
2051
2052 cursor.beginEditBlock();
2053 if (isGettingInput) {
2054 cursor.removeSelectedText();
2055 }
2056
2057 QTextBlock block;
2058
2059 // insert commit string
2060 if (!e->commitString().isEmpty() || e->replacementLength()) {
2061 if (e->commitString().endsWith(c: QChar::LineFeed))
2062 block = cursor.block(); // Remember the block where the preedit text is
2063 QTextCursor c = cursor;
2064 c.setPosition(pos: c.position() + e->replacementStart());
2065 c.setPosition(pos: c.position() + e->replacementLength(), mode: QTextCursor::KeepAnchor);
2066 c.insertText(text: e->commitString());
2067 }
2068
2069 for (int i = 0; i < e->attributes().size(); ++i) {
2070 const QInputMethodEvent::Attribute &a = e->attributes().at(i);
2071 if (a.type == QInputMethodEvent::Selection) {
2072 QTextCursor oldCursor = cursor;
2073 int blockStart = a.start + cursor.block().position();
2074 cursor.setPosition(pos: blockStart, mode: QTextCursor::MoveAnchor);
2075 cursor.setPosition(pos: blockStart + a.length, mode: QTextCursor::KeepAnchor);
2076 q->ensureCursorVisible();
2077 repaintOldAndNewSelection(oldSelection: oldCursor);
2078 }
2079 }
2080
2081 if (!block.isValid())
2082 block = cursor.block();
2083 QTextLayout *layout = block.layout();
2084 if (isGettingInput)
2085 layout->setPreeditArea(position: cursor.position() - block.position(), text: e->preeditString());
2086 QVector<QTextLayout::FormatRange> overrides;
2087 overrides.reserve(size: e->attributes().size());
2088 const int oldPreeditCursor = preeditCursor;
2089 preeditCursor = e->preeditString().length();
2090 hideCursor = false;
2091 for (int i = 0; i < e->attributes().size(); ++i) {
2092 const QInputMethodEvent::Attribute &a = e->attributes().at(i);
2093 if (a.type == QInputMethodEvent::Cursor) {
2094 preeditCursor = a.start;
2095 hideCursor = !a.length;
2096 } else if (a.type == QInputMethodEvent::TextFormat) {
2097 QTextCharFormat f = cursor.charFormat();
2098 f.merge(other: qvariant_cast<QTextFormat>(v: a.value).toCharFormat());
2099 if (f.isValid()) {
2100 QTextLayout::FormatRange o;
2101 o.start = a.start + cursor.position() - block.position();
2102 o.length = a.length;
2103 o.format = f;
2104
2105 // Make sure list is sorted by start index
2106 QVector<QTextLayout::FormatRange>::iterator it = overrides.end();
2107 while (it != overrides.begin()) {
2108 QVector<QTextLayout::FormatRange>::iterator previous = it - 1;
2109 if (o.start >= previous->start) {
2110 overrides.insert(before: it, x: o);
2111 break;
2112 }
2113 it = previous;
2114 }
2115
2116 if (it == overrides.begin())
2117 overrides.prepend(t: o);
2118 }
2119 }
2120 }
2121
2122 if (cursor.charFormat().isValid()) {
2123 int start = cursor.position() - block.position();
2124 int end = start + e->preeditString().length();
2125
2126 QVector<QTextLayout::FormatRange>::iterator it = overrides.begin();
2127 while (it != overrides.end()) {
2128 QTextLayout::FormatRange range = *it;
2129 int rangeStart = range.start;
2130 if (rangeStart > start) {
2131 QTextLayout::FormatRange o;
2132 o.start = start;
2133 o.length = rangeStart - start;
2134 o.format = cursor.charFormat();
2135 it = overrides.insert(before: it, x: o) + 1;
2136 }
2137
2138 ++it;
2139 start = range.start + range.length;
2140 }
2141
2142 if (start < end) {
2143 QTextLayout::FormatRange o;
2144 o.start = start;
2145 o.length = end - start;
2146 o.format = cursor.charFormat();
2147 overrides.append(t: o);
2148 }
2149 }
2150 layout->setFormats(overrides);
2151
2152 cursor.endEditBlock();
2153
2154 if (cursor.d)
2155 cursor.d->setX();
2156 if (oldCursorPos != cursor.position())
2157 emit q->cursorPositionChanged();
2158 if (oldPreeditCursor != preeditCursor)
2159 emit q->microFocusChanged();
2160}
2161
2162QVariant QWidgetTextControl::inputMethodQuery(Qt::InputMethodQuery property, QVariant argument) const
2163{
2164 Q_D(const QWidgetTextControl);
2165 QTextBlock block = d->cursor.block();
2166 switch(property) {
2167 case Qt::ImCursorRectangle:
2168 return cursorRect();
2169 case Qt::ImAnchorRectangle:
2170 return d->rectForPosition(position: d->cursor.anchor());
2171 case Qt::ImFont:
2172 return QVariant(d->cursor.charFormat().font());
2173 case Qt::ImCursorPosition: {
2174 const QPointF pt = argument.toPointF();
2175 if (!pt.isNull())
2176 return QVariant(cursorForPosition(pos: pt).position() - block.position());
2177 return QVariant(d->cursor.position() - block.position()); }
2178 case Qt::ImSurroundingText:
2179 return QVariant(block.text());
2180 case Qt::ImCurrentSelection:
2181 return QVariant(d->cursor.selectedText());
2182 case Qt::ImMaximumTextLength:
2183 return QVariant(); // No limit.
2184 case Qt::ImAnchorPosition:
2185 return QVariant(d->cursor.anchor() - block.position());
2186 case Qt::ImAbsolutePosition: {
2187 const QPointF pt = argument.toPointF();
2188 if (!pt.isNull())
2189 return QVariant(cursorForPosition(pos: pt).position());
2190 return QVariant(d->cursor.position()); }
2191 case Qt::ImTextAfterCursor:
2192 {
2193 int maxLength = argument.isValid() ? argument.toInt() : 1024;
2194 QTextCursor tmpCursor = d->cursor;
2195 int localPos = d->cursor.position() - block.position();
2196 QString result = block.text().mid(position: localPos);
2197 while (result.length() < maxLength) {
2198 int currentBlock = tmpCursor.blockNumber();
2199 tmpCursor.movePosition(op: QTextCursor::NextBlock);
2200 if (tmpCursor.blockNumber() == currentBlock)
2201 break;
2202 result += QLatin1Char('\n') + tmpCursor.block().text();
2203 }
2204 return QVariant(result);
2205 }
2206 case Qt::ImTextBeforeCursor:
2207 {
2208 int maxLength = argument.isValid() ? argument.toInt() : 1024;
2209 QTextCursor tmpCursor = d->cursor;
2210 int localPos = d->cursor.position() - block.position();
2211 int numBlocks = 0;
2212 int resultLen = localPos;
2213 while (resultLen < maxLength) {
2214 int currentBlock = tmpCursor.blockNumber();
2215 tmpCursor.movePosition(op: QTextCursor::PreviousBlock);
2216 if (tmpCursor.blockNumber() == currentBlock)
2217 break;
2218 numBlocks++;
2219 resultLen += tmpCursor.block().length();
2220 }
2221 QString result;
2222 while (numBlocks) {
2223 result += tmpCursor.block().text() + QLatin1Char('\n');
2224 tmpCursor.movePosition(op: QTextCursor::NextBlock);
2225 --numBlocks;
2226 }
2227 result += block.text().midRef(position: 0, n: localPos);
2228 return QVariant(result);
2229 }
2230 default:
2231 return QVariant();
2232 }
2233}
2234
2235void QWidgetTextControl::setFocus(bool focus, Qt::FocusReason reason)
2236{
2237 QFocusEvent ev(focus ? QEvent::FocusIn : QEvent::FocusOut,
2238 reason);
2239 processEvent(e: &ev);
2240}
2241
2242void QWidgetTextControlPrivate::focusEvent(QFocusEvent *e)
2243{
2244 Q_Q(QWidgetTextControl);
2245 emit q->updateRequest(rect: q->selectionRect());
2246 if (e->gotFocus()) {
2247#ifdef QT_KEYPAD_NAVIGATION
2248 if (!QApplicationPrivate::keypadNavigationEnabled() || (hasEditFocus && (e->reason() == Qt::PopupFocusReason))) {
2249#endif
2250 cursorOn = (interactionFlags & (Qt::TextSelectableByKeyboard | Qt::TextEditable));
2251 if (interactionFlags & Qt::TextEditable) {
2252 setCursorVisible(true);
2253 }
2254#ifdef QT_KEYPAD_NAVIGATION
2255 }
2256#endif
2257 } else {
2258 setCursorVisible(false);
2259 cursorOn = false;
2260
2261 if (cursorIsFocusIndicator
2262 && e->reason() != Qt::ActiveWindowFocusReason
2263 && e->reason() != Qt::PopupFocusReason
2264 && cursor.hasSelection()) {
2265 cursor.clearSelection();
2266 }
2267 }
2268 hasFocus = e->gotFocus();
2269}
2270
2271QString QWidgetTextControlPrivate::anchorForCursor(const QTextCursor &anchorCursor) const
2272{
2273 if (anchorCursor.hasSelection()) {
2274 QTextCursor cursor = anchorCursor;
2275 if (cursor.selectionStart() != cursor.position())
2276 cursor.setPosition(pos: cursor.selectionStart());
2277 cursor.movePosition(op: QTextCursor::NextCharacter);
2278 QTextCharFormat fmt = cursor.charFormat();
2279 if (fmt.isAnchor() && fmt.hasProperty(propertyId: QTextFormat::AnchorHref))
2280 return fmt.stringProperty(propertyId: QTextFormat::AnchorHref);
2281 }
2282 return QString();
2283}
2284
2285#ifdef QT_KEYPAD_NAVIGATION
2286void QWidgetTextControlPrivate::editFocusEvent(QEvent *e)
2287{
2288 Q_Q(QWidgetTextControl);
2289
2290 if (QApplicationPrivate::keypadNavigationEnabled()) {
2291 if (e->type() == QEvent::EnterEditFocus && interactionFlags & Qt::TextEditable) {
2292 const QTextCursor oldSelection = cursor;
2293 const int oldCursorPos = cursor.position();
2294 const bool moved = cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
2295 q->ensureCursorVisible();
2296 if (moved) {
2297 if (cursor.position() != oldCursorPos)
2298 emit q->cursorPositionChanged();
2299 emit q->microFocusChanged();
2300 }
2301 selectionChanged();
2302 repaintOldAndNewSelection(oldSelection);
2303
2304 setBlinkingCursorEnabled(true);
2305 } else
2306 setBlinkingCursorEnabled(false);
2307 }
2308
2309 hasEditFocus = e->type() == QEvent::EnterEditFocus;
2310}
2311#endif
2312
2313#ifndef QT_NO_CONTEXTMENU
2314static inline void setActionIcon(QAction *action, const QString &name)
2315{
2316 const QIcon icon = QIcon::fromTheme(name);
2317 if (!icon.isNull())
2318 action->setIcon(icon);
2319}
2320
2321QMenu *QWidgetTextControl::createStandardContextMenu(const QPointF &pos, QWidget *parent)
2322{
2323 Q_D(QWidgetTextControl);
2324
2325 const bool showTextSelectionActions = d->interactionFlags & (Qt::TextEditable | Qt::TextSelectableByKeyboard | Qt::TextSelectableByMouse);
2326
2327 d->linkToCopy = QString();
2328 if (!pos.isNull())
2329 d->linkToCopy = anchorAt(pos);
2330
2331 if (d->linkToCopy.isEmpty() && !showTextSelectionActions)
2332 return nullptr;
2333
2334 QMenu *menu = new QMenu(parent);
2335 QAction *a;
2336
2337 if (d->interactionFlags & Qt::TextEditable) {
2338 a = menu->addAction(text: tr(s: "&Undo") + ACCEL_KEY(QKeySequence::Undo), receiver: this, SLOT(undo()));
2339 a->setEnabled(d->doc->isUndoAvailable());
2340 a->setObjectName(QStringLiteral("edit-undo"));
2341 setActionIcon(action: a, QStringLiteral("edit-undo"));
2342 a = menu->addAction(text: tr(s: "&Redo") + ACCEL_KEY(QKeySequence::Redo), receiver: this, SLOT(redo()));
2343 a->setEnabled(d->doc->isRedoAvailable());
2344 a->setObjectName(QStringLiteral("edit-redo"));
2345 setActionIcon(action: a, QStringLiteral("edit-redo"));
2346 menu->addSeparator();
2347
2348#ifndef QT_NO_CLIPBOARD
2349 a = menu->addAction(text: tr(s: "Cu&t") + ACCEL_KEY(QKeySequence::Cut), receiver: this, SLOT(cut()));
2350 a->setEnabled(d->cursor.hasSelection());
2351 a->setObjectName(QStringLiteral("edit-cut"));
2352 setActionIcon(action: a, QStringLiteral("edit-cut"));
2353#endif
2354 }
2355
2356#ifndef QT_NO_CLIPBOARD
2357 if (showTextSelectionActions) {
2358 a = menu->addAction(text: tr(s: "&Copy") + ACCEL_KEY(QKeySequence::Copy), receiver: this, SLOT(copy()));
2359 a->setEnabled(d->cursor.hasSelection());
2360 a->setObjectName(QStringLiteral("edit-copy"));
2361 setActionIcon(action: a, QStringLiteral("edit-copy"));
2362 }
2363
2364 if ((d->interactionFlags & Qt::LinksAccessibleByKeyboard)
2365 || (d->interactionFlags & Qt::LinksAccessibleByMouse)) {
2366
2367 a = menu->addAction(text: tr(s: "Copy &Link Location"), receiver: this, SLOT(_q_copyLink()));
2368 a->setEnabled(!d->linkToCopy.isEmpty());
2369 a->setObjectName(QStringLiteral("link-copy"));
2370 }
2371#endif // QT_NO_CLIPBOARD
2372
2373 if (d->interactionFlags & Qt::TextEditable) {
2374#ifndef QT_NO_CLIPBOARD
2375 a = menu->addAction(text: tr(s: "&Paste") + ACCEL_KEY(QKeySequence::Paste), receiver: this, SLOT(paste()));
2376 a->setEnabled(canPaste());
2377 a->setObjectName(QStringLiteral("edit-paste"));
2378 setActionIcon(action: a, QStringLiteral("edit-paste"));
2379#endif
2380 a = menu->addAction(text: tr(s: "Delete"), receiver: this, SLOT(_q_deleteSelected()));
2381 a->setEnabled(d->cursor.hasSelection());
2382 a->setObjectName(QStringLiteral("edit-delete"));
2383 setActionIcon(action: a, QStringLiteral("edit-delete"));
2384 }
2385
2386
2387 if (showTextSelectionActions) {
2388 menu->addSeparator();
2389 a = menu->addAction(text: tr(s: "Select All") + ACCEL_KEY(QKeySequence::SelectAll), receiver: this, SLOT(selectAll()));
2390 a->setEnabled(!d->doc->isEmpty());
2391 a->setObjectName(QStringLiteral("select-all"));
2392 setActionIcon(action: a, QStringLiteral("edit-select-all"));
2393 }
2394
2395 if ((d->interactionFlags & Qt::TextEditable) && QGuiApplication::styleHints()->useRtlExtensions()) {
2396 menu->addSeparator();
2397 QUnicodeControlCharacterMenu *ctrlCharacterMenu = new QUnicodeControlCharacterMenu(this, menu);
2398 menu->addMenu(menu: ctrlCharacterMenu);
2399 }
2400
2401 return menu;
2402}
2403#endif // QT_NO_CONTEXTMENU
2404
2405QTextCursor QWidgetTextControl::cursorForPosition(const QPointF &pos) const
2406{
2407 Q_D(const QWidgetTextControl);
2408 int cursorPos = hitTest(point: pos, accuracy: Qt::FuzzyHit);
2409 if (cursorPos == -1)
2410 cursorPos = 0;
2411 QTextCursor c(d->doc);
2412 c.setPosition(pos: cursorPos);
2413 return c;
2414}
2415
2416QRectF QWidgetTextControl::cursorRect(const QTextCursor &cursor) const
2417{
2418 Q_D(const QWidgetTextControl);
2419 if (cursor.isNull())
2420 return QRectF();
2421
2422 return d->rectForPosition(position: cursor.position());
2423}
2424
2425QRectF QWidgetTextControl::cursorRect() const
2426{
2427 Q_D(const QWidgetTextControl);
2428 return cursorRect(cursor: d->cursor);
2429}
2430
2431QRectF QWidgetTextControlPrivate::cursorRectPlusUnicodeDirectionMarkers(const QTextCursor &cursor) const
2432{
2433 if (cursor.isNull())
2434 return QRectF();
2435
2436 return rectForPosition(position: cursor.position()).adjusted(xp1: -4, yp1: 0, xp2: 4, yp2: 0);
2437}
2438
2439QString QWidgetTextControl::anchorAt(const QPointF &pos) const
2440{
2441 Q_D(const QWidgetTextControl);
2442 return d->doc->documentLayout()->anchorAt(pos);
2443}
2444
2445QString QWidgetTextControl::anchorAtCursor() const
2446{
2447 Q_D(const QWidgetTextControl);
2448
2449 return d->anchorForCursor(anchorCursor: d->cursor);
2450}
2451
2452QTextBlock QWidgetTextControl::blockWithMarkerAt(const QPointF &pos) const
2453{
2454 Q_D(const QWidgetTextControl);
2455 return d->doc->documentLayout()->blockWithMarkerAt(pos);
2456}
2457
2458bool QWidgetTextControl::overwriteMode() const
2459{
2460 Q_D(const QWidgetTextControl);
2461 return d->overwriteMode;
2462}
2463
2464void QWidgetTextControl::setOverwriteMode(bool overwrite)
2465{
2466 Q_D(QWidgetTextControl);
2467 d->overwriteMode = overwrite;
2468}
2469
2470int QWidgetTextControl::cursorWidth() const
2471{
2472#ifndef QT_NO_PROPERTIES
2473 Q_D(const QWidgetTextControl);
2474 return d->doc->documentLayout()->property(name: "cursorWidth").toInt();
2475#else
2476 return 1;
2477#endif
2478}
2479
2480void QWidgetTextControl::setCursorWidth(int width)
2481{
2482 Q_D(QWidgetTextControl);
2483#ifdef QT_NO_PROPERTIES
2484 Q_UNUSED(width);
2485#else
2486 if (width == -1)
2487 width = QApplication::style()->pixelMetric(metric: QStyle::PM_TextCursorWidth, option: nullptr);
2488 d->doc->documentLayout()->setProperty(name: "cursorWidth", value: width);
2489#endif
2490 d->repaintCursor();
2491}
2492
2493bool QWidgetTextControl::acceptRichText() const
2494{
2495 Q_D(const QWidgetTextControl);
2496 return d->acceptRichText;
2497}
2498
2499void QWidgetTextControl::setAcceptRichText(bool accept)
2500{
2501 Q_D(QWidgetTextControl);
2502 d->acceptRichText = accept;
2503}
2504
2505#if QT_CONFIG(textedit)
2506
2507void QWidgetTextControl::setExtraSelections(const QList<QTextEdit::ExtraSelection> &selections)
2508{
2509 Q_D(QWidgetTextControl);
2510
2511 QMultiHash<int, int> hash;
2512 for (int i = 0; i < d->extraSelections.count(); ++i) {
2513 const QAbstractTextDocumentLayout::Selection &esel = d->extraSelections.at(i);
2514 hash.insert(key: esel.cursor.anchor(), value: i);
2515 }
2516
2517 for (int i = 0; i < selections.count(); ++i) {
2518 const QTextEdit::ExtraSelection &sel = selections.at(i);
2519 const auto it = hash.constFind(key: sel.cursor.anchor());
2520 if (it != hash.cend()) {
2521 const QAbstractTextDocumentLayout::Selection &esel = d->extraSelections.at(i: it.value());
2522 if (esel.cursor.position() == sel.cursor.position()
2523 && esel.format == sel.format) {
2524 hash.erase(it);
2525 continue;
2526 }
2527 }
2528 QRectF r = selectionRect(cursor: sel.cursor);
2529 if (sel.format.boolProperty(propertyId: QTextFormat::FullWidthSelection)) {
2530 r.setLeft(0);
2531 r.setWidth(qreal(INT_MAX));
2532 }
2533 emit updateRequest(rect: r);
2534 }
2535
2536 for (QHash<int, int>::iterator it = hash.begin(); it != hash.end(); ++it) {
2537 const QAbstractTextDocumentLayout::Selection &esel = d->extraSelections.at(i: it.value());
2538 QRectF r = selectionRect(cursor: esel.cursor);
2539 if (esel.format.boolProperty(propertyId: QTextFormat::FullWidthSelection)) {
2540 r.setLeft(0);
2541 r.setWidth(qreal(INT_MAX));
2542 }
2543 emit updateRequest(rect: r);
2544 }
2545
2546 d->extraSelections.resize(size: selections.count());
2547 for (int i = 0; i < selections.count(); ++i) {
2548 d->extraSelections[i].cursor = selections.at(i).cursor;
2549 d->extraSelections[i].format = selections.at(i).format;
2550 }
2551}
2552
2553QList<QTextEdit::ExtraSelection> QWidgetTextControl::extraSelections() const
2554{
2555 Q_D(const QWidgetTextControl);
2556 QList<QTextEdit::ExtraSelection> selections;
2557 const int numExtraSelections = d->extraSelections.count();
2558 selections.reserve(size: numExtraSelections);
2559 for (int i = 0; i < numExtraSelections; ++i) {
2560 QTextEdit::ExtraSelection sel;
2561 const QAbstractTextDocumentLayout::Selection &sel2 = d->extraSelections.at(i);
2562 sel.cursor = sel2.cursor;
2563 sel.format = sel2.format;
2564 selections.append(t: sel);
2565 }
2566 return selections;
2567}
2568
2569#endif // QT_CONFIG(textedit)
2570
2571void QWidgetTextControl::setTextWidth(qreal width)
2572{
2573 Q_D(QWidgetTextControl);
2574 d->doc->setTextWidth(width);
2575}
2576
2577qreal QWidgetTextControl::textWidth() const
2578{
2579 Q_D(const QWidgetTextControl);
2580 return d->doc->textWidth();
2581}
2582
2583QSizeF QWidgetTextControl::size() const
2584{
2585 Q_D(const QWidgetTextControl);
2586 return d->doc->size();
2587}
2588
2589void QWidgetTextControl::setOpenExternalLinks(bool open)
2590{
2591 Q_D(QWidgetTextControl);
2592 d->openExternalLinks = open;
2593}
2594
2595bool QWidgetTextControl::openExternalLinks() const
2596{
2597 Q_D(const QWidgetTextControl);
2598 return d->openExternalLinks;
2599}
2600
2601bool QWidgetTextControl::ignoreUnusedNavigationEvents() const
2602{
2603 Q_D(const QWidgetTextControl);
2604 return d->ignoreUnusedNavigationEvents;
2605}
2606
2607void QWidgetTextControl::setIgnoreUnusedNavigationEvents(bool ignore)
2608{
2609 Q_D(QWidgetTextControl);
2610 d->ignoreUnusedNavigationEvents = ignore;
2611}
2612
2613void QWidgetTextControl::moveCursor(QTextCursor::MoveOperation op, QTextCursor::MoveMode mode)
2614{
2615 Q_D(QWidgetTextControl);
2616 const QTextCursor oldSelection = d->cursor;
2617 const bool moved = d->cursor.movePosition(op, mode);
2618 d->_q_updateCurrentCharFormatAndSelection();
2619 ensureCursorVisible();
2620 d->repaintOldAndNewSelection(oldSelection);
2621 if (moved)
2622 emit cursorPositionChanged();
2623}
2624
2625bool QWidgetTextControl::canPaste() const
2626{
2627#ifndef QT_NO_CLIPBOARD
2628 Q_D(const QWidgetTextControl);
2629 if (d->interactionFlags & Qt::TextEditable) {
2630 const QMimeData *md = QGuiApplication::clipboard()->mimeData();
2631 return md && canInsertFromMimeData(source: md);
2632 }
2633#endif
2634 return false;
2635}
2636
2637void QWidgetTextControl::setCursorIsFocusIndicator(bool b)
2638{
2639 Q_D(QWidgetTextControl);
2640 d->cursorIsFocusIndicator = b;
2641 d->repaintCursor();
2642}
2643
2644bool QWidgetTextControl::cursorIsFocusIndicator() const
2645{
2646 Q_D(const QWidgetTextControl);
2647 return d->cursorIsFocusIndicator;
2648}
2649
2650
2651void QWidgetTextControl::setDragEnabled(bool enabled)
2652{
2653 Q_D(QWidgetTextControl);
2654 d->dragEnabled = enabled;
2655}
2656
2657bool QWidgetTextControl::isDragEnabled() const
2658{
2659 Q_D(const QWidgetTextControl);
2660 return d->dragEnabled;
2661}
2662
2663void QWidgetTextControl::setWordSelectionEnabled(bool enabled)
2664{
2665 Q_D(QWidgetTextControl);
2666 d->wordSelectionEnabled = enabled;
2667}
2668
2669bool QWidgetTextControl::isWordSelectionEnabled() const
2670{
2671 Q_D(const QWidgetTextControl);
2672 return d->wordSelectionEnabled;
2673}
2674
2675bool QWidgetTextControl::isPreediting()
2676{
2677 return d_func()->isPreediting();
2678}
2679
2680#ifndef QT_NO_PRINTER
2681void QWidgetTextControl::print(QPagedPaintDevice *printer) const
2682{
2683 Q_D(const QWidgetTextControl);
2684 if (!printer)
2685 return;
2686 QTextDocument *tempDoc = nullptr;
2687 const QTextDocument *doc = d->doc;
2688 if (QPagedPaintDevicePrivate::get(pd: printer)->printSelectionOnly) {
2689 if (!d->cursor.hasSelection())
2690 return;
2691 tempDoc = new QTextDocument(const_cast<QTextDocument *>(doc));
2692 tempDoc->setMetaInformation(info: QTextDocument::DocumentTitle, doc->metaInformation(info: QTextDocument::DocumentTitle));
2693 tempDoc->setPageSize(doc->pageSize());
2694 tempDoc->setDefaultFont(doc->defaultFont());
2695 tempDoc->setUseDesignMetrics(doc->useDesignMetrics());
2696 QTextCursor(tempDoc).insertFragment(fragment: d->cursor.selection());
2697 doc = tempDoc;
2698
2699 // copy the custom object handlers
2700 doc->documentLayout()->d_func()->handlers = d->doc->documentLayout()->d_func()->handlers;
2701 }
2702 doc->print(printer);
2703 delete tempDoc;
2704}
2705#endif
2706
2707QMimeData *QWidgetTextControl::createMimeDataFromSelection() const
2708{
2709 Q_D(const QWidgetTextControl);
2710 const QTextDocumentFragment fragment(d->cursor);
2711 return new QTextEditMimeData(fragment);
2712}
2713
2714bool QWidgetTextControl::canInsertFromMimeData(const QMimeData *source) const
2715{
2716 Q_D(const QWidgetTextControl);
2717 if (d->acceptRichText)
2718 return (source->hasText() && !source->text().isEmpty())
2719 || source->hasHtml()
2720 || source->hasFormat(mimetype: QLatin1String("application/x-qrichtext"))
2721 || source->hasFormat(mimetype: QLatin1String("application/x-qt-richtext"));
2722 else
2723 return source->hasText() && !source->text().isEmpty();
2724}
2725
2726void QWidgetTextControl::insertFromMimeData(const QMimeData *source)
2727{
2728 Q_D(QWidgetTextControl);
2729 if (!(d->interactionFlags & Qt::TextEditable) || !source)
2730 return;
2731
2732 bool hasData = false;
2733 QTextDocumentFragment fragment;
2734#ifndef QT_NO_TEXTHTMLPARSER
2735 if (source->hasFormat(mimetype: QLatin1String("application/x-qrichtext")) && d->acceptRichText) {
2736 // x-qrichtext is always UTF-8 (taken from Qt3 since we don't use it anymore).
2737 const QString richtext = QLatin1String("<meta name=\"qrichtext\" content=\"1\" />")
2738 + QString::fromUtf8(str: source->data(mimetype: QLatin1String("application/x-qrichtext")));
2739 fragment = QTextDocumentFragment::fromHtml(html: richtext, resourceProvider: d->doc);
2740 hasData = true;
2741 } else if (source->hasHtml() && d->acceptRichText) {
2742 fragment = QTextDocumentFragment::fromHtml(html: source->html(), resourceProvider: d->doc);
2743 hasData = true;
2744 } else {
2745 QString text = source->text();
2746 if (!text.isNull()) {
2747 fragment = QTextDocumentFragment::fromPlainText(plainText: text);
2748 hasData = true;
2749 }
2750 }
2751#else
2752 fragment = QTextDocumentFragment::fromPlainText(source->text());
2753#endif // QT_NO_TEXTHTMLPARSER
2754
2755 if (hasData)
2756 d->cursor.insertFragment(fragment);
2757 ensureCursorVisible();
2758}
2759
2760bool QWidgetTextControl::findNextPrevAnchor(const QTextCursor &startCursor, bool next, QTextCursor &newAnchor)
2761{
2762 Q_D(QWidgetTextControl);
2763
2764 int anchorStart = -1;
2765 QString anchorHref;
2766 int anchorEnd = -1;
2767
2768 if (next) {
2769 const int startPos = startCursor.selectionEnd();
2770
2771 QTextBlock block = d->doc->findBlock(pos: startPos);
2772 QTextBlock::Iterator it = block.begin();
2773
2774 while (!it.atEnd() && it.fragment().position() < startPos)
2775 ++it;
2776
2777 while (block.isValid()) {
2778 anchorStart = -1;
2779
2780 // find next anchor
2781 for (; !it.atEnd(); ++it) {
2782 const QTextFragment fragment = it.fragment();
2783 const QTextCharFormat fmt = fragment.charFormat();
2784
2785 if (fmt.isAnchor() && fmt.hasProperty(propertyId: QTextFormat::AnchorHref)) {
2786 anchorStart = fragment.position();
2787 anchorHref = fmt.anchorHref();
2788 break;
2789 }
2790 }
2791
2792 if (anchorStart != -1) {
2793 anchorEnd = -1;
2794
2795 // find next non-anchor fragment
2796 for (; !it.atEnd(); ++it) {
2797 const QTextFragment fragment = it.fragment();
2798 const QTextCharFormat fmt = fragment.charFormat();
2799
2800 if (!fmt.isAnchor() || fmt.anchorHref() != anchorHref) {
2801 anchorEnd = fragment.position();
2802 break;
2803 }
2804 }
2805
2806 if (anchorEnd == -1)
2807 anchorEnd = block.position() + block.length() - 1;
2808
2809 // make found selection
2810 break;
2811 }
2812
2813 block = block.next();
2814 it = block.begin();
2815 }
2816 } else {
2817 int startPos = startCursor.selectionStart();
2818 if (startPos > 0)
2819 --startPos;
2820
2821 QTextBlock block = d->doc->findBlock(pos: startPos);
2822 QTextBlock::Iterator blockStart = block.begin();
2823 QTextBlock::Iterator it = block.end();
2824
2825 if (startPos == block.position()) {
2826 it = block.begin();
2827 } else {
2828 do {
2829 if (it == blockStart) {
2830 it = QTextBlock::Iterator();
2831 block = QTextBlock();
2832 } else {
2833 --it;
2834 }
2835 } while (!it.atEnd() && it.fragment().position() + it.fragment().length() - 1 > startPos);
2836 }
2837
2838 while (block.isValid()) {
2839 anchorStart = -1;
2840
2841 if (!it.atEnd()) {
2842 do {
2843 const QTextFragment fragment = it.fragment();
2844 const QTextCharFormat fmt = fragment.charFormat();
2845
2846 if (fmt.isAnchor() && fmt.hasProperty(propertyId: QTextFormat::AnchorHref)) {
2847 anchorStart = fragment.position() + fragment.length();
2848 anchorHref = fmt.anchorHref();
2849 break;
2850 }
2851
2852 if (it == blockStart)
2853 it = QTextBlock::Iterator();
2854 else
2855 --it;
2856 } while (!it.atEnd());
2857 }
2858
2859 if (anchorStart != -1 && !it.atEnd()) {
2860 anchorEnd = -1;
2861
2862 do {
2863 const QTextFragment fragment = it.fragment();
2864 const QTextCharFormat fmt = fragment.charFormat();
2865
2866 if (!fmt.isAnchor() || fmt.anchorHref() != anchorHref) {
2867 anchorEnd = fragment.position() + fragment.length();
2868 break;
2869 }
2870
2871 if (it == blockStart)
2872 it = QTextBlock::Iterator();
2873 else
2874 --it;
2875 } while (!it.atEnd());
2876
2877 if (anchorEnd == -1)
2878 anchorEnd = qMax(a: 0, b: block.position());
2879
2880 break;
2881 }
2882
2883 block = block.previous();
2884 it = block.end();
2885 if (it != block.begin())
2886 --it;
2887 blockStart = block.begin();
2888 }
2889
2890 }
2891
2892 if (anchorStart != -1 && anchorEnd != -1) {
2893 newAnchor = d->cursor;
2894 newAnchor.setPosition(pos: anchorStart);
2895 newAnchor.setPosition(pos: anchorEnd, mode: QTextCursor::KeepAnchor);
2896 return true;
2897 }
2898
2899 return false;
2900}
2901
2902void QWidgetTextControlPrivate::activateLinkUnderCursor(QString href)
2903{
2904 QTextCursor oldCursor = cursor;
2905
2906 if (href.isEmpty()) {
2907 QTextCursor tmp = cursor;
2908 if (tmp.selectionStart() != tmp.position())
2909 tmp.setPosition(pos: tmp.selectionStart());
2910 tmp.movePosition(op: QTextCursor::NextCharacter);
2911 href = tmp.charFormat().anchorHref();
2912 }
2913 if (href.isEmpty())
2914 return;
2915
2916 if (!cursor.hasSelection()) {
2917 QTextBlock block = cursor.block();
2918 const int cursorPos = cursor.position();
2919
2920 QTextBlock::Iterator it = block.begin();
2921 QTextBlock::Iterator linkFragment;
2922
2923 for (; !it.atEnd(); ++it) {
2924 QTextFragment fragment = it.fragment();
2925 const int fragmentPos = fragment.position();
2926 if (fragmentPos <= cursorPos &&
2927 fragmentPos + fragment.length() > cursorPos) {
2928 linkFragment = it;
2929 break;
2930 }
2931 }
2932
2933 if (!linkFragment.atEnd()) {
2934 it = linkFragment;
2935 cursor.setPosition(pos: it.fragment().position());
2936 if (it != block.begin()) {
2937 do {
2938 --it;
2939 QTextFragment fragment = it.fragment();
2940 if (fragment.charFormat().anchorHref() != href)
2941 break;
2942 cursor.setPosition(pos: fragment.position());
2943 } while (it != block.begin());
2944 }
2945
2946 for (it = linkFragment; !it.atEnd(); ++it) {
2947 QTextFragment fragment = it.fragment();
2948 if (fragment.charFormat().anchorHref() != href)
2949 break;
2950 cursor.setPosition(pos: fragment.position() + fragment.length(), mode: QTextCursor::KeepAnchor);
2951 }
2952 }
2953 }
2954
2955 if (hasFocus) {
2956 cursorIsFocusIndicator = true;
2957 } else {
2958 cursorIsFocusIndicator = false;
2959 cursor.clearSelection();
2960 }
2961 repaintOldAndNewSelection(oldSelection: oldCursor);
2962
2963#ifndef QT_NO_DESKTOPSERVICES
2964 if (openExternalLinks)
2965 QDesktopServices::openUrl(url: href);
2966 else
2967#endif
2968 emit q_func()->linkActivated(link: href);
2969}
2970
2971#ifndef QT_NO_TOOLTIP
2972void QWidgetTextControlPrivate::showToolTip(const QPoint &globalPos, const QPointF &pos, QWidget *contextWidget)
2973{
2974 const QString toolTip = q_func()->cursorForPosition(pos).charFormat().toolTip();
2975 if (toolTip.isEmpty())
2976 return;
2977 QToolTip::showText(pos: globalPos, text: toolTip, w: contextWidget);
2978}
2979#endif // QT_NO_TOOLTIP
2980
2981bool QWidgetTextControlPrivate::isPreediting() const
2982{
2983 QTextLayout *layout = cursor.block().layout();
2984 if (layout && !layout->preeditAreaText().isEmpty())
2985 return true;
2986
2987 return false;
2988}
2989
2990void QWidgetTextControlPrivate::commitPreedit()
2991{
2992 if (!isPreediting())
2993 return;
2994
2995 QGuiApplication::inputMethod()->commit();
2996
2997 if (!isPreediting())
2998 return;
2999
3000 cursor.beginEditBlock();
3001 preeditCursor = 0;
3002 QTextBlock block = cursor.block();
3003 QTextLayout *layout = block.layout();
3004 layout->setPreeditArea(position: -1, text: QString());
3005 layout->clearFormats();
3006 cursor.endEditBlock();
3007}
3008
3009bool QWidgetTextControl::setFocusToNextOrPreviousAnchor(bool next)
3010{
3011 Q_D(QWidgetTextControl);
3012
3013 if (!(d->interactionFlags & Qt::LinksAccessibleByKeyboard))
3014 return false;
3015
3016 QRectF crect = selectionRect();
3017 emit updateRequest(rect: crect);
3018
3019 // If we don't have a current anchor, we start from the start/end
3020 if (!d->cursor.hasSelection()) {
3021 d->cursor = QTextCursor(d->doc);
3022 if (next)
3023 d->cursor.movePosition(op: QTextCursor::Start);
3024 else
3025 d->cursor.movePosition(op: QTextCursor::End);
3026 }
3027
3028 QTextCursor newAnchor;
3029 if (findNextPrevAnchor(startCursor: d->cursor, next, newAnchor)) {
3030 d->cursor = newAnchor;
3031 d->cursorIsFocusIndicator = true;
3032 } else {
3033 d->cursor.clearSelection();
3034 }
3035
3036 if (d->cursor.hasSelection()) {
3037 crect = selectionRect();
3038 emit updateRequest(rect: crect);
3039 emit visibilityRequest(rect: crect);
3040 return true;
3041 } else {
3042 return false;
3043 }
3044}
3045
3046bool QWidgetTextControl::setFocusToAnchor(const QTextCursor &newCursor)
3047{
3048 Q_D(QWidgetTextControl);
3049
3050 if (!(d->interactionFlags & Qt::LinksAccessibleByKeyboard))
3051 return false;
3052
3053 // Verify that this is an anchor.
3054 const QString anchorHref = d->anchorForCursor(anchorCursor: newCursor);
3055 if (anchorHref.isEmpty())
3056 return false;
3057
3058 // and process it
3059 QRectF crect = selectionRect();
3060 emit updateRequest(rect: crect);
3061
3062 d->cursor.setPosition(pos: newCursor.selectionStart());
3063 d->cursor.setPosition(pos: newCursor.selectionEnd(), mode: QTextCursor::KeepAnchor);
3064 d->cursorIsFocusIndicator = true;
3065
3066 crect = selectionRect();
3067 emit updateRequest(rect: crect);
3068 emit visibilityRequest(rect: crect);
3069 return true;
3070}
3071
3072void QWidgetTextControl::setTextInteractionFlags(Qt::TextInteractionFlags flags)
3073{
3074 Q_D(QWidgetTextControl);
3075 if (flags == d->interactionFlags)
3076 return;
3077 d->interactionFlags = flags;
3078
3079 if (d->hasFocus)
3080 d->setCursorVisible(flags & Qt::TextEditable);
3081}
3082
3083Qt::TextInteractionFlags QWidgetTextControl::textInteractionFlags() const
3084{
3085 Q_D(const QWidgetTextControl);
3086 return d->interactionFlags;
3087}
3088
3089void QWidgetTextControl::mergeCurrentCharFormat(const QTextCharFormat &modifier)
3090{
3091 Q_D(QWidgetTextControl);
3092 d->cursor.mergeCharFormat(modifier);
3093 d->updateCurrentCharFormat();
3094}
3095
3096void QWidgetTextControl::setCurrentCharFormat(const QTextCharFormat &format)
3097{
3098 Q_D(QWidgetTextControl);
3099 d->cursor.setCharFormat(format);
3100 d->updateCurrentCharFormat();
3101}
3102
3103QTextCharFormat QWidgetTextControl::currentCharFormat() const
3104{
3105 Q_D(const QWidgetTextControl);
3106 return d->cursor.charFormat();
3107}
3108
3109void QWidgetTextControl::insertPlainText(const QString &text)
3110{
3111 Q_D(QWidgetTextControl);
3112 d->cursor.insertText(text);
3113}
3114
3115#ifndef QT_NO_TEXTHTMLPARSER
3116void QWidgetTextControl::insertHtml(const QString &text)
3117{
3118 Q_D(QWidgetTextControl);
3119 d->cursor.insertHtml(html: text);
3120}
3121#endif // QT_NO_TEXTHTMLPARSER
3122
3123QPointF QWidgetTextControl::anchorPosition(const QString &name) const
3124{
3125 Q_D(const QWidgetTextControl);
3126 if (name.isEmpty())
3127 return QPointF();
3128
3129 QRectF r;
3130 for (QTextBlock block = d->doc->begin(); block.isValid(); block = block.next()) {
3131 QTextCharFormat format = block.charFormat();
3132 if (format.isAnchor() && format.anchorNames().contains(str: name)) {
3133 r = d->rectForPosition(position: block.position());
3134 break;
3135 }
3136
3137 for (QTextBlock::Iterator it = block.begin(); !it.atEnd(); ++it) {
3138 QTextFragment fragment = it.fragment();
3139 format = fragment.charFormat();
3140 if (format.isAnchor() && format.anchorNames().contains(str: name)) {
3141 r = d->rectForPosition(position: fragment.position());
3142 block = QTextBlock();
3143 break;
3144 }
3145 }
3146 }
3147 if (!r.isValid())
3148 return QPointF();
3149 return QPointF(0, r.top());
3150}
3151
3152void QWidgetTextControl::adjustSize()
3153{
3154 Q_D(QWidgetTextControl);
3155 d->doc->adjustSize();
3156}
3157
3158bool QWidgetTextControl::find(const QString &exp, QTextDocument::FindFlags options)
3159{
3160 Q_D(QWidgetTextControl);
3161 QTextCursor search = d->doc->find(subString: exp, cursor: d->cursor, options);
3162 if (search.isNull())
3163 return false;
3164
3165 setTextCursor(cursor: search);
3166 return true;
3167}
3168
3169#ifndef QT_NO_REGEXP
3170bool QWidgetTextControl::find(const QRegExp &exp, QTextDocument::FindFlags options)
3171{
3172 Q_D(QWidgetTextControl);
3173 QTextCursor search = d->doc->find(expr: exp, cursor: d->cursor, options);
3174 if (search.isNull())
3175 return false;
3176
3177 setTextCursor(cursor: search);
3178 return true;
3179}
3180#endif
3181
3182#if QT_CONFIG(regularexpression)
3183bool QWidgetTextControl::find(const QRegularExpression &exp, QTextDocument::FindFlags options)
3184{
3185 Q_D(QWidgetTextControl);
3186 QTextCursor search = d->doc->find(expr: exp, cursor: d->cursor, options);
3187 if (search.isNull())
3188 return false;
3189
3190 setTextCursor(cursor: search);
3191 return true;
3192}
3193#endif
3194
3195QString QWidgetTextControl::toPlainText() const
3196{
3197 return document()->toPlainText();
3198}
3199
3200#ifndef QT_NO_TEXTHTMLPARSER
3201QString QWidgetTextControl::toHtml() const
3202{
3203 return document()->toHtml();
3204}
3205#endif
3206
3207#if QT_CONFIG(textmarkdownwriter)
3208QString QWidgetTextControl::toMarkdown(QTextDocument::MarkdownFeatures features) const
3209{
3210 return document()->toMarkdown(features);
3211}
3212#endif
3213
3214void QWidgetTextControlPrivate::append(const QString &text, Qt::TextFormat format)
3215{
3216 QTextCursor tmp(doc);
3217 tmp.beginEditBlock();
3218 tmp.movePosition(op: QTextCursor::End);
3219
3220 if (!doc->isEmpty())
3221 tmp.insertBlock(format: cursor.blockFormat(), charFormat: cursor.charFormat());
3222 else
3223 tmp.setCharFormat(cursor.charFormat());
3224
3225 // preserve the char format
3226 QTextCharFormat oldCharFormat = cursor.charFormat();
3227
3228#ifndef QT_NO_TEXTHTMLPARSER
3229 if (format == Qt::RichText || (format == Qt::AutoText && Qt::mightBeRichText(text))) {
3230 tmp.insertHtml(html: text);
3231 } else {
3232 tmp.insertText(text);
3233 }
3234#else
3235 Q_UNUSED(format);
3236 tmp.insertText(text);
3237#endif // QT_NO_TEXTHTMLPARSER
3238 if (!cursor.hasSelection())
3239 cursor.setCharFormat(oldCharFormat);
3240
3241 tmp.endEditBlock();
3242}
3243
3244void QWidgetTextControl::append(const QString &text)
3245{
3246 Q_D(QWidgetTextControl);
3247 d->append(text, format: Qt::AutoText);
3248}
3249
3250void QWidgetTextControl::appendHtml(const QString &html)
3251{
3252 Q_D(QWidgetTextControl);
3253 d->append(text: html, format: Qt::RichText);
3254}
3255
3256void QWidgetTextControl::appendPlainText(const QString &text)
3257{
3258 Q_D(QWidgetTextControl);
3259 d->append(text, format: Qt::PlainText);
3260}
3261
3262
3263void QWidgetTextControl::ensureCursorVisible()
3264{
3265 Q_D(QWidgetTextControl);
3266 QRectF crect = d->rectForPosition(position: d->cursor.position()).adjusted(xp1: -5, yp1: 0, xp2: 5, yp2: 0);
3267 emit visibilityRequest(rect: crect);
3268 emit microFocusChanged();
3269}
3270
3271QPalette QWidgetTextControl::palette() const
3272{
3273 Q_D(const QWidgetTextControl);
3274 return d->palette;
3275}
3276
3277void QWidgetTextControl::setPalette(const QPalette &pal)
3278{
3279 Q_D(QWidgetTextControl);
3280 d->palette = pal;
3281}
3282
3283QAbstractTextDocumentLayout::PaintContext QWidgetTextControl::getPaintContext(QWidget *widget) const
3284{
3285 Q_D(const QWidgetTextControl);
3286
3287 QAbstractTextDocumentLayout::PaintContext ctx;
3288
3289 ctx.selections = d->extraSelections;
3290 ctx.palette = d->palette;
3291#if QT_CONFIG(style_stylesheet)
3292 if (widget) {
3293 if (auto cssStyle = qt_styleSheet(style: widget->style())) {
3294 QStyleOption option;
3295 option.initFrom(w: widget);
3296 cssStyle->styleSheetPalette(w: widget, opt: &option, pal: &ctx.palette);
3297 }
3298 }
3299#endif // style_stylesheet
3300 if (d->cursorOn && d->isEnabled) {
3301 if (d->hideCursor)
3302 ctx.cursorPosition = -1;
3303 else if (d->preeditCursor != 0)
3304 ctx.cursorPosition = - (d->preeditCursor + 2);
3305 else
3306 ctx.cursorPosition = d->cursor.position();
3307 }
3308
3309 if (!d->dndFeedbackCursor.isNull())
3310 ctx.cursorPosition = d->dndFeedbackCursor.position();
3311#ifdef QT_KEYPAD_NAVIGATION
3312 if (!QApplicationPrivate::keypadNavigationEnabled() || d->hasEditFocus)
3313#endif
3314 if (d->cursor.hasSelection()) {
3315 QAbstractTextDocumentLayout::Selection selection;
3316 selection.cursor = d->cursor;
3317 if (d->cursorIsFocusIndicator) {
3318 QStyleOption opt;
3319 opt.palette = ctx.palette;
3320 QStyleHintReturnVariant ret;
3321 QStyle *style = QApplication::style();
3322 if (widget)
3323 style = widget->style();
3324 style->styleHint(stylehint: QStyle::SH_TextControl_FocusIndicatorTextCharFormat, opt: &opt, widget, returnData: &ret);
3325 selection.format = qvariant_cast<QTextFormat>(v: ret.variant).toCharFormat();
3326 } else {
3327 QPalette::ColorGroup cg = d->hasFocus ? QPalette::Active : QPalette::Inactive;
3328 selection.format.setBackground(ctx.palette.brush(cg, cr: QPalette::Highlight));
3329 selection.format.setForeground(ctx.palette.brush(cg, cr: QPalette::HighlightedText));
3330 QStyleOption opt;
3331 QStyle *style = QApplication::style();
3332 if (widget) {
3333 opt.initFrom(w: widget);
3334 style = widget->style();
3335 }
3336 if (style->styleHint(stylehint: QStyle::SH_RichText_FullWidthSelection, opt: &opt, widget))
3337 selection.format.setProperty(propertyId: QTextFormat::FullWidthSelection, value: true);
3338 }
3339 ctx.selections.append(t: selection);
3340 }
3341
3342 return ctx;
3343}
3344
3345void QWidgetTextControl::drawContents(QPainter *p, const QRectF &rect, QWidget *widget)
3346{
3347 Q_D(QWidgetTextControl);
3348 p->save();
3349 QAbstractTextDocumentLayout::PaintContext ctx = getPaintContext(widget);
3350 if (rect.isValid())
3351 p->setClipRect(rect, op: Qt::IntersectClip);
3352 ctx.clip = rect;
3353
3354 d->doc->documentLayout()->draw(painter: p, context: ctx);
3355 p->restore();
3356}
3357
3358void QWidgetTextControlPrivate::_q_copyLink()
3359{
3360#ifndef QT_NO_CLIPBOARD
3361 QMimeData *md = new QMimeData;
3362 md->setText(linkToCopy);
3363 QGuiApplication::clipboard()->setMimeData(data: md);
3364#endif
3365}
3366
3367int QWidgetTextControl::hitTest(const QPointF &point, Qt::HitTestAccuracy accuracy) const
3368{
3369 Q_D(const QWidgetTextControl);
3370 return d->doc->documentLayout()->hitTest(point, accuracy);
3371}
3372
3373QRectF QWidgetTextControl::blockBoundingRect(const QTextBlock &block) const
3374{
3375 Q_D(const QWidgetTextControl);
3376 return d->doc->documentLayout()->blockBoundingRect(block);
3377}
3378
3379#ifndef QT_NO_CONTEXTMENU
3380#define NUM_CONTROL_CHARACTERS 14
3381const struct QUnicodeControlCharacter {
3382 const char *text;
3383 ushort character;
3384} qt_controlCharacters[NUM_CONTROL_CHARACTERS] = {
3385 { QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "LRM Left-to-right mark"), .character: 0x200e },
3386 { QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "RLM Right-to-left mark"), .character: 0x200f },
3387 { QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "ZWJ Zero width joiner"), .character: 0x200d },
3388 { QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "ZWNJ Zero width non-joiner"), .character: 0x200c },
3389 { QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "ZWSP Zero width space"), .character: 0x200b },
3390 { QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "LRE Start of left-to-right embedding"), .character: 0x202a },
3391 { QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "RLE Start of right-to-left embedding"), .character: 0x202b },
3392 { QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "LRO Start of left-to-right override"), .character: 0x202d },
3393 { QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "RLO Start of right-to-left override"), .character: 0x202e },
3394 { QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "PDF Pop directional formatting"), .character: 0x202c },
3395 { QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "LRI Left-to-right isolate"), .character: 0x2066 },
3396 { QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "RLI Right-to-left isolate"), .character: 0x2067 },
3397 { QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "FSI First strong isolate"), .character: 0x2068 },
3398 { QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "PDI Pop directional isolate"), .character: 0x2069 }
3399};
3400
3401QUnicodeControlCharacterMenu::QUnicodeControlCharacterMenu(QObject *_editWidget, QWidget *parent)
3402 : QMenu(parent), editWidget(_editWidget)
3403{
3404 setTitle(tr(s: "Insert Unicode control character"));
3405 for (int i = 0; i < NUM_CONTROL_CHARACTERS; ++i) {
3406 addAction(text: tr(s: qt_controlCharacters[i].text), receiver: this, SLOT(menuActionTriggered()));
3407 }
3408}
3409
3410void QUnicodeControlCharacterMenu::menuActionTriggered()
3411{
3412 QAction *a = qobject_cast<QAction *>(object: sender());
3413 int idx = actions().indexOf(t: a);
3414 if (idx < 0 || idx >= NUM_CONTROL_CHARACTERS)
3415 return;
3416 QChar c(qt_controlCharacters[idx].character);
3417 QString str(c);
3418
3419#if QT_CONFIG(textedit)
3420 if (QTextEdit *edit = qobject_cast<QTextEdit *>(object: editWidget)) {
3421 edit->insertPlainText(text: str);
3422 return;
3423 }
3424#endif
3425 if (QWidgetTextControl *control = qobject_cast<QWidgetTextControl *>(object: editWidget)) {
3426 control->insertPlainText(text: str);
3427 }
3428#if QT_CONFIG(lineedit)
3429 if (QLineEdit *edit = qobject_cast<QLineEdit *>(object: editWidget)) {
3430 edit->insert(str);
3431 return;
3432 }
3433#endif
3434}
3435#endif // QT_NO_CONTEXTMENU
3436
3437QStringList QTextEditMimeData::formats() const
3438{
3439 if (!fragment.isEmpty())
3440 return QStringList() << QString::fromLatin1(str: "text/plain") << QString::fromLatin1(str: "text/html")
3441#ifndef QT_NO_TEXTODFWRITER
3442 << QString::fromLatin1(str: "application/vnd.oasis.opendocument.text")
3443#endif
3444 ;
3445 else
3446 return QMimeData::formats();
3447}
3448
3449QVariant QTextEditMimeData::retrieveData(const QString &mimeType, QVariant::Type type) const
3450{
3451 if (!fragment.isEmpty())
3452 setup();
3453 return QMimeData::retrieveData(mimetype: mimeType, preferredType: type);
3454}
3455
3456void QTextEditMimeData::setup() const
3457{
3458 QTextEditMimeData *that = const_cast<QTextEditMimeData *>(this);
3459#ifndef QT_NO_TEXTHTMLPARSER
3460 that->setData(mimetype: QLatin1String("text/html"), data: fragment.toHtml(encoding: "utf-8").toUtf8());
3461#endif
3462#ifndef QT_NO_TEXTODFWRITER
3463 {
3464 QBuffer buffer;
3465 QTextDocumentWriter writer(&buffer, "ODF");
3466 writer.write(fragment);
3467 buffer.close();
3468 that->setData(mimetype: QLatin1String("application/vnd.oasis.opendocument.text"), data: buffer.data());
3469 }
3470#endif
3471 that->setText(fragment.toPlainText());
3472 fragment = QTextDocumentFragment();
3473}
3474
3475QT_END_NAMESPACE
3476
3477#include "moc_qwidgettextcontrol_p.cpp"
3478
3479#endif // QT_NO_TEXTCONTROL
3480

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