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