Akonadi Calendar

standardcalendaractionmanager.cpp
1/*
2 * SPDX-FileCopyrightText: 2010 Casey Link <unnamedrambler@gmail.com>
3 * SPDX-FileCopyrightText: 2010 Klaralvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
4 * SPDX-FileCopyrightText: 2009-2010 Tobias Koenig <tokoe@kde.org>
5 *
6 * SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8
9#include "standardcalendaractionmanager.h"
10
11#include <Akonadi/EntityTreeModel>
12
13#include <KActionCollection>
14#include <KCalendarCore/Event>
15#include <KCalendarCore/Journal>
16#include <KLocalizedString>
17#include <QAction>
18
19#include <QItemSelectionModel>
20
21using namespace Akonadi;
22
23class Akonadi::StandardCalendarActionManagerPrivate
24{
25public:
26 StandardCalendarActionManagerPrivate(KActionCollection *actionCollection, QWidget *parentWidget, StandardCalendarActionManager *parent)
27 : mActionCollection(actionCollection)
28 , mParentWidget(parentWidget)
29 , mParent(parent)
30 {
31 mGenericManager = new StandardActionManager(actionCollection, parentWidget);
33 mGenericManager->setMimeTypeFilter(QStringList() << QStringLiteral("text/calendar"));
34 mGenericManager->setCapabilityFilter(QStringList() << QStringLiteral("Resource"));
35 }
36
37 ~StandardCalendarActionManagerPrivate()
38 {
39 delete mGenericManager;
40 }
41
42 void updateGenericAction(StandardActionManager::Type type)
43 {
44 switch (type) {
46 mGenericManager->action(Akonadi::StandardActionManager::CreateCollection)->setText(i18n("Add Folder…"));
48 ->setWhatsThis(i18n("Add a new calendar folder to the currently selected calendar folder."));
50
53 ki18n("Could not create calendar folder: %1"));
54
57 i18nc("@title:window", "Calendar Folder Creation Failed"));
59 ->setProperty("ContentMimeTypes",
60 QStringList() << QStringLiteral("inode/directory") << QStringLiteral("application/x-vnd.akonadi.calendar.todo")
61 << QStringLiteral("application/x-vnd.akonadi.calendar.event")
62 << QStringLiteral("application/x-vnd.akonadi.calendar.journal"));
63
64 break;
66 mGenericManager->setActionText(Akonadi::StandardActionManager::CopyCollections, ki18np("Copy Folder", "Copy %1 Folders"));
68 ->setWhatsThis(i18n("Copy the selected calendar folders to the clipboard."));
69 break;
71 mGenericManager->setActionText(Akonadi::StandardActionManager::DeleteCollections, ki18np("Delete Folder", "Delete %1 Folders"));
73 ->setWhatsThis(i18n("Delete the selected calendar folders from the calendar."));
76 ki18np("Do you really want to delete this calendar folder and all its sub-folders?",
77 "Do you really want to delete %1 calendar folders and all their sub-folders?"));
78
81 ki18ncp("@title:window", "Delete Calendar Folder?", "Delete Calendar Folders?"));
82
85 ki18n("Could not delete calendar folder: %1"));
86
89 i18nc("@title:window", "Calendar Folder Deletion Failed"));
90
91 break;
93 mGenericManager->setActionText(Akonadi::StandardActionManager::SynchronizeCollections, ki18np("Update Folder", "Update %1 Folders"));
95 ->setWhatsThis(i18n("Update the content of the selected calendar folders."));
96
97 break;
99 mGenericManager->setActionText(Akonadi::StandardActionManager::CutCollections, ki18np("Cut Folder", "Cut %1 Folders"));
100 mGenericManager->action(Akonadi::StandardActionManager::CutCollections)->setWhatsThis(i18n("Cut the selected calendar folders from the calendar."));
101 break;
103 mGenericManager->action(Akonadi::StandardActionManager::CollectionProperties)->setText(i18n("Folder Properties…"));
105 ->setWhatsThis(i18n("Open a dialog to edit the properties of the selected calendar folder."));
108 ki18nc("@title:window", "Properties of Calendar Folder %1"));
109 break;
111 mGenericManager->setActionText(Akonadi::StandardActionManager::CopyItems, ki18np("Copy Event", "Copy %1 Events"));
112 mGenericManager->action(Akonadi::StandardActionManager::CopyItems)->setWhatsThis(i18n("Copy the selected events to the clipboard."));
113
114 break;
116 mGenericManager->setActionText(Akonadi::StandardActionManager::DeleteItems, ki18np("Delete Event", "Delete %1 Events"));
117 mGenericManager->action(Akonadi::StandardActionManager::DeleteItems)->setWhatsThis(i18n("Delete the selected events from the calendar."));
120 ki18np("Do you really want to delete the selected event?", "Do you really want to delete %1 events?"));
121
124 ki18ncp("@title:window", "Delete Event?", "Delete Events?"));
125
127
130 i18nc("@title:window", "Event Deletion Failed"));
131 break;
132
134 mGenericManager->setActionText(Akonadi::StandardActionManager::CutItems, ki18np("Cut Event", "Cut %1 Events"));
135 mGenericManager->action(Akonadi::StandardActionManager::CutItems)->setWhatsThis(i18n("Cut the selected events from the calendar."));
136 break;
138 mGenericManager->action(Akonadi::StandardActionManager::CreateResource)->setText(i18n("Add &Calendar…"));
140 ->setWhatsThis(i18n("Add a new calendar<p>"
141 "You will be presented with a dialog where you can select "
142 "the type of the calendar that shall be added.</p>"));
143 mGenericManager->setContextText(StandardActionManager::CreateResource, StandardActionManager::DialogTitle, i18nc("@title:window", "Add Calendar"));
144
147 ki18n("Could not create calendar: %1"));
148
151 i18nc("@title:window", "Calendar Creation Failed"));
152
153 break;
155
156 mGenericManager->setActionText(Akonadi::StandardActionManager::DeleteResources, ki18np("&Delete Calendar", "&Delete %1 Calendars"));
158 ->setWhatsThis(i18n("Delete the selected calendars<p>"
159 "The currently selected calendars will be deleted, "
160 "along with all the events, todos and journals they contain.</p>"));
163 ki18np("Do you really want to delete this calendar?", "Do you really want to delete %1 calendars?"));
164
167 ki18ncp("@title:window", "Delete Calendar?", "Delete Calendars?"));
168
169 break;
171 mGenericManager->action(Akonadi::StandardActionManager::ResourceProperties)->setText(i18n("Calendar Properties…"));
173 ->setWhatsThis(i18n("Open a dialog to edit properties of the selected calendar."));
174 break;
176
177 mGenericManager->setActionText(Akonadi::StandardActionManager::SynchronizeResources, ki18np("Update Calendar", "Update %1 Calendars"));
179 ->setWhatsThis(i18n("Updates the content of all folders of the selected calendars."));
180 break;
182 mGenericManager->action(Akonadi::StandardActionManager::CopyItemToMenu)->setText(i18n("&Copy to Calendar"));
183 mGenericManager->action(Akonadi::StandardActionManager::CopyItemToMenu)->setWhatsThis(i18n("Copy the selected event to a different calendar."));
184 break;
186 mGenericManager->action(Akonadi::StandardActionManager::MoveItemToMenu)->setText(i18n("&Move to Calendar"));
187 mGenericManager->action(Akonadi::StandardActionManager::MoveItemToMenu)->setWhatsThis(i18n("Move the selected event to a different calendar."));
188 break;
190 mGenericManager->setContextText(StandardActionManager::Paste, StandardActionManager::ErrorMessageText, ki18n("Could not paste event: %1"));
191
192 mGenericManager->setContextText(StandardActionManager::Paste, StandardActionManager::ErrorMessageTitle, i18nc("@title:window", "Paste Failed"));
193 break;
195 mGenericManager->action(Akonadi::StandardActionManager::SynchronizeCollectionTree)->setText(i18n("Update Available Calendars"));
196 mGenericManager->action(Akonadi::StandardActionManager::SynchronizeCollectionTree)->setWhatsThis(i18n("Updates the tree of available calendars."));
197
198 break;
199 default:
200 break;
201 }
202 }
203
204 void updateGenericAllActions()
205 {
206 updateGenericAction(StandardActionManager::CreateCollection);
207 updateGenericAction(StandardActionManager::CopyCollections);
208 updateGenericAction(StandardActionManager::DeleteCollections);
211 updateGenericAction(StandardActionManager::CopyItems);
212 updateGenericAction(StandardActionManager::Paste);
213 updateGenericAction(StandardActionManager::DeleteItems);
219 updateGenericAction(StandardActionManager::CopyItemToMenu);
220 updateGenericAction(StandardActionManager::MoveItemToMenu);
222 updateGenericAction(StandardActionManager::CutItems);
223 updateGenericAction(StandardActionManager::CutCollections);
224 updateGenericAction(StandardActionManager::CreateResource);
225 updateGenericAction(StandardActionManager::DeleteResources);
228 updateGenericAction(StandardActionManager::ToggleWorkOffline);
231 updateGenericAction(StandardActionManager::CopyItemToDialog);
232 updateGenericAction(StandardActionManager::MoveItemToDialog);
235 updateGenericAction(StandardActionManager::MoveItemsToTrash);
244 }
245
246 static bool hasWritableCollection(const QModelIndex &index, const QString &mimeType)
247 {
249 if (collection.isValid()) {
250 if (collection.contentMimeTypes().contains(mimeType) && (collection.rights() & Akonadi::Collection::CanCreateItem)) {
251 return true;
252 }
253 }
254
255 const QAbstractItemModel *model = index.model();
256 if (!model) {
257 return false;
258 }
259
260 for (int row = 0; row < model->rowCount(index); ++row) {
261 if (hasWritableCollection(model->index(row, 0, index), mimeType)) {
262 return true;
263 }
264 }
265
266 return false;
267 }
268
269 [[nodiscard]] bool hasWritableCollection(const QString &mimeType) const
270 {
271 if (!mCollectionSelectionModel) {
272 return false;
273 }
274
275 const QAbstractItemModel *collectionModel = mCollectionSelectionModel->model();
276 for (int row = 0; row < collectionModel->rowCount(); ++row) {
277 if (hasWritableCollection(collectionModel->index(row, 0, QModelIndex()), mimeType)) {
278 return true;
279 }
280 }
281
282 return false;
283 }
284
285 void updateActions()
286 {
287 if (!mItemSelectionModel) {
288 return;
289 }
290
291 // update action labels
292 const int itemCount = mItemSelectionModel->selectedRows().count();
293 if (itemCount == 1) {
294 const QModelIndex index = mItemSelectionModel->selectedRows().at(0);
295 if (index.isValid()) {
297 if (mimeType == KCalendarCore::Event::eventMimeType()) {
299 mGenericManager->setActionText(Akonadi::StandardActionManager::CopyItems, ki18np("Copy Event", "Copy %1 Events"));
300 }
302 if (act) {
303 act->setText(i18n("Copy Event To"));
304 }
306 if (act) {
307 act->setText(i18n("Copy Event To"));
308 }
310 mGenericManager->setActionText(Akonadi::StandardActionManager::DeleteItems, ki18np("Delete Event", "Delete %1 Events"));
311 }
312 if (mGenericManager->action(Akonadi::StandardActionManager::CutItems)) {
313 mGenericManager->setActionText(Akonadi::StandardActionManager::CutItems, ki18np("Cut Event", "Cut %1 Events"));
314 }
316 if (act) {
317 act->setText(i18n("Move Event To"));
318 }
320 if (act) {
321 act->setText(i18n("Move Event To"));
322 }
324 if (act) {
325 act->setText(i18n("Edit Event…"));
326 }
327 } else if (mimeType == KCalendarCore::Todo::todoMimeType()) {
329 mGenericManager->setActionText(Akonadi::StandardActionManager::CopyItems, ki18np("Copy To-do", "Copy %1 To-dos"));
330 }
332 if (act) {
333 act->setText(i18n("Copy To-do To"));
334 }
336 if (act) {
337 act->setText(i18n("Copy To-do To"));
338 }
340 mGenericManager->setActionText(Akonadi::StandardActionManager::DeleteItems, ki18np("Delete To-do", "Delete %1 To-dos"));
341 }
342 if (mGenericManager->action(Akonadi::StandardActionManager::CutItems)) {
343 mGenericManager->setActionText(Akonadi::StandardActionManager::CutItems, ki18np("Cut To-do", "Cut %1 To-dos"));
344 }
346 if (act) {
347 act->setText(i18n("Move To-do To"));
348 }
350 if (act) {
351 act->setText(i18n("Move To-do To"));
352 }
354 if (act) {
355 act->setText(i18n("Edit To-do…"));
356 }
357 } else if (mimeType == KCalendarCore::Journal::journalMimeType()) {
359 mGenericManager->setActionText(Akonadi::StandardActionManager::CopyItems, ki18np("Copy Journal", "Copy %1 Journals"));
360 }
362 if (act) {
363 act->setText(i18n("Copy Journal To"));
364 }
366 if (act) {
367 act->setText(i18n("Copy Journal To"));
368 }
370 mGenericManager->setActionText(Akonadi::StandardActionManager::DeleteItems, ki18np("Delete Journal", "Delete %1 Journals"));
371 }
372 if (mGenericManager->action(Akonadi::StandardActionManager::CutItems)) {
373 mGenericManager->setActionText(Akonadi::StandardActionManager::CutItems, ki18np("Cut Journal", "Cut %1 Journals"));
374 }
376 if (act) {
377 act->setText(i18n("Move Journal To"));
378 }
380 if (act) {
381 act->setText(i18n("Move Journal To"));
382 }
384 if (act) {
385 act->setText(i18n("Edit Journal…"));
386 }
387 }
388 }
389 }
390
391 // update action states
393 if (act) {
394 act->setEnabled(hasWritableCollection(KCalendarCore::Event::eventMimeType()));
395 }
397 if (act) {
398 act->setEnabled(hasWritableCollection(KCalendarCore::Todo::todoMimeType()));
399 }
401 if (act) {
402 act->setEnabled(hasWritableCollection(KCalendarCore::Journal::journalMimeType()));
403 }
404
406 if (act) {
407 bool canEditItem = true;
408
409 // only one selected item can be edited
410 canEditItem = canEditItem && (itemCount == 1);
411
412 // check whether parent collection allows changing the item
413 const QModelIndexList rows = mItemSelectionModel->selectedRows();
414 if (rows.count() == 1) {
415 const QModelIndex index = rows.first();
416 const auto parentCollection = index.data(EntityTreeModel::ParentCollectionRole).value<Collection>();
417 if (parentCollection.isValid()) {
418 canEditItem = canEditItem && (parentCollection.rights() & Collection::CanChangeItem);
419 }
420 }
421
422 act->setEnabled(canEditItem);
423 }
424
426 if (act) {
427 act->setEnabled(false);
428 }
429
430 if (itemCount == 1) {
431 const Akonadi::Item item = mGenericManager->selectedItems().at(0);
432 if (item.isValid() && item.hasPayload<KCalendarCore::Todo::Ptr>()) {
433 if (QAction *actSubTodo = mActions.value(StandardCalendarActionManager::CreateSubTodo)) {
434 actSubTodo->setEnabled(hasWritableCollection(KCalendarCore::Todo::todoMimeType()));
435 }
436 }
437 }
438
439 Q_EMIT mParent->actionStateUpdated();
440 }
441
442 void slotCreateEvent()
443 {
444 // dummy as long as there are no editors available in kdepimlibs/
445 }
446
447 void slotCreateTodo()
448 {
449 // dummy as long as there are no editors available in kdepimlibs/
450 }
451
452 void slotCreateSubTodo()
453 {
454 // dummy as long as there are no editors available in kdepimlibs/
455 }
456
457 void slotCreateJournal()
458 {
459 // dummy as long as there are no editors available in kdepimlibs/
460 }
461
462 void slotEditIncidence()
463 {
464 // dummy as long as there are no editors available in kdepimlibs/
465 }
466
467 KActionCollection *mActionCollection = nullptr;
468 QWidget *mParentWidget = nullptr;
469 StandardActionManager *mGenericManager = nullptr;
470 QItemSelectionModel *mCollectionSelectionModel = nullptr;
471 QItemSelectionModel *mItemSelectionModel = nullptr;
474 StandardCalendarActionManager *const mParent;
475};
476
478 : QObject(parent)
479 , d(new StandardCalendarActionManagerPrivate(actionCollection, parent, this))
480{
481}
482
484
486{
487 d->mCollectionSelectionModel = selectionModel;
488 d->mGenericManager->setCollectionSelectionModel(selectionModel);
489
490 connect(selectionModel->model(), &QAbstractItemModel::rowsInserted, this, [this]() {
491 d->updateActions();
492 });
493 connect(selectionModel->model(), &QAbstractItemModel::rowsRemoved, this, [this]() {
494 d->updateActions();
495 });
496 connect(selectionModel, &QItemSelectionModel::selectionChanged, this, [this]() {
497 d->updateActions();
498 });
499 d->updateActions();
500}
501
503{
504 d->mItemSelectionModel = selectionModel;
505 d->mGenericManager->setItemSelectionModel(selectionModel);
506
507 connect(selectionModel, &QItemSelectionModel::selectionChanged, this, [this]() {
508 d->updateActions();
509 });
510
511 d->updateActions();
512}
513
515{
516 QAction *action = d->mActions.value(type);
517 if (action) {
518 return action;
519 }
520
521 switch (type) {
522 case CreateEvent:
523 action = new QAction(d->mParentWidget);
524 action->setIcon(QIcon::fromTheme(QStringLiteral("appointment-new")));
525 action->setText(i18n("New E&vent…"));
526 action->setWhatsThis(i18n("Create a new event"));
527 d->mActions.insert(CreateEvent, action);
528 d->mActionCollection->addAction(QStringLiteral("akonadi_event_create"), action);
529 connect(action, &QAction::triggered, this, [this]() {
530 d->slotCreateEvent();
531 });
532 break;
533 case CreateTodo:
534 action = new QAction(d->mParentWidget);
535 action->setIcon(QIcon::fromTheme(QStringLiteral("task-new")));
536 action->setText(i18n("New &To-do…"));
537 action->setWhatsThis(i18n("Create a new To-do"));
538 d->mActions.insert(CreateTodo, action);
539 d->mActionCollection->addAction(QStringLiteral("akonadi_todo_create"), action);
540 connect(action, &QAction::triggered, this, [this]() {
541 d->slotCreateTodo();
542 });
543 break;
544 case CreateSubTodo:
545 action = new QAction(d->mParentWidget);
546 action->setText(i18n("New Su&b-to-do…"));
547 action->setWhatsThis(i18n("Create a new Sub-to-do"));
548 d->mActions.insert(CreateSubTodo, action);
549 d->mActionCollection->addAction(QStringLiteral("akonadi_subtodo_create"), action);
550 connect(action, &QAction::triggered, this, [this]() {
551 d->slotCreateSubTodo();
552 });
553 break;
554 case CreateJournal:
555 action = new QAction(d->mParentWidget);
556 action->setIcon(QIcon::fromTheme(QStringLiteral("journal-new")));
557 action->setText(i18n("New &Journal…"));
558 action->setWhatsThis(i18n("Create a new Journal"));
559 d->mActions.insert(CreateJournal, action);
560 d->mActionCollection->addAction(QStringLiteral("akonadi_journal_create"), action);
561 connect(action, &QAction::triggered, this, [this]() {
562 d->slotCreateJournal();
563 });
564 break;
565 case EditIncidence:
566 action = new QAction(d->mParentWidget);
567 action->setText(i18n("&Edit…"));
568 action->setWhatsThis(i18n("Edit the selected incidence."));
569 d->mActions.insert(EditIncidence, action);
570 d->mActionCollection->addAction(QStringLiteral("akonadi_incidence_edit"), action);
571 connect(action, &QAction::triggered, this, [this]() {
572 d->slotEditIncidence();
573 });
574 break;
575 default:
576 Q_ASSERT(false); // should never happen
577 break;
578 }
579
580 return action;
581}
582
584{
585 QAction *act = d->mGenericManager->action(type);
586 if (!act) {
587 act = d->mGenericManager->createAction(type);
588 }
589 d->updateGenericAction(type);
590 return act;
591}
592
594{
600
601 d->mGenericManager->createAllActions();
602 d->updateGenericAllActions();
603 d->updateActions();
604}
605
607{
608 return d->mActions.value(type);
609}
610
612{
613 return d->mGenericManager->action(type);
614}
615
617{
618 d->mGenericManager->setActionText(type, text);
619}
620
622{
623 if (intercept) {
624 d->mInterceptedActions.insert(type);
625 } else {
626 d->mInterceptedActions.remove(type);
627 }
628}
629
631{
632 d->mGenericManager->interceptAction(type, intercept);
633}
634
636{
637 return d->mGenericManager->selectedCollections();
638}
639
641{
642 return d->mGenericManager->selectedItems();
643}
644
646{
647 d->mGenericManager->setContextText(type, context, text);
648}
649
651{
652 d->mGenericManager->setContextText(type, context, text);
653}
654
655void StandardCalendarActionManager::setCollectionPropertiesPageNames(const QStringList &names)
656{
657 d->mGenericManager->setCollectionPropertiesPageNames(names);
658}
659
660#include "moc_standardcalendaractionmanager.cpp"
bool isValid() const
bool hasPayload() const
void setActionText(Type type, const KLocalizedString &text)
Akonadi::Item::List selectedItems() const
void setCapabilityFilter(const QStringList &capabilities)
void setMimeTypeFilter(const QStringList &mimeTypes)
void setContextText(Type type, TextContext context, const KLocalizedString &text)
QAction * action(Type type) const
Manages calendar specific actions for collection and item views.
void createAllActions()
Convenience method to create all standard actions.
void actionStateUpdated()
This signal is emitted whenever the action state has been updated.
void interceptAction(Type type, bool intercept=true)
Sets whether the default implementation for the given action type shall be executed when the action i...
void setContextText(StandardActionManager::Type type, StandardActionManager::TextContext context, const QString &text)
Sets the text of the action type for the given context.
StandardCalendarActionManager(KActionCollection *actionCollection, QWidget *parent=nullptr)
Creates a new standard calendar action manager.
Akonadi::Collection::List selectedCollections() const
Returns the list of collections that are currently selected.
void setActionText(StandardActionManager::Type type, const KLocalizedString &text)
Sets the label of the action type to text, which is used during updating the action state and substit...
Akonadi::Item::List selectedItems() const
Returns the list of items that are currently selected.
QAction * action(Type type) const
Returns the action of the given type, 0 if it has not been created (yet).
~StandardCalendarActionManager() override
Destroys the standard calendar action manager.
QAction * createAction(Type type)
Creates the action of the given type and adds it to the action collection specified in the constructo...
void setCollectionSelectionModel(QItemSelectionModel *selectionModel)
Sets the collection selection model based on which the collection related actions should operate.
@ EditIncidence
Edit currently selected event/todo/journal.
void setItemSelectionModel(QItemSelectionModel *selectionModel)
Sets the item selection model based on which the item related actions should operate.
static QLatin1String eventMimeType()
static QLatin1String journalMimeType()
static QLatin1String todoMimeType()
KLocalizedString KI18N_EXPORT ki18np(const char *singular, const char *plural)
KLocalizedString KI18N_EXPORT ki18n(const char *text)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
KLocalizedString KI18N_EXPORT ki18ncp(const char *context, const char *singular, const char *plural)
KLocalizedString KI18N_EXPORT ki18nc(const char *context, const char *text)
QString i18n(const char *text, const TYPE &arg...)
FreeBusyManager::Singleton.
KCALUTILS_EXPORT QString mimeType()
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const=0
virtual int rowCount(const QModelIndex &parent) const const=0
void rowsInserted(const QModelIndex &parent, int first, int last)
void rowsRemoved(const QModelIndex &parent, int first, int last)
void setEnabled(bool)
void setIcon(const QIcon &icon)
void setText(const QString &text)
void triggered(bool checked)
void setWhatsThis(const QString &what)
T value(const Key &key) const const
QIcon fromTheme(const QString &name)
QAbstractItemModel * model()
QModelIndexList selectedRows(int column) const const
void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
const_reference at(qsizetype i) const const
QVariant data(int role) const const
bool isValid() const const
const QAbstractItemModel * model() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool setProperty(const char *name, QVariant &&value)
QString toString() const const
T value() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Nov 1 2024 18:47:32 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.