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 "qtextdocument.h"
62#include "private/qtextdocument_p.h"
63#include "qtextlist.h"
64#include "private/qwidgettextcontrol_p.h"
65#if QT_CONFIG(style_stylesheet)
66# include "private/qstylesheetstyle_p.h"
67#endif
68#if QT_CONFIG(graphicsview)
69#include "qgraphicssceneevent.h"
70#endif
71#include "qpagedpaintdevice.h"
72#include "private/qpagedpaintdevice_p.h"
73#include "qtextdocumentwriter.h"
74#include "qstylehints.h"
75#include "private/qtextcursor_p.h"
76
77#include <qtextformat.h>
78#include <qdatetime.h>
79#include <qbuffer.h>
80#include <qapplication.h>
81#include <limits.h>
82#include <qtexttable.h>
83#include <qvariant.h>
84#include <qurl.h>
85#include <qdesktopservices.h>
86#include <qinputmethod.h>
87#include <qtooltip.h>
88#include <qstyleoption.h>
89#if QT_CONFIG(lineedit)
90#include <QtWidgets/qlineedit.h>
91#endif
92#include <QtGui/qaccessible.h>
93#include <QtCore/qmetaobject.h>
94
95#ifndef QT_NO_SHORTCUT
96#include "private/qapplication_p.h"
97#include "private/qshortcutmap_p.h"
98#include <qkeysequence.h>
99#define ACCEL_KEY(k) ((!QCoreApplication::testAttribute(Qt::AA_DontShowShortcutsInContextMenus) \
100 && QGuiApplication::styleHints()->showShortcutsInContextMenus()) \
101 && !QGuiApplicationPrivate::instance()->shortcutMap.hasShortcutForKeySequence(k) ? \
102 QLatin1Char('\t') + QKeySequence(k).toString(QKeySequence::NativeText) : QString())
103
104#else
105#define ACCEL_KEY(k) QString()
106#endif
107
108#include <algorithm>
109
110QT_BEGIN_NAMESPACE
111
112// could go into QTextCursor...
113static QTextLine currentTextLine(const QTextCursor &cursor)
114{
115 const QTextBlock block = cursor.block();
116 if (!block.isValid())
117 return QTextLine();
118
119 const QTextLayout *layout = block.layout();
120 if (!layout)
121 return QTextLine();
122
123 const int relativePos = cursor.position() - block.position();
124 return layout->lineForTextPosition(relativePos);
125}
126
127QWidgetTextControlPrivate::QWidgetTextControlPrivate()
128 : doc(0), cursorOn(false), cursorVisible(false), cursorIsFocusIndicator(false),
129#ifndef Q_OS_ANDROID
130 interactionFlags(Qt::TextEditorInteraction),
131#else
132 interactionFlags(Qt::TextEditable | Qt::TextSelectableByKeyboard),
133#endif
134 dragEnabled(true),
135#if QT_CONFIG(draganddrop)
136 mousePressed(false), mightStartDrag(false),
137#endif
138 lastSelectionPosition(0), lastSelectionAnchor(0),
139 ignoreAutomaticScrollbarAdjustement(false),
140 overwriteMode(false),
141 acceptRichText(true),
142 preeditCursor(0), hideCursor(false),
143 hasFocus(false),
144#ifdef QT_KEYPAD_NAVIGATION
145 hasEditFocus(false),
146#endif
147 isEnabled(true),
148 hadSelectionOnMousePress(false),
149 ignoreUnusedNavigationEvents(false),
150 openExternalLinks(false),
151 wordSelectionEnabled(false)
152{}
153
154bool QWidgetTextControlPrivate::cursorMoveKeyEvent(QKeyEvent *e)
155{
156#ifdef QT_NO_SHORTCUT
157 Q_UNUSED(e);
158#endif
159
160 Q_Q(QWidgetTextControl);
161 if (cursor.isNull())
162 return false;
163
164 const QTextCursor oldSelection = cursor;
165 const int oldCursorPos = cursor.position();
166
167 QTextCursor::MoveMode mode = QTextCursor::MoveAnchor;
168 QTextCursor::MoveOperation op = QTextCursor::NoMove;
169
170 if (false) {
171 }
172#ifndef QT_NO_SHORTCUT
173 if (e == QKeySequence::MoveToNextChar) {
174 op = QTextCursor::Right;
175 }
176 else if (e == QKeySequence::MoveToPreviousChar) {
177 op = QTextCursor::Left;
178 }
179 else if (e == QKeySequence::SelectNextChar) {
180 op = QTextCursor::Right;
181 mode = QTextCursor::KeepAnchor;
182 }
183 else if (e == QKeySequence::SelectPreviousChar) {
184 op = QTextCursor::Left;
185 mode = QTextCursor::KeepAnchor;
186 }
187 else if (e == QKeySequence::SelectNextWord) {
188 op = QTextCursor::WordRight;
189 mode = QTextCursor::KeepAnchor;
190 }
191 else if (e == QKeySequence::SelectPreviousWord) {
192 op = QTextCursor::WordLeft;
193 mode = QTextCursor::KeepAnchor;
194 }
195 else if (e == QKeySequence::SelectStartOfLine) {
196 op = QTextCursor::StartOfLine;
197 mode = QTextCursor::KeepAnchor;
198 }
199 else if (e == QKeySequence::SelectEndOfLine) {
200 op = QTextCursor::EndOfLine;
201 mode = QTextCursor::KeepAnchor;
202 }
203 else if (e == QKeySequence::SelectStartOfBlock) {
204 op = QTextCursor::StartOfBlock;
205 mode = QTextCursor::KeepAnchor;
206 }
207 else if (e == QKeySequence::SelectEndOfBlock) {
208 op = QTextCursor::EndOfBlock;
209 mode = QTextCursor::KeepAnchor;
210 }
211 else if (e == QKeySequence::SelectStartOfDocument) {
212 op = QTextCursor::Start;
213 mode = QTextCursor::KeepAnchor;
214 }
215 else if (e == QKeySequence::SelectEndOfDocument) {
216 op = QTextCursor::End;
217 mode = QTextCursor::KeepAnchor;
218 }
219 else if (e == QKeySequence::SelectPreviousLine) {
220 op = QTextCursor::Up;
221 mode = QTextCursor::KeepAnchor;
222 {
223 QTextBlock block = cursor.block();
224 QTextLine line = currentTextLine(cursor);
225 if (!block.previous().isValid()
226 && line.isValid()
227 && line.lineNumber() == 0)
228 op = QTextCursor::Start;
229 }
230 }
231 else if (e == QKeySequence::SelectNextLine) {
232 op = QTextCursor::Down;
233 mode = QTextCursor::KeepAnchor;
234 {
235 QTextBlock block = cursor.block();
236 QTextLine line = currentTextLine(cursor);
237 if (!block.next().isValid()
238 && line.isValid()
239 && line.lineNumber() == block.layout()->lineCount() - 1)
240 op = QTextCursor::End;
241 }
242 }
243 else if (e == QKeySequence::MoveToNextWord) {
244 op = QTextCursor::WordRight;
245 }
246 else if (e == QKeySequence::MoveToPreviousWord) {
247 op = QTextCursor::WordLeft;
248 }
249 else if (e == QKeySequence::MoveToEndOfBlock) {
250 op = QTextCursor::EndOfBlock;
251 }
252 else if (e == QKeySequence::MoveToStartOfBlock) {
253 op = QTextCursor::StartOfBlock;
254 }
255 else if (e == QKeySequence::MoveToNextLine) {
256 op = QTextCursor::Down;
257 }
258 else if (e == QKeySequence::MoveToPreviousLine) {
259 op = QTextCursor::Up;
260 }
261 else if (e == QKeySequence::MoveToStartOfLine) {
262 op = QTextCursor::StartOfLine;
263 }
264 else if (e == QKeySequence::MoveToEndOfLine) {
265 op = QTextCursor::EndOfLine;
266 }
267 else if (e == QKeySequence::MoveToStartOfDocument) {
268 op = QTextCursor::Start;
269 }
270 else if (e == QKeySequence::MoveToEndOfDocument) {
271 op = QTextCursor::End;
272 }
273#endif // QT_NO_SHORTCUT
274 else {
275 return false;
276 }
277
278// Except for pageup and pagedown, OS X has very different behavior, we don't do it all, but
279// here's the breakdown:
280// Shift still works as an anchor, but only one of the other keys can be down Ctrl (Command),
281// Alt (Option), or Meta (Control).
282// Command/Control + Left/Right -- Move to left or right of the line
283// + Up/Down -- Move to top bottom of the file. (Control doesn't move the cursor)
284// Option + Left/Right -- Move one word Left/right.
285// + Up/Down -- Begin/End of Paragraph.
286// Home/End Top/Bottom of file. (usually don't move the cursor, but will select)
287
288 bool visualNavigation = cursor.visualNavigation();
289 cursor.setVisualNavigation(true);
290 const bool moved = cursor.movePosition(op, mode);
291 cursor.setVisualNavigation(visualNavigation);
292 q->ensureCursorVisible();
293
294 bool ignoreNavigationEvents = ignoreUnusedNavigationEvents;
295 bool isNavigationEvent = e->key() == Qt::Key_Up || e->key() == Qt::Key_Down;
296
297#ifdef QT_KEYPAD_NAVIGATION
298 ignoreNavigationEvents = ignoreNavigationEvents || QApplicationPrivate::keypadNavigationEnabled();
299 isNavigationEvent = isNavigationEvent ||
300 (QApplication::navigationMode() == Qt::NavigationModeKeypadDirectional
301 && (e->key() == Qt::Key_Left || e->key() == Qt::Key_Right));
302#else
303 isNavigationEvent = isNavigationEvent || e->key() == Qt::Key_Left || e->key() == Qt::Key_Right;
304#endif
305
306 if (moved) {
307 if (cursor.position() != oldCursorPos)
308 emit q->cursorPositionChanged();
309 emit q->microFocusChanged();
310 } else if (ignoreNavigationEvents && isNavigationEvent && oldSelection.anchor() == cursor.anchor()) {
311 return false;
312 }
313
314 selectionChanged(/*forceEmitSelectionChanged =*/(mode == QTextCursor::KeepAnchor));
315
316 repaintOldAndNewSelection(oldSelection);
317
318 return true;
319}
320
321void QWidgetTextControlPrivate::updateCurrentCharFormat()
322{
323 Q_Q(QWidgetTextControl);
324
325 QTextCharFormat fmt = cursor.charFormat();
326 if (fmt == lastCharFormat)
327 return;
328 lastCharFormat = fmt;
329
330 emit q->currentCharFormatChanged(fmt);
331 emit q->microFocusChanged();
332}
333
334void QWidgetTextControlPrivate::indent()
335{
336 QTextBlockFormat blockFmt = cursor.blockFormat();
337
338 QTextList *list = cursor.currentList();
339 if (!list) {
340 QTextBlockFormat modifier;
341 modifier.setIndent(blockFmt.indent() + 1);
342 cursor.mergeBlockFormat(modifier);
343 } else {
344 QTextListFormat format = list->format();
345 format.setIndent(format.indent() + 1);
346
347 if (list->itemNumber(cursor.block()) == 1)
348 list->setFormat(format);
349 else
350 cursor.createList(format);
351 }
352}
353
354void QWidgetTextControlPrivate::outdent()
355{
356 QTextBlockFormat blockFmt = cursor.blockFormat();
357
358 QTextList *list = cursor.currentList();
359
360 if (!list) {
361 QTextBlockFormat modifier;
362 modifier.setIndent(blockFmt.indent() - 1);
363 cursor.mergeBlockFormat(modifier);
364 } else {
365 QTextListFormat listFmt = list->format();
366 listFmt.setIndent(listFmt.indent() - 1);
367 list->setFormat(listFmt);
368 }
369}
370
371void QWidgetTextControlPrivate::gotoNextTableCell()
372{
373 QTextTable *table = cursor.currentTable();
374 QTextTableCell cell = table->cellAt(cursor);
375
376 int newColumn = cell.column() + cell.columnSpan();
377 int newRow = cell.row();
378
379 if (newColumn >= table->columns()) {
380 newColumn = 0;
381 ++newRow;
382 if (newRow >= table->rows())
383 table->insertRows(table->rows(), 1);
384 }
385
386 cell = table->cellAt(newRow, newColumn);
387 cursor = cell.firstCursorPosition();
388}
389
390void QWidgetTextControlPrivate::gotoPreviousTableCell()
391{
392 QTextTable *table = cursor.currentTable();
393 QTextTableCell cell = table->cellAt(cursor);
394
395 int newColumn = cell.column() - 1;
396 int newRow = cell.row();
397
398 if (newColumn < 0) {
399 newColumn = table->columns() - 1;
400 --newRow;
401 if (newRow < 0)
402 return;
403 }
404
405 cell = table->cellAt(newRow, newColumn);
406 cursor = cell.firstCursorPosition();
407}
408
409void QWidgetTextControlPrivate::createAutoBulletList()
410{
411 cursor.beginEditBlock();
412
413 QTextBlockFormat blockFmt = cursor.blockFormat();
414
415 QTextListFormat listFmt;
416 listFmt.setStyle(QTextListFormat::ListDisc);
417 listFmt.setIndent(blockFmt.indent() + 1);
418
419 blockFmt.setIndent(0);
420 cursor.setBlockFormat(blockFmt);
421
422 cursor.createList(listFmt);
423
424 cursor.endEditBlock();
425}
426
427void QWidgetTextControlPrivate::init(Qt::TextFormat format, const QString &text, QTextDocument *document)
428{
429 Q_Q(QWidgetTextControl);
430 setContent(format, text, document);
431
432 doc->setUndoRedoEnabled(interactionFlags & Qt::TextEditable);
433 q->setCursorWidth(-1);
434}
435
436void QWidgetTextControlPrivate::setContent(Qt::TextFormat format, const QString &text, QTextDocument *document)
437{
438 Q_Q(QWidgetTextControl);
439
440 // for use when called from setPlainText. we may want to re-use the currently
441 // set char format then.
442 const QTextCharFormat charFormatForInsertion = cursor.charFormat();
443
444 bool clearDocument = true;
445 if (!doc) {
446 if (document) {
447 doc = document;
448 clearDocument = false;
449 } else {
450 palette = QApplication::palette("QWidgetTextControl");
451 doc = new QTextDocument(q);
452 }
453 _q_documentLayoutChanged();
454 cursor = QTextCursor(doc);
455
456// #### doc->documentLayout()->setPaintDevice(viewport);
457
458 QObject::connect(doc, SIGNAL(contentsChanged()), q, SLOT(_q_updateCurrentCharFormatAndSelection()));
459 QObject::connect(doc, SIGNAL(cursorPositionChanged(QTextCursor)), q, SLOT(_q_emitCursorPosChanged(QTextCursor)));
460 QObject::connect(doc, SIGNAL(documentLayoutChanged()), q, SLOT(_q_documentLayoutChanged()));
461
462 // convenience signal forwards
463 QObject::connect(doc, SIGNAL(undoAvailable(bool)), q, SIGNAL(undoAvailable(bool)));
464 QObject::connect(doc, SIGNAL(redoAvailable(bool)), q, SIGNAL(redoAvailable(bool)));
465 QObject::connect(doc, SIGNAL(modificationChanged(bool)), q, SIGNAL(modificationChanged(bool)));
466 QObject::connect(doc, SIGNAL(blockCountChanged(int)), q, SIGNAL(blockCountChanged(int)));
467 }
468
469 bool previousUndoRedoState = doc->isUndoRedoEnabled();
470 if (!document)
471 doc->setUndoRedoEnabled(false);
472
473 //Saving the index save some time.
474 static int contentsChangedIndex = QMetaMethod::fromSignal(&QTextDocument::contentsChanged).methodIndex();
475 static int textChangedIndex = QMetaMethod::fromSignal(&QWidgetTextControl::textChanged).methodIndex();
476 // avoid multiple textChanged() signals being emitted
477 QMetaObject::disconnect(doc, contentsChangedIndex, q, textChangedIndex);
478
479 if (!text.isEmpty()) {
480 // clear 'our' cursor for insertion to prevent
481 // the emission of the cursorPositionChanged() signal.
482 // instead we emit it only once at the end instead of
483 // at the end of the document after loading and when
484 // positioning the cursor again to the start of the
485 // document.
486 cursor = QTextCursor();
487 if (format == Qt::PlainText) {
488 QTextCursor formatCursor(doc);
489 // put the setPlainText and the setCharFormat into one edit block,
490 // so that the syntax highlight triggers only /once/ for the entire
491 // document, not twice.
492 formatCursor.beginEditBlock();
493 doc->setPlainText(text);
494 doc->setUndoRedoEnabled(false);
495 formatCursor.select(QTextCursor::Document);
496 formatCursor.setCharFormat(charFormatForInsertion);
497 formatCursor.endEditBlock();
498#if QT_CONFIG(textmarkdownreader)
499 } else if (format == Qt::MarkdownText) {
500 doc->setMarkdown(text);
501 doc->setUndoRedoEnabled(false);
502#endif
503 } else {
504#ifndef QT_NO_TEXTHTMLPARSER
505 doc->setHtml(text);
506#else
507 doc->setPlainText(text);
508#endif
509 doc->setUndoRedoEnabled(false);
510 }
511 cursor = QTextCursor(doc);
512 } else if (clearDocument) {
513 doc->clear();
514 }
515 cursor.setCharFormat(charFormatForInsertion);
516
517 QMetaObject::connect(doc, contentsChangedIndex, q, textChangedIndex);
518 emit q->textChanged();
519 if (!document)
520 doc->setUndoRedoEnabled(previousUndoRedoState);
521 _q_updateCurrentCharFormatAndSelection();
522 if (!document)
523 doc->setModified(false);
524
525 q->ensureCursorVisible();
526 emit q->cursorPositionChanged();
527
528 QObject::connect(doc, SIGNAL(contentsChange(int,int,int)), q, SLOT(_q_contentsChanged(int,int,int)), Qt::UniqueConnection);
529}
530
531void QWidgetTextControlPrivate::startDrag()
532{
533#if QT_CONFIG(draganddrop)
534 Q_Q(QWidgetTextControl);
535 mousePressed = false;
536 if (!contextWidget)
537 return;
538 QMimeData *data = q->createMimeDataFromSelection();
539
540 QDrag *drag = new QDrag(contextWidget);
541 drag->setMimeData(data);
542
543 Qt::DropActions actions = Qt::CopyAction;
544 Qt::DropAction action;
545 if (interactionFlags & Qt::TextEditable) {
546 actions |= Qt::MoveAction;
547 action = drag->exec(actions, Qt::MoveAction);
548 } else {
549 action = drag->exec(actions, Qt::CopyAction);
550 }
551
552 if (action == Qt::MoveAction && drag->target() != contextWidget)
553 cursor.removeSelectedText();
554#endif
555}
556
557void QWidgetTextControlPrivate::setCursorPosition(const QPointF &pos)
558{
559 Q_Q(QWidgetTextControl);
560 const int cursorPos = q->hitTest(pos, Qt::FuzzyHit);
561 if (cursorPos == -1)
562 return;
563 cursor.setPosition(cursorPos);
564}
565
566void QWidgetTextControlPrivate::setCursorPosition(int pos, QTextCursor::MoveMode mode)
567{
568 cursor.setPosition(pos, mode);
569
570 if (mode != QTextCursor::KeepAnchor) {
571 selectedWordOnDoubleClick = QTextCursor();
572 selectedBlockOnTrippleClick = QTextCursor();
573 }
574}
575
576void QWidgetTextControlPrivate::repaintCursor()
577{
578 Q_Q(QWidgetTextControl);
579 emit q->updateRequest(cursorRectPlusUnicodeDirectionMarkers(cursor));
580}
581
582void QWidgetTextControlPrivate::repaintOldAndNewSelection(const QTextCursor &oldSelection)
583{
584 Q_Q(QWidgetTextControl);
585 if (cursor.hasSelection()
586 && oldSelection.hasSelection()
587 && cursor.currentFrame() == oldSelection.currentFrame()
588 && !cursor.hasComplexSelection()
589 && !oldSelection.hasComplexSelection()
590 && cursor.anchor() == oldSelection.anchor()
591 ) {
592 QTextCursor differenceSelection(doc);
593 differenceSelection.setPosition(oldSelection.position());
594 differenceSelection.setPosition(cursor.position(), QTextCursor::KeepAnchor);
595 emit q->updateRequest(q->selectionRect(differenceSelection));
596 } else {
597 if (!oldSelection.isNull())
598 emit q->updateRequest(q->selectionRect(oldSelection) | cursorRectPlusUnicodeDirectionMarkers(oldSelection));
599 emit q->updateRequest(q->selectionRect() | cursorRectPlusUnicodeDirectionMarkers(cursor));
600 }
601}
602
603void QWidgetTextControlPrivate::selectionChanged(bool forceEmitSelectionChanged /*=false*/)
604{
605 Q_Q(QWidgetTextControl);
606 if (forceEmitSelectionChanged) {
607 emit q->selectionChanged();
608#ifndef QT_NO_ACCESSIBILITY
609 if (q->parent() && q->parent()->isWidgetType()) {
610 QAccessibleTextSelectionEvent ev(q->parent(), cursor.anchor(), cursor.position());
611 QAccessible::updateAccessibility(&ev);
612 }
613#endif
614 }
615
616 if (cursor.position() == lastSelectionPosition
617 && cursor.anchor() == lastSelectionAnchor)
618 return;
619
620 bool selectionStateChange = (cursor.hasSelection()
621 != (lastSelectionPosition != lastSelectionAnchor));
622 if (selectionStateChange)
623 emit q->copyAvailable(cursor.hasSelection());
624
625 if (!forceEmitSelectionChanged
626 && (selectionStateChange
627 || (cursor.hasSelection()
628 && (cursor.position() != lastSelectionPosition
629 || cursor.anchor() != lastSelectionAnchor)))) {
630 emit q->selectionChanged();
631#ifndef QT_NO_ACCESSIBILITY
632 if (q->parent() && q->parent()->isWidgetType()) {
633 QAccessibleTextSelectionEvent ev(q->parent(), cursor.anchor(), cursor.position());
634 QAccessible::updateAccessibility(&ev);
635 }
636#endif
637 }
638 emit q->microFocusChanged();
639 lastSelectionPosition = cursor.position();
640 lastSelectionAnchor = cursor.anchor();
641}
642
643void QWidgetTextControlPrivate::_q_updateCurrentCharFormatAndSelection()
644{
645 updateCurrentCharFormat();
646 selectionChanged();
647}
648
649#ifndef QT_NO_CLIPBOARD
650void QWidgetTextControlPrivate::setClipboardSelection()
651{
652 QClipboard *clipboard = QGuiApplication::clipboard();
653 if (!cursor.hasSelection() || !clipboard->supportsSelection())
654 return;
655 Q_Q(QWidgetTextControl);
656 QMimeData *data = q->createMimeDataFromSelection();
657 clipboard->setMimeData(data, QClipboard::Selection);
658}
659#endif
660
661void QWidgetTextControlPrivate::_q_emitCursorPosChanged(const QTextCursor &someCursor)
662{
663 Q_Q(QWidgetTextControl);
664 if (someCursor.isCopyOf(cursor)) {
665 emit q->cursorPositionChanged();
666 emit q->microFocusChanged();
667 }
668}
669
670void QWidgetTextControlPrivate::_q_contentsChanged(int from, int charsRemoved, int charsAdded)
671{
672#ifndef QT_NO_ACCESSIBILITY
673 Q_Q(QWidgetTextControl);
674
675 if (QAccessible::isActive() && q->parent() && q->parent()->isWidgetType()) {
676 QTextCursor tmp(doc);
677 tmp.setPosition(from);
678 // when setting a new text document the length is off
679 // QTBUG-32583 - characterCount is off by 1 requires the -1
680 tmp.setPosition(qMin(doc->characterCount() - 1, from + charsAdded), QTextCursor::KeepAnchor);
681 QString newText = tmp.selectedText();
682
683 // always report the right number of removed chars, but in lack of the real string use spaces
684 QString oldText = QString(charsRemoved, QLatin1Char(' '));
685
686 QAccessibleEvent *ev = 0;
687 if (charsRemoved == 0) {
688 ev = new QAccessibleTextInsertEvent(q->parent(), from, newText);
689 } else if (charsAdded == 0) {
690 ev = new QAccessibleTextRemoveEvent(q->parent(), from, oldText);
691 } else {
692 ev = new QAccessibleTextUpdateEvent(q->parent(), from, oldText, newText);
693 }
694 QAccessible::updateAccessibility(ev);
695 delete ev;
696 }
697#else
698 Q_UNUSED(from)
699 Q_UNUSED(charsRemoved)
700 Q_UNUSED(charsAdded)
701#endif
702}
703
704void QWidgetTextControlPrivate::_q_documentLayoutChanged()
705{
706 Q_Q(QWidgetTextControl);
707 QAbstractTextDocumentLayout *layout = doc->documentLayout();
708 QObject::connect(layout, SIGNAL(update(QRectF)), q, SIGNAL(updateRequest(QRectF)));
709 QObject::connect(layout, SIGNAL(updateBlock(QTextBlock)), q, SLOT(_q_updateBlock(QTextBlock)));
710 QObject::connect(layout, SIGNAL(documentSizeChanged(QSizeF)), q, SIGNAL(documentSizeChanged(QSizeF)));
711
712}
713
714void QWidgetTextControlPrivate::setCursorVisible(bool visible)
715{
716 if (cursorVisible == visible)
717 return;
718
719 cursorVisible = visible;
720 updateCursorBlinking();
721
722 if (cursorVisible)
723 connect(QGuiApplication::styleHints(), &QStyleHints::cursorFlashTimeChanged, this, &QWidgetTextControlPrivate::updateCursorBlinking);
724 else
725 disconnect(QGuiApplication::styleHints(), &QStyleHints::cursorFlashTimeChanged, this, &QWidgetTextControlPrivate::updateCursorBlinking);
726}
727
728void QWidgetTextControlPrivate::updateCursorBlinking()
729{
730 cursorBlinkTimer.stop();
731 if (cursorVisible) {
732 int flashTime = QGuiApplication::styleHints()->cursorFlashTime();
733 if (flashTime >= 2)
734 cursorBlinkTimer.start(flashTime / 2, q_func());
735 }
736
737 cursorOn = cursorVisible;
738 repaintCursor();
739}
740
741void QWidgetTextControlPrivate::extendWordwiseSelection(int suggestedNewPosition, qreal mouseXPosition)
742{
743 Q_Q(QWidgetTextControl);
744
745 // if inside the initial selected word keep that
746 if (suggestedNewPosition >= selectedWordOnDoubleClick.selectionStart()
747 && suggestedNewPosition <= selectedWordOnDoubleClick.selectionEnd()) {
748 q->setTextCursor(selectedWordOnDoubleClick);
749 return;
750 }
751
752 QTextCursor curs = selectedWordOnDoubleClick;
753 curs.setPosition(suggestedNewPosition, QTextCursor::KeepAnchor);
754
755 if (!curs.movePosition(QTextCursor::StartOfWord))
756 return;
757 const int wordStartPos = curs.position();
758
759 const int blockPos = curs.block().position();
760 const QPointF blockCoordinates = q->blockBoundingRect(curs.block()).topLeft();
761
762 QTextLine line = currentTextLine(curs);
763 if (!line.isValid())
764 return;
765
766 const qreal wordStartX = line.cursorToX(curs.position() - blockPos) + blockCoordinates.x();
767
768 if (!curs.movePosition(QTextCursor::EndOfWord))
769 return;
770 const int wordEndPos = curs.position();
771
772 const QTextLine otherLine = currentTextLine(curs);
773 if (otherLine.textStart() != line.textStart()
774 || wordEndPos == wordStartPos)
775 return;
776
777 const qreal wordEndX = line.cursorToX(curs.position() - blockPos) + blockCoordinates.x();
778
779 if (!wordSelectionEnabled && (mouseXPosition < wordStartX || mouseXPosition > wordEndX))
780 return;
781
782 if (wordSelectionEnabled) {
783 if (suggestedNewPosition < selectedWordOnDoubleClick.position()) {
784 cursor.setPosition(selectedWordOnDoubleClick.selectionEnd());
785 setCursorPosition(wordStartPos, QTextCursor::KeepAnchor);
786 } else {
787 cursor.setPosition(selectedWordOnDoubleClick.selectionStart());
788 setCursorPosition(wordEndPos, QTextCursor::KeepAnchor);
789 }
790 } else {
791 // keep the already selected word even when moving to the left
792 // (#39164)
793 if (suggestedNewPosition < selectedWordOnDoubleClick.position())
794 cursor.setPosition(selectedWordOnDoubleClick.selectionEnd());
795 else
796 cursor.setPosition(selectedWordOnDoubleClick.selectionStart());
797
798 const qreal differenceToStart = mouseXPosition - wordStartX;
799 const qreal differenceToEnd = wordEndX - mouseXPosition;
800
801 if (differenceToStart < differenceToEnd)
802 setCursorPosition(wordStartPos, QTextCursor::KeepAnchor);
803 else
804 setCursorPosition(wordEndPos, QTextCursor::KeepAnchor);
805 }
806
807 if (interactionFlags & Qt::TextSelectableByMouse) {
808#ifndef QT_NO_CLIPBOARD
809 setClipboardSelection();
810#endif
811 selectionChanged(true);
812 }
813}
814
815void QWidgetTextControlPrivate::extendBlockwiseSelection(int suggestedNewPosition)
816{
817 Q_Q(QWidgetTextControl);
818
819 // if inside the initial selected line keep that
820 if (suggestedNewPosition >= selectedBlockOnTrippleClick.selectionStart()
821 && suggestedNewPosition <= selectedBlockOnTrippleClick.selectionEnd()) {
822 q->setTextCursor(selectedBlockOnTrippleClick);
823 return;
824 }
825
826 if (suggestedNewPosition < selectedBlockOnTrippleClick.position()) {
827 cursor.setPosition(selectedBlockOnTrippleClick.selectionEnd());
828 cursor.setPosition(suggestedNewPosition, QTextCursor::KeepAnchor);
829 cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor);
830 } else {
831 cursor.setPosition(selectedBlockOnTrippleClick.selectionStart());
832 cursor.setPosition(suggestedNewPosition, QTextCursor::KeepAnchor);
833 cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
834 cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
835 }
836
837 if (interactionFlags & Qt::TextSelectableByMouse) {
838#ifndef QT_NO_CLIPBOARD
839 setClipboardSelection();
840#endif
841 selectionChanged(true);
842 }
843}
844
845void QWidgetTextControlPrivate::_q_deleteSelected()
846{
847 if (!(interactionFlags & Qt::TextEditable) || !cursor.hasSelection())
848 return;
849 cursor.removeSelectedText();
850}
851
852void QWidgetTextControl::undo()
853{
854 Q_D(QWidgetTextControl);
855 d->repaintSelection();
856 const int oldCursorPos = d->cursor.position();
857 d->doc->undo(&d->cursor);
858 if (d->cursor.position() != oldCursorPos)
859 emit cursorPositionChanged();
860 emit microFocusChanged();
861 ensureCursorVisible();
862}
863
864void QWidgetTextControl::redo()
865{
866 Q_D(QWidgetTextControl);
867 d->repaintSelection();
868 const int oldCursorPos = d->cursor.position();
869 d->doc->redo(&d->cursor);
870 if (d->cursor.position() != oldCursorPos)
871 emit cursorPositionChanged();
872 emit microFocusChanged();
873 ensureCursorVisible();
874}
875
876QWidgetTextControl::QWidgetTextControl(QObject *parent)
877 : QInputControl(QInputControl::TextEdit, *new QWidgetTextControlPrivate, parent)
878{
879 Q_D(QWidgetTextControl);
880 d->init();
881}
882
883QWidgetTextControl::QWidgetTextControl(const QString &text, QObject *parent)
884 : QInputControl(QInputControl::TextEdit, *new QWidgetTextControlPrivate, parent)
885{
886 Q_D(QWidgetTextControl);
887 d->init(Qt::RichText, text);
888}
889
890QWidgetTextControl::QWidgetTextControl(QTextDocument *doc, QObject *parent)
891 : QInputControl(QInputControl::TextEdit, *new QWidgetTextControlPrivate, parent)
892{
893 Q_D(QWidgetTextControl);
894 d->init(Qt::RichText, QString(), doc);
895}
896
897QWidgetTextControl::~QWidgetTextControl()
898{
899}
900
901void QWidgetTextControl::setDocument(QTextDocument *document)
902{
903 Q_D(QWidgetTextControl);
904 if (d->doc == document)
905 return;
906
907 d->doc->disconnect(this);
908 d->doc->documentLayout()->disconnect(this);
909 d->doc->documentLayout()->setPaintDevice(0);
910
911 if (d->doc->parent() == this)
912 delete d->doc;
913
914 d->doc = 0;
915 d->setContent(Qt::RichText, QString(), document);
916}
917
918QTextDocument *QWidgetTextControl::document() const
919{
920 Q_D(const QWidgetTextControl);
921 return d->doc;
922}
923
924void QWidgetTextControl::setTextCursor(const QTextCursor &cursor)
925{
926 Q_D(QWidgetTextControl);
927 d->cursorIsFocusIndicator = false;
928 const bool posChanged = cursor.position() != d->cursor.position();
929 const QTextCursor oldSelection = d->cursor;
930 d->cursor = cursor;
931 d->cursorOn = d->hasFocus
932 && (d->interactionFlags & (Qt::TextSelectableByKeyboard | Qt::TextEditable));
933 d->_q_updateCurrentCharFormatAndSelection();
934 ensureCursorVisible();
935 d->repaintOldAndNewSelection(oldSelection);
936 if (posChanged)
937 emit cursorPositionChanged();
938}
939
940QTextCursor QWidgetTextControl::textCursor() const
941{
942 Q_D(const QWidgetTextControl);
943 return d->cursor;
944}
945
946#ifndef QT_NO_CLIPBOARD
947
948void QWidgetTextControl::cut()
949{
950 Q_D(QWidgetTextControl);
951 if (!(d->interactionFlags & Qt::TextEditable) || !d->cursor.hasSelection())
952 return;
953 copy();
954 d->cursor.removeSelectedText();
955}
956
957void QWidgetTextControl::copy()
958{
959 Q_D(QWidgetTextControl);
960 if (!d->cursor.hasSelection())
961 return;
962 QMimeData *data = createMimeDataFromSelection();
963 QGuiApplication::clipboard()->setMimeData(data);
964}
965
966void QWidgetTextControl::paste(QClipboard::Mode mode)
967{
968 const QMimeData *md = QGuiApplication::clipboard()->mimeData(mode);
969 if (md)
970 insertFromMimeData(md);
971}
972#endif
973
974void QWidgetTextControl::clear()
975{
976 Q_D(QWidgetTextControl);
977 // clears and sets empty content
978 d->extraSelections.clear();
979 d->setContent();
980}
981
982
983void QWidgetTextControl::selectAll()
984{
985 Q_D(QWidgetTextControl);
986 const int selectionLength = qAbs(d->cursor.position() - d->cursor.anchor());
987 d->cursor.select(QTextCursor::Document);
988 d->selectionChanged(selectionLength != qAbs(d->cursor.position() - d->cursor.anchor()));
989 d->cursorIsFocusIndicator = false;
990 emit updateRequest();
991}
992
993void QWidgetTextControl::processEvent(QEvent *e, const QPointF &coordinateOffset, QWidget *contextWidget)
994{
995 QMatrix m;
996 m.translate(coordinateOffset.x(), coordinateOffset.y());
997 processEvent(e, m, contextWidget);
998}
999
1000void QWidgetTextControl::processEvent(QEvent *e, const QMatrix &matrix, QWidget *contextWidget)
1001{
1002 Q_D(QWidgetTextControl);
1003 if (d->interactionFlags == Qt::NoTextInteraction) {
1004 e->ignore();
1005 return;
1006 }
1007
1008 d->contextWidget = contextWidget;
1009
1010 if (!d->contextWidget) {
1011 switch (e->type()) {
1012#if QT_CONFIG(graphicsview)
1013 case QEvent::GraphicsSceneMouseMove:
1014 case QEvent::GraphicsSceneMousePress:
1015 case QEvent::GraphicsSceneMouseRelease:
1016 case QEvent::GraphicsSceneMouseDoubleClick:
1017 case QEvent::GraphicsSceneContextMenu:
1018 case QEvent::GraphicsSceneHoverEnter:
1019 case QEvent::GraphicsSceneHoverMove:
1020 case QEvent::GraphicsSceneHoverLeave:
1021 case QEvent::GraphicsSceneHelp:
1022 case QEvent::GraphicsSceneDragEnter:
1023 case QEvent::GraphicsSceneDragMove:
1024 case QEvent::GraphicsSceneDragLeave:
1025 case QEvent::GraphicsSceneDrop: {
1026 QGraphicsSceneEvent *ev = static_cast<QGraphicsSceneEvent *>(e);
1027 d->contextWidget = ev->widget();
1028 break;
1029 }
1030#endif // QT_CONFIG(graphicsview)
1031 default: break;
1032 };
1033 }
1034
1035 switch (e->type()) {
1036 case QEvent::KeyPress:
1037 d->keyPressEvent(static_cast<QKeyEvent *>(e));
1038 break;
1039 case QEvent::MouseButtonPress: {
1040 QMouseEvent *ev = static_cast<QMouseEvent *>(e);
1041 d->mousePressEvent(ev, ev->button(), matrix.map(ev->pos()), ev->modifiers(),
1042 ev->buttons(), ev->globalPos());
1043 break; }
1044 case QEvent::MouseMove: {
1045 QMouseEvent *ev = static_cast<QMouseEvent *>(e);
1046 d->mouseMoveEvent(ev, ev->button(), matrix.map(ev->pos()), ev->modifiers(),
1047 ev->buttons(), ev->globalPos());
1048 break; }
1049 case QEvent::MouseButtonRelease: {
1050 QMouseEvent *ev = static_cast<QMouseEvent *>(e);
1051 d->mouseReleaseEvent(ev, ev->button(), matrix.map(ev->pos()), ev->modifiers(),
1052 ev->buttons(), ev->globalPos());
1053 break; }
1054 case QEvent::MouseButtonDblClick: {
1055 QMouseEvent *ev = static_cast<QMouseEvent *>(e);
1056 d->mouseDoubleClickEvent(ev, ev->button(), matrix.map(ev->pos()), ev->modifiers(),
1057 ev->buttons(), ev->globalPos());
1058 break; }
1059 case QEvent::InputMethod:
1060 d->inputMethodEvent(static_cast<QInputMethodEvent *>(e));
1061 break;
1062#ifndef QT_NO_CONTEXTMENU
1063 case QEvent::ContextMenu: {
1064 QContextMenuEvent *ev = static_cast<QContextMenuEvent *>(e);
1065 d->contextMenuEvent(ev->globalPos(), matrix.map(ev->pos()), contextWidget);
1066 break; }
1067#endif // QT_NO_CONTEXTMENU
1068 case QEvent::FocusIn:
1069 case QEvent::FocusOut:
1070 d->focusEvent(static_cast<QFocusEvent *>(e));
1071 break;
1072
1073 case QEvent::EnabledChange:
1074 d->isEnabled = e->isAccepted();
1075 break;
1076
1077#ifndef QT_NO_TOOLTIP
1078 case QEvent::ToolTip: {
1079 QHelpEvent *ev = static_cast<QHelpEvent *>(e);
1080 d->showToolTip(ev->globalPos(), matrix.map(ev->pos()), contextWidget);
1081 break;
1082 }
1083#endif // QT_NO_TOOLTIP
1084
1085#if QT_CONFIG(draganddrop)
1086 case QEvent::DragEnter: {
1087 QDragEnterEvent *ev = static_cast<QDragEnterEvent *>(e);
1088 if (d->dragEnterEvent(e, ev->mimeData()))
1089 ev->acceptProposedAction();
1090 break;
1091 }
1092 case QEvent::DragLeave:
1093 d->dragLeaveEvent();
1094 break;
1095 case QEvent::DragMove: {
1096 QDragMoveEvent *ev = static_cast<QDragMoveEvent *>(e);
1097 if (d->dragMoveEvent(e, ev->mimeData(), matrix.map(ev->pos())))
1098 ev->acceptProposedAction();
1099 break;
1100 }
1101 case QEvent::Drop: {
1102 QDropEvent *ev = static_cast<QDropEvent *>(e);
1103 if (d->dropEvent(ev->mimeData(), matrix.map(ev->pos()), ev->dropAction(), ev->source()))
1104 ev->acceptProposedAction();
1105 break;
1106 }
1107#endif
1108
1109#if QT_CONFIG(graphicsview)
1110 case QEvent::GraphicsSceneMousePress: {
1111 QGraphicsSceneMouseEvent *ev = static_cast<QGraphicsSceneMouseEvent *>(e);
1112 d->mousePressEvent(ev, ev->button(), matrix.map(ev->pos()), ev->modifiers(), ev->buttons(),
1113 ev->screenPos());
1114 break; }
1115 case QEvent::GraphicsSceneMouseMove: {
1116 QGraphicsSceneMouseEvent *ev = static_cast<QGraphicsSceneMouseEvent *>(e);
1117 d->mouseMoveEvent(ev, ev->button(), matrix.map(ev->pos()), ev->modifiers(), ev->buttons(),
1118 ev->screenPos());
1119 break; }
1120 case QEvent::GraphicsSceneMouseRelease: {
1121 QGraphicsSceneMouseEvent *ev = static_cast<QGraphicsSceneMouseEvent *>(e);
1122 d->mouseReleaseEvent(ev, ev->button(), matrix.map(ev->pos()), ev->modifiers(), ev->buttons(),
1123 ev->screenPos());
1124 break; }
1125 case QEvent::GraphicsSceneMouseDoubleClick: {
1126 QGraphicsSceneMouseEvent *ev = static_cast<QGraphicsSceneMouseEvent *>(e);
1127 d->mouseDoubleClickEvent(ev, ev->button(), matrix.map(ev->pos()), ev->modifiers(), ev->buttons(),
1128 ev->screenPos());
1129 break; }
1130 case QEvent::GraphicsSceneContextMenu: {
1131 QGraphicsSceneContextMenuEvent *ev = static_cast<QGraphicsSceneContextMenuEvent *>(e);
1132 d->contextMenuEvent(ev->screenPos(), matrix.map(ev->pos()), contextWidget);
1133 break; }
1134
1135 case QEvent::GraphicsSceneHoverMove: {
1136 QGraphicsSceneHoverEvent *ev = static_cast<QGraphicsSceneHoverEvent *>(e);
1137 d->mouseMoveEvent(ev, Qt::NoButton, matrix.map(ev->pos()), ev->modifiers(),Qt::NoButton,
1138 ev->screenPos());
1139 break; }
1140
1141 case QEvent::GraphicsSceneDragEnter: {
1142 QGraphicsSceneDragDropEvent *ev = static_cast<QGraphicsSceneDragDropEvent *>(e);
1143 if (d->dragEnterEvent(e, ev->mimeData()))
1144 ev->acceptProposedAction();
1145 break; }
1146 case QEvent::GraphicsSceneDragLeave:
1147 d->dragLeaveEvent();
1148 break;
1149 case QEvent::GraphicsSceneDragMove: {
1150 QGraphicsSceneDragDropEvent *ev = static_cast<QGraphicsSceneDragDropEvent *>(e);
1151 if (d->dragMoveEvent(e, ev->mimeData(), matrix.map(ev->pos())))
1152 ev->acceptProposedAction();
1153 break; }
1154 case QEvent::GraphicsSceneDrop: {
1155 QGraphicsSceneDragDropEvent *ev = static_cast<QGraphicsSceneDragDropEvent *>(e);
1156 if (d->dropEvent(ev->mimeData(), matrix.map(ev->pos()), ev->dropAction(), ev->source()))
1157 ev->accept();
1158 break; }
1159#endif // QT_CONFIG(graphicsview)
1160#ifdef QT_KEYPAD_NAVIGATION
1161 case QEvent::EnterEditFocus:
1162 case QEvent::LeaveEditFocus:
1163 if (QApplicationPrivate::keypadNavigationEnabled())
1164 d->editFocusEvent(e);
1165 break;
1166#endif
1167 case QEvent::ShortcutOverride:
1168 if (d->interactionFlags & Qt::TextEditable) {
1169 QKeyEvent* ke = static_cast<QKeyEvent *>(e);
1170 if (isCommonTextEditShortcut(ke))
1171 ke->accept();
1172 }
1173 break;
1174 default:
1175 break;
1176 }
1177}
1178
1179bool QWidgetTextControl::event(QEvent *e)
1180{
1181 return QObject::event(e);
1182}
1183
1184void QWidgetTextControl::timerEvent(QTimerEvent *e)
1185{
1186 Q_D(QWidgetTextControl);
1187 if (e->timerId() == d->cursorBlinkTimer.timerId()) {
1188 d->cursorOn = !d->cursorOn;
1189
1190 if (d->cursor.hasSelection())
1191 d->cursorOn &= (QApplication::style()->styleHint(QStyle::SH_BlinkCursorWhenTextSelected)
1192 != 0);
1193
1194 d->repaintCursor();
1195 } else if (e->timerId() == d->trippleClickTimer.timerId()) {
1196 d->trippleClickTimer.stop();
1197 }
1198}
1199
1200void QWidgetTextControl::setPlainText(const QString &text)
1201{
1202 Q_D(QWidgetTextControl);
1203 d->setContent(Qt::PlainText, text);
1204}
1205
1206#if QT_CONFIG(textmarkdownreader)
1207void QWidgetTextControl::setMarkdown(const QString &text)
1208{
1209 Q_D(QWidgetTextControl);
1210 d->setContent(Qt::MarkdownText, text);
1211}
1212#endif
1213
1214void QWidgetTextControl::setHtml(const QString &text)
1215{
1216 Q_D(QWidgetTextControl);
1217 d->setContent(Qt::RichText, text);
1218}
1219
1220void QWidgetTextControlPrivate::keyPressEvent(QKeyEvent *e)
1221{
1222 Q_Q(QWidgetTextControl);
1223#ifndef QT_NO_SHORTCUT
1224 if (e == QKeySequence::SelectAll) {
1225 e->accept();
1226 q->selectAll();
1227 return;
1228 }
1229#ifndef QT_NO_CLIPBOARD
1230 else if (e == QKeySequence::Copy) {
1231 e->accept();
1232 q->copy();
1233 return;
1234 }
1235#endif
1236#endif // QT_NO_SHORTCUT
1237
1238 if (interactionFlags & Qt::TextSelectableByKeyboard
1239 && cursorMoveKeyEvent(e))
1240 goto accept;
1241
1242 if (interactionFlags & Qt::LinksAccessibleByKeyboard) {
1243 if ((e->key() == Qt::Key_Return
1244 || e->key() == Qt::Key_Enter
1245#ifdef QT_KEYPAD_NAVIGATION
1246 || e->key() == Qt::Key_Select
1247#endif
1248 )
1249 && cursor.hasSelection()) {
1250
1251 e->accept();
1252 activateLinkUnderCursor();
1253 return;
1254 }
1255 }
1256
1257 if (!(interactionFlags & Qt::TextEditable)) {
1258 e->ignore();
1259 return;
1260 }
1261
1262 if (e->key() == Qt::Key_Direction_L || e->key() == Qt::Key_Direction_R) {
1263 QTextBlockFormat fmt;
1264 fmt.setLayoutDirection((e->key() == Qt::Key_Direction_L) ? Qt::LeftToRight : Qt::RightToLeft);
1265 cursor.mergeBlockFormat(fmt);
1266 goto accept;
1267 }
1268
1269 // schedule a repaint of the region of the cursor, as when we move it we
1270 // want to make sure the old cursor disappears (not noticeable when moving
1271 // only a few pixels but noticeable when jumping between cells in tables for
1272 // example)
1273 repaintSelection();
1274
1275 if (e->key() == Qt::Key_Backspace && !(e->modifiers() & ~Qt::ShiftModifier)) {
1276 QTextBlockFormat blockFmt = cursor.blockFormat();
1277 QTextList *list = cursor.currentList();
1278 if (list && cursor.atBlockStart() && !cursor.hasSelection()) {
1279 list->remove(cursor.block());
1280 } else if (cursor.atBlockStart() && blockFmt.indent() > 0) {
1281 blockFmt.setIndent(blockFmt.indent() - 1);
1282 cursor.setBlockFormat(blockFmt);
1283 } else {
1284 QTextCursor localCursor = cursor;
1285 localCursor.deletePreviousChar();
1286 }
1287 goto accept;
1288 }
1289#ifndef QT_NO_SHORTCUT
1290 else if (e == QKeySequence::InsertParagraphSeparator) {
1291 cursor.insertBlock();
1292 e->accept();
1293 goto accept;
1294 } else if (e == QKeySequence::InsertLineSeparator) {
1295 cursor.insertText(QString(QChar::LineSeparator));
1296 e->accept();
1297 goto accept;
1298 }
1299#endif
1300 if (false) {
1301 }
1302#ifndef QT_NO_SHORTCUT
1303 else if (e == QKeySequence::Undo) {
1304 q->undo();
1305 }
1306 else if (e == QKeySequence::Redo) {
1307 q->redo();
1308 }
1309#ifndef QT_NO_CLIPBOARD
1310 else if (e == QKeySequence::Cut) {
1311 q->cut();
1312 }
1313 else if (e == QKeySequence::Paste) {
1314 QClipboard::Mode mode = QClipboard::Clipboard;
1315 if (QGuiApplication::clipboard()->supportsSelection()) {
1316 if (e->modifiers() == (Qt::CTRL | Qt::SHIFT) && e->key() == Qt::Key_Insert)
1317 mode = QClipboard::Selection;
1318 }
1319 q->paste(mode);
1320 }
1321#endif
1322 else if (e == QKeySequence::Delete) {
1323 QTextCursor localCursor = cursor;
1324 localCursor.deleteChar();
1325 } else if (e == QKeySequence::Backspace) {
1326 QTextCursor localCursor = cursor;
1327 localCursor.deletePreviousChar();
1328 }else if (e == QKeySequence::DeleteEndOfWord) {
1329 if (!cursor.hasSelection())
1330 cursor.movePosition(QTextCursor::NextWord, QTextCursor::KeepAnchor);
1331 cursor.removeSelectedText();
1332 }
1333 else if (e == QKeySequence::DeleteStartOfWord) {
1334 if (!cursor.hasSelection())
1335 cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor);
1336 cursor.removeSelectedText();
1337 }
1338 else if (e == QKeySequence::DeleteEndOfLine) {
1339 QTextBlock block = cursor.block();
1340 if (cursor.position() == block.position() + block.length() - 2)
1341 cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
1342 else
1343 cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
1344 cursor.removeSelectedText();
1345 }
1346#endif // QT_NO_SHORTCUT
1347 else {
1348 goto process;
1349 }
1350 goto accept;
1351
1352process:
1353 {
1354 if (q->isAcceptableInput(e)) {
1355 if (overwriteMode
1356 // no need to call deleteChar() if we have a selection, insertText
1357 // does it already
1358 && !cursor.hasSelection()
1359 && !cursor.atBlockEnd())
1360 cursor.deleteChar();
1361
1362 cursor.insertText(e->text());
1363 selectionChanged();
1364 } else {
1365 e->ignore();
1366 return;
1367 }
1368 }
1369
1370 accept:
1371
1372 e->accept();
1373 cursorOn = true;
1374
1375 q->ensureCursorVisible();
1376
1377 updateCurrentCharFormat();
1378}
1379
1380QVariant QWidgetTextControl::loadResource(int type, const QUrl &name)
1381{
1382 Q_UNUSED(type);
1383 Q_UNUSED(name);
1384 return QVariant();
1385}
1386
1387void QWidgetTextControlPrivate::_q_updateBlock(const QTextBlock &block)
1388{
1389 Q_Q(QWidgetTextControl);
1390 QRectF br = q->blockBoundingRect(block);
1391 br.setRight(qreal(INT_MAX)); // the block might have shrunk
1392 emit q->updateRequest(br);
1393}
1394
1395QRectF QWidgetTextControlPrivate::rectForPosition(int position) const
1396{
1397 Q_Q(const QWidgetTextControl);
1398 const QTextBlock block = doc->findBlock(position);
1399 if (!block.isValid())
1400 return QRectF();
1401 const QAbstractTextDocumentLayout *docLayout = doc->documentLayout();
1402 const QTextLayout *layout = block.layout();
1403 const QPointF layoutPos = q->blockBoundingRect(block).topLeft();
1404 int relativePos = position - block.position();
1405 if (preeditCursor != 0) {
1406 int preeditPos = layout->preeditAreaPosition();
1407 if (relativePos == preeditPos)
1408 relativePos += preeditCursor;
1409 else if (relativePos > preeditPos)
1410 relativePos += layout->preeditAreaText().length();
1411 }
1412 QTextLine line = layout->lineForTextPosition(relativePos);
1413
1414 int cursorWidth;
1415 {
1416 bool ok = false;
1417#ifndef QT_NO_PROPERTIES
1418 cursorWidth = docLayout->property("cursorWidth").toInt(&ok);
1419#endif
1420 if (!ok)
1421 cursorWidth = 1;
1422 }
1423
1424 QRectF r;
1425
1426 if (line.isValid()) {
1427 qreal x = line.cursorToX(relativePos);
1428 qreal w = 0;
1429 if (overwriteMode) {
1430 if (relativePos < line.textLength() - line.textStart())
1431 w = line.cursorToX(relativePos + 1) - x;
1432 else
1433 w = QFontMetrics(block.layout()->font()).horizontalAdvance(QLatin1Char(' ')); // in sync with QTextLine::draw()
1434 }
1435 r = QRectF(layoutPos.x() + x, layoutPos.y() + line.y(),
1436 cursorWidth + w, line.height());
1437 } else {
1438 r = QRectF(layoutPos.x(), layoutPos.y(), cursorWidth, 10); // #### correct height
1439 }
1440
1441 return r;
1442}
1443
1444namespace {
1445struct QTextFrameComparator {
1446 bool operator()(QTextFrame *frame, int position) { return frame->firstPosition() < position; }
1447 bool operator()(int position, QTextFrame *frame) { return position < frame->firstPosition(); }
1448};
1449}
1450
1451static QRectF boundingRectOfFloatsInSelection(const QTextCursor &cursor)
1452{
1453 QRectF r;
1454 QTextFrame *frame = cursor.currentFrame();
1455 const QList<QTextFrame *> children = frame->childFrames();
1456
1457 const QList<QTextFrame *>::ConstIterator firstFrame = std::lower_bound(children.constBegin(), children.constEnd(),
1458 cursor.selectionStart(), QTextFrameComparator());
1459 const QList<QTextFrame *>::ConstIterator lastFrame = std::upper_bound(children.constBegin(), children.constEnd(),
1460 cursor.selectionEnd(), QTextFrameComparator());
1461 for (QList<QTextFrame *>::ConstIterator it = firstFrame; it != lastFrame; ++it) {
1462 if ((*it)->frameFormat().position() != QTextFrameFormat::InFlow)
1463 r |= frame->document()->documentLayout()->frameBoundingRect(*it);
1464 }
1465 return r;
1466}
1467
1468QRectF QWidgetTextControl::selectionRect(const QTextCursor &cursor) const
1469{
1470 Q_D(const QWidgetTextControl);
1471
1472 QRectF r = d->rectForPosition(cursor.selectionStart());
1473
1474 if (cursor.hasComplexSelection() && cursor.currentTable()) {
1475 QTextTable *table = cursor.currentTable();
1476
1477 r = d->doc->documentLayout()->frameBoundingRect(table);
1478 /*
1479 int firstRow, numRows, firstColumn, numColumns;
1480 cursor.selectedTableCells(&firstRow, &numRows, &firstColumn, &numColumns);
1481
1482 const QTextTableCell firstCell = table->cellAt(firstRow, firstColumn);
1483 const QTextTableCell lastCell = table->cellAt(firstRow + numRows - 1, firstColumn + numColumns - 1);
1484
1485 const QAbstractTextDocumentLayout * const layout = doc->documentLayout();
1486
1487 QRectF tableSelRect = layout->blockBoundingRect(firstCell.firstCursorPosition().block());
1488
1489 for (int col = firstColumn; col < firstColumn + numColumns; ++col) {
1490 const QTextTableCell cell = table->cellAt(firstRow, col);
1491 const qreal y = layout->blockBoundingRect(cell.firstCursorPosition().block()).top();
1492
1493 tableSelRect.setTop(qMin(tableSelRect.top(), y));
1494 }
1495
1496 for (int row = firstRow; row < firstRow + numRows; ++row) {
1497 const QTextTableCell cell = table->cellAt(row, firstColumn);
1498 const qreal x = layout->blockBoundingRect(cell.firstCursorPosition().block()).left();
1499
1500 tableSelRect.setLeft(qMin(tableSelRect.left(), x));
1501 }
1502
1503 for (int col = firstColumn; col < firstColumn + numColumns; ++col) {
1504 const QTextTableCell cell = table->cellAt(firstRow + numRows - 1, col);
1505 const qreal y = layout->blockBoundingRect(cell.lastCursorPosition().block()).bottom();
1506
1507 tableSelRect.setBottom(qMax(tableSelRect.bottom(), y));
1508 }
1509
1510 for (int row = firstRow; row < firstRow + numRows; ++row) {
1511 const QTextTableCell cell = table->cellAt(row, firstColumn + numColumns - 1);
1512 const qreal x = layout->blockBoundingRect(cell.lastCursorPosition().block()).right();
1513
1514 tableSelRect.setRight(qMax(tableSelRect.right(), x));
1515 }
1516
1517 r = tableSelRect.toRect();
1518 */
1519 } else if (cursor.hasSelection()) {
1520 const int position = cursor.selectionStart();
1521 const int anchor = cursor.selectionEnd();
1522 const QTextBlock posBlock = d->doc->findBlock(position);
1523 const QTextBlock anchorBlock = d->doc->findBlock(anchor);
1524 if (posBlock == anchorBlock && posBlock.isValid() && posBlock.layout()->lineCount()) {
1525 const QTextLine posLine = posBlock.layout()->lineForTextPosition(position - posBlock.position());
1526 const QTextLine anchorLine = anchorBlock.layout()->lineForTextPosition(anchor - anchorBlock.position());
1527
1528 const int firstLine = qMin(posLine.lineNumber(), anchorLine.lineNumber());
1529 const int lastLine = qMax(posLine.lineNumber(), anchorLine.lineNumber());
1530 const QTextLayout *layout = posBlock.layout();
1531 r = QRectF();
1532 for (int i = firstLine; i <= lastLine; ++i) {
1533 r |= layout->lineAt(i).rect();
1534 r |= layout->lineAt(i).naturalTextRect(); // might be bigger in the case of wrap not enabled
1535 }
1536 r.translate(blockBoundingRect(posBlock).topLeft());
1537 } else {
1538 QRectF anchorRect = d->rectForPosition(cursor.selectionEnd());
1539 r |= anchorRect;
1540 r |= boundingRectOfFloatsInSelection(cursor);
1541 QRectF frameRect(d->doc->documentLayout()->frameBoundingRect(cursor.currentFrame()));
1542 r.setLeft(frameRect.left());
1543 r.setRight(frameRect.right());
1544 }
1545 if (r.isValid())
1546 r.adjust(-1, -1, 1, 1);
1547 }
1548
1549 return r;
1550}
1551
1552QRectF QWidgetTextControl::selectionRect() const
1553{
1554 Q_D(const QWidgetTextControl);
1555 return selectionRect(d->cursor);
1556}
1557
1558void QWidgetTextControlPrivate::mousePressEvent(QEvent *e, Qt::MouseButton button, const QPointF &pos, Qt::KeyboardModifiers modifiers,
1559 Qt::MouseButtons buttons, const QPoint &globalPos)
1560{
1561 Q_Q(QWidgetTextControl);
1562
1563 mousePressPos = pos.toPoint();
1564
1565#if QT_CONFIG(draganddrop)
1566 mightStartDrag = false;
1567#endif
1568
1569 if (sendMouseEventToInputContext(
1570 e, QEvent::MouseButtonPress, button, pos, modifiers, buttons, globalPos)) {
1571 return;
1572 }
1573
1574 if (interactionFlags & Qt::LinksAccessibleByMouse) {
1575 anchorOnMousePress = q->anchorAt(pos);
1576
1577 if (cursorIsFocusIndicator) {
1578 cursorIsFocusIndicator = false;
1579 repaintSelection();
1580 cursor.clearSelection();
1581 }
1582 }
1583 if (!(button & Qt::LeftButton) ||
1584 !((interactionFlags & Qt::TextSelectableByMouse) || (interactionFlags & Qt::TextEditable))) {
1585 e->ignore();
1586 return;
1587 }
1588 bool wasValid = blockWithMarkerUnderMouse.isValid();
1589 blockWithMarkerUnderMouse = q->blockWithMarkerAt(pos);
1590 if (wasValid != blockWithMarkerUnderMouse.isValid())
1591 emit q->blockMarkerHovered(blockWithMarkerUnderMouse);
1592
1593
1594 cursorIsFocusIndicator = false;
1595 const QTextCursor oldSelection = cursor;
1596 const int oldCursorPos = cursor.position();
1597
1598 mousePressed = (interactionFlags & Qt::TextSelectableByMouse);
1599
1600 commitPreedit();
1601
1602 if (trippleClickTimer.isActive()
1603 && ((pos - trippleClickPoint).toPoint().manhattanLength() < QApplication::startDragDistance())) {
1604
1605 cursor.movePosition(QTextCursor::StartOfBlock);
1606 cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
1607 cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
1608 selectedBlockOnTrippleClick = cursor;
1609
1610 anchorOnMousePress = QString();
1611 blockWithMarkerUnderMouse = QTextBlock();
1612 emit q->blockMarkerHovered(blockWithMarkerUnderMouse);
1613
1614 trippleClickTimer.stop();
1615 } else {
1616 int cursorPos = q->hitTest(pos, Qt::FuzzyHit);
1617 if (cursorPos == -1) {
1618 e->ignore();
1619 return;
1620 }
1621
1622 if (modifiers == Qt::ShiftModifier && (interactionFlags & Qt::TextSelectableByMouse)) {
1623 if (wordSelectionEnabled && !selectedWordOnDoubleClick.hasSelection()) {
1624 selectedWordOnDoubleClick = cursor;
1625 selectedWordOnDoubleClick.select(QTextCursor::WordUnderCursor);
1626 }
1627
1628 if (selectedBlockOnTrippleClick.hasSelection())
1629 extendBlockwiseSelection(cursorPos);
1630 else if (selectedWordOnDoubleClick.hasSelection())
1631 extendWordwiseSelection(cursorPos, pos.x());
1632 else if (!wordSelectionEnabled)
1633 setCursorPosition(cursorPos, QTextCursor::KeepAnchor);
1634 } else {
1635
1636 if (dragEnabled
1637 && cursor.hasSelection()
1638 && !cursorIsFocusIndicator
1639 && cursorPos >= cursor.selectionStart()
1640 && cursorPos <= cursor.selectionEnd()
1641 && q->hitTest(pos, Qt::ExactHit) != -1) {
1642#if QT_CONFIG(draganddrop)
1643 mightStartDrag = true;
1644#endif
1645 return;
1646 }
1647
1648 setCursorPosition(cursorPos);
1649 }
1650 }
1651
1652 if (interactionFlags & Qt::TextEditable) {
1653 q->ensureCursorVisible();
1654 if (cursor.position() != oldCursorPos)
1655 emit q->cursorPositionChanged();
1656 _q_updateCurrentCharFormatAndSelection();
1657 } else {
1658 if (cursor.position() != oldCursorPos) {
1659 emit q->cursorPositionChanged();
1660 emit q->microFocusChanged();
1661 }
1662 selectionChanged();
1663 }
1664 repaintOldAndNewSelection(oldSelection);
1665 hadSelectionOnMousePress = cursor.hasSelection();
1666}
1667
1668void QWidgetTextControlPrivate::mouseMoveEvent(QEvent *e, Qt::MouseButton button, const QPointF &mousePos, Qt::KeyboardModifiers modifiers,
1669 Qt::MouseButtons buttons, const QPoint &globalPos)
1670{
1671 Q_Q(QWidgetTextControl);
1672
1673 if (interactionFlags & Qt::LinksAccessibleByMouse) {
1674 QString anchor = q->anchorAt(mousePos);
1675 if (anchor != highlightedAnchor) {
1676 highlightedAnchor = anchor;
1677 emit q->linkHovered(anchor);
1678 }
1679 }
1680
1681 if (buttons & Qt::LeftButton) {
1682 const bool editable = interactionFlags & Qt::TextEditable;
1683
1684 if (!(mousePressed
1685 || editable
1686 || mightStartDrag
1687 || selectedWordOnDoubleClick.hasSelection()
1688 || selectedBlockOnTrippleClick.hasSelection()))
1689 return;
1690
1691 const QTextCursor oldSelection = cursor;
1692 const int oldCursorPos = cursor.position();
1693
1694 if (mightStartDrag) {
1695 if ((mousePos.toPoint() - mousePressPos).manhattanLength() > QApplication::startDragDistance())
1696 startDrag();
1697 return;
1698 }
1699
1700 const qreal mouseX = qreal(mousePos.x());
1701
1702 int newCursorPos = q->hitTest(mousePos, Qt::FuzzyHit);
1703
1704 if (isPreediting()) {
1705 // note: oldCursorPos not including preedit
1706 int selectionStartPos = q->hitTest(mousePressPos, Qt::FuzzyHit);
1707
1708 if (newCursorPos != selectionStartPos) {
1709 commitPreedit();
1710 // commit invalidates positions
1711 newCursorPos = q->hitTest(mousePos, Qt::FuzzyHit);
1712 selectionStartPos = q->hitTest(mousePressPos, Qt::FuzzyHit);
1713 setCursorPosition(selectionStartPos);
1714 }
1715 }
1716
1717 if (newCursorPos == -1)
1718 return;
1719
1720 if (mousePressed && wordSelectionEnabled && !selectedWordOnDoubleClick.hasSelection()) {
1721 selectedWordOnDoubleClick = cursor;
1722 selectedWordOnDoubleClick.select(QTextCursor::WordUnderCursor);
1723 }
1724
1725 if (selectedBlockOnTrippleClick.hasSelection())
1726 extendBlockwiseSelection(newCursorPos);
1727 else if (selectedWordOnDoubleClick.hasSelection())
1728 extendWordwiseSelection(newCursorPos, mouseX);
1729 else if (mousePressed && !isPreediting())
1730 setCursorPosition(newCursorPos, QTextCursor::KeepAnchor);
1731
1732 if (interactionFlags & Qt::TextEditable) {
1733 // don't call ensureVisible for the visible cursor to avoid jumping
1734 // scrollbars. the autoscrolling ensures smooth scrolling if necessary.
1735 //q->ensureCursorVisible();
1736 if (cursor.position() != oldCursorPos)
1737 emit q->cursorPositionChanged();
1738 _q_updateCurrentCharFormatAndSelection();
1739#ifndef QT_NO_IM
1740 if (contextWidget)
1741 QGuiApplication::inputMethod()->update(Qt::ImQueryInput);
1742#endif //QT_NO_IM
1743 } else {
1744 //emit q->visibilityRequest(QRectF(mousePos, QSizeF(1, 1)));
1745 if (cursor.position() != oldCursorPos) {
1746 emit q->cursorPositionChanged();
1747 emit q->microFocusChanged();
1748 }
1749 }
1750 selectionChanged(true);
1751 repaintOldAndNewSelection(oldSelection);
1752 } else {
1753 bool wasValid = blockWithMarkerUnderMouse.isValid();
1754 blockWithMarkerUnderMouse = q->blockWithMarkerAt(mousePos);
1755 if (wasValid != blockWithMarkerUnderMouse.isValid())
1756 emit q->blockMarkerHovered(blockWithMarkerUnderMouse);
1757 }
1758
1759 sendMouseEventToInputContext(e, QEvent::MouseMove, button, mousePos, modifiers, buttons, globalPos);
1760}
1761
1762void QWidgetTextControlPrivate::mouseReleaseEvent(QEvent *e, Qt::MouseButton button, const QPointF &pos, Qt::KeyboardModifiers modifiers,
1763 Qt::MouseButtons buttons, const QPoint &globalPos)
1764{
1765 Q_Q(QWidgetTextControl);
1766
1767 const QTextCursor oldSelection = cursor;
1768 if (sendMouseEventToInputContext(
1769 e, QEvent::MouseButtonRelease, button, pos, modifiers, buttons, globalPos)) {
1770 repaintOldAndNewSelection(oldSelection);
1771 return;
1772 }
1773
1774 const int oldCursorPos = cursor.position();
1775
1776#if QT_CONFIG(draganddrop)
1777 if (mightStartDrag && (button & Qt::LeftButton)) {
1778 mousePressed = false;
1779 setCursorPosition(pos);
1780 cursor.clearSelection();
1781 selectionChanged();
1782 }
1783#endif
1784 if (mousePressed) {
1785 mousePressed = false;
1786#ifndef QT_NO_CLIPBOARD
1787 setClipboardSelection();
1788 selectionChanged(true);
1789 } else if (button == Qt::MidButton
1790 && (interactionFlags & Qt::TextEditable)
1791 && QGuiApplication::clipboard()->supportsSelection()) {
1792 setCursorPosition(pos);
1793 const QMimeData *md = QGuiApplication::clipboard()->mimeData(QClipboard::Selection);
1794 if (md)
1795 q->insertFromMimeData(md);
1796#endif
1797 }
1798
1799 repaintOldAndNewSelection(oldSelection);
1800
1801 if (cursor.position() != oldCursorPos) {
1802 emit q->cursorPositionChanged();
1803 emit q->microFocusChanged();
1804 }
1805
1806 // toggle any checkbox that the user clicks
1807 if ((interactionFlags & Qt::TextEditable) && (button & Qt::LeftButton) &&
1808 (blockWithMarkerUnderMouse.isValid()) && !cursor.hasSelection()) {
1809 QTextBlock markerBlock = q->blockWithMarkerAt(pos);
1810 if (markerBlock == blockWithMarkerUnderMouse) {
1811 auto fmt = blockWithMarkerUnderMouse.blockFormat();
1812 switch (fmt.marker()) {
1813 case QTextBlockFormat::Unchecked :
1814 fmt.setMarker(QTextBlockFormat::Checked);
1815 break;
1816 case QTextBlockFormat::Checked:
1817 fmt.setMarker(QTextBlockFormat::Unchecked);
1818 break;
1819 default:
1820 break;
1821 }
1822 cursor.setBlockFormat(fmt);
1823 }
1824 }
1825
1826 if (interactionFlags & Qt::LinksAccessibleByMouse) {
1827 if (!(button & Qt::LeftButton))
1828 return;
1829
1830 const QString anchor = q->anchorAt(pos);
1831
1832 if (anchor.isEmpty())
1833 return;
1834
1835 if (!cursor.hasSelection()
1836 || (anchor == anchorOnMousePress && hadSelectionOnMousePress)) {
1837
1838 const int anchorPos = q->hitTest(pos, Qt::ExactHit);
1839 if (anchorPos != -1) {
1840 cursor.setPosition(anchorPos);
1841
1842 QString anchor = anchorOnMousePress;
1843 anchorOnMousePress = QString();
1844 activateLinkUnderCursor(anchor);
1845 }
1846 }
1847 }
1848}
1849
1850void QWidgetTextControlPrivate::mouseDoubleClickEvent(QEvent *e, Qt::MouseButton button, const QPointF &pos,
1851 Qt::KeyboardModifiers modifiers, Qt::MouseButtons buttons,
1852 const QPoint &globalPos)
1853{
1854 Q_Q(QWidgetTextControl);
1855
1856 if (button == Qt::LeftButton
1857 && (interactionFlags & Qt::TextSelectableByMouse)) {
1858
1859#if QT_CONFIG(draganddrop)
1860 mightStartDrag = false;
1861#endif
1862 commitPreedit();
1863
1864 const QTextCursor oldSelection = cursor;
1865 setCursorPosition(pos);
1866 QTextLine line = currentTextLine(cursor);
1867 bool doEmit = false;
1868 if (line.isValid() && line.textLength()) {
1869 cursor.select(QTextCursor::WordUnderCursor);
1870 doEmit = true;
1871 }
1872 repaintOldAndNewSelection(oldSelection);
1873
1874 cursorIsFocusIndicator = false;
1875 selectedWordOnDoubleClick = cursor;
1876
1877 trippleClickPoint = pos;
1878 trippleClickTimer.start(QApplication::doubleClickInterval(), q);
1879 if (doEmit) {
1880 selectionChanged();
1881#ifndef QT_NO_CLIPBOARD
1882 setClipboardSelection();
1883#endif
1884 emit q->cursorPositionChanged();
1885 }
1886 } else if (!sendMouseEventToInputContext(e, QEvent::MouseButtonDblClick, button, pos,
1887 modifiers, buttons, globalPos)) {
1888 e->ignore();
1889 }
1890}
1891
1892bool QWidgetTextControlPrivate::sendMouseEventToInputContext(
1893 QEvent *e, QEvent::Type eventType, Qt::MouseButton button, const QPointF &pos,
1894 Qt::KeyboardModifiers modifiers, Qt::MouseButtons buttons, const QPoint &globalPos)
1895{
1896 Q_UNUSED(eventType);
1897 Q_UNUSED(button);
1898 Q_UNUSED(pos);
1899 Q_UNUSED(modifiers);
1900 Q_UNUSED(buttons);
1901 Q_UNUSED(globalPos);
1902#if !defined(QT_NO_IM)
1903 Q_Q(QWidgetTextControl);
1904
1905 if (isPreediting()) {
1906 QTextLayout *layout = cursor.block().layout();
1907 int cursorPos = q->hitTest(pos, Qt::FuzzyHit) - cursor.position();
1908
1909 if (cursorPos < 0 || cursorPos > layout->preeditAreaText().length())
1910 cursorPos = -1;
1911
1912 if (cursorPos >= 0) {
1913 if (eventType == QEvent::MouseButtonRelease)
1914 QGuiApplication::inputMethod()->invokeAction(QInputMethod::Click, cursorPos);
1915
1916 e->setAccepted(true);
1917 return true;
1918 }
1919 }
1920#else
1921 Q_UNUSED(e);
1922#endif
1923 return false;
1924}
1925
1926void QWidgetTextControlPrivate::contextMenuEvent(const QPoint &screenPos, const QPointF &docPos, QWidget *contextWidget)
1927{
1928#ifdef QT_NO_CONTEXTMENU
1929 Q_UNUSED(screenPos);
1930 Q_UNUSED(docPos);
1931 Q_UNUSED(contextWidget);
1932#else
1933 Q_Q(QWidgetTextControl);
1934 QMenu *menu = q->createStandardContextMenu(docPos, contextWidget);
1935 if (!menu)
1936 return;
1937 menu->setAttribute(Qt::WA_DeleteOnClose);
1938 menu->popup(screenPos);
1939#endif
1940}
1941
1942bool QWidgetTextControlPrivate::dragEnterEvent(QEvent *e, const QMimeData *mimeData)
1943{
1944 Q_Q(QWidgetTextControl);
1945 if (!(interactionFlags & Qt::TextEditable) || !q->canInsertFromMimeData(mimeData)) {
1946 e->ignore();
1947 return false;
1948 }
1949
1950 dndFeedbackCursor = QTextCursor();
1951
1952 return true; // accept proposed action
1953}
1954
1955void QWidgetTextControlPrivate::dragLeaveEvent()
1956{
1957 Q_Q(QWidgetTextControl);
1958
1959 const QRectF crect = q->cursorRect(dndFeedbackCursor);
1960 dndFeedbackCursor = QTextCursor();
1961
1962 if (crect.isValid())
1963 emit q->updateRequest(crect);
1964}
1965
1966bool QWidgetTextControlPrivate::dragMoveEvent(QEvent *e, const QMimeData *mimeData, const QPointF &pos)
1967{
1968 Q_Q(QWidgetTextControl);
1969 if (!(interactionFlags & Qt::TextEditable) || !q->canInsertFromMimeData(mimeData)) {
1970 e->ignore();
1971 return false;
1972 }
1973
1974 const int cursorPos = q->hitTest(pos, Qt::FuzzyHit);
1975 if (cursorPos != -1) {
1976 QRectF crect = q->cursorRect(dndFeedbackCursor);
1977 if (crect.isValid())
1978 emit q->updateRequest(crect);
1979
1980 dndFeedbackCursor = cursor;
1981 dndFeedbackCursor.setPosition(cursorPos);
1982
1983 crect = q->cursorRect(dndFeedbackCursor);
1984 emit q->updateRequest(crect);
1985 }
1986
1987 return true; // accept proposed action
1988}
1989
1990bool QWidgetTextControlPrivate::dropEvent(const QMimeData *mimeData, const QPointF &pos, Qt::DropAction dropAction, QObject *source)
1991{
1992 Q_Q(QWidgetTextControl);
1993 dndFeedbackCursor = QTextCursor();
1994
1995 if (!(interactionFlags & Qt::TextEditable) || !q->canInsertFromMimeData(mimeData))
1996 return false;
1997
1998 repaintSelection();
1999
2000 QTextCursor insertionCursor = q->cursorForPosition(pos);
2001 insertionCursor.beginEditBlock();
2002
2003 if (dropAction == Qt::MoveAction && source == contextWidget)
2004 cursor.removeSelectedText();
2005
2006 cursor = insertionCursor;
2007 q->insertFromMimeData(mimeData);
2008 insertionCursor.endEditBlock();
2009 q->ensureCursorVisible();
2010 return true; // accept proposed action
2011}
2012
2013void QWidgetTextControlPrivate::inputMethodEvent(QInputMethodEvent *e)
2014{
2015 Q_Q(QWidgetTextControl);
2016 if (!(interactionFlags & Qt::TextEditable) || cursor.isNull()) {
2017 e->ignore();
2018 return;
2019 }
2020 bool isGettingInput = !e->commitString().isEmpty()
2021 || e->preeditString() != cursor.block().layout()->preeditAreaText()
2022 || e->replacementLength() > 0;
2023
2024 int oldCursorPos = cursor.position();
2025
2026 cursor.beginEditBlock();
2027 if (isGettingInput) {
2028 cursor.removeSelectedText();
2029 }
2030
2031 QTextBlock block;
2032
2033 // insert commit string
2034 if (!e->commitString().isEmpty() || e->replacementLength()) {
2035 if (e->commitString().endsWith(QChar::LineFeed))
2036 block = cursor.block(); // Remember the block where the preedit text is
2037 QTextCursor c = cursor;
2038 c.setPosition(c.position() + e->replacementStart());
2039 c.setPosition(c.position() + e->replacementLength(), QTextCursor::KeepAnchor);
2040 c.insertText(e->commitString());
2041 }
2042
2043 for (int i = 0; i < e->attributes().size(); ++i) {
2044 const QInputMethodEvent::Attribute &a = e->attributes().at(i);
2045 if (a.type == QInputMethodEvent::Selection) {
2046 QTextCursor oldCursor = cursor;
2047 int blockStart = a.start + cursor.block().position();
2048 cursor.setPosition(blockStart, QTextCursor::MoveAnchor);
2049 cursor.setPosition(blockStart + a.length, QTextCursor::KeepAnchor);
2050 q->ensureCursorVisible();
2051 repaintOldAndNewSelection(oldCursor);
2052 }
2053 }
2054
2055 if (!block.isValid())
2056 block = cursor.block();
2057 QTextLayout *layout = block.layout();
2058 if (isGettingInput)
2059 layout->setPreeditArea(cursor.position() - block.position(), e->preeditString());
2060 QVector<QTextLayout::FormatRange> overrides;
2061 overrides.reserve(e->attributes().size());
2062 const int oldPreeditCursor = preeditCursor;
2063 preeditCursor = e->preeditString().length();
2064 hideCursor = false;
2065 for (int i = 0; i < e->attributes().size(); ++i) {
2066 const QInputMethodEvent::Attribute &a = e->attributes().at(i);
2067 if (a.type == QInputMethodEvent::Cursor) {
2068 preeditCursor = a.start;
2069 hideCursor = !a.length;
2070 } else if (a.type == QInputMethodEvent::TextFormat) {
2071 QTextCharFormat f = cursor.charFormat();
2072 f.merge(qvariant_cast<QTextFormat>(a.value).toCharFormat());
2073 if (f.isValid()) {
2074 QTextLayout::FormatRange o;
2075 o.start = a.start + cursor.position() - block.position();
2076 o.length = a.length;
2077 o.format = f;
2078
2079 // Make sure list is sorted by start index
2080 QVector<QTextLayout::FormatRange>::iterator it = overrides.end();
2081 while (it != overrides.begin()) {
2082 QVector<QTextLayout::FormatRange>::iterator previous = it - 1;
2083 if (o.start >= previous->start) {
2084 overrides.insert(it, o);
2085 break;
2086 }
2087 it = previous;
2088 }
2089
2090 if (it == overrides.begin())
2091 overrides.prepend(o);
2092 }
2093 }
2094 }
2095
2096 if (cursor.charFormat().isValid()) {
2097 int start = cursor.position() - block.position();
2098 int end = start + e->preeditString().length();
2099
2100 QVector<QTextLayout::FormatRange>::iterator it = overrides.begin();
2101 while (it != overrides.end()) {
2102 QTextLayout::FormatRange range = *it;
2103 int rangeStart = range.start;
2104 if (rangeStart > start) {
2105 QTextLayout::FormatRange o;
2106 o.start = start;
2107 o.length = rangeStart - start;
2108 o.format = cursor.charFormat();
2109 it = overrides.insert(it, o) + 1;
2110 }
2111
2112 ++it;
2113 start = range.start + range.length;
2114 }
2115
2116 if (start < end) {
2117 QTextLayout::FormatRange o;
2118 o.start = start;
2119 o.length = end - start;
2120 o.format = cursor.charFormat();
2121 overrides.append(o);
2122 }
2123 }
2124 layout->setFormats(overrides);
2125
2126 cursor.endEditBlock();
2127
2128 if (cursor.d)
2129 cursor.d->setX();
2130 if (oldCursorPos != cursor.position())
2131 emit q->cursorPositionChanged();
2132 if (oldPreeditCursor != preeditCursor)
2133 emit q->microFocusChanged();
2134}
2135
2136QVariant QWidgetTextControl::inputMethodQuery(Qt::InputMethodQuery property, QVariant argument) const
2137{
2138 Q_D(const QWidgetTextControl);
2139 QTextBlock block = d->cursor.block();
2140 switch(property) {
2141 case Qt::ImCursorRectangle:
2142 return cursorRect();
2143 case Qt::ImAnchorRectangle:
2144 return d->rectForPosition(d->cursor.anchor());
2145 case Qt::ImFont:
2146 return QVariant(d->cursor.charFormat().font());
2147 case Qt::ImCursorPosition: {
2148 const QPointF pt = argument.toPointF();
2149 if (!pt.isNull())
2150 return QVariant(cursorForPosition(pt).position() - block.position());
2151 return QVariant(d->cursor.position() - block.position()); }
2152 case Qt::ImSurroundingText:
2153 return QVariant(block.text());
2154 case Qt::ImCurrentSelection:
2155 return QVariant(d->cursor.selectedText());
2156 case Qt::ImMaximumTextLength:
2157 return QVariant(); // No limit.
2158 case Qt::ImAnchorPosition:
2159 return QVariant(d->cursor.anchor() - block.position());
2160 case Qt::ImAbsolutePosition: {
2161 const QPointF pt = argument.toPointF();
2162 if (!pt.isNull())
2163 return QVariant(cursorForPosition(pt).position());
2164 return QVariant(d->cursor.position()); }
2165 case Qt::ImTextAfterCursor:
2166 {
2167 int maxLength = argument.isValid() ? argument.toInt() : 1024;
2168 QTextCursor tmpCursor = d->cursor;
2169 int localPos = d->cursor.position() - block.position();
2170 QString result = block.text().mid(localPos);
2171 while (result.length() < maxLength) {
2172 int currentBlock = tmpCursor.blockNumber();
2173 tmpCursor.movePosition(QTextCursor::NextBlock);
2174 if (tmpCursor.blockNumber() == currentBlock)
2175 break;
2176 result += QLatin1Char('\n') + tmpCursor.block().text();
2177 }
2178 return QVariant(result);
2179 }
2180 case Qt::ImTextBeforeCursor:
2181 {
2182 int maxLength = argument.isValid() ? argument.toInt() : 1024;
2183 QTextCursor tmpCursor = d->cursor;
2184 int localPos = d->cursor.position() - block.position();
2185 int numBlocks = 0;
2186 int resultLen = localPos;
2187 while (resultLen < maxLength) {
2188 int currentBlock = tmpCursor.blockNumber();
2189 tmpCursor.movePosition(QTextCursor::PreviousBlock);
2190 if (tmpCursor.blockNumber() == currentBlock)
2191 break;
2192 numBlocks++;
2193 resultLen += tmpCursor.block().length();
2194 }
2195 QString result;
2196 while (numBlocks) {
2197 result += tmpCursor.block().text() + QLatin1Char('\n');
2198 tmpCursor.movePosition(QTextCursor::NextBlock);
2199 --numBlocks;
2200 }
2201 result += block.text().midRef(0, localPos);
2202 return QVariant(result);
2203 }
2204 default:
2205 return QVariant();
2206 }
2207}
2208
2209void QWidgetTextControl::setFocus(bool focus, Qt::FocusReason reason)
2210{
2211 QFocusEvent ev(focus ? QEvent::FocusIn : QEvent::FocusOut,
2212 reason);
2213 processEvent(&ev);
2214}
2215
2216void QWidgetTextControlPrivate::focusEvent(QFocusEvent *e)
2217{
2218 Q_Q(QWidgetTextControl);
2219 emit q->updateRequest(q->selectionRect());
2220 if (e->gotFocus()) {
2221#ifdef QT_KEYPAD_NAVIGATION
2222 if (!QApplicationPrivate::keypadNavigationEnabled() || (hasEditFocus && (e->reason() == Qt::PopupFocusReason))) {
2223#endif
2224 cursorOn = (interactionFlags & (Qt::TextSelectableByKeyboard | Qt::TextEditable));
2225 if (interactionFlags & Qt::TextEditable) {
2226 setCursorVisible(true);
2227 }
2228#ifdef QT_KEYPAD_NAVIGATION
2229 }
2230#endif
2231 } else {
2232 setCursorVisible(false);
2233
2234 if (cursorIsFocusIndicator
2235 && e->reason() != Qt::ActiveWindowFocusReason
2236 && e->reason() != Qt::PopupFocusReason
2237 && cursor.hasSelection()) {
2238 cursor.clearSelection();
2239 }
2240 }
2241 hasFocus = e->gotFocus();
2242}
2243
2244QString QWidgetTextControlPrivate::anchorForCursor(const QTextCursor &anchorCursor) const
2245{
2246 if (anchorCursor.hasSelection()) {
2247 QTextCursor cursor = anchorCursor;
2248 if (cursor.selectionStart() != cursor.position())
2249 cursor.setPosition(cursor.selectionStart());
2250 cursor.movePosition(QTextCursor::NextCharacter);
2251 QTextCharFormat fmt = cursor.charFormat();
2252 if (fmt.isAnchor() && fmt.hasProperty(QTextFormat::AnchorHref))
2253 return fmt.stringProperty(QTextFormat::AnchorHref);
2254 }
2255 return QString();
2256}
2257
2258#ifdef QT_KEYPAD_NAVIGATION
2259void QWidgetTextControlPrivate::editFocusEvent(QEvent *e)
2260{
2261 Q_Q(QWidgetTextControl);
2262
2263 if (QApplicationPrivate::keypadNavigationEnabled()) {
2264 if (e->type() == QEvent::EnterEditFocus && interactionFlags & Qt::TextEditable) {
2265 const QTextCursor oldSelection = cursor;
2266 const int oldCursorPos = cursor.position();
2267 const bool moved = cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
2268 q->ensureCursorVisible();
2269 if (moved) {
2270 if (cursor.position() != oldCursorPos)
2271 emit q->cursorPositionChanged();
2272 emit q->microFocusChanged();
2273 }
2274 selectionChanged();
2275 repaintOldAndNewSelection(oldSelection);
2276
2277 setBlinkingCursorEnabled(true);
2278 } else
2279 setBlinkingCursorEnabled(false);
2280 }
2281
2282 hasEditFocus = e->type() == QEvent::EnterEditFocus;
2283}
2284#endif
2285
2286#ifndef QT_NO_CONTEXTMENU
2287static inline void setActionIcon(QAction *action, const QString &name)
2288{
2289 const QIcon icon = QIcon::fromTheme(name);
2290 if (!icon.isNull())
2291 action->setIcon(icon);
2292}
2293
2294QMenu *QWidgetTextControl::createStandardContextMenu(const QPointF &pos, QWidget *parent)
2295{
2296 Q_D(QWidgetTextControl);
2297
2298 const bool showTextSelectionActions = d->interactionFlags & (Qt::TextEditable | Qt::TextSelectableByKeyboard | Qt::TextSelectableByMouse);
2299
2300 d->linkToCopy = QString();
2301 if (!pos.isNull())
2302 d->linkToCopy = anchorAt(pos);
2303
2304 if (d->linkToCopy.isEmpty() && !showTextSelectionActions)
2305 return 0;
2306
2307 QMenu *menu = new QMenu(parent);
2308 QAction *a;
2309
2310 if (d->interactionFlags & Qt::TextEditable) {
2311 a = menu->addAction(tr("&Undo") + ACCEL_KEY(QKeySequence::Undo), this, SLOT(undo()));
2312 a->setEnabled(d->doc->isUndoAvailable());
2313 a->setObjectName(QStringLiteral("edit-undo"));
2314 setActionIcon(a, QStringLiteral("edit-undo"));
2315 a = menu->addAction(tr("&Redo") + ACCEL_KEY(QKeySequence::Redo), this, SLOT(redo()));
2316 a->setEnabled(d->doc->isRedoAvailable());
2317 a->setObjectName(QStringLiteral("edit-redo"));
2318 setActionIcon(a, QStringLiteral("edit-redo"));
2319 menu->addSeparator();
2320
2321#ifndef QT_NO_CLIPBOARD
2322 a = menu->addAction(tr("Cu&t") + ACCEL_KEY(QKeySequence::Cut), this, SLOT(cut()));
2323 a->setEnabled(d->cursor.hasSelection());
2324 a->setObjectName(QStringLiteral("edit-cut"));
2325 setActionIcon(a, QStringLiteral("edit-cut"));
2326#endif
2327 }
2328
2329#ifndef QT_NO_CLIPBOARD
2330 if (showTextSelectionActions) {
2331 a = menu->addAction(tr("&Copy") +