diff --git a/resources/rssguard.qrc b/resources/rssguard.qrc
index 258511cab..b0ba56fda 100755
--- a/resources/rssguard.qrc
+++ b/resources/rssguard.qrc
@@ -6,6 +6,11 @@
text/COPYING_GNU_GPL
text/COPYING_GNU_GPL_HTML
+ sounds/boing.wav
+ sounds/doorbell.wav
+ sounds/rooster.wav
+ sounds/sheep.wav
+
scripts/adblock/adblock-server.js
scripts/public_suffix_list.dat
diff --git a/resources/sounds/boing.wav b/resources/sounds/boing.wav
new file mode 100755
index 000000000..426fe37be
Binary files /dev/null and b/resources/sounds/boing.wav differ
diff --git a/resources/sounds/doorbell.wav b/resources/sounds/doorbell.wav
new file mode 100755
index 000000000..ec05baa00
Binary files /dev/null and b/resources/sounds/doorbell.wav differ
diff --git a/resources/sounds/rooster.wav b/resources/sounds/rooster.wav
new file mode 100755
index 000000000..f1c18546d
Binary files /dev/null and b/resources/sounds/rooster.wav differ
diff --git a/resources/sounds/sheep.wav b/resources/sounds/sheep.wav
new file mode 100755
index 000000000..e26c3f5df
Binary files /dev/null and b/resources/sounds/sheep.wav differ
diff --git a/src/librssguard/definitions/definitions.h b/src/librssguard/definitions/definitions.h
index 88ff96b8c..ba5f99034 100755
--- a/src/librssguard/definitions/definitions.h
+++ b/src/librssguard/definitions/definitions.h
@@ -45,6 +45,7 @@
#define MSG_SCORE_MAX 100.0
#define MSG_SCORE_MIN 0.0
+#define SOUNDS_BUILTIN_DIRECTORY ":/sounds"
#define ARGUMENTS_LIST_SEPARATOR "\n"
#define IS_IN_ARRAY(offset, array) ((offset >= 0) && (offset < array.count()))
#define DEFAULT_SQL_MESSAGES_FILTER "0 > 1"
diff --git a/src/librssguard/gui/newspaperpreviewer.cpp b/src/librssguard/gui/newspaperpreviewer.cpp
index 384a007e3..b20490d1c 100644
--- a/src/librssguard/gui/newspaperpreviewer.cpp
+++ b/src/librssguard/gui/newspaperpreviewer.cpp
@@ -42,13 +42,13 @@ void NewspaperPreviewer::showMoreMessages() {
prev->loadMessage(msg, m_root.data());
}
- m_ui->m_btnShowMoreMessages->setText(tr("Show more messages (%n remaining)", "", m_messages.size()));
+ m_ui->m_btnShowMoreMessages->setText(tr("Show more articles (%n remaining)", "", m_messages.size()));
m_ui->m_btnShowMoreMessages->setEnabled(!m_messages.isEmpty());
m_ui->scrollArea->verticalScrollBar()->setValue(current_scroll);
}
else {
- qApp->showGuiMessage(tr("Cannot show more messages"),
- tr("Cannot show more messages because parent feed was removed."),
+ qApp->showGuiMessage(tr("Cannot show more articles"),
+ tr("Cannot show more articles because parent feed was removed."),
QSystemTrayIcon::MessageIcon::Warning,
qApp->mainForm(),
true);
diff --git a/src/librssguard/gui/notifications/notificationseditor.cpp b/src/librssguard/gui/notifications/notificationseditor.cpp
new file mode 100755
index 000000000..5bb9a45de
--- /dev/null
+++ b/src/librssguard/gui/notifications/notificationseditor.cpp
@@ -0,0 +1,41 @@
+// For license of this file, see /LICENSE.md.
+
+#include "gui/notifications/notificationseditor.h"
+
+#include "3rd-party/boolinq/boolinq.h"
+#include "gui/notifications/singlenotificationeditor.h"
+
+#include
+
+NotificationsEditor::NotificationsEditor(QWidget* parent) : QScrollArea(parent), m_layout(new QVBoxLayout(this)) {
+ m_ui.setupUi(this);
+ setLayout(m_layout);
+}
+
+void NotificationsEditor::loadNotifications(const QList& notifications) {
+ auto all_events = Notification::allEvents();
+ auto notif = boolinq::from(notifications);
+
+ for (auto ev : all_events) {
+ if (notif.any([ev](auto n) {
+ return n.event() == ev;
+ })) {
+ auto* notif_editor = new SingleNotificationEditor(notif.first([ev](auto n) {
+ return n.event() == ev;
+ }), this);
+
+ notif_editor->setNotificationEnabled(true);
+
+ m_layout->addWidget(notif_editor);
+ }
+ else {
+ auto* notif_editor = new SingleNotificationEditor(Notification(ev, {}), this);
+
+ notif_editor->setNotificationEnabled(false);
+ m_layout->addWidget(notif_editor);
+
+ }
+ }
+
+ m_layout->addSpacerItem(new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding));
+}
diff --git a/src/librssguard/gui/notifications/notificationseditor.h b/src/librssguard/gui/notifications/notificationseditor.h
new file mode 100755
index 000000000..df7d66561
--- /dev/null
+++ b/src/librssguard/gui/notifications/notificationseditor.h
@@ -0,0 +1,27 @@
+// For license of this file, see /LICENSE.md.
+
+#ifndef NOTIFICATIONSEDITOR_H
+#define NOTIFICATIONSEDITOR_H
+
+#include
+
+#include "ui_notificationseditor.h"
+
+#include "miscellaneous/notification.h"
+
+class QVBoxLayout;
+
+class NotificationsEditor : public QScrollArea {
+ Q_OBJECT
+
+ public:
+ explicit NotificationsEditor(QWidget* parent = nullptr);
+
+ void loadNotifications(const QList& notifications);
+
+ private:
+ Ui::NotificationsEditor m_ui;
+ QVBoxLayout* m_layout;
+};
+
+#endif // NOTIFICATIONSEDITOR_H
diff --git a/src/librssguard/gui/notifications/notificationseditor.ui b/src/librssguard/gui/notifications/notificationseditor.ui
new file mode 100755
index 000000000..ebf93bf17
--- /dev/null
+++ b/src/librssguard/gui/notifications/notificationseditor.ui
@@ -0,0 +1,19 @@
+
+
+ NotificationsEditor
+
+
+
+ 0
+ 0
+ 400
+ 300
+
+
+
+ QFrame::NoFrame
+
+
+
+
+
diff --git a/src/librssguard/gui/notifications/singlenotificationeditor.cpp b/src/librssguard/gui/notifications/singlenotificationeditor.cpp
new file mode 100755
index 000000000..934fdd236
--- /dev/null
+++ b/src/librssguard/gui/notifications/singlenotificationeditor.cpp
@@ -0,0 +1,41 @@
+// For license of this file, see /LICENSE.md.
+
+#include "gui/notifications/singlenotificationeditor.h"
+
+#include "miscellaneous/application.h"
+#include "miscellaneous/iconfactory.h"
+
+SingleNotificationEditor::SingleNotificationEditor(const Notification& notification, QWidget* parent)
+ : QWidget(parent), m_notificationEvent(Notification::Event::UnknownEvent) {
+ m_ui.setupUi(this);
+
+ m_ui.m_btnBrowseSound->setIcon(qApp->icons()->fromTheme(QSL("document-open")));
+ m_ui.m_btnPlaySound->setIcon(qApp->icons()->fromTheme(QSL("media-playback-start")));
+
+ connect(m_ui.m_btnPlaySound, &QPushButton::clicked, this, &SingleNotificationEditor::playSound);
+
+ loadNotification(notification);
+}
+
+Notification SingleNotificationEditor::notification() const {
+ return Notification(m_notificationEvent, m_ui.m_txtSound->text());
+}
+
+bool SingleNotificationEditor::notificationEnabled() const {
+ return m_ui.m_gbNotification->isChecked();
+}
+
+void SingleNotificationEditor::setNotificationEnabled(bool enabled) {
+ m_ui.m_gbNotification->setChecked(enabled);
+}
+
+void SingleNotificationEditor::playSound() {
+ Notification({}, m_ui.m_txtSound->text()).playSound(qApp);
+}
+
+void SingleNotificationEditor::loadNotification(const Notification& notification) {
+ m_ui.m_txtSound->setText(notification.soundPath());
+ m_ui.m_gbNotification->setTitle(Notification::nameForEvent(notification.event()));
+
+ m_notificationEvent = notification.event();
+}
diff --git a/src/librssguard/gui/notifications/singlenotificationeditor.h b/src/librssguard/gui/notifications/singlenotificationeditor.h
new file mode 100755
index 000000000..bf9d1020f
--- /dev/null
+++ b/src/librssguard/gui/notifications/singlenotificationeditor.h
@@ -0,0 +1,34 @@
+// For license of this file, see /LICENSE.md.
+
+#ifndef SINGLENOTIFICATIONEDITOR_H
+#define SINGLENOTIFICATIONEDITOR_H
+
+#include
+
+#include "ui_singlenotificationeditor.h"
+
+#include "miscellaneous/notification.h"
+
+class SingleNotificationEditor : public QWidget {
+ Q_OBJECT
+
+ public:
+ explicit SingleNotificationEditor(const Notification& notification, QWidget* parent = nullptr);
+
+ Notification notification() const;
+
+ bool notificationEnabled() const;
+ void setNotificationEnabled(bool enabled);
+
+ private slots:
+ void playSound();
+
+ private:
+ void loadNotification(const Notification& notification);
+
+ private:
+ Ui::SingleNotificationEditor m_ui;
+ Notification::Event m_notificationEvent;
+};
+
+#endif // SINGLENOTIFICATIONEDITOR_H
diff --git a/src/librssguard/gui/notifications/singlenotificationeditor.ui b/src/librssguard/gui/notifications/singlenotificationeditor.ui
new file mode 100755
index 000000000..e04d22d0e
--- /dev/null
+++ b/src/librssguard/gui/notifications/singlenotificationeditor.ui
@@ -0,0 +1,86 @@
+
+
+ SingleNotificationEditor
+
+
+
+ 0
+ 0
+ 400
+ 125
+
+
+
+
+ 0
+ 0
+
+
+
+ Form
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ true
+
+
+
-
+
+
+ Sound
+
+
+
+ -
+
+
-
+
+
+ Full path to your WAV sound file
+
+
+
+ -
+
+
+ &Browse
+
+
+
+ -
+
+
+ &Play
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/librssguard/gui/settings/settingsnotifications.cpp b/src/librssguard/gui/settings/settingsnotifications.cpp
index 16245f7f2..3416f8cb2 100755
--- a/src/librssguard/gui/settings/settingsnotifications.cpp
+++ b/src/librssguard/gui/settings/settingsnotifications.cpp
@@ -2,19 +2,38 @@
#include "gui/settings/settingsnotifications.h"
+#include "3rd-party/boolinq/boolinq.h"
+#include "gui/guiutilities.h"
+#include "gui/notifications/notificationseditor.h"
+#include "miscellaneous/application.h"
+#include "miscellaneous/notificationfactory.h"
#include "miscellaneous/settings.h"
+#include
+
SettingsNotifications::SettingsNotifications(Settings* settings, QWidget* parent) : SettingsPanel(settings, parent) {
m_ui.setupUi(this);
+ GuiUtilities::setLabelAsNotice(*m_ui.m_lblAvailableSounds, false);
+
connect(m_ui.m_checkEnableNotifications, &QCheckBox::toggled, this, &SettingsNotifications::dirtifySettings);
}
void SettingsNotifications::loadSettings() {
onBeginLoadSettings();
+ auto builtin_sounds = QDir(SOUNDS_BUILTIN_DIRECTORY).entryInfoList(QDir::Filter::Files,
+ QDir::SortFlag::Name);
+ auto iter = boolinq::from(builtin_sounds).select([](const QFileInfo& i) {
+ return QSL(" %1").arg(i.absoluteFilePath());
+ }).toStdList();
+ auto descs = FROM_STD_LIST(QStringList, iter).join(QSL("\n"));
+
+ m_ui.m_lblAvailableSounds->setText(QSL("Built-in sounds:\n%1").arg(descs));
+
// Load fancy notification settings.
m_ui.m_checkEnableNotifications->setChecked(settings()->value(GROUP(Notifications), SETTING(Notifications::EnableNotifications)).toBool());
+ m_ui.m_editor->loadNotifications(qApp->notifications()->allNotifications());
onEndLoadSettings();
}
diff --git a/src/librssguard/gui/settings/settingsnotifications.ui b/src/librssguard/gui/settings/settingsnotifications.ui
index ebde7f9b8..5a193102e 100755
--- a/src/librssguard/gui/settings/settingsnotifications.ui
+++ b/src/librssguard/gui/settings/settingsnotifications.ui
@@ -26,28 +26,59 @@
0
- -
-
-
- Qt::Vertical
-
-
-
- 20
- 40
-
-
-
-
-
- Enable popup balloon tooltips
+ Enable notifications
+ -
+
+
+ false
+
+
+
+ 0
+ 1
+
+
+
+
+ -
+
+
+
+
+ NotificationsEditor
+ QWidget
+
+ 1
+
+
+
+ m_checkEnableNotifications
+
-
+
+
+ m_checkEnableNotifications
+ toggled(bool)
+ m_editor
+ setEnabled(bool)
+
+
+ 199
+ 8
+
+
+ 0
+ 31
+
+
+
+
diff --git a/src/librssguard/librssguard.pro b/src/librssguard/librssguard.pro
index c255b4dc9..7d0ac14a2 100644
--- a/src/librssguard/librssguard.pro
+++ b/src/librssguard/librssguard.pro
@@ -60,6 +60,8 @@ HEADERS += core/feeddownloader.h \
exceptions/ioexception.h \
exceptions/networkexception.h \
exceptions/scriptexception.h \
+ gui/notifications/notificationseditor.h \
+ gui/notifications/singlenotificationeditor.h \
gui/reusable/baselineedit.h \
gui/settings/settingsnotifications.h \
gui/toolbars/basetoolbar.h \
@@ -242,6 +244,8 @@ SOURCES += core/feeddownloader.cpp \
exceptions/ioexception.cpp \
exceptions/networkexception.cpp \
exceptions/scriptexception.cpp \
+ gui/notifications/notificationseditor.cpp \
+ gui/notifications/singlenotificationeditor.cpp \
gui/reusable/baselineedit.cpp \
gui/settings/settingsnotifications.cpp \
gui/toolbars/basetoolbar.cpp \
@@ -402,6 +406,8 @@ FORMS += gui/dialogs/formabout.ui \
gui/dialogs/formrestoredatabasesettings.ui \
gui/dialogs/formsettings.ui \
gui/dialogs/formupdate.ui \
+ gui/notifications/notificationseditor.ui \
+ gui/notifications/singlenotificationeditor.ui \
gui/reusable/networkproxydetails.ui \
gui/newspaperpreviewer.ui \
gui/reusable/searchtextwidget.ui \
@@ -494,6 +500,7 @@ INCLUDEPATH += $$PWD/. \
$$PWD/gui/dialogs \
$$PWD/gui/reusable \
$$PWD/gui/toolbars \
+ $$PWD/gui/notifications \
$$PWD/dynamic-shortcuts
TRANSLATIONS += $$files($$PWD/../../localization/rssguard_*.ts, false) \
diff --git a/src/librssguard/miscellaneous/application.cpp b/src/librssguard/miscellaneous/application.cpp
index 76a7f7fe1..724cd4055 100755
--- a/src/librssguard/miscellaneous/application.cpp
+++ b/src/librssguard/miscellaneous/application.cpp
@@ -87,7 +87,15 @@ Application::Application(const QString& id, int& argc, char** argv)
});
#endif
- m_notifications->load(settings());
+ if (isFirstRun()) {
+ m_notifications->save({
+ Notification(Notification::Event::NewArticlesFetched,
+ QSL("%1/rooster.wav").arg(SOUNDS_BUILTIN_DIRECTORY))
+ }, settings());
+ }
+ else {
+ m_notifications->load(settings());
+ }
qDebugNN << LOGSEC_CORE
<< "OpenSSL version:"
@@ -231,6 +239,11 @@ void Application::eliminateFirstRuns() {
settings()->setValue(GROUP(General), QString(General::FirstRun) + QL1C('_') + APP_VERSION, false);
}
+NotificationFactory* Application::notifications() const
+{
+ return m_notifications;
+}
+
void Application::setFeedReader(FeedReader* feed_reader) {
m_feedReader = feed_reader;
connect(m_feedReader, &FeedReader::feedUpdatesFinished, this, &Application::onFeedUpdatesFinished);
diff --git a/src/librssguard/miscellaneous/application.h b/src/librssguard/miscellaneous/application.h
index 958e4cf7d..4832d78f8 100755
--- a/src/librssguard/miscellaneous/application.h
+++ b/src/librssguard/miscellaneous/application.h
@@ -76,6 +76,7 @@ class RSSGUARD_DLLSPEC Application : public SingleApplication {
FormMain* mainForm();
QWidget* mainFormWidget();
SystemTrayIcon* trayIcon();
+ NotificationFactory* notifications() const;
QIcon desktopAwareIcon() const;
diff --git a/src/librssguard/miscellaneous/notification.cpp b/src/librssguard/miscellaneous/notification.cpp
index 89bf43b71..b9a80877b 100755
--- a/src/librssguard/miscellaneous/notification.cpp
+++ b/src/librssguard/miscellaneous/notification.cpp
@@ -30,3 +30,27 @@ void Notification::setSoundPath(const QString& sound_path) {
void Notification::playSound(Application* app) const {
QSound::play(QDir::toNativeSeparators(app->replaceDataUserDataFolderPlaceholder(m_soundPath)));
}
+
+QList Notification::allEvents() {
+ return {
+ Event::NewArticlesFetched,
+ Event::ArticlesFetchingStarted,
+ Event::LoginDataRefreshed
+ };
+}
+
+QString Notification::nameForEvent(Notification::Event event) {
+ switch (event) {
+ case Notification::Event::NewArticlesFetched:
+ return QObject::tr("New articles fetched");
+
+ case Notification::Event::ArticlesFetchingStarted:
+ return QObject::tr("Fetching articles right now");
+
+ case Notification::Event::LoginDataRefreshed:
+ return QObject::tr("Login data refreshed");
+
+ default:
+ return QObject::tr("Unknown event");
+ }
+}
diff --git a/src/librssguard/miscellaneous/notification.h b/src/librssguard/miscellaneous/notification.h
index 6d2436c39..51c46972d 100755
--- a/src/librssguard/miscellaneous/notification.h
+++ b/src/librssguard/miscellaneous/notification.h
@@ -10,16 +10,20 @@ class Application;
class Notification {
public:
enum class Event {
+ UnknownEvent = 0,
+
// New (unread) messages were downloaded for some feed.
- NewMessagesDownloaded = 1,
+ NewArticlesFetched = 1,
// RSS Guard started downloading messages for some feed.
- MesssagesDownloadStarted = 2,
+ ArticlesFetchingStarted = 2,
// Login tokens were successfuly refreshed.
// NOTE: This is primarily used in accounts which use
// OAuth or similar mechanism.
- LoginDataRefreshed = 4
+ LoginDataRefreshed = 4,
+
+ // TODO: app update is available
};
explicit Notification();
@@ -36,6 +40,9 @@ class Notification {
void playSound(Application* app) const;
+ static QList allEvents();
+ static QString nameForEvent(Event event);
+
private:
Event m_event;
QString m_soundPath;
diff --git a/src/librssguard/miscellaneous/notificationfactory.cpp b/src/librssguard/miscellaneous/notificationfactory.cpp
index 2de3356f6..54c1171b8 100755
--- a/src/librssguard/miscellaneous/notificationfactory.cpp
+++ b/src/librssguard/miscellaneous/notificationfactory.cpp
@@ -7,8 +7,14 @@
#include "exceptions/applicationexception.h"
#include "miscellaneous/settings.h"
+#include
+
NotificationFactory::NotificationFactory(QObject* parent) : QObject(parent) {}
+QList NotificationFactory::allNotifications() const {
+ return m_notifications;
+}
+
Notification NotificationFactory::notificationForEvent(Notification::Event event) const {
auto good_n = boolinq::from(m_notifications).where([event](const Notification& n) {
return n.event() == event;
@@ -23,15 +29,23 @@ Notification NotificationFactory::notificationForEvent(Notification::Event event
}
void NotificationFactory::load(Settings* settings) {
- //settings->allKeys(Notifications::ID)
+ auto notif_keys = settings->allKeys(GROUP(Notifications)).filter(QRegularExpression(QSL("^\\d+$")));
- m_notifications = {
- Notification()
- };
+ m_notifications.clear();
+
+ for (const auto& key : notif_keys) {
+ auto event = Notification::Event(key.toInt());
+ auto sound = settings->value(GROUP(Notifications), key).toString();
+
+ m_notifications.append(Notification(event, sound));
+ }
}
void NotificationFactory::save(const QList& new_notifications, Settings* settings) {
+ settings->remove(GROUP(Notifications));
m_notifications = new_notifications;
- //
+ for (const auto& n : qAsConst(m_notifications)) {
+ settings->setValue(GROUP(Notifications), QString::number(int(n.event())), n.soundPath());
+ }
}
diff --git a/src/librssguard/miscellaneous/notificationfactory.h b/src/librssguard/miscellaneous/notificationfactory.h
index 96fc76547..719ed61fd 100755
--- a/src/librssguard/miscellaneous/notificationfactory.h
+++ b/src/librssguard/miscellaneous/notificationfactory.h
@@ -15,6 +15,7 @@ class NotificationFactory : public QObject {
public:
explicit NotificationFactory(QObject* parent = nullptr);
+ QList allNotifications() const;
Notification notificationForEvent(Notification::Event event) const;
public slots:
diff --git a/src/librssguard/miscellaneous/settings.h b/src/librssguard/miscellaneous/settings.h
index 30e644adf..02e6e4e7b 100644
--- a/src/librssguard/miscellaneous/settings.h
+++ b/src/librssguard/miscellaneous/settings.h
@@ -432,7 +432,7 @@ class Settings : public QSettings {
void setValue(const QString& key, const QVariant& value);
bool contains(const QString& section, const QString& key) const;
- void remove(const QString& section, const QString& key);
+ void remove(const QString& section, const QString& key = {});
// Returns the path which contains the settings.
QString pathName() const;
@@ -487,7 +487,14 @@ inline bool Settings::contains(const QString& section, const QString& key) const
}
inline void Settings::remove(const QString& section, const QString& key) {
- QSettings::remove(QString(QSL("%1/%2")).arg(section, key));
+ if (key.isEmpty()) {
+ beginGroup(section);
+ QSettings::remove({});
+ endGroup();
+ }
+ else {
+ QSettings::remove(QString(QSL("%1/%2")).arg(section, key));
+ }
}
#endif // SETTINGS_H