File indexing completed on 2024-05-12 03:54:56
0001 /* 0002 This file is part of the KDE project 0003 0004 SPDX-FileCopyrightText: 2000 Stephan Kulow <coolo@kde.org> 0005 SPDX-FileCopyrightText: 2000 David Faure <faure@kde.org> 0006 SPDX-FileCopyrightText: 2006 Kevin Ottens <ervin@kde.org> 0007 0008 SPDX-License-Identifier: LGPL-2.0-or-later 0009 */ 0010 0011 #include "kjob.h" 0012 #include "kjob_p.h" 0013 0014 #include "kcoreaddons_debug.h" 0015 #include "kjobuidelegate.h" 0016 0017 #include <QEventLoop> 0018 #include <QTimer> 0019 0020 KJobPrivate::KJobPrivate() 0021 { 0022 } 0023 0024 KJobPrivate::~KJobPrivate() 0025 { 0026 } 0027 0028 KJob::KJob(QObject *parent) 0029 : QObject(parent) 0030 , d_ptr(new KJobPrivate) 0031 { 0032 d_ptr->q_ptr = this; 0033 } 0034 0035 KJob::KJob(KJobPrivate &dd, QObject *parent) 0036 : QObject(parent) 0037 , d_ptr(&dd) 0038 { 0039 d_ptr->q_ptr = this; 0040 } 0041 0042 KJob::~KJob() 0043 { 0044 if (!d_ptr->isFinished) { 0045 d_ptr->isFinished = true; 0046 Q_EMIT finished(this, QPrivateSignal()); 0047 } 0048 0049 delete d_ptr->speedTimer; 0050 delete d_ptr->uiDelegate; 0051 } 0052 0053 void KJob::setUiDelegate(KJobUiDelegate *delegate) 0054 { 0055 Q_D(KJob); 0056 if (!delegate) { 0057 delete d->uiDelegate; 0058 d->uiDelegate = nullptr; 0059 return; 0060 } 0061 0062 if (delegate->setJob(this)) { 0063 delete d->uiDelegate; 0064 d->uiDelegate = delegate; 0065 d->uiDelegate->connectJob(this); 0066 } 0067 } 0068 0069 KJobUiDelegate *KJob::uiDelegate() const 0070 { 0071 return d_func()->uiDelegate; 0072 } 0073 0074 KJob::Capabilities KJob::capabilities() const 0075 { 0076 return d_func()->capabilities; 0077 } 0078 0079 bool KJob::isSuspended() const 0080 { 0081 return d_func()->suspended; 0082 } 0083 0084 void KJob::finishJob(bool emitResult) 0085 { 0086 Q_D(KJob); 0087 Q_ASSERT(!d->isFinished); 0088 d->isFinished = true; 0089 0090 if (d->eventLoop) { 0091 d->eventLoop->quit(); 0092 } 0093 0094 // If we are displaying a progress dialog, remove it first. 0095 Q_EMIT finished(this, QPrivateSignal()); 0096 0097 if (emitResult) { 0098 Q_EMIT result(this, QPrivateSignal()); 0099 } 0100 0101 if (isAutoDelete()) { 0102 deleteLater(); 0103 } 0104 } 0105 0106 bool KJob::kill(KillVerbosity verbosity) 0107 { 0108 Q_D(KJob); 0109 if (d->isFinished) { 0110 return true; 0111 } 0112 0113 if (doKill()) { 0114 // A subclass can (but should not) call emitResult() or kill() 0115 // from doKill() and thus set isFinished to true. 0116 if (!d->isFinished) { 0117 setError(KilledJobError); 0118 finishJob(verbosity != Quietly); 0119 } 0120 return true; 0121 } else { 0122 return false; 0123 } 0124 } 0125 0126 bool KJob::suspend() 0127 { 0128 Q_D(KJob); 0129 if (!d->suspended) { 0130 if (doSuspend()) { 0131 d->suspended = true; 0132 Q_EMIT suspended(this, QPrivateSignal()); 0133 0134 return true; 0135 } 0136 } 0137 0138 return false; 0139 } 0140 0141 bool KJob::resume() 0142 { 0143 Q_D(KJob); 0144 if (d->suspended) { 0145 if (doResume()) { 0146 d->suspended = false; 0147 Q_EMIT resumed(this, QPrivateSignal()); 0148 0149 return true; 0150 } 0151 } 0152 0153 return false; 0154 } 0155 0156 bool KJob::doKill() 0157 { 0158 return false; 0159 } 0160 0161 bool KJob::doSuspend() 0162 { 0163 return false; 0164 } 0165 0166 bool KJob::doResume() 0167 { 0168 return false; 0169 } 0170 0171 void KJob::setCapabilities(KJob::Capabilities capabilities) 0172 { 0173 Q_D(KJob); 0174 d->capabilities = capabilities; 0175 } 0176 0177 bool KJob::exec() 0178 { 0179 Q_D(KJob); 0180 // Usually this job would delete itself, via deleteLater() just after 0181 // emitting result() (unless configured otherwise). Since we use an event 0182 // loop below, that event loop will process the deletion event and we'll 0183 // have been deleted when exec() returns. This crashes, so temporarily 0184 // suspend autodeletion and manually do it afterwards. 0185 const bool wasAutoDelete = isAutoDelete(); 0186 setAutoDelete(false); 0187 0188 Q_ASSERT(!d->eventLoop); 0189 0190 QEventLoop loop(this); 0191 d->eventLoop = &loop; 0192 0193 start(); 0194 if (!d->isFinished) { 0195 d->m_startedWithExec = true; 0196 d->eventLoop->exec(QEventLoop::ExcludeUserInputEvents); 0197 } 0198 d->eventLoop = nullptr; 0199 0200 if (wasAutoDelete) { 0201 deleteLater(); 0202 } 0203 return (d->error == NoError); 0204 } 0205 0206 int KJob::error() const 0207 { 0208 return d_func()->error; 0209 } 0210 0211 QString KJob::errorText() const 0212 { 0213 return d_func()->errorText; 0214 } 0215 0216 QString KJob::errorString() const 0217 { 0218 return d_func()->errorText; 0219 } 0220 0221 qulonglong KJob::processedAmount(Unit unit) const 0222 { 0223 if (unit >= UnitsCount) { 0224 qCWarning(KCOREADDONS_DEBUG) << "KJob::processedAmount() was called on an invalid Unit" << unit; 0225 return 0; 0226 } 0227 0228 return d_func()->m_jobAmounts[unit].processedAmount; 0229 } 0230 0231 qulonglong KJob::totalAmount(Unit unit) const 0232 { 0233 if (unit >= UnitsCount) { 0234 qCWarning(KCOREADDONS_DEBUG) << "KJob::totalAmount() was called on an invalid Unit" << unit; 0235 return 0; 0236 } 0237 0238 return d_func()->m_jobAmounts[unit].totalAmount; 0239 } 0240 0241 unsigned long KJob::percent() const 0242 { 0243 return d_func()->percentage; 0244 } 0245 0246 bool KJob::isFinished() const 0247 { 0248 return d_func()->isFinished; 0249 } 0250 0251 void KJob::setError(int errorCode) 0252 { 0253 Q_D(KJob); 0254 d->error = errorCode; 0255 } 0256 0257 void KJob::setErrorText(const QString &errorText) 0258 { 0259 Q_D(KJob); 0260 d->errorText = errorText; 0261 } 0262 0263 void KJob::setProcessedAmount(Unit unit, qulonglong amount) 0264 { 0265 if (unit >= UnitsCount) { 0266 qCWarning(KCOREADDONS_DEBUG) << "KJob::setProcessedAmount() was called on an invalid Unit" << unit; 0267 return; 0268 } 0269 0270 Q_D(KJob); 0271 0272 auto &[processed, total] = d->m_jobAmounts[unit]; 0273 0274 const bool should_emit = (processed != amount); 0275 0276 processed = amount; 0277 0278 if (should_emit) { 0279 Q_EMIT processedAmountChanged(this, unit, amount, QPrivateSignal{}); 0280 if (unit == d->progressUnit) { 0281 Q_EMIT processedSize(this, amount); 0282 emitPercent(processed, total); 0283 } 0284 } 0285 } 0286 0287 void KJob::setTotalAmount(Unit unit, qulonglong amount) 0288 { 0289 if (unit >= UnitsCount) { 0290 qCWarning(KCOREADDONS_DEBUG) << "KJob::setTotalAmount() was called on an invalid Unit" << unit; 0291 return; 0292 } 0293 0294 Q_D(KJob); 0295 0296 auto &[processed, total] = d->m_jobAmounts[unit]; 0297 0298 const bool should_emit = (total != amount); 0299 0300 total = amount; 0301 0302 if (should_emit) { 0303 Q_EMIT totalAmountChanged(this, unit, amount, QPrivateSignal{}); 0304 if (unit == d->progressUnit) { 0305 Q_EMIT totalSize(this, amount); 0306 emitPercent(processed, total); 0307 } 0308 } 0309 } 0310 0311 void KJob::setProgressUnit(Unit unit) 0312 { 0313 Q_D(KJob); 0314 d->progressUnit = unit; 0315 } 0316 0317 void KJob::setPercent(unsigned long percentage) 0318 { 0319 Q_D(KJob); 0320 if (d->percentage != percentage) { 0321 d->percentage = percentage; 0322 Q_EMIT percentChanged(this, percentage, QPrivateSignal{}); 0323 } 0324 } 0325 0326 void KJob::emitResult() 0327 { 0328 if (!d_func()->isFinished) { 0329 finishJob(true); 0330 } 0331 } 0332 0333 void KJob::emitPercent(qulonglong processedAmount, qulonglong totalAmount) 0334 { 0335 if (totalAmount != 0) { 0336 setPercent(100.0 * processedAmount / totalAmount); 0337 } 0338 } 0339 0340 void KJob::emitSpeed(unsigned long value) 0341 { 0342 Q_D(KJob); 0343 if (!d->speedTimer) { 0344 d->speedTimer = new QTimer(this); 0345 connect(d->speedTimer, &QTimer::timeout, this, [d]() { 0346 d->speedTimeout(); 0347 }); 0348 } 0349 0350 Q_EMIT speed(this, value); 0351 d->speedTimer->start(5000); // 5 seconds interval should be enough 0352 } 0353 0354 void KJobPrivate::speedTimeout() 0355 { 0356 Q_Q(KJob); 0357 // send 0 and stop the timer 0358 // timer will be restarted only when we receive another speed event 0359 Q_EMIT q->speed(q, 0); 0360 speedTimer->stop(); 0361 } 0362 0363 bool KJob::isAutoDelete() const 0364 { 0365 Q_D(const KJob); 0366 return d->isAutoDelete; 0367 } 0368 0369 void KJob::setAutoDelete(bool autodelete) 0370 { 0371 Q_D(KJob); 0372 d->isAutoDelete = autodelete; 0373 } 0374 0375 void KJob::setFinishedNotificationHidden(bool hide) 0376 { 0377 Q_D(KJob); 0378 d->m_hideFinishedNotification = hide; 0379 } 0380 0381 bool KJob::isFinishedNotificationHidden() const 0382 { 0383 Q_D(const KJob); 0384 return d->m_hideFinishedNotification; 0385 } 0386 0387 bool KJob::isStartedWithExec() const 0388 { 0389 Q_D(const KJob); 0390 return d->m_startedWithExec; 0391 } 0392 0393 #include "moc_kjob.cpp"