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