|
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 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
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 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include "qtextcontrol_p.h" |
|
43 #include "qtextcontrol_p_p.h" |
|
44 |
|
45 #ifndef QT_NO_TEXTCONTROL |
|
46 |
|
47 #include <qfont.h> |
|
48 #include <qpainter.h> |
|
49 #include <qevent.h> |
|
50 #include <qdebug.h> |
|
51 #include <qmime.h> |
|
52 #include <qdrag.h> |
|
53 #include <qclipboard.h> |
|
54 #include <qmenu.h> |
|
55 #include <qstyle.h> |
|
56 #include <qtimer.h> |
|
57 #include "private/qtextdocumentlayout_p.h" |
|
58 #include "private/qabstracttextdocumentlayout_p.h" |
|
59 #include "private/qtextedit_p.h" |
|
60 #include "qtextdocument.h" |
|
61 #include "private/qtextdocument_p.h" |
|
62 #include "qtextlist.h" |
|
63 #include "private/qtextcontrol_p.h" |
|
64 #include "qgraphicssceneevent.h" |
|
65 #include "qprinter.h" |
|
66 #include "qtextdocumentwriter.h" |
|
67 |
|
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 |
|
91 QT_BEGIN_NAMESPACE |
|
92 |
|
93 #ifndef QT_NO_CONTEXTMENU |
|
94 #if defined(Q_WS_WIN) |
|
95 extern bool qt_use_rtl_extensions; |
|
96 #endif |
|
97 #endif |
|
98 |
|
99 // could go into QTextCursor... |
|
100 static 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 |
|
114 QTextControlPrivate::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 |
|
134 bool 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 |
|
296 void 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 |
|
309 void 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 |
|
329 void 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 |
|
346 void 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 |
|
365 void 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 |
|
384 void 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 |
|
402 void 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 |
|
417 void 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 |
|
503 void 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 |
|
529 void 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 |
|
538 void 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 |
|
548 void QTextControlPrivate::repaintCursor() |
|
549 { |
|
550 Q_Q(QTextControl); |
|
551 emit q->updateRequest(cursorRectPlusUnicodeDirectionMarkers(cursor)); |
|
552 } |
|
553 |
|
554 void 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 |
|
575 void 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 |
|
592 void QTextControlPrivate::_q_updateCurrentCharFormatAndSelection() |
|
593 { |
|
594 updateCurrentCharFormat(); |
|
595 selectionChanged(); |
|
596 } |
|
597 |
|
598 #ifndef QT_NO_CLIPBOARD |
|
599 void 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 |
|
610 void 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 |
|
619 void 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 |
|
629 void 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 |
|
643 void 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 |
|
707 void 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 |
|
737 void QTextControlPrivate::_q_deleteSelected() |
|
738 { |
|
739 if (!(interactionFlags & Qt::TextEditable) || !cursor.hasSelection()) |
|
740 return; |
|
741 cursor.removeSelectedText(); |
|
742 } |
|
743 |
|
744 void QTextControl::undo() |
|
745 { |
|
746 Q_D(QTextControl); |
|
747 d->repaintSelection(); |
|
748 d->doc->undo(&d->cursor); |
|
749 ensureCursorVisible(); |
|
750 } |
|
751 |
|
752 void QTextControl::redo() |
|
753 { |
|
754 Q_D(QTextControl); |
|
755 d->repaintSelection(); |
|
756 d->doc->redo(&d->cursor); |
|
757 ensureCursorVisible(); |
|
758 } |
|
759 |
|
760 QTextControl::QTextControl(QObject *parent) |
|
761 : QObject(*new QTextControlPrivate, parent) |
|
762 { |
|
763 Q_D(QTextControl); |
|
764 d->init(); |
|
765 } |
|
766 |
|
767 QTextControl::QTextControl(const QString &text, QObject *parent) |
|
768 : QObject(*new QTextControlPrivate, parent) |
|
769 { |
|
770 Q_D(QTextControl); |
|
771 d->init(Qt::RichText, text); |
|
772 } |
|
773 |
|
774 QTextControl::QTextControl(QTextDocument *doc, QObject *parent) |
|
775 : QObject(*new QTextControlPrivate, parent) |
|
776 { |
|
777 Q_D(QTextControl); |
|
778 d->init(Qt::RichText, QString(), doc); |
|
779 } |
|
780 |
|
781 QTextControl::~QTextControl() |
|
782 { |
|
783 } |
|
784 |
|
785 void 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 |
|
802 QTextDocument *QTextControl::document() const |
|
803 { |
|
804 Q_D(const QTextControl); |
|
805 return d->doc; |
|
806 } |
|
807 |
|
808 void 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 |
|
823 QTextCursor QTextControl::textCursor() const |
|
824 { |
|
825 Q_D(const QTextControl); |
|
826 return d->cursor; |
|
827 } |
|
828 |
|
829 #ifndef QT_NO_CLIPBOARD |
|
830 |
|
831 void 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 |
|
840 void 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 |
|
849 void QTextControl::paste() |
|
850 { |
|
851 const QMimeData *md = QApplication::clipboard()->mimeData(); |
|
852 if (md) |
|
853 insertFromMimeData(md); |
|
854 } |
|
855 #endif |
|
856 |
|
857 void QTextControl::clear() |
|
858 { |
|
859 Q_D(QTextControl); |
|
860 // clears and sets empty content |
|
861 d->extraSelections.clear(); |
|
862 d->setContent(); |
|
863 } |
|
864 |
|
865 |
|
866 void 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 |
|
876 void 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 |
|
883 void 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 |
|
1105 bool QTextControl::event(QEvent *e) |
|
1106 { |
|
1107 return QObject::event(e); |
|
1108 } |
|
1109 |
|
1110 void 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 |
|
1126 void QTextControl::setPlainText(const QString &text) |
|
1127 { |
|
1128 Q_D(QTextControl); |
|
1129 d->setContent(Qt::PlainText, text); |
|
1130 } |
|
1131 |
|
1132 void QTextControl::setHtml(const QString &text) |
|
1133 { |
|
1134 Q_D(QTextControl); |
|
1135 d->setContent(Qt::RichText, text); |
|
1136 } |
|
1137 |
|
1138 void 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 cursor.deletePreviousChar(); |
|
1203 } |
|
1204 goto accept; |
|
1205 } |
|
1206 #ifndef QT_NO_SHORTCUT |
|
1207 else if (e == QKeySequence::InsertParagraphSeparator) { |
|
1208 cursor.insertBlock(); |
|
1209 e->accept(); |
|
1210 goto accept; |
|
1211 } else if (e == QKeySequence::InsertLineSeparator) { |
|
1212 cursor.insertText(QString(QChar::LineSeparator)); |
|
1213 e->accept(); |
|
1214 goto accept; |
|
1215 } |
|
1216 #endif |
|
1217 if (false) { |
|
1218 } |
|
1219 #ifndef QT_NO_SHORTCUT |
|
1220 else if (e == QKeySequence::Undo) { |
|
1221 q->undo(); |
|
1222 } |
|
1223 else if (e == QKeySequence::Redo) { |
|
1224 q->redo(); |
|
1225 } |
|
1226 #ifndef QT_NO_CLIPBOARD |
|
1227 else if (e == QKeySequence::Cut) { |
|
1228 q->cut(); |
|
1229 } |
|
1230 else if (e == QKeySequence::Paste) { |
|
1231 q->paste(); |
|
1232 } |
|
1233 #endif |
|
1234 else if (e == QKeySequence::Delete) { |
|
1235 cursor.deleteChar(); |
|
1236 } |
|
1237 else if (e == QKeySequence::DeleteEndOfWord) { |
|
1238 if (!cursor.hasSelection()) |
|
1239 cursor.movePosition(QTextCursor::NextWord, QTextCursor::KeepAnchor); |
|
1240 cursor.removeSelectedText(); |
|
1241 } |
|
1242 else if (e == QKeySequence::DeleteStartOfWord) { |
|
1243 if (!cursor.hasSelection()) |
|
1244 cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor); |
|
1245 cursor.removeSelectedText(); |
|
1246 } |
|
1247 else if (e == QKeySequence::DeleteEndOfLine) { |
|
1248 QTextBlock block = cursor.block(); |
|
1249 if (cursor.position() == block.position() + block.length() - 2) |
|
1250 cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor); |
|
1251 else |
|
1252 cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); |
|
1253 cursor.removeSelectedText(); |
|
1254 } |
|
1255 #endif // QT_NO_SHORTCUT |
|
1256 else { |
|
1257 goto process; |
|
1258 } |
|
1259 goto accept; |
|
1260 |
|
1261 process: |
|
1262 { |
|
1263 QString text = e->text(); |
|
1264 if (!text.isEmpty() && (text.at(0).isPrint() || text.at(0) == QLatin1Char('\t'))) { |
|
1265 if (overwriteMode |
|
1266 // no need to call deleteChar() if we have a selection, insertText |
|
1267 // does it already |
|
1268 && !cursor.hasSelection() |
|
1269 && !cursor.atBlockEnd()) |
|
1270 cursor.deleteChar(); |
|
1271 |
|
1272 cursor.insertText(text); |
|
1273 selectionChanged(); |
|
1274 } else { |
|
1275 e->ignore(); |
|
1276 return; |
|
1277 } |
|
1278 } |
|
1279 |
|
1280 accept: |
|
1281 |
|
1282 e->accept(); |
|
1283 cursorOn = true; |
|
1284 |
|
1285 q->ensureCursorVisible(); |
|
1286 |
|
1287 updateCurrentCharFormat(); |
|
1288 } |
|
1289 |
|
1290 QVariant QTextControl::loadResource(int type, const QUrl &name) |
|
1291 { |
|
1292 #ifdef QT_NO_TEXTEDIT |
|
1293 Q_UNUSED(type); |
|
1294 Q_UNUSED(name); |
|
1295 #else |
|
1296 if (QTextEdit *textEdit = qobject_cast<QTextEdit *>(parent())) { |
|
1297 QUrl resolvedName = textEdit->d_func()->resolveUrl(name); |
|
1298 return textEdit->loadResource(type, resolvedName); |
|
1299 } |
|
1300 #endif |
|
1301 return QVariant(); |
|
1302 } |
|
1303 |
|
1304 void QTextControlPrivate::_q_updateBlock(const QTextBlock &block) |
|
1305 { |
|
1306 Q_Q(QTextControl); |
|
1307 QRectF br = q->blockBoundingRect(block); |
|
1308 br.setRight(qreal(INT_MAX)); // the block might have shrunk |
|
1309 emit q->updateRequest(br); |
|
1310 } |
|
1311 |
|
1312 QRectF QTextControlPrivate::rectForPosition(int position) const |
|
1313 { |
|
1314 Q_Q(const QTextControl); |
|
1315 const QTextBlock block = doc->findBlock(position); |
|
1316 if (!block.isValid()) |
|
1317 return QRectF(); |
|
1318 const QAbstractTextDocumentLayout *docLayout = doc->documentLayout(); |
|
1319 const QTextLayout *layout = block.layout(); |
|
1320 const QPointF layoutPos = q->blockBoundingRect(block).topLeft(); |
|
1321 int relativePos = position - block.position(); |
|
1322 if (preeditCursor != 0) { |
|
1323 int preeditPos = layout->preeditAreaPosition(); |
|
1324 if (relativePos == preeditPos) |
|
1325 relativePos += preeditCursor; |
|
1326 else if (relativePos > preeditPos) |
|
1327 relativePos += layout->preeditAreaText().length(); |
|
1328 } |
|
1329 QTextLine line = layout->lineForTextPosition(relativePos); |
|
1330 |
|
1331 int cursorWidth; |
|
1332 { |
|
1333 bool ok = false; |
|
1334 #ifndef QT_NO_PROPERTIES |
|
1335 cursorWidth = docLayout->property("cursorWidth").toInt(&ok); |
|
1336 #endif |
|
1337 if (!ok) |
|
1338 cursorWidth = 1; |
|
1339 } |
|
1340 |
|
1341 QRectF r; |
|
1342 |
|
1343 if (line.isValid()) { |
|
1344 qreal x = line.cursorToX(relativePos); |
|
1345 qreal w = 0; |
|
1346 if (overwriteMode) { |
|
1347 if (relativePos < line.textLength() - line.textStart()) |
|
1348 w = line.cursorToX(relativePos + 1) - x; |
|
1349 else |
|
1350 w = QFontMetrics(block.layout()->font()).width(QLatin1Char(' ')); // in sync with QTextLine::draw() |
|
1351 } |
|
1352 r = QRectF(layoutPos.x() + x, layoutPos.y() + line.y(), |
|
1353 cursorWidth + w, line.height()); |
|
1354 } else { |
|
1355 r = QRectF(layoutPos.x(), layoutPos.y(), cursorWidth, 10); // #### correct height |
|
1356 } |
|
1357 |
|
1358 return r; |
|
1359 } |
|
1360 |
|
1361 static inline bool firstFramePosLessThanCursorPos(QTextFrame *frame, int position) |
|
1362 { |
|
1363 return frame->firstPosition() < position; |
|
1364 } |
|
1365 |
|
1366 static inline bool cursorPosLessThanLastFramePos(int position, QTextFrame *frame) |
|
1367 { |
|
1368 return position < frame->lastPosition(); |
|
1369 } |
|
1370 |
|
1371 static QRectF boundingRectOfFloatsInSelection(const QTextCursor &cursor) |
|
1372 { |
|
1373 QRectF r; |
|
1374 QTextFrame *frame = cursor.currentFrame(); |
|
1375 const QList<QTextFrame *> children = frame->childFrames(); |
|
1376 |
|
1377 const QList<QTextFrame *>::ConstIterator firstFrame = qLowerBound(children.constBegin(), children.constEnd(), |
|
1378 cursor.selectionStart(), firstFramePosLessThanCursorPos); |
|
1379 const QList<QTextFrame *>::ConstIterator lastFrame = qUpperBound(children.constBegin(), children.constEnd(), |
|
1380 cursor.selectionEnd(), cursorPosLessThanLastFramePos); |
|
1381 for (QList<QTextFrame *>::ConstIterator it = firstFrame; it != lastFrame; ++it) { |
|
1382 if ((*it)->frameFormat().position() != QTextFrameFormat::InFlow) |
|
1383 r |= frame->document()->documentLayout()->frameBoundingRect(*it); |
|
1384 } |
|
1385 return r; |
|
1386 } |
|
1387 |
|
1388 QRectF QTextControl::selectionRect(const QTextCursor &cursor) const |
|
1389 { |
|
1390 Q_D(const QTextControl); |
|
1391 |
|
1392 QRectF r = d->rectForPosition(cursor.selectionStart()); |
|
1393 |
|
1394 if (cursor.hasComplexSelection() && cursor.currentTable()) { |
|
1395 QTextTable *table = cursor.currentTable(); |
|
1396 |
|
1397 r = d->doc->documentLayout()->frameBoundingRect(table); |
|
1398 /* |
|
1399 int firstRow, numRows, firstColumn, numColumns; |
|
1400 cursor.selectedTableCells(&firstRow, &numRows, &firstColumn, &numColumns); |
|
1401 |
|
1402 const QTextTableCell firstCell = table->cellAt(firstRow, firstColumn); |
|
1403 const QTextTableCell lastCell = table->cellAt(firstRow + numRows - 1, firstColumn + numColumns - 1); |
|
1404 |
|
1405 const QAbstractTextDocumentLayout * const layout = doc->documentLayout(); |
|
1406 |
|
1407 QRectF tableSelRect = layout->blockBoundingRect(firstCell.firstCursorPosition().block()); |
|
1408 |
|
1409 for (int col = firstColumn; col < firstColumn + numColumns; ++col) { |
|
1410 const QTextTableCell cell = table->cellAt(firstRow, col); |
|
1411 const qreal y = layout->blockBoundingRect(cell.firstCursorPosition().block()).top(); |
|
1412 |
|
1413 tableSelRect.setTop(qMin(tableSelRect.top(), y)); |
|
1414 } |
|
1415 |
|
1416 for (int row = firstRow; row < firstRow + numRows; ++row) { |
|
1417 const QTextTableCell cell = table->cellAt(row, firstColumn); |
|
1418 const qreal x = layout->blockBoundingRect(cell.firstCursorPosition().block()).left(); |
|
1419 |
|
1420 tableSelRect.setLeft(qMin(tableSelRect.left(), x)); |
|
1421 } |
|
1422 |
|
1423 for (int col = firstColumn; col < firstColumn + numColumns; ++col) { |
|
1424 const QTextTableCell cell = table->cellAt(firstRow + numRows - 1, col); |
|
1425 const qreal y = layout->blockBoundingRect(cell.lastCursorPosition().block()).bottom(); |
|
1426 |
|
1427 tableSelRect.setBottom(qMax(tableSelRect.bottom(), y)); |
|
1428 } |
|
1429 |
|
1430 for (int row = firstRow; row < firstRow + numRows; ++row) { |
|
1431 const QTextTableCell cell = table->cellAt(row, firstColumn + numColumns - 1); |
|
1432 const qreal x = layout->blockBoundingRect(cell.lastCursorPosition().block()).right(); |
|
1433 |
|
1434 tableSelRect.setRight(qMax(tableSelRect.right(), x)); |
|
1435 } |
|
1436 |
|
1437 r = tableSelRect.toRect(); |
|
1438 */ |
|
1439 } else if (cursor.hasSelection()) { |
|
1440 const int position = cursor.selectionStart(); |
|
1441 const int anchor = cursor.selectionEnd(); |
|
1442 const QTextBlock posBlock = d->doc->findBlock(position); |
|
1443 const QTextBlock anchorBlock = d->doc->findBlock(anchor); |
|
1444 if (posBlock == anchorBlock && posBlock.isValid() && posBlock.layout()->lineCount()) { |
|
1445 const QTextLine posLine = posBlock.layout()->lineForTextPosition(position - posBlock.position()); |
|
1446 const QTextLine anchorLine = anchorBlock.layout()->lineForTextPosition(anchor - anchorBlock.position()); |
|
1447 |
|
1448 const int firstLine = qMin(posLine.lineNumber(), anchorLine.lineNumber()); |
|
1449 const int lastLine = qMax(posLine.lineNumber(), anchorLine.lineNumber()); |
|
1450 const QTextLayout *layout = posBlock.layout(); |
|
1451 r = QRectF(); |
|
1452 for (int i = firstLine; i <= lastLine; ++i) { |
|
1453 r |= layout->lineAt(i).rect(); |
|
1454 r |= layout->lineAt(i).naturalTextRect(); // might be bigger in the case of wrap not enabled |
|
1455 } |
|
1456 r.translate(blockBoundingRect(posBlock).topLeft()); |
|
1457 } else { |
|
1458 QRectF anchorRect = d->rectForPosition(cursor.selectionEnd()); |
|
1459 r |= anchorRect; |
|
1460 r |= boundingRectOfFloatsInSelection(cursor); |
|
1461 QRectF frameRect(d->doc->documentLayout()->frameBoundingRect(cursor.currentFrame())); |
|
1462 r.setLeft(frameRect.left()); |
|
1463 r.setRight(frameRect.right()); |
|
1464 } |
|
1465 if (r.isValid()) |
|
1466 r.adjust(-1, -1, 1, 1); |
|
1467 } |
|
1468 |
|
1469 return r; |
|
1470 } |
|
1471 |
|
1472 QRectF QTextControl::selectionRect() const |
|
1473 { |
|
1474 Q_D(const QTextControl); |
|
1475 return selectionRect(d->cursor); |
|
1476 } |
|
1477 |
|
1478 void QTextControlPrivate::mousePressEvent(QEvent *e, Qt::MouseButton button, const QPointF &pos, Qt::KeyboardModifiers modifiers, |
|
1479 Qt::MouseButtons buttons, const QPoint &globalPos) |
|
1480 { |
|
1481 Q_Q(QTextControl); |
|
1482 |
|
1483 if (interactionFlags & Qt::LinksAccessibleByMouse) { |
|
1484 anchorOnMousePress = q->anchorAt(pos); |
|
1485 |
|
1486 if (cursorIsFocusIndicator) { |
|
1487 cursorIsFocusIndicator = false; |
|
1488 repaintSelection(); |
|
1489 cursor.clearSelection(); |
|
1490 } |
|
1491 } |
|
1492 if (!(button & Qt::LeftButton) || |
|
1493 !((interactionFlags & Qt::TextSelectableByMouse) || (interactionFlags & Qt::TextEditable))) { |
|
1494 e->ignore(); |
|
1495 return; |
|
1496 } |
|
1497 |
|
1498 cursorIsFocusIndicator = false; |
|
1499 const QTextCursor oldSelection = cursor; |
|
1500 const int oldCursorPos = cursor.position(); |
|
1501 |
|
1502 mousePressed = true; |
|
1503 #ifndef QT_NO_DRAGANDDROP |
|
1504 mightStartDrag = false; |
|
1505 #endif |
|
1506 |
|
1507 if (trippleClickTimer.isActive() |
|
1508 && ((pos - trippleClickPoint).toPoint().manhattanLength() < QApplication::startDragDistance())) { |
|
1509 |
|
1510 cursor.movePosition(QTextCursor::StartOfBlock); |
|
1511 cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); |
|
1512 cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor); |
|
1513 selectedBlockOnTrippleClick = cursor; |
|
1514 |
|
1515 anchorOnMousePress = QString(); |
|
1516 |
|
1517 trippleClickTimer.stop(); |
|
1518 } else { |
|
1519 int cursorPos = q->hitTest(pos, Qt::FuzzyHit); |
|
1520 if (cursorPos == -1) { |
|
1521 e->ignore(); |
|
1522 return; |
|
1523 } |
|
1524 |
|
1525 #if !defined(QT_NO_IM) |
|
1526 QTextLayout *layout = cursor.block().layout(); |
|
1527 if (contextWidget && layout && !layout->preeditAreaText().isEmpty()) { |
|
1528 QInputContext *ctx = inputContext(); |
|
1529 if (ctx) { |
|
1530 QMouseEvent ev(QEvent::MouseButtonPress, contextWidget->mapFromGlobal(globalPos), globalPos, |
|
1531 button, buttons, modifiers); |
|
1532 ctx->mouseHandler(cursorPos - cursor.position(), &ev); |
|
1533 } |
|
1534 if (!layout->preeditAreaText().isEmpty()) { |
|
1535 e->ignore(); |
|
1536 return; |
|
1537 } |
|
1538 } |
|
1539 #endif |
|
1540 if (modifiers == Qt::ShiftModifier) { |
|
1541 if (selectedBlockOnTrippleClick.hasSelection()) |
|
1542 extendBlockwiseSelection(cursorPos); |
|
1543 else if (selectedWordOnDoubleClick.hasSelection()) |
|
1544 extendWordwiseSelection(cursorPos, pos.x()); |
|
1545 else |
|
1546 setCursorPosition(cursorPos, QTextCursor::KeepAnchor); |
|
1547 } else { |
|
1548 |
|
1549 if (cursor.hasSelection() |
|
1550 && !cursorIsFocusIndicator |
|
1551 && cursorPos >= cursor.selectionStart() |
|
1552 && cursorPos <= cursor.selectionEnd() |
|
1553 && q->hitTest(pos, Qt::ExactHit) != -1) { |
|
1554 #ifndef QT_NO_DRAGANDDROP |
|
1555 mightStartDrag = true; |
|
1556 dragStartPos = pos.toPoint(); |
|
1557 #endif |
|
1558 return; |
|
1559 } |
|
1560 |
|
1561 setCursorPosition(cursorPos); |
|
1562 } |
|
1563 } |
|
1564 |
|
1565 if (interactionFlags & Qt::TextEditable) { |
|
1566 q->ensureCursorVisible(); |
|
1567 if (cursor.position() != oldCursorPos) |
|
1568 emit q->cursorPositionChanged(); |
|
1569 _q_updateCurrentCharFormatAndSelection(); |
|
1570 } else { |
|
1571 if (cursor.position() != oldCursorPos) |
|
1572 emit q->cursorPositionChanged(); |
|
1573 selectionChanged(); |
|
1574 } |
|
1575 repaintOldAndNewSelection(oldSelection); |
|
1576 hadSelectionOnMousePress = cursor.hasSelection(); |
|
1577 } |
|
1578 |
|
1579 void QTextControlPrivate::mouseMoveEvent(Qt::MouseButtons buttons, const QPointF &mousePos) |
|
1580 { |
|
1581 Q_Q(QTextControl); |
|
1582 |
|
1583 if (interactionFlags & Qt::LinksAccessibleByMouse) { |
|
1584 QString anchor = q->anchorAt(mousePos); |
|
1585 if (anchor != highlightedAnchor) { |
|
1586 highlightedAnchor = anchor; |
|
1587 emit q->linkHovered(anchor); |
|
1588 } |
|
1589 } |
|
1590 |
|
1591 if (!(buttons & Qt::LeftButton)) |
|
1592 return; |
|
1593 |
|
1594 if (!((interactionFlags & Qt::TextSelectableByMouse) || (interactionFlags & Qt::TextEditable))) |
|
1595 return; |
|
1596 |
|
1597 if (!(mousePressed |
|
1598 || selectedWordOnDoubleClick.hasSelection() |
|
1599 || selectedBlockOnTrippleClick.hasSelection())) |
|
1600 return; |
|
1601 |
|
1602 const QTextCursor oldSelection = cursor; |
|
1603 const int oldCursorPos = cursor.position(); |
|
1604 |
|
1605 if (mightStartDrag) { |
|
1606 if ((mousePos.toPoint() - dragStartPos).manhattanLength() > QApplication::startDragDistance()) |
|
1607 startDrag(); |
|
1608 return; |
|
1609 } |
|
1610 const qreal mouseX = qreal(mousePos.x()); |
|
1611 |
|
1612 #if !defined(QT_NO_IM) |
|
1613 QTextLayout *layout = cursor.block().layout(); |
|
1614 if (layout && !layout->preeditAreaText().isEmpty()) |
|
1615 return; |
|
1616 #endif |
|
1617 |
|
1618 int newCursorPos = q->hitTest(mousePos, Qt::FuzzyHit); |
|
1619 if (newCursorPos == -1) |
|
1620 return; |
|
1621 |
|
1622 if (selectedBlockOnTrippleClick.hasSelection()) |
|
1623 extendBlockwiseSelection(newCursorPos); |
|
1624 else if (selectedWordOnDoubleClick.hasSelection()) |
|
1625 extendWordwiseSelection(newCursorPos, mouseX); |
|
1626 else |
|
1627 setCursorPosition(newCursorPos, QTextCursor::KeepAnchor); |
|
1628 |
|
1629 if (interactionFlags & Qt::TextEditable) { |
|
1630 // don't call ensureVisible for the visible cursor to avoid jumping |
|
1631 // scrollbars. the autoscrolling ensures smooth scrolling if necessary. |
|
1632 //q->ensureCursorVisible(); |
|
1633 if (cursor.position() != oldCursorPos) |
|
1634 emit q->cursorPositionChanged(); |
|
1635 _q_updateCurrentCharFormatAndSelection(); |
|
1636 #ifndef QT_NO_IM |
|
1637 if (QInputContext *ic = inputContext()) { |
|
1638 ic->update(); |
|
1639 } |
|
1640 #endif //QT_NO_IM |
|
1641 } else { |
|
1642 //emit q->visibilityRequest(QRectF(mousePos, QSizeF(1, 1))); |
|
1643 if (cursor.position() != oldCursorPos) |
|
1644 emit q->cursorPositionChanged(); |
|
1645 } |
|
1646 selectionChanged(true); |
|
1647 repaintOldAndNewSelection(oldSelection); |
|
1648 } |
|
1649 |
|
1650 void QTextControlPrivate::mouseReleaseEvent(Qt::MouseButton button, const QPointF &pos) |
|
1651 { |
|
1652 Q_Q(QTextControl); |
|
1653 |
|
1654 const QTextCursor oldSelection = cursor; |
|
1655 const int oldCursorPos = cursor.position(); |
|
1656 |
|
1657 #ifndef QT_NO_DRAGANDDROP |
|
1658 if (mightStartDrag && (button & Qt::LeftButton)) { |
|
1659 mousePressed = false; |
|
1660 setCursorPosition(pos); |
|
1661 cursor.clearSelection(); |
|
1662 selectionChanged(); |
|
1663 } |
|
1664 #endif |
|
1665 if (mousePressed) { |
|
1666 mousePressed = false; |
|
1667 #ifndef QT_NO_CLIPBOARD |
|
1668 if (interactionFlags & Qt::TextSelectableByMouse) { |
|
1669 setClipboardSelection(); |
|
1670 selectionChanged(true); |
|
1671 } |
|
1672 } else if (button == Qt::MidButton |
|
1673 && (interactionFlags & Qt::TextEditable) |
|
1674 && QApplication::clipboard()->supportsSelection()) { |
|
1675 setCursorPosition(pos); |
|
1676 const QMimeData *md = QApplication::clipboard()->mimeData(QClipboard::Selection); |
|
1677 if (md) |
|
1678 q->insertFromMimeData(md); |
|
1679 #endif |
|
1680 } |
|
1681 |
|
1682 repaintOldAndNewSelection(oldSelection); |
|
1683 |
|
1684 if (cursor.position() != oldCursorPos) |
|
1685 emit q->cursorPositionChanged(); |
|
1686 |
|
1687 if (interactionFlags & Qt::LinksAccessibleByMouse) { |
|
1688 if (!(button & Qt::LeftButton)) |
|
1689 return; |
|
1690 |
|
1691 const QString anchor = q->anchorAt(pos); |
|
1692 |
|
1693 if (anchor.isEmpty()) |
|
1694 return; |
|
1695 |
|
1696 if (!cursor.hasSelection() |
|
1697 || (anchor == anchorOnMousePress && hadSelectionOnMousePress)) { |
|
1698 |
|
1699 const int anchorPos = q->hitTest(pos, Qt::ExactHit); |
|
1700 if (anchorPos != -1) { |
|
1701 cursor.setPosition(anchorPos); |
|
1702 |
|
1703 QString anchor = anchorOnMousePress; |
|
1704 anchorOnMousePress = QString(); |
|
1705 activateLinkUnderCursor(anchor); |
|
1706 } |
|
1707 } |
|
1708 } |
|
1709 } |
|
1710 |
|
1711 void QTextControlPrivate::mouseDoubleClickEvent(QEvent *e, Qt::MouseButton button, const QPointF &pos) |
|
1712 { |
|
1713 Q_Q(QTextControl); |
|
1714 if (button != Qt::LeftButton |
|
1715 || !(interactionFlags & Qt::TextSelectableByMouse)) { |
|
1716 e->ignore(); |
|
1717 return; |
|
1718 } |
|
1719 #if !defined(QT_NO_IM) |
|
1720 QTextLayout *layout = cursor.block().layout(); |
|
1721 if (layout && !layout->preeditAreaText().isEmpty()) |
|
1722 return; |
|
1723 #endif |
|
1724 |
|
1725 #ifndef QT_NO_DRAGANDDROP |
|
1726 mightStartDrag = false; |
|
1727 #endif |
|
1728 const QTextCursor oldSelection = cursor; |
|
1729 setCursorPosition(pos); |
|
1730 QTextLine line = currentTextLine(cursor); |
|
1731 bool doEmit = false; |
|
1732 if (line.isValid() && line.textLength()) { |
|
1733 cursor.select(QTextCursor::WordUnderCursor); |
|
1734 doEmit = true; |
|
1735 } |
|
1736 repaintOldAndNewSelection(oldSelection); |
|
1737 |
|
1738 cursorIsFocusIndicator = false; |
|
1739 selectedWordOnDoubleClick = cursor; |
|
1740 |
|
1741 trippleClickPoint = pos; |
|
1742 trippleClickTimer.start(QApplication::doubleClickInterval(), q); |
|
1743 if (doEmit) { |
|
1744 selectionChanged(); |
|
1745 #ifndef QT_NO_CLIPBOARD |
|
1746 setClipboardSelection(); |
|
1747 #endif |
|
1748 emit q->cursorPositionChanged(); |
|
1749 } |
|
1750 } |
|
1751 |
|
1752 void QTextControlPrivate::contextMenuEvent(const QPoint &screenPos, const QPointF &docPos, QWidget *contextWidget) |
|
1753 { |
|
1754 #ifdef QT_NO_CONTEXTMENU |
|
1755 Q_UNUSED(screenPos); |
|
1756 Q_UNUSED(docPos); |
|
1757 Q_UNUSED(contextWidget); |
|
1758 #else |
|
1759 Q_Q(QTextControl); |
|
1760 if (!hasFocus) |
|
1761 return; |
|
1762 QMenu *menu = q->createStandardContextMenu(docPos, contextWidget); |
|
1763 if (!menu) |
|
1764 return; |
|
1765 menu->exec(screenPos); |
|
1766 delete menu; |
|
1767 #endif |
|
1768 } |
|
1769 |
|
1770 bool QTextControlPrivate::dragEnterEvent(QEvent *e, const QMimeData *mimeData) |
|
1771 { |
|
1772 Q_Q(QTextControl); |
|
1773 if (!(interactionFlags & Qt::TextEditable) || !q->canInsertFromMimeData(mimeData)) { |
|
1774 e->ignore(); |
|
1775 return false; |
|
1776 } |
|
1777 |
|
1778 dndFeedbackCursor = QTextCursor(); |
|
1779 |
|
1780 return true; // accept proposed action |
|
1781 } |
|
1782 |
|
1783 void QTextControlPrivate::dragLeaveEvent() |
|
1784 { |
|
1785 Q_Q(QTextControl); |
|
1786 |
|
1787 const QRectF crect = q->cursorRect(dndFeedbackCursor); |
|
1788 dndFeedbackCursor = QTextCursor(); |
|
1789 |
|
1790 if (crect.isValid()) |
|
1791 emit q->updateRequest(crect); |
|
1792 } |
|
1793 |
|
1794 bool QTextControlPrivate::dragMoveEvent(QEvent *e, const QMimeData *mimeData, const QPointF &pos) |
|
1795 { |
|
1796 Q_Q(QTextControl); |
|
1797 if (!(interactionFlags & Qt::TextEditable) || !q->canInsertFromMimeData(mimeData)) { |
|
1798 e->ignore(); |
|
1799 return false; |
|
1800 } |
|
1801 |
|
1802 const int cursorPos = q->hitTest(pos, Qt::FuzzyHit); |
|
1803 if (cursorPos != -1) { |
|
1804 QRectF crect = q->cursorRect(dndFeedbackCursor); |
|
1805 if (crect.isValid()) |
|
1806 emit q->updateRequest(crect); |
|
1807 |
|
1808 dndFeedbackCursor = cursor; |
|
1809 dndFeedbackCursor.setPosition(cursorPos); |
|
1810 |
|
1811 crect = q->cursorRect(dndFeedbackCursor); |
|
1812 emit q->updateRequest(crect); |
|
1813 } |
|
1814 |
|
1815 return true; // accept proposed action |
|
1816 } |
|
1817 |
|
1818 bool QTextControlPrivate::dropEvent(const QMimeData *mimeData, const QPointF &pos, Qt::DropAction dropAction, QWidget *source) |
|
1819 { |
|
1820 Q_Q(QTextControl); |
|
1821 dndFeedbackCursor = QTextCursor(); |
|
1822 |
|
1823 if (!(interactionFlags & Qt::TextEditable) || !q->canInsertFromMimeData(mimeData)) |
|
1824 return false; |
|
1825 |
|
1826 repaintSelection(); |
|
1827 |
|
1828 QTextCursor insertionCursor = q->cursorForPosition(pos); |
|
1829 insertionCursor.beginEditBlock(); |
|
1830 |
|
1831 if (dropAction == Qt::MoveAction && source == contextWidget) |
|
1832 cursor.removeSelectedText(); |
|
1833 |
|
1834 cursor = insertionCursor; |
|
1835 q->insertFromMimeData(mimeData); |
|
1836 insertionCursor.endEditBlock(); |
|
1837 q->ensureCursorVisible(); |
|
1838 return true; // accept proposed action |
|
1839 } |
|
1840 |
|
1841 void QTextControlPrivate::inputMethodEvent(QInputMethodEvent *e) |
|
1842 { |
|
1843 Q_Q(QTextControl); |
|
1844 if (!(interactionFlags & Qt::TextEditable) || cursor.isNull()) { |
|
1845 e->ignore(); |
|
1846 return; |
|
1847 } |
|
1848 bool isGettingInput = !e->commitString().isEmpty() |
|
1849 || e->preeditString() != cursor.block().layout()->preeditAreaText() |
|
1850 || e->replacementLength() > 0; |
|
1851 |
|
1852 cursor.beginEditBlock(); |
|
1853 if (isGettingInput) { |
|
1854 cursor.removeSelectedText(); |
|
1855 } |
|
1856 |
|
1857 // insert commit string |
|
1858 if (!e->commitString().isEmpty() || e->replacementLength()) { |
|
1859 QTextCursor c = cursor; |
|
1860 c.setPosition(c.position() + e->replacementStart()); |
|
1861 c.setPosition(c.position() + e->replacementLength(), QTextCursor::KeepAnchor); |
|
1862 c.insertText(e->commitString()); |
|
1863 } |
|
1864 |
|
1865 for (int i = 0; i < e->attributes().size(); ++i) { |
|
1866 const QInputMethodEvent::Attribute &a = e->attributes().at(i); |
|
1867 if (a.type == QInputMethodEvent::Selection) { |
|
1868 QTextCursor oldCursor = cursor; |
|
1869 int blockStart = a.start + cursor.block().position(); |
|
1870 cursor.setPosition(blockStart, QTextCursor::MoveAnchor); |
|
1871 cursor.setPosition(blockStart + a.length, QTextCursor::KeepAnchor); |
|
1872 q->ensureCursorVisible(); |
|
1873 repaintOldAndNewSelection(oldCursor); |
|
1874 } |
|
1875 } |
|
1876 |
|
1877 QTextBlock block = cursor.block(); |
|
1878 QTextLayout *layout = block.layout(); |
|
1879 if (isGettingInput) |
|
1880 layout->setPreeditArea(cursor.position() - block.position(), e->preeditString()); |
|
1881 QList<QTextLayout::FormatRange> overrides; |
|
1882 preeditCursor = e->preeditString().length(); |
|
1883 hideCursor = false; |
|
1884 for (int i = 0; i < e->attributes().size(); ++i) { |
|
1885 const QInputMethodEvent::Attribute &a = e->attributes().at(i); |
|
1886 if (a.type == QInputMethodEvent::Cursor) { |
|
1887 preeditCursor = a.start; |
|
1888 hideCursor = !a.length; |
|
1889 } else if (a.type == QInputMethodEvent::TextFormat) { |
|
1890 QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat(); |
|
1891 if (f.isValid()) { |
|
1892 QTextLayout::FormatRange o; |
|
1893 o.start = a.start + cursor.position() - block.position(); |
|
1894 o.length = a.length; |
|
1895 o.format = f; |
|
1896 overrides.append(o); |
|
1897 } |
|
1898 } |
|
1899 } |
|
1900 layout->setAdditionalFormats(overrides); |
|
1901 cursor.endEditBlock(); |
|
1902 } |
|
1903 |
|
1904 QVariant QTextControl::inputMethodQuery(Qt::InputMethodQuery property) const |
|
1905 { |
|
1906 Q_D(const QTextControl); |
|
1907 QTextBlock block = d->cursor.block(); |
|
1908 switch(property) { |
|
1909 case Qt::ImMicroFocus: |
|
1910 return cursorRect(); |
|
1911 case Qt::ImFont: |
|
1912 return QVariant(d->cursor.charFormat().font()); |
|
1913 case Qt::ImCursorPosition: |
|
1914 return QVariant(d->cursor.position() - block.position()); |
|
1915 case Qt::ImSurroundingText: |
|
1916 return QVariant(block.text()); |
|
1917 case Qt::ImCurrentSelection: |
|
1918 return QVariant(d->cursor.selectedText()); |
|
1919 case Qt::ImMaximumTextLength: |
|
1920 return QVariant(); // No limit. |
|
1921 case Qt::ImAnchorPosition: |
|
1922 return QVariant(qBound(0, d->cursor.anchor() - block.position(), block.length())); |
|
1923 default: |
|
1924 return QVariant(); |
|
1925 } |
|
1926 } |
|
1927 |
|
1928 void QTextControl::setFocus(bool focus, Qt::FocusReason reason) |
|
1929 { |
|
1930 QFocusEvent ev(focus ? QEvent::FocusIn : QEvent::FocusOut, |
|
1931 reason); |
|
1932 processEvent(&ev); |
|
1933 } |
|
1934 |
|
1935 void QTextControlPrivate::focusEvent(QFocusEvent *e) |
|
1936 { |
|
1937 Q_Q(QTextControl); |
|
1938 emit q->updateRequest(q->selectionRect()); |
|
1939 if (e->gotFocus()) { |
|
1940 #ifdef QT_KEYPAD_NAVIGATION |
|
1941 if (!QApplication::keypadNavigationEnabled() || (hasEditFocus && (e->reason() == Qt::PopupFocusReason |
|
1942 #ifdef Q_OS_SYMBIAN |
|
1943 || e->reason() == Qt::ActiveWindowFocusReason |
|
1944 #endif |
|
1945 ))) { |
|
1946 #endif |
|
1947 cursorOn = (interactionFlags & Qt::TextSelectableByKeyboard); |
|
1948 if (interactionFlags & Qt::TextEditable) { |
|
1949 setBlinkingCursorEnabled(true); |
|
1950 } |
|
1951 #ifdef QT_KEYPAD_NAVIGATION |
|
1952 } |
|
1953 #endif |
|
1954 } else { |
|
1955 setBlinkingCursorEnabled(false); |
|
1956 |
|
1957 if (cursorIsFocusIndicator |
|
1958 && e->reason() != Qt::ActiveWindowFocusReason |
|
1959 && e->reason() != Qt::PopupFocusReason |
|
1960 && cursor.hasSelection()) { |
|
1961 cursor.clearSelection(); |
|
1962 } |
|
1963 } |
|
1964 hasFocus = e->gotFocus(); |
|
1965 } |
|
1966 |
|
1967 QString QTextControlPrivate::anchorForCursor(const QTextCursor &anchorCursor) const |
|
1968 { |
|
1969 if (anchorCursor.hasSelection()) { |
|
1970 QTextCursor cursor = anchorCursor; |
|
1971 if (cursor.selectionStart() != cursor.position()) |
|
1972 cursor.setPosition(cursor.selectionStart()); |
|
1973 cursor.movePosition(QTextCursor::NextCharacter); |
|
1974 QTextCharFormat fmt = cursor.charFormat(); |
|
1975 if (fmt.isAnchor() && fmt.hasProperty(QTextFormat::AnchorHref)) |
|
1976 return fmt.stringProperty(QTextFormat::AnchorHref); |
|
1977 } |
|
1978 return QString(); |
|
1979 } |
|
1980 |
|
1981 #ifdef QT_KEYPAD_NAVIGATION |
|
1982 void QTextControlPrivate::editFocusEvent(QEvent *e) |
|
1983 { |
|
1984 Q_Q(QTextControl); |
|
1985 |
|
1986 if (QApplication::keypadNavigationEnabled()) { |
|
1987 if (e->type() == QEvent::EnterEditFocus && interactionFlags & Qt::TextEditable) { |
|
1988 const QTextCursor oldSelection = cursor; |
|
1989 const int oldCursorPos = cursor.position(); |
|
1990 const bool moved = cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor); |
|
1991 q->ensureCursorVisible(); |
|
1992 if (moved) { |
|
1993 if (cursor.position() != oldCursorPos) |
|
1994 emit q->cursorPositionChanged(); |
|
1995 emit q->microFocusChanged(); |
|
1996 } |
|
1997 selectionChanged(); |
|
1998 repaintOldAndNewSelection(oldSelection); |
|
1999 |
|
2000 setBlinkingCursorEnabled(true); |
|
2001 } else |
|
2002 setBlinkingCursorEnabled(false); |
|
2003 } |
|
2004 |
|
2005 hasEditFocus = e->type() == QEvent::EnterEditFocus ? true : false; |
|
2006 } |
|
2007 #endif |
|
2008 |
|
2009 #ifndef QT_NO_CONTEXTMENU |
|
2010 QMenu *QTextControl::createStandardContextMenu(const QPointF &pos, QWidget *parent) |
|
2011 { |
|
2012 Q_D(QTextControl); |
|
2013 |
|
2014 const bool showTextSelectionActions = d->interactionFlags & (Qt::TextEditable | Qt::TextSelectableByKeyboard | Qt::TextSelectableByMouse); |
|
2015 |
|
2016 d->linkToCopy = QString(); |
|
2017 if (!pos.isNull()) |
|
2018 d->linkToCopy = anchorAt(pos); |
|
2019 |
|
2020 if (d->linkToCopy.isEmpty() && !showTextSelectionActions) |
|
2021 return 0; |
|
2022 |
|
2023 QMenu *menu = new QMenu(parent); |
|
2024 QAction *a; |
|
2025 |
|
2026 if (d->interactionFlags & Qt::TextEditable) { |
|
2027 a = menu->addAction(tr("&Undo") + ACCEL_KEY(QKeySequence::Undo), this, SLOT(undo())); |
|
2028 a->setEnabled(d->doc->isUndoAvailable()); |
|
2029 a = menu->addAction(tr("&Redo") + ACCEL_KEY(QKeySequence::Redo), this, SLOT(redo())); |
|
2030 a->setEnabled(d->doc->isRedoAvailable()); |
|
2031 menu->addSeparator(); |
|
2032 |
|
2033 a = menu->addAction(tr("Cu&t") + ACCEL_KEY(QKeySequence::Cut), this, SLOT(cut())); |
|
2034 a->setEnabled(d->cursor.hasSelection()); |
|
2035 } |
|
2036 |
|
2037 if (showTextSelectionActions) { |
|
2038 a = menu->addAction(tr("&Copy") + ACCEL_KEY(QKeySequence::Copy), this, SLOT(copy())); |
|
2039 a->setEnabled(d->cursor.hasSelection()); |
|
2040 } |
|
2041 |
|
2042 if ((d->interactionFlags & Qt::LinksAccessibleByKeyboard) |
|
2043 || (d->interactionFlags & Qt::LinksAccessibleByMouse)) { |
|
2044 |
|
2045 a = menu->addAction(tr("Copy &Link Location"), this, SLOT(_q_copyLink())); |
|
2046 a->setEnabled(!d->linkToCopy.isEmpty()); |
|
2047 } |
|
2048 |
|
2049 if (d->interactionFlags & Qt::TextEditable) { |
|
2050 #if !defined(QT_NO_CLIPBOARD) |
|
2051 a = menu->addAction(tr("&Paste") + ACCEL_KEY(QKeySequence::Paste), this, SLOT(paste())); |
|
2052 a->setEnabled(canPaste()); |
|
2053 #endif |
|
2054 a = menu->addAction(tr("Delete"), this, SLOT(_q_deleteSelected())); |
|
2055 a->setEnabled(d->cursor.hasSelection()); |
|
2056 } |
|
2057 |
|
2058 |
|
2059 if (showTextSelectionActions) { |
|
2060 menu->addSeparator(); |
|
2061 a = menu->addAction(tr("Select All") + ACCEL_KEY(QKeySequence::SelectAll), this, SLOT(selectAll())); |
|
2062 a->setEnabled(!d->doc->isEmpty()); |
|
2063 } |
|
2064 |
|
2065 #if !defined(QT_NO_IM) |
|
2066 if (d->contextWidget) { |
|
2067 QInputContext *qic = d->inputContext(); |
|
2068 if (qic) { |
|
2069 QList<QAction *> imActions = qic->actions(); |
|
2070 for (int i = 0; i < imActions.size(); ++i) |
|
2071 menu->addAction(imActions.at(i)); |
|
2072 } |
|
2073 } |
|
2074 #endif |
|
2075 |
|
2076 #if defined(Q_WS_WIN) |
|
2077 if ((d->interactionFlags & Qt::TextEditable) && qt_use_rtl_extensions) { |
|
2078 #else |
|
2079 if (d->interactionFlags & Qt::TextEditable) { |
|
2080 #endif |
|
2081 menu->addSeparator(); |
|
2082 QUnicodeControlCharacterMenu *ctrlCharacterMenu = new QUnicodeControlCharacterMenu(this, menu); |
|
2083 menu->addMenu(ctrlCharacterMenu); |
|
2084 } |
|
2085 |
|
2086 return menu; |
|
2087 } |
|
2088 #endif // QT_NO_CONTEXTMENU |
|
2089 |
|
2090 QTextCursor QTextControl::cursorForPosition(const QPointF &pos) const |
|
2091 { |
|
2092 Q_D(const QTextControl); |
|
2093 int cursorPos = hitTest(pos, Qt::FuzzyHit); |
|
2094 if (cursorPos == -1) |
|
2095 cursorPos = 0; |
|
2096 QTextCursor c(d->doc); |
|
2097 c.setPosition(cursorPos); |
|
2098 return c; |
|
2099 } |
|
2100 |
|
2101 QRectF QTextControl::cursorRect(const QTextCursor &cursor) const |
|
2102 { |
|
2103 Q_D(const QTextControl); |
|
2104 if (cursor.isNull()) |
|
2105 return QRectF(); |
|
2106 |
|
2107 return d->rectForPosition(cursor.position()); |
|
2108 } |
|
2109 |
|
2110 QRectF QTextControl::cursorRect() const |
|
2111 { |
|
2112 Q_D(const QTextControl); |
|
2113 return cursorRect(d->cursor); |
|
2114 } |
|
2115 |
|
2116 QRectF QTextControlPrivate::cursorRectPlusUnicodeDirectionMarkers(const QTextCursor &cursor) const |
|
2117 { |
|
2118 if (cursor.isNull()) |
|
2119 return QRectF(); |
|
2120 |
|
2121 return rectForPosition(cursor.position()).adjusted(-4, 0, 4, 0); |
|
2122 } |
|
2123 |
|
2124 QString QTextControl::anchorAt(const QPointF &pos) const |
|
2125 { |
|
2126 Q_D(const QTextControl); |
|
2127 return d->doc->documentLayout()->anchorAt(pos); |
|
2128 } |
|
2129 |
|
2130 QString QTextControl::anchorAtCursor() const |
|
2131 { |
|
2132 Q_D(const QTextControl); |
|
2133 |
|
2134 return d->anchorForCursor(d->cursor); |
|
2135 } |
|
2136 |
|
2137 bool QTextControl::overwriteMode() const |
|
2138 { |
|
2139 Q_D(const QTextControl); |
|
2140 return d->overwriteMode; |
|
2141 } |
|
2142 |
|
2143 void QTextControl::setOverwriteMode(bool overwrite) |
|
2144 { |
|
2145 Q_D(QTextControl); |
|
2146 d->overwriteMode = overwrite; |
|
2147 } |
|
2148 |
|
2149 int QTextControl::cursorWidth() const |
|
2150 { |
|
2151 #ifndef QT_NO_PROPERTIES |
|
2152 Q_D(const QTextControl); |
|
2153 return d->doc->documentLayout()->property("cursorWidth").toInt(); |
|
2154 #else |
|
2155 return 1; |
|
2156 #endif |
|
2157 } |
|
2158 |
|
2159 void QTextControl::setCursorWidth(int width) |
|
2160 { |
|
2161 Q_D(QTextControl); |
|
2162 #ifdef QT_NO_PROPERTIES |
|
2163 Q_UNUSED(width); |
|
2164 #else |
|
2165 if (width == -1) |
|
2166 width = QApplication::style()->pixelMetric(QStyle::PM_TextCursorWidth); |
|
2167 d->doc->documentLayout()->setProperty("cursorWidth", width); |
|
2168 #endif |
|
2169 d->repaintCursor(); |
|
2170 } |
|
2171 |
|
2172 bool QTextControl::acceptRichText() const |
|
2173 { |
|
2174 Q_D(const QTextControl); |
|
2175 return d->acceptRichText; |
|
2176 } |
|
2177 |
|
2178 void QTextControl::setAcceptRichText(bool accept) |
|
2179 { |
|
2180 Q_D(QTextControl); |
|
2181 d->acceptRichText = accept; |
|
2182 } |
|
2183 |
|
2184 #ifndef QT_NO_TEXTEDIT |
|
2185 |
|
2186 void QTextControl::setExtraSelections(const QList<QTextEdit::ExtraSelection> &selections) |
|
2187 { |
|
2188 Q_D(QTextControl); |
|
2189 |
|
2190 QHash<int, int> hash; |
|
2191 for (int i = 0; i < d->extraSelections.count(); ++i) { |
|
2192 const QAbstractTextDocumentLayout::Selection &esel = d->extraSelections.at(i); |
|
2193 hash.insertMulti(esel.cursor.anchor(), i); |
|
2194 } |
|
2195 |
|
2196 for (int i = 0; i < selections.count(); ++i) { |
|
2197 const QTextEdit::ExtraSelection &sel = selections.at(i); |
|
2198 QHash<int, int>::iterator it = hash.find(sel.cursor.anchor()); |
|
2199 if (it != hash.end()) { |
|
2200 const QAbstractTextDocumentLayout::Selection &esel = d->extraSelections.at(it.value()); |
|
2201 if (esel.cursor.position() == sel.cursor.position() |
|
2202 && esel.format == sel.format) { |
|
2203 hash.erase(it); |
|
2204 continue; |
|
2205 } |
|
2206 } |
|
2207 QRectF r = selectionRect(sel.cursor); |
|
2208 if (sel.format.boolProperty(QTextFormat::FullWidthSelection)) { |
|
2209 r.setLeft(0); |
|
2210 r.setWidth(qreal(INT_MAX)); |
|
2211 } |
|
2212 emit updateRequest(r); |
|
2213 } |
|
2214 |
|
2215 for (QHash<int, int>::iterator it = hash.begin(); it != hash.end(); ++it) { |
|
2216 const QAbstractTextDocumentLayout::Selection &esel = d->extraSelections.at(it.value()); |
|
2217 QRectF r = selectionRect(esel.cursor); |
|
2218 if (esel.format.boolProperty(QTextFormat::FullWidthSelection)) { |
|
2219 r.setLeft(0); |
|
2220 r.setWidth(qreal(INT_MAX)); |
|
2221 } |
|
2222 emit updateRequest(r); |
|
2223 } |
|
2224 |
|
2225 d->extraSelections.resize(selections.count()); |
|
2226 for (int i = 0; i < selections.count(); ++i) { |
|
2227 d->extraSelections[i].cursor = selections.at(i).cursor; |
|
2228 d->extraSelections[i].format = selections.at(i).format; |
|
2229 } |
|
2230 } |
|
2231 |
|
2232 QList<QTextEdit::ExtraSelection> QTextControl::extraSelections() const |
|
2233 { |
|
2234 Q_D(const QTextControl); |
|
2235 QList<QTextEdit::ExtraSelection> selections; |
|
2236 for (int i = 0; i < d->extraSelections.count(); ++i) { |
|
2237 QTextEdit::ExtraSelection sel; |
|
2238 sel.cursor = d->extraSelections.at(i).cursor; |
|
2239 sel.format = d->extraSelections.at(i).format; |
|
2240 selections.append(sel); |
|
2241 } |
|
2242 return selections; |
|
2243 } |
|
2244 |
|
2245 #endif // QT_NO_TEXTEDIT |
|
2246 |
|
2247 void QTextControl::setTextWidth(qreal width) |
|
2248 { |
|
2249 Q_D(QTextControl); |
|
2250 d->doc->setTextWidth(width); |
|
2251 } |
|
2252 |
|
2253 qreal QTextControl::textWidth() const |
|
2254 { |
|
2255 Q_D(const QTextControl); |
|
2256 return d->doc->textWidth(); |
|
2257 } |
|
2258 |
|
2259 QSizeF QTextControl::size() const |
|
2260 { |
|
2261 Q_D(const QTextControl); |
|
2262 return d->doc->size(); |
|
2263 } |
|
2264 |
|
2265 void QTextControl::setOpenExternalLinks(bool open) |
|
2266 { |
|
2267 Q_D(QTextControl); |
|
2268 d->openExternalLinks = open; |
|
2269 } |
|
2270 |
|
2271 bool QTextControl::openExternalLinks() const |
|
2272 { |
|
2273 Q_D(const QTextControl); |
|
2274 return d->openExternalLinks; |
|
2275 } |
|
2276 |
|
2277 bool QTextControl::ignoreUnusedNavigationEvents() const |
|
2278 { |
|
2279 Q_D(const QTextControl); |
|
2280 return d->ignoreUnusedNavigationEvents; |
|
2281 } |
|
2282 |
|
2283 void QTextControl::setIgnoreUnusedNavigationEvents(bool ignore) |
|
2284 { |
|
2285 Q_D(QTextControl); |
|
2286 d->ignoreUnusedNavigationEvents = ignore; |
|
2287 } |
|
2288 |
|
2289 void QTextControl::moveCursor(QTextCursor::MoveOperation op, QTextCursor::MoveMode mode) |
|
2290 { |
|
2291 Q_D(QTextControl); |
|
2292 const QTextCursor oldSelection = d->cursor; |
|
2293 const bool moved = d->cursor.movePosition(op, mode); |
|
2294 d->_q_updateCurrentCharFormatAndSelection(); |
|
2295 ensureCursorVisible(); |
|
2296 d->repaintOldAndNewSelection(oldSelection); |
|
2297 if (moved) |
|
2298 emit cursorPositionChanged(); |
|
2299 } |
|
2300 |
|
2301 bool QTextControl::canPaste() const |
|
2302 { |
|
2303 #ifndef QT_NO_CLIPBOARD |
|
2304 Q_D(const QTextControl); |
|
2305 if (d->interactionFlags & Qt::TextEditable) { |
|
2306 const QMimeData *md = QApplication::clipboard()->mimeData(); |
|
2307 return md && canInsertFromMimeData(md); |
|
2308 } |
|
2309 #endif |
|
2310 return false; |
|
2311 } |
|
2312 |
|
2313 void QTextControl::setCursorIsFocusIndicator(bool b) |
|
2314 { |
|
2315 Q_D(QTextControl); |
|
2316 d->cursorIsFocusIndicator = b; |
|
2317 d->repaintCursor(); |
|
2318 } |
|
2319 |
|
2320 bool QTextControl::cursorIsFocusIndicator() const |
|
2321 { |
|
2322 Q_D(const QTextControl); |
|
2323 return d->cursorIsFocusIndicator; |
|
2324 } |
|
2325 |
|
2326 #ifndef QT_NO_PRINTER |
|
2327 void QTextControl::print(QPrinter *printer) const |
|
2328 { |
|
2329 #ifndef QT_NO_PRINTER |
|
2330 Q_D(const QTextControl); |
|
2331 if (!printer || !printer->isValid()) |
|
2332 return; |
|
2333 QTextDocument *tempDoc = 0; |
|
2334 const QTextDocument *doc = d->doc; |
|
2335 if (printer->printRange() == QPrinter::Selection) { |
|
2336 if (!d->cursor.hasSelection()) |
|
2337 return; |
|
2338 tempDoc = new QTextDocument(const_cast<QTextDocument *>(doc)); |
|
2339 tempDoc->setMetaInformation(QTextDocument::DocumentTitle, doc->metaInformation(QTextDocument::DocumentTitle)); |
|
2340 tempDoc->setPageSize(doc->pageSize()); |
|
2341 tempDoc->setDefaultFont(doc->defaultFont()); |
|
2342 tempDoc->setUseDesignMetrics(doc->useDesignMetrics()); |
|
2343 QTextCursor(tempDoc).insertFragment(d->cursor.selection()); |
|
2344 doc = tempDoc; |
|
2345 |
|
2346 // copy the custom object handlers |
|
2347 doc->documentLayout()->d_func()->handlers = d->doc->documentLayout()->d_func()->handlers; |
|
2348 } |
|
2349 doc->print(printer); |
|
2350 delete tempDoc; |
|
2351 #endif |
|
2352 } |
|
2353 #endif // QT_NO_PRINTER |
|
2354 |
|
2355 QMimeData *QTextControl::createMimeDataFromSelection() const |
|
2356 { |
|
2357 Q_D(const QTextControl); |
|
2358 const QTextDocumentFragment fragment(d->cursor); |
|
2359 return new QTextEditMimeData(fragment); |
|
2360 } |
|
2361 |
|
2362 bool QTextControl::canInsertFromMimeData(const QMimeData *source) const |
|
2363 { |
|
2364 Q_D(const QTextControl); |
|
2365 if (d->acceptRichText) |
|
2366 return (source->hasText() && !source->text().isEmpty()) |
|
2367 || source->hasHtml() |
|
2368 || source->hasFormat(QLatin1String("application/x-qrichtext")) |
|
2369 || source->hasFormat(QLatin1String("application/x-qt-richtext")); |
|
2370 else |
|
2371 return source->hasText() && !source->text().isEmpty(); |
|
2372 } |
|
2373 |
|
2374 void QTextControl::insertFromMimeData(const QMimeData *source) |
|
2375 { |
|
2376 Q_D(QTextControl); |
|
2377 if (!(d->interactionFlags & Qt::TextEditable) || !source) |
|
2378 return; |
|
2379 |
|
2380 bool hasData = false; |
|
2381 QTextDocumentFragment fragment; |
|
2382 #ifndef QT_NO_TEXTHTMLPARSER |
|
2383 if (source->hasFormat(QLatin1String("application/x-qrichtext")) && d->acceptRichText) { |
|
2384 // x-qrichtext is always UTF-8 (taken from Qt3 since we don't use it anymore). |
|
2385 QString richtext = QString::fromUtf8(source->data(QLatin1String("application/x-qrichtext"))); |
|
2386 richtext.prepend(QLatin1String("<meta name=\"qrichtext\" content=\"1\" />")); |
|
2387 fragment = QTextDocumentFragment::fromHtml(richtext, d->doc); |
|
2388 hasData = true; |
|
2389 } else if (source->hasHtml() && d->acceptRichText) { |
|
2390 fragment = QTextDocumentFragment::fromHtml(source->html(), d->doc); |
|
2391 hasData = true; |
|
2392 } else { |
|
2393 QString text = source->text(); |
|
2394 if (!text.isNull()) { |
|
2395 fragment = QTextDocumentFragment::fromPlainText(text); |
|
2396 hasData = true; |
|
2397 } |
|
2398 } |
|
2399 #else |
|
2400 fragment = QTextDocumentFragment::fromPlainText(source->text()); |
|
2401 #endif // QT_NO_TEXTHTMLPARSER |
|
2402 |
|
2403 if (hasData) |
|
2404 d->cursor.insertFragment(fragment); |
|
2405 ensureCursorVisible(); |
|
2406 } |
|
2407 |
|
2408 bool QTextControl::findNextPrevAnchor(const QTextCursor &startCursor, bool next, QTextCursor &newAnchor) |
|
2409 { |
|
2410 Q_D(QTextControl); |
|
2411 |
|
2412 int anchorStart = -1; |
|
2413 QString anchorHref; |
|
2414 int anchorEnd = -1; |
|
2415 |
|
2416 if (next) { |
|
2417 const int startPos = startCursor.selectionEnd(); |
|
2418 |
|
2419 QTextBlock block = d->doc->findBlock(startPos); |
|
2420 QTextBlock::Iterator it = block.begin(); |
|
2421 |
|
2422 while (!it.atEnd() && it.fragment().position() < startPos) |
|
2423 ++it; |
|
2424 |
|
2425 while (block.isValid()) { |
|
2426 anchorStart = -1; |
|
2427 |
|
2428 // find next anchor |
|
2429 for (; !it.atEnd(); ++it) { |
|
2430 const QTextFragment fragment = it.fragment(); |
|
2431 const QTextCharFormat fmt = fragment.charFormat(); |
|
2432 |
|
2433 if (fmt.isAnchor() && fmt.hasProperty(QTextFormat::AnchorHref)) { |
|
2434 anchorStart = fragment.position(); |
|
2435 anchorHref = fmt.anchorHref(); |
|
2436 break; |
|
2437 } |
|
2438 } |
|
2439 |
|
2440 if (anchorStart != -1) { |
|
2441 anchorEnd = -1; |
|
2442 |
|
2443 // find next non-anchor fragment |
|
2444 for (; !it.atEnd(); ++it) { |
|
2445 const QTextFragment fragment = it.fragment(); |
|
2446 const QTextCharFormat fmt = fragment.charFormat(); |
|
2447 |
|
2448 if (!fmt.isAnchor() || fmt.anchorHref() != anchorHref) { |
|
2449 anchorEnd = fragment.position(); |
|
2450 break; |
|
2451 } |
|
2452 } |
|
2453 |
|
2454 if (anchorEnd == -1) |
|
2455 anchorEnd = block.position() + block.length() - 1; |
|
2456 |
|
2457 // make found selection |
|
2458 break; |
|
2459 } |
|
2460 |
|
2461 block = block.next(); |
|
2462 it = block.begin(); |
|
2463 } |
|
2464 } else { |
|
2465 int startPos = startCursor.selectionStart(); |
|
2466 if (startPos > 0) |
|
2467 --startPos; |
|
2468 |
|
2469 QTextBlock block = d->doc->findBlock(startPos); |
|
2470 QTextBlock::Iterator blockStart = block.begin(); |
|
2471 QTextBlock::Iterator it = block.end(); |
|
2472 |
|
2473 if (startPos == block.position()) { |
|
2474 it = block.begin(); |
|
2475 } else { |
|
2476 do { |
|
2477 if (it == blockStart) { |
|
2478 it = QTextBlock::Iterator(); |
|
2479 block = QTextBlock(); |
|
2480 } else { |
|
2481 --it; |
|
2482 } |
|
2483 } while (!it.atEnd() && it.fragment().position() + it.fragment().length() - 1 > startPos); |
|
2484 } |
|
2485 |
|
2486 while (block.isValid()) { |
|
2487 anchorStart = -1; |
|
2488 |
|
2489 if (!it.atEnd()) { |
|
2490 do { |
|
2491 const QTextFragment fragment = it.fragment(); |
|
2492 const QTextCharFormat fmt = fragment.charFormat(); |
|
2493 |
|
2494 if (fmt.isAnchor() && fmt.hasProperty(QTextFormat::AnchorHref)) { |
|
2495 anchorStart = fragment.position() + fragment.length(); |
|
2496 anchorHref = fmt.anchorHref(); |
|
2497 break; |
|
2498 } |
|
2499 |
|
2500 if (it == blockStart) |
|
2501 it = QTextBlock::Iterator(); |
|
2502 else |
|
2503 --it; |
|
2504 } while (!it.atEnd()); |
|
2505 } |
|
2506 |
|
2507 if (anchorStart != -1 && !it.atEnd()) { |
|
2508 anchorEnd = -1; |
|
2509 |
|
2510 do { |
|
2511 const QTextFragment fragment = it.fragment(); |
|
2512 const QTextCharFormat fmt = fragment.charFormat(); |
|
2513 |
|
2514 if (!fmt.isAnchor() || fmt.anchorHref() != anchorHref) { |
|
2515 anchorEnd = fragment.position() + fragment.length(); |
|
2516 break; |
|
2517 } |
|
2518 |
|
2519 if (it == blockStart) |
|
2520 it = QTextBlock::Iterator(); |
|
2521 else |
|
2522 --it; |
|
2523 } while (!it.atEnd()); |
|
2524 |
|
2525 if (anchorEnd == -1) |
|
2526 anchorEnd = qMax(0, block.position()); |
|
2527 |
|
2528 break; |
|
2529 } |
|
2530 |
|
2531 block = block.previous(); |
|
2532 it = block.end(); |
|
2533 if (it != block.begin()) |
|
2534 --it; |
|
2535 blockStart = block.begin(); |
|
2536 } |
|
2537 |
|
2538 } |
|
2539 |
|
2540 if (anchorStart != -1 && anchorEnd != -1) { |
|
2541 newAnchor = d->cursor; |
|
2542 newAnchor.setPosition(anchorStart); |
|
2543 newAnchor.setPosition(anchorEnd, QTextCursor::KeepAnchor); |
|
2544 return true; |
|
2545 } |
|
2546 |
|
2547 return false; |
|
2548 } |
|
2549 |
|
2550 void QTextControlPrivate::activateLinkUnderCursor(QString href) |
|
2551 { |
|
2552 QTextCursor oldCursor = cursor; |
|
2553 |
|
2554 if (href.isEmpty()) { |
|
2555 QTextCursor tmp = cursor; |
|
2556 if (tmp.selectionStart() != tmp.position()) |
|
2557 tmp.setPosition(tmp.selectionStart()); |
|
2558 tmp.movePosition(QTextCursor::NextCharacter); |
|
2559 href = tmp.charFormat().anchorHref(); |
|
2560 } |
|
2561 if (href.isEmpty()) |
|
2562 return; |
|
2563 |
|
2564 if (!cursor.hasSelection()) { |
|
2565 QTextBlock block = cursor.block(); |
|
2566 const int cursorPos = cursor.position(); |
|
2567 |
|
2568 QTextBlock::Iterator it = block.begin(); |
|
2569 QTextBlock::Iterator linkFragment; |
|
2570 |
|
2571 for (; !it.atEnd(); ++it) { |
|
2572 QTextFragment fragment = it.fragment(); |
|
2573 const int fragmentPos = fragment.position(); |
|
2574 if (fragmentPos <= cursorPos && |
|
2575 fragmentPos + fragment.length() > cursorPos) { |
|
2576 linkFragment = it; |
|
2577 break; |
|
2578 } |
|
2579 } |
|
2580 |
|
2581 if (!linkFragment.atEnd()) { |
|
2582 it = linkFragment; |
|
2583 cursor.setPosition(it.fragment().position()); |
|
2584 if (it != block.begin()) { |
|
2585 do { |
|
2586 --it; |
|
2587 QTextFragment fragment = it.fragment(); |
|
2588 if (fragment.charFormat().anchorHref() != href) |
|
2589 break; |
|
2590 cursor.setPosition(fragment.position()); |
|
2591 } while (it != block.begin()); |
|
2592 } |
|
2593 |
|
2594 for (it = linkFragment; !it.atEnd(); ++it) { |
|
2595 QTextFragment fragment = it.fragment(); |
|
2596 if (fragment.charFormat().anchorHref() != href) |
|
2597 break; |
|
2598 cursor.setPosition(fragment.position() + fragment.length(), QTextCursor::KeepAnchor); |
|
2599 } |
|
2600 } |
|
2601 } |
|
2602 |
|
2603 if (hasFocus) { |
|
2604 cursorIsFocusIndicator = true; |
|
2605 } else { |
|
2606 cursorIsFocusIndicator = false; |
|
2607 cursor.clearSelection(); |
|
2608 } |
|
2609 repaintOldAndNewSelection(oldCursor); |
|
2610 |
|
2611 #ifndef QT_NO_DESKTOPSERVICES |
|
2612 if (openExternalLinks) |
|
2613 QDesktopServices::openUrl(href); |
|
2614 else |
|
2615 #endif |
|
2616 emit q_func()->linkActivated(href); |
|
2617 } |
|
2618 |
|
2619 #ifndef QT_NO_TOOLTIP |
|
2620 void QTextControlPrivate::showToolTip(const QPoint &globalPos, const QPointF &pos, QWidget *contextWidget) |
|
2621 { |
|
2622 const QString toolTip = q_func()->cursorForPosition(pos).charFormat().toolTip(); |
|
2623 if (toolTip.isEmpty()) |
|
2624 return; |
|
2625 QToolTip::showText(globalPos, toolTip, contextWidget); |
|
2626 } |
|
2627 #endif // QT_NO_TOOLTIP |
|
2628 |
|
2629 bool QTextControl::setFocusToNextOrPreviousAnchor(bool next) |
|
2630 { |
|
2631 Q_D(QTextControl); |
|
2632 |
|
2633 if (!(d->interactionFlags & Qt::LinksAccessibleByKeyboard)) |
|
2634 return false; |
|
2635 |
|
2636 QRectF crect = selectionRect(); |
|
2637 emit updateRequest(crect); |
|
2638 |
|
2639 // If we don't have a current anchor, we start from the start/end |
|
2640 if (!d->cursor.hasSelection()) { |
|
2641 d->cursor = QTextCursor(d->doc); |
|
2642 if (next) |
|
2643 d->cursor.movePosition(QTextCursor::Start); |
|
2644 else |
|
2645 d->cursor.movePosition(QTextCursor::End); |
|
2646 } |
|
2647 |
|
2648 QTextCursor newAnchor; |
|
2649 if (findNextPrevAnchor(d->cursor, next, newAnchor)) { |
|
2650 d->cursor = newAnchor; |
|
2651 d->cursorIsFocusIndicator = true; |
|
2652 } else { |
|
2653 d->cursor.clearSelection(); |
|
2654 } |
|
2655 |
|
2656 if (d->cursor.hasSelection()) { |
|
2657 crect = selectionRect(); |
|
2658 emit updateRequest(crect); |
|
2659 emit visibilityRequest(crect); |
|
2660 return true; |
|
2661 } else { |
|
2662 return false; |
|
2663 } |
|
2664 } |
|
2665 |
|
2666 bool QTextControl::setFocusToAnchor(const QTextCursor &newCursor) |
|
2667 { |
|
2668 Q_D(QTextControl); |
|
2669 |
|
2670 if (!(d->interactionFlags & Qt::LinksAccessibleByKeyboard)) |
|
2671 return false; |
|
2672 |
|
2673 // Verify that this is an anchor. |
|
2674 const QString anchorHref = d->anchorForCursor(newCursor); |
|
2675 if (anchorHref.isEmpty()) |
|
2676 return false; |
|
2677 |
|
2678 // and process it |
|
2679 QRectF crect = selectionRect(); |
|
2680 emit updateRequest(crect); |
|
2681 |
|
2682 d->cursor.setPosition(newCursor.selectionStart()); |
|
2683 d->cursor.setPosition(newCursor.selectionEnd(), QTextCursor::KeepAnchor); |
|
2684 d->cursorIsFocusIndicator = true; |
|
2685 |
|
2686 crect = selectionRect(); |
|
2687 emit updateRequest(crect); |
|
2688 emit visibilityRequest(crect); |
|
2689 return true; |
|
2690 } |
|
2691 |
|
2692 void QTextControl::setTextInteractionFlags(Qt::TextInteractionFlags flags) |
|
2693 { |
|
2694 Q_D(QTextControl); |
|
2695 if (flags == d->interactionFlags) |
|
2696 return; |
|
2697 d->interactionFlags = flags; |
|
2698 |
|
2699 if (d->hasFocus) |
|
2700 d->setBlinkingCursorEnabled(flags & Qt::TextEditable); |
|
2701 } |
|
2702 |
|
2703 Qt::TextInteractionFlags QTextControl::textInteractionFlags() const |
|
2704 { |
|
2705 Q_D(const QTextControl); |
|
2706 return d->interactionFlags; |
|
2707 } |
|
2708 |
|
2709 void QTextControl::mergeCurrentCharFormat(const QTextCharFormat &modifier) |
|
2710 { |
|
2711 Q_D(QTextControl); |
|
2712 d->cursor.mergeCharFormat(modifier); |
|
2713 d->updateCurrentCharFormat(); |
|
2714 } |
|
2715 |
|
2716 void QTextControl::setCurrentCharFormat(const QTextCharFormat &format) |
|
2717 { |
|
2718 Q_D(QTextControl); |
|
2719 d->cursor.setCharFormat(format); |
|
2720 d->updateCurrentCharFormat(); |
|
2721 } |
|
2722 |
|
2723 QTextCharFormat QTextControl::currentCharFormat() const |
|
2724 { |
|
2725 Q_D(const QTextControl); |
|
2726 return d->cursor.charFormat(); |
|
2727 } |
|
2728 |
|
2729 void QTextControl::insertPlainText(const QString &text) |
|
2730 { |
|
2731 Q_D(QTextControl); |
|
2732 d->cursor.insertText(text); |
|
2733 } |
|
2734 |
|
2735 #ifndef QT_NO_TEXTHTMLPARSER |
|
2736 void QTextControl::insertHtml(const QString &text) |
|
2737 { |
|
2738 Q_D(QTextControl); |
|
2739 d->cursor.insertHtml(text); |
|
2740 } |
|
2741 #endif // QT_NO_TEXTHTMLPARSER |
|
2742 |
|
2743 QPointF QTextControl::anchorPosition(const QString &name) const |
|
2744 { |
|
2745 Q_D(const QTextControl); |
|
2746 if (name.isEmpty()) |
|
2747 return QPointF(); |
|
2748 |
|
2749 QRectF r; |
|
2750 for (QTextBlock block = d->doc->begin(); block.isValid(); block = block.next()) { |
|
2751 QTextCharFormat format = block.charFormat(); |
|
2752 if (format.isAnchor() && format.anchorNames().contains(name)) { |
|
2753 r = d->rectForPosition(block.position()); |
|
2754 break; |
|
2755 } |
|
2756 |
|
2757 for (QTextBlock::Iterator it = block.begin(); !it.atEnd(); ++it) { |
|
2758 QTextFragment fragment = it.fragment(); |
|
2759 format = fragment.charFormat(); |
|
2760 if (format.isAnchor() && format.anchorNames().contains(name)) { |
|
2761 r = d->rectForPosition(fragment.position()); |
|
2762 block = QTextBlock(); |
|
2763 break; |
|
2764 } |
|
2765 } |
|
2766 } |
|
2767 if (!r.isValid()) |
|
2768 return QPointF(); |
|
2769 return QPointF(0, r.top()); |
|
2770 } |
|
2771 |
|
2772 void QTextControl::adjustSize() |
|
2773 { |
|
2774 Q_D(QTextControl); |
|
2775 d->doc->adjustSize(); |
|
2776 } |
|
2777 |
|
2778 bool QTextControl::find(const QString &exp, QTextDocument::FindFlags options) |
|
2779 { |
|
2780 Q_D(QTextControl); |
|
2781 QTextCursor search = d->doc->find(exp, d->cursor, options); |
|
2782 if (search.isNull()) |
|
2783 return false; |
|
2784 |
|
2785 setTextCursor(search); |
|
2786 return true; |
|
2787 } |
|
2788 |
|
2789 |
|
2790 |
|
2791 void QTextControlPrivate::append(const QString &text, Qt::TextFormat format) |
|
2792 { |
|
2793 QTextCursor tmp(doc); |
|
2794 tmp.beginEditBlock(); |
|
2795 tmp.movePosition(QTextCursor::End); |
|
2796 |
|
2797 if (!doc->isEmpty()) |
|
2798 tmp.insertBlock(cursor.blockFormat(), cursor.charFormat()); |
|
2799 else |
|
2800 tmp.setCharFormat(cursor.charFormat()); |
|
2801 |
|
2802 // preserve the char format |
|
2803 QTextCharFormat oldCharFormat = cursor.charFormat(); |
|
2804 |
|
2805 #ifndef QT_NO_TEXTHTMLPARSER |
|
2806 if (format == Qt::RichText || (format == Qt::AutoText && Qt::mightBeRichText(text))) { |
|
2807 tmp.insertHtml(text); |
|
2808 } else { |
|
2809 tmp.insertText(text); |
|
2810 } |
|
2811 #else |
|
2812 tmp.insertText(text); |
|
2813 #endif // QT_NO_TEXTHTMLPARSER |
|
2814 if (!cursor.hasSelection()) |
|
2815 cursor.setCharFormat(oldCharFormat); |
|
2816 |
|
2817 tmp.endEditBlock(); |
|
2818 } |
|
2819 |
|
2820 void QTextControl::append(const QString &text) |
|
2821 { |
|
2822 Q_D(QTextControl); |
|
2823 d->append(text, Qt::AutoText); |
|
2824 } |
|
2825 |
|
2826 void QTextControl::appendHtml(const QString &html) |
|
2827 { |
|
2828 Q_D(QTextControl); |
|
2829 d->append(html, Qt::RichText); |
|
2830 } |
|
2831 |
|
2832 void QTextControl::appendPlainText(const QString &text) |
|
2833 { |
|
2834 Q_D(QTextControl); |
|
2835 d->append(text, Qt::PlainText); |
|
2836 } |
|
2837 |
|
2838 |
|
2839 void QTextControl::ensureCursorVisible() |
|
2840 { |
|
2841 Q_D(QTextControl); |
|
2842 QRectF crect = d->rectForPosition(d->cursor.position()).adjusted(-5, 0, 5, 0); |
|
2843 emit visibilityRequest(crect); |
|
2844 emit microFocusChanged(); |
|
2845 } |
|
2846 |
|
2847 QPalette QTextControl::palette() const |
|
2848 { |
|
2849 Q_D(const QTextControl); |
|
2850 return d->palette; |
|
2851 } |
|
2852 |
|
2853 void QTextControl::setPalette(const QPalette &pal) |
|
2854 { |
|
2855 Q_D(QTextControl); |
|
2856 d->palette = pal; |
|
2857 } |
|
2858 |
|
2859 QAbstractTextDocumentLayout::PaintContext QTextControl::getPaintContext(QWidget *widget) const |
|
2860 { |
|
2861 Q_D(const QTextControl); |
|
2862 |
|
2863 QAbstractTextDocumentLayout::PaintContext ctx; |
|
2864 |
|
2865 ctx.selections = d->extraSelections; |
|
2866 ctx.palette = d->palette; |
|
2867 if (d->cursorOn && d->isEnabled) { |
|
2868 if (d->hideCursor) |
|
2869 ctx.cursorPosition = -1; |
|
2870 else if (d->preeditCursor != 0) |
|
2871 ctx.cursorPosition = - (d->preeditCursor + 2); |
|
2872 else |
|
2873 ctx.cursorPosition = d->cursor.position(); |
|
2874 } |
|
2875 |
|
2876 if (!d->dndFeedbackCursor.isNull()) |
|
2877 ctx.cursorPosition = d->dndFeedbackCursor.position(); |
|
2878 #ifdef QT_KEYPAD_NAVIGATION |
|
2879 if (!QApplication::keypadNavigationEnabled() || d->hasEditFocus) |
|
2880 #endif |
|
2881 if (d->cursor.hasSelection()) { |
|
2882 QAbstractTextDocumentLayout::Selection selection; |
|
2883 selection.cursor = d->cursor; |
|
2884 if (d->cursorIsFocusIndicator) { |
|
2885 QStyleOption opt; |
|
2886 opt.palette = ctx.palette; |
|
2887 QStyleHintReturnVariant ret; |
|
2888 QStyle *style = QApplication::style(); |
|
2889 if (widget) |
|
2890 style = widget->style(); |
|
2891 style->styleHint(QStyle::SH_TextControl_FocusIndicatorTextCharFormat, &opt, widget, &ret); |
|
2892 selection.format = qVariantValue<QTextFormat>(ret.variant).toCharFormat(); |
|
2893 } else { |
|
2894 QPalette::ColorGroup cg = d->hasFocus ? QPalette::Active : QPalette::Inactive; |
|
2895 selection.format.setBackground(ctx.palette.brush(cg, QPalette::Highlight)); |
|
2896 selection.format.setForeground(ctx.palette.brush(cg, QPalette::HighlightedText)); |
|
2897 QStyleOption opt; |
|
2898 QStyle *style = QApplication::style(); |
|
2899 if (widget) { |
|
2900 opt.initFrom(widget); |
|
2901 style = widget->style(); |
|
2902 } |
|
2903 if (style->styleHint(QStyle::SH_RichText_FullWidthSelection, &opt, widget)) |
|
2904 selection.format.setProperty(QTextFormat::FullWidthSelection, true); |
|
2905 } |
|
2906 ctx.selections.append(selection); |
|
2907 } |
|
2908 |
|
2909 return ctx; |
|
2910 } |
|
2911 |
|
2912 void QTextControl::drawContents(QPainter *p, const QRectF &rect, QWidget *widget) |
|
2913 { |
|
2914 Q_D(QTextControl); |
|
2915 p->save(); |
|
2916 QAbstractTextDocumentLayout::PaintContext ctx = getPaintContext(widget); |
|
2917 if (rect.isValid()) |
|
2918 p->setClipRect(rect, Qt::IntersectClip); |
|
2919 ctx.clip = rect; |
|
2920 |
|
2921 d->doc->documentLayout()->draw(p, ctx); |
|
2922 p->restore(); |
|
2923 } |
|
2924 |
|
2925 void QTextControlPrivate::_q_copyLink() |
|
2926 { |
|
2927 #ifndef QT_NO_CLIPBOARD |
|
2928 QMimeData *md = new QMimeData; |
|
2929 md->setText(linkToCopy); |
|
2930 QApplication::clipboard()->setMimeData(md); |
|
2931 #endif |
|
2932 } |
|
2933 |
|
2934 QInputContext *QTextControlPrivate::inputContext() |
|
2935 { |
|
2936 QInputContext *ctx = contextWidget->inputContext(); |
|
2937 if (!ctx && contextWidget->parentWidget()) |
|
2938 ctx = contextWidget->parentWidget()->inputContext(); |
|
2939 return ctx; |
|
2940 } |
|
2941 |
|
2942 int QTextControl::hitTest(const QPointF &point, Qt::HitTestAccuracy accuracy) const |
|
2943 { |
|
2944 Q_D(const QTextControl); |
|
2945 return d->doc->documentLayout()->hitTest(point, accuracy); |
|
2946 } |
|
2947 |
|
2948 QRectF QTextControl::blockBoundingRect(const QTextBlock &block) const |
|
2949 { |
|
2950 Q_D(const QTextControl); |
|
2951 return d->doc->documentLayout()->blockBoundingRect(block); |
|
2952 } |
|
2953 |
|
2954 #ifndef QT_NO_CONTEXTMENU |
|
2955 #define NUM_CONTROL_CHARACTERS 10 |
|
2956 const struct QUnicodeControlCharacter { |
|
2957 const char *text; |
|
2958 ushort character; |
|
2959 } qt_controlCharacters[NUM_CONTROL_CHARACTERS] = { |
|
2960 { QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "LRM Left-to-right mark"), 0x200e }, |
|
2961 { QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "RLM Right-to-left mark"), 0x200f }, |
|
2962 { QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "ZWJ Zero width joiner"), 0x200d }, |
|
2963 { QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "ZWNJ Zero width non-joiner"), 0x200c }, |
|
2964 { QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "ZWSP Zero width space"), 0x200b }, |
|
2965 { QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "LRE Start of left-to-right embedding"), 0x202a }, |
|
2966 { QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "RLE Start of right-to-left embedding"), 0x202b }, |
|
2967 { QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "LRO Start of left-to-right override"), 0x202d }, |
|
2968 { QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "RLO Start of right-to-left override"), 0x202e }, |
|
2969 { QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "PDF Pop directional formatting"), 0x202c }, |
|
2970 }; |
|
2971 |
|
2972 QUnicodeControlCharacterMenu::QUnicodeControlCharacterMenu(QObject *_editWidget, QWidget *parent) |
|
2973 : QMenu(parent), editWidget(_editWidget) |
|
2974 { |
|
2975 setTitle(tr("Insert Unicode control character")); |
|
2976 for (int i = 0; i < NUM_CONTROL_CHARACTERS; ++i) { |
|
2977 addAction(tr(qt_controlCharacters[i].text), this, SLOT(menuActionTriggered())); |
|
2978 } |
|
2979 } |
|
2980 |
|
2981 void QUnicodeControlCharacterMenu::menuActionTriggered() |
|
2982 { |
|
2983 QAction *a = qobject_cast<QAction *>(sender()); |
|
2984 int idx = actions().indexOf(a); |
|
2985 if (idx < 0 || idx >= NUM_CONTROL_CHARACTERS) |
|
2986 return; |
|
2987 QChar c(qt_controlCharacters[idx].character); |
|
2988 QString str(c); |
|
2989 |
|
2990 #ifndef QT_NO_TEXTEDIT |
|
2991 if (QTextEdit *edit = qobject_cast<QTextEdit *>(editWidget)) { |
|
2992 edit->insertPlainText(str); |
|
2993 return; |
|
2994 } |
|
2995 #endif |
|
2996 if (QTextControl *control = qobject_cast<QTextControl *>(editWidget)) { |
|
2997 control->insertPlainText(str); |
|
2998 } |
|
2999 #ifndef QT_NO_LINEEDIT |
|
3000 if (QLineEdit *edit = qobject_cast<QLineEdit *>(editWidget)) { |
|
3001 edit->insert(str); |
|
3002 return; |
|
3003 } |
|
3004 #endif |
|
3005 } |
|
3006 #endif // QT_NO_CONTEXTMENU |
|
3007 |
|
3008 QStringList QTextEditMimeData::formats() const |
|
3009 { |
|
3010 if (!fragment.isEmpty()) |
|
3011 return QStringList() << QString::fromLatin1("text/plain") << QString::fromLatin1("text/html") |
|
3012 #ifndef QT_NO_TEXTODFWRITER |
|
3013 << QString::fromLatin1("application/vnd.oasis.opendocument.text") |
|
3014 #endif |
|
3015 ; |
|
3016 else |
|
3017 return QMimeData::formats(); |
|
3018 } |
|
3019 |
|
3020 QVariant QTextEditMimeData::retrieveData(const QString &mimeType, QVariant::Type type) const |
|
3021 { |
|
3022 if (!fragment.isEmpty()) |
|
3023 setup(); |
|
3024 return QMimeData::retrieveData(mimeType, type); |
|
3025 } |
|
3026 |
|
3027 void QTextEditMimeData::setup() const |
|
3028 { |
|
3029 QTextEditMimeData *that = const_cast<QTextEditMimeData *>(this); |
|
3030 #ifndef QT_NO_TEXTHTMLPARSER |
|
3031 that->setData(QLatin1String("text/html"), fragment.toHtml("utf-8").toUtf8()); |
|
3032 #endif |
|
3033 #ifndef QT_NO_TEXTODFWRITER |
|
3034 { |
|
3035 QBuffer buffer; |
|
3036 QTextDocumentWriter writer(&buffer, "ODF"); |
|
3037 writer.write(fragment); |
|
3038 buffer.close(); |
|
3039 that->setData(QLatin1String("application/vnd.oasis.opendocument.text"), buffer.data()); |
|
3040 } |
|
3041 #endif |
|
3042 that->setText(fragment.toPlainText()); |
|
3043 fragment = QTextDocumentFragment(); |
|
3044 } |
|
3045 |
|
3046 QT_END_NAMESPACE |
|
3047 |
|
3048 #include "moc_qtextcontrol_p.cpp" |
|
3049 |
|
3050 #endif // QT_NO_TEXTCONTROL |