1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-05-01 16:19:43 +02:00

Bug1327, 1337: Changes to tags (edit, import or export) should undo and redo

This commit is contained in:
Paul Licameli 2016-02-24 19:25:52 -05:00
commit b3f60bc222
35 changed files with 399 additions and 251 deletions

View File

@ -42,6 +42,7 @@
#include "../images/Arrow.xpm"
#include "../images/Empty9x16.xpm"
#include "BatchCommands.h"
#include "Track.h"
#include "UndoManager.h"
#include "Theme.h"

View File

@ -36,6 +36,7 @@ undo memory so as to free up space.
#include "UndoManager.h"
#include "Project.h"
#include "ShuttleGui.h"
#include "Track.h"
enum {
ID_AVAIL = 1000,

View File

@ -58,6 +58,7 @@ for drawing different aspects of the label and its text box.
#include "Project.h"
#include "TrackArtist.h"
#include "TrackPanel.h"
#include "UndoManager.h"
#include "commands/CommandManager.h"
#include "effects/TimeWarper.h"
@ -1132,7 +1133,7 @@ double LabelTrack::GetEndTime() const
return end;
}
Track *LabelTrack::Duplicate()
Track *LabelTrack::Duplicate() const
{
return new LabelTrack(*this);
}
@ -2066,7 +2067,7 @@ void LabelTrack::OnContextMenu(wxCommandEvent & evt)
{
p->PushState(_("Modified Label"),
_("Label Edit"),
PUSH_CONSOLIDATE);
UndoPush::CONSOLIDATE);
}
break;
@ -2081,7 +2082,7 @@ void LabelTrack::OnContextMenu(wxCommandEvent & evt)
{
p->PushState(_("Modified Label"),
_("Label Edit"),
true /* consolidate */);
UndoPush::CONSOLIDATE);
}
break;
@ -2093,7 +2094,7 @@ void LabelTrack::OnContextMenu(wxCommandEvent & evt)
DeleteLabel(ndx);
p->PushState(_("Deleted Label"),
_("Label Edit"),
true /* consolidate */);
UndoPush::CONSOLIDATE);
}
break;
}

View File

@ -139,7 +139,7 @@ class AUDACITY_DLL_API LabelTrack : public Track
virtual double GetStartTime() const;
virtual double GetEndTime() const;
virtual Track *Duplicate();
virtual Track *Duplicate() const;
virtual void SetSelected(bool s);

View File

@ -119,6 +119,7 @@ simplifies construction of menu items.
#include "Snap.h"
#include "UndoManager.h"
#include "WaveTrack.h"
#if defined(EXPERIMENTAL_CRASH_REPORT)
@ -1598,10 +1599,10 @@ void AudacityProject::CreateRecentFilesMenu(CommandManager *c)
void AudacityProject::ModifyUndoMenuItems()
{
wxString desc;
int cur = mUndoManager.GetCurrentState();
int cur = GetUndoManager()->GetCurrentState();
if (mUndoManager.UndoAvailable()) {
mUndoManager.GetShortDescription(cur, &desc);
if (GetUndoManager()->UndoAvailable()) {
GetUndoManager()->GetShortDescription(cur, &desc);
mCommandManager.Modify(wxT("Undo"),
wxString::Format(_("&Undo %s"),
desc.c_str()));
@ -1611,8 +1612,8 @@ void AudacityProject::ModifyUndoMenuItems()
wxString::Format(_("&Undo")));
}
if (mUndoManager.RedoAvailable()) {
mUndoManager.GetShortDescription(cur+1, &desc);
if (GetUndoManager()->RedoAvailable()) {
GetUndoManager()->GetShortDescription(cur+1, &desc);
mCommandManager.Modify(wxT("Redo"),
wxString::Format(_("&Redo %s"),
desc.c_str()));
@ -1756,16 +1757,16 @@ wxUint32 AudacityProject::GetUpdateFlags()
if((msClipT1 - msClipT0) > 0.0)
flags |= ClipboardFlag;
if (mUndoManager.UnsavedChanges())
if (GetUndoManager()->UnsavedChanges())
flags |= UnsavedChangesFlag;
if (!mLastEffect.IsEmpty())
flags |= HasLastEffectFlag;
if (mUndoManager.UndoAvailable())
if (GetUndoManager()->UndoAvailable())
flags |= UndoAvailableFlag;
if (mUndoManager.RedoAvailable())
if (GetUndoManager()->RedoAvailable())
flags |= RedoAvailableFlag;
if (ZoomInAvailable() && (flags & TracksExistFlag))
@ -3761,7 +3762,7 @@ void AudacityProject::OnPrint()
void AudacityProject::OnUndo()
{
if (!mUndoManager.UndoAvailable()) {
if (!GetUndoManager()->UndoAvailable()) {
wxMessageBox(_("Nothing to undo"));
return;
}
@ -3771,8 +3772,8 @@ void AudacityProject::OnUndo()
return;
}
TrackList *l = mUndoManager.Undo(&mViewInfo.selectedRegion);
PopState(l);
const UndoState &state = GetUndoManager()->Undo(&mViewInfo.selectedRegion);
PopState(state);
mTrackPanel->SetFocusedTrack(NULL);
mTrackPanel->EnsureVisible(mTrackPanel->GetFirstSelectedTrack());
@ -3787,7 +3788,7 @@ void AudacityProject::OnUndo()
void AudacityProject::OnRedo()
{
if (!mUndoManager.RedoAvailable()) {
if (!GetUndoManager()->RedoAvailable()) {
wxMessageBox(_("Nothing to redo"));
return;
}
@ -3796,8 +3797,8 @@ void AudacityProject::OnRedo()
return;
}
TrackList *l = mUndoManager.Redo(&mViewInfo.selectedRegion);
PopState(l);
const UndoState &state = GetUndoManager()->Redo(&mViewInfo.selectedRegion);
PopState(state);
mTrackPanel->SetFocusedTrack(NULL);
mTrackPanel->EnsureVisible(mTrackPanel->GetFirstSelectedTrack());
@ -5262,7 +5263,7 @@ void AudacityProject::OnShowClipping()
void AudacityProject::OnHistory()
{
if (!mHistoryWindow)
mHistoryWindow = new HistoryWindow(this, &mUndoManager);
mHistoryWindow = new HistoryWindow(this, GetUndoManager());
mHistoryWindow->Show();
mHistoryWindow->Raise();
mHistoryWindow->UpdateDisplay();
@ -5581,8 +5582,24 @@ void AudacityProject::OnImportRaw()
void AudacityProject::OnEditMetadata()
{
if (mTags->ShowEditDialog(this, _("Edit Metadata Tags"), true))
PushState(_("Edit Metadata Tags"), _("Metadata Tags"));
(void)DoEditMetadata(_("Edit Metadata Tags"), _("Metadata Tags"), true);
}
bool AudacityProject::DoEditMetadata
(const wxString &title, const wxString &shortUndoDescription, bool force)
{
// Back up my tags
auto newTags = mTags->Duplicate();
if (newTags->ShowEditDialog(this, title, force)) {
// Commit the change to project state only now.
mTags = newTags;
PushState(title, shortUndoDescription);
return true;
}
return false;
}
void AudacityProject::HandleMixAndRender(bool toNewTrack)

View File

@ -320,6 +320,7 @@ void OnImportMIDI();
void OnImportRaw();
void OnEditMetadata();
bool DoEditMetadata(const wxString &title, const wxString &shortUndoDescription, bool force);
void OnMixAndRender();
void OnMixAndRenderToNewTrack();

View File

@ -27,6 +27,7 @@
#endif
#include "Project.h"
#include "TrackPanel.h" // for EVT_TRACK_PANEL_TIMER
#include "UndoManager.h"
#include "WaveTrack.h"
#include "widgets/Meter.h"
@ -405,7 +406,7 @@ void MixerTrackCluster::HandleSliderGain(const bool bWantPushState /*= false*/)
mProject->RefreshTPTrack(mLeftTrack);
#endif
if (bWantPushState)
mProject->TP_PushState(_("Moved gain slider"), _("Gain"), PUSH_CONSOLIDATE );
mProject->TP_PushState(_("Moved gain slider"), _("Gain"), UndoPush::CONSOLIDATE );
}
void MixerTrackCluster::HandleSliderPan(const bool bWantPushState /*= false*/)
@ -424,7 +425,7 @@ void MixerTrackCluster::HandleSliderPan(const bool bWantPushState /*= false*/)
#endif
if (bWantPushState)
mProject->TP_PushState(_("Moved pan slider"), _("Pan"), PUSH_CONSOLIDATE );
mProject->TP_PushState(_("Moved pan slider"), _("Pan"), UndoPush::CONSOLIDATE );
}
void MixerTrackCluster::ResetMeter(const bool bResetClipping)

View File

@ -135,7 +135,7 @@ NoteTrack::~NoteTrack()
}
}
Track *NoteTrack::Duplicate()
Track *NoteTrack::Duplicate() const
{
NoteTrack *duplicate = new NoteTrack(mDirManager);
duplicate->Init(*this);

View File

@ -57,7 +57,7 @@ class AUDACITY_DLL_API NoteTrack:public Track {
NoteTrack(DirManager * projDirManager);
virtual ~NoteTrack();
virtual Track *Duplicate();
virtual Track *Duplicate() const;
virtual int GetKind() const { return Note; }

View File

@ -143,6 +143,8 @@ scroll information. It also has some status flags.
#include "FileDialog.h"
#include "UndoManager.h"
#include "toolbars/ToolManager.h"
#include "toolbars/ControlToolBar.h"
#include "toolbars/DeviceToolBar.h"
@ -796,6 +798,7 @@ AudacityProject::AudacityProject(wxWindow * parent, wxWindowID id,
mSelectionFormat(gPrefs->Read(wxT("/SelectionFormat"), wxT(""))),
mFrequencySelectionFormatName(gPrefs->Read(wxT("/FrequencySelectionFormatName"), wxT(""))),
mBandwidthSelectionFormatName(gPrefs->Read(wxT("/BandwidthSelectionFormatName"), wxT(""))),
mUndoManager(safenew UndoManager),
mDirty(false),
mRuler(NULL),
mTrackPanel(NULL),
@ -1023,6 +1026,9 @@ AudacityProject::AudacityProject(wxWindow * parent, wxWindowID id,
// MM: Give track panel the focus to ensure keyboard commands work
mTrackPanel->SetFocus();
// Create tags object
mTags = std::make_shared<Tags>();
InitialState();
FixScrollbars();
mRuler->SetLeftOffset(mTrackPanel->GetLeftOffset()); // bevel on AdornedRuler
@ -1048,9 +1054,6 @@ AudacityProject::AudacityProject(wxWindow * parent, wxWindowID id,
#endif
mIconized = false;
// Create tags object
mTags = new Tags();
mTrackFactory = new TrackFactory(mDirManager, &mViewInfo);
int widths[] = {0, GetControlToolBar()->WidthForStatusBar(mStatusBar), -1, 150};
@ -1247,9 +1250,9 @@ bool AudacityProject::IsAudioActive() const
gAudioIO->IsStreamActive(GetAudioIOToken());
}
Tags *AudacityProject::GetTags()
const Tags *AudacityProject::GetTags()
{
return mTags;
return mTags.get();
}
wxString AudacityProject::GetName()
@ -2211,7 +2214,7 @@ void AudacityProject::OnCloseWindow(wxCloseEvent & event)
// We may not bother to prompt the user to save, if the
// project is now empty.
if (event.CanVeto() && (mEmptyCanBeDirty || bHasTracks)) {
if (mUndoManager.UnsavedChanges()) {
if (GetUndoManager()->UnsavedChanges()) {
wxString Message = _("Save changes before closing?");
if( !bHasTracks )
@ -2317,8 +2320,7 @@ void AudacityProject::OnCloseWindow(wxCloseEvent & event)
delete mTrackFactory;
mTrackFactory = NULL;
delete mTags;
mTags = NULL;
mTags.reset();
delete mImportXMLTagHandler;
mImportXMLTagHandler = NULL;
@ -2336,7 +2338,7 @@ void AudacityProject::OnCloseWindow(wxCloseEvent & event)
// This must be done before the following Deref() since it holds
// references to the DirManager.
mUndoManager.ClearStates();
GetUndoManager()->ClearStates();
// MM: Tell the DirManager it can now DELETE itself
// if it finds it is no longer needed. If it is still
@ -3179,7 +3181,7 @@ bool AudacityProject::HandleXMLTag(const wxChar *tag, const wxChar **attrs)
XMLTagHandler *AudacityProject::HandleXMLChild(const wxChar *tag)
{
if (!wxStrcmp(tag, wxT("tags"))) {
return mTags;
return mTags.get();
}
if (!wxStrcmp(tag, wxT("wavetrack"))) {
@ -3411,7 +3413,7 @@ bool AudacityProject::Save(bool overwrite /* = true */ ,
bool bHasTracks = (iter.First() != NULL);
if (!bHasTracks)
{
if (mUndoManager.UnsavedChanges() && mEmptyCanBeDirty) {
if (GetUndoManager()->UnsavedChanges() && mEmptyCanBeDirty) {
int result = wxMessageBox(_("Your project is now empty.\nIf saved, the project will have no tracks.\n\nTo save any previously open tracks:\nClick 'No', Edit > Undo until all tracks\nare open, then File > Save Project.\n\nSave anyway?"),
_("Warning - Empty Project"),
wxYES_NO | wxICON_QUESTION, this);
@ -3596,7 +3598,7 @@ bool AudacityProject::Save(bool overwrite /* = true */ ,
t = iter.Next();
}
mUndoManager.StateSaved();
GetUndoManager()->StateSaved();
}
// If we get here, saving the project was successful, so we can DELETE
@ -3790,12 +3792,40 @@ bool AudacityProject::Import(const wxString &fileName, WaveTrackArray* pTrackArr
{
Track **newTracks;
int numTracks;
wxString errorMessage=wxT("");
wxString errorMessage = wxEmptyString;
// Backup Tags, before the import. Be prepared to roll back changes.
struct TempTags {
TempTags(std::shared_ptr<Tags> & pTags_)
: pTags(pTags_)
{
oldTags = pTags;
if (oldTags)
pTags = oldTags->Duplicate();
}
~TempTags()
{
if (oldTags) {
// roll back
pTags = oldTags;
}
}
void Commit()
{
oldTags.reset();
}
std::shared_ptr<Tags> & pTags;
std::shared_ptr<Tags> oldTags;
};
TempTags tempTags(mTags);
numTracks = Importer::Get().Import(fileName,
mTrackFactory,
&newTracks,
mTags,
mTags.get(),
errorMessage);
if (!errorMessage.IsEmpty()) {
@ -3811,9 +3841,17 @@ bool AudacityProject::Import(const wxString &fileName, WaveTrackArray* pTrackArr
wxGetApp().AddFileToHistory(fileName);
// no more errors
tempTags.Commit();
// for LOF ("list of files") files, do not import the file as if it
// were an audio file itself
if (fileName.AfterLast('.').IsSameAs(wxT("lof"), false)) {
// PRL: don't redundantly do the steps below, because we already
// did it in case of LOF, because of some weird recursion back to this
// same function. I think this should be untangled.
// So Undo history push is not bypassed, despite appearances.
return false;
}
@ -3827,6 +3865,7 @@ bool AudacityProject::Import(const wxString &fileName, WaveTrackArray* pTrackArr
}
}
// PRL: Undo history is incremented inside this:
AddImportedTracks(fileName, newTracks, numTracks);
int mode = gPrefs->Read(wxT("/AudioFiles/NormalizeOnLoad"), 0L);
@ -3979,12 +4018,12 @@ void AudacityProject::InitialState()
mImportXMLTagHandler = NULL;
}
mUndoManager.ClearStates();
GetUndoManager()->ClearStates();
mUndoManager.PushState(mTracks, mViewInfo.selectedRegion,
GetUndoManager()->PushState(mTracks, mViewInfo.selectedRegion, mTags,
_("Created new project"), wxT(""));
mUndoManager.StateSaved();
GetUndoManager()->StateSaved();
if (mHistoryWindow)
mHistoryWindow->UpdateDisplay();
@ -3996,11 +4035,16 @@ void AudacityProject::InitialState()
this->UpdateMixerBoard();
}
void AudacityProject::PushState(const wxString &desc, const wxString &shortDesc)
{
PushState(desc, shortDesc, UndoPush::AUTOSAVE);
}
void AudacityProject::PushState(const wxString &desc,
const wxString &shortDesc,
int flags )
UndoPush flags )
{
mUndoManager.PushState(mTracks, mViewInfo.selectedRegion,
GetUndoManager()->PushState(mTracks, mViewInfo.selectedRegion, mTags,
desc, shortDesc, flags);
mDirty = true;
@ -4025,7 +4069,7 @@ void AudacityProject::PushState(const wxString &desc,
if (GetTracksFitVerticallyZoomed())
this->DoZoomFitV();
if( (flags & PUSH_AUTOSAVE)!= 0)
if((flags & UndoPush::AUTOSAVE) != UndoPush::MINIMAL)
AutoSave();
}
@ -4036,7 +4080,7 @@ void AudacityProject::RollbackState()
void AudacityProject::ModifyState(bool bWantsAutoSave)
{
mUndoManager.ModifyState(mTracks, mViewInfo.selectedRegion);
GetUndoManager()->ModifyState(mTracks, mViewInfo.selectedRegion, mTags);
if (bWantsAutoSave)
AutoSave();
}
@ -4044,10 +4088,15 @@ void AudacityProject::ModifyState(bool bWantsAutoSave)
// LL: Is there a memory leak here as "l" and "t" are not deleted???
// Vaughan, 2010-08-29: No, as "l" is a TrackList* of an Undo stack state.
// Need to keep it and its tracks "t" available for Undo/Redo/SetStateTo.
void AudacityProject::PopState(TrackList * l)
void AudacityProject::PopState(const UndoState &state)
{
// Restore tags
mTags = state.tags;
TrackList *const tracks = state.tracks.get();
mTracks->Clear(true);
TrackListIterator iter(l);
TrackListIterator iter(tracks);
Track *t = iter.First();
bool odUsed = false;
ODComputeSummaryTask* computeTask = NULL;
@ -4096,9 +4145,9 @@ void AudacityProject::PopState(TrackList * l)
void AudacityProject::SetStateTo(unsigned int n)
{
TrackList *l =
mUndoManager.SetStateTo(n, &mViewInfo.selectedRegion);
PopState(l);
const UndoState &state =
GetUndoManager()->SetStateTo(n, &mViewInfo.selectedRegion);
PopState(state);
HandleResize();
mTrackPanel->SetFocusedTrack(NULL);
@ -4699,7 +4748,7 @@ void AudacityProject::RefreshTPTrack(Track* pTrk, bool refreshbacking /*= true*/
// TrackPanel callback method
void AudacityProject::TP_PushState(const wxString &desc, const wxString &shortDesc,
int flags)
UndoPush flags)
{
PushState(desc, shortDesc, flags);
}
@ -4965,7 +5014,7 @@ void AudacityProject::SetTrackGain(Track * track, LWSlider * slider)
if (link)
link->SetGain(newValue);
PushState(_("Adjusted gain"), _("Gain"), PUSH_CONSOLIDATE);
PushState(_("Adjusted gain"), _("Gain"), UndoPush::CONSOLIDATE);
GetTrackPanel()->RefreshTrack(track);
}
@ -4982,7 +5031,7 @@ void AudacityProject::SetTrackPan(Track * track, LWSlider * slider)
if (link)
link->SetPan(newValue);
PushState(_("Adjusted Pan"), _("Pan"), PUSH_CONSOLIDATE);
PushState(_("Adjusted Pan"), _("Pan"), UndoPush::CONSOLIDATE);
GetTrackPanel()->RefreshTrack(track);
}

View File

@ -22,7 +22,6 @@
#include "Experimental.h"
#include "DirManager.h"
#include "UndoManager.h"
#include "ViewInfo.h"
#include "TrackPanelListener.h"
#include "AudioIOListener.h"
@ -85,11 +84,14 @@ class MixerBoard;
class MixerBoardFrame;
struct AudioIOStartStreamOptions;
struct UndoState;
class WaveTrackArray;
class Regions;
class LWSlider;
class UndoManager;
enum class UndoPush;
AudacityProject *CreateNewAudacityProject();
AUDACITY_DLL_API AudacityProject *GetActiveProject();
@ -157,7 +159,7 @@ class AUDACITY_DLL_API AudacityProject: public wxFrame,
AudioIOStartStreamOptions GetDefaultPlayOptions();
TrackList *GetTracks() { return mTracks; }
UndoManager *GetUndoManager() { return &mUndoManager; }
UndoManager *GetUndoManager() { return mUndoManager.get(); }
sampleFormat GetDefaultFormat() { return mDefaultFormat; }
@ -183,7 +185,7 @@ class AUDACITY_DLL_API AudacityProject: public wxFrame,
DirManager *GetDirManager();
TrackFactory *GetTrackFactory();
AdornedRulerPanel *GetRulerPanel();
Tags *GetTags();
const Tags *GetTags();
int GetAudioIOToken() const;
bool IsAudioActive() const;
void SetAudioIOToken(int token);
@ -399,7 +401,7 @@ class AUDACITY_DLL_API AudacityProject: public wxFrame,
virtual ToolsToolBar * TP_GetToolsToolBar();
virtual void TP_PushState(const wxString &longDesc, const wxString &shortDesc,
int flags) override;
UndoPush flags) override;
virtual void TP_ModifyState(bool bWantsAutoSave); // if true, writes auto-save file. Should set only if you really want the state change restored after
// a crash, as it can take many seconds for large (eg. 10 track-hours) projects
virtual void TP_RedrawScrollbars();
@ -486,8 +488,8 @@ public:
static void AllProjectsDeleteLock();
static void AllProjectsDeleteUnlock();
void PushState(const wxString &desc, const wxString &shortDesc,
int flags = PUSH_AUTOSAVE);
void PushState(const wxString &desc, const wxString &shortDesc); // use UndoPush::AUTOSAVE
void PushState(const wxString &desc, const wxString &shortDesc, UndoPush flags);
void RollbackState();
private:
@ -497,7 +499,7 @@ public:
void InitialState();
void ModifyState(bool bWantsAutoSave); // if true, writes auto-save file. Should set only if you really want the state change restored after
// a crash, as it can take many seconds for large (eg. 10 track-hours) projects
void PopState(TrackList * l);
void PopState(const UndoState &state);
void UpdateLyrics();
void UpdateMixerBoard();
@ -524,7 +526,10 @@ public:
wxMenu *mRecentFilesMenu;
// Tags (artist name, song properties, MP3 ID3 info, etc.)
Tags *mTags;
// The structure may be shared with undo history entries
// To keep undo working correctly, always replace this with a new duplicate
// BEFORE doing any editing of it!
std::shared_ptr<Tags> mTags;
// List of tracks and display info
TrackList *mTracks;
@ -546,7 +551,7 @@ public:
static ODLock *msAllProjectDeleteMutex;
// History/Undo manager
UndoManager mUndoManager;
std::unique_ptr<UndoManager> mUndoManager;
bool mDirty;
// Commands

View File

@ -233,6 +233,11 @@ Tags::~Tags()
{
}
std::shared_ptr<Tags> Tags::Duplicate() const
{
return std::make_shared<Tags>(*this);
}
Tags & Tags::operator=(const Tags & src)
{
mEditTitle = src.mEditTitle;
@ -371,53 +376,38 @@ int Tags::GetGenre(const wxString & name)
return 255;
}
bool Tags::HasTag(const wxString & name)
bool Tags::HasTag(const wxString & name) const
{
wxString key = name;
key.UpperCase();
TagMap::iterator iter = mXref.find(key);
auto iter = mXref.find(key);
return (iter != mXref.end());
}
wxString Tags::GetTag(const wxString & name)
wxString Tags::GetTag(const wxString & name) const
{
wxString key = name;
key.UpperCase();
TagMap::iterator iter = mXref.find(key);
auto iter = mXref.find(key);
if (iter == mXref.end()) {
return wxEmptyString;
}
return mMap[iter->second];
auto iter2 = mMap.find(iter->second);
if (iter2 == mMap.end()) {
wxASSERT(false);
return wxEmptyString;
}
else
return iter2->second;
}
bool Tags::GetFirst(wxString & name, wxString & value)
Tags::Iterators Tags::GetRange() const
{
mIter = mMap.begin();
if (mIter == mMap.end()) {
return false;
}
name = mIter->first;
value = mIter->second;
return true;
}
bool Tags::GetNext(wxString & name, wxString & value)
{
++mIter;
if (mIter == mMap.end()) {
return false;
}
name = mIter->first;
value = mIter->second;
return true;
return std::make_pair(mMap.begin(), mMap.end());
}
void Tags::SetTag(const wxString & name, const wxString & value)
@ -510,8 +500,9 @@ void Tags::WriteXML(XMLWriter &xmlFile)
{
xmlFile.StartTag(wxT("tags"));
wxString n, v;
for (bool cont = GetFirst(n, v); cont; cont = GetNext(n, v)) {
for (const auto &pair : GetRange()) {
const auto &n = pair.first;
const auto &v = pair.second;
xmlFile.StartTag(wxT("tag"));
xmlFile.WriteAttr(wxT("name"), n);
xmlFile.WriteAttr(wxT("value"), v);
@ -863,8 +854,6 @@ bool TagsEditor::TransferDataFromWindow()
bool TagsEditor::TransferDataToWindow()
{
size_t i;
wxString n;
wxString v;
TagMap popTagMap;
// Disable redrawing until we're done
@ -895,12 +884,14 @@ bool TagsEditor::TransferDataToWindow()
}
// Populate the rest
for (bool cont = mLocal.GetFirst(n, v); cont; cont = mLocal.GetNext(n, v)) {
if ( popTagMap.find(n) == popTagMap.end() ) {
for (const auto &pair : mLocal.GetRange()) {
const auto &n = pair.first;
const auto &v = pair.second;
if (popTagMap.find(n) == popTagMap.end()) {
mGrid->AppendRows();
mGrid->SetCellValue(i, 0, n);
mGrid->SetCellValue(i, 1, v);
i++;
i++;
}
}
@ -1186,8 +1177,9 @@ void TagsEditor::OnSaveDefaults(wxCommandEvent & WXUNUSED(event))
gPrefs->DeleteGroup(wxT("/Tags"));
// Write out each tag
wxString n, v;
for (bool cont = mLocal.GetFirst(n, v); cont; cont = mLocal.GetNext(n, v)) {
for (const auto &pair : mLocal.GetRange()) {
const auto &n = pair.first;
const auto &v = pair.second;
gPrefs->Write(wxT("/Tags/") + n, v);
}
gPrefs->Flush();

View File

@ -33,6 +33,7 @@
#include "widgets/Grid.h"
#include "xml/XMLTagHandler.h"
#include <utility>
#include <wx/dialog.h>
#include <wx/hashmap.h>
#include <wx/notebook.h>
@ -76,6 +77,8 @@ class AUDACITY_DLL_API Tags: public XMLTagHandler {
Tags(); // constructor
virtual ~Tags();
std::shared_ptr<Tags> Duplicate() const;
Tags & operator= (const Tags & src );
bool ShowEditDialog(wxWindow *parent, const wxString &title, bool force = false);
@ -96,11 +99,17 @@ class AUDACITY_DLL_API Tags: public XMLTagHandler {
wxString GetGenre(int value);
int GetGenre(const wxString & name);
bool HasTag(const wxString & name);
wxString GetTag(const wxString & name);
bool HasTag(const wxString & name) const;
wxString GetTag(const wxString & name) const;
bool GetFirst(wxString & name, wxString & value);
bool GetNext(wxString & name, wxString & value);
using IterPair = std::pair<TagMap::const_iterator, TagMap::const_iterator>;
struct Iterators : public IterPair {
Iterators(IterPair p) : IterPair(p) {}
// Define begin() and end() for convenience in range-for
auto begin() -> decltype(first) const { return first; }
auto end() -> decltype(second) const { return second; }
};
Iterators GetRange() const;
void SetTag(const wxString & name, const wxString & value);
void SetTag(const wxString & name, const int & value);
@ -111,7 +120,6 @@ class AUDACITY_DLL_API Tags: public XMLTagHandler {
private:
void LoadDefaults();
TagMap::iterator mIter;
TagMap mXref;
TagMap mMap;

View File

@ -64,7 +64,7 @@ TimeTrack::TimeTrack(DirManager *projDirManager, const ZoomInfo *zoomInfo):
blankPen.SetColour(214, 214, 214);
}
TimeTrack::TimeTrack(TimeTrack &orig):
TimeTrack::TimeTrack(const TimeTrack &orig):
Track(orig)
, mZoomInfo(orig.mZoomInfo)
{
@ -109,7 +109,7 @@ TimeTrack::~TimeTrack()
delete mRuler;
}
Track *TimeTrack::Duplicate()
Track *TimeTrack::Duplicate() const
{
return new TimeTrack(*this);
}

View File

@ -35,7 +35,7 @@ class TimeTrack: public Track {
* copy-constructor to encapsulate this.
* @param orig The original track to copy from
*/
TimeTrack(TimeTrack &orig);
TimeTrack(const TimeTrack &orig);
virtual ~TimeTrack();
@ -131,7 +131,7 @@ class TimeTrack: public Track {
* @param orig the TimeTrack to copy from
*/
void Init(const TimeTrack &orig);
virtual Track *Duplicate();
virtual Track *Duplicate() const;
friend class TrackFactory;

View File

@ -29,7 +29,6 @@
class wxTextFile;
class DirManager;
class UndoStack;
class Track;
class LabelTrack;
class TimeTrack;
@ -146,7 +145,7 @@ class AUDACITY_DLL_API Track: public XMLTagHandler
virtual ~ Track();
void Init(const Track &orig);
virtual Track *Duplicate() = 0;
virtual Track *Duplicate() const = 0;
// Called when this track is merged to stereo with another, and should
// take on some paramaters of its partner.
@ -240,6 +239,30 @@ class AUDACITY_DLL_API TrackListIterator
TrackListNode *cur;
};
class AUDACITY_DLL_API TrackListConstIterator
{
public:
TrackListConstIterator(const TrackList * val = NULL)
: mIter(const_cast<TrackList*>(val))
{}
~TrackListConstIterator() {}
// Iterate functions
const Track *First(const TrackList * val = NULL)
{ return mIter.First(const_cast<TrackList*>(val)); }
const Track *StartWith(const Track * val)
{ return mIter.StartWith(const_cast<Track*>(val)); }
const Track *Next(bool skiplinked = false)
{ return mIter.Next(skiplinked); }
const Track *Prev(bool skiplinked = false)
{ return mIter.Prev(skiplinked); }
const Track *Last(bool skiplinked = false)
{ return mIter.Last(skiplinked); }
private:
TrackListIterator mIter;
};
// TrackListCondIterator (base class for iterators that iterate over all tracks)
// that meet a condition)
class AUDACITY_DLL_API TrackListCondIterator: public TrackListIterator

View File

@ -181,6 +181,7 @@ is time to refresh some aspect of the screen.
#include "TimeTrack.h"
#include "TrackArtist.h"
#include "TrackPanelAx.h"
#include "UndoManager.h"
#include "WaveTrack.h"
#include "commands/Keyboard.h"
@ -1492,11 +1493,16 @@ void TrackPanel::OnPaint(wxPaintEvent & /* event */)
/// Makes our Parent (well, whoever is listening to us) push their state.
/// this causes application state to be preserved on a stack for undo ops.
void TrackPanel::MakeParentPushState(const wxString &desc, const wxString &shortDesc,
int flags)
UndoPush flags)
{
mListener->TP_PushState(desc, shortDesc, flags);
}
void TrackPanel::MakeParentPushState(const wxString &desc, const wxString &shortDesc)
{
MakeParentPushState(desc, shortDesc, UndoPush::AUTOSAVE);
}
void TrackPanel::MakeParentModifyState(bool bWantsAutoSave)
{
mListener->TP_ModifyState(bWantsAutoSave);
@ -3300,7 +3306,7 @@ void TrackPanel::Stretch(int mouseXCoordinate, int trackLeftEdge,
break;
}
MakeParentPushState(_("Stretch Note Track"), _("Stretch"),
PUSH_CONSOLIDATE | PUSH_AUTOSAVE);
UndoPush::CONSOLIDATE | UndoPush::AUTOSAVE);
mStretched = true;
Refresh(false);
}
@ -3831,7 +3837,7 @@ void TrackPanel::HandleSlide(wxMouseEvent & event)
consolidate = true;
}
MakeParentPushState(msg, _("Time-Shift"),
consolidate ? (PUSH_CONSOLIDATE) : (PUSH_AUTOSAVE));
consolidate ? (UndoPush::CONSOLIDATE) : (UndoPush::AUTOSAVE));
}
}
@ -5170,7 +5176,7 @@ void TrackPanel::HandleSampleEditingButtonUp( wxMouseEvent & WXUNUSED(event))
mDrawingTrack=NULL; //Set this to NULL so it will catch improper drag events.
MakeParentPushState(_("Moved Samples"),
_("Sample Edit"),
PUSH_CONSOLIDATE|PUSH_AUTOSAVE);
UndoPush::CONSOLIDATE | UndoPush::AUTOSAVE);
}
@ -5420,7 +5426,7 @@ void TrackPanel::HandleSliders(wxMouseEvent &event, bool pan)
#endif
MakeParentPushState(pan ? _("Moved pan slider") : _("Moved gain slider"),
pan ? _("Pan") : _("Gain"),
PUSH_CONSOLIDATE);
UndoPush::CONSOLIDATE);
#ifdef EXPERIMENTAL_MIDI_OUT
} else {
MakeParentPushState(_("Moved velocity slider"), _("Velocity"), true);
@ -6435,7 +6441,7 @@ void TrackPanel::OnKeyDown(wxKeyEvent & event)
if (lt->OnKeyDown(mViewInfo->selectedRegion, event))
MakeParentPushState(_("Modified Label"),
_("Label Edit"),
PUSH_CONSOLIDATE);
UndoPush::CONSOLIDATE);
// Make sure caret is in view
int x;
@ -6479,7 +6485,7 @@ void TrackPanel::OnChar(wxKeyEvent & event)
if (((LabelTrack *)t)->OnChar(mViewInfo->selectedRegion, event))
MakeParentPushState(_("Modified Label"),
_("Label Edit"),
PUSH_CONSOLIDATE);
UndoPush::CONSOLIDATE);
// If selection modified, refresh
// Otherwise, refresh track display if the keystroke was handled
@ -6731,7 +6737,7 @@ bool TrackPanel::HandleTrackLocationMouseEvent(WaveTrack * track, wxRect &rect,
}
}
MakeParentPushState(_("Merged Clips"),_("Merge"), PUSH_CONSOLIDATE);
MakeParentPushState(_("Merged Clips"),_("Merge"), UndoPush::CONSOLIDATE);
handled = true;
}
}
@ -6880,7 +6886,7 @@ void TrackPanel::HandleGlyphDragRelease(LabelTrack * lTrack, wxMouseEvent & even
*mViewInfo, &mViewInfo->selectedRegion)) {
MakeParentPushState(_("Modified Label"),
_("Label Edit"),
PUSH_CONSOLIDATE);
UndoPush::CONSOLIDATE);
}
//If we are adjusting a label on a labeltrack, do not do anything

View File

@ -21,9 +21,9 @@
#include "Experimental.h"
#include "audacity/Types.h"
#include "UndoManager.h" //JKC: Included for PUSH_XXX definitions.
#include "widgets/NumericTextCtrl.h"
#include "SelectedRegion.h"
#include "WaveTrackLocation.h"
#include "Snap.h"
@ -457,8 +457,9 @@ protected:
virtual void MakeParentRedrawScrollbars();
// AS: Pushing the state preserves state for Undo operations.
virtual void MakeParentPushState(const wxString &desc, const wxString &shortDesc); // use UndoPush::AUTOSAVE
virtual void MakeParentPushState(const wxString &desc, const wxString &shortDesc,
int flags = PUSH_AUTOSAVE);
UndoPush flags);
virtual void MakeParentModifyState(bool bWantsAutoSave); // if true, writes auto-save file. Should set only if you really want the state change restored after
// a crash, as it can take many seconds for large (eg. 10 track-hours) projects

View File

@ -13,6 +13,7 @@
class ToolsToolBar;
class ControlToolBar;
enum class UndoPush;
class AUDACITY_DLL_API TrackPanelListener {
@ -26,7 +27,7 @@ class AUDACITY_DLL_API TrackPanelListener {
virtual ToolsToolBar * TP_GetToolsToolBar() = 0;
virtual void TP_PushState(const wxString &shortDesc, const wxString &longDesc,
int flags = PUSH_AUTOSAVE) = 0;
UndoPush flags) = 0;
virtual void TP_ModifyState(bool bWantsAutoSave) = 0; // if true, writes auto-save file. Should set only if you really want the state change restored after
// a crash, as it can take many seconds for large (eg. 10 track-hours) projects
virtual void TP_RedrawScrollbars() = 0;

View File

@ -31,11 +31,30 @@ UndoManager
#include "WaveTrack.h" // temp
#include "NoteTrack.h" // for Sonify* function declarations
#include "Diags.h"
#include "Tags.h"
#include "UndoManager.h"
WX_DECLARE_HASH_SET(BlockFile *, wxPointerHash, wxPointerEqual, Set );
struct UndoStackElem {
UndoStackElem(std::unique_ptr<TrackList> &&tracks_,
const wxString &description_,
const wxString &shortDescription_,
const SelectedRegion &selectedRegion_,
const std::shared_ptr<Tags> &tags_)
: state(std::move(tracks_), tags_, selectedRegion_)
, description(description_)
, shortDescription(shortDescription_)
{
}
UndoState state;
wxString description;
wxString shortDescription;
};
UndoManager::UndoManager()
{
current = -1;
@ -54,14 +73,14 @@ void UndoManager::CalculateSpaceUsage()
TIMER_START( "CalculateSpaceUsage", space_calc );
TrackListOfKindIterator iter(Track::Wave);
space.Clear();
space.Add(0, stack.GetCount());
space.clear();
space.resize(stack.size(), 0);
Set s1, s2;
Set *prev = &s1;
Set *cur = &s2;
for (size_t i = 0, cnt = stack.GetCount(); i < cnt; i++)
for (size_t i = 0, cnt = stack.size(); i < cnt; i++)
{
// Swap map pointers
std::swap(cur, prev);
@ -70,7 +89,7 @@ void UndoManager::CalculateSpaceUsage()
cur->clear();
// Scan all tracks at current level
WaveTrack *wt = (WaveTrack *) iter.First(stack[i]->tracks);
WaveTrack *wt = (WaveTrack *) iter.First(stack[i]->state.tracks.get());
while (wt)
{
// Scan all clips within current track
@ -109,8 +128,8 @@ wxLongLong_t UndoManager::GetLongDescription(unsigned int n, wxString *desc,
{
n -= 1; // 1 based to zero based
wxASSERT(n < stack.Count());
wxASSERT(space.Count() == stack.Count());
wxASSERT(n < stack.size());
wxASSERT(space.size() == stack.size());
*desc = stack[n]->description;
@ -123,7 +142,7 @@ void UndoManager::GetShortDescription(unsigned int n, wxString *desc)
{
n -= 1; // 1 based to zero based
wxASSERT(n < stack.Count());
wxASSERT(n < stack.size());
*desc = stack[n]->shortDescription;
}
@ -132,19 +151,14 @@ void UndoManager::SetLongDescription(unsigned int n, const wxString &desc)
{
n -= 1;
wxASSERT(n < stack.Count());
wxASSERT(n < stack.size());
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;
stack.erase(stack.begin() + n);
}
@ -160,12 +174,12 @@ void UndoManager::RemoveStates(int num)
void UndoManager::ClearStates()
{
RemoveStates(stack.Count());
RemoveStates(stack.size());
}
unsigned int UndoManager::GetNumStates()
{
return stack.Count();
return stack.size();
}
unsigned int UndoManager::GetCurrentState()
@ -180,49 +194,52 @@ bool UndoManager::UndoAvailable()
bool UndoManager::RedoAvailable()
{
return (current < (int)stack.Count() - 1);
return (current < (int)stack.size() - 1);
}
void UndoManager::ModifyState(TrackList * l,
const SelectedRegion &selectedRegion)
void UndoManager::ModifyState(const TrackList * l,
const SelectedRegion &selectedRegion,
const std::shared_ptr<Tags> &tags)
{
if (current == wxNOT_FOUND) {
return;
}
SonifyBeginModifyState();
// Delete current
stack[current]->tracks->Clear(true);
delete stack[current]->tracks;
// Delete current -- not necessary, but let's reclaim space early
stack[current]->state.tracks.reset();
// Duplicate
TrackList *tracksCopy = new TrackList();
TrackListIterator iter(l);
Track *t = iter.First();
auto tracksCopy = std::make_unique<TrackList>(true);
TrackListConstIterator iter(l);
const Track *t = iter.First();
while (t) {
tracksCopy->Add(t->Duplicate());
t = iter.Next();
}
// Replace
stack[current]->tracks = tracksCopy;
stack[current]->selectedRegion = selectedRegion;
stack[current]->state.tracks = std::move(tracksCopy);
stack[current]->state.tags = tags;
stack[current]->state.selectedRegion = selectedRegion;
SonifyEndModifyState();
}
void UndoManager::PushState(TrackList * l,
void UndoManager::PushState(const TrackList * l,
const SelectedRegion &selectedRegion,
const std::shared_ptr<Tags> &tags,
const wxString &longDescription,
const wxString &shortDescription,
int flags)
UndoPush flags)
{
unsigned int i;
// If consolidate is set to true, group up to 3 identical operations.
if (((flags&PUSH_CONSOLIDATE)!=0) && lastAction == longDescription &&
if (((flags & UndoPush::CONSOLIDATE) != UndoPush::MINIMAL) && lastAction == longDescription &&
consolidationCount < 2) {
consolidationCount++;
ModifyState(l, selectedRegion);
ModifyState(l, selectedRegion, tags);
// MB: If the "saved" state was modified by ModifyState, reset
// it so that UnsavedChanges returns true.
if (current == saved) {
@ -234,25 +251,26 @@ void UndoManager::PushState(TrackList * l,
consolidationCount = 0;
i = current + 1;
while (i < stack.Count()) {
while (i < stack.size()) {
RemoveStateAt(i);
}
TrackList *tracksCopy = new TrackList();
TrackListIterator iter(l);
Track *t = iter.First();
auto tracksCopy = std::make_unique<TrackList>(true);
TrackListConstIterator iter(l);
const 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;
// Assume tags was duplicted before any changes.
// Just save a new shared_ptr to it.
stack.emplace_back(
std::make_unique<UndoStackElem>
(std::move(tracksCopy),
longDescription, shortDescription, selectedRegion, tags)
);
stack.Add(push);
current++;
if (saved >= current) {
@ -262,49 +280,49 @@ void UndoManager::PushState(TrackList * l,
lastAction = longDescription;
}
TrackList *UndoManager::SetStateTo(unsigned int n,
SelectedRegion *selectedRegion)
const UndoState &UndoManager::SetStateTo
(unsigned int n, SelectedRegion *selectedRegion)
{
n -= 1;
wxASSERT(n < stack.Count());
wxASSERT(n < stack.size());
current = n;
if (current == int(stack.Count()-1)) {
*selectedRegion = stack[current]->selectedRegion;
if (current == int(stack.size()-1)) {
*selectedRegion = stack[current]->state.selectedRegion;
}
else {
*selectedRegion = stack[current + 1]->selectedRegion;
*selectedRegion = stack[current + 1]->state.selectedRegion;
}
lastAction = wxT("");
consolidationCount = 0;
return stack[current]->tracks;
return stack[current]->state;
}
TrackList *UndoManager::Undo(SelectedRegion *selectedRegion)
const UndoState &UndoManager::Undo(SelectedRegion *selectedRegion)
{
wxASSERT(UndoAvailable());
current--;
*selectedRegion = stack[current]->selectedRegion;
*selectedRegion = stack[current]->state.selectedRegion;
lastAction = wxT("");
consolidationCount = 0;
return stack[current]->tracks;
return stack[current]->state;
}
TrackList *UndoManager::Redo(SelectedRegion *selectedRegion)
const UndoState &UndoManager::Redo(SelectedRegion *selectedRegion)
{
wxASSERT(RedoAvailable());
current++;
*selectedRegion = stack[current]->selectedRegion;
*selectedRegion = stack[current]->state.selectedRegion;
/*
if (!RedoAvailable()) {
@ -322,7 +340,7 @@ TrackList *UndoManager::Redo(SelectedRegion *selectedRegion)
lastAction = wxT("");
consolidationCount = 0;
return stack[current]->tracks;
return stack[current]->state;
}
bool UndoManager::UnsavedChanges()

View File

@ -48,44 +48,58 @@
#ifndef __AUDACITY_UNDOMANAGER__
#define __AUDACITY_UNDOMANAGER__
#include <wx/dynarray.h>
#include <memory>
#include <vector>
#include <wx/string.h>
#include "ondemand/ODTaskThread.h"
#include "SelectedRegion.h"
class Tags;
class Track;
class TrackList;
struct UndoStackElem {
TrackList *tracks;
wxString description;
wxString shortDescription;
SelectedRegion selectedRegion;
struct UndoStackElem;
struct UndoState {
UndoState(std::unique_ptr<TrackList> &&tracks_,
const std::shared_ptr<Tags> &tags_,
const SelectedRegion &selectedRegion_)
: tracks(std::move(tracks_)), tags(tags_), selectedRegion(selectedRegion_)
{}
std::unique_ptr<TrackList> tracks;
std::shared_ptr<Tags> tags;
SelectedRegion selectedRegion; // by value
};
WX_DEFINE_USER_EXPORTED_ARRAY(UndoStackElem *, UndoStack, class AUDACITY_DLL_API);
// wxWidgets arrays have a base size and to use wxLongLong_t we need to use DOUBLE
// to ensure we get a size big enough to hold a wxLongLong_t.
WX_DEFINE_USER_EXPORTED_ARRAY_DOUBLE(wxLongLong_t, SpaceArray, class AUDACITY_DLL_API);
using UndoStack = std::vector <std::unique_ptr<UndoStackElem>> ;
using SpaceArray = std::vector <wxLongLong_t> ;
// These flags control what extra to do on a PushState
// Default is PUSH_AUTOSAVE
// Frequent/faster actions use PUSH_CONSOLIDATE
const int PUSH_MINIMAL = 0;
const int PUSH_CONSOLIDATE = 1;
const int PUSH_AUTOSAVE = 2;
// Default is AUTOSAVE
// Frequent/faster actions use CONSOLIDATE
enum class UndoPush {
MINIMAL = 0,
CONSOLIDATE = 1 << 0,
AUTOSAVE = 1 << 1
};
inline UndoPush operator | (UndoPush a, UndoPush b)
{ return static_cast<UndoPush>(static_cast<int>(a) | static_cast<int>(b)); }
inline UndoPush operator & (UndoPush a, UndoPush b)
{ return static_cast<UndoPush>(static_cast<int>(a) & static_cast<int>(b)); }
class AUDACITY_DLL_API UndoManager {
public:
UndoManager();
~UndoManager();
void PushState(TrackList * l,
void PushState(const TrackList * l,
const SelectedRegion &selectedRegion,
const std::shared_ptr<Tags> &tags,
const wxString &longDescription, const wxString &shortDescription,
int flags = PUSH_AUTOSAVE);
void ModifyState(TrackList * l,
const SelectedRegion &selectedRegion);
UndoPush flags = UndoPush::AUTOSAVE);
void ModifyState(const TrackList * l,
const SelectedRegion &selectedRegion, const std::shared_ptr<Tags> &tags);
void ClearStates();
void RemoveStates(int num); // removes the 'num' oldest states
void RemoveStateAt(int n); // removes the n'th state (1 is oldest)
@ -96,9 +110,9 @@ class AUDACITY_DLL_API UndoManager {
wxLongLong_t GetLongDescription(unsigned int n, wxString *desc, wxString *size);
void SetLongDescription(unsigned int n, const wxString &desc);
TrackList *SetStateTo(unsigned int n, SelectedRegion *selectedRegion);
TrackList *Undo(SelectedRegion *selectedRegion);
TrackList *Redo(SelectedRegion *selectedRegion);
const UndoState &SetStateTo(unsigned int n, SelectedRegion *selectedRegion);
const UndoState &Undo(SelectedRegion *selectedRegion);
const UndoState &Redo(SelectedRegion *selectedRegion);
bool UndoAvailable();
bool RedoAvailable();

View File

@ -116,7 +116,7 @@ WaveTrack::WaveTrack(DirManager *projDirManager, sampleFormat format, double rat
mAutoSaveIdent = 0;
}
WaveTrack::WaveTrack(WaveTrack &orig):
WaveTrack::WaveTrack(const WaveTrack &orig):
Track(orig)
, mpSpectrumSettings(orig.mpSpectrumSettings
? new SpectrogramSettings(*orig.mpSpectrumSettings) : 0
@ -362,7 +362,7 @@ void WaveTrack::SetSpectrumBounds(float min, float max)
mSpectrumMax = max;
}
Track *WaveTrack::Duplicate()
Track *WaveTrack::Duplicate() const
{
return new WaveTrack(*this);
}

View File

@ -72,10 +72,10 @@ class AUDACITY_DLL_API WaveTrack : public Track {
WaveTrack(DirManager * projDirManager,
sampleFormat format = (sampleFormat)0,
double rate = 0);
WaveTrack(WaveTrack &orig);
WaveTrack(const WaveTrack &orig);
void Init(const WaveTrack &orig);
virtual Track *Duplicate();
virtual Track *Duplicate() const;
#ifdef EXPERIMENTAL_OUTPUT_DISPLAY
void VirtualStereoInit();
#endif

View File

@ -364,7 +364,7 @@ bool Exporter::Process(AudacityProject *project, bool selectedOnly, double t0, d
// Let user edit MetaData
if (mPlugins[mFormat]->GetCanMetaData(mSubFormat)) {
if (!(project->GetTags()->ShowEditDialog(project, _("Edit Metadata Tags"), mProject->GetShowId3Dialog()))) {
if (!(project->DoEditMetadata(_("Edit Metadata Tags for Export"), _("Exported Tags"), mProject->GetShowId3Dialog()))) {
return false;
}
}

View File

@ -110,7 +110,7 @@ public:
double t0,
double t1,
MixerSpec *mixerSpec = NULL,
Tags *metadata = NULL,
const Tags *metadata = NULL,
int subformat = 0) = 0;
protected:

View File

@ -291,7 +291,7 @@ public:
double t0,
double t1,
MixerSpec *mixerSpec = NULL,
Tags *metadata = NULL,
const Tags *metadata = NULL,
int subformat = 0) override;
};
@ -318,7 +318,7 @@ int ExportCL::Export(AudacityProject *project,
double t0,
double t1,
MixerSpec *mixerSpec,
Tags *WXUNUSED(metadata),
const Tags *WXUNUSED(metadata),
int WXUNUSED(subformat))
{
wxString output;

View File

@ -101,16 +101,16 @@ public:
bool CheckFileName(wxFileName &filename, int format = 0);
/// Format intialization
bool Init(const char *shortname, AudacityProject *project, Tags *metadata, int subformat);
bool Init(const char *shortname, AudacityProject *project, const Tags *metadata, int subformat);
/// Codec intialization
bool InitCodecs(AudacityProject *project);
/// Writes metadata
bool AddTags(Tags *metadata);
bool AddTags(const Tags *metadata);
/// Sets individual metadata values
void SetMetadata(Tags *tags, const char *name, const wxChar *tag);
void SetMetadata(const Tags *tags, const char *name, const wxChar *tag);
/// Encodes audio
bool EncodeAudioFrame(int16_t *pFrame, int frameSize);
@ -145,7 +145,7 @@ public:
double t0,
double t1,
MixerSpec *mixerSpec = NULL,
Tags *metadata = NULL,
const Tags *metadata = NULL,
int subformat = 0) override;
private:
@ -256,7 +256,7 @@ bool ExportFFmpeg::CheckFileName(wxFileName & WXUNUSED(filename), int WXUNUSED(f
return result;
}
bool ExportFFmpeg::Init(const char *shortname, AudacityProject *project, Tags *metadata, int subformat)
bool ExportFFmpeg::Init(const char *shortname, AudacityProject *project, const Tags *metadata, int subformat)
{
int err;
//FFmpegLibsInst->LoadLibs(NULL,true); //Loaded at startup or from Prefs now
@ -312,7 +312,8 @@ bool ExportFFmpeg::Init(const char *shortname, AudacityProject *project, Tags *m
if (!InitCodecs(project))
return false;
if (metadata == NULL) metadata = project->GetTags();
if (metadata == NULL)
metadata = project->GetTags();
// Add metadata BEFORE writing the header.
// At the moment that works with ffmpeg-git and ffmpeg-0.5 for MP4.
@ -792,7 +793,7 @@ bool ExportFFmpeg::EncodeAudioFrame(int16_t *pFrame, int frameSize)
int ExportFFmpeg::Export(AudacityProject *project,
int channels, const wxString &fName,
bool selectionOnly, double t0, double t1, MixerSpec *mixerSpec, Tags *metadata, int subformat)
bool selectionOnly, double t0, double t1, MixerSpec *mixerSpec, const Tags *metadata, int subformat)
{
if (!CheckFFmpegPresence())
return false;
@ -873,7 +874,7 @@ void AddStringTagANSI(char field[], int size, wxString value)
memcpy(field,value.mb_str(),(int)strlen(value.mb_str()) > size -1 ? size -1 : strlen(value.mb_str()));
}
bool ExportFFmpeg::AddTags(Tags *tags)
bool ExportFFmpeg::AddTags(const Tags *tags)
{
if (tags == NULL)
{
@ -891,7 +892,7 @@ bool ExportFFmpeg::AddTags(Tags *tags)
return true;
}
void ExportFFmpeg::SetMetadata(Tags *tags, const char *name, const wxChar *tag)
void ExportFFmpeg::SetMetadata(const Tags *tags, const char *name, const wxChar *tag)
{
if (tags->HasTag(tag))
{

View File

@ -190,12 +190,12 @@ public:
double t0,
double t1,
MixerSpec *mixerSpec = NULL,
Tags *metadata = NULL,
const Tags *metadata = NULL,
int subformat = 0) override;
private:
bool GetMetadata(AudacityProject *project, Tags *tags);
bool GetMetadata(AudacityProject *project, const Tags *tags);
FLAC__StreamMetadata *mMetadata;
};
@ -225,7 +225,7 @@ int ExportFLAC::Export(AudacityProject *project,
double t0,
double t1,
MixerSpec *mixerSpec,
Tags *metadata,
const Tags *metadata,
int WXUNUSED(subformat))
{
double rate = project->GetRate();
@ -380,7 +380,7 @@ wxWindow *ExportFLAC::OptionsCreate(wxWindow *parent, int format)
// expects that array to be valid until the stream is initialized.
//
// This has been fixed in 1.1.4.
bool ExportFLAC::GetMetadata(AudacityProject *project, Tags *tags)
bool ExportFLAC::GetMetadata(AudacityProject *project, const Tags *tags)
{
// Retrieve tags if needed
if (tags == NULL)
@ -388,8 +388,10 @@ bool ExportFLAC::GetMetadata(AudacityProject *project, Tags *tags)
mMetadata = ::FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT);
wxString n, v;
for (bool cont = tags->GetFirst(n, v); cont; cont = tags->GetNext(n, v)) {
wxString n;
for (const auto &pair : tags->GetRange()) {
n = pair.first;
const auto &v = pair.second;
if (n == TAG_YEAR) {
n = wxT("DATE");
}

View File

@ -181,12 +181,12 @@ public:
double t0,
double t1,
MixerSpec *mixerSpec = NULL,
Tags *metadata = NULL,
const Tags *metadata = NULL,
int subformat = 0) override;
private:
int AddTags(AudacityProject *project, char **buffer, bool *endOfFile, Tags *tags);
int AddTags(AudacityProject *project, char **buffer, bool *endOfFile, const Tags *tags);
#ifdef USE_LIBID3TAG
void AddFrame(struct id3_tag *tp, const wxString & n, const wxString & v, const char *name);
#endif
@ -211,7 +211,7 @@ void ExportMP2::Destroy()
int ExportMP2::Export(AudacityProject *project,
int channels, const wxString &fName,
bool selectionOnly, double t0, double t1, MixerSpec *mixerSpec, Tags *metadata,
bool selectionOnly, double t0, double t1, MixerSpec *mixerSpec, const Tags *metadata,
int WXUNUSED(subformat))
{
bool stereo = (channels == 2);
@ -339,13 +339,14 @@ wxWindow *ExportMP2::OptionsCreate(wxWindow *parent, int format)
}
// returns buffer len; caller frees
int ExportMP2::AddTags(AudacityProject * WXUNUSED(project), char **buffer, bool *endOfFile, Tags *tags)
int ExportMP2::AddTags(AudacityProject * WXUNUSED(project), char **buffer, bool *endOfFile, const Tags *tags)
{
#ifdef USE_LIBID3TAG
struct id3_tag *tp = id3_tag_new();
wxString n, v;
for (bool cont = tags->GetFirst(n, v); cont; cont = tags->GetNext(n, v)) {
for (const auto &pair : tags->GetRange()) {
const auto &n = pair.first;
const auto &v = pair.second;
const char *name = "TXXX";
if (n.CmpNoCase(TAG_TITLE) == 0) {

View File

@ -1569,7 +1569,7 @@ public:
double t0,
double t1,
MixerSpec *mixerSpec = NULL,
Tags *metadata = NULL,
const Tags *metadata = NULL,
int subformat = 0) override;
private:
@ -1577,7 +1577,7 @@ private:
int FindValue(CHOICES *choices, int cnt, int needle, int def);
wxString FindName(CHOICES *choices, int cnt, int needle);
int AskResample(int bitrate, int rate, int lowrate, int highrate);
int AddTags(AudacityProject *project, char **buffer, bool *endOfFile, Tags *tags);
int AddTags(AudacityProject *project, char **buffer, bool *endOfFile, const Tags *tags);
#ifdef USE_LIBID3TAG
void AddFrame(struct id3_tag *tp, const wxString & n, const wxString & v, const char *name);
#endif
@ -1624,7 +1624,7 @@ int ExportMP3::Export(AudacityProject *project,
double t0,
double t1,
MixerSpec *mixerSpec,
Tags *metadata,
const Tags *metadata,
int WXUNUSED(subformat))
{
int rate = lrint(project->GetRate());
@ -1965,13 +1965,14 @@ int ExportMP3::AskResample(int bitrate, int rate, int lowrate, int highrate)
}
// returns buffer len; caller frees
int ExportMP3::AddTags(AudacityProject *WXUNUSED(project), char **buffer, bool *endOfFile, Tags *tags)
int ExportMP3::AddTags(AudacityProject *WXUNUSED(project), char **buffer, bool *endOfFile, const Tags *tags)
{
#ifdef USE_LIBID3TAG
struct id3_tag *tp = id3_tag_new();
wxString n, v;
for (bool cont = tags->GetFirst(n, v); cont; cont = tags->GetNext(n, v)) {
for (const auto &pair : tags->GetRange()) {
const auto &n = pair.first;
const auto &v = pair.second;
const char *name = "TXXX";
if (n.CmpNoCase(TAG_TITLE) == 0) {

View File

@ -911,7 +911,7 @@ int ExportMultiple::DoExport(int channels,
bool selectedOnly,
double t0,
double t1,
Tags tags)
const Tags &tags)
{
wxLogDebug(wxT("Doing multiple Export: File name \"%s\""), (name.GetFullName()).c_str());
wxLogDebug(wxT("Channels: %i, Start: %lf, End: %lf "), channels, t0, t1);

View File

@ -79,7 +79,7 @@ private:
bool selectedOnly,
double t0,
double t1,
Tags tags);
const Tags &tags);
/** \brief Takes an arbitrary text string and converts it to a form that can
* be used as a file name, if necessary prompting the user to edit the file
* name produced */

View File

@ -140,12 +140,12 @@ public:
double t0,
double t1,
MixerSpec *mixerSpec = NULL,
Tags *metadata = NULL,
const Tags *metadata = NULL,
int subformat = 0) override;
private:
bool FillComment(AudacityProject *project, vorbis_comment *comment, Tags *metadata);
bool FillComment(AudacityProject *project, vorbis_comment *comment, const Tags *metadata);
};
ExportOGG::ExportOGG()
@ -171,7 +171,7 @@ int ExportOGG::Export(AudacityProject *project,
double t0,
double t1,
MixerSpec *mixerSpec,
Tags *metadata,
const Tags *metadata,
int WXUNUSED(subformat))
{
double rate = project->GetRate();
@ -341,7 +341,7 @@ wxWindow *ExportOGG::OptionsCreate(wxWindow *parent, int format)
return safenew ExportOGGOptions(parent, format);
}
bool ExportOGG::FillComment(AudacityProject *project, vorbis_comment *comment, Tags *metadata)
bool ExportOGG::FillComment(AudacityProject *project, vorbis_comment *comment, const Tags *metadata)
{
// Retrieve tags from project if not over-ridden
if (metadata == NULL)
@ -349,8 +349,10 @@ bool ExportOGG::FillComment(AudacityProject *project, vorbis_comment *comment, T
vorbis_comment_init(comment);
wxString n, v;
for (bool cont = metadata->GetFirst(n, v); cont; cont = metadata->GetNext(n, v)) {
wxString n;
for (const auto &pair : metadata->GetRange()) {
n = pair.first;
const auto &v = pair.second;
if (n == TAG_YEAR) {
n = wxT("DATE");
}

View File

@ -320,7 +320,7 @@ public:
double t0,
double t1,
MixerSpec *mixerSpec = NULL,
Tags *metadata = NULL,
const Tags *metadata = NULL,
int subformat = 0) override;
// optional
wxString GetExtension(int index);
@ -329,8 +329,8 @@ public:
private:
char *AdjustString(const wxString & wxStr, int sf_format);
bool AddStrings(AudacityProject *project, SNDFILE *sf, Tags *tags, int sf_format);
void AddID3Chunk(wxString fName, Tags *tags, int sf_format);
bool AddStrings(AudacityProject *project, SNDFILE *sf, const Tags *tags, int sf_format);
void AddID3Chunk(wxString fName, const Tags *tags, int sf_format);
};
@ -395,7 +395,7 @@ int ExportPCM::Export(AudacityProject *project,
double t0,
double t1,
MixerSpec *mixerSpec,
Tags *metadata,
const Tags *metadata,
int subformat)
{
double rate = project->GetRate();
@ -664,7 +664,7 @@ char *ExportPCM::AdjustString(const wxString & wxStr, int sf_format)
return pDest;
}
bool ExportPCM::AddStrings(AudacityProject * WXUNUSED(project), SNDFILE *sf, Tags *tags, int sf_format)
bool ExportPCM::AddStrings(AudacityProject * WXUNUSED(project), SNDFILE *sf, const Tags *tags, int sf_format)
{
if (tags->HasTag(TAG_TITLE)) {
char * ascii7Str = AdjustString(tags->GetTag(TAG_TITLE), sf_format);
@ -741,13 +741,14 @@ bool ExportPCM::AddStrings(AudacityProject * WXUNUSED(project), SNDFILE *sf, Tag
return true;
}
void ExportPCM::AddID3Chunk(wxString fName, Tags *tags, int sf_format)
void ExportPCM::AddID3Chunk(wxString fName, const Tags *tags, int sf_format)
{
#ifdef USE_LIBID3TAG
struct id3_tag *tp = id3_tag_new();
wxString n, v;
for (bool cont = tags->GetFirst(n, v); cont; cont = tags->GetNext(n, v)) {
for (const auto &pair : tags->GetRange()) {
const auto &n = pair.first;
const auto &v = pair.second;
const char *name = "TXXX";
if (n.CmpNoCase(TAG_TITLE) == 0) {

View File

@ -22,6 +22,7 @@ in a background thread.
#include "ODManager.h"
#include "../WaveTrack.h"
#include "../Project.h"
#include "../UndoManager.h"
//temporarilly commented out till it is added to all projects
//#include "../Profiler.h"