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.

296 lines
8.0KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-9 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. if (actionName.isNotEmpty())
  62. currentTransactionName = actionName;
  63. if (reentrancyCheck)
  64. {
  65. jassertfalse // don't call perform() recursively from the UndoableAction::perform() or
  66. // undo() methods, or else these actions won't actually get done.
  67. return false;
  68. }
  69. else
  70. {
  71. bool success = false;
  72. JUCE_TRY
  73. {
  74. success = command->perform();
  75. }
  76. JUCE_CATCH_EXCEPTION
  77. jassert (success);
  78. if (success)
  79. {
  80. if (nextIndex > 0 && ! newTransaction)
  81. {
  82. OwnedArray<UndoableAction>* commandSet = transactions [nextIndex - 1];
  83. jassert (commandSet != 0);
  84. if (commandSet == 0)
  85. return false;
  86. commandSet->add (command);
  87. }
  88. else
  89. {
  90. OwnedArray<UndoableAction>* commandSet = new OwnedArray<UndoableAction>();
  91. commandSet->add (command);
  92. transactions.insert (nextIndex, commandSet);
  93. transactionNames.insert (nextIndex, currentTransactionName);
  94. ++nextIndex;
  95. }
  96. totalUnitsStored += command->getSizeInUnits();
  97. newTransaction = false;
  98. }
  99. while (nextIndex < transactions.size())
  100. {
  101. const OwnedArray <UndoableAction>* const lastSet = transactions.getLast();
  102. for (int i = lastSet->size(); --i >= 0;)
  103. totalUnitsStored -= lastSet->getUnchecked (i)->getSizeInUnits();
  104. transactions.removeLast();
  105. transactionNames.remove (transactionNames.size() - 1);
  106. }
  107. while (nextIndex > 0
  108. && totalUnitsStored > maxNumUnitsToKeep
  109. && transactions.size() > minimumTransactionsToKeep)
  110. {
  111. const OwnedArray <UndoableAction>* const firstSet = transactions.getFirst();
  112. for (int i = firstSet->size(); --i >= 0;)
  113. totalUnitsStored -= firstSet->getUnchecked (i)->getSizeInUnits();
  114. jassert (totalUnitsStored >= 0); // something fishy going on if this fails!
  115. transactions.remove (0);
  116. transactionNames.remove (0);
  117. --nextIndex;
  118. }
  119. sendChangeMessage (this);
  120. return success;
  121. }
  122. }
  123. return false;
  124. }
  125. void UndoManager::beginNewTransaction (const String& actionName)
  126. {
  127. newTransaction = true;
  128. currentTransactionName = actionName;
  129. }
  130. void UndoManager::setCurrentTransactionName (const String& newName)
  131. {
  132. currentTransactionName = newName;
  133. }
  134. //==============================================================================
  135. bool UndoManager::canUndo() const
  136. {
  137. return nextIndex > 0;
  138. }
  139. bool UndoManager::canRedo() const
  140. {
  141. return nextIndex < transactions.size();
  142. }
  143. const String UndoManager::getUndoDescription() const
  144. {
  145. return transactionNames [nextIndex - 1];
  146. }
  147. const String UndoManager::getRedoDescription() const
  148. {
  149. return transactionNames [nextIndex];
  150. }
  151. bool UndoManager::undo()
  152. {
  153. const OwnedArray<UndoableAction>* const commandSet = transactions [nextIndex - 1];
  154. if (commandSet == 0)
  155. return false;
  156. reentrancyCheck = true;
  157. bool failed = false;
  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. reentrancyCheck = false;
  168. if (failed)
  169. {
  170. clearUndoHistory();
  171. }
  172. else
  173. {
  174. --nextIndex;
  175. }
  176. beginNewTransaction();
  177. sendChangeMessage (this);
  178. return true;
  179. }
  180. bool UndoManager::redo()
  181. {
  182. const OwnedArray<UndoableAction>* const commandSet = transactions [nextIndex];
  183. if (commandSet == 0)
  184. return false;
  185. reentrancyCheck = true;
  186. bool failed = false;
  187. for (int i = 0; i < commandSet->size(); ++i)
  188. {
  189. if (! commandSet->getUnchecked(i)->perform())
  190. {
  191. jassertfalse
  192. failed = true;
  193. break;
  194. }
  195. }
  196. reentrancyCheck = false;
  197. if (failed)
  198. {
  199. clearUndoHistory();
  200. }
  201. else
  202. {
  203. ++nextIndex;
  204. }
  205. beginNewTransaction();
  206. sendChangeMessage (this);
  207. return true;
  208. }
  209. bool UndoManager::undoCurrentTransactionOnly()
  210. {
  211. return newTransaction ? false
  212. : undo();
  213. }
  214. void UndoManager::getActionsInCurrentTransaction (Array <const UndoableAction*>& actionsFound) const
  215. {
  216. const OwnedArray <UndoableAction>* const commandSet = transactions [nextIndex - 1];
  217. if (commandSet != 0 && ! newTransaction)
  218. {
  219. for (int i = 0; i < commandSet->size(); ++i)
  220. actionsFound.add (commandSet->getUnchecked(i));
  221. }
  222. }
  223. int UndoManager::getNumActionsInCurrentTransaction() const
  224. {
  225. const OwnedArray <UndoableAction>* const commandSet = transactions [nextIndex - 1];
  226. if (commandSet != 0 && ! newTransaction)
  227. return commandSet->size();
  228. return 0;
  229. }
  230. END_JUCE_NAMESPACE