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