mirror of
https://github.com/cookiengineer/audacity
synced 2025-05-11 22:51:06 +02:00
392 lines
8.7 KiB
C++
392 lines
8.7 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 "BlockFile.h"
|
|
#include "Internat.h"
|
|
#include "Sequence.h"
|
|
#include "Track.h"
|
|
#include "WaveTrack.h" // temp
|
|
#include "NoteTrack.h" // for Sonify* function declarations
|
|
|
|
#include <map>
|
|
#include <set>
|
|
|
|
#include "UndoManager.h"
|
|
|
|
UndoManager::UndoManager()
|
|
{
|
|
current = -1;
|
|
saved = -1;
|
|
consolidationCount = 0;
|
|
ResetODChangesFlag();
|
|
}
|
|
|
|
UndoManager::~UndoManager()
|
|
{
|
|
ClearStates();
|
|
}
|
|
|
|
// get the sum of the sizes of all blocks this track list
|
|
// references. However, if a block is referred to multiple
|
|
// times it is only counted once. Return value is in bytes.
|
|
wxLongLong UndoManager::CalculateSpaceUsage(int index)
|
|
{
|
|
TrackListOfKindIterator iter(Track::Wave);
|
|
WaveTrack *wt;
|
|
WaveClipList::compatibility_iterator it;
|
|
BlockArray *blocks;
|
|
unsigned int i;
|
|
|
|
// get a map of all blocks referenced in this TrackList
|
|
std::map<BlockFile*, wxLongLong> cur;
|
|
|
|
wt = (WaveTrack *) iter.First(stack[index]->tracks);
|
|
while (wt) {
|
|
for (it = wt->GetClipIterator(); it; it = it->GetNext()) {
|
|
blocks = it->GetData()->GetSequenceBlockArray();
|
|
for (i = 0; i < blocks->GetCount(); i++)
|
|
{
|
|
BlockFile* pBlockFile = blocks->Item(i)->f;
|
|
if (pBlockFile->GetFileName().FileExists())
|
|
cur[pBlockFile] = pBlockFile->GetSpaceUsage();
|
|
}
|
|
}
|
|
wt = (WaveTrack *) iter.Next();
|
|
}
|
|
|
|
if (index > 0) {
|
|
// get a set of all blocks referenced in all prev TrackList
|
|
std::set<BlockFile*> prev;
|
|
while (--index) {
|
|
wt = (WaveTrack *) iter.First(stack[index]->tracks);
|
|
while (wt) {
|
|
for (it = wt->GetClipIterator(); it; it = it->GetNext()) {
|
|
blocks = it->GetData()->GetSequenceBlockArray();
|
|
for (i = 0; i < blocks->GetCount(); i++) {
|
|
prev.insert(blocks->Item(i)->f);
|
|
}
|
|
}
|
|
wt = (WaveTrack *) iter.Next();
|
|
}
|
|
}
|
|
|
|
// remove all blocks in prevBlockFiles from curBlockFiles
|
|
std::set<BlockFile*>::const_iterator prevIter;
|
|
for (prevIter = prev.begin(); prevIter != prev.end(); prevIter++) {
|
|
cur.erase(*prevIter);
|
|
}
|
|
}
|
|
|
|
// sum the sizes of the blocks remaining in curBlockFiles;
|
|
wxLongLong bytes = 0;
|
|
std::map<BlockFile*, wxLongLong>::const_iterator curIter;
|
|
for (curIter = cur.begin(); curIter != cur.end(); curIter++) {
|
|
bytes += curIter->second;
|
|
}
|
|
|
|
return bytes;
|
|
}
|
|
|
|
void UndoManager::GetLongDescription(unsigned int n, wxString *desc,
|
|
wxString *size)
|
|
{
|
|
n -= 1; // 1 based to zero based
|
|
|
|
wxASSERT(n < stack.Count());
|
|
|
|
*desc = stack[n]->description;
|
|
|
|
*size = Internat::FormatSize(stack[n]->spaceUsage);
|
|
}
|
|
|
|
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, double sel0, double sel1)
|
|
{
|
|
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]->sel0 = sel0;
|
|
stack[current]->sel1 = sel1;
|
|
SonifyEndModifyState();
|
|
}
|
|
|
|
void UndoManager::PushState(TrackList * l, double sel0, double sel1,
|
|
wxString longDescription,
|
|
wxString shortDescription,
|
|
bool consolidate)
|
|
{
|
|
unsigned int i;
|
|
|
|
// If consolidate is set to true, group up to 3 identical operations.
|
|
|
|
if (consolidate && lastAction == longDescription &&
|
|
consolidationCount < 2) {
|
|
consolidationCount++;
|
|
ModifyState(l, sel0, sel1);
|
|
// 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->sel0 = sel0;
|
|
push->sel1 = sel1;
|
|
push->description = longDescription;
|
|
push->shortDescription = shortDescription;
|
|
push->spaceUsage = 0; // Calculate actual value after it's on the stack.
|
|
|
|
stack.Add(push);
|
|
current++;
|
|
push->spaceUsage = this->CalculateSpaceUsage(current);
|
|
|
|
if (saved >= current) {
|
|
saved = -1;
|
|
}
|
|
|
|
lastAction = longDescription;
|
|
}
|
|
|
|
TrackList *UndoManager::SetStateTo(unsigned int n, double *sel0, double *sel1)
|
|
{
|
|
n -= 1;
|
|
|
|
wxASSERT(n < stack.Count());
|
|
|
|
current = n;
|
|
|
|
if (current == int(stack.Count()-1)) {
|
|
*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;
|
|
}
|
|
|
|
TrackList *UndoManager::Undo(double *sel0, double *sel1)
|
|
{
|
|
wxASSERT(UndoAvailable());
|
|
|
|
current--;
|
|
|
|
*sel0 = stack[current]->sel0;
|
|
*sel1 = stack[current]->sel1;
|
|
|
|
lastAction = wxT("");
|
|
consolidationCount = 0;
|
|
|
|
return stack[current]->tracks;
|
|
}
|
|
|
|
TrackList *UndoManager::Redo(double *sel0, double *sel1)
|
|
{
|
|
wxASSERT(RedoAvailable());
|
|
|
|
current++;
|
|
|
|
*sel0 = stack[current]->sel0;
|
|
*sel1 = stack[current]->sel1;
|
|
|
|
/*
|
|
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();
|
|
}
|
|
|
|
|
|
|
|
// Indentation settings for Vim and Emacs and unique identifier for Arch, a
|
|
// version control system. Please do not modify past this point.
|
|
//
|
|
// Local Variables:
|
|
// c-basic-offset: 3
|
|
// indent-tabs-mode: nil
|
|
// End:
|
|
//
|
|
// vim: et sts=3 sw=3
|
|
// arch-tag: 726e879c-98e1-4721-9488-bae71a171cc2
|
|
|