KMail design principles
=======================

This file is intended to guide the reader's way through the KMail
codebase. It should esp. be handy for people not hacking full-time on
KMail as well as people that want to trace bugs in parts of KMail
which they don't know well.

Contents:
- Identity
- Filters
- ConfigureDialog

TODO: reader, composer, messages, folder, accounts, ...

IDENTITY
========

Files: identity*, kmidentity.{h,cpp}, configuredialog.cpp,
       signatureconfigurator.{h,cpp}

Contact Marc Mutz <mutz@kde.org> on questions...

Identities consists of various fields represented by
QStrings. Currently, those fields are hardcoded, but feel free to
implement KMIdentity as a map from strings to QVariants or somesuch.

One part of identities are signatures. They can represent four modes
(Signature::Type) of operation (disabled, text from file or command
and inline text), which correspond to the combo box in the
identitydialog.

KMIdentities are designed to be used through the IdentityManager:
const KMIdentity & ident =
	 kernel->identityManager()->identityForUoidOrDefault(...)
Make sure you assign to a _const_ reference, since the identityForFoo
methods are overloaded with non-const methods that access a different
list of identities in the manager that is used while configuring. That
is known source of errors when you use identityForFoo() as a parameter
to a method taking const KMIdentity &.

WARNING: Don't hold the reference longer than the current functions
scope or next return to the event loop. That's b/c the config dialog
is not modal and the user may hit apply/ok anytime between calls to
function that want to use the identity reference. Store the UOID
instead if you need to keep track of the identity. You may also want
to connect to one of the IdentityManager::changed() or :;deleted()
signals, if you want to do special processing in case the identity
changes.

Thus, in the ConfigureDialog, you will see non-const KMIdentity
references being used, while everywhere else (KMMessage,
IdentityCombo) const references are used.

The IdentityCombo is what you see in the composer. It's a
self-updating combo box of KMIdentity's. Use this if you want the user
to choose an identity, e.g. in the folder dialog.

Ihe IdentityListView is what you see in the config dialog's identity
management page. It's not meant to be used elsewhere, but is DnD
enabled (well, at the time of this writing, only drag-enabled). This
is going to be used to dnd identities around between KNode and KMail,
e.g.

The SignatureConfigurator is the third tab in the identity
dialog. It's separate since it is used by the identity manager to
request a new file/command if the current value somehow fails.



FILTER
=======

Contact Marc Mutz <mutz@kde.org> on questions...

Filters consist of a search pattern and a list of actions plus a few
flags to indicate when they are to be applied (kmfilter.h).
  They are managed in a QList<KMFilter>, called KMFilterMgr. This
filter magnager is responsible for loading and storing filters
(read/writeConfig) and for executing them (process). The unique
instance of the filter manager is held by the kernel
(KMKernel::filterMgr()).

The search pattern is a QList of search rules (kmsearchpattern.h) and a
boolean operator that defines their relation (and/or).

A search rule consists of a field-QString, a "function"-enum and a
"contents" or "value" QString. The first gives the header (or
pseudoheader) to match against, the second says how to match (equals,
consists, is less than,...) and the third holds the pattern to match
against.
  Currently, there are two types of search rules, whcih are mixed
together into a single class: String-valued and int-valued. The latter
is a hack to enable <size> and <age in days> pseudo-header matching.
  KMSearchRules should better be organized like KMFilterActions are.

A filter action (kmfilteraction.h) inherits from KMFilterAction or one
of it's convenience sub-classes. They have three sub-interfaces: (1)
argument handling, (2) processing and (3) parameter widget handling.
  Interface (1) consists of args{From,As}String(), name() and
isEmpty() and is used to read and write the arguments (if any) from/to
the config.
  Interface (2) is used by the filter manager to execute the action
(process() / ReturnCode).
  Interface (3) is used by the filter dialog to allow editing of
actions and consists of name(), label() and the
*ParamWidget*(). Complex parameter widgets are collected in
kmfawidget.{h,cpp}.

A typical call for applying filters is 

KMKernel::filterMgr()
foreach message {
  KMFilterMgr::process():
}


CONFIGURE DIALOG
================

Files: configuredialog*.{h,cpp} ( identitylistview.{h,cpp} )

Contact Marc Mutz <mutz@kde.org> on questions...

The configuredialog is made up of pages that in turn may consist of a
number of tabs. The genral rule of thumb is that each page and tab is
responsible for reading and writing the config options presented on
it, although in the future, that may be reduced to interacting with
the corresponding config manager instead. But that won't change the
basic principle.

Thus, there is an abstract base class ConfigurePage (defined in
configuredialog_p.h), which derives from QWidget. It has four methods
of which you have to reimplement at least the first two:

- void setup()
  Re-read the config (from the config file or the manager) and update
  the widgets correspondingly. That is, you should only create the
  widgets in the ctor, not set the options yet. The reason for that is
  that the config dialog, once created, is simply hidden and shown
  subsequently, so we need a reset-like method anyway.

- void apply()
  Read the config from the widgets and write it into the config file
  or the corresponding config manager.

- void dismiss()
  Called on cancel. You should clean up any temporary stuff you
  needed. If you work with a config-manager backed page, you want to
  call ConfigManager::rollback() here.

- void installProfile()
  This is called when the user selected a profile and hit apply. A
  profile is just another KConfig object. Therefore, this method
  should be the same as setup(), except that you should only alter
  widgets for configs that really exist in the profile.

For tabbed config pages, there exists a convenience class called
TabbedConfigurationPage, which (as of this writing only offers the
addTab() convenience method. It is planned to also provide
reimplemenations of setup, dismiss, apply and installProfile that just
call the same functions for each tab.
