SHADE_Y3/SHADE_Managed/src/Editor/UndoRedoStack.cxx

339 lines
12 KiB
C++

/************************************************************************************//*!
\file UndoRedoStack.cxx
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Sep 29, 2022
\brief Contains the definition of the functions for the UndoRedoStack managed
class.
Note: This file is written in C++17/CLI.
Copyright (C) 2022 DigiPen Institute of Technology.
Reproduction or disclosure of this file or its contents without the prior written consent
of DigiPen Institute of Technology is prohibited.
*//*************************************************************************************/
// Precompiled Headers
#include "SHpch.h"
// Primary Header
#include "UndoRedoStack.hxx"
// External Dependencies
#include "Editor/SHEditorUI.h"
// Project Headers
#include "Utility/Debug.hxx"
#include "Utility/Convert.hxx"
#include "Scripts/ScriptStore.hxx"
#include "Serialisation/SerialisationUtilities.hxx"
namespace SHADE
{
/*---------------------------------------------------------------------------------*/
/* UndoRedoStack - Properties */
/*---------------------------------------------------------------------------------*/
bool UndoRedoStack::UndoActionPresent::get()
{
return commandStack->Count > 0 && latestActionIndex >= 0;
}
bool UndoRedoStack::RedoActionPresent::get()
{
const int REDO_ACTION_INDEX = latestActionIndex + 1;
return REDO_ACTION_INDEX >= 0 && REDO_ACTION_INDEX < commandStack->Count;
}
/*---------------------------------------------------------------------------------*/
/* UndoRedoStack - Usage Functions */
/*---------------------------------------------------------------------------------*/
void UndoRedoStack::Add(ICommand^ command)
{
// Erase any other actions ahead of the current action
if (latestActionIndex >= 0 && latestActionIndex < commandStack->Count - 1)
{
commandStack->RemoveRange(latestActionIndex, commandStack->Count - latestActionIndex);
}
// Add the command
commandStack->Add(command);
// Set the latest command
latestActionIndex = commandStack->Count - 1;
}
void UndoRedoStack::Undo()
{
if (!UndoActionPresent)
return;
ICommand^ cmd = commandStack[latestActionIndex];
cmd->Unexceute();
--latestActionIndex;
}
void UndoRedoStack::Redo()
{
if (!RedoActionPresent)
return;
const int REDO_ACTION_INDEX = latestActionIndex + 1;
ICommand^ cmd = commandStack[REDO_ACTION_INDEX];
cmd->Execute();
++latestActionIndex;
}
/*---------------------------------------------------------------------------------*/
/* FieldChangeCommand - Constructor */
/*---------------------------------------------------------------------------------*/
FieldChangeCommand::FieldChangeCommand(System::Object^ obj, System::Reflection::FieldInfo^ field, System::Object^ newData, System::Object^ oldData)
: objectToChange { obj }
, field { field }
, newData { newData }
, oldData { oldData }
{}
/*---------------------------------------------------------------------------------*/
/* FieldChangeCommand - ICommand Functions */
/*---------------------------------------------------------------------------------*/
bool FieldChangeCommand::Execute()
{
if (field && objectToChange)
{
field->SetValue(objectToChange, newData);
return true;
}
return false;
}
bool FieldChangeCommand::Unexceute()
{
if (field && objectToChange)
{
field->SetValue(objectToChange, oldData);
return true;
}
return false;
}
bool FieldChangeCommand::Merge(ICommand^ command)
{
FieldChangeCommand^ otherCommand = safe_cast<FieldChangeCommand^>(command);
if (otherCommand == nullptr)
{
Debug::LogWarning("[Field Change Command] Attempted to merge two incompatible commands!");
return false;
}
// Only merge if they are workng on the same object and field
if (field == otherCommand->field && objectToChange == otherCommand->objectToChange)
{
newData = otherCommand->newData;
return true;
}
return false;
}
/*---------------------------------------------------------------------------------*/
/* ListElementChangeCommand - Constructor */
/*---------------------------------------------------------------------------------*/
ListElementChangeCommand::ListElementChangeCommand(System::Collections::IList^ list, int index, System::Object^ newData, System::Object^ oldData)
: list { list }
, index { index }
, newData { newData }
, oldData { oldData }
{}
/*---------------------------------------------------------------------------------*/
/* ListElementChangeCommand - ICommand Functions */
/*---------------------------------------------------------------------------------*/
bool ListElementChangeCommand::Execute()
{
if (list && index < list->Count)
{
list[index] = newData;
return true;
}
return false;
}
bool ListElementChangeCommand::Unexceute()
{
if (list && index < list->Count)
{
list[index] = oldData;
return true;
}
return false;
}
bool ListElementChangeCommand::Merge(ICommand^ command)
{
ListElementChangeCommand^ otherCommand = safe_cast<ListElementChangeCommand^>(command);
if (otherCommand == nullptr)
{
Debug::LogWarning("[Field Change Command] Attempted to merge two incompatible commands!");
return false;
}
if (command && list == otherCommand->list && index == otherCommand->index)
{
newData = otherCommand->newData;
return true;
}
}
/*---------------------------------------------------------------------------------*/
/* ListElementAddCommand - Constructor */
/*---------------------------------------------------------------------------------*/
ListElementAddCommand::ListElementAddCommand(System::Collections::IList^ list, int addIndex, System::Object^ data)
: list { list }
, addIndex { addIndex }
, data { data }
{}
/*---------------------------------------------------------------------------------*/
/* ListElementAddCommand - ICommand Functions */
/*---------------------------------------------------------------------------------*/
bool ListElementAddCommand::Execute()
{
if (list)
{
list->Insert(addIndex, data);
return true;
}
return false;
}
bool ListElementAddCommand::Unexceute()
{
if (list && addIndex < list->Count)
{
list->RemoveAt(addIndex);
return true;
}
return false;
}
bool ListElementAddCommand::Merge(ICommand^)
{
// Not allowed
return false;
}
/*---------------------------------------------------------------------------------*/
/* ListElementRemoveCommand - Constructor */
/*---------------------------------------------------------------------------------*/
ListElementRemoveCommand::ListElementRemoveCommand(System::Collections::IList^ list, int removeIndex, System::Object^ data)
: list { list }
, removeIndex { removeIndex }
, data { data }
{}
/*---------------------------------------------------------------------------------*/
/* ListElementRemoveCommand - ICommand Functions */
/*---------------------------------------------------------------------------------*/
bool ListElementRemoveCommand::Execute()
{
if (list && removeIndex < list->Count)
{
list->RemoveAt(removeIndex);
return true;
}
return false;
}
bool ListElementRemoveCommand::Unexceute()
{
if (list)
{
list->Insert(removeIndex, data);
return true;
}
return false;
}
bool ListElementRemoveCommand::Merge(ICommand^)
{
// Not allowed
return false;
}
/*---------------------------------------------------------------------------------*/
/* ScriptAddCommand - Constructor */
/*---------------------------------------------------------------------------------*/
ScriptAddCommand::ScriptAddCommand(EntityID id, Script^ script)
: entity { id }
, typeName { script->GetType()->FullName }
, serialisedScript { SerialisationUtilities::Serialise(script) }
, insertedIndex { ScriptStore::GetScriptIndex(script) }
{}
/*---------------------------------------------------------------------------------*/
/* ScriptAddCommand - ICommand Functions */
/*---------------------------------------------------------------------------------*/
bool ScriptAddCommand::Execute()
{
Script^ script = nullptr;
if (ScriptStore::AddScriptViaNameWithRef(entity, typeName, script))
{
SerialisationUtilities::Deserialise(script, serialisedScript);
insertedIndex = ScriptStore::GetScriptIndex(script);
return true;
}
return false;
}
bool ScriptAddCommand::Unexceute()
{
return ScriptStore::RemoveScript(entity, insertedIndex);
}
bool ScriptAddCommand::Merge(ICommand^)
{
// Not allowed
return false;
}
/*---------------------------------------------------------------------------------*/
/* ScriptRemoveCommand - Constructor */
/*---------------------------------------------------------------------------------*/
ScriptRemoveCommand::ScriptRemoveCommand(EntityID id, Script^ script, int index)
: entity{ id }
, typeName{ script->GetType()->FullName }
, serialisedScript{ SerialisationUtilities::Serialise(script) }
, originalIndex { index }
{}
/*---------------------------------------------------------------------------------*/
/* ScriptRemoveCommand - ICommand Functions */
/*---------------------------------------------------------------------------------*/
bool ScriptRemoveCommand::Execute()
{
return ScriptStore::RemoveScript(entity, originalIndex);
}
bool ScriptRemoveCommand::Unexceute()
{
Script^ script = nullptr;
if (ScriptStore::AddScriptViaNameWithRef(entity, typeName, script, originalIndex))
{
SerialisationUtilities::Deserialise(script, serialisedScript);
return true;
}
return false;
}
bool ScriptRemoveCommand::Merge(ICommand^)
{
// Not allowed
return false;
}
}