diff options
Diffstat (limited to 'src/widgets/widgets/qtextbrowser.cpp')
-rw-r--r-- | src/widgets/widgets/qtextbrowser.cpp | 219 |
1 files changed, 88 insertions, 131 deletions
diff --git a/src/widgets/widgets/qtextbrowser.cpp b/src/widgets/widgets/qtextbrowser.cpp index d0ccd435b3..2c01ed2b26 100644 --- a/src/widgets/widgets/qtextbrowser.cpp +++ b/src/widgets/widgets/qtextbrowser.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 "qtextbrowser.h" #include "qtextedit_p.h" @@ -44,13 +8,9 @@ #include <qapplication.h> #include <private/qapplication_p.h> #include <qevent.h> -#include <qdesktopwidget.h> #include <qdebug.h> #include <qabstracttextdocumentlayout.h> #include "private/qtextdocumentlayout_p.h" -#if QT_CONFIG(textcodec) -#include <qtextcodec.h> -#endif #include <qpainter.h> #include <qdir.h> #if QT_CONFIG(whatsthis) @@ -58,9 +18,21 @@ #endif #include <qtextobject.h> #include <qdesktopservices.h> +#include <qstringconverter.h> QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + +static inline bool shouldEnableInputMethod(QTextBrowser *texbrowser) +{ +#if defined (Q_OS_ANDROID) + return !texbrowser->isReadOnly() || (texbrowser->textInteractionFlags() & Qt::TextSelectableByMouse); +#else + return !texbrowser->isReadOnly(); +#endif +} + Q_LOGGING_CATEGORY(lcBrowser, "qt.text.browser") class QTextBrowserPrivate : public QTextEditPrivate @@ -74,6 +46,11 @@ public: , lastKeypadScrollValue(-1) #endif {} + ~QTextBrowserPrivate() + { + for (const QMetaObject::Connection &connection : connections) + QObject::disconnect(connection); + } void init(); @@ -92,13 +69,13 @@ public: HistoryEntry history(int i) const { if (i <= 0) - if (-i < stack.count()) - return stack[stack.count()+i-1]; + if (-i < stack.size()) + return stack[stack.size()+i-1]; else return HistoryEntry(); else - if (i <= forwardStack.count()) - return forwardStack[forwardStack.count()-i]; + if (i <= forwardStack.size()) + return forwardStack[forwardStack.size()-i]; else return HistoryEntry(); } @@ -131,14 +108,14 @@ public: QString findFile(const QUrl &name) const; - inline void _q_documentModified() + inline void documentModified() { textOrSourceChanged = true; forceLoadOnSourceChange = !currentURL.path().isEmpty(); } - void _q_activateAnchor(const QString &href); - void _q_highlightLink(const QString &href); + void activateAnchor(const QString &href); + void highlightLink(const QString &href); void setSource(const QUrl &url, QTextDocument::ResourceType type); @@ -152,20 +129,26 @@ public: QTextCursor prevFocus; int lastKeypadScrollValue; #endif + void emitHighlighted(const QUrl &url) + { + Q_Q(QTextBrowser); + emit q->highlighted(url); + } + std::array<QMetaObject::Connection, 3> connections; }; -Q_DECLARE_TYPEINFO(QTextBrowserPrivate::HistoryEntry, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(QTextBrowserPrivate::HistoryEntry, Q_RELOCATABLE_TYPE); QString QTextBrowserPrivate::findFile(const QUrl &name) const { QString fileName; - if (name.scheme() == QLatin1String("qrc")) { - fileName = QLatin1String(":/") + name.path(); + if (name.scheme() == "qrc"_L1) { + fileName = ":/"_L1 + name.path(); } else if (name.scheme().isEmpty()) { fileName = name.path(); } else { #if defined(Q_OS_ANDROID) - if (name.scheme() == QLatin1String("assets")) - fileName = QLatin1String("assets:") + name.path(); + if (name.scheme() == "assets"_L1) + fileName = "assets:"_L1 + name.path(); else #endif fileName = name.toLocalFile(); @@ -177,9 +160,9 @@ QString QTextBrowserPrivate::findFile(const QUrl &name) const if (QFileInfo(fileName).isAbsolute()) return fileName; - for (QString path : qAsConst(searchPaths)) { - if (!path.endsWith(QLatin1Char('/'))) - path.append(QLatin1Char('/')); + for (QString path : std::as_const(searchPaths)) { + if (!path.endsWith(u'/')) + path.append(u'/'); path.append(fileName); if (QFileInfo(path).isReadable()) return path; @@ -196,7 +179,7 @@ QUrl QTextBrowserPrivate::resolveUrl(const QUrl &url) const // For the second case QUrl can merge "#someanchor" with "foo.html" // correctly to "foo.html#someanchor" if (!(currentURL.isRelative() - || (currentURL.scheme() == QLatin1String("file") + || (currentURL.scheme() == "file"_L1 && !QFileInfo(currentURL.toLocalFile()).isAbsolute())) || (url.hasFragment() && url.path().isEmpty())) { return currentURL.resolved(url); @@ -213,7 +196,7 @@ QUrl QTextBrowserPrivate::resolveUrl(const QUrl &url) const return url; } -void QTextBrowserPrivate::_q_activateAnchor(const QString &href) +void QTextBrowserPrivate::activateAnchor(const QString &href) { if (href.isEmpty()) return; @@ -234,11 +217,11 @@ void QTextBrowserPrivate::_q_activateAnchor(const QString &href) #ifndef QT_NO_DESKTOPSERVICES bool isFileScheme = - url.scheme() == QLatin1String("file") + url.scheme() == "file"_L1 #if defined(Q_OS_ANDROID) - || url.scheme() == QLatin1String("assets") + || url.scheme() == "assets"_L1 #endif - || url.scheme() == QLatin1String("qrc"); + || url.scheme() == "qrc"_L1; if ((openExternalLinks && !isFileScheme && !url.isRelative()) || (url.isRelative() && !currentURL.isRelative() && !isFileScheme)) { QDesktopServices::openUrl(url); @@ -254,26 +237,22 @@ void QTextBrowserPrivate::_q_activateAnchor(const QString &href) q->setSource(url); } -void QTextBrowserPrivate::_q_highlightLink(const QString &anchor) +void QTextBrowserPrivate::highlightLink(const QString &anchor) { - Q_Q(QTextBrowser); if (anchor.isEmpty()) { #ifndef QT_NO_CURSOR if (viewport->cursor().shape() != Qt::PointingHandCursor) oldCursor = viewport->cursor(); viewport->setCursor(oldCursor); #endif - emit q->highlighted(QUrl()); - emit q->highlighted(QString()); + emitHighlighted(QUrl()); } else { #ifndef QT_NO_CURSOR viewport->setCursor(Qt::PointingHandCursor); #endif const QUrl url = resolveUrl(anchor); - emit q->highlighted(url); - // convenience to ease connecting to QStatusBar::showMessage(const QString &) - emit q->highlighted(url.toString()); + emitHighlighted(url); } } @@ -297,9 +276,9 @@ void QTextBrowserPrivate::setSource(const QUrl &url, QTextDocument::ResourceType QString fileName = url.fileName(); if (type == QTextDocument::UnknownResource) { #if QT_CONFIG(textmarkdownreader) - if (fileName.endsWith(QLatin1String(".md")) || - fileName.endsWith(QLatin1String(".mkd")) || - fileName.endsWith(QLatin1String(".markdown"))) + if (fileName.endsWith(".md"_L1) || + fileName.endsWith(".mkd"_L1) || + fileName.endsWith(".markdown"_L1)) type = QTextDocument::MarkdownResource; else #endif @@ -310,27 +289,26 @@ void QTextBrowserPrivate::setSource(const QUrl &url, QTextDocument::ResourceType if (url.isValid() && (newUrlWithoutFragment != currentUrlWithoutFragment || forceLoadOnSourceChange)) { QVariant data = q->loadResource(type, resolveUrl(url)); - if (data.type() == QVariant::String) { + if (data.userType() == QMetaType::QString) { txt = data.toString(); - } else if (data.type() == QVariant::ByteArray) { + } else if (data.userType() == QMetaType::QByteArray) { + QByteArray ba = data.toByteArray(); if (type == QTextDocument::HtmlResource) { -#if QT_CONFIG(textcodec) - QByteArray ba = data.toByteArray(); - QTextCodec *codec = Qt::codecForHtml(ba); - txt = codec->toUnicode(ba); -#else - txt = data.toString(); -#endif + auto decoder = QStringDecoder::decoderForHtml(ba); + if (!decoder.isValid()) + // fall back to utf8 + decoder = QStringDecoder(QStringDecoder::Utf8); + txt = decoder(ba); } else { - txt = QString::fromUtf8(data.toByteArray()); + txt = QString::fromUtf8(ba); } } if (Q_UNLIKELY(txt.isEmpty())) qWarning("QTextBrowser: No document for %s", url.toString().toLatin1().constData()); if (q->isVisible()) { - const QStringRef firstTag = txt.leftRef(txt.indexOf(QLatin1Char('>')) + 1); - if (firstTag.startsWith(QLatin1String("<qt")) && firstTag.contains(QLatin1String("type")) && firstTag.contains(QLatin1String("detail"))) { + const QStringView firstTag = QStringView{txt}.left(txt.indexOf(u'>') + 1); + if (firstTag.startsWith("<qt"_L1) && firstTag.contains("type"_L1) && firstTag.contains("detail"_L1)) { #ifndef QT_NO_CURSOR QGuiApplication::restoreOverrideCursor(); #endif @@ -383,8 +361,7 @@ void QTextBrowserPrivate::setSource(const QUrl &url, QTextDocument::ResourceType } #ifdef QT_KEYPAD_NAVIGATION lastKeypadScrollValue = vbar->value(); - emit q->highlighted(QUrl()); - emit q->highlighted(QString()); + emitHighlighted(QUrl()); #endif #ifndef QT_NO_CURSOR @@ -506,7 +483,7 @@ void QTextBrowserPrivate::keypadMove(bool next) // up e.g. 110% // Obviously if a link is entirely visible, we still // focus it. - if(bothViewRects.contains(desiredRect) + if (bothViewRects.contains(desiredRect) || bothViewRects.adjusted(0, visibleLinkAmount, 0, -visibleLinkAmount).intersects(desiredRect)) { focusIt = true; @@ -535,7 +512,7 @@ void QTextBrowserPrivate::keypadMove(bool next) if (!focusIt && prevFocus.hasSelection()) { QRectF desiredRect = control->selectionRect(prevFocus); // XXX this may be better off also using the visibleLinkAmount value - if(newViewRect.intersects(desiredRect)) { + if (newViewRect.intersects(desiredRect)) { focusedPos = scrollYOffset; focusIt = true; anchorToFocus = prevFocus; @@ -559,8 +536,7 @@ void QTextBrowserPrivate::keypadMove(bool next) // Ensure that the new selection is highlighted. const QString href = control->anchorAtCursor(); QUrl url = resolveUrl(href); - emit q->highlighted(url); - emit q->highlighted(url.toString()); + emitHighlighted(url); } else { // Scroll vbar->setValue(scrollYOffset); @@ -575,8 +551,7 @@ void QTextBrowserPrivate::keypadMove(bool next) hbar->setValue(savedXOffset); vbar->setValue(scrollYOffset); - emit q->highlighted(QUrl()); - emit q->highlighted(QString()); + emitHighlighted(QUrl()); } } #endif @@ -619,8 +594,7 @@ void QTextBrowserPrivate::restoreHistoryEntry(const HistoryEntry &entry) Q_Q(QTextBrowser); const QString href = prevFocus.charFormat().anchorHref(); QUrl url = resolveUrl(href); - emit q->highlighted(url); - emit q->highlighted(url.toString()); + emitHighlighted(url); #endif } @@ -699,14 +673,17 @@ void QTextBrowserPrivate::init() #ifndef QT_NO_CURSOR viewport->setCursor(oldCursor); #endif - q->setAttribute(Qt::WA_InputMethodEnabled, !q->isReadOnly()); + q->setAttribute(Qt::WA_InputMethodEnabled, shouldEnableInputMethod(q)); q->setUndoRedoEnabled(false); viewport->setMouseTracking(true); - QObject::connect(q->document(), SIGNAL(contentsChanged()), q, SLOT(_q_documentModified())); - QObject::connect(control, SIGNAL(linkActivated(QString)), - q, SLOT(_q_activateAnchor(QString))); - QObject::connect(control, SIGNAL(linkHovered(QString)), - q, SLOT(_q_highlightLink(QString))); + connections = { + QObjectPrivate::connect(q->document(), &QTextDocument::contentsChanged, + this, &QTextBrowserPrivate::documentModified), + QObjectPrivate::connect(control, &QWidgetTextControl::linkActivated, + this, &QTextBrowserPrivate::activateAnchor), + QObjectPrivate::connect(control, &QWidgetTextControl::linkHovered, + this, &QTextBrowserPrivate::highlightLink), + }; } /*! @@ -813,13 +790,6 @@ void QTextBrowser::reload() setSource(s, d->currentType); } -#if QT_VERSION < QT_VERSION_CHECK(6,0,0) -void QTextBrowser::setSource(const QUrl &url) -{ - setSource(url, QTextDocument::UnknownResource); -} -#endif - /*! Attempts to load the document at the given \a url with the specified \a type. @@ -835,14 +805,12 @@ void QTextBrowser::setSource(const QUrl &url, QTextDocument::ResourceType type) doSetSource(url, type); } -#if QT_VERSION >= QT_VERSION_CHECK(6,0,0) /*! Attempts to load the document at the given \a url with the specified \a type. setSource() calls doSetSource. In Qt 5, setSource(const QUrl &url) was virtual. In Qt 6, doSetSource() is virtual instead, so that it can be overridden in subclasses. */ -#endif void QTextBrowser::doSetSource(const QUrl &url, QTextDocument::ResourceType type) { Q_D(QTextBrowser); @@ -869,11 +837,11 @@ void QTextBrowser::doSetSource(const QUrl &url, QTextDocument::ResourceType type entry.vpos = 0; d->stack.push(entry); - emit backwardAvailable(d->stack.count() > 1); + emit backwardAvailable(d->stack.size() > 1); if (!d->forwardStack.isEmpty() && d->forwardStack.top().url == url) { d->forwardStack.pop(); - emit forwardAvailable(d->forwardStack.count() > 0); + emit forwardAvailable(d->forwardStack.size() > 0); } else { d->forwardStack.clear(); emit forwardAvailable(false); @@ -914,7 +882,7 @@ void QTextBrowser::doSetSource(const QUrl &url, QTextDocument::ResourceType type being the new source. Source changes happen both programmatically when calling - setSource(), forward(), backword() or home() or when the user + setSource(), forward(), backward() or home() or when the user clicks on links or presses the equivalent key sequences. */ @@ -925,15 +893,6 @@ void QTextBrowser::doSetSource(const QUrl &url, QTextDocument::ResourceType type anchor is passed in \a link. */ -/*! \fn void QTextBrowser::highlighted(const QString &link) - \overload - - Convenience signal that allows connecting to a slot - that takes just a QString, like for example QStatusBar's - message(). -*/ - - /*! \fn void QTextBrowser::anchorClicked(const QUrl &link) @@ -956,14 +915,14 @@ void QTextBrowser::doSetSource(const QUrl &url, QTextDocument::ResourceType type void QTextBrowser::backward() { Q_D(QTextBrowser); - if (d->stack.count() <= 1) + if (d->stack.size() <= 1) return; // Update the history entry d->forwardStack.push(d->createHistoryEntry()); d->stack.pop(); // throw away the old version of the current entry d->restoreHistoryEntry(d->stack.top()); // previous entry - emit backwardAvailable(d->stack.count() > 1); + emit backwardAvailable(d->stack.size() > 1); emit forwardAvailable(true); emit historyChanged(); } @@ -1127,8 +1086,7 @@ bool QTextBrowser::focusNextPrevChild(bool next) if (d->prevFocus != d->control->textCursor() && d->control->textCursor().hasSelection()) { const QString href = d->control->anchorAtCursor(); QUrl url = d->resolveUrl(href); - emit highlighted(url); - emit highlighted(url.toString()); + emitHighlighted(url); } d->prevFocus = d->control->textCursor(); #endif @@ -1136,8 +1094,7 @@ bool QTextBrowser::focusNextPrevChild(bool next) } else { #ifdef QT_KEYPAD_NAVIGATION // We assume we have no highlight now. - emit highlighted(QUrl()); - emit highlighted(QString()); + emitHighlighted(QUrl()); #endif } return QTextEdit::focusNextPrevChild(next); @@ -1171,7 +1128,7 @@ void QTextBrowser::paintEvent(QPaintEvent *e) depending on the resource type: \table - \header \li ResourceType \li QVariant::Type + \header \li ResourceType \li QMetaType::Type \row \li QTextDocument::HtmlResource \li QString or QByteArray \row \li QTextDocument::ImageResource \li QImage, QPixmap or QByteArray \row \li QTextDocument::StyleSheetResource \li QString or QByteArray @@ -1208,7 +1165,7 @@ QVariant QTextBrowser::loadResource(int /*type*/, const QUrl &name) bool QTextBrowser::isBackwardAvailable() const { Q_D(const QTextBrowser); - return d->stack.count() > 1; + return d->stack.size() > 1; } /*! @@ -1295,7 +1252,7 @@ QString QTextBrowser::historyTitle(int i) const int QTextBrowser::forwardHistoryCount() const { Q_D(const QTextBrowser); - return d->forwardStack.count(); + return d->forwardStack.size(); } /*! @@ -1306,7 +1263,7 @@ int QTextBrowser::forwardHistoryCount() const int QTextBrowser::backwardHistoryCount() const { Q_D(const QTextBrowser); - return d->stack.count()-1; + return d->stack.size()-1; } /*! |