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
16#include "limitedrowcountproxymodel_p.h"
17#include "notificationfilterproxymodel_p.h"
18#include "notificationgroupcollapsingproxymodel_p.h"
19#include "notificationgroupingproxymodel_p.h"
20#include "notificationsmodel.h"
21#include "notificationsortproxymodel_p.h"
22
23#include "jobsmodel.h"
24
25#include "settings.h"
26
27#include "notification.h"
28
29#include "utils_p.h"
30
31#include "debug.h"
32
33using namespace NotificationManager;
34
35class Q_DECL_HIDDEN Notifications::Private
36{
37public:
38 explicit Private(Notifications *q);
39 ~Private();
40
41 void initSourceModels();
42 void initProxyModels();
43
44 void updateCount();
45
46 bool showNotifications = true;
47 bool showJobs = false;
48
49 Notifications::GroupMode groupMode = Notifications::GroupDisabled;
50 int groupLimit = 0;
51 bool expandUnread = false;
52
53 int activeNotificationsCount = 0;
54 int expiredNotificationsCount = 0;
55
56 int unreadNotificationsCount = 0;
57
58 int activeJobsCount = 0;
59 int jobsPercentage = 0;
60
61 static bool isGroup(const QModelIndex &idx);
62 static uint notificationId(const QModelIndex &idx);
63 QModelIndex mapFromModel(const QModelIndex &idx) const;
64
65 // NOTE when you add or re-arrange models make sure to update mapFromModel()!
66 NotificationsModel::Ptr notificationsModel;
67 JobsModel::Ptr jobsModel;
68 std::shared_ptr<Settings> settings() const;
69
70 QConcatenateTablesProxyModel *notificationsAndJobsModel = nullptr;
71
72 NotificationFilterProxyModel *filterModel = nullptr;
73 NotificationSortProxyModel *sortModel = nullptr;
74 NotificationGroupingProxyModel *groupingModel = nullptr;
75 NotificationGroupCollapsingProxyModel *groupCollapsingModel = nullptr;
76 KDescendantsProxyModel *flattenModel = nullptr;
77
78 LimitedRowCountProxyModel *limiterModel = nullptr;
79
80private:
81 Notifications *const q;
82};
83
84Notifications::Private::Private(Notifications *q)
85 : q(q)
86{
87}
88
89Notifications::Private::~Private()
90{
91}
92
93void Notifications::Private::initSourceModels()
94{
95 Q_ASSERT(notificationsAndJobsModel); // initProxyModels must be called before initSourceModels
96
97 if (showNotifications && !notificationsModel) {
98 notificationsModel = NotificationsModel::createNotificationsModel();
99 notificationsAndJobsModel->addSourceModel(notificationsModel.get());
100 connect(notificationsModel.get(), &NotificationsModel::lastReadChanged, q, [this] {
101 updateCount();
102 Q_EMIT q->lastReadChanged();
103 });
104 } else if (!showNotifications && notificationsModel) {
105 notificationsAndJobsModel->removeSourceModel(notificationsModel.get());
106 disconnect(notificationsModel.get(), nullptr, q, nullptr); // disconnect all
107 notificationsModel = nullptr;
108 }
109
110 if (showJobs && !jobsModel) {
111 jobsModel = JobsModel::createJobsModel();
112 notificationsAndJobsModel->addSourceModel(jobsModel.get());
113 jobsModel->init();
114 } else if (!showJobs && jobsModel) {
115 notificationsAndJobsModel->removeSourceModel(jobsModel.get());
116 jobsModel = nullptr;
117 }
118}
119
120void Notifications::Private::initProxyModels()
121{
122 /* The data flow is as follows:
123 * NOTE when you add or re-arrange models make sure to update mapFromModel()!
124 *
125 * NotificationsModel JobsModel
126 * \\ /
127 * \\ /
128 * QConcatenateTablesProxyModel
129 * |||
130 * |||
131 * NotificationFilterProxyModel
132 * (filters by urgency, whitelist, etc)
133 * |
134 * |
135 * NotificationSortProxyModel
136 * (sorts by urgency, date, etc)
137 * |
138 * --- BEGIN: Only when grouping is enabled ---
139 * |
140 * NotificationGroupingProxyModel
141 * (turns list into tree grouped by app)
142 * //\\
143 * //\\
144 * NotificationGroupCollapsingProxyModel
145 * (limits number of tree leaves for expand/collapse feature)
146 * /\
147 * /\
148 * KDescendantsProxyModel
149 * (flattens tree back into a list for consumption in ListView)
150 * |
151 * --- END: Only when grouping is enabled ---
152 * |
153 * LimitedRowCountProxyModel
154 * (limits the total number of items in the model)
155 * |
156 * |
157 * \o/ <- Happy user seeing their notifications
158 */
159
160 if (!notificationsAndJobsModel) {
161 notificationsAndJobsModel = new QConcatenateTablesProxyModel(q);
162 }
163
164 if (!filterModel) {
165 filterModel = new NotificationFilterProxyModel();
166 connect(filterModel, &NotificationFilterProxyModel::urgenciesChanged, q, &Notifications::urgenciesChanged);
167 connect(filterModel, &NotificationFilterProxyModel::showExpiredChanged, q, &Notifications::showExpiredChanged);
168 connect(filterModel, &NotificationFilterProxyModel::showDismissedChanged, q, &Notifications::showDismissedChanged);
169 connect(filterModel, &NotificationFilterProxyModel::blacklistedDesktopEntriesChanged, q, &Notifications::blacklistedDesktopEntriesChanged);
170 connect(filterModel, &NotificationFilterProxyModel::blacklistedNotifyRcNamesChanged, q, &Notifications::blacklistedNotifyRcNamesChanged);
171
172 filterModel->setSourceModel(notificationsAndJobsModel);
173
174 connect(filterModel, &QAbstractItemModel::rowsInserted, q, [this] {
175 updateCount();
176 });
177 connect(filterModel, &QAbstractItemModel::rowsRemoved, q, [this] {
178 updateCount();
179 });
180 connect(filterModel, &QAbstractItemModel::dataChanged, q, [this](const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList<int> &roles) {
181 Q_UNUSED(topLeft);
182 Q_UNUSED(bottomRight);
185 updateCount();
186 }
187 });
188 }
189
190 if (!sortModel) {
191 sortModel = new NotificationSortProxyModel(q);
192 connect(sortModel, &NotificationSortProxyModel::sortModeChanged, q, &Notifications::sortModeChanged);
193 connect(sortModel, &NotificationSortProxyModel::sortOrderChanged, q, &Notifications::sortOrderChanged);
194 }
195
196 if (!limiterModel) {
197 limiterModel = new LimitedRowCountProxyModel(q);
198 connect(limiterModel, &LimitedRowCountProxyModel::limitChanged, q, &Notifications::limitChanged);
199 }
200
201 if (groupMode == GroupApplicationsFlat) {
202 if (!groupingModel) {
203 groupingModel = new NotificationGroupingProxyModel(q);
204 groupingModel->setSourceModel(filterModel);
205 }
206
207 if (!groupCollapsingModel) {
208 groupCollapsingModel = new NotificationGroupCollapsingProxyModel(q);
209 groupCollapsingModel->setLimit(groupLimit);
210 groupCollapsingModel->setExpandUnread(expandUnread);
211 groupCollapsingModel->setLastRead(q->lastRead());
212 groupCollapsingModel->setSourceModel(groupingModel);
213 }
214
215 sortModel->setSourceModel(groupCollapsingModel);
216
217 flattenModel = new KDescendantsProxyModel(q);
218 flattenModel->setSourceModel(sortModel);
219
220 limiterModel->setSourceModel(flattenModel);
221 } else {
222 sortModel->setSourceModel(filterModel);
223 limiterModel->setSourceModel(sortModel);
224 delete flattenModel;
225 flattenModel = nullptr;
226 delete groupingModel;
227 groupingModel = nullptr;
228 }
229
230 q->setSourceModel(limiterModel);
231}
232
233void Notifications::Private::updateCount()
234{
235 int active = 0;
236 int expired = 0;
237 int unread = 0;
238
239 int jobs = 0;
240 int totalPercentage = 0;
241
242 // We want to get the numbers after main filtering (urgencies, whitelists, etc)
243 // but before any limiting or group limiting, hence asking the filterModel for advice
244 // at which point notifications and jobs also have already been merged
245 for (int i = 0; i < filterModel->rowCount(); ++i) {
246 const QModelIndex idx = filterModel->index(i, 0);
247
249 ++expired;
250 } else {
251 ++active;
252 }
253
254 const bool read = idx.data(Notifications::ReadRole).toBool();
255 if (!active && !read) {
257 if (!date.isValid()) {
259 }
260
261 if (notificationsModel && date > notificationsModel->lastRead()) {
262 ++unread;
263 }
264 }
265
268 ++jobs;
269
270 totalPercentage += idx.data(Notifications::PercentageRole).toInt();
271 }
272 }
273 }
274
275 if (activeNotificationsCount != active) {
277 Q_EMIT q->activeNotificationsCountChanged();
278 }
279 if (expiredNotificationsCount != expired) {
281 Q_EMIT q->expiredNotificationsCountChanged();
282 }
283 if (unreadNotificationsCount != unread) {
285 Q_EMIT q->unreadNotificationsCountChanged();
286 }
287 if (activeJobsCount != jobs) {
288 activeJobsCount = jobs;
289 Q_EMIT q->activeJobsCountChanged();
290 }
291
292 const int percentage = (jobs > 0 ? totalPercentage / jobs : 0);
293 if (jobsPercentage != percentage) {
294 jobsPercentage = percentage;
295 Q_EMIT q->jobsPercentageChanged();
296 }
297
298 // TODO don't Q_EMIT in dataChanged
299 Q_EMIT q->countChanged();
300}
301
302bool Notifications::Private::isGroup(const QModelIndex &idx)
303{
305}
306
307uint Notifications::Private::notificationId(const QModelIndex &idx)
308{
309 return idx.data(Notifications::IdRole).toUInt();
310}
311
312QModelIndex Notifications::Private::mapFromModel(const QModelIndex &idx) const
313{
314 QModelIndex resolvedIdx = idx;
315
316 QAbstractItemModel *models[] = {
317 notificationsAndJobsModel,
319 sortModel,
320 groupingModel,
321 groupCollapsingModel,
322 flattenModel,
323 limiterModel,
324 };
325
326 // TODO can we do this with a generic loop like mapFromModel
327 while (resolvedIdx.isValid() && resolvedIdx.model() != q) {
328 const auto *idxModel = resolvedIdx.model();
329
330 // HACK try to find the model that uses the index' model as source
331 bool found = false;
332 for (QAbstractItemModel *model : models) {
333 if (!model) {
334 continue;
335 }
336
337 if (auto *proxyModel = qobject_cast<QAbstractProxyModel *>(model)) {
338 if (proxyModel->sourceModel() == idxModel) {
339 resolvedIdx = proxyModel->mapFromSource(resolvedIdx);
340 found = true;
341 break;
342 }
343 } else if (auto *concatenateModel = qobject_cast<QConcatenateTablesProxyModel *>(model)) {
344 if (idxModel == notificationsModel.get() || idxModel == jobsModel.get()) {
345 resolvedIdx = concatenateModel->mapFromSource(resolvedIdx);
346 found = true;
347 break;
348 }
349 }
350 }
351
352 if (!found) {
353 break;
354 }
355 }
356 return resolvedIdx;
357}
358
359std::shared_ptr<Settings> Notifications::Private::settings() const
360{
361 static std::weak_ptr<Settings> s_instance;
362 if (!s_instance.expired()) {
363 std::shared_ptr<Settings> ptr(new Settings());
364 s_instance = ptr;
365 return ptr;
366 }
367 return s_instance.lock();
368}
369
370Notifications::Notifications(QObject *parent)
372 , d(new Private(this))
373{
374 // The proxy models are always the same, just with different
375 // properties set whereas we want to avoid loading a source model
376 // e.g. notifications or jobs when we're not actually using them
377 d->initProxyModels();
378
379 // init source models when used from C++
381 this,
382 [this] {
383 d->initSourceModels();
384 },
386}
387
388Notifications::~Notifications() = default;
389
390void Notifications::classBegin()
391{
392}
393
394void Notifications::componentComplete()
395{
396 // init source models when used from QML
397 d->initSourceModels();
398}
399
400int Notifications::limit() const
401{
402 return d->limiterModel->limit();
403}
404
405void Notifications::setLimit(int limit)
406{
407 d->limiterModel->setLimit(limit);
408}
409
411{
412 return d->groupLimit;
413}
414
415void Notifications::setGroupLimit(int limit)
416{
417 if (d->groupLimit == limit) {
418 return;
419 }
420
421 d->groupLimit = limit;
422 if (d->groupCollapsingModel) {
423 d->groupCollapsingModel->setLimit(limit);
424 }
425 Q_EMIT groupLimitChanged();
426}
427
429{
430 return d->expandUnread;
431}
432
433void Notifications::setExpandUnread(bool expand)
434{
435 if (d->expandUnread == expand) {
436 return;
437 }
438
439 d->expandUnread = expand;
440 if (d->groupCollapsingModel) {
441 d->groupCollapsingModel->setExpandUnread(expand);
442 }
443 Q_EMIT expandUnreadChanged();
444}
445
447{
448 return d->notificationsModel ? d->notificationsModel->window() : nullptr;
449}
450
451void Notifications::setWindow(QWindow *window)
452{
453 if (d->notificationsModel) {
454 d->notificationsModel->setWindow(window);
455 } else {
456 qCWarning(NOTIFICATIONMANAGER) << "Setting window before initialising the model" << this << window;
457 }
458}
459
461{
462 return d->filterModel->showExpired();
463}
464
465void Notifications::setShowExpired(bool show)
466{
467 d->filterModel->setShowExpired(show);
468}
469
471{
472 return d->filterModel->showDismissed();
473}
474
475void Notifications::setShowDismissed(bool show)
476{
477 d->filterModel->setShowDismissed(show);
478}
479
481{
482 return d->filterModel->blacklistedDesktopEntries();
483}
484
485void Notifications::setBlacklistedDesktopEntries(const QStringList &blacklist)
486{
487 d->filterModel->setBlackListedDesktopEntries(blacklist);
488}
489
491{
492 return d->filterModel->blacklistedNotifyRcNames();
493}
494
495void Notifications::setBlacklistedNotifyRcNames(const QStringList &blacklist)
496{
497 d->filterModel->setBlacklistedNotifyRcNames(blacklist);
498}
499
501{
502 return d->filterModel->whitelistedDesktopEntries();
503}
504
505void Notifications::setWhitelistedDesktopEntries(const QStringList &whitelist)
506{
507 d->filterModel->setWhiteListedDesktopEntries(whitelist);
508}
509
511{
512 return d->filterModel->whitelistedNotifyRcNames();
513}
514
515void Notifications::setWhitelistedNotifyRcNames(const QStringList &whitelist)
516{
517 d->filterModel->setWhitelistedNotifyRcNames(whitelist);
518}
519
521{
522 return d->showNotifications;
523}
524
525void Notifications::setShowNotifications(bool show)
526{
527 if (d->showNotifications == show) {
528 return;
529 }
530
531 d->showNotifications = show;
532 d->initSourceModels();
533 Q_EMIT showNotificationsChanged();
534}
535
536bool Notifications::showJobs() const
537{
538 return d->showJobs;
539}
540
541void Notifications::setShowJobs(bool show)
542{
543 if (d->showJobs == show) {
544 return;
545 }
546
547 d->showJobs = show;
548 d->initSourceModels();
549 Q_EMIT showJobsChanged();
550}
551
553{
554 return d->filterModel->urgencies();
555}
556
557void Notifications::setUrgencies(Urgencies urgencies)
558{
559 d->filterModel->setUrgencies(urgencies);
560}
561
563{
564 return d->sortModel->sortMode();
565}
566
567void Notifications::setSortMode(SortMode sortMode)
568{
569 d->sortModel->setSortMode(sortMode);
570}
571
573{
574 return d->sortModel->sortOrder();
575}
576
577void Notifications::setSortOrder(Qt::SortOrder sortOrder)
578{
579 d->sortModel->setSortOrder(sortOrder);
580}
581
583{
584 return d->groupMode;
585}
586
587void Notifications::setGroupMode(GroupMode groupMode)
588{
589 if (d->groupMode != groupMode) {
590 d->groupMode = groupMode;
591 d->initProxyModels();
592 Q_EMIT groupModeChanged();
593 }
594}
595
596int Notifications::count() const
597{
598 return rowCount(QModelIndex());
599}
600
602{
603 return d->activeNotificationsCount;
604}
605
607{
608 return d->expiredNotificationsCount;
609}
610
612{
613 if (d->notificationsModel) {
614 return d->notificationsModel->lastRead();
615 }
616 return QDateTime();
617}
618
619void Notifications::setLastRead(const QDateTime &lastRead)
620{
621 // TODO jobs could also be unread?
622 if (d->notificationsModel) {
623 d->notificationsModel->setLastRead(lastRead);
624 }
625 if (d->groupCollapsingModel) {
626 d->groupCollapsingModel->setLastRead(lastRead);
627 }
628}
629
630void Notifications::resetLastRead()
631{
632 setLastRead(QDateTime::currentDateTimeUtc());
633}
634
636{
637 return d->unreadNotificationsCount;
638}
639
641{
642 return d->activeJobsCount;
643}
644
646{
647 return d->jobsPercentage;
648}
649
654
656{
657 switch (static_cast<Notifications::Type>(idx.data(Notifications::TypeRole).toInt())) {
659 d->notificationsModel->expire(Private::notificationId(idx));
660 break;
662 d->jobsModel->expire(Utils::mapToModel(idx, d->jobsModel.get()));
663 break;
664 default:
665 Q_UNREACHABLE();
666 }
667}
668
670{
672 const QModelIndex groupIdx = Utils::mapToModel(idx, d->groupingModel);
673 if (!groupIdx.isValid()) {
674 qCWarning(NOTIFICATIONMANAGER) << "Failed to find group model index for this item";
675 return;
676 }
677
678 Q_ASSERT(groupIdx.model() == d->groupingModel);
679
680 const int childCount = d->groupingModel->rowCount(groupIdx);
681 for (int i = childCount - 1; i >= 0; --i) {
682 const QModelIndex childIdx = d->groupingModel->index(i, 0, groupIdx);
683 close(childIdx);
684 }
685 return;
686 }
687
689 return;
690 }
691
692 switch (static_cast<Notifications::Type>(idx.data(Notifications::TypeRole).toInt())) {
694 d->notificationsModel->close(Private::notificationId(idx));
695 break;
697 d->jobsModel->close(Utils::mapToModel(idx, d->jobsModel.get()));
698 break;
699 default:
700 Q_UNREACHABLE();
701 }
702}
703
705{
706 if (!d->notificationsModel) {
707 return;
708 }
709
710 // For groups just configure the application, not the individual event
711 if (Private::isGroup(idx)) {
712 const QString desktopEntry = idx.data(Notifications::DesktopEntryRole).toString();
713 const QString notifyRcName = idx.data(Notifications::NotifyRcNameRole).toString();
714
715 d->notificationsModel->configure(desktopEntry, notifyRcName, QString() /*eventId*/);
716 return;
717 }
718
719 d->notificationsModel->configure(Private::notificationId(idx));
720}
721
722void Notifications::invokeDefaultAction(const QModelIndex &idx, InvokeBehavior behavior)
723{
724 if (d->notificationsModel) {
725 d->notificationsModel->invokeDefaultAction(Private::notificationId(idx), behavior);
726 }
727}
728
729void Notifications::invokeAction(const QModelIndex &idx, const QString &actionId, InvokeBehavior behavior)
730{
731 if (d->notificationsModel) {
732 d->notificationsModel->invokeAction(Private::notificationId(idx), actionId, behavior);
733 }
734}
735
736void Notifications::reply(const QModelIndex &idx, const QString &text, InvokeBehavior behavior)
737{
738 if (d->notificationsModel) {
739 d->notificationsModel->reply(Private::notificationId(idx), text, behavior);
740 }
741}
742
744{
745 startTimeout(Private::notificationId(idx));
746}
747
748void Notifications::startTimeout(uint notificationId)
749{
750 if (d->notificationsModel) {
751 d->notificationsModel->startTimeout(notificationId);
752 }
753}
754
756{
757 if (d->notificationsModel) {
758 d->notificationsModel->stopTimeout(Private::notificationId(idx));
759 }
760}
761
763{
764 if (d->jobsModel) {
765 d->jobsModel->suspend(Utils::mapToModel(idx, d->jobsModel.get()));
766 }
767}
768
770{
771 if (d->jobsModel) {
772 d->jobsModel->resume(Utils::mapToModel(idx, d->jobsModel.get()));
773 }
774}
775
777{
778 if (d->jobsModel) {
779 d->jobsModel->kill(Utils::mapToModel(idx, d->jobsModel.get()));
780 }
781}
782
784{
785 if (d->notificationsModel) {
786 d->notificationsModel->clear(flags);
787 }
788 if (d->jobsModel) {
789 d->jobsModel->clear(flags);
790 }
791}
792
794{
796 return idx;
797 }
798
800 QModelIndex groupingIdx = Utils::mapToModel(idx, d->groupingModel);
801 return d->mapFromModel(groupingIdx.parent());
802 }
803
804 qCWarning(NOTIFICATIONMANAGER) << "Cannot get group index for item that isn't a group or inside one";
805 return QModelIndex();
806}
807
808void Notifications::collapseAllGroups()
809{
810 if (d->groupCollapsingModel) {
811 d->groupCollapsingModel->collapseAll();
812 }
813}
814
815QVariant Notifications::data(const QModelIndex &index, int role) const
816{
818}
819
820bool Notifications::setData(const QModelIndex &index, const QVariant &value, int role)
821{
822 return QSortFilterProxyModel::setData(index, value, role);
823}
824
825bool Notifications::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
826{
827 return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
828}
829
830bool Notifications::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
831{
832 return QSortFilterProxyModel::lessThan(source_left, source_right);
833}
834
835int Notifications::rowCount(const QModelIndex &parent) const
836{
838}
839
840QHash<int, QByteArray> Notifications::roleNames() const
841{
842 return Utils::roleNames();
843}
844
845#include "moc_notifications.cpp"
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.
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.
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.
@ 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.
Notification settings and state.
Definition settings.h:35
const FMH::MODEL filterModel(const MODEL &model, const QVector< MODEL_KEY > &keys)
QVariant read(const QByteArray &data, int versionOverride=0)
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList< int > &roles)
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
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
QObject * parent() const const
T qobject_cast(QObject *object)
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
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-2024 The KDE developers.
Generated on Sat Dec 21 2024 16:58:38 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.