339 lines
12 KiB
C++
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;
|
|
}
|
|
}
|