1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtGui module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** 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 lastSelectionState(false), ignoreAutomaticScrollbarAdjustement(false),
104 overwriteMode(false),
105 acceptRichText(true),
106 preeditCursor(0), hideCursor(false),
109 hadSelectionOnMousePress(false),
110 wordSelectionEnabled(false)
113 bool QQuickTextControlPrivate::cursorMoveKeyEvent(QKeyEvent *e)
115 #ifdef QT_NO_SHORTCUT
119 Q_Q(QQuickTextControl);
123 const QTextCursor oldSelection = cursor;
124 const int oldCursorPos = cursor.position();
126 QTextCursor::MoveMode mode = QTextCursor::MoveAnchor;
127 QTextCursor::MoveOperation op = QTextCursor::NoMove;
131 #ifndef QT_NO_SHORTCUT
132 if (e == QKeySequence::MoveToNextChar) {
133 op = QTextCursor::Right;
135 else if (e == QKeySequence::MoveToPreviousChar) {
136 op = QTextCursor::Left;
138 else if (e == QKeySequence::SelectNextChar) {
139 op = QTextCursor::Right;
140 mode = QTextCursor::KeepAnchor;
142 else if (e == QKeySequence::SelectPreviousChar) {
143 op = QTextCursor::Left;
144 mode = QTextCursor::KeepAnchor;
146 else if (e == QKeySequence::SelectNextWord) {
147 op = QTextCursor::WordRight;
148 mode = QTextCursor::KeepAnchor;
150 else if (e == QKeySequence::SelectPreviousWord) {
151 op = QTextCursor::WordLeft;
152 mode = QTextCursor::KeepAnchor;
154 else if (e == QKeySequence::SelectStartOfLine) {
155 op = QTextCursor::StartOfLine;
156 mode = QTextCursor::KeepAnchor;
158 else if (e == QKeySequence::SelectEndOfLine) {
159 op = QTextCursor::EndOfLine;
160 mode = QTextCursor::KeepAnchor;
162 else if (e == QKeySequence::SelectStartOfBlock) {
163 op = QTextCursor::StartOfBlock;
164 mode = QTextCursor::KeepAnchor;
166 else if (e == QKeySequence::SelectEndOfBlock) {
167 op = QTextCursor::EndOfBlock;
168 mode = QTextCursor::KeepAnchor;
170 else if (e == QKeySequence::SelectStartOfDocument) {
171 op = QTextCursor::Start;
172 mode = QTextCursor::KeepAnchor;
174 else if (e == QKeySequence::SelectEndOfDocument) {
175 op = QTextCursor::End;
176 mode = QTextCursor::KeepAnchor;
178 else if (e == QKeySequence::SelectPreviousLine) {
179 op = QTextCursor::Up;
180 mode = QTextCursor::KeepAnchor;
182 else if (e == QKeySequence::SelectNextLine) {
183 op = QTextCursor::Down;
184 mode = QTextCursor::KeepAnchor;
186 QTextBlock block = cursor.block();
187 QTextLine line = currentTextLine(cursor);
188 if (!block.next().isValid()
190 && line.lineNumber() == block.layout()->lineCount() - 1)
191 op = QTextCursor::End;
194 else if (e == QKeySequence::MoveToNextWord) {
195 op = QTextCursor::WordRight;
197 else if (e == QKeySequence::MoveToPreviousWord) {
198 op = QTextCursor::WordLeft;
200 else if (e == QKeySequence::MoveToEndOfBlock) {
201 op = QTextCursor::EndOfBlock;
203 else if (e == QKeySequence::MoveToStartOfBlock) {
204 op = QTextCursor::StartOfBlock;
206 else if (e == QKeySequence::MoveToNextLine) {
207 op = QTextCursor::Down;
209 else if (e == QKeySequence::MoveToPreviousLine) {
210 op = QTextCursor::Up;
212 else if (e == QKeySequence::MoveToStartOfLine) {
213 op = QTextCursor::StartOfLine;
215 else if (e == QKeySequence::MoveToEndOfLine) {
216 op = QTextCursor::EndOfLine;
218 else if (e == QKeySequence::MoveToStartOfDocument) {
219 op = QTextCursor::Start;
221 else if (e == QKeySequence::MoveToEndOfDocument) {
222 op = QTextCursor::End;
224 #endif // QT_NO_SHORTCUT
229 // Except for pageup and pagedown, Mac OS X has very different behavior, we don't do it all, but
230 // here's the breakdown:
231 // Shift still works as an anchor, but only one of the other keys can be down Ctrl (Command),
232 // Alt (Option), or Meta (Control).
233 // Command/Control + Left/Right -- Move to left or right of the line
234 // + Up/Down -- Move to top bottom of the file. (Control doesn't move the cursor)
235 // Option + Left/Right -- Move one word Left/right.
236 // + Up/Down -- Begin/End of Paragraph.
237 // Home/End Top/Bottom of file. (usually don't move the cursor, but will select)
239 bool visualNavigation = cursor.visualNavigation();
240 cursor.setVisualNavigation(true);
241 const bool moved = cursor.movePosition(op, mode);
242 cursor.setVisualNavigation(visualNavigation);
244 bool isNavigationEvent
245 = e->key() == Qt::Key_Up
246 || e->key() == Qt::Key_Down
247 || e->key() == Qt::Key_Left
248 || e->key() == Qt::Key_Right;
251 if (cursor.position() != oldCursorPos)
252 emit q->cursorPositionChanged();
253 emit q->cursorRectangleChanged();
254 } else if (isNavigationEvent && oldSelection.anchor() == cursor.anchor()) {
258 selectionChanged(/*forceEmitSelectionChanged =*/(mode == QTextCursor::KeepAnchor));
260 repaintOldAndNewSelection(oldSelection);
265 void QQuickTextControlPrivate::updateCurrentCharFormat()
267 Q_Q(QQuickTextControl);
269 QTextCharFormat fmt = cursor.charFormat();
270 if (fmt == lastCharFormat)
272 lastCharFormat = fmt;
274 emit q->currentCharFormatChanged(fmt);
275 emit q->cursorRectangleChanged();
278 void QQuickTextControlPrivate::init(Qt::TextFormat format, const QString &text, QTextDocument *document)
280 Q_Q(QQuickTextControl);
281 setContent(format, text, document);
283 doc->setUndoRedoEnabled(interactionFlags & Qt::TextEditable);
284 q->setCursorWidth(-1);
287 void QQuickTextControlPrivate::setContent(Qt::TextFormat format, const QString &text, QTextDocument *document)
289 Q_Q(QQuickTextControl);
291 // for use when called from setPlainText. we may want to re-use the currently
292 // set char format then.
293 const QTextCharFormat charFormatForInsertion = cursor.charFormat();
295 bool clearDocument = true;
299 clearDocument = false;
301 palette = QGuiApplication::palette();
302 doc = new QTextDocument(q);
304 _q_documentLayoutChanged();
305 cursor = QTextCursor(doc);
307 // #### doc->documentLayout()->setPaintDevice(viewport);
309 QObject::connect(doc, SIGNAL(contentsChanged()), q, SLOT(_q_updateCurrentCharFormatAndSelection()));
310 QObject::connect(doc, SIGNAL(cursorPositionChanged(QTextCursor)), q, SLOT(_q_emitCursorPosChanged(QTextCursor)));
311 QObject::connect(doc, SIGNAL(documentLayoutChanged()), q, SLOT(_q_documentLayoutChanged()));
314 bool previousUndoRedoState = doc->isUndoRedoEnabled();
316 doc->setUndoRedoEnabled(false);
318 //Saving the index save some time.
319 static int contentsChangedIndex = QTextDocument::staticMetaObject.indexOfSignal("contentsChanged()");
320 static int textChangedIndex = QQuickTextControl::staticMetaObject.indexOfSignal("textChanged()");
321 // avoid multiple textChanged() signals being emitted
322 QMetaObject::disconnect(doc, contentsChangedIndex, q, textChangedIndex);
324 if (!text.isEmpty()) {
325 // clear 'our' cursor for insertion to prevent
326 // the emission of the cursorPositionChanged() signal.
327 // instead we emit it only once at the end instead of
328 // at the end of the document after loading and when
329 // positioning the cursor again to the start of the
331 cursor = QTextCursor();
332 if (format == Qt::PlainText) {
333 QTextCursor formatCursor(doc);
334 // put the setPlainText and the setCharFormat into one edit block,
335 // so that the syntax highlight triggers only /once/ for the entire
336 // document, not twice.
337 formatCursor.beginEditBlock();
338 doc->setPlainText(text);
339 doc->setUndoRedoEnabled(false);
340 formatCursor.select(QTextCursor::Document);
341 formatCursor.setCharFormat(charFormatForInsertion);
342 formatCursor.endEditBlock();
344 #ifndef QT_NO_TEXTHTMLPARSER
347 doc->setPlainText(text);
349 doc->setUndoRedoEnabled(false);
351 cursor = QTextCursor(doc);
352 } else if (clearDocument) {
355 cursor.setCharFormat(charFormatForInsertion);
357 QMetaObject::connect(doc, contentsChangedIndex, q, textChangedIndex);
358 emit q->textChanged();
360 doc->setUndoRedoEnabled(previousUndoRedoState);
361 _q_updateCurrentCharFormatAndSelection();
363 doc->setModified(false);
365 emit q->cursorRectangleChanged();
366 emit q->cursorPositionChanged();
369 void QQuickTextControlPrivate::setCursorPosition(const QPointF &pos)
371 Q_Q(QQuickTextControl);
372 const int cursorPos = q->hitTest(pos, Qt::FuzzyHit);
375 cursor.setPosition(cursorPos);
378 void QQuickTextControlPrivate::setCursorPosition(int pos, QTextCursor::MoveMode mode)
380 cursor.setPosition(pos, mode);
382 if (mode != QTextCursor::KeepAnchor) {
383 selectedWordOnDoubleClick = QTextCursor();
384 selectedBlockOnTrippleClick = QTextCursor();
388 void QQuickTextControlPrivate::repaintCursor()
390 Q_Q(QQuickTextControl);
391 emit q->updateCursorRequest(cursorRectPlusUnicodeDirectionMarkers(cursor));
394 void QQuickTextControlPrivate::repaintOldAndNewSelection(const QTextCursor &oldSelection)
396 Q_Q(QQuickTextControl);
397 if (cursor.hasSelection()
398 && oldSelection.hasSelection()
399 && cursor.currentFrame() == oldSelection.currentFrame()
400 && !cursor.hasComplexSelection()
401 && !oldSelection.hasComplexSelection()
402 && cursor.anchor() == oldSelection.anchor()
404 QTextCursor differenceSelection(doc);
405 differenceSelection.setPosition(oldSelection.position());
406 differenceSelection.setPosition(cursor.position(), QTextCursor::KeepAnchor);
407 emit q->updateRequest(q->selectionRect(differenceSelection));
409 if (!oldSelection.hasSelection() && !cursor.hasSelection()) {
410 if (!oldSelection.isNull())
411 emit q->updateCursorRequest(q->selectionRect(oldSelection) | cursorRectPlusUnicodeDirectionMarkers(oldSelection));
412 emit q->updateCursorRequest(q->selectionRect() | cursorRectPlusUnicodeDirectionMarkers(cursor));
415 if (!oldSelection.isNull())
416 emit q->updateRequest(q->selectionRect(oldSelection) | cursorRectPlusUnicodeDirectionMarkers(oldSelection));
417 emit q->updateRequest(q->selectionRect() | cursorRectPlusUnicodeDirectionMarkers(cursor));
422 void QQuickTextControlPrivate::selectionChanged(bool forceEmitSelectionChanged /*=false*/)
424 Q_Q(QQuickTextControl);
425 if (forceEmitSelectionChanged)
426 emit q->selectionChanged();
428 bool current = cursor.hasSelection();
429 if (current == lastSelectionState)
432 lastSelectionState = current;
433 emit q->copyAvailable(current);
434 if (!forceEmitSelectionChanged)
435 emit q->selectionChanged();
436 emit q->cursorRectangleChanged();
439 void QQuickTextControlPrivate::_q_updateCurrentCharFormatAndSelection()
441 updateCurrentCharFormat();
445 #ifndef QT_NO_CLIPBOARD
446 void QQuickTextControlPrivate::setClipboardSelection()
448 QClipboard *clipboard = QGuiApplication::clipboard();
449 if (!cursor.hasSelection() || !clipboard->supportsSelection())
451 Q_Q(QQuickTextControl);
452 QMimeData *data = q->createMimeDataFromSelection();
453 clipboard->setMimeData(data, QClipboard::Selection);
457 void QQuickTextControlPrivate::_q_emitCursorPosChanged(const QTextCursor &someCursor)
459 Q_Q(QQuickTextControl);
460 if (someCursor.isCopyOf(cursor)) {
461 emit q->cursorPositionChanged();
462 emit q->cursorRectangleChanged();
466 void QQuickTextControlPrivate::_q_documentLayoutChanged()
468 Q_Q(QQuickTextControl);
469 QAbstractTextDocumentLayout *layout = doc->documentLayout();
470 QObject::connect(layout, SIGNAL(update(QRectF)), q, SIGNAL(updateRequest(QRectF)));
471 QObject::connect(layout, SIGNAL(updateBlock(QTextBlock)), q, SLOT(_q_updateBlock(QTextBlock)));
472 QObject::connect(layout, SIGNAL(documentSizeChanged(QSizeF)), q, SIGNAL(documentSizeChanged(QSizeF)));
476 void QQuickTextControlPrivate::setBlinkingCursorEnabled(bool enable)
478 Q_Q(QQuickTextControl);
480 if (enable && qApp->styleHints()->cursorFlashTime() > 0)
481 cursorBlinkTimer.start(qApp->styleHints()->cursorFlashTime() / 2, q);
483 cursorBlinkTimer.stop();
490 void QQuickTextControlPrivate::extendWordwiseSelection(int suggestedNewPosition, qreal mouseXPosition)
492 Q_Q(QQuickTextControl);
494 // if inside the initial selected word keep that
495 if (suggestedNewPosition >= selectedWordOnDoubleClick.selectionStart()
496 && suggestedNewPosition <= selectedWordOnDoubleClick.selectionEnd()) {
497 q->setTextCursor(selectedWordOnDoubleClick);
501 QTextCursor curs = selectedWordOnDoubleClick;
502 curs.setPosition(suggestedNewPosition, QTextCursor::KeepAnchor);
504 if (!curs.movePosition(QTextCursor::StartOfWord))
506 const int wordStartPos = curs.position();
508 const int blockPos = curs.block().position();
509 const QPointF blockCoordinates = q->blockBoundingRect(curs.block()).topLeft();
511 QTextLine line = currentTextLine(curs);
515 const qreal wordStartX = line.cursorToX(curs.position() - blockPos) + blockCoordinates.x();
517 if (!curs.movePosition(QTextCursor::EndOfWord))
519 const int wordEndPos = curs.position();
521 const QTextLine otherLine = currentTextLine(curs);
522 if (otherLine.textStart() != line.textStart()
523 || wordEndPos == wordStartPos)
526 const qreal wordEndX = line.cursorToX(curs.position() - blockPos) + blockCoordinates.x();
528 if (!wordSelectionEnabled && (mouseXPosition < wordStartX || mouseXPosition > wordEndX))
531 if (wordSelectionEnabled) {
532 if (suggestedNewPosition < selectedWordOnDoubleClick.position()) {
533 cursor.setPosition(selectedWordOnDoubleClick.selectionEnd());
534 setCursorPosition(wordStartPos, QTextCursor::KeepAnchor);
536 cursor.setPosition(selectedWordOnDoubleClick.selectionStart());
537 setCursorPosition(wordEndPos, QTextCursor::KeepAnchor);
540 // keep the already selected word even when moving to the left
542 if (suggestedNewPosition < selectedWordOnDoubleClick.position())
543 cursor.setPosition(selectedWordOnDoubleClick.selectionEnd());
545 cursor.setPosition(selectedWordOnDoubleClick.selectionStart());
547 const qreal differenceToStart = mouseXPosition - wordStartX;
548 const qreal differenceToEnd = wordEndX - mouseXPosition;
550 if (differenceToStart < differenceToEnd)
551 setCursorPosition(wordStartPos, QTextCursor::KeepAnchor);
553 setCursorPosition(wordEndPos, QTextCursor::KeepAnchor);
556 if (interactionFlags & Qt::TextSelectableByMouse) {
557 #ifndef QT_NO_CLIPBOARD
558 setClipboardSelection();
560 selectionChanged(true);
564 void QQuickTextControlPrivate::extendBlockwiseSelection(int suggestedNewPosition)
566 Q_Q(QQuickTextControl);
568 // if inside the initial selected line keep that
569 if (suggestedNewPosition >= selectedBlockOnTrippleClick.selectionStart()
570 && suggestedNewPosition <= selectedBlockOnTrippleClick.selectionEnd()) {
571 q->setTextCursor(selectedBlockOnTrippleClick);
575 if (suggestedNewPosition < selectedBlockOnTrippleClick.position()) {
576 cursor.setPosition(selectedBlockOnTrippleClick.selectionEnd());
577 cursor.setPosition(suggestedNewPosition, QTextCursor::KeepAnchor);
578 cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor);
580 cursor.setPosition(selectedBlockOnTrippleClick.selectionStart());
581 cursor.setPosition(suggestedNewPosition, QTextCursor::KeepAnchor);
582 cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
583 cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
586 if (interactionFlags & Qt::TextSelectableByMouse) {
587 #ifndef QT_NO_CLIPBOARD
588 setClipboardSelection();
590 selectionChanged(true);
594 void QQuickTextControlPrivate::_q_deleteSelected()
596 if (!(interactionFlags & Qt::TextEditable) || !cursor.hasSelection())
598 cursor.removeSelectedText();
601 void QQuickTextControl::undo()
603 Q_D(QQuickTextControl);
604 d->repaintSelection();
605 const int oldCursorPos = d->cursor.position();
606 d->doc->undo(&d->cursor);
607 if (d->cursor.position() != oldCursorPos)
608 emit cursorPositionChanged();
609 emit cursorRectangleChanged();
612 void QQuickTextControl::redo()
614 Q_D(QQuickTextControl);
615 d->repaintSelection();
616 const int oldCursorPos = d->cursor.position();
617 d->doc->redo(&d->cursor);
618 if (d->cursor.position() != oldCursorPos)
619 emit cursorPositionChanged();
620 emit cursorRectangleChanged();
623 QQuickTextControl::QQuickTextControl(QTextDocument *doc, QObject *parent)
624 : QObject(*new QQuickTextControlPrivate, parent)
626 Q_D(QQuickTextControl);
627 d->init(Qt::PlainText, QString(), doc);
630 QQuickTextControl::~QQuickTextControl()
634 void QQuickTextControl::setView(QObject *view)
636 Q_D(QQuickTextControl);
637 d->contextObject = view;
640 QObject *QQuickTextControl::view() const
642 Q_D(const QQuickTextControl);
643 return d->contextObject;
646 QTextDocument *QQuickTextControl::document() const
648 Q_D(const QQuickTextControl);
652 void QQuickTextControl::setTextCursor(const QTextCursor &cursor)
654 Q_D(QQuickTextControl);
656 d->cursorIsFocusIndicator = false;
657 const bool posChanged = cursor.position() != d->cursor.position();
658 const QTextCursor oldSelection = d->cursor;
660 d->cursorOn = d->hasFocus && (d->interactionFlags & Qt::TextEditable);
661 d->_q_updateCurrentCharFormatAndSelection();
662 emit cursorRectangleChanged();
663 d->repaintOldAndNewSelection(oldSelection);
665 emit cursorPositionChanged();
668 QTextCursor QQuickTextControl::textCursor() const
670 Q_D(const QQuickTextControl);
674 #ifndef QT_NO_CLIPBOARD
676 void QQuickTextControl::cut()
678 Q_D(QQuickTextControl);
679 if (!(d->interactionFlags & Qt::TextEditable) || !d->cursor.hasSelection())
682 d->cursor.removeSelectedText();
685 void QQuickTextControl::copy()
687 Q_D(QQuickTextControl);
688 if (!d->cursor.hasSelection())
690 QMimeData *data = createMimeDataFromSelection();
691 QGuiApplication::clipboard()->setMimeData(data);
694 void QQuickTextControl::paste(QClipboard::Mode mode)
696 const QMimeData *md = QGuiApplication::clipboard()->mimeData(mode);
698 insertFromMimeData(md);
702 void QQuickTextControl::clear()
704 Q_D(QQuickTextControl);
705 // clears and sets empty content
710 void QQuickTextControl::selectAll()
712 Q_D(QQuickTextControl);
713 const int selectionLength = qAbs(d->cursor.position() - d->cursor.anchor());
714 d->cursor.select(QTextCursor::Document);
715 d->selectionChanged(selectionLength != qAbs(d->cursor.position() - d->cursor.anchor()));
716 d->cursorIsFocusIndicator = false;
717 emit updateRequest();
720 void QQuickTextControl::processEvent(QEvent *e, const QPointF &coordinateOffset)
723 m.translate(coordinateOffset.x(), coordinateOffset.y());
727 void QQuickTextControl::processEvent(QEvent *e, const QMatrix &matrix)
729 Q_D(QQuickTextControl);
730 if (d->interactionFlags == Qt::NoTextInteraction) {
736 case QEvent::KeyPress:
737 d->keyPressEvent(static_cast<QKeyEvent *>(e));
739 case QEvent::MouseButtonPress: {
740 QMouseEvent *ev = static_cast<QMouseEvent *>(e);
741 d->mousePressEvent(ev, matrix.map(ev->localPos()));
743 case QEvent::MouseMove: {
744 QMouseEvent *ev = static_cast<QMouseEvent *>(e);
745 d->mouseMoveEvent(ev, matrix.map(ev->localPos()));
747 case QEvent::MouseButtonRelease: {
748 QMouseEvent *ev = static_cast<QMouseEvent *>(e);
749 d->mouseReleaseEvent(ev, matrix.map(ev->localPos()));
751 case QEvent::MouseButtonDblClick: {
752 QMouseEvent *ev = static_cast<QMouseEvent *>(e);
753 d->mouseDoubleClickEvent(ev, matrix.map(ev->localPos()));
755 case QEvent::InputMethod:
756 d->inputMethodEvent(static_cast<QInputMethodEvent *>(e));
758 case QEvent::FocusIn:
759 case QEvent::FocusOut:
760 d->focusEvent(static_cast<QFocusEvent *>(e));
763 case QEvent::EnabledChange:
764 d->isEnabled = e->isAccepted();
767 case QEvent::ShortcutOverride:
768 if (d->interactionFlags & Qt::TextEditable) {
769 QKeyEvent* ke = static_cast<QKeyEvent *>(e);
770 if (ke->modifiers() == Qt::NoModifier
771 || ke->modifiers() == Qt::ShiftModifier
772 || ke->modifiers() == Qt::KeypadModifier) {
773 if (ke->key() < Qt::Key_Escape) {
782 case Qt::Key_Backspace:
793 #ifndef QT_NO_SHORTCUT
794 } else if (ke == QKeySequence::Copy
795 || ke == QKeySequence::Paste
796 || ke == QKeySequence::Cut
797 || ke == QKeySequence::Redo
798 || ke == QKeySequence::Undo
799 || ke == QKeySequence::MoveToNextWord
800 || ke == QKeySequence::MoveToPreviousWord
801 || ke == QKeySequence::MoveToStartOfDocument
802 || ke == QKeySequence::MoveToEndOfDocument
803 || ke == QKeySequence::SelectNextWord
804 || ke == QKeySequence::SelectPreviousWord
805 || ke == QKeySequence::SelectStartOfLine
806 || ke == QKeySequence::SelectEndOfLine
807 || ke == QKeySequence::SelectStartOfBlock
808 || ke == QKeySequence::SelectEndOfBlock
809 || ke == QKeySequence::SelectStartOfDocument
810 || ke == QKeySequence::SelectEndOfDocument
811 || ke == QKeySequence::SelectAll
823 bool QQuickTextControl::event(QEvent *e)
825 return QObject::event(e);
828 void QQuickTextControl::timerEvent(QTimerEvent *e)
830 Q_D(QQuickTextControl);
831 if (e->timerId() == d->cursorBlinkTimer.timerId()) {
832 d->cursorOn = !d->cursorOn;
835 // if (d->cursor.hasSelection())
836 // d->cursorOn &= (QGuiApplication::style()->styleHint(QStyle::SH_BlinkCursorWhenTextSelected)
840 } else if (e->timerId() == d->trippleClickTimer.timerId()) {
841 d->trippleClickTimer.stop();
845 void QQuickTextControl::setPlainText(const QString &text)
847 Q_D(QQuickTextControl);
848 d->setContent(Qt::PlainText, text);
851 void QQuickTextControl::setHtml(const QString &text)
853 Q_D(QQuickTextControl);
854 d->setContent(Qt::RichText, text);
857 void QQuickTextControlPrivate::keyPressEvent(QKeyEvent *e)
859 Q_Q(QQuickTextControl);
860 #ifndef QT_NO_SHORTCUT
861 if (e == QKeySequence::SelectAll) {
866 #ifndef QT_NO_CLIPBOARD
867 else if (e == QKeySequence::Copy) {
873 #endif // QT_NO_SHORTCUT
875 if (interactionFlags & Qt::TextSelectableByKeyboard
876 && cursorMoveKeyEvent(e))
879 if (interactionFlags & Qt::LinksAccessibleByKeyboard) {
880 if ((e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) && cursor.hasSelection()) {
882 activateLinkUnderCursor();
887 if (!(interactionFlags & Qt::TextEditable)) {
892 if (e->key() == Qt::Key_Direction_L || e->key() == Qt::Key_Direction_R) {
893 QTextBlockFormat fmt;
894 fmt.setLayoutDirection((e->key() == Qt::Key_Direction_L) ? Qt::LeftToRight : Qt::RightToLeft);
895 cursor.mergeBlockFormat(fmt);
899 // schedule a repaint of the region of the cursor, as when we move it we
900 // want to make sure the old cursor disappears (not noticeable when moving
901 // only a few pixels but noticeable when jumping between cells in tables for
905 if (e->key() == Qt::Key_Backspace && !(e->modifiers() & ~Qt::ShiftModifier)) {
906 QTextBlockFormat blockFmt = cursor.blockFormat();
907 QTextList *list = cursor.currentList();
908 if (list && cursor.atBlockStart() && !cursor.hasSelection()) {
909 list->remove(cursor.block());
910 } else if (cursor.atBlockStart() && blockFmt.indent() > 0) {
911 blockFmt.setIndent(blockFmt.indent() - 1);
912 cursor.setBlockFormat(blockFmt);
914 QTextCursor localCursor = cursor;
915 localCursor.deletePreviousChar();
919 #ifndef QT_NO_SHORTCUT
920 else if (e == QKeySequence::InsertParagraphSeparator) {
921 cursor.insertBlock();
924 } else if (e == QKeySequence::InsertLineSeparator) {
925 cursor.insertText(QString(QChar::LineSeparator));
932 #ifndef QT_NO_SHORTCUT
933 else if (e == QKeySequence::Undo) {
936 else if (e == QKeySequence::Redo) {
939 #ifndef QT_NO_CLIPBOARD
940 else if (e == QKeySequence::Cut) {
943 else if (e == QKeySequence::Paste) {
944 QClipboard::Mode mode = QClipboard::Clipboard;
948 else if (e == QKeySequence::Delete) {
949 QTextCursor localCursor = cursor;
950 localCursor.deleteChar();
952 else if (e == QKeySequence::DeleteEndOfWord) {
953 if (!cursor.hasSelection())
954 cursor.movePosition(QTextCursor::NextWord, QTextCursor::KeepAnchor);
955 cursor.removeSelectedText();
957 else if (e == QKeySequence::DeleteStartOfWord) {
958 if (!cursor.hasSelection())
959 cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor);
960 cursor.removeSelectedText();
962 else if (e == QKeySequence::DeleteEndOfLine) {
963 QTextBlock block = cursor.block();
964 if (cursor.position() == block.position() + block.length() - 2)
965 cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
967 cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
968 cursor.removeSelectedText();
970 #endif // QT_NO_SHORTCUT
978 QString text = e->text();
979 if (!text.isEmpty() && (text.at(0).isPrint() || text.at(0) == QLatin1Char('\t'))) {
980 cursor.insertText(text);
993 emit q->cursorRectangleChanged();
995 updateCurrentCharFormat();
998 void QQuickTextControlPrivate::_q_updateBlock(const QTextBlock &block)
1000 Q_Q(QQuickTextControl);
1001 QRectF br = q->blockBoundingRect(block);
1002 br.setRight(qreal(INT_MAX)); // the block might have shrunk
1003 emit q->updateRequest(br);
1006 QRectF QQuickTextControlPrivate::rectForPosition(int position) const
1008 Q_Q(const QQuickTextControl);
1009 const QTextBlock block = doc->findBlock(position);
1010 if (!block.isValid())
1012 const QAbstractTextDocumentLayout *docLayout = doc->documentLayout();
1013 const QTextLayout *layout = block.layout();
1014 const QPointF layoutPos = q->blockBoundingRect(block).topLeft();
1015 int relativePos = position - block.position();
1016 if (preeditCursor != 0) {
1017 int preeditPos = layout->preeditAreaPosition();
1018 if (relativePos == preeditPos)
1019 relativePos += preeditCursor;
1020 else if (relativePos > preeditPos)
1021 relativePos += layout->preeditAreaText().length();
1023 QTextLine line = layout->lineForTextPosition(relativePos);
1028 #ifndef QT_NO_PROPERTIES
1029 cursorWidth = docLayout->property("cursorWidth").toInt(&ok);
1037 if (line.isValid()) {
1038 qreal x = line.cursorToX(relativePos);
1040 r = QRectF(layoutPos.x() + x, layoutPos.y() + line.y(), cursorWidth + w, line.height());
1042 r = QRectF(layoutPos.x(), layoutPos.y(), cursorWidth, 10); // #### correct height
1048 static inline bool firstFramePosLessThanCursorPos(QTextFrame *frame, int position)
1050 return frame->firstPosition() < position;
1053 static inline bool cursorPosLessThanLastFramePos(int position, QTextFrame *frame)
1055 return position < frame->lastPosition();
1058 static QRectF boundingRectOfFloatsInSelection(const QTextCursor &cursor)
1061 QTextFrame *frame = cursor.currentFrame();
1062 const QList<QTextFrame *> children = frame->childFrames();
1064 const QList<QTextFrame *>::ConstIterator firstFrame = qLowerBound(children.constBegin(), children.constEnd(),
1065 cursor.selectionStart(), firstFramePosLessThanCursorPos);
1066 const QList<QTextFrame *>::ConstIterator lastFrame = qUpperBound(children.constBegin(), children.constEnd(),
1067 cursor.selectionEnd(), cursorPosLessThanLastFramePos);
1068 for (QList<QTextFrame *>::ConstIterator it = firstFrame; it != lastFrame; ++it) {
1069 if ((*it)->frameFormat().position() != QTextFrameFormat::InFlow)
1070 r |= frame->document()->documentLayout()->frameBoundingRect(*it);
1075 QRectF QQuickTextControl::selectionRect(const QTextCursor &cursor) const
1077 Q_D(const QQuickTextControl);
1079 QRectF r = d->rectForPosition(cursor.selectionStart());
1081 if (cursor.hasComplexSelection() && cursor.currentTable()) {
1082 QTextTable *table = cursor.currentTable();
1084 r = d->doc->documentLayout()->frameBoundingRect(table);
1086 int firstRow, numRows, firstColumn, numColumns;
1087 cursor.selectedTableCells(&firstRow, &numRows, &firstColumn, &numColumns);
1089 const QTextTableCell firstCell = table->cellAt(firstRow, firstColumn);
1090 const QTextTableCell lastCell = table->cellAt(firstRow + numRows - 1, firstColumn + numColumns - 1);
1092 const QAbstractTextDocumentLayout * const layout = doc->documentLayout();
1094 QRectF tableSelRect = layout->blockBoundingRect(firstCell.firstCursorPosition().block());
1096 for (int col = firstColumn; col < firstColumn + numColumns; ++col) {
1097 const QTextTableCell cell = table->cellAt(firstRow, col);
1098 const qreal y = layout->blockBoundingRect(cell.firstCursorPosition().block()).top();
1100 tableSelRect.setTop(qMin(tableSelRect.top(), y));
1103 for (int row = firstRow; row < firstRow + numRows; ++row) {
1104 const QTextTableCell cell = table->cellAt(row, firstColumn);
1105 const qreal x = layout->blockBoundingRect(cell.firstCursorPosition().block()).left();
1107 tableSelRect.setLeft(qMin(tableSelRect.left(), x));
1110 for (int col = firstColumn; col < firstColumn + numColumns; ++col) {
1111 const QTextTableCell cell = table->cellAt(firstRow + numRows - 1, col);
1112 const qreal y = layout->blockBoundingRect(cell.lastCursorPosition().block()).bottom();
1114 tableSelRect.setBottom(qMax(tableSelRect.bottom(), y));
1117 for (int row = firstRow; row < firstRow + numRows; ++row) {
1118 const QTextTableCell cell = table->cellAt(row, firstColumn + numColumns - 1);
1119 const qreal x = layout->blockBoundingRect(cell.lastCursorPosition().block()).right();
1121 tableSelRect.setRight(qMax(tableSelRect.right(), x));
1124 r = tableSelRect.toRect();
1126 } else if (cursor.hasSelection()) {
1127 const int position = cursor.selectionStart();
1128 const int anchor = cursor.selectionEnd();
1129 const QTextBlock posBlock = d->doc->findBlock(position);
1130 const QTextBlock anchorBlock = d->doc->findBlock(anchor);
1131 if (posBlock == anchorBlock && posBlock.isValid() && posBlock.layout()->lineCount()) {
1132 const QTextLine posLine = posBlock.layout()->lineForTextPosition(position - posBlock.position());
1133 const QTextLine anchorLine = anchorBlock.layout()->lineForTextPosition(anchor - anchorBlock.position());
1135 const int firstLine = qMin(posLine.lineNumber(), anchorLine.lineNumber());
1136 const int lastLine = qMax(posLine.lineNumber(), anchorLine.lineNumber());
1137 const QTextLayout *layout = posBlock.layout();
1139 for (int i = firstLine; i <= lastLine; ++i) {
1140 r |= layout->lineAt(i).rect();
1141 r |= layout->lineAt(i).naturalTextRect(); // might be bigger in the case of wrap not enabled
1143 r.translate(blockBoundingRect(posBlock).topLeft());
1145 QRectF anchorRect = d->rectForPosition(cursor.selectionEnd());
1147 r |= boundingRectOfFloatsInSelection(cursor);
1148 QRectF frameRect(d->doc->documentLayout()->frameBoundingRect(cursor.currentFrame()));
1149 r.setLeft(frameRect.left());
1150 r.setRight(frameRect.right());
1153 r.adjust(-1, -1, 1, 1);
1159 QRectF QQuickTextControl::selectionRect() const
1161 Q_D(const QQuickTextControl);
1162 return selectionRect(d->cursor);
1165 void QQuickTextControlPrivate::mousePressEvent(QMouseEvent *e, const QPointF &pos)
1167 Q_Q(QQuickTextControl);
1169 mousePressed = (interactionFlags & Qt::TextSelectableByMouse);
1170 mousePressPos = pos.toPoint();
1172 if (sendMouseEventToInputContext(e, pos))
1175 if (interactionFlags & Qt::LinksAccessibleByMouse) {
1176 anchorOnMousePress = q->anchorAt(pos);
1178 if (cursorIsFocusIndicator) {
1179 cursorIsFocusIndicator = false;
1181 cursor.clearSelection();
1184 if (!(e->button() & Qt::LeftButton)) {
1187 } else if (!(interactionFlags & (Qt::TextSelectableByMouse | Qt::TextEditable))) {
1188 if (!(interactionFlags & Qt::LinksAccessibleByMouse))
1193 cursorIsFocusIndicator = false;
1194 const QTextCursor oldSelection = cursor;
1195 const int oldCursorPos = cursor.position();
1199 if (trippleClickTimer.isActive()
1200 && ((pos - trippleClickPoint).toPoint().manhattanLength() < qApp->styleHints()->startDragDistance())) {
1202 cursor.movePosition(QTextCursor::StartOfBlock);
1203 cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
1204 cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
1205 selectedBlockOnTrippleClick = cursor;
1207 anchorOnMousePress = QString();
1209 trippleClickTimer.stop();
1211 int cursorPos = q->hitTest(pos, Qt::FuzzyHit);
1212 if (cursorPos == -1) {
1217 if (e->modifiers() == Qt::ShiftModifier && (interactionFlags & Qt::TextSelectableByMouse)) {
1218 if (wordSelectionEnabled && !selectedWordOnDoubleClick.hasSelection()) {
1219 selectedWordOnDoubleClick = cursor;
1220 selectedWordOnDoubleClick.select(QTextCursor::WordUnderCursor);
1223 if (selectedBlockOnTrippleClick.hasSelection())
1224 extendBlockwiseSelection(cursorPos);
1225 else if (selectedWordOnDoubleClick.hasSelection())
1226 extendWordwiseSelection(cursorPos, pos.x());
1227 else if (!wordSelectionEnabled)
1228 setCursorPosition(cursorPos, QTextCursor::KeepAnchor);
1230 setCursorPosition(cursorPos);
1234 if (interactionFlags & Qt::TextEditable) {
1235 emit q->cursorRectangleChanged();
1236 if (cursor.position() != oldCursorPos)
1237 emit q->cursorPositionChanged();
1238 _q_updateCurrentCharFormatAndSelection();
1240 if (cursor.position() != oldCursorPos) {
1241 emit q->cursorPositionChanged();
1242 emit q->cursorRectangleChanged();
1246 repaintOldAndNewSelection(oldSelection);
1247 hadSelectionOnMousePress = cursor.hasSelection();
1250 void QQuickTextControlPrivate::mouseMoveEvent(QMouseEvent *e, const QPointF &mousePos)
1252 Q_Q(QQuickTextControl);
1254 if (interactionFlags & Qt::LinksAccessibleByMouse) {
1255 QString anchor = q->anchorAt(mousePos);
1256 if (anchor != highlightedAnchor) {
1257 highlightedAnchor = anchor;
1258 emit q->linkHovered(anchor);
1262 if ((e->buttons() & Qt::LeftButton)) {
1263 const bool editable = interactionFlags & Qt::TextEditable;
1267 || selectedWordOnDoubleClick.hasSelection()
1268 || selectedBlockOnTrippleClick.hasSelection()))
1271 const QTextCursor oldSelection = cursor;
1272 const int oldCursorPos = cursor.position();
1277 const qreal mouseX = qreal(mousePos.x());
1279 int newCursorPos = q->hitTest(mousePos, Qt::FuzzyHit);
1281 if (isPreediting()) {
1282 // note: oldCursorPos not including preedit
1283 int selectionStartPos = q->hitTest(mousePressPos, Qt::FuzzyHit);
1284 if (newCursorPos != selectionStartPos) {
1286 // commit invalidates positions
1287 newCursorPos = q->hitTest(mousePos, Qt::FuzzyHit);
1288 selectionStartPos = q->hitTest(mousePressPos, Qt::FuzzyHit);
1289 setCursorPosition(selectionStartPos);
1293 if (newCursorPos == -1)
1296 if (wordSelectionEnabled && !selectedWordOnDoubleClick.hasSelection()) {
1297 selectedWordOnDoubleClick = cursor;
1298 selectedWordOnDoubleClick.select(QTextCursor::WordUnderCursor);
1301 if (selectedBlockOnTrippleClick.hasSelection())
1302 extendBlockwiseSelection(newCursorPos);
1303 else if (selectedWordOnDoubleClick.hasSelection())
1304 extendWordwiseSelection(newCursorPos, mouseX);
1305 else if (!isPreediting())
1306 setCursorPosition(newCursorPos, QTextCursor::KeepAnchor);
1308 if (interactionFlags & Qt::TextEditable) {
1309 if (cursor.position() != oldCursorPos)
1310 emit q->cursorPositionChanged();
1311 _q_updateCurrentCharFormatAndSelection();
1313 qGuiApp->inputPanel()->update(Qt::ImQueryInput);
1314 } else if (cursor.position() != oldCursorPos) {
1315 emit q->cursorPositionChanged();
1317 selectionChanged(true);
1318 repaintOldAndNewSelection(oldSelection);
1321 sendMouseEventToInputContext(e, mousePos);
1324 void QQuickTextControlPrivate::mouseReleaseEvent(QMouseEvent *e, const QPointF &pos)
1326 Q_Q(QQuickTextControl);
1328 if (sendMouseEventToInputContext(e, pos))
1331 const QTextCursor oldSelection = cursor;
1332 const int oldCursorPos = cursor.position();
1335 mousePressed = false;
1336 #ifndef QT_NO_CLIPBOARD
1337 setClipboardSelection();
1338 selectionChanged(true);
1339 } else if (e->button() == Qt::MidButton
1340 && (interactionFlags & Qt::TextEditable)
1341 && QGuiApplication::clipboard()->supportsSelection()) {
1342 setCursorPosition(pos);
1343 const QMimeData *md = QGuiApplication::clipboard()->mimeData(QClipboard::Selection);
1345 q->insertFromMimeData(md);
1349 repaintOldAndNewSelection(oldSelection);
1351 if (cursor.position() != oldCursorPos) {
1352 emit q->cursorPositionChanged();
1353 emit q->cursorRectangleChanged();
1356 if (interactionFlags & Qt::LinksAccessibleByMouse) {
1357 if (!(e->button() & Qt::LeftButton))
1360 const QString anchor = q->anchorAt(pos);
1362 if (anchor.isEmpty())
1365 if (!cursor.hasSelection()
1366 || (anchor == anchorOnMousePress && hadSelectionOnMousePress)) {
1368 const int anchorPos = q->hitTest(pos, Qt::ExactHit);
1369 if (anchorPos != -1) {
1370 cursor.setPosition(anchorPos);
1372 QString anchor = anchorOnMousePress;
1373 anchorOnMousePress = QString();
1374 activateLinkUnderCursor(anchor);
1380 void QQuickTextControlPrivate::mouseDoubleClickEvent(QMouseEvent *e, const QPointF &pos)
1382 Q_Q(QQuickTextControl);
1384 if (e->button() == Qt::LeftButton && (interactionFlags & Qt::TextSelectableByMouse)) {
1387 const QTextCursor oldSelection = cursor;
1388 setCursorPosition(pos);
1389 QTextLine line = currentTextLine(cursor);
1390 bool doEmit = false;
1391 if (line.isValid() && line.textLength()) {
1392 cursor.select(QTextCursor::WordUnderCursor);
1395 repaintOldAndNewSelection(oldSelection);
1397 cursorIsFocusIndicator = false;
1398 selectedWordOnDoubleClick = cursor;
1400 trippleClickPoint = pos;
1401 trippleClickTimer.start(qApp->styleHints()->mouseDoubleClickInterval(), q);
1404 #ifndef QT_NO_CLIPBOARD
1405 setClipboardSelection();
1407 emit q->cursorPositionChanged();
1409 } else if (!sendMouseEventToInputContext(e, pos)) {
1414 bool QQuickTextControlPrivate::sendMouseEventToInputContext(QMouseEvent *e, const QPointF &pos)
1416 #if !defined(QT_NO_IM)
1417 Q_Q(QQuickTextControl);
1421 if (contextObject && isPreediting()) {
1422 QTextLayout *layout = cursor.block().layout();
1423 int cursorPos = q->hitTest(pos, Qt::FuzzyHit) - cursor.position();
1425 if (cursorPos >= 0 && cursorPos <= layout->preeditAreaText().length()) {
1426 if (e->type() == QEvent::MouseButtonRelease) {
1427 qApp->inputPanel()->invokeAction(QInputPanel::Click, cursorPos);
1440 void QQuickTextControlPrivate::inputMethodEvent(QInputMethodEvent *e)
1442 Q_Q(QQuickTextControl);
1443 if (!(interactionFlags & Qt::TextEditable) || cursor.isNull()) {
1447 bool isGettingInput = !e->commitString().isEmpty()
1448 || e->preeditString() != cursor.block().layout()->preeditAreaText()
1449 || e->replacementLength() > 0;
1450 bool forceSelectionChanged = false;
1452 cursor.beginEditBlock();
1453 if (isGettingInput) {
1454 cursor.removeSelectedText();
1457 // insert commit string
1458 if (!e->commitString().isEmpty() || e->replacementLength()) {
1459 QTextCursor c = cursor;
1460 c.setPosition(c.position() + e->replacementStart());
1461 c.setPosition(c.position() + e->replacementLength(), QTextCursor::KeepAnchor);
1462 c.insertText(e->commitString());
1465 for (int i = 0; i < e->attributes().size(); ++i) {
1466 const QInputMethodEvent::Attribute &a = e->attributes().at(i);
1467 if (a.type == QInputMethodEvent::Selection) {
1468 QTextCursor oldCursor = cursor;
1469 int blockStart = a.start + cursor.block().position();
1470 cursor.setPosition(blockStart, QTextCursor::MoveAnchor);
1471 cursor.setPosition(blockStart + a.length, QTextCursor::KeepAnchor);
1472 emit q->cursorRectangleChanged();
1473 repaintOldAndNewSelection(oldCursor);
1474 forceSelectionChanged = true;
1478 QTextBlock block = cursor.block();
1479 QTextLayout *layout = block.layout();
1481 layout->setPreeditArea(cursor.position() - block.position(), e->preeditString());
1482 QList<QTextLayout::FormatRange> overrides;
1483 const int oldPreeditCursor = preeditCursor;
1484 preeditCursor = e->preeditString().length();
1486 for (int i = 0; i < e->attributes().size(); ++i) {
1487 const QInputMethodEvent::Attribute &a = e->attributes().at(i);
1488 if (a.type == QInputMethodEvent::Cursor) {
1489 preeditCursor = a.start;
1490 hideCursor = !a.length;
1491 } else if (a.type == QInputMethodEvent::TextFormat) {
1492 QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat();
1494 QTextLayout::FormatRange o;
1495 o.start = a.start + cursor.position() - block.position();
1496 o.length = a.length;
1498 overrides.append(o);
1502 layout->setAdditionalFormats(overrides);
1503 tentativeCommit = e->tentativeCommitString();
1505 cursor.endEditBlock();
1507 QTextCursorPrivate *cursor_d = QTextCursorPrivate::getPrivate(&cursor);
1510 if (oldPreeditCursor != preeditCursor)
1511 emit q->cursorRectangleChanged();
1512 selectionChanged(forceSelectionChanged);
1515 QVariant QQuickTextControl::inputMethodQuery(Qt::InputMethodQuery property) const
1517 Q_D(const QQuickTextControl);
1518 QTextBlock block = d->cursor.block();
1520 case Qt::ImCursorRectangle:
1521 return cursorRect();
1523 return QVariant(d->cursor.charFormat().font());
1524 case Qt::ImCursorPosition:
1525 return QVariant(d->cursor.position() - block.position());
1526 case Qt::ImSurroundingText:
1527 return QVariant(block.text());
1528 case Qt::ImCurrentSelection:
1529 return QVariant(d->cursor.selectedText());
1530 case Qt::ImMaximumTextLength:
1531 return QVariant(); // No limit.
1532 case Qt::ImAnchorPosition:
1533 return QVariant(d->cursor.anchor() - block.position());
1539 void QQuickTextControl::setFocus(bool focus, Qt::FocusReason reason)
1541 QFocusEvent ev(focus ? QEvent::FocusIn : QEvent::FocusOut,
1546 void QQuickTextControlPrivate::focusEvent(QFocusEvent *e)
1548 Q_Q(QQuickTextControl);
1549 emit q->updateRequest(q->selectionRect());
1550 if (e->gotFocus()) {
1551 setBlinkingCursorEnabled(interactionFlags & (Qt::TextEditable | Qt::TextSelectableByKeyboard));
1554 setBlinkingCursorEnabled(false);
1556 if (cursorIsFocusIndicator
1557 && e->reason() != Qt::ActiveWindowFocusReason
1558 && e->reason() != Qt::PopupFocusReason
1559 && cursor.hasSelection()) {
1560 cursor.clearSelection();
1563 hasFocus = e->gotFocus();
1566 QString QQuickTextControlPrivate::anchorForCursor(const QTextCursor &anchorCursor) const
1568 if (anchorCursor.hasSelection()) {
1569 QTextCursor cursor = anchorCursor;
1570 if (cursor.selectionStart() != cursor.position())
1571 cursor.setPosition(cursor.selectionStart());
1572 cursor.movePosition(QTextCursor::NextCharacter);
1573 QTextCharFormat fmt = cursor.charFormat();
1574 if (fmt.isAnchor() && fmt.hasProperty(QTextFormat::AnchorHref))
1575 return fmt.stringProperty(QTextFormat::AnchorHref);
1580 QTextCursor QQuickTextControl::cursorForPosition(const QPointF &pos) const
1582 Q_D(const QQuickTextControl);
1583 int cursorPos = hitTest(pos, Qt::FuzzyHit);
1584 if (cursorPos == -1)
1586 QTextCursor c(d->doc);
1587 c.setPosition(cursorPos);
1591 QRectF QQuickTextControl::cursorRect(const QTextCursor &cursor) const
1593 Q_D(const QQuickTextControl);
1594 if (cursor.isNull())
1597 return d->rectForPosition(cursor.position());
1600 QRectF QQuickTextControl::cursorRect() const
1602 Q_D(const QQuickTextControl);
1603 return cursorRect(d->cursor);
1606 QRectF QQuickTextControlPrivate::cursorRectPlusUnicodeDirectionMarkers(const QTextCursor &cursor) const
1608 if (cursor.isNull())
1611 return rectForPosition(cursor.position()).adjusted(-4, 0, 4, 0);
1614 QString QQuickTextControl::anchorAt(const QPointF &pos) const
1616 Q_D(const QQuickTextControl);
1617 return d->doc->documentLayout()->anchorAt(pos);
1620 QString QQuickTextControl::anchorAtCursor() const
1622 Q_D(const QQuickTextControl);
1624 return d->anchorForCursor(d->cursor);
1627 int QQuickTextControl::cursorWidth() const
1629 #ifndef QT_NO_PROPERTIES
1630 Q_D(const QQuickTextControl);
1631 return d->doc->documentLayout()->property("cursorWidth").toInt();
1637 void QQuickTextControl::setCursorWidth(int width)
1639 Q_D(QQuickTextControl);
1640 #ifdef QT_NO_PROPERTIES
1644 width = textCursorWidth;
1645 d->doc->documentLayout()->setProperty("cursorWidth", width);
1650 bool QQuickTextControl::acceptRichText() const
1652 Q_D(const QQuickTextControl);
1653 return d->acceptRichText;
1656 void QQuickTextControl::setAcceptRichText(bool accept)
1658 Q_D(QQuickTextControl);
1659 d->acceptRichText = accept;
1662 void QQuickTextControl::setTextWidth(qreal width)
1664 Q_D(QQuickTextControl);
1665 d->doc->setTextWidth(width);
1668 qreal QQuickTextControl::textWidth() const
1670 Q_D(const QQuickTextControl);
1671 return d->doc->textWidth();
1674 QSizeF QQuickTextControl::size() const
1676 Q_D(const QQuickTextControl);
1677 return d->doc->size();
1680 void QQuickTextControl::moveCursor(QTextCursor::MoveOperation op, QTextCursor::MoveMode mode)
1682 Q_D(QQuickTextControl);
1683 const QTextCursor oldSelection = d->cursor;
1684 const bool moved = d->cursor.movePosition(op, mode);
1685 d->_q_updateCurrentCharFormatAndSelection();
1686 emit cursorRectangleChanged();
1687 d->repaintOldAndNewSelection(oldSelection);
1689 emit cursorPositionChanged();
1692 bool QQuickTextControl::canPaste() const
1694 #ifndef QT_NO_CLIPBOARD
1695 Q_D(const QQuickTextControl);
1696 if (d->interactionFlags & Qt::TextEditable) {
1697 const QMimeData *md = QGuiApplication::clipboard()->mimeData();
1698 return md && canInsertFromMimeData(md);
1704 void QQuickTextControl::setCursorIsFocusIndicator(bool b)
1706 Q_D(QQuickTextControl);
1707 d->cursorIsFocusIndicator = b;
1711 bool QQuickTextControl::cursorIsFocusIndicator() const
1713 Q_D(const QQuickTextControl);
1714 return d->cursorIsFocusIndicator;
1717 void QQuickTextControl::setWordSelectionEnabled(bool enabled)
1719 Q_D(QQuickTextControl);
1720 d->wordSelectionEnabled = enabled;
1723 bool QQuickTextControl::isWordSelectionEnabled() const
1725 Q_D(const QQuickTextControl);
1726 return d->wordSelectionEnabled;
1729 QMimeData *QQuickTextControl::createMimeDataFromSelection() const
1731 Q_D(const QQuickTextControl);
1732 const QTextDocumentFragment fragment(d->cursor);
1733 return new QQuickTextEditMimeData(fragment);
1736 bool QQuickTextControl::canInsertFromMimeData(const QMimeData *source) const
1738 Q_D(const QQuickTextControl);
1739 if (d->acceptRichText)
1740 return source->hasText()
1741 || source->hasHtml()
1742 || source->hasFormat(QLatin1String("application/x-qrichtext"))
1743 || source->hasFormat(QLatin1String("application/x-qt-richtext"));
1745 return source->hasText();
1748 void QQuickTextControl::insertFromMimeData(const QMimeData *source)
1750 Q_D(QQuickTextControl);
1751 if (!(d->interactionFlags & Qt::TextEditable) || !source)
1754 bool hasData = false;
1755 QTextDocumentFragment fragment;
1756 #ifndef QT_NO_TEXTHTMLPARSER
1757 if (source->hasFormat(QLatin1String("application/x-qrichtext")) && d->acceptRichText) {
1758 // x-qrichtext is always UTF-8 (taken from Qt3 since we don't use it anymore).
1759 QString richtext = QString::fromUtf8(source->data(QLatin1String("application/x-qrichtext")));
1760 richtext.prepend(QLatin1String("<meta name=\"qrichtext\" content=\"1\" />"));
1761 fragment = QTextDocumentFragment::fromHtml(richtext, d->doc);
1763 } else if (source->hasHtml() && d->acceptRichText) {
1764 fragment = QTextDocumentFragment::fromHtml(source->html(), d->doc);
1767 QString text = source->text();
1768 if (!text.isNull()) {
1769 fragment = QTextDocumentFragment::fromPlainText(text);
1774 fragment = QTextDocumentFragment::fromPlainText(source->text());
1775 #endif // QT_NO_TEXTHTMLPARSER
1778 d->cursor.insertFragment(fragment);
1779 emit cursorRectangleChanged();
1782 void QQuickTextControlPrivate::activateLinkUnderCursor(QString href)
1784 QTextCursor oldCursor = cursor;
1786 if (href.isEmpty()) {
1787 QTextCursor tmp = cursor;
1788 if (tmp.selectionStart() != tmp.position())
1789 tmp.setPosition(tmp.selectionStart());
1790 tmp.movePosition(QTextCursor::NextCharacter);
1791 href = tmp.charFormat().anchorHref();
1796 if (!cursor.hasSelection()) {
1797 QTextBlock block = cursor.block();
1798 const int cursorPos = cursor.position();
1800 QTextBlock::Iterator it = block.begin();
1801 QTextBlock::Iterator linkFragment;
1803 for (; !it.atEnd(); ++it) {
1804 QTextFragment fragment = it.fragment();
1805 const int fragmentPos = fragment.position();
1806 if (fragmentPos <= cursorPos &&
1807 fragmentPos + fragment.length() > cursorPos) {
1813 if (!linkFragment.atEnd()) {
1815 cursor.setPosition(it.fragment().position());
1816 if (it != block.begin()) {
1819 QTextFragment fragment = it.fragment();
1820 if (fragment.charFormat().anchorHref() != href)
1822 cursor.setPosition(fragment.position());
1823 } while (it != block.begin());
1826 for (it = linkFragment; !it.atEnd(); ++it) {
1827 QTextFragment fragment = it.fragment();
1828 if (fragment.charFormat().anchorHref() != href)
1830 cursor.setPosition(fragment.position() + fragment.length(), QTextCursor::KeepAnchor);
1836 cursorIsFocusIndicator = true;
1838 cursorIsFocusIndicator = false;
1839 cursor.clearSelection();
1841 repaintOldAndNewSelection(oldCursor);
1843 emit q_func()->linkActivated(href);
1846 bool QQuickTextControlPrivate::isPreediting() const
1848 QTextLayout *layout = cursor.block().layout();
1849 if (layout && !layout->preeditAreaText().isEmpty())
1855 void QQuickTextControlPrivate::commitPreedit()
1857 if (!isPreediting())
1860 cursor.beginEditBlock();
1861 qApp->inputPanel()->reset();
1863 if (!tentativeCommit.isEmpty()) {
1864 cursor.insertText(tentativeCommit);
1865 tentativeCommit.clear();
1869 QTextBlock block = cursor.block();
1870 QTextLayout *layout = block.layout();
1871 layout->setPreeditArea(-1, QString());
1872 layout->clearAdditionalFormats();
1873 cursor.endEditBlock();
1876 void QQuickTextControl::setTextInteractionFlags(Qt::TextInteractionFlags flags)
1878 Q_D(QQuickTextControl);
1879 if (flags == d->interactionFlags)
1881 d->interactionFlags = flags;
1884 d->setBlinkingCursorEnabled(flags & (Qt::TextEditable | Qt::TextSelectableByKeyboard));
1887 Qt::TextInteractionFlags QQuickTextControl::textInteractionFlags() const
1889 Q_D(const QQuickTextControl);
1890 return d->interactionFlags;
1893 QString QQuickTextControl::toPlainText() const
1895 Q_D(const QQuickTextControl);
1896 QString plainText = document()->toPlainText();
1897 if (!d->tentativeCommit.isEmpty())
1898 plainText.insert(textCursor().position(), d->tentativeCommit);
1902 #ifndef QT_NO_TEXTHTMLPARSER
1903 QString QQuickTextControl::toHtml() const
1905 // note: currently not including tentative commit
1906 return document()->toHtml();
1910 QPalette QQuickTextControl::palette() const
1912 Q_D(const QQuickTextControl);
1916 void QQuickTextControl::setPalette(const QPalette &pal)
1918 Q_D(QQuickTextControl);
1922 bool QQuickTextControl::cursorOn() const
1924 Q_D(const QQuickTextControl);
1928 QAbstractTextDocumentLayout::PaintContext QQuickTextControl::getPaintContext() const
1930 Q_D(const QQuickTextControl);
1932 QAbstractTextDocumentLayout::PaintContext ctx;
1934 ctx.palette = d->palette;
1935 if (d->cursorOn && d->isEnabled) {
1937 ctx.cursorPosition = -1;
1938 else if (d->preeditCursor != 0)
1939 ctx.cursorPosition = - (d->preeditCursor + 2);
1941 ctx.cursorPosition = d->cursor.position();
1944 if (d->cursor.hasSelection()) {
1945 QAbstractTextDocumentLayout::Selection selection;
1946 selection.cursor = d->cursor;
1947 if (0 && d->cursorIsFocusIndicator) {
1951 opt.palette = ctx.palette;
1952 QStyleHintReturnVariant ret;
1953 QStyle *style = QGuiApplication::style();
1955 style = widget->style();
1956 style->styleHint(QStyle::SH_TextControl_FocusIndicatorTextCharFormat, &opt, widget, &ret);
1957 selection.format = qvariant_cast<QTextFormat>(ret.variant).toCharFormat();
1960 QPalette::ColorGroup cg = d->hasFocus ? QPalette::Active : QPalette::Inactive;
1961 selection.format.setBackground(ctx.palette.brush(cg, QPalette::Highlight));
1962 selection.format.setForeground(ctx.palette.brush(cg, QPalette::HighlightedText));
1963 if (fullWidthSelection)
1964 selection.format.setProperty(QTextFormat::FullWidthSelection, true);
1966 ctx.selections.append(selection);
1972 void QQuickTextControl::drawContents(QPainter *p, const QRectF &rect)
1974 Q_D(QQuickTextControl);
1976 QAbstractTextDocumentLayout::PaintContext ctx = getPaintContext();
1978 p->setClipRect(rect, Qt::IntersectClip);
1981 d->doc->documentLayout()->draw(p, ctx);
1985 int QQuickTextControl::hitTest(const QPointF &point, Qt::HitTestAccuracy accuracy) const
1987 Q_D(const QQuickTextControl);
1988 return d->doc->documentLayout()->hitTest(point, accuracy);
1991 QRectF QQuickTextControl::blockBoundingRect(const QTextBlock &block) const
1993 Q_D(const QQuickTextControl);
1994 return d->doc->documentLayout()->blockBoundingRect(block);
1999 QStringList QQuickTextEditMimeData::formats() const
2001 if (!fragment.isEmpty())
2002 return QStringList() << QString::fromLatin1("text/plain") << QString::fromLatin1("text/html")
2003 #ifndef QT_NO_TEXTODFWRITER
2004 << QString::fromLatin1("application/vnd.oasis.opendocument.text")
2008 return QMimeData::formats();
2011 QVariant QQuickTextEditMimeData::retrieveData(const QString &mimeType, QVariant::Type type) const
2013 if (!fragment.isEmpty())
2015 return QMimeData::retrieveData(mimeType, type);
2018 void QQuickTextEditMimeData::setup() const
2020 QQuickTextEditMimeData *that = const_cast<QQuickTextEditMimeData *>(this);
2021 #ifndef QT_NO_TEXTHTMLPARSER
2022 that->setData(QLatin1String("text/html"), fragment.toHtml("utf-8").toUtf8());
2024 #ifndef QT_NO_TEXTODFWRITER
2027 QTextDocumentWriter writer(&buffer, "ODF");
2028 writer.write(fragment);
2030 that->setData(QLatin1String("application/vnd.oasis.opendocument.text"), buffer.data());
2033 that->setText(fragment.toPlainText());
2034 fragment = QTextDocumentFragment();
2040 #include "moc_qquicktextcontrol_p.cpp"
2042 #endif // QT_NO_TEXTCONTROL