mirror of
https://github.com/zealdocs/zeal.git
synced 2026-08-30 00:50:51 +08:00
770 lines
25 KiB
C++
770 lines
25 KiB
C++
#include "mainwindow.h"
|
|
#include "ui_mainwindow.h"
|
|
|
|
#include "aboutdialog.h"
|
|
#include "networkaccessmanager.h"
|
|
#include "searchitemdelegate.h"
|
|
#include "settingsdialog.h"
|
|
#include "core/application.h"
|
|
#include "core/settings.h"
|
|
#include "registry/docsetregistry.h"
|
|
#include "registry/listmodel.h"
|
|
#include "registry/searchmodel.h"
|
|
|
|
#include <QAbstractEventDispatcher>
|
|
#include <QCloseEvent>
|
|
#include <QDesktopServices>
|
|
#include <QFileInfo>
|
|
#include <QKeyEvent>
|
|
#include <QMenu>
|
|
#include <QMessageBox>
|
|
#include <QScrollBar>
|
|
#include <QShortcut>
|
|
#include <QSystemTrayIcon>
|
|
#include <QTabBar>
|
|
#include <QTimer>
|
|
|
|
#ifdef USE_WEBENGINE
|
|
#include <QWebEngineHistory>
|
|
#include <QWebEnginePage>
|
|
#include <QWebEngineSettings>
|
|
#else
|
|
#include <QWebFrame>
|
|
#include <QWebHistory>
|
|
#include <QWebPage>
|
|
#endif
|
|
|
|
#include <qxtglobalshortcut.h>
|
|
|
|
/// TODO: [Qt 5.5] Remove in favour of native Qt support (QTBUG-31762)
|
|
#ifdef USE_APPINDICATOR
|
|
#undef signals
|
|
#include <libappindicator/app-indicator.h>
|
|
#define signals public
|
|
#include <gtk/gtk.h>
|
|
#endif
|
|
|
|
using namespace Zeal;
|
|
|
|
namespace {
|
|
const char startPageUrl[] = "qrc:///browser/start.html";
|
|
}
|
|
|
|
MainWindow::MainWindow(Core::Application *app, QWidget *parent) :
|
|
QMainWindow(parent),
|
|
ui(new Ui::MainWindow),
|
|
m_application(app),
|
|
m_settings(app->settings()),
|
|
m_zealListModel(new ListModel(app->docsetRegistry(), this)),
|
|
m_settingsDialog(new SettingsDialog(app, m_zealListModel, this)),
|
|
m_globalShortcut(new QxtGlobalShortcut(m_settings->showShortcut, this))
|
|
{
|
|
connect(m_settings, &Core::Settings::updated, this, &MainWindow::applySettings);
|
|
|
|
m_tabBar = new QTabBar(this);
|
|
|
|
setWindowIcon(QIcon::fromTheme(QStringLiteral("zeal"), QIcon(QStringLiteral(":/zeal.ico"))));
|
|
|
|
if (m_settings->showSystrayIcon)
|
|
createTrayIcon();
|
|
|
|
// initialise key grabber
|
|
connect(m_globalShortcut, &QxtGlobalShortcut::activated, [this]() {
|
|
if (!isVisible() || !isActiveWindow()) {
|
|
bringToFront();
|
|
} else {
|
|
#ifdef USE_APPINDICATOR
|
|
if (m_trayIcon || m_appIndicator) {
|
|
#else
|
|
if (m_trayIcon) {
|
|
#endif
|
|
hide();
|
|
} else {
|
|
showMinimized();
|
|
}
|
|
}
|
|
});
|
|
|
|
m_application->docsetRegistry()->init(m_settings->docsetPath);
|
|
|
|
// initialise ui
|
|
ui->setupUi(this);
|
|
|
|
setupShortcuts();
|
|
|
|
restoreGeometry(m_settings->windowGeometry);
|
|
ui->splitter->restoreState(m_settings->splitterGeometry);
|
|
connect(ui->splitter, &QSplitter::splitterMoved, [=](int, int) {
|
|
m_settings->splitterGeometry = ui->splitter->saveState();
|
|
});
|
|
|
|
m_zealNetworkManager = new NetworkAccessManager(this);
|
|
#ifdef USE_WEBENGINE
|
|
/// FIXME AngularJS workaround (zealnetworkaccessmanager.cpp)
|
|
#else
|
|
ui->webView->page()->setNetworkAccessManager(m_zealNetworkManager);
|
|
#endif
|
|
|
|
// menu
|
|
if (QKeySequence(QKeySequence::Quit) != QKeySequence(QStringLiteral("Ctrl+Q"))) {
|
|
ui->actionQuit->setShortcuts(QList<QKeySequence>{QKeySequence(QStringLiteral("Ctrl+Q")),
|
|
QKeySequence::Quit});
|
|
} else {
|
|
// Quit == Ctrl+Q - don't set the same sequence twice because it causes
|
|
// "QAction::eventFilter: Ambiguous shortcut overload: Ctrl+Q"
|
|
ui->actionQuit->setShortcuts(QList<QKeySequence>{QKeySequence::Quit});
|
|
}
|
|
addAction(ui->actionQuit);
|
|
connect(ui->actionQuit, &QAction::triggered, [=]() {
|
|
m_settings->windowGeometry = saveGeometry();
|
|
});
|
|
connect(ui->actionQuit, &QAction::triggered, qApp, &QCoreApplication::quit);
|
|
|
|
connect(ui->actionOptions, &QAction::triggered, [=]() {
|
|
m_globalShortcut->setEnabled(false);
|
|
m_settingsDialog->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, this, &MainWindow::back);
|
|
connect(ui->actionForward, &QAction::triggered, this, &MainWindow::forward);
|
|
|
|
// Help Menu
|
|
connect(ui->actionReportProblem, &QAction::triggered, [this]() {
|
|
QDesktopServices::openUrl(QStringLiteral("https://github.com/zealdocs/zeal/issues"));
|
|
});
|
|
connect(ui->actionCheckForUpdate, &QAction::triggered,
|
|
m_application, &Core::Application::checkUpdate);
|
|
connect(ui->actionAboutZeal, &QAction::triggered, [this]() {
|
|
QScopedPointer<AboutDialog> dialog(new AboutDialog(this));
|
|
dialog->exec();
|
|
});
|
|
connect(ui->actionAboutQt, &QAction::triggered, [this]() {
|
|
QMessageBox::aboutQt(this);
|
|
});
|
|
|
|
// Update check
|
|
connect(m_application, &Core::Application::updateCheckError, [this](const QString &message) {
|
|
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 Zeal version."));
|
|
return;
|
|
}
|
|
|
|
const int ret = QMessageBox::information(this, QStringLiteral("Zeal"),
|
|
QString(tr("A new version <b>%1</b> is available. Open download page?")).arg(version),
|
|
QMessageBox::Yes, QMessageBox::No);
|
|
if (ret == QMessageBox::Yes)
|
|
QDesktopServices::openUrl(QStringLiteral("http://zealdocs.org/download.html"));
|
|
});
|
|
|
|
m_backMenu = new QMenu(ui->backButton);
|
|
ui->backButton->setMenu(m_backMenu);
|
|
|
|
m_forwardMenu = new QMenu(ui->forwardButton);
|
|
ui->forwardButton->setMenu(m_forwardMenu);
|
|
|
|
displayViewActions();
|
|
|
|
// treeView and lineEdit
|
|
ui->lineEdit->setTreeView(ui->treeView);
|
|
ui->lineEdit->setFocus();
|
|
setupSearchBoxCompletions();
|
|
ui->treeView->setModel(m_zealListModel);
|
|
ui->treeView->setColumnHidden(1, true);
|
|
ui->treeView->setItemDelegate(new SearchItemDelegate(ui->lineEdit, ui->treeView));
|
|
|
|
createTab();
|
|
|
|
connect(ui->treeView, &QTreeView::clicked, [this](const QModelIndex &index) {
|
|
m_treeViewClicked = true;
|
|
ui->treeView->activated(index);
|
|
});
|
|
connect(ui->sections, &QListView::clicked, [this](const QModelIndex &index) {
|
|
m_treeViewClicked = true;
|
|
ui->sections->activated(index);
|
|
});
|
|
connect(ui->treeView, &QTreeView::activated, this, &MainWindow::openDocset);
|
|
connect(ui->sections, &QListView::activated, this, &MainWindow::openDocset);
|
|
connect(ui->forwardButton, &QPushButton::clicked, this, &MainWindow::forward);
|
|
connect(ui->backButton, &QPushButton::clicked, this, &MainWindow::back);
|
|
|
|
connect(ui->webView, &SearchableWebView::urlChanged, [this](const QUrl &url) {
|
|
const QString name = docsetName(url);
|
|
m_tabBar->setTabIcon(m_tabBar->currentIndex(), docsetIcon(name));
|
|
|
|
Docset *docset = m_application->docsetRegistry()->docset(name);
|
|
if (docset)
|
|
m_searchState->sectionsList->setResults(docset->relatedLinks(url));
|
|
|
|
displayViewActions();
|
|
});
|
|
|
|
connect(ui->webView, &SearchableWebView::titleChanged, [this](const QString &) {
|
|
displayViewActions();
|
|
});
|
|
|
|
connect(ui->webView, &SearchableWebView::linkClicked, [this](const QUrl &url) {
|
|
const QString message = tr("Do you want to open an external link?<br>URL: <b>%1</b>");
|
|
int ret = QMessageBox::question(this, QStringLiteral("Zeal"), message.arg(url.toString()));
|
|
if (ret == QMessageBox::Yes)
|
|
QDesktopServices::openUrl(url);
|
|
});
|
|
|
|
ui->sections->hide();
|
|
ui->seeAlsoLabel->hide();
|
|
ui->sections->setModel(m_searchState->sectionsList);
|
|
connect(m_application->docsetRegistry(), &DocsetRegistry::queryCompleted, this, &MainWindow::onSearchComplete);
|
|
|
|
connect(m_application->docsetRegistry(), &DocsetRegistry::docsetRemoved,
|
|
[this](const QString &name) {
|
|
for (SearchState *searchState : m_tabs) {
|
|
#ifdef USE_WEBENGINE
|
|
if (docsetName(searchState->page->url()) != name)
|
|
continue;
|
|
|
|
searchState->page->load(QUrl(startPageUrl));
|
|
#else
|
|
if (docsetName(searchState->page->mainFrame()->url()) != name)
|
|
continue;
|
|
|
|
searchState->page->mainFrame()->load(QUrl(startPageUrl));
|
|
#endif
|
|
/// TODO: Cleanup history
|
|
}
|
|
});
|
|
|
|
connect(ui->lineEdit, &QLineEdit::textChanged, [this](const QString &text) {
|
|
if (!m_searchState || text == m_searchState->searchQuery)
|
|
return;
|
|
|
|
m_searchState->searchQuery = text;
|
|
m_application->docsetRegistry()->search(text);
|
|
if (text.isEmpty()) {
|
|
m_searchState->sectionsList->setResults();
|
|
ui->treeView->setModel(m_zealListModel);
|
|
}
|
|
});
|
|
|
|
ui->actionNewTab->setShortcut(QKeySequence::AddTab);
|
|
addAction(ui->actionNewTab);
|
|
connect(ui->actionNewTab, &QAction::triggered, [this]() {
|
|
saveTabState();
|
|
createTab();
|
|
});
|
|
|
|
// save the expanded items:
|
|
connect(ui->treeView, &QTreeView::expanded, [this](QModelIndex index) {
|
|
if (m_searchState->expansions.indexOf(index) == -1)
|
|
m_searchState->expansions.append(index);
|
|
});
|
|
|
|
connect(ui->treeView, &QTreeView::collapsed, [this](QModelIndex index) {
|
|
m_searchState->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, &MainWindow::closeTab);
|
|
|
|
m_tabBar->setTabsClosable(true);
|
|
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, &MainWindow::goToTab);
|
|
connect(m_tabBar, &QTabBar::tabCloseRequested, this, &MainWindow::closeTab);
|
|
|
|
((QHBoxLayout *)ui->tabBarFrame->layout())->insertWidget(2, m_tabBar, 0, Qt::AlignBottom);
|
|
|
|
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());
|
|
});
|
|
|
|
ui->actionPreviousTab->setShortcut(QKeySequence::PreviousChild);
|
|
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->sections->setAttribute(Qt::WA_MacShowFocusRect, false);
|
|
#endif
|
|
|
|
/// TODO: Remove in the future releases
|
|
// Check pre-0.1 docset path
|
|
QString oldDocsetDir = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
|
|
oldDocsetDir.remove(QStringLiteral("Zeal/Zeal"));
|
|
oldDocsetDir += QLatin1String("zeal/docsets");
|
|
if (QFileInfo::exists(oldDocsetDir) && m_settings->docsetPath != oldDocsetDir) {
|
|
QMessageBox::information(this, QStringLiteral("Zeal"),
|
|
QString(tr("Old docset storage has been found in <b>%1</b>. "
|
|
"You can move docsets to <b>%2</b> or change the docset storage path in the settings. <br><br>"
|
|
"Please note, that old docsets cannot be updated automatically, so it is better to download your docsets again. <br><br>"
|
|
"Remove or use the old docset storage to avoid this message in the future."))
|
|
.arg(oldDocsetDir, m_settings->docsetPath));
|
|
}
|
|
}
|
|
|
|
MainWindow::~MainWindow()
|
|
{
|
|
delete ui;
|
|
|
|
for (SearchState *state : m_tabs) {
|
|
delete state->zealSearch;
|
|
delete state->sectionsList;
|
|
delete state;
|
|
}
|
|
}
|
|
|
|
void MainWindow::openDocset(const QModelIndex &index)
|
|
{
|
|
const QVariant urlStr = index.sibling(index.row(), 1).data();
|
|
if (urlStr.isNull())
|
|
return;
|
|
|
|
/// TODO: Keep anchor separately from file address
|
|
QStringList urlParts = urlStr.toString().split(QLatin1Char('#'));
|
|
QUrl url = QUrl::fromLocalFile(urlParts[0]);
|
|
if (urlParts.count() > 1)
|
|
/// NOTE: QUrl::DecodedMode is a fix for #121. Let's hope it doesn't break anything.
|
|
url.setFragment(urlParts[1], QUrl::DecodedMode);
|
|
|
|
ui->webView->load(url);
|
|
|
|
if (!m_treeViewClicked)
|
|
ui->webView->focus();
|
|
else
|
|
m_treeViewClicked = false;
|
|
}
|
|
|
|
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
|
|
{
|
|
const Docset * const docset = m_application->docsetRegistry()->docset(docsetName);
|
|
if (!docset)
|
|
return QIcon(QStringLiteral(":/icons/logo/icon.png"));
|
|
return docset->icon();
|
|
}
|
|
|
|
void MainWindow::queryCompleted()
|
|
{
|
|
m_treeViewClicked = true;
|
|
|
|
ui->treeView->setModel(m_searchState->zealSearch);
|
|
ui->treeView->setColumnHidden(1, true);
|
|
ui->treeView->setCurrentIndex(m_searchState->zealSearch->index(0, 0, QModelIndex()));
|
|
ui->treeView->activated(ui->treeView->currentIndex());
|
|
}
|
|
|
|
void MainWindow::goToTab(int index)
|
|
{
|
|
saveTabState();
|
|
|
|
if (m_tabs.isEmpty())
|
|
return;
|
|
|
|
m_searchState = m_tabs.at(index);
|
|
reloadTabState();
|
|
}
|
|
|
|
void MainWindow::closeTab(int index)
|
|
{
|
|
if (index == -1)
|
|
index = m_tabBar->currentIndex();
|
|
|
|
/// TODO: proper deletion here
|
|
SearchState *tab = m_tabs.takeAt(index);
|
|
|
|
if (m_searchState == tab)
|
|
m_searchState = nullptr;
|
|
|
|
delete tab->zealSearch;
|
|
delete tab->sectionsList;
|
|
delete tab;
|
|
|
|
m_tabBar->removeTab(index);
|
|
|
|
if (m_tabs.count() == 0)
|
|
createTab();
|
|
}
|
|
|
|
void MainWindow::createTab()
|
|
{
|
|
SearchState *newTab = new SearchState();
|
|
newTab->zealSearch = new Zeal::SearchModel();
|
|
newTab->sectionsList = new Zeal::SearchModel();
|
|
|
|
connect(newTab->zealSearch, &SearchModel::queryCompleted, this, &MainWindow::queryCompleted);
|
|
connect(newTab->sectionsList, &SearchModel::queryCompleted, [=]() {
|
|
const bool hasResults = newTab->sectionsList->rowCount(QModelIndex());
|
|
ui->sections->setVisible(hasResults);
|
|
ui->seeAlsoLabel->setVisible(hasResults);
|
|
});
|
|
|
|
ui->lineEdit->clear();
|
|
|
|
newTab->page = new QWebPage(ui->webView);
|
|
#ifndef USE_WEBENGINE
|
|
newTab->page->setLinkDelegationPolicy(QWebPage::DelegateExternalLinks);
|
|
newTab->page->setNetworkAccessManager(m_zealNetworkManager);
|
|
#endif
|
|
|
|
ui->treeView->setModel(NULL);
|
|
ui->treeView->setModel(m_zealListModel);
|
|
ui->treeView->setColumnHidden(1, true);
|
|
|
|
m_tabs.append(newTab);
|
|
m_searchState = newTab;
|
|
|
|
m_tabBar->addTab(QStringLiteral("title"));
|
|
m_tabBar->setCurrentIndex(m_tabs.size() - 1);
|
|
|
|
reloadTabState();
|
|
#ifdef USE_WEBENGINE
|
|
newTab->page->load(QUrl(startPageUrl));
|
|
#else
|
|
newTab->page->mainFrame()->load(QUrl(startPageUrl));
|
|
#endif
|
|
}
|
|
|
|
void MainWindow::displayTabs()
|
|
{
|
|
ui->menuTabs->clear();
|
|
ui->menuTabs->addAction(ui->actionNewTab);
|
|
ui->menuTabs->addAction(ui->actionCloseTab);
|
|
ui->menuTabs->addSeparator();
|
|
ui->menuTabs->addAction(ui->actionNextTab);
|
|
ui->menuTabs->addAction(ui->actionPreviousTab);
|
|
ui->menuTabs->addSeparator();
|
|
|
|
ui->actionNextTab->setEnabled(m_tabBar->count() > 1);
|
|
ui->actionPreviousTab->setEnabled(m_tabBar->count() > 1);
|
|
|
|
for (int i = 0; i < m_tabs.count(); i++) {
|
|
SearchState *state = m_tabs.at(i);
|
|
#ifdef USE_WEBENGINE
|
|
QString title = state->page->title();
|
|
#else
|
|
QString title = state->page->history()->currentItem().title();
|
|
#endif
|
|
QAction *action = ui->menuTabs->addAction(title);
|
|
action->setCheckable(true);
|
|
action->setChecked(i == m_tabBar->currentIndex());
|
|
|
|
if (i < 10) {
|
|
#ifdef Q_OS_LINUX
|
|
const QKeySequence shortcut = QString("Alt+%1").arg(QString::number((i + 1) % 10));
|
|
#else
|
|
const QKeySequence shortcut = QString("Ctrl+%1").arg(QString::number((i + 1) % 10));
|
|
#endif
|
|
|
|
for (QAction *oldAction : actions()) {
|
|
if (oldAction->shortcut() == shortcut)
|
|
removeAction(oldAction);
|
|
}
|
|
|
|
action->setShortcut(shortcut);
|
|
addAction(action);
|
|
}
|
|
|
|
m_tabBar->setTabText(i, title);
|
|
connect(action, &QAction::triggered, [=]() {
|
|
m_tabBar->setCurrentIndex(i);
|
|
});
|
|
}
|
|
}
|
|
|
|
void MainWindow::reloadTabState()
|
|
{
|
|
ui->lineEdit->setText(m_searchState->searchQuery);
|
|
ui->sections->setModel(m_searchState->sectionsList);
|
|
|
|
if (!m_searchState->searchQuery.isEmpty()) {
|
|
ui->treeView->setModel(m_searchState->zealSearch);
|
|
} else {
|
|
ui->treeView->setModel(m_zealListModel);
|
|
ui->treeView->setColumnHidden(1, true);
|
|
}
|
|
|
|
// Bring back the selections and expansions.
|
|
for (const QModelIndex &selection: m_searchState->selections)
|
|
ui->treeView->selectionModel()->select(selection, QItemSelectionModel::Select);
|
|
for (const QModelIndex &expandedIndex: m_searchState->expansions)
|
|
ui->treeView->expand(expandedIndex);
|
|
|
|
ui->webView->setPage(m_searchState->page);
|
|
ui->webView->setZealZoomFactor(m_searchState->zoomFactor);
|
|
|
|
int resultCount = m_searchState->sectionsList->rowCount(QModelIndex());
|
|
ui->sections->setVisible(resultCount > 1);
|
|
ui->seeAlsoLabel->setVisible(resultCount > 1);
|
|
|
|
// scroll after the object gets loaded
|
|
/// TODO: [Qt 5.4] QTimer::singleShot(100, this, &MainWindow::scrollSearch);
|
|
QTimer::singleShot(100, this, SLOT(scrollSearch()));
|
|
|
|
displayViewActions();
|
|
}
|
|
|
|
void MainWindow::scrollSearch()
|
|
{
|
|
ui->treeView->verticalScrollBar()->setValue(m_searchState->scrollPosition);
|
|
ui->sections->verticalScrollBar()->setValue(m_searchState->sectionsScroll);
|
|
}
|
|
|
|
void MainWindow::saveTabState()
|
|
{
|
|
if (!m_searchState)
|
|
return;
|
|
m_searchState->searchQuery = ui->lineEdit->text();
|
|
m_searchState->selections = ui->treeView->selectionModel()->selectedIndexes();
|
|
m_searchState->scrollPosition = ui->treeView->verticalScrollBar()->value();
|
|
m_searchState->sectionsScroll = ui->sections->verticalScrollBar()->value();
|
|
m_searchState->zoomFactor = ui->webView->zealZoomFactor();
|
|
}
|
|
|
|
void MainWindow::onSearchComplete()
|
|
{
|
|
m_searchState->zealSearch->setResults(m_application->docsetRegistry()->queryResults());
|
|
}
|
|
|
|
// Sets up the search box autocompletions.
|
|
void MainWindow::setupSearchBoxCompletions()
|
|
{
|
|
QStringList completions;
|
|
for (const Docset * const docset: m_application->docsetRegistry()->docsets())
|
|
completions << docset->keyword() + QLatin1Char(':');
|
|
ui->lineEdit->setCompletions(completions);
|
|
}
|
|
|
|
void MainWindow::displayViewActions()
|
|
{
|
|
ui->actionBack->setEnabled(ui->webView->canGoBack());
|
|
ui->backButton->setEnabled(ui->webView->canGoBack());
|
|
ui->actionForward->setEnabled(ui->webView->canGoForward());
|
|
ui->forwardButton->setEnabled(ui->webView->canGoForward());
|
|
|
|
ui->menuView->clear();
|
|
ui->menuView->addAction(ui->actionBack);
|
|
ui->menuView->addAction(ui->actionForward);
|
|
ui->menuView->addSeparator();
|
|
|
|
m_backMenu->clear();
|
|
m_forwardMenu->clear();
|
|
|
|
QWebHistory *history = ui->webView->page()->history();
|
|
for (const QWebHistoryItem &item: history->backItems(10))
|
|
m_backMenu->addAction(addHistoryAction(history, item));
|
|
if (history->count() > 0)
|
|
addHistoryAction(history, history->currentItem())->setEnabled(false);
|
|
for (const QWebHistoryItem &item: history->forwardItems(10))
|
|
m_forwardMenu->addAction(addHistoryAction(history, item));
|
|
|
|
displayTabs();
|
|
}
|
|
|
|
void MainWindow::back()
|
|
{
|
|
ui->webView->back();
|
|
displayViewActions();
|
|
}
|
|
|
|
void MainWindow::forward()
|
|
{
|
|
ui->webView->forward();
|
|
displayViewActions();
|
|
}
|
|
|
|
QAction *MainWindow::addHistoryAction(QWebHistory *history, const QWebHistoryItem &item)
|
|
{
|
|
const QIcon icon = docsetIcon(docsetName(item.url()));
|
|
QAction *backAction = new QAction(icon, item.title(), ui->menuView);
|
|
ui->menuView->addAction(backAction);
|
|
connect(backAction, &QAction::triggered, [=](bool) {
|
|
history->goToItem(item);
|
|
});
|
|
|
|
return backAction;
|
|
}
|
|
|
|
void MainWindow::createTrayIcon()
|
|
{
|
|
#ifdef USE_APPINDICATOR
|
|
if (m_trayIcon || m_appIndicator)
|
|
return;
|
|
#else
|
|
if (m_trayIcon)
|
|
return;
|
|
#endif
|
|
|
|
#ifdef USE_APPINDICATOR
|
|
const QString desktop = getenv("XDG_CURRENT_DESKTOP");
|
|
const bool isUnity = (desktop.toLower() == QLatin1String("unity"));
|
|
|
|
if (isUnity) { // Application Indicators for Unity
|
|
m_appIndicatorMenu = gtk_menu_new();
|
|
|
|
m_appIndicatorQuitMenuItem = gtk_menu_item_new_with_label("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(m_appIndicatorQuitMenuItem);
|
|
|
|
/// 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;
|
|
|
|
// Disable, when settings window is open
|
|
if (m_settingsDialog->isVisible()) {
|
|
m_settingsDialog->activateWindow();
|
|
return;
|
|
}
|
|
|
|
if (isVisible())
|
|
hide();
|
|
else
|
|
bringToFront();
|
|
});
|
|
|
|
QMenu *trayIconMenu = new QMenu(this);
|
|
QAction *quitAction = trayIconMenu->addAction(tr("&Quit"));
|
|
connect(quitAction, &QAction::triggered, qApp, &QCoreApplication::quit);
|
|
|
|
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
|
|
const QString desktop = getenv("XDG_CURRENT_DESKTOP");
|
|
const bool isUnity = (desktop.toLower() == QLatin1String("unity"));
|
|
|
|
if (isUnity) {
|
|
g_clear_object(&m_appIndicator);
|
|
g_clear_object(&m_appIndicatorMenu);
|
|
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(const Zeal::SearchQuery &query)
|
|
{
|
|
show();
|
|
setWindowState((windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);
|
|
raise();
|
|
activateWindow();
|
|
ui->lineEdit->setFocus();
|
|
|
|
if (!query.isEmpty()) {
|
|
ui->lineEdit->setText(query.toString());
|
|
ui->treeView->setFocus();
|
|
ui->treeView->activated(ui->treeView->currentIndex());
|
|
}
|
|
}
|
|
|
|
void MainWindow::closeEvent(QCloseEvent *event)
|
|
{
|
|
m_settings->windowGeometry = saveGeometry();
|
|
if (m_settings->showSystrayIcon && m_settings->hideOnClose) {
|
|
event->ignore();
|
|
hide();
|
|
}
|
|
}
|
|
|
|
void MainWindow::setupShortcuts()
|
|
{
|
|
QShortcut *focusSearch = new QShortcut(QKeySequence(QStringLiteral("Ctrl+K")), this);
|
|
focusSearch->setContext(Qt::ApplicationShortcut);
|
|
connect(focusSearch, &QShortcut::activated, [=]() {
|
|
ui->lineEdit->setFocus();
|
|
});
|
|
}
|
|
|
|
// 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();
|
|
}
|