File indexing completed on 2024-12-08 03:41:13
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 0079 void KUiServerJobTracker::registerJob(KJob *job) 0080 { 0081 // Already registered job? 0082 if (d->progressJobView.contains(job)) { 0083 return; 0084 } 0085 0086 // Watch the server registering/unregistering and re-register the jobs as needed 0087 if (!d->serverRegisteredConnection) { 0088 d->serverRegisteredConnection = connect(serverProxy(), &KSharedUiServerProxy::serverRegistered, this, [this]() { 0089 // Remember the list of jobs to re-register and then delete the old ones 0090 const QList<KJob *> staleJobs = d->progressJobView.keys(); 0091 0092 qDeleteAll(d->progressJobView); 0093 d->progressJobView.clear(); 0094 0095 for (KJob *job : staleJobs) { 0096 registerJob(job); 0097 } 0098 }); 0099 } 0100 0101 const QString appName = QCoreApplication::applicationName(); 0102 // This will only work if main() used QIcon::fromTheme. 0103 QString programIconName = QApplication::windowIcon().name(); 0104 0105 if (programIconName.isEmpty()) { 0106 programIconName = appName; 0107 } 0108 0109 QPointer<KJob> jobWatch = job; 0110 QDBusReply<QDBusObjectPath> reply = serverProxy()->uiserver()->requestView(appName, programIconName, job->capabilities()); 0111 0112 // If we got a valid reply, register the interface for later usage. 0113 if (reply.isValid()) { 0114 org::kde::JobViewV2 *jobView = new org::kde::JobViewV2(QStringLiteral("org.kde.JobViewServer"), reply.value().path(), QDBusConnection::sessionBus()); 0115 if (!jobWatch) { 0116 // qCDebug(KJOBWIDGETS) << "deleted out from under us when asking the server proxy for the view"; 0117 jobView->terminate(QString()); 0118 delete jobView; 0119 return; 0120 } 0121 0122 QObject::connect(jobView, SIGNAL(cancelRequested()), this, SLOT(_k_killJob())); 0123 QObject::connect(jobView, &org::kde::JobViewV2::suspendRequested, job, &KJob::suspend); 0124 QObject::connect(jobView, &org::kde::JobViewV2::resumeRequested, job, &KJob::resume); 0125 0126 d->updateDestUrl(job, jobView); 0127 0128 if (!jobWatch) { 0129 // qCDebug(KJOBWIDGETS) << "deleted out from under us when creating the dbus interface"; 0130 jobView->terminate(QString()); 0131 delete jobView; 0132 return; 0133 } 0134 0135 d->progressJobView.insert(job, jobView); 0136 } else if (!jobWatch) { 0137 qWarning() << "Uh-oh...KUiServerJobTracker was trying to forward a job, but it was deleted from under us." 0138 << "kuiserver *may* have a stranded job. we can't do anything about it because the returned objectPath is invalid."; 0139 return; 0140 } 0141 0142 KJobTrackerInterface::registerJob(job); 0143 } 0144 0145 void KUiServerJobTracker::unregisterJob(KJob *job) 0146 { 0147 KJobTrackerInterface::unregisterJob(job); 0148 0149 if (!d->progressJobView.contains(job)) { 0150 return; 0151 } 0152 0153 org::kde::JobViewV2 *jobView = d->progressJobView.take(job); 0154 0155 d->updateDestUrl(job, jobView); 0156 0157 jobView->setError(job->error()); 0158 0159 if (job->error()) { 0160 jobView->terminate(job->errorText()); 0161 } else { 0162 jobView->terminate(QString()); 0163 } 0164 0165 delete jobView; 0166 } 0167 0168 void KUiServerJobTracker::finished(KJob *job) 0169 { 0170 if (!d->progressJobView.contains(job)) { 0171 return; 0172 } 0173 0174 org::kde::JobViewV2 *jobView = d->progressJobView.take(job); 0175 0176 d->updateDestUrl(job, jobView); 0177 0178 jobView->setError(job->error()); 0179 0180 if (job->error()) { 0181 jobView->terminate(job->errorText()); 0182 } else { 0183 jobView->terminate(QString()); 0184 } 0185 } 0186 0187 void KUiServerJobTracker::suspended(KJob *job) 0188 { 0189 if (!d->progressJobView.contains(job)) { 0190 return; 0191 } 0192 0193 org::kde::JobViewV2 *jobView = d->progressJobView[job]; 0194 0195 jobView->setSuspended(true); 0196 } 0197 0198 void KUiServerJobTracker::resumed(KJob *job) 0199 { 0200 if (!d->progressJobView.contains(job)) { 0201 return; 0202 } 0203 0204 org::kde::JobViewV2 *jobView = d->progressJobView[job]; 0205 0206 jobView->setSuspended(false); 0207 } 0208 0209 void KUiServerJobTracker::description(KJob *job, const QString &title, const QPair<QString, QString> &field1, const QPair<QString, QString> &field2) 0210 { 0211 if (!d->progressJobView.contains(job)) { 0212 return; 0213 } 0214 0215 org::kde::JobViewV2 *jobView = d->progressJobView[job]; 0216 0217 jobView->setInfoMessage(title); 0218 0219 if (field1.first.isNull() || field1.second.isNull()) { 0220 jobView->clearDescriptionField(0); 0221 } else { 0222 jobView->setDescriptionField(0, field1.first, field1.second); 0223 } 0224 0225 if (field2.first.isNull() || field2.second.isNull()) { 0226 jobView->clearDescriptionField(1); 0227 } else { 0228 jobView->setDescriptionField(1, field2.first, field2.second); 0229 } 0230 } 0231 0232 void KUiServerJobTracker::infoMessage(KJob *job, const QString &message) 0233 { 0234 if (!d->progressJobView.contains(job)) { 0235 return; 0236 } 0237 0238 org::kde::JobViewV2 *jobView = d->progressJobView[job]; 0239 0240 jobView->setInfoMessage(message); 0241 } 0242 0243 void KUiServerJobTracker::totalAmount(KJob *job, KJob::Unit unit, qulonglong amount) 0244 { 0245 if (!d->progressJobView.contains(job)) { 0246 return; 0247 } 0248 0249 org::kde::JobViewV2 *jobView = d->progressJobView[job]; 0250 0251 switch (unit) { 0252 case KJob::Bytes: 0253 jobView->setTotalAmount(amount, QStringLiteral("bytes")); 0254 break; 0255 case KJob::Files: 0256 jobView->setTotalAmount(amount, QStringLiteral("files")); 0257 break; 0258 case KJob::Directories: 0259 jobView->setTotalAmount(amount, QStringLiteral("dirs")); 0260 break; 0261 case KJob::Items: 0262 jobView->setTotalAmount(amount, QStringLiteral("items")); 0263 break; 0264 case KJob::UnitsCount: 0265 Q_UNREACHABLE(); 0266 break; 0267 } 0268 } 0269 0270 void KUiServerJobTracker::processedAmount(KJob *job, KJob::Unit unit, qulonglong amount) 0271 { 0272 if (!d->progressJobView.contains(job)) { 0273 return; 0274 } 0275 0276 org::kde::JobViewV2 *jobView = d->progressJobView[job]; 0277 0278 switch (unit) { 0279 case KJob::Bytes: 0280 jobView->setProcessedAmount(amount, QStringLiteral("bytes")); 0281 break; 0282 case KJob::Files: 0283 jobView->setProcessedAmount(amount, QStringLiteral("files")); 0284 break; 0285 case KJob::Directories: 0286 jobView->setProcessedAmount(amount, QStringLiteral("dirs")); 0287 break; 0288 case KJob::Items: 0289 jobView->setProcessedAmount(amount, QStringLiteral("items")); 0290 break; 0291 case KJob::UnitsCount: 0292 Q_UNREACHABLE(); 0293 break; 0294 } 0295 } 0296 0297 void KUiServerJobTracker::percent(KJob *job, unsigned long percent) 0298 { 0299 if (!d->progressJobView.contains(job)) { 0300 return; 0301 } 0302 0303 org::kde::JobViewV2 *jobView = d->progressJobView[job]; 0304 0305 jobView->setPercent(percent); 0306 } 0307 0308 void KUiServerJobTracker::speed(KJob *job, unsigned long value) 0309 { 0310 if (!d->progressJobView.contains(job)) { 0311 return; 0312 } 0313 0314 org::kde::JobViewV2 *jobView = d->progressJobView[job]; 0315 0316 jobView->setSpeed(value); 0317 } 0318 0319 KSharedUiServerProxy::KSharedUiServerProxy() 0320 : m_uiserver(new org::kde::JobViewServer(QStringLiteral("org.kde.JobViewServer"), QStringLiteral("/JobViewServer"), QDBusConnection::sessionBus())) 0321 , m_watcher(new QDBusServiceWatcher(QStringLiteral("org.kde.JobViewServer"), QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForOwnerChange)) 0322 { 0323 QDBusConnectionInterface *bus = QDBusConnection::sessionBus().interface(); 0324 if (!bus->isServiceRegistered(QStringLiteral("org.kde.JobViewServer"))) { 0325 QDBusReply<void> reply = bus->startService(QStringLiteral("org.kde.kuiserver")); 0326 if (!reply.isValid()) { 0327 qCCritical(KJOBWIDGETS) << "Couldn't start kuiserver from org.kde.kuiserver.service:" << reply.error(); 0328 return; 0329 } 0330 0331 if (!bus->isServiceRegistered(QStringLiteral("org.kde.JobViewServer"))) { 0332 qCDebug(KJOBWIDGETS) << "The dbus name org.kde.JobViewServer is STILL NOT REGISTERED, even after starting kuiserver. Should not happen."; 0333 return; 0334 } 0335 0336 qCDebug(KJOBWIDGETS) << "kuiserver registered"; 0337 } else { 0338 qCDebug(KJOBWIDGETS) << "kuiserver found"; 0339 } 0340 0341 connect(m_watcher.get(), &QDBusServiceWatcher::serviceOwnerChanged, this, &KSharedUiServerProxy::uiserverOwnerChanged); 0342 0343 // cleanup early enough to avoid issues with dbus at application exit 0344 // see e.g. https://phabricator.kde.org/D2545 0345 qAddPostRoutine([]() { 0346 serverProxy->m_uiserver.reset(); 0347 serverProxy->m_watcher.reset(); 0348 }); 0349 } 0350 0351 KSharedUiServerProxy::~KSharedUiServerProxy() 0352 { 0353 } 0354 0355 org::kde::JobViewServer *KSharedUiServerProxy::uiserver() 0356 { 0357 return m_uiserver.get(); 0358 } 0359 0360 void KSharedUiServerProxy::uiserverOwnerChanged(const QString &serviceName, const QString &oldOwner, const QString &newOwner) 0361 { 0362 Q_UNUSED(serviceName); 0363 Q_UNUSED(oldOwner); 0364 0365 if (!newOwner.isEmpty()) { // registered 0366 Q_EMIT serverRegistered(); 0367 } else if (newOwner.isEmpty()) { // unregistered 0368 Q_EMIT serverUnregistered(); 0369 } 0370 } 0371 0372 #include "moc_kuiserverjobtracker.cpp" 0373 #include "moc_kuiserverjobtracker_p.cpp"