BsUndoRedo.cpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "UndoRedo/BsUndoRedo.h"
  4. #include "UndoRedo/BsEditorCommand.h"
  5. namespace bs
  6. {
  7. const UINT32 UndoRedo::MAX_STACK_ELEMENTS = 1000;
  8. UndoRedo::UndoRedo()
  9. : mUndoStack(nullptr), mRedoStack(nullptr), mUndoStackPtr(0), mUndoNumElements(0), mRedoStackPtr(0)
  10. , mRedoNumElements(0), mNextCommandId(0)
  11. {
  12. mUndoStack = bs_newN<SPtr<EditorCommand>>(MAX_STACK_ELEMENTS);
  13. mRedoStack = bs_newN<SPtr<EditorCommand>>(MAX_STACK_ELEMENTS);
  14. }
  15. UndoRedo::~UndoRedo()
  16. {
  17. clear();
  18. bs_deleteN(mUndoStack, MAX_STACK_ELEMENTS);
  19. bs_deleteN(mRedoStack, MAX_STACK_ELEMENTS);
  20. }
  21. void UndoRedo::undo()
  22. {
  23. if(mUndoNumElements == 0)
  24. return;
  25. SPtr<EditorCommand> command = removeLastFromUndoStack();
  26. mRedoStackPtr = (mRedoStackPtr + 1) % MAX_STACK_ELEMENTS;
  27. mRedoStack[mRedoStackPtr] = command;
  28. mRedoNumElements = std::min(mRedoNumElements + 1, MAX_STACK_ELEMENTS);
  29. command->revert();
  30. }
  31. void UndoRedo::redo()
  32. {
  33. if(mRedoNumElements == 0)
  34. return;
  35. SPtr<EditorCommand> command = mRedoStack[mRedoStackPtr];
  36. mRedoStack[mRedoStackPtr] = SPtr<EditorCommand>();
  37. mRedoStackPtr = (mRedoStackPtr - 1) % MAX_STACK_ELEMENTS;
  38. mRedoNumElements--;
  39. addToUndoStack(command);
  40. command->commit();
  41. }
  42. void UndoRedo::pushGroup(const String& name)
  43. {
  44. mGroups.push(GroupData());
  45. GroupData& newGroup = mGroups.top();
  46. newGroup.name = name;
  47. newGroup.numEntries = 0;
  48. clearRedoStack();
  49. }
  50. void UndoRedo::popGroup(const String& name)
  51. {
  52. if(mGroups.empty())
  53. BS_EXCEPT(InvalidStateException, "Attempting to pop an UndoRedo group that doesn't exist: " + name);
  54. GroupData& topGroup = mGroups.top();
  55. if(topGroup.name != name)
  56. BS_EXCEPT(InvalidStateException, "Attempting to pop invalid UndoRedo group. Got: " + name + ". Expected: " + topGroup.name);
  57. for(UINT32 i = 0; i < topGroup.numEntries; i++)
  58. {
  59. if (mUndoStack[mUndoStackPtr] != nullptr)
  60. mUndoStack[mUndoStackPtr]->onCommandRemoved();
  61. mUndoStack[mUndoStackPtr] = SPtr<EditorCommand>();
  62. mUndoStackPtr = (mUndoStackPtr - 1) % MAX_STACK_ELEMENTS;
  63. mUndoNumElements--;
  64. }
  65. mGroups.pop();
  66. clearRedoStack();
  67. }
  68. void UndoRedo::registerCommand(const SPtr<EditorCommand>& command)
  69. {
  70. command->mId = mNextCommandId++;
  71. command->onCommandAdded();
  72. SPtr<EditorCommand> existingCommand = addToUndoStack(command);
  73. if (existingCommand != nullptr)
  74. existingCommand->onCommandRemoved();
  75. clearRedoStack();
  76. }
  77. UINT32 UndoRedo::getTopCommandId() const
  78. {
  79. if (mUndoNumElements > 0)
  80. return mUndoStack[mUndoStackPtr]->mId;
  81. return 0;
  82. }
  83. void UndoRedo::popCommand(UINT32 id)
  84. {
  85. UINT32 undoPtr = mUndoStackPtr;
  86. for (UINT32 i = 0; i < mUndoNumElements; i++)
  87. {
  88. if (mUndoStack[undoPtr]->mId == id)
  89. {
  90. if (mUndoStack[undoPtr] != nullptr)
  91. mUndoStack[undoPtr]->onCommandRemoved();
  92. mUndoStack[undoPtr] = SPtr<EditorCommand>();
  93. for (UINT32 j = mUndoNumElements - i; j < (mUndoNumElements - 1); j++)
  94. {
  95. UINT32 nextUndoPtr = (undoPtr + 1) % MAX_STACK_ELEMENTS;
  96. std::swap(mUndoStack[undoPtr], mUndoStack[nextUndoPtr]);
  97. undoPtr = nextUndoPtr;
  98. }
  99. mUndoStackPtr = (mUndoStackPtr - 1) % MAX_STACK_ELEMENTS;
  100. mUndoNumElements--;
  101. break;
  102. }
  103. undoPtr = (undoPtr - 1) % MAX_STACK_ELEMENTS;
  104. }
  105. UINT32 redoPtr = mRedoStackPtr;
  106. for (UINT32 i = 0; i < mRedoNumElements; i++)
  107. {
  108. if (mRedoStack[redoPtr]->mId == id)
  109. {
  110. if (mRedoStack[redoPtr] != nullptr)
  111. mRedoStack[redoPtr]->onCommandRemoved();
  112. mRedoStack[redoPtr] = SPtr<EditorCommand>();
  113. for (UINT32 j = mRedoNumElements - i; j < (mRedoNumElements - 1); j++)
  114. {
  115. UINT32 nextRedoPtr = (redoPtr + 1) % MAX_STACK_ELEMENTS;
  116. std::swap(mRedoStack[redoPtr], mRedoStack[nextRedoPtr]);
  117. redoPtr = nextRedoPtr;
  118. }
  119. mRedoStackPtr = (mRedoStackPtr - 1) % MAX_STACK_ELEMENTS;
  120. mRedoNumElements--;
  121. break;
  122. }
  123. redoPtr = (redoPtr - 1) % MAX_STACK_ELEMENTS;
  124. }
  125. }
  126. void UndoRedo::clear()
  127. {
  128. clearUndoStack();
  129. clearRedoStack();
  130. }
  131. SPtr<EditorCommand> UndoRedo::removeLastFromUndoStack()
  132. {
  133. SPtr<EditorCommand> command = mUndoStack[mUndoStackPtr];
  134. mUndoStack[mUndoStackPtr] = SPtr<EditorCommand>();
  135. mUndoStackPtr = (mUndoStackPtr - 1) % MAX_STACK_ELEMENTS;
  136. mUndoNumElements--;
  137. if(!mGroups.empty())
  138. {
  139. GroupData& topGroup = mGroups.top();
  140. if(topGroup.numEntries == 0)
  141. {
  142. BS_EXCEPT(InvalidStateException, "Removing an element from UndoRedo stack while in an " \
  143. "invalid UndoRedo group. Current group: " + topGroup.name);
  144. }
  145. topGroup.numEntries--;
  146. }
  147. return command;
  148. }
  149. SPtr<EditorCommand> UndoRedo::addToUndoStack(const SPtr<EditorCommand>& command)
  150. {
  151. mUndoStackPtr = (mUndoStackPtr + 1) % MAX_STACK_ELEMENTS;
  152. SPtr<EditorCommand> existingCommand = mUndoStack[mUndoStackPtr];
  153. mUndoStack[mUndoStackPtr] = command;
  154. mUndoNumElements = std::min(mUndoNumElements + 1, MAX_STACK_ELEMENTS);
  155. if(!mGroups.empty())
  156. {
  157. GroupData& topGroup = mGroups.top();
  158. topGroup.numEntries = std::min(topGroup.numEntries + 1, MAX_STACK_ELEMENTS);
  159. }
  160. return existingCommand;
  161. }
  162. void UndoRedo::clearUndoStack()
  163. {
  164. while(mUndoNumElements > 0)
  165. {
  166. if (mUndoStack[mUndoStackPtr] != nullptr)
  167. mUndoStack[mUndoStackPtr]->onCommandRemoved();
  168. mUndoStack[mUndoStackPtr] = SPtr<EditorCommand>();
  169. mUndoStackPtr = (mUndoStackPtr - 1) % MAX_STACK_ELEMENTS;
  170. mUndoNumElements--;
  171. }
  172. while(!mGroups.empty())
  173. mGroups.pop();
  174. }
  175. void UndoRedo::clearRedoStack()
  176. {
  177. while(mRedoNumElements > 0)
  178. {
  179. if (mRedoStack[mRedoStackPtr] != nullptr)
  180. mRedoStack[mRedoStackPtr]->onCommandRemoved();
  181. mRedoStack[mRedoStackPtr] = SPtr<EditorCommand>();
  182. mRedoStackPtr = (mRedoStackPtr - 1) % MAX_STACK_ELEMENTS;
  183. mRedoNumElements--;
  184. }
  185. }
  186. }