Plasma-workspace

notifications.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 "notifications.h"
8
9#include <QConcatenateTablesProxyModel>
10#include <QDebug>
11#include <QMetaEnum>
12#include <memory>
13
14#include <KDescendantsProxyModel>
15#include <KLocalizedString>
16#include <KNotification>
17
18#include "limitedrowcountproxymodel_p.h"
19#include "notificationfilterproxymodel_p.h"
20#include "notificationgroupcollapsingproxymodel_p.h"
21#include "notificationgroupingproxymodel_p.h"
22#include "notificationsmodel.h"
23#include "notificationsortproxymodel_p.h"
24
25#include "jobsmodel.h"
26
27#include "settings.h"
28
29#include "notification.h"
30
31#include "utils_p.h"
32
33#include "debug.h"
34
35using namespace Qt::StringLiterals;
36using namespace NotificationManager;
37
38class Q_DECL_HIDDEN Notifications::Private
39{
40public:
41 explicit Private(Notifications *q);
42 ~Private();
43
44 void initSourceModels();
45 void initProxyModels();
46
47 void updateCount();
48
49 bool showNotifications = true;
50 bool showJobs = false;
51
52 Notifications::GroupMode groupMode = Notifications::GroupDisabled;
53 int groupLimit = 0;
54 bool expandUnread = false;
55
56 int activeNotificationsCount = 0;
57 int expiredNotificationsCount = 0;
58
59 int unreadNotificationsCount = 0;
60
61 int activeJobsCount = 0;
62 int jobsPercentage = 0;
63
64 static bool isGroup(const QModelIndex &idx);
65 static uint notificationId(const QModelIndex &idx);
66 QModelIndex mapFromModel(const QModelIndex &idx) const;
67
68 // NOTE when you add or re-arrange models make sure to update mapFromModel()!
69 NotificationsModel::Ptr notificationsModel;
70 JobsModel::Ptr jobsModel;
71 std::shared_ptr<Settings> settings() const;
72
73 QConcatenateTablesProxyModel *notificationsAndJobsModel = nullptr;
74
75 NotificationFilterProxyModel *filterModel = nullptr;
76 NotificationSortProxyModel *sortModel = nullptr;
77 NotificationGroupingProxyModel *groupingModel = nullptr;
78 NotificationGroupCollapsingProxyModel *groupCollapsingModel = nullptr;
79 KDescendantsProxyModel *flattenModel = nullptr;
80
81 LimitedRowCountProxyModel *limiterModel = nullptr;
82
83private:
84 Notifications *const q;
85};
86
87Notifications::Private::Private(Notifications *q)
88 : q(q)
89{
90}
91
92Notifications::Private::~Private()
93{
94}
95
96void Notifications::Private::initSourceModels()
97{
98 Q_ASSERT(notificationsAndJobsModel); // initProxyModels must be called before initSourceModels
99
100 if (showNotifications && !notificationsModel) {
101 notificationsModel = NotificationsModel::createNotificationsModel();
102 notificationsAndJobsModel->addSourceModel(notificationsModel.get());
103 connect(notificationsModel.get(), &NotificationsModel::lastReadChanged, q, [this] {
104 updateCount();
105 Q_EMIT q->lastReadChanged();
106 });
107 } else if (!showNotifications && notificationsModel) {
108 notificationsAndJobsModel->removeSourceModel(notificationsModel.get());
109 disconnect(notificationsModel.get(), nullptr, q, nullptr); // disconnect all
110 notificationsModel = nullptr;
111 }
112
113 if (showJobs && !jobsModel) {
114 jobsModel = JobsModel::createJobsModel();
115 notificationsAndJobsModel->addSourceModel(jobsModel.get());
116 jobsModel->init();
117 } else if (!showJobs && jobsModel) {
118 notificationsAndJobsModel->removeSourceModel(jobsModel.get());
119 jobsModel = nullptr;
120 }
121}
122
123void Notifications::Private::initProxyModels()
124{
125 /* The data flow is as follows:
126 * NOTE when you add or re-arrange models make sure to update mapFromModel()!
127 *
128 * NotificationsModel JobsModel
129 * \\ /
130 * \\ /
131 * QConcatenateTablesProxyModel
132 * |||
133 * |||
134 * NotificationFilterProxyModel
135 * (filters by urgency, whitelist, etc)
136 * |
137 * |
138 * NotificationSortProxyModel
139 * (sorts by urgency, date, etc)
140 * |
141 * --- BEGIN: Only when grouping is enabled ---
142 * |
143 * NotificationGroupingProxyModel
144 * (turns list into tree grouped by app)
145 * //\\
146 * //\\
147 * NotificationGroupCollapsingProxyModel
148 * (limits number of tree leaves for expand/collapse feature)
149 * /\
150 * /\
151 * KDescendantsProxyModel
152 * (flattens tree back into a list for consumption in ListView)
153 * |
154 * --- END: Only when grouping is enabled ---
155 * |
156 * LimitedRowCountProxyModel
157 * (limits the total number of items in the model)
158 * |
159 * |
160 * \o/ <- Happy user seeing their notifications
161 */
162
163 if (!notificationsAndJobsModel) {
164 notificationsAndJobsModel = new QConcatenateTablesProxyModel(q);
165 }
166
167 if (!filterModel) {
168 filterModel = new NotificationFilterProxyModel();
169 connect(filterModel, &NotificationFilterProxyModel::urgenciesChanged, q, &Notifications::urgenciesChanged);
170 connect(filterModel, &NotificationFilterProxyModel::showExpiredChanged, q, &Notifications::showExpiredChanged);
171 connect(filterModel, &NotificationFilterProxyModel::showDismissedChanged, q, &Notifications::showDismissedChanged);
172 connect(filterModel, &NotificationFilterProxyModel::showAddedDuringInhibitionChanged, q, &Notifications::showAddedDuringInhibitionChanged);
173 connect(filterModel, &NotificationFilterProxyModel::blacklistedDesktopEntriesChanged, q, &Notifications::blacklistedDesktopEntriesChanged);
174 connect(filterModel, &NotificationFilterProxyModel::blacklistedNotifyRcNamesChanged, q, &Notifications::blacklistedNotifyRcNamesChanged);
175
176 filterModel->setSourceModel(notificationsAndJobsModel);
177
178 connect(filterModel, &QAbstractItemModel::rowsInserted, q, [this] {
179 updateCount();
180 });
181 connect(filterModel, &QAbstractItemModel::rowsRemoved, q, [this] {
182 updateCount();
183 });
184 connect(filterModel, &QAbstractItemModel::dataChanged, q, [this](const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList<int> &roles) {
185 Q_UNUSED(topLeft);
186 Q_UNUSED(bottomRight);
189 updateCount();
190 }
191 });
192 }
193
194 if (!sortModel) {
195 sortModel = new NotificationSortProxyModel(q);
196 connect(sortModel, &NotificationSortProxyModel::sortModeChanged, q, &Notifications::sortModeChanged);
197 connect(sortModel, &NotificationSortProxyModel::sortOrderChanged, q, &Notifications::sortOrderChanged);
198 }
199
200 if (!limiterModel) {
201 limiterModel = new LimitedRowCountProxyModel(q);
202 connect(limiterModel, &LimitedRowCountProxyModel::limitChanged, q, &Notifications::limitChanged);
203 }
204
205 if (groupMode == GroupApplicationsFlat) {
206 if (!groupingModel) {
207 groupingModel = new NotificationGroupingProxyModel(q);
208 groupingModel->setSourceModel(filterModel);
209 }
210
211 if (!groupCollapsingModel) {
212 groupCollapsingModel = new NotificationGroupCollapsingProxyModel(q);
213 groupCollapsingModel->setLimit(groupLimit);
214 groupCollapsingModel->setExpandUnread(expandUnread);
215 groupCollapsingModel->setLastRead(q->lastRead());
216 groupCollapsingModel->setSourceModel(groupingModel);
217 }
218
219 sortModel->setSourceModel(groupCollapsingModel);
220
221 flattenModel = new KDescendantsProxyModel(q);
222 flattenModel->setSourceModel(sortModel);
223
224 limiterModel->setSourceModel(flattenModel);
225 } else {
226 sortModel->setSourceModel(filterModel);
227 limiterModel->setSourceModel(sortModel);
228 delete flattenModel;
229 flattenModel = nullptr;
230 delete groupingModel;
231 groupingModel = nullptr;
232 }
233
234 q->setSourceModel(limiterModel);
235}
236
237void Notifications::Private::updateCount()
238{
239 int active = 0;
240 int expired = 0;
241 int unread = 0;
242
243 int jobs = 0;
244 int totalPercentage = 0;
245
246 // We want to get the numbers after main filtering (urgencies, whitelists, etc)
247 // but before any limiting or group limiting, hence asking the filterModel for advice
248 // at which point notifications and jobs also have already been merged
249 for (int i = 0; i < filterModel->rowCount(); ++i) {
250 const QModelIndex idx = filterModel->index(i, 0);
251
253 ++expired;
254 } else {
255 ++active;
256 }
257
258 const bool read = idx.data(Notifications::ReadRole).toBool();
259 if (!active && !read) {
260 QDateTime date = idx.data(Notifications::UpdatedRole).toDateTime();
261 if (!date.isValid()) {
263 }
264
265 if (notificationsModel && date > notificationsModel->lastRead()) {
266 ++unread;
267 }
268 }
269
272 ++jobs;
273
274 totalPercentage += idx.data(Notifications::PercentageRole).toInt();
275 }
276 }
277 }
278
279 if (activeNotificationsCount != active) {
281 Q_EMIT q->activeNotificationsCountChanged();
282 }
283 if (expiredNotificationsCount != expired) {
285 Q_EMIT q->expiredNotificationsCountChanged();
286 }
287 if (unreadNotificationsCount != unread) {
289 Q_EMIT q->unreadNotificationsCountChanged();
290 }
291 if (activeJobsCount != jobs) {
292 activeJobsCount = jobs;
293 Q_EMIT q->activeJobsCountChanged();
294 }
295
296 const int percentage = (jobs > 0 ? totalPercentage / jobs : 0);
297 if (jobsPercentage != percentage) {
298 jobsPercentage = percentage;
299 Q_EMIT q->jobsPercentageChanged();
300 }
301
302 // TODO don't Q_EMIT in dataChanged
303 Q_EMIT q->countChanged();
304}
305
306bool Notifications::Private::isGroup(const QModelIndex &idx)
307{
309}
310
311uint Notifications::Private::notificationId(const QModelIndex &idx)
312{
313 return idx.data(Notifications::IdRole).toUInt();
314}
315
316QModelIndex Notifications::Private::mapFromModel(const QModelIndex &idx) const
317{
318 QModelIndex resolvedIdx = idx;
319
320 QAbstractItemModel *models[] = {
321 notificationsAndJobsModel,
323 sortModel,
324 groupingModel,
325 groupCollapsingModel,
326 flattenModel,
327 limiterModel,
328 };
329
330 // TODO can we do this with a generic loop like mapFromModel
331 while (resolvedIdx.isValid() && resolvedIdx.model() != q) {
332 const auto *idxModel = resolvedIdx.model();
333
334 // HACK try to find the model that uses the index' model as source
335 bool found = false;
336 for (QAbstractItemModel *model : models) {
337 if (!model) {
338 continue;
339 }
340
341 if (auto *proxyModel = qobject_cast<QAbstractProxyModel *>(model)) {
342 if (proxyModel->sourceModel() == idxModel) {
343 resolvedIdx = proxyModel->mapFromSource(resolvedIdx);
344 found = true;
345 break;
346 }
347 } else if (auto *concatenateModel = qobject_cast<QConcatenateTablesProxyModel *>(model)) {
348 if (idxModel == notificationsModel.get() || idxModel == jobsModel.get()) {
349 resolvedIdx = concatenateModel->mapFromSource(resolvedIdx);
350 found = true;
351 break;
352 }
353 }
354 }
355
356 if (!found) {
357 break;
358 }
359 }
360 return resolvedIdx;
361}
362
363std::shared_ptr<Settings> Notifications::Private::settings() const
364{
365 static std::weak_ptr<Settings> s_instance;
366 if (!s_instance.expired()) {
367 std::shared_ptr<Settings> ptr(new Settings());
368 s_instance = ptr;
369 return ptr;
370 }
371 return s_instance.lock();
372}
373
374Notifications::Notifications(QObject *parent)
376 , d(new Private(this))
377{
378 // The proxy models are always the same, just with different
379 // properties set whereas we want to avoid loading a source model
380 // e.g. notifications or jobs when we're not actually using them
381 d->initProxyModels();
382
383 // init source models when used from C++
385 this,
386 [this] {
387 d->initSourceModels();
388 },
390}
391
392Notifications::~Notifications() = default;
393
394void Notifications::classBegin()
395{
396}
397
398void Notifications::componentComplete()
399{
400 // init source models when used from QML
401 d->initSourceModels();
402}
403
404int Notifications::limit() const
405{
406 return d->limiterModel->limit();
407}
408
409void Notifications::setLimit(int limit)
410{
411 d->limiterModel->setLimit(limit);
412}
413
415{
416 return d->groupLimit;
417}
418
419void Notifications::setGroupLimit(int limit)
420{
421 if (d->groupLimit == limit) {
422 return;
423 }
424
425 d->groupLimit = limit;
426 if (d->groupCollapsingModel) {
427 d->groupCollapsingModel->setLimit(limit);
428 }
429 Q_EMIT groupLimitChanged();
430}
431
433{
434 return d->expandUnread;
435}
436
437void Notifications::setExpandUnread(bool expand)
438{
439 if (d->expandUnread == expand) {
440 return;
441 }
442
443 d->expandUnread = expand;
444 if (d->groupCollapsingModel) {
445 d->groupCollapsingModel->setExpandUnread(expand);
446 }
447 Q_EMIT expandUnreadChanged();
448}
449
450QWindow *Notifications::window() const
451{
452 return d->notificationsModel ? d->notificationsModel->window() : nullptr;
453}
454
455void Notifications::setWindow(QWindow *window)
456{
457 if (d->notificationsModel) {
458 d->notificationsModel->setWindow(window);
459 } else {
460 qCWarning(NOTIFICATIONMANAGER) << "Setting window before initialising the model" << this << window;
461 }
462}
463
465{
466 return d->filterModel->showExpired();
467}
468
469void Notifications::setShowExpired(bool show)
470{
471 d->filterModel->setShowExpired(show);
472}
473
475{
476 return d->filterModel->showDismissed();
477}
478
479void Notifications::setShowDismissed(bool show)
480{
481 d->filterModel->setShowDismissed(show);
482}
483
485{
486 return d->filterModel->showAddedDuringInhibition();
487}
488
489void Notifications::setShowAddedDuringInhibition(bool show)
490{
491 d->filterModel->setShowAddedDuringInhibition(show);
492}
493
495{
496 return d->filterModel->blacklistedDesktopEntries();
497}
498
499void Notifications::setBlacklistedDesktopEntries(const QStringList &blacklist)
500{
501 d->filterModel->setBlackListedDesktopEntries(blacklist);
502}
503
505{
506 return d->filterModel->blacklistedNotifyRcNames();
507}
508
509void Notifications::setBlacklistedNotifyRcNames(const QStringList &blacklist)
510{
511 d->filterModel->setBlacklistedNotifyRcNames(blacklist);
512}
513
515{
516 return d->filterModel->whitelistedDesktopEntries();
517}
518
519void Notifications::setWhitelistedDesktopEntries(const QStringList &whitelist)
520{
521 d->filterModel->setWhiteListedDesktopEntries(whitelist);
522}
523
525{
526 return d->filterModel->whitelistedNotifyRcNames();
527}
528
529void Notifications::setWhitelistedNotifyRcNames(const QStringList &whitelist)
530{
531 d->filterModel->setWhitelistedNotifyRcNames(whitelist);
532}
533
535{
536 return d->showNotifications;
537}
538
539void Notifications::setShowNotifications(bool show)
540{
541 if (d->showNotifications == show) {
542 return;
543 }
544
545 d->showNotifications = show;
546 d->initSourceModels();
547 Q_EMIT showNotificationsChanged();
548}
549
550bool Notifications::showJobs() const
551{
552 return d->showJobs;
553}
554
555void Notifications::setShowJobs(bool show)
556{
557 if (d->showJobs == show) {
558 return;
559 }
560
561 d->showJobs = show;
562 d->initSourceModels();
563 Q_EMIT showJobsChanged();
564}
565
566Notifications::Urgencies Notifications::urgencies() const
567{
568 return d->filterModel->urgencies();
569}
570
571void Notifications::setUrgencies(Urgencies urgencies)
572{
573 d->filterModel->setUrgencies(urgencies);
574}
575
577{
578 return d->sortModel->sortMode();
579}
580
581void Notifications::setSortMode(SortMode sortMode)
582{
583 d->sortModel->setSortMode(sortMode);
584}
585
587{
588 return d->sortModel->sortOrder();
589}
590
591void Notifications::setSortOrder(Qt::SortOrder sortOrder)
592{
593 d->sortModel->setSortOrder(sortOrder);
594}
595
597{
598 return d->groupMode;
599}
600
601void Notifications::setGroupMode(GroupMode groupMode)
602{
603 if (d->groupMode != groupMode) {
604 d->groupMode = groupMode;
605 d->initProxyModels();
606 Q_EMIT groupModeChanged();
607 }
608}
609
610int Notifications::count() const
611{
612 return rowCount(QModelIndex());
613}
614
616{
617 return d->activeNotificationsCount;
618}
619
621{
622 return d->expiredNotificationsCount;
623}
624
625QDateTime Notifications::lastRead() const
626{
627 if (d->notificationsModel) {
628 return d->notificationsModel->lastRead();
629 }
630 return QDateTime();
631}
632
633void Notifications::setLastRead(const QDateTime &lastRead)
634{
635 // TODO jobs could also be unread?
636 if (d->notificationsModel) {
637 d->notificationsModel->setLastRead(lastRead);
638 }
639 if (d->groupCollapsingModel) {
640 d->groupCollapsingModel->setLastRead(lastRead);
641 }
642}
643
644void Notifications::resetLastRead()
645{
646 setLastRead(QDateTime::currentDateTimeUtc());
647}
648
650{
651 return d->unreadNotificationsCount;
652}
653
655{
656 return d->activeJobsCount;
657}
658
660{
661 return d->jobsPercentage;
662}
663
668
670{
671 switch (static_cast<Notifications::Type>(idx.data(Notifications::TypeRole).toInt())) {
673 d->notificationsModel->expire(Private::notificationId(idx));
674 break;
676 d->jobsModel->expire(Utils::mapToModel(idx, d->jobsModel.get()));
677 break;
678 default:
679 Q_UNREACHABLE();
680 }
681}
682
684{
686 const QModelIndex groupIdx = Utils::mapToModel(idx, d->groupingModel);
687 if (!groupIdx.isValid()) {
688 qCWarning(NOTIFICATIONMANAGER) << "Failed to find group model index for this item";
689 return;
690 }
691
692 Q_ASSERT(groupIdx.model() == d->groupingModel);
693
694 const int childCount = d->groupingModel->rowCount(groupIdx);
695 for (int i = childCount - 1; i >= 0; --i) {
696 const QModelIndex childIdx = d->groupingModel->index(i, 0, groupIdx);
697 close(childIdx);
698 }
699 return;
700 }
701
703 return;
704 }
705
706 switch (static_cast<Notifications::Type>(idx.data(Notifications::TypeRole).toInt())) {
708 d->notificationsModel->close(Private::notificationId(idx));
709 break;
711 d->jobsModel->close(Utils::mapToModel(idx, d->jobsModel.get()));
712 break;
713 default:
714 Q_UNREACHABLE();
715 }
716}
717
719{
720 if (!d->notificationsModel) {
721 return;
722 }
723
724 // For groups just configure the application, not the individual event
725 if (Private::isGroup(idx)) {
726 const QString desktopEntry = idx.data(Notifications::DesktopEntryRole).toString();
727 const QString notifyRcName = idx.data(Notifications::NotifyRcNameRole).toString();
728
729 d->notificationsModel->configure(desktopEntry, notifyRcName, QString() /*eventId*/);
730 return;
731 }
732
733 d->notificationsModel->configure(Private::notificationId(idx));
734}
735
736void Notifications::invokeDefaultAction(const QModelIndex &idx, InvokeBehavior behavior)
737{
738 if (d->notificationsModel) {
739 d->notificationsModel->invokeDefaultAction(Private::notificationId(idx), behavior);
740 }
741}
742
743void Notifications::invokeAction(const QModelIndex &idx, const QString &actionId, InvokeBehavior behavior)
744{
745 if (d->notificationsModel) {
746 d->notificationsModel->invokeAction(Private::notificationId(idx), actionId, behavior);
747 }
748}
749
750void Notifications::reply(const QModelIndex &idx, const QString &text, InvokeBehavior behavior)
751{
752 if (d->notificationsModel) {
753 d->notificationsModel->reply(Private::notificationId(idx), text, behavior);
754 }
755}
756
758{
759 startTimeout(Private::notificationId(idx));
760}
761
762void Notifications::startTimeout(uint notificationId)
763{
764 if (d->notificationsModel) {
765 d->notificationsModel->startTimeout(notificationId);
766 }
767}
768
770{
771 if (d->notificationsModel) {
772 d->notificationsModel->stopTimeout(Private::notificationId(idx));
773 }
774}
775
777{
778 if (d->jobsModel) {
779 d->jobsModel->suspend(Utils::mapToModel(idx, d->jobsModel.get()));
780 }
781}
782
784{
785 if (d->jobsModel) {
786 d->jobsModel->resume(Utils::mapToModel(idx, d->jobsModel.get()));
787 }
788}
789
791{
792 if (d->jobsModel) {
793 d->jobsModel->kill(Utils::mapToModel(idx, d->jobsModel.get()));
794 }
795}
796
798{
799 if (d->notificationsModel) {
800 d->notificationsModel->clear(flags);
801 }
802 if (d->jobsModel) {
803 d->jobsModel->clear(flags);
804 }
805}
806
808{
810 return idx;
811 }
812
814 QModelIndex groupingIdx = Utils::mapToModel(idx, d->groupingModel);
815 return d->mapFromModel(groupingIdx.parent());
816 }
817
818 qCWarning(NOTIFICATIONMANAGER) << "Cannot get group index for item that isn't a group or inside one";
819 return QModelIndex();
820}
821
822void Notifications::collapseAllGroups()
823{
824 if (d->groupCollapsingModel) {
825 d->groupCollapsingModel->collapseAll();
826 }
827}
828
830{
831 int inhibited = 0;
832 for (int i = 0, count = d->notificationsAndJobsModel->rowCount(); i < count; ++i) {
833 const QModelIndex idx = d->notificationsAndJobsModel->index(i, 0);
835 ++inhibited;
836 }
837 }
838
839 if (!inhibited) {
840 return;
841 }
842
843 KNotification::event(u"inhibitionSummary"_s,
844 i18nc("@title", "Unread Notifications"),
845 i18nc("@info", "%1 notifications were received while Do Not Disturb was active.", QString::number(inhibited)),
846 u"preferences-desktop-notification-bell"_s,
848 u"libnotificationmanager"_s);
849}
850
851QVariant Notifications::data(const QModelIndex &index, int role) const
852{
854}
855
856bool Notifications::setData(const QModelIndex &index, const QVariant &value, int role)
857{
858 return QSortFilterProxyModel::setData(index, value, role);
859}
860
861bool Notifications::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
862{
863 return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
864}
865
866bool Notifications::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
867{
868 return QSortFilterProxyModel::lessThan(source_left, source_right);
869}
870
871int Notifications::rowCount(const QModelIndex &parent) const
872{
874}
875
876QHash<int, QByteArray> Notifications::roleNames() const
877{
878 return Utils::roleNames();
879}
880
881#include "moc_notifications.cpp"
static KNotification * event(const QString &eventId, const QString &text=QString(), const QPixmap &pixmap=QPixmap(), const NotificationFlags &flags=CloseOnTimeout, const QString &componentName=QString())
A model with notifications and jobs.
Q_INVOKABLE void expire(const QModelIndex &idx)
Expire a notification.
QStringList whitelistedDesktopEntries
A list of desktop entries for which notifications should be shown.
GroupMode groupMode
The group mode for notifications.
Q_INVOKABLE void invokeAction(const QModelIndex &idx, const QString &actionId, InvokeBehavior=None)
Invoke a notification action.
QStringList blacklistedNotifyRcNames
A list of notifyrc names for which no notifications should be shown.
int groupLimit
How many notifications are shown in each group.
Q_INVOKABLE void resumeJob(const QModelIndex &idx)
Resume a job.
bool showNotifications
Whether to show notifications.
int unreadNotificationsCount
The number of notifications added since lastRead.
bool expandUnread
Whether to automatically show notifications that are unread.
Q_INVOKABLE void configure(const QModelIndex &idx)
Configure a notification.
bool showDismissed
Whether to show dismissed notifications.
Q_INVOKABLE void clear(ClearFlags flags)
Clear notifications.
int activeNotificationsCount
The number of active, i.e.
Q_INVOKABLE void invokeDefaultAction(const QModelIndex &idx, InvokeBehavior behavior=None)
Invoke the default notification action.
Q_INVOKABLE void stopTimeout(const QModelIndex &idx)
Stop the automatic timeout of notifications.
QWindow * window
The window that will render the notifications.
bool showExpired
Whether to show expired notifications.
Q_INVOKABLE QPersistentModelIndex makePersistentModelIndex(const QModelIndex &idx) const
Convert the given QModelIndex into a QPersistentModelIndex.
int jobsPercentage
The combined percentage of all jobs.
Urgencies urgencies
The notification urgency types the model should contain.
Q_INVOKABLE void showInhibitionSummary()
Shows a notification to report the number of unread inhibited notifications.
QStringList whitelistedNotifyRcNames
A list of notifyrc names for which notifications should be shown.
SortMode
The sort mode for the model.
@ JobType
This item represents an application job.
@ NotificationType
This item represents a notification.
bool showAddedDuringInhibition
Whether to show notifications added during inhibition.
Qt::SortOrder sortOrder
The sort order for notifications.
@ JobStateStopped
The job is stopped. It has either finished (error is 0) or failed (error is not 0)
int expiredNotificationsCount
The number of inactive, i.e.
bool showJobs
Whether to show application jobs.
QML_ELEMENTint limit
The number of notifications the model should at most contain.
GroupMode
The group mode for the model.
Q_INVOKABLE void reply(const QModelIndex &idx, const QString &text, InvokeBehavior behavior)
Reply to a notification.
int activeJobsCount
The number of active jobs.
int count
The number of notifications in the model.
Q_INVOKABLE void close(const QModelIndex &idx)
Close a notification.
@ UpdatedRole
When the notification was last updated, invalid when it hasn't been updated.
@ NotifyRcNameRole
The notifyrc name (e.g. spectaclerc) of the application that sent the notification.
@ ReadRole
Whether the notification got read by the user.
@ IsInGroupRole
Whether the notification is currently inside a group.
@ JobStateRole
The state of the job, either JobStateJopped, JobStateSuspended, or JobStateRunning.
@ IdRole
A notification identifier. This can be uint notification ID or string application job source.
@ DesktopEntryRole
The desktop entry (without .desktop suffix, e.g. org.kde.spectacle) of the application that sent the ...
@ IsGroupRole
Whether the item is a group.
@ WasAddedDuringInhibitionRole
Whether the notification was added while inhibition was active.
@ ExpiredRole
The notification timed out and closed. Actions on it cannot be invoked anymore.
@ CreatedRole
When the notification was first created.
@ ClosableRole
Whether the item can be closed. Notifications are always closable, jobs are only when in JobStateStop...
@ TypeRole
The type of model entry, either NotificationType or JobType.
@ PercentageRole
The percentage of the job. Use jobsPercentage to get a global percentage for all jobs.
QDateTime lastRead
The time when the user last could read the notifications.
Q_INVOKABLE void startTimeout(const QModelIndex &idx)
Start automatic timeout of notifications.
QStringList blacklistedDesktopEntries
A list of desktop entries for which no notifications should be shown.
Q_INVOKABLE QModelIndex groupIndex(const QModelIndex &idx) const
Returns a model index pointing to the group of a notification.
SortMode sortMode
The sort mode for notifications.
Q_INVOKABLE void killJob(const QModelIndex &idx)
Kill a job.
Q_INVOKABLE void suspendJob(const QModelIndex &idx)
Suspend a job.
QString i18nc(const char *context, const char *text, const TYPE &arg...)
const FMH::MODEL filterModel(const MODEL &model, const QVector< MODEL_KEY > &keys)
QVariant read(const QByteArray &data, int versionOverride=0)
QAbstractItemModel(QObject *parent)
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList< int > &roles)
virtual QModelIndex parent(const QModelIndex &index) const const=0
void rowsInserted(const QModelIndex &parent, int first, int last)
void rowsRemoved(const QModelIndex &parent, int first, int last)
QDateTime currentDateTimeUtc()
bool isValid() const const
bool contains(const AT &value) const const
bool isEmpty() const const
bool invokeMethod(QObject *context, Functor &&function, FunctorReturnType *ret)
QVariant data(int role) const const
bool isValid() const const
const QAbstractItemModel * model() const const
QModelIndex parent() const const
QObject(QObject *parent)
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
T qobject_cast(QObject *object)
QSortFilterProxyModel(QObject *parent)
virtual QVariant data(const QModelIndex &index, int role) const const override
virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const const
virtual Qt::ItemFlags flags(const QModelIndex &index) const const override
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const override
virtual bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const const
virtual int rowCount(const QModelIndex &parent) const const override
virtual bool setData(const QModelIndex &index, const QVariant &value, int role) override
QString number(double n, char format, int precision)
QueuedConnection
SortOrder
bool toBool() const const
QDateTime toDateTime() const const
int toInt(bool *ok) const const
QString toString() const const
uint toUInt(bool *ok) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Jan 24 2025 11:57:53 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.