diff options
Diffstat (limited to 'src/widgets/widgets/qwidgettextcontrol.cpp')
-rw-r--r-- | src/widgets/widgets/qwidgettextcontrol.cpp | 377 |
1 files changed, 213 insertions, 164 deletions
diff --git a/src/widgets/widgets/qwidgettextcontrol.cpp b/src/widgets/widgets/qwidgettextcontrol.cpp index 49a1a02867..96f2ec22ff 100644 --- a/src/widgets/widgets/qwidgettextcontrol.cpp +++ b/src/widgets/widgets/qwidgettextcontrol.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWidgets module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwidgettextcontrol_p.h" #include "qwidgettextcontrol_p_p.h" @@ -50,16 +14,16 @@ #include <qdrag.h> #endif #include <qclipboard.h> -#if QT_CONFIG(menu) -#include <qmenu.h> -#endif #include <qstyle.h> -#include <qtimer.h> #include "private/qapplication_p.h" #include "private/qtextdocumentlayout_p.h" #include "private/qabstracttextdocumentlayout_p.h" +#if QT_CONFIG(menu) +#include "private/qmenu_p.h" +#endif #include "qtextdocument.h" #include "private/qtextdocument_p.h" +#include "private/qtextdocumentfragment_p.h" #include "qtextlist.h" #include "private/qwidgettextcontrol_p.h" #if QT_CONFIG(style_stylesheet) @@ -84,7 +48,9 @@ #include <qurl.h> #include <qdesktopservices.h> #include <qinputmethod.h> +#if QT_CONFIG(tooltip) #include <qtooltip.h> +#endif #include <qstyleoption.h> #if QT_CONFIG(lineedit) #include <QtWidgets/qlineedit.h> @@ -96,10 +62,9 @@ #include "private/qapplication_p.h" #include "private/qshortcutmap_p.h" #include <qkeysequence.h> -#define ACCEL_KEY(k) ((!QCoreApplication::testAttribute(Qt::AA_DontShowShortcutsInContextMenus) \ - && QGuiApplication::styleHints()->showShortcutsInContextMenus()) \ +#define ACCEL_KEY(k) (!QCoreApplication::testAttribute(Qt::AA_DontShowShortcutsInContextMenus) \ && !QGuiApplicationPrivate::instance()->shortcutMap.hasShortcutForKeySequence(k) ? \ - QLatin1Char('\t') + QKeySequence(k).toString(QKeySequence::NativeText) : QString()) + u'\t' + QKeySequence(k).toString(QKeySequence::NativeText) : QString()) #else #define ACCEL_KEY(k) QString() @@ -109,6 +74,8 @@ QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + // could go into QTextCursor... static QTextLine currentTextLine(const QTextCursor &cursor) { @@ -445,25 +412,30 @@ void QWidgetTextControlPrivate::setContent(Qt::TextFormat format, const QString if (!doc) { if (document) { doc = document; - clearDocument = false; } else { palette = QApplication::palette("QWidgetTextControl"); doc = new QTextDocument(q); } + clearDocument = false; _q_documentLayoutChanged(); cursor = QTextCursor(doc); // #### doc->documentLayout()->setPaintDevice(viewport); - QObject::connect(doc, SIGNAL(contentsChanged()), q, SLOT(_q_updateCurrentCharFormatAndSelection())); - QObject::connect(doc, SIGNAL(cursorPositionChanged(QTextCursor)), q, SLOT(_q_emitCursorPosChanged(QTextCursor))); - QObject::connect(doc, SIGNAL(documentLayoutChanged()), q, SLOT(_q_documentLayoutChanged())); + QObjectPrivate::connect(doc, &QTextDocument::contentsChanged, this, + &QWidgetTextControlPrivate::_q_updateCurrentCharFormatAndSelection); + QObjectPrivate::connect(doc, &QTextDocument::cursorPositionChanged, this, + &QWidgetTextControlPrivate::_q_emitCursorPosChanged); + QObjectPrivate::connect(doc, &QTextDocument::documentLayoutChanged, this, + &QWidgetTextControlPrivate::_q_documentLayoutChanged); // convenience signal forwards - QObject::connect(doc, SIGNAL(undoAvailable(bool)), q, SIGNAL(undoAvailable(bool))); - QObject::connect(doc, SIGNAL(redoAvailable(bool)), q, SIGNAL(redoAvailable(bool))); - QObject::connect(doc, SIGNAL(modificationChanged(bool)), q, SIGNAL(modificationChanged(bool))); - QObject::connect(doc, SIGNAL(blockCountChanged(int)), q, SIGNAL(blockCountChanged(int))); + QObject::connect(doc, &QTextDocument::undoAvailable, q, &QWidgetTextControl::undoAvailable); + QObject::connect(doc, &QTextDocument::redoAvailable, q, &QWidgetTextControl::redoAvailable); + QObject::connect(doc, &QTextDocument::modificationChanged, q, + &QWidgetTextControl::modificationChanged); + QObject::connect(doc, &QTextDocument::blockCountChanged, q, + &QWidgetTextControl::blockCountChanged); } bool previousUndoRedoState = doc->isUndoRedoEnabled(); @@ -525,7 +497,8 @@ void QWidgetTextControlPrivate::setContent(Qt::TextFormat format, const QString q->ensureCursorVisible(); emit q->cursorPositionChanged(); - QObject::connect(doc, SIGNAL(contentsChange(int,int,int)), q, SLOT(_q_contentsChanged(int,int,int)), Qt::UniqueConnection); + QObjectPrivate::connect(doc, &QTextDocument::contentsChange, this, + &QWidgetTextControlPrivate::_q_contentsChanged, Qt::UniqueConnection); } void QWidgetTextControlPrivate::startDrag() @@ -605,7 +578,7 @@ void QWidgetTextControlPrivate::selectionChanged(bool forceEmitSelectionChanged Q_Q(QWidgetTextControl); if (forceEmitSelectionChanged) { emit q->selectionChanged(); -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) if (q->parent() && q->parent()->isWidgetType()) { QAccessibleTextSelectionEvent ev(q->parent(), cursor.anchor(), cursor.position()); QAccessible::updateAccessibility(&ev); @@ -628,7 +601,7 @@ void QWidgetTextControlPrivate::selectionChanged(bool forceEmitSelectionChanged && (cursor.position() != lastSelectionPosition || cursor.anchor() != lastSelectionAnchor)))) { emit q->selectionChanged(); -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) if (q->parent() && q->parent()->isWidgetType()) { QAccessibleTextSelectionEvent ev(q->parent(), cursor.anchor(), cursor.position()); QAccessible::updateAccessibility(&ev); @@ -669,7 +642,7 @@ void QWidgetTextControlPrivate::_q_emitCursorPosChanged(const QTextCursor &someC void QWidgetTextControlPrivate::_q_contentsChanged(int from, int charsRemoved, int charsAdded) { -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) Q_Q(QWidgetTextControl); if (QAccessible::isActive() && q->parent() && q->parent()->isWidgetType()) { @@ -681,7 +654,7 @@ void QWidgetTextControlPrivate::_q_contentsChanged(int from, int charsRemoved, i QString newText = tmp.selectedText(); // always report the right number of removed chars, but in lack of the real string use spaces - QString oldText = QString(charsRemoved, QLatin1Char(' ')); + QString oldText = QString(charsRemoved, u' '); QAccessibleEvent *ev = nullptr; if (charsRemoved == 0) { @@ -695,9 +668,9 @@ void QWidgetTextControlPrivate::_q_contentsChanged(int from, int charsRemoved, i delete ev; } #else - Q_UNUSED(from) - Q_UNUSED(charsRemoved) - Q_UNUSED(charsAdded) + Q_UNUSED(from); + Q_UNUSED(charsRemoved); + Q_UNUSED(charsAdded); #endif } @@ -705,10 +678,12 @@ void QWidgetTextControlPrivate::_q_documentLayoutChanged() { Q_Q(QWidgetTextControl); QAbstractTextDocumentLayout *layout = doc->documentLayout(); - QObject::connect(layout, SIGNAL(update(QRectF)), q, SIGNAL(updateRequest(QRectF))); - QObject::connect(layout, SIGNAL(updateBlock(QTextBlock)), q, SLOT(_q_updateBlock(QTextBlock))); - QObject::connect(layout, SIGNAL(documentSizeChanged(QSizeF)), q, SIGNAL(documentSizeChanged(QSizeF))); - + QObject::connect(layout, &QAbstractTextDocumentLayout::update, q, + &QWidgetTextControl::updateRequest); + QObjectPrivate::connect(layout, &QAbstractTextDocumentLayout::updateBlock, this, + &QWidgetTextControlPrivate::_q_updateBlock); + QObject::connect(layout, &QAbstractTextDocumentLayout::documentSizeChanged, q, + &QWidgetTextControl::documentSizeChanged); } void QWidgetTextControlPrivate::setCursorVisible(bool visible) @@ -921,7 +896,7 @@ QTextDocument *QWidgetTextControl::document() const return d->doc; } -void QWidgetTextControl::setTextCursor(const QTextCursor &cursor) +void QWidgetTextControl::setTextCursor(const QTextCursor &cursor, bool selectionClipboard) { Q_D(QWidgetTextControl); d->cursorIsFocusIndicator = false; @@ -935,6 +910,13 @@ void QWidgetTextControl::setTextCursor(const QTextCursor &cursor) d->repaintOldAndNewSelection(oldSelection); if (posChanged) emit cursorPositionChanged(); + +#ifndef QT_NO_CLIPBOARD + if (selectionClipboard) + d->setClipboardSelection(); +#else + Q_UNUSED(selectionClipboard); +#endif } QTextCursor QWidgetTextControl::textCursor() const @@ -984,20 +966,23 @@ void QWidgetTextControl::selectAll() { Q_D(QWidgetTextControl); const int selectionLength = qAbs(d->cursor.position() - d->cursor.anchor()); + const int oldCursorPos = d->cursor.position(); d->cursor.select(QTextCursor::Document); d->selectionChanged(selectionLength != qAbs(d->cursor.position() - d->cursor.anchor())); d->cursorIsFocusIndicator = false; + if (d->cursor.position() != oldCursorPos) + emit cursorPositionChanged(); emit updateRequest(); } void QWidgetTextControl::processEvent(QEvent *e, const QPointF &coordinateOffset, QWidget *contextWidget) { - QMatrix m; - m.translate(coordinateOffset.x(), coordinateOffset.y()); - processEvent(e, m, contextWidget); + QTransform t; + t.translate(coordinateOffset.x(), coordinateOffset.y()); + processEvent(e, t, contextWidget); } -void QWidgetTextControl::processEvent(QEvent *e, const QMatrix &matrix, QWidget *contextWidget) +void QWidgetTextControl::processEvent(QEvent *e, const QTransform &transform, QWidget *contextWidget) { Q_D(QWidgetTextControl); if (d->interactionFlags == Qt::NoTextInteraction) { @@ -1038,23 +1023,23 @@ void QWidgetTextControl::processEvent(QEvent *e, const QMatrix &matrix, QWidget break; case QEvent::MouseButtonPress: { QMouseEvent *ev = static_cast<QMouseEvent *>(e); - d->mousePressEvent(ev, ev->button(), matrix.map(ev->pos()), ev->modifiers(), - ev->buttons(), ev->globalPos()); + d->mousePressEvent(ev, ev->button(), transform.map(ev->position().toPoint()), ev->modifiers(), + ev->buttons(), ev->globalPosition().toPoint()); break; } case QEvent::MouseMove: { QMouseEvent *ev = static_cast<QMouseEvent *>(e); - d->mouseMoveEvent(ev, ev->button(), matrix.map(ev->pos()), ev->modifiers(), - ev->buttons(), ev->globalPos()); + d->mouseMoveEvent(ev, ev->button(), transform.map(ev->position().toPoint()), ev->modifiers(), + ev->buttons(), ev->globalPosition().toPoint()); break; } case QEvent::MouseButtonRelease: { QMouseEvent *ev = static_cast<QMouseEvent *>(e); - d->mouseReleaseEvent(ev, ev->button(), matrix.map(ev->pos()), ev->modifiers(), - ev->buttons(), ev->globalPos()); + d->mouseReleaseEvent(ev, ev->button(), transform.map(ev->position().toPoint()), ev->modifiers(), + ev->buttons(), ev->globalPosition().toPoint()); break; } case QEvent::MouseButtonDblClick: { QMouseEvent *ev = static_cast<QMouseEvent *>(e); - d->mouseDoubleClickEvent(ev, ev->button(), matrix.map(ev->pos()), ev->modifiers(), - ev->buttons(), ev->globalPos()); + d->mouseDoubleClickEvent(ev, ev->button(), transform.map(ev->position().toPoint()), ev->modifiers(), + ev->buttons(), ev->globalPosition().toPoint()); break; } case QEvent::InputMethod: d->inputMethodEvent(static_cast<QInputMethodEvent *>(e)); @@ -1062,7 +1047,7 @@ void QWidgetTextControl::processEvent(QEvent *e, const QMatrix &matrix, QWidget #ifndef QT_NO_CONTEXTMENU case QEvent::ContextMenu: { QContextMenuEvent *ev = static_cast<QContextMenuEvent *>(e); - d->contextMenuEvent(ev->globalPos(), matrix.map(ev->pos()), contextWidget); + d->contextMenuEvent(ev->globalPos(), transform.map(ev->pos()), contextWidget); break; } #endif // QT_NO_CONTEXTMENU case QEvent::FocusIn: @@ -1074,13 +1059,13 @@ void QWidgetTextControl::processEvent(QEvent *e, const QMatrix &matrix, QWidget d->isEnabled = e->isAccepted(); break; -#ifndef QT_NO_TOOLTIP +#if QT_CONFIG(tooltip) case QEvent::ToolTip: { QHelpEvent *ev = static_cast<QHelpEvent *>(e); - d->showToolTip(ev->globalPos(), matrix.map(ev->pos()), contextWidget); + d->showToolTip(ev->globalPos(), transform.map(ev->pos()), contextWidget); break; } -#endif // QT_NO_TOOLTIP +#endif // QT_CONFIG(tooltip) #if QT_CONFIG(draganddrop) case QEvent::DragEnter: { @@ -1094,13 +1079,13 @@ void QWidgetTextControl::processEvent(QEvent *e, const QMatrix &matrix, QWidget break; case QEvent::DragMove: { QDragMoveEvent *ev = static_cast<QDragMoveEvent *>(e); - if (d->dragMoveEvent(e, ev->mimeData(), matrix.map(ev->pos()))) + if (d->dragMoveEvent(e, ev->mimeData(), transform.map(ev->position().toPoint()))) ev->acceptProposedAction(); break; } case QEvent::Drop: { QDropEvent *ev = static_cast<QDropEvent *>(e); - if (d->dropEvent(ev->mimeData(), matrix.map(ev->pos()), ev->dropAction(), ev->source())) + if (d->dropEvent(ev->mimeData(), transform.map(ev->position().toPoint()), ev->dropAction(), ev->source())) ev->acceptProposedAction(); break; } @@ -1109,32 +1094,32 @@ void QWidgetTextControl::processEvent(QEvent *e, const QMatrix &matrix, QWidget #if QT_CONFIG(graphicsview) case QEvent::GraphicsSceneMousePress: { QGraphicsSceneMouseEvent *ev = static_cast<QGraphicsSceneMouseEvent *>(e); - d->mousePressEvent(ev, ev->button(), matrix.map(ev->pos()), ev->modifiers(), ev->buttons(), + d->mousePressEvent(ev, ev->button(), transform.map(ev->pos()), ev->modifiers(), ev->buttons(), ev->screenPos()); break; } case QEvent::GraphicsSceneMouseMove: { QGraphicsSceneMouseEvent *ev = static_cast<QGraphicsSceneMouseEvent *>(e); - d->mouseMoveEvent(ev, ev->button(), matrix.map(ev->pos()), ev->modifiers(), ev->buttons(), + d->mouseMoveEvent(ev, ev->button(), transform.map(ev->pos()), ev->modifiers(), ev->buttons(), ev->screenPos()); break; } case QEvent::GraphicsSceneMouseRelease: { QGraphicsSceneMouseEvent *ev = static_cast<QGraphicsSceneMouseEvent *>(e); - d->mouseReleaseEvent(ev, ev->button(), matrix.map(ev->pos()), ev->modifiers(), ev->buttons(), + d->mouseReleaseEvent(ev, ev->button(), transform.map(ev->pos()), ev->modifiers(), ev->buttons(), ev->screenPos()); break; } case QEvent::GraphicsSceneMouseDoubleClick: { QGraphicsSceneMouseEvent *ev = static_cast<QGraphicsSceneMouseEvent *>(e); - d->mouseDoubleClickEvent(ev, ev->button(), matrix.map(ev->pos()), ev->modifiers(), ev->buttons(), + d->mouseDoubleClickEvent(ev, ev->button(), transform.map(ev->pos()), ev->modifiers(), ev->buttons(), ev->screenPos()); break; } case QEvent::GraphicsSceneContextMenu: { QGraphicsSceneContextMenuEvent *ev = static_cast<QGraphicsSceneContextMenuEvent *>(e); - d->contextMenuEvent(ev->screenPos(), matrix.map(ev->pos()), contextWidget); + d->contextMenuEvent(ev->screenPos(), transform.map(ev->pos()), contextWidget); break; } case QEvent::GraphicsSceneHoverMove: { QGraphicsSceneHoverEvent *ev = static_cast<QGraphicsSceneHoverEvent *>(e); - d->mouseMoveEvent(ev, Qt::NoButton, matrix.map(ev->pos()), ev->modifiers(),Qt::NoButton, + d->mouseMoveEvent(ev, Qt::NoButton, transform.map(ev->pos()), ev->modifiers(),Qt::NoButton, ev->screenPos()); break; } @@ -1148,12 +1133,12 @@ void QWidgetTextControl::processEvent(QEvent *e, const QMatrix &matrix, QWidget break; case QEvent::GraphicsSceneDragMove: { QGraphicsSceneDragDropEvent *ev = static_cast<QGraphicsSceneDragDropEvent *>(e); - if (d->dragMoveEvent(e, ev->mimeData(), matrix.map(ev->pos()))) + if (d->dragMoveEvent(e, ev->mimeData(), transform.map(ev->pos()))) ev->acceptProposedAction(); break; } case QEvent::GraphicsSceneDrop: { QGraphicsSceneDragDropEvent *ev = static_cast<QGraphicsSceneDragDropEvent *>(e); - if (d->dropEvent(ev->mimeData(), matrix.map(ev->pos()), ev->dropAction(), ev->source())) + if (d->dropEvent(ev->mimeData(), transform.map(ev->pos()), ev->dropAction(), ev->source())) ev->accept(); break; } #endif // QT_CONFIG(graphicsview) @@ -1224,6 +1209,9 @@ void QWidgetTextControlPrivate::keyPressEvent(QKeyEvent *e) if (e == QKeySequence::SelectAll) { e->accept(); q->selectAll(); +#ifndef QT_NO_CLIPBOARD + setClipboardSelection(); +#endif return; } #ifndef QT_NO_CLIPBOARD @@ -1272,7 +1260,7 @@ void QWidgetTextControlPrivate::keyPressEvent(QKeyEvent *e) // example) repaintSelection(); - if (e->key() == Qt::Key_Backspace && !(e->modifiers() & ~Qt::ShiftModifier)) { + if (e->key() == Qt::Key_Backspace && !(e->modifiers() & ~(Qt::ShiftModifier | Qt::GroupSwitchModifier))) { QTextBlockFormat blockFmt = cursor.blockFormat(); QTextList *list = cursor.currentList(); if (list && cursor.atBlockStart() && !cursor.hasSelection()) { @@ -1290,7 +1278,7 @@ void QWidgetTextControlPrivate::keyPressEvent(QKeyEvent *e) } #ifndef QT_NO_SHORTCUT else if (e == QKeySequence::InsertParagraphSeparator) { - cursor.insertBlock(); + insertParagraphSeparator(); e->accept(); goto accept; } else if (e == QKeySequence::InsertLineSeparator) { @@ -1375,6 +1363,10 @@ process: accept: +#ifndef QT_NO_CLIPBOARD + setClipboardSelection(); +#endif + e->accept(); cursorOn = true; @@ -1413,16 +1405,14 @@ QRectF QWidgetTextControlPrivate::rectForPosition(int position) const if (relativePos == preeditPos) relativePos += preeditCursor; else if (relativePos > preeditPos) - relativePos += layout->preeditAreaText().length(); + relativePos += layout->preeditAreaText().size(); } QTextLine line = layout->lineForTextPosition(relativePos); int cursorWidth; { bool ok = false; -#ifndef QT_NO_PROPERTIES cursorWidth = docLayout->property("cursorWidth").toInt(&ok); -#endif if (!ok) cursorWidth = 1; } @@ -1436,7 +1426,7 @@ QRectF QWidgetTextControlPrivate::rectForPosition(int position) const if (relativePos < line.textLength() - line.textStart()) w = line.cursorToX(relativePos + 1) - x; else - w = QFontMetrics(block.layout()->font()).horizontalAdvance(QLatin1Char(' ')); // in sync with QTextLine::draw() + w = QFontMetrics(block.layout()->font()).horizontalAdvance(u' '); // in sync with QTextLine::draw() } r = QRectF(layoutPos.x() + x, layoutPos.y() + line.y(), cursorWidth + w, line.height()); @@ -1792,7 +1782,7 @@ void QWidgetTextControlPrivate::mouseReleaseEvent(QEvent *e, Qt::MouseButton but #ifndef QT_NO_CLIPBOARD setClipboardSelection(); selectionChanged(true); - } else if (button == Qt::MidButton + } else if (button == Qt::MiddleButton && (interactionFlags & Qt::TextEditable) && QGuiApplication::clipboard()->supportsSelection()) { setCursorPosition(pos); @@ -1830,25 +1820,36 @@ void QWidgetTextControlPrivate::mouseReleaseEvent(QEvent *e, Qt::MouseButton but } if (interactionFlags & Qt::LinksAccessibleByMouse) { - if (!(button & Qt::LeftButton)) + + // Ignore event unless left button has been pressed + if (!(button & Qt::LeftButton)) { + e->ignore(); return; + } const QString anchor = q->anchorAt(pos); - if (anchor.isEmpty()) + // Ignore event without selection anchor + if (anchor.isEmpty()) { + e->ignore(); return; + } if (!cursor.hasSelection() || (anchor == anchorOnMousePress && hadSelectionOnMousePress)) { const int anchorPos = q->hitTest(pos, Qt::ExactHit); - if (anchorPos != -1) { - cursor.setPosition(anchorPos); - QString anchor = anchorOnMousePress; - anchorOnMousePress = QString(); - activateLinkUnderCursor(anchor); + // Ignore event without valid anchor position + if (anchorPos < 0) { + e->ignore(); + return; } + + cursor.setPosition(anchorPos); + QString anchor = anchorOnMousePress; + anchorOnMousePress = QString(); + activateLinkUnderCursor(anchor); } } } @@ -1912,7 +1913,7 @@ bool QWidgetTextControlPrivate::sendMouseEventToInputContext( QTextLayout *layout = cursor.block().layout(); int cursorPos = q->hitTest(pos, Qt::FuzzyHit) - cursor.position(); - if (cursorPos < 0 || cursorPos > layout->preeditAreaText().length()) + if (cursorPos < 0 || cursorPos > layout->preeditAreaText().size()) cursorPos = -1; if (cursorPos >= 0) { @@ -1941,6 +1942,12 @@ void QWidgetTextControlPrivate::contextMenuEvent(const QPoint &screenPos, const if (!menu) return; menu->setAttribute(Qt::WA_DeleteOnClose); + + if (auto *widget = qobject_cast<QWidget *>(parent)) { + if (auto *window = widget->window()->windowHandle()) + QMenuPrivate::get(menu)->topData()->initialScreen = window->screen(); + } + menu->popup(screenPos); #endif } @@ -2019,7 +2026,7 @@ bool QWidgetTextControlPrivate::dropEvent(const QMimeData *mimeData, const QPoin void QWidgetTextControlPrivate::inputMethodEvent(QInputMethodEvent *e) { Q_Q(QWidgetTextControl); - if (!(interactionFlags & Qt::TextEditable) || cursor.isNull()) { + if (!(interactionFlags & (Qt::TextEditable | Qt::TextSelectableByMouse)) || cursor.isNull()) { e->ignore(); return; } @@ -2027,6 +2034,11 @@ void QWidgetTextControlPrivate::inputMethodEvent(QInputMethodEvent *e) || e->preeditString() != cursor.block().layout()->preeditAreaText() || e->replacementLength() > 0; + if (!isGettingInput && e->attributes().isEmpty()) { + e->ignore(); + return; + } + int oldCursorPos = cursor.position(); cursor.beginEditBlock(); @@ -2063,10 +2075,10 @@ void QWidgetTextControlPrivate::inputMethodEvent(QInputMethodEvent *e) QTextLayout *layout = block.layout(); if (isGettingInput) layout->setPreeditArea(cursor.position() - block.position(), e->preeditString()); - QVector<QTextLayout::FormatRange> overrides; + QList<QTextLayout::FormatRange> overrides; overrides.reserve(e->attributes().size()); const int oldPreeditCursor = preeditCursor; - preeditCursor = e->preeditString().length(); + preeditCursor = e->preeditString().size(); hideCursor = false; for (int i = 0; i < e->attributes().size(); ++i) { const QInputMethodEvent::Attribute &a = e->attributes().at(i); @@ -2083,9 +2095,9 @@ void QWidgetTextControlPrivate::inputMethodEvent(QInputMethodEvent *e) o.format = f; // Make sure list is sorted by start index - QVector<QTextLayout::FormatRange>::iterator it = overrides.end(); + QList<QTextLayout::FormatRange>::iterator it = overrides.end(); while (it != overrides.begin()) { - QVector<QTextLayout::FormatRange>::iterator previous = it - 1; + QList<QTextLayout::FormatRange>::iterator previous = it - 1; if (o.start >= previous->start) { overrides.insert(it, o); break; @@ -2101,9 +2113,9 @@ void QWidgetTextControlPrivate::inputMethodEvent(QInputMethodEvent *e) if (cursor.charFormat().isValid()) { int start = cursor.position() - block.position(); - int end = start + e->preeditString().length(); + int end = start + e->preeditString().size(); - QVector<QTextLayout::FormatRange>::iterator it = overrides.begin(); + QList<QTextLayout::FormatRange>::iterator it = overrides.begin(); while (it != overrides.end()) { QTextLayout::FormatRange range = *it; int rangeStart = range.start; @@ -2174,12 +2186,12 @@ QVariant QWidgetTextControl::inputMethodQuery(Qt::InputMethodQuery property, QVa QTextCursor tmpCursor = d->cursor; int localPos = d->cursor.position() - block.position(); QString result = block.text().mid(localPos); - while (result.length() < maxLength) { + while (result.size() < maxLength) { int currentBlock = tmpCursor.blockNumber(); tmpCursor.movePosition(QTextCursor::NextBlock); if (tmpCursor.blockNumber() == currentBlock) break; - result += QLatin1Char('\n') + tmpCursor.block().text(); + result += u'\n' + tmpCursor.block().text(); } return QVariant(result); } @@ -2200,11 +2212,11 @@ QVariant QWidgetTextControl::inputMethodQuery(Qt::InputMethodQuery property, QVa } QString result; while (numBlocks) { - result += tmpCursor.block().text() + QLatin1Char('\n'); + result += tmpCursor.block().text() + u'\n'; tmpCursor.movePosition(QTextCursor::NextBlock); --numBlocks; } - result += block.text().midRef(0, localPos); + result += QStringView{block.text()}.mid(0, localPos); return QVariant(result); } default: @@ -2236,6 +2248,7 @@ void QWidgetTextControlPrivate::focusEvent(QFocusEvent *e) #endif } else { setCursorVisible(false); + cursorOn = false; if (cursorIsFocusIndicator && e->reason() != Qt::ActiveWindowFocusReason @@ -2290,7 +2303,7 @@ void QWidgetTextControlPrivate::editFocusEvent(QEvent *e) #endif #ifndef QT_NO_CONTEXTMENU -static inline void setActionIcon(QAction *action, const QString &name) +void setActionIcon(QAction *action, const QString &name) { const QIcon icon = QIcon::fromTheme(name); if (!icon.isNull()) @@ -2448,24 +2461,16 @@ void QWidgetTextControl::setOverwriteMode(bool overwrite) int QWidgetTextControl::cursorWidth() const { -#ifndef QT_NO_PROPERTIES Q_D(const QWidgetTextControl); return d->doc->documentLayout()->property("cursorWidth").toInt(); -#else - return 1; -#endif } void QWidgetTextControl::setCursorWidth(int width) { Q_D(QWidgetTextControl); -#ifdef QT_NO_PROPERTIES - Q_UNUSED(width); -#else if (width == -1) - width = QApplication::style()->pixelMetric(QStyle::PM_TextCursorWidth); + width = QApplication::style()->pixelMetric(QStyle::PM_TextCursorWidth, nullptr, qobject_cast<QWidget *>(parent())); d->doc->documentLayout()->setProperty("cursorWidth", width); -#endif d->repaintCursor(); } @@ -2488,12 +2493,12 @@ void QWidgetTextControl::setExtraSelections(const QList<QTextEdit::ExtraSelectio Q_D(QWidgetTextControl); QMultiHash<int, int> hash; - for (int i = 0; i < d->extraSelections.count(); ++i) { + for (int i = 0; i < d->extraSelections.size(); ++i) { const QAbstractTextDocumentLayout::Selection &esel = d->extraSelections.at(i); hash.insert(esel.cursor.anchor(), i); } - for (int i = 0; i < selections.count(); ++i) { + for (int i = 0; i < selections.size(); ++i) { const QTextEdit::ExtraSelection &sel = selections.at(i); const auto it = hash.constFind(sel.cursor.anchor()); if (it != hash.cend()) { @@ -2512,7 +2517,7 @@ void QWidgetTextControl::setExtraSelections(const QList<QTextEdit::ExtraSelectio emit updateRequest(r); } - for (QHash<int, int>::iterator it = hash.begin(); it != hash.end(); ++it) { + for (auto it = hash.cbegin(); it != hash.cend(); ++it) { const QAbstractTextDocumentLayout::Selection &esel = d->extraSelections.at(it.value()); QRectF r = selectionRect(esel.cursor); if (esel.format.boolProperty(QTextFormat::FullWidthSelection)) { @@ -2522,8 +2527,8 @@ void QWidgetTextControl::setExtraSelections(const QList<QTextEdit::ExtraSelectio emit updateRequest(r); } - d->extraSelections.resize(selections.count()); - for (int i = 0; i < selections.count(); ++i) { + d->extraSelections.resize(selections.size()); + for (int i = 0; i < selections.size(); ++i) { d->extraSelections[i].cursor = selections.at(i).cursor; d->extraSelections[i].format = selections.at(i).format; } @@ -2533,7 +2538,7 @@ QList<QTextEdit::ExtraSelection> QWidgetTextControl::extraSelections() const { Q_D(const QWidgetTextControl); QList<QTextEdit::ExtraSelection> selections; - const int numExtraSelections = d->extraSelections.count(); + const int numExtraSelections = d->extraSelections.size(); selections.reserve(numExtraSelections); for (int i = 0; i < numExtraSelections; ++i) { QTextEdit::ExtraSelection sel; @@ -2668,6 +2673,7 @@ void QWidgetTextControl::print(QPagedPaintDevice *printer) const if (!d->cursor.hasSelection()) return; tempDoc = new QTextDocument(const_cast<QTextDocument *>(doc)); + tempDoc->setResourceProvider(doc->resourceProvider()); tempDoc->setMetaInformation(QTextDocument::DocumentTitle, doc->metaInformation(QTextDocument::DocumentTitle)); tempDoc->setPageSize(doc->pageSize()); tempDoc->setDefaultFont(doc->defaultFont()); @@ -2696,8 +2702,8 @@ bool QWidgetTextControl::canInsertFromMimeData(const QMimeData *source) const if (d->acceptRichText) return (source->hasText() && !source->text().isEmpty()) || source->hasHtml() - || source->hasFormat(QLatin1String("application/x-qrichtext")) - || source->hasFormat(QLatin1String("application/x-qt-richtext")); + || source->hasFormat("application/x-qrichtext"_L1) + || source->hasFormat("application/x-qt-richtext"_L1); else return source->hasText() && !source->text().isEmpty(); } @@ -2710,26 +2716,33 @@ void QWidgetTextControl::insertFromMimeData(const QMimeData *source) bool hasData = false; QTextDocumentFragment fragment; +#if QT_CONFIG(textmarkdownreader) + const auto formats = source->formats(); + if (formats.size() && formats.first() == "text/markdown"_L1) { + auto s = QString::fromUtf8(source->data("text/markdown"_L1)); + fragment = QTextDocumentFragment::fromMarkdown(s); + hasData = true; + } else +#endif #ifndef QT_NO_TEXTHTMLPARSER - if (source->hasFormat(QLatin1String("application/x-qrichtext")) && d->acceptRichText) { + if (source->hasFormat("application/x-qrichtext"_L1) && d->acceptRichText) { // x-qrichtext is always UTF-8 (taken from Qt3 since we don't use it anymore). - const QString richtext = QLatin1String("<meta name=\"qrichtext\" content=\"1\" />") - + QString::fromUtf8(source->data(QLatin1String("application/x-qrichtext"))); + const QString richtext = "<meta name=\"qrichtext\" content=\"1\" />"_L1 + + QString::fromUtf8(source->data("application/x-qrichtext"_L1)); fragment = QTextDocumentFragment::fromHtml(richtext, d->doc); hasData = true; } else if (source->hasHtml() && d->acceptRichText) { fragment = QTextDocumentFragment::fromHtml(source->html(), d->doc); hasData = true; - } else { - QString text = source->text(); + } +#endif // QT_NO_TEXTHTMLPARSER + if (!hasData) { + const QString text = source->text(); if (!text.isNull()) { fragment = QTextDocumentFragment::fromPlainText(text); hasData = true; } } -#else - fragment = QTextDocumentFragment::fromPlainText(source->text()); -#endif // QT_NO_TEXTHTMLPARSER if (hasData) d->cursor.insertFragment(fragment); @@ -2947,7 +2960,7 @@ void QWidgetTextControlPrivate::activateLinkUnderCursor(QString href) emit q_func()->linkActivated(href); } -#ifndef QT_NO_TOOLTIP +#if QT_CONFIG(tooltip) void QWidgetTextControlPrivate::showToolTip(const QPoint &globalPos, const QPointF &pos, QWidget *contextWidget) { const QString toolTip = q_func()->cursorForPosition(pos).charFormat().toolTip(); @@ -2955,7 +2968,7 @@ void QWidgetTextControlPrivate::showToolTip(const QPoint &globalPos, const QPoin return; QToolTip::showText(globalPos, toolTip, contextWidget); } -#endif // QT_NO_TOOLTIP +#endif // QT_CONFIG(tooltip) bool QWidgetTextControlPrivate::isPreediting() const { @@ -3145,19 +3158,6 @@ bool QWidgetTextControl::find(const QString &exp, QTextDocument::FindFlags optio return true; } -#ifndef QT_NO_REGEXP -bool QWidgetTextControl::find(const QRegExp &exp, QTextDocument::FindFlags options) -{ - Q_D(QWidgetTextControl); - QTextCursor search = d->doc->find(exp, d->cursor, options); - if (search.isNull()) - return false; - - setTextCursor(search); - return true; -} -#endif - #if QT_CONFIG(regularexpression) bool QWidgetTextControl::find(const QRegularExpression &exp, QTextDocument::FindFlags options) { @@ -3190,6 +3190,49 @@ QString QWidgetTextControl::toMarkdown(QTextDocument::MarkdownFeatures features) } #endif +void QWidgetTextControlPrivate::insertParagraphSeparator() +{ + // clear blockFormat properties that the user is unlikely to want duplicated: + // - don't insert <hr/> automatically + // - the next paragraph after a heading should be a normal paragraph + // - remove the bottom margin from the last list item before appending + // - the next checklist item after a checked item should be unchecked + auto blockFmt = cursor.blockFormat(); + auto charFmt = cursor.charFormat(); + blockFmt.clearProperty(QTextFormat::BlockTrailingHorizontalRulerWidth); + if (blockFmt.hasProperty(QTextFormat::HeadingLevel)) { + blockFmt.clearProperty(QTextFormat::HeadingLevel); + charFmt = QTextCharFormat(); + } + if (cursor.currentList()) { + auto existingFmt = cursor.blockFormat(); + existingFmt.clearProperty(QTextBlockFormat::BlockBottomMargin); + cursor.setBlockFormat(existingFmt); + if (blockFmt.marker() == QTextBlockFormat::MarkerType::Checked) + blockFmt.setMarker(QTextBlockFormat::MarkerType::Unchecked); + } + + // After a blank line, reset block and char formats. I.e. you can end a list, + // block quote, etc. by hitting enter twice, and get back to normal paragraph style. + if (cursor.block().text().isEmpty() && + !cursor.blockFormat().hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth) && + !cursor.blockFormat().hasProperty(QTextFormat::BlockCodeLanguage)) { + blockFmt = QTextBlockFormat(); + const bool blockFmtChanged = (cursor.blockFormat() != blockFmt); + charFmt = QTextCharFormat(); + cursor.setBlockFormat(blockFmt); + cursor.setCharFormat(charFmt); + // If the user hit enter twice just to get back to default format, + // don't actually insert a new block. But if the user then hits enter + // yet again, the block format will not change, so we will insert a block. + // This is what many word processors do. + if (blockFmtChanged) + return; + } + + cursor.insertBlock(blockFmt, charFmt); +} + void QWidgetTextControlPrivate::append(const QString &text, Qt::TextFormat format) { QTextCursor tmp(doc); @@ -3416,16 +3459,19 @@ void QUnicodeControlCharacterMenu::menuActionTriggered() QStringList QTextEditMimeData::formats() const { if (!fragment.isEmpty()) - return QStringList() << QString::fromLatin1("text/plain") << QString::fromLatin1("text/html") + return QStringList() << u"text/plain"_s << u"text/html"_s +#if QT_CONFIG(textmarkdownwriter) + << u"text/markdown"_s +#endif #ifndef QT_NO_TEXTODFWRITER - << QString::fromLatin1("application/vnd.oasis.opendocument.text") + << u"application/vnd.oasis.opendocument.text"_s #endif ; else return QMimeData::formats(); } -QVariant QTextEditMimeData::retrieveData(const QString &mimeType, QVariant::Type type) const +QVariant QTextEditMimeData::retrieveData(const QString &mimeType, QMetaType type) const { if (!fragment.isEmpty()) setup(); @@ -3436,7 +3482,10 @@ void QTextEditMimeData::setup() const { QTextEditMimeData *that = const_cast<QTextEditMimeData *>(this); #ifndef QT_NO_TEXTHTMLPARSER - that->setData(QLatin1String("text/html"), fragment.toHtml("utf-8").toUtf8()); + that->setData("text/html"_L1, fragment.toHtml().toUtf8()); +#endif +#if QT_CONFIG(textmarkdownwriter) + that->setData("text/markdown"_L1, fragment.toMarkdown().toUtf8()); #endif #ifndef QT_NO_TEXTODFWRITER { @@ -3444,7 +3493,7 @@ void QTextEditMimeData::setup() const QTextDocumentWriter writer(&buffer, "ODF"); writer.write(fragment); buffer.close(); - that->setData(QLatin1String("application/vnd.oasis.opendocument.text"), buffer.data()); + that->setData("application/vnd.oasis.opendocument.text"_L1, buffer.data()); } #endif that->setText(fragment.toPlainText()); |