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 if (sendMouseEventToInputContext(
1417 e, QEvent::MouseButtonPress, button, pos, modifiers, buttons, globalPos)) {
1421 if (interactionFlags & Qt::LinksAccessibleByMouse) {
1422 anchorOnMousePress = q->anchorAt(pos);
1424 if (cursorIsFocusIndicator) {
1425 cursorIsFocusIndicator = false;
1427 cursor.clearSelection();
1430 if (!(button & Qt::LeftButton) ||
1431 !((interactionFlags & Qt::TextSelectableByMouse) || (interactionFlags & Qt::TextEditable))) {
1436 cursorIsFocusIndicator = false;
1437 const QTextCursor oldSelection = cursor;
1438 const int oldCursorPos = cursor.position();
1440 mousePressed = (interactionFlags & Qt::TextSelectableByMouse);
1441 #ifndef QT_NO_DRAGANDDROP
1442 mightStartDrag = false;
1445 if (trippleClickTimer.isActive()
1446 && ((pos - trippleClickPoint).toPoint().manhattanLength() < qApp->styleHints()->startDragDistance())) {
1448 cursor.movePosition(QTextCursor::StartOfBlock);
1449 cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
1450 cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
1451 selectedBlockOnTrippleClick = cursor;
1453 anchorOnMousePress = QString();
1455 trippleClickTimer.stop();
1457 int cursorPos = q->hitTest(pos, Qt::FuzzyHit);
1458 if (cursorPos == -1) {
1463 if (modifiers == Qt::ShiftModifier && (interactionFlags & Qt::TextSelectableByMouse)) {
1464 if (wordSelectionEnabled && !selectedWordOnDoubleClick.hasSelection()) {
1465 selectedWordOnDoubleClick = cursor;
1466 selectedWordOnDoubleClick.select(QTextCursor::WordUnderCursor);
1469 if (selectedBlockOnTrippleClick.hasSelection())
1470 extendBlockwiseSelection(cursorPos);
1471 else if (selectedWordOnDoubleClick.hasSelection())
1472 extendWordwiseSelection(cursorPos, pos.x());
1473 else if (!wordSelectionEnabled)
1474 setCursorPosition(cursorPos, QTextCursor::KeepAnchor);
1478 && cursor.hasSelection()
1479 && !cursorIsFocusIndicator
1480 && cursorPos >= cursor.selectionStart()
1481 && cursorPos <= cursor.selectionEnd()
1482 && q->hitTest(pos, Qt::ExactHit) != -1) {
1483 #ifndef QT_NO_DRAGANDDROP
1484 mightStartDrag = true;
1485 dragStartPos = pos.toPoint();
1490 setCursorPosition(cursorPos);
1494 if (interactionFlags & Qt::TextEditable) {
1495 q->ensureCursorVisible();
1496 if (cursor.position() != oldCursorPos)
1497 emit q->cursorPositionChanged();
1498 _q_updateCurrentCharFormatAndSelection();
1500 if (cursor.position() != oldCursorPos) {
1501 emit q->cursorPositionChanged();
1502 emit q->microFocusChanged();
1506 repaintOldAndNewSelection(oldSelection);
1507 hadSelectionOnMousePress = cursor.hasSelection();
1510 void QQuickTextControlPrivate::mouseMoveEvent(QEvent *e, Qt::MouseButton button, const QPointF &mousePos, Qt::KeyboardModifiers modifiers,
1511 Qt::MouseButtons buttons, const QPoint &globalPos)
1513 Q_Q(QQuickTextControl);
1515 if (sendMouseEventToInputContext(
1516 e, QEvent::MouseMove, button, mousePos, modifiers, buttons, globalPos)) {
1520 if (interactionFlags & Qt::LinksAccessibleByMouse) {
1521 QString anchor = q->anchorAt(mousePos);
1522 if (anchor != highlightedAnchor) {
1523 highlightedAnchor = anchor;
1524 emit q->linkHovered(anchor);
1528 if (!(buttons & Qt::LeftButton))
1531 const bool editable = interactionFlags & Qt::TextEditable;
1536 || selectedWordOnDoubleClick.hasSelection()
1537 || selectedBlockOnTrippleClick.hasSelection()))
1540 const QTextCursor oldSelection = cursor;
1541 const int oldCursorPos = cursor.position();
1543 if (mightStartDrag) {
1544 if ((mousePos.toPoint() - dragStartPos).manhattanLength() > qApp->styleHints()->startDragDistance())
1552 const qreal mouseX = qreal(mousePos.x());
1554 int newCursorPos = q->hitTest(mousePos, Qt::FuzzyHit);
1555 if (newCursorPos == -1)
1558 if (wordSelectionEnabled && !selectedWordOnDoubleClick.hasSelection()) {
1559 selectedWordOnDoubleClick = cursor;
1560 selectedWordOnDoubleClick.select(QTextCursor::WordUnderCursor);
1563 if (selectedBlockOnTrippleClick.hasSelection())
1564 extendBlockwiseSelection(newCursorPos);
1565 else if (selectedWordOnDoubleClick.hasSelection())
1566 extendWordwiseSelection(newCursorPos, mouseX);
1568 setCursorPosition(newCursorPos, QTextCursor::KeepAnchor);
1570 if (interactionFlags & Qt::TextEditable) {
1571 // don't call ensureVisible for the visible cursor to avoid jumping
1572 // scrollbars. the autoscrolling ensures smooth scrolling if necessary.
1573 //q->ensureCursorVisible();
1574 if (cursor.position() != oldCursorPos)
1575 emit q->cursorPositionChanged();
1576 _q_updateCurrentCharFormatAndSelection();
1578 qGuiApp->inputPanel()->update(Qt::ImQueryInput);
1580 //emit q->visibilityRequest(QRectF(mousePos, QSizeF(1, 1)));
1581 if (cursor.position() != oldCursorPos) {
1582 emit q->cursorPositionChanged();
1583 emit q->microFocusChanged();
1586 selectionChanged(true);
1587 repaintOldAndNewSelection(oldSelection);
1590 void QQuickTextControlPrivate::mouseReleaseEvent(QEvent *e, Qt::MouseButton button, const QPointF &pos, Qt::KeyboardModifiers modifiers,
1591 Qt::MouseButtons buttons, const QPoint &globalPos)
1593 Q_Q(QQuickTextControl);
1595 if (sendMouseEventToInputContext(
1596 e, QEvent::MouseButtonRelease, button, pos, modifiers, buttons, globalPos)) {
1600 const QTextCursor oldSelection = cursor;
1601 const int oldCursorPos = cursor.position();
1603 #ifndef QT_NO_DRAGANDDROP
1604 if (mightStartDrag && (button & Qt::LeftButton)) {
1605 mousePressed = false;
1606 setCursorPosition(pos);
1607 cursor.clearSelection();
1612 mousePressed = false;
1613 #ifndef QT_NO_CLIPBOARD
1614 setClipboardSelection();
1615 selectionChanged(true);
1616 } else if (button == Qt::MidButton
1617 && (interactionFlags & Qt::TextEditable)
1618 && QGuiApplication::clipboard()->supportsSelection()) {
1619 setCursorPosition(pos);
1620 const QMimeData *md = QGuiApplication::clipboard()->mimeData(QClipboard::Selection);
1622 q->insertFromMimeData(md);
1626 repaintOldAndNewSelection(oldSelection);
1628 if (cursor.position() != oldCursorPos) {
1629 emit q->cursorPositionChanged();
1630 emit q->microFocusChanged();
1633 if (interactionFlags & Qt::LinksAccessibleByMouse) {
1634 if (!(button & Qt::LeftButton))
1637 const QString anchor = q->anchorAt(pos);
1639 if (anchor.isEmpty())
1642 if (!cursor.hasSelection()
1643 || (anchor == anchorOnMousePress && hadSelectionOnMousePress)) {
1645 const int anchorPos = q->hitTest(pos, Qt::ExactHit);
1646 if (anchorPos != -1) {
1647 cursor.setPosition(anchorPos);
1649 QString anchor = anchorOnMousePress;
1650 anchorOnMousePress = QString();
1651 activateLinkUnderCursor(anchor);
1657 void QQuickTextControlPrivate::mouseDoubleClickEvent(QEvent *e, Qt::MouseButton button, const QPointF &pos, Qt::KeyboardModifiers modifiers,
1658 Qt::MouseButtons buttons, const QPoint &globalPos)
1660 Q_Q(QQuickTextControl);
1662 if (sendMouseEventToInputContext(
1663 e, QEvent::MouseButtonDblClick, button, pos, modifiers, buttons, globalPos)) {
1667 if (button != Qt::LeftButton
1668 || !(interactionFlags & Qt::TextSelectableByMouse)) {
1673 #ifndef QT_NO_DRAGANDDROP
1674 mightStartDrag = false;
1676 const QTextCursor oldSelection = cursor;
1677 setCursorPosition(pos);
1678 QTextLine line = currentTextLine(cursor);
1679 bool doEmit = false;
1680 if (line.isValid() && line.textLength()) {
1681 cursor.select(QTextCursor::WordUnderCursor);
1684 repaintOldAndNewSelection(oldSelection);
1686 cursorIsFocusIndicator = false;
1687 selectedWordOnDoubleClick = cursor;
1689 trippleClickPoint = pos;
1690 trippleClickTimer.start(qApp->styleHints()->mouseDoubleClickInterval(), q);
1693 #ifndef QT_NO_CLIPBOARD
1694 setClipboardSelection();
1696 emit q->cursorPositionChanged();
1700 bool QQuickTextControlPrivate::sendMouseEventToInputContext(
1701 QEvent *e, QEvent::Type eventType, Qt::MouseButton button, const QPointF &pos,
1702 Qt::KeyboardModifiers modifiers, Qt::MouseButtons buttons, const QPoint &globalPos)
1704 #if 0 // ### !defined(QT_NO_IM)
1705 Q_Q(QQuickTextControl);
1707 QTextLayout *layout = cursor.block().layout();
1708 if (contextObject && layout && !layout->preeditAreaText().isEmpty()) {
1709 QInputContext *ctx = inputContext();
1710 int cursorPos = q->hitTest(pos, Qt::FuzzyHit) - cursor.position();
1712 if (cursorPos < 0 || cursorPos > layout->preeditAreaText().length()) {
1714 // don't send move events outside the preedit area
1715 if (eventType == QEvent::MouseMove)
1719 QMouseEvent ev(eventType, contextObject->mapFromGlobal(globalPos), globalPos,
1720 button, buttons, modifiers);
1721 ctx->mouseHandler(cursorPos, &ev);
1722 e->setAccepted(ev.isAccepted());
1724 if (!layout->preeditAreaText().isEmpty())
1729 Q_UNUSED(eventType);
1732 Q_UNUSED(modifiers);
1734 Q_UNUSED(globalPos);
1739 bool QQuickTextControlPrivate::dragEnterEvent(QEvent *e, const QMimeData *mimeData)
1741 Q_Q(QQuickTextControl);
1742 if (!(interactionFlags & Qt::TextEditable) || !q->canInsertFromMimeData(mimeData)) {
1747 dndFeedbackCursor = QTextCursor();
1749 return true; // accept proposed action
1752 void QQuickTextControlPrivate::dragLeaveEvent()
1754 Q_Q(QQuickTextControl);
1756 const QRectF crect = q->cursorRect(dndFeedbackCursor);
1757 dndFeedbackCursor = QTextCursor();
1759 if (crect.isValid())
1760 emit q->updateRequest(crect);
1763 bool QQuickTextControlPrivate::dragMoveEvent(QEvent *e, const QMimeData *mimeData, const QPointF &pos)
1765 Q_Q(QQuickTextControl);
1766 if (!(interactionFlags & Qt::TextEditable) || !q->canInsertFromMimeData(mimeData)) {
1771 const int cursorPos = q->hitTest(pos, Qt::FuzzyHit);
1772 if (cursorPos != -1) {
1773 QRectF crect = q->cursorRect(dndFeedbackCursor);
1774 if (crect.isValid())
1775 emit q->updateRequest(crect);
1777 dndFeedbackCursor = cursor;
1778 dndFeedbackCursor.setPosition(cursorPos);
1780 crect = q->cursorRect(dndFeedbackCursor);
1781 emit q->updateRequest(crect);
1784 return true; // accept proposed action
1787 bool QQuickTextControlPrivate::dropEvent(const QMimeData *mimeData, const QPointF &pos, Qt::DropAction dropAction, QObject *source)
1789 Q_Q(QQuickTextControl);
1790 dndFeedbackCursor = QTextCursor();
1792 if (!(interactionFlags & Qt::TextEditable) || !q->canInsertFromMimeData(mimeData))
1797 QTextCursor insertionCursor = q->cursorForPosition(pos);
1798 insertionCursor.beginEditBlock();
1800 if (dropAction == Qt::MoveAction && source == contextObject)
1801 cursor.removeSelectedText();
1803 cursor = insertionCursor;
1804 q->insertFromMimeData(mimeData);
1805 insertionCursor.endEditBlock();
1806 q->ensureCursorVisible();
1807 return true; // accept proposed action
1810 void QQuickTextControlPrivate::inputMethodEvent(QInputMethodEvent *e)
1812 Q_Q(QQuickTextControl);
1813 if (!(interactionFlags & Qt::TextEditable) || cursor.isNull()) {
1817 bool isGettingInput = !e->commitString().isEmpty()
1818 || e->preeditString() != cursor.block().layout()->preeditAreaText()
1819 || e->replacementLength() > 0;
1820 bool forceSelectionChanged = false;
1822 cursor.beginEditBlock();
1823 if (isGettingInput) {
1824 cursor.removeSelectedText();
1827 // insert commit string
1828 if (!e->commitString().isEmpty() || e->replacementLength()) {
1829 QTextCursor c = cursor;
1830 c.setPosition(c.position() + e->replacementStart());
1831 c.setPosition(c.position() + e->replacementLength(), QTextCursor::KeepAnchor);
1832 c.insertText(e->commitString());
1835 for (int i = 0; i < e->attributes().size(); ++i) {
1836 const QInputMethodEvent::Attribute &a = e->attributes().at(i);
1837 if (a.type == QInputMethodEvent::Selection) {
1838 QTextCursor oldCursor = cursor;
1839 int blockStart = a.start + cursor.block().position();
1840 cursor.setPosition(blockStart, QTextCursor::MoveAnchor);
1841 cursor.setPosition(blockStart + a.length, QTextCursor::KeepAnchor);
1842 q->ensureCursorVisible();
1843 repaintOldAndNewSelection(oldCursor);
1844 forceSelectionChanged = true;
1848 QTextBlock block = cursor.block();
1849 QTextLayout *layout = block.layout();
1851 layout->setPreeditArea(cursor.position() - block.position(), e->preeditString());
1852 QList<QTextLayout::FormatRange> overrides;
1853 const int oldPreeditCursor = preeditCursor;
1854 preeditCursor = e->preeditString().length();
1856 for (int i = 0; i < e->attributes().size(); ++i) {
1857 const QInputMethodEvent::Attribute &a = e->attributes().at(i);
1858 if (a.type == QInputMethodEvent::Cursor) {
1859 preeditCursor = a.start;
1860 hideCursor = !a.length;
1861 } else if (a.type == QInputMethodEvent::TextFormat) {
1862 QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat();
1864 QTextLayout::FormatRange o;
1865 o.start = a.start + cursor.position() - block.position();
1866 o.length = a.length;
1868 overrides.append(o);
1872 layout->setAdditionalFormats(overrides);
1873 tentativeCommit = e->tentativeCommitString();
1875 cursor.endEditBlock();
1877 QTextCursorPrivate *cursor_d = QTextCursorPrivate::getPrivate(&cursor);
1880 if (oldPreeditCursor != preeditCursor)
1881 emit q->microFocusChanged();
1882 selectionChanged(forceSelectionChanged);
1885 QVariant QQuickTextControl::inputMethodQuery(Qt::InputMethodQuery property) const
1887 Q_D(const QQuickTextControl);
1888 QTextBlock block = d->cursor.block();
1890 case Qt::ImCursorRectangle:
1891 return cursorRect();
1893 return QVariant(d->cursor.charFormat().font());
1894 case Qt::ImCursorPosition:
1895 return QVariant(d->cursor.position() - block.position());
1896 case Qt::ImSurroundingText:
1897 return QVariant(block.text());
1898 case Qt::ImCurrentSelection:
1899 return QVariant(d->cursor.selectedText());
1900 case Qt::ImMaximumTextLength:
1901 return QVariant(); // No limit.
1902 case Qt::ImAnchorPosition:
1903 return QVariant(d->cursor.anchor() - block.position());
1909 void QQuickTextControl::setFocus(bool focus, Qt::FocusReason reason)
1911 QFocusEvent ev(focus ? QEvent::FocusIn : QEvent::FocusOut,
1916 void QQuickTextControlPrivate::focusEvent(QFocusEvent *e)
1918 Q_Q(QQuickTextControl);
1919 emit q->updateRequest(q->selectionRect());
1920 if (e->gotFocus()) {
1921 #ifdef QT_KEYPAD_NAVIGATION
1922 if (!QGuiApplication::keypadNavigationEnabled() || (hasEditFocus && (e->reason() == Qt::PopupFocusReason
1925 setBlinkingCursorEnabled(interactionFlags & (Qt::TextEditable | Qt::TextSelectableByKeyboard));
1926 #ifdef QT_KEYPAD_NAVIGATION
1930 setBlinkingCursorEnabled(false);
1932 if (cursorIsFocusIndicator
1933 && e->reason() != Qt::ActiveWindowFocusReason
1934 && e->reason() != Qt::PopupFocusReason
1935 && cursor.hasSelection()) {
1936 cursor.clearSelection();
1939 hasFocus = e->gotFocus();
1942 QString QQuickTextControlPrivate::anchorForCursor(const QTextCursor &anchorCursor) const
1944 if (anchorCursor.hasSelection()) {
1945 QTextCursor cursor = anchorCursor;
1946 if (cursor.selectionStart() != cursor.position())
1947 cursor.setPosition(cursor.selectionStart());
1948 cursor.movePosition(QTextCursor::NextCharacter);
1949 QTextCharFormat fmt = cursor.charFormat();
1950 if (fmt.isAnchor() && fmt.hasProperty(QTextFormat::AnchorHref))
1951 return fmt.stringProperty(QTextFormat::AnchorHref);
1956 #ifdef QT_KEYPAD_NAVIGATION
1957 void QQuickTextControlPrivate::editFocusEvent(QEvent *e)
1959 Q_Q(QQuickTextControl);
1961 if (QGuiApplication::keypadNavigationEnabled()) {
1962 if (e->type() == QEvent::EnterEditFocus && interactionFlags & Qt::TextEditable) {
1963 const QTextCursor oldSelection = cursor;
1964 const int oldCursorPos = cursor.position();
1965 const bool moved = cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
1966 q->ensureCursorVisible();
1968 if (cursor.position() != oldCursorPos)
1969 emit q->cursorPositionChanged();
1970 emit q->microFocusChanged();
1973 repaintOldAndNewSelection(oldSelection);
1975 setBlinkingCursorEnabled(true);
1977 setBlinkingCursorEnabled(false);
1980 hasEditFocus = e->type() == QEvent::EnterEditFocus ? true : false;
1984 QTextCursor QQuickTextControl::cursorForPosition(const QPointF &pos) const
1986 Q_D(const QQuickTextControl);
1987 int cursorPos = hitTest(pos, Qt::FuzzyHit);
1988 if (cursorPos == -1)
1990 QTextCursor c(d->doc);
1991 c.setPosition(cursorPos);
1995 QRectF QQuickTextControl::cursorRect(const QTextCursor &cursor) const
1997 Q_D(const QQuickTextControl);
1998 if (cursor.isNull())
2001 return d->rectForPosition(cursor.position());
2004 QRectF QQuickTextControl::cursorRect() const
2006 Q_D(const QQuickTextControl);
2007 return cursorRect(d->cursor);
2010 QRectF QQuickTextControlPrivate::cursorRectPlusUnicodeDirectionMarkers(const QTextCursor &cursor) const
2012 if (cursor.isNull())
2015 return rectForPosition(cursor.position()).adjusted(-4, 0, 4, 0);
2018 QString QQuickTextControl::anchorAt(const QPointF &pos) const
2020 Q_D(const QQuickTextControl);
2021 return d->doc->documentLayout()->anchorAt(pos);
2024 QString QQuickTextControl::anchorAtCursor() const
2026 Q_D(const QQuickTextControl);
2028 return d->anchorForCursor(d->cursor);
2031 bool QQuickTextControl::overwriteMode() const
2033 Q_D(const QQuickTextControl);
2034 return d->overwriteMode;
2037 void QQuickTextControl::setOverwriteMode(bool overwrite)
2039 Q_D(QQuickTextControl);
2040 d->overwriteMode = overwrite;
2043 int QQuickTextControl::cursorWidth() const
2045 #ifndef QT_NO_PROPERTIES
2046 Q_D(const QQuickTextControl);
2047 return d->doc->documentLayout()->property("cursorWidth").toInt();
2053 void QQuickTextControl::setCursorWidth(int width)
2055 Q_D(QQuickTextControl);
2056 #ifdef QT_NO_PROPERTIES
2060 width = textCursorWidth;
2061 d->doc->documentLayout()->setProperty("cursorWidth", width);
2066 bool QQuickTextControl::acceptRichText() const
2068 Q_D(const QQuickTextControl);
2069 return d->acceptRichText;
2072 void QQuickTextControl::setAcceptRichText(bool accept)
2074 Q_D(QQuickTextControl);
2075 d->acceptRichText = accept;
2078 void QQuickTextControl::setExtraSelections(const QVector<QAbstractTextDocumentLayout::Selection> &selections)
2080 Q_D(QQuickTextControl);
2082 QHash<int, int> hash;
2083 for (int i = 0; i < d->extraSelections.count(); ++i) {
2084 const QAbstractTextDocumentLayout::Selection &esel = d->extraSelections.at(i);
2085 hash.insertMulti(esel.cursor.anchor(), i);
2088 for (int i = 0; i < selections.count(); ++i) {
2089 const QAbstractTextDocumentLayout::Selection &sel = selections.at(i);
2090 QHash<int, int>::iterator it = hash.find(sel.cursor.anchor());
2091 if (it != hash.end()) {
2092 const QAbstractTextDocumentLayout::Selection &esel = d->extraSelections.at(it.value());
2093 if (esel.cursor.position() == sel.cursor.position()
2094 && esel.format == sel.format) {
2099 QRectF r = selectionRect(sel.cursor);
2100 if (sel.format.boolProperty(QTextFormat::FullWidthSelection)) {
2102 r.setWidth(qreal(INT_MAX));
2104 emit updateRequest(r);
2107 for (QHash<int, int>::iterator it = hash.begin(); it != hash.end(); ++it) {
2108 const QAbstractTextDocumentLayout::Selection &esel = d->extraSelections.at(it.value());
2109 QRectF r = selectionRect(esel.cursor);
2110 if (esel.format.boolProperty(QTextFormat::FullWidthSelection)) {
2112 r.setWidth(qreal(INT_MAX));
2114 emit updateRequest(r);
2117 d->extraSelections = selections;
2120 QVector<QAbstractTextDocumentLayout::Selection> QQuickTextControl::extraSelections() const
2122 Q_D(const QQuickTextControl);
2123 return d->extraSelections;
2126 void QQuickTextControl::setTextWidth(qreal width)
2128 Q_D(QQuickTextControl);
2129 d->doc->setTextWidth(width);
2132 qreal QQuickTextControl::textWidth() const
2134 Q_D(const QQuickTextControl);
2135 return d->doc->textWidth();
2138 QSizeF QQuickTextControl::size() const
2140 Q_D(const QQuickTextControl);
2141 return d->doc->size();
2144 void QQuickTextControl::setOpenExternalLinks(bool open)
2146 Q_D(QQuickTextControl);
2147 d->openExternalLinks = open;
2150 bool QQuickTextControl::openExternalLinks() const
2152 Q_D(const QQuickTextControl);
2153 return d->openExternalLinks;
2156 bool QQuickTextControl::ignoreUnusedNavigationEvents() const
2158 Q_D(const QQuickTextControl);
2159 return d->ignoreUnusedNavigationEvents;
2162 void QQuickTextControl::setIgnoreUnusedNavigationEvents(bool ignore)
2164 Q_D(QQuickTextControl);
2165 d->ignoreUnusedNavigationEvents = ignore;
2168 void QQuickTextControl::moveCursor(QTextCursor::MoveOperation op, QTextCursor::MoveMode mode)
2170 Q_D(QQuickTextControl);
2171 const QTextCursor oldSelection = d->cursor;
2172 const bool moved = d->cursor.movePosition(op, mode);
2173 d->_q_updateCurrentCharFormatAndSelection();
2174 ensureCursorVisible();
2175 d->repaintOldAndNewSelection(oldSelection);
2177 emit cursorPositionChanged();
2180 bool QQuickTextControl::canPaste() const
2182 #ifndef QT_NO_CLIPBOARD
2183 Q_D(const QQuickTextControl);
2184 if (d->interactionFlags & Qt::TextEditable) {
2185 const QMimeData *md = QGuiApplication::clipboard()->mimeData();
2186 return md && canInsertFromMimeData(md);
2192 void QQuickTextControl::setCursorIsFocusIndicator(bool b)
2194 Q_D(QQuickTextControl);
2195 d->cursorIsFocusIndicator = b;
2199 bool QQuickTextControl::cursorIsFocusIndicator() const
2201 Q_D(const QQuickTextControl);
2202 return d->cursorIsFocusIndicator;
2206 void QQuickTextControl::setDragEnabled(bool enabled)
2208 Q_D(QQuickTextControl);
2209 d->dragEnabled = enabled;
2212 bool QQuickTextControl::isDragEnabled() const
2214 Q_D(const QQuickTextControl);
2215 return d->dragEnabled;
2218 void QQuickTextControl::setWordSelectionEnabled(bool enabled)
2220 Q_D(QQuickTextControl);
2221 d->wordSelectionEnabled = enabled;
2224 bool QQuickTextControl::isWordSelectionEnabled() const
2226 Q_D(const QQuickTextControl);
2227 return d->wordSelectionEnabled;
2230 QMimeData *QQuickTextControl::createMimeDataFromSelection() const
2232 Q_D(const QQuickTextControl);
2233 const QTextDocumentFragment fragment(d->cursor);
2234 return new QQuickTextEditMimeData(fragment);
2237 bool QQuickTextControl::canInsertFromMimeData(const QMimeData *source) const
2239 Q_D(const QQuickTextControl);
2240 if (d->acceptRichText)
2241 return (source->hasText() && !source->text().isEmpty())
2242 || source->hasHtml()
2243 || source->hasFormat(QLatin1String("application/x-qrichtext"))
2244 || source->hasFormat(QLatin1String("application/x-qt-richtext"));
2246 return source->hasText() && !source->text().isEmpty();
2249 void QQuickTextControl::insertFromMimeData(const QMimeData *source)
2251 Q_D(QQuickTextControl);
2252 if (!(d->interactionFlags & Qt::TextEditable) || !source)
2255 bool hasData = false;
2256 QTextDocumentFragment fragment;
2257 #ifndef QT_NO_TEXTHTMLPARSER
2258 if (source->hasFormat(QLatin1String("application/x-qrichtext")) && d->acceptRichText) {
2259 // x-qrichtext is always UTF-8 (taken from Qt3 since we don't use it anymore).
2260 QString richtext = QString::fromUtf8(source->data(QLatin1String("application/x-qrichtext")));
2261 richtext.prepend(QLatin1String("<meta name=\"qrichtext\" content=\"1\" />"));
2262 fragment = QTextDocumentFragment::fromHtml(richtext, d->doc);
2264 } else if (source->hasHtml() && d->acceptRichText) {
2265 fragment = QTextDocumentFragment::fromHtml(source->html(), d->doc);
2268 QString text = source->text();
2269 if (!text.isNull()) {
2270 fragment = QTextDocumentFragment::fromPlainText(text);
2275 fragment = QTextDocumentFragment::fromPlainText(source->text());
2276 #endif // QT_NO_TEXTHTMLPARSER
2279 d->cursor.insertFragment(fragment);
2280 ensureCursorVisible();
2283 bool QQuickTextControl::findNextPrevAnchor(const QTextCursor &startCursor, bool next, QTextCursor &newAnchor)
2285 Q_D(QQuickTextControl);
2287 int anchorStart = -1;
2292 const int startPos = startCursor.selectionEnd();
2294 QTextBlock block = d->doc->findBlock(startPos);
2295 QTextBlock::Iterator it = block.begin();
2297 while (!it.atEnd() && it.fragment().position() < startPos)
2300 while (block.isValid()) {
2304 for (; !it.atEnd(); ++it) {
2305 const QTextFragment fragment = it.fragment();
2306 const QTextCharFormat fmt = fragment.charFormat();
2308 if (fmt.isAnchor() && fmt.hasProperty(QTextFormat::AnchorHref)) {
2309 anchorStart = fragment.position();
2310 anchorHref = fmt.anchorHref();
2315 if (anchorStart != -1) {
2318 // find next non-anchor fragment
2319 for (; !it.atEnd(); ++it) {
2320 const QTextFragment fragment = it.fragment();
2321 const QTextCharFormat fmt = fragment.charFormat();
2323 if (!fmt.isAnchor() || fmt.anchorHref() != anchorHref) {
2324 anchorEnd = fragment.position();
2329 if (anchorEnd == -1)
2330 anchorEnd = block.position() + block.length() - 1;
2332 // make found selection
2336 block = block.next();
2340 int startPos = startCursor.selectionStart();
2344 QTextBlock block = d->doc->findBlock(startPos);
2345 QTextBlock::Iterator blockStart = block.begin();
2346 QTextBlock::Iterator it = block.end();
2348 if (startPos == block.position()) {
2352 if (it == blockStart) {
2353 it = QTextBlock::Iterator();
2354 block = QTextBlock();
2358 } while (!it.atEnd() && it.fragment().position() + it.fragment().length() - 1 > startPos);
2361 while (block.isValid()) {
2366 const QTextFragment fragment = it.fragment();
2367 const QTextCharFormat fmt = fragment.charFormat();
2369 if (fmt.isAnchor() && fmt.hasProperty(QTextFormat::AnchorHref)) {
2370 anchorStart = fragment.position() + fragment.length();
2371 anchorHref = fmt.anchorHref();
2375 if (it == blockStart)
2376 it = QTextBlock::Iterator();
2379 } while (!it.atEnd());
2382 if (anchorStart != -1 && !it.atEnd()) {
2386 const QTextFragment fragment = it.fragment();
2387 const QTextCharFormat fmt = fragment.charFormat();
2389 if (!fmt.isAnchor() || fmt.anchorHref() != anchorHref) {
2390 anchorEnd = fragment.position() + fragment.length();
2394 if (it == blockStart)
2395 it = QTextBlock::Iterator();
2398 } while (!it.atEnd());
2400 if (anchorEnd == -1)
2401 anchorEnd = qMax(0, block.position());
2406 block = block.previous();
2408 if (it != block.begin())
2410 blockStart = block.begin();
2415 if (anchorStart != -1 && anchorEnd != -1) {
2416 newAnchor = d->cursor;
2417 newAnchor.setPosition(anchorStart);
2418 newAnchor.setPosition(anchorEnd, QTextCursor::KeepAnchor);
2425 void QQuickTextControlPrivate::activateLinkUnderCursor(QString href)
2427 QTextCursor oldCursor = cursor;
2429 if (href.isEmpty()) {
2430 QTextCursor tmp = cursor;
2431 if (tmp.selectionStart() != tmp.position())
2432 tmp.setPosition(tmp.selectionStart());
2433 tmp.movePosition(QTextCursor::NextCharacter);
2434 href = tmp.charFormat().anchorHref();
2439 if (!cursor.hasSelection()) {
2440 QTextBlock block = cursor.block();
2441 const int cursorPos = cursor.position();
2443 QTextBlock::Iterator it = block.begin();
2444 QTextBlock::Iterator linkFragment;
2446 for (; !it.atEnd(); ++it) {
2447 QTextFragment fragment = it.fragment();
2448 const int fragmentPos = fragment.position();
2449 if (fragmentPos <= cursorPos &&
2450 fragmentPos + fragment.length() > cursorPos) {
2456 if (!linkFragment.atEnd()) {
2458 cursor.setPosition(it.fragment().position());
2459 if (it != block.begin()) {
2462 QTextFragment fragment = it.fragment();
2463 if (fragment.charFormat().anchorHref() != href)
2465 cursor.setPosition(fragment.position());
2466 } while (it != block.begin());
2469 for (it = linkFragment; !it.atEnd(); ++it) {
2470 QTextFragment fragment = it.fragment();
2471 if (fragment.charFormat().anchorHref() != href)
2473 cursor.setPosition(fragment.position() + fragment.length(), QTextCursor::KeepAnchor);
2479 cursorIsFocusIndicator = true;
2481 cursorIsFocusIndicator = false;
2482 cursor.clearSelection();
2484 repaintOldAndNewSelection(oldCursor);
2486 #if 0 // ###ndef QT_NO_DESKTOPSERVICES
2487 if (openExternalLinks)
2488 QDesktopServices::openUrl(href);
2491 emit q_func()->linkActivated(href);
2494 bool QQuickTextControl::setFocusToNextOrPreviousAnchor(bool next)
2496 Q_D(QQuickTextControl);
2498 if (!(d->interactionFlags & Qt::LinksAccessibleByKeyboard))
2501 QRectF crect = selectionRect();
2502 emit updateRequest(crect);
2504 // If we don't have a current anchor, we start from the start/end
2505 if (!d->cursor.hasSelection()) {
2506 d->cursor = QTextCursor(d->doc);
2508 d->cursor.movePosition(QTextCursor::Start);
2510 d->cursor.movePosition(QTextCursor::End);
2513 QTextCursor newAnchor;
2514 if (findNextPrevAnchor(d->cursor, next, newAnchor)) {
2515 d->cursor = newAnchor;
2516 d->cursorIsFocusIndicator = true;
2518 d->cursor.clearSelection();
2521 if (d->cursor.hasSelection()) {
2522 crect = selectionRect();
2523 emit updateRequest(crect);
2524 emit visibilityRequest(crect);
2531 bool QQuickTextControl::setFocusToAnchor(const QTextCursor &newCursor)
2533 Q_D(QQuickTextControl);
2535 if (!(d->interactionFlags & Qt::LinksAccessibleByKeyboard))
2538 // Verify that this is an anchor.
2539 const QString anchorHref = d->anchorForCursor(newCursor);
2540 if (anchorHref.isEmpty())
2544 QRectF crect = selectionRect();
2545 emit updateRequest(crect);
2547 d->cursor.setPosition(newCursor.selectionStart());
2548 d->cursor.setPosition(newCursor.selectionEnd(), QTextCursor::KeepAnchor);
2549 d->cursorIsFocusIndicator = true;
2551 crect = selectionRect();
2552 emit updateRequest(crect);
2553 emit visibilityRequest(crect);
2557 void QQuickTextControl::setTextInteractionFlags(Qt::TextInteractionFlags flags)
2559 Q_D(QQuickTextControl);
2560 if (flags == d->interactionFlags)
2562 d->interactionFlags = flags;
2565 d->setBlinkingCursorEnabled(flags & (Qt::TextEditable | Qt::TextSelectableByKeyboard));
2568 Qt::TextInteractionFlags QQuickTextControl::textInteractionFlags() const
2570 Q_D(const QQuickTextControl);
2571 return d->interactionFlags;
2574 void QQuickTextControl::mergeCurrentCharFormat(const QTextCharFormat &modifier)
2576 Q_D(QQuickTextControl);
2577 d->cursor.mergeCharFormat(modifier);
2578 d->updateCurrentCharFormat();
2581 void QQuickTextControl::setCurrentCharFormat(const QTextCharFormat &format)
2583 Q_D(QQuickTextControl);
2584 d->cursor.setCharFormat(format);
2585 d->updateCurrentCharFormat();
2588 QTextCharFormat QQuickTextControl::currentCharFormat() const
2590 Q_D(const QQuickTextControl);
2591 return d->cursor.charFormat();
2594 void QQuickTextControl::insertPlainText(const QString &text)
2596 Q_D(QQuickTextControl);
2597 d->cursor.insertText(text);
2600 #ifndef QT_NO_TEXTHTMLPARSER
2601 void QQuickTextControl::insertHtml(const QString &text)
2603 Q_D(QQuickTextControl);
2604 d->cursor.insertHtml(text);
2606 #endif // QT_NO_TEXTHTMLPARSER
2608 QPointF QQuickTextControl::anchorPosition(const QString &name) const
2610 Q_D(const QQuickTextControl);
2615 for (QTextBlock block = d->doc->begin(); block.isValid(); block = block.next()) {
2616 QTextCharFormat format = block.charFormat();
2617 if (format.isAnchor() && format.anchorNames().contains(name)) {
2618 r = d->rectForPosition(block.position());
2622 for (QTextBlock::Iterator it = block.begin(); !it.atEnd(); ++it) {
2623 QTextFragment fragment = it.fragment();
2624 format = fragment.charFormat();
2625 if (format.isAnchor() && format.anchorNames().contains(name)) {
2626 r = d->rectForPosition(fragment.position());
2627 block = QTextBlock();
2634 return QPointF(0, r.top());
2637 void QQuickTextControl::adjustSize()
2639 Q_D(QQuickTextControl);
2640 d->doc->adjustSize();
2643 bool QQuickTextControl::find(const QString &exp, QTextDocument::FindFlags options)
2645 Q_D(QQuickTextControl);
2646 QTextCursor search = d->doc->find(exp, d->cursor, options);
2647 if (search.isNull())
2650 setTextCursor(search);
2656 void QQuickTextControlPrivate::append(const QString &text, Qt::TextFormat format)
2658 QTextCursor tmp(doc);
2659 tmp.beginEditBlock();
2660 tmp.movePosition(QTextCursor::End);
2662 if (!doc->isEmpty())
2663 tmp.insertBlock(cursor.blockFormat(), cursor.charFormat());
2665 tmp.setCharFormat(cursor.charFormat());
2667 // preserve the char format
2668 QTextCharFormat oldCharFormat = cursor.charFormat();
2670 #ifndef QT_NO_TEXTHTMLPARSER
2671 if (format == Qt::RichText || (format == Qt::AutoText && Qt::mightBeRichText(text))) {
2672 tmp.insertHtml(text);
2674 tmp.insertText(text);
2677 tmp.insertText(text);
2678 #endif // QT_NO_TEXTHTMLPARSER
2679 if (!cursor.hasSelection())
2680 cursor.setCharFormat(oldCharFormat);
2685 void QQuickTextControl::append(const QString &text)
2687 Q_D(QQuickTextControl);
2688 d->append(text, Qt::AutoText);
2691 void QQuickTextControl::appendHtml(const QString &html)
2693 Q_D(QQuickTextControl);
2694 d->append(html, Qt::RichText);
2697 void QQuickTextControl::appendPlainText(const QString &text)
2699 Q_D(QQuickTextControl);
2700 d->append(text, Qt::PlainText);
2703 QString QQuickTextControl::toPlainText() const
2705 Q_D(const QQuickTextControl);
2706 QString plainText = document()->toPlainText();
2707 if (!d->tentativeCommit.isEmpty())
2708 plainText.insert(textCursor().position(), d->tentativeCommit);
2712 #ifndef QT_NO_TEXTHTMLPARSER
2713 QString QQuickTextControl::toHtml() const
2715 // note: currently not including tentative commit
2716 return document()->toHtml();
2720 void QQuickTextControl::ensureCursorVisible()
2722 Q_D(QQuickTextControl);
2723 QRectF crect = d->rectForPosition(d->cursor.position()).adjusted(-5, 0, 5, 0);
2724 emit visibilityRequest(crect);
2725 emit microFocusChanged();
2728 QPalette QQuickTextControl::palette() const
2730 Q_D(const QQuickTextControl);
2734 void QQuickTextControl::setPalette(const QPalette &pal)
2736 Q_D(QQuickTextControl);
2740 bool QQuickTextControl::cursorOn() const
2742 Q_D(const QQuickTextControl);
2746 QAbstractTextDocumentLayout::PaintContext QQuickTextControl::getPaintContext() const
2748 Q_D(const QQuickTextControl);
2750 QAbstractTextDocumentLayout::PaintContext ctx;
2752 ctx.selections = d->extraSelections;
2753 ctx.palette = d->palette;
2754 if (d->cursorOn && d->isEnabled) {
2756 ctx.cursorPosition = -1;
2757 else if (d->preeditCursor != 0)
2758 ctx.cursorPosition = - (d->preeditCursor + 2);
2760 ctx.cursorPosition = d->cursor.position();
2763 if (!d->dndFeedbackCursor.isNull())
2764 ctx.cursorPosition = d->dndFeedbackCursor.position();
2765 #ifdef QT_KEYPAD_NAVIGATION
2766 if (!QGuiApplication::keypadNavigationEnabled() || d->hasEditFocus)
2768 if (d->cursor.hasSelection()) {
2769 QAbstractTextDocumentLayout::Selection selection;
2770 selection.cursor = d->cursor;
2771 if (0 && d->cursorIsFocusIndicator) {
2775 opt.palette = ctx.palette;
2776 QStyleHintReturnVariant ret;
2777 QStyle *style = QGuiApplication::style();
2779 style = widget->style();
2780 style->styleHint(QStyle::SH_TextControl_FocusIndicatorTextCharFormat, &opt, widget, &ret);
2781 selection.format = qvariant_cast<QTextFormat>(ret.variant).toCharFormat();
2784 QPalette::ColorGroup cg = d->hasFocus ? QPalette::Active : QPalette::Inactive;
2785 selection.format.setBackground(ctx.palette.brush(cg, QPalette::Highlight));
2786 selection.format.setForeground(ctx.palette.brush(cg, QPalette::HighlightedText));
2787 if (fullWidthSelection)
2788 selection.format.setProperty(QTextFormat::FullWidthSelection, true);
2790 ctx.selections.append(selection);
2796 void QQuickTextControl::drawContents(QPainter *p, const QRectF &rect)
2798 Q_D(QQuickTextControl);
2800 QAbstractTextDocumentLayout::PaintContext ctx = getPaintContext();
2802 p->setClipRect(rect, Qt::IntersectClip);
2805 d->doc->documentLayout()->draw(p, ctx);
2809 void QQuickTextControlPrivate::_q_copyLink()
2811 #ifndef QT_NO_CLIPBOARD
2812 QMimeData *md = new QMimeData;
2813 md->setText(linkToCopy);
2814 QGuiApplication::clipboard()->setMimeData(md);
2818 QInputContext *QQuickTextControlPrivate::inputContext()
2822 QInputContext *ctx = contextObject->inputContext();
2823 if (!ctx && contextObject->parentWidget())
2824 ctx = contextObject->parentWidget()->inputContext();
2831 int QQuickTextControl::hitTest(const QPointF &point, Qt::HitTestAccuracy accuracy) const
2833 Q_D(const QQuickTextControl);
2834 return d->doc->documentLayout()->hitTest(point, accuracy);
2837 QRectF QQuickTextControl::blockBoundingRect(const QTextBlock &block) const
2839 Q_D(const QQuickTextControl);
2840 return d->doc->documentLayout()->blockBoundingRect(block);
2845 QStringList QQuickTextEditMimeData::formats() const
2847 if (!fragment.isEmpty())
2848 return QStringList() << QString::fromLatin1("text/plain") << QString::fromLatin1("text/html")
2849 #ifndef QT_NO_TEXTODFWRITER
2850 << QString::fromLatin1("application/vnd.oasis.opendocument.text")
2854 return QMimeData::formats();
2857 QVariant QQuickTextEditMimeData::retrieveData(const QString &mimeType, QVariant::Type type) const
2859 if (!fragment.isEmpty())
2861 return QMimeData::retrieveData(mimeType, type);
2864 void QQuickTextEditMimeData::setup() const
2866 QQuickTextEditMimeData *that = const_cast<QQuickTextEditMimeData *>(this);
2867 #ifndef QT_NO_TEXTHTMLPARSER
2868 that->setData(QLatin1String("text/html"), fragment.toHtml("utf-8").toUtf8());
2870 #ifndef QT_NO_TEXTODFWRITER
2873 QTextDocumentWriter writer(&buffer, "ODF");
2874 writer.write(fragment);
2876 that->setData(QLatin1String("application/vnd.oasis.opendocument.text"), buffer.data());
2879 that->setText(fragment.toPlainText());
2880 fragment = QTextDocumentFragment();
2886 #include "moc_qquicktextcontrol_p.cpp"
2888 #endif // QT_NO_TEXTCONTROL