BsUndoRedo.cpp 5.0 KB

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