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.

280 lines
8.0KB

  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. BEGIN_JUCE_NAMESPACE
  19. //==============================================================================
  20. UndoManager::UndoManager (const int maxNumberOfUnitsToKeep,
  21. const int minimumTransactions)
  22. : totalUnitsStored (0),
  23. nextIndex (0),
  24. newTransaction (true),
  25. reentrancyCheck (false)
  26. {
  27. setMaxNumberOfStoredUnits (maxNumberOfUnitsToKeep,
  28. minimumTransactions);
  29. }
  30. UndoManager::~UndoManager()
  31. {
  32. clearUndoHistory();
  33. }
  34. //==============================================================================
  35. void UndoManager::clearUndoHistory()
  36. {
  37. transactions.clear();
  38. transactionNames.clear();
  39. totalUnitsStored = 0;
  40. nextIndex = 0;
  41. sendChangeMessage();
  42. }
  43. int UndoManager::getNumberOfUnitsTakenUpByStoredCommands() const
  44. {
  45. return totalUnitsStored;
  46. }
  47. void UndoManager::setMaxNumberOfStoredUnits (const int maxNumberOfUnitsToKeep,
  48. const int minimumTransactions)
  49. {
  50. maxNumUnitsToKeep = jmax (1, maxNumberOfUnitsToKeep);
  51. minimumTransactionsToKeep = jmax (1, minimumTransactions);
  52. }
  53. //==============================================================================
  54. bool UndoManager::perform (UndoableAction* const command_, const String& actionName)
  55. {
  56. if (command_ != nullptr)
  57. {
  58. ScopedPointer<UndoableAction> command (command_);
  59. if (actionName.isNotEmpty())
  60. currentTransactionName = actionName;
  61. if (reentrancyCheck)
  62. {
  63. jassertfalse; // don't call perform() recursively from the UndoableAction::perform() or
  64. // undo() methods, or else these actions won't actually get done.
  65. return false;
  66. }
  67. else if (command->perform())
  68. {
  69. OwnedArray<UndoableAction>* commandSet = transactions [nextIndex - 1];
  70. if (commandSet != nullptr && ! newTransaction)
  71. {
  72. UndoableAction* lastAction = commandSet->getLast();
  73. if (lastAction != nullptr)
  74. {
  75. UndoableAction* coalescedAction = lastAction->createCoalescedAction (command);
  76. if (coalescedAction != nullptr)
  77. {
  78. command = coalescedAction;
  79. totalUnitsStored -= lastAction->getSizeInUnits();
  80. commandSet->removeLast();
  81. }
  82. }
  83. }
  84. else
  85. {
  86. commandSet = new OwnedArray<UndoableAction>();
  87. transactions.insert (nextIndex, commandSet);
  88. transactionNames.insert (nextIndex, currentTransactionName);
  89. ++nextIndex;
  90. }
  91. totalUnitsStored += command->getSizeInUnits();
  92. commandSet->add (command.release());
  93. newTransaction = false;
  94. while (nextIndex < transactions.size())
  95. {
  96. const OwnedArray <UndoableAction>* const lastSet = transactions.getLast();
  97. for (int i = lastSet->size(); --i >= 0;)
  98. totalUnitsStored -= lastSet->getUnchecked (i)->getSizeInUnits();
  99. transactions.removeLast();
  100. transactionNames.remove (transactionNames.size() - 1);
  101. }
  102. while (nextIndex > 0
  103. && totalUnitsStored > maxNumUnitsToKeep
  104. && transactions.size() > minimumTransactionsToKeep)
  105. {
  106. const OwnedArray <UndoableAction>* const firstSet = transactions.getFirst();
  107. for (int i = firstSet->size(); --i >= 0;)
  108. totalUnitsStored -= firstSet->getUnchecked (i)->getSizeInUnits();
  109. jassert (totalUnitsStored >= 0); // something fishy going on if this fails!
  110. transactions.remove (0);
  111. transactionNames.remove (0);
  112. --nextIndex;
  113. }
  114. sendChangeMessage();
  115. return true;
  116. }
  117. }
  118. return false;
  119. }
  120. void UndoManager::beginNewTransaction (const String& actionName)
  121. {
  122. newTransaction = true;
  123. currentTransactionName = actionName;
  124. }
  125. void UndoManager::setCurrentTransactionName (const String& newName)
  126. {
  127. currentTransactionName = newName;
  128. }
  129. //==============================================================================
  130. bool UndoManager::canUndo() const
  131. {
  132. return nextIndex > 0;
  133. }
  134. bool UndoManager::canRedo() const
  135. {
  136. return nextIndex < transactions.size();
  137. }
  138. String UndoManager::getUndoDescription() const
  139. {
  140. return transactionNames [nextIndex - 1];
  141. }
  142. String UndoManager::getRedoDescription() const
  143. {
  144. return transactionNames [nextIndex];
  145. }
  146. bool UndoManager::undo()
  147. {
  148. const OwnedArray<UndoableAction>* const commandSet = transactions [nextIndex - 1];
  149. if (commandSet == nullptr)
  150. return false;
  151. bool failed = false;
  152. {
  153. const ScopedValueSetter<bool> setter (reentrancyCheck, true);
  154. for (int i = commandSet->size(); --i >= 0;)
  155. {
  156. if (! commandSet->getUnchecked(i)->undo())
  157. {
  158. jassertfalse;
  159. failed = true;
  160. break;
  161. }
  162. }
  163. }
  164. if (failed)
  165. clearUndoHistory();
  166. else
  167. --nextIndex;
  168. beginNewTransaction();
  169. sendChangeMessage();
  170. return true;
  171. }
  172. bool UndoManager::redo()
  173. {
  174. const OwnedArray<UndoableAction>* const commandSet = transactions [nextIndex];
  175. if (commandSet == nullptr)
  176. return false;
  177. bool failed = false;
  178. {
  179. const ScopedValueSetter<bool> setter (reentrancyCheck, true);
  180. for (int i = 0; i < commandSet->size(); ++i)
  181. {
  182. if (! commandSet->getUnchecked(i)->perform())
  183. {
  184. jassertfalse;
  185. failed = true;
  186. break;
  187. }
  188. }
  189. }
  190. if (failed)
  191. clearUndoHistory();
  192. else
  193. ++nextIndex;
  194. beginNewTransaction();
  195. sendChangeMessage();
  196. return true;
  197. }
  198. bool UndoManager::undoCurrentTransactionOnly()
  199. {
  200. return newTransaction ? false : undo();
  201. }
  202. void UndoManager::getActionsInCurrentTransaction (Array <const UndoableAction*>& actionsFound) const
  203. {
  204. const OwnedArray <UndoableAction>* const commandSet = transactions [nextIndex - 1];
  205. if (commandSet != nullptr && ! newTransaction)
  206. {
  207. for (int i = 0; i < commandSet->size(); ++i)
  208. actionsFound.add (commandSet->getUnchecked(i));
  209. }
  210. }
  211. int UndoManager::getNumActionsInCurrentTransaction() const
  212. {
  213. const OwnedArray <UndoableAction>* const commandSet = transactions [nextIndex - 1];
  214. if (commandSet != nullptr && ! newTransaction)
  215. return commandSet->size();
  216. return 0;
  217. }
  218. END_JUCE_NAMESPACE