diff --git a/resources/icons.qrc b/resources/icons.qrc
index 30fae3bd5..db31a46ea 100644
--- a/resources/icons.qrc
+++ b/resources/icons.qrc
@@ -71,6 +71,7 @@
./graphics/Breeze/mimetypes/64/text-html.svg
./graphics/Breeze/places/96/user-trash.svg
./graphics/Breeze/actions/22/view-fullscreen.svg
+ ./graphics/Breeze/actions/22/viewimage.svg
./graphics/Breeze/actions/32/view-list-details.svg
./graphics/Breeze/actions/32/view-refresh.svg
./graphics/Breeze/actions/22/view-restore.svg
@@ -147,6 +148,7 @@
./graphics/Breeze Dark/mimetypes/64/text-html.svg
./graphics/Breeze Dark/places/96/user-trash.svg
./graphics/Breeze Dark/actions/22/view-fullscreen.svg
+ ./graphics/Breeze Dark/actions/22/viewimage.svg
./graphics/Breeze Dark/actions/32/view-list-details.svg
./graphics/Breeze Dark/actions/32/view-refresh.svg
./graphics/Breeze Dark/actions/22/view-restore.svg
@@ -303,6 +305,7 @@
./graphics/Numix/22/actions/up.svg
./graphics/Numix/22/places/user-trash.svg
./graphics/Numix/22/actions/view-fullscreen.svg
+ ./graphics/Numix/22/actions/viewimage.svg
./graphics/Numix/22/actions/view-list-details.svg
./graphics/Numix/22/actions/view-refresh.svg
./graphics/Numix/22/actions/view-restore.svg
diff --git a/src/librssguard/gui/messagepreviewer.cpp b/src/librssguard/gui/messagepreviewer.cpp
index 45af3eb4b..d922c602d 100644
--- a/src/librssguard/gui/messagepreviewer.cpp
+++ b/src/librssguard/gui/messagepreviewer.cpp
@@ -68,6 +68,18 @@ MessagePreviewer::MessagePreviewer(QWidget* parent)
clear();
}
+MessagePreviewer::~MessagePreviewer() {
+ if (m_viewerLayout->count() > 1) {
+ // Make sure that previewer does not delete any custom article
+ // viewers as those are responsibility to free by their accounts.
+ auto* wdg = m_viewerLayout->widget(1);
+
+ wdg->setParent(nullptr);
+
+ m_viewerLayout->removeWidget(wdg);
+ }
+}
+
void MessagePreviewer::reloadFontSettings() {
m_msgBrowser->reloadFontSettings();
}
diff --git a/src/librssguard/gui/messagepreviewer.h b/src/librssguard/gui/messagepreviewer.h
index 391698ba1..edbaeb9e6 100644
--- a/src/librssguard/gui/messagepreviewer.h
+++ b/src/librssguard/gui/messagepreviewer.h
@@ -34,6 +34,7 @@ class MessagePreviewer : public QWidget {
public:
explicit MessagePreviewer(QWidget* parent = nullptr);
+ virtual ~MessagePreviewer();
void reloadFontSettings();
diff --git a/src/librssguard/gui/webviewers/litehtml/litehtmlviewer.cpp b/src/librssguard/gui/webviewers/litehtml/litehtmlviewer.cpp
index da6c20995..e63dc5ef0 100644
--- a/src/librssguard/gui/webviewers/litehtml/litehtmlviewer.cpp
+++ b/src/librssguard/gui/webviewers/litehtml/litehtmlviewer.cpp
@@ -23,10 +23,11 @@
#include
#include
-LiteHtmlViewer::LiteHtmlViewer(QWidget* parent) : QLiteHtmlWidget(parent), m_downloader(new Downloader(this)) {
+LiteHtmlViewer::LiteHtmlViewer(QWidget* parent) : QLiteHtmlWidget(parent), m_downloader(new Downloader(this)),
+ m_reloadingWithImages(false) {
setResourceHandler([this](const QUrl& url) {
emit loadProgress(-1);
- return handleResource(url);
+ return m_reloadingWithImages ? handleResource(url) : QByteArray{};
});
setFrameShape(QFrame::Shape::NoFrame);
@@ -131,68 +132,9 @@ void LiteHtmlViewer::clear() {
}
void LiteHtmlViewer::loadMessages(const QList& messages, RootItem* root) {
- Skin skin = qApp->skins()->currentSkin();
- QString messages_layout;
- QString single_message_layout = skin.m_layoutMarkup;
+ auto html_messages = qApp->skins()->generateHtmlOfArticles(messages, root);
- for (const Message& message : messages) {
- QString enclosures;
- QString enclosure_images;
-
- for (const Enclosure& enclosure : message.m_enclosures) {
- QString enc_url = QUrl::fromPercentEncoding(enclosure.m_url.toUtf8());
-
- enclosures += skin.m_enclosureMarkup.arg(enc_url,
- QSL("🧷"),
- enclosure.m_mimeType);
-
- if (enclosure.m_mimeType.startsWith(QSL("image/")) &&
- qApp->settings()->value(GROUP(Messages), SETTING(Messages::DisplayEnclosuresInMessage)).toBool()) {
- // Add thumbnail image.
- enclosure_images += skin.m_enclosureImageMarkup.arg(
- enclosure.m_url,
- enclosure.m_mimeType,
- qApp->settings()->value(GROUP(Messages), SETTING(Messages::MessageHeadImageHeight)).toString());
- }
- }
-
- QString msg_date = qApp->settings()->value(GROUP(Messages), SETTING(Messages::UseCustomDate)).toBool()
- ? message.m_created.toLocalTime().toString(qApp->settings()->value(GROUP(Messages),
- SETTING(Messages::CustomDateFormat)).toString())
- : qApp->localization()->loadedLocale().toString(message.m_created.toLocalTime(),
- QLocale::FormatType::ShortFormat);
-
- messages_layout.append(single_message_layout
- .arg(message.m_title,
- tr("Written by ") + (message.m_author.isEmpty() ?
- tr("unknown author") :
- message.m_author),
- message.m_url,
- message.m_contents,
- msg_date,
- enclosures,
- enclosure_images,
- QString::number(message.m_id)));
- }
-
- QString msg_contents = skin.m_layoutMarkupWrapper.arg(messages.size() == 1
- ? messages.at(0).m_title
- : tr("Newspaper view"),
- messages_layout);
- auto* feed = root->getParentServiceRoot()->getItemFromSubTree([messages](const RootItem* it) {
- return it->kind() == RootItem::Kind::Feed && it->customId() == messages.at(0).m_feedId;
- })->toFeed();
- QString base_url;
-
- if (feed != nullptr) {
- QUrl url(NetworkFactory::sanitizeUrl(feed->source()));
-
- if (url.isValid()) {
- base_url = url.scheme() + QSL("://") + url.host();
- }
- }
-
- setHtml(msg_contents, QUrl::fromUserInput(base_url));
+ setHtml(html_messages.first, html_messages.second);
emit loadFinished(true);
}
@@ -261,26 +203,46 @@ void LiteHtmlViewer::onLinkClicked(const QUrl& link) {
}
}
+void LiteHtmlViewer::reloadPageWithImages() {
+ m_reloadingWithImages = true;
+
+ auto scroll = verticalScrollBar()->value();
+
+ setHtml(html(), url());
+
+ if (scroll > 0) {
+ verticalScrollBar()->setValue(scroll);
+ }
+
+ m_reloadingWithImages = false;
+}
+
void LiteHtmlViewer::showContextMenu(const QPoint& pos, const QUrl& url) {
if (m_contextMenu.isNull()) {
m_contextMenu.reset(new QMenu("Context menu for web browser", this));
- m_actionCopyUrl.reset(m_contextMenu->addAction(qApp->icons()->fromTheme(QSL("edit-copy")),
- tr("Copy URL"),
- [url]() {
- QGuiApplication::clipboard()->setText(url.toString(), QClipboard::Mode::Clipboard);
- }));
+ m_actionCopyUrl.reset(new QAction(qApp->icons()->fromTheme(QSL("edit-copy")),
+ tr("Copy URL"),
+ this));
- m_actionCopyText.reset(m_contextMenu->addAction(qApp->icons()->fromTheme(QSL("edit-copy")),
- tr("Copy selection"),
- [this]() {
+ connect(m_actionCopyUrl.data(), &QAction::triggered, this, [url]() {
+ QGuiApplication::clipboard()->setText(url.toString(), QClipboard::Mode::Clipboard);
+ });
+
+ m_actionCopyText.reset(new QAction(qApp->icons()->fromTheme(QSL("edit-copy")),
+ tr("Copy selection"),
+ this));
+
+ connect(m_actionCopyText.data(), &QAction::triggered, this, [this]() {
QGuiApplication::clipboard()->setText(QLiteHtmlWidget::selectedText(), QClipboard::Mode::Clipboard);
- }));
+ });
// Add option to open link in external viewe
- m_actionOpenLinkExternally.reset(m_contextMenu->addAction(qApp->icons()->fromTheme(QSL("document-open")),
- tr("Open link in external browser"),
- [url]() {
+ m_actionOpenLinkExternally.reset(new QAction(qApp->icons()->fromTheme(QSL("document-open")),
+ tr("Open link in external browser"),
+ this));
+
+ connect(m_actionOpenLinkExternally.data(), &QAction::triggered, this, [url]() {
qApp->web()->openUrlInExternalBrowser(url.toString());
if (qApp->settings()->value(GROUP(Messages), SETTING(Messages::BringAppToFrontAfterMessageOpenedExternally)).toBool()) {
@@ -288,19 +250,24 @@ void LiteHtmlViewer::showContextMenu(const QPoint& pos, const QUrl& url) {
qApp->mainForm()->display();
});
}
- }));
- }
- else {
- m_contextMenu->clear();
+ });
+
+ m_actionReloadWithImages.reset(new QAction(qApp->icons()->fromTheme(QSL("viewimage"), QSL("view-refresh")),
+ tr("Reload with images"),
+ this));
+
+ connect(m_actionReloadWithImages.data(), &QAction::triggered, this, &LiteHtmlViewer::reloadPageWithImages);
}
m_actionCopyUrl->setEnabled(url.isValid());
m_actionCopyText->setEnabled(!QLiteHtmlWidget::selectedText().isEmpty());
m_actionOpenLinkExternally->setEnabled(url.isValid());
+ m_contextMenu->clear();
m_contextMenu->addActions({ m_actionCopyUrl.data(),
m_actionCopyText.data(),
- m_actionOpenLinkExternally.data() });
+ m_actionOpenLinkExternally.data(),
+ m_actionReloadWithImages.data() });
if (url.isValid()) {
QFileIconProvider icon_provider;
diff --git a/src/librssguard/gui/webviewers/litehtml/litehtmlviewer.h b/src/librssguard/gui/webviewers/litehtml/litehtmlviewer.h
index faa8211e4..d1c76ea4d 100644
--- a/src/librssguard/gui/webviewers/litehtml/litehtmlviewer.h
+++ b/src/librssguard/gui/webviewers/litehtml/litehtmlviewer.h
@@ -40,6 +40,7 @@ class LiteHtmlViewer : public QLiteHtmlWidget, public WebViewer {
private slots:
void selectedTextChanged(bool available);
void onLinkClicked(const QUrl& link);
+ void reloadPageWithImages();
void showContextMenu(const QPoint& pos, const QUrl& url);
signals:
@@ -63,6 +64,8 @@ class LiteHtmlViewer : public QLiteHtmlWidget, public WebViewer {
QScopedPointer m_actionCopyUrl;
QScopedPointer m_actionCopyText;
QScopedPointer m_actionOpenLinkExternally;
+ QScopedPointer m_actionReloadWithImages;
+ bool m_reloadingWithImages;
};
#endif // LITEHTMLVIEWER_H
diff --git a/src/librssguard/gui/webviewers/webengine/webengineviewer.cpp b/src/librssguard/gui/webviewers/webengine/webengineviewer.cpp
index 8534259a5..a9a73f68c 100644
--- a/src/librssguard/gui/webviewers/webengine/webengineviewer.cpp
+++ b/src/librssguard/gui/webviewers/webengine/webengineviewer.cpp
@@ -53,75 +53,16 @@ WebEnginePage* WebEngineViewer::page() const {
}
void WebEngineViewer::loadMessages(const QList& messages, RootItem* root) {
- Skin skin = qApp->skins()->currentSkin();
- QString messages_layout;
- QString single_message_layout = skin.m_layoutMarkup;
-
- for (const Message& message : messages) {
- QString enclosures;
- QString enclosure_images;
-
- for (const Enclosure& enclosure : message.m_enclosures) {
- QString enc_url = QUrl::fromPercentEncoding(enclosure.m_url.toUtf8());
-
- enclosures += skin.m_enclosureMarkup.arg(enc_url,
- QSL("🧷"),
- enclosure.m_mimeType);
-
- if (enclosure.m_mimeType.startsWith(QSL("image/")) &&
- qApp->settings()->value(GROUP(Messages), SETTING(Messages::DisplayEnclosuresInMessage)).toBool()) {
- // Add thumbnail image.
- enclosure_images += skin.m_enclosureImageMarkup.arg(
- enclosure.m_url,
- enclosure.m_mimeType,
- qApp->settings()->value(GROUP(Messages), SETTING(Messages::MessageHeadImageHeight)).toString());
- }
- }
-
- QString msg_date = qApp->settings()->value(GROUP(Messages), SETTING(Messages::UseCustomDate)).toBool()
- ? message.m_created.toLocalTime().toString(qApp->settings()->value(GROUP(Messages),
- SETTING(Messages::CustomDateFormat)).toString())
- : qApp->localization()->loadedLocale().toString(message.m_created.toLocalTime(),
- QLocale::FormatType::ShortFormat);
-
- messages_layout.append(single_message_layout
- .arg(message.m_title,
- tr("Written by ") + (message.m_author.isEmpty() ?
- tr("unknown author") :
- message.m_author),
- message.m_url,
- message.m_contents,
- msg_date,
- enclosures,
- enclosure_images,
- QString::number(message.m_id)));
- }
-
- m_messageContents = skin.m_layoutMarkupWrapper.arg(messages.size() == 1
- ? messages.at(0).m_title
- : tr("Newspaper view"),
- messages_layout);
+ auto html_messages = qApp->skins()->generateHtmlOfArticles(messages, root);
m_root = root;
-
- auto* feed = root->getParentServiceRoot()->getItemFromSubTree([messages](const RootItem* it) {
- return it->kind() == RootItem::Kind::Feed && it->customId() == messages.at(0).m_feedId;
- })->toFeed();
-
- m_messageBaseUrl = QString();
-
- if (feed != nullptr) {
- QUrl url(NetworkFactory::sanitizeUrl(feed->source()));
-
- if (url.isValid()) {
- m_messageBaseUrl = url.scheme() + QSL("://") + url.host();
- }
- }
+ m_messageContents = html_messages.first;
+ m_messageBaseUrl = html_messages.second;
bool previously_enabled = isEnabled();
setEnabled(false);
- setHtml(m_messageContents, m_messageBaseUrl /*, QUrl::fromUserInput(INTERNAL_URL_MESSAGE)*/);
+ setHtml(m_messageContents, m_messageBaseUrl);
setEnabled(previously_enabled);
page()->runJavaScript(QSL("window.scrollTo(0, 0);"));
diff --git a/src/librssguard/gui/webviewers/webengine/webengineviewer.h b/src/librssguard/gui/webviewers/webengine/webengineviewer.h
index 0325804bb..28397fc77 100644
--- a/src/librssguard/gui/webviewers/webengine/webengineviewer.h
+++ b/src/librssguard/gui/webviewers/webengine/webengineviewer.h
@@ -58,7 +58,7 @@ class WebEngineViewer : public QWebEngineView, public WebViewer {
private:
WebBrowser* m_browser;
RootItem* m_root;
- QString m_messageBaseUrl;
+ QUrl m_messageBaseUrl;
QString m_messageContents;
};
diff --git a/src/librssguard/miscellaneous/skinfactory.cpp b/src/librssguard/miscellaneous/skinfactory.cpp
index 1f4d0e5ff..6565981f9 100644
--- a/src/librssguard/miscellaneous/skinfactory.cpp
+++ b/src/librssguard/miscellaneous/skinfactory.cpp
@@ -4,6 +4,8 @@
#include "exceptions/ioexception.h"
#include "miscellaneous/application.h"
+#include "network-web/networkfactory.h"
+#include "services/abstract/rootitem.h"
#include
#include
@@ -129,6 +131,71 @@ QString SkinFactory::adBlockedPage(const QString& url, const QString& filter) {
return currentSkin().m_layoutMarkupWrapper.arg(tr("This page was blocked by AdBlock"), adblocked);
}
+QPair SkinFactory::generateHtmlOfArticles(const QList& messages, RootItem* root) const {
+ Skin skin = currentSkin();
+ QString messages_layout;
+ QString single_message_layout = skin.m_layoutMarkup;
+
+ for (const Message& message : messages) {
+ QString enclosures;
+ QString enclosure_images;
+
+ for (const Enclosure& enclosure : message.m_enclosures) {
+ QString enc_url = QUrl::fromPercentEncoding(enclosure.m_url.toUtf8());
+
+ enclosures += skin.m_enclosureMarkup.arg(enc_url,
+ QSL("🧷"),
+ enclosure.m_mimeType);
+
+ if (enclosure.m_mimeType.startsWith(QSL("image/")) &&
+ qApp->settings()->value(GROUP(Messages), SETTING(Messages::DisplayEnclosuresInMessage)).toBool()) {
+ // Add thumbnail image.
+ enclosure_images += skin.m_enclosureImageMarkup.arg(
+ enclosure.m_url,
+ enclosure.m_mimeType,
+ qApp->settings()->value(GROUP(Messages), SETTING(Messages::MessageHeadImageHeight)).toString());
+ }
+ }
+
+ QString msg_date = qApp->settings()->value(GROUP(Messages), SETTING(Messages::UseCustomDate)).toBool()
+ ? message.m_created.toLocalTime().toString(qApp->settings()->value(GROUP(Messages),
+ SETTING(Messages::CustomDateFormat)).toString())
+ : qApp->localization()->loadedLocale().toString(message.m_created.toLocalTime(),
+ QLocale::FormatType::ShortFormat);
+
+ messages_layout.append(single_message_layout
+ .arg(message.m_title,
+ tr("Written by ") + (message.m_author.isEmpty() ?
+ tr("unknown author") :
+ message.m_author),
+ message.m_url,
+ message.m_contents,
+ msg_date,
+ enclosures,
+ enclosure_images,
+ QString::number(message.m_id)));
+ }
+
+ QString msg_contents = skin.m_layoutMarkupWrapper.arg(messages.size() == 1
+ ? messages.at(0).m_title
+ : tr("Newspaper view"),
+ messages_layout);
+ auto* feed = root->getParentServiceRoot()->getItemFromSubTree([messages](const RootItem* it) {
+ return it->kind() == RootItem::Kind::Feed && it->customId() == messages.at(0).m_feedId;
+ })->toFeed();
+ QString base_url;
+
+ if (feed != nullptr) {
+ QUrl url(NetworkFactory::sanitizeUrl(feed->source()));
+
+ if (url.isValid()) {
+ base_url = url.scheme() + QSL("://") + url.host();
+ }
+ }
+
+ return { msg_contents, base_url };
+}
+
Skin SkinFactory::skinInfo(const QString& skin_name, bool* ok) const {
Skin skin;
const QStringList skins_root_folders = {
diff --git a/src/librssguard/miscellaneous/skinfactory.h b/src/librssguard/miscellaneous/skinfactory.h
index d4dadba94..4f8bbc5f4 100644
--- a/src/librssguard/miscellaneous/skinfactory.h
+++ b/src/librssguard/miscellaneous/skinfactory.h
@@ -5,6 +5,8 @@
#include
+#include "core/message.h"
+
#include
#include
#include
@@ -12,6 +14,8 @@
#include
#include
+class RootItem;
+
class SkinEnums : public QObject {
Q_OBJECT
@@ -82,6 +86,8 @@ class RSSGUARD_DLLSPEC SkinFactory : public QObject {
QString adBlockedPage(const QString& url, const QString& filter);
+ QPair generateHtmlOfArticles(const QList& messages, RootItem* root) const;
+
// Gets skin about a particular skin.
Skin skinInfo(const QString& skin_name, bool* ok = nullptr) const;
diff --git a/src/librssguard/services/abstract/cacheforserviceroot.cpp b/src/librssguard/services/abstract/cacheforserviceroot.cpp
index 10454301c..24d605f46 100644
--- a/src/librssguard/services/abstract/cacheforserviceroot.cpp
+++ b/src/librssguard/services/abstract/cacheforserviceroot.cpp
@@ -12,6 +12,8 @@
CacheForServiceRoot::CacheForServiceRoot() : m_uniqueId(NO_PARENT_CATEGORY), m_cacheSaveMutex(new QMutex()) {}
+CacheForServiceRoot::~CacheForServiceRoot() {}
+
void CacheForServiceRoot::addLabelsAssignmentsToCache(const QStringList& ids_of_messages,
const QString& lbl_custom_id,
bool assign) {
diff --git a/src/librssguard/services/abstract/cacheforserviceroot.h b/src/librssguard/services/abstract/cacheforserviceroot.h
index 763589b8f..0c89238af 100644
--- a/src/librssguard/services/abstract/cacheforserviceroot.h
+++ b/src/librssguard/services/abstract/cacheforserviceroot.h
@@ -20,6 +20,7 @@ struct CacheSnapshot {
class CacheForServiceRoot {
public:
explicit CacheForServiceRoot();
+ virtual ~CacheForServiceRoot();
virtual void saveAllCachedData(bool ignore_errors) = 0;
diff --git a/src/librssguard/services/abstract/serviceroot.cpp b/src/librssguard/services/abstract/serviceroot.cpp
index 6aca669c5..acf678398 100644
--- a/src/librssguard/services/abstract/serviceroot.cpp
+++ b/src/librssguard/services/abstract/serviceroot.cpp
@@ -440,7 +440,7 @@ void ServiceRoot::syncIn() {
QIcon original_icon = icon();
setIcon(qApp->icons()->fromTheme(QSL("view-refresh")));
- itemChanged(QList() << this);
+ itemChanged({ this });
RootItem* new_tree = obtainNewTreeForSyncIn();
if (new_tree != nullptr) {
diff --git a/src/librssguard/services/gmail/gmailserviceroot.cpp b/src/librssguard/services/gmail/gmailserviceroot.cpp
index 6e226deef..ac30d08b2 100644
--- a/src/librssguard/services/gmail/gmailserviceroot.cpp
+++ b/src/librssguard/services/gmail/gmailserviceroot.cpp
@@ -25,6 +25,12 @@ GmailServiceRoot::GmailServiceRoot(RootItem* parent)
setIcon(GmailEntryPoint().icon());
}
+GmailServiceRoot::~GmailServiceRoot() {
+ if (!m_emailPreview.isNull()) {
+ m_emailPreview->deleteLater();
+ }
+}
+
void GmailServiceRoot::updateTitle() {
setTitle(TextFactory::extractUsernameFromEmail(m_network->username()) + QSL(" (Gmail)"));
}
@@ -100,7 +106,7 @@ bool GmailServiceRoot::wantsBaggedIdsOfExistingMessages() const {
CustomMessagePreviewer* GmailServiceRoot::customMessagePreviewer() {
if (m_emailPreview.isNull()) {
- m_emailPreview.reset(new EmailPreviewer());
+ m_emailPreview = new EmailPreviewer();
}
return m_emailPreview.data();
diff --git a/src/librssguard/services/gmail/gmailserviceroot.h b/src/librssguard/services/gmail/gmailserviceroot.h
index 63aa8e8ad..7d48d1dde 100644
--- a/src/librssguard/services/gmail/gmailserviceroot.h
+++ b/src/librssguard/services/gmail/gmailserviceroot.h
@@ -15,6 +15,7 @@ class GmailServiceRoot : public ServiceRoot, public CacheForServiceRoot {
public:
explicit GmailServiceRoot(RootItem* parent = nullptr);
+ virtual ~GmailServiceRoot();
void setNetwork(GmailNetworkFactory* network);
GmailNetworkFactory* network() const;
@@ -49,7 +50,7 @@ class GmailServiceRoot : public ServiceRoot, public CacheForServiceRoot {
void updateTitle();
private:
- QScopedPointer m_emailPreview;
+ QPointer m_emailPreview;
GmailNetworkFactory* m_network;
QAction* m_actionReply;
Message m_replyToMessage;