2019-07-29 15:30:58 +02:00
|
|
|
/****************************************************************************
|
|
|
|
|
**
|
|
|
|
|
** Copyright (C) 2019 The Qt Company Ltd.
|
|
|
|
|
** Contact: https://www.qt.io/licensing/
|
|
|
|
|
**
|
|
|
|
|
** This file is part of Qt Creator.
|
|
|
|
|
**
|
|
|
|
|
** 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 General Public License Usage
|
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
|
|
|
** General Public License version 3 as published by the Free Software
|
|
|
|
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
|
|
|
|
** 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-3.0.html.
|
|
|
|
|
**
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
|
#include "litehtmlhelpviewer.h"
|
|
|
|
|
|
2019-09-03 15:10:37 +02:00
|
|
|
#include "helpconstants.h"
|
2019-07-29 15:30:58 +02:00
|
|
|
#include "localhelpmanager.h"
|
|
|
|
|
|
|
|
|
|
#include <utils/algorithm.h>
|
|
|
|
|
#include <utils/qtcassert.h>
|
|
|
|
|
|
|
|
|
|
#include <QClipboard>
|
|
|
|
|
#include <QGuiApplication>
|
|
|
|
|
#include <QScrollBar>
|
|
|
|
|
#include <QTimer>
|
|
|
|
|
#include <QVBoxLayout>
|
2019-09-23 16:42:57 +02:00
|
|
|
#include <QWheelEvent>
|
2019-07-29 15:30:58 +02:00
|
|
|
|
|
|
|
|
#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);
|
2019-09-03 09:33:21 +02:00
|
|
|
return help.data;
|
2019-07-29 15:30:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LiteHtmlHelpViewer::LiteHtmlHelpViewer(QWidget *parent)
|
|
|
|
|
: HelpViewer(parent)
|
|
|
|
|
, m_viewer(new QLiteHtmlWidget)
|
|
|
|
|
{
|
|
|
|
|
m_viewer->setResourceHandler([](const QUrl &url) { return getData(url); });
|
2021-03-25 09:11:31 +01:00
|
|
|
m_viewer->setFrameStyle(QFrame::NoFrame);
|
2019-09-17 12:33:01 +02:00
|
|
|
m_viewer->viewport()->installEventFilter(this);
|
2019-07-29 15:30:58 +02:00
|
|
|
connect(m_viewer, &QLiteHtmlWidget::linkClicked, this, &LiteHtmlHelpViewer::setSource);
|
2019-09-03 15:10:37 +02:00
|
|
|
connect(m_viewer,
|
|
|
|
|
&QLiteHtmlWidget::contextMenuRequested,
|
|
|
|
|
this,
|
|
|
|
|
&LiteHtmlHelpViewer::showContextMenu);
|
2019-07-29 15:30:58 +02:00
|
|
|
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)
|
|
|
|
|
{
|
2019-09-17 12:33:01 +02:00
|
|
|
// interpret 0 as "default"
|
|
|
|
|
m_viewer->setZoomFactor(scale == 0 ? qreal(1) : scale);
|
2019-07-29 15:30:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
2019-09-16 14:24:03 +02:00
|
|
|
return m_viewer->findText(text,
|
|
|
|
|
Core::textDocumentFlagsForFindFlags(flags),
|
|
|
|
|
incremental,
|
|
|
|
|
wrapped);
|
2019-07-29 15:30:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
HistoryItem nextItem = currentHistoryItem();
|
|
|
|
|
for (int i = 0; i < count; ++i) {
|
|
|
|
|
QTC_ASSERT(!m_forwardItems.empty(), return );
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
HistoryItem previousItem = currentHistoryItem();
|
|
|
|
|
for (int i = 0; i < count; ++i) {
|
|
|
|
|
QTC_ASSERT(!m_backItems.empty(), return );
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
// TODO
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-17 12:33:01 +02:00
|
|
|
bool LiteHtmlHelpViewer::eventFilter(QObject *src, QEvent *e)
|
|
|
|
|
{
|
|
|
|
|
if (isScrollWheelZoomingEnabled() && e->type() == QEvent::Wheel) {
|
|
|
|
|
auto we = static_cast<QWheelEvent *>(e);
|
|
|
|
|
if (we->modifiers() == Qt::ControlModifier)
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return HelpViewer::eventFilter(src, e);
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-29 15:30:58 +02:00
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-03 15:10:37 +02:00
|
|
|
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(
|
|
|
|
|
QCoreApplication::translate("HelpViewer", 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(
|
|
|
|
|
QCoreApplication::translate("HelpViewer", Constants::TR_OPEN_LINK_IN_WINDOW));
|
|
|
|
|
connect(action, &QAction::triggered, this, [this, url]() {
|
|
|
|
|
emit externalPageRequested(url);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
copyAnchorAction = menu.addAction(tr("Copy Link"));
|
|
|
|
|
} else if (!m_viewer->selectedText().isEmpty()) {
|
|
|
|
|
connect(menu.addAction(tr("Copy")), &QAction::triggered, this, &HelpViewer::copy);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (copyAnchorAction == menu.exec(m_viewer->mapToGlobal(pos)))
|
|
|
|
|
QGuiApplication::clipboard()->setText(url.toString());
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-29 15:30:58 +02:00
|
|
|
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);
|
|
|
|
|
//}
|