Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3bdb8ee0fd | |||
| 4626bc0dd4 | |||
| 0785dd4efa | |||
| 00f299345c | |||
| 83a8bb75fe | |||
| cb2073f866 | |||
| 55d9947be5 | |||
| 2c8fb85471 | |||
| 83c4746009 | |||
| 52cda7f2ed | |||
| 9d8d5d288e | |||
| 7060a34d06 | |||
| fe31f7fffb | |||
| 9bf296af12 | |||
| 769504b020 | |||
| 768a8fef38 | |||
| f127d0a7b3 | |||
| 2cfc816621 | |||
| faf4a68bd0 | |||
| 30c85bb136 | |||
| 01447079b4 | |||
| 249e9a91a3 |
@@ -0,0 +1,9 @@
|
||||
# Configuration for the Lock Threads app - https://probot.github.io/apps/lock/
|
||||
|
||||
daysUntilLock: 180
|
||||
lockComment: >
|
||||
This thread has been automatically locked since there has not been
|
||||
any recent activity after it was closed. Please open a new issue for
|
||||
a related request.
|
||||
setLockReason: false
|
||||
only: issues
|
||||
@@ -30,3 +30,9 @@ Makefile*
|
||||
|
||||
# CMake
|
||||
CMakeLists.txt.user
|
||||
|
||||
# VS Code
|
||||
/.vscode
|
||||
|
||||
# Linux appdata
|
||||
/assets/freedesktop/org.zealdocs.Zeal.appdata.xml
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
cmake_minimum_required(VERSION 3.5.1)
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
|
||||
|
||||
project(Zeal VERSION 0.6.0)
|
||||
project(Zeal VERSION 0.6.1)
|
||||
set(RELEASE_DATE 2018-09-28)
|
||||
|
||||
# Project information.
|
||||
if(APPLE)
|
||||
@@ -11,7 +12,7 @@ else()
|
||||
endif()
|
||||
|
||||
set(PROJECT_COMPANY_NAME "ZealDocs")
|
||||
string(TIMESTAMP PROJECT_COPYRIGHT "© 2015-%Y Oleg Shparber" UTC)
|
||||
set(PROJECT_COPYRIGHT "© 2015-2018 Oleg Shparber")
|
||||
set(PROJECT_DESCRIPTION "A simple documentation browser.")
|
||||
set(PROJECT_URL "https://zealdocs.org")
|
||||
|
||||
|
||||
@@ -1,17 +1 @@
|
||||
if(UNIX AND NOT APPLE)
|
||||
find_package(ECM REQUIRED NO_MODULE)
|
||||
set(CMAKE_MODULE_PATH ${ECM_KDE_MODULE_DIR})
|
||||
include(KDEInstallDirs)
|
||||
|
||||
foreach(_i 16 24 32 64 128)
|
||||
install(FILES "freedesktop/appicons/${_i}/zeal.png"
|
||||
DESTINATION "${KDE_INSTALL_ICONDIR}/hicolor/${_i}x${_i}/apps"
|
||||
# TODO: Use `RENAME zeal.png` and get rid of subdirectories.
|
||||
)
|
||||
endforeach()
|
||||
|
||||
# TODO: Generate via zeal.desktop.in.
|
||||
install(FILES "freedesktop/zeal.desktop"
|
||||
DESTINATION ${KDE_INSTALL_APPDIR}
|
||||
)
|
||||
endif()
|
||||
add_subdirectory(freedesktop)
|
||||
|
||||
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 872 B After Width: | Height: | Size: 872 B |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
@@ -0,0 +1,27 @@
|
||||
if(UNIX AND NOT APPLE)
|
||||
find_package(ECM 1.0.0 REQUIRED NO_MODULE)
|
||||
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH})
|
||||
include(ECMInstallIcons)
|
||||
include(KDEInstallDirs)
|
||||
|
||||
ecm_install_icons(ICONS "16-apps-zeal.png"
|
||||
"24-apps-zeal.png"
|
||||
"32-apps-zeal.png"
|
||||
"64-apps-zeal.png"
|
||||
"128-apps-zeal.png"
|
||||
DESTINATION ${KDE_INSTALL_ICONDIR}
|
||||
)
|
||||
|
||||
configure_file(
|
||||
org.zealdocs.Zeal.appdata.xml.in
|
||||
org.zealdocs.Zeal.appdata.xml
|
||||
)
|
||||
|
||||
install(FILES ${CMAKE_BINARY_DIR}/assets/freedesktop/org.zealdocs.Zeal.appdata.xml
|
||||
DESTINATION ${KDE_INSTALL_METAINFODIR}
|
||||
)
|
||||
|
||||
install(FILES "org.zealdocs.Zeal.desktop"
|
||||
DESTINATION ${KDE_INSTALL_APPDIR}
|
||||
)
|
||||
endif()
|
||||
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<component type="desktop">
|
||||
<id>org.zealdocs.Zeal.desktop</id>
|
||||
<name>Zeal</name>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<project_license>GPL-3.0-only</project_license>
|
||||
<summary>Documentation browser</summary>
|
||||
<description>
|
||||
<p>Zeal is a simple offline documentation browser inspired by Dash.</p>
|
||||
</description>
|
||||
<url type="homepage">https://zealdocs.org/</url>
|
||||
<url type="bugtracker">https://github.com/zealdocs/zeal/issues</url>
|
||||
<url type="help">https://zealdocs.org/usage.html</url>
|
||||
<screenshots>
|
||||
<screenshot type="default">
|
||||
<image>https://i.imgur.com/qBkZduS.png</image>
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
<provides>
|
||||
<id>zeal.desktop</id>
|
||||
</provides>
|
||||
<releases>
|
||||
<release date="${RELEASE_DATE}" version="${Zeal_VERSION}" />
|
||||
</releases>
|
||||
<update_contact>zeal@zealdocs.org</update_contact>
|
||||
<content_rating type="oars-1.1">
|
||||
<content_attribute id="violence-cartoon">none</content_attribute>
|
||||
<content_attribute id="violence-fantasy">none</content_attribute>
|
||||
<content_attribute id="violence-realistic">none</content_attribute>
|
||||
<content_attribute id="violence-bloodshed">none</content_attribute>
|
||||
<content_attribute id="violence-sexual">none</content_attribute>
|
||||
<content_attribute id="violence-desecration">none</content_attribute>
|
||||
<content_attribute id="violence-slavery">none</content_attribute>
|
||||
<content_attribute id="violence-worship">none</content_attribute>
|
||||
<content_attribute id="drugs-alcohol">none</content_attribute>
|
||||
<content_attribute id="drugs-narcotics">none</content_attribute>
|
||||
<content_attribute id="drugs-tobacco">none</content_attribute>
|
||||
<content_attribute id="sex-nudity">none</content_attribute>
|
||||
<content_attribute id="sex-themes">none</content_attribute>
|
||||
<content_attribute id="sex-homosexuality">none</content_attribute>
|
||||
<content_attribute id="sex-prostitution">none</content_attribute>
|
||||
<content_attribute id="sex-adultery">none</content_attribute>
|
||||
<content_attribute id="sex-appearance">none</content_attribute>
|
||||
<content_attribute id="language-profanity">none</content_attribute>
|
||||
<content_attribute id="language-humor">none</content_attribute>
|
||||
<content_attribute id="language-discrimination">none</content_attribute>
|
||||
<content_attribute id="social-chat">none</content_attribute>
|
||||
<content_attribute id="social-info">none</content_attribute>
|
||||
<content_attribute id="social-audio">none</content_attribute>
|
||||
<content_attribute id="social-location">none</content_attribute>
|
||||
<content_attribute id="social-contacts">none</content_attribute>
|
||||
<content_attribute id="money-purchasing">none</content_attribute>
|
||||
<content_attribute id="money-gambling">none</content_attribute>
|
||||
</content_rating>
|
||||
</component>
|
||||
@@ -27,6 +27,8 @@
|
||||
#include <archive.h>
|
||||
#include <archive_entry.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
using namespace Zeal::Core;
|
||||
|
||||
Extractor::Extractor(QObject *parent) :
|
||||
@@ -34,31 +36,28 @@ Extractor::Extractor(QObject *parent) :
|
||||
{
|
||||
}
|
||||
|
||||
void Extractor::extract(const QString &filePath, const QString &destination, const QString &root)
|
||||
void Extractor::extract(const QString &sourceFile, const QString &destination, const QString &root)
|
||||
{
|
||||
ExtractInfo info = {
|
||||
this, // extractor
|
||||
archive_read_new(), // archiveHandle
|
||||
filePath, // filePath
|
||||
QFileInfo(filePath).size(), // totalBytes
|
||||
sourceFile, // filePath
|
||||
QFileInfo(sourceFile).size(), // totalBytes
|
||||
0 // extractedBytes
|
||||
};
|
||||
|
||||
archive_read_support_filter_all(info.archiveHandle);
|
||||
archive_read_support_format_all(info.archiveHandle);
|
||||
|
||||
int r = archive_read_open_filename(info.archiveHandle, qPrintable(filePath), 10240);
|
||||
int r = archive_read_open_filename(info.archiveHandle, qPrintable(sourceFile), 10240);
|
||||
if (r) {
|
||||
emit error(filePath, QString::fromLocal8Bit(archive_error_string(info.archiveHandle)));
|
||||
emit error(sourceFile, QString::fromLocal8Bit(archive_error_string(info.archiveHandle)));
|
||||
return;
|
||||
}
|
||||
|
||||
archive_read_extract_set_progress_callback(info.archiveHandle, &Extractor::progressCallback,
|
||||
&info);
|
||||
|
||||
QDir destinationDir(destination);
|
||||
if (!root.isEmpty())
|
||||
if (!root.isEmpty()) {
|
||||
destinationDir = destinationDir.filePath(root);
|
||||
}
|
||||
|
||||
// TODO: Do not strip root directory in archive if it equals to 'root'
|
||||
archive_entry *entry;
|
||||
@@ -69,25 +68,62 @@ void Extractor::extract(const QString &filePath, const QString &destination, con
|
||||
// TODO: Remove once https://github.com/libarchive/libarchive/issues/587 is resolved.
|
||||
QString pathname = QString::fromWCharArray(archive_entry_pathname_w(entry));
|
||||
#endif
|
||||
if (!root.isEmpty())
|
||||
|
||||
if (!root.isEmpty()) {
|
||||
pathname.remove(0, pathname.indexOf(QLatin1String("/")) + 1);
|
||||
archive_entry_set_pathname(entry, qPrintable(destinationDir.filePath(pathname)));
|
||||
archive_read_extract(info.archiveHandle, entry, 0);
|
||||
}
|
||||
|
||||
const QString filePath = destinationDir.absoluteFilePath(pathname);
|
||||
|
||||
const auto filetype = archive_entry_filetype(entry);
|
||||
if (filetype == S_IFDIR) {
|
||||
QDir().mkpath(QFileInfo(filePath).absolutePath());
|
||||
continue;
|
||||
} else if (filetype != S_IFREG) {
|
||||
qWarning("Unsupported filetype %d for %s!", filetype, qPrintable(pathname));
|
||||
continue;
|
||||
}
|
||||
|
||||
QScopedPointer<QFile> file(new QFile(filePath));
|
||||
if (!file->open(QIODevice::WriteOnly)) {
|
||||
qWarning("Cannot open file for writing: %s", qPrintable(pathname));
|
||||
continue;
|
||||
}
|
||||
|
||||
const void *buffer;
|
||||
size_t size;
|
||||
std::int64_t offset;
|
||||
for (;;) {
|
||||
int rc = archive_read_data_block(info.archiveHandle, &buffer, &size, &offset);
|
||||
if (rc != ARCHIVE_OK) {
|
||||
if (rc == ARCHIVE_EOF) {
|
||||
break;
|
||||
}
|
||||
|
||||
qWarning("Cannot read from archive: %s", archive_error_string(info.archiveHandle));
|
||||
emit error(sourceFile,
|
||||
QString::fromLocal8Bit(archive_error_string(info.archiveHandle)));
|
||||
return;
|
||||
}
|
||||
|
||||
file->write(static_cast<const char *>(buffer), size);
|
||||
}
|
||||
|
||||
emitProgress(info);
|
||||
}
|
||||
|
||||
emit completed(filePath);
|
||||
emit completed(sourceFile);
|
||||
archive_read_free(info.archiveHandle);
|
||||
}
|
||||
|
||||
void Extractor::progressCallback(void *ptr)
|
||||
void Extractor::emitProgress(ExtractInfo &info)
|
||||
{
|
||||
ExtractInfo *info = static_cast<ExtractInfo *>(ptr);
|
||||
|
||||
const qint64 extractedBytes = archive_filter_bytes(info->archiveHandle, -1);
|
||||
if (extractedBytes == info->extractedBytes)
|
||||
const qint64 extractedBytes = archive_filter_bytes(info.archiveHandle, -1);
|
||||
if (extractedBytes == info.extractedBytes) {
|
||||
return;
|
||||
}
|
||||
|
||||
info->extractedBytes = extractedBytes;
|
||||
info.extractedBytes = extractedBytes;
|
||||
|
||||
emit info->extractor->progress(info->filePath, extractedBytes, info->totalBytes);
|
||||
emit progress(info.filePath, extractedBytes, info.totalBytes);
|
||||
}
|
||||
|
||||
@@ -37,7 +37,9 @@ public:
|
||||
explicit Extractor(QObject *parent = nullptr);
|
||||
|
||||
public slots:
|
||||
void extract(const QString &filePath, const QString &destination, const QString &root = QString());
|
||||
void extract(const QString &sourceFile,
|
||||
const QString &destination,
|
||||
const QString &root = QString());
|
||||
|
||||
signals:
|
||||
void error(const QString &filePath, const QString &message);
|
||||
@@ -46,14 +48,13 @@ signals:
|
||||
|
||||
private:
|
||||
struct ExtractInfo {
|
||||
Extractor *extractor;
|
||||
archive *archiveHandle;
|
||||
QString filePath;
|
||||
qint64 totalBytes;
|
||||
qint64 extractedBytes;
|
||||
};
|
||||
|
||||
static void progressCallback(void *ptr);
|
||||
void emitProgress(ExtractInfo &info);
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -408,6 +408,13 @@ void Docset::countSymbols()
|
||||
|
||||
while (m_db->next()) {
|
||||
const QString symbolTypeStr = m_db->value(0).toString();
|
||||
|
||||
// A workaround for https://github.com/zealdocs/zeal/issues/980.
|
||||
if (symbolTypeStr.isEmpty()) {
|
||||
qWarning("Empty symbol type in the '%s' docset, skipping...", qPrintable(m_name));
|
||||
continue;
|
||||
}
|
||||
|
||||
const QString symbolType = parseSymbolType(symbolTypeStr);
|
||||
m_symbolStrings.insertMulti(symbolType, symbolTypeStr);
|
||||
m_symbolCounts[symbolType] += m_db->value(1).toInt();
|
||||
|
||||
@@ -91,23 +91,14 @@ bool SearchQuery::hasKeywords() const
|
||||
return !m_keywords.isEmpty();
|
||||
}
|
||||
|
||||
bool SearchQuery::hasKeyword(const QString &keyword) const
|
||||
{
|
||||
// Temporary workaround for #333
|
||||
// TODO: Remove once #167 is implemented
|
||||
for (const QString &kw : m_keywords) {
|
||||
if (keyword.startsWith(kw, Qt::CaseInsensitive))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SearchQuery::hasKeywords(const QStringList &keywords) const
|
||||
{
|
||||
for (const QString &keyword : keywords) {
|
||||
if (m_keywords.contains(keyword))
|
||||
if (m_keywords.contains(keyword, Qt::CaseInsensitive)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -66,8 +66,7 @@ public:
|
||||
/// Returns true if there's a docset filter for the given query
|
||||
bool hasKeywords() const;
|
||||
|
||||
/// Returns true if the docset prefix match the ones given on query
|
||||
bool hasKeyword(const QString &keyword) const;
|
||||
/// Returns true if one the query contains one of the @c keywords.
|
||||
bool hasKeywords(const QStringList &keywords) const;
|
||||
|
||||
/// Returns the docset filter raw size for the given query
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
<property name="text">
|
||||
<string><strong>A simple offline documentation browser</strong>
|
||||
<br><br>
|
||||
Copyright &copy; Oleg Shparber and other conributors, 2013-2018.
|
||||
Copyright &copy; Oleg Shparber and other contributors, 2013-2018.
|
||||
<br>
|
||||
<a href="https://zealdocs.org">zealdocs.org</a>
|
||||
<br>
|
||||
|
||||
@@ -157,12 +157,19 @@ MainWindow::MainWindow(Core::Application *app, QWidget *parent) :
|
||||
|
||||
setupTabBar();
|
||||
|
||||
QShortcut *focusSearch = new QShortcut(QStringLiteral("Ctrl+K"), this);
|
||||
connect(focusSearch, &QShortcut::activated,
|
||||
// Setup application wide shortcuts.
|
||||
// Focus search bar.
|
||||
QShortcut *shortcut = new QShortcut(QStringLiteral("Ctrl+K"), this);
|
||||
connect(shortcut, &QShortcut::activated,
|
||||
ui->lineEdit, static_cast<void (SearchEdit::*)()>(&SearchEdit::setFocus));
|
||||
|
||||
QShortcut *duplicate = new QShortcut(QStringLiteral("Ctrl+Alt+T"), this);
|
||||
connect(duplicate, &QShortcut::activated, this, [this]() { duplicateTab(m_tabBar->currentIndex()); });
|
||||
shortcut = new QShortcut(QStringLiteral("Ctrl+L"), this);
|
||||
connect(shortcut, &QShortcut::activated,
|
||||
ui->lineEdit, static_cast<void (SearchEdit::*)()>(&SearchEdit::setFocus));
|
||||
|
||||
// Duplicate current tab.
|
||||
shortcut = new QShortcut(QStringLiteral("Ctrl+Alt+T"), this);
|
||||
connect(shortcut, &QShortcut::activated, this, [this]() { duplicateTab(m_tabBar->currentIndex()); });
|
||||
|
||||
restoreGeometry(m_settings->windowGeometry);
|
||||
ui->splitter->restoreState(m_settings->verticalSplitterGeometry);
|
||||
@@ -191,7 +198,7 @@ MainWindow::MainWindow(Core::Application *app, QWidget *parent) :
|
||||
if (QKeySequence(QKeySequence::Preferences).isEmpty()) {
|
||||
ui->actionPreferences->setShortcut(QStringLiteral("Ctrl+,"));
|
||||
} else {
|
||||
ui->actionPreferences->setShortcut(QKeySequence::Quit);
|
||||
ui->actionPreferences->setShortcut(QKeySequence::Preferences);
|
||||
}
|
||||
|
||||
connect(ui->actionPreferences, &QAction::triggered, [this]() {
|
||||
@@ -879,7 +886,7 @@ void MainWindow::applySettings()
|
||||
removeTrayIcon();
|
||||
|
||||
// Content
|
||||
QByteArray ba;
|
||||
QByteArray ba = QByteArrayLiteral("body { background-color: white; }");
|
||||
if (m_settings->darkModeEnabled) {
|
||||
QScopedPointer<QFile> file(new QFile(DarkModeCssUrl));
|
||||
if (file->open(QIODevice::ReadOnly)) {
|
||||
|
||||
@@ -143,12 +143,21 @@ void SettingsDialog::chooseCustomCssFile()
|
||||
|
||||
void SettingsDialog::chooseDocsetStoragePath()
|
||||
{
|
||||
const QString dir = QFileDialog::getExistingDirectory(this, tr("Open Directory"),
|
||||
ui->docsetStorageEdit->text());
|
||||
if (dir.isEmpty())
|
||||
QString path = QFileDialog::getExistingDirectory(this, tr("Open Directory"),
|
||||
ui->docsetStorageEdit->text());
|
||||
if (path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ui->docsetStorageEdit->setText(QDir::toNativeSeparators(dir));
|
||||
#ifdef PORTABLE_BUILD
|
||||
// Use relative path if selected directory is under the application binary path.
|
||||
if (path.startsWith(QCoreApplication::applicationDirPath() + QLatin1String("/"))) {
|
||||
const QDir appDirPath(QCoreApplication::applicationDirPath());
|
||||
path = appDirPath.relativeFilePath(path);
|
||||
}
|
||||
#endif
|
||||
|
||||
ui->docsetStorageEdit->setText(QDir::toNativeSeparators(path));
|
||||
}
|
||||
|
||||
void SettingsDialog::loadSettings()
|
||||
@@ -256,7 +265,7 @@ void SettingsDialog::saveSettings()
|
||||
settings->fixedFontFamily = ui->fixedFontComboBox->currentText();
|
||||
|
||||
settings->defaultFontSize = ui->fontSizeComboBox->currentData().toInt();
|
||||
settings->defaultFixedFontSize = ui->fixedFontComboBox->currentData().toInt();
|
||||
settings->defaultFixedFontSize = ui->fixedFontSizeComboBox->currentData().toInt();
|
||||
settings->minimumFontSize = ui->minFontSizeComboBox->currentData().toInt();
|
||||
|
||||
settings->darkModeEnabled = ui->darkModeCheckBox->isChecked();
|
||||
|
||||
@@ -72,7 +72,7 @@ const QVector<int> &WebView::availableZoomLevels()
|
||||
return zoomLevels;
|
||||
}
|
||||
|
||||
const int WebView::defaultZoomLevel()
|
||||
const int &WebView::defaultZoomLevel()
|
||||
{
|
||||
static const int level = availableZoomLevels().indexOf(100);
|
||||
return level;
|
||||
@@ -122,14 +122,20 @@ void WebView::contextMenuEvent(QContextMenuEvent *event)
|
||||
const QString linkText = hitTestResult.linkText();
|
||||
|
||||
if (linkUrl.isValid()) {
|
||||
m_contextMenu->addAction(tr("Open Link in New Tab"), this, [this]() {
|
||||
triggerPageAction(QWebPage::WebAction::OpenLinkInNewWindow);
|
||||
});
|
||||
const QString scheme = linkUrl.scheme();
|
||||
|
||||
if (linkUrl.scheme() != QLatin1String("qrc")) {
|
||||
m_contextMenu->addAction(tr("Open Link in Desktop Browser"), this, [linkUrl]() {
|
||||
QDesktopServices::openUrl(linkUrl);
|
||||
if (scheme != QLatin1String("javascript")) {
|
||||
m_contextMenu->addAction(tr("Open Link in New Tab"), this, [this]() {
|
||||
triggerPageAction(QWebPage::WebAction::OpenLinkInNewWindow);
|
||||
});
|
||||
}
|
||||
|
||||
if (scheme != QLatin1String("qrc")) {
|
||||
if (scheme != QLatin1String("javascript")) {
|
||||
m_contextMenu->addAction(tr("Open Link in Desktop Browser"), this, [linkUrl]() {
|
||||
QDesktopServices::openUrl(linkUrl);
|
||||
});
|
||||
}
|
||||
|
||||
m_contextMenu->addAction(pageAction(QWebPage::CopyLinkToClipboard));
|
||||
}
|
||||
@@ -148,6 +154,10 @@ void WebView::contextMenuEvent(QContextMenuEvent *event)
|
||||
m_contextMenu->addSeparator();
|
||||
}
|
||||
|
||||
m_contextMenu->addAction(pageAction(QWebPage::Back));
|
||||
m_contextMenu->addAction(pageAction(QWebPage::Forward));
|
||||
m_contextMenu->addSeparator();
|
||||
|
||||
m_contextMenu->addAction(tr("Open Page in Desktop Browser"), this, [this]() {
|
||||
QDesktopServices::openUrl(url());
|
||||
});
|
||||
@@ -177,7 +187,7 @@ void WebView::mousePressEvent(QMouseEvent *event)
|
||||
case Qt::LeftButton:
|
||||
case Qt::MiddleButton:
|
||||
m_clickedLink = hitTestContent(event->pos()).linkUrl();
|
||||
if (!m_clickedLink.isValid()) {
|
||||
if (!m_clickedLink.isValid() || m_clickedLink.scheme() == QLatin1String("javascript")) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -198,7 +208,8 @@ void WebView::mouseReleaseEvent(QMouseEvent *event)
|
||||
}
|
||||
|
||||
const QUrl clickedLink = hitTestContent(event->pos()).linkUrl();
|
||||
if (!clickedLink.isValid() || clickedLink != m_clickedLink) {
|
||||
if (!clickedLink.isValid() || clickedLink != m_clickedLink
|
||||
|| clickedLink.scheme() == QLatin1String("javascript")) {
|
||||
QWebView::mouseReleaseEvent(event);
|
||||
return;
|
||||
}
|
||||
@@ -299,6 +310,11 @@ QWebHitTestResult WebView::hitTestContent(const QPoint &pos) const
|
||||
|
||||
bool WebView::isUrlExternal(const QUrl &url)
|
||||
{
|
||||
static const QStringList localSchemes = {
|
||||
QStringLiteral("file"),
|
||||
QStringLiteral("qrc"),
|
||||
};
|
||||
|
||||
const QString scheme = url.scheme();
|
||||
return !scheme.isEmpty() && scheme != QLatin1String("file") && scheme != QLatin1String("qrc");
|
||||
return !scheme.isEmpty() && !localSchemes.contains(scheme);
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ public:
|
||||
void setZoomLevel(int level);
|
||||
|
||||
static const QVector<int> &availableZoomLevels();
|
||||
static const int defaultZoomLevel();
|
||||
static const int &defaultZoomLevel();
|
||||
|
||||
public slots:
|
||||
void zoomIn();
|
||||
|
||||