Plasma-workspace

settings.cpp
1/*
2 SPDX-FileCopyrightText: 2019 Kai Uwe Broulik <kde@privat.broulik.de>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5*/
6
7#include "settings.h"
8
9#include <QDebug>
10
11#include <KConfigWatcher>
12#include <KService>
13
14#include "debug.h"
15#include "mirroredscreenstracker_p.h"
16#include "server.h"
17
18// Settings
19#include "badgesettings.h"
20#include "donotdisturbsettings.h"
21#include "jobsettings.h"
22#include "notificationsettings.h"
23
24namespace NotificationManager
25{
26constexpr const char s_configFile[] = "plasmanotifyrc";
27}
28
29using namespace NotificationManager;
30
31class Q_DECL_HIDDEN Settings::Private
32{
33public:
34 explicit Private(Settings *q);
35 ~Private();
36
37 void setDirty(bool dirty);
38
39 Settings::NotificationBehaviors groupBehavior(const KConfigGroup &group) const;
40 void setGroupBehavior(KConfigGroup &group, const Settings::NotificationBehaviors &behavior);
41
42 KConfigGroup servicesGroup() const;
43 KConfigGroup applicationsGroup() const;
44
45 QStringList behaviorMatchesList(const KConfigGroup &group, Settings::NotificationBehavior behavior, bool on) const;
46
47 Settings *q;
48
49 KSharedConfig::Ptr config;
50
51 KConfigWatcher::Ptr watcher;
52 QMetaObject::Connection watcherConnection;
53
54 MirroredScreensTracker::Ptr mirroredScreensTracker;
55
56 DoNotDisturbSettings dndSettings;
57 NotificationSettings notificationSettings;
58 JobSettings jobSettings;
59 BadgeSettings badgeSettings;
60
61 bool live = false; // set to true initially in constructor
62 bool dirty = false;
63};
64
65Settings::Private::Private(Settings *q)
66 : q(q)
67{
68}
69
70Settings::Private::~Private() = default;
71
72void Settings::Private::setDirty(bool dirty)
73{
74 if (this->dirty != dirty) {
75 this->dirty = dirty;
76 Q_EMIT q->dirtyChanged();
77 }
78}
79
80Settings::NotificationBehaviors Settings::Private::groupBehavior(const KConfigGroup &group) const
81{
83 behaviors.setFlag(Settings::ShowPopups, group.readEntry("ShowPopups", true));
84 // show popups in dnd mode implies the show popups
85 behaviors.setFlag(Settings::ShowPopupsInDoNotDisturbMode, behaviors.testFlag(Settings::ShowPopups) && group.readEntry("ShowPopupsInDndMode", false));
86 behaviors.setFlag(Settings::ShowInHistory, group.readEntry("ShowInHistory", true));
87 behaviors.setFlag(Settings::ShowBadges, group.readEntry("ShowBadges", true));
88 return behaviors;
89}
90
91void Settings::Private::setGroupBehavior(KConfigGroup &group, const Settings::NotificationBehaviors &behavior)
92{
93 if (groupBehavior(group) == behavior) {
94 return;
95 }
96
97 const bool showPopups = behavior.testFlag(Settings::ShowPopups);
98 if (showPopups && !group.hasDefault("ShowPopups")) {
99 group.revertToDefault("ShowPopups", KConfigBase::Notify);
100 } else {
101 group.writeEntry("ShowPopups", showPopups, KConfigBase::Notify);
102 }
103
104 const bool showPopupsInDndMode = behavior.testFlag(Settings::ShowPopupsInDoNotDisturbMode);
105 if (!showPopupsInDndMode && !group.hasDefault("ShowPopupsInDndMode")) {
106 group.revertToDefault("ShowPopupsInDndMode", KConfigBase::Notify);
107 } else {
108 group.writeEntry("ShowPopupsInDndMode", showPopupsInDndMode, KConfigBase::Notify);
109 }
110
111 const bool showInHistory = behavior.testFlag(Settings::ShowInHistory);
112 if (showInHistory && !group.hasDefault("ShowInHistory")) {
113 group.revertToDefault("ShowInHistory", KConfig::Notify);
114 } else {
115 group.writeEntry("ShowInHistory", showInHistory, KConfigBase::Notify);
116 }
117
118 const bool showBadges = behavior.testFlag(Settings::ShowBadges);
119 if (showBadges && !group.hasDefault("ShowBadges")) {
120 group.revertToDefault("ShowBadges", KConfigBase::Notify);
121 } else {
122 group.writeEntry("ShowBadges", showBadges, KConfigBase::Notify);
123 }
124
125 setDirty(true);
126}
127
128KConfigGroup Settings::Private::servicesGroup() const
129{
130 return config->group(QStringLiteral("Services"));
131}
132
133KConfigGroup Settings::Private::applicationsGroup() const
134{
135 return config->group(QStringLiteral("Applications"));
136}
137
138QStringList Settings::Private::behaviorMatchesList(const KConfigGroup &group, Settings::NotificationBehavior behavior, bool on) const
139{
140 QStringList matches;
141
142 const QStringList apps = group.groupList();
143 for (const QString &app : apps) {
144 if (groupBehavior(group.group(app)).testFlag(behavior) == on) {
145 matches.append(app);
146 }
147 }
148
149 return matches;
150}
151
152Settings::Settings(QObject *parent)
153 : QObject(parent)
154 , d(new Private(this))
155{
156 d->config = KSharedConfig::openConfig(s_configFile);
157
158 setLive(true);
159
160 connect(&Server::self(), &Server::inhibitedByApplicationChanged, this, &Settings::notificationsInhibitedByApplicationChanged);
161 connect(&Server::self(), &Server::inhibitionApplicationsChanged, this, &Settings::notificationInhibitionApplicationsChanged);
162
163 if (d->dndSettings.whenScreensMirrored()) {
164 d->mirroredScreensTracker = MirroredScreensTracker::createTracker();
165 connect(d->mirroredScreensTracker.get(), &MirroredScreensTracker::screensMirroredChanged, this, &Settings::screensMirroredChanged);
166 }
167}
168
169Settings::Settings(const KSharedConfig::Ptr &config, QObject *parent)
170 : Settings(parent)
171{
172 d->config = config;
173}
174
175Settings::~Settings()
176{
177 d->config->markAsClean();
178}
179
180Settings::NotificationBehaviors Settings::applicationBehavior(const QString &desktopEntry) const
181{
182 return d->groupBehavior(d->applicationsGroup().group(desktopEntry));
183}
184
185void Settings::setApplicationBehavior(const QString &desktopEntry, NotificationBehaviors behaviors)
186{
187 KConfigGroup group(d->applicationsGroup().group(desktopEntry));
188 d->setGroupBehavior(group, behaviors);
189}
190
191Settings::NotificationBehaviors Settings::serviceBehavior(const QString &notifyRcName) const
192{
193 return d->groupBehavior(d->servicesGroup().group(notifyRcName));
194}
195
196void Settings::setServiceBehavior(const QString &notifyRcName, NotificationBehaviors behaviors)
197{
198 KConfigGroup group(d->servicesGroup().group(notifyRcName));
199 d->setGroupBehavior(group, behaviors);
200}
201
202void Settings::registerKnownApplication(const QString &desktopEntry)
203{
204 KService::Ptr service = KService::serviceByDesktopName(desktopEntry);
205 if (!service) {
206 qCDebug(NOTIFICATIONMANAGER) << "Application" << desktopEntry << "cannot be registered as seen application since there is no service for it";
207 return;
208 }
209
210 if (service->noDisplay()) {
211 qCDebug(NOTIFICATIONMANAGER) << "Application" << desktopEntry << "will not be registered as seen application since it's marked as NoDisplay";
212 return;
213 }
214
215 if (knownApplications().contains(desktopEntry)) {
216 return;
217 }
218
219 d->applicationsGroup().group(desktopEntry).writeEntry("Seen", true);
220
221 Q_EMIT knownApplicationsChanged();
222}
223
224void Settings::forgetKnownApplication(const QString &desktopEntry)
225{
226 if (!knownApplications().contains(desktopEntry)) {
227 return;
228 }
229
230 // Only remove applications that were added through registerKnownApplication
231 if (!d->applicationsGroup().group(desktopEntry).readEntry("Seen", false)) {
232 qCDebug(NOTIFICATIONMANAGER) << "Application" << desktopEntry << "will not be removed from seen applications since it wasn't one.";
233 return;
234 }
235
236 d->applicationsGroup().deleteGroup(desktopEntry);
237
238 Q_EMIT knownApplicationsChanged();
239}
240
241void Settings::load()
242{
243 d->config->markAsClean();
244 d->config->reparseConfiguration();
245 d->dndSettings.load();
246 d->notificationSettings.load();
247 d->jobSettings.load();
248 d->badgeSettings.load();
249 Q_EMIT settingsChanged();
250 d->setDirty(false);
251}
252
253void Settings::save()
254{
255 d->dndSettings.save();
256 d->notificationSettings.save();
257 d->jobSettings.save();
258 d->badgeSettings.save();
259
260 d->config->sync();
261 d->setDirty(false);
262}
263
264void Settings::defaults()
265{
266 d->dndSettings.setDefaults();
267 d->notificationSettings.setDefaults();
268 d->jobSettings.setDefaults();
269 d->badgeSettings.setDefaults();
270 Q_EMIT settingsChanged();
271 d->setDirty(false);
272}
273
274bool Settings::live() const
275{
276 return d->live;
277}
278
279void Settings::setLive(bool live)
280{
281 if (live == d->live) {
282 return;
283 }
284
285 d->live = live;
286
287 if (live) {
288 d->watcher = KConfigWatcher::create(d->config);
289 d->watcherConnection = connect(d->watcher.get(), &KConfigWatcher::configChanged, this, [this](const KConfigGroup &group, const QByteArrayList &names) {
290 Q_UNUSED(names);
291
292 if (group.name() == QLatin1String("DoNotDisturb")) {
293 d->dndSettings.load();
294
295 bool emitScreensMirroredChanged = false;
296 if (d->dndSettings.whenScreensMirrored()) {
297 if (!d->mirroredScreensTracker) {
298 d->mirroredScreensTracker = MirroredScreensTracker::createTracker();
299 emitScreensMirroredChanged = d->mirroredScreensTracker->screensMirrored();
300 connect(d->mirroredScreensTracker.get(), &MirroredScreensTracker::screensMirroredChanged, this, &Settings::screensMirroredChanged);
301 }
302 } else if (d->mirroredScreensTracker) {
303 emitScreensMirroredChanged = d->mirroredScreensTracker->screensMirrored();
304 d->mirroredScreensTracker.reset();
305 }
306
307 if (emitScreensMirroredChanged) {
308 Q_EMIT screensMirroredChanged();
309 }
310 } else if (group.name() == QLatin1String("Notifications")) {
311 d->notificationSettings.load();
312 } else if (group.name() == QLatin1String("Jobs")) {
313 d->jobSettings.load();
314 } else if (group.name() == QLatin1String("Badges")) {
315 d->badgeSettings.load();
316 }
317
318 Q_EMIT settingsChanged();
319 });
320 } else {
321 disconnect(d->watcherConnection);
322 d->watcherConnection = QMetaObject::Connection();
323 d->watcher.reset();
324 }
325
326 Q_EMIT liveChanged();
327}
328
329bool Settings::dirty() const
330{
331 // KConfigSkeleton doesn't write into the KConfig until calling save()
332 // so we need to track d->config->isDirty() manually
333 return d->dirty;
334}
335
337{
338 return d->notificationSettings.criticalInDndMode();
339}
340
341void Settings::setCriticalPopupsInDoNotDisturbMode(bool enable)
342{
343 if (this->criticalPopupsInDoNotDisturbMode() == enable) {
344 return;
345 }
346 d->notificationSettings.setCriticalInDndMode(enable);
347 d->setDirty(true);
348}
349
351{
352 return d->notificationSettings.normalAlwaysOnTop();
353}
354
355void Settings::setKeepNormalAlwaysOnTop(bool enable)
356{
357 if (this->keepNormalAlwaysOnTop() == enable) {
358 return;
359 }
360 d->notificationSettings.setNormalAlwaysOnTop(enable);
361 d->setDirty(true);
362}
363
365{
366 return d->notificationSettings.lowPriorityPopups();
367}
368
369void Settings::setLowPriorityPopups(bool enable)
370{
371 if (this->lowPriorityPopups() == enable) {
372 return;
373 }
374 d->notificationSettings.setLowPriorityPopups(enable);
375 d->setDirty(true);
376}
377
379{
380 return d->notificationSettings.lowPriorityHistory();
381}
382
383void Settings::setLowPriorityHistory(bool enable)
384{
385 if (this->lowPriorityHistory() == enable) {
386 return;
387 }
388 d->notificationSettings.setLowPriorityHistory(enable);
389 d->setDirty(true);
390}
391
392Settings::PopupPosition Settings::popupPosition() const
393{
394 return static_cast<Settings::PopupPosition>(d->notificationSettings.popupPosition());
395}
396
397void Settings::setPopupPosition(Settings::PopupPosition position)
398{
399 if (this->popupPosition() == position) {
400 return;
401 }
402 d->notificationSettings.setPopupPosition(position);
403 d->setDirty(true);
404}
405
406int Settings::popupTimeout() const
407{
408 return d->notificationSettings.popupTimeout();
409}
410
411void Settings::setPopupTimeout(int timeout)
412{
413 if (this->popupTimeout() == timeout) {
414 return;
415 }
416 d->notificationSettings.setPopupTimeout(timeout);
417 d->setDirty(true);
418}
419
420void Settings::resetPopupTimeout()
421{
422 setPopupTimeout(d->notificationSettings.defaultPopupTimeoutValue());
423}
424
426{
427 return d->jobSettings.inNotifications();
428}
429void Settings::setJobsInNotifications(bool enable)
430{
431 if (jobsInNotifications() == enable) {
432 return;
433 }
434 d->jobSettings.setInNotifications(enable);
435 d->setDirty(true);
436}
437
439{
440 return d->jobSettings.permanentPopups();
441}
442
443void Settings::setPermanentJobPopups(bool enable)
444{
445 if (permanentJobPopups() == enable) {
446 return;
447 }
448 d->jobSettings.setPermanentPopups(enable);
449 d->setDirty(true);
450}
451
453{
454 return d->badgeSettings.inTaskManager();
455}
456
457void Settings::setBadgesInTaskManager(bool enable)
458{
459 if (badgesInTaskManager() == enable) {
460 return;
461 }
462 d->badgeSettings.setInTaskManager(enable);
463 d->setDirty(true);
464}
465
467{
468 return d->applicationsGroup().groupList();
469}
470
472{
473 return d->behaviorMatchesList(d->applicationsGroup(), ShowPopups, false);
474}
475
477{
478 return d->behaviorMatchesList(d->servicesGroup(), ShowPopups, false);
479}
480
482{
483 return d->behaviorMatchesList(d->applicationsGroup(), ShowPopupsInDoNotDisturbMode, true);
484}
485
487{
488 return d->behaviorMatchesList(d->servicesGroup(), ShowPopupsInDoNotDisturbMode, true);
489}
490
492{
493 return d->behaviorMatchesList(d->applicationsGroup(), ShowInHistory, false);
494}
495
497{
498 return d->behaviorMatchesList(d->servicesGroup(), ShowInHistory, false);
499}
500
502{
503 return d->behaviorMatchesList(d->applicationsGroup(), ShowBadges, false);
504}
505
507{
508 return d->dndSettings.until();
509}
510
511void Settings::setNotificationsInhibitedUntil(const QDateTime &time)
512{
513 d->dndSettings.setUntil(time);
514 d->setDirty(true);
515}
516
517void Settings::resetNotificationsInhibitedUntil()
518{
519 setNotificationsInhibitedUntil(QDateTime()); // FIXME d->dndSettings.defaultUntilValue());
520}
521
523{
524 return Server::self().inhibitedByApplication();
525}
526
527QStringList Settings::notificationInhibitionApplications() const
528{
529 return Server::self().inhibitionApplications();
530}
531
532QStringList Settings::notificationInhibitionReasons() const
533{
534 return Server::self().inhibitionReasons();
535}
536
538{
539 return d->dndSettings.whenScreensMirrored();
540}
541
542void Settings::setInhibitNotificationsWhenScreensMirrored(bool inhibit)
543{
545 return;
546 }
547
548 d->dndSettings.setWhenScreensMirrored(inhibit);
549 d->setDirty(true);
550}
551
552bool Settings::screensMirrored() const
553{
554 return d->mirroredScreensTracker && d->mirroredScreensTracker->screensMirrored();
555}
556
557void Settings::setScreensMirrored(bool mirrored)
558{
559 if (mirrored) {
560 qCWarning(NOTIFICATIONMANAGER) << "Cannot forcefully set screens mirrored";
561 return;
562 }
563
564 if (d->mirroredScreensTracker) {
565 d->mirroredScreensTracker->setScreensMirrored(mirrored);
566 }
567}
568
570{
571 return d->dndSettings.whenScreenSharing();
572}
573
574void Settings::setInhibitNotificationsWhenScreenSharing(bool inhibit)
575{
576 if (inhibit == inhibitNotificationsWhenScreenSharing()) {
577 return;
578 }
579
580 d->dndSettings.setWhenScreenSharing(inhibit);
581 d->setDirty(true);
582}
583
585{
586 Server::self().clearInhibitions();
587}
588
590{
591 return d->dndSettings.notificationSoundsMuted();
592}
593
594void Settings::setNotificationSoundsInhibited(bool inhibited)
595{
596 if (inhibited == notificationSoundsInhibited()) {
597 return;
598 }
599
600 d->dndSettings.setNotificationSoundsMuted(inhibited);
601 d->setDirty(true);
602}
KConfigGroup group(const QString &group)
QString name() const
void revertToDefault(const char *key, WriteConfigFlags pFlag=WriteConfigFlags())
bool hasDefault(const char *key) const
void writeEntry(const char *key, const char *value, WriteConfigFlags pFlags=Normal)
QString readEntry(const char *key, const char *aDefault=nullptr) const
QStringList groupList() const override
static Ptr create(const KSharedConfig::Ptr &config)
void configChanged(const KConfigGroup &group, const QByteArrayList &names)
static Ptr serviceByDesktopName(const QString &_name)
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
void inhibitedByApplicationChanged(bool inhibited)
Emitted when inhibitions by application have been changed.
void inhibitionApplicationsChanged()
Emitted when the list of applications holding a notification inhibition changes.
Notification settings and state.
Definition settings.h:35
QStringList popupBlacklistedApplications
A list of desktop entries of applications for which no popups should be shown.
Definition settings.h:90
bool notificationsInhibitedByApplication
Whether an application currently requested do not disturb mode.
Definition settings.h:137
bool screensMirrored
Whether there currently are mirrored/overlapping screens.
Definition settings.h:161
bool inhibitNotificationsWhenScreensMirrored
Whether to enable do not disturb mode when screens are mirrored/overlapping.
Definition settings.h:149
bool jobsInNotifications
Whether to show application jobs as notifications.
Definition settings.h:71
QStringList badgeBlacklistedApplications
A list of desktop entries of applications which shouldn't show badges in task manager.
Definition settings.h:117
bool dirty
Whether the settings have changed and need to be saved.
Definition settings.h:200
QStringList doNotDisturbPopupWhitelistedServices
A list of notifyrc names of services for which a popup should be shown even in do not disturb mode.
Definition settings.h:103
QML_ELEMENTbool criticalPopupsInDoNotDisturbMode
Whether to show critical notification popups in do not disturb mode.
Definition settings.h:42
bool keepNormalAlwaysOnTop
Whether to keep normal notifications always on top.
Definition settings.h:46
bool notificationSoundsInhibited
Whether notification sounds should be disabled.
Definition settings.h:183
QDateTime notificationsInhibitedUntil
The date until which do not disturb mode is enabled.
Definition settings.h:127
int popupTimeout
The default timeout for notification popups that do not have an explicit timeout set,...
Definition settings.h:66
QStringList historyBlacklistedApplications
A list of desktop entries of applications which shouldn't be shown in the history.
Definition settings.h:108
QStringList knownApplications
A list of desktop entries of applications that have been seen sending a notification.
Definition settings.h:85
bool lowPriorityHistory
Whether to add low priority notifications to the history.
Definition settings.h:54
bool live
Whether to update the properties immediately when they are changed on disk.
Definition settings.h:193
Q_INVOKABLE void revokeApplicationInhibitions()
Revoke application notification inhibitions.
Definition settings.cpp:584
QStringList popupBlacklistedServices
A list of notifyrc names of services for which no popups should be shown.
Definition settings.h:94
QStringList historyBlacklistedServices
A list of notifyrc names of services which shouldn't be shown in the history.
Definition settings.h:112
PopupPosition popupPosition
The notification popup position on screen.
Definition settings.h:60
QStringList doNotDisturbPopupWhitelistedApplications
A list of desktop entries of applications for which a popup should be shown even in do not disturb mo...
Definition settings.h:99
bool inhibitNotificationsWhenScreenSharing
Whether to enable do not disturb mode while screen sharing.
Definition settings.h:169
bool badgesInTaskManager
Whether to show notification badges (numbers in circles) in task manager.
Definition settings.h:80
bool permanentJobPopups
Whether application jobs stay visible for the whole duration of the job.
Definition settings.h:75
bool lowPriorityPopups
Whether to show popups for low priority notifications.
Definition settings.h:50
QFlags< T > & setFlag(Enum flag, bool on)
bool testFlag(Enum flag) const const
void append(QList< T > &&value)
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QObject * parent() const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:17:42 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.