// Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
//

#ifdef _DEBUG
#undef _NO_SESSION_LOGGING_
#endif

#include <s32std.h>
#include <txtrich.h>
#include "MSVIDS.H"
#include "MSVUIDS.H"

#include "MSVAPI.H"
#include "MSVCOP.H"
#include "MCLENTRY.H"
#include "MSVPANIC.H"
#include "MSVUTILS.H"

#include <mmsvstoremanager.h>

const TInt KMsvClientEntryArrayGranuality=8;
const TInt KMsvEntryObserverArrayGranuality=4;
const TInt KMsvMtmListGranularity=8;

//**********************************
// CMsvClientEntry
//**********************************

// static 
CMsvClientEntry* CMsvClientEntry::NewLC(const TMsvEntry& aEntry, TMsvClientEntryType aType)
	{
	CMsvClientEntry* self = new(ELeave) CMsvClientEntry(aEntry, aType);
	CleanupStack::PushL(self);
	self->ConstructL();
	return self;
	}


CMsvClientEntry::CMsvClientEntry(const TMsvEntry& aEntry, TMsvClientEntryType aType)
:iEntry(aEntry), iType(aType)
	{
	__DECLARE_NAME(_S("CMsvClientEntry"));
	}


CMsvClientEntry::~CMsvClientEntry()
	{
	delete iDescription;
	delete iDetails;
	}

void CMsvClientEntry::ConstructL()
	{
	iDescription = HBufC::NewL(iEntry.iDescription.Length());
	iDescription->Des().Copy(iEntry.iDescription);
	iEntry.iDescription.Set(iDescription->Des());
	iDetails = HBufC::NewL(iEntry.iDetails.Length());
	iDetails->Des().Copy(iEntry.iDetails);
	iEntry.iDetails.Set(iDetails->Des());
	}




//**********************************
// CMsvEntry
//**********************************
#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)

EXPORT_C CMsvEntry* CMsvEntry::NewL(CMsvSession& aMsvSession, TMsvId aMsvId, const TMsvSelectionOrdering& aOrdering, TBool aChildrenOfAvailableDrives)
/** Creates a new CMsvEntry for the specified entry ID. 

Note that this function does not create a new entry, but simply a new object 
to access an existing entry. To create a new entry, use CreateL().

@param aMsvSession The client’s Message Server session 
@param aMsvId ID of the entry to access 
@param aSortType  The initial sort order of children of the entry, for example, 
when returned by ChildrenL(). The order can be changed later by SetSortTypeL().
@param aChildrenOfAvailableDrives Indicates whether children from all available drives are 
to be fetched during construction of this entry. However, a value of true is valid only
if aMsvId is one of the standard folders, i.e. Inbox, Outbox, Drafts, Sent or Deleted.

@return If the function succeeds, this is a pointer to a newly allocated and initialised object. 
@leave KErrNotFound The requested entry does not exist.
@leave KErrNoMemory A memory allocation failed.
@leave KErrArgument aChildrenOfAvailableDrives is set to true and the requested entry is not 
one of the standard folders, i.e. Inbox, Outbox, Drafts, Sent or Deleted.
*/
    {
    CMsvEntry* self = new(ELeave) CMsvEntry(aMsvSession, aOrdering, aChildrenOfAvailableDrives);
    CleanupStack::PushL(self);
    self->ConstructL(aMsvId);
    CleanupStack::Pop();
    return self;
    }


CMsvEntry::CMsvEntry(CMsvSession& aMsvSession, const TMsvSelectionOrdering& aOrdering, TBool aChildrenOfAvailableDrives)
:iState(EValid), iMsvSession(aMsvSession), iOrdering(aOrdering), iChildrenOfAvailableDrives(aChildrenOfAvailableDrives)
    {
    __DECLARE_NAME(_S("CMsvEntry"));
    }


EXPORT_C void CMsvEntry::SetStandardFolderEntryL(TMsvId aId)
//
// Changes the context to another entry
// If the function leaves, the context is unchanged
//
/** Sets the context to the specified entry while also fetching
children from all available drives. This function can be used to
set the context to only the standard folders i.e. Inbox, Outbox,
Drafts, Sent or Deleted.

If the function leaves, the context is unchanged.

@param aId ID of the message entry which is to become the new context 
@leave KErrNotFound aId could not be found in the index.
@leave KErrArgument aId is not one of the standard folders, i.e. Inbox,
Outbox, Drafts, Sent or Deleted folders.
*/
    {
    __ASSERT_ALWAYS(iStore==NULL, PanicServer(EMsvStoreLeftOpen));

    if (aId==iEntryPtr->Id() && iState==EValid)
        return;
    
    SetEntryNoCheckL(aId, ETrue);
    }



#endif           // #if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)


EXPORT_C CMsvEntry* CMsvEntry::NewL(CMsvSession& aMsvSession, TMsvId aMsvId, const TMsvSelectionOrdering& aOrdering)
//
//
//
/** Creates a new CMsvEntry for the specified entry ID. 

Note that this function does not create a new entry, but simply a new object 
to access an existing entry. To create a new entry, use CreateL().

@param aMsvSession The client’s Message Server session 
@param aMsvId ID of the entry to access 
@param aSortType  The initial sort order of children of the entry, for example, 
when returned by ChildrenL(). The order can be changed later by SetSortTypeL(). 
@return If the function succeeds, this is a pointer to a newly allocated and initialised object. 
@leave KErrNotFound The requested entry does not exist 
@leave KErrNoMemory A memory allocation failed 
*/
	{
	CMsvEntry* self = new(ELeave) CMsvEntry(aMsvSession, aOrdering);
	CleanupStack::PushL(self);
	self->ConstructL(aMsvId);
	CleanupStack::Pop();
	return self;
	}


CMsvEntry::CMsvEntry(CMsvSession& aMsvSession, const TMsvSelectionOrdering& aOrdering)
:iState(EValid), iMsvSession(aMsvSession), iOrdering(aOrdering)
//
//
//
	{
	__DECLARE_NAME(_S("CMsvEntry"));
	}


EXPORT_C CMsvEntry::~CMsvEntry()
//
//
//
/** Destructor. 

This cleans up the object. CMsvEntry objects must be deleted by client applications 
when they are no longer required. Note that deleting a CMsvEntry object does 
not delete the context, simply the immediate means of accessing it. */
	{
	__ASSERT_ALWAYS(iStore==NULL, PanicServer(EMsvStoreLeftOpenOnDestruction2));

	if (iOberserverAdded)
		iMsvSession.RemoveObserver(*this);

	if (iEntries)
		iEntries->ResetAndDestroy();

	delete iEntries;
	delete iSortedChildren;
	delete iObservers;
	delete iMtmList;
	}

void CMsvEntry::ConstructL(TMsvId aId)
//
//
//
	{
#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
    if(iChildrenOfAvailableDrives && !MsvUtils::IsStandardId(aId))
        {
        User::Leave(KErrArgument);
        }
#endif
	iEntries = new (ELeave) CArrayPtrFlat<CMsvClientEntry>(KMsvClientEntryArrayGranuality);
	iMtmList = new(ELeave) CArrayFixFlat<TUid>(KMsvMtmListGranularity);
	iSortedChildren = CMsvEntryArray::NewL(*iMtmList);

	// get the required entry from the server
	CMsvClientEntry* cEntry = DoGetEntryLC(aId, iOwningService);
	cEntry->SetType(EMsvClientContext);
	iEntries->AppendL(cEntry);
	CleanupStack::Pop(); // cEntry
	iEntryPtr = &iEntries->At(0)->Entry();

	// get the children
	DoGetChildrenL();

	// Get the notification sequence number
	iNotifySequence = iMsvSession.Session().NotifySequenceL();

	// get the event notifications from the server
	iMsvSession.AddObserverL(*this);
	iOberserverAdded=ETrue;

#ifndef _NO_SESSION_LOGGING_
	Log(_L("Constructed CMsvEntry on entry %x"), aId); 
#endif
	}

#ifndef _NO_SESSION_LOGGING_
void CMsvEntry::Log(TRefByValue<const TDesC> aFmt, ...)
	{
	VA_LIST list;
	VA_START(list, aFmt);

	// Generate the text
	TBuf<256> buf;
	buf.FormatList(aFmt, list);

	// Write to file
	_LIT(KFormatFile, "CMsvEntry %x: %S");
	iMsvSession.iLog.WriteFormat(KFormatFile, this, &buf);

	// Write to serial
#ifndef _NO_SESSION_LOGGING_SERIAL_
	_LIT(KFormatSerial, "MSGS: CMsvEntry %x: %S");
	RDebug::Print(KFormatSerial, this, &buf);
#endif
	}
#endif

CMsvClientEntry* CMsvEntry::DoGetEntryLC(TMsvId aId, TMsvId& aOwningService)
//
// Gets the entry data from the server and create a CMsvClientEntry around it
//
	{
	TMsvEntry tEntry;
	User::LeaveIfError(iMsvSession.Session().GetEntry(aId, aOwningService, tEntry));
	if (tEntry.iType==KUidMsvRootEntry)
		aOwningService=aId;
	return CMsvClientEntry::NewLC(tEntry, EMsvClientNull);
	}



void CMsvEntry::DoGetChildrenL()
//
// Gets the children of the context and places them in the entries array
// The sorted children pointer array is implicited sorted, as the entries are retrieved
// from the server with the current sort order
//
	{
	__ASSERT_DEBUG(iEntries->Count()==1, PanicServer(EMsvChildEntriesExist1));
	__ASSERT_DEBUG(iSortedChildren->Count()==0, PanicServer(EMsvChildEntriesExist2));

	// get the children from the server - using only the visible flag
	TMsvSelectionOrdering order(KMsvNoGrouping, EMsvSortByNone, iOrdering.ShowInvisibleEntries());
#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
    // If child entries from all drives in the device are to be fetched.
    if(iChildrenOfAvailableDrives)
        {
		User::LeaveIfError(iMsvSession.Session().GetChildrenAll(iEntryPtr->Id(), *iEntries, order));
		}
	else
#endif
        {
        User::LeaveIfError(iMsvSession.Session().GetChildren(iEntryPtr->Id(), *iEntries, order));
		}
	
	// add the children to the sorted pointer list - current context is the first entry
	TInt totalCount=iEntries->Count();
	for (TInt count=1; count<totalCount; count++)
		iSortedChildren->AppendL(&iEntries->At(count)->Entry());

	// sort the children
	iSortedChildren->SortL(iOrdering);
	}


EXPORT_C void CMsvEntry::SetEntryL(TMsvId aId)
//
// Changes the context to another entry
// If the function leaves, the context is unchanged
//
/** Sets the context to the specified entry. 

If the function leaves, the context is unchanged.

@param aId ID of the message entry which is to become the new context 
@leave KErrNotFound aId could not be found in the index */
	{
	__ASSERT_ALWAYS(iStore==NULL, PanicServer(EMsvStoreLeftOpen));

	if (aId==iEntryPtr->Id() && iState==EValid)
		return;
	
 	SetEntryNoCheckL(aId);
	}

#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
EXPORT_C void CMsvEntry::SetEntryNoCheckL(TMsvId aId, TBool aChildrenOfAvailableDrives /* DEFAULT=EFalse*/)
//
// Changes the context to another entry
// If the function leaves, the context is unchanged
// The function does not check for when the context is already on
// the given TMsvId.
//
/** Sets the context to the specified entry.
Children from all available drives will be fetched depending on the value of
aChildrenOfAvailableDrives.

If the function leaves, the context is unchanged.

@internalTechnology
@param aId ID of the message entry which is to become the new context
@param aChildrenOfAvailableDrives Indicates whether children from all available drives are to
be fetched during construction of this entry. However, a value of true is valid only
if aMsvId is one among TMsvId's of Inbox, Outbox, Drafts, Sent or Deleted folders.
@leave KErrNotFound aMsvId could not be found in the index 
@leave KErrArgument aChildrenOfAvailableDrives is set to true and the requested entry is not
one of the standard folders, i.e. Inbox, Outbox, Drafts, Sent or Deleted. */
    {
    __ASSERT_ALWAYS(iStore==NULL, PanicServer(EMsvStoreLeftOpen));

    if(aChildrenOfAvailableDrives && !MsvUtils::IsStandardId(aId))
        {
        User::Leave(KErrArgument);
        }

    // create new entry array
    CArrayPtrFlat<CMsvClientEntry>* newEntries = new (ELeave) CArrayPtrFlat<CMsvClientEntry>(KMsvClientEntryArrayGranuality);
    CleanupStack::PushL(newEntries);

    // get the new context and place in array
    TMsvId newOwningService;
    CMsvClientEntry* cEntry = DoGetEntryLC(aId, newOwningService);
    cEntry->SetType(EMsvClientContext);
    newEntries->AppendL(cEntry);
    // create new children array
    CMsvEntryArray* newSortedChildren = CMsvEntryArray::NewL(*iMtmList);

    // keep the old arrays
    CArrayPtrFlat<CMsvClientEntry>* oldEntries = iEntries;
    CMsvEntryArray* oldSortedChildren = iSortedChildren;

    TBool oldFlag = iChildrenOfAvailableDrives;
    iChildrenOfAvailableDrives = aChildrenOfAvailableDrives;

    // use new arrays
    CleanupStack::Pop(2); // cEntry, newEntries
    iEntries = newEntries;
    iSortedChildren = newSortedChildren;
    iEntryPtr = &iEntries->At(0)->Entry();

    // get the children and sort accordingly
    TRAPD(leave, DoGetChildrenL(); iNotifySequence = iMsvSession.Session().NotifySequenceL());

    if (leave)
        {
#ifndef _NO_SESSION_LOGGING_
        Log(_L("Failed to SetEntryL to %x with %d"), aId, leave); 
#endif

        // reset the old context
        iChildrenOfAvailableDrives = oldFlag;

        iEntries = oldEntries;
        iSortedChildren = oldSortedChildren;
        iEntryPtr = &iEntries->At(0)->Entry();
        // cleanup
        newEntries->ResetAndDestroy();
        delete newEntries;
        delete newSortedChildren;
        // propogate leave
        User::Leave(leave);
        }
    else
        {
#ifndef _NO_SESSION_LOGGING_
        Log(_L("SetEntryL to %x, sequence %d"), aId, iNotifySequence); 
#endif

        // set the new owning service
        iOwningService = newOwningService;
        // delete the old context
        oldEntries->ResetAndDestroy();
        delete oldEntries;
        delete oldSortedChildren;
        // make sure the state is marked as valid
        iState = EValid;
        }
    }
#else   //#define SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT

EXPORT_C void CMsvEntry::SetEntryNoCheckL(TMsvId aId)
//
// Changes the context to another entry
// If the function leaves, the context is unchanged
// The function does not check for when the context is already on
// the given TMsvId.
//
/** Sets the context to the specified entry. 

If the function leaves, the context is unchanged.

@internalTechnology
@param aId ID of the message entry which is to become the new context 
@leave KErrNotFound aMsvId could not be found in the index */
	{
	__ASSERT_ALWAYS(iStore==NULL, PanicServer(EMsvStoreLeftOpen));

	// create new entry array
	CArrayPtrFlat<CMsvClientEntry>* newEntries = new (ELeave) CArrayPtrFlat<CMsvClientEntry>(KMsvClientEntryArrayGranuality);
	CleanupStack::PushL(newEntries);

	// get the new context and place in array
	TMsvId newOwningService;
	CMsvClientEntry* cEntry = DoGetEntryLC(aId, newOwningService);
	cEntry->SetType(EMsvClientContext);
	newEntries->AppendL(cEntry);
	// create new children array
	CMsvEntryArray* newSortedChildren = CMsvEntryArray::NewL(*iMtmList);

	// keep the old arrays
	CArrayPtrFlat<CMsvClientEntry>* oldEntries = iEntries;
	CMsvEntryArray* oldSortedChildren = iSortedChildren;

	// use new arrays
	CleanupStack::Pop(2); // cEntry, newEntries
	iEntries = newEntries;
	iSortedChildren = newSortedChildren;
	iEntryPtr = &iEntries->At(0)->Entry();

	// get the children and sort accordingly
	TRAPD(leave, DoGetChildrenL(); iNotifySequence = iMsvSession.Session().NotifySequenceL());

	if (leave)
		{
#ifndef _NO_SESSION_LOGGING_
		Log(_L("Failed to SetEntryL to %x with %d"), aId, leave); 
#endif

		// reset the old context
		iEntries = oldEntries;
		iSortedChildren = oldSortedChildren;
		iEntryPtr = &iEntries->At(0)->Entry();
		// cleanup
		newEntries->ResetAndDestroy();
		delete newEntries;
		delete newSortedChildren;
		// propogate leave
		User::Leave(leave);
		}
	else
		{
#ifndef _NO_SESSION_LOGGING_
		Log(_L("SetEntryL to %x, sequence %d"), aId, iNotifySequence); 
#endif

		// set the new owning service
		iOwningService = newOwningService;
		// delete the old context
		oldEntries->ResetAndDestroy();
		delete oldEntries;
		delete oldSortedChildren;
		// make sure the state is marked as valid
		iState = EValid;
		}
	}
#endif
/**
Creates a new child entry owned by the context asynchronously.

Note that all session observers are notified when a new entry is created with 
an EMsvEntriesCreated event (see TMsvSessionEvent). CMsvEntry objects are 
such session observers themselves. When the object receives such a session 
notification, it calls all registered entry observers with a TMsvEntryEvent 
event EMsvNewChildren, passing in the ID of the new child.

If aEntry is not a service entry, then the context must not be set to the root entry and iServiceId field must be defined .
If aEntry is a service entry, then the context must be set to the root entry.

The returned CMsvOperation object completes when creation is complete.

@param	aEntry
Index entry value for the new entry 

@param	aStatus
The request status to be completed when the operation has finished 

@leave	KErrArgument aEntry is invalid 

@return 
The operation object controlling the create command. 
*/
EXPORT_C CMsvOperation* CMsvEntry::CreateL(const TMsvEntry& aEntry, TRequestStatus& aStatus)
	{
	return CreateL(aEntry, RProcess().SecureId(), aStatus);
	}
	
/**
Creates a new child entry owned by the context asynchronously. Sets the owner of 
the created entry to process specified by the supplied ID.

Note that all session observers are notified when a new entry is created with 
an EMsvEntriesCreated event (see TMsvSessionEvent). CMsvEntry objects are 
such session observers themselves. When the object receives such a session 
notification, it calls all registered entry observers with a TMsvEntryEvent 
event EMsvNewChildren, passing in the ID of the new child.

If aEntry is not a service entry, then the context must not be set to the root entry and iServiceId field must be defined .
If aEntry is a service entry, then the context must be set to the root entry.

The returned CMsvOperation object completes when creation is complete.

@param	aEntry
Index entry value for the new entry 

@param	aOwnerId
The ID of process that owns the created entry.

@param	aStatus
The request status to be completed when the operation has finished 

@leave 	KErrArgument aEntry is invalid 

@return 
The operation object controlling the create command. 
*/
EXPORT_C CMsvOperation* CMsvEntry::CreateL(const TMsvEntry& aEntry, TSecureId aOwnerId, TRequestStatus& aStatus)
	{
#ifndef _NO_SESSION_LOGGING_
	Log(_L("Asynchronous CreateL")); 
#endif

	__ASSERT_DEBUG(iState==EValid || iState==EInvalidDeletedContext || iState==EInvalidChangingContext, PanicServer(EMsvEntryStateUndetermined));

	TMsvEntry entry=aEntry;
	entry.SetParent(iEntryPtr->Id());

	__ASSERT_DEBUG(MsvUtils::ValidEntry(entry, ETrue), PanicServer(EMsvCreatingInvalidEntry));
	if (!MsvUtils::ValidEntry(entry, ETrue))
		User::Leave(KErrArgument);

	CMsvEntryOperation* operation = CMsvEntryOperation::NewL(iMsvSession, aStatus);
	CleanupStack::PushL(operation);
	User::LeaveIfError(iMsvSession.Session().OperationMtmL(iEntryPtr->Id(), operation->iMtm, operation->iService));
	iMsvSession.Session().CreateEntryL(entry, operation->Id(), aOwnerId, operation->iStatus);
	operation->Start();
	CleanupStack::Pop(); // operation
	return operation;
	}
	

/** 
Creates a new child entry owned by the context synchronously. 

Note that all session observers are notified when a new entry is created with 
an EMsvEntriesCreated event (see TMsvSessionEvent). CMsvEntry objects are 
such session observers themselves. When the object receives such a session 
notification, it calls all registered entry observers with a TMsvEntryEvent 
event EMsvNewChildren, passing in the ID of the new child.

If aEntry is not a service entry, then the context must not be set to the root entry and iServiceId field must be defined .
If aEntry is a service entry, then the context must be set to the root entry.

This function can only be used on local entries.

@param	aEntry
Index entry value for the new entry 

@leave	KErrArgument aEntry is invalid
*/
EXPORT_C void CMsvEntry::CreateL(TMsvEntry& aEntry)
	{
	CreateL(aEntry, RProcess().SecureId());
	}
	
/** 
Creates a new child entry owned by the context synchronously. Sets the owner of 
the created entry to process specified by the supplied ID. 

Note that all session observers are notified when a new entry is created with 
an EMsvEntriesCreated event (see TMsvSessionEvent). CMsvEntry objects are 
such session observers themselves. When the object receives such a session 
notification, it calls all registered entry observers with a TMsvEntryEvent 
event EMsvNewChildren, passing in the ID of the new child.

If aEntry is not a service entry, then the context must not be set to the root entry and iServiceId field must be defined .
If aEntry is a service entry, then the context must be set to the root entry.

This function can only be used on local entries.

@param	aEntry
Index entry value for the new entry

@param	aOwnerId
The ID of process that owns the created entry.

@leave	KErrArgument aEntry is invalid
*/
EXPORT_C void CMsvEntry::CreateL(TMsvEntry& aEntry, TSecureId aOwnerId)
	{
#ifndef _NO_SESSION_LOGGING_
	Log(_L("Synchronous CreateL")); 
#endif
	__ASSERT_ALWAYS(iOwningService == KMsvLocalServiceIndexEntryId || iOwningService == KMsvRootIndexEntryId, PanicServer(EMsvNotLocalService));
	__ASSERT_DEBUG(iState==EValid || iState==EInvalidDeletedContext || iState==EInvalidChangingContext, PanicServer(EMsvEntryStateUndetermined));

	TMsvEntry entry=aEntry;
	entry.SetParent(iEntryPtr->Id());

	__ASSERT_DEBUG(MsvUtils::ValidEntry(entry, ETrue), PanicServer(EMsvCreatingInvalidEntry));
	if (!MsvUtils::ValidEntry(entry, ETrue))
		User::Leave(KErrArgument);

	CMsvEntryArray* newSortedChildren = NULL;
	CMsvClientEntry* cEntry = NULL;

	TBool addEntry = entry.Visible() || iOrdering.ShowInvisibleEntries();
	if (addEntry)
		{
		// Create what may be the new child entry if nothing goes wrong
		cEntry = CMsvClientEntry::NewLC(entry, EMsvClientChild);

		// Reserve space for the new entry for later
		iEntries->SetReserveL(iEntries->Count() + 1);

		newSortedChildren = CMsvEntryArray::NewLC(*iMtmList);

		// add the children to the sorted pointer list - current context is the first entry
		TInt totalCount=iEntries->Count();
		for (TInt count=1; count<totalCount; count++)
			newSortedChildren->AppendL(&iEntries->At(count)->Entry());

		// We've created a new sorted child list now so we won't leave later
		newSortedChildren->AppendL(&cEntry->Entry());
		newSortedChildren->SortL(iOrdering);
		}

	TInt id = iMsvSession.OperationId();
	iMsvSession.Session().CreateEntryL(entry, id, aOwnerId);
	iMsvSession.CheckDrive();

    TPckgBuf<TMsvLocalOperationProgress> progressPack;
	User::LeaveIfError(iMsvSession.Session().OperationCompletion(id, progressPack));
	User::LeaveIfError(progressPack().iError);

	// Can't leave after here

	if (addEntry)
		{
		CleanupStack::Pop(); // newSortedChildren
		delete iSortedChildren;
		iSortedChildren = newSortedChildren;

		CleanupStack::Pop(); // cEntry
		iEntries->AppendL(cEntry); // Will not leave because we've reserved space earlier
		cEntry->SetId(progressPack().iId);

		TMsvEntry* ptr = CONST_CAST(TMsvEntry*, iEntryPtr);
		ptr->SetOwner(ETrue);
		}

	aEntry.SetParent(iEntryPtr->Id());
	aEntry.SetId(progressPack().iId);

	// If the entry is a service its service Id is the same as its Id
	// Otherwise it must be a local entry
	// We don't allow synchronous creation of remote entries
	TMsvId serviceId = aEntry.Id();

	aEntry.iServiceId = (aEntry.iType == KUidMsvServiceEntry) ? serviceId : KMsvLocalServiceIndexEntryId;
	}

/** 
Sets the context's index entry to the specified values. The returned CMsvOperation 
object completes when the change is complete.

It is important to note that the state of the context is undefined until the 
observer of the entry has been informed that the entry has been changed, or 
the operation is completed with an error. If the function leaves, the context 
is unchanged.

@param	aEntry 
The new index entry values for the context

@param	aStatus 
The request status to be completed when the operation has finished 

@leave KErrAccessDenied The entry is locked by another client 

@leave KErrArgument aEntry is invalid or the ID specified in aEntry is not 
the same as the context ID 

@leave KErrNoMemory The operation could not be created or passed to the server 

@return
An operation object controlling the change command
*/
EXPORT_C CMsvOperation* CMsvEntry::ChangeL(const TMsvEntry& aEntry, TRequestStatus& aStatus)
	{
	return ChangeL(aEntry, RProcess().SecureId(), aStatus);
	}

/** 
Sets the context's index entry to the specified values. The returned CMsvOperation 
object completes when the change is complete. Sets the owner of the changed entry 
to process specified by the supplied ID. 

It is important to note that the state of the context is undefined until the 
observer of the entry has been informed that the entry has been changed, or 
the operation is completed with an error. If the function leaves, the context 
is unchanged.

@param	aEntry 
The new index entry values for the context

@param	aOwnerId
The ID of process that owns the changed entry.

@param	aStatus 
The request status to be completed when the operation has finished 

@leave KErrAccessDenied The entry is locked by another client 

@leave KErrArgument aEntry is invalid or the ID specified in aEntry is not 
the same as the context ID 

@leave KErrNoMemory The operation could not be created or passed to the server 

@return
An operation object controlling the change command
*/
EXPORT_C CMsvOperation* CMsvEntry::ChangeL(const TMsvEntry& aEntry, TSecureId aOwnerId, TRequestStatus& aStatus)
	{
#ifndef _NO_SESSION_LOGGING_
	Log(_L("Asynchronous ChangeL to %x"), aEntry.Id()); 
#endif

	__ASSERT_DEBUG(iState==EValid || iState==EInvalidDeletedContext, PanicServer(EMsvEntryAlreadyChangingContext));
	__ASSERT_DEBUG(aEntry.Id()==iEntryPtr->Id(), PanicServer(EMsvChangingEntryNotContext));
	__ASSERT_DEBUG(MsvUtils::ValidEntry(aEntry), PanicServer(EMsvChangingToInvalidEntry));

	// can only change the current context
	if (aEntry.Id()!=iEntryPtr->Id() || aEntry.Parent()!=iEntryPtr->Parent() || !MsvUtils::ValidEntry(aEntry))
		User::Leave(KErrArgument);

	// cannot change standard folders
	if (iEntryPtr->StandardFolder())
		User::Leave(KErrAccessDenied);

	// create the operation
	CMsvEntryOperation* operation = CMsvEntryOperation::NewL(iMsvSession, aStatus);
	CleanupStack::PushL(operation);
	if (iEntryPtr->iType==KUidMsvServiceEntry)
		{
		operation->iMtm = KUidMsvLocalServiceMtm;
		operation->iService = KMsvLocalServiceIndexEntryId;
		}
	else
		User::LeaveIfError(iMsvSession.Session().OperationMtmL(iEntryPtr->Id(), operation->iMtm, operation->iService));
	
	// check that no other entries are type EMsvClientChangedContext
	TInt count=iEntries->Count();
	while (count--)
		{
		if (iEntries->At(count)->Type() == EMsvClientChangedContext)
			{
			delete iEntries->At(count);
			iEntries->Delete(count);
			}
		}

	// create local copy of entry
	TMsvEntry entry=aEntry;

	// check the hidden flags are correct
	entry.SetOwner(iEntryPtr->Owner());
	entry.SetDeleted(iEntryPtr->Deleted());

	// store the new context for after the operation has completed
	CMsvClientEntry* cEntry = CMsvClientEntry::NewLC(entry, EMsvClientChangedContext);
	if (iEntries->Count()==1)
		iEntries->AppendL(cEntry);
	else
		iEntries->InsertL(1, cEntry);

	// start the change operation
	TRAPD(leave, iMsvSession.Session().ChangeEntryL(entry, operation->Id(), aOwnerId, operation->iStatus)); 
	if (leave)
		{
		iEntries->Delete(1);
		CleanupStack::PopAndDestroy(); // operation	& cEntry
		User::Leave(leave);
		}

	operation->Start();
	iState = EInvalidChangingContext;
	CleanupStack::Pop(2); // operation and cEntry
	return operation;
	}

/** 
Sets the context's index entry to the specified values. The function is performed 
synchronously.

This function can only be used on local entries.

@param	aEntry 
The new index entry values for the context 

@leave KErrAccessDenied The entry is locked by another client 

@leave KErrArgument aEntry is invalid or the ID specified in aEntry is not 
the same as the context ID 

@leave KErrNoMemory The operation could not be created or passed to the server
*/
EXPORT_C void CMsvEntry::ChangeL(const TMsvEntry& aEntry)
	{
	ChangeL(aEntry, RProcess().SecureId());
	}

/** 
Sets the context's index entry to the specified values. The function is performed 
synchronously. Sets the owner of the changed entry to process specified by the 
supplied ID. 

This function can only be used on local entries.

@param	aEntry 
The new index entry values for the context 

@param	aOwnerId
The ID of process that owns the changed entry.

@leave KErrAccessDenied The entry is locked by another client 

@leave KErrArgument aEntry is invalid or the ID specified in aEntry is not 
the same as the context ID 

@leave KErrNoMemory The operation could not be created or passed to the server
*/
EXPORT_C void CMsvEntry::ChangeL(const TMsvEntry& aEntry, TSecureId aOwnerId)
	{
#ifndef _NO_SESSION_LOGGING_
	Log(_L("Synchronous ChangeL to %x"), aEntry.Id()); 
#endif

	__ASSERT_DEBUG(iState==EValid || iState==EInvalidDeletedContext, PanicServer(EMsvEntryAlreadyChangingContext));
	__ASSERT_DEBUG(aEntry.Id()==iEntryPtr->Id(), PanicServer(EMsvChangingEntryNotContext));
	__ASSERT_DEBUG(MsvUtils::ValidEntry(aEntry), PanicServer(EMsvChangingToInvalidEntry));

	// can only change the current context
	if (aEntry.Id()!=iEntryPtr->Id() || aEntry.Parent()!=iEntryPtr->Parent() || !MsvUtils::ValidEntry(aEntry))
		User::Leave(KErrArgument);

	// cannot change standard folders
	if (iEntryPtr->StandardFolder())
		User::Leave(KErrAccessDenied);

	// create local copy of entry
	TMsvEntry entry=aEntry;

	// check the hidden flags are correct
	entry.SetOwner(iEntryPtr->Owner());
	entry.SetDeleted(iEntryPtr->Deleted());

	// store the new context for after the operation has completed
	CMsvClientEntry* cEntry = CMsvClientEntry::NewLC(entry, EMsvClientContext);

	TInt id = iMsvSession.OperationId();
	iMsvSession.Session().ChangeEntryL(aEntry, id, aOwnerId); 

    TPckgBuf<TMsvLocalOperationProgress> progressPack;
	User::LeaveIfError(iMsvSession.Session().OperationCompletion(id, progressPack));
	User::LeaveIfError(progressPack().iError);

	// Cannot leave after this
	delete iEntries->At(0);
	CleanupStack::Pop(); // cEntry
	iEntries->At(0) = cEntry;
	iEntryPtr = &iEntries->At(0)->Entry();
	}


EXPORT_C CMsvOperation* CMsvEntry::DeleteL(TMsvId aId, TRequestStatus& aStatus)
//
// Deletes a child of the context
//
	/** Deletes a child entry of the context asynchronously.
	
	The delete works recursively through all the descendants. If a child or any 
	descendant is locked by another client or any store or file is open, then 
	that child will not be deleted. Any files and stores associated with the entry 
	are deleted.
	
	The returned CMsvOperation object completes when deletion is complete.
	
	
	@param aId ID of entry to be deleted 
	@param aStatus The request status to be completed when the operation has finished 
	
	@leave KErrNotFound The specified entry was not a child of the context 
	@leave KErrNotSupported If deleting entries from non-current drive
	@return The operation object controlling the deletion command */
	{
#ifndef _NO_SESSION_LOGGING_
	Log(_L("Asynchronous DeleteL, entry %x"), aId); 
#endif

	__ASSERT_DEBUG(iState==EValid || iState==EInvalidDeletedContext || iState==EInvalidChangingContext, PanicServer(EMsvEntryStateUndetermined));
	CMsvEntrySelection* selection = new (ELeave) CMsvEntrySelection;
	CleanupStack::PushL(selection);
	selection->AppendL(aId);
	CMsvOperation* operation = DoDeleteL(*selection, aStatus);
	CleanupStack::PopAndDestroy(); // selection
	return operation;
	}



EXPORT_C CMsvOperation* CMsvEntry::DeleteL(const CMsvEntrySelection& aSelection, TRequestStatus& aStatus)
//
// Deletes a selection containing children of the context
//
/** Deletes child entries of the context asynchronously. 

The delete works recursively through all the descendants. If a child or any 
descendant is locked by another client or any store or file is open, then 
that child will not be deleted. Any files and stores associated with the entries 
are deleted.

The returned CMsvOperation object completes when deletion is complete.

@param aSelection List of ID of the entries to be deleted 
@param aStatus The request status to be completed when the operation has finished 

@leave KErrNotFound A specified entry was not a child of the context 
@leave KErrNotSupported If deleting entries from non-current drive
@return The operation object controlling the deletion command */
	{
#ifndef _NO_SESSION_LOGGING_
	Log(_L("Asynchronous DeleteL with selection of %d entries"), aSelection.Count()); 
#endif

	__ASSERT_DEBUG(iState==EValid || iState==EInvalidDeletedContext || iState==EInvalidChangingContext, PanicServer(EMsvEntryStateUndetermined));
	return DoDeleteL(aSelection, aStatus);
	}



CMsvOperation* CMsvEntry::DoDeleteL(const CMsvEntrySelection& aSelection, TRequestStatus& aStatus)
//
//
//
	{
	__ASSERT_DEBUG(aSelection.Count(), PanicServer(EMsvEmptySelection));
	
	if (!AreChildren(aSelection))
		User::Leave(KErrNotFound);

	CMsvEntryOperation* operation = CMsvEntryOperation::NewL(iMsvSession, aStatus);
	CleanupStack::PushL(operation);
	if (iEntryPtr->iType==KUidMsvRootEntry)
		{
		// we must be deleting services - so it is a local operation
		operation->iMtm = KUidMsvLocalServiceMtm;
		operation->iService = KMsvLocalServiceIndexEntryId;
		}
	else
		User::LeaveIfError(iMsvSession.Session().OperationMtmL(aSelection.At(0), operation->iMtm, operation->iService));

#if defined(_DEBUG)
	// check other entries in selection are consistent and are not read only
	TInt dCount = aSelection.Count();
	while (dCount--)
		{
		if (iEntryPtr->iType!=KUidMsvRootEntry)
			{
				TMsvId service;
				TUid mtm;
				TInt error = iMsvSession.Session().OperationMtmL(aSelection.At(dCount), mtm, service);
				__ASSERT_DEBUG(error==KErrNone || error==KErrNotFound, PanicServer(EMsvNonConsistentDeleteSelection));
				__ASSERT_DEBUG(mtm==operation->iMtm, PanicServer(EMsvNonConsistentDeleteSelection));
				__ASSERT_DEBUG(service==operation->iService, PanicServer(EMsvNonConsistentDeleteSelection));
			}
		TMsvEntry dEntry;
		TMsvId dService;
		if (iMsvSession.Session().GetEntry(aSelection.At(dCount), dService, dEntry)==KErrNone)
			{
			__ASSERT_DEBUG(dService==iOwningService || (iEntryPtr->iType==KUidMsvServiceEntry && dService==iEntryPtr->Id()) || (iEntryPtr->iType==KUidMsvRootEntry && dService==aSelection.At(dCount)), PanicServer(EMsvDeletingEntryDifferentOwningService));
			}
		}
#endif
	
	iMsvSession.Session().DeleteEntriesL(aSelection, operation->Id(), operation->iStatus);
	operation->Start();
	CleanupStack::Pop(); // operation
	return operation;
	}

EXPORT_C void CMsvEntry::DeleteL(TMsvId aId)
/** Deletes a child entry of the context synchronously. 

The delete works recursively through all the descendants. If a child or any descendant is locked by another 
client or any store or file is open, then that child will not be deleted. Any files and stores associated 
with the entry are deleted.

This function can only be used on local entries.

@param aId ID of entry to be deleted
@leave KErrNotFound The specified entry was not a child of the context
@leave KErrNotSupported If deleting entries from non-current drive
*/
	{
#ifndef _NO_SESSION_LOGGING_
	Log(_L("Synchronous DeleteL, entry %x"), aId); 
#endif
	__ASSERT_ALWAYS(iOwningService == KMsvLocalServiceIndexEntryId || iOwningService == KMsvRootIndexEntryId, PanicServer(EMsvNotLocalService)); // Root as well?
	__ASSERT_DEBUG(iState==EValid || iState==EInvalidDeletedContext || iState==EInvalidChangingContext, PanicServer(EMsvEntryStateUndetermined));
	
	User::LeaveIfError(CMsvEntry::DeleteOneL(aId));
	}

EXPORT_C void CMsvEntry::DeleteL(const CMsvEntrySelection& aSelection, TMsvLocalOperationProgress& aProgress)
/** Deletes child entries of the context synchronously. 

The delete works recursively through all the descendants. If a child or any 
descendant is locked by another client or any store or file is open, then 
that child will not be deleted. Any files and stores associated with the entries 
are deleted.

@param aSelection List of ID of the entries to be deleted 
@param aProgress Progress information for the delete operation
@leave KErrNotFound A specified entry was not a child of the context 
@leave KErrNotSupported If deleting entries from non-current drive */
	{
#ifndef _NO_SESSION_LOGGING_
	Log(_L("Synchronous DeleteL with selection of %d entries"), aSelection.Count()); 
#endif

	__ASSERT_ALWAYS(iOwningService == KMsvLocalServiceIndexEntryId || iOwningService == KMsvRootIndexEntryId, PanicServer(EMsvNotLocalService)); // Root as well?
	__ASSERT_DEBUG(iState==EValid || iState==EInvalidDeletedContext || iState==EInvalidChangingContext, PanicServer(EMsvEntryStateUndetermined));
	__ASSERT_DEBUG(aSelection.Count(), PanicServer(EMsvEmptySelection));

	if (!AreChildren(aSelection))
		User::Leave(KErrNotFound);

	aProgress.iTotalNumberOfEntries=aSelection.Count();
	aProgress.iNumberCompleted=0;
	aProgress.iNumberRemaining=aProgress.iTotalNumberOfEntries;
	aProgress.iError=KErrNone;

	TInt count=aProgress.iTotalNumberOfEntries;
	while(count--)
		{
		aProgress.iId=aSelection.At(count);
		TInt err=DeleteOneL(aProgress.iId);
		aProgress.iNumberRemaining--;
		if(err==KErrNone)
			aProgress.iNumberCompleted++;
		else
			{
			aProgress.iError=err;
			aProgress.iNumberFailed++;
			}
		}
	}


TInt CMsvEntry::DeleteOneL(TMsvId aMsvId)
	{
	CMsvEntrySelection* selection = new(ELeave)CMsvEntrySelection;
	CleanupStack::PushL(selection);
	selection->AppendL(aMsvId);
	TInt opid = iMsvSession.OperationId();
	iMsvSession.Session().DeleteEntriesL(*selection, opid);

	TMsvLocalOperationProgress progress;
	TPckg<TMsvLocalOperationProgress> progressPack(progress);
    User::LeaveIfError(iMsvSession.Session().OperationCompletion(opid, progressPack));
	
	if(progress.iError==KErrNone)
		{
		TMsvId id = selection->At(0);
		TInt ii = iSortedChildren->Count();
		while (ii--)
			{
			if (iSortedChildren->At(ii)->Id() == id)
				{
				iSortedChildren->Delete(ii);
				break;
				}
			}

		ii = iEntries->Count();
		while (ii--)
			{
			if (iEntries->At(ii)->Entry().Id() == id)
				{
				delete iEntries->At(ii);
				iEntries->Delete(ii);
				break;
				}
			}
		// Reset the owner flag
		if (Count() == 0)
			{
			TMsvEntry* ptr = CONST_CAST(TMsvEntry*, iEntryPtr);
			ptr->SetOwner(EFalse);
			}		
		}
	CleanupStack::PopAndDestroy(selection);
	return(progress.iError);
	}




EXPORT_C CMsvEntrySelection* CMsvEntry::ChildrenL() const
//
// Gets a selection containing the children of the context
//
/** Gets a selection containing the IDs of all the context children. If the entry 
has no children, the selection is empty.

The calling function is responsible for the deletion of the returned CMsvEntrySelection. 

@leave KErrNoMemory Not enough memory to create the selection 
@return A selection containing the ID of all children of the context */
	{
	__ASSERT_DEBUG(iState==EValid || iState==EInvalidDeletedContext || iState==EInvalidChangingContext, PanicServer(EMsvEntryStateUndetermined));
	CMsvEntrySelection* selection = new (ELeave) CMsvEntrySelection;
	CleanupStack::PushL(selection);
	TInt totalCount=iSortedChildren->Count();
	for (TInt count=0; count<totalCount; count++)
		{
		selection->AppendL(iSortedChildren->At(count)->Id());
		}
	CleanupStack::Pop(); // selection
	return selection;
	}

EXPORT_C CMsvEntrySelection* CMsvEntry::ChildrenWithServiceL(TMsvId aServiceId) const
//
// Gets a selection containing the children of the context which use the service
//
/** Gets a selection containing the IDs of all the context children filtered by message service.
i.e. the index entry's iServiceId field equals aId.

If the entry has no such children, the selection is empty.

The calling function is responsible for the deletion of the returned CMsvEntrySelection.
@return	List of IDs of all children of the context meeting the criterion		
@param aServiceId Service by which to filter
@leave KErrNoMemory Not enough memory to create the selection 
*/
	{
	__ASSERT_DEBUG(iState==EValid || iState==EInvalidDeletedContext || iState==EInvalidChangingContext, PanicServer(EMsvEntryStateUndetermined));
	CMsvEntrySelection* selection = new (ELeave) CMsvEntrySelection;
	CleanupStack::PushL(selection);
	TInt totalCount=iSortedChildren->Count();
	for (TInt count=0; count<totalCount; count++)
		{
		if (iSortedChildren->At(count)->iServiceId==aServiceId)
			{
			selection->AppendL(iSortedChildren->At(count)->Id());
			}
		}
	CleanupStack::Pop(); // selection
	return selection;
	}

EXPORT_C CMsvEntrySelection* CMsvEntry::ChildrenWithMtmL(TUid aMtm) const
//
// Gets a selection containing the children of the context which use the same MTM
//
/** Gets a selection containing the IDs of all the context children filtered by 
MTM type. i.e. the index entry's iMtm field equals aMtm.

If the entry has no such children, the selection is empty.

The calling function is responsible for the deletion of the returned CMsvEntrySelection. 

@param aMtm MTM type by which to filter 
@leave KErrNoMemory Not enough memory to create the selection 
@return A selection containing the ID of all children of the context meeting 
the criterion */
	{
	__ASSERT_DEBUG(iState==EValid || iState==EInvalidDeletedContext || iState==EInvalidChangingContext, PanicServer(EMsvEntryStateUndetermined));
	CMsvEntrySelection* selection = new (ELeave) CMsvEntrySelection;
	CleanupStack::PushL(selection);
	TInt totalCount=iSortedChildren->Count();
	for (TInt count=0; count<totalCount; count++)
		{
		if (iSortedChildren->At(count)->iMtm==aMtm)
			{
			selection->AppendL(iSortedChildren->At(count)->Id());
			}
		}
	CleanupStack::Pop(); // selection
	return selection;
	}

EXPORT_C CMsvEntrySelection* CMsvEntry::ChildrenWithTypeL(TUid aType) const
//
// Gets a selection containing the children of the context which are the same type
//
/** Gets a selection containing the IDs of all the context children filtered by 
entry type. i.e. is the entry a folder, a message, etc.

If the entry has no such children, the selection is empty.

The calling function is responsible for the deletion of the returned CMsvEntrySelection. 


@param aType Entry type by which to filter. 
@leave KErrNoMemory Not enough memory to create the selection 
@return A selection containing the ID of all children of the context meeting 
the criterion */
	{
	__ASSERT_DEBUG(iState==EValid || iState==EInvalidDeletedContext || iState==EInvalidChangingContext, PanicServer(EMsvEntryStateUndetermined));

	CMsvEntrySelection* selection = new (ELeave) CMsvEntrySelection;
	CleanupStack::PushL(selection);
	TInt totalCount=iSortedChildren->Count();
	for (TInt count=0; count<totalCount; count++)
		{
		if (iSortedChildren->At(count)->iType==aType)
			{
			selection->AppendL(iSortedChildren->At(count)->Id());
			}
		}
	CleanupStack::Pop(); // selection
	return selection;
	}


#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
/**
 * ChildrenOfAvailableDrivesL()
 *
 * @param None.
 * @return CMsvEntrySelection List of child ids from all available drives.
 * @leave KErrArgument If the function is used for a TMsvId other than that 
 * of standard folders, i.e. Inbox, Outbox, Drafts, Sent or Deleted.
 * @leave KErrNoMemory Not enough memory to create the selection.
 * 
 * Gets a selection containing the child Id's from all drives currently present
 * in the server preferred drive list.
 * The function must be used only if the context is set to one of the standard folders,
 * i.e. Inbox, Outbox, Drafts, Sent or Deleted.
 * 
 * The calling function is responsible for the deletion of the returned CMsvEntrySelection.
 *
 @publishedAll
 @released
 */ 
EXPORT_C CMsvEntrySelection* CMsvEntry::ChildrenOfAvailableDrivesL() const
    {
    __ASSERT_DEBUG(iState==EValid || iState==EInvalidDeletedContext || iState==EInvalidChangingContext, PanicServer(EMsvEntryStateUndetermined));

    if(!iChildrenOfAvailableDrives)
        {
		User::Leave(KErrArgument);		
		}

    CMsvEntrySelection* selection = new (ELeave) CMsvEntrySelection;
	CleanupStack::PushL(selection);

    TInt totalCount = iSortedChildren->Count();
	for (TInt count=0; count<totalCount; count++)
		{
		selection->AppendL(iSortedChildren->At(count)->Id());
		}
	CleanupStack::Pop(); // selection
	return selection;
	}
#endif			// #if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)

	

EXPORT_C const TMsvEntry& CMsvEntry::operator[](TInt aIndex) const
//
// Returns the data for a child, zero index with the current sort order
//
/** Gets the index entry of the child at the position specified by the array index. 
The child entries of the context can be considered as a zero-based array, 
with entries sorted according to the current sort order. 

Note:

The function panics with E32USER-CBase 21 if aIndex was out of range.

@param aIndex Array index 
@return Index entry for the specified child. Valid for in-range values of aIndex. */
	{
	__ASSERT_DEBUG(iState==EValid || iState==EInvalidDeletedContext || iState==EInvalidChangingContext, PanicServer(EMsvEntryStateUndetermined));
	return *iSortedChildren->At(aIndex);
	}


EXPORT_C CMsvEntry* CMsvEntry::ChildEntryL(TMsvId aId) const
//
// Returns a new entry with the child as the context
//
/** Gets a new CMsvEntry object with its context set to the child entry ID. aMsvId 
must specify a child of the current context.

The CMsvEntry object must be deleted by the client application when it is 
no longer required. 

@param aId ID of a child entry 
@leave KErrNotFound aMsvId does not specify a child of the context 
@return CMsvEntry object with its context set to child entry */
	{
	__ASSERT_DEBUG(iState==EValid || iState==EInvalidDeletedContext || iState==EInvalidChangingContext, PanicServer(EMsvEntryStateUndetermined));
	if (!IsAChild(aId))
		User::Leave(KErrNotFound);
	return CMsvEntry::NewL(iMsvSession, aId, iOrdering);
	}


EXPORT_C const TMsvEntry& CMsvEntry::ChildDataL(TMsvId aId) const
//
// Returns the data for a child with the aId
//
/** Gets the index entry of context's child with the specified ID.

@param aId ID of the child 
@leave KErrNotFound No child exists with that ID 
@return Index entry for the specified child. Valid for in-range values of aIndex. */
	{
	__ASSERT_DEBUG(iState==EValid || iState==EInvalidDeletedContext || iState==EInvalidChangingContext, PanicServer(EMsvEntryStateUndetermined));

	TInt count=iSortedChildren->Count();
	while (count--)
		{
		if (iSortedChildren->At(count)->Id()==aId)
			break;
		}
	User::LeaveIfError(count); // will be -1 (KErrNotFound)
	return *iSortedChildren->At(count);
	}


EXPORT_C CMsvStore* CMsvEntry::ReadStoreL()
//
// Return store for the current context which is opened for read only
//
/** Obtains the message store for the current context with read-only access. 

Multiple clients can read from a store simultaneously. If another client is already 
writing to the store, the function leaves with KErrAccessDenied. 

The returned CMsvStore must be deleted when it is no longer required. 

@leave KErrNoMemory Not enough memory to open store 
@leave KErrAccessDenied Another client is currently writing to the store 
@leave KErrNotFound There is no store associated with this entry 
@return Context's message store open for read-only access */
	{
#ifndef _NO_SESSION_LOGGING_
	Log(_L("ReadStoreL for entry %x"), iEntryPtr->Id()); 
#endif

	__ASSERT_ALWAYS(iStore==NULL, PanicServer(EMsvStoreAlreadyOpen));
	__ASSERT_DEBUG(iState==EValid || iState==EInvalidDeletedContext || iState==EInvalidChangingContext, PanicServer(EMsvEntryStateUndetermined));
	User::LeaveIfError(iMsvSession.Session().ReadStore(iEntryPtr->Id()));

	// open the store
	TInt err =0;
#if (defined SYMBIAN_MESSAGESTORE_HEADER_BODY_USING_SQLDB)
		TRAP(err, iStore = CMsvStore::OpenForReadL(*this, iMsvSession.FileSession(), iMsvSession.StoreManager(), iEntryPtr->Id(), this->Entry().iMtm));	
#else
		TRAP(err, iStore = CMsvStore::OpenForReadL(*this, iMsvSession.FileSession(), iMsvSession.StoreManager(), iEntryPtr->Id()));			
#endif
	if (err != KErrNone)
		{
		iMsvSession.Session().DecStoreReaderCount(iEntryPtr->Id()); // error ignored
		User::Leave(err);
		}
	return iStore;
	}


EXPORT_C CMsvStore* CMsvEntry::EditStoreL()
//
// Return store for the current context which can be writen to
//
/** Gets the message store for the current context with read-write access. 

Only one client can edit a message store at one time. If another client is 
already writing to the store, KErrAccessDenied is returned. Other clients 
can be reading the store. 

If the message store does not exist when EditStore() is called, a new message 
store is created. 

The returned CMsvStore must be deleted when it is no longer required. 

@leave KErrAccessDenied Store is locked by another process or the entry is 
read only 
@leave KErrNoMemory Not enough memory to open the store 
@return Context's message store open for read-write access */
	{
#ifndef _NO_SESSION_LOGGING_
	Log(_L("EditStoreL for entry %x"), iEntryPtr->Id()); 
#endif

	__ASSERT_ALWAYS(iStore==NULL, PanicServer(EMsvStoreAlreadyOpen));
	__ASSERT_DEBUG(iState==EValid || iState==EInvalidDeletedContext || iState==EInvalidChangingContext, PanicServer(EMsvEntryStateUndetermined));
	
	if (iEntryPtr->ReadOnly())
		User::Leave(KErrAccessDenied);

	User::LeaveIfError(iMsvSession.Session().LockStore(iEntryPtr->Id()));

	// open the store
	TInt error = 0;
#if (defined SYMBIAN_MESSAGESTORE_HEADER_BODY_USING_SQLDB)	
		TRAP(error,  iStore = CMsvStore::OpenForWriteL(*this, iMsvSession.FileSession(), iMsvSession.StoreManager(), iEntryPtr->Id(), this->Entry().iMtm));
#else
		TRAP(error,  iStore = CMsvStore::OpenForWriteL(*this, iMsvSession.FileSession(), iMsvSession.StoreManager(), iEntryPtr->Id()));
#endif
	if (error)
		{
		iMsvSession.Session().ReleaseStore(iEntryPtr->Id()); // error ignored
		User::Leave(error);		
		}

	return iStore;
	}



void CMsvEntry::HandleStoreEvent(MMsvStoreObserver::TMsvStoreEvent aEvent, TMsvId /*aId*/)
//
//
//
	{
	switch (aEvent)
		{
		case EMsvEditStoreClosed:
			iMsvSession.Session().ReleaseStore(iEntryPtr->Id()); // error ignored
			iStore=NULL;
			break;
		case EMsvReadStoreClosed:
			iMsvSession.Session().DecStoreReaderCount(iEntryPtr->Id()); // error ignored
			iStore=NULL;
			break;
		default:
			__ASSERT_DEBUG(EFalse, PanicServer(EMsvUnknownStoreEvent3));
		}

	}


EXPORT_C CMsvOperation* CMsvEntry::MoveL(TMsvId aMsvId, TMsvId aTargetId, TRequestStatus& aStatus)
//
// Move a single child to another parent
//
/** Moves, asynchronously, a child of the context to become an entry owned by the target entry. 

All descendants will be moved as well. Any files and stores associated with 
the entry are also moved.

The returned CMsvOperation object completes when moving is complete.

@param aMsvId The ID of the entry to be moved 
@param aTargetId The ID of the entry to own the moved entries 
@param aStatus The request status to be completed when the operation has finished 

@leave KErrNoMemory The operation could not be created or passed to the server 
@leave KErrNotFound An entry was not a child of the context 
@return The operation object controlling the move command. */
	{
#ifndef _NO_SESSION_LOGGING_
	Log(_L("Asynchronous MoveL %x to %x"), aMsvId, aTargetId); 
#endif

	CMsvEntrySelection* selection = new (ELeave) CMsvEntrySelection;
	CleanupStack::PushL(selection);
	selection->AppendL(aMsvId);
	CMsvOperation* operation = MoveL(*selection, aTargetId, aStatus);
	CleanupStack::PopAndDestroy(); // selection
	return operation;
	}


TInt CMsvEntry::MoveOneL(TMsvId aMsvId, TMsvId aTargetId)
	{
	CMsvEntrySelection* selection = new(ELeave)CMsvEntrySelection;
	CleanupStack::PushL(selection);
	selection->AppendL(aMsvId);
	TInt opid = iMsvSession.OperationId();
	iMsvSession.Session().MoveEntriesL(*selection, aTargetId, opid);

	TMsvLocalOperationProgress progress;
	TPckg<TMsvLocalOperationProgress> progressPack(progress);
    User::LeaveIfError(iMsvSession.Session().OperationCompletion(opid, progressPack));
	
	if(progress.iError==KErrNone)
		{
		TMsvId id = selection->At(0);
		TInt ii = iSortedChildren->Count();
		while (ii--)
			{
			if (iSortedChildren->At(ii)->Id() == id)
				{
				iSortedChildren->Delete(ii);
				break;
				}
			}

		ii = iEntries->Count();
		while (ii--)
			{
			if (iEntries->At(ii)->Entry().Id() == id)
				{
				delete iEntries->At(ii);
				iEntries->Delete(ii);
				break;
				}
			}
		// Reset the owner flag
		if (Count() == 0)
			{
			TMsvEntry* ptr = CONST_CAST(TMsvEntry*, iEntryPtr);
			ptr->SetOwner(EFalse);
			}		
		}
	CleanupStack::PopAndDestroy(selection);
	return(progress.iError);
	}

/** Moves, synchronously, a child of the context to become an entry owned by the target entry. 

All descendants will be moved as well. Any files and stores associated with 
the entry are also moved.

@param aMsvId The ID of the entry to be moved 
@param aTargetId The ID of the entry to own the moved entries 

@leave KErrNoMemory 
@leave KErrNotFound An entry was not a child of the context 
*/
EXPORT_C void CMsvEntry::MoveL(TMsvId aMsvId, TMsvId aTargetId)
//
// Move a single child to another parent
//
	{
	__ASSERT_ALWAYS(iOwningService == KMsvLocalServiceIndexEntryId || iOwningService == KMsvRootIndexEntryId, PanicServer(EMsvNotLocalService)); // Root as well?
	__ASSERT_DEBUG(iState==EValid || iState==EInvalidDeletedContext || iState==EInvalidChangingContext, PanicServer(EMsvEntryStateUndetermined));
	__ASSERT_DEBUG(aTargetId!=iEntryPtr->Id(), PanicServer(EMsvMovingEntryToSameParent));

#ifndef _NO_SESSION_LOGGING_
	Log(_L("Synchronous MoveL %x to %x"), aMsvId, aTargetId); 
#endif
	User::LeaveIfError(MoveOneL(aMsvId,aTargetId));
	}


/** Moves, synchronously, children of the context to become entries owned by the target entry. 

All descendants will be moved as well. Any files and stores associated with 
the entries are also moved.

@param aSelection List of IDs of the entries to be moved 
@param aTargetId The ID of the entry to own the moved entires 
@param aProgress On return, records the outcome of the move 

@leave KErrNoMemory 
@leave KErrNotFound An entry was not a child of the context 
*/
EXPORT_C void CMsvEntry::MoveL(const CMsvEntrySelection& aSelection, TMsvId aTargetId, TMsvLocalOperationProgress& aProgress)
//
// Move a selection of children to another parent
//
	{
	__ASSERT_ALWAYS(iOwningService == KMsvLocalServiceIndexEntryId || iOwningService == KMsvRootIndexEntryId, PanicServer(EMsvNotLocalService)); // Root as well?
	__ASSERT_DEBUG(iState==EValid || iState==EInvalidDeletedContext || iState==EInvalidChangingContext, PanicServer(EMsvEntryStateUndetermined));
	__ASSERT_DEBUG(aTargetId!=iEntryPtr->Id(), PanicServer(EMsvMovingEntryToSameParent));
	__ASSERT_DEBUG(aSelection.Count(), PanicServer(EMsvEmptySelection));


#ifndef _NO_SESSION_LOGGING_
	Log(_L("Synchronous MoveL with selection of %d entries to %x"), aSelection.Count(),aTargetId); 
#endif

	aProgress.iTotalNumberOfEntries=aSelection.Count();
	aProgress.iNumberCompleted=0;
	aProgress.iNumberRemaining=aProgress.iTotalNumberOfEntries;
	aProgress.iError=KErrNone;

	TInt count=aProgress.iTotalNumberOfEntries;
	while(count--)
		{
		aProgress.iId=aSelection.At(count);
		TInt err=MoveOneL(aProgress.iId,aTargetId);
		aProgress.iNumberRemaining--;
		if(err==KErrNone)
			aProgress.iNumberCompleted++;
		else
			{
			aProgress.iError=err;
			aProgress.iNumberFailed++;
			}
		}
	}






EXPORT_C CMsvOperation* CMsvEntry::MoveL(const CMsvEntrySelection& aSelection, TMsvId aTargetId, TRequestStatus& aStatus)
//
// Move a selection of children to another parent
//
/** Moves, asynchronously, children of the context to become entries owned by the target entry. 

All descendants will be moved as well. Any files and stores associated with 
the entries are also moved.

The returned CMsvOperation object completes when moving is complete.

@param aSelection List of IDs of the entries to be moved 
@param aTargetId The ID of the entry to own the moved entires 
@param aStatus The request status to be completed when the operation has finished 

@leave KErrNoMemory The operation could not be created or passed to the server 
@leave KErrNotFound An entry was not a child of the context 
@return The operation object controlling the move command. */
	{
#ifndef _NO_SESSION_LOGGING_
	Log(_L("Asynchronous MoveL of selection of %d entries to %x"), aSelection.Count(), aTargetId); 
#endif

	__ASSERT_DEBUG(aTargetId!=iEntryPtr->Id(), PanicServer(EMsvMovingEntryToSameParent));
	__ASSERT_DEBUG(iState==EValid || iState==EInvalidDeletedContext || iState==EInvalidChangingContext, PanicServer(EMsvEntryStateUndetermined));
	if (!AreChildren(aSelection))
		User::Leave(KErrNotFound);
	CMsvEntryOperation* operation = CMsvEntryOperation::NewL(iMsvSession, aStatus);
	CleanupStack::PushL(operation);
	User::LeaveIfError(iMsvSession.Session().OperationMtmL(aSelection.At(0), aTargetId, operation->iMtm, operation->iService));

#if defined(_DEBUG)
	// check other entries in selection are consistent and are not read only
	TInt dCount = aSelection.Count();
	while (dCount--)
		{
		TMsvId service;
		TUid mtm;
		TInt error = iMsvSession.Session().OperationMtmL(aSelection.At(dCount), aTargetId, mtm, service);
		__ASSERT_DEBUG(error==KErrNone || error==KErrNotFound, PanicServer(EMsvMulitpleMtmsForMoveCommand));
		__ASSERT_DEBUG(mtm==operation->iMtm, PanicServer(EMsvMulitpleMtmsForMoveCommand));
		__ASSERT_DEBUG(service==operation->iService, PanicServer(EMsvMulitpleMtmsForMoveCommand));
		TMsvEntry dEntry;
		TMsvId dService;
		if (iMsvSession.Session().GetEntry(aSelection.At(dCount), dService, dEntry)==KErrNone)
			__ASSERT_DEBUG(dService==iOwningService || (iEntryPtr->iType==KUidMsvServiceEntry && dService==iEntryPtr->Id()) || (iEntryPtr->iType==KUidMsvRootEntry && service==aSelection.At(dCount)), PanicServer(EMsvMovingEntryDifferentOwningService));
		}
#endif

	iMsvSession.Session().MoveEntriesL(aSelection, aTargetId, operation->Id(), operation->iStatus);
	operation->Start();
	CleanupStack::Pop(); // operation
	return operation;
	}


EXPORT_C CMsvOperation* CMsvEntry::CopyL(const CMsvEntrySelection& aSelection, TMsvId aTargetId, TRequestStatus& aStatus)
//
// Copy a selection ocf children to another parent
//
/** Creates, asynchronously. copies of children of the context as new entries owned by the specified 
target ID.

All descendants will be copied as well. Any files and stores associated with 
the entries are also copied.

The returned CMsvOperation object completes when copying is complete.

@param aSelection List of IDs of the entries to be copied 
@param aTargetId The ID of the entry to own the copies 
@param aStatus The request status to be completed when the operation has finished 

@leave KErrNoMemory The operation could not be created or passed to the server 
@leave KErrNotFound An entry was not a child of the context 
@return The operation object controlling the copy command. */
	{
#ifndef _NO_SESSION_LOGGING_
	Log(_L("Asynchronous CopyL of selection of %d entries to %x"), aSelection.Count(), aTargetId); 
#endif

	__ASSERT_DEBUG(iState==EValid || iState==EInvalidDeletedContext || iState==EInvalidChangingContext, PanicServer(EMsvEntryStateUndetermined));
	if (!AreChildren(aSelection))
		User::Leave(KErrNotFound);
	CMsvEntryOperation* operation = CMsvEntryOperation::NewL(iMsvSession, aStatus);
	CleanupStack::PushL(operation);
	User::LeaveIfError(iMsvSession.Session().OperationMtmL(aSelection.At(0), aTargetId, operation->iMtm, operation->iService)); 

#if defined(_DEBUG)
	// check other entries in selection are consistent 
	TInt dCount = aSelection.Count();
	while (dCount--)
		{
		TMsvId service;
		TUid mtm;
		TInt error = iMsvSession.Session().OperationMtmL(aSelection.At(dCount), aTargetId, mtm, service);
		__ASSERT_DEBUG(error==KErrNone || error==KErrNotFound, PanicServer(EMsvMulitpleMtmsForCopyCommand));
		__ASSERT_DEBUG(mtm==operation->iMtm, PanicServer(EMsvMulitpleMtmsForCopyCommand));
		__ASSERT_DEBUG(service==operation->iService, PanicServer(EMsvMulitpleMtmsForCopyCommand));
		TMsvEntry dEntry;
		TMsvId dService;
		if (iMsvSession.Session().GetEntry(aSelection.At(dCount), dService, dEntry)==KErrNone)
			__ASSERT_DEBUG(dService==iOwningService || (iEntryPtr->iType==KUidMsvServiceEntry && dService==iEntryPtr->Id()) || (iEntryPtr->iType==KUidMsvRootEntry && service==aSelection.At(dCount)), PanicServer(EMsvCopyingEntryDifferentOwningService));
		}
#endif

	iMsvSession.Session().CopyEntriesL(aSelection, aTargetId, operation->Id(), operation->iStatus);
	operation->Start();
	CleanupStack::Pop(); // operation
	return operation;
	}

EXPORT_C CMsvOperation* CMsvEntry::CopyL(TMsvId aMsvId, TMsvId aTargetId, TRequestStatus& aStatus)
//
// Copy a single entry to another parent
//
/** Creates, asynchronously, a copy of a child of the context as a new entry owned by the specified 
target ID.

All descendants will be copied as well. Any files and stores associated with 
the entry are also copied.

The returned CMsvOperation object completes when copying is complete.

@param aMsvId The ID of the entry to be copied 
@param aTargetId The ID of the entry to own the copy 
@param aStatus The request status to be completed when the operation has finished 

@leave KErrNoMemory The operation could not be created or passed to the server 
@leave KErrNotFound An entry was not a child of the context 
@return The operation object controlling the copy command. */
	{
#ifndef _NO_SESSION_LOGGING_
	Log(_L("Asynchronous CopyL of entry %x to %x"), aMsvId, aTargetId); 
#endif

	CMsvEntrySelection* selection = new (ELeave) CMsvEntrySelection;
	CleanupStack::PushL(selection);
	selection->AppendL(aMsvId);
	CMsvOperation* operation = CopyL(*selection, aTargetId, aStatus);
	CleanupStack::PopAndDestroy(); // selection
	return operation;
	}


TInt CMsvEntry::CopyOneL(TMsvId aMsvId, TMsvId aTargetId)
	{

	CMsvEntryArray* newSortedChildren = NULL;
	CMsvClientEntry *toadd=NULL;

	
	if(aTargetId==iEntryPtr->Id())
		{
		const TMsvEntry &entry=ChildDataL(aMsvId);
		if (entry.Visible() || iOrdering.ShowInvisibleEntries())
			{
			// Create what may be the new child entry if nothing goes wrong
			toadd = CMsvClientEntry::NewLC(entry, EMsvClientChild);

			// Reserve space for the new entry for later
			iEntries->SetReserveL(iEntries->Count() + 1);

			newSortedChildren = CMsvEntryArray::NewLC(*iMtmList);

			// add the children to the sorted pointer list - current context is the first entry
			TInt totalCount=iEntries->Count();
			for (TInt count=1; count<totalCount; count++)
				newSortedChildren->AppendL(&iEntries->At(count)->Entry());

			// We've created a new sorted child list now so we won't leave later
			newSortedChildren->AppendL(&toadd->Entry());
			newSortedChildren->SortL(iOrdering);
			}
		}

	CMsvEntrySelection* selection = new(ELeave)CMsvEntrySelection;
	CleanupStack::PushL(selection);
	selection->AppendL(aMsvId);
	TInt opid = iMsvSession.OperationId();
	iMsvSession.Session().CopyEntriesL(*selection, aTargetId, opid);

	TMsvLocalOperationProgress progress;
	TPckg<TMsvLocalOperationProgress> progressPack(progress);
    User::LeaveIfError(iMsvSession.Session().OperationCompletion(opid, progressPack));

	CleanupStack::PopAndDestroy(selection);

	// can't leave after this point
	if(newSortedChildren!=NULL) CleanupStack::Pop(newSortedChildren);
	if(toadd!=NULL) CleanupStack::Pop(toadd);
	if(newSortedChildren!=NULL && progress.iError==KErrNone)
		{
		delete iSortedChildren;
		iSortedChildren=newSortedChildren;
		newSortedChildren=NULL;
		toadd->SetId(progress.iId);
		// Will not leave because we've reserved space earlier
		iEntries->AppendL(toadd);
		toadd=NULL;
		}
	delete newSortedChildren;
	delete toadd;
	return(progress.iError);
	}

EXPORT_C void CMsvEntry::CopyL(TMsvId aMsvId, TMsvId aTargetId)
//
// Copy a single child to another parent (or duplicate)
//
/** Creates, synchronously, a copy of a child of the context as a new entry owned by the specified target ID.

@param aMsvId The ID of the entry to be copied
@param aTargetId The ID of the entry to own the copy
*/
	{	
	__ASSERT_ALWAYS(iOwningService == KMsvLocalServiceIndexEntryId || iOwningService == KMsvRootIndexEntryId, PanicServer(EMsvNotLocalService)); // Root as well?
	__ASSERT_DEBUG(iState==EValid || iState==EInvalidDeletedContext || iState==EInvalidChangingContext, PanicServer(EMsvEntryStateUndetermined));
#ifndef _NO_SESSION_LOGGING_
	Log(_L("Synchronous CopyL %x to %x"), aMsvId, aTargetId); 
#endif
	User::LeaveIfError(CopyOneL(aMsvId,aTargetId));
	}


/** Creates, synchronously. copies of children of the context as new entries owned by the specified 
target ID.

All descendants will be copied as well. Any files and stores associated with 
the entries are also copied.

@param aSelection List of IDs of the entries to be copied 
@param aTargetId The ID of the entry to own the copies 
@param aProgress On return, records the outcome of the copy 

@leave KErrNoMemory 
@leave KErrNotFound An entry was not a child of the context 
*/
EXPORT_C void CMsvEntry::CopyL(const CMsvEntrySelection& aSelection, TMsvId aTargetId, TMsvLocalOperationProgress& aProgress)
//
// Copy a selection to another parent (or duplicate)
//
	{
	__ASSERT_ALWAYS(iOwningService == KMsvLocalServiceIndexEntryId || iOwningService == KMsvRootIndexEntryId, PanicServer(EMsvNotLocalService)); // Root as well?
	__ASSERT_DEBUG(iState==EValid || iState==EInvalidDeletedContext || iState==EInvalidChangingContext, PanicServer(EMsvEntryStateUndetermined));
	__ASSERT_DEBUG(aSelection.Count(), PanicServer(EMsvEmptySelection));

#ifndef _NO_SESSION_LOGGING_
	Log(_L("Synchronous CopyL with selection of %d entries to %x"), aSelection.Count(),aTargetId); 
#endif


	aProgress.iTotalNumberOfEntries=aSelection.Count();
	aProgress.iNumberCompleted=0;
	aProgress.iNumberRemaining=aProgress.iTotalNumberOfEntries;
	aProgress.iError=KErrNone;

	TInt count=aProgress.iTotalNumberOfEntries;
	while(count--)
		{
		aProgress.iId=aSelection.At(count);
		TInt err=CopyOneL(aProgress.iId,aTargetId);
		aProgress.iNumberRemaining--;
		if(err==KErrNone)
			aProgress.iNumberCompleted++;
		else
			{
			aProgress.iError=err;
			aProgress.iNumberFailed++;
			}
		}
	}





EXPORT_C void CMsvEntry::AddObserverL(MMsvEntryObserver& aObserver)
//
// Adds an observer to this entry
// If the function leaves, the observer was not appended
// 
/** Registers an observer for the object. 

CMsvEntry objects can call back observer objects that implement the MMsvEntryObserver 
interface when certain events occur. Any number of observers can be registered.

Observers are called primarily when the context changes state or contents. 
For details, see MMsvEntryObserver::TMsvEntryEvent.

@param aObserver The observer to be registered for events 
@leave KErrNoMemory Not enough memory to register the observer */
	{
	if (iObservers==NULL)
		iObservers=new(ELeave) CArrayPtrFlat<MMsvEntryObserver> (KMsvEntryObserverArrayGranuality);
	iObservers->AppendL(&aObserver);

#ifndef _NO_SESSION_LOGGING_
	Log(_L("Observer %d added"), iObservers->Count()); 
#endif
	}


EXPORT_C void CMsvEntry::RemoveObserver(MMsvEntryObserver& aObserver)
//
// Removes an observer of the entry
//
/** Unregisters an observer previously registered with AddObserverL(). 

@param aObserver A reference to an observer to be unregistered for events */
	{
	__ASSERT_DEBUG(iObservers, PanicServer(EMsvEntryUnknownObserver));
	if (iObservers)
		{
		TInt count=iObservers->Count();
		while (count--)
			{
			if (iObservers->At(count)==&aObserver)
				{
#ifndef _NO_SESSION_LOGGING_
				Log(_L("Observer %d removed"), count + 1); 
#endif
				iObservers->Delete(count);
				if (iObservers->Count()==0)
					{
					delete iObservers;
					iObservers=NULL;
					}
				return;
				}
			}
		__ASSERT_DEBUG(count>=0, PanicServer(EMsvEntryUnknownObserver));
		}
	}


void CMsvEntry::NotifyAllObserversL(MMsvEntryObserver::TMsvEntryEvent aEvent, TAny* aArg1, TAny* aArg2, TAny* aArg3)
//
// Notifies all observers of the event affecting the context
//
	{
	if (iObservers==NULL)
		return;
	TInt count=iObservers->Count();
	while (count--)
		iObservers->At(count)->HandleEntryEventL(aEvent,aArg1,aArg2,aArg3);	
	}


void CMsvEntry::HandleSessionEventL(TMsvSessionEvent aEvent, TAny* aArg1, TAny* aArg2, TAny* aArg3)
//
// Is informed of all the session events
// This are filtered to find the events relating to the context
//
	{
	// Check the notification sequence and ignore if neccessary
	if (iNotifySequence >= iMsvSession.iNotifySequence)
		{
#ifndef _NO_SESSION_LOGGING_
		Log(_L("Ignoring notification, %d >= %d"), iNotifySequence, iMsvSession.iNotifySequence); 
#endif
		return;
		}

	switch (aEvent)
		{
		case EMsvEntriesChanged:
			{
			CMsvEntrySelection* selection = (CMsvEntrySelection*) aArg1;
			if (iEntryPtr->Id() == selection->At(0))
				ContextChangedL(MMsvEntryObserver::EMsvEntryChanged);
			else if (iEntryPtr->Id()==*(TMsvId*) aArg2)
				ChildrenChangedL(*selection);
			break;
			}
		case EMsvEntriesCreated:
			if (*(TMsvId*) aArg2==iEntryPtr->Id())
				NewChildrenL(*(CMsvEntrySelection*) aArg1);
			else
				CheckNewGrandchildrenL(*(TMsvId*) aArg2);
			break;
		case EMsvEntriesDeleted:
			{
			CMsvEntrySelection* selection = (CMsvEntrySelection*) aArg1;
			if (*(TMsvId*) aArg2==iEntryPtr->Id())
				DeletedChildrenL(*selection);
			else
				{
				// check if we have been deleted
				TInt index=selection->Find(iEntryPtr->Id());
				if (index!=KErrNotFound)
					{
					iState = EInvalidDeletedContext;
					NotifyAllObserversL(MMsvEntryObserver::EMsvEntryDeleted, NULL, NULL, NULL);
					}
				else
					CheckDeletedGrandchildrenL(*(TMsvId*) aArg2);
				}
			break;
			}
		case EMsvEntriesMoved:
			if (*(TMsvId*) aArg3==iEntryPtr->Parent())
				CheckIfContextMovedL(*(CMsvEntrySelection*) aArg1);
			if (*(TMsvId*) aArg2==iEntryPtr->Id())
				NewChildrenL(*(CMsvEntrySelection*) aArg1);
			else if (*(TMsvId*) aArg3==iEntryPtr->Id())
				DeletedChildrenL(*(CMsvEntrySelection*) aArg1);
			else
				{
				CheckNewGrandchildrenL(*(TMsvId*) aArg2);
				CheckDeletedGrandchildrenL(*(TMsvId*) aArg3);
				}
			break;
		case EMsvMediaChanged:
			{
			TRAPD(error, HandleMediaChangeL());			
			if (error)
				{
				// An error occurred or this is a non standard entry
				// in which case the media has changed so this entry is not accessible
				// Just mark the entry as invalid - there is nothing else we can do!
				iState = EInvalidOldContext;
				NotifyAllObserversL(MMsvEntryObserver::EMsvContextInvalid, (TAny*)&error, NULL, NULL);
				}
			break;
			}
#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
		case EMsvRefreshMessageView:
			{
			// A drive/disk has been added/removed.
			if(iEntryPtr->StandardFolder())
				{
				SetEntryNoCheckL(iEntryPtr->Id(), iChildrenOfAvailableDrives);
				}
			break;
			}
#endif
		default:
			break;
		}
	}

void CMsvEntry::HandleMediaChangeL()
	{
	// If this is not a standard entry there is nothing we can do
	if (!iEntryPtr->StandardFolder())
		User::Leave(KMsvMediaChanged);

	// This is a standard folder so it will exist on all media
	// Refresh the entry and child list - if this fails mark the entry as invalid
	// Otherwise the context will be told that everything has changed
	CMsvEntrySelection* oldChildren = new(ELeave)CMsvEntrySelection;
	CleanupStack::PushL(oldChildren);

	// Get list of old children
	TInt count = iSortedChildren->Count();
	while(count--)
		oldChildren->AppendL(iSortedChildren->At(count)->Id());

	// Refresh the context
	iState = EInvalidOldContext;

#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
	if(iChildrenOfAvailableDrives)
		{
		SetStandardFolderEntryL(iEntryPtr->Id());
		}
	else
#endif
		{
		SetEntryL(iEntryPtr->Id());
		}
	CMsvEntrySelection* newChildren = new(ELeave)CMsvEntrySelection;
	CleanupStack::PushL(newChildren);

	// Get list of new children
	count = iSortedChildren->Count();
	while(count--)
		newChildren->AppendL(iSortedChildren->At(count)->Id());

	// Tell the context about the children that have effectively been deleted and created
	if (oldChildren->Count())
		NotifyAllObserversL(MMsvEntryObserver::EMsvDeletedChildren, (TAny*)oldChildren, NULL, NULL);
	if (newChildren->Count())
		NotifyAllObserversL(MMsvEntryObserver::EMsvNewChildren, (TAny*)newChildren, NULL, NULL);

	// Tell the context that it might have changed
	NotifyAllObserversL(MMsvEntryObserver::EMsvEntryChanged, NULL, NULL, NULL);

	CleanupStack::PopAndDestroy(2); // newChildren, oldChildren
	}

void CMsvEntry::CheckIfContextMovedL(const CMsvEntrySelection& aSelection)
//
// Some of the contexts parents children have moved, have to check if the context was one of them
//
	{
	TInt index=aSelection.Find(iEntryPtr->Id());
	if (index!=KErrNotFound)
		ContextChangedL(MMsvEntryObserver::EMsvEntryMoved);
	}


void CMsvEntry::ContextChangedL(MMsvEntryObserver::TMsvEntryEvent aEvent)
//
// The context has ben changed, spo we need to get the enw version
//
	{
#ifndef _NO_SESSION_LOGGING_
	Log(_L("Context changed")); 
#endif

	if (iEntries->Count()>1 && iEntries->At(1)->Type()==EMsvClientChangedContext)
		{
		// we changed the entry, so get the new conext from the entry list
		// delete the old entry context (the new one is at position 1)
		delete iEntries->At(0);
		iEntries->Delete(0);
		}
	else
		{
		// someone else changed the context, so we have to update
		CMsvClientEntry* cEntry=NULL;
		TMsvId owningService=KMsvNullIndexEntryId;
		TRAPD(error, {cEntry = DoGetEntryLC(iEntryPtr->Id(), owningService); CleanupStack::Pop();});
		if (error==KErrNone)
			{
			if(iEntries->Count() != 0)
				{
				delete iEntries->At(0);
				iEntries->At(0) = cEntry;
				}
			else
				{
				iEntries->AppendL(cEntry); 	
				}
			iOwningService = owningService;
			}
		else
			{
			iState = EInvalidOldContext;
			NotifyAllObserversL(MMsvEntryObserver::EMsvContextInvalid, (TAny*)&error, NULL, NULL);
			return;
			}
		}

	iEntries->At(0)->SetType(EMsvClientContext);
	iEntryPtr = &iEntries->At(0)->Entry();
	iState = EValid;

	// notify all observers
	NotifyAllObserversL(aEvent, NULL, NULL, NULL);
	}



void CMsvEntry::NewChildrenL(const CMsvEntrySelection& aSelection)
//
// New children have been created
//
	{
#ifndef _NO_SESSION_LOGGING_
	Log(_L("New children")); 
#endif

	CMsvEntrySelection* newChildren = DoGetNewChildrenL(aSelection);
	CleanupStack::PushL(newChildren);
	if (newChildren->Count())
		{
		TMsvEntry* ptr = CONST_CAST(TMsvEntry*, iEntryPtr);
		ptr->SetOwner(ETrue);
		NotifyAllObserversL(MMsvEntryObserver::EMsvNewChildren, (TAny*)newChildren, NULL, NULL);
		}
	CleanupStack::PopAndDestroy(); // newChildren
	}


CMsvEntrySelection* CMsvEntry::DoGetNewChildrenL(const CMsvEntrySelection& aSelection)
//
// New children have been created
//
	{
	CMsvEntrySelection* newChildren = aSelection.CopyLC();
	TInt count=aSelection.Count();
	while (count--)
		{
		// check if we already have this child
		if (IsAChild(aSelection.At(count)))
			{
			newChildren->Delete(count);
			continue;
			}

		// get the new child data and add to to the list
		TMsvEntry tEntry;
		TMsvId service;
		TInt error=iMsvSession.Session().GetEntry(aSelection.At(count), service, tEntry);
		if(error!=KErrNotFound) User::LeaveIfError(error);
		
		if (error == KErrNone && (tEntry.Visible() || iOrdering.ShowInvisibleEntries()))
			{
			__ASSERT_DEBUG(service==iOwningService || (iEntryPtr->iType==KUidMsvServiceEntry && service==iEntryPtr->Id()) || (iEntryPtr->iType==KUidMsvRootEntry && service==(aSelection.At(count))), PanicServer(EMsvNewChildDifferentOwningService));

			CMsvClientEntry* cEntry = CMsvClientEntry::NewLC(tEntry, EMsvClientNull);
			cEntry->SetType(EMsvClientChild);
			iEntries->AppendL(cEntry);
			CleanupStack::Pop(); // cEntry
			// resort the children
			iSortedChildren->AppendL(&cEntry->Entry());
			}
		else
			newChildren->Delete(count);
		}
	
	if (newChildren->Count())
		iSortedChildren->SortL(iOrdering);
	CleanupStack::Pop(); // newChildren
	return newChildren;
	}


void CMsvEntry::CheckNewGrandchildrenL(TMsvId aId)
//
//
//
	{
	TInt count=iEntries->Count();
	while (count--)
		{
		if (iEntries->At(count)->Entry().Id()==aId && !iEntries->At(count)->Entry().Owner())
			{
			iEntries->At(count)->SetOwnerFlag(ETrue);
			NotifyChildChangedL(aId);
			break;
			}
		}
	}


void CMsvEntry::CheckDeletedGrandchildrenL(TMsvId aId)
//
//
//
	{
	TInt count=iEntries->Count();
	while (count--)
		{
		if (iEntries->At(count)->Entry().Id()==aId)
			{
			TMsvEntry entry;
			TMsvId service;
			TInt error = iMsvSession.Session().GetEntry(aId, service, entry);
			__ASSERT_DEBUG(error || service==iOwningService || (iEntryPtr->iType==KUidMsvServiceEntry && service==iEntryPtr->Id()) || (iEntryPtr->iType==KUidMsvRootEntry && service==aId), PanicServer(EMsvDeletedGrandChildDifferentOwningService));
			if (error)
				NotifyAllObserversL(MMsvEntryObserver::EMsvChildrenInvalid, (TAny*)&error, NULL, NULL);
			else if (!entry.Owner())
				{
				iEntries->At(count)->SetOwnerFlag(EFalse);
				NotifyChildChangedL(aId);
				}
			break;
			}
		}
	}


void CMsvEntry::NotifyChildChangedL(TMsvId aId)
//
//
//
	{
	CMsvEntrySelection* selection = DoMakeSelectionL(aId);
	CleanupStack::PushL(selection);
	NotifyAllObserversL(MMsvEntryObserver::EMsvChildrenChanged, (TAny*)selection, NULL, NULL);
	CleanupStack::PopAndDestroy(); // selection
	}


CMsvEntrySelection* CMsvEntry::DoMakeSelectionL(TMsvId aId)
//
//
//
	{
	CMsvEntrySelection* selection = new (ELeave) CMsvEntrySelection;
	CleanupStack::PushL(selection);
	selection->AppendL(aId);
	CleanupStack::Pop(); // selection
	return selection;
	}


void CMsvEntry::ChildrenChangedL(const CMsvEntrySelection& aSelection)
//
// Children have been changed
//
	{
#ifndef _NO_SESSION_LOGGING_
	Log(_L("Children changed")); 
#endif

	CMsvEntrySelection* changedChildren = new(ELeave) CMsvEntrySelection;
	CleanupStack::PushL(changedChildren);
	CMsvEntrySelection* newChildren = new(ELeave) CMsvEntrySelection;
	CleanupStack::PushL(newChildren);
	CMsvEntrySelection* deletedChildren = new(ELeave) CMsvEntrySelection;
	CleanupStack::PushL(deletedChildren);

	TInt count=aSelection.Count();
	while (count--)
		{
		// get the changed child data
		TMsvId id = aSelection.At(count);
		TMsvEntry tEntry;
		TMsvId service;

		// find the child in the sorted list
		TInt pos=iSortedChildren->Count();
		while (pos--)
			if (iSortedChildren->At(pos)->Id()==id)
				break;

		TInt error = iMsvSession.Session().GetEntry(id, service, tEntry);
		if (error == KErrNotFound)
			{
			if (pos >= 0)
				{
				// The child has been deleted by the server
				DeleteChild(pos);
				deletedChildren->AppendL(id);
				}
			}
		else
			{
			User::LeaveIfError(error);
			__ASSERT_DEBUG(service==iOwningService || (iEntryPtr->iType==KUidMsvServiceEntry && service==iEntryPtr->Id()) || (iEntryPtr->iType==KUidMsvRootEntry && service==id), PanicServer(EMsvChangedChildHasDifferentOwningService));

			if ( pos!=KErrNotFound  )
				{
				// replace it, if showing all children or its was and still is visible
				if (iOrdering.ShowInvisibleEntries() || tEntry.Visible())
					{
					ReplaceChildL(pos, tEntry);
					changedChildren->AppendL(id);
					continue;
					}
				}

			if ( pos==KErrNotFound )
				{
				if (tEntry.Visible())
					{
					// the child has just been made visible so add it to our sorted list
					CMsvClientEntry* cEntry = CMsvClientEntry::NewLC(tEntry, EMsvClientChild);
					iEntries->AppendL(cEntry);
					CleanupStack::Pop(); // cEntry
					iSortedChildren->AppendL(&cEntry->Entry());
					newChildren->AppendL(id);		
					}				
				}
			else if (!tEntry.Visible())
				{
				DeleteChild(pos);
				deletedChildren->AppendL(id);
				}
			}
		}
	
	// resort the children
	if (changedChildren->Count() || newChildren->Count() || deletedChildren->Count())
		{
		iSortedChildren->SortL(iOrdering);
		// notify the observers
		if (changedChildren->Count())
			NotifyAllObserversL(MMsvEntryObserver::EMsvChildrenChanged, (TAny*)changedChildren, NULL, NULL);
		if (newChildren->Count())
			NotifyAllObserversL(MMsvEntryObserver::EMsvNewChildren, (TAny*)newChildren, NULL, NULL);
		if (deletedChildren->Count())
			NotifyAllObserversL(MMsvEntryObserver::EMsvDeletedChildren, (TAny*)deletedChildren, NULL, NULL);
		}

	CleanupStack::PopAndDestroy(3); // changedChildren, newChildren, deletedChildren
	}

void CMsvEntry::DeleteChild(TInt aPosition)
	{
	TMsvId id = iSortedChildren->At(aPosition)->Id();
	iSortedChildren->Delete(aPosition);
	TInt ii=iEntries->Count();
	while (ii--)
		if (iEntries->At(ii)->Entry().Id()==id)
			{
			__ASSERT_DEBUG(iEntries->At(ii)->Type()==EMsvClientChild, PanicServer(EMsvNonChildDeleted));
			delete iEntries->At(ii);
			iEntries->Delete(ii);
			break;
			}
	__ASSERT_DEBUG(ii!=KErrNotFound, PanicServer(EMsvChangedChildNotFound1));
	}

void CMsvEntry::ReplaceChildL(TInt aPosition, const TMsvEntry& aEntry)
//
//
//
	{
	CMsvClientEntry* cEntry = CMsvClientEntry::NewLC(aEntry, EMsvClientChild);
			
	TInt ii=iEntries->Count();
	while (ii--)
		{
		if (iEntries->At(ii)->Entry().Id()==aEntry.Id())
			{
			__ASSERT_DEBUG(iEntries->At(ii)->Type()==EMsvClientChild, PanicServer(EMsvNonChildDeleted));
			delete iEntries->At(ii);
			iEntries->At(ii) = cEntry;
			iSortedChildren->At(aPosition) = &cEntry->Entry();
			break;
			}
		}
	__ASSERT_DEBUG(ii!=KErrNotFound, PanicServer(EMsvChangedChildNotFound1));

	CleanupStack::Pop(); // cEntry
	}



void CMsvEntry::DeletedChildrenL(const CMsvEntrySelection& aSelection)
//
// Some of the children have been deleted
//
	{
#ifndef _NO_SESSION_LOGGING_
	Log(_L("Deleted children")); 
#endif

	CMsvEntrySelection* deletedChildren = aSelection.CopyL();
	CleanupStack::PushL(deletedChildren);

	TInt count=aSelection.Count();
	while (count--)
		{
		TMsvId id = aSelection.At(count);
		TInt ii=iSortedChildren->Count();
		while (ii--)
			{
			if (iSortedChildren->At(ii)->Id()==id)
				{
				iSortedChildren->Delete(ii);
				break;
				}
			}
		if (ii==KErrNotFound)
			deletedChildren->Delete(count);
		else
			{
			ii=iEntries->Count();
			while (ii--)
				{
				if (iEntries->At(ii)->Entry().Id()==id)
					{
					__ASSERT_DEBUG(iEntries->At(ii)->Type()==EMsvClientChild, PanicServer(EMsvNonChildDeleted));
					delete iEntries->At(ii);
					iEntries->Delete(ii);
					break;
					}
				__ASSERT_DEBUG(ii!=KErrNotFound, PanicServer(EMsvDeletedChildNotInMainList));
				}
			}
		}

	// notify all observers
	if (deletedChildren->Count())
		{
		// reset the owner flag
		if (Count()==0)
			{
			TMsvEntry* ptr = CONST_CAST(TMsvEntry*, iEntryPtr);
			ptr->SetOwner(EFalse);
			}

		NotifyAllObserversL(MMsvEntryObserver::EMsvDeletedChildren, (TAny*)deletedChildren, NULL, NULL);
		}

	CleanupStack::PopAndDestroy(); // deletedChildren
	}


CMsvEntryArray* CMsvEntry::GetNewSortedListL(const TMsvSelectionOrdering& aOrdering, const CArrayFix<TUid>& aMtmList)
//
// Gets a new sorted list for new order and mtm list
// The entries should have the correct visiblity
//
	{
	CMsvEntryArray* newSortedChildren = CMsvEntryArray::NewLC(aMtmList);
	if (iSortedChildren->Count())
		{
//		newSortedChildren->InsertL(0, &iSortedChildren->At(0), iSortedChildren->Count());
//		newSortedChildren->SortL(aOrdering);
		TInt count=1;
		if (iEntries->At(count)->Type()!=EMsvClientChild)
			count++;
		TInt totalCount=iEntries->Count();
		for (; count<totalCount; count++)
			newSortedChildren->AppendL(&iEntries->At(count)->Entry());
		newSortedChildren->SortL(aOrdering);
		}
	CleanupStack::Pop(); // newSortedChildren
	return newSortedChildren;
	}


EXPORT_C void CMsvEntry::SetSortTypeL(const TMsvSelectionOrdering& aOrdering)
//
// Sets the sort type
// If this leaves the sort type has not been changed
//
/** Sets the sort order that is used when listing children, for example with ChildrenL().

If the function leaves, the sort order is unchanged.

@param aOrdering Sort order to use 
@leave KErrNoMemory Insufficient memory to resort the entries */
	{
	__ASSERT_DEBUG(iState==EValid || iState==EInvalidDeletedContext || iState==EInvalidChangingContext, PanicServer(EMsvEntryStateUndetermined));

#ifndef _NO_SESSION_LOGGING_
		Log(_L("Sort order changed, sorting %x, invisible %d"), aOrdering.Sorting(), aOrdering.ShowInvisibleEntries()); 
#endif

	if (aOrdering.ShowInvisibleEntries()==iOrdering.ShowInvisibleEntries())
		{
		// just resort the current list of children
		CMsvEntryArray* newSortedChildren = GetNewSortedListL(aOrdering, *iMtmList);
		delete iSortedChildren;
		iSortedChildren = newSortedChildren;
		iOrdering = aOrdering;
		}
	else if (!aOrdering.ShowInvisibleEntries())
		{
		// resort the current list
		CMsvEntryArray* newSortedChildren = GetNewSortedListL(aOrdering, *iMtmList);
		delete iSortedChildren;
		iSortedChildren = newSortedChildren;
		iOrdering = aOrdering;
		// remove the invisible entries from sort list
		TInt count = iSortedChildren->Count();
		while (count--)
			if (!iSortedChildren->At(count)->Visible())
				iSortedChildren->Delete(count);
		// remove the invisible children from main list
		count = iEntries->Count();
		while (count-->=1)
			if (!iEntries->At(count)->Entry().Visible())
			{
			delete iEntries->At(count);
			iEntries->Delete(count);
			}
		}
	else
		{
		// keep old variable
		CArrayPtrFlat<CMsvClientEntry>* oldEntries = iEntries;
		iEntries=NULL;
		CMsvEntryArray* oldSortedChildren = iSortedChildren;
		iSortedChildren=NULL;
		TMsvSelectionOrdering oldOrder = iOrdering;	
		
		iOrdering = aOrdering;
		TRAPD(leave, DoSortTypeL(oldEntries->At(0)));

		if (leave)
			{
			// we left, the function may have created a new iEntries,
			// if iEntries has been created we need to delete all the elements
			// except the first one, that is used in the old list
			// then delete iEntries, and put back the old one.

			if(iEntries!=NULL)
				{
				if(iEntries->Count()!=0) 
					iEntries->Delete(0);
				iEntries->ResetAndDestroy();
				delete iEntries;
				}
			iEntries = oldEntries;

			// iSortedChildren doesn't own the children so just delete the new one.
			// and put the old one back.
			delete iSortedChildren;
			iSortedChildren = oldSortedChildren;
			iOrdering = oldOrder;
			User::Leave(leave);
			}
		else
			{
			oldEntries->Delete(0); // the object is used in new list
			oldEntries->ResetAndDestroy();
			delete oldEntries;
			delete oldSortedChildren;
			}
		}
	}



void CMsvEntry::DoSortTypeL(CMsvClientEntry* aContext)
//
// 
//
	{
	iEntries = new(ELeave) CArrayPtrFlat<CMsvClientEntry>(KMsvClientEntryArrayGranuality);
	iEntries->AppendL(aContext);

	iSortedChildren = CMsvEntryArray::NewL(*iMtmList);

	DoGetChildrenL();
	}



EXPORT_C void CMsvEntry::SetMtmListL(const CArrayFix<TUid>& aMtmList)
//
// Sets the mtm list
// If this leaves the mtm list has not been changed
//
/** Sets the MTM order to the specified sort order. When children of an entry are 
sorted, entries belonging to the same MTM type can be grouped together. 

MTM grouping can be switched on or off through setting the appropriate TMsvSelectionOrdering 
value by SetSortTypeL(). 

If the function leaves, the sort order is unchanged.

@param aMtmList The order of MTMs to use for sorting 
@leave KErrNoMemory Insufficient memory to resort the entries */
	{
	__ASSERT_DEBUG(iState==EValid || iState==EInvalidDeletedContext || iState==EInvalidChangingContext, PanicServer(EMsvEntryStateUndetermined));

	// create new mtm list
	CArrayFixFlat<TUid>* mtmList = new(ELeave)CArrayFixFlat<TUid>(KMsvMtmListGranularity);
	CleanupStack::PushL(mtmList);
	mtmList->InsertL(0, &aMtmList.At(0), aMtmList.Count());

	// create new sorted children list
	CMsvEntryArray* newSortedChildren = GetNewSortedListL(iOrdering, *mtmList);
	
	// install the new sorted children array
	delete iSortedChildren;
	delete iMtmList;
	iSortedChildren = newSortedChildren;
	iMtmList = mtmList;
	
	CleanupStack::Pop(); // mtmList
	}


TBool CMsvEntry::IsAChild(TMsvId aId) const
//
// Returns true if the entry is a child
//
	{
	TInt count=iSortedChildren->Count();
	while (count--)
		{
		if (iSortedChildren->At(count)->Id()==aId)
			return ETrue;
		}
	return EFalse;
	}

TBool CMsvEntry::AreChildren(const CMsvEntrySelection& aSelection) const
//
// Returns true if all the entries are children
//
	{
	TInt count = aSelection.Count();
	if(!count)
	    {
        return EFalse;
	    }
	else
	    {
        while (count--)
            {
            if (!IsAChild(aSelection.At(count)))
                {
                return EFalse;
                }
            }
	    }
	return ETrue;
	}


EXPORT_C TBool CMsvEntry::HasStoreL() const
/** Checks if the context has an associated message store.

@return ETrue: entry has a message store EFalse: entry does not have a message 
store */
	{
#if (defined SYMBIAN_MESSAGESTORE_HEADER_BODY_USING_SQLDB)
	if(iMsvSession.StoreManager().DoesAnyStoreExists(iEntryPtr->Id(), this->Entry().iMtm))
		return ETrue;
	else
		return iMsvSession.StoreManager().FileStoreExistsL(iEntryPtr->Id());
#else
	return iMsvSession.StoreManager().FileStoreExistsL(iEntryPtr->Id());
#endif
	}

/** Sets or clears multiple fields in a selection of children of the context.

Fields to change are specified using a bitmask of TMsvAttribute values. Possible 
fields that can be changed using this function are the PC synchronisation, Visibility, 
Pending Deletion, Read, In-preparation, Connected, and New flags. 

@param aSelection The entries to change
@param aSetAttributes A bitmask of the fields to set
@param aClearAttributes A bitmask of the fields to clear
@leave KErrNotFound An entry was not a child of the context
@see CMsvSession::ChangeAttributesL() 
*/
EXPORT_C void CMsvEntry::ChangeAttributesL(const CMsvEntrySelection& aSelection, TUint aSetAttributes, TUint aClearAttributes)
	{
#ifndef _NO_SESSION_LOGGING_
	Log(_L("ChangeAttributesL")); 
#endif

	__ASSERT_DEBUG(iState==EValid || iState==EInvalidDeletedContext || iState==EInvalidChangingContext, PanicServer(EMsvEntryStateUndetermined));
	__ASSERT_DEBUG(aSelection.Count(), PanicServer(EMsvEmptySelection));

	if (!AreChildren(aSelection))
		User::Leave(KErrNotFound);

	iMsvSession.Session().ChangeAttributesL(aSelection, aSetAttributes, aClearAttributes);
	}

