diff options
Diffstat (limited to 'src/widgets/widgets/qwidgettextcontrol.cpp')
-rw-r--r-- | src/widgets/widgets/qwidgettextcontrol.cpp | 497 |
1 files changed, 312 insertions, 185 deletions
diff --git a/src/widgets/widgets/qwidgettextcontrol.cpp b/src/widgets/widgets/qwidgettextcontrol.cpp index ee757bf870..6dcaeee0c8 100644 --- a/src/widgets/widgets/qwidgettextcontrol.cpp +++ b/src/widgets/widgets/qwidgettextcontrol.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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,18 +14,17 @@ #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(textedit) -#include "private/qtextedit_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) @@ -86,7 +49,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> @@ -94,14 +59,13 @@ #include <QtGui/qaccessible.h> #include <QtCore/qmetaobject.h> -#ifndef QT_NO_SHORTCUT +#if QT_CONFIG(shortcut) #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()) \ - && !qApp->d_func()->shortcutMap.hasShortcutForKeySequence(k) ? \ - QLatin1Char('\t') + QKeySequence(k).toString(QKeySequence::NativeText) : QString()) +#define ACCEL_KEY(k) (!QCoreApplication::testAttribute(Qt::AA_DontShowShortcutsInContextMenus) \ + && !QGuiApplicationPrivate::instance()->shortcutMap.hasShortcutForKeySequence(k) ? \ + u'\t' + QKeySequence(k).toString(QKeySequence::NativeText) : QString()) #else #define ACCEL_KEY(k) QString() @@ -111,6 +75,8 @@ QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + // could go into QTextCursor... static QTextLine currentTextLine(const QTextCursor &cursor) { @@ -127,7 +93,7 @@ static QTextLine currentTextLine(const QTextCursor &cursor) } QWidgetTextControlPrivate::QWidgetTextControlPrivate() - : doc(0), cursorOn(false), cursorVisible(false), cursorIsFocusIndicator(false), + : doc(nullptr), cursorOn(false), cursorVisible(false), cursorIsFocusIndicator(false), #ifndef Q_OS_ANDROID interactionFlags(Qt::TextEditorInteraction), #else @@ -221,6 +187,14 @@ bool QWidgetTextControlPrivate::cursorMoveKeyEvent(QKeyEvent *e) else if (e == QKeySequence::SelectPreviousLine) { op = QTextCursor::Up; mode = QTextCursor::KeepAnchor; + { + QTextBlock block = cursor.block(); + QTextLine line = currentTextLine(cursor); + if (!block.previous().isValid() + && line.isValid() + && line.lineNumber() == 0) + op = QTextCursor::Start; + } } else if (e == QKeySequence::SelectNextLine) { op = QTextCursor::Down; @@ -289,7 +263,7 @@ bool QWidgetTextControlPrivate::cursorMoveKeyEvent(QKeyEvent *e) bool isNavigationEvent = e->key() == Qt::Key_Up || e->key() == Qt::Key_Down; #ifdef QT_KEYPAD_NAVIGATION - ignoreNavigationEvents = ignoreNavigationEvents || QApplication::keypadNavigationEnabled(); + ignoreNavigationEvents = ignoreNavigationEvents || QApplicationPrivate::keypadNavigationEnabled(); isNavigationEvent = isNavigationEvent || (QApplication::navigationMode() == Qt::NavigationModeKeypadDirectional && (e->key() == Qt::Key_Left || e->key() == Qt::Key_Right)); @@ -439,25 +413,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(); @@ -489,6 +468,11 @@ void QWidgetTextControlPrivate::setContent(Qt::TextFormat format, const QString formatCursor.select(QTextCursor::Document); formatCursor.setCharFormat(charFormatForInsertion); formatCursor.endEditBlock(); +#if QT_CONFIG(textmarkdownreader) + } else if (format == Qt::MarkdownText) { + doc->setMarkdown(text); + doc->setUndoRedoEnabled(false); +#endif } else { #ifndef QT_NO_TEXTHTMLPARSER doc->setHtml(text); @@ -514,7 +498,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() @@ -594,7 +579,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); @@ -617,7 +602,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); @@ -638,7 +623,7 @@ void QWidgetTextControlPrivate::_q_updateCurrentCharFormatAndSelection() #ifndef QT_NO_CLIPBOARD void QWidgetTextControlPrivate::setClipboardSelection() { - QClipboard *clipboard = QApplication::clipboard(); + QClipboard *clipboard = QGuiApplication::clipboard(); if (!cursor.hasSelection() || !clipboard->supportsSelection()) return; Q_Q(QWidgetTextControl); @@ -658,7 +643,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()) { @@ -670,9 +655,9 @@ 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 = 0; + QAccessibleEvent *ev = nullptr; if (charsRemoved == 0) { ev = new QAccessibleTextInsertEvent(q->parent(), from, newText); } else if (charsAdded == 0) { @@ -684,9 +669,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 } @@ -694,10 +679,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) @@ -709,9 +696,9 @@ void QWidgetTextControlPrivate::setCursorVisible(bool visible) updateCursorBlinking(); if (cursorVisible) - connect(qApp->styleHints(), &QStyleHints::cursorFlashTimeChanged, this, &QWidgetTextControlPrivate::updateCursorBlinking); + connect(QGuiApplication::styleHints(), &QStyleHints::cursorFlashTimeChanged, this, &QWidgetTextControlPrivate::updateCursorBlinking); else - disconnect(qApp->styleHints(), &QStyleHints::cursorFlashTimeChanged, this, &QWidgetTextControlPrivate::updateCursorBlinking); + disconnect(QGuiApplication::styleHints(), &QStyleHints::cursorFlashTimeChanged, this, &QWidgetTextControlPrivate::updateCursorBlinking); } void QWidgetTextControlPrivate::updateCursorBlinking() @@ -895,12 +882,12 @@ void QWidgetTextControl::setDocument(QTextDocument *document) d->doc->disconnect(this); d->doc->documentLayout()->disconnect(this); - d->doc->documentLayout()->setPaintDevice(0); + d->doc->documentLayout()->setPaintDevice(nullptr); if (d->doc->parent() == this) delete d->doc; - d->doc = 0; + d->doc = nullptr; d->setContent(Qt::RichText, QString(), document); } @@ -910,7 +897,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; @@ -924,6 +911,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 @@ -949,12 +943,12 @@ void QWidgetTextControl::copy() if (!d->cursor.hasSelection()) return; QMimeData *data = createMimeDataFromSelection(); - QApplication::clipboard()->setMimeData(data); + QGuiApplication::clipboard()->setMimeData(data); } void QWidgetTextControl::paste(QClipboard::Mode mode) { - const QMimeData *md = QApplication::clipboard()->mimeData(mode); + const QMimeData *md = QGuiApplication::clipboard()->mimeData(mode); if (md) insertFromMimeData(md); } @@ -973,20 +967,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) { @@ -1027,23 +1024,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)); @@ -1051,7 +1048,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: @@ -1063,13 +1060,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: { @@ -1083,13 +1080,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; } @@ -1098,32 +1095,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; } @@ -1137,19 +1134,19 @@ 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) #ifdef QT_KEYPAD_NAVIGATION case QEvent::EnterEditFocus: case QEvent::LeaveEditFocus: - if (QApplication::keypadNavigationEnabled()) + if (QApplicationPrivate::keypadNavigationEnabled()) d->editFocusEvent(e); break; #endif @@ -1192,6 +1189,14 @@ void QWidgetTextControl::setPlainText(const QString &text) d->setContent(Qt::PlainText, text); } +#if QT_CONFIG(textmarkdownreader) +void QWidgetTextControl::setMarkdown(const QString &text) +{ + Q_D(QWidgetTextControl); + d->setContent(Qt::MarkdownText, text); +} +#endif + void QWidgetTextControl::setHtml(const QString &text) { Q_D(QWidgetTextControl); @@ -1205,6 +1210,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 @@ -1253,7 +1261,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()) { @@ -1264,12 +1272,14 @@ void QWidgetTextControlPrivate::keyPressEvent(QKeyEvent *e) } else { QTextCursor localCursor = cursor; localCursor.deletePreviousChar(); + if (cursor.d) + cursor.d->setX(); } goto accept; } #ifndef QT_NO_SHORTCUT else if (e == QKeySequence::InsertParagraphSeparator) { - cursor.insertBlock(); + insertParagraphSeparator(); e->accept(); goto accept; } else if (e == QKeySequence::InsertLineSeparator) { @@ -1303,9 +1313,13 @@ void QWidgetTextControlPrivate::keyPressEvent(QKeyEvent *e) else if (e == QKeySequence::Delete) { QTextCursor localCursor = cursor; localCursor.deleteChar(); + if (cursor.d) + cursor.d->setX(); } else if (e == QKeySequence::Backspace) { QTextCursor localCursor = cursor; localCursor.deletePreviousChar(); + if (cursor.d) + cursor.d->setX(); }else if (e == QKeySequence::DeleteEndOfWord) { if (!cursor.hasSelection()) cursor.movePosition(QTextCursor::NextWord, QTextCursor::KeepAnchor); @@ -1350,6 +1364,10 @@ process: accept: +#ifndef QT_NO_CLIPBOARD + setClipboardSelection(); +#endif + e->accept(); cursorOn = true; @@ -1360,15 +1378,8 @@ process: QVariant QWidgetTextControl::loadResource(int type, const QUrl &name) { -#if !QT_CONFIG(textedit) Q_UNUSED(type); Q_UNUSED(name); -#else - if (QTextEdit *textEdit = qobject_cast<QTextEdit *>(parent())) { - QUrl resolvedName = textEdit->d_func()->resolveUrl(name); - return textEdit->loadResource(type, resolvedName); - } -#endif return QVariant(); } @@ -1395,16 +1406,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; } @@ -1418,7 +1427,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()); @@ -1573,6 +1582,11 @@ void QWidgetTextControlPrivate::mousePressEvent(QEvent *e, Qt::MouseButton butto e->ignore(); return; } + bool wasValid = blockWithMarkerUnderMouse.isValid(); + blockWithMarkerUnderMouse = q->blockWithMarkerAt(pos); + if (wasValid != blockWithMarkerUnderMouse.isValid()) + emit q->blockMarkerHovered(blockWithMarkerUnderMouse); + cursorIsFocusIndicator = false; const QTextCursor oldSelection = cursor; @@ -1591,6 +1605,8 @@ void QWidgetTextControlPrivate::mousePressEvent(QEvent *e, Qt::MouseButton butto selectedBlockOnTrippleClick = cursor; anchorOnMousePress = QString(); + blockWithMarkerUnderMouse = QTextBlock(); + emit q->blockMarkerHovered(blockWithMarkerUnderMouse); trippleClickTimer.stop(); } else { @@ -1730,6 +1746,11 @@ void QWidgetTextControlPrivate::mouseMoveEvent(QEvent *e, Qt::MouseButton button } selectionChanged(true); repaintOldAndNewSelection(oldSelection); + } else { + bool wasValid = blockWithMarkerUnderMouse.isValid(); + blockWithMarkerUnderMouse = q->blockWithMarkerAt(mousePos); + if (wasValid != blockWithMarkerUnderMouse.isValid()) + emit q->blockMarkerHovered(blockWithMarkerUnderMouse); } sendMouseEventToInputContext(e, QEvent::MouseMove, button, mousePos, modifiers, buttons, globalPos); @@ -1762,11 +1783,11 @@ 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) - && QApplication::clipboard()->supportsSelection()) { + && QGuiApplication::clipboard()->supportsSelection()) { setCursorPosition(pos); - const QMimeData *md = QApplication::clipboard()->mimeData(QClipboard::Selection); + const QMimeData *md = QGuiApplication::clipboard()->mimeData(QClipboard::Selection); if (md) q->insertFromMimeData(md); #endif @@ -1779,26 +1800,57 @@ void QWidgetTextControlPrivate::mouseReleaseEvent(QEvent *e, Qt::MouseButton but emit q->microFocusChanged(); } + // toggle any checkbox that the user clicks + if ((interactionFlags & Qt::TextEditable) && (button & Qt::LeftButton) && + (blockWithMarkerUnderMouse.isValid()) && !cursor.hasSelection()) { + QTextBlock markerBlock = q->blockWithMarkerAt(pos); + if (markerBlock == blockWithMarkerUnderMouse) { + auto fmt = blockWithMarkerUnderMouse.blockFormat(); + switch (fmt.marker()) { + case QTextBlockFormat::MarkerType::Unchecked : + fmt.setMarker(QTextBlockFormat::MarkerType::Checked); + break; + case QTextBlockFormat::MarkerType::Checked: + fmt.setMarker(QTextBlockFormat::MarkerType::Unchecked); + break; + default: + break; + } + cursor.setBlockFormat(fmt); + } + } + 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); } } } @@ -1862,7 +1914,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) { @@ -1891,6 +1943,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 } @@ -1969,7 +2027,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; } @@ -1977,6 +2035,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(); @@ -2013,10 +2076,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); @@ -2033,9 +2096,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; @@ -2051,9 +2114,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; @@ -2124,12 +2187,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); } @@ -2150,11 +2213,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: @@ -2175,7 +2238,7 @@ void QWidgetTextControlPrivate::focusEvent(QFocusEvent *e) emit q->updateRequest(q->selectionRect()); if (e->gotFocus()) { #ifdef QT_KEYPAD_NAVIGATION - if (!QApplication::keypadNavigationEnabled() || (hasEditFocus && (e->reason() == Qt::PopupFocusReason))) { + if (!QApplicationPrivate::keypadNavigationEnabled() || (hasEditFocus && (e->reason() == Qt::PopupFocusReason))) { #endif cursorOn = (interactionFlags & (Qt::TextSelectableByKeyboard | Qt::TextEditable)); if (interactionFlags & Qt::TextEditable) { @@ -2186,6 +2249,7 @@ void QWidgetTextControlPrivate::focusEvent(QFocusEvent *e) #endif } else { setCursorVisible(false); + cursorOn = false; if (cursorIsFocusIndicator && e->reason() != Qt::ActiveWindowFocusReason @@ -2216,7 +2280,7 @@ void QWidgetTextControlPrivate::editFocusEvent(QEvent *e) { Q_Q(QWidgetTextControl); - if (QApplication::keypadNavigationEnabled()) { + if (QApplicationPrivate::keypadNavigationEnabled()) { if (e->type() == QEvent::EnterEditFocus && interactionFlags & Qt::TextEditable) { const QTextCursor oldSelection = cursor; const int oldCursorPos = cursor.position(); @@ -2240,7 +2304,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()) @@ -2258,7 +2322,7 @@ QMenu *QWidgetTextControl::createStandardContextMenu(const QPointF &pos, QWidget d->linkToCopy = anchorAt(pos); if (d->linkToCopy.isEmpty() && !showTextSelectionActions) - return 0; + return nullptr; QMenu *menu = new QMenu(parent); QAction *a; @@ -2318,6 +2382,7 @@ QMenu *QWidgetTextControl::createStandardContextMenu(const QPointF &pos, QWidget a = menu->addAction(tr("Select All") + ACCEL_KEY(QKeySequence::SelectAll), this, SLOT(selectAll())); a->setEnabled(!d->doc->isEmpty()); a->setObjectName(QStringLiteral("select-all")); + setActionIcon(a, QStringLiteral("edit-select-all")); } if ((d->interactionFlags & Qt::TextEditable) && QGuiApplication::styleHints()->useRtlExtensions()) { @@ -2377,6 +2442,12 @@ QString QWidgetTextControl::anchorAtCursor() const return d->anchorForCursor(d->cursor); } +QTextBlock QWidgetTextControl::blockWithMarkerAt(const QPointF &pos) const +{ + Q_D(const QWidgetTextControl); + return d->doc->documentLayout()->blockWithMarkerAt(pos); +} + bool QWidgetTextControl::overwriteMode() const { Q_D(const QWidgetTextControl); @@ -2391,24 +2462,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(); } @@ -2430,13 +2493,13 @@ void QWidgetTextControl::setExtraSelections(const QList<QTextEdit::ExtraSelectio { Q_D(QWidgetTextControl); - QHash<int, int> hash; - for (int i = 0; i < d->extraSelections.count(); ++i) { + QMultiHash<int, int> hash; + for (int i = 0; i < d->extraSelections.size(); ++i) { const QAbstractTextDocumentLayout::Selection &esel = d->extraSelections.at(i); - hash.insertMulti(esel.cursor.anchor(), 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()) { @@ -2455,7 +2518,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)) { @@ -2465,8 +2528,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; } @@ -2476,7 +2539,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; @@ -2549,7 +2612,7 @@ bool QWidgetTextControl::canPaste() const #ifndef QT_NO_CLIPBOARD Q_D(const QWidgetTextControl); if (d->interactionFlags & Qt::TextEditable) { - const QMimeData *md = QApplication::clipboard()->mimeData(); + const QMimeData *md = QGuiApplication::clipboard()->mimeData(); return md && canInsertFromMimeData(md); } #endif @@ -2605,12 +2668,13 @@ void QWidgetTextControl::print(QPagedPaintDevice *printer) const Q_D(const QWidgetTextControl); if (!printer) return; - QTextDocument *tempDoc = 0; + QTextDocument *tempDoc = nullptr; const QTextDocument *doc = d->doc; if (QPagedPaintDevicePrivate::get(printer)->printSelectionOnly) { 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()); @@ -2639,8 +2703,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(); } @@ -2653,26 +2717,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); @@ -2890,7 +2961,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(); @@ -2898,7 +2969,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 { @@ -3088,8 +3159,8 @@ bool QWidgetTextControl::find(const QString &exp, QTextDocument::FindFlags optio return true; } -#ifndef QT_NO_REGEXP -bool QWidgetTextControl::find(const QRegExp &exp, QTextDocument::FindFlags options) +#if QT_CONFIG(regularexpression) +bool QWidgetTextControl::find(const QRegularExpression &exp, QTextDocument::FindFlags options) { Q_D(QWidgetTextControl); QTextCursor search = d->doc->find(exp, d->cursor, options); @@ -3113,6 +3184,56 @@ QString QWidgetTextControl::toHtml() const } #endif +#if QT_CONFIG(textmarkdownwriter) +QString QWidgetTextControl::toMarkdown(QTextDocument::MarkdownFeatures features) const +{ + return document()->toMarkdown(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); @@ -3211,7 +3332,7 @@ QAbstractTextDocumentLayout::PaintContext QWidgetTextControl::getPaintContext(QW if (!d->dndFeedbackCursor.isNull()) ctx.cursorPosition = d->dndFeedbackCursor.position(); #ifdef QT_KEYPAD_NAVIGATION - if (!QApplication::keypadNavigationEnabled() || d->hasEditFocus) + if (!QApplicationPrivate::keypadNavigationEnabled() || d->hasEditFocus) #endif if (d->cursor.hasSelection()) { QAbstractTextDocumentLayout::Selection selection; @@ -3262,7 +3383,7 @@ void QWidgetTextControlPrivate::_q_copyLink() #ifndef QT_NO_CLIPBOARD QMimeData *md = new QMimeData; md->setText(linkToCopy); - QApplication::clipboard()->setMimeData(md); + QGuiApplication::clipboard()->setMimeData(md); #endif } @@ -3339,16 +3460,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(); @@ -3359,7 +3483,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 { @@ -3367,7 +3494,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()); |