QQuickTextControl - mouse events to override input context
[profile/ivi/qtdeclarative.git] / src / quick / items / qquicktextcontrol.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtGui module of the Qt Toolkit.
8 **
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.
17 **
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.
21 **
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.
29 **
30 ** Other Usage
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.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qquicktextcontrol_p.h"
43 #include "qquicktextcontrol_p_p.h"
44
45 #ifndef QT_NO_TEXTCONTROL
46
47 #include <qfont.h>
48 #include <qpainter.h>
49 #include <qevent.h>
50 #include <qdebug.h>
51 #include <qdrag.h>
52 #include <qclipboard.h>
53 #include <qtimer.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"
64
65 #include <qtextformat.h>
66 #include <qdatetime.h>
67 #include <qbuffer.h>
68 #include <qguiapplication.h>
69 #include <limits.h>
70 #include <qtexttable.h>
71 #include <qvariant.h>
72 #include <qurl.h>
73 #include <qstylehints.h>
74
75 // ### these should come from QStyleHints
76 const int textCursorWidth = 1;
77 const bool fullWidthSelection = true;
78
79 QT_BEGIN_NAMESPACE
80
81 #ifndef QT_NO_CONTEXTMENU
82 #endif
83
84 // could go into QTextCursor...
85 static QTextLine currentTextLine(const QTextCursor &cursor)
86 {
87     const QTextBlock block = cursor.block();
88     if (!block.isValid())
89         return QTextLine();
90
91     const QTextLayout *layout = block.layout();
92     if (!layout)
93         return QTextLine();
94
95     const int relativePos = cursor.position() - block.position();
96     return layout->lineForTextPosition(relativePos);
97 }
98
99 QQuickTextControlPrivate::QQuickTextControlPrivate()
100     : doc(0), cursorOn(false), cursorIsFocusIndicator(false),
101       interactionFlags(Qt::TextEditorInteraction),
102       dragEnabled(true),
103 #ifndef QT_NO_DRAGANDDROP
104       mousePressed(false), mightStartDrag(false),
105 #endif
106       lastSelectionState(false), ignoreAutomaticScrollbarAdjustement(false),
107       overwriteMode(false),
108       acceptRichText(true),
109       preeditCursor(0), hideCursor(false),
110       hasFocus(false),
111 #ifdef QT_KEYPAD_NAVIGATION
112       hasEditFocus(false),
113 #endif
114       isEnabled(true),
115       hadSelectionOnMousePress(false),
116       ignoreUnusedNavigationEvents(false),
117       openExternalLinks(false),
118       wordSelectionEnabled(false)
119 {}
120
121 bool QQuickTextControlPrivate::cursorMoveKeyEvent(QKeyEvent *e)
122 {
123 #ifdef QT_NO_SHORTCUT
124     Q_UNUSED(e);
125 #endif
126
127     Q_Q(QQuickTextControl);
128     if (cursor.isNull())
129         return false;
130
131     const QTextCursor oldSelection = cursor;
132     const int oldCursorPos = cursor.position();
133
134     QTextCursor::MoveMode mode = QTextCursor::MoveAnchor;
135     QTextCursor::MoveOperation op = QTextCursor::NoMove;
136
137     if (false) {
138     }
139 #ifndef QT_NO_SHORTCUT
140     if (e == QKeySequence::MoveToNextChar) {
141             op = QTextCursor::Right;
142     }
143     else if (e == QKeySequence::MoveToPreviousChar) {
144             op = QTextCursor::Left;
145     }
146     else if (e == QKeySequence::SelectNextChar) {
147            op = QTextCursor::Right;
148            mode = QTextCursor::KeepAnchor;
149     }
150     else if (e == QKeySequence::SelectPreviousChar) {
151             op = QTextCursor::Left;
152             mode = QTextCursor::KeepAnchor;
153     }
154     else if (e == QKeySequence::SelectNextWord) {
155             op = QTextCursor::WordRight;
156             mode = QTextCursor::KeepAnchor;
157     }
158     else if (e == QKeySequence::SelectPreviousWord) {
159             op = QTextCursor::WordLeft;
160             mode = QTextCursor::KeepAnchor;
161     }
162     else if (e == QKeySequence::SelectStartOfLine) {
163             op = QTextCursor::StartOfLine;
164             mode = QTextCursor::KeepAnchor;
165     }
166     else if (e == QKeySequence::SelectEndOfLine) {
167             op = QTextCursor::EndOfLine;
168             mode = QTextCursor::KeepAnchor;
169     }
170     else if (e == QKeySequence::SelectStartOfBlock) {
171             op = QTextCursor::StartOfBlock;
172             mode = QTextCursor::KeepAnchor;
173     }
174     else if (e == QKeySequence::SelectEndOfBlock) {
175             op = QTextCursor::EndOfBlock;
176             mode = QTextCursor::KeepAnchor;
177     }
178     else if (e == QKeySequence::SelectStartOfDocument) {
179             op = QTextCursor::Start;
180             mode = QTextCursor::KeepAnchor;
181     }
182     else if (e == QKeySequence::SelectEndOfDocument) {
183             op = QTextCursor::End;
184             mode = QTextCursor::KeepAnchor;
185     }
186     else if (e == QKeySequence::SelectPreviousLine) {
187             op = QTextCursor::Up;
188             mode = QTextCursor::KeepAnchor;
189     }
190     else if (e == QKeySequence::SelectNextLine) {
191             op = QTextCursor::Down;
192             mode = QTextCursor::KeepAnchor;
193             {
194                 QTextBlock block = cursor.block();
195                 QTextLine line = currentTextLine(cursor);
196                 if (!block.next().isValid()
197                     && line.isValid()
198                     && line.lineNumber() == block.layout()->lineCount() - 1)
199                     op = QTextCursor::End;
200             }
201     }
202     else if (e == QKeySequence::MoveToNextWord) {
203             op = QTextCursor::WordRight;
204     }
205     else if (e == QKeySequence::MoveToPreviousWord) {
206             op = QTextCursor::WordLeft;
207     }
208     else if (e == QKeySequence::MoveToEndOfBlock) {
209             op = QTextCursor::EndOfBlock;
210     }
211     else if (e == QKeySequence::MoveToStartOfBlock) {
212             op = QTextCursor::StartOfBlock;
213     }
214     else if (e == QKeySequence::MoveToNextLine) {
215             op = QTextCursor::Down;
216     }
217     else if (e == QKeySequence::MoveToPreviousLine) {
218             op = QTextCursor::Up;
219     }
220     else if (e == QKeySequence::MoveToStartOfLine) {
221             op = QTextCursor::StartOfLine;
222     }
223     else if (e == QKeySequence::MoveToEndOfLine) {
224             op = QTextCursor::EndOfLine;
225     }
226     else if (e == QKeySequence::MoveToStartOfDocument) {
227             op = QTextCursor::Start;
228     }
229     else if (e == QKeySequence::MoveToEndOfDocument) {
230             op = QTextCursor::End;
231     }
232 #endif // QT_NO_SHORTCUT
233     else {
234         return false;
235     }
236
237 // Except for pageup and pagedown, Mac OS X has very different behavior, we don't do it all, but
238 // here's the breakdown:
239 // Shift still works as an anchor, but only one of the other keys can be down Ctrl (Command),
240 // Alt (Option), or Meta (Control).
241 // Command/Control + Left/Right -- Move to left or right of the line
242 //                 + Up/Down -- Move to top bottom of the file. (Control doesn't move the cursor)
243 // Option + Left/Right -- Move one word Left/right.
244 //        + Up/Down  -- Begin/End of Paragraph.
245 // Home/End Top/Bottom of file. (usually don't move the cursor, but will select)
246
247     bool visualNavigation = cursor.visualNavigation();
248     cursor.setVisualNavigation(true);
249     const bool moved = cursor.movePosition(op, mode);
250     cursor.setVisualNavigation(visualNavigation);
251     q->ensureCursorVisible();
252
253     bool ignoreNavigationEvents = ignoreUnusedNavigationEvents;
254     bool isNavigationEvent = e->key() == Qt::Key_Up || e->key() == Qt::Key_Down;
255
256 #ifdef QT_KEYPAD_NAVIGATION
257     ignoreNavigationEvents = ignoreNavigationEvents || QGuiApplication::keypadNavigationEnabled();
258     isNavigationEvent = isNavigationEvent ||
259                         (QGuiApplication::navigationMode() == Qt::NavigationModeKeypadDirectional
260                          && (e->key() == Qt::Key_Left || e->key() == Qt::Key_Right));
261 #else
262     isNavigationEvent = isNavigationEvent || e->key() == Qt::Key_Left || e->key() == Qt::Key_Right;
263 #endif
264
265     if (moved) {
266         if (cursor.position() != oldCursorPos)
267             emit q->cursorPositionChanged();
268         emit q->microFocusChanged();
269     } else if (ignoreNavigationEvents && isNavigationEvent && oldSelection.anchor() == cursor.anchor()) {
270         return false;
271     }
272
273     selectionChanged(/*forceEmitSelectionChanged =*/(mode == QTextCursor::KeepAnchor));
274
275     repaintOldAndNewSelection(oldSelection);
276
277     return true;
278 }
279
280 void QQuickTextControlPrivate::updateCurrentCharFormat()
281 {
282     Q_Q(QQuickTextControl);
283
284     QTextCharFormat fmt = cursor.charFormat();
285     if (fmt == lastCharFormat)
286         return;
287     lastCharFormat = fmt;
288
289     emit q->currentCharFormatChanged(fmt);
290     emit q->microFocusChanged();
291 }
292
293 void QQuickTextControlPrivate::indent()
294 {
295     QTextBlockFormat blockFmt = cursor.blockFormat();
296
297     QTextList *list = cursor.currentList();
298     if (!list) {
299         QTextBlockFormat modifier;
300         modifier.setIndent(blockFmt.indent() + 1);
301         cursor.mergeBlockFormat(modifier);
302     } else {
303         QTextListFormat format = list->format();
304         format.setIndent(format.indent() + 1);
305
306         if (list->itemNumber(cursor.block()) == 1)
307             list->setFormat(format);
308         else
309             cursor.createList(format);
310     }
311 }
312
313 void QQuickTextControlPrivate::outdent()
314 {
315     QTextBlockFormat blockFmt = cursor.blockFormat();
316
317     QTextList *list = cursor.currentList();
318
319     if (!list) {
320         QTextBlockFormat modifier;
321         modifier.setIndent(blockFmt.indent() - 1);
322         cursor.mergeBlockFormat(modifier);
323     } else {
324         QTextListFormat listFmt = list->format();
325         listFmt.setIndent(listFmt.indent() - 1);
326         list->setFormat(listFmt);
327     }
328 }
329
330 void QQuickTextControlPrivate::gotoNextTableCell()
331 {
332     QTextTable *table = cursor.currentTable();
333     QTextTableCell cell = table->cellAt(cursor);
334
335     int newColumn = cell.column() + cell.columnSpan();
336     int newRow = cell.row();
337
338     if (newColumn >= table->columns()) {
339         newColumn = 0;
340         ++newRow;
341         if (newRow >= table->rows())
342             table->insertRows(table->rows(), 1);
343     }
344
345     cell = table->cellAt(newRow, newColumn);
346     cursor = cell.firstCursorPosition();
347 }
348
349 void QQuickTextControlPrivate::gotoPreviousTableCell()
350 {
351     QTextTable *table = cursor.currentTable();
352     QTextTableCell cell = table->cellAt(cursor);
353
354     int newColumn = cell.column() - 1;
355     int newRow = cell.row();
356
357     if (newColumn < 0) {
358         newColumn = table->columns() - 1;
359         --newRow;
360         if (newRow < 0)
361             return;
362     }
363
364     cell = table->cellAt(newRow, newColumn);
365     cursor = cell.firstCursorPosition();
366 }
367
368 void QQuickTextControlPrivate::createAutoBulletList()
369 {
370     cursor.beginEditBlock();
371
372     QTextBlockFormat blockFmt = cursor.blockFormat();
373
374     QTextListFormat listFmt;
375     listFmt.setStyle(QTextListFormat::ListDisc);
376     listFmt.setIndent(blockFmt.indent() + 1);
377
378     blockFmt.setIndent(0);
379     cursor.setBlockFormat(blockFmt);
380
381     cursor.createList(listFmt);
382
383     cursor.endEditBlock();
384 }
385
386 void QQuickTextControlPrivate::init(Qt::TextFormat format, const QString &text, QTextDocument *document)
387 {
388     Q_Q(QQuickTextControl);
389     setContent(format, text, document);
390
391     doc->setUndoRedoEnabled(interactionFlags & Qt::TextEditable);
392     q->setCursorWidth(-1);
393
394     QObject::connect(q, SIGNAL(updateCursorRequest(QRectF)), q, SIGNAL(updateRequest(QRectF)));
395 }
396
397 void QQuickTextControlPrivate::setContent(Qt::TextFormat format, const QString &text, QTextDocument *document)
398 {
399     Q_Q(QQuickTextControl);
400
401     // for use when called from setPlainText. we may want to re-use the currently
402     // set char format then.
403     const QTextCharFormat charFormatForInsertion = cursor.charFormat();
404
405     bool clearDocument = true;
406     if (!doc) {
407         if (document) {
408             doc = document;
409             clearDocument = false;
410         } else {
411             palette = QGuiApplication::palette();
412             doc = new QTextDocument(q);
413         }
414         _q_documentLayoutChanged();
415         cursor = QTextCursor(doc);
416
417 // ####        doc->documentLayout()->setPaintDevice(viewport);
418
419         QObject::connect(doc, SIGNAL(contentsChanged()), q, SLOT(_q_updateCurrentCharFormatAndSelection()));
420         QObject::connect(doc, SIGNAL(cursorPositionChanged(QTextCursor)), q, SLOT(_q_emitCursorPosChanged(QTextCursor)));
421         QObject::connect(doc, SIGNAL(documentLayoutChanged()), q, SLOT(_q_documentLayoutChanged()));
422
423         // convenience signal forwards
424         QObject::connect(doc, SIGNAL(undoAvailable(bool)), q, SIGNAL(undoAvailable(bool)));
425         QObject::connect(doc, SIGNAL(redoAvailable(bool)), q, SIGNAL(redoAvailable(bool)));
426         QObject::connect(doc, SIGNAL(modificationChanged(bool)), q, SIGNAL(modificationChanged(bool)));
427         QObject::connect(doc, SIGNAL(blockCountChanged(int)), q, SIGNAL(blockCountChanged(int)));
428     }
429
430     bool previousUndoRedoState = doc->isUndoRedoEnabled();
431     if (!document)
432         doc->setUndoRedoEnabled(false);
433
434     //Saving the index save some time.
435     static int contentsChangedIndex = QTextDocument::staticMetaObject.indexOfSignal("contentsChanged()");
436     static int textChangedIndex = QQuickTextControl::staticMetaObject.indexOfSignal("textChanged()");
437     // avoid multiple textChanged() signals being emitted
438     QMetaObject::disconnect(doc, contentsChangedIndex, q, textChangedIndex);
439
440     if (!text.isEmpty()) {
441         // clear 'our' cursor for insertion to prevent
442         // the emission of the cursorPositionChanged() signal.
443         // instead we emit it only once at the end instead of
444         // at the end of the document after loading and when
445         // positioning the cursor again to the start of the
446         // document.
447         cursor = QTextCursor();
448         if (format == Qt::PlainText) {
449             QTextCursor formatCursor(doc);
450             // put the setPlainText and the setCharFormat into one edit block,
451             // so that the syntax highlight triggers only /once/ for the entire
452             // document, not twice.
453             formatCursor.beginEditBlock();
454             doc->setPlainText(text);
455             doc->setUndoRedoEnabled(false);
456             formatCursor.select(QTextCursor::Document);
457             formatCursor.setCharFormat(charFormatForInsertion);
458             formatCursor.endEditBlock();
459         } else {
460 #ifndef QT_NO_TEXTHTMLPARSER
461             doc->setHtml(text);
462 #else
463             doc->setPlainText(text);
464 #endif
465             doc->setUndoRedoEnabled(false);
466         }
467         cursor = QTextCursor(doc);
468     } else if (clearDocument) {
469         doc->clear();
470     }
471     cursor.setCharFormat(charFormatForInsertion);
472
473     QMetaObject::connect(doc, contentsChangedIndex, q, textChangedIndex);
474     emit q->textChanged();
475     if (!document)
476         doc->setUndoRedoEnabled(previousUndoRedoState);
477     _q_updateCurrentCharFormatAndSelection();
478     if (!document)
479         doc->setModified(false);
480
481     q->ensureCursorVisible();
482     emit q->cursorPositionChanged();
483 }
484
485 void QQuickTextControlPrivate::startDrag()
486 {
487 #ifndef QT_NO_DRAGANDDROP
488     Q_Q(QQuickTextControl);
489     mousePressed = false;
490     if (!contextObject)
491         return;
492     QMimeData *data = q->createMimeDataFromSelection();
493
494     QDrag *drag = new QDrag(contextObject);
495     drag->setMimeData(data);
496
497     Qt::DropActions actions = Qt::CopyAction;
498     Qt::DropAction action;
499     if (interactionFlags & Qt::TextEditable) {
500         actions |= Qt::MoveAction;
501         action = drag->exec(actions, Qt::MoveAction);
502     } else {
503         action = drag->exec(actions, Qt::CopyAction);
504     }
505
506     if (action == Qt::MoveAction && drag->target() != contextObject)
507         cursor.removeSelectedText();
508 #endif
509 }
510
511 void QQuickTextControlPrivate::setCursorPosition(const QPointF &pos)
512 {
513     Q_Q(QQuickTextControl);
514     const int cursorPos = q->hitTest(pos, Qt::FuzzyHit);
515     if (cursorPos == -1)
516         return;
517     cursor.setPosition(cursorPos);
518 }
519
520 void QQuickTextControlPrivate::setCursorPosition(int pos, QTextCursor::MoveMode mode)
521 {
522     cursor.setPosition(pos, mode);
523
524     if (mode != QTextCursor::KeepAnchor) {
525         selectedWordOnDoubleClick = QTextCursor();
526         selectedBlockOnTrippleClick = QTextCursor();
527     }
528 }
529
530 void QQuickTextControlPrivate::repaintCursor()
531 {
532     Q_Q(QQuickTextControl);
533     emit q->updateCursorRequest(cursorRectPlusUnicodeDirectionMarkers(cursor));
534 }
535
536 void QQuickTextControlPrivate::repaintOldAndNewSelection(const QTextCursor &oldSelection)
537 {
538     Q_Q(QQuickTextControl);
539     if (cursor.hasSelection()
540         && oldSelection.hasSelection()
541         && cursor.currentFrame() == oldSelection.currentFrame()
542         && !cursor.hasComplexSelection()
543         && !oldSelection.hasComplexSelection()
544         && cursor.anchor() == oldSelection.anchor()
545         ) {
546         QTextCursor differenceSelection(doc);
547         differenceSelection.setPosition(oldSelection.position());
548         differenceSelection.setPosition(cursor.position(), QTextCursor::KeepAnchor);
549         emit q->updateRequest(q->selectionRect(differenceSelection));
550     } else {
551         if (!oldSelection.hasSelection() && !cursor.hasSelection()) {
552             if (!oldSelection.isNull())
553                 emit q->updateCursorRequest(q->selectionRect(oldSelection) | cursorRectPlusUnicodeDirectionMarkers(oldSelection));
554             emit q->updateCursorRequest(q->selectionRect() | cursorRectPlusUnicodeDirectionMarkers(cursor));
555
556         } else {
557             if (!oldSelection.isNull())
558                 emit q->updateRequest(q->selectionRect(oldSelection) | cursorRectPlusUnicodeDirectionMarkers(oldSelection));
559             emit q->updateRequest(q->selectionRect() | cursorRectPlusUnicodeDirectionMarkers(cursor));
560         }
561     }
562 }
563
564 void QQuickTextControlPrivate::selectionChanged(bool forceEmitSelectionChanged /*=false*/)
565 {
566     Q_Q(QQuickTextControl);
567     if (forceEmitSelectionChanged)
568         emit q->selectionChanged();
569
570     bool current = cursor.hasSelection();
571     if (current == lastSelectionState)
572         return;
573
574     lastSelectionState = current;
575     emit q->copyAvailable(current);
576     if (!forceEmitSelectionChanged)
577         emit q->selectionChanged();
578     emit q->microFocusChanged();
579 }
580
581 void QQuickTextControlPrivate::_q_updateCurrentCharFormatAndSelection()
582 {
583     updateCurrentCharFormat();
584     selectionChanged();
585 }
586
587 #ifndef QT_NO_CLIPBOARD
588 void QQuickTextControlPrivate::setClipboardSelection()
589 {
590     QClipboard *clipboard = QGuiApplication::clipboard();
591     if (!cursor.hasSelection() || !clipboard->supportsSelection())
592         return;
593     Q_Q(QQuickTextControl);
594     QMimeData *data = q->createMimeDataFromSelection();
595     clipboard->setMimeData(data, QClipboard::Selection);
596 }
597 #endif
598
599 void QQuickTextControlPrivate::_q_emitCursorPosChanged(const QTextCursor &someCursor)
600 {
601     Q_Q(QQuickTextControl);
602     if (someCursor.isCopyOf(cursor)) {
603         emit q->cursorPositionChanged();
604         emit q->microFocusChanged();
605     }
606 }
607
608 void QQuickTextControlPrivate::_q_documentLayoutChanged()
609 {
610     Q_Q(QQuickTextControl);
611     QAbstractTextDocumentLayout *layout = doc->documentLayout();
612     QObject::connect(layout, SIGNAL(update(QRectF)), q, SIGNAL(updateRequest(QRectF)));
613     QObject::connect(layout, SIGNAL(updateBlock(QTextBlock)), q, SLOT(_q_updateBlock(QTextBlock)));
614     QObject::connect(layout, SIGNAL(documentSizeChanged(QSizeF)), q, SIGNAL(documentSizeChanged(QSizeF)));
615
616 }
617
618 void QQuickTextControlPrivate::setBlinkingCursorEnabled(bool enable)
619 {
620     Q_Q(QQuickTextControl);
621
622     if (enable && qApp->styleHints()->cursorFlashTime() > 0)
623         cursorBlinkTimer.start(qApp->styleHints()->cursorFlashTime() / 2, q);
624     else
625         cursorBlinkTimer.stop();
626
627     cursorOn = enable;
628
629     repaintCursor();
630 }
631
632 void QQuickTextControlPrivate::extendWordwiseSelection(int suggestedNewPosition, qreal mouseXPosition)
633 {
634     Q_Q(QQuickTextControl);
635
636     // if inside the initial selected word keep that
637     if (suggestedNewPosition >= selectedWordOnDoubleClick.selectionStart()
638         && suggestedNewPosition <= selectedWordOnDoubleClick.selectionEnd()) {
639         q->setTextCursor(selectedWordOnDoubleClick);
640         return;
641     }
642
643     QTextCursor curs = selectedWordOnDoubleClick;
644     curs.setPosition(suggestedNewPosition, QTextCursor::KeepAnchor);
645
646     if (!curs.movePosition(QTextCursor::StartOfWord))
647         return;
648     const int wordStartPos = curs.position();
649
650     const int blockPos = curs.block().position();
651     const QPointF blockCoordinates = q->blockBoundingRect(curs.block()).topLeft();
652
653     QTextLine line = currentTextLine(curs);
654     if (!line.isValid())
655         return;
656
657     const qreal wordStartX = line.cursorToX(curs.position() - blockPos) + blockCoordinates.x();
658
659     if (!curs.movePosition(QTextCursor::EndOfWord))
660         return;
661     const int wordEndPos = curs.position();
662
663     const QTextLine otherLine = currentTextLine(curs);
664     if (otherLine.textStart() != line.textStart()
665         || wordEndPos == wordStartPos)
666         return;
667
668     const qreal wordEndX = line.cursorToX(curs.position() - blockPos) + blockCoordinates.x();
669
670     if (!wordSelectionEnabled && (mouseXPosition < wordStartX || mouseXPosition > wordEndX))
671         return;
672
673     if (wordSelectionEnabled) {
674         if (suggestedNewPosition < selectedWordOnDoubleClick.position()) {
675             cursor.setPosition(selectedWordOnDoubleClick.selectionEnd());
676             setCursorPosition(wordStartPos, QTextCursor::KeepAnchor);
677         } else {
678             cursor.setPosition(selectedWordOnDoubleClick.selectionStart());
679             setCursorPosition(wordEndPos, QTextCursor::KeepAnchor);
680         }
681     } else {
682         // keep the already selected word even when moving to the left
683         // (#39164)
684         if (suggestedNewPosition < selectedWordOnDoubleClick.position())
685             cursor.setPosition(selectedWordOnDoubleClick.selectionEnd());
686         else
687             cursor.setPosition(selectedWordOnDoubleClick.selectionStart());
688
689         const qreal differenceToStart = mouseXPosition - wordStartX;
690         const qreal differenceToEnd = wordEndX - mouseXPosition;
691
692         if (differenceToStart < differenceToEnd)
693             setCursorPosition(wordStartPos, QTextCursor::KeepAnchor);
694         else
695             setCursorPosition(wordEndPos, QTextCursor::KeepAnchor);
696     }
697
698     if (interactionFlags & Qt::TextSelectableByMouse) {
699 #ifndef QT_NO_CLIPBOARD
700         setClipboardSelection();
701 #endif
702         selectionChanged(true);
703     }
704 }
705
706 void QQuickTextControlPrivate::extendBlockwiseSelection(int suggestedNewPosition)
707 {
708     Q_Q(QQuickTextControl);
709
710     // if inside the initial selected line keep that
711     if (suggestedNewPosition >= selectedBlockOnTrippleClick.selectionStart()
712         && suggestedNewPosition <= selectedBlockOnTrippleClick.selectionEnd()) {
713         q->setTextCursor(selectedBlockOnTrippleClick);
714         return;
715     }
716
717     if (suggestedNewPosition < selectedBlockOnTrippleClick.position()) {
718         cursor.setPosition(selectedBlockOnTrippleClick.selectionEnd());
719         cursor.setPosition(suggestedNewPosition, QTextCursor::KeepAnchor);
720         cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor);
721     } else {
722         cursor.setPosition(selectedBlockOnTrippleClick.selectionStart());
723         cursor.setPosition(suggestedNewPosition, QTextCursor::KeepAnchor);
724         cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
725         cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
726     }
727
728     if (interactionFlags & Qt::TextSelectableByMouse) {
729 #ifndef QT_NO_CLIPBOARD
730         setClipboardSelection();
731 #endif
732         selectionChanged(true);
733     }
734 }
735
736 void QQuickTextControlPrivate::_q_deleteSelected()
737 {
738     if (!(interactionFlags & Qt::TextEditable) || !cursor.hasSelection())
739         return;
740     cursor.removeSelectedText();
741 }
742
743 void QQuickTextControl::undo()
744 {
745     Q_D(QQuickTextControl);
746     d->repaintSelection();
747     const int oldCursorPos = d->cursor.position();
748     d->doc->undo(&d->cursor);
749     if (d->cursor.position() != oldCursorPos)
750         emit cursorPositionChanged();
751     emit microFocusChanged();
752     ensureCursorVisible();
753 }
754
755 void QQuickTextControl::redo()
756 {
757     Q_D(QQuickTextControl);
758     d->repaintSelection();
759     const int oldCursorPos = d->cursor.position();
760     d->doc->redo(&d->cursor);
761         if (d->cursor.position() != oldCursorPos)
762         emit cursorPositionChanged();
763     emit microFocusChanged();
764     ensureCursorVisible();
765 }
766
767 QQuickTextControl::QQuickTextControl(QObject *parent)
768     : QObject(*new QQuickTextControlPrivate, parent)
769 {
770     Q_D(QQuickTextControl);
771     d->init();
772 }
773
774 QQuickTextControl::QQuickTextControl(const QString &text, QObject *parent)
775     : QObject(*new QQuickTextControlPrivate, parent)
776 {
777     Q_D(QQuickTextControl);
778     d->init(Qt::RichText, text);
779 }
780
781 QQuickTextControl::QQuickTextControl(QTextDocument *doc, QObject *parent)
782     : QObject(*new QQuickTextControlPrivate, parent)
783 {
784     Q_D(QQuickTextControl);
785     d->init(Qt::RichText, QString(), doc);
786 }
787
788 QQuickTextControl::~QQuickTextControl()
789 {
790 }
791
792 void QQuickTextControl::setView(QObject *view)
793 {
794     Q_D(QQuickTextControl);
795     d->contextObject = view;
796 }
797
798 QObject *QQuickTextControl::view() const
799 {
800     Q_D(const QQuickTextControl);
801     return d->contextObject;
802 }
803
804 void QQuickTextControl::setDocument(QTextDocument *document)
805 {
806     Q_D(QQuickTextControl);
807     if (d->doc == document)
808         return;
809
810     d->doc->disconnect(this);
811     d->doc->documentLayout()->disconnect(this);
812     d->doc->documentLayout()->setPaintDevice(0);
813
814     if (d->doc->parent() == this)
815         delete d->doc;
816
817     d->doc = 0;
818     d->setContent(Qt::RichText, QString(), document);
819 }
820
821 QTextDocument *QQuickTextControl::document() const
822 {
823     Q_D(const QQuickTextControl);
824     return d->doc;
825 }
826
827 void QQuickTextControl::setTextCursor(const QTextCursor &cursor)
828 {
829     Q_D(QQuickTextControl);
830     d->commitPreedit();
831     d->cursorIsFocusIndicator = false;
832     const bool posChanged = cursor.position() != d->cursor.position();
833     const QTextCursor oldSelection = d->cursor;
834     d->cursor = cursor;
835     d->cursorOn = d->hasFocus && (d->interactionFlags & Qt::TextEditable);
836     d->_q_updateCurrentCharFormatAndSelection();
837     ensureCursorVisible();
838     d->repaintOldAndNewSelection(oldSelection);
839     if (posChanged)
840         emit cursorPositionChanged();
841 }
842
843 QTextCursor QQuickTextControl::textCursor() const
844 {
845     Q_D(const QQuickTextControl);
846     return d->cursor;
847 }
848
849 #ifndef QT_NO_CLIPBOARD
850
851 void QQuickTextControl::cut()
852 {
853     Q_D(QQuickTextControl);
854     if (!(d->interactionFlags & Qt::TextEditable) || !d->cursor.hasSelection())
855         return;
856     copy();
857     d->cursor.removeSelectedText();
858 }
859
860 void QQuickTextControl::copy()
861 {
862     Q_D(QQuickTextControl);
863     if (!d->cursor.hasSelection())
864         return;
865     QMimeData *data = createMimeDataFromSelection();
866     QGuiApplication::clipboard()->setMimeData(data);
867 }
868
869 void QQuickTextControl::paste(QClipboard::Mode mode)
870 {
871     const QMimeData *md = QGuiApplication::clipboard()->mimeData(mode);
872     if (md)
873         insertFromMimeData(md);
874 }
875 #endif
876
877 void QQuickTextControl::clear()
878 {
879     Q_D(QQuickTextControl);
880     // clears and sets empty content
881     d->extraSelections.clear();
882     d->setContent();
883 }
884
885
886 void QQuickTextControl::selectAll()
887 {
888     Q_D(QQuickTextControl);
889     const int selectionLength = qAbs(d->cursor.position() - d->cursor.anchor());
890     d->cursor.select(QTextCursor::Document);
891     d->selectionChanged(selectionLength != qAbs(d->cursor.position() - d->cursor.anchor()));
892     d->cursorIsFocusIndicator = false;
893     emit updateRequest();
894 }
895
896 void QQuickTextControl::processEvent(QEvent *e, const QPointF &coordinateOffset)
897 {
898     QMatrix m;
899     m.translate(coordinateOffset.x(), coordinateOffset.y());
900     processEvent(e, m);
901 }
902
903 void QQuickTextControl::processEvent(QEvent *e, const QMatrix &matrix)
904 {
905     Q_D(QQuickTextControl);
906     if (d->interactionFlags == Qt::NoTextInteraction) {
907         e->ignore();
908         return;
909     }
910
911     switch (e->type()) {
912         case QEvent::KeyPress:
913             d->keyPressEvent(static_cast<QKeyEvent *>(e));
914             break;
915         case QEvent::MouseButtonPress: {
916             QMouseEvent *ev = static_cast<QMouseEvent *>(e);
917             d->mousePressEvent(ev, ev->button(), matrix.map(ev->localPos()), ev->modifiers(),
918                                ev->buttons(), ev->screenPos().toPoint());
919             break; }
920         case QEvent::MouseMove: {
921             QMouseEvent *ev = static_cast<QMouseEvent *>(e);
922             d->mouseMoveEvent(ev, ev->button(), matrix.map(ev->localPos()), ev->modifiers(),
923                               ev->buttons(), ev->screenPos().toPoint());
924             break; }
925         case QEvent::MouseButtonRelease: {
926             QMouseEvent *ev = static_cast<QMouseEvent *>(e);
927             d->mouseReleaseEvent(ev, ev->button(), matrix.map(ev->localPos()), ev->modifiers(),
928                                  ev->buttons(), ev->screenPos().toPoint());
929             break; }
930         case QEvent::MouseButtonDblClick: {
931             QMouseEvent *ev = static_cast<QMouseEvent *>(e);
932             d->mouseDoubleClickEvent(ev, ev->button(), matrix.map(ev->localPos()), ev->modifiers(),
933                                      ev->buttons(), ev->screenPos().toPoint());
934             break; }
935         case QEvent::InputMethod:
936             d->inputMethodEvent(static_cast<QInputMethodEvent *>(e));
937             break;
938         case QEvent::FocusIn:
939         case QEvent::FocusOut:
940             d->focusEvent(static_cast<QFocusEvent *>(e));
941             break;
942
943         case QEvent::EnabledChange:
944             d->isEnabled = e->isAccepted();
945             break;
946
947 #ifndef QT_NO_DRAGANDDROP
948         case QEvent::DragEnter: {
949             QDragEnterEvent *ev = static_cast<QDragEnterEvent *>(e);
950             if (d->dragEnterEvent(e, ev->mimeData()))
951                 ev->acceptProposedAction();
952             break;
953         }
954         case QEvent::DragLeave:
955             d->dragLeaveEvent();
956             break;
957         case QEvent::DragMove: {
958             QDragMoveEvent *ev = static_cast<QDragMoveEvent *>(e);
959             if (d->dragMoveEvent(e, ev->mimeData(), matrix.map(ev->pos())))
960                 ev->acceptProposedAction();
961             break;
962         }
963         case QEvent::Drop: {
964             QDropEvent *ev = static_cast<QDropEvent *>(e);
965             if (d->dropEvent(ev->mimeData(), matrix.map(ev->pos()), ev->dropAction(), ev->source()))
966                 ev->acceptProposedAction();
967             break;
968         }
969 #endif
970
971 #ifdef QT_KEYPAD_NAVIGATION
972         case QEvent::EnterEditFocus:
973         case QEvent::LeaveEditFocus:
974             if (QGuiApplication::keypadNavigationEnabled())
975                 d->editFocusEvent(e);
976             break;
977 #endif
978         case QEvent::ShortcutOverride:
979             if (d->interactionFlags & Qt::TextEditable) {
980                 QKeyEvent* ke = static_cast<QKeyEvent *>(e);
981                 if (ke->modifiers() == Qt::NoModifier
982                     || ke->modifiers() == Qt::ShiftModifier
983                     || ke->modifiers() == Qt::KeypadModifier) {
984                     if (ke->key() < Qt::Key_Escape) {
985                         ke->accept();
986                     } else {
987                         switch (ke->key()) {
988                             case Qt::Key_Return:
989                             case Qt::Key_Enter:
990                             case Qt::Key_Delete:
991                             case Qt::Key_Home:
992                             case Qt::Key_End:
993                             case Qt::Key_Backspace:
994                             case Qt::Key_Left:
995                             case Qt::Key_Right:
996                             case Qt::Key_Up:
997                             case Qt::Key_Down:
998                             case Qt::Key_Tab:
999                             ke->accept();
1000                         default:
1001                             break;
1002                         }
1003                     }
1004 #ifndef QT_NO_SHORTCUT
1005                 } else if (ke == QKeySequence::Copy
1006                            || ke == QKeySequence::Paste
1007                            || ke == QKeySequence::Cut
1008                            || ke == QKeySequence::Redo
1009                            || ke == QKeySequence::Undo
1010                            || ke == QKeySequence::MoveToNextWord
1011                            || ke == QKeySequence::MoveToPreviousWord
1012                            || ke == QKeySequence::MoveToStartOfDocument
1013                            || ke == QKeySequence::MoveToEndOfDocument
1014                            || ke == QKeySequence::SelectNextWord
1015                            || ke == QKeySequence::SelectPreviousWord
1016                            || ke == QKeySequence::SelectStartOfLine
1017                            || ke == QKeySequence::SelectEndOfLine
1018                            || ke == QKeySequence::SelectStartOfBlock
1019                            || ke == QKeySequence::SelectEndOfBlock
1020                            || ke == QKeySequence::SelectStartOfDocument
1021                            || ke == QKeySequence::SelectEndOfDocument
1022                            || ke == QKeySequence::SelectAll
1023                           ) {
1024                     ke->accept();
1025 #endif
1026                 }
1027             }
1028             break;
1029         default:
1030             break;
1031     }
1032 }
1033
1034 bool QQuickTextControl::event(QEvent *e)
1035 {
1036     return QObject::event(e);
1037 }
1038
1039 void QQuickTextControl::timerEvent(QTimerEvent *e)
1040 {
1041     Q_D(QQuickTextControl);
1042     if (e->timerId() == d->cursorBlinkTimer.timerId()) {
1043         d->cursorOn = !d->cursorOn;
1044
1045         // ###
1046 //        if (d->cursor.hasSelection())
1047 //            d->cursorOn &= (QGuiApplication::style()->styleHint(QStyle::SH_BlinkCursorWhenTextSelected)
1048 //                            != 0);
1049
1050         d->repaintCursor();
1051     } else if (e->timerId() == d->trippleClickTimer.timerId()) {
1052         d->trippleClickTimer.stop();
1053     }
1054 }
1055
1056 void QQuickTextControl::setPlainText(const QString &text)
1057 {
1058     Q_D(QQuickTextControl);
1059     d->setContent(Qt::PlainText, text);
1060 }
1061
1062 void QQuickTextControl::setHtml(const QString &text)
1063 {
1064     Q_D(QQuickTextControl);
1065     d->setContent(Qt::RichText, text);
1066 }
1067
1068 void QQuickTextControlPrivate::keyPressEvent(QKeyEvent *e)
1069 {
1070     Q_Q(QQuickTextControl);
1071 #ifndef QT_NO_SHORTCUT
1072     if (e == QKeySequence::SelectAll) {
1073             e->accept();
1074             q->selectAll();
1075             return;
1076     }
1077 #ifndef QT_NO_CLIPBOARD
1078     else if (e == QKeySequence::Copy) {
1079             e->accept();
1080             q->copy();
1081             return;
1082     }
1083 #endif
1084 #endif // QT_NO_SHORTCUT
1085
1086     if (interactionFlags & Qt::TextSelectableByKeyboard
1087         && cursorMoveKeyEvent(e))
1088         goto accept;
1089
1090     if (interactionFlags & Qt::LinksAccessibleByKeyboard) {
1091         if ((e->key() == Qt::Key_Return
1092              || e->key() == Qt::Key_Enter
1093 #ifdef QT_KEYPAD_NAVIGATION
1094              || e->key() == Qt::Key_Select
1095 #endif
1096              )
1097             && cursor.hasSelection()) {
1098
1099             e->accept();
1100             activateLinkUnderCursor();
1101             return;
1102         }
1103     }
1104
1105     if (!(interactionFlags & Qt::TextEditable)) {
1106         e->ignore();
1107         return;
1108     }
1109
1110     if (e->key() == Qt::Key_Direction_L || e->key() == Qt::Key_Direction_R) {
1111         QTextBlockFormat fmt;
1112         fmt.setLayoutDirection((e->key() == Qt::Key_Direction_L) ? Qt::LeftToRight : Qt::RightToLeft);
1113         cursor.mergeBlockFormat(fmt);
1114         goto accept;
1115     }
1116
1117     // schedule a repaint of the region of the cursor, as when we move it we
1118     // want to make sure the old cursor disappears (not noticeable when moving
1119     // only a few pixels but noticeable when jumping between cells in tables for
1120     // example)
1121     repaintSelection();
1122
1123     if (e->key() == Qt::Key_Backspace && !(e->modifiers() & ~Qt::ShiftModifier)) {
1124         QTextBlockFormat blockFmt = cursor.blockFormat();
1125         QTextList *list = cursor.currentList();
1126         if (list && cursor.atBlockStart() && !cursor.hasSelection()) {
1127             list->remove(cursor.block());
1128         } else if (cursor.atBlockStart() && blockFmt.indent() > 0) {
1129             blockFmt.setIndent(blockFmt.indent() - 1);
1130             cursor.setBlockFormat(blockFmt);
1131         } else {
1132             QTextCursor localCursor = cursor;
1133             localCursor.deletePreviousChar();
1134         }
1135         goto accept;
1136     }
1137 #ifndef QT_NO_SHORTCUT
1138       else if (e == QKeySequence::InsertParagraphSeparator) {
1139         cursor.insertBlock();
1140         e->accept();
1141         goto accept;
1142     } else if (e == QKeySequence::InsertLineSeparator) {
1143         cursor.insertText(QString(QChar::LineSeparator));
1144         e->accept();
1145         goto accept;
1146     }
1147 #endif
1148     if (false) {
1149     }
1150 #ifndef QT_NO_SHORTCUT
1151     else if (e == QKeySequence::Undo) {
1152             q->undo();
1153     }
1154     else if (e == QKeySequence::Redo) {
1155            q->redo();
1156     }
1157 #ifndef QT_NO_CLIPBOARD
1158     else if (e == QKeySequence::Cut) {
1159            q->cut();
1160     }
1161     else if (e == QKeySequence::Paste) {
1162         QClipboard::Mode mode = QClipboard::Clipboard;
1163         q->paste(mode);
1164     }
1165 #endif
1166     else if (e == QKeySequence::Delete) {
1167         QTextCursor localCursor = cursor;
1168         localCursor.deleteChar();
1169     }
1170     else if (e == QKeySequence::DeleteEndOfWord) {
1171         if (!cursor.hasSelection())
1172             cursor.movePosition(QTextCursor::NextWord, QTextCursor::KeepAnchor);
1173         cursor.removeSelectedText();
1174     }
1175     else if (e == QKeySequence::DeleteStartOfWord) {
1176         if (!cursor.hasSelection())
1177             cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor);
1178         cursor.removeSelectedText();
1179     }
1180     else if (e == QKeySequence::DeleteEndOfLine) {
1181         QTextBlock block = cursor.block();
1182         if (cursor.position() == block.position() + block.length() - 2)
1183             cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
1184         else
1185             cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
1186         cursor.removeSelectedText();
1187     }
1188 #endif // QT_NO_SHORTCUT
1189     else {
1190         goto process;
1191     }
1192     goto accept;
1193
1194 process:
1195     {
1196         QString text = e->text();
1197         if (!text.isEmpty() && (text.at(0).isPrint() || text.at(0) == QLatin1Char('\t'))) {
1198             if (overwriteMode
1199                 // no need to call deleteChar() if we have a selection, insertText
1200                 // does it already
1201                 && !cursor.hasSelection()
1202                 && !cursor.atBlockEnd())
1203                 cursor.deleteChar();
1204
1205             cursor.insertText(text);
1206             selectionChanged();
1207         } else {
1208             e->ignore();
1209             return;
1210         }
1211     }
1212
1213  accept:
1214
1215     e->accept();
1216     cursorOn = true;
1217
1218     q->ensureCursorVisible();
1219
1220     updateCurrentCharFormat();
1221 }
1222
1223 QVariant QQuickTextControl::loadResource(int type, const QUrl &name)
1224 {
1225 #if 1
1226     Q_UNUSED(type);
1227     Q_UNUSED(name);
1228 #else
1229     if (QTextEdit *textEdit = qobject_cast<QTextEdit *>(parent())) {
1230         QUrl resolvedName = textEdit->d_func()->resolveUrl(name);
1231         return textEdit->loadResource(type, resolvedName);
1232     }
1233 #endif
1234     return QVariant();
1235 }
1236
1237 void QQuickTextControlPrivate::_q_updateBlock(const QTextBlock &block)
1238 {
1239     Q_Q(QQuickTextControl);
1240     QRectF br = q->blockBoundingRect(block);
1241     br.setRight(qreal(INT_MAX)); // the block might have shrunk
1242     emit q->updateRequest(br);
1243 }
1244
1245 QRectF QQuickTextControlPrivate::rectForPosition(int position) const
1246 {
1247     Q_Q(const QQuickTextControl);
1248     const QTextBlock block = doc->findBlock(position);
1249     if (!block.isValid())
1250         return QRectF();
1251     const QAbstractTextDocumentLayout *docLayout = doc->documentLayout();
1252     const QTextLayout *layout = block.layout();
1253     const QPointF layoutPos = q->blockBoundingRect(block).topLeft();
1254     int relativePos = position - block.position();
1255     if (preeditCursor != 0) {
1256         int preeditPos = layout->preeditAreaPosition();
1257         if (relativePos == preeditPos)
1258             relativePos += preeditCursor;
1259         else if (relativePos > preeditPos)
1260             relativePos += layout->preeditAreaText().length();
1261     }
1262     QTextLine line = layout->lineForTextPosition(relativePos);
1263
1264     int cursorWidth;
1265     {
1266         bool ok = false;
1267 #ifndef QT_NO_PROPERTIES
1268         cursorWidth = docLayout->property("cursorWidth").toInt(&ok);
1269 #endif
1270         if (!ok)
1271             cursorWidth = 1;
1272     }
1273
1274     QRectF r;
1275
1276     if (line.isValid()) {
1277         qreal x = line.cursorToX(relativePos);
1278         qreal w = 0;
1279         if (overwriteMode) {
1280             if (relativePos < line.textLength() - line.textStart())
1281                 w = line.cursorToX(relativePos + 1) - x;
1282             else
1283                 w = QFontMetrics(block.layout()->font()).width(QLatin1Char(' ')); // in sync with QTextLine::draw()
1284         }
1285         r = QRectF(layoutPos.x() + x, layoutPos.y() + line.y(),
1286                    cursorWidth + w, line.height());
1287     } else {
1288         r = QRectF(layoutPos.x(), layoutPos.y(), cursorWidth, 10); // #### correct height
1289     }
1290
1291     return r;
1292 }
1293
1294 static inline bool firstFramePosLessThanCursorPos(QTextFrame *frame, int position)
1295 {
1296     return frame->firstPosition() < position;
1297 }
1298
1299 static inline bool cursorPosLessThanLastFramePos(int position, QTextFrame *frame)
1300 {
1301     return position < frame->lastPosition();
1302 }
1303
1304 static QRectF boundingRectOfFloatsInSelection(const QTextCursor &cursor)
1305 {
1306     QRectF r;
1307     QTextFrame *frame = cursor.currentFrame();
1308     const QList<QTextFrame *> children = frame->childFrames();
1309
1310     const QList<QTextFrame *>::ConstIterator firstFrame = qLowerBound(children.constBegin(), children.constEnd(),
1311                                                                       cursor.selectionStart(), firstFramePosLessThanCursorPos);
1312     const QList<QTextFrame *>::ConstIterator lastFrame = qUpperBound(children.constBegin(), children.constEnd(),
1313                                                                      cursor.selectionEnd(), cursorPosLessThanLastFramePos);
1314     for (QList<QTextFrame *>::ConstIterator it = firstFrame; it != lastFrame; ++it) {
1315         if ((*it)->frameFormat().position() != QTextFrameFormat::InFlow)
1316             r |= frame->document()->documentLayout()->frameBoundingRect(*it);
1317     }
1318     return r;
1319 }
1320
1321 QRectF QQuickTextControl::selectionRect(const QTextCursor &cursor) const
1322 {
1323     Q_D(const QQuickTextControl);
1324
1325     QRectF r = d->rectForPosition(cursor.selectionStart());
1326
1327     if (cursor.hasComplexSelection() && cursor.currentTable()) {
1328         QTextTable *table = cursor.currentTable();
1329
1330         r = d->doc->documentLayout()->frameBoundingRect(table);
1331         /*
1332         int firstRow, numRows, firstColumn, numColumns;
1333         cursor.selectedTableCells(&firstRow, &numRows, &firstColumn, &numColumns);
1334
1335         const QTextTableCell firstCell = table->cellAt(firstRow, firstColumn);
1336         const QTextTableCell lastCell = table->cellAt(firstRow + numRows - 1, firstColumn + numColumns - 1);
1337
1338         const QAbstractTextDocumentLayout * const layout = doc->documentLayout();
1339
1340         QRectF tableSelRect = layout->blockBoundingRect(firstCell.firstCursorPosition().block());
1341
1342         for (int col = firstColumn; col < firstColumn + numColumns; ++col) {
1343             const QTextTableCell cell = table->cellAt(firstRow, col);
1344             const qreal y = layout->blockBoundingRect(cell.firstCursorPosition().block()).top();
1345
1346             tableSelRect.setTop(qMin(tableSelRect.top(), y));
1347         }
1348
1349         for (int row = firstRow; row < firstRow + numRows; ++row) {
1350             const QTextTableCell cell = table->cellAt(row, firstColumn);
1351             const qreal x = layout->blockBoundingRect(cell.firstCursorPosition().block()).left();
1352
1353             tableSelRect.setLeft(qMin(tableSelRect.left(), x));
1354         }
1355
1356         for (int col = firstColumn; col < firstColumn + numColumns; ++col) {
1357             const QTextTableCell cell = table->cellAt(firstRow + numRows - 1, col);
1358             const qreal y = layout->blockBoundingRect(cell.lastCursorPosition().block()).bottom();
1359
1360             tableSelRect.setBottom(qMax(tableSelRect.bottom(), y));
1361         }
1362
1363         for (int row = firstRow; row < firstRow + numRows; ++row) {
1364             const QTextTableCell cell = table->cellAt(row, firstColumn + numColumns - 1);
1365             const qreal x = layout->blockBoundingRect(cell.lastCursorPosition().block()).right();
1366
1367             tableSelRect.setRight(qMax(tableSelRect.right(), x));
1368         }
1369
1370         r = tableSelRect.toRect();
1371         */
1372     } else if (cursor.hasSelection()) {
1373         const int position = cursor.selectionStart();
1374         const int anchor = cursor.selectionEnd();
1375         const QTextBlock posBlock = d->doc->findBlock(position);
1376         const QTextBlock anchorBlock = d->doc->findBlock(anchor);
1377         if (posBlock == anchorBlock && posBlock.isValid() && posBlock.layout()->lineCount()) {
1378             const QTextLine posLine = posBlock.layout()->lineForTextPosition(position - posBlock.position());
1379             const QTextLine anchorLine = anchorBlock.layout()->lineForTextPosition(anchor - anchorBlock.position());
1380
1381             const int firstLine = qMin(posLine.lineNumber(), anchorLine.lineNumber());
1382             const int lastLine = qMax(posLine.lineNumber(), anchorLine.lineNumber());
1383             const QTextLayout *layout = posBlock.layout();
1384             r = QRectF();
1385             for (int i = firstLine; i <= lastLine; ++i) {
1386                 r |= layout->lineAt(i).rect();
1387                 r |= layout->lineAt(i).naturalTextRect(); // might be bigger in the case of wrap not enabled
1388             }
1389             r.translate(blockBoundingRect(posBlock).topLeft());
1390         } else {
1391             QRectF anchorRect = d->rectForPosition(cursor.selectionEnd());
1392             r |= anchorRect;
1393             r |= boundingRectOfFloatsInSelection(cursor);
1394             QRectF frameRect(d->doc->documentLayout()->frameBoundingRect(cursor.currentFrame()));
1395             r.setLeft(frameRect.left());
1396             r.setRight(frameRect.right());
1397         }
1398         if (r.isValid())
1399             r.adjust(-1, -1, 1, 1);
1400     }
1401
1402     return r;
1403 }
1404
1405 QRectF QQuickTextControl::selectionRect() const
1406 {
1407     Q_D(const QQuickTextControl);
1408     return selectionRect(d->cursor);
1409 }
1410
1411 void QQuickTextControlPrivate::mousePressEvent(QEvent *e, Qt::MouseButton button, const QPointF &pos, Qt::KeyboardModifiers modifiers,
1412                                           Qt::MouseButtons buttons, const QPoint &globalPos)
1413 {
1414     Q_Q(QQuickTextControl);
1415
1416     mousePressed = (interactionFlags & Qt::TextSelectableByMouse);
1417     mousePressPos = pos.toPoint();
1418 #ifndef QT_NO_DRAGANDDROP
1419     mightStartDrag = false;
1420 #endif
1421
1422     if (sendMouseEventToInputContext(
1423             e, QEvent::MouseButtonPress, button, pos, modifiers, buttons, globalPos)) {
1424         return;
1425     }
1426
1427     if (interactionFlags & Qt::LinksAccessibleByMouse) {
1428         anchorOnMousePress = q->anchorAt(pos);
1429
1430         if (cursorIsFocusIndicator) {
1431             cursorIsFocusIndicator = false;
1432             repaintSelection();
1433             cursor.clearSelection();
1434         }
1435     }
1436     if (!(button & Qt::LeftButton) ||
1437         !((interactionFlags & Qt::TextSelectableByMouse) || (interactionFlags & Qt::TextEditable))) {
1438             e->ignore();
1439             return;
1440     }
1441
1442     cursorIsFocusIndicator = false;
1443     const QTextCursor oldSelection = cursor;
1444     const int oldCursorPos = cursor.position();
1445
1446     commitPreedit();
1447
1448     if (trippleClickTimer.isActive()
1449         && ((pos - trippleClickPoint).toPoint().manhattanLength() < qApp->styleHints()->startDragDistance())) {
1450
1451         cursor.movePosition(QTextCursor::StartOfBlock);
1452         cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
1453         cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
1454         selectedBlockOnTrippleClick = cursor;
1455
1456         anchorOnMousePress = QString();
1457
1458         trippleClickTimer.stop();
1459     } else {
1460         int cursorPos = q->hitTest(pos, Qt::FuzzyHit);
1461         if (cursorPos == -1) {
1462             e->ignore();
1463             return;
1464         }
1465
1466         if (modifiers == Qt::ShiftModifier && (interactionFlags & Qt::TextSelectableByMouse)) {
1467             if (wordSelectionEnabled && !selectedWordOnDoubleClick.hasSelection()) {
1468                 selectedWordOnDoubleClick = cursor;
1469                 selectedWordOnDoubleClick.select(QTextCursor::WordUnderCursor);
1470             }
1471
1472             if (selectedBlockOnTrippleClick.hasSelection())
1473                 extendBlockwiseSelection(cursorPos);
1474             else if (selectedWordOnDoubleClick.hasSelection())
1475                 extendWordwiseSelection(cursorPos, pos.x());
1476             else if (!wordSelectionEnabled)
1477                 setCursorPosition(cursorPos, QTextCursor::KeepAnchor);
1478         } else {
1479
1480             if (dragEnabled
1481                 && cursor.hasSelection()
1482                 && !cursorIsFocusIndicator
1483                 && cursorPos >= cursor.selectionStart()
1484                 && cursorPos <= cursor.selectionEnd()
1485                 && q->hitTest(pos, Qt::ExactHit) != -1) {
1486 #ifndef QT_NO_DRAGANDDROP
1487                 mightStartDrag = true;
1488 #endif
1489                 return;
1490             }
1491
1492             setCursorPosition(cursorPos);
1493         }
1494     }
1495
1496     if (interactionFlags & Qt::TextEditable) {
1497         q->ensureCursorVisible();
1498         if (cursor.position() != oldCursorPos)
1499             emit q->cursorPositionChanged();
1500         _q_updateCurrentCharFormatAndSelection();
1501     } else {
1502         if (cursor.position() != oldCursorPos) {
1503             emit q->cursorPositionChanged();
1504             emit q->microFocusChanged();
1505         }
1506         selectionChanged();
1507     }
1508     repaintOldAndNewSelection(oldSelection);
1509     hadSelectionOnMousePress = cursor.hasSelection();
1510 }
1511
1512 void QQuickTextControlPrivate::mouseMoveEvent(QEvent *e, Qt::MouseButton button, const QPointF &mousePos, Qt::KeyboardModifiers modifiers,
1513                                          Qt::MouseButtons buttons, const QPoint &globalPos)
1514 {
1515     Q_Q(QQuickTextControl);
1516
1517     if (interactionFlags & Qt::LinksAccessibleByMouse) {
1518         QString anchor = q->anchorAt(mousePos);
1519         if (anchor != highlightedAnchor) {
1520             highlightedAnchor = anchor;
1521             emit q->linkHovered(anchor);
1522         }
1523     }
1524
1525     if (buttons & Qt::LeftButton) {
1526         const bool editable = interactionFlags & Qt::TextEditable;
1527
1528         if (!(mousePressed
1529               || editable
1530               || mightStartDrag
1531               || selectedWordOnDoubleClick.hasSelection()
1532               || selectedBlockOnTrippleClick.hasSelection()))
1533             return;
1534
1535         const QTextCursor oldSelection = cursor;
1536         const int oldCursorPos = cursor.position();
1537
1538         if (mightStartDrag) {
1539             if ((mousePos.toPoint() - mousePressPos).manhattanLength() > qApp->styleHints()->startDragDistance())
1540                 startDrag();
1541             return;
1542         }
1543
1544         if (!mousePressed)
1545             return;
1546
1547         const qreal mouseX = qreal(mousePos.x());
1548
1549         int newCursorPos = q->hitTest(mousePos, Qt::FuzzyHit);
1550
1551         if (isPreediting()) {
1552             // note: oldCursorPos not including preedit
1553             int selectionStartPos = q->hitTest(mousePressPos, Qt::FuzzyHit);
1554             if (newCursorPos != selectionStartPos) {
1555                 commitPreedit();
1556                 // commit invalidates positions
1557                 newCursorPos = q->hitTest(mousePos, Qt::FuzzyHit);
1558                 selectionStartPos = q->hitTest(mousePressPos, Qt::FuzzyHit);
1559                 setCursorPosition(selectionStartPos);
1560             }
1561         }
1562
1563         if (newCursorPos == -1)
1564             return;
1565
1566         if (wordSelectionEnabled && !selectedWordOnDoubleClick.hasSelection()) {
1567             selectedWordOnDoubleClick = cursor;
1568             selectedWordOnDoubleClick.select(QTextCursor::WordUnderCursor);
1569         }
1570
1571         if (selectedBlockOnTrippleClick.hasSelection())
1572             extendBlockwiseSelection(newCursorPos);
1573         else if (selectedWordOnDoubleClick.hasSelection())
1574             extendWordwiseSelection(newCursorPos, mouseX);
1575         else if (!isPreediting())
1576             setCursorPosition(newCursorPos, QTextCursor::KeepAnchor);
1577
1578         if (interactionFlags & Qt::TextEditable) {
1579             // don't call ensureVisible for the visible cursor to avoid jumping
1580             // scrollbars. the autoscrolling ensures smooth scrolling if necessary.
1581             //q->ensureCursorVisible();
1582             if (cursor.position() != oldCursorPos)
1583                 emit q->cursorPositionChanged();
1584             _q_updateCurrentCharFormatAndSelection();
1585             if (qGuiApp)
1586                 qGuiApp->inputPanel()->update(Qt::ImQueryInput);
1587         } else {
1588             //emit q->visibilityRequest(QRectF(mousePos, QSizeF(1, 1)));
1589             if (cursor.position() != oldCursorPos) {
1590                 emit q->cursorPositionChanged();
1591                 emit q->microFocusChanged();
1592             }
1593         }
1594         selectionChanged(true);
1595         repaintOldAndNewSelection(oldSelection);
1596     }
1597
1598     sendMouseEventToInputContext(e, QEvent::MouseMove, button, mousePos, modifiers, buttons, globalPos);
1599 }
1600
1601 void QQuickTextControlPrivate::mouseReleaseEvent(QEvent *e, Qt::MouseButton button, const QPointF &pos, Qt::KeyboardModifiers modifiers,
1602                                             Qt::MouseButtons buttons, const QPoint &globalPos)
1603 {
1604     Q_Q(QQuickTextControl);
1605
1606     if (sendMouseEventToInputContext(
1607             e, QEvent::MouseButtonRelease, button, pos, modifiers, buttons, globalPos)) {
1608         return;
1609     }
1610
1611     const QTextCursor oldSelection = cursor;
1612     const int oldCursorPos = cursor.position();
1613
1614 #ifndef QT_NO_DRAGANDDROP
1615     if (mightStartDrag && (button & Qt::LeftButton)) {
1616         mousePressed = false;
1617         setCursorPosition(pos);
1618         cursor.clearSelection();
1619         selectionChanged();
1620     }
1621 #endif
1622     if (mousePressed) {
1623         mousePressed = false;
1624 #ifndef QT_NO_CLIPBOARD
1625         setClipboardSelection();
1626         selectionChanged(true);
1627     } else if (button == Qt::MidButton
1628                && (interactionFlags & Qt::TextEditable)
1629                && QGuiApplication::clipboard()->supportsSelection()) {
1630         setCursorPosition(pos);
1631         const QMimeData *md = QGuiApplication::clipboard()->mimeData(QClipboard::Selection);
1632         if (md)
1633             q->insertFromMimeData(md);
1634 #endif
1635     }
1636
1637     repaintOldAndNewSelection(oldSelection);
1638
1639     if (cursor.position() != oldCursorPos) {
1640         emit q->cursorPositionChanged();
1641         emit q->microFocusChanged();
1642     }
1643
1644     if (interactionFlags & Qt::LinksAccessibleByMouse) {
1645         if (!(button & Qt::LeftButton))
1646             return;
1647
1648         const QString anchor = q->anchorAt(pos);
1649
1650         if (anchor.isEmpty())
1651             return;
1652
1653         if (!cursor.hasSelection()
1654             || (anchor == anchorOnMousePress && hadSelectionOnMousePress)) {
1655
1656             const int anchorPos = q->hitTest(pos, Qt::ExactHit);
1657             if (anchorPos != -1) {
1658                 cursor.setPosition(anchorPos);
1659
1660                 QString anchor = anchorOnMousePress;
1661                 anchorOnMousePress = QString();
1662                 activateLinkUnderCursor(anchor);
1663             }
1664         }
1665     }
1666 }
1667
1668 void QQuickTextControlPrivate::mouseDoubleClickEvent(QEvent *e, Qt::MouseButton button, const QPointF &pos, Qt::KeyboardModifiers modifiers,
1669                                                 Qt::MouseButtons buttons, const QPoint &globalPos)
1670 {
1671     Q_Q(QQuickTextControl);
1672
1673     if (button == Qt::LeftButton
1674         && (interactionFlags & Qt::TextSelectableByMouse)) {
1675
1676 #ifndef QT_NO_DRAGANDDROP
1677         mightStartDrag = false;
1678 #endif
1679         commitPreedit();
1680
1681         const QTextCursor oldSelection = cursor;
1682         setCursorPosition(pos);
1683         QTextLine line = currentTextLine(cursor);
1684         bool doEmit = false;
1685         if (line.isValid() && line.textLength()) {
1686             cursor.select(QTextCursor::WordUnderCursor);
1687             doEmit = true;
1688         }
1689         repaintOldAndNewSelection(oldSelection);
1690
1691         cursorIsFocusIndicator = false;
1692         selectedWordOnDoubleClick = cursor;
1693
1694         trippleClickPoint = pos;
1695         trippleClickTimer.start(qApp->styleHints()->mouseDoubleClickInterval(), q);
1696         if (doEmit) {
1697             selectionChanged();
1698 #ifndef QT_NO_CLIPBOARD
1699             setClipboardSelection();
1700 #endif
1701             emit q->cursorPositionChanged();
1702         }
1703     } else if (!sendMouseEventToInputContext(e, QEvent::MouseButtonDblClick, button, pos,
1704                                              modifiers, buttons, globalPos)) {
1705         e->ignore();
1706     }
1707 }
1708
1709 bool QQuickTextControlPrivate::sendMouseEventToInputContext(
1710         QEvent *e, QEvent::Type eventType, Qt::MouseButton button, const QPointF &pos,
1711         Qt::KeyboardModifiers modifiers, Qt::MouseButtons buttons, const QPoint &globalPos)
1712 {
1713 #if !defined(QT_NO_IM)
1714     Q_Q(QQuickTextControl);
1715
1716     Q_UNUSED(e);
1717     Q_UNUSED(button);
1718     Q_UNUSED(modifiers);
1719     Q_UNUSED(buttons);
1720     Q_UNUSED(globalPos);
1721
1722     if (contextObject && isPreediting()) {
1723         QTextLayout *layout = cursor.block().layout();
1724         int cursorPos = q->hitTest(pos, Qt::FuzzyHit) - cursor.position();
1725
1726         if (cursorPos >= 0 && cursorPos <= layout->preeditAreaText().length()) {
1727             if (eventType == QEvent::MouseButtonRelease) {
1728                 qApp->inputPanel()->invokeAction(QInputPanel::Click, cursorPos);
1729             }
1730
1731             return true;
1732         }
1733     }
1734 #else
1735     Q_UNUSED(e);
1736     Q_UNUSED(eventType);
1737     Q_UNUSED(button);
1738     Q_UNUSED(pos);
1739     Q_UNUSED(modifiers);
1740     Q_UNUSED(buttons);
1741     Q_UNUSED(globalPos);
1742 #endif
1743     return false;
1744 }
1745
1746 bool QQuickTextControlPrivate::dragEnterEvent(QEvent *e, const QMimeData *mimeData)
1747 {
1748     Q_Q(QQuickTextControl);
1749     if (!(interactionFlags & Qt::TextEditable) || !q->canInsertFromMimeData(mimeData)) {
1750         e->ignore();
1751         return false;
1752     }
1753
1754     dndFeedbackCursor = QTextCursor();
1755
1756     return true; // accept proposed action
1757 }
1758
1759 void QQuickTextControlPrivate::dragLeaveEvent()
1760 {
1761     Q_Q(QQuickTextControl);
1762
1763     const QRectF crect = q->cursorRect(dndFeedbackCursor);
1764     dndFeedbackCursor = QTextCursor();
1765
1766     if (crect.isValid())
1767         emit q->updateRequest(crect);
1768 }
1769
1770 bool QQuickTextControlPrivate::dragMoveEvent(QEvent *e, const QMimeData *mimeData, const QPointF &pos)
1771 {
1772     Q_Q(QQuickTextControl);
1773     if (!(interactionFlags & Qt::TextEditable) || !q->canInsertFromMimeData(mimeData)) {
1774         e->ignore();
1775         return false;
1776     }
1777
1778     const int cursorPos = q->hitTest(pos, Qt::FuzzyHit);
1779     if (cursorPos != -1) {
1780         QRectF crect = q->cursorRect(dndFeedbackCursor);
1781         if (crect.isValid())
1782             emit q->updateRequest(crect);
1783
1784         dndFeedbackCursor = cursor;
1785         dndFeedbackCursor.setPosition(cursorPos);
1786
1787         crect = q->cursorRect(dndFeedbackCursor);
1788         emit q->updateRequest(crect);
1789     }
1790
1791     return true; // accept proposed action
1792 }
1793
1794 bool QQuickTextControlPrivate::dropEvent(const QMimeData *mimeData, const QPointF &pos, Qt::DropAction dropAction, QObject *source)
1795 {
1796     Q_Q(QQuickTextControl);
1797     dndFeedbackCursor = QTextCursor();
1798
1799     if (!(interactionFlags & Qt::TextEditable) || !q->canInsertFromMimeData(mimeData))
1800         return false;
1801
1802     repaintSelection();
1803
1804     QTextCursor insertionCursor = q->cursorForPosition(pos);
1805     insertionCursor.beginEditBlock();
1806
1807     if (dropAction == Qt::MoveAction && source == contextObject)
1808         cursor.removeSelectedText();
1809
1810     cursor = insertionCursor;
1811     q->insertFromMimeData(mimeData);
1812     insertionCursor.endEditBlock();
1813     q->ensureCursorVisible();
1814     return true; // accept proposed action
1815 }
1816
1817 void QQuickTextControlPrivate::inputMethodEvent(QInputMethodEvent *e)
1818 {
1819     Q_Q(QQuickTextControl);
1820     if (!(interactionFlags & Qt::TextEditable) || cursor.isNull()) {
1821         e->ignore();
1822         return;
1823     }
1824     bool isGettingInput = !e->commitString().isEmpty()
1825             || e->preeditString() != cursor.block().layout()->preeditAreaText()
1826             || e->replacementLength() > 0;
1827     bool forceSelectionChanged = false;
1828
1829     cursor.beginEditBlock();
1830     if (isGettingInput) {
1831         cursor.removeSelectedText();
1832     }
1833
1834     // insert commit string
1835     if (!e->commitString().isEmpty() || e->replacementLength()) {
1836         QTextCursor c = cursor;
1837         c.setPosition(c.position() + e->replacementStart());
1838         c.setPosition(c.position() + e->replacementLength(), QTextCursor::KeepAnchor);
1839         c.insertText(e->commitString());
1840     }
1841
1842     for (int i = 0; i < e->attributes().size(); ++i) {
1843         const QInputMethodEvent::Attribute &a = e->attributes().at(i);
1844         if (a.type == QInputMethodEvent::Selection) {
1845             QTextCursor oldCursor = cursor;
1846             int blockStart = a.start + cursor.block().position();
1847             cursor.setPosition(blockStart, QTextCursor::MoveAnchor);
1848             cursor.setPosition(blockStart + a.length, QTextCursor::KeepAnchor);
1849             q->ensureCursorVisible();
1850             repaintOldAndNewSelection(oldCursor);
1851             forceSelectionChanged = true;
1852         }
1853     }
1854
1855     QTextBlock block = cursor.block();
1856     QTextLayout *layout = block.layout();
1857     if (isGettingInput)
1858         layout->setPreeditArea(cursor.position() - block.position(), e->preeditString());
1859     QList<QTextLayout::FormatRange> overrides;
1860     const int oldPreeditCursor = preeditCursor;
1861     preeditCursor = e->preeditString().length();
1862     hideCursor = false;
1863     for (int i = 0; i < e->attributes().size(); ++i) {
1864         const QInputMethodEvent::Attribute &a = e->attributes().at(i);
1865         if (a.type == QInputMethodEvent::Cursor) {
1866             preeditCursor = a.start;
1867             hideCursor = !a.length;
1868         } else if (a.type == QInputMethodEvent::TextFormat) {
1869             QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat();
1870             if (f.isValid()) {
1871                 QTextLayout::FormatRange o;
1872                 o.start = a.start + cursor.position() - block.position();
1873                 o.length = a.length;
1874                 o.format = f;
1875                 overrides.append(o);
1876             }
1877         }
1878     }
1879     layout->setAdditionalFormats(overrides);
1880     tentativeCommit = e->tentativeCommitString();
1881
1882     cursor.endEditBlock();
1883
1884     QTextCursorPrivate *cursor_d = QTextCursorPrivate::getPrivate(&cursor);
1885     if (cursor_d)
1886         cursor_d->setX();
1887     if (oldPreeditCursor != preeditCursor)
1888         emit q->microFocusChanged();
1889     selectionChanged(forceSelectionChanged);
1890 }
1891
1892 QVariant QQuickTextControl::inputMethodQuery(Qt::InputMethodQuery property) const
1893 {
1894     Q_D(const QQuickTextControl);
1895     QTextBlock block = d->cursor.block();
1896     switch (property) {
1897     case Qt::ImCursorRectangle:
1898         return cursorRect();
1899     case Qt::ImFont:
1900         return QVariant(d->cursor.charFormat().font());
1901     case Qt::ImCursorPosition:
1902         return QVariant(d->cursor.position() - block.position());
1903     case Qt::ImSurroundingText:
1904         return QVariant(block.text());
1905     case Qt::ImCurrentSelection:
1906         return QVariant(d->cursor.selectedText());
1907     case Qt::ImMaximumTextLength:
1908         return QVariant(); // No limit.
1909     case Qt::ImAnchorPosition:
1910         return QVariant(d->cursor.anchor() - block.position());
1911     default:
1912         return QVariant();
1913     }
1914 }
1915
1916 void QQuickTextControl::setFocus(bool focus, Qt::FocusReason reason)
1917 {
1918     QFocusEvent ev(focus ? QEvent::FocusIn : QEvent::FocusOut,
1919                    reason);
1920     processEvent(&ev);
1921 }
1922
1923 void QQuickTextControlPrivate::focusEvent(QFocusEvent *e)
1924 {
1925     Q_Q(QQuickTextControl);
1926     emit q->updateRequest(q->selectionRect());
1927     if (e->gotFocus()) {
1928 #ifdef QT_KEYPAD_NAVIGATION
1929         if (!QGuiApplication::keypadNavigationEnabled() || (hasEditFocus && (e->reason() == Qt::PopupFocusReason
1930             ))) {
1931 #endif
1932             setBlinkingCursorEnabled(interactionFlags & (Qt::TextEditable | Qt::TextSelectableByKeyboard));
1933 #ifdef QT_KEYPAD_NAVIGATION
1934         }
1935 #endif
1936     } else {
1937         commitPreedit();
1938         setBlinkingCursorEnabled(false);
1939
1940         if (cursorIsFocusIndicator
1941             && e->reason() != Qt::ActiveWindowFocusReason
1942             && e->reason() != Qt::PopupFocusReason
1943             && cursor.hasSelection()) {
1944             cursor.clearSelection();
1945         }
1946     }
1947     hasFocus = e->gotFocus();
1948 }
1949
1950 QString QQuickTextControlPrivate::anchorForCursor(const QTextCursor &anchorCursor) const
1951 {
1952     if (anchorCursor.hasSelection()) {
1953         QTextCursor cursor = anchorCursor;
1954         if (cursor.selectionStart() != cursor.position())
1955             cursor.setPosition(cursor.selectionStart());
1956         cursor.movePosition(QTextCursor::NextCharacter);
1957         QTextCharFormat fmt = cursor.charFormat();
1958         if (fmt.isAnchor() && fmt.hasProperty(QTextFormat::AnchorHref))
1959             return fmt.stringProperty(QTextFormat::AnchorHref);
1960     }
1961     return QString();
1962 }
1963
1964 #ifdef QT_KEYPAD_NAVIGATION
1965 void QQuickTextControlPrivate::editFocusEvent(QEvent *e)
1966 {
1967     Q_Q(QQuickTextControl);
1968
1969     if (QGuiApplication::keypadNavigationEnabled()) {
1970         if (e->type() == QEvent::EnterEditFocus && interactionFlags & Qt::TextEditable) {
1971             const QTextCursor oldSelection = cursor;
1972             const int oldCursorPos = cursor.position();
1973             const bool moved = cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
1974             q->ensureCursorVisible();
1975             if (moved) {
1976                 if (cursor.position() != oldCursorPos)
1977                     emit q->cursorPositionChanged();
1978                 emit q->microFocusChanged();
1979             }
1980             selectionChanged();
1981             repaintOldAndNewSelection(oldSelection);
1982
1983             setBlinkingCursorEnabled(true);
1984         } else
1985             setBlinkingCursorEnabled(false);
1986     }
1987
1988     hasEditFocus = e->type() == QEvent::EnterEditFocus ? true : false;
1989 }
1990 #endif
1991
1992 QTextCursor QQuickTextControl::cursorForPosition(const QPointF &pos) const
1993 {
1994     Q_D(const QQuickTextControl);
1995     int cursorPos = hitTest(pos, Qt::FuzzyHit);
1996     if (cursorPos == -1)
1997         cursorPos = 0;
1998     QTextCursor c(d->doc);
1999     c.setPosition(cursorPos);
2000     return c;
2001 }
2002
2003 QRectF QQuickTextControl::cursorRect(const QTextCursor &cursor) const
2004 {
2005     Q_D(const QQuickTextControl);
2006     if (cursor.isNull())
2007         return QRectF();
2008
2009     return d->rectForPosition(cursor.position());
2010 }
2011
2012 QRectF QQuickTextControl::cursorRect() const
2013 {
2014     Q_D(const QQuickTextControl);
2015     return cursorRect(d->cursor);
2016 }
2017
2018 QRectF QQuickTextControlPrivate::cursorRectPlusUnicodeDirectionMarkers(const QTextCursor &cursor) const
2019 {
2020     if (cursor.isNull())
2021         return QRectF();
2022
2023     return rectForPosition(cursor.position()).adjusted(-4, 0, 4, 0);
2024 }
2025
2026 QString QQuickTextControl::anchorAt(const QPointF &pos) const
2027 {
2028     Q_D(const QQuickTextControl);
2029     return d->doc->documentLayout()->anchorAt(pos);
2030 }
2031
2032 QString QQuickTextControl::anchorAtCursor() const
2033 {
2034     Q_D(const QQuickTextControl);
2035
2036     return d->anchorForCursor(d->cursor);
2037 }
2038
2039 bool QQuickTextControl::overwriteMode() const
2040 {
2041     Q_D(const QQuickTextControl);
2042     return d->overwriteMode;
2043 }
2044
2045 void QQuickTextControl::setOverwriteMode(bool overwrite)
2046 {
2047     Q_D(QQuickTextControl);
2048     d->overwriteMode = overwrite;
2049 }
2050
2051 int QQuickTextControl::cursorWidth() const
2052 {
2053 #ifndef QT_NO_PROPERTIES
2054     Q_D(const QQuickTextControl);
2055     return d->doc->documentLayout()->property("cursorWidth").toInt();
2056 #else
2057     return 1;
2058 #endif
2059 }
2060
2061 void QQuickTextControl::setCursorWidth(int width)
2062 {
2063     Q_D(QQuickTextControl);
2064 #ifdef QT_NO_PROPERTIES
2065     Q_UNUSED(width);
2066 #else
2067     if (width == -1)
2068         width = textCursorWidth;
2069     d->doc->documentLayout()->setProperty("cursorWidth", width);
2070 #endif
2071     d->repaintCursor();
2072 }
2073
2074 bool QQuickTextControl::acceptRichText() const
2075 {
2076     Q_D(const QQuickTextControl);
2077     return d->acceptRichText;
2078 }
2079
2080 void QQuickTextControl::setAcceptRichText(bool accept)
2081 {
2082     Q_D(QQuickTextControl);
2083     d->acceptRichText = accept;
2084 }
2085
2086 void QQuickTextControl::setExtraSelections(const QVector<QAbstractTextDocumentLayout::Selection> &selections)
2087 {
2088     Q_D(QQuickTextControl);
2089
2090     QHash<int, int> hash;
2091     for (int i = 0; i < d->extraSelections.count(); ++i) {
2092         const QAbstractTextDocumentLayout::Selection &esel = d->extraSelections.at(i);
2093         hash.insertMulti(esel.cursor.anchor(), i);
2094     }
2095
2096     for (int i = 0; i < selections.count(); ++i) {
2097         const QAbstractTextDocumentLayout::Selection &sel = selections.at(i);
2098         QHash<int, int>::iterator it = hash.find(sel.cursor.anchor());
2099         if (it != hash.end()) {
2100             const QAbstractTextDocumentLayout::Selection &esel = d->extraSelections.at(it.value());
2101             if (esel.cursor.position() == sel.cursor.position()
2102                 && esel.format == sel.format) {
2103                 hash.erase(it);
2104                 continue;
2105             }
2106         }
2107         QRectF r = selectionRect(sel.cursor);
2108         if (sel.format.boolProperty(QTextFormat::FullWidthSelection)) {
2109             r.setLeft(0);
2110             r.setWidth(qreal(INT_MAX));
2111         }
2112         emit updateRequest(r);
2113     }
2114
2115     for (QHash<int, int>::iterator it = hash.begin(); it != hash.end(); ++it) {
2116         const QAbstractTextDocumentLayout::Selection &esel = d->extraSelections.at(it.value());
2117         QRectF r = selectionRect(esel.cursor);
2118         if (esel.format.boolProperty(QTextFormat::FullWidthSelection)) {
2119             r.setLeft(0);
2120             r.setWidth(qreal(INT_MAX));
2121         }
2122         emit updateRequest(r);
2123     }
2124
2125     d->extraSelections = selections;
2126 }
2127
2128 QVector<QAbstractTextDocumentLayout::Selection> QQuickTextControl::extraSelections() const
2129 {
2130     Q_D(const QQuickTextControl);
2131     return d->extraSelections;
2132 }
2133
2134 void QQuickTextControl::setTextWidth(qreal width)
2135 {
2136     Q_D(QQuickTextControl);
2137     d->doc->setTextWidth(width);
2138 }
2139
2140 qreal QQuickTextControl::textWidth() const
2141 {
2142     Q_D(const QQuickTextControl);
2143     return d->doc->textWidth();
2144 }
2145
2146 QSizeF QQuickTextControl::size() const
2147 {
2148     Q_D(const QQuickTextControl);
2149     return d->doc->size();
2150 }
2151
2152 void QQuickTextControl::setOpenExternalLinks(bool open)
2153 {
2154     Q_D(QQuickTextControl);
2155     d->openExternalLinks = open;
2156 }
2157
2158 bool QQuickTextControl::openExternalLinks() const
2159 {
2160     Q_D(const QQuickTextControl);
2161     return d->openExternalLinks;
2162 }
2163
2164 bool QQuickTextControl::ignoreUnusedNavigationEvents() const
2165 {
2166     Q_D(const QQuickTextControl);
2167     return d->ignoreUnusedNavigationEvents;
2168 }
2169
2170 void QQuickTextControl::setIgnoreUnusedNavigationEvents(bool ignore)
2171 {
2172     Q_D(QQuickTextControl);
2173     d->ignoreUnusedNavigationEvents = ignore;
2174 }
2175
2176 void QQuickTextControl::moveCursor(QTextCursor::MoveOperation op, QTextCursor::MoveMode mode)
2177 {
2178     Q_D(QQuickTextControl);
2179     const QTextCursor oldSelection = d->cursor;
2180     const bool moved = d->cursor.movePosition(op, mode);
2181     d->_q_updateCurrentCharFormatAndSelection();
2182     ensureCursorVisible();
2183     d->repaintOldAndNewSelection(oldSelection);
2184     if (moved)
2185         emit cursorPositionChanged();
2186 }
2187
2188 bool QQuickTextControl::canPaste() const
2189 {
2190 #ifndef QT_NO_CLIPBOARD
2191     Q_D(const QQuickTextControl);
2192     if (d->interactionFlags & Qt::TextEditable) {
2193         const QMimeData *md = QGuiApplication::clipboard()->mimeData();
2194         return md && canInsertFromMimeData(md);
2195     }
2196 #endif
2197     return false;
2198 }
2199
2200 void QQuickTextControl::setCursorIsFocusIndicator(bool b)
2201 {
2202     Q_D(QQuickTextControl);
2203     d->cursorIsFocusIndicator = b;
2204     d->repaintCursor();
2205 }
2206
2207 bool QQuickTextControl::cursorIsFocusIndicator() const
2208 {
2209     Q_D(const QQuickTextControl);
2210     return d->cursorIsFocusIndicator;
2211 }
2212
2213
2214 void QQuickTextControl::setDragEnabled(bool enabled)
2215 {
2216     Q_D(QQuickTextControl);
2217     d->dragEnabled = enabled;
2218 }
2219
2220 bool QQuickTextControl::isDragEnabled() const
2221 {
2222     Q_D(const QQuickTextControl);
2223     return d->dragEnabled;
2224 }
2225
2226 void QQuickTextControl::setWordSelectionEnabled(bool enabled)
2227 {
2228     Q_D(QQuickTextControl);
2229     d->wordSelectionEnabled = enabled;
2230 }
2231
2232 bool QQuickTextControl::isWordSelectionEnabled() const
2233 {
2234     Q_D(const QQuickTextControl);
2235     return d->wordSelectionEnabled;
2236 }
2237
2238 QMimeData *QQuickTextControl::createMimeDataFromSelection() const
2239 {
2240     Q_D(const QQuickTextControl);
2241     const QTextDocumentFragment fragment(d->cursor);
2242     return new QQuickTextEditMimeData(fragment);
2243 }
2244
2245 bool QQuickTextControl::canInsertFromMimeData(const QMimeData *source) const
2246 {
2247     Q_D(const QQuickTextControl);
2248     if (d->acceptRichText)
2249         return (source->hasText() && !source->text().isEmpty())
2250             || source->hasHtml()
2251             || source->hasFormat(QLatin1String("application/x-qrichtext"))
2252             || source->hasFormat(QLatin1String("application/x-qt-richtext"));
2253     else
2254         return source->hasText() && !source->text().isEmpty();
2255 }
2256
2257 void QQuickTextControl::insertFromMimeData(const QMimeData *source)
2258 {
2259     Q_D(QQuickTextControl);
2260     if (!(d->interactionFlags & Qt::TextEditable) || !source)
2261         return;
2262
2263     bool hasData = false;
2264     QTextDocumentFragment fragment;
2265 #ifndef QT_NO_TEXTHTMLPARSER
2266     if (source->hasFormat(QLatin1String("application/x-qrichtext")) && d->acceptRichText) {
2267         // x-qrichtext is always UTF-8 (taken from Qt3 since we don't use it anymore).
2268         QString richtext = QString::fromUtf8(source->data(QLatin1String("application/x-qrichtext")));
2269         richtext.prepend(QLatin1String("<meta name=\"qrichtext\" content=\"1\" />"));
2270         fragment = QTextDocumentFragment::fromHtml(richtext, d->doc);
2271         hasData = true;
2272     } else if (source->hasHtml() && d->acceptRichText) {
2273         fragment = QTextDocumentFragment::fromHtml(source->html(), d->doc);
2274         hasData = true;
2275     } else {
2276         QString text = source->text();
2277         if (!text.isNull()) {
2278             fragment = QTextDocumentFragment::fromPlainText(text);
2279             hasData = true;
2280         }
2281     }
2282 #else
2283     fragment = QTextDocumentFragment::fromPlainText(source->text());
2284 #endif // QT_NO_TEXTHTMLPARSER
2285
2286     if (hasData)
2287         d->cursor.insertFragment(fragment);
2288     ensureCursorVisible();
2289 }
2290
2291 bool QQuickTextControl::findNextPrevAnchor(const QTextCursor &startCursor, bool next, QTextCursor &newAnchor)
2292 {
2293     Q_D(QQuickTextControl);
2294
2295     int anchorStart = -1;
2296     QString anchorHref;
2297     int anchorEnd = -1;
2298
2299     if (next) {
2300         const int startPos = startCursor.selectionEnd();
2301
2302         QTextBlock block = d->doc->findBlock(startPos);
2303         QTextBlock::Iterator it = block.begin();
2304
2305         while (!it.atEnd() && it.fragment().position() < startPos)
2306             ++it;
2307
2308         while (block.isValid()) {
2309             anchorStart = -1;
2310
2311             // find next anchor
2312             for (; !it.atEnd(); ++it) {
2313                 const QTextFragment fragment = it.fragment();
2314                 const QTextCharFormat fmt = fragment.charFormat();
2315
2316                 if (fmt.isAnchor() && fmt.hasProperty(QTextFormat::AnchorHref)) {
2317                     anchorStart = fragment.position();
2318                     anchorHref = fmt.anchorHref();
2319                     break;
2320                 }
2321             }
2322
2323             if (anchorStart != -1) {
2324                 anchorEnd = -1;
2325
2326                 // find next non-anchor fragment
2327                 for (; !it.atEnd(); ++it) {
2328                     const QTextFragment fragment = it.fragment();
2329                     const QTextCharFormat fmt = fragment.charFormat();
2330
2331                     if (!fmt.isAnchor() || fmt.anchorHref() != anchorHref) {
2332                         anchorEnd = fragment.position();
2333                         break;
2334                     }
2335                 }
2336
2337                 if (anchorEnd == -1)
2338                     anchorEnd = block.position() + block.length() - 1;
2339
2340                 // make found selection
2341                 break;
2342             }
2343
2344             block = block.next();
2345             it = block.begin();
2346         }
2347     } else {
2348         int startPos = startCursor.selectionStart();
2349         if (startPos > 0)
2350             --startPos;
2351
2352         QTextBlock block = d->doc->findBlock(startPos);
2353         QTextBlock::Iterator blockStart = block.begin();
2354         QTextBlock::Iterator it = block.end();
2355
2356         if (startPos == block.position()) {
2357             it = block.begin();
2358         } else {
2359             do {
2360                 if (it == blockStart) {
2361                     it = QTextBlock::Iterator();
2362                     block = QTextBlock();
2363                 } else {
2364                     --it;
2365                 }
2366             } while (!it.atEnd() && it.fragment().position() + it.fragment().length() - 1 > startPos);
2367         }
2368
2369         while (block.isValid()) {
2370             anchorStart = -1;
2371
2372             if (!it.atEnd()) {
2373                 do {
2374                     const QTextFragment fragment = it.fragment();
2375                     const QTextCharFormat fmt = fragment.charFormat();
2376
2377                     if (fmt.isAnchor() && fmt.hasProperty(QTextFormat::AnchorHref)) {
2378                         anchorStart = fragment.position() + fragment.length();
2379                         anchorHref = fmt.anchorHref();
2380                         break;
2381                     }
2382
2383                     if (it == blockStart)
2384                         it = QTextBlock::Iterator();
2385                     else
2386                         --it;
2387                 } while (!it.atEnd());
2388             }
2389
2390             if (anchorStart != -1 && !it.atEnd()) {
2391                 anchorEnd = -1;
2392
2393                 do {
2394                     const QTextFragment fragment = it.fragment();
2395                     const QTextCharFormat fmt = fragment.charFormat();
2396
2397                     if (!fmt.isAnchor() || fmt.anchorHref() != anchorHref) {
2398                         anchorEnd = fragment.position() + fragment.length();
2399                         break;
2400                     }
2401
2402                     if (it == blockStart)
2403                         it = QTextBlock::Iterator();
2404                     else
2405                         --it;
2406                 } while (!it.atEnd());
2407
2408                 if (anchorEnd == -1)
2409                     anchorEnd = qMax(0, block.position());
2410
2411                 break;
2412             }
2413
2414             block = block.previous();
2415             it = block.end();
2416             if (it != block.begin())
2417                 --it;
2418             blockStart = block.begin();
2419         }
2420
2421     }
2422
2423     if (anchorStart != -1 && anchorEnd != -1) {
2424         newAnchor = d->cursor;
2425         newAnchor.setPosition(anchorStart);
2426         newAnchor.setPosition(anchorEnd, QTextCursor::KeepAnchor);
2427         return true;
2428     }
2429
2430     return false;
2431 }
2432
2433 void QQuickTextControlPrivate::activateLinkUnderCursor(QString href)
2434 {
2435     QTextCursor oldCursor = cursor;
2436
2437     if (href.isEmpty()) {
2438         QTextCursor tmp = cursor;
2439         if (tmp.selectionStart() != tmp.position())
2440             tmp.setPosition(tmp.selectionStart());
2441         tmp.movePosition(QTextCursor::NextCharacter);
2442         href = tmp.charFormat().anchorHref();
2443     }
2444     if (href.isEmpty())
2445         return;
2446
2447     if (!cursor.hasSelection()) {
2448         QTextBlock block = cursor.block();
2449         const int cursorPos = cursor.position();
2450
2451         QTextBlock::Iterator it = block.begin();
2452         QTextBlock::Iterator linkFragment;
2453
2454         for (; !it.atEnd(); ++it) {
2455             QTextFragment fragment = it.fragment();
2456             const int fragmentPos = fragment.position();
2457             if (fragmentPos <= cursorPos &&
2458                 fragmentPos + fragment.length() > cursorPos) {
2459                 linkFragment = it;
2460                 break;
2461             }
2462         }
2463
2464         if (!linkFragment.atEnd()) {
2465             it = linkFragment;
2466             cursor.setPosition(it.fragment().position());
2467             if (it != block.begin()) {
2468                 do {
2469                     --it;
2470                     QTextFragment fragment = it.fragment();
2471                     if (fragment.charFormat().anchorHref() != href)
2472                         break;
2473                     cursor.setPosition(fragment.position());
2474                 } while (it != block.begin());
2475             }
2476
2477             for (it = linkFragment; !it.atEnd(); ++it) {
2478                 QTextFragment fragment = it.fragment();
2479                 if (fragment.charFormat().anchorHref() != href)
2480                     break;
2481                 cursor.setPosition(fragment.position() + fragment.length(), QTextCursor::KeepAnchor);
2482             }
2483         }
2484     }
2485
2486     if (hasFocus) {
2487         cursorIsFocusIndicator = true;
2488     } else {
2489         cursorIsFocusIndicator = false;
2490         cursor.clearSelection();
2491     }
2492     repaintOldAndNewSelection(oldCursor);
2493
2494 #if 0 // ###ndef QT_NO_DESKTOPSERVICES
2495     if (openExternalLinks)
2496         QDesktopServices::openUrl(href);
2497     else
2498 #endif
2499         emit q_func()->linkActivated(href);
2500 }
2501
2502 bool QQuickTextControlPrivate::isPreediting() const
2503 {
2504     QTextLayout *layout = cursor.block().layout();
2505     if (layout && !layout->preeditAreaText().isEmpty())
2506         return true;
2507
2508     return false;
2509 }
2510
2511 void QQuickTextControlPrivate::commitPreedit()
2512 {
2513     if (!isPreediting())
2514         return;
2515
2516     cursor.beginEditBlock();
2517     qApp->inputPanel()->reset();
2518
2519     if (!tentativeCommit.isEmpty()) {
2520         cursor.insertText(tentativeCommit);
2521         tentativeCommit.clear();
2522     }
2523
2524     preeditCursor = 0;
2525     QTextBlock block = cursor.block();
2526     QTextLayout *layout = block.layout();
2527     layout->setPreeditArea(-1, QString());
2528     layout->clearAdditionalFormats();
2529     cursor.endEditBlock();
2530 }
2531
2532 bool QQuickTextControl::setFocusToNextOrPreviousAnchor(bool next)
2533 {
2534     Q_D(QQuickTextControl);
2535
2536     if (!(d->interactionFlags & Qt::LinksAccessibleByKeyboard))
2537         return false;
2538
2539     QRectF crect = selectionRect();
2540     emit updateRequest(crect);
2541
2542     // If we don't have a current anchor, we start from the start/end
2543     if (!d->cursor.hasSelection()) {
2544         d->cursor = QTextCursor(d->doc);
2545         if (next)
2546             d->cursor.movePosition(QTextCursor::Start);
2547         else
2548             d->cursor.movePosition(QTextCursor::End);
2549     }
2550
2551     QTextCursor newAnchor;
2552     if (findNextPrevAnchor(d->cursor, next, newAnchor)) {
2553         d->cursor = newAnchor;
2554         d->cursorIsFocusIndicator = true;
2555     } else {
2556         d->cursor.clearSelection();
2557     }
2558
2559     if (d->cursor.hasSelection()) {
2560         crect = selectionRect();
2561         emit updateRequest(crect);
2562         emit visibilityRequest(crect);
2563         return true;
2564     } else {
2565         return false;
2566     }
2567 }
2568
2569 bool QQuickTextControl::setFocusToAnchor(const QTextCursor &newCursor)
2570 {
2571     Q_D(QQuickTextControl);
2572
2573     if (!(d->interactionFlags & Qt::LinksAccessibleByKeyboard))
2574         return false;
2575
2576     // Verify that this is an anchor.
2577     const QString anchorHref = d->anchorForCursor(newCursor);
2578     if (anchorHref.isEmpty())
2579         return false;
2580
2581     // and process it
2582     QRectF crect = selectionRect();
2583     emit updateRequest(crect);
2584
2585     d->cursor.setPosition(newCursor.selectionStart());
2586     d->cursor.setPosition(newCursor.selectionEnd(), QTextCursor::KeepAnchor);
2587     d->cursorIsFocusIndicator = true;
2588
2589     crect = selectionRect();
2590     emit updateRequest(crect);
2591     emit visibilityRequest(crect);
2592     return true;
2593 }
2594
2595 void QQuickTextControl::setTextInteractionFlags(Qt::TextInteractionFlags flags)
2596 {
2597     Q_D(QQuickTextControl);
2598     if (flags == d->interactionFlags)
2599         return;
2600     d->interactionFlags = flags;
2601
2602     if (d->hasFocus)
2603         d->setBlinkingCursorEnabled(flags & (Qt::TextEditable | Qt::TextSelectableByKeyboard));
2604 }
2605
2606 Qt::TextInteractionFlags QQuickTextControl::textInteractionFlags() const
2607 {
2608     Q_D(const QQuickTextControl);
2609     return d->interactionFlags;
2610 }
2611
2612 void QQuickTextControl::mergeCurrentCharFormat(const QTextCharFormat &modifier)
2613 {
2614     Q_D(QQuickTextControl);
2615     d->cursor.mergeCharFormat(modifier);
2616     d->updateCurrentCharFormat();
2617 }
2618
2619 void QQuickTextControl::setCurrentCharFormat(const QTextCharFormat &format)
2620 {
2621     Q_D(QQuickTextControl);
2622     d->cursor.setCharFormat(format);
2623     d->updateCurrentCharFormat();
2624 }
2625
2626 QTextCharFormat QQuickTextControl::currentCharFormat() const
2627 {
2628     Q_D(const QQuickTextControl);
2629     return d->cursor.charFormat();
2630 }
2631
2632 void QQuickTextControl::insertPlainText(const QString &text)
2633 {
2634     Q_D(QQuickTextControl);
2635     d->cursor.insertText(text);
2636 }
2637
2638 #ifndef QT_NO_TEXTHTMLPARSER
2639 void QQuickTextControl::insertHtml(const QString &text)
2640 {
2641     Q_D(QQuickTextControl);
2642     d->cursor.insertHtml(text);
2643 }
2644 #endif // QT_NO_TEXTHTMLPARSER
2645
2646 QPointF QQuickTextControl::anchorPosition(const QString &name) const
2647 {
2648     Q_D(const QQuickTextControl);
2649     if (name.isEmpty())
2650         return QPointF();
2651
2652     QRectF r;
2653     for (QTextBlock block = d->doc->begin(); block.isValid(); block = block.next()) {
2654         QTextCharFormat format = block.charFormat();
2655         if (format.isAnchor() && format.anchorNames().contains(name)) {
2656             r = d->rectForPosition(block.position());
2657             break;
2658         }
2659
2660         for (QTextBlock::Iterator it = block.begin(); !it.atEnd(); ++it) {
2661             QTextFragment fragment = it.fragment();
2662             format = fragment.charFormat();
2663             if (format.isAnchor() && format.anchorNames().contains(name)) {
2664                 r = d->rectForPosition(fragment.position());
2665                 block = QTextBlock();
2666                 break;
2667             }
2668         }
2669     }
2670     if (!r.isValid())
2671         return QPointF();
2672     return QPointF(0, r.top());
2673 }
2674
2675 void QQuickTextControl::adjustSize()
2676 {
2677     Q_D(QQuickTextControl);
2678     d->doc->adjustSize();
2679 }
2680
2681 bool QQuickTextControl::find(const QString &exp, QTextDocument::FindFlags options)
2682 {
2683     Q_D(QQuickTextControl);
2684     QTextCursor search = d->doc->find(exp, d->cursor, options);
2685     if (search.isNull())
2686         return false;
2687
2688     setTextCursor(search);
2689     return true;
2690 }
2691
2692
2693
2694 void QQuickTextControlPrivate::append(const QString &text, Qt::TextFormat format)
2695 {
2696     QTextCursor tmp(doc);
2697     tmp.beginEditBlock();
2698     tmp.movePosition(QTextCursor::End);
2699
2700     if (!doc->isEmpty())
2701         tmp.insertBlock(cursor.blockFormat(), cursor.charFormat());
2702     else
2703         tmp.setCharFormat(cursor.charFormat());
2704
2705     // preserve the char format
2706     QTextCharFormat oldCharFormat = cursor.charFormat();
2707
2708 #ifndef QT_NO_TEXTHTMLPARSER
2709     if (format == Qt::RichText || (format == Qt::AutoText && Qt::mightBeRichText(text))) {
2710         tmp.insertHtml(text);
2711     } else {
2712         tmp.insertText(text);
2713     }
2714 #else
2715     tmp.insertText(text);
2716 #endif // QT_NO_TEXTHTMLPARSER
2717     if (!cursor.hasSelection())
2718         cursor.setCharFormat(oldCharFormat);
2719
2720     tmp.endEditBlock();
2721 }
2722
2723 void QQuickTextControl::append(const QString &text)
2724 {
2725     Q_D(QQuickTextControl);
2726     d->append(text, Qt::AutoText);
2727 }
2728
2729 void QQuickTextControl::appendHtml(const QString &html)
2730 {
2731     Q_D(QQuickTextControl);
2732     d->append(html, Qt::RichText);
2733 }
2734
2735 void QQuickTextControl::appendPlainText(const QString &text)
2736 {
2737     Q_D(QQuickTextControl);
2738     d->append(text, Qt::PlainText);
2739 }
2740
2741 QString QQuickTextControl::toPlainText() const
2742 {
2743     Q_D(const QQuickTextControl);
2744     QString plainText = document()->toPlainText();
2745     if (!d->tentativeCommit.isEmpty())
2746         plainText.insert(textCursor().position(), d->tentativeCommit);
2747     return plainText;
2748 }
2749
2750 #ifndef QT_NO_TEXTHTMLPARSER
2751 QString QQuickTextControl::toHtml() const
2752 {
2753     // note: currently not including tentative commit
2754     return document()->toHtml();
2755 }
2756 #endif
2757
2758 void QQuickTextControl::ensureCursorVisible()
2759 {
2760     Q_D(QQuickTextControl);
2761     QRectF crect = d->rectForPosition(d->cursor.position()).adjusted(-5, 0, 5, 0);
2762     emit visibilityRequest(crect);
2763     emit microFocusChanged();
2764 }
2765
2766 QPalette QQuickTextControl::palette() const
2767 {
2768     Q_D(const QQuickTextControl);
2769     return d->palette;
2770 }
2771
2772 void QQuickTextControl::setPalette(const QPalette &pal)
2773 {
2774     Q_D(QQuickTextControl);
2775     d->palette = pal;
2776 }
2777
2778 bool QQuickTextControl::cursorOn() const
2779 {
2780     Q_D(const QQuickTextControl);
2781     return d->cursorOn;
2782 }
2783
2784 QAbstractTextDocumentLayout::PaintContext QQuickTextControl::getPaintContext() const
2785 {
2786     Q_D(const QQuickTextControl);
2787
2788     QAbstractTextDocumentLayout::PaintContext ctx;
2789
2790     ctx.selections = d->extraSelections;
2791     ctx.palette = d->palette;
2792     if (d->cursorOn && d->isEnabled) {
2793         if (d->hideCursor)
2794             ctx.cursorPosition = -1;
2795         else if (d->preeditCursor != 0)
2796             ctx.cursorPosition = - (d->preeditCursor + 2);
2797         else
2798             ctx.cursorPosition = d->cursor.position();
2799     }
2800
2801     if (!d->dndFeedbackCursor.isNull())
2802         ctx.cursorPosition = d->dndFeedbackCursor.position();
2803 #ifdef QT_KEYPAD_NAVIGATION
2804     if (!QGuiApplication::keypadNavigationEnabled() || d->hasEditFocus)
2805 #endif
2806     if (d->cursor.hasSelection()) {
2807         QAbstractTextDocumentLayout::Selection selection;
2808         selection.cursor = d->cursor;
2809         if (0 && d->cursorIsFocusIndicator) {
2810 #if 0
2811             // ###
2812             QStyleOption opt;
2813             opt.palette = ctx.palette;
2814             QStyleHintReturnVariant ret;
2815             QStyle *style = QGuiApplication::style();
2816             if (widget)
2817                 style = widget->style();
2818             style->styleHint(QStyle::SH_TextControl_FocusIndicatorTextCharFormat, &opt, widget, &ret);
2819             selection.format = qvariant_cast<QTextFormat>(ret.variant).toCharFormat();
2820 #endif
2821         } else {
2822             QPalette::ColorGroup cg = d->hasFocus ? QPalette::Active : QPalette::Inactive;
2823             selection.format.setBackground(ctx.palette.brush(cg, QPalette::Highlight));
2824             selection.format.setForeground(ctx.palette.brush(cg, QPalette::HighlightedText));
2825             if (fullWidthSelection)
2826                 selection.format.setProperty(QTextFormat::FullWidthSelection, true);
2827         }
2828         ctx.selections.append(selection);
2829     }
2830
2831     return ctx;
2832 }
2833
2834 void QQuickTextControl::drawContents(QPainter *p, const QRectF &rect)
2835 {
2836     Q_D(QQuickTextControl);
2837     p->save();
2838     QAbstractTextDocumentLayout::PaintContext ctx = getPaintContext();
2839     if (rect.isValid())
2840         p->setClipRect(rect, Qt::IntersectClip);
2841     ctx.clip = rect;
2842
2843     d->doc->documentLayout()->draw(p, ctx);
2844     p->restore();
2845 }
2846
2847 void QQuickTextControlPrivate::_q_copyLink()
2848 {
2849 #ifndef QT_NO_CLIPBOARD
2850     QMimeData *md = new QMimeData;
2851     md->setText(linkToCopy);
2852     QGuiApplication::clipboard()->setMimeData(md);
2853 #endif
2854 }
2855
2856 int QQuickTextControl::hitTest(const QPointF &point, Qt::HitTestAccuracy accuracy) const
2857 {
2858     Q_D(const QQuickTextControl);
2859     return d->doc->documentLayout()->hitTest(point, accuracy);
2860 }
2861
2862 QRectF QQuickTextControl::blockBoundingRect(const QTextBlock &block) const
2863 {
2864     Q_D(const QQuickTextControl);
2865     return d->doc->documentLayout()->blockBoundingRect(block);
2866 }
2867
2868
2869
2870 QStringList QQuickTextEditMimeData::formats() const
2871 {
2872     if (!fragment.isEmpty())
2873         return QStringList() << QString::fromLatin1("text/plain") << QString::fromLatin1("text/html")
2874 #ifndef QT_NO_TEXTODFWRITER
2875             << QString::fromLatin1("application/vnd.oasis.opendocument.text")
2876 #endif
2877         ;
2878     else
2879         return QMimeData::formats();
2880 }
2881
2882 QVariant QQuickTextEditMimeData::retrieveData(const QString &mimeType, QVariant::Type type) const
2883 {
2884     if (!fragment.isEmpty())
2885         setup();
2886     return QMimeData::retrieveData(mimeType, type);
2887 }
2888
2889 void QQuickTextEditMimeData::setup() const
2890 {
2891     QQuickTextEditMimeData *that = const_cast<QQuickTextEditMimeData *>(this);
2892 #ifndef QT_NO_TEXTHTMLPARSER
2893     that->setData(QLatin1String("text/html"), fragment.toHtml("utf-8").toUtf8());
2894 #endif
2895 #ifndef QT_NO_TEXTODFWRITER
2896     {
2897         QBuffer buffer;
2898         QTextDocumentWriter writer(&buffer, "ODF");
2899         writer.write(fragment);
2900         buffer.close();
2901         that->setData(QLatin1String("application/vnd.oasis.opendocument.text"), buffer.data());
2902     }
2903 #endif
2904     that->setText(fragment.toPlainText());
2905     fragment = QTextDocumentFragment();
2906 }
2907
2908
2909 QT_END_NAMESPACE
2910
2911 #include "moc_qquicktextcontrol_p.cpp"
2912
2913 #endif // QT_NO_TEXTCONTROL