Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

juce_UndoManager.cpp 7.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. struct UndoManager::ActionSet
  18. {
  19. ActionSet (const String& transactionName)
  20. : name (transactionName),
  21. time (Time::getCurrentTime())
  22. {}
  23. OwnedArray <UndoableAction> actions;
  24. String name;
  25. Time time;
  26. bool perform() const
  27. {
  28. for (int i = 0; i < actions.size(); ++i)
  29. if (! actions.getUnchecked(i)->perform())
  30. return false;
  31. return true;
  32. }
  33. bool undo() const
  34. {
  35. for (int i = actions.size(); --i >= 0;)
  36. if (! actions.getUnchecked(i)->undo())
  37. return false;
  38. return true;
  39. }
  40. int getTotalSize() const
  41. {
  42. int total = 0;
  43. for (int i = actions.size(); --i >= 0;)
  44. total += actions.getUnchecked(i)->getSizeInUnits();
  45. return total;
  46. }
  47. };
  48. //==============================================================================
  49. UndoManager::UndoManager (const int maxNumberOfUnitsToKeep,
  50. const int minimumTransactions)
  51. : totalUnitsStored (0),
  52. nextIndex (0),
  53. newTransaction (true),
  54. reentrancyCheck (false)
  55. {
  56. setMaxNumberOfStoredUnits (maxNumberOfUnitsToKeep,
  57. minimumTransactions);
  58. }
  59. UndoManager::~UndoManager()
  60. {
  61. }
  62. //==============================================================================
  63. void UndoManager::clearUndoHistory()
  64. {
  65. transactions.clear();
  66. totalUnitsStored = 0;
  67. nextIndex = 0;
  68. sendChangeMessage();
  69. }
  70. int UndoManager::getNumberOfUnitsTakenUpByStoredCommands() const
  71. {
  72. return totalUnitsStored;
  73. }
  74. void UndoManager::setMaxNumberOfStoredUnits (const int maxNumberOfUnitsToKeep,
  75. const int minimumTransactions)
  76. {
  77. maxNumUnitsToKeep = jmax (1, maxNumberOfUnitsToKeep);
  78. minimumTransactionsToKeep = jmax (1, minimumTransactions);
  79. }
  80. //==============================================================================
  81. bool UndoManager::perform (UndoableAction* const newAction, const String& actionName)
  82. {
  83. if (newAction != nullptr)
  84. {
  85. ScopedPointer<UndoableAction> action (newAction);
  86. if (reentrancyCheck)
  87. {
  88. jassertfalse; // don't call perform() recursively from the UndoableAction::perform()
  89. // or undo() methods, or else these actions will be discarded!
  90. return false;
  91. }
  92. if (actionName.isNotEmpty())
  93. currentTransactionName = actionName;
  94. if (action->perform())
  95. {
  96. ActionSet* actionSet = getCurrentSet();
  97. if (actionSet != nullptr && ! newTransaction)
  98. {
  99. if (UndoableAction* const lastAction = actionSet->actions.getLast())
  100. {
  101. if (UndoableAction* const coalescedAction = lastAction->createCoalescedAction (action))
  102. {
  103. action = coalescedAction;
  104. totalUnitsStored -= lastAction->getSizeInUnits();
  105. actionSet->actions.removeLast();
  106. }
  107. }
  108. }
  109. else
  110. {
  111. actionSet = new ActionSet (currentTransactionName);
  112. transactions.insert (nextIndex, actionSet);
  113. ++nextIndex;
  114. }
  115. totalUnitsStored += action->getSizeInUnits();
  116. actionSet->actions.add (action.release());
  117. newTransaction = false;
  118. clearFutureTransactions();
  119. sendChangeMessage();
  120. return true;
  121. }
  122. }
  123. return false;
  124. }
  125. void UndoManager::clearFutureTransactions()
  126. {
  127. while (nextIndex < transactions.size())
  128. {
  129. totalUnitsStored -= transactions.getLast()->getTotalSize();
  130. transactions.removeLast();
  131. }
  132. while (nextIndex > 0
  133. && totalUnitsStored > maxNumUnitsToKeep
  134. && transactions.size() > minimumTransactionsToKeep)
  135. {
  136. totalUnitsStored -= transactions.getFirst()->getTotalSize();
  137. transactions.remove (0);
  138. --nextIndex;
  139. // if this fails, then some actions may not be returning
  140. // consistent results from their getSizeInUnits() method
  141. jassert (totalUnitsStored >= 0);
  142. }
  143. }
  144. void UndoManager::beginNewTransaction (const String& actionName)
  145. {
  146. newTransaction = true;
  147. currentTransactionName = actionName;
  148. }
  149. void UndoManager::setCurrentTransactionName (const String& newName)
  150. {
  151. currentTransactionName = newName;
  152. }
  153. //==============================================================================
  154. UndoManager::ActionSet* UndoManager::getCurrentSet() const noexcept { return transactions [nextIndex - 1]; }
  155. UndoManager::ActionSet* UndoManager::getNextSet() const noexcept { return transactions [nextIndex]; }
  156. bool UndoManager::canUndo() const { return getCurrentSet() != nullptr; }
  157. bool UndoManager::canRedo() const { return getNextSet() != nullptr; }
  158. bool UndoManager::undo()
  159. {
  160. if (const ActionSet* const s = getCurrentSet())
  161. {
  162. const ScopedValueSetter<bool> setter (reentrancyCheck, true);
  163. if (s->undo())
  164. --nextIndex;
  165. else
  166. clearUndoHistory();
  167. beginNewTransaction();
  168. sendChangeMessage();
  169. return true;
  170. }
  171. return false;
  172. }
  173. bool UndoManager::redo()
  174. {
  175. if (const ActionSet* const s = getNextSet())
  176. {
  177. const ScopedValueSetter<bool> setter (reentrancyCheck, true);
  178. if (s->perform())
  179. ++nextIndex;
  180. else
  181. clearUndoHistory();
  182. beginNewTransaction();
  183. sendChangeMessage();
  184. return true;
  185. }
  186. return false;
  187. }
  188. String UndoManager::getUndoDescription() const
  189. {
  190. if (const ActionSet* const s = getCurrentSet())
  191. return s->name;
  192. return String::empty;
  193. }
  194. String UndoManager::getRedoDescription() const
  195. {
  196. if (const ActionSet* const s = getNextSet())
  197. return s->name;
  198. return String::empty;
  199. }
  200. Time UndoManager::getTimeOfUndoTransaction() const
  201. {
  202. if (const ActionSet* const s = getCurrentSet())
  203. return s->time;
  204. return Time();
  205. }
  206. Time UndoManager::getTimeOfRedoTransaction() const
  207. {
  208. if (const ActionSet* const s = getNextSet())
  209. return s->time;
  210. return Time::getCurrentTime();
  211. }
  212. bool UndoManager::undoCurrentTransactionOnly()
  213. {
  214. return newTransaction ? false : undo();
  215. }
  216. void UndoManager::getActionsInCurrentTransaction (Array <const UndoableAction*>& actionsFound) const
  217. {
  218. if (! newTransaction)
  219. if (const ActionSet* const s = getCurrentSet())
  220. for (int i = 0; i < s->actions.size(); ++i)
  221. actionsFound.add (s->actions.getUnchecked(i));
  222. }
  223. int UndoManager::getNumActionsInCurrentTransaction() const
  224. {
  225. if (! newTransaction)
  226. if (const ActionSet* const s = getCurrentSet())
  227. return s->actions.size();
  228. return 0;
  229. }