File indexing completed on 2024-05-05 05:38:28
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 "jobsmodel.h" 0008 #include "jobsmodel_p.h" 0009 0010 #include "utils_p.h" 0011 0012 #include <QDebug> 0013 0014 #include <KJob> 0015 #include <KLocalizedString> 0016 0017 #include "job.h" 0018 #include "job_p.h" 0019 0020 using namespace NotificationManager; 0021 0022 JobsModel::JobsModel() 0023 : QAbstractListModel(nullptr) 0024 , d(new JobsModelPrivate(this)) 0025 { 0026 connect(d, &JobsModelPrivate::jobViewAboutToBeAdded, this, [this](int row, Job *job) { 0027 Q_UNUSED(job); 0028 beginInsertRows(QModelIndex(), row, row); 0029 }); 0030 connect(d, &JobsModelPrivate::jobViewAdded, this, [this](int row) { 0031 Q_UNUSED(row); 0032 endInsertRows(); 0033 }); 0034 0035 connect(d, &JobsModelPrivate::jobViewAboutToBeRemoved, this, [this](int row) { 0036 beginRemoveRows(QModelIndex(), row, row); 0037 }); 0038 connect(d, &JobsModelPrivate::jobViewRemoved, this, [this](int row) { 0039 Q_UNUSED(row); 0040 endRemoveRows(); 0041 }); 0042 0043 connect(d, &JobsModelPrivate::jobViewChanged, this, [this](int row, Job *job, const QList<int> &roles) { 0044 Q_UNUSED(job); 0045 const QModelIndex idx = index(row, 0); 0046 Q_EMIT dataChanged(idx, idx, roles); 0047 }); 0048 0049 connect(d, &JobsModelPrivate::serviceOwnershipLost, this, &JobsModel::serviceOwnershipLost); 0050 } 0051 0052 JobsModel::~JobsModel() = default; 0053 0054 JobsModel::Ptr JobsModel::createJobsModel() 0055 { 0056 static std::weak_ptr<JobsModel> s_instance; 0057 if (s_instance.expired()) { 0058 std::shared_ptr<JobsModel> ptr(new JobsModel()); 0059 s_instance = ptr; 0060 return ptr; 0061 } 0062 return s_instance.lock(); 0063 } 0064 0065 bool JobsModel::init() 0066 { 0067 return d->init(); 0068 } 0069 0070 bool JobsModel::isValid() const 0071 { 0072 return d->m_valid; 0073 } 0074 0075 QVariant JobsModel::data(const QModelIndex &index, int role) const 0076 { 0077 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid)) { 0078 return QVariant(); 0079 } 0080 0081 Job *job = d->m_jobViews.at(index.row()); 0082 0083 switch (role) { 0084 case Notifications::IdRole: 0085 return job->id(); 0086 case Notifications::TypeRole: 0087 return Notifications::JobType; 0088 // basically when it started 0089 case Notifications::CreatedRole: 0090 if (job->created().isValid()) { 0091 return job->created(); 0092 } 0093 break; 0094 // basically when it finished 0095 case Notifications::UpdatedRole: 0096 if (job->updated().isValid()) { 0097 return job->updated(); 0098 } 0099 break; 0100 case Notifications::SummaryRole: 0101 return job->summary(); 0102 case Notifications::BodyRole: 0103 return job->text(); 0104 case Qt::AccessibleDescriptionRole: 0105 return i18nc("@info %1 notification body %2 job name", "%1 from %2", job->text(), job->applicationName()); 0106 case Notifications::DesktopEntryRole: 0107 return job->desktopEntry(); 0108 case Notifications::ApplicationNameRole: 0109 return job->applicationName(); 0110 case Notifications::ApplicationIconNameRole: 0111 return job->applicationIconName(); 0112 0113 case Notifications::JobStateRole: 0114 return job->state(); 0115 case Notifications::PercentageRole: 0116 return job->percentage(); 0117 case Notifications::JobErrorRole: 0118 return job->error(); 0119 case Notifications::SuspendableRole: 0120 return job->suspendable(); 0121 case Notifications::KillableRole: 0122 return job->killable(); 0123 case Notifications::JobDetailsRole: 0124 return QVariant::fromValue(job); 0125 0126 // successfully finished jobs timeout like a regular notifiation 0127 // whereas running or error'd jobs are persistent 0128 case Notifications::TimeoutRole: 0129 return job->state() == Notifications::JobStateStopped && !job->error() ? -1 : 0; 0130 case Notifications::ClosableRole: 0131 return job->state() == Notifications::JobStateStopped; 0132 0133 case Notifications::ConfigurableRole: 0134 return false; 0135 case Notifications::ExpiredRole: 0136 return job->expired(); 0137 case Notifications::DismissedRole: 0138 return job->dismissed(); 0139 0140 // A job is usually either a long lasting operation you're aware about 0141 // or a quick job you don't care about. 0142 // When it's running, it's there, when it failed, it's persistent. 0143 // There's hardly a reason why it should show up as "unread". 0144 case Notifications::ReadRole: 0145 return true; 0146 } 0147 0148 return QVariant(); 0149 } 0150 0151 bool JobsModel::setData(const QModelIndex &index, const QVariant &value, int role) 0152 { 0153 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid)) { 0154 return false; 0155 } 0156 0157 Job *job = d->m_jobViews.at(index.row()); 0158 0159 switch (role) { 0160 case Notifications::DismissedRole: 0161 if (value.toBool() != job->dismissed()) { 0162 job->setDismissed(value.toBool()); 0163 return true; 0164 } 0165 break; 0166 } 0167 0168 return false; 0169 } 0170 0171 int JobsModel::rowCount(const QModelIndex &parent) const 0172 { 0173 if (parent.isValid()) { 0174 return 0; 0175 } 0176 0177 return d->m_jobViews.count(); 0178 } 0179 0180 QHash<int, QByteArray> JobsModel::roleNames() const 0181 { 0182 return Utils::roleNames(); 0183 } 0184 0185 void JobsModel::close(const QModelIndex &idx) 0186 { 0187 if (checkIndex(idx, QAbstractItemModel::CheckIndexOption::IndexIsValid)) { 0188 d->removeAt(idx.row()); 0189 } 0190 } 0191 0192 void JobsModel::expire(const QModelIndex &idx) 0193 { 0194 if (checkIndex(idx, QAbstractItemModel::CheckIndexOption::IndexIsValid)) { 0195 d->m_jobViews.at(idx.row())->setExpired(true); 0196 } 0197 } 0198 0199 void JobsModel::suspend(const QModelIndex &idx) 0200 { 0201 if (checkIndex(idx, QAbstractItemModel::CheckIndexOption::IndexIsValid)) { 0202 d->m_jobViews.at(idx.row())->suspend(); 0203 } 0204 } 0205 0206 void JobsModel::resume(const QModelIndex &idx) 0207 { 0208 if (checkIndex(idx, QAbstractItemModel::CheckIndexOption::IndexIsValid)) { 0209 d->m_jobViews.at(idx.row())->resume(); 0210 } 0211 } 0212 0213 void JobsModel::kill(const QModelIndex &idx) 0214 { 0215 if (checkIndex(idx, QAbstractItemModel::CheckIndexOption::IndexIsValid)) { 0216 d->m_jobViews.at(idx.row())->kill(); 0217 } 0218 } 0219 0220 void JobsModel::clear(Notifications::ClearFlags flags) 0221 { 0222 if (d->m_jobViews.isEmpty()) { 0223 return; 0224 } 0225 0226 for (int i = d->m_jobViews.count() - 1; i >= 0; --i) { 0227 Job *job = d->m_jobViews.at(i); 0228 0229 bool clear = (flags.testFlag(Notifications::ClearExpired) && job->expired()); 0230 0231 // Compared to notifications, the number of jobs is typically small 0232 // so for simplicity we can just delete one item at a time 0233 if (clear) { 0234 d->removeAt(i); 0235 } 0236 } 0237 }