mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Added timestamps to UndoManager
This commit is contained in:
parent
db5c92f760
commit
1b85566090
2 changed files with 164 additions and 143 deletions
|
|
@ -23,6 +23,47 @@
|
|||
==============================================================================
|
||||
*/
|
||||
|
||||
struct UndoManager::ActionSet
|
||||
{
|
||||
ActionSet (const String& transactionName)
|
||||
: name (transactionName),
|
||||
time (Time::getCurrentTime())
|
||||
{}
|
||||
|
||||
OwnedArray <UndoableAction> actions;
|
||||
String name;
|
||||
Time time;
|
||||
|
||||
bool perform() const
|
||||
{
|
||||
for (int i = 0; i < actions.size(); ++i)
|
||||
if (! actions.getUnchecked(i)->perform())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo() const
|
||||
{
|
||||
for (int i = actions.size(); --i >= 0;)
|
||||
if (! actions.getUnchecked(i)->undo())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int getTotalSize() const
|
||||
{
|
||||
int total = 0;
|
||||
|
||||
for (int i = actions.size(); --i >= 0;)
|
||||
total += actions.getUnchecked(i)->getSizeInUnits();
|
||||
|
||||
return total;
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
UndoManager::UndoManager (const int maxNumberOfUnitsToKeep,
|
||||
const int minimumTransactions)
|
||||
: totalUnitsStored (0),
|
||||
|
|
@ -36,14 +77,12 @@ UndoManager::UndoManager (const int maxNumberOfUnitsToKeep,
|
|||
|
||||
UndoManager::~UndoManager()
|
||||
{
|
||||
clearUndoHistory();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void UndoManager::clearUndoHistory()
|
||||
{
|
||||
transactions.clear();
|
||||
transactionNames.clear();
|
||||
totalUnitsStored = 0;
|
||||
nextIndex = 0;
|
||||
sendChangeMessage();
|
||||
|
|
@ -62,88 +101,78 @@ void UndoManager::setMaxNumberOfStoredUnits (const int maxNumberOfUnitsToKeep,
|
|||
}
|
||||
|
||||
//==============================================================================
|
||||
bool UndoManager::perform (UndoableAction* const command_, const String& actionName)
|
||||
bool UndoManager::perform (UndoableAction* const newAction, const String& actionName)
|
||||
{
|
||||
if (command_ != nullptr)
|
||||
if (newAction != nullptr)
|
||||
{
|
||||
ScopedPointer<UndoableAction> command (command_);
|
||||
ScopedPointer<UndoableAction> action (newAction);
|
||||
|
||||
if (reentrancyCheck)
|
||||
{
|
||||
jassertfalse; // don't call perform() recursively from the UndoableAction::perform()
|
||||
// or undo() methods, or else these actions will be discarded!
|
||||
return false;
|
||||
}
|
||||
|
||||
if (actionName.isNotEmpty())
|
||||
currentTransactionName = actionName;
|
||||
|
||||
if (reentrancyCheck)
|
||||
if (action->perform())
|
||||
{
|
||||
jassertfalse; // don't call perform() recursively from the UndoableAction::perform() or
|
||||
// undo() methods, or else these actions won't actually get done.
|
||||
ActionSet* actionSet = getCurrentSet();
|
||||
|
||||
return false;
|
||||
}
|
||||
else if (command->perform())
|
||||
if (actionSet != nullptr && ! newTransaction)
|
||||
{
|
||||
OwnedArray<UndoableAction>* commandSet = transactions [nextIndex - 1];
|
||||
|
||||
if (commandSet != nullptr && ! newTransaction)
|
||||
if (UndoableAction* const lastAction = actionSet->actions.getLast())
|
||||
{
|
||||
UndoableAction* lastAction = commandSet->getLast();
|
||||
|
||||
if (lastAction != nullptr)
|
||||
if (UndoableAction* const coalescedAction = lastAction->createCoalescedAction (action))
|
||||
{
|
||||
UndoableAction* coalescedAction = lastAction->createCoalescedAction (command);
|
||||
|
||||
if (coalescedAction != nullptr)
|
||||
{
|
||||
command = coalescedAction;
|
||||
action = coalescedAction;
|
||||
totalUnitsStored -= lastAction->getSizeInUnits();
|
||||
commandSet->removeLast();
|
||||
actionSet->actions.removeLast();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
commandSet = new OwnedArray<UndoableAction>();
|
||||
transactions.insert (nextIndex, commandSet);
|
||||
transactionNames.insert (nextIndex, currentTransactionName);
|
||||
actionSet = new ActionSet (currentTransactionName);
|
||||
transactions.insert (nextIndex, actionSet);
|
||||
++nextIndex;
|
||||
}
|
||||
|
||||
totalUnitsStored += command->getSizeInUnits();
|
||||
commandSet->add (command.release());
|
||||
totalUnitsStored += action->getSizeInUnits();
|
||||
actionSet->actions.add (action.release());
|
||||
newTransaction = false;
|
||||
|
||||
clearFutureTransactions();
|
||||
sendChangeMessage();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void UndoManager::clearFutureTransactions()
|
||||
{
|
||||
while (nextIndex < transactions.size())
|
||||
{
|
||||
const OwnedArray <UndoableAction>* const lastSet = transactions.getLast();
|
||||
|
||||
for (int i = lastSet->size(); --i >= 0;)
|
||||
totalUnitsStored -= lastSet->getUnchecked (i)->getSizeInUnits();
|
||||
|
||||
totalUnitsStored -= transactions.getLast()->getTotalSize();
|
||||
transactions.removeLast();
|
||||
transactionNames.remove (transactionNames.size() - 1);
|
||||
}
|
||||
|
||||
while (nextIndex > 0
|
||||
&& totalUnitsStored > maxNumUnitsToKeep
|
||||
&& transactions.size() > minimumTransactionsToKeep)
|
||||
{
|
||||
const OwnedArray <UndoableAction>* const firstSet = transactions.getFirst();
|
||||
|
||||
for (int i = firstSet->size(); --i >= 0;)
|
||||
totalUnitsStored -= firstSet->getUnchecked (i)->getSizeInUnits();
|
||||
|
||||
jassert (totalUnitsStored >= 0); // something fishy going on if this fails!
|
||||
|
||||
totalUnitsStored -= transactions.getFirst()->getTotalSize();
|
||||
transactions.remove (0);
|
||||
transactionNames.remove (0);
|
||||
--nextIndex;
|
||||
}
|
||||
|
||||
sendChangeMessage();
|
||||
|
||||
return true;
|
||||
// if this fails, then some actions may not be returning
|
||||
// consistent results from their getSizeInUnits() method
|
||||
jassert (totalUnitsStored >= 0);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void UndoManager::beginNewTransaction (const String& actionName)
|
||||
|
|
@ -158,92 +187,80 @@ void UndoManager::setCurrentTransactionName (const String& newName)
|
|||
}
|
||||
|
||||
//==============================================================================
|
||||
bool UndoManager::canUndo() const
|
||||
{
|
||||
return nextIndex > 0;
|
||||
}
|
||||
UndoManager::ActionSet* UndoManager::getCurrentSet() const noexcept { return transactions [nextIndex - 1]; }
|
||||
UndoManager::ActionSet* UndoManager::getNextSet() const noexcept { return transactions [nextIndex]; }
|
||||
|
||||
bool UndoManager::canRedo() const
|
||||
{
|
||||
return nextIndex < transactions.size();
|
||||
}
|
||||
|
||||
String UndoManager::getUndoDescription() const
|
||||
{
|
||||
return transactionNames [nextIndex - 1];
|
||||
}
|
||||
|
||||
String UndoManager::getRedoDescription() const
|
||||
{
|
||||
return transactionNames [nextIndex];
|
||||
}
|
||||
bool UndoManager::canUndo() const { return getCurrentSet() != nullptr; }
|
||||
bool UndoManager::canRedo() const { return getNextSet() != nullptr; }
|
||||
|
||||
bool UndoManager::undo()
|
||||
{
|
||||
const OwnedArray<UndoableAction>* const commandSet = transactions [nextIndex - 1];
|
||||
|
||||
if (commandSet == nullptr)
|
||||
return false;
|
||||
|
||||
bool failed = false;
|
||||
|
||||
if (const ActionSet* const s = getCurrentSet())
|
||||
{
|
||||
const ScopedValueSetter<bool> setter (reentrancyCheck, true);
|
||||
|
||||
for (int i = commandSet->size(); --i >= 0;)
|
||||
{
|
||||
if (! commandSet->getUnchecked(i)->undo())
|
||||
{
|
||||
jassertfalse;
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (failed)
|
||||
clearUndoHistory();
|
||||
else
|
||||
if (s->undo())
|
||||
--nextIndex;
|
||||
else
|
||||
clearUndoHistory();
|
||||
|
||||
beginNewTransaction();
|
||||
|
||||
sendChangeMessage();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UndoManager::redo()
|
||||
{
|
||||
const OwnedArray<UndoableAction>* const commandSet = transactions [nextIndex];
|
||||
|
||||
if (commandSet == nullptr)
|
||||
return false;
|
||||
|
||||
bool failed = false;
|
||||
|
||||
if (const ActionSet* const s = getNextSet())
|
||||
{
|
||||
const ScopedValueSetter<bool> setter (reentrancyCheck, true);
|
||||
|
||||
for (int i = 0; i < commandSet->size(); ++i)
|
||||
{
|
||||
if (! commandSet->getUnchecked(i)->perform())
|
||||
{
|
||||
jassertfalse;
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (failed)
|
||||
clearUndoHistory();
|
||||
else
|
||||
if (s->perform())
|
||||
++nextIndex;
|
||||
else
|
||||
clearUndoHistory();
|
||||
|
||||
beginNewTransaction();
|
||||
|
||||
sendChangeMessage();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
String UndoManager::getUndoDescription() const
|
||||
{
|
||||
if (const ActionSet* const s = getCurrentSet())
|
||||
return s->name;
|
||||
|
||||
return String::empty;
|
||||
}
|
||||
|
||||
String UndoManager::getRedoDescription() const
|
||||
{
|
||||
if (const ActionSet* const s = getNextSet())
|
||||
return s->name;
|
||||
|
||||
return String::empty;
|
||||
}
|
||||
|
||||
Time UndoManager::getTimeOfUndoTransaction() const
|
||||
{
|
||||
if (const ActionSet* const s = getCurrentSet())
|
||||
return s->time;
|
||||
|
||||
return Time();
|
||||
}
|
||||
|
||||
Time UndoManager::getTimeOfRedoTransaction() const
|
||||
{
|
||||
if (const ActionSet* const s = getNextSet())
|
||||
return s->time;
|
||||
|
||||
return Time::getCurrentTime();
|
||||
}
|
||||
|
||||
bool UndoManager::undoCurrentTransactionOnly()
|
||||
|
|
@ -253,21 +270,17 @@ bool UndoManager::undoCurrentTransactionOnly()
|
|||
|
||||
void UndoManager::getActionsInCurrentTransaction (Array <const UndoableAction*>& actionsFound) const
|
||||
{
|
||||
const OwnedArray <UndoableAction>* const commandSet = transactions [nextIndex - 1];
|
||||
|
||||
if (commandSet != nullptr && ! newTransaction)
|
||||
{
|
||||
for (int i = 0; i < commandSet->size(); ++i)
|
||||
actionsFound.add (commandSet->getUnchecked(i));
|
||||
}
|
||||
if (! newTransaction)
|
||||
if (const ActionSet* const s = getCurrentSet())
|
||||
for (int i = 0; i < s->actions.size(); ++i)
|
||||
actionsFound.add (s->actions.getUnchecked(i));
|
||||
}
|
||||
|
||||
int UndoManager::getNumActionsInCurrentTransaction() const
|
||||
{
|
||||
const OwnedArray <UndoableAction>* const commandSet = transactions [nextIndex - 1];
|
||||
|
||||
if (commandSet != nullptr && ! newTransaction)
|
||||
return commandSet->size();
|
||||
if (! newTransaction)
|
||||
if (const ActionSet* const s = getCurrentSet())
|
||||
return s->actions.size();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,7 +77,6 @@ public:
|
|||
void clearUndoHistory();
|
||||
|
||||
/** Returns the current amount of space to use for storing UndoableAction objects.
|
||||
|
||||
@see setMaxNumberOfStoredUnits
|
||||
*/
|
||||
int getNumberOfUnitsTakenUpByStoredCommands() const;
|
||||
|
|
@ -134,7 +133,6 @@ public:
|
|||
|
||||
//==============================================================================
|
||||
/** Returns true if there's at least one action in the list to undo.
|
||||
|
||||
@see getUndoDescription, undo, canRedo
|
||||
*/
|
||||
bool canUndo() const;
|
||||
|
|
@ -149,7 +147,6 @@ public:
|
|||
String getUndoDescription() const;
|
||||
|
||||
/** Tries to roll-back the last transaction.
|
||||
|
||||
@returns true if the transaction can be undone, and false if it fails, or
|
||||
if there aren't any transactions to undo
|
||||
*/
|
||||
|
|
@ -186,15 +183,23 @@ public:
|
|||
*/
|
||||
int getNumActionsInCurrentTransaction() const;
|
||||
|
||||
/** Returns the time to which the state would be restored if undo() was to be called.
|
||||
If an undo isn't currently possible, it'll return Time().
|
||||
*/
|
||||
Time getTimeOfUndoTransaction() const;
|
||||
|
||||
/** Returns the time to which the state would be restored if redo() was to be called.
|
||||
If a redo isn't currently possible, it'll return Time::getCurrentTime().
|
||||
*/
|
||||
Time getTimeOfRedoTransaction() const;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if there's at least one action in the list to redo.
|
||||
|
||||
@see getRedoDescription, redo, canUndo
|
||||
*/
|
||||
bool canRedo() const;
|
||||
|
||||
/** Returns the description of the transaction that would be next to get redone.
|
||||
|
||||
The description returned is the one that was passed into beginNewTransaction
|
||||
before the set of actions was performed.
|
||||
|
||||
|
|
@ -203,7 +208,6 @@ public:
|
|||
String getRedoDescription() const;
|
||||
|
||||
/** Tries to redo the last transaction that was undone.
|
||||
|
||||
@returns true if the transaction can be redone, and false if it fails, or
|
||||
if there aren't any transactions to redo
|
||||
*/
|
||||
|
|
@ -212,11 +216,15 @@ public:
|
|||
|
||||
private:
|
||||
//==============================================================================
|
||||
OwnedArray <OwnedArray <UndoableAction> > transactions;
|
||||
StringArray transactionNames;
|
||||
struct ActionSet;
|
||||
friend class OwnedArray<ActionSet>;
|
||||
OwnedArray<ActionSet> transactions;
|
||||
String currentTransactionName;
|
||||
int totalUnitsStored, maxNumUnitsToKeep, minimumTransactionsToKeep, nextIndex;
|
||||
bool newTransaction, reentrancyCheck;
|
||||
ActionSet* getCurrentSet() const noexcept;
|
||||
ActionSet* getNextSet() const noexcept;
|
||||
void clearFutureTransactions();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UndoManager);
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue