juce_UndoManager.cpp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. namespace juce
  20. {
  21. struct UndoManager::ActionSet
  22. {
  23. ActionSet (const String& transactionName) : name (transactionName)
  24. {}
  25. bool perform() const
  26. {
  27. for (auto* a : actions)
  28. if (! a->perform())
  29. return false;
  30. return true;
  31. }
  32. bool undo() const
  33. {
  34. for (int i = actions.size(); --i >= 0;)
  35. if (! actions.getUnchecked(i)->undo())
  36. return false;
  37. return true;
  38. }
  39. int getTotalSize() const
  40. {
  41. int total = 0;
  42. for (auto* a : actions)
  43. total += a->getSizeInUnits();
  44. return total;
  45. }
  46. OwnedArray<UndoableAction> actions;
  47. String name;
  48. Time time { Time::getCurrentTime() };
  49. };
  50. //==============================================================================
  51. UndoManager::UndoManager (int maxNumberOfUnitsToKeep, int minimumTransactions)
  52. {
  53. setMaxNumberOfStoredUnits (maxNumberOfUnitsToKeep, minimumTransactions);
  54. }
  55. UndoManager::~UndoManager()
  56. {
  57. }
  58. //==============================================================================
  59. void UndoManager::clearUndoHistory()
  60. {
  61. transactions.clear();
  62. totalUnitsStored = 0;
  63. nextIndex = 0;
  64. sendChangeMessage();
  65. }
  66. int UndoManager::getNumberOfUnitsTakenUpByStoredCommands() const
  67. {
  68. return totalUnitsStored;
  69. }
  70. void UndoManager::setMaxNumberOfStoredUnits (int maxUnits, int minTransactions)
  71. {
  72. maxNumUnitsToKeep = jmax (1, maxUnits);
  73. minimumTransactionsToKeep = jmax (1, minTransactions);
  74. }
  75. //==============================================================================
  76. bool UndoManager::perform (UndoableAction* newAction, const String& actionName)
  77. {
  78. if (perform (newAction))
  79. {
  80. if (actionName.isNotEmpty())
  81. setCurrentTransactionName (actionName);
  82. return true;
  83. }
  84. return false;
  85. }
  86. bool UndoManager::perform (UndoableAction* newAction)
  87. {
  88. if (newAction != nullptr)
  89. {
  90. std::unique_ptr<UndoableAction> action (newAction);
  91. if (isPerformingUndoRedo())
  92. {
  93. jassertfalse; // Don't call perform() recursively from the UndoableAction::perform()
  94. // or undo() methods, or else these actions will be discarded!
  95. return false;
  96. }
  97. if (action->perform())
  98. {
  99. auto* actionSet = getCurrentSet();
  100. if (actionSet != nullptr && ! newTransaction)
  101. {
  102. if (auto* lastAction = actionSet->actions.getLast())
  103. {
  104. if (auto coalescedAction = lastAction->createCoalescedAction (action.get()))
  105. {
  106. action.reset (coalescedAction);
  107. totalUnitsStored -= lastAction->getSizeInUnits();
  108. actionSet->actions.removeLast();
  109. }
  110. }
  111. }
  112. else
  113. {
  114. actionSet = new ActionSet (newTransactionName);
  115. transactions.insert (nextIndex, actionSet);
  116. ++nextIndex;
  117. }
  118. totalUnitsStored += action->getSizeInUnits();
  119. actionSet->actions.add (std::move (action));
  120. newTransaction = false;
  121. moveFutureTransactionsToStash();
  122. dropOldTransactionsIfTooLarge();
  123. sendChangeMessage();
  124. return true;
  125. }
  126. }
  127. return false;
  128. }
  129. void UndoManager::moveFutureTransactionsToStash()
  130. {
  131. if (nextIndex < transactions.size())
  132. {
  133. stashedFutureTransactions.clear();
  134. while (nextIndex < transactions.size())
  135. {
  136. auto* removed = transactions.removeAndReturn (nextIndex);
  137. stashedFutureTransactions.add (removed);
  138. totalUnitsStored -= removed->getTotalSize();
  139. }
  140. }
  141. }
  142. void UndoManager::restoreStashedFutureTransactions()
  143. {
  144. while (nextIndex < transactions.size())
  145. {
  146. totalUnitsStored -= transactions.getUnchecked (nextIndex)->getTotalSize();
  147. transactions.remove (nextIndex);
  148. }
  149. for (auto* stashed : stashedFutureTransactions)
  150. {
  151. transactions.add (stashed);
  152. totalUnitsStored += stashed->getTotalSize();
  153. }
  154. stashedFutureTransactions.clearQuick (false);
  155. }
  156. void UndoManager::dropOldTransactionsIfTooLarge()
  157. {
  158. while (nextIndex > 0
  159. && totalUnitsStored > maxNumUnitsToKeep
  160. && transactions.size() > minimumTransactionsToKeep)
  161. {
  162. totalUnitsStored -= transactions.getFirst()->getTotalSize();
  163. transactions.remove (0);
  164. --nextIndex;
  165. // if this fails, then some actions may not be returning
  166. // consistent results from their getSizeInUnits() method
  167. jassert (totalUnitsStored >= 0);
  168. }
  169. }
  170. void UndoManager::beginNewTransaction()
  171. {
  172. beginNewTransaction ({});
  173. }
  174. void UndoManager::beginNewTransaction (const String& actionName)
  175. {
  176. newTransaction = true;
  177. newTransactionName = actionName;
  178. }
  179. void UndoManager::setCurrentTransactionName (const String& newName)
  180. {
  181. if (newTransaction)
  182. newTransactionName = newName;
  183. else if (auto* action = getCurrentSet())
  184. action->name = newName;
  185. }
  186. String UndoManager::getCurrentTransactionName() const
  187. {
  188. if (auto* action = getCurrentSet())
  189. return action->name;
  190. return newTransactionName;
  191. }
  192. //==============================================================================
  193. UndoManager::ActionSet* UndoManager::getCurrentSet() const { return transactions[nextIndex - 1]; }
  194. UndoManager::ActionSet* UndoManager::getNextSet() const { return transactions[nextIndex]; }
  195. bool UndoManager::isPerformingUndoRedo() const { return isInsideUndoRedoCall; }
  196. bool UndoManager::canUndo() const { return getCurrentSet() != nullptr; }
  197. bool UndoManager::canRedo() const { return getNextSet() != nullptr; }
  198. bool UndoManager::undo()
  199. {
  200. if (auto* s = getCurrentSet())
  201. {
  202. const ScopedValueSetter<bool> setter (isInsideUndoRedoCall, true);
  203. if (s->undo())
  204. --nextIndex;
  205. else
  206. clearUndoHistory();
  207. beginNewTransaction();
  208. sendChangeMessage();
  209. return true;
  210. }
  211. return false;
  212. }
  213. bool UndoManager::redo()
  214. {
  215. if (auto* s = getNextSet())
  216. {
  217. const ScopedValueSetter<bool> setter (isInsideUndoRedoCall, true);
  218. if (s->perform())
  219. ++nextIndex;
  220. else
  221. clearUndoHistory();
  222. beginNewTransaction();
  223. sendChangeMessage();
  224. return true;
  225. }
  226. return false;
  227. }
  228. String UndoManager::getUndoDescription() const
  229. {
  230. if (auto* s = getCurrentSet())
  231. return s->name;
  232. return {};
  233. }
  234. String UndoManager::getRedoDescription() const
  235. {
  236. if (auto* s = getNextSet())
  237. return s->name;
  238. return {};
  239. }
  240. StringArray UndoManager::getUndoDescriptions() const
  241. {
  242. StringArray descriptions;
  243. for (int i = nextIndex;;)
  244. {
  245. if (auto* t = transactions[--i])
  246. descriptions.add (t->name);
  247. else
  248. return descriptions;
  249. }
  250. }
  251. StringArray UndoManager::getRedoDescriptions() const
  252. {
  253. StringArray descriptions;
  254. for (int i = nextIndex;;)
  255. {
  256. if (auto* t = transactions[i++])
  257. descriptions.add (t->name);
  258. else
  259. return descriptions;
  260. }
  261. }
  262. Time UndoManager::getTimeOfUndoTransaction() const
  263. {
  264. if (auto* s = getCurrentSet())
  265. return s->time;
  266. return {};
  267. }
  268. Time UndoManager::getTimeOfRedoTransaction() const
  269. {
  270. if (auto* s = getNextSet())
  271. return s->time;
  272. return Time::getCurrentTime();
  273. }
  274. bool UndoManager::undoCurrentTransactionOnly()
  275. {
  276. if ((! newTransaction) && undo())
  277. {
  278. restoreStashedFutureTransactions();
  279. return true;
  280. }
  281. return false;
  282. }
  283. void UndoManager::getActionsInCurrentTransaction (Array<const UndoableAction*>& actionsFound) const
  284. {
  285. if (! newTransaction)
  286. if (auto* s = getCurrentSet())
  287. for (auto* a : s->actions)
  288. actionsFound.add (a);
  289. }
  290. int UndoManager::getNumActionsInCurrentTransaction() const
  291. {
  292. if (! newTransaction)
  293. if (auto* s = getCurrentSet())
  294. return s->actions.size();
  295. return 0;
  296. }
  297. } // namespace juce