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.

363 lines
9.0KB

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