74 Commits

Author SHA1 Message Date
Oleg Shparber 3bdb8ee0fd chore(release): 0.6.1 2018-09-28 01:26:37 -04:00
Oleg Shparber 4626bc0dd4 fix(cmake): make copyright string static
This is required to have reproducible builds.
2018-09-28 00:52:24 -04:00
Oleg Shparber 0785dd4efa fix(core): replace libarchive extraction with own logic (fixes #747)
On Windows this allows to properly handle paths containing non-Latin
characters.
2018-09-28 00:45:16 -04:00
Oleg Shparber 00f299345c feat(ui): use relative path in portable build if possible (fixes #956)
Applicable only to the subdirectories within path to the executable.
2018-09-27 21:25:39 -04:00
Oleg Shparber 83a8bb75fe feat(ui): add support for the Ctrl+L shortcut (fixes #401) 2018-09-24 01:36:39 -04:00
Oleg Shparber cb2073f866 refactor(registry): remove unused method from SearchQuery 2018-09-24 00:48:58 -04:00
Oleg Shparber 55d9947be5 fix(registry): make query prefix check case insensitive (fixes #957) 2018-09-24 00:47:40 -04:00
Oleg Shparber 2c8fb85471 chore(github): update lock comment 2018-09-16 10:00:44 -04:00
Oleg Shparber 83c4746009 chore(github): update closing comment 2018-09-16 09:55:50 -04:00
Oleg Shparber 52cda7f2ed chore(git): add .vscode to .gitignore 2018-09-16 01:21:13 -04:00
Oleg Shparber 9d8d5d288e chore(github): add config for the Lock Threads app 2018-09-16 01:18:34 -04:00
Oleg Shparber 7060a34d06 registry: Ignore symbols without type (fixes #980) 2018-09-15 19:42:33 -04:00
Bartłomiej Piotrowski fe31f7fffb assets: Add appstream metadata (#911) 2018-09-06 23:15:41 -04:00
htower 9bf296af12 ui: Set background color to white by default (fixes #892) (#951) 2018-06-26 21:32:57 -04:00
Oleg Shparber 769504b020 assets: Install app icons with ECMInstallIcons (fixes #922) (#926) 2018-04-15 23:24:26 +03:00
Oleg Shparber 768a8fef38 ui: Add Back/Forward to the webview's context menu (fixes #925) 2018-04-15 17:40:36 +03:00
Oleg Shparber f127d0a7b3 ui: Refactor WebView::isUrlExternal 2018-04-15 17:39:09 +03:00
Oleg Shparber 2cfc816621 ui: Fix handling of javascript: links (fixes #915) 2018-04-15 17:38:43 +03:00
Oleg Shparber faf4a68bd0 ui: Fix return by constant value 2018-04-15 17:35:08 +03:00
Oleg Shparber 30c85bb136 ui: Fix default fixed font size not being saved (fixes #903) 2018-03-19 00:16:35 +02:00
Matt Sephton 01447079b4 ui: Fix typo in the AboutDialog (#899) 2018-03-03 12:50:01 +02:00
Moritz Nisblé 249e9a91a3 ui: Fix Preferences shortcut (#891) 2018-03-02 18:43:19 +02:00
Oleg Shparber bae9a49b0d Bump version to 0.6.0 2018-02-18 02:48:47 +02:00
Oleg Shparber f85b902fc7 ui: Implement interactive welcome page (fixes #323) 2018-02-18 02:00:48 +02:00
Oleg Shparber ab1c1a2bae registry: Exclude all keywords containing 'dashtoc' 2018-02-09 00:14:23 +02:00
Oleg Shparber 6e40307608 ui: Add missing QDateTime include (fixes #877) 2018-01-28 09:57:15 +02:00
Oleg Shparber af56557f34 app: Update welcome page to FontAwesome v5.0.4 and Bulma v0.6.2
Also links now point to go.zealdocs.org.
2018-01-27 13:28:36 +02:00
Oleg Shparber daf55ff31c Add *.woff to .gitattributes 2018-01-27 12:22:19 +02:00
Oleg Shparber f872a2dd59 ui: Set minimal width for the web view and the side bar 2018-01-26 01:26:50 +02:00
Oleg Shparber 10620933b4 doc: Update the screenshot in README 2018-01-22 01:05:20 +02:00
Oleg Shparber 14330638bb ui: Immediately delete unused QSeletionModels
This is a workaround for QTBUG-49966, as we don't want to keep
replaced selection models around.
2018-01-21 16:09:05 +02:00
Oleg Shparber 2a2627aa79 core: Fix portable build 2018-01-21 02:58:41 +02:00
Oleg Shparber 04159cfdd5 ui: Fix indentation 2018-01-21 01:57:19 +02:00
Oleg Shparber 9fdaa35420 core: Enable HTML5 local storage (fixes #872) 2018-01-21 01:56:50 +02:00
Oleg Shparber 18de7a1b5d core,ui: Add FileManager::cacheLocation() helper 2018-01-21 01:55:20 +02:00
Oleg Shparber 26da8bef6e core,ui: Fix crash when middle-clicking an external link
QApplication::activeWindow() returned a null pointer if message box
was shown.
2018-01-20 23:50:35 +02:00
Oleg Shparber 192a451a25 ui: Improve shortcuts in the Preferences dialog 2018-01-20 22:40:29 +02:00
Oleg Shparber b7ca52ab54 ui: Cleanup MainWindow header 2018-01-20 22:12:16 +02:00
Oleg Shparber 20faeb5fed ui: Add default font preferences (fixes #269) 2018-01-20 17:46:10 +02:00
Oleg Shparber b6abb5104b app: Remove unused constant 2018-01-19 22:21:28 +02:00
Oleg Shparber b18f88d852 ui: Set Ctrl+, as a fallback shortcut for Edit->Preferences 2018-01-18 00:55:44 +02:00
Oleg Shparber 7fbabfc425 ui: Do not pass Core::Application to SettingsDialog 2018-01-15 09:54:42 +02:00
Oleg Shparber 44e0289c35 ui: Use QStyledItemDelegate for all delegates 2018-01-14 02:49:13 +02:00
Oleg Shparber 132619f1f6 ui: Check if docset storage is not writable (fixes #522) 2018-01-14 02:31:41 +02:00
Oleg Shparber 053c46ccbd registry: Ignore case of SQLite table names (fixes #867)
In SQLite all SQL identifies are case-insensitive, even put in quotes.
2018-01-11 02:19:41 +02:00
Oleg Shparber d259a7a0d7 doc: Add CHANGELOG.md pointing to GitHub Releases (closes #395) 2018-01-09 09:32:26 +02:00
Oleg Shparber e406a35255 ui: Set spacing of the cental widget's layout to zero 2018-01-08 00:44:43 +02:00
Oleg Shparber fb78df0504 ui: Cleanup unused header includes and forward declarations 2018-01-08 00:43:27 +02:00
Oleg Shparber 72b254bb18 ui: Remove About Qt menu item
Attribution to Qt is now available in the About dialog.
2018-01-08 00:42:51 +02:00
Oleg Shparber af429d74a0 ui: Add information about third party software (fixes #495) 2018-01-08 00:40:02 +02:00
Oleg Shparber 6dd244949c ui: Add option to enable smooth scrolling (fixes #789) 2018-01-07 23:22:01 +02:00
Oleg Shparber d0d3f56d1d core: Remove implemented TODO comments in Settings 2018-01-07 23:09:43 +02:00
Oleg Shparber 486ce17296 ui: Use system icons for back and forward buttons 2018-01-07 21:08:18 +02:00
Oleg Shparber 7c3789dead ui: Reduce horizontal margins and spacing in tool bars 2018-01-07 20:10:44 +02:00
Oleg Shparber 9e98a6860e ui: Do not disable touch events for web view (fixes #356) 2018-01-07 17:49:08 +02:00
Oleg Shparber a9307b4a1e ui: Conditionally use Qt 5.6 features 2018-01-07 17:31:54 +02:00
Oleg Shparber 478791a2d8 cmake: Require Qt version >= 5.5.1 2018-01-07 17:10:44 +02:00
Oleg Shparber ba3c63c9ae cmake: Silence CMP0071 2018-01-07 17:09:25 +02:00
Oleg Shparber 3b2a395728 ui: Make external link behavior configurable (fixes #416) 2018-01-07 16:00:22 +02:00
Oleg Shparber 8fa793a6f6 ui: Fix sizing of the tab behavior group box in SettingsDialog 2018-01-07 13:42:02 +02:00
Oleg Shparber 9e86fcdfd5 ui: Fix compilation with Qt version < 5.6 2018-01-07 01:02:53 +02:00
Oleg Shparber 4b65cb29da ui: Remove confusing "Open URL" button (fixes #848)
The same action can be now performed through the context menu.
2018-01-07 00:51:38 +02:00
Oleg Shparber b468d31aab ui: Implement custom context menu for the web view (fixes #635) 2018-01-07 00:47:06 +02:00
Oleg Shparber 5cec65591b ui: Refactor content hit testing in WebView 2018-01-07 00:42:55 +02:00
Oleg Shparber 57fbea15c2 ui: Fix new tab link also loading in the current tab 2018-01-07 00:41:03 +02:00
Oleg Shparber fafdc268d8 ui: Do not accept QKeyEvent explicitly
According to the QWidget::keyPressEvent() docs it arrives accepted.
2018-01-06 16:01:28 +02:00
Oleg Shparber d0cdc200bb ui: Fix new tabs always showing the welcome page (fixes #851) 2018-01-06 15:47:26 +02:00
Oleg Shparber a406f8de5f ui: Remove unused search methods in WebViewTab 2018-01-05 12:13:49 +02:00
Oleg Shparber bf574687e5 ui: Fix license information in the About dialog 2018-01-04 02:14:39 +02:00
Oleg Shparber ee4b00efba ui: Update copyright years in the About dialog 2018-01-04 02:14:15 +02:00
Oleg Shparber e58fc7f089 ui: Remove unused WebViewTab::sizeHint() method 2018-01-04 02:13:45 +02:00
Oleg Shparber 2aed15f824 doc: Remove build info for the stable branch in README 2018-01-04 02:13:11 +02:00
Oleg Shparber 38d14735b6 ui: Add in-page search toolbar (#861) 2018-01-04 02:05:47 +02:00
Oleg Shparber 7d39f3fbb4 qmake: Remove qmake support 2017-12-17 17:56:56 +02:00
79 changed files with 1805 additions and 770 deletions
+1
View File
@@ -24,3 +24,4 @@ COPYING
*.icns binary
*.ico binary
*.png binary
*.woff binary
+9
View File
@@ -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
+6
View File
@@ -30,3 +30,9 @@ Makefile*
# CMake
CMakeLists.txt.user
# VS Code
/.vscode
# Linux appdata
/assets/freedesktop/org.zealdocs.Zeal.appdata.xml
-5
View File
@@ -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
+3
View File
@@ -0,0 +1,3 @@
# Changelog
The version history is available on [GitHub Releases](https://github.com/zealdocs/zeal/releases).
+5 -2
View File
@@ -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)
+5 -7
View File
@@ -8,18 +8,16 @@
Zeal is a simple offline documentation browser inspired by [Dash](https://kapeli.com/dash).
![Screenshot](https://i.imgur.com/v63u1ZJ.png)
[More screenshots](https://imgur.com/a/eVi97)
![Screenshot](https://i.imgur.com/qBkZduS.png)
## Build Status
[![Coverity Scan](https://img.shields.io/coverity/scan/4271.svg?style=flat-square)](https://scan.coverity.com/projects/4271)
OS \ Branch | master | stable
------------|--------|-------
Linux | [![Shippable](https://img.shields.io/shippable/54ac2ce4d46935d5fbc19b84/master.svg?style=flat-square)](https://app.shippable.com/projects/54ac2ce4d46935d5fbc19b84) | [![Shippable](https://img.shields.io/shippable/54ac2ce4d46935d5fbc19b84/stable.svg?style=flat-square)](https://app.shippable.com/projects/54ac2ce4d46935d5fbc19b84)
Windows | [![AppVeyor](https://img.shields.io/appveyor/ci/trollixx/zeal/master.svg?style=flat-square)](https://ci.appveyor.com/project/trollixx/zeal) | [![AppVeyor](https://img.shields.io/appveyor/ci/trollixx/zeal/stable.svg?style=flat-square)](https://ci.appveyor.com/project/trollixx/zeal)
| OS | Build Status |
| -------- | ------------ |
| Linux | [![Shippable](https://img.shields.io/shippable/54ac2ce4d46935d5fbc19b84/master.svg?style=flat-square)](https://app.shippable.com/projects/54ac2ce4d46935d5fbc19b84) |
| Windows | [![AppVeyor](https://img.shields.io/appveyor/ci/trollixx/zeal/master.svg?style=flat-square)](https://ci.appveyor.com/project/trollixx/zeal) |
## Download
+1 -17
View File
@@ -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)
-23
View File
@@ -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

+27
View File
@@ -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>
-45
View File
@@ -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
}
-8
View File
@@ -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
+6
View File
@@ -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
-45
View File
@@ -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
}
}
}
-4
View File
@@ -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}
+5
View File
@@ -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}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+49 -24
View File
@@ -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>
+55 -30
View File
@@ -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>
+5 -2
View File
@@ -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>
+5
View 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;
+2
View File
@@ -55,6 +55,8 @@ public:
static Application *instance();
WidgetUi::MainWindow *mainWindow() const;
QNetworkAccessManager *networkManager() const;
Settings *settings() const;
-11
View File
@@ -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
}
-4
View File
@@ -1,4 +0,0 @@
include($$ZEAL_LIBRARY_PRI)
HEADERS += $$files(*.h)
SOURCES += $$files(*.cpp)
+57 -21
View File
@@ -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);
}
+4 -3
View File
@@ -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
+16
View File
@@ -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
}
+3 -2
View File
@@ -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
+75 -2
View File
@@ -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;
}
+22 -3
View File
@@ -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
-7
View File
@@ -1,7 +0,0 @@
TEMPLATE = subdirs
SUBDIRS += \
core \
registry \
ui \
util
+11 -2
View File
@@ -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();
-3
View File
@@ -1,3 +0,0 @@
ZEAL_LIB_NAME = Registry
QT += concurrent
-4
View File
@@ -1,4 +0,0 @@
include($$ZEAL_LIBRARY_PRI)
HEADERS += $$files(*.h)
SOURCES += $$files(*.cpp)
+3 -12
View File
@@ -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;
}
+1 -2
View File
@@ -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
View File
@@ -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.
)
+29 -2
View File
@@ -75,13 +75,13 @@
<property name="text">
<string>&lt;strong&gt;A simple offline documentation browser&lt;/strong&gt;
&lt;br&gt;&lt;br&gt;
Copyright &amp;copy; Oleg Shparber and other conributors, 2013-2017.
Copyright &amp;copy; Oleg Shparber and other contributors, 2013-2018.
&lt;br&gt;
&lt;a href=&quot;https://zealdocs.org&quot;&gt;zealdocs.org&lt;/a&gt;
&lt;br&gt;
&lt;a href=&quot;irc://irc.freenode.net/zealdocs&quot;&gt;#zealdocs&lt;/a&gt; on &lt;a href=&quot;https://www.freenode.net&quot;&gt;Freenode&lt;/a&gt;
&lt;br&gt;&lt;br&gt;
Zeal is an open source software available under the terms of General Public License version 3 (&lt;a href=&quot;https://www.gnu.org/copyleft/gpl.html&quot;&gt;GPLv3&lt;/a&gt;).
Zeal is an open source software available under the terms of the General Public License version 3 (&lt;a href=&quot;https://www.gnu.org/copyleft/gpl.html&quot;&gt;GPLv3&lt;/a&gt;) or later.
&lt;br&gt;&lt;br&gt;
Docsets are courtesy of &lt;a href=&quot;https://kapeli.com/dash&quot;&gt;Dash&lt;/a&gt;.</string>
</property>
@@ -98,6 +98,33 @@ Docsets are courtesy of &lt;a href=&quot;https://kapeli.com/dash&quot;&gt;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>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Zeal heavily relies on other open source software listed below.&lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Bulma&lt;/span&gt; - &lt;a href=&quot;https://bulma.io/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#007af4;&quot;&gt;bulma.io&lt;/span&gt;&lt;/a&gt;&lt;br /&gt;License: MIT License &lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Font Awesome&lt;/span&gt; - &lt;a href=&quot;http://fontawesome.io/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#007af4;&quot;&gt;fontawesome.io&lt;/span&gt;&lt;/a&gt;&lt;br /&gt;License: SIL OFL 1.1 (font), MIT License (css) &lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;libarchive&lt;/span&gt; - &lt;a href=&quot;https://www.libarchive.org/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#007af4;&quot;&gt;www.libarchive.org&lt;/span&gt;&lt;/a&gt;&lt;br /&gt;License: Simplified BSD License &lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;LibQxt&lt;/span&gt; - &lt;a href=&quot;https://bitbucket.org/libqxt/libqxt&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#007af4;&quot;&gt;bitbucket.org/libqxt/libqxt&lt;/span&gt;&lt;/a&gt;&lt;br /&gt;License: New BSD License &lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Qt&lt;/span&gt; - &lt;a href=&quot;https://www.qt.io/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#007af4;&quot;&gt;www.qt.io&lt;/span&gt;&lt;/a&gt;&lt;br /&gt;License: GNU LGPL version 3 &lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;SQLite&lt;/span&gt; - &lt;a href=&quot;https://www.sqlite.org/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#007af4;&quot;&gt;www.sqlite.org&lt;/span&gt;&lt;/a&gt;&lt;br /&gt;License: Public Domain &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item>
+21 -5
View File
@@ -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);
+2 -2
View File
@@ -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:
+183 -127
View File
@@ -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);
}
+8 -1
View File
@@ -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;
+8 -1
View File
@@ -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>&lt;b&gt;Docset storage is read only.&lt;/b&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
+77 -41
View File
@@ -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()
+10 -8
View File
@@ -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;
+48 -20
View File
@@ -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 &amp;Zeal</string>
</property>
</action>
<action name="actionAboutQt">
<property name="text">
<string>About &amp;Qt</string>
</property>
</action>
<action name="actionBack">
<property name="enabled">
<bool>false</bool>
+4 -4
View File
@@ -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);
}
+2 -2
View File
@@ -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
+136 -14
View File
@@ -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
+4 -9
View File
@@ -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
+216 -20
View File
@@ -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>&amp;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&amp;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>&amp;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&amp;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>&amp;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&amp;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&amp;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&amp;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>&amp;Dark Mode</string>
<string>&amp;Dark mode</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QCheckBox" name="highlightOnNavigateCheckBox">
<property name="text">
<string>&amp;Highlight on Navigate</string>
<string>&amp;Highlight on navigate</string>
</property>
</widget>
</item>
@@ -274,7 +430,7 @@
<item>
<widget class="QLabel" name="customCssFileLabel">
<property name="text">
<string>&amp;Custom CSS File:</string>
<string>&amp;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>&amp;Browse...</string>
<string>Bro&amp;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>&amp;Ask everytime</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioExternalLinkOpenDesktop">
<property name="text">
<string>&amp;Open in desktop browser</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioExternalLinkOpen">
<property name="text">
<string>O&amp;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&amp;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&amp;vertisement on the welcome page</string>
</property>
</widget>
</item>
-15
View File
@@ -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
}
-14
View File
@@ -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)
+49
View File
@@ -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();
}
+52
View File
@@ -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
+229
View File
@@ -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);
}
}
+71
View File
@@ -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
+176 -31
View File
@@ -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 &not 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);
}
+7 -2
View File
@@ -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();
};
+24 -97
View File
@@ -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();
}
+5 -11
View File
@@ -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
-9
View File
@@ -1,9 +0,0 @@
ZEAL_LIB_NAME = Util
unix:!macx {
CONFIG += link_pkgconfig
PKGCONFIG += sqlite3
}
win32: {
LIBS += -lsqlite3
}
-4
View File
@@ -1,4 +0,0 @@
include($$ZEAL_LIBRARY_PRI)
HEADERS += $$files(*.h)
SOURCES += $$files(*.cpp)
-10
View File
@@ -1,10 +0,0 @@
TEMPLATE = subdirs
SUBDIRS += \
app \
libs
app.subdir = app
libs.subdir = libs
app.depends = libs
-22
View File
@@ -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