textrendering/textformatting/undo/UndoSystemImpl.cpp
changeset 0 1fb32624e06b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/textrendering/textformatting/undo/UndoSystemImpl.cpp	Tue Feb 02 02:02:46 2010 +0200
@@ -0,0 +1,324 @@
+/*
+* 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 UndoSystemImpl
+{
+const TInt KCommandStackGranularity = 10;
+_LIT(KUndoPanic, "Undo System");
+}
+
+using namespace UndoSystemImpl;
+
+
+void UndoSystem::Panic(TPanicCode aCode)
+	{
+	User::Panic(KUndoPanic, aCode);
+	}
+
+/////////////////////
+//				   //
+//	CCommandStack  //
+//				   //
+/////////////////////
+
+CCommandStack::CCommandStack() : iEnd(0), iBookmark(0) {}
+
+CCommandStack::~CCommandStack()
+	{
+	if (iStack)
+		{
+		Reset();
+		delete iStack;
+		}
+	}
+
+void CCommandStack::ConstructL()
+	{
+	iStack = new(ELeave) CArrayFixSeg<CCommand*>(KCommandStackGranularity);
+	}
+
+CCommandStack* CCommandStack::NewL()
+	{
+	CCommandStack* r = new(ELeave) CCommandStack();
+	CleanupStack::PushL(r);
+	r->ConstructL();
+	CleanupStack::Pop(r);
+	return r;
+	}
+
+TInt CCommandStack::Count() const
+	{
+	ASSERT(iEnd <= iStack->Count());
+	return iEnd;
+	}
+
+void CCommandStack::Reset()
+	{
+	PruneTo(0);
+	iBookmark = -1;
+	}
+
+CCommand* CCommandStack::Top() const
+	{
+	ASSERT(0 <= iEnd);
+	ASSERT(iEnd <= iStack->Count());
+	return iEnd == 0? 0 : iStack->At(iEnd - 1);
+	}
+
+CCommand* CCommandStack::Pop()
+	{
+	ASSERT(iEnd <= iStack->Count());
+	CCommand* top = Top();
+	__ASSERT_ALWAYS(top, UndoSystem::Panic(KCommandStackUnderflow));
+	TInt numSlots = iStack->Count() - iEnd + 1;
+	ASSERT(numSlots <= iStack->Count());
+	iStack->Delete(iStack->Count() - numSlots, numSlots);
+	iEnd = iStack->Count();
+	if (iEnd < iBookmark)
+		iBookmark = -1;
+	return top;
+	}
+
+void CCommandStack::PruneTo(TInt aNumberOfItems)
+	{
+	ASSERT(iEnd <= iStack->Count());
+	iStack->Delete(iEnd, iStack->Count() - iEnd);
+	iEnd = iStack->Count();
+	if (aNumberOfItems < iEnd)
+		{
+		TInt numberToDestroy = iEnd - aNumberOfItems;
+		for (TInt i = 0; i != numberToDestroy; ++i)
+			{
+			delete iStack->At(i);
+			}
+		iStack->Delete(0, numberToDestroy);
+		iStack->Compress();
+		iEnd = aNumberOfItems;
+		iBookmark -= aNumberOfItems;
+		}
+	}
+
+void CCommandStack::PrepareToPushL(TInt aNumberOfItems)
+	{
+	iStack->ResizeL(iEnd + aNumberOfItems);
+	}
+
+void CCommandStack::Push(CCommand* aCommand)
+	{
+	ASSERT(aCommand);
+	__ASSERT_ALWAYS(iEnd < iStack->Count(), UndoSystem::Panic(KCommandStackPushNotPrepared));
+	iStack->At(iEnd) = aCommand;
+	++iEnd;
+	}
+
+void CCommandStack::Concatenate(CCommandStack& aStack)
+	{
+	TInt thisCount = iEnd;
+	iEnd += aStack.iEnd;
+	__ASSERT_DEBUG(iEnd <= iStack->Count(),
+		UndoSystem::Panic(KCommandStackPushNotPrepared));
+	for (TInt i = 0; i != aStack.iEnd; ++i, ++thisCount)
+		iStack->At(thisCount) = (aStack.iStack->At(i));
+	aStack.iEnd = 0;
+	aStack.iStack->Reset();
+	}
+
+void CCommandStack::SetBookmark()
+	{
+	iBookmark = iEnd;
+	}
+
+TBool CCommandStack::IsAtBookmark() const
+	{
+	return iBookmark == iEnd? ETrue : EFalse;
+	}
+
+CSingleCommandStack* CSingleCommandStack::NewL()
+	{
+	CSingleCommandStack* r = new(ELeave) CSingleCommandStack;
+	CleanupStack::PushL(r);
+	r->iStack.ConstructL();
+	CleanupStack::Pop(r);
+	return r;
+	}
+
+///////////////////////
+//					 //
+//	CCommandHistory  //
+//					 //
+///////////////////////
+
+CCommandHistory::CCommandHistory()
+	: iMaxItems(KMaxTInt >> 1)
+	{
+	}
+
+CCommandHistory::~CCommandHistory()
+	{
+	delete iStack;
+	}
+
+void CCommandHistory::ConstructL()
+	{
+	iStack = CCommandStack::NewL();
+	iCurrent = 0;
+	}
+
+CCommandHistory* CCommandHistory::NewL()
+	{
+	CCommandHistory* r = new(ELeave) CCommandHistory();
+	CleanupStack::PushL(r);
+	r->ConstructL();
+	CleanupStack::Pop(r);
+	return r;
+	}
+
+void CCommandHistory::Prune()
+	{
+	if (iMaxItems < iStack->Count())
+		iStack->PruneTo(iMaxItems);
+	}
+
+void CCommandHistory::SetMaxItems(TInt aMaxItems)
+	{
+	ASSERT(0 < aMaxItems);
+	iMaxItems = aMaxItems;
+	Prune();
+	}
+
+void CCommandHistory::CloseBatch(void* a)
+	{
+	CCommandHistory* aThat = reinterpret_cast<CCommandHistory*>(a);
+	aThat->iCurrent = 0;
+	if (aThat->iBatchUndoHasBeenWaived)
+		aThat->Reset();
+	}
+
+void CCommandHistory::DownBatchLevel(void*) {}
+
+TBool CCommandHistory::IsWithinBatch() const
+	{
+	return iCurrent? ETrue : EFalse;
+	}
+
+void CCommandHistory::BeginBatchLC()
+	{
+	if (iCurrent)
+		{
+		CleanupStack::PushL(TCleanupItem(DownBatchLevel, this));
+		}
+	else
+		{
+		iStack->PrepareToPushL(1);
+		iBatchUndoHasBeenWaived = EFalse;
+		CleanupStack::PushL(TCleanupItem(CloseBatch, this));
+		CBatchCommand* batch = CBatchCommand::NewL();
+		iCurrent = batch;
+		iStack->Push(batch);
+		}
+	}
+
+TBool CCommandHistory::UndoHasBeenWaived() const
+	{
+	return iCurrent? iBatchUndoHasBeenWaived : EFalse;
+	}
+
+void CCommandHistory::SetUndoWaived()
+	{
+	iBatchUndoHasBeenWaived = ETrue;
+	}
+
+void CCommandHistory::Reset()
+	{
+	iStack->Reset();
+	iCurrent = 0;
+	}
+
+void CCommandHistory::PrepareToAddCommandL(CCommand* aCommand)
+	{
+	if (iCurrent)
+		iCurrent->PrepareToPushL(aCommand);
+	else
+		iStack->PrepareToPushL(1);
+	}
+
+void CCommandHistory::AddCommand(CCommand* aCommand)
+	{
+	if (iCurrent)
+		iCurrent->Push(aCommand);
+	else
+		iStack->Push(aCommand);
+	if (!iCurrent)
+		Prune();
+	}
+
+CSingleCommand* CCommandHistory::TopSingleCommand() const
+	{
+	CCommand* top = Top();
+
+	if (!top)
+		return 0;
+
+	// if the top of the undo stack is an empty batch, then we are starting a new
+	// batch command and so do not want to combine. Otherwise, if the top is a batch
+	// with elements, we want to see if we can combine with the top one.
+	CBatchCommand* batch = top->Batch();
+	return batch? batch->Top() : top->Single();
+	}
+
+CCommand* CCommandHistory::Top() const
+	{
+	return iStack->Top();
+	}
+
+CCommand* CCommandHistory::Pop()
+	{
+	ASSERT(!iCurrent);
+	return iStack->Pop();
+	}
+
+void CCommandHistory::Clean()
+	{
+	ASSERT(!iCurrent);
+	CCommand* command = Top();
+	if (!command)
+		return;
+	CBatchCommand* batch = command->Batch();
+	if (!batch)
+		return;
+	if (batch->IsEmpty())
+		{
+		delete Pop();
+		}
+	}
+
+void CCommandHistory::SetBookmark()
+	{
+	iStack->SetBookmark();
+	}
+
+TBool CCommandHistory::IsAtBookmark()
+	{
+	return iStack->IsAtBookmark();
+	}
+