mirror of
https://github.com/cookiengineer/audacity
synced 2025-09-17 16:50:26 +02:00
A 4hr track used to take about 20s to cut a few samples. This is now significantly improved, to around 3s. Leland did this by (a) moving the size calculation to when we examine the undo history, so it isn't slowing down the edits. (b) in size calculation, using sizes that are cached rather than going to disk to find the sizes. (c) writing the autosave file which is to an FFIle to a string first, i.e. using XMLStringWriter as a buffer for XMLFileWriter. Step (c) may also make autosave marginally safer, as the risk of a partially updated autosave file is reduced.
371 lines
7.9 KiB
C++
371 lines
7.9 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 "Internat.h"
|
|
#include "Sequence.h"
|
|
#include "Track.h"
|
|
#include "WaveTrack.h" // temp
|
|
#include "NoteTrack.h" // for Sonify* function declarations
|
|
|
|
#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()
|
|
{
|
|
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))
|
|
{
|
|
space[i] += file->GetSpaceUsage().GetValue();
|
|
}
|
|
|
|
// Add file to current set
|
|
cur->insert(file);
|
|
}
|
|
|
|
it = it->GetNext();
|
|
}
|
|
|
|
wt = (WaveTrack *) iter.Next();
|
|
}
|
|
}
|
|
|
|
delete cur;
|
|
delete prev;
|
|
}
|
|
|
|
void 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]);
|
|
}
|
|
|
|
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();
|
|
}
|