KCoreAddons

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

KDE's Doxygen guidelines are available online.