File indexing completed on 2024-11-10 12:24:33
0001 /* 0002 This file is part of the KDE project 0003 SPDX-FileCopyrightText: 2008 Rafael Fernández López <ereslibre@kde.org> 0004 SPDX-FileCopyrightText: 2007 Kevin Ottens <ervin@kde.org> 0005 0006 SPDX-License-Identifier: LGPL-2.0-only 0007 */ 0008 0009 #include "kuiserverjobtracker.h" 0010 #include "kuiserverjobtracker_p.h" 0011 0012 #include "debug.h" 0013 #include "jobviewiface.h" 0014 0015 #include <KJob> 0016 0017 #include <QtGlobal> 0018 #include <QApplication> 0019 #include <QDBusConnection> 0020 #include <QIcon> 0021 0022 Q_GLOBAL_STATIC(KSharedUiServerProxy, serverProxy) 0023 0024 class Q_DECL_HIDDEN KUiServerJobTracker::Private 0025 { 0026 public: 0027 Private(KUiServerJobTracker *parent) 0028 : q(parent) 0029 { 0030 } 0031 0032 KUiServerJobTracker *const q; 0033 0034 void _k_killJob(); 0035 0036 static void updateDestUrl(KJob *job, org::kde::JobViewV2 *jobView); 0037 0038 QHash<KJob *, org::kde::JobViewV2 *> progressJobView; 0039 0040 QMetaObject::Connection serverRegisteredConnection; 0041 }; 0042 0043 void KUiServerJobTracker::Private::_k_killJob() 0044 { 0045 org::kde::JobViewV2 *jobView = qobject_cast<org::kde::JobViewV2 *>(q->sender()); 0046 0047 if (jobView) { 0048 KJob *job = progressJobView.key(jobView); 0049 0050 if (job) { 0051 job->kill(KJob::EmitResult); 0052 } 0053 } 0054 } 0055 0056 void KUiServerJobTracker::Private::updateDestUrl(KJob *job, org::kde::JobViewV2 *jobView) 0057 { 0058 const QVariant destUrl = job->property("destUrl"); 0059 if (destUrl.isValid()) { 0060 jobView->setDestUrl(QDBusVariant(destUrl)); 0061 } 0062 } 0063 0064 KUiServerJobTracker::KUiServerJobTracker(QObject *parent) 0065 : KJobTrackerInterface(parent) 0066 , d(new Private(this)) 0067 { 0068 } 0069 0070 KUiServerJobTracker::~KUiServerJobTracker() 0071 { 0072 if (!d->progressJobView.isEmpty()) { 0073 qWarning() << "A KUiServerJobTracker instance contains" << d->progressJobView.size() << "stalled jobs"; 0074 } 0075 0076 qDeleteAll(d->progressJobView); 0077 0078 delete d; 0079 } 0080 0081 void KUiServerJobTracker::registerJob(KJob *job) 0082 { 0083 // Already registered job? 0084 if (d->progressJobView.contains(job)) { 0085 return; 0086 } 0087 0088 // Watch the server registering/unregistering and re-register the jobs as needed 0089 if (!d->serverRegisteredConnection) { 0090 d->serverRegisteredConnection = connect(serverProxy(), &KSharedUiServerProxy::serverRegistered, this, [this]() { 0091 // Remember the list of jobs to re-register and then delete the old ones 0092 const QList<KJob *> staleJobs = d->progressJobView.keys(); 0093 0094 qDeleteAll(d->progressJobView); 0095 d->progressJobView.clear(); 0096 0097 for (KJob *job : staleJobs) { 0098 registerJob(job); 0099 } 0100 }); 0101 } 0102 0103 const QString appName = QCoreApplication::applicationName(); 0104 // This will only work if main() used QIcon::fromTheme. 0105 QString programIconName = QApplication::windowIcon().name(); 0106 0107 if (programIconName.isEmpty()) { 0108 programIconName = appName; 0109 } 0110 0111 QPointer<KJob> jobWatch = job; 0112 QDBusReply<QDBusObjectPath> reply = serverProxy()->uiserver()->requestView(appName, programIconName, job->capabilities()); 0113 0114 // If we got a valid reply, register the interface for later usage. 0115 if (reply.isValid()) { 0116 org::kde::JobViewV2 *jobView = new org::kde::JobViewV2(QStringLiteral("org.kde.JobViewServer"), reply.value().path(), QDBusConnection::sessionBus()); 0117 if (!jobWatch) { 0118 // qCDebug(KJOBWIDGETS) << "deleted out from under us when asking the server proxy for the view"; 0119 jobView->terminate(QString()); 0120 delete jobView; 0121 return; 0122 } 0123 0124 QObject::connect(jobView, SIGNAL(cancelRequested()), this, SLOT(_k_killJob())); 0125 QObject::connect(jobView, &org::kde::JobViewV2::suspendRequested, job, &KJob::suspend); 0126 QObject::connect(jobView, &org::kde::JobViewV2::resumeRequested, job, &KJob::resume); 0127 0128 d->updateDestUrl(job, jobView); 0129 0130 if (!jobWatch) { 0131 // qCDebug(KJOBWIDGETS) << "deleted out from under us when creating the dbus interface"; 0132 jobView->terminate(QString()); 0133 delete jobView; 0134 return; 0135 } 0136 0137 d->progressJobView.insert(job, jobView); 0138 } else if (!jobWatch) { 0139 qWarning() << "Uh-oh...KUiServerJobTracker was trying to forward a job, but it was deleted from under us." 0140 << "kuiserver *may* have a stranded job. we can't do anything about it because the returned objectPath is invalid."; 0141 return; 0142 } 0143 0144 KJobTrackerInterface::registerJob(job); 0145 } 0146 0147 void KUiServerJobTracker::unregisterJob(KJob *job) 0148 { 0149 KJobTrackerInterface::unregisterJob(job); 0150 0151 if (!d->progressJobView.contains(job)) { 0152 return; 0153 } 0154 0155 org::kde::JobViewV2 *jobView = d->progressJobView.take(job); 0156 0157 d->updateDestUrl(job, jobView); 0158 0159 jobView->setError(job->error()); 0160 0161 if (job->error()) { 0162 jobView->terminate(job->errorText()); 0163 } else { 0164 jobView->terminate(QString()); 0165 } 0166 0167 delete jobView; 0168 } 0169 0170 void KUiServerJobTracker::finished(KJob *job) 0171 { 0172 if (!d->progressJobView.contains(job)) { 0173 return; 0174 } 0175 0176 org::kde::JobViewV2 *jobView = d->progressJobView.take(job); 0177 0178 d->updateDestUrl(job, jobView); 0179 0180 jobView->setError(job->error()); 0181 0182 if (job->error()) { 0183 jobView->terminate(job->errorText()); 0184 } else { 0185 jobView->terminate(QString()); 0186 } 0187 } 0188 0189 void KUiServerJobTracker::suspended(KJob *job) 0190 { 0191 if (!d->progressJobView.contains(job)) { 0192 return; 0193 } 0194 0195 org::kde::JobViewV2 *jobView = d->progressJobView[job]; 0196 0197 jobView->setSuspended(true); 0198 } 0199 0200 void KUiServerJobTracker::resumed(KJob *job) 0201 { 0202 if (!d->progressJobView.contains(job)) { 0203 return; 0204 } 0205 0206 org::kde::JobViewV2 *jobView = d->progressJobView[job]; 0207 0208 jobView->setSuspended(false); 0209 } 0210 0211 void KUiServerJobTracker::description(KJob *job, const QString &title, const QPair<QString, QString> &field1, const QPair<QString, QString> &field2) 0212 { 0213 if (!d->progressJobView.contains(job)) { 0214 return; 0215 } 0216 0217 org::kde::JobViewV2 *jobView = d->progressJobView[job]; 0218 0219 jobView->setInfoMessage(title); 0220 0221 if (field1.first.isNull() || field1.second.isNull()) { 0222 jobView->clearDescriptionField(0); 0223 } else { 0224 jobView->setDescriptionField(0, field1.first, field1.second); 0225 } 0226 0227 if (field2.first.isNull() || field2.second.isNull()) { 0228 jobView->clearDescriptionField(1); 0229 } else { 0230 jobView->setDescriptionField(1, field2.first, field2.second); 0231 } 0232 } 0233 0234 void KUiServerJobTracker::infoMessage(KJob *job, const QString &plain, const QString &rich) 0235 { 0236 Q_UNUSED(rich) 0237 0238 if (!d->progressJobView.contains(job)) { 0239 return; 0240 } 0241 0242 org::kde::JobViewV2 *jobView = d->progressJobView[job]; 0243 0244 jobView->setInfoMessage(plain); 0245 } 0246 0247 void KUiServerJobTracker::totalAmount(KJob *job, KJob::Unit unit, qulonglong amount) 0248 { 0249 if (!d->progressJobView.contains(job)) { 0250 return; 0251 } 0252 0253 org::kde::JobViewV2 *jobView = d->progressJobView[job]; 0254 0255 switch (unit) { 0256 case KJob::Bytes: 0257 jobView->setTotalAmount(amount, QStringLiteral("bytes")); 0258 break; 0259 case KJob::Files: 0260 jobView->setTotalAmount(amount, QStringLiteral("files")); 0261 break; 0262 case KJob::Directories: 0263 jobView->setTotalAmount(amount, QStringLiteral("dirs")); 0264 break; 0265 case KJob::Items: 0266 jobView->setTotalAmount(amount, QStringLiteral("items")); 0267 break; 0268 case KJob::UnitsCount: 0269 Q_UNREACHABLE(); 0270 break; 0271 } 0272 } 0273 0274 void KUiServerJobTracker::processedAmount(KJob *job, KJob::Unit unit, qulonglong amount) 0275 { 0276 if (!d->progressJobView.contains(job)) { 0277 return; 0278 } 0279 0280 org::kde::JobViewV2 *jobView = d->progressJobView[job]; 0281 0282 switch (unit) { 0283 case KJob::Bytes: 0284 jobView->setProcessedAmount(amount, QStringLiteral("bytes")); 0285 break; 0286 case KJob::Files: 0287 jobView->setProcessedAmount(amount, QStringLiteral("files")); 0288 break; 0289 case KJob::Directories: 0290 jobView->setProcessedAmount(amount, QStringLiteral("dirs")); 0291 break; 0292 case KJob::Items: 0293 jobView->setProcessedAmount(amount, QStringLiteral("items")); 0294 break; 0295 case KJob::UnitsCount: 0296 Q_UNREACHABLE(); 0297 break; 0298 } 0299 } 0300 0301 void KUiServerJobTracker::percent(KJob *job, unsigned long percent) 0302 { 0303 if (!d->progressJobView.contains(job)) { 0304 return; 0305 } 0306 0307 org::kde::JobViewV2 *jobView = d->progressJobView[job]; 0308 0309 jobView->setPercent(percent); 0310 } 0311 0312 void KUiServerJobTracker::speed(KJob *job, unsigned long value) 0313 { 0314 if (!d->progressJobView.contains(job)) { 0315 return; 0316 } 0317 0318 org::kde::JobViewV2 *jobView = d->progressJobView[job]; 0319 0320 jobView->setSpeed(value); 0321 } 0322 0323 KSharedUiServerProxy::KSharedUiServerProxy() 0324 : m_uiserver(new org::kde::JobViewServer(QStringLiteral("org.kde.JobViewServer"), QStringLiteral("/JobViewServer"), QDBusConnection::sessionBus())) 0325 , m_watcher(new QDBusServiceWatcher(QStringLiteral("org.kde.JobViewServer"), QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForOwnerChange)) 0326 { 0327 QDBusConnectionInterface *bus = QDBusConnection::sessionBus().interface(); 0328 if (!bus->isServiceRegistered(QStringLiteral("org.kde.JobViewServer"))) { 0329 QDBusReply<void> reply = bus->startService(QStringLiteral("org.kde.kuiserver")); 0330 if (!reply.isValid()) { 0331 qCCritical(KJOBWIDGETS) << "Couldn't start kuiserver from org.kde.kuiserver.service:" << reply.error(); 0332 return; 0333 } 0334 0335 if (!bus->isServiceRegistered(QStringLiteral("org.kde.JobViewServer"))) { 0336 qCDebug(KJOBWIDGETS) << "The dbus name org.kde.JobViewServer is STILL NOT REGISTERED, even after starting kuiserver. Should not happen."; 0337 return; 0338 } 0339 0340 qCDebug(KJOBWIDGETS) << "kuiserver registered"; 0341 } else { 0342 qCDebug(KJOBWIDGETS) << "kuiserver found"; 0343 } 0344 0345 connect(m_watcher.get(), &QDBusServiceWatcher::serviceOwnerChanged, this, &KSharedUiServerProxy::uiserverOwnerChanged); 0346 0347 // cleanup early enough to avoid issues with dbus at application exit 0348 // see e.g. https://phabricator.kde.org/D2545 0349 qAddPostRoutine([]() { 0350 serverProxy->m_uiserver.reset(); 0351 serverProxy->m_watcher.reset(); 0352 }); 0353 } 0354 0355 KSharedUiServerProxy::~KSharedUiServerProxy() 0356 { 0357 } 0358 0359 org::kde::JobViewServer *KSharedUiServerProxy::uiserver() 0360 { 0361 return m_uiserver.get(); 0362 } 0363 0364 void KSharedUiServerProxy::uiserverOwnerChanged(const QString &serviceName, const QString &oldOwner, const QString &newOwner) 0365 { 0366 Q_UNUSED(serviceName); 0367 Q_UNUSED(oldOwner); 0368 0369 if (!newOwner.isEmpty()) { // registered 0370 Q_EMIT serverRegistered(); 0371 } else if (newOwner.isEmpty()) { // unregistered 0372 Q_EMIT serverUnregistered(); 0373 } 0374 } 0375 0376 #include "moc_kuiserverjobtracker.cpp" 0377 #include "moc_kuiserverjobtracker_p.cpp"