BsUndoRedo.cpp 5.2 KB

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