diff --git a/resources/docs/Documentation.md b/resources/docs/Documentation.md
index 3278e6708..e51769298 100644
--- a/resources/docs/Documentation.md
+++ b/resources/docs/Documentation.md
@@ -378,7 +378,7 @@ Typical post-processing filter might do things like advanced CSS formatting, loc
It's completely up to you if you decide to only use script as `Source` of the script or separate your custom functionality between `Source` script and `Post-process` script. Sometimes you might need different `Source` scripts for different online sources and the same `Post-process` script and vice versa.
### Notifications
-RSS Guard allows you to configure behavior of desktop notifications. There is a number of events which can be configured:
+RSS Guard allows you to customize desktop notifications. There are a number of events which can be configured:
* New (unread) articles fetched.
* Fetching of articles is started.
* Login OAuth tokens are refreshed.
diff --git a/src/librssguard/gui/webviewers/qtextbrowser/textbrowserviewer.cpp b/src/librssguard/gui/webviewers/qtextbrowser/textbrowserviewer.cpp
index 1472379bf..b65af558f 100644
--- a/src/librssguard/gui/webviewers/qtextbrowser/textbrowserviewer.cpp
+++ b/src/librssguard/gui/webviewers/qtextbrowser/textbrowserviewer.cpp
@@ -2,6 +2,7 @@
#include "gui/webviewers/qtextbrowser/textbrowserviewer.h"
+#include "gui/dialogs/formmain.h"
#include "gui/messagebox.h"
#include "gui/webbrowser.h"
#include "miscellaneous/application.h"
@@ -15,9 +16,10 @@
#include
#include
#include
+#include
TextBrowserViewer::TextBrowserViewer(QWidget* parent)
- : QTextBrowser(parent), m_downloader(new Downloader(this)), m_reloadingWithResources(false) {
+ : QTextBrowser(parent), m_downloader(new Downloader(this)), m_document(new TextBrowserDocument(this)) {
setAutoFillBackground(true);
setFrameShape(QFrame::Shape::NoFrame);
setFrameShadow(QFrame::Shadow::Plain);
@@ -25,26 +27,12 @@ TextBrowserViewer::TextBrowserViewer(QWidget* parent)
setOpenLinks(false);
viewport()->setAutoFillBackground(true);
+ setDocument(m_document.data());
+
connect(this, &QTextBrowser::anchorClicked, this, &TextBrowserViewer::onAnchorClicked);
connect(this, QOverload::of(&QTextBrowser::highlighted), this, &TextBrowserViewer::linkMouseHighlighted);
}
-QVariant TextBrowserViewer::loadResource(int type, const QUrl& name) {
- if (!m_reloadingWithResources) {
- if (type == QTextDocument::ResourceType::ImageResource) {
- m_resourcesForHtml.append(name);
- }
-
- return {};
- }
- else if (m_loadedResources.contains(name)) {
- return QImage::fromData(m_loadedResources.value(name));
- }
- else {
- return {};
- }
-}
-
QSize TextBrowserViewer::sizeHint() const {
auto doc_size = document()->size().toSize();
@@ -222,21 +210,6 @@ void TextBrowserViewer::setUrl(const QUrl& url) {
emit loadingFinished(!is_error);
}
-void TextBrowserViewer::setHtml(const QString& html, const QUrl& base_url) {
- m_currentUrl = base_url;
-
- if (!m_reloadingWithResources) {
- m_resourcesForHtml.clear();
- }
-
- QTextBrowser::setHtml(html);
-
- setZoomFactor(m_zoomFactor);
-
- emit pageTitleChanged(documentTitle());
- emit pageUrlChanged(base_url);
-}
-
QString TextBrowserViewer::html() const {
return QTextBrowser::toHtml();
}
@@ -300,14 +273,29 @@ void TextBrowserViewer::contextMenuEvent(QContextMenuEvent* event) {
m_actionReloadWithImages.reset(new QAction(qApp->icons()->fromTheme(QSL("viewimage"), QSL("view-refresh")),
tr("Reload with images"),
this));
+ m_actionOpenExternalBrowser.reset(new QAction(qApp->icons()->fromTheme(QSL("document-open")),
+ tr("Open in external browser"),
+ this));
+ m_actionDownloadLink.reset(new QAction(qApp->icons()->fromTheme(QSL("download")), tr("Download"), this));
connect(m_actionReloadWithImages.data(), &QAction::triggered, this, &TextBrowserViewer::reloadWithImages);
+ connect(m_actionOpenExternalBrowser.data(),
+ &QAction::triggered,
+ this,
+ &TextBrowserViewer::openLinkInExternalBrowser);
+ connect(m_actionDownloadLink.data(), &QAction::triggered, this, &TextBrowserViewer::downloadLink);
}
menu->addAction(m_actionReloadWithImages.data());
+ menu->addAction(m_actionOpenExternalBrowser.data());
+ menu->addAction(m_actionDownloadLink.data());
auto anchor = anchorAt(event->pos());
+ m_lastContextMenuPos = event->pos();
+ m_actionOpenExternalBrowser.data()->setEnabled(!anchor.isEmpty());
+ m_actionDownloadLink.data()->setEnabled(!anchor.isEmpty());
+
if (!anchor.isEmpty()) {
QFileIconProvider icon_provider;
QMenu* menu_ext_tools = new QMenu(tr("Open with external tool"), menu);
@@ -358,11 +346,11 @@ void TextBrowserViewer::wheelEvent(QWheelEvent* event) {
}
void TextBrowserViewer::reloadWithImages() {
- m_reloadingWithResources = true;
- m_loadedResources.clear();
+ m_document.data()->m_reloadingWithResources = true;
+ m_document.data()->m_loadedResources.clear();
- for (const QUrl& url : m_resourcesForHtml) {
- if (m_loadedResources.contains(url)) {
+ for (const QUrl& url : m_document.data()->m_resourcesForHtml) {
+ if (m_document.data()->m_loadedResources.contains(url)) {
continue;
}
@@ -374,13 +362,39 @@ void TextBrowserViewer::reloadWithImages() {
loop.exec();
if (m_downloader->lastOutputError() == QNetworkReply::NetworkError::NoError) {
- m_loadedResources.insert(url, m_downloader->lastOutputData());
+ m_document.data()->m_loadedResources.insert(url, m_downloader->lastOutputData());
}
}
- setHtml(html(), m_currentUrl);
+ auto scrolled = verticalScrollBar()->value();
- m_reloadingWithResources = false;
+ setHtmlPrivate(html(), m_currentUrl);
+
+ verticalScrollBar()->setValue(scrolled);
+}
+
+void TextBrowserViewer::openLinkInExternalBrowser() {
+ auto link = anchorAt(m_lastContextMenuPos);
+
+ if (!link.isEmpty()) {
+ qApp->web()->openUrlInExternalBrowser(link);
+
+ if (qApp->settings()
+ ->value(GROUP(Messages), SETTING(Messages::BringAppToFrontAfterMessageOpenedExternally))
+ .toBool()) {
+ QTimer::singleShot(1000, qApp, []() {
+ qApp->mainForm()->display();
+ });
+ }
+ }
+}
+
+void TextBrowserViewer::downloadLink() {
+ auto link = anchorAt(m_lastContextMenuPos);
+
+ if (!link.isEmpty()) {
+ qApp->downloadManager()->download(link);
+ }
}
void TextBrowserViewer::onAnchorClicked(const QUrl& url) {
@@ -392,38 +406,7 @@ void TextBrowserViewer::onAnchorClicked(const QUrl& url) {
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 = false;
-
- 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();
+ setUrl(url);
}
}
else {
@@ -433,3 +416,44 @@ void TextBrowserViewer::onAnchorClicked(const QUrl& url) {
tr("Selected hyperlink is invalid."));
}
}
+
+void TextBrowserViewer::setHtml(const QString& html, const QUrl& base_url) {
+ m_document.data()->m_reloadingWithResources = false;
+ m_document.data()->m_loadedResources.clear();
+ m_document.data()->m_resourcesForHtml.clear();
+
+ setHtmlPrivate(html, base_url);
+}
+
+void TextBrowserViewer::setHtmlPrivate(const QString& html, const QUrl& base_url) {
+ m_currentUrl = base_url;
+
+ if (!m_document.data()->m_reloadingWithResources) {
+ m_document.data()->m_resourcesForHtml.clear();
+ }
+
+ QTextBrowser::setHtml(html);
+
+ setZoomFactor(m_zoomFactor);
+
+ emit pageTitleChanged(documentTitle());
+ emit pageUrlChanged(base_url);
+}
+
+TextBrowserDocument::TextBrowserDocument(QObject* parent) : QTextDocument(parent), m_reloadingWithResources(false) {}
+
+QVariant TextBrowserDocument::loadResource(int type, const QUrl& name) {
+ if (!m_reloadingWithResources) {
+ if (type == QTextDocument::ResourceType::ImageResource && !m_resourcesForHtml.contains(name)) {
+ m_resourcesForHtml.append(name);
+ }
+
+ return QTextDocument::loadResource(type, name);
+ }
+ else if (m_loadedResources.contains(name)) {
+ return QImage::fromData(m_loadedResources.value(name));
+ }
+ else {
+ return QTextDocument::loadResource(type, name);
+ }
+}
diff --git a/src/librssguard/gui/webviewers/qtextbrowser/textbrowserviewer.h b/src/librssguard/gui/webviewers/qtextbrowser/textbrowserviewer.h
index aa4191437..4db693f05 100644
--- a/src/librssguard/gui/webviewers/qtextbrowser/textbrowserviewer.h
+++ b/src/librssguard/gui/webviewers/qtextbrowser/textbrowserviewer.h
@@ -16,6 +16,23 @@ class QResizeEvent;
class WebBrowser;
class Downloader;
+class TextBrowserDocument : public QTextDocument {
+ Q_OBJECT
+
+ friend class TextBrowserViewer;
+
+ public:
+ explicit TextBrowserDocument(QObject* parent = nullptr);
+
+ protected:
+ virtual QVariant loadResource(int type, const QUrl& name);
+
+ private:
+ bool m_reloadingWithResources;
+ QList m_resourcesForHtml;
+ QMap m_loadedResources;
+};
+
class TextBrowserViewer : public QTextBrowser, public WebViewer {
Q_OBJECT
Q_INTERFACES(WebViewer)
@@ -23,7 +40,6 @@ class TextBrowserViewer : public QTextBrowser, public WebViewer {
public:
explicit TextBrowserViewer(QWidget* parent = nullptr);
- virtual QVariant loadResource(int type, const QUrl& name);
virtual QSize sizeHint() const;
public:
@@ -48,6 +64,8 @@ class TextBrowserViewer : public QTextBrowser, public WebViewer {
private slots:
void reloadWithImages();
+ void openLinkInExternalBrowser();
+ void downloadLink();
void onAnchorClicked(const QUrl& url);
signals:
@@ -62,6 +80,8 @@ class TextBrowserViewer : public QTextBrowser, public WebViewer {
void closeWindowRequested();
private:
+ void setHtmlPrivate(const QString& html, const QUrl& base_url);
+
BlockingResult blockedWithAdblock(const QUrl& url);
QScopedPointer m_downloader;
QPair prepareHtmlForMessage(const QList& messages, RootItem* selected_item) const;
@@ -72,9 +92,10 @@ class TextBrowserViewer : public QTextBrowser, public WebViewer {
QFont m_baseFont;
qreal m_zoomFactor = 1.0;
QScopedPointer m_actionReloadWithImages;
- bool m_reloadingWithResources;
- QList m_resourcesForHtml;
- QMap m_loadedResources;
+ QScopedPointer m_actionOpenExternalBrowser;
+ QScopedPointer m_actionDownloadLink;
+ QScopedPointer m_document;
+ QPoint m_lastContextMenuPos;
};
#endif // TEXTBROWSERVIEWER_H