|         |      1 /* | 
|         |      2 * Copyright (c) 2000-2009 Nokia Corporation and/or its subsidiary(-ies). | 
|         |      3 * All rights reserved. | 
|         |      4 * This component and the accompanying materials are made available | 
|         |      5 * under the terms of "Eclipse Public License v1.0" | 
|         |      6 * which accompanies this distribution, and is available | 
|         |      7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". | 
|         |      8 * | 
|         |      9 * Initial Contributors: | 
|         |     10 * Nokia Corporation - initial contribution. | 
|         |     11 * | 
|         |     12 * Contributors: | 
|         |     13 * | 
|         |     14 * Description:  | 
|         |     15 * | 
|         |     16 */ | 
|         |     17  | 
|         |     18  | 
|         |     19 #include "UndoSystemImpl.h" | 
|         |     20 #include "AssertFileAndLine.h" | 
|         |     21  | 
|         |     22 using namespace UndoSystem; | 
|         |     23  | 
|         |     24 namespace UndoSystem | 
|         |     25 { | 
|         |     26 /** | 
|         |     27  * Gatekeeper that overrides none of the default functionality. | 
|         |     28  * @since App-frameworks6.1 | 
|         |     29  */ | 
|         |     30 class CDefaultGatekeeper : public CBase, public MNotUndoableGatekeeper {}; | 
|         |     31 } | 
|         |     32  | 
|         |     33  | 
|         |     34 ////////////////////////////// | 
|         |     35 //							// | 
|         |     36 //	MNotUndoableGatekeeper  // | 
|         |     37 //							// | 
|         |     38 ////////////////////////////// | 
|         |     39 EXPORT_C TBool MNotUndoableGatekeeper::RetryOutOfMemoryL(TInt) | 
|         |     40 	{ | 
|         |     41 	return EFalse; | 
|         |     42 	} | 
|         |     43  | 
|         |     44 EXPORT_C TBool MNotUndoableGatekeeper::AllowNotUndoableL(TInt aReason) | 
|         |     45 	{ | 
|         |     46 	if (aReason != KErrNotSupported) | 
|         |     47 		User::Leave(aReason); | 
|         |     48 	return ETrue; | 
|         |     49 	} | 
|         |     50  | 
|         |     51 /////////////////////// | 
|         |     52 //					 // | 
|         |     53 //	CCommandManager  // | 
|         |     54 //					 // | 
|         |     55 /////////////////////// | 
|         |     56  | 
|         |     57 CCommandManager::~CCommandManager() | 
|         |     58 	{ | 
|         |     59 	ResetUndo(); | 
|         |     60 	delete iFuture; | 
|         |     61 	delete iPast; | 
|         |     62 	delete iDefaultGatekeeper; | 
|         |     63 	} | 
|         |     64  | 
|         |     65 CCommandManager::CCommandManager() : iRefCount(1) | 
|         |     66 	{ | 
|         |     67 	} | 
|         |     68  | 
|         |     69 EXPORT_C void CCommandManager::NewReference() | 
|         |     70 	{ | 
|         |     71 	++iRefCount; | 
|         |     72 	} | 
|         |     73  | 
|         |     74 EXPORT_C void CCommandManager::Release() | 
|         |     75 	{ | 
|         |     76 	if (--iRefCount == 0) | 
|         |     77 		delete this; | 
|         |     78 	} | 
|         |     79  | 
|         |     80 void CCommandManager::ConstructL() | 
|         |     81 	{ | 
|         |     82 	iFuture = CCommandHistory::NewL(); | 
|         |     83 	iPast = CCommandHistory::NewL(); | 
|         |     84 	iDefaultGatekeeper = new(ELeave) CDefaultGatekeeper(); | 
|         |     85 	iCurrentGatekeeper = iDefaultGatekeeper; | 
|         |     86 	} | 
|         |     87  | 
|         |     88 EXPORT_C CCommandManager* CCommandManager::NewL() | 
|         |     89 	{ | 
|         |     90 	CCommandManager* r = new(ELeave) CCommandManager; | 
|         |     91 	CleanupStack::PushL(r); | 
|         |     92 	r->ConstructL(); | 
|         |     93 	CleanupStack::Pop(r); | 
|         |     94 	return r; | 
|         |     95 	} | 
|         |     96  | 
|         |     97 TInt CCommandManager::ExecuteSingleCommandL(const CSingleCommand& aCommand, CCommandHistory& aUndo) | 
|         |     98 	{ | 
|         |     99 	ASSERT(iCurrentGatekeeper); | 
|         |    100  | 
|         |    101 	TInt retries = 0; | 
|         |    102 	CCommand* inverse = 0; | 
|         |    103 	TBool addingToLast = EFalse; | 
|         |    104 	while (!aUndo.UndoHasBeenWaived()) | 
|         |    105 		{ | 
|         |    106 		TRAPD(err, addingToLast = CreateAndPrepareToAddInverseL(aCommand, aUndo, inverse)); | 
|         |    107 		if (err == KErrNone) | 
|         |    108 			break; | 
|         |    109 		if (err == KErrNoMemory) | 
|         |    110 			{ | 
|         |    111 			if ( iCurrentGatekeeper->RetryOutOfMemoryL(retries++) ) | 
|         |    112 				continue; | 
|         |    113 			} | 
|         |    114 		if ( !iCurrentGatekeeper->AllowNotUndoableL(err) ) | 
|         |    115 			return KErrAbort; | 
|         |    116 		aUndo.SetUndoWaived(); | 
|         |    117 		aUndo.Reset(); | 
|         |    118 		break; | 
|         |    119 		} | 
|         |    120  | 
|         |    121 	ASSERT(!(addingToLast && inverse)); | 
|         |    122  | 
|         |    123 	if (inverse) | 
|         |    124 		CleanupStack::PushL(inverse); | 
|         |    125  | 
|         |    126 	TInt error = KErrNone; | 
|         |    127 	retries = 0; | 
|         |    128 	for (;;) | 
|         |    129 		{ | 
|         |    130 		TRAPD(leaveError, | 
|         |    131 			error = aCommand.ExecuteL(); | 
|         |    132 			); | 
|         |    133 		if (leaveError == KErrNone) | 
|         |    134 			break; | 
|         |    135 		if (leaveError != KErrNoMemory | 
|         |    136 			|| !iCurrentGatekeeper->RetryOutOfMemoryL(retries++)) | 
|         |    137 			User::Leave(leaveError); | 
|         |    138 		} | 
|         |    139 	if (error < 0) | 
|         |    140 		{ | 
|         |    141 		// execution caused an error: no inverse is required. | 
|         |    142 		if (inverse) | 
|         |    143 			CleanupStack::PopAndDestroy(inverse); | 
|         |    144 		return error; | 
|         |    145 		} | 
|         |    146  | 
|         |    147 	if (addingToLast) | 
|         |    148 		{ | 
|         |    149 		aCommand.AddInverseToLast(*aUndo.TopSingleCommand()); | 
|         |    150 		} | 
|         |    151 	else if (inverse) | 
|         |    152 		{ | 
|         |    153 		aUndo.AddCommand(inverse); | 
|         |    154 		CleanupStack::Pop(inverse); | 
|         |    155 		} | 
|         |    156  | 
|         |    157 	return KErrNone; | 
|         |    158 	} | 
|         |    159  | 
|         |    160 TInt CCommandManager::ExecuteBatchCommandL(CBatchCommand& aCommand, CCommandHistory& aUndo) | 
|         |    161 	{ | 
|         |    162 	aUndo.BeginBatchLC(); | 
|         |    163 	TInt err = KErrNone; | 
|         |    164 	for (CSingleCommand* single = aCommand.Top(); (err != KErrAbort) && single; | 
|         |    165 		delete aCommand.Pop(), single = aCommand.Top()) | 
|         |    166 		{ | 
|         |    167 		err = ExecuteSingleCommandL(*single, aUndo); | 
|         |    168 		if (aUndo.UndoHasBeenWaived()) | 
|         |    169 			aUndo.Reset(); | 
|         |    170 		} | 
|         |    171 	CleanupStack::PopAndDestroy();	// close batch | 
|         |    172 	return err == KErrAbort? KErrAbort : KErrNone; | 
|         |    173 	} | 
|         |    174  | 
|         |    175 void CCommandManager::MoveHistoryL(CCommandHistory& aFrom, CCommandHistory& aTo) | 
|         |    176 	{ | 
|         |    177 	ASSERT(iFuture); | 
|         |    178 	ASSERT(iPast); | 
|         |    179  | 
|         |    180 	if (aFrom.IsAtBookmark()) | 
|         |    181 		aTo.SetBookmark(); | 
|         |    182  | 
|         |    183 	TInt err = KErrNone; | 
|         |    184 	CCommand* command = aFrom.Top(); | 
|         |    185 	if (!command) | 
|         |    186 		return; | 
|         |    187 	CSingleCommand* single = command->Single(); | 
|         |    188 	if (single) | 
|         |    189 		{ | 
|         |    190 		err = ExecuteSingleCommandL(*single, aTo); | 
|         |    191 		} | 
|         |    192 	else | 
|         |    193 		{ | 
|         |    194 		ASSERT(command->Batch()); | 
|         |    195 		err = ExecuteBatchCommandL(*command->Batch(), aTo); | 
|         |    196 		} | 
|         |    197  | 
|         |    198 	if (0 <= err) | 
|         |    199 		delete aFrom.Pop(); | 
|         |    200  | 
|         |    201 	aFrom.Clean(); | 
|         |    202 	aTo.Clean(); | 
|         |    203 	} | 
|         |    204  | 
|         |    205 TBool CCommandManager::CreateAndPrepareToAddInverseL(const CSingleCommand& aCommand, | 
|         |    206 	CCommandHistory& aUndo, CCommand*& aInverse) | 
|         |    207 	{ | 
|         |    208 	aInverse = 0; | 
|         |    209  | 
|         |    210 	// commands should not be sneaked beyond the bookmark | 
|         |    211 	if (!IsAtBookmark()) | 
|         |    212 		{ | 
|         |    213 		CSingleCommand* top = aUndo.TopSingleCommand(); | 
|         |    214 		if ( top && aCommand.PrepareToAddInverseToLastL( *top ) ) | 
|         |    215 			return ETrue; | 
|         |    216 		} | 
|         |    217  | 
|         |    218 	CCommand* inverse = aCommand.CreateInverseL(); | 
|         |    219 	// null return value indicates "nothing to do", not "inverse not possible" | 
|         |    220 	if (inverse) | 
|         |    221 		{ | 
|         |    222 		CleanupStack::PushL(inverse); | 
|         |    223 		aUndo.PrepareToAddCommandL(inverse); | 
|         |    224 		CleanupStack::Pop(inverse); | 
|         |    225 		} | 
|         |    226 	aInverse = inverse; | 
|         |    227 	return EFalse; | 
|         |    228 	} | 
|         |    229  | 
|         |    230 EXPORT_C void CCommandManager::BeginBatchLC() | 
|         |    231 	{ | 
|         |    232 	iPast->BeginBatchLC(); | 
|         |    233 	} | 
|         |    234  | 
|         |    235 EXPORT_C void CCommandManager::UndoL() | 
|         |    236 	{ | 
|         |    237 	ASSERT(!iPast->IsWithinBatch()); | 
|         |    238 	MoveHistoryL(*iPast, *iFuture); | 
|         |    239 	} | 
|         |    240  | 
|         |    241 EXPORT_C void CCommandManager::RedoL() | 
|         |    242 	{ | 
|         |    243 	ASSERT(!iPast->IsWithinBatch()); | 
|         |    244 	MoveHistoryL(*iFuture, *iPast); | 
|         |    245 	} | 
|         |    246  | 
|         |    247 EXPORT_C TBool CCommandManager::CanUndo() const | 
|         |    248 	{ | 
|         |    249 	ASSERT(iPast); | 
|         |    250 	return !iPast->IsEmpty(); | 
|         |    251 	} | 
|         |    252  | 
|         |    253 EXPORT_C TBool CCommandManager::CanRedo() const | 
|         |    254 	{ | 
|         |    255 	ASSERT(iFuture); | 
|         |    256 	return !iFuture->IsEmpty(); | 
|         |    257 	} | 
|         |    258  | 
|         |    259 EXPORT_C void CCommandManager::ResetUndo() | 
|         |    260 	{ | 
|         |    261 	ASSERT(iPast); | 
|         |    262 	ASSERT(!iPast->IsWithinBatch()); | 
|         |    263 	ASSERT(iFuture); | 
|         |    264 	iPast->Reset(); | 
|         |    265 	iFuture->Reset(); | 
|         |    266 	} | 
|         |    267  | 
|         |    268 EXPORT_C void CCommandManager::SetBookmark() | 
|         |    269 	{ | 
|         |    270 	// Bookmarks cannot be set in the middle of batches. | 
|         |    271 	// More sophisticated logic could be written, but it is unlikely to raise | 
|         |    272 	// any issues. | 
|         |    273 	if (!iPast->IsWithinBatch()) | 
|         |    274 		iPast->SetBookmark(); | 
|         |    275 	} | 
|         |    276  | 
|         |    277 EXPORT_C TBool CCommandManager::IsAtBookmark() const | 
|         |    278 	{ | 
|         |    279 	return iPast->IsAtBookmark() | iFuture->IsAtBookmark(); | 
|         |    280 	} | 
|         |    281  | 
|         |    282 EXPORT_C TInt CCommandManager::ExecuteL(const CSingleCommand& aCommand) | 
|         |    283 	{ | 
|         |    284 	TInt retval = ExecuteSingleCommandL(aCommand, *iPast); | 
|         |    285 	if (0 <= retval) | 
|         |    286 		iFuture->Reset(); | 
|         |    287 	return retval; | 
|         |    288 	} | 
|         |    289  | 
|         |    290 EXPORT_C MNotUndoableGatekeeper* | 
|         |    291 	CCommandManager::SetGatekeeper(MNotUndoableGatekeeper* a) | 
|         |    292 	{ | 
|         |    293 	ASSERT(iCurrentGatekeeper); | 
|         |    294 	MNotUndoableGatekeeper* current = iCurrentGatekeeper; | 
|         |    295 	iCurrentGatekeeper = a? a : iDefaultGatekeeper; | 
|         |    296 	return current; | 
|         |    297 	} | 
|         |    298  | 
|         |    299 EXPORT_C void CCommandManager::SetMaxItems(TInt aMaxItems) | 
|         |    300 	{ | 
|         |    301 	ASSERT(iPast); | 
|         |    302 	iPast->SetMaxItems(aMaxItems); | 
|         |    303 	} | 
|         |    304  | 
|         |    305 ////////////////////// | 
|         |    306 //					// | 
|         |    307 //	CSingleCommand  // | 
|         |    308 //					// | 
|         |    309 ////////////////////// | 
|         |    310  | 
|         |    311 EXPORT_C TUid CSingleCommand::FamilyUid() const | 
|         |    312 	{ | 
|         |    313 	return KNullUid; | 
|         |    314 	} | 
|         |    315  | 
|         |    316 EXPORT_C CSingleCommand* CSingleCommand::Single() | 
|         |    317 	{ | 
|         |    318 	return this; | 
|         |    319 	} | 
|         |    320  | 
|         |    321 EXPORT_C CBatchCommand* CSingleCommand::Batch() | 
|         |    322 	{ | 
|         |    323 	return 0; | 
|         |    324 	} | 
|         |    325  | 
|         |    326 EXPORT_C TBool CSingleCommand::PrepareToAddInverseToLastL(CSingleCommand&) const | 
|         |    327 	{ | 
|         |    328 	return EFalse; | 
|         |    329 	} | 
|         |    330  | 
|         |    331 EXPORT_C void CSingleCommand::AddInverseToLast(CSingleCommand&) const | 
|         |    332 	{ | 
|         |    333 	Panic(KAddToLastOnlyHalfImplemented); | 
|         |    334 	} | 
|         |    335  | 
|         |    336 EXPORT_C CCommand* CSingleCommand::CreateInverseL() const | 
|         |    337 	{ | 
|         |    338 	User::Leave(KErrNotSupported); | 
|         |    339 	return 0; | 
|         |    340 	} | 
|         |    341  | 
|         |    342 ///////////////////// | 
|         |    343 //				   // | 
|         |    344 //	CBatchCommand  // | 
|         |    345 //				   // | 
|         |    346 ///////////////////// | 
|         |    347  | 
|         |    348 CBatchCommand::CBatchCommand() {} | 
|         |    349 EXPORT_C CBatchCommand::~CBatchCommand() | 
|         |    350 	{ | 
|         |    351 	if (iStack) | 
|         |    352 		{ | 
|         |    353 		iStack->Reset(); | 
|         |    354 		delete iStack; | 
|         |    355 		} | 
|         |    356 	} | 
|         |    357  | 
|         |    358 void CBatchCommand::ConstructL() | 
|         |    359 	{ | 
|         |    360 	iStack = CSingleCommandStack::NewL(); | 
|         |    361 	} | 
|         |    362  | 
|         |    363 EXPORT_C CBatchCommand* CBatchCommand::NewLC() | 
|         |    364 	{ | 
|         |    365 	CBatchCommand* r = new(ELeave) CBatchCommand(); | 
|         |    366 	CleanupStack::PushL(r); | 
|         |    367 	r->ConstructL(); | 
|         |    368 	return r; | 
|         |    369 	} | 
|         |    370  | 
|         |    371 EXPORT_C CBatchCommand* CBatchCommand::NewL() | 
|         |    372 	{ | 
|         |    373 	CBatchCommand* r = NewLC(); | 
|         |    374 	CleanupStack::Pop(r); | 
|         |    375 	return r; | 
|         |    376 	} | 
|         |    377  | 
|         |    378 EXPORT_C CBatchCommand* CBatchCommand::Batch() | 
|         |    379 	{ | 
|         |    380 	return this; | 
|         |    381 	} | 
|         |    382  | 
|         |    383 EXPORT_C CSingleCommand* CBatchCommand::Single() | 
|         |    384 	{ | 
|         |    385 	return 0; | 
|         |    386 	} | 
|         |    387  | 
|         |    388 EXPORT_C CSingleCommand* CBatchCommand::Pop() | 
|         |    389 	{ | 
|         |    390 	return iStack->Pop(); | 
|         |    391 	} | 
|         |    392  | 
|         |    393 EXPORT_C CSingleCommand* CBatchCommand::Top() const | 
|         |    394 	{ | 
|         |    395 	return iStack->Top(); | 
|         |    396 	} | 
|         |    397  | 
|         |    398 EXPORT_C void CBatchCommand::PrepareToPushL(CCommand* aCommand) | 
|         |    399 	{ | 
|         |    400 	CBatchCommand* batch = aCommand->Batch(); | 
|         |    401 	if (!batch) | 
|         |    402 		{ | 
|         |    403 		ASSERT(aCommand->Single()); | 
|         |    404 		iStack->PrepareToPushL(1); | 
|         |    405 		} | 
|         |    406 	else | 
|         |    407 		iStack->PrepareToPushL(batch->iStack->Count()); | 
|         |    408 	} | 
|         |    409  | 
|         |    410 EXPORT_C void CBatchCommand::Push(CCommand* aCommand) | 
|         |    411 	{ | 
|         |    412 	CSingleCommand* single = aCommand->Single(); | 
|         |    413 	if (single) | 
|         |    414 		{ | 
|         |    415 		iStack->Push(single); | 
|         |    416 		return; | 
|         |    417 		} | 
|         |    418 	CBatchCommand* batch = aCommand->Batch(); | 
|         |    419 	ASSERT(batch); | 
|         |    420 	iStack->Concatenate(*batch->iStack); | 
|         |    421 	delete aCommand; | 
|         |    422 	} | 
|         |    423  | 
|         |    424 EXPORT_C void CBatchCommand::PushL(CCommand* aCommand) | 
|         |    425 	{ | 
|         |    426 	CleanupStack::PushL(aCommand); | 
|         |    427 	PrepareToPushL(aCommand); | 
|         |    428 	Push(aCommand); | 
|         |    429 	CleanupStack::Pop(aCommand); | 
|         |    430 	} |