File indexing completed on 2024-05-19 05:37:49
0001 /* 0002 SPDX-FileCopyrightText: 2008 Rob Scheepmaker <r.scheepmaker@student.utwente.nl> 0003 0004 SPDX-License-Identifier: LGPL-2.0-only 0005 */ 0006 0007 #include "kuiserverengine.h" 0008 #include "jobcontrol.h" 0009 0010 #include <QUrl> 0011 0012 #include <KFormat> 0013 #include <KJob> 0014 #include <KLocalizedString> 0015 0016 #include "notifications.h" 0017 0018 #include <algorithm> 0019 0020 using namespace NotificationManager; 0021 0022 KuiserverEngine::KuiserverEngine(QObject *parent) 0023 : Plasma5Support::DataEngine(parent) 0024 { 0025 init(); 0026 } 0027 0028 KuiserverEngine::~KuiserverEngine() 0029 { 0030 } 0031 0032 QString KuiserverEngine::sourceName(Job *job) 0033 { 0034 return QStringLiteral("Job %1").arg(job->id()); 0035 } 0036 0037 uint KuiserverEngine::jobId(const QString &sourceName) 0038 { 0039 return QStringView(sourceName).mid(4 /*length of Job + space*/).toUInt(); 0040 } 0041 0042 Plasma5Support::Service *KuiserverEngine::serviceForSource(const QString &source) 0043 { 0044 const uint id = jobId(source); 0045 if (!id) { 0046 return DataEngine::serviceForSource(source); 0047 } 0048 0049 auto it = std::find_if(m_jobs.constBegin(), m_jobs.constBegin(), [&id](Job *job) { 0050 return job->id() == id; 0051 }); 0052 0053 if (it == m_jobs.constEnd()) { 0054 return DataEngine::serviceForSource(source); 0055 } 0056 0057 return new JobControl(this, *it); 0058 } 0059 0060 void KuiserverEngine::init() 0061 { 0062 m_jobsModel = JobsModel::createJobsModel(); 0063 // TODO see if this causes any issues when/if other processes are using applicationjobs engine, e.g. Latte Dock 0064 m_jobsModel->init(); 0065 0066 connect(m_jobsModel.get(), &Notifications::rowsInserted, this, [this](const QModelIndex &parent, int first, int last) { 0067 for (int i = first; i <= last; ++i) { 0068 const QModelIndex idx = m_jobsModel->index(first, 0, parent); 0069 Job *job = idx.data(Notifications::JobDetailsRole).value<Job *>(); 0070 registerJob(job); 0071 } 0072 }); 0073 0074 connect(m_jobsModel.get(), &Notifications::rowsAboutToBeRemoved, this, [this](const QModelIndex &parent, int first, int last) { 0075 for (int i = first; i <= last; ++i) { 0076 const QModelIndex idx = m_jobsModel->index(first, 0, parent); 0077 Job *job = idx.data(Notifications::JobDetailsRole).value<Job *>(); 0078 removeJob(job); 0079 } 0080 }); 0081 } 0082 0083 void KuiserverEngine::updateDescriptionField(Job *job, int number, QString (Job::*labelGetter)() const, QString (Job::*valueGetter)() const) 0084 { 0085 const QString source = sourceName(job); 0086 const QString labelString = QStringLiteral("label%1").arg(number); 0087 const QString labelNameString = QStringLiteral("labelName%1").arg(number); 0088 const QString labelFileNameString = QStringLiteral("labelFileName%1").arg(number); 0089 0090 const QString label = ((job)->*labelGetter)(); 0091 const QString value = ((job)->*valueGetter)(); 0092 0093 if (label.isEmpty() && value.isEmpty()) { 0094 setData(source, labelString, QVariant()); 0095 setData(source, labelNameString, QVariant()); 0096 setData(source, labelFileNameString, QVariant()); 0097 } else { 0098 setData(source, labelNameString, label); 0099 setData(source, labelString, value); 0100 0101 const QUrl url = QUrl::fromUserInput(value, QString(), QUrl::AssumeLocalFile); 0102 setData(source, labelFileNameString, url.toString(QUrl::PreferLocalFile | QUrl::RemoveFragment | QUrl::RemoveQuery)); 0103 } 0104 setData(source, labelString); 0105 } 0106 0107 void KuiserverEngine::updateUnit(Job *job, 0108 int number, 0109 const QString &unit, 0110 qulonglong (NotificationManager::Job::*processedGetter)() const, 0111 qulonglong (NotificationManager::Job::*totalGetter)() const) 0112 { 0113 const QString source = sourceName(job); 0114 0115 setData(source, QStringLiteral("totalUnit%1").arg(number), unit); 0116 setData(source, QStringLiteral("totalAmount%1").arg(number), ((job)->*totalGetter)()); 0117 setData(source, QStringLiteral("processedUnit%1").arg(number), unit); 0118 setData(source, QStringLiteral("processedAmount%1").arg(number), ((job)->*processedGetter)()); 0119 } 0120 0121 void KuiserverEngine::registerJob(Job *job) 0122 { 0123 if (m_jobs.contains(job)) { // shouldn't really happen 0124 return; 0125 } 0126 0127 const QString source = sourceName(job); 0128 0129 setData(source, QStringLiteral("appName"), job->desktopEntry()); // job->applicationName()); 0130 setData(source, QStringLiteral("appIconName"), job->applicationIconName()); 0131 setData(source, QStringLiteral("suspendable"), job->suspendable()); 0132 setData(source, QStringLiteral("killable"), job->killable()); 0133 updateState(job); 0134 0135 connect(job, &Job::stateChanged, this, [this, job] { 0136 updateState(job); 0137 }); 0138 connect(job, &Job::speedChanged, this, [this, job] { 0139 updateEta(job); 0140 }); 0141 0142 connectJobField(job, &Job::summary, &Job::summaryChanged, QStringLiteral("infoMessage")); 0143 connectJobField(job, &Job::percentage, &Job::percentageChanged, QStringLiteral("percentage")); 0144 connectJobField(job, &Job::error, &Job::errorChanged, QStringLiteral("error")); 0145 connectJobField(job, &Job::errorText, &Job::errorTextChanged, QStringLiteral("errorText")); 0146 connectJobField(job, &Job::destUrl, &Job::destUrlChanged, QStringLiteral("destUrl")); 0147 0148 static const struct { 0149 int number; 0150 QString (Job::*labelGetter)() const; 0151 void (Job::*labelSignal)(); 0152 QString (Job::*valueGetter)() const; 0153 void (Job::*valueSignal)(); 0154 } s_descriptionFields[] = { 0155 {0, &Job::descriptionLabel1, &Job::descriptionLabel1Changed, &Job::descriptionValue1, &Job::descriptionValue1Changed}, 0156 {1, &Job::descriptionLabel2, &Job::descriptionLabel2Changed, &Job::descriptionValue2, &Job::descriptionValue2Changed}, 0157 }; 0158 0159 for (auto fields : s_descriptionFields) { 0160 updateDescriptionField(job, fields.number, fields.labelGetter, fields.valueGetter); 0161 connect(job, fields.labelSignal, this, [=, this] { 0162 updateDescriptionField(job, fields.number, fields.labelGetter, fields.valueGetter); 0163 }); 0164 connect(job, fields.valueSignal, this, [=, this] { 0165 updateDescriptionField(job, fields.number, fields.labelGetter, fields.valueGetter); 0166 }); 0167 } 0168 0169 static const struct { 0170 // Previously the dataengine counted units up but for simplicity a fixed number is assigned to each unit 0171 int number; 0172 QString unit; 0173 qulonglong (Job::*processedGetter)() const; 0174 void (Job::*processedSignal)(); 0175 qulonglong (Job::*totalGetter)() const; 0176 void (Job::*totalSignal)(); 0177 } s_unitsFields[] = { 0178 {0, QStringLiteral("bytes"), &Job::processedBytes, &Job::processedBytesChanged, &Job::totalBytes, &Job::totalBytesChanged}, 0179 {1, QStringLiteral("files"), &Job::processedFiles, &Job::processedFilesChanged, &Job::totalFiles, &Job::totalFilesChanged}, 0180 {2, QStringLiteral("dirs"), &Job::processedDirectories, &Job::processedDirectoriesChanged, &Job::totalDirectories, &Job::totalDirectoriesChanged}}; 0181 0182 for (auto fields : s_unitsFields) { 0183 updateUnit(job, fields.number, fields.unit, fields.processedGetter, fields.totalGetter); 0184 connect(job, fields.processedSignal, this, [=, this] { 0185 updateUnit(job, fields.number, fields.unit, fields.processedGetter, fields.totalGetter); 0186 }); 0187 connect(job, fields.totalSignal, this, [=, this] { 0188 updateUnit(job, fields.number, fields.unit, fields.processedGetter, fields.totalGetter); 0189 }); 0190 } 0191 0192 m_jobs.append(job); 0193 } 0194 0195 void KuiserverEngine::removeJob(Job *job) 0196 { 0197 if (!job || !m_jobs.contains(job)) { 0198 return; 0199 } 0200 0201 m_jobs.removeOne(job); 0202 0203 const QString source = sourceName(job); 0204 removeSource(source); 0205 } 0206 0207 QString KuiserverEngine::speedString(qulonglong speed) 0208 { 0209 return i18nc("Bytes per second", "%1/s", KFormat().formatByteSize(speed)); 0210 } 0211 0212 void KuiserverEngine::updateState(Job *job) 0213 { 0214 const QString source = sourceName(job); 0215 0216 QString stateString; 0217 switch (job->state()) { 0218 case Notifications::JobStateRunning: 0219 stateString = QStringLiteral("running"); 0220 updateSpeed(job); 0221 break; 0222 case Notifications::JobStateSuspended: 0223 stateString = QStringLiteral("suspended"); 0224 setData(source, QStringLiteral("speed"), QVariant()); 0225 setData(source, QStringLiteral("numericSpeed"), QVariant()); 0226 break; 0227 case Notifications::JobStateStopped: 0228 stateString = QStringLiteral("stopped"); 0229 break; 0230 } 0231 0232 setData(source, QStringLiteral("state"), stateString); 0233 0234 if (job->state() == Notifications::JobStateStopped) { 0235 removeJob(job); 0236 } 0237 } 0238 0239 void KuiserverEngine::updateSpeed(Job *job) 0240 { 0241 const QString source = sourceName(job); 0242 setData(source, QStringLiteral("speed"), speedString(job->speed())); 0243 setData(source, QStringLiteral("numericSpeed"), job->speed()); 0244 updateEta(job); 0245 } 0246 0247 void KuiserverEngine::updateEta(Job *job) 0248 { 0249 const QString source = sourceName(job); 0250 0251 if (job->speed() < 1 || job->totalBytes() < 1) { 0252 setData(source, QStringLiteral("eta"), 0); 0253 return; 0254 } 0255 0256 const qlonglong remaining = 1000 * (job->totalBytes() - job->processedBytes()); 0257 setData(source, QStringLiteral("eta"), remaining / job->speed()); 0258 } 0259 0260 K_PLUGIN_CLASS_WITH_JSON(KuiserverEngine, "plasma-dataengine-applicationjobs.json") 0261 0262 #include "kuiserverengine.moc"