1 | /***************************************************************************** |
2 | * Copyright (C) 2009 Shaun Reich <shaun.reich@kdemail.net> * |
3 | * Copyright (C) 2006-2008 Rafael Fernández López <ereslibre@kde.org> * |
4 | * Copyright (C) 2001 George Staikos <staikos@kde.org> * |
5 | * Copyright (C) 2000 Matej Koss <koss@miesto.sk> * |
6 | * Copyright (C) 2000 David Faure <faure@kde.org> * |
7 | * * |
8 | * This program is free software; you can redistribute it and/or * |
9 | * modify it under the terms of the GNU General Public License as * |
10 | * published by the Free Software Foundation; either version 2 of * |
11 | * the License, or (at your option) any later version. * |
12 | * * |
13 | * This program is distributed in the hope that it will be useful, * |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * |
16 | * GNU General Public License for more details. * |
17 | * * |
18 | * You should have received a copy of the GNU General Public License * |
19 | * along with this program. If not, see <http://www.gnu.org/licenses/>. * |
20 | *****************************************************************************/ |
21 | |
22 | #include "uiserver.h" |
23 | #include "jobviewadaptor.h" |
24 | #include "jobview_interface.h" |
25 | #include "requestviewcallwatcher.h" |
26 | |
27 | #include <klocale.h> |
28 | #include <kdebug.h> |
29 | |
30 | #include <QtDBus/QDBusPendingReply> |
31 | #include <qdbusabstractinterface.h> |
32 | |
33 | JobView::JobView(uint jobId, QObject *parent) |
34 | : QObject(parent), |
35 | m_capabilities(-1), |
36 | m_percent(-1), |
37 | m_totalAmount(0), |
38 | m_processAmount(0), |
39 | m_jobId(jobId), |
40 | m_state(Running), |
41 | m_isTerminated(false), |
42 | m_currentPendingCalls(0) |
43 | { |
44 | new JobViewV2Adaptor(this); |
45 | |
46 | m_objectPath.setPath(QString("/JobViewServer/JobView_%1" ).arg(m_jobId)); |
47 | QDBusConnection::sessionBus().registerObject(m_objectPath.path(), this); |
48 | } |
49 | |
50 | JobView::~JobView() |
51 | { |
52 | } |
53 | |
54 | void JobView::terminate(const QString &errorMessage) |
55 | { |
56 | QDBusConnection::sessionBus().unregisterObject(m_objectPath.path(), QDBusConnection::UnregisterTree); |
57 | |
58 | typedef QPair<QString, QDBusAbstractInterface*> iFacePair; |
59 | foreach(const iFacePair &pair, m_objectPaths) { |
60 | kDebug(7024) << "making async call of terminate for: " << pair.first; |
61 | pair.second->asyncCall(QLatin1String("terminate" ), errorMessage); |
62 | } |
63 | |
64 | m_error = errorMessage; |
65 | |
66 | if (m_currentPendingCalls < 1) { |
67 | // if hit it means a job exists for *something* but can't be terminated properly |
68 | // because the async call to create the job didn't come back fast enough. |
69 | // (thus addJobContact wasn't called before this was hit). |
70 | // Q_ASSERT(!m_objectPaths.isEmpty()); |
71 | |
72 | // no more calls waiting. Lets mark ourselves for deletion. |
73 | emit finished(this); |
74 | } |
75 | |
76 | m_isTerminated = true; |
77 | } |
78 | |
79 | void JobView::requestSuspend() |
80 | { |
81 | emit suspendRequested(); |
82 | } |
83 | |
84 | void JobView::requestResume() |
85 | { |
86 | emit resumeRequested(); |
87 | } |
88 | |
89 | void JobView::requestCancel() |
90 | { |
91 | emit cancelRequested(); |
92 | } |
93 | |
94 | void JobView::setSuspended(bool suspended) |
95 | { |
96 | typedef QPair<QString, QDBusAbstractInterface*> iFacePair; |
97 | foreach(const iFacePair &pair, m_objectPaths) { |
98 | pair.second->asyncCall(QLatin1String("setSuspended" ), suspended); |
99 | } |
100 | |
101 | m_state = suspended ? Suspended : Running; |
102 | emit changed(m_jobId); |
103 | } |
104 | |
105 | uint JobView::state() const |
106 | { |
107 | return m_state; |
108 | } |
109 | |
110 | void JobView::setTotalAmount(qulonglong amount, const QString &unit) |
111 | { |
112 | typedef QPair<QString, QDBusAbstractInterface*> iFacePair; |
113 | foreach(const iFacePair &pair, m_objectPaths) { |
114 | pair.second->asyncCall(QLatin1String("setTotalAmount" ), amount, unit); |
115 | } |
116 | |
117 | m_totalAmount = amount; |
118 | m_totalUnit = unit; |
119 | |
120 | if (unit == "bytes" ) { |
121 | m_sizeTotal = amount ? KGlobal::locale()->formatByteSize(amount) : QString(); |
122 | |
123 | } else if (unit == "files" ) { |
124 | m_sizeTotal = amount ? i18np("%1 file" , "%1 files" , amount) : QString(); |
125 | |
126 | } else if (unit == "dirs" ) { |
127 | m_sizeTotal = amount ? i18np("%1 folder" , "%1 folders" , amount) : QString(); |
128 | |
129 | } |
130 | emit changed(m_jobId); |
131 | } |
132 | |
133 | QString JobView::sizeTotal() const |
134 | { |
135 | return m_sizeTotal; |
136 | } |
137 | |
138 | void JobView::setProcessedAmount(qulonglong amount, const QString &unit) |
139 | { |
140 | typedef QPair<QString, QDBusAbstractInterface*> iFacePair; |
141 | foreach(const iFacePair &pair, m_objectPaths) { |
142 | pair.second->asyncCall(QLatin1String("setProcessedAmount" ), amount, unit); |
143 | } |
144 | |
145 | m_processAmount = amount; |
146 | m_processUnit = unit; |
147 | |
148 | if (unit == "bytes" ) { |
149 | m_sizeProcessed = amount ? KGlobal::locale()->formatByteSize(amount) : QString(); |
150 | |
151 | } else if (unit == "files" ) { |
152 | m_sizeProcessed = amount ? i18np("%1 file" , "%1 files" , amount) : QString(); |
153 | |
154 | } else if (unit == "dirs" ) { |
155 | m_sizeProcessed = amount ? i18np("%1 folder" , "%1 folders" , amount) : QString(); |
156 | } |
157 | emit changed(m_jobId); |
158 | } |
159 | |
160 | QString JobView::sizeProcessed() const |
161 | { |
162 | return m_sizeProcessed; |
163 | } |
164 | |
165 | void JobView::setPercent(uint value) |
166 | { |
167 | typedef QPair<QString, QDBusAbstractInterface*> iFacePair; |
168 | foreach(const iFacePair &pair, m_objectPaths) { |
169 | pair.second->asyncCall(QLatin1String("setPercent" ), value); |
170 | } |
171 | |
172 | m_percent = value; |
173 | emit changed(m_jobId); |
174 | } |
175 | |
176 | uint JobView::percent() const |
177 | { |
178 | return m_percent; |
179 | } |
180 | |
181 | void JobView::setSpeed(qulonglong bytesPerSecond) |
182 | { |
183 | typedef QPair<QString, QDBusAbstractInterface*> iFacePair; |
184 | foreach(const iFacePair &pair, m_objectPaths) { |
185 | pair.second->asyncCall(QLatin1String("setSpeed" ), bytesPerSecond); |
186 | } |
187 | |
188 | m_speed = bytesPerSecond ? KGlobal::locale()->formatByteSize(bytesPerSecond) : QString(); |
189 | emit changed(m_jobId); |
190 | } |
191 | |
192 | QString JobView::speed() const |
193 | { |
194 | return m_speed; |
195 | } |
196 | |
197 | void JobView::setInfoMessage(const QString &infoMessage) |
198 | { |
199 | typedef QPair<QString, QDBusAbstractInterface*> iFacePair; |
200 | foreach(const iFacePair &pair, m_objectPaths) { |
201 | pair.second->asyncCall(QLatin1String("setInfoMessage" ), infoMessage); |
202 | } |
203 | |
204 | m_infoMessage = infoMessage; |
205 | emit changed(m_jobId); |
206 | } |
207 | |
208 | QString JobView::infoMessage() const |
209 | { |
210 | return m_infoMessage; |
211 | } |
212 | |
213 | bool JobView::setDescriptionField(uint number, const QString &name, const QString &value) |
214 | { |
215 | typedef QPair<QString, QDBusAbstractInterface*> iFacePair; |
216 | foreach(const iFacePair &pair, m_objectPaths) { |
217 | pair.second->asyncCall(QLatin1String("setDescriptionField" ), number, name, value); |
218 | } |
219 | |
220 | if (m_descFields.contains(number)) { |
221 | m_descFields[number].first = name; |
222 | m_descFields[number].second = value; |
223 | } else { |
224 | QPair<QString, QString> tempDescField(name, value); |
225 | m_descFields.insert(number, tempDescField); |
226 | } |
227 | emit changed(m_jobId); |
228 | return true; |
229 | } |
230 | |
231 | void JobView::clearDescriptionField(uint number) |
232 | { |
233 | typedef QPair<QString, QDBusAbstractInterface*> iFacePair; |
234 | foreach(const iFacePair &pair, m_objectPaths) { |
235 | pair.second->asyncCall(QLatin1String("clearDescriptionField" ), number); |
236 | } |
237 | |
238 | if (m_descFields.contains(number)) { |
239 | m_descFields.remove(number); |
240 | } |
241 | emit changed(m_jobId); |
242 | } |
243 | |
244 | void JobView::setAppName(const QString &appName) |
245 | { |
246 | typedef QPair<QString, QDBusAbstractInterface*> iFacePair; |
247 | foreach(const iFacePair &pair, m_objectPaths) { |
248 | pair.second->asyncCall(QLatin1String("setAppName" ), appName); |
249 | } |
250 | |
251 | m_applicationName = appName; |
252 | } |
253 | |
254 | QString JobView::appName() const |
255 | { |
256 | return m_appIconName; |
257 | } |
258 | |
259 | void JobView::setAppIconName(const QString &appIconName) |
260 | { |
261 | typedef QPair<QString, QDBusAbstractInterface*> iFacePair; |
262 | foreach(const iFacePair &pair, m_objectPaths) { |
263 | pair.second->asyncCall(QLatin1String("setAppIconName" ), appIconName); |
264 | } |
265 | |
266 | m_appIconName = appIconName; |
267 | } |
268 | |
269 | QString JobView::appIconName() const |
270 | { |
271 | return m_appIconName; |
272 | } |
273 | |
274 | void JobView::setCapabilities(int capabilities) |
275 | { |
276 | typedef QPair<QString, QDBusAbstractInterface*> iFacePair; |
277 | foreach(const iFacePair &pair, m_objectPaths) { |
278 | pair.second->asyncCall(QLatin1String("setCapabilities" ), capabilities); |
279 | } |
280 | |
281 | m_capabilities = capabilities; |
282 | } |
283 | |
284 | int JobView::capabilities() const |
285 | { |
286 | return m_capabilities; |
287 | } |
288 | |
289 | QString JobView::error() const |
290 | { |
291 | return m_error; |
292 | } |
293 | |
294 | uint JobView::jobId() const |
295 | { |
296 | return m_jobId; |
297 | } |
298 | |
299 | QDBusObjectPath JobView::objectPath() const |
300 | { |
301 | return m_objectPath; |
302 | } |
303 | |
304 | void JobView::setDestUrl(const QDBusVariant &destUrl) |
305 | { |
306 | m_destUrl = destUrl.variant(); |
307 | emit destUrlSet(); |
308 | } |
309 | |
310 | QVariant JobView::destUrl() const |
311 | { |
312 | return m_destUrl; |
313 | } |
314 | |
315 | void JobView::addJobContact(const QString& objectPath, const QString& address) |
316 | { |
317 | org::kde::JobViewV2 *client = |
318 | new org::kde::JobViewV2(address, objectPath, QDBusConnection::sessionBus()); |
319 | |
320 | QPair<QString, QDBusAbstractInterface*> pair(objectPath, client); |
321 | |
322 | //propagate any request signals from the client's job, up to us, then to the parent KJob |
323 | //otherwise e.g. the pause button on plasma's tray would be broken. |
324 | connect(client, SIGNAL(suspendRequested()), this, SIGNAL(suspendRequested())); |
325 | connect(client, SIGNAL(resumeRequested()), this, SIGNAL(resumeRequested())); |
326 | connect(client, SIGNAL(cancelRequested()), this, SIGNAL(cancelRequested())); |
327 | Q_ASSERT(!m_objectPaths.contains(address)); |
328 | m_objectPaths.insert(address, pair); |
329 | |
330 | //If the job already has any information, send it to the contact |
331 | if (m_capabilities > -1) { |
332 | client->asyncCall(QLatin1String("setCapabilities" ), m_capabilities); |
333 | } |
334 | |
335 | if (!m_applicationName.isEmpty()) { |
336 | client->asyncCall(QLatin1String("setAppName" ), m_applicationName); |
337 | } |
338 | |
339 | if (!m_appIconName.isEmpty()) { |
340 | client->asyncCall(QLatin1String("setAppIconName" ), m_appIconName); |
341 | } |
342 | |
343 | if (m_percent > -1) { |
344 | client->asyncCall(QLatin1String("setPercent" ), m_percent); |
345 | } |
346 | |
347 | if (!m_infoMessage.isEmpty()) { |
348 | client->asyncCall(QLatin1String("setInfoMessage" ), m_infoMessage); |
349 | } |
350 | |
351 | if (!m_descFields.isEmpty()) { |
352 | QHashIterator<uint, QPair <QString, QString > > i(m_descFields); |
353 | while (i.hasNext()) { |
354 | i.next(); |
355 | client->asyncCall(QLatin1String("setDescriptionField" ), i.key(), i.value().first, i.value().second); |
356 | } |
357 | } |
358 | |
359 | if (m_state == Suspended) { |
360 | client->asyncCall(QLatin1String("setSuspended" ), true); |
361 | } |
362 | |
363 | if (m_processAmount > 0) { |
364 | client->asyncCall(QLatin1String("setProcessedAmount" ), m_processAmount, m_processUnit); |
365 | } |
366 | |
367 | if (m_totalAmount > 0) { |
368 | client->asyncCall(QLatin1String("setTotalAmount" ), m_totalAmount, m_totalUnit); |
369 | } |
370 | } |
371 | |
372 | QStringList JobView::jobContacts() |
373 | { |
374 | QStringList output; |
375 | QHash<QString, QPair<QString, QDBusAbstractInterface*> >::const_iterator it = m_objectPaths.constBegin(); |
376 | for (; it != m_objectPaths.constEnd(); ++it) { |
377 | //for debug purposes only |
378 | output.append("service name of the interface: " + it.key() + "; objectPath for the interface: " + it.value().first); |
379 | } |
380 | return output; |
381 | } |
382 | |
383 | void JobView::pendingCallStarted() |
384 | { |
385 | ++m_currentPendingCalls; |
386 | } |
387 | |
388 | void JobView::pendingCallFinished(RequestViewCallWatcher* watcher) |
389 | { |
390 | QDBusPendingReply<QDBusObjectPath> reply = *watcher; |
391 | QString address = watcher->service(); |
392 | |
393 | if (reply.isError()) { // this happens if plasma crashed meanwhile |
394 | kWarning() << "got error from" << address << ":" << reply.error(); |
395 | kWarning() << "app name was" << watcher->jobView()->appName(); |
396 | return; |
397 | } |
398 | |
399 | // note: this is the *remote* jobview objectpath, not the kuiserver one. |
400 | QDBusObjectPath objectPath = reply.argumentAt<0>(); |
401 | |
402 | Q_ASSERT(reply.isValid()); |
403 | |
404 | --m_currentPendingCalls; |
405 | |
406 | if (m_isTerminated) { |
407 | |
408 | // do basically the same as terminate() except only for service |
409 | // since this one missed out. |
410 | |
411 | org::kde::JobViewV2 *client = new org::kde::JobViewV2(address, objectPath.path(), QDBusConnection::sessionBus()); |
412 | |
413 | kDebug(7024) << "making async terminate call to objectPath: " << objectPath.path(); |
414 | kDebug(7024) << "this was because a pending call was finished, but the job was already terminated before it returned." ; |
415 | kDebug(7024) << "current pending calls left: " << m_currentPendingCalls; |
416 | |
417 | // forcibly set the percent (should be 100). Since the job missed out on that too. |
418 | client->asyncCall(QLatin1String("setPercent" ), m_percent); |
419 | client->asyncCall(QLatin1String("terminate" ), m_error); |
420 | |
421 | if (m_currentPendingCalls < 1) { |
422 | kDebug() << "no more async calls left pending..emitting finished so we can have ourselves deleted." ; |
423 | emit finished(this); |
424 | } |
425 | } else { |
426 | // add this job contact because we are _not_ just terminating here. |
427 | // we'll need it for regular things like speed changes, etc. |
428 | kDebug(7024) << "adding job contact for address: " << address << " objectPath: " << objectPath.path(); |
429 | addJobContact(objectPath.path(), address); |
430 | } |
431 | } |
432 | |
433 | void JobView::serviceDropped(const QString &address) |
434 | { |
435 | m_objectPaths.remove(address); |
436 | --m_currentPendingCalls; |
437 | } |
438 | |
439 | #include "jobview.moc" |
440 | |