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.

287 lines
8.2KB

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