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

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

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

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