OpenShot Audio Library | OpenShotAudio  0.3.1
juce_UndoManager.cpp
1 /*
2  ==============================================================================
3 
4  This file is part of the JUCE library.
5  Copyright (c) 2017 - ROLI Ltd.
6 
7  JUCE is an open source library subject to commercial or open-source
8  licensing.
9 
10  By using JUCE, you agree to the terms of both the JUCE 5 End-User License
11  Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
12  27th April 2017).
13 
14  End User License Agreement: www.juce.com/juce-5-licence
15  Privacy Policy: www.juce.com/juce-5-privacy-policy
16 
17  Or: You may also use this code under the terms of the GPL v3 (see
18  www.gnu.org/licenses).
19 
20  JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
21  EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
22  DISCLAIMED.
23 
24  ==============================================================================
25 */
26 
27 namespace juce
28 {
29 
30 struct UndoManager::ActionSet
31 {
32  ActionSet (const String& transactionName) : name (transactionName)
33  {}
34 
35  bool perform() const
36  {
37  for (auto* a : actions)
38  if (! a->perform())
39  return false;
40 
41  return true;
42  }
43 
44  bool undo() const
45  {
46  for (int i = actions.size(); --i >= 0;)
47  if (! actions.getUnchecked(i)->undo())
48  return false;
49 
50  return true;
51  }
52 
53  int getTotalSize() const
54  {
55  int total = 0;
56 
57  for (auto* a : actions)
58  total += a->getSizeInUnits();
59 
60  return total;
61  }
62 
63  OwnedArray<UndoableAction> actions;
64  String name;
65  Time time { Time::getCurrentTime() };
66 };
67 
68 //==============================================================================
69 UndoManager::UndoManager (int maxNumberOfUnitsToKeep, int minimumTransactions)
70 {
71  setMaxNumberOfStoredUnits (maxNumberOfUnitsToKeep, minimumTransactions);
72 }
73 
75 {
76 }
77 
78 //==============================================================================
80 {
81  transactions.clear();
82  totalUnitsStored = 0;
83  nextIndex = 0;
85 }
86 
88 {
89  return totalUnitsStored;
90 }
91 
92 void UndoManager::setMaxNumberOfStoredUnits (int maxUnits, int minTransactions)
93 {
94  maxNumUnitsToKeep = jmax (1, maxUnits);
95  minimumTransactionsToKeep = jmax (1, minTransactions);
96 }
97 
98 //==============================================================================
99 bool UndoManager::perform (UndoableAction* newAction, const String& actionName)
100 {
101  if (perform (newAction))
102  {
103  if (actionName.isNotEmpty())
104  setCurrentTransactionName (actionName);
105 
106  return true;
107  }
108 
109  return false;
110 }
111 
113 {
114  if (newAction != nullptr)
115  {
116  std::unique_ptr<UndoableAction> action (newAction);
117 
118  if (isPerformingUndoRedo())
119  {
120  jassertfalse; // Don't call perform() recursively from the UndoableAction::perform()
121  // or undo() methods, or else these actions will be discarded!
122  return false;
123  }
124 
125  if (action->perform())
126  {
127  auto* actionSet = getCurrentSet();
128 
129  if (actionSet != nullptr && ! newTransaction)
130  {
131  if (auto* lastAction = actionSet->actions.getLast())
132  {
133  if (auto coalescedAction = lastAction->createCoalescedAction (action.get()))
134  {
135  action.reset (coalescedAction);
136  totalUnitsStored -= lastAction->getSizeInUnits();
137  actionSet->actions.removeLast();
138  }
139  }
140  }
141  else
142  {
143  actionSet = new ActionSet (newTransactionName);
144  transactions.insert (nextIndex, actionSet);
145  ++nextIndex;
146  }
147 
148  totalUnitsStored += action->getSizeInUnits();
149  actionSet->actions.add (std::move (action));
150  newTransaction = false;
151 
152  moveFutureTransactionsToStash();
153  dropOldTransactionsIfTooLarge();
155  return true;
156  }
157  }
158 
159  return false;
160 }
161 
162 void UndoManager::moveFutureTransactionsToStash()
163 {
164  if (nextIndex < transactions.size())
165  {
166  stashedFutureTransactions.clear();
167 
168  while (nextIndex < transactions.size())
169  {
170  auto* removed = transactions.removeAndReturn (nextIndex);
171  stashedFutureTransactions.add (removed);
172  totalUnitsStored -= removed->getTotalSize();
173  }
174  }
175 }
176 
177 void UndoManager::restoreStashedFutureTransactions()
178 {
179  while (nextIndex < transactions.size())
180  {
181  totalUnitsStored -= transactions.getUnchecked (nextIndex)->getTotalSize();
182  transactions.remove (nextIndex);
183  }
184 
185  for (auto* stashed : stashedFutureTransactions)
186  {
187  transactions.add (stashed);
188  totalUnitsStored += stashed->getTotalSize();
189  }
190 
191  stashedFutureTransactions.clearQuick (false);
192 }
193 
194 void UndoManager::dropOldTransactionsIfTooLarge()
195 {
196  while (nextIndex > 0
197  && totalUnitsStored > maxNumUnitsToKeep
198  && transactions.size() > minimumTransactionsToKeep)
199  {
200  totalUnitsStored -= transactions.getFirst()->getTotalSize();
201  transactions.remove (0);
202  --nextIndex;
203 
204  // if this fails, then some actions may not be returning
205  // consistent results from their getSizeInUnits() method
206  jassert (totalUnitsStored >= 0);
207  }
208 }
209 
211 {
212  beginNewTransaction ({});
213 }
214 
215 void UndoManager::beginNewTransaction (const String& actionName)
216 {
217  newTransaction = true;
218  newTransactionName = actionName;
219 }
220 
222 {
223  if (newTransaction)
224  newTransactionName = newName;
225  else if (auto* action = getCurrentSet())
226  action->name = newName;
227 }
228 
230 {
231  if (auto* action = getCurrentSet())
232  return action->name;
233 
234  return newTransactionName;
235 }
236 
237 //==============================================================================
238 UndoManager::ActionSet* UndoManager::getCurrentSet() const { return transactions[nextIndex - 1]; }
239 UndoManager::ActionSet* UndoManager::getNextSet() const { return transactions[nextIndex]; }
240 
241 bool UndoManager::isPerformingUndoRedo() const { return isInsideUndoRedoCall; }
242 
243 bool UndoManager::canUndo() const { return getCurrentSet() != nullptr; }
244 bool UndoManager::canRedo() const { return getNextSet() != nullptr; }
245 
247 {
248  if (auto* s = getCurrentSet())
249  {
250  const ScopedValueSetter<bool> setter (isInsideUndoRedoCall, true);
251 
252  if (s->undo())
253  --nextIndex;
254  else
256 
259  return true;
260  }
261 
262  return false;
263 }
264 
266 {
267  if (auto* s = getNextSet())
268  {
269  const ScopedValueSetter<bool> setter (isInsideUndoRedoCall, true);
270 
271  if (s->perform())
272  ++nextIndex;
273  else
275 
278  return true;
279  }
280 
281  return false;
282 }
283 
285 {
286  if (auto* s = getCurrentSet())
287  return s->name;
288 
289  return {};
290 }
291 
293 {
294  if (auto* s = getNextSet())
295  return s->name;
296 
297  return {};
298 }
299 
301 {
302  StringArray descriptions;
303 
304  for (int i = nextIndex;;)
305  {
306  if (auto* t = transactions[--i])
307  descriptions.add (t->name);
308  else
309  return descriptions;
310  }
311 }
312 
314 {
315  StringArray descriptions;
316 
317  for (int i = nextIndex;;)
318  {
319  if (auto* t = transactions[i++])
320  descriptions.add (t->name);
321  else
322  return descriptions;
323  }
324 }
325 
327 {
328  if (auto* s = getCurrentSet())
329  return s->time;
330 
331  return {};
332 }
333 
335 {
336  if (auto* s = getNextSet())
337  return s->time;
338 
339  return Time::getCurrentTime();
340 }
341 
343 {
344  if ((! newTransaction) && undo())
345  {
346  restoreStashedFutureTransactions();
347  return true;
348  }
349 
350  return false;
351 }
352 
354 {
355  if (! newTransaction)
356  if (auto* s = getCurrentSet())
357  for (auto* a : s->actions)
358  actionsFound.add (a);
359 }
360 
362 {
363  if (! newTransaction)
364  if (auto* s = getCurrentSet())
365  return s->actions.size();
366 
367  return 0;
368 }
369 
370 } // namespace juce
String getUndoDescription() const
bool isNotEmpty() const noexcept
Definition: juce_String.h:302
StringArray getUndoDescriptions() const
Time getTimeOfUndoTransaction() const
void add(const ElementType &newElement)
Definition: juce_Array.h:418
int getNumberOfUnitsTakenUpByStoredCommands() const
static Time JUCE_CALLTYPE getCurrentTime() noexcept
Definition: juce_Time.cpp:218
void setCurrentTransactionName(const String &newName)
bool isPerformingUndoRedo() const
Time getTimeOfRedoTransaction() const
bool perform(UndoableAction *action)
void setMaxNumberOfStoredUnits(int maxNumberOfUnitsToKeep, int minimumTransactionsToKeep)
StringArray getRedoDescriptions() const
int getNumActionsInCurrentTransaction() const
String getCurrentTransactionName() const
String getRedoDescription() const
void getActionsInCurrentTransaction(Array< const UndoableAction *> &actionsFound) const
UndoManager(int maxNumberOfUnitsToKeep=30000, int minimumTransactionsToKeep=30)
void add(String stringToAdd)