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.
805 lines
20 KiB
C++
805 lines
20 KiB
C++
/*
|
|
* Copyright (c) Contributors to the Open 3D Engine Project.
|
|
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
*
|
|
*/
|
|
|
|
|
|
#include "EditorDefs.h"
|
|
|
|
#include "Undo.h"
|
|
|
|
#include "Settings.h"
|
|
#include "IUndoManagerListener.h"
|
|
#include "Objects/ObjectManager.h"
|
|
#include <Include/ILogFile.h>
|
|
#include <list>
|
|
|
|
#include <QString>
|
|
#include "QtUtilWin.h"
|
|
|
|
#define UNDOREDO_BUTTON_POPUP_TEXT_WIDTH 81
|
|
#define UNDOREDO_MULTIPLE_OBJECTS_TEXT " (Multiple Objects)"
|
|
|
|
|
|
//! CSuperUndo objects groups together a block of UndoStepto to allow them to be Undo by single operation.
|
|
class CSuperUndoStep
|
|
: public CUndoStep
|
|
{
|
|
public:
|
|
//! Add new undo object to undo step.
|
|
void AddUndoStep(CUndoStep* step)
|
|
{
|
|
m_undoSteps.push_back(step);
|
|
}
|
|
int GetSize() const override
|
|
{
|
|
int size = 0;
|
|
for (int i = 0; i < m_undoSteps.size(); i++)
|
|
{
|
|
size += m_undoSteps[i]->GetSize();
|
|
}
|
|
return size;
|
|
}
|
|
bool IsEmpty() const override
|
|
{
|
|
return m_undoSteps.empty();
|
|
}
|
|
void Undo(bool bUndo) override
|
|
{
|
|
for (int i = static_cast<int>(m_undoSteps.size()) - 1; i >= 0; i--)
|
|
{
|
|
m_undoSteps[i]->Undo(bUndo);
|
|
}
|
|
}
|
|
void Redo() override
|
|
{
|
|
for (int i = 0; i < m_undoSteps.size(); i++)
|
|
{
|
|
m_undoSteps[i]->Redo();
|
|
}
|
|
}
|
|
|
|
private:
|
|
//! Undo steps included in this step.
|
|
std::vector<CUndoStep*> m_undoSteps;
|
|
};
|
|
|
|
// Helper class for CUndoManager that monitors the Asset Manager and suspends undo recording while the Asset Manager
|
|
// is processing asset loading events. The events are processed non-deterministically, so they could accidentally get captured
|
|
// within an undo recording block.
|
|
class AssetManagerUndoInterruptor
|
|
: public AZ::Data::AssetManagerNotificationBus::Handler
|
|
{
|
|
public:
|
|
AssetManagerUndoInterruptor()
|
|
{
|
|
AZ::Data::AssetManagerNotificationBus::Handler::BusConnect();
|
|
}
|
|
|
|
~AssetManagerUndoInterruptor() override
|
|
{
|
|
AZ::Data::AssetManagerNotificationBus::Handler::BusDisconnect();
|
|
}
|
|
|
|
void OnAssetEventsDispatchBegin() override
|
|
{
|
|
GetIEditor()->GetUndoManager()->Suspend();
|
|
}
|
|
|
|
void OnAssetEventsDispatchEnd() override
|
|
{
|
|
GetIEditor()->GetUndoManager()->Resume();
|
|
}
|
|
};
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CUndoManager::CUndoManager()
|
|
{
|
|
m_bRecording = false;
|
|
m_bSuperRecording = false;
|
|
|
|
m_currentUndo = nullptr;
|
|
m_superUndo = nullptr;
|
|
m_assetManagerUndoInterruptor = new AssetManagerUndoInterruptor();
|
|
|
|
m_suspendCount = 0;
|
|
|
|
m_bUndoing = false;
|
|
m_bRedoing = false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CUndoManager::~CUndoManager()
|
|
{
|
|
|
|
m_bRecording = false;
|
|
ClearRedoStack();
|
|
ClearUndoStack();
|
|
|
|
delete m_superUndo;
|
|
delete m_currentUndo;
|
|
delete m_assetManagerUndoInterruptor;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CUndoManager::Begin()
|
|
{
|
|
//CryLog( "<Undo> Begin SuspendCount=%d",m_suspendCount );
|
|
//if (m_bSuperRecording)
|
|
//CLogFile::FormatLine( "<Undo> Begin (Inside SuperSuper)" );
|
|
if (m_bUndoing || m_bRedoing) // If Undoing or redoing now, ignore this calls.
|
|
{
|
|
return;
|
|
}
|
|
|
|
// assert( m_bRecording == false );
|
|
|
|
if (m_bRecording)
|
|
{
|
|
//CLogFile::WriteLine( "<Undo> Begin (already recording)" );
|
|
// Not cancel, just combine.
|
|
return;
|
|
}
|
|
|
|
// Begin Creates a new undo object.
|
|
m_currentUndo = new CUndoStep;
|
|
|
|
m_bRecording = true;
|
|
//CLogFile::WriteLine( "<Undo> Begin OK" );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CUndoManager::Restore(bool bUndo)
|
|
{
|
|
if (m_bUndoing || m_bRedoing) // If Undoing or redoing now, ignore this calls.
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (m_currentUndo)
|
|
{
|
|
BeginRestoreTransaction();
|
|
Suspend();
|
|
if (bUndo && m_currentUndo)
|
|
{
|
|
m_currentUndo->Undo(false); // Undo not by Undo command (no need to store Redo)
|
|
}
|
|
Resume();
|
|
if (m_currentUndo)
|
|
{
|
|
m_currentUndo->ClearObjects();
|
|
}
|
|
EndRestoreTransaction();
|
|
}
|
|
//CryLog( "Restore Undo" );
|
|
}
|
|
|
|
// This function is used below to decide if an operation should force a save or not. This currently
|
|
// prevents selecting an entity, either from the outliner or both the old and new viewports.
|
|
static bool ShouldPersist(const QString& name)
|
|
{
|
|
return name != "Select Object(s)" && name != "Select Entity" && name != "Box Select Entities";
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CUndoManager::Accept(const QString& name)
|
|
{
|
|
//CryLog( "<Undo> Accept, Suspend Count=%d",m_suspendCount );
|
|
if (m_bUndoing || m_bRedoing) // If Undoing or redoing now, ignore this calls.
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!m_bRecording)
|
|
{
|
|
//CLogFile::WriteLine( "<Undo> Accept (Not recording)" );
|
|
return;
|
|
}
|
|
|
|
if (!m_currentUndo->IsEmpty())
|
|
{
|
|
const bool persist = ShouldPersist(name);
|
|
if (persist)
|
|
{
|
|
GetIEditor()->SetModifiedFlag();
|
|
}
|
|
|
|
// If accepting new undo object, must clear all redo stack.
|
|
ClearRedoStack();
|
|
|
|
m_currentUndo->SetName(name);
|
|
if (m_bSuperRecording)
|
|
{
|
|
m_superUndo->AddUndoStep(m_currentUndo);
|
|
}
|
|
else
|
|
{
|
|
// Normal recording.
|
|
// Keep max undo steps.
|
|
while (m_undoStack.size() && (m_undoStack.size() >= GetIEditor()->GetEditorSettings()->undoLevels || GetDatabaseSize() > 100 * 1024 * 1024))
|
|
{
|
|
delete m_undoStack.front();
|
|
m_undoStack.pop_front();
|
|
}
|
|
m_undoStack.push_back(m_currentUndo);
|
|
}
|
|
//CLogFile::FormatLine( "Undo Object Accepted (Undo:%d,Redo:%d, Size=%dKb)",m_undoStack.size(),m_redoStack.size(),GetDatabaseSize()/1024 );
|
|
|
|
// If undo accepted, document modified.
|
|
if (persist)
|
|
{
|
|
GetIEditor()->SetModifiedFlag();
|
|
}
|
|
|
|
if (name.compare("Select Object(s)", Qt::CaseInsensitive) == 0)
|
|
{
|
|
GetIEditor()->SetModifiedModule(eModifiedBrushes);
|
|
}
|
|
else if (name.compare("Move Selection", Qt::CaseInsensitive) == 0)
|
|
{
|
|
GetIEditor()->SetModifiedModule(eModifiedBrushes);
|
|
}
|
|
else if (name.compare("SubObject Select", Qt::CaseInsensitive) == 0)
|
|
{
|
|
GetIEditor()->SetModifiedModule(eModifiedBrushes);
|
|
}
|
|
else if (name.compare("Manipulator Drag", Qt::CaseInsensitive) == 0)
|
|
{
|
|
GetIEditor()->SetModifiedModule(eModifiedBrushes);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If no any object was recorded, Cancel undo operation.
|
|
Cancel();
|
|
}
|
|
|
|
m_bRecording = false;
|
|
m_currentUndo = nullptr;
|
|
|
|
SignalNumUndoRedoToListeners();
|
|
|
|
//CLogFile::WriteLine( "<Undo> Accept OK" );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CUndoManager::Cancel()
|
|
{
|
|
//CryLog( "<Undo> Cancel" );
|
|
if (m_bUndoing || m_bRedoing) // If Undoing or redoing now, ignore this calls.
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!m_bRecording)
|
|
{
|
|
return;
|
|
}
|
|
|
|
assert(m_currentUndo != 0);
|
|
|
|
m_bRecording = false;
|
|
|
|
if (!m_currentUndo->IsEmpty())
|
|
{
|
|
// Restore all objects to the state they was at Begin call and throws out all undo objects.
|
|
Restore(true);
|
|
//GetIEditor()->GetLogFile()->FormatLine( "Undo Object Canceled (Undo:%d,Redo:%d)",m_undoStack.size(),m_redoStack.size() );
|
|
}
|
|
|
|
delete m_currentUndo;
|
|
m_currentUndo = nullptr;
|
|
//CLogFile::WriteLine( "<Undo> Cancel OK" );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CUndoManager::Redo(int numSteps)
|
|
{
|
|
GetIEditor()->Notify(eNotify_OnBeginUndoRedo);
|
|
|
|
if (m_bUndoing || m_bRedoing) // If Undoing or redoing now, ignore this calls.
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (m_bRecording || m_bSuperRecording)
|
|
{
|
|
GetIEditor()->GetLogFile()->FormatLine("Cannot Redo while Recording");
|
|
return;
|
|
}
|
|
|
|
m_bRedoing = true;
|
|
BeginUndoTransaction();
|
|
m_bRedoing = false;
|
|
|
|
if (!m_redoStack.empty())
|
|
{
|
|
Suspend();
|
|
while (numSteps-- > 0 && !m_redoStack.empty() && !m_bClearRedoStackQueued)
|
|
{
|
|
m_bRedoing = true;
|
|
CUndoStep* redo = m_redoStack.back();
|
|
redo->Redo();
|
|
m_redoStack.pop_back();
|
|
// Push undo object to redo stack.
|
|
m_undoStack.push_back(redo);
|
|
m_bRedoing = false;
|
|
}
|
|
Resume();
|
|
}
|
|
if (m_suspendCount == 0)
|
|
{
|
|
GetIEditor()->UpdateViews(eUpdateObjects);
|
|
}
|
|
GetIEditor()->GetLogFile()->FormatLine("Redo (Undo:%d,Redo:%d)", m_undoStack.size(), m_redoStack.size());
|
|
GetIEditor()->GetObjectManager()->InvalidateVisibleList();
|
|
|
|
m_bRedoing = true;
|
|
EndUndoTransaction();
|
|
SignalNumUndoRedoToListeners();
|
|
m_bRedoing = false;
|
|
|
|
GetIEditor()->Notify(eNotify_OnEndUndoRedo);
|
|
|
|
if (m_bClearRedoStackQueued)
|
|
{
|
|
ClearRedoStack();
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CUndoManager::Undo(int numSteps)
|
|
{
|
|
GetIEditor()->Notify(eNotify_OnBeginUndoRedo);
|
|
|
|
if (m_bUndoing || m_bRedoing) // If Undoing or redoing now, ignore this calls.
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (m_bRecording || m_bSuperRecording)
|
|
{
|
|
GetIEditor()->GetLogFile()->FormatLine("Cannot Undo while Recording");
|
|
return;
|
|
}
|
|
|
|
m_bUndoing = true;
|
|
BeginUndoTransaction();
|
|
m_bUndoing = false;
|
|
|
|
if (!m_undoStack.empty())
|
|
{
|
|
Suspend();
|
|
while (numSteps-- > 0 && !m_undoStack.empty())
|
|
{
|
|
m_bUndoing = true;
|
|
CUndoStep* undo = m_undoStack.back();
|
|
undo->Undo(true);
|
|
m_undoStack.pop_back();
|
|
// Push undo object to redo stack.
|
|
m_redoStack.push_back(undo);
|
|
m_bUndoing = false;
|
|
}
|
|
Resume();
|
|
}
|
|
// Update viewports.
|
|
if (m_suspendCount == 0)
|
|
{
|
|
GetIEditor()->UpdateViews(eUpdateObjects);
|
|
}
|
|
GetIEditor()->GetLogFile()->FormatLine("Undo (Undo:%d,Redo:%d)", m_undoStack.size(), m_redoStack.size());
|
|
GetIEditor()->GetObjectManager()->InvalidateVisibleList();
|
|
|
|
m_bUndoing = true;
|
|
EndUndoTransaction();
|
|
SignalNumUndoRedoToListeners();
|
|
m_bUndoing = false;
|
|
|
|
GetIEditor()->Notify(eNotify_OnEndUndoRedo);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CUndoManager::RecordUndo(IUndoObject* obj)
|
|
{
|
|
//CryLog( "<Undo> RecordUndo Name=%s",obj->GetDescription() );
|
|
|
|
if (m_bUndoing || m_bRedoing) // If Undoing or redoing now, ignore this calls.
|
|
{
|
|
//CLogFile::WriteLine( "<Undo> RecordUndo (Undoing or Redoing)" );
|
|
obj->Release();
|
|
return;
|
|
}
|
|
|
|
if (m_bRecording && (m_suspendCount == 0))
|
|
{
|
|
assert(m_currentUndo != 0);
|
|
m_currentUndo->AddUndoObject(obj);
|
|
//CLogFile::FormatLine( "Undo Object Added: %s",obj->GetDescription() );
|
|
}
|
|
else
|
|
{
|
|
//CLogFile::WriteLine( "<Undo> RecordUndo (Not Recording)" );
|
|
// Ignore this object.
|
|
obj->Release();
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CUndoManager::ClearRedoStack()
|
|
{
|
|
if (m_bRedoing)
|
|
{
|
|
m_bClearRedoStackQueued = true;
|
|
return;
|
|
}
|
|
m_bClearRedoStackQueued = false;
|
|
|
|
for (std::list<CUndoStep*>::iterator it = m_redoStack.begin(); it != m_redoStack.end(); it++)
|
|
{
|
|
delete *it;
|
|
}
|
|
m_redoStack.clear();
|
|
|
|
SignalNumUndoRedoToListeners();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CUndoManager::ClearUndoStack()
|
|
{
|
|
for (std::list<CUndoStep*>::iterator it = m_undoStack.begin(); it != m_undoStack.end(); it++)
|
|
{
|
|
delete *it;
|
|
}
|
|
m_undoStack.clear();
|
|
|
|
SignalNumUndoRedoToListeners();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CUndoManager::ClearUndoStack(int num)
|
|
{
|
|
int i = num;
|
|
while (i > 0 && !m_undoStack.empty())
|
|
{
|
|
delete m_undoStack.front();
|
|
m_undoStack.pop_front();
|
|
i--;
|
|
}
|
|
|
|
SignalNumUndoRedoToListeners();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CUndoManager::ClearRedoStack(int num)
|
|
{
|
|
int i = num;
|
|
while (i > 0 && !m_redoStack.empty())
|
|
{
|
|
delete m_redoStack.back();
|
|
m_redoStack.pop_back();
|
|
i--;
|
|
}
|
|
|
|
SignalNumUndoRedoToListeners();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CUndoManager::IsHaveRedo() const
|
|
{
|
|
return !m_redoStack.empty();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CUndoManager::IsHaveUndo() const
|
|
{
|
|
return !m_undoStack.empty();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CUndoManager::Suspend()
|
|
{
|
|
m_suspendCount++;
|
|
//CLogFile::FormatLine( "<Undo> Suspend %d",m_suspendCount );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CUndoManager::Resume()
|
|
{
|
|
assert(m_suspendCount >= 0);
|
|
if (m_suspendCount > 0)
|
|
{
|
|
m_suspendCount--;
|
|
}
|
|
//CLogFile::FormatLine( "<Undo> Resume %d",m_suspendCount );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CUndoManager::SuperBegin()
|
|
{
|
|
//CLogFile::FormatLine( "<Undo> SuperBegin (SuspendCount%d)",m_suspendCount );
|
|
if (m_bUndoing || m_bRedoing) // If Undoing or redoing now, ignore this calls.
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_bSuperRecording = true;
|
|
m_superUndo = new CSuperUndoStep;
|
|
//CLogFile::WriteLine( "<Undo> SuperBegin OK" );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CUndoManager::SuperAccept(const QString& name)
|
|
{
|
|
//CLogFile::WriteLine( "<Undo> SupperAccept" );
|
|
if (m_bUndoing || m_bRedoing) // If Undoing or redoing now, ignore this calls.
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!m_bSuperRecording)
|
|
{
|
|
return;
|
|
}
|
|
|
|
assert(m_superUndo != 0);
|
|
|
|
if (m_bRecording)
|
|
{
|
|
Accept(name);
|
|
}
|
|
|
|
if (!m_superUndo->IsEmpty())
|
|
{
|
|
m_superUndo->SetName(name);
|
|
// Keep max undo steps.
|
|
while (m_undoStack.size() && (m_undoStack.size() >= GetIEditor()->GetEditorSettings()->undoLevels || GetDatabaseSize() > 100 * 1024 * 1024))
|
|
{
|
|
delete m_undoStack.front();
|
|
m_undoStack.pop_front();
|
|
}
|
|
m_undoStack.push_back(m_superUndo);
|
|
}
|
|
else
|
|
{
|
|
// If no any object was recorded, Cancel undo operation.
|
|
SuperCancel();
|
|
}
|
|
|
|
//CLogFile::FormatLine( "Undo Object Accepted (Undo:%d,Redo:%d)",m_undoStack.size(),m_redoStack.size() );
|
|
m_bSuperRecording = false;
|
|
m_superUndo = nullptr;
|
|
//CLogFile::WriteLine( "<Undo> SupperAccept OK" );
|
|
|
|
SignalNumUndoRedoToListeners();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CUndoManager::SuperCancel()
|
|
{
|
|
//CLogFile::WriteLine( "<Undo> SuperCancel" );
|
|
if (m_bUndoing || m_bRedoing) // If Undoing or redoing now, ignore this calls.
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!m_bSuperRecording)
|
|
{
|
|
return;
|
|
}
|
|
|
|
assert(m_superUndo != 0);
|
|
|
|
if (m_bRecording)
|
|
{
|
|
Cancel();
|
|
}
|
|
|
|
Suspend();
|
|
//! Undo all changes already made.
|
|
m_superUndo->Undo(false); // Undo not by Undo command (no need to store Redo)
|
|
Resume();
|
|
|
|
m_bSuperRecording = false;
|
|
delete m_superUndo;
|
|
m_superUndo = nullptr;
|
|
//CLogFile::WriteLine( "<Undo> SuperCancel OK" );
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
int CUndoManager::GetUndoStackLen() const
|
|
{
|
|
return static_cast<int>(m_undoStack.size());
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
int CUndoManager::GetRedoStackLen() const
|
|
{
|
|
return static_cast<int>(m_redoStack.size());
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
std::vector<QString> CUndoManager::GetUndoStackNames() const
|
|
{
|
|
std::vector<QString> undos(m_undoStack.size());
|
|
int i = 0;
|
|
QString text;
|
|
|
|
for (auto it = m_undoStack.begin(); it != m_undoStack.end(); it++)
|
|
{
|
|
QString objNames = (*it)->GetObjectNames();
|
|
text = (*it)->GetName() + objNames;
|
|
if (text.length() > UNDOREDO_BUTTON_POPUP_TEXT_WIDTH)
|
|
{
|
|
undos[i++] = (*it)->GetName() + UNDOREDO_MULTIPLE_OBJECTS_TEXT;
|
|
}
|
|
else
|
|
{
|
|
undos[i++] = (*it)->GetName() + (objNames.isEmpty() ? "" : " (" + objNames + ")");
|
|
}
|
|
}
|
|
|
|
return undos;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
std::vector<QString> CUndoManager::GetRedoStackNames() const
|
|
{
|
|
std::vector<QString> redos(m_redoStack.size());
|
|
int i = 0;
|
|
QString text;
|
|
|
|
for (auto it = m_redoStack.begin(); it != m_redoStack.end(); it++)
|
|
{
|
|
text = (*it)->GetName() + (*it)->GetObjectNames();
|
|
if (text.length() > UNDOREDO_BUTTON_POPUP_TEXT_WIDTH)
|
|
{
|
|
redos[i++] = (*it)->GetName() + UNDOREDO_MULTIPLE_OBJECTS_TEXT;
|
|
}
|
|
else
|
|
{
|
|
redos[i++] = (*it)->GetName() + " (" + (*it)->GetObjectNames() + ")";
|
|
}
|
|
}
|
|
|
|
return redos;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
int CUndoManager::GetDatabaseSize()
|
|
{
|
|
int size = 0;
|
|
{
|
|
for (std::list<CUndoStep*>::iterator it = m_undoStack.begin(); it != m_undoStack.end(); it++)
|
|
{
|
|
size += (*it)->GetSize();
|
|
}
|
|
}
|
|
{
|
|
for (std::list<CUndoStep*>::iterator it = m_redoStack.begin(); it != m_redoStack.end(); it++)
|
|
{
|
|
size += (*it)->GetSize();
|
|
}
|
|
}
|
|
return size;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CUndoManager::Flush()
|
|
{
|
|
m_bRecording = false;
|
|
ClearRedoStack();
|
|
ClearUndoStack();
|
|
|
|
delete m_superUndo;
|
|
delete m_currentUndo;
|
|
m_superUndo = nullptr;
|
|
m_currentUndo = nullptr;
|
|
|
|
SignalUndoFlushedToListeners();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CUndoStep* CUndoManager::GetNextUndo()
|
|
{
|
|
if (!m_undoStack.empty())
|
|
{
|
|
return m_undoStack.back();
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CUndoStep* CUndoManager::GetNextRedo()
|
|
{
|
|
if (!m_redoStack.empty())
|
|
{
|
|
return m_redoStack.back();
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CUndoManager::SetMaxUndoStep(int steps)
|
|
{
|
|
GetIEditor()->GetEditorSettings()->undoLevels = steps;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
int CUndoManager::GetMaxUndoStep() const
|
|
{
|
|
return GetIEditor()->GetEditorSettings()->undoLevels;
|
|
}
|
|
|
|
void CUndoManager::AddListener(IUndoManagerListener* pListener)
|
|
{
|
|
stl::push_back_unique(m_listeners, pListener);
|
|
}
|
|
|
|
void CUndoManager::RemoveListener(IUndoManagerListener* pListener)
|
|
{
|
|
stl::find_and_erase(m_listeners, pListener);
|
|
}
|
|
|
|
void CUndoManager::BeginUndoTransaction()
|
|
{
|
|
for (auto iter = m_listeners.begin(); iter != m_listeners.end(); ++iter)
|
|
{
|
|
(*iter)->BeginUndoTransaction();
|
|
}
|
|
}
|
|
|
|
void CUndoManager::EndUndoTransaction()
|
|
{
|
|
for (auto iter = m_listeners.begin(); iter != m_listeners.end(); ++iter)
|
|
{
|
|
(*iter)->EndUndoTransaction();
|
|
}
|
|
}
|
|
|
|
void CUndoManager::BeginRestoreTransaction()
|
|
{
|
|
for (auto iter = m_listeners.begin(); iter != m_listeners.end(); ++iter)
|
|
{
|
|
(*iter)->BeginRestoreTransaction();
|
|
}
|
|
}
|
|
|
|
void CUndoManager::EndRestoreTransaction()
|
|
{
|
|
for (auto iter = m_listeners.begin(); iter != m_listeners.end(); ++iter)
|
|
{
|
|
(*iter)->EndRestoreTransaction();
|
|
}
|
|
}
|
|
|
|
void CUndoManager::SignalNumUndoRedoToListeners()
|
|
{
|
|
for (IUndoManagerListener* listener : m_listeners)
|
|
{
|
|
listener->SignalNumUndoRedo(static_cast<unsigned int>(m_undoStack.size()), static_cast<unsigned int>(m_redoStack.size()));
|
|
}
|
|
}
|
|
|
|
void CUndoManager::SignalUndoFlushedToListeners()
|
|
{
|
|
for (IUndoManagerListener* listener : m_listeners)
|
|
{
|
|
listener->UndoStackFlushed();
|
|
}
|
|
}
|
|
|
|
bool CUndoManager::IsUndoRecording() const
|
|
{
|
|
return (m_bRecording || m_bSuperRecording) && m_suspendCount == 0;
|
|
}
|
|
|
|
bool CUndoManager::IsUndoSuspended() const
|
|
{
|
|
return m_suspendCount != 0;
|
|
}
|