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

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