1/*
2 This file is part of the KDE project
3
4 SPDX-FileCopyrightText: 2000 Stephan Kulow <coolo@kde.org>
5 SPDX-FileCopyrightText: 2000 David Faure <faure@kde.org>
6 SPDX-FileCopyrightText: 2006 Kevin Ottens <ervin@kde.org>
7
8 SPDX-License-Identifier: LGPL-2.0-or-later
9*/
10
11#ifndef KJOB_H
12#define KJOB_H
13
14#include <QObject>
15#include <QPair>
16#include <kcoreaddons_export.h>
17#include <memory>
18
19class KJobUiDelegate;
20
21class KJobPrivate;
22
23/*!
24 * \class KJob
25 * \inmodule KCoreAddons
26 *
27 * \brief The base class for all jobs.
28 *
29 * For all jobs created in an application, the code looks like
30 *
31 * \code
32 * void SomeClass::methodWithAsynchronousJobCall()
33 * {
34 * KJob *job = someoperation(some parameters);
35 * connect(job, &KJob::result, this, &SomeClass::handleResult);
36 * job->start();
37 * }
38 * \endcode
39 * (other connects, specific to the job)
40 *
41 * And handleResult is usually at least:
42 *
43 * \code
44 * void SomeClass::handleResult(KJob *job)
45 * {
46 * if (job->error()) {
47 * doSomething();
48 * }
49 * }
50 * \endcode
51 *
52 * With the synchronous interface the code looks like
53 *
54 * \code
55 * void SomeClass::methodWithSynchronousJobCall()
56 * {
57 * KJob *job = someoperation( some parameters );
58 * if (!job->exec()) {
59 * // An error occurred
60 * } else {
61 * // Do something
62 * }
63 * }
64 * \endcode
65 *
66 * Subclasses must implement start(), which should trigger the execution of
67 * the job (although the work should be done asynchronously). errorString()
68 * should also be reimplemented by any subclasses that introduce new error
69 * codes.
70 *
71 * \note KJob and its subclasses are meant to be used in a fire-and-forget way.
72 * Jobs will delete themselves when they finish using deleteLater() (although
73 * this behaviour can be changed), so a job instance will disappear after the
74 * next event loop run.
75 */
76class KCOREADDONS_EXPORT KJob : public QObject
77{
78 Q_OBJECT
79
80 /*!
81 * \property KJob::error
82 */
83 Q_PROPERTY(int error READ error NOTIFY result)
84
85 /*!
86 * \property KJob::errorText
87 */
88 Q_PROPERTY(QString errorText READ errorText NOTIFY result)
89
90 /*!
91 * \property KJob::errorString
92 */
93 Q_PROPERTY(QString errorString READ errorString NOTIFY result)
94
95 /*!
96 * \property KJob::percent
97 */
98 Q_PROPERTY(ulong percent READ percent NOTIFY percentChanged) // KF6 TODO: make "int", is enough
99
100 /*!
101 * \property KJob::capabilities
102 */
103 Q_PROPERTY(Capabilities capabilities READ capabilities CONSTANT)
104
105public:
106 /*!
107 * Describes the unit used in the methods that handle reporting the job progress info.
108 * \sa totalAmount
109 *
110 * \value Bytes Directory and file sizes in bytes
111 * \value Files The number of files handled by the job
112 * \value Directories The number of directories handled by the job
113 * \value [since 5.72] Items The number of items (e.g. both directories and files) handled by the job
114 * \value [since 5.87] UnitsCount Used internally only, do not use
115 */
116 enum Unit {
117 Bytes = 0,
118 Files,
119 Directories,
120 Items,
121 UnitsCount,
122 };
123 Q_ENUM(Unit)
124
125 /*!
126 * \value NoCapabilities None of the capabilities exist
127 * \value Killable The job can be killed
128 * \value Suspendable The job can be suspended
129 */
130 enum Capability {
131 NoCapabilities = 0x0000,
132 Killable = 0x0001,
133 Suspendable = 0x0002,
134 };
135 Q_ENUM(Capability)
136
137 Q_DECLARE_FLAGS(Capabilities, Capability)
138 Q_FLAG(Capabilities)
139
140 /*!
141 * Creates a new KJob object.
142 *
143 * \a parent the parent QObject
144 */
145 explicit KJob(QObject *parent = nullptr);
146
147 ~KJob() override;
148
149 /*!
150 * Attach a UI delegate to this job.
151 *
152 * If the job had another UI delegate, it's automatically deleted. Once
153 * attached to the job, the UI delegate will be deleted with the job.
154 *
155 * \a delegate the new UI delegate to use
156 *
157 * \sa KJobUiDelegate
158 */
159 void setUiDelegate(KJobUiDelegate *delegate);
160
161 /*!
162 * Returns the delegate attached to this job, or \c nullptr if there's no such delegate
163 */
164 KJobUiDelegate *uiDelegate() const;
165
166 /*!
167 * Returns the capabilities that this job supports
168 * \sa setCapabilities()
169 */
170 Capabilities capabilities() const;
171
172 /*!
173 * Returns if the job was suspended with the suspend() call.
174 *
175 * \sa suspend(), resume()
176 */
177 bool isSuspended() const;
178
179 // TODO KF7 make it non-virtual and expose a doStart protected instead
180 /*!
181 * Starts the job asynchronously.
182 *
183 * When the job is finished, result() is emitted.
184 *
185 * Warning: Never implement any synchronous workload in this method. This method
186 * should just trigger the job startup, not do any work itself. It is expected to
187 * be non-blocking.
188 *
189 * This is the method all subclasses need to implement.
190 * It should setup and trigger the workload of the job. It should not do any
191 * work itself. This includes all signals and terminating the job, e.g. by
192 * emitResult(). The workload, which could be another method of the
193 * subclass, is to be triggered using the event loop, e.g. by code like:
194 * \code
195 * void ExampleJob::start()
196 * {
197 * QTimer::singleShot(0, this, &ExampleJob::doWork);
198 * }
199 * \endcode
200 */
201 Q_SCRIPTABLE virtual void start() = 0;
202
203 /*!
204 * \value Quietly
205 * \value EmitResult
206 */
207 enum KillVerbosity {
208 Quietly,
209 EmitResult,
210 };
211 Q_ENUM(KillVerbosity)
212
213public Q_SLOTS:
214 /*!
215 * Aborts this job.
216 *
217 * This kills and deletes the job.
218 *
219 * \a verbosity if equals to EmitResult, Job will emit signal result
220 * and ask uiserver to close the progress window.
221 *
222 * \a verbosity is set to EmitResult for subjobs. Whether applications
223 * should call with Quietly or EmitResult depends on whether they rely
224 * on result being emitted or not. Please notice that if \a verbosity is
225 * set to Quietly, signal result will NOT be emitted.
226 *
227 * Returns \c true if the operation is supported and succeeded, false otherwise
228 */
229 bool kill(KJob::KillVerbosity verbosity = KJob::Quietly);
230
231 /*!
232 * Suspends this job.
233 *
234 * The job should be kept in a state in which it is possible to resume it.
235 *
236 * Returns \c true if the operation is supported and succeeded, \c false otherwise
237 */
238 bool suspend();
239
240 /*!
241 * Resumes this job.
242 *
243 * Returns \c true if the operation is supported and succeeded, \c false otherwise
244 */
245 bool resume();
246
247protected:
248 /*!
249 * Aborts this job quietly.
250 *
251 * This simply kills the job, no error reporting or job deletion should be involved.
252 *
253 * Returns \c true if the operation is supported and succeeded, \c false otherwise
254 */
255 virtual bool doKill();
256
257 /*!
258 * Suspends this job.
259 *
260 * Returns \c true if the operation is supported and succeeded, \c false otherwise
261 */
262 virtual bool doSuspend();
263
264 /*!
265 * Resumes this job.
266 *
267 * Returns \c true if the operation is supported and succeeded, \c false otherwise
268 */
269 virtual bool doResume();
270
271 /*!
272 * Sets the capabilities for this job.
273 *
274 * \a capabilities are the capabilities supported by this job
275 *
276 * \sa capabilities()
277 */
278 void setCapabilities(Capabilities capabilities);
279
280public:
281 /*!
282 * Executes the job synchronously.
283 *
284 * This will start a nested QEventLoop internally. Nested event loop can be dangerous and
285 * can have unintended side effects, you should avoid calling exec() whenever you can and use the
286 * asynchronous interface of KJob instead.
287 *
288 * Should you indeed call this method, you need to make sure that all callers are reentrant,
289 * so that events delivered by the inner event loop don't cause non-reentrant functions to be
290 * called, which usually wreaks havoc.
291 *
292 * Note that the event loop started by this method does not process user input events, which means
293 * your user interface will effectively be blocked. Other events like paint or network events are
294 * still being processed. The advantage of not processing user input events is that the chance of
295 * accidental reentrance is greatly reduced. Still you should avoid calling this function.
296 *
297 * Returns \c true if the job has been executed without error, \c false otherwise
298 */
299 bool exec();
300
301 /*!
302 * \value NoError Indicates there is no error
303 * \value KilledJobError Indicates the job was killed
304 * \value UserDefinedError Subclasses should define error codes starting at this value
305 */
306 enum {
307 NoError = 0,
308 KilledJobError = 1,
309 UserDefinedError = 100,
310 };
311
312 /*!
313 * Returns the error code, if there has been an error.
314 *
315 * Only call this method from the slot connected to result().
316 *
317 * Returns the error code for this job, 0 if no error.
318 */
319 int error() const;
320
321 /*!
322 * Returns the error text if there has been an error.
323 *
324 * Only call if error is not 0.
325 *
326 * This is usually some extra data associated with the error,
327 * such as a URL. Use errorString() to get a human-readable,
328 * translated message.
329 *
330 * Returns a string to help understand the error
331 */
332 QString errorText() const;
333
334 /*!
335 * A human-readable error message.
336 *
337 * This provides a translated, human-readable description of the
338 * error. Only call if error is not 0.
339 *
340 * Subclasses should implement this to create a translated
341 * error message from the error code and error text.
342 * For example:
343 * \code
344 * if (error() == ReadFailed) {
345 * i18n("Could not read \"%1\"", errorText());
346 * }
347 * \endcode
348 *
349 * Returns a translated error message, providing error() is not 0
350 */
351 virtual QString errorString() const;
352
353 /*!
354 * Returns the processed amount of a given unit for this job.
355 *
356 * \a unit the unit of the requested amount
357 */
358 Q_SCRIPTABLE qulonglong processedAmount(Unit unit) const;
359
360 /*!
361 * Returns the total amount of a given unit for this job.
362 *
363 * \a unit the unit of the requested amount
364 */
365 Q_SCRIPTABLE qulonglong totalAmount(Unit unit) const;
366
367 /*!
368 * Returns the overall progress of this job
369 */
370 unsigned long percent() const;
371
372 /*!
373 * Sets the auto-delete property of the job. If \a autodelete is
374 * set to \c false the job will not delete itself once it is finished.
375 *
376 * The default for any KJob is to automatically delete itself, which
377 * implies that the job was created on the heap (using new).
378 * If the job is created on the stack (which isn't the typical use-case
379 * for a job) then you must set auto-delete to \c false, otherwise you
380 * could get a crash when the job finishes and tries to delete itself.
381 *
382 * \note If you set auto-delete to \c false then you need to kill the
383 * job manually, ideally by calling kill().
384 *
385 * \a autodelete set to \c false to disable automatic deletion
386 * of the job.
387 */
388 void setAutoDelete(bool autodelete);
389
390 /*!
391 * Returns whether this job automatically deletes itself once
392 * the job is finished.
393 */
394 bool isAutoDelete() const;
395
396 /*!
397 * This method can be used to indicate to classes listening to signals from a job
398 * that they should ideally show a progress bar, but not a finished notification.
399 *
400 * For example when opening a remote URL, a job will emit the progress of the
401 * download, which can be used to show a progress dialog or a Plasma notification,
402 * then when the job is done it'll emit e.g. the finished signal. Showing the user the
403 * progress dialog is useful, however the dialog/notification about the download being
404 * finished isn't of much interest, because the user can see the application that invoked
405 * the job opening the actual file that was downloaded.
406 *
407 * \since 5.92
408 */
409 void setFinishedNotificationHidden(bool hide = true);
410
411 /*!
412 * Whether to not show a finished notification when a job's finished
413 * signal is emitted.
414 *
415 * \sa setFinishedNotificationHidden()
416 *
417 * \since 5.92
418 */
419 bool isFinishedNotificationHidden() const;
420
421 /*!
422 * Returns \c true if this job was started with exec(), which starts a nested event-loop
423 * (with QEventLoop::ExcludeUserInputEvents, which blocks the GUI), otherwise returns
424 * \c false which indicates this job was started asynchronously with start().
425 *
426 * This is useful for code that for example shows a dialog to ask the user a question,
427 * and that would be no-op since the user cannot interact with the dialog.
428 *
429 * \since 5.95
430 */
431 bool isStartedWithExec() const;
432
433 /*!
434 * The number of milliseconds the job has been running for.
435 * Starting from the last start() call.
436 *
437 * Sub-classes must call startElapsedTimer() from their start() implementation, to get elapsedTime() measurement. Otherwise this method will always return
438 * 0.
439 *
440 * The time when paused is excluded.
441 *
442 * \since 6.8
443 */
444 qint64 elapsedTime() const;
445
446Q_SIGNALS:
447
448 /*!
449 * Emitted when the job is finished, in any case. It is used to notify
450 * observers that the job is terminated and that progress can be hidden.
451 *
452 * Since 5.75 this signal is guaranteed to be emitted exactly once.
453 *
454 * This is a private signal, it can't be emitted directly by subclasses of
455 * KJob, use emitResult() instead.
456 *
457 * In general, to be notified of a job's completion, client code should connect to result()
458 * rather than finished(), so that kill(Quietly) is indeed quiet.
459 * However if you store a list of jobs and they might get killed silently,
460 * then you must connect to this instead of result(), to avoid dangling pointers in your list.
461 *
462 * \a job the job that emitted this signal
463 *
464 * \sa result
465 */
466 void finished(KJob *job, QPrivateSignal);
467
468 /*!
469 * Emitted when the job is suspended.
470 *
471 * This is a private signal, it can't be emitted directly by subclasses of
472 * KJob.
473 *
474 * \a job the job that emitted this signal
475 */
476 void suspended(KJob *job, QPrivateSignal);
477
478 /*!
479 * Emitted when the job is resumed.
480 *
481 * \a job the job that emitted this signal
482 */
483 void resumed(KJob *job, QPrivateSignal);
484
485 /*!
486 * Emitted when the job is finished (except when killed with KJob::Quietly).
487 *
488 * Since 5.75 this signal is guaranteed to be emitted at most once.
489 *
490 * Use error to know if the job was finished with error.
491 *
492 * This is a private signal, it can't be emitted directly by subclasses of
493 * KJob, use emitResult() instead.
494 *
495 * Please connect to this signal instead of finished.
496 *
497 * \a job the job that emitted this signal
498 *
499 * \sa kill
500 */
501 void result(KJob *job, QPrivateSignal);
502
503 /*!
504 * Emitted to display general description of this job. A description has
505 * a title and two optional fields which can be used to complete the
506 * description.
507 *
508 * Examples of titles are "Copying", "Creating resource", etc.
509 * The fields of the description can be "Source" with an URL, and,
510 * "Destination" with an URL for a "Copying" description.
511 *
512 * \a job the job that emitted this signal
513 *
514 * \a title the general description of the job
515 *
516 * \a field1 first field (localized name and value)
517 *
518 * \a field2 second field (localized name and value)
519 */
520 void description(KJob *job,
521 const QString &title,
522 const QPair<QString, QString> &field1 = QPair<QString, QString>(),
523 const QPair<QString, QString> &field2 = QPair<QString, QString>());
524 /*!
525 * Emitted to display state information about this job.
526 * Examples of message are "Resolving host", "Connecting to host...", etc.
527 *
528 * \a job the job that emitted this signal
529 *
530 * \a message the info message
531 */
532 void infoMessage(KJob *job, const QString &message);
533
534 /*!
535 * Emitted to display a warning about this job.
536 *
537 * \a job the job that emitted this signal
538 *
539 * \a message the warning message
540 */
541 void warning(KJob *job, const QString &message);
542
543Q_SIGNALS:
544 /*!
545 * Emitted when we know the amount the job will have to process. The unit of this
546 * amount is sent too. It can be emitted several times if the job manages several
547 * different units.
548 *
549 * \note This is a private signal, it shouldn't be emitted directly by subclasses of
550 * KJob, use setTotalAmount() instead.
551 *
552 * \a job the job that emitted this signal
553 *
554 * \a unit the unit of the total amount
555 *
556 * \a amount the total amount
557 *
558 * \since 5.80
559 */
560 void totalAmountChanged(KJob *job, KJob::Unit unit, qulonglong amount, QPrivateSignal);
561
562 /*!
563 * Regularly emitted to show the progress of this job by giving the current amount.
564 * The unit of this amount is sent too. It can be emitted several times if the job
565 * manages several different units.
566 *
567 * \note This is a private signal, it shouldn't be emitted directly by subclasses of
568 * KJob, use setProcessedAmount() instead.
569 *
570 * \a job the job that emitted this signal
571 *
572 * \a unit the unit of the processed amount
573 *
574 * \a amount the processed amount
575 *
576 * \since 5.80
577 */
578 void processedAmountChanged(KJob *job, KJob::Unit unit, qulonglong amount, QPrivateSignal);
579
580 /*!
581 * Emitted when we know the size of this job (data size in bytes for transfers,
582 * number of entries for listings, etc).
583 *
584 * \note This is a private signal, it shouldn't be emitted directly by subclasses of
585 * KJob, use setTotalAmount() instead.
586 *
587 * \a job the job that emitted this signal
588 *
589 * \a size the total size
590 */
591 void totalSize(KJob *job, qulonglong size);
592
593 /*!
594 * Regularly emitted to show the progress of this job
595 * (current data size in bytes for transfers, entries listed, etc.).
596 *
597 * \note This is a private signal, it shouldn't be emitted directly by subclasses of
598 * KJob, use setProcessedAmount() instead.
599 *
600 * \a job the job that emitted this signal
601 *
602 * \a size the processed size
603 */
604 void processedSize(KJob *job, qulonglong size);
605
606 /*!
607 * Progress signal showing the overall progress of the job. This is
608 * valid for any kind of job, and allows using a progress bar very
609 * easily. (see KProgressBar).
610 *
611 * Note that this signal is not emitted for finished jobs.
612 *
613 * \note This is a private signal, it shouldn't be emitted directly
614 * by subclasses of KJob, use emitPercent(), setPercent() setTotalAmount()
615 * or setProcessedAmount() instead.
616 *
617 * \a job the job that emitted this signal
618 *
619 * \a percent the percentage
620 *
621 * \since 5.80
622 */
623 void percentChanged(KJob *job, unsigned long percent, QPrivateSignal);
624
625 /*!
626 * Emitted to display information about the speed of this job.
627 *
628 * \note This is a private signal, it shouldn't be emitted directly by subclasses of
629 * KJob, use emitSpeed() instead.
630 *
631 * \a job the job that emitted this signal
632 *
633 * \a speed the speed in bytes/s
634 */
635 void speed(KJob *job, unsigned long speed);
636
637protected:
638 /*!
639 * Returns if the job has been finished and has emitted the finished() signal.
640 * \sa finished()
641 * \since 5.75
642 */
643 // KF6 TODO: make public. Useful at least for unittests that run multiple jobs in parallel.
644 bool isFinished() const;
645
646 /*!
647 * Sets the error code.
648 *
649 * It should be called when an error
650 * is encountered in the job, just before calling emitResult().
651 *
652 * You should define an (anonymous) enum of error codes,
653 * with values starting at KJob::UserDefinedError, and use
654 * those. For example,
655 * \code
656 * enum {
657 * InvalidFoo = UserDefinedError,
658 * BarNotFound,
659 * };
660 * \endcode
661 *
662 * \a errorCode the error code
663 *
664 * \sa emitResult()
665 */
666 void setError(int errorCode);
667
668 /*!
669 * Sets the error text.
670 *
671 * It should be called when an error
672 * is encountered in the job, just before calling emitResult().
673 *
674 * Provides extra information about the error that cannot be
675 * determined directly from the error code. For example, a
676 * URL or filename. This string is not normally translatable.
677 *
678 * \a errorText the error text
679 *
680 * \sa emitResult(), errorString(), setError()
681 */
682 void setErrorText(const QString &errorText);
683
684 /*!
685 * Sets the processed size. The processedAmount() and percent() signals
686 * are emitted if the values changed. The percent() signal is emitted
687 * only for the progress unit.
688 *
689 * \a unit the unit of the new processed amount
690 *
691 * \a amount the new processed amount
692 */
693 void setProcessedAmount(Unit unit, qulonglong amount);
694
695 /*!
696 * Sets the total size. The totalSize() and percent() signals
697 * are emitted if the values changed. The percent() signal is emitted
698 * only for the progress unit.
699 *
700 * \a unit the unit of the new total amount
701 *
702 * \a amount the new total amount
703 */
704 void setTotalAmount(Unit unit, qulonglong amount);
705
706 /*!
707 * Sets the unit that will be used internally to calculate
708 * the progress percentage.
709 * The default progress unit is Bytes.
710 * \since 5.76
711 */
712 void setProgressUnit(Unit unit);
713
714 /*!
715 * Sets the overall progress of the job. The percent() signal
716 * is emitted if the value changed.
717 *
718 * The job takes care of this if you call setProcessedAmount
719 * in Bytes (or the unit set by setProgressUnit).
720 * This method allows you to set your own progress, as an alternative.
721 *
722 * \a percentage the new overall progress
723 */
724 void setPercent(unsigned long percentage);
725
726 /*!
727 * Utility function to emit the result signal, and end this job.
728 * It first notifies the observers to hide the progress for this job using
729 * the finished() signal.
730 *
731 * \note Deletes this job using deleteLater().
732 *
733 * \sa result()
734 * \sa finished()
735 */
736 void emitResult();
737
738 /*!
739 * Utility function for inherited jobs.
740 * Emits the percent signal if bigger than previous value,
741 * after calculating it from the parameters.
742 *
743 * \a processedAmount the processed amount
744 *
745 * \a totalAmount the total amount
746 *
747 * \sa percent()
748 */
749 void emitPercent(qulonglong processedAmount, qulonglong totalAmount);
750
751 /*!
752 * Utility function for inherited jobs.
753 * Emits the speed signal and starts the timer for removing that info
754 *
755 * \a speed the speed in bytes/s
756 */
757 void emitSpeed(unsigned long speed);
758
759 /*!
760 * Starts the internal elapsed time measurement timer.
761 *
762 * Sub-classes must call startElapsedTimer() from their start() implementation, to get elapsedTime() measurement.
763 * Otherwise elapsedTimer() method will always return 0.
764 *
765 * \since 6.8
766 */
767 void startElapsedTimer();
768
769protected:
770 std::unique_ptr<KJobPrivate> const d_ptr;
771
772 KCOREADDONS_NO_EXPORT KJob(KJobPrivate &dd, QObject *parent);
773
774private:
775 KCOREADDONS_NO_EXPORT void finishJob(bool emitResult);
776
777 Q_DECLARE_PRIVATE(KJob)
778};
779
780Q_DECLARE_OPERATORS_FOR_FLAGS(KJob::Capabilities)
781
782#endif
783

source code of kcoreaddons/src/lib/jobs/kjob.h