--- /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);
+ }