diff -r 0efa10d348c0 -r a5a39a295112 menucontentsrv/engsrc/menueng.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/menucontentsrv/engsrc/menueng.cpp Wed Sep 01 12:22:09 2010 +0100 @@ -0,0 +1,1403 @@ +/* +* 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 +#include +#include +#include +#include +#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& 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 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& 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 + { + TBool exists(EFalse); + const RNodeArray& nodes = iTree->Nodes(); + for ( TInt i = 0; i < nodes.Count(); i++ ) + { + CMenuEngObject& object = Object( *nodes[i] ); + if ( aId == object.Id() + && !( ( object.Flags() & TMenuItem::EHidden ) + || ( object.Flags() & TMenuItem::EMissing ) ) ) + { + exists = ETrue; + } + } + return exists; + } +// --------------------------------------------------------- +// 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; + AppendPredefinedAttributeL(); + 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 buf; + MenuEngId::AsString( aSeed, buf ); + object->SetAttributeL( KMenuAttrIdSeed(), buf, EFalse ); + CleanupStack::PopAndDestroy( &nodes ); + } + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMenuEng::AppendPredefinedAttributeL( ) + { + __ASSERT_DEBUG( iTree, User::Invariant() ); + MXCFWNode* root = iTree->Root(); + RNodeArray nodes; + CleanupClosePushL( nodes ); + iTree->GetNodesOfTypeL(KMenuTypeApp(),nodes, root, ETrue ); + for (TInt i = 0; iSetAttributeL( KMenuAttrPredefined(), KNullDesC(), 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