/* * Copyright 2019 Kai Uwe Broulik * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "settings.h" #include #include #include #include "server.h" #include "mirroredscreenstracker_p.h" #include "debug.h" // Settings #include "donotdisturbsettings.h" #include "notificationsettings.h" #include "jobsettings.h" #include "badgesettings.h" using namespace NotificationManager; class Q_DECL_HIDDEN Settings::Private { public: explicit Private(Settings *q); ~Private(); void setDirty(bool dirty); Settings::NotificationBehaviors groupBehavior(const KConfigGroup &group) const; void setGroupBehavior(KConfigGroup &group, const Settings::NotificationBehaviors &behavior); KConfigGroup servicesGroup() const; KConfigGroup applicationsGroup() const; QStringList behaviorMatchesList(const KConfigGroup &group, Settings::NotificationBehavior behavior, bool on) const; Settings *q; KSharedConfig::Ptr config; KConfigWatcher::Ptr watcher; QMetaObject::Connection watcherConnection; MirroredScreensTracker::Ptr mirroredScreensTracker; bool live = false; // set to true initially in constructor bool dirty = false; }; Settings::Private::Private(Settings *q) : q(q) { } Settings::Private::~Private() = default; void Settings::Private::setDirty(bool dirty) { if (this->dirty != dirty) { this->dirty = dirty; emit q->dirtyChanged(); } } Settings::NotificationBehaviors Settings::Private::groupBehavior(const KConfigGroup &group) const { Settings::NotificationBehaviors behaviors; behaviors.setFlag(Settings::ShowPopups, group.readEntry("ShowPopups", true)); // show popups in dnd mode implies the show popups behaviors.setFlag(Settings::ShowPopupsInDoNotDisturbMode, behaviors.testFlag(Settings::ShowPopups) && group.readEntry("ShowPopupsInDndMode", false)); behaviors.setFlag(Settings::ShowInHistory, group.readEntry("ShowInHistory", true)); behaviors.setFlag(Settings::ShowBadges, group.readEntry("ShowBadges", true)); return behaviors; } void Settings::Private::setGroupBehavior(KConfigGroup &group, const Settings::NotificationBehaviors &behavior) { if (groupBehavior(group) == behavior) { return; } const bool showPopups = behavior.testFlag(Settings::ShowPopups); if (showPopups && !group.hasDefault("ShowPopups")) { group.revertToDefault("ShowPopups", KConfigBase::Notify); } else { group.writeEntry("ShowPopups", showPopups, KConfigBase::Notify); } const bool showPopupsInDndMode = behavior.testFlag(Settings::ShowPopupsInDoNotDisturbMode); if (!showPopupsInDndMode && !group.hasDefault("ShowPopupsInDndMode")) { group.revertToDefault("ShowPopupsInDndMode", KConfigBase::Notify); } else { group.writeEntry("ShowPopupsInDndMode", showPopupsInDndMode, KConfigBase::Notify); } const bool showInHistory = behavior.testFlag(Settings::ShowInHistory); if (showInHistory && !group.hasDefault("ShowInHistory")) { group.revertToDefault("ShowInHistory", KConfig::Notify); } else { group.writeEntry("ShowInHistory", showInHistory, KConfigBase::Notify); } const bool showBadges = behavior.testFlag(Settings::ShowBadges); if (showBadges && !group.hasDefault("ShowBadges")) { group.revertToDefault("ShowBadges", KConfigBase::Notify); } else { group.writeEntry("ShowBadges", showBadges, KConfigBase::Notify); } setDirty(true); } KConfigGroup Settings::Private::servicesGroup() const { return config->group("Services"); } KConfigGroup Settings::Private::applicationsGroup() const { return config->group("Applications"); } QStringList Settings::Private::behaviorMatchesList(const KConfigGroup &group, Settings::NotificationBehavior behavior, bool on) const { QStringList matches; const QStringList apps = group.groupList(); for (const QString &app : apps) { if (groupBehavior(group.group(app)).testFlag(behavior) == on) { matches.append(app); } } return matches; } Settings::Settings(QObject *parent) // FIXME static thing for config file name : Settings(KSharedConfig::openConfig(QStringLiteral("plasmanotifyrc")), parent) { } Settings::Settings(const KSharedConfig::Ptr &config, QObject *parent) : QObject(parent) , d(new Private(this)) { d->config = config; static bool s_settingsInited = false; if (!s_settingsInited) { DoNotDisturbSettings::instance(config); NotificationSettings::instance(config); JobSettings::instance(config); BadgeSettings::instance(config); s_settingsInited = true; } setLive(true); connect(&Server::self(), &Server::inhibitedByApplicationChanged, this, &Settings::notificationsInhibitedByApplicationChanged); connect(&Server::self(), &Server::inhibitionApplicationsChanged, this, &Settings::notificationInhibitionApplicationsChanged); if (DoNotDisturbSettings::whenScreensMirrored()) { d->mirroredScreensTracker = MirroredScreensTracker::createTracker(); connect(d->mirroredScreensTracker.data(), &MirroredScreensTracker::screensMirroredChanged, this, &Settings::screensMirroredChanged); } } Settings::~Settings() { d->config->markAsClean(); } Settings::NotificationBehaviors Settings::applicationBehavior(const QString &desktopEntry) const { return d->groupBehavior(d->applicationsGroup().group(desktopEntry)); } void Settings::setApplicationBehavior(const QString &desktopEntry, NotificationBehaviors behaviors) { KConfigGroup group(d->applicationsGroup().group(desktopEntry)); d->setGroupBehavior(group, behaviors); } Settings::NotificationBehaviors Settings::serviceBehavior(const QString ¬ifyRcName) const { return d->groupBehavior(d->servicesGroup().group(notifyRcName)); } void Settings::setServiceBehavior(const QString ¬ifyRcName, NotificationBehaviors behaviors) { KConfigGroup group(d->servicesGroup().group(notifyRcName)); d->setGroupBehavior(group, behaviors); } void Settings::registerKnownApplication(const QString &desktopEntry) { KService::Ptr service = KService::serviceByDesktopName(desktopEntry); if (!service) { qCDebug(NOTIFICATIONMANAGER) << "Application" << desktopEntry << "cannot be registered as seen application since there is no service for it"; return; } if (service->noDisplay()) { qCDebug(NOTIFICATIONMANAGER) << "Application" << desktopEntry << "will not be registered as seen application since it's marked as NoDisplay"; return; } if (knownApplications().contains(desktopEntry)) { return; } d->applicationsGroup().group(desktopEntry).writeEntry("Seen", true); emit knownApplicationsChanged(); } void Settings::forgetKnownApplication(const QString &desktopEntry) { if (!knownApplications().contains(desktopEntry)) { return; } // Only remove applications that were added through registerKnownApplication if (!d->applicationsGroup().group(desktopEntry).readEntry("Seen", false)) { qCDebug(NOTIFICATIONMANAGER) << "Application" << desktopEntry << "will not be removed from seen applications since it wasn't one."; return; } d->applicationsGroup().deleteGroup(desktopEntry); emit knownApplicationsChanged(); } void Settings::load() { d->config->markAsClean(); d->config->reparseConfiguration(); DoNotDisturbSettings::self()->load(); NotificationSettings::self()->load(); JobSettings::self()->load(); BadgeSettings::self()->load(); emit settingsChanged(); d->setDirty(false); } void Settings::save() { DoNotDisturbSettings::self()->save(); NotificationSettings::self()->save(); JobSettings::self()->save(); BadgeSettings::self()->save(); d->config->sync(); d->setDirty(false); } void Settings::defaults() { DoNotDisturbSettings::self()->setDefaults(); NotificationSettings::self()->setDefaults(); JobSettings::self()->setDefaults(); BadgeSettings::self()->setDefaults(); } bool Settings::live() const { return d->live; } void Settings::setLive(bool live) { if (live == d->live) { return; } d->live = live; if (live) { d->watcher = KConfigWatcher::create(d->config); d->watcherConnection = connect(d->watcher.data(), &KConfigWatcher::configChanged, this, [this](const KConfigGroup &group, const QByteArrayList &names) { Q_UNUSED(names); if (group.name() == QLatin1String("DoNotDisturb")) { DoNotDisturbSettings::self()->load(); bool emitScreensMirroredChanged = false; if (DoNotDisturbSettings::whenScreensMirrored()) { if (!d->mirroredScreensTracker) { d->mirroredScreensTracker = MirroredScreensTracker::createTracker(); emitScreensMirroredChanged = d->mirroredScreensTracker->screensMirrored(); connect(d->mirroredScreensTracker.data(), &MirroredScreensTracker::screensMirroredChanged, this, &Settings::screensMirroredChanged); } } else if (d->mirroredScreensTracker) { emitScreensMirroredChanged = d->mirroredScreensTracker->screensMirrored(); d->mirroredScreensTracker.reset(); } if (emitScreensMirroredChanged) { emit screensMirroredChanged(); } } else if (group.name() == QLatin1String("Notifications")) { NotificationSettings::self()->load(); } else if (group.name() == QLatin1String("Jobs")) { JobSettings::self()->load(); } else if (group.name() == QLatin1String("Badges")) { BadgeSettings::self()->load(); } emit settingsChanged(); }); } else { disconnect(d->watcherConnection); d->watcherConnection = QMetaObject::Connection(); d->watcher.reset(); } emit liveChanged(); } bool Settings::dirty() const { // KConfigSkeleton doesn't write into the KConfig until calling save() // so we need to track d->config->isDirty() manually return d->dirty; } bool Settings::keepCriticalAlwaysOnTop() const { return NotificationSettings::criticalAlwaysOnTop(); } void Settings::setKeepCriticalAlwaysOnTop(bool enable) { if (this->keepCriticalAlwaysOnTop() == enable) { return; } NotificationSettings::setCriticalAlwaysOnTop(enable); d->setDirty(true); } bool Settings::criticalPopupsInDoNotDisturbMode() const { return NotificationSettings::criticalInDndMode(); } void Settings::setCriticalPopupsInDoNotDisturbMode(bool enable) { if (this->criticalPopupsInDoNotDisturbMode() == enable) { return; } NotificationSettings::setCriticalInDndMode(enable); d->setDirty(true); } bool Settings::lowPriorityPopups() const { return NotificationSettings::lowPriorityPopups(); } void Settings::setLowPriorityPopups(bool enable) { if (this->lowPriorityPopups() == enable) { return; } NotificationSettings::setLowPriorityPopups(enable); d->setDirty(true); } bool Settings::lowPriorityHistory() const { return NotificationSettings::lowPriorityHistory(); } void Settings::setLowPriorityHistory(bool enable) { if (this->lowPriorityHistory() == enable) { return; } NotificationSettings::setLowPriorityHistory(enable); d->setDirty(true); } Settings::PopupPosition Settings::popupPosition() const { return NotificationSettings::popupPosition(); } void Settings::setPopupPosition(Settings::PopupPosition position) { if (this->popupPosition() == position) { return; } NotificationSettings::setPopupPosition(position); d->setDirty(true); } int Settings::popupTimeout() const { return NotificationSettings::popupTimeout(); } void Settings::setPopupTimeout(int timeout) { if (this->popupTimeout() == timeout) { return; } NotificationSettings::setPopupTimeout(timeout); d->setDirty(true); } void Settings::resetPopupTimeout() { setPopupTimeout(NotificationSettings::defaultPopupTimeoutValue()); } bool Settings::jobsInTaskManager() const { return JobSettings::inTaskManager(); } void Settings::setJobsInTaskManager(bool enable) { if (jobsInTaskManager() == enable) { return; } JobSettings::setInTaskManager(enable); d->setDirty(true); } bool Settings::jobsInNotifications() const { return JobSettings::inNotifications(); } void Settings::setJobsInNotifications(bool enable) { if (jobsInNotifications() == enable) { return; } JobSettings::setInNotifications(enable); d->setDirty(true); } bool Settings::permanentJobPopups() const { return JobSettings::permanentPopups(); } void Settings::setPermanentJobPopups(bool enable) { if (permanentJobPopups() == enable) { return; } JobSettings::setPermanentPopups(enable); d->setDirty(true); } bool Settings::badgesInTaskManager() const { return BadgeSettings::inTaskManager(); } void Settings::setBadgesInTaskManager(bool enable) { if (badgesInTaskManager() == enable) { return; } BadgeSettings::setInTaskManager(enable); d->setDirty(true); } QStringList Settings::knownApplications() const { return d->applicationsGroup().groupList(); } QStringList Settings::popupBlacklistedApplications() const { return d->behaviorMatchesList(d->applicationsGroup(), ShowPopups, false); } QStringList Settings::popupBlacklistedServices() const { return d->behaviorMatchesList(d->servicesGroup(), ShowPopups, false); } QStringList Settings::doNotDisturbPopupWhitelistedApplications() const { return d->behaviorMatchesList(d->applicationsGroup(), ShowPopupsInDoNotDisturbMode, true); } QStringList Settings::doNotDisturbPopupWhitelistedServices() const { return d->behaviorMatchesList(d->servicesGroup(), ShowPopupsInDoNotDisturbMode, true); } QStringList Settings::historyBlacklistedApplications() const { return d->behaviorMatchesList(d->applicationsGroup(), ShowInHistory, false); } QStringList Settings::historyBlacklistedServices() const { return d->behaviorMatchesList(d->servicesGroup(), ShowInHistory, false); } QStringList Settings::badgeBlacklistedApplications() const { return d->behaviorMatchesList(d->applicationsGroup(), ShowBadges, false); } QDateTime Settings::notificationsInhibitedUntil() const { return DoNotDisturbSettings::until(); } void Settings::setNotificationsInhibitedUntil(const QDateTime &time) { DoNotDisturbSettings::setUntil(time); d->setDirty(true); } void Settings::resetNotificationsInhibitedUntil() { setNotificationsInhibitedUntil(QDateTime());// FIXME DoNotDisturbSettings::defaultUntilValue()); } bool Settings::notificationsInhibitedByApplication() const { return Server::self().inhibitedByApplication(); } QStringList Settings::notificationInhibitionApplications() const { return Server::self().inhibitionApplications(); } QStringList Settings::notificationInhibitionReasons() const { return Server::self().inhibitionReasons(); } bool Settings::inhibitNotificationsWhenScreensMirrored() const { return DoNotDisturbSettings::whenScreensMirrored(); } void Settings::setInhibitNotificationsWhenScreensMirrored(bool inhibit) { if (inhibit == inhibitNotificationsWhenScreensMirrored()) { return; } DoNotDisturbSettings::setWhenScreensMirrored(inhibit); d->setDirty(true); } bool Settings::screensMirrored() const { return d->mirroredScreensTracker && d->mirroredScreensTracker->screensMirrored(); } void Settings::setScreensMirrored(bool mirrored) { if (mirrored) { qCWarning(NOTIFICATIONMANAGER) << "Cannot forcefully set screens mirrored"; return; } if (d->mirroredScreensTracker) { d->mirroredScreensTracker->setScreensMirrored(mirrored); } } void Settings::revokeApplicationInhibitions() { Server::self().clearInhibitions(); } bool Settings::notificationSoundsInhibited() const { return DoNotDisturbSettings::notificationSoundsMuted(); } void Settings::setNotificationSoundsInhibited(bool inhibited) { if (inhibited == notificationSoundsInhibited()) { return; } DoNotDisturbSettings::setNotificationSoundsMuted(inhibited); d->setDirty(true); }