Compare commits
74 Commits
release/0.5
...
v0.6.1
| 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 | |||
| bae9a49b0d | |||
| f85b902fc7 | |||
| ab1c1a2bae | |||
| 6e40307608 | |||
| af56557f34 | |||
| daf55ff31c | |||
| f872a2dd59 | |||
| 10620933b4 | |||
| 14330638bb | |||
| 2a2627aa79 | |||
| 04159cfdd5 | |||
| 9fdaa35420 | |||
| 18de7a1b5d | |||
| 26da8bef6e | |||
| 192a451a25 | |||
| b7ca52ab54 | |||
| 20faeb5fed | |||
| b6abb5104b | |||
| b18f88d852 | |||
| 7fbabfc425 | |||
| 44e0289c35 | |||
| 132619f1f6 | |||
| 053c46ccbd | |||
| d259a7a0d7 | |||
| e406a35255 | |||
| fb78df0504 | |||
| 72b254bb18 | |||
| af429d74a0 | |||
| 6dd244949c | |||
| d0d3f56d1d | |||
| 486ce17296 | |||
| 7c3789dead | |||
| 9e98a6860e | |||
| a9307b4a1e | |||
| 478791a2d8 | |||
| ba3c63c9ae | |||
| 3b2a395728 | |||
| 8fa793a6f6 | |||
| 9e86fcdfd5 | |||
| 4b65cb29da | |||
| b468d31aab | |||
| 5cec65591b | |||
| 57fbea15c2 | |||
| fafdc268d8 | |||
| d0cdc200bb | |||
| a406f8de5f | |||
| bf574687e5 | |||
| ee4b00efba | |||
| e58fc7f089 | |||
| 2aed15f824 | |||
| 38d14735b6 | |||
| 7d39f3fbb4 |
@@ -24,3 +24,4 @@ COPYING
|
||||
*.icns binary
|
||||
*.ico binary
|
||||
*.png binary
|
||||
*.woff binary
|
||||
|
||||
@@ -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,5 +0,0 @@
|
||||
SRC_ROOT=$$PWD
|
||||
BUILD_ROOT=$$shadowed($$PWD)
|
||||
|
||||
ZEAL_COMMON_PRI = $$SRC_ROOT/qmake/common.pri
|
||||
ZEAL_LIBRARY_PRI = $$SRC_ROOT/qmake/library.pri
|
||||
@@ -0,0 +1,3 @@
|
||||
# Changelog
|
||||
|
||||
The version history is available on [GitHub Releases](https://github.com/zealdocs/zeal/releases).
|
||||
@@ -1,7 +1,8 @@
|
||||
cmake_minimum_required(VERSION 3.5.1)
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
|
||||
|
||||
project(Zeal VERSION 0.5.0)
|
||||
project(Zeal VERSION 0.6.1)
|
||||
set(RELEASE_DATE 2018-09-28)
|
||||
|
||||
# Project information.
|
||||
if(APPLE)
|
||||
@@ -11,9 +12,11 @@ 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")
|
||||
|
||||
set(QT_MINIMUM_VERSION 5.5.1)
|
||||
|
||||
add_subdirectory(assets)
|
||||
add_subdirectory(src)
|
||||
|
||||
@@ -8,18 +8,16 @@
|
||||
|
||||
Zeal is a simple offline documentation browser inspired by [Dash](https://kapeli.com/dash).
|
||||
|
||||

|
||||
|
||||
[More screenshots](https://imgur.com/a/eVi97)
|
||||

|
||||
|
||||
## Build Status
|
||||
|
||||
[](https://scan.coverity.com/projects/4271)
|
||||
|
||||
OS \ Branch | master | stable
|
||||
------------|--------|-------
|
||||
Linux | [](https://app.shippable.com/projects/54ac2ce4d46935d5fbc19b84) | [](https://app.shippable.com/projects/54ac2ce4d46935d5fbc19b84)
|
||||
Windows | [](https://ci.appveyor.com/project/trollixx/zeal) | [](https://ci.appveyor.com/project/trollixx/zeal)
|
||||
| OS | Build Status |
|
||||
| -------- | ------------ |
|
||||
| Linux | [](https://app.shippable.com/projects/54ac2ce4d46935d5fbc19b84) |
|
||||
| Windows | [](https://ci.appveyor.com/project/trollixx/zeal) |
|
||||
|
||||
## Download
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
include($$ZEAL_COMMON_PRI)
|
||||
|
||||
TEMPLATE = aux
|
||||
|
||||
unix:!macx {
|
||||
TARGET = zeal
|
||||
appicons16.files=freedesktop/appicons/16/*
|
||||
appicons24.files=freedesktop/appicons/24/*
|
||||
appicons32.files=freedesktop/appicons/32/*
|
||||
appicons64.files=freedesktop/appicons/64/*
|
||||
appicons128.files=freedesktop/appicons/128/*
|
||||
|
||||
appicons16.path=$$PREFIX/share/icons/hicolor/16x16/apps
|
||||
appicons24.path=$$PREFIX/share/icons/hicolor/24x24/apps
|
||||
appicons32.path=$$PREFIX/share/icons/hicolor/32x32/apps
|
||||
appicons64.path=$$PREFIX/share/icons/hicolor/64x64/apps
|
||||
appicons128.path=$$PREFIX/share/icons/hicolor/128x128/apps
|
||||
|
||||
desktop.files=freedesktop/zeal.desktop
|
||||
desktop.path=$$PREFIX/share/applications
|
||||
|
||||
INSTALLS += appicons16 appicons24 appicons32 appicons64 appicons128 desktop
|
||||
}
|
||||
|
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>
|
||||
@@ -1,45 +0,0 @@
|
||||
# Shared build options
|
||||
#
|
||||
# This file must be included at the top of every non-subdirs .pro file.
|
||||
# Use:
|
||||
# include($$ZEAL_COMMON_PRI)
|
||||
|
||||
# Compilation settings
|
||||
CONFIG += c++11 silent
|
||||
|
||||
# Shared include path
|
||||
INCLUDEPATH += $$SRC_ROOT/src/libs
|
||||
LIBS = -L$$BUILD_ROOT/.lib
|
||||
|
||||
# QString options
|
||||
DEFINES *= QT_USE_QSTRINGBUILDER
|
||||
DEFINES *= QT_RESTRICTED_CAST_FROM_ASCII
|
||||
DEFINES *= QT_NO_CAST_TO_ASCII
|
||||
DEFINES *= QT_NO_URL_CAST_FROM_STRING
|
||||
|
||||
# Workaround for AppVeyor: Do not warn if the library had to be created.
|
||||
# Based on https://codereview.qt-project.org/150326
|
||||
win32-g++:lessThan(QT_VERSION, "5.6.0") {
|
||||
QMAKE_LIB = ar -rc
|
||||
}
|
||||
|
||||
# Keep build directory clean
|
||||
MOC_DIR = $$BUILD_ROOT/.moc
|
||||
OBJECTS_DIR = $$BUILD_ROOT/.obj
|
||||
RCC_DIR = $$BUILD_ROOT/.rcc
|
||||
UI_DIR = $$BUILD_ROOT/.ui
|
||||
|
||||
# Application version
|
||||
VERSION = 0.5.0
|
||||
DEFINES += ZEAL_VERSION=\\\"$${VERSION}\\\"
|
||||
|
||||
# Portable build
|
||||
CONFIG(zeal_portable) {
|
||||
message("Portable build: Yes.")
|
||||
DEFINES += PORTABLE_BUILD
|
||||
}
|
||||
|
||||
# Unix installation prefix
|
||||
unix:!macx {
|
||||
isEmpty(PREFIX): PREFIX = /usr
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
include($$ZEAL_COMMON_PRI)
|
||||
include($$replace(_PRO_FILE_PWD_, ([^/]+$), \\1/\\1.pri))
|
||||
|
||||
TEMPLATE = lib
|
||||
CONFIG += staticlib
|
||||
|
||||
DESTDIR = $$BUILD_ROOT/.lib
|
||||
TARGET = $$ZEAL_LIB_NAME
|
||||
@@ -1,6 +1,8 @@
|
||||
# TODO: Move zeal.qrc one level up to rely on CMAKE_AUTORCC.
|
||||
find_package(Qt5Core REQUIRED)
|
||||
qt5_add_resources(App_RESOURCES resources/zeal.qrc)
|
||||
# Silence CMP0071.
|
||||
set_property(SOURCE qrc_zeal.cpp PROPERTY SKIP_AUTOGEN ON)
|
||||
|
||||
if(APPLE)
|
||||
list(APPEND App_RESOURCES resources/zeal.icns)
|
||||
@@ -17,6 +19,10 @@ add_executable(App WIN32
|
||||
target_link_libraries(App Core Util)
|
||||
|
||||
find_package(Qt5 COMPONENTS Widgets REQUIRED)
|
||||
if (Qt5Widgets_VERSION VERSION_LESS QT_MINIMUM_VERSION)
|
||||
message(FATAL_ERROR "Qt version >= ${QT_MINIMUM_VERSION} is required.")
|
||||
endif()
|
||||
|
||||
target_link_libraries(App Qt5::Widgets)
|
||||
|
||||
set_target_properties(App PROPERTIES
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
include($$ZEAL_COMMON_PRI)
|
||||
|
||||
TEMPLATE = app
|
||||
|
||||
QT += gui widgets
|
||||
|
||||
SOURCES += \
|
||||
main.cpp
|
||||
|
||||
RESOURCES += \
|
||||
resources/zeal.qrc
|
||||
|
||||
DESTDIR = $$BUILD_ROOT/bin
|
||||
|
||||
unix:!macx {
|
||||
TARGET = zeal
|
||||
target.path = $$PREFIX/bin
|
||||
|
||||
INSTALLS += target
|
||||
}
|
||||
|
||||
win32 {
|
||||
TARGET = zeal
|
||||
RC_ICONS = resources/zeal.ico
|
||||
}
|
||||
|
||||
macx {
|
||||
TARGET = Zeal
|
||||
ICON = resources/zeal.icns
|
||||
}
|
||||
|
||||
# FIXME: Hardcoded link line & cyclic dependencies.
|
||||
LIBS += -lCore -lUi -lRegistry -lUtil
|
||||
|
||||
# Depend on all dependencies of libraries
|
||||
for(lib_dir, $$list($$files($$SRC_ROOT/src/libs/*))) {
|
||||
!equals(lib_dir, $$SRC_ROOT/src/libs/libs.pro) {
|
||||
exists($$lib_dir/$$basename(lib_dir).pri) {
|
||||
include($$lib_dir/$$basename(lib_dir).pri)
|
||||
msvc:PRE_TARGETDEPS += $$BUILD_ROOT/.lib/$${ZEAL_LIB_NAME}.$${QMAKE_EXTENSION_STATICLIB}
|
||||
else:PRE_TARGETDEPS += $$BUILD_ROOT/.lib/lib$${ZEAL_LIB_NAME}.$${QMAKE_EXTENSION_STATICLIB}
|
||||
# LIBS += -l$$ZEAL_LIB_NAME
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,10 +46,6 @@
|
||||
|
||||
using namespace Zeal;
|
||||
|
||||
namespace {
|
||||
const char contactUrl[] = "https://go.zealdocs.org/l/contact";
|
||||
}
|
||||
|
||||
struct CommandLineParameters
|
||||
{
|
||||
bool force;
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
/*!
|
||||
* Font Awesome Free 5.0.4 by @fontawesome - http://fontawesome.com
|
||||
* License - http://fontawesome.com/license (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
*/
|
||||
@font-face{font-family:Font Awesome\ 5 Brands;font-style:normal;font-weight:400;src:url(../fonts/fa-brands-400.eot);src:url(../fonts/fa-brands-400.eot?#iefix) format("embedded-opentype"),url(../fonts/fa-brands-400.woff2) format("woff2"),url(../fonts/fa-brands-400.woff) format("woff"),url(../fonts/fa-brands-400.ttf) format("truetype"),url(../fonts/fa-brands-400.svg#fontawesome) format("svg")}.fab{font-family:Font Awesome\ 5 Brands}
|
||||
@@ -0,0 +1,5 @@
|
||||
/*!
|
||||
* Font Awesome Free 5.0.4 by @fontawesome - http://fontawesome.com
|
||||
* License - http://fontawesome.com/license (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
*/
|
||||
@font-face{font-family:Font Awesome\ 5 Free;font-style:normal;font-weight:900;src:url(../fonts/fa-solid-900.eot);src:url(../fonts/fa-solid-900.eot?#iefix) format("embedded-opentype"),url(../fonts/fa-solid-900.woff2) format("woff2"),url(../fonts/fa-solid-900.woff) format("woff"),url(../fonts/fa-solid-900.ttf) format("truetype"),url(../fonts/fa-solid-900.svg#fontawesome) format("svg")}.fa,.fas{font-family:Font Awesome\ 5 Free;font-weight:900}
|
||||
@@ -4,31 +4,44 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Welcome</title>
|
||||
<link rel="stylesheet" type="text/css" href="assets/css/fa-brands.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="assets/css/fa-solid.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="assets/css/fontawesome.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="assets/css/welcome.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="assets/css/font-awesome.min.css">
|
||||
</head>
|
||||
|
||||
<body class="is-unselectable">
|
||||
<section class="hero is-fullheight">
|
||||
<div class="hero-head">
|
||||
<header class="navbar is-transparent" role="navigation">
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item" href="https://zealdocs.org/">ZealDocs.org</a>
|
||||
<a class="navbar-item" href="https://gitter.im/zealdocs/zeal">Gitter</a>
|
||||
<a class="navbar-item" href="https://github.com/zealdocs/zeal">GitHub</a>
|
||||
</div>
|
||||
</header>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="hero-body">
|
||||
<div class="container has-text-centered">
|
||||
<h1 class="title">
|
||||
Zeal
|
||||
</h1>
|
||||
<h2 class="subtitle">
|
||||
Docs for everyone
|
||||
</h2>
|
||||
<div class="container">
|
||||
<div class="columns is-vcentered">
|
||||
<div class="column has-text-centered">
|
||||
<p class="title">Zeal
|
||||
<span id="version" class="has-text-weight-light is-size-5"></span>
|
||||
</p>
|
||||
<p class="subtitle">Docs for everyone</p>
|
||||
</div>
|
||||
<div class="column">
|
||||
<p class="subtitle is-5">Customize</p>
|
||||
<div class="command-block" onclick="zAppBridge.triggerAction('openDocsetManager')">
|
||||
<p class="title is-6">Docsets</p>
|
||||
<p class="subtitle is-6">Install and update docsets</p>
|
||||
</div>
|
||||
<div class="command-block" onclick="zAppBridge.triggerAction('openPreferences')">
|
||||
<p class="title is-6">Preferences</p>
|
||||
<p class="subtitle is-6">Adjust application settings</p>
|
||||
</div>
|
||||
<p class="subtitle is-5">Get in touch</p>
|
||||
<div class="command-block" onclick="zAppBridge.openShortUrl('gitter')">
|
||||
<p class="title is-6">Gitter</p>
|
||||
<p class="subtitle is-6">Chat with developers and other users</p>
|
||||
</div>
|
||||
<div class="command-block" onclick="zAppBridge.openShortUrl('github')">
|
||||
<p class="title is-6">GitHub</p>
|
||||
<p class="subtitle is-6">Contribute to the project</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -36,21 +49,33 @@
|
||||
<div class="container">
|
||||
<div class="content has-text-centered">
|
||||
<p>
|
||||
<a class="icon" href="https://github.com/zealdocs/zeal">
|
||||
<i class="fa fa-github"></i>
|
||||
<a class="icon" onclick="zAppBridge.openShortUrl('github')">
|
||||
<i class="fab fa-github"></i>
|
||||
</a>
|
||||
<a class="icon" href="https://twitter.com/zealdocs">
|
||||
<i class="fa fa-twitter"></i>
|
||||
<a class="icon" onclick="zAppBridge.openShortUrl('gitter')">
|
||||
<i class="fab fa-gitter"></i>
|
||||
</a>
|
||||
<a class="icon" onclick="zAppBridge.openShortUrl('twiter')">
|
||||
<i class="fab fa-twitter"></i>
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
<a class="is-size-7" href="https://www.kapeli.com/dash">Get Dash for macOS or iOS</a>
|
||||
<a class="is-size-7" onclick="zAppBridge.openShortUrl('report-bug')">
|
||||
<span class="icon">
|
||||
<i class="fas fa-bug"></i>
|
||||
</span>
|
||||
<span>Report a problem</span>
|
||||
</a>
|
||||
</p>
|
||||
<p></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
document.getElementById("version").textContent = zAppBridge.AppVersion;
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
@@ -4,59 +4,84 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Welcome</title>
|
||||
<link rel="stylesheet" type="text/css" href="assets/css/fa-brands.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="assets/css/fa-solid.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="assets/css/fontawesome.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="assets/css/welcome.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="assets/css/font-awesome.min.css">
|
||||
</head>
|
||||
|
||||
<body class="is-unselectable">
|
||||
<section class="hero is-fullheight">
|
||||
<div class="hero-head">
|
||||
<header class="navbar is-transparent" role="navigation">
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item" href="https://zealdocs.org/">ZealDocs.org</a>
|
||||
<a class="navbar-item" href="https://gitter.im/zealdocs/zeal">Gitter</a>
|
||||
<a class="navbar-item" href="https://github.com/zealdocs/zeal">GitHub</a>
|
||||
</div>
|
||||
</header>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="hero-body">
|
||||
<div class="container has-text-centered">
|
||||
<h1 class="title">
|
||||
Zeal
|
||||
</h1>
|
||||
<h2 class="subtitle">
|
||||
Docs for everyone
|
||||
</h2>
|
||||
<div class="container">
|
||||
<div class="columns is-vcentered">
|
||||
<div class="column has-text-centered">
|
||||
<p class="title">Zeal
|
||||
<span id="version" class="has-text-weight-light is-size-5"></span>
|
||||
</p>
|
||||
<p class="subtitle">Docs for everyone</p>
|
||||
<div id="carboncontainer" style="margin-top: 75px;">
|
||||
<div id="carbon" class="box">
|
||||
<script async type="text/javascript" src="https://cdn.carbonads.com/carbon.js?zoneid=1673&serve=C6AILKT&placement=zealdocsforwindowsorg"
|
||||
onerror="document.getElementById('carboncontainer').style.display = 'none';" id="_carbonads_js"></script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<p class="subtitle is-5">Customize</p>
|
||||
<div class="command-block" onclick="zAppBridge.triggerAction('openDocsetManager')">
|
||||
<p class="title is-6">Docsets</p>
|
||||
<p class="subtitle is-6">Install and update docsets</p>
|
||||
</div>
|
||||
<div class="command-block" onclick="zAppBridge.triggerAction('openPreferences')">
|
||||
<p class="title is-6">Preferences</p>
|
||||
<p class="subtitle is-6">Adjust application settings</p>
|
||||
</div>
|
||||
<p class="subtitle is-5">Get in touch</p>
|
||||
<div class="command-block" onclick="zAppBridge.openShortUrl('gitter')">
|
||||
<p class="title is-6">Gitter</p>
|
||||
<p class="subtitle is-6">Chat with developers and other users</p>
|
||||
</div>
|
||||
<div class="command-block" onclick="zAppBridge.openShortUrl('github')">
|
||||
<p class="title is-6">GitHub</p>
|
||||
<p class="subtitle is-6">Contribute to the project</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hero-foot">
|
||||
<div class="container">
|
||||
<div class="content has-text-centered">
|
||||
<div id="carboncontainer">
|
||||
<div id="carbon" class="box">
|
||||
<script async type="text/javascript" src="https://cdn.carbonads.com/carbon.js?zoneid=1673&serve=C6AILKT&placement=zealdocsforwindowsorg"
|
||||
onerror="document.getElementById('carboncontainer').style.display = 'none';" id="_carbonads_js"></script>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
<a class="icon" href="https://github.com/zealdocs/zeal">
|
||||
<i class="fa fa-github"></i>
|
||||
<a class="icon" onclick="zAppBridge.openShortUrl('github')">
|
||||
<i class="fab fa-github"></i>
|
||||
</a>
|
||||
<a class="icon" href="https://twitter.com/zealdocs">
|
||||
<i class="fa fa-twitter"></i>
|
||||
<a class="icon" onclick="zAppBridge.openShortUrl('gitter')">
|
||||
<i class="fab fa-gitter"></i>
|
||||
</a>
|
||||
<a class="icon" onclick="zAppBridge.openShortUrl('twiter')">
|
||||
<i class="fab fa-twitter"></i>
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
<a class="is-size-7" href="https://www.kapeli.com/dash">Get Dash for macOS or iOS</a>
|
||||
<a class="is-size-7" onclick="zAppBridge.openShortUrl('report-bug')">
|
||||
<span class="icon">
|
||||
<i class="fas fa-bug"></i>
|
||||
</span>
|
||||
<span>Report a problem</span>
|
||||
</a>
|
||||
</p>
|
||||
<p></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
document.getElementById("version").textContent = zAppBridge.AppVersion;
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
@@ -2,10 +2,13 @@
|
||||
<qresource prefix="/">
|
||||
<file>zeal.ico</file>
|
||||
<file>browser/assets/css/darkmode.css</file>
|
||||
<file>browser/assets/css/font-awesome.min.css</file>
|
||||
<file>browser/assets/css/fa-brands.min.css</file>
|
||||
<file>browser/assets/css/fa-solid.min.css</file>
|
||||
<file>browser/assets/css/fontawesome.min.css</file>
|
||||
<file>browser/assets/css/highlight.css</file>
|
||||
<file>browser/assets/css/welcome.min.css</file>
|
||||
<file>browser/assets/fonts/fontawesome-webfont.woff</file>
|
||||
<file>browser/assets/fonts/fa-brands-400.woff</file>
|
||||
<file>browser/assets/fonts/fa-solid-900.woff</file>
|
||||
<file>browser/welcome.html</file>
|
||||
<file>browser/welcome-noad.html</file>
|
||||
<file>icons/type/Abbreviation.png</file>
|
||||
|
||||
@@ -109,6 +109,11 @@ Application *Application::instance()
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
WidgetUi::MainWindow *Application::mainWindow() const
|
||||
{
|
||||
return m_mainWindow;
|
||||
}
|
||||
|
||||
QNetworkAccessManager *Application::networkManager() const
|
||||
{
|
||||
return m_networkManager;
|
||||
|
||||
@@ -55,6 +55,8 @@ public:
|
||||
|
||||
static Application *instance();
|
||||
|
||||
WidgetUi::MainWindow *mainWindow() const;
|
||||
|
||||
QNetworkAccessManager *networkManager() const;
|
||||
Settings *settings() const;
|
||||
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
ZEAL_LIB_NAME = Core
|
||||
|
||||
QT += concurrent network webkit widgets
|
||||
|
||||
unix:!macx {
|
||||
CONFIG += link_pkgconfig
|
||||
PKGCONFIG += libarchive
|
||||
}
|
||||
win32: {
|
||||
LIBS += -larchive_static -lz
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
include($$ZEAL_LIBRARY_PRI)
|
||||
|
||||
HEADERS += $$files(*.h)
|
||||
SOURCES += $$files(*.cpp)
|
||||
@@ -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
|
||||
|
||||
@@ -22,9 +22,13 @@
|
||||
|
||||
#include "filemanager.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDateTime>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QFutureWatcher>
|
||||
#include <QLoggingCategory>
|
||||
#include <QStandardPaths>
|
||||
|
||||
#include <QtConcurrent>
|
||||
|
||||
@@ -35,6 +39,9 @@ static Q_LOGGING_CATEGORY(log, "zeal.core.filemanager")
|
||||
FileManager::FileManager(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
// Ensure that cache location exists.
|
||||
// TODO: Check for errors.
|
||||
QDir().mkpath(cacheLocation());
|
||||
}
|
||||
|
||||
bool FileManager::removeRecursively(const QString &path)
|
||||
@@ -74,3 +81,12 @@ bool FileManager::removeRecursively(const QString &path)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QString FileManager::cacheLocation()
|
||||
{
|
||||
#ifndef PORTABLE_BUILD
|
||||
return QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
|
||||
#else
|
||||
return QCoreApplication::applicationDirPath() + QLatin1String("/cache");
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -30,12 +30,13 @@ namespace Core {
|
||||
|
||||
class FileManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
typedef unsigned int JobId;
|
||||
|
||||
explicit FileManager(QObject *parent = nullptr);
|
||||
|
||||
bool removeRecursively(const QString &path);
|
||||
|
||||
static QString cacheLocation();
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
|
||||
#include "settings.h"
|
||||
|
||||
#include "filemanager.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include <QSettings>
|
||||
@@ -47,6 +49,13 @@ using namespace Zeal::Core;
|
||||
Settings::Settings(QObject *parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
qRegisterMetaTypeStreamOperators<ExternalLinkPolicy>("ExternalLinkPolicy");
|
||||
|
||||
// Enable local storage due to https://github.com/zealdocs/zeal/issues/872.
|
||||
QWebSettings *webSettings = QWebSettings::globalSettings();
|
||||
webSettings->setLocalStoragePath(FileManager::cacheLocation() + QLatin1String("/localStorage"));
|
||||
webSettings->setAttribute(QWebSettings::LocalStorageEnabled, true);
|
||||
|
||||
load();
|
||||
}
|
||||
|
||||
@@ -81,13 +90,53 @@ void Settings::load()
|
||||
settings->endGroup();
|
||||
|
||||
settings->beginGroup(GroupContent);
|
||||
// Fonts
|
||||
QWebSettings *webSettings = QWebSettings::globalSettings();
|
||||
serifFontFamily = settings->value(QStringLiteral("serif_font_family"),
|
||||
webSettings->fontFamily(QWebSettings::SerifFont)).toString();
|
||||
sansSerifFontFamily = settings->value(QStringLiteral("sans_serif_font_family"),
|
||||
webSettings->fontFamily(QWebSettings::SansSerifFont)).toString();
|
||||
fixedFontFamily = settings->value(QStringLiteral("fixed_font_family"),
|
||||
webSettings->fontFamily(QWebSettings::FixedFont)).toString();
|
||||
|
||||
static const QMap<QString, QWebSettings::FontFamily> fontFamilies = {
|
||||
{QStringLiteral("sans-serif"), QWebSettings::SansSerifFont},
|
||||
{QStringLiteral("serif"), QWebSettings::SerifFont},
|
||||
{QStringLiteral("monospace"), QWebSettings::FixedFont}
|
||||
};
|
||||
|
||||
defaultFontFamily = settings->value(QStringLiteral("default_font_family"),
|
||||
QStringLiteral("serif")).toString();
|
||||
|
||||
// Fallback to the serif font family.
|
||||
if (!fontFamilies.contains(defaultFontFamily)) {
|
||||
defaultFontFamily = QStringLiteral("serif");
|
||||
}
|
||||
|
||||
webSettings->setFontFamily(QWebSettings::SansSerifFont, sansSerifFontFamily);
|
||||
webSettings->setFontFamily(QWebSettings::SerifFont, serifFontFamily);
|
||||
webSettings->setFontFamily(QWebSettings::FixedFont, fixedFontFamily);
|
||||
|
||||
const QString defaultFontFamilyResolved = webSettings->fontFamily(fontFamilies.value(defaultFontFamily));
|
||||
webSettings->setFontFamily(QWebSettings::StandardFont, defaultFontFamilyResolved);
|
||||
|
||||
defaultFontSize = settings->value(QStringLiteral("default_font_size"),
|
||||
webSettings->fontSize(QWebSettings::DefaultFontSize)).toInt();
|
||||
defaultFixedFontSize = settings->value(QStringLiteral("default_fixed_font_size"),
|
||||
webSettings->fontSize(QWebSettings::DefaultFixedFontSize)).toInt();
|
||||
minimumFontSize = settings->value(QStringLiteral("minimum_font_size"),
|
||||
QWebSettings::globalSettings()->fontSize(QWebSettings::MinimumFontSize)).toInt();
|
||||
QWebSettings::globalSettings()->setFontSize(QWebSettings::MinimumFontSize, minimumFontSize);
|
||||
webSettings->fontSize(QWebSettings::MinimumFontSize)).toInt();
|
||||
|
||||
webSettings->setFontSize(QWebSettings::DefaultFontSize, defaultFontSize);
|
||||
webSettings->setFontSize(QWebSettings::DefaultFixedFontSize, defaultFixedFontSize);
|
||||
webSettings->setFontSize(QWebSettings::MinimumFontSize, minimumFontSize);
|
||||
|
||||
darkModeEnabled = settings->value(QStringLiteral("dark_mode"), false).toBool();
|
||||
highlightOnNavigateEnabled = settings->value(QStringLiteral("highlight_on_navigate"), true).toBool();
|
||||
customCssFile = settings->value(QStringLiteral("custom_css_file")).toString();
|
||||
externalLinkPolicy = settings->value(QStringLiteral("external_link_policy"),
|
||||
QVariant::fromValue(ExternalLinkPolicy::Ask)).value<ExternalLinkPolicy>();
|
||||
isSmoothScrollingEnabled = settings->value(QStringLiteral("smooth_scrolling"), false).toBool();
|
||||
isAdDisabled = settings->value(QStringLiteral("disable_ad"), false).toBool();
|
||||
settings->endGroup();
|
||||
|
||||
@@ -153,10 +202,20 @@ void Settings::save()
|
||||
settings->endGroup();
|
||||
|
||||
settings->beginGroup(GroupContent);
|
||||
settings->setValue(QStringLiteral("default_font_family"), defaultFontFamily);
|
||||
settings->setValue(QStringLiteral("serif_font_family"), serifFontFamily);
|
||||
settings->setValue(QStringLiteral("sans_serif_font_family"), sansSerifFontFamily);
|
||||
settings->setValue(QStringLiteral("fixed_font_family"), fixedFontFamily);
|
||||
|
||||
settings->setValue(QStringLiteral("default_font_size"), defaultFontSize);
|
||||
settings->setValue(QStringLiteral("default_fixed_font_size"), defaultFixedFontSize);
|
||||
settings->setValue(QStringLiteral("minimum_font_size"), minimumFontSize);
|
||||
|
||||
settings->setValue(QStringLiteral("dark_mode"), darkModeEnabled);
|
||||
settings->setValue(QStringLiteral("highlight_on_navigate"), highlightOnNavigateEnabled);
|
||||
settings->setValue(QStringLiteral("custom_css_file"), customCssFile);
|
||||
settings->setValue(QStringLiteral("external_link_policy"), QVariant::fromValue(externalLinkPolicy));
|
||||
settings->setValue(QStringLiteral("smooth_scrolling"), isSmoothScrollingEnabled);
|
||||
settings->setValue(QStringLiteral("disable_ad"), isAdDisabled);
|
||||
settings->endGroup();
|
||||
|
||||
@@ -254,3 +313,17 @@ QSettings *Settings::qsettings(QObject *parent)
|
||||
QSettings::IniFormat, parent);
|
||||
#endif
|
||||
}
|
||||
|
||||
QDataStream &operator<<(QDataStream &out, const Settings::ExternalLinkPolicy &policy)
|
||||
{
|
||||
out << static_cast<std::underlying_type<Settings::ExternalLinkPolicy>::type>(policy);
|
||||
return out;
|
||||
}
|
||||
|
||||
QDataStream &operator>>(QDataStream &in, Settings::ExternalLinkPolicy &policy)
|
||||
{
|
||||
std::underlying_type<Settings::ExternalLinkPolicy>::type value;
|
||||
in >> value;
|
||||
policy = static_cast<Settings::ExternalLinkPolicy>(value);
|
||||
return in;
|
||||
}
|
||||
|
||||
@@ -60,15 +60,29 @@ public:
|
||||
bool fuzzySearchEnabled;
|
||||
|
||||
// Content
|
||||
QString defaultFontFamily;
|
||||
QString serifFontFamily;
|
||||
QString sansSerifFontFamily;
|
||||
QString fixedFontFamily;
|
||||
|
||||
int defaultFontSize;
|
||||
int defaultFixedFontSize;
|
||||
int minimumFontSize;
|
||||
|
||||
enum class ExternalLinkPolicy : unsigned int {
|
||||
Ask = 0,
|
||||
Open,
|
||||
OpenInSystemBrowser
|
||||
};
|
||||
Q_ENUM(ExternalLinkPolicy)
|
||||
ExternalLinkPolicy externalLinkPolicy = ExternalLinkPolicy::Ask;
|
||||
|
||||
bool darkModeEnabled;
|
||||
bool highlightOnNavigateEnabled;
|
||||
QString customCssFile;
|
||||
bool isSmoothScrollingEnabled;
|
||||
bool isAdDisabled;
|
||||
|
||||
// TODO: bool askOnExternalLink;
|
||||
// TODO: QString customCss;
|
||||
|
||||
// Network
|
||||
enum ProxyType : unsigned int {
|
||||
None,
|
||||
@@ -118,4 +132,9 @@ private:
|
||||
} // namespace Core
|
||||
} // namespace Zeal
|
||||
|
||||
QDataStream &operator<<(QDataStream &out, const Zeal::Core::Settings::ExternalLinkPolicy &policy);
|
||||
QDataStream &operator>>(QDataStream &in, Zeal::Core::Settings::ExternalLinkPolicy &policy);
|
||||
|
||||
Q_DECLARE_METATYPE(Zeal::Core::Settings::ExternalLinkPolicy)
|
||||
|
||||
#endif // ZEAL_CORE_SETTINGS_H
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
TEMPLATE = subdirs
|
||||
|
||||
SUBDIRS += \
|
||||
core \
|
||||
registry \
|
||||
ui \
|
||||
util
|
||||
@@ -132,7 +132,8 @@ Docset::Docset(const QString &path) :
|
||||
sqlite3_create_function(m_db->handle(), "zealScore", 2, SQLITE_UTF8, nullptr,
|
||||
sqliteScoreFunction, nullptr, nullptr);
|
||||
|
||||
m_type = m_db->tables().contains(QStringLiteral("searchIndex")) ? Type::Dash : Type::ZDash;
|
||||
m_type = m_db->tables().contains(QStringLiteral("searchIndex"), Qt::CaseInsensitive)
|
||||
? Type::Dash : Type::ZDash;
|
||||
|
||||
createIndex();
|
||||
|
||||
@@ -157,8 +158,9 @@ Docset::Docset(const QString &path) :
|
||||
|
||||
if (plist.contains(InfoPlist::DashDocSetFamily)) {
|
||||
const QString kw = plist[InfoPlist::DashDocSetFamily].toString();
|
||||
if (kw != QLatin1String("dashtoc") && kw != QLatin1String("unsorteddashtoc"))
|
||||
if (!kw.contains(QLatin1String("dashtoc"))) {
|
||||
m_keywords << kw;
|
||||
}
|
||||
}
|
||||
|
||||
m_keywords.removeDuplicates();
|
||||
@@ -406,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();
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
ZEAL_LIB_NAME = Registry
|
||||
|
||||
QT += concurrent
|
||||
@@ -1,4 +0,0 @@
|
||||
include($$ZEAL_LIBRARY_PRI)
|
||||
|
||||
HEADERS += $$files(*.h)
|
||||
SOURCES += $$files(*.cpp)
|
||||
@@ -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
|
||||
|
||||
@@ -2,6 +2,7 @@ add_subdirectory(qxtglobalshortcut)
|
||||
|
||||
list(APPEND Widgets_SOURCES
|
||||
widgets/searchedit.cpp
|
||||
widgets/searchtoolbar.cpp
|
||||
widgets/shortcutedit.cpp
|
||||
widgets/toolbarframe.cpp
|
||||
widgets/webview.cpp
|
||||
@@ -23,6 +24,7 @@ add_library(Ui
|
||||
progressitemdelegate.cpp
|
||||
searchitemdelegate.cpp
|
||||
settingsdialog.cpp
|
||||
webbridge.cpp
|
||||
${Widgets_SOURCES}
|
||||
${Ui_FORMS} # For Qt Creator.
|
||||
)
|
||||
|
||||
@@ -75,13 +75,13 @@
|
||||
<property name="text">
|
||||
<string><strong>A simple offline documentation browser</strong>
|
||||
<br><br>
|
||||
Copyright &copy; Oleg Shparber and other conributors, 2013-2017.
|
||||
Copyright &copy; Oleg Shparber and other contributors, 2013-2018.
|
||||
<br>
|
||||
<a href="https://zealdocs.org">zealdocs.org</a>
|
||||
<br>
|
||||
<a href="irc://irc.freenode.net/zealdocs">#zealdocs</a> on <a href="https://www.freenode.net">Freenode</a>
|
||||
<br><br>
|
||||
Zeal is an open source software available under the terms of General Public License version 3 (<a href="https://www.gnu.org/copyleft/gpl.html">GPLv3</a>).
|
||||
Zeal is an open source software available under the terms of the General Public License version 3 (<a href="https://www.gnu.org/copyleft/gpl.html">GPLv3</a>) or later.
|
||||
<br><br>
|
||||
Docsets are courtesy of <a href="https://kapeli.com/dash">Dash</a>.</string>
|
||||
</property>
|
||||
@@ -98,6 +98,33 @@ Docsets are courtesy of <a href="https://kapeli.com/dash">Dash&l
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_2">
|
||||
<attribute name="title">
|
||||
<string>Licenses</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QTextBrowser" name="textBrowser">
|
||||
<property name="html">
|
||||
<string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;">
|
||||
<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Zeal heavily relies on other open source software listed below.</p>
|
||||
<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Bulma</span> - <a href="https://bulma.io/"><span style=" text-decoration: underline; color:#007af4;">bulma.io</span></a><br />License: MIT License </p>
|
||||
<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Font Awesome</span> - <a href="http://fontawesome.io/"><span style=" text-decoration: underline; color:#007af4;">fontawesome.io</span></a><br />License: SIL OFL 1.1 (font), MIT License (css) </p>
|
||||
<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">libarchive</span> - <a href="https://www.libarchive.org/"><span style=" text-decoration: underline; color:#007af4;">www.libarchive.org</span></a><br />License: Simplified BSD License </p>
|
||||
<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">LibQxt</span> - <a href="https://bitbucket.org/libqxt/libqxt"><span style=" text-decoration: underline; color:#007af4;">bitbucket.org/libqxt/libqxt</span></a><br />License: New BSD License </p>
|
||||
<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Qt</span> - <a href="https://www.qt.io/"><span style=" text-decoration: underline; color:#007af4;">www.qt.io</span></a><br />License: GNU LGPL version 3 </p>
|
||||
<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">SQLite</span> - <a href="https://www.sqlite.org/"><span style=" text-decoration: underline; color:#007af4;">www.sqlite.org</span></a><br />License: Public Domain </p></body></html></string>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
||||
@@ -31,14 +31,14 @@ using namespace Zeal;
|
||||
using namespace Zeal::WidgetUi;
|
||||
|
||||
DocsetListItemDelegate::DocsetListItemDelegate(QObject *parent) :
|
||||
QItemDelegate(parent)
|
||||
QStyledItemDelegate(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void DocsetListItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const
|
||||
const QModelIndex &index) const
|
||||
{
|
||||
QItemDelegate::paint(painter, option, index);
|
||||
QStyledItemDelegate::paint(painter, option, index);
|
||||
|
||||
if (!index.model()->data(index, Registry::ItemDataRole::UpdateAvailableRole).toBool())
|
||||
return;
|
||||
@@ -55,8 +55,24 @@ void DocsetListItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem
|
||||
|
||||
painter->save();
|
||||
|
||||
if (option.state & QStyle::State_Selected)
|
||||
painter->setPen(QPen(option.palette.highlightedText(), 1));
|
||||
QPalette palette = option.palette;
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
// QWindowsVistaStyle overrides highlight colour.
|
||||
if (option.widget->style()->objectName() == QLatin1String("windowsvista")) {
|
||||
palette.setColor(QPalette::All, QPalette::HighlightedText,
|
||||
palette.color(QPalette::Active, QPalette::Text));
|
||||
}
|
||||
#endif
|
||||
|
||||
const QPalette::ColorGroup cg = (option.state & QStyle::State_Active)
|
||||
? QPalette::Normal : QPalette::Inactive;
|
||||
|
||||
if (option.state & QStyle::State_Selected) {
|
||||
painter->setPen(palette.color(cg, QPalette::HighlightedText));
|
||||
} else {
|
||||
painter->setPen(palette.color(cg, QPalette::Text));
|
||||
}
|
||||
|
||||
painter->setFont(font);
|
||||
painter->drawText(textRect, text);
|
||||
|
||||
@@ -24,12 +24,12 @@
|
||||
#ifndef ZEAL_WIDGETUI_DOCSETLISTITEMDELEGATE_H
|
||||
#define ZEAL_WIDGETUI_DOCSETLISTITEMDELEGATE_H
|
||||
|
||||
#include <QItemDelegate>
|
||||
#include <QStyledItemDelegate>
|
||||
|
||||
namespace Zeal {
|
||||
namespace WidgetUi {
|
||||
|
||||
class DocsetListItemDelegate : public QItemDelegate
|
||||
class DocsetListItemDelegate : public QStyledItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include <registry/listmodel.h>
|
||||
|
||||
#include <QClipboard>
|
||||
#include <QDateTime>
|
||||
#include <QDir>
|
||||
#include <QInputDialog>
|
||||
#include <QJsonArray>
|
||||
@@ -43,13 +44,16 @@
|
||||
#include <QMessageBox>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
#include <QStandardPaths>
|
||||
#include <QTemporaryFile>
|
||||
#include <QUrl>
|
||||
|
||||
using namespace Zeal;
|
||||
using namespace Zeal::WidgetUi;
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
extern Q_CORE_EXPORT int qt_ntfs_permission_lookup;
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
const char ApiServerUrl[] = "http://api.zealdocs.org/v1";
|
||||
const char RedirectServerUrl[] = "https://go.zealdocs.org";
|
||||
@@ -72,12 +76,21 @@ DocsetsDialog::DocsetsDialog(Core::Application *app, QWidget *parent) :
|
||||
m_application(app),
|
||||
m_docsetRegistry(app->docsetRegistry())
|
||||
{
|
||||
using Registry::DocsetRegistry;
|
||||
using Registry::ListModel;
|
||||
|
||||
ui->setupUi(this);
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
loadDocsetList();
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
qt_ntfs_permission_lookup++;
|
||||
#endif
|
||||
|
||||
m_isStorageReadOnly = !QFileInfo(m_application->settings()->docsetPath).isWritable();
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
qt_ntfs_permission_lookup--;
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_OSX
|
||||
ui->availableDocsetList->setAttribute(Qt::WA_MacShowFocusRect, false);
|
||||
ui->installedDocsetList->setAttribute(Qt::WA_MacShowFocusRect, false);
|
||||
@@ -85,90 +98,7 @@ DocsetsDialog::DocsetsDialog(Core::Application *app, QWidget *parent) :
|
||||
|
||||
ui->combinedProgressBar->hide();
|
||||
ui->cancelButton->hide();
|
||||
|
||||
// Installed docsets tab
|
||||
ui->installedDocsetList->setItemDelegate(new DocsetListItemDelegate(this));
|
||||
ui->installedDocsetList->setModel(new ListModel(app->docsetRegistry(), this));
|
||||
connect(ui->installedDocsetList, &QListView::activated, this, [this](const QModelIndex &index) {
|
||||
if (!index.data(Registry::ItemDataRole::UpdateAvailableRole).toBool()) {
|
||||
return;
|
||||
}
|
||||
|
||||
downloadDashDocset(index);
|
||||
});
|
||||
|
||||
QItemSelectionModel *selectionModel = ui->installedDocsetList->selectionModel();
|
||||
connect(selectionModel, &QItemSelectionModel::selectionChanged,
|
||||
[this, selectionModel]() {
|
||||
ui->removeDocsetsButton->setEnabled(selectionModel->hasSelection());
|
||||
|
||||
for (const QModelIndex &index : selectionModel->selectedRows()) {
|
||||
if (index.data(Registry::ItemDataRole::UpdateAvailableRole).toBool()) {
|
||||
ui->updateSelectedDocsetsButton->setEnabled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ui->updateSelectedDocsetsButton->setEnabled(false);
|
||||
});
|
||||
connect(ui->updateSelectedDocsetsButton, &QPushButton::clicked,
|
||||
this, &DocsetsDialog::updateSelectedDocsets);
|
||||
connect(ui->updateAllDocsetsButton, &QPushButton::clicked,
|
||||
this, &DocsetsDialog::updateAllDocsets);
|
||||
connect(ui->removeDocsetsButton, &QPushButton::clicked,
|
||||
this, &DocsetsDialog::removeSelectedDocsets);
|
||||
connect(ui->docsetFilterInput, &QLineEdit::textEdited,
|
||||
this, &DocsetsDialog::updateDocsetFilter);
|
||||
|
||||
// Available docsets tab
|
||||
ui->availableDocsetList->setItemDelegate(new ProgressItemDelegate(this));
|
||||
connect(ui->availableDocsetList, &QListView::activated, this, [this](const QModelIndex &index) {
|
||||
// TODO: Cancel download if it's already in progress.
|
||||
if (index.data(ProgressItemDelegate::ShowProgressRole).toBool())
|
||||
return;
|
||||
|
||||
ui->availableDocsetList->selectionModel()->select(index, QItemSelectionModel::Deselect);
|
||||
|
||||
QAbstractItemModel *model = ui->availableDocsetList->model();
|
||||
model->setData(index, tr("Downloading: %p%"), ProgressItemDelegate::FormatRole);
|
||||
model->setData(index, 0, ProgressItemDelegate::ValueRole);
|
||||
model->setData(index, true, ProgressItemDelegate::ShowProgressRole);
|
||||
|
||||
downloadDashDocset(index);
|
||||
});
|
||||
|
||||
selectionModel = ui->availableDocsetList->selectionModel();
|
||||
connect(selectionModel, &QItemSelectionModel::selectionChanged, [this, selectionModel]() {
|
||||
for (const QModelIndex &index : selectionModel->selectedRows()) {
|
||||
if (!index.data(ProgressItemDelegate::ShowProgressRole).toBool()) {
|
||||
ui->downloadDocsetsButton->setEnabled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ui->downloadDocsetsButton->setEnabled(false);
|
||||
});
|
||||
|
||||
connect(ui->downloadDocsetsButton, &QPushButton::clicked,
|
||||
this, &DocsetsDialog::downloadSelectedDocsets);
|
||||
|
||||
connect(m_docsetRegistry, &DocsetRegistry::docsetUnloaded, this, [this](const QString name) {
|
||||
QListWidgetItem *item = findDocsetListItem(name);
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
item->setHidden(false);
|
||||
});
|
||||
connect(m_docsetRegistry, &DocsetRegistry::docsetLoaded, this, [this](const QString name) {
|
||||
QListWidgetItem *item = findDocsetListItem(name);
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
item->setHidden(true);
|
||||
});
|
||||
|
||||
connect(ui->addFeedButton, &QPushButton::clicked, this, &DocsetsDialog::addDashFeed);
|
||||
connect(ui->refreshButton, &QPushButton::clicked, this, &DocsetsDialog::downloadDocsetList);
|
||||
ui->readOnlyLabel->setVisible(m_isStorageReadOnly);
|
||||
|
||||
connect(m_application, &Core::Application::extractionCompleted,
|
||||
this, &DocsetsDialog::extractionCompleted);
|
||||
@@ -179,7 +109,15 @@ DocsetsDialog::DocsetsDialog(Core::Application *app, QWidget *parent) :
|
||||
|
||||
connect(ui->cancelButton, &QPushButton::clicked, this, &DocsetsDialog::cancelDownloads);
|
||||
|
||||
loadDocsetList();
|
||||
setupInstalledDocsetsTab();
|
||||
setupAvailableDocsetsTab();
|
||||
|
||||
if (m_isStorageReadOnly) {
|
||||
disableControls();
|
||||
|
||||
// Updating the docset list is fine;
|
||||
ui->refreshButton->setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
DocsetsDialog::~DocsetsDialog()
|
||||
@@ -419,7 +357,11 @@ void DocsetsDialog::downloadCompleted()
|
||||
if (metadata.latestVersion() != docset->version()
|
||||
|| metadata.revision() > docset->revision()) {
|
||||
docset->hasUpdate = true;
|
||||
ui->updateAllDocsetsButton->setEnabled(true);
|
||||
|
||||
if (!m_isStorageReadOnly) {
|
||||
ui->updateAllDocsetsButton->setEnabled(true);
|
||||
}
|
||||
|
||||
ui->installedDocsetList->reset();
|
||||
}
|
||||
}
|
||||
@@ -515,8 +457,7 @@ void DocsetsDialog::extractionCompleted(const QString &filePath)
|
||||
|
||||
// Write metadata about docset
|
||||
Registry::DocsetMetadata metadata = m_availableDocsets.contains(docsetName)
|
||||
? m_availableDocsets[docsetName]
|
||||
: m_userFeeds[docsetName];
|
||||
? m_availableDocsets[docsetName] : m_userFeeds[docsetName];
|
||||
metadata.save(docsetPath, metadata.latestVersion());
|
||||
|
||||
m_docsetRegistry->loadDocset(docsetPath);
|
||||
@@ -582,6 +523,149 @@ void DocsetsDialog::loadDocsetList()
|
||||
processDocsetList(jsonDoc.array());
|
||||
}
|
||||
|
||||
void DocsetsDialog::setupInstalledDocsetsTab()
|
||||
{
|
||||
using Registry::ListModel;
|
||||
|
||||
ui->installedDocsetList->setItemDelegate(new DocsetListItemDelegate(this));
|
||||
ui->installedDocsetList->setModel(new ListModel(m_application->docsetRegistry(), this));
|
||||
|
||||
if (m_isStorageReadOnly) {
|
||||
return;
|
||||
}
|
||||
|
||||
connect(ui->installedDocsetList, &QListView::activated, this, [this](const QModelIndex &index) {
|
||||
if (!index.data(Registry::ItemDataRole::UpdateAvailableRole).toBool()) {
|
||||
return;
|
||||
}
|
||||
|
||||
downloadDashDocset(index);
|
||||
});
|
||||
|
||||
QItemSelectionModel *selectionModel = ui->installedDocsetList->selectionModel();
|
||||
connect(selectionModel, &QItemSelectionModel::selectionChanged,
|
||||
[this, selectionModel]() {
|
||||
ui->removeDocsetsButton->setEnabled(selectionModel->hasSelection());
|
||||
|
||||
for (const QModelIndex &index : selectionModel->selectedRows()) {
|
||||
if (index.data(Registry::ItemDataRole::UpdateAvailableRole).toBool()) {
|
||||
ui->updateSelectedDocsetsButton->setEnabled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ui->updateSelectedDocsetsButton->setEnabled(false);
|
||||
});
|
||||
|
||||
connect(ui->addFeedButton, &QPushButton::clicked, this, &DocsetsDialog::addDashFeed);
|
||||
|
||||
connect(ui->updateSelectedDocsetsButton, &QPushButton::clicked,
|
||||
this, &DocsetsDialog::updateSelectedDocsets);
|
||||
connect(ui->updateAllDocsetsButton, &QPushButton::clicked,
|
||||
this, &DocsetsDialog::updateAllDocsets);
|
||||
connect(ui->removeDocsetsButton, &QPushButton::clicked,
|
||||
this, &DocsetsDialog::removeSelectedDocsets);
|
||||
}
|
||||
|
||||
void DocsetsDialog::setupAvailableDocsetsTab()
|
||||
{
|
||||
using Registry::DocsetRegistry;
|
||||
|
||||
ui->availableDocsetList->setItemDelegate(new ProgressItemDelegate(this));
|
||||
|
||||
connect(m_docsetRegistry, &DocsetRegistry::docsetUnloaded, this, [this](const QString name) {
|
||||
QListWidgetItem *item = findDocsetListItem(name);
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
item->setHidden(false);
|
||||
});
|
||||
connect(m_docsetRegistry, &DocsetRegistry::docsetLoaded, this, [this](const QString name) {
|
||||
QListWidgetItem *item = findDocsetListItem(name);
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
item->setHidden(true);
|
||||
});
|
||||
|
||||
connect(ui->refreshButton, &QPushButton::clicked, this, &DocsetsDialog::downloadDocsetList);
|
||||
|
||||
connect(ui->docsetFilterInput, &QLineEdit::textEdited,
|
||||
this, &DocsetsDialog::updateDocsetFilter);
|
||||
|
||||
if (m_isStorageReadOnly) {
|
||||
return;
|
||||
}
|
||||
|
||||
connect(ui->availableDocsetList, &QListView::activated, this, [this](const QModelIndex &index) {
|
||||
// TODO: Cancel download if it's already in progress.
|
||||
if (index.data(ProgressItemDelegate::ShowProgressRole).toBool())
|
||||
return;
|
||||
|
||||
ui->availableDocsetList->selectionModel()->select(index, QItemSelectionModel::Deselect);
|
||||
|
||||
QAbstractItemModel *model = ui->availableDocsetList->model();
|
||||
model->setData(index, tr("Downloading: %p%"), ProgressItemDelegate::FormatRole);
|
||||
model->setData(index, 0, ProgressItemDelegate::ValueRole);
|
||||
model->setData(index, true, ProgressItemDelegate::ShowProgressRole);
|
||||
|
||||
downloadDashDocset(index);
|
||||
});
|
||||
|
||||
QItemSelectionModel *selectionModel = ui->availableDocsetList->selectionModel();
|
||||
connect(selectionModel, &QItemSelectionModel::selectionChanged, [this, selectionModel]() {
|
||||
for (const QModelIndex &index : selectionModel->selectedRows()) {
|
||||
if (!index.data(ProgressItemDelegate::ShowProgressRole).toBool()) {
|
||||
ui->downloadDocsetsButton->setEnabled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ui->downloadDocsetsButton->setEnabled(false);
|
||||
});
|
||||
|
||||
connect(ui->downloadDocsetsButton, &QPushButton::clicked,
|
||||
this, &DocsetsDialog::downloadSelectedDocsets);
|
||||
}
|
||||
|
||||
void DocsetsDialog::enableControls()
|
||||
{
|
||||
// Available docsets
|
||||
ui->refreshButton->setEnabled(true);
|
||||
|
||||
if (m_isStorageReadOnly) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Installed docsets
|
||||
ui->addFeedButton->setEnabled(true);
|
||||
QItemSelectionModel *selectionModel = ui->installedDocsetList->selectionModel();
|
||||
bool hasSelectedUpdates = false;
|
||||
for (const QModelIndex &index : selectionModel->selectedRows()) {
|
||||
if (index.data(Registry::ItemDataRole::UpdateAvailableRole).toBool()) {
|
||||
hasSelectedUpdates = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ui->updateSelectedDocsetsButton->setEnabled(hasSelectedUpdates);
|
||||
ui->updateAllDocsetsButton->setEnabled(updatesAvailable());
|
||||
ui->removeDocsetsButton->setEnabled(selectionModel->hasSelection());
|
||||
}
|
||||
|
||||
void DocsetsDialog::disableControls()
|
||||
{
|
||||
// Installed docsets
|
||||
ui->addFeedButton->setEnabled(false);
|
||||
ui->updateSelectedDocsetsButton->setEnabled(false);
|
||||
ui->updateAllDocsetsButton->setEnabled(false);
|
||||
ui->downloadDocsetsButton->setEnabled(false);
|
||||
ui->removeDocsetsButton->setEnabled(false);
|
||||
|
||||
// Available docsets
|
||||
ui->refreshButton->setEnabled(false);
|
||||
}
|
||||
|
||||
QListWidgetItem *DocsetsDialog::findDocsetListItem(const QString &name) const
|
||||
{
|
||||
for (int i = 0; i < ui->availableDocsetList->count(); ++i) {
|
||||
@@ -611,14 +695,7 @@ QNetworkReply *DocsetsDialog::download(const QUrl &url)
|
||||
connect(reply, &QNetworkReply::finished, this, &DocsetsDialog::downloadCompleted);
|
||||
m_replies.append(reply);
|
||||
|
||||
// Installed docsets
|
||||
ui->addFeedButton->setEnabled(false);
|
||||
ui->updateSelectedDocsetsButton->setEnabled(false);
|
||||
ui->updateAllDocsetsButton->setEnabled(false);
|
||||
ui->removeDocsetsButton->setEnabled(false);
|
||||
|
||||
// Available docsets
|
||||
ui->refreshButton->setEnabled(false);
|
||||
disableControls();
|
||||
|
||||
updateCombinedProgress();
|
||||
|
||||
@@ -685,7 +762,10 @@ void DocsetsDialog::processDocsetList(const QJsonArray &list)
|
||||
if (metadata.latestVersion() != docset->version()
|
||||
|| metadata.revision() > docset->revision()) {
|
||||
docset->hasUpdate = true;
|
||||
ui->updateAllDocsetsButton->setEnabled(true);
|
||||
|
||||
if (!m_isStorageReadOnly) {
|
||||
ui->updateAllDocsetsButton->setEnabled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -761,23 +841,7 @@ void DocsetsDialog::resetProgress()
|
||||
m_combinedReceived = 0;
|
||||
m_combinedTotal = 0;
|
||||
|
||||
// Installed docsets
|
||||
ui->addFeedButton->setEnabled(true);
|
||||
QItemSelectionModel *selectionModel = ui->installedDocsetList->selectionModel();
|
||||
bool hasSelectedUpdates = false;
|
||||
for (const QModelIndex &index : selectionModel->selectedRows()) {
|
||||
if (index.data(Registry::ItemDataRole::UpdateAvailableRole).toBool()) {
|
||||
hasSelectedUpdates = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ui->updateSelectedDocsetsButton->setEnabled(hasSelectedUpdates);
|
||||
ui->updateAllDocsetsButton->setEnabled(updatesAvailable());
|
||||
ui->removeDocsetsButton->setEnabled(selectionModel->hasSelection());
|
||||
|
||||
// Available docsets
|
||||
ui->refreshButton->setEnabled(true);
|
||||
enableControls();
|
||||
}
|
||||
|
||||
QString DocsetsDialog::docsetNameForTmpFilePath(const QString &filePath) const
|
||||
@@ -801,13 +865,5 @@ int DocsetsDialog::percent(qint64 fraction, qint64 total)
|
||||
|
||||
QString DocsetsDialog::cacheLocation(const QString &fileName)
|
||||
{
|
||||
#ifndef PORTABLE_BUILD
|
||||
const QDir cacheDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
||||
#else
|
||||
const QDir cacheDir(QCoreApplication::applicationDirPath() + QLatin1String("/cache"));
|
||||
#endif
|
||||
// TODO: Report error
|
||||
QDir().mkpath(cacheDir.path());
|
||||
|
||||
return cacheDir.filePath(fileName);
|
||||
return QDir(Core::FileManager::cacheLocation()).filePath(fileName);
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ class QNetworkReply;
|
||||
class QTemporaryFile;
|
||||
class QUrl;
|
||||
|
||||
|
||||
namespace Zeal {
|
||||
|
||||
namespace Registry {
|
||||
@@ -90,6 +89,8 @@ private:
|
||||
Core::Application *m_application = nullptr;
|
||||
Registry::DocsetRegistry *m_docsetRegistry = nullptr;
|
||||
|
||||
bool m_isStorageReadOnly = false;
|
||||
|
||||
QList<QNetworkReply *> m_replies;
|
||||
qint64 m_combinedTotal = 0;
|
||||
qint64 m_combinedReceived = 0;
|
||||
@@ -100,6 +101,12 @@ private:
|
||||
|
||||
QHash<QString, QTemporaryFile *> m_tmpFiles;
|
||||
|
||||
void setupInstalledDocsetsTab();
|
||||
void setupAvailableDocsetsTab();
|
||||
|
||||
void enableControls();
|
||||
void disableControls();
|
||||
|
||||
QListWidgetItem *findDocsetListItem(const QString &name) const;
|
||||
bool updatesAvailable() const;
|
||||
|
||||
|
||||
@@ -196,7 +196,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,0,1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,0,0,1">
|
||||
<item>
|
||||
<widget class="QProgressBar" name="combinedProgressBar">
|
||||
<property name="minimumSize">
|
||||
@@ -214,6 +214,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="readOnlyLabel">
|
||||
<property name="text">
|
||||
<string><b>Docset storage is read only.</b></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "docsetsdialog.h"
|
||||
#include "searchitemdelegate.h"
|
||||
#include "settingsdialog.h"
|
||||
#include "webbridge.h"
|
||||
#include "qxtglobalshortcut/qxtglobalshortcut.h"
|
||||
#include "widgets/webviewtab.h"
|
||||
|
||||
@@ -38,6 +39,7 @@
|
||||
#include <registry/itemdatarole.h>
|
||||
#include <registry/listmodel.h>
|
||||
#include <registry/searchmodel.h>
|
||||
#include <registry/searchquery.h>
|
||||
|
||||
#include <QCloseEvent>
|
||||
#include <QDesktopServices>
|
||||
@@ -155,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);
|
||||
@@ -183,22 +192,31 @@ MainWindow::MainWindow(Core::Application *app, QWidget *parent) :
|
||||
// Edit
|
||||
ui->actionFind->setShortcut(QKeySequence::Find);
|
||||
connect(ui->actionFind, &QAction::triggered, this, [this]() {
|
||||
currentTab()->showSearchBar();
|
||||
currentTab()->activateSearchBar();
|
||||
});
|
||||
|
||||
if (QKeySequence(QKeySequence::Preferences).isEmpty()) {
|
||||
ui->actionPreferences->setShortcut(QStringLiteral("Ctrl+,"));
|
||||
} else {
|
||||
ui->actionPreferences->setShortcut(QKeySequence::Preferences);
|
||||
}
|
||||
|
||||
connect(ui->actionPreferences, &QAction::triggered, [this]() {
|
||||
m_globalShortcut->setEnabled(false);
|
||||
QScopedPointer<SettingsDialog> dialog(new SettingsDialog(m_application, this));
|
||||
QScopedPointer<SettingsDialog> dialog(new SettingsDialog(this));
|
||||
dialog->exec();
|
||||
m_globalShortcut->setEnabled(true);
|
||||
});
|
||||
|
||||
ui->actionBack->setIcon(qApp->style()->standardIcon(QStyle::SP_ArrowBack));
|
||||
ui->actionBack->setShortcut(QKeySequence::Back);
|
||||
addAction(ui->actionBack);
|
||||
ui->actionForward->setShortcut(QKeySequence::Forward);
|
||||
addAction(ui->actionForward);
|
||||
connect(ui->actionBack, &QAction::triggered, this, [this]() { currentTab()->back(); });
|
||||
addAction(ui->actionBack);
|
||||
|
||||
ui->actionForward->setIcon(qApp->style()->standardIcon(QStyle::SP_ArrowForward));
|
||||
ui->actionForward->setShortcut(QKeySequence::Forward);
|
||||
connect(ui->actionForward, &QAction::triggered, this, [this]() { currentTab()->forward(); });
|
||||
addAction(ui->actionForward);
|
||||
|
||||
// Tools Menu
|
||||
connect(ui->actionDocsets, &QAction::triggered, [this]() {
|
||||
@@ -216,9 +234,6 @@ MainWindow::MainWindow(Core::Application *app, QWidget *parent) :
|
||||
QScopedPointer<AboutDialog> dialog(new AboutDialog(this));
|
||||
dialog->exec();
|
||||
});
|
||||
connect(ui->actionAboutQt, &QAction::triggered, [this]() {
|
||||
QMessageBox::aboutQt(this);
|
||||
});
|
||||
|
||||
// Update check
|
||||
connect(m_application, &Core::Application::updateCheckError, [this](const QString &message) {
|
||||
@@ -251,15 +266,19 @@ MainWindow::MainWindow(Core::Application *app, QWidget *parent) :
|
||||
m_backMenu->clear();
|
||||
QWebHistory *history = currentTab()->history();
|
||||
QList<QWebHistoryItem> items = history->backItems(10);
|
||||
// TODO: [Qt 5.6]
|
||||
//for (auto it = items.crbegin(); it != items.crend(); ++it) {
|
||||
#if QT_VERSION >= 0x050600
|
||||
for (auto it = items.crbegin(); it != items.crend(); ++it) {
|
||||
#else
|
||||
for (auto it = items.cend() - 1; it >= items.cbegin(); --it) {
|
||||
#endif
|
||||
const QIcon icon = docsetIcon(docsetName(it->url()));
|
||||
const QWebHistoryItem item = *it;
|
||||
// TODO: [Qt 5.6]
|
||||
// m_backMenu->addAction(icon, it->title(), [=](bool) { history->goToItem(item); });
|
||||
#if QT_VERSION >= 0x050600
|
||||
m_backMenu->addAction(icon, it->title(), [=](bool) { history->goToItem(item); });
|
||||
#else
|
||||
QAction *action = m_backMenu->addAction(icon, it->title());
|
||||
connect(action, &QAction::triggered, [=](bool) { history->goToItem(item); });
|
||||
#endif
|
||||
}
|
||||
});
|
||||
ui->backButton->setDefaultAction(ui->actionBack);
|
||||
@@ -271,10 +290,12 @@ MainWindow::MainWindow(Core::Application *app, QWidget *parent) :
|
||||
QWebHistory *history = currentTab()->history();
|
||||
for (const QWebHistoryItem &item: history->forwardItems(10)) {
|
||||
const QIcon icon = docsetIcon(docsetName(item.url()));
|
||||
// TODO: [Qt 5.6]
|
||||
//m_forwardMenu->addAction(icon, item.title(), [=](bool) { history->goToItem(item); });
|
||||
#if QT_VERSION >= 0x050600
|
||||
m_forwardMenu->addAction(icon, item.title(), [=](bool) { history->goToItem(item); });
|
||||
#else
|
||||
QAction *action = m_forwardMenu->addAction(icon, item.title());
|
||||
connect(action, &QAction::triggered, [=](bool) { history->goToItem(item); });
|
||||
#endif
|
||||
}
|
||||
});
|
||||
ui->forwardButton->setDefaultAction(ui->actionForward);
|
||||
@@ -299,6 +320,17 @@ MainWindow::MainWindow(Core::Application *app, QWidget *parent) :
|
||||
m_settings->tocSplitterState = ui->tocSplitter->saveState();
|
||||
});
|
||||
|
||||
|
||||
m_webBridge = new WebBridge(this);
|
||||
connect(m_webBridge, &WebBridge::actionTriggered, this, [this](const QString &action) {
|
||||
// TODO: In the future connect directly to the ActionManager.
|
||||
if (action == "openDocsetManager") {
|
||||
ui->actionDocsets->trigger();
|
||||
} else if (action == "openPreferences") {
|
||||
ui->actionPreferences->trigger();
|
||||
}
|
||||
});
|
||||
|
||||
createTab();
|
||||
|
||||
connect(ui->treeView, &QTreeView::clicked, this, &MainWindow::openDocset);
|
||||
@@ -390,12 +422,6 @@ MainWindow::MainWindow(Core::Application *app, QWidget *parent) :
|
||||
addAction(ui->actionCloseTab);
|
||||
connect(ui->actionCloseTab, &QAction::triggered, this, [this]() { closeTab(); });
|
||||
|
||||
connect(ui->openUrlButton, &QPushButton::clicked, [this]() {
|
||||
const QUrl url(currentTab()->url());
|
||||
if (url.scheme() != QLatin1String("qrc"))
|
||||
QDesktopServices::openUrl(url);
|
||||
});
|
||||
|
||||
ui->actionNextTab->setShortcuts({QKeySequence::NextChild,
|
||||
QKeySequence(Qt::ControlModifier| Qt::Key_PageDown)});
|
||||
addAction(ui->actionNextTab);
|
||||
@@ -505,7 +531,7 @@ void MainWindow::moveTab(int from, int to) {
|
||||
ui->webViewStack->insertWidget(to, w);
|
||||
}
|
||||
|
||||
void MainWindow::createTab(int index)
|
||||
WebViewTab *MainWindow::createTab(int index)
|
||||
{
|
||||
if (m_settings->openNewTabAfterActive)
|
||||
index = m_tabBar->currentIndex() + 1;
|
||||
@@ -513,6 +539,7 @@ void MainWindow::createTab(int index)
|
||||
index = m_tabStates.size();
|
||||
|
||||
TabState *newState = new TabState();
|
||||
newState->widget->setWebBridgeObject("zAppBridge", m_webBridge);
|
||||
newState->goToStartPage();
|
||||
|
||||
m_tabStates.insert(index, newState);
|
||||
@@ -521,6 +548,8 @@ void MainWindow::createTab(int index)
|
||||
m_tabBar->setCurrentIndex(index);
|
||||
|
||||
ui->lineEdit->setFocus();
|
||||
|
||||
return newState->widget;
|
||||
}
|
||||
|
||||
void MainWindow::duplicateTab(int index)
|
||||
@@ -532,6 +561,7 @@ void MainWindow::duplicateTab(int index)
|
||||
syncTabState(tabState);
|
||||
|
||||
TabState *newState = new TabState(*tabState);
|
||||
newState->widget->setWebBridgeObject("zAppBridge", m_webBridge);
|
||||
|
||||
++index;
|
||||
m_tabStates.insert(index, newState);
|
||||
@@ -542,15 +572,21 @@ void MainWindow::duplicateTab(int index)
|
||||
|
||||
void MainWindow::syncTreeView()
|
||||
{
|
||||
TabState *tabState = currentTabState();
|
||||
QItemSelectionModel *oldSelectionModel = ui->treeView->selectionModel();
|
||||
|
||||
if (!tabState->searchQuery.isEmpty()) {
|
||||
TabState *tabState = currentTabState();
|
||||
if (tabState->searchQuery.isEmpty()) {
|
||||
ui->treeView->setModel(m_zealListModel);
|
||||
ui->treeView->setRootIsDecorated(true);
|
||||
} else {
|
||||
ui->treeView->setModel(tabState->searchModel);
|
||||
ui->treeView->setRootIsDecorated(false);
|
||||
} else {
|
||||
ui->treeView->setModel(m_zealListModel);
|
||||
ui->treeView->setColumnHidden(1, true);
|
||||
ui->treeView->setRootIsDecorated(true);
|
||||
}
|
||||
|
||||
// TODO: Remove once QTBUG-49966 is addressed.
|
||||
QItemSelectionModel *newSelectionModel = ui->treeView->selectionModel();
|
||||
if (oldSelectionModel && newSelectionModel != oldSelectionModel) {
|
||||
oldSelectionModel->deleteLater();
|
||||
}
|
||||
|
||||
ui->treeView->reset();
|
||||
@@ -608,13 +644,6 @@ void MainWindow::attachTab(TabState *tabState)
|
||||
m_tabBar->setTabToolTip(m_tabBar->currentIndex(), title);
|
||||
});
|
||||
|
||||
connect(tabState->widget, &WebViewTab::linkClicked, this, [this](const QUrl &url) {
|
||||
const QString message = tr("Do you want to open an external link?<br>URL: <b>%1</b>");
|
||||
int ret = QMessageBox::question(this, QStringLiteral("Zeal"), message.arg(url.toString()));
|
||||
if (ret == QMessageBox::Yes)
|
||||
QDesktopServices::openUrl(url);
|
||||
});
|
||||
|
||||
ui->lineEdit->setText(tabState->searchQuery);
|
||||
ui->tocListView->setModel(tabState->tocModel);
|
||||
|
||||
@@ -738,10 +767,14 @@ void MainWindow::createTrayIcon()
|
||||
});
|
||||
|
||||
QMenu *trayIconMenu = new QMenu(this);
|
||||
|
||||
// TODO: [Qt 5.6] Use addAction(text, receiver, method...).
|
||||
#if QT_VERSION >= 0x050600
|
||||
QAction *toggleAction = trayIconMenu->addAction(tr("Show Zeal"),
|
||||
this, &MainWindow::toggleWindow);
|
||||
#else
|
||||
QAction *toggleAction = trayIconMenu->addAction(tr("Show Zeal"));
|
||||
connect(toggleAction, &QAction::triggered, this, &MainWindow::toggleWindow);
|
||||
#endif
|
||||
|
||||
connect(trayIconMenu, &QMenu::aboutToShow, this, [this, toggleAction]() {
|
||||
toggleAction->setText(isVisible() ? tr("Minimize to Tray") : tr("Show Zeal"));
|
||||
});
|
||||
@@ -853,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)) {
|
||||
@@ -877,6 +910,9 @@ void MainWindow::applySettings()
|
||||
|
||||
const QString cssUrl = QLatin1String("data:text/css;charset=utf-8;base64,") + ba.toBase64();
|
||||
QWebSettings::globalSettings()->setUserStyleSheetUrl(QUrl(cssUrl));
|
||||
|
||||
QWebSettings::globalSettings()->setAttribute(QWebSettings::ScrollAnimatorEnabled,
|
||||
m_settings->isSmoothScrollingEnabled);
|
||||
}
|
||||
|
||||
void MainWindow::toggleWindow()
|
||||
|
||||
@@ -24,8 +24,6 @@
|
||||
#ifndef ZEAL_WIDGETUI_MAINWINDOW_H
|
||||
#define ZEAL_WIDGETUI_MAINWINDOW_H
|
||||
|
||||
#include <registry/searchquery.h>
|
||||
|
||||
#include <QMainWindow>
|
||||
|
||||
class QxtGlobalShortcut;
|
||||
@@ -44,6 +42,7 @@ class Settings;
|
||||
|
||||
namespace Registry {
|
||||
class ListModel;
|
||||
class SearchQuery;
|
||||
} //namespace Registry
|
||||
|
||||
namespace WidgetUi {
|
||||
@@ -52,6 +51,7 @@ namespace Ui {
|
||||
class MainWindow;
|
||||
} // namespace Ui
|
||||
|
||||
class WebBridge;
|
||||
class WebViewTab;
|
||||
|
||||
struct TabState;
|
||||
@@ -60,12 +60,12 @@ class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MainWindow(Zeal::Core::Application *app, QWidget *parent = nullptr);
|
||||
explicit MainWindow(Core::Application *app, QWidget *parent = nullptr);
|
||||
~MainWindow() override;
|
||||
|
||||
void search(const Zeal::Registry::SearchQuery &query);
|
||||
void search(const Registry::SearchQuery &query);
|
||||
void bringToFront();
|
||||
void createTab(int index = -1);
|
||||
WebViewTab *createTab(int index = -1);
|
||||
|
||||
public slots:
|
||||
void toggleWindow();
|
||||
@@ -107,9 +107,11 @@ private:
|
||||
QList<TabState *> m_tabStates;
|
||||
|
||||
Ui::MainWindow *ui = nullptr;
|
||||
Zeal::Core::Application *m_application = nullptr;
|
||||
Zeal::Core::Settings *m_settings = nullptr;
|
||||
Zeal::Registry::ListModel *m_zealListModel = nullptr;
|
||||
Core::Application *m_application = nullptr;
|
||||
Core::Settings *m_settings = nullptr;
|
||||
Registry::ListModel *m_zealListModel = nullptr;
|
||||
|
||||
WebBridge *m_webBridge = nullptr;
|
||||
|
||||
QMenu *m_backMenu = nullptr;
|
||||
QMenu *m_forwardMenu = nullptr;
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
</property>
|
||||
<widget class="QWidget" name="centralWidget">
|
||||
<layout class="QVBoxLayout" name="centralWidgetLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
@@ -46,9 +49,18 @@
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="searchToolBarFrame">
|
||||
<property name="spacing">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
@@ -67,6 +79,12 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSplitter" name="tocSplitter">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>150</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
@@ -104,9 +122,18 @@
|
||||
<item>
|
||||
<widget class="ToolBarFrame" name="navigationBar" native="true">
|
||||
<layout class="QHBoxLayout" name="toolbox">
|
||||
<property name="spacing">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
@@ -121,8 +148,8 @@
|
||||
<property name="text">
|
||||
<string>←</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="go-previous"/>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -137,29 +164,36 @@
|
||||
<property name="text">
|
||||
<string>→</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="go-next"/>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="openUrlButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Open URL</string>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QStackedWidget" name="webViewStack"/>
|
||||
<widget class="QStackedWidget" name="webViewStack">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>600</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
@@ -193,7 +227,6 @@
|
||||
<addaction name="actionCheckForUpdates"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionAboutZeal"/>
|
||||
<addaction name="actionAboutQt"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu_Edit">
|
||||
<property name="title">
|
||||
@@ -238,11 +271,6 @@
|
||||
<string>About &Zeal</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAboutQt">
|
||||
<property name="text">
|
||||
<string>About &Qt</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionBack">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
using namespace Zeal::WidgetUi;
|
||||
|
||||
ProgressItemDelegate::ProgressItemDelegate(QObject *parent) :
|
||||
QItemDelegate(parent)
|
||||
QStyledItemDelegate(parent)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ void ProgressItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
||||
const QModelIndex &index) const
|
||||
{
|
||||
if (!index.model()->data(index, ShowProgressRole).toBool()) {
|
||||
QItemDelegate::paint(painter, option, index);
|
||||
QStyledItemDelegate::paint(painter, option, index);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ void ProgressItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
||||
const int value = index.model()->data(index, ValueRole).toInt(&ok);
|
||||
|
||||
if (!ok) {
|
||||
QItemDelegate::paint(painter, option, index);
|
||||
QStyledItemDelegate::paint(painter, option, index);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -71,5 +71,5 @@ void ProgressItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
||||
|
||||
painter->restore();
|
||||
|
||||
QItemDelegate::paint(painter, styleOption, index);
|
||||
QStyledItemDelegate::paint(painter, styleOption, index);
|
||||
}
|
||||
|
||||
@@ -24,12 +24,12 @@
|
||||
#ifndef ZEAL_WIDGETUI_PROGRESSITEMDELEGATE_H
|
||||
#define ZEAL_WIDGETUI_PROGRESSITEMDELEGATE_H
|
||||
|
||||
#include <QItemDelegate>
|
||||
#include <QStyledItemDelegate>
|
||||
|
||||
namespace Zeal {
|
||||
namespace WidgetUi {
|
||||
|
||||
class ProgressItemDelegate : public QItemDelegate
|
||||
class ProgressItemDelegate : public QStyledItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
HEADERS += $$files($$PWD/*.h)
|
||||
SOURCES += $$PWD/qxtglobalshortcut.cpp
|
||||
|
||||
unix:!macx:SOURCES += $$PWD/qxtglobalshortcut_x11.cpp
|
||||
win32:SOURCES += $$PWD/qxtglobalshortcut_win.cpp
|
||||
macx:SOURCES += $$PWD/qxtglobalshortcut_mac.cpp
|
||||
@@ -34,10 +34,19 @@
|
||||
using namespace Zeal;
|
||||
using namespace Zeal::WidgetUi;
|
||||
|
||||
SettingsDialog::SettingsDialog(Core::Application *app, QWidget *parent) :
|
||||
namespace {
|
||||
// QFontDatabase::standardSizes() lacks some sizes, like 13, which QtWK uses by default.
|
||||
const int AvailableFontSizes[] = {9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
|
||||
20, 22, 24, 26, 28, 30, 32, 34, 36,
|
||||
40, 44, 48, 56, 64, 72};
|
||||
const QWebSettings::FontFamily BasicFontFamilies[] = {QWebSettings::SerifFont,
|
||||
QWebSettings::SansSerifFont,
|
||||
QWebSettings::FixedFont};
|
||||
}
|
||||
|
||||
SettingsDialog::SettingsDialog(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::SettingsDialog()),
|
||||
m_application(app)
|
||||
ui(new Ui::SettingsDialog())
|
||||
{
|
||||
ui->setupUi(this);
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
@@ -50,9 +59,67 @@ SettingsDialog::SettingsDialog(Core::Application *app, QWidget *parent) :
|
||||
saveSettings();
|
||||
});
|
||||
|
||||
connect(ui->minimumFontSizeSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
|
||||
this, [](int value) {
|
||||
QWebSettings::globalSettings()->setFontSize(QWebSettings::MinimumFontSize, value);
|
||||
// Fonts
|
||||
ui->defaultFontComboBox->addItem(tr("Serif"), QStringLiteral("serif"));
|
||||
ui->defaultFontComboBox->addItem(tr("Sans-serif"), QStringLiteral("sans-serif"));
|
||||
ui->defaultFontComboBox->addItem(tr("Monospace"), QStringLiteral("monospace"));
|
||||
|
||||
ui->minFontSizeComboBox->addItem(tr("None"), 0);
|
||||
for (int fontSize : AvailableFontSizes) {
|
||||
ui->fontSizeComboBox->addItem(QString::number(fontSize), fontSize);
|
||||
ui->fixedFontSizeComboBox->addItem(QString::number(fontSize), fontSize);
|
||||
ui->minFontSizeComboBox->addItem(QString::number(fontSize), fontSize);
|
||||
}
|
||||
|
||||
// Fix tab order.
|
||||
setTabOrder(ui->defaultFontComboBox, ui->fontSizeComboBox);
|
||||
setTabOrder(ui->fontSizeComboBox, ui->serifFontComboBox);
|
||||
|
||||
QWebSettings *webSettings = QWebSettings::globalSettings();
|
||||
|
||||
// Avoid casting in each connect.
|
||||
auto currentIndexChangedSignal
|
||||
= static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged);
|
||||
|
||||
auto syncStandardFont = [this, webSettings](QWebSettings::FontFamily fontFamily,
|
||||
const QFont &font) {
|
||||
const int index = ui->defaultFontComboBox->currentIndex();
|
||||
if (BasicFontFamilies[index] == fontFamily) {
|
||||
webSettings->setFontFamily(QWebSettings::StandardFont, font.family());
|
||||
}
|
||||
};
|
||||
|
||||
connect(ui->defaultFontComboBox, currentIndexChangedSignal,
|
||||
this, [webSettings](int index) {
|
||||
const QString fontFamily = webSettings->fontFamily(BasicFontFamilies[index]);
|
||||
webSettings->setFontFamily(QWebSettings::StandardFont, fontFamily);
|
||||
});
|
||||
|
||||
connect(ui->serifFontComboBox, &QFontComboBox::currentFontChanged,
|
||||
this, [webSettings, syncStandardFont](const QFont &font) {
|
||||
webSettings->setFontFamily(QWebSettings::SerifFont, font.family());
|
||||
syncStandardFont(QWebSettings::SerifFont, font);
|
||||
});
|
||||
connect(ui->sansSerifFontComboBox, &QFontComboBox::currentFontChanged,
|
||||
this, [webSettings, syncStandardFont](const QFont &font) {
|
||||
webSettings->setFontFamily(QWebSettings::SansSerifFont, font.family());
|
||||
syncStandardFont(QWebSettings::SansSerifFont, font);
|
||||
});
|
||||
connect(ui->fixedFontComboBox, &QFontComboBox::currentFontChanged,
|
||||
this, [webSettings, syncStandardFont](const QFont &font) {
|
||||
webSettings->setFontFamily(QWebSettings::FixedFont, font.family());
|
||||
syncStandardFont(QWebSettings::FixedFont, font);
|
||||
});
|
||||
|
||||
connect(ui->fontSizeComboBox, currentIndexChangedSignal, this, [webSettings](int index) {
|
||||
webSettings->setFontSize(QWebSettings::DefaultFontSize, AvailableFontSizes[index]);
|
||||
});
|
||||
connect(ui->fixedFontSizeComboBox, currentIndexChangedSignal, this, [webSettings](int index) {
|
||||
webSettings->setFontSize(QWebSettings::DefaultFixedFontSize, AvailableFontSizes[index]);
|
||||
});
|
||||
connect(ui->minFontSizeComboBox, currentIndexChangedSignal, this, [webSettings](int index) {
|
||||
const int fontSize = index == 0 ? 0 : AvailableFontSizes[index-1];
|
||||
webSettings->setFontSize(QWebSettings::MinimumFontSize, fontSize);
|
||||
});
|
||||
|
||||
loadSettings();
|
||||
@@ -76,17 +143,27 @@ 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()
|
||||
{
|
||||
const Core::Settings * const settings = m_application->settings();
|
||||
const Core::Settings * const settings = Core::Application::instance()->settings();
|
||||
|
||||
// General Tab
|
||||
ui->startMinimizedCheckBox->setChecked(settings->startMinimized);
|
||||
ui->checkForUpdateCheckBox->setChecked(settings->checkForUpdate);
|
||||
@@ -106,10 +183,37 @@ void SettingsDialog::loadSettings()
|
||||
ui->fuzzySearchCheckBox->setChecked(settings->fuzzySearchEnabled);
|
||||
|
||||
// Content Tab
|
||||
ui->minimumFontSizeSpinBox->setValue(settings->minimumFontSize);
|
||||
for (int i = 0; i < ui->defaultFontComboBox->count(); ++i) {
|
||||
if (ui->defaultFontComboBox->itemData(i).toString() == settings->defaultFontFamily) {
|
||||
ui->defaultFontComboBox->setCurrentIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ui->serifFontComboBox->setCurrentText(settings->serifFontFamily);
|
||||
ui->sansSerifFontComboBox->setCurrentText(settings->sansSerifFontFamily);
|
||||
ui->fixedFontComboBox->setCurrentText(settings->fixedFontFamily);
|
||||
|
||||
ui->fontSizeComboBox->setCurrentText(QString::number(settings->defaultFontSize));
|
||||
ui->fixedFontSizeComboBox->setCurrentText(QString::number(settings->defaultFixedFontSize));
|
||||
ui->minFontSizeComboBox->setCurrentText(QString::number(settings->minimumFontSize));
|
||||
|
||||
ui->darkModeCheckBox->setChecked(settings->darkModeEnabled);
|
||||
ui->highlightOnNavigateCheckBox->setChecked(settings->highlightOnNavigateEnabled);
|
||||
ui->customCssFileEdit->setText(QDir::toNativeSeparators(settings->customCssFile));
|
||||
|
||||
switch (settings->externalLinkPolicy) {
|
||||
case Core::Settings::ExternalLinkPolicy::Ask:
|
||||
ui->radioExternalLinkAsk->setChecked(true);
|
||||
break;
|
||||
case Core::Settings::ExternalLinkPolicy::Open:
|
||||
ui->radioExternalLinkOpen->setChecked(true);
|
||||
break;
|
||||
case Core::Settings::ExternalLinkPolicy::OpenInSystemBrowser:
|
||||
ui->radioExternalLinkOpenDesktop->setChecked(true);
|
||||
break;
|
||||
}
|
||||
|
||||
ui->useSmoothScrollingCheckBox->setChecked(settings->isSmoothScrollingEnabled);
|
||||
ui->disableAdCheckBox->setChecked(settings->isAdDisabled);
|
||||
|
||||
// Network Tab
|
||||
@@ -134,7 +238,7 @@ void SettingsDialog::loadSettings()
|
||||
|
||||
void SettingsDialog::saveSettings()
|
||||
{
|
||||
Core::Settings * const settings = m_application->settings();
|
||||
Core::Settings * const settings = Core::Application::instance()->settings();
|
||||
|
||||
// General Tab
|
||||
settings->startMinimized = ui->startMinimizedCheckBox->isChecked();
|
||||
@@ -155,10 +259,28 @@ void SettingsDialog::saveSettings()
|
||||
settings->fuzzySearchEnabled = ui->fuzzySearchCheckBox->isChecked();
|
||||
|
||||
// Content Tab
|
||||
settings->minimumFontSize = ui->minimumFontSizeSpinBox->text().toInt();
|
||||
settings->defaultFontFamily = ui->defaultFontComboBox->currentData().toString();
|
||||
settings->serifFontFamily = ui->serifFontComboBox->currentText();
|
||||
settings->sansSerifFontFamily = ui->sansSerifFontComboBox->currentText();
|
||||
settings->fixedFontFamily = ui->fixedFontComboBox->currentText();
|
||||
|
||||
settings->defaultFontSize = ui->fontSizeComboBox->currentData().toInt();
|
||||
settings->defaultFixedFontSize = ui->fixedFontSizeComboBox->currentData().toInt();
|
||||
settings->minimumFontSize = ui->minFontSizeComboBox->currentData().toInt();
|
||||
|
||||
settings->darkModeEnabled = ui->darkModeCheckBox->isChecked();
|
||||
settings->highlightOnNavigateEnabled = ui->highlightOnNavigateCheckBox->isChecked();
|
||||
settings->customCssFile = QDir::fromNativeSeparators(ui->customCssFileEdit->text());
|
||||
|
||||
if (ui->radioExternalLinkAsk->isChecked()) {
|
||||
settings->externalLinkPolicy = Core::Settings::ExternalLinkPolicy::Ask;
|
||||
} else if (ui->radioExternalLinkOpen->isChecked()) {
|
||||
settings->externalLinkPolicy = Core::Settings::ExternalLinkPolicy::Open;
|
||||
} else if (ui->radioExternalLinkOpenDesktop->isChecked()) {
|
||||
settings->externalLinkPolicy = Core::Settings::ExternalLinkPolicy::OpenInSystemBrowser;
|
||||
}
|
||||
|
||||
settings->isSmoothScrollingEnabled = ui->useSmoothScrollingCheckBox->isChecked();
|
||||
settings->isAdDisabled = ui->disableAdCheckBox->isChecked();
|
||||
|
||||
// Network Tab
|
||||
|
||||
@@ -27,11 +27,6 @@
|
||||
#include <QDialog>
|
||||
|
||||
namespace Zeal {
|
||||
|
||||
namespace Core {
|
||||
class Application;
|
||||
} // namespace Core
|
||||
|
||||
namespace WidgetUi {
|
||||
|
||||
namespace Ui {
|
||||
@@ -42,7 +37,7 @@ class SettingsDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SettingsDialog(Core::Application *app, QWidget *parent = nullptr);
|
||||
explicit SettingsDialog(QWidget *parent = nullptr);
|
||||
~SettingsDialog() override;
|
||||
|
||||
private slots:
|
||||
@@ -50,11 +45,11 @@ private slots:
|
||||
void chooseDocsetStoragePath();
|
||||
|
||||
private:
|
||||
Ui::SettingsDialog *ui = nullptr;
|
||||
Core::Application *m_application = nullptr;
|
||||
|
||||
void loadSettings();
|
||||
void saveSettings();
|
||||
|
||||
private:
|
||||
Ui::SettingsDialog *ui = nullptr;
|
||||
};
|
||||
|
||||
} // namespace WidgetUi
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>600</width>
|
||||
<height>500</height>
|
||||
<height>625</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -173,8 +173,8 @@
|
||||
<property name="title">
|
||||
<string>Behavior</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_12">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="openNewTabAfterActive">
|
||||
<property name="text">
|
||||
<string>Open new tab after active</string>
|
||||
@@ -184,6 +184,19 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="searchTab">
|
||||
@@ -232,19 +245,162 @@
|
||||
<property name="title">
|
||||
<string>Fonts</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout_4">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>&Minimum size:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>minimumFontSizeSpinBox</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_14">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3" stretch="4,0">
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetMinimumSize</enum>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>De&fault:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>defaultFontComboBox</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="defaultFontComboBox"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>&Serif:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>serifFontComboBox</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QFontComboBox" name="serifFontComboBox">
|
||||
<property name="fontFilters">
|
||||
<set>QFontComboBox::ScalableFonts</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Sa&ns-serif:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>sansSerifFontComboBox</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QFontComboBox" name="sansSerifFontComboBox">
|
||||
<property name="fontFilters">
|
||||
<set>QFontComboBox::ScalableFonts</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>&Monospace:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>fixedFontComboBox</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QFontComboBox" name="fixedFontComboBox">
|
||||
<property name="fontFilters">
|
||||
<set>QFontComboBox::MonospacedFonts|QFontComboBox::ScalableFonts</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_16">
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout_6">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>Si&ze:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>fontSizeComboBox</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="fontSizeComboBox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_6">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout_4">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_11">
|
||||
<property name="text">
|
||||
<string>Siz&e:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>fixedFontSizeComboBox</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="fixedFontSizeComboBox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QSpinBox" name="minimumFontSizeSpinBox"/>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Minimum f&ont size:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>minFontSizeComboBox</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="minFontSizeComboBox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
@@ -258,14 +414,14 @@
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="darkModeCheckBox">
|
||||
<property name="text">
|
||||
<string>&Dark Mode</string>
|
||||
<string>&Dark mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="highlightOnNavigateCheckBox">
|
||||
<property name="text">
|
||||
<string>&Highlight on Navigate</string>
|
||||
<string>&Highlight on navigate</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -274,7 +430,7 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="customCssFileLabel">
|
||||
<property name="text">
|
||||
<string>&Custom CSS File:</string>
|
||||
<string>&Custom CSS file:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>customCssFileEdit</cstring>
|
||||
@@ -291,7 +447,7 @@
|
||||
<item>
|
||||
<widget class="QPushButton" name="customCssFileBrowseButton">
|
||||
<property name="text">
|
||||
<string>&Browse...</string>
|
||||
<string>Bro&wse...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -300,16 +456,56 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_7">
|
||||
<property name="title">
|
||||
<string>External Link Behavior</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_13">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioExternalLinkAsk">
|
||||
<property name="text">
|
||||
<string>&Ask everytime</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioExternalLinkOpenDesktop">
|
||||
<property name="text">
|
||||
<string>&Open in desktop browser</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioExternalLinkOpen">
|
||||
<property name="text">
|
||||
<string>O&pen in Zeal</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="title">
|
||||
<string>Other</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="useSmoothScrollingCheckBox">
|
||||
<property name="text">
|
||||
<string>Use smoo&th scrolling</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="disableAdCheckBox">
|
||||
<property name="text">
|
||||
<string>Disable advertisement on the welcome page</string>
|
||||
<string>Disable ad&vertisement on the welcome page</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
ZEAL_LIB_NAME = Ui
|
||||
|
||||
QT += webkitwidgets
|
||||
|
||||
# QxtGlobalShortcut dependencies
|
||||
unix:!macx {
|
||||
QT += x11extras
|
||||
|
||||
CONFIG += link_pkgconfig
|
||||
PKGCONFIG += x11 xcb xcb-keysyms
|
||||
}
|
||||
|
||||
macx {
|
||||
LIBS += -framework Carbon
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
include($$ZEAL_LIBRARY_PRI)
|
||||
|
||||
HEADERS += \
|
||||
$$files(*.h) \
|
||||
$$files(widgets/*.h)
|
||||
|
||||
SOURCES += \
|
||||
$$files(*.cpp) \
|
||||
$$files(widgets/*.cpp)
|
||||
|
||||
FORMS += \
|
||||
$$files(*.ui)
|
||||
|
||||
include(qxtglobalshortcut/qxtglobalshortcut.pri)
|
||||
@@ -0,0 +1,49 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2018 Oleg Shparber
|
||||
** Contact: https://go.zealdocs.org/l/contact
|
||||
**
|
||||
** This file is part of Zeal.
|
||||
**
|
||||
** Zeal is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** Zeal is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with Zeal. If not, see <https://www.gnu.org/licenses/>.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "webbridge.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDesktopServices>
|
||||
#include <QUrl>
|
||||
|
||||
using namespace Zeal::WidgetUi;
|
||||
|
||||
WebBridge::WebBridge(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void WebBridge::openShortUrl(const QString &key)
|
||||
{
|
||||
QDesktopServices::openUrl(QUrl(QStringLiteral("https://go.zealdocs.org/l/") + key));
|
||||
}
|
||||
|
||||
void WebBridge::triggerAction(const QString &action)
|
||||
{
|
||||
emit actionTriggered(action);
|
||||
}
|
||||
|
||||
QString WebBridge::appVersion() const
|
||||
{
|
||||
return QCoreApplication::applicationVersion();
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2018 Oleg Shparber
|
||||
** Contact: https://go.zealdocs.org/l/contact
|
||||
**
|
||||
** This file is part of Zeal.
|
||||
**
|
||||
** Zeal is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** Zeal is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with Zeal. If not, see <https://www.gnu.org/licenses/>.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef ZEAL_WIDGETUI_WEBBRIDGE_H
|
||||
#define ZEAL_WIDGETUI_WEBBRIDGE_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace Zeal {
|
||||
namespace WidgetUi {
|
||||
|
||||
class WebBridge : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString AppVersion READ appVersion CONSTANT)
|
||||
public:
|
||||
explicit WebBridge(QObject *parent = nullptr);
|
||||
|
||||
signals:
|
||||
void actionTriggered(const QString &action);
|
||||
|
||||
public slots:
|
||||
Q_INVOKABLE void openShortUrl(const QString &key);
|
||||
Q_INVOKABLE void triggerAction(const QString &action);
|
||||
|
||||
private:
|
||||
QString appVersion() const;
|
||||
};
|
||||
|
||||
} // namespace WidgetUi
|
||||
} // namespace Zeal
|
||||
|
||||
#endif // ZEAL_WIDGETUI_WEBBRIDGE_H
|
||||
@@ -0,0 +1,229 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2018 Oleg Shparber
|
||||
** Contact: https://go.zealdocs.org/l/contact
|
||||
**
|
||||
** This file is part of Zeal.
|
||||
**
|
||||
** Zeal is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** Zeal is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with Zeal. If not, see <https://www.gnu.org/licenses/>.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "searchtoolbar.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QApplication>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLineEdit>
|
||||
#include <QKeyEvent>
|
||||
#include <QStyle>
|
||||
#include <QToolButton>
|
||||
#include <QWebPage>
|
||||
#include <QWebView>
|
||||
|
||||
using namespace Zeal::WidgetUi;
|
||||
|
||||
SearchToolBar::SearchToolBar(QWebView *webView, QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, m_webView(webView)
|
||||
{
|
||||
QHBoxLayout *layout = new QHBoxLayout(this);
|
||||
layout->setContentsMargins(4, 4, 4, 4);
|
||||
layout->setSpacing(4);
|
||||
|
||||
m_lineEdit = new QLineEdit();
|
||||
m_lineEdit->installEventFilter(this);
|
||||
m_lineEdit->setPlaceholderText(tr("Find in page"));
|
||||
m_lineEdit->setMaximumWidth(200);
|
||||
connect(m_lineEdit, &QLineEdit::textChanged, this, &SearchToolBar::findNext);
|
||||
connect(m_lineEdit, &QLineEdit::textChanged, this, &SearchToolBar::updateHighlight);
|
||||
layout->addWidget(m_lineEdit);
|
||||
|
||||
m_findPreviousButton = new QToolButton();
|
||||
m_findPreviousButton->setAutoRaise(true);
|
||||
m_findPreviousButton->setIcon(qApp->style()->standardIcon(QStyle::SP_ArrowBack));
|
||||
m_findPreviousButton->setToolTip(tr("Previous result"));
|
||||
connect(m_findPreviousButton, &QToolButton::clicked, this, &SearchToolBar::findPrevious);
|
||||
layout->addWidget(m_findPreviousButton);
|
||||
|
||||
// A workaround for QAbstractButton lacking support for multiple shortcuts.
|
||||
QAction *action = new QAction(m_findPreviousButton);
|
||||
action->setShortcuts(QKeySequence::FindPrevious);
|
||||
// TODO: Investigate why direct connection does not work.
|
||||
//connect(action, &QAction::triggered, m_findPreviousButton, &QToolButton::animateClick);
|
||||
connect(action, &QAction::triggered, this, [this]() { m_findPreviousButton->animateClick(); });
|
||||
addAction(action);
|
||||
|
||||
m_findNextButton = new QToolButton();
|
||||
m_findNextButton->setAutoRaise(true);
|
||||
m_findNextButton->setIcon(qApp->style()->standardIcon(QStyle::SP_ArrowForward));
|
||||
m_findNextButton->setToolTip(tr("Next result"));
|
||||
connect(m_findNextButton, &QToolButton::clicked, this, &SearchToolBar::findNext);
|
||||
layout->addWidget(m_findNextButton);
|
||||
|
||||
action = new QAction(m_findNextButton);
|
||||
action->setShortcuts(QKeySequence::FindNext);
|
||||
connect(action, &QAction::triggered, this, [this]() { m_findNextButton->animateClick(); });
|
||||
addAction(action);
|
||||
|
||||
m_highlightAllButton = new QToolButton();
|
||||
m_highlightAllButton->setAutoRaise(true);
|
||||
m_highlightAllButton->setCheckable(true);
|
||||
m_highlightAllButton->setText(tr("High&light All"));
|
||||
connect(m_highlightAllButton, &QToolButton::toggled, this, &SearchToolBar::updateHighlight);
|
||||
layout->addWidget(m_highlightAllButton);
|
||||
|
||||
m_matchCaseButton = new QToolButton();
|
||||
m_matchCaseButton->setAutoRaise(true);
|
||||
m_matchCaseButton->setCheckable(true);
|
||||
m_matchCaseButton->setText(tr("Mat&ch Case"));
|
||||
connect(m_matchCaseButton, &QToolButton::toggled, this, &SearchToolBar::updateHighlight);
|
||||
layout->addWidget(m_matchCaseButton);
|
||||
|
||||
layout->addStretch();
|
||||
|
||||
QToolButton *closeButton = new QToolButton();
|
||||
closeButton->setAutoRaise(true);
|
||||
closeButton->setIcon(qApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
|
||||
closeButton->setToolTip(tr("Close find bar"));
|
||||
connect(closeButton, &QToolButton::clicked, this, &QWidget::hide);
|
||||
layout->addWidget(closeButton);
|
||||
|
||||
setLayout(layout);
|
||||
|
||||
setMaximumHeight(sizeHint().height());
|
||||
setMinimumWidth(sizeHint().width());
|
||||
}
|
||||
|
||||
void SearchToolBar::setText(const QString &text)
|
||||
{
|
||||
m_lineEdit->setText(text);
|
||||
}
|
||||
|
||||
void SearchToolBar::activate()
|
||||
{
|
||||
show();
|
||||
|
||||
m_lineEdit->selectAll();
|
||||
m_lineEdit->setFocus();
|
||||
}
|
||||
|
||||
bool SearchToolBar::eventFilter(QObject *object, QEvent *event)
|
||||
{
|
||||
if (object == m_lineEdit && event->type() == QEvent::KeyPress) {
|
||||
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
|
||||
|
||||
switch (keyEvent->key()) {
|
||||
case Qt::Key_Enter:
|
||||
case Qt::Key_Return:
|
||||
if (keyEvent->modifiers().testFlag(Qt::ShiftModifier)) {
|
||||
findPrevious();
|
||||
} else {
|
||||
findNext();
|
||||
}
|
||||
return true;
|
||||
case Qt::Key_Down:
|
||||
case Qt::Key_Up:
|
||||
case Qt::Key_PageDown:
|
||||
case Qt::Key_PageUp:
|
||||
QCoreApplication::sendEvent(m_webView, event);
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return QWidget::eventFilter(object, event);
|
||||
}
|
||||
|
||||
void SearchToolBar::hideEvent(QHideEvent *event)
|
||||
{
|
||||
hideHighlight();
|
||||
m_webView->setFocus();
|
||||
QWidget::hideEvent(event);
|
||||
}
|
||||
|
||||
void SearchToolBar::showEvent(QShowEvent *event)
|
||||
{
|
||||
activate();
|
||||
|
||||
QWidget::showEvent(event);
|
||||
}
|
||||
|
||||
void SearchToolBar::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
if (event->key() == Qt::Key_Escape) {
|
||||
hide();
|
||||
}
|
||||
}
|
||||
|
||||
void SearchToolBar::findNext()
|
||||
{
|
||||
if (!isVisible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QWebPage::FindFlags ff = QWebPage::FindWrapsAroundDocument;
|
||||
#if QT_VERSION >= 0x050700
|
||||
ff.setFlag(QWebPage::FindCaseSensitively, m_matchCaseButton->isChecked());
|
||||
#else
|
||||
if (m_matchCaseButton->isChecked()) {
|
||||
ff |= QWebPage::FindCaseSensitively;
|
||||
}
|
||||
#endif
|
||||
m_webView->findText(m_lineEdit->text(), ff);
|
||||
}
|
||||
|
||||
void SearchToolBar::findPrevious()
|
||||
{
|
||||
if (!isVisible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QWebPage::FindFlags ff = QWebPage::FindWrapsAroundDocument;
|
||||
#if QT_VERSION >= 0x050700
|
||||
ff.setFlag(QWebPage::FindCaseSensitively, m_matchCaseButton->isChecked());
|
||||
ff.setFlag(QWebPage::FindBackward);
|
||||
#else
|
||||
if (m_matchCaseButton->isChecked()) {
|
||||
ff |= QWebPage::FindCaseSensitively;
|
||||
}
|
||||
|
||||
ff |= QWebPage::FindBackward;
|
||||
#endif
|
||||
m_webView->findText(m_lineEdit->text(), ff);
|
||||
}
|
||||
|
||||
void SearchToolBar::hideHighlight()
|
||||
{
|
||||
m_webView->findText(QString(), QWebPage::HighlightAllOccurrences);
|
||||
}
|
||||
|
||||
void SearchToolBar::updateHighlight()
|
||||
{
|
||||
hideHighlight();
|
||||
|
||||
if (m_highlightAllButton->isChecked()) {
|
||||
QWebPage::FindFlags ff = QWebPage::HighlightAllOccurrences;
|
||||
#if QT_VERSION >= 0x050700
|
||||
ff.setFlag(QWebPage::FindCaseSensitively, m_matchCaseButton->isChecked());
|
||||
#else
|
||||
if (m_matchCaseButton->isChecked()) {
|
||||
ff |= QWebPage::FindCaseSensitively;
|
||||
}
|
||||
#endif
|
||||
m_webView->findText(m_lineEdit->text(), ff);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2018 Oleg Shparber
|
||||
** Contact: https://go.zealdocs.org/l/contact
|
||||
**
|
||||
** This file is part of Zeal.
|
||||
**
|
||||
** Zeal is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** Zeal is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with Zeal. If not, see <https://www.gnu.org/licenses/>.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef ZEAL_WIDGETUI_SEARCHTOOLBAR_H
|
||||
#define ZEAL_WIDGETUI_SEARCHTOOLBAR_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class QLineEdit;
|
||||
class QToolButton;
|
||||
class QWebView;
|
||||
|
||||
namespace Zeal {
|
||||
namespace WidgetUi {
|
||||
|
||||
class SearchToolBar : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SearchToolBar(QWebView *webView, QWidget *parent = nullptr);
|
||||
|
||||
void setText(const QString &text);
|
||||
void activate();
|
||||
|
||||
bool eventFilter(QObject *object, QEvent *event) override;
|
||||
|
||||
protected:
|
||||
void hideEvent(QHideEvent *event) override;
|
||||
void showEvent(QShowEvent *event) override;
|
||||
|
||||
void keyPressEvent(QKeyEvent *event) override;
|
||||
|
||||
private:
|
||||
void findNext();
|
||||
void findPrevious();
|
||||
|
||||
void hideHighlight();
|
||||
void updateHighlight();
|
||||
|
||||
QLineEdit *m_lineEdit = nullptr;
|
||||
QToolButton *m_findNextButton = nullptr;
|
||||
QToolButton *m_findPreviousButton = nullptr;
|
||||
QToolButton *m_highlightAllButton = nullptr;
|
||||
QToolButton *m_matchCaseButton = nullptr;
|
||||
|
||||
QWebView *m_webView = nullptr;
|
||||
};
|
||||
|
||||
} // namespace WidgetUi
|
||||
} // namespace Zeal
|
||||
|
||||
#endif // ZEAL_WIDGETUI_SEARCHTOOLBAR_H
|
||||
@@ -23,21 +23,24 @@
|
||||
|
||||
#include "webview.h"
|
||||
|
||||
#include "webviewtab.h"
|
||||
#include "../mainwindow.h"
|
||||
|
||||
#include <core/application.h>
|
||||
#include <core/settings.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QWebFrame>
|
||||
#include <QCheckBox>
|
||||
#include <QDesktopServices>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QWheelEvent>
|
||||
|
||||
using namespace Zeal::WidgetUi;
|
||||
|
||||
WebView::WebView(QWidget *parent) :
|
||||
QWebView(parent)
|
||||
WebView::WebView(QWidget *parent)
|
||||
: QWebView(parent)
|
||||
{
|
||||
setAttribute(Qt::WA_AcceptTouchEvents, false);
|
||||
page()->setLinkDelegationPolicy(QWebPage::DelegateExternalLinks);
|
||||
page()->setNetworkAccessManager(Core::Application::instance()->networkManager());
|
||||
}
|
||||
|
||||
@@ -69,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;
|
||||
@@ -93,10 +96,81 @@ void WebView::resetZoom()
|
||||
QWebView *WebView::createWindow(QWebPage::WebWindowType type)
|
||||
{
|
||||
Q_UNUSED(type)
|
||||
return Core::Application::instance()->mainWindow()->createTab()->m_webView;
|
||||
}
|
||||
|
||||
MainWindow *mw = qobject_cast<MainWindow *>(qApp->activeWindow());
|
||||
mw->createTab();
|
||||
return this;
|
||||
void WebView::contextMenuEvent(QContextMenuEvent *event)
|
||||
{
|
||||
#if QT_VERSION >= 0x050600
|
||||
const QWebHitTestResult hitTestResult = hitTestContent(event->pos());
|
||||
|
||||
// Return standard menu for input fields.
|
||||
if (hitTestResult.isContentEditable()) {
|
||||
QWebView::contextMenuEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
event->accept();
|
||||
|
||||
if (m_contextMenu) {
|
||||
m_contextMenu->deleteLater();
|
||||
}
|
||||
|
||||
QMenu *m_contextMenu = new QMenu(this);
|
||||
|
||||
const QUrl linkUrl = hitTestResult.linkUrl();
|
||||
const QString linkText = hitTestResult.linkText();
|
||||
|
||||
if (linkUrl.isValid()) {
|
||||
const QString scheme = linkUrl.scheme();
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
if (hitTestResult.isContentSelected()) {
|
||||
if (!m_contextMenu->isEmpty()) {
|
||||
m_contextMenu->addSeparator();
|
||||
}
|
||||
|
||||
m_contextMenu->addAction(pageAction(QWebPage::Copy));
|
||||
}
|
||||
|
||||
if (!linkUrl.isValid() && url().scheme() != QLatin1String("qrc")) {
|
||||
if (!m_contextMenu->isEmpty()) {
|
||||
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());
|
||||
});
|
||||
}
|
||||
|
||||
if (m_contextMenu->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_contextMenu->popup(event->globalPos());
|
||||
#else
|
||||
QWebView::contextMenuEvent(event);
|
||||
#endif
|
||||
}
|
||||
|
||||
void WebView::mousePressEvent(QMouseEvent *event)
|
||||
@@ -105,19 +179,20 @@ void WebView::mousePressEvent(QMouseEvent *event)
|
||||
case Qt::BackButton:
|
||||
back();
|
||||
event->accept();
|
||||
break;
|
||||
return;
|
||||
case Qt::ForwardButton:
|
||||
forward();
|
||||
event->accept();
|
||||
break;
|
||||
return;
|
||||
case Qt::LeftButton:
|
||||
if (!(event->modifiers() & Qt::ControlModifier || event->modifiers() & Qt::ShiftModifier))
|
||||
break;
|
||||
case Qt::MiddleButton:
|
||||
m_clickedLink = clickedLink(event->pos());
|
||||
if (m_clickedLink.isValid())
|
||||
event->accept();
|
||||
break;
|
||||
m_clickedLink = hitTestContent(event->pos()).linkUrl();
|
||||
if (!m_clickedLink.isValid() || m_clickedLink.scheme() == QLatin1String("javascript")) {
|
||||
break;
|
||||
}
|
||||
|
||||
event->accept();
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -127,20 +202,83 @@ void WebView::mousePressEvent(QMouseEvent *event)
|
||||
|
||||
void WebView::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() != Qt::LeftButton && event->button() != Qt::MiddleButton) {
|
||||
QWebView::mouseReleaseEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
const QUrl clickedLink = hitTestContent(event->pos()).linkUrl();
|
||||
if (!clickedLink.isValid() || clickedLink != m_clickedLink
|
||||
|| clickedLink.scheme() == QLatin1String("javascript")) {
|
||||
QWebView::mouseReleaseEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isUrlExternal(clickedLink)) {
|
||||
switch (Core::Application::instance()->settings()->externalLinkPolicy) {
|
||||
case Core::Settings::ExternalLinkPolicy::Open:
|
||||
break;
|
||||
case Core::Settings::ExternalLinkPolicy::Ask: {
|
||||
QScopedPointer<QMessageBox> mb(new QMessageBox());
|
||||
mb->setIcon(QMessageBox::Question);
|
||||
mb->setText(tr("How do you want to open the external link?<br>URL: <b>%1</b>")
|
||||
.arg(clickedLink.toString()));
|
||||
|
||||
|
||||
QCheckBox *checkBox = new QCheckBox("Do ¬ ask again");
|
||||
mb->setCheckBox(checkBox);
|
||||
|
||||
QPushButton *openInBrowserButton = mb->addButton(tr("Open in &Desktop Browser"),
|
||||
QMessageBox::ActionRole);
|
||||
QPushButton *openInZealButton = mb->addButton(tr("Open in &Zeal"),
|
||||
QMessageBox::ActionRole);
|
||||
mb->addButton(QMessageBox::Cancel);
|
||||
|
||||
mb->setDefaultButton(openInBrowserButton);
|
||||
|
||||
if (mb->exec() == QMessageBox::Cancel) {
|
||||
event->accept();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mb->clickedButton() == openInZealButton) {
|
||||
if (checkBox->isChecked()) {
|
||||
Core::Application::instance()->settings()->externalLinkPolicy
|
||||
= Core::Settings::ExternalLinkPolicy::Open;
|
||||
Core::Application::instance()->settings()->save();
|
||||
}
|
||||
|
||||
break;
|
||||
} else if (mb->clickedButton() == openInBrowserButton) {
|
||||
if (checkBox->isChecked()) {
|
||||
Core::Application::instance()->settings()->externalLinkPolicy
|
||||
= Core::Settings::ExternalLinkPolicy::OpenInSystemBrowser;
|
||||
Core::Application::instance()->settings()->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
case Core::Settings::ExternalLinkPolicy::OpenInSystemBrowser:
|
||||
QDesktopServices::openUrl(clickedLink);
|
||||
event->accept();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (event->button()) {
|
||||
case Qt::LeftButton:
|
||||
if (!(event->modifiers() & Qt::ControlModifier || event->modifiers() & Qt::ShiftModifier))
|
||||
break;
|
||||
case Qt::MiddleButton:
|
||||
if (m_clickedLink == clickedLink(event->pos()) && m_clickedLink.isValid()) {
|
||||
QWebView *webView = createWindow(QWebPage::WebBrowserWindow);
|
||||
webView->load(m_clickedLink);
|
||||
if (!(event->modifiers() & Qt::ControlModifier || event->modifiers() & Qt::ShiftModifier)) {
|
||||
load(clickedLink);
|
||||
event->accept();
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case Qt::MiddleButton:
|
||||
createWindow(QWebPage::WebBrowserWindow)->load(clickedLink);
|
||||
event->accept();
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
QWebView::mouseReleaseEvent(event);
|
||||
}
|
||||
|
||||
@@ -165,11 +303,18 @@ void WebView::wheelEvent(QWheelEvent *event)
|
||||
QWebView::wheelEvent(event);
|
||||
}
|
||||
|
||||
QUrl WebView::clickedLink(const QPoint &pos) const
|
||||
QWebHitTestResult WebView::hitTestContent(const QPoint &pos) const
|
||||
{
|
||||
QWebFrame *frame = page()->frameAt(pos);
|
||||
if (!frame)
|
||||
return QUrl();
|
||||
|
||||
return frame->hitTestContent(pos).linkUrl();
|
||||
return page()->mainFrame()->hitTestContent(pos);
|
||||
}
|
||||
|
||||
bool WebView::isUrlExternal(const QUrl &url)
|
||||
{
|
||||
static const QStringList localSchemes = {
|
||||
QStringLiteral("file"),
|
||||
QStringLiteral("qrc"),
|
||||
};
|
||||
|
||||
const QString scheme = url.scheme();
|
||||
return !scheme.isEmpty() && !localSchemes.contains(scheme);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#define ZEAL_WIDGETUI_WEBVIEW_H
|
||||
|
||||
#include <QVector>
|
||||
#include <QWebFrame>
|
||||
#include <QWebView>
|
||||
|
||||
namespace Zeal {
|
||||
@@ -40,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();
|
||||
@@ -52,13 +53,17 @@ signals:
|
||||
|
||||
protected:
|
||||
QWebView *createWindow(QWebPage::WebWindowType type) override;
|
||||
void contextMenuEvent(QContextMenuEvent *event) override;
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
void wheelEvent(QWheelEvent *event) override;
|
||||
|
||||
private:
|
||||
QUrl clickedLink(const QPoint &pos) const;
|
||||
QWebHitTestResult hitTestContent(const QPoint &pos) const;
|
||||
|
||||
static bool isUrlExternal(const QUrl &url);
|
||||
|
||||
QMenu *m_contextMenu = nullptr;
|
||||
QUrl m_clickedLink;
|
||||
int m_zoomLevel = defaultZoomLevel();
|
||||
};
|
||||
|
||||
@@ -23,12 +23,12 @@
|
||||
|
||||
#include "webviewtab.h"
|
||||
|
||||
#include "searchtoolbar.h"
|
||||
#include "webview.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QLineEdit>
|
||||
#include <QKeyEvent>
|
||||
#include <QStyle>
|
||||
#include <QResizeEvent>
|
||||
#include <QWebFrame>
|
||||
#include <QWebHistory>
|
||||
#include <QWebPage>
|
||||
@@ -36,24 +36,14 @@
|
||||
|
||||
using namespace Zeal::WidgetUi;
|
||||
|
||||
WebViewTab::WebViewTab(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
m_searchLineEdit(new QLineEdit(this))
|
||||
WebViewTab::WebViewTab(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->setSpacing(0);
|
||||
|
||||
m_searchLineEdit->hide();
|
||||
m_searchLineEdit->installEventFilter(this);
|
||||
connect(m_searchLineEdit, &QLineEdit::textChanged, this, &WebViewTab::find);
|
||||
|
||||
m_webView = new WebView();
|
||||
connect(m_webView, &QWebView::loadFinished, this, [this](bool ok) {
|
||||
Q_UNUSED(ok)
|
||||
moveLineEdit();
|
||||
});
|
||||
|
||||
connect(m_webView->page(), &QWebPage::linkHovered, [this](const QString &link) {
|
||||
if (link.startsWith(QLatin1String("file:")) || link.startsWith(QLatin1String("qrc:")))
|
||||
return;
|
||||
@@ -61,7 +51,6 @@ WebViewTab::WebViewTab(QWidget *parent) :
|
||||
setToolTip(link);
|
||||
});
|
||||
|
||||
connect(m_webView, &QWebView::linkClicked, this, &WebViewTab::linkClicked);
|
||||
connect(m_webView, &QWebView::titleChanged, this, &WebViewTab::titleChanged);
|
||||
connect(m_webView, &QWebView::urlChanged, this, &WebViewTab::urlChanged);
|
||||
|
||||
@@ -81,30 +70,12 @@ void WebViewTab::setZoomLevel(int level)
|
||||
m_webView->setZoomLevel(level);
|
||||
}
|
||||
|
||||
bool WebViewTab::eventFilter(QObject *object, QEvent *event)
|
||||
void WebViewTab::setWebBridgeObject(const QString &name, QObject *object)
|
||||
{
|
||||
if (object == m_searchLineEdit && event->type() == QEvent::KeyPress) {
|
||||
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
|
||||
switch (keyEvent->key()) {
|
||||
case Qt::Key_Escape:
|
||||
hideSearchBar();
|
||||
return true;
|
||||
case Qt::Key_Enter:
|
||||
case Qt::Key_Return:
|
||||
findNext(m_searchLineEdit->text(), keyEvent->modifiers() & Qt::ShiftModifier);
|
||||
return true;
|
||||
case Qt::Key_Down:
|
||||
case Qt::Key_Up:
|
||||
case Qt::Key_PageDown:
|
||||
case Qt::Key_PageUp:
|
||||
QCoreApplication::sendEvent(m_webView, event);
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return QWidget::eventFilter(object, event);
|
||||
connect(m_webView->page()->mainFrame(), &QWebFrame::javaScriptWindowObjectCleared,
|
||||
this, [=]() {
|
||||
m_webView->page()->mainFrame()->addToJavaScriptWindowObject(name, object);
|
||||
});
|
||||
}
|
||||
|
||||
void WebViewTab::load(const QUrl &url)
|
||||
@@ -117,9 +88,21 @@ void WebViewTab::focus()
|
||||
m_webView->setFocus();
|
||||
}
|
||||
|
||||
QSize WebViewTab::sizeHint() const
|
||||
void WebViewTab::activateSearchBar()
|
||||
{
|
||||
return m_webView->sizeHint();
|
||||
if (m_searchToolBar == nullptr) {
|
||||
m_searchToolBar = new SearchToolBar(m_webView);
|
||||
layout()->addWidget(m_searchToolBar);
|
||||
}
|
||||
|
||||
if (m_webView->hasSelection()) {
|
||||
const QString selectedText = m_webView->selectedText().simplified();
|
||||
if (!selectedText.isEmpty()) {
|
||||
m_searchToolBar->setText(selectedText);
|
||||
}
|
||||
}
|
||||
|
||||
m_searchToolBar->activate();
|
||||
}
|
||||
|
||||
void WebViewTab::back()
|
||||
@@ -132,22 +115,6 @@ void WebViewTab::forward()
|
||||
m_webView->forward();
|
||||
}
|
||||
|
||||
void WebViewTab::showSearchBar()
|
||||
{
|
||||
m_searchLineEdit->show();
|
||||
m_searchLineEdit->setFocus();
|
||||
if (!m_searchLineEdit->text().isEmpty()) {
|
||||
m_searchLineEdit->selectAll();
|
||||
find(m_searchLineEdit->text());
|
||||
}
|
||||
}
|
||||
|
||||
void WebViewTab::hideSearchBar()
|
||||
{
|
||||
m_searchLineEdit->hide();
|
||||
m_webView->findText(QString(), QWebPage::HighlightAllOccurrences);
|
||||
}
|
||||
|
||||
bool WebViewTab::canGoBack() const
|
||||
{
|
||||
return m_webView->history()->canGoBack();
|
||||
@@ -177,50 +144,10 @@ void WebViewTab::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
switch (event->key()) {
|
||||
case Qt::Key_Slash:
|
||||
showSearchBar();
|
||||
event->accept();
|
||||
activateSearchBar();
|
||||
break;
|
||||
default:
|
||||
event->ignore();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void WebViewTab::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
QWidget::resizeEvent(event);
|
||||
m_webView->resize(event->size().width(), event->size().height());
|
||||
moveLineEdit();
|
||||
}
|
||||
|
||||
void WebViewTab::find(const QString &text)
|
||||
{
|
||||
if (m_webView->selectedText() != text) {
|
||||
m_webView->findText(QString(), QWebPage::HighlightAllOccurrences);
|
||||
m_webView->findText(QString());
|
||||
if (text.isEmpty())
|
||||
return;
|
||||
|
||||
m_webView->findText(text, QWebPage::FindWrapsAroundDocument);
|
||||
}
|
||||
|
||||
m_webView->findText(text, QWebPage::HighlightAllOccurrences);
|
||||
}
|
||||
|
||||
void WebViewTab::findNext(const QString &text, bool backward)
|
||||
{
|
||||
QWebPage::FindFlags flags = QWebPage::FindWrapsAroundDocument;
|
||||
if (backward)
|
||||
flags |= QWebPage::FindBackward;
|
||||
|
||||
m_webView->findText(text, flags);
|
||||
}
|
||||
|
||||
void WebViewTab::moveLineEdit()
|
||||
{
|
||||
int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
|
||||
frameWidth += m_webView->page()->currentFrame()->scrollBarGeometry(Qt::Vertical).width();
|
||||
|
||||
m_searchLineEdit->move(rect().right() - frameWidth - m_searchLineEdit->sizeHint().width(), rect().top());
|
||||
m_searchLineEdit->raise();
|
||||
}
|
||||
|
||||
@@ -26,12 +26,12 @@
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class QLineEdit;
|
||||
class QWebHistory;
|
||||
|
||||
namespace Zeal {
|
||||
namespace WidgetUi {
|
||||
|
||||
class SearchToolBar;
|
||||
class WebView;
|
||||
|
||||
class WebViewTab : public QWidget
|
||||
@@ -42,7 +42,6 @@ public:
|
||||
|
||||
void load(const QUrl &url);
|
||||
void focus();
|
||||
QSize sizeHint() const override;
|
||||
bool canGoBack() const;
|
||||
bool canGoForward() const;
|
||||
|
||||
@@ -54,30 +53,25 @@ public:
|
||||
int zoomLevel() const;
|
||||
void setZoomLevel(int level);
|
||||
|
||||
bool eventFilter(QObject *object, QEvent *event) override;
|
||||
void setWebBridgeObject(const QString &name, QObject *object);
|
||||
|
||||
signals:
|
||||
void linkClicked(const QUrl &url);
|
||||
void titleChanged(const QString &title);
|
||||
void urlChanged(const QUrl &url);
|
||||
|
||||
public slots:
|
||||
void activateSearchBar();
|
||||
void back();
|
||||
void forward();
|
||||
void showSearchBar();
|
||||
void hideSearchBar();
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent *event) override;
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
|
||||
private:
|
||||
void find(const QString &text);
|
||||
void findNext(const QString &text, bool backward = false);
|
||||
void moveLineEdit();
|
||||
friend class WebView;
|
||||
|
||||
QLineEdit *m_searchLineEdit = nullptr;
|
||||
WebView *m_webView = nullptr;
|
||||
SearchToolBar *m_searchToolBar = nullptr;
|
||||
};
|
||||
|
||||
} // namespace WidgetUi
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
ZEAL_LIB_NAME = Util
|
||||
|
||||
unix:!macx {
|
||||
CONFIG += link_pkgconfig
|
||||
PKGCONFIG += sqlite3
|
||||
}
|
||||
win32: {
|
||||
LIBS += -lsqlite3
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
include($$ZEAL_LIBRARY_PRI)
|
||||
|
||||
HEADERS += $$files(*.h)
|
||||
SOURCES += $$files(*.cpp)
|
||||
@@ -1,10 +0,0 @@
|
||||
TEMPLATE = subdirs
|
||||
|
||||
SUBDIRS += \
|
||||
app \
|
||||
libs
|
||||
|
||||
app.subdir = app
|
||||
libs.subdir = libs
|
||||
|
||||
app.depends = libs
|
||||
@@ -1,22 +0,0 @@
|
||||
#-------------------------------------------------
|
||||
#
|
||||
# Project created by QtCreator 2013-01-18T22:28:41
|
||||
#
|
||||
#-------------------------------------------------
|
||||
|
||||
lessThan(QT_VERSION, "5.5.1") {
|
||||
error("Qt 5.5.1 or above is required to build Zeal.")
|
||||
}
|
||||
|
||||
TEMPLATE = subdirs
|
||||
|
||||
SUBDIRS += \
|
||||
assets \
|
||||
src
|
||||
|
||||
# Ease access to these files from Qt Creator
|
||||
OTHER_FILES += \
|
||||
.gitignore \
|
||||
.qmake.conf \
|
||||
.shippable.yml \
|
||||
README.md
|
||||