The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
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.

275 lines
7.9KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. UndoManager::UndoManager (const int maxNumberOfUnitsToKeep,
  19. const int minimumTransactions)
  20. : totalUnitsStored (0),
  21. nextIndex (0),
  22. newTransaction (true),
  23. reentrancyCheck (false)
  24. {
  25. setMaxNumberOfStoredUnits (maxNumberOfUnitsToKeep,
  26. minimumTransactions);
  27. }
  28. UndoManager::~UndoManager()
  29. {
  30. clearUndoHistory();
  31. }
  32. //==============================================================================
  33. void UndoManager::clearUndoHistory()
  34. {
  35. transactions.clear();
  36. transactionNames.clear();
  37. totalUnitsStored = 0;
  38. nextIndex = 0;
  39. sendChangeMessage();
  40. }
  41. int UndoManager::getNumberOfUnitsTakenUpByStoredCommands() const
  42. {
  43. return totalUnitsStored;
  44. }
  45. void UndoManager::setMaxNumberOfStoredUnits (const int maxNumberOfUnitsToKeep,
  46. const int minimumTransactions)
  47. {
  48. maxNumUnitsToKeep = jmax (1, maxNumberOfUnitsToKeep);
  49. minimumTransactionsToKeep = jmax (1, minimumTransactions);
  50. }
  51. //==============================================================================
  52. bool UndoManager::perform (UndoableAction* const command_, const String& actionName)
  53. {
  54. if (command_ != nullptr)
  55. {
  56. ScopedPointer<UndoableAction> command (command_);
  57. if (actionName.isNotEmpty())
  58. currentTransactionName = actionName;
  59. if (reentrancyCheck)
  60. {
  61. jassertfalse; // don't call perform() recursively from the UndoableAction::perform() or
  62. // undo() methods, or else these actions won't actually get done.
  63. return false;
  64. }
  65. else if (command->perform())
  66. {
  67. OwnedArray<UndoableAction>* commandSet = transactions [nextIndex - 1];
  68. if (commandSet != nullptr && ! newTransaction)
  69. {
  70. UndoableAction* lastAction = commandSet->getLast();
  71. if (lastAction != nullptr)
  72. {
  73. UndoableAction* coalescedAction = lastAction->createCoalescedAction (command);
  74. if (coalescedAction != nullptr)
  75. {
  76. command = coalescedAction;
  77. totalUnitsStored -= lastAction->getSizeInUnits();
  78. commandSet->removeLast();
  79. }
  80. }
  81. }
  82. else
  83. {
  84. commandSet = new OwnedArray<UndoableAction>();
  85. transactions.insert (nextIndex, commandSet);
  86. transactionNames.insert (nextIndex, currentTransactionName);
  87. ++nextIndex;
  88. }
  89. totalUnitsStored += command->getSizeInUnits();
  90. commandSet->add (command.release());
  91. newTransaction = false;
  92. while (nextIndex < transactions.size())
  93. {
  94. const OwnedArray <UndoableAction>* const lastSet = transactions.getLast();
  95. for (int i = lastSet->size(); --i >= 0;)
  96. totalUnitsStored -= lastSet->getUnchecked (i)->getSizeInUnits();
  97. transactions.removeLast();
  98. transactionNames.remove (transactionNames.size() - 1);
  99. }
  100. while (nextIndex > 0
  101. && totalUnitsStored > maxNumUnitsToKeep
  102. && transactions.size() > minimumTransactionsToKeep)
  103. {
  104. const OwnedArray <UndoableAction>* const firstSet = transactions.getFirst();
  105. for (int i = firstSet->size(); --i >= 0;)
  106. totalUnitsStored -= firstSet->getUnchecked (i)->getSizeInUnits();
  107. jassert (totalUnitsStored >= 0); // something fishy going on if this fails!
  108. transactions.remove (0);
  109. transactionNames.remove (0);
  110. --nextIndex;
  111. }
  112. sendChangeMessage();
  113. return true;
  114. }
  115. }
  116. return false;
  117. }
  118. void UndoManager::beginNewTransaction (const String& actionName)
  119. {
  120. newTransaction = true;
  121. currentTransactionName = actionName;
  122. }
  123. void UndoManager::setCurrentTransactionName (const String& newName)
  124. {
  125. currentTransactionName = newName;
  126. }
  127. //==============================================================================
  128. bool UndoManager::canUndo() const
  129. {
  130. return nextIndex > 0;
  131. }
  132. bool UndoManager::canRedo() const
  133. {
  134. return nextIndex < transactions.size();
  135. }
  136. String UndoManager::getUndoDescription() const
  137. {
  138. return transactionNames [nextIndex - 1];
  139. }
  140. String UndoManager::getRedoDescription() const
  141. {
  142. return transactionNames [nextIndex];
  143. }
  144. bool UndoManager::undo()
  145. {
  146. const OwnedArray<UndoableAction>* const commandSet = transactions [nextIndex - 1];
  147. if (commandSet == nullptr)
  148. return false;
  149. bool failed = false;
  150. {
  151. const ScopedValueSetter<bool> setter (reentrancyCheck, true);
  152. for (int i = commandSet->size(); --i >= 0;)
  153. {
  154. if (! commandSet->getUnchecked(i)->undo())
  155. {
  156. jassertfalse;
  157. failed = true;
  158. break;
  159. }
  160. }
  161. }
  162. if (failed)
  163. clearUndoHistory();
  164. else
  165. --nextIndex;
  166. beginNewTransaction();
  167. sendChangeMessage();
  168. return true;
  169. }
  170. bool UndoManager::redo()
  171. {
  172. const OwnedArray<UndoableAction>* const commandSet = transactions [nextIndex];
  173. if (commandSet == nullptr)
  174. return false;
  175. bool failed = false;
  176. {
  177. const ScopedValueSetter<bool> setter (reentrancyCheck, true);
  178. for (int i = 0; i < commandSet->size(); ++i)
  179. {
  180. if (! commandSet->getUnchecked(i)->perform())
  181. {
  182. jassertfalse;
  183. failed = true;
  184. break;
  185. }
  186. }
  187. }
  188. if (failed)
  189. clearUndoHistory();
  190. else
  191. ++nextIndex;
  192. beginNewTransaction();
  193. sendChangeMessage();
  194. return true;
  195. }
  196. bool UndoManager::undoCurrentTransactionOnly()
  197. {
  198. return newTransaction ? false : undo();
  199. }
  200. void UndoManager::getActionsInCurrentTransaction (Array <const UndoableAction*>& actionsFound) const
  201. {
  202. const OwnedArray <UndoableAction>* const commandSet = transactions [nextIndex - 1];
  203. if (commandSet != nullptr && ! newTransaction)
  204. {
  205. for (int i = 0; i < commandSet->size(); ++i)
  206. actionsFound.add (commandSet->getUnchecked(i));
  207. }
  208. }
  209. int UndoManager::getNumActionsInCurrentTransaction() const
  210. {
  211. const OwnedArray <UndoableAction>* const commandSet = transactions [nextIndex - 1];
  212. if (commandSet != nullptr && ! newTransaction)
  213. return commandSet->size();
  214. return 0;
  215. }