/*
* Copyright (c) 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:
*
*/
// INCLUDE FILES
#include <xcfwtree.h>
#include <xcfwengine.h>
#include <pathinfo.h>
#include <f32file.h>
#include <sysutil.h>
#include "menueng.h"
#include "menuengobjectfactoryproxy.h"
#include "menuengobject.h"
#include "menuengobserver.h"
#include "menuengoperation.h"
#include "menuengfilter.h"
#include "menuengvisitor.h"
#include "menuengitemlister.h"
#include "menuengdeletionchecker.h"
#include "menuengidmanagerinit.h"
#include "menuengidsetter.h"
#include "menuengidmanager.h"
#include "menuengidcollector.h"
#include "menuengflags.h"
#include "mcsmenuitem.h" // for flags
// CONSTANTS
LOCAL_D const TInt KDriveAndColon = 2; //drive letter and colon, e.g. "c:"
_LIT( KMenuTypeIdSeed, "menu:id_seed" );
_LIT( KMenuAttrIdSeed, "seed" );
_LIT( KMenuContentDirName, "content\\" );
_LIT( KMenuContentExtension, ".xml" );
_LIT( KMenuTempExtension, ".$$$" );
// ================= LOCAL FUNCTIONS =======================
/**
* Insert string into other string, checking vaailable space.
* Leaves with KErrOverflow if aSoure cannot be inserted to aTarget.
* @param aTarget Target descriptor.
* @param aPos Insertion point.
* @param aSource Source descriptor.
*/
LOCAL_C void InsertL( TDes& aTarget, TInt aPos, const TDesC& aSource )
{
if ( aTarget.MaxLength() < aTarget.Length() + aSource.Length() )
{
User::Leave( KErrOverflow );
}
aTarget.Insert( aPos, aSource );
}
/**
* Append string to the end of other string, checking available space.
* Leaves with KErrOverflow if aSource cannot be appended to aTarget.
* @param aTarget Target descriptor.
* @param aSource Source descriptor.
*/
LOCAL_C void AppendL( TDes& aTarget, const TDesC& aSource )
{
if ( aTarget.MaxLength() < aTarget.Length() + aSource.Length() )
{
User::Leave( KErrOverflow );
}
aTarget.Append( aSource );
}
// ================= MEMBER FUNCTIONS =======================
// ---------------------------------------------------------
// CMenuEng::Object
// ---------------------------------------------------------
//
CMenuEngObject& CMenuEng::Object( MXCFWNode& aNode ) const
{
return (CMenuEngObject&)(*aNode.Data());
}
// ---------------------------------------------------------
// CMenuEng::~CMenuEng
// ---------------------------------------------------------
//
EXPORT_C CMenuEng::~CMenuEng()
{
Cancel();
CancelAllOperations();
iOperations.Close();
iNotifyQueue.Close();
delete iEngine;
delete iTree;
delete iObjectFactory;
delete iIdManager;
iFs.Close();
iName.Close();
iTempFileName.Close();
iRamFileName.Close();
delete iActiveWait;
}
// ---------------------------------------------------------
// CMenuEng::NewL
// ---------------------------------------------------------
//
EXPORT_C CMenuEng* CMenuEng::NewL
( const TDesC& aName, MMenuEngObserver& aObserver )
{
CMenuEng* eng = new (ELeave) CMenuEng( aObserver );
CleanupStack::PushL( eng );
eng->ConstructL( aName );
CleanupStack::Pop( eng );
return eng;
}
// ---------------------------------------------------------
// CMenuEng::CMenuEng
// ---------------------------------------------------------
//
CMenuEng::CMenuEng( MMenuEngObserver& aObserver )
: CActive( CActive::EPriorityStandard ), iObserver( aObserver )
{
CActiveScheduler::Add( this );
iLegacyFormat = EFalse;
}
// ---------------------------------------------------------
// CMenuEng::ConstructL
// ---------------------------------------------------------
//
void CMenuEng::ConstructL( const TDesC& aName )
{
ValidateNameL( aName );
iName.CreateL( aName );
User::LeaveIfError( iFs.Connect() );
// Keep temp file and the RAM file name as members -
// we use these all the time.
TFileName fname;
GetFileNameL( fname, ETempFile );
iTempFileName.CreateL( fname );
GetFileNameL( fname, ERamFile );
iRamFileName.CreateL( fname );
iObjectFactory = CMenuEngObjectFactoryProxy::NewL( *this );
iEngine = CXCFWEngine::NewL( this );
iEngine->RegisterObjectFactoryL( iObjectFactory );
SelfComplete( KErrNone ); // Start processing asynchronously.
ActiveWaitForFileLoadL(); //we need to wait until parsing is complete
}
// ---------------------------------------------------------
// CMenuEng::QueueOperationL
// ---------------------------------------------------------
//
EXPORT_C void CMenuEng::QueueOperationL( MMenuEngOperation& aOperation )
{
__ASSERT_DEBUG( KErrNotFound == iOperations.Find( &aOperation ), \
User::Invariant() );
iOperations.AppendL( &aOperation );
// If idle, kick back into life.
if ( EReady == iState )
{
iState = EExecuteOp;
SelfComplete( KErrNone );
}
}
// ---------------------------------------------------------
// CMenuEng::DequeueOperation
// ---------------------------------------------------------
//
EXPORT_C void CMenuEng::DequeueOperation( MMenuEngOperation& aOperation )
{
if ( iCurrentOperation == &aOperation )
{
iCurrentOperation = NULL;
}
TInt i = iOperations.Find( &aOperation );
if ( KErrNotFound != i )
{
iOperations.Remove( i );
}
}
// ---------------------------------------------------------
// CMenuEng::TraverseFolderL
// ---------------------------------------------------------
//
EXPORT_C void CMenuEng::TraverseFolderL
( TInt aFolder, MMenuEngVisitor& aVisitor ) const
{
if ( !iTree )
{
User::Leave( KErrNotReady );
}
TraverseNodeL( FolderNodeL( aFolder ), aVisitor );
}
// ---------------------------------------------------------
// CMenuEng::NewObjectL
// ---------------------------------------------------------
//
EXPORT_C CMenuEngObject* CMenuEng::NewObjectL( const TDesC& aType )
{
if ( !iIdManager )
{
User::Leave( KErrNotReady );
}
CMenuEngObject* object = CMenuEngObject::NewL( *this, aType );
CleanupStack::PushL( object );
TInt id;
iIdManager->AllocL( id );
object->SetId( id );
CleanupStack::Pop( object );
return object;
}
// ---------------------------------------------------------
// CMenuEng::RootFolderL
// ---------------------------------------------------------
//
EXPORT_C void CMenuEng::RootFolderL( TInt& aId ) const
{
if ( !iTree )
{
User::Leave( KErrNotReady );
}
aId = iRoot;
}
// ---------------------------------------------------------
// CMenuEng::ParentFolderL
// ---------------------------------------------------------
//
EXPORT_C void CMenuEng::ParentFolderL( TInt aId, TInt& aParentId ) const
{
if ( !iTree )
{
User::Leave( KErrNotReady );
}
TInt parentId = 0;
if ( aId != iRoot )
{
// We may have nodes above the root, but do not allow access to those.
// Say 0 to the root's parent.
MXCFWNode* parentNode = NodeL( aId ).Parent();
if ( parentNode )
{
parentId = Object( *parentNode ).Id();
}
}
aParentId = parentId;
}
// ---------------------------------------------------------
// CMenuEng::GetItemL
// ---------------------------------------------------------
//
EXPORT_C void CMenuEng::GetItemL( TInt aId, TMenuItem& aItem ) const
{
if ( !iTree )
{
User::Leave( KErrNotReady );
}
const CMenuEngObject& object = ObjectL( aId );
aItem.SetId( object.Id() );
aItem.SetFlags( object.Flags() );
aItem.SetType( object.Type() );
TInt parent;
ParentFolderL( aId, parent );
aItem.SetParent( parent );
}
// ---------------------------------------------------------
// CMenuEng::GetItemsL
// ---------------------------------------------------------
//
EXPORT_C void CMenuEng::GetItemsL(
RArray<TMenuItem>& aItemArray,
TInt aFolder,
const MMenuEngFilter* aFilter,
TBool aRecursive ) const
{
if ( !iTree )
{
User::Leave( KErrNotReady );
}
TMenuEngItemLister lister
( *this, aItemArray, aFolder, aFilter, aRecursive );
TraverseFolderL( aFolder, lister );
}
// ---------------------------------------------------------
// CMenuEng::ObjectL
// ---------------------------------------------------------
//
EXPORT_C const CMenuEngObject& CMenuEng::ObjectL( TInt aId ) const
{
if ( !iTree )
{
User::Leave( KErrNotReady );
}
return Object( NodeL( aId ) );
}
// ---------------------------------------------------------
// CMenuEng::GetLegacyFormat
// ---------------------------------------------------------
//
EXPORT_C TBool CMenuEng::GetOnceLegacyFormat()
{
TBool format = iLegacyFormat;
iLegacyFormat = EFalse;
return format;
}
// ---------------------------------------------------------
// CMenuEng::RemoveL
// ---------------------------------------------------------
//
EXPORT_C void CMenuEng::RemoveL( TInt aId )
{
if ( SysUtil::DiskSpaceBelowCriticalLevelL( &iFs, 0, EDriveC) )
{
User::Leave( KErrDiskFull );
}
if ( iState != EExecuteOp )
{
User::Leave( KErrLocked );
}
// Check if aId exists.
MXCFWNode& node = NodeL( aId );
// Can't delete the root.
if ( aId == iRoot )
{
User::Leave( KErrAccessDenied );
}
// Can't delete read-only items, or folders containing any read-only items.
TMenuEngDeletionChecker checker;
TraverseNodeL( node, checker );
// Get the IDs of items to be deleted.
RArray<TInt> deletedIds;
CleanupClosePushL( deletedIds );
TMenuEngIdCollector idCollector( deletedIds );
TraverseNodeL( node, idCollector );
// Add notification event.
MXCFWNode* parent = node.Parent();
__ASSERT_DEBUG( parent, User::Invariant() );
AppendNotifyL( Object( *parent ).Id(),
RMenuNotifier::EItemsAddedRemoved );
// Delete node.
iTree->RemoveNodeL( &node );
iChanged = ETrue;
// Deletion successful, remove the IDs.
for ( TInt i = 0; i < deletedIds.Count(); i++ )
{
iIdManager->Remove( deletedIds[i] );
}
CleanupStack::PopAndDestroy( &deletedIds );
}
// ---------------------------------------------------------
// CMenuEng::MoveToFolderL
// ---------------------------------------------------------
//
EXPORT_C void CMenuEng::MoveToFolderL
( RArray<TInt>& aItems, TInt aFolder, TInt aMoveBefore )
{
if ( SysUtil::DiskSpaceBelowCriticalLevelL( &iFs, 0, EDriveC) )
{
User::Leave( KErrDiskFull );
}
if ( iState != EExecuteOp )
{
User::Leave( KErrLocked );
}
TInt i;
TInt id;
MXCFWNode* fromFolder = NULL;
// Check that toFolder exists.
MXCFWNode& toFolder = FolderNodeL( aFolder );
CMenuEngObject& toFolderObj = Object( toFolder );
// Check that we can move items into target toFolder.
if ( toFolderObj.Flags() & TMenuItem::ELockMoveInto )
{
User::Leave( KErrAccessDenied );
}
// Check that all items exist and they are not locked against moving.
// Also check that and they are in the same toFolder.
if ( aItems.Count() )
{
fromFolder = NodeL( aItems[0] ).Parent();
for ( i = 0; i< aItems.Count(); i++ )
{
id = aItems[i];
if ( id == iRoot )
{
User::Leave( KErrAccessDenied ); // Can't move the root.
}
MXCFWNode& node = NodeL( id );
if ( Object( node ).Flags() & TMenuItem::ELockItem )
{
User::Leave( KErrAccessDenied ); // lock_item
}
if ( node.Parent() != fromFolder )
{
User::Leave( KErrArgument ); // Items from different folders.
}
}
// Add notification events. Presume that there will be no recursion.
// If there is recursion, then we make an unnecessary notify, that is
// better than missing one.
__ASSERT_DEBUG( fromFolder, User::Invariant() );
AppendNotifyL( Object( *fromFolder ).Id(),
RMenuNotifier::EItemsAddedRemoved );
AppendNotifyL( toFolderObj.Id(),
RMenuNotifier::EItemsAddedRemoved );
// Do move the items.
// Recursion check is done by the XML tree (->KErrArgument).
MXCFWNode* moveBefore = Child( toFolder, aMoveBefore );
for ( i = 0; i < aItems.Count(); i++ )
{
iTree->MoveNodeL( &NodeL( aItems[i] ), &toFolder, moveBefore );
}
iChanged = ETrue;
}
}
// ---------------------------------------------------------
// CMenuEng::ReorderL
// ---------------------------------------------------------
//
EXPORT_C void CMenuEng::ReorderL( TInt aId, TInt aMoveBefore )
{
if ( SysUtil::DiskSpaceBelowCriticalLevelL( &iFs, 0, EDriveC) )
{
User::Leave( KErrDiskFull );
}
if ( iState != EExecuteOp )
{
User::Leave( KErrLocked );
}
// Can't reorder the root.
if ( aId == iRoot )
{
User::Leave( KErrArgument );
}
// Check if aId exists.
MXCFWNode& node = NodeL( aId );
// Check if item can be reordered.
if ( Object( node ).Flags() & TMenuItem::ELockItem )
{
User::Leave( KErrAccessDenied ); // lock_item
}
MXCFWNode* parent = node.Parent();
__ASSERT_DEBUG( parent, User::Invariant() );
if ( parent )
{
// Add notification event.
AppendNotifyL( Object( *parent ).Id(),
RMenuNotifier::EItemsReordered );
// Move it.
MXCFWNode* moveBefore = Child( *parent, aMoveBefore );
iTree->MoveNodeL( &node, parent, moveBefore );
iChanged = ETrue;
}
}
// ---------------------------------------------------------
// CMenuEng::AddL
// ---------------------------------------------------------
//
EXPORT_C void CMenuEng::AddL
( CMenuEngObject& aObject, TInt aFolder, TInt aInsertBefore )
{
if ( SysUtil::DiskSpaceBelowCriticalLevelL( &iFs, 0, EDriveC) )
{
User::Leave( KErrDiskFull );
}
if ( &aObject.Engine() != this )
{
// ID integrity requires that objects are bound to engines.
User::Leave( KErrArgument );
}
if ( iState != EExecuteOp )
{
User::Leave( KErrLocked );
}
if ( KMenuTypeData() == aObject.Type() )
{
// Internal type, disallow.
User::Leave( KErrArgument );
}
// Check if aFolder exists.
MXCFWNode& folder = FolderNodeL( aFolder );
// Check if folder is locked.
if ( Object( folder ).Flags() & TMenuItem::ELockMoveInto )
{
User::Leave( KErrAccessDenied ); // lock_moveinto
}
// Add notification event.
AppendNotifyL( aFolder, RMenuNotifier::EItemsAddedRemoved );
// Add the node.
MXCFWNode* insertBefore = Child( folder, aInsertBefore );
if ( insertBefore )
{
iTree->AddNodeL( &aObject, &folder, insertBefore );
// No leaving after this point - ownership of aObject taken by the
// tree. (Well, almost -- CXCFWTree is buggy, but hopefully it will
// be fixed.)
}
else
{
// CXCFWTree API design flaw:
// AddNodeL( &aObject, &folder, NULL ) leaves with KErrArgument!
iTree->AddNodeL( &aObject, &folder );
// No leaving after this point - ownership of aObject taken by the
// tree. (Well, almost -- CXCFWTree is buggy, but hopefully it will
// be fixed.)
}
iChanged = ETrue;
}
// ---------------------------------------------------------
// CMenuEng::ModifiableObjectL
// ---------------------------------------------------------
//
EXPORT_C CMenuEngObject& CMenuEng::ModifiableObjectL( TInt aId , TInt aEvent)
{
if ( !iTree )
{
User::Leave( KErrNotReady );
}
if ( iState != EExecuteOp )
{
User::Leave( KErrLocked );
}
MXCFWNode& node = NodeL( aId );
CMenuEngObject& object = Object( node );
// Add notification event.
MXCFWNode* parent = node.Parent();
__ASSERT_DEBUG( parent, User::Invariant() );
if( aEvent != RMenuNotifier::EItemsNone )
{
AppendNotifyL( Object( *parent ).Id(), aEvent );
}
iChanged = ETrue; // Might not be true, but how could we know?
// Note1: We must presume that this object will actually be changed
// because we haven't any information what is being done to it!
// If the object had a back-pointer to the engine, this could
// be solved, but that would be a BAD THING to do.
// Note2: other possible solution is to drop this method and invent
// something else to allow item data update.
return object;
}
// ---------------------------------------------------------
// CMenuEng::AppendNotifyL
// ---------------------------------------------------------
//
EXPORT_C void CMenuEng::AppendNotifyL( TInt aFolder, TInt aEvents )
{
for ( TInt i = 0; i < iNotifyQueue.Count(); i++ )
{
if ( iNotifyQueue[i].iFolder == aFolder )
{
iNotifyQueue[i].iEvents |= aEvents;
return;
}
}
iNotifyQueue.AppendL( TMenuEngNotify( aFolder, aEvents ) );
}
// ---------------------------------------------------------
// CMenuEng::NodeL
// ---------------------------------------------------------
//
EXPORT_C TBool CMenuEng::Exist( TInt aId ) const
{
MXCFWNode* node = NULL;
const RNodeArray& nodes = iTree->Nodes();
for ( TInt i = 0; i < nodes.Count(); i++ )
{
node = nodes[i];
if ( aId == Object( *node ).Id() )
{
return ETrue;
}
}
return EFalse;
}
// ---------------------------------------------------------
// CMenuEng::RunL
// ---------------------------------------------------------
//
void CMenuEng::RunL()
{
switch( iState )
{
case ENull:
{
// Self-completion in ConstructL(). Load RAM tree.
__ASSERT_DEBUG( !iStatus.Int(), User::Invariant() );
iState = ELoadRamFile;
LoadRamFileL();
break;
}
case ELoadRamFile:
{
if ( iStatus.Int() )
{
// Error loading RAM tree -> Try ROM tree.
iObserver.EngineTreeReloaded();
iState = ELoadRomFile;
LoadRomFileL();
}
else
{
// Tree loaded OK. Start processing operations.
iState = EExecuteOp;
ExecuteOperationL();
}
break;
}
case ELoadRomFile:
{
// Error loading ROM tree is fatal. Nothing we can do.
User::LeaveIfError( iStatus.Int() );
iState = ESaveFile;
SaveTempFileL();
break;
}
case ESaveFile:
{
// Error saving tree is fatal. Nothing we can do.
User::LeaveIfError( iStatus.Int() );
// Tree saved to temp file OK. Replace content file with temp file.
ReplaceRamFileL();
// Saving completed succesfully.
iChanged = EFalse;
// If there is a current operation, this is the final result.
CompleteCurrentOperation( iStatus.Int() );
// Start next operation.
iState = EExecuteOp;
ExecuteOperationL();
break;
}
case EExecuteOp:
{
// Start next operation.
__ASSERT_DEBUG( !iStatus.Int(), User::Invariant() );
ExecuteOperationL();
break;
}
case EReady:
case EDead:
default:
{
User::Invariant();
}
}
}
// ---------------------------------------------------------
// CMenuEng::DoCancel
// ---------------------------------------------------------
//
void CMenuEng::DoCancel()
{
// We don't have real requests, only self-completion; which is already
// completed.
}
// ---------------------------------------------------------
// CMenuEng::RunError
// ---------------------------------------------------------
//
TInt CMenuEng::RunError( TInt aError )
{
switch( iState )
{
case ENull:
{
User::Invariant(); // Self-completion cannot fail.
break;
}
case ELoadRamFile:
{
// Error loading RAM tree -> keep going (try ROM tree).
SelfComplete( aError );
break;
}
case ELoadRomFile:
{
// Error loading ROM tree is fatal. Nothing we can do.
// stop nested active scheduler loop if error in parsing
if ( iActiveWait->IsStarted() )
{
iActiveWait->AsyncStop();
}
iState = EDead;
iObserver.EngineError( aError );
// Can't delete the tree now, XCFW Engine keeps a pointer to it
// and still uses it even after reporting the error. :(
break;
}
case ESaveFile:
{
// File is too big and cannot be saved, delete it
// and try reading from rom
if (iStatus.Int() == KErrNoMemory)
{
iState = ELoadRamFile;
SelfComplete( aError );
}
else {
// Error saving tree is fatal. Nothing we can do.
// If there is a current operation, this is the final result.
CompleteCurrentOperation( aError );
iState = EDead;
iObserver.EngineError( aError );
}
// Can't delete the tree now, XCFW Engine keeps a pointer to it
// and still uses it even after reporting the error. :(
break;
}
case EExecuteOp:
{
// Current operation failed.
CompleteCurrentOperation( aError );
// Continue with next operation.
SelfComplete( KErrNone );
break;
}
case EReady:
case EDead:
default:
{
User::Invariant();
}
}
return KErrNone;
}
// ---------------------------------------------------------
// CMenuEng::HandleEngineEventL
// ---------------------------------------------------------
//
void CMenuEng::HandleEngineEventL( TXCFWEngineEvent aEvent )
{
switch ( aEvent )
{
case EEvtParsingComplete:
{
__ASSERT_DEBUG( iTree, User::Invariant() );
// parsing operation complete
// we can stop nested active scheduler loop
if ( iActiveWait->IsStarted() )
{
iActiveWait->AsyncStop();
}
TBool legacyFormat = iObjectFactory->IsLegacyFormat();
// Reset object factory in all cases.
iObjectFactory->Reset();
// Tree is up (maybe unsaved yet).
// Check the structure and get the root folder node.
MXCFWNode& rootNode = CheckTreeL( *iTree );
// Init ID manager, check and fix ID-s.
InitIdManagerL( rootNode );
// Now we have ID for all (including the root).
iRoot = Object( rootNode ).Id();
// This is the earliest point to do an ID sanity check.
__ASSERT_DEBUG( DebugSanityCheck( *iTree ), User::Invariant() );
// Send a "wildcard" notification so everybody reloads.
iObserver.EngineEvents( 0, RMenuNotifier::EItemsAddedRemoved );
iLegacyFormat = legacyFormat;
SelfComplete( KErrNone ); // Go to RunL().
break;
}
case EEvtSavingComplete:
{
//iLegacyFormat = EFalse;
SelfComplete( KErrNone ); // Go to RunL().
break;
}
case EEvtParsingCanceled:
case EEvtSavingCanceled:
{
// Reset object factory in all cases.
iObjectFactory->Reset();
SelfComplete( KErrCancel ); // Go to RunL().
break;
}
case EEvtParsingStarted:
case EEvtSavingStarted:
case EEvtNull:
default:
{
break;
}
}
}
// ---------------------------------------------------------
// CMenuEng::HandleEngineErrorL
// ---------------------------------------------------------
//
void CMenuEng::HandleEngineErrorL( TInt aErrorCode )
{
// Reset object factory in all cases.
iObjectFactory->Reset();
SelfComplete( aErrorCode ); // Go to RunL().
}
// ---------------------------------------------------------
// CMenuEng::CheckTreeL
// ---------------------------------------------------------
//
MXCFWNode& CMenuEng::CheckTreeL( MXCFWTree& aTree ) const
{
MXCFWNode* rootFolder = NULL;
MXCFWNode* treeRoot = aTree.Root();
if ( treeRoot )
{
// The tree has nodes.
if ( KMenuTypeData() == Object( *treeRoot ).Type() )
{
// Root node is "menu:data"
RNodeArray nodes;
CleanupClosePushL( nodes );
aTree.GetNodesOfTypeL( KMenuTypeFolder(), nodes,
treeRoot, EFalse );
if ( 1 == nodes.Count() )
{
// Exactly 1 "menu:folder" in the root.
// This is the root folder.
rootFolder = nodes[0];
}
CleanupStack::PopAndDestroy( &nodes );
}
}
if ( !rootFolder )
{
User::Leave( KErrCorrupt );
}
return *rootFolder;
}
// ---------------------------------------------------------
// CMenuEng::DebugSanityCheck
// ---------------------------------------------------------
//
TBool CMenuEng::DebugSanityCheck( MXCFWTree& aTree ) const
{
const RNodeArray& nodes = aTree.Nodes();
for ( TInt i = 0; i < nodes.Count(); i++ )
{
if ( !DebugSanityCheck( *nodes[i] ) )
{
return EFalse;
}
}
return ETrue;
}
// ---------------------------------------------------------
// CMenuEng::ActiveWaitForLoadFileL
// ---------------------------------------------------------
//
void CMenuEng::ActiveWaitForFileLoadL()
{
iActiveWait = new( ELeave ) CActiveSchedulerWait();
if ( !iActiveWait->IsStarted() )
{
iActiveWait->Start();
}
//now we check if file was properly loaded
TInt root(0);
RootFolderL( root );
if( !root && ( iState != EDead ) )
{
//there was problem with Ram file(probably file was empty), lets load Rom file
iState = ELoadRamFile;
if ( !iActiveWait->IsStarted() )
{
iActiveWait->Start();
}
}
}
// ---------------------------------------------------------
// CMenuEng::DebugSanityCheck
// ---------------------------------------------------------
//
TBool CMenuEng::DebugSanityCheck( MXCFWNode& aNode ) const
{
__ASSERT_DEBUG( iRoot, User::Invariant() );
// Sanity check, for node location and ID.
// - Nodes under the root folder (==items) should have non-0 ID.
// - Nodes not under the root folder (==other stuff like id_seed) should
// always have 0 id.
// This is crucial for the ID space integrity. XCFW node lookup uses
// a flat list of nodes; the only way we can differentiate items from
// non-items is the ID.
MXCFWNode* node;
for ( node = &aNode; node; node = node->Parent() )
{
if ( Object( *node ).Id() == iRoot )
{
// The node is, or descendant of, the root folder. Must have an ID.
return 0 != Object( aNode ).Id();
}
}
// Node is not under the root folder. Must not have an ID.
return 0 == Object( aNode ).Id();
}
// ---------------------------------------------------------
// CMenuEng::NodeL
// ---------------------------------------------------------
//
MXCFWNode& CMenuEng::NodeL( TInt aId ) const
{
__ASSERT_DEBUG( iTree, User::Invariant() );
if ( !aId )
{
// We can actually have 0 id node (outside the item tree), but
// those are not items.
User::Leave( KErrNotFound );
}
MXCFWNode* node = NULL;
const RNodeArray& nodes = iTree->Nodes();
for ( TInt i = 0; i < nodes.Count(); i++ )
{
node = nodes[i];
if ( aId == Object( *node ).Id() )
{
__ASSERT_DEBUG( DebugSanityCheck( *node ), User::Invariant() );
return *node;
}
}
User::Leave( KErrNotFound );
/* NOTREACHED */
return *node;
}
// ---------------------------------------------------------
// CMenuEng::FolderNodeL
// ---------------------------------------------------------
//
MXCFWNode& CMenuEng::FolderNodeL( TInt aId ) const
{
__ASSERT_DEBUG( iTree, User::Invariant() );
MXCFWNode& node = NodeL( aId );
if ( KMenuTypeFolder() == Object( node ).Type() )
{
return node;
}
User::Leave( KErrNotFound );
/* NOTREACHED */
return node;
}
// ---------------------------------------------------------
// CMenuEng::Child
// ---------------------------------------------------------
//
MXCFWNode* CMenuEng::Child( MXCFWNode& aParent, TInt aId )
{
MXCFWNode* node;
for ( node = aParent.FirstChild(); node; node = node->NextSibling() )
{
if ( Object( *node ).Id() == aId )
{
break;
}
}
return node;
}
// ---------------------------------------------------------
// CMenuEng::CancelAllOperations
// ---------------------------------------------------------
//
void CMenuEng::CancelAllOperations()
{
// For safety, we remove pointers before completion.
// (OperationCompleted might want to remove operation too, avoid
// conflict.)
MMenuEngOperation* operation;
if ( iCurrentOperation )
{
operation = iCurrentOperation;
iCurrentOperation = NULL;
operation->CompletedMenuEngOperation( KErrCancel );
}
while ( iOperations.Count() )
{
operation = iOperations[0];
iOperations.Remove( 0 );
operation->CompletedMenuEngOperation( KErrCancel );
}
}
// ---------------------------------------------------------
// CMenuEng::ValidateNameL
// ---------------------------------------------------------
//
void CMenuEng::ValidateNameL( const TDesC& aName )
{
// We only accept alphanumeric characters and underscore as content
// file name. This is stricter than the (current) file system
// restriction, for future-proofing reasons.
if ( !aName.Length() )
{
User::Leave( KErrArgument );
}
for ( TInt i = 0; i < aName.Length(); i++ )
{
TChar c = aName[i];
if ( !c.IsAlphaDigit() && '_' != c )
{
User::Leave( KErrArgument );
}
}
}
// ---------------------------------------------------------
// CMenuEng::GetFileNameL
// ---------------------------------------------------------
//
void CMenuEng::GetFileNameL( TFileName& aFname, TFile aSelector )
{
User::LeaveIfError( iFs.PrivatePath( aFname ) );
if ( ERomFile == aSelector )
{
InsertL(
aFname,
0,
PathInfo::RomRootPath().Left( KDriveAndColon ) );
}
else
{
InsertL(
aFname,
0,
PathInfo::PhoneMemoryRootPath().Left( KDriveAndColon ) );
}
AppendL( aFname, KMenuContentDirName );
AppendL( aFname, iName );
AppendL( aFname, ETempFile == aSelector ?
KMenuTempExtension : KMenuContentExtension );
}
// ---------------------------------------------------------
// CMenuEng::TraverseNodeL
// ---------------------------------------------------------
//
TBool CMenuEng::TraverseNodeL
( MXCFWNode& aNode, MMenuEngVisitor& aVisitor ) const
{
CMenuEngObject& object = Object( aNode );
if ( KMenuTypeFolder() == object.Type() )
{
if ( aVisitor.VisitEnterL( object ) )
{
// Recursion is used - menu data tree is not very deep.
MXCFWNode* node = aNode.FirstChild();
while( node && TraverseNodeL( *node, aVisitor ) )
{
node = node->NextSibling();
}
}
// countChildren( object );
return aVisitor.VisitLeaveL( object );
}
else
{
return aVisitor.VisitL( object );
}
}
// ---------------------------------------------------------
// CMenuEng::InitIdManagerL
// ---------------------------------------------------------
//
void CMenuEng::InitIdManagerL( MXCFWNode& aRootNode )
{
__ASSERT_DEBUG( !iIdManager, User::Invariant() );
__ASSERT_DEBUG( iTree, User::Invariant() );
iIdManager = new (ELeave) CMenuEngIdManager();
TInt idSeed;
GetIdSeedL( idSeed );
iIdManager->SetSeed( idSeed );
// Read ID-s to ID manager.
TMenuEngIdManagerInit idManagerInit( *iIdManager );
TraverseNodeL( aRootNode, idManagerInit );
// Make sure all nodes have ID.
TMenuEngIdSetter idSetter( *iIdManager );
TraverseNodeL( aRootNode, idSetter );
}
// ---------------------------------------------------------
// CMenuEng::GetIdSeedL
// ---------------------------------------------------------
//
void CMenuEng::GetIdSeedL( TInt& aIdSeed )
{
TInt seed = 0;
__ASSERT_DEBUG( iTree, User::Invariant() );
MXCFWNode* root = iTree->Root();
if ( root )
{
RNodeArray nodes;
CleanupClosePushL( nodes );
iTree->GetNodesOfTypeL
( KMenuTypeIdSeed(), nodes, root, EFalse );
if ( nodes.Count() )
{
const CMenuEngObject& object = Object( *nodes[0] );
TPtrC val;
TBool dummy;
if ( object.FindAttribute( KMenuAttrIdSeed(), val, dummy ) )
{
seed = MenuEngId::AsInt( val );
}
}
CleanupStack::PopAndDestroy( &nodes );
}
aIdSeed = seed;
}
// ---------------------------------------------------------
// CMenuEng::SetIdSeedL
// ---------------------------------------------------------
//
void CMenuEng::SetIdSeedL( TInt aSeed )
{
__ASSERT_DEBUG( iTree, User::Invariant() );
MXCFWNode* root = iTree->Root();
if ( root )
{
CMenuEngObject* object = NULL;
RNodeArray nodes;
CleanupClosePushL( nodes );
iTree->GetNodesOfTypeL
( KMenuTypeIdSeed(), nodes, root, EFalse );
if ( nodes.Count() )
{
object = &Object( *nodes[0] );
}
else
{
// Outside of the item tree -> no ID set.
object = CMenuEngObject::NewL( *this, KMenuTypeIdSeed() );
CleanupStack::PushL( object );
iTree->AddNodeL( object, root );
CleanupStack::Pop( object );
}
__ASSERT_DEBUG( object, User::Invariant() );
TBuf<KMenuMaxAttrValueLen> buf;
MenuEngId::AsString( aSeed, buf );
object->SetAttributeL( KMenuAttrIdSeed(), buf, EFalse );
CleanupStack::PopAndDestroy( &nodes );
}
}
// ---------------------------------------------------------
// CMenuEng::SelfComplete
// ---------------------------------------------------------
//
void CMenuEng::SelfComplete( TInt aError )
{
TRequestStatus* ownStatus = &iStatus;
*ownStatus = KRequestPending;
SetActive();
User::RequestComplete( ownStatus, aError );
}
// ---------------------------------------------------------
// CMenuEng::LoadRamFileL
// ---------------------------------------------------------
//
void CMenuEng::LoadRamFileL()
{
__ASSERT_DEBUG( ELoadRamFile == iState, User::Invariant() );
__ASSERT_DEBUG( !iTree, User::Invariant() );
iTree = CXCFWTree::NewL();
// Legacy xml format supported only if the xml is from rom:
iObjectFactory->SupportLegacyFormat( EFalse );
iEngine->LoadL( *iTree, iRamFileName );
}
// ---------------------------------------------------------
// CMenuEng::LoadRomFileL
// ---------------------------------------------------------
//
void CMenuEng::LoadRomFileL()
{
__ASSERT_DEBUG( ELoadRomFile == iState, User::Invariant() );
// ROM file name is not kept as member - it is used only once.
TFileName fname;
GetFileNameL( fname, ERomFile );
delete iIdManager; iIdManager = NULL;
delete iTree; iTree = NULL;
iTree = CXCFWTree::NewL();
// Legacy xml format supported only if the xml is from rom:
iObjectFactory->SupportLegacyFormat( ETrue );
iEngine->LoadL( *iTree, fname );
}
// ---------------------------------------------------------
// CMenuEng::SaveTempFileL
// ---------------------------------------------------------
//
void CMenuEng::SaveTempFileL()
{
__ASSERT_DEBUG( ESaveFile == iState, User::Invariant() );
__ASSERT_DEBUG( iTree, User::Invariant() );
__ASSERT_DEBUG( iIdManager, User::Invariant() );
// Write back ID seed.
SetIdSeedL( iIdManager->Seed() );
// Save to temp file then replace content file with temp file.
// This avoids having incomplete content file, if something goes wrong.
__ASSERT_DEBUG( DebugSanityCheck( *iTree ), User::Invariant() );
TRAPD( err, iEngine->SaveL( *iTree, iTempFileName ) );
if( err==KErrDiskFull )
{
iDiskWasFullAndRamFileWasNotCreated = ETrue;
SelfComplete( KErrNone );
}
else
{
iDiskWasFullAndRamFileWasNotCreated = EFalse;
User::LeaveIfError( err );
}
}
// ---------------------------------------------------------
// CMenuEng::ReplaceRamFileL
// ---------------------------------------------------------
//
void CMenuEng::ReplaceRamFileL()
{
__ASSERT_DEBUG( ESaveFile == iState, User::Invariant() );
// RFs::Replace() copies the file data ->
// Delete() + Rename() is used instead.
iFs.Delete( iRamFileName );
TInt err = iFs.Rename( iTempFileName, iRamFileName );
if( err==KErrDiskFull )
{
iDiskWasFullAndRamFileWasNotCreated = ETrue;
}
else
{
iDiskWasFullAndRamFileWasNotCreated = EFalse;
User::LeaveIfError( err );
}
}
// ---------------------------------------------------------
// CMenuEng::CompleteCurrentOperation
// ---------------------------------------------------------
//
void CMenuEng::CompleteCurrentOperation( TInt aError )
{
// Operation completion reported two ways:
// - "No change" operations report completion immediately;
// - "Change" operations trigger saving and wait the result.
__ASSERT_DEBUG( EExecuteOp == iState || ESaveFile == iState, \
User::Invariant() );
if ( iCurrentOperation )
{
iCurrentOperation->CompletedMenuEngOperation( aError );
iCurrentOperation = NULL;
}
// Flush notifications in the success case.
// (Otherwise, the changes did not happen.)
if ( !aError )
{
for ( TInt i = 0; i < iNotifyQueue.Count(); i++ )
{
const TMenuEngNotify& notify = iNotifyQueue[i];
iObserver.EngineEvents( notify.iFolder, notify.iEvents );
}
}
iNotifyQueue.Reset();
}
// ---------------------------------------------------------
// CMenuEng::ExecuteOperationL
// ---------------------------------------------------------
//
void CMenuEng::ExecuteOperationL()
{
__ASSERT_DEBUG( EExecuteOp == iState, User::Invariant() );
__ASSERT_DEBUG( !iCurrentOperation, User::Invariant() );
if ( iOperations.Count() )
{
iCurrentOperation = iOperations[0];
iOperations.Remove( 0 );
iCurrentOperation->RunMenuEngOperationL();
SaveChangesL();
}
else
{
// No pending operations, we stop here.
// Next QueueOperationL will kick us back to operation.
iState = EReady;
}
}
// ---------------------------------------------------------
// CMenuEng::SaveChangesL
// ---------------------------------------------------------
//
void CMenuEng::SaveChangesL()
{
// This method is part of ExecuteOperationL.
// (Post-operation processing.)
__ASSERT_DEBUG( EExecuteOp == iState, User::Invariant() );
if ( iChanged || iDiskWasFullAndRamFileWasNotCreated )
{
// Tree changed, async save.
iState = ESaveFile;
SaveTempFileL();
}
else
{
// Tree not changed, we are done.
CompleteCurrentOperation( KErrNone );
if ( iOperations.Count() )
{
// Go for the next operation (async).
iState = EExecuteOp; // Same as current value, code clarity.
SelfComplete( KErrNone );
}
else
{
// No pending operations, we stop here.
// Next QueueOperationL will kick us back to operation.
iState = EReady;
}
}
}
// End of File