File indexing completed on 2024-05-12 13:41:39
0001 /* 0002 SPDX-FileCopyrightText: 2019 Kai Uwe Broulik <kde@privat.broulik.de> 0003 0004 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0005 */ 0006 0007 #include "settings.h" 0008 0009 #include <QDebug> 0010 0011 #include <KConfigWatcher> 0012 #include <KService> 0013 0014 #include "debug.h" 0015 #include "mirroredscreenstracker_p.h" 0016 #include "server.h" 0017 0018 // Settings 0019 #include "badgesettings.h" 0020 #include "donotdisturbsettings.h" 0021 #include "jobsettings.h" 0022 #include "notificationsettings.h" 0023 0024 namespace NotificationManager 0025 { 0026 constexpr const char s_configFile[] = "plasmanotifyrc"; 0027 } 0028 0029 using namespace NotificationManager; 0030 0031 class Q_DECL_HIDDEN Settings::Private 0032 { 0033 public: 0034 explicit Private(Settings *q); 0035 ~Private(); 0036 0037 void setDirty(bool dirty); 0038 0039 Settings::NotificationBehaviors groupBehavior(const KConfigGroup &group) const; 0040 void setGroupBehavior(KConfigGroup &group, const Settings::NotificationBehaviors &behavior); 0041 0042 KConfigGroup servicesGroup() const; 0043 KConfigGroup applicationsGroup() const; 0044 0045 QStringList behaviorMatchesList(const KConfigGroup &group, Settings::NotificationBehavior behavior, bool on) const; 0046 0047 Settings *q; 0048 0049 KSharedConfig::Ptr config; 0050 0051 KConfigWatcher::Ptr watcher; 0052 QMetaObject::Connection watcherConnection; 0053 0054 MirroredScreensTracker::Ptr mirroredScreensTracker; 0055 0056 DoNotDisturbSettings dndSettings; 0057 NotificationSettings notificationSettings; 0058 JobSettings jobSettings; 0059 BadgeSettings badgeSettings; 0060 0061 bool live = false; // set to true initially in constructor 0062 bool dirty = false; 0063 }; 0064 0065 Settings::Private::Private(Settings *q) 0066 : q(q) 0067 { 0068 } 0069 0070 Settings::Private::~Private() = default; 0071 0072 void Settings::Private::setDirty(bool dirty) 0073 { 0074 if (this->dirty != dirty) { 0075 this->dirty = dirty; 0076 Q_EMIT q->dirtyChanged(); 0077 } 0078 } 0079 0080 Settings::NotificationBehaviors Settings::Private::groupBehavior(const KConfigGroup &group) const 0081 { 0082 Settings::NotificationBehaviors behaviors; 0083 behaviors.setFlag(Settings::ShowPopups, group.readEntry("ShowPopups", true)); 0084 // show popups in dnd mode implies the show popups 0085 behaviors.setFlag(Settings::ShowPopupsInDoNotDisturbMode, behaviors.testFlag(Settings::ShowPopups) && group.readEntry("ShowPopupsInDndMode", false)); 0086 behaviors.setFlag(Settings::ShowInHistory, group.readEntry("ShowInHistory", true)); 0087 behaviors.setFlag(Settings::ShowBadges, group.readEntry("ShowBadges", true)); 0088 return behaviors; 0089 } 0090 0091 void Settings::Private::setGroupBehavior(KConfigGroup &group, const Settings::NotificationBehaviors &behavior) 0092 { 0093 if (groupBehavior(group) == behavior) { 0094 return; 0095 } 0096 0097 const bool showPopups = behavior.testFlag(Settings::ShowPopups); 0098 if (showPopups && !group.hasDefault("ShowPopups")) { 0099 group.revertToDefault("ShowPopups", KConfigBase::Notify); 0100 } else { 0101 group.writeEntry("ShowPopups", showPopups, KConfigBase::Notify); 0102 } 0103 0104 const bool showPopupsInDndMode = behavior.testFlag(Settings::ShowPopupsInDoNotDisturbMode); 0105 if (!showPopupsInDndMode && !group.hasDefault("ShowPopupsInDndMode")) { 0106 group.revertToDefault("ShowPopupsInDndMode", KConfigBase::Notify); 0107 } else { 0108 group.writeEntry("ShowPopupsInDndMode", showPopupsInDndMode, KConfigBase::Notify); 0109 } 0110 0111 const bool showInHistory = behavior.testFlag(Settings::ShowInHistory); 0112 if (showInHistory && !group.hasDefault("ShowInHistory")) { 0113 group.revertToDefault("ShowInHistory", KConfig::Notify); 0114 } else { 0115 group.writeEntry("ShowInHistory", showInHistory, KConfigBase::Notify); 0116 } 0117 0118 const bool showBadges = behavior.testFlag(Settings::ShowBadges); 0119 if (showBadges && !group.hasDefault("ShowBadges")) { 0120 group.revertToDefault("ShowBadges", KConfigBase::Notify); 0121 } else { 0122 group.writeEntry("ShowBadges", showBadges, KConfigBase::Notify); 0123 } 0124 0125 setDirty(true); 0126 } 0127 0128 KConfigGroup Settings::Private::servicesGroup() const 0129 { 0130 return config->group("Services"); 0131 } 0132 0133 KConfigGroup Settings::Private::applicationsGroup() const 0134 { 0135 return config->group("Applications"); 0136 } 0137 0138 QStringList Settings::Private::behaviorMatchesList(const KConfigGroup &group, Settings::NotificationBehavior behavior, bool on) const 0139 { 0140 QStringList matches; 0141 0142 const QStringList apps = group.groupList(); 0143 for (const QString &app : apps) { 0144 if (groupBehavior(group.group(app)).testFlag(behavior) == on) { 0145 matches.append(app); 0146 } 0147 } 0148 0149 return matches; 0150 } 0151 0152 Settings::Settings(QObject *parent) 0153 : QObject(parent) 0154 , d(new Private(this)) 0155 { 0156 d->config = KSharedConfig::openConfig(s_configFile); 0157 0158 setLive(true); 0159 0160 connect(&Server::self(), &Server::inhibitedByApplicationChanged, this, &Settings::notificationsInhibitedByApplicationChanged); 0161 connect(&Server::self(), &Server::inhibitionApplicationsChanged, this, &Settings::notificationInhibitionApplicationsChanged); 0162 0163 if (d->dndSettings.whenScreensMirrored()) { 0164 d->mirroredScreensTracker = MirroredScreensTracker::createTracker(); 0165 connect(d->mirroredScreensTracker.data(), &MirroredScreensTracker::screensMirroredChanged, this, &Settings::screensMirroredChanged); 0166 } 0167 } 0168 0169 Settings::Settings(const KSharedConfig::Ptr &config, QObject *parent) 0170 : Settings(parent) 0171 { 0172 d->config = config; 0173 } 0174 0175 Settings::~Settings() 0176 { 0177 d->config->markAsClean(); 0178 } 0179 0180 Settings::NotificationBehaviors Settings::applicationBehavior(const QString &desktopEntry) const 0181 { 0182 return d->groupBehavior(d->applicationsGroup().group(desktopEntry)); 0183 } 0184 0185 void Settings::setApplicationBehavior(const QString &desktopEntry, NotificationBehaviors behaviors) 0186 { 0187 KConfigGroup group(d->applicationsGroup().group(desktopEntry)); 0188 d->setGroupBehavior(group, behaviors); 0189 } 0190 0191 Settings::NotificationBehaviors Settings::serviceBehavior(const QString ¬ifyRcName) const 0192 { 0193 return d->groupBehavior(d->servicesGroup().group(notifyRcName)); 0194 } 0195 0196 void Settings::setServiceBehavior(const QString ¬ifyRcName, NotificationBehaviors behaviors) 0197 { 0198 KConfigGroup group(d->servicesGroup().group(notifyRcName)); 0199 d->setGroupBehavior(group, behaviors); 0200 } 0201 0202 void Settings::registerKnownApplication(const QString &desktopEntry) 0203 { 0204 KService::Ptr service = KService::serviceByDesktopName(desktopEntry); 0205 if (!service) { 0206 qCDebug(NOTIFICATIONMANAGER) << "Application" << desktopEntry << "cannot be registered as seen application since there is no service for it"; 0207 return; 0208 } 0209 0210 if (service->noDisplay()) { 0211 qCDebug(NOTIFICATIONMANAGER) << "Application" << desktopEntry << "will not be registered as seen application since it's marked as NoDisplay"; 0212 return; 0213 } 0214 0215 if (knownApplications().contains(desktopEntry)) { 0216 return; 0217 } 0218 0219 d->applicationsGroup().group(desktopEntry).writeEntry("Seen", true); 0220 0221 Q_EMIT knownApplicationsChanged(); 0222 } 0223 0224 void Settings::forgetKnownApplication(const QString &desktopEntry) 0225 { 0226 if (!knownApplications().contains(desktopEntry)) { 0227 return; 0228 } 0229 0230 // Only remove applications that were added through registerKnownApplication 0231 if (!d->applicationsGroup().group(desktopEntry).readEntry("Seen", false)) { 0232 qCDebug(NOTIFICATIONMANAGER) << "Application" << desktopEntry << "will not be removed from seen applications since it wasn't one."; 0233 return; 0234 } 0235 0236 d->applicationsGroup().deleteGroup(desktopEntry); 0237 0238 Q_EMIT knownApplicationsChanged(); 0239 } 0240 0241 void Settings::load() 0242 { 0243 d->config->markAsClean(); 0244 d->config->reparseConfiguration(); 0245 d->dndSettings.load(); 0246 d->notificationSettings.load(); 0247 d->jobSettings.load(); 0248 d->badgeSettings.load(); 0249 Q_EMIT settingsChanged(); 0250 d->setDirty(false); 0251 } 0252 0253 void Settings::save() 0254 { 0255 d->dndSettings.save(); 0256 d->notificationSettings.save(); 0257 d->jobSettings.save(); 0258 d->badgeSettings.save(); 0259 0260 d->config->sync(); 0261 d->setDirty(false); 0262 } 0263 0264 void Settings::defaults() 0265 { 0266 d->dndSettings.setDefaults(); 0267 d->notificationSettings.setDefaults(); 0268 d->jobSettings.setDefaults(); 0269 d->badgeSettings.setDefaults(); 0270 Q_EMIT settingsChanged(); 0271 d->setDirty(false); 0272 } 0273 0274 bool Settings::live() const 0275 { 0276 return d->live; 0277 } 0278 0279 void Settings::setLive(bool live) 0280 { 0281 if (live == d->live) { 0282 return; 0283 } 0284 0285 d->live = live; 0286 0287 if (live) { 0288 d->watcher = KConfigWatcher::create(d->config); 0289 d->watcherConnection = connect(d->watcher.data(), &KConfigWatcher::configChanged, this, [this](const KConfigGroup &group, const QByteArrayList &names) { 0290 Q_UNUSED(names); 0291 0292 if (group.name() == QLatin1String("DoNotDisturb")) { 0293 d->dndSettings.load(); 0294 0295 bool emitScreensMirroredChanged = false; 0296 if (d->dndSettings.whenScreensMirrored()) { 0297 if (!d->mirroredScreensTracker) { 0298 d->mirroredScreensTracker = MirroredScreensTracker::createTracker(); 0299 emitScreensMirroredChanged = d->mirroredScreensTracker->screensMirrored(); 0300 connect(d->mirroredScreensTracker.data(), &MirroredScreensTracker::screensMirroredChanged, this, &Settings::screensMirroredChanged); 0301 } 0302 } else if (d->mirroredScreensTracker) { 0303 emitScreensMirroredChanged = d->mirroredScreensTracker->screensMirrored(); 0304 d->mirroredScreensTracker.reset(); 0305 } 0306 0307 if (emitScreensMirroredChanged) { 0308 Q_EMIT screensMirroredChanged(); 0309 } 0310 } else if (group.name() == QLatin1String("Notifications")) { 0311 d->notificationSettings.load(); 0312 } else if (group.name() == QLatin1String("Jobs")) { 0313 d->jobSettings.load(); 0314 } else if (group.name() == QLatin1String("Badges")) { 0315 d->badgeSettings.load(); 0316 } 0317 0318 Q_EMIT settingsChanged(); 0319 }); 0320 } else { 0321 disconnect(d->watcherConnection); 0322 d->watcherConnection = QMetaObject::Connection(); 0323 d->watcher.reset(); 0324 } 0325 0326 Q_EMIT liveChanged(); 0327 } 0328 0329 bool Settings::dirty() const 0330 { 0331 // KConfigSkeleton doesn't write into the KConfig until calling save() 0332 // so we need to track d->config->isDirty() manually 0333 return d->dirty; 0334 } 0335 0336 bool Settings::criticalPopupsInDoNotDisturbMode() const 0337 { 0338 return d->notificationSettings.criticalInDndMode(); 0339 } 0340 0341 void Settings::setCriticalPopupsInDoNotDisturbMode(bool enable) 0342 { 0343 if (this->criticalPopupsInDoNotDisturbMode() == enable) { 0344 return; 0345 } 0346 d->notificationSettings.setCriticalInDndMode(enable); 0347 d->setDirty(true); 0348 } 0349 0350 bool Settings::keepNormalAlwaysOnTop() const 0351 { 0352 return d->notificationSettings.normalAlwaysOnTop(); 0353 } 0354 0355 void Settings::setKeepNormalAlwaysOnTop(bool enable) 0356 { 0357 if (this->keepNormalAlwaysOnTop() == enable) { 0358 return; 0359 } 0360 d->notificationSettings.setNormalAlwaysOnTop(enable); 0361 d->setDirty(true); 0362 } 0363 0364 bool Settings::lowPriorityPopups() const 0365 { 0366 return d->notificationSettings.lowPriorityPopups(); 0367 } 0368 0369 void Settings::setLowPriorityPopups(bool enable) 0370 { 0371 if (this->lowPriorityPopups() == enable) { 0372 return; 0373 } 0374 d->notificationSettings.setLowPriorityPopups(enable); 0375 d->setDirty(true); 0376 } 0377 0378 bool Settings::lowPriorityHistory() const 0379 { 0380 return d->notificationSettings.lowPriorityHistory(); 0381 } 0382 0383 void Settings::setLowPriorityHistory(bool enable) 0384 { 0385 if (this->lowPriorityHistory() == enable) { 0386 return; 0387 } 0388 d->notificationSettings.setLowPriorityHistory(enable); 0389 d->setDirty(true); 0390 } 0391 0392 Settings::PopupPosition Settings::popupPosition() const 0393 { 0394 return static_cast<Settings::PopupPosition>(d->notificationSettings.popupPosition()); 0395 } 0396 0397 void Settings::setPopupPosition(Settings::PopupPosition position) 0398 { 0399 if (this->popupPosition() == position) { 0400 return; 0401 } 0402 d->notificationSettings.setPopupPosition(position); 0403 d->setDirty(true); 0404 } 0405 0406 int Settings::popupTimeout() const 0407 { 0408 return d->notificationSettings.popupTimeout(); 0409 } 0410 0411 void Settings::setPopupTimeout(int timeout) 0412 { 0413 if (this->popupTimeout() == timeout) { 0414 return; 0415 } 0416 d->notificationSettings.setPopupTimeout(timeout); 0417 d->setDirty(true); 0418 } 0419 0420 void Settings::resetPopupTimeout() 0421 { 0422 setPopupTimeout(d->notificationSettings.defaultPopupTimeoutValue()); 0423 } 0424 0425 bool Settings::jobsInTaskManager() const 0426 { 0427 return d->jobSettings.inTaskManager(); 0428 } 0429 0430 void Settings::setJobsInTaskManager(bool enable) 0431 { 0432 if (jobsInTaskManager() == enable) { 0433 return; 0434 } 0435 d->jobSettings.setInTaskManager(enable); 0436 d->setDirty(true); 0437 } 0438 0439 bool Settings::jobsInNotifications() const 0440 { 0441 return d->jobSettings.inNotifications(); 0442 } 0443 void Settings::setJobsInNotifications(bool enable) 0444 { 0445 if (jobsInNotifications() == enable) { 0446 return; 0447 } 0448 d->jobSettings.setInNotifications(enable); 0449 d->setDirty(true); 0450 } 0451 0452 bool Settings::permanentJobPopups() const 0453 { 0454 return d->jobSettings.permanentPopups(); 0455 } 0456 0457 void Settings::setPermanentJobPopups(bool enable) 0458 { 0459 if (permanentJobPopups() == enable) { 0460 return; 0461 } 0462 d->jobSettings.setPermanentPopups(enable); 0463 d->setDirty(true); 0464 } 0465 0466 bool Settings::badgesInTaskManager() const 0467 { 0468 return d->badgeSettings.inTaskManager(); 0469 } 0470 0471 void Settings::setBadgesInTaskManager(bool enable) 0472 { 0473 if (badgesInTaskManager() == enable) { 0474 return; 0475 } 0476 d->badgeSettings.setInTaskManager(enable); 0477 d->setDirty(true); 0478 } 0479 0480 QStringList Settings::knownApplications() const 0481 { 0482 return d->applicationsGroup().groupList(); 0483 } 0484 0485 QStringList Settings::popupBlacklistedApplications() const 0486 { 0487 return d->behaviorMatchesList(d->applicationsGroup(), ShowPopups, false); 0488 } 0489 0490 QStringList Settings::popupBlacklistedServices() const 0491 { 0492 return d->behaviorMatchesList(d->servicesGroup(), ShowPopups, false); 0493 } 0494 0495 QStringList Settings::doNotDisturbPopupWhitelistedApplications() const 0496 { 0497 return d->behaviorMatchesList(d->applicationsGroup(), ShowPopupsInDoNotDisturbMode, true); 0498 } 0499 0500 QStringList Settings::doNotDisturbPopupWhitelistedServices() const 0501 { 0502 return d->behaviorMatchesList(d->servicesGroup(), ShowPopupsInDoNotDisturbMode, true); 0503 } 0504 0505 QStringList Settings::historyBlacklistedApplications() const 0506 { 0507 return d->behaviorMatchesList(d->applicationsGroup(), ShowInHistory, false); 0508 } 0509 0510 QStringList Settings::historyBlacklistedServices() const 0511 { 0512 return d->behaviorMatchesList(d->servicesGroup(), ShowInHistory, false); 0513 } 0514 0515 QStringList Settings::badgeBlacklistedApplications() const 0516 { 0517 return d->behaviorMatchesList(d->applicationsGroup(), ShowBadges, false); 0518 } 0519 0520 QDateTime Settings::notificationsInhibitedUntil() const 0521 { 0522 return d->dndSettings.until(); 0523 } 0524 0525 void Settings::setNotificationsInhibitedUntil(const QDateTime &time) 0526 { 0527 d->dndSettings.setUntil(time); 0528 d->setDirty(true); 0529 } 0530 0531 void Settings::resetNotificationsInhibitedUntil() 0532 { 0533 setNotificationsInhibitedUntil(QDateTime()); // FIXME d->dndSettings.defaultUntilValue()); 0534 } 0535 0536 bool Settings::notificationsInhibitedByApplication() const 0537 { 0538 return Server::self().inhibitedByApplication(); 0539 } 0540 0541 QStringList Settings::notificationInhibitionApplications() const 0542 { 0543 return Server::self().inhibitionApplications(); 0544 } 0545 0546 QStringList Settings::notificationInhibitionReasons() const 0547 { 0548 return Server::self().inhibitionReasons(); 0549 } 0550 0551 bool Settings::inhibitNotificationsWhenScreensMirrored() const 0552 { 0553 return d->dndSettings.whenScreensMirrored(); 0554 } 0555 0556 void Settings::setInhibitNotificationsWhenScreensMirrored(bool inhibit) 0557 { 0558 if (inhibit == inhibitNotificationsWhenScreensMirrored()) { 0559 return; 0560 } 0561 0562 d->dndSettings.setWhenScreensMirrored(inhibit); 0563 d->setDirty(true); 0564 } 0565 0566 bool Settings::screensMirrored() const 0567 { 0568 return d->mirroredScreensTracker && d->mirroredScreensTracker->screensMirrored(); 0569 } 0570 0571 void Settings::setScreensMirrored(bool mirrored) 0572 { 0573 if (mirrored) { 0574 qCWarning(NOTIFICATIONMANAGER) << "Cannot forcefully set screens mirrored"; 0575 return; 0576 } 0577 0578 if (d->mirroredScreensTracker) { 0579 d->mirroredScreensTracker->setScreensMirrored(mirrored); 0580 } 0581 } 0582 0583 bool Settings::inhibitNotificationsWhenScreenSharing() const 0584 { 0585 return d->dndSettings.whenScreenSharing(); 0586 } 0587 0588 void Settings::setInhibitNotificationsWhenScreenSharing(bool inhibit) 0589 { 0590 if (inhibit == inhibitNotificationsWhenScreenSharing()) { 0591 return; 0592 } 0593 0594 d->dndSettings.setWhenScreenSharing(inhibit); 0595 d->setDirty(true); 0596 } 0597 0598 void Settings::revokeApplicationInhibitions() 0599 { 0600 Server::self().clearInhibitions(); 0601 } 0602 0603 bool Settings::notificationSoundsInhibited() const 0604 { 0605 return d->dndSettings.notificationSoundsMuted(); 0606 } 0607 0608 void Settings::setNotificationSoundsInhibited(bool inhibited) 0609 { 0610 if (inhibited == notificationSoundsInhibited()) { 0611 return; 0612 } 0613 0614 d->dndSettings.setNotificationSoundsMuted(inhibited); 0615 d->setDirty(true); 0616 }