diff --git a/resources/icons.qrc b/resources/icons.qrc
index 68cbb2aee..2d41be1a3 100644
--- a/resources/icons.qrc
+++ b/resources/icons.qrc
@@ -8,6 +8,8 @@
./graphics/Breeze/categories/32/applications-system.svg
./graphics/Breeze/actions/32/arrow-down.svg
./graphics/Breeze/actions/32/arrow-down-double.svg
+ ./graphics/Breeze/actions/32/arrow-left.svg
+ ./graphics/Breeze/actions/32/arrow-right.svg
./graphics/Breeze/actions/32/arrow-up.svg
./graphics/Breeze/actions/32/arrow-up-double.svg
./graphics/Breeze/actions/32/call-start.svg
@@ -99,6 +101,8 @@
./graphics/Breeze Dark/categories/32/applications-system.svg
./graphics/Breeze Dark/actions/32/arrow-down.svg
./graphics/Breeze Dark/actions/32/arrow-down-double.svg
+ ./graphics/Breeze Dark/actions/32/arrow-left.svg
+ ./graphics/Breeze Dark/actions/32/arrow-right.svg
./graphics/Breeze Dark/actions/32/arrow-up.svg
./graphics/Breeze Dark/actions/32/arrow-up-double.svg
./graphics/Breeze Dark/actions/32/call-start.svg
@@ -274,6 +278,8 @@
./graphics/Numix/22/categories/applications-system.svg
./graphics/Numix/22/actions/arrow-down.svg
./graphics/Numix/22/actions/arrow-down-double.svg
+ ./graphics/Numix/22/actions/arrow-left.svg
+ ./graphics/Numix/22/actions/arrow-right.svg
./graphics/Numix/22/actions/arrow-up.svg
./graphics/Numix/22/actions/arrow-up-double.svg
./graphics/Numix/22/actions/browser-download.svg
diff --git a/src/librssguard/CMakeLists.txt b/src/librssguard/CMakeLists.txt
index c2dccee1b..f6afa971d 100644
--- a/src/librssguard/CMakeLists.txt
+++ b/src/librssguard/CMakeLists.txt
@@ -1,4 +1,6 @@
set(SOURCES
+ core/articlelistnotificationmodel.cpp
+ core/articlelistnotificationmodel.h
core/feeddownloader.cpp
core/feeddownloader.h
core/feedsmodel.cpp
diff --git a/src/librssguard/core/articlelistnotificationmodel.cpp b/src/librssguard/core/articlelistnotificationmodel.cpp
new file mode 100644
index 000000000..4b1998dfd
--- /dev/null
+++ b/src/librssguard/core/articlelistnotificationmodel.cpp
@@ -0,0 +1,23 @@
+// For license of this file, see /LICENSE.md.
+
+#include "core/articlelistnotificationmodel.h"
+
+ArticleListNotificationModel::ArticleListNotificationModel(QObject* parent)
+ : QAbstractListModel(parent), m_currentPage(-1) {}
+
+ArticleListNotificationModel::~ArticleListNotificationModel() {}
+
+void ArticleListNotificationModel::setArticles(const QList& msgs) {
+ m_articles = msgs;
+ m_currentPage = 0;
+}
+
+void ArticleListNotificationModel::nextPage() {}
+
+void ArticleListNotificationModel::previousPage() {}
+
+int ArticleListNotificationModel::rowCount(const QModelIndex& parent) const {}
+
+int ArticleListNotificationModel::columnCount(const QModelIndex& parent) const {}
+
+QVariant ArticleListNotificationModel::data(const QModelIndex& index, int role) const {}
diff --git a/src/librssguard/core/articlelistnotificationmodel.h b/src/librssguard/core/articlelistnotificationmodel.h
new file mode 100644
index 000000000..96a898a97
--- /dev/null
+++ b/src/librssguard/core/articlelistnotificationmodel.h
@@ -0,0 +1,33 @@
+// For license of this file, see /LICENSE.md.
+
+#ifndef ARTICLELISTNOTIFICATIONMODEL_H
+#define ARTICLELISTNOTIFICATIONMODEL_H
+
+#include
+
+#include "core/message.h"
+
+class ArticleListNotificationModel : public QAbstractListModel {
+ public:
+ explicit ArticleListNotificationModel(QObject* parent = nullptr);
+ virtual ~ArticleListNotificationModel();
+
+ void setArticles(const QList& msgs);
+
+ void nextPage();
+ void previousPage();
+
+ virtual int rowCount(const QModelIndex& parent) const;
+ virtual int columnCount(const QModelIndex& parent) const;
+ virtual QVariant data(const QModelIndex& index, int role) const;
+
+ signals:
+ void nextPagePossibleChanged(bool possible);
+ void previousPagePossibleChanged(bool possible);
+
+ private:
+ QList m_articles;
+ int m_currentPage;
+};
+
+#endif // ARTICLELISTNOTIFICATIONMODEL_H
diff --git a/src/librssguard/core/feeddownloader.cpp b/src/librssguard/core/feeddownloader.cpp
index 8b51c3d42..dbe1027a0 100644
--- a/src/librssguard/core/feeddownloader.cpp
+++ b/src/librssguard/core/feeddownloader.cpp
@@ -413,15 +413,17 @@ void FeedDownloader::updateOneFeed(ServiceRoot* acc,
<< "microseconds.";
if (feed->status() != Feed::Status::NewMessages) {
- feed->setStatus(updated_messages.first > 0 || updated_messages.second > 0 ? Feed::Status::NewMessages
- : Feed::Status::Normal);
+ feed->setStatus((!updated_messages.m_all.isEmpty() || !updated_messages.m_unread.isEmpty())
+ ? Feed::Status::NewMessages
+ : Feed::Status::Normal);
}
- qDebugNN << LOGSEC_FEEDDOWNLOADER << updated_messages << " messages for feed " << feed->customId()
- << " stored in DB.";
+ qDebugNN << LOGSEC_FEEDDOWNLOADER << updated_messages.m_unread.size() << " unread messages and"
+ << NONQUOTE_W_SPACE(updated_messages.m_all.size()) "total messages for feed"
+ << QUOTE_W_SPACE(feed->customId()) << "stored in DB.";
- if (updated_messages.first > 0) {
- m_results.appendUpdatedFeed({feed, updated_messages.first});
+ if (!updated_messages.m_unread.isEmpty()) {
+ m_results.appendUpdatedFeed(feed, updated_messages.m_unread);
}
}
catch (const FeedFetchException& feed_ex) {
@@ -445,7 +447,6 @@ void FeedDownloader::finalizeUpdate() {
qDebugNN << LOGSEC_FEEDDOWNLOADER << "Finished feed updates in thread"
<< QUOTE_W_SPACE_DOT(QThread::currentThreadId());
- m_results.sort();
m_feeds.clear();
// Update of feeds has finished.
@@ -528,7 +529,8 @@ QString FeedDownloadResults::overview(int how_many_feeds) const {
QStringList result;
for (int i = 0, number_items_output = qMin(how_many_feeds, m_updatedFeeds.size()); i < number_items_output; i++) {
- result.append(m_updatedFeeds.at(i).first->title() + QSL(": ") + QString::number(m_updatedFeeds.at(i).second));
+ result.append(m_updatedFeeds.keys().at(i)->title() + QSL(": ") +
+ QString::number(m_updatedFeeds.value(m_updatedFeeds.keys().at(i)).size()));
}
QString res_str = result.join(QSL("\n"));
@@ -540,22 +542,14 @@ QString FeedDownloadResults::overview(int how_many_feeds) const {
return res_str;
}
-void FeedDownloadResults::appendUpdatedFeed(const QPair& feed) {
- m_updatedFeeds.append(feed);
-}
-
-void FeedDownloadResults::sort() {
- std::sort(m_updatedFeeds.begin(),
- m_updatedFeeds.end(),
- [](const QPair& lhs, const QPair& rhs) {
- return lhs.second > rhs.second;
- });
+void FeedDownloadResults::appendUpdatedFeed(Feed* feed, const QList& updated_unread_msgs) {
+ m_updatedFeeds.insert(feed, updated_unread_msgs);
}
void FeedDownloadResults::clear() {
m_updatedFeeds.clear();
}
-QList> FeedDownloadResults::updatedFeeds() const {
+QHash> FeedDownloadResults::updatedFeeds() const {
return m_updatedFeeds;
}
diff --git a/src/librssguard/core/feeddownloader.h b/src/librssguard/core/feeddownloader.h
index 09e29fd5e..8e24fd925 100644
--- a/src/librssguard/core/feeddownloader.h
+++ b/src/librssguard/core/feeddownloader.h
@@ -5,29 +5,28 @@
#include
-#include
-#include
-
#include "core/message.h"
#include "exceptions/applicationexception.h"
#include "services/abstract/cacheforserviceroot.h"
#include "services/abstract/feed.h"
+#include
+#include
+#include
+
class MessageFilter;
// Represents results of batch feed updates.
class FeedDownloadResults {
public:
- QList> updatedFeeds() const;
+ QHash> updatedFeeds() const;
QString overview(int how_many_feeds) const;
-
- void appendUpdatedFeed(const QPair& feed);
- void sort();
+ void appendUpdatedFeed(Feed* feed, const QList& updated_unread_msgs);
void clear();
private:
// QString represents title if the feed, int represents count of newly downloaded messages.
- QList> m_updatedFeeds;
+ QHash> m_updatedFeeds;
};
struct FeedUpdateRequest {
diff --git a/src/librssguard/core/message.cpp b/src/librssguard/core/message.cpp
index 181cee753..20c735f1f 100644
--- a/src/librssguard/core/message.cpp
+++ b/src/librssguard/core/message.cpp
@@ -2,7 +2,9 @@
#include "core/message.h"
+#include "miscellaneous/application.h"
#include "miscellaneous/textfactory.h"
+#include "network-web/webfactory.h"
#include "services/abstract/feed.h"
#include "services/abstract/label.h"
@@ -75,6 +77,8 @@ Message::Message() {
void Message::sanitize(const Feed* feed, bool fix_future_datetimes) {
// Sanitize title.
+ m_title = qApp->web()->stripTags(qApp->web()->unescapeHtml(m_title));
+
m_title = m_title
// Remove non-breaking spaces.
@@ -89,6 +93,9 @@ void Message::sanitize(const Feed* feed, bool fix_future_datetimes) {
// Remove non-breaking zero-width spaces.
.remove(QChar(65279));
+ // Sanitize author.
+ m_author = qApp->web()->stripTags(qApp->web()->unescapeHtml(m_author));
+
// Sanitize URL.
m_url = m_url.trimmed();
diff --git a/src/librssguard/database/databasequeries.cpp b/src/librssguard/database/databasequeries.cpp
index a06807faa..aa9bb6107 100644
--- a/src/librssguard/database/databasequeries.cpp
+++ b/src/librssguard/database/databasequeries.cpp
@@ -1354,7 +1354,7 @@ QHash DatabaseQueries::bagsOfMessages(const QSqlDatabase&
return ids;
}
-QPair DatabaseQueries::updateMessages(const QSqlDatabase& db,
+UpdatedArticles DatabaseQueries::updateMessages(const QSqlDatabase& db,
QList& messages,
Feed* feed,
bool force_update,
@@ -1362,10 +1362,10 @@ QPair DatabaseQueries::updateMessages(const QSqlDatabase& db,
bool* ok) {
if (messages.isEmpty()) {
*ok = true;
- return {0, 0};
+ return {};
}
- QPair updated_messages = {0, 0};
+ UpdatedArticles updated_messages;
int account_id = feed->getParentServiceRoot()->accountId();
auto feed_custom_id = feed->customId();
@@ -1615,10 +1615,10 @@ QPair DatabaseQueries::updateMessages(const QSqlDatabase& db,
<< QUOTE_W_SPACE(message.m_url) << "in DB.";
if (!message.m_isRead) {
- updated_messages.first++;
+ updated_messages.m_unread.append(message);
}
- updated_messages.second++;
+ updated_messages.m_all.append(message);
message.m_insertedUpdated = true;
}
else if (query_update.lastError().isValid()) {
@@ -1655,10 +1655,10 @@ QPair DatabaseQueries::updateMessages(const QSqlDatabase& db,
}
if (!msg->m_isRead) {
- updated_messages.first++;
+ updated_messages.m_unread.append(*msg);
}
- updated_messages.second++;
+ updated_messages.m_all.append(*msg);
msg->m_insertedUpdated = true;
vals.append(QSL("\n(':feed', ':title', :is_read, :is_important, :is_deleted, "
@@ -1742,10 +1742,10 @@ QPair DatabaseQueries::updateMessages(const QSqlDatabase& db,
// but its assigned labels were changed. Therefore we must count article
// as updated.
if (!message.m_isRead) {
- updated_messages.first++;
+ updated_messages.m_unread.append(message);
}
- updated_messages.second++;
+ updated_messages.m_all.append(message);
}
}
else {
diff --git a/src/librssguard/database/databasequeries.h b/src/librssguard/database/databasequeries.h
index f674d51dc..4a27d405b 100644
--- a/src/librssguard/database/databasequeries.h
+++ b/src/librssguard/database/databasequeries.h
@@ -21,11 +21,6 @@
#include
#include
-struct ArticleCounts {
- int m_total = -1;
- int m_unread = -1;
-};
-
class DatabaseQueries {
public:
static QMap messageTableAttributes(bool only_msg_table, bool is_sqlite);
@@ -162,7 +157,7 @@ class DatabaseQueries {
static void createOverwriteAccount(const QSqlDatabase& db, ServiceRoot* account);
// Returns counts of updated messages .
- static QPair updateMessages(const QSqlDatabase& db,
+ static UpdatedArticles updateMessages(const QSqlDatabase& db,
QList& messages,
Feed* feed,
bool force_update,
diff --git a/src/librssguard/definitions/definitions.h b/src/librssguard/definitions/definitions.h
index d4e347dbd..61b67d787 100644
--- a/src/librssguard/definitions/definitions.h
+++ b/src/librssguard/definitions/definitions.h
@@ -100,9 +100,10 @@
#define MAX_THREADPOOL_THREADS 32
#define WEB_BROWSER_SCROLL_STEP 50.0
-#define NOTIFICATIONS_MARGIN 16
-#define NOTIFICATIONS_WIDTH 256
-#define NOTIFICATIONS_TIMEOUT 15s
+#define NOTIFICATIONS_MARGIN 16
+#define NOTIFICATIONS_WIDTH 256
+#define NOTIFICATIONS_TIMEOUT 15s
+#define NOTIFICATIONS_PAGE_SIZE 10
#define GOOGLE_SEARCH_URL "https://www.google.com/search?q=%1&ie=utf-8&oe=utf-8"
#define GOOGLE_SUGGEST_URL "http://suggestqueries.google.com/complete/search?output=toolbar&hl=en&q=%1"
diff --git a/src/librssguard/definitions/typedefs.h b/src/librssguard/definitions/typedefs.h
index 19e7bb02e..73e13d1ba 100644
--- a/src/librssguard/definitions/typedefs.h
+++ b/src/librssguard/definitions/typedefs.h
@@ -14,4 +14,14 @@ typedef QList> Assignment;
typedef QPair AssignmentItem;
typedef QPair ImportanceChange;
+struct ArticleCounts {
+ int m_total = -1;
+ int m_unread = -1;
+};
+
+struct UpdatedArticles {
+ QList m_unread;
+ QList m_all;
+};
+
#endif // TYPEDEFS_H
diff --git a/src/librssguard/gui/notifications/articlelistnotification.cpp b/src/librssguard/gui/notifications/articlelistnotification.cpp
index 5eb62d926..ecb38c997 100644
--- a/src/librssguard/gui/notifications/articlelistnotification.cpp
+++ b/src/librssguard/gui/notifications/articlelistnotification.cpp
@@ -2,9 +2,21 @@
#include "gui/notifications/articlelistnotification.h"
+#include "miscellaneous/iconfactory.h"
+
ArticleListNotification::ArticleListNotification(QWidget* parent) : BaseToastNotification(parent) {
m_ui.setupUi(this);
setupCloseButton(m_ui.m_btnClose);
- setupTimedClosing();
+
+ m_ui.m_btnNextPage->setIcon(qApp->icons()->fromTheme(QSL("arrow-right"), QSL("stock_right")));
+ m_ui.m_btnPreviousPage->setIcon(qApp->icons()->fromTheme(QSL("arrow-left"), QSL("stock_left")));
+ m_ui.m_btnOpenArticleList->setIcon(qApp->icons()->fromTheme(QSL("view-list-details")));
+ m_ui.m_btnOpenWebBrowser->setIcon(qApp->icons()->fromTheme(QSL("document-open")));
+}
+
+void ArticleListNotification::loadResults(const QHash>& new_messages) {
+ setupTimedClosing();
+
+ m_ui.m_treeArticles->setModel()
}
diff --git a/src/librssguard/gui/notifications/articlelistnotification.h b/src/librssguard/gui/notifications/articlelistnotification.h
index 8aaea8095..f8888165f 100644
--- a/src/librssguard/gui/notifications/articlelistnotification.h
+++ b/src/librssguard/gui/notifications/articlelistnotification.h
@@ -5,14 +5,20 @@
#include "gui/notifications/basetoastnotification.h"
+#include "core/message.h"
+
#include "ui_articlelistnotification.h"
+class Feed;
+
class ArticleListNotification : public BaseToastNotification {
Q_OBJECT
public:
explicit ArticleListNotification(QWidget* parent = nullptr);
+ void loadResults(const QHash>& new_messages);
+
private:
Ui::ArticleListNotification m_ui;
};
diff --git a/src/librssguard/gui/notifications/articlelistnotification.ui b/src/librssguard/gui/notifications/articlelistnotification.ui
new file mode 100644
index 000000000..fda69a518
--- /dev/null
+++ b/src/librssguard/gui/notifications/articlelistnotification.ui
@@ -0,0 +1,130 @@
+
+
+ ArticleListNotification
+
+
+
+ 0
+ 0
+ 338
+ 271
+
+
+
+
+ 6
+
+
+ 6
+
+
+ 6
+
+
+ 6
+
+ -
+
+
+ true
+
+
+ false
+
+
+ false
+
+
+ true
+
+
+
+ -
+
+
-
+
+
+
+
+
+ Qt::AlignCenter
+
+
+ true
+
+
+
+ -
+
+
+ ...
+
+
+
+
+
+ -
+
+
-
+
+
+ Go to previous page
+
+
+
+ -
+
+
+ Go to next page
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Open article in article list
+
+
+
+ -
+
+
+ Open article in web browser
+
+
+
+
+
+
+
+
+
+ PlainToolButton
+ QToolButton
+
+
+
+
+ m_btnClose
+ m_treeArticles
+ m_btnPreviousPage
+ m_btnNextPage
+ m_btnOpenArticleList
+ m_btnOpenWebBrowser
+
+
+
+
diff --git a/src/librssguard/gui/notifications/basetoastnotification.cpp b/src/librssguard/gui/notifications/basetoastnotification.cpp
index c396c6e56..a2a59addc 100644
--- a/src/librssguard/gui/notifications/basetoastnotification.cpp
+++ b/src/librssguard/gui/notifications/basetoastnotification.cpp
@@ -6,12 +6,13 @@
#include
#include
+#include
#include
using namespace std::chrono_literals;
-BaseToastNotification::BaseToastNotification(QWidget* parent) : QDialog(parent) {
+BaseToastNotification::BaseToastNotification(QWidget* parent) : QDialog(parent), m_timerId(-1) {
setAttribute(Qt::WidgetAttribute::WA_ShowWithoutActivating);
setFixedWidth(NOTIFICATIONS_WIDTH);
setFocusPolicy(Qt::FocusPolicy::NoFocus);
@@ -39,8 +40,15 @@ void BaseToastNotification::setupCloseButton(QAbstractButton* btn) {
connect(btn, &QAbstractButton::clicked, this, &BaseToastNotification::close);
}
+void BaseToastNotification::stopTimedClosing() {
+ killTimer(m_timerId);
+ m_timerId = -1;
+}
+
void BaseToastNotification::setupTimedClosing() {
- QTimer::singleShot(NOTIFICATIONS_TIMEOUT, this, &BaseToastNotification::close);
+ if (m_timerId < 0) {
+ m_timerId = startTimer(NOTIFICATIONS_TIMEOUT);
+ }
}
bool BaseToastNotification::eventFilter(QObject* watched, QEvent* event) {
@@ -48,12 +56,28 @@ bool BaseToastNotification::eventFilter(QObject* watched, QEvent* event) {
return true;
}
else {
+ if (event->type() == QEvent::Type::Enter) {
+ stopTimedClosing();
+ }
+
+ if (event->type() == QEvent::Type::Leave) {
+ setupTimedClosing();
+ }
+
return QDialog::eventFilter(watched, event);
}
}
void BaseToastNotification::closeEvent(QCloseEvent* event) {
+ stopTimedClosing();
emit closeRequested(this);
}
void BaseToastNotification::reject() {}
+
+void BaseToastNotification::timerEvent(QTimerEvent* event) {
+ if (event->timerId() == m_timerId) {
+ stopTimedClosing();
+ emit closeRequested(this);
+ }
+}
diff --git a/src/librssguard/gui/notifications/basetoastnotification.h b/src/librssguard/gui/notifications/basetoastnotification.h
index 654e937ea..7749b073d 100644
--- a/src/librssguard/gui/notifications/basetoastnotification.h
+++ b/src/librssguard/gui/notifications/basetoastnotification.h
@@ -19,10 +19,12 @@ class BaseToastNotification : public QDialog {
protected:
virtual bool eventFilter(QObject* watched, QEvent* event);
+ virtual void timerEvent(QTimerEvent* event);
virtual void closeEvent(QCloseEvent* event);
- void setupCloseButton(QAbstractButton* btn);
void setupTimedClosing();
+ void setupCloseButton(QAbstractButton* btn);
+ void stopTimedClosing();
signals:
void closeRequested(BaseToastNotification* notif);
diff --git a/src/librssguard/gui/notifications/toastnotification.cpp b/src/librssguard/gui/notifications/toastnotification.cpp
index 8918bb749..c057be392 100644
--- a/src/librssguard/gui/notifications/toastnotification.cpp
+++ b/src/librssguard/gui/notifications/toastnotification.cpp
@@ -43,7 +43,10 @@ void ToastNotification::loadNotification(Notification::Event event, const GuiMes
if (action.m_action) {
m_ui.m_btnAction->setText(action.m_title.isEmpty() ? tr("Do it!") : action.m_title);
- connect(m_ui.m_btnAction, &QPushButton::clicked, this, action.m_action);
+ connect(m_ui.m_btnAction, &QPushButton::clicked, this, [this, action]() {
+ action.m_action();
+ emit closeRequested(this);
+ });
}
else {
m_ui.m_mainLayout->removeItem(m_ui.m_actionLayout);
diff --git a/src/librssguard/gui/notifications/toastnotification.ui b/src/librssguard/gui/notifications/toastnotification.ui
index 029cec080..09e3b815b 100644
--- a/src/librssguard/gui/notifications/toastnotification.ui
+++ b/src/librssguard/gui/notifications/toastnotification.ui
@@ -10,9 +10,6 @@
143
-
- Dialog
-
6
diff --git a/src/librssguard/gui/notifications/toastnotificationsmanager.cpp b/src/librssguard/gui/notifications/toastnotificationsmanager.cpp
index 73547ad85..d783c40ef 100644
--- a/src/librssguard/gui/notifications/toastnotificationsmanager.cpp
+++ b/src/librssguard/gui/notifications/toastnotificationsmanager.cpp
@@ -12,6 +12,23 @@
#include
#include
+QString ToastNotificationsManager::textForPosition(ToastNotificationsManager::NotificationPosition pos) {
+ switch (pos) {
+ case TopLeft:
+ return QObject::tr("top-left");
+
+ case TopRight:
+ return QObject::tr("top-right");
+
+ case BottomLeft:
+ return QObject::tr("bottom-left");
+
+ case BottomRight:
+ default:
+ return QObject::tr("bottom-right");
+ }
+}
+
ToastNotificationsManager::ToastNotificationsManager(NotificationPosition position, int screen, QObject* parent)
: QObject(parent), m_position(position), m_screen(screen), m_articleListNotification(nullptr) {}
@@ -50,9 +67,26 @@ void ToastNotificationsManager::clear() {
void ToastNotificationsManager::showNotification(Notification::Event event,
const GuiMessage& msg,
const GuiAction& action) {
- ToastNotification* notif = new ToastNotification(event, msg, action, qApp->mainFormWidget());
+ BaseToastNotification* notif;
- hookNotification(notif);
+ if (!msg.m_feedFetchResults.updatedFeeds().isEmpty()) {
+ if (m_articleListNotification == nullptr) {
+ m_articleListNotification = new ArticleListNotification();
+ hookNotification(m_articleListNotification);
+ }
+ else if (m_activeNotifications.contains(m_articleListNotification)) {
+ // Article notification is somewhere in list, clear first to move it to first positon.
+ closeNotification(m_articleListNotification, false);
+ }
+
+ m_articleListNotification->loadResults(msg.m_feedFetchResults.updatedFeeds());
+
+ notif = m_articleListNotification;
+ }
+ else {
+ notif = new ToastNotification(event, msg, action, qApp->mainFormWidget());
+ hookNotification(notif);
+ }
auto* screen = moveToProperScreen(notif);
@@ -76,39 +110,6 @@ void ToastNotificationsManager::showNotification(Notification::Event event,
m_activeNotifications.prepend(notif);
}
-void ToastNotificationsManager::showNotification(const QList& new_messages) {
- if (m_articleListNotification == nullptr) {
- m_articleListNotification = new ArticleListNotification();
- hookNotification(m_articleListNotification);
- }
-
- if (!m_activeNotifications.isEmpty() && m_activeNotifications.first() != m_articleListNotification) {
- // Article notification is somewhere in list, clear first to move it to first positon.
- closeNotification(m_articleListNotification, false);
- }
-
- auto* screen = moveToProperScreen(m_articleListNotification);
-
- // Insert new notification into free space.
- m_articleListNotification->show();
-
- auto notif_new_pos = cornerForNewNotification(screen->availableGeometry());
-
- // Make sure notification is finally resized.
- m_articleListNotification->adjustSize();
- qApp->processEvents();
-
- // Move notification, at this point we already need to know its precise size.
- moveNotificationToCorner(m_articleListNotification, notif_new_pos);
-
- // Remove out-of-bounds old notifications and shift existing
- // ones to make space for new notifications.
- removeOutOfBoundsNotifications(m_articleListNotification->height());
- makeSpaceForNotification(m_articleListNotification->height());
-
- m_activeNotifications.prepend(m_articleListNotification);
-}
-
void ToastNotificationsManager::closeNotification(BaseToastNotification* notif, bool delete_from_memory) {
auto notif_idx = m_activeNotifications.indexOf(notif);
@@ -160,7 +161,7 @@ QPoint ToastNotificationsManager::cornerForNewNotification(QRect screen_rect) {
void ToastNotificationsManager::hookNotification(BaseToastNotification* notif) {
connect(notif, &BaseToastNotification::closeRequested, this, [this](BaseToastNotification* notif) {
- closeNotification(notif, false);
+ closeNotification(notif, notif != m_articleListNotification);
});
}
diff --git a/src/librssguard/gui/notifications/toastnotificationsmanager.h b/src/librssguard/gui/notifications/toastnotificationsmanager.h
index 1a28ca677..10f2b54ed 100644
--- a/src/librssguard/gui/notifications/toastnotificationsmanager.h
+++ b/src/librssguard/gui/notifications/toastnotificationsmanager.h
@@ -23,6 +23,10 @@ class ToastNotificationsManager : public QObject {
BottomRight = 3
};
+ Q_ENUM(NotificationPosition)
+
+ static QString textForPosition(ToastNotificationsManager::NotificationPosition pos);
+
explicit ToastNotificationsManager(ToastNotificationsManager::NotificationPosition position,
int screen,
QObject* parent = nullptr);
@@ -41,7 +45,6 @@ class ToastNotificationsManager : public QObject {
public slots:
void clear();
void showNotification(Notification::Event event, const GuiMessage& msg, const GuiAction& action);
- void showNotification(const QList& new_messages);
private slots:
void closeNotification(BaseToastNotification* notif, bool delete_from_memory);
diff --git a/src/librssguard/gui/reusable/helpspoiler.cpp b/src/librssguard/gui/reusable/helpspoiler.cpp
index 71a79a010..6fbaab86a 100644
--- a/src/librssguard/gui/reusable/helpspoiler.cpp
+++ b/src/librssguard/gui/reusable/helpspoiler.cpp
@@ -26,6 +26,7 @@ HelpSpoiler::HelpSpoiler(QWidget* parent)
m_btnToggle->setCheckable(true);
m_btnToggle->setChecked(false);
+ m_content->setStyleSheet(QSL("QScrollArea { border: 1px solid %1; }").arg(palette().windowText().color().name()));
m_content->setSizePolicy(QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Fixed);
m_content->setMaximumHeight(0);
m_content->setMinimumHeight(0);
diff --git a/src/librssguard/gui/settings/settingsnotifications.cpp b/src/librssguard/gui/settings/settingsnotifications.cpp
index 672062450..5ea235ffa 100644
--- a/src/librssguard/gui/settings/settingsnotifications.cpp
+++ b/src/librssguard/gui/settings/settingsnotifications.cpp
@@ -8,6 +8,8 @@
#include "miscellaneous/settings.h"
#include
+#include
+#include
SettingsNotifications::SettingsNotifications(Settings* settings, QWidget* parent) : SettingsPanel(settings, parent) {
m_ui.setupUi(this);
@@ -25,6 +27,14 @@ SettingsNotifications::SettingsNotifications(Settings* settings, QWidget* parent
connect(m_ui.m_rbNativeNotifications, &QRadioButton::toggled, this, &SettingsNotifications::dirtifySettings);
connect(m_ui.m_rbNativeNotifications, &QRadioButton::toggled, this, &SettingsNotifications::requireRestart);
+ connect(m_ui.m_sbScreen, &QSpinBox::valueChanged, this, &SettingsNotifications::dirtifySettings);
+ connect(m_ui.m_sbScreen, &QSpinBox::valueChanged, this, &SettingsNotifications::requireRestart);
+
+ m_ui.m_sbScreen->setMinimum(-1);
+ m_ui.m_sbScreen->setMaximum(QGuiApplication::screens().size() - 1);
+
+ connect(m_ui.m_sbScreen, &QSpinBox::valueChanged, this, &SettingsNotifications::showScreenInfo);
+
connect(m_ui.m_cbCustomNotificationsPosition,
&QComboBox::currentIndexChanged,
this,
@@ -33,6 +43,15 @@ SettingsNotifications::SettingsNotifications(Settings* settings, QWidget* parent
&QComboBox::currentIndexChanged,
this,
&SettingsNotifications::requireRestart);
+
+ QMetaEnum enm = QMetaEnum::fromType();
+
+ for (int i = 0; i < enm.keyCount(); i++) {
+ m_ui.m_cbCustomNotificationsPosition
+ ->addItem(ToastNotificationsManager::
+ textForPosition(ToastNotificationsManager::NotificationPosition(enm.value(i))),
+ enm.value(i));
+ }
}
void SettingsNotifications::loadSettings() {
@@ -43,6 +62,16 @@ void SettingsNotifications::loadSettings() {
->setChecked(settings()->value(GROUP(GUI), SETTING(GUI::EnableNotifications)).toBool());
m_ui.m_editor->loadNotifications(qApp->notifications()->allNotifications());
+ m_ui.m_rbNativeNotifications
+ ->setChecked(!settings()->value(GROUP(GUI), SETTING(GUI::UseToastNotifications)).toBool());
+ m_ui.m_sbScreen->setValue(settings()->value(GROUP(GUI), SETTING(GUI::ToastNotificationsScreen)).toInt());
+
+ m_ui.m_cbCustomNotificationsPosition
+ ->setCurrentIndex(m_ui.m_cbCustomNotificationsPosition
+ ->findData(settings()
+ ->value(GROUP(GUI), SETTING(GUI::ToastNotificationsPosition))
+ .value()));
+
onEndLoadSettings();
}
@@ -53,5 +82,29 @@ void SettingsNotifications::saveSettings() {
settings()->setValue(GROUP(GUI), GUI::EnableNotifications, m_ui.m_checkEnableNotifications->isChecked());
qApp->notifications()->save(m_ui.m_editor->allNotifications(), settings());
+ settings()->setValue(GROUP(GUI), GUI::UseToastNotifications, m_ui.m_rbCustomNotifications->isChecked());
+ settings()->setValue(GROUP(GUI), GUI::ToastNotificationsScreen, m_ui.m_sbScreen->value());
+
+ settings()->setValue(GROUP(GUI),
+ GUI::ToastNotificationsPosition,
+ m_ui.m_cbCustomNotificationsPosition->currentData()
+ .value());
+
onEndSaveSettings();
}
+
+void SettingsNotifications::showScreenInfo(int index) {
+ QScreen* scr;
+
+ if (index < 0 || index >= QGuiApplication::screens().size()) {
+ scr = QGuiApplication::primaryScreen();
+ }
+ else {
+ scr = QGuiApplication::screens().at(index);
+ }
+
+ m_ui.m_lblScreenInfo->setText(QSL("%1 (%2x%3)")
+ .arg(scr->name(),
+ QString::number(scr->virtualSize().width()),
+ QString::number(scr->virtualSize().height())));
+}
diff --git a/src/librssguard/gui/settings/settingsnotifications.h b/src/librssguard/gui/settings/settingsnotifications.h
index eac55fb4f..8c306377f 100644
--- a/src/librssguard/gui/settings/settingsnotifications.h
+++ b/src/librssguard/gui/settings/settingsnotifications.h
@@ -10,7 +10,7 @@
class Settings;
class SettingsNotifications : public SettingsPanel {
- Q_OBJECT
+ Q_OBJECT
public:
explicit SettingsNotifications(Settings* settings, QWidget* parent = nullptr);
@@ -19,6 +19,9 @@ class SettingsNotifications : public SettingsPanel {
virtual void loadSettings();
virtual void saveSettings();
+ private slots:
+ void showScreenInfo(int index);
+
private:
Ui::SettingsNotifications m_ui;
};
diff --git a/src/librssguard/gui/settings/settingsnotifications.ui b/src/librssguard/gui/settings/settingsnotifications.ui
index 917ffadf7..6f3aa6a4c 100644
--- a/src/librssguard/gui/settings/settingsnotifications.ui
+++ b/src/librssguard/gui/settings/settingsnotifications.ui
@@ -45,9 +45,6 @@
Native notifications (tray icon must be enabled)
-
- true
-
-
@@ -55,6 +52,9 @@
Custom notifications
+
+ true
+
-
@@ -76,6 +76,27 @@
-
+ -
+
+
+ 99
+
+
+
+ -
+
+
+ Screen
+
+
+
+ -
+
+
+
+
+
+
diff --git a/src/librssguard/gui/webviewers/qtextbrowser/textbrowserviewer.cpp b/src/librssguard/gui/webviewers/qtextbrowser/textbrowserviewer.cpp
index a368e1be0..d4f761cf5 100644
--- a/src/librssguard/gui/webviewers/qtextbrowser/textbrowserviewer.cpp
+++ b/src/librssguard/gui/webviewers/qtextbrowser/textbrowserviewer.cpp
@@ -134,7 +134,7 @@ PreparedHtml TextBrowserViewer::prepareHtmlForMessage(const QList& mess
html.m_html += is_plain ? Qt::convertFromPlainText(message.m_contents, Qt::WhiteSpaceMode::WhiteSpaceNormal)
: message.m_contents;
- static QRegularExpression img_tag_rgx("\\
]*src\\s*=\\s*[\"\']([^\"\']*)[\"\'][^\\>]*\\>",
+ static QRegularExpression img_tag_rgx(QSL("\\
]*src\\s*=\\s*[\"\']([^\"\']*)[\"\'][^\\>]*\\>"),
QRegularExpression::PatternOption::CaseInsensitiveOption |
QRegularExpression::PatternOption::InvertedGreedinessOption);
diff --git a/src/librssguard/miscellaneous/application.cpp b/src/librssguard/miscellaneous/application.cpp
index 0e669ca8c..f4ae62f14 100644
--- a/src/librssguard/miscellaneous/application.cpp
+++ b/src/librssguard/miscellaneous/application.cpp
@@ -111,11 +111,13 @@ Application::Application(const QString& id, int& argc, char** argv, const QStrin
m_downloadManager = nullptr;
m_notifications = new NotificationFactory(this);
m_toastNotifications =
- new ToastNotificationsManager(settings()
- ->value(GROUP(GUI), SETTING(GUI::ToastNotificationsPosition))
- .value(),
- settings()->value(GROUP(GUI), SETTING(GUI::ToastNotificationsScreen)).toInt(),
- this);
+ settings()->value(GROUP(GUI), SETTING(GUI::UseToastNotifications)).toBool()
+ ? new ToastNotificationsManager(settings()
+ ->value(GROUP(GUI), SETTING(GUI::ToastNotificationsPosition))
+ .value(),
+ settings()->value(GROUP(GUI), SETTING(GUI::ToastNotificationsScreen)).toInt(),
+ this)
+ : nullptr;
m_shouldRestart = false;
#if defined(Q_OS_WIN)
@@ -695,22 +697,26 @@ void Application::showGuiMessageCore(Notification::Event event,
GuiMessageDestination dest,
const GuiAction& action,
QWidget* parent) {
- m_toastNotifications->showNotification(event, msg, action);
- return;
-
if (m_notifications->areNotificationsEnabled()) {
auto notification = m_notifications->notificationForEvent(event);
notification.playSound(this);
- if (SystemTrayIcon::isSystemTrayDesired() && SystemTrayIcon::isSystemTrayAreaAvailable() &&
- notification.balloonEnabled() && dest.m_tray) {
- trayIcon()->showMessage(msg.m_title.simplified().isEmpty() ? Notification::nameForEvent(notification.event())
- : msg.m_title,
- msg.m_message,
- msg.m_type,
- TRAY_ICON_BUBBLE_TIMEOUT,
- std::move(action.m_action));
+ if (notification.balloonEnabled() && dest.m_tray) {
+ if (m_toastNotifications != nullptr) {
+ // Toasts are enabled.
+ m_toastNotifications->showNotification(event, msg, action);
+ }
+ else if (SystemTrayIcon::isSystemTrayDesired() && SystemTrayIcon::isSystemTrayAreaAvailable()) {
+ // Use tray icon balloons (which are implemented as native notifications on most systems.
+ trayIcon()->showMessage(msg.m_title.simplified().isEmpty() ? Notification::nameForEvent(notification.event())
+ : msg.m_title,
+ msg.m_message,
+ msg.m_type,
+ TRAY_ICON_BUBBLE_TIMEOUT,
+ std::move(action.m_action));
+ }
+
return;
}
}
@@ -999,15 +1005,24 @@ void Application::onFeedUpdatesProgress(const Feed* feed, int current, int total
}
void Application::onFeedUpdatesFinished(const FeedDownloadResults& results) {
- auto fds = results.updatedFeeds();
- bool some_unquiet_feed = boolinq::from(fds).any([](const QPair& fd) {
- return !fd.first->isQuiet();
+ auto fds = results.updatedFeeds().keys();
+ bool some_unquiet_feed = boolinq::from(fds).any([](Feed* fd) {
+ return !fd->isQuiet();
});
if (some_unquiet_feed) {
- // Now, inform about results via GUI message/notification.
- qApp->showGuiMessage(Notification::Event::NewUnreadArticlesFetched,
- {tr("Unread articles fetched"), results.overview(10), QSystemTrayIcon::MessageIcon::NoIcon});
+ GuiMessage msg = {tr("Unread articles fetched"), QString(), QSystemTrayIcon::MessageIcon::NoIcon};
+
+ if (m_toastNotifications != nullptr) {
+ // Show custom and richer overview of updated feeds and articles.
+ msg.m_feedFetchResults = results;
+ }
+ else {
+ // Show simpler overview of updated feeds.
+ msg.m_message = results.overview(10);
+ }
+
+ qApp->showGuiMessage(Notification::Event::NewUnreadArticlesFetched, msg);
}
#if defined(Q_OS_WIN)
diff --git a/src/librssguard/miscellaneous/application.h b/src/librssguard/miscellaneous/application.h
index 41e333d30..c1a6e92be 100644
--- a/src/librssguard/miscellaneous/application.h
+++ b/src/librssguard/miscellaneous/application.h
@@ -60,6 +60,7 @@ struct GuiMessage {
QString m_title;
QString m_message;
QSystemTrayIcon::MessageIcon m_type;
+ FeedDownloadResults m_feedFetchResults;
};
Q_DECLARE_METATYPE(GuiMessage)
diff --git a/src/librssguard/miscellaneous/feedreader.cpp b/src/librssguard/miscellaneous/feedreader.cpp
index c9ccbef19..4e1fd627e 100644
--- a/src/librssguard/miscellaneous/feedreader.cpp
+++ b/src/librssguard/miscellaneous/feedreader.cpp
@@ -255,19 +255,6 @@ void FeedReader::removeMessageFilterToFeedAssignment(Feed* feed, MessageFilter*
}
void FeedReader::updateAllFeeds() {
- qApp
- ->showGuiMessage(Notification::Event::GeneralEvent,
- GuiMessage(QDateTime::currentDateTime().toString(),
- "Quisque ullamcorper ut purus nec tempus. Vivamus eros dolor, sagittis ultrices augue "
- "ut, posuere fringilla lorem. Donec posuere, enim sit amet fermentum dignissim, tellus "
- "lectus laoreet lectus, vestibulum laoreet felis tortor eget nunc. Curabitur sagittis "
- "quam in scelerisque placerat. Vivamus vel porta tortor. Vivamus nec volutpat sem",
- QSystemTrayIcon::MessageIcon::Information),
- GuiMessageDestination(),
- GuiAction("test", []() {
- qDebugNN << "aa";
- }));
-
updateFeeds(m_feedsModel->rootItem()->getSubTreeFeeds());
}
diff --git a/src/librssguard/services/abstract/serviceroot.cpp b/src/librssguard/services/abstract/serviceroot.cpp
index d316fba4a..09e9da7e9 100644
--- a/src/librssguard/services/abstract/serviceroot.cpp
+++ b/src/librssguard/services/abstract/serviceroot.cpp
@@ -1110,8 +1110,8 @@ ServiceRoot::LabelOperation operator&(ServiceRoot::LabelOperation lhs, ServiceRo
return static_cast(static_cast(lhs) & static_cast(rhs));
}
-QPair ServiceRoot::updateMessages(QList& messages, Feed* feed, bool force_update, QMutex* db_mutex) {
- QPair updated_messages = {0, 0};
+UpdatedArticles ServiceRoot::updateMessages(QList& messages, Feed* feed, bool force_update, QMutex* db_mutex) {
+ UpdatedArticles updated_messages;
if (messages.isEmpty()) {
qDebugNN << "No messages to be updated/added in DB for feed" << QUOTE_W_SPACE_DOT(feed->customId());
@@ -1125,7 +1125,7 @@ QPair ServiceRoot::updateMessages(QList& messages, Feed* feed
updated_messages = DatabaseQueries::updateMessages(database, messages, feed, force_update, db_mutex, &ok);
- if (updated_messages.first > 0 || updated_messages.second > 0) {
+ if (!updated_messages.m_unread.isEmpty() || !updated_messages.m_all.isEmpty()) {
QMutexLocker lck(db_mutex);
// Something was added or updated in the DB, update numbers.
diff --git a/src/librssguard/services/abstract/serviceroot.h b/src/librssguard/services/abstract/serviceroot.h
index 054d37c12..6b3a1e7d5 100644
--- a/src/librssguard/services/abstract/serviceroot.h
+++ b/src/librssguard/services/abstract/serviceroot.h
@@ -6,7 +6,6 @@
#include "services/abstract/rootitem.h"
#include "core/message.h"
-#include "core/messagefilter.h"
#include "definitions/typedefs.h"
#include
@@ -206,7 +205,7 @@ class ServiceRoot : public RootItem {
void completelyRemoveAllData();
// Returns counts of updated messages .
- QPair updateMessages(QList& messages, Feed* feed, bool force_update, QMutex* db_mutex);
+ UpdatedArticles updateMessages(QList& messages, Feed* feed, bool force_update, QMutex* db_mutex);
QIcon feedIconForMessage(const QString& feed_custom_id) const;
diff --git a/src/librssguard/services/feedly/feedlynetwork.cpp b/src/librssguard/services/feedly/feedlynetwork.cpp
index 25a1c0dfa..901da883c 100644
--- a/src/librssguard/services/feedly/feedlynetwork.cpp
+++ b/src/librssguard/services/feedly/feedlynetwork.cpp
@@ -418,7 +418,7 @@ QList FeedlyNetwork::decodeStreamContents(const QByteArray& stream_cont
Message message;
message.m_feedId = entry_obj[QSL("origin")].toObject()[QSL("streamId")].toString();
- message.m_title = qApp->web()->stripTags(entry_obj[QSL("title")].toString());
+ message.m_title = entry_obj[QSL("title")].toString();
message.m_author = entry_obj[QSL("author")].toString();
message.m_contents = entry_obj[QSL("content")].toObject()[QSL("content")].toString();
message.m_rawContents = QJsonDocument(entry_obj).toJson(QJsonDocument::JsonFormat::Compact);
diff --git a/src/librssguard/services/greader/greadernetwork.cpp b/src/librssguard/services/greader/greadernetwork.cpp
index 27f3bece6..8c79194de 100644
--- a/src/librssguard/services/greader/greadernetwork.cpp
+++ b/src/librssguard/services/greader/greadernetwork.cpp
@@ -957,8 +957,8 @@ QList GreaderNetwork::decodeStreamContents(ServiceRoot* root,
auto message_obj = obj.toObject();
Message message;
- message.m_title = qApp->web()->unescapeHtml(message_obj[QSL("title")].toString());
- message.m_author = qApp->web()->unescapeHtml(message_obj[QSL("author")].toString());
+ message.m_title = message_obj[QSL("title")].toString();
+ message.m_author = message_obj[QSL("author")].toString();
message.m_created = QDateTime::fromSecsSinceEpoch(message_obj[QSL("published")].toInt(), Qt::TimeSpec::UTC);
message.m_createdFromFeed = true;
message.m_customId = message_obj[QSL("id")].toString();
diff --git a/src/librssguard/services/standard/parsers/feedparser.cpp b/src/librssguard/services/standard/parsers/feedparser.cpp
index b13f7784c..cdfd2f3a0 100644
--- a/src/librssguard/services/standard/parsers/feedparser.cpp
+++ b/src/librssguard/services/standard/parsers/feedparser.cpp
@@ -100,9 +100,9 @@ QList FeedParser::messages() {
Message new_message;
// Fill available data.
- new_message.m_title = qApp->web()->stripTags(qApp->web()->unescapeHtml(xmlMessageTitle(message_item)));
+ new_message.m_title = xmlMessageTitle(message_item);
new_message.m_contents = xmlMessageDescription(message_item);
- new_message.m_author = qApp->web()->stripTags(qApp->web()->unescapeHtml(xmlMessageAuthor(message_item)));
+ new_message.m_author = xmlMessageAuthor(message_item);
new_message.m_url = xmlMessageUrl(message_item);
new_message.m_created = xmlMessageDateCreated(message_item);
new_message.m_customId = xmlMessageId(message_item);
@@ -128,9 +128,9 @@ QList FeedParser::messages() {
Message new_message;
// Fill available data.
- new_message.m_title = qApp->web()->stripTags(qApp->web()->unescapeHtml(jsonMessageTitle(message_item)));
+ new_message.m_title = jsonMessageTitle(message_item);
new_message.m_contents = jsonMessageDescription(message_item);
- new_message.m_author = qApp->web()->stripTags(qApp->web()->unescapeHtml(jsonMessageAuthor(message_item)));
+ new_message.m_author = jsonMessageAuthor(message_item);
new_message.m_url = jsonMessageUrl(message_item);
new_message.m_created = jsonMessageDateCreated(message_item);
new_message.m_customId = jsonMessageId(message_item);