File indexing completed on 2024-04-28 04:44:14

0001 /*
0002     SnoreNotify is a Notification Framework based on Qt
0003     Copyright (C) 2014-2015  Hannah von Reth <vonreth@kde.org>
0004 
0005     SnoreNotify is free software: you can redistribute it and/or modify
0006     it under the terms of the GNU Lesser General Public License as published by
0007     the Free Software Foundation, either version 3 of the License, or
0008     (at your option) any later version.
0009 
0010     SnoreNotify is distributed in the hope that it will be useful,
0011     but WITHOUT ANY WARRANTY; without even the implied warranty of
0012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013     GNU Lesser General Public License for more details.
0014 
0015     You should have received a copy of the GNU Lesser General Public License
0016     along with SnoreNotify.  If not, see <http://www.gnu.org/licenses/>.
0017 */
0018 
0019 #include "snore_p.h"
0020 #include "snore.h"
0021 #include "snoreconstants.h"
0022 #include "plugins/plugins.h"
0023 #include "plugins/snorebackend.h"
0024 #include "plugins/snorefrontend.h"
0025 #include "plugins/plugincontainer.h"
0026 #include "notification/notification_p.h"
0027 #include "version.h"
0028 
0029 #include <QGuiApplication>
0030 #include <QSettings>
0031 #include <QTemporaryDir>
0032 
0033 using namespace Snore;
0034 
0035 #if QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)
0036 Q_LOGGING_CATEGORY(SNORE, "libsnorenotify", QtWarningMsg)
0037 #else
0038 Q_LOGGING_CATEGORY(SNORE, "libsnorenotify")
0039 #endif
0040 
0041 SnoreCorePrivate::SnoreCorePrivate():
0042     m_localSettingsPrefix(qApp->applicationName().isEmpty() ? QStringLiteral("SnoreNotify") : qApp->applicationName())
0043 {
0044     if (qEnvironmentVariableIsSet("LIBSNORE_SETTINGS_FILE")) {
0045         m_settings =  new QSettings(QString::fromUtf8(qgetenv("LIBSNORE_SETTINGS_FILE")), QSettings::IniFormat);
0046     } else {
0047         m_settings = new QSettings(QStringLiteral("Snorenotify"), QStringLiteral("libsnore"), this);
0048     }
0049     qCDebug(SNORE) << "Version:" << Version::version();
0050     if (!Version::revision().isEmpty()) {
0051         qCDebug(SNORE) << "Revision:" << Version::revision();
0052     }
0053 
0054     qCDebug(SNORE) << "Temp dir is" << tempPath();
0055     qCDebug(SNORE) << "Snore settings are located in" << m_settings->fileName();
0056     qCDebug(SNORE) << "Snore local settings are located in" << normalizeSettingsKey(QStringLiteral("Test"), LocalSetting);
0057 
0058     connect(qApp, &QCoreApplication::aboutToQuit, this, &SnoreCorePrivate::slotAboutToQuit);
0059 }
0060 
0061 SnoreCorePrivate::~SnoreCorePrivate()
0062 {
0063 
0064 }
0065 
0066 Application SnoreCorePrivate::defaultApplication()
0067 {
0068     if (!SnoreCore::instance().aplications().contains(m_defaultApp.key())) {
0069         SnoreCore::instance().registerApplication(m_defaultApp);
0070     }
0071     return m_defaultApp;
0072 }
0073 
0074 void SnoreCorePrivate::slotNotificationActionInvoked(Notification notification)
0075 {
0076     Q_Q(SnoreCore);
0077     emit q->actionInvoked(notification);
0078 }
0079 
0080 void SnoreCorePrivate::slotNotificationDisplayed(Notification notification)
0081 {
0082     emit notificationDisplayed(notification);
0083     startNotificationTimeoutTimer(notification);
0084 }
0085 
0086 bool SnoreCorePrivate::setBackendIfAvailible(const QString &backend)
0087 {
0088     Q_Q(SnoreCore);
0089     if (m_pluginNames[SnorePlugin::Backend].contains(backend)) {
0090         if (backend == q->primaryNotificationBackend()) {
0091             return true;
0092         }
0093         const QHash<QString, PluginContainer *> backends = PluginContainer::pluginCache(SnorePlugin::Backend);
0094         if (!backends.contains(backend)) {
0095             qCDebug(SNORE) << "Unknown Backend:" << backend;
0096             return false;
0097         }
0098         qCDebug(SNORE) << "Setting Notification Backend to:" << backend;
0099         SnoreBackend *b = qobject_cast<SnoreBackend *>(backends.value(backend)->load());
0100         if (!b->isReady()) {
0101             qCDebug(SNORE) << "Backend not ready:" << b->errorString();
0102 
0103             emit q->primaryNotificationBackendError(b->errorString());
0104             return false;
0105         }
0106         if (m_notificationBackend) {
0107             m_notificationBackend->disable();
0108         }
0109         m_notificationBackend = b;
0110         m_notificationBackend->enable();
0111         q->setSettingsValue(Constants::SettingsKeys::PrimaryBackend, backend);
0112 
0113         connect(b, &SnoreBackend::error, [this, b](const QString &) {
0114             slotInitPrimaryNotificationBackend();
0115         });
0116         emit q->primaryNotificationBackendChanged(b->name());
0117         return true;
0118     }
0119     return false;
0120 }
0121 
0122 bool SnoreCorePrivate::slotInitPrimaryNotificationBackend()
0123 {
0124     Q_Q(SnoreCore);
0125     qCDebug(SNORE) << q->settingsValue(Constants::SettingsKeys::PrimaryBackend).toString();
0126     if (setBackendIfAvailible(q->settingsValue(Constants::SettingsKeys::PrimaryBackend).toString())) {
0127         return true;
0128     }
0129 #ifdef Q_OS_WIN
0130     if (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS8 && setBackendIfAvailible(QLatin1String("Windows Toast"))) {
0131         return true;
0132     }
0133     if (setBackendIfAvailible(QStringLiteral("Growl"))) {
0134         return true;
0135     }
0136     if (setBackendIfAvailible(QStringLiteral("Snarl"))) {
0137         return true;
0138     }
0139 #elif defined(Q_OS_LINUX)
0140     if (setBackendIfAvailible(QStringLiteral("Freedesktop"))) {
0141         return true;
0142     }
0143 #elif defined(Q_OS_MAC)
0144     if (setBackendIfAvailible(QStringLiteral("OSX Notification Center"))) {
0145         return true;
0146     }
0147     if (setBackendIfAvailible(QStringLiteral("Growl"))) {
0148         return true;
0149     }
0150 #endif
0151     if (setBackendIfAvailible(QStringLiteral("Snore"))) {
0152         return true;
0153     }
0154     return false;
0155 }
0156 
0157 void SnoreCorePrivate::init()
0158 {
0159     setDefaultSettingsValueIntern(QStringLiteral("Timeout"), 10);
0160     setDefaultSettingsValueIntern(QStringLiteral("Silent"), false);
0161     m_defaultApp = Application(QStringLiteral("SnoreNotify"), Icon::defaultIcon());
0162 }
0163 
0164 void SnoreCorePrivate::setDefaultSettingsValueIntern(const QString &key, const QVariant &value)
0165 {
0166     QString nk = normalizeSettingsKey(key + QLatin1String("-SnoreDefault"), LocalSetting);
0167     if (!m_settings->contains(nk)) {
0168         m_settings->setValue(nk, value);
0169     }
0170 }
0171 
0172 void SnoreCorePrivate::syncSettings()
0173 {
0174     Q_Q(SnoreCore);
0175     QString newBackend = q->settingsValue(Constants::SettingsKeys::PrimaryBackend).toString();
0176     if (!newBackend.isEmpty()) {
0177         QString oldBackend;
0178         if (m_notificationBackend) {
0179             oldBackend = m_notificationBackend->name();
0180             m_notificationBackend->disable();
0181             m_notificationBackend = nullptr;
0182         }
0183         if (!setBackendIfAvailible(newBackend)) {
0184             qCWarning(SNORE) << "Failed to set new backend" << q->settingsValue(Constants::SettingsKeys::PrimaryBackend).toString() << "restoring" << oldBackend;
0185             setBackendIfAvailible(oldBackend);
0186         }
0187     }
0188 
0189     auto types = SnorePlugin::types();
0190     types.removeOne(SnorePlugin::Backend);
0191     foreach(auto type, types) {
0192         foreach(auto & pluginName, m_pluginNames[type]) {
0193             auto key = qMakePair(type, pluginName);
0194             SnorePlugin *plugin = m_plugins.value(key);
0195             bool enable = m_plugins[key]->settingsValue(Constants::SettingsKeys::Enabled).toBool();
0196             plugin->setEnabled(enable);
0197         }
0198     }
0199 }
0200 
0201 QSettings &SnoreCorePrivate::settings()
0202 {
0203     return *m_settings;
0204 }
0205 
0206 void SnoreCorePrivate::setLocalSttingsPrefix(const QString &prefix)
0207 {
0208     m_localSettingsPrefix = prefix;
0209     init();
0210     syncSettings();
0211 }
0212 
0213 QString SnoreCorePrivate::tempPath()
0214 {
0215 #if 1
0216     static QTemporaryDir dir;
0217     return dir.path();
0218 #else
0219     return QDir::temp().path() + QLatin1String("/libsnore/");
0220 #endif
0221 }
0222 
0223 int SnoreCorePrivate::maxNumberOfActiveNotifications() const
0224 {
0225     return m_notificationBackend ? m_notificationBackend->maxNumberOfActiveNotifications() : 3;
0226 }
0227 
0228 // TODO: this is somehow horrible code
0229 SnoreCorePrivate *SnoreCorePrivate::instance()
0230 {
0231     return SnoreCore::instance().d_ptr;
0232 }
0233 
0234 bool SnoreCorePrivate::primaryBackendCanUpdateNotification() const
0235 {
0236     return m_notificationBackend->canUpdateNotification();
0237 }
0238 
0239 void SnoreCorePrivate::slotNotificationClosed(Notification n)
0240 {
0241     Q_Q(SnoreCore);
0242     emit q->notificationClosed(n);
0243     if (!n.removeActiveIn(q)) {
0244         qCWarning(SNORE) << n << "was already closed";
0245     }
0246     if (!m_notificationQue.isEmpty() && m_activeNotifications.size() < maxNumberOfActiveNotifications()) {
0247         qCDebug(SNORE) << "Broadcast from queue" << m_notificationQue.size();
0248         q->broadcastNotification(m_notificationQue.takeFirst());
0249     }
0250 }
0251 
0252 void SnoreCorePrivate::slotAboutToQuit()
0253 {
0254     for (PluginContainer *p : PluginContainer::pluginCache(SnorePlugin::All)) {
0255         if (p->isLoaded()) {
0256             qCDebug(SNORE) << "deinitialize" << p->name();
0257             p->load()->disable();
0258         }
0259     }
0260 }
0261 
0262 void SnoreCorePrivate::startNotificationTimeoutTimer(Notification notification)
0263 {
0264     Q_Q(SnoreCore);
0265     if (notification.isSticky()) {
0266         return;
0267     }
0268 
0269     notification.data()->stopTimeoutTimer();
0270     QTimer *timer = new QTimer();
0271     notification.data()->m_timeoutTimer = timer;
0272     timer->setSingleShot(true);
0273 
0274     if (notification.isUpdate()) {
0275         notification.old().data()->stopTimeoutTimer();
0276     }
0277     timer->setInterval(notification.timeout() * 1000);
0278     connect(timer, &QTimer::timeout, [q, notification]() {
0279         qCDebug(SNORE) << notification;
0280         q->requestCloseNotification(notification, Notification::TimedOut);
0281     });
0282     timer->start();
0283 }