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