File indexing completed on 2024-05-19 03:56:21
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 #ifndef KJOB_H 0012 #define KJOB_H 0013 0014 #include <QObject> 0015 #include <QPair> 0016 #include <kcoreaddons_export.h> 0017 #include <memory> 0018 0019 class KJobUiDelegate; 0020 0021 class KJobPrivate; 0022 /** 0023 * @class KJob kjob.h KJob 0024 * 0025 * The base class for all jobs. 0026 * For all jobs created in an application, the code looks like 0027 * 0028 * \code 0029 * void SomeClass::methodWithAsynchronousJobCall() 0030 * { 0031 * KJob *job = someoperation(some parameters); 0032 * connect(job, &KJob::result, this, &SomeClass::handleResult); 0033 * job->start(); 0034 * } 0035 * \endcode 0036 * (other connects, specific to the job) 0037 * 0038 * And handleResult is usually at least: 0039 * 0040 * \code 0041 * void SomeClass::handleResult(KJob *job) 0042 * { 0043 * if (job->error()) { 0044 * doSomething(); 0045 * } 0046 * } 0047 * \endcode 0048 * 0049 * With the synchronous interface the code looks like 0050 * 0051 * \code 0052 * void SomeClass::methodWithSynchronousJobCall() 0053 * { 0054 * KJob *job = someoperation( some parameters ); 0055 * if (!job->exec()) { 0056 * // An error occurred 0057 * } else { 0058 * // Do something 0059 * } 0060 * } 0061 * \endcode 0062 * 0063 * Subclasses must implement start(), which should trigger the execution of 0064 * the job (although the work should be done asynchronously). errorString() 0065 * should also be reimplemented by any subclasses that introduce new error 0066 * codes. 0067 * 0068 * @note KJob and its subclasses are meant to be used in a fire-and-forget way. 0069 * Jobs will delete themselves when they finish using deleteLater() (although 0070 * this behaviour can be changed), so a job instance will disappear after the 0071 * next event loop run. 0072 */ 0073 class KCOREADDONS_EXPORT KJob : public QObject 0074 { 0075 Q_OBJECT 0076 Q_PROPERTY(int error READ error NOTIFY result) 0077 Q_PROPERTY(QString errorText READ errorText NOTIFY result) 0078 Q_PROPERTY(QString errorString READ errorString NOTIFY result) 0079 Q_PROPERTY(ulong percent READ percent NOTIFY percentChanged) // KF6 TODO: make "int", is enough 0080 Q_PROPERTY(Capabilities capabilities READ capabilities CONSTANT) 0081 0082 public: 0083 /** 0084 * Describes the unit used in the methods that handle reporting the job progress info. 0085 * @see totalAmount 0086 */ 0087 enum Unit { 0088 Bytes = 0, ///< Directory and file sizes in bytes 0089 Files, ///< The number of files handled by the job 0090 Directories, ///< The number of directories handled by the job 0091 Items, ///< The number of items (e.g. both directories and files) handled by the job 0092 ///< @since 5.72 0093 0094 UnitsCount, ///< @internal since 5.87, used internally only, do not use. 0095 }; 0096 Q_ENUM(Unit) 0097 0098 /** 0099 * @see Capabilities 0100 */ 0101 enum Capability { 0102 NoCapabilities = 0x0000, ///< None of the capabilities exist 0103 Killable = 0x0001, ///< The job can be killed 0104 Suspendable = 0x0002, ///< The job can be suspended 0105 }; 0106 Q_ENUM(Capability) 0107 0108 /** 0109 * Stores a combination of #Capability values. 0110 */ 0111 Q_DECLARE_FLAGS(Capabilities, Capability) 0112 Q_FLAG(Capabilities) 0113 0114 /** 0115 * Creates a new KJob object. 0116 * 0117 * @param parent the parent QObject 0118 */ 0119 explicit KJob(QObject *parent = nullptr); 0120 0121 /** 0122 * Destroys a KJob object. 0123 */ 0124 ~KJob() override; 0125 0126 /** 0127 * Attach a UI delegate to this job. 0128 * 0129 * If the job had another UI delegate, it's automatically deleted. Once 0130 * attached to the job, the UI delegate will be deleted with the job. 0131 * 0132 * @param delegate the new UI delegate to use 0133 * @see KJobUiDelegate 0134 */ 0135 void setUiDelegate(KJobUiDelegate *delegate); 0136 0137 /** 0138 * Retrieves the delegate attached to this job. 0139 * 0140 * @return the delegate attached to this job, or @c nullptr if there's no such delegate 0141 */ 0142 KJobUiDelegate *uiDelegate() const; 0143 0144 /** 0145 * Returns the capabilities of this job. 0146 * 0147 * @return the capabilities that this job supports 0148 * @see setCapabilities() 0149 */ 0150 Capabilities capabilities() const; 0151 0152 /** 0153 * Returns if the job was suspended with the suspend() call. 0154 * 0155 * @return if the job was suspended 0156 * @see suspend() resume() 0157 */ 0158 bool isSuspended() const; 0159 0160 /** 0161 * Starts the job asynchronously. 0162 * 0163 * When the job is finished, result() is emitted. 0164 * 0165 * Warning: Never implement any synchronous workload in this method. This method 0166 * should just trigger the job startup, not do any work itself. It is expected to 0167 * be non-blocking. 0168 * 0169 * This is the method all subclasses need to implement. 0170 * It should setup and trigger the workload of the job. It should not do any 0171 * work itself. This includes all signals and terminating the job, e.g. by 0172 * emitResult(). The workload, which could be another method of the 0173 * subclass, is to be triggered using the event loop, e.g. by code like: 0174 * \code 0175 * void ExampleJob::start() 0176 * { 0177 * QTimer::singleShot(0, this, &ExampleJob::doWork); 0178 * } 0179 * \endcode 0180 */ 0181 Q_SCRIPTABLE virtual void start() = 0; 0182 0183 enum KillVerbosity { 0184 Quietly, 0185 EmitResult, 0186 }; 0187 Q_ENUM(KillVerbosity) 0188 0189 public Q_SLOTS: 0190 /** 0191 * Aborts this job. 0192 * 0193 * This kills and deletes the job. 0194 * 0195 * @param verbosity if equals to EmitResult, Job will emit signal result 0196 * and ask uiserver to close the progress window. 0197 * @p verbosity is set to EmitResult for subjobs. Whether applications 0198 * should call with Quietly or EmitResult depends on whether they rely 0199 * on result being emitted or not. Please notice that if @p verbosity is 0200 * set to Quietly, signal result will NOT be emitted. 0201 * @return true if the operation is supported and succeeded, false otherwise 0202 */ 0203 bool kill(KJob::KillVerbosity verbosity = KJob::Quietly); 0204 0205 /** 0206 * Suspends this job. 0207 * The job should be kept in a state in which it is possible to resume it. 0208 * 0209 * @return true if the operation is supported and succeeded, false otherwise 0210 */ 0211 bool suspend(); 0212 0213 /** 0214 * Resumes this job. 0215 * 0216 * @return true if the operation is supported and succeeded, false otherwise 0217 */ 0218 bool resume(); 0219 0220 protected: 0221 /** 0222 * Aborts this job quietly. 0223 * 0224 * This simply kills the job, no error reporting or job deletion should be involved. 0225 * 0226 * @return true if the operation is supported and succeeded, false otherwise 0227 */ 0228 virtual bool doKill(); 0229 0230 /** 0231 * Suspends this job. 0232 * 0233 * @return true if the operation is supported and succeeded, false otherwise 0234 */ 0235 virtual bool doSuspend(); 0236 0237 /** 0238 * Resumes this job. 0239 * 0240 * @return true if the operation is supported and succeeded, false otherwise 0241 */ 0242 virtual bool doResume(); 0243 0244 /** 0245 * Sets the capabilities for this job. 0246 * 0247 * @param capabilities are the capabilities supported by this job 0248 * @see capabilities() 0249 */ 0250 void setCapabilities(Capabilities capabilities); 0251 0252 public: 0253 /** 0254 * Executes the job synchronously. 0255 * 0256 * This will start a nested QEventLoop internally. Nested event loop can be dangerous and 0257 * can have unintended side effects, you should avoid calling exec() whenever you can and use the 0258 * asynchronous interface of KJob instead. 0259 * 0260 * Should you indeed call this method, you need to make sure that all callers are reentrant, 0261 * so that events delivered by the inner event loop don't cause non-reentrant functions to be 0262 * called, which usually wreaks havoc. 0263 * 0264 * Note that the event loop started by this method does not process user input events, which means 0265 * your user interface will effectively be blocked. Other events like paint or network events are 0266 * still being processed. The advantage of not processing user input events is that the chance of 0267 * accidental reentrance is greatly reduced. Still you should avoid calling this function. 0268 * 0269 * @return true if the job has been executed without error, false otherwise 0270 */ 0271 bool exec(); 0272 0273 enum { 0274 /*** Indicates there is no error */ 0275 NoError = 0, 0276 /*** Indicates the job was killed */ 0277 KilledJobError = 1, 0278 /*** Subclasses should define error codes starting at this value */ 0279 UserDefinedError = 100, 0280 }; 0281 0282 /** 0283 * Returns the error code, if there has been an error. 0284 * 0285 * Only call this method from the slot connected to result(). 0286 * 0287 * @return the error code for this job, 0 if no error. 0288 */ 0289 int error() const; 0290 0291 /** 0292 * Returns the error text if there has been an error. 0293 * 0294 * Only call if error is not 0. 0295 * 0296 * This is usually some extra data associated with the error, 0297 * such as a URL. Use errorString() to get a human-readable, 0298 * translated message. 0299 * 0300 * @return a string to help understand the error 0301 */ 0302 QString errorText() const; 0303 0304 /** 0305 * A human-readable error message. 0306 * 0307 * This provides a translated, human-readable description of the 0308 * error. Only call if error is not 0. 0309 * 0310 * Subclasses should implement this to create a translated 0311 * error message from the error code and error text. 0312 * For example: 0313 * \code 0314 * if (error() == ReadFailed) { 0315 * i18n("Could not read \"%1\"", errorText()); 0316 * } 0317 * \endcode 0318 * 0319 * @return a translated error message, providing error() is 0 0320 */ 0321 virtual QString errorString() const; 0322 0323 /** 0324 * Returns the processed amount of a given unit for this job. 0325 * 0326 * @param unit the unit of the requested amount 0327 * @return the processed size 0328 */ 0329 Q_SCRIPTABLE qulonglong processedAmount(Unit unit) const; 0330 0331 /** 0332 * Returns the total amount of a given unit for this job. 0333 * 0334 * @param unit the unit of the requested amount 0335 * @return the total size 0336 */ 0337 Q_SCRIPTABLE qulonglong totalAmount(Unit unit) const; 0338 0339 /** 0340 * Returns the overall progress of this job. 0341 * 0342 * @return the overall progress of this job 0343 */ 0344 unsigned long percent() const; 0345 0346 /** 0347 * Sets the auto-delete property of the job. If @p autodelete is 0348 * set to @c false the job will not delete itself once it is finished. 0349 * 0350 * The default for any KJob is to automatically delete itself, which 0351 * implies that the job was created on the heap (using <tt>new</tt>). 0352 * If the job is created on the stack (which isn't the typical use-case 0353 * for a job) then you must set auto-delete to @c false, otherwise you 0354 * could get a crash when the job finishes and tries to delete itself. 0355 * 0356 * @note If you set auto-delete to @c false then you need to kill the 0357 * job manually, ideally by calling kill(). 0358 * 0359 * @param autodelete set to @c false to disable automatic deletion 0360 * of the job. 0361 */ 0362 void setAutoDelete(bool autodelete); 0363 0364 /** 0365 * Returns whether this job automatically deletes itself once 0366 * the job is finished. 0367 * 0368 * @return whether the job is deleted automatically after 0369 * finishing. 0370 */ 0371 bool isAutoDelete() const; 0372 0373 /** 0374 * This method can be used to indicate to classes listening to signals from a job 0375 * that they should ideally show a progress bar, but not a finished notification. 0376 * 0377 * For example when opening a remote URL, a job will emit the progress of the 0378 * download, which can be used to show a progress dialog or a Plasma notification, 0379 * then when the job is done it'll emit e.g. the finished signal. Showing the user the 0380 * progress dialog is useful, however the dialog/notification about the download being 0381 * finished isn't of much interest, because the user can see the application that invoked 0382 * the job opening the actual file that was downloaded. 0383 * 0384 * @since 5.92 0385 */ 0386 void setFinishedNotificationHidden(bool hide = true); 0387 0388 /** 0389 * Whether to <em>not</em> show a finished notification when a job's finished 0390 * signal is emitted. 0391 * 0392 * @see setFinishedNotificationHidden() 0393 * 0394 * @since 5.92 0395 */ 0396 bool isFinishedNotificationHidden() const; 0397 0398 /** 0399 * Returns @c true if this job was started with exec(), which starts a nested event-loop 0400 * (with QEventLoop::ExcludeUserInputEvents, which blocks the GUI), otherwise returns 0401 * @c false which indicates this job was started asynchronously with start(). 0402 * 0403 * This is useful for code that for example shows a dialog to ask the user a question, 0404 * and that would be no-op since the user cannot interact with the dialog. 0405 * 0406 * @since 5.95 0407 */ 0408 bool isStartedWithExec() const; 0409 0410 Q_SIGNALS: 0411 /** 0412 * Emitted when the job is finished, in any case. It is used to notify 0413 * observers that the job is terminated and that progress can be hidden. 0414 * 0415 * Since 5.75 this signal is guaranteed to be emitted exactly once. 0416 * 0417 * This is a private signal, it can't be emitted directly by subclasses of 0418 * KJob, use emitResult() instead. 0419 * 0420 * In general, to be notified of a job's completion, client code should connect to result() 0421 * rather than finished(), so that kill(Quietly) is indeed quiet. 0422 * However if you store a list of jobs and they might get killed silently, 0423 * then you must connect to this instead of result(), to avoid dangling pointers in your list. 0424 * 0425 * @param job the job that emitted this signal 0426 * @internal 0427 * 0428 * @see result 0429 */ 0430 void finished(KJob *job 0431 #if !defined(K_DOXYGEN) 0432 , 0433 QPrivateSignal 0434 #endif 0435 ); 0436 0437 /** 0438 * Emitted when the job is suspended. 0439 * 0440 * This is a private signal, it can't be emitted directly by subclasses of 0441 * KJob. 0442 * 0443 * @param job the job that emitted this signal 0444 */ 0445 void suspended(KJob *job 0446 #if !defined(K_DOXYGEN) 0447 , 0448 QPrivateSignal 0449 #endif 0450 ); 0451 0452 /** 0453 * Emitted when the job is resumed. 0454 * 0455 * This is a private signal, it can't be emitted directly by subclasses of 0456 * KJob. 0457 * 0458 * @param job the job that emitted this signal 0459 */ 0460 void resumed(KJob *job 0461 #if !defined(K_DOXYGEN) 0462 , 0463 QPrivateSignal 0464 #endif 0465 ); 0466 0467 /** 0468 * Emitted when the job is finished (except when killed with KJob::Quietly). 0469 * 0470 * Since 5.75 this signal is guaranteed to be emitted at most once. 0471 * 0472 * Use error to know if the job was finished with error. 0473 * 0474 * This is a private signal, it can't be emitted directly by subclasses of 0475 * KJob, use emitResult() instead. 0476 * 0477 * Please connect to this signal instead of finished. 0478 * 0479 * @param job the job that emitted this signal 0480 * 0481 * @see kill 0482 */ 0483 void result(KJob *job 0484 #if !defined(K_DOXYGEN) 0485 , 0486 QPrivateSignal 0487 #endif 0488 ); 0489 0490 /** 0491 * Emitted to display general description of this job. A description has 0492 * a title and two optional fields which can be used to complete the 0493 * description. 0494 * 0495 * Examples of titles are "Copying", "Creating resource", etc. 0496 * The fields of the description can be "Source" with an URL, and, 0497 * "Destination" with an URL for a "Copying" description. 0498 * @param job the job that emitted this signal 0499 * @param title the general description of the job 0500 * @param field1 first field (localized name and value) 0501 * @param field2 second field (localized name and value) 0502 */ 0503 void description(KJob *job, 0504 const QString &title, 0505 const QPair<QString, QString> &field1 = QPair<QString, QString>(), 0506 const QPair<QString, QString> &field2 = QPair<QString, QString>()); 0507 /** 0508 * Emitted to display state information about this job. 0509 * Examples of message are "Resolving host", "Connecting to host...", etc. 0510 * 0511 * @param job the job that emitted this signal 0512 * @param message the info message 0513 */ 0514 void infoMessage(KJob *job, const QString &message); 0515 0516 /** 0517 * Emitted to display a warning about this job. 0518 * 0519 * @param job the job that emitted this signal 0520 * @param message the warning message 0521 */ 0522 void warning(KJob *job, const QString &message); 0523 0524 Q_SIGNALS: 0525 // These signals must be connected from KIO::KCoreDirLister (among others), 0526 // therefore they must be public. 0527 /** 0528 * Emitted when we know the amount the job will have to process. The unit of this 0529 * amount is sent too. It can be emitted several times if the job manages several 0530 * different units. 0531 * 0532 * @note This is a private signal, it shouldn't be emitted directly by subclasses of 0533 * KJob, use setTotalAmount() instead. 0534 * 0535 * @param job the job that emitted this signal 0536 * @param unit the unit of the total amount 0537 * @param amount the total amount 0538 * 0539 * @since 5.80 0540 */ 0541 void totalAmountChanged(KJob *job, 0542 KJob::Unit unit, 0543 qulonglong amount 0544 #if !defined(K_DOXYGEN) 0545 , 0546 QPrivateSignal 0547 #endif 0548 ); 0549 0550 /** 0551 * Regularly emitted to show the progress of this job by giving the current amount. 0552 * The unit of this amount is sent too. It can be emitted several times if the job 0553 * manages several different units. 0554 * 0555 * @note This is a private signal, it shouldn't be emitted directly by subclasses of 0556 * KJob, use setProcessedAmount() instead. 0557 * 0558 * @param job the job that emitted this signal 0559 * @param unit the unit of the processed amount 0560 * @param amount the processed amount 0561 * 0562 * @since 5.80 0563 */ 0564 void processedAmountChanged(KJob *job, 0565 KJob::Unit unit, 0566 qulonglong amount 0567 #if !defined(K_DOXYGEN) 0568 , 0569 QPrivateSignal 0570 #endif 0571 ); 0572 0573 /** 0574 * Emitted when we know the size of this job (data size in bytes for transfers, 0575 * number of entries for listings, etc). 0576 * 0577 * @note This is a private signal, it shouldn't be emitted directly by subclasses of 0578 * KJob, use setTotalAmount() instead. 0579 * 0580 * @param job the job that emitted this signal 0581 * @param size the total size 0582 */ 0583 void totalSize(KJob *job, qulonglong size); 0584 0585 /** 0586 * Regularly emitted to show the progress of this job 0587 * (current data size in bytes for transfers, entries listed, etc.). 0588 * 0589 * @note This is a private signal, it shouldn't be emitted directly by subclasses of 0590 * KJob, use setProcessedAmount() instead. 0591 * 0592 * @param job the job that emitted this signal 0593 * @param size the processed size 0594 */ 0595 void processedSize(KJob *job, qulonglong size); 0596 0597 /** 0598 * Progress signal showing the overall progress of the job. This is 0599 * valid for any kind of job, and allows using a progress bar very 0600 * easily. (see KProgressBar). 0601 * 0602 * Note that this signal is not emitted for finished jobs. 0603 * 0604 * @note This is a private signal, it shouldn't be emitted directly 0605 * by subclasses of KJob, use emitPercent(), setPercent() setTotalAmount() 0606 * or setProcessedAmount() instead. 0607 * 0608 * @param job the job that emitted this signal 0609 * @param percent the percentage 0610 * 0611 * @since 5.80 0612 */ 0613 void percentChanged(KJob *job, 0614 unsigned long percent 0615 #if !defined(K_DOXYGEN) 0616 , 0617 QPrivateSignal 0618 #endif 0619 ); 0620 0621 /** 0622 * Emitted to display information about the speed of this job. 0623 * 0624 * @note This is a private signal, it shouldn't be emitted directly by subclasses of 0625 * KJob, use emitSpeed() instead. 0626 * 0627 * @param job the job that emitted this signal 0628 * @param speed the speed in bytes/s 0629 */ 0630 void speed(KJob *job, unsigned long speed); 0631 0632 protected: 0633 /** 0634 * Returns if the job has been finished and has emitted the finished() signal. 0635 * 0636 * @return if the job has been finished 0637 * @see finished() 0638 * @since 5.75 0639 */ 0640 // KF6 TODO: make public. Useful at least for unittests that run multiple jobs in parallel. 0641 bool isFinished() const; 0642 0643 /** 0644 * Sets the error code. 0645 * 0646 * It should be called when an error 0647 * is encountered in the job, just before calling emitResult(). 0648 * 0649 * You should define an (anonymous) enum of error codes, 0650 * with values starting at KJob::UserDefinedError, and use 0651 * those. For example, 0652 * @code 0653 * enum { 0654 * InvalidFoo = UserDefinedError, 0655 * BarNotFound, 0656 * }; 0657 * @endcode 0658 * 0659 * @param errorCode the error code 0660 * @see emitResult() 0661 */ 0662 void setError(int errorCode); 0663 0664 /** 0665 * Sets the error text. 0666 * 0667 * It should be called when an error 0668 * is encountered in the job, just before calling emitResult(). 0669 * 0670 * Provides extra information about the error that cannot be 0671 * determined directly from the error code. For example, a 0672 * URL or filename. This string is not normally translatable. 0673 * 0674 * @param errorText the error text 0675 * @see emitResult(), errorString(), setError() 0676 */ 0677 void setErrorText(const QString &errorText); 0678 0679 /** 0680 * Sets the processed size. The processedAmount() and percent() signals 0681 * are emitted if the values changed. The percent() signal is emitted 0682 * only for the progress unit. 0683 * 0684 * @param unit the unit of the new processed amount 0685 * @param amount the new processed amount 0686 */ 0687 void setProcessedAmount(Unit unit, qulonglong amount); 0688 0689 /** 0690 * Sets the total size. The totalSize() and percent() signals 0691 * are emitted if the values changed. The percent() signal is emitted 0692 * only for the progress unit. 0693 * 0694 * @param unit the unit of the new total amount 0695 * @param amount the new total amount 0696 */ 0697 void setTotalAmount(Unit unit, qulonglong amount); 0698 0699 /** 0700 * Sets the unit that will be used internally to calculate 0701 * the progress percentage. 0702 * The default progress unit is Bytes. 0703 * @since 5.76 0704 */ 0705 void setProgressUnit(Unit unit); 0706 0707 /** 0708 * Sets the overall progress of the job. The percent() signal 0709 * is emitted if the value changed. 0710 * 0711 * The job takes care of this if you call setProcessedAmount 0712 * in Bytes (or the unit set by setProgressUnit). 0713 * This method allows you to set your own progress, as an alternative. 0714 * 0715 * @param percentage the new overall progress 0716 */ 0717 void setPercent(unsigned long percentage); 0718 0719 /** 0720 * Utility function to emit the result signal, and end this job. 0721 * It first notifies the observers to hide the progress for this job using 0722 * the finished() signal. 0723 * 0724 * @note Deletes this job using deleteLater(). 0725 * 0726 * @see result() 0727 * @see finished() 0728 */ 0729 void emitResult(); 0730 0731 /** 0732 * Utility function for inherited jobs. 0733 * Emits the percent signal if bigger than previous value, 0734 * after calculating it from the parameters. 0735 * 0736 * @param processedAmount the processed amount 0737 * @param totalAmount the total amount 0738 * @see percent() 0739 */ 0740 void emitPercent(qulonglong processedAmount, qulonglong totalAmount); 0741 0742 /** 0743 * Utility function for inherited jobs. 0744 * Emits the speed signal and starts the timer for removing that info 0745 * 0746 * @param speed the speed in bytes/s 0747 */ 0748 void emitSpeed(unsigned long speed); 0749 0750 protected: 0751 std::unique_ptr<KJobPrivate> const d_ptr; 0752 0753 KCOREADDONS_NO_EXPORT KJob(KJobPrivate &dd, QObject *parent); 0754 0755 private: 0756 KCOREADDONS_NO_EXPORT void finishJob(bool emitResult); 0757 0758 Q_DECLARE_PRIVATE(KJob) 0759 }; 0760 0761 Q_DECLARE_OPERATORS_FOR_FLAGS(KJob::Capabilities) 0762 0763 #endif