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