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.

284 lines
8.0KB

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