diff --git a/.qmake.conf b/.qmake.conf
index 5fbbff49d..e96e51b13 100644
--- a/.qmake.conf
+++ b/.qmake.conf
@@ -1,2 +1,5 @@
SRC_ROOT=$$PWD
BUILD_ROOT=$$shadowed($$PWD)
+
+ZEAL_COMMON_PRI = $$SRC_ROOT/qmake/common.pri
+ZEAL_LIBRARY_PRI = $$SRC_ROOT/qmake/library.pri
diff --git a/.shippable.yml b/.shippable.yml
index eca162338..1a1ff3619 100644
--- a/.shippable.yml
+++ b/.shippable.yml
@@ -1,7 +1,9 @@
-before_install:
- - shippable_retry sudo apt-get -y -qq update
-install:
- - shippable_retry sudo apt-get -y -qq install --no-install-recommends qt5-default libqt5webkit5-dev libqt5x11extras5-dev libarchive-dev libxcb-keysyms1-dev
-script:
- - qmake
- - make
+language: c
+compiler:
+ - gcc
+build:
+ ci:
+ - shippable_retry sudo apt-get -y -qq update
+ - shippable_retry sudo apt-get -y -qq install --no-install-recommends qt5-default libqt5webkit5-dev libqt5x11extras5-dev libarchive-dev libxcb-keysyms1-dev
+ - qmake
+ - make
diff --git a/README.md b/README.md
index 611f2feb5..05b87eae2 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,10 @@
# Zeal
-[![IRC](https://img.shields.io/badge/irc-%23zealdocs-blue.svg?style=flat-square)](https://kiwiirc.com/client/irc.freenode.net/#zealdocs)
+[![Changelog](https://img.shields.io/github/release/zealdocs/zeal.svg?style=flat-square)](https://github.com/zealdocs/zeal/releases)
+[![IRC](https://img.shields.io/badge/chat-on%20irc-blue.svg?style=flat-square)](https://kiwiirc.com/client/irc.freenode.net/#zealdocs)
+[![Telegram Chat](https://img.shields.io/badge/chat-on%20telegram-179cde.svg?style=flat-square)](https://telegram.me/zealdocschat)
+[![Telegram Channel](https://img.shields.io/badge/follow-on%20telegram-179cde.svg?style=flat-square)](https://telegram.me/zealdocs)
+[![Twitter](https://img.shields.io/badge/follow-on%20twitter-1da1f2.svg?style=flat-square)](https://twitter.com/zealdocs)
> **zeal** *noun*
>
@@ -12,7 +16,7 @@
Zeal is a simple offline documentation browser inspired by [Dash](https://kapeli.com/dash).
-![Screenshot](https://i.imgur.com/SiLvpz8.png)
+![Screenshot](https://i.imgur.com/v63u1ZJ.png)
[More screenshots](https://imgur.com/a/eVi97)
@@ -22,7 +26,7 @@ Zeal is a simple offline documentation browser inspired by [Dash](https://kapeli
OS \ Branch | master | stable
------------|--------|-------
-Linux | [![Shippable](https://img.shields.io/shippable/54ac2ce4d46935d5fbc19b84/master.svg?style=flat-square)](https://app.shippable.com/builds/54ac2ce4d46935d5fbc19b84) | [![Shippable](https://img.shields.io/shippable/54ac2ce4d46935d5fbc19b84/stable.svg?style=flat-square)](https://app.shippable.com/builds/54ac2ce4d46935d5fbc19b84)
+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)
## Download
diff --git a/assets/assets.pro b/assets/assets.pro
new file mode 100644
index 000000000..bb39f61e8
--- /dev/null
+++ b/assets/assets.pro
@@ -0,0 +1,23 @@
+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
+}
diff --git a/src/appicons/128/zeal.png b/assets/freedesktop/appicons/128/zeal.png
similarity index 100%
rename from src/appicons/128/zeal.png
rename to assets/freedesktop/appicons/128/zeal.png
diff --git a/src/appicons/16/zeal.png b/assets/freedesktop/appicons/16/zeal.png
similarity index 100%
rename from src/appicons/16/zeal.png
rename to assets/freedesktop/appicons/16/zeal.png
diff --git a/src/appicons/24/zeal.png b/assets/freedesktop/appicons/24/zeal.png
similarity index 100%
rename from src/appicons/24/zeal.png
rename to assets/freedesktop/appicons/24/zeal.png
diff --git a/src/appicons/32/zeal.png b/assets/freedesktop/appicons/32/zeal.png
similarity index 100%
rename from src/appicons/32/zeal.png
rename to assets/freedesktop/appicons/32/zeal.png
diff --git a/src/appicons/64/zeal.png b/assets/freedesktop/appicons/64/zeal.png
similarity index 100%
rename from src/appicons/64/zeal.png
rename to assets/freedesktop/appicons/64/zeal.png
diff --git a/src/zeal.desktop b/assets/freedesktop/zeal.desktop
similarity index 100%
rename from src/zeal.desktop
rename to assets/freedesktop/zeal.desktop
diff --git a/qmake/common.pri b/qmake/common.pri
new file mode 100644
index 000000000..4e26c76b7
--- /dev/null
+++ b/qmake/common.pri
@@ -0,0 +1,65 @@
+# 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.3.0
+DEFINES += ZEAL_VERSION=\\\"$${VERSION}\\\"
+
+# Browser engine
+CONFIG(zeal_qtwebengine) {
+ qtHaveModule(webenginewidgets): BROWSER_ENGINE = qtwebengine
+ else: error("Qt WebEngine is not available.")
+} else {
+ qtHaveModule(webkitwidgets): BROWSER_ENGINE = qtwebkit
+ else: qtHaveModule(webenginewidgets): BROWSER_ENGINE = qtwebengine
+ else: error("Zeal requires Qt WebEngine or Qt WebKit.")
+}
+
+equals(BROWSER_ENGINE, qtwebengine) {
+ message("Browser engine: Qt WebEngine.")
+ QT += webenginewidgets
+ DEFINES += USE_WEBENGINE
+} else {
+ message("Browser engine: Qt WebKit.")
+ QT += webkitwidgets
+ DEFINES += USE_WEBKIT
+}
+
+# Portable build
+CONFIG(zeal_portable) {
+ message("Portable build: Yes.")
+ DEFINES += PORTABLE_BUILD
+}
+
+# Unix installation prefix
+unix:!macx {
+ isEmpty(PREFIX): PREFIX = /usr
+}
diff --git a/qmake/library.pri b/qmake/library.pri
new file mode 100644
index 000000000..7effb1c8c
--- /dev/null
+++ b/qmake/library.pri
@@ -0,0 +1,8 @@
+include($$ZEAL_COMMON_PRI)
+include($$replace(_PRO_FILE_PWD_, ([^/]+$), \\1/\\1.pri))
+
+TEMPLATE = lib
+CONFIG += staticlib
+
+DESTDIR = $$BUILD_ROOT/.lib
+TARGET = $$ZEAL_LIB_NAME
diff --git a/src/3rdparty/qxtglobalshortcut/qxtglobalshortcut.pri b/src/3rdparty/qxtglobalshortcut/qxtglobalshortcut.pri
deleted file mode 100644
index 44257cfd9..000000000
--- a/src/3rdparty/qxtglobalshortcut/qxtglobalshortcut.pri
+++ /dev/null
@@ -1,20 +0,0 @@
-INCLUDEPATH += $$PWD
-
-HEADERS += $$files($$PWD/*.h)
-SOURCES += $$PWD/qxtglobalshortcut.cpp
-
-unix:!macx {
- QT += x11extras
-
- CONFIG += link_pkgconfig
- PKGCONFIG += x11 xcb xcb-keysyms
-
- SOURCES += $$PWD/qxtglobalshortcut_x11.cpp
-}
-
-win32:SOURCES += $$PWD/qxtglobalshortcut_win.cpp
-
-macx {
- SOURCES += $$PWD/qxtglobalshortcut_mac.cpp
- LIBS += -framework Carbon
-}
diff --git a/src/app/app.pro b/src/app/app.pro
new file mode 100644
index 000000000..7dfd39a05
--- /dev/null
+++ b/src/app/app.pro
@@ -0,0 +1,42 @@
+include($$ZEAL_COMMON_PRI)
+
+TEMPLATE = app
+
+QT += gui widgets sql concurrent
+
+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) {
+ include($$lib_dir/$$basename(lib_dir).pri)
+ PRE_TARGETDEPS += $$BUILD_ROOT/.lib/lib$${ZEAL_LIB_NAME}.a
+ # LIBS += -l$$ZEAL_LIB_NAME
+ }
+}
diff --git a/src/main.cpp b/src/app/main.cpp
similarity index 54%
rename from src/main.cpp
rename to src/app/main.cpp
index 917ce66df..0ca13fd54 100644
--- a/src/main.cpp
+++ b/src/app/main.cpp
@@ -1,8 +1,8 @@
/****************************************************************************
**
-** Copyright (C) 2015 Oleg Shparber
+** Copyright (C) 2015-2016 Oleg Shparber
** Copyright (C) 2013-2014 Jerzy Kozera
-** Contact: http://zealdocs.org/contact.html
+** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
@@ -17,35 +17,41 @@
** 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 .
+** along with Zeal. If not, see .
**
****************************************************************************/
-#include "core/application.h"
-#include "registry/searchquery.h"
+#include
+#include
+#include
#include
#include
-#include
#include
#include
-#include
+#include
#include
#include
-#include
#include
#include
#ifdef Q_OS_WIN32
-#include
#include
-#include
#endif
+#include
+
+using namespace Zeal;
+
+namespace {
+const char contactUrl[] = "https://go.zealdocs.org/l/contact";
+}
+
struct CommandLineParameters
{
bool force;
- Zeal::SearchQuery query;
+ bool preventActivation;
+ Registry::SearchQuery query;
#ifdef Q_OS_WIN32
bool registerProtocolHandlers;
bool unregisterProtocolHandlers;
@@ -69,13 +75,10 @@ CommandLineParameters parseCommandLine(const QStringList &arguments)
parser.addHelpOption();
parser.addVersionOption();
- /// TODO: [Qt 5.4] parser.addOption({{"f", "force"}, "Force the application run."});
+ // TODO: [Qt 5.4] parser.addOption({{"f", "force"}, "Force the application run."});
parser.addOption(QCommandLineOption({QStringLiteral("f"), QStringLiteral("force")},
QObject::tr("Force the application run.")));
- /// TODO: [0.3.0] Remove --query support
- parser.addOption(QCommandLineOption({QStringLiteral("q"), QStringLiteral("query")},
- QObject::tr("[DEPRECATED] Query ."),
- QStringLiteral("term")));
+
#ifdef Q_OS_WIN32
parser.addOption(QCommandLineOption({QStringLiteral("register")},
QObject::tr("Register protocol handlers")));
@@ -87,6 +90,7 @@ CommandLineParameters parseCommandLine(const QStringList &arguments)
CommandLineParameters clParams;
clParams.force = parser.isSet(QStringLiteral("force"));
+ clParams.preventActivation = false;
#ifdef Q_OS_WIN32
clParams.registerProtocolHandlers = parser.isSet(QStringLiteral("register"));
@@ -98,23 +102,25 @@ CommandLineParameters parseCommandLine(const QStringList &arguments)
}
#endif
- if (parser.isSet(QStringLiteral("query"))) {
- clParams.query.setQuery(parser.value(QStringLiteral("query")));
+ // TODO: Support dash-feed:// protocol
+ const QString arg
+ = QUrl::fromPercentEncoding(parser.positionalArguments().value(0).toUtf8());
+ if (arg.startsWith(QLatin1String("dash:"))) {
+ clParams.query.setQuery(stripParameterUrl(arg, QStringLiteral("dash")));
+ } else if (arg.startsWith(QLatin1String("dash-plugin:"))) {
+ const QUrlQuery urlQuery(stripParameterUrl(arg, QStringLiteral("dash-plugin")));
+
+ const QString keys = urlQuery.queryItemValue(QStringLiteral("keys"));
+ if (!keys.isEmpty())
+ clParams.query.setKeywords(keys.split(QLatin1Char(',')));
+
+ clParams.query.setQuery(urlQuery.queryItemValue(QStringLiteral("query")));
+
+ const QString preventActivation
+ = urlQuery.queryItemValue(QStringLiteral("prevent_activation"));
+ clParams.preventActivation = preventActivation == QLatin1String("true");
} else {
- /// TODO: Support dash-feed:// protocol
- const QString arg
- = QUrl::fromPercentEncoding(parser.positionalArguments().value(0).toUtf8());
- if (arg.startsWith(QLatin1String("dash:"))) {
- clParams.query.setQuery(stripParameterUrl(arg, QStringLiteral("dash")));
- } else if (arg.startsWith(QLatin1String("dash-plugin:"))) {
- const QUrlQuery urlQuery(stripParameterUrl(arg, QStringLiteral("dash-plugin")));
- const QString keys = urlQuery.queryItemValue(QStringLiteral("keys"));
- if (!keys.isEmpty())
- clParams.query.setKeywords(keys.split(QLatin1Char(',')));
- clParams.query.setQuery(urlQuery.queryItemValue(QStringLiteral("query")));
- } else {
- clParams.query.setQuery(arg);
- }
+ clParams.query.setQuery(arg);
}
return clParams;
@@ -138,7 +144,7 @@ void registerProtocolHandler(const QString &scheme, const QString &description)
reg->beginGroup(QStringLiteral("shell"));
reg->beginGroup(QStringLiteral("open"));
reg->beginGroup(QStringLiteral("command"));
- reg->setValue(QStringLiteral("Default"), appPath + QLatin1String(" %1"));
+ reg->setValue(QStringLiteral("Default"), QVariant(appPath + QLatin1String(" %1")));
}
void registerProtocolHandlers(const QHash &protocols, bool force = false)
@@ -161,28 +167,13 @@ void unregisterProtocolHandlers(const QHash &protocols)
for (auto it = protocols.cbegin(); it != protocols.cend(); ++it)
reg->remove(it.key());
}
-
-/// TODO: Verify if this bug still exists in Qt 5.2+
-class ZealProxyStyle : public QProxyStyle
-{
-public:
- void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter,
- const QWidget *widget = nullptr) const
- {
- if (element == PE_FrameLineEdit && option->styleObject) {
- option->styleObject->setProperty("_q_no_animation", true);
- // Workaround for a probable bug in QWindowsVistaStyle - for example opening the 'String (CommandEvent)'
- // item from wxPython docset (available at http://wxpython.org/Phoenix/docsets) causes very high CPU usage.
- // Some rough debugging shows that the 'd->startAnimation(t);' call in QWindowsVistaStyle::drawPrimitive
- // is the cuplrit and setting _q_no_animation to true here fixes the issue.
- }
- return QProxyStyle::drawPrimitive(element, option, painter, widget);
- }
-};
#endif
int main(int argc, char *argv[])
{
+ // Do not allow Qt version lower than the app was compiled with.
+ QT_REQUIRE_VERSION(argc, argv, QT_VERSION_STR);
+
QCoreApplication::setApplicationName(QStringLiteral("Zeal"));
QCoreApplication::setApplicationVersion(ZEAL_VERSION);
QCoreApplication::setOrganizationDomain(QStringLiteral("zealdocs.org"));
@@ -200,44 +191,82 @@ int main(int argc, char *argv[])
if (clParams.unregisterProtocolHandlers) {
unregisterProtocolHandlers(protocols);
- ::exit(EXIT_SUCCESS);
+ return EXIT_SUCCESS;
} else {
registerProtocolHandlers(protocols, clParams.registerProtocolHandlers);
if (clParams.registerProtocolHandlers)
- ::exit(EXIT_SUCCESS);
+ return EXIT_SUCCESS;
}
-
- qapp->setStyle(new ZealProxyStyle());
#endif
+ // Detect already running instance and optionally send the search query to it.
+ if (!clParams.force && Core::LocalServer::sendQuery(clParams.query, clParams.preventActivation))
+ return 0;
- // Detect already running instance and optionally pass a search query to it.
- if (!clParams.force) {
- QScopedPointer socket(new QLocalSocket());
- socket->connectToServer(Zeal::Core::Application::localServerName());
+ // Set application-wide window icon. All message boxes and other windows will use it by default.
+ qapp->setWindowIcon(QIcon::fromTheme(QStringLiteral("zeal"),
+ QIcon(QStringLiteral(":/zeal.ico"))));
+
+ QScopedPointer localServer(new Core::LocalServer());
+ if (!localServer->start()) {
+ QScopedPointer msgBox(new QMessageBox());
+ msgBox->setWindowTitle(QStringLiteral("Zeal"));
+
+ msgBox->setIcon(QMessageBox::Warning);
+ msgBox->setText(QObject::tr("Another application instance can be still running, "
+ "or has crashed.
Make sure to start Zeal only once."));
+ msgBox->addButton(QMessageBox::Help);
+ msgBox->addButton(QMessageBox::Retry);
+ QPushButton *quitButton = msgBox->addButton(QObject::tr("&Quit"),
+ QMessageBox::DestructiveRole);
+ msgBox->setDefaultButton(quitButton);
+
+ switch (msgBox->exec()) {
+ case QMessageBox::Rejected:
+ return EXIT_SUCCESS;
+ case QMessageBox::Help:
+ QDesktopServices::openUrl(QUrl(contactUrl));
+ }
- if (socket->waitForConnected(500)) {
- QDataStream out(socket.data());
- out << clParams.query;
- socket->flush();
- return 0;
+ msgBox->removeButton(msgBox->button(QMessageBox::Retry));
+
+ if (!localServer->start(true)) {
+ msgBox->setIcon(QMessageBox::Critical);
+ msgBox->setText(QObject::tr("Zeal is unable to start. Please report the issue "
+ "providing the details below."));
+ msgBox->setDetailedText(localServer->errorString());
+
+ if (msgBox->exec() == QMessageBox::Help)
+ QDesktopServices::openUrl(QUrl(contactUrl));
+
+ return EXIT_SUCCESS;
}
}
// Check for SQLite plugin
- /// TODO: Specific to docset format and should be handled accordingly in the future
+ // TODO: Specific to docset format and should be handled accordingly in the future
if (!QSqlDatabase::isDriverAvailable(QStringLiteral("QSQLITE"))) {
- const int ret = QMessageBox::critical(nullptr, QStringLiteral("Zeal"),
- QObject::tr("Qt SQLite driver is not available."),
- QMessageBox::Close, QMessageBox::Help);
- if (ret == QMessageBox::Help)
- QDesktopServices::openUrl(QStringLiteral("https://zealdocs.org/contact.html"));
- return 0;
+ QScopedPointer msgBox(new QMessageBox());
+ msgBox->setWindowTitle(QStringLiteral("Zeal"));
+ msgBox->setIcon(QMessageBox::Critical);
+ msgBox->setText(QObject::tr("Qt SQLite driver is not available."));
+ msgBox->addButton(QMessageBox::Help);
+ QPushButton *quitButton = msgBox->addButton(QObject::tr("&Quit"),
+ QMessageBox::DestructiveRole);
+ msgBox->setDefaultButton(quitButton);
+ if (msgBox->exec() == QMessageBox::Help)
+ QDesktopServices::openUrl(QUrl(contactUrl));
+ return EXIT_SUCCESS;
}
QDir::setSearchPaths(QStringLiteral("typeIcon"), {QStringLiteral(":/icons/type")});
- QScopedPointer app(new Zeal::Core::Application(clParams.query));
+ QScopedPointer app(new Core::Application());
+ QObject::connect(localServer.data(), &Core::LocalServer::newQuery,
+ app.data(), &Core::Application::executeQuery);
+
+ if (!clParams.query.isEmpty())
+ Core::LocalServer::sendQuery(clParams.query, clParams.preventActivation);
return qapp->exec();
}
diff --git a/src/resources/browser/highlight.css b/src/app/resources/browser/highlight.css
similarity index 100%
rename from src/resources/browser/highlight.css
rename to src/app/resources/browser/highlight.css
diff --git a/src/resources/browser/main.css b/src/app/resources/browser/main.css
similarity index 100%
rename from src/resources/browser/main.css
rename to src/app/resources/browser/main.css
diff --git a/src/resources/browser/start.html b/src/app/resources/browser/start.html
similarity index 94%
rename from src/resources/browser/start.html
rename to src/app/resources/browser/start.html
index 326ec2b0f..2a53b774c 100644
--- a/src/resources/browser/start.html
+++ b/src/app/resources/browser/start.html
@@ -1,6 +1,6 @@
- Zeal - Start Page
+ Start Page
diff --git a/src/app/resources/icons/logo/128x128.png b/src/app/resources/icons/logo/128x128.png
new file mode 100644
index 000000000..a7d7dd556
Binary files /dev/null and b/src/app/resources/icons/logo/128x128.png differ
diff --git a/src/app/resources/icons/logo/64x64.png b/src/app/resources/icons/logo/64x64.png
new file mode 100644
index 000000000..601eadcff
Binary files /dev/null and b/src/app/resources/icons/logo/64x64.png differ
diff --git a/src/app/resources/icons/logo/icon.png b/src/app/resources/icons/logo/icon.png
new file mode 100644
index 000000000..4bd3ce273
Binary files /dev/null and b/src/app/resources/icons/logo/icon.png differ
diff --git a/src/app/resources/icons/logo/icon@2x.png b/src/app/resources/icons/logo/icon@2x.png
new file mode 100644
index 000000000..28b878550
Binary files /dev/null and b/src/app/resources/icons/logo/icon@2x.png differ
diff --git a/src/app/resources/icons/type/Abbreviation.png b/src/app/resources/icons/type/Abbreviation.png
new file mode 100644
index 000000000..9f72492f8
Binary files /dev/null and b/src/app/resources/icons/type/Abbreviation.png differ
diff --git a/src/resources/icons/type/Abbreviation@2x.png b/src/app/resources/icons/type/Abbreviation@2x.png
similarity index 59%
rename from src/resources/icons/type/Abbreviation@2x.png
rename to src/app/resources/icons/type/Abbreviation@2x.png
index e66bf08ab..51e78a500 100644
Binary files a/src/resources/icons/type/Abbreviation@2x.png and b/src/app/resources/icons/type/Abbreviation@2x.png differ
diff --git a/src/app/resources/icons/type/Alias.png b/src/app/resources/icons/type/Alias.png
new file mode 100644
index 000000000..25444691f
Binary files /dev/null and b/src/app/resources/icons/type/Alias.png differ
diff --git a/src/resources/icons/type/Alias@2x.png b/src/app/resources/icons/type/Alias@2x.png
similarity index 59%
rename from src/resources/icons/type/Alias@2x.png
rename to src/app/resources/icons/type/Alias@2x.png
index 68c459323..ce1a37dbe 100644
Binary files a/src/resources/icons/type/Alias@2x.png and b/src/app/resources/icons/type/Alias@2x.png differ
diff --git a/src/app/resources/icons/type/Annotation.png b/src/app/resources/icons/type/Annotation.png
new file mode 100644
index 000000000..25444691f
Binary files /dev/null and b/src/app/resources/icons/type/Annotation.png differ
diff --git a/src/resources/icons/type/Annotation@2x.png b/src/app/resources/icons/type/Annotation@2x.png
similarity index 59%
rename from src/resources/icons/type/Annotation@2x.png
rename to src/app/resources/icons/type/Annotation@2x.png
index 68c459323..ce1a37dbe 100644
Binary files a/src/resources/icons/type/Annotation@2x.png and b/src/app/resources/icons/type/Annotation@2x.png differ
diff --git a/src/app/resources/icons/type/Attribute.png b/src/app/resources/icons/type/Attribute.png
new file mode 100644
index 000000000..89ce4af5c
Binary files /dev/null and b/src/app/resources/icons/type/Attribute.png differ
diff --git a/src/resources/icons/type/Attribute@2x.png b/src/app/resources/icons/type/Attribute@2x.png
similarity index 59%
rename from src/resources/icons/type/Attribute@2x.png
rename to src/app/resources/icons/type/Attribute@2x.png
index 2beee3d54..2e4aaa7ee 100644
Binary files a/src/resources/icons/type/Attribute@2x.png and b/src/app/resources/icons/type/Attribute@2x.png differ
diff --git a/src/app/resources/icons/type/Axiom.png b/src/app/resources/icons/type/Axiom.png
new file mode 100644
index 000000000..25444691f
Binary files /dev/null and b/src/app/resources/icons/type/Axiom.png differ
diff --git a/src/resources/icons/type/Axiom@2x.png b/src/app/resources/icons/type/Axiom@2x.png
similarity index 59%
rename from src/resources/icons/type/Axiom@2x.png
rename to src/app/resources/icons/type/Axiom@2x.png
index 68c459323..ce1a37dbe 100644
Binary files a/src/resources/icons/type/Axiom@2x.png and b/src/app/resources/icons/type/Axiom@2x.png differ
diff --git a/src/app/resources/icons/type/Binding.png b/src/app/resources/icons/type/Binding.png
new file mode 100644
index 000000000..b18b85bda
Binary files /dev/null and b/src/app/resources/icons/type/Binding.png differ
diff --git a/src/app/resources/icons/type/Binding@2x.png b/src/app/resources/icons/type/Binding@2x.png
new file mode 100644
index 000000000..77b161e18
Binary files /dev/null and b/src/app/resources/icons/type/Binding@2x.png differ
diff --git a/src/resources/icons/type/Bookmark.png b/src/app/resources/icons/type/Bookmark.png
similarity index 67%
rename from src/resources/icons/type/Bookmark.png
rename to src/app/resources/icons/type/Bookmark.png
index 6f41ba0fb..9be3830e5 100644
Binary files a/src/resources/icons/type/Bookmark.png and b/src/app/resources/icons/type/Bookmark.png differ
diff --git a/src/resources/icons/type/Bookmark@2x.png b/src/app/resources/icons/type/Bookmark@2x.png
similarity index 80%
rename from src/resources/icons/type/Bookmark@2x.png
rename to src/app/resources/icons/type/Bookmark@2x.png
index 3e4adbb78..9748bc288 100644
Binary files a/src/resources/icons/type/Bookmark@2x.png and b/src/app/resources/icons/type/Bookmark@2x.png differ
diff --git a/src/app/resources/icons/type/Builtin.png b/src/app/resources/icons/type/Builtin.png
new file mode 100644
index 000000000..8f52dc09a
Binary files /dev/null and b/src/app/resources/icons/type/Builtin.png differ
diff --git a/src/app/resources/icons/type/Builtin@2x.png b/src/app/resources/icons/type/Builtin@2x.png
new file mode 100644
index 000000000..5628543c5
Binary files /dev/null and b/src/app/resources/icons/type/Builtin@2x.png differ
diff --git a/src/app/resources/icons/type/Callback.png b/src/app/resources/icons/type/Callback.png
new file mode 100644
index 000000000..377b08e0e
Binary files /dev/null and b/src/app/resources/icons/type/Callback.png differ
diff --git a/src/resources/icons/type/Callback@2x.png b/src/app/resources/icons/type/Callback@2x.png
similarity index 57%
rename from src/resources/icons/type/Callback@2x.png
rename to src/app/resources/icons/type/Callback@2x.png
index b52723988..87f08c9a2 100644
Binary files a/src/resources/icons/type/Callback@2x.png and b/src/app/resources/icons/type/Callback@2x.png differ
diff --git a/src/app/resources/icons/type/Category.png b/src/app/resources/icons/type/Category.png
new file mode 100644
index 000000000..f12144ea5
Binary files /dev/null and b/src/app/resources/icons/type/Category.png differ
diff --git a/src/app/resources/icons/type/Category@2x.png b/src/app/resources/icons/type/Category@2x.png
new file mode 100644
index 000000000..7cdf9953c
Binary files /dev/null and b/src/app/resources/icons/type/Category@2x.png differ
diff --git a/src/app/resources/icons/type/Class.png b/src/app/resources/icons/type/Class.png
new file mode 100644
index 000000000..46e4bf7e7
Binary files /dev/null and b/src/app/resources/icons/type/Class.png differ
diff --git a/src/app/resources/icons/type/Class@2x.png b/src/app/resources/icons/type/Class@2x.png
new file mode 100644
index 000000000..4180f7ea7
Binary files /dev/null and b/src/app/resources/icons/type/Class@2x.png differ
diff --git a/src/app/resources/icons/type/Collection.png b/src/app/resources/icons/type/Collection.png
new file mode 100644
index 000000000..428fd1eb1
Binary files /dev/null and b/src/app/resources/icons/type/Collection.png differ
diff --git a/src/app/resources/icons/type/Collection@2x.png b/src/app/resources/icons/type/Collection@2x.png
new file mode 100644
index 000000000..0e7efa6b3
Binary files /dev/null and b/src/app/resources/icons/type/Collection@2x.png differ
diff --git a/src/app/resources/icons/type/Column.png b/src/app/resources/icons/type/Column.png
new file mode 100644
index 000000000..9cc37d618
Binary files /dev/null and b/src/app/resources/icons/type/Column.png differ
diff --git a/src/app/resources/icons/type/Column@2x.png b/src/app/resources/icons/type/Column@2x.png
new file mode 100644
index 000000000..8ada6f043
Binary files /dev/null and b/src/app/resources/icons/type/Column@2x.png differ
diff --git a/src/app/resources/icons/type/Command.png b/src/app/resources/icons/type/Command.png
new file mode 100644
index 000000000..6724a8150
Binary files /dev/null and b/src/app/resources/icons/type/Command.png differ
diff --git a/src/resources/icons/type/Command@2x.png b/src/app/resources/icons/type/Command@2x.png
similarity index 57%
rename from src/resources/icons/type/Command@2x.png
rename to src/app/resources/icons/type/Command@2x.png
index b88e7ad9c..f355eb099 100644
Binary files a/src/resources/icons/type/Command@2x.png and b/src/app/resources/icons/type/Command@2x.png differ
diff --git a/src/app/resources/icons/type/Component.png b/src/app/resources/icons/type/Component.png
new file mode 100644
index 000000000..9cc37d618
Binary files /dev/null and b/src/app/resources/icons/type/Component.png differ
diff --git a/src/app/resources/icons/type/Component@2x.png b/src/app/resources/icons/type/Component@2x.png
new file mode 100644
index 000000000..8ada6f043
Binary files /dev/null and b/src/app/resources/icons/type/Component@2x.png differ
diff --git a/src/app/resources/icons/type/Constant.png b/src/app/resources/icons/type/Constant.png
new file mode 100644
index 000000000..1d56e2cfd
Binary files /dev/null and b/src/app/resources/icons/type/Constant.png differ
diff --git a/src/app/resources/icons/type/Constant@2x.png b/src/app/resources/icons/type/Constant@2x.png
new file mode 100644
index 000000000..8540dab0e
Binary files /dev/null and b/src/app/resources/icons/type/Constant@2x.png differ
diff --git a/src/app/resources/icons/type/Constructor.png b/src/app/resources/icons/type/Constructor.png
new file mode 100644
index 000000000..cb08c47da
Binary files /dev/null and b/src/app/resources/icons/type/Constructor.png differ
diff --git a/src/app/resources/icons/type/Constructor@2x.png b/src/app/resources/icons/type/Constructor@2x.png
new file mode 100644
index 000000000..cee38eb9d
Binary files /dev/null and b/src/app/resources/icons/type/Constructor@2x.png differ
diff --git a/src/app/resources/icons/type/Conversion.png b/src/app/resources/icons/type/Conversion.png
new file mode 100644
index 000000000..5b929d89b
Binary files /dev/null and b/src/app/resources/icons/type/Conversion.png differ
diff --git a/src/resources/icons/type/Conversion@2x.png b/src/app/resources/icons/type/Conversion@2x.png
similarity index 57%
rename from src/resources/icons/type/Conversion@2x.png
rename to src/app/resources/icons/type/Conversion@2x.png
index 2f8586c44..839b60aa0 100644
Binary files a/src/resources/icons/type/Conversion@2x.png and b/src/app/resources/icons/type/Conversion@2x.png differ
diff --git a/src/app/resources/icons/type/Database.png b/src/app/resources/icons/type/Database.png
new file mode 100644
index 000000000..f090f83d8
Binary files /dev/null and b/src/app/resources/icons/type/Database.png differ
diff --git a/src/resources/icons/type/Database@2x.png b/src/app/resources/icons/type/Database@2x.png
similarity index 54%
rename from src/resources/icons/type/Database@2x.png
rename to src/app/resources/icons/type/Database@2x.png
index fbbb943ba..535ac3aa0 100644
Binary files a/src/resources/icons/type/Database@2x.png and b/src/app/resources/icons/type/Database@2x.png differ
diff --git a/src/app/resources/icons/type/Decorator.png b/src/app/resources/icons/type/Decorator.png
new file mode 100644
index 000000000..7b88c5db9
Binary files /dev/null and b/src/app/resources/icons/type/Decorator.png differ
diff --git a/src/app/resources/icons/type/Decorator@2x.png b/src/app/resources/icons/type/Decorator@2x.png
new file mode 100644
index 000000000..08243f82d
Binary files /dev/null and b/src/app/resources/icons/type/Decorator@2x.png differ
diff --git a/src/app/resources/icons/type/Define.png b/src/app/resources/icons/type/Define.png
new file mode 100644
index 000000000..e13cc63ec
Binary files /dev/null and b/src/app/resources/icons/type/Define.png differ
diff --git a/src/app/resources/icons/type/Define@2x.png b/src/app/resources/icons/type/Define@2x.png
new file mode 100644
index 000000000..1bc6183ac
Binary files /dev/null and b/src/app/resources/icons/type/Define@2x.png differ
diff --git a/src/app/resources/icons/type/Delegate.png b/src/app/resources/icons/type/Delegate.png
new file mode 100644
index 000000000..a6d7808cb
Binary files /dev/null and b/src/app/resources/icons/type/Delegate.png differ
diff --git a/src/app/resources/icons/type/Delegate@2x.png b/src/app/resources/icons/type/Delegate@2x.png
new file mode 100644
index 000000000..97d30f5c3
Binary files /dev/null and b/src/app/resources/icons/type/Delegate@2x.png differ
diff --git a/src/app/resources/icons/type/DeletedSnippet.png b/src/app/resources/icons/type/DeletedSnippet.png
new file mode 100644
index 000000000..91c802efe
Binary files /dev/null and b/src/app/resources/icons/type/DeletedSnippet.png differ
diff --git a/src/app/resources/icons/type/DeletedSnippet@2x.png b/src/app/resources/icons/type/DeletedSnippet@2x.png
new file mode 100644
index 000000000..7be2bc3a9
Binary files /dev/null and b/src/app/resources/icons/type/DeletedSnippet@2x.png differ
diff --git a/src/app/resources/icons/type/Diagram.png b/src/app/resources/icons/type/Diagram.png
new file mode 100644
index 000000000..7b88c5db9
Binary files /dev/null and b/src/app/resources/icons/type/Diagram.png differ
diff --git a/src/app/resources/icons/type/Diagram@2x.png b/src/app/resources/icons/type/Diagram@2x.png
new file mode 100644
index 000000000..08243f82d
Binary files /dev/null and b/src/app/resources/icons/type/Diagram@2x.png differ
diff --git a/src/app/resources/icons/type/Directive.png b/src/app/resources/icons/type/Directive.png
new file mode 100644
index 000000000..f090f83d8
Binary files /dev/null and b/src/app/resources/icons/type/Directive.png differ
diff --git a/src/resources/icons/type/Directive@2x.png b/src/app/resources/icons/type/Directive@2x.png
similarity index 54%
rename from src/resources/icons/type/Directive@2x.png
rename to src/app/resources/icons/type/Directive@2x.png
index fbbb943ba..535ac3aa0 100644
Binary files a/src/resources/icons/type/Directive@2x.png and b/src/app/resources/icons/type/Directive@2x.png differ
diff --git a/src/app/resources/icons/type/Element.png b/src/app/resources/icons/type/Element.png
new file mode 100644
index 000000000..68dc09ce2
Binary files /dev/null and b/src/app/resources/icons/type/Element.png differ
diff --git a/src/app/resources/icons/type/Element@2x.png b/src/app/resources/icons/type/Element@2x.png
new file mode 100644
index 000000000..3fb4a9714
Binary files /dev/null and b/src/app/resources/icons/type/Element@2x.png differ
diff --git a/src/app/resources/icons/type/Entry.png b/src/app/resources/icons/type/Entry.png
new file mode 100644
index 000000000..ffc001204
Binary files /dev/null and b/src/app/resources/icons/type/Entry.png differ
diff --git a/src/app/resources/icons/type/Entry@2x.png b/src/app/resources/icons/type/Entry@2x.png
new file mode 100644
index 000000000..e1a0f1a1e
Binary files /dev/null and b/src/app/resources/icons/type/Entry@2x.png differ
diff --git a/src/resources/icons/type/Enumeration.png b/src/app/resources/icons/type/Enumeration.png
similarity index 57%
rename from src/resources/icons/type/Enumeration.png
rename to src/app/resources/icons/type/Enumeration.png
index 47ef92023..e8e7f58dd 100644
Binary files a/src/resources/icons/type/Enumeration.png and b/src/app/resources/icons/type/Enumeration.png differ
diff --git a/src/app/resources/icons/type/Enumeration@2x.png b/src/app/resources/icons/type/Enumeration@2x.png
new file mode 100644
index 000000000..42f71ed42
Binary files /dev/null and b/src/app/resources/icons/type/Enumeration@2x.png differ
diff --git a/src/app/resources/icons/type/Environment.png b/src/app/resources/icons/type/Environment.png
new file mode 100644
index 000000000..ffc001204
Binary files /dev/null and b/src/app/resources/icons/type/Environment.png differ
diff --git a/src/app/resources/icons/type/Environment@2x.png b/src/app/resources/icons/type/Environment@2x.png
new file mode 100644
index 000000000..e1a0f1a1e
Binary files /dev/null and b/src/app/resources/icons/type/Environment@2x.png differ
diff --git a/src/app/resources/icons/type/Error.png b/src/app/resources/icons/type/Error.png
new file mode 100644
index 000000000..45b9bce17
Binary files /dev/null and b/src/app/resources/icons/type/Error.png differ
diff --git a/src/resources/icons/type/Error@2x.png b/src/app/resources/icons/type/Error@2x.png
similarity index 75%
rename from src/resources/icons/type/Error@2x.png
rename to src/app/resources/icons/type/Error@2x.png
index cde9f9d44..d0bb08081 100644
Binary files a/src/resources/icons/type/Error@2x.png and b/src/app/resources/icons/type/Error@2x.png differ
diff --git a/src/app/resources/icons/type/Event.png b/src/app/resources/icons/type/Event.png
new file mode 100644
index 000000000..19116ccb2
Binary files /dev/null and b/src/app/resources/icons/type/Event.png differ
diff --git a/src/resources/icons/type/Event@2x.png b/src/app/resources/icons/type/Event@2x.png
similarity index 55%
rename from src/resources/icons/type/Event@2x.png
rename to src/app/resources/icons/type/Event@2x.png
index 27f1d4e85..14caba71f 100644
Binary files a/src/resources/icons/type/Event@2x.png and b/src/app/resources/icons/type/Event@2x.png differ
diff --git a/src/app/resources/icons/type/Exception.png b/src/app/resources/icons/type/Exception.png
new file mode 100644
index 000000000..5bf1ea2a4
Binary files /dev/null and b/src/app/resources/icons/type/Exception.png differ
diff --git a/src/resources/icons/type/Exception@2x.png b/src/app/resources/icons/type/Exception@2x.png
similarity index 58%
rename from src/resources/icons/type/Exception@2x.png
rename to src/app/resources/icons/type/Exception@2x.png
index 015047c88..d451af706 100644
Binary files a/src/resources/icons/type/Exception@2x.png and b/src/app/resources/icons/type/Exception@2x.png differ
diff --git a/src/app/resources/icons/type/Extension.png b/src/app/resources/icons/type/Extension.png
new file mode 100644
index 000000000..b550fcafb
Binary files /dev/null and b/src/app/resources/icons/type/Extension.png differ
diff --git a/src/resources/icons/type/Extension@2x.png b/src/app/resources/icons/type/Extension@2x.png
similarity index 60%
rename from src/resources/icons/type/Extension@2x.png
rename to src/app/resources/icons/type/Extension@2x.png
index d826c9745..2de3c6e93 100644
Binary files a/src/resources/icons/type/Extension@2x.png and b/src/app/resources/icons/type/Extension@2x.png differ
diff --git a/src/app/resources/icons/type/Field.png b/src/app/resources/icons/type/Field.png
new file mode 100644
index 000000000..3938859f1
Binary files /dev/null and b/src/app/resources/icons/type/Field.png differ
diff --git a/src/app/resources/icons/type/Field@2x.png b/src/app/resources/icons/type/Field@2x.png
new file mode 100644
index 000000000..7b7d466b8
Binary files /dev/null and b/src/app/resources/icons/type/Field@2x.png differ
diff --git a/src/app/resources/icons/type/File.png b/src/app/resources/icons/type/File.png
new file mode 100644
index 000000000..c1d61e7d2
Binary files /dev/null and b/src/app/resources/icons/type/File.png differ
diff --git a/src/app/resources/icons/type/File@2x.png b/src/app/resources/icons/type/File@2x.png
new file mode 100644
index 000000000..c977228af
Binary files /dev/null and b/src/app/resources/icons/type/File@2x.png differ
diff --git a/src/app/resources/icons/type/Filter.png b/src/app/resources/icons/type/Filter.png
new file mode 100644
index 000000000..3938859f1
Binary files /dev/null and b/src/app/resources/icons/type/Filter.png differ
diff --git a/src/app/resources/icons/type/Filter@2x.png b/src/app/resources/icons/type/Filter@2x.png
new file mode 100644
index 000000000..7b7d466b8
Binary files /dev/null and b/src/app/resources/icons/type/Filter@2x.png differ
diff --git a/src/app/resources/icons/type/Foreign Key.png b/src/app/resources/icons/type/Foreign Key.png
new file mode 100644
index 000000000..3938859f1
Binary files /dev/null and b/src/app/resources/icons/type/Foreign Key.png differ
diff --git a/src/app/resources/icons/type/Foreign Key@2x.png b/src/app/resources/icons/type/Foreign Key@2x.png
new file mode 100644
index 000000000..7b7d466b8
Binary files /dev/null and b/src/app/resources/icons/type/Foreign Key@2x.png differ
diff --git a/src/app/resources/icons/type/Framework.png b/src/app/resources/icons/type/Framework.png
new file mode 100644
index 000000000..c1d61e7d2
Binary files /dev/null and b/src/app/resources/icons/type/Framework.png differ
diff --git a/src/app/resources/icons/type/Framework@2x.png b/src/app/resources/icons/type/Framework@2x.png
new file mode 100644
index 000000000..c977228af
Binary files /dev/null and b/src/app/resources/icons/type/Framework@2x.png differ
diff --git a/src/app/resources/icons/type/Function.png b/src/app/resources/icons/type/Function.png
new file mode 100644
index 000000000..d09ea3a53
Binary files /dev/null and b/src/app/resources/icons/type/Function.png differ
diff --git a/src/app/resources/icons/type/Function@2x.png b/src/app/resources/icons/type/Function@2x.png
new file mode 100644
index 000000000..136800065
Binary files /dev/null and b/src/app/resources/icons/type/Function@2x.png differ
diff --git a/src/app/resources/icons/type/Global.png b/src/app/resources/icons/type/Global.png
new file mode 100644
index 000000000..f74d2be22
Binary files /dev/null and b/src/app/resources/icons/type/Global.png differ
diff --git a/src/resources/icons/type/Global@2x.png b/src/app/resources/icons/type/Global@2x.png
similarity index 57%
rename from src/resources/icons/type/Global@2x.png
rename to src/app/resources/icons/type/Global@2x.png
index 32627a345..822ef7621 100644
Binary files a/src/resources/icons/type/Global@2x.png and b/src/app/resources/icons/type/Global@2x.png differ
diff --git a/src/app/resources/icons/type/Guide.png b/src/app/resources/icons/type/Guide.png
new file mode 100644
index 000000000..579fa6389
Binary files /dev/null and b/src/app/resources/icons/type/Guide.png differ
diff --git a/src/resources/icons/type/Guide@2x.png b/src/app/resources/icons/type/Guide@2x.png
similarity index 92%
rename from src/resources/icons/type/Guide@2x.png
rename to src/app/resources/icons/type/Guide@2x.png
index d2b2025de..df2fe0c68 100644
Binary files a/src/resources/icons/type/Guide@2x.png and b/src/app/resources/icons/type/Guide@2x.png differ
diff --git a/src/app/resources/icons/type/Helper.png b/src/app/resources/icons/type/Helper.png
new file mode 100644
index 000000000..4c366ab54
Binary files /dev/null and b/src/app/resources/icons/type/Helper.png differ
diff --git a/src/app/resources/icons/type/Helper@2x.png b/src/app/resources/icons/type/Helper@2x.png
new file mode 100644
index 000000000..ede52e355
Binary files /dev/null and b/src/app/resources/icons/type/Helper@2x.png differ
diff --git a/src/resources/icons/type/Hook.png b/src/app/resources/icons/type/Hook.png
similarity index 61%
rename from src/resources/icons/type/Hook.png
rename to src/app/resources/icons/type/Hook.png
index b338384f5..b088fcea9 100644
Binary files a/src/resources/icons/type/Hook.png and b/src/app/resources/icons/type/Hook.png differ
diff --git a/src/resources/icons/type/Hook@2x.png b/src/app/resources/icons/type/Hook@2x.png
similarity index 70%
rename from src/resources/icons/type/Hook@2x.png
rename to src/app/resources/icons/type/Hook@2x.png
index c2467961f..b0173cb45 100644
Binary files a/src/resources/icons/type/Hook@2x.png and b/src/app/resources/icons/type/Hook@2x.png differ
diff --git a/src/app/resources/icons/type/Index.png b/src/app/resources/icons/type/Index.png
new file mode 100644
index 000000000..6e2fd9b4a
Binary files /dev/null and b/src/app/resources/icons/type/Index.png differ
diff --git a/src/resources/icons/type/Index@2x.png b/src/app/resources/icons/type/Index@2x.png
similarity index 56%
rename from src/resources/icons/type/Index@2x.png
rename to src/app/resources/icons/type/Index@2x.png
index 599b9ec9c..63c84035b 100644
Binary files a/src/resources/icons/type/Index@2x.png and b/src/app/resources/icons/type/Index@2x.png differ
diff --git a/src/app/resources/icons/type/Indirection.png b/src/app/resources/icons/type/Indirection.png
new file mode 100644
index 000000000..bbe095a22
Binary files /dev/null and b/src/app/resources/icons/type/Indirection.png differ
diff --git a/src/resources/icons/type/Indirection@2x.png b/src/app/resources/icons/type/Indirection@2x.png
similarity index 56%
rename from src/resources/icons/type/Indirection@2x.png
rename to src/app/resources/icons/type/Indirection@2x.png
index fc4312c69..e65480386 100644
Binary files a/src/resources/icons/type/Indirection@2x.png and b/src/app/resources/icons/type/Indirection@2x.png differ
diff --git a/src/app/resources/icons/type/Inductive.png b/src/app/resources/icons/type/Inductive.png
new file mode 100644
index 000000000..bbe095a22
Binary files /dev/null and b/src/app/resources/icons/type/Inductive.png differ
diff --git a/src/resources/icons/type/Inductive@2x.png b/src/app/resources/icons/type/Inductive@2x.png
similarity index 56%
rename from src/resources/icons/type/Inductive@2x.png
rename to src/app/resources/icons/type/Inductive@2x.png
index fc4312c69..e65480386 100644
Binary files a/src/resources/icons/type/Inductive@2x.png and b/src/app/resources/icons/type/Inductive@2x.png differ
diff --git a/src/app/resources/icons/type/Instance.png b/src/app/resources/icons/type/Instance.png
new file mode 100644
index 000000000..199b1f66e
Binary files /dev/null and b/src/app/resources/icons/type/Instance.png differ
diff --git a/src/resources/icons/type/Instance@2x.png b/src/app/resources/icons/type/Instance@2x.png
similarity index 56%
rename from src/resources/icons/type/Instance@2x.png
rename to src/app/resources/icons/type/Instance@2x.png
index c206248c2..a2d2e9693 100644
Binary files a/src/resources/icons/type/Instance@2x.png and b/src/app/resources/icons/type/Instance@2x.png differ
diff --git a/src/app/resources/icons/type/Instruction.png b/src/app/resources/icons/type/Instruction.png
new file mode 100644
index 000000000..6e2fd9b4a
Binary files /dev/null and b/src/app/resources/icons/type/Instruction.png differ
diff --git a/src/resources/icons/type/Instruction@2x.png b/src/app/resources/icons/type/Instruction@2x.png
similarity index 56%
rename from src/resources/icons/type/Instruction@2x.png
rename to src/app/resources/icons/type/Instruction@2x.png
index 599b9ec9c..63c84035b 100644
Binary files a/src/resources/icons/type/Instruction@2x.png and b/src/app/resources/icons/type/Instruction@2x.png differ
diff --git a/src/app/resources/icons/type/Interface.png b/src/app/resources/icons/type/Interface.png
new file mode 100644
index 000000000..b4843b6ef
Binary files /dev/null and b/src/app/resources/icons/type/Interface.png differ
diff --git a/src/resources/icons/type/Interface@2x.png b/src/app/resources/icons/type/Interface@2x.png
similarity index 53%
rename from src/resources/icons/type/Interface@2x.png
rename to src/app/resources/icons/type/Interface@2x.png
index 53ab08b13..af43e55f3 100644
Binary files a/src/resources/icons/type/Interface@2x.png and b/src/app/resources/icons/type/Interface@2x.png differ
diff --git a/src/app/resources/icons/type/Keyword.png b/src/app/resources/icons/type/Keyword.png
new file mode 100644
index 000000000..2c0490116
Binary files /dev/null and b/src/app/resources/icons/type/Keyword.png differ
diff --git a/src/app/resources/icons/type/Keyword@2x.png b/src/app/resources/icons/type/Keyword@2x.png
new file mode 100644
index 000000000..069281065
Binary files /dev/null and b/src/app/resources/icons/type/Keyword@2x.png differ
diff --git a/src/app/resources/icons/type/Lemma.png b/src/app/resources/icons/type/Lemma.png
new file mode 100644
index 000000000..a9d3fd2fb
Binary files /dev/null and b/src/app/resources/icons/type/Lemma.png differ
diff --git a/src/app/resources/icons/type/Lemma@2x.png b/src/app/resources/icons/type/Lemma@2x.png
new file mode 100644
index 000000000..7a9ea0d04
Binary files /dev/null and b/src/app/resources/icons/type/Lemma@2x.png differ
diff --git a/src/app/resources/icons/type/Library.png b/src/app/resources/icons/type/Library.png
new file mode 100644
index 000000000..bf458ccfb
Binary files /dev/null and b/src/app/resources/icons/type/Library.png differ
diff --git a/src/app/resources/icons/type/Library@2x.png b/src/app/resources/icons/type/Library@2x.png
new file mode 100644
index 000000000..923d19903
Binary files /dev/null and b/src/app/resources/icons/type/Library@2x.png differ
diff --git a/src/app/resources/icons/type/Literal.png b/src/app/resources/icons/type/Literal.png
new file mode 100644
index 000000000..bd3b28ebc
Binary files /dev/null and b/src/app/resources/icons/type/Literal.png differ
diff --git a/src/app/resources/icons/type/Literal@2x.png b/src/app/resources/icons/type/Literal@2x.png
new file mode 100644
index 000000000..f8824f518
Binary files /dev/null and b/src/app/resources/icons/type/Literal@2x.png differ
diff --git a/src/app/resources/icons/type/Macro.png b/src/app/resources/icons/type/Macro.png
new file mode 100644
index 000000000..e13cc63ec
Binary files /dev/null and b/src/app/resources/icons/type/Macro.png differ
diff --git a/src/app/resources/icons/type/Macro@2x.png b/src/app/resources/icons/type/Macro@2x.png
new file mode 100644
index 000000000..1bc6183ac
Binary files /dev/null and b/src/app/resources/icons/type/Macro@2x.png differ
diff --git a/src/app/resources/icons/type/Method.png b/src/app/resources/icons/type/Method.png
new file mode 100644
index 000000000..38ce04947
Binary files /dev/null and b/src/app/resources/icons/type/Method.png differ
diff --git a/src/app/resources/icons/type/Method@2x.png b/src/app/resources/icons/type/Method@2x.png
new file mode 100644
index 000000000..623b7e9a0
Binary files /dev/null and b/src/app/resources/icons/type/Method@2x.png differ
diff --git a/src/app/resources/icons/type/Mixin.png b/src/app/resources/icons/type/Mixin.png
new file mode 100644
index 000000000..41d520205
Binary files /dev/null and b/src/app/resources/icons/type/Mixin.png differ
diff --git a/src/app/resources/icons/type/Mixin@2x.png b/src/app/resources/icons/type/Mixin@2x.png
new file mode 100644
index 000000000..2832b04c7
Binary files /dev/null and b/src/app/resources/icons/type/Mixin@2x.png differ
diff --git a/src/app/resources/icons/type/Modifier.png b/src/app/resources/icons/type/Modifier.png
new file mode 100644
index 000000000..61830023a
Binary files /dev/null and b/src/app/resources/icons/type/Modifier.png differ
diff --git a/src/app/resources/icons/type/Modifier@2x.png b/src/app/resources/icons/type/Modifier@2x.png
new file mode 100644
index 000000000..9e6cf0fb1
Binary files /dev/null and b/src/app/resources/icons/type/Modifier@2x.png differ
diff --git a/src/app/resources/icons/type/Module.png b/src/app/resources/icons/type/Module.png
new file mode 100644
index 000000000..658fdc99b
Binary files /dev/null and b/src/app/resources/icons/type/Module.png differ
diff --git a/src/app/resources/icons/type/Module@2x.png b/src/app/resources/icons/type/Module@2x.png
new file mode 100644
index 000000000..cd5c70bad
Binary files /dev/null and b/src/app/resources/icons/type/Module@2x.png differ
diff --git a/src/app/resources/icons/type/Namespace.png b/src/app/resources/icons/type/Namespace.png
new file mode 100644
index 000000000..ece329284
Binary files /dev/null and b/src/app/resources/icons/type/Namespace.png differ
diff --git a/src/app/resources/icons/type/Namespace@2x.png b/src/app/resources/icons/type/Namespace@2x.png
new file mode 100644
index 000000000..0cbb09794
Binary files /dev/null and b/src/app/resources/icons/type/Namespace@2x.png differ
diff --git a/src/app/resources/icons/type/NewSnippet.png b/src/app/resources/icons/type/NewSnippet.png
new file mode 100644
index 000000000..9148b87cc
Binary files /dev/null and b/src/app/resources/icons/type/NewSnippet.png differ
diff --git a/src/app/resources/icons/type/NewSnippet@2x.png b/src/app/resources/icons/type/NewSnippet@2x.png
new file mode 100644
index 000000000..914695d3b
Binary files /dev/null and b/src/app/resources/icons/type/NewSnippet@2x.png differ
diff --git a/src/app/resources/icons/type/Notation.png b/src/app/resources/icons/type/Notation.png
new file mode 100644
index 000000000..d4dcf8192
Binary files /dev/null and b/src/app/resources/icons/type/Notation.png differ
diff --git a/src/app/resources/icons/type/Notation@2x.png b/src/app/resources/icons/type/Notation@2x.png
new file mode 100644
index 000000000..a417138aa
Binary files /dev/null and b/src/app/resources/icons/type/Notation@2x.png differ
diff --git a/src/app/resources/icons/type/Object.png b/src/app/resources/icons/type/Object.png
new file mode 100644
index 000000000..a68029b06
Binary files /dev/null and b/src/app/resources/icons/type/Object.png differ
diff --git a/src/app/resources/icons/type/Object@2x.png b/src/app/resources/icons/type/Object@2x.png
new file mode 100644
index 000000000..e2a2064ba
Binary files /dev/null and b/src/app/resources/icons/type/Object@2x.png differ
diff --git a/src/app/resources/icons/type/Operator.png b/src/app/resources/icons/type/Operator.png
new file mode 100644
index 000000000..a797cd887
Binary files /dev/null and b/src/app/resources/icons/type/Operator.png differ
diff --git a/src/app/resources/icons/type/Operator@2x.png b/src/app/resources/icons/type/Operator@2x.png
new file mode 100644
index 000000000..99f517178
Binary files /dev/null and b/src/app/resources/icons/type/Operator@2x.png differ
diff --git a/src/app/resources/icons/type/Option.png b/src/app/resources/icons/type/Option.png
new file mode 100644
index 000000000..6a4b57ed8
Binary files /dev/null and b/src/app/resources/icons/type/Option.png differ
diff --git a/src/app/resources/icons/type/Option@2x.png b/src/app/resources/icons/type/Option@2x.png
new file mode 100644
index 000000000..5c4350c21
Binary files /dev/null and b/src/app/resources/icons/type/Option@2x.png differ
diff --git a/src/app/resources/icons/type/Package.png b/src/app/resources/icons/type/Package.png
new file mode 100644
index 000000000..9c215dc04
Binary files /dev/null and b/src/app/resources/icons/type/Package.png differ
diff --git a/src/app/resources/icons/type/Package@2x.png b/src/app/resources/icons/type/Package@2x.png
new file mode 100644
index 000000000..1dc2294db
Binary files /dev/null and b/src/app/resources/icons/type/Package@2x.png differ
diff --git a/src/app/resources/icons/type/Parameter.png b/src/app/resources/icons/type/Parameter.png
new file mode 100644
index 000000000..2c8e5069e
Binary files /dev/null and b/src/app/resources/icons/type/Parameter.png differ
diff --git a/src/app/resources/icons/type/Parameter@2x.png b/src/app/resources/icons/type/Parameter@2x.png
new file mode 100644
index 000000000..c3689e4e6
Binary files /dev/null and b/src/app/resources/icons/type/Parameter@2x.png differ
diff --git a/src/app/resources/icons/type/Pattern.png b/src/app/resources/icons/type/Pattern.png
new file mode 100644
index 000000000..25c6a615e
Binary files /dev/null and b/src/app/resources/icons/type/Pattern.png differ
diff --git a/src/app/resources/icons/type/Pattern@2x.png b/src/app/resources/icons/type/Pattern@2x.png
new file mode 100644
index 000000000..a8a983932
Binary files /dev/null and b/src/app/resources/icons/type/Pattern@2x.png differ
diff --git a/src/app/resources/icons/type/Plugin.png b/src/app/resources/icons/type/Plugin.png
new file mode 100644
index 000000000..9c215dc04
Binary files /dev/null and b/src/app/resources/icons/type/Plugin.png differ
diff --git a/src/app/resources/icons/type/Plugin@2x.png b/src/app/resources/icons/type/Plugin@2x.png
new file mode 100644
index 000000000..1dc2294db
Binary files /dev/null and b/src/app/resources/icons/type/Plugin@2x.png differ
diff --git a/src/app/resources/icons/type/Procedure.png b/src/app/resources/icons/type/Procedure.png
new file mode 100644
index 000000000..e6f0525e0
Binary files /dev/null and b/src/app/resources/icons/type/Procedure.png differ
diff --git a/src/app/resources/icons/type/Procedure@2x.png b/src/app/resources/icons/type/Procedure@2x.png
new file mode 100644
index 000000000..19bdfb107
Binary files /dev/null and b/src/app/resources/icons/type/Procedure@2x.png differ
diff --git a/src/app/resources/icons/type/Projection.png b/src/app/resources/icons/type/Projection.png
new file mode 100644
index 000000000..e6f0525e0
Binary files /dev/null and b/src/app/resources/icons/type/Projection.png differ
diff --git a/src/app/resources/icons/type/Projection@2x.png b/src/app/resources/icons/type/Projection@2x.png
new file mode 100644
index 000000000..19bdfb107
Binary files /dev/null and b/src/app/resources/icons/type/Projection@2x.png differ
diff --git a/src/app/resources/icons/type/Property.png b/src/app/resources/icons/type/Property.png
new file mode 100644
index 000000000..719967d92
Binary files /dev/null and b/src/app/resources/icons/type/Property.png differ
diff --git a/src/app/resources/icons/type/Property@2x.png b/src/app/resources/icons/type/Property@2x.png
new file mode 100644
index 000000000..a47a3e8ce
Binary files /dev/null and b/src/app/resources/icons/type/Property@2x.png differ
diff --git a/src/app/resources/icons/type/Protocol.png b/src/app/resources/icons/type/Protocol.png
new file mode 100644
index 000000000..a12b9dde8
Binary files /dev/null and b/src/app/resources/icons/type/Protocol.png differ
diff --git a/src/app/resources/icons/type/Protocol@2x.png b/src/app/resources/icons/type/Protocol@2x.png
new file mode 100644
index 000000000..dce85d2c5
Binary files /dev/null and b/src/app/resources/icons/type/Protocol@2x.png differ
diff --git a/src/app/resources/icons/type/Provider.png b/src/app/resources/icons/type/Provider.png
new file mode 100644
index 000000000..2c8e5069e
Binary files /dev/null and b/src/app/resources/icons/type/Provider.png differ
diff --git a/src/app/resources/icons/type/Provider@2x.png b/src/app/resources/icons/type/Provider@2x.png
new file mode 100644
index 000000000..c3689e4e6
Binary files /dev/null and b/src/app/resources/icons/type/Provider@2x.png differ
diff --git a/src/app/resources/icons/type/Provisioner.png b/src/app/resources/icons/type/Provisioner.png
new file mode 100644
index 000000000..e6f0525e0
Binary files /dev/null and b/src/app/resources/icons/type/Provisioner.png differ
diff --git a/src/app/resources/icons/type/Provisioner@2x.png b/src/app/resources/icons/type/Provisioner@2x.png
new file mode 100644
index 000000000..19bdfb107
Binary files /dev/null and b/src/app/resources/icons/type/Provisioner@2x.png differ
diff --git a/src/app/resources/icons/type/Query.png b/src/app/resources/icons/type/Query.png
new file mode 100644
index 000000000..5eff5167e
Binary files /dev/null and b/src/app/resources/icons/type/Query.png differ
diff --git a/src/app/resources/icons/type/Query@2x.png b/src/app/resources/icons/type/Query@2x.png
new file mode 100644
index 000000000..eef6cb644
Binary files /dev/null and b/src/app/resources/icons/type/Query@2x.png differ
diff --git a/src/resources/icons/type/Record.png b/src/app/resources/icons/type/Record.png
similarity index 69%
rename from src/resources/icons/type/Record.png
rename to src/app/resources/icons/type/Record.png
index cb84bf665..bb51ab5f8 100644
Binary files a/src/resources/icons/type/Record.png and b/src/app/resources/icons/type/Record.png differ
diff --git a/src/resources/icons/type/Record@2x.png b/src/app/resources/icons/type/Record@2x.png
similarity index 78%
rename from src/resources/icons/type/Record@2x.png
rename to src/app/resources/icons/type/Record@2x.png
index bdfc1a0ef..b8ce0176d 100644
Binary files a/src/resources/icons/type/Record@2x.png and b/src/app/resources/icons/type/Record@2x.png differ
diff --git a/src/resources/icons/type/Relationship.png b/src/app/resources/icons/type/Relationship.png
similarity index 69%
rename from src/resources/icons/type/Relationship.png
rename to src/app/resources/icons/type/Relationship.png
index cb84bf665..bb51ab5f8 100644
Binary files a/src/resources/icons/type/Relationship.png and b/src/app/resources/icons/type/Relationship.png differ
diff --git a/src/resources/icons/type/Relationship@2x.png b/src/app/resources/icons/type/Relationship@2x.png
similarity index 78%
rename from src/resources/icons/type/Relationship@2x.png
rename to src/app/resources/icons/type/Relationship@2x.png
index bdfc1a0ef..b8ce0176d 100644
Binary files a/src/resources/icons/type/Relationship@2x.png and b/src/app/resources/icons/type/Relationship@2x.png differ
diff --git a/src/resources/icons/type/Report.png b/src/app/resources/icons/type/Report.png
similarity index 69%
rename from src/resources/icons/type/Report.png
rename to src/app/resources/icons/type/Report.png
index cb84bf665..bb51ab5f8 100644
Binary files a/src/resources/icons/type/Report.png and b/src/app/resources/icons/type/Report.png differ
diff --git a/src/resources/icons/type/Report@2x.png b/src/app/resources/icons/type/Report@2x.png
similarity index 78%
rename from src/resources/icons/type/Report@2x.png
rename to src/app/resources/icons/type/Report@2x.png
index bdfc1a0ef..b8ce0176d 100644
Binary files a/src/resources/icons/type/Report@2x.png and b/src/app/resources/icons/type/Report@2x.png differ
diff --git a/src/app/resources/icons/type/Request.png b/src/app/resources/icons/type/Request.png
new file mode 100644
index 000000000..bb51ab5f8
Binary files /dev/null and b/src/app/resources/icons/type/Request.png differ
diff --git a/src/app/resources/icons/type/Request@2x.png b/src/app/resources/icons/type/Request@2x.png
new file mode 100644
index 000000000..b8ce0176d
Binary files /dev/null and b/src/app/resources/icons/type/Request@2x.png differ
diff --git a/src/resources/icons/type/Resource.png b/src/app/resources/icons/type/Resource.png
similarity index 69%
rename from src/resources/icons/type/Resource.png
rename to src/app/resources/icons/type/Resource.png
index cb84bf665..bb51ab5f8 100644
Binary files a/src/resources/icons/type/Resource.png and b/src/app/resources/icons/type/Resource.png differ
diff --git a/src/resources/icons/type/Resource@2x.png b/src/app/resources/icons/type/Resource@2x.png
similarity index 78%
rename from src/resources/icons/type/Resource@2x.png
rename to src/app/resources/icons/type/Resource@2x.png
index bdfc1a0ef..b8ce0176d 100644
Binary files a/src/resources/icons/type/Resource@2x.png and b/src/app/resources/icons/type/Resource@2x.png differ
diff --git a/src/app/resources/icons/type/Sample.png b/src/app/resources/icons/type/Sample.png
new file mode 100644
index 000000000..8f8c54fbb
Binary files /dev/null and b/src/app/resources/icons/type/Sample.png differ
diff --git a/src/resources/icons/type/Sample@2x.png b/src/app/resources/icons/type/Sample@2x.png
similarity index 70%
rename from src/resources/icons/type/Sample@2x.png
rename to src/app/resources/icons/type/Sample@2x.png
index d97ee9509..de38d7ab7 100644
Binary files a/src/resources/icons/type/Sample@2x.png and b/src/app/resources/icons/type/Sample@2x.png differ
diff --git a/src/app/resources/icons/type/Schema.png b/src/app/resources/icons/type/Schema.png
new file mode 100644
index 000000000..87f6d189b
Binary files /dev/null and b/src/app/resources/icons/type/Schema.png differ
diff --git a/src/app/resources/icons/type/Schema@2x.png b/src/app/resources/icons/type/Schema@2x.png
new file mode 100644
index 000000000..f545cfd12
Binary files /dev/null and b/src/app/resources/icons/type/Schema@2x.png differ
diff --git a/src/app/resources/icons/type/Script.png b/src/app/resources/icons/type/Script.png
new file mode 100644
index 000000000..f58a61795
Binary files /dev/null and b/src/app/resources/icons/type/Script.png differ
diff --git a/src/app/resources/icons/type/Script@2x.png b/src/app/resources/icons/type/Script@2x.png
new file mode 100644
index 000000000..416723398
Binary files /dev/null and b/src/app/resources/icons/type/Script@2x.png differ
diff --git a/src/app/resources/icons/type/Section.png b/src/app/resources/icons/type/Section.png
new file mode 100644
index 000000000..77ff762bf
Binary files /dev/null and b/src/app/resources/icons/type/Section.png differ
diff --git a/src/resources/icons/type/Section@2x.png b/src/app/resources/icons/type/Section@2x.png
similarity index 77%
rename from src/resources/icons/type/Section@2x.png
rename to src/app/resources/icons/type/Section@2x.png
index 37dc5cd35..bcb647b9e 100644
Binary files a/src/resources/icons/type/Section@2x.png and b/src/app/resources/icons/type/Section@2x.png differ
diff --git a/src/app/resources/icons/type/Service.png b/src/app/resources/icons/type/Service.png
new file mode 100644
index 000000000..87f6d189b
Binary files /dev/null and b/src/app/resources/icons/type/Service.png differ
diff --git a/src/app/resources/icons/type/Service@2x.png b/src/app/resources/icons/type/Service@2x.png
new file mode 100644
index 000000000..f545cfd12
Binary files /dev/null and b/src/app/resources/icons/type/Service@2x.png differ
diff --git a/src/app/resources/icons/type/Setting.png b/src/app/resources/icons/type/Setting.png
new file mode 100644
index 000000000..6a6460812
Binary files /dev/null and b/src/app/resources/icons/type/Setting.png differ
diff --git a/src/app/resources/icons/type/Setting@2x.png b/src/app/resources/icons/type/Setting@2x.png
new file mode 100644
index 000000000..65d421626
Binary files /dev/null and b/src/app/resources/icons/type/Setting@2x.png differ
diff --git a/src/app/resources/icons/type/Shortcut.png b/src/app/resources/icons/type/Shortcut.png
new file mode 100644
index 000000000..5f5dcee85
Binary files /dev/null and b/src/app/resources/icons/type/Shortcut.png differ
diff --git a/src/app/resources/icons/type/Shortcut@2x.png b/src/app/resources/icons/type/Shortcut@2x.png
new file mode 100644
index 000000000..6be85a650
Binary files /dev/null and b/src/app/resources/icons/type/Shortcut@2x.png differ
diff --git a/src/app/resources/icons/type/Signature.png b/src/app/resources/icons/type/Signature.png
new file mode 100644
index 000000000..87f6d189b
Binary files /dev/null and b/src/app/resources/icons/type/Signature.png differ
diff --git a/src/app/resources/icons/type/Signature@2x.png b/src/app/resources/icons/type/Signature@2x.png
new file mode 100644
index 000000000..f545cfd12
Binary files /dev/null and b/src/app/resources/icons/type/Signature@2x.png differ
diff --git a/src/app/resources/icons/type/Snippet.png b/src/app/resources/icons/type/Snippet.png
new file mode 100644
index 000000000..ff43f3e58
Binary files /dev/null and b/src/app/resources/icons/type/Snippet.png differ
diff --git a/src/app/resources/icons/type/Snippet@2x.png b/src/app/resources/icons/type/Snippet@2x.png
new file mode 100644
index 000000000..7677000a1
Binary files /dev/null and b/src/app/resources/icons/type/Snippet@2x.png differ
diff --git a/src/app/resources/icons/type/Special Form.png b/src/app/resources/icons/type/Special Form.png
new file mode 100644
index 000000000..3938859f1
Binary files /dev/null and b/src/app/resources/icons/type/Special Form.png differ
diff --git a/src/app/resources/icons/type/Special Form@2x.png b/src/app/resources/icons/type/Special Form@2x.png
new file mode 100644
index 000000000..7b7d466b8
Binary files /dev/null and b/src/app/resources/icons/type/Special Form@2x.png differ
diff --git a/src/app/resources/icons/type/Statement.png b/src/app/resources/icons/type/Statement.png
new file mode 100644
index 000000000..87f6d189b
Binary files /dev/null and b/src/app/resources/icons/type/Statement.png differ
diff --git a/src/app/resources/icons/type/Statement@2x.png b/src/app/resources/icons/type/Statement@2x.png
new file mode 100644
index 000000000..f545cfd12
Binary files /dev/null and b/src/app/resources/icons/type/Statement@2x.png differ
diff --git a/src/app/resources/icons/type/Structure.png b/src/app/resources/icons/type/Structure.png
new file mode 100644
index 000000000..31db77582
Binary files /dev/null and b/src/app/resources/icons/type/Structure.png differ
diff --git a/src/app/resources/icons/type/Structure@2x.png b/src/app/resources/icons/type/Structure@2x.png
new file mode 100644
index 000000000..1895f9e73
Binary files /dev/null and b/src/app/resources/icons/type/Structure@2x.png differ
diff --git a/src/app/resources/icons/type/Style.png b/src/app/resources/icons/type/Style.png
new file mode 100644
index 000000000..6a6460812
Binary files /dev/null and b/src/app/resources/icons/type/Style.png differ
diff --git a/src/app/resources/icons/type/Style@2x.png b/src/app/resources/icons/type/Style@2x.png
new file mode 100644
index 000000000..65d421626
Binary files /dev/null and b/src/app/resources/icons/type/Style@2x.png differ
diff --git a/src/app/resources/icons/type/Subroutine.png b/src/app/resources/icons/type/Subroutine.png
new file mode 100644
index 000000000..6a6460812
Binary files /dev/null and b/src/app/resources/icons/type/Subroutine.png differ
diff --git a/src/app/resources/icons/type/Subroutine@2x.png b/src/app/resources/icons/type/Subroutine@2x.png
new file mode 100644
index 000000000..65d421626
Binary files /dev/null and b/src/app/resources/icons/type/Subroutine@2x.png differ
diff --git a/src/app/resources/icons/type/Syntax.png b/src/app/resources/icons/type/Syntax.png
new file mode 100644
index 000000000..f58a61795
Binary files /dev/null and b/src/app/resources/icons/type/Syntax.png differ
diff --git a/src/app/resources/icons/type/Syntax@2x.png b/src/app/resources/icons/type/Syntax@2x.png
new file mode 100644
index 000000000..416723398
Binary files /dev/null and b/src/app/resources/icons/type/Syntax@2x.png differ
diff --git a/src/resources/icons/type/Table.png b/src/app/resources/icons/type/Table.png
similarity index 56%
rename from src/resources/icons/type/Table.png
rename to src/app/resources/icons/type/Table.png
index 93e2aa2b1..19f520a7a 100644
Binary files a/src/resources/icons/type/Table.png and b/src/app/resources/icons/type/Table.png differ
diff --git a/src/app/resources/icons/type/Table@2x.png b/src/app/resources/icons/type/Table@2x.png
new file mode 100644
index 000000000..2790afe7e
Binary files /dev/null and b/src/app/resources/icons/type/Table@2x.png differ
diff --git a/src/app/resources/icons/type/Tactic.png b/src/app/resources/icons/type/Tactic.png
new file mode 100644
index 000000000..572ae6718
Binary files /dev/null and b/src/app/resources/icons/type/Tactic.png differ
diff --git a/src/app/resources/icons/type/Tactic@2x.png b/src/app/resources/icons/type/Tactic@2x.png
new file mode 100644
index 000000000..e24f8d378
Binary files /dev/null and b/src/app/resources/icons/type/Tactic@2x.png differ
diff --git a/src/resources/icons/type/Tag.png b/src/app/resources/icons/type/Tag.png
similarity index 56%
rename from src/resources/icons/type/Tag.png
rename to src/app/resources/icons/type/Tag.png
index 93e2aa2b1..19f520a7a 100644
Binary files a/src/resources/icons/type/Tag.png and b/src/app/resources/icons/type/Tag.png differ
diff --git a/src/app/resources/icons/type/Tag@2x.png b/src/app/resources/icons/type/Tag@2x.png
new file mode 100644
index 000000000..2790afe7e
Binary files /dev/null and b/src/app/resources/icons/type/Tag@2x.png differ
diff --git a/src/app/resources/icons/type/Test.png b/src/app/resources/icons/type/Test.png
new file mode 100644
index 000000000..572ae6718
Binary files /dev/null and b/src/app/resources/icons/type/Test.png differ
diff --git a/src/app/resources/icons/type/Test@2x.png b/src/app/resources/icons/type/Test@2x.png
new file mode 100644
index 000000000..e24f8d378
Binary files /dev/null and b/src/app/resources/icons/type/Test@2x.png differ
diff --git a/src/resources/icons/type/Trait.png b/src/app/resources/icons/type/Trait.png
similarity index 56%
rename from src/resources/icons/type/Trait.png
rename to src/app/resources/icons/type/Trait.png
index 93e2aa2b1..19f520a7a 100644
Binary files a/src/resources/icons/type/Trait.png and b/src/app/resources/icons/type/Trait.png differ
diff --git a/src/app/resources/icons/type/Trait@2x.png b/src/app/resources/icons/type/Trait@2x.png
new file mode 100644
index 000000000..2790afe7e
Binary files /dev/null and b/src/app/resources/icons/type/Trait@2x.png differ
diff --git a/src/app/resources/icons/type/Trigger.png b/src/app/resources/icons/type/Trigger.png
new file mode 100644
index 000000000..572ae6718
Binary files /dev/null and b/src/app/resources/icons/type/Trigger.png differ
diff --git a/src/app/resources/icons/type/Trigger@2x.png b/src/app/resources/icons/type/Trigger@2x.png
new file mode 100644
index 000000000..e24f8d378
Binary files /dev/null and b/src/app/resources/icons/type/Trigger@2x.png differ
diff --git a/src/app/resources/icons/type/Type.png b/src/app/resources/icons/type/Type.png
new file mode 100644
index 000000000..70dfe4d7b
Binary files /dev/null and b/src/app/resources/icons/type/Type.png differ
diff --git a/src/app/resources/icons/type/Type@2x.png b/src/app/resources/icons/type/Type@2x.png
new file mode 100644
index 000000000..78850a363
Binary files /dev/null and b/src/app/resources/icons/type/Type@2x.png differ
diff --git a/src/app/resources/icons/type/Union.png b/src/app/resources/icons/type/Union.png
new file mode 100644
index 000000000..46810c093
Binary files /dev/null and b/src/app/resources/icons/type/Union.png differ
diff --git a/src/app/resources/icons/type/Union@2x.png b/src/app/resources/icons/type/Union@2x.png
new file mode 100644
index 000000000..c09361058
Binary files /dev/null and b/src/app/resources/icons/type/Union@2x.png differ
diff --git a/src/resources/icons/type/Unknown.png b/src/app/resources/icons/type/Unknown.png
similarity index 60%
rename from src/resources/icons/type/Unknown.png
rename to src/app/resources/icons/type/Unknown.png
index 0aa339922..de57ae505 100644
Binary files a/src/resources/icons/type/Unknown.png and b/src/app/resources/icons/type/Unknown.png differ
diff --git a/src/app/resources/icons/type/Unknown@2x.png b/src/app/resources/icons/type/Unknown@2x.png
new file mode 100644
index 000000000..295522df8
Binary files /dev/null and b/src/app/resources/icons/type/Unknown@2x.png differ
diff --git a/src/app/resources/icons/type/Value.png b/src/app/resources/icons/type/Value.png
new file mode 100644
index 000000000..8a1498766
Binary files /dev/null and b/src/app/resources/icons/type/Value.png differ
diff --git a/src/app/resources/icons/type/Value@2x.png b/src/app/resources/icons/type/Value@2x.png
new file mode 100644
index 000000000..fcabbdbf0
Binary files /dev/null and b/src/app/resources/icons/type/Value@2x.png differ
diff --git a/src/app/resources/icons/type/Variable.png b/src/app/resources/icons/type/Variable.png
new file mode 100644
index 000000000..84cee5014
Binary files /dev/null and b/src/app/resources/icons/type/Variable.png differ
diff --git a/src/app/resources/icons/type/Variable@2x.png b/src/app/resources/icons/type/Variable@2x.png
new file mode 100644
index 000000000..4be45091e
Binary files /dev/null and b/src/app/resources/icons/type/Variable@2x.png differ
diff --git a/src/app/resources/icons/type/Variant.png b/src/app/resources/icons/type/Variant.png
new file mode 100644
index 000000000..8a1498766
Binary files /dev/null and b/src/app/resources/icons/type/Variant.png differ
diff --git a/src/app/resources/icons/type/Variant@2x.png b/src/app/resources/icons/type/Variant@2x.png
new file mode 100644
index 000000000..fcabbdbf0
Binary files /dev/null and b/src/app/resources/icons/type/Variant@2x.png differ
diff --git a/src/app/resources/icons/type/View.png b/src/app/resources/icons/type/View.png
new file mode 100644
index 000000000..6b20c6ff5
Binary files /dev/null and b/src/app/resources/icons/type/View.png differ
diff --git a/src/app/resources/icons/type/View@2x.png b/src/app/resources/icons/type/View@2x.png
new file mode 100644
index 000000000..8fc7158b7
Binary files /dev/null and b/src/app/resources/icons/type/View@2x.png differ
diff --git a/src/resources/icons/type/Web.png b/src/app/resources/icons/type/Web.png
similarity index 80%
rename from src/resources/icons/type/Web.png
rename to src/app/resources/icons/type/Web.png
index d7a026469..942b17c83 100644
Binary files a/src/resources/icons/type/Web.png and b/src/app/resources/icons/type/Web.png differ
diff --git a/src/resources/icons/type/Web@2x.png b/src/app/resources/icons/type/Web@2x.png
similarity index 96%
rename from src/resources/icons/type/Web@2x.png
rename to src/app/resources/icons/type/Web@2x.png
index 3cc37b1a5..2949c5a86 100644
Binary files a/src/resources/icons/type/Web@2x.png and b/src/app/resources/icons/type/Web@2x.png differ
diff --git a/src/app/resources/icons/type/Word.png b/src/app/resources/icons/type/Word.png
new file mode 100644
index 000000000..f8f1d9649
Binary files /dev/null and b/src/app/resources/icons/type/Word.png differ
diff --git a/src/app/resources/icons/type/Word@2x.png b/src/app/resources/icons/type/Word@2x.png
new file mode 100644
index 000000000..30a7c2563
Binary files /dev/null and b/src/app/resources/icons/type/Word@2x.png differ
diff --git a/src/app/resources/icons/type/_DashAnnotations.png b/src/app/resources/icons/type/_DashAnnotations.png
new file mode 100644
index 000000000..ce249000a
Binary files /dev/null and b/src/app/resources/icons/type/_DashAnnotations.png differ
diff --git a/src/app/resources/icons/type/_DashAnnotations@2x.png b/src/app/resources/icons/type/_DashAnnotations@2x.png
new file mode 100644
index 000000000..cbf63fa8d
Binary files /dev/null and b/src/app/resources/icons/type/_DashAnnotations@2x.png differ
diff --git a/src/app/resources/icons/type/_Struct.png b/src/app/resources/icons/type/_Struct.png
new file mode 100644
index 000000000..ac1ad436e
Binary files /dev/null and b/src/app/resources/icons/type/_Struct.png differ
diff --git a/src/app/resources/icons/type/_Struct@2x.png b/src/app/resources/icons/type/_Struct@2x.png
new file mode 100644
index 000000000..0fce0e833
Binary files /dev/null and b/src/app/resources/icons/type/_Struct@2x.png differ
diff --git a/src/resources/zeal.icns b/src/app/resources/zeal.icns
similarity index 100%
rename from src/resources/zeal.icns
rename to src/app/resources/zeal.icns
diff --git a/src/resources/zeal.ico b/src/app/resources/zeal.ico
similarity index 100%
rename from src/resources/zeal.ico
rename to src/app/resources/zeal.ico
diff --git a/src/resources/zeal.qrc b/src/app/resources/zeal.qrc
similarity index 93%
rename from src/resources/zeal.qrc
rename to src/app/resources/zeal.qrc
index 9b592bac0..b61104e47 100644
--- a/src/resources/zeal.qrc
+++ b/src/app/resources/zeal.qrc
@@ -30,6 +30,8 @@
icons/type/Category@2x.png
icons/type/Class.png
icons/type/Class@2x.png
+ icons/type/Collection.png
+ icons/type/Collection@2x.png
icons/type/Column.png
icons/type/Column@2x.png
icons/type/Command.png
@@ -44,6 +46,8 @@
icons/type/Conversion@2x.png
icons/type/Database.png
icons/type/Database@2x.png
+ icons/type/Decorator.png
+ icons/type/Decorator@2x.png
icons/type/Define.png
icons/type/Define@2x.png
icons/type/Delegate.png
@@ -86,6 +90,8 @@
icons/type/Global@2x.png
icons/type/Guide.png
icons/type/Guide@2x.png
+ icons/type/Helper.png
+ icons/type/Helper@2x.png
icons/type/Hook.png
icons/type/Hook@2x.png
icons/type/Index.png
@@ -134,6 +140,8 @@
icons/type/Package@2x.png
icons/type/Parameter.png
icons/type/Parameter@2x.png
+ icons/type/Pattern.png
+ icons/type/Pattern@2x.png
icons/type/Plugin.png
icons/type/Plugin@2x.png
icons/type/Procedure.png
@@ -156,6 +164,8 @@
icons/type/Relationship@2x.png
icons/type/Report.png
icons/type/Report@2x.png
+ icons/type/Request.png
+ icons/type/Request@2x.png
icons/type/Resource.png
icons/type/Resource@2x.png
icons/type/Sample.png
@@ -172,6 +182,8 @@
icons/type/Setting@2x.png
icons/type/Shortcut.png
icons/type/Shortcut@2x.png
+ icons/type/Signature.png
+ icons/type/Signature@2x.png
icons/type/Snippet.png
icons/type/Snippet@2x.png
icons/type/Special Form.png
@@ -210,6 +222,8 @@
icons/type/Variable@2x.png
icons/type/Variant.png
icons/type/Variant@2x.png
+ icons/type/View.png
+ icons/type/View@2x.png
icons/type/Web.png
icons/type/Web@2x.png
icons/type/Word.png
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
deleted file mode 100644
index 5a7342045..000000000
--- a/src/core/settings.cpp
+++ /dev/null
@@ -1,180 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 Oleg Shparber
-** Contact: http://zealdocs.org/contact.html
-**
-** 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 .
-**
-****************************************************************************/
-
-#include "settings.h"
-
-#include
-#include
-#include
-#include
-#include
-#include
-
-#ifdef USE_WEBENGINE
-#include
-#define QWebSettings QWebEngineSettings
-#else
-#include
-#endif
-
-namespace {
-// Configuration file groups
-const char GroupBrowser[] = "browser";
-const char GroupDocsets[] = "docsets";
-const char GroupGlobalShortcuts[] = "global_shortcuts";
-const char GroupInternal[] = "internal";
-const char GroupState[] = "state";
-const char GroupProxy[] = "proxy";
-}
-
-using namespace Zeal::Core;
-
-Settings::Settings(QObject *parent) :
- QObject(parent),
- #ifndef PORTABLE_BUILD
- m_settings(new QSettings(this))
- #else
- m_settings(new QSettings(QCoreApplication::applicationDirPath() + QLatin1String("/zeal.ini"),
- QSettings::IniFormat, this))
- #endif
-{
- /// TODO: Move to user style sheet (related to #268)
-#ifndef USE_WEBENGINE
- QWebSettings::globalSettings()->setUserStyleSheetUrl(QStringLiteral("qrc:///browser/highlight.css"));
-#endif
-
- load();
-}
-
-Settings::~Settings()
-{
- save();
-}
-
-void Settings::load()
-{
- /// TODO: Put everything in groups
- startMinimized = m_settings->value(QStringLiteral("start_minimized"), false).toBool();
- checkForUpdate = m_settings->value(QStringLiteral("check_for_update"), true).toBool();
-
- showSystrayIcon = m_settings->value(QStringLiteral("show_systray_icon"), true).toBool();
- minimizeToSystray = m_settings->value(QStringLiteral("minimize_to_systray"), false).toBool();
- hideOnClose = m_settings->value(QStringLiteral("hide_on_close"), false).toBool();
-
- m_settings->beginGroup(GroupGlobalShortcuts);
-#ifndef Q_OS_OSX
- showShortcut = m_settings->value(QStringLiteral("show"), QStringLiteral("Meta+Z")).value();
-#else
- showShortcut = m_settings->value(QStringLiteral("show"), QStringLiteral("Alt+Space")).value();
-#endif
- m_settings->endGroup();
-
- m_settings->beginGroup(GroupBrowser);
- minimumFontSize = m_settings->value(QStringLiteral("minimum_font_size"),
- QWebSettings::globalSettings()->fontSize(QWebSettings::MinimumFontSize)).toInt();
- m_settings->endGroup();
-
- m_settings->beginGroup(GroupProxy);
- proxyType = static_cast(m_settings->value(QStringLiteral("type"),
- ProxyType::System).toUInt());
- proxyHost = m_settings->value(QStringLiteral("host")).toString();
- proxyPort = m_settings->value(QStringLiteral("port"), 0).toInt();
- proxyAuthenticate = m_settings->value(QStringLiteral("authenticate"), false).toBool();
- proxyUserName = m_settings->value(QStringLiteral("username")).toString();
- proxyPassword = m_settings->value(QStringLiteral("password")).toString();
- m_settings->endGroup();
-
- m_settings->beginGroup(GroupDocsets);
- if (m_settings->contains(QStringLiteral("path"))) {
- docsetPath = m_settings->value(QStringLiteral("path")).toString();
- } else {
-#ifndef PORTABLE_BUILD
- docsetPath = QStandardPaths::writableLocation(QStandardPaths::DataLocation)
- + QLatin1String("/docsets");
-#else
- docsetPath = QCoreApplication::applicationDirPath() + QLatin1String("/docsets");
-#endif
- QDir().mkpath(docsetPath);
- }
- m_settings->endGroup();
-
- m_settings->beginGroup(GroupState);
- windowGeometry = m_settings->value(QStringLiteral("window_geometry")).toByteArray();
- splitterGeometry = m_settings->value(QStringLiteral("splitter_geometry")).toByteArray();
- m_settings->endGroup();
-
- m_settings->beginGroup(GroupInternal);
- installId = m_settings->value(QStringLiteral("install_id"),
- // Avoid curly braces (QTBUG-885)
- QUuid::createUuid().toString().mid(1, 36)).toString();
- version = m_settings->value(QStringLiteral("version"),
- QCoreApplication::applicationVersion()).toString();
- m_settings->endGroup();
-}
-
-void Settings::save()
-{
- /// TODO: Put everything in groups
- m_settings->setValue(QStringLiteral("start_minimized"), startMinimized);
- m_settings->setValue(QStringLiteral("check_for_update"), checkForUpdate);
-
- m_settings->setValue(QStringLiteral("show_systray_icon"), showSystrayIcon);
- m_settings->setValue(QStringLiteral("minimize_to_systray"), minimizeToSystray);
- m_settings->setValue(QStringLiteral("hide_on_close"), hideOnClose);
-
- m_settings->beginGroup(GroupGlobalShortcuts);
- m_settings->setValue(QStringLiteral("show"), showShortcut);
- m_settings->endGroup();
-
- m_settings->beginGroup(GroupBrowser);
- m_settings->setValue(QStringLiteral("minimum_font_size"), minimumFontSize);
- m_settings->endGroup();
-
- m_settings->beginGroup(GroupProxy);
- m_settings->setValue(QStringLiteral("type"), proxyType);
- m_settings->setValue(QStringLiteral("host"), proxyHost);
- m_settings->setValue(QStringLiteral("port"), proxyPort);
- m_settings->setValue(QStringLiteral("authenticate"), proxyAuthenticate);
- m_settings->setValue(QStringLiteral("username"), proxyUserName);
- m_settings->setValue(QStringLiteral("password"), proxyPassword);
- m_settings->endGroup();
-
-#ifndef PORTABLE_BUILD
- m_settings->beginGroup(GroupDocsets);
- m_settings->setValue(QStringLiteral("path"), docsetPath);
- m_settings->endGroup();
-#endif
-
- m_settings->beginGroup(GroupState);
- m_settings->setValue(QStringLiteral("window_geometry"), windowGeometry);
- m_settings->setValue(QStringLiteral("splitter_geometry"), splitterGeometry);
- m_settings->endGroup();
-
- m_settings->beginGroup(GroupInternal);
- m_settings->setValue(QStringLiteral("install_id"), installId);
- m_settings->setValue(QStringLiteral("version"), QCoreApplication::applicationVersion());
- m_settings->endGroup();
-
- m_settings->sync();
-
- emit updated();
-}
diff --git a/src/core/application.cpp b/src/libs/core/application.cpp
similarity index 79%
rename from src/core/application.cpp
rename to src/libs/core/application.cpp
index 95c50aabc..c2f500c5c 100644
--- a/src/core/application.cpp
+++ b/src/libs/core/application.cpp
@@ -1,7 +1,7 @@
/****************************************************************************
**
-** Copyright (C) 2015 Oleg Shparber
-** Contact: http://zealdocs.org/contact.html
+** Copyright (C) 2015-2016 Oleg Shparber
+** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
@@ -16,7 +16,7 @@
** 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 .
+** along with Zeal. If not, see .
**
****************************************************************************/
@@ -24,21 +24,21 @@
#include "extractor.h"
#include "settings.h"
-#include "registry/docsetregistry.h"
-#include "registry/searchquery.h"
-#include "ui/mainwindow.h"
-#include "util/version.h"
+
+#include
+#include
+#include
+#include
#include
#include
#include
#include
-#include
-#include
#include
#include
#include
#include
+#include
#include
#include
@@ -46,18 +46,12 @@ using namespace Zeal;
using namespace Zeal::Core;
namespace {
-const char LocalServerName[] = "ZealLocalServer";
const char ReleasesApiUrl[] = "http://api.zealdocs.org/v1/releases";
}
Application *Application::m_instance = nullptr;
Application::Application(QObject *parent) :
- Application(SearchQuery(), parent)
-{
-}
-
-Application::Application(const SearchQuery &query, QObject *parent) :
QObject(parent)
{
// Ensure only one instance of Application
@@ -65,35 +59,11 @@ Application::Application(const SearchQuery &query, QObject *parent) :
m_instance = this;
m_settings = new Settings(this);
- m_localServer = new QLocalServer(this);
m_networkManager = new QNetworkAccessManager(this);
- m_extractorThread = new QThread(this);
- m_extractor = new Extractor();
-
- m_docsetRegistry = new DocsetRegistry();
- m_docsetRegistry->init(m_settings->docsetPath);
-
- m_mainWindow = new MainWindow(this);
-
- // Server for detecting already running instances
- connect(m_localServer, &QLocalServer::newConnection, [this]() {
- QLocalSocket *connection = m_localServer->nextPendingConnection();
- // Wait a little while the other side writes the bytes
- connection->waitForReadyRead();
- if (connection->bytesAvailable()) {
- QDataStream in(connection);
- Zeal::SearchQuery query;
- in >> query;
- m_mainWindow->bringToFront(query);
- } else {
- m_mainWindow->bringToFront();
- }
- });
- /// TODO: Verify if removeServer() is needed
- QLocalServer::removeServer(LocalServerName); // remove in case previous instance crashed
- m_localServer->listen(LocalServerName);
// Extractor setup
+ m_extractorThread = new QThread(this);
+ m_extractor = new Extractor();
m_extractor->moveToThread(m_extractorThread);
m_extractorThread->start();
connect(m_extractor, &Extractor::completed, this, &Application::extractionCompleted);
@@ -103,10 +73,19 @@ Application::Application(const SearchQuery &query, QObject *parent) :
connect(m_settings, &Settings::updated, this, &Application::applySettings);
applySettings();
- if (!query.isEmpty())
- m_mainWindow->bringToFront(query);
- else if (!m_settings->startMinimized)
+ m_docsetRegistry = new Registry::DocsetRegistry();
+ m_docsetRegistry->init(m_settings->docsetPath);
+
+ m_mainWindow = new MainWindow(this);
+
+ if (m_settings->startMinimized) {
+ if (m_settings->showSystrayIcon && m_settings->minimizeToSystray)
+ return;
+
+ m_mainWindow->showMinimized();
+ } else {
m_mainWindow->show();
+ }
}
Application::~Application()
@@ -118,9 +97,14 @@ Application::~Application()
delete m_docsetRegistry;
}
-QString Application::localServerName()
+/*!
+ * \internal
+ * \brief Returns a pointer to the Core::Application instance.
+ * \return A pointer or \c nullptr, if no instance has been created.
+ */
+Application *Application::instance()
{
- return LocalServerName;
+ return m_instance;
}
QNetworkAccessManager *Application::networkManager() const
@@ -133,9 +117,19 @@ Settings *Application::settings() const
return m_settings;
}
-DocsetRegistry *Application::docsetRegistry()
+Registry::DocsetRegistry *Application::docsetRegistry()
{
- return m_instance->m_docsetRegistry;
+ return m_docsetRegistry;
+}
+
+void Application::executeQuery(const Registry::SearchQuery &query, bool preventActivation)
+{
+ m_mainWindow->search(query);
+
+ if (preventActivation)
+ return;
+
+ m_mainWindow->bringToFront();
}
void Application::extract(const QString &filePath, const QString &destination, const QString &root)
@@ -148,13 +142,13 @@ void Application::extract(const QString &filePath, const QString &destination, c
QNetworkReply *Application::download(const QUrl &url)
{
static const QString ua = userAgent();
- static const QString uaJson = userAgentJson();
+ static const QByteArray uaJson = userAgentJson().toUtf8();
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::UserAgentHeader, ua);
if (url.host().endsWith(QLatin1String(".zealdocs.org", Qt::CaseInsensitive)))
- request.setRawHeader("X-Zeal-User-Agent", uaJson.toUtf8());
+ request.setRawHeader("X-Zeal-User-Agent", uaJson);
return m_networkManager->get(request);
}
@@ -165,7 +159,7 @@ QNetworkReply *Application::download(const QUrl &url)
Performs a check whether a new Zeal version is available. Setting \a quiet to true supresses
error and "you are using the latest version" message boxes.
*/
-void Application::checkForUpdate(bool quiet)
+void Application::checkForUpdates(bool quiet)
{
QNetworkReply *reply = download(QUrl(ReleasesApiUrl));
connect(reply, &QNetworkReply::finished, this, [this, quiet]() {
@@ -222,12 +216,12 @@ void Application::applySettings()
QString Application::userAgent()
{
- return QString("Zeal/%1").arg(QCoreApplication::applicationVersion());
+ return QStringLiteral("Zeal/%1").arg(QCoreApplication::applicationVersion());
}
QString Application::userAgentJson() const
{
- /// TODO: [Qt 5.4] Remove else branch
+ // TODO: [Qt 5.4] Remove else branch
#if QT_VERSION >= 0x050400
QJsonObject app = {
{QStringLiteral("version"), QCoreApplication::applicationVersion()},
@@ -290,5 +284,5 @@ QString Application::userAgentJson() const
#endif // QT_VERSION >= 0x050400
- return QJsonDocument(ua).toJson(QJsonDocument::Compact);
+ return QString::fromUtf8(QJsonDocument(ua).toJson(QJsonDocument::Compact));
}
diff --git a/src/core/application.h b/src/libs/core/application.h
similarity index 82%
rename from src/core/application.h
rename to src/libs/core/application.h
index 7d2fdf594..749fc8ca7 100644
--- a/src/core/application.h
+++ b/src/libs/core/application.h
@@ -1,7 +1,7 @@
/****************************************************************************
**
-** Copyright (C) 2015 Oleg Shparber
-** Contact: http://zealdocs.org/contact.html
+** Copyright (C) 2015-2016 Oleg Shparber
+** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
@@ -16,7 +16,7 @@
** 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 .
+** along with Zeal. If not, see .
**
****************************************************************************/
@@ -25,18 +25,18 @@
#include
-class QLocalServer;
-
-class MainWindow;
-
class QNetworkAccessManager;
class QNetworkReply;
class QThread;
+class MainWindow;
+
namespace Zeal {
+namespace Registry {
class DocsetRegistry;
class SearchQuery;
+} // namespace Registry
namespace Core {
@@ -48,20 +48,20 @@ class Application : public QObject
Q_OBJECT
public:
explicit Application(QObject *parent = nullptr);
- explicit Application(const SearchQuery &query, QObject *parent = nullptr);
~Application() override;
- static QString localServerName();
+ static Application *instance();
QNetworkAccessManager *networkManager() const;
Settings *settings() const;
- static DocsetRegistry *docsetRegistry();
+ Registry::DocsetRegistry *docsetRegistry();
public slots:
+ void executeQuery(const Registry::SearchQuery &query, bool preventActivation);
void extract(const QString &filePath, const QString &destination, const QString &root = QString());
QNetworkReply *download(const QUrl &url);
- void checkForUpdate(bool quiet = false);
+ void checkForUpdates(bool quiet = false);
signals:
void extractionCompleted(const QString &filePath);
@@ -81,13 +81,12 @@ private slots:
Settings *m_settings = nullptr;
- QLocalServer *m_localServer = nullptr;
QNetworkAccessManager *m_networkManager = nullptr;
QThread *m_extractorThread = nullptr;
Extractor *m_extractor = nullptr;
- DocsetRegistry *m_docsetRegistry = nullptr;
+ Registry::DocsetRegistry *m_docsetRegistry = nullptr;
MainWindow *m_mainWindow = nullptr;
};
diff --git a/src/core/core.pri b/src/libs/core/core.pri
similarity index 65%
rename from src/core/core.pri
rename to src/libs/core/core.pri
index a9823b8e2..0f7c62ea3 100644
--- a/src/core/core.pri
+++ b/src/libs/core/core.pri
@@ -1,5 +1,6 @@
-HEADERS += $$files($$PWD/*.h)
-SOURCES += $$files($$PWD/*.cpp)
+ZEAL_LIB_NAME = Core
+
+QT += network
unix:!macx {
CONFIG += link_pkgconfig
diff --git a/src/libs/core/core.pro b/src/libs/core/core.pro
new file mode 100644
index 000000000..1c61c1c65
--- /dev/null
+++ b/src/libs/core/core.pro
@@ -0,0 +1,4 @@
+include($$ZEAL_LIBRARY_PRI)
+
+HEADERS += $$files(*.h)
+SOURCES += $$files(*.cpp)
diff --git a/src/core/extractor.cpp b/src/libs/core/extractor.cpp
similarity index 84%
rename from src/core/extractor.cpp
rename to src/libs/core/extractor.cpp
index 1fd38a4ef..97c68e014 100644
--- a/src/core/extractor.cpp
+++ b/src/libs/core/extractor.cpp
@@ -1,7 +1,7 @@
/****************************************************************************
**
-** Copyright (C) 2015 Oleg Shparber
-** Contact: http://zealdocs.org/contact.html
+** Copyright (C) 2015-2016 Oleg Shparber
+** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
@@ -16,7 +16,7 @@
** 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 .
+** along with Zeal. If not, see .
**
****************************************************************************/
@@ -60,10 +60,15 @@ void Extractor::extract(const QString &filePath, const QString &destination, con
if (!root.isEmpty())
destinationDir = destinationDir.absoluteFilePath(root);
- /// TODO: Do not strip root directory in archive if it equals to 'root'
+ // TODO: Do not strip root directory in archive if it equals to 'root'
archive_entry *entry;
while (archive_read_next_header(info.archiveHandle, &entry) == ARCHIVE_OK) {
- QString pathname = archive_entry_pathname(entry);
+#ifndef Q_OS_WIN32
+ QString pathname = QString::fromUtf8(archive_entry_pathname(entry));
+#else
+ // 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())
pathname.remove(0, pathname.indexOf(QLatin1String("/")) + 1);
archive_entry_set_pathname(entry, qPrintable(destinationDir.absoluteFilePath(pathname)));
diff --git a/src/core/extractor.h b/src/libs/core/extractor.h
similarity index 91%
rename from src/core/extractor.h
rename to src/libs/core/extractor.h
index 605384ca0..1d09191c9 100644
--- a/src/core/extractor.h
+++ b/src/libs/core/extractor.h
@@ -1,7 +1,7 @@
/****************************************************************************
**
-** Copyright (C) 2015 Oleg Shparber
-** Contact: http://zealdocs.org/contact.html
+** Copyright (C) 2015-2016 Oleg Shparber
+** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
@@ -16,7 +16,7 @@
** 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 .
+** along with Zeal. If not, see .
**
****************************************************************************/
diff --git a/src/libs/core/localserver.cpp b/src/libs/core/localserver.cpp
new file mode 100644
index 000000000..efac2193e
--- /dev/null
+++ b/src/libs/core/localserver.cpp
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2015-2016 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 .
+**
+****************************************************************************/
+
+#include "localserver.h"
+
+#include
+
+#include
+#include
+#include
+#include
+
+using namespace Zeal;
+using namespace Zeal::Core;
+
+namespace {
+const char LocalServerName[] = "ZealLocalServer";
+}
+
+LocalServer::LocalServer(QObject *parent)
+ : QObject(parent)
+ , m_localServer(new QLocalServer(this))
+{
+ connect(m_localServer, &QLocalServer::newConnection, [this]() {
+ QScopedPointer connection(m_localServer->nextPendingConnection());
+ // Wait while the other side writes the data.
+ if (!connection->waitForReadyRead(500))
+ return;
+
+ QDataStream in(connection.data());
+ Registry::SearchQuery query;
+ bool preventActivation;
+ in >> query;
+ in >> preventActivation;
+
+ emit newQuery(query, preventActivation);
+ });
+}
+
+/*!
+ * \internal
+ * \brief Returns the error message about the last occured error.
+ * \return Human-readable error message, or an empty string.
+ */
+QString LocalServer::errorString() const
+{
+ return m_localServer->errorString();
+}
+
+/*!
+ * \internal
+ * \brief Instructs server to listen for incoming connections.
+ * \param force If \c true, an attempt to remove socket file will be made. No effect on Windows.
+ * \return \c true if successful, \c false otherwise.
+ */
+bool LocalServer::start(bool force)
+{
+#ifndef Q_OS_WIN32
+ if (force && !QLocalServer::removeServer(LocalServerName)) {
+ return false;
+ }
+#else
+ Q_UNUSED(force)
+#endif
+
+ return m_localServer->listen(LocalServerName);
+}
+
+/*!
+ * \internal
+ * \brief Sends \a query to an already running application instance, if it exists.
+ * \param query A query to execute search with.
+ * \param preventActivation If \c true, application window will not activated.
+ * \return \c true if communication with another instance has been successful.
+ */
+bool LocalServer::sendQuery(const Registry::SearchQuery &query, bool preventActivation)
+{
+ QScopedPointer socket(new QLocalSocket());
+ socket->connectToServer(LocalServerName);
+
+ if (!socket->waitForConnected(500))
+ return false;
+
+ QDataStream out(socket.data());
+ out << query;
+ out << preventActivation;
+ socket->flush();
+ return true;
+}
diff --git a/src/libs/core/localserver.h b/src/libs/core/localserver.h
new file mode 100644
index 000000000..034dea028
--- /dev/null
+++ b/src/libs/core/localserver.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2015-2016 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 .
+**
+****************************************************************************/
+
+#ifndef ZEAL_CORE_LOCALSERVER_H
+#define ZEAL_CORE_LOCALSERVER_H
+
+#include
+
+class QLocalServer;
+
+namespace Zeal {
+
+namespace Registry {
+class SearchQuery;
+} // namespace Registry
+
+namespace Core {
+
+class LocalServer : public QObject
+{
+ Q_OBJECT
+public:
+ explicit LocalServer(QObject *parent = 0);
+
+ QString errorString() const;
+
+ bool start(bool force = false);
+
+ static bool sendQuery(const Registry::SearchQuery &query, bool preventActivation);
+
+signals:
+ void newQuery(const Registry::SearchQuery &query, bool preventActivation);
+
+private:
+ QLocalServer *m_localServer = nullptr;
+};
+
+} // namespace Core
+} // namespace Zeal
+
+#endif // ZEAL_CORE_LOCALSERVER_H
diff --git a/src/libs/core/settings.cpp b/src/libs/core/settings.cpp
new file mode 100644
index 000000000..cffdc9549
--- /dev/null
+++ b/src/libs/core/settings.cpp
@@ -0,0 +1,232 @@
+/****************************************************************************
+**
+** Copyright (C) 2015-2016 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 .
+**
+****************************************************************************/
+
+#include "settings.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef USE_WEBENGINE
+#include
+typedef QWebEngineSettings QWebSettings;
+#else
+#include
+#endif
+
+namespace {
+// Configuration file groups
+const char GroupBrowser[] = "browser";
+const char GroupDocsets[] = "docsets";
+const char GroupGlobalShortcuts[] = "global_shortcuts";
+const char GroupTabs[] = "tabs";
+const char GroupInternal[] = "internal";
+const char GroupState[] = "state";
+const char GroupProxy[] = "proxy";
+}
+
+using namespace Zeal::Core;
+
+Settings::Settings(QObject *parent) :
+ QObject(parent)
+{
+ // TODO: Move to user style sheet (related to #268)
+#ifndef USE_WEBENGINE
+ QWebSettings::globalSettings()
+ ->setUserStyleSheetUrl(QUrl(QStringLiteral("qrc:///browser/highlight.css")));
+#endif
+
+ load();
+}
+
+Settings::~Settings()
+{
+ save();
+}
+
+void Settings::load()
+{
+ QScopedPointer settings(qsettings());
+ migrate(settings.data());
+
+ // TODO: Put everything in groups
+ startMinimized = settings->value(QStringLiteral("start_minimized"), false).toBool();
+ checkForUpdate = settings->value(QStringLiteral("check_for_update"), true).toBool();
+
+ showSystrayIcon = settings->value(QStringLiteral("show_systray_icon"), true).toBool();
+ minimizeToSystray = settings->value(QStringLiteral("minimize_to_systray"), false).toBool();
+ hideOnClose = settings->value(QStringLiteral("hide_on_close"), false).toBool();
+
+ settings->beginGroup(GroupGlobalShortcuts);
+ showShortcut = settings->value(QStringLiteral("show")).value();
+ settings->endGroup();
+
+ settings->beginGroup(GroupTabs);
+ openNewTabAfterActive = settings->value(QStringLiteral("open_new_tab_after_active"), false).toBool();
+ settings->endGroup();
+
+ settings->beginGroup(GroupBrowser);
+ minimumFontSize = settings->value(QStringLiteral("minimum_font_size"),
+ QWebSettings::globalSettings()->fontSize(QWebSettings::MinimumFontSize)).toInt();
+ QWebSettings::globalSettings()->setFontSize(QWebSettings::MinimumFontSize, minimumFontSize);
+ settings->endGroup();
+
+ settings->beginGroup(GroupProxy);
+ proxyType = static_cast(settings->value(QStringLiteral("type"),
+ ProxyType::System).toUInt());
+ proxyHost = settings->value(QStringLiteral("host")).toString();
+ proxyPort = static_cast(settings->value(QStringLiteral("port"), 0).toUInt());
+ proxyAuthenticate = settings->value(QStringLiteral("authenticate"), false).toBool();
+ proxyUserName = settings->value(QStringLiteral("username")).toString();
+ proxyPassword = settings->value(QStringLiteral("password")).toString();
+ settings->endGroup();
+
+ settings->beginGroup(GroupDocsets);
+ if (settings->contains(QStringLiteral("path"))) {
+ docsetPath = settings->value(QStringLiteral("path")).toString();
+ } else {
+#ifndef PORTABLE_BUILD
+ docsetPath = QStandardPaths::writableLocation(QStandardPaths::DataLocation)
+ + QLatin1String("/docsets");
+#else
+ docsetPath = QCoreApplication::applicationDirPath() + QLatin1String("/docsets");
+#endif
+ QDir().mkpath(docsetPath);
+ }
+ settings->endGroup();
+
+ settings->beginGroup(GroupState);
+ windowGeometry = settings->value(QStringLiteral("window_geometry")).toByteArray();
+ verticalSplitterGeometry = settings->value(QStringLiteral("splitter_geometry")).toByteArray();
+ tocSplitterState = settings->value(QStringLiteral("toc_splitter_state")).toByteArray();
+ settings->endGroup();
+
+ settings->beginGroup(GroupInternal);
+ installId = settings->value(QStringLiteral("install_id"),
+ // Avoid curly braces (QTBUG-885)
+ QUuid::createUuid().toString().mid(1, 36)).toString();
+ settings->endGroup();
+}
+
+void Settings::save()
+{
+ QScopedPointer settings(qsettings());
+
+ // TODO: Put everything in groups
+ settings->setValue(QStringLiteral("start_minimized"), startMinimized);
+ settings->setValue(QStringLiteral("check_for_update"), checkForUpdate);
+
+ settings->setValue(QStringLiteral("show_systray_icon"), showSystrayIcon);
+ settings->setValue(QStringLiteral("minimize_to_systray"), minimizeToSystray);
+ settings->setValue(QStringLiteral("hide_on_close"), hideOnClose);
+
+ settings->beginGroup(GroupGlobalShortcuts);
+ settings->setValue(QStringLiteral("show"), showShortcut);
+ settings->endGroup();
+
+ settings->beginGroup(GroupTabs);
+ settings->setValue(QStringLiteral("open_new_tab_after_active"), openNewTabAfterActive);
+ settings->endGroup();
+
+ settings->beginGroup(GroupBrowser);
+ settings->setValue(QStringLiteral("minimum_font_size"), minimumFontSize);
+ settings->endGroup();
+
+ settings->beginGroup(GroupProxy);
+ settings->setValue(QStringLiteral("type"), proxyType);
+ settings->setValue(QStringLiteral("host"), proxyHost);
+ settings->setValue(QStringLiteral("port"), proxyPort);
+ settings->setValue(QStringLiteral("authenticate"), proxyAuthenticate);
+ settings->setValue(QStringLiteral("username"), proxyUserName);
+ settings->setValue(QStringLiteral("password"), proxyPassword);
+ settings->endGroup();
+
+#ifndef PORTABLE_BUILD
+ settings->beginGroup(GroupDocsets);
+ settings->setValue(QStringLiteral("path"), docsetPath);
+ settings->endGroup();
+#endif
+
+ settings->beginGroup(GroupState);
+ settings->setValue(QStringLiteral("window_geometry"), windowGeometry);
+ settings->setValue(QStringLiteral("splitter_geometry"), verticalSplitterGeometry);
+ settings->setValue(QStringLiteral("toc_splitter_state"), tocSplitterState);
+ settings->endGroup();
+
+ settings->beginGroup(GroupInternal);
+ settings->setValue(QStringLiteral("install_id"), installId);
+ // Version of configuration file format, should match Zeal version. Used for migration rules.
+ settings->setValue(QStringLiteral("version"), QCoreApplication::applicationVersion());
+ settings->endGroup();
+
+ settings->sync();
+
+ emit updated();
+}
+
+/*!
+ * \internal
+ * \brief Migrates settings from older application versions.
+ * \param settings QSettings object to update.
+ *
+ * The settings migration process relies on 'internal/version' option, that was introduced in the
+ * release 0.2.0, so a missing option indicates pre-0.2 release.
+ */
+void Settings::migrate(QSettings *settings) const
+{
+ settings->beginGroup(GroupInternal);
+ const QString version = settings->value(QStringLiteral("version")).toString();
+ settings->endGroup();
+
+ //
+ // Pre 0.3
+ //
+
+ // Unset 'state/splitter_geometry', because custom styles were removed.
+ if (version.isEmpty() || version.startsWith(QLatin1String("0.2"))) {
+ settings->beginGroup(GroupState);
+ settings->remove(QStringLiteral("splitter_geometry"));
+ settings->endGroup();
+ }
+}
+
+/*!
+ * \internal
+ * \brief Returns an initialized QSettings object.
+ * \param parent Optional parent object.
+ * \return QSettings object.
+ *
+ * QSettings is initialized according to build options, e.g. standard vs portable.
+ * Caller is responsible for deleting the returned object.
+ */
+QSettings *Settings::qsettings(QObject *parent)
+{
+#ifndef PORTABLE_BUILD
+ return new QSettings(parent);
+#else
+ return new QSettings(QCoreApplication::applicationDirPath() + QLatin1String("/zeal.ini"),
+ QSettings::IniFormat, parent);
+#endif
+}
diff --git a/src/core/settings.h b/src/libs/core/settings.h
similarity index 73%
rename from src/core/settings.h
rename to src/libs/core/settings.h
index a7c36c8d5..50fc37ae5 100644
--- a/src/core/settings.h
+++ b/src/libs/core/settings.h
@@ -1,7 +1,7 @@
/****************************************************************************
**
-** Copyright (C) 2015 Oleg Shparber
-** Contact: http://zealdocs.org/contact.html
+** Copyright (C) 2015-2016 Oleg Shparber
+** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
@@ -16,7 +16,7 @@
** 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 .
+** along with Zeal. If not, see .
**
****************************************************************************/
@@ -35,13 +35,14 @@ class Settings : public QObject
{
Q_OBJECT
public:
- /// NOTE: This public members are here just for simplification and should go away
- /// once a more advanced settings management come in place.
+ /* This public members are here just for simplification and should go away
+ * once a more advanced settings management come in place.
+ */
// Startup
bool startMinimized;
bool checkForUpdate;
- /// TODO: bool restoreLastState;
+ // TODO: bool restoreLastState;
// System Tray
bool showSystrayIcon;
@@ -50,12 +51,15 @@ class Settings : public QObject
// Global Shortcuts
QKeySequence showShortcut;
- /// TODO: QKeySequence searchSelectedTextShortcut;
+ // TODO: QKeySequence searchSelectedTextShortcut;
+
+ // Tabs Behavior
+ bool openNewTabAfterActive;
// Browser
int minimumFontSize;
- /// TODO: bool askOnExternalLink;
- /// TODO: QString customCss;
+ // TODO: bool askOnExternalLink;
+ // TODO: QString customCss;
// Network
enum ProxyType : unsigned int {
@@ -63,6 +67,9 @@ class Settings : public QObject
System,
UserDefined
};
+#if QT_VERSION >= 0x050500
+ Q_ENUM(ProxyType)
+#endif
// Internal
// --------
@@ -70,8 +77,6 @@ class Settings : public QObject
// a settings wipe. It is not attached to user hardware or software, and is sent exclusevely
// to *.zealdocs.org hosts.
QString installId;
- // Version of configuration file format, should match Zeal version. Useful for migration rules.
- QString version;
ProxyType proxyType = ProxyType::System;
QString proxyHost;
@@ -85,7 +90,8 @@ class Settings : public QObject
// State
QByteArray windowGeometry;
- QByteArray splitterGeometry;
+ QByteArray verticalSplitterGeometry;
+ QByteArray tocSplitterState;
explicit Settings(QObject *parent = nullptr);
~Settings() override;
@@ -98,12 +104,16 @@ public slots:
void updated();
private:
- QSettings *m_settings = nullptr;
+ void migrate(QSettings *settings) const;
+
+ static QSettings *qsettings(QObject *parent = nullptr);
};
} // namespace Core
} // namespace Zeal
+#if QT_VERSION < 0x050500
Q_DECLARE_METATYPE(Zeal::Core::Settings::ProxyType)
+#endif
#endif // SETTINGS_H
diff --git a/src/libs/libs.pro b/src/libs/libs.pro
new file mode 100644
index 000000000..a323f5d48
--- /dev/null
+++ b/src/libs/libs.pro
@@ -0,0 +1,7 @@
+TEMPLATE = subdirs
+
+SUBDIRS += \
+ core \
+ registry \
+ ui \
+ util
diff --git a/src/ui/searchitemstyle.h b/src/libs/registry/cancellationtoken.cpp
similarity index 66%
rename from src/ui/searchitemstyle.h
rename to src/libs/registry/cancellationtoken.cpp
index ef87b5e18..b767c4c52 100644
--- a/src/ui/searchitemstyle.h
+++ b/src/libs/registry/cancellationtoken.cpp
@@ -1,8 +1,7 @@
/****************************************************************************
**
-** Copyright (C) 2015 Oleg Shparber
-** Copyright (C) 2013-2014 Jerzy Kozera
-** Contact: http://zealdocs.org/contact.html
+** Copyright (C) 2015 Artur Spychaj
+** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
@@ -21,16 +20,21 @@
**
****************************************************************************/
-#ifndef SEARCHITEMSTYLE_H
-#define SEARCHITEMSTYLE_H
+#include "cancellationtoken.h"
-#include
+using namespace Zeal::Registry;
-class ZealSearchItemStyle : public QProxyStyle
+CancellationToken::CancellationToken()
{
-public:
- QRect subElementRect(SubElement element, const QStyleOption *option,
- const QWidget *widget) const override;
-};
+ m_cancelled = QSharedPointer(new bool(false));
+}
-#endif // SEARCHITEMSTYLE_H
+void CancellationToken::cancel()
+{
+ *m_cancelled = true;
+}
+
+bool CancellationToken::isCanceled() const
+{
+ return *m_cancelled;
+}
diff --git a/src/ui/networkaccessmanager.h b/src/libs/registry/cancellationtoken.h
similarity index 60%
rename from src/ui/networkaccessmanager.h
rename to src/libs/registry/cancellationtoken.h
index ac7900bda..585a87e87 100644
--- a/src/ui/networkaccessmanager.h
+++ b/src/libs/registry/cancellationtoken.h
@@ -1,8 +1,7 @@
/****************************************************************************
**
-** Copyright (C) 2015 Oleg Shparber
-** Copyright (C) 2013-2014 Jerzy Kozera
-** Contact: http://zealdocs.org/contact.html
+** Copyright (C) 2015 Artur Spychaj
+** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
@@ -21,23 +20,30 @@
**
****************************************************************************/
-#ifndef NETWORKACCESSMANAGER_H
-#define NETWORKACCESSMANAGER_H
+#ifndef CANCELLATIONTOKEN_H
+#define CANCELLATIONTOKEN_H
-#include
+#include
namespace Zeal {
+namespace Registry {
-class NetworkAccessManager : public QNetworkAccessManager
+/// Token that stores whether cancel was called on it.
+/// In async code can be used to check if another thread called cancel.
+struct CancellationToken
{
- Q_OBJECT
public:
- explicit NetworkAccessManager(QObject *parent = nullptr);
+ CancellationToken();
+ bool isCanceled() const;
+ void cancel();
- QNetworkReply *createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &req,
- QIODevice *outgoingData = nullptr) override;
+private:
+ QSharedPointer m_cancelled;
};
+} // namespace Registry
} // namespace Zeal
-#endif // NETWORKACCESSMANAGER_H
+Q_DECLARE_METATYPE(Zeal::Registry::CancellationToken)
+
+#endif // CANCELLATIONTOKEN_H
diff --git a/src/registry/docset.cpp b/src/libs/registry/docset.cpp
similarity index 68%
rename from src/registry/docset.cpp
rename to src/libs/registry/docset.cpp
index 6256dc5c3..c6e7395da 100644
--- a/src/registry/docset.cpp
+++ b/src/libs/registry/docset.cpp
@@ -1,8 +1,8 @@
/****************************************************************************
**
-** Copyright (C) 2015 Oleg Shparber
+** Copyright (C) 2015-2016 Oleg Shparber
** Copyright (C) 2013-2014 Jerzy Kozera
-** Contact: http://zealdocs.org/contact.html
+** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
@@ -17,25 +17,28 @@
** 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 .
+** along with Zeal. If not, see .
**
****************************************************************************/
#include "docset.h"
-#include "searchquery.h"
-#include "util/plist.h"
+#include "cancellationtoken.h"
+#include "searchresult.h"
+
+#include
#include
#include
#include
#include
+#include
+#include
#include
#include
-#include
#include
-using namespace Zeal;
+using namespace Zeal::Registry;
namespace {
const char IndexNamePrefix[] = "__zi_name"; // zi - Zeal index
@@ -43,14 +46,14 @@ const char IndexNameVersion[] = "0001"; // Current index version
namespace InfoPlist {
const char CFBundleName[] = "CFBundleName";
-const char CFBundleIdentifier[] = "CFBundleIdentifier";
+//const char CFBundleIdentifier[] = "CFBundleIdentifier";
const char DashDocSetFamily[] = "DashDocSetFamily";
const char DashDocSetKeyword[] = "DashDocSetKeyword";
const char DashDocSetPluginKeyword[] = "DashDocSetPluginKeyword";
const char DashIndexFilePath[] = "dashIndexFilePath";
const char DocSetPlatformFamily[] = "DocSetPlatformFamily";
-const char IsDashDocset[] = "isDashDocset";
-const char IsJavaScriptEnabled[] = "isJavaScriptEnabled";
+//const char IsDashDocset[] = "isDashDocset";
+//const char IsJavaScriptEnabled[] = "isJavaScriptEnabled";
}
}
@@ -70,13 +73,13 @@ Docset::Docset(const QString &path) :
break;
}
- /// TODO: Report errors here and below
+ // TODO: Report errors here and below
if (!dir.cd(QStringLiteral("Contents")))
return;
- /// TODO: 'info.plist' is invalid according to Apple, and must alsways be 'Info.plist'
- /// https://developer.apple.com/library/mac/documentation/MacOSX/Conceptual/BPRuntimeConfig/
- /// Articles/ConfigFiles.html
+ // TODO: 'info.plist' is invalid according to Apple, and must alsways be 'Info.plist'
+ // https://developer.apple.com/library/mac/documentation/MacOSX/Conceptual/BPRuntimeConfig
+ // /Articles/ConfigFiles.html
Util::Plist plist;
if (dir.exists(QStringLiteral("Info.plist")))
plist.read(dir.absoluteFilePath(QStringLiteral("Info.plist")));
@@ -92,7 +95,7 @@ Docset::Docset(const QString &path) :
// Fallback if meta.json is absent
if (!plist.contains(InfoPlist::CFBundleName)) {
m_name = m_title = plist[InfoPlist::CFBundleName].toString();
- /// TODO: Remove when MainWindow::docsetName() will not use directory name
+ // TODO: Remove when MainWindow::docsetName() will not use directory name
m_name.replace(QLatin1Char(' '), QLatin1Char('_'));
} else {
m_name = QFileInfo(m_path).fileName().remove(QStringLiteral(".docset"));
@@ -104,7 +107,7 @@ Docset::Docset(const QString &path) :
m_title.replace(QLatin1Char('_'), QLatin1Char(' '));
}
- /// TODO: Verify if this is needed
+ // TODO: Verify if this is needed
if (plist.contains(InfoPlist::DashDocSetFamily)
&& plist[InfoPlist::DashDocSetFamily].toString() == QLatin1String("cheatsheet")) {
m_name = m_name + QLatin1String("cheats");
@@ -125,11 +128,12 @@ Docset::Docset(const QString &path) :
createIndex();
- if (!dir.cd(QStringLiteral("Documents")))
+ if (!dir.cd(QStringLiteral("Documents"))) {
+ m_type = Type::Invalid;
return;
+ }
- //
- // Setyp keywords
+ // Setup keywords
if (plist.contains(InfoPlist::DocSetPlatformFamily))
m_keywords << plist[InfoPlist::DocSetPlatformFamily].toString();
@@ -141,19 +145,18 @@ Docset::Docset(const QString &path) :
if (plist.contains(InfoPlist::DashDocSetFamily)) {
const QString kw = plist[InfoPlist::DashDocSetFamily].toString();
- if (kw != QStringLiteral("dashtoc"))
+ if (kw != QLatin1String("dashtoc") && kw != QLatin1String("unsorteddashtoc"))
m_keywords << kw;
}
- /// TODO: Use 'unknown' instead of CFBundleName? (See #383)
- m_keywords << plist.value(InfoPlist::CFBundleName, m_name).toString().toLower();
+ m_keywords.removeDuplicates();
- // Try to find index path if metadata is missing one
- if (m_indexFilePath.isEmpty()) {
- if (plist.contains(InfoPlist::DashIndexFilePath))
- m_indexFilePath = plist[InfoPlist::DashIndexFilePath].toString();
- else if (dir.exists(QStringLiteral("index.html")))
- m_indexFilePath = QStringLiteral("index.html");
+ // Prefer index path provided by the docset over metadata.
+ if (plist.contains(InfoPlist::DashIndexFilePath)) {
+ m_indexFileUrl = createPageUrl(plist[InfoPlist::DashIndexFilePath].toString());
+ } else if (m_indexFileUrl.isEmpty()) {
+ if (dir.exists(QStringLiteral("index.html")))
+ m_indexFileUrl = createPageUrl(QStringLiteral("index.html"));
else
qWarning("Cannot determine index file for docset %s", qPrintable(m_name));
}
@@ -211,9 +214,17 @@ QIcon Docset::icon() const
return m_icon;
}
-QString Docset::indexFilePath() const
+QIcon Docset::symbolTypeIcon(const QString &symbolType) const
{
- return QDir(documentPath()).absoluteFilePath(m_indexFilePath);
+ static const QIcon unknownIcon(QStringLiteral("typeIcon:Unknown.png"));
+
+ const QIcon icon(QStringLiteral("typeIcon:%1.png").arg(symbolType));
+ return icon.availableSizes().isEmpty() ? unknownIcon : icon;
+}
+
+QUrl Docset::indexFileUrl() const
+{
+ return m_indexFileUrl;
}
QMap Docset::symbolCounts() const
@@ -226,83 +237,54 @@ int Docset::symbolCount(const QString &symbolType) const
return m_symbolCounts.value(symbolType);
}
-const QMap &Docset::symbols(const QString &symbolType) const
+const QMap &Docset::symbols(const QString &symbolType) const
{
if (!m_symbols.contains(symbolType))
loadSymbols(symbolType);
return m_symbols[symbolType];
}
-QList Docset::search(const QString &query) const
+QList Docset::search(const QString &query, const CancellationToken &token) const
{
- QList results;
-
- const SearchQuery searchQuery = SearchQuery::fromString(query);
- const QString sanitizedQuery = searchQuery.sanitizedQuery();
-
- if (searchQuery.hasKeywords() && !searchQuery.hasKeywords(m_keywords))
- return results;
+ // Make it safe to use in a SQL query.
+ QString sanitizedQuery = query;
+ sanitizedQuery.replace(QLatin1String("\\"), QLatin1String("\\\\"));
+ sanitizedQuery.replace(QLatin1String("_"), QLatin1String("\\_"));
+ sanitizedQuery.replace(QLatin1String("%"), QLatin1String("\\%"));
+ sanitizedQuery.replace(QLatin1String("'"), QLatin1String("''"));
QString queryStr;
+ if (m_type == Docset::Type::Dash) {
+ queryStr = QStringLiteral("SELECT name, type, path "
+ " FROM searchIndex "
+ "WHERE (name LIKE '%%1%' ESCAPE '\\') "
+ "ORDER BY name COLLATE NOCASE").arg(sanitizedQuery);
+ } else {
+ queryStr = QStringLiteral("SELECT ztokenname, ztypename, zpath, zanchor "
+ " FROM ztoken "
+ "LEFT JOIN ztokenmetainformation "
+ " ON ztoken.zmetainformation = ztokenmetainformation.z_pk "
+ "LEFT JOIN zfilepath "
+ " ON ztokenmetainformation.zfile = zfilepath.z_pk "
+ "LEFT JOIN ztokentype "
+ " ON ztoken.ztokentype = ztokentype.z_pk "
+ "WHERE (ztokenname LIKE '%%1%' ESCAPE '\\') "
+ "ORDER BY ztokenname COLLATE NOCASE").arg(sanitizedQuery);
+ }
- bool withSubStrings = false;
- // %.%1% for long Django docset values like django.utils.http
- // %::%1% for long C++ docset values like std::set
- // %/%1% for long Go docset values like archive/tar
- QString subNames = QStringLiteral(" OR %1 LIKE '%.%2%' ESCAPE '\\'");
- subNames += QLatin1String(" OR %1 LIKE '%::%2%' ESCAPE '\\'");
- subNames += QLatin1String(" OR %1 LIKE '%/%2%' ESCAPE '\\'");
- while (results.size() < 100) {
- QString curQuery = sanitizedQuery;
- QString notQuery; // don't return the same result twice
- if (withSubStrings) {
- // if less than 100 found starting with query, search all substrings
- curQuery = QLatin1Char('%') + sanitizedQuery;
- // don't return 'starting with' results twice
- if (m_type == Docset::Type::Dash)
- notQuery = QString(" AND NOT (name LIKE '%1%' ESCAPE '\\' %2) ").arg(sanitizedQuery, subNames.arg("name", sanitizedQuery));
- else
- notQuery = QString(" AND NOT (ztokenname LIKE '%1%' ESCAPE '\\' %2) ").arg(sanitizedQuery, subNames.arg("ztokenname", sanitizedQuery));
- }
- if (m_type == Docset::Type::Dash) {
- queryStr = QString("SELECT name, type, path "
- " FROM searchIndex "
- "WHERE (name LIKE '%1%' ESCAPE '\\' %3) %2 "
- "ORDER BY name COLLATE NOCASE LIMIT 100")
- .arg(curQuery, notQuery, subNames.arg("name", curQuery));
- } else {
- queryStr = QString("SELECT ztokenname, ztypename, zpath, zanchor "
- " FROM ztoken "
- "JOIN ztokenmetainformation "
- " ON ztoken.zmetainformation = ztokenmetainformation.z_pk "
- "JOIN zfilepath "
- " ON ztokenmetainformation.zfile = zfilepath.z_pk "
- "JOIN ztokentype "
- " ON ztoken.ztokentype = ztokentype.z_pk "
- "WHERE (ztokenname LIKE '%1%' ESCAPE '\\' %3) %2 "
- "ORDER BY ztokenname COLLATE NOCASE LIMIT 100")
- .arg(curQuery, notQuery, subNames.arg("ztokenname", curQuery));
- }
+ // Limit for very short queries.
+ // TODO: Show a notification about the reduced result set.
+ if (query.size() < 3)
+ queryStr += QLatin1String(" LIMIT 1000");
- QSqlQuery query(queryStr, database());
- while (query.next()) {
- const QString itemName = query.value(0).toString();
- QString path = query.value(2).toString();
- if (m_type == Docset::Type::ZDash) {
- const QString anchor = query.value(3).toString();
- if (!anchor.isEmpty())
- path += QLatin1Char('#') + anchor;
- }
-
- /// TODO: Third should be type
- results.append(SearchResult{itemName, QString(),
- parseSymbolType(query.value(1).toString()),
- const_cast(this), path, sanitizedQuery});
- }
+ QList results;
- if (withSubStrings)
- break;
- withSubStrings = true; // try again searching for substrings
+ QSqlQuery sqlQuery(queryStr, database());
+ while (sqlQuery.next() && !token.isCanceled()) {
+ results.append({sqlQuery.value(0).toString(),
+ parseSymbolType(sqlQuery.value(1).toString()),
+ const_cast(this),
+ createPageUrl(sqlQuery.value(2).toString(), sqlQuery.value(3).toString())});
}
return results;
@@ -330,25 +312,18 @@ QList Docset::relatedLinks(const QUrl &url) const
} else if (m_type == Docset::Type::ZDash) {
queryStr = QStringLiteral("SELECT ztoken.ztokenname, ztokentype.ztypename, zfilepath.zpath, ztokenmetainformation.zanchor "
"FROM ztoken "
- "JOIN ztokenmetainformation ON ztoken.zmetainformation = ztokenmetainformation.z_pk "
- "JOIN zfilepath ON ztokenmetainformation.zfile = zfilepath.z_pk "
- "JOIN ztokentype ON ztoken.ztokentype = ztokentype.z_pk "
+ "LEFT JOIN ztokenmetainformation ON ztoken.zmetainformation = ztokenmetainformation.z_pk "
+ "LEFT JOIN zfilepath ON ztokenmetainformation.zfile = zfilepath.z_pk "
+ "LEFT JOIN ztokentype ON ztoken.ztokentype = ztokentype.z_pk "
"WHERE zfilepath.zpath = \"%1\" AND ztokenmetainformation.zanchor IS NOT NULL");
}
- QSqlQuery query(queryStr.arg(cleanUrl.toString()), database());
-
- while (query.next()) {
- const QString sectionName = query.value(0).toString();
- QString sectionPath = query.value(2).toString();
- if (m_type == Docset::Type::ZDash) {
- sectionPath += QLatin1Char('#');
- sectionPath += query.value(3).toString();
- }
-
- results.append(SearchResult{sectionName, QString(),
- parseSymbolType(query.value(1).toString()),
- const_cast(this), sectionPath, QString()});
+ QSqlQuery sqlQuery(queryStr.arg(cleanUrl.toString()), database());
+ while (sqlQuery.next()) {
+ results.append({sqlQuery.value(0).toString(),
+ parseSymbolType(sqlQuery.value(1).toString()),
+ const_cast(this),
+ createPageUrl(sqlQuery.value(2).toString(), sqlQuery.value(3).toString())});
}
if (results.size() == 1)
@@ -387,7 +362,7 @@ void Docset::loadMetadata()
if (jsonObject.contains(QStringLiteral("extra"))) {
const QJsonObject extra = jsonObject[QStringLiteral("extra")].toObject();
- m_indexFilePath = extra[QStringLiteral("indexFilePath")].toString();
+ m_indexFileUrl = createPageUrl(extra[QStringLiteral("indexFilePath")].toString());
}
}
@@ -397,7 +372,7 @@ void Docset::countSymbols()
if (m_type == Docset::Type::Dash) {
queryStr = QStringLiteral("SELECT type, COUNT(*) FROM searchIndex GROUP BY type");
} else if (m_type == Docset::Type::ZDash) {
- queryStr = QStringLiteral("SELECT ztypename, COUNT(*) FROM ztoken JOIN ztokentype"
+ queryStr = QStringLiteral("SELECT ztypename, COUNT(*) FROM ztoken LEFT JOIN ztokentype"
" ON ztoken.ztokentype = ztokentype.z_pk GROUP BY ztypename");
}
@@ -415,7 +390,7 @@ void Docset::countSymbols()
}
}
-/// TODO: Fetch and cache only portions of symbols
+// TODO: Fetch and cache only portions of symbols
void Docset::loadSymbols(const QString &symbolType) const
{
for (const QString &symbol : m_symbolStrings.values(symbolType))
@@ -424,21 +399,15 @@ void Docset::loadSymbols(const QString &symbolType) const
void Docset::loadSymbols(const QString &symbolType, const QString &symbolString) const
{
- QSqlDatabase db = database();
- if (!db.isOpen())
- return;
-
QString queryStr;
if (m_type == Docset::Type::Dash) {
queryStr = QStringLiteral("SELECT name, path FROM searchIndex WHERE type='%1' ORDER BY name ASC");
} else {
- queryStr = QStringLiteral("SELECT ztokenname AS name, "
- "CASE WHEN (zanchor IS NULL) THEN zpath "
- "ELSE (zpath || '#' || zanchor) "
- "END AS path FROM ztoken "
- "JOIN ztokenmetainformation ON ztoken.zmetainformation = ztokenmetainformation.z_pk "
- "JOIN zfilepath ON ztokenmetainformation.zfile = zfilepath.z_pk "
- "JOIN ztokentype ON ztoken.ztokentype = ztokentype.z_pk WHERE ztypename='%1' "
+ queryStr = QStringLiteral("SELECT ztokenname, zpath, zanchor "
+ "FROM ztoken "
+ "LEFT JOIN ztokenmetainformation ON ztoken.zmetainformation = ztokenmetainformation.z_pk "
+ "LEFT JOIN zfilepath ON ztokenmetainformation.zfile = zfilepath.z_pk "
+ "LEFT JOIN ztokentype ON ztoken.ztokentype = ztokentype.z_pk WHERE ztypename='%1' "
"ORDER BY ztokenname ASC");
}
@@ -448,9 +417,10 @@ void Docset::loadSymbols(const QString &symbolType, const QString &symbolString)
return;
}
- QMap &symbols = m_symbols[symbolType];
+ QMap &symbols = m_symbols[symbolType];
while (query.next())
- symbols.insertMulti(query.value(0).toString(), QDir(documentPath()).absoluteFilePath(query.value(1).toString()));
+ symbols.insertMulti(query.value(0).toString(),
+ createPageUrl(query.value(1).toString(), query.value(2).toString()));
}
void Docset::createIndex()
@@ -458,12 +428,14 @@ void Docset::createIndex()
static const QString indexListQuery = QStringLiteral("PRAGMA INDEX_LIST('%1')");
static const QString indexDropQuery = QStringLiteral("DROP INDEX '%1'");
static const QString indexCreateQuery = QStringLiteral("CREATE INDEX IF NOT EXISTS %1%2"
- " ON %3 (name COLLATE NOCASE)");
+ " ON %3 (%4 COLLATE NOCASE)");
QSqlQuery query(database());
const QString tableName = m_type == Type::Dash ? QStringLiteral("searchIndex")
: QStringLiteral("ztoken");
+ const QString columnName = m_type == Type::Dash ? QStringLiteral("name")
+ : QStringLiteral("ztokenname");
query.exec(indexListQuery.arg(tableName));
@@ -481,15 +453,45 @@ void Docset::createIndex()
}
// Drop old indexes
- for (const QString oldIndexName : oldIndexes)
+ for (const QString &oldIndexName : oldIndexes)
query.exec(indexDropQuery.arg(oldIndexName));
- query.exec(indexCreateQuery.arg(IndexNamePrefix, IndexNameVersion, tableName));
+ query.exec(indexCreateQuery.arg(IndexNamePrefix, IndexNameVersion, tableName, columnName));
+}
+
+QUrl Docset::createPageUrl(const QString &path, const QString &fragment) const
+{
+ QString realPath;
+ QString realFragment;
+
+ if (fragment.isEmpty()) {
+ const QStringList urlParts = path.split(QLatin1Char('#'));
+ realPath = urlParts[0];
+ if (urlParts.size() > 1)
+ realFragment = urlParts[1];
+ } else {
+ realPath = path;
+ realFragment = fragment;
+ }
+
+ static const QRegularExpression dashEntryRegExp(QLatin1String(""));
+ realPath.remove(dashEntryRegExp);
+ realFragment.remove(dashEntryRegExp);
+
+ QUrl url = QUrl::fromLocalFile(QDir(documentPath()).absoluteFilePath(realPath));
+ if (!realFragment.isEmpty()) {
+ if (realFragment.startsWith("//apple_ref"))
+ url.setFragment(realFragment, QUrl::DecodedMode);
+ else
+ url.setFragment(realFragment);
+ }
+
+ return url;
}
QString Docset::parseSymbolType(const QString &str)
{
- /// Dash symbol aliases
+ // Dash symbol aliases
const static QHash aliases = {
// Attribute
{QStringLiteral("Package Attributes"), QStringLiteral("Attribute")},
@@ -514,11 +516,14 @@ QString Docset::parseSymbolType(const QString &str)
// Constant
{QStringLiteral("data"), QStringLiteral("Constant")},
{QStringLiteral("econst"), QStringLiteral("Constant")},
+ {QStringLiteral("enumdata"), QStringLiteral("Constant")},
{QStringLiteral("enumelt"), QStringLiteral("Constant")},
{QStringLiteral("clconst"), QStringLiteral("Constant")},
{QStringLiteral("structdata"), QStringLiteral("Constant")},
+ {QStringLiteral("writerid"), QStringLiteral("Constant")},
{QStringLiteral("Notifications"), QStringLiteral("Constant")},
// Constructor
+ {QStringLiteral("structctr"), QStringLiteral("Constructor")},
{QStringLiteral("Public Constructors"), QStringLiteral("Constructor")},
// Enumeration
{QStringLiteral("enum"), QStringLiteral("Enumeration")},
@@ -561,20 +566,34 @@ QString Docset::parseSymbolType(const QString &str)
{QStringLiteral("macro"), QStringLiteral("Macro")},
// Method
{QStringLiteral("clm"), QStringLiteral("Method")},
+ {QStringLiteral("enumcm"), QStringLiteral("Method")},
+ {QStringLiteral("enumctr"), QStringLiteral("Method")},
+ {QStringLiteral("enumm"), QStringLiteral("Method")},
{QStringLiteral("intfctr"), QStringLiteral("Method")},
{QStringLiteral("intfcm"), QStringLiteral("Method")},
{QStringLiteral("intfm"), QStringLiteral("Method")},
+ {QStringLiteral("intfsub"), QStringLiteral("Method")},
+ {QStringLiteral("instsub"), QStringLiteral("Method")},
{QStringLiteral("instctr"), QStringLiteral("Method")},
{QStringLiteral("instm"), QStringLiteral("Method")},
+ {QStringLiteral("structcm"), QStringLiteral("Method")},
+ {QStringLiteral("structm"), QStringLiteral("Method")},
+ {QStringLiteral("structsub"), QStringLiteral("Method")},
{QStringLiteral("Class Methods"), QStringLiteral("Method")},
{QStringLiteral("Inherited Methods"), QStringLiteral("Method")},
{QStringLiteral("Instance Methods"), QStringLiteral("Method")},
{QStringLiteral("Private Methods"), QStringLiteral("Method")},
{QStringLiteral("Protected Methods"), QStringLiteral("Method")},
{QStringLiteral("Public Methods"), QStringLiteral("Method")},
+ // Operator
+ {QStringLiteral("intfopfunc"), QStringLiteral("Operator")},
+ {QStringLiteral("opfunc"), QStringLiteral("Operator")},
// Property
+ {QStringLiteral("enump"), QStringLiteral("Property")},
+ {QStringLiteral("intfdata"), QStringLiteral("Property")},
{QStringLiteral("intfp"), QStringLiteral("Property")},
{QStringLiteral("instp"), QStringLiteral("Property")},
+ {QStringLiteral("structp"), QStringLiteral("Property")},
{QStringLiteral("Inherited Properties"), QStringLiteral("Property")},
{QStringLiteral("Private Properties"), QStringLiteral("Property")},
{QStringLiteral("Protected Properties"), QStringLiteral("Property")},
diff --git a/src/registry/docset.h b/src/libs/registry/docset.h
similarity index 72%
rename from src/registry/docset.h
rename to src/libs/registry/docset.h
index 76feb93b2..0f2960feb 100644
--- a/src/registry/docset.h
+++ b/src/libs/registry/docset.h
@@ -1,8 +1,8 @@
/****************************************************************************
**
-** Copyright (C) 2015 Oleg Shparber
+** Copyright (C) 2015-2016 Oleg Shparber
** Copyright (C) 2013-2014 Jerzy Kozera
-** Contact: http://zealdocs.org/contact.html
+** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
@@ -17,28 +17,31 @@
** 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 .
+** along with Zeal. If not, see .
**
****************************************************************************/
#ifndef DOCSET_H
#define DOCSET_H
-#include "searchresult.h"
-
#include
#include
#include
-#include
+#include
+
+class QSqlDatabase;
namespace Zeal {
+namespace Registry {
+
+struct CancellationToken;
+struct SearchResult;
-class Docset : public QObject
+class Docset
{
- Q_OBJECT
public:
explicit Docset(const QString &path);
- ~Docset() override;
+ ~Docset();
bool isValid() const;
@@ -52,17 +55,18 @@ class Docset : public QObject
QString path() const;
QString documentPath() const;
QIcon icon() const;
- QString indexFilePath() const;
+ QIcon symbolTypeIcon(const QString &symbolType) const;
+ QUrl indexFileUrl() const;
QMap symbolCounts() const;
int symbolCount(const QString &symbolType) const;
- const QMap &symbols(const QString &symbolType) const;
+ const QMap &symbols(const QString &symbolType) const;
- QList search(const QString &query) const;
+ QList search(const QString &query, const CancellationToken &token) const;
QList relatedLinks(const QUrl &url) const;
- /// FIXME: This is an ugly workaround before we have a proper docset sources implementation
+ // FIXME: This is an ugly workaround before we have a proper docset sources implementation
bool hasUpdate = false;
private:
@@ -78,6 +82,7 @@ class Docset : public QObject
void loadSymbols(const QString &symbolType) const;
void loadSymbols(const QString &symbolType, const QString &symbolString) const;
void createIndex();
+ QUrl createPageUrl(const QString &path, const QString &fragment = QString()) const;
static QString parseSymbolType(const QString &str);
@@ -91,13 +96,14 @@ class Docset : public QObject
QString m_path;
QIcon m_icon;
- QString m_indexFilePath;
+ QUrl m_indexFileUrl;
QMap m_symbolStrings;
QMap m_symbolCounts;
- mutable QMap> m_symbols;
+ mutable QMap> m_symbols;
};
+} // namespace Registry
} // namespace Zeal
#endif // DOCSET_H
diff --git a/src/registry/docsetmetadata.cpp b/src/libs/registry/docsetmetadata.cpp
similarity index 93%
rename from src/registry/docsetmetadata.cpp
rename to src/libs/registry/docsetmetadata.cpp
index 1e4c9f0ef..42d3b392e 100644
--- a/src/registry/docsetmetadata.cpp
+++ b/src/libs/registry/docsetmetadata.cpp
@@ -1,8 +1,8 @@
/****************************************************************************
**
-** Copyright (C) 2015 Oleg Shparber
+** Copyright (C) 2015-2016 Oleg Shparber
** Copyright (C) 2013-2014 Jerzy Kozera
-** Contact: http://zealdocs.org/contact.html
+** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
@@ -17,7 +17,7 @@
** 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 .
+** along with Zeal. If not, see .
**
****************************************************************************/
@@ -30,7 +30,7 @@
#include
#include
-using namespace Zeal;
+using namespace Zeal::Registry;
DocsetMetadata::DocsetMetadata()
{
@@ -48,7 +48,7 @@ DocsetMetadata::DocsetMetadata(const QJsonObject &jsonObject)
m_rawIcon2x = QByteArray::fromBase64(jsonObject[QStringLiteral("icon2x")].toString()
.toLocal8Bit());
- /// TODO: Check on a high-resolution screen
+ // TODO: Check on a high-resolution screen
if (qApp->devicePixelRatio() > 1.0) {
QPixmap pixmap = QPixmap::fromImage(QImage::fromData(m_rawIcon2x));
pixmap.setDevicePixelRatio(2.0);
@@ -62,10 +62,10 @@ DocsetMetadata::DocsetMetadata(const QJsonObject &jsonObject)
m_versions << vv.toString();
m_revision = jsonObject[QStringLiteral("revision")].toString();
- m_feedUrl = jsonObject[QStringLiteral("feed_url")].toString();
+ m_feedUrl = QUrl(jsonObject[QStringLiteral("feed_url")].toString());
const QJsonArray urlArray = jsonObject[QStringLiteral("urls")].toArray();
for (const QJsonValue &url : urlArray)
- m_urls.append(url.toString());
+ m_urls.append(QUrl(url.toString()));
m_extra = jsonObject[QStringLiteral("extra")].toObject();
}
@@ -207,7 +207,7 @@ DocsetMetadata DocsetMetadata::fromDashFeed(const QUrl &feedUrl, const QByteArra
} else if (xml.name() == QLatin1String("url")) {
if (xml.readNext() != QXmlStreamReader::Characters)
continue;
- metadata.m_urls.append(xml.text().toString());
+ metadata.m_urls.append(QUrl(xml.text().toString()));
}
}
diff --git a/src/registry/docsetmetadata.h b/src/libs/registry/docsetmetadata.h
similarity index 90%
rename from src/registry/docsetmetadata.h
rename to src/libs/registry/docsetmetadata.h
index 5dda9778c..5672bf769 100644
--- a/src/registry/docsetmetadata.h
+++ b/src/libs/registry/docsetmetadata.h
@@ -1,8 +1,8 @@
/****************************************************************************
**
-** Copyright (C) 2015 Oleg Shparber
+** Copyright (C) 2015-2016 Oleg Shparber
** Copyright (C) 2013-2014 Jerzy Kozera
-** Contact: http://zealdocs.org/contact.html
+** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
@@ -17,23 +17,20 @@
** 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 .
+** along with Zeal. If not, see .
**
****************************************************************************/
#ifndef DOCSETMETADATA_H
#define DOCSETMETADATA_H
-#include
#include
+#include
#include
#include
-class QJsonObject;
-
namespace Zeal {
-
-/// TODO: Use QUrl
+namespace Registry {
class DocsetMetadata
{
@@ -78,6 +75,7 @@ class DocsetMetadata
QList m_urls;
};
+} // namespace Registry
} // namespace Zeal
#endif // DOCSETMETADATA_H
diff --git a/src/registry/docsetregistry.cpp b/src/libs/registry/docsetregistry.cpp
similarity index 60%
rename from src/registry/docsetregistry.cpp
rename to src/libs/registry/docsetregistry.cpp
index d2f1ad913..adc234c94 100644
--- a/src/registry/docsetregistry.cpp
+++ b/src/libs/registry/docsetregistry.cpp
@@ -1,8 +1,8 @@
/****************************************************************************
**
-** Copyright (C) 2015 Oleg Shparber
+** Copyright (C) 2015-2016 Oleg Shparber
** Copyright (C) 2013-2014 Jerzy Kozera
-** Contact: http://zealdocs.org/contact.html
+** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
@@ -17,27 +17,39 @@
** 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 .
+** along with Zeal. If not, see .
**
****************************************************************************/
#include "docsetregistry.h"
+#include "cancellationtoken.h"
+#include "searchquery.h"
#include "searchresult.h"
#include
-#include
#include
-#include
-#include
-using namespace Zeal;
+#include
+
+#include
+
+using namespace Zeal::Registry;
+
+void MergeQueryResults(QList &finalResult, const QList &partial)
+{
+ finalResult << partial;
+}
DocsetRegistry::DocsetRegistry(QObject *parent) :
QObject(parent),
m_thread(new QThread(this))
{
- /// FIXME: Only search should be performed in a separate thread
+ // Register for use in signal connections.
+ qRegisterMetaType("CancellationToken");
+ qRegisterMetaType>("QList");
+
+ // FIXME: Only search should be performed in a separate thread
moveToThread(m_thread);
m_thread->start();
}
@@ -106,8 +118,9 @@ void DocsetRegistry::_addDocset(const QString &path)
{
Docset *docset = new Docset(path);
- /// TODO: Emit error
+ // TODO: Emit error
if (!docset->isValid()) {
+ qWarning("Could not load docset from '%s'. Please reinstall the docset.", qPrintable(path));
delete docset;
return;
}
@@ -121,30 +134,43 @@ void DocsetRegistry::_addDocset(const QString &path)
emit docsetAdded(name);
}
-void DocsetRegistry::search(const QString &query)
+void DocsetRegistry::search(const QString &query, const CancellationToken &token)
{
- // Only invalidate queries
- if (query.isEmpty())
- return;
-
- QMetaObject::invokeMethod(this, "_runQuery", Qt::QueuedConnection, Q_ARG(QString, query));
+ QMetaObject::invokeMethod(this, "_runQuery", Qt::QueuedConnection,
+ Q_ARG(QString, query), Q_ARG(CancellationToken, token));
}
-void DocsetRegistry::_runQuery(const QString &query)
+void DocsetRegistry::_runQuery(const QString &query, const CancellationToken &token)
{
- m_queryResults.clear();
+ QList enabledDocsets;
- for (Docset *docset : docsets())
- m_queryResults << docset->search(query);
+ const SearchQuery searchQuery = SearchQuery::fromString(query);
+ if (searchQuery.hasKeywords()) {
+ for (Docset *docset : docsets()) {
+ if (searchQuery.hasKeywords(docset->keywords()))
+ enabledDocsets << docset;
+ }
+ } else {
+ enabledDocsets = docsets();
+ }
- std::sort(m_queryResults.begin(), m_queryResults.end());
+ QFuture> queryResultsFuture
+ = QtConcurrent::mappedReduced(enabledDocsets,
+ std::bind(&Docset::search,
+ std::placeholders::_1,
+ searchQuery.query(), token),
+ &MergeQueryResults);
+ QList results = queryResultsFuture.result();
- emit queryCompleted();
-}
+ if (token.isCanceled())
+ return;
-const QList &DocsetRegistry::queryResults()
-{
- return m_queryResults;
+ std::sort(results.begin(), results.end());
+
+ if (token.isCanceled())
+ return;
+
+ emit queryCompleted(results);
}
// Recursively finds and adds all docsets in a given directory.
diff --git a/src/registry/docsetregistry.h b/src/libs/registry/docsetregistry.h
similarity index 80%
rename from src/registry/docsetregistry.h
rename to src/libs/registry/docsetregistry.h
index 61bf2fb0b..2b8bcadc4 100644
--- a/src/registry/docsetregistry.h
+++ b/src/libs/registry/docsetregistry.h
@@ -1,8 +1,8 @@
/****************************************************************************
**
-** Copyright (C) 2015 Oleg Shparber
+** Copyright (C) 2015-2016 Oleg Shparber
** Copyright (C) 2013-2014 Jerzy Kozera
-** Contact: http://zealdocs.org/contact.html
+** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
@@ -17,7 +17,7 @@
** 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 .
+** along with Zeal. If not, see .
**
****************************************************************************/
@@ -25,13 +25,16 @@
#define DOCSETREGISTRY_H
#include "docset.h"
-#include "searchresult.h"
#include
class QThread;
namespace Zeal {
+namespace Registry {
+
+struct CancellationToken;
+struct SearchResult;
class DocsetRegistry : public QObject
{
@@ -49,11 +52,10 @@ class DocsetRegistry : public QObject
Docset *docset(const QString &name) const;
Docset *docset(int index) const;
+ QList docsets() const;
- QString prepareQuery(const QString &rawQuery);
- void search(const QString &query);
+ void search(const QString &query, const CancellationToken &token);
const QList &queryResults();
- QList docsets() const;
public slots:
void addDocset(const QString &path);
@@ -62,20 +64,20 @@ public slots:
void docsetAdded(const QString &name);
void docsetAboutToBeRemoved(const QString &name);
void docsetRemoved(const QString &name);
- void queryCompleted();
+ void queryCompleted(const QList &results);
private slots:
void _addDocset(const QString &path);
- void _runQuery(const QString &query);
+ void _runQuery(const QString &query, const CancellationToken &token);
private:
void addDocsetsFromFolder(const QString &path);
QThread *m_thread = nullptr;
QMap m_docsets;
- QList m_queryResults;
};
+} // namespace Registry
} // namespace Zeal
#endif // DOCSETREGISTRY_H
diff --git a/src/registry/listmodel.cpp b/src/libs/registry/listmodel.cpp
similarity index 92%
rename from src/registry/listmodel.cpp
rename to src/libs/registry/listmodel.cpp
index 26c0698c8..863379994 100644
--- a/src/registry/listmodel.cpp
+++ b/src/libs/registry/listmodel.cpp
@@ -1,8 +1,8 @@
/****************************************************************************
**
-** Copyright (C) 2015 Oleg Shparber
+** Copyright (C) 2015-2016 Oleg Shparber
** Copyright (C) 2013-2014 Jerzy Kozera
-** Contact: http://zealdocs.org/contact.html
+** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
@@ -17,7 +17,7 @@
** 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 .
+** along with Zeal. If not, see .
**
****************************************************************************/
@@ -26,7 +26,7 @@
#include "docset.h"
#include "docsetregistry.h"
-using namespace Zeal;
+using namespace Zeal::Registry;
ListModel::ListModel(DocsetRegistry *docsetRegistry, QObject *parent) :
QAbstractItemModel(parent),
@@ -60,11 +60,11 @@ QVariant ListModel::data(const QModelIndex &index, int role) const
case Level::GroupLevel: {
DocsetItem *docsetItem = reinterpret_cast(index.internalPointer());
const QString symbolType = docsetItem->groups.at(index.row())->symbolType;
- return QIcon(QString("typeIcon:%1.png").arg(symbolType));
+ return docsetItem->docset->symbolTypeIcon(symbolType);
}
case Level::SymbolLevel: {
GroupItem *groupItem = reinterpret_cast(index.internalPointer());
- return QIcon(QString("typeIcon:%1.png").arg(groupItem->symbolType));
+ return groupItem->docsetItem->docset->symbolTypeIcon(groupItem->symbolType);
}
default:
return QVariant();
@@ -75,12 +75,12 @@ QVariant ListModel::data(const QModelIndex &index, int role) const
if (!index.column())
return m_docsetRegistry->docset(index.row())->title();
else
- return m_docsetRegistry->docset(index.row())->indexFilePath();
+ return m_docsetRegistry->docset(index.row())->indexFileUrl();
case Level::GroupLevel: {
DocsetItem *docsetItem = reinterpret_cast(index.internalPointer());
const QString symbolType = docsetItem->groups.at(index.row())->symbolType;
- return QString(QLatin1String("%1 (%2)")).arg(pluralize(symbolType),
- QString::number(docsetItem->docset->symbolCount(symbolType)));
+ return QStringLiteral("%1 (%2)").arg(pluralize(symbolType),
+ QString::number(docsetItem->docset->symbolCount(symbolType)));
}
case Level::SymbolLevel: {
GroupItem *groupItem = reinterpret_cast(index.internalPointer());
@@ -193,7 +193,7 @@ void ListModel::addDocset(const QString &name)
void ListModel::removeDocset(const QString &name)
{
const int index = m_docsetItems.keys().indexOf(name);
- /// TODO: Investigate why this can happen (see #420)
+ // TODO: Investigate why this can happen (see #420)
if (index == -1)
return;
diff --git a/src/registry/listmodel.h b/src/libs/registry/listmodel.h
similarity index 87%
rename from src/registry/listmodel.h
rename to src/libs/registry/listmodel.h
index 79283352f..2d0ed913e 100644
--- a/src/registry/listmodel.h
+++ b/src/libs/registry/listmodel.h
@@ -1,8 +1,8 @@
/****************************************************************************
**
-** Copyright (C) 2015 Oleg Shparber
+** Copyright (C) 2015-2016 Oleg Shparber
** Copyright (C) 2013-2014 Jerzy Kozera
-** Contact: http://zealdocs.org/contact.html
+** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
@@ -17,7 +17,7 @@
** 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 .
+** along with Zeal. If not, see .
**
****************************************************************************/
@@ -28,6 +28,7 @@
#include
namespace Zeal {
+namespace Registry {
class Docset;
class DocsetRegistry;
@@ -36,8 +37,9 @@ class ListModel : public QAbstractItemModel
{
Q_OBJECT
public:
- enum {
- DocsetNameRole = Qt::UserRole,
+ enum ItemDataRole {
+ // Do not collide with SearchModel, and ProgressItemDelegate.
+ DocsetNameRole = Qt::UserRole + 20,
UpdateAvailableRole
};
@@ -83,6 +85,7 @@ private slots:
QMap m_docsetItems;
};
+} // namespace Registry
} // namespace Zeal
#endif // LISTMODEL_H
diff --git a/src/libs/registry/registry.pri b/src/libs/registry/registry.pri
new file mode 100644
index 000000000..e998c795b
--- /dev/null
+++ b/src/libs/registry/registry.pri
@@ -0,0 +1 @@
+ZEAL_LIB_NAME = Registry
diff --git a/src/libs/registry/registry.pro b/src/libs/registry/registry.pro
new file mode 100644
index 000000000..9007b98b0
--- /dev/null
+++ b/src/libs/registry/registry.pro
@@ -0,0 +1,6 @@
+include($$ZEAL_LIBRARY_PRI)
+
+QT += sql
+
+HEADERS += $$files(*.h)
+SOURCES += $$files(*.cpp)
diff --git a/src/registry/searchmodel.cpp b/src/libs/registry/searchmodel.cpp
similarity index 57%
rename from src/registry/searchmodel.cpp
rename to src/libs/registry/searchmodel.cpp
index c7dbc67b4..ba9990dd8 100644
--- a/src/registry/searchmodel.cpp
+++ b/src/libs/registry/searchmodel.cpp
@@ -1,8 +1,8 @@
/****************************************************************************
**
-** Copyright (C) 2015 Oleg Shparber
+** Copyright (C) 2015-2016 Oleg Shparber
** Copyright (C) 2013-2014 Jerzy Kozera
-** Contact: http://zealdocs.org/contact.html
+** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
@@ -17,24 +17,34 @@
** 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 .
+** along with Zeal. If not, see .
**
****************************************************************************/
#include "searchmodel.h"
-#include "core/application.h"
-#include "registry/docsetregistry.h"
+#include "docset.h"
#include
-using namespace Zeal;
+using namespace Zeal::Registry;
SearchModel::SearchModel(QObject *parent) :
QAbstractItemModel(parent)
{
}
+SearchModel::SearchModel(const SearchModel &other) :
+ QAbstractItemModel(other.d_ptr->parent),
+ m_dataList(other.m_dataList)
+{
+}
+
+bool SearchModel::isEmpty() const
+{
+ return m_dataList.isEmpty();
+}
+
QVariant SearchModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
@@ -46,23 +56,19 @@ QVariant SearchModel::data(const QModelIndex &index, int role) const
case Qt::DisplayRole:
switch (index.column()) {
case 0:
- if (item->parentName.isEmpty())
- return item->name;
- else
- return QString("%1 (%2)").arg(item->name, item->parentName);
+ return item->name;
case 1:
- return QDir(item->docset->documentPath()).absoluteFilePath(item->path);
+ return item->url;
default:
return QVariant();
}
- case Qt::DecorationRole:
- return item->docset->icon();
+ case Qt::DecorationRole: {
+ return item->docset->symbolTypeIcon(item->type);
+ }
- case Roles::TypeIconRole:
- if (index.column() != 0)
- return QVariant();
- return QIcon(QString("typeIcon:%1.png").arg(item->type));
+ case ItemDataRole::DocsetIconRole:
+ return item->docset->icon();
default:
return QVariant();
@@ -74,7 +80,9 @@ QModelIndex SearchModel::index(int row, int column, const QModelIndex &parent) c
if (parent.isValid() || m_dataList.count() <= row || column > 1)
return QModelIndex();
- return createIndex(row, column, (void *)&m_dataList.at(row));
+ // FIXME: const_cast
+ SearchResult *item = const_cast(&m_dataList.at(row));
+ return createIndex(row, column, item);
}
QModelIndex SearchModel::parent(const QModelIndex &child) const
@@ -96,6 +104,36 @@ int SearchModel::columnCount(const QModelIndex &parent) const
return 2;
}
+bool SearchModel::removeRows(int row, int count, const QModelIndex &parent)
+{
+ if (row + count <= m_dataList.size() && !parent.isValid()) {
+ beginRemoveRows(parent, row, row + count - 1);
+ while (count) {
+ m_dataList.removeAt(row);
+ --count;
+ }
+ endRemoveRows();
+ return true;
+ }
+ return false;
+}
+
+void SearchModel::removeSearchResultWithName(const QString &name)
+{
+ QMutableListIterator iterator(m_dataList);
+
+ int rowNum = 0;
+ while (iterator.hasNext()) {
+ if (iterator.next().docset->name() == name) {
+ beginRemoveRows(QModelIndex(), rowNum, rowNum);
+ iterator.remove();
+ rowNum -= 1;
+ endRemoveRows();
+ }
+ rowNum += 1;
+ }
+}
+
void SearchModel::setResults(const QList &results)
{
beginResetModel();
diff --git a/src/registry/searchmodel.h b/src/libs/registry/searchmodel.h
similarity index 75%
rename from src/registry/searchmodel.h
rename to src/libs/registry/searchmodel.h
index f7a14cd3b..d2a7dad0a 100644
--- a/src/registry/searchmodel.h
+++ b/src/libs/registry/searchmodel.h
@@ -1,8 +1,8 @@
/****************************************************************************
**
-** Copyright (C) 2015 Oleg Shparber
+** Copyright (C) 2015-2016 Oleg Shparber
** Copyright (C) 2013-2014 Jerzy Kozera
-** Contact: http://zealdocs.org/contact.html
+** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
@@ -17,7 +17,7 @@
** 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 .
+** along with Zeal. If not, see .
**
****************************************************************************/
@@ -29,22 +29,29 @@
#include
namespace Zeal {
+namespace Registry {
class SearchModel : public QAbstractItemModel
{
Q_OBJECT
public:
- enum Roles {
- TypeIconRole = Qt::UserRole
+ // TODO: Standardise roles across app
+ enum ItemDataRole {
+ DocsetIconRole = Qt::UserRole
};
explicit SearchModel(QObject *parent = nullptr);
+ SearchModel(const SearchModel &other);
+
+ bool isEmpty() const;
QVariant data(const QModelIndex &index, int role) const override;
QModelIndex index(int row, int column, const QModelIndex &parent) const override;
QModelIndex parent(const QModelIndex &child) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent) const override;
+ bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
+ void removeSearchResultWithName(const QString &name);
public slots:
void setResults(const QList &results = QList());
@@ -56,6 +63,7 @@ public slots:
QList m_dataList;
};
+} // namespace Registry
} // namespace Zeal
#endif // SEARCHMODEL_H
diff --git a/src/registry/searchquery.cpp b/src/libs/registry/searchquery.cpp
similarity index 81%
rename from src/registry/searchquery.cpp
rename to src/libs/registry/searchquery.cpp
index 021379b46..1dedf5c01 100644
--- a/src/registry/searchquery.cpp
+++ b/src/libs/registry/searchquery.cpp
@@ -1,8 +1,8 @@
/****************************************************************************
**
-** Copyright (C) 2015 Oleg Shparber
+** Copyright (C) 2015-2016 Oleg Shparber
** Copyright (C) 2013-2014 Jerzy Kozera
-** Contact: http://zealdocs.org/contact.html
+** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
@@ -17,13 +17,13 @@
** 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 .
+** along with Zeal. If not, see .
**
****************************************************************************/
#include "searchquery.h"
-using namespace Zeal;
+using namespace Zeal::Registry;
namespace {
const char prefixSeparator = ':';
@@ -94,7 +94,7 @@ bool SearchQuery::hasKeywords() const
bool SearchQuery::hasKeyword(const QString &keyword) const
{
// Temporary workaround for #333
- /// TODO: Remove once #167 is implemented
+ // TODO: Remove once #167 is implemented
for (const QString &kw : m_keywords) {
if (keyword.startsWith(kw, Qt::CaseInsensitive))
return true;
@@ -126,23 +126,13 @@ void SearchQuery::setQuery(const QString &str)
m_query = str;
}
-QString SearchQuery::sanitizedQuery() const
-{
- QString q = m_query;
- q.replace(QLatin1String("\\"), QLatin1String("\\\\"));
- q.replace(QLatin1String("_"), QLatin1String("\\_"));
- q.replace(QLatin1String("%"), QLatin1String("\\%"));
- q.replace(QLatin1String("'"), QLatin1String("''"));
- return q;
-}
-
-QDataStream &Zeal::operator<<(QDataStream &out, const SearchQuery &query)
+QDataStream &operator<<(QDataStream &out, const Zeal::Registry::SearchQuery &query)
{
out << query.toString();
return out;
}
-QDataStream &Zeal::operator>>(QDataStream &in, SearchQuery &query)
+QDataStream &operator>>(QDataStream &in, Zeal::Registry::SearchQuery &query)
{
QString str;
in >> str;
diff --git a/src/registry/searchquery.h b/src/libs/registry/searchquery.h
similarity index 88%
rename from src/registry/searchquery.h
rename to src/libs/registry/searchquery.h
index bbebb99f4..27fe9f243 100644
--- a/src/registry/searchquery.h
+++ b/src/libs/registry/searchquery.h
@@ -1,8 +1,8 @@
/****************************************************************************
**
-** Copyright (C) 2015 Oleg Shparber
+** Copyright (C) 2015-2016 Oleg Shparber
** Copyright (C) 2013-2014 Jerzy Kozera
-** Contact: http://zealdocs.org/contact.html
+** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
@@ -17,7 +17,7 @@
** 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 .
+** along with Zeal. If not, see .
**
****************************************************************************/
@@ -27,6 +27,7 @@
#include
namespace Zeal {
+namespace Registry {
/**
* @short The search query model.
@@ -75,18 +76,16 @@ class SearchQuery
QString query() const;
void setQuery(const QString &str);
- /// Returns the core query, sanitized for use in SQL queries
- QString sanitizedQuery() const;
-
private:
QString m_query;
QStringList m_keywords;
QString m_keywordPrefix;
};
-QDataStream &operator<<(QDataStream &out, const SearchQuery &query);
-QDataStream &operator>>(QDataStream &in, SearchQuery &query);
-
+} // namespace Registry
} // namespace Zeal
+QDataStream &operator<<(QDataStream &out, const Zeal::Registry::SearchQuery &query);
+QDataStream &operator>>(QDataStream &in, Zeal::Registry::SearchQuery &query);
+
#endif // SEARCHQUERY_H
diff --git a/src/ui/searchitemstyle.cpp b/src/libs/registry/searchresult.cpp
similarity index 59%
rename from src/ui/searchitemstyle.cpp
rename to src/libs/registry/searchresult.cpp
index c181db14e..1679f6620 100644
--- a/src/ui/searchitemstyle.cpp
+++ b/src/libs/registry/searchresult.cpp
@@ -1,8 +1,8 @@
/****************************************************************************
**
-** Copyright (C) 2015 Oleg Shparber
+** Copyright (C) 2015-2016 Oleg Shparber
** Copyright (C) 2013-2014 Jerzy Kozera
-** Contact: http://zealdocs.org/contact.html
+** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
@@ -17,19 +17,10 @@
** 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 .
+** along with Zeal. If not, see .
**
****************************************************************************/
-#include "searchitemstyle.h"
+#include "searchresult.h"
-QRect ZealSearchItemStyle::subElementRect(SubElement element, const QStyleOption *option,
- const QWidget *widget) const
-{
- if (element == QStyle::SE_ItemViewItemText) {
- // do not draw text - delegate does it
- return QRect();
- } else {
- return QProxyStyle::subElementRect(element, option, widget);
- }
-}
+using namespace Zeal::Registry;
diff --git a/src/registry/searchresult.h b/src/libs/registry/searchresult.h
similarity index 73%
rename from src/registry/searchresult.h
rename to src/libs/registry/searchresult.h
index e879daf2f..5ece8073b 100644
--- a/src/registry/searchresult.h
+++ b/src/libs/registry/searchresult.h
@@ -1,8 +1,8 @@
/****************************************************************************
**
-** Copyright (C) 2015 Oleg Shparber
+** Copyright (C) 2015-2016 Oleg Shparber
** Copyright (C) 2013-2014 Jerzy Kozera
-** Contact: http://zealdocs.org/contact.html
+** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
@@ -17,7 +17,7 @@
** 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 .
+** along with Zeal. If not, see .
**
****************************************************************************/
@@ -25,27 +25,29 @@
#define SEARCHRESULT_H
#include
+#include
namespace Zeal {
+namespace Registry {
class Docset;
struct SearchResult
{
QString name;
- QString parentName;
QString type;
Docset *docset;
- QString path;
+ QUrl url;
- /// TODO: Remove
- QString query;
-
- bool operator<(const SearchResult &r) const;
+ inline bool operator<(const SearchResult &other) const
+ {
+ return QString::compare(name, other.name, Qt::CaseInsensitive) < 0;
+ }
};
+} // namespace Registry
} // namespace Zeal
#endif // SEARCHRESULT_H
diff --git a/src/ui/aboutdialog.cpp b/src/libs/ui/aboutdialog.cpp
similarity index 77%
rename from src/ui/aboutdialog.cpp
rename to src/libs/ui/aboutdialog.cpp
index bb49c91fa..32ebdfe40 100644
--- a/src/ui/aboutdialog.cpp
+++ b/src/libs/ui/aboutdialog.cpp
@@ -1,7 +1,7 @@
/****************************************************************************
**
-** Copyright (C) 2015 Oleg Shparber
-** Contact: http://zealdocs.org/contact.html
+** Copyright (C) 2015-2016 Oleg Shparber
+** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
@@ -16,7 +16,7 @@
** 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 .
+** along with Zeal. If not, see .
**
****************************************************************************/
@@ -30,10 +30,10 @@ AboutDialog::AboutDialog(QWidget *parent) :
ui(new Ui::AboutDialog)
{
ui->setupUi(this);
+ setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
const QString buildInfo
- = QString(tr("Version: %1
Built: %2"))
- .arg(QCoreApplication::applicationVersion(), QString("%1 %2").arg(__DATE__, __TIME__));
+ = tr("Version: %1
").arg(QCoreApplication::applicationVersion());
ui->buildInfoLabel->setText(buildInfo);
ui->buttonBox->setFocus(Qt::OtherFocusReason);
diff --git a/src/ui/aboutdialog.h b/src/libs/ui/aboutdialog.h
similarity index 87%
rename from src/ui/aboutdialog.h
rename to src/libs/ui/aboutdialog.h
index 1ee9ab0db..bf4e0ee95 100644
--- a/src/ui/aboutdialog.h
+++ b/src/libs/ui/aboutdialog.h
@@ -1,7 +1,7 @@
/****************************************************************************
**
-** Copyright (C) 2015 Oleg Shparber
-** Contact: http://zealdocs.org/contact.html
+** Copyright (C) 2015-2016 Oleg Shparber
+** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
@@ -16,7 +16,7 @@
** 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 .
+** along with Zeal. If not, see .
**
****************************************************************************/
diff --git a/src/ui/docsetlistitemdelegate.cpp b/src/libs/ui/docsetlistitemdelegate.cpp
similarity index 81%
rename from src/ui/docsetlistitemdelegate.cpp
rename to src/libs/ui/docsetlistitemdelegate.cpp
index aa32635e1..c368cf2b8 100644
--- a/src/ui/docsetlistitemdelegate.cpp
+++ b/src/libs/ui/docsetlistitemdelegate.cpp
@@ -1,8 +1,8 @@
/****************************************************************************
**
-** Copyright (C) 2015 Oleg Shparber
+** Copyright (C) 2015-2016 Oleg Shparber
** Copyright (C) 2013-2014 Jerzy Kozera
-** Contact: http://zealdocs.org/contact.html
+** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
@@ -17,16 +17,18 @@
** 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 .
+** along with Zeal. If not, see .
**
****************************************************************************/
#include "docsetlistitemdelegate.h"
-#include "registry/listmodel.h"
+#include
#include
+using namespace Zeal;
+
DocsetListItemDelegate::DocsetListItemDelegate(QObject *parent) :
QItemDelegate(parent)
{
@@ -37,7 +39,7 @@ void DocsetListItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem
{
QItemDelegate::paint(painter, option, index);
- if (!index.model()->data(index, Zeal::ListModel::UpdateAvailableRole).toBool())
+ if (!index.model()->data(index, Registry::ListModel::UpdateAvailableRole).toBool())
return;
const QString text = tr("Update available");
@@ -46,10 +48,9 @@ void DocsetListItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem
font.setItalic(true);
const QFontMetrics fontMetrics(font);
- const int textWidth = fontMetrics.width(text) + 2;
QRect textRect = option.rect;
- textRect.setLeft(textRect.right() - textWidth);
+ textRect.setLeft(textRect.right() - fontMetrics.width(text) - 2);
painter->save();
diff --git a/src/ui/docsetlistitemdelegate.h b/src/libs/ui/docsetlistitemdelegate.h
similarity index 88%
rename from src/ui/docsetlistitemdelegate.h
rename to src/libs/ui/docsetlistitemdelegate.h
index a310b776e..0d12ce9a8 100644
--- a/src/ui/docsetlistitemdelegate.h
+++ b/src/libs/ui/docsetlistitemdelegate.h
@@ -1,8 +1,8 @@
/****************************************************************************
**
-** Copyright (C) 2015 Oleg Shparber
+** Copyright (C) 2015-2016 Oleg Shparber
** Copyright (C) 2013-2014 Jerzy Kozera
-** Contact: http://zealdocs.org/contact.html
+** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
@@ -17,7 +17,7 @@
** 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 .
+** along with Zeal. If not, see .
**
****************************************************************************/
diff --git a/src/libs/ui/docsetsdialog.cpp b/src/libs/ui/docsetsdialog.cpp
new file mode 100644
index 000000000..bd869e158
--- /dev/null
+++ b/src/libs/ui/docsetsdialog.cpp
@@ -0,0 +1,797 @@
+/****************************************************************************
+**
+** Copyright (C) 2015-2016 Oleg Shparber
+** Copyright (C) 2013-2014 Jerzy Kozera
+** 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 .
+**
+****************************************************************************/
+
+#include "docsetsdialog.h"
+#include "ui_docsetsdialog.h"
+
+#include "docsetlistitemdelegate.h"
+#include "progressitemdelegate.h"
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+using namespace Zeal;
+
+namespace {
+const char ApiServerUrl[] = "http://api.zealdocs.org/v1";
+const char RedirectServerUrl[] = "https://go.zealdocs.org";
+// TODO: Each source plugin should have its own cache
+const char DocsetListCacheFileName[] = "com.kapeli.json";
+
+// TODO: Make the timeout period configurable
+constexpr int CacheTimeout = 24 * 60 * 60 * 1000; // 24 hours in microseconds
+
+// QNetworkReply properties
+const char DocsetNameProperty[] = "docsetName";
+const char DownloadTypeProperty[] = "downloadType";
+const char DownloadPreviousReceived[] = "downloadPreviousReceived";
+const char ListItemIndexProperty[] = "listItem";
+}
+
+DocsetsDialog::DocsetsDialog(Core::Application *app, QWidget *parent) :
+ QDialog(parent),
+ ui(new Ui::DocsetsDialog()),
+ m_application(app),
+ m_docsetRegistry(app->docsetRegistry())
+{
+ using Registry::DocsetRegistry;
+ using Registry::ListModel;
+
+ ui->setupUi(this);
+ setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
+
+#ifdef Q_OS_OSX
+ ui->availableDocsetList->setAttribute(Qt::WA_MacShowFocusRect, false);
+ ui->installedDocsetList->setAttribute(Qt::WA_MacShowFocusRect, false);
+#endif
+
+ 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(ListModel::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->selectedIndexes()) {
+ if (index.data(ListModel::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->selectedIndexes()) {
+ 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::docsetRemoved, this, [this](const QString name) {
+ QListWidgetItem *item = findDocsetListItem(name);
+ if (!item)
+ return;
+
+ item->setHidden(false);
+ });
+ connect(m_docsetRegistry, &DocsetRegistry::docsetAdded, 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);
+
+ connect(m_application, &Core::Application::extractionCompleted,
+ this, &DocsetsDialog::extractionCompleted);
+ connect(m_application, &Core::Application::extractionError,
+ this, &DocsetsDialog::extractionError);
+ connect(m_application, &Core::Application::extractionProgress,
+ this, &DocsetsDialog::extractionProgress);
+
+ connect(ui->cancelButton, &QPushButton::clicked, this, &DocsetsDialog::cancelDownloads);
+
+ loadDocsetList();
+}
+
+DocsetsDialog::~DocsetsDialog()
+{
+ delete ui;
+}
+
+void DocsetsDialog::reject()
+{
+ if (m_replies.isEmpty() && m_tmpFiles.isEmpty() && m_docsetsBeingDeleted.isEmpty()) {
+ QDialog::reject();
+ return;
+ }
+
+ QMessageBox::information(this, QStringLiteral("Zeal"),
+ tr("Please wait for all operations to finish."));
+}
+
+void DocsetsDialog::addDashFeed()
+{
+ QString clipboardText = QApplication::clipboard()->text();
+ if (!clipboardText.startsWith(QLatin1String("dash-feed://")))
+ clipboardText.clear();
+
+ QString feedUrl = QInputDialog::getText(this, QStringLiteral("Zeal"), tr("Feed URL:"),
+ QLineEdit::Normal, clipboardText);
+ if (feedUrl.isEmpty())
+ return;
+
+ if (feedUrl.startsWith(QLatin1String("dash-feed://"))) {
+ feedUrl = feedUrl.remove(0, 12);
+ feedUrl = QUrl::fromPercentEncoding(feedUrl.toUtf8());
+ }
+
+ QNetworkReply *reply = download(QUrl(feedUrl));
+ reply->setProperty(DownloadTypeProperty, DownloadDashFeed);
+ connect(reply, &QNetworkReply::finished, this, &DocsetsDialog::downloadCompleted);
+}
+
+void DocsetsDialog::updateSelectedDocsets()
+{
+ for (const QModelIndex &index : ui->installedDocsetList->selectionModel()->selectedIndexes()) {
+ if (!index.data(Registry::ListModel::UpdateAvailableRole).toBool())
+ continue;
+
+ downloadDashDocset(index);
+ }
+}
+
+void DocsetsDialog::updateAllDocsets()
+{
+ QAbstractItemModel *model = ui->installedDocsetList->model();
+ for (int i = 0; i < model->rowCount(); ++i) {
+ const QModelIndex index = model->index(i, 0);
+ if (!index.data(Registry::ListModel::UpdateAvailableRole).toBool())
+ continue;
+
+ downloadDashDocset(index);
+ }
+}
+
+void DocsetsDialog::removeSelectedDocsets()
+{
+ QItemSelectionModel *selectonModel = ui->installedDocsetList->selectionModel();
+ if (!selectonModel->hasSelection())
+ return;
+
+ int ret;
+
+ const QModelIndexList selectedIndexes = selectonModel->selectedIndexes();
+ if (selectedIndexes.size() == 1) {
+ const QString docsetTitle = selectedIndexes.first().data().toString();
+ ret = QMessageBox::question(this, QStringLiteral("Zeal"),
+ tr("Remove %1 docset?").arg(docsetTitle));
+ } else {
+ ret = QMessageBox::question(this, QStringLiteral("Zeal"),
+ tr("Remove %n docset(s)?", nullptr,
+ selectedIndexes.size()));
+ }
+
+ if (ret == QMessageBox::No)
+ return;
+
+ // Gather names first, because model indicies become invalid when docsets are removed.
+ QStringList names;
+ for (const QModelIndex &index : selectedIndexes)
+ names.append(index.data(Registry::ListModel::DocsetNameRole).toString());
+
+ for (const QString &name : names)
+ removeDocset(name);
+}
+
+void DocsetsDialog::updateDocsetFilter(const QString &filterString)
+{
+ const bool doSearch = !filterString.simplified().isEmpty();
+
+ for (int i = 0; i < ui->availableDocsetList->count(); ++i) {
+ QListWidgetItem *item = ui->availableDocsetList->item(i);
+
+ // Skip installed docsets
+ if (m_docsetRegistry->contains(item->data(Registry::ListModel::DocsetNameRole).toString()))
+ continue;
+
+ item->setHidden(doSearch && !item->text().contains(filterString, Qt::CaseInsensitive));
+ }
+}
+
+void DocsetsDialog::downloadSelectedDocsets()
+{
+ QItemSelectionModel *selectionModel = ui->availableDocsetList->selectionModel();
+ for (const QModelIndex &index : selectionModel->selectedIndexes()) {
+ selectionModel->select(index, QItemSelectionModel::Deselect);
+
+ // Do nothing if a download is already in progress.
+ if (index.data(ProgressItemDelegate::ShowProgressRole).toBool())
+ continue;
+
+ 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);
+ }
+}
+
+/*!
+ \internal
+ Should be connected to all \l QNetworkReply::finished signals in order to process possible
+ HTTP-redirects correctly.
+*/
+void DocsetsDialog::downloadCompleted()
+{
+ QScopedPointer reply(
+ qobject_cast(sender()));
+
+ m_replies.removeOne(reply.data());
+
+ if (reply->error() != QNetworkReply::NoError) {
+ if (reply->error() != QNetworkReply::OperationCanceledError) {
+ const int ret = QMessageBox::warning(this, QStringLiteral("Zeal"), reply->errorString(),
+ QMessageBox::Retry | QMessageBox::Default,
+ QMessageBox::Cancel | QMessageBox::Escape,
+ QMessageBox::NoButton);
+
+ if (ret == QMessageBox::Retry) {
+ QNetworkReply *newReply = download(reply->request().url());
+
+ // Copy properties
+ newReply->setProperty(DocsetNameProperty, reply->property(DocsetNameProperty));
+ newReply->setProperty(DownloadTypeProperty, reply->property(DownloadTypeProperty));
+ newReply->setProperty(ListItemIndexProperty,
+ reply->property(ListItemIndexProperty));
+
+ connect(newReply, &QNetworkReply::finished,
+ this, &DocsetsDialog::downloadCompleted);
+ return;
+ }
+
+ bool ok;
+ QListWidgetItem *listItem = ui->availableDocsetList->item(
+ reply->property(ListItemIndexProperty).toInt(&ok));
+ if (ok && listItem)
+ listItem->setData(ProgressItemDelegate::ShowProgressRole, false);
+ }
+
+ resetProgress();
+ return;
+ }
+
+ QUrl redirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
+ if (redirectUrl.isValid()) {
+ if (redirectUrl.isRelative())
+ redirectUrl = reply->request().url().resolved(redirectUrl);
+
+ // TODO: Verify if scheme can be missing
+ if (redirectUrl.scheme().isEmpty())
+ redirectUrl.setScheme(reply->request().url().scheme());
+
+ QNetworkReply *newReply = download(redirectUrl);
+
+ // Copy properties
+ newReply->setProperty(DocsetNameProperty, reply->property(DocsetNameProperty));
+ newReply->setProperty(DownloadTypeProperty, reply->property(DownloadTypeProperty));
+ newReply->setProperty(ListItemIndexProperty, reply->property(ListItemIndexProperty));
+
+ connect(newReply, &QNetworkReply::finished, this, &DocsetsDialog::downloadCompleted);
+
+ return;
+ }
+
+ switch (reply->property(DownloadTypeProperty).toUInt()) {
+ case DownloadDocsetList: {
+ const QByteArray data = reply->readAll();
+
+ QScopedPointer file(new QFile(cacheLocation(DocsetListCacheFileName)));
+ if (file->open(QIODevice::WriteOnly))
+ file->write(data);
+
+ ui->lastUpdatedLabel->setText(QFileInfo(file->fileName())
+ .lastModified().toString(Qt::SystemLocaleShortDate));
+
+ QJsonParseError jsonError;
+ const QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
+
+ if (jsonError.error != QJsonParseError::NoError) {
+ QMessageBox::warning(this, QStringLiteral("Zeal"),
+ tr("Corrupted docset list: ") + jsonError.errorString());
+ break;
+ }
+
+ processDocsetList(jsonDoc.array());
+ break;
+ }
+
+ case DownloadDashFeed: {
+ Registry::DocsetMetadata metadata
+ = Registry::DocsetMetadata::fromDashFeed(reply->request().url(), reply->readAll());
+
+ if (metadata.urls().isEmpty()) {
+ QMessageBox::warning(this, QStringLiteral("Zeal"), tr("Invalid docset feed!"));
+ break;
+ }
+
+ m_userFeeds[metadata.name()] = metadata;
+ QNetworkReply *reply = download(metadata.url());
+ reply->setProperty(DocsetNameProperty, metadata.name());
+ reply->setProperty(DownloadTypeProperty, DownloadDocset);
+ connect(reply, &QNetworkReply::finished, this, &DocsetsDialog::downloadCompleted);
+ break;
+ }
+
+ case DownloadDocset: {
+ const QString docsetName = reply->property(DocsetNameProperty).toString();
+
+ // TODO: Implement an explicit and verbose docset update logic
+ const QDir dir(m_application->settings()->docsetPath);
+ if (dir.exists(docsetName + QLatin1String(".docset")))
+ removeDocset(docsetName);
+
+ QTemporaryFile *tmpFile = m_tmpFiles[docsetName];
+ if (!tmpFile) {
+ tmpFile = new QTemporaryFile(this);
+ tmpFile->open();
+ m_tmpFiles.insert(docsetName, tmpFile);
+ }
+
+ while (reply->bytesAvailable())
+ tmpFile->write(reply->read(1024 * 1024)); // Use small chunks
+ tmpFile->close();
+
+ QListWidgetItem *item = findDocsetListItem(docsetName);
+ if (item) {
+ item->setData(ProgressItemDelegate::ValueRole, 0);
+ item->setData(ProgressItemDelegate::FormatRole, tr("Installing: %p%"));
+ }
+
+ m_application->extract(tmpFile->fileName(), m_application->settings()->docsetPath,
+ docsetName + QLatin1String(".docset"));
+ break;
+ }
+ }
+
+ // If all enqueued downloads have finished executing
+ if (m_replies.isEmpty())
+ resetProgress();
+}
+
+// creates a total download progress for multiple QNetworkReplies
+void DocsetsDialog::downloadProgress(qint64 received, qint64 total)
+{
+ // Don't show progress for non-docset pages
+ if (total == -1 || received < 10240)
+ return;
+
+ QNetworkReply *reply = qobject_cast(sender());
+ if (!reply || !reply->isOpen())
+ return;
+
+ if (reply->property(DownloadTypeProperty).toInt() == DownloadDocset) {
+ const QString docsetName = reply->property(DocsetNameProperty).toString();
+
+ QTemporaryFile *tmpFile = m_tmpFiles[docsetName];
+ if (!tmpFile) {
+ tmpFile = new QTemporaryFile(this);
+ tmpFile->open();
+ m_tmpFiles.insert(docsetName, tmpFile);
+ }
+
+ tmpFile->write(reply->read(received));
+ }
+
+ // Try to get the item associated to the request
+ QListWidgetItem *item
+ = ui->availableDocsetList->item(reply->property(ListItemIndexProperty).toInt());
+ if (item)
+ item->setData(ProgressItemDelegate::ValueRole, percent(received, total));
+
+ qint64 previousReceived = 0;
+ const QVariant previousReceivedVariant = reply->property(DownloadPreviousReceived);
+ if (!previousReceivedVariant.isValid())
+ m_combinedTotal += total;
+ else
+ previousReceived = previousReceivedVariant.toLongLong();
+
+ m_combinedReceived += received - previousReceived;
+ reply->setProperty(DownloadPreviousReceived, received);
+
+ updateCombinedProgress();
+}
+
+void DocsetsDialog::extractionCompleted(const QString &filePath)
+{
+ const QString docsetName = docsetNameForTmpFilePath(filePath);
+
+ const QDir dataDir(m_application->settings()->docsetPath);
+ const QString docsetPath = dataDir.absoluteFilePath(docsetName + QLatin1String(".docset"));
+
+ // Write metadata about docset
+ Registry::DocsetMetadata metadata = m_availableDocsets.contains(docsetName)
+ ? m_availableDocsets[docsetName]
+ : m_userFeeds[docsetName];
+ metadata.save(docsetPath, metadata.latestVersion());
+
+ m_docsetRegistry->addDocset(docsetPath);
+
+ QListWidgetItem *listItem = findDocsetListItem(docsetName);
+ if (listItem) {
+ listItem->setHidden(true);
+ listItem->setData(ProgressItemDelegate::ShowProgressRole, false);
+ }
+
+ resetProgress();
+ delete m_tmpFiles.take(docsetName);
+}
+
+void DocsetsDialog::extractionError(const QString &filePath, const QString &errorString)
+{
+ const QString docsetName = docsetNameForTmpFilePath(filePath);
+
+ QMessageBox::warning(this, QStringLiteral("Zeal"),
+ tr("Cannot extract docset %1: %2").arg(docsetName, errorString));
+
+ QListWidgetItem *listItem = findDocsetListItem(docsetName);
+ if (listItem)
+ listItem->setData(ProgressItemDelegate::ShowProgressRole, false);
+
+ delete m_tmpFiles.take(docsetName);
+}
+
+void DocsetsDialog::extractionProgress(const QString &filePath, qint64 extracted, qint64 total)
+{
+ const QString docsetName = docsetNameForTmpFilePath(filePath);
+ QListWidgetItem *listItem = findDocsetListItem(docsetName);
+ if (listItem)
+ listItem->setData(ProgressItemDelegate::ValueRole, percent(extracted, total));
+}
+
+void DocsetsDialog::loadDocsetList()
+{
+ const QFileInfo fi(cacheLocation(DocsetListCacheFileName));
+ if (!fi.exists() || fi.lastModified().msecsTo(QDateTime::currentDateTime()) > CacheTimeout) {
+ downloadDocsetList();
+ return;
+ }
+
+ QScopedPointer file(new QFile(fi.filePath()));
+ if (!file->open(QIODevice::ReadOnly)) {
+ downloadDocsetList();
+ return;
+ }
+
+ QJsonParseError jsonError;
+ const QJsonDocument jsonDoc = QJsonDocument::fromJson(file->readAll(), &jsonError);
+
+ if (jsonError.error != QJsonParseError::NoError) {
+ downloadDocsetList();
+ return;
+ }
+
+ // TODO: Show more user friendly labels, like "5 hours ago"
+ ui->lastUpdatedLabel->setText(fi.lastModified().toString(Qt::SystemLocaleShortDate));
+ processDocsetList(jsonDoc.array());
+}
+
+QListWidgetItem *DocsetsDialog::findDocsetListItem(const QString &name) const
+{
+ for (int i = 0; i < ui->availableDocsetList->count(); ++i) {
+ QListWidgetItem *item = ui->availableDocsetList->item(i);
+
+ if (item->data(Registry::ListModel::DocsetNameRole).toString() == name)
+ return item;
+ }
+
+ return nullptr;
+}
+
+bool DocsetsDialog::updatesAvailable() const
+{
+ for (Registry::Docset *docset : m_docsetRegistry->docsets()) {
+ if (docset->hasUpdate)
+ return true;
+ }
+
+ return false;
+}
+
+QNetworkReply *DocsetsDialog::download(const QUrl &url)
+{
+ QNetworkReply *reply = m_application->download(url);
+ connect(reply, &QNetworkReply::downloadProgress, this, &DocsetsDialog::downloadProgress);
+ 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);
+
+ updateCombinedProgress();
+
+ return reply;
+}
+
+void DocsetsDialog::cancelDownloads()
+{
+ for (QNetworkReply *reply : m_replies) {
+ // Hide progress bar
+ QListWidgetItem *listItem
+ = ui->availableDocsetList->item(reply->property(ListItemIndexProperty).toInt());
+ if (listItem)
+ listItem->setData(ProgressItemDelegate::ShowProgressRole, false);
+
+ if (reply->property(DownloadTypeProperty).toInt() == DownloadDocset)
+ delete m_tmpFiles.take(reply->property(DocsetNameProperty).toString());
+
+ reply->abort();
+ }
+
+ resetProgress();
+}
+
+void DocsetsDialog::downloadDocsetList()
+{
+ ui->availableDocsetList->clear();
+ m_availableDocsets.clear();
+
+ QNetworkReply *reply = download(QUrl(ApiServerUrl + QLatin1String("/docsets")));
+ reply->setProperty(DownloadTypeProperty, DownloadDocsetList);
+ connect(reply, &QNetworkReply::finished, this, &DocsetsDialog::downloadCompleted);
+}
+
+void DocsetsDialog::processDocsetList(const QJsonArray &list)
+{
+ for (const QJsonValue &v : list) {
+ QJsonObject docsetJson = v.toObject();
+
+ Registry::DocsetMetadata metadata(docsetJson);
+ m_availableDocsets.insert(metadata.name(), metadata);
+ }
+
+ // TODO: Move into dedicated method
+ for (const Registry::DocsetMetadata &metadata : m_availableDocsets) {
+ QListWidgetItem *listItem
+ = new QListWidgetItem(metadata.icon(), metadata.title(), ui->availableDocsetList);
+ listItem->setData(Registry::ListModel::DocsetNameRole, metadata.name());
+
+ if (m_docsetRegistry->contains(metadata.name())) {
+ listItem->setHidden(true);
+
+ Registry::Docset *docset = m_docsetRegistry->docset(metadata.name());
+
+ if (metadata.latestVersion() != docset->version()
+ || (metadata.latestVersion() == docset->version()
+ && metadata.revision() > docset->revision())) {
+ docset->hasUpdate = true;
+ ui->updateAllDocsetsButton->setEnabled(true);
+ }
+ }
+ }
+
+ ui->installedDocsetList->reset();
+}
+
+void DocsetsDialog::downloadDashDocset(const QModelIndex &index)
+{
+ const QString name = index.data(Registry::ListModel::DocsetNameRole).toString();
+
+ if (!m_availableDocsets.contains(name))
+ return;
+
+ const QString urlString = RedirectServerUrl + QStringLiteral("/d/com.kapeli/%1/latest");
+ QNetworkReply *reply = download(QUrl(urlString.arg(name)));
+ reply->setProperty(DocsetNameProperty, name);
+ reply->setProperty(DownloadTypeProperty, DownloadDocset);
+ reply->setProperty(ListItemIndexProperty,
+ ui->availableDocsetList->row(findDocsetListItem(name)));
+
+ connect(reply, &QNetworkReply::finished, this, &DocsetsDialog::downloadCompleted);
+}
+
+void DocsetsDialog::removeDocset(const QString &name)
+{
+ if (m_docsetsBeingDeleted.contains(name))
+ return;
+
+ m_docsetsBeingDeleted.append(name);
+
+ Registry::Docset *docset = m_docsetRegistry->docset(name);
+ const QString title = docset->title();
+ const QString docsetPath = docset->path();
+ const QString tmpPath = docsetPath + QLatin1String(".deleteme.")
+ + QString::number(QDateTime::currentMSecsSinceEpoch());
+
+ // Rename first to allow simultaneous installation.
+ // TODO: Check for error
+ QDir().rename(docsetPath, tmpPath);
+
+ m_docsetRegistry->remove(name);
+
+ QFuture future = QtConcurrent::run([tmpPath] {
+ return QDir(tmpPath).removeRecursively();
+ });
+
+ QFutureWatcher *watcher = new QFutureWatcher();
+ watcher->setFuture(future);
+ connect(watcher, &QFutureWatcher::finished, [=] {
+ if (!watcher->result()) {
+ QMessageBox::warning(this, QStringLiteral("Zeal"),
+ tr("Cannot delete docset %1!").arg(title));
+ }
+
+ QListWidgetItem *listItem = findDocsetListItem(name);
+ if (listItem)
+ listItem->setHidden(false);
+
+ watcher->deleteLater();
+
+ m_docsetsBeingDeleted.removeOne(name);
+ });
+}
+
+void DocsetsDialog::updateCombinedProgress()
+{
+ if (m_replies.isEmpty()) {
+ resetProgress();
+ return;
+ }
+
+ ui->combinedProgressBar->show();
+ ui->combinedProgressBar->setValue(percent(m_combinedReceived, m_combinedTotal));
+ ui->cancelButton->show();
+}
+
+void DocsetsDialog::resetProgress()
+{
+ if (!m_replies.isEmpty())
+ return;
+
+ ui->cancelButton->hide();
+ ui->combinedProgressBar->hide();
+ ui->combinedProgressBar->setValue(0);
+
+ 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->selectedIndexes()) {
+ if (index.data(Registry::ListModel::UpdateAvailableRole).toBool()) {
+ hasSelectedUpdates = true;
+ break;
+ }
+ }
+
+ ui->updateSelectedDocsetsButton->setEnabled(hasSelectedUpdates);
+ ui->updateAllDocsetsButton->setEnabled(updatesAvailable());
+ ui->removeDocsetsButton->setEnabled(selectionModel->hasSelection());
+
+ // Available docsets
+ ui->refreshButton->setEnabled(true);
+}
+
+QString DocsetsDialog::docsetNameForTmpFilePath(const QString &filePath) const
+{
+ for (const QString &key : m_tmpFiles.keys()) {
+ if (m_tmpFiles[key]->fileName() == filePath) {
+ return key;
+ }
+ }
+
+ return QString();
+}
+
+int DocsetsDialog::percent(qint64 fraction, qint64 total)
+{
+ if (!total)
+ return 0;
+
+ return static_cast(fraction / static_cast(total) * 100);
+}
+
+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);
+}
diff --git a/src/ui/settingsdialog.h b/src/libs/ui/docsetsdialog.h
similarity index 62%
rename from src/ui/settingsdialog.h
rename to src/libs/ui/docsetsdialog.h
index e951a7e15..d8a9de33a 100644
--- a/src/ui/settingsdialog.h
+++ b/src/libs/ui/docsetsdialog.h
@@ -1,8 +1,8 @@
/****************************************************************************
**
-** Copyright (C) 2015 Oleg Shparber
+** Copyright (C) 2015-2016 Oleg Shparber
** Copyright (C) 2013-2014 Jerzy Kozera
-** Contact: http://zealdocs.org/contact.html
+** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
@@ -17,50 +17,55 @@
** 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 .
+** along with Zeal. If not, see .
**
****************************************************************************/
-#ifndef SETTINGSDIALOG_H
-#define SETTINGSDIALOG_H
+#ifndef DOCSETSDIALOG_H
+#define DOCSETSDIALOG_H
-#include "registry/docsetmetadata.h"
+#include
#include
#include
#include
-class QAbstractButton;
class QListWidgetItem;
class QNetworkReply;
class QTemporaryFile;
class QUrl;
namespace Ui {
-class SettingsDialog;
+class DocsetsDialog;
}
namespace Zeal {
+namespace Registry {
class DocsetRegistry;
-class ListModel;
+} // namespace Registry
namespace Core {
class Application;
}
-class SettingsDialog : public QDialog
+class DocsetsDialog : public QDialog
{
Q_OBJECT
public:
- explicit SettingsDialog(Core::Application *app, ListModel *listModel, QWidget *parent = nullptr);
- ~SettingsDialog() override;
+ explicit DocsetsDialog(Core::Application *app, QWidget *parent = nullptr);
+ ~DocsetsDialog() override;
+
+ void reject() override;
private slots:
void addDashFeed();
void updateSelectedDocsets();
void updateAllDocsets();
void removeSelectedDocsets();
+ void updateDocsetFilter(const QString &filterString);
+
+ void downloadSelectedDocsets();
void downloadCompleted();
void downloadProgress(qint64 received, qint64 total);
@@ -69,9 +74,7 @@ private slots:
void extractionError(const QString &filePath, const QString &errorString);
void extractionProgress(const QString &filePath, qint64 extracted, qint64 total);
- void on_downloadDocsetButton_clicked();
- void on_storageButton_clicked();
- void on_tabWidget_currentChanged(int current);
+ void loadDocsetList();
private:
enum DownloadType {
@@ -80,21 +83,22 @@ private slots:
DownloadDocsetList
};
- Ui::SettingsDialog *ui = nullptr;
+ Ui::DocsetsDialog *ui = nullptr;
Core::Application *m_application = nullptr;
- DocsetRegistry *m_docsetRegistry = nullptr;
+ Registry::DocsetRegistry *m_docsetRegistry = nullptr;
QList m_replies;
qint64 m_combinedTotal = 0;
qint64 m_combinedReceived = 0;
- /// TODO: Create a special model
- QMap m_availableDocsets;
- QMap m_userFeeds;
+ // TODO: Create a special model
+ QMap m_availableDocsets;
+ QMap m_userFeeds;
QHash m_tmpFiles;
+ QStringList m_docsetsBeingDeleted;
- QListWidgetItem *findDocsetListItem(const QString &title) const;
+ QListWidgetItem *findDocsetListItem(const QString &name) const;
bool updatesAvailable() const;
QNetworkReply *download(const QUrl &url);
@@ -103,17 +107,20 @@ private slots:
void downloadDocsetList();
void processDocsetList(const QJsonArray &list);
- void downloadDashDocset(const QString &name);
- void removeDocsets(const QStringList &names);
+ void downloadDashDocset(const QModelIndex &index);
+ void removeDocset(const QString &name);
- void displayProgress();
+ void updateCombinedProgress();
void resetProgress();
- void loadSettings();
- void saveSettings();
+ // FIXME: Come up with a better approach
+ QString docsetNameForTmpFilePath(const QString &filePath) const;
+
static inline int percent(qint64 fraction, qint64 total);
+
+ static QString cacheLocation(const QString &fileName);
};
} // namespace Zeal
-#endif // SETTINGSDIALOG_H
+#endif // DOCSETSDIALOG_H
diff --git a/src/ui/forms/aboutdialog.ui b/src/libs/ui/forms/aboutdialog.ui
similarity index 93%
rename from src/ui/forms/aboutdialog.ui
rename to src/libs/ui/forms/aboutdialog.ui
index 3b01eeb1f..46061a807 100644
--- a/src/ui/forms/aboutdialog.ui
+++ b/src/libs/ui/forms/aboutdialog.ui
@@ -75,13 +75,13 @@
<strong>A simple offline documentation browser</strong>
<br><br>
-Copyright © Zeal Developers, 2013-2015.
+Copyright © Oleg Shparber and other conributors, 2013-2016.
<br>
<a href="https://zealdocs.org">zealdocs.org</a>
<br>
-<a href="irc://irc.freenode.net/zealdocs">#zealdocs</a> on <a href="http://www.freenode.net">Freenode</a>
+<a href="irc://irc.freenode.net/zealdocs">#zealdocs</a> on <a href="https://www.freenode.net">Freenode</a>
<br><br>
-Zeal is an open source software available under the terms of General Public License version 3 (<a href="http://www.gnu.org/copyleft/gpl.html">GPLv3</a>).
+Zeal is an open source software available under the terms of General Public License version 3 (<a href="https://www.gnu.org/copyleft/gpl.html">GPLv3</a>).
<br><br>
Docsets are courtesy of <a href="https://kapeli.com/dash">Dash</a>.
diff --git a/src/libs/ui/forms/docsetsdialog.ui b/src/libs/ui/forms/docsetsdialog.ui
new file mode 100644
index 000000000..c3ba0bbeb
--- /dev/null
+++ b/src/libs/ui/forms/docsetsdialog.ui
@@ -0,0 +1,257 @@
+
+
+ DocsetsDialog
+
+
+ Qt::ApplicationModal
+
+
+
+ 0
+ 0
+ 600
+ 500
+
+
+
+ Docsets
+
+
+ -
+
+
+ 0
+
+
+
+ Installed
+
+
+
-
+
+
+ QAbstractItemView::ExtendedSelection
+
+
+
+ 16
+ 16
+
+
+
+
+ -
+
+
-
+
+
+ Add feed
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ false
+
+
+ Update
+
+
+
+ -
+
+
+ false
+
+
+ Update all
+
+
+
+ -
+
+
+ false
+
+
+ Remove
+
+
+
+
+
+
+
+
+
+ Available
+
+
+ -
+
+
+ Filter docsets
+
+
+ true
+
+
+
+ -
+
+
+ QAbstractItemView::ExtendedSelection
+
+
+
+ 16
+ 16
+
+
+
+
+ -
+
+
-
+
+
+ Last updated:
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ Refresh
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ false
+
+
+ Download
+
+
+
+
+
+ -
+
+
+ <i>Docsets are provided by <a href="https://kapeli.com/dash">Dash</a>, the OS X Documentation Browser.</i>
+
+
+ Qt::RichText
+
+
+ true
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+
+ 100
+ 0
+
+
+
+
+ -
+
+
+ Cancel
+
+
+
+ -
+
+
+ QDialogButtonBox::Close
+
+
+
+
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ DocsetsDialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ DocsetsDialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/src/ui/forms/mainwindow.ui b/src/libs/ui/forms/mainwindow.ui
similarity index 58%
rename from src/ui/forms/mainwindow.ui
rename to src/libs/ui/forms/mainwindow.ui
index e31299056..6d26b9916 100644
--- a/src/ui/forms/mainwindow.ui
+++ b/src/libs/ui/forms/mainwindow.ui
@@ -6,29 +6,15 @@
0
0
- 1050
- 579
+ 900
+ 600
Zeal
-
-
-QSplitter::handle {
- background: black;
-}
-
-QFrame#searchEditFrame, QFrame#tabBarFrame {
- border-top: none;
- border-right: none;
- border-bottom: 1px solid black;
- border-left: none;
-}
-
-
-
+
0
@@ -41,24 +27,18 @@ QFrame#searchEditFrame, QFrame#tabBarFrame {
0
- -
+
-
-
- 5
-
-
- Qt::Horizontal
-
-
- 1
+
+ false
-
+
0
-
-
+
0
@@ -68,7 +48,7 @@ QFrame#searchEditFrame, QFrame#tabBarFrame {
-
+
6
@@ -95,36 +75,32 @@ QFrame#searchEditFrame, QFrame#tabBarFrame {
-
-
-
- QFrame::NoFrame
-
-
- QAbstractItemView::SelectItems
-
-
- 10
-
-
- false
-
-
-
- -
-
-
- See also
-
-
- 2
-
-
-
- -
-
-
- Qt::ScrollBarAlwaysOff
+
+
+ Qt::Vertical
+
+
+ QFrame::NoFrame
+
+
+ true
+
+
+ false
+
+
+
+
+ QFrame::NoFrame
+
+
+ Qt::ScrollBarAlwaysOff
+
+
+ true
+
+
@@ -135,7 +111,7 @@ QFrame#searchEditFrame, QFrame#tabBarFrame {
0
-
-
+
0
@@ -146,42 +122,53 @@ QFrame#searchEditFrame, QFrame#tabBarFrame {
0
-
- 12
-
0
-
+
+ Go back one page
+
+
+ QToolButton::menu-indicator { image: none; }
+
←
+
+
+
-
+
+ Go forward one page
+
+
+ QToolButton::menu-indicator { image: none; }
+
→
+
+
+
-
-
-
-
-
-
-
- 0
- 0
-
-
-
- Open URL
-
-
-
-
+
+
+
+ 0
+ 0
+
+
+
+ Open URL
+
+
@@ -207,7 +194,7 @@ QFrame#searchEditFrame, QFrame#tabBarFrame {
0
0
- 1050
+ 900
23
@@ -215,7 +202,8 @@ QFrame#searchEditFrame, QFrame#tabBarFrame {
&File
-
+
+
@@ -223,45 +211,51 @@ QFrame#searchEditFrame, QFrame#tabBarFrame {
&Help
-
-
+
+
-
@@ -328,6 +347,12 @@ QFrame#searchEditFrame, QFrame#tabBarFrame {
ui/widgets/searchablewebview.h
1
+
+ ToolBarFrame
+ QWidget
+ ui/widgets/toolbarframe.h
+ 1
+
diff --git a/src/ui/forms/settingsdialog.ui b/src/libs/ui/forms/settingsdialog.ui
similarity index 76%
rename from src/ui/forms/settingsdialog.ui
rename to src/libs/ui/forms/settingsdialog.ui
index 711ca14cf..efc3a8965 100644
--- a/src/ui/forms/settingsdialog.ui
+++ b/src/libs/ui/forms/settingsdialog.ui
@@ -9,12 +9,12 @@
0
0
- 624
- 505
+ 600
+ 500
- Zeal Options
+ Preferences
-
@@ -156,6 +156,29 @@
+
+
+ Tabs
+
+
+ -
+
+
+ Behavior
+
+
+
-
+
+
+ Open new tab after active
+
+
+
+
+
+
+
+
Appearance
@@ -370,163 +393,13 @@
-
-
-
- Installed docsets
+
+
+ Docsets are now available via the <b>Tools</b> menu.
-
-
-
-
-
-
- 16
- 16
-
-
-
-
- -
-
-
-
-
-
- Add feed
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
- -
-
-
- false
-
-
- Update
-
-
-
- -
-
-
- false
-
-
- Update all
-
-
-
- -
-
-
- false
-
-
- Remove
-
-
-
- -
-
-
- 0
-
-
-
-
-
-
-
-
- -
-
-
- Downloadable docsets
+
+ Qt::AlignCenter
-
-
-
-
-
-
- 16
- 16
-
-
-
-
- -
-
-
-
-
-
- Last updated:
-
-
-
- -
-
-
-
-
-
-
- -
-
-
- Refresh
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
- -
-
-
- true
-
-
- Download
-
-
-
-
-
- -
-
-
- <i>Docsets are provided by <a href="https://kapeli.com/dash">Dash</a>, the OS X Documentation Browser.</i>
-
-
- Qt::RichText
-
-
- true
-
-
-
-
@@ -535,9 +408,6 @@
-
-
- Qt::Horizontal
-
QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok
diff --git a/src/libs/ui/mainwindow.cpp b/src/libs/ui/mainwindow.cpp
new file mode 100644
index 000000000..d7766c687
--- /dev/null
+++ b/src/libs/ui/mainwindow.cpp
@@ -0,0 +1,937 @@
+/****************************************************************************
+**
+** Copyright (C) 2015-2016 Oleg Shparber
+** Copyright (C) 2013-2014 Jerzy Kozera
+** 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 .
+**
+****************************************************************************/
+
+#include "mainwindow.h"
+#include "ui_mainwindow.h"
+
+#include "aboutdialog.h"
+#include "docsetsdialog.h"
+#include "searchitemdelegate.h"
+#include "settingsdialog.h"
+#include "qxtglobalshortcut/qxtglobalshortcut.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef USE_WEBENGINE
+#include
+#include
+#include
+
+typedef QWebEngineHistory QWebHistory;
+typedef QWebEngineHistoryItem QWebHistoryItem;
+typedef QWebEnginePage QWebPage;
+#else
+#include
+#include
+#include
+#endif
+
+// TODO: [Qt 5.5] Remove in favour of native Qt support (QTBUG-31762)
+#ifdef USE_APPINDICATOR
+#undef signals
+#include
+#define signals public
+#include
+#endif
+
+using namespace Zeal;
+
+namespace {
+const char startPageUrl[] = "qrc:///browser/start.html";
+}
+
+struct TabState
+{
+ explicit TabState()
+ {
+ searchModel = new Registry::SearchModel();
+ tocModel = new Registry::SearchModel();
+
+ webPage = new QWebPage();
+#ifndef USE_WEBENGINE
+ webPage->setLinkDelegationPolicy(QWebPage::DelegateExternalLinks);
+ webPage->setNetworkAccessManager(Core::Application::instance()->networkManager());
+#endif
+ }
+
+ TabState(const TabState &other)
+ : searchQuery(other.searchQuery)
+ , selections(other.selections)
+ , expansions(other.expansions)
+ , searchScrollPosition(other.searchScrollPosition)
+ , tocScrollPosition(other.tocScrollPosition)
+ , webViewZoomFactor(other.webViewZoomFactor)
+ {
+ searchModel = new Registry::SearchModel(*other.searchModel);
+ tocModel = new Registry::SearchModel(*other.tocModel);
+
+ webPage = new QWebPage();
+#ifndef USE_WEBENGINE
+ webPage->setLinkDelegationPolicy(QWebPage::DelegateExternalLinks);
+ webPage->setNetworkAccessManager(Core::Application::instance()->networkManager());
+#endif
+
+ restoreHistory(other.saveHistory());
+ }
+
+ ~TabState()
+ {
+ delete searchModel;
+ delete tocModel;
+ // deleteLater() prevents crashing on quit (#577)
+ webPage->deleteLater();
+ }
+
+ void restoreHistory(const QByteArray &array) const
+ {
+ QDataStream stream(array);
+ stream >> *webPage->history();
+ }
+
+ QByteArray saveHistory() const
+ {
+ QByteArray array;
+ QDataStream stream(&array, QIODevice::WriteOnly);
+ stream << *webPage->history();
+ return array;
+ }
+
+ QUrl url() const {
+#ifdef USE_WEBENGINE
+ return webPage->url();
+#else
+ return webPage->mainFrame()->url();
+#endif
+ }
+
+ void loadUrl(const QUrl &url)
+ {
+#ifdef USE_WEBENGINE
+ webPage->load(url);
+#else
+ webPage->mainFrame()->load(url);
+#endif
+ }
+
+ QString title() const
+ {
+#ifdef USE_WEBENGINE
+ return webPage->title();
+#else
+ return webPage->mainFrame()->title();
+#endif
+ }
+
+ QString searchQuery;
+
+ // Content/Search results tree view state
+ Registry::SearchModel *searchModel = nullptr;
+ QModelIndexList selections;
+ QModelIndexList expansions;
+ int searchScrollPosition = 0;
+
+ // TOC list view state
+ Registry::SearchModel *tocModel = nullptr;
+ int tocScrollPosition = 0;
+
+ QWebPage *webPage = nullptr;
+ int webViewZoomFactor = 0;
+};
+
+MainWindow::MainWindow(Core::Application *app, QWidget *parent) :
+ QMainWindow(parent),
+ ui(new Ui::MainWindow),
+ m_application(app),
+ m_settings(app->settings()),
+ m_zealListModel(new Registry::ListModel(app->docsetRegistry(), this)),
+ m_globalShortcut(new QxtGlobalShortcut(m_settings->showShortcut, this)),
+ m_openDocsetTimer(new QTimer(this))
+{
+ ui->setupUi(this);
+
+ connect(m_settings, &Core::Settings::updated, this, &MainWindow::applySettings);
+
+#ifdef USE_APPINDICATOR
+ detectAppIndicatorSupport();
+#endif
+
+ if (m_settings->showSystrayIcon)
+ createTrayIcon();
+
+ // initialise key grabber
+ connect(m_globalShortcut, &QxtGlobalShortcut::activated, this, &MainWindow::toggleWindow);
+
+ setupTabBar();
+
+ QShortcut *focusSearch = new QShortcut(QStringLiteral("Ctrl+K"), this);
+ connect(focusSearch, &QShortcut::activated,
+ ui->lineEdit, static_cast(&SearchEdit::setFocus));
+
+ QShortcut *duplicate = new QShortcut(QStringLiteral("Ctrl+Alt+T"), this);
+ connect(duplicate, &QShortcut::activated, this, [this]() { duplicateTab(m_tabBar->currentIndex()); });
+
+ restoreGeometry(m_settings->windowGeometry);
+ ui->splitter->restoreState(m_settings->verticalSplitterGeometry);
+
+ // Menu
+ // File
+ // Some platform plugins do not define QKeySequence::Quit.
+ if (QKeySequence(QKeySequence::Quit).isEmpty())
+ ui->actionQuit->setShortcut(QStringLiteral("Ctrl+Q"));
+ else
+ ui->actionQuit->setShortcut(QKeySequence::Quit);
+
+ connect(ui->actionQuit, &QAction::triggered, qApp, &QCoreApplication::quit);
+
+ // Edit
+ ui->actionFind->setShortcut(QKeySequence::Find);
+ connect(ui->actionFind, &QAction::triggered, ui->webView, &SearchableWebView::showSearchBar);
+
+ connect(ui->actionPreferences, &QAction::triggered, [this]() {
+ m_globalShortcut->setEnabled(false);
+ QScopedPointer dialog(new SettingsDialog(m_application, this));
+ dialog->exec();
+ m_globalShortcut->setEnabled(true);
+ });
+
+ ui->actionBack->setShortcut(QKeySequence::Back);
+ addAction(ui->actionBack);
+ ui->actionForward->setShortcut(QKeySequence::Forward);
+ addAction(ui->actionForward);
+ connect(ui->actionBack, &QAction::triggered, ui->webView, &SearchableWebView::back);
+ connect(ui->actionForward, &QAction::triggered, ui->webView, &SearchableWebView::forward);
+
+ // Tools Menu
+ connect(ui->actionDocsets, &QAction::triggered, [this]() {
+ QScopedPointer dialog(new DocsetsDialog(m_application, this));
+ dialog->exec();
+ });
+
+ // Help Menu
+ connect(ui->actionSubmitFeedback, &QAction::triggered, [this]() {
+ QDesktopServices::openUrl(QUrl(QStringLiteral("https://github.com/zealdocs/zeal/issues")));
+ });
+ connect(ui->actionCheckForUpdates, &QAction::triggered,
+ m_application, &Core::Application::checkForUpdates);
+ connect(ui->actionAboutZeal, &QAction::triggered, [this]() {
+ QScopedPointer 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) {
+ QMessageBox::warning(this, QStringLiteral("Zeal"), message);
+ });
+
+ connect(m_application, &Core::Application::updateCheckDone, [this](const QString &version) {
+ if (version.isEmpty()) {
+ QMessageBox::information(this, QStringLiteral("Zeal"),
+ tr("You are using the latest version."));
+ return;
+ }
+
+ const int ret
+ = QMessageBox::information(this, QStringLiteral("Zeal"),
+ tr("Zeal %1 is available. Open download page?").arg(version),
+ QMessageBox::Yes | QMessageBox::Default,
+ QMessageBox::No | QMessageBox::Escape,
+ QMessageBox::NoButton);
+ if (ret == QMessageBox::Yes)
+ QDesktopServices::openUrl(QUrl(QStringLiteral("https://zealdocs.org/download.html")));
+ });
+
+ m_backMenu = new QMenu(ui->backButton);
+ connect(m_backMenu, &QMenu::aboutToShow, this, [this]() {
+ m_backMenu->clear();
+ QWebHistory *history = currentTabState()->webPage->history();
+ QList items = history->backItems(10);
+ // TODO: [Qt 5.6]
+ //for (auto it = items.crbegin(); it != items.crend(); ++it) {
+ for (auto it = items.cend() - 1; it >= items.cbegin(); --it) {
+ 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); });
+ QAction *action = m_backMenu->addAction(icon, it->title());
+ connect(action, &QAction::triggered, [=](bool) { history->goToItem(item); });
+ }
+ });
+ ui->backButton->setDefaultAction(ui->actionBack);
+ ui->backButton->setMenu(m_backMenu);
+
+ m_forwardMenu = new QMenu(ui->forwardButton);
+ connect(m_forwardMenu, &QMenu::aboutToShow, this, [this]() {
+ m_forwardMenu->clear();
+ QWebHistory *history = currentTabState()->webPage->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); });
+ QAction *action = m_forwardMenu->addAction(icon, item.title());
+ connect(action, &QAction::triggered, [=](bool) { history->goToItem(item); });
+ }
+ });
+ ui->forwardButton->setDefaultAction(ui->actionForward);
+ ui->forwardButton->setMenu(m_forwardMenu);
+
+ // treeView and lineEdit
+ ui->lineEdit->setTreeView(ui->treeView);
+ ui->lineEdit->setFocus();
+ setupSearchBoxCompletions();
+ SearchItemDelegate *delegate = new SearchItemDelegate(ui->treeView);
+ delegate->setDecorationRoles({Registry::SearchModel::DocsetIconRole, Qt::DecorationRole});
+ connect(ui->lineEdit, &QLineEdit::textChanged, [delegate](const QString &text) {
+ delegate->setHighlight(Registry::SearchQuery::fromString(text).query());
+ });
+ ui->treeView->setItemDelegate(delegate);
+
+ ui->tocListView->setItemDelegate(new SearchItemDelegate(ui->tocListView));
+ connect(ui->tocSplitter, &QSplitter::splitterMoved, this, [this]() {
+ m_settings->tocSplitterState = ui->tocSplitter->saveState();
+ });
+
+ createTab();
+
+ connect(ui->treeView, &QTreeView::clicked, this, &MainWindow::openDocset);
+ connect(ui->tocListView, &QListView::clicked, this, &MainWindow::openDocset);
+ connect(ui->treeView, &QTreeView::activated, this, &MainWindow::openDocset);
+ connect(ui->tocListView, &QListView::activated, this, &MainWindow::openDocset);
+
+ connect(ui->webView, &SearchableWebView::urlChanged, [this](const QUrl &url) {
+ const QString name = docsetName(url);
+ m_tabBar->setTabIcon(m_tabBar->currentIndex(), docsetIcon(name));
+
+ Registry::Docset *docset = m_application->docsetRegistry()->docset(name);
+ if (docset)
+ currentTabState()->tocModel->setResults(docset->relatedLinks(url));
+
+ ui->actionBack->setEnabled(ui->webView->canGoBack());
+ ui->actionForward->setEnabled(ui->webView->canGoForward());
+ });
+
+ connect(ui->webView, &SearchableWebView::titleChanged, [this](const QString &title) {
+ if (title.isEmpty())
+ return;
+
+ setWindowTitle(QStringLiteral("%1 - Zeal").arg(title));
+ m_tabBar->setTabText(m_tabBar->currentIndex(), title);
+ m_tabBar->setTabToolTip(m_tabBar->currentIndex(), title);
+ });
+
+ connect(ui->webView, &SearchableWebView::linkClicked, [this](const QUrl &url) {
+ const QString message = tr("Do you want to open an external link?
URL: %1");
+ int ret = QMessageBox::question(this, QStringLiteral("Zeal"), message.arg(url.toString()));
+ if (ret == QMessageBox::Yes)
+ QDesktopServices::openUrl(url);
+ });
+
+ connect(m_application->docsetRegistry(), &Registry::DocsetRegistry::queryCompleted,
+ this, [this](const QList &results) {
+ currentTabState()->searchModel->setResults(results);
+ });
+
+ connect(m_application->docsetRegistry(), &Registry::DocsetRegistry::docsetRemoved,
+ this, [this](const QString &name) {
+ setupSearchBoxCompletions();
+ for (TabState *tabState : m_tabStates) {
+ if (docsetName(tabState->url()) != name)
+ continue;
+
+ tabState->tocModel->setResults();
+
+ // optimization: disable updates temporarily because
+ // removeSearchResultWithName can call {begin,end}RemoveRows
+ // multiple times which can cause GUI updates to be suboptimal
+ // in case of many rows to be removed
+ ui->treeView->setUpdatesEnabled(false);
+ tabState->searchModel->removeSearchResultWithName(name);
+ ui->treeView->setUpdatesEnabled(true);
+
+ tabState->loadUrl(QUrl(startPageUrl));
+
+ // TODO: Cleanup history
+ }
+ });
+
+ connect(m_application->docsetRegistry(), &Registry::DocsetRegistry::docsetAdded,
+ this, [this](const QString &) {
+ setupSearchBoxCompletions();
+ });
+
+ connect(ui->lineEdit, &QLineEdit::textChanged, [this](const QString &text) {
+ if (text == currentTabState()->searchQuery)
+ return;
+
+ currentTabState()->searchQuery = text;
+ m_cancelSearch.cancel();
+ m_cancelSearch = Registry::CancellationToken();
+ m_application->docsetRegistry()->search(text, m_cancelSearch);
+ if (text.isEmpty()) {
+ currentTabState()->tocModel->setResults();
+ syncTreeView();
+ }
+ });
+
+ // Setup delayed navigation to a page until user makes a pause in typing a search query.
+ m_openDocsetTimer->setInterval(400);
+ m_openDocsetTimer->setSingleShot(true);
+ connect(m_openDocsetTimer, &QTimer::timeout, this, [this]() {
+ QModelIndex index = m_openDocsetTimer->property("index").toModelIndex();
+ if (!index.isValid())
+ return;
+
+ openDocset(index);
+
+ // Get focus back. QWebPageEngine::load() always steals focus.
+ ui->lineEdit->setFocus(Qt::MouseFocusReason);
+ });
+
+ ui->actionNewTab->setShortcut(QKeySequence::AddTab);
+ connect(ui->actionNewTab, &QAction::triggered, this, [this]() { createTab(); });
+ addAction(ui->actionNewTab);
+
+ // Save expanded items
+ connect(ui->treeView, &QTreeView::expanded, [this](QModelIndex index) {
+ if (currentTabState()->expansions.indexOf(index) == -1)
+ currentTabState()->expansions.append(index);
+ });
+
+ connect(ui->treeView, &QTreeView::collapsed, [this](QModelIndex index) {
+ currentTabState()->expansions.removeOne(index);
+ });
+
+#ifdef Q_OS_WIN32
+ ui->actionCloseTab->setShortcut(QKeySequence(Qt::Key_W + Qt::CTRL));
+#else
+ ui->actionCloseTab->setShortcut(QKeySequence::Close);
+#endif
+ addAction(ui->actionCloseTab);
+ connect(ui->actionCloseTab, &QAction::triggered, this, [this]() { closeTab(); });
+
+ connect(ui->openUrlButton, &QPushButton::clicked, [this]() {
+ const QUrl url(ui->webView->page()->history()->currentItem().url());
+ if (url.scheme() != QLatin1String("qrc"))
+ QDesktopServices::openUrl(url);
+ });
+
+ ui->actionNextTab->setShortcut(QKeySequence::NextChild);
+ addAction(ui->actionNextTab);
+ connect(ui->actionNextTab, &QAction::triggered, [this]() {
+ m_tabBar->setCurrentIndex((m_tabBar->currentIndex() + 1) % m_tabBar->count());
+ });
+
+ // TODO: Use QKeySequence::PreviousChild, when QTBUG-15746 is fixed.
+ ui->actionPreviousTab->setShortcut(Qt::ControlModifier | Qt::ShiftModifier | Qt::Key_Tab);
+ addAction(ui->actionPreviousTab);
+ connect(ui->actionPreviousTab, &QAction::triggered, [this]() {
+ m_tabBar->setCurrentIndex((m_tabBar->currentIndex() - 1 + m_tabBar->count()) % m_tabBar->count());
+ });
+
+#ifdef Q_OS_OSX
+ ui->treeView->setAttribute(Qt::WA_MacShowFocusRect, false);
+ ui->tocListView->setAttribute(Qt::WA_MacShowFocusRect, false);
+#endif
+
+ if (m_settings->checkForUpdate)
+ m_application->checkForUpdates(true);
+}
+
+MainWindow::~MainWindow()
+{
+ m_settings->verticalSplitterGeometry = ui->splitter->saveState();
+ m_settings->windowGeometry = saveGeometry();
+
+ // Delete the UI first, because it depends on tab states.
+ delete ui;
+ qDeleteAll(m_tabStates);
+}
+
+void MainWindow::search(const Registry::SearchQuery &query)
+{
+ if (query.isEmpty())
+ return;
+
+ ui->lineEdit->setText(query.toString());
+ ui->treeView->activated(ui->treeView->currentIndex());
+}
+
+void MainWindow::openDocset(const QModelIndex &index)
+{
+ const QVariant url = index.sibling(index.row(), 1).data();
+ if (url.isNull())
+ return;
+
+ ui->webView->load(url.toUrl());
+
+ // QWebEnginePage::load() always steals focus, so no need to do it twice.
+#ifndef USE_WEBENGINE
+ ui->webView->focus();
+#endif
+}
+
+QString MainWindow::docsetName(const QUrl &url) const
+{
+ const QRegExp docsetRegex(QStringLiteral("/([^/]+)[.]docset"));
+ return docsetRegex.indexIn(url.path()) != -1 ? docsetRegex.cap(1) : QString();
+}
+
+QIcon MainWindow::docsetIcon(const QString &docsetName) const
+{
+ Registry::Docset *docset = m_application->docsetRegistry()->docset(docsetName);
+ return docset ? docset->icon() : QIcon(QStringLiteral(":/icons/logo/icon.png"));
+}
+
+void MainWindow::queryCompleted()
+{
+ m_openDocsetTimer->stop();
+
+ syncTreeView();
+
+ ui->treeView->setCurrentIndex(currentTabState()->searchModel->index(0, 0, QModelIndex()));
+
+ m_openDocsetTimer->setProperty("index", ui->treeView->currentIndex());
+ m_openDocsetTimer->start();
+}
+
+void MainWindow::closeTab(int index)
+{
+ if (index == -1)
+ index = m_tabBar->currentIndex();
+
+ if (index == -1)
+ return;
+
+ delete m_tabStates.takeAt(index);
+
+ m_tabBar->removeTab(index);
+
+ if (m_tabStates.isEmpty())
+ createTab();
+}
+
+void MainWindow::createTab(int index)
+{
+ if (m_settings->openNewTabAfterActive)
+ index = m_tabBar->currentIndex() + 1;
+ else if (index == -1)
+ index = m_tabStates.size();
+
+ using Registry::SearchModel;
+ TabState *newTab = new TabState();
+ connect(newTab->searchModel, &SearchModel::queryCompleted, this, &MainWindow::queryCompleted);
+ connect(newTab->tocModel, &SearchModel::queryCompleted, this, &MainWindow::syncToc);
+
+ newTab->loadUrl(QUrl(startPageUrl));
+
+ m_tabStates.insert(index, newTab);
+ m_tabBar->insertTab(index, tr("Loading..."));
+ m_tabBar->setCurrentIndex(index);
+}
+
+void MainWindow::duplicateTab(int index)
+{
+ if (index < 0 || index >= m_tabStates.size())
+ return;
+
+ using Registry::SearchModel;
+ TabState *newTab = new TabState(*m_tabStates.at(index));
+ connect(newTab->searchModel, &SearchModel::queryCompleted, this, &MainWindow::queryCompleted);
+ connect(newTab->tocModel, &SearchModel::queryCompleted, this, &MainWindow::syncToc);
+
+ ++index;
+ m_tabStates.insert(index, newTab);
+ m_tabBar->insertTab(index, newTab->title());
+ m_tabBar->setCurrentIndex(index);
+}
+
+void MainWindow::syncTreeView()
+{
+ TabState *tabState = currentTabState();
+
+ if (!tabState->searchQuery.isEmpty()) {
+ ui->treeView->setModel(tabState->searchModel);
+ ui->treeView->setRootIsDecorated(false);
+ } else {
+ ui->treeView->setModel(m_zealListModel);
+ ui->treeView->setColumnHidden(1, true);
+ ui->treeView->setRootIsDecorated(true);
+ }
+
+ ui->treeView->reset();
+}
+
+void MainWindow::syncToc()
+{
+ if (!currentTabState()->tocModel->isEmpty()) {
+ ui->tocListView->show();
+ ui->tocSplitter->restoreState(m_settings->tocSplitterState);
+ } else {
+ ui->tocListView->hide();
+ }
+
+}
+
+TabState *MainWindow::currentTabState() const
+{
+ return m_tabStates.at(m_tabBar->currentIndex());
+}
+
+// Sets up the search box autocompletions.
+void MainWindow::setupSearchBoxCompletions()
+{
+ QStringList completions;
+ for (const Registry::Docset * const docset: m_application->docsetRegistry()->docsets()) {
+ if (docset->keywords().isEmpty())
+ continue;
+
+ completions << docset->keywords().first() + QLatin1Char(':');
+ }
+
+ ui->lineEdit->setCompletions(completions);
+}
+
+void MainWindow::setupTabBar()
+{
+ m_tabBar = new QTabBar(this);
+
+ m_tabBar->installEventFilter(this);
+
+ m_tabBar->setTabsClosable(true);
+ m_tabBar->setSelectionBehaviorOnRemove(QTabBar::SelectPreviousTab);
+ m_tabBar->setExpanding(false);
+ m_tabBar->setUsesScrollButtons(true);
+ m_tabBar->setDrawBase(false);
+ m_tabBar->setDocumentMode(true);
+ m_tabBar->setElideMode(Qt::ElideRight);
+ m_tabBar->setStyleSheet(QStringLiteral("QTabBar::tab { width: 150px; }"));
+
+ connect(m_tabBar, &QTabBar::currentChanged, this, [this](int index) {
+ static const char PreviousTabIndexProperty[] = "previousTabIndex";
+
+ if (index == -1)
+ return;
+
+ // Save previous tab state
+ const QVariant previousTabIndex = m_tabBar->property(PreviousTabIndexProperty);
+ if (previousTabIndex.isValid() && previousTabIndex.toInt() < m_tabStates.size()) {
+ TabState *previousTabState = m_tabStates.at(previousTabIndex.toInt());
+ previousTabState->selections = ui->treeView->selectionModel()->selectedIndexes();
+ previousTabState->searchScrollPosition = ui->treeView->verticalScrollBar()->value();
+ previousTabState->tocScrollPosition = ui->tocListView->verticalScrollBar()->value();
+ previousTabState->webViewZoomFactor = ui->webView->zoomFactor();
+ }
+
+ // Load current tab state
+ m_tabBar->setProperty(PreviousTabIndexProperty, index);
+ TabState *tabState = m_tabStates.at(index);
+
+ ui->lineEdit->setText(tabState->searchQuery);
+ ui->tocListView->setModel(tabState->tocModel);
+
+ syncTreeView();
+ syncToc();
+
+ // Bring back the selections and expansions
+ ui->treeView->blockSignals(true);
+ for (const QModelIndex &selection: tabState->selections)
+ ui->treeView->selectionModel()->select(selection, QItemSelectionModel::Select);
+ for (const QModelIndex &expandedIndex: tabState->expansions)
+ ui->treeView->expand(expandedIndex);
+ ui->treeView->blockSignals(false);
+
+ ui->webView->setPage(tabState->webPage);
+ ui->webView->setZoomFactor(tabState->webViewZoomFactor);
+
+ ui->actionBack->setEnabled(ui->webView->canGoBack());
+ ui->actionForward->setEnabled(ui->webView->canGoForward());
+
+ ui->treeView->verticalScrollBar()->setValue(tabState->searchScrollPosition);
+ ui->tocListView->verticalScrollBar()->setValue(tabState->tocScrollPosition);
+ });
+ connect(m_tabBar, &QTabBar::tabCloseRequested, this, &MainWindow::closeTab);
+
+ for (int i = 1; i < 10; i++) {
+ QAction *action = new QAction(m_tabBar);
+#ifdef Q_OS_LINUX
+ action->setShortcut(QStringLiteral("Alt+%1").arg(i));
+#else
+ action->setShortcut(QStringLiteral("Ctrl+%1").arg(i));
+#endif
+ if (i == 9) {
+ connect(action, &QAction::triggered, [=]() {
+ m_tabBar->setCurrentIndex(m_tabBar->count() - 1);
+ });
+ } else {
+ connect(action, &QAction::triggered, [=]() {
+ m_tabBar->setCurrentIndex(i - 1);
+ });
+ }
+
+ addAction(action);
+ }
+
+ QHBoxLayout *layout = reinterpret_cast(ui->navigationBar->layout());
+ layout->insertWidget(2, m_tabBar, 0, Qt::AlignBottom);
+}
+
+#ifdef USE_APPINDICATOR
+void MainWindow::detectAppIndicatorSupport()
+{
+ const QByteArray xdgDesktop = qgetenv("XDG_CURRENT_DESKTOP");
+
+ // Unity
+ if (xdgDesktop == "Unity") {
+ m_useAppIndicator = true;
+ return;
+ }
+
+ // Cinnamon 2.8
+ // Checking specifically for 2.8 because direct AppIndicator support will be dropped soon.
+ if (xdgDesktop == "X-Cinnamon" && qgetenv("CINNAMON_VERSION").startsWith("2.8")) {
+ m_useAppIndicator = true;
+ return;
+ }
+}
+#endif
+
+#ifdef USE_APPINDICATOR
+void appIndicatorToggleWindow(GtkMenu *menu, gpointer data)
+{
+ Q_UNUSED(menu)
+ static_cast(data)->toggleWindow();
+}
+#endif
+
+void MainWindow::createTrayIcon()
+{
+#ifdef USE_APPINDICATOR
+ if (m_trayIcon || m_appIndicator)
+ return;
+#else
+ if (m_trayIcon)
+ return;
+#endif
+
+#ifdef USE_APPINDICATOR
+ if (m_useAppIndicator) {
+ m_appIndicatorMenu = gtk_menu_new();
+
+ m_appIndicatorShowHideMenuItem = gtk_menu_item_new_with_label(qPrintable(tr("Hide")));
+ gtk_menu_shell_append(GTK_MENU_SHELL(m_appIndicatorMenu), m_appIndicatorShowHideMenuItem);
+ g_signal_connect(m_appIndicatorShowHideMenuItem, "activate",
+ G_CALLBACK(appIndicatorToggleWindow), this);
+
+ m_appIndicatorMenuSeparator = gtk_separator_menu_item_new();
+ gtk_menu_shell_append(GTK_MENU_SHELL(m_appIndicatorMenu), m_appIndicatorMenuSeparator);
+
+ m_appIndicatorQuitMenuItem = gtk_menu_item_new_with_label(qPrintable(tr("Quit")));
+ gtk_menu_shell_append(GTK_MENU_SHELL(m_appIndicatorMenu), m_appIndicatorQuitMenuItem);
+ g_signal_connect(m_appIndicatorQuitMenuItem, "activate",
+ G_CALLBACK(QCoreApplication::quit), NULL);
+
+ gtk_widget_show_all(m_appIndicatorMenu);
+
+ // NOTE: Zeal icon has to be installed, otherwise app indicator won't be shown
+ m_appIndicator = app_indicator_new("zeal", "zeal", APP_INDICATOR_CATEGORY_OTHER);
+
+ app_indicator_set_status(m_appIndicator, APP_INDICATOR_STATUS_ACTIVE);
+ app_indicator_set_menu(m_appIndicator, GTK_MENU(m_appIndicatorMenu));
+ } else { // others
+#endif
+ m_trayIcon = new QSystemTrayIcon(this);
+ m_trayIcon->setIcon(windowIcon());
+ m_trayIcon->setToolTip(QStringLiteral("Zeal"));
+
+ connect(m_trayIcon, &QSystemTrayIcon::activated, [this](QSystemTrayIcon::ActivationReason reason) {
+ if (reason != QSystemTrayIcon::Trigger && reason != QSystemTrayIcon::DoubleClick)
+ return;
+
+ toggleWindow();
+ });
+
+ QMenu *trayIconMenu = new QMenu(this);
+ trayIconMenu->addAction(ui->actionQuit);
+
+ m_trayIcon->setContextMenu(trayIconMenu);
+
+ m_trayIcon->show();
+#ifdef USE_APPINDICATOR
+ }
+#endif
+}
+
+void MainWindow::removeTrayIcon()
+{
+#ifdef USE_APPINDICATOR
+ if (!m_trayIcon && !m_appIndicator)
+ return;
+#else
+ if (!m_trayIcon)
+ return;
+#endif
+
+#ifdef USE_APPINDICATOR
+ if (m_useAppIndicator) {
+ g_clear_object(&m_appIndicator);
+ g_clear_object(&m_appIndicatorMenu);
+ g_clear_object(&m_appIndicatorShowHideMenuItem);
+ g_clear_object(&m_appIndicatorMenuSeparator);
+ g_clear_object(&m_appIndicatorQuitMenuItem);
+ } else {
+#endif
+ QMenu *trayIconMenu = m_trayIcon->contextMenu();
+ delete m_trayIcon;
+ m_trayIcon = nullptr;
+ delete trayIconMenu;
+#ifdef USE_APPINDICATOR
+ }
+#endif
+}
+
+void MainWindow::bringToFront()
+{
+ show();
+ setWindowState((windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);
+ raise();
+ activateWindow();
+
+ ui->lineEdit->setFocus();
+}
+
+void MainWindow::changeEvent(QEvent *event)
+{
+ if (m_settings->showSystrayIcon && m_settings->minimizeToSystray
+ && event->type() == QEvent::WindowStateChange && isMinimized()) {
+ hide();
+ }
+ QMainWindow::changeEvent(event);
+}
+
+void MainWindow::closeEvent(QCloseEvent *event)
+{
+ if (m_settings->showSystrayIcon && m_settings->hideOnClose) {
+ event->ignore();
+ toggleWindow();
+ }
+}
+
+bool MainWindow::eventFilter(QObject *object, QEvent *event)
+{
+ if (object == m_tabBar) {
+ switch (event->type()) {
+ case QEvent::MouseButtonRelease: {
+ QMouseEvent *e = reinterpret_cast(event);
+ if (e->button() == Qt::MiddleButton) {
+ const int index = m_tabBar->tabAt(e->pos());
+ if (index != -1) {
+ closeTab(index);
+ return true;
+ }
+ }
+ break;
+ }
+ case QEvent::Wheel:
+ // TODO: Remove in case QTBUG-8428 is fixed on all platforms
+ return true;
+ default:
+ break;
+ }
+ }
+
+ return QMainWindow::eventFilter(object, event);
+}
+
+// Captures global events in order to pass them to the search bar.
+void MainWindow::keyPressEvent(QKeyEvent *keyEvent)
+{
+ switch (keyEvent->key()) {
+ case Qt::Key_Escape:
+ ui->lineEdit->setFocus();
+ ui->lineEdit->clearQuery();
+ break;
+ case Qt::Key_Question:
+ ui->lineEdit->setFocus();
+ ui->lineEdit->selectQuery();
+ break;
+ default:
+ QMainWindow::keyPressEvent(keyEvent);
+ break;
+ }
+}
+
+void MainWindow::applySettings()
+{
+ m_globalShortcut->setShortcut(m_settings->showShortcut);
+
+ if (m_settings->showSystrayIcon)
+ createTrayIcon();
+ else
+ removeTrayIcon();
+}
+
+void MainWindow::toggleWindow()
+{
+ const bool checkActive = sender() == m_globalShortcut;
+
+ if (!isVisible() || (checkActive && !isActiveWindow())) {
+#ifdef USE_APPINDICATOR
+ if (m_appIndicator) {
+ gtk_menu_item_set_label(GTK_MENU_ITEM(m_appIndicatorShowHideMenuItem),
+ qPrintable(tr("Hide")));
+ }
+#endif
+ bringToFront();
+ } else {
+#ifdef USE_APPINDICATOR
+ if (m_trayIcon || m_appIndicator) {
+ if (m_appIndicator) {
+ gtk_menu_item_set_label(GTK_MENU_ITEM(m_appIndicatorShowHideMenuItem),
+ qPrintable(tr("Show")));
+ }
+#else
+ if (m_trayIcon) {
+#endif
+ hide();
+ } else {
+ showMinimized();
+ }
+ }
+}
diff --git a/src/ui/mainwindow.h b/src/libs/ui/mainwindow.h
similarity index 59%
rename from src/ui/mainwindow.h
rename to src/libs/ui/mainwindow.h
index d94353ad2..7d886d777 100644
--- a/src/ui/mainwindow.h
+++ b/src/libs/ui/mainwindow.h
@@ -1,8 +1,8 @@
/****************************************************************************
**
-** Copyright (C) 2015 Oleg Shparber
+** Copyright (C) 2015-2016 Oleg Shparber
** Copyright (C) 2013-2014 Jerzy Kozera
-** Contact: http://zealdocs.org/contact.html
+** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
@@ -17,24 +17,17 @@
** 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 .
+** along with Zeal. If not, see .
**
****************************************************************************/
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
-#include "registry/searchquery.h"
+#include
+#include
-#include
#include
-#include
-
-#ifdef USE_WEBENGINE
-#define QWebPage QWebEnginePage
-#define QWebHistory QWebEngineHistory
-#define QWebHistoryItem QWebEngineHistoryItem
-#endif
#ifdef USE_APPINDICATOR
struct _AppIndicator;
@@ -43,51 +36,29 @@ struct _GtkWidget;
class QxtGlobalShortcut;
+class QModelIndex;
class QSystemTrayIcon;
class QTabBar;
-class QWebHistory;
-class QWebHistoryItem;
-class QWebPage;
+class QTimer;
namespace Ui {
class MainWindow;
-}
+} // namespace Ui
namespace Zeal {
namespace Core {
class Application;
class Settings;
-}
+} // namespace Core
+namespace Registry {
class ListModel;
-class NetworkAccessManager;
-class SearchModel;
-class SettingsDialog;
+} //namespace Registry
-}
+} // namespace Zeal
-// Represents per tab search state.
-// needs to contain [search input, search model, section model, url]
-struct SearchState
-{
- QWebPage *page;
- // model representing sections
- Zeal::SearchModel *sectionsList;
- // model representing searched for items
- Zeal::SearchModel *zealSearch;
- // query being searched for
- QString searchQuery;
-
- // list of selected indices
- QModelIndexList selections;
- // list of expanded indices
- QModelIndexList expansions;
-
- int scrollPosition;
- int sectionsScroll;
- int zoomFactor;
-};
+struct TabState;
class MainWindow : public QMainWindow
{
@@ -96,8 +67,9 @@ class MainWindow : public QMainWindow
explicit MainWindow(Zeal::Core::Application *app, QWidget *parent = nullptr);
~MainWindow() override;
- void bringToFront(const Zeal::SearchQuery &query = Zeal::SearchQuery());
- void createTab();
+ void search(const Zeal::Registry::SearchQuery &query);
+ void bringToFront();
+ void createTab(int index = -1);
public slots:
void toggleWindow();
@@ -110,42 +82,39 @@ public slots:
private slots:
void applySettings();
- void back();
- void forward();
- void onSearchComplete();
void openDocset(const QModelIndex &index);
void queryCompleted();
- void scrollSearch();
- void saveTabState();
- void goToTab(int index);
void closeTab(int index = -1);
+ void duplicateTab(int index);
private:
- void displayViewActions();
+ void syncTreeView();
+ void syncToc();
void setupSearchBoxCompletions();
- void reloadTabState();
- void displayTabs();
+ void setupTabBar();
+
+ TabState *currentTabState() const;
+
QString docsetName(const QUrl &url) const;
QIcon docsetIcon(const QString &docsetName) const;
- QAction *addHistoryAction(QWebHistory *history, const QWebHistoryItem &item);
+
+#ifdef USE_APPINDICATOR
+ void detectAppIndicatorSupport();
+#endif
void createTrayIcon();
void removeTrayIcon();
- QList m_tabs;
-
- SearchState *m_searchState = nullptr;
- Zeal::NetworkAccessManager *m_zealNetworkManager = nullptr;
+ QList m_tabStates;
Ui::MainWindow *ui = nullptr;
Zeal::Core::Application *m_application = nullptr;
Zeal::Core::Settings *m_settings = nullptr;
- Zeal::ListModel *m_zealListModel = nullptr;
- Zeal::SettingsDialog *m_settingsDialog = nullptr;
+ Zeal::Registry::ListModel *m_zealListModel = nullptr;
QMenu *m_backMenu = nullptr;
QMenu *m_forwardMenu = nullptr;
- bool m_treeViewClicked = false;
+ Zeal::Registry::CancellationToken m_cancelSearch;
QxtGlobalShortcut *m_globalShortcut = nullptr;
@@ -153,7 +122,10 @@ private slots:
QSystemTrayIcon *m_trayIcon = nullptr;
+ QTimer *m_openDocsetTimer = nullptr;
+
#ifdef USE_APPINDICATOR
+ bool m_useAppIndicator = false;
_AppIndicator *m_appIndicator = nullptr;
_GtkWidget *m_appIndicatorMenu = nullptr;
_GtkWidget *m_appIndicatorQuitMenuItem = nullptr;
diff --git a/src/ui/progressitemdelegate.cpp b/src/libs/ui/progressitemdelegate.cpp
similarity index 93%
rename from src/ui/progressitemdelegate.cpp
rename to src/libs/ui/progressitemdelegate.cpp
index 3d0b880b2..9c72c97d2 100644
--- a/src/ui/progressitemdelegate.cpp
+++ b/src/libs/ui/progressitemdelegate.cpp
@@ -1,8 +1,8 @@
/****************************************************************************
**
-** Copyright (C) 2015 Oleg Shparber
+** Copyright (C) 2015-2016 Oleg Shparber
** Copyright (C) 2013-2014 Jerzy Kozera
-** Contact: http://zealdocs.org/contact.html
+** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
@@ -17,7 +17,7 @@
** 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 .
+** along with Zeal. If not, see .
**
****************************************************************************/
diff --git a/src/ui/progressitemdelegate.h b/src/libs/ui/progressitemdelegate.h
similarity index 89%
rename from src/ui/progressitemdelegate.h
rename to src/libs/ui/progressitemdelegate.h
index bc458f9ea..d3bc70b7e 100644
--- a/src/ui/progressitemdelegate.h
+++ b/src/libs/ui/progressitemdelegate.h
@@ -1,8 +1,8 @@
/****************************************************************************
**
-** Copyright (C) 2015 Oleg Shparber
+** Copyright (C) 2015-2016 Oleg Shparber
** Copyright (C) 2013-2014 Jerzy Kozera
-** Contact: http://zealdocs.org/contact.html
+** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
@@ -17,7 +17,7 @@
** 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 .
+** along with Zeal. If not, see .
**
****************************************************************************/
diff --git a/src/3rdparty/qxtglobalshortcut/qxtglobalshortcut.cpp b/src/libs/ui/qxtglobalshortcut/qxtglobalshortcut.cpp
similarity index 91%
rename from src/3rdparty/qxtglobalshortcut/qxtglobalshortcut.cpp
rename to src/libs/ui/qxtglobalshortcut/qxtglobalshortcut.cpp
index 4d067b772..0790f0dcc 100644
--- a/src/3rdparty/qxtglobalshortcut/qxtglobalshortcut.cpp
+++ b/src/libs/ui/qxtglobalshortcut/qxtglobalshortcut.cpp
@@ -1,8 +1,8 @@
/****************************************************************************
**
-** Copyright (C) 2015 Oleg Shparber
+** Copyright (C) 2015-2016 Oleg Shparber
** Copyright (C) 2013-2014 Jerzy Kozera
-** Contact: http://zealdocs.org/contact.html
+** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
@@ -17,7 +17,7 @@
** 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 .
+** along with Zeal. If not, see .
**
****************************************************************************/
/****************************************************************************
@@ -62,10 +62,7 @@ int QxtGlobalShortcutPrivate::ref = 0;
QHash, QxtGlobalShortcut *> QxtGlobalShortcutPrivate::shortcuts;
QxtGlobalShortcutPrivate::QxtGlobalShortcutPrivate(QxtGlobalShortcut *qq) :
- q_ptr(qq),
- enabled(true),
- key(Qt::Key(0)),
- mods(Qt::NoModifier)
+ q_ptr(qq)
{
#ifndef Q_OS_OSX
if (ref == 0)
@@ -96,24 +93,28 @@ bool QxtGlobalShortcutPrivate::setShortcut(const QKeySequence &shortcut)
const quint32 nativeMods = nativeModifiers(mods);
const bool res = registerShortcut(nativeKey, nativeMods);
if (res)
- shortcuts.insert(qMakePair(nativeKey, nativeMods), q);
+ shortcuts.insert({nativeKey, nativeMods}, q);
else
qWarning("QxtGlobalShortcut failed to register: %s", qPrintable(QKeySequence(key + mods).toString()));
+
return res;
}
bool QxtGlobalShortcutPrivate::unsetShortcut()
{
Q_Q(QxtGlobalShortcut);
+
bool res = false;
const quint32 nativeKey = nativeKeycode(key);
const quint32 nativeMods = nativeModifiers(mods);
- if (shortcuts.value(qMakePair(nativeKey, nativeMods)) == q)
+ if (shortcuts.value({nativeKey, nativeMods}) == q)
res = unregisterShortcut(nativeKey, nativeMods);
+
if (res)
- shortcuts.remove(qMakePair(nativeKey, nativeMods));
+ shortcuts.remove({nativeKey, nativeMods});
else
qWarning("QxtGlobalShortcut failed to unregister: %s", qPrintable(QKeySequence(key + mods).toString()));
+
key = Qt::Key(0);
mods = Qt::KeyboardModifiers(0);
return res;
@@ -121,7 +122,7 @@ bool QxtGlobalShortcutPrivate::unsetShortcut()
bool QxtGlobalShortcutPrivate::activateShortcut(quint32 nativeKey, quint32 nativeMods)
{
- QxtGlobalShortcut *shortcut = shortcuts.value(qMakePair(nativeKey, nativeMods));
+ QxtGlobalShortcut *shortcut = shortcuts.value({nativeKey, nativeMods});
if (!shortcut || !shortcut->isEnabled())
return false;
diff --git a/src/3rdparty/qxtglobalshortcut/qxtglobalshortcut.h b/src/libs/ui/qxtglobalshortcut/qxtglobalshortcut.h
similarity index 90%
rename from src/3rdparty/qxtglobalshortcut/qxtglobalshortcut.h
rename to src/libs/ui/qxtglobalshortcut/qxtglobalshortcut.h
index 60e9d1e0f..4ffbf60a2 100644
--- a/src/3rdparty/qxtglobalshortcut/qxtglobalshortcut.h
+++ b/src/libs/ui/qxtglobalshortcut/qxtglobalshortcut.h
@@ -1,8 +1,8 @@
/****************************************************************************
**
-** Copyright (C) 2015 Oleg Shparber
+** Copyright (C) 2015-2016 Oleg Shparber
** Copyright (C) 2013-2014 Jerzy Kozera
-** Contact: http://zealdocs.org/contact.html
+** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
@@ -17,7 +17,7 @@
** 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 .
+** along with Zeal. If not, see .
**
****************************************************************************/
/****************************************************************************
@@ -62,12 +62,11 @@ class QxtGlobalShortcut : public QObject
{
Q_OBJECT
- QxtGlobalShortcutPrivate *d_ptr;
+ QxtGlobalShortcutPrivate *d_ptr = nullptr;
Q_DECLARE_PRIVATE(QxtGlobalShortcut)
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
Q_PROPERTY(QKeySequence shortcut READ shortcut WRITE setShortcut)
-
public:
explicit QxtGlobalShortcut(QObject *parent = nullptr);
explicit QxtGlobalShortcut(const QKeySequence &shortcut, QObject *parent = nullptr);
@@ -78,10 +77,10 @@ class QxtGlobalShortcut : public QObject
bool isEnabled() const;
-public Q_SLOTS:
- void setEnabled(bool enabled = true);
+public slots:
+ void setEnabled(bool enabled);
-Q_SIGNALS:
+signals:
void activated();
};
diff --git a/src/libs/ui/qxtglobalshortcut/qxtglobalshortcut.pri b/src/libs/ui/qxtglobalshortcut/qxtglobalshortcut.pri
new file mode 100644
index 000000000..d4c3e3c69
--- /dev/null
+++ b/src/libs/ui/qxtglobalshortcut/qxtglobalshortcut.pri
@@ -0,0 +1,6 @@
+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
diff --git a/src/3rdparty/qxtglobalshortcut/qxtglobalshortcut_mac.cpp b/src/libs/ui/qxtglobalshortcut/qxtglobalshortcut_mac.cpp
similarity index 95%
rename from src/3rdparty/qxtglobalshortcut/qxtglobalshortcut_mac.cpp
rename to src/libs/ui/qxtglobalshortcut/qxtglobalshortcut_mac.cpp
index a1d1b3619..573a51231 100644
--- a/src/3rdparty/qxtglobalshortcut/qxtglobalshortcut_mac.cpp
+++ b/src/libs/ui/qxtglobalshortcut/qxtglobalshortcut_mac.cpp
@@ -1,8 +1,8 @@
/****************************************************************************
**
-** Copyright (C) 2015 Oleg Shparber
+** Copyright (C) 2015-2016 Oleg Shparber
** Copyright (C) 2013-2014 Jerzy Kozera
-** Contact: http://zealdocs.org/contact.html
+** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
@@ -17,7 +17,7 @@
** 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 .
+** along with Zeal. If not, see .
**
****************************************************************************/
/****************************************************************************
@@ -66,8 +66,8 @@ static bool qxt_mac_handler_installed = false;
OSStatus qxt_mac_handle_hot_key(EventHandlerCallRef nextHandler, EventRef event, void *data)
{
- Q_UNUSED(nextHandler);
- Q_UNUSED(data);
+ Q_UNUSED(nextHandler)
+ Q_UNUSED(data)
if (GetEventClass(event) == kEventClassKeyboard && GetEventKind(event) == kEventHotKeyPressed) {
EventHotKeyID keyID;
GetEventParameter(event, kEventParamDirectObject, typeEventHotKeyID, NULL, sizeof(keyID), NULL, &keyID);
diff --git a/src/3rdparty/qxtglobalshortcut/qxtglobalshortcut_p.h b/src/libs/ui/qxtglobalshortcut/qxtglobalshortcut_p.h
similarity index 90%
rename from src/3rdparty/qxtglobalshortcut/qxtglobalshortcut_p.h
rename to src/libs/ui/qxtglobalshortcut/qxtglobalshortcut_p.h
index 274d7b170..45704e22f 100644
--- a/src/3rdparty/qxtglobalshortcut/qxtglobalshortcut_p.h
+++ b/src/libs/ui/qxtglobalshortcut/qxtglobalshortcut_p.h
@@ -1,8 +1,8 @@
/****************************************************************************
**
-** Copyright (C) 2015 Oleg Shparber
+** Copyright (C) 2015-2016 Oleg Shparber
** Copyright (C) 2013-2014 Jerzy Kozera
-** Contact: http://zealdocs.org/contact.html
+** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
@@ -17,7 +17,7 @@
** 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 .
+** along with Zeal. If not, see .
**
****************************************************************************/
/****************************************************************************
@@ -62,22 +62,23 @@ class QxtGlobalShortcut;
class QxtGlobalShortcutPrivate : public QAbstractNativeEventFilter
{
- QxtGlobalShortcut *q_ptr;
+ QxtGlobalShortcut *q_ptr = nullptr;
Q_DECLARE_PUBLIC(QxtGlobalShortcut)
public:
QxtGlobalShortcutPrivate(QxtGlobalShortcut *qq);
~QxtGlobalShortcutPrivate() override;
- bool enabled;
- Qt::Key key;
- Qt::KeyboardModifiers mods;
-
- bool setShortcut(const QKeySequence &shortcut);
- bool unsetShortcut();
+ bool enabled = true;
+ Qt::Key key = Qt::Key(0);
+ Qt::KeyboardModifiers mods = Qt::NoModifier;
#ifndef Q_OS_OSX
static int ref;
#endif // Q_OS_OSX
+
+ bool setShortcut(const QKeySequence &shortcut);
+ bool unsetShortcut();
+
virtual bool nativeEventFilter(const QByteArray &eventType, void *message,
long *result) override;
diff --git a/src/3rdparty/qxtglobalshortcut/qxtglobalshortcut_win.cpp b/src/libs/ui/qxtglobalshortcut/qxtglobalshortcut_win.cpp
similarity index 94%
rename from src/3rdparty/qxtglobalshortcut/qxtglobalshortcut_win.cpp
rename to src/libs/ui/qxtglobalshortcut/qxtglobalshortcut_win.cpp
index eae080c86..6f3d4a01b 100644
--- a/src/3rdparty/qxtglobalshortcut/qxtglobalshortcut_win.cpp
+++ b/src/libs/ui/qxtglobalshortcut/qxtglobalshortcut_win.cpp
@@ -1,8 +1,8 @@
/****************************************************************************
**
-** Copyright (C) 2015 Oleg Shparber
+** Copyright (C) 2015-2016 Oleg Shparber
** Copyright (C) 2013-2014 Jerzy Kozera
-** Contact: http://zealdocs.org/contact.html
+** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
@@ -17,7 +17,7 @@
** 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 .
+** along with Zeal. If not, see .
**
****************************************************************************/
/****************************************************************************
@@ -57,8 +57,8 @@
bool QxtGlobalShortcutPrivate::nativeEventFilter(const QByteArray &eventType,
void *message, long *result)
{
- Q_UNUSED(eventType);
- Q_UNUSED(result);
+ Q_UNUSED(eventType)
+ Q_UNUSED(result)
MSG *msg = static_cast(message);
if (msg->message == WM_HOTKEY) {
@@ -83,7 +83,7 @@ quint32 QxtGlobalShortcutPrivate::nativeModifiers(Qt::KeyboardModifiers modifier
native |= MOD_ALT;
if (modifiers & Qt::MetaModifier)
native |= MOD_WIN;
- /// TODO: resolve these?
+ // TODO: resolve these?
//if (modifiers & Qt::KeypadModifier)
//if (modifiers & Qt::GroupSwitchModifier)
return native;
diff --git a/src/3rdparty/qxtglobalshortcut/qxtglobalshortcut_x11.cpp b/src/libs/ui/qxtglobalshortcut/qxtglobalshortcut_x11.cpp
similarity index 93%
rename from src/3rdparty/qxtglobalshortcut/qxtglobalshortcut_x11.cpp
rename to src/libs/ui/qxtglobalshortcut/qxtglobalshortcut_x11.cpp
index 24649b3d3..228ff1e37 100644
--- a/src/3rdparty/qxtglobalshortcut/qxtglobalshortcut_x11.cpp
+++ b/src/libs/ui/qxtglobalshortcut/qxtglobalshortcut_x11.cpp
@@ -1,8 +1,8 @@
/****************************************************************************
**
-** Copyright (C) 2015 Oleg Shparber
+** Copyright (C) 2015-2016 Oleg Shparber
** Copyright (C) 2013-2014 Jerzy Kozera
-** Contact: http://zealdocs.org/contact.html
+** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
@@ -17,7 +17,7 @@
** 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