7#include "notifications.h"
9#include <QConcatenateTablesProxyModel>
14#include <KDescendantsProxyModel>
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"
27#include "notification.h"
33using namespace NotificationManager;
41 void initSourceModels();
42 void initProxyModels();
46 bool showNotifications =
true;
47 bool showJobs =
false;
51 bool expandUnread =
false;
53 int activeNotificationsCount = 0;
54 int expiredNotificationsCount = 0;
56 int unreadNotificationsCount = 0;
58 int activeJobsCount = 0;
59 int jobsPercentage = 0;
66 NotificationsModel::Ptr notificationsModel;
67 JobsModel::Ptr jobsModel;
68 std::shared_ptr<Settings> settings()
const;
72 NotificationFilterProxyModel *
filterModel =
nullptr;
73 NotificationSortProxyModel *sortModel =
nullptr;
74 NotificationGroupingProxyModel *groupingModel =
nullptr;
75 NotificationGroupCollapsingProxyModel *groupCollapsingModel =
nullptr;
78 LimitedRowCountProxyModel *limiterModel =
nullptr;
89Notifications::Private::~Private()
93void Notifications::Private::initSourceModels()
95 Q_ASSERT(notificationsAndJobsModel);
98 notificationsModel = NotificationsModel::createNotificationsModel();
99 notificationsAndJobsModel->addSourceModel(notificationsModel.get());
100 connect(notificationsModel.get(), &NotificationsModel::lastReadChanged, q, [
this] {
102 Q_EMIT q->lastReadChanged();
105 notificationsAndJobsModel->removeSourceModel(notificationsModel.get());
106 disconnect(notificationsModel.get(),
nullptr, q,
nullptr);
107 notificationsModel =
nullptr;
111 jobsModel = JobsModel::createJobsModel();
112 notificationsAndJobsModel->addSourceModel(jobsModel.get());
114 }
else if (!
showJobs && jobsModel) {
115 notificationsAndJobsModel->removeSourceModel(jobsModel.get());
120void Notifications::Private::initProxyModels()
160 if (!notificationsAndJobsModel) {
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);
172 filterModel->setSourceModel(notificationsAndJobsModel);
182 Q_UNUSED(bottomRight);
191 sortModel =
new NotificationSortProxyModel(q);
192 connect(sortModel, &NotificationSortProxyModel::sortModeChanged, q, &Notifications::sortModeChanged);
193 connect(sortModel, &NotificationSortProxyModel::sortOrderChanged, q, &Notifications::sortOrderChanged);
197 limiterModel =
new LimitedRowCountProxyModel(q);
198 connect(limiterModel, &LimitedRowCountProxyModel::limitChanged, q, &Notifications::limitChanged);
201 if (
groupMode == GroupApplicationsFlat) {
202 if (!groupingModel) {
203 groupingModel =
new NotificationGroupingProxyModel(q);
204 groupingModel->setSourceModel(filterModel);
207 if (!groupCollapsingModel) {
208 groupCollapsingModel =
new NotificationGroupCollapsingProxyModel(q);
211 groupCollapsingModel->setLastRead(q->lastRead());
212 groupCollapsingModel->setSourceModel(groupingModel);
215 sortModel->setSourceModel(groupCollapsingModel);
218 flattenModel->setSourceModel(sortModel);
220 limiterModel->setSourceModel(flattenModel);
222 sortModel->setSourceModel(filterModel);
223 limiterModel->setSourceModel(sortModel);
225 flattenModel =
nullptr;
226 delete groupingModel;
227 groupingModel =
nullptr;
230 q->setSourceModel(limiterModel);
233void Notifications::Private::updateCount()
240 int totalPercentage = 0;
245 for (
int i = 0; i <
filterModel->rowCount(); ++i) {
255 if (!active && !read) {
261 if (notificationsModel && date > notificationsModel->lastRead()) {
277 Q_EMIT q->activeNotificationsCountChanged();
281 Q_EMIT q->expiredNotificationsCountChanged();
285 Q_EMIT q->unreadNotificationsCountChanged();
289 Q_EMIT q->activeJobsCountChanged();
292 const int percentage = (jobs > 0 ? totalPercentage / jobs : 0);
295 Q_EMIT q->jobsPercentageChanged();
302bool Notifications::Private::isGroup(
const QModelIndex &idx)
307uint Notifications::Private::notificationId(
const QModelIndex &idx)
317 notificationsAndJobsModel,
321 groupCollapsingModel,
327 while (resolvedIdx.
isValid() && resolvedIdx.
model() != q) {
328 const auto *idxModel = resolvedIdx.
model();
338 if (proxyModel->sourceModel() == idxModel) {
339 resolvedIdx = proxyModel->mapFromSource(resolvedIdx);
344 if (idxModel == notificationsModel.get() || idxModel == jobsModel.get()) {
345 resolvedIdx = concatenateModel->mapFromSource(resolvedIdx);
359std::shared_ptr<Settings> Notifications::Private::settings()
const
361 static std::weak_ptr<Settings> s_instance;
362 if (!s_instance.expired()) {
363 std::shared_ptr<Settings> ptr(
new Settings());
367 return s_instance.lock();
372 , d(new Private(this))
377 d->initProxyModels();
383 d->initSourceModels();
388Notifications::~Notifications() =
default;
390void Notifications::classBegin()
394void Notifications::componentComplete()
397 d->initSourceModels();
402 return d->limiterModel->limit();
405void Notifications::setLimit(
int limit)
407 d->limiterModel->setLimit(
limit);
412 return d->groupLimit;
415void Notifications::setGroupLimit(
int limit)
417 if (d->groupLimit ==
limit) {
421 d->groupLimit =
limit;
422 if (d->groupCollapsingModel) {
423 d->groupCollapsingModel->setLimit(
limit);
425 Q_EMIT groupLimitChanged();
430 return d->expandUnread;
433void Notifications::setExpandUnread(
bool expand)
435 if (d->expandUnread == expand) {
439 d->expandUnread = expand;
440 if (d->groupCollapsingModel) {
441 d->groupCollapsingModel->setExpandUnread(expand);
443 Q_EMIT expandUnreadChanged();
448 return d->notificationsModel ? d->notificationsModel->window() :
nullptr;
451void Notifications::setWindow(
QWindow *window)
453 if (d->notificationsModel) {
454 d->notificationsModel->setWindow(
window);
456 qCWarning(NOTIFICATIONMANAGER) <<
"Setting window before initialising the model" <<
this <<
window;
462 return d->filterModel->showExpired();
465void Notifications::setShowExpired(
bool show)
467 d->filterModel->setShowExpired(show);
472 return d->filterModel->showDismissed();
475void Notifications::setShowDismissed(
bool show)
477 d->filterModel->setShowDismissed(show);
482 return d->filterModel->blacklistedDesktopEntries();
485void Notifications::setBlacklistedDesktopEntries(
const QStringList &blacklist)
487 d->filterModel->setBlackListedDesktopEntries(blacklist);
492 return d->filterModel->blacklistedNotifyRcNames();
495void Notifications::setBlacklistedNotifyRcNames(
const QStringList &blacklist)
497 d->filterModel->setBlacklistedNotifyRcNames(blacklist);
502 return d->filterModel->whitelistedDesktopEntries();
505void Notifications::setWhitelistedDesktopEntries(
const QStringList &whitelist)
507 d->filterModel->setWhiteListedDesktopEntries(whitelist);
512 return d->filterModel->whitelistedNotifyRcNames();
515void Notifications::setWhitelistedNotifyRcNames(
const QStringList &whitelist)
517 d->filterModel->setWhitelistedNotifyRcNames(whitelist);
522 return d->showNotifications;
525void Notifications::setShowNotifications(
bool show)
527 if (d->showNotifications == show) {
531 d->showNotifications = show;
532 d->initSourceModels();
533 Q_EMIT showNotificationsChanged();
541void Notifications::setShowJobs(
bool show)
543 if (d->showJobs == show) {
548 d->initSourceModels();
554 return d->filterModel->urgencies();
557void Notifications::setUrgencies(Urgencies urgencies)
564 return d->sortModel->sortMode();
567void Notifications::setSortMode(SortMode sortMode)
569 d->sortModel->setSortMode(
sortMode);
574 return d->sortModel->sortOrder();
587void Notifications::setGroupMode(GroupMode groupMode)
591 d->initProxyModels();
592 Q_EMIT groupModeChanged();
603 return d->activeNotificationsCount;
608 return d->expiredNotificationsCount;
613 if (d->notificationsModel) {
614 return d->notificationsModel->lastRead();
619void Notifications::setLastRead(
const QDateTime &lastRead)
622 if (d->notificationsModel) {
623 d->notificationsModel->setLastRead(
lastRead);
625 if (d->groupCollapsingModel) {
626 d->groupCollapsingModel->setLastRead(
lastRead);
630void Notifications::resetLastRead()
637 return d->unreadNotificationsCount;
642 return d->activeJobsCount;
647 return d->jobsPercentage;
659 d->notificationsModel->expire(Private::notificationId(idx));
662 d->jobsModel->expire(Utils::mapToModel(idx, d->jobsModel.get()));
672 const QModelIndex groupIdx = Utils::mapToModel(idx, d->groupingModel);
674 qCWarning(NOTIFICATIONMANAGER) <<
"Failed to find group model index for this item";
678 Q_ASSERT(groupIdx.
model() == d->groupingModel);
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);
694 d->notificationsModel->close(Private::notificationId(idx));
697 d->jobsModel->close(Utils::mapToModel(idx, d->jobsModel.get()));
706 if (!d->notificationsModel) {
711 if (Private::isGroup(idx)) {
715 d->notificationsModel->configure(desktopEntry, notifyRcName,
QString() );
719 d->notificationsModel->configure(Private::notificationId(idx));
724 if (d->notificationsModel) {
725 d->notificationsModel->invokeDefaultAction(Private::notificationId(idx), behavior);
731 if (d->notificationsModel) {
732 d->notificationsModel->invokeAction(Private::notificationId(idx), actionId, behavior);
738 if (d->notificationsModel) {
739 d->notificationsModel->reply(Private::notificationId(idx), text, behavior);
750 if (d->notificationsModel) {
751 d->notificationsModel->startTimeout(notificationId);
757 if (d->notificationsModel) {
758 d->notificationsModel->stopTimeout(Private::notificationId(idx));
765 d->jobsModel->suspend(Utils::mapToModel(idx, d->jobsModel.get()));
772 d->jobsModel->resume(Utils::mapToModel(idx, d->jobsModel.get()));
779 d->jobsModel->kill(Utils::mapToModel(idx, d->jobsModel.get()));
785 if (d->notificationsModel) {
786 d->notificationsModel->clear(
flags);
789 d->jobsModel->clear(
flags);
800 QModelIndex groupingIdx = Utils::mapToModel(idx, d->groupingModel);
801 return d->mapFromModel(groupingIdx.
parent());
804 qCWarning(NOTIFICATIONMANAGER) <<
"Cannot get group index for item that isn't a group or inside one";
808void Notifications::collapseAllGroups()
810 if (d->groupCollapsingModel) {
811 d->groupCollapsingModel->collapseAll();
825bool Notifications::filterAcceptsRow(
int source_row,
const QModelIndex &source_parent)
const
835int Notifications::rowCount(
const QModelIndex &parent)
const
842 return Utils::roleNames();
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.
Type
The type of model item.
@ 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.
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
QVariant data(int role) const const
bool isValid() const const
const QAbstractItemModel * model() const const
QModelIndex parent() const const
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
bool toBool() const const
QDateTime toDateTime() const const
int toInt(bool *ok) const const
QString toString() const const
uint toUInt(bool *ok) const const