You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

355 lines
8.4 KiB
C++

1 month ago
//////////////////////////////////////////////////////////////////////////////
//<2F><>Ҫ<EFBFBD><D2AA><EFBFBD><EFBFBD>: Undo and Redo<64><6F>ȫ<EFBFBD>ֲ<EFBFBD><D6B2><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊ<EFBFBD><CEAA><EFBFBD><EFBFBD><EFBFBD>ַ<EFBFBD><D6B7><EFBFBD><EFBFBD>޸<EFBFBD><DEB8><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC>޸ĵ<DEB8>Redo/Undo
//
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>д: 2006-12-07
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "UndoManager.h"
#include "Action.h"
#include "Util.h"
#include <assert.h>
#include <algorithm>
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
namespace NAction
{
CActionManager::CActionManager(void *aTarget)
: actionTarget(aTarget)
, cleanMarker(0)
, capacity(30)
, m_pSavedAction(nullptr)
{
}
CActionManager::~CActionManager()
{
DeleteActions(undoStack, (long)undoStack.size());
DeleteActions(redoStack, (long)redoStack.size());
}
void CActionManager::SetStackCapacity(long nActions)
{
assert(nActions > 0);
// the new size may be smaller than the previous stack
// size, so we may have to delete some initial actions
if ((long)undoStack.size() > nActions)
DeleteActions(undoStack, (long)undoStack.size() - nActions);
if ((long)redoStack.size() > nActions)
DeleteActions(redoStack, (long)redoStack.size() - nActions);
capacity = nActions;
// and the new size may also be greater than our previous
// clean state; if so, we need to mark the document so that
// it cannot be cleaned
if (cleanMarker >= capacity)
cleanMarker = -1;
}
void CActionManager::SavePostion()
{
deque <CAction *>::iterator itEnd = undoStack.end();
if (itEnd == undoStack.begin())
{
m_pSavedAction = nullptr;
return;
}
itEnd--;
m_pSavedAction = *itEnd;
}
bool CActionManager::IsModified()
{
deque <CAction *>::iterator itEnd = undoStack.end();
if (itEnd == undoStack.begin())
{
return FALSE;
}
itEnd--;
return m_pSavedAction != *itEnd;
}
void CActionManager::SetLastAction(CAction *anAction)
{
// ensure last action knows its done
if (undoStack.empty() == false)
{
CAction *previousAction = undoStack.back();
if (previousAction->IsFinished() == false)
previousAction->Finish();
}
// this may just be a notification that the current
// action is done; if so, the action must finish
// (above) but nothing else should change.
if (anAction != 0)
{
// a new undoable action cancels any previously redoable ones
DeleteActions(redoStack, (long)redoStack.size());
// remove bottom action if stack is full
if (undoStack.size() == capacity)
{
CAction *firstAction = undoStack.front();
// we are removing an action which modified the
// command target (e.g., a document); this effects
// our ability to undo back into an unmodified state
cleanMarker = -1;
undoStack.pop_front();
delete firstAction;
firstAction = nullptr;
}
// we use the standard reverse vector approach for our stacks,
// even though we can efficiently push front with a deque; this
// way however, indexes in the deque remain constant; that makes
// building menus and tracking the clean marker much easier
undoStack.push_back(anAction);
//NotifyTarget(UM_Do, 1);
}
//else
// NotifyTarget(UM_Do, 1);
}
const UMString &CActionManager::GetActionName(StackKind kind, long pos) const
{
const ActionStack &theStack = (kind == CActionManager::UNDO) ? undoStack : redoStack;
assert(pos < (long)theStack.size());
return theStack[theStack.size() - (pos + 1)]->GetName();
}
long CActionManager::GetStackSize(CActionManager::StackKind kind) const
{
const ActionStack &theStack = (kind == CActionManager::UNDO) ? undoStack : redoStack;
return (long)theStack.size();
}
bool CActionManager::CanUndo(long nActions) const
{
return nActions <= (long)undoStack.size();
}
int CActionManager::Undo(long nActions)
{
//assert( CanUndo(nActions) );
if (!CanUndo(nActions)) return 0;
int nActionType = 0;
for (long i = 0; i < nActions; ++i)
{
CAction *lastAction = undoStack.back();
undoStack.pop_back();
// to undo it, the action must finish
// whatever its currently doing
if (lastAction->IsFinished() == false)
lastAction->Finish();
lastAction->Undo();
// redo size is limited by the undo stack and its
// limitations are enforced by SetLastAction()
redoStack.push_back(lastAction);
nActionType = lastAction->GetActionType();
}
return nActionType;
}
bool CActionManager::CanRedo(long nActions) const
{
return nActions <= (long)redoStack.size();
}
int CActionManager::Redo(long nActions)
{
//assert( CanRedo(nActions) );
if (!CanRedo(nActions)) return 0;
int nActionType = 0;
for (long i = 0; i < nActions; ++i)
{
CAction *lastAction = redoStack.back();
redoStack.pop_back();
lastAction->Redo();
// undo size is limited by the redo stack, which is
// limited in turn by the original undo stack, which
// is limited by SetLastAction() (whew!)
undoStack.push_back(lastAction);
nActionType = lastAction->GetActionType();
}
return nActionType;
}
void CActionManager::TargetCleaned()
{
if (undoStack.size() > 0)
{
// finish the last action; once we've
// saved a document, actions like typing
// must create a new action
CAction *lastAction = undoStack.back();
if (lastAction->IsFinished() == false)
lastAction->Finish();
}
// set the marker to the size of the stack
// (we must be at that point for the target
// to be clean)
cleanMarker = (long)undoStack.size();
}
const CActionManager::ActionStack& CActionManager::GetUndoStack() const
{
return undoStack;
}
const CActionManager::ActionStack& CActionManager::GetRedoStack() const
{
return redoStack;
}
std::vector<ActionDetail> CActionManager::ListActionDetails() const
{
std::vector<ActionDetail> details;
// undo ջ<>Խ<EFA3AC><D4BD><EFBFBD><EFBFBD>ջ<EFBFBD>׵ģ<D7B5>Խ<EFBFBD><D4BD><EFBFBD>Ȳ<EFBFBD><C8B2><EFBFBD><EFBFBD><EFBFBD>
for (auto* pAction : undoStack)
{
details.push_back({ pAction->GetUUID(), pAction->GetActionName(), FormatTime(pAction->GetTimePoint()) });
}
// redo ջ<><D5BB><EFBFBD><EFBFBD> Action һ<><D2BB><EFBFBD><EFBFBD> undo ջ<><D5BB><EFBFBD><EFBFBD>ִ<EFBFBD>У<EFBFBD><D0A3><EFBFBD><EFBFBD><EFBFBD>Խ<EFBFBD>ǿ<EFBFBD><C7BF><EFBFBD>ջ<EFBFBD><D5BB>Խ<EFBFBD><D4BD><EFBFBD>Ȳ<EFBFBD><C8B2><EFBFBD><EFBFBD><EFBFBD> Action
for (auto it = redoStack.rbegin(); it != redoStack.rend(); it++)
{
CAction* pAction = *it;
details.push_back({ pAction->GetUUID(), pAction->GetActionName(), FormatTime(pAction->GetTimePoint()) });
}
return details;
}
void CActionManager::RestoreToTargetAction(const std::string& targetUUID)
{
// <20><><EFBFBD><EFBFBD>Ϊ<EFBFBD>գ<EFBFBD><D5A3><EFBFBD><EFBFBD><EFBFBD><EFBFBD>˻ص<CBBB>ԭʼͼ<CABC><CDBC>
if (targetUUID.empty())
{
while (CanUndo())
{
Undo();
}
return;
}
/*
* <EFBFBD><EFBFBD><EFBFBD><EFBFBD>ÿ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><EFBFBD> Action<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> undoStack<EFBFBD><EFBFBD><EFBFBD>ٶ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ѿ<EFBFBD><EFBFBD>ָ<EFBFBD><EFBFBD><EFBFBD>ָ<EFBFBD><EFBFBD> Action <EFBFBD>ˣ<EFBFBD><EFBFBD><EFBFBD>ʱ<EFBFBD><EFBFBD><EFBFBD>ʲô<EFBFBD><EFBFBD>
* <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǣ<EFBFBD><EFBFBD><EFBFBD> Action <EFBFBD><EFBFBD> undoStack ջ<EFBFBD><EFBFBD>ջ<EFBFBD><EFBFBD>
* <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Ŀ<EFBFBD><EFBFBD> Action <EFBFBD><EFBFBD> undoStack ջ<EFBFBD>У<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ǿ<EFBFBD><EFBFBD><EFBFBD>Ҫһֱ Undo ֱ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> undoStack ջ<EFBFBD><EFBFBD>Ϊֹ<EFBFBD><EFBFBD>
* <EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŀ<EFBFBD><EFBFBD> Action <EFBFBD><EFBFBD> redoStack <EFBFBD>У<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ҫһֱ Redo ֱ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> undoStack ջ<EFBFBD><EFBFBD>
*
* | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
* /\
* <EFBFBD><EFBFBD><EFBFBD>Dz<EFBFBD><EFBFBD>Dz<EFBFBD><EFBFBD><EFBFBD><EFBFBD>б<EFBFBD><EFBFBD><EFBFBD><EFBFBD>α<EFBFBD><EFBFBD><EFBFBD>ʵ<EFBFBD>ֲ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͻ<EFBFBD><EFBFBD>˵ģ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Dz<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ջ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>˼·<EFBFBD><EFBFBD>һ<EFBFBD><EFBFBD><EFBFBD>ģ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>У<EFBFBD><EFBFBD><EFBFBD>ǰ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
* ͣ<EFBFBD><EFBFBD><EFBFBD>ڲ<EFBFBD><EFBFBD><EFBFBD> 4 <EFBFBD>ϣ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> undo<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> redo<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ָ<EFBFBD><EFBFBD><EFBFBD> 2<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> undo<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ָ<EFBFBD><EFBFBD><EFBFBD> 6 <EFBFBD><EFBFBD><EFBFBD><EFBFBD> redo
*/
if (InUndoStack(targetUUID))
{
std::string currentUUID;
while (!undoStack.empty())
{
currentUUID = undoStack.back()->GetUUID();
if (currentUUID == targetUUID)
{
break;
}
Undo();
}
}
else if (InRedoStack(targetUUID))
{
std::string currentUUID;
while (!redoStack.empty())
{
currentUUID = redoStack.back()->GetUUID();
Redo();
if (currentUUID == targetUUID)
{
break;
}
}
}
}
bool CActionManager::InUndoStack(const std::string& uuid) const
{
return AnyOf(undoStack, [&uuid](const CAction* pAction)
{
return pAction->GetUUID() == uuid;
});
}
bool CActionManager::InRedoStack(const std::string& uuid) const
{
return AnyOf(redoStack, [&uuid](const CAction* pAction)
{
return pAction->GetUUID() == uuid;
});
}
void CActionManager::DeleteActions(ActionStack &aStack, long nActions)
{
// delete from the bottom up; because
// we're using a reverse storage strategy,
// we walk from front to back
for (long i = 0; i < nActions; ++i)
{
CAction *anAction = aStack.front();
aStack.pop_front();
delete anAction;
anAction = nullptr;
}
}
void CActionManager::RemoveLastAction()
{
assert(CanUndo(1));
undoStack.pop_back();
// may have undone very first action since last
// save, or undone something that was saved
//NotifyTarget(UM_Undo, nActions);
}
long CActionManager::GetStackCapacity() const
{
return capacity;
}
void* CActionManager::GetTarget() const
{
return actionTarget;
}
void CActionManager::ClearTarget() //clear all actioin
{
DeleteActions(undoStack, (long)undoStack.size());
DeleteActions(redoStack, (long)redoStack.size());
}
}//end namespace