KJobWidgets

kuiserverjobtracker.cpp
1 /*
2  This file is part of the KDE project
3  SPDX-FileCopyrightText: 2008 Rafael Fernández López <[email protected]>
4  SPDX-FileCopyrightText: 2007 Kevin Ottens <[email protected]>
5 
6  SPDX-License-Identifier: LGPL-2.0-only
7 */
8 
9 #include "kuiserverjobtracker.h"
10 #include "kuiserverjobtracker_p.h"
11 
12 #include "debug.h"
13 #include "jobviewiface.h"
14 
15 #include <KJob>
16 
17 #include <QtGlobal>
18 #include <QApplication>
19 #include <QDBusConnection>
20 #include <QIcon>
21 
22 Q_GLOBAL_STATIC(KSharedUiServerProxy, serverProxy)
23 
24 class Q_DECL_HIDDEN KUiServerJobTracker::Private
25 {
26 public:
27  Private(KUiServerJobTracker *parent)
28  : q(parent)
29  {
30  }
31 
32  KUiServerJobTracker *const q;
33 
34  void _k_killJob();
35 
36  static void updateDestUrl(KJob *job, org::kde::JobViewV2 *jobView);
37 
39 
40  QMetaObject::Connection serverRegisteredConnection;
41 };
42 
43 void KUiServerJobTracker::Private::_k_killJob()
44 {
45  org::kde::JobViewV2 *jobView = qobject_cast<org::kde::JobViewV2 *>(q->sender());
46 
47  if (jobView) {
48  KJob *job = progressJobView.key(jobView);
49 
50  if (job) {
51  job->kill(KJob::EmitResult);
52  }
53  }
54 }
55 
56 void KUiServerJobTracker::Private::updateDestUrl(KJob *job, org::kde::JobViewV2 *jobView)
57 {
58  const QVariant destUrl = job->property("destUrl");
59  if (destUrl.isValid()) {
60  jobView->setDestUrl(QDBusVariant(destUrl));
61  }
62 }
63 
65  : KJobTrackerInterface(parent)
66  , d(new Private(this))
67 {
68 }
69 
71 {
72  if (!d->progressJobView.isEmpty()) {
73  qWarning() << "A KUiServerJobTracker instance contains" << d->progressJobView.size() << "stalled jobs";
74  }
75 
76  qDeleteAll(d->progressJobView);
77 
78  delete d;
79 }
80 
82 {
83  // Already registered job?
84  if (d->progressJobView.contains(job)) {
85  return;
86  }
87 
88  // Watch the server registering/unregistering and re-register the jobs as needed
89  if (!d->serverRegisteredConnection) {
90  d->serverRegisteredConnection = connect(serverProxy(), &KSharedUiServerProxy::serverRegistered, this, [this]() {
91  // Remember the list of jobs to re-register and then delete the old ones
92  const QList<KJob *> staleJobs = d->progressJobView.keys();
93 
94  qDeleteAll(d->progressJobView);
95  d->progressJobView.clear();
96 
97  for (KJob *job : staleJobs) {
98  registerJob(job);
99  }
100  });
101  }
102 
104  // This will only work if main() used QIcon::fromTheme.
105  QString programIconName = QApplication::windowIcon().name();
106 
107  if (programIconName.isEmpty()) {
108  programIconName = appName;
109  }
110 
111  QPointer<KJob> jobWatch = job;
112  QDBusReply<QDBusObjectPath> reply = serverProxy()->uiserver()->requestView(appName, programIconName, job->capabilities());
113 
114  // If we got a valid reply, register the interface for later usage.
115  if (reply.isValid()) {
116  org::kde::JobViewV2 *jobView = new org::kde::JobViewV2(QStringLiteral("org.kde.JobViewServer"), reply.value().path(), QDBusConnection::sessionBus());
117  if (!jobWatch) {
118  // qCDebug(KJOBWIDGETS) << "deleted out from under us when asking the server proxy for the view";
119  jobView->terminate(QString());
120  delete jobView;
121  return;
122  }
123 
124  QObject::connect(jobView, SIGNAL(cancelRequested()), this, SLOT(_k_killJob()));
125  QObject::connect(jobView, &org::kde::JobViewV2::suspendRequested, job, &KJob::suspend);
126  QObject::connect(jobView, &org::kde::JobViewV2::resumeRequested, job, &KJob::resume);
127 
128  d->updateDestUrl(job, jobView);
129 
130  if (!jobWatch) {
131  // qCDebug(KJOBWIDGETS) << "deleted out from under us when creating the dbus interface";
132  jobView->terminate(QString());
133  delete jobView;
134  return;
135  }
136 
137  d->progressJobView.insert(job, jobView);
138  } else if (!jobWatch) {
139  qWarning() << "Uh-oh...KUiServerJobTracker was trying to forward a job, but it was deleted from under us."
140  << "kuiserver *may* have a stranded job. we can't do anything about it because the returned objectPath is invalid.";
141  return;
142  }
143 
145 }
146 
148 {
150 
151  if (!d->progressJobView.contains(job)) {
152  return;
153  }
154 
155  org::kde::JobViewV2 *jobView = d->progressJobView.take(job);
156 
157  d->updateDestUrl(job, jobView);
158 
159  jobView->setError(job->error());
160 
161  if (job->error()) {
162  jobView->terminate(job->errorText());
163  } else {
164  jobView->terminate(QString());
165  }
166 
167  delete jobView;
168 }
169 
171 {
172  if (!d->progressJobView.contains(job)) {
173  return;
174  }
175 
176  org::kde::JobViewV2 *jobView = d->progressJobView.take(job);
177 
178  d->updateDestUrl(job, jobView);
179 
180  jobView->setError(job->error());
181 
182  if (job->error()) {
183  jobView->terminate(job->errorText());
184  } else {
185  jobView->terminate(QString());
186  }
187 }
188 
189 void KUiServerJobTracker::suspended(KJob *job)
190 {
191  if (!d->progressJobView.contains(job)) {
192  return;
193  }
194 
195  org::kde::JobViewV2 *jobView = d->progressJobView[job];
196 
197  jobView->setSuspended(true);
198 }
199 
200 void KUiServerJobTracker::resumed(KJob *job)
201 {
202  if (!d->progressJobView.contains(job)) {
203  return;
204  }
205 
206  org::kde::JobViewV2 *jobView = d->progressJobView[job];
207 
208  jobView->setSuspended(false);
209 }
210 
211 void KUiServerJobTracker::description(KJob *job, const QString &title, const QPair<QString, QString> &field1, const QPair<QString, QString> &field2)
212 {
213  if (!d->progressJobView.contains(job)) {
214  return;
215  }
216 
217  org::kde::JobViewV2 *jobView = d->progressJobView[job];
218 
219  jobView->setInfoMessage(title);
220 
221  if (field1.first.isNull() || field1.second.isNull()) {
222  jobView->clearDescriptionField(0);
223  } else {
224  jobView->setDescriptionField(0, field1.first, field1.second);
225  }
226 
227  if (field2.first.isNull() || field2.second.isNull()) {
228  jobView->clearDescriptionField(1);
229  } else {
230  jobView->setDescriptionField(1, field2.first, field2.second);
231  }
232 }
233 
234 void KUiServerJobTracker::infoMessage(KJob *job, const QString &plain, const QString &rich)
235 {
236  Q_UNUSED(rich)
237 
238  if (!d->progressJobView.contains(job)) {
239  return;
240  }
241 
242  org::kde::JobViewV2 *jobView = d->progressJobView[job];
243 
244  jobView->setInfoMessage(plain);
245 }
246 
247 void KUiServerJobTracker::totalAmount(KJob *job, KJob::Unit unit, qulonglong amount)
248 {
249  if (!d->progressJobView.contains(job)) {
250  return;
251  }
252 
253  org::kde::JobViewV2 *jobView = d->progressJobView[job];
254 
255  switch (unit) {
256  case KJob::Bytes:
257  jobView->setTotalAmount(amount, QStringLiteral("bytes"));
258  break;
259  case KJob::Files:
260  jobView->setTotalAmount(amount, QStringLiteral("files"));
261  break;
262  case KJob::Directories:
263  jobView->setTotalAmount(amount, QStringLiteral("dirs"));
264  break;
265  case KJob::Items:
266  jobView->setTotalAmount(amount, QStringLiteral("items"));
267  break;
268  case KJob::UnitsCount:
269  Q_UNREACHABLE();
270  break;
271  }
272 }
273 
274 void KUiServerJobTracker::processedAmount(KJob *job, KJob::Unit unit, qulonglong amount)
275 {
276  if (!d->progressJobView.contains(job)) {
277  return;
278  }
279 
280  org::kde::JobViewV2 *jobView = d->progressJobView[job];
281 
282  switch (unit) {
283  case KJob::Bytes:
284  jobView->setProcessedAmount(amount, QStringLiteral("bytes"));
285  break;
286  case KJob::Files:
287  jobView->setProcessedAmount(amount, QStringLiteral("files"));
288  break;
289  case KJob::Directories:
290  jobView->setProcessedAmount(amount, QStringLiteral("dirs"));
291  break;
292  case KJob::Items:
293  jobView->setProcessedAmount(amount, QStringLiteral("items"));
294  break;
295  case KJob::UnitsCount:
296  Q_UNREACHABLE();
297  break;
298  }
299 }
300 
301 void KUiServerJobTracker::percent(KJob *job, unsigned long percent)
302 {
303  if (!d->progressJobView.contains(job)) {
304  return;
305  }
306 
307  org::kde::JobViewV2 *jobView = d->progressJobView[job];
308 
309  jobView->setPercent(percent);
310 }
311 
312 void KUiServerJobTracker::speed(KJob *job, unsigned long value)
313 {
314  if (!d->progressJobView.contains(job)) {
315  return;
316  }
317 
318  org::kde::JobViewV2 *jobView = d->progressJobView[job];
319 
320  jobView->setSpeed(value);
321 }
322 
323 KSharedUiServerProxy::KSharedUiServerProxy()
324  : m_uiserver(new org::kde::JobViewServer(QStringLiteral("org.kde.JobViewServer"), QStringLiteral("/JobViewServer"), QDBusConnection::sessionBus()))
325  , m_watcher(new QDBusServiceWatcher(QStringLiteral("org.kde.JobViewServer"), QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForOwnerChange))
326 {
328  if (!bus->isServiceRegistered(QStringLiteral("org.kde.JobViewServer"))) {
329  QDBusReply<void> reply = bus->startService(QStringLiteral("org.kde.kuiserver"));
330  if (!reply.isValid()) {
331  qCCritical(KJOBWIDGETS) << "Couldn't start kuiserver from org.kde.kuiserver.service:" << reply.error();
332  return;
333  }
334 
335  if (!bus->isServiceRegistered(QStringLiteral("org.kde.JobViewServer"))) {
336  qCDebug(KJOBWIDGETS) << "The dbus name org.kde.JobViewServer is STILL NOT REGISTERED, even after starting kuiserver. Should not happen.";
337  return;
338  }
339 
340  qCDebug(KJOBWIDGETS) << "kuiserver registered";
341  } else {
342  qCDebug(KJOBWIDGETS) << "kuiserver found";
343  }
344 
345  connect(m_watcher.get(), &QDBusServiceWatcher::serviceOwnerChanged, this, &KSharedUiServerProxy::uiserverOwnerChanged);
346 
347  // cleanup early enough to avoid issues with dbus at application exit
348  // see e.g. https://phabricator.kde.org/D2545
349  qAddPostRoutine([]() {
350  serverProxy->m_uiserver.reset();
351  serverProxy->m_watcher.reset();
352  });
353 }
354 
355 KSharedUiServerProxy::~KSharedUiServerProxy()
356 {
357 }
358 
359 org::kde::JobViewServer *KSharedUiServerProxy::uiserver()
360 {
361  return m_uiserver.get();
362 }
363 
364 void KSharedUiServerProxy::uiserverOwnerChanged(const QString &serviceName, const QString &oldOwner, const QString &newOwner)
365 {
366  Q_UNUSED(serviceName);
367  Q_UNUSED(oldOwner);
368 
369  if (!newOwner.isEmpty()) { // registered
370  Q_EMIT serverRegistered();
371  } else if (newOwner.isEmpty()) { // unregistered
372  Q_EMIT serverUnregistered();
373  }
374 }
375 
376 #include "moc_kuiserverjobtracker.cpp"
377 #include "moc_kuiserverjobtracker_p.cpp"
bool resume()
virtual void unregisterJob(KJob *job)
Capabilities capabilities() const
virtual void registerJob(KJob *job)
bool isValid() const const
bool suspend()
bool isValid() const const
KUiServerJobTracker(QObject *parent=nullptr)
Creates a new KJobTrackerInterface.
QDBusReply< bool > isServiceRegistered(const QString &serviceName) const const
bool kill(KillVerbosity verbosity=Quietly)
void finished(KJob *job) override
The following slots are inherited from KJobTrackerInterface.
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
Q_GLOBAL_STATIC(Internal::StaticControl, s_instance) class ControlPrivate
QDBusReply< void > startService(const QString &name)
const QDBusError & error()
void unregisterJob(KJob *job) override
Unregister a job from this tracker.
QDBusConnection sessionBus()
bool isEmpty() const const
QCA_EXPORT QString appName()
QString errorText() const
QDBusConnectionInterface * interface() const const
~KUiServerJobTracker() override
Destroys a KJobTrackerInterface.
int error() const
void serviceOwnerChanged(const QString &serviceName, const QString &oldOwner, const QString &newOwner)
QDBusReply::Type value() const const
QVariant property(const char *name) const const
void registerJob(KJob *job) override
Register a new job in this tracker.
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Thu Oct 6 2022 04:15:43 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.