forked from qt-creator/qt-creator
Replace the current license disclaimer in files by a SPDX-License-Identifier. Task-number: QTBUG-67283 Change-Id: I708fd1f9f2b73d60f57cc3568646929117825813 Reviewed-by: Eike Ziller <eike.ziller@qt.io>
341 lines
10 KiB
C++
341 lines
10 KiB
C++
// Copyright (C) 2019 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
|
|
|
#include "litehtmlhelpviewer.h"
|
|
|
|
#include "helpconstants.h"
|
|
#include "helptr.h"
|
|
#include "localhelpmanager.h"
|
|
|
|
#include <utils/algorithm.h>
|
|
#include <utils/qtcassert.h>
|
|
|
|
#include <QClipboard>
|
|
#include <QGuiApplication>
|
|
#include <QScrollBar>
|
|
#include <QTimer>
|
|
#include <QVBoxLayout>
|
|
#include <QWheelEvent>
|
|
|
|
#include <QDebug>
|
|
|
|
using namespace Help;
|
|
using namespace Help::Internal;
|
|
|
|
const int kMaxHistoryItems = 20;
|
|
|
|
static QByteArray getData(const QUrl &url)
|
|
{
|
|
// TODO: this is just a hack for Qt documentation
|
|
// which decides to use a simpler CSS if the viewer does not have JavaScript
|
|
// which was a hack to decide if we are viewing in QTextBrowser or QtWebEngine et al
|
|
QUrl actualUrl = url;
|
|
QString path = url.path(QUrl::FullyEncoded);
|
|
static const char simpleCss[] = "/offline-simple.css";
|
|
if (path.endsWith(simpleCss)) {
|
|
path.replace(simpleCss, "/offline.css");
|
|
actualUrl.setPath(path);
|
|
}
|
|
const LocalHelpManager::HelpData help = LocalHelpManager::helpData(actualUrl);
|
|
return help.data;
|
|
}
|
|
|
|
LiteHtmlHelpViewer::LiteHtmlHelpViewer(QWidget *parent)
|
|
: HelpViewer(parent)
|
|
, m_viewer(new QLiteHtmlWidget)
|
|
{
|
|
m_viewer->setResourceHandler([](const QUrl &url) { return getData(url); });
|
|
m_viewer->setFrameStyle(QFrame::NoFrame);
|
|
m_viewer->viewport()->installEventFilter(this);
|
|
connect(m_viewer, &QLiteHtmlWidget::linkClicked, this, [this](const QUrl &url) {
|
|
const Qt::KeyboardModifiers modifiers = QGuiApplication::keyboardModifiers();
|
|
if (modifiers == Qt::ControlModifier)
|
|
emit newPageRequested(url);
|
|
else
|
|
setSource(url);
|
|
});
|
|
connect(m_viewer,
|
|
&QLiteHtmlWidget::contextMenuRequested,
|
|
this,
|
|
&LiteHtmlHelpViewer::showContextMenu);
|
|
auto layout = new QVBoxLayout;
|
|
setLayout(layout);
|
|
layout->setContentsMargins(0, 0, 0, 0);
|
|
layout->addWidget(m_viewer, 10);
|
|
setFocusProxy(m_viewer);
|
|
QPalette p = palette();
|
|
p.setColor(QPalette::Inactive, QPalette::Highlight,
|
|
p.color(QPalette::Active, QPalette::Highlight));
|
|
p.setColor(QPalette::Inactive, QPalette::HighlightedText,
|
|
p.color(QPalette::Active, QPalette::HighlightedText));
|
|
p.setColor(QPalette::Base, Qt::white);
|
|
p.setColor(QPalette::Text, Qt::black);
|
|
setPalette(p);
|
|
}
|
|
|
|
LiteHtmlHelpViewer::~LiteHtmlHelpViewer() = default;
|
|
|
|
void LiteHtmlHelpViewer::setViewerFont(const QFont &newFont)
|
|
{
|
|
m_viewer->setDefaultFont(newFont);
|
|
}
|
|
|
|
void LiteHtmlHelpViewer::setScale(qreal scale)
|
|
{
|
|
// interpret 0 as "default"
|
|
m_viewer->setZoomFactor(scale == 0 ? qreal(1) : scale);
|
|
}
|
|
|
|
QString LiteHtmlHelpViewer::title() const
|
|
{
|
|
return m_viewer->title();
|
|
}
|
|
|
|
QUrl LiteHtmlHelpViewer::source() const
|
|
{
|
|
return m_viewer->url();
|
|
}
|
|
|
|
void LiteHtmlHelpViewer::setSource(const QUrl &url)
|
|
{
|
|
if (launchWithExternalApp(url))
|
|
return;
|
|
m_forwardItems.clear();
|
|
emit forwardAvailable(false);
|
|
if (m_viewer->url().isValid()) {
|
|
m_backItems.push_back(currentHistoryItem());
|
|
while (m_backItems.size() > kMaxHistoryItems) // this should trigger only once anyhow
|
|
m_backItems.erase(m_backItems.begin());
|
|
emit backwardAvailable(true);
|
|
}
|
|
setSourceInternal(url);
|
|
}
|
|
|
|
void LiteHtmlHelpViewer::setHtml(const QString &html)
|
|
{
|
|
m_viewer->setUrl({"about:invalid"});
|
|
m_viewer->setHtml(html);
|
|
}
|
|
|
|
QString LiteHtmlHelpViewer::selectedText() const
|
|
{
|
|
return m_viewer->selectedText();
|
|
}
|
|
|
|
bool LiteHtmlHelpViewer::isForwardAvailable() const
|
|
{
|
|
return !m_forwardItems.empty();
|
|
}
|
|
|
|
bool LiteHtmlHelpViewer::isBackwardAvailable() const
|
|
{
|
|
return !m_backItems.empty();
|
|
}
|
|
|
|
void LiteHtmlHelpViewer::addBackHistoryItems(QMenu *backMenu)
|
|
{
|
|
int backCount = 0;
|
|
Utils::reverseForeach(m_backItems, [this, backMenu, &backCount](const HistoryItem &item) {
|
|
++backCount;
|
|
auto action = new QAction(backMenu);
|
|
action->setText(item.title);
|
|
connect(action, &QAction::triggered, this, [this, backCount] { goBackward(backCount); });
|
|
backMenu->addAction(action);
|
|
});
|
|
}
|
|
|
|
void LiteHtmlHelpViewer::addForwardHistoryItems(QMenu *forwardMenu)
|
|
{
|
|
int forwardCount = 0;
|
|
for (const HistoryItem &item : m_forwardItems) {
|
|
++forwardCount;
|
|
auto action = new QAction(forwardMenu);
|
|
action->setText(item.title);
|
|
connect(action, &QAction::triggered, this, [this, forwardCount] {
|
|
goForward(forwardCount);
|
|
});
|
|
forwardMenu->addAction(action);
|
|
}
|
|
}
|
|
|
|
bool LiteHtmlHelpViewer::findText(
|
|
const QString &text, Core::FindFlags flags, bool incremental, bool fromSearch, bool *wrapped)
|
|
{
|
|
Q_UNUSED(fromSearch)
|
|
return m_viewer->findText(text,
|
|
Core::textDocumentFlagsForFindFlags(flags),
|
|
incremental,
|
|
wrapped);
|
|
}
|
|
|
|
void LiteHtmlHelpViewer::copy()
|
|
{
|
|
QGuiApplication::clipboard()->setText(selectedText());
|
|
}
|
|
|
|
void LiteHtmlHelpViewer::stop() {}
|
|
|
|
void LiteHtmlHelpViewer::forward()
|
|
{
|
|
goForward(1);
|
|
}
|
|
|
|
void LiteHtmlHelpViewer::backward()
|
|
{
|
|
goBackward(1);
|
|
}
|
|
|
|
void LiteHtmlHelpViewer::goForward(int count)
|
|
{
|
|
const int steps = qMin(count, int(m_forwardItems.size()));
|
|
if (steps == 0)
|
|
return;
|
|
HistoryItem nextItem = currentHistoryItem();
|
|
for (int i = 0; i < steps; ++i) {
|
|
m_backItems.push_back(nextItem);
|
|
nextItem = m_forwardItems.front();
|
|
m_forwardItems.erase(m_forwardItems.begin());
|
|
}
|
|
emit backwardAvailable(isBackwardAvailable());
|
|
emit forwardAvailable(isForwardAvailable());
|
|
setSourceInternal(nextItem.url, nextItem.vscroll);
|
|
}
|
|
|
|
void LiteHtmlHelpViewer::goBackward(int count)
|
|
{
|
|
const int steps = qMin(count, int(m_backItems.size()));
|
|
if (steps == 0)
|
|
return;
|
|
HistoryItem previousItem = currentHistoryItem();
|
|
for (int i = 0; i < steps; ++i) {
|
|
m_forwardItems.insert(m_forwardItems.begin(), previousItem);
|
|
previousItem = m_backItems.back();
|
|
m_backItems.pop_back();
|
|
}
|
|
emit backwardAvailable(isBackwardAvailable());
|
|
emit forwardAvailable(isForwardAvailable());
|
|
setSourceInternal(previousItem.url, previousItem.vscroll);
|
|
}
|
|
|
|
void LiteHtmlHelpViewer::print(QPrinter *printer)
|
|
{
|
|
Q_UNUSED(printer)
|
|
// TODO
|
|
}
|
|
|
|
bool LiteHtmlHelpViewer::eventFilter(QObject *src, QEvent *e)
|
|
{
|
|
if (isScrollWheelZoomingEnabled() && e->type() == QEvent::Wheel) {
|
|
auto we = static_cast<QWheelEvent *>(e);
|
|
if (we->modifiers() == Qt::ControlModifier) {
|
|
e->ignore();
|
|
return true;
|
|
}
|
|
} else if (e->type() == QEvent::MouseButtonPress) {
|
|
auto me = static_cast<QMouseEvent *>(e);
|
|
if (me->button() == Qt::BackButton) {
|
|
goBackward(1);
|
|
return true;
|
|
} else if (me->button() == Qt::ForwardButton) {
|
|
goForward(1);
|
|
return true;
|
|
}
|
|
}
|
|
return HelpViewer::eventFilter(src, e);
|
|
}
|
|
|
|
void LiteHtmlHelpViewer::setSourceInternal(const QUrl &url, Utils::optional<int> vscroll)
|
|
{
|
|
slotLoadStarted();
|
|
QUrl currentUrlWithoutFragment = m_viewer->url();
|
|
currentUrlWithoutFragment.setFragment({});
|
|
QUrl newUrlWithoutFragment = url;
|
|
newUrlWithoutFragment.setFragment({});
|
|
m_viewer->setUrl(url);
|
|
if (currentUrlWithoutFragment != newUrlWithoutFragment)
|
|
m_viewer->setHtml(QString::fromUtf8(getData(url)));
|
|
if (vscroll)
|
|
m_viewer->verticalScrollBar()->setValue(*vscroll);
|
|
else
|
|
m_viewer->scrollToAnchor(url.fragment(QUrl::FullyEncoded));
|
|
slotLoadFinished();
|
|
emit titleChanged();
|
|
}
|
|
|
|
void LiteHtmlHelpViewer::showContextMenu(const QPoint &pos, const QUrl &url)
|
|
{
|
|
QMenu menu(nullptr);
|
|
|
|
QAction *copyAnchorAction = nullptr;
|
|
if (!url.isEmpty() && url.isValid()) {
|
|
if (isActionVisible(HelpViewer::Action::NewPage)) {
|
|
QAction *action = menu.addAction(Tr::tr(Constants::TR_OPEN_LINK_AS_NEW_PAGE));
|
|
connect(action, &QAction::triggered, this, [this, url]() {
|
|
emit newPageRequested(url);
|
|
});
|
|
}
|
|
if (isActionVisible(HelpViewer::Action::ExternalWindow)) {
|
|
QAction *action = menu.addAction(Tr::tr(Constants::TR_OPEN_LINK_IN_WINDOW));
|
|
connect(action, &QAction::triggered, this, [this, url]() {
|
|
emit externalPageRequested(url);
|
|
});
|
|
}
|
|
copyAnchorAction = menu.addAction(Tr::tr("Copy Link"));
|
|
} else if (!m_viewer->selectedText().isEmpty()) {
|
|
connect(menu.addAction(Tr::tr("Copy")), &QAction::triggered, this, &HelpViewer::copy);
|
|
}
|
|
|
|
if (copyAnchorAction == menu.exec(m_viewer->mapToGlobal(pos)))
|
|
QGuiApplication::clipboard()->setText(url.toString());
|
|
}
|
|
|
|
LiteHtmlHelpViewer::HistoryItem LiteHtmlHelpViewer::currentHistoryItem() const
|
|
{
|
|
return {m_viewer->url(), m_viewer->title(), m_viewer->verticalScrollBar()->value()};
|
|
}
|
|
|
|
//bool TextBrowserHelpWidget::eventFilter(QObject *obj, QEvent *event)
|
|
//{
|
|
// if (obj == this) {
|
|
// if (event->type() == QEvent::FontChange) {
|
|
// if (!forceFont)
|
|
// return true;
|
|
// } else if (event->type() == QEvent::KeyPress) {
|
|
// auto keyEvent = static_cast<QKeyEvent *>(event);
|
|
// if (keyEvent->key() == Qt::Key_Slash) {
|
|
// keyEvent->accept();
|
|
// Core::Find::openFindToolBar(Core::Find::FindForwardDirection);
|
|
// return true;
|
|
// }
|
|
// } else if (event->type() == QEvent::ToolTip) {
|
|
// auto e = static_cast<const QHelpEvent *>(event);
|
|
// QToolTip::showText(e->globalPos(), linkAt(e->pos()));
|
|
// return true;
|
|
// }
|
|
// }
|
|
// return QTextBrowser::eventFilter(obj, event);
|
|
//}
|
|
|
|
//void TextBrowserHelpWidget::mousePressEvent(QMouseEvent *e)
|
|
//{
|
|
// if (Utils::HostOsInfo::isLinuxHost() && m_parent->handleForwardBackwardMouseButtons(e))
|
|
// return;
|
|
// QTextBrowser::mousePressEvent(e);
|
|
//}
|
|
|
|
//void TextBrowserHelpWidget::mouseReleaseEvent(QMouseEvent *e)
|
|
//{
|
|
// if (!Utils::HostOsInfo::isLinuxHost() && m_parent->handleForwardBackwardMouseButtons(e))
|
|
// return;
|
|
|
|
// bool controlPressed = e->modifiers() & Qt::ControlModifier;
|
|
// const QString link = linkAt(e->pos());
|
|
// if (m_parent->isActionVisible(HelpViewer::Action::NewPage)
|
|
// && (controlPressed || e->button() == Qt::MidButton) && !link.isEmpty()) {
|
|
// emit m_parent->newPageRequested(QUrl(link));
|
|
// return;
|
|
// }
|
|
|
|
// QTextBrowser::mouseReleaseEvent(e);
|
|
//}
|