merge qtextbrowser, covid here, have to take pause

This commit is contained in:
Martin Rotter 2022-04-10 11:24:57 +02:00
parent 52798f9ed2
commit 983f1eef10
6 changed files with 577 additions and 120 deletions

View File

@ -568,9 +568,13 @@ if(USE_WEBENGINE)
endif() endif()
list(APPEND SOURCES list(APPEND SOURCES
# Lite non-WebEngine message browser. # Litehtml.
gui/webviewers/litehtml/litehtmlviewer.h gui/webviewers/litehtml/litehtmlviewer.h
gui/webviewers/litehtml/litehtmlviewer.cpp gui/webviewers/litehtml/litehtmlviewer.cpp
# QTextBrowser.
gui/webviewers/qtextbrowser/textbrowserviewer.h
gui/webviewers/qtextbrowser/textbrowserviewer.cpp
) )
# Deal with .ui files. # Deal with .ui files.

View File

@ -26,10 +26,11 @@ class DiscoverFeedsButton;
class SearchTextWidget; class SearchTextWidget;
class WebBrowser : public TabContent { class WebBrowser : public TabContent {
Q_OBJECT Q_OBJECT
friend class WebEngineViewer; friend class WebEngineViewer;
friend class LiteHtmlViewer; friend class LiteHtmlViewer;
friend class TextBrowserViewer;
public: public:
explicit WebBrowser(WebViewer* viewer = nullptr, QWidget* parent = nullptr); explicit WebBrowser(WebViewer* viewer = nullptr, QWidget* parent = nullptr);

View File

@ -161,7 +161,8 @@ QPair<QString, QUrl> LiteHtmlViewer::prepareHtmlForMessage(const QList<Message>&
else {*/ else {*/
QString cnts = message.m_contents; QString cnts = message.m_contents;
html += cnts.replace(imgTagRegex, QString()); html += cnts;
// html += cnts.replace(imgTagRegex, QString());
//} //}

View File

@ -0,0 +1,275 @@
// For license of this file, see <project-root-folder>/LICENSE.md.
#include "gui/webviewers/qtextbrowser/textbrowserviewer.h"
#include "gui/messagebox.h"
#include "gui/webbrowser.h"
#include "miscellaneous/application.h"
#include "miscellaneous/externaltool.h"
#include "miscellaneous/iconfactory.h"
#include "network-web/webfactory.h"
#include <QContextMenuEvent>
#include <QFileIconProvider>
#include <QScrollBar>
TextBrowserViewer::TextBrowserViewer(QWidget* parent) : QTextBrowser(parent) {
setAutoFillBackground(true);
setFrameShape(QFrame::Shape::StyledPanel);
setFrameShadow(QFrame::Shadow::Plain);
setTabChangesFocus(true);
setOpenLinks(false);
viewport()->setAutoFillBackground(true);
connect(this, &QTextBrowser::anchorClicked, this, &TextBrowserViewer::onAnchorClicked);
connect(this, QOverload<const QUrl&>::of(&QTextBrowser::highlighted), this, &TextBrowserViewer::linkMouseHighlighted);
}
QVariant TextBrowserViewer::loadResource(int type, const QUrl& name) {
return {};
}
QSize TextBrowserViewer::sizeHint() const {
auto doc_size = document()->size().toSize();
doc_size.setHeight(doc_size.height() + contentsMargins().top() + contentsMargins().bottom());
return doc_size;
}
QPair<QString, QUrl> TextBrowserViewer::prepareHtmlForMessage(const QList<Message>& messages,
RootItem* selected_item) const {
QString html;
for (const Message& message : messages) {
html += QString("<h2 align=\"center\">%1</h2>").arg(message.m_title);
if (!message.m_url.isEmpty()) {
html += QString("[url] <a href=\"%1\">%1</a><br/>").arg(message.m_url);
}
for (const Enclosure& enc : message.m_enclosures) {
html += QString("[%2] <a href=\"%1\">%1</a><br/>").arg(enc.m_url, enc.m_mimeType);
}
QRegularExpression imgTagRegex("\\<img[^\\>]*src\\s*=\\s*[\"\']([^\"\']*)[\"\'][^\\>]*\\>",
QRegularExpression::PatternOption::CaseInsensitiveOption |
QRegularExpression::PatternOption::InvertedGreedinessOption);
QRegularExpressionMatchIterator i = imgTagRegex.globalMatch(message.m_contents);
QString pictures_html;
while (i.hasNext()) {
QRegularExpressionMatch match = i.next();
pictures_html += QString("<br/>[%1] <a href=\"%2\">%2</a>").arg(tr("image"), match.captured(1));
}
/*if (qApp->settings()->value(GROUP(Messages), SETTING(Messages::DisplayImagePlaceholders)).toBool()) {
html += message.m_contents;
}
else {*/
QString cnts = message.m_contents;
html += cnts;
// html += cnts.replace(imgTagRegex, QString());
//}
html += pictures_html;
}
// TODO: If FgInteresting not defined by the skin
// use current pallette/Highlight color perhaps.
return {
QSL("<html>"
"<head><style>"
"a { color: %2; }"
"</style></head>"
"<body>%1</body>"
"</html>")
.arg(html,
qApp->skins()->currentSkin().colorForModel(SkinEnums::PaletteColors::FgInteresting).value<QColor>().name()),
QUrl()};
}
void TextBrowserViewer::bindToBrowser(WebBrowser* browser) {
installEventFilter(browser);
browser->m_actionBack = new QAction(this);
browser->m_actionForward = new QAction(this);
browser->m_actionReload = new QAction(this);
browser->m_actionStop = new QAction(this);
browser->m_actionBack->setEnabled(false);
browser->m_actionForward->setEnabled(false);
browser->m_actionReload->setEnabled(false);
// TODO: When clicked "Stop", save the "Stop" state and return {} from "handleResource(...)"
// right away.
browser->m_actionStop->setEnabled(false);
// TODO: add "Open in new tab" to context menu.
}
void TextBrowserViewer::findText(const QString& text, bool backwards) {
if (!text.isEmpty()) {
QTextBrowser::find(text, backwards ? QTextDocument::FindFlag::FindBackward : QTextDocument::FindFlag(0));
}
else {
textCursor().clearSelection();
moveCursor(QTextCursor::MoveOperation::Left);
}
}
void TextBrowserViewer::setUrl(const QUrl& url) {}
void TextBrowserViewer::setHtml(const QString& html, const QUrl& base_url) {
m_currentUrl = base_url;
QTextBrowser::setHtml(html);
emit pageTitleChanged(documentTitle());
emit pageUrlChanged(base_url);
}
QString TextBrowserViewer::html() const {
return QTextBrowser::toHtml();
}
QUrl TextBrowserViewer::url() const {
return m_currentUrl;
}
void TextBrowserViewer::clear() {
setHtml({});
}
void TextBrowserViewer::loadMessages(const QList<Message>& messages, RootItem* root) {
m_root = root;
auto html_messages = prepareHtmlForMessage(messages, root);
setHtml(html_messages.first, html_messages.second);
emit loadingFinished(true);
}
double TextBrowserViewer::verticalScrollBarPosition() const {
return verticalScrollBar()->value();
}
void TextBrowserViewer::setVerticalScrollBarPosition(double pos) {
verticalScrollBar()->setValue(int(pos));
}
void TextBrowserViewer::applyFont(const QFont& fon) {
setFont(fon);
}
qreal TextBrowserViewer::zoomFactor() const {
return font().pointSize() / 12.0;
}
void TextBrowserViewer::setZoomFactor(qreal zoom_factor) {
auto fon = font();
fon.setPointSize(fon.pointSize() * zoom_factor);
}
void TextBrowserViewer::contextMenuEvent(QContextMenuEvent* event) {
event->accept();
auto* menu = createStandardContextMenu();
if (menu == nullptr) {
return;
}
auto anchor = anchorAt(event->pos());
if (!anchor.isEmpty()) {
QFileIconProvider icon_provider;
QMenu* menu_ext_tools = new QMenu(tr("Open with external tool"), menu);
auto tools = ExternalTool::toolsFromSettings();
menu_ext_tools->setIcon(qApp->icons()->fromTheme(QSL("document-open")));
for (const ExternalTool& tool : qAsConst(tools)) {
QAction* act_tool = new QAction(QFileInfo(tool.executable()).fileName(), menu_ext_tools);
act_tool->setIcon(icon_provider.icon(QFileInfo(tool.executable())));
act_tool->setToolTip(tool.executable());
act_tool->setData(QVariant::fromValue(tool));
menu_ext_tools->addAction(act_tool);
connect(act_tool, &QAction::triggered, this, [act_tool, anchor]() {
act_tool->data().value<ExternalTool>().run(anchor);
});
}
if (menu_ext_tools->actions().isEmpty()) {
QAction* act_not_tools = new QAction("No external tools activated");
act_not_tools->setEnabled(false);
menu_ext_tools->addAction(act_not_tools);
}
menu->addMenu(menu_ext_tools);
}
menu->popup(event->globalPos());
}
void TextBrowserViewer::resizeEvent(QResizeEvent* event) {
// Notify parents about changed geometry.
updateGeometry();
QTextBrowser::resizeEvent(event);
}
void TextBrowserViewer::onAnchorClicked(const QUrl& url) {
if (!url.isEmpty()) {
bool open_externally_now =
qApp->settings()->value(GROUP(Browser), SETTING(Browser::OpenLinksInExternalBrowserRightAway)).toBool();
if (open_externally_now) {
qApp->web()->openUrlInExternalBrowser(url.toString());
}
else {
// User clicked some URL. Open it in external browser or download?
MsgBox box(qApp->mainFormWidget());
box.setText(tr("You clicked some link. You can download the link contents or open it in external web browser."));
box.setInformativeText(tr("What action do you want to take?"));
box.setDetailedText(url.toString());
QAbstractButton* btn_open = box.addButton(tr("Open in external browser"), QMessageBox::ButtonRole::ActionRole);
QAbstractButton* btn_download = box.addButton(tr("Download"), QMessageBox::ButtonRole::ActionRole);
QAbstractButton* btn_cancel = box.addButton(QMessageBox::StandardButton::Cancel);
bool always;
MsgBox::setCheckBox(&box, tr("Always open links in external browser."), &always);
box.setDefaultButton(QMessageBox::StandardButton::Cancel);
box.exec();
if (box.clickedButton() != box.button(QMessageBox::StandardButton::Cancel)) {
// Store selected checkbox value.
qApp->settings()->setValue(GROUP(Browser), Browser::OpenLinksInExternalBrowserRightAway, always);
}
if (box.clickedButton() == btn_open) {
qApp->web()->openUrlInExternalBrowser(url.toString());
}
else if (box.clickedButton() == btn_download) {
qApp->downloadManager()->download(url);
}
btn_download->deleteLater();
btn_open->deleteLater();
btn_cancel->deleteLater();
}
}
else {
MsgBox::show(qApp->mainFormWidget(),
QMessageBox::Warning,
tr("Incorrect link"),
tr("Selected hyperlink is invalid."));
}
}

View File

@ -0,0 +1,67 @@
// For license of this file, see <project-root-folder>/LICENSE.md.
#ifndef TEXTBROWSERVIEWER_H
#define TEXTBROWSERVIEWER_H
#include "gui/webviewers/webviewer.h"
#include <QTextBrowser>
#include <QPointer>
class QContextMenuEvent;
class QResizeEvent;
class WebBrowser;
class TextBrowserViewer : public QTextBrowser, public WebViewer {
Q_OBJECT
Q_INTERFACES(WebViewer)
public:
explicit TextBrowserViewer(QWidget* parent = nullptr);
virtual QVariant loadResource(int type, const QUrl& name);
virtual QSize sizeHint() const;
public:
virtual void bindToBrowser(WebBrowser* browser);
virtual void findText(const QString& text, bool backwards);
virtual void setUrl(const QUrl& url);
virtual void setHtml(const QString& html, const QUrl& base_url = {});
virtual QString html() const;
virtual QUrl url() const;
virtual void clear();
virtual void loadMessages(const QList<Message>& messages, RootItem* root);
virtual double verticalScrollBarPosition() const;
virtual void setVerticalScrollBarPosition(double pos);
virtual void applyFont(const QFont& fon);
virtual qreal zoomFactor() const;
virtual void setZoomFactor(qreal zoom_factor);
protected:
virtual void contextMenuEvent(QContextMenuEvent* event);
virtual void resizeEvent(QResizeEvent* event);
private slots:
void onAnchorClicked(const QUrl& url);
signals:
void pageTitleChanged(const QString& new_title);
void pageUrlChanged(const QUrl& url);
void pageIconChanged(const QIcon&);
void linkMouseHighlighted(const QUrl& url);
void loadingStarted();
void loadingProgress(int progress);
void loadingFinished(bool success);
void newWindowRequested(WebViewer* viewer);
void closeWindowRequested();
private:
QPair<QString, QUrl> prepareHtmlForMessage(const QList<Message>& messages, RootItem* selected_item) const;
private:
QUrl m_currentUrl;
QPointer<RootItem> m_root;
};
#endif // TEXTBROWSERVIEWER_H

View File

@ -11,7 +11,8 @@
#include "gui/feedsview.h" #include "gui/feedsview.h"
#include "gui/messagebox.h" #include "gui/messagebox.h"
#include "gui/toolbars/statusbar.h" #include "gui/toolbars/statusbar.h"
#include "gui/webviewers/litehtml/litehtmlviewer.h" // QLiteHtml-based web browsing. #include "gui/webviewers/litehtml/litehtmlviewer.h" // QLiteHtml-based web browsing.
#include "gui/webviewers/qtextbrowser/textbrowserviewer.h" // QTextBrowser-based web browsing.
#include "miscellaneous/feedreader.h" #include "miscellaneous/feedreader.h"
#include "miscellaneous/iconfactory.h" #include "miscellaneous/iconfactory.h"
#include "miscellaneous/iofactory.h" #include "miscellaneous/iofactory.h"
@ -62,7 +63,7 @@
#endif #endif
#endif #endif
Application::Application(const QString &id, int &argc, char **argv, const QStringList &raw_cli_args) Application::Application(const QString& id, int& argc, char** argv, const QStringList& raw_cli_args)
: SingleApplication(id, argc, argv), m_updateFeedsLock(new Mutex()) { : SingleApplication(id, argc, argv), m_updateFeedsLock(new Mutex()) {
parseCmdArgumentsFromMyInstance(raw_cli_args); parseCmdArgumentsFromMyInstance(raw_cli_args);
qInstallMessageHandler(performLogging); qInstallMessageHandler(performLogging);
@ -87,15 +88,19 @@ Application::Application(const QString &id, int &argc, char **argv, const QStrin
m_windowsTaskBar = nullptr; m_windowsTaskBar = nullptr;
const GUID qIID_ITaskbarList4 = {0xc43dc798, 0x95d1, 0x4bea, {0x90, 0x30, 0xbb, 0x99, 0xe2, 0x98, 0x3a, 0x1a}}; const GUID qIID_ITaskbarList4 = {0xc43dc798, 0x95d1, 0x4bea, {0x90, 0x30, 0xbb, 0x99, 0xe2, 0x98, 0x3a, 0x1a}};
HRESULT task_result = CoCreateInstance(CLSID_TaskbarList, nullptr, CLSCTX_INPROC_SERVER, qIID_ITaskbarList4, HRESULT task_result = CoCreateInstance(CLSID_TaskbarList,
reinterpret_cast<void **>(&m_windowsTaskBar)); nullptr,
CLSCTX_INPROC_SERVER,
qIID_ITaskbarList4,
reinterpret_cast<void**>(&m_windowsTaskBar));
if (FAILED(task_result)) { if (FAILED(task_result)) {
qCriticalNN << LOGSEC_CORE << "Taskbar integration for Windows failed to initialize with HRESULT:" qCriticalNN << LOGSEC_CORE << "Taskbar integration for Windows failed to initialize with HRESULT:"
<< QUOTE_W_SPACE_DOT(task_result); << QUOTE_W_SPACE_DOT(task_result);
m_windowsTaskBar = nullptr; m_windowsTaskBar = nullptr;
} else if (FAILED(m_windowsTaskBar->HrInit())) { }
else if (FAILED(m_windowsTaskBar->HrInit())) {
qCriticalNN << LOGSEC_CORE << "Taskbar integration for Windows failed to initialize with inner HRESULT:" qCriticalNN << LOGSEC_CORE << "Taskbar integration for Windows failed to initialize with inner HRESULT:"
<< QUOTE_W_SPACE_DOT(m_windowsTaskBar->HrInit()); << QUOTE_W_SPACE_DOT(m_windowsTaskBar->HrInit());
@ -156,7 +161,9 @@ Application::Application(const QString &id, int &argc, char **argv, const QStrin
qDebugNN << LOGSEC_NETWORK << "Persistent web data storage path:" qDebugNN << LOGSEC_NETWORK << "Persistent web data storage path:"
<< QUOTE_W_SPACE_DOT(QWebEngineProfile::defaultProfile()->persistentStoragePath()); << QUOTE_W_SPACE_DOT(QWebEngineProfile::defaultProfile()->persistentStoragePath());
connect(QWebEngineProfile::defaultProfile(), &QWebEngineProfile::downloadRequested, this, connect(QWebEngineProfile::defaultProfile(),
&QWebEngineProfile::downloadRequested,
this,
&Application::downloadRequested); &Application::downloadRequested);
#endif #endif
@ -164,9 +171,10 @@ Application::Application(const QString &id, int &argc, char **argv, const QStrin
QTimer::singleShot(3000, this, [=]() { QTimer::singleShot(3000, this, [=]() {
try { try {
m_webFactory->adBlock()->setEnabled( m_webFactory->adBlock()
qApp->settings()->value(GROUP(AdBlock), SETTING(AdBlock::AdBlockEnabled)).toBool()); ->setEnabled(qApp->settings()->value(GROUP(AdBlock), SETTING(AdBlock::AdBlockEnabled)).toBool());
} catch (...) { }
catch (...) {
onAdBlockFailure(); onAdBlockFailure();
} }
}); });
@ -175,14 +183,16 @@ Application::Application(const QString &id, int &argc, char **argv, const QStrin
if (isFirstRun()) { if (isFirstRun()) {
m_notifications->save({Notification(Notification::Event::GeneralEvent, true), m_notifications->save({Notification(Notification::Event::GeneralEvent, true),
Notification(Notification::Event::NewUnreadArticlesFetched, true, Notification(Notification::Event::NewUnreadArticlesFetched,
true,
QSL("%1/notify.wav").arg(SOUNDS_BUILTIN_DIRECTORY)), QSL("%1/notify.wav").arg(SOUNDS_BUILTIN_DIRECTORY)),
Notification(Notification::Event::NewAppVersionAvailable, true), Notification(Notification::Event::NewAppVersionAvailable, true),
Notification(Notification::Event::LoginFailure, true), Notification(Notification::Event::LoginFailure, true),
Notification(Notification::Event::NodePackageUpdated, true), Notification(Notification::Event::NodePackageUpdated, true),
Notification(Notification::Event::NodePackageFailedToUpdate, true)}, Notification(Notification::Event::NodePackageFailedToUpdate, true)},
settings()); settings());
} else { }
else {
m_notifications->load(settings()); m_notifications->load(settings());
} }
@ -206,7 +216,7 @@ Application::~Application() {
QString s_customLogFile = QString(); QString s_customLogFile = QString();
bool s_disableDebug = false; bool s_disableDebug = false;
void Application::performLogging(QtMsgType type, const QMessageLogContext &context, const QString &msg) { void Application::performLogging(QtMsgType type, const QMessageLogContext& context, const QString& msg) {
#ifndef QT_NO_DEBUG_OUTPUT #ifndef QT_NO_DEBUG_OUTPUT
QString console_message = qFormatLogMessage(type, context, msg); QString console_message = qFormatLogMessage(type, context, msg);
@ -244,13 +254,16 @@ void Application::hideOrShowMainForm() {
SystemTrayIcon::isSystemTrayDesired() && SystemTrayIcon::isSystemTrayAreaAvailable()) { SystemTrayIcon::isSystemTrayDesired() && SystemTrayIcon::isSystemTrayAreaAvailable()) {
qDebugNN << LOGSEC_CORE << "Hiding the main window when the application is starting."; qDebugNN << LOGSEC_CORE << "Hiding the main window when the application is starting.";
mainForm()->switchVisibility(true); mainForm()->switchVisibility(true);
} else { }
else {
qDebugNN << LOGSEC_CORE << "Showing the main window when the application is starting."; qDebugNN << LOGSEC_CORE << "Showing the main window when the application is starting.";
mainForm()->show(); mainForm()->show();
} }
} }
void Application::loadDynamicShortcuts() { DynamicShortcuts::load(userActions()); } void Application::loadDynamicShortcuts() {
DynamicShortcuts::load(userActions());
}
void Application::showPolls() const { void Application::showPolls() const {
/* /*
@ -271,7 +284,10 @@ void Application::offerChanges() const {
"version by clicking this popup notification.") "version by clicking this popup notification.")
.arg(QSL(APP_LONG_NAME)), .arg(QSL(APP_LONG_NAME)),
QSystemTrayIcon::MessageIcon::NoIcon}, QSystemTrayIcon::MessageIcon::NoIcon},
{}, {tr("Go to changelog"), [] { FormAbout(qApp->mainForm()).exec(); }}); {},
{tr("Go to changelog"), [] {
FormAbout(qApp->mainForm()).exec();
}});
} }
} }
@ -284,15 +300,21 @@ bool Application::isAlreadyRunning() {
QStringList Application::builtinSounds() const { QStringList Application::builtinSounds() const {
auto builtin_sounds = QDir(QSL(SOUNDS_BUILTIN_DIRECTORY)).entryInfoList(QDir::Filter::Files, QDir::SortFlag::Name); auto builtin_sounds = QDir(QSL(SOUNDS_BUILTIN_DIRECTORY)).entryInfoList(QDir::Filter::Files, QDir::SortFlag::Name);
auto iter = boolinq::from(builtin_sounds).select([](const QFileInfo &i) { return i.absoluteFilePath(); }).toStdList(); auto iter = boolinq::from(builtin_sounds)
.select([](const QFileInfo& i) {
return i.absoluteFilePath();
})
.toStdList();
auto descs = FROM_STD_LIST(QStringList, iter); auto descs = FROM_STD_LIST(QStringList, iter);
return descs; return descs;
} }
FeedReader *Application::feedReader() { return m_feedReader; } FeedReader* Application::feedReader() {
return m_feedReader;
}
QList<QAction *> Application::userActions() { QList<QAction*> Application::userActions() {
if (m_mainForm != nullptr && m_userActions.isEmpty()) { if (m_mainForm != nullptr && m_userActions.isEmpty()) {
m_userActions = m_mainForm->allActions(); m_userActions = m_mainForm->allActions();
m_userActions.append(m_webFactory->adBlock()->adBlockIcon()); m_userActions.append(m_webFactory->adBlock()->adBlockIcon());
@ -301,21 +323,37 @@ QList<QAction *> Application::userActions() {
return m_userActions; return m_userActions;
} }
bool Application::isFirstRun() const { return m_firstRunEver; } bool Application::isFirstRun() const {
return m_firstRunEver;
}
bool Application::isFirstRunCurrentVersion() const { return m_firstRunCurrentVersion; } bool Application::isFirstRunCurrentVersion() const {
return m_firstRunCurrentVersion;
}
QCommandLineParser *Application::cmdParser() { return &m_cmdParser; } QCommandLineParser* Application::cmdParser() {
return &m_cmdParser;
}
WebFactory *Application::web() const { return m_webFactory; } WebFactory* Application::web() const {
return m_webFactory;
}
SystemFactory *Application::system() { return m_system; } SystemFactory* Application::system() {
return m_system;
}
SkinFactory *Application::skins() { return m_skins; } SkinFactory* Application::skins() {
return m_skins;
}
Localization *Application::localization() { return m_localization; } Localization* Application::localization() {
return m_localization;
}
DatabaseFactory *Application::database() { return m_database; } DatabaseFactory* Application::database() {
return m_database;
}
void Application::eliminateFirstRuns() { void Application::eliminateFirstRuns() {
settings()->setValue(GROUP(General), General::FirstRun, false); settings()->setValue(GROUP(General), General::FirstRun, false);
@ -323,15 +361,21 @@ void Application::eliminateFirstRuns() {
} }
#if defined(USE_WEBENGINE) #if defined(USE_WEBENGINE)
bool Application::forcedNoWebEngine() const { return m_forcedNoWebEngine; } bool Application::forcedNoWebEngine() const {
return m_forcedNoWebEngine;
}
#endif #endif
NodeJs *Application::nodejs() const { return m_nodejs; } NodeJs* Application::nodejs() const {
return m_nodejs;
}
NotificationFactory *Application::notifications() const { return m_notifications; } NotificationFactory* Application::notifications() const {
return m_notifications;
}
void Application::setFeedReader(FeedReader *feed_reader) { void Application::setFeedReader(FeedReader* feed_reader) {
m_feedReader = feed_reader; m_feedReader = feed_reader;
connect(m_feedReader, &FeedReader::feedUpdatesStarted, this, &Application::onFeedUpdatesStarted); connect(m_feedReader, &FeedReader::feedUpdatesStarted, this, &Application::onFeedUpdatesStarted);
@ -340,29 +384,45 @@ void Application::setFeedReader(FeedReader *feed_reader) {
connect(m_feedReader->feedsModel(), &FeedsModel::messageCountsChanged, this, &Application::showMessagesNumber); connect(m_feedReader->feedsModel(), &FeedsModel::messageCountsChanged, this, &Application::showMessagesNumber);
} }
IconFactory *Application::icons() { return m_icons; } IconFactory* Application::icons() {
return m_icons;
}
DownloadManager *Application::downloadManager() { DownloadManager* Application::downloadManager() {
if (m_downloadManager == nullptr) { if (m_downloadManager == nullptr) {
m_downloadManager = new DownloadManager(); m_downloadManager = new DownloadManager();
connect(m_downloadManager, &DownloadManager::downloadFinished, mainForm()->statusBar(), connect(m_downloadManager,
&DownloadManager::downloadFinished,
mainForm()->statusBar(),
&StatusBar::clearProgressDownload); &StatusBar::clearProgressDownload);
connect(m_downloadManager, &DownloadManager::downloadProgressed, mainForm()->statusBar(), connect(m_downloadManager,
&DownloadManager::downloadProgressed,
mainForm()->statusBar(),
&StatusBar::showProgressDownload); &StatusBar::showProgressDownload);
} }
return m_downloadManager; return m_downloadManager;
} }
Settings *Application::settings() const { return m_settings; } Settings* Application::settings() const {
return m_settings;
}
Mutex *Application::feedUpdateLock() { return m_updateFeedsLock.data(); } Mutex* Application::feedUpdateLock() {
return m_updateFeedsLock.data();
}
FormMain *Application::mainForm() { return m_mainForm; } FormMain* Application::mainForm() {
return m_mainForm;
}
QWidget *Application::mainFormWidget() { return m_mainForm; } QWidget* Application::mainFormWidget() {
return m_mainForm;
}
void Application::setMainForm(FormMain *main_form) { m_mainForm = main_form; } void Application::setMainForm(FormMain* main_form) {
m_mainForm = main_form;
}
QString Application::configFolder() const { QString Application::configFolder() const {
return IOFactory::getSystemFolder(QStandardPaths::StandardLocation::GenericConfigLocation); return IOFactory::getSystemFolder(QStandardPaths::StandardLocation::GenericConfigLocation);
@ -377,9 +437,11 @@ QString Application::userDataAppFolder() const {
QString Application::userDataFolder() { QString Application::userDataFolder() {
if (settings()->type() == SettingsProperties::SettingsType::Custom) { if (settings()->type() == SettingsProperties::SettingsType::Custom) {
return customDataFolder(); return customDataFolder();
} else if (settings()->type() == SettingsProperties::SettingsType::Portable) { }
else if (settings()->type() == SettingsProperties::SettingsType::Portable) {
return userDataAppFolder(); return userDataAppFolder();
} else { }
else {
return userDataHomeFolder(); return userDataHomeFolder();
} }
} }
@ -421,8 +483,10 @@ QString Application::homeFolder() const {
#endif #endif
} }
void Application::backupDatabaseSettings(bool backup_database, bool backup_settings, const QString &target_path, void Application::backupDatabaseSettings(bool backup_database,
const QString &backup_name) { bool backup_settings,
const QString& target_path,
const QString& backup_name) {
if (!QFileInfo(target_path).isWritable()) { if (!QFileInfo(target_path).isWritable()) {
throw ApplicationException(tr("Output directory is not writable.")); throw ApplicationException(tr("Output directory is not writable."));
} }
@ -443,29 +507,31 @@ void Application::backupDatabaseSettings(bool backup_database, bool backup_setti
} }
} }
void Application::restoreDatabaseSettings(bool restore_database, bool restore_settings, void Application::restoreDatabaseSettings(bool restore_database,
const QString &source_database_file_path, bool restore_settings,
const QString &source_settings_file_path) { const QString& source_database_file_path,
const QString& source_settings_file_path) {
if (restore_database) { if (restore_database) {
if (!qApp->database()->driver()->initiateRestoration(source_database_file_path)) { if (!qApp->database()->driver()->initiateRestoration(source_database_file_path)) {
throw ApplicationException( throw ApplicationException(tr("Database restoration was not initiated. Make sure that output directory is "
tr("Database restoration was not initiated. Make sure that output directory is writable.")); "writable."));
} }
} }
if (restore_settings) { if (restore_settings) {
if (!qApp->settings()->initiateRestoration(source_settings_file_path)) { if (!qApp->settings()->initiateRestoration(source_settings_file_path)) {
throw ApplicationException( throw ApplicationException(tr("Settings restoration was not initiated. Make sure that output directory is "
tr("Settings restoration was not initiated. Make sure that output directory is writable.")); "writable."));
} }
} }
} }
SystemTrayIcon *Application::trayIcon() { SystemTrayIcon* Application::trayIcon() {
if (m_trayIcon == nullptr) { if (m_trayIcon == nullptr) {
if (qApp->settings()->value(GROUP(GUI), SETTING(GUI::MonochromeTrayIcon)).toBool()) { if (qApp->settings()->value(GROUP(GUI), SETTING(GUI::MonochromeTrayIcon)).toBool()) {
m_trayIcon = new SystemTrayIcon(APP_ICON_MONO_PATH, APP_ICON_MONO_PLAIN_PATH, m_mainForm); m_trayIcon = new SystemTrayIcon(APP_ICON_MONO_PATH, APP_ICON_MONO_PLAIN_PATH, m_mainForm);
} else { }
else {
m_trayIcon = new SystemTrayIcon(APP_ICON_PATH, APP_ICON_PLAIN_PATH, m_mainForm); m_trayIcon = new SystemTrayIcon(APP_ICON_PATH, APP_ICON_PLAIN_PATH, m_mainForm);
} }
@ -480,7 +546,8 @@ QIcon Application::desktopAwareIcon() const {
if (!from_theme.isNull()) { if (!from_theme.isNull()) {
return from_theme; return from_theme;
} else { }
else {
return QIcon(APP_ICON_PATH); return QIcon(APP_ICON_PATH);
} }
} }
@ -494,7 +561,8 @@ void Application::showTrayIcon() {
if (SystemTrayIcon::isSystemTrayAreaAvailable()) { if (SystemTrayIcon::isSystemTrayAreaAvailable()) {
qDebugNN << LOGSEC_GUI << "Tray icon is available, showing now."; qDebugNN << LOGSEC_GUI << "Tray icon is available, showing now.";
trayIcon()->show(); trayIcon()->show();
} else { }
else {
m_feedReader->feedsModel()->notifyWithCounts(); m_feedReader->feedsModel()->notifyWithCounts();
} }
#else #else
@ -504,12 +572,14 @@ void Application::showTrayIcon() {
if (SystemTrayIcon::isSystemTrayAreaAvailable()) { if (SystemTrayIcon::isSystemTrayAreaAvailable()) {
qWarningNN << LOGSEC_GUI << "Tray icon is available, showing now."; qWarningNN << LOGSEC_GUI << "Tray icon is available, showing now.";
trayIcon()->show(); trayIcon()->show();
} else { }
else {
m_feedReader->feedsModel()->notifyWithCounts(); m_feedReader->feedsModel()->notifyWithCounts();
} }
}); });
#endif #endif
} else { }
else {
m_feedReader->feedsModel()->notifyWithCounts(); m_feedReader->feedsModel()->notifyWithCounts();
} }
} }
@ -526,8 +596,11 @@ void Application::deleteTrayIcon() {
} }
} }
void Application::showGuiMessage(Notification::Event event, const GuiMessage &msg, const GuiMessageDestination &dest, void Application::showGuiMessage(Notification::Event event,
const GuiAction &action, QWidget *parent) { const GuiMessage& msg,
const GuiMessageDestination& dest,
const GuiAction& action,
QWidget* parent) {
if (SystemTrayIcon::areNotificationsEnabled()) { if (SystemTrayIcon::areNotificationsEnabled()) {
auto notification = m_notifications->notificationForEvent(event); auto notification = m_notifications->notificationForEvent(event);
@ -538,36 +611,51 @@ void Application::showGuiMessage(Notification::Event event, const GuiMessage &ms
notification.balloonEnabled() && dest.m_tray) { notification.balloonEnabled() && dest.m_tray) {
trayIcon()->showMessage(msg.m_title.simplified().isEmpty() ? Notification::nameForEvent(notification.event()) trayIcon()->showMessage(msg.m_title.simplified().isEmpty() ? Notification::nameForEvent(notification.event())
: msg.m_title, : msg.m_title,
msg.m_message, msg.m_type, TRAY_ICON_BUBBLE_TIMEOUT, std::move(action.m_action)); msg.m_message,
msg.m_type,
TRAY_ICON_BUBBLE_TIMEOUT,
std::move(action.m_action));
return; return;
} }
} }
if (dest.m_messageBox || msg.m_type == QSystemTrayIcon::MessageIcon::Critical) { if (dest.m_messageBox || msg.m_type == QSystemTrayIcon::MessageIcon::Critical) {
// Tray icon or OSD is not available, display simple text box. // Tray icon or OSD is not available, display simple text box.
MsgBox::show(parent == nullptr ? mainFormWidget() : parent, QMessageBox::Icon(msg.m_type), msg.m_title, MsgBox::show(parent == nullptr ? mainFormWidget() : parent,
msg.m_message, {}, {}, QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok, {}, QMessageBox::Icon(msg.m_type),
action.m_title, action.m_action); msg.m_title,
} else if (dest.m_statusBar && mainForm()->statusBar() != nullptr && mainForm()->statusBar()->isVisible()) { msg.m_message,
{},
{},
QMessageBox::StandardButton::Ok,
QMessageBox::StandardButton::Ok,
{},
action.m_title,
action.m_action);
}
else if (dest.m_statusBar && mainForm()->statusBar() != nullptr && mainForm()->statusBar()->isVisible()) {
mainForm()->statusBar()->showMessage(msg.m_message); mainForm()->statusBar()->showMessage(msg.m_message);
} else { }
else {
qDebugNN << LOGSEC_CORE << "Silencing GUI message:" << QUOTE_W_SPACE_DOT(msg.m_message); qDebugNN << LOGSEC_CORE << "Silencing GUI message:" << QUOTE_W_SPACE_DOT(msg.m_message);
} }
} }
WebViewer *Application::createWebView() { WebViewer* Application::createWebView() {
#if !defined(USE_WEBENGINE) #if !defined(USE_WEBENGINE)
return new LiteHtmlViewer(); return new LiteHtmlViewer();
#else #else
if (forcedNoWebEngine()) { if (forcedNoWebEngine()) {
return new LiteHtmlViewer(); return new TextBrowserViewer();
} else { // return new LiteHtmlViewer();
}
else {
return new WebEngineViewer(); return new WebEngineViewer();
} }
#endif #endif
} }
void Application::onCommitData(QSessionManager &manager) { void Application::onCommitData(QSessionManager& manager) {
qDebugNN << LOGSEC_CORE << "OS asked application to commit its data."; qDebugNN << LOGSEC_CORE << "OS asked application to commit its data.";
onAboutToQuit(); onAboutToQuit();
@ -576,7 +664,7 @@ void Application::onCommitData(QSessionManager &manager) {
manager.release(); manager.release();
} }
void Application::onSaveState(QSessionManager &manager) { void Application::onSaveState(QSessionManager& manager) {
qDebugNN << LOGSEC_CORE << "OS asked application to save its state."; qDebugNN << LOGSEC_CORE << "OS asked application to save its state.";
manager.setRestartHint(QSessionManager::RestartHint::RestartNever); manager.setRestartHint(QSessionManager::RestartHint::RestartNever);
@ -603,7 +691,8 @@ void Application::onAboutToQuit() {
// We locked the lock to exit peacefully, unlock it to avoid warnings. // We locked the lock to exit peacefully, unlock it to avoid warnings.
feedUpdateLock()->unlock(); feedUpdateLock()->unlock();
} else { }
else {
// Request for write lock timed-out. This means // Request for write lock timed-out. This means
// that some critical action can be processed right now. // that some critical action can be processed right now.
qWarningNN << LOGSEC_CORE << "Close lock timed-out."; qWarningNN << LOGSEC_CORE << "Close lock timed-out.";
@ -623,7 +712,8 @@ void Application::onAboutToQuit() {
if (QProcess::startDetached(QDir::toNativeSeparators(applicationFilePath()), arguments().mid(1))) { if (QProcess::startDetached(QDir::toNativeSeparators(applicationFilePath()), arguments().mid(1))) {
qDebugNN << LOGSEC_CORE << "New application instance was started."; qDebugNN << LOGSEC_CORE << "New application instance was started.";
} else { }
else {
qCriticalNN << LOGSEC_CORE << "New application instance was not started successfully."; qCriticalNN << LOGSEC_CORE << "New application instance was not started successfully.";
} }
} }
@ -663,9 +753,10 @@ void Application::showMessagesNumber(int unread_messages, bool any_feed_has_new_
HICON overlay_hicon = overlay_icon.toHICON(); HICON overlay_hicon = overlay_icon.toHICON();
#endif #endif
HRESULT overlay_result = m_windowsTaskBar->SetOverlayIcon( HRESULT overlay_result =
reinterpret_cast<HWND>(m_mainForm->winId()), m_windowsTaskBar->SetOverlayIcon(reinterpret_cast<HWND>(m_mainForm->winId()),
(task_bar_count_enabled && unread_messages > 0) ? overlay_hicon : nullptr, nullptr); (task_bar_count_enabled && unread_messages > 0) ? overlay_hicon : nullptr,
nullptr);
DestroyIcon(overlay_hicon); DestroyIcon(overlay_hicon);
@ -690,9 +781,11 @@ QImage Application::generateOverlayIcon(int number) const {
if (number < 1000) { if (number < 1000) {
num_txt = QString::number(number); num_txt = QString::number(number);
} else if (number < 100000) { }
else if (number < 100000) {
num_txt = QSL("%1k").arg(int(number / 1000)); num_txt = QSL("%1k").arg(int(number / 1000));
} else { }
else {
num_txt = QChar(8734); num_txt = QChar(8734);
} }
@ -702,9 +795,11 @@ QImage Application::generateOverlayIcon(int number) const {
if (num_txt.size() == 3) { if (num_txt.size() == 3) {
fon.setPixelSize(img.width() * 0.52); fon.setPixelSize(img.width() * 0.52);
} else if (num_txt.size() == 2) { }
else if (num_txt.size() == 2) {
fon.setPixelSize(img.width() * 0.68); fon.setPixelSize(img.width() * 0.68);
} else { }
else {
fon.setPixelSize(img.width() * 0.79); fon.setPixelSize(img.width() * 0.79);
} }
@ -721,7 +816,8 @@ QImage Application::generateOverlayIcon(int number) const {
p.setPen(Qt::GlobalColor::black); p.setPen(Qt::GlobalColor::black);
p.drawPath(rounded_rectangle); p.drawPath(rounded_rectangle);
p.drawText(img.rect().marginsRemoved(QMargins(0, 0, 0, img.height() * 0.05)), num_txt, p.drawText(img.rect().marginsRemoved(QMargins(0, 0, 0, img.height() * 0.05)),
num_txt,
QTextOption(Qt::AlignmentFlag::AlignCenter)); QTextOption(Qt::AlignmentFlag::AlignCenter));
p.end(); p.end();
@ -738,9 +834,9 @@ void Application::restart() {
#if defined(USE_WEBENGINE) #if defined(USE_WEBENGINE)
#if QT_VERSION_MAJOR == 6 #if QT_VERSION_MAJOR == 6
void Application::downloadRequested(QWebEngineDownloadRequest *download_item) { void Application::downloadRequested(QWebEngineDownloadRequest* download_item) {
#else #else
void Application::downloadRequested(QWebEngineDownloadItem *download_item) { void Application::downloadRequested(QWebEngineDownloadItem* download_item) {
#endif #endif
downloadManager()->download(download_item->url()); downloadManager()->download(download_item->url());
download_item->cancel(); download_item->cancel();
@ -760,7 +856,7 @@ void Application::onFeedUpdatesStarted() {
#endif #endif
} }
void Application::onFeedUpdatesProgress(const Feed *feed, int current, int total) { void Application::onFeedUpdatesProgress(const Feed* feed, int current, int total) {
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
// Use SetOverlayIcon Windows API method on Windows. // Use SetOverlayIcon Windows API method on Windows.
bool task_bar_count_enabled = settings()->value(GROUP(GUI), SETTING(GUI::UnreadNumbersOnTaskBar)).toBool(); bool task_bar_count_enabled = settings()->value(GROUP(GUI), SETTING(GUI::UnreadNumbersOnTaskBar)).toBool();
@ -771,7 +867,7 @@ void Application::onFeedUpdatesProgress(const Feed *feed, int current, int total
#endif #endif
} }
void Application::onFeedUpdatesFinished(const FeedDownloadResults &results) { void Application::onFeedUpdatesFinished(const FeedDownloadResults& results) {
if (!results.updatedFeeds().isEmpty()) { if (!results.updatedFeeds().isEmpty()) {
// Now, inform about results via GUI message/notification. // Now, inform about results via GUI message/notification.
qApp->showGuiMessage(Notification::Event::NewUnreadArticlesFetched, qApp->showGuiMessage(Notification::Event::NewUnreadArticlesFetched,
@ -788,7 +884,7 @@ void Application::onFeedUpdatesFinished(const FeedDownloadResults &results) {
#endif #endif
} }
void Application::setupCustomDataFolder(const QString &data_folder) { void Application::setupCustomDataFolder(const QString& data_folder) {
if (!QDir().mkpath(data_folder)) { if (!QDir().mkpath(data_folder)) {
qCriticalNN << LOGSEC_CORE << "Failed to create custom data path" << QUOTE_W_SPACE(data_folder) qCriticalNN << LOGSEC_CORE << "Failed to create custom data path" << QUOTE_W_SPACE(data_folder)
<< "thus falling back to standard setup."; << "thus falling back to standard setup.";
@ -822,7 +918,7 @@ void Application::determineFirstRuns() {
eliminateFirstRuns(); eliminateFirstRuns();
} }
void Application::parseCmdArgumentsFromOtherInstance(const QString &message) { void Application::parseCmdArgumentsFromOtherInstance(const QString& message) {
if (message.isEmpty()) { if (message.isEmpty()) {
qDebugNN << LOGSEC_CORE << "No execution message received from other app instances."; qDebugNN << LOGSEC_CORE << "No execution message received from other app instances.";
return; return;
@ -842,9 +938,10 @@ void Application::parseCmdArgumentsFromOtherInstance(const QString &message) {
cmd_parser.addOption(QCommandLineOption({QSL(CLI_QUIT_INSTANCE)})); cmd_parser.addOption(QCommandLineOption({QSL(CLI_QUIT_INSTANCE)}));
cmd_parser.addOption(QCommandLineOption({QSL(CLI_IS_RUNNING)})); cmd_parser.addOption(QCommandLineOption({QSL(CLI_IS_RUNNING)}));
cmd_parser.addPositionalArgument( cmd_parser
QSL("urls"), QSL("List of URL addresses pointing to individual online feeds which should be added."), .addPositionalArgument(QSL("urls"),
QSL("[url-1 ... url-n]")); QSL("List of URL addresses pointing to individual online feeds which should be added."),
QSL("[url-1 ... url-n]"));
if (!cmd_parser.parse(messages)) { if (!cmd_parser.parse(messages)) {
qCriticalNN << LOGSEC_CORE << cmd_parser.errorText(); qCriticalNN << LOGSEC_CORE << cmd_parser.errorText();
@ -853,23 +950,27 @@ void Application::parseCmdArgumentsFromOtherInstance(const QString &message) {
if (cmd_parser.isSet(QSL(CLI_QUIT_INSTANCE))) { if (cmd_parser.isSet(QSL(CLI_QUIT_INSTANCE))) {
quit(); quit();
return; return;
} else if (cmd_parser.isSet(QSL(CLI_IS_RUNNING))) { }
showGuiMessage(Notification::Event::GeneralEvent, {tr("Already running"), tr("Application is already running."), else if (cmd_parser.isSet(QSL(CLI_IS_RUNNING))) {
QSystemTrayIcon::MessageIcon::Information}); showGuiMessage(Notification::Event::GeneralEvent,
{tr("Already running"),
tr("Application is already running."),
QSystemTrayIcon::MessageIcon::Information});
mainForm()->display(); mainForm()->display();
} }
messages = cmd_parser.positionalArguments(); messages = cmd_parser.positionalArguments();
for (const QString &msg : qAsConst(messages)) { for (const QString& msg : qAsConst(messages)) {
// Application was running, and someone wants to add new feed. // Application was running, and someone wants to add new feed.
ServiceRoot *rt = boolinq::from(feedReader()->feedsModel()->serviceRoots()).firstOrDefault([](ServiceRoot *root) { ServiceRoot* rt = boolinq::from(feedReader()->feedsModel()->serviceRoots()).firstOrDefault([](ServiceRoot* root) {
return root->supportsFeedAdding(); return root->supportsFeedAdding();
}); });
if (rt != nullptr) { if (rt != nullptr) {
rt->addNewFeed(nullptr, msg); rt->addNewFeed(nullptr, msg);
} else { }
else {
showGuiMessage(Notification::Event::GeneralEvent, showGuiMessage(Notification::Event::GeneralEvent,
{tr("Cannot add feed"), {tr("Cannot add feed"),
tr("Feed cannot be added because there is no active account which can add feeds."), tr("Feed cannot be added because there is no active account which can add feeds."),
@ -878,15 +979,17 @@ void Application::parseCmdArgumentsFromOtherInstance(const QString &message) {
} }
} }
void Application::parseCmdArgumentsFromMyInstance(const QStringList &raw_cli_args) { void Application::parseCmdArgumentsFromMyInstance(const QStringList& raw_cli_args) {
QCommandLineOption help({QSL(CLI_HELP_SHORT), QSL(CLI_HELP_LONG)}, QSL("Displays overview of CLI.")); QCommandLineOption help({QSL(CLI_HELP_SHORT), QSL(CLI_HELP_LONG)}, QSL("Displays overview of CLI."));
QCommandLineOption version({QSL(CLI_VER_SHORT), QSL(CLI_VER_LONG)}, QSL("Displays version of the application.")); QCommandLineOption version({QSL(CLI_VER_SHORT), QSL(CLI_VER_LONG)}, QSL("Displays version of the application."));
QCommandLineOption log_file( QCommandLineOption
{QSL(CLI_LOG_SHORT), QSL(CLI_LOG_LONG)}, log_file({QSL(CLI_LOG_SHORT), QSL(CLI_LOG_LONG)},
QSL("Write application debug log to file. Note that logging to file may slow application down."), QSL("log-file")); QSL("Write application debug log to file. Note that logging to file may slow application down."),
QCommandLineOption custom_data_folder( QSL("log-file"));
{QSL(CLI_DAT_SHORT), QSL(CLI_DAT_LONG)}, QCommandLineOption
QSL("Use custom folder for user data and disable single instance application mode."), QSL("user-data-folder")); custom_data_folder({QSL(CLI_DAT_SHORT), QSL(CLI_DAT_LONG)},
QSL("Use custom folder for user data and disable single instance application mode."),
QSL("user-data-folder"));
QCommandLineOption disable_singleinstance({QSL(CLI_SIN_SHORT), QSL(CLI_SIN_LONG)}, QCommandLineOption disable_singleinstance({QSL(CLI_SIN_SHORT), QSL(CLI_SIN_LONG)},
QSL("Allow running of multiple application instances.")); QSL("Allow running of multiple application instances."));
@ -899,7 +1002,8 @@ void Application::parseCmdArgumentsFromMyInstance(const QStringList &raw_cli_arg
QSL("Disable just \"debug\" output.")); QSL("Disable just \"debug\" output."));
QCommandLineOption disable_debug({QSL(CLI_NSTDOUTERR_SHORT), QSL(CLI_NSTDOUTERR_LONG)}, QCommandLineOption disable_debug({QSL(CLI_NSTDOUTERR_SHORT), QSL(CLI_NSTDOUTERR_LONG)},
QSL("Completely disable stdout/stderr outputs.")); QSL("Completely disable stdout/stderr outputs."));
QCommandLineOption forced_style({QSL(CLI_STYLE_SHORT), QSL(CLI_STYLE_LONG)}, QSL("Force some application style."), QCommandLineOption forced_style({QSL(CLI_STYLE_SHORT), QSL(CLI_STYLE_LONG)},
QSL("Force some application style."),
QSL("style-name")); QSL("style-name"));
m_cmdParser.addOptions({ m_cmdParser.addOptions({
@ -909,9 +1013,10 @@ void Application::parseCmdArgumentsFromMyInstance(const QStringList &raw_cli_arg
#endif #endif
forced_style forced_style
}); });
m_cmdParser.addPositionalArgument( m_cmdParser
QSL("urls"), QSL("List of URL addresses pointing to individual online feeds which should be added."), .addPositionalArgument(QSL("urls"),
QSL("[url-1 ... url-n]")); QSL("List of URL addresses pointing to individual online feeds which should be added."),
QSL("[url-1 ... url-n]"));
m_cmdParser.setApplicationDescription(QSL(APP_NAME)); m_cmdParser.setApplicationDescription(QSL(APP_NAME));
m_cmdParser.setSingleDashWordOptionMode(QCommandLineParser::SingleDashWordOptionMode::ParseAsLongOptions); m_cmdParser.setSingleDashWordOptionMode(QCommandLineParser::SingleDashWordOptionMode::ParseAsLongOptions);
@ -940,13 +1045,15 @@ void Application::parseCmdArgumentsFromMyInstance(const QStringList &raw_cli_arg
<< QUOTE_W_SPACE_DOT(data_folder); << QUOTE_W_SPACE_DOT(data_folder);
setupCustomDataFolder(data_folder); setupCustomDataFolder(data_folder);
} else { }
else {
m_allowMultipleInstances = false; m_allowMultipleInstances = false;
} }
if (m_cmdParser.isSet(QSL(CLI_HELP_SHORT))) { if (m_cmdParser.isSet(QSL(CLI_HELP_SHORT))) {
m_cmdParser.showHelp(); m_cmdParser.showHelp();
} else if (m_cmdParser.isSet(QSL(CLI_VER_SHORT))) { }
else if (m_cmdParser.isSet(QSL(CLI_VER_SHORT))) {
m_cmdParser.showVersion(); m_cmdParser.showVersion();
} }
@ -969,15 +1076,15 @@ void Application::parseCmdArgumentsFromMyInstance(const QStringList &raw_cli_arg
} }
} }
void Application::onNodeJsPackageUpdateError(const QList<NodeJs::PackageMetadata> &pkgs, const QString &error) { void Application::onNodeJsPackageUpdateError(const QList<NodeJs::PackageMetadata>& pkgs, const QString& error) {
qApp->showGuiMessage( qApp->showGuiMessage(Notification::Event::NodePackageFailedToUpdate,
Notification::Event::NodePackageFailedToUpdate, {{},
{{}, tr("Packages %1 were NOT updated because of error: %2.")
tr("Packages %1 were NOT updated because of error: %2.").arg(NodeJs::packagesToString(pkgs), error), .arg(NodeJs::packagesToString(pkgs), error),
QSystemTrayIcon::MessageIcon::Critical}); QSystemTrayIcon::MessageIcon::Critical});
} }
void Application::onNodeJsPackageInstalled(const QList<NodeJs::PackageMetadata> &pkgs, bool already_up_to_date) { void Application::onNodeJsPackageInstalled(const QList<NodeJs::PackageMetadata>& pkgs, bool already_up_to_date) {
if (!already_up_to_date) { if (!already_up_to_date) {
qApp->showGuiMessage(Notification::Event::NodePackageUpdated, qApp->showGuiMessage(Notification::Event::NodePackageUpdated,
{{}, {{},
@ -986,4 +1093,6 @@ void Application::onNodeJsPackageInstalled(const QList<NodeJs::PackageMetadata>
} }
} }
QString Application::customDataFolder() const { return m_customDataFolder; } QString Application::customDataFolder() const {
return m_customDataFolder;
}