1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtGui module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qquicktextcontrol_p.h"
43 #include "qquicktextcontrol_p_p.h"
45 #ifndef QT_NO_TEXTCONTROL
52 #include <qclipboard.h>
54 #include <qinputpanel.h>
55 #include "private/qtextdocumentlayout_p.h"
56 #include "private/qabstracttextdocumentlayout_p.h"
57 #include "qtextdocument.h"
58 #include "private/qtextdocument_p.h"
59 #include "qtextlist.h"
60 #include "qtextdocumentwriter.h"
61 #include "private/qtextcursor_p.h"
62 #include "qpagedpaintdevice.h"
63 #include "private/qpagedpaintdevice_p.h"
65 #include <qtextformat.h>
66 #include <qdatetime.h>
68 #include <qguiapplication.h>
70 #include <qtexttable.h>
73 #include <qstylehints.h>
75 // ### these should come from QStyleHints
76 const int textCursorWidth = 1;
77 const bool fullWidthSelection = true;
81 #ifndef QT_NO_CONTEXTMENU
84 // could go into QTextCursor...
85 static QTextLine currentTextLine(const QTextCursor &cursor)
87 const QTextBlock block = cursor.block();
91 const QTextLayout *layout = block.layout();
95 const int relativePos = cursor.position() - block.position();
96 return layout->lineForTextPosition(relativePos);
99 QQuickTextControlPrivate::QQuickTextControlPrivate()
100 : doc(0), cursorOn(false), cursorIsFocusIndicator(false),
101 interactionFlags(Qt::TextEditorInteraction),
103 #ifndef QT_NO_DRAGANDDROP
104 mousePressed(false), mightStartDrag(false),
106 lastSelectionState(false), ignoreAutomaticScrollbarAdjustement(false),
107 overwriteMode(false),
108 acceptRichText(true),
109 preeditCursor(0), hideCursor(false),
111 #ifdef QT_KEYPAD_NAVIGATION
115 hadSelectionOnMousePress(false),
116 ignoreUnusedNavigationEvents(false),
117 openExternalLinks(false),
118 wordSelectionEnabled(false)
121 bool QQuickTextControlPrivate::cursorMoveKeyEvent(QKeyEvent *e)
123 #ifdef QT_NO_SHORTCUT
127 Q_Q(QQuickTextControl);
131 const QTextCursor oldSelection = cursor;
132 const int oldCursorPos = cursor.position();
134 QTextCursor::MoveMode mode = QTextCursor::MoveAnchor;
135 QTextCursor::MoveOperation op = QTextCursor::NoMove;
139 #ifndef QT_NO_SHORTCUT
140 if (e == QKeySequence::MoveToNextChar) {
141 op = QTextCursor::Right;
143 else if (e == QKeySequence::MoveToPreviousChar) {
144 op = QTextCursor::Left;
146 else if (e == QKeySequence::SelectNextChar) {
147 op = QTextCursor::Right;
148 mode = QTextCursor::KeepAnchor;
150 else if (e == QKeySequence::SelectPreviousChar) {
151 op = QTextCursor::Left;
152 mode = QTextCursor::KeepAnchor;
154 else if (e == QKeySequence::SelectNextWord) {
155 op = QTextCursor::WordRight;
156 mode = QTextCursor::KeepAnchor;
158 else if (e == QKeySequence::SelectPreviousWord) {
159 op = QTextCursor::WordLeft;
160 mode = QTextCursor::KeepAnchor;
162 else if (e == QKeySequence::SelectStartOfLine) {
163 op = QTextCursor::StartOfLine;
164 mode = QTextCursor::KeepAnchor;
166 else if (e == QKeySequence::SelectEndOfLine) {
167 op = QTextCursor::EndOfLine;
168 mode = QTextCursor::KeepAnchor;
170 else if (e == QKeySequence::SelectStartOfBlock) {
171 op = QTextCursor::StartOfBlock;
172 mode = QTextCursor::KeepAnchor;
174 else if (e == QKeySequence::SelectEndOfBlock) {
175 op = QTextCursor::EndOfBlock;
176 mode = QTextCursor::KeepAnchor;
178 else if (e == QKeySequence::SelectStartOfDocument) {
179 op = QTextCursor::Start;
180 mode = QTextCursor::KeepAnchor;
182 else if (e == QKeySequence::SelectEndOfDocument) {
183 op = QTextCursor::End;
184 mode = QTextCursor::KeepAnchor;
186 else if (e == QKeySequence::SelectPreviousLine) {
187 op = QTextCursor::Up;
188 mode = QTextCursor::KeepAnchor;
190 else if (e == QKeySequence::SelectNextLine) {
191 op = QTextCursor::Down;
192 mode = QTextCursor::KeepAnchor;
194 QTextBlock block = cursor.block();
195 QTextLine line = currentTextLine(cursor);
196 if (!block.next().isValid()
198 && line.lineNumber() == block.layout()->lineCount() - 1)
199 op = QTextCursor::End;
202 else if (e == QKeySequence::MoveToNextWord) {
203 op = QTextCursor::WordRight;
205 else if (e == QKeySequence::MoveToPreviousWord) {
206 op = QTextCursor::WordLeft;
208 else if (e == QKeySequence::MoveToEndOfBlock) {
209 op = QTextCursor::EndOfBlock;
211 else if (e == QKeySequence::MoveToStartOfBlock) {
212 op = QTextCursor::StartOfBlock;
214 else if (e == QKeySequence::MoveToNextLine) {
215 op = QTextCursor::Down;
217 else if (e == QKeySequence::MoveToPreviousLine) {
218 op = QTextCursor::Up;
220 else if (e == QKeySequence::MoveToStartOfLine) {
221 op = QTextCursor::StartOfLine;
223 else if (e == QKeySequence::MoveToEndOfLine) {
224 op = QTextCursor::EndOfLine;
226 else if (e == QKeySequence::MoveToStartOfDocument) {
227 op = QTextCursor::Start;
229 else if (e == QKeySequence::MoveToEndOfDocument) {
230 op = QTextCursor::End;
232 #endif // QT_NO_SHORTCUT
237 // Except for pageup and pagedown, Mac OS X has very different behavior, we don't do it all, but
238 // here's the breakdown:
239 // Shift still works as an anchor, but only one of the other keys can be down Ctrl (Command),
240 // Alt (Option), or Meta (Control).
241 // Command/Control + Left/Right -- Move to left or right of the line
242 // + Up/Down -- Move to top bottom of the file. (Control doesn't move the cursor)
243 // Option + Left/Right -- Move one word Left/right.
244 // + Up/Down -- Begin/End of Paragraph.
245 // Home/End Top/Bottom of file. (usually don't move the cursor, but will select)
247 bool visualNavigation = cursor.visualNavigation();
248 cursor.setVisualNavigation(true);
249 const bool moved = cursor.movePosition(op, mode);
250 cursor.setVisualNavigation(visualNavigation);
251 q->ensureCursorVisible();
253 bool ignoreNavigationEvents = ignoreUnusedNavigationEvents;
254 bool isNavigationEvent = e->key() == Qt::Key_Up || e->key() == Qt::Key_Down;
256 #ifdef QT_KEYPAD_NAVIGATION
257 ignoreNavigationEvents = ignoreNavigationEvents || QGuiApplication::keypadNavigationEnabled();
258 isNavigationEvent = isNavigationEvent ||
259 (QGuiApplication::navigationMode() == Qt::NavigationModeKeypadDirectional
260 && (e->key() == Qt::Key_Left || e->key() == Qt::Key_Right));
262 isNavigationEvent = isNavigationEvent || e->key() == Qt::Key_Left || e->key() == Qt::Key_Right;
266 if (cursor.position() != oldCursorPos)
267 emit q->cursorPositionChanged();
268 emit q->microFocusChanged();
269 } else if (ignoreNavigationEvents && isNavigationEvent && oldSelection.anchor() == cursor.anchor()) {
273 selectionChanged(/*forceEmitSelectionChanged =*/(mode == QTextCursor::KeepAnchor));
275 repaintOldAndNewSelection(oldSelection);
280 void QQuickTextControlPrivate::updateCurrentCharFormat()
282 Q_Q(QQuickTextControl);
284 QTextCharFormat fmt = cursor.charFormat();
285 if (fmt == lastCharFormat)
287 lastCharFormat = fmt;
289 emit q->currentCharFormatChanged(fmt);
290 emit q->microFocusChanged();
293 void QQuickTextControlPrivate::indent()
295 QTextBlockFormat blockFmt = cursor.blockFormat();
297 QTextList *list = cursor.currentList();
299 QTextBlockFormat modifier;
300 modifier.setIndent(blockFmt.indent() + 1);
301 cursor.mergeBlockFormat(modifier);
303 QTextListFormat format = list->format();
304 format.setIndent(format.indent() + 1);
306 if (list->itemNumber(cursor.block()) == 1)
307 list->setFormat(format);
309 cursor.createList(format);
313 void QQuickTextControlPrivate::outdent()
315 QTextBlockFormat blockFmt = cursor.blockFormat();
317 QTextList *list = cursor.currentList();
320 QTextBlockFormat modifier;
321 modifier.setIndent(blockFmt.indent() - 1);
322 cursor.mergeBlockFormat(modifier);
324 QTextListFormat listFmt = list->format();
325 listFmt.setIndent(listFmt.indent() - 1);
326 list->setFormat(listFmt);
330 void QQuickTextControlPrivate::gotoNextTableCell()
332 QTextTable *table = cursor.currentTable();
333 QTextTableCell cell = table->cellAt(cursor);
335 int newColumn = cell.column() + cell.columnSpan();
336 int newRow = cell.row();
338 if (newColumn >= table->columns()) {
341 if (newRow >= table->rows())
342 table->insertRows(table->rows(), 1);
345 cell = table->cellAt(newRow, newColumn);
346 cursor = cell.firstCursorPosition();
349 void QQuickTextControlPrivate::gotoPreviousTableCell()
351 QTextTable *table = cursor.currentTable();
352 QTextTableCell cell = table->cellAt(cursor);
354 int newColumn = cell.column() - 1;
355 int newRow = cell.row();
358 newColumn = table->columns() - 1;
364 cell = table->cellAt(newRow, newColumn);
365 cursor = cell.firstCursorPosition();
368 void QQuickTextControlPrivate::createAutoBulletList()
370 cursor.beginEditBlock();
372 QTextBlockFormat blockFmt = cursor.blockFormat();
374 QTextListFormat listFmt;
375 listFmt.setStyle(QTextListFormat::ListDisc);
376 listFmt.setIndent(blockFmt.indent() + 1);
378 blockFmt.setIndent(0);
379 cursor.setBlockFormat(blockFmt);
381 cursor.createList(listFmt);
383 cursor.endEditBlock();
386 void QQuickTextControlPrivate::init(Qt::TextFormat format, const QString &text, QTextDocument *document)
388 Q_Q(QQuickTextControl);
389 setContent(format, text, document);
391 doc->setUndoRedoEnabled(interactionFlags & Qt::TextEditable);
392 q->setCursorWidth(-1);
394 QObject::connect(q, SIGNAL(updateCursorRequest(QRectF)), q, SIGNAL(updateRequest(QRectF)));
397 void QQuickTextControlPrivate::setContent(Qt::TextFormat format, const QString &text, QTextDocument *document)
399 Q_Q(QQuickTextControl);
401 // for use when called from setPlainText. we may want to re-use the currently
402 // set char format then.
403 const QTextCharFormat charFormatForInsertion = cursor.charFormat();
405 bool clearDocument = true;
409 clearDocument = false;
411 palette = QGuiApplication::palette();
412 doc = new QTextDocument(q);
414 _q_documentLayoutChanged();
415 cursor = QTextCursor(doc);
417 // #### doc->documentLayout()->setPaintDevice(viewport);
419 QObject::connect(doc, SIGNAL(contentsChanged()), q, SLOT(_q_updateCurrentCharFormatAndSelection()));
420 QObject::connect(doc, SIGNAL(cursorPositionChanged(QTextCursor)), q, SLOT(_q_emitCursorPosChanged(QTextCursor)));
421 QObject::connect(doc, SIGNAL(documentLayoutChanged()), q, SLOT(_q_documentLayoutChanged()));
423 // convenience signal forwards
424 QObject::connect(doc, SIGNAL(undoAvailable(bool)), q, SIGNAL(undoAvailable(bool)));
425 QObject::connect(doc, SIGNAL(redoAvailable(bool)), q, SIGNAL(redoAvailable(bool)));
426 QObject::connect(doc, SIGNAL(modificationChanged(bool)), q, SIGNAL(modificationChanged(bool)));
427 QObject::connect(doc, SIGNAL(blockCountChanged(int)), q, SIGNAL(blockCountChanged(int)));
430 bool previousUndoRedoState = doc->isUndoRedoEnabled();
432 doc->setUndoRedoEnabled(false);
434 //Saving the index save some time.
435 static int contentsChangedIndex = QTextDocument::staticMetaObject.indexOfSignal("contentsChanged()");
436 static int textChangedIndex = QQuickTextControl::staticMetaObject.indexOfSignal("textChanged()");
437 // avoid multiple textChanged() signals being emitted
438 QMetaObject::disconnect(doc, contentsChangedIndex, q, textChangedIndex);
440 if (!text.isEmpty()) {
441 // clear 'our' cursor for insertion to prevent
442 // the emission of the cursorPositionChanged() signal.
443 // instead we emit it only once at the end instead of
444 // at the end of the document after loading and when
445 // positioning the cursor again to the start of the
447 cursor = QTextCursor();
448 if (format == Qt::PlainText) {
449 QTextCursor formatCursor(doc);
450 // put the setPlainText and the setCharFormat into one edit block,
451 // so that the syntax highlight triggers only /once/ for the entire
452 // document, not twice.
453 formatCursor.beginEditBlock();
454 doc->setPlainText(text);
455 doc->setUndoRedoEnabled(false);
456 formatCursor.select(QTextCursor::Document);
457 formatCursor.setCharFormat(charFormatForInsertion);
458 formatCursor.endEditBlock();
460 #ifndef QT_NO_TEXTHTMLPARSER
463 doc->setPlainText(text);
465 doc->setUndoRedoEnabled(false);
467 cursor = QTextCursor(doc);
468 } else if (clearDocument) {
471 cursor.setCharFormat(charFormatForInsertion);
473 QMetaObject::connect(doc, contentsChangedIndex, q, textChangedIndex);
474 emit q->textChanged();
476 doc->setUndoRedoEnabled(previousUndoRedoState);
477 _q_updateCurrentCharFormatAndSelection();
479 doc->setModified(false);
481 q->ensureCursorVisible();
482 emit q->cursorPositionChanged();
485 void QQuickTextControlPrivate::startDrag()
487 #ifndef QT_NO_DRAGANDDROP
488 Q_Q(QQuickTextControl);
489 mousePressed = false;
492 QMimeData *data = q->createMimeDataFromSelection();
494 QDrag *drag = new QDrag(contextObject);
495 drag->setMimeData(data);
497 Qt::DropActions actions = Qt::CopyAction;
498 Qt::DropAction action;
499 if (interactionFlags & Qt::TextEditable) {
500 actions |= Qt::MoveAction;
501 action = drag->exec(actions, Qt::MoveAction);
503 action = drag->exec(actions, Qt::CopyAction);
506 if (action == Qt::MoveAction && drag->target() != contextObject)
507 cursor.removeSelectedText();
511 void QQuickTextControlPrivate::setCursorPosition(const QPointF &pos)
513 Q_Q(QQuickTextControl);
514 const int cursorPos = q->hitTest(pos, Qt::FuzzyHit);
517 cursor.setPosition(cursorPos);
520 void QQuickTextControlPrivate::setCursorPosition(int pos, QTextCursor::MoveMode mode)
522 cursor.setPosition(pos, mode);
524 if (mode != QTextCursor::KeepAnchor) {
525 selectedWordOnDoubleClick = QTextCursor();
526 selectedBlockOnTrippleClick = QTextCursor();
530 void QQuickTextControlPrivate::repaintCursor()
532 Q_Q(QQuickTextControl);
533 emit q->updateCursorRequest(cursorRectPlusUnicodeDirectionMarkers(cursor));
536 void QQuickTextControlPrivate::repaintOldAndNewSelection(const QTextCursor &oldSelection)
538 Q_Q(QQuickTextControl);
539 if (cursor.hasSelection()
540 && oldSelection.hasSelection()
541 && cursor.currentFrame() == oldSelection.currentFrame()
542 && !cursor.hasComplexSelection()
543 && !oldSelection.hasComplexSelection()
544 && cursor.anchor() == oldSelection.anchor()
546 QTextCursor differenceSelection(doc);
547 differenceSelection.setPosition(oldSelection.position());
548 differenceSelection.setPosition(cursor.position(), QTextCursor::KeepAnchor);
549 emit q->updateRequest(q->selectionRect(differenceSelection));
551 if (!oldSelection.hasSelection() && !cursor.hasSelection()) {
552 if (!oldSelection.isNull())
553 emit q->updateCursorRequest(q->selectionRect(oldSelection) | cursorRectPlusUnicodeDirectionMarkers(oldSelection));
554 emit q->updateCursorRequest(q->selectionRect() | cursorRectPlusUnicodeDirectionMarkers(cursor));
557 if (!oldSelection.isNull())
558 emit q->updateRequest(q->selectionRect(oldSelection) | cursorRectPlusUnicodeDirectionMarkers(oldSelection));
559 emit q->updateRequest(q->selectionRect() | cursorRectPlusUnicodeDirectionMarkers(cursor));
564 void QQuickTextControlPrivate::selectionChanged(bool forceEmitSelectionChanged /*=false*/)
566 Q_Q(QQuickTextControl);
567 if (forceEmitSelectionChanged)
568 emit q->selectionChanged();
570 bool current = cursor.hasSelection();
571 if (current == lastSelectionState)
574 lastSelectionState = current;
575 emit q->copyAvailable(current);
576 if (!forceEmitSelectionChanged)
577 emit q->selectionChanged();
578 emit q->microFocusChanged();
581 void QQuickTextControlPrivate::_q_updateCurrentCharFormatAndSelection()
583 updateCurrentCharFormat();
587 #ifndef QT_NO_CLIPBOARD
588 void QQuickTextControlPrivate::setClipboardSelection()
590 QClipboard *clipboard = QGuiApplication::clipboard();
591 if (!cursor.hasSelection() || !clipboard->supportsSelection())
593 Q_Q(QQuickTextControl);
594 QMimeData *data = q->createMimeDataFromSelection();
595 clipboard->setMimeData(data, QClipboard::Selection);
599 void QQuickTextControlPrivate::_q_emitCursorPosChanged(const QTextCursor &someCursor)
601 Q_Q(QQuickTextControl);
602 if (someCursor.isCopyOf(cursor)) {
603 emit q->cursorPositionChanged();
604 emit q->microFocusChanged();
608 void QQuickTextControlPrivate::_q_documentLayoutChanged()
610 Q_Q(QQuickTextControl);
611 QAbstractTextDocumentLayout *layout = doc->documentLayout();
612 QObject::connect(layout, SIGNAL(update(QRectF)), q, SIGNAL(updateRequest(QRectF)));
613 QObject::connect(layout, SIGNAL(updateBlock(QTextBlock)), q, SLOT(_q_updateBlock(QTextBlock)));
614 QObject::connect(layout, SIGNAL(documentSizeChanged(QSizeF)), q, SIGNAL(documentSizeChanged(QSizeF)));
618 void QQuickTextControlPrivate::setBlinkingCursorEnabled(bool enable)
620 Q_Q(QQuickTextControl);
622 if (enable && qApp->styleHints()->cursorFlashTime() > 0)
623 cursorBlinkTimer.start(qApp->styleHints()->cursorFlashTime() / 2, q);
625 cursorBlinkTimer.stop();
632 void QQuickTextControlPrivate::extendWordwiseSelection(int suggestedNewPosition, qreal mouseXPosition)
634 Q_Q(QQuickTextControl);
636 // if inside the initial selected word keep that
637 if (suggestedNewPosition >= selectedWordOnDoubleClick.selectionStart()
638 && suggestedNewPosition <= selectedWordOnDoubleClick.selectionEnd()) {
639 q->setTextCursor(selectedWordOnDoubleClick);
643 QTextCursor curs = selectedWordOnDoubleClick;
644 curs.setPosition(suggestedNewPosition, QTextCursor::KeepAnchor);
646 if (!curs.movePosition(QTextCursor::StartOfWord))
648 const int wordStartPos = curs.position();
650 const int blockPos = curs.block().position();
651 const QPointF blockCoordinates = q->blockBoundingRect(curs.block()).topLeft();
653 QTextLine line = currentTextLine(curs);
657 const qreal wordStartX = line.cursorToX(curs.position() - blockPos) + blockCoordinates.x();
659 if (!curs.movePosition(QTextCursor::EndOfWord))
661 const int wordEndPos = curs.position();
663 const QTextLine otherLine = currentTextLine(curs);
664 if (otherLine.textStart() != line.textStart()
665 || wordEndPos == wordStartPos)
668 const qreal wordEndX = line.cursorToX(curs.position() - blockPos) + blockCoordinates.x();
670 if (!wordSelectionEnabled && (mouseXPosition < wordStartX || mouseXPosition > wordEndX))
673 if (wordSelectionEnabled) {
674 if (suggestedNewPosition < selectedWordOnDoubleClick.position()) {
675 cursor.setPosition(selectedWordOnDoubleClick.selectionEnd());
676 setCursorPosition(wordStartPos, QTextCursor::KeepAnchor);
678 cursor.setPosition(selectedWordOnDoubleClick.selectionStart());
679 setCursorPosition(wordEndPos, QTextCursor::KeepAnchor);
682 // keep the already selected word even when moving to the left
684 if (suggestedNewPosition < selectedWordOnDoubleClick.position())
685 cursor.setPosition(selectedWordOnDoubleClick.selectionEnd());
687 cursor.setPosition(selectedWordOnDoubleClick.selectionStart());
689 const qreal differenceToStart = mouseXPosition - wordStartX;
690 const qreal differenceToEnd = wordEndX - mouseXPosition;
692 if (differenceToStart < differenceToEnd)
693 setCursorPosition(wordStartPos, QTextCursor::KeepAnchor);
695 setCursorPosition(wordEndPos, QTextCursor::KeepAnchor);
698 if (interactionFlags & Qt::TextSelectableByMouse) {
699 #ifndef QT_NO_CLIPBOARD
700 setClipboardSelection();
702 selectionChanged(true);
706 void QQuickTextControlPrivate::extendBlockwiseSelection(int suggestedNewPosition)
708 Q_Q(QQuickTextControl);
710 // if inside the initial selected line keep that
711 if (suggestedNewPosition >= selectedBlockOnTrippleClick.selectionStart()
712 && suggestedNewPosition <= selectedBlockOnTrippleClick.selectionEnd()) {
713 q->setTextCursor(selectedBlockOnTrippleClick);
717 if (suggestedNewPosition < selectedBlockOnTrippleClick.position()) {
718 cursor.setPosition(selectedBlockOnTrippleClick.selectionEnd());
719 cursor.setPosition(suggestedNewPosition, QTextCursor::KeepAnchor);
720 cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor);
722 cursor.setPosition(selectedBlockOnTrippleClick.selectionStart());
723 cursor.setPosition(suggestedNewPosition, QTextCursor::KeepAnchor);
724 cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
725 cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
728 if (interactionFlags & Qt::TextSelectableByMouse) {
729 #ifndef QT_NO_CLIPBOARD
730 setClipboardSelection();
732 selectionChanged(true);
736 void QQuickTextControlPrivate::_q_deleteSelected()
738 if (!(interactionFlags & Qt::TextEditable) || !cursor.hasSelection())
740 cursor.removeSelectedText();
743 void QQuickTextControl::undo()
745 Q_D(QQuickTextControl);
746 d->repaintSelection();
747 const int oldCursorPos = d->cursor.position();
748 d->doc->undo(&d->cursor);
749 if (d->cursor.position() != oldCursorPos)
750 emit cursorPositionChanged();
751 emit microFocusChanged();
752 ensureCursorVisible();
755 void QQuickTextControl::redo()
757 Q_D(QQuickTextControl);
758 d->repaintSelection();
759 const int oldCursorPos = d->cursor.position();
760 d->doc->redo(&d->cursor);
761 if (d->cursor.position() != oldCursorPos)
762 emit cursorPositionChanged();
763 emit microFocusChanged();
764 ensureCursorVisible();
767 QQuickTextControl::QQuickTextControl(QObject *parent)
768 : QObject(*new QQuickTextControlPrivate, parent)
770 Q_D(QQuickTextControl);
774 QQuickTextControl::QQuickTextControl(const QString &text, QObject *parent)
775 : QObject(*new QQuickTextControlPrivate, parent)
777 Q_D(QQuickTextControl);
778 d->init(Qt::RichText, text);
781 QQuickTextControl::QQuickTextControl(QTextDocument *doc, QObject *parent)
782 : QObject(*new QQuickTextControlPrivate, parent)
784 Q_D(QQuickTextControl);
785 d->init(Qt::RichText, QString(), doc);
788 QQuickTextControl::~QQuickTextControl()
792 void QQuickTextControl::setView(QObject *view)
794 Q_D(QQuickTextControl);
795 d->contextObject = view;
798 QObject *QQuickTextControl::view() const
800 Q_D(const QQuickTextControl);
801 return d->contextObject;
804 void QQuickTextControl::setDocument(QTextDocument *document)
806 Q_D(QQuickTextControl);
807 if (d->doc == document)
810 d->doc->disconnect(this);
811 d->doc->documentLayout()->disconnect(this);
812 d->doc->documentLayout()->setPaintDevice(0);
814 if (d->doc->parent() == this)
818 d->setContent(Qt::RichText, QString(), document);
821 QTextDocument *QQuickTextControl::document() const
823 Q_D(const QQuickTextControl);
827 void QQuickTextControl::setTextCursor(const QTextCursor &cursor)
829 Q_D(QQuickTextControl);
831 d->cursorIsFocusIndicator = false;
832 const bool posChanged = cursor.position() != d->cursor.position();
833 const QTextCursor oldSelection = d->cursor;
835 d->cursorOn = d->hasFocus && (d->interactionFlags & Qt::TextEditable);
836 d->_q_updateCurrentCharFormatAndSelection();
837 ensureCursorVisible();
838 d->repaintOldAndNewSelection(oldSelection);
840 emit cursorPositionChanged();
843 QTextCursor QQuickTextControl::textCursor() const
845 Q_D(const QQuickTextControl);
849 #ifndef QT_NO_CLIPBOARD
851 void QQuickTextControl::cut()
853 Q_D(QQuickTextControl);
854 if (!(d->interactionFlags & Qt::TextEditable) || !d->cursor.hasSelection())
857 d->cursor.removeSelectedText();
860 void QQuickTextControl::copy()
862 Q_D(QQuickTextControl);
863 if (!d->cursor.hasSelection())
865 QMimeData *data = createMimeDataFromSelection();
866 QGuiApplication::clipboard()->setMimeData(data);
869 void QQuickTextControl::paste(QClipboard::Mode mode)
871 const QMimeData *md = QGuiApplication::clipboard()->mimeData(mode);
873 insertFromMimeData(md);
877 void QQuickTextControl::clear()
879 Q_D(QQuickTextControl);
880 // clears and sets empty content
881 d->extraSelections.clear();
886 void QQuickTextControl::selectAll()
888 Q_D(QQuickTextControl);
889 const int selectionLength = qAbs(d->cursor.position() - d->cursor.anchor());
890 d->cursor.select(QTextCursor::Document);
891 d->selectionChanged(selectionLength != qAbs(d->cursor.position() - d->cursor.anchor()));
892 d->cursorIsFocusIndicator = false;
893 emit updateRequest();
896 void QQuickTextControl::processEvent(QEvent *e, const QPointF &coordinateOffset)
899 m.translate(coordinateOffset.x(), coordinateOffset.y());
903 void QQuickTextControl::processEvent(QEvent *e, const QMatrix &matrix)
905 Q_D(QQuickTextControl);
906 if (d->interactionFlags == Qt::NoTextInteraction) {
912 case QEvent::KeyPress:
913 d->keyPressEvent(static_cast<QKeyEvent *>(e));
915 case QEvent::MouseButtonPress: {
916 QMouseEvent *ev = static_cast<QMouseEvent *>(e);
917 d->mousePressEvent(ev, ev->button(), matrix.map(ev->localPos()), ev->modifiers(),
918 ev->buttons(), ev->screenPos().toPoint());
920 case QEvent::MouseMove: {
921 QMouseEvent *ev = static_cast<QMouseEvent *>(e);
922 d->mouseMoveEvent(ev, ev->button(), matrix.map(ev->localPos()), ev->modifiers(),
923 ev->buttons(), ev->screenPos().toPoint());
925 case QEvent::MouseButtonRelease: {
926 QMouseEvent *ev = static_cast<QMouseEvent *>(e);
927 d->mouseReleaseEvent(ev, ev->button(), matrix.map(ev->localPos()), ev->modifiers(),
928 ev->buttons(), ev->screenPos().toPoint());
930 case QEvent::MouseButtonDblClick: {
931 QMouseEvent *ev = static_cast<QMouseEvent *>(e);
932 d->mouseDoubleClickEvent(ev, ev->button(), matrix.map(ev->localPos()), ev->modifiers(),
933 ev->buttons(), ev->screenPos().toPoint());
935 case QEvent::InputMethod:
936 d->inputMethodEvent(static_cast<QInputMethodEvent *>(e));
938 case QEvent::FocusIn:
939 case QEvent::FocusOut:
940 d->focusEvent(static_cast<QFocusEvent *>(e));
943 case QEvent::EnabledChange:
944 d->isEnabled = e->isAccepted();
947 #ifndef QT_NO_DRAGANDDROP
948 case QEvent::DragEnter: {
949 QDragEnterEvent *ev = static_cast<QDragEnterEvent *>(e);
950 if (d->dragEnterEvent(e, ev->mimeData()))
951 ev->acceptProposedAction();
954 case QEvent::DragLeave:
957 case QEvent::DragMove: {
958 QDragMoveEvent *ev = static_cast<QDragMoveEvent *>(e);
959 if (d->dragMoveEvent(e, ev->mimeData(), matrix.map(ev->pos())))
960 ev->acceptProposedAction();
964 QDropEvent *ev = static_cast<QDropEvent *>(e);
965 if (d->dropEvent(ev->mimeData(), matrix.map(ev->pos()), ev->dropAction(), ev->source()))
966 ev->acceptProposedAction();
971 #ifdef QT_KEYPAD_NAVIGATION
972 case QEvent::EnterEditFocus:
973 case QEvent::LeaveEditFocus:
974 if (QGuiApplication::keypadNavigationEnabled())
975 d->editFocusEvent(e);
978 case QEvent::ShortcutOverride:
979 if (d->interactionFlags & Qt::TextEditable) {
980 QKeyEvent* ke = static_cast<QKeyEvent *>(e);
981 if (ke->modifiers() == Qt::NoModifier
982 || ke->modifiers() == Qt::ShiftModifier
983 || ke->modifiers() == Qt::KeypadModifier) {
984 if (ke->key() < Qt::Key_Escape) {
993 case Qt::Key_Backspace:
1004 #ifndef QT_NO_SHORTCUT
1005 } else if (ke == QKeySequence::Copy
1006 || ke == QKeySequence::Paste
1007 || ke == QKeySequence::Cut
1008 || ke == QKeySequence::Redo
1009 || ke == QKeySequence::Undo
1010 || ke == QKeySequence::MoveToNextWord
1011 || ke == QKeySequence::MoveToPreviousWord
1012 || ke == QKeySequence::MoveToStartOfDocument
1013 || ke == QKeySequence::MoveToEndOfDocument
1014 || ke == QKeySequence::SelectNextWord
1015 || ke == QKeySequence::SelectPreviousWord
1016 || ke == QKeySequence::SelectStartOfLine
1017 || ke == QKeySequence::SelectEndOfLine
1018 || ke == QKeySequence::SelectStartOfBlock
1019 || ke == QKeySequence::SelectEndOfBlock
1020 || ke == QKeySequence::SelectStartOfDocument
1021 || ke == QKeySequence::SelectEndOfDocument
1022 || ke == QKeySequence::SelectAll
1034 bool QQuickTextControl::event(QEvent *e)
1036 return QObject::event(e);
1039 void QQuickTextControl::timerEvent(QTimerEvent *e)
1041 Q_D(QQuickTextControl);
1042 if (e->timerId() == d->cursorBlinkTimer.timerId()) {
1043 d->cursorOn = !d->cursorOn;
1046 // if (d->cursor.hasSelection())
1047 // d->cursorOn &= (QGuiApplication::style()->styleHint(QStyle::SH_BlinkCursorWhenTextSelected)
1051 } else if (e->timerId() == d->trippleClickTimer.timerId()) {
1052 d->trippleClickTimer.stop();
1056 void QQuickTextControl::setPlainText(const QString &text)
1058 Q_D(QQuickTextControl);
1059 d->setContent(Qt::PlainText, text);
1062 void QQuickTextControl::setHtml(const QString &text)
1064 Q_D(QQuickTextControl);
1065 d->setContent(Qt::RichText, text);
1068 void QQuickTextControlPrivate::keyPressEvent(QKeyEvent *e)
1070 Q_Q(QQuickTextControl);
1071 #ifndef QT_NO_SHORTCUT
1072 if (e == QKeySequence::SelectAll) {
1077 #ifndef QT_NO_CLIPBOARD
1078 else if (e == QKeySequence::Copy) {
1084 #endif // QT_NO_SHORTCUT
1086 if (interactionFlags & Qt::TextSelectableByKeyboard
1087 && cursorMoveKeyEvent(e))
1090 if (interactionFlags & Qt::LinksAccessibleByKeyboard) {
1091 if ((e->key() == Qt::Key_Return
1092 || e->key() == Qt::Key_Enter
1093 #ifdef QT_KEYPAD_NAVIGATION
1094 || e->key() == Qt::Key_Select
1097 && cursor.hasSelection()) {
1100 activateLinkUnderCursor();
1105 if (!(interactionFlags & Qt::TextEditable)) {
1110 if (e->key() == Qt::Key_Direction_L || e->key() == Qt::Key_Direction_R) {
1111 QTextBlockFormat fmt;
1112 fmt.setLayoutDirection((e->key() == Qt::Key_Direction_L) ? Qt::LeftToRight : Qt::RightToLeft);
1113 cursor.mergeBlockFormat(fmt);
1117 // schedule a repaint of the region of the cursor, as when we move it we
1118 // want to make sure the old cursor disappears (not noticeable when moving
1119 // only a few pixels but noticeable when jumping between cells in tables for
1123 if (e->key() == Qt::Key_Backspace && !(e->modifiers() & ~Qt::ShiftModifier)) {
1124 QTextBlockFormat blockFmt = cursor.blockFormat();
1125 QTextList *list = cursor.currentList();
1126 if (list && cursor.atBlockStart() && !cursor.hasSelection()) {
1127 list->remove(cursor.block());
1128 } else if (cursor.atBlockStart() && blockFmt.indent() > 0) {
1129 blockFmt.setIndent(blockFmt.indent() - 1);
1130 cursor.setBlockFormat(blockFmt);
1132 QTextCursor localCursor = cursor;
1133 localCursor.deletePreviousChar();
1137 #ifndef QT_NO_SHORTCUT
1138 else if (e == QKeySequence::InsertParagraphSeparator) {
1139 cursor.insertBlock();
1142 } else if (e == QKeySequence::InsertLineSeparator) {
1143 cursor.insertText(QString(QChar::LineSeparator));
1150 #ifndef QT_NO_SHORTCUT
1151 else if (e == QKeySequence::Undo) {
1154 else if (e == QKeySequence::Redo) {
1157 #ifndef QT_NO_CLIPBOARD
1158 else if (e == QKeySequence::Cut) {
1161 else if (e == QKeySequence::Paste) {
1162 QClipboard::Mode mode = QClipboard::Clipboard;
1166 else if (e == QKeySequence::Delete) {
1167 QTextCursor localCursor = cursor;
1168 localCursor.deleteChar();
1170 else if (e == QKeySequence::DeleteEndOfWord) {
1171 if (!cursor.hasSelection())
1172 cursor.movePosition(QTextCursor::NextWord, QTextCursor::KeepAnchor);
1173 cursor.removeSelectedText();
1175 else if (e == QKeySequence::DeleteStartOfWord) {
1176 if (!cursor.hasSelection())
1177 cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor);
1178 cursor.removeSelectedText();
1180 else if (e == QKeySequence::DeleteEndOfLine) {
1181 QTextBlock block = cursor.block();
1182 if (cursor.position() == block.position() + block.length() - 2)
1183 cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
1185 cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
1186 cursor.removeSelectedText();
1188 #endif // QT_NO_SHORTCUT
1196 QString text = e->text();
1197 if (!text.isEmpty() && (text.at(0).isPrint() || text.at(0) == QLatin1Char('\t'))) {
1199 // no need to call deleteChar() if we have a selection, insertText
1201 && !cursor.hasSelection()
1202 && !cursor.atBlockEnd())
1203 cursor.deleteChar();
1205 cursor.insertText(text);
1218 q->ensureCursorVisible();
1220 updateCurrentCharFormat();
1223 QVariant QQuickTextControl::loadResource(int type, const QUrl &name)
1229 if (QTextEdit *textEdit = qobject_cast<QTextEdit *>(parent())) {
1230 QUrl resolvedName = textEdit->d_func()->resolveUrl(name);
1231 return textEdit->loadResource(type, resolvedName);
1237 void QQuickTextControlPrivate::_q_updateBlock(const QTextBlock &block)
1239 Q_Q(QQuickTextControl);
1240 QRectF br = q->blockBoundingRect(block);
1241 br.setRight(qreal(INT_MAX)); // the block might have shrunk
1242 emit q->updateRequest(br);
1245 QRectF QQuickTextControlPrivate::rectForPosition(int position) const
1247 Q_Q(const QQuickTextControl);
1248 const QTextBlock block = doc->findBlock(position);
1249 if (!block.isValid())
1251 const QAbstractTextDocumentLayout *docLayout = doc->documentLayout();
1252 const QTextLayout *layout = block.layout();
1253 const QPointF layoutPos = q->blockBoundingRect(block).topLeft();
1254 int relativePos = position - block.position();
1255 if (preeditCursor != 0) {
1256 int preeditPos = layout->preeditAreaPosition();
1257 if (relativePos == preeditPos)
1258 relativePos += preeditCursor;
1259 else if (relativePos > preeditPos)
1260 relativePos += layout->preeditAreaText().length();
1262 QTextLine line = layout->lineForTextPosition(relativePos);
1267 #ifndef QT_NO_PROPERTIES
1268 cursorWidth = docLayout->property("cursorWidth").toInt(&ok);
1276 if (line.isValid()) {
1277 qreal x = line.cursorToX(relativePos);
1279 if (overwriteMode) {
1280 if (relativePos < line.textLength() - line.textStart())
1281 w = line.cursorToX(relativePos + 1) - x;
1283 w = QFontMetrics(block.layout()->font()).width(QLatin1Char(' ')); // in sync with QTextLine::draw()
1285 r = QRectF(layoutPos.x() + x, layoutPos.y() + line.y(),
1286 cursorWidth + w, line.height());
1288 r = QRectF(layoutPos.x(), layoutPos.y(), cursorWidth, 10); // #### correct height
1294 static inline bool firstFramePosLessThanCursorPos(QTextFrame *frame, int position)
1296 return frame->firstPosition() < position;
1299 static inline bool cursorPosLessThanLastFramePos(int position, QTextFrame *frame)
1301 return position < frame->lastPosition();
1304 static QRectF boundingRectOfFloatsInSelection(const QTextCursor &cursor)
1307 QTextFrame *frame = cursor.currentFrame();
1308 const QList<QTextFrame *> children = frame->childFrames();
1310 const QList<QTextFrame *>::ConstIterator firstFrame = qLowerBound(children.constBegin(), children.constEnd(),
1311 cursor.selectionStart(), firstFramePosLessThanCursorPos);
1312 const QList<QTextFrame *>::ConstIterator lastFrame = qUpperBound(children.constBegin(), children.constEnd(),
1313 cursor.selectionEnd(), cursorPosLessThanLastFramePos);
1314 for (QList<QTextFrame *>::ConstIterator it = firstFrame; it != lastFrame; ++it) {
1315 if ((*it)->frameFormat().position() != QTextFrameFormat::InFlow)
1316 r |= frame->document()->documentLayout()->frameBoundingRect(*it);
1321 QRectF QQuickTextControl::selectionRect(const QTextCursor &cursor) const
1323 Q_D(const QQuickTextControl);
1325 QRectF r = d->rectForPosition(cursor.selectionStart());
1327 if (cursor.hasComplexSelection() && cursor.currentTable()) {
1328 QTextTable *table = cursor.currentTable();
1330 r = d->doc->documentLayout()->frameBoundingRect(table);
1332 int firstRow, numRows, firstColumn, numColumns;
1333 cursor.selectedTableCells(&firstRow, &numRows, &firstColumn, &numColumns);
1335 const QTextTableCell firstCell = table->cellAt(firstRow, firstColumn);
1336 const QTextTableCell lastCell = table->cellAt(firstRow + numRows - 1, firstColumn + numColumns - 1);
1338 const QAbstractTextDocumentLayout * const layout = doc->documentLayout();
1340 QRectF tableSelRect = layout->blockBoundingRect(firstCell.firstCursorPosition().block());
1342 for (int col = firstColumn; col < firstColumn + numColumns; ++col) {
1343 const QTextTableCell cell = table->cellAt(firstRow, col);
1344 const qreal y = layout->blockBoundingRect(cell.firstCursorPosition().block()).top();
1346 tableSelRect.setTop(qMin(tableSelRect.top(), y));
1349 for (int row = firstRow; row < firstRow + numRows; ++row) {
1350 const QTextTableCell cell = table->cellAt(row, firstColumn);
1351 const qreal x = layout->blockBoundingRect(cell.firstCursorPosition().block()).left();
1353 tableSelRect.setLeft(qMin(tableSelRect.left(), x));
1356 for (int col = firstColumn; col < firstColumn + numColumns; ++col) {
1357 const QTextTableCell cell = table->cellAt(firstRow + numRows - 1, col);
1358 const qreal y = layout->blockBoundingRect(cell.lastCursorPosition().block()).bottom();
1360 tableSelRect.setBottom(qMax(tableSelRect.bottom(), y));
1363 for (int row = firstRow; row < firstRow + numRows; ++row) {
1364 const QTextTableCell cell = table->cellAt(row, firstColumn + numColumns - 1);
1365 const qreal x = layout->blockBoundingRect(cell.lastCursorPosition().block()).right();
1367 tableSelRect.setRight(qMax(tableSelRect.right(), x));
1370 r = tableSelRect.toRect();
1372 } else if (cursor.hasSelection()) {
1373 const int position = cursor.selectionStart();
1374 const int anchor = cursor.selectionEnd();
1375 const QTextBlock posBlock = d->doc->findBlock(position);
1376 const QTextBlock anchorBlock = d->doc->findBlock(anchor);
1377 if (posBlock == anchorBlock && posBlock.isValid() && posBlock.layout()->lineCount()) {
1378 const QTextLine posLine = posBlock.layout()->lineForTextPosition(position - posBlock.position());
1379 const QTextLine anchorLine = anchorBlock.layout()->lineForTextPosition(anchor - anchorBlock.position());
1381 const int firstLine = qMin(posLine.lineNumber(), anchorLine.lineNumber());
1382 const int lastLine = qMax(posLine.lineNumber(), anchorLine.lineNumber());
1383 const QTextLayout *layout = posBlock.layout();
1385 for (int i = firstLine; i <= lastLine; ++i) {
1386 r |= layout->lineAt(i).rect();
1387 r |= layout->lineAt(i).naturalTextRect(); // might be bigger in the case of wrap not enabled
1389 r.translate(blockBoundingRect(posBlock).topLeft());
1391 QRectF anchorRect = d->rectForPosition(cursor.selectionEnd());
1393 r |= boundingRectOfFloatsInSelection(cursor);
1394 QRectF frameRect(d->doc->documentLayout()->frameBoundingRect(cursor.currentFrame()));
1395 r.setLeft(frameRect.left());
1396 r.setRight(frameRect.right());
1399 r.adjust(-1, -1, 1, 1);
1405 QRectF QQuickTextControl::selectionRect() const
1407 Q_D(const QQuickTextControl);
1408 return selectionRect(d->cursor);
1411 void QQuickTextControlPrivate::mousePressEvent(QEvent *e, Qt::MouseButton button, const QPointF &pos, Qt::KeyboardModifiers modifiers,
1412 Qt::MouseButtons buttons, const QPoint &globalPos)
1414 Q_Q(QQuickTextControl);
1416 mousePressed = (interactionFlags & Qt::TextSelectableByMouse);
1417 mousePressPos = pos.toPoint();
1418 #ifndef QT_NO_DRAGANDDROP
1419 mightStartDrag = false;
1422 if (sendMouseEventToInputContext(
1423 e, QEvent::MouseButtonPress, button, pos, modifiers, buttons, globalPos)) {
1427 if (interactionFlags & Qt::LinksAccessibleByMouse) {
1428 anchorOnMousePress = q->anchorAt(pos);
1430 if (cursorIsFocusIndicator) {
1431 cursorIsFocusIndicator = false;
1433 cursor.clearSelection();
1436 if (!(button & Qt::LeftButton) ||
1437 !((interactionFlags & Qt::TextSelectableByMouse) || (interactionFlags & Qt::TextEditable))) {
1442 cursorIsFocusIndicator = false;
1443 const QTextCursor oldSelection = cursor;
1444 const int oldCursorPos = cursor.position();
1448 if (trippleClickTimer.isActive()
1449 && ((pos - trippleClickPoint).toPoint().manhattanLength() < qApp->styleHints()->startDragDistance())) {
1451 cursor.movePosition(QTextCursor::StartOfBlock);
1452 cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
1453 cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
1454 selectedBlockOnTrippleClick = cursor;
1456 anchorOnMousePress = QString();
1458 trippleClickTimer.stop();
1460 int cursorPos = q->hitTest(pos, Qt::FuzzyHit);
1461 if (cursorPos == -1) {
1466 if (modifiers == Qt::ShiftModifier && (interactionFlags & Qt::TextSelectableByMouse)) {
1467 if (wordSelectionEnabled && !selectedWordOnDoubleClick.hasSelection()) {
1468 selectedWordOnDoubleClick = cursor;
1469 selectedWordOnDoubleClick.select(QTextCursor::WordUnderCursor);
1472 if (selectedBlockOnTrippleClick.hasSelection())
1473 extendBlockwiseSelection(cursorPos);
1474 else if (selectedWordOnDoubleClick.hasSelection())
1475 extendWordwiseSelection(cursorPos, pos.x());
1476 else if (!wordSelectionEnabled)
1477 setCursorPosition(cursorPos, QTextCursor::KeepAnchor);
1481 && cursor.hasSelection()
1482 && !cursorIsFocusIndicator
1483 && cursorPos >= cursor.selectionStart()
1484 && cursorPos <= cursor.selectionEnd()
1485 && q->hitTest(pos, Qt::ExactHit) != -1) {
1486 #ifndef QT_NO_DRAGANDDROP
1487 mightStartDrag = true;
1492 setCursorPosition(cursorPos);
1496 if (interactionFlags & Qt::TextEditable) {
1497 q->ensureCursorVisible();
1498 if (cursor.position() != oldCursorPos)
1499 emit q->cursorPositionChanged();
1500 _q_updateCurrentCharFormatAndSelection();
1502 if (cursor.position() != oldCursorPos) {
1503 emit q->cursorPositionChanged();
1504 emit q->microFocusChanged();
1508 repaintOldAndNewSelection(oldSelection);
1509 hadSelectionOnMousePress = cursor.hasSelection();
1512 void QQuickTextControlPrivate::mouseMoveEvent(QEvent *e, Qt::MouseButton button, const QPointF &mousePos, Qt::KeyboardModifiers modifiers,
1513 Qt::MouseButtons buttons, const QPoint &globalPos)
1515 Q_Q(QQuickTextControl);
1517 if (interactionFlags & Qt::LinksAccessibleByMouse) {
1518 QString anchor = q->anchorAt(mousePos);
1519 if (anchor != highlightedAnchor) {
1520 highlightedAnchor = anchor;
1521 emit q->linkHovered(anchor);
1525 if (buttons & Qt::LeftButton) {
1526 const bool editable = interactionFlags & Qt::TextEditable;
1531 || selectedWordOnDoubleClick.hasSelection()
1532 || selectedBlockOnTrippleClick.hasSelection()))
1535 const QTextCursor oldSelection = cursor;
1536 const int oldCursorPos = cursor.position();
1538 if (mightStartDrag) {
1539 if ((mousePos.toPoint() - mousePressPos).manhattanLength() > qApp->styleHints()->startDragDistance())
1547 const qreal mouseX = qreal(mousePos.x());
1549 int newCursorPos = q->hitTest(mousePos, Qt::FuzzyHit);
1551 if (isPreediting()) {
1552 // note: oldCursorPos not including preedit
1553 int selectionStartPos = q->hitTest(mousePressPos, Qt::FuzzyHit);
1554 if (newCursorPos != selectionStartPos) {
1556 // commit invalidates positions
1557 newCursorPos = q->hitTest(mousePos, Qt::FuzzyHit);
1558 selectionStartPos = q->hitTest(mousePressPos, Qt::FuzzyHit);
1559 setCursorPosition(selectionStartPos);
1563 if (newCursorPos == -1)
1566 if (wordSelectionEnabled && !selectedWordOnDoubleClick.hasSelection()) {
1567 selectedWordOnDoubleClick = cursor;
1568 selectedWordOnDoubleClick.select(QTextCursor::WordUnderCursor);
1571 if (selectedBlockOnTrippleClick.hasSelection())
1572 extendBlockwiseSelection(newCursorPos);
1573 else if (selectedWordOnDoubleClick.hasSelection())
1574 extendWordwiseSelection(newCursorPos, mouseX);
1575 else if (!isPreediting())
1576 setCursorPosition(newCursorPos, QTextCursor::KeepAnchor);
1578 if (interactionFlags & Qt::TextEditable) {
1579 // don't call ensureVisible for the visible cursor to avoid jumping
1580 // scrollbars. the autoscrolling ensures smooth scrolling if necessary.
1581 //q->ensureCursorVisible();
1582 if (cursor.position() != oldCursorPos)
1583 emit q->cursorPositionChanged();
1584 _q_updateCurrentCharFormatAndSelection();
1586 qGuiApp->inputPanel()->update(Qt::ImQueryInput);
1588 //emit q->visibilityRequest(QRectF(mousePos, QSizeF(1, 1)));
1589 if (cursor.position() != oldCursorPos) {
1590 emit q->cursorPositionChanged();
1591 emit q->microFocusChanged();
1594 selectionChanged(true);
1595 repaintOldAndNewSelection(oldSelection);
1598 sendMouseEventToInputContext(e, QEvent::MouseMove, button, mousePos, modifiers, buttons, globalPos);
1601 void QQuickTextControlPrivate::mouseReleaseEvent(QEvent *e, Qt::MouseButton button, const QPointF &pos, Qt::KeyboardModifiers modifiers,
1602 Qt::MouseButtons buttons, const QPoint &globalPos)
1604 Q_Q(QQuickTextControl);
1606 if (sendMouseEventToInputContext(
1607 e, QEvent::MouseButtonRelease, button, pos, modifiers, buttons, globalPos)) {
1611 const QTextCursor oldSelection = cursor;
1612 const int oldCursorPos = cursor.position();
1614 #ifndef QT_NO_DRAGANDDROP
1615 if (mightStartDrag && (button & Qt::LeftButton)) {
1616 mousePressed = false;
1617 setCursorPosition(pos);
1618 cursor.clearSelection();
1623 mousePressed = false;
1624 #ifndef QT_NO_CLIPBOARD
1625 setClipboardSelection();
1626 selectionChanged(true);
1627 } else if (button == Qt::MidButton
1628 && (interactionFlags & Qt::TextEditable)
1629 && QGuiApplication::clipboard()->supportsSelection()) {
1630 setCursorPosition(pos);
1631 const QMimeData *md = QGuiApplication::clipboard()->mimeData(QClipboard::Selection);
1633 q->insertFromMimeData(md);
1637 repaintOldAndNewSelection(oldSelection);
1639 if (cursor.position() != oldCursorPos) {
1640 emit q->cursorPositionChanged();
1641 emit q->microFocusChanged();
1644 if (interactionFlags & Qt::LinksAccessibleByMouse) {
1645 if (!(button & Qt::LeftButton))
1648 const QString anchor = q->anchorAt(pos);
1650 if (anchor.isEmpty())
1653 if (!cursor.hasSelection()
1654 || (anchor == anchorOnMousePress && hadSelectionOnMousePress)) {
1656 const int anchorPos = q->hitTest(pos, Qt::ExactHit);
1657 if (anchorPos != -1) {
1658 cursor.setPosition(anchorPos);
1660 QString anchor = anchorOnMousePress;
1661 anchorOnMousePress = QString();
1662 activateLinkUnderCursor(anchor);
1668 void QQuickTextControlPrivate::mouseDoubleClickEvent(QEvent *e, Qt::MouseButton button, const QPointF &pos, Qt::KeyboardModifiers modifiers,
1669 Qt::MouseButtons buttons, const QPoint &globalPos)
1671 Q_Q(QQuickTextControl);
1673 if (button == Qt::LeftButton
1674 && (interactionFlags & Qt::TextSelectableByMouse)) {
1676 #ifndef QT_NO_DRAGANDDROP
1677 mightStartDrag = false;
1681 const QTextCursor oldSelection = cursor;
1682 setCursorPosition(pos);
1683 QTextLine line = currentTextLine(cursor);
1684 bool doEmit = false;
1685 if (line.isValid() && line.textLength()) {
1686 cursor.select(QTextCursor::WordUnderCursor);
1689 repaintOldAndNewSelection(oldSelection);
1691 cursorIsFocusIndicator = false;
1692 selectedWordOnDoubleClick = cursor;
1694 trippleClickPoint = pos;
1695 trippleClickTimer.start(qApp->styleHints()->mouseDoubleClickInterval(), q);
1698 #ifndef QT_NO_CLIPBOARD
1699 setClipboardSelection();
1701 emit q->cursorPositionChanged();
1703 } else if (!sendMouseEventToInputContext(e, QEvent::MouseButtonDblClick, button, pos,
1704 modifiers, buttons, globalPos)) {
1709 bool QQuickTextControlPrivate::sendMouseEventToInputContext(
1710 QEvent *e, QEvent::Type eventType, Qt::MouseButton button, const QPointF &pos,
1711 Qt::KeyboardModifiers modifiers, Qt::MouseButtons buttons, const QPoint &globalPos)
1713 #if !defined(QT_NO_IM)
1714 Q_Q(QQuickTextControl);
1718 Q_UNUSED(modifiers);
1720 Q_UNUSED(globalPos);
1722 if (contextObject && isPreediting()) {
1723 QTextLayout *layout = cursor.block().layout();
1724 int cursorPos = q->hitTest(pos, Qt::FuzzyHit) - cursor.position();
1726 if (cursorPos >= 0 && cursorPos <= layout->preeditAreaText().length()) {
1727 if (eventType == QEvent::MouseButtonRelease) {
1728 qApp->inputPanel()->invokeAction(QInputPanel::Click, cursorPos);
1736 Q_UNUSED(eventType);
1739 Q_UNUSED(modifiers);
1741 Q_UNUSED(globalPos);
1746 bool QQuickTextControlPrivate::dragEnterEvent(QEvent *e, const QMimeData *mimeData)
1748 Q_Q(QQuickTextControl);
1749 if (!(interactionFlags & Qt::TextEditable) || !q->canInsertFromMimeData(mimeData)) {
1754 dndFeedbackCursor = QTextCursor();
1756 return true; // accept proposed action
1759 void QQuickTextControlPrivate::dragLeaveEvent()
1761 Q_Q(QQuickTextControl);
1763 const QRectF crect = q->cursorRect(dndFeedbackCursor);
1764 dndFeedbackCursor = QTextCursor();
1766 if (crect.isValid())
1767 emit q->updateRequest(crect);
1770 bool QQuickTextControlPrivate::dragMoveEvent(QEvent *e, const QMimeData *mimeData, const QPointF &pos)
1772 Q_Q(QQuickTextControl);
1773 if (!(interactionFlags & Qt::TextEditable) || !q->canInsertFromMimeData(mimeData)) {
1778 const int cursorPos = q->hitTest(pos, Qt::FuzzyHit);
1779 if (cursorPos != -1) {
1780 QRectF crect = q->cursorRect(dndFeedbackCursor);
1781 if (crect.isValid())
1782 emit q->updateRequest(crect);
1784 dndFeedbackCursor = cursor;
1785 dndFeedbackCursor.setPosition(cursorPos);
1787 crect = q->cursorRect(dndFeedbackCursor);
1788 emit q->updateRequest(crect);
1791 return true; // accept proposed action
1794 bool QQuickTextControlPrivate::dropEvent(const QMimeData *mimeData, const QPointF &pos, Qt::DropAction dropAction, QObject *source)
1796 Q_Q(QQuickTextControl);
1797 dndFeedbackCursor = QTextCursor();
1799 if (!(interactionFlags & Qt::TextEditable) || !q->canInsertFromMimeData(mimeData))
1804 QTextCursor insertionCursor = q->cursorForPosition(pos);
1805 insertionCursor.beginEditBlock();
1807 if (dropAction == Qt::MoveAction && source == contextObject)
1808 cursor.removeSelectedText();
1810 cursor = insertionCursor;
1811 q->insertFromMimeData(mimeData);
1812 insertionCursor.endEditBlock();
1813 q->ensureCursorVisible();
1814 return true; // accept proposed action
1817 void QQuickTextControlPrivate::inputMethodEvent(QInputMethodEvent *e)
1819 Q_Q(QQuickTextControl);
1820 if (!(interactionFlags & Qt::TextEditable) || cursor.isNull()) {
1824 bool isGettingInput = !e->commitString().isEmpty()
1825 || e->preeditString() != cursor.block().layout()->preeditAreaText()
1826 || e->replacementLength() > 0;
1827 bool forceSelectionChanged = false;
1829 cursor.beginEditBlock();
1830 if (isGettingInput) {
1831 cursor.removeSelectedText();
1834 // insert commit string
1835 if (!e->commitString().isEmpty() || e->replacementLength()) {
1836 QTextCursor c = cursor;
1837 c.setPosition(c.position() + e->replacementStart());
1838 c.setPosition(c.position() + e->replacementLength(), QTextCursor::KeepAnchor);
1839 c.insertText(e->commitString());
1842 for (int i = 0; i < e->attributes().size(); ++i) {
1843 const QInputMethodEvent::Attribute &a = e->attributes().at(i);
1844 if (a.type == QInputMethodEvent::Selection) {
1845 QTextCursor oldCursor = cursor;
1846 int blockStart = a.start + cursor.block().position();
1847 cursor.setPosition(blockStart, QTextCursor::MoveAnchor);
1848 cursor.setPosition(blockStart + a.length, QTextCursor::KeepAnchor);
1849 q->ensureCursorVisible();
1850 repaintOldAndNewSelection(oldCursor);
1851 forceSelectionChanged = true;
1855 QTextBlock block = cursor.block();
1856 QTextLayout *layout = block.layout();
1858 layout->setPreeditArea(cursor.position() - block.position(), e->preeditString());
1859 QList<QTextLayout::FormatRange> overrides;
1860 const int oldPreeditCursor = preeditCursor;
1861 preeditCursor = e->preeditString().length();
1863 for (int i = 0; i < e->attributes().size(); ++i) {
1864 const QInputMethodEvent::Attribute &a = e->attributes().at(i);
1865 if (a.type == QInputMethodEvent::Cursor) {
1866 preeditCursor = a.start;
1867 hideCursor = !a.length;
1868 } else if (a.type == QInputMethodEvent::TextFormat) {
1869 QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat();
1871 QTextLayout::FormatRange o;
1872 o.start = a.start + cursor.position() - block.position();
1873 o.length = a.length;
1875 overrides.append(o);
1879 layout->setAdditionalFormats(overrides);
1880 tentativeCommit = e->tentativeCommitString();
1882 cursor.endEditBlock();
1884 QTextCursorPrivate *cursor_d = QTextCursorPrivate::getPrivate(&cursor);
1887 if (oldPreeditCursor != preeditCursor)
1888 emit q->microFocusChanged();
1889 selectionChanged(forceSelectionChanged);
1892 QVariant QQuickTextControl::inputMethodQuery(Qt::InputMethodQuery property) const
1894 Q_D(const QQuickTextControl);
1895 QTextBlock block = d->cursor.block();
1897 case Qt::ImCursorRectangle:
1898 return cursorRect();
1900 return QVariant(d->cursor.charFormat().font());
1901 case Qt::ImCursorPosition:
1902 return QVariant(d->cursor.position() - block.position());
1903 case Qt::ImSurroundingText:
1904 return QVariant(block.text());
1905 case Qt::ImCurrentSelection:
1906 return QVariant(d->cursor.selectedText());
1907 case Qt::ImMaximumTextLength:
1908 return QVariant(); // No limit.
1909 case Qt::ImAnchorPosition:
1910 return QVariant(d->cursor.anchor() - block.position());
1916 void QQuickTextControl::setFocus(bool focus, Qt::FocusReason reason)
1918 QFocusEvent ev(focus ? QEvent::FocusIn : QEvent::FocusOut,
1923 void QQuickTextControlPrivate::focusEvent(QFocusEvent *e)
1925 Q_Q(QQuickTextControl);
1926 emit q->updateRequest(q->selectionRect());
1927 if (e->gotFocus()) {
1928 #ifdef QT_KEYPAD_NAVIGATION
1929 if (!QGuiApplication::keypadNavigationEnabled() || (hasEditFocus && (e->reason() == Qt::PopupFocusReason
1932 setBlinkingCursorEnabled(interactionFlags & (Qt::TextEditable | Qt::TextSelectableByKeyboard));
1933 #ifdef QT_KEYPAD_NAVIGATION
1938 setBlinkingCursorEnabled(false);
1940 if (cursorIsFocusIndicator
1941 && e->reason() != Qt::ActiveWindowFocusReason
1942 && e->reason() != Qt::PopupFocusReason
1943 && cursor.hasSelection()) {
1944 cursor.clearSelection();
1947 hasFocus = e->gotFocus();
1950 QString QQuickTextControlPrivate::anchorForCursor(const QTextCursor &anchorCursor) const
1952 if (anchorCursor.hasSelection()) {
1953 QTextCursor cursor = anchorCursor;
1954 if (cursor.selectionStart() != cursor.position())
1955 cursor.setPosition(cursor.selectionStart());
1956 cursor.movePosition(QTextCursor::NextCharacter);
1957 QTextCharFormat fmt = cursor.charFormat();
1958 if (fmt.isAnchor() && fmt.hasProperty(QTextFormat::AnchorHref))
1959 return fmt.stringProperty(QTextFormat::AnchorHref);
1964 #ifdef QT_KEYPAD_NAVIGATION
1965 void QQuickTextControlPrivate::editFocusEvent(QEvent *e)
1967 Q_Q(QQuickTextControl);
1969 if (QGuiApplication::keypadNavigationEnabled()) {
1970 if (e->type() == QEvent::EnterEditFocus && interactionFlags & Qt::TextEditable) {
1971 const QTextCursor oldSelection = cursor;
1972 const int oldCursorPos = cursor.position();
1973 const bool moved = cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
1974 q->ensureCursorVisible();
1976 if (cursor.position() != oldCursorPos)
1977 emit q->cursorPositionChanged();
1978 emit q->microFocusChanged();
1981 repaintOldAndNewSelection(oldSelection);
1983 setBlinkingCursorEnabled(true);
1985 setBlinkingCursorEnabled(false);
1988 hasEditFocus = e->type() == QEvent::EnterEditFocus ? true : false;
1992 QTextCursor QQuickTextControl::cursorForPosition(const QPointF &pos) const
1994 Q_D(const QQuickTextControl);
1995 int cursorPos = hitTest(pos, Qt::FuzzyHit);
1996 if (cursorPos == -1)
1998 QTextCursor c(d->doc);
1999 c.setPosition(cursorPos);
2003 QRectF QQuickTextControl::cursorRect(const QTextCursor &cursor) const
2005 Q_D(const QQuickTextControl);
2006 if (cursor.isNull())
2009 return d->rectForPosition(cursor.position());
2012 QRectF QQuickTextControl::cursorRect() const
2014 Q_D(const QQuickTextControl);
2015 return cursorRect(d->cursor);
2018 QRectF QQuickTextControlPrivate::cursorRectPlusUnicodeDirectionMarkers(const QTextCursor &cursor) const
2020 if (cursor.isNull())
2023 return rectForPosition(cursor.position()).adjusted(-4, 0, 4, 0);
2026 QString QQuickTextControl::anchorAt(const QPointF &pos) const
2028 Q_D(const QQuickTextControl);
2029 return d->doc->documentLayout()->anchorAt(pos);
2032 QString QQuickTextControl::anchorAtCursor() const
2034 Q_D(const QQuickTextControl);
2036 return d->anchorForCursor(d->cursor);
2039 bool QQuickTextControl::overwriteMode() const
2041 Q_D(const QQuickTextControl);
2042 return d->overwriteMode;
2045 void QQuickTextControl::setOverwriteMode(bool overwrite)
2047 Q_D(QQuickTextControl);
2048 d->overwriteMode = overwrite;
2051 int QQuickTextControl::cursorWidth() const
2053 #ifndef QT_NO_PROPERTIES
2054 Q_D(const QQuickTextControl);
2055 return d->doc->documentLayout()->property("cursorWidth").toInt();
2061 void QQuickTextControl::setCursorWidth(int width)
2063 Q_D(QQuickTextControl);
2064 #ifdef QT_NO_PROPERTIES
2068 width = textCursorWidth;
2069 d->doc->documentLayout()->setProperty("cursorWidth", width);
2074 bool QQuickTextControl::acceptRichText() const
2076 Q_D(const QQuickTextControl);
2077 return d->acceptRichText;
2080 void QQuickTextControl::setAcceptRichText(bool accept)
2082 Q_D(QQuickTextControl);
2083 d->acceptRichText = accept;
2086 void QQuickTextControl::setExtraSelections(const QVector<QAbstractTextDocumentLayout::Selection> &selections)
2088 Q_D(QQuickTextControl);
2090 QHash<int, int> hash;
2091 for (int i = 0; i < d->extraSelections.count(); ++i) {
2092 const QAbstractTextDocumentLayout::Selection &esel = d->extraSelections.at(i);
2093 hash.insertMulti(esel.cursor.anchor(), i);
2096 for (int i = 0; i < selections.count(); ++i) {
2097 const QAbstractTextDocumentLayout::Selection &sel = selections.at(i);
2098 QHash<int, int>::iterator it = hash.find(sel.cursor.anchor());
2099 if (it != hash.end()) {
2100 const QAbstractTextDocumentLayout::Selection &esel = d->extraSelections.at(it.value());
2101 if (esel.cursor.position() == sel.cursor.position()
2102 && esel.format == sel.format) {
2107 QRectF r = selectionRect(sel.cursor);
2108 if (sel.format.boolProperty(QTextFormat::FullWidthSelection)) {
2110 r.setWidth(qreal(INT_MAX));
2112 emit updateRequest(r);
2115 for (QHash<int, int>::iterator it = hash.begin(); it != hash.end(); ++it) {
2116 const QAbstractTextDocumentLayout::Selection &esel = d->extraSelections.at(it.value());
2117 QRectF r = selectionRect(esel.cursor);
2118 if (esel.format.boolProperty(QTextFormat::FullWidthSelection)) {
2120 r.setWidth(qreal(INT_MAX));
2122 emit updateRequest(r);
2125 d->extraSelections = selections;
2128 QVector<QAbstractTextDocumentLayout::Selection> QQuickTextControl::extraSelections() const
2130 Q_D(const QQuickTextControl);
2131 return d->extraSelections;
2134 void QQuickTextControl::setTextWidth(qreal width)
2136 Q_D(QQuickTextControl);
2137 d->doc->setTextWidth(width);
2140 qreal QQuickTextControl::textWidth() const
2142 Q_D(const QQuickTextControl);
2143 return d->doc->textWidth();
2146 QSizeF QQuickTextControl::size() const
2148 Q_D(const QQuickTextControl);
2149 return d->doc->size();
2152 void QQuickTextControl::setOpenExternalLinks(bool open)
2154 Q_D(QQuickTextControl);
2155 d->openExternalLinks = open;
2158 bool QQuickTextControl::openExternalLinks() const
2160 Q_D(const QQuickTextControl);
2161 return d->openExternalLinks;
2164 bool QQuickTextControl::ignoreUnusedNavigationEvents() const
2166 Q_D(const QQuickTextControl);
2167 return d->ignoreUnusedNavigationEvents;
2170 void QQuickTextControl::setIgnoreUnusedNavigationEvents(bool ignore)
2172 Q_D(QQuickTextControl);
2173 d->ignoreUnusedNavigationEvents = ignore;
2176 void QQuickTextControl::moveCursor(QTextCursor::MoveOperation op, QTextCursor::MoveMode mode)
2178 Q_D(QQuickTextControl);
2179 const QTextCursor oldSelection = d->cursor;
2180 const bool moved = d->cursor.movePosition(op, mode);
2181 d->_q_updateCurrentCharFormatAndSelection();
2182 ensureCursorVisible();
2183 d->repaintOldAndNewSelection(oldSelection);
2185 emit cursorPositionChanged();
2188 bool QQuickTextControl::canPaste() const
2190 #ifndef QT_NO_CLIPBOARD
2191 Q_D(const QQuickTextControl);
2192 if (d->interactionFlags & Qt::TextEditable) {
2193 const QMimeData *md = QGuiApplication::clipboard()->mimeData();
2194 return md && canInsertFromMimeData(md);
2200 void QQuickTextControl::setCursorIsFocusIndicator(bool b)
2202 Q_D(QQuickTextControl);
2203 d->cursorIsFocusIndicator = b;
2207 bool QQuickTextControl::cursorIsFocusIndicator() const
2209 Q_D(const QQuickTextControl);
2210 return d->cursorIsFocusIndicator;
2214 void QQuickTextControl::setDragEnabled(bool enabled)
2216 Q_D(QQuickTextControl);
2217 d->dragEnabled = enabled;
2220 bool QQuickTextControl::isDragEnabled() const
2222 Q_D(const QQuickTextControl);
2223 return d->dragEnabled;
2226 void QQuickTextControl::setWordSelectionEnabled(bool enabled)
2228 Q_D(QQuickTextControl);
2229 d->wordSelectionEnabled = enabled;
2232 bool QQuickTextControl::isWordSelectionEnabled() const
2234 Q_D(const QQuickTextControl);
2235 return d->wordSelectionEnabled;
2238 QMimeData *QQuickTextControl::createMimeDataFromSelection() const
2240 Q_D(const QQuickTextControl);
2241 const QTextDocumentFragment fragment(d->cursor);
2242 return new QQuickTextEditMimeData(fragment);
2245 bool QQuickTextControl::canInsertFromMimeData(const QMimeData *source) const
2247 Q_D(const QQuickTextControl);
2248 if (d->acceptRichText)
2249 return (source->hasText() && !source->text().isEmpty())
2250 || source->hasHtml()
2251 || source->hasFormat(QLatin1String("application/x-qrichtext"))
2252 || source->hasFormat(QLatin1String("application/x-qt-richtext"));
2254 return source->hasText() && !source->text().isEmpty();
2257 void QQuickTextControl::insertFromMimeData(const QMimeData *source)
2259 Q_D(QQuickTextControl);
2260 if (!(d->interactionFlags & Qt::TextEditable) || !source)
2263 bool hasData = false;
2264 QTextDocumentFragment fragment;
2265 #ifndef QT_NO_TEXTHTMLPARSER
2266 if (source->hasFormat(QLatin1String("application/x-qrichtext")) && d->acceptRichText) {
2267 // x-qrichtext is always UTF-8 (taken from Qt3 since we don't use it anymore).
2268 QString richtext = QString::fromUtf8(source->data(QLatin1String("application/x-qrichtext")));
2269 richtext.prepend(QLatin1String("<meta name=\"qrichtext\" content=\"1\" />"));
2270 fragment = QTextDocumentFragment::fromHtml(richtext, d->doc);
2272 } else if (source->hasHtml() && d->acceptRichText) {
2273 fragment = QTextDocumentFragment::fromHtml(source->html(), d->doc);
2276 QString text = source->text();
2277 if (!text.isNull()) {
2278 fragment = QTextDocumentFragment::fromPlainText(text);
2283 fragment = QTextDocumentFragment::fromPlainText(source->text());
2284 #endif // QT_NO_TEXTHTMLPARSER
2287 d->cursor.insertFragment(fragment);
2288 ensureCursorVisible();
2291 bool QQuickTextControl::findNextPrevAnchor(const QTextCursor &startCursor, bool next, QTextCursor &newAnchor)
2293 Q_D(QQuickTextControl);
2295 int anchorStart = -1;
2300 const int startPos = startCursor.selectionEnd();
2302 QTextBlock block = d->doc->findBlock(startPos);
2303 QTextBlock::Iterator it = block.begin();
2305 while (!it.atEnd() && it.fragment().position() < startPos)
2308 while (block.isValid()) {
2312 for (; !it.atEnd(); ++it) {
2313 const QTextFragment fragment = it.fragment();
2314 const QTextCharFormat fmt = fragment.charFormat();
2316 if (fmt.isAnchor() && fmt.hasProperty(QTextFormat::AnchorHref)) {
2317 anchorStart = fragment.position();
2318 anchorHref = fmt.anchorHref();
2323 if (anchorStart != -1) {
2326 // find next non-anchor fragment
2327 for (; !it.atEnd(); ++it) {
2328 const QTextFragment fragment = it.fragment();
2329 const QTextCharFormat fmt = fragment.charFormat();
2331 if (!fmt.isAnchor() || fmt.anchorHref() != anchorHref) {
2332 anchorEnd = fragment.position();
2337 if (anchorEnd == -1)
2338 anchorEnd = block.position() + block.length() - 1;
2340 // make found selection
2344 block = block.next();
2348 int startPos = startCursor.selectionStart();
2352 QTextBlock block = d->doc->findBlock(startPos);
2353 QTextBlock::Iterator blockStart = block.begin();
2354 QTextBlock::Iterator it = block.end();
2356 if (startPos == block.position()) {
2360 if (it == blockStart) {
2361 it = QTextBlock::Iterator();
2362 block = QTextBlock();
2366 } while (!it.atEnd() && it.fragment().position() + it.fragment().length() - 1 > startPos);
2369 while (block.isValid()) {
2374 const QTextFragment fragment = it.fragment();
2375 const QTextCharFormat fmt = fragment.charFormat();
2377 if (fmt.isAnchor() && fmt.hasProperty(QTextFormat::AnchorHref)) {
2378 anchorStart = fragment.position() + fragment.length();
2379 anchorHref = fmt.anchorHref();
2383 if (it == blockStart)
2384 it = QTextBlock::Iterator();
2387 } while (!it.atEnd());
2390 if (anchorStart != -1 && !it.atEnd()) {
2394 const QTextFragment fragment = it.fragment();
2395 const QTextCharFormat fmt = fragment.charFormat();
2397 if (!fmt.isAnchor() || fmt.anchorHref() != anchorHref) {
2398 anchorEnd = fragment.position() + fragment.length();
2402 if (it == blockStart)
2403 it = QTextBlock::Iterator();
2406 } while (!it.atEnd());
2408 if (anchorEnd == -1)
2409 anchorEnd = qMax(0, block.position());
2414 block = block.previous();
2416 if (it != block.begin())
2418 blockStart = block.begin();
2423 if (anchorStart != -1 && anchorEnd != -1) {
2424 newAnchor = d->cursor;
2425 newAnchor.setPosition(anchorStart);
2426 newAnchor.setPosition(anchorEnd, QTextCursor::KeepAnchor);
2433 void QQuickTextControlPrivate::activateLinkUnderCursor(QString href)
2435 QTextCursor oldCursor = cursor;
2437 if (href.isEmpty()) {
2438 QTextCursor tmp = cursor;
2439 if (tmp.selectionStart() != tmp.position())
2440 tmp.setPosition(tmp.selectionStart());
2441 tmp.movePosition(QTextCursor::NextCharacter);
2442 href = tmp.charFormat().anchorHref();
2447 if (!cursor.hasSelection()) {
2448 QTextBlock block = cursor.block();
2449 const int cursorPos = cursor.position();
2451 QTextBlock::Iterator it = block.begin();
2452 QTextBlock::Iterator linkFragment;
2454 for (; !it.atEnd(); ++it) {
2455 QTextFragment fragment = it.fragment();
2456 const int fragmentPos = fragment.position();
2457 if (fragmentPos <= cursorPos &&
2458 fragmentPos + fragment.length() > cursorPos) {
2464 if (!linkFragment.atEnd()) {
2466 cursor.setPosition(it.fragment().position());
2467 if (it != block.begin()) {
2470 QTextFragment fragment = it.fragment();
2471 if (fragment.charFormat().anchorHref() != href)
2473 cursor.setPosition(fragment.position());
2474 } while (it != block.begin());
2477 for (it = linkFragment; !it.atEnd(); ++it) {
2478 QTextFragment fragment = it.fragment();
2479 if (fragment.charFormat().anchorHref() != href)
2481 cursor.setPosition(fragment.position() + fragment.length(), QTextCursor::KeepAnchor);
2487 cursorIsFocusIndicator = true;
2489 cursorIsFocusIndicator = false;
2490 cursor.clearSelection();
2492 repaintOldAndNewSelection(oldCursor);
2494 #if 0 // ###ndef QT_NO_DESKTOPSERVICES
2495 if (openExternalLinks)
2496 QDesktopServices::openUrl(href);
2499 emit q_func()->linkActivated(href);
2502 bool QQuickTextControlPrivate::isPreediting() const
2504 QTextLayout *layout = cursor.block().layout();
2505 if (layout && !layout->preeditAreaText().isEmpty())
2511 void QQuickTextControlPrivate::commitPreedit()
2513 if (!isPreediting())
2516 cursor.beginEditBlock();
2517 qApp->inputPanel()->reset();
2519 if (!tentativeCommit.isEmpty()) {
2520 cursor.insertText(tentativeCommit);
2521 tentativeCommit.clear();
2525 QTextBlock block = cursor.block();
2526 QTextLayout *layout = block.layout();
2527 layout->setPreeditArea(-1, QString());
2528 layout->clearAdditionalFormats();
2529 cursor.endEditBlock();
2532 bool QQuickTextControl::setFocusToNextOrPreviousAnchor(bool next)
2534 Q_D(QQuickTextControl);
2536 if (!(d->interactionFlags & Qt::LinksAccessibleByKeyboard))
2539 QRectF crect = selectionRect();
2540 emit updateRequest(crect);
2542 // If we don't have a current anchor, we start from the start/end
2543 if (!d->cursor.hasSelection()) {
2544 d->cursor = QTextCursor(d->doc);
2546 d->cursor.movePosition(QTextCursor::Start);
2548 d->cursor.movePosition(QTextCursor::End);
2551 QTextCursor newAnchor;
2552 if (findNextPrevAnchor(d->cursor, next, newAnchor)) {
2553 d->cursor = newAnchor;
2554 d->cursorIsFocusIndicator = true;
2556 d->cursor.clearSelection();
2559 if (d->cursor.hasSelection()) {
2560 crect = selectionRect();
2561 emit updateRequest(crect);
2562 emit visibilityRequest(crect);
2569 bool QQuickTextControl::setFocusToAnchor(const QTextCursor &newCursor)
2571 Q_D(QQuickTextControl);
2573 if (!(d->interactionFlags & Qt::LinksAccessibleByKeyboard))
2576 // Verify that this is an anchor.
2577 const QString anchorHref = d->anchorForCursor(newCursor);
2578 if (anchorHref.isEmpty())
2582 QRectF crect = selectionRect();
2583 emit updateRequest(crect);
2585 d->cursor.setPosition(newCursor.selectionStart());
2586 d->cursor.setPosition(newCursor.selectionEnd(), QTextCursor::KeepAnchor);
2587 d->cursorIsFocusIndicator = true;
2589 crect = selectionRect();
2590 emit updateRequest(crect);
2591 emit visibilityRequest(crect);
2595 void QQuickTextControl::setTextInteractionFlags(Qt::TextInteractionFlags flags)
2597 Q_D(QQuickTextControl);
2598 if (flags == d->interactionFlags)
2600 d->interactionFlags = flags;
2603 d->setBlinkingCursorEnabled(flags & (Qt::TextEditable | Qt::TextSelectableByKeyboard));
2606 Qt::TextInteractionFlags QQuickTextControl::textInteractionFlags() const
2608 Q_D(const QQuickTextControl);
2609 return d->interactionFlags;
2612 void QQuickTextControl::mergeCurrentCharFormat(const QTextCharFormat &modifier)
2614 Q_D(QQuickTextControl);
2615 d->cursor.mergeCharFormat(modifier);
2616 d->updateCurrentCharFormat();
2619 void QQuickTextControl::setCurrentCharFormat(const QTextCharFormat &format)
2621 Q_D(QQuickTextControl);
2622 d->cursor.setCharFormat(format);
2623 d->updateCurrentCharFormat();
2626 QTextCharFormat QQuickTextControl::currentCharFormat() const
2628 Q_D(const QQuickTextControl);
2629 return d->cursor.charFormat();
2632 void QQuickTextControl::insertPlainText(const QString &text)
2634 Q_D(QQuickTextControl);
2635 d->cursor.insertText(text);
2638 #ifndef QT_NO_TEXTHTMLPARSER
2639 void QQuickTextControl::insertHtml(const QString &text)
2641 Q_D(QQuickTextControl);
2642 d->cursor.insertHtml(text);
2644 #endif // QT_NO_TEXTHTMLPARSER
2646 QPointF QQuickTextControl::anchorPosition(const QString &name) const
2648 Q_D(const QQuickTextControl);
2653 for (QTextBlock block = d->doc->begin(); block.isValid(); block = block.next()) {
2654 QTextCharFormat format = block.charFormat();
2655 if (format.isAnchor() && format.anchorNames().contains(name)) {
2656 r = d->rectForPosition(block.position());
2660 for (QTextBlock::Iterator it = block.begin(); !it.atEnd(); ++it) {
2661 QTextFragment fragment = it.fragment();
2662 format = fragment.charFormat();
2663 if (format.isAnchor() && format.anchorNames().contains(name)) {
2664 r = d->rectForPosition(fragment.position());
2665 block = QTextBlock();
2672 return QPointF(0, r.top());
2675 void QQuickTextControl::adjustSize()
2677 Q_D(QQuickTextControl);
2678 d->doc->adjustSize();
2681 bool QQuickTextControl::find(const QString &exp, QTextDocument::FindFlags options)
2683 Q_D(QQuickTextControl);
2684 QTextCursor search = d->doc->find(exp, d->cursor, options);
2685 if (search.isNull())
2688 setTextCursor(search);
2694 void QQuickTextControlPrivate::append(const QString &text, Qt::TextFormat format)
2696 QTextCursor tmp(doc);
2697 tmp.beginEditBlock();
2698 tmp.movePosition(QTextCursor::End);
2700 if (!doc->isEmpty())
2701 tmp.insertBlock(cursor.blockFormat(), cursor.charFormat());
2703 tmp.setCharFormat(cursor.charFormat());
2705 // preserve the char format
2706 QTextCharFormat oldCharFormat = cursor.charFormat();
2708 #ifndef QT_NO_TEXTHTMLPARSER
2709 if (format == Qt::RichText || (format == Qt::AutoText && Qt::mightBeRichText(text))) {
2710 tmp.insertHtml(text);
2712 tmp.insertText(text);
2715 tmp.insertText(text);
2716 #endif // QT_NO_TEXTHTMLPARSER
2717 if (!cursor.hasSelection())
2718 cursor.setCharFormat(oldCharFormat);
2723 void QQuickTextControl::append(const QString &text)
2725 Q_D(QQuickTextControl);
2726 d->append(text, Qt::AutoText);
2729 void QQuickTextControl::appendHtml(const QString &html)
2731 Q_D(QQuickTextControl);
2732 d->append(html, Qt::RichText);
2735 void QQuickTextControl::appendPlainText(const QString &text)
2737 Q_D(QQuickTextControl);
2738 d->append(text, Qt::PlainText);
2741 QString QQuickTextControl::toPlainText() const
2743 Q_D(const QQuickTextControl);
2744 QString plainText = document()->toPlainText();
2745 if (!d->tentativeCommit.isEmpty())
2746 plainText.insert(textCursor().position(), d->tentativeCommit);
2750 #ifndef QT_NO_TEXTHTMLPARSER
2751 QString QQuickTextControl::toHtml() const
2753 // note: currently not including tentative commit
2754 return document()->toHtml();
2758 void QQuickTextControl::ensureCursorVisible()
2760 Q_D(QQuickTextControl);
2761 QRectF crect = d->rectForPosition(d->cursor.position()).adjusted(-5, 0, 5, 0);
2762 emit visibilityRequest(crect);
2763 emit microFocusChanged();
2766 QPalette QQuickTextControl::palette() const
2768 Q_D(const QQuickTextControl);
2772 void QQuickTextControl::setPalette(const QPalette &pal)
2774 Q_D(QQuickTextControl);
2778 bool QQuickTextControl::cursorOn() const
2780 Q_D(const QQuickTextControl);
2784 QAbstractTextDocumentLayout::PaintContext QQuickTextControl::getPaintContext() const
2786 Q_D(const QQuickTextControl);
2788 QAbstractTextDocumentLayout::PaintContext ctx;
2790 ctx.selections = d->extraSelections;
2791 ctx.palette = d->palette;
2792 if (d->cursorOn && d->isEnabled) {
2794 ctx.cursorPosition = -1;
2795 else if (d->preeditCursor != 0)
2796 ctx.cursorPosition = - (d->preeditCursor + 2);
2798 ctx.cursorPosition = d->cursor.position();
2801 if (!d->dndFeedbackCursor.isNull())
2802 ctx.cursorPosition = d->dndFeedbackCursor.position();
2803 #ifdef QT_KEYPAD_NAVIGATION
2804 if (!QGuiApplication::keypadNavigationEnabled() || d->hasEditFocus)
2806 if (d->cursor.hasSelection()) {
2807 QAbstractTextDocumentLayout::Selection selection;
2808 selection.cursor = d->cursor;
2809 if (0 && d->cursorIsFocusIndicator) {
2813 opt.palette = ctx.palette;
2814 QStyleHintReturnVariant ret;
2815 QStyle *style = QGuiApplication::style();
2817 style = widget->style();
2818 style->styleHint(QStyle::SH_TextControl_FocusIndicatorTextCharFormat, &opt, widget, &ret);
2819 selection.format = qvariant_cast<QTextFormat>(ret.variant).toCharFormat();
2822 QPalette::ColorGroup cg = d->hasFocus ? QPalette::Active : QPalette::Inactive;
2823 selection.format.setBackground(ctx.palette.brush(cg, QPalette::Highlight));
2824 selection.format.setForeground(ctx.palette.brush(cg, QPalette::HighlightedText));
2825 if (fullWidthSelection)
2826 selection.format.setProperty(QTextFormat::FullWidthSelection, true);
2828 ctx.selections.append(selection);
2834 void QQuickTextControl::drawContents(QPainter *p, const QRectF &rect)
2836 Q_D(QQuickTextControl);
2838 QAbstractTextDocumentLayout::PaintContext ctx = getPaintContext();
2840 p->setClipRect(rect, Qt::IntersectClip);
2843 d->doc->documentLayout()->draw(p, ctx);
2847 void QQuickTextControlPrivate::_q_copyLink()
2849 #ifndef QT_NO_CLIPBOARD
2850 QMimeData *md = new QMimeData;
2851 md->setText(linkToCopy);
2852 QGuiApplication::clipboard()->setMimeData(md);
2856 int QQuickTextControl::hitTest(const QPointF &point, Qt::HitTestAccuracy accuracy) const
2858 Q_D(const QQuickTextControl);
2859 return d->doc->documentLayout()->hitTest(point, accuracy);
2862 QRectF QQuickTextControl::blockBoundingRect(const QTextBlock &block) const
2864 Q_D(const QQuickTextControl);
2865 return d->doc->documentLayout()->blockBoundingRect(block);
2870 QStringList QQuickTextEditMimeData::formats() const
2872 if (!fragment.isEmpty())
2873 return QStringList() << QString::fromLatin1("text/plain") << QString::fromLatin1("text/html")
2874 #ifndef QT_NO_TEXTODFWRITER
2875 << QString::fromLatin1("application/vnd.oasis.opendocument.text")
2879 return QMimeData::formats();
2882 QVariant QQuickTextEditMimeData::retrieveData(const QString &mimeType, QVariant::Type type) const
2884 if (!fragment.isEmpty())
2886 return QMimeData::retrieveData(mimeType, type);
2889 void QQuickTextEditMimeData::setup() const
2891 QQuickTextEditMimeData *that = const_cast<QQuickTextEditMimeData *>(this);
2892 #ifndef QT_NO_TEXTHTMLPARSER
2893 that->setData(QLatin1String("text/html"), fragment.toHtml("utf-8").toUtf8());
2895 #ifndef QT_NO_TEXTODFWRITER
2898 QTextDocumentWriter writer(&buffer, "ODF");
2899 writer.write(fragment);
2901 that->setData(QLatin1String("application/vnd.oasis.opendocument.text"), buffer.data());
2904 that->setText(fragment.toPlainText());
2905 fragment = QTextDocumentFragment();
2911 #include "moc_qquicktextcontrol_p.cpp"
2913 #endif // QT_NO_TEXTCONTROL