source: trunk/src/gui/text/qtextcontrol.cpp@ 318

Last change on this file since 318 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

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