mirror of
https://github.com/cookiengineer/audacity
synced 2025-05-02 00:29:41 +02:00
377 lines
8.1 KiB
C++
377 lines
8.1 KiB
C++
/**********************************************************************
|
|
|
|
Audacity: A Digital Audio Editor
|
|
|
|
UndoManager.cpp
|
|
|
|
Dominic Mazzoni
|
|
|
|
*******************************************************************//**
|
|
|
|
\class UndoManager
|
|
\brief Works with HistoryWindow to provide the Undo functionality.
|
|
|
|
*//****************************************************************//**
|
|
|
|
\class UndoStackElem
|
|
\brief Holds one item with description and time range for the
|
|
UndoManager
|
|
|
|
*//*******************************************************************/
|
|
|
|
|
|
#include "Audacity.h"
|
|
|
|
#include <wx/hashset.h>
|
|
|
|
#include "BlockFile.h"
|
|
#include "Diags.h"
|
|
#include "Internat.h"
|
|
#include "Sequence.h"
|
|
#include "Track.h"
|
|
#include "WaveTrack.h" // temp
|
|
#include "NoteTrack.h" // for Sonify* function declarations
|
|
#include "Diags.h"
|
|
|
|
#include "UndoManager.h"
|
|
|
|
WX_DECLARE_HASH_SET(BlockFile *, wxPointerHash, wxPointerEqual, Set );
|
|
|
|
UndoManager::UndoManager()
|
|
{
|
|
current = -1;
|
|
saved = -1;
|
|
consolidationCount = 0;
|
|
ResetODChangesFlag();
|
|
}
|
|
|
|
UndoManager::~UndoManager()
|
|
{
|
|
ClearStates();
|
|
}
|
|
|
|
void UndoManager::CalculateSpaceUsage()
|
|
{
|
|
TIMER_START( "CalculateSpaceUsage", space_calc );
|
|
TrackListOfKindIterator iter(Track::Wave);
|
|
|
|
space.Clear();
|
|
space.Add(0, stack.GetCount());
|
|
|
|
Set *prev = new Set;
|
|
Set *cur = new Set;
|
|
|
|
for (size_t i = 0, cnt = stack.GetCount(); i < cnt; i++)
|
|
{
|
|
// Swap map pointers
|
|
Set *swap = prev;
|
|
prev = cur;
|
|
cur = swap;
|
|
|
|
// And clean out the new current map
|
|
cur->clear();
|
|
|
|
// Scan all tracks at current level
|
|
WaveTrack *wt = (WaveTrack *) iter.First(stack[i]->tracks);
|
|
while (wt)
|
|
{
|
|
// Scan all clips within current track
|
|
WaveClipList::compatibility_iterator it = wt->GetClipIterator();
|
|
while (it)
|
|
{
|
|
// Scan all blockfiles within current clip
|
|
BlockArray *blocks = it->GetData()->GetSequenceBlockArray();
|
|
for (size_t b = 0, cnt = blocks->GetCount(); b < cnt; b++)
|
|
{
|
|
BlockFile *file = blocks->Item(b)->f;
|
|
|
|
// Accumulate space used by the file if the file didn't exist
|
|
// in the previous level
|
|
if (prev->count(file) == 0 && cur->count(file) == 0)
|
|
{
|
|
space[i] += file->GetSpaceUsage().GetValue();
|
|
}
|
|
|
|
// Add file to current set
|
|
cur->insert(file);
|
|
}
|
|
|
|
it = it->GetNext();
|
|
}
|
|
|
|
wt = (WaveTrack *) iter.Next();
|
|
}
|
|
}
|
|
|
|
delete cur;
|
|
delete prev;
|
|
TIMER_STOP( space_calc );
|
|
}
|
|
|
|
wxLongLong_t UndoManager::GetLongDescription(unsigned int n, wxString *desc,
|
|
wxString *size)
|
|
{
|
|
n -= 1; // 1 based to zero based
|
|
|
|
wxASSERT(n < stack.Count());
|
|
wxASSERT(space.Count() == stack.Count());
|
|
|
|
*desc = stack[n]->description;
|
|
|
|
*size = Internat::FormatSize(space[n]);
|
|
|
|
return space[n];
|
|
}
|
|
|
|
void UndoManager::GetShortDescription(unsigned int n, wxString *desc)
|
|
{
|
|
n -= 1; // 1 based to zero based
|
|
|
|
wxASSERT(n < stack.Count());
|
|
|
|
*desc = stack[n]->shortDescription;
|
|
}
|
|
|
|
void UndoManager::SetLongDescription(unsigned int n, wxString desc)
|
|
{
|
|
n -= 1;
|
|
|
|
wxASSERT(n < stack.Count());
|
|
|
|
stack[n]->description = desc;
|
|
}
|
|
|
|
void UndoManager::RemoveStateAt(int n)
|
|
{
|
|
stack[n]->tracks->Clear(true);
|
|
delete stack[n]->tracks;
|
|
|
|
UndoStackElem *tmpStackElem = stack[n];
|
|
stack.RemoveAt(n);
|
|
delete tmpStackElem;
|
|
}
|
|
|
|
|
|
void UndoManager::RemoveStates(int num)
|
|
{
|
|
for (int i = 0; i < num; i++) {
|
|
RemoveStateAt(0);
|
|
|
|
current -= 1;
|
|
saved -= 1;
|
|
}
|
|
}
|
|
|
|
void UndoManager::ClearStates()
|
|
{
|
|
RemoveStates(stack.Count());
|
|
}
|
|
|
|
unsigned int UndoManager::GetNumStates()
|
|
{
|
|
return stack.Count();
|
|
}
|
|
|
|
unsigned int UndoManager::GetCurrentState()
|
|
{
|
|
return current + 1; // the array is 0 based, the abstraction is 1 based
|
|
}
|
|
|
|
bool UndoManager::UndoAvailable()
|
|
{
|
|
return (current > 0);
|
|
}
|
|
|
|
bool UndoManager::RedoAvailable()
|
|
{
|
|
return (current < (int)stack.Count() - 1);
|
|
}
|
|
|
|
void UndoManager::ModifyState(TrackList * l,
|
|
const SelectedRegion &selectedRegion)
|
|
{
|
|
if (current == wxNOT_FOUND) {
|
|
return;
|
|
}
|
|
|
|
SonifyBeginModifyState();
|
|
// Delete current
|
|
stack[current]->tracks->Clear(true);
|
|
delete stack[current]->tracks;
|
|
|
|
// Duplicate
|
|
TrackList *tracksCopy = new TrackList();
|
|
TrackListIterator iter(l);
|
|
Track *t = iter.First();
|
|
while (t) {
|
|
tracksCopy->Add(t->Duplicate());
|
|
t = iter.Next();
|
|
}
|
|
|
|
// Replace
|
|
stack[current]->tracks = tracksCopy;
|
|
stack[current]->selectedRegion = selectedRegion;
|
|
SonifyEndModifyState();
|
|
}
|
|
|
|
void UndoManager::PushState(TrackList * l,
|
|
const SelectedRegion &selectedRegion,
|
|
wxString longDescription,
|
|
wxString shortDescription,
|
|
int flags)
|
|
{
|
|
unsigned int i;
|
|
|
|
// If consolidate is set to true, group up to 3 identical operations.
|
|
if (((flags&PUSH_CONSOLIDATE)!=0) && lastAction == longDescription &&
|
|
consolidationCount < 2) {
|
|
consolidationCount++;
|
|
ModifyState(l, selectedRegion);
|
|
// MB: If the "saved" state was modified by ModifyState, reset
|
|
// it so that UnsavedChanges returns true.
|
|
if (current == saved) {
|
|
saved = -1;
|
|
}
|
|
return;
|
|
}
|
|
|
|
consolidationCount = 0;
|
|
|
|
i = current + 1;
|
|
while (i < stack.Count()) {
|
|
RemoveStateAt(i);
|
|
}
|
|
|
|
TrackList *tracksCopy = new TrackList();
|
|
TrackListIterator iter(l);
|
|
Track *t = iter.First();
|
|
while (t) {
|
|
tracksCopy->Add(t->Duplicate());
|
|
t = iter.Next();
|
|
}
|
|
|
|
UndoStackElem *push = new UndoStackElem();
|
|
push->tracks = tracksCopy;
|
|
push->selectedRegion = selectedRegion;
|
|
push->description = longDescription;
|
|
push->shortDescription = shortDescription;
|
|
|
|
stack.Add(push);
|
|
current++;
|
|
|
|
if (saved >= current) {
|
|
saved = -1;
|
|
}
|
|
|
|
lastAction = longDescription;
|
|
}
|
|
|
|
TrackList *UndoManager::SetStateTo(unsigned int n,
|
|
SelectedRegion *selectedRegion)
|
|
{
|
|
n -= 1;
|
|
|
|
wxASSERT(n < stack.Count());
|
|
|
|
current = n;
|
|
|
|
if (current == int(stack.Count()-1)) {
|
|
*selectedRegion = stack[current]->selectedRegion;
|
|
}
|
|
else {
|
|
*selectedRegion = stack[current + 1]->selectedRegion;
|
|
}
|
|
|
|
lastAction = wxT("");
|
|
consolidationCount = 0;
|
|
|
|
return stack[current]->tracks;
|
|
}
|
|
|
|
TrackList *UndoManager::Undo(SelectedRegion *selectedRegion)
|
|
{
|
|
wxASSERT(UndoAvailable());
|
|
|
|
current--;
|
|
|
|
*selectedRegion = stack[current]->selectedRegion;
|
|
|
|
lastAction = wxT("");
|
|
consolidationCount = 0;
|
|
|
|
return stack[current]->tracks;
|
|
}
|
|
|
|
TrackList *UndoManager::Redo(SelectedRegion *selectedRegion)
|
|
{
|
|
wxASSERT(RedoAvailable());
|
|
|
|
current++;
|
|
|
|
*selectedRegion = stack[current]->selectedRegion;
|
|
|
|
/*
|
|
if (!RedoAvailable()) {
|
|
*sel0 = stack[current]->sel0;
|
|
*sel1 = stack[current]->sel1;
|
|
}
|
|
else {
|
|
current++;
|
|
*sel0 = stack[current]->sel0;
|
|
*sel1 = stack[current]->sel1;
|
|
current--;
|
|
}
|
|
*/
|
|
|
|
lastAction = wxT("");
|
|
consolidationCount = 0;
|
|
|
|
return stack[current]->tracks;
|
|
}
|
|
|
|
bool UndoManager::UnsavedChanges()
|
|
{
|
|
return (saved != current) || HasODChangesFlag();
|
|
}
|
|
|
|
void UndoManager::StateSaved()
|
|
{
|
|
saved = current;
|
|
ResetODChangesFlag();
|
|
}
|
|
|
|
// currently unused
|
|
//void UndoManager::Debug()
|
|
//{
|
|
// for (unsigned int i = 0; i < stack.Count(); i++) {
|
|
// TrackListIterator iter(stack[i]->tracks);
|
|
// WaveTrack *t = (WaveTrack *) (iter.First());
|
|
// wxPrintf(wxT("*%d* %s %f\n"), i, (i == (unsigned int)current) ? wxT("-->") : wxT(" "),
|
|
// t ? t->GetEndTime()-t->GetStartTime() : 0);
|
|
// }
|
|
//}
|
|
|
|
///to mark as unsaved changes without changing the state/tracks.
|
|
void UndoManager::SetODChangesFlag()
|
|
{
|
|
mODChangesMutex.Lock();
|
|
mODChanges=true;
|
|
mODChangesMutex.Unlock();
|
|
}
|
|
|
|
bool UndoManager::HasODChangesFlag()
|
|
{
|
|
bool ret;
|
|
mODChangesMutex.Lock();
|
|
ret=mODChanges;
|
|
mODChangesMutex.Unlock();
|
|
return ret;
|
|
}
|
|
|
|
void UndoManager::ResetODChangesFlag()
|
|
{
|
|
mODChangesMutex.Lock();
|
|
mODChanges=false;
|
|
mODChangesMutex.Unlock();
|
|
}
|