diff -r 000000000000 -r 1fb32624e06b textrendering/textformatting/undo/UndoSystem.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/textrendering/textformatting/undo/UndoSystem.cpp Tue Feb 02 02:02:46 2010 +0200 @@ -0,0 +1,430 @@ +/* +* Copyright (c) 2000-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 "UndoSystemImpl.h" +#include "AssertFileAndLine.h" + +using namespace UndoSystem; + +namespace UndoSystem +{ +/** + * Gatekeeper that overrides none of the default functionality. + * @since App-frameworks6.1 + */ +class CDefaultGatekeeper : public CBase, public MNotUndoableGatekeeper {}; +} + + +////////////////////////////// +// // +// MNotUndoableGatekeeper // +// // +////////////////////////////// +EXPORT_C TBool MNotUndoableGatekeeper::RetryOutOfMemoryL(TInt) + { + return EFalse; + } + +EXPORT_C TBool MNotUndoableGatekeeper::AllowNotUndoableL(TInt aReason) + { + if (aReason != KErrNotSupported) + User::Leave(aReason); + return ETrue; + } + +/////////////////////// +// // +// CCommandManager // +// // +/////////////////////// + +CCommandManager::~CCommandManager() + { + ResetUndo(); + delete iFuture; + delete iPast; + delete iDefaultGatekeeper; + } + +CCommandManager::CCommandManager() : iRefCount(1) + { + } + +EXPORT_C void CCommandManager::NewReference() + { + ++iRefCount; + } + +EXPORT_C void CCommandManager::Release() + { + if (--iRefCount == 0) + delete this; + } + +void CCommandManager::ConstructL() + { + iFuture = CCommandHistory::NewL(); + iPast = CCommandHistory::NewL(); + iDefaultGatekeeper = new(ELeave) CDefaultGatekeeper(); + iCurrentGatekeeper = iDefaultGatekeeper; + } + +EXPORT_C CCommandManager* CCommandManager::NewL() + { + CCommandManager* r = new(ELeave) CCommandManager; + CleanupStack::PushL(r); + r->ConstructL(); + CleanupStack::Pop(r); + return r; + } + +TInt CCommandManager::ExecuteSingleCommandL(const CSingleCommand& aCommand, CCommandHistory& aUndo) + { + ASSERT(iCurrentGatekeeper); + + TInt retries = 0; + CCommand* inverse = 0; + TBool addingToLast = EFalse; + while (!aUndo.UndoHasBeenWaived()) + { + TRAPD(err, addingToLast = CreateAndPrepareToAddInverseL(aCommand, aUndo, inverse)); + if (err == KErrNone) + break; + if (err == KErrNoMemory) + { + if ( iCurrentGatekeeper->RetryOutOfMemoryL(retries++) ) + continue; + } + if ( !iCurrentGatekeeper->AllowNotUndoableL(err) ) + return KErrAbort; + aUndo.SetUndoWaived(); + aUndo.Reset(); + break; + } + + ASSERT(!(addingToLast && inverse)); + + if (inverse) + CleanupStack::PushL(inverse); + + TInt error = KErrNone; + retries = 0; + for (;;) + { + TRAPD(leaveError, + error = aCommand.ExecuteL(); + ); + if (leaveError == KErrNone) + break; + if (leaveError != KErrNoMemory + || !iCurrentGatekeeper->RetryOutOfMemoryL(retries++)) + User::Leave(leaveError); + } + if (error < 0) + { + // execution caused an error: no inverse is required. + if (inverse) + CleanupStack::PopAndDestroy(inverse); + return error; + } + + if (addingToLast) + { + aCommand.AddInverseToLast(*aUndo.TopSingleCommand()); + } + else if (inverse) + { + aUndo.AddCommand(inverse); + CleanupStack::Pop(inverse); + } + + return KErrNone; + } + +TInt CCommandManager::ExecuteBatchCommandL(CBatchCommand& aCommand, CCommandHistory& aUndo) + { + aUndo.BeginBatchLC(); + TInt err = KErrNone; + for (CSingleCommand* single = aCommand.Top(); (err != KErrAbort) && single; + delete aCommand.Pop(), single = aCommand.Top()) + { + err = ExecuteSingleCommandL(*single, aUndo); + if (aUndo.UndoHasBeenWaived()) + aUndo.Reset(); + } + CleanupStack::PopAndDestroy(); // close batch + return err == KErrAbort? KErrAbort : KErrNone; + } + +void CCommandManager::MoveHistoryL(CCommandHistory& aFrom, CCommandHistory& aTo) + { + ASSERT(iFuture); + ASSERT(iPast); + + if (aFrom.IsAtBookmark()) + aTo.SetBookmark(); + + TInt err = KErrNone; + CCommand* command = aFrom.Top(); + if (!command) + return; + CSingleCommand* single = command->Single(); + if (single) + { + err = ExecuteSingleCommandL(*single, aTo); + } + else + { + ASSERT(command->Batch()); + err = ExecuteBatchCommandL(*command->Batch(), aTo); + } + + if (0 <= err) + delete aFrom.Pop(); + + aFrom.Clean(); + aTo.Clean(); + } + +TBool CCommandManager::CreateAndPrepareToAddInverseL(const CSingleCommand& aCommand, + CCommandHistory& aUndo, CCommand*& aInverse) + { + aInverse = 0; + + // commands should not be sneaked beyond the bookmark + if (!IsAtBookmark()) + { + CSingleCommand* top = aUndo.TopSingleCommand(); + if ( top && aCommand.PrepareToAddInverseToLastL( *top ) ) + return ETrue; + } + + CCommand* inverse = aCommand.CreateInverseL(); + // null return value indicates "nothing to do", not "inverse not possible" + if (inverse) + { + CleanupStack::PushL(inverse); + aUndo.PrepareToAddCommandL(inverse); + CleanupStack::Pop(inverse); + } + aInverse = inverse; + return EFalse; + } + +EXPORT_C void CCommandManager::BeginBatchLC() + { + iPast->BeginBatchLC(); + } + +EXPORT_C void CCommandManager::UndoL() + { + ASSERT(!iPast->IsWithinBatch()); + MoveHistoryL(*iPast, *iFuture); + } + +EXPORT_C void CCommandManager::RedoL() + { + ASSERT(!iPast->IsWithinBatch()); + MoveHistoryL(*iFuture, *iPast); + } + +EXPORT_C TBool CCommandManager::CanUndo() const + { + ASSERT(iPast); + return !iPast->IsEmpty(); + } + +EXPORT_C TBool CCommandManager::CanRedo() const + { + ASSERT(iFuture); + return !iFuture->IsEmpty(); + } + +EXPORT_C void CCommandManager::ResetUndo() + { + ASSERT(iPast); + ASSERT(!iPast->IsWithinBatch()); + ASSERT(iFuture); + iPast->Reset(); + iFuture->Reset(); + } + +EXPORT_C void CCommandManager::SetBookmark() + { + // Bookmarks cannot be set in the middle of batches. + // More sophisticated logic could be written, but it is unlikely to raise + // any issues. + if (!iPast->IsWithinBatch()) + iPast->SetBookmark(); + } + +EXPORT_C TBool CCommandManager::IsAtBookmark() const + { + return iPast->IsAtBookmark() | iFuture->IsAtBookmark(); + } + +EXPORT_C TInt CCommandManager::ExecuteL(const CSingleCommand& aCommand) + { + TInt retval = ExecuteSingleCommandL(aCommand, *iPast); + if (0 <= retval) + iFuture->Reset(); + return retval; + } + +EXPORT_C MNotUndoableGatekeeper* + CCommandManager::SetGatekeeper(MNotUndoableGatekeeper* a) + { + ASSERT(iCurrentGatekeeper); + MNotUndoableGatekeeper* current = iCurrentGatekeeper; + iCurrentGatekeeper = a? a : iDefaultGatekeeper; + return current; + } + +EXPORT_C void CCommandManager::SetMaxItems(TInt aMaxItems) + { + ASSERT(iPast); + iPast->SetMaxItems(aMaxItems); + } + +////////////////////// +// // +// CSingleCommand // +// // +////////////////////// + +EXPORT_C TUid CSingleCommand::FamilyUid() const + { + return KNullUid; + } + +EXPORT_C CSingleCommand* CSingleCommand::Single() + { + return this; + } + +EXPORT_C CBatchCommand* CSingleCommand::Batch() + { + return 0; + } + +EXPORT_C TBool CSingleCommand::PrepareToAddInverseToLastL(CSingleCommand&) const + { + return EFalse; + } + +EXPORT_C void CSingleCommand::AddInverseToLast(CSingleCommand&) const + { + Panic(KAddToLastOnlyHalfImplemented); + } + +EXPORT_C CCommand* CSingleCommand::CreateInverseL() const + { + User::Leave(KErrNotSupported); + return 0; + } + +///////////////////// +// // +// CBatchCommand // +// // +///////////////////// + +CBatchCommand::CBatchCommand() {} +EXPORT_C CBatchCommand::~CBatchCommand() + { + if (iStack) + { + iStack->Reset(); + delete iStack; + } + } + +void CBatchCommand::ConstructL() + { + iStack = CSingleCommandStack::NewL(); + } + +EXPORT_C CBatchCommand* CBatchCommand::NewLC() + { + CBatchCommand* r = new(ELeave) CBatchCommand(); + CleanupStack::PushL(r); + r->ConstructL(); + return r; + } + +EXPORT_C CBatchCommand* CBatchCommand::NewL() + { + CBatchCommand* r = NewLC(); + CleanupStack::Pop(r); + return r; + } + +EXPORT_C CBatchCommand* CBatchCommand::Batch() + { + return this; + } + +EXPORT_C CSingleCommand* CBatchCommand::Single() + { + return 0; + } + +EXPORT_C CSingleCommand* CBatchCommand::Pop() + { + return iStack->Pop(); + } + +EXPORT_C CSingleCommand* CBatchCommand::Top() const + { + return iStack->Top(); + } + +EXPORT_C void CBatchCommand::PrepareToPushL(CCommand* aCommand) + { + CBatchCommand* batch = aCommand->Batch(); + if (!batch) + { + ASSERT(aCommand->Single()); + iStack->PrepareToPushL(1); + } + else + iStack->PrepareToPushL(batch->iStack->Count()); + } + +EXPORT_C void CBatchCommand::Push(CCommand* aCommand) + { + CSingleCommand* single = aCommand->Single(); + if (single) + { + iStack->Push(single); + return; + } + CBatchCommand* batch = aCommand->Batch(); + ASSERT(batch); + iStack->Concatenate(*batch->iStack); + delete aCommand; + } + +EXPORT_C void CBatchCommand::PushL(CCommand* aCommand) + { + CleanupStack::PushL(aCommand); + PrepareToPushL(aCommand); + Push(aCommand); + CleanupStack::Pop(aCommand); + }