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

Last change on this file since 846 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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