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