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