File indexing completed on 2024-04-28 16:54:36
0001 /* 0002 SPDX-FileCopyrightText: 2019 Kai Uwe Broulik <kde@privat.broulik.de> 0003 0004 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0005 */ 0006 0007 #include "notifications.h" 0008 0009 #include <QConcatenateTablesProxyModel> 0010 #include <QDebug> 0011 #include <QMetaEnum> 0012 #include <QSharedPointer> 0013 0014 #include <KDescendantsProxyModel> 0015 0016 #include "limitedrowcountproxymodel_p.h" 0017 #include "notificationfilterproxymodel_p.h" 0018 #include "notificationgroupcollapsingproxymodel_p.h" 0019 #include "notificationgroupingproxymodel_p.h" 0020 #include "notificationsmodel.h" 0021 #include "notificationsortproxymodel_p.h" 0022 0023 #include "jobsmodel.h" 0024 0025 #include "settings.h" 0026 0027 #include "notification.h" 0028 0029 #include "utils_p.h" 0030 0031 #include "debug.h" 0032 0033 using namespace NotificationManager; 0034 0035 class Q_DECL_HIDDEN Notifications::Private 0036 { 0037 public: 0038 explicit Private(Notifications *q); 0039 ~Private(); 0040 0041 void initSourceModels(); 0042 void initProxyModels(); 0043 0044 void updateCount(); 0045 0046 bool showNotifications = true; 0047 bool showJobs = false; 0048 0049 Notifications::GroupMode groupMode = Notifications::GroupDisabled; 0050 int groupLimit = 0; 0051 bool expandUnread = false; 0052 0053 int activeNotificationsCount = 0; 0054 int expiredNotificationsCount = 0; 0055 0056 int unreadNotificationsCount = 0; 0057 0058 int activeJobsCount = 0; 0059 int jobsPercentage = 0; 0060 0061 static bool isGroup(const QModelIndex &idx); 0062 static uint notificationId(const QModelIndex &idx); 0063 QModelIndex mapFromModel(const QModelIndex &idx) const; 0064 0065 // NOTE when you add or re-arrange models make sure to update mapFromModel()! 0066 NotificationsModel::Ptr notificationsModel; 0067 JobsModel::Ptr jobsModel; 0068 QSharedPointer<Settings> settings() const; 0069 0070 QConcatenateTablesProxyModel *notificationsAndJobsModel = nullptr; 0071 0072 NotificationFilterProxyModel *filterModel = nullptr; 0073 NotificationSortProxyModel *sortModel = nullptr; 0074 NotificationGroupingProxyModel *groupingModel = nullptr; 0075 NotificationGroupCollapsingProxyModel *groupCollapsingModel = nullptr; 0076 KDescendantsProxyModel *flattenModel = nullptr; 0077 0078 LimitedRowCountProxyModel *limiterModel = nullptr; 0079 0080 private: 0081 Notifications *q; 0082 }; 0083 0084 Notifications::Private::Private(Notifications *q) 0085 : q(q) 0086 { 0087 } 0088 0089 Notifications::Private::~Private() 0090 { 0091 } 0092 0093 void Notifications::Private::initSourceModels() 0094 { 0095 Q_ASSERT(notificationsAndJobsModel); // initProxyModels must be called before initSourceModels 0096 0097 if (showNotifications && !notificationsModel) { 0098 notificationsModel = NotificationsModel::createNotificationsModel(); 0099 notificationsAndJobsModel->addSourceModel(notificationsModel.data()); 0100 connect(notificationsModel.data(), &NotificationsModel::windowChanged, q, &Notifications::windowChanged); 0101 connect(notificationsModel.data(), &NotificationsModel::lastReadChanged, q, [this] { 0102 updateCount(); 0103 Q_EMIT q->lastReadChanged(); 0104 }); 0105 } else if (!showNotifications && notificationsModel) { 0106 notificationsAndJobsModel->removeSourceModel(notificationsModel.data()); 0107 disconnect(notificationsModel.data(), nullptr, q, nullptr); // disconnect all 0108 notificationsModel = nullptr; 0109 } 0110 0111 if (showJobs && !jobsModel) { 0112 jobsModel = JobsModel::createJobsModel(); 0113 notificationsAndJobsModel->addSourceModel(jobsModel.data()); 0114 jobsModel->init(); 0115 } else if (!showJobs && jobsModel) { 0116 notificationsAndJobsModel->removeSourceModel(jobsModel.data()); 0117 jobsModel = nullptr; 0118 } 0119 } 0120 0121 void Notifications::Private::initProxyModels() 0122 { 0123 /* The data flow is as follows: 0124 * NOTE when you add or re-arrange models make sure to update mapFromModel()! 0125 * 0126 * NotificationsModel JobsModel 0127 * \\ / 0128 * \\ / 0129 * QConcatenateTablesProxyModel 0130 * ||| 0131 * ||| 0132 * NotificationFilterProxyModel 0133 * (filters by urgency, whitelist, etc) 0134 * | 0135 * | 0136 * NotificationSortProxyModel 0137 * (sorts by urgency, date, etc) 0138 * | 0139 * --- BEGIN: Only when grouping is enabled --- 0140 * | 0141 * NotificationGroupingProxyModel 0142 * (turns list into tree grouped by app) 0143 * //\\ 0144 * //\\ 0145 * NotificationGroupCollapsingProxyModel 0146 * (limits number of tree leaves for expand/collapse feature) 0147 * /\ 0148 * /\ 0149 * KDescendantsProxyModel 0150 * (flattens tree back into a list for consumption in ListView) 0151 * | 0152 * --- END: Only when grouping is enabled --- 0153 * | 0154 * LimitedRowCountProxyModel 0155 * (limits the total number of items in the model) 0156 * | 0157 * | 0158 * \o/ <- Happy user seeing their notifications 0159 */ 0160 0161 if (!notificationsAndJobsModel) { 0162 notificationsAndJobsModel = new QConcatenateTablesProxyModel(q); 0163 } 0164 0165 if (!filterModel) { 0166 filterModel = new NotificationFilterProxyModel(); 0167 connect(filterModel, &NotificationFilterProxyModel::urgenciesChanged, q, &Notifications::urgenciesChanged); 0168 connect(filterModel, &NotificationFilterProxyModel::showExpiredChanged, q, &Notifications::showExpiredChanged); 0169 connect(filterModel, &NotificationFilterProxyModel::showDismissedChanged, q, &Notifications::showDismissedChanged); 0170 connect(filterModel, &NotificationFilterProxyModel::blacklistedDesktopEntriesChanged, q, &Notifications::blacklistedDesktopEntriesChanged); 0171 connect(filterModel, &NotificationFilterProxyModel::blacklistedNotifyRcNamesChanged, q, &Notifications::blacklistedNotifyRcNamesChanged); 0172 0173 filterModel->setSourceModel(notificationsAndJobsModel); 0174 0175 connect(filterModel, &QAbstractItemModel::rowsInserted, q, [this] { 0176 updateCount(); 0177 }); 0178 connect(filterModel, &QAbstractItemModel::rowsRemoved, q, [this] { 0179 updateCount(); 0180 }); 0181 connect(filterModel, 0182 &QAbstractItemModel::dataChanged, 0183 q, 0184 [this](const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles) { 0185 Q_UNUSED(topLeft); 0186 Q_UNUSED(bottomRight); 0187 if (roles.isEmpty() || roles.contains(Notifications::UpdatedRole) || roles.contains(Notifications::ExpiredRole) 0188 || roles.contains(Notifications::JobStateRole) || roles.contains(Notifications::PercentageRole) 0189 || roles.contains(Notifications::ReadRole)) { 0190 updateCount(); 0191 } 0192 }); 0193 } 0194 0195 if (!sortModel) { 0196 sortModel = new NotificationSortProxyModel(q); 0197 connect(sortModel, &NotificationSortProxyModel::sortModeChanged, q, &Notifications::sortModeChanged); 0198 connect(sortModel, &NotificationSortProxyModel::sortOrderChanged, q, &Notifications::sortOrderChanged); 0199 } 0200 0201 if (!limiterModel) { 0202 limiterModel = new LimitedRowCountProxyModel(q); 0203 connect(limiterModel, &LimitedRowCountProxyModel::limitChanged, q, &Notifications::limitChanged); 0204 } 0205 0206 if (groupMode == GroupApplicationsFlat) { 0207 if (!groupingModel) { 0208 groupingModel = new NotificationGroupingProxyModel(q); 0209 groupingModel->setSourceModel(filterModel); 0210 } 0211 0212 if (!groupCollapsingModel) { 0213 groupCollapsingModel = new NotificationGroupCollapsingProxyModel(q); 0214 groupCollapsingModel->setLimit(groupLimit); 0215 groupCollapsingModel->setExpandUnread(expandUnread); 0216 groupCollapsingModel->setLastRead(q->lastRead()); 0217 groupCollapsingModel->setSourceModel(groupingModel); 0218 } 0219 0220 sortModel->setSourceModel(groupCollapsingModel); 0221 0222 flattenModel = new KDescendantsProxyModel(q); 0223 flattenModel->setSourceModel(sortModel); 0224 0225 limiterModel->setSourceModel(flattenModel); 0226 } else { 0227 sortModel->setSourceModel(filterModel); 0228 limiterModel->setSourceModel(sortModel); 0229 delete flattenModel; 0230 flattenModel = nullptr; 0231 delete groupingModel; 0232 groupingModel = nullptr; 0233 } 0234 0235 q->setSourceModel(limiterModel); 0236 } 0237 0238 void Notifications::Private::updateCount() 0239 { 0240 int active = 0; 0241 int expired = 0; 0242 int unread = 0; 0243 0244 int jobs = 0; 0245 int totalPercentage = 0; 0246 0247 // We want to get the numbers after main filtering (urgencies, whitelists, etc) 0248 // but before any limiting or group limiting, hence asking the filterModel for advice 0249 // at which point notifications and jobs also have already been merged 0250 for (int i = 0; i < filterModel->rowCount(); ++i) { 0251 const QModelIndex idx = filterModel->index(i, 0); 0252 0253 if (idx.data(Notifications::ExpiredRole).toBool()) { 0254 ++expired; 0255 } else { 0256 ++active; 0257 } 0258 0259 const bool read = idx.data(Notifications::ReadRole).toBool(); 0260 if (!active && !read) { 0261 QDateTime date = idx.data(Notifications::UpdatedRole).toDateTime(); 0262 if (!date.isValid()) { 0263 date = idx.data(Notifications::CreatedRole).toDateTime(); 0264 } 0265 0266 if (notificationsModel && date > notificationsModel->lastRead()) { 0267 ++unread; 0268 } 0269 } 0270 0271 if (idx.data(Notifications::TypeRole).toInt() == Notifications::JobType) { 0272 if (idx.data(Notifications::JobStateRole).toInt() != Notifications::JobStateStopped) { 0273 ++jobs; 0274 0275 totalPercentage += idx.data(Notifications::PercentageRole).toInt(); 0276 } 0277 } 0278 } 0279 0280 if (activeNotificationsCount != active) { 0281 activeNotificationsCount = active; 0282 Q_EMIT q->activeNotificationsCountChanged(); 0283 } 0284 if (expiredNotificationsCount != expired) { 0285 expiredNotificationsCount = expired; 0286 Q_EMIT q->expiredNotificationsCountChanged(); 0287 } 0288 if (unreadNotificationsCount != unread) { 0289 unreadNotificationsCount = unread; 0290 Q_EMIT q->unreadNotificationsCountChanged(); 0291 } 0292 if (activeJobsCount != jobs) { 0293 activeJobsCount = jobs; 0294 Q_EMIT q->activeJobsCountChanged(); 0295 } 0296 0297 const int percentage = (jobs > 0 ? totalPercentage / jobs : 0); 0298 if (jobsPercentage != percentage) { 0299 jobsPercentage = percentage; 0300 Q_EMIT q->jobsPercentageChanged(); 0301 } 0302 0303 // TODO don't Q_EMIT in dataChanged 0304 Q_EMIT q->countChanged(); 0305 } 0306 0307 bool Notifications::Private::isGroup(const QModelIndex &idx) 0308 { 0309 return idx.data(Notifications::IsGroupRole).toBool(); 0310 } 0311 0312 uint Notifications::Private::notificationId(const QModelIndex &idx) 0313 { 0314 return idx.data(Notifications::IdRole).toUInt(); 0315 } 0316 0317 QModelIndex Notifications::Private::mapFromModel(const QModelIndex &idx) const 0318 { 0319 QModelIndex resolvedIdx = idx; 0320 0321 QAbstractItemModel *models[] = { 0322 notificationsAndJobsModel, 0323 filterModel, 0324 sortModel, 0325 groupingModel, 0326 groupCollapsingModel, 0327 flattenModel, 0328 limiterModel, 0329 }; 0330 0331 // TODO can we do this with a generic loop like mapFromModel 0332 while (resolvedIdx.isValid() && resolvedIdx.model() != q) { 0333 const auto *idxModel = resolvedIdx.model(); 0334 0335 // HACK try to find the model that uses the index' model as source 0336 bool found = false; 0337 for (QAbstractItemModel *model : models) { 0338 if (!model) { 0339 continue; 0340 } 0341 0342 if (auto *proxyModel = qobject_cast<QAbstractProxyModel *>(model)) { 0343 if (proxyModel->sourceModel() == idxModel) { 0344 resolvedIdx = proxyModel->mapFromSource(resolvedIdx); 0345 found = true; 0346 break; 0347 } 0348 } else if (auto *concatenateModel = qobject_cast<QConcatenateTablesProxyModel *>(model)) { 0349 if (idxModel == notificationsModel.data() || idxModel == jobsModel.data()) { 0350 resolvedIdx = concatenateModel->mapFromSource(resolvedIdx); 0351 found = true; 0352 break; 0353 } 0354 } 0355 } 0356 0357 if (!found) { 0358 break; 0359 } 0360 } 0361 return resolvedIdx; 0362 } 0363 0364 QSharedPointer<Settings> Notifications::Private::settings() const 0365 { 0366 static QWeakPointer<Settings> s_instance; 0367 if (!s_instance) { 0368 QSharedPointer<Settings> ptr(new Settings()); 0369 s_instance = ptr.toWeakRef(); 0370 return ptr; 0371 } 0372 return s_instance.toStrongRef(); 0373 } 0374 0375 Notifications::Notifications(QObject *parent) 0376 : QSortFilterProxyModel(parent) 0377 , d(new Private(this)) 0378 { 0379 // The proxy models are always the same, just with different 0380 // properties set whereas we want to avoid loading a source model 0381 // e.g. notifications or jobs when we're not actually using them 0382 d->initProxyModels(); 0383 0384 // init source models when used from C++ 0385 QMetaObject::invokeMethod( 0386 this, 0387 [this] { 0388 d->initSourceModels(); 0389 }, 0390 Qt::QueuedConnection); 0391 } 0392 0393 Notifications::~Notifications() = default; 0394 0395 void Notifications::classBegin() 0396 { 0397 } 0398 0399 void Notifications::componentComplete() 0400 { 0401 // init source models when used from QML 0402 d->initSourceModels(); 0403 } 0404 0405 int Notifications::limit() const 0406 { 0407 return d->limiterModel->limit(); 0408 } 0409 0410 void Notifications::setLimit(int limit) 0411 { 0412 d->limiterModel->setLimit(limit); 0413 } 0414 0415 int Notifications::groupLimit() const 0416 { 0417 return d->groupLimit; 0418 } 0419 0420 void Notifications::setGroupLimit(int limit) 0421 { 0422 if (d->groupLimit == limit) { 0423 return; 0424 } 0425 0426 d->groupLimit = limit; 0427 if (d->groupCollapsingModel) { 0428 d->groupCollapsingModel->setLimit(limit); 0429 } 0430 Q_EMIT groupLimitChanged(); 0431 } 0432 0433 bool Notifications::expandUnread() const 0434 { 0435 return d->expandUnread; 0436 } 0437 0438 void Notifications::setExpandUnread(bool expand) 0439 { 0440 if (d->expandUnread == expand) { 0441 return; 0442 } 0443 0444 d->expandUnread = expand; 0445 if (d->groupCollapsingModel) { 0446 d->groupCollapsingModel->setExpandUnread(expand); 0447 } 0448 Q_EMIT expandUnreadChanged(); 0449 } 0450 0451 QWindow *Notifications::window() const 0452 { 0453 return d->notificationsModel ? d->notificationsModel->window() : nullptr; 0454 } 0455 0456 void Notifications::setWindow(QWindow *window) 0457 { 0458 if (d->notificationsModel) { 0459 d->notificationsModel->setWindow(window); 0460 } else { 0461 qCWarning(NOTIFICATIONMANAGER) << "Setting window before initialising the model" << this << window; 0462 } 0463 } 0464 0465 bool Notifications::showExpired() const 0466 { 0467 return d->filterModel->showExpired(); 0468 } 0469 0470 void Notifications::setShowExpired(bool show) 0471 { 0472 d->filterModel->setShowExpired(show); 0473 } 0474 0475 bool Notifications::showDismissed() const 0476 { 0477 return d->filterModel->showDismissed(); 0478 } 0479 0480 void Notifications::setShowDismissed(bool show) 0481 { 0482 d->filterModel->setShowDismissed(show); 0483 } 0484 0485 QStringList Notifications::blacklistedDesktopEntries() const 0486 { 0487 return d->filterModel->blacklistedDesktopEntries(); 0488 } 0489 0490 void Notifications::setBlacklistedDesktopEntries(const QStringList &blacklist) 0491 { 0492 d->filterModel->setBlackListedDesktopEntries(blacklist); 0493 } 0494 0495 QStringList Notifications::blacklistedNotifyRcNames() const 0496 { 0497 return d->filterModel->blacklistedNotifyRcNames(); 0498 } 0499 0500 void Notifications::setBlacklistedNotifyRcNames(const QStringList &blacklist) 0501 { 0502 d->filterModel->setBlacklistedNotifyRcNames(blacklist); 0503 } 0504 0505 QStringList Notifications::whitelistedDesktopEntries() const 0506 { 0507 return d->filterModel->whitelistedDesktopEntries(); 0508 } 0509 0510 void Notifications::setWhitelistedDesktopEntries(const QStringList &whitelist) 0511 { 0512 d->filterModel->setWhiteListedDesktopEntries(whitelist); 0513 } 0514 0515 QStringList Notifications::whitelistedNotifyRcNames() const 0516 { 0517 return d->filterModel->whitelistedNotifyRcNames(); 0518 } 0519 0520 void Notifications::setWhitelistedNotifyRcNames(const QStringList &whitelist) 0521 { 0522 d->filterModel->setWhitelistedNotifyRcNames(whitelist); 0523 } 0524 0525 bool Notifications::showNotifications() const 0526 { 0527 return d->showNotifications; 0528 } 0529 0530 void Notifications::setShowNotifications(bool show) 0531 { 0532 if (d->showNotifications == show) { 0533 return; 0534 } 0535 0536 d->showNotifications = show; 0537 d->initSourceModels(); 0538 Q_EMIT showNotificationsChanged(); 0539 } 0540 0541 bool Notifications::showJobs() const 0542 { 0543 return d->showJobs; 0544 } 0545 0546 void Notifications::setShowJobs(bool show) 0547 { 0548 if (d->showJobs == show) { 0549 return; 0550 } 0551 0552 d->showJobs = show; 0553 d->initSourceModels(); 0554 Q_EMIT showJobsChanged(); 0555 } 0556 0557 Notifications::Urgencies Notifications::urgencies() const 0558 { 0559 return d->filterModel->urgencies(); 0560 } 0561 0562 void Notifications::setUrgencies(Urgencies urgencies) 0563 { 0564 d->filterModel->setUrgencies(urgencies); 0565 } 0566 0567 Notifications::SortMode Notifications::sortMode() const 0568 { 0569 return d->sortModel->sortMode(); 0570 } 0571 0572 void Notifications::setSortMode(SortMode sortMode) 0573 { 0574 d->sortModel->setSortMode(sortMode); 0575 } 0576 0577 Qt::SortOrder Notifications::sortOrder() const 0578 { 0579 return d->sortModel->sortOrder(); 0580 } 0581 0582 void Notifications::setSortOrder(Qt::SortOrder sortOrder) 0583 { 0584 d->sortModel->setSortOrder(sortOrder); 0585 } 0586 0587 Notifications::GroupMode Notifications::groupMode() const 0588 { 0589 return d->groupMode; 0590 } 0591 0592 void Notifications::setGroupMode(GroupMode groupMode) 0593 { 0594 if (d->groupMode != groupMode) { 0595 d->groupMode = groupMode; 0596 d->initProxyModels(); 0597 Q_EMIT groupModeChanged(); 0598 } 0599 } 0600 0601 int Notifications::count() const 0602 { 0603 return rowCount(QModelIndex()); 0604 } 0605 0606 int Notifications::activeNotificationsCount() const 0607 { 0608 return d->activeNotificationsCount; 0609 } 0610 0611 int Notifications::expiredNotificationsCount() const 0612 { 0613 return d->expiredNotificationsCount; 0614 } 0615 0616 QDateTime Notifications::lastRead() const 0617 { 0618 if (d->notificationsModel) { 0619 return d->notificationsModel->lastRead(); 0620 } 0621 return QDateTime(); 0622 } 0623 0624 void Notifications::setLastRead(const QDateTime &lastRead) 0625 { 0626 // TODO jobs could also be unread? 0627 if (d->notificationsModel) { 0628 d->notificationsModel->setLastRead(lastRead); 0629 } 0630 if (d->groupCollapsingModel) { 0631 d->groupCollapsingModel->setLastRead(lastRead); 0632 } 0633 } 0634 0635 void Notifications::resetLastRead() 0636 { 0637 setLastRead(QDateTime::currentDateTimeUtc()); 0638 } 0639 0640 int Notifications::unreadNotificationsCount() const 0641 { 0642 return d->unreadNotificationsCount; 0643 } 0644 0645 int Notifications::activeJobsCount() const 0646 { 0647 return d->activeJobsCount; 0648 } 0649 0650 int Notifications::jobsPercentage() const 0651 { 0652 return d->jobsPercentage; 0653 } 0654 0655 QPersistentModelIndex Notifications::makePersistentModelIndex(const QModelIndex &idx) const 0656 { 0657 return QPersistentModelIndex(idx); 0658 } 0659 0660 void Notifications::expire(const QModelIndex &idx) 0661 { 0662 switch (static_cast<Notifications::Type>(idx.data(Notifications::TypeRole).toInt())) { 0663 case Notifications::NotificationType: 0664 d->notificationsModel->expire(Private::notificationId(idx)); 0665 break; 0666 case Notifications::JobType: 0667 d->jobsModel->expire(Utils::mapToModel(idx, d->jobsModel.data())); 0668 break; 0669 default: 0670 Q_UNREACHABLE(); 0671 } 0672 } 0673 0674 void Notifications::close(const QModelIndex &idx) 0675 { 0676 if (idx.data(Notifications::IsGroupRole).toBool()) { 0677 const QModelIndex groupIdx = Utils::mapToModel(idx, d->groupingModel); 0678 if (!groupIdx.isValid()) { 0679 qCWarning(NOTIFICATIONMANAGER) << "Failed to find group model index for this item"; 0680 return; 0681 } 0682 0683 Q_ASSERT(groupIdx.model() == d->groupingModel); 0684 0685 const int childCount = d->groupingModel->rowCount(groupIdx); 0686 for (int i = childCount - 1; i >= 0; --i) { 0687 const QModelIndex childIdx = d->groupingModel->index(i, 0, groupIdx); 0688 close(childIdx); 0689 } 0690 return; 0691 } 0692 0693 if (!idx.data(Notifications::ClosableRole).toBool()) { 0694 return; 0695 } 0696 0697 switch (static_cast<Notifications::Type>(idx.data(Notifications::TypeRole).toInt())) { 0698 case Notifications::NotificationType: 0699 d->notificationsModel->close(Private::notificationId(idx)); 0700 break; 0701 case Notifications::JobType: 0702 d->jobsModel->close(Utils::mapToModel(idx, d->jobsModel.data())); 0703 break; 0704 default: 0705 Q_UNREACHABLE(); 0706 } 0707 } 0708 0709 void Notifications::configure(const QModelIndex &idx) 0710 { 0711 if (!d->notificationsModel) { 0712 return; 0713 } 0714 0715 // For groups just configure the application, not the individual event 0716 if (Private::isGroup(idx)) { 0717 const QString desktopEntry = idx.data(Notifications::DesktopEntryRole).toString(); 0718 const QString notifyRcName = idx.data(Notifications::NotifyRcNameRole).toString(); 0719 0720 d->notificationsModel->configure(desktopEntry, notifyRcName, QString() /*eventId*/); 0721 return; 0722 } 0723 0724 d->notificationsModel->configure(Private::notificationId(idx)); 0725 } 0726 0727 void Notifications::invokeDefaultAction(const QModelIndex &idx, InvokeBehavior behavior) 0728 { 0729 if (d->notificationsModel) { 0730 d->notificationsModel->invokeDefaultAction(Private::notificationId(idx), behavior); 0731 } 0732 } 0733 0734 void Notifications::invokeAction(const QModelIndex &idx, const QString &actionId, InvokeBehavior behavior) 0735 { 0736 if (d->notificationsModel) { 0737 d->notificationsModel->invokeAction(Private::notificationId(idx), actionId, behavior); 0738 } 0739 } 0740 0741 void Notifications::reply(const QModelIndex &idx, const QString &text, InvokeBehavior behavior) 0742 { 0743 if (d->notificationsModel) { 0744 d->notificationsModel->reply(Private::notificationId(idx), text, behavior); 0745 } 0746 } 0747 0748 void Notifications::startTimeout(const QModelIndex &idx) 0749 { 0750 startTimeout(Private::notificationId(idx)); 0751 } 0752 0753 void Notifications::startTimeout(uint notificationId) 0754 { 0755 if (d->notificationsModel) { 0756 d->notificationsModel->startTimeout(notificationId); 0757 } 0758 } 0759 0760 void Notifications::stopTimeout(const QModelIndex &idx) 0761 { 0762 if (d->notificationsModel) { 0763 d->notificationsModel->stopTimeout(Private::notificationId(idx)); 0764 } 0765 } 0766 0767 void Notifications::suspendJob(const QModelIndex &idx) 0768 { 0769 if (d->jobsModel) { 0770 d->jobsModel->suspend(Utils::mapToModel(idx, d->jobsModel.data())); 0771 } 0772 } 0773 0774 void Notifications::resumeJob(const QModelIndex &idx) 0775 { 0776 if (d->jobsModel) { 0777 d->jobsModel->resume(Utils::mapToModel(idx, d->jobsModel.data())); 0778 } 0779 } 0780 0781 void Notifications::killJob(const QModelIndex &idx) 0782 { 0783 if (d->jobsModel) { 0784 d->jobsModel->kill(Utils::mapToModel(idx, d->jobsModel.data())); 0785 } 0786 } 0787 0788 void Notifications::clear(ClearFlags flags) 0789 { 0790 if (d->notificationsModel) { 0791 d->notificationsModel->clear(flags); 0792 } 0793 if (d->jobsModel) { 0794 d->jobsModel->clear(flags); 0795 } 0796 } 0797 0798 QModelIndex Notifications::groupIndex(const QModelIndex &idx) const 0799 { 0800 if (idx.data(Notifications::IsGroupRole).toBool()) { 0801 return idx; 0802 } 0803 0804 if (idx.data(Notifications::IsInGroupRole).toBool()) { 0805 QModelIndex groupingIdx = Utils::mapToModel(idx, d->groupingModel); 0806 return d->mapFromModel(groupingIdx.parent()); 0807 } 0808 0809 qCWarning(NOTIFICATIONMANAGER) << "Cannot get group index for item that isn't a group or inside one"; 0810 return QModelIndex(); 0811 } 0812 0813 void Notifications::collapseAllGroups() 0814 { 0815 if (d->groupCollapsingModel) { 0816 d->groupCollapsingModel->collapseAll(); 0817 } 0818 } 0819 0820 QVariant Notifications::data(const QModelIndex &index, int role) const 0821 { 0822 return QSortFilterProxyModel::data(index, role); 0823 } 0824 0825 bool Notifications::setData(const QModelIndex &index, const QVariant &value, int role) 0826 { 0827 return QSortFilterProxyModel::setData(index, value, role); 0828 } 0829 0830 bool Notifications::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const 0831 { 0832 return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); 0833 } 0834 0835 bool Notifications::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const 0836 { 0837 return QSortFilterProxyModel::lessThan(source_left, source_right); 0838 } 0839 0840 int Notifications::rowCount(const QModelIndex &parent) const 0841 { 0842 return QSortFilterProxyModel::rowCount(parent); 0843 } 0844 0845 QHash<int, QByteArray> Notifications::roleNames() const 0846 { 0847 return Utils::roleNames(); 0848 }