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
24using namespace Qt::StringLiterals;
25using namespace NotificationManager;
26
27class Q_DECL_HIDDEN Settings::Private
28{
29public:
30 explicit Private(Settings *q);
31 ~Private();
32
33 void setDirty(bool dirty);
34
35 Settings::NotificationBehaviors groupBehavior(const KConfigGroup &group) const;
36 void setGroupBehavior(KConfigGroup &group, const Settings::NotificationBehaviors &behavior);
37
38 KConfigGroup servicesGroup() const;
39 KConfigGroup applicationsGroup() const;
40
41 QStringList behaviorMatchesList(const KConfigGroup &group, Settings::NotificationBehavior behavior, bool on) const;
42
43 Settings *q;
44
45 KSharedConfig::Ptr config;
46
47 KConfigWatcher::Ptr watcher;
48 QMetaObject::Connection watcherConnection;
49
50 MirroredScreensTracker::Ptr mirroredScreensTracker;
51
52 DoNotDisturbSettings dndSettings;
53 NotificationSettings notificationSettings;
54 JobSettings jobSettings;
55 BadgeSettings badgeSettings;
56
57 bool live = false; // set to true initially in constructor
58 bool dirty = false;
59};
60
61Settings::Private::Private(Settings *q)
62 : q(q)
63{
64}
65
66Settings::Private::~Private() = default;
67
68void Settings::Private::setDirty(bool dirty)
69{
70 if (this->dirty != dirty) {
71 this->dirty = dirty;
72 Q_EMIT q->dirtyChanged();
73 }
74}
75
76Settings::NotificationBehaviors Settings::Private::groupBehavior(const KConfigGroup &group) const
77{
79 behaviors.setFlag(Settings::ShowPopups, group.readEntry("ShowPopups", true));
80 // show popups in dnd mode implies the show popups
81 behaviors.setFlag(Settings::ShowPopupsInDoNotDisturbMode, behaviors.testFlag(Settings::ShowPopups) && group.readEntry("ShowPopupsInDndMode", false));
82 behaviors.setFlag(Settings::ShowInHistory, group.readEntry("ShowInHistory", true));
83 behaviors.setFlag(Settings::ShowBadges, group.readEntry("ShowBadges", true));
84 return behaviors;
85}
86
87void Settings::Private::setGroupBehavior(KConfigGroup &group, const Settings::NotificationBehaviors &behavior)
88{
89 if (groupBehavior(group) == behavior) {
90 return;
91 }
92
93 const bool showPopups = behavior.testFlag(Settings::ShowPopups);
94 if (showPopups && !group.hasDefault("ShowPopups")) {
95 group.revertToDefault("ShowPopups", KConfigBase::Notify);
96 } else {
97 group.writeEntry("ShowPopups", showPopups, KConfigBase::Notify);
98 }
99
100 const bool showPopupsInDndMode = behavior.testFlag(Settings::ShowPopupsInDoNotDisturbMode);
101 if (!showPopupsInDndMode && !group.hasDefault("ShowPopupsInDndMode")) {
102 group.revertToDefault("ShowPopupsInDndMode", KConfigBase::Notify);
103 } else {
104 group.writeEntry("ShowPopupsInDndMode", showPopupsInDndMode, KConfigBase::Notify);
105 }
106
107 const bool showInHistory = behavior.testFlag(Settings::ShowInHistory);
108 if (showInHistory && !group.hasDefault("ShowInHistory")) {
109 group.revertToDefault("ShowInHistory", KConfig::Notify);
110 } else {
111 group.writeEntry("ShowInHistory", showInHistory, KConfigBase::Notify);
112 }
113
114 const bool showBadges = behavior.testFlag(Settings::ShowBadges);
115 if (showBadges && !group.hasDefault("ShowBadges")) {
116 group.revertToDefault("ShowBadges", KConfigBase::Notify);
117 } else {
118 group.writeEntry("ShowBadges", showBadges, KConfigBase::Notify);
119 }
120
121 setDirty(true);
122}
123
124KConfigGroup Settings::Private::servicesGroup() const
125{
126 return config->group(QStringLiteral("Services"));
127}
128
129KConfigGroup Settings::Private::applicationsGroup() const
130{
131 return config->group(QStringLiteral("Applications"));
132}
133
134QStringList Settings::Private::behaviorMatchesList(const KConfigGroup &group, Settings::NotificationBehavior behavior, bool on) const
135{
136 QStringList matches;
137
138 const QStringList apps = group.groupList();
139 for (const QString &app : apps) {
140 if (groupBehavior(group.group(app)).testFlag(behavior) == on) {
141 matches.append(app);
142 }
143 }
144
145 return matches;
146}
147
148Settings::Settings(QObject *parent)
149 : QObject(parent)
150 , d(new Private(this))
151{
152 d->config = KSharedConfig::openConfig(u"plasmanotifyrc"_s);
153
154 setLive(true);
155
156 connect(&Server::self(), &Server::inhibitedByApplicationChanged, this, &Settings::notificationsInhibitedByApplicationChanged);
157 connect(&Server::self(), &Server::inhibitionApplicationsChanged, this, &Settings::notificationInhibitionApplicationsChanged);
158
159 if (d->dndSettings.whenScreensMirrored()) {
160 d->mirroredScreensTracker = MirroredScreensTracker::createTracker();
161 connect(d->mirroredScreensTracker.get(), &MirroredScreensTracker::screensMirroredChanged, this, &Settings::screensMirroredChanged);
162 }
163}
164
165Settings::Settings(const KSharedConfig::Ptr &config, QObject *parent)
166 : Settings(parent)
167{
168 d->config = config;
169}
170
171Settings::~Settings()
172{
173 d->config->markAsClean();
174}
175
176Settings::NotificationBehaviors Settings::applicationBehavior(const QString &desktopEntry) const
177{
178 return d->groupBehavior(d->applicationsGroup().group(desktopEntry));
179}
180
181void Settings::setApplicationBehavior(const QString &desktopEntry, NotificationBehaviors behaviors)
182{
183 KConfigGroup group(d->applicationsGroup().group(desktopEntry));
184 d->setGroupBehavior(group, behaviors);
185}
186
187Settings::NotificationBehaviors Settings::serviceBehavior(const QString &notifyRcName) const
188{
189 return d->groupBehavior(d->servicesGroup().group(notifyRcName));
190}
191
192void Settings::setServiceBehavior(const QString &notifyRcName, NotificationBehaviors behaviors)
193{
194 KConfigGroup group(d->servicesGroup().group(notifyRcName));
195 d->setGroupBehavior(group, behaviors);
196}
197
198void Settings::registerKnownApplication(const QString &desktopEntry)
199{
200 KService::Ptr service = KService::serviceByDesktopName(desktopEntry);
201 if (!service) {
202 qCDebug(NOTIFICATIONMANAGER) << "Application" << desktopEntry << "cannot be registered as seen application since there is no service for it";
203 return;
204 }
205
206 if (service->noDisplay()) {
207 qCDebug(NOTIFICATIONMANAGER) << "Application" << desktopEntry << "will not be registered as seen application since it's marked as NoDisplay";
208 return;
209 }
210
211 if (knownApplications().contains(desktopEntry)) {
212 return;
213 }
214
215 d->applicationsGroup().group(desktopEntry).writeEntry("Seen", true);
216
217 Q_EMIT knownApplicationsChanged();
218}
219
220void Settings::forgetKnownApplication(const QString &desktopEntry)
221{
222 if (!knownApplications().contains(desktopEntry)) {
223 return;
224 }
225
226 // Only remove applications that were added through registerKnownApplication
227 if (!d->applicationsGroup().group(desktopEntry).readEntry("Seen", false)) {
228 qCDebug(NOTIFICATIONMANAGER) << "Application" << desktopEntry << "will not be removed from seen applications since it wasn't one.";
229 return;
230 }
231
232 d->applicationsGroup().deleteGroup(desktopEntry);
233
234 Q_EMIT knownApplicationsChanged();
235}
236
237void Settings::load()
238{
239 d->config->markAsClean();
240 d->config->reparseConfiguration();
241 d->dndSettings.load();
242 d->notificationSettings.load();
243 d->jobSettings.load();
244 d->badgeSettings.load();
245 Q_EMIT settingsChanged();
246 d->setDirty(false);
247}
248
249void Settings::save()
250{
251 d->dndSettings.save();
252 d->notificationSettings.save();
253 d->jobSettings.save();
254 d->badgeSettings.save();
255
256 d->config->sync();
257 d->setDirty(false);
258}
259
260void Settings::defaults()
261{
262 d->dndSettings.setDefaults();
263 d->notificationSettings.setDefaults();
264 d->jobSettings.setDefaults();
265 d->badgeSettings.setDefaults();
266 Q_EMIT settingsChanged();
267 d->setDirty(false);
268}
269
270bool Settings::live() const
271{
272 return d->live;
273}
274
275void Settings::setLive(bool live)
276{
277 if (live == d->live) {
278 return;
279 }
280
281 d->live = live;
282
283 if (live) {
284 d->watcher = KConfigWatcher::create(d->config);
285 d->watcherConnection = connect(d->watcher.get(), &KConfigWatcher::configChanged, this, [this](const KConfigGroup &group, const QByteArrayList &names) {
286 Q_UNUSED(names);
287
288 if (group.name() == QLatin1String("DoNotDisturb")) {
289 d->dndSettings.load();
290
291 bool emitScreensMirroredChanged = false;
292 if (d->dndSettings.whenScreensMirrored()) {
293 if (!d->mirroredScreensTracker) {
294 d->mirroredScreensTracker = MirroredScreensTracker::createTracker();
295 emitScreensMirroredChanged = d->mirroredScreensTracker->screensMirrored();
296 connect(d->mirroredScreensTracker.get(), &MirroredScreensTracker::screensMirroredChanged, this, &Settings::screensMirroredChanged);
297 }
298 } else if (d->mirroredScreensTracker) {
299 emitScreensMirroredChanged = d->mirroredScreensTracker->screensMirrored();
300 d->mirroredScreensTracker.reset();
301 }
302
303 if (emitScreensMirroredChanged) {
304 Q_EMIT screensMirroredChanged();
305 }
306 } else if (group.name() == QLatin1String("Notifications")) {
307 d->notificationSettings.load();
308 } else if (group.name() == QLatin1String("Jobs")) {
309 d->jobSettings.load();
310 } else if (group.name() == QLatin1String("Badges")) {
311 d->badgeSettings.load();
312 }
313
314 Q_EMIT settingsChanged();
315 });
316 } else {
317 disconnect(d->watcherConnection);
318 d->watcherConnection = QMetaObject::Connection();
319 d->watcher.reset();
320 }
321
322 Q_EMIT liveChanged();
323}
324
325bool Settings::dirty() const
326{
327 // KConfigSkeleton doesn't write into the KConfig until calling save()
328 // so we need to track d->config->isDirty() manually
329 return d->dirty;
330}
331
333{
334 return d->notificationSettings.criticalInDndMode();
335}
336
337void Settings::setCriticalPopupsInDoNotDisturbMode(bool enable)
338{
339 if (this->criticalPopupsInDoNotDisturbMode() == enable) {
340 return;
341 }
342 d->notificationSettings.setCriticalInDndMode(enable);
343 d->setDirty(true);
344}
345
347{
348 return d->notificationSettings.normalAlwaysOnTop();
349}
350
351void Settings::setKeepNormalAlwaysOnTop(bool enable)
352{
353 if (this->keepNormalAlwaysOnTop() == enable) {
354 return;
355 }
356 d->notificationSettings.setNormalAlwaysOnTop(enable);
357 d->setDirty(true);
358}
359
361{
362 return d->notificationSettings.lowPriorityPopups();
363}
364
365void Settings::setLowPriorityPopups(bool enable)
366{
367 if (this->lowPriorityPopups() == enable) {
368 return;
369 }
370 d->notificationSettings.setLowPriorityPopups(enable);
371 d->setDirty(true);
372}
373
375{
376 return d->notificationSettings.lowPriorityHistory();
377}
378
379void Settings::setLowPriorityHistory(bool enable)
380{
381 if (this->lowPriorityHistory() == enable) {
382 return;
383 }
384 d->notificationSettings.setLowPriorityHistory(enable);
385 d->setDirty(true);
386}
387
388Settings::PopupPosition Settings::popupPosition() const
389{
390 return static_cast<Settings::PopupPosition>(d->notificationSettings.popupPosition());
391}
392
393void Settings::setPopupPosition(Settings::PopupPosition position)
394{
395 if (this->popupPosition() == position) {
396 return;
397 }
398 d->notificationSettings.setPopupPosition(position);
399 d->setDirty(true);
400}
401
402int Settings::popupTimeout() const
403{
404 return d->notificationSettings.popupTimeout();
405}
406
407void Settings::setPopupTimeout(int timeout)
408{
409 if (this->popupTimeout() == timeout) {
410 return;
411 }
412 d->notificationSettings.setPopupTimeout(timeout);
413 d->setDirty(true);
414}
415
416void Settings::resetPopupTimeout()
417{
418 setPopupTimeout(d->notificationSettings.defaultPopupTimeoutValue());
419}
420
422{
423 return d->jobSettings.inNotifications();
424}
425void Settings::setJobsInNotifications(bool enable)
426{
427 if (jobsInNotifications() == enable) {
428 return;
429 }
430 d->jobSettings.setInNotifications(enable);
431 d->setDirty(true);
432}
433
435{
436 return d->jobSettings.permanentPopups();
437}
438
439void Settings::setPermanentJobPopups(bool enable)
440{
441 if (permanentJobPopups() == enable) {
442 return;
443 }
444 d->jobSettings.setPermanentPopups(enable);
445 d->setDirty(true);
446}
447
449{
450 return d->badgeSettings.inTaskManager();
451}
452
453void Settings::setBadgesInTaskManager(bool enable)
454{
455 if (badgesInTaskManager() == enable) {
456 return;
457 }
458 d->badgeSettings.setInTaskManager(enable);
459 d->setDirty(true);
460}
461
463{
464 return d->applicationsGroup().groupList();
465}
466
468{
469 return d->behaviorMatchesList(d->applicationsGroup(), ShowPopups, false);
470}
471
473{
474 return d->behaviorMatchesList(d->servicesGroup(), ShowPopups, false);
475}
476
478{
479 return d->behaviorMatchesList(d->applicationsGroup(), ShowPopupsInDoNotDisturbMode, true);
480}
481
483{
484 return d->behaviorMatchesList(d->servicesGroup(), ShowPopupsInDoNotDisturbMode, true);
485}
486
488{
489 return d->behaviorMatchesList(d->applicationsGroup(), ShowInHistory, false);
490}
491
493{
494 return d->behaviorMatchesList(d->servicesGroup(), ShowInHistory, false);
495}
496
498{
499 return d->behaviorMatchesList(d->applicationsGroup(), ShowBadges, false);
500}
501
503{
504 return d->dndSettings.until();
505}
506
507void Settings::setNotificationsInhibitedUntil(const QDateTime &time)
508{
509 d->dndSettings.setUntil(time);
510 d->setDirty(true);
511}
512
513void Settings::resetNotificationsInhibitedUntil()
514{
515 setNotificationsInhibitedUntil(QDateTime()); // FIXME d->dndSettings.defaultUntilValue());
516}
517
519{
520 return Server::self().inhibitedByApplication();
521}
522
523QStringList Settings::notificationInhibitionApplications() const
524{
525 return Server::self().inhibitionApplications();
526}
527
528QStringList Settings::notificationInhibitionReasons() const
529{
530 return Server::self().inhibitionReasons();
531}
532
534{
535 return d->dndSettings.whenScreensMirrored();
536}
537
538void Settings::setInhibitNotificationsWhenScreensMirrored(bool inhibit)
539{
541 return;
542 }
543
544 d->dndSettings.setWhenScreensMirrored(inhibit);
545 d->setDirty(true);
546}
547
548bool Settings::screensMirrored() const
549{
550 return d->mirroredScreensTracker && d->mirroredScreensTracker->screensMirrored();
551}
552
553void Settings::setScreensMirrored(bool mirrored)
554{
555 if (mirrored) {
556 qCWarning(NOTIFICATIONMANAGER) << "Cannot forcefully set screens mirrored";
557 return;
558 }
559
560 if (d->mirroredScreensTracker) {
561 d->mirroredScreensTracker->setScreensMirrored(mirrored);
562 }
563}
564
566{
567 return d->dndSettings.whenScreenSharing();
568}
569
570void Settings::setInhibitNotificationsWhenScreenSharing(bool inhibit)
571{
572 if (inhibit == inhibitNotificationsWhenScreenSharing()) {
573 return;
574 }
575
576 d->dndSettings.setWhenScreenSharing(inhibit);
577 d->setDirty(true);
578}
579
581{
582 Server::self().clearInhibitions();
583}
584
586{
587 return d->dndSettings.notificationSoundsMuted();
588}
589
590void Settings::setNotificationSoundsInhibited(bool inhibited)
591{
592 if (inhibited == notificationSoundsInhibited()) {
593 return;
594 }
595
596 d->dndSettings.setNotificationSoundsMuted(inhibited);
597 d->setDirty(true);
598}
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:580
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 Mon Nov 18 2024 12:14:59 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.