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"