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