81330878ddbab48228a6415a381aa74d37859db4
[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     if (sendMouseEventToInputContext(
1417             e, QEvent::MouseButtonPress, button, pos, modifiers, buttons, globalPos)) {
1418         return;
1419     }
1420
1421     if (interactionFlags & Qt::LinksAccessibleByMouse) {
1422         anchorOnMousePress = q->anchorAt(pos);
1423
1424         if (cursorIsFocusIndicator) {
1425             cursorIsFocusIndicator = false;
1426             repaintSelection();
1427             cursor.clearSelection();
1428         }
1429     }
1430     if (!(button & Qt::LeftButton) ||
1431         !((interactionFlags & Qt::TextSelectableByMouse) || (interactionFlags & Qt::TextEditable))) {
1432             e->ignore();
1433             return;
1434     }
1435
1436     cursorIsFocusIndicator = false;
1437     const QTextCursor oldSelection = cursor;
1438     const int oldCursorPos = cursor.position();
1439
1440     mousePressed = (interactionFlags & Qt::TextSelectableByMouse);
1441 #ifndef QT_NO_DRAGANDDROP
1442     mightStartDrag = false;
1443 #endif
1444
1445     if (trippleClickTimer.isActive()
1446         && ((pos - trippleClickPoint).toPoint().manhattanLength() < qApp->styleHints()->startDragDistance())) {
1447
1448         cursor.movePosition(QTextCursor::StartOfBlock);
1449         cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
1450         cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
1451         selectedBlockOnTrippleClick = cursor;
1452
1453         anchorOnMousePress = QString();
1454
1455         trippleClickTimer.stop();
1456     } else {
1457         int cursorPos = q->hitTest(pos, Qt::FuzzyHit);
1458         if (cursorPos == -1) {
1459             e->ignore();
1460             return;
1461         }
1462
1463         if (modifiers == Qt::ShiftModifier && (interactionFlags & Qt::TextSelectableByMouse)) {
1464             if (wordSelectionEnabled && !selectedWordOnDoubleClick.hasSelection()) {
1465                 selectedWordOnDoubleClick = cursor;
1466                 selectedWordOnDoubleClick.select(QTextCursor::WordUnderCursor);
1467             }
1468
1469             if (selectedBlockOnTrippleClick.hasSelection())
1470                 extendBlockwiseSelection(cursorPos);
1471             else if (selectedWordOnDoubleClick.hasSelection())
1472                 extendWordwiseSelection(cursorPos, pos.x());
1473             else if (!wordSelectionEnabled)
1474                 setCursorPosition(cursorPos, QTextCursor::KeepAnchor);
1475         } else {
1476
1477             if (dragEnabled
1478                 && cursor.hasSelection()
1479                 && !cursorIsFocusIndicator
1480                 && cursorPos >= cursor.selectionStart()
1481                 && cursorPos <= cursor.selectionEnd()
1482                 && q->hitTest(pos, Qt::ExactHit) != -1) {
1483 #ifndef QT_NO_DRAGANDDROP
1484                 mightStartDrag = true;
1485                 dragStartPos = pos.toPoint();
1486 #endif
1487                 return;
1488             }
1489
1490             setCursorPosition(cursorPos);
1491         }
1492     }
1493
1494     if (interactionFlags & Qt::TextEditable) {
1495         q->ensureCursorVisible();
1496         if (cursor.position() != oldCursorPos)
1497             emit q->cursorPositionChanged();
1498         _q_updateCurrentCharFormatAndSelection();
1499     } else {
1500         if (cursor.position() != oldCursorPos) {
1501             emit q->cursorPositionChanged();
1502             emit q->microFocusChanged();
1503         }
1504         selectionChanged();
1505     }
1506     repaintOldAndNewSelection(oldSelection);
1507     hadSelectionOnMousePress = cursor.hasSelection();
1508 }
1509
1510 void QQuickTextControlPrivate::mouseMoveEvent(QEvent *e, Qt::MouseButton button, const QPointF &mousePos, Qt::KeyboardModifiers modifiers,
1511                                          Qt::MouseButtons buttons, const QPoint &globalPos)
1512 {
1513     Q_Q(QQuickTextControl);
1514
1515     if (sendMouseEventToInputContext(
1516             e, QEvent::MouseMove, button, mousePos, modifiers, buttons, globalPos)) {
1517         return;
1518     }
1519
1520     if (interactionFlags & Qt::LinksAccessibleByMouse) {
1521         QString anchor = q->anchorAt(mousePos);
1522         if (anchor != highlightedAnchor) {
1523             highlightedAnchor = anchor;
1524             emit q->linkHovered(anchor);
1525         }
1526     }
1527
1528     if (!(buttons & Qt::LeftButton))
1529         return;
1530
1531     const bool editable = interactionFlags & Qt::TextEditable;
1532
1533     if (!(mousePressed
1534           || editable
1535           || mightStartDrag
1536           || selectedWordOnDoubleClick.hasSelection()
1537           || selectedBlockOnTrippleClick.hasSelection()))
1538         return;
1539
1540     const QTextCursor oldSelection = cursor;
1541     const int oldCursorPos = cursor.position();
1542
1543     if (mightStartDrag) {
1544         if ((mousePos.toPoint() - dragStartPos).manhattanLength() > qApp->styleHints()->startDragDistance())
1545             startDrag();
1546         return;
1547     }
1548
1549     if (!mousePressed)
1550         return;
1551
1552     const qreal mouseX = qreal(mousePos.x());
1553
1554     int newCursorPos = q->hitTest(mousePos, Qt::FuzzyHit);
1555     if (newCursorPos == -1)
1556         return;
1557
1558     if (wordSelectionEnabled && !selectedWordOnDoubleClick.hasSelection()) {
1559         selectedWordOnDoubleClick = cursor;
1560         selectedWordOnDoubleClick.select(QTextCursor::WordUnderCursor);
1561     }
1562
1563     if (selectedBlockOnTrippleClick.hasSelection())
1564         extendBlockwiseSelection(newCursorPos);
1565     else if (selectedWordOnDoubleClick.hasSelection())
1566         extendWordwiseSelection(newCursorPos, mouseX);
1567     else
1568         setCursorPosition(newCursorPos, QTextCursor::KeepAnchor);
1569
1570     if (interactionFlags & Qt::TextEditable) {
1571         // don't call ensureVisible for the visible cursor to avoid jumping
1572         // scrollbars. the autoscrolling ensures smooth scrolling if necessary.
1573         //q->ensureCursorVisible();
1574         if (cursor.position() != oldCursorPos)
1575             emit q->cursorPositionChanged();
1576         _q_updateCurrentCharFormatAndSelection();
1577         if (qGuiApp)
1578             qGuiApp->inputPanel()->update(Qt::ImQueryInput);
1579     } else {
1580         //emit q->visibilityRequest(QRectF(mousePos, QSizeF(1, 1)));
1581         if (cursor.position() != oldCursorPos) {
1582             emit q->cursorPositionChanged();
1583             emit q->microFocusChanged();
1584         }
1585     }
1586     selectionChanged(true);
1587     repaintOldAndNewSelection(oldSelection);
1588 }
1589
1590 void QQuickTextControlPrivate::mouseReleaseEvent(QEvent *e, Qt::MouseButton button, const QPointF &pos, Qt::KeyboardModifiers modifiers,
1591                                             Qt::MouseButtons buttons, const QPoint &globalPos)
1592 {
1593     Q_Q(QQuickTextControl);
1594
1595     if (sendMouseEventToInputContext(
1596             e, QEvent::MouseButtonRelease, button, pos, modifiers, buttons, globalPos)) {
1597         return;
1598     }
1599
1600     const QTextCursor oldSelection = cursor;
1601     const int oldCursorPos = cursor.position();
1602
1603 #ifndef QT_NO_DRAGANDDROP
1604     if (mightStartDrag && (button & Qt::LeftButton)) {
1605         mousePressed = false;
1606         setCursorPosition(pos);
1607         cursor.clearSelection();
1608         selectionChanged();
1609     }
1610 #endif
1611     if (mousePressed) {
1612         mousePressed = false;
1613 #ifndef QT_NO_CLIPBOARD
1614         setClipboardSelection();
1615         selectionChanged(true);
1616     } else if (button == Qt::MidButton
1617                && (interactionFlags & Qt::TextEditable)
1618                && QGuiApplication::clipboard()->supportsSelection()) {
1619         setCursorPosition(pos);
1620         const QMimeData *md = QGuiApplication::clipboard()->mimeData(QClipboard::Selection);
1621         if (md)
1622             q->insertFromMimeData(md);
1623 #endif
1624     }
1625
1626     repaintOldAndNewSelection(oldSelection);
1627
1628     if (cursor.position() != oldCursorPos) {
1629         emit q->cursorPositionChanged();
1630         emit q->microFocusChanged();
1631     }
1632
1633     if (interactionFlags & Qt::LinksAccessibleByMouse) {
1634         if (!(button & Qt::LeftButton))
1635             return;
1636
1637         const QString anchor = q->anchorAt(pos);
1638
1639         if (anchor.isEmpty())
1640             return;
1641
1642         if (!cursor.hasSelection()
1643             || (anchor == anchorOnMousePress && hadSelectionOnMousePress)) {
1644
1645             const int anchorPos = q->hitTest(pos, Qt::ExactHit);
1646             if (anchorPos != -1) {
1647                 cursor.setPosition(anchorPos);
1648
1649                 QString anchor = anchorOnMousePress;
1650                 anchorOnMousePress = QString();
1651                 activateLinkUnderCursor(anchor);
1652             }
1653         }
1654     }
1655 }
1656
1657 void QQuickTextControlPrivate::mouseDoubleClickEvent(QEvent *e, Qt::MouseButton button, const QPointF &pos, Qt::KeyboardModifiers modifiers,
1658                                                 Qt::MouseButtons buttons, const QPoint &globalPos)
1659 {
1660     Q_Q(QQuickTextControl);
1661
1662     if (sendMouseEventToInputContext(
1663             e, QEvent::MouseButtonDblClick, button, pos, modifiers, buttons, globalPos)) {
1664         return;
1665     }
1666
1667     if (button != Qt::LeftButton
1668         || !(interactionFlags & Qt::TextSelectableByMouse)) {
1669         e->ignore();
1670         return;
1671     }
1672
1673 #ifndef QT_NO_DRAGANDDROP
1674     mightStartDrag = false;
1675 #endif
1676     const QTextCursor oldSelection = cursor;
1677     setCursorPosition(pos);
1678     QTextLine line = currentTextLine(cursor);
1679     bool doEmit = false;
1680     if (line.isValid() && line.textLength()) {
1681         cursor.select(QTextCursor::WordUnderCursor);
1682         doEmit = true;
1683     }
1684     repaintOldAndNewSelection(oldSelection);
1685
1686     cursorIsFocusIndicator = false;
1687     selectedWordOnDoubleClick = cursor;
1688
1689     trippleClickPoint = pos;
1690     trippleClickTimer.start(qApp->styleHints()->mouseDoubleClickInterval(), q);
1691     if (doEmit) {
1692         selectionChanged();
1693 #ifndef QT_NO_CLIPBOARD
1694         setClipboardSelection();
1695 #endif
1696         emit q->cursorPositionChanged();
1697     }
1698 }
1699
1700 bool QQuickTextControlPrivate::sendMouseEventToInputContext(
1701         QEvent *e, QEvent::Type eventType, Qt::MouseButton button, const QPointF &pos,
1702         Qt::KeyboardModifiers modifiers, Qt::MouseButtons buttons, const QPoint &globalPos)
1703 {
1704 #if 0 // ### !defined(QT_NO_IM)
1705     Q_Q(QQuickTextControl);
1706
1707     QTextLayout *layout = cursor.block().layout();
1708     if (contextObject && layout && !layout->preeditAreaText().isEmpty()) {
1709         QInputContext *ctx = inputContext();
1710         int cursorPos = q->hitTest(pos, Qt::FuzzyHit) - cursor.position();
1711
1712         if (cursorPos < 0 || cursorPos > layout->preeditAreaText().length()) {
1713             cursorPos = -1;
1714             // don't send move events outside the preedit area
1715             if (eventType == QEvent::MouseMove)
1716                 return true;
1717         }
1718         if (ctx) {
1719             QMouseEvent ev(eventType, contextObject->mapFromGlobal(globalPos), globalPos,
1720                            button, buttons, modifiers);
1721             ctx->mouseHandler(cursorPos, &ev);
1722             e->setAccepted(ev.isAccepted());
1723         }
1724         if (!layout->preeditAreaText().isEmpty())
1725             return true;
1726     }
1727 #else
1728     Q_UNUSED(e);
1729     Q_UNUSED(eventType);
1730     Q_UNUSED(button);
1731     Q_UNUSED(pos);
1732     Q_UNUSED(modifiers);
1733     Q_UNUSED(buttons);
1734     Q_UNUSED(globalPos);
1735 #endif
1736     return false;
1737 }
1738
1739 bool QQuickTextControlPrivate::dragEnterEvent(QEvent *e, const QMimeData *mimeData)
1740 {
1741     Q_Q(QQuickTextControl);
1742     if (!(interactionFlags & Qt::TextEditable) || !q->canInsertFromMimeData(mimeData)) {
1743         e->ignore();
1744         return false;
1745     }
1746
1747     dndFeedbackCursor = QTextCursor();
1748
1749     return true; // accept proposed action
1750 }
1751
1752 void QQuickTextControlPrivate::dragLeaveEvent()
1753 {
1754     Q_Q(QQuickTextControl);
1755
1756     const QRectF crect = q->cursorRect(dndFeedbackCursor);
1757     dndFeedbackCursor = QTextCursor();
1758
1759     if (crect.isValid())
1760         emit q->updateRequest(crect);
1761 }
1762
1763 bool QQuickTextControlPrivate::dragMoveEvent(QEvent *e, const QMimeData *mimeData, const QPointF &pos)
1764 {
1765     Q_Q(QQuickTextControl);
1766     if (!(interactionFlags & Qt::TextEditable) || !q->canInsertFromMimeData(mimeData)) {
1767         e->ignore();
1768         return false;
1769     }
1770
1771     const int cursorPos = q->hitTest(pos, Qt::FuzzyHit);
1772     if (cursorPos != -1) {
1773         QRectF crect = q->cursorRect(dndFeedbackCursor);
1774         if (crect.isValid())
1775             emit q->updateRequest(crect);
1776
1777         dndFeedbackCursor = cursor;
1778         dndFeedbackCursor.setPosition(cursorPos);
1779
1780         crect = q->cursorRect(dndFeedbackCursor);
1781         emit q->updateRequest(crect);
1782     }
1783
1784     return true; // accept proposed action
1785 }
1786
1787 bool QQuickTextControlPrivate::dropEvent(const QMimeData *mimeData, const QPointF &pos, Qt::DropAction dropAction, QObject *source)
1788 {
1789     Q_Q(QQuickTextControl);
1790     dndFeedbackCursor = QTextCursor();
1791
1792     if (!(interactionFlags & Qt::TextEditable) || !q->canInsertFromMimeData(mimeData))
1793         return false;
1794
1795     repaintSelection();
1796
1797     QTextCursor insertionCursor = q->cursorForPosition(pos);
1798     insertionCursor.beginEditBlock();
1799
1800     if (dropAction == Qt::MoveAction && source == contextObject)
1801         cursor.removeSelectedText();
1802
1803     cursor = insertionCursor;
1804     q->insertFromMimeData(mimeData);
1805     insertionCursor.endEditBlock();
1806     q->ensureCursorVisible();
1807     return true; // accept proposed action
1808 }
1809
1810 void QQuickTextControlPrivate::inputMethodEvent(QInputMethodEvent *e)
1811 {
1812     Q_Q(QQuickTextControl);
1813     if (!(interactionFlags & Qt::TextEditable) || cursor.isNull()) {
1814         e->ignore();
1815         return;
1816     }
1817     bool isGettingInput = !e->commitString().isEmpty()
1818             || e->preeditString() != cursor.block().layout()->preeditAreaText()
1819             || e->replacementLength() > 0;
1820     bool forceSelectionChanged = false;
1821
1822     cursor.beginEditBlock();
1823     if (isGettingInput) {
1824         cursor.removeSelectedText();
1825     }
1826
1827     // insert commit string
1828     if (!e->commitString().isEmpty() || e->replacementLength()) {
1829         QTextCursor c = cursor;
1830         c.setPosition(c.position() + e->replacementStart());
1831         c.setPosition(c.position() + e->replacementLength(), QTextCursor::KeepAnchor);
1832         c.insertText(e->commitString());
1833     }
1834
1835     for (int i = 0; i < e->attributes().size(); ++i) {
1836         const QInputMethodEvent::Attribute &a = e->attributes().at(i);
1837         if (a.type == QInputMethodEvent::Selection) {
1838             QTextCursor oldCursor = cursor;
1839             int blockStart = a.start + cursor.block().position();
1840             cursor.setPosition(blockStart, QTextCursor::MoveAnchor);
1841             cursor.setPosition(blockStart + a.length, QTextCursor::KeepAnchor);
1842             q->ensureCursorVisible();
1843             repaintOldAndNewSelection(oldCursor);
1844             forceSelectionChanged = true;
1845         }
1846     }
1847
1848     QTextBlock block = cursor.block();
1849     QTextLayout *layout = block.layout();
1850     if (isGettingInput)
1851         layout->setPreeditArea(cursor.position() - block.position(), e->preeditString());
1852     QList<QTextLayout::FormatRange> overrides;
1853     const int oldPreeditCursor = preeditCursor;
1854     preeditCursor = e->preeditString().length();
1855     hideCursor = false;
1856     for (int i = 0; i < e->attributes().size(); ++i) {
1857         const QInputMethodEvent::Attribute &a = e->attributes().at(i);
1858         if (a.type == QInputMethodEvent::Cursor) {
1859             preeditCursor = a.start;
1860             hideCursor = !a.length;
1861         } else if (a.type == QInputMethodEvent::TextFormat) {
1862             QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat();
1863             if (f.isValid()) {
1864                 QTextLayout::FormatRange o;
1865                 o.start = a.start + cursor.position() - block.position();
1866                 o.length = a.length;
1867                 o.format = f;
1868                 overrides.append(o);
1869             }
1870         }
1871     }
1872     layout->setAdditionalFormats(overrides);
1873     tentativeCommit = e->tentativeCommitString();
1874
1875     cursor.endEditBlock();
1876
1877     QTextCursorPrivate *cursor_d = QTextCursorPrivate::getPrivate(&cursor);
1878     if (cursor_d)
1879         cursor_d->setX();
1880     if (oldPreeditCursor != preeditCursor)
1881         emit q->microFocusChanged();
1882     selectionChanged(forceSelectionChanged);
1883 }
1884
1885 QVariant QQuickTextControl::inputMethodQuery(Qt::InputMethodQuery property) const
1886 {
1887     Q_D(const QQuickTextControl);
1888     QTextBlock block = d->cursor.block();
1889     switch (property) {
1890     case Qt::ImCursorRectangle:
1891         return cursorRect();
1892     case Qt::ImFont:
1893         return QVariant(d->cursor.charFormat().font());
1894     case Qt::ImCursorPosition:
1895         return QVariant(d->cursor.position() - block.position());
1896     case Qt::ImSurroundingText:
1897         return QVariant(block.text());
1898     case Qt::ImCurrentSelection:
1899         return QVariant(d->cursor.selectedText());
1900     case Qt::ImMaximumTextLength:
1901         return QVariant(); // No limit.
1902     case Qt::ImAnchorPosition:
1903         return QVariant(d->cursor.anchor() - block.position());
1904     default:
1905         return QVariant();
1906     }
1907 }
1908
1909 void QQuickTextControl::setFocus(bool focus, Qt::FocusReason reason)
1910 {
1911     QFocusEvent ev(focus ? QEvent::FocusIn : QEvent::FocusOut,
1912                    reason);
1913     processEvent(&ev);
1914 }
1915
1916 void QQuickTextControlPrivate::focusEvent(QFocusEvent *e)
1917 {
1918     Q_Q(QQuickTextControl);
1919     emit q->updateRequest(q->selectionRect());
1920     if (e->gotFocus()) {
1921 #ifdef QT_KEYPAD_NAVIGATION
1922         if (!QGuiApplication::keypadNavigationEnabled() || (hasEditFocus && (e->reason() == Qt::PopupFocusReason
1923             ))) {
1924 #endif
1925             setBlinkingCursorEnabled(interactionFlags & (Qt::TextEditable | Qt::TextSelectableByKeyboard));
1926 #ifdef QT_KEYPAD_NAVIGATION
1927         }
1928 #endif
1929     } else {
1930         setBlinkingCursorEnabled(false);
1931
1932         if (cursorIsFocusIndicator
1933             && e->reason() != Qt::ActiveWindowFocusReason
1934             && e->reason() != Qt::PopupFocusReason
1935             && cursor.hasSelection()) {
1936             cursor.clearSelection();
1937         }
1938     }
1939     hasFocus = e->gotFocus();
1940 }
1941
1942 QString QQuickTextControlPrivate::anchorForCursor(const QTextCursor &anchorCursor) const
1943 {
1944     if (anchorCursor.hasSelection()) {
1945         QTextCursor cursor = anchorCursor;
1946         if (cursor.selectionStart() != cursor.position())
1947             cursor.setPosition(cursor.selectionStart());
1948         cursor.movePosition(QTextCursor::NextCharacter);
1949         QTextCharFormat fmt = cursor.charFormat();
1950         if (fmt.isAnchor() && fmt.hasProperty(QTextFormat::AnchorHref))
1951             return fmt.stringProperty(QTextFormat::AnchorHref);
1952     }
1953     return QString();
1954 }
1955
1956 #ifdef QT_KEYPAD_NAVIGATION
1957 void QQuickTextControlPrivate::editFocusEvent(QEvent *e)
1958 {
1959     Q_Q(QQuickTextControl);
1960
1961     if (QGuiApplication::keypadNavigationEnabled()) {
1962         if (e->type() == QEvent::EnterEditFocus && interactionFlags & Qt::TextEditable) {
1963             const QTextCursor oldSelection = cursor;
1964             const int oldCursorPos = cursor.position();
1965             const bool moved = cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
1966             q->ensureCursorVisible();
1967             if (moved) {
1968                 if (cursor.position() != oldCursorPos)
1969                     emit q->cursorPositionChanged();
1970                 emit q->microFocusChanged();
1971             }
1972             selectionChanged();
1973             repaintOldAndNewSelection(oldSelection);
1974
1975             setBlinkingCursorEnabled(true);
1976         } else
1977             setBlinkingCursorEnabled(false);
1978     }
1979
1980     hasEditFocus = e->type() == QEvent::EnterEditFocus ? true : false;
1981 }
1982 #endif
1983
1984 QTextCursor QQuickTextControl::cursorForPosition(const QPointF &pos) const
1985 {
1986     Q_D(const QQuickTextControl);
1987     int cursorPos = hitTest(pos, Qt::FuzzyHit);
1988     if (cursorPos == -1)
1989         cursorPos = 0;
1990     QTextCursor c(d->doc);
1991     c.setPosition(cursorPos);
1992     return c;
1993 }
1994
1995 QRectF QQuickTextControl::cursorRect(const QTextCursor &cursor) const
1996 {
1997     Q_D(const QQuickTextControl);
1998     if (cursor.isNull())
1999         return QRectF();
2000
2001     return d->rectForPosition(cursor.position());
2002 }
2003
2004 QRectF QQuickTextControl::cursorRect() const
2005 {
2006     Q_D(const QQuickTextControl);
2007     return cursorRect(d->cursor);
2008 }
2009
2010 QRectF QQuickTextControlPrivate::cursorRectPlusUnicodeDirectionMarkers(const QTextCursor &cursor) const
2011 {
2012     if (cursor.isNull())
2013         return QRectF();
2014
2015     return rectForPosition(cursor.position()).adjusted(-4, 0, 4, 0);
2016 }
2017
2018 QString QQuickTextControl::anchorAt(const QPointF &pos) const
2019 {
2020     Q_D(const QQuickTextControl);
2021     return d->doc->documentLayout()->anchorAt(pos);
2022 }
2023
2024 QString QQuickTextControl::anchorAtCursor() const
2025 {
2026     Q_D(const QQuickTextControl);
2027
2028     return d->anchorForCursor(d->cursor);
2029 }
2030
2031 bool QQuickTextControl::overwriteMode() const
2032 {
2033     Q_D(const QQuickTextControl);
2034     return d->overwriteMode;
2035 }
2036
2037 void QQuickTextControl::setOverwriteMode(bool overwrite)
2038 {
2039     Q_D(QQuickTextControl);
2040     d->overwriteMode = overwrite;
2041 }
2042
2043 int QQuickTextControl::cursorWidth() const
2044 {
2045 #ifndef QT_NO_PROPERTIES
2046     Q_D(const QQuickTextControl);
2047     return d->doc->documentLayout()->property("cursorWidth").toInt();
2048 #else
2049     return 1;
2050 #endif
2051 }
2052
2053 void QQuickTextControl::setCursorWidth(int width)
2054 {
2055     Q_D(QQuickTextControl);
2056 #ifdef QT_NO_PROPERTIES
2057     Q_UNUSED(width);
2058 #else
2059     if (width == -1)
2060         width = textCursorWidth;
2061     d->doc->documentLayout()->setProperty("cursorWidth", width);
2062 #endif
2063     d->repaintCursor();
2064 }
2065
2066 bool QQuickTextControl::acceptRichText() const
2067 {
2068     Q_D(const QQuickTextControl);
2069     return d->acceptRichText;
2070 }
2071
2072 void QQuickTextControl::setAcceptRichText(bool accept)
2073 {
2074     Q_D(QQuickTextControl);
2075     d->acceptRichText = accept;
2076 }
2077
2078 void QQuickTextControl::setExtraSelections(const QVector<QAbstractTextDocumentLayout::Selection> &selections)
2079 {
2080     Q_D(QQuickTextControl);
2081
2082     QHash<int, int> hash;
2083     for (int i = 0; i < d->extraSelections.count(); ++i) {
2084         const QAbstractTextDocumentLayout::Selection &esel = d->extraSelections.at(i);
2085         hash.insertMulti(esel.cursor.anchor(), i);
2086     }
2087
2088     for (int i = 0; i < selections.count(); ++i) {
2089         const QAbstractTextDocumentLayout::Selection &sel = selections.at(i);
2090         QHash<int, int>::iterator it = hash.find(sel.cursor.anchor());
2091         if (it != hash.end()) {
2092             const QAbstractTextDocumentLayout::Selection &esel = d->extraSelections.at(it.value());
2093             if (esel.cursor.position() == sel.cursor.position()
2094                 && esel.format == sel.format) {
2095                 hash.erase(it);
2096                 continue;
2097             }
2098         }
2099         QRectF r = selectionRect(sel.cursor);
2100         if (sel.format.boolProperty(QTextFormat::FullWidthSelection)) {
2101             r.setLeft(0);
2102             r.setWidth(qreal(INT_MAX));
2103         }
2104         emit updateRequest(r);
2105     }
2106
2107     for (QHash<int, int>::iterator it = hash.begin(); it != hash.end(); ++it) {
2108         const QAbstractTextDocumentLayout::Selection &esel = d->extraSelections.at(it.value());
2109         QRectF r = selectionRect(esel.cursor);
2110         if (esel.format.boolProperty(QTextFormat::FullWidthSelection)) {
2111             r.setLeft(0);
2112             r.setWidth(qreal(INT_MAX));
2113         }
2114         emit updateRequest(r);
2115     }
2116
2117     d->extraSelections = selections;
2118 }
2119
2120 QVector<QAbstractTextDocumentLayout::Selection> QQuickTextControl::extraSelections() const
2121 {
2122     Q_D(const QQuickTextControl);
2123     return d->extraSelections;
2124 }
2125
2126 void QQuickTextControl::setTextWidth(qreal width)
2127 {
2128     Q_D(QQuickTextControl);
2129     d->doc->setTextWidth(width);
2130 }
2131
2132 qreal QQuickTextControl::textWidth() const
2133 {
2134     Q_D(const QQuickTextControl);
2135     return d->doc->textWidth();
2136 }
2137
2138 QSizeF QQuickTextControl::size() const
2139 {
2140     Q_D(const QQuickTextControl);
2141     return d->doc->size();
2142 }
2143
2144 void QQuickTextControl::setOpenExternalLinks(bool open)
2145 {
2146     Q_D(QQuickTextControl);
2147     d->openExternalLinks = open;
2148 }
2149
2150 bool QQuickTextControl::openExternalLinks() const
2151 {
2152     Q_D(const QQuickTextControl);
2153     return d->openExternalLinks;
2154 }
2155
2156 bool QQuickTextControl::ignoreUnusedNavigationEvents() const
2157 {
2158     Q_D(const QQuickTextControl);
2159     return d->ignoreUnusedNavigationEvents;
2160 }
2161
2162 void QQuickTextControl::setIgnoreUnusedNavigationEvents(bool ignore)
2163 {
2164     Q_D(QQuickTextControl);
2165     d->ignoreUnusedNavigationEvents = ignore;
2166 }
2167
2168 void QQuickTextControl::moveCursor(QTextCursor::MoveOperation op, QTextCursor::MoveMode mode)
2169 {
2170     Q_D(QQuickTextControl);
2171     const QTextCursor oldSelection = d->cursor;
2172     const bool moved = d->cursor.movePosition(op, mode);
2173     d->_q_updateCurrentCharFormatAndSelection();
2174     ensureCursorVisible();
2175     d->repaintOldAndNewSelection(oldSelection);
2176     if (moved)
2177         emit cursorPositionChanged();
2178 }
2179
2180 bool QQuickTextControl::canPaste() const
2181 {
2182 #ifndef QT_NO_CLIPBOARD
2183     Q_D(const QQuickTextControl);
2184     if (d->interactionFlags & Qt::TextEditable) {
2185         const QMimeData *md = QGuiApplication::clipboard()->mimeData();
2186         return md && canInsertFromMimeData(md);
2187     }
2188 #endif
2189     return false;
2190 }
2191
2192 void QQuickTextControl::setCursorIsFocusIndicator(bool b)
2193 {
2194     Q_D(QQuickTextControl);
2195     d->cursorIsFocusIndicator = b;
2196     d->repaintCursor();
2197 }
2198
2199 bool QQuickTextControl::cursorIsFocusIndicator() const
2200 {
2201     Q_D(const QQuickTextControl);
2202     return d->cursorIsFocusIndicator;
2203 }
2204
2205
2206 void QQuickTextControl::setDragEnabled(bool enabled)
2207 {
2208     Q_D(QQuickTextControl);
2209     d->dragEnabled = enabled;
2210 }
2211
2212 bool QQuickTextControl::isDragEnabled() const
2213 {
2214     Q_D(const QQuickTextControl);
2215     return d->dragEnabled;
2216 }
2217
2218 void QQuickTextControl::setWordSelectionEnabled(bool enabled)
2219 {
2220     Q_D(QQuickTextControl);
2221     d->wordSelectionEnabled = enabled;
2222 }
2223
2224 bool QQuickTextControl::isWordSelectionEnabled() const
2225 {
2226     Q_D(const QQuickTextControl);
2227     return d->wordSelectionEnabled;
2228 }
2229
2230 QMimeData *QQuickTextControl::createMimeDataFromSelection() const
2231 {
2232     Q_D(const QQuickTextControl);
2233     const QTextDocumentFragment fragment(d->cursor);
2234     return new QQuickTextEditMimeData(fragment);
2235 }
2236
2237 bool QQuickTextControl::canInsertFromMimeData(const QMimeData *source) const
2238 {
2239     Q_D(const QQuickTextControl);
2240     if (d->acceptRichText)
2241         return (source->hasText() && !source->text().isEmpty())
2242             || source->hasHtml()
2243             || source->hasFormat(QLatin1String("application/x-qrichtext"))
2244             || source->hasFormat(QLatin1String("application/x-qt-richtext"));
2245     else
2246         return source->hasText() && !source->text().isEmpty();
2247 }
2248
2249 void QQuickTextControl::insertFromMimeData(const QMimeData *source)
2250 {
2251     Q_D(QQuickTextControl);
2252     if (!(d->interactionFlags & Qt::TextEditable) || !source)
2253         return;
2254
2255     bool hasData = false;
2256     QTextDocumentFragment fragment;
2257 #ifndef QT_NO_TEXTHTMLPARSER
2258     if (source->hasFormat(QLatin1String("application/x-qrichtext")) && d->acceptRichText) {
2259         // x-qrichtext is always UTF-8 (taken from Qt3 since we don't use it anymore).
2260         QString richtext = QString::fromUtf8(source->data(QLatin1String("application/x-qrichtext")));
2261         richtext.prepend(QLatin1String("<meta name=\"qrichtext\" content=\"1\" />"));
2262         fragment = QTextDocumentFragment::fromHtml(richtext, d->doc);
2263         hasData = true;
2264     } else if (source->hasHtml() && d->acceptRichText) {
2265         fragment = QTextDocumentFragment::fromHtml(source->html(), d->doc);
2266         hasData = true;
2267     } else {
2268         QString text = source->text();
2269         if (!text.isNull()) {
2270             fragment = QTextDocumentFragment::fromPlainText(text);
2271             hasData = true;
2272         }
2273     }
2274 #else
2275     fragment = QTextDocumentFragment::fromPlainText(source->text());
2276 #endif // QT_NO_TEXTHTMLPARSER
2277
2278     if (hasData)
2279         d->cursor.insertFragment(fragment);
2280     ensureCursorVisible();
2281 }
2282
2283 bool QQuickTextControl::findNextPrevAnchor(const QTextCursor &startCursor, bool next, QTextCursor &newAnchor)
2284 {
2285     Q_D(QQuickTextControl);
2286
2287     int anchorStart = -1;
2288     QString anchorHref;
2289     int anchorEnd = -1;
2290
2291     if (next) {
2292         const int startPos = startCursor.selectionEnd();
2293
2294         QTextBlock block = d->doc->findBlock(startPos);
2295         QTextBlock::Iterator it = block.begin();
2296
2297         while (!it.atEnd() && it.fragment().position() < startPos)
2298             ++it;
2299
2300         while (block.isValid()) {
2301             anchorStart = -1;
2302
2303             // find next anchor
2304             for (; !it.atEnd(); ++it) {
2305                 const QTextFragment fragment = it.fragment();
2306                 const QTextCharFormat fmt = fragment.charFormat();
2307
2308                 if (fmt.isAnchor() && fmt.hasProperty(QTextFormat::AnchorHref)) {
2309                     anchorStart = fragment.position();
2310                     anchorHref = fmt.anchorHref();
2311                     break;
2312                 }
2313             }
2314
2315             if (anchorStart != -1) {
2316                 anchorEnd = -1;
2317
2318                 // find next non-anchor fragment
2319                 for (; !it.atEnd(); ++it) {
2320                     const QTextFragment fragment = it.fragment();
2321                     const QTextCharFormat fmt = fragment.charFormat();
2322
2323                     if (!fmt.isAnchor() || fmt.anchorHref() != anchorHref) {
2324                         anchorEnd = fragment.position();
2325                         break;
2326                     }
2327                 }
2328
2329                 if (anchorEnd == -1)
2330                     anchorEnd = block.position() + block.length() - 1;
2331
2332                 // make found selection
2333                 break;
2334             }
2335
2336             block = block.next();
2337             it = block.begin();
2338         }
2339     } else {
2340         int startPos = startCursor.selectionStart();
2341         if (startPos > 0)
2342             --startPos;
2343
2344         QTextBlock block = d->doc->findBlock(startPos);
2345         QTextBlock::Iterator blockStart = block.begin();
2346         QTextBlock::Iterator it = block.end();
2347
2348         if (startPos == block.position()) {
2349             it = block.begin();
2350         } else {
2351             do {
2352                 if (it == blockStart) {
2353                     it = QTextBlock::Iterator();
2354                     block = QTextBlock();
2355                 } else {
2356                     --it;
2357                 }
2358             } while (!it.atEnd() && it.fragment().position() + it.fragment().length() - 1 > startPos);
2359         }
2360
2361         while (block.isValid()) {
2362             anchorStart = -1;
2363
2364             if (!it.atEnd()) {
2365                 do {
2366                     const QTextFragment fragment = it.fragment();
2367                     const QTextCharFormat fmt = fragment.charFormat();
2368
2369                     if (fmt.isAnchor() && fmt.hasProperty(QTextFormat::AnchorHref)) {
2370                         anchorStart = fragment.position() + fragment.length();
2371                         anchorHref = fmt.anchorHref();
2372                         break;
2373                     }
2374
2375                     if (it == blockStart)
2376                         it = QTextBlock::Iterator();
2377                     else
2378                         --it;
2379                 } while (!it.atEnd());
2380             }
2381
2382             if (anchorStart != -1 && !it.atEnd()) {
2383                 anchorEnd = -1;
2384
2385                 do {
2386                     const QTextFragment fragment = it.fragment();
2387                     const QTextCharFormat fmt = fragment.charFormat();
2388
2389                     if (!fmt.isAnchor() || fmt.anchorHref() != anchorHref) {
2390                         anchorEnd = fragment.position() + fragment.length();
2391                         break;
2392                     }
2393
2394                     if (it == blockStart)
2395                         it = QTextBlock::Iterator();
2396                     else
2397                         --it;
2398                 } while (!it.atEnd());
2399
2400                 if (anchorEnd == -1)
2401                     anchorEnd = qMax(0, block.position());
2402
2403                 break;
2404             }
2405
2406             block = block.previous();
2407             it = block.end();
2408             if (it != block.begin())
2409                 --it;
2410             blockStart = block.begin();
2411         }
2412
2413     }
2414
2415     if (anchorStart != -1 && anchorEnd != -1) {
2416         newAnchor = d->cursor;
2417         newAnchor.setPosition(anchorStart);
2418         newAnchor.setPosition(anchorEnd, QTextCursor::KeepAnchor);
2419         return true;
2420     }
2421
2422     return false;
2423 }
2424
2425 void QQuickTextControlPrivate::activateLinkUnderCursor(QString href)
2426 {
2427     QTextCursor oldCursor = cursor;
2428
2429     if (href.isEmpty()) {
2430         QTextCursor tmp = cursor;
2431         if (tmp.selectionStart() != tmp.position())
2432             tmp.setPosition(tmp.selectionStart());
2433         tmp.movePosition(QTextCursor::NextCharacter);
2434         href = tmp.charFormat().anchorHref();
2435     }
2436     if (href.isEmpty())
2437         return;
2438
2439     if (!cursor.hasSelection()) {
2440         QTextBlock block = cursor.block();
2441         const int cursorPos = cursor.position();
2442
2443         QTextBlock::Iterator it = block.begin();
2444         QTextBlock::Iterator linkFragment;
2445
2446         for (; !it.atEnd(); ++it) {
2447             QTextFragment fragment = it.fragment();
2448             const int fragmentPos = fragment.position();
2449             if (fragmentPos <= cursorPos &&
2450                 fragmentPos + fragment.length() > cursorPos) {
2451                 linkFragment = it;
2452                 break;
2453             }
2454         }
2455
2456         if (!linkFragment.atEnd()) {
2457             it = linkFragment;
2458             cursor.setPosition(it.fragment().position());
2459             if (it != block.begin()) {
2460                 do {
2461                     --it;
2462                     QTextFragment fragment = it.fragment();
2463                     if (fragment.charFormat().anchorHref() != href)
2464                         break;
2465                     cursor.setPosition(fragment.position());
2466                 } while (it != block.begin());
2467             }
2468
2469             for (it = linkFragment; !it.atEnd(); ++it) {
2470                 QTextFragment fragment = it.fragment();
2471                 if (fragment.charFormat().anchorHref() != href)
2472                     break;
2473                 cursor.setPosition(fragment.position() + fragment.length(), QTextCursor::KeepAnchor);
2474             }
2475         }
2476     }
2477
2478     if (hasFocus) {
2479         cursorIsFocusIndicator = true;
2480     } else {
2481         cursorIsFocusIndicator = false;
2482         cursor.clearSelection();
2483     }
2484     repaintOldAndNewSelection(oldCursor);
2485
2486 #if 0 // ###ndef QT_NO_DESKTOPSERVICES
2487     if (openExternalLinks)
2488         QDesktopServices::openUrl(href);
2489     else
2490 #endif
2491         emit q_func()->linkActivated(href);
2492 }
2493
2494 bool QQuickTextControl::setFocusToNextOrPreviousAnchor(bool next)
2495 {
2496     Q_D(QQuickTextControl);
2497
2498     if (!(d->interactionFlags & Qt::LinksAccessibleByKeyboard))
2499         return false;
2500
2501     QRectF crect = selectionRect();
2502     emit updateRequest(crect);
2503
2504     // If we don't have a current anchor, we start from the start/end
2505     if (!d->cursor.hasSelection()) {
2506         d->cursor = QTextCursor(d->doc);
2507         if (next)
2508             d->cursor.movePosition(QTextCursor::Start);
2509         else
2510             d->cursor.movePosition(QTextCursor::End);
2511     }
2512
2513     QTextCursor newAnchor;
2514     if (findNextPrevAnchor(d->cursor, next, newAnchor)) {
2515         d->cursor = newAnchor;
2516         d->cursorIsFocusIndicator = true;
2517     } else {
2518         d->cursor.clearSelection();
2519     }
2520
2521     if (d->cursor.hasSelection()) {
2522         crect = selectionRect();
2523         emit updateRequest(crect);
2524         emit visibilityRequest(crect);
2525         return true;
2526     } else {
2527         return false;
2528     }
2529 }
2530
2531 bool QQuickTextControl::setFocusToAnchor(const QTextCursor &newCursor)
2532 {
2533     Q_D(QQuickTextControl);
2534
2535     if (!(d->interactionFlags & Qt::LinksAccessibleByKeyboard))
2536         return false;
2537
2538     // Verify that this is an anchor.
2539     const QString anchorHref = d->anchorForCursor(newCursor);
2540     if (anchorHref.isEmpty())
2541         return false;
2542
2543     // and process it
2544     QRectF crect = selectionRect();
2545     emit updateRequest(crect);
2546
2547     d->cursor.setPosition(newCursor.selectionStart());
2548     d->cursor.setPosition(newCursor.selectionEnd(), QTextCursor::KeepAnchor);
2549     d->cursorIsFocusIndicator = true;
2550
2551     crect = selectionRect();
2552     emit updateRequest(crect);
2553     emit visibilityRequest(crect);
2554     return true;
2555 }
2556
2557 void QQuickTextControl::setTextInteractionFlags(Qt::TextInteractionFlags flags)
2558 {
2559     Q_D(QQuickTextControl);
2560     if (flags == d->interactionFlags)
2561         return;
2562     d->interactionFlags = flags;
2563
2564     if (d->hasFocus)
2565         d->setBlinkingCursorEnabled(flags & (Qt::TextEditable | Qt::TextSelectableByKeyboard));
2566 }
2567
2568 Qt::TextInteractionFlags QQuickTextControl::textInteractionFlags() const
2569 {
2570     Q_D(const QQuickTextControl);
2571     return d->interactionFlags;
2572 }
2573
2574 void QQuickTextControl::mergeCurrentCharFormat(const QTextCharFormat &modifier)
2575 {
2576     Q_D(QQuickTextControl);
2577     d->cursor.mergeCharFormat(modifier);
2578     d->updateCurrentCharFormat();
2579 }
2580
2581 void QQuickTextControl::setCurrentCharFormat(const QTextCharFormat &format)
2582 {
2583     Q_D(QQuickTextControl);
2584     d->cursor.setCharFormat(format);
2585     d->updateCurrentCharFormat();
2586 }
2587
2588 QTextCharFormat QQuickTextControl::currentCharFormat() const
2589 {
2590     Q_D(const QQuickTextControl);
2591     return d->cursor.charFormat();
2592 }
2593
2594 void QQuickTextControl::insertPlainText(const QString &text)
2595 {
2596     Q_D(QQuickTextControl);
2597     d->cursor.insertText(text);
2598 }
2599
2600 #ifndef QT_NO_TEXTHTMLPARSER
2601 void QQuickTextControl::insertHtml(const QString &text)
2602 {
2603     Q_D(QQuickTextControl);
2604     d->cursor.insertHtml(text);
2605 }
2606 #endif // QT_NO_TEXTHTMLPARSER
2607
2608 QPointF QQuickTextControl::anchorPosition(const QString &name) const
2609 {
2610     Q_D(const QQuickTextControl);
2611     if (name.isEmpty())
2612         return QPointF();
2613
2614     QRectF r;
2615     for (QTextBlock block = d->doc->begin(); block.isValid(); block = block.next()) {
2616         QTextCharFormat format = block.charFormat();
2617         if (format.isAnchor() && format.anchorNames().contains(name)) {
2618             r = d->rectForPosition(block.position());
2619             break;
2620         }
2621
2622         for (QTextBlock::Iterator it = block.begin(); !it.atEnd(); ++it) {
2623             QTextFragment fragment = it.fragment();
2624             format = fragment.charFormat();
2625             if (format.isAnchor() && format.anchorNames().contains(name)) {
2626                 r = d->rectForPosition(fragment.position());
2627                 block = QTextBlock();
2628                 break;
2629             }
2630         }
2631     }
2632     if (!r.isValid())
2633         return QPointF();
2634     return QPointF(0, r.top());
2635 }
2636
2637 void QQuickTextControl::adjustSize()
2638 {
2639     Q_D(QQuickTextControl);
2640     d->doc->adjustSize();
2641 }
2642
2643 bool QQuickTextControl::find(const QString &exp, QTextDocument::FindFlags options)
2644 {
2645     Q_D(QQuickTextControl);
2646     QTextCursor search = d->doc->find(exp, d->cursor, options);
2647     if (search.isNull())
2648         return false;
2649
2650     setTextCursor(search);
2651     return true;
2652 }
2653
2654
2655
2656 void QQuickTextControlPrivate::append(const QString &text, Qt::TextFormat format)
2657 {
2658     QTextCursor tmp(doc);
2659     tmp.beginEditBlock();
2660     tmp.movePosition(QTextCursor::End);
2661
2662     if (!doc->isEmpty())
2663         tmp.insertBlock(cursor.blockFormat(), cursor.charFormat());
2664     else
2665         tmp.setCharFormat(cursor.charFormat());
2666
2667     // preserve the char format
2668     QTextCharFormat oldCharFormat = cursor.charFormat();
2669
2670 #ifndef QT_NO_TEXTHTMLPARSER
2671     if (format == Qt::RichText || (format == Qt::AutoText && Qt::mightBeRichText(text))) {
2672         tmp.insertHtml(text);
2673     } else {
2674         tmp.insertText(text);
2675     }
2676 #else
2677     tmp.insertText(text);
2678 #endif // QT_NO_TEXTHTMLPARSER
2679     if (!cursor.hasSelection())
2680         cursor.setCharFormat(oldCharFormat);
2681
2682     tmp.endEditBlock();
2683 }
2684
2685 void QQuickTextControl::append(const QString &text)
2686 {
2687     Q_D(QQuickTextControl);
2688     d->append(text, Qt::AutoText);
2689 }
2690
2691 void QQuickTextControl::appendHtml(const QString &html)
2692 {
2693     Q_D(QQuickTextControl);
2694     d->append(html, Qt::RichText);
2695 }
2696
2697 void QQuickTextControl::appendPlainText(const QString &text)
2698 {
2699     Q_D(QQuickTextControl);
2700     d->append(text, Qt::PlainText);
2701 }
2702
2703 QString QQuickTextControl::toPlainText() const
2704 {
2705     Q_D(const QQuickTextControl);
2706     QString plainText = document()->toPlainText();
2707     if (!d->tentativeCommit.isEmpty())
2708         plainText.insert(textCursor().position(), d->tentativeCommit);
2709     return plainText;
2710 }
2711
2712 #ifndef QT_NO_TEXTHTMLPARSER
2713 QString QQuickTextControl::toHtml() const
2714 {
2715     // note: currently not including tentative commit
2716     return document()->toHtml();
2717 }
2718 #endif
2719
2720 void QQuickTextControl::ensureCursorVisible()
2721 {
2722     Q_D(QQuickTextControl);
2723     QRectF crect = d->rectForPosition(d->cursor.position()).adjusted(-5, 0, 5, 0);
2724     emit visibilityRequest(crect);
2725     emit microFocusChanged();
2726 }
2727
2728 QPalette QQuickTextControl::palette() const
2729 {
2730     Q_D(const QQuickTextControl);
2731     return d->palette;
2732 }
2733
2734 void QQuickTextControl::setPalette(const QPalette &pal)
2735 {
2736     Q_D(QQuickTextControl);
2737     d->palette = pal;
2738 }
2739
2740 bool QQuickTextControl::cursorOn() const
2741 {
2742     Q_D(const QQuickTextControl);
2743     return d->cursorOn;
2744 }
2745
2746 QAbstractTextDocumentLayout::PaintContext QQuickTextControl::getPaintContext() const
2747 {
2748     Q_D(const QQuickTextControl);
2749
2750     QAbstractTextDocumentLayout::PaintContext ctx;
2751
2752     ctx.selections = d->extraSelections;
2753     ctx.palette = d->palette;
2754     if (d->cursorOn && d->isEnabled) {
2755         if (d->hideCursor)
2756             ctx.cursorPosition = -1;
2757         else if (d->preeditCursor != 0)
2758             ctx.cursorPosition = - (d->preeditCursor + 2);
2759         else
2760             ctx.cursorPosition = d->cursor.position();
2761     }
2762
2763     if (!d->dndFeedbackCursor.isNull())
2764         ctx.cursorPosition = d->dndFeedbackCursor.position();
2765 #ifdef QT_KEYPAD_NAVIGATION
2766     if (!QGuiApplication::keypadNavigationEnabled() || d->hasEditFocus)
2767 #endif
2768     if (d->cursor.hasSelection()) {
2769         QAbstractTextDocumentLayout::Selection selection;
2770         selection.cursor = d->cursor;
2771         if (0 && d->cursorIsFocusIndicator) {
2772 #if 0
2773             // ###
2774             QStyleOption opt;
2775             opt.palette = ctx.palette;
2776             QStyleHintReturnVariant ret;
2777             QStyle *style = QGuiApplication::style();
2778             if (widget)
2779                 style = widget->style();
2780             style->styleHint(QStyle::SH_TextControl_FocusIndicatorTextCharFormat, &opt, widget, &ret);
2781             selection.format = qvariant_cast<QTextFormat>(ret.variant).toCharFormat();
2782 #endif
2783         } else {
2784             QPalette::ColorGroup cg = d->hasFocus ? QPalette::Active : QPalette::Inactive;
2785             selection.format.setBackground(ctx.palette.brush(cg, QPalette::Highlight));
2786             selection.format.setForeground(ctx.palette.brush(cg, QPalette::HighlightedText));
2787             if (fullWidthSelection)
2788                 selection.format.setProperty(QTextFormat::FullWidthSelection, true);
2789         }
2790         ctx.selections.append(selection);
2791     }
2792
2793     return ctx;
2794 }
2795
2796 void QQuickTextControl::drawContents(QPainter *p, const QRectF &rect)
2797 {
2798     Q_D(QQuickTextControl);
2799     p->save();
2800     QAbstractTextDocumentLayout::PaintContext ctx = getPaintContext();
2801     if (rect.isValid())
2802         p->setClipRect(rect, Qt::IntersectClip);
2803     ctx.clip = rect;
2804
2805     d->doc->documentLayout()->draw(p, ctx);
2806     p->restore();
2807 }
2808
2809 void QQuickTextControlPrivate::_q_copyLink()
2810 {
2811 #ifndef QT_NO_CLIPBOARD
2812     QMimeData *md = new QMimeData;
2813     md->setText(linkToCopy);
2814     QGuiApplication::clipboard()->setMimeData(md);
2815 #endif
2816 }
2817
2818 QInputContext *QQuickTextControlPrivate::inputContext()
2819 {
2820 #if 0
2821     // ###
2822     QInputContext *ctx = contextObject->inputContext();
2823     if (!ctx && contextObject->parentWidget())
2824         ctx = contextObject->parentWidget()->inputContext();
2825     return ctx;
2826 #else
2827     return 0;
2828 #endif
2829 }
2830
2831 int QQuickTextControl::hitTest(const QPointF &point, Qt::HitTestAccuracy accuracy) const
2832 {
2833     Q_D(const QQuickTextControl);
2834     return d->doc->documentLayout()->hitTest(point, accuracy);
2835 }
2836
2837 QRectF QQuickTextControl::blockBoundingRect(const QTextBlock &block) const
2838 {
2839     Q_D(const QQuickTextControl);
2840     return d->doc->documentLayout()->blockBoundingRect(block);
2841 }
2842
2843
2844
2845 QStringList QQuickTextEditMimeData::formats() const
2846 {
2847     if (!fragment.isEmpty())
2848         return QStringList() << QString::fromLatin1("text/plain") << QString::fromLatin1("text/html")
2849 #ifndef QT_NO_TEXTODFWRITER
2850             << QString::fromLatin1("application/vnd.oasis.opendocument.text")
2851 #endif
2852         ;
2853     else
2854         return QMimeData::formats();
2855 }
2856
2857 QVariant QQuickTextEditMimeData::retrieveData(const QString &mimeType, QVariant::Type type) const
2858 {
2859     if (!fragment.isEmpty())
2860         setup();
2861     return QMimeData::retrieveData(mimeType, type);
2862 }
2863
2864 void QQuickTextEditMimeData::setup() const
2865 {
2866     QQuickTextEditMimeData *that = const_cast<QQuickTextEditMimeData *>(this);
2867 #ifndef QT_NO_TEXTHTMLPARSER
2868     that->setData(QLatin1String("text/html"), fragment.toHtml("utf-8").toUtf8());
2869 #endif
2870 #ifndef QT_NO_TEXTODFWRITER
2871     {
2872         QBuffer buffer;
2873         QTextDocumentWriter writer(&buffer, "ODF");
2874         writer.write(fragment);
2875         buffer.close();
2876         that->setData(QLatin1String("application/vnd.oasis.opendocument.text"), buffer.data());
2877     }
2878 #endif
2879     that->setText(fragment.toPlainText());
2880     fragment = QTextDocumentFragment();
2881 }
2882
2883
2884 QT_END_NAMESPACE
2885
2886 #include "moc_qquicktextcontrol_p.cpp"
2887
2888 #endif // QT_NO_TEXTCONTROL