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 "qtextcontrol_p.h"
43 #include "qtextcontrol_p_p.h"
45 #ifndef QT_NO_TEXTCONTROL
53 #include <qclipboard.h>
55 #include <qinputpanel.h>
56 #include "private/qtextdocumentlayout_p.h"
57 #include "private/qabstracttextdocumentlayout_p.h"
58 #include "qtextdocument.h"
59 #include "private/qtextdocument_p.h"
60 #include "qtextlist.h"
61 #include "qtextdocumentwriter.h"
62 #include "private/qtextcursor_p.h"
63 #include "qpagedpaintdevice.h"
64 #include "private/qpagedpaintdevice_p.h"
66 #include <qtextformat.h>
67 #include <qdatetime.h>
69 #include <qguiapplication.h>
71 #include <qtexttable.h>
74 #include <qstylehints.h>
76 // ### these should come from QStyleHints
77 const int textCursorWidth = 1;
78 const bool fullWidthSelection = true;
80 //#ifndef QT_NO_SHORTCUT
81 //#include "private/QGuiApplication_p.h"
82 //#include "private/qshortcutmap_p.h"
83 //#include <qkeysequence.h>
84 //#define ACCEL_KEY(k) (!qApp->d_func()->shortcutMap.hasShortcutForKeySequence(k) ? QLatin1Char('\t') + QString(QKeySequence(k)) : QString())
86 //#define ACCEL_KEY(k) QString()
91 #ifndef QT_NO_CONTEXTMENU
92 #if defined(Q_WS_WIN) || defined(Q_WS_X11)
93 extern bool qt_use_rtl_extensions;
97 // could go into QTextCursor...
98 static QTextLine currentTextLine(const QTextCursor &cursor)
100 const QTextBlock block = cursor.block();
101 if (!block.isValid())
104 const QTextLayout *layout = block.layout();
108 const int relativePos = cursor.position() - block.position();
109 return layout->lineForTextPosition(relativePos);
112 QTextControlPrivate::QTextControlPrivate()
113 : doc(0), cursorOn(false), cursorIsFocusIndicator(false),
114 interactionFlags(Qt::TextEditorInteraction),
116 #ifndef QT_NO_DRAGANDDROP
117 mousePressed(false), mightStartDrag(false),
119 lastSelectionState(false), ignoreAutomaticScrollbarAdjustement(false),
120 overwriteMode(false),
121 acceptRichText(true),
122 preeditCursor(0), hideCursor(false),
124 #ifdef QT_KEYPAD_NAVIGATION
128 hadSelectionOnMousePress(false),
129 ignoreUnusedNavigationEvents(false),
130 openExternalLinks(false),
131 wordSelectionEnabled(false)
134 bool QTextControlPrivate::cursorMoveKeyEvent(QKeyEvent *e)
136 #ifdef QT_NO_SHORTCUT
144 const QTextCursor oldSelection = cursor;
145 const int oldCursorPos = cursor.position();
147 QTextCursor::MoveMode mode = QTextCursor::MoveAnchor;
148 QTextCursor::MoveOperation op = QTextCursor::NoMove;
152 #ifndef QT_NO_SHORTCUT
153 if (e == QKeySequence::MoveToNextChar) {
154 op = QTextCursor::Right;
156 else if (e == QKeySequence::MoveToPreviousChar) {
157 op = QTextCursor::Left;
159 else if (e == QKeySequence::SelectNextChar) {
160 op = QTextCursor::Right;
161 mode = QTextCursor::KeepAnchor;
163 else if (e == QKeySequence::SelectPreviousChar) {
164 op = QTextCursor::Left;
165 mode = QTextCursor::KeepAnchor;
167 else if (e == QKeySequence::SelectNextWord) {
168 op = QTextCursor::WordRight;
169 mode = QTextCursor::KeepAnchor;
171 else if (e == QKeySequence::SelectPreviousWord) {
172 op = QTextCursor::WordLeft;
173 mode = QTextCursor::KeepAnchor;
175 else if (e == QKeySequence::SelectStartOfLine) {
176 op = QTextCursor::StartOfLine;
177 mode = QTextCursor::KeepAnchor;
179 else if (e == QKeySequence::SelectEndOfLine) {
180 op = QTextCursor::EndOfLine;
181 mode = QTextCursor::KeepAnchor;
183 else if (e == QKeySequence::SelectStartOfBlock) {
184 op = QTextCursor::StartOfBlock;
185 mode = QTextCursor::KeepAnchor;
187 else if (e == QKeySequence::SelectEndOfBlock) {
188 op = QTextCursor::EndOfBlock;
189 mode = QTextCursor::KeepAnchor;
191 else if (e == QKeySequence::SelectStartOfDocument) {
192 op = QTextCursor::Start;
193 mode = QTextCursor::KeepAnchor;
195 else if (e == QKeySequence::SelectEndOfDocument) {
196 op = QTextCursor::End;
197 mode = QTextCursor::KeepAnchor;
199 else if (e == QKeySequence::SelectPreviousLine) {
200 op = QTextCursor::Up;
201 mode = QTextCursor::KeepAnchor;
203 else if (e == QKeySequence::SelectNextLine) {
204 op = QTextCursor::Down;
205 mode = QTextCursor::KeepAnchor;
207 QTextBlock block = cursor.block();
208 QTextLine line = currentTextLine(cursor);
209 if (!block.next().isValid()
211 && line.lineNumber() == block.layout()->lineCount() - 1)
212 op = QTextCursor::End;
215 else if (e == QKeySequence::MoveToNextWord) {
216 op = QTextCursor::WordRight;
218 else if (e == QKeySequence::MoveToPreviousWord) {
219 op = QTextCursor::WordLeft;
221 else if (e == QKeySequence::MoveToEndOfBlock) {
222 op = QTextCursor::EndOfBlock;
224 else if (e == QKeySequence::MoveToStartOfBlock) {
225 op = QTextCursor::StartOfBlock;
227 else if (e == QKeySequence::MoveToNextLine) {
228 op = QTextCursor::Down;
230 else if (e == QKeySequence::MoveToPreviousLine) {
231 op = QTextCursor::Up;
233 else if (e == QKeySequence::MoveToPreviousLine) {
234 op = QTextCursor::Up;
236 else if (e == QKeySequence::MoveToStartOfLine) {
237 op = QTextCursor::StartOfLine;
239 else if (e == QKeySequence::MoveToEndOfLine) {
240 op = QTextCursor::EndOfLine;
242 else if (e == QKeySequence::MoveToStartOfDocument) {
243 op = QTextCursor::Start;
245 else if (e == QKeySequence::MoveToEndOfDocument) {
246 op = QTextCursor::End;
248 #endif // QT_NO_SHORTCUT
253 // Except for pageup and pagedown, Mac OS X has very different behavior, we don't do it all, but
254 // here's the breakdown:
255 // Shift still works as an anchor, but only one of the other keys can be down Ctrl (Command),
256 // Alt (Option), or Meta (Control).
257 // Command/Control + Left/Right -- Move to left or right of the line
258 // + Up/Down -- Move to top bottom of the file. (Control doesn't move the cursor)
259 // Option + Left/Right -- Move one word Left/right.
260 // + Up/Down -- Begin/End of Paragraph.
261 // Home/End Top/Bottom of file. (usually don't move the cursor, but will select)
263 bool visualNavigation = cursor.visualNavigation();
264 cursor.setVisualNavigation(true);
265 const bool moved = cursor.movePosition(op, mode);
266 cursor.setVisualNavigation(visualNavigation);
267 q->ensureCursorVisible();
269 bool ignoreNavigationEvents = ignoreUnusedNavigationEvents;
270 bool isNavigationEvent = e->key() == Qt::Key_Up || e->key() == Qt::Key_Down;
272 #ifdef QT_KEYPAD_NAVIGATION
273 ignoreNavigationEvents = ignoreNavigationEvents || QGuiApplication::keypadNavigationEnabled();
274 isNavigationEvent = isNavigationEvent ||
275 (QGuiApplication::navigationMode() == Qt::NavigationModeKeypadDirectional
276 && (e->key() == Qt::Key_Left || e->key() == Qt::Key_Right));
278 isNavigationEvent = isNavigationEvent || e->key() == Qt::Key_Left || e->key() == Qt::Key_Right;
282 if (cursor.position() != oldCursorPos)
283 emit q->cursorPositionChanged();
284 emit q->microFocusChanged();
285 } else if (ignoreNavigationEvents && isNavigationEvent && oldSelection.anchor() == cursor.anchor()) {
289 selectionChanged(/*forceEmitSelectionChanged =*/(mode == QTextCursor::KeepAnchor));
291 repaintOldAndNewSelection(oldSelection);
296 void QTextControlPrivate::updateCurrentCharFormat()
300 QTextCharFormat fmt = cursor.charFormat();
301 if (fmt == lastCharFormat)
303 lastCharFormat = fmt;
305 emit q->currentCharFormatChanged(fmt);
306 emit q->microFocusChanged();
309 void QTextControlPrivate::indent()
311 QTextBlockFormat blockFmt = cursor.blockFormat();
313 QTextList *list = cursor.currentList();
315 QTextBlockFormat modifier;
316 modifier.setIndent(blockFmt.indent() + 1);
317 cursor.mergeBlockFormat(modifier);
319 QTextListFormat format = list->format();
320 format.setIndent(format.indent() + 1);
322 if (list->itemNumber(cursor.block()) == 1)
323 list->setFormat(format);
325 cursor.createList(format);
329 void QTextControlPrivate::outdent()
331 QTextBlockFormat blockFmt = cursor.blockFormat();
333 QTextList *list = cursor.currentList();
336 QTextBlockFormat modifier;
337 modifier.setIndent(blockFmt.indent() - 1);
338 cursor.mergeBlockFormat(modifier);
340 QTextListFormat listFmt = list->format();
341 listFmt.setIndent(listFmt.indent() - 1);
342 list->setFormat(listFmt);
346 void QTextControlPrivate::gotoNextTableCell()
348 QTextTable *table = cursor.currentTable();
349 QTextTableCell cell = table->cellAt(cursor);
351 int newColumn = cell.column() + cell.columnSpan();
352 int newRow = cell.row();
354 if (newColumn >= table->columns()) {
357 if (newRow >= table->rows())
358 table->insertRows(table->rows(), 1);
361 cell = table->cellAt(newRow, newColumn);
362 cursor = cell.firstCursorPosition();
365 void QTextControlPrivate::gotoPreviousTableCell()
367 QTextTable *table = cursor.currentTable();
368 QTextTableCell cell = table->cellAt(cursor);
370 int newColumn = cell.column() - 1;
371 int newRow = cell.row();
374 newColumn = table->columns() - 1;
380 cell = table->cellAt(newRow, newColumn);
381 cursor = cell.firstCursorPosition();
384 void QTextControlPrivate::createAutoBulletList()
386 cursor.beginEditBlock();
388 QTextBlockFormat blockFmt = cursor.blockFormat();
390 QTextListFormat listFmt;
391 listFmt.setStyle(QTextListFormat::ListDisc);
392 listFmt.setIndent(blockFmt.indent() + 1);
394 blockFmt.setIndent(0);
395 cursor.setBlockFormat(blockFmt);
397 cursor.createList(listFmt);
399 cursor.endEditBlock();
402 void QTextControlPrivate::init(Qt::TextFormat format, const QString &text, QTextDocument *document)
405 setContent(format, text, document);
407 doc->setUndoRedoEnabled(interactionFlags & Qt::TextEditable);
408 q->setCursorWidth(-1);
410 QObject::connect(q, SIGNAL(updateCursorRequest(QRectF)), q, SIGNAL(updateRequest(QRectF)));
413 void QTextControlPrivate::setContent(Qt::TextFormat format, const QString &text, QTextDocument *document)
417 // for use when called from setPlainText. we may want to re-use the currently
418 // set char format then.
419 const QTextCharFormat charFormatForInsertion = cursor.charFormat();
421 bool clearDocument = true;
425 clearDocument = false;
427 palette = QGuiApplication::palette();
428 doc = new QTextDocument(q);
430 _q_documentLayoutChanged();
431 cursor = QTextCursor(doc);
433 // #### doc->documentLayout()->setPaintDevice(viewport);
435 QObject::connect(doc, SIGNAL(contentsChanged()), q, SLOT(_q_updateCurrentCharFormatAndSelection()));
436 QObject::connect(doc, SIGNAL(cursorPositionChanged(QTextCursor)), q, SLOT(_q_emitCursorPosChanged(QTextCursor)));
437 QObject::connect(doc, SIGNAL(documentLayoutChanged()), q, SLOT(_q_documentLayoutChanged()));
439 // convenience signal forwards
440 QObject::connect(doc, SIGNAL(undoAvailable(bool)), q, SIGNAL(undoAvailable(bool)));
441 QObject::connect(doc, SIGNAL(redoAvailable(bool)), q, SIGNAL(redoAvailable(bool)));
442 QObject::connect(doc, SIGNAL(modificationChanged(bool)), q, SIGNAL(modificationChanged(bool)));
443 QObject::connect(doc, SIGNAL(blockCountChanged(int)), q, SIGNAL(blockCountChanged(int)));
446 bool previousUndoRedoState = doc->isUndoRedoEnabled();
448 doc->setUndoRedoEnabled(false);
450 //Saving the index save some time.
451 static int contentsChangedIndex = QTextDocument::staticMetaObject.indexOfSignal("contentsChanged()");
452 static int textChangedIndex = QTextControl::staticMetaObject.indexOfSignal("textChanged()");
453 // avoid multiple textChanged() signals being emitted
454 QMetaObject::disconnect(doc, contentsChangedIndex, q, textChangedIndex);
456 if (!text.isEmpty()) {
457 // clear 'our' cursor for insertion to prevent
458 // the emission of the cursorPositionChanged() signal.
459 // instead we emit it only once at the end instead of
460 // at the end of the document after loading and when
461 // positioning the cursor again to the start of the
463 cursor = QTextCursor();
464 if (format == Qt::PlainText) {
465 QTextCursor formatCursor(doc);
466 // put the setPlainText and the setCharFormat into one edit block,
467 // so that the syntax highlight triggers only /once/ for the entire
468 // document, not twice.
469 formatCursor.beginEditBlock();
470 doc->setPlainText(text);
471 doc->setUndoRedoEnabled(false);
472 formatCursor.select(QTextCursor::Document);
473 formatCursor.setCharFormat(charFormatForInsertion);
474 formatCursor.endEditBlock();
476 #ifndef QT_NO_TEXTHTMLPARSER
479 doc->setPlainText(text);
481 doc->setUndoRedoEnabled(false);
483 cursor = QTextCursor(doc);
484 } else if (clearDocument) {
487 cursor.setCharFormat(charFormatForInsertion);
489 QMetaObject::connect(doc, contentsChangedIndex, q, textChangedIndex);
490 emit q->textChanged();
492 doc->setUndoRedoEnabled(previousUndoRedoState);
493 _q_updateCurrentCharFormatAndSelection();
495 doc->setModified(false);
497 q->ensureCursorVisible();
498 emit q->cursorPositionChanged();
501 void QTextControlPrivate::startDrag()
503 #ifndef QT_NO_DRAGANDDROP
505 mousePressed = false;
508 QMimeData *data = q->createMimeDataFromSelection();
510 QDrag *drag = new QDrag(contextObject);
511 drag->setMimeData(data);
513 Qt::DropActions actions = Qt::CopyAction;
514 Qt::DropAction action;
515 if (interactionFlags & Qt::TextEditable) {
516 actions |= Qt::MoveAction;
517 action = drag->exec(actions, Qt::MoveAction);
519 action = drag->exec(actions, Qt::CopyAction);
522 if (action == Qt::MoveAction && drag->target() != contextObject)
523 cursor.removeSelectedText();
527 void QTextControlPrivate::setCursorPosition(const QPointF &pos)
530 const int cursorPos = q->hitTest(pos, Qt::FuzzyHit);
533 cursor.setPosition(cursorPos);
536 void QTextControlPrivate::setCursorPosition(int pos, QTextCursor::MoveMode mode)
538 cursor.setPosition(pos, mode);
540 if (mode != QTextCursor::KeepAnchor) {
541 selectedWordOnDoubleClick = QTextCursor();
542 selectedBlockOnTrippleClick = QTextCursor();
546 void QTextControlPrivate::repaintCursor()
549 emit q->updateCursorRequest(cursorRectPlusUnicodeDirectionMarkers(cursor));
552 void QTextControlPrivate::repaintOldAndNewSelection(const QTextCursor &oldSelection)
555 if (cursor.hasSelection()
556 && oldSelection.hasSelection()
557 && cursor.currentFrame() == oldSelection.currentFrame()
558 && !cursor.hasComplexSelection()
559 && !oldSelection.hasComplexSelection()
560 && cursor.anchor() == oldSelection.anchor()
562 QTextCursor differenceSelection(doc);
563 differenceSelection.setPosition(oldSelection.position());
564 differenceSelection.setPosition(cursor.position(), QTextCursor::KeepAnchor);
565 emit q->updateRequest(q->selectionRect(differenceSelection));
567 if (!oldSelection.hasSelection() && !cursor.hasSelection()) {
568 if (!oldSelection.isNull())
569 emit q->updateCursorRequest(q->selectionRect(oldSelection) | cursorRectPlusUnicodeDirectionMarkers(oldSelection));
570 emit q->updateCursorRequest(q->selectionRect() | cursorRectPlusUnicodeDirectionMarkers(cursor));
573 if (!oldSelection.isNull())
574 emit q->updateRequest(q->selectionRect(oldSelection) | cursorRectPlusUnicodeDirectionMarkers(oldSelection));
575 emit q->updateRequest(q->selectionRect() | cursorRectPlusUnicodeDirectionMarkers(cursor));
580 void QTextControlPrivate::selectionChanged(bool forceEmitSelectionChanged /*=false*/)
583 if (forceEmitSelectionChanged)
584 emit q->selectionChanged();
586 bool current = cursor.hasSelection();
587 if (current == lastSelectionState)
590 lastSelectionState = current;
591 emit q->copyAvailable(current);
592 if (!forceEmitSelectionChanged)
593 emit q->selectionChanged();
594 emit q->microFocusChanged();
597 void QTextControlPrivate::_q_updateCurrentCharFormatAndSelection()
599 updateCurrentCharFormat();
603 #ifndef QT_NO_CLIPBOARD
604 void QTextControlPrivate::setClipboardSelection()
606 QClipboard *clipboard = QGuiApplication::clipboard();
607 if (!cursor.hasSelection() || !clipboard->supportsSelection())
610 QMimeData *data = q->createMimeDataFromSelection();
611 clipboard->setMimeData(data, QClipboard::Selection);
615 void QTextControlPrivate::_q_emitCursorPosChanged(const QTextCursor &someCursor)
618 if (someCursor.isCopyOf(cursor)) {
619 emit q->cursorPositionChanged();
620 emit q->microFocusChanged();
624 void QTextControlPrivate::_q_documentLayoutChanged()
627 QAbstractTextDocumentLayout *layout = doc->documentLayout();
628 QObject::connect(layout, SIGNAL(update(QRectF)), q, SIGNAL(updateRequest(QRectF)));
629 QObject::connect(layout, SIGNAL(updateBlock(QTextBlock)), q, SLOT(_q_updateBlock(QTextBlock)));
630 QObject::connect(layout, SIGNAL(documentSizeChanged(QSizeF)), q, SIGNAL(documentSizeChanged(QSizeF)));
634 void QTextControlPrivate::setBlinkingCursorEnabled(bool enable)
638 if (enable && qApp->styleHints()->cursorFlashTime() > 0)
639 cursorBlinkTimer.start(qApp->styleHints()->cursorFlashTime() / 2, q);
641 cursorBlinkTimer.stop();
648 void QTextControlPrivate::extendWordwiseSelection(int suggestedNewPosition, qreal mouseXPosition)
652 // if inside the initial selected word keep that
653 if (suggestedNewPosition >= selectedWordOnDoubleClick.selectionStart()
654 && suggestedNewPosition <= selectedWordOnDoubleClick.selectionEnd()) {
655 q->setTextCursor(selectedWordOnDoubleClick);
659 QTextCursor curs = selectedWordOnDoubleClick;
660 curs.setPosition(suggestedNewPosition, QTextCursor::KeepAnchor);
662 if (!curs.movePosition(QTextCursor::StartOfWord))
664 const int wordStartPos = curs.position();
666 const int blockPos = curs.block().position();
667 const QPointF blockCoordinates = q->blockBoundingRect(curs.block()).topLeft();
669 QTextLine line = currentTextLine(curs);
673 const qreal wordStartX = line.cursorToX(curs.position() - blockPos) + blockCoordinates.x();
675 if (!curs.movePosition(QTextCursor::EndOfWord))
677 const int wordEndPos = curs.position();
679 const QTextLine otherLine = currentTextLine(curs);
680 if (otherLine.textStart() != line.textStart()
681 || wordEndPos == wordStartPos)
684 const qreal wordEndX = line.cursorToX(curs.position() - blockPos) + blockCoordinates.x();
686 if (!wordSelectionEnabled && (mouseXPosition < wordStartX || mouseXPosition > wordEndX))
689 if (wordSelectionEnabled) {
690 if (suggestedNewPosition < selectedWordOnDoubleClick.position()) {
691 cursor.setPosition(selectedWordOnDoubleClick.selectionEnd());
692 setCursorPosition(wordStartPos, QTextCursor::KeepAnchor);
694 cursor.setPosition(selectedWordOnDoubleClick.selectionStart());
695 setCursorPosition(wordEndPos, QTextCursor::KeepAnchor);
698 // keep the already selected word even when moving to the left
700 if (suggestedNewPosition < selectedWordOnDoubleClick.position())
701 cursor.setPosition(selectedWordOnDoubleClick.selectionEnd());
703 cursor.setPosition(selectedWordOnDoubleClick.selectionStart());
705 const qreal differenceToStart = mouseXPosition - wordStartX;
706 const qreal differenceToEnd = wordEndX - mouseXPosition;
708 if (differenceToStart < differenceToEnd)
709 setCursorPosition(wordStartPos, QTextCursor::KeepAnchor);
711 setCursorPosition(wordEndPos, QTextCursor::KeepAnchor);
714 if (interactionFlags & Qt::TextSelectableByMouse) {
715 #ifndef QT_NO_CLIPBOARD
716 setClipboardSelection();
718 selectionChanged(true);
722 void QTextControlPrivate::extendBlockwiseSelection(int suggestedNewPosition)
726 // if inside the initial selected line keep that
727 if (suggestedNewPosition >= selectedBlockOnTrippleClick.selectionStart()
728 && suggestedNewPosition <= selectedBlockOnTrippleClick.selectionEnd()) {
729 q->setTextCursor(selectedBlockOnTrippleClick);
733 if (suggestedNewPosition < selectedBlockOnTrippleClick.position()) {
734 cursor.setPosition(selectedBlockOnTrippleClick.selectionEnd());
735 cursor.setPosition(suggestedNewPosition, QTextCursor::KeepAnchor);
736 cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor);
738 cursor.setPosition(selectedBlockOnTrippleClick.selectionStart());
739 cursor.setPosition(suggestedNewPosition, QTextCursor::KeepAnchor);
740 cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
741 cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
744 if (interactionFlags & Qt::TextSelectableByMouse) {
745 #ifndef QT_NO_CLIPBOARD
746 setClipboardSelection();
748 selectionChanged(true);
752 void QTextControlPrivate::_q_deleteSelected()
754 if (!(interactionFlags & Qt::TextEditable) || !cursor.hasSelection())
756 cursor.removeSelectedText();
759 void QTextControl::undo()
762 d->repaintSelection();
763 const int oldCursorPos = d->cursor.position();
764 d->doc->undo(&d->cursor);
765 if (d->cursor.position() != oldCursorPos)
766 emit cursorPositionChanged();
767 emit microFocusChanged();
768 ensureCursorVisible();
771 void QTextControl::redo()
774 d->repaintSelection();
775 const int oldCursorPos = d->cursor.position();
776 d->doc->redo(&d->cursor);
777 if (d->cursor.position() != oldCursorPos)
778 emit cursorPositionChanged();
779 emit microFocusChanged();
780 ensureCursorVisible();
783 QTextControl::QTextControl(QObject *parent)
784 : QObject(*new QTextControlPrivate, parent)
790 QTextControl::QTextControl(const QString &text, QObject *parent)
791 : QObject(*new QTextControlPrivate, parent)
794 d->init(Qt::RichText, text);
797 QTextControl::QTextControl(QTextDocument *doc, QObject *parent)
798 : QObject(*new QTextControlPrivate, parent)
801 d->init(Qt::RichText, QString(), doc);
804 QTextControl::~QTextControl()
808 void QTextControl::setView(QObject *view)
811 d->contextObject = view;
814 QObject *QTextControl::view() const
816 Q_D(const QTextControl);
817 return d->contextObject;
820 void QTextControl::setDocument(QTextDocument *document)
823 if (d->doc == document)
826 d->doc->disconnect(this);
827 d->doc->documentLayout()->disconnect(this);
828 d->doc->documentLayout()->setPaintDevice(0);
830 if (d->doc->parent() == this)
834 d->setContent(Qt::RichText, QString(), document);
837 QTextDocument *QTextControl::document() const
839 Q_D(const QTextControl);
843 void QTextControl::setTextCursor(const QTextCursor &cursor)
846 d->cursorIsFocusIndicator = false;
847 const bool posChanged = cursor.position() != d->cursor.position();
848 const QTextCursor oldSelection = d->cursor;
850 d->cursorOn = d->hasFocus && (d->interactionFlags & Qt::TextEditable);
851 d->_q_updateCurrentCharFormatAndSelection();
852 ensureCursorVisible();
853 d->repaintOldAndNewSelection(oldSelection);
855 emit cursorPositionChanged();
858 QTextCursor QTextControl::textCursor() const
860 Q_D(const QTextControl);
864 #ifndef QT_NO_CLIPBOARD
866 void QTextControl::cut()
869 if (!(d->interactionFlags & Qt::TextEditable) || !d->cursor.hasSelection())
872 d->cursor.removeSelectedText();
875 void QTextControl::copy()
878 if (!d->cursor.hasSelection())
880 QMimeData *data = createMimeDataFromSelection();
881 QGuiApplication::clipboard()->setMimeData(data);
884 void QTextControl::paste(QClipboard::Mode mode)
886 const QMimeData *md = QGuiApplication::clipboard()->mimeData(mode);
888 insertFromMimeData(md);
892 void QTextControl::clear()
895 // clears and sets empty content
896 d->extraSelections.clear();
901 void QTextControl::selectAll()
904 const int selectionLength = qAbs(d->cursor.position() - d->cursor.anchor());
905 d->cursor.select(QTextCursor::Document);
906 d->selectionChanged(selectionLength != qAbs(d->cursor.position() - d->cursor.anchor()));
907 d->cursorIsFocusIndicator = false;
908 emit updateRequest();
911 void QTextControl::processEvent(QEvent *e, const QPointF &coordinateOffset)
914 m.translate(coordinateOffset.x(), coordinateOffset.y());
918 void QTextControl::processEvent(QEvent *e, const QMatrix &matrix)
921 if (d->interactionFlags == Qt::NoTextInteraction) {
927 case QEvent::KeyPress:
928 d->keyPressEvent(static_cast<QKeyEvent *>(e));
930 case QEvent::MouseButtonPress: {
931 QMouseEvent *ev = static_cast<QMouseEvent *>(e);
932 d->mousePressEvent(ev, ev->button(), matrix.map(ev->pos()), ev->modifiers(),
933 ev->buttons(), ev->globalPos());
935 case QEvent::MouseMove: {
936 QMouseEvent *ev = static_cast<QMouseEvent *>(e);
937 d->mouseMoveEvent(ev, ev->button(), matrix.map(ev->pos()), ev->modifiers(),
938 ev->buttons(), ev->globalPos());
940 case QEvent::MouseButtonRelease: {
941 QMouseEvent *ev = static_cast<QMouseEvent *>(e);
942 d->mouseReleaseEvent(ev, ev->button(), matrix.map(ev->pos()), ev->modifiers(),
943 ev->buttons(), ev->globalPos());
945 case QEvent::MouseButtonDblClick: {
946 QMouseEvent *ev = static_cast<QMouseEvent *>(e);
947 d->mouseDoubleClickEvent(ev, ev->button(), matrix.map(ev->pos()), ev->modifiers(),
948 ev->buttons(), ev->globalPos());
950 case QEvent::InputMethod:
951 d->inputMethodEvent(static_cast<QInputMethodEvent *>(e));
953 case QEvent::FocusIn:
954 case QEvent::FocusOut:
955 d->focusEvent(static_cast<QFocusEvent *>(e));
958 case QEvent::EnabledChange:
959 d->isEnabled = e->isAccepted();
962 #ifndef QT_NO_DRAGANDDROP
963 case QEvent::DragEnter: {
964 QDragEnterEvent *ev = static_cast<QDragEnterEvent *>(e);
965 if (d->dragEnterEvent(e, ev->mimeData()))
966 ev->acceptProposedAction();
969 case QEvent::DragLeave:
972 case QEvent::DragMove: {
973 QDragMoveEvent *ev = static_cast<QDragMoveEvent *>(e);
974 if (d->dragMoveEvent(e, ev->mimeData(), matrix.map(ev->pos())))
975 ev->acceptProposedAction();
979 QDropEvent *ev = static_cast<QDropEvent *>(e);
980 if (d->dropEvent(ev->mimeData(), matrix.map(ev->pos()), ev->dropAction(), ev->source()))
981 ev->acceptProposedAction();
986 #ifdef QT_KEYPAD_NAVIGATION
987 case QEvent::EnterEditFocus:
988 case QEvent::LeaveEditFocus:
989 if (QGuiApplication::keypadNavigationEnabled())
990 d->editFocusEvent(e);
993 case QEvent::ShortcutOverride:
994 if (d->interactionFlags & Qt::TextEditable) {
995 QKeyEvent* ke = static_cast<QKeyEvent *>(e);
996 if (ke->modifiers() == Qt::NoModifier
997 || ke->modifiers() == Qt::ShiftModifier
998 || ke->modifiers() == Qt::KeypadModifier) {
999 if (ke->key() < Qt::Key_Escape) {
1002 switch (ke->key()) {
1003 case Qt::Key_Return:
1005 case Qt::Key_Delete:
1008 case Qt::Key_Backspace:
1019 #ifndef QT_NO_SHORTCUT
1020 } else if (ke == QKeySequence::Copy
1021 || ke == QKeySequence::Paste
1022 || ke == QKeySequence::Cut
1023 || ke == QKeySequence::Redo
1024 || ke == QKeySequence::Undo
1025 || ke == QKeySequence::MoveToNextWord
1026 || ke == QKeySequence::MoveToPreviousWord
1027 || ke == QKeySequence::MoveToStartOfDocument
1028 || ke == QKeySequence::MoveToEndOfDocument
1029 || ke == QKeySequence::SelectNextWord
1030 || ke == QKeySequence::SelectPreviousWord
1031 || ke == QKeySequence::SelectStartOfLine
1032 || ke == QKeySequence::SelectEndOfLine
1033 || ke == QKeySequence::SelectStartOfBlock
1034 || ke == QKeySequence::SelectEndOfBlock
1035 || ke == QKeySequence::SelectStartOfDocument
1036 || ke == QKeySequence::SelectEndOfDocument
1037 || ke == QKeySequence::SelectAll
1049 bool QTextControl::event(QEvent *e)
1051 return QObject::event(e);
1054 void QTextControl::timerEvent(QTimerEvent *e)
1057 if (e->timerId() == d->cursorBlinkTimer.timerId()) {
1058 d->cursorOn = !d->cursorOn;
1061 // if (d->cursor.hasSelection())
1062 // d->cursorOn &= (QGuiApplication::style()->styleHint(QStyle::SH_BlinkCursorWhenTextSelected)
1066 } else if (e->timerId() == d->trippleClickTimer.timerId()) {
1067 d->trippleClickTimer.stop();
1071 void QTextControl::setPlainText(const QString &text)
1074 d->setContent(Qt::PlainText, text);
1077 void QTextControl::setHtml(const QString &text)
1080 d->setContent(Qt::RichText, text);
1083 void QTextControlPrivate::keyPressEvent(QKeyEvent *e)
1086 #ifndef QT_NO_SHORTCUT
1087 if (e == QKeySequence::SelectAll) {
1092 #ifndef QT_NO_CLIPBOARD
1093 else if (e == QKeySequence::Copy) {
1099 #endif // QT_NO_SHORTCUT
1101 if (interactionFlags & Qt::TextSelectableByKeyboard
1102 && cursorMoveKeyEvent(e))
1105 if (interactionFlags & Qt::LinksAccessibleByKeyboard) {
1106 if ((e->key() == Qt::Key_Return
1107 || e->key() == Qt::Key_Enter
1108 #ifdef QT_KEYPAD_NAVIGATION
1109 || e->key() == Qt::Key_Select
1112 && cursor.hasSelection()) {
1115 activateLinkUnderCursor();
1120 if (!(interactionFlags & Qt::TextEditable)) {
1125 if (e->key() == Qt::Key_Direction_L || e->key() == Qt::Key_Direction_R) {
1126 QTextBlockFormat fmt;
1127 fmt.setLayoutDirection((e->key() == Qt::Key_Direction_L) ? Qt::LeftToRight : Qt::RightToLeft);
1128 cursor.mergeBlockFormat(fmt);
1132 // schedule a repaint of the region of the cursor, as when we move it we
1133 // want to make sure the old cursor disappears (not noticeable when moving
1134 // only a few pixels but noticeable when jumping between cells in tables for
1138 if (e->key() == Qt::Key_Backspace && !(e->modifiers() & ~Qt::ShiftModifier)) {
1139 QTextBlockFormat blockFmt = cursor.blockFormat();
1140 QTextList *list = cursor.currentList();
1141 if (list && cursor.atBlockStart() && !cursor.hasSelection()) {
1142 list->remove(cursor.block());
1143 } else if (cursor.atBlockStart() && blockFmt.indent() > 0) {
1144 blockFmt.setIndent(blockFmt.indent() - 1);
1145 cursor.setBlockFormat(blockFmt);
1147 QTextCursor localCursor = cursor;
1148 localCursor.deletePreviousChar();
1152 #ifndef QT_NO_SHORTCUT
1153 else if (e == QKeySequence::InsertParagraphSeparator) {
1154 cursor.insertBlock();
1157 } else if (e == QKeySequence::InsertLineSeparator) {
1158 cursor.insertText(QString(QChar::LineSeparator));
1165 #ifndef QT_NO_SHORTCUT
1166 else if (e == QKeySequence::Undo) {
1169 else if (e == QKeySequence::Redo) {
1172 #ifndef QT_NO_CLIPBOARD
1173 else if (e == QKeySequence::Cut) {
1176 else if (e == QKeySequence::Paste) {
1177 QClipboard::Mode mode = QClipboard::Clipboard;
1179 if (e->modifiers() == (Qt::CTRL | Qt::SHIFT) && e->key() == Qt::Key_Insert)
1180 mode = QClipboard::Selection;
1185 else if (e == QKeySequence::Delete) {
1186 QTextCursor localCursor = cursor;
1187 localCursor.deleteChar();
1189 else if (e == QKeySequence::DeleteEndOfWord) {
1190 if (!cursor.hasSelection())
1191 cursor.movePosition(QTextCursor::NextWord, QTextCursor::KeepAnchor);
1192 cursor.removeSelectedText();
1194 else if (e == QKeySequence::DeleteStartOfWord) {
1195 if (!cursor.hasSelection())
1196 cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor);
1197 cursor.removeSelectedText();
1199 else if (e == QKeySequence::DeleteEndOfLine) {
1200 QTextBlock block = cursor.block();
1201 if (cursor.position() == block.position() + block.length() - 2)
1202 cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
1204 cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
1205 cursor.removeSelectedText();
1207 #endif // QT_NO_SHORTCUT
1215 QString text = e->text();
1216 if (!text.isEmpty() && (text.at(0).isPrint() || text.at(0) == QLatin1Char('\t'))) {
1218 // no need to call deleteChar() if we have a selection, insertText
1220 && !cursor.hasSelection()
1221 && !cursor.atBlockEnd())
1222 cursor.deleteChar();
1224 cursor.insertText(text);
1237 q->ensureCursorVisible();
1239 updateCurrentCharFormat();
1242 QVariant QTextControl::loadResource(int type, const QUrl &name)
1248 if (QTextEdit *textEdit = qobject_cast<QTextEdit *>(parent())) {
1249 QUrl resolvedName = textEdit->d_func()->resolveUrl(name);
1250 return textEdit->loadResource(type, resolvedName);
1256 void QTextControlPrivate::_q_updateBlock(const QTextBlock &block)
1259 QRectF br = q->blockBoundingRect(block);
1260 br.setRight(qreal(INT_MAX)); // the block might have shrunk
1261 emit q->updateRequest(br);
1264 QRectF QTextControlPrivate::rectForPosition(int position) const
1266 Q_Q(const QTextControl);
1267 const QTextBlock block = doc->findBlock(position);
1268 if (!block.isValid())
1270 const QAbstractTextDocumentLayout *docLayout = doc->documentLayout();
1271 const QTextLayout *layout = block.layout();
1272 const QPointF layoutPos = q->blockBoundingRect(block).topLeft();
1273 int relativePos = position - block.position();
1274 if (preeditCursor != 0) {
1275 int preeditPos = layout->preeditAreaPosition();
1276 if (relativePos == preeditPos)
1277 relativePos += preeditCursor;
1278 else if (relativePos > preeditPos)
1279 relativePos += layout->preeditAreaText().length();
1281 QTextLine line = layout->lineForTextPosition(relativePos);
1286 #ifndef QT_NO_PROPERTIES
1287 cursorWidth = docLayout->property("cursorWidth").toInt(&ok);
1295 if (line.isValid()) {
1296 qreal x = line.cursorToX(relativePos);
1298 if (overwriteMode) {
1299 if (relativePos < line.textLength() - line.textStart())
1300 w = line.cursorToX(relativePos + 1) - x;
1302 w = QFontMetrics(block.layout()->font()).width(QLatin1Char(' ')); // in sync with QTextLine::draw()
1304 r = QRectF(layoutPos.x() + x, layoutPos.y() + line.y(),
1305 cursorWidth + w, line.height());
1307 r = QRectF(layoutPos.x(), layoutPos.y(), cursorWidth, 10); // #### correct height
1313 static inline bool firstFramePosLessThanCursorPos(QTextFrame *frame, int position)
1315 return frame->firstPosition() < position;
1318 static inline bool cursorPosLessThanLastFramePos(int position, QTextFrame *frame)
1320 return position < frame->lastPosition();
1323 static QRectF boundingRectOfFloatsInSelection(const QTextCursor &cursor)
1326 QTextFrame *frame = cursor.currentFrame();
1327 const QList<QTextFrame *> children = frame->childFrames();
1329 const QList<QTextFrame *>::ConstIterator firstFrame = qLowerBound(children.constBegin(), children.constEnd(),
1330 cursor.selectionStart(), firstFramePosLessThanCursorPos);
1331 const QList<QTextFrame *>::ConstIterator lastFrame = qUpperBound(children.constBegin(), children.constEnd(),
1332 cursor.selectionEnd(), cursorPosLessThanLastFramePos);
1333 for (QList<QTextFrame *>::ConstIterator it = firstFrame; it != lastFrame; ++it) {
1334 if ((*it)->frameFormat().position() != QTextFrameFormat::InFlow)
1335 r |= frame->document()->documentLayout()->frameBoundingRect(*it);
1340 QRectF QTextControl::selectionRect(const QTextCursor &cursor) const
1342 Q_D(const QTextControl);
1344 QRectF r = d->rectForPosition(cursor.selectionStart());
1346 if (cursor.hasComplexSelection() && cursor.currentTable()) {
1347 QTextTable *table = cursor.currentTable();
1349 r = d->doc->documentLayout()->frameBoundingRect(table);
1351 int firstRow, numRows, firstColumn, numColumns;
1352 cursor.selectedTableCells(&firstRow, &numRows, &firstColumn, &numColumns);
1354 const QTextTableCell firstCell = table->cellAt(firstRow, firstColumn);
1355 const QTextTableCell lastCell = table->cellAt(firstRow + numRows - 1, firstColumn + numColumns - 1);
1357 const QAbstractTextDocumentLayout * const layout = doc->documentLayout();
1359 QRectF tableSelRect = layout->blockBoundingRect(firstCell.firstCursorPosition().block());
1361 for (int col = firstColumn; col < firstColumn + numColumns; ++col) {
1362 const QTextTableCell cell = table->cellAt(firstRow, col);
1363 const qreal y = layout->blockBoundingRect(cell.firstCursorPosition().block()).top();
1365 tableSelRect.setTop(qMin(tableSelRect.top(), y));
1368 for (int row = firstRow; row < firstRow + numRows; ++row) {
1369 const QTextTableCell cell = table->cellAt(row, firstColumn);
1370 const qreal x = layout->blockBoundingRect(cell.firstCursorPosition().block()).left();
1372 tableSelRect.setLeft(qMin(tableSelRect.left(), x));
1375 for (int col = firstColumn; col < firstColumn + numColumns; ++col) {
1376 const QTextTableCell cell = table->cellAt(firstRow + numRows - 1, col);
1377 const qreal y = layout->blockBoundingRect(cell.lastCursorPosition().block()).bottom();
1379 tableSelRect.setBottom(qMax(tableSelRect.bottom(), y));
1382 for (int row = firstRow; row < firstRow + numRows; ++row) {
1383 const QTextTableCell cell = table->cellAt(row, firstColumn + numColumns - 1);
1384 const qreal x = layout->blockBoundingRect(cell.lastCursorPosition().block()).right();
1386 tableSelRect.setRight(qMax(tableSelRect.right(), x));
1389 r = tableSelRect.toRect();
1391 } else if (cursor.hasSelection()) {
1392 const int position = cursor.selectionStart();
1393 const int anchor = cursor.selectionEnd();
1394 const QTextBlock posBlock = d->doc->findBlock(position);
1395 const QTextBlock anchorBlock = d->doc->findBlock(anchor);
1396 if (posBlock == anchorBlock && posBlock.isValid() && posBlock.layout()->lineCount()) {
1397 const QTextLine posLine = posBlock.layout()->lineForTextPosition(position - posBlock.position());
1398 const QTextLine anchorLine = anchorBlock.layout()->lineForTextPosition(anchor - anchorBlock.position());
1400 const int firstLine = qMin(posLine.lineNumber(), anchorLine.lineNumber());
1401 const int lastLine = qMax(posLine.lineNumber(), anchorLine.lineNumber());
1402 const QTextLayout *layout = posBlock.layout();
1404 for (int i = firstLine; i <= lastLine; ++i) {
1405 r |= layout->lineAt(i).rect();
1406 r |= layout->lineAt(i).naturalTextRect(); // might be bigger in the case of wrap not enabled
1408 r.translate(blockBoundingRect(posBlock).topLeft());
1410 QRectF anchorRect = d->rectForPosition(cursor.selectionEnd());
1412 r |= boundingRectOfFloatsInSelection(cursor);
1413 QRectF frameRect(d->doc->documentLayout()->frameBoundingRect(cursor.currentFrame()));
1414 r.setLeft(frameRect.left());
1415 r.setRight(frameRect.right());
1418 r.adjust(-1, -1, 1, 1);
1424 QRectF QTextControl::selectionRect() const
1426 Q_D(const QTextControl);
1427 return selectionRect(d->cursor);
1430 void QTextControlPrivate::mousePressEvent(QEvent *e, Qt::MouseButton button, const QPointF &pos, Qt::KeyboardModifiers modifiers,
1431 Qt::MouseButtons buttons, const QPoint &globalPos)
1435 if (sendMouseEventToInputContext(
1436 e, QEvent::MouseButtonPress, button, pos, modifiers, buttons, globalPos)) {
1440 if (interactionFlags & Qt::LinksAccessibleByMouse) {
1441 anchorOnMousePress = q->anchorAt(pos);
1443 if (cursorIsFocusIndicator) {
1444 cursorIsFocusIndicator = false;
1446 cursor.clearSelection();
1449 if (!(button & Qt::LeftButton) ||
1450 !((interactionFlags & Qt::TextSelectableByMouse) || (interactionFlags & Qt::TextEditable))) {
1455 cursorIsFocusIndicator = false;
1456 const QTextCursor oldSelection = cursor;
1457 const int oldCursorPos = cursor.position();
1459 mousePressed = (interactionFlags & Qt::TextSelectableByMouse);
1460 #ifndef QT_NO_DRAGANDDROP
1461 mightStartDrag = false;
1464 if (trippleClickTimer.isActive()
1465 && ((pos - trippleClickPoint).toPoint().manhattanLength() < qApp->styleHints()->startDragDistance())) {
1467 cursor.movePosition(QTextCursor::StartOfBlock);
1468 cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
1469 cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
1470 selectedBlockOnTrippleClick = cursor;
1472 anchorOnMousePress = QString();
1474 trippleClickTimer.stop();
1476 int cursorPos = q->hitTest(pos, Qt::FuzzyHit);
1477 if (cursorPos == -1) {
1482 if (modifiers == Qt::ShiftModifier && (interactionFlags & Qt::TextSelectableByMouse)) {
1483 if (wordSelectionEnabled && !selectedWordOnDoubleClick.hasSelection()) {
1484 selectedWordOnDoubleClick = cursor;
1485 selectedWordOnDoubleClick.select(QTextCursor::WordUnderCursor);
1488 if (selectedBlockOnTrippleClick.hasSelection())
1489 extendBlockwiseSelection(cursorPos);
1490 else if (selectedWordOnDoubleClick.hasSelection())
1491 extendWordwiseSelection(cursorPos, pos.x());
1492 else if (!wordSelectionEnabled)
1493 setCursorPosition(cursorPos, QTextCursor::KeepAnchor);
1497 && cursor.hasSelection()
1498 && !cursorIsFocusIndicator
1499 && cursorPos >= cursor.selectionStart()
1500 && cursorPos <= cursor.selectionEnd()
1501 && q->hitTest(pos, Qt::ExactHit) != -1) {
1502 #ifndef QT_NO_DRAGANDDROP
1503 mightStartDrag = true;
1504 dragStartPos = pos.toPoint();
1509 setCursorPosition(cursorPos);
1513 if (interactionFlags & Qt::TextEditable) {
1514 q->ensureCursorVisible();
1515 if (cursor.position() != oldCursorPos)
1516 emit q->cursorPositionChanged();
1517 _q_updateCurrentCharFormatAndSelection();
1519 if (cursor.position() != oldCursorPos) {
1520 emit q->cursorPositionChanged();
1521 emit q->microFocusChanged();
1525 repaintOldAndNewSelection(oldSelection);
1526 hadSelectionOnMousePress = cursor.hasSelection();
1529 void QTextControlPrivate::mouseMoveEvent(QEvent *e, Qt::MouseButton button, const QPointF &mousePos, Qt::KeyboardModifiers modifiers,
1530 Qt::MouseButtons buttons, const QPoint &globalPos)
1534 if (sendMouseEventToInputContext(
1535 e, QEvent::MouseMove, button, mousePos, modifiers, buttons, globalPos)) {
1539 if (interactionFlags & Qt::LinksAccessibleByMouse) {
1540 QString anchor = q->anchorAt(mousePos);
1541 if (anchor != highlightedAnchor) {
1542 highlightedAnchor = anchor;
1543 emit q->linkHovered(anchor);
1547 if (!(buttons & Qt::LeftButton))
1550 const bool editable = interactionFlags & Qt::TextEditable;
1555 || selectedWordOnDoubleClick.hasSelection()
1556 || selectedBlockOnTrippleClick.hasSelection()))
1559 const QTextCursor oldSelection = cursor;
1560 const int oldCursorPos = cursor.position();
1562 if (mightStartDrag) {
1563 if ((mousePos.toPoint() - dragStartPos).manhattanLength() > qApp->styleHints()->startDragDistance())
1571 const qreal mouseX = qreal(mousePos.x());
1573 int newCursorPos = q->hitTest(mousePos, Qt::FuzzyHit);
1574 if (newCursorPos == -1)
1577 if (wordSelectionEnabled && !selectedWordOnDoubleClick.hasSelection()) {
1578 selectedWordOnDoubleClick = cursor;
1579 selectedWordOnDoubleClick.select(QTextCursor::WordUnderCursor);
1582 if (selectedBlockOnTrippleClick.hasSelection())
1583 extendBlockwiseSelection(newCursorPos);
1584 else if (selectedWordOnDoubleClick.hasSelection())
1585 extendWordwiseSelection(newCursorPos, mouseX);
1587 setCursorPosition(newCursorPos, QTextCursor::KeepAnchor);
1589 if (interactionFlags & Qt::TextEditable) {
1590 // don't call ensureVisible for the visible cursor to avoid jumping
1591 // scrollbars. the autoscrolling ensures smooth scrolling if necessary.
1592 //q->ensureCursorVisible();
1593 if (cursor.position() != oldCursorPos)
1594 emit q->cursorPositionChanged();
1595 _q_updateCurrentCharFormatAndSelection();
1597 qGuiApp->inputPanel()->update(Qt::ImQueryInput);
1599 //emit q->visibilityRequest(QRectF(mousePos, QSizeF(1, 1)));
1600 if (cursor.position() != oldCursorPos) {
1601 emit q->cursorPositionChanged();
1602 emit q->microFocusChanged();
1605 selectionChanged(true);
1606 repaintOldAndNewSelection(oldSelection);
1609 void QTextControlPrivate::mouseReleaseEvent(QEvent *e, Qt::MouseButton button, const QPointF &pos, Qt::KeyboardModifiers modifiers,
1610 Qt::MouseButtons buttons, const QPoint &globalPos)
1614 if (sendMouseEventToInputContext(
1615 e, QEvent::MouseButtonRelease, button, pos, modifiers, buttons, globalPos)) {
1619 const QTextCursor oldSelection = cursor;
1620 const int oldCursorPos = cursor.position();
1622 #ifndef QT_NO_DRAGANDDROP
1623 if (mightStartDrag && (button & Qt::LeftButton)) {
1624 mousePressed = false;
1625 setCursorPosition(pos);
1626 cursor.clearSelection();
1631 mousePressed = false;
1632 #ifndef QT_NO_CLIPBOARD
1633 setClipboardSelection();
1634 selectionChanged(true);
1635 } else if (button == Qt::MidButton
1636 && (interactionFlags & Qt::TextEditable)
1637 && QGuiApplication::clipboard()->supportsSelection()) {
1638 setCursorPosition(pos);
1639 const QMimeData *md = QGuiApplication::clipboard()->mimeData(QClipboard::Selection);
1641 q->insertFromMimeData(md);
1645 repaintOldAndNewSelection(oldSelection);
1647 if (cursor.position() != oldCursorPos) {
1648 emit q->cursorPositionChanged();
1649 emit q->microFocusChanged();
1652 if (interactionFlags & Qt::LinksAccessibleByMouse) {
1653 if (!(button & Qt::LeftButton))
1656 const QString anchor = q->anchorAt(pos);
1658 if (anchor.isEmpty())
1661 if (!cursor.hasSelection()
1662 || (anchor == anchorOnMousePress && hadSelectionOnMousePress)) {
1664 const int anchorPos = q->hitTest(pos, Qt::ExactHit);
1665 if (anchorPos != -1) {
1666 cursor.setPosition(anchorPos);
1668 QString anchor = anchorOnMousePress;
1669 anchorOnMousePress = QString();
1670 activateLinkUnderCursor(anchor);
1676 void QTextControlPrivate::mouseDoubleClickEvent(QEvent *e, Qt::MouseButton button, const QPointF &pos, Qt::KeyboardModifiers modifiers,
1677 Qt::MouseButtons buttons, const QPoint &globalPos)
1681 if (sendMouseEventToInputContext(
1682 e, QEvent::MouseButtonDblClick, button, pos, modifiers, buttons, globalPos)) {
1686 if (button != Qt::LeftButton
1687 || !(interactionFlags & Qt::TextSelectableByMouse)) {
1692 #ifndef QT_NO_DRAGANDDROP
1693 mightStartDrag = false;
1695 const QTextCursor oldSelection = cursor;
1696 setCursorPosition(pos);
1697 QTextLine line = currentTextLine(cursor);
1698 bool doEmit = false;
1699 if (line.isValid() && line.textLength()) {
1700 cursor.select(QTextCursor::WordUnderCursor);
1703 repaintOldAndNewSelection(oldSelection);
1705 cursorIsFocusIndicator = false;
1706 selectedWordOnDoubleClick = cursor;
1708 trippleClickPoint = pos;
1709 trippleClickTimer.start(qApp->styleHints()->mouseDoubleClickInterval(), q);
1712 #ifndef QT_NO_CLIPBOARD
1713 setClipboardSelection();
1715 emit q->cursorPositionChanged();
1719 bool QTextControlPrivate::sendMouseEventToInputContext(
1720 QEvent *e, QEvent::Type eventType, Qt::MouseButton button, const QPointF &pos,
1721 Qt::KeyboardModifiers modifiers, Qt::MouseButtons buttons, const QPoint &globalPos)
1723 #if 0 // ### !defined(QT_NO_IM)
1726 QTextLayout *layout = cursor.block().layout();
1727 if (contextObject && layout && !layout->preeditAreaText().isEmpty()) {
1728 QInputContext *ctx = inputContext();
1729 int cursorPos = q->hitTest(pos, Qt::FuzzyHit) - cursor.position();
1731 if (cursorPos < 0 || cursorPos > layout->preeditAreaText().length()) {
1733 // don't send move events outside the preedit area
1734 if (eventType == QEvent::MouseMove)
1738 QMouseEvent ev(eventType, contextObject->mapFromGlobal(globalPos), globalPos,
1739 button, buttons, modifiers);
1740 ctx->mouseHandler(cursorPos, &ev);
1741 e->setAccepted(ev.isAccepted());
1743 if (!layout->preeditAreaText().isEmpty())
1748 Q_UNUSED(eventType);
1751 Q_UNUSED(modifiers);
1753 Q_UNUSED(globalPos);
1758 bool QTextControlPrivate::dragEnterEvent(QEvent *e, const QMimeData *mimeData)
1761 if (!(interactionFlags & Qt::TextEditable) || !q->canInsertFromMimeData(mimeData)) {
1766 dndFeedbackCursor = QTextCursor();
1768 return true; // accept proposed action
1771 void QTextControlPrivate::dragLeaveEvent()
1775 const QRectF crect = q->cursorRect(dndFeedbackCursor);
1776 dndFeedbackCursor = QTextCursor();
1778 if (crect.isValid())
1779 emit q->updateRequest(crect);
1782 bool QTextControlPrivate::dragMoveEvent(QEvent *e, const QMimeData *mimeData, const QPointF &pos)
1785 if (!(interactionFlags & Qt::TextEditable) || !q->canInsertFromMimeData(mimeData)) {
1790 const int cursorPos = q->hitTest(pos, Qt::FuzzyHit);
1791 if (cursorPos != -1) {
1792 QRectF crect = q->cursorRect(dndFeedbackCursor);
1793 if (crect.isValid())
1794 emit q->updateRequest(crect);
1796 dndFeedbackCursor = cursor;
1797 dndFeedbackCursor.setPosition(cursorPos);
1799 crect = q->cursorRect(dndFeedbackCursor);
1800 emit q->updateRequest(crect);
1803 return true; // accept proposed action
1806 bool QTextControlPrivate::dropEvent(const QMimeData *mimeData, const QPointF &pos, Qt::DropAction dropAction, QObject *source)
1809 dndFeedbackCursor = QTextCursor();
1811 if (!(interactionFlags & Qt::TextEditable) || !q->canInsertFromMimeData(mimeData))
1816 QTextCursor insertionCursor = q->cursorForPosition(pos);
1817 insertionCursor.beginEditBlock();
1819 if (dropAction == Qt::MoveAction && source == contextObject)
1820 cursor.removeSelectedText();
1822 cursor = insertionCursor;
1823 q->insertFromMimeData(mimeData);
1824 insertionCursor.endEditBlock();
1825 q->ensureCursorVisible();
1826 return true; // accept proposed action
1829 void QTextControlPrivate::inputMethodEvent(QInputMethodEvent *e)
1832 if (!(interactionFlags & Qt::TextEditable) || cursor.isNull()) {
1836 bool isGettingInput = !e->commitString().isEmpty()
1837 || e->preeditString() != cursor.block().layout()->preeditAreaText()
1838 || e->replacementLength() > 0;
1839 bool forceSelectionChanged = false;
1841 cursor.beginEditBlock();
1842 if (isGettingInput) {
1843 cursor.removeSelectedText();
1846 // insert commit string
1847 if (!e->commitString().isEmpty() || e->replacementLength()) {
1848 QTextCursor c = cursor;
1849 c.setPosition(c.position() + e->replacementStart());
1850 c.setPosition(c.position() + e->replacementLength(), QTextCursor::KeepAnchor);
1851 c.insertText(e->commitString());
1854 for (int i = 0; i < e->attributes().size(); ++i) {
1855 const QInputMethodEvent::Attribute &a = e->attributes().at(i);
1856 if (a.type == QInputMethodEvent::Selection) {
1857 QTextCursor oldCursor = cursor;
1858 int blockStart = a.start + cursor.block().position();
1859 cursor.setPosition(blockStart, QTextCursor::MoveAnchor);
1860 cursor.setPosition(blockStart + a.length, QTextCursor::KeepAnchor);
1861 q->ensureCursorVisible();
1862 repaintOldAndNewSelection(oldCursor);
1863 forceSelectionChanged = true;
1867 QTextBlock block = cursor.block();
1868 QTextLayout *layout = block.layout();
1870 layout->setPreeditArea(cursor.position() - block.position(), e->preeditString());
1871 QList<QTextLayout::FormatRange> overrides;
1872 const int oldPreeditCursor = preeditCursor;
1873 preeditCursor = e->preeditString().length();
1875 for (int i = 0; i < e->attributes().size(); ++i) {
1876 const QInputMethodEvent::Attribute &a = e->attributes().at(i);
1877 if (a.type == QInputMethodEvent::Cursor) {
1878 preeditCursor = a.start;
1879 hideCursor = !a.length;
1880 } else if (a.type == QInputMethodEvent::TextFormat) {
1881 QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat();
1883 QTextLayout::FormatRange o;
1884 o.start = a.start + cursor.position() - block.position();
1885 o.length = a.length;
1887 overrides.append(o);
1891 layout->setAdditionalFormats(overrides);
1892 cursor.endEditBlock();
1893 QTextCursorPrivate *cursor_d = QTextCursorPrivate::getPrivate(&cursor);
1896 if (oldPreeditCursor != preeditCursor)
1897 emit q->microFocusChanged();
1898 selectionChanged(forceSelectionChanged);
1901 QVariant QTextControl::inputMethodQuery(Qt::InputMethodQuery property) const
1903 Q_D(const QTextControl);
1904 QTextBlock block = d->cursor.block();
1906 case Qt::ImCursorRectangle:
1907 return cursorRect();
1909 return QVariant(d->cursor.charFormat().font());
1910 case Qt::ImCursorPosition:
1911 return QVariant(d->cursor.position() - block.position());
1912 case Qt::ImSurroundingText:
1913 return QVariant(block.text());
1914 case Qt::ImCurrentSelection:
1915 return QVariant(d->cursor.selectedText());
1916 case Qt::ImMaximumTextLength:
1917 return QVariant(); // No limit.
1918 case Qt::ImAnchorPosition:
1919 return QVariant(qBound(0, d->cursor.anchor() - block.position(), block.length()));
1925 void QTextControl::setFocus(bool focus, Qt::FocusReason reason)
1927 QFocusEvent ev(focus ? QEvent::FocusIn : QEvent::FocusOut,
1932 void QTextControlPrivate::focusEvent(QFocusEvent *e)
1935 emit q->updateRequest(q->selectionRect());
1936 if (e->gotFocus()) {
1937 #ifdef QT_KEYPAD_NAVIGATION
1938 if (!QGuiApplication::keypadNavigationEnabled() || (hasEditFocus && (e->reason() == Qt::PopupFocusReason
1940 || e->reason() == Qt::ActiveWindowFocusReason
1944 cursorOn = (interactionFlags & Qt::TextSelectableByKeyboard);
1945 if (interactionFlags & Qt::TextEditable) {
1946 setBlinkingCursorEnabled(true);
1948 #ifdef QT_KEYPAD_NAVIGATION
1952 setBlinkingCursorEnabled(false);
1954 if (cursorIsFocusIndicator
1955 && e->reason() != Qt::ActiveWindowFocusReason
1956 && e->reason() != Qt::PopupFocusReason
1957 && cursor.hasSelection()) {
1958 cursor.clearSelection();
1961 hasFocus = e->gotFocus();
1964 QString QTextControlPrivate::anchorForCursor(const QTextCursor &anchorCursor) const
1966 if (anchorCursor.hasSelection()) {
1967 QTextCursor cursor = anchorCursor;
1968 if (cursor.selectionStart() != cursor.position())
1969 cursor.setPosition(cursor.selectionStart());
1970 cursor.movePosition(QTextCursor::NextCharacter);
1971 QTextCharFormat fmt = cursor.charFormat();
1972 if (fmt.isAnchor() && fmt.hasProperty(QTextFormat::AnchorHref))
1973 return fmt.stringProperty(QTextFormat::AnchorHref);
1978 #ifdef QT_KEYPAD_NAVIGATION
1979 void QTextControlPrivate::editFocusEvent(QEvent *e)
1983 if (QGuiApplication::keypadNavigationEnabled()) {
1984 if (e->type() == QEvent::EnterEditFocus && interactionFlags & Qt::TextEditable) {
1985 const QTextCursor oldSelection = cursor;
1986 const int oldCursorPos = cursor.position();
1987 const bool moved = cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
1988 q->ensureCursorVisible();
1990 if (cursor.position() != oldCursorPos)
1991 emit q->cursorPositionChanged();
1992 emit q->microFocusChanged();
1995 repaintOldAndNewSelection(oldSelection);
1997 setBlinkingCursorEnabled(true);
1999 setBlinkingCursorEnabled(false);
2002 hasEditFocus = e->type() == QEvent::EnterEditFocus ? true : false;
2006 QTextCursor QTextControl::cursorForPosition(const QPointF &pos) const
2008 Q_D(const QTextControl);
2009 int cursorPos = hitTest(pos, Qt::FuzzyHit);
2010 if (cursorPos == -1)
2012 QTextCursor c(d->doc);
2013 c.setPosition(cursorPos);
2017 QRectF QTextControl::cursorRect(const QTextCursor &cursor) const
2019 Q_D(const QTextControl);
2020 if (cursor.isNull())
2023 return d->rectForPosition(cursor.position());
2026 QRectF QTextControl::cursorRect() const
2028 Q_D(const QTextControl);
2029 return cursorRect(d->cursor);
2032 QRectF QTextControlPrivate::cursorRectPlusUnicodeDirectionMarkers(const QTextCursor &cursor) const
2034 if (cursor.isNull())
2037 return rectForPosition(cursor.position()).adjusted(-4, 0, 4, 0);
2040 QString QTextControl::anchorAt(const QPointF &pos) const
2042 Q_D(const QTextControl);
2043 return d->doc->documentLayout()->anchorAt(pos);
2046 QString QTextControl::anchorAtCursor() const
2048 Q_D(const QTextControl);
2050 return d->anchorForCursor(d->cursor);
2053 bool QTextControl::overwriteMode() const
2055 Q_D(const QTextControl);
2056 return d->overwriteMode;
2059 void QTextControl::setOverwriteMode(bool overwrite)
2062 d->overwriteMode = overwrite;
2065 int QTextControl::cursorWidth() const
2067 #ifndef QT_NO_PROPERTIES
2068 Q_D(const QTextControl);
2069 return d->doc->documentLayout()->property("cursorWidth").toInt();
2075 void QTextControl::setCursorWidth(int width)
2078 #ifdef QT_NO_PROPERTIES
2082 width = textCursorWidth;
2083 d->doc->documentLayout()->setProperty("cursorWidth", width);
2088 bool QTextControl::acceptRichText() const
2090 Q_D(const QTextControl);
2091 return d->acceptRichText;
2094 void QTextControl::setAcceptRichText(bool accept)
2097 d->acceptRichText = accept;
2100 void QTextControl::setExtraSelections(const QVector<QAbstractTextDocumentLayout::Selection> &selections)
2104 QHash<int, int> hash;
2105 for (int i = 0; i < d->extraSelections.count(); ++i) {
2106 const QAbstractTextDocumentLayout::Selection &esel = d->extraSelections.at(i);
2107 hash.insertMulti(esel.cursor.anchor(), i);
2110 for (int i = 0; i < selections.count(); ++i) {
2111 const QAbstractTextDocumentLayout::Selection &sel = selections.at(i);
2112 QHash<int, int>::iterator it = hash.find(sel.cursor.anchor());
2113 if (it != hash.end()) {
2114 const QAbstractTextDocumentLayout::Selection &esel = d->extraSelections.at(it.value());
2115 if (esel.cursor.position() == sel.cursor.position()
2116 && esel.format == sel.format) {
2121 QRectF r = selectionRect(sel.cursor);
2122 if (sel.format.boolProperty(QTextFormat::FullWidthSelection)) {
2124 r.setWidth(qreal(INT_MAX));
2126 emit updateRequest(r);
2129 for (QHash<int, int>::iterator it = hash.begin(); it != hash.end(); ++it) {
2130 const QAbstractTextDocumentLayout::Selection &esel = d->extraSelections.at(it.value());
2131 QRectF r = selectionRect(esel.cursor);
2132 if (esel.format.boolProperty(QTextFormat::FullWidthSelection)) {
2134 r.setWidth(qreal(INT_MAX));
2136 emit updateRequest(r);
2139 d->extraSelections = selections;
2142 QVector<QAbstractTextDocumentLayout::Selection> QTextControl::extraSelections() const
2144 Q_D(const QTextControl);
2145 return d->extraSelections;
2148 void QTextControl::setTextWidth(qreal width)
2151 d->doc->setTextWidth(width);
2154 qreal QTextControl::textWidth() const
2156 Q_D(const QTextControl);
2157 return d->doc->textWidth();
2160 QSizeF QTextControl::size() const
2162 Q_D(const QTextControl);
2163 return d->doc->size();
2166 void QTextControl::setOpenExternalLinks(bool open)
2169 d->openExternalLinks = open;
2172 bool QTextControl::openExternalLinks() const
2174 Q_D(const QTextControl);
2175 return d->openExternalLinks;
2178 bool QTextControl::ignoreUnusedNavigationEvents() const
2180 Q_D(const QTextControl);
2181 return d->ignoreUnusedNavigationEvents;
2184 void QTextControl::setIgnoreUnusedNavigationEvents(bool ignore)
2187 d->ignoreUnusedNavigationEvents = ignore;
2190 void QTextControl::moveCursor(QTextCursor::MoveOperation op, QTextCursor::MoveMode mode)
2193 const QTextCursor oldSelection = d->cursor;
2194 const bool moved = d->cursor.movePosition(op, mode);
2195 d->_q_updateCurrentCharFormatAndSelection();
2196 ensureCursorVisible();
2197 d->repaintOldAndNewSelection(oldSelection);
2199 emit cursorPositionChanged();
2202 bool QTextControl::canPaste() const
2204 #ifndef QT_NO_CLIPBOARD
2205 Q_D(const QTextControl);
2206 if (d->interactionFlags & Qt::TextEditable) {
2207 const QMimeData *md = QGuiApplication::clipboard()->mimeData();
2208 return md && canInsertFromMimeData(md);
2214 void QTextControl::setCursorIsFocusIndicator(bool b)
2217 d->cursorIsFocusIndicator = b;
2221 bool QTextControl::cursorIsFocusIndicator() const
2223 Q_D(const QTextControl);
2224 return d->cursorIsFocusIndicator;
2228 void QTextControl::setDragEnabled(bool enabled)
2231 d->dragEnabled = enabled;
2234 bool QTextControl::isDragEnabled() const
2236 Q_D(const QTextControl);
2237 return d->dragEnabled;
2240 void QTextControl::setWordSelectionEnabled(bool enabled)
2243 d->wordSelectionEnabled = enabled;
2246 bool QTextControl::isWordSelectionEnabled() const
2248 Q_D(const QTextControl);
2249 return d->wordSelectionEnabled;
2252 void QTextControl::print(QPagedPaintDevice *printer) const
2254 Q_D(const QTextControl);
2257 QTextDocument *tempDoc = 0;
2258 const QTextDocument *doc = d->doc;
2259 if (QPagedPaintDevicePrivate::get(printer)->printSelectionOnly) {
2260 if (!d->cursor.hasSelection())
2262 tempDoc = new QTextDocument(const_cast<QTextDocument *>(doc));
2263 tempDoc->setMetaInformation(QTextDocument::DocumentTitle, doc->metaInformation(QTextDocument::DocumentTitle));
2264 tempDoc->setPageSize(doc->pageSize());
2265 tempDoc->setDefaultFont(doc->defaultFont());
2266 tempDoc->setUseDesignMetrics(doc->useDesignMetrics());
2267 QTextCursor(tempDoc).insertFragment(d->cursor.selection());
2270 // copy the custom object handlers
2271 doc->documentLayout()->d_func()->handlers = d->doc->documentLayout()->d_func()->handlers;
2273 doc->print(printer);
2277 QMimeData *QTextControl::createMimeDataFromSelection() const
2279 Q_D(const QTextControl);
2280 const QTextDocumentFragment fragment(d->cursor);
2281 return new QTextEditMimeData(fragment);
2284 bool QTextControl::canInsertFromMimeData(const QMimeData *source) const
2286 Q_D(const QTextControl);
2287 if (d->acceptRichText)
2288 return (source->hasText() && !source->text().isEmpty())
2289 || source->hasHtml()
2290 || source->hasFormat(QLatin1String("application/x-qrichtext"))
2291 || source->hasFormat(QLatin1String("application/x-qt-richtext"));
2293 return source->hasText() && !source->text().isEmpty();
2296 void QTextControl::insertFromMimeData(const QMimeData *source)
2299 if (!(d->interactionFlags & Qt::TextEditable) || !source)
2302 bool hasData = false;
2303 QTextDocumentFragment fragment;
2304 #ifndef QT_NO_TEXTHTMLPARSER
2305 if (source->hasFormat(QLatin1String("application/x-qrichtext")) && d->acceptRichText) {
2306 // x-qrichtext is always UTF-8 (taken from Qt3 since we don't use it anymore).
2307 QString richtext = QString::fromUtf8(source->data(QLatin1String("application/x-qrichtext")));
2308 richtext.prepend(QLatin1String("<meta name=\"qrichtext\" content=\"1\" />"));
2309 fragment = QTextDocumentFragment::fromHtml(richtext, d->doc);
2311 } else if (source->hasHtml() && d->acceptRichText) {
2312 fragment = QTextDocumentFragment::fromHtml(source->html(), d->doc);
2315 QString text = source->text();
2316 if (!text.isNull()) {
2317 fragment = QTextDocumentFragment::fromPlainText(text);
2322 fragment = QTextDocumentFragment::fromPlainText(source->text());
2323 #endif // QT_NO_TEXTHTMLPARSER
2326 d->cursor.insertFragment(fragment);
2327 ensureCursorVisible();
2330 bool QTextControl::findNextPrevAnchor(const QTextCursor &startCursor, bool next, QTextCursor &newAnchor)
2334 int anchorStart = -1;
2339 const int startPos = startCursor.selectionEnd();
2341 QTextBlock block = d->doc->findBlock(startPos);
2342 QTextBlock::Iterator it = block.begin();
2344 while (!it.atEnd() && it.fragment().position() < startPos)
2347 while (block.isValid()) {
2351 for (; !it.atEnd(); ++it) {
2352 const QTextFragment fragment = it.fragment();
2353 const QTextCharFormat fmt = fragment.charFormat();
2355 if (fmt.isAnchor() && fmt.hasProperty(QTextFormat::AnchorHref)) {
2356 anchorStart = fragment.position();
2357 anchorHref = fmt.anchorHref();
2362 if (anchorStart != -1) {
2365 // find next non-anchor fragment
2366 for (; !it.atEnd(); ++it) {
2367 const QTextFragment fragment = it.fragment();
2368 const QTextCharFormat fmt = fragment.charFormat();
2370 if (!fmt.isAnchor() || fmt.anchorHref() != anchorHref) {
2371 anchorEnd = fragment.position();
2376 if (anchorEnd == -1)
2377 anchorEnd = block.position() + block.length() - 1;
2379 // make found selection
2383 block = block.next();
2387 int startPos = startCursor.selectionStart();
2391 QTextBlock block = d->doc->findBlock(startPos);
2392 QTextBlock::Iterator blockStart = block.begin();
2393 QTextBlock::Iterator it = block.end();
2395 if (startPos == block.position()) {
2399 if (it == blockStart) {
2400 it = QTextBlock::Iterator();
2401 block = QTextBlock();
2405 } while (!it.atEnd() && it.fragment().position() + it.fragment().length() - 1 > startPos);
2408 while (block.isValid()) {
2413 const QTextFragment fragment = it.fragment();
2414 const QTextCharFormat fmt = fragment.charFormat();
2416 if (fmt.isAnchor() && fmt.hasProperty(QTextFormat::AnchorHref)) {
2417 anchorStart = fragment.position() + fragment.length();
2418 anchorHref = fmt.anchorHref();
2422 if (it == blockStart)
2423 it = QTextBlock::Iterator();
2426 } while (!it.atEnd());
2429 if (anchorStart != -1 && !it.atEnd()) {
2433 const QTextFragment fragment = it.fragment();
2434 const QTextCharFormat fmt = fragment.charFormat();
2436 if (!fmt.isAnchor() || fmt.anchorHref() != anchorHref) {
2437 anchorEnd = fragment.position() + fragment.length();
2441 if (it == blockStart)
2442 it = QTextBlock::Iterator();
2445 } while (!it.atEnd());
2447 if (anchorEnd == -1)
2448 anchorEnd = qMax(0, block.position());
2453 block = block.previous();
2455 if (it != block.begin())
2457 blockStart = block.begin();
2462 if (anchorStart != -1 && anchorEnd != -1) {
2463 newAnchor = d->cursor;
2464 newAnchor.setPosition(anchorStart);
2465 newAnchor.setPosition(anchorEnd, QTextCursor::KeepAnchor);
2472 void QTextControlPrivate::activateLinkUnderCursor(QString href)
2474 QTextCursor oldCursor = cursor;
2476 if (href.isEmpty()) {
2477 QTextCursor tmp = cursor;
2478 if (tmp.selectionStart() != tmp.position())
2479 tmp.setPosition(tmp.selectionStart());
2480 tmp.movePosition(QTextCursor::NextCharacter);
2481 href = tmp.charFormat().anchorHref();
2486 if (!cursor.hasSelection()) {
2487 QTextBlock block = cursor.block();
2488 const int cursorPos = cursor.position();
2490 QTextBlock::Iterator it = block.begin();
2491 QTextBlock::Iterator linkFragment;
2493 for (; !it.atEnd(); ++it) {
2494 QTextFragment fragment = it.fragment();
2495 const int fragmentPos = fragment.position();
2496 if (fragmentPos <= cursorPos &&
2497 fragmentPos + fragment.length() > cursorPos) {
2503 if (!linkFragment.atEnd()) {
2505 cursor.setPosition(it.fragment().position());
2506 if (it != block.begin()) {
2509 QTextFragment fragment = it.fragment();
2510 if (fragment.charFormat().anchorHref() != href)
2512 cursor.setPosition(fragment.position());
2513 } while (it != block.begin());
2516 for (it = linkFragment; !it.atEnd(); ++it) {
2517 QTextFragment fragment = it.fragment();
2518 if (fragment.charFormat().anchorHref() != href)
2520 cursor.setPosition(fragment.position() + fragment.length(), QTextCursor::KeepAnchor);
2526 cursorIsFocusIndicator = true;
2528 cursorIsFocusIndicator = false;
2529 cursor.clearSelection();
2531 repaintOldAndNewSelection(oldCursor);
2533 #if 0 // ###ndef QT_NO_DESKTOPSERVICES
2534 if (openExternalLinks)
2535 QDesktopServices::openUrl(href);
2538 emit q_func()->linkActivated(href);
2541 bool QTextControl::setFocusToNextOrPreviousAnchor(bool next)
2545 if (!(d->interactionFlags & Qt::LinksAccessibleByKeyboard))
2548 QRectF crect = selectionRect();
2549 emit updateRequest(crect);
2551 // If we don't have a current anchor, we start from the start/end
2552 if (!d->cursor.hasSelection()) {
2553 d->cursor = QTextCursor(d->doc);
2555 d->cursor.movePosition(QTextCursor::Start);
2557 d->cursor.movePosition(QTextCursor::End);
2560 QTextCursor newAnchor;
2561 if (findNextPrevAnchor(d->cursor, next, newAnchor)) {
2562 d->cursor = newAnchor;
2563 d->cursorIsFocusIndicator = true;
2565 d->cursor.clearSelection();
2568 if (d->cursor.hasSelection()) {
2569 crect = selectionRect();
2570 emit updateRequest(crect);
2571 emit visibilityRequest(crect);
2578 bool QTextControl::setFocusToAnchor(const QTextCursor &newCursor)
2582 if (!(d->interactionFlags & Qt::LinksAccessibleByKeyboard))
2585 // Verify that this is an anchor.
2586 const QString anchorHref = d->anchorForCursor(newCursor);
2587 if (anchorHref.isEmpty())
2591 QRectF crect = selectionRect();
2592 emit updateRequest(crect);
2594 d->cursor.setPosition(newCursor.selectionStart());
2595 d->cursor.setPosition(newCursor.selectionEnd(), QTextCursor::KeepAnchor);
2596 d->cursorIsFocusIndicator = true;
2598 crect = selectionRect();
2599 emit updateRequest(crect);
2600 emit visibilityRequest(crect);
2604 void QTextControl::setTextInteractionFlags(Qt::TextInteractionFlags flags)
2607 if (flags == d->interactionFlags)
2609 d->interactionFlags = flags;
2612 d->setBlinkingCursorEnabled(flags & Qt::TextEditable);
2615 Qt::TextInteractionFlags QTextControl::textInteractionFlags() const
2617 Q_D(const QTextControl);
2618 return d->interactionFlags;
2621 void QTextControl::mergeCurrentCharFormat(const QTextCharFormat &modifier)
2624 d->cursor.mergeCharFormat(modifier);
2625 d->updateCurrentCharFormat();
2628 void QTextControl::setCurrentCharFormat(const QTextCharFormat &format)
2631 d->cursor.setCharFormat(format);
2632 d->updateCurrentCharFormat();
2635 QTextCharFormat QTextControl::currentCharFormat() const
2637 Q_D(const QTextControl);
2638 return d->cursor.charFormat();
2641 void QTextControl::insertPlainText(const QString &text)
2644 d->cursor.insertText(text);
2647 #ifndef QT_NO_TEXTHTMLPARSER
2648 void QTextControl::insertHtml(const QString &text)
2651 d->cursor.insertHtml(text);
2653 #endif // QT_NO_TEXTHTMLPARSER
2655 QPointF QTextControl::anchorPosition(const QString &name) const
2657 Q_D(const QTextControl);
2662 for (QTextBlock block = d->doc->begin(); block.isValid(); block = block.next()) {
2663 QTextCharFormat format = block.charFormat();
2664 if (format.isAnchor() && format.anchorNames().contains(name)) {
2665 r = d->rectForPosition(block.position());
2669 for (QTextBlock::Iterator it = block.begin(); !it.atEnd(); ++it) {
2670 QTextFragment fragment = it.fragment();
2671 format = fragment.charFormat();
2672 if (format.isAnchor() && format.anchorNames().contains(name)) {
2673 r = d->rectForPosition(fragment.position());
2674 block = QTextBlock();
2681 return QPointF(0, r.top());
2684 void QTextControl::adjustSize()
2687 d->doc->adjustSize();
2690 bool QTextControl::find(const QString &exp, QTextDocument::FindFlags options)
2693 QTextCursor search = d->doc->find(exp, d->cursor, options);
2694 if (search.isNull())
2697 setTextCursor(search);
2703 void QTextControlPrivate::append(const QString &text, Qt::TextFormat format)
2705 QTextCursor tmp(doc);
2706 tmp.beginEditBlock();
2707 tmp.movePosition(QTextCursor::End);
2709 if (!doc->isEmpty())
2710 tmp.insertBlock(cursor.blockFormat(), cursor.charFormat());
2712 tmp.setCharFormat(cursor.charFormat());
2714 // preserve the char format
2715 QTextCharFormat oldCharFormat = cursor.charFormat();
2717 #ifndef QT_NO_TEXTHTMLPARSER
2718 if (format == Qt::RichText || (format == Qt::AutoText && Qt::mightBeRichText(text))) {
2719 tmp.insertHtml(text);
2721 tmp.insertText(text);
2724 tmp.insertText(text);
2725 #endif // QT_NO_TEXTHTMLPARSER
2726 if (!cursor.hasSelection())
2727 cursor.setCharFormat(oldCharFormat);
2732 void QTextControl::append(const QString &text)
2735 d->append(text, Qt::AutoText);
2738 void QTextControl::appendHtml(const QString &html)
2741 d->append(html, Qt::RichText);
2744 void QTextControl::appendPlainText(const QString &text)
2747 d->append(text, Qt::PlainText);
2751 void QTextControl::ensureCursorVisible()
2754 QRectF crect = d->rectForPosition(d->cursor.position()).adjusted(-5, 0, 5, 0);
2755 emit visibilityRequest(crect);
2756 emit microFocusChanged();
2759 QPalette QTextControl::palette() const
2761 Q_D(const QTextControl);
2765 void QTextControl::setPalette(const QPalette &pal)
2771 bool QTextControl::cursorOn() const
2773 Q_D(const QTextControl);
2777 QAbstractTextDocumentLayout::PaintContext QTextControl::getPaintContext() const
2779 Q_D(const QTextControl);
2781 QAbstractTextDocumentLayout::PaintContext ctx;
2783 ctx.selections = d->extraSelections;
2784 ctx.palette = d->palette;
2785 if (d->cursorOn && d->isEnabled) {
2787 ctx.cursorPosition = -1;
2788 else if (d->preeditCursor != 0)
2789 ctx.cursorPosition = - (d->preeditCursor + 2);
2791 ctx.cursorPosition = d->cursor.position();
2794 if (!d->dndFeedbackCursor.isNull())
2795 ctx.cursorPosition = d->dndFeedbackCursor.position();
2796 #ifdef QT_KEYPAD_NAVIGATION
2797 if (!QGuiApplication::keypadNavigationEnabled() || d->hasEditFocus)
2799 if (d->cursor.hasSelection()) {
2800 QAbstractTextDocumentLayout::Selection selection;
2801 selection.cursor = d->cursor;
2802 if (0 && d->cursorIsFocusIndicator) {
2806 opt.palette = ctx.palette;
2807 QStyleHintReturnVariant ret;
2808 QStyle *style = QGuiApplication::style();
2810 style = widget->style();
2811 style->styleHint(QStyle::SH_TextControl_FocusIndicatorTextCharFormat, &opt, widget, &ret);
2812 selection.format = qvariant_cast<QTextFormat>(ret.variant).toCharFormat();
2815 QPalette::ColorGroup cg = d->hasFocus ? QPalette::Active : QPalette::Inactive;
2816 selection.format.setBackground(ctx.palette.brush(cg, QPalette::Highlight));
2817 selection.format.setForeground(ctx.palette.brush(cg, QPalette::HighlightedText));
2818 if (fullWidthSelection)
2819 selection.format.setProperty(QTextFormat::FullWidthSelection, true);
2821 ctx.selections.append(selection);
2827 void QTextControl::drawContents(QPainter *p, const QRectF &rect)
2831 QAbstractTextDocumentLayout::PaintContext ctx = getPaintContext();
2833 p->setClipRect(rect, Qt::IntersectClip);
2836 d->doc->documentLayout()->draw(p, ctx);
2840 void QTextControlPrivate::_q_copyLink()
2842 #ifndef QT_NO_CLIPBOARD
2843 QMimeData *md = new QMimeData;
2844 md->setText(linkToCopy);
2845 QGuiApplication::clipboard()->setMimeData(md);
2849 QInputContext *QTextControlPrivate::inputContext()
2853 QInputContext *ctx = contextObject->inputContext();
2854 if (!ctx && contextObject->parentWidget())
2855 ctx = contextObject->parentWidget()->inputContext();
2862 int QTextControl::hitTest(const QPointF &point, Qt::HitTestAccuracy accuracy) const
2864 Q_D(const QTextControl);
2865 return d->doc->documentLayout()->hitTest(point, accuracy);
2868 QRectF QTextControl::blockBoundingRect(const QTextBlock &block) const
2870 Q_D(const QTextControl);
2871 return d->doc->documentLayout()->blockBoundingRect(block);
2876 QStringList QTextEditMimeData::formats() const
2878 if (!fragment.isEmpty())
2879 return QStringList() << QString::fromLatin1("text/plain") << QString::fromLatin1("text/html")
2880 #ifndef QT_NO_TEXTODFWRITER
2881 << QString::fromLatin1("application/vnd.oasis.opendocument.text")
2885 return QMimeData::formats();
2888 QVariant QTextEditMimeData::retrieveData(const QString &mimeType, QVariant::Type type) const
2890 if (!fragment.isEmpty())
2892 return QMimeData::retrieveData(mimeType, type);
2895 void QTextEditMimeData::setup() const
2897 QTextEditMimeData *that = const_cast<QTextEditMimeData *>(this);
2898 #ifndef QT_NO_TEXTHTMLPARSER
2899 that->setData(QLatin1String("text/html"), fragment.toHtml("utf-8").toUtf8());
2901 #ifndef QT_NO_TEXTODFWRITER
2904 QTextDocumentWriter writer(&buffer, "ODF");
2905 writer.write(fragment);
2907 that->setData(QLatin1String("application/vnd.oasis.opendocument.text"), buffer.data());
2910 that->setText(fragment.toPlainText());
2911 fragment = QTextDocumentFragment();
2917 #include "moc_qtextcontrol_p.cpp"
2919 #endif // QT_NO_TEXTCONTROL