Remove "All rights reserved" line from license headers.
[profile/ivi/qtdeclarative.git] / src / quick / items / qquicktextcontrol.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
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       mousePressed(false),
103       lastSelectionState(false), ignoreAutomaticScrollbarAdjustement(false),
104       overwriteMode(false),
105       acceptRichText(true),
106       preeditCursor(0), hideCursor(false),
107       hasFocus(false),
108       isEnabled(true),
109       hadSelectionOnMousePress(false),
110       wordSelectionEnabled(false)
111 {}
112
113 bool QQuickTextControlPrivate::cursorMoveKeyEvent(QKeyEvent *e)
114 {
115 #ifdef QT_NO_SHORTCUT
116     Q_UNUSED(e);
117 #endif
118
119     Q_Q(QQuickTextControl);
120     if (cursor.isNull())
121         return false;
122
123     const QTextCursor oldSelection = cursor;
124     const int oldCursorPos = cursor.position();
125
126     QTextCursor::MoveMode mode = QTextCursor::MoveAnchor;
127     QTextCursor::MoveOperation op = QTextCursor::NoMove;
128
129     if (false) {
130     }
131 #ifndef QT_NO_SHORTCUT
132     if (e == QKeySequence::MoveToNextChar) {
133             op = QTextCursor::Right;
134     }
135     else if (e == QKeySequence::MoveToPreviousChar) {
136             op = QTextCursor::Left;
137     }
138     else if (e == QKeySequence::SelectNextChar) {
139            op = QTextCursor::Right;
140            mode = QTextCursor::KeepAnchor;
141     }
142     else if (e == QKeySequence::SelectPreviousChar) {
143             op = QTextCursor::Left;
144             mode = QTextCursor::KeepAnchor;
145     }
146     else if (e == QKeySequence::SelectNextWord) {
147             op = QTextCursor::WordRight;
148             mode = QTextCursor::KeepAnchor;
149     }
150     else if (e == QKeySequence::SelectPreviousWord) {
151             op = QTextCursor::WordLeft;
152             mode = QTextCursor::KeepAnchor;
153     }
154     else if (e == QKeySequence::SelectStartOfLine) {
155             op = QTextCursor::StartOfLine;
156             mode = QTextCursor::KeepAnchor;
157     }
158     else if (e == QKeySequence::SelectEndOfLine) {
159             op = QTextCursor::EndOfLine;
160             mode = QTextCursor::KeepAnchor;
161     }
162     else if (e == QKeySequence::SelectStartOfBlock) {
163             op = QTextCursor::StartOfBlock;
164             mode = QTextCursor::KeepAnchor;
165     }
166     else if (e == QKeySequence::SelectEndOfBlock) {
167             op = QTextCursor::EndOfBlock;
168             mode = QTextCursor::KeepAnchor;
169     }
170     else if (e == QKeySequence::SelectStartOfDocument) {
171             op = QTextCursor::Start;
172             mode = QTextCursor::KeepAnchor;
173     }
174     else if (e == QKeySequence::SelectEndOfDocument) {
175             op = QTextCursor::End;
176             mode = QTextCursor::KeepAnchor;
177     }
178     else if (e == QKeySequence::SelectPreviousLine) {
179             op = QTextCursor::Up;
180             mode = QTextCursor::KeepAnchor;
181     }
182     else if (e == QKeySequence::SelectNextLine) {
183             op = QTextCursor::Down;
184             mode = QTextCursor::KeepAnchor;
185             {
186                 QTextBlock block = cursor.block();
187                 QTextLine line = currentTextLine(cursor);
188                 if (!block.next().isValid()
189                     && line.isValid()
190                     && line.lineNumber() == block.layout()->lineCount() - 1)
191                     op = QTextCursor::End;
192             }
193     }
194     else if (e == QKeySequence::MoveToNextWord) {
195             op = QTextCursor::WordRight;
196     }
197     else if (e == QKeySequence::MoveToPreviousWord) {
198             op = QTextCursor::WordLeft;
199     }
200     else if (e == QKeySequence::MoveToEndOfBlock) {
201             op = QTextCursor::EndOfBlock;
202     }
203     else if (e == QKeySequence::MoveToStartOfBlock) {
204             op = QTextCursor::StartOfBlock;
205     }
206     else if (e == QKeySequence::MoveToNextLine) {
207             op = QTextCursor::Down;
208     }
209     else if (e == QKeySequence::MoveToPreviousLine) {
210             op = QTextCursor::Up;
211     }
212     else if (e == QKeySequence::MoveToStartOfLine) {
213             op = QTextCursor::StartOfLine;
214     }
215     else if (e == QKeySequence::MoveToEndOfLine) {
216             op = QTextCursor::EndOfLine;
217     }
218     else if (e == QKeySequence::MoveToStartOfDocument) {
219             op = QTextCursor::Start;
220     }
221     else if (e == QKeySequence::MoveToEndOfDocument) {
222             op = QTextCursor::End;
223     }
224 #endif // QT_NO_SHORTCUT
225     else {
226         return false;
227     }
228
229 // Except for pageup and pagedown, Mac OS X has very different behavior, we don't do it all, but
230 // here's the breakdown:
231 // Shift still works as an anchor, but only one of the other keys can be down Ctrl (Command),
232 // Alt (Option), or Meta (Control).
233 // Command/Control + Left/Right -- Move to left or right of the line
234 //                 + Up/Down -- Move to top bottom of the file. (Control doesn't move the cursor)
235 // Option + Left/Right -- Move one word Left/right.
236 //        + Up/Down  -- Begin/End of Paragraph.
237 // Home/End Top/Bottom of file. (usually don't move the cursor, but will select)
238
239     bool visualNavigation = cursor.visualNavigation();
240     cursor.setVisualNavigation(true);
241     const bool moved = cursor.movePosition(op, mode);
242     cursor.setVisualNavigation(visualNavigation);
243
244     bool isNavigationEvent
245             =  e->key() == Qt::Key_Up
246             || e->key() == Qt::Key_Down
247             || e->key() == Qt::Key_Left
248             || e->key() == Qt::Key_Right;
249
250     if (moved) {
251         if (cursor.position() != oldCursorPos)
252             emit q->cursorPositionChanged();
253         emit q->cursorRectangleChanged();
254     } else if (isNavigationEvent && oldSelection.anchor() == cursor.anchor()) {
255         return false;
256     }
257
258     selectionChanged(/*forceEmitSelectionChanged =*/(mode == QTextCursor::KeepAnchor));
259
260     repaintOldAndNewSelection(oldSelection);
261
262     return true;
263 }
264
265 void QQuickTextControlPrivate::updateCurrentCharFormat()
266 {
267     Q_Q(QQuickTextControl);
268
269     QTextCharFormat fmt = cursor.charFormat();
270     if (fmt == lastCharFormat)
271         return;
272     lastCharFormat = fmt;
273
274     emit q->currentCharFormatChanged(fmt);
275     emit q->cursorRectangleChanged();
276 }
277
278 void QQuickTextControlPrivate::init(Qt::TextFormat format, const QString &text, QTextDocument *document)
279 {
280     Q_Q(QQuickTextControl);
281     setContent(format, text, document);
282
283     doc->setUndoRedoEnabled(interactionFlags & Qt::TextEditable);
284     q->setCursorWidth(-1);
285 }
286
287 void QQuickTextControlPrivate::setContent(Qt::TextFormat format, const QString &text, QTextDocument *document)
288 {
289     Q_Q(QQuickTextControl);
290
291     // for use when called from setPlainText. we may want to re-use the currently
292     // set char format then.
293     const QTextCharFormat charFormatForInsertion = cursor.charFormat();
294
295     bool clearDocument = true;
296     if (!doc) {
297         if (document) {
298             doc = document;
299             clearDocument = false;
300         } else {
301             palette = QGuiApplication::palette();
302             doc = new QTextDocument(q);
303         }
304         _q_documentLayoutChanged();
305         cursor = QTextCursor(doc);
306
307 // ####        doc->documentLayout()->setPaintDevice(viewport);
308
309         QObject::connect(doc, SIGNAL(contentsChanged()), q, SLOT(_q_updateCurrentCharFormatAndSelection()));
310         QObject::connect(doc, SIGNAL(cursorPositionChanged(QTextCursor)), q, SLOT(_q_emitCursorPosChanged(QTextCursor)));
311         QObject::connect(doc, SIGNAL(documentLayoutChanged()), q, SLOT(_q_documentLayoutChanged()));
312     }
313
314     bool previousUndoRedoState = doc->isUndoRedoEnabled();
315     if (!document)
316         doc->setUndoRedoEnabled(false);
317
318     //Saving the index save some time.
319     static int contentsChangedIndex = QTextDocument::staticMetaObject.indexOfSignal("contentsChanged()");
320     static int textChangedIndex = QQuickTextControl::staticMetaObject.indexOfSignal("textChanged()");
321     // avoid multiple textChanged() signals being emitted
322     QMetaObject::disconnect(doc, contentsChangedIndex, q, textChangedIndex);
323
324     if (!text.isEmpty()) {
325         // clear 'our' cursor for insertion to prevent
326         // the emission of the cursorPositionChanged() signal.
327         // instead we emit it only once at the end instead of
328         // at the end of the document after loading and when
329         // positioning the cursor again to the start of the
330         // document.
331         cursor = QTextCursor();
332         if (format == Qt::PlainText) {
333             QTextCursor formatCursor(doc);
334             // put the setPlainText and the setCharFormat into one edit block,
335             // so that the syntax highlight triggers only /once/ for the entire
336             // document, not twice.
337             formatCursor.beginEditBlock();
338             doc->setPlainText(text);
339             doc->setUndoRedoEnabled(false);
340             formatCursor.select(QTextCursor::Document);
341             formatCursor.setCharFormat(charFormatForInsertion);
342             formatCursor.endEditBlock();
343         } else {
344 #ifndef QT_NO_TEXTHTMLPARSER
345             doc->setHtml(text);
346 #else
347             doc->setPlainText(text);
348 #endif
349             doc->setUndoRedoEnabled(false);
350         }
351         cursor = QTextCursor(doc);
352     } else if (clearDocument) {
353         doc->clear();
354     }
355     cursor.setCharFormat(charFormatForInsertion);
356
357     QMetaObject::connect(doc, contentsChangedIndex, q, textChangedIndex);
358     emit q->textChanged();
359     if (!document)
360         doc->setUndoRedoEnabled(previousUndoRedoState);
361     _q_updateCurrentCharFormatAndSelection();
362     if (!document)
363         doc->setModified(false);
364
365     emit q->cursorRectangleChanged();
366     emit q->cursorPositionChanged();
367 }
368
369 void QQuickTextControlPrivate::setCursorPosition(const QPointF &pos)
370 {
371     Q_Q(QQuickTextControl);
372     const int cursorPos = q->hitTest(pos, Qt::FuzzyHit);
373     if (cursorPos == -1)
374         return;
375     cursor.setPosition(cursorPos);
376 }
377
378 void QQuickTextControlPrivate::setCursorPosition(int pos, QTextCursor::MoveMode mode)
379 {
380     cursor.setPosition(pos, mode);
381
382     if (mode != QTextCursor::KeepAnchor) {
383         selectedWordOnDoubleClick = QTextCursor();
384         selectedBlockOnTrippleClick = QTextCursor();
385     }
386 }
387
388 void QQuickTextControlPrivate::repaintCursor()
389 {
390     Q_Q(QQuickTextControl);
391     emit q->updateCursorRequest(cursorRectPlusUnicodeDirectionMarkers(cursor));
392 }
393
394 void QQuickTextControlPrivate::repaintOldAndNewSelection(const QTextCursor &oldSelection)
395 {
396     Q_Q(QQuickTextControl);
397     if (cursor.hasSelection()
398         && oldSelection.hasSelection()
399         && cursor.currentFrame() == oldSelection.currentFrame()
400         && !cursor.hasComplexSelection()
401         && !oldSelection.hasComplexSelection()
402         && cursor.anchor() == oldSelection.anchor()
403         ) {
404         QTextCursor differenceSelection(doc);
405         differenceSelection.setPosition(oldSelection.position());
406         differenceSelection.setPosition(cursor.position(), QTextCursor::KeepAnchor);
407         emit q->updateRequest(q->selectionRect(differenceSelection));
408     } else {
409         if (!oldSelection.hasSelection() && !cursor.hasSelection()) {
410             if (!oldSelection.isNull())
411                 emit q->updateCursorRequest(q->selectionRect(oldSelection) | cursorRectPlusUnicodeDirectionMarkers(oldSelection));
412             emit q->updateCursorRequest(q->selectionRect() | cursorRectPlusUnicodeDirectionMarkers(cursor));
413
414         } else {
415             if (!oldSelection.isNull())
416                 emit q->updateRequest(q->selectionRect(oldSelection) | cursorRectPlusUnicodeDirectionMarkers(oldSelection));
417             emit q->updateRequest(q->selectionRect() | cursorRectPlusUnicodeDirectionMarkers(cursor));
418         }
419     }
420 }
421
422 void QQuickTextControlPrivate::selectionChanged(bool forceEmitSelectionChanged /*=false*/)
423 {
424     Q_Q(QQuickTextControl);
425     if (forceEmitSelectionChanged)
426         emit q->selectionChanged();
427
428     bool current = cursor.hasSelection();
429     if (current == lastSelectionState)
430         return;
431
432     lastSelectionState = current;
433     emit q->copyAvailable(current);
434     if (!forceEmitSelectionChanged)
435         emit q->selectionChanged();
436     emit q->cursorRectangleChanged();
437 }
438
439 void QQuickTextControlPrivate::_q_updateCurrentCharFormatAndSelection()
440 {
441     updateCurrentCharFormat();
442     selectionChanged();
443 }
444
445 #ifndef QT_NO_CLIPBOARD
446 void QQuickTextControlPrivate::setClipboardSelection()
447 {
448     QClipboard *clipboard = QGuiApplication::clipboard();
449     if (!cursor.hasSelection() || !clipboard->supportsSelection())
450         return;
451     Q_Q(QQuickTextControl);
452     QMimeData *data = q->createMimeDataFromSelection();
453     clipboard->setMimeData(data, QClipboard::Selection);
454 }
455 #endif
456
457 void QQuickTextControlPrivate::_q_emitCursorPosChanged(const QTextCursor &someCursor)
458 {
459     Q_Q(QQuickTextControl);
460     if (someCursor.isCopyOf(cursor)) {
461         emit q->cursorPositionChanged();
462         emit q->cursorRectangleChanged();
463     }
464 }
465
466 void QQuickTextControlPrivate::_q_documentLayoutChanged()
467 {
468     Q_Q(QQuickTextControl);
469     QAbstractTextDocumentLayout *layout = doc->documentLayout();
470     QObject::connect(layout, SIGNAL(update(QRectF)), q, SIGNAL(updateRequest(QRectF)));
471     QObject::connect(layout, SIGNAL(updateBlock(QTextBlock)), q, SLOT(_q_updateBlock(QTextBlock)));
472     QObject::connect(layout, SIGNAL(documentSizeChanged(QSizeF)), q, SIGNAL(documentSizeChanged(QSizeF)));
473
474 }
475
476 void QQuickTextControlPrivate::setBlinkingCursorEnabled(bool enable)
477 {
478     Q_Q(QQuickTextControl);
479
480     if (enable && qApp->styleHints()->cursorFlashTime() > 0)
481         cursorBlinkTimer.start(qApp->styleHints()->cursorFlashTime() / 2, q);
482     else
483         cursorBlinkTimer.stop();
484
485     cursorOn = enable;
486
487     repaintCursor();
488 }
489
490 void QQuickTextControlPrivate::extendWordwiseSelection(int suggestedNewPosition, qreal mouseXPosition)
491 {
492     Q_Q(QQuickTextControl);
493
494     // if inside the initial selected word keep that
495     if (suggestedNewPosition >= selectedWordOnDoubleClick.selectionStart()
496         && suggestedNewPosition <= selectedWordOnDoubleClick.selectionEnd()) {
497         q->setTextCursor(selectedWordOnDoubleClick);
498         return;
499     }
500
501     QTextCursor curs = selectedWordOnDoubleClick;
502     curs.setPosition(suggestedNewPosition, QTextCursor::KeepAnchor);
503
504     if (!curs.movePosition(QTextCursor::StartOfWord))
505         return;
506     const int wordStartPos = curs.position();
507
508     const int blockPos = curs.block().position();
509     const QPointF blockCoordinates = q->blockBoundingRect(curs.block()).topLeft();
510
511     QTextLine line = currentTextLine(curs);
512     if (!line.isValid())
513         return;
514
515     const qreal wordStartX = line.cursorToX(curs.position() - blockPos) + blockCoordinates.x();
516
517     if (!curs.movePosition(QTextCursor::EndOfWord))
518         return;
519     const int wordEndPos = curs.position();
520
521     const QTextLine otherLine = currentTextLine(curs);
522     if (otherLine.textStart() != line.textStart()
523         || wordEndPos == wordStartPos)
524         return;
525
526     const qreal wordEndX = line.cursorToX(curs.position() - blockPos) + blockCoordinates.x();
527
528     if (!wordSelectionEnabled && (mouseXPosition < wordStartX || mouseXPosition > wordEndX))
529         return;
530
531     if (wordSelectionEnabled) {
532         if (suggestedNewPosition < selectedWordOnDoubleClick.position()) {
533             cursor.setPosition(selectedWordOnDoubleClick.selectionEnd());
534             setCursorPosition(wordStartPos, QTextCursor::KeepAnchor);
535         } else {
536             cursor.setPosition(selectedWordOnDoubleClick.selectionStart());
537             setCursorPosition(wordEndPos, QTextCursor::KeepAnchor);
538         }
539     } else {
540         // keep the already selected word even when moving to the left
541         // (#39164)
542         if (suggestedNewPosition < selectedWordOnDoubleClick.position())
543             cursor.setPosition(selectedWordOnDoubleClick.selectionEnd());
544         else
545             cursor.setPosition(selectedWordOnDoubleClick.selectionStart());
546
547         const qreal differenceToStart = mouseXPosition - wordStartX;
548         const qreal differenceToEnd = wordEndX - mouseXPosition;
549
550         if (differenceToStart < differenceToEnd)
551             setCursorPosition(wordStartPos, QTextCursor::KeepAnchor);
552         else
553             setCursorPosition(wordEndPos, QTextCursor::KeepAnchor);
554     }
555
556     if (interactionFlags & Qt::TextSelectableByMouse) {
557 #ifndef QT_NO_CLIPBOARD
558         setClipboardSelection();
559 #endif
560         selectionChanged(true);
561     }
562 }
563
564 void QQuickTextControlPrivate::extendBlockwiseSelection(int suggestedNewPosition)
565 {
566     Q_Q(QQuickTextControl);
567
568     // if inside the initial selected line keep that
569     if (suggestedNewPosition >= selectedBlockOnTrippleClick.selectionStart()
570         && suggestedNewPosition <= selectedBlockOnTrippleClick.selectionEnd()) {
571         q->setTextCursor(selectedBlockOnTrippleClick);
572         return;
573     }
574
575     if (suggestedNewPosition < selectedBlockOnTrippleClick.position()) {
576         cursor.setPosition(selectedBlockOnTrippleClick.selectionEnd());
577         cursor.setPosition(suggestedNewPosition, QTextCursor::KeepAnchor);
578         cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor);
579     } else {
580         cursor.setPosition(selectedBlockOnTrippleClick.selectionStart());
581         cursor.setPosition(suggestedNewPosition, QTextCursor::KeepAnchor);
582         cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
583         cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
584     }
585
586     if (interactionFlags & Qt::TextSelectableByMouse) {
587 #ifndef QT_NO_CLIPBOARD
588         setClipboardSelection();
589 #endif
590         selectionChanged(true);
591     }
592 }
593
594 void QQuickTextControlPrivate::_q_deleteSelected()
595 {
596     if (!(interactionFlags & Qt::TextEditable) || !cursor.hasSelection())
597         return;
598     cursor.removeSelectedText();
599 }
600
601 void QQuickTextControl::undo()
602 {
603     Q_D(QQuickTextControl);
604     d->repaintSelection();
605     const int oldCursorPos = d->cursor.position();
606     d->doc->undo(&d->cursor);
607     if (d->cursor.position() != oldCursorPos)
608         emit cursorPositionChanged();
609     emit cursorRectangleChanged();
610 }
611
612 void QQuickTextControl::redo()
613 {
614     Q_D(QQuickTextControl);
615     d->repaintSelection();
616     const int oldCursorPos = d->cursor.position();
617     d->doc->redo(&d->cursor);
618         if (d->cursor.position() != oldCursorPos)
619         emit cursorPositionChanged();
620     emit cursorRectangleChanged();
621 }
622
623 QQuickTextControl::QQuickTextControl(QTextDocument *doc, QObject *parent)
624     : QObject(*new QQuickTextControlPrivate, parent)
625 {
626     Q_D(QQuickTextControl);
627     d->init(Qt::PlainText, QString(), doc);
628 }
629
630 QQuickTextControl::~QQuickTextControl()
631 {
632 }
633
634 void QQuickTextControl::setView(QObject *view)
635 {
636     Q_D(QQuickTextControl);
637     d->contextObject = view;
638 }
639
640 QObject *QQuickTextControl::view() const
641 {
642     Q_D(const QQuickTextControl);
643     return d->contextObject;
644 }
645
646 QTextDocument *QQuickTextControl::document() const
647 {
648     Q_D(const QQuickTextControl);
649     return d->doc;
650 }
651
652 void QQuickTextControl::setTextCursor(const QTextCursor &cursor)
653 {
654     Q_D(QQuickTextControl);
655     d->commitPreedit();
656     d->cursorIsFocusIndicator = false;
657     const bool posChanged = cursor.position() != d->cursor.position();
658     const QTextCursor oldSelection = d->cursor;
659     d->cursor = cursor;
660     d->cursorOn = d->hasFocus && (d->interactionFlags & Qt::TextEditable);
661     d->_q_updateCurrentCharFormatAndSelection();
662     emit cursorRectangleChanged();
663     d->repaintOldAndNewSelection(oldSelection);
664     if (posChanged)
665         emit cursorPositionChanged();
666 }
667
668 QTextCursor QQuickTextControl::textCursor() const
669 {
670     Q_D(const QQuickTextControl);
671     return d->cursor;
672 }
673
674 #ifndef QT_NO_CLIPBOARD
675
676 void QQuickTextControl::cut()
677 {
678     Q_D(QQuickTextControl);
679     if (!(d->interactionFlags & Qt::TextEditable) || !d->cursor.hasSelection())
680         return;
681     copy();
682     d->cursor.removeSelectedText();
683 }
684
685 void QQuickTextControl::copy()
686 {
687     Q_D(QQuickTextControl);
688     if (!d->cursor.hasSelection())
689         return;
690     QMimeData *data = createMimeDataFromSelection();
691     QGuiApplication::clipboard()->setMimeData(data);
692 }
693
694 void QQuickTextControl::paste(QClipboard::Mode mode)
695 {
696     const QMimeData *md = QGuiApplication::clipboard()->mimeData(mode);
697     if (md)
698         insertFromMimeData(md);
699 }
700 #endif
701
702 void QQuickTextControl::clear()
703 {
704     Q_D(QQuickTextControl);
705     // clears and sets empty content
706     d->setContent();
707 }
708
709
710 void QQuickTextControl::selectAll()
711 {
712     Q_D(QQuickTextControl);
713     const int selectionLength = qAbs(d->cursor.position() - d->cursor.anchor());
714     d->cursor.select(QTextCursor::Document);
715     d->selectionChanged(selectionLength != qAbs(d->cursor.position() - d->cursor.anchor()));
716     d->cursorIsFocusIndicator = false;
717     emit updateRequest();
718 }
719
720 void QQuickTextControl::processEvent(QEvent *e, const QPointF &coordinateOffset)
721 {
722     QMatrix m;
723     m.translate(coordinateOffset.x(), coordinateOffset.y());
724     processEvent(e, m);
725 }
726
727 void QQuickTextControl::processEvent(QEvent *e, const QMatrix &matrix)
728 {
729     Q_D(QQuickTextControl);
730     if (d->interactionFlags == Qt::NoTextInteraction) {
731         e->ignore();
732         return;
733     }
734
735     switch (e->type()) {
736         case QEvent::KeyPress:
737             d->keyPressEvent(static_cast<QKeyEvent *>(e));
738             break;
739         case QEvent::MouseButtonPress: {
740             QMouseEvent *ev = static_cast<QMouseEvent *>(e);
741             d->mousePressEvent(ev, matrix.map(ev->localPos()));
742             break; }
743         case QEvent::MouseMove: {
744             QMouseEvent *ev = static_cast<QMouseEvent *>(e);
745             d->mouseMoveEvent(ev, matrix.map(ev->localPos()));
746             break; }
747         case QEvent::MouseButtonRelease: {
748             QMouseEvent *ev = static_cast<QMouseEvent *>(e);
749             d->mouseReleaseEvent(ev, matrix.map(ev->localPos()));
750             break; }
751         case QEvent::MouseButtonDblClick: {
752             QMouseEvent *ev = static_cast<QMouseEvent *>(e);
753             d->mouseDoubleClickEvent(ev, matrix.map(ev->localPos()));
754             break; }
755         case QEvent::InputMethod:
756             d->inputMethodEvent(static_cast<QInputMethodEvent *>(e));
757             break;
758         case QEvent::FocusIn:
759         case QEvent::FocusOut:
760             d->focusEvent(static_cast<QFocusEvent *>(e));
761             break;
762
763         case QEvent::EnabledChange:
764             d->isEnabled = e->isAccepted();
765             break;
766
767         case QEvent::ShortcutOverride:
768             if (d->interactionFlags & Qt::TextEditable) {
769                 QKeyEvent* ke = static_cast<QKeyEvent *>(e);
770                 if (ke->modifiers() == Qt::NoModifier
771                     || ke->modifiers() == Qt::ShiftModifier
772                     || ke->modifiers() == Qt::KeypadModifier) {
773                     if (ke->key() < Qt::Key_Escape) {
774                         ke->accept();
775                     } else {
776                         switch (ke->key()) {
777                             case Qt::Key_Return:
778                             case Qt::Key_Enter:
779                             case Qt::Key_Delete:
780                             case Qt::Key_Home:
781                             case Qt::Key_End:
782                             case Qt::Key_Backspace:
783                             case Qt::Key_Left:
784                             case Qt::Key_Right:
785                             case Qt::Key_Up:
786                             case Qt::Key_Down:
787                             case Qt::Key_Tab:
788                             ke->accept();
789                         default:
790                             break;
791                         }
792                     }
793 #ifndef QT_NO_SHORTCUT
794                 } else if (ke == QKeySequence::Copy
795                            || ke == QKeySequence::Paste
796                            || ke == QKeySequence::Cut
797                            || ke == QKeySequence::Redo
798                            || ke == QKeySequence::Undo
799                            || ke == QKeySequence::MoveToNextWord
800                            || ke == QKeySequence::MoveToPreviousWord
801                            || ke == QKeySequence::MoveToStartOfDocument
802                            || ke == QKeySequence::MoveToEndOfDocument
803                            || ke == QKeySequence::SelectNextWord
804                            || ke == QKeySequence::SelectPreviousWord
805                            || ke == QKeySequence::SelectStartOfLine
806                            || ke == QKeySequence::SelectEndOfLine
807                            || ke == QKeySequence::SelectStartOfBlock
808                            || ke == QKeySequence::SelectEndOfBlock
809                            || ke == QKeySequence::SelectStartOfDocument
810                            || ke == QKeySequence::SelectEndOfDocument
811                            || ke == QKeySequence::SelectAll
812                           ) {
813                     ke->accept();
814 #endif
815                 }
816             }
817             break;
818         default:
819             break;
820     }
821 }
822
823 bool QQuickTextControl::event(QEvent *e)
824 {
825     return QObject::event(e);
826 }
827
828 void QQuickTextControl::timerEvent(QTimerEvent *e)
829 {
830     Q_D(QQuickTextControl);
831     if (e->timerId() == d->cursorBlinkTimer.timerId()) {
832         d->cursorOn = !d->cursorOn;
833
834         // ###
835 //        if (d->cursor.hasSelection())
836 //            d->cursorOn &= (QGuiApplication::style()->styleHint(QStyle::SH_BlinkCursorWhenTextSelected)
837 //                            != 0);
838
839         d->repaintCursor();
840     } else if (e->timerId() == d->trippleClickTimer.timerId()) {
841         d->trippleClickTimer.stop();
842     }
843 }
844
845 void QQuickTextControl::setPlainText(const QString &text)
846 {
847     Q_D(QQuickTextControl);
848     d->setContent(Qt::PlainText, text);
849 }
850
851 void QQuickTextControl::setHtml(const QString &text)
852 {
853     Q_D(QQuickTextControl);
854     d->setContent(Qt::RichText, text);
855 }
856
857 void QQuickTextControlPrivate::keyPressEvent(QKeyEvent *e)
858 {
859     Q_Q(QQuickTextControl);
860 #ifndef QT_NO_SHORTCUT
861     if (e == QKeySequence::SelectAll) {
862             e->accept();
863             q->selectAll();
864             return;
865     }
866 #ifndef QT_NO_CLIPBOARD
867     else if (e == QKeySequence::Copy) {
868             e->accept();
869             q->copy();
870             return;
871     }
872 #endif
873 #endif // QT_NO_SHORTCUT
874
875     if (interactionFlags & Qt::TextSelectableByKeyboard
876         && cursorMoveKeyEvent(e))
877         goto accept;
878
879     if (interactionFlags & Qt::LinksAccessibleByKeyboard) {
880         if ((e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) && cursor.hasSelection()) {
881             e->accept();
882             activateLinkUnderCursor();
883             return;
884         }
885     }
886
887     if (!(interactionFlags & Qt::TextEditable)) {
888         e->ignore();
889         return;
890     }
891
892     if (e->key() == Qt::Key_Direction_L || e->key() == Qt::Key_Direction_R) {
893         QTextBlockFormat fmt;
894         fmt.setLayoutDirection((e->key() == Qt::Key_Direction_L) ? Qt::LeftToRight : Qt::RightToLeft);
895         cursor.mergeBlockFormat(fmt);
896         goto accept;
897     }
898
899     // schedule a repaint of the region of the cursor, as when we move it we
900     // want to make sure the old cursor disappears (not noticeable when moving
901     // only a few pixels but noticeable when jumping between cells in tables for
902     // example)
903     repaintSelection();
904
905     if (e->key() == Qt::Key_Backspace && !(e->modifiers() & ~Qt::ShiftModifier)) {
906         QTextBlockFormat blockFmt = cursor.blockFormat();
907         QTextList *list = cursor.currentList();
908         if (list && cursor.atBlockStart() && !cursor.hasSelection()) {
909             list->remove(cursor.block());
910         } else if (cursor.atBlockStart() && blockFmt.indent() > 0) {
911             blockFmt.setIndent(blockFmt.indent() - 1);
912             cursor.setBlockFormat(blockFmt);
913         } else {
914             QTextCursor localCursor = cursor;
915             localCursor.deletePreviousChar();
916         }
917         goto accept;
918     }
919 #ifndef QT_NO_SHORTCUT
920       else if (e == QKeySequence::InsertParagraphSeparator) {
921         cursor.insertBlock();
922         e->accept();
923         goto accept;
924     } else if (e == QKeySequence::InsertLineSeparator) {
925         cursor.insertText(QString(QChar::LineSeparator));
926         e->accept();
927         goto accept;
928     }
929 #endif
930     if (false) {
931     }
932 #ifndef QT_NO_SHORTCUT
933     else if (e == QKeySequence::Undo) {
934             q->undo();
935     }
936     else if (e == QKeySequence::Redo) {
937            q->redo();
938     }
939 #ifndef QT_NO_CLIPBOARD
940     else if (e == QKeySequence::Cut) {
941            q->cut();
942     }
943     else if (e == QKeySequence::Paste) {
944         QClipboard::Mode mode = QClipboard::Clipboard;
945         q->paste(mode);
946     }
947 #endif
948     else if (e == QKeySequence::Delete) {
949         QTextCursor localCursor = cursor;
950         localCursor.deleteChar();
951     }
952     else if (e == QKeySequence::DeleteEndOfWord) {
953         if (!cursor.hasSelection())
954             cursor.movePosition(QTextCursor::NextWord, QTextCursor::KeepAnchor);
955         cursor.removeSelectedText();
956     }
957     else if (e == QKeySequence::DeleteStartOfWord) {
958         if (!cursor.hasSelection())
959             cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor);
960         cursor.removeSelectedText();
961     }
962     else if (e == QKeySequence::DeleteEndOfLine) {
963         QTextBlock block = cursor.block();
964         if (cursor.position() == block.position() + block.length() - 2)
965             cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
966         else
967             cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
968         cursor.removeSelectedText();
969     }
970 #endif // QT_NO_SHORTCUT
971     else {
972         goto process;
973     }
974     goto accept;
975
976 process:
977     {
978         QString text = e->text();
979         if (!text.isEmpty() && (text.at(0).isPrint() || text.at(0) == QLatin1Char('\t'))) {
980             cursor.insertText(text);
981             selectionChanged();
982         } else {
983             e->ignore();
984             return;
985         }
986     }
987
988  accept:
989
990     e->accept();
991     cursorOn = true;
992
993     emit q->cursorRectangleChanged();
994
995     updateCurrentCharFormat();
996 }
997
998 void QQuickTextControlPrivate::_q_updateBlock(const QTextBlock &block)
999 {
1000     Q_Q(QQuickTextControl);
1001     QRectF br = q->blockBoundingRect(block);
1002     br.setRight(qreal(INT_MAX)); // the block might have shrunk
1003     emit q->updateRequest(br);
1004 }
1005
1006 QRectF QQuickTextControlPrivate::rectForPosition(int position) const
1007 {
1008     Q_Q(const QQuickTextControl);
1009     const QTextBlock block = doc->findBlock(position);
1010     if (!block.isValid())
1011         return QRectF();
1012     const QAbstractTextDocumentLayout *docLayout = doc->documentLayout();
1013     const QTextLayout *layout = block.layout();
1014     const QPointF layoutPos = q->blockBoundingRect(block).topLeft();
1015     int relativePos = position - block.position();
1016     if (preeditCursor != 0) {
1017         int preeditPos = layout->preeditAreaPosition();
1018         if (relativePos == preeditPos)
1019             relativePos += preeditCursor;
1020         else if (relativePos > preeditPos)
1021             relativePos += layout->preeditAreaText().length();
1022     }
1023     QTextLine line = layout->lineForTextPosition(relativePos);
1024
1025     int cursorWidth;
1026     {
1027         bool ok = false;
1028 #ifndef QT_NO_PROPERTIES
1029         cursorWidth = docLayout->property("cursorWidth").toInt(&ok);
1030 #endif
1031         if (!ok)
1032             cursorWidth = 1;
1033     }
1034
1035     QRectF r;
1036
1037     if (line.isValid()) {
1038         qreal x = line.cursorToX(relativePos);
1039         qreal w = 0;
1040         r = QRectF(layoutPos.x() + x, layoutPos.y() + line.y(), cursorWidth + w, line.height());
1041     } else {
1042         r = QRectF(layoutPos.x(), layoutPos.y(), cursorWidth, 10); // #### correct height
1043     }
1044
1045     return r;
1046 }
1047
1048 static inline bool firstFramePosLessThanCursorPos(QTextFrame *frame, int position)
1049 {
1050     return frame->firstPosition() < position;
1051 }
1052
1053 static inline bool cursorPosLessThanLastFramePos(int position, QTextFrame *frame)
1054 {
1055     return position < frame->lastPosition();
1056 }
1057
1058 static QRectF boundingRectOfFloatsInSelection(const QTextCursor &cursor)
1059 {
1060     QRectF r;
1061     QTextFrame *frame = cursor.currentFrame();
1062     const QList<QTextFrame *> children = frame->childFrames();
1063
1064     const QList<QTextFrame *>::ConstIterator firstFrame = qLowerBound(children.constBegin(), children.constEnd(),
1065                                                                       cursor.selectionStart(), firstFramePosLessThanCursorPos);
1066     const QList<QTextFrame *>::ConstIterator lastFrame = qUpperBound(children.constBegin(), children.constEnd(),
1067                                                                      cursor.selectionEnd(), cursorPosLessThanLastFramePos);
1068     for (QList<QTextFrame *>::ConstIterator it = firstFrame; it != lastFrame; ++it) {
1069         if ((*it)->frameFormat().position() != QTextFrameFormat::InFlow)
1070             r |= frame->document()->documentLayout()->frameBoundingRect(*it);
1071     }
1072     return r;
1073 }
1074
1075 QRectF QQuickTextControl::selectionRect(const QTextCursor &cursor) const
1076 {
1077     Q_D(const QQuickTextControl);
1078
1079     QRectF r = d->rectForPosition(cursor.selectionStart());
1080
1081     if (cursor.hasComplexSelection() && cursor.currentTable()) {
1082         QTextTable *table = cursor.currentTable();
1083
1084         r = d->doc->documentLayout()->frameBoundingRect(table);
1085         /*
1086         int firstRow, numRows, firstColumn, numColumns;
1087         cursor.selectedTableCells(&firstRow, &numRows, &firstColumn, &numColumns);
1088
1089         const QTextTableCell firstCell = table->cellAt(firstRow, firstColumn);
1090         const QTextTableCell lastCell = table->cellAt(firstRow + numRows - 1, firstColumn + numColumns - 1);
1091
1092         const QAbstractTextDocumentLayout * const layout = doc->documentLayout();
1093
1094         QRectF tableSelRect = layout->blockBoundingRect(firstCell.firstCursorPosition().block());
1095
1096         for (int col = firstColumn; col < firstColumn + numColumns; ++col) {
1097             const QTextTableCell cell = table->cellAt(firstRow, col);
1098             const qreal y = layout->blockBoundingRect(cell.firstCursorPosition().block()).top();
1099
1100             tableSelRect.setTop(qMin(tableSelRect.top(), y));
1101         }
1102
1103         for (int row = firstRow; row < firstRow + numRows; ++row) {
1104             const QTextTableCell cell = table->cellAt(row, firstColumn);
1105             const qreal x = layout->blockBoundingRect(cell.firstCursorPosition().block()).left();
1106
1107             tableSelRect.setLeft(qMin(tableSelRect.left(), x));
1108         }
1109
1110         for (int col = firstColumn; col < firstColumn + numColumns; ++col) {
1111             const QTextTableCell cell = table->cellAt(firstRow + numRows - 1, col);
1112             const qreal y = layout->blockBoundingRect(cell.lastCursorPosition().block()).bottom();
1113
1114             tableSelRect.setBottom(qMax(tableSelRect.bottom(), y));
1115         }
1116
1117         for (int row = firstRow; row < firstRow + numRows; ++row) {
1118             const QTextTableCell cell = table->cellAt(row, firstColumn + numColumns - 1);
1119             const qreal x = layout->blockBoundingRect(cell.lastCursorPosition().block()).right();
1120
1121             tableSelRect.setRight(qMax(tableSelRect.right(), x));
1122         }
1123
1124         r = tableSelRect.toRect();
1125         */
1126     } else if (cursor.hasSelection()) {
1127         const int position = cursor.selectionStart();
1128         const int anchor = cursor.selectionEnd();
1129         const QTextBlock posBlock = d->doc->findBlock(position);
1130         const QTextBlock anchorBlock = d->doc->findBlock(anchor);
1131         if (posBlock == anchorBlock && posBlock.isValid() && posBlock.layout()->lineCount()) {
1132             const QTextLine posLine = posBlock.layout()->lineForTextPosition(position - posBlock.position());
1133             const QTextLine anchorLine = anchorBlock.layout()->lineForTextPosition(anchor - anchorBlock.position());
1134
1135             const int firstLine = qMin(posLine.lineNumber(), anchorLine.lineNumber());
1136             const int lastLine = qMax(posLine.lineNumber(), anchorLine.lineNumber());
1137             const QTextLayout *layout = posBlock.layout();
1138             r = QRectF();
1139             for (int i = firstLine; i <= lastLine; ++i) {
1140                 r |= layout->lineAt(i).rect();
1141                 r |= layout->lineAt(i).naturalTextRect(); // might be bigger in the case of wrap not enabled
1142             }
1143             r.translate(blockBoundingRect(posBlock).topLeft());
1144         } else {
1145             QRectF anchorRect = d->rectForPosition(cursor.selectionEnd());
1146             r |= anchorRect;
1147             r |= boundingRectOfFloatsInSelection(cursor);
1148             QRectF frameRect(d->doc->documentLayout()->frameBoundingRect(cursor.currentFrame()));
1149             r.setLeft(frameRect.left());
1150             r.setRight(frameRect.right());
1151         }
1152         if (r.isValid())
1153             r.adjust(-1, -1, 1, 1);
1154     }
1155
1156     return r;
1157 }
1158
1159 QRectF QQuickTextControl::selectionRect() const
1160 {
1161     Q_D(const QQuickTextControl);
1162     return selectionRect(d->cursor);
1163 }
1164
1165 void QQuickTextControlPrivate::mousePressEvent(QMouseEvent *e, const QPointF &pos)
1166 {
1167     Q_Q(QQuickTextControl);
1168
1169     mousePressed = (interactionFlags & Qt::TextSelectableByMouse);
1170     mousePressPos = pos.toPoint();
1171
1172     if (sendMouseEventToInputContext(e, pos))
1173         return;
1174
1175     if (interactionFlags & Qt::LinksAccessibleByMouse) {
1176         anchorOnMousePress = q->anchorAt(pos);
1177
1178         if (cursorIsFocusIndicator) {
1179             cursorIsFocusIndicator = false;
1180             repaintSelection();
1181             cursor.clearSelection();
1182         }
1183     }
1184     if (!(e->button() & Qt::LeftButton)) {
1185         e->ignore();
1186         return;
1187     } else if (!(interactionFlags & (Qt::TextSelectableByMouse | Qt::TextEditable))) {
1188         if (!(interactionFlags & Qt::LinksAccessibleByMouse))
1189             e->ignore();
1190         return;
1191     }
1192
1193     cursorIsFocusIndicator = false;
1194     const QTextCursor oldSelection = cursor;
1195     const int oldCursorPos = cursor.position();
1196
1197     commitPreedit();
1198
1199     if (trippleClickTimer.isActive()
1200         && ((pos - trippleClickPoint).toPoint().manhattanLength() < qApp->styleHints()->startDragDistance())) {
1201
1202         cursor.movePosition(QTextCursor::StartOfBlock);
1203         cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
1204         cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
1205         selectedBlockOnTrippleClick = cursor;
1206
1207         anchorOnMousePress = QString();
1208
1209         trippleClickTimer.stop();
1210     } else {
1211         int cursorPos = q->hitTest(pos, Qt::FuzzyHit);
1212         if (cursorPos == -1) {
1213             e->ignore();
1214             return;
1215         }
1216
1217         if (e->modifiers() == Qt::ShiftModifier && (interactionFlags & Qt::TextSelectableByMouse)) {
1218             if (wordSelectionEnabled && !selectedWordOnDoubleClick.hasSelection()) {
1219                 selectedWordOnDoubleClick = cursor;
1220                 selectedWordOnDoubleClick.select(QTextCursor::WordUnderCursor);
1221             }
1222
1223             if (selectedBlockOnTrippleClick.hasSelection())
1224                 extendBlockwiseSelection(cursorPos);
1225             else if (selectedWordOnDoubleClick.hasSelection())
1226                 extendWordwiseSelection(cursorPos, pos.x());
1227             else if (!wordSelectionEnabled)
1228                 setCursorPosition(cursorPos, QTextCursor::KeepAnchor);
1229         } else {
1230             setCursorPosition(cursorPos);
1231         }
1232     }
1233
1234     if (interactionFlags & Qt::TextEditable) {
1235         emit q->cursorRectangleChanged();
1236         if (cursor.position() != oldCursorPos)
1237             emit q->cursorPositionChanged();
1238         _q_updateCurrentCharFormatAndSelection();
1239     } else {
1240         if (cursor.position() != oldCursorPos) {
1241             emit q->cursorPositionChanged();
1242             emit q->cursorRectangleChanged();
1243         }
1244         selectionChanged();
1245     }
1246     repaintOldAndNewSelection(oldSelection);
1247     hadSelectionOnMousePress = cursor.hasSelection();
1248 }
1249
1250 void QQuickTextControlPrivate::mouseMoveEvent(QMouseEvent *e, const QPointF &mousePos)
1251 {
1252     Q_Q(QQuickTextControl);
1253
1254     if (interactionFlags & Qt::LinksAccessibleByMouse) {
1255         QString anchor = q->anchorAt(mousePos);
1256         if (anchor != highlightedAnchor) {
1257             highlightedAnchor = anchor;
1258             emit q->linkHovered(anchor);
1259         }
1260     }
1261
1262     if ((e->buttons() & Qt::LeftButton)) {
1263         const bool editable = interactionFlags & Qt::TextEditable;
1264
1265         if (!(mousePressed
1266               || editable
1267               || selectedWordOnDoubleClick.hasSelection()
1268               || selectedBlockOnTrippleClick.hasSelection()))
1269             return;
1270
1271         const QTextCursor oldSelection = cursor;
1272         const int oldCursorPos = cursor.position();
1273
1274         if (!mousePressed)
1275             return;
1276
1277         const qreal mouseX = qreal(mousePos.x());
1278
1279         int newCursorPos = q->hitTest(mousePos, Qt::FuzzyHit);
1280
1281         if (isPreediting()) {
1282             // note: oldCursorPos not including preedit
1283             int selectionStartPos = q->hitTest(mousePressPos, Qt::FuzzyHit);
1284             if (newCursorPos != selectionStartPos) {
1285                 commitPreedit();
1286                 // commit invalidates positions
1287                 newCursorPos = q->hitTest(mousePos, Qt::FuzzyHit);
1288                 selectionStartPos = q->hitTest(mousePressPos, Qt::FuzzyHit);
1289                 setCursorPosition(selectionStartPos);
1290             }
1291         }
1292
1293         if (newCursorPos == -1)
1294             return;
1295
1296         if (wordSelectionEnabled && !selectedWordOnDoubleClick.hasSelection()) {
1297             selectedWordOnDoubleClick = cursor;
1298             selectedWordOnDoubleClick.select(QTextCursor::WordUnderCursor);
1299         }
1300
1301         if (selectedBlockOnTrippleClick.hasSelection())
1302             extendBlockwiseSelection(newCursorPos);
1303         else if (selectedWordOnDoubleClick.hasSelection())
1304             extendWordwiseSelection(newCursorPos, mouseX);
1305         else if (!isPreediting())
1306             setCursorPosition(newCursorPos, QTextCursor::KeepAnchor);
1307
1308         if (interactionFlags & Qt::TextEditable) {
1309             if (cursor.position() != oldCursorPos)
1310                 emit q->cursorPositionChanged();
1311             _q_updateCurrentCharFormatAndSelection();
1312             if (qGuiApp)
1313                 qGuiApp->inputPanel()->update(Qt::ImQueryInput);
1314         } else if (cursor.position() != oldCursorPos) {
1315             emit q->cursorPositionChanged();
1316         }
1317         selectionChanged(true);
1318         repaintOldAndNewSelection(oldSelection);
1319     }
1320
1321     sendMouseEventToInputContext(e, mousePos);
1322 }
1323
1324 void QQuickTextControlPrivate::mouseReleaseEvent(QMouseEvent *e, const QPointF &pos)
1325 {
1326     Q_Q(QQuickTextControl);
1327
1328     if (sendMouseEventToInputContext(e, pos))
1329         return;
1330
1331     const QTextCursor oldSelection = cursor;
1332     const int oldCursorPos = cursor.position();
1333
1334     if (mousePressed) {
1335         mousePressed = false;
1336 #ifndef QT_NO_CLIPBOARD
1337         setClipboardSelection();
1338         selectionChanged(true);
1339     } else if (e->button() == Qt::MidButton
1340                && (interactionFlags & Qt::TextEditable)
1341                && QGuiApplication::clipboard()->supportsSelection()) {
1342         setCursorPosition(pos);
1343         const QMimeData *md = QGuiApplication::clipboard()->mimeData(QClipboard::Selection);
1344         if (md)
1345             q->insertFromMimeData(md);
1346 #endif
1347     }
1348
1349     repaintOldAndNewSelection(oldSelection);
1350
1351     if (cursor.position() != oldCursorPos) {
1352         emit q->cursorPositionChanged();
1353         emit q->cursorRectangleChanged();
1354     }
1355
1356     if (interactionFlags & Qt::LinksAccessibleByMouse) {
1357         if (!(e->button() & Qt::LeftButton))
1358             return;
1359
1360         const QString anchor = q->anchorAt(pos);
1361
1362         if (anchor.isEmpty())
1363             return;
1364
1365         if (!cursor.hasSelection()
1366             || (anchor == anchorOnMousePress && hadSelectionOnMousePress)) {
1367
1368             const int anchorPos = q->hitTest(pos, Qt::ExactHit);
1369             if (anchorPos != -1) {
1370                 cursor.setPosition(anchorPos);
1371
1372                 QString anchor = anchorOnMousePress;
1373                 anchorOnMousePress = QString();
1374                 activateLinkUnderCursor(anchor);
1375             }
1376         }
1377     }
1378 }
1379
1380 void QQuickTextControlPrivate::mouseDoubleClickEvent(QMouseEvent *e, const QPointF &pos)
1381 {
1382     Q_Q(QQuickTextControl);
1383
1384     if (e->button() == Qt::LeftButton && (interactionFlags & Qt::TextSelectableByMouse)) {
1385         commitPreedit();
1386
1387         const QTextCursor oldSelection = cursor;
1388         setCursorPosition(pos);
1389         QTextLine line = currentTextLine(cursor);
1390         bool doEmit = false;
1391         if (line.isValid() && line.textLength()) {
1392             cursor.select(QTextCursor::WordUnderCursor);
1393             doEmit = true;
1394         }
1395         repaintOldAndNewSelection(oldSelection);
1396
1397         cursorIsFocusIndicator = false;
1398         selectedWordOnDoubleClick = cursor;
1399
1400         trippleClickPoint = pos;
1401         trippleClickTimer.start(qApp->styleHints()->mouseDoubleClickInterval(), q);
1402         if (doEmit) {
1403             selectionChanged();
1404 #ifndef QT_NO_CLIPBOARD
1405             setClipboardSelection();
1406 #endif
1407             emit q->cursorPositionChanged();
1408         }
1409     } else if (!sendMouseEventToInputContext(e, pos)) {
1410         e->ignore();
1411     }
1412 }
1413
1414 bool QQuickTextControlPrivate::sendMouseEventToInputContext(QMouseEvent *e, const QPointF &pos)
1415 {
1416 #if !defined(QT_NO_IM)
1417     Q_Q(QQuickTextControl);
1418
1419     Q_UNUSED(e);
1420
1421     if (contextObject && isPreediting()) {
1422         QTextLayout *layout = cursor.block().layout();
1423         int cursorPos = q->hitTest(pos, Qt::FuzzyHit) - cursor.position();
1424
1425         if (cursorPos >= 0 && cursorPos <= layout->preeditAreaText().length()) {
1426             if (e->type() == QEvent::MouseButtonRelease) {
1427                 qApp->inputPanel()->invokeAction(QInputPanel::Click, cursorPos);
1428             }
1429
1430             return true;
1431         }
1432     }
1433 #else
1434     Q_UNUSED(e);
1435     Q_UNUSED(pos);
1436 #endif
1437     return false;
1438 }
1439
1440 void QQuickTextControlPrivate::inputMethodEvent(QInputMethodEvent *e)
1441 {
1442     Q_Q(QQuickTextControl);
1443     if (!(interactionFlags & Qt::TextEditable) || cursor.isNull()) {
1444         e->ignore();
1445         return;
1446     }
1447     bool isGettingInput = !e->commitString().isEmpty()
1448             || e->preeditString() != cursor.block().layout()->preeditAreaText()
1449             || e->replacementLength() > 0;
1450     bool forceSelectionChanged = false;
1451
1452     cursor.beginEditBlock();
1453     if (isGettingInput) {
1454         cursor.removeSelectedText();
1455     }
1456
1457     // insert commit string
1458     if (!e->commitString().isEmpty() || e->replacementLength()) {
1459         QTextCursor c = cursor;
1460         c.setPosition(c.position() + e->replacementStart());
1461         c.setPosition(c.position() + e->replacementLength(), QTextCursor::KeepAnchor);
1462         c.insertText(e->commitString());
1463     }
1464
1465     for (int i = 0; i < e->attributes().size(); ++i) {
1466         const QInputMethodEvent::Attribute &a = e->attributes().at(i);
1467         if (a.type == QInputMethodEvent::Selection) {
1468             QTextCursor oldCursor = cursor;
1469             int blockStart = a.start + cursor.block().position();
1470             cursor.setPosition(blockStart, QTextCursor::MoveAnchor);
1471             cursor.setPosition(blockStart + a.length, QTextCursor::KeepAnchor);
1472             emit q->cursorRectangleChanged();
1473             repaintOldAndNewSelection(oldCursor);
1474             forceSelectionChanged = true;
1475         }
1476     }
1477
1478     QTextBlock block = cursor.block();
1479     QTextLayout *layout = block.layout();
1480     if (isGettingInput)
1481         layout->setPreeditArea(cursor.position() - block.position(), e->preeditString());
1482     QList<QTextLayout::FormatRange> overrides;
1483     const int oldPreeditCursor = preeditCursor;
1484     preeditCursor = e->preeditString().length();
1485     hideCursor = false;
1486     for (int i = 0; i < e->attributes().size(); ++i) {
1487         const QInputMethodEvent::Attribute &a = e->attributes().at(i);
1488         if (a.type == QInputMethodEvent::Cursor) {
1489             preeditCursor = a.start;
1490             hideCursor = !a.length;
1491         } else if (a.type == QInputMethodEvent::TextFormat) {
1492             QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat();
1493             if (f.isValid()) {
1494                 QTextLayout::FormatRange o;
1495                 o.start = a.start + cursor.position() - block.position();
1496                 o.length = a.length;
1497                 o.format = f;
1498                 overrides.append(o);
1499             }
1500         }
1501     }
1502     layout->setAdditionalFormats(overrides);
1503     tentativeCommit = e->tentativeCommitString();
1504
1505     cursor.endEditBlock();
1506
1507     QTextCursorPrivate *cursor_d = QTextCursorPrivate::getPrivate(&cursor);
1508     if (cursor_d)
1509         cursor_d->setX();
1510     if (oldPreeditCursor != preeditCursor)
1511         emit q->cursorRectangleChanged();
1512     selectionChanged(forceSelectionChanged);
1513 }
1514
1515 QVariant QQuickTextControl::inputMethodQuery(Qt::InputMethodQuery property) const
1516 {
1517     Q_D(const QQuickTextControl);
1518     QTextBlock block = d->cursor.block();
1519     switch (property) {
1520     case Qt::ImCursorRectangle:
1521         return cursorRect();
1522     case Qt::ImFont:
1523         return QVariant(d->cursor.charFormat().font());
1524     case Qt::ImCursorPosition:
1525         return QVariant(d->cursor.position() - block.position());
1526     case Qt::ImSurroundingText:
1527         return QVariant(block.text());
1528     case Qt::ImCurrentSelection:
1529         return QVariant(d->cursor.selectedText());
1530     case Qt::ImMaximumTextLength:
1531         return QVariant(); // No limit.
1532     case Qt::ImAnchorPosition:
1533         return QVariant(d->cursor.anchor() - block.position());
1534     default:
1535         return QVariant();
1536     }
1537 }
1538
1539 void QQuickTextControl::setFocus(bool focus, Qt::FocusReason reason)
1540 {
1541     QFocusEvent ev(focus ? QEvent::FocusIn : QEvent::FocusOut,
1542                    reason);
1543     processEvent(&ev);
1544 }
1545
1546 void QQuickTextControlPrivate::focusEvent(QFocusEvent *e)
1547 {
1548     Q_Q(QQuickTextControl);
1549     emit q->updateRequest(q->selectionRect());
1550     if (e->gotFocus()) {
1551         setBlinkingCursorEnabled(interactionFlags & (Qt::TextEditable | Qt::TextSelectableByKeyboard));
1552     } else {
1553         commitPreedit();
1554         setBlinkingCursorEnabled(false);
1555
1556         if (cursorIsFocusIndicator
1557             && e->reason() != Qt::ActiveWindowFocusReason
1558             && e->reason() != Qt::PopupFocusReason
1559             && cursor.hasSelection()) {
1560             cursor.clearSelection();
1561         }
1562     }
1563     hasFocus = e->gotFocus();
1564 }
1565
1566 QString QQuickTextControlPrivate::anchorForCursor(const QTextCursor &anchorCursor) const
1567 {
1568     if (anchorCursor.hasSelection()) {
1569         QTextCursor cursor = anchorCursor;
1570         if (cursor.selectionStart() != cursor.position())
1571             cursor.setPosition(cursor.selectionStart());
1572         cursor.movePosition(QTextCursor::NextCharacter);
1573         QTextCharFormat fmt = cursor.charFormat();
1574         if (fmt.isAnchor() && fmt.hasProperty(QTextFormat::AnchorHref))
1575             return fmt.stringProperty(QTextFormat::AnchorHref);
1576     }
1577     return QString();
1578 }
1579
1580 QTextCursor QQuickTextControl::cursorForPosition(const QPointF &pos) const
1581 {
1582     Q_D(const QQuickTextControl);
1583     int cursorPos = hitTest(pos, Qt::FuzzyHit);
1584     if (cursorPos == -1)
1585         cursorPos = 0;
1586     QTextCursor c(d->doc);
1587     c.setPosition(cursorPos);
1588     return c;
1589 }
1590
1591 QRectF QQuickTextControl::cursorRect(const QTextCursor &cursor) const
1592 {
1593     Q_D(const QQuickTextControl);
1594     if (cursor.isNull())
1595         return QRectF();
1596
1597     return d->rectForPosition(cursor.position());
1598 }
1599
1600 QRectF QQuickTextControl::cursorRect() const
1601 {
1602     Q_D(const QQuickTextControl);
1603     return cursorRect(d->cursor);
1604 }
1605
1606 QRectF QQuickTextControlPrivate::cursorRectPlusUnicodeDirectionMarkers(const QTextCursor &cursor) const
1607 {
1608     if (cursor.isNull())
1609         return QRectF();
1610
1611     return rectForPosition(cursor.position()).adjusted(-4, 0, 4, 0);
1612 }
1613
1614 QString QQuickTextControl::anchorAt(const QPointF &pos) const
1615 {
1616     Q_D(const QQuickTextControl);
1617     return d->doc->documentLayout()->anchorAt(pos);
1618 }
1619
1620 QString QQuickTextControl::anchorAtCursor() const
1621 {
1622     Q_D(const QQuickTextControl);
1623
1624     return d->anchorForCursor(d->cursor);
1625 }
1626
1627 int QQuickTextControl::cursorWidth() const
1628 {
1629 #ifndef QT_NO_PROPERTIES
1630     Q_D(const QQuickTextControl);
1631     return d->doc->documentLayout()->property("cursorWidth").toInt();
1632 #else
1633     return 1;
1634 #endif
1635 }
1636
1637 void QQuickTextControl::setCursorWidth(int width)
1638 {
1639     Q_D(QQuickTextControl);
1640 #ifdef QT_NO_PROPERTIES
1641     Q_UNUSED(width);
1642 #else
1643     if (width == -1)
1644         width = textCursorWidth;
1645     d->doc->documentLayout()->setProperty("cursorWidth", width);
1646 #endif
1647     d->repaintCursor();
1648 }
1649
1650 bool QQuickTextControl::acceptRichText() const
1651 {
1652     Q_D(const QQuickTextControl);
1653     return d->acceptRichText;
1654 }
1655
1656 void QQuickTextControl::setAcceptRichText(bool accept)
1657 {
1658     Q_D(QQuickTextControl);
1659     d->acceptRichText = accept;
1660 }
1661
1662 void QQuickTextControl::setTextWidth(qreal width)
1663 {
1664     Q_D(QQuickTextControl);
1665     d->doc->setTextWidth(width);
1666 }
1667
1668 qreal QQuickTextControl::textWidth() const
1669 {
1670     Q_D(const QQuickTextControl);
1671     return d->doc->textWidth();
1672 }
1673
1674 QSizeF QQuickTextControl::size() const
1675 {
1676     Q_D(const QQuickTextControl);
1677     return d->doc->size();
1678 }
1679
1680 void QQuickTextControl::moveCursor(QTextCursor::MoveOperation op, QTextCursor::MoveMode mode)
1681 {
1682     Q_D(QQuickTextControl);
1683     const QTextCursor oldSelection = d->cursor;
1684     const bool moved = d->cursor.movePosition(op, mode);
1685     d->_q_updateCurrentCharFormatAndSelection();
1686     emit cursorRectangleChanged();
1687     d->repaintOldAndNewSelection(oldSelection);
1688     if (moved)
1689         emit cursorPositionChanged();
1690 }
1691
1692 bool QQuickTextControl::canPaste() const
1693 {
1694 #ifndef QT_NO_CLIPBOARD
1695     Q_D(const QQuickTextControl);
1696     if (d->interactionFlags & Qt::TextEditable) {
1697         const QMimeData *md = QGuiApplication::clipboard()->mimeData();
1698         return md && canInsertFromMimeData(md);
1699     }
1700 #endif
1701     return false;
1702 }
1703
1704 void QQuickTextControl::setCursorIsFocusIndicator(bool b)
1705 {
1706     Q_D(QQuickTextControl);
1707     d->cursorIsFocusIndicator = b;
1708     d->repaintCursor();
1709 }
1710
1711 bool QQuickTextControl::cursorIsFocusIndicator() const
1712 {
1713     Q_D(const QQuickTextControl);
1714     return d->cursorIsFocusIndicator;
1715 }
1716
1717 void QQuickTextControl::setWordSelectionEnabled(bool enabled)
1718 {
1719     Q_D(QQuickTextControl);
1720     d->wordSelectionEnabled = enabled;
1721 }
1722
1723 bool QQuickTextControl::isWordSelectionEnabled() const
1724 {
1725     Q_D(const QQuickTextControl);
1726     return d->wordSelectionEnabled;
1727 }
1728
1729 QMimeData *QQuickTextControl::createMimeDataFromSelection() const
1730 {
1731     Q_D(const QQuickTextControl);
1732     const QTextDocumentFragment fragment(d->cursor);
1733     return new QQuickTextEditMimeData(fragment);
1734 }
1735
1736 bool QQuickTextControl::canInsertFromMimeData(const QMimeData *source) const
1737 {
1738     Q_D(const QQuickTextControl);
1739     if (d->acceptRichText)
1740         return source->hasText()
1741             || source->hasHtml()
1742             || source->hasFormat(QLatin1String("application/x-qrichtext"))
1743             || source->hasFormat(QLatin1String("application/x-qt-richtext"));
1744     else
1745         return source->hasText();
1746 }
1747
1748 void QQuickTextControl::insertFromMimeData(const QMimeData *source)
1749 {
1750     Q_D(QQuickTextControl);
1751     if (!(d->interactionFlags & Qt::TextEditable) || !source)
1752         return;
1753
1754     bool hasData = false;
1755     QTextDocumentFragment fragment;
1756 #ifndef QT_NO_TEXTHTMLPARSER
1757     if (source->hasFormat(QLatin1String("application/x-qrichtext")) && d->acceptRichText) {
1758         // x-qrichtext is always UTF-8 (taken from Qt3 since we don't use it anymore).
1759         QString richtext = QString::fromUtf8(source->data(QLatin1String("application/x-qrichtext")));
1760         richtext.prepend(QLatin1String("<meta name=\"qrichtext\" content=\"1\" />"));
1761         fragment = QTextDocumentFragment::fromHtml(richtext, d->doc);
1762         hasData = true;
1763     } else if (source->hasHtml() && d->acceptRichText) {
1764         fragment = QTextDocumentFragment::fromHtml(source->html(), d->doc);
1765         hasData = true;
1766     } else {
1767         QString text = source->text();
1768         if (!text.isNull()) {
1769             fragment = QTextDocumentFragment::fromPlainText(text);
1770             hasData = true;
1771         }
1772     }
1773 #else
1774     fragment = QTextDocumentFragment::fromPlainText(source->text());
1775 #endif // QT_NO_TEXTHTMLPARSER
1776
1777     if (hasData)
1778         d->cursor.insertFragment(fragment);
1779     emit cursorRectangleChanged();
1780 }
1781
1782 void QQuickTextControlPrivate::activateLinkUnderCursor(QString href)
1783 {
1784     QTextCursor oldCursor = cursor;
1785
1786     if (href.isEmpty()) {
1787         QTextCursor tmp = cursor;
1788         if (tmp.selectionStart() != tmp.position())
1789             tmp.setPosition(tmp.selectionStart());
1790         tmp.movePosition(QTextCursor::NextCharacter);
1791         href = tmp.charFormat().anchorHref();
1792     }
1793     if (href.isEmpty())
1794         return;
1795
1796     if (!cursor.hasSelection()) {
1797         QTextBlock block = cursor.block();
1798         const int cursorPos = cursor.position();
1799
1800         QTextBlock::Iterator it = block.begin();
1801         QTextBlock::Iterator linkFragment;
1802
1803         for (; !it.atEnd(); ++it) {
1804             QTextFragment fragment = it.fragment();
1805             const int fragmentPos = fragment.position();
1806             if (fragmentPos <= cursorPos &&
1807                 fragmentPos + fragment.length() > cursorPos) {
1808                 linkFragment = it;
1809                 break;
1810             }
1811         }
1812
1813         if (!linkFragment.atEnd()) {
1814             it = linkFragment;
1815             cursor.setPosition(it.fragment().position());
1816             if (it != block.begin()) {
1817                 do {
1818                     --it;
1819                     QTextFragment fragment = it.fragment();
1820                     if (fragment.charFormat().anchorHref() != href)
1821                         break;
1822                     cursor.setPosition(fragment.position());
1823                 } while (it != block.begin());
1824             }
1825
1826             for (it = linkFragment; !it.atEnd(); ++it) {
1827                 QTextFragment fragment = it.fragment();
1828                 if (fragment.charFormat().anchorHref() != href)
1829                     break;
1830                 cursor.setPosition(fragment.position() + fragment.length(), QTextCursor::KeepAnchor);
1831             }
1832         }
1833     }
1834
1835     if (hasFocus) {
1836         cursorIsFocusIndicator = true;
1837     } else {
1838         cursorIsFocusIndicator = false;
1839         cursor.clearSelection();
1840     }
1841     repaintOldAndNewSelection(oldCursor);
1842
1843     emit q_func()->linkActivated(href);
1844 }
1845
1846 bool QQuickTextControlPrivate::isPreediting() const
1847 {
1848     QTextLayout *layout = cursor.block().layout();
1849     if (layout && !layout->preeditAreaText().isEmpty())
1850         return true;
1851
1852     return false;
1853 }
1854
1855 void QQuickTextControlPrivate::commitPreedit()
1856 {
1857     if (!isPreediting())
1858         return;
1859
1860     cursor.beginEditBlock();
1861     qApp->inputPanel()->reset();
1862
1863     if (!tentativeCommit.isEmpty()) {
1864         cursor.insertText(tentativeCommit);
1865         tentativeCommit.clear();
1866     }
1867
1868     preeditCursor = 0;
1869     QTextBlock block = cursor.block();
1870     QTextLayout *layout = block.layout();
1871     layout->setPreeditArea(-1, QString());
1872     layout->clearAdditionalFormats();
1873     cursor.endEditBlock();
1874 }
1875
1876 void QQuickTextControl::setTextInteractionFlags(Qt::TextInteractionFlags flags)
1877 {
1878     Q_D(QQuickTextControl);
1879     if (flags == d->interactionFlags)
1880         return;
1881     d->interactionFlags = flags;
1882
1883     if (d->hasFocus)
1884         d->setBlinkingCursorEnabled(flags & (Qt::TextEditable | Qt::TextSelectableByKeyboard));
1885 }
1886
1887 Qt::TextInteractionFlags QQuickTextControl::textInteractionFlags() const
1888 {
1889     Q_D(const QQuickTextControl);
1890     return d->interactionFlags;
1891 }
1892
1893 QString QQuickTextControl::toPlainText() const
1894 {
1895     Q_D(const QQuickTextControl);
1896     QString plainText = document()->toPlainText();
1897     if (!d->tentativeCommit.isEmpty())
1898         plainText.insert(textCursor().position(), d->tentativeCommit);
1899     return plainText;
1900 }
1901
1902 #ifndef QT_NO_TEXTHTMLPARSER
1903 QString QQuickTextControl::toHtml() const
1904 {
1905     // note: currently not including tentative commit
1906     return document()->toHtml();
1907 }
1908 #endif
1909
1910 QPalette QQuickTextControl::palette() const
1911 {
1912     Q_D(const QQuickTextControl);
1913     return d->palette;
1914 }
1915
1916 void QQuickTextControl::setPalette(const QPalette &pal)
1917 {
1918     Q_D(QQuickTextControl);
1919     d->palette = pal;
1920 }
1921
1922 bool QQuickTextControl::cursorOn() const
1923 {
1924     Q_D(const QQuickTextControl);
1925     return d->cursorOn;
1926 }
1927
1928 QAbstractTextDocumentLayout::PaintContext QQuickTextControl::getPaintContext() const
1929 {
1930     Q_D(const QQuickTextControl);
1931
1932     QAbstractTextDocumentLayout::PaintContext ctx;
1933
1934     ctx.palette = d->palette;
1935     if (d->cursorOn && d->isEnabled) {
1936         if (d->hideCursor)
1937             ctx.cursorPosition = -1;
1938         else if (d->preeditCursor != 0)
1939             ctx.cursorPosition = - (d->preeditCursor + 2);
1940         else
1941             ctx.cursorPosition = d->cursor.position();
1942     }
1943
1944     if (d->cursor.hasSelection()) {
1945         QAbstractTextDocumentLayout::Selection selection;
1946         selection.cursor = d->cursor;
1947         if (0 && d->cursorIsFocusIndicator) {
1948 #if 0
1949             // ###
1950             QStyleOption opt;
1951             opt.palette = ctx.palette;
1952             QStyleHintReturnVariant ret;
1953             QStyle *style = QGuiApplication::style();
1954             if (widget)
1955                 style = widget->style();
1956             style->styleHint(QStyle::SH_TextControl_FocusIndicatorTextCharFormat, &opt, widget, &ret);
1957             selection.format = qvariant_cast<QTextFormat>(ret.variant).toCharFormat();
1958 #endif
1959         } else {
1960             QPalette::ColorGroup cg = d->hasFocus ? QPalette::Active : QPalette::Inactive;
1961             selection.format.setBackground(ctx.palette.brush(cg, QPalette::Highlight));
1962             selection.format.setForeground(ctx.palette.brush(cg, QPalette::HighlightedText));
1963             if (fullWidthSelection)
1964                 selection.format.setProperty(QTextFormat::FullWidthSelection, true);
1965         }
1966         ctx.selections.append(selection);
1967     }
1968
1969     return ctx;
1970 }
1971
1972 void QQuickTextControl::drawContents(QPainter *p, const QRectF &rect)
1973 {
1974     Q_D(QQuickTextControl);
1975     p->save();
1976     QAbstractTextDocumentLayout::PaintContext ctx = getPaintContext();
1977     if (rect.isValid())
1978         p->setClipRect(rect, Qt::IntersectClip);
1979     ctx.clip = rect;
1980
1981     d->doc->documentLayout()->draw(p, ctx);
1982     p->restore();
1983 }
1984
1985 int QQuickTextControl::hitTest(const QPointF &point, Qt::HitTestAccuracy accuracy) const
1986 {
1987     Q_D(const QQuickTextControl);
1988     return d->doc->documentLayout()->hitTest(point, accuracy);
1989 }
1990
1991 QRectF QQuickTextControl::blockBoundingRect(const QTextBlock &block) const
1992 {
1993     Q_D(const QQuickTextControl);
1994     return d->doc->documentLayout()->blockBoundingRect(block);
1995 }
1996
1997
1998
1999 QStringList QQuickTextEditMimeData::formats() const
2000 {
2001     if (!fragment.isEmpty())
2002         return QStringList() << QString::fromLatin1("text/plain") << QString::fromLatin1("text/html")
2003 #ifndef QT_NO_TEXTODFWRITER
2004             << QString::fromLatin1("application/vnd.oasis.opendocument.text")
2005 #endif
2006         ;
2007     else
2008         return QMimeData::formats();
2009 }
2010
2011 QVariant QQuickTextEditMimeData::retrieveData(const QString &mimeType, QVariant::Type type) const
2012 {
2013     if (!fragment.isEmpty())
2014         setup();
2015     return QMimeData::retrieveData(mimeType, type);
2016 }
2017
2018 void QQuickTextEditMimeData::setup() const
2019 {
2020     QQuickTextEditMimeData *that = const_cast<QQuickTextEditMimeData *>(this);
2021 #ifndef QT_NO_TEXTHTMLPARSER
2022     that->setData(QLatin1String("text/html"), fragment.toHtml("utf-8").toUtf8());
2023 #endif
2024 #ifndef QT_NO_TEXTODFWRITER
2025     {
2026         QBuffer buffer;
2027         QTextDocumentWriter writer(&buffer, "ODF");
2028         writer.write(fragment);
2029         buffer.close();
2030         that->setData(QLatin1String("application/vnd.oasis.opendocument.text"), buffer.data());
2031     }
2032 #endif
2033     that->setText(fragment.toPlainText());
2034     fragment = QTextDocumentFragment();
2035 }
2036
2037
2038 QT_END_NAMESPACE
2039
2040 #include "moc_qquicktextcontrol_p.cpp"
2041
2042 #endif // QT_NO_TEXTCONTROL