Client MTMs provide a client-side API to functionality provided by the Server MTM, mainly for message manipulation and transport. It acts as the interface between the internal representation of a message’s data and the User Interface (UI) MTM. This architecture provides certain advantages; for example, a UI MTM can offer a user interface to enter message addresses without knowing the address storage format. Message client applications that do not require a user interface can use these client-side functions directly for automated message handling.
The Message Server entry with which Client MTMs currently work is referred to as the context. UI MTMs always operate on the contexts set through Client MTMs.
Client MTMs offer some or all of the following capabilities:
Address list manipulation
Creation of forward and response messages
Message subject access
Message validation
Internalising and externalising MTM-specific data to the Message Store
The base class for Client MTMs is CBaseMtm
which provides a high-level interface for accessing
and manipulating a Message Server entry
The following are
some significant functions in CBaseMtm
. Some functions
are implemented by the base class, and others must be implemented
in a derived class:
Context functions
The CBaseMtm::SetCurrentEntryL
(CMsvEntry *)
and CBaseMtm::SwitchCurrentEntryL
(TMsvId)
functions change the Message Server entry
(the context) on which later actions are performed. After creating
a new Client MTM object, a message client application must set an
initial context before using other functions.
Store and restore entry data functions
The changes that a message
client application makes to a message context through Client MTM functions,
such as altering the body text obtained through CBaseMtm::Body()
, are, for efficiency, cached in memory by a Client MTM. The message
store (SaveMessageL())and restore (LoadMessageL()), functions are concerned with transferring data between that cache
and committed storage.
Note: Unlike message contexts,
message client applications are not expected to manipulate directly
contexts for message services. Instead, the corresponding User
Interface MTM provides a dialog to allow the user
to alter service settings, and calls Client MTM functions to handle
their retrieval and storage.
Store and restore body text functions
The base class maintains
a private CRichText object cache to store the body
text for the current context. This can be accessed for reading and
writing by message client applications through CBaseMtm::Body()
. The CBaseMtm::StoreBodyL()
(CMsvStore
&)
and CBaseMtm::RestoreBodyL()
(CMsvStore &)
functions encapsulate the retrieval and
storage of this CRichText object to a CMsvStore
.
Address list functions
The format and storage of message addresses
is MTM-specific. The CBaseMtm::AddresseeList()
const
, CBaseMtm::AddAddresseeL
(const TDesC &)
, and CBaseMtm::RemoveAddressee
(TInt)
allow clients to access address information
in a generic way without having MTM-specific knowledge.
MTM-specific client functions
MTM components can offer protocol-specific
functionality not provided by base class interface functions. MTM
components define IDs that correspond to each protocol-specific operation
offered, and implement the CBaseMtm::InvokeSyncFunctionL()
and CBaseMtm::InvokeAsyncFunctionL()
functions
to allow clients to access these operations by passing in the appropriate
ID. These functions are provided to allow the MTM component to offer
both synchronous and asynchronous functionality. Message client applications
can dynamically add user-interface features for these operations using CBaseMtmUiData::MtmSpecificFunctions()
.
Query function
The Client MTM QueryCapability() function
is used to inquire whether an MTM supports a given feature. The first
argument is a UID defined to correspond to a particular capability.
The function returns a system error code and the response (whether
the MTM supports the queried capability) is passed back through the
second (by-reference)aResponse
parameter. This value
can be an integer or a bit set, depending on the capability.
The UIDs for the different types of query are as follows:
UID | Capability |
---|---|
|
Maximum message body size |
|
Maximum total size of message. |
|
Character widths supported by message type. The returned value is the sum of the appropriate values. The return type is either of KMtm7BitBody, KMtm8BitBody, KMtm16BitBody, and KMtmBinaryBody) |
|
To check if the attachments are supported |
|
Does the MTM message type have a subject field |
|
Does the MTM support folders |
|
Off-line operation allowed |
|
Can send the message |
|
Can receive the message |
|
Maximum number of recipients (-1 indicates unlimited numbers). |
|
When using the MTM in Send As, does a rendered image have to be prepared. |
|
Printer driver UID for rendering the fax image. |
|
UID of default message editor. |
|
Does the MTM support BIO messages |
|
Does the MTM support scheduled sending. |
|
Does the MTM support the use of recipient type. |
|
Support for Sending messages using SendAs. If this is supported, then the MTM supports sending messages created through the SendAs API. |
TInt CTextMtmClient::QueryCapability(TUid aCapability, TInt& aResponse) { TInt error = KErrNone; switch (aCapability.iUid) { case KUidMtmQueryMaxBodySizeValue: case KUidMtmQueryMaxTotalMsgSizeValue: aResponse = KMaxTextMessageSize; break; case KUidMtmQuerySupportedBodyValue: aResponse = KMtm8BitBody + KMtm7BitBody; break; case KUidMtmQueryOffLineAllowedValue: case KUidMtmQueryCanReceiveMsgValue: aResponse = ETrue; break; case KUidMtmQuerySupportAttachmentsValue: default: return KErrNotSupported; } return error; }
The CBaseMtm
class provides the virtual
functions for overriding in derived classes.
Many Messaging tasks can take a long time to
complete. For example, sending all items from the Outbox or downloading
a large number of items from a POP3 server. In these situations, the
use of active objects to wait for the completion of the task is inadequate,
since applications also require to obtain progress information while
the task is running (for example, to update a progress dialog). To
solve this problem, most asynchronous functions in Messaging create
a CMsvOperation
object and return a pointer to
it, as well as taking a TRequestStatus&
parameter.
The CMsvOperation
class is derived from CActive
and is used to obtain progress information about
a long-running task.
The base class for all client-side Messaging
operations is CMsvOperation
. The asynchronous methods
in the CBaseMtm
interface all return a pointer
to a CMsvOperation
object. When you implement an
MTM, you will have to provide your own custom operations, derived
from CMsvOperation
, and return those polymorphically
from your implementations of the asynchronous APIs.
Example
The forward operation of the Text MTM is explained in the following example.
The forward operation is an active object
state machine that runs a series of asynchronous tasks one after another
and waits for each to complete before moving on to the next. After
the operation has been completed, an active object is notified through
its TRequestStatus
passed into the operation at construction.
The forward operation is constructed using standard two-phase
construction. The NewL() function takes two arguments
of a TMsvId (source message and destination folder)
type, a CMsvSession
reference, and the observer’s TRequestStatus
, which is notified on completion. The active
object is constructed and added to the scheduler, and then ConstructL() is called:
void CTxtMtmForwardOperation::ConstructL() { iObserverRequestStatus = KRequestPending; SetActive(); TRequestStatus* stat = &iStatus; User::RequestComplete(stat, KErrNone); }
This has the effect of marking this stage of the operation as complete and means that RunL() is called at the next opportunity. This simplifies the state machine because it means that all states and tasks are started in RunL() and not in ConstructL().
The RunL() function starts each task and receives a notification
when it completes. The current task being executed is stored in iProgress and iState, as are the errors
and the ID of the new forwarded message, once complete. The default
state for iState on construction is EInit
.
void CTxtMtmForwardOperation::RunL() { User::LeaveIfError(iStatus.Int()); switch(iProgress.iState) { case TTxtMtmForwardOpProgress::EInit: iProgress.iState = TTxtMtmForwardOpProgress::ECreateMessage; CreateMessageL(); break; case TTxtMtmForwardOpProgress::ECreateMessage: iProgress.iState = TTxtMtmForwardOpProgress::EFormatBodyText; FormatBodyTextL(); break; case TTxtMtmForwardOpProgress::EFormatBodyText: iProgress.iState = TTxtMtmForwardOpProgress::EFinished; default: break; } if(!IsActive()) { TRequestStatus* stat = &iObserverRequestStatus; User::RequestComplete(stat, KErrNone); } }
If the state is EInit
, the RunL() changes state to ECreateMessage
and then calls CreateMessageL() to start the
process of creating a message asynchronously.
void CTxtMtmForwardOperation::CreateMessageL() {
The function constructs a CMsvEntry
for the source entry and then retrieves its index entry.
delete iMsvEntry; iMsvEntry = NULL; iMsvEntry = iMsvSession.GetEntryL(iSourceMessage); // Get the entry to be forwarded TMsvEntry forwardEntry = iMsvEntry->Entry();
The context of iMsvEntry is changed to the destination folder and an entry is created.
// Set the context to the destination folder iMsvEntry->SetEntryL(iDestinationFolder);
The index entry’s date is updated to reflect the current date.
// Create the forwarded index entry forwardEntry.iDate.HomeTime(); iMsvEntry->CreateL(forwardEntry);
The iFinalMsgId member of the progress is updated to reflect the newly created entry.
iProgress.iFinalMsgId = forwardEntry.Id(); // schedules the active object to complete so that this // class's RunL method // will be called by the active scheduler SetActive();
The operation’s TRequestStatus
is
then flagged to cause RunL().
TRequestStatus* stat = &iStatus; User::RequestComplete(stat, KErrNone); }
When this happens, iProgress.iState
is set to ECreateMessage
, so the following code
is executed:
case TTxtMtmForwardOpProgress::ECreateMessage: iProgress.iState = TTxtMtmForwardOpProgress::EFormatBodyText; FormatBodyTextL(); break;
The state is set to EFormatBodyText
and the FormatBodyTextL() function is called.
void CTxtMtmForwardOperation::FormatBodyTextL() {
The function constructs a CParaFormatLayer, a CCharFormatLayer, and a CRichText object. These objects are required to store the body text of the forwarded message.
CParaFormatLayer* paraLayer = CParaFormatLayer::NewL(); CleanupStack::PushL(paraLayer); CCharFormatLayer* charLayer = CCharFormatLayer::NewL(); CleanupStack::PushL(charLayer); CRichText* body = CRichText::NewL(paraLayer, charLayer); CleanupStack::PushL(body);
Then the context of iMsvEntry
is
changed to point at the source message and a read-only CMsvStore
is opened for that entry.
// Get the message store from the source entry iMsvEntry->SetEntryL(iSourceMessage); CMsvStore* readStore = iMsvEntry->ReadStoreL(); CleanupStack::PushL(readStore);
The context of the iMsvEntry object is changed to point at the newly created
entry and a CMsvStore
is opened for that entry.
// Get the message store from the newly created destination // entry iMsvEntry->SetEntryL(iProgress.iFinalMsgId); CMsvStore* writeStore = iMsvEntry->EditStoreL(); CleanupStack::PushL(writeStore); // Get the body text from the source entry and write it // to the destination entry, prepending the forward // prefix readStore->RestoreBodyTextL(*body); body->InsertL(0, KTxtMtmFwdPrefix); // this method performs a commit for us writeStore->StoreBodyTextL(*body); CleanupStack::PopAndDestroy(5, paraLayer); // schedules the active object to complete so that this // class's RunL method // will be called by the active scheduler SetActive(); TRequestStatus* stat = &iStatus; User::RequestComplete(stat, KErrNone); }
The iProgress.iState
is set to EFinished
, and because the object has not been activated
the following conditional statement evaluates true:
if(!IsActive()) { TRequestStatus* stat = &iObserverRequestStatus; User::RequestComplete(stat, KErrNone); }
The observer active object’s TRequestStatus
is flagged to let it know that the operation is complete.
To return progress from the operation, implement the ProgressL() and FinalProgress() functions. ProgressL() must be used to return progress information on the operation during
processing, and FinalProgress() is guaranteed to
return the status of a completed operation. ProgressL() must be implemented to leave with KErrNotReady
if
the operation is not in progress, and FinalProgress() must panic if the operation is not complete.
const TDesC8& CTxtMtmForwardOperation::ProgressL() { if (!IsActive()) { User::Leave(KErrNotReady); } iProgressBuf() = iProgress; return iProgressBuf; } const TDesC8& CTxtMtmForwardOperation::FinalProgress() { __ASSERT_ALWAYS(!IsActive(), User::Panic(KTxtCPanic, ETxtcOperationIsActive)); iProgressBuf() = iProgress; return iProgressBuf; }
The RunError() function must also be implemented to get any leaves in RunL().
TInt CTxtMtmForwardOperation::RunError(TInt aError) { iProgress.iError = aError; TRequestStatus* stat = &iObserverRequestStatus; User::RequestComplete(stat, aError); return KErrNone; }