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.

304 lines
8.3KB

  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. bool perform() const
  24. {
  25. for (int i = 0; i < actions.size(); ++i)
  26. if (! actions.getUnchecked(i)->perform())
  27. return false;
  28. return true;
  29. }
  30. bool undo() const
  31. {
  32. for (int i = actions.size(); --i >= 0;)
  33. if (! actions.getUnchecked(i)->undo())
  34. return false;
  35. return true;
  36. }
  37. int getTotalSize() const
  38. {
  39. int total = 0;
  40. for (int i = actions.size(); --i >= 0;)
  41. total += actions.getUnchecked(i)->getSizeInUnits();
  42. return total;
  43. }
  44. OwnedArray<UndoableAction> actions;
  45. String name;
  46. Time time;
  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 (perform (newAction))
  84. {
  85. if (actionName.isNotEmpty())
  86. setCurrentTransactionName (actionName);
  87. return true;
  88. }
  89. return false;
  90. }
  91. bool UndoManager::perform (UndoableAction* const newAction)
  92. {
  93. if (newAction != nullptr)
  94. {
  95. ScopedPointer<UndoableAction> action (newAction);
  96. if (reentrancyCheck)
  97. {
  98. jassertfalse; // don't call perform() recursively from the UndoableAction::perform()
  99. // or undo() methods, or else these actions will be discarded!
  100. return false;
  101. }
  102. if (action->perform())
  103. {
  104. ActionSet* actionSet = getCurrentSet();
  105. if (actionSet != nullptr && ! newTransaction)
  106. {
  107. if (UndoableAction* const lastAction = actionSet->actions.getLast())
  108. {
  109. if (UndoableAction* const coalescedAction = lastAction->createCoalescedAction (action))
  110. {
  111. action = coalescedAction;
  112. totalUnitsStored -= lastAction->getSizeInUnits();
  113. actionSet->actions.removeLast();
  114. }
  115. }
  116. }
  117. else
  118. {
  119. actionSet = new ActionSet (newTransactionName);
  120. transactions.insert (nextIndex, actionSet);
  121. ++nextIndex;
  122. }
  123. totalUnitsStored += action->getSizeInUnits();
  124. actionSet->actions.add (action.release());
  125. newTransaction = false;
  126. clearFutureTransactions();
  127. sendChangeMessage();
  128. return true;
  129. }
  130. }
  131. return false;
  132. }
  133. void UndoManager::clearFutureTransactions()
  134. {
  135. while (nextIndex < transactions.size())
  136. {
  137. totalUnitsStored -= transactions.getLast()->getTotalSize();
  138. transactions.removeLast();
  139. }
  140. while (nextIndex > 0
  141. && totalUnitsStored > maxNumUnitsToKeep
  142. && transactions.size() > minimumTransactionsToKeep)
  143. {
  144. totalUnitsStored -= transactions.getFirst()->getTotalSize();
  145. transactions.remove (0);
  146. --nextIndex;
  147. // if this fails, then some actions may not be returning
  148. // consistent results from their getSizeInUnits() method
  149. jassert (totalUnitsStored >= 0);
  150. }
  151. }
  152. void UndoManager::beginNewTransaction() noexcept
  153. {
  154. beginNewTransaction (String());
  155. }
  156. void UndoManager::beginNewTransaction (const String& actionName) noexcept
  157. {
  158. newTransaction = true;
  159. newTransactionName = actionName;
  160. }
  161. void UndoManager::setCurrentTransactionName (const String& newName) noexcept
  162. {
  163. if (newTransaction)
  164. newTransactionName = newName;
  165. else if (ActionSet* action = getCurrentSet())
  166. action->name = newName;
  167. }
  168. //==============================================================================
  169. UndoManager::ActionSet* UndoManager::getCurrentSet() const noexcept { return transactions [nextIndex - 1]; }
  170. UndoManager::ActionSet* UndoManager::getNextSet() const noexcept { return transactions [nextIndex]; }
  171. bool UndoManager::canUndo() const noexcept { return getCurrentSet() != nullptr; }
  172. bool UndoManager::canRedo() const noexcept { return getNextSet() != nullptr; }
  173. bool UndoManager::undo()
  174. {
  175. if (const ActionSet* const s = getCurrentSet())
  176. {
  177. const ScopedValueSetter<bool> setter (reentrancyCheck, true);
  178. if (s->undo())
  179. --nextIndex;
  180. else
  181. clearUndoHistory();
  182. beginNewTransaction();
  183. sendChangeMessage();
  184. return true;
  185. }
  186. return false;
  187. }
  188. bool UndoManager::redo()
  189. {
  190. if (const ActionSet* const s = getNextSet())
  191. {
  192. const ScopedValueSetter<bool> setter (reentrancyCheck, true);
  193. if (s->perform())
  194. ++nextIndex;
  195. else
  196. clearUndoHistory();
  197. beginNewTransaction();
  198. sendChangeMessage();
  199. return true;
  200. }
  201. return false;
  202. }
  203. String UndoManager::getUndoDescription() const
  204. {
  205. if (const ActionSet* const s = getCurrentSet())
  206. return s->name;
  207. return String();
  208. }
  209. String UndoManager::getRedoDescription() const
  210. {
  211. if (const ActionSet* const s = getNextSet())
  212. return s->name;
  213. return String();
  214. }
  215. Time UndoManager::getTimeOfUndoTransaction() const
  216. {
  217. if (const ActionSet* const s = getCurrentSet())
  218. return s->time;
  219. return Time();
  220. }
  221. Time UndoManager::getTimeOfRedoTransaction() const
  222. {
  223. if (const ActionSet* const s = getNextSet())
  224. return s->time;
  225. return Time::getCurrentTime();
  226. }
  227. bool UndoManager::undoCurrentTransactionOnly()
  228. {
  229. return newTransaction ? false : undo();
  230. }
  231. void UndoManager::getActionsInCurrentTransaction (Array<const UndoableAction*>& actionsFound) const
  232. {
  233. if (! newTransaction)
  234. if (const ActionSet* const s = getCurrentSet())
  235. for (int i = 0; i < s->actions.size(); ++i)
  236. actionsFound.add (s->actions.getUnchecked(i));
  237. }
  238. int UndoManager::getNumActionsInCurrentTransaction() const
  239. {
  240. if (! newTransaction)
  241. if (const ActionSet* const s = getCurrentSet())
  242. return s->actions.size();
  243. return 0;
  244. }